2015-02-22 10:50:36 +00:00
|
|
|
/*
|
|
|
|
* 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 "recog_intel.h"
|
|
|
|
#include "recog_amd.h"
|
|
|
|
#include "asm-bits.h"
|
|
|
|
#include "libcpuid_util.h"
|
|
|
|
#include "config.h"
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
|
|
/* 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 = -1;
|
|
|
|
id->l1_assoc = id->l2_assoc = id->l3_assoc = -1;
|
|
|
|
id->l1_cacheline = id->l2_cacheline = id->l3_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 <unistd.h>
|
|
|
|
#include <mach/clock_types.h>
|
|
|
|
#include <mach/clock.h>
|
|
|
|
#include <mach/mach.h>
|
|
|
|
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 <windows.h>
|
|
|
|
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 <sys/sysinfo.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
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__
|
2016-12-24 01:03:10 +00:00
|
|
|
#include <sys/types.h>
|
2015-02-22 10:50:36 +00:00
|
|
|
#include <sys/sysctl.h>
|
|
|
|
|
|
|
|
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 },
|
|
|
|
{ 3, CPU_FEATURE_MONITOR },
|
|
|
|
{ 9, CPU_FEATURE_SSSE3 },
|
|
|
|
{ 12, CPU_FEATURE_FMA3 },
|
|
|
|
{ 13, CPU_FEATURE_CX16 },
|
|
|
|
{ 19, CPU_FEATURE_SSE4_1 },
|
|
|
|
{ 21, CPU_FEATURE_X2APIC },
|
|
|
|
{ 23, CPU_FEATURE_POPCNT },
|
|
|
|
{ 29, CPU_FEATURE_F16C },
|
|
|
|
};
|
|
|
|
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->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 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};
|
|
|
|
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(data->vendor_str + 0, &raw->basic_cpuid[0][1], 4);
|
|
|
|
memcpy(data->vendor_str + 4, &raw->basic_cpuid[0][3], 4);
|
|
|
|
memcpy(data->vendor_str + 8, &raw->basic_cpuid[0][2], 4);
|
|
|
|
data->vendor_str[12] = 0;
|
|
|
|
/* Determine vendor: */
|
|
|
|
data->vendor = VENDOR_UNKNOWN;
|
|
|
|
for (i = 0; i < NUM_CPU_VENDORS; i++)
|
|
|
|
if (!strcmp(data->vendor_str, matchtable[i].match)) {
|
|
|
|
data->vendor = matchtable[i].vendor;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
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 < 4; 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]);
|
|
|
|
}
|
|
|
|
return set_error(ERR_OK);
|
|
|
|
}
|
|
|
|
|
|
|
|
int cpuid_serialize_raw_data(struct cpu_raw_data_t* data, const char* filename)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
FILE *f;
|
|
|
|
|
|
|
|
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][0], data->basic_cpuid[i][1],
|
|
|
|
data->basic_cpuid[i][2], data->basic_cpuid[i][3]);
|
|
|
|
for (i = 0; i < MAX_EXT_CPUID_LEVEL; i++)
|
|
|
|
fprintf(f, "ext_cpuid[%d]=%08x %08x %08x %08x\n", i,
|
|
|
|
data->ext_cpuid[i][0], data->ext_cpuid[i][1],
|
|
|
|
data->ext_cpuid[i][2], data->ext_cpuid[i][3]);
|
|
|
|
for (i = 0; i < MAX_INTELFN4_LEVEL; i++)
|
|
|
|
fprintf(f, "intel_fn4[%d]=%08x %08x %08x %08x\n", i,
|
|
|
|
data->intel_fn4[i][0], data->intel_fn4[i][1],
|
|
|
|
data->intel_fn4[i][2], data->intel_fn4[i][3]);
|
|
|
|
for (i = 0; i < MAX_INTELFN11_LEVEL; i++)
|
|
|
|
fprintf(f, "intel_fn11[%d]=%08x %08x %08x %08x\n", i,
|
|
|
|
data->intel_fn11[i][0], data->intel_fn11[i][1],
|
|
|
|
data->intel_fn11[i][2], data->intel_fn11[i][3]);
|
|
|
|
|
|
|
|
if (strcmp(filename, ""))
|
|
|
|
fclose(f);
|
|
|
|
return set_error(ERR_OK);
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
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, 32, &recognized);
|
|
|
|
syntax = syntax && parse_token("ext_cpuid", token, value, data->ext_cpuid, 32, &recognized);
|
|
|
|
syntax = syntax && parse_token("intel_fn4", token, value, data->intel_fn4, 4, &recognized);
|
|
|
|
syntax = syntax && parse_token("intel_fn11", token, value, data->intel_fn11, 4, &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 cpu_identify(struct cpu_raw_data_t* raw, struct cpu_id_t* data)
|
|
|
|
{
|
|
|
|
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);
|
|
|
|
break;
|
|
|
|
case VENDOR_AMD:
|
|
|
|
r = cpuid_identify_amd(raw, data);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return set_error(r);
|
|
|
|
}
|
|
|
|
|
|
|
|
const char* cpu_feature_str(cpu_feature_t feature)
|
|
|
|
{
|
|
|
|
const struct { cpu_feature_t feature; const char* name; }
|
|
|
|
matchtable[] = {
|
|
|
|
{ CPU_FEATURE_FPU, "fpu" },
|
|
|
|
{ CPU_FEATURE_VME, "vme" },
|
|
|
|
{ CPU_FEATURE_DE, "de" },
|
|
|
|
{ CPU_FEATURE_PSE, "pse" },
|
|
|
|
{ CPU_FEATURE_TSC, "tsc" },
|
|
|
|
{ CPU_FEATURE_MSR, "msr" },
|
|
|
|
{ CPU_FEATURE_PAE, "pae" },
|
|
|
|
{ CPU_FEATURE_MCE, "mce" },
|
|
|
|
{ CPU_FEATURE_CX8, "cx8" },
|
|
|
|
{ CPU_FEATURE_APIC, "apic" },
|
|
|
|
{ CPU_FEATURE_MTRR, "mtrr" },
|
|
|
|
{ CPU_FEATURE_SEP, "sep" },
|
|
|
|
{ CPU_FEATURE_PGE, "pge" },
|
|
|
|
{ CPU_FEATURE_MCA, "mca" },
|
|
|
|
{ CPU_FEATURE_CMOV, "cmov" },
|
|
|
|
{ CPU_FEATURE_PAT, "pat" },
|
|
|
|
{ CPU_FEATURE_PSE36, "pse36" },
|
|
|
|
{ CPU_FEATURE_PN, "pn" },
|
|
|
|
{ CPU_FEATURE_CLFLUSH, "clflush" },
|
|
|
|
{ CPU_FEATURE_DTS, "dts" },
|
|
|
|
{ CPU_FEATURE_ACPI, "acpi" },
|
|
|
|
{ CPU_FEATURE_MMX, "mmx" },
|
|
|
|
{ CPU_FEATURE_FXSR, "fxsr" },
|
|
|
|
{ CPU_FEATURE_SSE, "sse" },
|
|
|
|
{ CPU_FEATURE_SSE2, "sse2" },
|
|
|
|
{ CPU_FEATURE_SS, "ss" },
|
|
|
|
{ CPU_FEATURE_HT, "ht" },
|
|
|
|
{ CPU_FEATURE_TM, "tm" },
|
|
|
|
{ CPU_FEATURE_IA64, "ia64" },
|
|
|
|
{ CPU_FEATURE_PBE, "pbe" },
|
|
|
|
{ CPU_FEATURE_PNI, "pni" },
|
|
|
|
{ CPU_FEATURE_PCLMUL, "pclmul" },
|
|
|
|
{ CPU_FEATURE_DTS64, "dts64" },
|
|
|
|
{ CPU_FEATURE_MONITOR, "monitor" },
|
|
|
|
{ CPU_FEATURE_DS_CPL, "ds_cpl" },
|
|
|
|
{ CPU_FEATURE_VMX, "vmx" },
|
|
|
|
{ CPU_FEATURE_SMX, "smx" },
|
|
|
|
{ CPU_FEATURE_EST, "est" },
|
|
|
|
{ CPU_FEATURE_TM2, "tm2" },
|
|
|
|
{ CPU_FEATURE_SSSE3, "ssse3" },
|
|
|
|
{ CPU_FEATURE_CID, "cid" },
|
|
|
|
{ CPU_FEATURE_CX16, "cx16" },
|
|
|
|
{ CPU_FEATURE_XTPR, "xtpr" },
|
|
|
|
{ CPU_FEATURE_PDCM, "pdcm" },
|
|
|
|
{ CPU_FEATURE_DCA, "dca" },
|
|
|
|
{ CPU_FEATURE_SSE4_1, "sse4_1" },
|
|
|
|
{ CPU_FEATURE_SSE4_2, "sse4_2" },
|
|
|
|
{ CPU_FEATURE_SYSCALL, "syscall" },
|
|
|
|
{ CPU_FEATURE_XD, "xd" },
|
|
|
|
{ CPU_FEATURE_X2APIC, "x2apic"},
|
|
|
|
{ CPU_FEATURE_MOVBE, "movbe" },
|
|
|
|
{ CPU_FEATURE_POPCNT, "popcnt" },
|
|
|
|
{ CPU_FEATURE_AES, "aes" },
|
|
|
|
{ CPU_FEATURE_XSAVE, "xsave" },
|
|
|
|
{ CPU_FEATURE_OSXSAVE, "osxsave" },
|
|
|
|
{ CPU_FEATURE_AVX, "avx" },
|
|
|
|
{ CPU_FEATURE_MMXEXT, "mmxext" },
|
|
|
|
{ CPU_FEATURE_3DNOW, "3dnow" },
|
|
|
|
{ CPU_FEATURE_3DNOWEXT, "3dnowext" },
|
|
|
|
{ CPU_FEATURE_NX, "nx" },
|
|
|
|
{ CPU_FEATURE_FXSR_OPT, "fxsr_opt" },
|
|
|
|
{ CPU_FEATURE_RDTSCP, "rdtscp" },
|
|
|
|
{ CPU_FEATURE_LM, "lm" },
|
|
|
|
{ CPU_FEATURE_LAHF_LM, "lahf_lm" },
|
|
|
|
{ CPU_FEATURE_CMP_LEGACY, "cmp_legacy" },
|
|
|
|
{ CPU_FEATURE_SVM, "svm" },
|
|
|
|
{ CPU_FEATURE_SSE4A, "sse4a" },
|
|
|
|
{ CPU_FEATURE_MISALIGNSSE, "misalignsse" },
|
|
|
|
{ CPU_FEATURE_ABM, "abm" },
|
|
|
|
{ CPU_FEATURE_3DNOWPREFETCH, "3dnowprefetch" },
|
|
|
|
{ CPU_FEATURE_OSVW, "osvw" },
|
|
|
|
{ CPU_FEATURE_IBS, "ibs" },
|
|
|
|
{ CPU_FEATURE_SSE5, "sse5" },
|
|
|
|
{ CPU_FEATURE_SKINIT, "skinit" },
|
|
|
|
{ CPU_FEATURE_WDT, "wdt" },
|
|
|
|
{ CPU_FEATURE_TS, "ts" },
|
|
|
|
{ CPU_FEATURE_FID, "fid" },
|
|
|
|
{ CPU_FEATURE_VID, "vid" },
|
|
|
|
{ CPU_FEATURE_TTP, "ttp" },
|
|
|
|
{ CPU_FEATURE_TM_AMD, "tm_amd" },
|
|
|
|
{ CPU_FEATURE_STC, "stc" },
|
|
|
|
{ CPU_FEATURE_100MHZSTEPS, "100mhzsteps" },
|
|
|
|
{ CPU_FEATURE_HWPSTATE, "hwpstate" },
|
|
|
|
{ CPU_FEATURE_CONSTANT_TSC, "constant_tsc" },
|
|
|
|
{ CPU_FEATURE_XOP, "xop" },
|
|
|
|
{ CPU_FEATURE_FMA3, "fma3" },
|
|
|
|
{ CPU_FEATURE_FMA4, "fma4" },
|
|
|
|
{ CPU_FEATURE_TBM, "tbm" },
|
|
|
|
{ CPU_FEATURE_F16C, "f16c" },
|
|
|
|
{ CPU_FEATURE_RDRAND, "rdrand" },
|
|
|
|
{ CPU_FEATURE_CPB, "cpb" },
|
|
|
|
{ CPU_FEATURE_APERFMPERF, "aperfmperf" },
|
|
|
|
{ CPU_FEATURE_PFI, "pfi" },
|
|
|
|
{ CPU_FEATURE_PA, "pa" },
|
|
|
|
{ CPU_FEATURE_AVX2, "avx2" },
|
|
|
|
};
|
|
|
|
unsigned i, n = COUNT_OF(matchtable);
|
|
|
|
if (n != NUM_CPU_FEATURES) {
|
|
|
|
warnf("Warning: incomplete library, feature matchtable size differs from the actual number of features.\n");
|
|
|
|
}
|
|
|
|
for (i = 0; i < n; i++)
|
|
|
|
if (matchtable[i].feature == feature)
|
|
|
|
return matchtable[i].name;
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
|
|
|
const char* cpuid_error(void)
|
|
|
|
{
|
|
|
|
const struct { cpu_error_t error; const char *description; }
|
|
|
|
matchtable[] = {
|
|
|
|
{ ERR_OK , "No error"},
|
|
|
|
{ ERR_NO_CPUID , "CPUID instruction is not supported"},
|
|
|
|
{ ERR_NO_RDTSC , "RDTSC instruction is not supported"},
|
|
|
|
{ ERR_NO_MEM , "Memory allocation failed"},
|
|
|
|
{ ERR_OPEN , "File open operation failed"},
|
|
|
|
{ ERR_BADFMT , "Bad file format"},
|
|
|
|
{ ERR_NOT_IMP , "Not implemented"},
|
|
|
|
{ ERR_CPU_UNKN , "Unsupported processor"},
|
|
|
|
};
|
|
|
|
unsigned i;
|
|
|
|
for (i = 0; i < COUNT_OF(matchtable); i++)
|
|
|
|
if (_libcpiud_errno == matchtable[i].error)
|
|
|
|
return matchtable[i].description;
|
|
|
|
return "Unknown error";
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const char* cpuid_lib_version(void)
|
|
|
|
{
|
|
|
|
return VERSION;
|
|
|
|
}
|
|
|
|
|
|
|
|
libcpuid_warn_fn_t cpuid_set_warn_function(libcpuid_warn_fn_t new_fn)
|
|
|
|
{
|
|
|
|
libcpuid_warn_fn_t ret = _warn_fun;
|
|
|
|
_warn_fun = new_fn;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
void cpuid_set_verbosiness_level(int level)
|
|
|
|
{
|
|
|
|
_current_verboselevel = level;
|
|
|
|
}
|
|
|
|
|
|
|
|
void cpuid_get_cpu_list(cpu_vendor_t vendor, struct cpu_list_t* list)
|
|
|
|
{
|
|
|
|
switch (vendor) {
|
|
|
|
case VENDOR_INTEL:
|
|
|
|
cpuid_get_list_intel(list);
|
|
|
|
break;
|
|
|
|
case VENDOR_AMD:
|
|
|
|
cpuid_get_list_amd(list);
|
|
|
|
break;
|
|
|
|
case VENDOR_CYRIX:
|
|
|
|
make_list_from_string("Cx486,Cx5x86,6x86,6x86MX,M II,MediaGX,MediaGXi,MediaGXm", list);
|
|
|
|
break;
|
|
|
|
case VENDOR_NEXGEN:
|
|
|
|
make_list_from_string("Nx586", list);
|
|
|
|
break;
|
|
|
|
case VENDOR_TRANSMETA:
|
|
|
|
make_list_from_string("Crusoe,Efficeon", list);
|
|
|
|
break;
|
|
|
|
case VENDOR_UMC:
|
|
|
|
make_list_from_string("UMC x86 CPU", list);
|
|
|
|
break;
|
|
|
|
case VENDOR_CENTAUR:
|
|
|
|
make_list_from_string("VIA C3,VIA C7,VIA Nano", list);
|
|
|
|
break;
|
|
|
|
case VENDOR_RISE:
|
|
|
|
make_list_from_string("Rise mP6", list);
|
|
|
|
break;
|
|
|
|
case VENDOR_SIS:
|
|
|
|
make_list_from_string("SiS mP6", list);
|
|
|
|
break;
|
|
|
|
case VENDOR_NSC:
|
|
|
|
make_list_from_string("Geode GXm,Geode GXLV,Geode GX1,Geode GX2", list);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
warnf("Unknown vendor passed to cpuid_get_cpu_list()\n");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void cpuid_free_cpu_list(struct cpu_list_t* list)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
if (list->num_entries <= 0) return;
|
|
|
|
for (i = 0; i < list->num_entries; i++)
|
|
|
|
free(list->names[i]);
|
|
|
|
free(list->names);
|
|
|
|
}
|