diff mbox

[24/24] 440fx: fix PAM, PCI holes

Message ID 1312823229-12822-25-git-send-email-avi@redhat.com
State New
Headers show

Commit Message

Avi Kivity Aug. 8, 2011, 5:07 p.m. UTC
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>
---
 hw/pc.c       |   11 +++--
 hw/pc.h       |   13 +++++-
 hw/pc_piix.c  |   23 ++++++++---
 hw/piix_pci.c |  114 ++++++++++++++++++++++++++++++++++++++++----------------
 4 files changed, 115 insertions(+), 46 deletions(-)
diff mbox

Patch

diff --git a/hw/pc.c b/hw/pc.c
index f43d181..d328304 100644
--- a/hw/pc.c
+++ b/hw/pc.c
@@ -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 = qemu_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 = qemu_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 = qemu_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 = qemu_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);
 
diff --git a/hw/pc.h b/hw/pc.h
index d871fd8..dae736e 100644
--- a/hw/pc.h
+++ b/hw/pc.h
@@ -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 7dd5008..ee404d9 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 28a3ee2..c563c6e 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;
 }