214 lines · c
1// SPDX-License-Identifier: GPL-2.02#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 10static 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 datum21 *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 }31cpuid_func(eax);32cpuid_func(ebx);33cpuid_func(ecx);34cpuid_func(edx);35 36#endif /* defined(__i386__) || defined(__x86_64__) */37 38/* get_cpu_info39 *40 * Extract CPU vendor, family, model, stepping info from /proc/cpuinfo41 *42 * Returns 0 on success or a negativ error code43 *44 * TBD: Should there be a cpuid alternative for this if /proc is not mounted?45 */46int 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;108out: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 if (cpupower_amd_pstate_enabled()) {154 cpu_info->caps |= CPUPOWER_CAP_AMD_PSTATE;155 156 /*157 * If AMD P-State is enabled, the firmware will treat158 * AMD P-State function as high priority.159 */160 cpu_info->caps &= ~CPUPOWER_CAP_AMD_CPB;161 cpu_info->caps &= ~CPUPOWER_CAP_AMD_CPB_MSR;162 cpu_info->caps &= ~CPUPOWER_CAP_AMD_HW_PSTATE;163 cpu_info->caps &= ~CPUPOWER_CAP_AMD_PSTATEDEF;164 }165 }166 167 if (cpu_info->vendor == X86_VENDOR_INTEL) {168 if (cpuid_level >= 6 &&169 (cpuid_eax(6) & (1 << 1)))170 cpu_info->caps |= CPUPOWER_CAP_INTEL_IDA;171 }172 173 if (cpu_info->vendor == X86_VENDOR_INTEL) {174 /* Intel's perf-bias MSR support */175 if (cpuid_level >= 6 && (cpuid_ecx(6) & (1 << 3)))176 cpu_info->caps |= CPUPOWER_CAP_PERF_BIAS;177 178 /* Intel's Turbo Ratio Limit support */179 if (cpu_info->family == 6) {180 switch (cpu_info->model) {181 case 0x1A: /* Core i7, Xeon 5500 series182 * Bloomfield, Gainstown NHM-EP183 */184 case 0x1E: /* Core i7 and i5 Processor185 * Clarksfield, Lynnfield, Jasper Forest186 */187 case 0x1F: /* Core i7 and i5 Processor - Nehalem */188 case 0x25: /* Westmere Client189 * Clarkdale, Arrandale190 */191 case 0x2C: /* Westmere EP - Gulftown */192 cpu_info->caps |= CPUPOWER_CAP_HAS_TURBO_RATIO;193 break;194 case 0x2A: /* SNB */195 case 0x2D: /* SNB Xeon */196 case 0x3A: /* IVB */197 case 0x3E: /* IVB Xeon */198 cpu_info->caps |= CPUPOWER_CAP_HAS_TURBO_RATIO;199 cpu_info->caps |= CPUPOWER_CAP_IS_SNB;200 break;201 case 0x2E: /* Nehalem-EX Xeon - Beckton */202 case 0x2F: /* Westmere-EX Xeon - Eagleton */203 default:204 break;205 }206 }207 }208 209 /* printf("ID: %u - Extid: 0x%x - Caps: 0x%llx\n",210 cpuid_level, ext_cpuid_level, cpu_info->caps);211 */212 return ret;213}214