Load memory window setup for pci from host. This makes it possible for host to make sure setup matches hardware exactly: especially important for when ACPI tables are loaded from host. This will also make it easier to add more chipsets down the road.
Signed-off-by: Michael S. Tsirkin mst@redhat.com --- src/paravirt.c | 25 ++++++++++++++++++++++++- src/pciinit.c | 54 ++++++++++++++++++++++++++++++++++++++++-------------- src/util.h | 8 +++++++- 3 files changed, 71 insertions(+), 16 deletions(-)
diff --git a/src/paravirt.c b/src/paravirt.c index 85aa423..067a4dc 100644 --- a/src/paravirt.c +++ b/src/paravirt.c @@ -91,14 +91,35 @@ qemu_preinit(void) dprintf(1, "Ram Size=0x%08x (0x%016llx high)\n", RamSize, RamSizeOver4G); }
+struct pci_mem *qemu_get_pci_mem(void) +{ + int psize; + struct pci_mem *mem = romfile_loadfile("etc/pci-info", &psize); + if (!mem) + return NULL; + if (psize < sizeof *mem) { + free(mem); + return NULL; + } + mem->start32 = le64_to_cpu(mem->start32); + mem->end32 = le64_to_cpu(mem->end32); + mem->start64 = le64_to_cpu(mem->start64); + mem->end64 = le64_to_cpu(mem->end64); + return mem; +} + void qemu_platform_setup(void) { + struct pci_mem *mem; + if (!CONFIG_QEMU) return;
qemu_cfg_init();
+ mem = qemu_get_pci_mem(); + if (runningOnXen()) { pci_probe_devices(); xen_hypercall_setup(); @@ -107,7 +128,7 @@ qemu_platform_setup(void) }
// Initialize pci - pci_setup(); + pci_setup(mem); smm_device_setup(); smm_setup();
@@ -120,6 +141,8 @@ qemu_platform_setup(void) mptable_setup(); smbios_setup(); acpi_setup(); + if (mem) + free(mem); }
diff --git a/src/pciinit.c b/src/pciinit.c index bb9355f..f3bac02 100644 --- a/src/pciinit.c +++ b/src/pciinit.c @@ -361,6 +361,8 @@ static void pci_enable_default_vga(void)
void i440fx_mem_addr_setup(struct pci_device *dev, void *arg) { + if (arg) + /* use supplied memory */; if (RamSize <= 0x80000000) pcimem_start = 0x80000000; else if (RamSize <= 0xc0000000) @@ -383,8 +385,9 @@ void mch_mem_addr_setup(struct pci_device *dev, void *arg) pci_config_writel(bdf, Q35_HOST_BRIDGE_PCIEXBAR, lower); add_e820(addr, size, E820_RESERVED);
- /* setup pci i/o window (above mmconfig) */ - pcimem_start = addr + size; + /* unless done already, setup pci i/o window (above mmconfig) */ + if (!arg) + pcimem_start = addr + size;
pci_slot_get_irq = mch_pci_slot_get_irq; } @@ -397,11 +400,11 @@ static const struct pci_device_id pci_platform_tbl[] = { PCI_DEVICE_END };
-static void pci_bios_init_platform(void) +static void pci_bios_init_platform(struct pci_mem *mem) { struct pci_device *pci; foreachpci(pci) { - pci_init_device(pci_platform_tbl, pci, NULL); + pci_init_device(pci_platform_tbl, pci, mem); } }
@@ -762,10 +765,14 @@ static void pci_region_map_entries(struct pci_bus *busses, struct pci_region *r) } }
-static void pci_bios_map_devices(struct pci_bus *busses) +static void pci_bios_map_devices(struct pci_bus *busses, struct pci_mem *mem) { if (pci_bios_init_root_regions(busses)) { struct pci_region r64_mem, r64_pref; + + if (mem && mem->start64 <= mem->end64) + panic("PCI: out of 32bit address space\n"); + r64_mem.list = NULL; r64_pref.list = NULL; pci_region_migrate_64bit_entries(&busses[0].r[PCI_REGION_TYPE_MEM], @@ -781,14 +788,27 @@ static void pci_bios_map_devices(struct pci_bus *busses) u64 align_mem = pci_region_align(&r64_mem); u64 align_pref = pci_region_align(&r64_pref);
- r64_mem.base = ALIGN(0x100000000LL + RamSizeOver4G, align_mem); - r64_pref.base = ALIGN(r64_mem.base + sum_mem, align_pref); - pcimem64_start = r64_mem.base; - pcimem64_end = r64_pref.base + sum_pref; + if (mem) { + /* + * Non prefetcheable memory at start of the window, + * prefetcheable memory at the end. + * This way OS has the maximum flexibility for + * allocating the rest of the memory. + */ + r64_mem.base = ALIGN(mem->start64, align_mem); + r64_pref.base = ALIGN_DOWN(mem->end64 - sum_pref + 1, align_pref); + if (sum_pref && r64_pref.base < r64_mem.base + sum_mem) + panic("PCI: out of 64bit address space\n"); + } else { + r64_mem.base = ALIGN(0x100000000LL + RamSizeOver4G, align_mem); + r64_pref.base = ALIGN(r64_mem.base + sum_mem, align_pref); + pcimem64_start = r64_mem.base; + pcimem64_end = r64_pref.base + sum_pref; + }
pci_region_map_entries(busses, &r64_mem); pci_region_map_entries(busses, &r64_pref); - } else { + } else if (!mem) { // no bars mapped high -> drop 64bit window (see dsdt) pcimem64_start = 0; } @@ -807,7 +827,7 @@ static void pci_bios_map_devices(struct pci_bus *busses) ****************************************************************/
void -pci_setup(void) +pci_setup(struct pci_mem *mem) { if (!CONFIG_QEMU) return; @@ -823,8 +843,14 @@ pci_setup(void) dprintf(1, "=== PCI device probing ===\n"); pci_probe_devices();
- pcimem_start = RamSize; - pci_bios_init_platform(); + if (mem) { + pcimem_start = mem->start32; + pcimem_end = mem->end32; + pcimem64_start = mem->start64; + pcimem64_end = mem->end64; + } else + pcimem_start = RamSize; + pci_bios_init_platform(mem);
dprintf(1, "=== PCI new allocation pass #1 ===\n"); struct pci_bus *busses = malloc_tmp(sizeof(*busses) * (MaxPCIBus + 1)); @@ -837,7 +863,7 @@ pci_setup(void) return;
dprintf(1, "=== PCI new allocation pass #2 ===\n"); - pci_bios_map_devices(busses); + pci_bios_map_devices(busses, mem);
pci_bios_init_devices();
diff --git a/src/util.h b/src/util.h index 7b50c38..03fe60c 100644 --- a/src/util.h +++ b/src/util.h @@ -311,7 +311,13 @@ void qemu_prep_reset(void);
// pciinit.c extern const u8 pci_irqs[4]; -void pci_setup(void); +struct pci_mem { + u64 start32; + u64 end32; + u64 start64; + u64 end64; +}; +void pci_setup(struct pci_mem *);
// smm.c void smm_device_setup(void);