diff --git a/sys/amd64/amd64/xen-locore.S b/sys/amd64/amd64/xen-locore.S index 899499adda60..2124c532539c 100644 --- a/sys/amd64/amd64/xen-locore.S +++ b/sys/amd64/amd64/xen-locore.S @@ -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 */ diff --git a/sys/x86/xen/hvm.c b/sys/x86/xen/hvm.c index f8c886ec0fab..24b5f1d2e72e 100644 --- a/sys/x86/xen/hvm.c +++ b/sys/x86/xen/hvm.c @@ -82,6 +82,12 @@ static MALLOC_DEFINE(M_XENHVM, "xen_hvm", "Xen HVM PV Support"); */ 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 = { diff --git a/sys/x86/xen/pv.c b/sys/x86/xen/pv.c index 2963e1fbc011..62bccee09a37 100644 --- a/sys/x86/xen/pv.c +++ b/sys/x86/xen/pv.c @@ -58,6 +58,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include #include #include @@ -67,11 +68,13 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include #include #include #include +#include #include #include @@ -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 void -xen_pv_set_env(void) +static bool reject_option(const char *option) { - char *cmd_line_next, *cmd_line; - size_t env_size; + static const char *reject[] = { + "acpi.rsdp", + }; + unsigned int i; - cmd_line = legacy_start_info->cmd_line; - env_size = sizeof(legacy_start_info->cmd_line); + for (i = 0; i < nitems(reject); i++) + if (strncmp(option, reject[i], strlen(reject[i])) == 0) + return (true); - /* Skip leading spaces */ - for (; isspace(*cmd_line) && (env_size != 0); cmd_line++) - env_size--; + return (false); +} - /* Replace ',' with '\0' */ - for (cmd_line_next = cmd_line; strsep(&cmd_line_next, ",") != NULL;) - ; +static void +xen_pvh_set_env(char *env, bool (*filter)(const char *)) +{ + char *option; - init_static_kenv(cmd_line, 0); + if (env == NULL) + return; + + option = env; + while (*option != 0) { + char *value; + + if (filter != NULL && filter(option)) { + option += strlen(option) + 1; + continue; + } + + 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,26 +468,14 @@ 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) || @@ -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, int *physmap_idx) 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); diff --git a/sys/xen/hvm.h b/sys/xen/hvm.h index 055ba4ffe752..bc7518d26575 100644 --- a/sys/xen/hvm.h +++ b/sys/xen/hvm.h @@ -102,4 +102,7 @@ int xen_hvm_init_hypercall_stubs(enum xen_hvm_init_type); 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__ */