From: Marco Schlumpp Date: Thu, 9 Feb 2023 13:43:51 +0000 (+0100) Subject: plat/kvm/x86: Add an entry point for the firecracker hypervisor X-Git-Tag: RELEASE-0.13.0~40 X-Git-Url: http://xenbits.xensource.com/gitweb?a=commitdiff_plain;h=e85a0cd860e4166b26269785e229c6676b175a30;p=unikraft%2Funikraft.git plat/kvm/x86: Add an entry point for the firecracker hypervisor This is currently not conforming to the actual Linux boot protocol and only suitable for firecracker. The kernel image would have to include a proper Linux boot protocol header to allow loading with proper bootloaders such as the one in QEMU. Co-authored-by: Marc Rittinghaus Signed-off-by: Marco Schlumpp Reviewed-by: Sergiu Moga Approved-by: Marc Rittinghaus Tested-by: Unikraft CI GitHub-Closes: #760 --- diff --git a/plat/kvm/Config.uk b/plat/kvm/Config.uk index 8452edef2..ab789c50c 100644 --- a/plat/kvm/Config.uk +++ b/plat/kvm/Config.uk @@ -17,14 +17,21 @@ if (ARCH_X86_64) config KVM_BOOT_PROTO_MULTIBOOT bool +config KVM_BOOT_PROTO_LXBOOT + bool + choice - prompt "Hypervisor" - default KVM_HYPERVISOR_QEMU + prompt "Virtual Machine Monitor" + default KVM_VMM_QEMU -config KVM_HYPERVISOR_QEMU +config KVM_VMM_QEMU bool "QEMU" select KVM_BOOT_PROTO_MULTIBOOT +config KVM_VMM_FIRECRACKER + bool "Firecracker" + select KVM_BOOT_PROTO_LXBOOT + endchoice endif @@ -101,7 +108,7 @@ config KVM_MAX_IRQ_HANDLER_ENTRIES config KVM_PCI bool "PCI Bus Driver" default y - depends on (ARCH_X86_64 || ARCH_ARM_64) && KVM_HYPERVISOR_QEMU + depends on (ARCH_X86_64 || ARCH_ARM_64) && KVM_VMM_QEMU select LIBUKBUS help PCI bus driver for probing and operating PCI devices diff --git a/plat/kvm/Linker.uk b/plat/kvm/Linker.uk index c1ddc84a3..347c9f7b9 100644 --- a/plat/kvm/Linker.uk +++ b/plat/kvm/Linker.uk @@ -3,6 +3,8 @@ ifeq ($(CONFIG_KVM_BOOT_PROTO_MULTIBOOT),y) KVM_LDFLAGS-y += -Wl,-m,elf_x86_64 KVM_LDFLAGS-y += -Wl,--entry=_multiboot_entry KVM_STRIPFLAGS += --output-target elf32-i386 +else ifeq ($(CONFIG_KVM_BOOT_PROTO_LXBOOT),y) +KVM_LDFLAGS-y += -Wl,--entry=_lxboot_entry endif else ifeq (arm64,$(CONFIG_UK_ARCH)) KVM_LDFLAGS-y += -Wl,-m,aarch64elf diff --git a/plat/kvm/Makefile.uk b/plat/kvm/Makefile.uk index 120ba54f5..6f4d37b83 100644 --- a/plat/kvm/Makefile.uk +++ b/plat/kvm/Makefile.uk @@ -63,6 +63,9 @@ LIBKVMPLAT_SRCS-$(CONFIG_ARCH_X86_64) += $(UK_PLAT_COMMON_BASE)/bootinfo.lds.S|c ifeq ($(CONFIG_KVM_BOOT_PROTO_MULTIBOOT),y) LIBKVMPLAT_SRCS-$(CONFIG_ARCH_X86_64) += $(LIBKVMPLAT_BASE)/x86/multiboot.S|x86 LIBKVMPLAT_SRCS-$(CONFIG_ARCH_X86_64) += $(LIBKVMPLAT_BASE)/x86/multiboot.c +else ifeq ($(CONFIG_KVM_BOOT_PROTO_LXBOOT),y) +LIBKVMPLAT_SRCS-$(CONFIG_ARCH_X86_64) += $(LIBKVMPLAT_BASE)/x86/lxboot.S|x86 +LIBKVMPLAT_SRCS-$(CONFIG_ARCH_X86_64) += $(LIBKVMPLAT_BASE)/x86/lxboot.c endif LIBKVMPLAT_SRCS-$(CONFIG_ARCH_X86_64) += $(LIBKVMPLAT_BASE)/x86/pagetable64.S LIBKVMPLAT_SRCS-$(CONFIG_ARCH_X86_64) += $(LIBKVMPLAT_BASE)/x86/traps.c diff --git a/plat/kvm/x86/lxboot.S b/plat/kvm/x86/lxboot.S new file mode 100644 index 000000000..8d0a34f39 --- /dev/null +++ b/plat/kvm/x86/lxboot.S @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* Copyright (c) 2022, Unikraft GmbH and The Unikraft Authors. + * Licensed under the BSD-3-Clause License (the "License"). + * You may not use this file except in compliance with the License. + */ + +#include +#include + +#include + +/** + * Stack and entry function to use during CPU initialization + */ +.section .bss +.space 4096 +lcpu_bootstack: + +.section .rodata +lcpu_boot_startup_args: + .quad lxboot_entry + .quad lcpu_bootstack + +/** + * 64-bit Linux Boot Protocol entry function + * + * RSI holds base address of struct lxboot_params. Flat 4GiB CS and DS segments, + * with ES and SS set to DS. A20 gate enabled. Protected mode enabled, paging + * enabled with ident mapping for loaded kernel, zero page (i.e., struct + * lxboot_params) and command line buffer. Interrupts disabled. + */ +.code64 +.section .text.64.boot +ENTRY(_lxboot_entry) + cmpl $LXBOOT_HDR_HEADER_MAGIC, LXBOOT_HDR_HEADER_OFFSET(%rsi) + jne no_lxboot + + movq $lcpu_boot_startup_args, %rdi /* startup args for boot CPU */ + + movq $x86_bpt_pml4, %rax + movq %rax, %cr3 + + jmp lcpu_start64 + +no_lxboot: + cli +1: + hlt + jmp 1b +END(_lxboot_entry) diff --git a/plat/kvm/x86/lxboot.c b/plat/kvm/x86/lxboot.c new file mode 100644 index 000000000..03b35f4ef --- /dev/null +++ b/plat/kvm/x86/lxboot.c @@ -0,0 +1,160 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* Copyright (c) 2022, Unikraft GmbH and The Unikraft Authors. + * Licensed under the BSD-3-Clause License (the "License"). + * You may not use this file except in compliance with the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define lxboot_crash(rc, msg, ...) ukplat_crash() + +void _ukplat_entry(struct lcpu *lcpu, struct ukplat_bootinfo *bi); + +static void +lxboot_init_cmdline(struct ukplat_bootinfo *bi, struct lxboot_params *bp) +{ + struct ukplat_memregion_desc mrd = {0}; + __u64 cmdline_addr; + __sz cmdline_size; + int rc; + + cmdline_addr = bp->ext_cmd_line_ptr; + cmdline_addr <<= 32; + cmdline_addr |= bp->hdr.cmd_line_ptr; + + cmdline_size = bp->hdr.cmd_line_size; + + if (unlikely(cmdline_addr == 0)) + lxboot_crash(-EINVAL, "Command line address is zero"); + + if (cmdline_size == 0) + return; + + mrd.pbase = cmdline_addr; + mrd.vbase = cmdline_addr; + mrd.len = cmdline_size; + mrd.type = UKPLAT_MEMRT_CMDLINE; + mrd.flags = UKPLAT_MEMRF_READ | UKPLAT_MEMRF_MAP; +#ifdef CONFIG_UKPLAT_MEMRNAME + memcpy(mrd.name, "cmdline", sizeof("cmdline")); +#endif /* CONFIG_UKPLAT_MEMRNAME */ + + rc = ukplat_memregion_list_insert(&bi->mrds, &mrd); + if (unlikely(rc < 0)) + lxboot_crash(rc, "Unable to add ram mapping"); + + bi->cmdline = cmdline_addr; + bi->cmdline_len = cmdline_size; +} + +static void +lxboot_init_initrd(struct ukplat_bootinfo *bi, struct lxboot_params *bp) +{ + __u64 initrd_addr; + __sz initrd_size; + struct ukplat_memregion_desc mrd = {0}; + int rc; + + initrd_addr = bp->ext_ramdisk_image; + initrd_addr <<= 32; + initrd_addr |= bp->hdr.ramdisk_image; + + initrd_size = bp->ext_ramdisk_size; + initrd_size <<= 32; + initrd_size |= bp->hdr.ramdisk_size; + + if (initrd_addr == 0 || initrd_size == 0) + return; + + mrd.type = UKPLAT_MEMRT_INITRD; + mrd.flags = UKPLAT_MEMRF_MAP | UKPLAT_MEMRF_READ; + mrd.vbase = initrd_addr; + mrd.pbase = initrd_addr; + mrd.len = initrd_size; +#ifdef CONFIG_UKPLAT_MEMRNAME + memcpy(mrd.name, "initrd", sizeof("initrd")); +#endif /* CONFIG_UKPLAT_MEMRNAME */ + + rc = ukplat_memregion_list_insert(&bi->mrds, &mrd); + if (unlikely(rc < 0)) + lxboot_crash(rc, "Unable to add ram mapping"); +} + +static void +lxboot_init_mem(struct ukplat_bootinfo *bi, struct lxboot_params *bp) +{ + struct ukplat_memregion_desc mrd = {0}; + struct lxboot_e820_entry *entry; + __u64 start, end; + int rc; + __s8 i; + + for (i = 0; i < bp->e820_entries; i++) { + entry = &bp->e820_table[i]; + + /* Kludge: Don't add zero-page, because the platform code does + * not handle address zero well. + */ + start = MAX(entry->addr, PAGE_SIZE); + /* Also make sure to not insert unaligned memory regions */ + start = PAGE_ALIGN_UP(start); + + end = entry->addr + entry->size; + + if (end <= start) + continue; + + mrd.pbase = start; + mrd.vbase = start; /* 1:1 mapping */ + mrd.len = end - start; + + if (entry->type == LXBOOT_E820_TYPE_RAM) { + mrd.type = UKPLAT_MEMRT_FREE; + mrd.flags = UKPLAT_MEMRF_READ | UKPLAT_MEMRF_WRITE; + + rc = ukplat_memregion_list_insert_split_phys( + &bi->mrds, &mrd, __PAGE_SIZE); + if (unlikely(rc < 0)) + lxboot_crash(rc, "Unable to add ram mapping"); + } else { + mrd.type = UKPLAT_MEMRT_RESERVED; + mrd.flags = UKPLAT_MEMRF_READ | UKPLAT_MEMRF_MAP; + + /* We assume that reserved regions cannot + * overlap with loaded modules. + */ + rc = ukplat_memregion_list_insert(&bi->mrds, &mrd); + if (unlikely(rc < 0)) + lxboot_crash(rc, "Unable to add ram mapping"); + } + } +} + +void lxboot_entry(struct lcpu *lcpu, struct lxboot_params *bp) +{ + struct ukplat_bootinfo *bi; + + bi = ukplat_bootinfo_get(); + if (unlikely(!bi)) + lxboot_crash(-EINVAL, "Incompatible or corrupted bootinfo"); + + if (unlikely(bp->hdr.boot_flag != 0xAA55)) + lxboot_crash(-EINVAL, "Invalid magic number"); + + lxboot_init_cmdline(bi, bp); + lxboot_init_initrd(bi, bp); + lxboot_init_mem(bi, bp); + + memcpy(bi->bootprotocol, "lxboot", sizeof("lxboot")); + + _ukplat_entry(lcpu, bi); +}