This patch improves the io address space allocation. It adds a check
that the region above 0xc000 which is traditionally used for pci io
is actually big enougth. If it isn't it tries the larger window at
0x1000. If that is to small too it errors out.
When creating guests with multiple pci-pci bridges (and devices with
io regions behind them) the 0xc000 -> 0xffff region quickly becomes
too small.
While being at it document the io address space layout used by
qemu/seabios.
Signed-off-by: Gerd Hoffmann <kraxel(a)redhat.com>
---
src/fw/pciinit.c | 45 ++++++++++++++++++++++++++++++++++++++++-----
1 file changed, 40 insertions(+), 5 deletions(-)
diff --git a/src/fw/pciinit.c b/src/fw/pciinit.c
index b0d01ca..64f1d41 100644
--- a/src/fw/pciinit.c
+++ b/src/fw/pciinit.c
@@ -705,10 +705,42 @@ static int pci_bios_check_devices(struct pci_bus *busses)
****************************************************************/
// Setup region bases (given the regions' size and alignment)
-static int pci_bios_init_root_regions(struct pci_bus *bus)
-{
- bus->r[PCI_REGION_TYPE_IO].base = 0xc000;
+static int pci_bios_init_root_regions_io(struct pci_bus *bus)
+{
+ /*
+ * QEMU I/O address space usage:
+ * 0000 - 0fff legacy isa, pci config, pci root bus, ...
+ * 1000 - 9fff free
+ * a000 - afff hotplug (cpu, pci via acpi, i440fx/piix only)
+ * b000 - bfff power management (PORT_ACPI_PM_BASE)
+ * [ qemu 1.4+ implements pci config registers
+ * properly so guests can place the registers
+ * where they want, on older versions its fixed ]
+ * c000 - ffff free, traditionally used for pci io
+ */
+ struct pci_region *r_io = &bus->r[PCI_REGION_TYPE_IO];
+ u64 sum = pci_region_sum(r_io);
+ if (sum < 0x4000) {
+ /* traditional region is big enougth, use it */
+ r_io->base = 0xc000;
+ } else if (sum < 0x9000) {
+ /* use the larger region at 0x1000 */
+ r_io->base = 0x1000;
+ } else {
+ /*
+ * Not enougth io address space -> error out.
+ *
+ * TODO: on q35 we can move PORT_ACPI_PM_BASE out of
+ * the way, then use the whole 1000 -> ffff region.
+ */
+ return -1;
+ }
+ dprintf(1, "PCI: IO: %4llx - %4llx\n", r_io->base, r_io->base + sum - 1);
+ return 0;
+}
+static int pci_bios_init_root_regions_mem(struct pci_bus *bus)
+{
struct pci_region *r_end = &bus->r[PCI_REGION_TYPE_PREFMEM];
struct pci_region *r_start = &bus->r[PCI_REGION_TYPE_MEM];
@@ -786,8 +818,11 @@ 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_io(busses))
+ panic("PCI: out of I/O address space\n");
+
dprintf(1, "PCI: 32: %016llx - %016llx\n", pcimem_start, pcimem_end);
- if (pci_bios_init_root_regions(busses)) {
+ if (pci_bios_init_root_regions_mem(busses)) {
struct pci_region r64_mem, r64_pref;
r64_mem.list.first = NULL;
r64_pref.list.first = NULL;
@@ -796,7 +831,7 @@ static void pci_bios_map_devices(struct pci_bus *busses)
pci_region_migrate_64bit_entries(&busses[0].r[PCI_REGION_TYPE_PREFMEM],
&r64_pref);
- if (pci_bios_init_root_regions(busses))
+ if (pci_bios_init_root_regions_mem(busses))
panic("PCI: out of 32bit address space\n");
u64 sum_mem = pci_region_sum(&r64_mem);
--
1.8.3.1