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 */
get_cpu_info(struct cpupower_cpu_info * cpu_info)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