1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0 27fe2f639SDominik Brodowski #include <stdio.h> 37fe2f639SDominik Brodowski #include <errno.h> 47fe2f639SDominik Brodowski #include <string.h> 57fe2f639SDominik Brodowski #include <unistd.h> 67fe2f639SDominik Brodowski #include <stdlib.h> 77fe2f639SDominik Brodowski 87fe2f639SDominik Brodowski #include "helpers/helpers.h" 97fe2f639SDominik Brodowski 107fe2f639SDominik Brodowski static const char *cpu_vendor_table[X86_VENDOR_MAX] = { 11*995d5f64SPu Wen "Unknown", "GenuineIntel", "AuthenticAMD", "HygonGenuine", 127fe2f639SDominik Brodowski }; 137fe2f639SDominik Brodowski 147fe2f639SDominik Brodowski #if defined(__i386__) || defined(__x86_64__) 157fe2f639SDominik Brodowski 167fe2f639SDominik Brodowski /* from gcc */ 177fe2f639SDominik Brodowski #include <cpuid.h> 187fe2f639SDominik Brodowski 197fe2f639SDominik Brodowski /* 207fe2f639SDominik Brodowski * CPUID functions returning a single datum 217fe2f639SDominik Brodowski * 227fe2f639SDominik Brodowski * Define unsigned int cpuid_e[abcd]x(unsigned int op) 237fe2f639SDominik Brodowski */ 247fe2f639SDominik Brodowski #define cpuid_func(reg) \ 257fe2f639SDominik Brodowski unsigned int cpuid_##reg(unsigned int op) \ 267fe2f639SDominik Brodowski { \ 277fe2f639SDominik Brodowski unsigned int eax, ebx, ecx, edx; \ 287fe2f639SDominik Brodowski __cpuid(op, eax, ebx, ecx, edx); \ 297fe2f639SDominik Brodowski return reg; \ 307fe2f639SDominik Brodowski } 317fe2f639SDominik Brodowski cpuid_func(eax); 327fe2f639SDominik Brodowski cpuid_func(ebx); 337fe2f639SDominik Brodowski cpuid_func(ecx); 347fe2f639SDominik Brodowski cpuid_func(edx); 357fe2f639SDominik Brodowski 367fe2f639SDominik Brodowski #endif /* defined(__i386__) || defined(__x86_64__) */ 377fe2f639SDominik Brodowski 387fe2f639SDominik Brodowski /* get_cpu_info 397fe2f639SDominik Brodowski * 407fe2f639SDominik Brodowski * Extract CPU vendor, family, model, stepping info from /proc/cpuinfo 417fe2f639SDominik Brodowski * 427fe2f639SDominik Brodowski * Returns 0 on success or a negativ error code 437fe2f639SDominik Brodowski * 447fe2f639SDominik Brodowski * TBD: Should there be a cpuid alternative for this if /proc is not mounted? 457fe2f639SDominik Brodowski */ 46d0e4a193SPrarit Bhargava int get_cpu_info(struct cpupower_cpu_info *cpu_info) 477fe2f639SDominik Brodowski { 487fe2f639SDominik Brodowski FILE *fp; 497fe2f639SDominik Brodowski char value[64]; 507fe2f639SDominik Brodowski unsigned int proc, x; 517fe2f639SDominik Brodowski unsigned int unknown = 0xffffff; 527fe2f639SDominik Brodowski unsigned int cpuid_level, ext_cpuid_level; 537fe2f639SDominik Brodowski 547fe2f639SDominik Brodowski int ret = -EINVAL; 557fe2f639SDominik Brodowski 567fe2f639SDominik Brodowski cpu_info->vendor = X86_VENDOR_UNKNOWN; 577fe2f639SDominik Brodowski cpu_info->family = unknown; 587fe2f639SDominik Brodowski cpu_info->model = unknown; 597fe2f639SDominik Brodowski cpu_info->stepping = unknown; 607fe2f639SDominik Brodowski cpu_info->caps = 0; 617fe2f639SDominik Brodowski 627fe2f639SDominik Brodowski fp = fopen("/proc/cpuinfo", "r"); 637fe2f639SDominik Brodowski if (!fp) 647fe2f639SDominik Brodowski return -EIO; 657fe2f639SDominik Brodowski 667fe2f639SDominik Brodowski while (!feof(fp)) { 677fe2f639SDominik Brodowski if (!fgets(value, 64, fp)) 687fe2f639SDominik Brodowski continue; 697fe2f639SDominik Brodowski value[63 - 1] = '\0'; 707fe2f639SDominik Brodowski 712cd005caSDominik Brodowski if (!strncmp(value, "processor\t: ", 12)) 727fe2f639SDominik Brodowski sscanf(value, "processor\t: %u", &proc); 732cd005caSDominik Brodowski 74d0e4a193SPrarit Bhargava if (proc != (unsigned int)base_cpu) 757fe2f639SDominik Brodowski continue; 767fe2f639SDominik Brodowski 777fe2f639SDominik Brodowski /* Get CPU vendor */ 782cd005caSDominik Brodowski if (!strncmp(value, "vendor_id", 9)) { 797fe2f639SDominik Brodowski for (x = 1; x < X86_VENDOR_MAX; x++) { 807fe2f639SDominik Brodowski if (strstr(value, cpu_vendor_table[x])) 817fe2f639SDominik Brodowski cpu_info->vendor = x; 827fe2f639SDominik Brodowski } 837fe2f639SDominik Brodowski /* Get CPU family, etc. */ 842cd005caSDominik Brodowski } else if (!strncmp(value, "cpu family\t: ", 13)) { 857fe2f639SDominik Brodowski sscanf(value, "cpu family\t: %u", 867fe2f639SDominik Brodowski &cpu_info->family); 872cd005caSDominik Brodowski } else if (!strncmp(value, "model\t\t: ", 9)) { 887fe2f639SDominik Brodowski sscanf(value, "model\t\t: %u", 897fe2f639SDominik Brodowski &cpu_info->model); 902cd005caSDominik Brodowski } else if (!strncmp(value, "stepping\t: ", 10)) { 917fe2f639SDominik Brodowski sscanf(value, "stepping\t: %u", 927fe2f639SDominik Brodowski &cpu_info->stepping); 937fe2f639SDominik Brodowski 947fe2f639SDominik Brodowski /* Exit -> all values must have been set */ 957fe2f639SDominik Brodowski if (cpu_info->vendor == X86_VENDOR_UNKNOWN || 967fe2f639SDominik Brodowski cpu_info->family == unknown || 977fe2f639SDominik Brodowski cpu_info->model == unknown || 987fe2f639SDominik Brodowski cpu_info->stepping == unknown) { 997fe2f639SDominik Brodowski ret = -EINVAL; 1007fe2f639SDominik Brodowski goto out; 1017fe2f639SDominik Brodowski } 1027fe2f639SDominik Brodowski 1037fe2f639SDominik Brodowski ret = 0; 1047fe2f639SDominik Brodowski goto out; 1057fe2f639SDominik Brodowski } 1067fe2f639SDominik Brodowski } 1077fe2f639SDominik Brodowski ret = -ENODEV; 1087fe2f639SDominik Brodowski out: 1097fe2f639SDominik Brodowski fclose(fp); 1107fe2f639SDominik Brodowski /* Get some useful CPU capabilities from cpuid */ 1117fe2f639SDominik Brodowski if (cpu_info->vendor != X86_VENDOR_AMD && 112*995d5f64SPu Wen cpu_info->vendor != X86_VENDOR_HYGON && 1137fe2f639SDominik Brodowski cpu_info->vendor != X86_VENDOR_INTEL) 1147fe2f639SDominik Brodowski return ret; 1157fe2f639SDominik Brodowski 1167fe2f639SDominik Brodowski cpuid_level = cpuid_eax(0); 1177fe2f639SDominik Brodowski ext_cpuid_level = cpuid_eax(0x80000000); 1187fe2f639SDominik Brodowski 1197fe2f639SDominik Brodowski /* Invariant TSC */ 1207fe2f639SDominik Brodowski if (ext_cpuid_level >= 0x80000007 && 1217fe2f639SDominik Brodowski (cpuid_edx(0x80000007) & (1 << 8))) 1227fe2f639SDominik Brodowski cpu_info->caps |= CPUPOWER_CAP_INV_TSC; 1237fe2f639SDominik Brodowski 1247fe2f639SDominik Brodowski /* Aperf/Mperf registers support */ 1257fe2f639SDominik Brodowski if (cpuid_level >= 6 && (cpuid_ecx(6) & 0x1)) 1267fe2f639SDominik Brodowski cpu_info->caps |= CPUPOWER_CAP_APERF; 1277fe2f639SDominik Brodowski 128*995d5f64SPu Wen /* AMD or Hygon Boost state enable/disable register */ 129*995d5f64SPu Wen if (cpu_info->vendor == X86_VENDOR_AMD || 130*995d5f64SPu Wen cpu_info->vendor == X86_VENDOR_HYGON) { 1317fe2f639SDominik Brodowski if (ext_cpuid_level >= 0x80000007 && 1327fe2f639SDominik Brodowski (cpuid_edx(0x80000007) & (1 << 9))) 1337fe2f639SDominik Brodowski cpu_info->caps |= CPUPOWER_CAP_AMD_CBP; 1347fe2f639SDominik Brodowski } 1357fe2f639SDominik Brodowski 1367fe2f639SDominik Brodowski if (cpu_info->vendor == X86_VENDOR_INTEL) { 137029e9f73SThomas Renninger if (cpuid_level >= 6 && 138029e9f73SThomas Renninger (cpuid_eax(6) & (1 << 1))) 139029e9f73SThomas Renninger cpu_info->caps |= CPUPOWER_CAP_INTEL_IDA; 140029e9f73SThomas Renninger } 141029e9f73SThomas Renninger 142029e9f73SThomas Renninger if (cpu_info->vendor == X86_VENDOR_INTEL) { 1438fb2e440SThomas Renninger /* Intel's perf-bias MSR support */ 1447fe2f639SDominik Brodowski if (cpuid_level >= 6 && (cpuid_ecx(6) & (1 << 3))) 1457fe2f639SDominik Brodowski cpu_info->caps |= CPUPOWER_CAP_PERF_BIAS; 1468fb2e440SThomas Renninger 1478fb2e440SThomas Renninger /* Intel's Turbo Ratio Limit support */ 1488fb2e440SThomas Renninger if (cpu_info->family == 6) { 1498fb2e440SThomas Renninger switch (cpu_info->model) { 1508fb2e440SThomas Renninger case 0x1A: /* Core i7, Xeon 5500 series 1518fb2e440SThomas Renninger * Bloomfield, Gainstown NHM-EP 1528fb2e440SThomas Renninger */ 1538fb2e440SThomas Renninger case 0x1E: /* Core i7 and i5 Processor 1548fb2e440SThomas Renninger * Clarksfield, Lynnfield, Jasper Forest 1558fb2e440SThomas Renninger */ 1568fb2e440SThomas Renninger case 0x1F: /* Core i7 and i5 Processor - Nehalem */ 1578fb2e440SThomas Renninger case 0x25: /* Westmere Client 1588fb2e440SThomas Renninger * Clarkdale, Arrandale 1598fb2e440SThomas Renninger */ 1608fb2e440SThomas Renninger case 0x2C: /* Westmere EP - Gulftown */ 1618fb2e440SThomas Renninger cpu_info->caps |= CPUPOWER_CAP_HAS_TURBO_RATIO; 1624cca0457SBen Hutchings break; 1638fb2e440SThomas Renninger case 0x2A: /* SNB */ 1648fb2e440SThomas Renninger case 0x2D: /* SNB Xeon */ 1658d219e36SThomas Renninger case 0x3A: /* IVB */ 1668d219e36SThomas Renninger case 0x3E: /* IVB Xeon */ 1678fb2e440SThomas Renninger cpu_info->caps |= CPUPOWER_CAP_HAS_TURBO_RATIO; 1688fb2e440SThomas Renninger cpu_info->caps |= CPUPOWER_CAP_IS_SNB; 1698fb2e440SThomas Renninger break; 1708fb2e440SThomas Renninger case 0x2E: /* Nehalem-EX Xeon - Beckton */ 1718fb2e440SThomas Renninger case 0x2F: /* Westmere-EX Xeon - Eagleton */ 1728fb2e440SThomas Renninger default: 1738fb2e440SThomas Renninger break; 1748fb2e440SThomas Renninger } 1758fb2e440SThomas Renninger } 1767fe2f639SDominik Brodowski } 1777fe2f639SDominik Brodowski 1787fe2f639SDominik Brodowski /* printf("ID: %u - Extid: 0x%x - Caps: 0x%llx\n", 1797fe2f639SDominik Brodowski cpuid_level, ext_cpuid_level, cpu_info->caps); 1807fe2f639SDominik Brodowski */ 1817fe2f639SDominik Brodowski return ret; 1827fe2f639SDominik Brodowski } 183