Patchwork [-,V3] Port codes from qemu-kvm to support booting from SCSI disk image

login
register
mail settings
Submitter Haishan Bai
Date Aug. 18, 2010, 2:06 a.m.
Message ID <AANLkTimdV8kcDPPxFmYBAryVVK=Hyzo33SLE_+UEge8U@mail.gmail.com>
Download mbox | patch
Permalink /patch/61984/
State New
Headers show

Comments

Haishan Bai - Aug. 18, 2010, 2:06 a.m.
The qemu-kvm could boot from SCSI disk image by utilizing seabios, this
patch ported codes
from qemu-kvm to let upstream qemu to support booting from SCSI disk image.

Add 'boot=on' option to qemu command line like following,
qemu-system-x86_64 -drive file=scsi-disk.img,if=scsi,boot=on


Signed-off-by: Shan Hai <haishan.bai@gmail.com>
---
 Makefile.target |    1 +
 blockdev.c      |   13 ++++++
 blockdev.h      |    2 +
 hw/extboot.c    |  123
+++++++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/pc.c         |   17 ++++++++
 hw/pc.h         |    4 ++
 qemu-config.c   |    4 ++
 7 files changed, 164 insertions(+), 0 deletions(-)
 create mode 100644 hw/extboot.c
Haishan Bai - Aug. 18, 2010, 2:20 a.m.
Hai Shan Bai wrote:
> The qemu-kvm could boot from SCSI disk image by utilizing seabios, 
> this patch ported codes
> from qemu-kvm to let upstream qemu to support booting from SCSI disk 
> image.
>
> Add 'boot=on' option to qemu command line like following,
> qemu-system-x86_64 -drive file=scsi-disk.img,if=scsi,boot=on
>
>
> Signed-off-by: Shan Hai <haishan.bai@gmail.com 
> <mailto:haishan.bai@gmail.com>>
> ---
>  Makefile.target |    1 +
>  blockdev.c      |   13 ++++++
>  blockdev.h      |    2 +
>  hw/extboot.c    |  123 
> +++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  hw/pc.c         |   17 ++++++++
>  hw/pc.h         |    4 ++
>  qemu-config.c   |    4 ++
>  7 files changed, 164 insertions(+), 0 deletions(-)
>  create mode 100644 hw/extboot.c
>
> diff --git a/Makefile.target b/Makefile.target
> index c8281e9..abba79c 100644
> --- a/Makefile.target
> +++ b/Makefile.target
> @@ -199,6 +199,7 @@ obj-i386-y += mc146818rtc.o i8259.o pc.o
>  obj-i386-y += cirrus_vga.o apic.o ioapic.o piix_pci.o
>  obj-i386-y += vmmouse.o vmport.o hpet.o applesmc.o
>  obj-i386-y += device-hotplug.o pci-hotplug.o smbios.o wdt_ib700.o
> +obj-i386-y += extboot.o
>  obj-i386-y += debugcon.o multiboot.o
>  obj-i386-y += pc_piix.o
>  
> diff --git a/blockdev.c b/blockdev.c
> index 01e402b..78c286c 100644
> --- a/blockdev.c
> +++ b/blockdev.c
> @@ -15,6 +15,8 @@
>  #include "qemu-config.h"
>  #include "sysemu.h"
>  
> +DriveInfo *extboot_drive = NULL;
> +
>  static QTAILQ_HEAD(drivelist, DriveInfo) drives = 
> QTAILQ_HEAD_INITIALIZER(drives);
>  
>  /*
> @@ -150,6 +152,7 @@ DriveInfo *drive_init(QemuOpts *opts, int 
> default_to_scsi, int *fatal_error)
>      int on_read_error, on_write_error;
>      const char *devaddr;
>      DriveInfo *dinfo;
> +    int is_extboot = 0;
>      int snapshot = 0;
>      int ret;
>  
> @@ -311,6 +314,12 @@ DriveInfo *drive_init(QemuOpts *opts, int 
> default_to_scsi, int *fatal_error)
>          }
>      }
>  
> +    is_extboot = qemu_opt_get_bool(opts, "boot", 0);
> +    if (is_extboot && extboot_drive) {
> +        fprintf(stderr, "qemu: two bootable drives specified\n");
> +        return NULL;
> +    }
> +
>      on_write_error = BLOCK_ERR_STOP_ENOSPC;
>      if ((buf = qemu_opt_get(opts, "werror")) != NULL) {
>          if (type != IF_IDE && type != IF_SCSI && type != IF_VIRTIO && 
> type != IF_NONE) {
> @@ -421,6 +430,10 @@ DriveInfo *drive_init(QemuOpts *opts, int 
> default_to_scsi, int *fatal_error)
>          strncpy(dinfo->serial, serial, sizeof(dinfo->serial) - 1);
>      QTAILQ_INSERT_TAIL(&drives, dinfo, next);
>  
> +    if (is_extboot) {
> +        extboot_drive = dinfo;
> +    }
> +
>      bdrv_set_on_error(dinfo->bdrv, on_read_error, on_write_error);
>  
>      switch(type) {
> diff --git a/blockdev.h b/blockdev.h
> index 37f3a01..e707b87 100644
> --- a/blockdev.h
> +++ b/blockdev.h
> @@ -59,4 +59,6 @@ int do_block_set_passwd(Monitor *mon, const QDict 
> *qdict, QObject **ret_data);
>  int do_change_block(Monitor *mon, const char *device,
>                      const char *filename, const char *fmt);
>  
> +extern DriveInfo *extboot_drive;
> +
>  #endif
> diff --git a/hw/extboot.c b/hw/extboot.c
> new file mode 100644
> index 0000000..8ada21b
> --- /dev/null
> +++ b/hw/extboot.c
> @@ -0,0 +1,123 @@
> +/*
> + * Extended boot option ROM support.
> + *
> + * Copyright IBM, Corp. 2007
> + *
> + * Authors:
> + *  Anthony Liguori   <aliguori@us.ibm.com <mailto:aliguori@us.ibm.com>>
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2.  See
> + * the COPYING file in the top-level directory.
> + *
> + */
> +
> +#include "hw.h"
> +#include "pc.h"
> +#include "isa.h"
> +#include "block.h"
> +
> +/* Extended Boot ROM suport */
> +
> +union extboot_cmd
> +{
> +    uint16_t type;
> +    struct {
> +        uint16_t type;
> +        uint16_t cylinders;
> +        uint16_t heads;
> +        uint16_t sectors;
> +        uint64_t nb_sectors;
> +    } query_geometry;
> +    struct {
> +        uint16_t type;
> +        uint16_t nb_sectors;
> +        uint16_t segment;
> +        uint16_t offset;
> +        uint64_t sector;
> +    } xfer;
> +};
> +
> +static void get_translated_chs(BlockDriverState *bs, int *c, int *h, 
> int *s)
> +{
> +    bdrv_get_geometry_hint(bs, c, h, s);
> +
> +    if (*c <= 1024) {
> +        *c >>= 0;
> +        *h <<= 0;
> +    } else if (*c <= 2048) {
> +        *c >>= 1;
> +        *h <<= 1;
> +    } else if (*c <= 4096) {
> +        *c >>= 2;
> +        *h <<= 2;
> +    } else if (*c <= 8192) {
> +        *c >>= 3;
> +        *h <<= 3;
> +    } else {
> +        *c >>= 4;
> +        *h <<= 4;
> +    }
> +
> +    /* what is the correct algorithm for this?? */
> +    if (*h == 256) {
> +        *h = 255;
> +        *c = *c + 1;
> +    }
> +}
> +
> +static void extboot_write_cmd(void *opaque, uint32_t addr, uint32_t 
> value)
> +{
> +    union extboot_cmd cmd;
> +    BlockDriverState *bs = opaque;
> +    int cylinders, heads, sectors, err;
> +    uint64_t nb_sectors;
> +    target_phys_addr_t pa = 0;
> +    int blen = 0;
> +    void *buf = NULL;
> +
> +    cpu_physical_memory_read((value & 0xFFFF) << 4, (uint8_t *)&cmd,
> +                             sizeof(cmd));
> +
> +    if (cmd.type == 0x01 || cmd.type == 0x02) {
> +        pa = cmd.xfer.segment * 16 + cmd.xfer.offset;
> +        blen = cmd.xfer.nb_sectors * 512;
> +        buf = qemu_memalign(512, blen);
> +    }
> +
> +    switch (cmd.type) {
> +    case 0x00:
> +        get_translated_chs(bs, &cylinders, &heads, &sectors);
> +        bdrv_get_geometry(bs, &nb_sectors);
> +        cmd.query_geometry.cylinders = cylinders;
> +        cmd.query_geometry.heads = heads;
> +        cmd.query_geometry.sectors = sectors;
> +        cmd.query_geometry.nb_sectors = nb_sectors;
> +        break;
> +    case 0x01:
> +        err = bdrv_read(bs, cmd.xfer.sector, buf, cmd.xfer.nb_sectors);
> +        if (err)
> +            printf("Read failed\n");
> +
> +        cpu_physical_memory_write(pa, buf, blen);
> +
> +        break;
> +    case 0x02:
> +        cpu_physical_memory_read(pa, buf, blen);
> +
> +        err = bdrv_write(bs, cmd.xfer.sector, buf, cmd.xfer.nb_sectors);
> +        if (err)
> +            printf("Write failed\n");
> +
> +        break;
> +    }
> +
> +    cpu_physical_memory_write((value & 0xFFFF) << 4, (uint8_t *)&cmd,
> +                              sizeof(cmd));
> +    if (buf)
> +        qemu_free(buf);
> +}
> +
> +void extboot_init(BlockDriverState *bs)
> +{
> +    register_ioport_write(0x405, 1, 2, extboot_write_cmd, bs);
> +}
> diff --git a/hw/pc.c b/hw/pc.c
> index 58dea57..88f43aa 100644
> --- a/hw/pc.c
> +++ b/hw/pc.c
> @@ -54,6 +54,7 @@
>  #endif
>  
>  #define BIOS_FILENAME "bios.bin"
> +#define EXTBOOT_FILENAME "extboot.bin"
>  
>  #define PC_MAX_BIOS_SIZE (4 * 1024 * 1024)
>  
> @@ -953,6 +954,10 @@ void pc_memory_init(ram_addr_t ram_size,
>                                   isa_bios_size,
>                                   (bios_offset + bios_size - 
> isa_bios_size) | IO_MEM_ROM);
>  
> +    if (extboot_drive) {
> +        option_rom[nb_option_roms++] = qemu_strdup(EXTBOOT_FILENAME);
> +    }
> +   
>      option_rom_offset = qemu_ram_alloc(NULL, "pc.rom", PC_ROM_SIZE);
>      cpu_register_physical_memory(PC_ROM_MIN_VGA, PC_ROM_SIZE, 
> option_rom_offset);
>  
> @@ -1074,4 +1079,16 @@ void pc_pci_device_init(PCIBus *pci_bus)
>      for (bus = 0; bus <= max_bus; bus++) {
>          pci_create_simple(pci_bus, -1, "lsi53c895a");
>      }
> +
> +    if (extboot_drive) {
> +        DriveInfo *info = extboot_drive;
> +        int cyls, heads, secs;
> +
> +        if (info->type != IF_IDE && info->type != IF_VIRTIO) {
> +            bdrv_guess_geometry(info->bdrv, &cyls, &heads, &secs);
> +            bdrv_set_geometry_hint(info->bdrv, cyls, heads, secs);
> +        }
> +
> +        extboot_init(info->bdrv);
> +    }
>  }
> diff --git a/hw/pc.h b/hw/pc.h
> index 63b0249..61882db 100644
> --- a/hw/pc.h
> +++ b/hw/pc.h
> @@ -167,6 +167,10 @@ void isa_cirrus_vga_init(void);
>  
>  void isa_ne2000_init(int base, int irq, NICInfo *nd);
>  
> +/* extboot.c */
> +       
> +void extboot_init(BlockDriverState *bs);
> +
>  /* e820 types */
>  #define E820_RAM        1
>  #define E820_RESERVED   2
> diff --git a/qemu-config.c b/qemu-config.c
> index 95abe61..08ee553 100644
> --- a/qemu-config.c
> +++ b/qemu-config.c
> @@ -79,6 +79,10 @@ QemuOptsList qemu_drive_opts = {
>          },{
>              .name = "readonly",
>              .type = QEMU_OPT_BOOL,
> +        },{
> +            .name = "boot",
> +            .type = QEMU_OPT_BOOL,
> +            .help = "make this a boot drive",
>          },
>          { /* end if list */ }
>      },
> -- 
> 1.7.0.4
>

This version just corrected the code indentation errors in previous 
versions.

Thanks
Shan Hai
Paul Brook - Aug. 18, 2010, 3:08 p.m.
> The qemu-kvm could boot from SCSI disk image by utilizing seabios, this
> patch ported codes
> from qemu-kvm to let upstream qemu to support booting from SCSI disk image.

No. This has nothing to do with SCSI.

What it does is add a really cheap and nasty block storage device that aliases 
one of the other block devices in the system. This is bad. The OS has no way 
of knowing which devices are aliased, and we really don't want yet another 
braindead guest visible interface (c.f. recent performance issues where -
kernel/fw_cfg were being abused)

This has been discussed several times before. The proper solution is to teach 
the bios how to boot off SCSI devices. IIRC support for virtio devices already 
exists, implementing support for the SCSI controller shouldn't be that much 
harder.

Paul
Alexander Graf - Aug. 18, 2010, 3:16 p.m.
On 18.08.2010, at 17:08, Paul Brook wrote:

>> The qemu-kvm could boot from SCSI disk image by utilizing seabios, this
>> patch ported codes
>> from qemu-kvm to let upstream qemu to support booting from SCSI disk image.
> 
> No. This has nothing to do with SCSI.
> 
> What it does is add a really cheap and nasty block storage device that aliases 
> one of the other block devices in the system. This is bad. The OS has no way 
> of knowing which devices are aliased, and we really don't want yet another 
> braindead guest visible interface (c.f. recent performance issues where -
> kernel/fw_cfg were being abused)
> 
> This has been discussed several times before. The proper solution is to teach 
> the bios how to boot off SCSI devices. IIRC support for virtio devices already 
> exists, implementing support for the SCSI controller shouldn't be that much 
> harder.

Couldn't we just have an option rom for the SCSI controller? The same way the VGABIOS is a rewrite of a VGA BIOS for the Cirrus Logic...


Alex
Haishan Bai - Aug. 18, 2010, 3:31 p.m.
On 08/18/2010 11:16 PM, Alexander Graf wrote:
> On 18.08.2010, at 17:08, Paul Brook wrote:
>
>    
>>> The qemu-kvm could boot from SCSI disk image by utilizing seabios, this
>>> patch ported codes
>>> from qemu-kvm to let upstream qemu to support booting from SCSI disk image.
>>>        
>> No. This has nothing to do with SCSI.
>>
>> What it does is add a really cheap and nasty block storage device that aliases
>> one of the other block devices in the system. This is bad. The OS has no way
>> of knowing which devices are aliased, and we really don't want yet another
>> braindead guest visible interface (c.f. recent performance issues where -
>> kernel/fw_cfg were being abused)
>>
>> This has been discussed several times before. The proper solution is to teach
>> the bios how to boot off SCSI devices. IIRC support for virtio devices already
>> exists, implementing support for the SCSI controller shouldn't be that much
>> harder.
>>      
> Couldn't we just have an option rom for the SCSI controller? The same way the VGABIOS is a rewrite of a VGA BIOS for the Cirrus Logic...
>
>    

In my opinion add SCSI support to seabios would be a choice.


> Alex
>
>
Gleb Natapov - Aug. 18, 2010, 3:37 p.m.
On Wed, Aug 18, 2010 at 05:16:56PM +0200, Alexander Graf wrote:
> 
> On 18.08.2010, at 17:08, Paul Brook wrote:
> 
> >> The qemu-kvm could boot from SCSI disk image by utilizing seabios, this
> >> patch ported codes
> >> from qemu-kvm to let upstream qemu to support booting from SCSI disk image.
> > 
> > No. This has nothing to do with SCSI.
> > 
> > What it does is add a really cheap and nasty block storage device that aliases 
> > one of the other block devices in the system. This is bad. The OS has no way 
> > of knowing which devices are aliased, and we really don't want yet another 
> > braindead guest visible interface (c.f. recent performance issues where -
> > kernel/fw_cfg were being abused)
> > 
> > This has been discussed several times before. The proper solution is to teach 
> > the bios how to boot off SCSI devices. IIRC support for virtio devices already 
> > exists, implementing support for the SCSI controller shouldn't be that much 
> > harder.
> 
> Couldn't we just have an option rom for the SCSI controller? The same way the VGABIOS is a rewrite of a VGA BIOS for the Cirrus Logic...
> 
> 
Of course we could, but it should not be extboot, but proper scsi driver. gpxe has
src/drivers/block/scsi.c so may be it already supports qemu scsi device?

--
			Gleb.
Paul Brook - Aug. 18, 2010, 3:54 p.m.
> > This has been discussed several times before. The proper solution is to
> > teach the bios how to boot off SCSI devices. IIRC support for virtio
> > devices already exists, implementing support for the SCSI controller
> > shouldn't be that much harder.
> 
> Couldn't we just have an option rom for the SCSI controller? The same way
> the VGABIOS is a rewrite of a VGA BIOS for the Cirrus Logic...

Right, that may be the best way of implementing it. 
IIRC there are some complications because of the ways the different BIOS 
interfaces interact, but I expect these are fixable.

Paul
Paul Brook - Aug. 18, 2010, 4:04 p.m.
> > Couldn't we just have an option rom for the SCSI controller? The same way
> > the VGABIOS is a rewrite of a VGA BIOS for the Cirrus Logic...
> 
> Of course we could, but it should not be extboot, but proper scsi driver.
> gpxe has src/drivers/block/scsi.c so may be it already supports qemu scsi
> device?

IIRC it only supports iSCSI.  You'd need to add the LSI HBA driver and 
possibly parallel SCSI transport bits.

In practice I'd imagine the SCSI parts to be pretty straightforward. A bios 
driver can ignore most of the fancy bits (e.g. TCQ) that a full featured OS 
driver has to worry about.

Paul
Blue Swirl - Aug. 19, 2010, 6:14 p.m.
On Wed, Aug 18, 2010 at 4:04 PM, Paul Brook <paul@codesourcery.com> wrote:
>> > Couldn't we just have an option rom for the SCSI controller? The same way
>> > the VGABIOS is a rewrite of a VGA BIOS for the Cirrus Logic...
>>
>> Of course we could, but it should not be extboot, but proper scsi driver.
>> gpxe has src/drivers/block/scsi.c so may be it already supports qemu scsi
>> device?
>
> IIRC it only supports iSCSI.  You'd need to add the LSI HBA driver and
> possibly parallel SCSI transport bits.
>
> In practice I'd imagine the SCSI parts to be pretty straightforward. A bios
> driver can ignore most of the fancy bits (e.g. TCQ) that a full featured OS
> driver has to worry about.

ESP driver in OpenBIOS may be helpful:
http://tracker.coreboot.org/trac/openbios/browser/trunk/openbios-devel/drivers/esp.c#L227
Gerd Hoffmann - Aug. 20, 2010, 7:44 a.m.
On 08/18/10 17:16, Alexander Graf wrote:
>
>> This has been discussed several times before. The proper solution
>> is to teach the bios how to boot off SCSI devices. IIRC support for
>> virtio devices already exists, implementing support for the SCSI
>> controller shouldn't be that much harder.
>
> Couldn't we just have an option rom for the SCSI controller? The same
> way the VGABIOS is a rewrite of a VGA BIOS for the Cirrus Logic...

There already is one.  Needs qemu 0.12.4 or newer.  Unfortunaly it isn't 
redistributable, so we can't easily integrate it.  Thus seabios support 
or a free option rom would still be nice to have.

Fetch 
http://www.lsi.com/DistributionSystem/AssetDocument/files/support/ssp/sdms/Bios/lsi_bios.zip 
-> unzip -> boot with "-option-rom 8xx_64.rom"

cheers,
   Gerd
Anthony Liguori - Aug. 20, 2010, 1:42 p.m.
On 08/20/2010 02:44 AM, Gerd Hoffmann wrote:
> On 08/18/10 17:16, Alexander Graf wrote:
>>
>>> This has been discussed several times before. The proper solution
>>> is to teach the bios how to boot off SCSI devices. IIRC support for
>>> virtio devices already exists, implementing support for the SCSI
>>> controller shouldn't be that much harder.
>>
>> Couldn't we just have an option rom for the SCSI controller? The same
>> way the VGABIOS is a rewrite of a VGA BIOS for the Cirrus Logic...
>
> There already is one.  Needs qemu 0.12.4 or newer.  Unfortunaly it 
> isn't redistributable, so we can't easily integrate it.  Thus seabios 
> support or a free option rom would still be nice to have.
>
> Fetch 
> http://www.lsi.com/DistributionSystem/AssetDocument/files/support/ssp/sdms/Bios/lsi_bios.zip 
> -> unzip -> boot with "-option-rom 8xx_64.rom"

Maybe we could switch PCIDevice to default romfile to try to 
<devicename>.rom if none is specified.  Since qdev names are unique, 
this should work well.

That way, users could "install" the lsi rom if they really wanted to 
SCSI boot until someone writes proper SCSI boot support.

Regards,

Anthony Liguori

> cheers,
>   Gerd
>
>
Gerd Hoffmann - Aug. 20, 2010, 2:09 p.m.
Hi,

>> Fetch
>> http://www.lsi.com/DistributionSystem/AssetDocument/files/support/ssp/sdms/Bios/lsi_bios.zip
>> -> unzip -> boot with "-option-rom 8xx_64.rom"
>
> Maybe we could switch PCIDevice to default romfile to try to
> <devicename>.rom if none is specified. Since qdev names are unique, this
> should work well.

Been there, tried that.  It isn't *that* easy.  The PCI ID in the option 
rom header doesn't match the PCI ID of the emulated lsi, so seabios 
refuses to load it from the rom bar.

cheers,
   Gerd
Anthony Liguori - Aug. 20, 2010, 2:39 p.m.
On 08/20/2010 09:09 AM, Gerd Hoffmann wrote:
>   Hi,
>
>>> Fetch
>>> http://www.lsi.com/DistributionSystem/AssetDocument/files/support/ssp/sdms/Bios/lsi_bios.zip 
>>>
>>> -> unzip -> boot with "-option-rom 8xx_64.rom"
>>
>> Maybe we could switch PCIDevice to default romfile to try to
>> <devicename>.rom if none is specified. Since qdev names are unique, this
>> should work well.
>
> Been there, tried that.  It isn't *that* easy.  The PCI ID in the 
> option rom header doesn't match the PCI ID of the emulated lsi, so 
> seabios refuses to load it from the rom bar.

Heh, I was wondering why it didn't work unless I put rombar=0 :-)

Is this fixable in a reasonable way or does PCI ID in the option rom 
represent a much newer device that would trigger issues with guest drivers?

Regards,

Anthony Liguori

> cheers,
>   Gerd
>
Kevin O'Connor - Aug. 21, 2010, 2:20 p.m.
On Fri, Aug 20, 2010 at 09:39:10AM -0500, Anthony Liguori wrote:
> On 08/20/2010 09:09 AM, Gerd Hoffmann wrote:
> >Been there, tried that.  It isn't *that* easy.  The PCI ID in the
> >option rom header doesn't match the PCI ID of the emulated lsi, so
> >seabios refuses to load it from the rom bar.
> 
> Heh, I was wondering why it didn't work unless I put rombar=0 :-)
> 
> Is this fixable in a reasonable way or does PCI ID in the option rom
> represent a much newer device that would trigger issues with guest
> drivers?

The PCI spec requires that the PCI IDs match - this is done so that a
single rom can store multiple optionroms.

One could place the optionrom in fw_cfg with a "file" name of
"pciXXXX,YYYY.rom".  SeaBIOS will then deploy that rom for every
device that has the given vendor/devid.  (It wont require matching PCI
IDs ids in the rom.)

-Kevin
Gerd Hoffmann - Aug. 23, 2010, 8:52 a.m.
On 08/21/10 16:20, Kevin O'Connor wrote:
> On Fri, Aug 20, 2010 at 09:39:10AM -0500, Anthony Liguori wrote:
>> On 08/20/2010 09:09 AM, Gerd Hoffmann wrote:
>>> Been there, tried that.  It isn't *that* easy.  The PCI ID in the
>>> option rom header doesn't match the PCI ID of the emulated lsi, so
>>> seabios refuses to load it from the rom bar.
>>
>> Heh, I was wondering why it didn't work unless I put rombar=0 :-)
>>
>> Is this fixable in a reasonable way or does PCI ID in the option rom
>> represent a much newer device that would trigger issues with guest
>> drivers?
>
> The PCI spec requires that the PCI IDs match - this is done so that a
> single rom can store multiple optionroms.
>
> One could place the optionrom in fw_cfg with a "file" name of
> "pciXXXX,YYYY.rom".  SeaBIOS will then deploy that rom for every
> device that has the given vendor/devid.  (It wont require matching PCI
> IDs ids in the rom.)

Yea, this is exactly what "rombar=0" (property for pci devices) does: 
load the rom via fw_cfg instead of using the pci option rom bar ;)

cheers,
   Gerd

Patch

diff --git a/Makefile.target b/Makefile.target
index c8281e9..abba79c 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -199,6 +199,7 @@  obj-i386-y += mc146818rtc.o i8259.o pc.o
 obj-i386-y += cirrus_vga.o apic.o ioapic.o piix_pci.o
 obj-i386-y += vmmouse.o vmport.o hpet.o applesmc.o
 obj-i386-y += device-hotplug.o pci-hotplug.o smbios.o wdt_ib700.o
+obj-i386-y += extboot.o
 obj-i386-y += debugcon.o multiboot.o
 obj-i386-y += pc_piix.o

diff --git a/blockdev.c b/blockdev.c
index 01e402b..78c286c 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -15,6 +15,8 @@ 
 #include "qemu-config.h"
 #include "sysemu.h"

+DriveInfo *extboot_drive = NULL;
+
 static QTAILQ_HEAD(drivelist, DriveInfo) drives =
QTAILQ_HEAD_INITIALIZER(drives);

 /*
@@ -150,6 +152,7 @@  DriveInfo *drive_init(QemuOpts *opts, int
default_to_scsi, int *fatal_error)
     int on_read_error, on_write_error;
     const char *devaddr;
     DriveInfo *dinfo;
+    int is_extboot = 0;
     int snapshot = 0;
     int ret;

@@ -311,6 +314,12 @@  DriveInfo *drive_init(QemuOpts *opts, int
default_to_scsi, int *fatal_error)
         }
     }

+    is_extboot = qemu_opt_get_bool(opts, "boot", 0);
+    if (is_extboot && extboot_drive) {
+        fprintf(stderr, "qemu: two bootable drives specified\n");
+        return NULL;
+    }
+
     on_write_error = BLOCK_ERR_STOP_ENOSPC;
     if ((buf = qemu_opt_get(opts, "werror")) != NULL) {
         if (type != IF_IDE && type != IF_SCSI && type != IF_VIRTIO && type
!= IF_NONE) {
@@ -421,6 +430,10 @@  DriveInfo *drive_init(QemuOpts *opts, int
default_to_scsi, int *fatal_error)
         strncpy(dinfo->serial, serial, sizeof(dinfo->serial) - 1);
     QTAILQ_INSERT_TAIL(&drives, dinfo, next);

+    if (is_extboot) {
+        extboot_drive = dinfo;
+    }
+
     bdrv_set_on_error(dinfo->bdrv, on_read_error, on_write_error);

     switch(type) {
diff --git a/blockdev.h b/blockdev.h
index 37f3a01..e707b87 100644
--- a/blockdev.h
+++ b/blockdev.h
@@ -59,4 +59,6 @@  int do_block_set_passwd(Monitor *mon, const QDict *qdict,
QObject **ret_data);
 int do_change_block(Monitor *mon, const char *device,
                     const char *filename, const char *fmt);

+extern DriveInfo *extboot_drive;
+
 #endif
diff --git a/hw/extboot.c b/hw/extboot.c
new file mode 100644
index 0000000..8ada21b
--- /dev/null
+++ b/hw/extboot.c
@@ -0,0 +1,123 @@ 
+/*
+ * Extended boot option ROM support.
+ *
+ * Copyright IBM, Corp. 2007
+ *
+ * Authors:
+ *  Anthony Liguori   <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.  See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#include "hw.h"
+#include "pc.h"
+#include "isa.h"
+#include "block.h"
+
+/* Extended Boot ROM suport */
+
+union extboot_cmd
+{
+    uint16_t type;
+    struct {
+        uint16_t type;
+        uint16_t cylinders;
+        uint16_t heads;
+        uint16_t sectors;
+        uint64_t nb_sectors;
+    } query_geometry;
+    struct {
+        uint16_t type;
+        uint16_t nb_sectors;
+        uint16_t segment;
+        uint16_t offset;
+        uint64_t sector;
+    } xfer;
+};
+
+static void get_translated_chs(BlockDriverState *bs, int *c, int *h, int
*s)
+{
+    bdrv_get_geometry_hint(bs, c, h, s);
+
+    if (*c <= 1024) {
+        *c >>= 0;
+        *h <<= 0;
+    } else if (*c <= 2048) {
+        *c >>= 1;
+        *h <<= 1;
+    } else if (*c <= 4096) {
+        *c >>= 2;
+        *h <<= 2;
+    } else if (*c <= 8192) {
+        *c >>= 3;
+        *h <<= 3;
+    } else {
+        *c >>= 4;
+        *h <<= 4;
+    }
+
+    /* what is the correct algorithm for this?? */
+    if (*h == 256) {
+        *h = 255;
+        *c = *c + 1;
+    }
+}
+
+static void extboot_write_cmd(void *opaque, uint32_t addr, uint32_t value)
+{
+    union extboot_cmd cmd;
+    BlockDriverState *bs = opaque;
+    int cylinders, heads, sectors, err;
+    uint64_t nb_sectors;
+    target_phys_addr_t pa = 0;
+    int blen = 0;
+    void *buf = NULL;
+
+    cpu_physical_memory_read((value & 0xFFFF) << 4, (uint8_t *)&cmd,
+                             sizeof(cmd));
+
+    if (cmd.type == 0x01 || cmd.type == 0x02) {
+        pa = cmd.xfer.segment * 16 + cmd.xfer.offset;
+        blen = cmd.xfer.nb_sectors * 512;
+        buf = qemu_memalign(512, blen);
+    }
+
+    switch (cmd.type) {
+    case 0x00:
+        get_translated_chs(bs, &cylinders, &heads, &sectors);
+        bdrv_get_geometry(bs, &nb_sectors);
+        cmd.query_geometry.cylinders = cylinders;
+        cmd.query_geometry.heads = heads;
+        cmd.query_geometry.sectors = sectors;
+        cmd.query_geometry.nb_sectors = nb_sectors;
+        break;
+    case 0x01:
+        err = bdrv_read(bs, cmd.xfer.sector, buf, cmd.xfer.nb_sectors);
+        if (err)
+            printf("Read failed\n");
+
+        cpu_physical_memory_write(pa, buf, blen);
+
+        break;
+    case 0x02:
+        cpu_physical_memory_read(pa, buf, blen);
+
+        err = bdrv_write(bs, cmd.xfer.sector, buf, cmd.xfer.nb_sectors);
+        if (err)
+            printf("Write failed\n");
+
+        break;
+    }
+
+    cpu_physical_memory_write((value & 0xFFFF) << 4, (uint8_t *)&cmd,
+                              sizeof(cmd));
+    if (buf)
+        qemu_free(buf);
+}
+
+void extboot_init(BlockDriverState *bs)
+{
+    register_ioport_write(0x405, 1, 2, extboot_write_cmd, bs);
+}
diff --git a/hw/pc.c b/hw/pc.c
index 58dea57..88f43aa 100644
--- a/hw/pc.c
+++ b/hw/pc.c
@@ -54,6 +54,7 @@ 
 #endif

 #define BIOS_FILENAME "bios.bin"
+#define EXTBOOT_FILENAME "extboot.bin"

 #define PC_MAX_BIOS_SIZE (4 * 1024 * 1024)

@@ -953,6 +954,10 @@  void pc_memory_init(ram_addr_t ram_size,
                                  isa_bios_size,
                                  (bios_offset + bios_size - isa_bios_size)
| IO_MEM_ROM);

+    if (extboot_drive) {
+        option_rom[nb_option_roms++] = qemu_strdup(EXTBOOT_FILENAME);
+    }
+
     option_rom_offset = qemu_ram_alloc(NULL, "pc.rom", PC_ROM_SIZE);
     cpu_register_physical_memory(PC_ROM_MIN_VGA, PC_ROM_SIZE,
option_rom_offset);

@@ -1074,4 +1079,16 @@  void pc_pci_device_init(PCIBus *pci_bus)
     for (bus = 0; bus <= max_bus; bus++) {
         pci_create_simple(pci_bus, -1, "lsi53c895a");
     }
+
+    if (extboot_drive) {
+        DriveInfo *info = extboot_drive;
+        int cyls, heads, secs;
+
+        if (info->type != IF_IDE && info->type != IF_VIRTIO) {
+            bdrv_guess_geometry(info->bdrv, &cyls, &heads, &secs);
+            bdrv_set_geometry_hint(info->bdrv, cyls, heads, secs);
+        }
+
+        extboot_init(info->bdrv);
+    }
 }
diff --git a/hw/pc.h b/hw/pc.h
index 63b0249..61882db 100644
--- a/hw/pc.h
+++ b/hw/pc.h
@@ -167,6 +167,10 @@  void isa_cirrus_vga_init(void);

 void isa_ne2000_init(int base, int irq, NICInfo *nd);

+/* extboot.c */
+
+void extboot_init(BlockDriverState *bs);
+
 /* e820 types */
 #define E820_RAM        1
 #define E820_RESERVED   2
diff --git a/qemu-config.c b/qemu-config.c
index 95abe61..08ee553 100644
--- a/qemu-config.c
+++ b/qemu-config.c
@@ -79,6 +79,10 @@  QemuOptsList qemu_drive_opts = {
         },{
             .name = "readonly",
             .type = QEMU_OPT_BOOL,
+        },{
+            .name = "boot",
+            .type = QEMU_OPT_BOOL,
+            .help = "make this a boot drive",
         },
         { /* end if list */ }
     },