]> mj.ucw.cz Git - pciutils.git/blobdiff - lspci.c
lspci: Fix unsynchronized caches in lspci struct device and pci struct pci_dev
[pciutils.git] / lspci.c
diff --git a/lspci.c b/lspci.c
index 95a1318e5e80eceb0b5272645545d971ff0bddd4..071cc117bc682c11917e57d6392a987c6a50d528 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>
@@ -105,6 +107,7 @@ config_fetch(struct device *d, unsigned int pos, unsigned int len)
       d->config = xrealloc(d->config, d->config_bufsize);
       d->present = xrealloc(d->present, d->config_bufsize);
       memset(d->present + orig_size, 0, d->config_bufsize - orig_size);
+      pci_setup_cache(d->dev, d->config, d->dev->cache_len);
     }
   result = pci_read_block(d->dev, pos, d->config + pos, len);
   if (result)
@@ -124,18 +127,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... */
@@ -290,7 +293,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",
@@ -397,17 +400,17 @@ show_bases(struct device *d, int cnt, int without_config_data)
   struct pci_dev *p = d->dev;
   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 = without_config_data ? ioflg_to_pciflg(ioflg) : 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;
@@ -419,32 +422,26 @@ show_bases(struct device *d, int cnt, int without_config_data)
       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++;
-                 if (!without_config_data)
-                 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 (!without_config_data && 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)
        {
@@ -799,7 +796,7 @@ show_verbose(struct device *d)
   struct pci_dev *p = d->dev;
   int unknown_config_data = 0;
   word class = p->device_class;
-  byte htype = get_conf_byte(d, PCI_HEADER_TYPE) & 0x7f;
+  byte htype = d->no_config_access ? -1 : (get_conf_byte(d, PCI_HEADER_TYPE) & 0x7f);
   byte bist;
   byte max_lat, min_gnt;
   char *dt_node, *iommu_group;
@@ -832,6 +829,7 @@ show_verbose(struct device *d)
       min_gnt = max_lat = 0;
       break;
     default:
+      if (!d->no_config_access)
       printf("\t!!! Unknown header type %02x\n", htype);
       bist = 0;
       min_gnt = max_lat = 0;
@@ -973,6 +971,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))
     {
@@ -1008,7 +1012,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)