Download raw body.
make room for vmd to use 4MB firmware images
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);
make room for vmd to use 4MB firmware images