Commit aabb45be authored by Cyrill Gorcunov's avatar Cyrill Gorcunov Committed by Andrei Vagin

compel: Move cpu interface to compel

We will need it when parasite engine will be creating signal frames.
Export appropriate headers and use it in CRIU by linking with libcompel.a.
Signed-off-by: 's avatarCyrill Gorcunov <gorcunov@openvz.org>
Signed-off-by: 's avatarAndrei Vagin <avagin@virtuozzo.com>
parent 1a30731b
......@@ -228,9 +228,9 @@ $(SOCCR_A): |soccr/built-in.o
#
# But note that we're already included
# the nmk so we can reuse it there.
criu/%: images/built-in.o compel/plugins/std.built-in.o compel/compel-host $(VERSION_HEADER) .FORCE
criu/%: images/built-in.o compel/plugins/std.built-in.o compel/libcompel.a compel/compel-host $(VERSION_HEADER) .FORCE
$(Q) $(MAKE) $(build)=criu $@
criu: images/built-in.o compel/plugins/std.built-in.o compel/compel-host $(SOCCR_A) $(VERSION_HEADER)
criu: images/built-in.o compel/plugins/std.built-in.o compel/libcompel.a compel/compel-host $(SOCCR_A) $(VERSION_HEADER)
$(Q) $(MAKE) $(build)=criu all
.PHONY: criu
......
......@@ -33,6 +33,7 @@ compel-uapi-links += $(SRC_DIR)/compel/include/asm
compel-deps += $(compel-uapi-links)
compel-deps += $(COMPEL_VERSION_HEADER)
compel-deps += $(CONFIG_HEADER)
compel-deps += include/common/asm
#
# Compel itself.
......
......@@ -22,6 +22,8 @@ host-lib-y += src/lib/handle-elf.o
lib-y += src/lib/log.o
host-lib-y += src/lib/log.o
lib-y += arch/$(ARCH)/src/lib/cpu.o
ifeq ($(ARCH),x86)
lib-y += src/lib/handle-elf-32.o
host-lib-y += src/lib/handle-elf-32.o
......
#include <string.h>
#include <stdbool.h>
#include "uapi/compel/cpu.h"
#include "common/bitops.h"
#include "log.h"
#undef LOG_PREFIX
#define LOG_PREFIX "cpu: "
static compel_cpuinfo_t rt_info;
static bool rt_info_done = false;
void compel_set_cpu_cap(compel_cpuinfo_t *info, unsigned int feature) { }
void compel_clear_cpu_cap(compel_cpuinfo_t *info, unsigned int feature) { }
int compel_test_cpu_cap(compel_cpuinfo_t *info, unsigned int feature) { return 0; }
int compel_cpuid(compel_cpuinfo_t *info) { return 0; }
bool cpu_has_feature(unsigned int feature)
{
if (!rt_info_done) {
compel_cpuid(&rt_info);
rt_info_done = true;
}
return compel_test_cpu_cap(&rt_info, feature);
}
#ifndef UAPI_COMPEL_ASM_CPU_H__
#define UAPI_COMPEL_ASM_CPU_H__
typedef struct { } compel_cpuinfo_t;
#endif /* UAPI_COMPEL_ASM_CPU_H__ */
#ifndef UAPI_COMPEL_ASM_PROCESSOR_FLAGS_H__
#define UAPI_COMPEL_ASM_PROCESSOR_FLAGS_H__
#endif /* UAPI_COMPEL_ASM_PROCESSOR_FLAGS_H__ */
#include <string.h>
#include <stdbool.h>
#include "uapi/compel/cpu.h"
#include "common/bitops.h"
#include "log.h"
#undef LOG_PREFIX
#define LOG_PREFIX "cpu: "
static compel_cpuinfo_t rt_info;
static bool rt_info_done = false;
void compel_set_cpu_cap(compel_cpuinfo_t *info, unsigned int feature) { }
void compel_clear_cpu_cap(compel_cpuinfo_t *info, unsigned int feature) { }
int compel_test_cpu_cap(compel_cpuinfo_t *info, unsigned int feature) { return 0; }
int compel_cpuid(compel_cpuinfo_t *info) { return 0; }
bool cpu_has_feature(unsigned int feature)
{
if (!rt_info_done) {
compel_cpuid(&rt_info);
rt_info_done = true;
}
return compel_test_cpu_cap(&rt_info, feature);
}
#ifndef UAPI_COMPEL_ASM_CPU_H__
#define UAPI_COMPEL_ASM_CPU_H__
typedef struct { } compel_cpuinfo_t;
#endif /* UAPI_COMPEL_ASM_CPU_H__ */
#include <sys/auxv.h>
#include <asm/cputable.h>
#include <errno.h>
#include <stdbool.h>
#include "uapi/compel/cpu.h"
#include "common/bitops.h"
#include "log.h"
#undef LOG_PREFIX
#define LOG_PREFIX "cpu: "
static compel_cpuinfo_t rt_info;
static bool rt_info_done = false;
void compel_set_cpu_cap(compel_cpuinfo_t *info, unsigned int feature) { }
void compel_clear_cpu_cap(compel_cpuinfo_t *info, unsigned int feature) { }
int compel_test_cpu_cap(compel_cpuinfo_t *info, unsigned int feature) { return 0; }
int compel_cpuid(compel_cpuinfo_t *info)
{
info->hwcap[0] = getauxval(AT_HWCAP);
info->hwcap[1] = getauxval(AT_HWCAP2);
if (!info->hwcap[0] || !info->hwcap[1]) {
pr_err("Can't read the hardware capabilities");
return -1;
}
return 0;
}
bool cpu_has_feature(unsigned int feature)
{
if (!rt_info_done) {
compel_cpuid(&rt_info);
rt_info_done = true;
}
return compel_test_cpu_cap(&rt_info, feature);
}
#ifndef UAPI_COMPEL_ASM_CPU_H__
#define UAPI_COMPEL_ASM_CPU_H__
#include <stdint.h>
typedef struct {
uint64_t hwcap[2];
} compel_cpuinfo_t;
#endif /* UAPI_COMPEL_ASM_CPU_H__ */
#ifndef UAPI_COMPEL_ASM_PROCESSOR_FLAGS_H__
#define UAPI_COMPEL_ASM_PROCESSOR_FLAGS_H__
#endif /* UAPI_COMPEL_ASM_PROCESSOR_FLAGS_H__ */
#ifndef UAPI_COMPEL_ASM_PROCESSOR_H__
#define UAPI_COMPEL_ASM_PROCESSOR_H__
#endif /* UAPI_COMPEL_ASM_PROCESSOR_H__ */
#include <string.h>
#include <stdbool.h>
#include "uapi/compel/cpu.h"
#include "common/bitops.h"
#include "common/compiler.h"
#include "log.h"
#undef LOG_PREFIX
#define LOG_PREFIX "cpu: "
static compel_cpuinfo_t rt_info;
static bool rt_info_done = false;
void compel_set_cpu_cap(compel_cpuinfo_t *c, unsigned int feature)
{
if (likely(feature < NCAPINTS_BITS))
set_bit(feature, (unsigned long *)c->x86_capability);
}
void compel_clear_cpu_cap(compel_cpuinfo_t *c, unsigned int feature)
{
if (likely(feature < NCAPINTS_BITS))
clear_bit(feature, (unsigned long *)c->x86_capability);
}
int compel_test_cpu_cap(compel_cpuinfo_t *c, unsigned int feature)
{
if (likely(feature < NCAPINTS_BITS))
return test_bit(feature, (unsigned long *)c->x86_capability);
return 0;
}
int compel_cpuid(compel_cpuinfo_t *c)
{
/*
* See cpu_detect() in the kernel, also
* read cpuid specs not only from general
* SDM but for extended instructions set
* reference.
*/
/* Get vendor name */
cpuid(0x00000000,
(unsigned int *)&c->cpuid_level,
(unsigned int *)&c->x86_vendor_id[0],
(unsigned int *)&c->x86_vendor_id[8],
(unsigned int *)&c->x86_vendor_id[4]);
if (!strcmp(c->x86_vendor_id, "GenuineIntel")) {
c->x86_vendor = X86_VENDOR_INTEL;
} else if (!strcmp(c->x86_vendor_id, "AuthenticAMD")) {
c->x86_vendor = X86_VENDOR_AMD;
} else {
pr_err("Unsupported CPU vendor %s\n",
c->x86_vendor_id);
return -1;
}
c->x86_family = 4;
/* Intel-defined flags: level 0x00000001 */
if (c->cpuid_level >= 0x00000001) {
uint32_t eax, ebx, ecx, edx;
cpuid(0x00000001, &eax, &ebx, &ecx, &edx);
c->x86_family = (eax >> 8) & 0xf;
c->x86_model = (eax >> 4) & 0xf;
c->x86_mask = eax & 0xf;
if (c->x86_family == 0xf)
c->x86_family += (eax >> 20) & 0xff;
if (c->x86_family >= 0x6)
c->x86_model += ((eax >> 16) & 0xf) << 4;
c->x86_capability[0] = edx;
c->x86_capability[4] = ecx;
}
/* Additional Intel-defined flags: level 0x00000007 */
if (c->cpuid_level >= 0x00000007) {
uint32_t eax, ebx, ecx, edx;
cpuid_count(0x00000007, 0, &eax, &ebx, &ecx, &edx);
c->x86_capability[9] = ebx;
c->x86_capability[11] = ecx;
}
/* Extended state features: level 0x0000000d */
if (c->cpuid_level >= 0x0000000d) {
uint32_t eax, ebx, ecx, edx;
cpuid_count(0x0000000d, 1, &eax, &ebx, &ecx, &edx);
c->x86_capability[10] = eax;
}
/* AMD-defined flags: level 0x80000001 */
c->extended_cpuid_level = cpuid_eax(0x80000000);
if ((c->extended_cpuid_level & 0xffff0000) == 0x80000000) {
if (c->extended_cpuid_level >= 0x80000001) {
c->x86_capability[1] = cpuid_edx(0x80000001);
c->x86_capability[6] = cpuid_ecx(0x80000001);
}
}
/*
* We're don't care about scattered features for now,
* otherwise look into init_scattered_cpuid_features()
* in kernel.
*/
if (c->extended_cpuid_level >= 0x80000004) {
unsigned int *v;
char *p, *q;
v = (unsigned int *)c->x86_model_id;
cpuid(0x80000002, &v[0], &v[1], &v[2], &v[3]);
cpuid(0x80000003, &v[4], &v[5], &v[6], &v[7]);
cpuid(0x80000004, &v[8], &v[9], &v[10], &v[11]);
c->x86_model_id[48] = 0;
/*
* Intel chips right-justify this string for some dumb reason;
* undo that brain damage:
*/
p = q = &c->x86_model_id[0];
while (*p == ' ')
p++;
if (p != q) {
while (*p)
*q++ = *p++;
while (q <= &c->x86_model_id[48])
*q++ = '\0'; /* Zero-pad the rest */
}
}
/* On x86-64 NOP is always present */
compel_set_cpu_cap(c, X86_FEATURE_NOPL);
switch (c->x86_vendor) {
case X86_VENDOR_INTEL:
/*
* Strictly speaking we need to read MSR_IA32_MISC_ENABLE
* here but on ring3 it's impossible.
*/
if (c->x86_family == 15) {
compel_clear_cpu_cap(c, X86_FEATURE_REP_GOOD);
compel_clear_cpu_cap(c, X86_FEATURE_ERMS);
} else if (c->x86_family == 6) {
/* On x86-64 rep is fine */
compel_set_cpu_cap(c, X86_FEATURE_REP_GOOD);
}
/* See filter_cpuid_features in kernel */
if ((int32_t)c->cpuid_level < (int32_t)0x0000000d)
compel_clear_cpu_cap(c, X86_FEATURE_XSAVE);
break;
case X86_VENDOR_AMD:
/*
* Bit 31 in normal CPUID used for nonstandard 3DNow ID;
* 3DNow is IDd by bit 31 in extended CPUID (1*32+31) anyway
*/
compel_clear_cpu_cap(c, 0 * 32 + 31);
if (c->x86_family >= 0x10)
compel_set_cpu_cap(c, X86_FEATURE_REP_GOOD);
if (c->x86_family == 0xf) {
uint32_t level;
/* On C+ stepping K8 rep microcode works well for copy/memset */
level = cpuid_eax(1);
if ((level >= 0x0f48 && level < 0x0f50) || level >= 0x0f58)
compel_set_cpu_cap(c, X86_FEATURE_REP_GOOD);
}
break;
}
return 0;
}
bool cpu_has_feature(unsigned int feature)
{
if (!rt_info_done) {
compel_cpuid(&rt_info);
rt_info_done = true;
}
return compel_test_cpu_cap(&rt_info, feature);
}
#ifndef __CR_ASM_CPU_H__
#define __CR_ASM_CPU_H__
#include "asm/types.h"
#include <stdint.h>
/*
* Adopted from linux kernel and enhanced from Intel/AMD manuals.
......@@ -176,8 +176,6 @@ static inline unsigned int cpuid_edx(unsigned int op)
return edx;
}
#define X86_FEATURE_VERSION 1
enum {
X86_VENDOR_INTEL = 0,
X86_VENDOR_AMD = 1,
......@@ -186,22 +184,17 @@ enum {
};
struct cpuinfo_x86 {
u8 x86_family;
u8 x86_vendor;
u8 x86_model;
u8 x86_mask;
u32 x86_capability[NCAPINTS];
u32 extended_cpuid_level;
uint8_t x86_family;
uint8_t x86_vendor;
uint8_t x86_model;
uint8_t x86_mask;
uint32_t x86_capability[NCAPINTS];
uint32_t extended_cpuid_level;
int cpuid_level;
char x86_vendor_id[16];
char x86_model_id[64];
};
extern bool cpu_has_feature(unsigned int feature);
extern int cpu_init(void);
extern int cpu_dump_cpuinfo(void);
extern int cpu_validate_cpuinfo(void);
extern int cpuinfo_dump(void);
extern int cpuinfo_check(void);
typedef struct cpuinfo_x86 compel_cpuinfo_t;
#endif /* __CR_CPU_H__ */
#endif /* __CR_ASM_CPU_H__ */
#ifndef UAPI_COMPEL_CPU_H__
#define UAPI_COMPEL_CPU_H__
#include <stdbool.h>
#include <compel/asm/cpu.h>
extern void compel_set_cpu_cap(compel_cpuinfo_t *info, unsigned int feature);
extern void compel_clear_cpu_cap(compel_cpuinfo_t *info, unsigned int feature);
extern int compel_test_cpu_cap(compel_cpuinfo_t *info, unsigned int feature);
extern int compel_cpuid(compel_cpuinfo_t *info);
extern bool cpu_has_feature(unsigned int feature);
#endif /* UAPI_COMPEL_CPU_H__ */
......@@ -73,6 +73,7 @@ PROGRAM-BUILTINS += images/built-in.o
PROGRAM-BUILTINS += $(obj)/built-in.o
PROGRAM-BUILTINS += $(ARCH-LIB)
PROGRAM-BUILTINS += soccr/libsoccr.a
PROGRAM-BUILTINS += compel/libcompel.a
$(obj)/built-in.o: pie
$(Q) $(MAKE) $(call build-as,Makefile.crtools,criu) all
......
......@@ -4,11 +4,6 @@
#include <errno.h>
#include "cpu.h"
bool cpu_has_feature(unsigned int feature)
{
return false;
}
int cpu_init(void)
{
return 0;
......
......@@ -4,6 +4,8 @@
#include <linux/elf.h>
#include "types.h"
#include <compel/asm/processor-flags.h>
#include "asm/restorer.h"
#include "common/compiler.h"
#include "ptrace.h"
......
......@@ -4,11 +4,6 @@
#include <errno.h>
#include "cpu.h"
bool cpu_has_feature(unsigned int feature)
{
return false;
}
int cpu_init(void)
{
return 0;
......
......@@ -2,6 +2,8 @@
#include <unistd.h>
#include "types.h"
#include <compel/asm/processor-flags.h>
#include "asm/restorer.h"
#include "common/compiler.h"
#include "asm/dump.h"
......
......@@ -6,7 +6,6 @@
#include <asm/cputable.h>
#include "asm/types.h"
#include "asm/cpu.h"
#include "cr_options.h"
#include "image.h"
......@@ -17,7 +16,7 @@
#include "protobuf.h"
#include "images/cpuinfo.pb-c.h"
static uint64_t hwcap[2];
static compel_cpuinfo_t rt_cpuinfo;
#ifdef __LITTLE_ENDIAN__
#define CURRENT_ENDIANNESS CPUINFO_PPC64_ENTRY__ENDIANNESS__LITTLEENDIAN
......@@ -27,14 +26,7 @@ static uint64_t hwcap[2];
int cpu_init(void)
{
hwcap[0] = getauxval(AT_HWCAP);
hwcap[1] = getauxval(AT_HWCAP2);
if (!hwcap[0] || !hwcap[1]) {
pr_err("Can't read the hardware capabilities");
return -1;
}
return 0;
return compel_cpuid(&rt_cpuinfo);
}
int cpu_dump_cpuinfo(void)
......@@ -54,7 +46,7 @@ int cpu_dump_cpuinfo(void)
cpu_ppc64_info.endian = CURRENT_ENDIANNESS;
cpu_ppc64_info.n_hwcap = 2;
cpu_ppc64_info.hwcap = hwcap;
cpu_ppc64_info.hwcap = rt_cpuinfo.hwcap;
ret = pb_write_one(img, &cpu_info, PB_CPUINFO);
......@@ -92,7 +84,8 @@ int cpu_validate_cpuinfo(void)
}
#define CHECK_FEATURE(s,f) do { \
if ((cpu_ppc64_entry->hwcap[s] & f) && !(hwcap[s] & f)) { \
if ((cpu_ppc64_entry->hwcap[s] & f) && \
!(rt_cpuinfo.hwcap[s] & f)) { \
pr_err("CPU Feature %s required by image " \
"is not supported on host.\n", #f); \
goto error; \
......
......@@ -10,6 +10,7 @@
#include "asm/types.h"
#include "asm/cpu.h"
#include "asm/fpu.h"
#include <compel/cpu.h>
#include "common/compiler.h"
......@@ -26,181 +27,11 @@
#undef LOG_PREFIX
#define LOG_PREFIX "cpu: "
static struct cpuinfo_x86 rt_cpu_info;
static void set_cpu_cap(struct cpuinfo_x86 *c, unsigned int feature)
{
if (likely(feature < NCAPINTS_BITS))
set_bit(feature, (unsigned long *)c->x86_capability);
}
static void clear_cpu_cap(struct cpuinfo_x86 *c, unsigned int feature)
{
if (likely(feature < NCAPINTS_BITS))
clear_bit(feature, (unsigned long *)c->x86_capability);
}
static int test_cpu_cap(struct cpuinfo_x86 *c, unsigned int feature)
{
if (likely(feature < NCAPINTS_BITS))
return test_bit(feature, (unsigned long *)c->x86_capability);
return 0;
}
bool cpu_has_feature(unsigned int feature)
{
return test_cpu_cap(&rt_cpu_info, feature);
}
static int cpu_init_cpuid(struct cpuinfo_x86 *c)
{
/*
* See cpu_detect() in the kernel, also
* read cpuid specs not only from general
* SDM but for extended instructions set
* reference.
*/
/* Get vendor name */
cpuid(0x00000000,
(unsigned int *)&c->cpuid_level,
(unsigned int *)&c->x86_vendor_id[0],
(unsigned int *)&c->x86_vendor_id[8],
(unsigned int *)&c->x86_vendor_id[4]);
if (!strcmp(c->x86_vendor_id, "GenuineIntel")) {
c->x86_vendor = X86_VENDOR_INTEL;
} else if (!strcmp(c->x86_vendor_id, "AuthenticAMD")) {
c->x86_vendor = X86_VENDOR_AMD;
} else {
pr_err("Unsupported CPU vendor %s\n",
c->x86_vendor_id);
return -1;
}
c->x86_family = 4;
/* Intel-defined flags: level 0x00000001 */
if (c->cpuid_level >= 0x00000001) {
u32 eax, ebx, ecx, edx;
cpuid(0x00000001, &eax, &ebx, &ecx, &edx);
c->x86_family = (eax >> 8) & 0xf;
c->x86_model = (eax >> 4) & 0xf;
c->x86_mask = eax & 0xf;
if (c->x86_family == 0xf)
c->x86_family += (eax >> 20) & 0xff;
if (c->x86_family >= 0x6)
c->x86_model += ((eax >> 16) & 0xf) << 4;
c->x86_capability[0] = edx;
c->x86_capability[4] = ecx;
}
/* Additional Intel-defined flags: level 0x00000007 */
if (c->cpuid_level >= 0x00000007) {
u32 eax, ebx, ecx, edx;
cpuid_count(0x00000007, 0, &eax, &ebx, &ecx, &edx);
c->x86_capability[9] = ebx;
c->x86_capability[11] = ecx;
}
/* Extended state features: level 0x0000000d */
if (c->cpuid_level >= 0x0000000d) {
u32 eax, ebx, ecx, edx;
cpuid_count(0x0000000d, 1, &eax, &ebx, &ecx, &edx);
c->x86_capability[10] = eax;
}
/* AMD-defined flags: level 0x80000001 */
c->extended_cpuid_level = cpuid_eax(0x80000000);
if ((c->extended_cpuid_level & 0xffff0000) == 0x80000000) {
if (c->extended_cpuid_level >= 0x80000001) {
c->x86_capability[1] = cpuid_edx(0x80000001);
c->x86_capability[6] = cpuid_ecx(0x80000001);
}
}
/*
* We're don't care about scattered features for now,
* otherwise look into init_scattered_cpuid_features()
* in kernel.
*/
if (c->extended_cpuid_level >= 0x80000004) {
unsigned int *v;
char *p, *q;
v = (unsigned int *)c->x86_model_id;
cpuid(0x80000002, &v[0], &v[1], &v[2], &v[3]);
cpuid(0x80000003, &v[4], &v[5], &v[6], &v[7]);
cpuid(0x80000004, &v[8], &v[9], &v[10], &v[11]);
c->x86_model_id[48] = 0;
/*
* Intel chips right-justify this string for some dumb reason;
* undo that brain damage:
*/
p = q = &c->x86_model_id[0];
while (*p == ' ')
p++;
if (p != q) {
while (*p)
*q++ = *p++;
while (q <= &c->x86_model_id[48])
*q++ = '\0'; /* Zero-pad the rest */
}
}
/* On x86-64 NOP is always present */
set_cpu_cap(c, X86_FEATURE_NOPL);
switch (c->x86_vendor) {
case X86_VENDOR_INTEL:
/*
* Strictly speaking we need to read MSR_IA32_MISC_ENABLE
* here but on ring3 it's impossible.
*/
if (c->x86_family == 15) {
clear_cpu_cap(c, X86_FEATURE_REP_GOOD);
clear_cpu_cap(c, X86_FEATURE_ERMS);
} else if (c->x86_family == 6) {
/* On x86-64 rep is fine */
set_cpu_cap(c, X86_FEATURE_REP_GOOD);
}
/* See filter_cpuid_features in kernel */
if ((s32)c->cpuid_level < (s32)0x0000000d)
clear_cpu_cap(c, X86_FEATURE_XSAVE);
break;
case X86_VENDOR_AMD:
/*
* Bit 31 in normal CPUID used for nonstandard 3DNow ID;
* 3DNow is IDd by bit 31 in extended CPUID (1*32+31) anyway
*/
clear_cpu_cap(c, 0 * 32 + 31);
if (c->x86_family >= 0x10)
set_cpu_cap(c, X86_FEATURE_REP_GOOD);
if (c->x86_family == 0xf) {
u32 level;
/* On C+ stepping K8 rep microcode works well for copy/memset */
level = cpuid_eax(1);
if ((level >= 0x0f48 && level < 0x0f50) || level >= 0x0f58)
set_cpu_cap(c, X86_FEATURE_REP_GOOD);
}
break;
}
return 0;
}
static compel_cpuinfo_t rt_cpu_info;
int cpu_init(void)
{
if (cpu_init_cpuid(&rt_cpu_info))
if (compel_cpuid(&rt_cpu_info))
return -1;
BUILD_BUG_ON(sizeof(struct xsave_struct) != XSAVE_SIZE);
......
......@@ -9,7 +9,8 @@
#include "asm/cpu.h"
#include "kerndat.h"
#include "compel/include/asm/processor-flags.h"
#include <compel/asm/processor-flags.h>
#include <compel/cpu.h>
#include "compel/include/errno.h"
#include <compel/plugins/std/syscall-codes.h>
#include <compel/plugins/std/syscall.h>
......
#ifndef __CR_CPU_H__
#define __CR_CPU_H__
#include "asm/cpu.h"
#include <compel/cpu.h>
extern bool cpu_has_feature(unsigned int feature);
extern int cpu_init(void);
extern int cpu_dump_cpuinfo(void);
extern int cpu_validate_cpuinfo(void);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment