diff mbox series

[pciutils,4/4] lspci: Use PCI_FILL_BRIDGE_BASES to detect if range behind bridge is disabled or unsupported

Message ID 20211220155448.1233-4-pali@kernel.org
State New
Headers show
Series [pciutils,1/4] lspci: Show 16/32/64 bit width for address ranges behind bridge | expand

Commit Message

Pali Rohár Dec. 20, 2021, 3:54 p.m. UTC
Show resources behind bridge as reported by PCI_FILL_BRIDGE_BASES.

I/O or Prefetchable memory behind bridge is unsupported by bridge if both
base and limit bridge registers are read-only and returns zero. So if base
and limit registers returns zero (which is valid enabled range) and kernel
reports that particular resource is disabled it means that resource is
unsupported. Both I/O or Prefetchable memory resources are only optional.
---
 lspci.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++---------
 1 file changed, 49 insertions(+), 9 deletions(-)
diff mbox series

Patch

diff --git a/lspci.c b/lspci.c
index 67ac19b61a29..d14d1b9185d6 100644
--- a/lspci.c
+++ b/lspci.c
@@ -374,12 +374,12 @@  show_size(u64 x)
 }
 
 static void
-show_range(char *prefix, u64 base, u64 limit, int bits)
+show_range(char *prefix, u64 base, u64 limit, int bits, int disabled)
 {
   printf("%s:", prefix);
   if (base <= limit || verbose > 2)
     printf(" %0*" PCI_U64_FMT_X "-%0*" PCI_U64_FMT_X, (bits+3)/4, base, (bits+3)/4, limit);
-  if (base <= limit)
+  if (!disabled && base <= limit)
     show_size(limit - base + 1);
   else
     printf(" [disabled]");
@@ -543,6 +543,7 @@  show_htype0(struct device *d)
 static void
 show_htype1(struct device *d)
 {
+  struct pci_dev *p = d->dev;
   u32 io_base = get_conf_byte(d, PCI_IO_BASE);
   u32 io_limit = get_conf_byte(d, PCI_IO_LIMIT);
   u32 io_type = io_base & PCI_IO_RANGE_TYPE_MASK;
@@ -554,6 +555,10 @@  show_htype1(struct device *d)
   u32 pref_type = pref_base & PCI_PREF_RANGE_TYPE_MASK;
   word sec_stat = get_conf_word(d, PCI_SEC_STATUS);
   word brc = get_conf_word(d, PCI_BRIDGE_CONTROL);
+  int io_disabled = (p->known_fields & PCI_FILL_BRIDGE_BASES) && !p->bridge_size[0];
+  int mem_disabled = (p->known_fields & PCI_FILL_BRIDGE_BASES) && !p->bridge_size[1];
+  int pref_disabled = (p->known_fields & PCI_FILL_BRIDGE_BASES) && !p->bridge_size[2];
+  int io_bits, pref_bits;
 
   show_bases(d, 2);
   printf("\tBus: primary=%02x, secondary=%02x, subordinate=%02x, sec-latency=%d\n",
@@ -562,7 +567,15 @@  show_htype1(struct device *d)
 	 get_conf_byte(d, PCI_SUBORDINATE_BUS),
 	 get_conf_byte(d, PCI_SEC_LATENCY_TIMER));
 
-  if (io_type != (io_limit & PCI_IO_RANGE_TYPE_MASK) ||
+  if ((p->known_fields & PCI_FILL_BRIDGE_BASES) && !io_disabled)
+    {
+      io_base = p->bridge_base_addr[0] & PCI_IO_RANGE_MASK;
+      io_limit = io_base + p->bridge_size[0] - 1;
+      io_type = p->bridge_base_addr[0] & PCI_IO_RANGE_TYPE_MASK;
+      io_bits = (io_type == PCI_IO_RANGE_TYPE_32) ? 32 : 16;
+      show_range("\tI/O behind bridge", io_base, io_limit, io_bits, io_disabled);
+    }
+  else if (io_type != (io_limit & PCI_IO_RANGE_TYPE_MASK) ||
       (io_type != PCI_IO_RANGE_TYPE_16 && io_type != PCI_IO_RANGE_TYPE_32))
     printf("\t!!! Unknown I/O range types %x/%x\n", io_base, io_limit);
   else
@@ -574,20 +587,40 @@  show_htype1(struct device *d)
 	  io_base |= (get_conf_word(d, PCI_IO_BASE_UPPER16) << 16);
 	  io_limit |= (get_conf_word(d, PCI_IO_LIMIT_UPPER16) << 16);
 	}
-      show_range("\tI/O behind bridge", io_base, io_limit+0xfff, (io_type == PCI_IO_RANGE_TYPE_32) ? 32 : 16);
+      /* I/O is unsupported if both base and limit are zeros and resource is disabled */
+      if (!(io_base == 0x0 && io_limit == 0x0 && io_disabled))
+        {
+          io_limit += 0xfff;
+          io_bits = (io_type == PCI_IO_RANGE_TYPE_32) ? 32 : 16;
+          show_range("\tI/O behind bridge", io_base, io_limit, io_bits, io_disabled);
+        }
     }
 
-  if (mem_type != (mem_limit & PCI_MEMORY_RANGE_TYPE_MASK) ||
+  if ((p->known_fields & PCI_FILL_BRIDGE_BASES) && !mem_disabled)
+    {
+      mem_base = p->bridge_base_addr[1] & PCI_MEMORY_RANGE_MASK;
+      mem_limit = mem_base + p->bridge_size[1] - 1;
+      show_range("\tMemory behind bridge", mem_base, mem_limit, 32, mem_disabled);
+    }
+  else if (mem_type != (mem_limit & PCI_MEMORY_RANGE_TYPE_MASK) ||
       mem_type)
     printf("\t!!! Unknown memory range types %x/%x\n", mem_base, mem_limit);
   else
     {
       mem_base = (mem_base & PCI_MEMORY_RANGE_MASK) << 16;
       mem_limit = (mem_limit & PCI_MEMORY_RANGE_MASK) << 16;
-      show_range("\tMemory behind bridge", mem_base, mem_limit + 0xfffff, 32);
+      show_range("\tMemory behind bridge", mem_base, mem_limit + 0xfffff, 32, mem_disabled);
     }
 
-  if (pref_type != (pref_limit & PCI_PREF_RANGE_TYPE_MASK) ||
+  if ((p->known_fields & PCI_FILL_BRIDGE_BASES) && !pref_disabled)
+    {
+      u64 pref_base_64 = p->bridge_base_addr[2] & PCI_MEMORY_RANGE_MASK;
+      u64 pref_limit_64 = pref_base_64 + p->bridge_size[2] - 1;
+      pref_type = p->bridge_base_addr[2] & PCI_MEMORY_RANGE_TYPE_MASK;
+      pref_bits = (pref_type == PCI_PREF_RANGE_TYPE_64) ? 64 : 32;
+      show_range("\tPrefetchable memory behind bridge", pref_base_64, pref_limit_64, pref_bits, pref_disabled);
+    }
+  else if (pref_type != (pref_limit & PCI_PREF_RANGE_TYPE_MASK) ||
       (pref_type != PCI_PREF_RANGE_TYPE_32 && pref_type != PCI_PREF_RANGE_TYPE_64))
     printf("\t!!! Unknown prefetchable memory range types %x/%x\n", pref_base, pref_limit);
   else
@@ -599,7 +632,13 @@  show_htype1(struct device *d)
 	  pref_base_64 |= (u64) get_conf_long(d, PCI_PREF_BASE_UPPER32) << 32;
 	  pref_limit_64 |= (u64) get_conf_long(d, PCI_PREF_LIMIT_UPPER32) << 32;
 	}
-      show_range("\tPrefetchable memory behind bridge", pref_base_64, pref_limit_64 + 0xfffff, (pref_type == PCI_PREF_RANGE_TYPE_64) ? 64 : 32);
+      /* Prefetchable memory is unsupported if both base and limit are zeros and resource is disabled */
+      if (!(pref_base_64 == 0x0 && pref_limit_64 == 0x0 && pref_disabled))
+        {
+          pref_limit_64 += 0xfffff;
+          pref_bits = (pref_type == PCI_PREF_RANGE_TYPE_64) ? 64 : 32;
+          show_range("\tPrefetchable memory behind bridge", pref_base_64, pref_limit_64, pref_bits, pref_disabled);
+        }
     }
 
   if (verbose > 1)
@@ -726,7 +765,8 @@  show_verbose(struct device *d)
   show_terse(d);
 
   pci_fill_info(p, PCI_FILL_IRQ | PCI_FILL_BASES | PCI_FILL_ROM_BASE | PCI_FILL_SIZES |
-    PCI_FILL_PHYS_SLOT | PCI_FILL_NUMA_NODE | PCI_FILL_DT_NODE | PCI_FILL_IOMMU_GROUP);
+    PCI_FILL_PHYS_SLOT | PCI_FILL_NUMA_NODE | PCI_FILL_DT_NODE | PCI_FILL_IOMMU_GROUP |
+    PCI_FILL_BRIDGE_BASES);
   irq = p->irq;
 
   switch (htype)