diff options
author | Liam Merwick <liam.merwick@oracle.com> | 2018-11-20 13:01:47 +0000 |
---|---|---|
committer | Paolo Bonzini <bonzini@gnu.org> | 2018-12-28 14:31:29 +0100 |
commit | fec27f4d23b7335a72c4c6a4bb75e94e7500669a (patch) | |
tree | 51186d1df58fec3a27874708888bc58fccdf5089 /fw_cfg.c | |
parent | 281da37f5907f48b10c62622551446e8f14aea09 (diff) | |
download | qboot-fec27f4d23b7335a72c4c6a4bb75e94e7500669a.zip qboot-fec27f4d23b7335a72c4c6a4bb75e94e7500669a.tar.gz qboot-fec27f4d23b7335a72c4c6a4bb75e94e7500669a.tar.bz2 |
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 <George.Kennedy@oracle.com>
Signed-off-by: Liam Merwick <Liam.Merwick@oracle.com>
Diffstat (limited to 'fw_cfg.c')
-rw-r--r-- | fw_cfg.c | 71 |
1 files changed, 70 insertions, 1 deletions
@@ -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)) |