Index | Thread | Search

From:
Mike Larkin <mlarkin@nested.page>
Subject:
Re: make room for vmd to use 4MB firmware images
To:
Dave Voutila <dv@sisu.io>
Cc:
tech@openbsd.org, mlarkin@openbsd.org
Date:
Tue, 5 Aug 2025 03:23:45 -0700

Download raw body.

Thread
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);