>From d2fa8ad56a7f02bf0167d312e909ba9198975e29 Mon Sep 17 00:00:00 2001 From: Alexey Korolev 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 --- 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