forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
200 lines
5.0 KiB
200 lines
5.0 KiB
// SPDX-License-Identifier: GPL-2.0 |
|
#include <stdio.h> |
|
#include <errno.h> |
|
#include <string.h> |
|
#include <unistd.h> |
|
#include <stdlib.h> |
|
|
|
#include "helpers/helpers.h" |
|
|
|
static const char *cpu_vendor_table[X86_VENDOR_MAX] = { |
|
"Unknown", "GenuineIntel", "AuthenticAMD", "HygonGenuine", |
|
}; |
|
|
|
#if defined(__i386__) || defined(__x86_64__) |
|
|
|
/* from gcc */ |
|
#include <cpuid.h> |
|
|
|
/* |
|
* CPUID functions returning a single datum |
|
* |
|
* Define unsigned int cpuid_e[abcd]x(unsigned int op) |
|
*/ |
|
#define cpuid_func(reg) \ |
|
unsigned int cpuid_##reg(unsigned int op) \ |
|
{ \ |
|
unsigned int eax, ebx, ecx, edx; \ |
|
__cpuid(op, eax, ebx, ecx, edx); \ |
|
return reg; \ |
|
} |
|
cpuid_func(eax); |
|
cpuid_func(ebx); |
|
cpuid_func(ecx); |
|
cpuid_func(edx); |
|
|
|
#endif /* defined(__i386__) || defined(__x86_64__) */ |
|
|
|
/* get_cpu_info |
|
* |
|
* Extract CPU vendor, family, model, stepping info from /proc/cpuinfo |
|
* |
|
* Returns 0 on success or a negativ error code |
|
* |
|
* TBD: Should there be a cpuid alternative for this if /proc is not mounted? |
|
*/ |
|
int get_cpu_info(struct cpupower_cpu_info *cpu_info) |
|
{ |
|
FILE *fp; |
|
char value[64]; |
|
unsigned int proc, x; |
|
unsigned int unknown = 0xffffff; |
|
unsigned int cpuid_level, ext_cpuid_level; |
|
|
|
int ret = -EINVAL; |
|
|
|
cpu_info->vendor = X86_VENDOR_UNKNOWN; |
|
cpu_info->family = unknown; |
|
cpu_info->model = unknown; |
|
cpu_info->stepping = unknown; |
|
cpu_info->caps = 0; |
|
|
|
fp = fopen("/proc/cpuinfo", "r"); |
|
if (!fp) |
|
return -EIO; |
|
|
|
while (!feof(fp)) { |
|
if (!fgets(value, 64, fp)) |
|
continue; |
|
value[63 - 1] = '\0'; |
|
|
|
if (!strncmp(value, "processor\t: ", 12)) |
|
sscanf(value, "processor\t: %u", &proc); |
|
|
|
if (proc != (unsigned int)base_cpu) |
|
continue; |
|
|
|
/* Get CPU vendor */ |
|
if (!strncmp(value, "vendor_id", 9)) { |
|
for (x = 1; x < X86_VENDOR_MAX; x++) { |
|
if (strstr(value, cpu_vendor_table[x])) |
|
cpu_info->vendor = x; |
|
} |
|
/* Get CPU family, etc. */ |
|
} else if (!strncmp(value, "cpu family\t: ", 13)) { |
|
sscanf(value, "cpu family\t: %u", |
|
&cpu_info->family); |
|
} else if (!strncmp(value, "model\t\t: ", 9)) { |
|
sscanf(value, "model\t\t: %u", |
|
&cpu_info->model); |
|
} else if (!strncmp(value, "stepping\t: ", 10)) { |
|
sscanf(value, "stepping\t: %u", |
|
&cpu_info->stepping); |
|
|
|
/* Exit -> all values must have been set */ |
|
if (cpu_info->vendor == X86_VENDOR_UNKNOWN || |
|
cpu_info->family == unknown || |
|
cpu_info->model == unknown || |
|
cpu_info->stepping == unknown) { |
|
ret = -EINVAL; |
|
goto out; |
|
} |
|
|
|
ret = 0; |
|
goto out; |
|
} |
|
} |
|
ret = -ENODEV; |
|
out: |
|
fclose(fp); |
|
/* Get some useful CPU capabilities from cpuid */ |
|
if (cpu_info->vendor != X86_VENDOR_AMD && |
|
cpu_info->vendor != X86_VENDOR_HYGON && |
|
cpu_info->vendor != X86_VENDOR_INTEL) |
|
return ret; |
|
|
|
cpuid_level = cpuid_eax(0); |
|
ext_cpuid_level = cpuid_eax(0x80000000); |
|
|
|
/* Invariant TSC */ |
|
if (ext_cpuid_level >= 0x80000007 && |
|
(cpuid_edx(0x80000007) & (1 << 8))) |
|
cpu_info->caps |= CPUPOWER_CAP_INV_TSC; |
|
|
|
/* Aperf/Mperf registers support */ |
|
if (cpuid_level >= 6 && (cpuid_ecx(6) & 0x1)) |
|
cpu_info->caps |= CPUPOWER_CAP_APERF; |
|
|
|
/* AMD or Hygon Boost state enable/disable register */ |
|
if (cpu_info->vendor == X86_VENDOR_AMD || |
|
cpu_info->vendor == X86_VENDOR_HYGON) { |
|
if (ext_cpuid_level >= 0x80000007) { |
|
if (cpuid_edx(0x80000007) & (1 << 9)) { |
|
cpu_info->caps |= CPUPOWER_CAP_AMD_CPB; |
|
|
|
if (cpu_info->family >= 0x17) |
|
cpu_info->caps |= CPUPOWER_CAP_AMD_CPB_MSR; |
|
} |
|
|
|
if ((cpuid_edx(0x80000007) & (1 << 7)) && |
|
cpu_info->family != 0x14) { |
|
/* HW pstate was not implemented in family 0x14 */ |
|
cpu_info->caps |= CPUPOWER_CAP_AMD_HW_PSTATE; |
|
|
|
if (cpu_info->family >= 0x17) |
|
cpu_info->caps |= CPUPOWER_CAP_AMD_PSTATEDEF; |
|
} |
|
} |
|
|
|
if (ext_cpuid_level >= 0x80000008 && |
|
cpuid_ebx(0x80000008) & (1 << 4)) |
|
cpu_info->caps |= CPUPOWER_CAP_AMD_RDPRU; |
|
} |
|
|
|
if (cpu_info->vendor == X86_VENDOR_INTEL) { |
|
if (cpuid_level >= 6 && |
|
(cpuid_eax(6) & (1 << 1))) |
|
cpu_info->caps |= CPUPOWER_CAP_INTEL_IDA; |
|
} |
|
|
|
if (cpu_info->vendor == X86_VENDOR_INTEL) { |
|
/* Intel's perf-bias MSR support */ |
|
if (cpuid_level >= 6 && (cpuid_ecx(6) & (1 << 3))) |
|
cpu_info->caps |= CPUPOWER_CAP_PERF_BIAS; |
|
|
|
/* Intel's Turbo Ratio Limit support */ |
|
if (cpu_info->family == 6) { |
|
switch (cpu_info->model) { |
|
case 0x1A: /* Core i7, Xeon 5500 series |
|
* Bloomfield, Gainstown NHM-EP |
|
*/ |
|
case 0x1E: /* Core i7 and i5 Processor |
|
* Clarksfield, Lynnfield, Jasper Forest |
|
*/ |
|
case 0x1F: /* Core i7 and i5 Processor - Nehalem */ |
|
case 0x25: /* Westmere Client |
|
* Clarkdale, Arrandale |
|
*/ |
|
case 0x2C: /* Westmere EP - Gulftown */ |
|
cpu_info->caps |= CPUPOWER_CAP_HAS_TURBO_RATIO; |
|
break; |
|
case 0x2A: /* SNB */ |
|
case 0x2D: /* SNB Xeon */ |
|
case 0x3A: /* IVB */ |
|
case 0x3E: /* IVB Xeon */ |
|
cpu_info->caps |= CPUPOWER_CAP_HAS_TURBO_RATIO; |
|
cpu_info->caps |= CPUPOWER_CAP_IS_SNB; |
|
break; |
|
case 0x2E: /* Nehalem-EX Xeon - Beckton */ |
|
case 0x2F: /* Westmere-EX Xeon - Eagleton */ |
|
default: |
|
break; |
|
} |
|
} |
|
} |
|
|
|
/* printf("ID: %u - Extid: 0x%x - Caps: 0x%llx\n", |
|
cpuid_level, ext_cpuid_level, cpu_info->caps); |
|
*/ |
|
return ret; |
|
}
|
|
|