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 (cpuid_edx(0x80000007) & (1 << 9))) 133 cpu_info->caps |= CPUPOWER_CAP_AMD_CBP; 134 135 if (ext_cpuid_level >= 0x80000008 && 136 cpuid_ebx(0x80000008) & (1 << 4)) 137 cpu_info->caps |= CPUPOWER_CAP_AMD_RDPRU; 138 } 139 140 if (cpu_info->vendor == X86_VENDOR_INTEL) { 141 if (cpuid_level >= 6 && 142 (cpuid_eax(6) & (1 << 1))) 143 cpu_info->caps |= CPUPOWER_CAP_INTEL_IDA; 144 } 145 146 if (cpu_info->vendor == X86_VENDOR_INTEL) { 147 /* Intel's perf-bias MSR support */ 148 if (cpuid_level >= 6 && (cpuid_ecx(6) & (1 << 3))) 149 cpu_info->caps |= CPUPOWER_CAP_PERF_BIAS; 150 151 /* Intel's Turbo Ratio Limit support */ 152 if (cpu_info->family == 6) { 153 switch (cpu_info->model) { 154 case 0x1A: /* Core i7, Xeon 5500 series 155 * Bloomfield, Gainstown NHM-EP 156 */ 157 case 0x1E: /* Core i7 and i5 Processor 158 * Clarksfield, Lynnfield, Jasper Forest 159 */ 160 case 0x1F: /* Core i7 and i5 Processor - Nehalem */ 161 case 0x25: /* Westmere Client 162 * Clarkdale, Arrandale 163 */ 164 case 0x2C: /* Westmere EP - Gulftown */ 165 cpu_info->caps |= CPUPOWER_CAP_HAS_TURBO_RATIO; 166 break; 167 case 0x2A: /* SNB */ 168 case 0x2D: /* SNB Xeon */ 169 case 0x3A: /* IVB */ 170 case 0x3E: /* IVB Xeon */ 171 cpu_info->caps |= CPUPOWER_CAP_HAS_TURBO_RATIO; 172 cpu_info->caps |= CPUPOWER_CAP_IS_SNB; 173 break; 174 case 0x2E: /* Nehalem-EX Xeon - Beckton */ 175 case 0x2F: /* Westmere-EX Xeon - Eagleton */ 176 default: 177 break; 178 } 179 } 180 } 181 182 /* printf("ID: %u - Extid: 0x%x - Caps: 0x%llx\n", 183 cpuid_level, ext_cpuid_level, cpu_info->caps); 184 */ 185 return ret; 186 } 187