summaryrefslogtreecommitdiffstats
path: root/lib/emulated.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--lib/emulated.c301
1 files changed, 301 insertions, 0 deletions
diff --git a/lib/emulated.c b/lib/emulated.c
new file mode 100644
index 0000000..92cd06e
--- /dev/null
+++ b/lib/emulated.c
@@ -0,0 +1,301 @@
+/*
+ * 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 v2+.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#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;
+}