summaryrefslogtreecommitdiff
path: root/sys/dev
diff options
context:
space:
mode:
authorMark Kettenis <kettenis@cvs.openbsd.org>2022-06-17 10:08:37 +0000
committerMark Kettenis <kettenis@cvs.openbsd.org>2022-06-17 10:08:37 +0000
commita301b6717015db9037e28c3de0f300b5b8a70f2d (patch)
tree0405f94cf4e81e9f8c34735791b28f3cdc4850af /sys/dev
parentaf70baed0e23b069a6f8855780cf2eec822188cb (diff)
Only probe device 0 on PCI busses corresponding to a PCIe root port or a
PCIe switch/bridge downstream port. This fixes an issue where a device would show up 32 times if the PCIe port does not terminate configuration requests targeted at device numbers 1-31 as required by the spec and the PCIe device does not filter requests based on its own device number. Linux, NetBSD and FreeBSD (to some extent) already do this. This should fix an issue seen on Ampere Altra where the BMC's PCI bridge and USB controller would show up multiple times. ok jsg@
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/pci/pci.c26
-rw-r--r--sys/dev/pci/pcireg.h6
2 files changed, 28 insertions, 4 deletions
diff --git a/sys/dev/pci/pci.c b/sys/dev/pci/pci.c
index 2e5da270960..5f214d49d33 100644
--- a/sys/dev/pci/pci.c
+++ b/sys/dev/pci/pci.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: pci.c,v 1.124 2022/03/11 18:00:51 mpi Exp $ */
+/* $OpenBSD: pci.c,v 1.125 2022/06/17 10:08:36 kettenis Exp $ */
/* $NetBSD: pci.c,v 1.31 1997/06/06 23:48:04 thorpej Exp $ */
/*
@@ -807,11 +807,31 @@ pci_enumerate_bus(struct pci_softc *sc,
{
pci_chipset_tag_t pc = sc->sc_pc;
int device, function, nfunctions, ret;
+ int maxndevs = sc->sc_maxndevs;
const struct pci_quirkdata *qd;
- pcireg_t id, bhlcr;
+ pcireg_t id, bhlcr, cap;
pcitag_t tag;
- for (device = 0; device < sc->sc_maxndevs; device++) {
+ /*
+ * PCIe downstream ports and root ports should only forward
+ * configuration requests for device number 0. However, not
+ * all hardware implements this correctly, and some devices
+ * will respond to other device numbers making the device show
+ * up 32 times. Prevent this by only scanning a single
+ * device.
+ */
+ if (sc->sc_bridgetag && pci_get_capability(pc, *sc->sc_bridgetag,
+ PCI_CAP_PCIEXPRESS, NULL, &cap)) {
+ switch (PCI_PCIE_XCAP_TYPE(cap)) {
+ case PCI_PCIE_XCAP_TYPE_RP:
+ case PCI_PCIE_XCAP_TYPE_DOWN:
+ case PCI_PCIE_XCAP_TYPE_PCI2PCIE:
+ maxndevs = 1;
+ break;
+ }
+ }
+
+ for (device = 0; device < maxndevs; device++) {
tag = pci_make_tag(pc, sc->sc_bus, device, 0);
bhlcr = pci_conf_read(pc, tag, PCI_BHLC_REG);
diff --git a/sys/dev/pci/pcireg.h b/sys/dev/pci/pcireg.h
index 95cf74a18db..53124eccecb 100644
--- a/sys/dev/pci/pcireg.h
+++ b/sys/dev/pci/pcireg.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: pcireg.h,v 1.60 2021/12/31 11:24:24 jsg Exp $ */
+/* $OpenBSD: pcireg.h,v 1.61 2022/06/17 10:08:36 kettenis Exp $ */
/* $NetBSD: pcireg.h,v 1.26 2000/05/10 16:58:42 thorpej Exp $ */
/*
@@ -563,6 +563,10 @@ typedef u_int8_t pci_revision_t;
#define PCI_PCIE_XCAP 0x00
#define PCI_PCIE_XCAP_SI 0x01000000
#define PCI_PCIE_XCAP_VER(x) (((x) >> 16) & 0x0f)
+#define PCI_PCIE_XCAP_TYPE(x) (((x) >> 20) & 0x0f)
+#define PCI_PCIE_XCAP_TYPE_RP 0x4
+#define PCI_PCIE_XCAP_TYPE_DOWN 0x6
+#define PCI_PCIE_XCAP_TYPE_PCI2PCIE 0x8
#define PCI_PCIE_DCAP 0x04
#define PCI_PCIE_DCSR 0x08
#define PCI_PCIE_DCSR_ERO 0x00000010