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

Reply via email to