pci: add support for MMIO config space
MMIO config space has two benefits: it does not require the global PCI
lock, and it easily works with extended PCI config space (i.e. above 255).
For whatever reason, I had an old note that said you could use PIO for
the extended config space. I probably got that from looking at Linux or
something. It might have worked on some older machines for me; I don't
recall. But it certainly does not work with all machines. Maybe it was
an AMD/Intel thing.
I left support for PIO in case we run into a weird machine that doesn't
have the ACPI MCFG table or for debugging. Though even my QEMU has an
MCFG. We can remove it if it is a pain - maybe when we make PCI more
architecture-independent. Right now it is x86-specific, both in PIO and
MMIO ops.
Signed-off-by: Barret Rhoden <brho@cs.berkeley.edu>
diff --git a/kern/arch/x86/pci.c b/kern/arch/x86/pci.c
index 334def4..ed90424 100644
--- a/kern/arch/x86/pci.c
+++ b/kern/arch/x86/pci.c
@@ -14,6 +14,7 @@
#include <mm.h>
#include <arch/pci_defs.h>
#include <ros/errno.h>
+#include <acpi.h>
/* List of all discovered devices */
struct pcidev_stailq pci_devices = STAILQ_HEAD_INITIALIZER(pci_devices);
@@ -197,6 +198,18 @@
}
}
+static uintptr_t pci_get_mmio_cfg(struct pci_device *pcidev)
+{
+ physaddr_t paddr;
+
+ paddr = acpi_pci_get_mmio_cfg_addr(0 /* segment for legacy PCI enum*/,
+ pcidev->bus, pcidev->dev,
+ pcidev->func);
+ if (!paddr)
+ return 0;
+ return vmap_pmem_nocache(paddr, 4096);
+}
+
/* Scans the PCI bus. Won't actually work for anything other than bus 0, til we
* sort out how to handle bridge devices. */
void pci_init(void)
@@ -238,6 +251,8 @@
pcidev->dev, pcidev->func);
pcidev->dev_id = dev_id;
pcidev->ven_id = ven_id;
+ /* Set up the MMIO CFG before using accessors */
+ pcidev->mmio_cfg = pci_get_mmio_cfg(pcidev);
/* Get the Class/subclass */
pcidev->class =
pcidev_read8(pcidev, PCI_CLASS_REG);
@@ -375,40 +390,101 @@
spin_unlock_irqsave(&pci_lock);
}
+/* Some AMD processors require using eax for MMIO config ops. */
+static uint32_t pci_cfg_mmio_read32(uintptr_t mmio_cfg, uint32_t offset)
+{
+ uint32_t val;
+
+ asm volatile("movl (%1),%0" : "=a"(val) : "g"(mmio_cfg + offset));
+ return val;
+}
+
+static void pci_cfg_mmio_write32(uintptr_t mmio_cfg, uint32_t offset,
+ uint32_t val)
+{
+ asm volatile("movl %0,(%1)" : : "a"(val), "g"(mmio_cfg + offset));
+}
+
+static uint16_t pci_cfg_mmio_read16(uintptr_t mmio_cfg, uint32_t offset)
+{
+ uint16_t val;
+
+ asm volatile("movw (%1),%0" : "=a"(val) : "g"(mmio_cfg + offset));
+ return val;
+}
+
+static void pci_cfg_mmio_write16(uintptr_t mmio_cfg, uint32_t offset,
+ uint16_t val)
+{
+ asm volatile("movw %0,(%1)" : : "a"(val), "g"(mmio_cfg + offset));
+}
+
+static uint8_t pci_cfg_mmio_read8(uintptr_t mmio_cfg, uint32_t offset)
+{
+ uint8_t val;
+
+ asm volatile("movb (%1),%0" : "=a"(val) : "g"(mmio_cfg + offset));
+ return val;
+}
+
+static void pci_cfg_mmio_write8(uintptr_t mmio_cfg, uint32_t offset,
+ uint8_t val)
+{
+ asm volatile("movb %0,(%1)" : : "a"(val), "g"(mmio_cfg + offset));
+}
+
uint32_t pcidev_read32(struct pci_device *pcidev, uint32_t offset)
{
- return pci_cfg_pio_read32(pcidev->bus, pcidev->dev, pcidev->func,
- offset);
+ if (pcidev->mmio_cfg)
+ return pci_cfg_mmio_read32(pcidev->mmio_cfg, offset);
+ else
+ return pci_cfg_pio_read32(pcidev->bus, pcidev->dev,
+ pcidev->func, offset);
}
void pcidev_write32(struct pci_device *pcidev, uint32_t offset, uint32_t value)
{
- pci_cfg_pio_write32(pcidev->bus, pcidev->dev, pcidev->func, offset,
- value);
+ if (pcidev->mmio_cfg)
+ pci_cfg_mmio_write32(pcidev->mmio_cfg, offset, value);
+ else
+ pci_cfg_pio_write32(pcidev->bus, pcidev->dev, pcidev->func,
+ offset, value);
}
uint16_t pcidev_read16(struct pci_device *pcidev, uint32_t offset)
{
- return pci_cfg_pio_read16(pcidev->bus, pcidev->dev, pcidev->func,
- offset);
+ if (pcidev->mmio_cfg)
+ return pci_cfg_mmio_read16(pcidev->mmio_cfg, offset);
+ else
+ return pci_cfg_pio_read16(pcidev->bus, pcidev->dev,
+ pcidev->func, offset);
}
void pcidev_write16(struct pci_device *pcidev, uint32_t offset, uint16_t value)
{
- pci_cfg_pio_write16(pcidev->bus, pcidev->dev, pcidev->func, offset,
- value);
+ if (pcidev->mmio_cfg)
+ pci_cfg_mmio_write16(pcidev->mmio_cfg, offset, value);
+ else
+ pci_cfg_pio_write16(pcidev->bus, pcidev->dev, pcidev->func,
+ offset, value);
}
uint8_t pcidev_read8(struct pci_device *pcidev, uint32_t offset)
{
- return pci_cfg_pio_read8(pcidev->bus, pcidev->dev, pcidev->func,
- offset);
+ if (pcidev->mmio_cfg)
+ return pci_cfg_mmio_read8(pcidev->mmio_cfg, offset);
+ else
+ return pci_cfg_pio_read8(pcidev->bus, pcidev->dev, pcidev->func,
+ offset);
}
void pcidev_write8(struct pci_device *pcidev, uint32_t offset, uint8_t value)
{
- pci_cfg_pio_write8(pcidev->bus, pcidev->dev, pcidev->func, offset,
- value);
+ if (pcidev->mmio_cfg)
+ pci_cfg_mmio_write8(pcidev->mmio_cfg, offset, value);
+ else
+ pci_cfg_pio_write8(pcidev->bus, pcidev->dev, pcidev->func,
+ offset, value);
}
/* Helper to get the class description strings. Adapted from
diff --git a/kern/arch/x86/pci.h b/kern/arch/x86/pci.h
index 89c355a..55bfb93 100644
--- a/kern/arch/x86/pci.h
+++ b/kern/arch/x86/pci.h
@@ -171,6 +171,7 @@
SLIST_ENTRY(pci_device) irq_dev; /* list of all devs on irq */
char name[9];
spinlock_t lock;
+ uintptr_t mmio_cfg;
void *dev_data; /* device private pointer */
struct device device;
bool in_use; /* prevent double discovery */