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", "HygonGenuine", 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_HYGON && 113 cpu_info->vendor != X86_VENDOR_INTEL) 114 return ret; 115 116 cpuid_level = cpuid_eax(0); 117 ext_cpuid_level = cpuid_eax(0x80000000); 118 119 /* Invariant TSC */ 120 if (ext_cpuid_level >= 0x80000007 && 121 (cpuid_edx(0x80000007) & (1 << 8))) 122 cpu_info->caps |= CPUPOWER_CAP_INV_TSC; 123 124 /* Aperf/Mperf registers support */ 125 if (cpuid_level >= 6 && (cpuid_ecx(6) & 0x1)) 126 cpu_info->caps |= CPUPOWER_CAP_APERF; 127 128 /* AMD or Hygon Boost state enable/disable register */ 129 if (cpu_info->vendor == X86_VENDOR_AMD || 130 cpu_info->vendor == X86_VENDOR_HYGON) { 131 if (ext_cpuid_level >= 0x80000007) { 132 if (cpuid_edx(0x80000007) & (1 << 9)) { 133 cpu_info->caps |= CPUPOWER_CAP_AMD_CPB; 134 135 if (cpu_info->family >= 0x17) 136 cpu_info->caps |= CPUPOWER_CAP_AMD_CPB_MSR; 137 } 138 139 if ((cpuid_edx(0x80000007) & (1 << 7)) && 140 cpu_info->family != 0x14) { 141 /* HW pstate was not implemented in family 0x14 */ 142 cpu_info->caps |= CPUPOWER_CAP_AMD_HW_PSTATE; 143 144 if (cpu_info->family >= 0x17) 145 cpu_info->caps |= CPUPOWER_CAP_AMD_PSTATEDEF; 146 } 147 } 148 149 if (ext_cpuid_level >= 0x80000008 && 150 cpuid_ebx(0x80000008) & (1 << 4)) 151 cpu_info->caps |= CPUPOWER_CAP_AMD_RDPRU; 152 } 153 154 if (cpu_info->vendor == X86_VENDOR_INTEL) { 155 if (cpuid_level >= 6 && 156 (cpuid_eax(6) & (1 << 1))) 157 cpu_info->caps |= CPUPOWER_CAP_INTEL_IDA; 158 } 159 160 if (cpu_info->vendor == X86_VENDOR_INTEL) { 161 /* Intel's perf-bias MSR support */ 162 if (cpuid_level >= 6 && (cpuid_ecx(6) & (1 << 3))) 163 cpu_info->caps |= CPUPOWER_CAP_PERF_BIAS; 164 165 /* Intel's Turbo Ratio Limit support */ 166 if (cpu_info->family == 6) { 167 switch (cpu_info->model) { 168 case 0x1A: /* Core i7, Xeon 5500 series 169 * Bloomfield, Gainstown NHM-EP 170 */ 171 case 0x1E: /* Core i7 and i5 Processor 172 * Clarksfield, Lynnfield, Jasper Forest 173 */ 174 case 0x1F: /* Core i7 and i5 Processor - Nehalem */ 175 case 0x25: /* Westmere Client 176 * Clarkdale, Arrandale 177 */ 178 case 0x2C: /* Westmere EP - Gulftown */ 179 cpu_info->caps |= CPUPOWER_CAP_HAS_TURBO_RATIO; 180 break; 181 case 0x2A: /* SNB */ 182 case 0x2D: /* SNB Xeon */ 183 case 0x3A: /* IVB */ 184 case 0x3E: /* IVB Xeon */ 185 cpu_info->caps |= CPUPOWER_CAP_HAS_TURBO_RATIO; 186 cpu_info->caps |= CPUPOWER_CAP_IS_SNB; 187 break; 188 case 0x2E: /* Nehalem-EX Xeon - Beckton */ 189 case 0x2F: /* Westmere-EX Xeon - Eagleton */ 190 default: 191 break; 192 } 193 } 194 } 195 196 /* printf("ID: %u - Extid: 0x%x - Caps: 0x%llx\n", 197 cpuid_level, ext_cpuid_level, cpu_info->caps); 198 */ 199 return ret; 200 } 201