git: 6744fd8e7503 - main - x86/cpu: improve hypervisor detection

From: Roger Pau Monné <royger_at_FreeBSD.org>
Date: Thu, 22 Feb 2024 10:31:19 UTC
The branch main has been updated by royger:

URL: https://cgit.FreeBSD.org/src/commit/?id=6744fd8e75032c893e6a80bced8be3a991fa2901

commit 6744fd8e75032c893e6a80bced8be3a991fa2901
Author:     Roger Pau Monné <royger@FreeBSD.org>
AuthorDate: 2024-01-19 09:15:17 +0000
Commit:     Roger Pau Monné <royger@FreeBSD.org>
CommitDate: 2024-02-22 10:08:04 +0000

    x86/cpu: improve hypervisor detection
    
    Some hypervisors can expose multiple signatures, for example Xen will expose
    both the Xen and the HyperV signatures if Viridian extensions are enabled for
    the guest.  Presence of multiple signatures is currently not handled by
    FreeBSD, that will exit once a known signature is found in cpuid output.
    
    Exposing the HyperV signature on hypervisors different than HyperV is not
    uncommon, this is done so that such hypervisor can expose a (subset) of the
    Viridian extensions to Windows guests for performance reasons.  Likely for
    compatibility purposes the HyperV signature is always exposed on the first
    leaf, and the Xen signature is exposed in the secondary leaf.
    
    Fix the specific case of HyperV by not exiting from the scan if the HyperV
    signature is found, and prefer a second signature if one is found.
    
    Note that long term we might wish to convert vm_guest into a bitmap, so that it
    can signal detection of multiple hypervisor interfaces.
    
    Fixes: b0165dc4539f ('x86/xen: fix HVM guest hypercall page setup')
    PR: 276421
    Sponsored by: Cloud Software Group
    Reviewed by: markj kib
    Differential revision: https://reviews.freebsd.org/D43508
---
 sys/x86/x86/identcpu.c | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)

diff --git a/sys/x86/x86/identcpu.c b/sys/x86/x86/identcpu.c
index 9c82f9f8a882..df24c5bddffd 100644
--- a/sys/x86/x86/identcpu.c
+++ b/sys/x86/x86/identcpu.c
@@ -1385,6 +1385,8 @@ identify_hypervisor_cpuid_base(void)
 			regs[0] = leaf + 1;
 
 		if (regs[0] >= leaf) {
+			enum VM_GUEST prev_vm_guest = vm_guest;
+
 			for (i = 0; i < nitems(vm_cpuids); i++)
 				if (strncmp((const char *)&regs[1],
 				    vm_cpuids[i].vm_cpuid, 12) == 0) {
@@ -1397,7 +1399,7 @@ identify_hypervisor_cpuid_base(void)
 			 * specific hypervisor, record the base, high value,
 			 * and vendor identifier.
 			 */
-			if (vm_guest != VM_GUEST_VM || leaf == 0x40000000) {
+			if (vm_guest != prev_vm_guest || leaf == 0x40000000) {
 				hv_base = leaf;
 				hv_high = regs[0];
 				((u_int *)&hv_vendor)[0] = regs[1];
@@ -1409,7 +1411,18 @@ identify_hypervisor_cpuid_base(void)
 				 * If we found a specific hypervisor, then
 				 * we are finished.
 				 */
-				if (vm_guest != VM_GUEST_VM)
+				if (vm_guest != VM_GUEST_VM &&
+				    /*
+				     * Xen and other hypervisors can expose the
+				     * HyperV signature in addition to the
+				     * native one in order to support Viridian
+				     * extensions for Windows guests.
+				     *
+				     * Do the full cpuid scan if HyperV is
+				     * detected, as the native hypervisor is
+				     * preferred.
+				     */
+				    vm_guest != VM_GUEST_HV)
 					return;
 			}
 		}