Index: share/man/man4/pci.4 =================================================================== --- share/man/man4/pci.4 +++ share/man/man4/pci.4 @@ -492,6 +492,55 @@ pin identified by the tunable name. Mapping of IRQ values to platform interrupt sources is machine dependent. .El +.Sh DEVICE WIRING +You can wire the device unit at a given location with device.hints. +Entries of the form +.Va hints...at="pci::" +or +.Va hints...at="pci:::" +will force the driver +.Va name +to probe and attach at unit +.Va unit +for any PCI device found to match the specification, where: +.Bl -tag -width -indent +.It +The domain +.Pq or segment +of the PCI device in decimal. +Defaults to 0 if unspecified +.It +The bus address of the PCI device in decimal. +.It +The slot of the PCI device in decimal. +.It +The function of the PCI device in decimal. +.El +.Pp +The code to do the matching requires an exact string match. +Do not specify the angle brackets +.Pq < > +in the hints file. +Wiring multiple devices to the same +.Va name +and +.Va unit +produces undefined results. +.Ss Examples +Given the following lines in +.Pa /boot/device.hints : +.Cd hint.nvme.3.at="pci6:0:0" +.Cd hint.igb.8.at="pci14:0:0" +If there's a device that supports igb at PCI bus 14 slot 0 function 0, +then it will be assigned igb8 for probe and attach. +Likewise, if there's an NVMe card at PCI bus 6 slot 0 function 0, +then it will be assigned nvme3 for probe and attach. +If another type of card is in either of these locations, the name and +unit of that card will be the default names and will be unaffected by +these hints. +If other igb or nvme cards are located elsewhere, they will be +assigned their unit numbers sequentially, skipping the unit numbers +that have 'at' hints. .Sh FILES .Bl -tag -width /dev/pci -compact .It Pa /dev/pci Index: sys/cam/cam_ccb.h =================================================================== --- sys/cam/cam_ccb.h +++ sys/cam/cam_ccb.h @@ -633,6 +633,11 @@ struct ccb_pathinq_settings_nvme { uint32_t nsid; /* Namespace ID for this path */ + uint32_t domain; + uint8_t bus; + uint8_t slot; + uint8_t function; + uint8_t extra; }; #define PATHINQ_SETTINGS_SIZE 128 Index: sys/dev/acpica/acpi.c =================================================================== --- sys/dev/acpica/acpi.c +++ sys/dev/acpica/acpi.c @@ -1019,7 +1019,8 @@ break; /* Must have an "at" for acpi or isa. */ - resource_string_value(name, unit, "at", &s); + if (resource_string_value(name, unit, "at", &s) != 0) + continue; if (!(strcmp(s, "acpi0") == 0 || strcmp(s, "acpi") == 0 || strcmp(s, "isa0") == 0 || strcmp(s, "isa") == 0)) continue; Index: sys/dev/nvme/nvme_sim.c =================================================================== --- sys/dev/nvme/nvme_sim.c +++ sys/dev/nvme/nvme_sim.c @@ -182,7 +182,8 @@ break; case XPT_PATH_INQ: /* Path routing inquiry */ { - struct ccb_pathinq *cpi = &ccb->cpi; + struct ccb_pathinq *cpi = &ccb->cpi; + device_t dev = ctrlr->dev; /* * NVMe may have multiple LUNs on the same path. Current generation @@ -210,6 +211,11 @@ cpi->protocol = PROTO_NVME; cpi->protocol_version = nvme_mmio_read_4(ctrlr, vs); cpi->xport_specific.nvme.nsid = ns->id; + cpi->xport_specific.nvme.domain = pci_get_domain(dev); + cpi->xport_specific.nvme.bus = pci_get_bus(dev); + cpi->xport_specific.nvme.slot = pci_get_slot(dev); + cpi->xport_specific.nvme.function = pci_get_function(dev); + cpi->xport_specific.nvme.extra = 0; cpi->ccb_h.status = CAM_REQ_CMP; break; } Index: sys/dev/pci/pci.c =================================================================== --- sys/dev/pci/pci.c +++ sys/dev/pci/pci.c @@ -123,6 +123,8 @@ static void pci_resume_msix(device_t dev); static int pci_remap_intr_method(device_t bus, device_t dev, u_int irq); +static void pci_hint_device_unit(device_t acdev, device_t child, + const char *name, int *unitp); static int pci_get_id_method(device_t dev, device_t child, enum pci_id_type type, uintptr_t *rid); @@ -162,6 +164,7 @@ DEVMETHOD(bus_child_detached, pci_child_detached), DEVMETHOD(bus_child_pnpinfo_str, pci_child_pnpinfo_str_method), DEVMETHOD(bus_child_location_str, pci_child_location_str_method), + DEVMETHOD(bus_hint_device_unit, pci_hint_device_unit), DEVMETHOD(bus_remap_intr, pci_remap_intr_method), DEVMETHOD(bus_suspend_child, pci_suspend_child), DEVMETHOD(bus_resume_child, pci_resume_child), @@ -4220,6 +4223,39 @@ return (device_delete_children(dev)); } +static void +pci_hint_device_unit(device_t dev, device_t child, const char *name, int *unitp) +{ + int line, unit; + const char *at; + char me1[20], me2[30]; + uint8_t b, s, f; + uint32_t d; + + line = 0; + d = pci_get_domain(child); + b = pci_get_bus(child); + s = pci_get_slot(child); + f = pci_get_function(child); + /* + * asprintf would be handier and more stack friendly, but + * we get called when WAITOK is not allowed. + */ + snprintf(me1, sizeof(me1), "pci%u:%u:%u", b, s, f); + snprintf(me2, sizeof(me2), "pci%u:%u:%u:%u", d, b, s, f); + for (;;) { + if (resource_find_dev(&line, name, &unit, "at", NULL) != 0) + break; + if (resource_string_value(name, unit, "at", &at) != 0) + continue; + if (strcmp(at, me1) != 0 && + strcmp(at, me2) != 0) + continue; + *unitp = unit; + break; + } +} + static void pci_set_power_child(device_t dev, device_t child, int state) {