diff --git a/Makefile b/Makefile index 3294494..a9f6456 100644 --- a/Makefile +++ b/Makefile @@ -64,6 +64,7 @@ XHYVE_SRC := \ src/xmsr.c FIRMWARE_SRC := \ + src/firmware/bootrom.c \ src/firmware/kexec.c \ src/firmware/fbsd.c diff --git a/include/xhyve/firmware/bootrom.h b/include/xhyve/firmware/bootrom.h new file mode 100644 index 0000000..7787f9e --- /dev/null +++ b/include/xhyve/firmware/bootrom.h @@ -0,0 +1,8 @@ +#pragma once + +#include +#include + +void bootrom_init(const char *bootrom_path); +uint64_t bootrom_load(void); +bool bootrom_contains_gpa(uint64_t gpa); diff --git a/src/block_if.c b/src/block_if.c index 4d5df95..7fb4ab8 100644 --- a/src/block_if.c +++ b/src/block_if.c @@ -58,7 +58,7 @@ */ #define BLOCKIF_NUMTHR 1 -#define BLOCKIF_MAXREQ (64 + BLOCKIF_NUMTHR) +#define BLOCKIF_MAXREQ (128 + BLOCKIF_NUMTHR) enum blockop { BOP_READ, diff --git a/src/firmware/bootrom.c b/src/firmware/bootrom.c new file mode 100644 index 0000000..ac9fae0 --- /dev/null +++ b/src/firmware/bootrom.c @@ -0,0 +1,130 @@ +/*- + * Copyright (c) 2015 Neel Natu + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define MAX_BOOTROM_SIZE (16 * 1024 * 1024) /* 16 MB */ + +static const char *romfile; +static uint64_t bootrom_gpa = (1ULL << 32); + +void +bootrom_init(const char *romfile_path) +{ + romfile = romfile_path; +} + +uint64_t bootrom_load(void) +{ + + struct stat sbuf; + uint64_t gpa; + ssize_t rlen; + char *ptr; + int fd, i, rv; + + rv = -1; + fd = open(romfile, O_RDONLY); + if (fd < 0) { + fprintf(stderr, "Error opening bootrom \"%s\": %s\n", + romfile, strerror(errno)); + goto done; + } + + if (fstat(fd, &sbuf) < 0) { + fprintf(stderr, "Could not fstat bootrom file \"%s\": %s\n", + romfile, strerror(errno)); + goto done; + } + + /* + * Limit bootrom size to 16MB so it doesn't encroach into reserved + * MMIO space (e.g. APIC, HPET, MSI). + */ + if (sbuf.st_size > MAX_BOOTROM_SIZE || sbuf.st_size < XHYVE_PAGE_SIZE) { + fprintf(stderr, "Invalid bootrom size %zd\n", sbuf.st_size); + goto done; + } + + if (sbuf.st_size & XHYVE_PAGE_MASK) { + fprintf(stderr, "Bootrom size %zd is not a multiple of the " + "page size\n", sbuf.st_size); + goto done; + } + + gpa = bootrom_gpa -= (size_t)sbuf.st_size; + + /* XXX Mapping cold be R/O to guest */ + ptr = vmm_mem_alloc(gpa, (size_t)sbuf.st_size); + if (!ptr) { + fprintf(stderr, + "Failed to allocate %zd bytes of memory for bootrom\n", + sbuf.st_size); + rv = -1; + goto done; + } + + /* Read 'romfile' into the guest address space */ + for (i = 0; i < sbuf.st_size / XHYVE_PAGE_SIZE; i++) { + rlen = read(fd, ptr + i * XHYVE_PAGE_SIZE, XHYVE_PAGE_SIZE); + if (rlen != XHYVE_PAGE_SIZE) { + fprintf(stderr, "Incomplete read of page %d of bootrom " + "file %s: %ld bytes\n", i, romfile, rlen); + goto done; + } + } + + rv = 0; +done: + if (fd >= 0) + close(fd); + if (rv) + exit(-1); + return 0xfff0; +} + +bool +bootrom_contains_gpa(uint64_t gpa) +{ + return (gpa >= bootrom_gpa && gpa < (1ULL << 32)); +} diff --git a/src/pci_virtio_block.c b/src/pci_virtio_block.c index 763a1e9..5eb3b0b 100644 --- a/src/pci_virtio_block.c +++ b/src/pci_virtio_block.c @@ -50,7 +50,7 @@ #include #include -#define VTBLK_RINGSZ 64 +#define VTBLK_RINGSZ 128 #define VTBLK_S_OK 0 #define VTBLK_S_IOERR 1 diff --git a/src/vmm/intel/vmx.c b/src/vmm/intel/vmx.c index f4f2bb0..6a91ad3 100644 --- a/src/vmm/intel/vmx.c +++ b/src/vmm/intel/vmx.c @@ -52,6 +52,7 @@ #include #include #include +#include #define PROCBASED_CTLS_WINDOW_SETTING \ (PROCBASED_INT_WINDOW_EXITING | \ @@ -2041,6 +2042,7 @@ vmx_exit_process(struct vmx *vmx, int vcpu, struct vm_exit *vmexit) */ gpa = vmcs_gpa(vcpu); if (vm_mem_allocated(vmx->vm, gpa) || + bootrom_contains_gpa(gpa) || apic_access_fault(vmx, vcpu, gpa)) { vmexit->exitcode = VM_EXITCODE_PAGING; vmexit->inst_length = 0; diff --git a/src/xhyve.c b/src/xhyve.c index 969a435..4998a6f 100644 --- a/src/xhyve.c +++ b/src/xhyve.c @@ -67,6 +67,7 @@ #include #include +#include #define GUEST_NIO_PORT 0x488 /* guest upcalls via i/o port */ @@ -726,41 +727,78 @@ firmware_parse(const char *opt) { if (strncmp(fw, "kexec", strlen("kexec")) == 0) { fw_func = kexec; + + if ((cp = strchr(fw, ',')) != NULL) { + *cp = '\0'; + opt1 = cp + 1; + } else { + goto fail; + } + + if ((cp = strchr(opt1, ',')) != NULL) { + *cp = '\0'; + opt2 = cp + 1; + } else { + goto fail; + } + + if ((cp = strchr(opt2, ',')) != NULL) { + *cp = '\0'; + opt3 = cp + 1; + } else { + goto fail; + } + + } else if (strncmp(fw, "fbsd", strlen("fbsd")) == 0) { fw_func = fbsd_load; + + if ((cp = strchr(fw, ',')) != NULL) { + *cp = '\0'; + opt1 = cp + 1; + } else { + goto fail; + } + + if ((cp = strchr(opt1, ',')) != NULL) { + *cp = '\0'; + opt2 = cp + 1; + } else { + goto fail; + } + + if ((cp = strchr(opt2, ',')) != NULL) { + *cp = '\0'; + opt3 = cp + 1; + } else { + goto fail; + } + + } else if (strncmp(fw, "bootrom", strlen("bootrom")) == 0) { + fw_func = bootrom_load; + opt2 = ""; + opt3 = ""; + if ((cp = strchr(fw, ',')) != NULL) { + *cp = '\0'; + opt1 = cp + 1; + } else { + goto fail; + } + } else { goto fail; } - if ((cp = strchr(fw, ',')) != NULL) { - *cp = '\0'; - opt1 = cp + 1; - } else { - goto fail; - } - - if ((cp = strchr(opt1, ',')) != NULL) { - *cp = '\0'; - opt2 = cp + 1; - } else { - goto fail; - } - - if ((cp = strchr(opt2, ',')) != NULL) { - *cp = '\0'; - opt3 = cp + 1; - } else { - goto fail; - } - - opt2 = strlen(opt2) ? opt2 : NULL; - opt3 = strlen(opt3) ? opt3 : NULL; + opt2 = strlen(opt2) ? opt2 : NULL; + opt3 = strlen(opt3) ? opt3 : NULL; if (fw_func == kexec) { kexec_init(opt1, opt2, opt3); } else if (fw_func == fbsd_load) { /* FIXME: let user set boot-loader serial device */ fbsd_init(opt1, opt2, opt3, NULL); + } else if (fw_func == bootrom_load) { + bootrom_init(opt1); } else { goto fail; } @@ -770,7 +808,8 @@ firmware_parse(const char *opt) { fail: fprintf(stderr, "Invalid firmware argument\n" " -f kexec,'kernel','initrd','\"cmdline\"'\n" - " -f fbsd,'userboot','boot volume','\"kernel env\"'\n"); + " -f fbsd,'userboot','boot volume','\"kernel env\"'\n" + " -f bootrom,'ROM'\n"); return -1; }