]> mj.ucw.cz Git - pciutils.git/blobdiff - lspci.c
lib: fixup DOE status register bit
[pciutils.git] / lspci.c
diff --git a/lspci.c b/lspci.c
index f52e7924199484ea7aa142bf9af7b86f2accc39a..9452cd31a2b0a830f876fd11e13239fd625f9106 100644 (file)
--- a/lspci.c
+++ b/lspci.c
@@ -3,7 +3,9 @@
  *
  *     Copyright (c) 1997--2020 Martin Mares <mj@ucw.cz>
  *
- *     Can be freely distributed and used under the terms of the GNU GPL.
+ *     Can be freely distributed and used under the terms of the GNU GPL v2+.
+ *
+ *     SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 #include <stdio.h>
@@ -124,18 +126,18 @@ scan_device(struct pci_dev *p)
   d = xmalloc(sizeof(struct device));
   memset(d, 0, sizeof(*d));
   d->dev = p;
+  d->no_config_access = p->no_config_access;
   d->config_cached = d->config_bufsize = 64;
   d->config = xmalloc(64);
   d->present = xmalloc(64);
   memset(d->present, 1, 64);
-  if (!pci_read_block(p, 0, d->config, 64))
+  if (!d->no_config_access && !pci_read_block(p, 0, d->config, 64))
     {
-      fprintf(stderr, "lspci: Unable to read the standard configuration space header of device %04x:%02x:%02x.%d\n",
-             p->domain, p->bus, p->dev, p->func);
-      seen_errors++;
-      return NULL;
+      d->no_config_access = 1;
+      d->config_cached = d->config_bufsize = 0;
+      memset(d->present, 0, 64);
     }
-  if ((d->config[PCI_HEADER_TYPE] & 0x7f) == PCI_HEADER_TYPE_CARDBUS)
+  if (!d->no_config_access && (d->config[PCI_HEADER_TYPE] & 0x7f) == PCI_HEADER_TYPE_CARDBUS)
     {
       /* For cardbus bridges, we need to fetch 64 bytes more to get the
        * full standard header... */
@@ -143,7 +145,7 @@ scan_device(struct pci_dev *p)
        d->config_cached += 64;
     }
   pci_setup_cache(p, d->config, d->config_cached);
-  pci_fill_info(p, PCI_FILL_IDENT | PCI_FILL_CLASS | PCI_FILL_CLASS_EXT | PCI_FILL_SUBSYS);
+  pci_fill_info(p, PCI_FILL_IDENT | PCI_FILL_CLASS | PCI_FILL_CLASS_EXT | PCI_FILL_SUBSYS | (need_topology ? PCI_FILL_PARENT : 0));
   return d;
 }
 
@@ -290,7 +292,7 @@ show_terse(struct device *d)
 {
   int c;
   struct pci_dev *p = d->dev;
-  char classbuf[128], devbuf[128];
+  char classbuf[256], devbuf[256];
 
   show_slot_name(d);
   printf(" %s: %s",
@@ -354,7 +356,7 @@ show_size(u64 x)
 }
 
 static void
-show_range(char *prefix, u64 base, u64 limit, int bits, int disabled)
+show_range(const char *prefix, u64 base, u64 limit, int bits, int disabled)
 {
   printf("%s:", prefix);
   if (base <= limit || verbose > 2)
@@ -363,27 +365,51 @@ show_range(char *prefix, u64 base, u64 limit, int bits, int disabled)
     show_size(limit - base + 1);
   else
     printf(" [disabled]");
-  printf(" [%d-bit]", bits);
+  if (bits)
+    printf(" [%d-bit]", bits);
   putchar('\n');
 }
 
+static u32
+ioflg_to_pciflg(pciaddr_t ioflg)
+{
+  u32 flg;
+
+  if (ioflg & PCI_IORESOURCE_IO)
+    flg = PCI_BASE_ADDRESS_SPACE_IO;
+  else if (!(ioflg & PCI_IORESOURCE_MEM))
+    flg = 0;
+  else
+    {
+      flg = PCI_BASE_ADDRESS_SPACE_MEMORY;
+      if (ioflg & PCI_IORESOURCE_MEM_64)
+        flg |= PCI_BASE_ADDRESS_MEM_TYPE_64;
+      else
+        flg |= PCI_BASE_ADDRESS_MEM_TYPE_32;
+      if (ioflg & PCI_IORESOURCE_PREFETCH)
+        flg |= PCI_BASE_ADDRESS_MEM_PREFETCH;
+    }
+
+  return flg;
+}
+
 static void
-show_bases(struct device *d, int cnt)
+show_bases(struct device *d, int cnt, int without_config_data)
 {
   struct pci_dev *p = d->dev;
-  word cmd = get_conf_word(d, PCI_COMMAND);
+  word cmd = without_config_data ? (PCI_COMMAND_IO | PCI_COMMAND_MEMORY) : get_conf_word(d, PCI_COMMAND);
   int i;
-  int virtual = 0;
 
   for (i=0; i<cnt; i++)
     {
       pciaddr_t pos = p->base_addr[i];
       pciaddr_t len = (p->known_fields & PCI_FILL_SIZES) ? p->size[i] : 0;
       pciaddr_t ioflg = (p->known_fields & PCI_FILL_IO_FLAGS) ? p->flags[i] : 0;
-      u32 flg = get_conf_long(d, PCI_BASE_ADDRESS_0 + 4*i);
-      u32 hw_lower;
+      u32 flg = (p->known_fields & PCI_FILL_IO_FLAGS) ? ioflg_to_pciflg(ioflg) : without_config_data ? 0 : get_conf_long(d, PCI_BASE_ADDRESS_0 + 4*i);
+      u32 hw_lower = 0;
       u32 hw_upper = 0;
       int broken = 0;
+      int virtual = 0;
 
       if (flg == 0xffffffff)
        flg = 0;
@@ -395,31 +421,26 @@ show_bases(struct device *d, int cnt)
       else
        putchar('\t');
 
-      /* Read address as seen by the hardware */
-      if (flg & PCI_BASE_ADDRESS_SPACE_IO)
-       hw_lower = flg & PCI_BASE_ADDRESS_IO_MASK;
-      else
+      /* Detect virtual regions, which are reported by the OS, but unassigned in the device */
+      if ((p->known_fields & PCI_FILL_IO_FLAGS) && !without_config_data)
        {
-         hw_lower = flg & PCI_BASE_ADDRESS_MEM_MASK;
-         if ((flg & PCI_BASE_ADDRESS_MEM_TYPE_MASK) == PCI_BASE_ADDRESS_MEM_TYPE_64)
+         /* Read address as seen by the hardware */
+         hw_lower = get_conf_long(d, PCI_BASE_ADDRESS_0 + 4*i);
+         if ((hw_lower & PCI_BASE_ADDRESS_SPACE) == (ioflg_to_pciflg(ioflg) & PCI_BASE_ADDRESS_SPACE))
            {
-             if (i >= cnt - 1)
-               broken = 1;
-             else
-               {
-                 i++;
-                 hw_upper = get_conf_long(d, PCI_BASE_ADDRESS_0 + 4*i);
+             if ((ioflg & PCI_IORESOURCE_TYPE_BITS) == PCI_IORESOURCE_MEM &&
+                 (hw_lower & PCI_BASE_ADDRESS_MEM_TYPE_MASK) == PCI_BASE_ADDRESS_MEM_TYPE_64)
+               {
+                 if (i >= cnt - 1)
+                   broken = 1;
+                 else
+                   hw_upper = get_conf_long(d, PCI_BASE_ADDRESS_0 + 4*i + 1);
                }
+             if (pos && !hw_lower && !hw_upper && !(ioflg & PCI_IORESOURCE_PCI_EA_BEI))
+               virtual = 1;
            }
        }
 
-      /* Detect virtual regions, which are reported by the OS, but unassigned in the device */
-      if (pos && !hw_lower && !hw_upper && !(ioflg & PCI_IORESOURCE_PCI_EA_BEI))
-       {
-         flg = pos;
-         virtual = 1;
-       }
-
       /* Print base address */
       if (flg & PCI_BASE_ADDRESS_SPACE_IO)
        {
@@ -476,14 +497,14 @@ show_rom(struct device *d, int reg)
   pciaddr_t rom = p->rom_base_addr;
   pciaddr_t len = (p->known_fields & PCI_FILL_SIZES) ? p->rom_size : 0;
   pciaddr_t ioflg = (p->known_fields & PCI_FILL_IO_FLAGS) ? p->rom_flags : 0;
-  u32 flg = get_conf_long(d, reg);
-  word cmd = get_conf_word(d, PCI_COMMAND);
+  u32 flg = reg >= 0 ? get_conf_long(d, reg) : ioflg_to_pciflg(ioflg);
+  word cmd = reg >= 0 ? get_conf_word(d, PCI_COMMAND) : PCI_COMMAND_MEMORY;
   int virtual = 0;
 
   if (!rom && !flg && !len)
     return;
 
-  if ((rom & PCI_ROM_ADDRESS_MASK) && !(flg & PCI_ROM_ADDRESS_MASK) && !(ioflg & PCI_IORESOURCE_PCI_EA_BEI))
+  if (reg >= 0 && (rom & PCI_ROM_ADDRESS_MASK) && !(flg & PCI_ROM_ADDRESS_MASK) && !(ioflg & PCI_IORESOURCE_PCI_EA_BEI))
     {
       flg = rom;
       virtual = 1;
@@ -515,7 +536,7 @@ show_rom(struct device *d, int reg)
 static void
 show_htype0(struct device *d)
 {
-  show_bases(d, 6);
+  show_bases(d, 6, 0);
   show_rom(d, PCI_ROM_ADDRESS);
   show_caps(d, PCI_CAPABILITY_LIST);
 }
@@ -540,7 +561,7 @@ show_htype1(struct device *d)
   int pref_disabled = (p->known_fields & PCI_FILL_BRIDGE_BASES) && !p->bridge_size[2];
   int io_bits, pref_bits;
 
-  show_bases(d, 2);
+  show_bases(d, 2, 0);
   printf("\tBus: primary=%02x, secondary=%02x, subordinate=%02x, sec-latency=%d\n",
         get_conf_byte(d, PCI_PRIMARY_BUS),
         get_conf_byte(d, PCI_SECONDARY_BUS),
@@ -667,7 +688,7 @@ show_htype2(struct device *d)
   word exca;
   int verb = verbose > 2;
 
-  show_bases(d, 1);
+  show_bases(d, 1, 0);
   printf("\tBus: primary=%02x, secondary=%02x, subordinate=%02x, sec-latency=%d\n",
         get_conf_byte(d, PCI_CB_PRIMARY_BUS),
         get_conf_byte(d, PCI_CB_CARD_BUS),
@@ -726,20 +747,57 @@ show_htype2(struct device *d)
   show_caps(d, PCI_CB_CAPABILITY_LIST);
 }
 
+static void
+show_htype_unknown(struct device *d)
+{
+  struct pci_dev *p = d->dev;
+  u64 base, limit, flags;
+  const char *str;
+  int i, bits;
+
+  if (pacc->buscentric)
+    return;
+
+  show_bases(d, 6, 1);
+  for (i = 0; i < 4; i++)
+    {
+      if (!p->bridge_base_addr[i])
+        continue;
+      base = p->bridge_base_addr[i];
+      limit = base + p->bridge_size[i] - 1;
+      flags = p->bridge_flags[i];
+      if (flags & PCI_IORESOURCE_IO)
+        {
+          bits = (flags & PCI_IORESOURCE_IO_16BIT_ADDR) ? 16 : 32;
+          str = "\tI/O behind bridge";
+        }
+      else if (flags & PCI_IORESOURCE_MEM)
+        {
+          bits = (flags & PCI_IORESOURCE_MEM_64) ? 64 : 32;
+          if (flags & PCI_IORESOURCE_PREFETCH)
+            str = "\tPrefetchable memory behind bridge";
+          else
+            str = "\tMemory behind bridge";
+        }
+      else
+        {
+          bits = 0;
+          str = "\tUnknown resource behind bridge";
+        }
+      show_range(str, base, limit, bits, 0);
+    }
+  show_rom(d, -1);
+}
+
 static void
 show_verbose(struct device *d)
 {
   struct pci_dev *p = d->dev;
-  word status = get_conf_word(d, PCI_STATUS);
-  word cmd = get_conf_word(d, PCI_COMMAND);
+  int unknown_config_data = 0;
   word class = p->device_class;
-  byte bist = get_conf_byte(d, PCI_BIST);
-  byte htype = get_conf_byte(d, PCI_HEADER_TYPE) & 0x7f;
-  byte latency = get_conf_byte(d, PCI_LATENCY_TIMER);
-  byte cache_line = get_conf_byte(d, PCI_CACHE_LINE_SIZE);
+  byte htype = d->no_config_access ? -1 : (get_conf_byte(d, PCI_HEADER_TYPE) & 0x7f);
+  byte bist;
   byte max_lat, min_gnt;
-  byte int_pin = get_conf_byte(d, PCI_INTERRUPT_PIN);
-  unsigned int irq;
   char *dt_node, *iommu_group;
 
   show_terse(d);
@@ -747,29 +805,34 @@ show_verbose(struct device *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_BRIDGE_BASES | PCI_FILL_CLASS_EXT | PCI_FILL_SUBSYS);
-  irq = p->irq;
 
   switch (htype)
     {
     case PCI_HEADER_TYPE_NORMAL:
       if (class == PCI_CLASS_BRIDGE_PCI)
        printf("\t!!! Invalid class %04x for header type %02x\n", class, htype);
+      bist = get_conf_byte(d, PCI_BIST);
       max_lat = get_conf_byte(d, PCI_MAX_LAT);
       min_gnt = get_conf_byte(d, PCI_MIN_GNT);
       break;
     case PCI_HEADER_TYPE_BRIDGE:
       if ((class >> 8) != PCI_BASE_CLASS_BRIDGE)
        printf("\t!!! Invalid class %04x for header type %02x\n", class, htype);
+      bist = get_conf_byte(d, PCI_BIST);
       min_gnt = max_lat = 0;
       break;
     case PCI_HEADER_TYPE_CARDBUS:
       if ((class >> 8) != PCI_BASE_CLASS_BRIDGE)
        printf("\t!!! Invalid class %04x for header type %02x\n", class, htype);
+      bist = get_conf_byte(d, PCI_BIST);
       min_gnt = max_lat = 0;
       break;
     default:
+      if (!d->no_config_access)
       printf("\t!!! Unknown header type %02x\n", htype);
-      return;
+      bist = 0;
+      min_gnt = max_lat = 0;
+      unknown_config_data = 1;
     }
 
   if (p->phy_slot)
@@ -778,8 +841,10 @@ show_verbose(struct device *d)
   if (dt_node = pci_get_string_property(p, PCI_FILL_DT_NODE))
     printf("\tDevice tree node: %s\n", dt_node);
 
-  if (verbose > 1)
+  if (!unknown_config_data && verbose > 1)
     {
+      word cmd = get_conf_word(d, PCI_COMMAND);
+      word status = get_conf_word(d, PCI_STATUS);
       printf("\tControl: I/O%c Mem%c BusMaster%c SpecCycle%c MemWINV%c VGASnoop%c ParErr%c Stepping%c SERR%c FastB2B%c DisINTx%c\n",
             FLAG(cmd, PCI_COMMAND_IO),
             FLAG(cmd, PCI_COMMAND_MEMORY),
@@ -809,6 +874,8 @@ show_verbose(struct device *d)
             FLAG(status, PCI_STATUS_INTx));
       if (cmd & PCI_COMMAND_MASTER)
        {
+         byte latency = get_conf_byte(d, PCI_LATENCY_TIMER);
+         byte cache_line = get_conf_byte(d, PCI_CACHE_LINE_SIZE);
          printf("\tLatency: %d", latency);
          if (min_gnt || max_lat)
            {
@@ -825,16 +892,25 @@ show_verbose(struct device *d)
            printf(", Cache Line Size: %d bytes", cache_line * 4);
          putchar('\n');
        }
-      if (int_pin || irq)
+    }
+
+  if (verbose > 1)
+    {
+      byte int_pin = unknown_config_data ? 0 : get_conf_byte(d, PCI_INTERRUPT_PIN);
+      if (int_pin || p->irq)
        printf("\tInterrupt: pin %c routed to IRQ " PCIIRQ_FMT "\n",
-              (int_pin ? 'A' + int_pin - 1 : '?'), irq);
+              (int_pin ? 'A' + int_pin - 1 : '?'), p->irq);
       if (p->numa_node != -1)
        printf("\tNUMA node: %d\n", p->numa_node);
       if (iommu_group = pci_get_string_property(p, PCI_FILL_IOMMU_GROUP))
        printf("\tIOMMU group: %s\n", iommu_group);
     }
-  else
+
+  if (!unknown_config_data && verbose <= 1)
     {
+      word cmd = get_conf_word(d, PCI_COMMAND);
+      word status = get_conf_word(d, PCI_STATUS);
+      byte latency = get_conf_byte(d, PCI_LATENCY_TIMER);
       printf("\tFlags: ");
       if (cmd & PCI_COMMAND_MASTER)
        printf("bus master, ");
@@ -854,8 +930,8 @@ show_verbose(struct device *d)
             ((status & PCI_STATUS_DEVSEL_MASK) == PCI_STATUS_DEVSEL_FAST) ? "fast" : "??");
       if (cmd & PCI_COMMAND_MASTER)
        printf(", latency %d", latency);
-      if (irq)
-       printf(", IRQ " PCIIRQ_FMT, irq);
+      if (p->irq)
+       printf(", IRQ " PCIIRQ_FMT, p->irq);
       if (p->numa_node != -1)
        printf(", NUMA node %d", p->numa_node);
       if (iommu_group = pci_get_string_property(p, PCI_FILL_IOMMU_GROUP))
@@ -882,6 +958,8 @@ show_verbose(struct device *d)
     case PCI_HEADER_TYPE_CARDBUS:
       show_htype2(d);
       break;
+    default:
+      show_htype_unknown(d);
     }
 }
 
@@ -892,6 +970,12 @@ show_hex_dump(struct device *d)
 {
   unsigned int i, cnt;
 
+  if (d->no_config_access)
+    {
+      printf("WARNING: Cannot show hex-dump of the config space\n");
+      return;
+    }
+
   cnt = d->config_cached;
   if (opt_hex >= 3 && config_fetch(d, cnt, 256-cnt))
     {
@@ -927,7 +1011,7 @@ static void
 show_machine(struct device *d)
 {
   struct pci_dev *p = d->dev;
-  char classbuf[128], vendbuf[128], devbuf[128], svbuf[128], sdbuf[128];
+  char classbuf[256], vendbuf[256], devbuf[256], svbuf[256], sdbuf[256];
   char *dt_node, *iommu_group;
 
   if (verbose)