GNU Linux-libre 4.19.286-gnu1
[releases.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",
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_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 }