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 */