]> xenbits.xensource.com Git - unikraft/unikraft.git/commitdiff
plat/kvm/x86: Add an entry point for the firecracker hypervisor
authorMarco Schlumpp <marco@unikraft.io>
Thu, 9 Feb 2023 13:43:51 +0000 (14:43 +0100)
committerUnikraft <monkey@unikraft.io>
Mon, 8 May 2023 19:49:16 +0000 (19:49 +0000)
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 <marc@unikraft.io>
Signed-off-by: Marco Schlumpp <marco@unikraft.io>
Reviewed-by: Sergiu Moga <sergiu.moga@protonmail.com>
Approved-by: Marc Rittinghaus <marc.rittinghaus@unikraft.io>
Tested-by: Unikraft CI <monkey@unikraft.io>
GitHub-Closes: #760

plat/kvm/Config.uk
plat/kvm/Linker.uk
plat/kvm/Makefile.uk
plat/kvm/x86/lxboot.S [new file with mode: 0644]
plat/kvm/x86/lxboot.c [new file with mode: 0644]

index 8452edef2ef8a4b9fba60a0c2f7d9c2237438ba9..ab789c50c9b435cf9e22ddc55a0c3d2ec4a1e719 100644 (file)
@@ -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
index c1ddc84a3a36ceda68307f22616b5cbe8489e3f6..347c9f7b970e6b6e005928ab4639f1980a5e291e 100644 (file)
@@ -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
index 120ba54f5135f8ca7ff616a260f837d7f23bce28..6f4d37b8315f44ede66d9bbeb66db069ec6e975e 100644 (file)
@@ -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 (file)
index 0000000..8d0a34f
--- /dev/null
@@ -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 <uk/config.h>
+#include <uk/asm.h>
+
+#include <kvm-x86/lxboot.h>
+
+/**
+ * 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 (file)
index 0000000..03b35f4
--- /dev/null
@@ -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 <uk/essentials.h>
+#include <uk/arch/limits.h>
+#include <uk/arch/types.h>
+#include <uk/arch/paging.h>
+#include <uk/plat/bootstrap.h>
+#include <uk/plat/common/bootinfo.h>
+#include <uk/plat/common/lcpu.h>
+#include <uk/plat/common/memory.h>
+#include <uk/plat/common/sections.h>
+#include <kvm-x86/lxboot.h>
+
+#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);
+}