git: ec99803ad70a - main - bhyve: Populate the device version from the backend

From: ShengYi Hung <aokblast_at_FreeBSD.org>
Date: Sun, 10 Aug 2025 03:16:17 UTC
The branch main has been updated by aokblast:

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

commit ec99803ad70a757cc104334f745a2985cf60e948
Author:     ShengYi Hung <aokblast@FreeBSD.org>
AuthorDate: 2025-07-23 16:19:40 +0000
Commit:     ShengYi Hung <aokblast@FreeBSD.org>
CommitDate: 2025-08-10 03:15:41 +0000

    bhyve: Populate the device version from the backend
    
    The pci_xhci driver requires the USB device version to be known before
    allocating a hub port. To support this, we split the original xHCI
    initialization into two phases:
    
    1. Probe: Parse the nvlist and determine the device version.
    2. Init: Complete initialization and set up the softc details.
    
    This change ensures proper hub port allocation based on accurate device
    version.
    
    Approved by:    markj (mentor)
    Sponsored by:   The FreeBSD Foundation
    Differential Revision: https://reviews.freebsd.org/D51478
---
 usr.sbin/bhyve/pci_xhci.c  | 35 +++++++++++++++++++++++++----------
 usr.sbin/bhyve/usb_emul.h  |  4 +++-
 usr.sbin/bhyve/usb_mouse.c | 13 +++++++++++--
 3 files changed, 39 insertions(+), 13 deletions(-)

diff --git a/usr.sbin/bhyve/pci_xhci.c b/usr.sbin/bhyve/pci_xhci.c
index 0871bbb87fe5..ff12e40359e2 100644
--- a/usr.sbin/bhyve/pci_xhci.c
+++ b/usr.sbin/bhyve/pci_xhci.c
@@ -406,7 +406,7 @@ pci_xhci_usbcmd_write(struct pci_xhci_softc *sc, uint32_t cmd)
 				 * XHCI 4.19.3 USB2 RxDetect->Polling,
 				 *             USB3 Polling->U0
 				 */
-				if (dev->dev_ue->ue_usbver == 2)
+				if (dev->hci.hci_usbver == 2)
 					port->portsc |=
 					    XHCI_PS_PLS_SET(UPS_PORT_LS_POLL);
 				else
@@ -2590,7 +2590,7 @@ pci_xhci_reset_port(struct pci_xhci_softc *sc, int portn, int warm)
 		port->portsc |= XHCI_PS_PED |
 		    XHCI_PS_SPEED_SET(dev->hci.hci_speed);
 
-		if (warm && dev->dev_ue->ue_usbver == 3) {
+		if (warm && dev->hci.hci_usbver == 3) {
 			port->portsc |= XHCI_PS_WRC;
 		}
 
@@ -2620,7 +2620,7 @@ pci_xhci_init_port(struct pci_xhci_softc *sc, int portn)
 		port->portsc = XHCI_PS_CCS |		/* connected */
 		               XHCI_PS_PP;		/* port power */
 
-		if (dev->dev_ue->ue_usbver == 2) {
+		if (dev->hci.hci_usbver == 2) {
 			port->portsc |= XHCI_PS_PLS_SET(UPS_PORT_LS_POLL) |
 			    XHCI_PS_SPEED_SET(dev->hci.hci_speed);
 		} else {
@@ -2785,8 +2785,8 @@ pci_xhci_parse_devices(struct pci_xhci_softc *sc, nvlist_t *nvl)
 
 	cookie = NULL;
 	while ((name = nvlist_next(slots_nvl, &type, &cookie)) != NULL) {
-		if (usb2_port == ((sc->usb2_port_start) + XHCI_MAX_DEVS/2) ||
-		    usb3_port == ((sc->usb3_port_start) + XHCI_MAX_DEVS/2)) {
+		if (usb2_port == ((sc->usb2_port_start) + XHCI_MAX_DEVS / 2) ||
+		    usb3_port == ((sc->usb3_port_start) + XHCI_MAX_DEVS / 2)) {
 			WPRINTF(("pci_xhci max number of USB 2 or 3 "
 			     "devices reached, max %d", XHCI_MAX_DEVS/2));
 			goto bad;
@@ -2834,12 +2834,25 @@ pci_xhci_parse_devices(struct pci_xhci_softc *sc, nvlist_t *nvl)
 		dev->hci.hci_intr = pci_xhci_dev_intr;
 		dev->hci.hci_event = pci_xhci_dev_event;
 		dev->hci.hci_speed = USB_SPEED_MAX;
+		dev->hci.hci_usbver = -1;
 
-		if (ue->ue_usbver == 2) {
+		devsc = ue->ue_probe(&dev->hci, nvl);
+		if (devsc == NULL) {
+			free(dev);
+			goto bad;
+		}
+		dev->dev_sc = devsc;
+
+		if (dev->hci.hci_usbver == -1)
+			dev->hci.hci_usbver = ue->ue_usbver;
+
+		if (dev->hci.hci_usbver == 2) {
 			if (usb2_port == sc->usb2_port_start +
 			    XHCI_MAX_DEVS / 2) {
 				WPRINTF(("pci_xhci max number of USB 2 devices "
 				     "reached, max %d", XHCI_MAX_DEVS / 2));
+				free(dev->dev_sc);
+				free(dev);
 				goto bad;
 			}
 			dev->hci.hci_port = usb2_port;
@@ -2849,6 +2862,8 @@ pci_xhci_parse_devices(struct pci_xhci_softc *sc, nvlist_t *nvl)
 			    XHCI_MAX_DEVS / 2) {
 				WPRINTF(("pci_xhci max number of USB 3 devices "
 				     "reached, max %d", XHCI_MAX_DEVS / 2));
+				free(dev->dev_sc);
+				free(dev);
 				goto bad;
 			}
 			dev->hci.hci_port = usb3_port;
@@ -2857,13 +2872,10 @@ pci_xhci_parse_devices(struct pci_xhci_softc *sc, nvlist_t *nvl)
 		XHCI_DEVINST_PTR(sc, dev->hci.hci_port) = dev;
 
 		dev->hci.hci_address = 0;
-		devsc = ue->ue_init(&dev->hci, nvl);
-		if (devsc == NULL) {
+		if (ue->ue_init(dev->dev_sc))
 			goto bad;
-		}
 
 		dev->dev_ue = ue;
-		dev->dev_sc = devsc;
 		if (dev->hci.hci_speed == USB_SPEED_MAX)
 			dev->hci.hci_speed = ue->ue_usbspeed;
 
@@ -2885,6 +2897,8 @@ portsfinal:
 
 bad:
 	for (i = 1; i <= XHCI_MAX_DEVS; i++) {
+		if (XHCI_DEVINST_PTR(sc, i) != NULL)
+			free(XHCI_DEVINST_PTR(sc, i)->dev_sc);
 		free(XHCI_DEVINST_PTR(sc, i));
 	}
 
@@ -3232,6 +3246,7 @@ pci_xhci_snapshot(struct vm_snapshot_meta *meta)
 		SNAPSHOT_VAR_OR_LEAVE(dev->hci.hci_address, meta, ret, done);
 		SNAPSHOT_VAR_OR_LEAVE(dev->hci.hci_port, meta, ret, done);
 		SNAPSHOT_VAR_OR_LEAVE(dev->hci.hci_speed, meta, ret, done);
+		SNAPSHOT_VAR_OR_LEAVE(dev->hci.hci_usbver, meta, ret, done);
 	}
 
 	SNAPSHOT_VAR_OR_LEAVE(sc->usb2_port_start, meta, ret, done);
diff --git a/usr.sbin/bhyve/usb_emul.h b/usr.sbin/bhyve/usb_emul.h
index 85dedfeacd3b..43b6b53b5205 100644
--- a/usr.sbin/bhyve/usb_emul.h
+++ b/usr.sbin/bhyve/usb_emul.h
@@ -52,7 +52,8 @@ struct usb_devemu {
 	int	ue_usbspeed;	/* usb device speed */
 
 	/* instance creation */
-	void	*(*ue_init)(struct usb_hci *hci, nvlist_t *nvl);
+	void	*(*ue_probe)(struct usb_hci *hci, nvlist_t *nvl);
+	int	(*ue_init)(void *sc);
 
 	/* handlers */
 	int	(*ue_request)(void *sc, struct usb_data_xfer *xfer);
@@ -86,6 +87,7 @@ struct usb_hci {
 	int	hci_address;
 	int	hci_port;
 	int	hci_speed;
+	int	hci_usbver;
 };
 
 /*
diff --git a/usr.sbin/bhyve/usb_mouse.c b/usr.sbin/bhyve/usb_mouse.c
index a37941c0cd9d..82b1159d5f61 100644
--- a/usr.sbin/bhyve/usb_mouse.c
+++ b/usr.sbin/bhyve/usb_mouse.c
@@ -295,20 +295,28 @@ umouse_event(uint8_t button, int x, int y, void *arg)
 }
 
 static void *
-umouse_init(struct usb_hci *hci, nvlist_t *nvl __unused)
+umouse_probe(struct usb_hci *hci, nvlist_t *nvl __unused)
 {
 	struct umouse_softc *sc;
 
 	sc = calloc(1, sizeof(struct umouse_softc));
 	sc->hci = hci;
 
+	return (sc);
+}
+
+static int
+umouse_init(void *scarg)
+{
+	struct umouse_softc *sc = (struct umouse_softc *)scarg;
+
 	sc->hid.protocol = 1;	/* REPORT protocol */
 	pthread_mutex_init(&sc->mtx, NULL);
 	pthread_mutex_init(&sc->ev_mtx, NULL);
 
 	console_ptr_register(umouse_event, sc, 10);
 
-	return (sc);
+	return (0);
 }
 
 #define	UREQ(x,y)	((x) | ((y) << 8))
@@ -811,6 +819,7 @@ static struct usb_devemu ue_mouse = {
 	.ue_emu =	"tablet",
 	.ue_usbver =	3,
 	.ue_usbspeed =	USB_SPEED_HIGH,
+	.ue_probe =	umouse_probe,
 	.ue_init =	umouse_init,
 	.ue_request =	umouse_request,
 	.ue_data =	umouse_data_handler,