Skip to content

Commit 7a23453

Browse files
committed
cpuid-dump should iterate through all CPUs
As Intel puts it: "CPUID, by design, returns different values depending on the core it is executed on" (see https://www.intel.com/content/www/us/en/developer/articles/guide/12th-gen-intel-core-processor-gamedev-guide.html#inpage-nav-1-5-2:~:text=CPUID%2C%20by%20design%2C%20returns%20different%20values%20depending%20on%20the%20core%20it%20is%20executed%20on) In particular, leaves 1, 4, 0x0b, 0x1a, and 0x1f are known to vary by core. Leaf 0x1a differentiates core types on hybrid CPUs. In order to aid in exploration of CPUID contents, `cpuid-dump` should dump CPUID results from *all* CPUs, rather than just one. This is currently implemented for Linux only, using the `sched_setaffinity(2)` system call.
1 parent e2ed60b commit 7a23453

File tree

1 file changed

+184
-95
lines changed

1 file changed

+184
-95
lines changed

tools/cpuid-dump.c

Lines changed: 184 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
#ifdef __linux__
2+
#define _GNU_SOURCE
3+
#include <sched.h>
4+
#include <stdlib.h>
5+
#endif
6+
17
#include <inttypes.h>
28
#include <stdint.h>
39
#include <stdio.h>
@@ -63,119 +69,202 @@ static void print_cpuid_string(struct cpuid_regs regs, uint32_t eax, enum cpuid_
6369
}
6470
}
6571

72+
static void force_one_cpu(int cpu, int n_cpus) {
73+
if (n_cpus > 1) {
74+
#ifdef __linux__
75+
cpu_set_t mask_one;
76+
CPU_ZERO(&mask_one);
77+
CPU_SET(cpu, &mask_one);
78+
if (sched_setaffinity(0, sizeof mask_one, &mask_one) != 0) {
79+
char buf[80];
80+
snprintf(buf, sizeof(buf), "sched_setaffinity to CPU %d failed", cpu);
81+
perror(buf);
82+
exit(1);
83+
}
84+
#endif
85+
}
86+
}
87+
88+
static void print_cpu_index(int cpu, int n_cpus) {
89+
#ifdef __linux__
90+
if (n_cpus > 1)
91+
printf("cpu%.*d: ", (n_cpus < 10 ? 1 : (n_cpus < 100 ? 2 : 3)), cpu);
92+
#endif
93+
}
94+
6695
int main(int argc, char** argv) {
6796
const uint32_t max_base_index = cpuid(0).eax;
68-
uint32_t max_structured_index = 0, max_trace_index = 0, max_socid_index = 0;
69-
bool has_sgx = false;
70-
for (uint32_t eax = 0; eax <= max_base_index; eax++) {
71-
switch (eax) {
72-
case UINT32_C(0x00000000):
73-
print_cpuid_string(cpuid(eax), eax, REG_BDC);
74-
break;
75-
case UINT32_C(0x00000004):
76-
for (uint32_t ecx = 0;; ecx++) {
77-
const struct cpuid_regs regs = cpuidex(eax, ecx);
78-
if ((regs.eax & UINT32_C(0x1F)) == 0) {
79-
break;
80-
}
81-
print_cpuidex(regs, eax, ecx);
82-
}
83-
break;
84-
case UINT32_C(0x00000007):
85-
for (uint32_t ecx = 0; ecx <= max_structured_index; ecx++) {
86-
const struct cpuid_regs regs = cpuidex(eax, ecx);
87-
if (ecx == 0) {
88-
max_structured_index = regs.eax;
89-
has_sgx = !!(regs.ebx & UINT32_C(0x00000004));
97+
uint32_t max_structured_index = 0, max_trace_index = 0, max_socid_index = 0, n_log_proc = 1;
98+
bool has_sgx = false, is_hybrid = false;
99+
100+
#ifdef __linux__
101+
// TODO: handle case of >CPU_SETSIZE logical CPUs, or non-contiguity
102+
cpu_set_t mask_default;
103+
if (sched_getaffinity(0, sizeof mask_default, &mask_default) != 0) {
104+
perror("sched_getaffinity failed");
105+
exit(1);
106+
}
107+
n_log_proc = CPU_COUNT(&mask_default);
108+
#endif
109+
if (max_base_index >= 1) {
110+
// TODO: handle case of >256 logical CPUs, or multiple packages
111+
const struct cpuid_regs regs = cpuid(1);
112+
uint32_t n_apic_id;
113+
if (regs.edx & UINT32_C(0x10000000)) { // Number of logical CPUs field is valid
114+
n_apic_id = ((regs.ebx & UINT32_C(0x00ff0000)) >> 16);
115+
116+
#ifdef __linux__
117+
if (n_apic_id != n_log_proc)
118+
fprintf(stderr,
119+
"WARNING: %d logical CPUs per CPUID.01h.EBX[23:16] != %d per sched_getaffinity()\n",
120+
n_apic_id,
121+
n_log_proc);
122+
#else
123+
if (n_apic_id > 1)
124+
fprintf(stderr,
125+
"WARNING: %d logical CPUs per CPUID.01h.EBX, results may vary by logical CPU\n",
126+
n_apic_id);
127+
#endif
128+
}
129+
}
130+
131+
for (uint32_t lp = 0; lp < n_log_proc; lp++) {
132+
force_one_cpu(lp, n_log_proc);
133+
134+
for (uint32_t eax = 0; eax <= max_base_index; eax++) {
135+
switch (eax) {
136+
case UINT32_C(0x00000000):
137+
print_cpu_index(lp, n_log_proc);
138+
print_cpuid_string(cpuid(eax), eax, REG_BDC);
139+
break;
140+
case UINT32_C(0x00000004):
141+
for (uint32_t ecx = 0;; ecx++) {
142+
const struct cpuid_regs regs = cpuidex(eax, ecx);
143+
if ((regs.eax & UINT32_C(0x1F)) == 0) {
144+
break;
145+
}
146+
print_cpu_index(lp, n_log_proc);
147+
print_cpuidex(regs, eax, ecx);
90148
}
91-
print_cpuidex(regs, eax, ecx);
92-
}
93-
break;
94-
case UINT32_C(0x0000000B):
95-
case UINT32_C(0x0000001F): // Extended/V2
96-
for (uint32_t ecx = 0;; ecx++) {
97-
const struct cpuid_regs regs = cpuidex(eax, ecx);
98-
if ((regs.ecx & UINT32_C(0x0000FF00)) == 0) {
99-
break;
149+
break;
150+
case UINT32_C(0x00000007):
151+
for (uint32_t ecx = 0; ecx <= max_structured_index; ecx++) {
152+
const struct cpuid_regs regs = cpuidex(eax, ecx);
153+
if (ecx == 0) {
154+
max_structured_index = regs.eax;
155+
has_sgx = !!(regs.ebx & UINT32_C(0x00000004));
156+
}
157+
print_cpu_index(lp, n_log_proc);
158+
print_cpuidex(regs, eax, ecx);
100159
}
101-
print_cpuidex(regs, eax, ecx);
102-
}
103-
break;
104-
case UINT32_C(0x00000012):
105-
if (has_sgx) {
160+
break;
161+
case UINT32_C(0x0000000B):
162+
case UINT32_C(0x0000001F): // Extended/V2
106163
for (uint32_t ecx = 0;; ecx++) {
107164
const struct cpuid_regs regs = cpuidex(eax, ecx);
108-
if (ecx >= 2 && (regs.eax & UINT32_C(0x0000000F)) == 0) {
165+
if ((regs.ecx & UINT32_C(0x0000FF00)) == 0) {
109166
break;
110167
}
168+
print_cpu_index(lp, n_log_proc);
111169
print_cpuidex(regs, eax, ecx);
112170
}
113-
}
114-
break;
115-
case UINT32_C(0x00000014):
116-
for (uint32_t ecx = 0; ecx <= max_trace_index; ecx++) {
117-
const struct cpuid_regs regs = cpuidex(eax, ecx);
118-
if (ecx == 0) {
119-
max_trace_index = regs.eax;
171+
break;
172+
case UINT32_C(0x00000012):
173+
if (has_sgx) {
174+
for (uint32_t ecx = 0;; ecx++) {
175+
const struct cpuid_regs regs = cpuidex(eax, ecx);
176+
if (ecx >= 2 && (regs.eax & UINT32_C(0x0000000F)) == 0) {
177+
break;
178+
}
179+
print_cpu_index(lp, n_log_proc);
180+
print_cpuidex(regs, eax, ecx);
181+
}
182+
}
183+
break;
184+
case UINT32_C(0x00000014):
185+
for (uint32_t ecx = 0; ecx <= max_trace_index; ecx++) {
186+
const struct cpuid_regs regs = cpuidex(eax, ecx);
187+
if (ecx == 0) {
188+
max_trace_index = regs.eax;
189+
}
190+
print_cpu_index(lp, n_log_proc);
191+
print_cpuidex(regs, eax, ecx);
120192
}
121-
print_cpuidex(regs, eax, ecx);
122-
}
123-
break;
124-
case UINT32_C(0x00000017):
125-
for (uint32_t ecx = 0; ecx <= max_socid_index; ecx++) {
126-
const struct cpuid_regs regs = cpuidex(eax, ecx);
127-
if (ecx == 0) {
128-
max_socid_index = regs.eax;
193+
break;
194+
case UINT32_C(0x00000017):
195+
for (uint32_t ecx = 0; ecx <= max_socid_index; ecx++) {
196+
const struct cpuid_regs regs = cpuidex(eax, ecx);
197+
if (ecx == 0) {
198+
max_socid_index = regs.eax;
199+
}
200+
print_cpu_index(lp, n_log_proc);
201+
print_cpuidex(regs, eax, ecx);
129202
}
130-
print_cpuidex(regs, eax, ecx);
131-
}
132-
break;
133-
case UINT32_C(0x00000024):
134-
for (uint32_t ecx = 0; ecx <= max_socid_index; ecx++) {
135-
const struct cpuid_regs regs = cpuidex(eax, ecx);
136-
if (ecx == 0) {
137-
max_socid_index = regs.eax;
203+
break;
204+
case UINT32_C(0x00000024):
205+
for (uint32_t ecx = 0; ecx <= max_socid_index; ecx++) {
206+
const struct cpuid_regs regs = cpuidex(eax, ecx);
207+
if (ecx == 0) {
208+
max_socid_index = regs.eax;
209+
}
210+
print_cpu_index(lp, n_log_proc);
211+
print_cpuidex(regs, eax, ecx);
138212
}
139-
print_cpuidex(regs, eax, ecx);
140-
}
141-
break;
142-
default:
143-
print_cpuid(cpuidex(eax, 0), eax);
144-
break;
213+
break;
214+
default:
215+
print_cpu_index(lp, n_log_proc);
216+
print_cpuid(cpuidex(eax, 0), eax);
217+
break;
218+
}
219+
}
220+
221+
/**
222+
* CPUID[1].ECX bit 31 is supposed to indicate whether or not
223+
* a hypervisor is running, but not all hypervisors set it.
224+
*/
225+
const uint32_t max_hypervisor_index = cpuid(UINT32_C(0x40000000)).eax;
226+
for (uint32_t eax = UINT32_C(0x40000000); eax <= max_hypervisor_index; eax++) {
227+
switch (eax) {
228+
case UINT32_C(0x40000000):
229+
print_cpu_index(lp, n_log_proc);
230+
print_cpuid_string(cpuid(eax), eax, REG_BCD);
231+
break;
232+
case UINT32_C(0x40000001):
233+
print_cpu_index(lp, n_log_proc);
234+
print_cpuid_string(cpuid(eax), eax, REG_A);
235+
break;
236+
default:
237+
print_cpu_index(lp, n_log_proc);
238+
print_cpuid(cpuidex(eax, 0), eax);
239+
}
145240
}
146-
}
147241

148-
/**
149-
* CPUID[1].ECX bit 31 is supposed to indicate whether or not
150-
* a hypervisor is running, but not all hypervisors set it.
151-
*/
152-
const uint32_t max_hypervisor_index = cpuid(UINT32_C(0x40000000)).eax;
153-
for (uint32_t eax = UINT32_C(0x40000000); eax <= max_hypervisor_index; eax++) {
154-
switch (eax) {
155-
case UINT32_C(0x40000000):
156-
print_cpuid_string(cpuid(eax), eax, REG_BCD);
157-
break;
158-
case UINT32_C(0x40000001):
159-
print_cpuid_string(cpuid(eax), eax, REG_A);
160-
break;
161-
default:
162-
print_cpuid(cpuidex(eax, 0), eax);
242+
const uint32_t max_extended_index = cpuid(UINT32_C(0x80000000)).eax;
243+
for (uint32_t eax = UINT32_C(0x80000000); eax <= max_extended_index; eax++) {
244+
switch (eax) {
245+
case UINT32_C(0x80000000):
246+
print_cpu_index(lp, n_log_proc);
247+
print_cpuid_string(cpuid(eax), eax, REG_BDC);
248+
break;
249+
case UINT32_C(0x80000002):
250+
case UINT32_C(0x80000003):
251+
case UINT32_C(0x80000004):
252+
print_cpu_index(lp, n_log_proc);
253+
print_cpuid_string(cpuid(eax), eax, REG_ABCD);
254+
break;
255+
default:
256+
print_cpu_index(lp, n_log_proc);
257+
print_cpuid(cpuidex(eax, 0), eax);
258+
}
163259
}
164260
}
165261

166-
const uint32_t max_extended_index = cpuid(UINT32_C(0x80000000)).eax;
167-
for (uint32_t eax = UINT32_C(0x80000000); eax <= max_extended_index; eax++) {
168-
switch (eax) {
169-
case UINT32_C(0x80000000):
170-
print_cpuid_string(cpuid(eax), eax, REG_BDC);
171-
break;
172-
case UINT32_C(0x80000002):
173-
case UINT32_C(0x80000003):
174-
case UINT32_C(0x80000004):
175-
print_cpuid_string(cpuid(eax), eax, REG_ABCD);
176-
break;
177-
default:
178-
print_cpuid(cpuidex(eax, 0), eax);
262+
if (n_log_proc > 1) {
263+
#ifdef __linux__
264+
if (sched_setaffinity(0, sizeof mask_default, &mask_default) != 0) {
265+
perror("sched_setaffinity to restore process defaults failed");
266+
exit(1);
179267
}
268+
#endif
180269
}
181270
}

0 commit comments

Comments
 (0)