diff --git a/usr.sbin/bhyve/bhyve.8 b/usr.sbin/bhyve/bhyve.8 --- a/usr.sbin/bhyve/bhyve.8 +++ b/usr.sbin/bhyve/bhyve.8 @@ -24,7 +24,7 @@ .\" .\" $FreeBSD$ .\" -.Dd November 28, 2021 +.Dd August 19, 2022 .Dt BHYVE 8 .Os .Sh NAME @@ -390,6 +390,7 @@ .Xc .El .Sm on +.Pp If .Cm mac is not specified, the MAC address is derived from a fixed OUI and the @@ -520,14 +521,32 @@ .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 --- 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 June 30, 2022 +.Dd August 19, 2022 .Dt BHYVE_CONFIG 5 .Os .Sh NAME @@ -575,6 +575,12 @@ 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 @@ -583,6 +589,10 @@ 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 --- a/usr.sbin/bhyve/pci_passthru.c +++ b/usr.sbin/bhyve/pci_passthru.c @@ -52,6 +52,7 @@ #ifndef WITHOUT_CAPSICUM #include #endif +#include #include #include #include @@ -132,7 +133,7 @@ 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 @@ -645,30 +646,39 @@ 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 @@ -722,6 +732,72 @@ 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) { @@ -751,9 +827,15 @@ 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",