Index: usr.sbin/bhyve/bhyve.8 =================================================================== --- usr.sbin/bhyve/bhyve.8 +++ usr.sbin/bhyve/bhyve.8 @@ -24,7 +24,7 @@ .\" .\" $FreeBSD$ .\" -.Dd December 11, 2018 +.Dd April 19, 2019 .Dt BHYVE 8 .Os .Sh NAME @@ -323,10 +323,15 @@ .Pp Boot ROM device: .Bl -tag -width 10n -.It Pa romfile +.It Pa romfile Ns Op , Ns Pa varfile Map .Ar romfile in the guest address space reserved for boot firmware. +If +.Ar varfile +is provided, that file is also mapped in the boot firmware guest +address space, and any modifications the guest makes will be persisted +to that file. .El .Pp Pass-through devices: @@ -616,6 +621,17 @@ -l bootrom,/usr/local/share/uefi-firmware/BHYVE_UEFI.fd \\ uefivm .Ed +.Pp +Run a UEFI virtual machine with a VARS file to save EFI variables. +bhyve will write to the given VARS file, so make sure to create per-guest copies +of the template file in /usr. +.Bd -literal -offset indent +bhyve -c 2 -m 4g -w -H \\ + -s 0,hostbridge \\ + -s 31,lpc -p com1,stdio \\ + -l bootrom,/usr/.../BHYVE_UEFI_CODE.fd,/var/.../BHYVE_UEFI_VARS.fd + uefivm +.Ed .Sh SEE ALSO .Xr bhyve 4 , .Xr nmdm 4 , Index: usr.sbin/bhyve/bootrom.c =================================================================== --- usr.sbin/bhyve/bootrom.c +++ usr.sbin/bhyve/bootrom.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -45,19 +46,72 @@ #include #include "bhyverun.h" #include "bootrom.h" +#include "mem.h" #define MAX_BOOTROM_SIZE (16 * 1024 * 1024) /* 16 MB */ +#define CFI_BCS_WRITE_BYTE 0x10 +#define CFI_BCS_CLEAR_STATUS 0x50 +#define CFI_BCS_READ_STATUS 0x70 +#define CFI_BCS_READ_ARRAY 0xff + +static int +bootrom_var_mem_handler(struct vmctx *ctx, int vcpu, int dir, uint64_t addr, + int size, uint64_t *val, void *arg1, long arg2) +{ + static uint8_t cmd = CFI_BCS_READ_ARRAY; + unsigned char *var_mmap; + off_t offset; + + var_mmap = arg1; + offset = addr - (intptr_t)arg2; + if (dir == MEM_F_WRITE) { + switch (cmd) { + case CFI_BCS_WRITE_BYTE: + memcpy(var_mmap + offset, val, size); + cmd = CFI_BCS_READ_ARRAY; + break; + default: + cmd = *(uint8_t *)val; + } + } else { + switch (cmd) { + case CFI_BCS_CLEAR_STATUS: + case CFI_BCS_READ_STATUS: + memset(val, 0, size); + cmd = CFI_BCS_READ_ARRAY; + break; + default: + memcpy(val, var_mmap + offset, size); + break; + } + } + return (0); +} + int bootrom_init(struct vmctx *ctx, const char *romfile) { struct stat sbuf; vm_paddr_t gpa; + off_t rom_size, var_size, total_size; ssize_t rlen; - char *ptr; - int fd, i, rv, prot; + unsigned char *var_mmap; + char *ptr, *romfile_dup, *varfile; + int fd, varfd, i, rv, prot; rv = -1; + varfd = -1; + if (strchr(romfile, ',') == NULL) { + romfile_dup = NULL; + varfile = NULL; + } else { + romfile_dup = strdup(romfile); + romfile = romfile_dup; + varfile = romfile_dup; + strsep(&varfile, ","); + } + fd = open(romfile, O_RDONLY); if (fd < 0) { fprintf(stderr, "Error opening bootrom \"%s\": %s\n", @@ -65,39 +119,62 @@ goto done; } + if (varfile != NULL) { + varfd = open(varfile, O_RDWR); + if (fd < 0) { + fprintf(stderr, "Error opening bootrom variable file " + "\"%s\": %s\n", varfile, strerror(errno)); + goto done; + } + } + if (fstat(fd, &sbuf) < 0) { fprintf(stderr, "Could not fstat bootrom file \"%s\": %s\n", romfile, strerror(errno)); goto done; } + rom_size = sbuf.st_size; + if (varfd < 0) + var_size = 0; + else { + if (fstat(varfd, &sbuf) < 0) { + fprintf(stderr, "Could not fstat bootrom variable file \"%s\": %s\n", + varfile, strerror(errno)); + goto done; + } + var_size = sbuf.st_size; + } + /* * 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 < PAGE_SIZE) { - fprintf(stderr, "Invalid bootrom size %ld\n", sbuf.st_size); + total_size = rom_size + var_size; + if (total_size > MAX_BOOTROM_SIZE || rom_size < PAGE_SIZE || + (var_size != 0 && var_size < PAGE_SIZE)) { + fprintf(stderr, "Invalid bootrom size %ld\n", total_size); goto done; } - if (sbuf.st_size & PAGE_MASK) { - fprintf(stderr, "Bootrom size %ld is not a multiple of the " - "page size\n", sbuf.st_size); + if ((rom_size & PAGE_MASK) != 0 || (var_size & PAGE_MASK) != 0) { + fprintf(stderr, "Bootrom size %ld %ld is not a multiple of the " + "page size\n", rom_size, var_size); goto done; } - ptr = vm_create_devmem(ctx, VM_BOOTROM, "bootrom", sbuf.st_size); + ptr = vm_create_devmem(ctx, VM_BOOTROM, "bootrom", rom_size); if (ptr == MAP_FAILED) goto done; /* Map the bootrom into the guest address space */ prot = PROT_READ | PROT_EXEC; - gpa = (1ULL << 32) - sbuf.st_size; - if (vm_mmap_memseg(ctx, gpa, VM_BOOTROM, 0, sbuf.st_size, prot) != 0) + gpa = (1ULL << 32) - rom_size; + if (vm_mmap_memseg(ctx, gpa, VM_BOOTROM, 0, rom_size, prot) != 0) goto done; /* Read 'romfile' into the guest address space */ - for (i = 0; i < sbuf.st_size / PAGE_SIZE; i++) { + for (i = 0; i < rom_size / PAGE_SIZE; i++) { rlen = read(fd, ptr + i * PAGE_SIZE, PAGE_SIZE); if (rlen != PAGE_SIZE) { fprintf(stderr, "Incomplete read of page %d of bootrom " @@ -105,9 +182,32 @@ goto done; } } + + if (varfd >= 0) { + var_mmap = mmap(NULL, var_size, PROT_READ | PROT_WRITE, + MAP_SHARED, varfd, 0); + if (var_mmap == MAP_FAILED) + goto done; + gpa -= var_size; + rv = register_mem(&(struct mem_range){ + .name = "bootrom variables", + .flags = MEM_F_RW, + .handler = bootrom_var_mem_handler, + .arg1 = var_mmap, + .arg2 = gpa, + .base = gpa, + .size = var_size, + }); + if (rv != 0) + goto done; + } + rv = 0; done: + free(romfile_dup); if (fd >= 0) close(fd); + if (varfd >= 0) + close(varfd); return (rv); }