/* * Copyright 2008 Veselin Georgiev, * anrieffNOSPAM @ mgail_DOT.com (convert to gmail) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "libcpuid.h" #include "libcpuid_internal.h" #include "recog_intel.h" #include "recog_amd.h" #include "asm-bits.h" #include "libcpuid_util.h" #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include /* Implementation: */ static int _libcpiud_errno = ERR_OK; int set_error(cpu_error_t err) { _libcpiud_errno = (int) err; return (int) err; } static void raw_data_t_constructor(struct cpu_raw_data_t* raw) { memset(raw, 0, sizeof(struct cpu_raw_data_t)); } static void cpu_id_t_constructor(struct cpu_id_t* id) { memset(id, 0, sizeof(struct cpu_id_t)); id->l1_data_cache = id->l1_instruction_cache = id->l2_cache = id->l3_cache = id->l4_cache = -1; id->l1_assoc = id->l2_assoc = id->l3_assoc = id->l4_assoc = -1; id->l1_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; } /* get_total_cpus() system specific code: uses OS routines to determine total number of CPUs */ #ifdef __APPLE__ #include #include #include #include static int get_total_cpus(void) { kern_return_t kr; host_basic_info_data_t basic_info; host_info_t info = (host_info_t)&basic_info; host_flavor_t flavor = HOST_BASIC_INFO; mach_msg_type_number_t count = HOST_BASIC_INFO_COUNT; kr = host_info(mach_host_self(), flavor, info, &count); if (kr != KERN_SUCCESS) return 1; return basic_info.avail_cpus; } #define GET_TOTAL_CPUS_DEFINED #endif #ifdef _WIN32 #include static int get_total_cpus(void) { SYSTEM_INFO system_info; GetSystemInfo(&system_info); return system_info.dwNumberOfProcessors; } #define GET_TOTAL_CPUS_DEFINED #endif #if defined linux || defined __linux__ || defined __sun #include #include static int get_total_cpus(void) { return sysconf(_SC_NPROCESSORS_ONLN); } #define GET_TOTAL_CPUS_DEFINED #endif #if defined __FreeBSD__ || defined __OpenBSD__ || defined __NetBSD__ || defined __bsdi__ || defined __QNX__ #include #include static int get_total_cpus(void) { int mib[2] = { CTL_HW, HW_NCPU }; int ncpus; size_t len = sizeof(ncpus); if (sysctl(mib, 2, &ncpus, &len, (void *) 0, 0) != 0) return 1; return ncpus; } #define GET_TOTAL_CPUS_DEFINED #endif #ifndef GET_TOTAL_CPUS_DEFINED static int get_total_cpus(void) { static int warning_printed = 0; if (!warning_printed) { warning_printed = 1; warnf("Your system is not supported by libcpuid -- don't know how to detect the\n"); warnf("total number of CPUs on your system. It will be reported as 1.\n"); printf("Please use cpu_id_t.logical_cpus field instead.\n"); } return 1; } #endif /* GET_TOTAL_CPUS_DEFINED */ static void load_features_common(struct cpu_raw_data_t* raw, struct cpu_id_t* data) { const struct feature_map_t matchtable_edx1[] = { { 0, CPU_FEATURE_FPU }, { 1, CPU_FEATURE_VME }, { 2, CPU_FEATURE_DE }, { 3, CPU_FEATURE_PSE }, { 4, CPU_FEATURE_TSC }, { 5, CPU_FEATURE_MSR }, { 6, CPU_FEATURE_PAE }, { 7, CPU_FEATURE_MCE }, { 8, CPU_FEATURE_CX8 }, { 9, CPU_FEATURE_APIC }, { 11, CPU_FEATURE_SEP }, { 12, CPU_FEATURE_MTRR }, { 13, CPU_FEATURE_PGE }, { 14, CPU_FEATURE_MCA }, { 15, CPU_FEATURE_CMOV }, { 16, CPU_FEATURE_PAT }, { 17, CPU_FEATURE_PSE36 }, { 19, CPU_FEATURE_CLFLUSH }, { 23, CPU_FEATURE_MMX }, { 24, CPU_FEATURE_FXSR }, { 25, CPU_FEATURE_SSE }, { 26, CPU_FEATURE_SSE2 }, { 28, CPU_FEATURE_HT }, }; const struct feature_map_t matchtable_ecx1[] = { { 0, CPU_FEATURE_PNI }, { 1, CPU_FEATURE_PCLMUL }, { 3, CPU_FEATURE_MONITOR }, { 9, CPU_FEATURE_SSSE3 }, { 12, CPU_FEATURE_FMA3 }, { 13, CPU_FEATURE_CX16 }, { 19, CPU_FEATURE_SSE4_1 }, { 20, CPU_FEATURE_SSE4_2 }, { 22, CPU_FEATURE_MOVBE }, { 23, CPU_FEATURE_POPCNT }, { 25, CPU_FEATURE_AES }, { 26, CPU_FEATURE_XSAVE }, { 27, CPU_FEATURE_OSXSAVE }, { 28, CPU_FEATURE_AVX }, { 29, CPU_FEATURE_F16C }, { 30, CPU_FEATURE_RDRAND }, }; const struct feature_map_t matchtable_ebx7[] = { { 3, CPU_FEATURE_BMI1 }, { 5, CPU_FEATURE_AVX2 }, { 8, CPU_FEATURE_BMI2 }, }; const struct feature_map_t matchtable_edx81[] = { { 11, CPU_FEATURE_SYSCALL }, { 27, CPU_FEATURE_RDTSCP }, { 29, CPU_FEATURE_LM }, }; const struct feature_map_t matchtable_ecx81[] = { { 0, CPU_FEATURE_LAHF_LM }, }; const struct feature_map_t matchtable_edx87[] = { { 8, CPU_FEATURE_CONSTANT_TSC }, }; if (raw->basic_cpuid[0][0] >= 1) { match_features(matchtable_edx1, COUNT_OF(matchtable_edx1), raw->basic_cpuid[1][3], data); match_features(matchtable_ecx1, COUNT_OF(matchtable_ecx1), raw->basic_cpuid[1][2], data); } if (raw->basic_cpuid[0][0] >= 7) { match_features(matchtable_ebx7, COUNT_OF(matchtable_ebx7), raw->basic_cpuid[7][1], data); } if (raw->ext_cpuid[0][0] >= 0x80000001) { match_features(matchtable_edx81, COUNT_OF(matchtable_edx81), raw->ext_cpuid[1][3], data); match_features(matchtable_ecx81, COUNT_OF(matchtable_ecx81), raw->ext_cpuid[1][2], data); } if (raw->ext_cpuid[0][0] >= 0x80000007) { match_features(matchtable_edx87, COUNT_OF(matchtable_edx87), raw->ext_cpuid[7][3], data); } if (data->flags[CPU_FEATURE_SSE]) { /* apply guesswork to check if the SSE unit width is 128 bit */ switch (data->vendor) { case VENDOR_AMD: data->sse_size = (data->ext_family >= 16 && data->ext_family != 17) ? 128 : 64; break; case VENDOR_INTEL: data->sse_size = (data->family == 6 && data->ext_model >= 15) ? 128 : 64; break; default: break; } /* leave the CPU_FEATURE_128BIT_SSE_AUTH 0; the advanced per-vendor detection routines * will set it accordingly if they detect the needed bit */ } } static cpu_vendor_t cpuid_vendor_identify(const uint32_t *raw_vendor, char *vendor_str) { int i; cpu_vendor_t vendor = VENDOR_UNKNOWN; const struct { cpu_vendor_t vendor; char match[16]; } matchtable[NUM_CPU_VENDORS] = { /* source: http://www.sandpile.org/ia32/cpuid.htm */ { VENDOR_INTEL , "GenuineIntel" }, { VENDOR_AMD , "AuthenticAMD" }, { VENDOR_CYRIX , "CyrixInstead" }, { VENDOR_NEXGEN , "NexGenDriven" }, { VENDOR_TRANSMETA , "GenuineTMx86" }, { VENDOR_UMC , "UMC UMC UMC " }, { VENDOR_CENTAUR , "CentaurHauls" }, { VENDOR_RISE , "RiseRiseRise" }, { VENDOR_SIS , "SiS SiS SiS " }, { VENDOR_NSC , "Geode by NSC" }, }; memcpy(vendor_str + 0, &raw_vendor[1], 4); memcpy(vendor_str + 4, &raw_vendor[3], 4); memcpy(vendor_str + 8, &raw_vendor[2], 4); vendor_str[12] = 0; /* Determine vendor: */ for (i = 0; i < NUM_CPU_VENDORS; i++) if (!strcmp(vendor_str, matchtable[i].match)) { vendor = matchtable[i].vendor; break; } return vendor; } static int cpuid_basic_identify(struct cpu_raw_data_t* raw, struct cpu_id_t* data) { int i, j, basic, xmodel, xfamily, ext; char brandstr[64] = {0}; data->vendor = cpuid_vendor_identify(raw->basic_cpuid[0], data->vendor_str); if (data->vendor == VENDOR_UNKNOWN) return set_error(ERR_CPU_UNKN); basic = raw->basic_cpuid[0][0]; if (basic >= 1) { data->family = (raw->basic_cpuid[1][0] >> 8) & 0xf; data->model = (raw->basic_cpuid[1][0] >> 4) & 0xf; data->stepping = raw->basic_cpuid[1][0] & 0xf; xmodel = (raw->basic_cpuid[1][0] >> 16) & 0xf; xfamily = (raw->basic_cpuid[1][0] >> 20) & 0xff; if (data->vendor == VENDOR_AMD && data->family < 0xf) data->ext_family = data->family; else data->ext_family = data->family + xfamily; data->ext_model = data->model + (xmodel << 4); } ext = raw->ext_cpuid[0][0] - 0x8000000; /* obtain the brand string, if present: */ if (ext >= 4) { for (i = 0; i < 3; i++) for (j = 0; j < 4; j++) memcpy(brandstr + i * 16 + j * 4, &raw->ext_cpuid[2 + i][j], 4); brandstr[48] = 0; i = 0; while (brandstr[i] == ' ') i++; strncpy(data->brand_str, brandstr + i, sizeof(data->brand_str)); data->brand_str[48] = 0; } load_features_common(raw, data); data->total_logical_cpus = get_total_cpus(); return set_error(ERR_OK); } static void make_list_from_string(const char* csv, struct cpu_list_t* list) { int i, n, l, last; l = (int) strlen(csv); n = 0; for (i = 0; i < l; i++) if (csv[i] == ',') n++; n++; list->num_entries = n; list->names = (char**) malloc(sizeof(char*) * n); last = -1; n = 0; for (i = 0; i <= l; i++) if (i == l || csv[i] == ',') { list->names[n] = (char*) malloc(i - last); memcpy(list->names[n], &csv[last + 1], i - last - 1); list->names[n][i - last - 1] = '\0'; n++; last = i; } } /* Interface: */ int cpuid_get_total_cpus(void) { return get_total_cpus(); } int cpuid_present(void) { return cpuid_exists_by_eflags(); } void cpu_exec_cpuid(uint32_t eax, uint32_t* regs) { regs[0] = eax; regs[1] = regs[2] = regs[3] = 0; exec_cpuid(regs); } void cpu_exec_cpuid_ext(uint32_t* regs) { exec_cpuid(regs); } int cpuid_get_raw_data(struct cpu_raw_data_t* data) { unsigned i; if (!cpuid_present()) return set_error(ERR_NO_CPUID); for (i = 0; i < 32; i++) cpu_exec_cpuid(i, data->basic_cpuid[i]); for (i = 0; i < 32; i++) cpu_exec_cpuid(0x80000000 + i, data->ext_cpuid[i]); for (i = 0; i < MAX_INTELFN4_LEVEL; i++) { memset(data->intel_fn4[i], 0, sizeof(data->intel_fn4[i])); data->intel_fn4[i][0] = 4; data->intel_fn4[i][2] = i; cpu_exec_cpuid_ext(data->intel_fn4[i]); } for (i = 0; i < MAX_INTELFN11_LEVEL; i++) { memset(data->intel_fn11[i], 0, sizeof(data->intel_fn11[i])); data->intel_fn11[i][0] = 11; data->intel_fn11[i][2] = i; cpu_exec_cpuid_ext(data->intel_fn11[i]); } for (i = 0; i < MAX_INTELFN12H_LEVEL; i++) { memset(data->intel_fn12h[i], 0, sizeof(data->intel_fn12h[i])); data->intel_fn12h[i][0] = 0x12; data->intel_fn12h[i][2] = i; cpu_exec_cpuid_ext(data->intel_fn12h[i]); } for (i = 0; i < MAX_INTELFN14H_LEVEL; i++) { memset(data->intel_fn14h[i], 0, sizeof(data->intel_fn14h[i])); data->intel_fn14h[i][0] = 0x14; data->intel_fn14h[i][2] = i; cpu_exec_cpuid_ext(data->intel_fn14h[i]); } return set_error(ERR_OK); } int cpu_ident_internal(struct cpu_raw_data_t* raw, struct cpu_id_t* data, struct internal_id_info_t* internal) { int r; struct cpu_raw_data_t myraw; if (!raw) { if ((r = cpuid_get_raw_data(&myraw)) < 0) return set_error(r); raw = &myraw; } cpu_id_t_constructor(data); if ((r = cpuid_basic_identify(raw, data)) < 0) return set_error(r); switch (data->vendor) { case VENDOR_INTEL: r = cpuid_identify_intel(raw, data, internal); break; case VENDOR_AMD: r = cpuid_identify_amd(raw, data, internal); break; default: break; } return set_error(r); } int cpu_identify(struct cpu_raw_data_t* raw, struct cpu_id_t* data) { struct internal_id_info_t throwaway; return cpu_ident_internal(raw, data, &throwaway); } const char* cpuid_lib_version(void) { return VERSION; }