>From d2fa8ad56a7f02bf0167d312e909ba9198975e29 Mon Sep 17 00:00:00 2001
From: Alexey Korolev <alexey.korolev@endace.com>
Date: Thu, 19 Apr 2012 17:50:46 +1200
Subject: [PATCH 10/11] pciinit: Migrate 64bit entries to 64bit pci regions

 Migrate 64bit entries to 64bit pci regions
 if they do not fit in 32bit range. Pci region stats
 are now calculated. Added protection when total size of
 PCI resources is over 4GB.

Signed-off-by: Alexey Korolev <alexey.korolev@endace.com>
---
 src/config.h  |    2 +
 src/pciinit.c |  109 +++++++++++++++++++++++++++++++++++++++++++--------------
 2 files changed, 84 insertions(+), 27 deletions(-)

diff --git a/src/config.h b/src/config.h
index b0187a4..bbacae7 100644
--- a/src/config.h
+++ b/src/config.h
@@ -47,6 +47,8 @@
 
 #define BUILD_PCIMEM_START        0xe0000000
 #define BUILD_PCIMEM_END          0xfec00000    /* IOAPIC is mapped at */
+#define BUILD_PCIMEM64_START      0x8000000000ULL
+#define BUILD_PCIMEM64_END        0x10000000000ULL
 
 #define BUILD_IOAPIC_ADDR         0xfec00000
 #define BUILD_HPET_ADDRESS        0xfed00000
diff --git a/src/pciinit.c b/src/pciinit.c
index 9f54a87..b7f1009 100644
--- a/src/pciinit.c
+++ b/src/pciinit.c
@@ -40,8 +40,6 @@ struct pci_region_entry {
 };
 
 struct pci_region {
-    /* pci region stats */
-    u64 sum, align;
     /* pci region assignments */
     u64 base;
     struct pci_region_entry *list;
@@ -369,6 +367,50 @@ static int pci_bios_bridge_region_is64(struct pci_region *r,
     return 1;
 }
 
+static u64 pci_region_align(struct pci_region *r)
+{
+    if (!r->list)
+        return 1;
+    // The first entry in the sorted list has the largest alignment
+    return r->list->align;
+}
+
+static u64 pci_region_sum(struct pci_region *r)
+{
+    struct pci_region_entry *entry = r->list;
+    u64 sum = 0;
+    while (entry) {
+        sum += entry->size;
+        entry = entry->next;
+   }
+   return sum;
+}
+
+static void pci_region_migrate_64bit_entries(struct pci_region *from,
+                                             struct pci_region *to)
+{
+    struct pci_region_entry **pprev = &from->list;
+    struct pci_region_entry **last = &to->list;
+    while(*pprev) {
+        if ((*pprev)->is64) {
+            struct pci_region_entry *entry;
+            entry = *pprev;
+            /* Delete the entry and move next */
+            *pprev = (*pprev)->next;
+            /* Add entry at tail to keep a sorted order */
+            entry->next = NULL;
+            if (*last) {
+               (*last)->next = entry;
+                last  = &(*last)->next;
+            }
+            else
+               (*last) = entry;
+        }
+        else
+            pprev = &(*pprev)->next;
+    }
+}
+
 static struct pci_region_entry *
 pci_region_create_entry(struct pci_bus *bus, struct pci_device *dev,
                         int bar, u64 size, u64 align, int type, int is64)
@@ -394,10 +436,6 @@ pci_region_create_entry(struct pci_bus *bus, struct pci_device *dev,
     }
     entry->next = *pprev;
     *pprev = entry;
-
-    bus->r[type].sum += size;
-    if (bus->r[type].align < align)
-        bus->r[type].align = align;
     return entry;
 }
 
@@ -446,9 +484,9 @@ static int pci_bios_check_devices(struct pci_bus *busses)
         for (type = 0; type < PCI_REGION_TYPE_COUNT; type++) {
             u64 align = (type == PCI_REGION_TYPE_IO) ?
                 PCI_BRIDGE_IO_MIN : PCI_BRIDGE_MEM_MIN;
-            if (s->r[type].align > align)
-                align = s->r[type].align;
-            u64 size = ALIGN(s->r[type].sum, align);
+            if (pci_region_align(&s->r[type]) > align)
+                 align = pci_region_align(&s->r[type]);
+            u64 size = ALIGN(pci_region_sum(&s->r[type]), align);
             int is64 = pci_bios_bridge_region_is64(&s->r[type],
                                             s->bus_dev, type);
             // entry->bar is -1 if the entry represents a bridge region
@@ -464,28 +502,29 @@ static int pci_bios_check_devices(struct pci_bus *busses)
     return 0;
 }
 
-#define ROOT_BASE(top, sum, align) ALIGN_DOWN((top)-(sum),(align) ?: 1)
-
 // Setup region bases (given the regions' size and alignment)
-static void pci_bios_init_root_regions(struct pci_bus *bus)
+static int pci_bios_init_root_regions(struct pci_bus *bus)
 {
-    u64 start = BUILD_PCIMEM_START;
-    u64 end   = BUILD_PCIMEM_END;
-
     bus->r[PCI_REGION_TYPE_IO].base = 0xc000;
 
-    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;
+    struct pci_region *r_end = &bus->r[PCI_REGION_TYPE_PREFMEM];
+    struct pci_region *r_start = &bus->r[PCI_REGION_TYPE_MEM];
+
+    if (pci_region_sum(r_end) < pci_region_sum(r_start)) {
+        // Swap regions to improve alignment.
+        r_end = r_start;
+        r_start = &bus->r[PCI_REGION_TYPE_PREFMEM];
     }
-    bus->r[reg2].base = ROOT_BASE(end, bus->r[reg2].sum, bus->r[reg2].align);
-    bus->r[reg1].base = ROOT_BASE(bus->r[reg2].base, bus->r[reg1].sum
-                                  , bus->r[reg1].align);
-    if (bus->r[reg1].base < start)
+    r_end->base = ALIGN_DOWN((BUILD_PCIMEM_END - pci_region_sum(r_end)),
+                                pci_region_align(r_end));
+    r_start->base = ALIGN_DOWN((r_end->base - pci_region_sum(r_start)),
+                                pci_region_align(r_start));
+
+    if ((r_start->base < BUILD_PCIMEM_START) ||
+         (r_start->base > BUILD_PCIMEM_END))
         // Memory range requested is larger than available.
-        panic("PCI: out of address space\n");
+        return -1;
+    return 0;
 }
 
 
@@ -548,6 +587,24 @@ static void pci_region_map_entries(struct pci_bus *busses, struct pci_region *r)
 
 static void pci_bios_map_devices(struct pci_bus *busses)
 {
+    if (pci_bios_init_root_regions(busses)) {
+        struct pci_region r64_mem, r64_pref;
+        r64_mem.list = NULL;
+        r64_pref.list = NULL;
+        pci_region_migrate_64bit_entries(&busses[0].r[PCI_REGION_TYPE_MEM],
+                                         &r64_mem);
+        pci_region_migrate_64bit_entries(&busses[0].r[PCI_REGION_TYPE_PREFMEM],
+                                         &r64_pref);
+
+        if (pci_bios_init_root_regions(busses))
+            panic("PCI: out of address space\n");
+
+        r64_mem.base = BUILD_PCIMEM64_START;
+        r64_pref.base = ALIGN(r64_mem.base + pci_region_sum(&r64_mem),
+                              pci_region_align(&r64_pref));
+        pci_region_map_entries(busses, &r64_mem);
+        pci_region_map_entries(busses, &r64_pref);
+    }
     // Map regions on each device.
     int bus;
     for (bus = 0; bus<=MaxPCIBus; bus++) {
@@ -592,8 +649,6 @@ pci_setup(void)
     if (pci_bios_check_devices(busses))
         return;
 
-    pci_bios_init_root_regions(&busses[0]);
-
     dprintf(1, "=== PCI new allocation pass #2 ===\n");
     pci_bios_map_devices(busses);
 
-- 
1.7.5.4

