git: 6f051f0e56fe - main - bhyve: Treat BAR registration failures as non-fatal

From: Mark Johnston <markj_at_FreeBSD.org>
Date: Tue, 20 Oct 2026 22:07:00 UTC
The branch main has been updated by markj:

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

commit 6f051f0e56feba19731adfa6f6bd2a098b5091de
Author:     Mark Johnston <markj@FreeBSD.org>
AuthorDate: 2026-10-20 15:41:21 +0000
Commit:     Mark Johnston <markj@FreeBSD.org>
CommitDate: 2026-10-20 22:04:55 +0000

    bhyve: Treat BAR registration failures as non-fatal
    
    Some guest operating systems (at least 9front) will leave device I/O
    space decoding enabled while probing BAR sizes.  In this case, a
    sufficiently large 32-bit BAR can overlap with the HPET MMIO region
    fixed at 0xfed00000.  This results in an assertion failure in
    modify_bar_registration(), even though the collision is transient, i.e.,
    the guest will promptly map the BAR elsewhere.
    
    Tolerate such overlaps.  Keep track of a BAR's registration state and
    make sure that modify_bar_registration() doesn't try to unregister an
    I/O space if registration failed.
    
    This fixes a problem where a 32MB framebuffer ends up overlapping with
    the HPET MMIO region, causing some guests to crash with an assertion
    failure when probing BARs during boot.
    
    PR:             290098
    Reviewed by:    bnovkov
    MFC after:      2 weeks
    Sponsored by:   The FreeBSD Foundation
    Differential Revision:  https://reviews.freebsd.org/D60748
---
 usr.sbin/bhyve/pci_emul.c | 46 +++++++++++++++++++++++++++++++---------------
 usr.sbin/bhyve/pci_emul.h |  2 ++
 2 files changed, 33 insertions(+), 15 deletions(-)

diff --git a/usr.sbin/bhyve/pci_emul.c b/usr.sbin/bhyve/pci_emul.c
index 9170e0b6b93c..60128a735de1 100644
--- a/usr.sbin/bhyve/pci_emul.c
+++ b/usr.sbin/bhyve/pci_emul.c
@@ -639,12 +639,16 @@ static void
 modify_bar_registration(struct pci_devinst *pi, int idx, int registration)
 {
 	struct pci_devemu *pe;
+	struct pcibar *bar;
 	int error;
-	enum pcibar_type type;
 
 	pe = pi->pi_d;
-	type = pi->pi_bar[idx].type;
-	switch (type) {
+	bar = &pi->pi_bar[idx];
+
+	if (!registration && !bar->registered)
+		return;
+
+	switch (bar->type) {
 	case PCIBAR_IO:
 	{
 #ifdef __amd64__
@@ -652,30 +656,34 @@ modify_bar_registration(struct pci_devinst *pi, int idx, int registration)
 
 		bzero(&iop, sizeof(struct inout_port));
 		iop.name = pi->pi_name;
-		iop.port = pi->pi_bar[idx].addr;
-		iop.size = pi->pi_bar[idx].size;
+		iop.port = bar->addr;
+		iop.size = bar->size;
 		if (registration) {
 			iop.flags = IOPORT_F_INOUT;
 			iop.handler = pci_emul_io_handler;
 			iop.arg = pi;
 			error = register_inout(&iop);
-		} else
+		} else {
 			error = unregister_inout(&iop);
+			assert(error == 0);
+		}
 #else
 		struct mem_range mr;
 
 		bzero(&mr, sizeof(struct mem_range));
 		mr.name = pi->pi_name;
-		mr.base = pi->pi_bar[idx].addr;
-		mr.size = pi->pi_bar[idx].size;
+		mr.base = bar->addr;
+		mr.size = bar->size;
 		if (registration) {
 			mr.flags = MEM_F_RW;
 			mr.handler = pci_emul_iomem_handler;
 			mr.arg1 = pi;
 			mr.arg2 = idx;
 			error = register_mem(&mr);
-		} else
+		} else {
 			error = unregister_mem(&mr);
+			assert(error == 0);
+		}
 #endif
 		break;
 	}
@@ -686,16 +694,18 @@ modify_bar_registration(struct pci_devinst *pi, int idx, int registration)
 
 		bzero(&mr, sizeof(struct mem_range));
 		mr.name = pi->pi_name;
-		mr.base = pi->pi_bar[idx].addr;
-		mr.size = pi->pi_bar[idx].size;
+		mr.base = bar->addr;
+		mr.size = bar->size;
 		if (registration) {
 			mr.flags = MEM_F_RW;
 			mr.handler = pci_emul_mem_handler;
 			mr.arg1 = pi;
 			mr.arg2 = idx;
 			error = register_mem(&mr);
-		} else
+		} else {
 			error = unregister_mem(&mr);
+			assert(error == 0);
+		}
 		break;
 	}
 	case PCIBAR_ROM:
@@ -705,10 +715,16 @@ modify_bar_registration(struct pci_devinst *pi, int idx, int registration)
 		error = EINVAL;
 		break;
 	}
-	assert(error == 0);
 
-	if (pe->pe_baraddr != NULL)
-		(*pe->pe_baraddr)(pi, idx, registration, pi->pi_bar[idx].addr);
+	/*
+	 * An error may legitimately occur if the guest is probing BARs and
+	 * leaves decoding enabled.  Keep track of this if it happens so that we
+	 * can skip unregistration.
+	 */
+	bar->registered = (registration && error == 0);
+
+	if (error == 0 && pe->pe_baraddr != NULL)
+		(*pe->pe_baraddr)(pi, idx, registration, bar->addr);
 }
 
 static void
diff --git a/usr.sbin/bhyve/pci_emul.h b/usr.sbin/bhyve/pci_emul.h
index 9aeb11496943..b7ddfd73bda1 100644
--- a/usr.sbin/bhyve/pci_emul.h
+++ b/usr.sbin/bhyve/pci_emul.h
@@ -39,6 +39,7 @@
 #include <dev/pci/pcireg.h>
 
 #include <assert.h>
+#include <stdbool.h>
 
 #include "pci_irq.h"
 
@@ -99,6 +100,7 @@ struct pcibar {
 	uint64_t		size;
 	uint64_t		addr;
 	uint8_t			lobits;
+	bool			registered;	/* io region is registered */
 };
 
 #define PI_NAMESZ	40