diff --git a/block/iscsi.c b/block/iscsi.c
new file mode 100644
index 0000000..20fc288
--- /dev/null
+++ b/block/iscsi.c
@@ -0,0 +1,500 @@
+/*
+ * QEMU Block driver for iSCSI images
+ *
+ * Copyright (c) 2010 Ronnie Sahlberg <ronniesahlberg@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <poll.h>
+#include "sysemu.h"
+#include "qemu-common.h"
+#include "qemu-error.h"
+#include "block_int.h"
+#include "iscsi/iscsi.h"
+#include "iscsi/scsi-lowlevel.h"
+
+
+typedef struct ISCSILUN {
+    struct iscsi_context *iscsi;
+    int lun;
+    int block_size;
+    unsigned long num_blocks;
+} ISCSILUN;
+
+typedef struct ISCSIAIOCB {
+    BlockDriverAIOCB common;
+    QEMUIOVector *qiov;
+    QEMUBH *bh;
+    ISCSILUN *iscsilun;
+    int canceled;
+    int status;
+    size_t read_size;
+} ISCSIAIOCB;
+
+struct iscsi_task {
+    ISCSILUN *iscsilun;
+    int status;
+    int complete;
+};
+
+static int
+iscsi_is_inserted(BlockDriverState *bs)
+{
+    ISCSILUN *iscsilun = bs->opaque;
+    struct iscsi_context *iscsi = iscsilun->iscsi;
+
+    return iscsi_is_logged_in(iscsi);
+}
+
+
+static void
+iscsi_aio_cancel(BlockDriverAIOCB *blockacb)
+{
+    ISCSIAIOCB *acb = (ISCSIAIOCB *)blockacb;
+
+    acb->status = -EIO;
+    acb->common.cb(acb->common.opaque, acb->status);
+    acb->canceled = 1;
+}
+
+static AIOPool iscsi_aio_pool = {
+    .aiocb_size         = sizeof(ISCSIAIOCB),
+    .cancel             = iscsi_aio_cancel,
+};
+
+
+static void iscsi_process_read(void *arg);
+static void iscsi_process_write(void *arg);
+
+static void
+iscsi_set_events(ISCSILUN *iscsilun)
+{
+    struct iscsi_context *iscsi = iscsilun->iscsi;
+
+    qemu_aio_set_fd_handler(iscsi_get_fd(iscsi), iscsi_process_read,
+			    (iscsi_which_events(iscsi)&POLLOUT)
+			    ?iscsi_process_write:NULL,
+			    NULL, NULL, iscsilun);
+}
+
+static void
+iscsi_process_read(void *arg)
+{
+    ISCSILUN *iscsilun = arg;
+    struct iscsi_context *iscsi = iscsilun->iscsi;
+
+    iscsi_service(iscsi, POLLIN);
+    iscsi_set_events(iscsilun);
+}
+
+static void
+iscsi_process_write(void *arg)
+{
+    ISCSILUN *iscsilun = arg;
+    struct iscsi_context *iscsi = iscsilun->iscsi;
+
+    iscsi_service(iscsi, POLLOUT);
+    iscsi_set_events(iscsilun);
+}
+
+
+static int
+iscsi_schedule_bh(QEMUBHFunc *cb, ISCSIAIOCB *acb)
+{
+    acb->bh = qemu_bh_new(cb, acb);
+    if (!acb->bh) {
+	error_report("oom: could not create iscsi bh");
+	return -EIO;
+    }
+
+    qemu_bh_schedule(acb->bh);
+    return 0;
+}
+
+static void
+iscsi_readv_writev_bh_cb(void *p)
+{
+    ISCSIAIOCB *acb = p;
+
+    acb->common.cb(acb->common.opaque, acb->status);
+    qemu_aio_release(acb);
+}
+
+static void
+iscsi_aio_write10_cb(struct iscsi_context *iscsi, int status,
+		     void *command_data, void *private_data)
+{
+    ISCSIAIOCB *acb = private_data;
+
+    if (acb->canceled != 0) {
+	qemu_aio_release(acb);
+	return;
+    }
+
+    acb->status = 0;
+    if (status < 0) {
+	error_report("Failed to write10 data to iSCSI lun. %s",
+		     iscsi_get_error(iscsi));
+	acb->status = -EIO;
+    }
+
+    iscsi_schedule_bh(iscsi_readv_writev_bh_cb, acb);
+}
+
+static BlockDriverAIOCB *
+iscsi_aio_writev(BlockDriverState *bs, int64_t sector_num,
+		 QEMUIOVector *qiov, int nb_sectors,
+		 BlockDriverCompletionFunc *cb,
+		 void *opaque)
+{
+    ISCSILUN *iscsilun = bs->opaque;
+    struct iscsi_context *iscsi = iscsilun->iscsi;
+    ISCSIAIOCB *acb;
+    size_t size;
+    unsigned char *buf;
+
+    acb = qemu_aio_get(&iscsi_aio_pool, bs, cb, opaque);
+    if (!acb) {
+	return NULL;
+    }
+
+    acb->iscsilun = iscsilun;
+    acb->qiov     = qiov;
+
+    acb->canceled   = 0;
+
+    /* XXX we should pass the iovec to write10 to avoid the extra copy */
+    /* this will allow us to get rid of 'buf' completely */
+    size = nb_sectors * BDRV_SECTOR_SIZE;
+    buf = qemu_malloc(size);
+    qemu_iovec_to_buffer(acb->qiov, buf);
+    if (iscsi_write10_async(iscsi, iscsilun->lun, buf, size,
+			    sector_num * BDRV_SECTOR_SIZE
+			    / iscsilun->block_size,
+			    0, 0, iscsilun->block_size,
+			    iscsi_aio_write10_cb, acb) != 0) {
+	error_report("iSCSI: Failed to send write10 command. %s",
+		     iscsi_get_error(iscsi));
+	qemu_free(buf);
+	qemu_aio_release(acb);
+	return NULL;
+    }
+    qemu_free(buf);
+    iscsi_set_events(iscsilun);
+
+    return &acb->common;
+}
+
+static void
+iscsi_aio_read10_cb(struct iscsi_context *iscsi, int status,
+		    void *command_data, void *private_data)
+{
+    ISCSIAIOCB *acb = private_data;
+    struct scsi_task *scsi = command_data;
+
+    if (acb->canceled != 0) {
+	qemu_aio_release(acb);
+	return;
+    }
+
+    acb->status = 0;
+    if (status < 0) {
+	error_report("Failed to read10 data from iSCSI lun. %s",
+		     iscsi_get_error(iscsi));
+	acb->status = -EIO;
+    } else {
+	qemu_iovec_from_buffer(acb->qiov, scsi->datain.data, acb->read_size);
+    }
+
+    iscsi_schedule_bh(iscsi_readv_writev_bh_cb, acb);
+}
+
+static BlockDriverAIOCB *
+iscsi_aio_readv(BlockDriverState *bs, int64_t sector_num,
+		QEMUIOVector *qiov, int nb_sectors,
+		BlockDriverCompletionFunc *cb,
+		void *opaque)
+{
+    ISCSILUN *iscsilun = bs->opaque;
+    struct iscsi_context *iscsi = iscsilun->iscsi;
+    ISCSIAIOCB *acb;
+    size_t qemu_read_size, lun_read_size;
+
+    qemu_read_size = BDRV_SECTOR_SIZE * (size_t)nb_sectors;
+    lun_read_size  = (qemu_read_size + iscsilun->block_size - 1)
+      / iscsilun->block_size * iscsilun->block_size;
+
+    acb = qemu_aio_get(&iscsi_aio_pool, bs, cb, opaque);
+    if (!acb) {
+	return NULL;
+    }
+
+    acb->iscsilun = iscsilun;
+    acb->qiov     = qiov;
+
+    acb->canceled   = 0;
+    acb->read_size  = qemu_read_size;
+
+    if (iscsi_read10_async(iscsi, iscsilun->lun,
+			   sector_num * BDRV_SECTOR_SIZE
+			   / iscsilun->block_size,
+			   lun_read_size, iscsilun->block_size,
+			   iscsi_aio_read10_cb, acb) != 0) {
+	error_report("iSCSI: Failed to send read10 command. %s",
+		     iscsi_get_error(iscsi));
+	qemu_aio_release(acb);
+	return NULL;
+    }
+    iscsi_set_events(iscsilun);
+
+    return &acb->common;
+}
+
+
+static int
+iscsi_flush(BlockDriverState *bs)
+{
+    ISCSILUN *iscsilun = bs->opaque;
+    struct iscsi_context *iscsi = iscsilun->iscsi;
+    struct scsi_task *task;
+
+    task = iscsi_synchronizecache10_sync(iscsi, iscsilun->lun, 0, 0, 0, 0);
+    if (task == NULL) {
+	error_report("iSCSI: Failed to flush() device.");
+	return -1;
+    }
+    if (task->status != 0) {
+	error_report("iSCSI: Failed to flush to LUN : %s",
+		     iscsi_get_error(iscsi));
+	scsi_free_scsi_task(task);
+	return -1;
+    }
+    scsi_free_scsi_task(task);
+    return 0;
+}
+
+static int64_t
+iscsi_getlength(BlockDriverState *bs)
+{
+    ISCSILUN *iscsilun = bs->opaque;
+    int64_t len;
+
+    len  = iscsilun->num_blocks;
+    len *= iscsilun->block_size;
+
+    return len;
+}
+
+static void
+iscsi_readcapacity10_cb(struct iscsi_context *iscsi, int status,
+			void *command_data, void *private_data)
+{
+    struct iscsi_task *task = private_data;
+    struct scsi_readcapacity10 *rc10;
+    struct scsi_task *scsi = command_data;
+
+    if (status != 0) {
+	error_report("iSCSI: Failed to read capacity of iSCSI lun. %s",
+		     iscsi_get_error(iscsi));
+	task->status   = 1;
+	task->complete = 1;
+	return;
+    }
+
+    rc10 = scsi_datain_unmarshall(scsi);
+    if (rc10 == NULL) {
+	error_report("iSCSI: Failed to unmarshall readcapacity10 data.");
+	task->status   = 1;
+	task->complete = 1;
+	return;
+    }
+
+    task->iscsilun->block_size = rc10->block_size;
+    task->iscsilun->num_blocks = rc10->lba;
+
+    task->status   = 0;
+    task->complete = 1;
+}
+
+
+static void
+iscsi_connect_cb(struct iscsi_context *iscsi, int status, void *command_data,
+		 void *private_data)
+{
+    struct iscsi_task *task = private_data;
+
+    if (status != 0) {
+	task->status   = 1;
+	task->complete = 1;
+	return;
+    }
+
+    if (iscsi_readcapacity10_async(iscsi, task->iscsilun->lun, 0, 0,
+				   iscsi_readcapacity10_cb, private_data)
+	!= 0) {
+	error_report("iSCSI: failed to send readcapacity command.");
+	task->status   = 1;
+	task->complete = 1;
+    }
+}
+
+/*
+ * We support iscsi url's on the form
+ * iscsi://<host>[:<port>]/<targetname>/<lun>
+ */
+static int iscsi_open(BlockDriverState *bs, const char *filename, int flags)
+{
+    ISCSILUN *iscsilun = bs->opaque;
+    struct iscsi_context *iscsi = NULL;
+    struct iscsi_task task;
+    char *ptr, *host, *target, *url;
+    char *tmp;
+    int ret, lun;
+
+    bzero(iscsilun, sizeof(ISCSILUN));
+
+    url = qemu_strdup(filename);
+
+    if (strncmp(url, "iscsi://", 8)) {
+	error_report("iSCSI: url does not start with 'iscsi://'");
+	ret = -EINVAL;
+	goto failed;
+    }
+
+    host = url + 8;
+
+    ptr = index(host, '/');
+    if (ptr == NULL) {
+	error_report("iSCSI: host is not '/' terminated.");
+	ret = -EINVAL;
+	goto failed;
+    }
+
+    *ptr++ = 0;
+    target = ptr;
+
+    ptr = index(target, '/');
+    if (ptr == NULL) {
+	error_report("iSCSI: host/target is not '/' terminated.");
+	ret = -EINVAL;
+	goto failed;
+    }
+
+    *ptr++ = 0;
+
+    lun = strtol(ptr, &tmp, 10);
+    if (*tmp) {
+	error_report("iSCSI: Invalid LUN specified.");
+	ret = -EINVAL;
+	goto failed;
+    }
+
+    /* Should really append the KVM name after the ':' here */
+    iscsi = iscsi_create_context("iqn.2008-11.org.linux-kvm:");
+    if (iscsi == NULL) {
+	error_report("iSCSI: Failed to create iSCSI context.");
+	ret = -ENOMEM;
+	goto failed;
+    }
+
+    if (iscsi_set_targetname(iscsi, target)) {
+	error_report("iSCSI: Failed to set target name.");
+	ret = -ENOMEM;
+	goto failed;
+    }
+
+    if (iscsi_set_session_type(iscsi, ISCSI_SESSION_NORMAL) != 0) {
+	error_report("iSCSI: Failed to set session type to normal.");
+	ret = -ENOMEM;
+	goto failed;
+    }
+
+    iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE_CRC32C);
+
+    task.iscsilun = iscsilun;
+    task.status = 0;
+    task.complete = 0;
+
+    iscsilun->iscsi = iscsi;
+    iscsilun->lun   = lun;
+
+    if (iscsi_full_connect_async(iscsi, host, lun, iscsi_connect_cb, &task)
+	!= 0) {
+	error_report("iSCSI: Failed to start async connect.");
+	ret = -ENOMEM;
+	goto failed;
+    }
+    async_context_push();
+    while (!task.complete) {
+	iscsi_set_events(iscsilun);
+	qemu_aio_wait();
+    }
+    async_context_pop();
+    if (task.status != 0) {
+	error_report("iSCSI: Failed to connect to LUN : %s",
+		     iscsi_get_error(iscsi));
+	ret = -EINVAL;
+	goto failed;
+    }
+
+    return 0;
+
+failed:
+    qemu_free(url);
+    if (iscsi != NULL) {
+	iscsi_destroy_context(iscsi);
+    }
+    bzero(iscsilun, sizeof(ISCSILUN));
+    return ret;
+}
+
+static void iscsi_close(BlockDriverState *bs)
+{
+    ISCSILUN *iscsilun = bs->opaque;
+    struct iscsi_context *iscsi = iscsilun->iscsi;
+
+    qemu_aio_set_fd_handler(iscsi_get_fd(iscsi), NULL, NULL, NULL, NULL, NULL);
+    iscsi_destroy_context(iscsi);
+    bzero(iscsilun, sizeof(ISCSILUN));
+}
+
+static BlockDriver bdrv_iscsi = {
+    .format_name     = "iscsi",
+    .protocol_name   = "iscsi",
+
+    .instance_size   = sizeof(ISCSILUN),
+    .bdrv_file_open  = iscsi_open,
+    .bdrv_close      = iscsi_close,
+    .bdrv_flush      = iscsi_flush,
+
+    .bdrv_getlength  = iscsi_getlength,
+
+    .bdrv_aio_readv  = iscsi_aio_readv,
+    .bdrv_aio_writev = iscsi_aio_writev,
+
+    .bdrv_is_inserted = iscsi_is_inserted,
+};
+
+static void iscsi_block_init(void)
+{
+    bdrv_register(&bdrv_iscsi);
+}
+
+block_init(iscsi_block_init);
