Don't leave upper bits of 64bit BARs unitialized during pci init.
[seabios.git] / src / pciinit.c
index baafa5bb722e618cced0c6332da05726ad327933..9f3fdd49f0776f519ae1d943f84939ca5c951828 100644 (file)
@@ -31,7 +31,7 @@ static const char *region_type_name[] = {
     [ PCI_REGION_TYPE_PREFMEM ] = "prefmem",
 };
 
-static struct pci_bus {
+struct pci_bus {
     struct {
         /* pci region stats */
         u32 count[32 - PCI_MEM_INDEX_SHIFT];
@@ -43,7 +43,7 @@ static struct pci_bus {
         u32 base;
     } r[PCI_REGION_TYPE_COUNT];
     struct pci_device *bus_dev;
-} *busses;
+};
 
 static int pci_size_to_index(u32 size, enum pci_region_type type)
 {
@@ -230,17 +230,19 @@ static void pci_bios_init_device(struct pci_device *pci)
     pci_init_device(pci_device_tbl, pci, NULL);
 }
 
-static void pci_bios_init_device_in_bus(int bus)
+static void pci_bios_init_devices(void)
 {
     struct pci_device *pci;
     foreachpci(pci) {
-        u8 pci_bus = pci_bdf_to_bus(pci->bdf);
-        if (pci_bus < bus)
-            continue;
-        if (pci_bus > bus)
+        if (pci_bdf_to_bus(pci->bdf) != 0)
+            // Only init devices on host bus.
             break;
         pci_bios_init_device(pci);
     }
+
+    foreachpci(pci) {
+        pci_init_device(pci_isa_bridge_tbl, pci, NULL);
+    }
 }
 
 
@@ -362,7 +364,7 @@ static void pci_bios_bus_reserve(struct pci_bus *bus, int type, u32 size)
         bus->r[type].max = size;
 }
 
-static void pci_bios_check_devices(void)
+static void pci_bios_check_devices(struct pci_bus *busses)
 {
     dprintf(1, "PCI: check devices\n");
 
@@ -420,38 +422,24 @@ static void pci_bios_check_devices(void)
 
 #define ROOT_BASE(top, sum, max) ALIGN_DOWN((top)-(sum),(max) ?: 1)
 
-static int pci_bios_init_root_regions(u32 start, u32 end)
+// Setup region bases (given the regions' size and alignment)
+static int pci_bios_init_root_regions(struct pci_bus *bus, u32 start, u32 end)
 {
-    struct pci_bus *bus = &busses[0];
-
     bus->r[PCI_REGION_TYPE_IO].base = 0xc000;
 
-    if (bus->r[PCI_REGION_TYPE_MEM].sum < bus->r[PCI_REGION_TYPE_PREFMEM].sum) {
-        bus->r[PCI_REGION_TYPE_MEM].base =
-            ROOT_BASE(end,
-                      bus->r[PCI_REGION_TYPE_MEM].sum,
-                      bus->r[PCI_REGION_TYPE_MEM].max);
-        bus->r[PCI_REGION_TYPE_PREFMEM].base =
-            ROOT_BASE(bus->r[PCI_REGION_TYPE_MEM].base,
-                      bus->r[PCI_REGION_TYPE_PREFMEM].sum,
-                      bus->r[PCI_REGION_TYPE_PREFMEM].max);
-        if (bus->r[PCI_REGION_TYPE_PREFMEM].base >= start) {
-            return 0;
-        }
-    } else {
-        bus->r[PCI_REGION_TYPE_PREFMEM].base =
-            ROOT_BASE(end,
-                      bus->r[PCI_REGION_TYPE_PREFMEM].sum,
-                      bus->r[PCI_REGION_TYPE_PREFMEM].max);
-        bus->r[PCI_REGION_TYPE_MEM].base =
-            ROOT_BASE(bus->r[PCI_REGION_TYPE_PREFMEM].base,
-                      bus->r[PCI_REGION_TYPE_MEM].sum,
-                      bus->r[PCI_REGION_TYPE_MEM].max);
-        if (bus->r[PCI_REGION_TYPE_MEM].base >= start) {
-            return 0;
-        }
+    int reg1 = PCI_REGION_TYPE_PREFMEM, reg2 = PCI_REGION_TYPE_MEM;
+    if (bus->r[reg1].sum < bus->r[reg2].sum) {
+        // Swap regions so larger area is more likely to align well.
+        reg1 = PCI_REGION_TYPE_MEM;
+        reg2 = PCI_REGION_TYPE_PREFMEM;
     }
-    return -1;
+    bus->r[reg2].base = ROOT_BASE(end, bus->r[reg2].sum, bus->r[reg2].max);
+    bus->r[reg1].base = ROOT_BASE(bus->r[reg2].base, bus->r[reg1].sum
+                                  , bus->r[reg1].max);
+    if (bus->r[reg1].base < start)
+        // Memory range requested is larger than available.
+        return -1;
+    return 0;
 }
 
 
@@ -495,8 +483,12 @@ static u32 pci_bios_bus_get_addr(struct pci_bus *bus, int type, u32 size)
 #define PCI_MEMORY_SHIFT        16
 #define PCI_PREF_MEMORY_SHIFT   16
 
-static void pci_bios_map_devices(void)
+static void pci_bios_map_devices(struct pci_bus *busses)
 {
+    // Setup bases for root bus.
+    dprintf(1, "PCI: init bases bus 0 (primary)\n");
+    pci_bios_init_bus_bases(&busses[0]);
+
     // Map regions on each secondary bus.
     int secondary_bus;
     for (secondary_bus=1; secondary_bus<=MaxPCIBus; secondary_bus++) {
@@ -553,8 +545,10 @@ static void pci_bios_map_devices(void)
                     i, addr, pci->bars[i].size, region_type_name[type]);
             pci_set_io_region_addr(pci, i, addr);
 
-            if (pci->bars[i].is64)
+            if (pci->bars[i].is64) {
                 i++;
+                pci_set_io_region_addr(pci, i, 0);
+            }
         }
     }
 }
@@ -588,28 +582,21 @@ pci_setup(void)
     pci_probe_devices();
 
     dprintf(1, "=== PCI new allocation pass #1 ===\n");
-    busses = malloc_tmp(sizeof(*busses) * (MaxPCIBus + 1));
+    struct pci_bus *busses = malloc_tmp(sizeof(*busses) * (MaxPCIBus + 1));
     if (!busses) {
         warn_noalloc();
         return;
     }
     memset(busses, 0, sizeof(*busses) * (MaxPCIBus + 1));
-    pci_bios_check_devices();
-    if (pci_bios_init_root_regions(start, end) != 0) {
+    pci_bios_check_devices(busses);
+    if (pci_bios_init_root_regions(&busses[0], start, end) != 0) {
         panic("PCI: out of address space\n");
     }
 
     dprintf(1, "=== PCI new allocation pass #2 ===\n");
-    dprintf(1, "PCI: init bases bus 0 (primary)\n");
-    pci_bios_init_bus_bases(&busses[0]);
-    pci_bios_map_devices();
+    pci_bios_map_devices(busses);
 
-    pci_bios_init_device_in_bus(0 /* host bus */);
-
-    struct pci_device *pci;
-    foreachpci(pci) {
-        pci_init_device(pci_isa_bridge_tbl, pci, NULL);
-    }
+    pci_bios_init_devices();
 
     free(busses);
 }