git: a8ea1540640f - main - x86: Distinguish Xen from non-Xen PVH boots

From: Colin Percival <cperciva_at_FreeBSD.org>
Date: Tue, 18 Oct 2022 06:03:15 UTC
The branch main has been updated by cperciva:

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

commit a8ea1540640f154844f12a0b24d0e272b6e39ca9
Author:     Colin Percival <cperciva@FreeBSD.org>
AuthorDate: 2022-07-13 00:46:04 +0000
Commit:     Colin Percival <cperciva@FreeBSD.org>
CommitDate: 2022-10-18 06:02:22 +0000

    x86: Distinguish Xen from non-Xen PVH boots
    
    The PVH boot protocol, introduced by Xen, is now used by some non-Xen
    platforms (e.g. the Firecracker VM) as well.  In order to accommodate
    these, we use CPUID to detect Xen and only perform Xen-specific setup
    when running on that platform.
    
    The "isxen" function duplicates some work done by identcpu.c later in
    the boot process; but we need it here since this is the very first C
    code which runs when PVH booting (even before hammer_time).
    
    In many places the existing code had
            xc_printf(...);
            HYPERVISOR_shutdown(SHUTDOWN_crash);
    making use of Xen functionality to print a message and shut down; in
    the places where this idiom can be reached in the non-xen case, we
    replace it idiom with a CRASH(...) macro which calls those in the Xen
    case and halts in the non-Xen case.
    
    Reviewed by:    royger
    Sponsored by:   https://www.patreon.com/cperciva
    Differential Revision:  https://reviews.freebsd.org/D35801
---
 sys/x86/xen/pv.c | 92 +++++++++++++++++++++++++++++++++++++++++---------------
 1 file changed, 68 insertions(+), 24 deletions(-)

diff --git a/sys/x86/xen/pv.c b/sys/x86/xen/pv.c
index 845c2d7222eb..b81423a1be88 100644
--- a/sys/x86/xen/pv.c
+++ b/sys/x86/xen/pv.c
@@ -76,6 +76,7 @@ __FBSDID("$FreeBSD$");
 #include <xen/xenstore/xenstorevar.h>
 #include <xen/xen_pv.h>
 
+#include <contrib/xen/arch-x86/cpuid.h>
 #include <contrib/xen/arch-x86/hvm/start_info.h>
 #include <contrib/xen/vcpu.h>
 
@@ -117,6 +118,44 @@ static struct hvm_start_info *start_info;
 
 /*-------------------------------- Xen PV init -------------------------------*/
 
+static int
+isxen(void)
+{
+	static int xen = -1;
+	uint32_t base;
+	u_int regs[4];
+
+	if (xen != -1)
+		return (xen);
+
+	/*
+	 * The full code for identifying which hypervisor we're running under
+	 * is in sys/x86/x86/identcpu.c and runs later in the boot process;
+	 * this is sufficient to distinguish Xen PVH booting from non-Xen PVH
+	 * and skip some very early Xen-specific code in the non-Xen case.
+	 */
+	xen = 0;
+	for (base = 0x40000000; base < 0x40010000; base += 0x100) {
+		do_cpuid(base, regs);
+		if (regs[1] == XEN_CPUID_SIGNATURE_EBX &&
+		    regs[2] == XEN_CPUID_SIGNATURE_ECX &&
+		    regs[3] == XEN_CPUID_SIGNATURE_EDX) {
+			xen = 1;
+			break;
+		}
+	}
+	return (xen);
+}
+
+#define CRASH(...) do {					\
+	if (isxen()) {					\
+		xc_printf(__VA_ARGS__);			\
+		HYPERVISOR_shutdown(SHUTDOWN_crash);	\
+	} else {					\
+		halt();					\
+	}						\
+} while (0)
+
 uint64_t
 hammer_time_xen(vm_paddr_t start_info_paddr)
 {
@@ -126,21 +165,21 @@ hammer_time_xen(vm_paddr_t start_info_paddr)
 	char *kenv;
 	int rc;
 
-	xen_domain_type = XEN_HVM_DOMAIN;
-	vm_guest = VM_GUEST_XEN;
-
-	rc = xen_hvm_init_hypercall_stubs(XEN_HVM_INIT_EARLY);
-	if (rc) {
-		xc_printf("ERROR: failed to initialize hypercall page: %d\n",
-		    rc);
-		HYPERVISOR_shutdown(SHUTDOWN_crash);
+	if (isxen()) {
+		xen_domain_type = XEN_HVM_DOMAIN;
+		vm_guest = VM_GUEST_XEN;
+		rc = xen_hvm_init_hypercall_stubs(XEN_HVM_INIT_EARLY);
+		if (rc) {
+			xc_printf("ERROR: failed to initialize hypercall page: %d\n",
+			    rc);
+			HYPERVISOR_shutdown(SHUTDOWN_crash);
+		}
 	}
 
 	start_info = (struct hvm_start_info *)(start_info_paddr + KERNBASE);
 	if (start_info->magic != XEN_HVM_START_MAGIC_VALUE) {
-		xc_printf("Unknown magic value in start_info struct: %#x\n",
+		CRASH("Unknown magic value in start_info struct: %#x\n",
 		    start_info->magic);
-		HYPERVISOR_shutdown(SHUTDOWN_crash);
 	}
 
 	/*
@@ -164,9 +203,8 @@ hammer_time_xen(vm_paddr_t start_info_paddr)
 		unsigned int i;
 
 		if (start_info->nr_modules == 0) {
-			xc_printf(
+			CRASH(
 			    "ERROR: modlist_paddr != 0 but nr_modules == 0\n");
-			HYPERVISOR_shutdown(SHUTDOWN_crash);
 		}
 		mod = (struct hvm_modlist_entry *)
 		    (start_info->modlist_paddr + KERNBASE);
@@ -175,16 +213,18 @@ hammer_time_xen(vm_paddr_t start_info_paddr)
 			    PAGE_SIZE), physfree);
 	}
 
-	xatp.domid = DOMID_SELF;
-	xatp.idx = 0;
-	xatp.space = XENMAPSPACE_shared_info;
-	xatp.gpfn = atop(physfree);
-	if (HYPERVISOR_memory_op(XENMEM_add_to_physmap, &xatp)) {
-		xc_printf("ERROR: failed to setup shared_info page\n");
-		HYPERVISOR_shutdown(SHUTDOWN_crash);
+	if (isxen()) {
+		xatp.domid = DOMID_SELF;
+		xatp.idx = 0;
+		xatp.space = XENMAPSPACE_shared_info;
+		xatp.gpfn = atop(physfree);
+		if (HYPERVISOR_memory_op(XENMEM_add_to_physmap, &xatp)) {
+			xc_printf("ERROR: failed to setup shared_info page\n");
+			HYPERVISOR_shutdown(SHUTDOWN_crash);
+		}
+		HYPERVISOR_shared_info = (shared_info_t *)(physfree + KERNBASE);
+		physfree += PAGE_SIZE;
 	}
-	HYPERVISOR_shared_info = (shared_info_t *)(physfree + KERNBASE);
-	physfree += PAGE_SIZE;
 
 	/*
 	 * Init a static kenv using a free page. The contents will be filled
@@ -245,7 +285,7 @@ xen_pvh_set_env(char *env, bool (*filter)(const char *))
 
 		value = option;
 		option = strsep(&value, "=");
-		if (kern_setenv(option, value) != 0)
+		if (kern_setenv(option, value) != 0 && isxen())
 			xc_printf("unable to add kenv %s=%s\n", option, value);
 		option = value + strlen(value) + 1;
 	}
@@ -269,7 +309,8 @@ xen_pvh_parse_symtab(void)
 	if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) ||
 	    ehdr->e_ident[EI_CLASS] != ELF_TARG_CLASS ||
 	    ehdr->e_version > 1) {
-		xc_printf("Unable to load ELF symtab: invalid symbol table\n");
+		if (isxen())
+			xc_printf("Unable to load ELF symtab: invalid symbol table\n");
 		return;
 	}
 
@@ -289,7 +330,7 @@ xen_pvh_parse_symtab(void)
 		break;
 	}
 
-	if (ksymtab == 0 || kstrtab == 0)
+	if ((ksymtab == 0 || kstrtab == 0) && isxen())
 		xc_printf(
     "Unable to load ELF symtab: could not find symtab or strtab\n");
 }
@@ -432,6 +473,9 @@ xen_pvh_parse_memmap(caddr_t kmdp, vm_paddr_t *physmap, int *physmap_idx)
 	u_int32_t size;
 	int rc;
 
+	/* We should only reach here if we're running under Xen. */
+	KASSERT(isxen(), ("xen_pvh_parse_memmap reached when !Xen"));
+
 	/* Fetch the E820 map from Xen */
 	memmap.nr_entries = MAX_E820_ENTRIES;
 	set_xen_guest_handle(memmap.buffer, xen_smap);