The branch stable/13 has been updated by corvink:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=d6c280fb36fea18cbb61a07653b737182d085cf6

commit d6c280fb36fea18cbb61a07653b737182d085cf6
Author:     Rebecca Cran <[email protected]>
AuthorDate: 2021-11-28 16:34:33 +0000
Commit:     Corvin Köhne <[email protected]>
CommitDate: 2022-12-08 13:53:38 +0000

    bhyve: Support a _VARS.fd file for bootrom
    
    OVMF creates two separate .fd files, a _CODE.fd file containing
    the UEFI code, and a _VARS.fd file containing a template of an
    empty UEFI variable store.
    
    OVMF decides to write variables to the memory range just below the
    boot rom code if it detects a CFI flash device. So here we add
    just the barest facsimile of CFI command handling to bootrom.c
    that is needed to placate OVMF.
    
    Submitted by: D Scott Phillips <[email protected]>
    Sponsored by: Intel Corporation
    Differential Revision: https://reviews.freebsd.org/D19976
    MFC After: 1 week
    
    (cherry picked from commit 866036f46c6e8884cc7a2aa029408366ede40a23)
---
 usr.sbin/bhyve/bhyve.8   |  23 ++++++++-
 usr.sbin/bhyve/bootrom.c | 132 ++++++++++++++++++++++++++++++++++++++++++++---
 2 files changed, 145 insertions(+), 10 deletions(-)

diff --git a/usr.sbin/bhyve/bhyve.8 b/usr.sbin/bhyve/bhyve.8
index f56f4e771b70..d9f31a09b7db 100644
--- a/usr.sbin/bhyve/bhyve.8
+++ b/usr.sbin/bhyve/bhyve.8
@@ -24,7 +24,7 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd August 19, 2022
+.Dd December 8, 2022
 .Dt BHYVE 8
 .Os
 .Sh NAME
@@ -506,10 +506,15 @@ Use the host TTY device for serial port I/O.
 .Pp
 Boot ROM device backends:
 .Bl -tag -width 10n
-.It Ar romfile
+.It Ar romfile Ns Op Cm \&, Ns Ar 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 saved
+to that file.
 .El
 .Pp
 Pass-through device backends:
@@ -937,6 +942,20 @@ bhyve -c 2 -m 4G -w -H \\
   -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.
+Note that
+.Nm
+will write guest modifications to the given VARS file.
+Be sure to create a per-guest copy of the template VARS file from
+.Pa /usr .
+.Bd -literal -offset indent
+bhyve -c 2 -m 4g -w -H \\
+  -s 0,hostbridge \\
+  -s 31,lpc -p com1,stdio \\
+  -l 
bootrom,/usr/local/share/uefi-firmware/BHYVE_UEFI_CODE.fd,BHYVE_UEFI_VARS.fd
+   uefivm
+.Ed
 .Sh SEE ALSO
 .Xr bhyve 4 ,
 .Xr netgraph 4 ,
diff --git a/usr.sbin/bhyve/bootrom.c b/usr.sbin/bhyve/bootrom.c
index 38a50490eb0b..757ec07d4a54 100644
--- a/usr.sbin/bhyve/bootrom.c
+++ b/usr.sbin/bhyve/bootrom.c
@@ -39,14 +39,17 @@ __FBSDID("$FreeBSD$");
 #include <errno.h>
 #include <fcntl.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 #include <stdbool.h>
 
 #include <vmmapi.h>
+
 #include "bhyverun.h"
 #include "bootrom.h"
 #include "debug.h"
+#include "mem.h"
 
 #define        BOOTROM_SIZE    (16 * 1024 * 1024)      /* 16 MB */
 
@@ -64,6 +67,56 @@ static vm_paddr_t gpa_base;  /* GPA of low end of region. */
 static vm_paddr_t gpa_allocbot;        /* Low GPA of free region. */
 static vm_paddr_t gpa_alloctop;        /* High GPA, minus 1, of free region. */
 
+#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 struct bootrom_var_state {
+       uint8_t         *mmap;
+       uint64_t        gpa;
+       off_t           size;
+       uint8_t         cmd;
+} var = { NULL, 0, 0, CFI_BCS_READ_ARRAY };
+
+/*
+ * Emulate just those CFI basic commands that will convince EDK II
+ * that the Firmware Volume area is writable and persistent.
+ */
+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)
+{
+       off_t offset;
+
+       offset = addr - var.gpa;
+       if (offset + size > var.size || offset < 0 || offset + size <= offset)
+               return (EINVAL);
+
+       if (dir == MEM_F_WRITE) {
+               switch (var.cmd) {
+               case CFI_BCS_WRITE_BYTE:
+                       memcpy(var.mmap + offset, val, size);
+                       var.cmd = CFI_BCS_READ_ARRAY;
+                       break;
+               default:
+                       var.cmd = *(uint8_t *)val;
+               }
+       } else {
+               switch (var.cmd) {
+               case CFI_BCS_CLEAR_STATUS:
+               case CFI_BCS_READ_STATUS:
+                       memset(val, 0, size);
+                       var.cmd = CFI_BCS_READ_ARRAY;
+                       break;
+               default:
+                       memcpy(val, var.mmap + offset, size);
+                       break;
+               }
+       }
+       return (0);
+}
+
 void
 init_bootrom(struct vmctx *ctx)
 {
@@ -142,10 +195,16 @@ bootrom_loadrom(struct vmctx *ctx, const char *romfile)
 {
        struct stat sbuf;
        ssize_t rlen;
-       char *ptr;
-       int fd, i, rv;
+       off_t rom_size, var_size, total_size;
+       char *ptr, *varfile;
+       int fd, varfd, i, rv;
 
        rv = -1;
+       varfd = -1;
+
+       varfile = strdup(romfile);
+       romfile = strsep(&varfile, ",");
+
        fd = open(romfile, O_RDONLY);
        if (fd < 0) {
                EPRINTLN("Error opening bootrom \"%s\": %s",
@@ -153,19 +212,56 @@ bootrom_loadrom(struct vmctx *ctx, const char *romfile)
                goto done;
        }
 
-        if (fstat(fd, &sbuf) < 0) {
+       if (varfile != NULL) {
+               varfd = open(varfile, O_RDWR);
+               if (varfd < 0) {
+                       fprintf(stderr, "Error opening bootrom variable file "
+                           "\"%s\": %s\n", varfile, strerror(errno));
+                       goto done;
+               }
+       }
+
+       if (fstat(fd, &sbuf) < 0) {
                EPRINTLN("Could not fstat bootrom file \"%s\": %s",
-                   romfile, strerror(errno));
+                       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;
+       }
+
+       if (var_size > BOOTROM_SIZE ||
+           (var_size != 0 && var_size < PAGE_SIZE)) {
+               fprintf(stderr, "Invalid bootrom variable size %ld\n",
+                   var_size);
+               goto done;
+       }
+
+       total_size = rom_size + var_size;
+
+       if (total_size > BOOTROM_SIZE) {
+               fprintf(stderr, "Invalid bootrom and variable aggregate size "
+                   "%ld\n", total_size);
+               goto done;
+       }
 
        /* Map the bootrom into the guest address space */
-       if (bootrom_alloc(ctx, sbuf.st_size, PROT_READ | PROT_EXEC,
-           BOOTROM_ALLOC_TOP, &ptr, NULL) != 0)
+       if (bootrom_alloc(ctx, rom_size, PROT_READ | PROT_EXEC,
+           BOOTROM_ALLOC_TOP, &ptr, NULL) != 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) {
                        EPRINTLN("Incomplete read of page %d of bootrom "
@@ -173,6 +269,26 @@ bootrom_loadrom(struct vmctx *ctx, const char *romfile)
                        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;
+               var.size = var_size;
+               var.gpa = (gpa_alloctop - var_size) + 1;
+               gpa_alloctop = var.gpa - 1;
+               rv = register_mem(&(struct mem_range){
+                   .name = "bootrom variable",
+                   .flags = MEM_F_RW,
+                   .handler = bootrom_var_mem_handler,
+                   .base = var.gpa,
+                   .size = var.size,
+               });
+               if (rv != 0)
+                       goto done;
+       }
+
        rv = 0;
 done:
        if (fd >= 0)

Reply via email to