The PCI code already knows about option ROMs, so we just need to mmap some space for it, load it with a copy of the contents, and complete the plubming to the generic code. With this a Linux guest can get access to the ROM contents via /sys/bus/pci/devices/<dev>/rom. This might also enable the BIOS to execute ROMs by loading them dynamically from the device rather than hoping they all fit into RAM. Signed-off-by: Alex Williamson <alex.williamson@xxxxxx> --- hw/device-assignment.c | 60 ++++++++++++++++++++++++++++++++++++------------ hw/device-assignment.h | 5 +--- 2 files changed, 46 insertions(+), 19 deletions(-) diff --git a/hw/device-assignment.c b/hw/device-assignment.c index 65920d0..dfcd670 100644 --- a/hw/device-assignment.c +++ b/hw/device-assignment.c @@ -286,8 +286,8 @@ static void assigned_dev_pci_write_config(PCIDevice *d, uint32_t address, /* Continue to program the card */ } - if ((address >= 0x10 && address <= 0x24) || address == 0x34 || - address == 0x3c || address == 0x3d || + if ((address >= 0x10 && address <= 0x24) || address == 0x30 || + address == 0x34 || address == 0x3c || address == 0x3d || pci_access_cap_config(d, address, len)) { /* used for update-mappings (BAR emulation) */ pci_default_write_config(d, address, val, len); @@ -322,8 +322,8 @@ static uint32_t assigned_dev_pci_read_config(PCIDevice *d, uint32_t address, AssignedDevice *pci_dev = container_of(d, AssignedDevice, dev); if (address < 0x4 || (pci_dev->need_emulate_cmd && address == 0x4) || - (address >= 0x10 && address <= 0x24) || address == 0x34 || - address == 0x3c || address == 0x3d || + (address >= 0x10 && address <= 0x24) || address == 0x30 || + address == 0x34 || address == 0x3c || address == 0x3d || pci_access_cap_config(d, address, len)) { val = pci_default_read_config(d, address, len); DEBUG("(%x.%x): address=%04x val=0x%08x len=%d\n", @@ -384,11 +384,20 @@ static int assigned_dev_register_regions(PCIRegion *io_regions, /* map physical memory */ pci_dev->v_addrs[i].e_physbase = cur_region->base_addr; - pci_dev->v_addrs[i].u.r_virtbase = - mmap(NULL, - (cur_region->size + 0xFFF) & 0xFFFFF000, - PROT_WRITE | PROT_READ, MAP_SHARED, - cur_region->resource_fd, (off_t) 0); + if (i == PCI_ROM_SLOT) { + pci_dev->v_addrs[i].u.r_virtbase = + mmap(NULL, + (cur_region->size + 0xFFF) & 0xFFFFF000, + PROT_WRITE | PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, + 0, (off_t) 0); + + } else { + pci_dev->v_addrs[i].u.r_virtbase = + mmap(NULL, + (cur_region->size + 0xFFF) & 0xFFFFF000, + PROT_WRITE | PROT_READ, MAP_SHARED, + cur_region->resource_fd, (off_t) 0); + } if (pci_dev->v_addrs[i].u.r_virtbase == MAP_FAILED) { pci_dev->v_addrs[i].u.r_virtbase = NULL; @@ -397,6 +406,14 @@ static int assigned_dev_register_regions(PCIRegion *io_regions, (uint32_t) (cur_region->base_addr)); return -1; } + + if (i == PCI_ROM_SLOT) { + memset(pci_dev->v_addrs[i].u.r_virtbase, 0, + (cur_region->size + 0xFFF) & 0xFFFFF000); + mprotect(pci_dev->v_addrs[PCI_ROM_SLOT].u.r_virtbase, + (cur_region->size + 0xFFF) & 0xFFFFF000, PROT_READ); + } + pci_dev->v_addrs[i].r_size = cur_region->size; pci_dev->v_addrs[i].e_size = 0; @@ -468,7 +485,7 @@ again: return 1; } - for (r = 0; r < MAX_IO_REGIONS; r++) { + for (r = 0; r < PCI_NUM_REGIONS; r++) { if (fscanf(f, "%lli %lli %lli\n", &start, &end, &flags) != 3) break; @@ -480,11 +497,13 @@ again: continue; if (flags & IORESOURCE_MEM) { flags &= ~IORESOURCE_IO; - snprintf(name, sizeof(name), "%sresource%d", dir, r); - fd = open(name, O_RDWR); - if (fd == -1) - continue; /* probably ROM */ - rp->resource_fd = fd; + if (r != PCI_ROM_SLOT) { + snprintf(name, sizeof(name), "%sresource%d", dir, r); + fd = open(name, O_RDWR); + if (fd == -1) + continue; + rp->resource_fd = fd; + } } else flags &= ~IORESOURCE_PREFETCH; @@ -1390,6 +1409,17 @@ ram_addr_t assigned_dev_load_option_roms(ram_addr_t rom_base_offset) continue; } + /* Copy ROM contents into the space backing the ROM BAR */ + if (adev->assigned_dev->v_addrs[PCI_ROM_SLOT].r_size >= size && + adev->assigned_dev->v_addrs[PCI_ROM_SLOT].u.r_virtbase) { + mprotect(adev->assigned_dev->v_addrs[PCI_ROM_SLOT].u.r_virtbase, + size, PROT_READ | PROT_WRITE); + memcpy(adev->assigned_dev->v_addrs[PCI_ROM_SLOT].u.r_virtbase, + buf, size); + mprotect(adev->assigned_dev->v_addrs[PCI_ROM_SLOT].u.r_virtbase, + size, PROT_READ); + } + /* Scan the buffer for suitable ROMs and increase the offset */ offset += scan_option_rom(adev->assigned_dev->dev.devfn, buf, offset); diff --git a/hw/device-assignment.h b/hw/device-assignment.h index c691e11..713f9b7 100644 --- a/hw/device-assignment.h +++ b/hw/device-assignment.h @@ -36,9 +36,6 @@ /* From include/linux/pci.h in the kernel sources */ #define PCI_DEVFN(slot, func) ((((slot) & 0x1f) << 3) | ((func) & 0x07)) -/* The number of BARs in the config space header */ -#define MAX_IO_REGIONS (6) - typedef struct { int type; /* Memory or port I/O */ int valid; @@ -53,7 +50,7 @@ typedef struct { uint16_t region_number; /* number of active regions */ /* Port I/O or MMIO Regions */ - PCIRegion regions[MAX_IO_REGIONS]; + PCIRegion regions[PCI_NUM_REGIONS]; int config_fd; } PCIDevRegions; -- To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html