[seabios] update to 1.6.3 upstream

Justin M. Forbes jforbes at fedoraproject.org
Wed Feb 8 20:06:39 UTC 2012


commit 6960463bf170d62c9d4af9e82e3572cb72018aa1
Author: Justin M. Forbes <jforbes at redhat.com>
Date:   Wed Feb 8 14:06:34 2012 -0600

    update to 1.6.3 upstream

 .gitignore                       |    1 +
 seabios-0.6.2-build.patch        |   49 --
 seabios-0.6.2-fix-QXL.patch      |   17 -
 seabios-usb_fix_boot_paths.patch |   27 +
 seabios-virtio-scsi.patch        | 1557 ++++++++++++++++++++++++++++++++++++++
 seabios.spec                     |   14 +-
 sources                          |    2 +-
 7 files changed, 1595 insertions(+), 72 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index f412bfe..0b098a5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
 seabios-0.5.1.tar.gz
 seabios-0.6.0.tar.gz
 /seabios-0.6.2.tar.gz
+/seabios-1.6.3.tar.gz
diff --git a/seabios-usb_fix_boot_paths.patch b/seabios-usb_fix_boot_paths.patch
new file mode 100644
index 0000000..d5104b5
--- /dev/null
+++ b/seabios-usb_fix_boot_paths.patch
@@ -0,0 +1,27 @@
+commit a3fea015398d7c41db5b5d348fe3f6d76236b6be
+Author: Paolo Bonzini <pbonzini at redhat.com>
+Date:   Fri Nov 18 15:59:24 2011 +0100
+
+    usb: fix boot paths
+    
+    The fw paths for USB devices that SeaBIOS computes are off-by-one,
+    because QEMU builds those paths with a numbering that starts from one
+    (see usb_fill_port and usb_hub_initfn in QEMU).  Fix that so that
+    the numbering agrees.
+
+diff --git a/src/boot.c b/src/boot.c
+index 119f290..93928d3 100644
+--- a/src/boot.c
++++ b/src/boot.c
+@@ -191,9 +191,9 @@ int bootprio_find_usb(struct pci_device *pci, u64 path)
+     for (i=56; i>0; i-=8) {
+         int port = (path >> i) & 0xff;
+         if (port != 0xff)
+-            p += snprintf(p, desc+sizeof(desc)-p, "/hub@%x", port);
++            p += snprintf(p, desc+sizeof(desc)-p, "/hub@%x", port+1);
+     }
+-    snprintf(p, desc+sizeof(desc)-p, "/*@%x", (u32)(path & 0xff));
++    snprintf(p, desc+sizeof(desc)-p, "/*@%x", (u32)(path & 0xff)+1);
+     return find_prio(desc);
+ }
+
diff --git a/seabios-virtio-scsi.patch b/seabios-virtio-scsi.patch
new file mode 100644
index 0000000..47a4527
--- /dev/null
+++ b/seabios-virtio-scsi.patch
@@ -0,0 +1,1557 @@
+diff --git a/Makefile b/Makefile
+index 7d511e8..fb9bfaf 100644
+--- a/Makefile
++++ b/Makefile
+@@ -12,10 +12,10 @@ OUT=out/
+ 
+ # Source files
+ SRCBOTH=misc.c stacks.c pmm.c output.c util.c block.c floppy.c ata.c mouse.c \
+-        kbd.c pci.c serial.c clock.c pic.c cdrom.c ps2port.c smp.c resume.c \
+-        pnpbios.c pirtable.c vgahooks.c ramdisk.c pcibios.c blockcmd.c \
+-        usb.c usb-uhci.c usb-ohci.c usb-ehci.c usb-hid.c usb-msc.c \
+-        virtio-ring.c virtio-pci.c virtio-blk.c apm.c ahci.c
++    kbd.c pci.c serial.c clock.c pic.c cdrom.c ps2port.c smp.c resume.c \
++    pnpbios.c pirtable.c vgahooks.c ramdisk.c pcibios.c blockcmd.c \
++    usb.c usb-uhci.c usb-ohci.c usb-ehci.c usb-hid.c usb-msc.c \
++    virtio-ring.c virtio-pci.c virtio-blk.c virtio-scsi.c apm.c ahci.c
+ SRC16=$(SRCBOTH) system.c disk.c font.c
+ SRC32FLAT=$(SRCBOTH) post.c shadow.c memmap.c coreboot.c boot.c \
+       acpi.c smm.c mptable.c smbios.c pciinit.c optionroms.c mtrr.c \
+diff --git a/src/Kconfig b/src/Kconfig
+index 338f51a..8de3503 100644
+--- a/src/Kconfig
++++ b/src/Kconfig
+@@ -109,10 +109,16 @@ menu "Hardware support"
+             Support for AHCI disk code.
+     config VIRTIO_BLK
+         depends on DRIVES && !COREBOOT
+-        bool "VirtIO controllers"
++        bool "virtio-blk controllers"
+         default y
+         help
+-            Support boot from virtio storage.
++            Support boot from virtio-blk storage.
++    config VIRTIO_SCSI
++        depends on DRIVES && !COREBOOT
++        bool "virtio-scsi controllers"
++        default y
++        help
++            Support boot from virtio-scsi storage.
+     config FLOPPY
+         depends on DRIVES
+         bool "Floppy controller"
+diff --git a/src/block.c b/src/block.c
+index f7e7851..e607d67 100644
+--- a/src/block.c
++++ b/src/block.c
+@@ -11,8 +11,8 @@
+ #include "util.h" // dprintf
+ #include "ata.h" // process_ata_op
+ #include "ahci.h" // process_ahci_op
+-#include "usb-msc.h" // process_usb_op
+-#include "virtio-blk.h" // process_virtio_op
++#include "virtio-blk.h" // process_virtio_blk_op
++#include "blockcmd.h" // cdb_*
+ 
+ u8 FloppyCount VAR16VISIBLE;
+ u8 CDCount;
+@@ -276,6 +276,28 @@ map_floppy_drive(struct drive_s *drive_g)
+  * 16bit calling interface
+  ****************************************************************/
+ 
++int
++process_scsi_op(struct disk_op_s *op)
++{
++    if (!CONFIG_VIRTIO_SCSI && !CONFIG_USB_MSC)
++        return 0;
++    switch (op->command) {
++    case CMD_READ:
++        return cdb_read(op);
++    case CMD_WRITE:
++        return cdb_write(op);
++    case CMD_FORMAT:
++    case CMD_RESET:
++    case CMD_ISREADY:
++    case CMD_VERIFY:
++    case CMD_SEEK:
++        return DISK_RET_SUCCESS;
++    default:
++        op->count = 0;
++        return DISK_RET_EPARAM;
++    }
++}
++
+ // Execute a disk_op request.
+ int
+ process_op(struct disk_op_s *op)
+@@ -293,12 +315,13 @@ process_op(struct disk_op_s *op)
+         return process_ramdisk_op(op);
+     case DTYPE_CDEMU:
+         return process_cdemu_op(op);
+-    case DTYPE_USB:
+-        return process_usb_op(op);
+-    case DTYPE_VIRTIO:
+-	return process_virtio_op(op);
++    case DTYPE_VIRTIO_BLK:
++        return process_virtio_blk_op(op);
+     case DTYPE_AHCI:
+ 	return process_ahci_op(op);
++    case DTYPE_USB:
++    case DTYPE_VIRTIO_SCSI:
++        return process_scsi_op(op);
+     default:
+         op->count = 0;
+         return DISK_RET_EPARAM;
+diff --git a/src/blockcmd.c b/src/blockcmd.c
+index c9c6845..b2a8d71 100644
+--- a/src/blockcmd.c
++++ b/src/blockcmd.c
+@@ -12,6 +12,7 @@
+ #include "ata.h" // atapi_cmd_data
+ #include "ahci.h" // atapi_cmd_data
+ #include "usb-msc.h" // usb_cmd_data
++#include "virtio-scsi.h" // virtio_scsi_cmd_data
+ 
+ // Route command to low-level handler.
+ static int
+@@ -25,6 +26,8 @@ cdb_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize)
+         return usb_cmd_data(op, cdbcmd, blocksize);
+     case DTYPE_AHCI:
+         return ahci_cmd_data(op, cdbcmd, blocksize);
++    case DTYPE_VIRTIO_SCSI:
++        return virtio_scsi_cmd_data(op, cdbcmd, blocksize);
+     default:
+         op->count = 0;
+         return DISK_RET_EPARAM;
+@@ -32,6 +35,128 @@ cdb_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize)
+ }
+ 
+ int
++scsi_is_ready(struct disk_op_s *op)
++{
++    dprintf(6, "scsi_is_ready (drive=%p)\n", op->drive_g);
++
++    /* Retry TEST UNIT READY for 5 seconds unless MEDIUM NOT PRESENT is
++     * reported by the device.  If the device reports "IN PROGRESS",
++     * 30 seconds is added. */
++    int in_progress = 0;
++    u64 end = calc_future_tsc(5000);
++    for (;;) {
++        if (check_tsc(end)) {
++            dprintf(1, "test unit ready failed\n");
++            return -1;
++        }
++
++        int ret = cdb_test_unit_ready(op);
++        if (!ret)
++            // Success
++            break;
++
++        struct cdbres_request_sense sense;
++        ret = cdb_get_sense(op, &sense);
++        if (ret)
++            // Error - retry.
++            continue;
++
++        // Sense succeeded.
++        if (sense.asc == 0x3a) { /* MEDIUM NOT PRESENT */
++            dprintf(1, "Device reports MEDIUM NOT PRESENT\n");
++            return -1;
++        }
++
++        if (sense.asc == 0x04 && sense.ascq == 0x01 && !in_progress) {
++            /* IN PROGRESS OF BECOMING READY */
++            printf("Waiting for device to detect medium... ");
++            /* Allow 30 seconds more */
++            end = calc_future_tsc(30000);
++            in_progress = 1;
++        }
++    }
++    return 0;
++}
++
++// Validate drive and find block size and sector count.
++int
++scsi_init_drive(struct drive_s *drive, const char *s, int *pdt, char **desc)
++{
++    if (!CONFIG_USB_MSC && !CONFIG_VIRTIO_SCSI)
++        return 0;
++
++    struct disk_op_s dop;
++    memset(&dop, 0, sizeof(dop));
++    dop.drive_g = drive;
++    struct cdbres_inquiry data;
++    int ret = cdb_get_inquiry(&dop, &data);
++    if (ret)
++        return ret;
++    char vendor[sizeof(data.vendor)+1], product[sizeof(data.product)+1];
++    char rev[sizeof(data.rev)+1];
++    strtcpy(vendor, data.vendor, sizeof(vendor));
++    nullTrailingSpace(vendor);
++    strtcpy(product, data.product, sizeof(product));
++    nullTrailingSpace(product);
++    strtcpy(rev, data.rev, sizeof(rev));
++    nullTrailingSpace(rev);
++    *pdt = data.pdt & 0x1f;
++    int removable = !!(data.removable & 0x80);
++    dprintf(1, "%s vendor='%s' product='%s' rev='%s' type=%d removable=%d\n"
++            , s, vendor, product, rev, *pdt, removable);
++    drive->removable = removable;
++
++    if (*pdt == SCSI_TYPE_CDROM) {
++        drive->blksize = CDROM_SECTOR_SIZE;
++        drive->sectors = (u64)-1;
++
++        *desc = znprintf(MAXDESCSIZE, "DVD/CD [%s Drive %s %s %s]"
++                              , s, vendor, product, rev);
++        return 0;
++    }
++
++    ret = scsi_is_ready(&dop);
++    if (ret) {
++        dprintf(1, "scsi_is_ready returned %d\n", ret);
++        return ret;
++    }
++
++    struct cdbres_read_capacity capdata;
++    ret = cdb_read_capacity(&dop, &capdata);
++    if (ret)
++        return ret;
++
++    // READ CAPACITY returns the address of the last block.
++    // We do not bother with READ CAPACITY(16) because BIOS does not support
++    // 64-bit LBA anyway.
++    drive->blksize = ntohl(capdata.blksize);
++    drive->sectors = (u64)ntohl(capdata.sectors) + 1;
++    dprintf(1, "%s blksize=%d sectors=%d\n"
++            , s, drive->blksize, (unsigned)drive->sectors);
++
++    struct cdbres_mode_sense_geom geomdata;
++    ret = cdb_mode_sense_geom(&dop, &geomdata);
++    if (ret == 0) {
++        u32 cylinders;
++        cylinders = geomdata.cyl[0] << 16;
++        cylinders |= geomdata.cyl[1] << 8;
++        cylinders |= geomdata.cyl[2];
++        if (cylinders && geomdata.heads &&
++            drive->sectors <= 0xFFFFFFFFULL &&
++            ((u32)drive->sectors % (geomdata.heads * cylinders) == 0)) {
++            drive->pchs.cylinders = cylinders;
++            drive->pchs.heads = geomdata.heads;
++            drive->pchs.spt = (u32)drive->sectors
++     / (geomdata.heads * cylinders);
++        }
++    }
++
++    *desc = znprintf(MAXDESCSIZE, "%s Drive %s %s %s"
++                      , s, vendor, product, rev);
++    return 0;
++}
++
++int
+ cdb_get_inquiry(struct disk_op_s *op, struct cdbres_inquiry *data)
+ {
+     struct cdb_request_sense cmd;
+@@ -56,6 +181,18 @@ cdb_get_sense(struct disk_op_s *op, struct cdbres_request_sense *data)
+     return cdb_cmd_data(op, &cmd, sizeof(*data));
+ }
+ 
++// Test unit ready
++int
++cdb_test_unit_ready(struct disk_op_s *op)
++{
++    struct cdb_request_sense cmd;
++    memset(&cmd, 0, sizeof(cmd));
++    cmd.command = CDB_CMD_TEST_UNIT_READY;
++    op->count = 0;
++    op->buf_fl = NULL;
++    return cdb_cmd_data(op, &cmd, 0);
++}
++
+ // Request capacity
+ int
+ cdb_read_capacity(struct disk_op_s *op, struct cdbres_read_capacity *data)
+@@ -68,6 +205,21 @@ cdb_read_capacity(struct disk_op_s *op, struct cdbres_read_capacity *data)
+     return cdb_cmd_data(op, &cmd, sizeof(*data));
+ }
+ 
++// Mode sense, geometry page.
++int
++cdb_mode_sense_geom(struct disk_op_s *op, struct cdbres_mode_sense_geom *data)
++{
++    struct cdb_mode_sense cmd;
++    memset(&cmd, 0, sizeof(cmd));
++    cmd.command = CDB_CMD_MODE_SENSE;
++    cmd.flags = 8; /* DBD */
++    cmd.page = MODE_PAGE_HD_GEOMETRY;
++    cmd.count = htons(sizeof(*data));
++    op->count = 1;
++    op->buf_fl = data;
++    return cdb_cmd_data(op, &cmd, sizeof(*data));
++}
++
+ // Read sectors.
+ int
+ cdb_read(struct disk_op_s *op)
+@@ -79,3 +231,15 @@ cdb_read(struct disk_op_s *op)
+     cmd.count = htons(op->count);
+     return cdb_cmd_data(op, &cmd, GET_GLOBAL(op->drive_g->blksize));
+ }
++
++// Write sectors.
++int
++cdb_write(struct disk_op_s *op)
++{
++    struct cdb_rwdata_10 cmd;
++    memset(&cmd, 0, sizeof(cmd));
++    cmd.command = CDB_CMD_WRITE_10;
++    cmd.lba = htonl(op->lba);
++    cmd.count = htons(op->count);
++    return cdb_cmd_data(op, &cmd, GET_GLOBAL(op->drive_g->blksize));
++}
+diff --git a/src/blockcmd.h b/src/blockcmd.h
+index 903c435..bace649 100644
+--- a/src/blockcmd.h
++++ b/src/blockcmd.h
+@@ -32,8 +32,9 @@ struct cdbres_read_capacity {
+     u32 blksize;
+ } PACKED;
+ 
+-#define CDB_CMD_INQUIRY 0x12
+-#define CDB_CMD_REQUEST_SENSE 0x03
++#define CDB_CMD_TEST_UNIT_READY  0x00
++#define CDB_CMD_INQUIRY          0x12
++#define CDB_CMD_REQUEST_SENSE    0x03
+ 
+ struct cdb_request_sense {
+     u8 command;
+@@ -56,6 +57,9 @@ struct cdbres_request_sense {
+     u32 reserved_0e;
+ } PACKED;
+ 
++#define SCSI_TYPE_DISK  0x00
++#define SCSI_TYPE_CDROM 0x05
++
+ struct cdbres_inquiry {
+     u8 pdt;
+     u8 removable;
+@@ -67,11 +71,45 @@ struct cdbres_inquiry {
+     char rev[4];
+ } PACKED;
+ 
++#define CDB_CMD_MODE_SENSE    0x5A
++#define MODE_PAGE_HD_GEOMETRY 0x04
++
++struct cdb_mode_sense {
++    u8 command;
++    u8 flags;
++    u8 page;
++    u32 reserved_03;
++    u16 count;
++    u8 reserved_09;
++    u8 pad[6];
++} PACKED;
++
++struct cdbres_mode_sense_geom {
++    u8 unused_00[3];
++    u8 read_only;
++    u32 unused_04;
++    u8 page;
++    u8 length;
++    u8 cyl[3];
++    u8 heads;
++    u8 precomp[3];
++    u8 reduced[3];
++    u16 step_rate;
++    u8 landing[3];
++    u16 rpm;
++} PACKED;
++
+ // blockcmd.c
+ int cdb_get_inquiry(struct disk_op_s *op, struct cdbres_inquiry *data);
+ int cdb_get_sense(struct disk_op_s *op, struct cdbres_request_sense *data);
++int cdb_test_unit_ready(struct disk_op_s *op);
+ int cdb_read_capacity(struct disk_op_s *op, struct cdbres_read_capacity *data);
++int cdb_mode_sense_geom(struct disk_op_s *op, struct cdbres_mode_sense_geom *data);
+ int cdb_inquiry(struct disk_op_s *op, struct cdbres_inquiry *data);
+ int cdb_read(struct disk_op_s *op);
++int cdb_write(struct disk_op_s *op);
++
++int scsi_is_ready(struct disk_op_s *op);
++int scsi_init_drive(struct drive_s *drive, const char *s, int *pdt, char **desc);
+ 
+ #endif // blockcmd.h
+diff --git a/src/boot.c b/src/boot.c
+index 93928d3..c0991cd 100644
+--- a/src/boot.c
++++ b/src/boot.c
+@@ -128,6 +128,20 @@ int bootprio_find_pci_device(struct pci_device *pci)
+     return find_prio(desc);
+ }
+ 
++int bootprio_find_scsi_device(struct pci_device *pci, int target, int lun)
++{
++    if (!CONFIG_BOOTORDER)
++        return -1;
++    if (!pci)
++        // support only pci machine for now
++        return -1;
++    // Find scsi drive - for example: /pci at i0cf8/scsi at 5/channel at 0/disk at 1,0
++    char desc[256], *p;
++    p = build_pci_path(desc, sizeof(desc), "*", pci);
++    snprintf(p, desc+sizeof(desc)-p, "/*@0/*@%d,%d", target, lun);
++    return find_prio(desc);
++}
++
+ int bootprio_find_ata_device(struct pci_device *pci, int chanid, int slave)
+ {
+     if (!CONFIG_BOOTORDER)
+diff --git a/src/boot.h b/src/boot.h
+index d776aa1..686f04d 100644
+--- a/src/boot.h
++++ b/src/boot.h
+@@ -14,6 +14,7 @@ void boot_add_cbfs(void *data, const char *desc, int prio);
+ void boot_prep(void);
+ struct pci_device;
+ int bootprio_find_pci_device(struct pci_device *pci);
++int bootprio_find_scsi_device(struct pci_device *pci, int target, int lun);
+ int bootprio_find_ata_device(struct pci_device *pci, int chanid, int slave);
+ int bootprio_find_fdc_device(struct pci_device *pci, int port, int fdid);
+ int bootprio_find_pci_rom(struct pci_device *pci, int instance);
+diff --git a/src/cdrom.c b/src/cdrom.c
+index 6351fec..170ffc4 100644
+--- a/src/cdrom.c
++++ b/src/cdrom.c
+@@ -184,60 +184,6 @@ cdemu_134b(struct bregs *regs)
+  * CD booting
+  ****************************************************************/
+ 
+-static int
+-atapi_is_ready(struct disk_op_s *op)
+-{
+-    dprintf(6, "atapi_is_ready (drive=%p)\n", op->drive_g);
+-
+-    /* Retry READ CAPACITY for 5 seconds unless MEDIUM NOT PRESENT is
+-     * reported by the device.  If the device reports "IN PROGRESS",
+-     * 30 seconds is added. */
+-    struct cdbres_read_capacity info;
+-    int in_progress = 0;
+-    u64 end = calc_future_tsc(5000);
+-    for (;;) {
+-        if (check_tsc(end)) {
+-            dprintf(1, "read capacity failed\n");
+-            return -1;
+-        }
+-
+-        int ret = cdb_read_capacity(op, &info);
+-        if (!ret)
+-            // Success
+-            break;
+-
+-        struct cdbres_request_sense sense;
+-        ret = cdb_get_sense(op, &sense);
+-        if (ret)
+-            // Error - retry.
+-            continue;
+-
+-        // Sense succeeded.
+-        if (sense.asc == 0x3a) { /* MEDIUM NOT PRESENT */
+-            dprintf(1, "Device reports MEDIUM NOT PRESENT\n");
+-            return -1;
+-        }
+-
+-        if (sense.asc == 0x04 && sense.ascq == 0x01 && !in_progress) {
+-            /* IN PROGRESS OF BECOMING READY */
+-            printf("Waiting for device to detect medium... ");
+-            /* Allow 30 seconds more */
+-            end = calc_future_tsc(30000);
+-            in_progress = 1;
+-        }
+-    }
+-
+-    u32 blksize = ntohl(info.blksize), sectors = ntohl(info.sectors);
+-    if (blksize != GET_GLOBAL(op->drive_g->blksize)) {
+-        printf("Unsupported sector size %u\n", blksize);
+-        return -1;
+-    }
+-
+-    dprintf(6, "sectors=%u\n", sectors);
+-    printf("%dMB medium detected\n", sectors>>(20-11));
+-    return 0;
+-}
+-
+ int
+ cdrom_boot(struct drive_s *drive_g)
+ {
+@@ -248,9 +194,9 @@ cdrom_boot(struct drive_s *drive_g)
+     if (!dop.drive_g || cdid < 0)
+         return 1;
+ 
+-    int ret = atapi_is_ready(&dop);
++    int ret = scsi_is_ready(&dop);
+     if (ret)
+-        dprintf(1, "atapi_is_ready returned %d\n", ret);
++        dprintf(1, "scsi_is_ready returned %d\n", ret);
+ 
+     // Read the Boot Record Volume Descriptor
+     u8 buffer[2048];
+diff --git a/src/disk.c b/src/disk.c
+index 8f7c61f..6a170fd 100644
+--- a/src/disk.c
++++ b/src/disk.c
+@@ -546,7 +546,8 @@ disk_1348(struct bregs *regs, struct drive_s *drive_g)
+     SET_INT13DPT(regs, blksize, blksize);
+ 
+     if (size < 30 ||
+-        (type != DTYPE_ATA && type != DTYPE_ATAPI && type != DTYPE_VIRTIO)) {
++        (type != DTYPE_ATA && type != DTYPE_ATAPI &&
++         type != DTYPE_VIRTIO_BLK && type != DTYPE_VIRTIO_SCSI)) {
+         disk_ret(regs, DISK_RET_SUCCESS);
+         return;
+     }
+@@ -651,7 +652,7 @@ disk_1348(struct bregs *regs, struct drive_s *drive_g)
+         SET_INT13DPT(regs, iface_path, iobase1);
+     }
+ 
+-    if (type != DTYPE_VIRTIO) {
++    if (type != DTYPE_VIRTIO_BLK) {
+         SET_INT13DPT(regs, iface_type[0], 'A');
+         SET_INT13DPT(regs, iface_type[1], 'T');
+         SET_INT13DPT(regs, iface_type[2], 'A');
+diff --git a/src/disk.h b/src/disk.h
+index ac33518..d344399 100644
+--- a/src/disk.h
++++ b/src/disk.h
+@@ -198,15 +198,16 @@ struct drive_s {
+ #define DISK_SECTOR_SIZE  512
+ #define CDROM_SECTOR_SIZE 2048
+ 
+-#define DTYPE_NONE     0x00
+-#define DTYPE_FLOPPY   0x01
+-#define DTYPE_ATA      0x02
+-#define DTYPE_ATAPI    0x03
+-#define DTYPE_RAMDISK  0x04
+-#define DTYPE_CDEMU    0x05
+-#define DTYPE_USB      0x06
+-#define DTYPE_VIRTIO   0x07
+-#define DTYPE_AHCI     0x08
++#define DTYPE_NONE         0x00
++#define DTYPE_FLOPPY       0x01
++#define DTYPE_ATA          0x02
++#define DTYPE_ATAPI        0x03
++#define DTYPE_RAMDISK      0x04
++#define DTYPE_CDEMU        0x05
++#define DTYPE_USB          0x06
++#define DTYPE_VIRTIO_BLK   0x07
++#define DTYPE_AHCI         0x08
++#define DTYPE_VIRTIO_SCSI  0x09
+ 
+ #define MAXDESCSIZE 80
+ 
+diff --git a/src/pci_ids.h b/src/pci_ids.h
+index e1cded2..4b59585 100644
+--- a/src/pci_ids.h
++++ b/src/pci_ids.h
+@@ -2608,3 +2608,4 @@
+ 
+ #define PCI_VENDOR_ID_REDHAT_QUMRANET	0x1af4
+ #define PCI_DEVICE_ID_VIRTIO_BLK	0x1001
++#define PCI_DEVICE_ID_VIRTIO_SCSI	0x1004
+diff --git a/src/post.c b/src/post.c
+index b4ad1fa..d7bbcdd 100644
+--- a/src/post.c
++++ b/src/post.c
+@@ -26,6 +26,7 @@
+ #include "xen.h" // xen_probe_hvm_info
+ #include "ps2port.h" // ps2port_setup
+ #include "virtio-blk.h" // virtio_blk_setup
++#include "virtio-scsi.h" // virtio_scsi_setup
+ 
+ 
+ /****************************************************************
+@@ -190,6 +191,7 @@ init_hw(void)
+     cbfs_payload_setup();
+     ramdisk_setup();
+     virtio_blk_setup();
++    virtio_scsi_setup();
+ }
+ 
+ // Begin the boot process by invoking an int0x19 in 16bit mode.
+diff --git a/src/usb-ehci.c b/src/usb-ehci.c
+index a60c607..9bdd638 100644
+--- a/src/usb-ehci.c
++++ b/src/usb-ehci.c
+@@ -303,22 +303,12 @@ ehci_init(struct pci_device *pci, int busid, struct pci_device *comppci)
+  * End point communication
+  ****************************************************************/
+ 
+-static int
+-ehci_wait_qh(struct usb_ehci_s *cntl, struct ehci_qh *qh)
+-{
+-    // XXX - 500ms just a guess
+-    u64 end = calc_future_tsc(500);
+-    for (;;) {
+-        if (qh->qtd_next & EHCI_PTR_TERM)
+-            // XXX - confirm
+-            return 0;
+-        if (check_tsc(end)) {
+-            warn_timeout();
+-            return -1;
+-        }
+-        yield();
+-    }
+-}
++struct ehci_pipe {
++    struct ehci_qh qh;
++    struct ehci_qtd *next_td, *tds;
++    void *data;
++    struct usb_pipe pipe;
++};
+ 
+ // Wait for next USB async frame to start - for ensuring safe memory release.
+ static void
+@@ -362,12 +352,46 @@ ehci_waittick(struct usb_ehci_s *cntl)
+     writel(&cntl->regs->usbsts, STS_IAA);
+ }
+ 
+-struct ehci_pipe {
+-    struct ehci_qh qh;
+-    struct ehci_qtd *next_td, *tds;
+-    void *data;
+-    struct usb_pipe pipe;
+-};
++static void
++ehci_reset_pipe(struct ehci_pipe *pipe)
++{
++    SET_FLATPTR(pipe->qh.qtd_next, EHCI_PTR_TERM);
++    SET_FLATPTR(pipe->qh.alt_next, EHCI_PTR_TERM);
++    barrier();
++    SET_FLATPTR(pipe->qh.token, GET_FLATPTR(pipe->qh.token) & QTD_TOGGLE);
++}
++
++static int
++ehci_wait_td(struct ehci_pipe *pipe, struct ehci_qtd *td, int timeout)
++{
++    u64 end = calc_future_tsc(timeout);
++    u32 status;
++    for (;;) {
++        status = td->token;
++        if (!(status & QTD_STS_ACTIVE))
++            break;
++        if (check_tsc(end)) {
++            u32 cur = GET_FLATPTR(pipe->qh.current);
++            u32 tok = GET_FLATPTR(pipe->qh.token);
++            u32 next = GET_FLATPTR(pipe->qh.qtd_next);
++            warn_timeout();
++            dprintf(1, "ehci pipe=%p cur=%08x tok=%08x next=%x td=%p status=%x\n"
++                    , pipe, cur, tok, next, td, status);
++            ehci_reset_pipe(pipe);
++            struct usb_ehci_s *cntl = container_of(
++                GET_FLATPTR(pipe->pipe.cntl), struct usb_ehci_s, usb);
++            ehci_waittick(cntl);
++            return -1;
++        }
++        yield();
++    }
++    if (status & QTD_STS_HALT) {
++        dprintf(1, "ehci_wait_td error - status=%x\n", status);
++        ehci_reset_pipe(pipe);
++        return -2;
++    }
++    return 0;
++}
+ 
+ void
+ ehci_free_pipe(struct usb_pipe *p)
+@@ -416,7 +440,6 @@ ehci_alloc_control_pipe(struct usb_pipe *dummy)
+     memset(pipe, 0, sizeof(*pipe));
+     memcpy(&pipe->pipe, dummy, sizeof(pipe->pipe));
+     pipe->qh.qtd_next = pipe->qh.alt_next = EHCI_PTR_TERM;
+-    pipe->qh.token = QTD_STS_HALT;
+ 
+     // Add queue head to controller list.
+     struct ehci_qh *async_qh = cntl->async_qh;
+@@ -455,15 +478,14 @@ ehci_control(struct usb_pipe *p, int dir, const void *cmd, int cmdsize
+     ASSERT32FLAT();
+     if (! CONFIG_USB_EHCI)
+         return -1;
+-    dprintf(5, "ehci_control %p\n", p);
++    dprintf(5, "ehci_control %p (dir=%d cmd=%d data=%d)\n"
++            , p, dir, cmdsize, datasize);
+     if (datasize > 4*4096 || cmdsize > 4*4096) {
+         // XXX - should support larger sizes.
+         warn_noalloc();
+         return -1;
+     }
+     struct ehci_pipe *pipe = container_of(p, struct ehci_pipe, pipe);
+-    struct usb_ehci_s *cntl = container_of(
+-        pipe->pipe.cntl, struct usb_ehci_s, usb);
+ 
+     u16 maxpacket = pipe->pipe.maxpacket;
+     int speed = pipe->pipe.speed;
+@@ -513,14 +535,12 @@ ehci_control(struct usb_pipe *p, int dir, const void *cmd, int cmdsize
+     // Transfer data
+     barrier();
+     pipe->qh.qtd_next = (u32)tds;
+-    barrier();
+-    pipe->qh.token = 0;
+-    int ret = ehci_wait_qh(cntl, &pipe->qh);
+-    pipe->qh.token = QTD_STS_HALT;
+-    if (ret) {
+-        pipe->qh.qtd_next = pipe->qh.alt_next = EHCI_PTR_TERM;
+-        // XXX - halt qh?
+-        ehci_waittick(cntl);
++    int i, ret=0;
++    for (i=0; i<3; i++) {
++        struct ehci_qtd *td = &tds[i];
++        ret = ehci_wait_td(pipe, td, 500);
++        if (ret)
++            break;
+     }
+     free(tds);
+     return ret;
+@@ -545,7 +565,6 @@ ehci_alloc_bulk_pipe(struct usb_pipe *dummy)
+     memset(pipe, 0, sizeof(*pipe));
+     memcpy(&pipe->pipe, dummy, sizeof(pipe->pipe));
+     pipe->qh.qtd_next = pipe->qh.alt_next = EHCI_PTR_TERM;
+-    pipe->qh.token = QTD_STS_HALT;
+ 
+     // Add queue head to controller list.
+     struct ehci_qh *async_qh = cntl->async_qh;
+@@ -555,28 +574,6 @@ ehci_alloc_bulk_pipe(struct usb_pipe *dummy)
+     return &pipe->pipe;
+ }
+ 
+-static int
+-ehci_wait_td(struct ehci_qtd *td)
+-{
+-    u64 end = calc_future_tsc(5000); // XXX - lookup real time.
+-    u32 status;
+-    for (;;) {
+-        status = td->token;
+-        if (!(status & QTD_STS_ACTIVE))
+-            break;
+-        if (check_tsc(end)) {
+-            warn_timeout();
+-            return -1;
+-        }
+-        yield();
+-    }
+-    if (status & QTD_STS_HALT) {
+-        dprintf(1, "ehci_wait_td error - status=%x\n", status);
+-        return -2;
+-    }
+-    return 0;
+-}
+-
+ #define STACKQTDS 4
+ 
+ int
+@@ -607,15 +604,13 @@ ehci_send_bulk(struct usb_pipe *p, int dir, void *data, int datasize)
+                    | (GET_FLATPTR(pipe->pipe.tt_devaddr) << QH_HUBADDR_SHIFT)));
+     barrier();
+     SET_FLATPTR(pipe->qh.qtd_next, (u32)MAKE_FLATPTR(GET_SEG(SS), tds));
+-    barrier();
+-    SET_FLATPTR(pipe->qh.token, GET_FLATPTR(pipe->qh.token) & QTD_TOGGLE);
+ 
+     int tdpos = 0;
+     while (datasize) {
+         struct ehci_qtd *td = &tds[tdpos++ % STACKQTDS];
+-        int ret = ehci_wait_td(td);
++        int ret = ehci_wait_td(pipe, td, 5000);
+         if (ret)
+-            goto fail;
++            return -1;
+ 
+         struct ehci_qtd *nexttd_fl = MAKE_FLATPTR(GET_SEG(SS)
+                                                  , &tds[tdpos % STACKQTDS]);
+@@ -633,21 +628,12 @@ ehci_send_bulk(struct usb_pipe *p, int dir, void *data, int datasize)
+     int i;
+     for (i=0; i<STACKQTDS; i++) {
+         struct ehci_qtd *td = &tds[tdpos++ % STACKQTDS];
+-        int ret = ehci_wait_td(td);
++        int ret = ehci_wait_td(pipe, td, 5000);
+         if (ret)
+-            goto fail;
++            return -1;
+     }
+ 
+     return 0;
+-fail:
+-    dprintf(1, "ehci_send_bulk failed\n");
+-    SET_FLATPTR(pipe->qh.qtd_next, EHCI_PTR_TERM);
+-    SET_FLATPTR(pipe->qh.alt_next, EHCI_PTR_TERM);
+-    // XXX - halt qh?
+-    struct usb_ehci_s *cntl = container_of(
+-        GET_FLATPTR(pipe->pipe.cntl), struct usb_ehci_s, usb);
+-    ehci_waittick(cntl);
+-    return -1;
+ }
+ 
+ struct usb_pipe *
+diff --git a/src/usb-msc.c b/src/usb-msc.c
+index 13ef93e..4a09972 100644
+--- a/src/usb-msc.c
++++ b/src/usb-msc.c
+@@ -46,6 +46,17 @@ struct csw_s {
+     u8 bCSWStatus;
+ } PACKED;
+ 
++static int
++usb_msc_send(struct usbdrive_s *udrive_g, int dir, void *buf, u32 bytes)
++{
++    struct usb_pipe *pipe;
++    if (dir == USB_DIR_OUT)
++        pipe = GET_GLOBAL(udrive_g->bulkout);
++    else
++        pipe = GET_GLOBAL(udrive_g->bulkin);
++    return usb_send_bulk(pipe, dir, buf, bytes);
++}
++
+ // Low-level usb command transmit function.
+ int
+ usb_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize)
+@@ -57,35 +68,35 @@ usb_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize)
+             , op->drive_g, 0, op->count, blocksize, op->buf_fl);
+     struct usbdrive_s *udrive_g = container_of(
+         op->drive_g, struct usbdrive_s, drive);
+-    struct usb_pipe *bulkin = GET_GLOBAL(udrive_g->bulkin);
+-    struct usb_pipe *bulkout = GET_GLOBAL(udrive_g->bulkout);
+ 
+     // Setup command block wrapper.
+     u32 bytes = blocksize * op->count;
+     struct cbw_s cbw;
+     memset(&cbw, 0, sizeof(cbw));
++    memcpy(cbw.CBWCB, cdbcmd, USB_CDB_SIZE);
+     cbw.dCBWSignature = CBW_SIGNATURE;
+     cbw.dCBWTag = 999; // XXX
+     cbw.dCBWDataTransferLength = bytes;
+-    cbw.bmCBWFlags = USB_DIR_IN; // XXX
++    cbw.bmCBWFlags = (cbw.CBWCB[0] == CDB_CMD_WRITE_10) ? USB_DIR_OUT : USB_DIR_IN;
+     cbw.bCBWLUN = 0; // XXX
+     cbw.bCBWCBLength = USB_CDB_SIZE;
+-    memcpy(cbw.CBWCB, cdbcmd, USB_CDB_SIZE);
+ 
+     // Transfer cbw to device.
+-    int ret = usb_send_bulk(bulkout, USB_DIR_OUT
++    int ret = usb_msc_send(udrive_g, USB_DIR_OUT
+                             , MAKE_FLATPTR(GET_SEG(SS), &cbw), sizeof(cbw));
+     if (ret)
+         goto fail;
+ 
+-    // Transfer data from device.
+-    ret = usb_send_bulk(bulkin, USB_DIR_IN, op->buf_fl, bytes);
+-    if (ret)
+-        goto fail;
++    // Transfer data to/from device.
++    if (bytes) {
++        ret = usb_msc_send(udrive_g, cbw.bmCBWFlags, op->buf_fl, bytes);
++        if (ret)
++            goto fail;
++    }
+ 
+     // Transfer csw info.
+     struct csw_s csw;
+-    ret = usb_send_bulk(bulkin, USB_DIR_IN
++    ret = usb_msc_send(udrive_g, USB_DIR_IN
+                         , MAKE_FLATPTR(GET_SEG(SS), &csw), sizeof(csw));
+     if (ret)
+         goto fail;
+@@ -95,7 +106,8 @@ usb_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize)
+     if (csw.bCSWStatus == 2)
+         goto fail;
+ 
+-    op->count -= csw.dCSWDataResidue / blocksize;
++    if (blocksize)
++        op->count -= csw.dCSWDataResidue / blocksize;
+     return DISK_RET_EBADTRACK;
+ 
+ fail:
+@@ -107,75 +119,33 @@ fail:
+ 
+ 
+ /****************************************************************
+- * Drive ops
+- ****************************************************************/
+-
+-// 16bit command demuxer for ATAPI cdroms.
+-int
+-process_usb_op(struct disk_op_s *op)
+-{
+-    if (!CONFIG_USB_MSC)
+-        return 0;
+-    switch (op->command) {
+-    case CMD_READ:
+-        return cdb_read(op);
+-    case CMD_FORMAT:
+-    case CMD_WRITE:
+-        return DISK_RET_EWRITEPROTECT;
+-    case CMD_RESET:
+-    case CMD_ISREADY:
+-    case CMD_VERIFY:
+-    case CMD_SEEK:
+-        return DISK_RET_SUCCESS;
+-    default:
+-        op->count = 0;
+-        return DISK_RET_EPARAM;
+-    }
+-}
+-
+-
+-/****************************************************************
+  * Setup
+  ****************************************************************/
+ 
+ static int
+-setup_drive_cdrom(struct disk_op_s *op, char *desc)
++setup_drive_cdrom(struct drive_s *drive, char *desc)
+ {
+-    op->drive_g->blksize = CDROM_SECTOR_SIZE;
+-    op->drive_g->sectors = (u64)-1;
++    drive->sectors = (u64)-1;
+     struct usb_pipe *pipe = container_of(
+-        op->drive_g, struct usbdrive_s, drive)->bulkout;
++        drive, struct usbdrive_s, drive)->bulkout;
+     int prio = bootprio_find_usb(pipe->cntl->pci, pipe->path);
+-    boot_add_cd(op->drive_g, desc, prio);
++    boot_add_cd(drive, desc, prio);
+     return 0;
+ }
+ 
+ static int
+-setup_drive_hd(struct disk_op_s *op, char *desc)
++setup_drive_hd(struct drive_s *drive, char *desc)
+ {
+-    struct cdbres_read_capacity info;
+-    int ret = cdb_read_capacity(op, &info);
+-    if (ret)
+-        return ret;
+-    // XXX - retry for some timeout?
+-
+-    u32 blksize = ntohl(info.blksize), sectors = ntohl(info.sectors);
+-    if (blksize != DISK_SECTOR_SIZE) {
+-        if (blksize == CDROM_SECTOR_SIZE)
+-            return setup_drive_cdrom(op, desc);
+-        dprintf(1, "Unsupported USB MSC block size %d\n", blksize);
++    if (drive->blksize != DISK_SECTOR_SIZE) {
++        dprintf(1, "Unsupported USB MSC block size %d\n", drive->blksize);
+         return -1;
+     }
+-    op->drive_g->blksize = blksize;
+-    op->drive_g->sectors = sectors;
+-    dprintf(1, "USB MSC blksize=%d sectors=%d\n", blksize, sectors);
+ 
+     // Register with bcv system.
+     struct usb_pipe *pipe = container_of(
+-        op->drive_g, struct usbdrive_s, drive)->bulkout;
++        drive, struct usbdrive_s, drive)->bulkout;
+     int prio = bootprio_find_usb(pipe->cntl->pci, pipe->path);
+-    boot_add_hd(op->drive_g, desc, prio);
+-
++    boot_add_hd(drive, desc, prio);
+     return 0;
+ }
+ 
+@@ -218,37 +188,17 @@ usb_msc_init(struct usb_pipe *pipe
+     if (!udrive_g->bulkin || !udrive_g->bulkout)
+         goto fail;
+ 
+-    // Validate drive and find block size and sector count.
+-    struct disk_op_s dop;
+-    memset(&dop, 0, sizeof(dop));
+-    dop.drive_g = &udrive_g->drive;
+-    struct cdbres_inquiry data;
+-    int ret = cdb_get_inquiry(&dop, &data);
++    int ret, pdt;
++    char *desc = NULL;
++    ret = scsi_init_drive(&udrive_g->drive, "USB MSC", &pdt, &desc);
+     if (ret)
+         goto fail;
+-    char vendor[sizeof(data.vendor)+1], product[sizeof(data.product)+1];
+-    char rev[sizeof(data.rev)+1];
+-    strtcpy(vendor, data.vendor, sizeof(vendor));
+-    nullTrailingSpace(vendor);
+-    strtcpy(product, data.product, sizeof(product));
+-    nullTrailingSpace(product);
+-    strtcpy(rev, data.rev, sizeof(rev));
+-    nullTrailingSpace(rev);
+-    int pdt = data.pdt & 0x1f;
+-    int removable = !!(data.removable & 0x80);
+-    dprintf(1, "USB MSC vendor='%s' product='%s' rev='%s' type=%d removable=%d\n"
+-            , vendor, product, rev, pdt, removable);
+-    udrive_g->drive.removable = removable;
+-
+-    if (pdt == USB_MSC_TYPE_CDROM) {
+-        char *desc = znprintf(MAXDESCSIZE, "DVD/CD [USB Drive %s %s %s]"
+-                              , vendor, product, rev);
+-        ret = setup_drive_cdrom(&dop, desc);
+-    } else {
+-        char *desc = znprintf(MAXDESCSIZE, "USB Drive %s %s %s"
+-                              , vendor, product, rev);
+-        ret = setup_drive_hd(&dop, desc);
+-    }
++
++    if (pdt == SCSI_TYPE_CDROM)
++        ret = setup_drive_cdrom(&udrive_g->drive, desc);
++    else
++        ret = setup_drive_hd(&udrive_g->drive, desc);
++
+     if (ret)
+         goto fail;
+ 
+diff --git a/src/usb-msc.h b/src/usb-msc.h
+index 71adb20..a8686a3 100644
+--- a/src/usb-msc.h
++++ b/src/usb-msc.h
+@@ -21,7 +21,4 @@ int process_usb_op(struct disk_op_s *op);
+ 
+ #define US_PR_BULK         0x50
+ 
+-#define USB_MSC_TYPE_DISK  0x00
+-#define USB_MSC_TYPE_CDROM 0x05
+-
+ #endif // ush-msc.h
+diff --git a/src/usb-uhci.c b/src/usb-uhci.c
+index f3680d3..a78dbca 100644
+--- a/src/usb-uhci.c
++++ b/src/usb-uhci.c
+@@ -212,26 +212,13 @@ uhci_init(struct pci_device *pci, int busid)
+  * End point communication
+  ****************************************************************/
+ 
+-static int
+-wait_qh(struct usb_uhci_s *cntl, struct uhci_qh *qh)
+-{
+-    // XXX - 500ms just a guess
+-    u64 end = calc_future_tsc(500);
+-    for (;;) {
+-        if (qh->element & UHCI_PTR_TERM)
+-            return 0;
+-        if (check_tsc(end)) {
+-            warn_timeout();
+-            struct uhci_td *td = (void*)(qh->element & ~UHCI_PTR_BITS);
+-            dprintf(1, "Timeout on wait_qh %p (td=%p s=%x c=%x/%x)\n"
+-                    , qh, td, td->status
+-                    , inw(cntl->iobase + USBCMD)
+-                    , inw(cntl->iobase + USBSTS));
+-            return -1;
+-        }
+-        yield();
+-    }
+-}
++struct uhci_pipe {
++    struct uhci_qh qh;
++    struct uhci_td *next_td;
++    struct usb_pipe pipe;
++    u16 iobase;
++    u8 toggle;
++};
+ 
+ // Wait for next USB frame to start - for ensuring safe memory release.
+ static void
+@@ -251,13 +238,29 @@ uhci_waittick(u16 iobase)
+     }
+ }
+ 
+-struct uhci_pipe {
+-    struct uhci_qh qh;
+-    struct uhci_td *next_td;
+-    struct usb_pipe pipe;
+-    u16 iobase;
+-    u8 toggle;
+-};
++static int
++wait_pipe(struct uhci_pipe *pipe, int timeout)
++{
++    u64 end = calc_future_tsc(timeout);
++    for (;;) {
++        u32 el_link = GET_FLATPTR(pipe->qh.element);
++        if (el_link & UHCI_PTR_TERM)
++            return 0;
++        if (check_tsc(end)) {
++            warn_timeout();
++            u16 iobase = GET_FLATPTR(pipe->iobase);
++            struct uhci_td *td = (void*)(el_link & ~UHCI_PTR_BITS);
++            dprintf(1, "Timeout on wait_pipe %p (td=%p s=%x c=%x/%x)\n"
++                    , pipe, (void*)el_link, GET_FLATPTR(td->status)
++                    , inw(iobase + USBCMD)
++                    , inw(iobase + USBSTS));
++            SET_FLATPTR(pipe->qh.element, UHCI_PTR_TERM);
++            uhci_waittick(iobase);
++            return -1;
++        }
++        yield();
++    }
++}
+ 
+ void
+ uhci_free_pipe(struct usb_pipe *p)
+@@ -331,8 +334,6 @@ uhci_control(struct usb_pipe *p, int dir, const void *cmd, int cmdsize
+         return -1;
+     dprintf(5, "uhci_control %p\n", p);
+     struct uhci_pipe *pipe = container_of(p, struct uhci_pipe, pipe);
+-    struct usb_uhci_s *cntl = container_of(
+-        pipe->pipe.cntl, struct usb_uhci_s, usb);
+ 
+     int maxpacket = pipe->pipe.maxpacket;
+     int lowspeed = pipe->pipe.speed;
+@@ -376,11 +377,7 @@ uhci_control(struct usb_pipe *p, int dir, const void *cmd, int cmdsize
+     // Transfer data
+     barrier();
+     pipe->qh.element = (u32)&tds[0];
+-    int ret = wait_qh(cntl, &pipe->qh);
+-    if (ret) {
+-        pipe->qh.element = UHCI_PTR_TERM;
+-        uhci_waittick(pipe->iobase);
+-    }
++    int ret = wait_pipe(pipe, 500);
+     free(tds);
+     return ret;
+ }
+@@ -487,16 +484,8 @@ uhci_send_bulk(struct usb_pipe *p, int dir, void *data, int datasize)
+         data += transfer;
+         datasize -= transfer;
+     }
+-    int i;
+-    for (i=0; i<STACKTDS; i++) {
+-        struct uhci_td *td = &tds[tdpos++ % STACKTDS];
+-        int ret = wait_td(td);
+-        if (ret)
+-            goto fail;
+-    }
+-
+     SET_FLATPTR(pipe->toggle, !!toggle);
+-    return 0;
++    return wait_pipe(pipe, 5000);
+ fail:
+     dprintf(1, "uhci_send_bulk failed\n");
+     SET_FLATPTR(pipe->qh.element, UHCI_PTR_TERM);
+diff --git a/src/virtio-blk.c b/src/virtio-blk.c
+index b1274fc..b869189 100644
+--- a/src/virtio-blk.c
++++ b/src/virtio-blk.c
+@@ -75,7 +75,7 @@ virtio_blk_op(struct disk_op_s *op, int write)
+ }
+ 
+ int
+-process_virtio_op(struct disk_op_s *op)
++process_virtio_blk_op(struct disk_op_s *op)
+ {
+     if (! CONFIG_VIRTIO_BLK || CONFIG_COREBOOT)
+         return 0;
+@@ -103,27 +103,17 @@ init_virtio_blk(struct pci_device *pci)
+     dprintf(1, "found virtio-blk at %x:%x\n", pci_bdf_to_bus(bdf),
+             pci_bdf_to_dev(bdf));
+     struct virtiodrive_s *vdrive_g = malloc_fseg(sizeof(*vdrive_g));
+-    struct vring_virtqueue *vq = memalign_low(PAGE_SIZE, sizeof(*vq));
+-    if (!vdrive_g || !vq) {
++    if (!vdrive_g) {
+         warn_noalloc();
+-        goto fail;
++        return;
+     }
+     memset(vdrive_g, 0, sizeof(*vdrive_g));
+-    memset(vq, 0, sizeof(*vq));
+-    vdrive_g->drive.type = DTYPE_VIRTIO;
++    vdrive_g->drive.type = DTYPE_VIRTIO_BLK;
+     vdrive_g->drive.cntl_id = bdf;
+-    vdrive_g->vq = vq;
+-
+-    u16 ioaddr = pci_config_readl(bdf, PCI_BASE_ADDRESS_0) &
+-        PCI_BASE_ADDRESS_IO_MASK;
+ 
++    u16 ioaddr = vp_init_simple(bdf);
+     vdrive_g->ioaddr = ioaddr;
+-
+-    vp_reset(ioaddr);
+-    vp_set_status(ioaddr, VIRTIO_CONFIG_S_ACKNOWLEDGE |
+-                  VIRTIO_CONFIG_S_DRIVER );
+-
+-    if (vp_find_vq(ioaddr, 0, vdrive_g->vq) < 0 ) {
++    if (vp_find_vq(ioaddr, 0, &vdrive_g->vq) < 0 ) {
+         dprintf(1, "fail to find vq for virtio-blk %x:%x\n",
+                 pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf));
+         goto fail;
+@@ -161,8 +151,8 @@ init_virtio_blk(struct pci_device *pci)
+     return;
+ 
+ fail:
++    free(vdrive_g->vq);
+     free(vdrive_g);
+-    free(vq);
+ }
+ 
+ void
+diff --git a/src/virtio-blk.h b/src/virtio-blk.h
+index 7243704..0825f09 100644
+--- a/src/virtio-blk.h
++++ b/src/virtio-blk.h
+@@ -37,7 +37,7 @@ struct virtio_blk_outhdr {
+ #define VIRTIO_BLK_S_UNSUPP	2
+ 
+ struct disk_op_s;
+-int process_virtio_op(struct disk_op_s *op);
++int process_virtio_blk_op(struct disk_op_s *op);
+ void virtio_blk_setup(void);
+ 
+ #endif /* _VIRTIO_BLK_H */
+diff --git a/src/virtio-pci.c b/src/virtio-pci.c
+index db19e97..7e0c1a5 100644
+--- a/src/virtio-pci.c
++++ b/src/virtio-pci.c
+@@ -21,12 +21,18 @@
+ #include "util.h" // dprintf
+ 
+ int vp_find_vq(unsigned int ioaddr, int queue_index,
+-               struct vring_virtqueue *vq)
++               struct vring_virtqueue **p_vq)
+ {
+-   struct vring * vr = &vq->vring;
+    u16 num;
+ 
+    ASSERT32FLAT();
++   struct vring_virtqueue *vq = *p_vq = memalign_low(PAGE_SIZE, sizeof(*vq));
++   if (!vq) {
++       warn_noalloc();
++       goto fail;
++   }
++   memset(vq, 0, sizeof(*vq));
++
+    /* select the queue */
+ 
+    outw(queue_index, ioaddr + VIRTIO_PCI_QUEUE_SEL);
+@@ -36,25 +42,26 @@ int vp_find_vq(unsigned int ioaddr, int queue_index,
+    num = inw(ioaddr + VIRTIO_PCI_QUEUE_NUM);
+    if (!num) {
+        dprintf(1, "ERROR: queue size is 0\n");
+-       return -1;
++       goto fail;
+    }
+ 
+    if (num > MAX_QUEUE_NUM) {
+        dprintf(1, "ERROR: queue size %d > %d\n", num, MAX_QUEUE_NUM);
+-       return -1;
++       goto fail;
+    }
+ 
+    /* check if the queue is already active */
+ 
+    if (inl(ioaddr + VIRTIO_PCI_QUEUE_PFN)) {
+        dprintf(1, "ERROR: queue already active\n");
+-       return -1;
++       goto fail;
+    }
+ 
+    vq->queue_index = queue_index;
+ 
+    /* initialize the queue */
+ 
++   struct vring * vr = &vq->vring;
+    vring_init(vr, num, (unsigned char*)&vq->queue);
+ 
+    /* activate the queue
+@@ -66,4 +73,20 @@ int vp_find_vq(unsigned int ioaddr, int queue_index,
+         ioaddr + VIRTIO_PCI_QUEUE_PFN);
+ 
+    return num;
++
++fail:
++   free(vq);
++   *p_vq = NULL;
++   return -1;
++}
++
++u16 vp_init_simple(u16 bdf)
++{
++    u16 ioaddr = pci_config_readl(bdf, PCI_BASE_ADDRESS_0) &
++        PCI_BASE_ADDRESS_IO_MASK;
++
++    vp_reset(ioaddr);
++    vp_set_status(ioaddr, VIRTIO_CONFIG_S_ACKNOWLEDGE |
++                  VIRTIO_CONFIG_S_DRIVER );
++    return ioaddr;
+ }
+diff --git a/src/virtio-pci.h b/src/virtio-pci.h
+index d21d5a5..e1d972d 100644
+--- a/src/virtio-pci.h
++++ b/src/virtio-pci.h
+@@ -99,6 +99,7 @@ static inline void vp_del_vq(unsigned int ioaddr, int queue_index)
+ }
+ 
+ struct vring_virtqueue;
++u16 vp_init_simple(u16 bdf);
+ int vp_find_vq(unsigned int ioaddr, int queue_index,
+-               struct vring_virtqueue *vq);
++               struct vring_virtqueue **p_vq);
+ #endif /* _VIRTIO_PCI_H_ */
+diff --git a/src/virtio-scsi.c b/src/virtio-scsi.c
+new file mode 100644
+index 0000000..2f6c48a
+--- /dev/null
++++ b/src/virtio-scsi.c
+@@ -0,0 +1,211 @@
++// Virtio SCSI boot support.
++//
++// Copyright (C) 2011 Red Hat Inc.
++//
++// Authors:
++//  Paolo Bonzini <pbonzini at redhat.com>
++//
++// This file may be distributed under the terms of the GNU LGPLv3 license.
++
++#include "util.h" // dprintf
++#include "pci.h" // foreachpci
++#include "config.h" // CONFIG_*
++#include "biosvar.h" // GET_GLOBAL
++#include "pci_ids.h" // PCI_DEVICE_ID_VIRTIO_BLK
++#include "pci_regs.h" // PCI_VENDOR_ID
++#include "boot.h" // boot_add_hd
++#include "blockcmd.h" // CDB_CMD_WRITE_10, SCSI_TYPE_CDROM
++#include "virtio-pci.h"
++#include "virtio-ring.h"
++#include "virtio-scsi.h"
++#include "disk.h"
++
++struct virtio_lun_s {
++    struct drive_s drive;
++    struct pci_device *pci;
++    struct vring_virtqueue *vq;
++    u16 ioaddr;
++    u16 target;
++    u16 lun;
++};
++
++static int
++virtio_scsi_cmd(u16 ioaddr, struct vring_virtqueue *vq, struct disk_op_s *op,
++                void *cdbcmd, u16 target, u16 lun, u32 len)
++{
++    struct virtio_scsi_req_cmd req;
++    struct virtio_scsi_resp_cmd resp;
++    struct vring_list sg[3];
++
++    memset(&req, 0, sizeof(req));
++    req.lun[0] = 1;
++    req.lun[1] = target;
++    req.lun[2] = (lun >> 8) | 0x40;
++    req.lun[3] = (lun & 0xff);
++    memcpy(req.cdb, cdbcmd, 16);
++
++    int datain = (req.cdb[0] != CDB_CMD_WRITE_10);
++    int data_idx = (datain ? 2 : 1);
++    int out_num = (datain ? 1 : 2);
++    int in_num = (len ? 3 : 2) - out_num;
++
++    sg[0].addr   = MAKE_FLATPTR(GET_SEG(SS), &req);
++    sg[0].length = sizeof(req);
++
++    sg[out_num].addr   = MAKE_FLATPTR(GET_SEG(SS), &resp);
++    sg[out_num].length = sizeof(resp);
++
++    sg[data_idx].addr   = op->buf_fl;
++    sg[data_idx].length = len;
++
++    /* Add to virtqueue and kick host */
++    vring_add_buf(vq, sg, out_num, in_num, 0, 0);
++    vring_kick(ioaddr, vq, 1);
++
++    /* Wait for reply */
++    while (!vring_more_used(vq))
++        usleep(5);
++
++    /* Reclaim virtqueue element */
++    vring_get_buf(vq, NULL);
++
++    /* Clear interrupt status register.  Avoid leaving interrupts stuck if
++     * VRING_AVAIL_F_NO_INTERRUPT was ignored and interrupts were raised.
++     */
++    vp_get_isr(ioaddr);
++
++    if (resp.response == VIRTIO_SCSI_S_OK && resp.status == 0) {
++        return DISK_RET_SUCCESS;
++    }
++    return DISK_RET_EBADTRACK;
++}
++
++int
++virtio_scsi_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize)
++{
++    struct virtio_lun_s *vlun =
++        container_of(op->drive_g, struct virtio_lun_s, drive);
++
++    return virtio_scsi_cmd(GET_GLOBAL(vlun->ioaddr),
++                           GET_GLOBAL(vlun->vq), op, cdbcmd,
++                           GET_GLOBAL(vlun->target), GET_GLOBAL(vlun->lun),
++                           blocksize * op->count);
++}
++
++static int
++setup_lun_cdrom(struct virtio_lun_s *vlun, char *desc)
++{
++    int prio = bootprio_find_scsi_device(vlun->pci, vlun->target, vlun->lun);
++    boot_add_cd(&vlun->drive, desc, prio);
++    return 0;
++}
++
++static int
++setup_lun_hd(struct virtio_lun_s *vlun, char *desc)
++{
++    if (vlun->drive.blksize != DISK_SECTOR_SIZE) {
++        dprintf(1, "Unsupported block size %d\n", vlun->drive.blksize);
++        return -1;
++    }
++
++    // Register with bcv system.
++    int prio = bootprio_find_scsi_device(vlun->pci, vlun->target, vlun->lun);
++    boot_add_hd(&vlun->drive, desc, prio);
++
++    return 0;
++}
++
++static int
++virtio_scsi_add_lun(struct pci_device *pci, u16 ioaddr,
++                    struct vring_virtqueue *vq, u16 target, u16 lun)
++{
++    struct virtio_lun_s *vlun = malloc_fseg(sizeof(*vlun));
++    if (!vlun) {
++        warn_noalloc();
++        return -1;
++    }
++    memset(vlun, 0, sizeof(*vlun));
++    vlun->drive.type = DTYPE_VIRTIO_SCSI;
++    vlun->drive.cntl_id = pci->bdf;
++    vlun->pci = pci;
++    vlun->ioaddr = ioaddr;
++    vlun->vq = vq;
++    vlun->target = target;
++    vlun->lun = lun;
++
++    int pdt, ret;
++    char *desc = NULL;
++    ret = scsi_init_drive(&vlun->drive, "virtio-scsi", &pdt, &desc);
++    if (ret)
++        goto fail;
++
++    if (pdt == SCSI_TYPE_CDROM)
++        ret = setup_lun_cdrom(vlun, desc);
++    else
++        ret = setup_lun_hd(vlun, desc);
++    if (ret)
++        goto fail;
++    return ret;
++
++fail:
++    free(vlun);
++    return -1;
++}
++
++static int
++virtio_scsi_scan_target(struct pci_device *pci, u16 ioaddr,
++                        struct vring_virtqueue *vq, u16 target)
++{
++    /* TODO: send REPORT LUNS.  For now, only LUN 0 is recognized.  */
++    int ret = virtio_scsi_add_lun(pci, ioaddr, vq, target, 0);
++    return ret < 0 ? ret : 1;
++}
++
++static void
++init_virtio_scsi(struct pci_device *pci)
++{
++    u16 bdf = pci->bdf;
++    dprintf(1, "found virtio-scsi at %x:%x\n", pci_bdf_to_bus(bdf),
++            pci_bdf_to_dev(bdf));
++    struct vring_virtqueue *vq = NULL;
++    u16 ioaddr = vp_init_simple(bdf);
++    if (vp_find_vq(ioaddr, 2, &vq) < 0 ) {
++        if (vq) {
++            dprintf(1, "fail to find vq for virtio-scsi %x:%x\n",
++                    pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf));
++        }
++        goto fail;
++    }
++
++    int i, tot;
++    for (tot = 0, i = 0; i < 256; i++)
++        tot += virtio_scsi_scan_target(pci, ioaddr, vq, i);
++
++    if (!tot)
++        goto fail;
++
++    vp_set_status(ioaddr, VIRTIO_CONFIG_S_ACKNOWLEDGE |
++                  VIRTIO_CONFIG_S_DRIVER | VIRTIO_CONFIG_S_DRIVER_OK);
++    return;
++
++fail:
++    free(vq);
++}
++
++void
++virtio_scsi_setup(void)
++{
++    ASSERT32FLAT();
++    if (! CONFIG_VIRTIO_SCSI || CONFIG_COREBOOT)
++        return;
++
++    dprintf(3, "init virtio-scsi\n");
++
++    struct pci_device *pci;
++    foreachpci(pci) {
++        if (pci->vendor != PCI_VENDOR_ID_REDHAT_QUMRANET
++            || pci->device != PCI_DEVICE_ID_VIRTIO_SCSI)
++            continue;
++        init_virtio_scsi(pci);
++    }
++}
+diff --git a/src/virtio-scsi.h b/src/virtio-scsi.h
+new file mode 100644
+index 0000000..28d48b0
+--- /dev/null
++++ b/src/virtio-scsi.h
+@@ -0,0 +1,48 @@
++#ifndef _VIRTIO_SCSI_H
++#define _VIRTIO_SCSI_H
++
++#define VIRTIO_SCSI_CDB_SIZE      32
++#define VIRTIO_SCSI_SENSE_SIZE    96
++
++struct virtio_scsi_config
++{
++    u32 num_queues;
++    u32 seg_max;
++    u32 max_sectors;
++    u32 cmd_per_lun;
++    u32 event_info_size;
++    u32 sense_size;
++    u32 cdb_size;
++    u16 max_channel;
++    u16 max_target;
++    u32 max_lun;
++} __attribute__((packed));
++
++/* This is the first element of the "out" scatter-gather list. */
++struct virtio_scsi_req_cmd {
++    u8 lun[8];
++    u64 id;
++    u8 task_attr;
++    u8 prio;
++    u8 crn;
++    char cdb[VIRTIO_SCSI_CDB_SIZE];
++};
++
++/* This is the first element of the "in" scatter-gather list. */
++struct virtio_scsi_resp_cmd {
++    u32 sense_len;
++    u32 residual;
++    u16 status_qualifier;
++    u8 status;
++    u8 response;
++    u8 sense[VIRTIO_SCSI_SENSE_SIZE];
++};
++
++#define VIRTIO_SCSI_S_OK            0
++
++struct disk_op_s;
++int process_virtio_scsi_op(struct disk_op_s *op);
++int virtio_scsi_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize);
++void virtio_scsi_setup(void);
++
++#endif /* _VIRTIO_SCSI_H */
diff --git a/seabios.spec b/seabios.spec
index f94a07d..ad756cb 100644
--- a/seabios.spec
+++ b/seabios.spec
@@ -1,6 +1,6 @@
 Name:           seabios
-Version:        0.6.2
-Release:        4%{?dist}
+Version:        1.6.3
+Release:        1%{?dist}
 Summary:        Open-source legacy BIOS implementation
 
 Group:          Applications/Emulators
@@ -8,9 +8,9 @@ License:        LGPLv3
 URL:            http://www.coreboot.org/SeaBIOS
 Source0:        http://www.linuxtogo.org/~kevin/SeaBIOS/%{name}-%{version}.tar.gz
 
-Patch00: seabios-0.6.2-build.patch
-Patch01: seabios-0.6.2-fix-QXL.patch
-Patch02: seabios-do-not-advertise-S4-S3-in-DSDT.patch
+Patch00: seabios-usb_fix_boot_paths.patch
+Patch01: seabios-do-not-advertise-S4-S3-in-DSDT.patch
+Patch02: seabios-virtio-scsi.patch
 
 BuildRoot:      %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
 
@@ -82,6 +82,10 @@ rm -rf $RPM_BUILD_ROOT
 
 
 %changelog
+* Wed Feb 08 2012 Justin M. Forbes <jforbes at redhat.com> - 1.6.3-1
+- Update to 1.6.3 upstream
+- Add virtio-scsi
+
 * Sat Jan 14 2012 Fedora Release Engineering <rel-eng at lists.fedoraproject.org> - 0.6.2-4
 - Rebuilt for https://fedoraproject.org/wiki/Fedora_17_Mass_Rebuild
 
diff --git a/sources b/sources
index feab5a9..d98225b 100644
--- a/sources
+++ b/sources
@@ -1 +1 @@
-ec58d93b93fa29550164693f714c9320  seabios-0.6.2.tar.gz
+d7cd612ff34f9b910a63c2d73b25eef3  seabios-1.6.3.tar.gz


More information about the scm-commits mailing list