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

login
register
mail settings
Submitter Haishan Bai
Date Aug. 18, 2010, 1:43 a.m.
Message ID <4C6B3AB6.7010108@gmail.com>
Download mbox | patch
Permalink /patch/61983/
State New
Headers show

Comments

Haishan Bai - Aug. 18, 2010, 1:43 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

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 */ }
     },