Load assigned devices' PCI option ROMs to the RAM of guest OS. And pass the corresponding devfns to BIOS. Signed-off-by: Kechao Liu --- 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