// 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 (cpupower_amd_pstate_enabled()) { cpu_info->caps |= CPUPOWER_CAP_AMD_PSTATE; /* * If AMD P-State is enabled, the firmware will treat * AMD P-State function as high priority. */ cpu_info->caps &= ~CPUPOWER_CAP_AMD_CPB; cpu_info->caps &= ~CPUPOWER_CAP_AMD_CPB_MSR; cpu_info->caps &= ~CPUPOWER_CAP_AMD_HW_PSTATE; cpu_info->caps &= ~CPUPOWER_CAP_AMD_PSTATEDEF; } } 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; }