Load assigned devices' PCI option ROMs to the RAM of guest OS. And pass the corresponding devfns to BIOS.
Signed-off-by: Kechao Liu <[EMAIL PROTECTED]>
---
bios/rombios.c | 20 +++++-
qemu/hw/device-assignment.c | 140 +++++++++++++++++++++++++++++++++++++++++++
qemu/hw/device-assignment.h | 1 +
qemu/hw/pc.c | 8 ++-
4 files changed, 163 insertions(+), 6 deletions(-)
diff --git a/bios/rombios.c b/bios/rombios.c
index 9a1cdd6..6d63568 100644
--- a/bios/rombios.c
+++ b/bios/rombios.c
@@ -10216,18 +10216,30 @@ rom_scan_loop:
add al, #0x04
block_count_rounded:
- xor bx, bx ;; Restore DS back to 0000:
- mov ds, bx
push ax ;; Save AX
push di ;; Save DI
;; Push addr of ROM entry point
push cx ;; Push seg
push #0x0003 ;; Push offset
+ ;; Get the BDF into ax before invoking the option ROM
+ mov bl, [2]
+ mov al, bl
+ shr al, #7
+ cmp al, #1
+ jne fetch_bdf
+ mov ax, ds ;; Increment the DS since rom size larger than an segment
+ add ax, #0x1000
+ mov ds, ax
+fetch_bdf:
+ shl bx, #9
+ xor ax, ax
+ mov al, [bx]
+
;; Point ES:DI at "$PnP", which tells the ROM that we are a PnP BIOS.
;; That should stop it grabbing INT 19h; we will use its BEV instead.
- mov ax, #0xf000
- mov es, ax
+ mov bx, #0xf000
+ mov es, bx
lea di, pnp_string
mov bp, sp ;; Call ROM init routine using seg:off on stack
diff --git a/qemu/hw/device-assignment.c b/qemu/hw/device-assignment.c
index 7a66665..e53dda4 100644
--- a/qemu/hw/device-assignment.c
+++ b/qemu/hw/device-assignment.c
@@ -678,3 +678,143 @@ void add_assigned_devices(PCIBus *bus, const char
**devices, int n_devices)
}
}
}
+
+/* Option ROM header */
+struct option_rom_header {
+ uint8_t signature[2];
+ uint8_t rom_size;
+ uint32_t entry_point;
+ uint8_t reserved[17];
+ uint16_t pci_header_offset;
+ uint16_t expansion_header_offset;
+} __attribute__ ((packed));
+
+/* Option ROM PCI data structure */
+struct option_rom_pci_header {
+ uint8_t signature[4];
+ uint16_t vendor_id;
+ uint16_t device_id;
+ uint16_t vital_product_data_offset;
+ uint16_t structure_length;
+ uint8_t structure_revision;
+ uint8_t class_code[3];
+ uint16_t image_length;
+ uint16_t image_revision;
+ uint8_t code_type;
+ uint8_t indicator;
+ uint16_t reserved;
+} __attribute__ ((packed));
+
+/*
+ * Scan the list of Option ROMs at roms. If a suitable Option ROM is found,
+ * allocate a ram space and copy it there. Then return its size aligned to
+ * both 2KB and target page size.
+ */
+#define OPTION_ROM_ALIGN(x) (((x) + 2047) & ~2047)
+static int scan_option_rom(uint8_t devfn, void *roms, ram_addr_t offset)
+{
+ int i, size;
+ uint8_t csum;
+ ram_addr_t addr, phys_addr;
+ struct option_rom_header *rom;
+ struct option_rom_pci_header *pcih;
+
+ rom = roms;
+
+ for ( ; ; ) {
+ /* Invalid signature means we're out of option ROMs. */
+ if (strncmp((char *)rom->signature, "\x55\xaa", 2) ||
+ (rom->rom_size == 0))
+ break;
+
+ /* Invalid checksum means we're out of option ROMs. */
+ csum = 0;
+ for (i = 0; i < (rom->rom_size * 512); i++)
+ csum += ((uint8_t *)rom)[i];
+ if (csum != 0)
+ break;
+
+ /* Check the PCI header (if any) for a match. */
+ pcih = (struct option_rom_pci_header *)
+ ((char *)rom + rom->pci_header_offset);
+ if ((rom->pci_header_offset != 0) &&
+ !strncmp((char *)pcih->signature, "PCIR", 4))
+ goto found;
+
+ rom = (struct option_rom_header *)((char *)rom + rom->rom_size * 512);
+ }
+
+ return 0;
+
+ found:
+ /* The size should be both 2K-aligned and page-aligned */
+ size = (TARGET_PAGE_SIZE < 0x800)
+ ? OPTION_ROM_ALIGN(rom->rom_size * 512 + 1)
+ : TARGET_PAGE_ALIGN(rom->rom_size * 512 + 1);
+
+ /* Size of all available ram space is 0x10000 (0xd0000 to 0xe0000) */
+ if ((offset + size) > 0x10000u) {
+ fprintf(stderr, "Option ROM size %x exceeds available space\n",
+ rom->rom_size * 512);
+ return 0;
+ }
+
+ addr = qemu_ram_alloc(size);
+ phys_addr = addr + phys_ram_base;
+
+ /* Write ROM data and devfn to phys_addr */
+ memcpy((void *)phys_addr, rom, rom->rom_size * 512);
+ *(uint8_t *)(phys_addr + rom->rom_size * 512) = devfn;
+
+ cpu_register_physical_memory(0xd0000 + offset, size, addr);
+
+ return size;
+}
+
+/*
+ * Scan the assigned devices for the devices that have an option ROM,
+ * and then load the corresponding ROM data to RAM.
+ */
+ram_addr_t assigned_dev_load_option_roms(ram_addr_t rom_base_offset)
+{
+ ram_addr_t offset = rom_base_offset;
+ AssignedDevInfo *adev;
+
+ LIST_FOREACH(adev, &adev_head, next) {
+ int size;
+ void *buf;
+ FILE *fp;
+ char rom_file[64];
+ char cmd[64];
+
+ snprintf(rom_file, sizeof(rom_file),
+ "/sys/bus/pci/devices/0000:%02x:%02x.%01x/rom",
+ adev->bus, adev->dev, adev->func);
+
+ if (access(rom_file, F_OK))
+ continue;
+
+ /* Write something to the ROM file to enable it */
+ snprintf(cmd, sizeof(cmd), "echo 1 > %s", rom_file);
+ system(cmd);
+
+ fp = fopen(rom_file, "rb");
+ if (fp == NULL)
+ continue;
+
+ /* Read the data of the ROM file to the buffer */
+ fseek(fp, 0, SEEK_END);
+ size = ftell(fp);
+ fseek(fp, 0, SEEK_SET);
+ buf = malloc(size);
+ fread(buf, size, 1, fp);
+
+ /* Scan the buffer for suitable ROMs and increase the offset */
+ offset += scan_option_rom(adev->assigned_dev->dev.devfn, buf, offset);
+
+ free(buf);
+ fclose(fp);
+ }
+
+ return offset;
+}
diff --git a/qemu/hw/device-assignment.h b/qemu/hw/device-assignment.h
index c8c47d3..a565948 100644
--- a/qemu/hw/device-assignment.h
+++ b/qemu/hw/device-assignment.h
@@ -98,6 +98,7 @@ void free_assigned_device(AssignedDevInfo *adev);
PCIDevice *init_assigned_device(AssignedDevInfo *adev, PCIBus *bus);
AssignedDevInfo *add_assigned_device(const char *arg);
void add_assigned_devices(PCIBus *bus, const char **devices, int n_devices);
+ram_addr_t assigned_dev_load_option_roms(ram_addr_t rom_base_offset);
#define MAX_DEV_ASSIGN_CMDLINE 8
diff --git a/qemu/hw/pc.c b/qemu/hw/pc.c
index 3cf5a73..9b133da 100644
--- a/qemu/hw/pc.c
+++ b/qemu/hw/pc.c
@@ -809,6 +809,7 @@ static void pc_init1(ram_addr_t ram_size, int vga_ram_size,
ram_addr_t ram_addr, vga_ram_addr, bios_offset, vga_bios_offset;
ram_addr_t below_4g_mem_size, above_4g_mem_size = 0;
int bios_size, isa_bios_size, vga_bios_size, opt_rom_offset;
+ int pci_option_rom_offset;
PCIBus *pci_bus;
int piix3_devfn = -1;
CPUState *env;
@@ -971,6 +972,7 @@ static void pc_init1(ram_addr_t ram_size, int vga_ram_size,
option_rom_setup_reset(0xd0000 + offset, size);
offset += size;
}
+ pci_option_rom_offset = offset;
}
/* map all the bios at the top of memory */
@@ -1187,8 +1189,10 @@ static void pc_init1(ram_addr_t ram_size, int
vga_ram_size,
virtio_balloon_init(pci_bus);
#ifdef USE_KVM_DEVICE_ASSIGNMENT
- if (kvm_enabled())
- add_assigned_devices(pci_bus, assigned_devices, assigned_devices_index);
+ if (kvm_enabled()) {
+ add_assigned_devices(pci_bus, assigned_devices,
assigned_devices_index);
+ assigned_dev_load_option_roms(pci_option_rom_offset);
+ }
#endif /* USE_KVM_DEVICE_ASSIGNMENT */
}
--
1.6.0
0001-kvm-userspace-Load-PCI-option-ROMs.patch
Description: 0001-kvm-userspace-Load-PCI-option-ROMs.patch
