Support booting from compressed kernel images.
authordv <dv@openbsd.org>
Mon, 5 Apr 2021 18:09:48 +0000 (18:09 +0000)
committerdv <dv@openbsd.org>
Mon, 5 Apr 2021 18:09:48 +0000 (18:09 +0000)
The bsd.rd ramdisk now ships gzip'd on amd64. Use libz in base to
transparently handle decompression of any compressed kernel images.

Patch from Josh Rickmar.

ok kn@

usr.sbin/vmd/Makefile
usr.sbin/vmd/loadfile.h
usr.sbin/vmd/loadfile_elf.c
usr.sbin/vmd/vm.c
usr.sbin/vmd/vmd.h

index 132221f..0abaf00 100644 (file)
@@ -1,4 +1,4 @@
-#      $OpenBSD: Makefile,v 1.25 2021/03/19 09:29:33 kn Exp $
+#      $OpenBSD: Makefile,v 1.26 2021/04/05 18:09:48 dv Exp $
 
 .if ${MACHINE} == "amd64"
 
@@ -14,8 +14,8 @@ CFLAGS+=      -Wmissing-declarations
 CFLAGS+=       -Wshadow -Wpointer-arith -Wcast-qual
 CFLAGS+=       -Wsign-compare
 
-LDADD+=                -lutil -lpthread -levent
-DPADD+=                ${LIBUTIL} ${LIBPTHREAD} ${LIBEVENT}
+LDADD+=                -lutil -lpthread -levent -lz
+DPADD+=                ${LIBUTIL} ${LIBPTHREAD} ${LIBEVENT} ${LIBZ}
 
 YFLAGS=
 
index 43b79cf..fcc7464 100644 (file)
@@ -1,5 +1,5 @@
 /*     $NetBSD: loadfile.h,v 1.1 1999/04/28 09:08:50 christos Exp $     */
-/*     $OpenBSD: loadfile.h,v 1.13 2021/03/19 09:29:33 kn Exp $         */
+/*     $OpenBSD: loadfile.h,v 1.14 2021/04/05 18:09:48 dv Exp $         */
 
 /*-
  * Copyright (c) 1998 The NetBSD Foundation, Inc.
@@ -30,6 +30,8 @@
  * POSSIBILITY OF SUCH DAMAGE.
  */
 
+#include <zlib.h>
+
 /*
  * Array indices in the u_long position array
  */
@@ -73,6 +75,6 @@
 #define PML2_PAGE 0x13000
 #define NPTE_PG (PAGE_SIZE / sizeof(uint64_t))
 
-int loadfile_elf(FILE *, struct vm_create_params *, struct vcpu_reg_state *);
+int loadfile_elf(gzFile, struct vm_create_params *, struct vcpu_reg_state *);
 
-size_t mread(FILE *, paddr_t, size_t);
+size_t mread(gzFile, paddr_t, size_t);
index 8485ac5..8a18cb3 100644 (file)
@@ -1,5 +1,5 @@
 /* $NetBSD: loadfile.c,v 1.10 2000/12/03 02:53:04 tsutsui Exp $ */
-/* $OpenBSD: loadfile_elf.c,v 1.37 2021/03/19 09:29:33 kn Exp $ */
+/* $OpenBSD: loadfile_elf.c,v 1.38 2021/04/05 18:09:48 dv Exp $ */
 
 /*-
  * Copyright (c) 1997 The NetBSD Foundation, Inc.
@@ -115,8 +115,8 @@ union {
 
 static void setsegment(struct mem_segment_descriptor *, uint32_t,
     size_t, int, int, int, int);
-static int elf32_exec(FILE *, Elf32_Ehdr *, u_long *, int);
-static int elf64_exec(FILE *, Elf64_Ehdr *, u_long *, int);
+static int elf32_exec(gzFile, Elf32_Ehdr *, u_long *, int);
+static int elf64_exec(gzFile, Elf64_Ehdr *, u_long *, int);
 static size_t create_bios_memmap(struct vm_create_params *, bios_memmap_t *);
 static uint32_t push_bootargs(bios_memmap_t *, size_t);
 static size_t push_stack(uint32_t, uint32_t);
@@ -260,10 +260,11 @@ push_pt_64(void)
  *
  * Return values:
  *  0 if successful
- *  various error codes returned from read(2) or loadelf functions
+ *  various error codes returned from gzread(3) or loadelf functions
  */
 int
-loadfile_elf(FILE *fp, struct vm_create_params *vcp, struct vcpu_reg_state *vrs)
+loadfile_elf(gzFile fp, struct vm_create_params *vcp,
+    struct vcpu_reg_state *vrs)
 {
        int r, is_i386 = 0;
        uint32_t bootargsz;
@@ -271,7 +272,7 @@ loadfile_elf(FILE *fp, struct vm_create_params *vcp, struct vcpu_reg_state *vrs)
        u_long marks[MARK_MAX];
        bios_memmap_t memmap[VMM_MAX_MEM_RANGES + 1];
 
-       if ((r = fread(&hdr, 1, sizeof(hdr), fp)) != sizeof(hdr))
+       if ((r = gzread(fp, &hdr, sizeof(hdr))) != sizeof(hdr))
                return 1;
 
        memset(&marks, 0, sizeof(marks));
@@ -471,7 +472,7 @@ push_stack(uint32_t bootargsz, uint32_t end)
  * into the guest address space at paddr 'addr'.
  *
  * Parameters:
- *  fd: file descriptor of the kernel image file to read from.
+ *  fp: kernel image file to read from.
  *  addr: guest paddr_t to load to
  *  sz: number of bytes to load
  *
@@ -479,7 +480,7 @@ push_stack(uint32_t bootargsz, uint32_t end)
  *  returns 'sz' if successful, or 0 otherwise.
  */
 size_t
-mread(FILE *fp, paddr_t addr, size_t sz)
+mread(gzFile fp, paddr_t addr, size_t sz)
 {
        size_t ct;
        size_t i, rd, osz;
@@ -499,7 +500,7 @@ mread(FILE *fp, paddr_t addr, size_t sz)
                else
                        ct = sz;
 
-               if (fread(buf, 1, ct, fp) != ct) {
+               if ((size_t)gzread(fp, buf, ct) != ct) {
                        log_warn("%s: error %d in mread", __progname, errno);
                        return (0);
                }
@@ -523,7 +524,7 @@ mread(FILE *fp, paddr_t addr, size_t sz)
                else
                        ct = PAGE_SIZE;
 
-               if (fread(buf, 1, ct, fp) != ct) {
+               if ((size_t)gzread(fp, buf, ct) != ct) {
                        log_warn("%s: error %d in mread", __progname, errno);
                        return (0);
                }
@@ -628,13 +629,13 @@ mbcopy(void *src, paddr_t dst, int sz)
 /*
  * elf64_exec
  *
- * Load the kernel indicated by 'fd' into the guest physical memory
+ * Load the kernel indicated by 'fp' into the guest physical memory
  * space, at the addresses defined in the ELF header.
  *
  * This function is used for 64 bit kernels.
  *
  * Parameters:
- *  fd: file descriptor of the kernel to load
+ *  fp: kernel image file to load
  *  elf: ELF header of the kernel
  *  marks: array to store the offsets of various kernel structures
  *      (start, bss, etc)
@@ -646,7 +647,7 @@ mbcopy(void *src, paddr_t dst, int sz)
  *  1 if unsuccessful
  */
 static int
-elf64_exec(FILE *fp, Elf64_Ehdr *elf, u_long *marks, int flags)
+elf64_exec(gzFile fp, Elf64_Ehdr *elf, u_long *marks, int flags)
 {
        Elf64_Shdr *shp;
        Elf64_Phdr *phdr;
@@ -661,12 +662,12 @@ elf64_exec(FILE *fp, Elf64_Ehdr *elf, u_long *marks, int flags)
        sz = elf->e_phnum * sizeof(Elf64_Phdr);
        phdr = malloc(sz);
 
-       if (fseeko(fp, (off_t)elf->e_phoff, SEEK_SET) == -1)  {
+       if (gzseek(fp, (off_t)elf->e_phoff, SEEK_SET) == -1)  {
                free(phdr);
                return 1;
        }
 
-       if (fread(phdr, 1, sz, fp) != sz) {
+       if ((size_t)gzread(fp, phdr, sz) != sz) {
                free(phdr);
                return 1;
        }
@@ -706,7 +707,7 @@ elf64_exec(FILE *fp, Elf64_Ehdr *elf, u_long *marks, int flags)
                    (IS_DATA(phdr[i]) && (flags & LOAD_DATA))) {
 
                        /* Read in segment. */
-                       if (fseeko(fp, (off_t)phdr[i].p_offset,
+                       if (gzseek(fp, (off_t)phdr[i].p_offset,
                            SEEK_SET) == -1) {
                                free(phdr);
                                return 1;
@@ -751,14 +752,14 @@ elf64_exec(FILE *fp, Elf64_Ehdr *elf, u_long *marks, int flags)
                maxp += sizeof(Elf64_Ehdr);
 
        if (flags & (LOAD_SYM | COUNT_SYM)) {
-               if (fseeko(fp, (off_t)elf->e_shoff, SEEK_SET) == -1)  {
-                       warn("lseek section headers");
+               if (gzseek(fp, (off_t)elf->e_shoff, SEEK_SET) == -1) {
+                       warn("gzseek section headers");
                        return 1;
                }
                sz = elf->e_shnum * sizeof(Elf64_Shdr);
                shp = malloc(sz);
 
-               if (fread(shp, 1, sz, fp) != sz) {
+               if ((size_t)gzread(fp, shp, sz) != sz) {
                        free(shp);
                        return 1;
                }
@@ -768,13 +769,13 @@ elf64_exec(FILE *fp, Elf64_Ehdr *elf, u_long *marks, int flags)
 
                size_t shstrsz = shp[elf->e_shstrndx].sh_size;
                char *shstr = malloc(shstrsz);
-               if (fseeko(fp, (off_t)shp[elf->e_shstrndx].sh_offset,
+               if (gzseek(fp, (off_t)shp[elf->e_shstrndx].sh_offset,
                    SEEK_SET) == -1) {
                        free(shstr);
                        free(shp);
                        return 1;
                }
-               if (fread(shstr, 1, shstrsz, fp) != shstrsz) {
+               if ((size_t)gzread(fp, shstr, shstrsz) != shstrsz) {
                        free(shstr);
                        free(shp);
                        return 1;
@@ -797,7 +798,7 @@ elf64_exec(FILE *fp, Elf64_Ehdr *elf, u_long *marks, int flags)
                            !strcmp(shstr + shp[i].sh_name, ".debug_line") ||
                            !strcmp(shstr + shp[i].sh_name, ELF_CTF)) {
                                if (havesyms && (flags & LOAD_SYM)) {
-                                       if (fseeko(fp, (off_t)shp[i].sh_offset,
+                                       if (gzseek(fp, (off_t)shp[i].sh_offset,
                                            SEEK_SET) == -1) {
                                                free(shstr);
                                                free(shp);
@@ -850,13 +851,13 @@ elf64_exec(FILE *fp, Elf64_Ehdr *elf, u_long *marks, int flags)
 /*
  * elf32_exec
  *
- * Load the kernel indicated by 'fd' into the guest physical memory
+ * Load the kernel indicated by 'fp' into the guest physical memory
  * space, at the addresses defined in the ELF header.
  *
  * This function is used for 32 bit kernels.
  *
  * Parameters:
- *  fd: file descriptor of the kernel to load
+ *  fp: kernel image file to load
  *  elf: ELF header of the kernel
  *  marks: array to store the offsets of various kernel structures
  *      (start, bss, etc)
@@ -868,7 +869,7 @@ elf64_exec(FILE *fp, Elf64_Ehdr *elf, u_long *marks, int flags)
  *  1 if unsuccessful
  */
 static int
-elf32_exec(FILE *fp, Elf32_Ehdr *elf, u_long *marks, int flags)
+elf32_exec(gzFile fp, Elf32_Ehdr *elf, u_long *marks, int flags)
 {
        Elf32_Shdr *shp;
        Elf32_Phdr *phdr;
@@ -883,12 +884,12 @@ elf32_exec(FILE *fp, Elf32_Ehdr *elf, u_long *marks, int flags)
        sz = elf->e_phnum * sizeof(Elf32_Phdr);
        phdr = malloc(sz);
 
-       if (fseeko(fp, (off_t)elf->e_phoff, SEEK_SET) == -1)  {
+       if (gzseek(fp, (off_t)elf->e_phoff, SEEK_SET) == -1)  {
                free(phdr);
                return 1;
        }
 
-       if (fread(phdr, 1, sz, fp) != sz) {
+       if ((size_t)gzread(fp, phdr, sz) != sz) {
                free(phdr);
                return 1;
        }
@@ -928,7 +929,7 @@ elf32_exec(FILE *fp, Elf32_Ehdr *elf, u_long *marks, int flags)
                    (IS_DATA(phdr[i]) && (flags & LOAD_DATA))) {
 
                        /* Read in segment. */
-                       if (fseeko(fp, (off_t)phdr[i].p_offset,
+                       if (gzseek(fp, (off_t)phdr[i].p_offset,
                            SEEK_SET) == -1) {
                                free(phdr);
                                return 1;
@@ -973,14 +974,14 @@ elf32_exec(FILE *fp, Elf32_Ehdr *elf, u_long *marks, int flags)
                maxp += sizeof(Elf32_Ehdr);
 
        if (flags & (LOAD_SYM | COUNT_SYM)) {
-               if (fseeko(fp, (off_t)elf->e_shoff, SEEK_SET) == -1)  {
+               if (gzseek(fp, (off_t)elf->e_shoff, SEEK_SET) == -1) {
                        warn("lseek section headers");
                        return 1;
                }
                sz = elf->e_shnum * sizeof(Elf32_Shdr);
                shp = malloc(sz);
 
-               if (fread(shp, 1, sz, fp) != sz) {
+               if ((size_t)gzread(fp, shp, sz) != sz) {
                        free(shp);
                        return 1;
                }
@@ -990,13 +991,13 @@ elf32_exec(FILE *fp, Elf32_Ehdr *elf, u_long *marks, int flags)
 
                size_t shstrsz = shp[elf->e_shstrndx].sh_size;
                char *shstr = malloc(shstrsz);
-               if (fseeko(fp, (off_t)shp[elf->e_shstrndx].sh_offset,
+               if (gzseek(fp, (off_t)shp[elf->e_shstrndx].sh_offset,
                    SEEK_SET) == -1) {
                        free(shstr);
                        free(shp);
                        return 1;
                }
-               if (fread(shstr, 1, shstrsz, fp) != shstrsz) {
+               if ((size_t)gzread(fp, shstr, shstrsz) != shstrsz) {
                        free(shstr);
                        free(shp);
                        return 1;
@@ -1018,7 +1019,7 @@ elf32_exec(FILE *fp, Elf32_Ehdr *elf, u_long *marks, int flags)
                            shp[i].sh_type == SHT_STRTAB ||
                            !strcmp(shstr + shp[i].sh_name, ".debug_line")) {
                                if (havesyms && (flags & LOAD_SYM)) {
-                                       if (fseeko(fp, (off_t)shp[i].sh_offset,
+                                       if (gzseek(fp, (off_t)shp[i].sh_offset,
                                            SEEK_SET) == -1) {
                                                free(shstr);
                                                free(shp);
index 9ffd159..58f4411 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: vm.c,v 1.61 2021/03/29 23:37:01 dv Exp $      */
+/*     $OpenBSD: vm.c,v 1.62 2021/04/05 18:09:48 dv Exp $      */
 
 /*
  * Copyright (c) 2015 Mike Larkin <mlarkin@openbsd.org>
@@ -21,6 +21,7 @@
 #include <sys/queue.h>
 #include <sys/wait.h>
 #include <sys/uio.h>
+#include <sys/stat.h>
 #include <sys/socket.h>
 #include <sys/time.h>
 #include <sys/mman.h>
@@ -84,7 +85,7 @@ void vcpu_exit_inout(struct vm_run_params *);
 int vcpu_exit_eptviolation(struct vm_run_params *);
 uint8_t vcpu_exit_pci(struct vm_run_params *);
 int vcpu_pic_intr(uint32_t, uint32_t, uint8_t);
-int loadfile_bios(FILE *, struct vcpu_reg_state *);
+int loadfile_bios(gzFile, off_t, struct vcpu_reg_state *);
 int send_vm(int, struct vm_create_params *);
 int dump_send_header(int);
 int dump_vmr(int , struct vm_mem_range *);
@@ -213,6 +214,7 @@ static const struct vcpu_reg_state vcpu_init_flat16 = {
  *
  * Parameters:
  *  fp: file of a kernel file to load
+ *  size: uncompressed size of the image
  *  (out) vrs: register state to set on init for this kernel
  *
  * Return values:
@@ -220,16 +222,15 @@ static const struct vcpu_reg_state vcpu_init_flat16 = {
  *  various error codes returned from read(2) or loadelf functions
  */
 int
-loadfile_bios(FILE *fp, struct vcpu_reg_state *vrs)
+loadfile_bios(gzFile fp, off_t size, struct vcpu_reg_state *vrs)
 {
-       off_t    size, off;
+       off_t    off;
 
        /* Set up a "flat 16 bit" register state for BIOS */
        memcpy(vrs, &vcpu_init_flat16, sizeof(*vrs));
 
-       /* Get the size of the BIOS image and seek to the beginning */
-       if (fseeko(fp, 0, SEEK_END) == -1 || (size = ftello(fp)) == -1 ||
-           fseeko(fp, 0, SEEK_SET) == -1)
+       /* Seek to the beginning of the BIOS image */
+       if (gzseek(fp, 0, SEEK_SET) == -1)
                return (-1);
 
        /* The BIOS image must end at 1M */
@@ -277,9 +278,10 @@ start_vm(struct vmd_vm *vm, int fd)
        struct vcpu_reg_state    vrs;
        int                      nicfds[VMM_MAX_NICS_PER_VM];
        int                      ret;
-       FILE                    *fp;
+       gzFile                   fp;
        size_t                   i;
        struct vm_rwregs_params  vrp;
+       struct stat              sb;
 
        /* Child */
        setproctitle("%s", vcp->vcp_name);
@@ -331,7 +333,7 @@ start_vm(struct vmd_vm *vm, int fd)
                memcpy(&vrs, &vcpu_init_flat64, sizeof(vrs));
 
                /* Find and open kernel image */
-               if ((fp = fdopen(vm->vm_kernel, "r")) == NULL)
+               if ((fp = gzdopen(vm->vm_kernel, "r")) == NULL)
                        fatalx("failed to open kernel - exiting");
 
                /* Load kernel image */
@@ -339,16 +341,16 @@ start_vm(struct vmd_vm *vm, int fd)
 
                /*
                 * Try BIOS as a fallback (only if it was provided as an image
-                * with vm->vm_kernel and not loaded from the disk)
+                * with vm->vm_kernel and the file is not compressed)
                 */
-               if (ret && errno == ENOEXEC && vm->vm_kernel != -1)
-                       ret = loadfile_bios(fp, &vrs);
+               if (ret && errno == ENOEXEC && vm->vm_kernel != -1 &&
+                   gzdirect(fp) && (ret = fstat(vm->vm_kernel, &sb)) == 0)
+                       ret = loadfile_bios(fp, sb.st_size, &vrs);
 
                if (ret)
                        fatal("failed to load kernel or BIOS - exiting");
 
-               if (fp)
-                       fclose(fp);
+               gzclose(fp);
        }
 
        if (vm->vm_kernel != -1)
index a9d752a..79655f4 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: vmd.h,v 1.103 2021/03/29 23:37:01 dv Exp $    */
+/*     $OpenBSD: vmd.h,v 1.104 2021/04/05 18:09:48 dv Exp $    */
 
 /*
  * Copyright (c) 2015 Mike Larkin <mlarkin@openbsd.org>
@@ -485,10 +485,6 @@ int         config_getdisk(struct privsep *, struct imsg *);
 int     config_getif(struct privsep *, struct imsg *);
 int     config_getcdrom(struct privsep *, struct imsg *);
 
-/* vmboot.c */
-FILE   *vmboot_open(int, int *, int, unsigned int, struct vmboot_params *);
-void    vmboot_close(FILE *, struct vmboot_params *);
-
 /* parse.y */
 int     parse_config(const char *);
 int     cmdline_symset(char *);