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
This commit is contained in:
parent
07c2711fbf
commit
b0663c33c2
@ -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 */
|
||||
|
||||
|
@ -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 = {
|
||||
|
263
sys/x86/xen/pv.c
263
sys/x86/xen/pv.c
@ -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 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);
|
||||
|
@ -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__ */
|
||||
|
Loading…
Reference in New Issue
Block a user