diff --git a/tools/cpuid-dump.c b/tools/cpuid-dump.c index 1a65510f..cb78ae45 100644 --- a/tools/cpuid-dump.c +++ b/tools/cpuid-dump.c @@ -1,3 +1,9 @@ +#ifdef __linux__ +#define _GNU_SOURCE +#include +#include +#endif + #include #include #include @@ -24,133 +30,223 @@ static void print_cpuidex(struct cpuid_regs regs, uint32_t eax, uint32_t ecx) { ecx); } -static void print_cpuid_vendor(struct cpuid_regs regs, uint32_t eax) { - if (regs.ebx | regs.ecx | regs.edx) { - char vendor_id[12]; - memcpy(&vendor_id[0], ®s.ebx, sizeof(regs.ebx)); - memcpy(&vendor_id[4], ®s.edx, sizeof(regs.edx)); - memcpy(&vendor_id[8], ®s.ecx, sizeof(regs.ecx)); - printf("CPUID %08" PRIX32 ": %08" PRIX32 "-%08" PRIX32 "-%08" PRIX32 "-%08" PRIX32 " [%.12s]\n", +enum cpuid_string_format { REG_ABCD = 1, REG_BDC = 2, REG_BCD = 3, REG_A = 4 }; + +static void print_cpuid_string(struct cpuid_regs regs, uint32_t eax, enum cpuid_string_format fmt) { + char buf[16]; + memset(buf, 0, sizeof(buf)); + switch (fmt) { + case REG_ABCD: + memcpy(&buf[0], ®s.eax, sizeof(regs.eax)); + memcpy(&buf[4], ®s.ebx, sizeof(regs.ebx)); + memcpy(&buf[8], ®s.ecx, sizeof(regs.ecx)); + memcpy(&buf[12], ®s.edx, sizeof(regs.edx)); + break; + case REG_BCD: + memcpy(&buf[0], ®s.ebx, sizeof(regs.ebx)); + memcpy(&buf[4], ®s.ecx, sizeof(regs.ecx)); + memcpy(&buf[8], ®s.edx, sizeof(regs.edx)); + break; + case REG_BDC: + memcpy(&buf[0], ®s.ebx, sizeof(regs.ebx)); + memcpy(&buf[4], ®s.edx, sizeof(regs.ecx)); + memcpy(&buf[8], ®s.ecx, sizeof(regs.edx)); + break; + case REG_A: + memcpy(&buf[0], ®s.eax, sizeof(regs.eax)); + break; + } + if (buf[0]) { + printf("CPUID %08" PRIX32 ": %08" PRIX32 "-%08" PRIX32 "-%08" PRIX32 "-%08" PRIX32 " [%.16s]\n", eax, regs.eax, regs.ebx, regs.ecx, regs.edx, - vendor_id); + buf); } else { print_cpuid(regs, eax); } } -static void print_cpuid_brand_string(struct cpuid_regs regs, uint32_t eax) { - char brand_string[16]; - memcpy(&brand_string[0], ®s.eax, sizeof(regs.eax)); - memcpy(&brand_string[4], ®s.ebx, sizeof(regs.ebx)); - memcpy(&brand_string[8], ®s.ecx, sizeof(regs.ecx)); - memcpy(&brand_string[12], ®s.edx, sizeof(regs.edx)); - printf("CPUID %08" PRIX32 ": %08" PRIX32 "-%08" PRIX32 "-%08" PRIX32 "-%08" PRIX32 " [%.16s]\n", - eax, - regs.eax, - regs.ebx, - regs.ecx, - regs.edx, - brand_string); +static void force_one_cpu(int cpu, int n_cpus) { + if (n_cpus > 1) { +#ifdef __linux__ + cpu_set_t mask_one; + CPU_ZERO(&mask_one); + CPU_SET(cpu, &mask_one); + if (sched_setaffinity(0, sizeof mask_one, &mask_one) != 0) { + char buf[80]; + snprintf(buf, sizeof(buf), "sched_setaffinity to CPU %d failed", cpu); + perror(buf); + exit(1); + } +#endif + } +} + +static void print_cpu_index(int cpu, int n_cpus) { +#ifdef __linux__ + if (n_cpus > 1) + printf("cpu%.*d: ", (n_cpus < 10 ? 1 : (n_cpus < 100 ? 2 : 3)), cpu); +#endif } int main(int argc, char** argv) { const uint32_t max_base_index = cpuid(0).eax; - uint32_t max_structured_index = 0, max_trace_index = 0, max_socid_index = 0; - bool has_sgx = false; - for (uint32_t eax = 0; eax <= max_base_index; eax++) { - switch (eax) { - case UINT32_C(0x00000000): - print_cpuid_vendor(cpuid(eax), eax); - break; - case UINT32_C(0x00000004): - for (uint32_t ecx = 0;; ecx++) { - const struct cpuid_regs regs = cpuidex(eax, ecx); - if ((regs.eax & UINT32_C(0x1F)) == 0) { - break; - } - print_cpuidex(regs, eax, ecx); - } - break; - case UINT32_C(0x00000007): - for (uint32_t ecx = 0; ecx <= max_structured_index; ecx++) { - const struct cpuid_regs regs = cpuidex(eax, ecx); - if (ecx == 0) { - max_structured_index = regs.eax; - has_sgx = !!(regs.ebx & UINT32_C(0x00000004)); + uint32_t max_structured_index = 0, max_trace_index = 0, max_socid_index = 0, n_log_proc = 1; + bool has_sgx = false, is_hybrid = false; + +#ifdef __linux__ + // TODO: handle case of >CPU_SETSIZE logical CPUs, or non-contiguity + cpu_set_t mask_default; + if (sched_getaffinity(0, sizeof mask_default, &mask_default) != 0) { + perror("sched_getaffinity failed"); + exit(1); + } + n_log_proc = CPU_COUNT(&mask_default); +#else + fprintf(stderr, + "WARNING: results may vary by CPU, core or thread, but switching CPU is unsupported.\n"); +#endif + + for (uint32_t lp = 0; lp < n_log_proc; lp++) { + force_one_cpu(lp, n_log_proc); + + for (uint32_t eax = 0; eax <= max_base_index; eax++) { + switch (eax) { + case UINT32_C(0x00000000): + print_cpu_index(lp, n_log_proc); + print_cpuid_string(cpuid(eax), eax, REG_BDC); + break; + case UINT32_C(0x00000004): + for (uint32_t ecx = 0;; ecx++) { + const struct cpuid_regs regs = cpuidex(eax, ecx); + if ((regs.eax & UINT32_C(0x1F)) == 0) { + break; + } + print_cpu_index(lp, n_log_proc); + print_cpuidex(regs, eax, ecx); } - print_cpuidex(regs, eax, ecx); - } - break; - case UINT32_C(0x0000000B): - for (uint32_t ecx = 0;; ecx++) { - const struct cpuid_regs regs = cpuidex(eax, ecx); - if ((regs.ecx & UINT32_C(0x0000FF00)) == 0) { - break; + break; + case UINT32_C(0x00000007): + for (uint32_t ecx = 0; ecx <= max_structured_index; ecx++) { + const struct cpuid_regs regs = cpuidex(eax, ecx); + if (ecx == 0) { + max_structured_index = regs.eax; + has_sgx = !!(regs.ebx & UINT32_C(0x00000004)); + } + print_cpu_index(lp, n_log_proc); + print_cpuidex(regs, eax, ecx); } - print_cpuidex(regs, eax, ecx); - } - break; - case UINT32_C(0x00000012): - if (has_sgx) { + break; + case UINT32_C(0x0000000B): + case UINT32_C(0x0000001F): // Extended/V2 for (uint32_t ecx = 0;; ecx++) { const struct cpuid_regs regs = cpuidex(eax, ecx); - if (ecx >= 2 && (regs.eax & UINT32_C(0x0000000F)) == 0) { + if ((regs.ecx & UINT32_C(0x0000FF00)) == 0) { break; } + print_cpu_index(lp, n_log_proc); print_cpuidex(regs, eax, ecx); } - } - break; - case UINT32_C(0x00000014): - for (uint32_t ecx = 0; ecx <= max_trace_index; ecx++) { - const struct cpuid_regs regs = cpuidex(eax, ecx); - if (ecx == 0) { - max_trace_index = regs.eax; + break; + case UINT32_C(0x00000012): + if (has_sgx) { + for (uint32_t ecx = 0;; ecx++) { + const struct cpuid_regs regs = cpuidex(eax, ecx); + if (ecx >= 2 && (regs.eax & UINT32_C(0x0000000F)) == 0) { + break; + } + print_cpu_index(lp, n_log_proc); + print_cpuidex(regs, eax, ecx); + } + } + break; + case UINT32_C(0x00000014): + for (uint32_t ecx = 0; ecx <= max_trace_index; ecx++) { + const struct cpuid_regs regs = cpuidex(eax, ecx); + if (ecx == 0) { + max_trace_index = regs.eax; + } + print_cpu_index(lp, n_log_proc); + print_cpuidex(regs, eax, ecx); } - print_cpuidex(regs, eax, ecx); - } - break; - case UINT32_C(0x00000017): - for (uint32_t ecx = 0; ecx <= max_socid_index; ecx++) { - const struct cpuid_regs regs = cpuidex(eax, ecx); - if (ecx == 0) { - max_socid_index = regs.eax; + break; + case UINT32_C(0x00000017): + for (uint32_t ecx = 0; ecx <= max_socid_index; ecx++) { + const struct cpuid_regs regs = cpuidex(eax, ecx); + if (ecx == 0) { + max_socid_index = regs.eax; + } + print_cpu_index(lp, n_log_proc); + print_cpuidex(regs, eax, ecx); } - print_cpuidex(regs, eax, ecx); - } - break; - case UINT32_C(0x00000024): - for (uint32_t ecx = 0; ecx <= max_socid_index; ecx++) { - const struct cpuid_regs regs = cpuidex(eax, ecx); - if (ecx == 0) { - max_socid_index = regs.eax; + break; + case UINT32_C(0x00000024): + for (uint32_t ecx = 0; ecx <= max_socid_index; ecx++) { + const struct cpuid_regs regs = cpuidex(eax, ecx); + if (ecx == 0) { + max_socid_index = regs.eax; + } + print_cpu_index(lp, n_log_proc); + print_cpuidex(regs, eax, ecx); } - print_cpuidex(regs, eax, ecx); - } - break; - default: - print_cpuid(cpuidex(eax, 0), eax); - break; + break; + default: + print_cpu_index(lp, n_log_proc); + print_cpuid(cpuidex(eax, 0), eax); + break; + } + } + + /** + * CPUID[1].ECX bit 31 is supposed to indicate whether or not + * a hypervisor is running, but not all hypervisors set it. + */ + const uint32_t max_hypervisor_index = cpuid(UINT32_C(0x40000000)).eax; + for (uint32_t eax = UINT32_C(0x40000000); eax <= max_hypervisor_index; eax++) { + switch (eax) { + case UINT32_C(0x40000000): + print_cpu_index(lp, n_log_proc); + print_cpuid_string(cpuid(eax), eax, REG_BCD); + break; + case UINT32_C(0x40000001): + print_cpu_index(lp, n_log_proc); + print_cpuid_string(cpuid(eax), eax, REG_A); + break; + default: + print_cpu_index(lp, n_log_proc); + print_cpuid(cpuidex(eax, 0), eax); + } + } + + const uint32_t max_extended_index = cpuid(UINT32_C(0x80000000)).eax; + for (uint32_t eax = UINT32_C(0x80000000); eax <= max_extended_index; eax++) { + switch (eax) { + case UINT32_C(0x80000000): + print_cpu_index(lp, n_log_proc); + print_cpuid_string(cpuid(eax), eax, REG_BDC); + break; + case UINT32_C(0x80000002): + case UINT32_C(0x80000003): + case UINT32_C(0x80000004): + print_cpu_index(lp, n_log_proc); + print_cpuid_string(cpuid(eax), eax, REG_ABCD); + break; + default: + print_cpu_index(lp, n_log_proc); + print_cpuid(cpuidex(eax, 0), eax); + } } } - const uint32_t max_extended_index = cpuid(UINT32_C(0x80000000)).eax; - for (uint32_t eax = UINT32_C(0x80000000); eax <= max_extended_index; eax++) { - switch (eax) { - case UINT32_C(0x80000000): - print_cpuid_vendor(cpuid(eax), eax); - break; - case UINT32_C(0x80000002): - case UINT32_C(0x80000003): - case UINT32_C(0x80000004): - print_cpuid_brand_string(cpuid(eax), eax); - break; - default: - print_cpuid(cpuidex(eax, 0), eax); + if (n_log_proc > 1) { +#ifdef __linux__ + if (sched_setaffinity(0, sizeof mask_default, &mask_default) != 0) { + perror("sched_setaffinity to restore process defaults failed"); + exit(1); } +#endif } }