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 *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);

13
hw/pc.h
View file

@ -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;

View file

@ -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);
}

View file

@ -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;
}