This patch adds a prefmem64 region type. 64bit prefmem pci bars are assigned to that region if the device is either on the root bus or if they are behind a 64bit capable bridge and all other prefmem bars behind that bridge are 64bit capable too.
The patch also changes the bridge ressource allocation: Unused memory windows are disabled: If none of the devices connected to the bridge has -- say -- I/O bars, then the I/O memory window is turned off. This implies that bridges without devices don't get any ressources assigned. The pci bridge spec wants us behave that way (figured while looking at drivers/pci/setup-bus.c in the linux kernel source tree). It also avoids assigning ressources for both prefmem and prefmem64 ;)
With this patch applied seabios is able to map 64bit bars above 4G.
TODO: detect 64bit capable bridges.
Signed-off-by: Gerd Hoffmann kraxel@redhat.com --- src/acpi-dsdt.dsl | 7 +++ src/config.h | 2 + src/pciinit.c | 107 +++++++++++++++++++++++++++++++++++++++------------- 3 files changed, 89 insertions(+), 27 deletions(-)
diff --git a/src/acpi-dsdt.dsl b/src/acpi-dsdt.dsl index 7082b65..c17e947 100644 --- a/src/acpi-dsdt.dsl +++ b/src/acpi-dsdt.dsl @@ -175,6 +175,13 @@ DefinitionBlock ( 0x00000000, // Address Translation Offset 0x1EC00000, // Address Length ,, , AddressRangeMemory, TypeStatic) + QWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, Cacheable, ReadWrite, + 0x00000000, // Address Space Granularity + 0x8000000000, // Address Range Minimum + 0xFFFFFFFFFF, // Address Range Maximum + 0x00000000, // Address Translation Offset + 0x8000000000, // Address Length + ,, , AddressRangeMemory, TypeStatic) }) } } diff --git a/src/config.h b/src/config.h index b0187a4..5850476 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 0xFFFFFFFFFFULL
#define BUILD_IOAPIC_ADDR 0xfec00000 #define BUILD_HPET_ADDRESS 0xfed00000 diff --git a/src/pciinit.c b/src/pciinit.c index a98f6e3..927e59a 100644 --- a/src/pciinit.c +++ b/src/pciinit.c @@ -22,13 +22,15 @@ enum pci_region_type { PCI_REGION_TYPE_IO, PCI_REGION_TYPE_MEM, PCI_REGION_TYPE_PREFMEM, + PCI_REGION_TYPE_PREFMEM64, PCI_REGION_TYPE_COUNT, };
static const char *region_type_name[] = { - [ PCI_REGION_TYPE_IO ] = "io", - [ PCI_REGION_TYPE_MEM ] = "mem", - [ PCI_REGION_TYPE_PREFMEM ] = "prefmem", + [ PCI_REGION_TYPE_IO ] = "io", + [ PCI_REGION_TYPE_MEM ] = "mem", + [ PCI_REGION_TYPE_PREFMEM ] = "prefmem", + [ PCI_REGION_TYPE_PREFMEM64 ] = "prefmem64", };
struct pci_bus { @@ -42,6 +44,7 @@ struct pci_bus { u64 bases[32 - PCI_MEM_INDEX_SHIFT]; u64 base; } r[PCI_REGION_TYPE_COUNT]; + int use_prefmem64; struct pci_device *bus_dev; };
@@ -65,13 +68,17 @@ static u32 pci_index_to_size(int index, enum pci_region_type type) return 0x1 << (index + shift); }
-static enum pci_region_type pci_addr_to_type(u32 addr) +static enum pci_region_type pci_addr_to_type(struct pci_bus *bus, struct pci_bar *bar) { - if (addr & PCI_BASE_ADDRESS_SPACE_IO) + if (!bar->ismem) return PCI_REGION_TYPE_IO; - if (addr & PCI_BASE_ADDRESS_MEM_PREFETCH) + if (!bar->isprefetch) + return PCI_REGION_TYPE_MEM; + if (!bar->is64) return PCI_REGION_TYPE_PREFMEM; - return PCI_REGION_TYPE_MEM; + if (bus->bus_dev != NULL && !bus->use_prefmem64) + return PCI_REGION_TYPE_PREFMEM; + return PCI_REGION_TYPE_PREFMEM64; }
static u32 pci_bar(struct pci_device *pci, int region_num) @@ -410,11 +417,16 @@ static void pci_bios_check_devices(struct pci_bus *busses) continue; bus = &busses[pci->secondary_bus]; bus->bus_dev = pci; + /* + * TODO: figure whenever the bridge can handle 64bit prefmem + * and set bus->use_prefmem64 if so. + */ }
// discover pci bars foreachpci(pci) { num_regions = (pci->class == PCI_CLASS_BRIDGE_PCI) ? 2 : PCI_NUM_REGIONS; + bus = &busses[pci_bdf_to_bus(pci->bdf)]; dprintf(1, "PCI: check device bdf=%02x:%02x.%x\n", pci_bdf_to_bus(pci->bdf), pci_bdf_to_dev(pci->bdf), pci_bdf_to_fn(pci->bdf)); @@ -423,6 +435,10 @@ static void pci_bios_check_devices(struct pci_bus *busses) if (pci->bars[i].addr == 0) continue;
+ if (pci->bars[i].ismem && pci->bars[i].isprefetch && !pci->bars[i].is64) { + bus->use_prefmem64 = 0; + } + if (pci->bars[i].is64) i++; } @@ -437,7 +453,7 @@ static void pci_bios_check_devices(struct pci_bus *busses) if (pci->bars[i].addr == 0) continue;
- type = pci_addr_to_type(pci->bars[i].addr); + type = pci_addr_to_type(bus, &pci->bars[i]); pci_bios_bus_reserve(bus, type, pci->bars[i].size);
@@ -457,28 +473,38 @@ static void pci_bios_check_devices(struct pci_bus *busses) for (type = 0; type < PCI_REGION_TYPE_COUNT; type++) { u64 limit = (type == PCI_REGION_TYPE_IO) ? PCI_BRIDGE_IO_MIN : PCI_BRIDGE_MEM_MIN; + if (s->r[type].sum == 0) + continue; s->r[type].size = s->r[type].sum; if (s->r[type].size < limit) s->r[type].size = limit; s->r[type].size = pci_size_roundup(s->r[type].size); pci_bios_bus_reserve(parent, type, s->r[type].size); } - dprintf(1, "PCI: secondary bus %d sizes: io %llx, mem %llx, prefmem %llx\n", + dprintf(1, "PCI: secondary bus %d sizes: io %llx, mem %llx, " + "prefmem %llx, prefmem64 %llx\n", secondary_bus, s->r[PCI_REGION_TYPE_IO].size, s->r[PCI_REGION_TYPE_MEM].size, - s->r[PCI_REGION_TYPE_PREFMEM].size); + s->r[PCI_REGION_TYPE_PREFMEM].size, + s->r[PCI_REGION_TYPE_PREFMEM64].size); } }
#define ROOT_BASE(top, sum, max) ALIGN_DOWN((top)-(sum),(max) ?: 1)
// Setup region bases (given the regions' size and alignment) -static int pci_bios_init_root_regions(struct pci_bus *bus, u32 start, u32 end) +static int pci_bios_init_root_regions(struct pci_bus *bus) { + u32 start = BUILD_PCIMEM_START; + u32 end = BUILD_PCIMEM_END; + u64 start64 = BUILD_PCIMEM64_START; + u64 end64 = BUILD_PCIMEM64_END; + bus->r[PCI_REGION_TYPE_IO].base = 0xc000;
int reg1 = PCI_REGION_TYPE_PREFMEM, reg2 = PCI_REGION_TYPE_MEM; + int reg3 = PCI_REGION_TYPE_PREFMEM64; 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; @@ -490,6 +516,12 @@ static int pci_bios_init_root_regions(struct pci_bus *bus, u32 start, u32 end) if (bus->r[reg1].base < start) // Memory range requested is larger than available. return -1; + + bus->r[reg3].base = ROOT_BASE(end64, bus->r[reg3].sum, bus->r[reg3].max); + if (bus->r[reg3].base < start64) + // Memory range requested is larger than available. + return -1; + return 0; }
@@ -504,6 +536,8 @@ static void pci_bios_init_bus_bases(struct pci_bus *bus) int type, i;
for (type = 0; type < PCI_REGION_TYPE_COUNT; type++) { + if (bus->r[type].sum == 0) + continue; dprintf(1, " type %s max %llx sum %llx base %llx\n", region_type_name[type], bus->r[type].max, bus->r[type].sum, bus->r[type].base); base = bus->r[type].base; @@ -512,7 +546,7 @@ static void pci_bios_init_bus_bases(struct pci_bus *bus) if (!bus->r[type].count[i]) continue; newbase = base + size * bus->r[type].count[i]; - dprintf(1, " size %8llx: %d bar(s), %8llx -> %8llx\n", + dprintf(1, " size %llx: %d bar(s), %llx -> %llx\n", size, bus->r[type].count[i], base, newbase - 1); bus->r[type].bases[i] = base; base = newbase; @@ -520,9 +554,10 @@ static void pci_bios_init_bus_bases(struct pci_bus *bus) } }
-static u32 pci_bios_bus_get_addr(struct pci_bus *bus, int type, u32 size) +static u64 pci_bios_bus_get_addr(struct pci_bus *bus, int type, u64 size) { - u32 index, addr; + u32 index; + u64 addr;
index = pci_size_to_index(size, type); addr = bus->r[type].bases[index]; @@ -550,30 +585,51 @@ static void pci_bios_map_devices(struct pci_bus *busses) struct pci_bus *parent = &busses[pci_bdf_to_bus(bdf)]; int type; for (type = 0; type < PCI_REGION_TYPE_COUNT; type++) { + if (s->r[type].sum == 0) + continue; s->r[type].base = pci_bios_bus_get_addr( parent, type, s->r[type].size); } dprintf(1, "PCI: init bases bus %d (secondary)\n", secondary_bus); pci_bios_init_bus_bases(s);
- u64 base = s->r[PCI_REGION_TYPE_IO].base; - u64 limit = base + s->r[PCI_REGION_TYPE_IO].size - 1; + u64 base, limit; + if (s->r[PCI_REGION_TYPE_IO].sum) { + base = s->r[PCI_REGION_TYPE_IO].base; + limit = base + s->r[PCI_REGION_TYPE_IO].size - 1; + } else { + base = PCI_BRIDGE_IO_MIN; + limit = 0; + } pci_config_writeb(bdf, PCI_IO_BASE, base >> PCI_IO_SHIFT); pci_config_writew(bdf, PCI_IO_BASE_UPPER16, 0); pci_config_writeb(bdf, PCI_IO_LIMIT, limit >> PCI_IO_SHIFT); pci_config_writew(bdf, PCI_IO_LIMIT_UPPER16, 0);
- base = s->r[PCI_REGION_TYPE_MEM].base; - limit = base + s->r[PCI_REGION_TYPE_MEM].size - 1; + if (s->r[PCI_REGION_TYPE_MEM].sum) { + base = s->r[PCI_REGION_TYPE_MEM].base; + limit = base + s->r[PCI_REGION_TYPE_MEM].size - 1; + } else { + base = PCI_BRIDGE_MEM_MIN; + limit = 0; + } pci_config_writew(bdf, PCI_MEMORY_BASE, base >> PCI_MEMORY_SHIFT); pci_config_writew(bdf, PCI_MEMORY_LIMIT, limit >> PCI_MEMORY_SHIFT);
- base = s->r[PCI_REGION_TYPE_PREFMEM].base; - limit = base + s->r[PCI_REGION_TYPE_PREFMEM].size - 1; + if (s->r[PCI_REGION_TYPE_PREFMEM].sum) { + base = s->r[PCI_REGION_TYPE_PREFMEM].base; + limit = base + s->r[PCI_REGION_TYPE_PREFMEM].size - 1; + } else if (s->r[PCI_REGION_TYPE_PREFMEM64].sum) { + base = s->r[PCI_REGION_TYPE_PREFMEM64].base; + limit = base + s->r[PCI_REGION_TYPE_PREFMEM64].size - 1; + } else { + base = PCI_BRIDGE_MEM_MIN; + limit = 0; + } pci_config_writew(bdf, PCI_PREF_MEMORY_BASE, base >> PCI_PREF_MEMORY_SHIFT); pci_config_writew(bdf, PCI_PREF_MEMORY_LIMIT, limit >> PCI_PREF_MEMORY_SHIFT); - pci_config_writel(bdf, PCI_PREF_BASE_UPPER32, 0); - pci_config_writel(bdf, PCI_PREF_LIMIT_UPPER32, 0); + pci_config_writel(bdf, PCI_PREF_BASE_UPPER32, base >> 32); + pci_config_writel(bdf, PCI_PREF_LIMIT_UPPER32, base >> 32); }
// Map regions on each device. @@ -588,7 +644,7 @@ static void pci_bios_map_devices(struct pci_bus *busses) if (pci->bars[i].addr == 0) continue;
- int type = pci_addr_to_type(pci->bars[i].addr); + int type = pci_addr_to_type(bus, &pci->bars[i]); u64 addr = pci_bios_bus_get_addr(bus, type, pci->bars[i].size); dprintf(1, " bar %d, addr %llx, size %llx [%s]\n", i, addr, pci->bars[i].size, region_type_name[type]); @@ -617,9 +673,6 @@ pci_setup(void)
dprintf(3, "pci setup\n");
- u32 start = BUILD_PCIMEM_START; - u32 end = BUILD_PCIMEM_END; - dprintf(1, "=== PCI bus & bridge init ===\n"); if (pci_probe_host() != 0) { return; @@ -637,7 +690,7 @@ pci_setup(void) } memset(busses, 0, sizeof(*busses) * (MaxPCIBus + 1)); pci_bios_check_devices(busses); - if (pci_bios_init_root_regions(&busses[0], start, end) != 0) { + if (pci_bios_init_root_regions(&busses[0]) != 0) { panic("PCI: out of address space\n"); }