1 // SPDX-License-Identifier: GPL-2.0 2 #include <stdio.h> 3 #include <errno.h> 4 #include <string.h> 5 #include <unistd.h> 6 #include <stdlib.h> 7 8 #include "helpers/helpers.h" 9 10 static const char *cpu_vendor_table[X86_VENDOR_MAX] = { 11 "Unknown", "GenuineIntel", "AuthenticAMD", 12 }; 13 14 #if defined(__i386__) || defined(__x86_64__) 15 16 /* from gcc */ 17 #include <cpuid.h> 18 19 /* 20 * CPUID functions returning a single datum 21 * 22 * Define unsigned int cpuid_e[abcd]x(unsigned int op) 23 */ 24 #define cpuid_func(reg) \ 25 unsigned int cpuid_##reg(unsigned int op) \ 26 { \ 27 unsigned int eax, ebx, ecx, edx; \ 28 __cpuid(op, eax, ebx, ecx, edx); \ 29 return reg; \ 30 } 31 cpuid_func(eax); 32 cpuid_func(ebx); 33 cpuid_func(ecx); 34 cpuid_func(edx); 35 36 #endif /* defined(__i386__) || defined(__x86_64__) */ 37 38 /* get_cpu_info 39 * 40 * Extract CPU vendor, family, model, stepping info from /proc/cpuinfo 41 * 42 * Returns 0 on success or a negativ error code 43 * 44 * TBD: Should there be a cpuid alternative for this if /proc is not mounted? 45 */ 46 int get_cpu_info(struct cpupower_cpu_info *cpu_info) 47 { 48 FILE *fp; 49 char value[64]; 50 unsigned int proc, x; 51 unsigned int unknown = 0xffffff; 52 unsigned int cpuid_level, ext_cpuid_level; 53 54 int ret = -EINVAL; 55 56 cpu_info->vendor = X86_VENDOR_UNKNOWN; 57 cpu_info->family = unknown; 58 cpu_info->model = unknown; 59 cpu_info->stepping = unknown; 60 cpu_info->caps = 0; 61 62 fp = fopen("/proc/cpuinfo", "r"); 63 if (!fp) 64 return -EIO; 65 66 while (!feof(fp)) { 67 if (!fgets(value, 64, fp)) 68 continue; 69 value[63 - 1] = '\0'; 70 71 if (!strncmp(value, "processor\t: ", 12)) 72 sscanf(value, "processor\t: %u", &proc); 73 74 if (proc != (unsigned int)base_cpu) 75 continue; 76 77 /* Get CPU vendor */ 78 if (!strncmp(value, "vendor_id", 9)) { 79 for (x = 1; x < X86_VENDOR_MAX; x++) { 80 if (strstr(value, cpu_vendor_table[x])) 81 cpu_info->vendor = x; 82 } 83 /* Get CPU family, etc. */ 84 } else if (!strncmp(value, "cpu family\t: ", 13)) { 85 sscanf(value, "cpu family\t: %u", 86 &cpu_info->family); 87 } else if (!strncmp(value, "model\t\t: ", 9)) { 88 sscanf(value, "model\t\t: %u", 89 &cpu_info->model); 90 } else if (!strncmp(value, "stepping\t: ", 10)) { 91 sscanf(value, "stepping\t: %u", 92 &cpu_info->stepping); 93 94 /* Exit -> all values must have been set */ 95 if (cpu_info->vendor == X86_VENDOR_UNKNOWN || 96 cpu_info->family == unknown || 97 cpu_info->model == unknown || 98 cpu_info->stepping == unknown) { 99 ret = -EINVAL; 100 goto out; 101 } 102 103 ret = 0; 104 goto out; 105 } 106 } 107 ret = -ENODEV; 108 out: 109 fclose(fp); 110 /* Get some useful CPU capabilities from cpuid */ 111 if (cpu_info->vendor != X86_VENDOR_AMD && 112 cpu_info->vendor != X86_VENDOR_INTEL) 113 return ret; 114 115 cpuid_level = cpuid_eax(0); 116 ext_cpuid_level = cpuid_eax(0x80000000); 117 118 /* Invariant TSC */ 119 if (ext_cpuid_level >= 0x80000007 && 120 (cpuid_edx(0x80000007) & (1 << 8))) 121 cpu_info->caps |= CPUPOWER_CAP_INV_TSC; 122 123 /* Aperf/Mperf registers support */ 124 if (cpuid_level >= 6 && (cpuid_ecx(6) & 0x1)) 125 cpu_info->caps |= CPUPOWER_CAP_APERF; 126 127 /* AMD Boost state enable/disable register */ 128 if (cpu_info->vendor == X86_VENDOR_AMD) { 129 if (ext_cpuid_level >= 0x80000007 && 130 (cpuid_edx(0x80000007) & (1 << 9))) 131 cpu_info->caps |= CPUPOWER_CAP_AMD_CBP; 132 } 133 134 if (cpu_info->vendor == X86_VENDOR_INTEL) { 135 if (cpuid_level >= 6 && 136 (cpuid_eax(6) & (1 << 1))) 137 cpu_info->caps |= CPUPOWER_CAP_INTEL_IDA; 138 } 139 140 if (cpu_info->vendor == X86_VENDOR_INTEL) { 141 /* Intel's perf-bias MSR support */ 142 if (cpuid_level >= 6 && (cpuid_ecx(6) & (1 << 3))) 143 cpu_info->caps |= CPUPOWER_CAP_PERF_BIAS; 144 145 /* Intel's Turbo Ratio Limit support */ 146 if (cpu_info->family == 6) { 147 switch (cpu_info->model) { 148 case 0x1A: /* Core i7, Xeon 5500 series 149 * Bloomfield, Gainstown NHM-EP 150 */ 151 case 0x1E: /* Core i7 and i5 Processor 152 * Clarksfield, Lynnfield, Jasper Forest 153 */ 154 case 0x1F: /* Core i7 and i5 Processor - Nehalem */ 155 case 0x25: /* Westmere Client 156 * Clarkdale, Arrandale 157 */ 158 case 0x2C: /* Westmere EP - Gulftown */ 159 cpu_info->caps |= CPUPOWER_CAP_HAS_TURBO_RATIO; 160 break; 161 case 0x2A: /* SNB */ 162 case 0x2D: /* SNB Xeon */ 163 case 0x3A: /* IVB */ 164 case 0x3E: /* IVB Xeon */ 165 cpu_info->caps |= CPUPOWER_CAP_HAS_TURBO_RATIO; 166 cpu_info->caps |= CPUPOWER_CAP_IS_SNB; 167 break; 168 case 0x2E: /* Nehalem-EX Xeon - Beckton */ 169 case 0x2F: /* Westmere-EX Xeon - Eagleton */ 170 default: 171 break; 172 } 173 } 174 } 175 176 /* printf("ID: %u - Extid: 0x%x - Caps: 0x%llx\n", 177 cpuid_level, ext_cpuid_level, cpu_info->caps); 178 */ 179 return ret; 180 } 181