]> mj.ucw.cz Git - pciutils.git/commitdiff
libpci: Add support for filling bridge resources
authorPali Rohár <pali@kernel.org>
Sat, 20 Nov 2021 13:36:40 +0000 (14:36 +0100)
committerMartin Mares <mj@ucw.cz>
Thu, 10 Feb 2022 12:49:35 +0000 (13:49 +0100)
Extend libpci API and ABI to fill bridge resources from sysfs.

lib/access.c
lib/caps.c
lib/filter.c
lib/internal.h
lib/libpci.ver
lib/pci.h
lib/sysfs.c

index 9bd69891dde5c082ce1d81fe250248d1a8c153d2..b8fdbe2319b5a717ddadd6eda22dc9e039205508 100644 (file)
@@ -189,7 +189,7 @@ pci_reset_properties(struct pci_dev *d)
 }
 
 int
-pci_fill_info_v35(struct pci_dev *d, int flags)
+pci_fill_info_v38(struct pci_dev *d, int flags)
 {
   unsigned int uflags = flags;
   if (uflags & PCI_FILL_RESCAN)
@@ -203,19 +203,21 @@ pci_fill_info_v35(struct pci_dev *d, int flags)
 }
 
 /* In version 3.1, pci_fill_info got new flags => versioned alias */
-/* In versions 3.2, 3.3, 3.4 and 3.5, the same has happened */
-STATIC_ALIAS(int pci_fill_info(struct pci_dev *d, int flags), pci_fill_info_v35(d, flags));
-DEFINE_ALIAS(int pci_fill_info_v30(struct pci_dev *d, int flags), pci_fill_info_v35);
-DEFINE_ALIAS(int pci_fill_info_v31(struct pci_dev *d, int flags), pci_fill_info_v35);
-DEFINE_ALIAS(int pci_fill_info_v32(struct pci_dev *d, int flags), pci_fill_info_v35);
-DEFINE_ALIAS(int pci_fill_info_v33(struct pci_dev *d, int flags), pci_fill_info_v35);
-DEFINE_ALIAS(int pci_fill_info_v34(struct pci_dev *d, int flags), pci_fill_info_v35);
+/* In versions 3.2, 3.3, 3.4, 3.5 and 3.8, the same has happened */
+STATIC_ALIAS(int pci_fill_info(struct pci_dev *d, int flags), pci_fill_info_v38(d, flags));
+DEFINE_ALIAS(int pci_fill_info_v30(struct pci_dev *d, int flags), pci_fill_info_v38);
+DEFINE_ALIAS(int pci_fill_info_v31(struct pci_dev *d, int flags), pci_fill_info_v38);
+DEFINE_ALIAS(int pci_fill_info_v32(struct pci_dev *d, int flags), pci_fill_info_v38);
+DEFINE_ALIAS(int pci_fill_info_v33(struct pci_dev *d, int flags), pci_fill_info_v38);
+DEFINE_ALIAS(int pci_fill_info_v34(struct pci_dev *d, int flags), pci_fill_info_v38);
+DEFINE_ALIAS(int pci_fill_info_v35(struct pci_dev *d, int flags), pci_fill_info_v38);
 SYMBOL_VERSION(pci_fill_info_v30, pci_fill_info@LIBPCI_3.0);
 SYMBOL_VERSION(pci_fill_info_v31, pci_fill_info@LIBPCI_3.1);
 SYMBOL_VERSION(pci_fill_info_v32, pci_fill_info@LIBPCI_3.2);
 SYMBOL_VERSION(pci_fill_info_v33, pci_fill_info@LIBPCI_3.3);
 SYMBOL_VERSION(pci_fill_info_v34, pci_fill_info@LIBPCI_3.4);
-SYMBOL_VERSION(pci_fill_info_v35, pci_fill_info@@LIBPCI_3.5);
+SYMBOL_VERSION(pci_fill_info_v35, pci_fill_info@LIBPCI_3.5);
+SYMBOL_VERSION(pci_fill_info_v38, pci_fill_info@@LIBPCI_3.8);
 
 void
 pci_setup_cache(struct pci_dev *d, byte *cache, int len)
index 3c025a98347debabe0867822f1735dbd86cca72f..039c86f7e206a6bf5ec7a7bb0b85491a65eaacfa 100644 (file)
@@ -128,7 +128,7 @@ pci_find_cap_nr(struct pci_dev *d, unsigned int id, unsigned int type,
   unsigned int target = (cap_number ? *cap_number : 0);
   unsigned int index = 0;
 
-  pci_fill_info_v35(d, ((type == PCI_CAP_NORMAL) ? PCI_FILL_CAPS : PCI_FILL_EXT_CAPS));
+  pci_fill_info_v38(d, ((type == PCI_CAP_NORMAL) ? PCI_FILL_CAPS : PCI_FILL_EXT_CAPS));
 
   for (c=d->first_cap; c; c=c->next)
     {
index 573fb28103637f65d98411c9602bcb1899816471..195f813193c49f5f72906832e98132d48271f7fc 100644 (file)
@@ -129,7 +129,7 @@ pci_filter_match_v33(struct pci_filter *f, struct pci_dev *d)
     return 0;
   if (f->device >= 0 || f->vendor >= 0)
     {
-      pci_fill_info_v35(d, PCI_FILL_IDENT);
+      pci_fill_info_v38(d, PCI_FILL_IDENT);
       if ((f->device >= 0 && f->device != d->device_id) ||
          (f->vendor >= 0 && f->vendor != d->vendor_id))
        return 0;
index 942e3cb7feafa8f45f0b2ec3e31bcd413b9b5b38..a1c8cc83129299e61e3500fb916654c36a878f29 100644 (file)
@@ -74,6 +74,7 @@ int pci_fill_info_v32(struct pci_dev *, int flags) VERSIONED_ABI;
 int pci_fill_info_v33(struct pci_dev *, int flags) VERSIONED_ABI;
 int pci_fill_info_v34(struct pci_dev *, int flags) VERSIONED_ABI;
 int pci_fill_info_v35(struct pci_dev *, int flags) VERSIONED_ABI;
+int pci_fill_info_v38(struct pci_dev *, int flags) VERSIONED_ABI;
 
 static inline int want_fill(struct pci_dev *d, unsigned want_fields, unsigned int try_fields)
 {
@@ -87,6 +88,11 @@ static inline int want_fill(struct pci_dev *d, unsigned want_fields, unsigned in
     }
 }
 
+static inline void clear_fill(struct pci_dev *d, unsigned clear_fields)
+{
+  d->known_fields &= ~clear_fields;
+}
+
 struct pci_property {
   struct pci_property *next;
   u32 key;
index e20c3f581c711ff9cb55042e62e60c8d7325a065..73f7fa71e3576ce883524e238bc6067e0b2b5aed 100644 (file)
@@ -82,3 +82,8 @@ LIBPCI_3.7 {
        global:
                pci_find_cap_nr;
 };
+
+LIBPCI_3.8 {
+       global:
+               pci_fill_info;
+};
index 328a468a7bf918c13beccb30881a4b85d07cb3ca..b9fd9bfb9b5b4b55e2d26aea5ec520e856afe3cf 100644 (file)
--- a/lib/pci.h
+++ b/lib/pci.h
@@ -142,6 +142,9 @@ struct pci_dev {
   pciaddr_t flags[6];                  /* PCI_IORESOURCE_* flags for regions */
   pciaddr_t rom_flags;                 /* PCI_IORESOURCE_* flags for expansion ROM */
   int domain;                          /* PCI domain (host bridge) */
+  pciaddr_t bridge_base_addr[4];       /* Bridge base addresses (without flags) */
+  pciaddr_t bridge_size[4];            /* Bridge sizes */
+  pciaddr_t bridge_flags[4];           /* PCI_IORESOURCE_* flags for bridge addresses */
 
   /* Fields used internally */
   struct pci_access *access;
@@ -205,6 +208,7 @@ char *pci_get_string_property(struct pci_dev *d, u32 prop) PCI_ABI;
 #define PCI_FILL_IO_FLAGS      0x1000
 #define PCI_FILL_DT_NODE       0x2000          /* Device tree node */
 #define PCI_FILL_IOMMU_GROUP   0x4000
+#define PCI_FILL_BRIDGE_BASES  0x8000
 #define PCI_FILL_RESCAN                0x00010000
 
 void pci_setup_cache(struct pci_dev *, u8 *cache, int len) PCI_ABI;
index 4b8b2cdfb0c608b9cf817300243ebdabce107f6e..fe3f0e5ece534369d833ba73ece7861b901fea07 100644 (file)
@@ -153,14 +153,17 @@ sysfs_get_resources(struct pci_dev *d)
 {
   struct pci_access *a = d->access;
   char namebuf[OBJNAMELEN], buf[256];
+  struct { pciaddr_t flags, base_addr, size; } lines[10];
+  int have_bar_bases, have_rom_base, have_bridge_bases;
   FILE *file;
   int i;
 
+  have_bar_bases = have_rom_base = have_bridge_bases = 0;
   sysfs_obj_name(d, "resource", namebuf);
   file = fopen(namebuf, "r");
   if (!file)
     a->error("Cannot open %s: %s", namebuf, strerror(errno));
-  for (i = 0; i < 7; i++)
+  for (i = 0; i < 7+6+4+1; i++)
     {
       unsigned long long start, end, size, flags;
       if (!fgets(buf, sizeof(buf), file))
@@ -177,16 +180,55 @@ sysfs_get_resources(struct pci_dev *d)
          flags &= PCI_ADDR_FLAG_MASK;
          d->base_addr[i] = start | flags;
          d->size[i] = size;
+         have_bar_bases = 1;
        }
-      else
+      else if (i == 6)
        {
          d->rom_flags = flags;
          flags &= PCI_ADDR_FLAG_MASK;
          d->rom_base_addr = start | flags;
          d->rom_size = size;
+         have_rom_base = 1;
        }
+      else if (i < 7+6+4)
+        {
+          /*
+           * If kernel was compiled without CONFIG_PCI_IOV option then after
+           * the ROM line for configured bridge device (that which had set
+           * subordinary bus number to non-zero value) are four additional lines
+           * which describe resources behind bridge. For PCI-to-PCI bridges they
+           * are: IO, MEM, PREFMEM and empty. For CardBus bridges they are: IO0,
+           * IO1, MEM0 and MEM1. For unconfigured bridges and other devices
+           * there is no additional line after the ROM line. If kernel was
+           * compiled with CONFIG_PCI_IOV option then after the ROM line and
+           * before the first bridge resource line are six additional lines
+           * which describe IOV resources. Read all remaining lines in resource
+           * file and based on the number of remaining lines (0, 4, 6, 10) parse
+           * resources behind bridge.
+           */
+          lines[i-7].flags = flags;
+          lines[i-7].base_addr = start;
+          lines[i-7].size = size;
+        }
+    }
+  if (i == 7+4 || i == 7+6+4)
+    {
+      int offset = (i == 7+6+4) ? 6 : 0;
+      for (i = 0; i < 4; i++)
+        {
+          d->bridge_flags[i] = lines[offset+i].flags;
+          d->bridge_base_addr[i] = lines[offset+i].base_addr;
+          d->bridge_size[i] = lines[offset+i].size;
+        }
+      have_bridge_bases = 1;
     }
   fclose(file);
+  if (!have_bar_bases)
+    clear_fill(d, PCI_FILL_BASES | PCI_FILL_SIZES | PCI_FILL_IO_FLAGS);
+  if (!have_rom_base)
+    clear_fill(d, PCI_FILL_ROM_BASE);
+  if (!have_bridge_bases)
+    clear_fill(d, PCI_FILL_BRIDGE_BASES);
 }
 
 static void sysfs_scan(struct pci_access *a)
@@ -307,7 +349,7 @@ sysfs_fill_info(struct pci_dev *d, unsigned int flags)
        d->device_class = sysfs_get_value(d, "class", 1) >> 8;
       if (want_fill(d, flags, PCI_FILL_IRQ))
          d->irq = sysfs_get_value(d, "irq", 1);
-      if (want_fill(d, flags, PCI_FILL_BASES | PCI_FILL_ROM_BASE | PCI_FILL_SIZES | PCI_FILL_IO_FLAGS))
+      if (want_fill(d, flags, PCI_FILL_BASES | PCI_FILL_ROM_BASE | PCI_FILL_SIZES | PCI_FILL_IO_FLAGS | PCI_FILL_BRIDGE_BASES))
          sysfs_get_resources(d);
     }