From cd79485a96da328304a2978e4d2d094e06206219 Mon Sep 17 00:00:00 2001 From: Daniel Lenski Date: Thu, 20 Nov 2025 21:30:56 -0800 Subject: [PATCH] cpuid-dump should print hypervisor leaves (0x40000000) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per https://en.wikipedia.org/wiki/CPUID#EAX=4000'0000h-4FFFF'FFFh:_Reserved_for_Hypervisors: > CPUID leaves 40000000h to 4FFFFFFFh are not implemented in hardware, and > are reserved for use by hypervisors to provide hypervisor-specific > identification and feature information through this interception > mechanism. > > For leaf 40000000h, the hypervisor is expected to return the index of the > highest supported hypervisor CPUID leaf in EAX, and a 12-character > hypervisor ID string in EBX,ECX,EDX (in that order). For leaf 40000001h, > the hypervisor may return an interface identification signature in EAX - > e.g. hypervisors that wish to advertise that they are Hyper-V compatible > may return 0x31237648—"Hv#1" in EAX. For example, running with user-mode QEMU: ``` $ qemu-x86_64 cpuid-dump … CPUID 40000000: 40000001-54474354-43544743-47435447 [TCGTCGTCGTCG] CPUID 40000001: 00000000-00000000-00000000-00000000 … ``` Running under a recent version of WSL2 (Microsoft Hyper-V): ``` $ cpuid-dump … CPUID 40000000: 4000000B-7263694D-666F736F-76482074 [Microsoft Hv] CPUID 40000001: 31237648-00000000-00000000-00000000 [Hv#1] … CPUID 4000000B: 00000000-00000000-00000000-00000000 ``` --- tools/cpuid-dump.c | 76 ++++++++++++++++++++++++++++++---------------- 1 file changed, 50 insertions(+), 26 deletions(-) diff --git a/tools/cpuid-dump.c b/tools/cpuid-dump.c index 1a65510f..febdc682 100644 --- a/tools/cpuid-dump.c +++ b/tools/cpuid-dump.c @@ -24,39 +24,45 @@ 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); -} - 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; @@ -64,7 +70,7 @@ int main(int argc, char** argv) { for (uint32_t eax = 0; eax <= max_base_index; eax++) { switch (eax) { case UINT32_C(0x00000000): - print_cpuid_vendor(cpuid(eax), eax); + print_cpuid_string(cpuid(eax), eax, REG_BDC); break; case UINT32_C(0x00000004): for (uint32_t ecx = 0;; ecx++) { @@ -138,16 +144,34 @@ int main(int argc, char** argv) { } } + /** + * 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_cpuid_string(cpuid(eax), eax, REG_BCD); + break; + case UINT32_C(0x40000001): + print_cpuid_string(cpuid(eax), eax, REG_A); + break; + default: + 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); + print_cpuid_string(cpuid(eax), eax, REG_BDC); break; case UINT32_C(0x80000002): case UINT32_C(0x80000003): case UINT32_C(0x80000004): - print_cpuid_brand_string(cpuid(eax), eax); + print_cpuid_string(cpuid(eax), eax, REG_ABCD); break; default: print_cpuid(cpuidex(eax, 0), eax);