From: Mike Larkin Subject: Re: make room for vmd to use 4MB firmware images To: Dave Voutila Cc: tech@openbsd.org, mlarkin@openbsd.org Date: Tue, 5 Aug 2025 03:23:45 -0700 On Tue, Aug 05, 2025 at 05:06:11AM -0400, Dave Voutila wrote: > SeaBIOS is our current firmware for booting vmd guests (when not direct > booting a kernel image/ramdisk). To continue work on adding EDK2/OVMF > UEFI support, we need to bump our support from 2MB to 4MB because the > resulting firmware is 4MB. > > This diff adjusts how we carve out space for firmware images and how we > load them into the lower and upper bios areas. There should be no > regression with existing seabios / vmm-bios firmware images. > > ok? > ok mlarkin. I have been running with the earlier version of this diff for a few days now. Please let dv@ know if there is any fallout using VMs that worked before. Do note that while we are moving toward OVMF, it is not yet ready. > > diff refs/heads/master refs/heads/seabios > commit - 7b1f6ebfc4c7f6ea7eccf2eb68f2fe2a321a5266 > commit + 1420ebb5a40b7de8dc0b8a85d7c19e2761f62e9d > blob - 925eec31ad1e4d911d195240712e8a5e26c26264 > blob + 58825b7b5f99c1c4f8fb3056c36ba150eccdd1ff > --- usr.sbin/vmd/pci.h > +++ usr.sbin/vmd/pci.h > @@ -36,7 +36,7 @@ > #define PCI_BAR_TYPE_MMIO 0x1 > > #define PCI_MMIO_BAR_BASE 0xF0000000ULL > -#define PCI_MMIO_BAR_END 0xFFDFFFFFULL /* 2 MiB below 4 GiB */ > +#define PCI_MMIO_BAR_END 0xFFBFFFFFULL /* 4 MiB below 4 GiB */ > > #define PCI_MAX_PIC_IRQS 10 > > blob - 52a1c8ce4f09f00e155b0b34c021343d70d93b1c > blob + 11461b3ada7692c0129dbe933d5ab3fb9f51079f > --- usr.sbin/vmd/vmd.h > +++ usr.sbin/vmd/vmd.h > @@ -46,6 +46,7 @@ > #define CTASSERT(x) extern char _ctassert[(x) ? 1 : -1 ] \ > __attribute__((__unused__)) > > +#define KB(x) (x * 1024UL) > #define MB(x) (x * 1024UL * 1024UL) > #define GB(x) (x * 1024UL * 1024UL * 1024UL) > > blob - 6368571bd396c704f8fc7d2668c3a4aa930f77c1 > blob + a99a76da8707b8726fbd2297496442b7f9cd4e98 > --- usr.sbin/vmd/x86_vm.c > +++ usr.sbin/vmd/x86_vm.c > @@ -190,10 +190,13 @@ create_memory_map(struct vm_create_params *vcp) > vcp->vcp_memranges[1].vmr_type = VM_MEM_RESERVED; > mem_bytes -= len; > > - /* If we have less than 2MB remaining, still create a 2nd BIOS area. */ > - if (mem_bytes <= MB(2)) { > + /* > + * If we have less than 4MB remaining to assign, still create a 2nd > + * BIOS area. > + */ > + if (mem_bytes <= MB(4)) { > vcp->vcp_memranges[2].vmr_gpa = PCI_MMIO_BAR_END; > - vcp->vcp_memranges[2].vmr_size = MB(2); > + vcp->vcp_memranges[2].vmr_size = MB(4); > vcp->vcp_memranges[2].vmr_type = VM_MEM_RESERVED; > vcp->vcp_nmemranges = 3; > return; > @@ -225,7 +228,7 @@ create_memory_map(struct vm_create_params *vcp) > > /* Fifth region: 2nd copy of BIOS above MMIO ending at 4GB */ > vcp->vcp_memranges[4].vmr_gpa = PCI_MMIO_BAR_END + 1; > - vcp->vcp_memranges[4].vmr_size = MB(2); > + vcp->vcp_memranges[4].vmr_size = MB(4); > vcp->vcp_memranges[4].vmr_type = VM_MEM_RESERVED; > > /* Sixth region: any remainder above 4GB */ > @@ -290,35 +293,50 @@ load_firmware(struct vmd_vm *vm, struct vcpu_reg_state > int > loadfile_bios(gzFile fp, off_t size, struct vcpu_reg_state *vrs) > { > - off_t off; > + off_t off = 0; > + size_t lower_sz = size; > > - /* Set up a "flat 16 bit" register state for BIOS */ > + /* > + * While a 15 byte firmware is most likely useless, given the > + * reset vector on a PC is 15 bytes below 0xFFFFF, make sure > + * we will at least align to that boundary. > + */ > + if (size < 15) { > + log_warnx("bios image too small"); > + return (-1); > + } > + > + /* Assumptions elsewhere in memory layout limit to 4 MiB. */ > + if (size > (off_t)MB(4)) { > + log_warnx("bios image too large (> 4 MiB)"); > + return (-1); > + } > + > + /* Set up a "flat 16 bit" register state for BIOS. */ > memcpy(vrs, &vcpu_init_flat16, sizeof(*vrs)); > > - /* Seek to the beginning of the BIOS image */ > - if (gzseek(fp, 0, SEEK_SET) == -1) > + /* Read a full copy into BIOS area ending at 4 GiB. */ > + if (gzrewind(fp) == -1) > return (-1); > > - /* The BIOS image must end at 1MB */ > - if ((off = MB(1) - size) < 0) > - return (-1); > - > - /* Read BIOS image into memory */ > - if (mread(fp, off, size) != (size_t)size) { > - errno = EIO; > - return (-1); > - } > - > - if (gzseek(fp, 0, SEEK_SET) == -1) > - return (-1); > - > - /* Read a second BIOS copy into memory ending at 4GB */ > off = GB(4) - size; > if (mread(fp, off, size) != (size_t)size) { > errno = EIO; > return (-1); > } > > + /* Copy the last 128K into the lower BIOS area ending at 1 MiB. */ > + if (gzrewind(fp) == -1) > + return (-1); > + > + lower_sz = MIN((off_t)KB(128), size); > + if (gzseek(fp, size - lower_sz, SEEK_SET) == -1) > + return (-1); > + > + off = MB(1) - lower_sz; > + if (mread(fp, off, lower_sz) != lower_sz) > + return (-1); > + > log_debug("%s: loaded BIOS image", __func__); > > return (0);