svn commit: r336474 - in head/sys: amd64/amd64 x86/xen xen
Roger Pau Monné
royger at FreeBSD.org
Thu Jul 19 08:44:54 UTC 2018
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__ */
More information about the svn-src-head
mailing list