]> mj.ucw.cz Git - pciutils.git/commitdiff
libpci: Implement virtual config space for provides without access to config space
authorPali Rohár <pali@kernel.org>
Sun, 2 Jan 2022 12:33:12 +0000 (13:33 +0100)
committerMartin Mareš <mj@ucw.cz>
Fri, 15 Apr 2022 21:47:37 +0000 (23:47 +0200)
Add a new pci_dev member no_config_access which signals that there is no
access to config space for particular device. Reading operation in this
case should return data from emulated virtual config space. For provides
there is a new helper function pci_generic_read() which emulates config
spaces based on struct pci_dev members.

lib/Makefile
lib/emulated.c [new file with mode: 0644]
lib/internal.h
lib/pci.h

index 0332508bce7383caae1f5e6c9c0fa1ae097fcba8..bc8f5f19e553e4daae72885443966d9f59fb1545 100644 (file)
@@ -87,6 +87,7 @@ i386-ports.o: i386-ports.c $(INCL) i386-io-hurd.h i386-io-linux.h i386-io-sunos.
 proc.o: proc.c $(INCL) pread.h
 sysfs.o: sysfs.c $(INCL) pread.h
 generic.o: generic.c $(INCL)
+emulated.o: emulated.c $(INCL)
 syscalls.o: syscalls.c $(INCL)
 obsd-device.o: obsd-device.c $(INCL)
 fbsd-device.o: fbsd-device.c $(INCL)
diff --git a/lib/emulated.c b/lib/emulated.c
new file mode 100644 (file)
index 0000000..d15c8f7
--- /dev/null
@@ -0,0 +1,299 @@
+/*
+ *     The PCI Library -- Virtual Emulated Config Space Access Functions
+ *
+ *     Copyright (c) 2022 Pali Rohár
+ *
+ *     Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#include "internal.h"
+
+static u32
+ioflg_to_pciflg(pciaddr_t ioflg)
+{
+  u32 flg = 0;
+
+  if ((ioflg & PCI_IORESOURCE_TYPE_BITS) == PCI_IORESOURCE_IO)
+    flg = PCI_BASE_ADDRESS_SPACE_IO;
+  else if ((ioflg & PCI_IORESOURCE_TYPE_BITS) == PCI_IORESOURCE_MEM)
+    {
+      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 u32
+baseres_to_pcires(pciaddr_t addr, pciaddr_t ioflg, int *have_sec, u32 *sec_val)
+{
+  u32 val = ioflg_to_pciflg(ioflg);
+
+  if (have_sec)
+    *have_sec = 0;
+
+  if ((val & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO && addr <= 0xffffffff)
+    val |= addr & PCI_BASE_ADDRESS_IO_MASK;
+  else if ((val & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_MEMORY)
+    {
+      val |= addr & PCI_BASE_ADDRESS_MEM_MASK;
+      if ((val & PCI_BASE_ADDRESS_MEM_TYPE_64) && have_sec)
+        {
+          *have_sec = 1;
+          *sec_val = addr >> 32;
+        }
+    }
+
+  return val;
+}
+
+static inline u32
+even_baseres_to_pcires(pciaddr_t addr, pciaddr_t ioflg)
+{
+  return baseres_to_pcires(addr, ioflg, NULL, NULL);
+}
+
+static inline u32
+odd_baseres_to_pcires(pciaddr_t addr0, pciaddr_t ioflg0, pciaddr_t addr, pciaddr_t ioflg)
+{
+  int have_sec;
+  u32 val;
+  baseres_to_pcires(addr0, ioflg0, &have_sec, &val);
+  if (!have_sec)
+    val = baseres_to_pcires(addr, ioflg, NULL, NULL);
+  return val;
+}
+
+int
+pci_emulated_read(struct pci_dev *d, int pos, byte *buf, int len)
+{
+  u32 ht = PCI_HEADER_TYPE_NORMAL;
+  u32 val = 0;
+  int i;
+
+  if (pos >= 64)
+    return 0;
+
+  if (len > 4)
+    return pci_generic_block_read(d, pos, buf, len);
+
+  if (d->device_class == PCI_CLASS_BRIDGE_PCI)
+    ht = PCI_HEADER_TYPE_BRIDGE;
+  else if (d->device_class == PCI_CLASS_BRIDGE_CARDBUS)
+    ht = PCI_HEADER_TYPE_CARDBUS;
+
+  switch (pos & ~3)
+    {
+      case PCI_COMMAND:
+        for (i = 0; i < 6; i++)
+          {
+            if (!d->size[i])
+              continue;
+            if ((d->flags[i] & PCI_IORESOURCE_TYPE_BITS) == PCI_IORESOURCE_IO)
+              val |= PCI_COMMAND_IO;
+            else if ((d->flags[i] & PCI_IORESOURCE_TYPE_BITS) == PCI_IORESOURCE_MEM)
+              val |= PCI_COMMAND_MEMORY;
+          }
+        break;
+      case PCI_VENDOR_ID:
+        val = (d->device_id << 16) | d->vendor_id;
+        break;
+      case PCI_CLASS_REVISION:
+        val = (d->device_class << 16) | (d->prog_if << 8) | d->rev_id;
+        break;
+      case PCI_CACHE_LINE_SIZE:
+        val = ht << 16;
+        break;
+      case PCI_BASE_ADDRESS_0:
+        val = even_baseres_to_pcires(d->base_addr[0], d->flags[0]);
+        break;
+      case PCI_INTERRUPT_LINE:
+        val = (d->irq >= 0 && d->irq <= 0xff) ? d->irq : 0;
+        break;
+    }
+
+  if ((pos & ~3) == PCI_BASE_ADDRESS_1 && (ht == PCI_HEADER_TYPE_NORMAL || ht == PCI_HEADER_TYPE_BRIDGE))
+    val = odd_baseres_to_pcires(d->base_addr[0], d->flags[0], d->base_addr[1], d->flags[1]);
+
+  if (ht == PCI_HEADER_TYPE_NORMAL)
+    switch (pos & ~3)
+      {
+        case PCI_BASE_ADDRESS_2:
+          val = even_baseres_to_pcires(d->base_addr[2], d->flags[2]);
+          break;
+        case PCI_BASE_ADDRESS_3:
+          val = odd_baseres_to_pcires(d->base_addr[2], d->flags[2], d->base_addr[3], d->flags[3]);
+          break;
+        case PCI_BASE_ADDRESS_4:
+          val = even_baseres_to_pcires(d->base_addr[4], d->flags[4]);
+          break;
+        case PCI_BASE_ADDRESS_5:
+          val = odd_baseres_to_pcires(d->base_addr[4], d->flags[4], d->base_addr[5], d->flags[5]);
+          break;
+        case PCI_SUBSYSTEM_VENDOR_ID:
+          val = (d->subsys_id << 16) | d->subsys_vendor_id;
+          break;
+        case PCI_ROM_ADDRESS:
+          val = d->rom_base_addr & PCI_ROM_ADDRESS_MASK;
+          if (val)
+            val |= PCI_ROM_ADDRESS_ENABLE;
+          break;
+      }
+  else if (ht == PCI_HEADER_TYPE_BRIDGE)
+    switch (pos & ~3)
+      {
+        case PCI_COMMAND:
+          if (d->bridge_size[0])
+            val |= PCI_COMMAND_IO;
+          if (d->bridge_size[1] || d->bridge_size[2])
+            val |= PCI_COMMAND_MEMORY;
+          break;
+        case PCI_PRIMARY_BUS:
+          val = d->bus;
+          break;
+        case PCI_IO_BASE:
+          if (d->bridge_size[0])
+            {
+              val = (((((d->bridge_base_addr[0] + d->bridge_size[0] - 1) >> 8) & PCI_IO_RANGE_MASK) << 8) & 0xff00) |
+                    (((d->bridge_base_addr[0] >> 8) & PCI_IO_RANGE_MASK) & 0x00ff);
+              if ((d->bridge_flags[0] & PCI_IORESOURCE_IO_16BIT_ADDR) &&
+                  d->bridge_base_addr[0] + d->bridge_size[0] - 1 <= 0xffff)
+                val |= (PCI_IO_RANGE_TYPE_16 << 8) | PCI_IO_RANGE_TYPE_16;
+              else
+                val |= (PCI_IO_RANGE_TYPE_32 << 8) | PCI_IO_RANGE_TYPE_32;
+            }
+          else
+            val = 0xff & PCI_IO_RANGE_MASK;
+          break;
+        case PCI_MEMORY_BASE:
+          if (d->bridge_size[1])
+            val = (((((d->bridge_base_addr[1] + d->bridge_size[1] - 1) >> 16) & PCI_MEMORY_RANGE_MASK) << 16) & 0xffff0000) |
+                  (((d->bridge_base_addr[1] >> 16) & PCI_MEMORY_RANGE_MASK) & 0x0000ffff);
+          else
+            val = 0xffff & PCI_MEMORY_RANGE_MASK;
+          break;
+        case PCI_PREF_MEMORY_BASE:
+          if (d->bridge_size[2])
+            {
+              val = (((((d->bridge_base_addr[2] + d->bridge_size[2] - 1) >> 16) & PCI_PREF_RANGE_MASK) << 16) & 0xffff0000) |
+                    (((d->bridge_base_addr[2] >> 16) & PCI_PREF_RANGE_MASK) & 0x0000ffff);
+              if ((d->bridge_flags[2] & PCI_IORESOURCE_MEM_64) ||
+                  d->bridge_base_addr[2] + d->bridge_size[2] - 1 > 0xffffffff)
+                val |= (PCI_PREF_RANGE_TYPE_64 << 16) | PCI_PREF_RANGE_TYPE_64;
+              else
+                val |= (PCI_PREF_RANGE_TYPE_32 << 16) | PCI_PREF_RANGE_TYPE_32;
+            }
+          else
+            val = 0xffff & PCI_PREF_RANGE_MASK;
+          break;
+        case PCI_PREF_BASE_UPPER32:
+          if (d->bridge_size[2])
+            val = d->bridge_base_addr[2] >> 32;
+          break;
+        case PCI_PREF_LIMIT_UPPER32:
+          if (d->bridge_size[2])
+            val = (d->bridge_base_addr[2] + d->bridge_size[2] - 1) >> 32;
+          break;
+        case PCI_IO_BASE_UPPER16:
+          if (d->bridge_size[0])
+            val = ((((d->bridge_base_addr[0] + d->bridge_size[0] - 1) >> 16) << 16) & 0xffff0000) |
+                  ((d->bridge_base_addr[0] >> 16) & 0x0000ffff);
+          break;
+        case PCI_ROM_ADDRESS1:
+          val = d->rom_base_addr & PCI_ROM_ADDRESS_MASK;
+          if (val)
+            val |= PCI_ROM_ADDRESS_ENABLE;
+          break;
+      }
+  else if (ht == PCI_HEADER_TYPE_CARDBUS)
+    switch (pos & ~3)
+      {
+        case PCI_COMMAND:
+          if (d->bridge_size[0] || d->bridge_size[1])
+            val |= PCI_COMMAND_MEMORY;
+          if (d->bridge_size[2] || d->bridge_size[3])
+            val |= PCI_COMMAND_IO;
+          break;
+        case PCI_CB_PRIMARY_BUS:
+          val = d->bus;
+          break;
+        case PCI_CB_MEMORY_BASE_0:
+          if (d->bridge_size[0])
+            val = d->bridge_base_addr[0] & ~0xfff;
+          else
+            val = 0xffffffff & ~0xfff;
+          break;
+        case PCI_CB_MEMORY_LIMIT_0:
+          if (d->bridge_size[0])
+            val = (d->bridge_base_addr[0] + d->bridge_size[0] - 1) & ~0xfff;
+          break;
+        case PCI_CB_MEMORY_BASE_1:
+          if (d->bridge_size[1])
+            val = d->bridge_base_addr[1] & ~0xfff;
+          else
+            val = 0xffffffff & ~0xfff;
+          break;
+        case PCI_CB_MEMORY_LIMIT_1:
+          if (d->bridge_size[1])
+            val = (d->bridge_base_addr[1] + d->bridge_size[1] - 1) & ~0xfff;
+          break;
+        case PCI_CB_IO_BASE_0:
+          if (d->bridge_size[2])
+            {
+              val = d->bridge_base_addr[2] & PCI_CB_IO_RANGE_MASK;
+              if ((d->bridge_flags[2] & PCI_IORESOURCE_IO_16BIT_ADDR) ||
+                  d->bridge_base_addr[2] + d->bridge_size[2] - 1 <= 0xffff)
+                val |= PCI_IO_RANGE_TYPE_16;
+              else
+                val |= PCI_IO_RANGE_TYPE_32;
+            }
+          else
+            val = 0x0000ffff & PCI_CB_IO_RANGE_MASK;
+          break;
+        case PCI_CB_IO_LIMIT_0:
+          if (d->bridge_size[2])
+            val = (d->bridge_base_addr[2] + d->bridge_size[2] - 1) & PCI_CB_IO_RANGE_MASK;
+          break;
+        case PCI_CB_IO_BASE_1:
+          if (d->bridge_size[3])
+            {
+              val = d->bridge_base_addr[3] & PCI_CB_IO_RANGE_MASK;
+              if ((d->bridge_flags[3] & PCI_IORESOURCE_IO_16BIT_ADDR) ||
+                  d->bridge_base_addr[3] + d->bridge_size[3] - 1 <= 0xffff)
+                val |= PCI_IO_RANGE_TYPE_16;
+              else
+                val |= PCI_IO_RANGE_TYPE_32;
+            }
+          else
+            val = 0x0000ffff & PCI_CB_IO_RANGE_MASK;
+          break;
+        case PCI_CB_IO_LIMIT_1:
+          if (d->bridge_size[3])
+            val = (d->bridge_base_addr[3] + d->bridge_size[3] - 1) & PCI_CB_IO_RANGE_MASK;
+          break;
+        case PCI_CB_BRIDGE_CONTROL:
+          if (d->bridge_flags[0] & PCI_IORESOURCE_PREFETCH)
+            val |= PCI_CB_BRIDGE_CTL_PREFETCH_MEM0;
+          if (d->bridge_flags[1] & PCI_IORESOURCE_PREFETCH)
+            val |= PCI_CB_BRIDGE_CTL_PREFETCH_MEM1;
+          break;
+        case PCI_CB_SUBSYSTEM_VENDOR_ID:
+          val = (d->subsys_id << 16) | d->subsys_vendor_id;
+          break;
+      }
+
+  if (len <= 2)
+    val = (val >> (8 * (pos & 3))) & ((1 << (len * 8)) - 1);
+
+  while (len-- > 0)
+    {
+      *(buf++) = val & 0xff;
+      val >>= 8;
+    }
+  return 1;
+}
index a1c8cc83129299e61e3500fb916654c36a878f29..1726555bf1ecd4565bb3718bd42853b20cc42280 100644 (file)
@@ -56,6 +56,9 @@ void pci_generic_fill_info(struct pci_dev *, unsigned int flags);
 int pci_generic_block_read(struct pci_dev *, int pos, byte *buf, int len);
 int pci_generic_block_write(struct pci_dev *, int pos, byte *buf, int len);
 
+/* emulated.c */
+int pci_emulated_read(struct pci_dev *d, int pos, byte *buf, int len);
+
 /* init.c */
 void *pci_malloc(struct pci_access *, int);
 void pci_mfree(void *);
index 16388fa3508dd2a37e50b628e656fb235124e0a3..f623051b596af84b562654ab87f5ab2389604e0a 100644 (file)
--- a/lib/pci.h
+++ b/lib/pci.h
@@ -148,6 +148,7 @@ struct pci_dev {
   u8 prog_if, rev_id;                  /* Programming interface for device_class and revision id */
   u16 subsys_vendor_id, subsys_id;     /* Subsystem vendor id and subsystem id */
   struct pci_dev *parent;              /* Parent device, does not have to be always accessible */
+  int no_config_access;                        /* No access to config space for this device */
 
   /* Fields used internally */
   struct pci_access *access;