diff options
author | Avi Kivity <avi@redhat.com> | 2011-08-15 17:17:38 +0300 |
---|---|---|
committer | Anthony Liguori <aliguori@us.ibm.com> | 2011-08-22 10:47:49 -0500 |
commit | ae0a54664c76f1d2a2a7a268266d47b4e8c12a4c (patch) | |
tree | b4a664a81fe572ce96640fdc74b0ec2ca172ee2a | |
parent | be20f9e902ca47ea84e1b1c1e89b0a55d59b4c11 (diff) | |
download | qemu-ae0a54664c76f1d2a2a7a268266d47b4e8c12a4c.tar.gz qemu-ae0a54664c76f1d2a2a7a268266d47b4e8c12a4c.tar.bz2 qemu-ae0a54664c76f1d2a2a7a268266d47b4e8c12a4c.zip |
440fx: fix PAM, PCI holes
The current implementation of PAM and the PCI holes is broken in several
ways:
- PCI BARs are not restricted to the PCI hole (a BAR may hide memory)
- PCI devices do not respect PAM (if a PCI device maps a region while
PAM maps the region to RAM, the request will be honored)
This patch fixes things by introducing a pci address space, and using
memory region aliases to represent PAM regions, SMRAM, and PCI holes.
The memory hierarchy looks something like
system_memory
|
+--- low memory alias (0-0xe0000000)
| |
| +-- ram@0
|
+--- high memory alias (0x100000000-EOM)
| |
| +-- ram@0xe0000000
|
+--- pci hole alias (end of low memory-0x100000000)
| |
| +-- pci@end-of-low-memory
|
|
+--- pam[n] (0xc0000-0xc3fff etc) (when set to pci, priority 1)
| |
| +-- pci@0xc4000 etc
|
+--- smram (0xa0000-0xbffff) (when set to pci/vga, priority 1)
|
+-- pci@0xa0000 etc
ram (simple ram region)
pci
|
+--- BARn
|
+--- VGA 0xa0000-0xbffff
|
+--- ROMs
Signed-off-by: Avi Kivity <avi@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
-rw-r--r-- | hw/pc.c | 11 | ||||
-rw-r--r-- | hw/pc.h | 13 | ||||
-rw-r--r-- | hw/pc_piix.c | 23 | ||||
-rw-r--r-- | hw/piix_pci.c | 114 |
4 files changed, 115 insertions, 46 deletions
@@ -964,7 +964,9 @@ void pc_memory_init(MemoryRegion *system_memory, const char *kernel_cmdline, const char *initrd_filename, ram_addr_t below_4g_mem_size, - ram_addr_t above_4g_mem_size) + ram_addr_t above_4g_mem_size, + MemoryRegion *pci_memory, + MemoryRegion **ram_memory) { char *filename; int ret, linux_boot, i; @@ -982,6 +984,7 @@ void pc_memory_init(MemoryRegion *system_memory, ram = g_malloc(sizeof(*ram)); memory_region_init_ram(ram, NULL, "pc.ram", below_4g_mem_size + above_4g_mem_size); + *ram_memory = ram; ram_below_4g = g_malloc(sizeof(*ram_below_4g)); memory_region_init_alias(ram_below_4g, "ram-below-4g", ram, 0, below_4g_mem_size); @@ -1026,7 +1029,7 @@ void pc_memory_init(MemoryRegion *system_memory, isa_bios = g_malloc(sizeof(*isa_bios)); memory_region_init_alias(isa_bios, "isa-bios", bios, bios_size - isa_bios_size, isa_bios_size); - memory_region_add_subregion_overlap(system_memory, + memory_region_add_subregion_overlap(pci_memory, 0x100000 - isa_bios_size, isa_bios, 1); @@ -1034,13 +1037,13 @@ void pc_memory_init(MemoryRegion *system_memory, option_rom_mr = g_malloc(sizeof(*option_rom_mr)); memory_region_init_ram(option_rom_mr, NULL, "pc.rom", PC_ROM_SIZE); - memory_region_add_subregion_overlap(system_memory, + memory_region_add_subregion_overlap(pci_memory, PC_ROM_MIN_VGA, option_rom_mr, 1); /* map all the bios at the top of memory */ - memory_region_add_subregion(system_memory, + memory_region_add_subregion(pci_memory, (uint32_t)(-bios_size), bios); @@ -136,7 +136,9 @@ void pc_memory_init(MemoryRegion *system_memory, const char *kernel_cmdline, const char *initrd_filename, ram_addr_t below_4g_mem_size, - ram_addr_t above_4g_mem_size); + ram_addr_t above_4g_mem_size, + MemoryRegion *pci_memory, + MemoryRegion **ram_memory); qemu_irq *pc_allocate_cpu_irq(void); void pc_vga_init(PCIBus *pci_bus); void pc_basic_device_init(qemu_irq *isa_irq, @@ -182,8 +184,13 @@ PCIBus *i440fx_init(PCII440FXState **pi440fx_state, int *piix_devfn, qemu_irq *pic, MemoryRegion *address_space_mem, MemoryRegion *address_space_io, - ram_addr_t ram_size); -void i440fx_init_memory_mappings(PCII440FXState *d); + ram_addr_t ram_size, + target_phys_addr_t pci_hole_start, + target_phys_addr_t pci_hole_size, + target_phys_addr_t pci_hole64_start, + target_phys_addr_t pci_hole64_size, + MemoryRegion *pci_memory, + MemoryRegion *ram_memory); /* piix4.c */ extern PCIDevice *piix4_dev; diff --git a/hw/pc_piix.c b/hw/pc_piix.c index a9fe5964d8..75d96d97c6 100644 --- a/hw/pc_piix.c +++ b/hw/pc_piix.c @@ -22,6 +22,8 @@ * THE SOFTWARE. */ +#include <glib.h> + #include "hw.h" #include "pc.h" #include "apic.h" @@ -93,6 +95,8 @@ static void pc_init1(MemoryRegion *system_memory, DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; BusState *idebus[MAX_IDE_BUS]; ISADevice *rtc_state; + MemoryRegion *ram_memory; + MemoryRegion *pci_memory; pc_cpus_init(cpu_model); @@ -108,11 +112,15 @@ static void pc_init1(MemoryRegion *system_memory, below_4g_mem_size = ram_size; } + pci_memory = g_new(MemoryRegion, 1); + memory_region_init(pci_memory, "pci", INT64_MAX); + /* allocate ram and load rom/bios */ if (!xen_enabled()) { pc_memory_init(system_memory, kernel_filename, kernel_cmdline, initrd_filename, - below_4g_mem_size, above_4g_mem_size); + below_4g_mem_size, above_4g_mem_size, + pci_memory, &ram_memory); } if (!xen_enabled()) { @@ -130,7 +138,14 @@ static void pc_init1(MemoryRegion *system_memory, if (pci_enabled) { pci_bus = i440fx_init(&i440fx_state, &piix3_devfn, isa_irq, - system_memory, system_io, ram_size); + system_memory, system_io, ram_size, + below_4g_mem_size, + 0x100000000ULL - below_4g_mem_size, + 0x100000000ULL + above_4g_mem_size, + (sizeof(target_phys_addr_t) == 4 + ? 0 + : ((uint64_t)1 << 62)), + pci_memory, ram_memory); } else { pci_bus = NULL; i440fx_state = NULL; @@ -202,10 +217,6 @@ static void pc_init1(MemoryRegion *system_memory, smbus_eeprom_init(smbus, 8, NULL, 0); } - if (i440fx_state) { - i440fx_init_memory_mappings(i440fx_state); - } - if (pci_enabled) { pc_pci_device_init(pci_bus); } diff --git a/hw/piix_pci.c b/hw/piix_pci.c index 28a3ee2e9c..c563c6e1a3 100644 --- a/hw/piix_pci.c +++ b/hw/piix_pci.c @@ -66,10 +66,22 @@ typedef struct PIIX3State { int32_t pci_irq_levels_vmstate[PIIX_NUM_PIRQS]; } PIIX3State; +typedef struct PAMMemoryRegion { + MemoryRegion mem; + bool initialized; +} PAMMemoryRegion; + struct PCII440FXState { PCIDevice dev; - target_phys_addr_t isa_page_descs[384 / 4]; + MemoryRegion *system_memory; + MemoryRegion *pci_address_space; + MemoryRegion *ram_memory; + MemoryRegion pci_hole; + MemoryRegion pci_hole_64bit; + PAMMemoryRegion pam_regions[13]; + MemoryRegion smram_region; uint8_t smm_enabled; + bool smram_enabled; PIIX3State *piix3; }; @@ -92,50 +104,62 @@ static int pci_slot_get_pirq(PCIDevice *pci_dev, int pci_intx) return (pci_intx + slot_addend) & 3; } -static void update_pam(PCII440FXState *d, uint32_t start, uint32_t end, int r) +static void update_pam(PCII440FXState *d, uint32_t start, uint32_t end, int r, + PAMMemoryRegion *mem) { - uint32_t addr; + if (mem->initialized) { + memory_region_del_subregion(d->system_memory, &mem->mem); + memory_region_destroy(&mem->mem); + } // printf("ISA mapping %08x-0x%08x: %d\n", start, end, r); switch(r) { case 3: /* RAM */ - cpu_register_physical_memory(start, end - start, - start); + memory_region_init_alias(&mem->mem, "pam-ram", d->ram_memory, + start, end - start); break; case 1: /* ROM (XXX: not quite correct) */ - cpu_register_physical_memory(start, end - start, - start | IO_MEM_ROM); + memory_region_init_alias(&mem->mem, "pam-rom", d->ram_memory, + start, end - start); + memory_region_set_readonly(&mem->mem, true); break; case 2: case 0: /* XXX: should distinguish read/write cases */ - for(addr = start; addr < end; addr += 4096) { - cpu_register_physical_memory(addr, 4096, - d->isa_page_descs[(addr - 0xa0000) >> 12]); - } + memory_region_init_alias(&mem->mem, "pam-pci", d->pci_address_space, + start, end - start); break; } + memory_region_add_subregion_overlap(d->system_memory, + start, &mem->mem, 1); + mem->initialized = true; } static void i440fx_update_memory_mappings(PCII440FXState *d) { int i, r; - uint32_t smram, addr; + uint32_t smram; - update_pam(d, 0xf0000, 0x100000, (d->dev.config[I440FX_PAM] >> 4) & 3); + update_pam(d, 0xf0000, 0x100000, (d->dev.config[I440FX_PAM] >> 4) & 3, + &d->pam_regions[0]); for(i = 0; i < 12; i++) { r = (d->dev.config[(i >> 1) + (I440FX_PAM + 1)] >> ((i & 1) * 4)) & 3; - update_pam(d, 0xc0000 + 0x4000 * i, 0xc0000 + 0x4000 * (i + 1), r); + update_pam(d, 0xc0000 + 0x4000 * i, 0xc0000 + 0x4000 * (i + 1), r, + &d->pam_regions[i+1]); } smram = d->dev.config[I440FX_SMRAM]; if ((d->smm_enabled && (smram & 0x08)) || (smram & 0x40)) { - cpu_register_physical_memory(0xa0000, 0x20000, 0xa0000); + if (!d->smram_enabled) { + memory_region_del_subregion(d->system_memory, &d->smram_region); + d->smram_enabled = true; + } } else { - for(addr = 0xa0000; addr < 0xc0000; addr += 4096) { - cpu_register_physical_memory(addr, 4096, - d->isa_page_descs[(addr - 0xa0000) >> 12]); + if (d->smram_enabled) { + memory_region_add_subregion_overlap(d->system_memory, 0xa0000, + &d->smram_region, 1); + d->smram_enabled = false; } } } @@ -152,17 +176,6 @@ static void i440fx_set_smm(int val, void *arg) } -/* XXX: suppress when better memory API. We make the assumption that - no device (in particular the VGA) changes the memory mappings in - the 0xa0000-0x100000 range */ -void i440fx_init_memory_mappings(PCII440FXState *d) -{ - int i; - for(i = 0; i < 96; i++) { - d->isa_page_descs[i] = cpu_get_physical_page_desc(0xa0000 + i * 0x1000); - } -} - static void i440fx_write_config(PCIDevice *dev, uint32_t address, uint32_t val, int len) { @@ -244,24 +257,48 @@ static PCIBus *i440fx_common_init(const char *device_name, qemu_irq *pic, MemoryRegion *address_space_mem, MemoryRegion *address_space_io, - ram_addr_t ram_size) + ram_addr_t ram_size, + target_phys_addr_t pci_hole_start, + target_phys_addr_t pci_hole_size, + target_phys_addr_t pci_hole64_start, + target_phys_addr_t pci_hole64_size, + MemoryRegion *pci_address_space, + MemoryRegion *ram_memory) { DeviceState *dev; PCIBus *b; PCIDevice *d; I440FXState *s; PIIX3State *piix3; + PCII440FXState *f; dev = qdev_create(NULL, "i440FX-pcihost"); s = FROM_SYSBUS(I440FXState, sysbus_from_qdev(dev)); s->address_space = address_space_mem; - b = pci_bus_new(&s->busdev.qdev, NULL, s->address_space, + b = pci_bus_new(&s->busdev.qdev, NULL, pci_address_space, address_space_io, 0); s->bus = b; qdev_init_nofail(dev); d = pci_create_simple(b, 0, device_name); *pi440fx_state = DO_UPCAST(PCII440FXState, dev, d); + f = *pi440fx_state; + f->system_memory = address_space_mem; + f->pci_address_space = pci_address_space; + f->ram_memory = ram_memory; + memory_region_init_alias(&f->pci_hole, "pci-hole", f->pci_address_space, + pci_hole_start, pci_hole_size); + memory_region_add_subregion(f->system_memory, pci_hole_start, &f->pci_hole); + memory_region_init_alias(&f->pci_hole_64bit, "pci-hole64", + f->pci_address_space, + pci_hole64_start, pci_hole64_size); + if (pci_hole64_size) { + memory_region_add_subregion(f->system_memory, pci_hole64_start, + &f->pci_hole_64bit); + } + memory_region_init_alias(&f->smram_region, "smram-region", + f->pci_address_space, 0xa0000, 0x20000); + f->smram_enabled = true; /* Xen supports additional interrupt routes from the PCI devices to * the IOAPIC: the four pins of each PCI device on the bus are also @@ -289,6 +326,8 @@ static PCIBus *i440fx_common_init(const char *device_name, ram_size = 255; (*pi440fx_state)->dev.config[0x57]=ram_size; + i440fx_update_memory_mappings(f); + return b; } @@ -296,12 +335,21 @@ PCIBus *i440fx_init(PCII440FXState **pi440fx_state, int *piix3_devfn, qemu_irq *pic, MemoryRegion *address_space_mem, MemoryRegion *address_space_io, - ram_addr_t ram_size) + ram_addr_t ram_size, + target_phys_addr_t pci_hole_start, + target_phys_addr_t pci_hole_size, + target_phys_addr_t pci_hole64_start, + target_phys_addr_t pci_hole64_size, + MemoryRegion *pci_memory, MemoryRegion *ram_memory) + { PCIBus *b; b = i440fx_common_init("i440FX", pi440fx_state, piix3_devfn, pic, - address_space_mem, address_space_io, ram_size); + address_space_mem, address_space_io, ram_size, + pci_hole_start, pci_hole_size, + pci_hole64_size, pci_hole64_size, + pci_memory, ram_memory); return b; } |