pci: add support for MMIO config space
authorBarret Rhoden <brho@cs.berkeley.edu>
Wed, 14 Aug 2019 15:30:15 +0000 (11:30 -0400)
committerBarret Rhoden <brho@cs.berkeley.edu>
Wed, 14 Aug 2019 15:36:10 +0000 (11:36 -0400)
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>
kern/arch/x86/pci.c
kern/arch/x86/pci.h

index 334def4..ed90424 100644 (file)
@@ -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 void __pci_parse_caps(struct pci_device *pcidev)
        }
 }
 
+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 @@ void pci_init(void)
                                         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 @@ static void pci_cfg_pio_write8(uint8_t bus, uint8_t dev, uint8_t func,
        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
index 89c355a..55bfb93 100644 (file)
@@ -171,6 +171,7 @@ struct pci_device {
        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 */