Linux 6.7-rc7
[linux-modified.git] / tools / power / cpupower / utils / helpers / cpuid.c
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                 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 treat
158                          * 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 series
182                                          * Bloomfield, Gainstown NHM-EP
183                                          */
184                         case 0x1E:      /* Core i7 and i5 Processor
185                                          * Clarksfield, Lynnfield, Jasper Forest
186                                          */
187                         case 0x1F:      /* Core i7 and i5 Processor - Nehalem */
188                         case 0x25:      /* Westmere Client
189                                          * Clarkdale, Arrandale
190                                          */
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 }