From fec27f4d23b7335a72c4c6a4bb75e94e7500669a Mon Sep 17 00:00:00 2001 From: Liam Merwick Date: Tue, 20 Nov 2018 13:01:47 +0000 Subject: pvh: use x86/HVM direct boot ABI These changes (along with corresponding QEMU and Linux kernel changes) enable a guest to be booted using the x86/HVM direct boot ABI. QEMU parses the uncompressed kernel binary passed to it via -kernel to read the ELF Note which contains the address to be loaded. QEMU then depends on qboot to populate the start_info struct needed by the direct boot ABI and configure the guest e820 tables before jumping to the loaded kernel entry. Signed-off-by: George Kennedy Signed-off-by: Liam Merwick --- fw_cfg.c | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 70 insertions(+), 1 deletion(-) (limited to 'fw_cfg.c') diff --git a/fw_cfg.c b/fw_cfg.c index f5aac73..09bb0d3 100644 --- a/fw_cfg.c +++ b/fw_cfg.c @@ -6,8 +6,12 @@ #include "fw_cfg.h" #include "bswap.h" #include "linuxboot.h" +#include "memaccess.h" #include "multiboot.h" #include "benchmark.h" +#include "start_info.h" + +extern struct hvm_start_info start_info; struct fw_cfg_file { uint32_t size; @@ -184,6 +188,66 @@ static void boot_multiboot_from_fw_cfg(void) panic(); } +static void pvh_e820_setup() +{ + struct hvm_memmap_table_entry *pvh_e820p; + int i, pvh_e820_sz; + + pvh_e820_sz = sizeof(struct hvm_memmap_table_entry) * e820->nr_map; + + pvh_e820p = malloc(pvh_e820_sz); + memset(pvh_e820p, 0, pvh_e820_sz); + + for (i = 0; i < e820->nr_map; i++) { + pvh_e820p[i].addr = e820->map[i].addr; + pvh_e820p[i].size = e820->map[i].size; + pvh_e820p[i].type = e820->map[i].type; + } + start_info.memmap_paddr = (uintptr_t)pvh_e820p; + start_info.memmap_entries = e820->nr_map; +} + +static void boot_pvh_from_fw_cfg(void) +{ + void *kernel_entry; + uint32_t sz; + struct linuxboot_args args; + struct hvm_modlist_entry ramdisk_mod; + + start_info.magic = XEN_HVM_START_MAGIC_VALUE; + start_info.version = 1; + start_info.flags = 0; + start_info.nr_modules = 1; + start_info.reserved = 0; + + fw_cfg_select(FW_CFG_CMDLINE_SIZE); + args.cmdline_size = fw_cfg_readl_le(); + args.cmdline_addr = malloc(args.cmdline_size); + fw_cfg_read_entry(FW_CFG_CMDLINE_DATA, args.cmdline_addr, + args.cmdline_size); + start_info.cmdline_paddr = (uintptr_t)args.cmdline_addr; + + /* Use this field for pvhboot. Not used by pvhboot otherwise */ + fw_cfg_read_entry(FW_CFG_KERNEL_DATA, &ramdisk_mod, + sizeof(ramdisk_mod)); + ramdisk_mod.cmdline_paddr = (uintptr_t)&ramdisk_mod; + start_info.modlist_paddr = (uintptr_t)&ramdisk_mod; + + pvh_e820_setup(); + + fw_cfg_select(FW_CFG_KERNEL_SIZE); + sz = fw_cfg_readl_le(); + if (!sz) + panic(); + + fw_cfg_select(FW_CFG_KERNEL_ENTRY); + kernel_entry = (void *) fw_cfg_readl_le(); + + asm volatile("jmp *%2" : : "a" (0x2badb002), + "b"(&start_info), "c"(kernel_entry)); + panic(); +} + void boot_from_fwcfg(void) { struct linuxboot_args args; @@ -208,8 +272,13 @@ void boot_from_fwcfg(void) fw_cfg_select(FW_CFG_SETUP_DATA); fw_cfg_read(args.header, sizeof(args.header)); - if (!parse_bzimage(&args)) + if (!parse_bzimage(&args)) { + uint8_t *header = args.header; + + if (ldl_p(header) == 0x464c457f) /* ELF magic */ + boot_pvh_from_fw_cfg(); boot_multiboot_from_fw_cfg(); + } /* SETUP_DATA already selected */ if (args.setup_size > sizeof(args.header)) -- cgit v1.1