git: e5217bedc964 - stable/13 - bhyve: Support other schemes for naming pass-through devices.

From: John Baldwin <jhb_at_FreeBSD.org>
Date: Thu, 25 Aug 2022 17:31:41 UTC
The branch stable/13 has been updated by jhb:

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

commit e5217bedc964639ded1c8f5401012516c8558e39
Author:     John Baldwin <jhb@FreeBSD.org>
AuthorDate: 2022-08-19 21:55:29 +0000
Commit:     John Baldwin <jhb@FreeBSD.org>
CommitDate: 2022-08-25 16:38:33 +0000

    bhyve: Support other schemes for naming pass-through devices.
    
    Permit naming pass through devices using the syntax accepted by
    pciconf (pci[<domain>:]<bus>:<slot>:<func>) as well as by device name
    (e.g. "ppt0").
    
    While here, fix an error in the manpage that had the bus and slot
    arguments for the original /-delimited scheme swapped.
    
    Reviewed by:    imp, markj
    Sponsored by:   The FreeBSD Foundation
    Differential Revision:  https://reviews.freebsd.org/D36147
    
    (cherry picked from commit baf753cc196e2308828c7fb9b0385d5fe17ecdc2)
---
 usr.sbin/bhyve/bhyve.8        |  27 ++++++++--
 usr.sbin/bhyve/bhyve_config.5 |  12 ++++-
 usr.sbin/bhyve/pci_passthru.c | 112 ++++++++++++++++++++++++++++++++++++------
 3 files changed, 131 insertions(+), 20 deletions(-)

diff --git a/usr.sbin/bhyve/bhyve.8 b/usr.sbin/bhyve/bhyve.8
index a352a7ba2a93..1de8c516b785 100644
--- a/usr.sbin/bhyve/bhyve.8
+++ b/usr.sbin/bhyve/bhyve.8
@@ -24,7 +24,7 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd October 13, 2021
+.Dd August 19, 2022
 .Dt BHYVE 8
 .Os
 .Sh NAME
@@ -367,6 +367,7 @@ Network device backends:
 .Xc
 .El
 .Sm on
+.Pp
 If
 .Cm mac
 is not specified, the MAC address is derived from a fixed OUI and the
@@ -492,14 +493,32 @@ in the guest address space reserved for boot firmware.
 .El
 .Pp
 Pass-through device backends:
-.Bl -tag -width 10n
-.It Ns Ar slot Ns Cm \&/ Ns Ar bus Ns Cm \&/ Ns Ar function
-Connect to a PCI device on the host at the selector described by
+.Sm off
+.Bl -bullet
+.It
+.Cm ppt Ar N Oo , Ar passthru-device-options Oc
+.It
+.Ns Ar bus Cm \&/ Ar slot Cm \&/ Ar function
+.Op , Ar passthru-device-options
+.It
+.Cm pci Ar bus Cm : Ar slot Cm : Ns Ar function
+.Op , Ar passthru-device-options
+.El
+.Sm on
+.Pp
+Connect to a PCI device on the host either named ppt
+.Ns Ar N
+or at the selector described by
 .Ar slot ,
 .Ar bus ,
 and
 .Ar function
 numbers.
+.Pp
+The
+.Ar passthru-device-options
+are:
+.Bl -tag -width 10n
 .It Cm rom= Ns Ar romfile
 Add
 .Ar romfile
diff --git a/usr.sbin/bhyve/bhyve_config.5 b/usr.sbin/bhyve/bhyve_config.5
index fbbea50bc7be..b4160a37620d 100644
--- a/usr.sbin/bhyve/bhyve_config.5
+++ b/usr.sbin/bhyve/bhyve_config.5
@@ -23,7 +23,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd September 17, 2021
+.Dd August 19, 2022
 .Dt BHYVE_CONFIG 5
 .Os
 .Sh NAME
@@ -538,6 +538,12 @@ If set, allocate a memory disk as the backing store.
 The value of this variable is the size of the memory disk in megabytes.
 .El
 .Ss PCI Passthrough Settings
+The
+.Xr ppt 4
+device driver must be attached to the
+PCI device being passed through.
+The device to pass through can be identified either by name or its
+host PCI bus location.
 .Bl -column "Name" "integer" "Default"
 .It Sy Name Ta Sy Format Ta Sy Default Ta Sy Description
 .It Va bus Ta integer Ta Ta
@@ -546,6 +552,10 @@ Host PCI bus address of device to pass through.
 Host PCI slot address of device to pass through.
 .It Va func Ta integer Ta Ta
 Host PCI function address of device to pass through.
+.It Va pptdev Ta string Ta Ta
+Name of a
+.Xr ppt 4
+device to pass through.
 .It Va rom Ta path Ta Ta
 ROM file of the device which will be executed by OVMF to init the device.
 .El
diff --git a/usr.sbin/bhyve/pci_passthru.c b/usr.sbin/bhyve/pci_passthru.c
index 0dfaf5908670..9940a13250ac 100644
--- a/usr.sbin/bhyve/pci_passthru.c
+++ b/usr.sbin/bhyve/pci_passthru.c
@@ -52,6 +52,7 @@ __FBSDID("$FreeBSD$");
 #ifndef WITHOUT_CAPSICUM
 #include <capsicum_helpers.h>
 #endif
+#include <ctype.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -132,7 +133,7 @@ pcifd_init(void)
 		errx(EX_OSERR, "Unable to apply rights for sandbox");
 
 	const cap_ioctl_t pcifd_ioctls[] = { PCIOCREAD, PCIOCWRITE, PCIOCGETBAR,
-		PCIOCBARIO, PCIOCBARMMAP };
+		PCIOCBARIO, PCIOCBARMMAP, PCIOCGETCONF };
 	if (caph_ioctls_limit(pcifd, pcifd_ioctls, nitems(pcifd_ioctls)) == -1)
 		errx(EX_OSERR, "Unable to apply rights for sandbox");
 #endif
@@ -652,30 +653,39 @@ done:
 static int
 passthru_legacy_config(nvlist_t *nvl, const char *opts)
 {
+	const char *cp;
+	char *tofree;
 	char value[16];
 	int bus, slot, func;
 
 	if (opts == NULL)
 		return (0);
 
-	if (sscanf(opts, "%d/%d/%d", &bus, &slot, &func) != 3) {
+	cp = strchr(opts, ',');
+
+	if (strncmp(opts, "ppt", strlen("ppt")) == 0) {
+		tofree = strndup(opts, cp - opts);
+		set_config_value_node(nvl, "pptdev", tofree);
+		free(tofree);
+	} else if (sscanf(opts, "pci0:%d:%d:%d", &bus, &slot, &func) == 3 ||
+	    sscanf(opts, "pci%d:%d:%d", &bus, &slot, &func) == 3 ||
+	    sscanf(opts, "%d/%d/%d", &bus, &slot, &func) == 3) {
+		snprintf(value, sizeof(value), "%d", bus);
+		set_config_value_node(nvl, "bus", value);
+		snprintf(value, sizeof(value), "%d", slot);
+		set_config_value_node(nvl, "slot", value);
+		snprintf(value, sizeof(value), "%d", func);
+		set_config_value_node(nvl, "func", value);
+	} else {
 		EPRINTLN("passthru: invalid options \"%s\"", opts);
 		return (-1);
 	}
 
-	snprintf(value, sizeof(value), "%d", bus);
-	set_config_value_node(nvl, "bus", value);
-	snprintf(value, sizeof(value), "%d", slot);
-	set_config_value_node(nvl, "slot", value);
-	snprintf(value, sizeof(value), "%d", func);
-	set_config_value_node(nvl, "func", value);
-
-	opts = strchr(opts, ',');
-	if (opts == NULL) {
+	if (cp == NULL) {
 		return (0);
 	}
 
-	return pci_parse_legacy_config(nvl, opts + 1);
+	return (pci_parse_legacy_config(nvl, cp + 1));
 }
 
 static int
@@ -729,6 +739,72 @@ passthru_init_rom(struct vmctx *const ctx, struct passthru_softc *const sc,
 	return (0);
 }
 
+static bool
+passthru_lookup_pptdev(const char *name, int *bus, int *slot, int *func)
+{
+	struct pci_conf_io pc;
+	struct pci_conf conf[1];
+	struct pci_match_conf patterns[1];
+	char *cp;
+
+	bzero(&pc, sizeof(struct pci_conf_io));
+	pc.match_buf_len = sizeof(conf);
+	pc.matches = conf;
+
+	bzero(&patterns, sizeof(patterns));
+
+	/*
+	 * The pattern structure requires the unit to be split out from
+	 * the driver name.  Walk backwards from the end of the name to
+	 * find the start of the unit.
+	 */
+	cp = strchr(name, '\0');
+	assert(cp != NULL);
+	while (cp != name && isdigit(cp[-1]))
+		cp--;
+	if (cp == name || !isdigit(*cp)) {
+		EPRINTLN("Invalid passthru device name %s", name);
+		return (false);
+	}
+	if ((size_t)(cp - name) + 1 > sizeof(patterns[0].pd_name)) {
+		EPRINTLN("Passthru device name %s is too long", name);
+		return (false);
+	}
+	memcpy(patterns[0].pd_name, name, cp - name);
+	patterns[0].pd_unit = strtol(cp, &cp, 10);
+	if (*cp != '\0') {
+		EPRINTLN("Invalid passthru device name %s", name);
+		return (false);
+	}
+	patterns[0].flags = PCI_GETCONF_MATCH_NAME | PCI_GETCONF_MATCH_UNIT;
+	pc.num_patterns = 1;
+	pc.pat_buf_len = sizeof(patterns);
+	pc.patterns = patterns;
+
+	if (ioctl(pcifd, PCIOCGETCONF, &pc) == -1) {
+		EPRINTLN("ioctl(PCIOCGETCONF): %s", strerror(errno));
+		return (false);
+	}
+	if (pc.status != PCI_GETCONF_LAST_DEVICE &&
+	    pc.status != PCI_GETCONF_MORE_DEVS) {
+		EPRINTLN("error returned from PCIOCGETCONF ioctl");
+		return (false);
+	}
+	if (pc.num_matches == 0) {
+		EPRINTLN("Passthru device %s not found", name);
+		return (false);
+	}
+
+	if (conf[0].pc_sel.pc_domain != 0) {
+		EPRINTLN("Passthru device %s on unsupported domain", name);
+		return (false);
+	}
+	*bus = conf[0].pc_sel.pc_bus;
+	*slot = conf[0].pc_sel.pc_dev;
+	*func = conf[0].pc_sel.pc_func;
+	return (true);
+}
+
 static int
 passthru_init(struct vmctx *ctx, struct pci_devinst *pi, nvlist_t *nvl)
 {
@@ -758,9 +834,15 @@ passthru_init(struct vmctx *ctx, struct pci_devinst *pi, nvlist_t *nvl)
 	var = atoi(value);						\
 } while (0)
 
-	GET_INT_CONFIG(bus, "bus");
-	GET_INT_CONFIG(slot, "slot");
-	GET_INT_CONFIG(func, "func");
+	value = get_config_value_node(nvl, "pptdev");
+	if (value != NULL) {
+		if (!passthru_lookup_pptdev(value, &bus, &slot, &func))
+			return (error);
+	} else {
+		GET_INT_CONFIG(bus, "bus");
+		GET_INT_CONFIG(slot, "slot");
+		GET_INT_CONFIG(func, "func");
+	}
 
 	if (vm_assign_pptdev(ctx, bus, slot, func) != 0) {
 		warnx("PCI device at %d/%d/%d is not using the ppt(4) driver",