Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 19 Jul 2018 08:44:52 +0000 (UTC)
From:      =?UTF-8?Q?Roger_Pau_Monn=c3=a9?= <royger@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r336474 - in head/sys: amd64/amd64 x86/xen xen
Message-ID:  <201807190844.w6J8iqTh084024@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: royger
Date: Thu Jul 19 08:44:52 2018
New Revision: 336474
URL: https://svnweb.freebsd.org/changeset/base/336474

Log:
  xen: implement early init helper for PVHv2
  
  In order to setup an initial environment and jump into the generic
  hammer_time initialization function. Some of the code is shared with
  PVHv1, while other code is PVHv2 specific.
  
  This allows booting FreeBSD as a PVHv2 DomU and Dom0.
  
  Sponsored by:	Citrix Systems R&D

Modified:
  head/sys/amd64/amd64/xen-locore.S
  head/sys/x86/xen/hvm.c
  head/sys/x86/xen/pv.c
  head/sys/xen/hvm.h

Modified: head/sys/amd64/amd64/xen-locore.S
==============================================================================
--- head/sys/amd64/amd64/xen-locore.S	Thu Jul 19 08:13:41 2018	(r336473)
+++ head/sys/amd64/amd64/xen-locore.S	Thu Jul 19 08:44:52 2018	(r336474)
@@ -87,7 +87,7 @@ NON_GPROF_ENTRY(xen_start)
 	xorl	%ebp, %ebp
 
 	/* u_int64_t hammer_time_xen(start_info_t *si, u_int64_t xenstack); */
-	call	hammer_time_xen
+	call	hammer_time_xen_legacy
 	movq	%rax, %rsp		/* set up kstack for mi_startup() */
 	call	mi_startup		/* autoconfiguration, mountroot etc */
 

Modified: head/sys/x86/xen/hvm.c
==============================================================================
--- head/sys/x86/xen/hvm.c	Thu Jul 19 08:13:41 2018	(r336473)
+++ head/sys/x86/xen/hvm.c	Thu Jul 19 08:44:52 2018	(r336474)
@@ -82,6 +82,12 @@ static MALLOC_DEFINE(M_XENHVM, "xen_hvm", "Xen HVM PV 
  */
 int xen_vector_callback_enabled;
 
+/**
+ * Start info flags. ATM this only used to store the initial domain flag for
+ * PVHv2, and it's always empty for HVM guests.
+ */
+uint32_t hvm_start_flags;
+
 /*------------------------------- Per-CPU Data -------------------------------*/
 DPCPU_DEFINE(struct vcpu_info, vcpu_local_info);
 DPCPU_DEFINE(struct vcpu_info *, vcpu_info);
@@ -469,7 +475,7 @@ static uint32_t
 hvm_get_start_flags(void)
 {
 
-	return (0);
+	return (hvm_start_flags);
 }
 
 struct hypervisor_info hypervisor_info = {

Modified: head/sys/x86/xen/pv.c
==============================================================================
--- head/sys/x86/xen/pv.c	Thu Jul 19 08:13:41 2018	(r336473)
+++ head/sys/x86/xen/pv.c	Thu Jul 19 08:44:52 2018	(r336474)
@@ -58,6 +58,7 @@ __FBSDID("$FreeBSD$");
 #include <vm/vm_pager.h>
 #include <vm/vm_param.h>
 
+#include <machine/_inttypes.h>
 #include <machine/intr_machdep.h>
 #include <x86/apicvar.h>
 #include <x86/init.h>
@@ -67,11 +68,13 @@ __FBSDID("$FreeBSD$");
 #include <machine/metadata.h>
 
 #include <xen/xen-os.h>
+#include <xen/hvm.h>
 #include <xen/hypervisor.h>
 #include <xen/xenstore/xenstorevar.h>
 #include <xen/xen_pv.h>
 #include <xen/xen_msi.h>
 
+#include <xen/interface/arch-x86/hvm/start_info.h>
 #include <xen/interface/vcpu.h>
 
 #include <dev/xen/timer/timer.h>
@@ -83,13 +86,15 @@ __FBSDID("$FreeBSD$");
 /* Native initial function */
 extern u_int64_t hammer_time(u_int64_t, u_int64_t);
 /* Xen initial function */
-uint64_t hammer_time_xen(start_info_t *, uint64_t);
+uint64_t hammer_time_xen_legacy(start_info_t *, uint64_t);
+uint64_t hammer_time_xen(vm_paddr_t);
 
 #define MAX_E820_ENTRIES	128
 
 /*--------------------------- Forward Declarations ---------------------------*/
-static caddr_t xen_pv_parse_preload_data(u_int64_t);
-static void xen_pv_parse_memmap(caddr_t, vm_paddr_t *, int *);
+static caddr_t xen_legacy_pvh_parse_preload_data(uint64_t);
+static caddr_t xen_pvh_parse_preload_data(uint64_t);
+static void xen_pvh_parse_memmap(caddr_t, vm_paddr_t *, int *);
 
 #ifdef SMP
 static int xen_pv_start_all_aps(void);
@@ -112,20 +117,33 @@ extern uint32_t end;
 
 /*-------------------------------- Global Data -------------------------------*/
 /* Xen init_ops implementation. */
-struct init_ops xen_init_ops = {
-	.parse_preload_data		= xen_pv_parse_preload_data,
+struct init_ops xen_legacy_init_ops = {
+	.parse_preload_data		= xen_legacy_pvh_parse_preload_data,
 	.early_clock_source_init	= xen_clock_init,
 	.early_delay			= xen_delay,
-	.parse_memmap			= xen_pv_parse_memmap,
+	.parse_memmap			= xen_pvh_parse_memmap,
 #ifdef SMP
 	.start_all_aps			= xen_pv_start_all_aps,
 #endif
 	.msi_init			= xen_msi_init,
 };
 
+struct init_ops xen_pvh_init_ops = {
+	.parse_preload_data		= xen_pvh_parse_preload_data,
+	.early_clock_source_init	= xen_clock_init,
+	.early_delay			= xen_delay,
+	.parse_memmap			= xen_pvh_parse_memmap,
+#ifdef SMP
+	.mp_bootaddress			= mp_bootaddress,
+	.start_all_aps			= native_start_all_aps,
+#endif
+	.msi_init			= msi_init,
+};
+
 static struct bios_smap xen_smap[MAX_E820_ENTRIES];
 
 static start_info_t *legacy_start_info;
+static struct hvm_start_info *start_info;
 
 /*----------------------- Legacy PVH start_info accessors --------------------*/
 static vm_paddr_t
@@ -179,7 +197,7 @@ struct hypervisor_info legacy_info = {
  * as similar as possible to what native FreeBSD init function expects.
  */
 uint64_t
-hammer_time_xen(start_info_t *si, uint64_t xenstack)
+hammer_time_xen_legacy(start_info_t *si, uint64_t xenstack)
 {
 	uint64_t physfree;
 	uint64_t *PT4 = (u_int64_t *)xenstack;
@@ -235,7 +253,7 @@ hammer_time_xen(start_info_t *si, uint64_t xenstack)
 	load_cr3(((uint64_t)&PT4[0]) - KERNBASE);
 
 	/* Set the hooks for early functions that diverge from bare metal */
-	init_ops = xen_init_ops;
+	init_ops = xen_legacy_init_ops;
 	apic_ops = xen_apic_ops;
 	hypervisor_info = legacy_info;
 
@@ -243,6 +261,85 @@ hammer_time_xen(start_info_t *si, uint64_t xenstack)
 	return (hammer_time(0, physfree));
 }
 
+uint64_t
+hammer_time_xen(vm_paddr_t start_info_paddr)
+{
+	struct hvm_modlist_entry *mod;
+	struct xen_add_to_physmap xatp;
+	uint64_t physfree;
+	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);
+	}
+
+	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",
+		    start_info->magic);
+		HYPERVISOR_shutdown(SHUTDOWN_crash);
+	}
+
+	/*
+	 * The hvm_start_into structure is always appended after loading
+	 * the kernel and modules.
+	 */
+	physfree = roundup2(start_info_paddr + PAGE_SIZE, PAGE_SIZE);
+
+	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;
+
+	/*
+	 * Init a static kenv using a free page. The contents will be filled
+	 * from the parse_preload_data hook.
+	 */
+	kenv = (void *)(physfree + KERNBASE);
+	physfree += PAGE_SIZE;
+	bzero(kenv, PAGE_SIZE);
+	init_static_kenv(kenv, PAGE_SIZE);
+
+	if (start_info->modlist_paddr != 0) {
+		if (start_info->modlist_paddr >= physfree) {
+			xc_printf(
+			    "ERROR: unexpected module list memory address\n");
+			HYPERVISOR_shutdown(SHUTDOWN_crash);
+		}
+		if (start_info->nr_modules == 0) {
+			xc_printf(
+			    "ERROR: modlist_paddr != 0 but nr_modules == 0\n");
+			HYPERVISOR_shutdown(SHUTDOWN_crash);
+		}
+		mod = (struct hvm_modlist_entry *)
+		    (vm_paddr_t)start_info->modlist_paddr + KERNBASE;
+		if (mod[0].paddr >= physfree) {
+			xc_printf("ERROR: unexpected module memory address\n");
+			HYPERVISOR_shutdown(SHUTDOWN_crash);
+		}
+	}
+
+	/* Set the hooks for early functions that diverge from bare metal */
+	init_ops = xen_pvh_init_ops;
+	hvm_start_flags = start_info->flags;
+
+	/* Now we can jump into the native init function */
+	return (hammer_time(0, physfree));
+}
+
 /*-------------------------------- PV specific -------------------------------*/
 #ifdef SMP
 static bool
@@ -318,27 +415,49 @@ xen_pv_start_all_aps(void)
 #endif /* SMP */
 
 /*
- * Functions to convert the "extra" parameters passed by Xen
- * into FreeBSD boot options.
+ * When booted as a PVH guest FreeBSD needs to avoid using the RSDP address
+ * hint provided by the loader because it points to the native set of ACPI
+ * tables instead of the ones crafted by Xen. The acpi.rsdp env variable is
+ * removed from kenv if present, and a new acpi.rsdp is added to kenv that
+ * points to the address of the Xen crafted RSDP.
  */
+static bool reject_option(const char *option)
+{
+	static const char *reject[] = {
+		"acpi.rsdp",
+	};
+	unsigned int i;
+
+	for (i = 0; i < nitems(reject); i++)
+		if (strncmp(option, reject[i], strlen(reject[i])) == 0)
+			return (true);
+
+	return (false);
+}
+
 static void
-xen_pv_set_env(void)
+xen_pvh_set_env(char *env, bool (*filter)(const char *))
 {
-	char *cmd_line_next, *cmd_line;
-	size_t env_size;
+	char *option;
 
-	cmd_line = legacy_start_info->cmd_line;
-	env_size = sizeof(legacy_start_info->cmd_line);
+	if (env == NULL)
+		return;
 
-	/* Skip leading spaces */
-	for (; isspace(*cmd_line) && (env_size != 0); cmd_line++)
-		env_size--;
+	option = env;
+	while (*option != 0) {
+		char *value;
 
-	/* Replace ',' with '\0' */
-	for (cmd_line_next = cmd_line; strsep(&cmd_line_next, ",") != NULL;)
-		;
+		if (filter != NULL && filter(option)) {
+			option += strlen(option) + 1;
+			continue;
+		}
 
-	init_static_kenv(cmd_line, 0);
+		value = option;
+		option = strsep(&value, "=");
+		if (kern_setenv(option, value) != 0)
+			xc_printf("unable to add kenv %s=%s\n", option, value);
+		option = value + strlen(value) + 1;
+	}
 }
 
 #ifdef DDB
@@ -349,27 +468,15 @@ xen_pv_set_env(void)
  * sys/kern/kern_ksyms.c CVS Revision 1.71.
  */
 static void
-xen_pv_parse_symtab(void)
+xen_pvh_parse_symtab(void)
 {
 	Elf_Ehdr *ehdr;
 	Elf_Shdr *shdr;
-	vm_offset_t sym_end;
 	uint32_t size;
 	int i, j;
 
 	size = end;
-	sym_end = legacy_start_info->mod_start != 0 ?
-	    legacy_start_info->mod_start : legacy_start_info->mfn_list;
 
-	/*
-	 * Make sure the size is right headed, sym_end is just a
-	 * high boundary, but at least allows us to fail earlier.
-	 */
-	if ((vm_offset_t)&end + size > sym_end) {
-		xc_printf("Unable to load ELF symtab: size mismatch\n");
-		return;
-	}
-
 	ehdr = (Elf_Ehdr *)(&end + 1);
 	if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) ||
 	    ehdr->e_ident[EI_CLASS] != ELF_TARG_CLASS ||
@@ -394,16 +501,14 @@ xen_pv_parse_symtab(void)
 		break;
 	}
 
-	if (ksymtab == 0 || kstrtab == 0) {
+	if (ksymtab == 0 || kstrtab == 0)
 		xc_printf(
     "Unable to load ELF symtab: could not find symtab or strtab\n");
-		return;
-	}
 }
 #endif
 
 static caddr_t
-xen_pv_parse_preload_data(u_int64_t modulep)
+xen_legacy_pvh_parse_preload_data(uint64_t modulep)
 {
 	caddr_t		 kmdp;
 	vm_ooffset_t	 off;
@@ -434,22 +539,82 @@ xen_pv_parse_preload_data(u_int64_t modulep)
 		envp = MD_FETCH(kmdp, MODINFOMD_ENVP, char *);
 		if (envp != NULL)
 			envp += off;
-		init_static_kenv(envp, 0);
+		xen_pvh_set_env(envp, NULL);
 	} else {
 		/* Parse the extra boot information given by Xen */
-		xen_pv_set_env();
-		boothowto |= boot_env_to_howto();
+		boot_parse_cmdline_delim(legacy_start_info->cmd_line, ",");
 		kmdp = NULL;
 	}
 
+	boothowto |= boot_env_to_howto();
+
 #ifdef DDB
-	xen_pv_parse_symtab();
+	xen_pvh_parse_symtab();
 #endif
 	return (kmdp);
 }
 
+static caddr_t
+xen_pvh_parse_preload_data(uint64_t modulep)
+{
+	caddr_t kmdp;
+	vm_ooffset_t off;
+	vm_paddr_t metadata;
+	char *envp;
+	char acpi_rsdp[19];
+
+	if (start_info->modlist_paddr != 0) {
+		struct hvm_modlist_entry *mod;
+
+		mod = (struct hvm_modlist_entry *)
+		    (start_info->modlist_paddr + KERNBASE);
+		preload_metadata = (caddr_t)(mod[0].paddr + KERNBASE);
+
+		kmdp = preload_search_by_type("elf kernel");
+		if (kmdp == NULL)
+			kmdp = preload_search_by_type("elf64 kernel");
+		KASSERT(kmdp != NULL, ("unable to find kernel"));
+
+		/*
+		 * Xen has relocated the metadata and the modules,
+		 * so we need to recalculate it's position. This is
+		 * done by saving the original modulep address and
+		 * then calculating the offset with mod_start,
+		 * which contains the relocated modulep address.
+		 */
+		metadata = MD_FETCH(kmdp, MODINFOMD_MODULEP, vm_paddr_t);
+		off = mod[0].paddr + KERNBASE - metadata;
+
+		preload_bootstrap_relocate(off);
+
+		boothowto = MD_FETCH(kmdp, MODINFOMD_HOWTO, int);
+		envp = MD_FETCH(kmdp, MODINFOMD_ENVP, char *);
+		if (envp != NULL)
+			envp += off;
+		xen_pvh_set_env(envp, reject_option);
+	} else {
+		/* Parse the extra boot information given by Xen */
+		if (start_info->cmdline_paddr != 0)
+			boot_parse_cmdline_delim(
+			    (char *)(start_info->cmdline_paddr + KERNBASE),
+			    ",");
+		kmdp = NULL;
+	}
+
+	boothowto |= boot_env_to_howto();
+
+	snprintf(acpi_rsdp, sizeof(acpi_rsdp), "%#" PRIx64,
+	    start_info->rsdp_paddr);
+	kern_setenv("acpi.rsdp", acpi_rsdp);
+
+#ifdef DDB
+	xen_pvh_parse_symtab();
+#endif
+	return (kmdp);
+}
+
 static void
-xen_pv_parse_memmap(caddr_t kmdp, vm_paddr_t *physmap, int *physmap_idx)
+xen_pvh_parse_memmap(caddr_t kmdp, vm_paddr_t *physmap, int *physmap_idx)
 {
 	struct xen_memory_map memmap;
 	u_int32_t size;
@@ -459,8 +624,12 @@ xen_pv_parse_memmap(caddr_t kmdp, vm_paddr_t *physmap,
 	memmap.nr_entries = MAX_E820_ENTRIES;
 	set_xen_guest_handle(memmap.buffer, xen_smap);
 	rc = HYPERVISOR_memory_op(XENMEM_memory_map, &memmap);
-	if (rc)
-		panic("unable to fetch Xen E820 memory map");
+	if (rc) {
+		xc_printf("ERROR: unable to fetch Xen E820 memory map: %d\n",
+		    rc);
+		HYPERVISOR_shutdown(SHUTDOWN_crash);
+	}
+
 	size = memmap.nr_entries * sizeof(xen_smap[0]);
 
 	bios_add_smap_entries(xen_smap, size, physmap, physmap_idx);

Modified: head/sys/xen/hvm.h
==============================================================================
--- head/sys/xen/hvm.h	Thu Jul 19 08:13:41 2018	(r336473)
+++ head/sys/xen/hvm.h	Thu Jul 19 08:44:52 2018	(r336474)
@@ -102,4 +102,7 @@ int xen_hvm_init_hypercall_stubs(enum xen_hvm_init_typ
 void xen_hvm_set_callback(device_t);
 void xen_hvm_suspend(void);
 void xen_hvm_resume(bool suspend_cancelled);
+
+extern uint32_t hvm_start_flags;
+
 #endif	/* __XEN_HVM_H__ */



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201807190844.w6J8iqTh084024>