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] = { 11995d5f64SPu 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 && 112995d5f64SPu 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 128995d5f64SPu Wen /* AMD or Hygon Boost state enable/disable register */ 129995d5f64SPu Wen if (cpu_info->vendor == X86_VENDOR_AMD || 130995d5f64SPu Wen cpu_info->vendor == X86_VENDOR_HYGON) { 131a0255a76SNathan Fontenot if (ext_cpuid_level >= 0x80000007) { 132*3a3ecfdbSNathan Fontenot if (cpuid_edx(0x80000007) & (1 << 9)) { 1337a136a8fSRobert Richter cpu_info->caps |= CPUPOWER_CAP_AMD_CPB; 1346af2ed53SJanakarajan Natarajan 135*3a3ecfdbSNathan Fontenot if (cpu_info->family >= 0x17) 136*3a3ecfdbSNathan Fontenot cpu_info->caps |= CPUPOWER_CAP_AMD_CPB_MSR; 137*3a3ecfdbSNathan Fontenot } 138*3a3ecfdbSNathan Fontenot 139a0255a76SNathan Fontenot if ((cpuid_edx(0x80000007) & (1 << 7)) && 14023765b82SNathan Fontenot cpu_info->family != 0x14) { 141a0255a76SNathan Fontenot /* HW pstate was not implemented in family 0x14 */ 142a0255a76SNathan Fontenot cpu_info->caps |= CPUPOWER_CAP_AMD_HW_PSTATE; 14323765b82SNathan Fontenot 14423765b82SNathan Fontenot if (cpu_info->family >= 0x17) 14523765b82SNathan Fontenot cpu_info->caps |= CPUPOWER_CAP_AMD_PSTATEDEF; 14623765b82SNathan Fontenot } 147a0255a76SNathan Fontenot } 148a0255a76SNathan Fontenot 1496af2ed53SJanakarajan Natarajan if (ext_cpuid_level >= 0x80000008 && 1506af2ed53SJanakarajan Natarajan cpuid_ebx(0x80000008) & (1 << 4)) 1516af2ed53SJanakarajan Natarajan cpu_info->caps |= CPUPOWER_CAP_AMD_RDPRU; 1527fe2f639SDominik Brodowski } 1537fe2f639SDominik Brodowski 1547fe2f639SDominik Brodowski if (cpu_info->vendor == X86_VENDOR_INTEL) { 155029e9f73SThomas Renninger if (cpuid_level >= 6 && 156029e9f73SThomas Renninger (cpuid_eax(6) & (1 << 1))) 157029e9f73SThomas Renninger cpu_info->caps |= CPUPOWER_CAP_INTEL_IDA; 158029e9f73SThomas Renninger } 159029e9f73SThomas Renninger 160029e9f73SThomas Renninger if (cpu_info->vendor == X86_VENDOR_INTEL) { 1618fb2e440SThomas Renninger /* Intel's perf-bias MSR support */ 1627fe2f639SDominik Brodowski if (cpuid_level >= 6 && (cpuid_ecx(6) & (1 << 3))) 1637fe2f639SDominik Brodowski cpu_info->caps |= CPUPOWER_CAP_PERF_BIAS; 1648fb2e440SThomas Renninger 1658fb2e440SThomas Renninger /* Intel's Turbo Ratio Limit support */ 1668fb2e440SThomas Renninger if (cpu_info->family == 6) { 1678fb2e440SThomas Renninger switch (cpu_info->model) { 1688fb2e440SThomas Renninger case 0x1A: /* Core i7, Xeon 5500 series 1698fb2e440SThomas Renninger * Bloomfield, Gainstown NHM-EP 1708fb2e440SThomas Renninger */ 1718fb2e440SThomas Renninger case 0x1E: /* Core i7 and i5 Processor 1728fb2e440SThomas Renninger * Clarksfield, Lynnfield, Jasper Forest 1738fb2e440SThomas Renninger */ 1748fb2e440SThomas Renninger case 0x1F: /* Core i7 and i5 Processor - Nehalem */ 1758fb2e440SThomas Renninger case 0x25: /* Westmere Client 1768fb2e440SThomas Renninger * Clarkdale, Arrandale 1778fb2e440SThomas Renninger */ 1788fb2e440SThomas Renninger case 0x2C: /* Westmere EP - Gulftown */ 1798fb2e440SThomas Renninger cpu_info->caps |= CPUPOWER_CAP_HAS_TURBO_RATIO; 1804cca0457SBen Hutchings break; 1818fb2e440SThomas Renninger case 0x2A: /* SNB */ 1828fb2e440SThomas Renninger case 0x2D: /* SNB Xeon */ 1838d219e36SThomas Renninger case 0x3A: /* IVB */ 1848d219e36SThomas Renninger case 0x3E: /* IVB Xeon */ 1858fb2e440SThomas Renninger cpu_info->caps |= CPUPOWER_CAP_HAS_TURBO_RATIO; 1868fb2e440SThomas Renninger cpu_info->caps |= CPUPOWER_CAP_IS_SNB; 1878fb2e440SThomas Renninger break; 1888fb2e440SThomas Renninger case 0x2E: /* Nehalem-EX Xeon - Beckton */ 1898fb2e440SThomas Renninger case 0x2F: /* Westmere-EX Xeon - Eagleton */ 1908fb2e440SThomas Renninger default: 1918fb2e440SThomas Renninger break; 1928fb2e440SThomas Renninger } 1938fb2e440SThomas Renninger } 1947fe2f639SDominik Brodowski } 1957fe2f639SDominik Brodowski 1967fe2f639SDominik Brodowski /* printf("ID: %u - Extid: 0x%x - Caps: 0x%llx\n", 1977fe2f639SDominik Brodowski cpuid_level, ext_cpuid_level, cpu_info->caps); 1987fe2f639SDominik Brodowski */ 1997fe2f639SDominik Brodowski return ret; 2007fe2f639SDominik Brodowski } 201