grml...
[seabios.git] / src / pci.c
index 78bbac2e5e807591075ae725cccbadde6ed0516d..6031c9ffe400c5d9ed737f344612f177091e394e 100644 (file)
--- a/src/pci.c
+++ b/src/pci.c
@@ -8,8 +8,8 @@
 #include "pci.h" // pci_config_writel
 #include "ioport.h" // outl
 #include "util.h" // dprintf
-#include "config.h" // CONFIG_*
-#include "farptr.h" // CONFIG_*
+#include "paravirt.h" // romfile_loadint
+#include "farptr.h" // MAKE_FLATPTR
 #include "pci_regs.h" // PCI_VENDOR_ID
 #include "pci_ids.h" // PCI_CLASS_DISPLAY_VGA
 
@@ -59,135 +59,138 @@ pci_config_maskw(u16 bdf, u32 addr, u16 off, u16 on)
 
 // Helper function for foreachbdf() macro - return next device
 int
-pci_next(int bdf, int *pmax)
+pci_next(int bdf, int bus)
 {
-    if (pci_bdf_to_fn(bdf) == 1
-        && (pci_config_readb(bdf-1, PCI_HEADER_TYPE) & 0x80) == 0)
+    if (pci_bdf_to_fn(bdf) == 0
+        && (pci_config_readb(bdf, PCI_HEADER_TYPE) & 0x80) == 0)
         // Last found device wasn't a multi-function device - skip to
         // the next device.
-        bdf += 7;
+        bdf += 8;
+    else
+        bdf += 1;
 
-    int max = *pmax;
     for (;;) {
-        if (bdf >= max) {
-            if (CONFIG_PCI_ROOT1 && bdf <= (CONFIG_PCI_ROOT1 << 8))
-                bdf = CONFIG_PCI_ROOT1 << 8;
-            else if (CONFIG_PCI_ROOT2 && bdf <= (CONFIG_PCI_ROOT2 << 8))
-                bdf = CONFIG_PCI_ROOT2 << 8;
-            else
-               return -1;
-            *pmax = max = bdf + 0x0100;
-        }
+        if (pci_bdf_to_bus(bdf) != bus)
+            return -1;
 
         u16 v = pci_config_readw(bdf, PCI_VENDOR_ID);
         if (v != 0x0000 && v != 0xffff)
             // Device is present.
-            break;
+            return bdf;
 
         if (pci_bdf_to_fn(bdf) == 0)
             bdf += 8;
         else
             bdf += 1;
     }
-
-    // Check if found device is a bridge.
-    u32 v = pci_config_readb(bdf, PCI_HEADER_TYPE);
-    v &= 0x7f;
-    if (v == PCI_HEADER_TYPE_BRIDGE || v == PCI_HEADER_TYPE_CARDBUS) {
-        v = pci_config_readl(bdf, PCI_PRIMARY_BUS);
-        int newmax = (v & 0xff00) + 0x0100;
-        if (newmax > max)
-            *pmax = newmax;
-    }
-
-    return bdf;
 }
 
 struct pci_device *PCIDevices;
-int MaxPCIBus;
+int MaxPCIBus VAR16VISIBLE;
 
+// Check if PCI is available at all
+int
+pci_probe_host(void)
+{
+    outl(0x80000000, PORT_PCI_CMD);
+    if (inl(PORT_PCI_CMD) != 0x80000000) {
+        dprintf(1, "Detected non-PCI system\n");
+        return -1;
+    }
+    return 0;
+}
+
+// Find all PCI devices and populate PCIDevices linked list.
 void
-pci_probe(void)
+pci_probe_devices(void)
 {
-    int rootbuses = 0;
+    dprintf(3, "PCI probe\n");
     struct pci_device *busdevs[256];
     memset(busdevs, 0, sizeof(busdevs));
-
     struct pci_device **pprev = &PCIDevices;
-    u8 lastbus = 0;
-    int bdf, max;
-    foreachbdf(bdf, max) {
-        // Create new pci_device struct and add to list.
-        struct pci_device *dev = malloc_tmp(sizeof(*dev));
-        if (!dev) {
-            warn_noalloc();
-            return;
-        }
-        memset(dev, 0, sizeof(*dev));
-        *pprev = dev;
-        pprev = &dev->next;
+    int extraroots = romfile_loadint("etc/extra-pci-roots", 0);
+    int bus = -1, lastbus = 0, rootbuses = 0, count=0;
+    while (bus < 0xff && (bus < MaxPCIBus || rootbuses < extraroots)) {
+        bus++;
+        int bdf;
+        foreachbdf(bdf, bus) {
+            // Create new pci_device struct and add to list.
+            struct pci_device *dev = malloc_tmp(sizeof(*dev));
+            if (!dev) {
+                warn_noalloc();
+                return;
+            }
+            memset(dev, 0, sizeof(*dev));
+            *pprev = dev;
+            pprev = &dev->next;
+            count++;
 
-        // Find parent device.
-        u8 bus = pci_bdf_to_bus(bdf), rootbus;
-        struct pci_device *parent = busdevs[bus];
-        if (!parent) {
-            if (bus != lastbus)
-                rootbuses++;
-            lastbus = bus;
-            rootbus = rootbuses;
-        } else {
-            rootbus = parent->rootbus;
-        }
-        if (bus > MaxPCIBus)
-            MaxPCIBus = bus;
+            // Find parent device.
+            int rootbus;
+            struct pci_device *parent = busdevs[bus];
+            if (!parent) {
+                if (bus != lastbus)
+                    rootbuses++;
+                lastbus = bus;
+                rootbus = rootbuses;
+                if (bus > MaxPCIBus)
+                    MaxPCIBus = bus;
+            } else {
+                rootbus = parent->rootbus;
+            }
 
-        // Populate pci_device info.
-        dev->bdf = bdf;
-        dev->parent = parent;
-        dev->rootbus = rootbus;
-        u32 vendev = pci_config_readl(bdf, PCI_VENDOR_ID);
-        dev->vendor = vendev & 0xffff;
-        dev->device = vendev >> 16;
-        u32 classrev = pci_config_readl(bdf, PCI_CLASS_REVISION);
-        dev->class = classrev >> 16;
-        dev->prog_if = classrev >> 8;
-        dev->revision = classrev & 0xff;
-        dev->header_type = pci_config_readb(bdf, PCI_HEADER_TYPE);
-        u8 v = dev->header_type & 0x7f;
-        if (v == PCI_HEADER_TYPE_BRIDGE || v == PCI_HEADER_TYPE_CARDBUS) {
-            u8 secbus = pci_config_readb(bdf, PCI_SECONDARY_BUS);
-            dev->secondary_bus = secbus;
-            if (secbus > bus && !busdevs[secbus])
-                busdevs[secbus] = dev;
+            // Populate pci_device info.
+            dev->bdf = bdf;
+            dev->parent = parent;
+            dev->rootbus = rootbus;
+            u32 vendev = pci_config_readl(bdf, PCI_VENDOR_ID);
+            dev->vendor = vendev & 0xffff;
+            dev->device = vendev >> 16;
+            u32 classrev = pci_config_readl(bdf, PCI_CLASS_REVISION);
+            dev->class = classrev >> 16;
+            dev->prog_if = classrev >> 8;
+            dev->revision = classrev & 0xff;
+            dev->header_type = pci_config_readb(bdf, PCI_HEADER_TYPE);
+            u8 v = dev->header_type & 0x7f;
+            if (v == PCI_HEADER_TYPE_BRIDGE || v == PCI_HEADER_TYPE_CARDBUS) {
+                u8 secbus = pci_config_readb(bdf, PCI_SECONDARY_BUS);
+                dev->secondary_bus = secbus;
+                if (secbus > bus && !busdevs[secbus])
+                    busdevs[secbus] = dev;
+                if (secbus > MaxPCIBus)
+                    MaxPCIBus = secbus;
+            }
+            dprintf(4, "PCI device %02x:%02x.%x (vd=%04x:%04x c=%04x)\n"
+                    , pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf)
+                    , pci_bdf_to_fn(bdf)
+                    , dev->vendor, dev->device, dev->class);
         }
     }
+    dprintf(1, "Found %d PCI devices (max PCI bus is %02x)\n", count, MaxPCIBus);
 }
 
 // Search for a device with the specified vendor and device ids.
-int
+struct pci_device *
 pci_find_device(u16 vendid, u16 devid)
 {
-    u32 id = (devid << 16) | vendid;
-    int bdf, max;
-    foreachbdf(bdf, max) {
-        u32 v = pci_config_readl(bdf, PCI_VENDOR_ID);
-        if (v == id)
-            return bdf;
+    struct pci_device *pci;
+    foreachpci(pci) {
+        if (pci->vendor == vendid && pci->device == devid)
+            return pci;
     }
-    return -1;
+    return NULL;
 }
 
 // Search for a device with the specified class id.
-int
+struct pci_device *
 pci_find_class(u16 classid)
 {
-    int bdf, max;
-    foreachbdf(bdf, max) {
-        u16 v = pci_config_readw(bdf, PCI_CLASS_DEVICE);
-        if (v == classid)
-            return bdf;
+    struct pci_device *pci;
+    foreachpci(pci) {
+        if (pci->class == classid)
+            return pci;
     }
-    return -1;
+    return NULL;
 }
 
 int pci_init_device(const struct pci_device_id *ids