Ah, your approach is much better than mine. Please ignore ours. Best Regards Haitao Shan
Leendert van Doorn wrote: > I've been experimenting with passthru graphics for KVM and for that I > needed to add PCI ROM support to QEMU. I went a slightly different > route than Haitoa, I enabled the BIOS to do the PCI ROM mapping and > initialization and added support for QEMU to fully implement the ROM > BAR. This patch also uses the host BAR addresses in the guest for > direct assigned devices, because (as Avi already pointed out) this is > needed for more complex PCI cards such as graphics adapters. My patch > also allows you to optionally specify the ROM image, which I found > very useful for debugging. > > My ROMBIOS patches are probably more invasive than necessary. I found > it cleaner (==easier to understand) to call the rombios from 32-bit > mode than leave some droppings (option rom address, bdf) in the BIOS > extended data segment. The other thing you notice is that I > initialize the primary VGA ROM last. I found that this was necessary > when emulating dual headed displays (one display on qemu's emulation > window, the other the actual device). > > I've attached my patches. > > Note that I have not included the full graphics passthru patches yet. > I have that working for the xorg ati drivers (and ATI's ATOMBIOS) but > not for the closed sourced or Windows drivers yet. Let me know if you > are interested in those patches. > > Leendert > > > diff --git a/bios/rombios.c b/bios/rombios.c > index b7a240f..c8ef8ac 100644 > --- a/bios/rombios.c > +++ b/bios/rombios.c > @@ -10130,7 +10130,7 @@ rombios32_real_mode: > ret > > rombios32_gdt_48: > - dw 0x30 > + dw 0x38 > dw rombios32_gdt > dw 0x000f > > @@ -10141,6 +10141,7 @@ rombios32_gdt: > dw 0xffff, 0, 0x9300, 0x00cf ; 32 bit flat data segment (0x18) > dw 0xffff, 0, 0x9b0f, 0x0000 ; 16 bit code segment base=0xf0000 > limit=0xffff > dw 0xffff, 0, 0x9300, 0x0000 ; 16 bit data segment base=0x0 > limit=0xffff + dw 0xffff, 0, 0x9b0e, 0x0000 ; 16 bit code segment > base=0xE0000 limit=0xffff > #endif // BX_ROMBIOS32 > > > @@ -10194,6 +10195,7 @@ no_serial: > pop dx > ret > > +#if !BX_ROMBIOS32 > rom_checksum: > push ax > push bx > @@ -10266,7 +10268,6 @@ block_count_rounded: > mov ax, #0xf000 > mov es, ax > lea di, pnp_string > - > mov bp, sp ;; Call ROM init routine using seg:off on stack > db 0xff ;; call_far ss:[bp+0] > db 0x5e > @@ -10348,6 +10349,7 @@ rom_scan_increment: > xor ax, ax ;; Restore DS back to 0000: > mov ds, ax > ret > +#endif /* !BX_ROMBIOS32 */ > > post_enable_cache: > ;; enable cache > @@ -10652,12 +10654,6 @@ post_default_ints: > ;; PIC > call post_init_pic > > - mov cx, #0xc000 ;; init vga bios > - mov ax, #0xc780 > - call rom_scan > - > - call _print_bios_banner > - > #if BX_ROMBIOS32 > call rombios32_init > #else > @@ -10665,7 +10661,12 @@ post_default_ints: > call pcibios_init_iomem_bases > call pcibios_init_irqs > #endif //BX_PCIBIOS > -#endif > + mov cx, #0xc000 ;; init vga bios > + mov ax, #0xc780 > + call rom_scan > +#endif //BX_ROMBIOS32 > + > + call _print_bios_banner > > ;; > ;; Floppy setup > @@ -10698,9 +10699,11 @@ post_default_ints: > > call _init_boot_vectors > > +#if !BX_ROMBIOS32 > mov cx, #0xc800 ;; init option roms > mov ax, #0xe000 > call rom_scan > +#endif > > #if BX_ELTORITO_BOOT > call _interactive_bootkey > diff --git a/bios/rombios32.c b/bios/rombios32.c > index 321563d..580aa17 100755 > --- a/bios/rombios32.c > +++ b/bios/rombios32.c > @@ -1,4 +1,4 @@ > -///////////////////////////////////////////////////////////////////////// > +//////////////////////////////////////////////////////////////////////// > // $Id: rombios32.c,v 1.11 2007/08/03 13:56:13 vruppert Exp $ > ///////////////////////////////////////////////////////////////////////// > // > @@ -679,6 +679,7 @@ typedef struct PCIDevice { > int devfn; > } PCIDevice; > > +static int pci_bios_nvga = 0; > static uint32_t pci_bios_io_addr; > static uint32_t pci_bios_mem_addr; > static uint32_t pci_bios_bigmem_addr; > @@ -736,7 +737,7 @@ static void pci_set_io_region_addr(PCIDevice *d, > int region_num, uint32_t addr) > old_addr = pci_config_readl(d, ofs); > > pci_config_writel(d, ofs, addr); > - BX_INFO("region %d: 0x%08x\n", region_num, addr); > +// BX_INFO("region %d: 0x%08x\n", region_num, addr); > > /* enable memory mappings */ > cmd = pci_config_readw(d, PCI_COMMAND); > @@ -928,6 +929,7 @@ static void pci_bios_init_device(PCIDevice *d) > } > break; > case 0x0300: /* Display controller - VGA compatible controller */ > + pci_bios_nvga++; > if (vendor_id != 0x1234) > goto default_map; > /* VGA: map frame buffer to default Bochs VBE address */ > @@ -953,18 +955,21 @@ static void pci_bios_init_device(PCIDevice *d) > default_map: > /* default memory mappings */ > for(i = 0; i < PCI_NUM_REGIONS; i++) { > + uint32_t orig, addr, val, size; > int ofs; > - uint32_t val, size ; > > if (i == PCI_ROM_SLOT) > ofs = 0x30; > else > ofs = 0x10 + i * 4; > + orig = pci_config_readl(d, ofs); > pci_config_writel(d, ofs, 0xffffffff); > val = pci_config_readl(d, ofs); > if (val != 0) { > size = (~(val & ~0xf)) + 1; > - if (val & PCI_ADDRESS_SPACE_IO) > + if (i == PCI_ROM_SLOT) > + paddr = &pci_bios_mem_addr; > + else if (val & PCI_ADDRESS_SPACE_IO) > paddr = &pci_bios_io_addr; > else if (size >= 0x04000000) > paddr = &pci_bios_bigmem_addr; > @@ -972,7 +977,24 @@ static void pci_bios_init_device(PCIDevice *d) > paddr = &pci_bios_mem_addr; > *paddr = (*paddr + size - 1) & ~(size - 1); > pci_set_io_region_addr(d, i, *paddr); > + BX_INFO("PCI: region %d: paddr 0x%x, size 0x%x\n", > + i, *paddr, size); > *paddr += size; > + > + /* > + * Some complex PCI devices (such as graphic controllers) > + * keep lots of internal state, including their PCI mappings > + * that were determined at initial PCI configuration. > + * To make them work under QEMU/KVM, we preserve the host's > + * PCI mappings in the guest. > + * (we have to do this twice to ensure we update the > mappings) > + */ > + addr = orig & ~(size - 1); > + if (addr) { > + pci_set_io_region_addr(d, i, addr); > + BX_INFO("PCI: region %d: addr 0x%x, size 0x%x (host)\n", > + i, addr, size); > + } > } > } > break; > @@ -1033,6 +1055,310 @@ void pci_bios_init(void) > } > > /****************************************************/ > +/* Option ROM init */ > + > +#define ROM_SIGNATURE 0xAA55 > +#define PCI_SIGNATURE 0x52494350 /* "RICP" */ > + > +#define VGA_ROM 0xC0000 > +#define EXT_ROM_START 0xD0000 > +#define EXT_ROM_END 0xE0000 > + > +#define ROUND(v, n) (((v) + (n) - 1) & ~((n) - 1)) > + > +struct rom_header { > + uint16_t signature; > + uint8_t size; > + uint8_t init[3]; > + uint8_t reserved[0x12]; > + uint16_t data; > +}; > + > +struct pci_header { > + uint32_t signature; > + uint16_t vendor; > + uint16_t device; > + uint16_t reserved_1; > + uint16_t dlen; > + uint8_t drevision; > + uint8_t class_lo; > + uint16_t class_hi; > + uint16_t ilen; > + uint16_t irevision; > + uint8_t type; > + uint8_t indicator; > + uint16_t reserved_2; > +}; > + > +struct dtr { > + unsigned short size; > + unsigned long base __attribute__ ((packed)); > +}; > + > +struct dtr oldidtr; > +unsigned long stack; > + > +void rom_far_call(uint32_t seg, uint32_t eax) > +{ > + BX_INFO("ROM far call 0x%x:3; eax=0x%lx\n", seg, eax); > + > + __asm__ __volatile__ ( > + " .set RELOCBASE, 0xE0000 \n" /* we are executing @0xE0000 > */ > + " .set RELOCSEG, [RELOCBASE>>4]\n" > + " .set IPL_SEG, 0x9ff0 \n" > + " .set IPL_TABLE_ENTRIES, 8\n" > + " .set IPL_COUNT_OFFSET, 0x0080\n" > + " .set IPL_TYPE_BEV, 0x80\n" > + > + > + /* save the world */ > + " pushal \n" > + " mov %esp, stack \n" > + > + /* get arguments */ > + " movl 8(%ebp), %ecx \n" /* segment */ > + " movl 12(%ebp), %eax \n" /* eax */ > + > + /* save idt */ > + " sidt oldidtr \n" > + > + /* get into real mode */ > + " ljmp $0x30, $1f-RELOCBASE\n" > + > + /* initialized segment registers */ > + " .code16 \n" > + "1: mov $0x28, %bx \n" > + " mov %bx, %ds \n" > + " mov %bx, %es \n" > + " mov %bx, %ss \n" > + " mov %bx, %fs \n" > + " mov %bx, %gs \n" > + > + /* turn off protected mode */ > + " movl %cr0, %ebx \n" > + " andl $~1, %ebx \n" > + " movl %ebx, %cr0 \n" > + > + /* flush prefetch queue */ > + " data32 ljmp $RELOCSEG, $(1f-RELOCBASE)\n" > + > + /* setup 16-bit IDT */ > + " .align 4 \n" > + "idtr: .word 1024-1 \n" > + " .long 0 \n" > + "1: addr32 lidt %cs:(idtr-RELOCBASE)\n" > + > + /* zero the segment registers */ > + " xor %bx, %bx \n" > + " mov %bx, %ds \n" > + " mov %bx, %es \n" > + " mov %bx, %ss \n" > + " mov %bx, %fs \n" > + " mov %bx, %gs \n" > + > + /* setup a new stack */ > + " movl $0x1000, %ebx \n" > + " movl %ebx, %esp \n" > + > + /* > + * 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. > + */ > + " movw $RELOCBASE>>4, %bx\n" > + " mov %bx, %es \n" > + " movl $pnp-RELOCBASE, %edi\n" > + > + /* far call to extension ROM at %cx:3 */ > + " push %cx \n" > + " push $3 \n" > + " mov %sp, %bp \n" > + " .byte 0xFF, 0x5E, 0x00\n" /* call far %ss:(%bp) */ > + " cli \n" /* just in case */ > + > + /* > + * Look at the ROM's PnP Expansion header. Properly, we're supposed > + * to init all the ROMs and then go back and build an IPL table of > + * all the bootable devices, but we can get away with one pass. > + */ > + " pop %cx \n" > + " pop %cx \n" /* ROM segment */ > + " mov %cx, %ds \n" > + > + " mov $0x1A, %bx \n" > + " movl (%bx), %eax \n" > + " movl $pnp-RELOCBASE, %edi\n" > + " cmpl %eax, (%edi) \n" > + " je 1f \n" > + " mov 0x1A(%bx), %ax \n" > + " cmp $0, %ax \n" > + " je 1f \n" > + > + /* > + * Found a device that thinks it can boot the system. > + * Record its BEV and product name string. > + */ > + " mov 0x10(%bx), %di \n" > + " mov $IPL_SEG, %bx \n" > + " mov %bx, %ds \n" > + " mov IPL_COUNT_OFFSET, %bx\n" > + " cmpw $IPL_TABLE_ENTRIES, %bx\n" > + " je 1f \n" > + > + " shl $4, %bx \n" > + " movw $IPL_TYPE_BEV, (%bx)\n" > + " mov %ax, 4(%bx) \n" > + " mov %cx, 6(%bx) \n" > + " cmp $0, %di \n" > + " je 2f \n" > + > + " mov %di, 8(%bx) \n" > + " mov %cx, 10(%bx) \n" > + > + "2: shr $4, %bx \n" > + " inc %bx \n" > + " mov %bx, IPL_COUNT_OFFSET\n" > + > + " jmp 1f \n" > + "pnp: .asciz \"$PnP\" \n" > + > + /* turn on protected mode */ > + "1: movl %cr0, %ebx \n" > + " orl $0x1, %ebx \n" > + " movl %ebx, %cr0 \n" > + > + /* jump to 32-bit */ > + " data32 ljmp $0x10, $1f \n" > + > + " .code32 \n" > + "1: movw $0x18, %bx \n" > + " mov %bx, %ds \n" > + " mov %bx, %es \n" > + " mov %bx, %ss \n" > + " mov %bx, %fs \n" > + " mov %bx, %gs \n" > + > + " lidt oldidtr \n" > + " mov stack, %esp \n" > + " popal \n" > + ); > +} > + > +static int is_valid_rom(uint32_t rom_address) > +{ > + struct rom_header *rom_header; > + unsigned char sum = 0, *rom_bytes; > + unsigned int i; > + > + rom_header = (struct rom_header *)rom_address; > + if (rom_header->signature != ROM_SIGNATURE) > + return 0; > + > + rom_bytes = (unsigned char *)rom_address; > + for (i = 0; i < rom_header->size * 512; i++) > + sum += *(rom_bytes + i); > + if (sum != 0) > + return 0; > + > + return 1; > +} > + > +static int is_valid_pci_rom(PCIDevice *d, uint32_t rom_address) > +{ > + int vendor_id, device_id; > + struct pci_header *pci_header; > + struct rom_header *rom_header; > + > + if (!is_valid_rom(rom_address)) > + return 0; > + > + rom_header = (struct rom_header *)rom_address; > + pci_header = (struct pci_header *)((unsigned char *)rom_header > + + rom_header->data); > + if ( pci_header->signature != PCI_SIGNATURE) > + return 0; > + > + vendor_id = pci_config_readw(d, PCI_VENDOR_ID); > + device_id = pci_config_readw(d, PCI_DEVICE_ID); > + if (pci_header->vendor != vendor_id || pci_header->device != > device_id) + return 0; > + > + return 1; > +} > + > +static int pci_nvga = 0; > +static uint32_t pci_vga_devfn = 0; > +uint32_t ext_rom = EXT_ROM_START; > +uint32_t ext_rom_end = EXT_ROM_END; > + > +static void init_rom(PCIDevice *d) > +{ > + struct rom_header *rom_header; > + struct pci_header *pci_header; > + uint32_t rom_address; > + int is_primary_vga = 0; > + uint8_t *dest; > + int class; > + > + class = pci_config_readw(d, PCI_CLASS_DEVICE); > + > + /* first VGA card found it the primary */ > + if (class == 0x300 && pci_nvga++ == 0) > + is_primary_vga = 1; > + > + rom_address = pci_config_readl(d, 0x30) & ~1; > + if (rom_address == 0 || rom_address == ~0) > + return; > + > + BX_INFO("%x:%x: PCI option ROM address 0x%x\n", > + d->bus, d->devfn, rom_address); > + pci_config_writel(d, 0x30, rom_address | 1); /* enable rom */ > + > + if (!is_valid_pci_rom(d, rom_address)) > + goto bailout; > + > + rom_header = (struct rom_header *)rom_address; > + pci_header = (struct pci_header *)((unsigned char *)rom_header > + + rom_header->data); > + > + dest = is_primary_vga ? (uint8_t *)VGA_ROM : (uint8_t *)ext_rom; > + BX_INFO("Copying PCI %04x:%04x ROM to 0x%x ... ", > + pci_header->vendor, pci_header->device, dest); > + memcpy(dest, (char *)rom_address, rom_header->size * 512); > + BX_INFO("DONE\n"); > + > + if (!is_primary_vga) { > + rom_far_call(ext_rom >> 4, (d->bus << 8) | d->devfn); > + ext_rom += ROUND(rom_header->size * 512, 2048); > + } else > + pci_vga_devfn = (d->bus << 8) | d->devfn; > + > +bailout: > + pci_config_writel(d, 0x30, rom_address); /* disable rom */ > +} > + > +void option_rom_init(void) > +{ > + /* Execute any extension roms that were pre-loaded */ > + while (ext_rom < ext_rom_end) { > + struct rom_header *rom_header = (struct rom_header *)ext_rom; > + > + if (!is_valid_rom(ext_rom)) > + break; > + rom_far_call(ext_rom >> 4, 0); > + > + ext_rom += ROUND(rom_header->size * 512, 2048); > + } > + > + /* Execute any extension roms that came from PCI devices */ > + pci_nvga = 0; > + pci_for_each_device(init_rom); > + > + /* Initialize the primary VGA subsystem (last) */ > + if (is_valid_rom(VGA_ROM)) > + rom_far_call(VGA_ROM >> 4, pci_vga_devfn); > +} > + > +/****************************************************/ > /* Multi Processor table init */ > > static void putb(uint8_t **pp, int val) > @@ -2257,4 +2583,6 @@ void rombios32_init(uint32_t *s3_resume_vector, > uint8_t *shutdown_flag) > BX_PANIC("ebda_cur_addr overflow!\n"); > #endif > } > + > + option_rom_init(); > } > diff --git a/qemu/hw/device-assignment.c b/qemu/hw/device-assignment.c > index 7a66665..b6ba676 100644 > --- a/qemu/hw/device-assignment.c > +++ b/qemu/hw/device-assignment.c > @@ -175,13 +175,33 @@ static void assigned_dev_iomem_map(PCIDevice > *pci_dev, int region_num, > } > } > > +static void assigned_dev_ioperm(int start_port, int num, int turn_on) > +{ > + struct ioperm_data *data; > + CPUState *env; > + > + data = qemu_mallocz(sizeof(struct ioperm_data)); > + if (data == NULL) { > + fprintf(stderr, "%s: Out of memory\n", __func__); > + exit(1); > + } > + > + data->start_port = start_port; > + data->num = num; > + data->turn_on = 1; > + > + kvm_add_ioperm_data(data); > + > + for (env = first_cpu; env; env = env->next_cpu) > + kvm_ioperm(env, data); > +} > + > static void assigned_dev_ioport_map(PCIDevice *pci_dev, int > region_num, uint32_t addr, > uint32_t size, int type) { > AssignedDevice *r_dev = (AssignedDevice *) pci_dev; > AssignedDevRegion *region = &r_dev->v_addrs[region_num]; > int first_map = (region->e_size == 0); > - CPUState *env; > > region->e_physbase = addr; > region->e_size = size; > @@ -189,24 +209,8 @@ static void assigned_dev_ioport_map(PCIDevice > *pci_dev, int region_num, > DEBUG("e_phys=0x%x r_baseport=%x type=0x%x len=%d region_num=%d > \n", addr, region->u.r_baseport, type, size, region_num); > > - if (first_map) { > - struct ioperm_data *data; > - > - data = qemu_mallocz(sizeof(struct ioperm_data)); > - if (data == NULL) { > - fprintf(stderr, "%s: Out of memory\n", __func__); > - exit(1); > - } > - > - data->start_port = region->u.r_baseport; > - data->num = region->r_size; > - data->turn_on = 1; > - > - kvm_add_ioperm_data(data); > - > - for (env = first_cpu; env; env = env->next_cpu) > - kvm_ioperm(env, data); > - } > + if (first_map) > + assigned_dev_ioperm(region->u.r_baseport, region->r_size, 1); > > register_ioport_read(addr, size, 1, assigned_dev_ioport_readb, > (r_dev->v_addrs + region_num)); > @@ -237,8 +241,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) { > /* used for update-mappings (BAR emulation) */ > pci_default_write_config(d, address, val, len); > return; > @@ -270,8 +274,8 @@ static uint32_t > assigned_dev_pci_read_config(PCIDevice *d, uint32_t address, > int fd; > ssize_t ret; > > - if ((address >= 0x10 && address <= 0x24) || address == 0x34 || > - address == 0x3c || address == 0x3d) { > + if ((address >= 0x10 && address <= 0x24) || address == 0x30 || > + address == 0x34 || address == 0x3c || address == 0x3d) { > val = pci_default_read_config(d, address, len); > DEBUG("(%x.%x): address=%04x val=0x%08x len=%d\n", > (d->devfn >> 3) & 0x1F, (d->devfn & 0x7), address, > val, len); @@ -309,6 +313,29 @@ do_log: > return val; > } > > +#ifdef DEVICE_ASSIGNMENT_DEBUG > +void > +hexdump(uint8_t *data, int sz) > +{ > + uint8_t *d; > + int i; > + > + for (d = data; sz > 0; d += 16, sz -= 16) { > + int n = sz > 16 ? 16 : sz; > + > + printf("%08x: ", (unsigned) d); > + for (i = 0; i < n; i++) > + printf("%02x%c", d[i], i == 7 ? '-' : ' '); > + for (; i < 16; i++) > + printf(" %c", i == 7 ? '-' : ' '); > + printf(" "); > + for (i = 0; i < n; i++) > + printf("%c", d[i] >= ' ' && d[i] <= '~' ? d[i] : '.'); > + printf("\n"); > + } > +} > +#endif /* DEVICE_ASSIGNMENT_DEBUG */ > + > static int assigned_dev_register_regions(PCIRegion *io_regions, > unsigned long regions_num, > AssignedDevice *pci_dev) > @@ -321,6 +348,78 @@ static int > assigned_dev_register_regions(PCIRegion *io_regions, > continue; > pci_dev->v_addrs[i].num = i; > > + /* handle ROM region */ > + if (i == PCI_ROM_SLOT) { > + int t = cur_region->type & IORESOURCE_PREFETCH > + ? PCI_ADDRESS_SPACE_MEM_PREFETCH > + : PCI_ADDRESS_SPACE_MEM; > + int nread, tread = 0; > + > + /* > + * Map physical memory. Ideally we mmap the rom image but > + * since it may interfere with the device's operation (in > + * case we are reading from a real device) we need to make > + * a copy. > + */ > + pci_dev->v_addrs[i].e_physbase = cur_region->base_addr; > + pci_dev->v_addrs[i].u.r_virtbase = > + qemu_vmalloc((cur_region->size + 0xFFF) & 0xFFFFF000); > + > + if (pci_dev->v_addrs[i].u.r_virtbase == NULL) { > + fprintf(stderr, "%s: Error: Couldn't malloc %d" > + " bytes!\n", __func__, > + (cur_region->size + 0xFFF) & > + 0xFFFFF000); > + return -1; > + } > + > + /* Linux quirk: Enable PCI ROM access */ > + if (!pci_dev->romfile) { > + write(cur_region->resource_fd, "1\n", 2); > + lseek(cur_region->resource_fd, 0L, SEEK_SET); > + } > + > + /* in option ROM */ > + do { > + nread = read(cur_region->resource_fd, > + pci_dev->v_addrs[i].u.r_virtbase + tread, > + TARGET_PAGE_SIZE); > + if (nread > 0) > + tread += nread; > + } while (nread > 0 && tread < cur_region->size); > + > + if (nread < 0) { > + fprintf(stderr, "Error: Couldn't read ROM image\n"); > + return -1; > + } > + > + /* Linux quirk: Disable PCI ROM access */ > + if (!pci_dev->romfile) { > + lseek(cur_region->resource_fd, 0L, SEEK_SET); > + write(cur_region->resource_fd, "0\n", 2); > + } > + > +#ifdef DEVICE_ASSIGNMENT_DEBUG > + printf("PCI ROM:\n"); > + hexdump(pci_dev->v_addrs[i].u.r_virtbase, 64); > +#endif > + > + pci_dev->v_addrs[i].r_size = cur_region->size; > + pci_dev->v_addrs[i].e_size = 0; > + > + /* add offset */ > + pci_dev->v_addrs[i].u.r_virtbase += > + (cur_region->base_addr & 0xFFF); > + > + pci_register_io_region((PCIDevice *) pci_dev, i, > + cur_region->size, t, > + assigned_dev_iomem_map); > + > + /* no need to tell the BIOS about the original ROM location */ > + > + continue; > + } > + > /* handle memory io regions */ > if (cur_region->type & IORESOURCE_MEM) { > int t = cur_region->type & IORESOURCE_PREFETCH > @@ -352,8 +451,14 @@ static int > assigned_dev_register_regions(PCIRegion *io_regions, > pci_register_io_region((PCIDevice *) pci_dev, i, > cur_region->size, t, > assigned_dev_iomem_map); > + > + /* save original host mapping for BIOS (don't update > mappings!) */ > + *(uint32_t *)(pci_dev->dev.config + 0x10 + i * 4) = > + > cpu_to_le32(cur_region->base_addr); + > continue; > } > + > /* handle port io regions */ > pci_dev->v_addrs[i].e_physbase = cur_region->base_addr; > pci_dev->v_addrs[i].u.r_baseport = cur_region->base_addr; > @@ -364,6 +469,10 @@ static int > assigned_dev_register_regions(PCIRegion *io_regions, > cur_region->size, > PCI_ADDRESS_SPACE_IO, > assigned_dev_ioport_map); > > + /* save original host mapping for BIOS (don't update > mappings!) */ + *(uint32_t *)(pci_dev->dev.config + 0x10 + i > * 4) = + > cpu_to_le32(cur_region->base_addr); + > /* not relevant for port io */ > pci_dev->v_addrs[i].memory_index = 0; > } > @@ -423,10 +532,20 @@ 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 */ > + if (r == PCI_ROM_SLOT && pci_dev->romfile) { > + /* Use specified ROM instead of the card's */ > + strncpy(name, pci_dev->romfile, sizeof(name)); > + } else { > + /* Use the card's resources */ > + if (r == PCI_ROM_SLOT) > + snprintf(name, sizeof(name), "%srom", dir); > + else > + snprintf(name, sizeof(name), "%sresource%d", dir, r); > + } > + if ((fd = open(name, O_RDWR)) < 0) { > + fprintf(stderr, "%s: %s: %m\n", __func__, name); > + return 1; > + } > rp->resource_fd = fd; > } else > flags &= ~IORESOURCE_PREFETCH; > @@ -557,6 +676,7 @@ struct PCIDevice > *init_assigned_device(AssignedDevInfo *adev, PCIBus *bus) > > adev->assigned_dev = dev; > > + dev->romfile = adev->rom; > if (get_real_device(dev, adev->bus, adev->dev, adev->func)) { > fprintf(stderr, "%s: Error: Couldn't get real device > (%s)!\n", __func__, adev->name); > @@ -606,10 +726,11 @@ struct PCIDevice > *init_assigned_device(AssignedDevInfo *adev, PCIBus *bus) > /* > * Syntax to assign device: > * > - * -pcidevice host=bus:dev.func[,dma=none][,name=Foo] > + * -pcidevice host=bus:dev.func[,dma=none][,name=Foo][,rom=File] > * > * Example: > * -pcidevice host=00:13.0,dma=pvdma > + * -pcidevice host=01:00.0,rom=bios/RV610_B16905.bin > * > * dma can currently only be 'none' to disable iommu support. > */ > @@ -618,6 +739,7 @@ AssignedDevInfo *add_assigned_device(const char > *arg) char *cp, *cp1; > char device[8]; > char dma[6]; > + char rom[PATH_MAX]; > int r; > AssignedDevInfo *adev; > > @@ -636,6 +758,10 @@ AssignedDevInfo *add_assigned_device(const char > *arg) if (r && !strncmp(dma, "none", 4)) > adev->disable_iommu = 1; > #endif > + r = get_param_value(rom, sizeof rom, "rom", arg); > + if (r) > + adev->rom = strdup(rom); > + > cp = device; > adev->bus = strtoul(cp, &cp1, 16); > if (*cp1 != ':') > diff --git a/qemu/hw/device-assignment.h b/qemu/hw/device-assignment.h > index c8c47d3..3507be9 100644 > --- a/qemu/hw/device-assignment.h > +++ b/qemu/hw/device-assignment.h > @@ -37,7 +37,7 @@ > #define PCI_DEVFN(slot, func) ((((slot) & 0x1f) << 3) | ((func) & > 0x07)) > > /* The number of BARs in the config space header */ > -#define MAX_IO_REGIONS (6) > +#define MAX_IO_REGIONS (7) > > typedef struct { > int type; /* Memory or port I/O */ > @@ -80,6 +80,7 @@ typedef struct { > unsigned char h_busnr; > unsigned int h_devfn; > int bound; > + char *romfile; > } AssignedDevice; > > typedef struct AssignedDevInfo AssignedDevInfo; > @@ -89,6 +90,7 @@ struct AssignedDevInfo { > int bus; > int dev; > int func; > + char *rom; > AssignedDevice *assigned_dev; > LIST_ENTRY(AssignedDevInfo) next; > int disable_iommu; > diff --git a/qemu/hw/pc.c b/qemu/hw/pc.c > index 2d7e7f0..8451534 100644 > --- a/qemu/hw/pc.c > +++ b/qemu/hw/pc.c > @@ -972,6 +972,18 @@ static void pc_init1(ram_addr_t ram_size, int > vga_ram_size, > option_rom_setup_reset(0xd0000 + offset, size); > offset += size; > } > + > + /* If we are using device assignment then map in the rest of > the + * extension ROM space so PCI BIOS can use it to load > ROMs into. + */ > + if (assigned_devices_index && offset < 0x10000) { > + /* only 0xD0000..0xE0000 is available */ > + int size = 0x10000 - offset; > + ram_addr_t option_rom_offset = qemu_ram_alloc(size); > + cpu_register_physical_memory(0xd0000 + offset, size, > + option_rom_offset /*| IO_MEM_ROM > */); > + option_rom_setup_reset(0xd0000 + offset, size); > + } > } > > /* map all the bios at the top of memory */ -- To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html