From a5471559e7398418f1bb9c538a7bfc8f39e9ae98 Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Fri, 4 Oct 2019 15:57:42 +0300 Subject: [PATCH] Fix virt-what-cpuid-helper. The value returned in %eax is the max_entry (eg. 0x40000000 if there are no further leafs). However it is not reliable. In addition if there are multiple leafs we should probably only print the highest one. Also use uint32_t instead of unsigned int. Thanks: Paolo Bonzini. --- virt-what-cpuid-helper.c | 67 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 48 insertions(+), 19 deletions(-) diff --git a/virt-what-cpuid-helper.c b/virt-what-cpuid-helper.c index 7812545..0cd4a6f 100644 --- a/virt-what-cpuid-helper.c +++ b/virt-what-cpuid-helper.c @@ -1,5 +1,5 @@ /* virt-what-cpuid-helper: Are we running inside KVM or Xen HVM? - * Copyright (C) 2008 Red Hat Inc. + * Copyright (C) 2008-2019 Red Hat Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,14 +21,35 @@ */ #include +#include +#include #include #if defined(__i386__) || defined(__x86_64__) -static unsigned int -cpuid (unsigned int eax, char *sig) +/* Known x86 hypervisor signatures. Note that if you add a new test + * to virt-what.in you may need to update this list. The signature is + * always 12 bytes except in the case of KVM. + */ +static int +known_signature (char *sig) { - unsigned int *sig32 = (unsigned int *) sig; + return + strcmp (sig, "bhyve bhyve ") == 0 || + strcmp (sig, "KVMKVMKVM") == 0 || + strcmp (sig, "LKVMLKVMLKVM") == 0 || + strcmp (sig, "Microsoft Hv") == 0 || + strcmp (sig, "OpenBSDVMM58") == 0 || + strcmp (sig, "TCGTCGTCGTCG") == 0 || + strcmp (sig, "VMwareVMware") == 0 || + strcmp (sig, "XenVMMXenVMM") == 0 || + 0; +} + +static uint32_t +cpuid (uint32_t eax, char *sig) +{ + uint32_t *sig32 = (uint32_t *) sig; asm volatile ( "xchgl %%ebx,%1; xor %%ebx,%%ebx; cpuid; xchgl %%ebx,%1" @@ -43,24 +64,32 @@ static void cpu_sig (void) { char sig[13]; - unsigned int base = 0x40000000, leaf = base; - unsigned int max_entries; - - memset (sig, 0, sizeof sig); - max_entries = cpuid (leaf, sig); - puts (sig); + const uint32_t base = 0x40000000; + uint32_t leaf; - /* Most hypervisors only have information in leaf 0x40000000, but - * upstream Xen contains further leaf entries (in particular when - * used with Viridian [HyperV] extensions). CPUID is supposed to - * return the maximum leaf offset in %eax, so that's what we use, - * but only if it looks sensible. + /* Most hypervisors only have information in leaf 0x40000000. + * + * Some hypervisors have "Viridian [HyperV] extensions", and those + * must appear in slot 0x40000000, but they will also have the true + * hypervisor in a higher slot. + * + * CPUID is supposed to return the maximum leaf offset in %eax, but + * this is not reliable. Instead we check the returned signatures + * against a known list (the others will be empty or garbage) and + * only print the ones we know about. This is OK because if we add + * a new test in virt-what we can update the list. + * + * By searching backwards we only print the highest entry, thus + * ignoring Viridian for Xen (and Nutanix). If we ever encounter a + * hypervisor that has more than 2 entries we may need to revisit + * this. */ - if (max_entries > 3 && max_entries < 0x10000) { - for (leaf = base + 0x100; leaf <= base + max_entries; leaf += 0x100) { - memset (sig, 0, sizeof sig); - cpuid (leaf, sig); + for (leaf = base + 0xff00; leaf >= base; leaf -= 0x100) { + memset (sig, 0, sizeof sig); + cpuid (leaf, sig); + if (known_signature (sig)) { puts (sig); + break; } } } -- 1.8.3.1