diff --git a/libcpuid/cpuid_main.c b/libcpuid/cpuid_main.c index 4b5a130..5fd940c 100644 --- a/libcpuid/cpuid_main.c +++ b/libcpuid/cpuid_main.c @@ -65,29 +65,8 @@ static void cpu_id_t_constructor(struct cpu_id_t* id) id->l1_assoc = id->l1_data_assoc = id->l1_instruction_assoc = id->l2_assoc = id->l3_assoc = id->l4_assoc = -1; id->l1_cacheline = id->l1_data_cacheline = id->l1_instruction_cacheline = id->l2_cacheline = id->l3_cacheline = id->l4_cacheline = -1; id->sse_size = -1; -} - -static int parse_token(const char* expected_token, const char *token, - const char *value, uint32_t array[][4], int limit, int *recognized) -{ - char format[32]; - int veax, vebx, vecx, vedx; - int index; - - if (*recognized) return 1; /* already recognized */ - if (strncmp(token, expected_token, strlen(expected_token))) return 1; /* not what we search for */ - sprintf(format, "%s[%%d]", expected_token); - *recognized = 1; - if (1 == sscanf(token, format, &index) && index >=0 && index < limit) { - if (4 == sscanf(value, "%x%x%x%x", &veax, &vebx, &vecx, &vedx)) { - array[index][0] = veax; - array[index][1] = vebx; - array[index][2] = vecx; - array[index][3] = vedx; - return 1; - } - } - return 0; + id->affinity_mask = 0x00000000; + id->purpose = PURPOSE_GENERAL; } /* get_total_cpus() system specific code: uses OS routines to determine total number of CPUs */ @@ -270,6 +249,196 @@ static int set_cpu_affinity(uint32_t logical_cpu) } #endif /* SET_CPU_AFFINITY */ +static int cpuid_serialize_raw_data_internal(struct cpu_raw_data_t* single_raw, struct cpu_raw_data_array_t *raw_array, const char* filename) +{ + int i; + const bool raw_is_init = (raw_array != NULL) && raw_array->num_raw > 0; + int8_t logical_cpu = raw_is_init ? 0 : -1; + struct cpu_raw_data_t* raw_ptr = raw_is_init ? &raw_array->raw[0] : single_raw; + FILE *f; + + /* Open file descriptor */ + f = !strcmp(filename, "") ? stdin : fopen(filename, "wt"); + if (!f) + return set_error(ERR_OPEN); + debugf(1, "Writing RAW dump to '%s'\n", f == stdin ? "stdin" : filename); + + /* Write RAW data to output file */ + fprintf(f, "version=%s\n", VERSION); + while ((raw_is_init && (logical_cpu < raw_array->num_raw)) || (!raw_is_init && (logical_cpu < 0))) { + if (raw_is_init) { + debugf(2, "Writing RAW dump for logical CPU %i\n", logical_cpu); + fprintf(f, "\n_________________ Logical CPU #%i _________________\n", logical_cpu); + raw_ptr = &raw_array->raw[logical_cpu]; + } + for (i = 0; i < MAX_CPUID_LEVEL; i++) + fprintf(f, "basic_cpuid[%d]=%08x %08x %08x %08x\n", i, + raw_ptr->basic_cpuid[i][EAX], raw_ptr->basic_cpuid[i][EBX], + raw_ptr->basic_cpuid[i][ECX], raw_ptr->basic_cpuid[i][EDX]); + for (i = 0; i < MAX_EXT_CPUID_LEVEL; i++) + fprintf(f, "ext_cpuid[%d]=%08x %08x %08x %08x\n", i, + raw_ptr->ext_cpuid[i][EAX], raw_ptr->ext_cpuid[i][EBX], + raw_ptr->ext_cpuid[i][ECX], raw_ptr->ext_cpuid[i][EDX]); + for (i = 0; i < MAX_INTELFN4_LEVEL; i++) + fprintf(f, "intel_fn4[%d]=%08x %08x %08x %08x\n", i, + raw_ptr->intel_fn4[i][EAX], raw_ptr->intel_fn4[i][EBX], + raw_ptr->intel_fn4[i][ECX], raw_ptr->intel_fn4[i][EDX]); + for (i = 0; i < MAX_INTELFN11_LEVEL; i++) + fprintf(f, "intel_fn11[%d]=%08x %08x %08x %08x\n", i, + raw_ptr->intel_fn11[i][EAX], raw_ptr->intel_fn11[i][EBX], + raw_ptr->intel_fn11[i][ECX], raw_ptr->intel_fn11[i][EDX]); + for (i = 0; i < MAX_INTELFN12H_LEVEL; i++) + fprintf(f, "intel_fn12h[%d]=%08x %08x %08x %08x\n", i, + raw_ptr->intel_fn12h[i][EAX], raw_ptr->intel_fn12h[i][EBX], + raw_ptr->intel_fn12h[i][ECX], raw_ptr->intel_fn12h[i][EDX]); + for (i = 0; i < MAX_INTELFN14H_LEVEL; i++) + fprintf(f, "intel_fn14h[%d]=%08x %08x %08x %08x\n", i, + raw_ptr->intel_fn14h[i][EAX], raw_ptr->intel_fn14h[i][EBX], + raw_ptr->intel_fn14h[i][ECX], raw_ptr->intel_fn14h[i][EDX]); + for (i = 0; i < MAX_AMDFN8000001DH_LEVEL; i++) + fprintf(f, "amd_fn8000001dh[%d]=%08x %08x %08x %08x\n", i, + raw_ptr->amd_fn8000001dh[i][EAX], raw_ptr->amd_fn8000001dh[i][EBX], + raw_ptr->amd_fn8000001dh[i][ECX], raw_ptr->amd_fn8000001dh[i][EDX]); + logical_cpu++; + } + + /* Close file descriptor */ + if (strcmp(filename, "")) + fclose(f); + return set_error(ERR_OK); +} + +#define RAW_ASSIGN_LINE(__line) __line[EAX] = eax ; __line[EBX] = ebx ; __line[ECX] = ecx ; __line[EDX] = edx +static int cpuid_deserialize_raw_data_internal(struct cpu_raw_data_t* single_raw, struct cpu_raw_data_array_t *raw_array, const char* filename) +{ + int i; + int cur_line = 0; + int assigned = 0; + int subleaf = 0; + bool is_libcpuid_dump = true; + bool is_aida64_dump = false; + const bool raw_is_init = (raw_array != NULL); + int16_t logical_cpu = -1; + uint32_t addr, eax, ebx, ecx, edx; + char version[8]; + char line[100]; + struct cpu_raw_data_t* raw_ptr = raw_is_init ? &raw_array->raw[0] : single_raw; + FILE *f; + + /* Open file descriptor */ + f = !strcmp(filename, "") ? stdin : fopen(filename, "rt"); + if (!f) + return set_error(ERR_OPEN); + debugf(1, "Opening RAW dump from '%s'\n", f == stdin ? "stdin" : filename); + + if (raw_is_init) { + raw_array->with_affinity = false; + raw_array->num_raw = raw_array->with_affinity ? 0 : 1; + raw_data_t_constructor(raw_ptr); + } + + /* Parse file and store data in cpu_raw_data_t */ + while (fgets(line, sizeof(line), f) != NULL) { + line[strcspn(line, "\n")] = '\0'; + cur_line++; + if (cur_line == 1) { + if (sscanf(line, "version=%s", version) >= 1) { + is_libcpuid_dump = true; + is_aida64_dump = false; + debugf(2, "Recognized version '%s' from RAW dump\n", version); + continue; + } + else if (!strcmp(line, "------[ Versions ]------") || !strcmp(line, "------[ Logical CPU #0 ]------") || !strcmp(line, "------[ CPUID Registers / Logical CPU #0 ]------")) { + is_libcpuid_dump = false; + is_aida64_dump = true; + debugf(2, "Recognized AIDA64 RAW dump\n"); + } + } + + if (is_libcpuid_dump) { + if (raw_is_init && (sscanf(line, "_________________ Logical CPU #%hi _________________", &logical_cpu) >= 1)) { + if (raw_array->num_raw >= CPU_RAW_MAX) { + warnf("RAW dump contains more than %d logical CPU, cannot parse more.\n", CPU_RAW_MAX); + return set_error(ERR_NO_MEM); + } + debugf(2, "Parsing RAW dump for logical CPU %i\n", logical_cpu); + raw_ptr = &raw_array->raw[logical_cpu]; + raw_array->with_affinity = true; + raw_array->num_raw = logical_cpu + 1; + raw_data_t_constructor(raw_ptr); + } + else if ((sscanf(line, "basic_cpuid[%d]=%x %x %x %x", &i, &eax, &ebx, &ecx, &edx) >= 5) && (i >= 0) && (i < MAX_CPUID_LEVEL)) { + RAW_ASSIGN_LINE(raw_ptr->basic_cpuid[i]); + } + else if ((sscanf(line, "ext_cpuid[%d]=%x %x %x %x", &i, &eax, &ebx, &ecx, &edx) >= 5) && (i >= 0) && (i < MAX_EXT_CPUID_LEVEL)) { + RAW_ASSIGN_LINE(raw_ptr->ext_cpuid[i]); + } + else if ((sscanf(line, "intel_fn4[%d]=%x %x %x %x", &i, &eax, &ebx, &ecx, &edx) >= 5) && (i >= 0) && (i < MAX_INTELFN4_LEVEL)) { + RAW_ASSIGN_LINE(raw_ptr->intel_fn4[i]); + } + else if ((sscanf(line, "intel_fn11[%d]=%x %x %x %x", &i, &eax, &ebx, &ecx, &edx) >= 5) && (i >= 0) && (i < MAX_INTELFN11_LEVEL)) { + RAW_ASSIGN_LINE(raw_ptr->intel_fn11[i]); + } + else if ((sscanf(line, "intel_fn12h[%d]=%x %x %x %x", &i, &eax, &ebx, &ecx, &edx) >= 5) && (i >= 0) && (i < MAX_INTELFN12H_LEVEL)) { + RAW_ASSIGN_LINE(raw_ptr->intel_fn12h[i]); + } + else if ((sscanf(line, "intel_fn14h[%d]=%x %x %x %x", &i, &eax, &ebx, &ecx, &edx) >= 5) && (i >= 0) && (i < MAX_INTELFN14H_LEVEL)) { + RAW_ASSIGN_LINE(raw_ptr->intel_fn14h[i]); + } + else if ((sscanf(line, "amd_fn8000001dh[%d]=%x %x %x %x", &i, &eax, &ebx, &ecx, &edx) >= 5) && (i >= 0) && (i < MAX_AMDFN8000001DH_LEVEL)) { + RAW_ASSIGN_LINE(raw_ptr->amd_fn8000001dh[i]); + } + else if (line[0] != '\0') { + warnf("Warning: file '%s', line %d: '%s' not understood!\n", filename, cur_line, line); + } + } + else if (is_aida64_dump) { + if (raw_is_init && ((sscanf(line, "------[Logical CPU #%hi ]------", &logical_cpu) >= 1) || \ + (sscanf(line, "------[ CPUID Registers / Logical CPU #%hi ]------", &logical_cpu) >= 1))) { + if (raw_array->num_raw >= CPU_RAW_MAX) { + warnf("AIDA64 RAW dump contains more than %d logical CPU, cannot parse more.\n", CPU_RAW_MAX); + return set_error(ERR_NO_MEM); + } + debugf(2, "Parsing AIDA64 RAW dump for logical CPU %i\n", logical_cpu); + raw_ptr = &raw_array->raw[logical_cpu]; + raw_array->with_affinity = true; + raw_array->num_raw = logical_cpu + 1; + raw_data_t_constructor(raw_ptr); + continue; + } + subleaf = 0; + assigned = sscanf(line, "CPUID %x: %x-%x-%x-%x [SL %02i]", &addr, &eax, &ebx, &ecx, &edx, &subleaf); + debugf(3, "RAW line %d: %i items assigned for string '%s'\n", cur_line, assigned, line); + if ((assigned >= 5) && (subleaf == 0)) { + if (addr < MAX_CPUID_LEVEL) { + i = (int) addr; + RAW_ASSIGN_LINE(raw_ptr->basic_cpuid[i]); + } + else if ((addr >= ADDRESS_EXT_CPUID_START) && (addr < ADDRESS_EXT_CPUID_END)) { + i = (int) addr - ADDRESS_EXT_CPUID_START; + RAW_ASSIGN_LINE(raw_ptr->ext_cpuid[i]); + } + } + if (assigned >= 6) { + i = subleaf; + switch (addr) { + case 0x00000004: RAW_ASSIGN_LINE(raw_ptr->intel_fn4[i]); break; + case 0x0000000B: RAW_ASSIGN_LINE(raw_ptr->intel_fn11[i]); break; + case 0x00000012: RAW_ASSIGN_LINE(raw_ptr->intel_fn12h[i]); break; + case 0x00000014: RAW_ASSIGN_LINE(raw_ptr->intel_fn14h[i]); break; + case 0x8000001D: RAW_ASSIGN_LINE(raw_ptr->amd_fn8000001dh[i]); break; + default: break; + } + } + } + } + + /* Close file descriptor */ + if (strcmp(filename, "")) + fclose(f); + return set_error(ERR_OK); +} +#undef RAW_ASSIGN_LINE static void load_features_common(struct cpu_raw_data_t* raw, struct cpu_id_t* data) { @@ -542,110 +711,53 @@ int cpuid_get_raw_data(struct cpu_raw_data_t* data) return set_error(ERR_OK); } +int cpuid_get_all_raw_data(struct cpu_raw_data_array_t *data) +{ + int cur_error = set_error(ERR_OK); + int ret_error = set_error(ERR_OK); + struct cpu_raw_data_t* raw_ptr = NULL; + + if (data == NULL) + return set_error(ERR_HANDLE); + + data->with_affinity = true; + data->num_raw = 0; + while (set_cpu_affinity(data->num_raw) == 0) { + if (data->num_raw >= CPU_RAW_MAX) { + warnf("System has more than %d logical CPU, cannot get more RAW.\n", CPU_RAW_MAX); + return set_error(ERR_NO_MEM); + } + debugf(2, "Getting RAW dump for logical CPU %i\n", data->num_raw); + raw_ptr = &data->raw[data->num_raw]; + raw_data_t_constructor(raw_ptr); + cur_error = cpuid_get_raw_data(raw_ptr); + if (ret_error == ERR_OK) + ret_error = cur_error; + data->num_raw++; + } + + return ret_error; +} + int cpuid_serialize_raw_data(struct cpu_raw_data_t* data, const char* filename) { - int i; - FILE *f; + return cpuid_serialize_raw_data_internal(data, NULL, filename); +} - if (!strcmp(filename, "")) - f = stdout; - else - f = fopen(filename, "wt"); - if (!f) return set_error(ERR_OPEN); - - fprintf(f, "version=%s\n", VERSION); - for (i = 0; i < MAX_CPUID_LEVEL; i++) - fprintf(f, "basic_cpuid[%d]=%08x %08x %08x %08x\n", i, - data->basic_cpuid[i][EAX], data->basic_cpuid[i][EBX], - data->basic_cpuid[i][ECX], data->basic_cpuid[i][EDX]); - for (i = 0; i < MAX_EXT_CPUID_LEVEL; i++) - fprintf(f, "ext_cpuid[%d]=%08x %08x %08x %08x\n", i, - data->ext_cpuid[i][EAX], data->ext_cpuid[i][EBX], - data->ext_cpuid[i][ECX], data->ext_cpuid[i][EDX]); - for (i = 0; i < MAX_INTELFN4_LEVEL; i++) - fprintf(f, "intel_fn4[%d]=%08x %08x %08x %08x\n", i, - data->intel_fn4[i][EAX], data->intel_fn4[i][EBX], - data->intel_fn4[i][ECX], data->intel_fn4[i][EDX]); - for (i = 0; i < MAX_INTELFN11_LEVEL; i++) - fprintf(f, "intel_fn11[%d]=%08x %08x %08x %08x\n", i, - data->intel_fn11[i][EAX], data->intel_fn11[i][EBX], - data->intel_fn11[i][ECX], data->intel_fn11[i][EDX]); - for (i = 0; i < MAX_INTELFN12H_LEVEL; i++) - fprintf(f, "intel_fn12h[%d]=%08x %08x %08x %08x\n", i, - data->intel_fn12h[i][EAX], data->intel_fn12h[i][EBX], - data->intel_fn12h[i][ECX], data->intel_fn12h[i][EDX]); - for (i = 0; i < MAX_INTELFN14H_LEVEL; i++) - fprintf(f, "intel_fn14h[%d]=%08x %08x %08x %08x\n", i, - data->intel_fn14h[i][EAX], data->intel_fn14h[i][EBX], - data->intel_fn14h[i][ECX], data->intel_fn14h[i][EDX]); - for (i = 0; i < MAX_AMDFN8000001DH_LEVEL; i++) - fprintf(f, "amd_fn8000001dh[%d]=%08x %08x %08x %08x\n", i, - data->amd_fn8000001dh[i][EAX], data->amd_fn8000001dh[i][EBX], - data->amd_fn8000001dh[i][ECX], data->amd_fn8000001dh[i][EDX]); - - if (strcmp(filename, "")) - fclose(f); - return set_error(ERR_OK); +int cpuid_serialize_all_raw_data(struct cpu_raw_data_array_t *data, const char* filename) +{ + return cpuid_serialize_raw_data_internal(NULL, data, filename); } int cpuid_deserialize_raw_data(struct cpu_raw_data_t* data, const char* filename) { - int i, len; - char line[100]; - char token[100]; - char *value; - int syntax; - int cur_line = 0; - int recognized; - FILE *f; - raw_data_t_constructor(data); + return cpuid_deserialize_raw_data_internal(data, NULL, filename); +} - if (!strcmp(filename, "")) - f = stdin; - else - f = fopen(filename, "rt"); - if (!f) return set_error(ERR_OPEN); - while (fgets(line, sizeof(line), f)) { - ++cur_line; - len = (int) strlen(line); - if (len < 2) continue; - if (line[len - 1] == '\n') - line[--len] = '\0'; - for (i = 0; i < len && line[i] != '='; i++) - if (i >= len && i < 1 && len - i - 1 <= 0) { - fclose(f); - return set_error(ERR_BADFMT); - } - strncpy(token, line, i); - token[i] = '\0'; - value = &line[i + 1]; - /* try to recognize the line */ - recognized = 0; - if (!strcmp(token, "version") || !strcmp(token, "build_date")) { - recognized = 1; - } - syntax = 1; - syntax = syntax && parse_token("basic_cpuid", token, value, data->basic_cpuid, MAX_CPUID_LEVEL, &recognized); - syntax = syntax && parse_token("ext_cpuid", token, value, data->ext_cpuid, MAX_EXT_CPUID_LEVEL, &recognized); - syntax = syntax && parse_token("intel_fn4", token, value, data->intel_fn4, MAX_INTELFN4_LEVEL, &recognized); - syntax = syntax && parse_token("intel_fn11", token, value, data->intel_fn11, MAX_INTELFN11_LEVEL, &recognized); - syntax = syntax && parse_token("intel_fn12h", token, value, data->intel_fn12h, MAX_INTELFN12H_LEVEL, &recognized); - syntax = syntax && parse_token("intel_fn14h", token, value, data->intel_fn14h, MAX_INTELFN14H_LEVEL, &recognized); - syntax = syntax && parse_token("amd_fn8000001dh", token, value, data->amd_fn8000001dh, MAX_AMDFN8000001DH_LEVEL, &recognized); - if (!syntax) { - warnf("Error: %s:%d: Syntax error\n", filename, cur_line); - fclose(f); - return set_error(ERR_BADFMT); - } - if (!recognized) { - warnf("Warning: %s:%d not understood!\n", filename, cur_line); - } - } - - if (strcmp(filename, "")) - fclose(f); - return set_error(ERR_OK); +int cpuid_deserialize_all_raw_data(struct cpu_raw_data_array_t *data, const char* filename) +{ + return cpuid_deserialize_raw_data_internal(NULL, data, filename); } int cpu_ident_internal(struct cpu_raw_data_t* raw, struct cpu_id_t* data, struct internal_id_info_t* internal) diff --git a/libcpuid/exports.def b/libcpuid/exports.def index 7ecb383..0717fd3 100644 --- a/libcpuid/exports.def +++ b/libcpuid/exports.def @@ -33,6 +33,9 @@ cpuid_get_vendor @29 cpu_rdmsr_range @30 cpuid_get_epc @31 msr_serialize_raw_data @32 +cpuid_get_all_raw_data @33 +cpuid_serialize_all_raw_data @34 +cpuid_deserialize_all_raw_data @35 cpu_identify_all @36 cpu_request_core_type @37 cpu_architecture_str @38 diff --git a/libcpuid/libcpuid.h b/libcpuid/libcpuid.h index 28ef8e6..d00981d 100644 --- a/libcpuid/libcpuid.h +++ b/libcpuid/libcpuid.h @@ -713,6 +713,15 @@ void cpu_exec_cpuid_ext(uint32_t* regs); */ int cpuid_get_raw_data(struct cpu_raw_data_t* data); +/** + * @brief Obtains the raw CPUID data from all CPUs + * @param data - a pointer to cpu_raw_data_array_t structure + * @returns zero if successful, and some negative number on error. + * The error message can be obtained by calling \ref cpuid_error. + * @see cpu_error_t + */ +int cpuid_get_all_raw_data(struct cpu_raw_data_array_t* data); + /** * @brief Writes the raw CPUID data to a text file * @param data - a pointer to cpu_raw_data_t structure @@ -732,6 +741,25 @@ int cpuid_get_raw_data(struct cpu_raw_data_t* data); */ int cpuid_serialize_raw_data(struct cpu_raw_data_t* data, const char* filename); +/** + * @brief Writes all the raw CPUID data to a text file + * @param data - a pointer to cpu_raw_data_array_t structure + * @param filename - the path of the file, where the serialized data for all CPUs + * should be written. If empty, stdout will be used. + * @note This is intended primarily for debugging. On some processor, which is + * not currently supported or not completely recognized by cpu_identify_all, + * one can still successfully get the raw data and write it to a file. + * libcpuid developers can later import this file and debug the detection + * code as if running on the actual hardware. + * The file is simple text format of "something=value" pairs. Version info + * is also written, but the format is not intended to be neither backward- + * nor forward compatible. + * @returns zero if successful, and some negative number on error. + * The error message can be obtained by calling \ref cpuid_error. + * @see cpu_error_t + */ +int cpuid_serialize_all_raw_data(struct cpu_raw_data_array_t *data, const char* filename); + /** * @brief Reads raw CPUID data from file * @param data - a pointer to cpu_raw_data_t structure. The deserialized data will @@ -746,6 +774,20 @@ int cpuid_serialize_raw_data(struct cpu_raw_data_t* data, const char* filename); */ int cpuid_deserialize_raw_data(struct cpu_raw_data_t* data, const char* filename); +/** + * @brief Reads all raw CPUID data from file + * @param data - a pointer to cpu_raw_data_array_t structure. The deserialized array data will + * be written here. + * @param filename - the path of the file, containing the serialized raw data. + * If empty, stdin will be used. + * @note This function may fail, if the file is created by different version of + * the library. Also, see the notes on cpuid_serialize_all_raw_data. + * @returns zero if successful, and some negative number on error. + * The error message can be obtained by calling \ref cpuid_error. + * @see cpu_error_t +*/ +int cpuid_deserialize_all_raw_data(struct cpu_raw_data_array_t *data, const char* filename); + /** * @brief Identifies the CPU * @param raw - Input - a pointer to the raw CPUID data, which is obtained diff --git a/libcpuid/libcpuid.sym b/libcpuid/libcpuid.sym index 0e6ad4e..621bc1b 100644 --- a/libcpuid/libcpuid.sym +++ b/libcpuid/libcpuid.sym @@ -30,6 +30,9 @@ cpuid_get_vendor cpu_rdmsr_range cpuid_get_epc msr_serialize_raw_data +cpuid_get_all_raw_data +cpuid_serialize_all_raw_data +cpuid_deserialize_all_raw_data cpu_identify_all cpu_request_core_type cpu_architecture_str diff --git a/libcpuid/libcpuid_constants.h b/libcpuid/libcpuid_constants.h index 45203f6..4e39b57 100644 --- a/libcpuid/libcpuid_constants.h +++ b/libcpuid/libcpuid_constants.h @@ -46,6 +46,8 @@ #define SGX_FLAGS_MAX 14 #define CPU_RAW_MAX 512 #define CPU_TYPE_MAX 8 +#define ADDRESS_EXT_CPUID_START 0x80000000 +#define ADDRESS_EXT_CPUID_END ADDRESS_EXT_CPUID_START + MAX_EXT_CPUID_LEVEL typedef enum { EAX,