aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPali Rohár <pali@kernel.org>2022-01-02 13:33:12 +0100
committerMartin Mareš <mj@ucw.cz>2022-04-15 23:47:37 +0200
commit08609192656497bc1473a19acccb7779f477d11a (patch)
tree0816637549a3531f947918407257d70e1650f4a4
parent72cabfbb12ad48bd4cdd5ecbe72f882197e2dac6 (diff)
downloadpciutils-08609192656497bc1473a19acccb7779f477d11a.tar.gz
libpci: Implement virtual config space for provides without access to config space
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.
-rw-r--r--lib/Makefile1
-rw-r--r--lib/emulated.c299
-rw-r--r--lib/internal.h3
-rw-r--r--lib/pci.h1
4 files changed, 304 insertions, 0 deletions
diff --git a/lib/Makefile b/lib/Makefile
index 0332508..bc8f5f1 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -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
index 0000000..d15c8f7
--- /dev/null
+++ b/lib/emulated.c
@@ -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;
+}
diff --git a/lib/internal.h b/lib/internal.h
index a1c8cc8..1726555 100644
--- a/lib/internal.h
+++ b/lib/internal.h
@@ -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 *);
diff --git a/lib/pci.h b/lib/pci.h
index 16388fa..f623051 100644
--- 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;