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>
This commit is contained in:
Avi Kivity 2011-08-15 17:17:38 +03:00 committed by Anthony Liguori
parent be20f9e902
commit ae0a54664c
4 changed files with 115 additions and 46 deletions

11
hw/pc.c
View file

@ -964,7 +964,9 @@ void pc_memory_init(MemoryRegion *system_memory,
const char *kernel_cmdline, const char *kernel_cmdline,
const char *initrd_filename, const char *initrd_filename,
ram_addr_t below_4g_mem_size, 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; char *filename;
int ret, linux_boot, i; int ret, linux_boot, i;
@ -982,6 +984,7 @@ void pc_memory_init(MemoryRegion *system_memory,
ram = g_malloc(sizeof(*ram)); ram = g_malloc(sizeof(*ram));
memory_region_init_ram(ram, NULL, "pc.ram", memory_region_init_ram(ram, NULL, "pc.ram",
below_4g_mem_size + above_4g_mem_size); below_4g_mem_size + above_4g_mem_size);
*ram_memory = ram;
ram_below_4g = g_malloc(sizeof(*ram_below_4g)); ram_below_4g = g_malloc(sizeof(*ram_below_4g));
memory_region_init_alias(ram_below_4g, "ram-below-4g", ram, memory_region_init_alias(ram_below_4g, "ram-below-4g", ram,
0, below_4g_mem_size); 0, below_4g_mem_size);
@ -1026,7 +1029,7 @@ void pc_memory_init(MemoryRegion *system_memory,
isa_bios = g_malloc(sizeof(*isa_bios)); isa_bios = g_malloc(sizeof(*isa_bios));
memory_region_init_alias(isa_bios, "isa-bios", bios, memory_region_init_alias(isa_bios, "isa-bios", bios,
bios_size - isa_bios_size, isa_bios_size); 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, 0x100000 - isa_bios_size,
isa_bios, isa_bios,
1); 1);
@ -1034,13 +1037,13 @@ void pc_memory_init(MemoryRegion *system_memory,
option_rom_mr = g_malloc(sizeof(*option_rom_mr)); option_rom_mr = g_malloc(sizeof(*option_rom_mr));
memory_region_init_ram(option_rom_mr, NULL, "pc.rom", PC_ROM_SIZE); 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, PC_ROM_MIN_VGA,
option_rom_mr, option_rom_mr,
1); 1);
/* map all the bios at the top of memory */ /* 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), (uint32_t)(-bios_size),
bios); bios);

13
hw/pc.h
View file

@ -136,7 +136,9 @@ void pc_memory_init(MemoryRegion *system_memory,
const char *kernel_cmdline, const char *kernel_cmdline,
const char *initrd_filename, const char *initrd_filename,
ram_addr_t below_4g_mem_size, 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); qemu_irq *pc_allocate_cpu_irq(void);
void pc_vga_init(PCIBus *pci_bus); void pc_vga_init(PCIBus *pci_bus);
void pc_basic_device_init(qemu_irq *isa_irq, 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, qemu_irq *pic,
MemoryRegion *address_space_mem, MemoryRegion *address_space_mem,
MemoryRegion *address_space_io, MemoryRegion *address_space_io,
ram_addr_t ram_size); ram_addr_t ram_size,
void i440fx_init_memory_mappings(PCII440FXState *d); 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 */ /* piix4.c */
extern PCIDevice *piix4_dev; extern PCIDevice *piix4_dev;

View file

@ -22,6 +22,8 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#include <glib.h>
#include "hw.h" #include "hw.h"
#include "pc.h" #include "pc.h"
#include "apic.h" #include "apic.h"
@ -93,6 +95,8 @@ static void pc_init1(MemoryRegion *system_memory,
DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS];
BusState *idebus[MAX_IDE_BUS]; BusState *idebus[MAX_IDE_BUS];
ISADevice *rtc_state; ISADevice *rtc_state;
MemoryRegion *ram_memory;
MemoryRegion *pci_memory;
pc_cpus_init(cpu_model); pc_cpus_init(cpu_model);
@ -108,11 +112,15 @@ static void pc_init1(MemoryRegion *system_memory,
below_4g_mem_size = ram_size; 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 */ /* allocate ram and load rom/bios */
if (!xen_enabled()) { if (!xen_enabled()) {
pc_memory_init(system_memory, pc_memory_init(system_memory,
kernel_filename, kernel_cmdline, initrd_filename, 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()) { if (!xen_enabled()) {
@ -130,7 +138,14 @@ static void pc_init1(MemoryRegion *system_memory,
if (pci_enabled) { if (pci_enabled) {
pci_bus = i440fx_init(&i440fx_state, &piix3_devfn, isa_irq, 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 { } else {
pci_bus = NULL; pci_bus = NULL;
i440fx_state = NULL; i440fx_state = NULL;
@ -202,10 +217,6 @@ static void pc_init1(MemoryRegion *system_memory,
smbus_eeprom_init(smbus, 8, NULL, 0); smbus_eeprom_init(smbus, 8, NULL, 0);
} }
if (i440fx_state) {
i440fx_init_memory_mappings(i440fx_state);
}
if (pci_enabled) { if (pci_enabled) {
pc_pci_device_init(pci_bus); pc_pci_device_init(pci_bus);
} }

View file

@ -66,10 +66,22 @@ typedef struct PIIX3State {
int32_t pci_irq_levels_vmstate[PIIX_NUM_PIRQS]; int32_t pci_irq_levels_vmstate[PIIX_NUM_PIRQS];
} PIIX3State; } PIIX3State;
typedef struct PAMMemoryRegion {
MemoryRegion mem;
bool initialized;
} PAMMemoryRegion;
struct PCII440FXState { struct PCII440FXState {
PCIDevice dev; 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; uint8_t smm_enabled;
bool smram_enabled;
PIIX3State *piix3; PIIX3State *piix3;
}; };
@ -92,50 +104,62 @@ static int pci_slot_get_pirq(PCIDevice *pci_dev, int pci_intx)
return (pci_intx + slot_addend) & 3; 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); // printf("ISA mapping %08x-0x%08x: %d\n", start, end, r);
switch(r) { switch(r) {
case 3: case 3:
/* RAM */ /* RAM */
cpu_register_physical_memory(start, end - start, memory_region_init_alias(&mem->mem, "pam-ram", d->ram_memory,
start); start, end - start);
break; break;
case 1: case 1:
/* ROM (XXX: not quite correct) */ /* ROM (XXX: not quite correct) */
cpu_register_physical_memory(start, end - start, memory_region_init_alias(&mem->mem, "pam-rom", d->ram_memory,
start | IO_MEM_ROM); start, end - start);
memory_region_set_readonly(&mem->mem, true);
break; break;
case 2: case 2:
case 0: case 0:
/* XXX: should distinguish read/write cases */ /* XXX: should distinguish read/write cases */
for(addr = start; addr < end; addr += 4096) { memory_region_init_alias(&mem->mem, "pam-pci", d->pci_address_space,
cpu_register_physical_memory(addr, 4096, start, end - start);
d->isa_page_descs[(addr - 0xa0000) >> 12]);
}
break; break;
} }
memory_region_add_subregion_overlap(d->system_memory,
start, &mem->mem, 1);
mem->initialized = true;
} }
static void i440fx_update_memory_mappings(PCII440FXState *d) static void i440fx_update_memory_mappings(PCII440FXState *d)
{ {
int i, r; 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++) { for(i = 0; i < 12; i++) {
r = (d->dev.config[(i >> 1) + (I440FX_PAM + 1)] >> ((i & 1) * 4)) & 3; 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]; smram = d->dev.config[I440FX_SMRAM];
if ((d->smm_enabled && (smram & 0x08)) || (smram & 0x40)) { 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 { } else {
for(addr = 0xa0000; addr < 0xc0000; addr += 4096) { if (d->smram_enabled) {
cpu_register_physical_memory(addr, 4096, memory_region_add_subregion_overlap(d->system_memory, 0xa0000,
d->isa_page_descs[(addr - 0xa0000) >> 12]); &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, static void i440fx_write_config(PCIDevice *dev,
uint32_t address, uint32_t val, int len) uint32_t address, uint32_t val, int len)
{ {
@ -244,24 +257,48 @@ static PCIBus *i440fx_common_init(const char *device_name,
qemu_irq *pic, qemu_irq *pic,
MemoryRegion *address_space_mem, MemoryRegion *address_space_mem,
MemoryRegion *address_space_io, 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; DeviceState *dev;
PCIBus *b; PCIBus *b;
PCIDevice *d; PCIDevice *d;
I440FXState *s; I440FXState *s;
PIIX3State *piix3; PIIX3State *piix3;
PCII440FXState *f;
dev = qdev_create(NULL, "i440FX-pcihost"); dev = qdev_create(NULL, "i440FX-pcihost");
s = FROM_SYSBUS(I440FXState, sysbus_from_qdev(dev)); s = FROM_SYSBUS(I440FXState, sysbus_from_qdev(dev));
s->address_space = address_space_mem; 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); address_space_io, 0);
s->bus = b; s->bus = b;
qdev_init_nofail(dev); qdev_init_nofail(dev);
d = pci_create_simple(b, 0, device_name); d = pci_create_simple(b, 0, device_name);
*pi440fx_state = DO_UPCAST(PCII440FXState, dev, d); *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 /* Xen supports additional interrupt routes from the PCI devices to
* the IOAPIC: the four pins of each PCI device on the bus are also * 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; ram_size = 255;
(*pi440fx_state)->dev.config[0x57]=ram_size; (*pi440fx_state)->dev.config[0x57]=ram_size;
i440fx_update_memory_mappings(f);
return b; return b;
} }
@ -296,12 +335,21 @@ PCIBus *i440fx_init(PCII440FXState **pi440fx_state, int *piix3_devfn,
qemu_irq *pic, qemu_irq *pic,
MemoryRegion *address_space_mem, MemoryRegion *address_space_mem,
MemoryRegion *address_space_io, 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; PCIBus *b;
b = i440fx_common_init("i440FX", pi440fx_state, piix3_devfn, pic, 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; return b;
} }