17fe2f639SDominik Brodowski #include <stdio.h> 27fe2f639SDominik Brodowski #include <errno.h> 37fe2f639SDominik Brodowski #include <string.h> 47fe2f639SDominik Brodowski #include <unistd.h> 57fe2f639SDominik Brodowski #include <stdlib.h> 67fe2f639SDominik Brodowski 77fe2f639SDominik Brodowski #include "helpers/helpers.h" 87fe2f639SDominik Brodowski 97fe2f639SDominik Brodowski static const char *cpu_vendor_table[X86_VENDOR_MAX] = { 107fe2f639SDominik Brodowski "Unknown", "GenuineIntel", "AuthenticAMD", 117fe2f639SDominik Brodowski }; 127fe2f639SDominik Brodowski 137fe2f639SDominik Brodowski #if defined(__i386__) || defined(__x86_64__) 147fe2f639SDominik Brodowski 157fe2f639SDominik Brodowski /* from gcc */ 167fe2f639SDominik Brodowski #include <cpuid.h> 177fe2f639SDominik Brodowski 187fe2f639SDominik Brodowski /* 197fe2f639SDominik Brodowski * CPUID functions returning a single datum 207fe2f639SDominik Brodowski * 217fe2f639SDominik Brodowski * Define unsigned int cpuid_e[abcd]x(unsigned int op) 227fe2f639SDominik Brodowski */ 237fe2f639SDominik Brodowski #define cpuid_func(reg) \ 247fe2f639SDominik Brodowski unsigned int cpuid_##reg(unsigned int op) \ 257fe2f639SDominik Brodowski { \ 267fe2f639SDominik Brodowski unsigned int eax, ebx, ecx, edx; \ 277fe2f639SDominik Brodowski __cpuid(op, eax, ebx, ecx, edx); \ 287fe2f639SDominik Brodowski return reg; \ 297fe2f639SDominik Brodowski } 307fe2f639SDominik Brodowski cpuid_func(eax); 317fe2f639SDominik Brodowski cpuid_func(ebx); 327fe2f639SDominik Brodowski cpuid_func(ecx); 337fe2f639SDominik Brodowski cpuid_func(edx); 347fe2f639SDominik Brodowski 357fe2f639SDominik Brodowski #endif /* defined(__i386__) || defined(__x86_64__) */ 367fe2f639SDominik Brodowski 377fe2f639SDominik Brodowski /* get_cpu_info 387fe2f639SDominik Brodowski * 397fe2f639SDominik Brodowski * Extract CPU vendor, family, model, stepping info from /proc/cpuinfo 407fe2f639SDominik Brodowski * 417fe2f639SDominik Brodowski * Returns 0 on success or a negativ error code 427fe2f639SDominik Brodowski * 437fe2f639SDominik Brodowski * TBD: Should there be a cpuid alternative for this if /proc is not mounted? 447fe2f639SDominik Brodowski */ 457fe2f639SDominik Brodowski int get_cpu_info(unsigned int cpu, struct cpupower_cpu_info *cpu_info) 467fe2f639SDominik Brodowski { 477fe2f639SDominik Brodowski FILE *fp; 487fe2f639SDominik Brodowski char value[64]; 497fe2f639SDominik Brodowski unsigned int proc, x; 507fe2f639SDominik Brodowski unsigned int unknown = 0xffffff; 517fe2f639SDominik Brodowski unsigned int cpuid_level, ext_cpuid_level; 527fe2f639SDominik Brodowski 537fe2f639SDominik Brodowski int ret = -EINVAL; 547fe2f639SDominik Brodowski 557fe2f639SDominik Brodowski cpu_info->vendor = X86_VENDOR_UNKNOWN; 567fe2f639SDominik Brodowski cpu_info->family = unknown; 577fe2f639SDominik Brodowski cpu_info->model = unknown; 587fe2f639SDominik Brodowski cpu_info->stepping = unknown; 597fe2f639SDominik Brodowski cpu_info->caps = 0; 607fe2f639SDominik Brodowski 617fe2f639SDominik Brodowski fp = fopen("/proc/cpuinfo", "r"); 627fe2f639SDominik Brodowski if (!fp) 637fe2f639SDominik Brodowski return -EIO; 647fe2f639SDominik Brodowski 657fe2f639SDominik Brodowski while (!feof(fp)) { 667fe2f639SDominik Brodowski if (!fgets(value, 64, fp)) 677fe2f639SDominik Brodowski continue; 687fe2f639SDominik Brodowski value[63 - 1] = '\0'; 697fe2f639SDominik Brodowski 702cd005caSDominik Brodowski if (!strncmp(value, "processor\t: ", 12)) 717fe2f639SDominik Brodowski sscanf(value, "processor\t: %u", &proc); 722cd005caSDominik Brodowski 737fe2f639SDominik Brodowski if (proc != cpu) 747fe2f639SDominik Brodowski continue; 757fe2f639SDominik Brodowski 767fe2f639SDominik Brodowski /* Get CPU vendor */ 772cd005caSDominik Brodowski if (!strncmp(value, "vendor_id", 9)) { 787fe2f639SDominik Brodowski for (x = 1; x < X86_VENDOR_MAX; x++) { 797fe2f639SDominik Brodowski if (strstr(value, cpu_vendor_table[x])) 807fe2f639SDominik Brodowski cpu_info->vendor = x; 817fe2f639SDominik Brodowski } 827fe2f639SDominik Brodowski /* Get CPU family, etc. */ 832cd005caSDominik Brodowski } else if (!strncmp(value, "cpu family\t: ", 13)) { 847fe2f639SDominik Brodowski sscanf(value, "cpu family\t: %u", 857fe2f639SDominik Brodowski &cpu_info->family); 862cd005caSDominik Brodowski } else if (!strncmp(value, "model\t\t: ", 9)) { 877fe2f639SDominik Brodowski sscanf(value, "model\t\t: %u", 887fe2f639SDominik Brodowski &cpu_info->model); 892cd005caSDominik Brodowski } else if (!strncmp(value, "stepping\t: ", 10)) { 907fe2f639SDominik Brodowski sscanf(value, "stepping\t: %u", 917fe2f639SDominik Brodowski &cpu_info->stepping); 927fe2f639SDominik Brodowski 937fe2f639SDominik Brodowski /* Exit -> all values must have been set */ 947fe2f639SDominik Brodowski if (cpu_info->vendor == X86_VENDOR_UNKNOWN || 957fe2f639SDominik Brodowski cpu_info->family == unknown || 967fe2f639SDominik Brodowski cpu_info->model == unknown || 977fe2f639SDominik Brodowski cpu_info->stepping == unknown) { 987fe2f639SDominik Brodowski ret = -EINVAL; 997fe2f639SDominik Brodowski goto out; 1007fe2f639SDominik Brodowski } 1017fe2f639SDominik Brodowski 1027fe2f639SDominik Brodowski ret = 0; 1037fe2f639SDominik Brodowski goto out; 1047fe2f639SDominik Brodowski } 1057fe2f639SDominik Brodowski } 1067fe2f639SDominik Brodowski ret = -ENODEV; 1077fe2f639SDominik Brodowski out: 1087fe2f639SDominik Brodowski fclose(fp); 1097fe2f639SDominik Brodowski /* Get some useful CPU capabilities from cpuid */ 1107fe2f639SDominik Brodowski if (cpu_info->vendor != X86_VENDOR_AMD && 1117fe2f639SDominik Brodowski cpu_info->vendor != X86_VENDOR_INTEL) 1127fe2f639SDominik Brodowski return ret; 1137fe2f639SDominik Brodowski 1147fe2f639SDominik Brodowski cpuid_level = cpuid_eax(0); 1157fe2f639SDominik Brodowski ext_cpuid_level = cpuid_eax(0x80000000); 1167fe2f639SDominik Brodowski 1177fe2f639SDominik Brodowski /* Invariant TSC */ 1187fe2f639SDominik Brodowski if (ext_cpuid_level >= 0x80000007 && 1197fe2f639SDominik Brodowski (cpuid_edx(0x80000007) & (1 << 8))) 1207fe2f639SDominik Brodowski cpu_info->caps |= CPUPOWER_CAP_INV_TSC; 1217fe2f639SDominik Brodowski 1227fe2f639SDominik Brodowski /* Aperf/Mperf registers support */ 1237fe2f639SDominik Brodowski if (cpuid_level >= 6 && (cpuid_ecx(6) & 0x1)) 1247fe2f639SDominik Brodowski cpu_info->caps |= CPUPOWER_CAP_APERF; 1257fe2f639SDominik Brodowski 1267fe2f639SDominik Brodowski /* AMD Boost state enable/disable register */ 1277fe2f639SDominik Brodowski if (cpu_info->vendor == X86_VENDOR_AMD) { 1287fe2f639SDominik Brodowski if (ext_cpuid_level >= 0x80000007 && 1297fe2f639SDominik Brodowski (cpuid_edx(0x80000007) & (1 << 9))) 1307fe2f639SDominik Brodowski cpu_info->caps |= CPUPOWER_CAP_AMD_CBP; 1317fe2f639SDominik Brodowski } 1327fe2f639SDominik Brodowski 1337fe2f639SDominik Brodowski if (cpu_info->vendor == X86_VENDOR_INTEL) { 134*8fb2e440SThomas Renninger /* Intel's perf-bias MSR support */ 1357fe2f639SDominik Brodowski if (cpuid_level >= 6 && (cpuid_ecx(6) & (1 << 3))) 1367fe2f639SDominik Brodowski cpu_info->caps |= CPUPOWER_CAP_PERF_BIAS; 137*8fb2e440SThomas Renninger 138*8fb2e440SThomas Renninger /* Intel's Turbo Ratio Limit support */ 139*8fb2e440SThomas Renninger if (cpu_info->family == 6) { 140*8fb2e440SThomas Renninger switch (cpu_info->model) { 141*8fb2e440SThomas Renninger case 0x1A: /* Core i7, Xeon 5500 series 142*8fb2e440SThomas Renninger * Bloomfield, Gainstown NHM-EP 143*8fb2e440SThomas Renninger */ 144*8fb2e440SThomas Renninger case 0x1E: /* Core i7 and i5 Processor 145*8fb2e440SThomas Renninger * Clarksfield, Lynnfield, Jasper Forest 146*8fb2e440SThomas Renninger */ 147*8fb2e440SThomas Renninger case 0x1F: /* Core i7 and i5 Processor - Nehalem */ 148*8fb2e440SThomas Renninger case 0x25: /* Westmere Client 149*8fb2e440SThomas Renninger * Clarkdale, Arrandale 150*8fb2e440SThomas Renninger */ 151*8fb2e440SThomas Renninger case 0x2C: /* Westmere EP - Gulftown */ 152*8fb2e440SThomas Renninger cpu_info->caps |= CPUPOWER_CAP_HAS_TURBO_RATIO; 153*8fb2e440SThomas Renninger case 0x2A: /* SNB */ 154*8fb2e440SThomas Renninger case 0x2D: /* SNB Xeon */ 155*8fb2e440SThomas Renninger cpu_info->caps |= CPUPOWER_CAP_HAS_TURBO_RATIO; 156*8fb2e440SThomas Renninger cpu_info->caps |= CPUPOWER_CAP_IS_SNB; 157*8fb2e440SThomas Renninger break; 158*8fb2e440SThomas Renninger case 0x2E: /* Nehalem-EX Xeon - Beckton */ 159*8fb2e440SThomas Renninger case 0x2F: /* Westmere-EX Xeon - Eagleton */ 160*8fb2e440SThomas Renninger default: 161*8fb2e440SThomas Renninger break; 162*8fb2e440SThomas Renninger } 163*8fb2e440SThomas Renninger } 1647fe2f639SDominik Brodowski } 1657fe2f639SDominik Brodowski 1667fe2f639SDominik Brodowski /* printf("ID: %u - Extid: 0x%x - Caps: 0x%llx\n", 1677fe2f639SDominik Brodowski cpuid_level, ext_cpuid_level, cpu_info->caps); 1687fe2f639SDominik Brodowski */ 1697fe2f639SDominik Brodowski return ret; 1707fe2f639SDominik Brodowski } 171