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__ */
_______________________________________________
svn-src-head@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to