Index: qemu/block.c
===================================================================
--- qemu.orig/block.c	2008-04-19 07:03:43.000000000 +0000
+++ qemu/block.c	2008-04-19 19:20:50.000000000 +0000
@@ -1177,6 +1177,18 @@
     return ret;
 }
 
+BlockDriverAIOCB *bdrv_aio_readv(BlockDriverState *bs, int64_t sector_num,
+                                 IOVector *iovec,
+                                 BlockDriverCompletionFunc *cb, void *opaque)
+{
+    int ret;
+    static BlockDriverAIOCB dummy;
+
+    ret = bdrv_readv(bs, sector_num, iovec);
+    cb(opaque, ret);
+    return &dummy;
+}
+
 BlockDriverAIOCB *bdrv_aio_write(BlockDriverState *bs, int64_t sector_num,
                                  const uint8_t *buf, int nb_sectors,
                                  BlockDriverCompletionFunc *cb, void *opaque)
@@ -1203,6 +1215,18 @@
     return ret;
 }
 
+BlockDriverAIOCB *bdrv_aio_writev(BlockDriverState *bs, int64_t sector_num,
+                                  const IOVector *iovec,
+                                  BlockDriverCompletionFunc *cb, void *opaque)
+{
+    int ret;
+    static BlockDriverAIOCB dummy;
+
+    ret = bdrv_writev(bs, sector_num, iovec);
+    cb(opaque, ret);
+    return &dummy;
+}
+
 void bdrv_aio_cancel(BlockDriverAIOCB *acb)
 {
     BlockDriver *drv = acb->bs->drv;
Index: qemu/block.h
===================================================================
--- qemu.orig/block.h	2008-04-19 07:03:43.000000000 +0000
+++ qemu/block.h	2008-04-19 08:38:12.000000000 +0000
@@ -88,9 +88,15 @@
 BlockDriverAIOCB *bdrv_aio_read(BlockDriverState *bs, int64_t sector_num,
                                 uint8_t *buf, int nb_sectors,
                                 BlockDriverCompletionFunc *cb, void *opaque);
+BlockDriverAIOCB *bdrv_aio_readv(BlockDriverState *bs, int64_t sector_num,
+                                 IOVector *iovec,
+                                 BlockDriverCompletionFunc *cb, void *opaque);
 BlockDriverAIOCB *bdrv_aio_write(BlockDriverState *bs, int64_t sector_num,
                                  const uint8_t *buf, int nb_sectors,
                                  BlockDriverCompletionFunc *cb, void *opaque);
+BlockDriverAIOCB *bdrv_aio_writev(BlockDriverState *bs, int64_t sector_num,
+                                  const IOVector *iovec,
+                                  BlockDriverCompletionFunc *cb, void *opaque);
 void bdrv_aio_cancel(BlockDriverAIOCB *acb);
 
 void qemu_aio_init(void);
Index: qemu/hw/scsi-disk.c
===================================================================
--- qemu.orig/hw/scsi-disk.c	2008-04-19 09:08:36.000000000 +0000
+++ qemu/hw/scsi-disk.c	2008-04-19 16:36:28.000000000 +0000
@@ -166,7 +166,7 @@
 }
 
 /* Read more data from scsi device into buffer.  */
-static void scsi_read_data(SCSIDevice *d, uint32_t tag)
+static inline SCSIRequest *scsi_read_data_check(SCSIDevice *d, uint32_t tag)
 {
     SCSIDeviceState *s = d->state;
     SCSIRequest *r;
@@ -177,18 +177,18 @@
         BADF("Bad read tag 0x%x\n", tag);
         /* ??? This is the wrong error.  */
         scsi_command_complete(r, SENSE_HARDWARE_ERROR);
-        return;
+        return NULL;
     }
     if (r->sector_count == (uint32_t)-1) {
         DPRINTF("Read buf_len=%d\n", r->buf_len);
         r->sector_count = 0;
         s->completion(s->opaque, SCSI_REASON_DATA, r->tag, r->buf_len);
-        return;
+        return NULL;
     }
     DPRINTF("Read sector_count=%d\n", r->sector_count);
     if (r->sector_count == 0) {
         scsi_command_complete(r, SENSE_NO_SENSE);
-        return;
+        return NULL;
     }
 
     n = r->sector_count;
@@ -196,6 +196,21 @@
         n = SCSI_DMA_BUF_SIZE / 512;
 
     r->buf_len = n * 512;
+
+    return r;
+}
+
+static void scsi_read_data(SCSIDevice *d, uint32_t tag)
+{
+    SCSIDeviceState *s = d->state;
+    SCSIRequest *r;
+    uint32_t n;
+
+    r = scsi_read_data_check(d, tag);
+    if (!r)
+        return;
+
+    n = r->buf_len / 512;
     r->aiocb = bdrv_aio_read(s->bdrv, r->sector, r->dma_buf, n,
                              scsi_read_complete, r);
     if (r->aiocb == NULL)
@@ -204,6 +219,25 @@
     r->sector_count -= n;
 }
 
+static void scsi_readv(SCSIDevice *d, uint32_t tag, IOVector *iov)
+{
+    SCSIDeviceState *s = d->state;
+    SCSIRequest *r;
+    uint32_t n;
+
+    r = scsi_read_data_check(d, tag);
+    if (!r)
+        return;
+
+    n = r->buf_len / 512;
+    r->aiocb = bdrv_aio_readv(s->bdrv, r->sector, iov,
+                             scsi_read_complete, r);
+    if (r->aiocb == NULL)
+        scsi_command_complete(r, SENSE_HARDWARE_ERROR);
+    r->sector += n;
+    r->sector_count -= n;
+}
+
 static void scsi_write_complete(void * opaque, int ret)
 {
     SCSIRequest *r = (SCSIRequest *)opaque;
@@ -262,6 +296,37 @@
     return 0;
 }
 
+static int scsi_writev(SCSIDevice *d, uint32_t tag, IOVector *iov)
+{
+    SCSIDeviceState *s = d->state;
+    SCSIRequest *r;
+    size_t n;
+
+    DPRINTF("Write data tag=0x%x\n", tag);
+    r = scsi_find_request(s, tag);
+    if (!r) {
+        BADF("Bad write tag 0x%x\n", tag);
+        scsi_command_complete(r, SENSE_HARDWARE_ERROR);
+        return 1;
+    }
+    if (r->aiocb)
+        BADF("Data transfer already in progress\n");
+    n = iovector_size(iov);
+    if (n) {
+        r->aiocb = bdrv_aio_writev(s->bdrv, r->sector, iov,
+                                   scsi_write_complete, r);
+        if (r->aiocb == NULL)
+            scsi_command_complete(r, SENSE_HARDWARE_ERROR);
+        r->sector += n;
+        r->sector_count -= n;
+    } else {
+        /* Invoke completion routine to fetch data from host.  */
+        scsi_write_complete(r, 0);
+    }
+
+    return 0;
+}
+
 /* Return a pointer to the data buffer.  */
 static uint8_t *scsi_get_buf(SCSIDevice *d, uint32_t tag)
 {
@@ -732,7 +797,9 @@
     d->destroy = scsi_destroy;
     d->send_command = scsi_send_command;
     d->read_data = scsi_read_data;
+    d->readv = scsi_readv;
     d->write_data = scsi_write_data;
+    d->writev = scsi_writev;
     d->cancel_io = scsi_cancel_io;
     d->get_buf = scsi_get_buf;
 
Index: qemu/hw/scsi-disk.h
===================================================================
--- qemu.orig/hw/scsi-disk.h	2008-04-19 09:08:38.000000000 +0000
+++ qemu/hw/scsi-disk.h	2008-04-19 16:34:47.000000000 +0000
@@ -1,6 +1,8 @@
 #ifndef SCSI_DISK_H
 #define SCSI_DISK_H
 
+#include "iovector.h"
+
 /* scsi-disk.c */
 enum scsi_reason {
     SCSI_REASON_DONE, /* Command complete.  */
@@ -22,6 +24,8 @@
     int (*write_data)(SCSIDevice *s, uint32_t tag);
     void (*cancel_io)(SCSIDevice *s, uint32_t tag);
     uint8_t *(*get_buf)(SCSIDevice *s, uint32_t tag);
+    void (*readv)(SCSIDevice *s, uint32_t tag, IOVector *iov);
+    int (*writev)(SCSIDevice *s, uint32_t tag, IOVector *iov);
 };
 
 SCSIDevice *scsi_disk_init(BlockDriverState *bdrv, int tcq,
Index: qemu/hw/esp.c
===================================================================
--- qemu.orig/hw/esp.c	2008-04-19 16:37:11.000000000 +0000
+++ qemu/hw/esp.c	2008-04-19 19:17:29.000000000 +0000
@@ -27,7 +27,7 @@
 #include "scsi.h"
 
 /* debug ESP card */
-//#define DEBUG_ESP
+#define DEBUG_ESP
 
 /*
  * On Sparc32, this is the ESP (NCR53C90) part of chip STP2000 (Master I/O),
@@ -181,6 +181,22 @@
     return dmalen;
 }
 
+static void esp_iov_dma_read(void *opaque, uint64_t addr,
+                             void *data, size_t len)
+{
+    ESPState *s = (ESPState *)opaque;
+
+    s->dma_memory_read(s->dma_opaque, data, len);
+}
+
+static void esp_iov_dma_write(void *opaque, uint64_t addr,
+                              const void *data, size_t len)
+{
+    ESPState *s = (ESPState *)opaque;
+
+    s->dma_memory_write(s->dma_opaque, (void *)data, len);
+}
+
 static void do_cmd(ESPState *s, uint8_t *buf)
 {
     int32_t datalen;
@@ -191,9 +207,25 @@
     datalen = s->current_dev->send_command(s->current_dev, 0, &buf[1], lun);
     s->ti_size = datalen;
     if (datalen != 0) {
+        IOVector *iov;
+
         s->rregs[ESP_RSTAT] = STAT_IN | STAT_TC;
         s->dma_left = 0;
         s->dma_counter = 0;
+#if 1
+        iov = iovector_new(1, esp_iov_dma_read, esp_iov_dma_write,
+                           s->dma_opaque);
+        iov->sg[0].base = 0;
+        if (datalen > 0) {
+            iov->sg[0].len = datalen;
+            s->rregs[ESP_RSTAT] |= STAT_DI;
+            s->current_dev->readv(s->current_dev, 0, iov);
+        } else {
+            iov->sg[0].len = -datalen;
+            s->rregs[ESP_RSTAT] |= STAT_DO;
+            s->current_dev->writev(s->current_dev, 0, iov);
+        }
+#else
         if (datalen > 0) {
             s->rregs[ESP_RSTAT] |= STAT_DI;
             s->current_dev->read_data(s->current_dev, 0);
@@ -201,6 +233,7 @@
             s->rregs[ESP_RSTAT] |= STAT_DO;
             s->current_dev->write_data(s->current_dev, 0);
         }
+#endif
     }
     s->rregs[ESP_RINTR] = INTR_BS | INTR_FC;
     s->rregs[ESP_RSEQ] = SEQ_CD;
@@ -264,6 +297,7 @@
 {
     uint32_t len;
     int to_device;
+    IOVector *iov;
 
     to_device = (s->ti_size < 0);
     len = s->dma_left;
@@ -276,6 +310,23 @@
         do_cmd(s, s->cmdbuf);
         return;
     }
+#if 1
+    iov = iovector_new(1, esp_iov_dma_read, esp_iov_dma_write,
+                       s->dma_opaque);
+    iov->sg[0].base = 0;
+    iov->sg[0].len = len;
+    s->dma_left -= len;
+    if (to_device) {
+        s->current_dev->writev(s->current_dev, 0, iov);
+        s->ti_size += len;
+    } else {
+        s->current_dev->readv(s->current_dev, 0, iov);
+        s->ti_size -= len;
+        if (s->dma_left == 0 && s->ti_size > 0) {
+            esp_dma_done(s);
+        }
+    }
+#else
     if (s->async_len == 0) {
         /* Defer until data is available.  */
         return;
@@ -312,6 +363,7 @@
         /* Partially filled a scsi buffer. Complete immediately.  */
         esp_dma_done(s);
     }
+#endif
 }
 
 static void esp_command_complete(void *opaque, int reason, uint32_t tag,
