This patch implements a very naive virtio block device backend in QEMU.
There's a lot of room for future optimization.  We need to merge a -disk patch
before we can provide a mechanism to expose this to users.

Signed-off-by: Anthony Liguori <[EMAIL PROTECTED]>

diff --git a/qemu/Makefile.target b/qemu/Makefile.target
index c7686b2..49c0fc7 100644
--- a/qemu/Makefile.target
+++ b/qemu/Makefile.target
@@ -449,7 +449,7 @@ VL_OBJS += rtl8139.o
 VL_OBJS+= hypercall.o
 
 # virtio devices
-VL_OBJS += virtio.o
+VL_OBJS += virtio.o virtio-blk.o
 
 ifeq ($(TARGET_BASE_ARCH), i386)
 # Hardware support
diff --git a/qemu/hw/pc.c b/qemu/hw/pc.c
index 8aae814..c7483e8 100644
--- a/qemu/hw/pc.c
+++ b/qemu/hw/pc.c
@@ -943,6 +943,14 @@ static void pc_init1(ram_addr_t ram_size, int 
vga_ram_size, int boot_device,
 #ifdef USE_HYPERCALL
     pci_hypercall_init(pci_bus);
 #endif
+
+    if (1) {
+       BlockDriverState *bs = bdrv_new("vda");
+       if (bdrv_open(bs, "/home/anthony/images/linux.img", BDRV_O_SNAPSHOT))
+           exit(1);
+       virtio_blk_init(pci_bus, 0x5002, 0x2258, bs);
+    }
+
     if (pci_enabled) {
         pci_piix3_ide_init(pci_bus, bs_table, piix3_devfn + 1, i8259);
     } else {
diff --git a/qemu/hw/virtio-blk.c b/qemu/hw/virtio-blk.c
new file mode 100644
index 0000000..a1d5ea6
--- /dev/null
+++ b/qemu/hw/virtio-blk.c
@@ -0,0 +1,162 @@
+/*
+ * Virtio Block Device
+ *
+ * Copyright IBM, Corp. 2007
+ *
+ * Authors:
+ *  Anthony Liguori   <[EMAIL PROTECTED]>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.  See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#include "vl.h"
+#include "virtio.h"
+
+/* from Linux's linux/virtio_blk.h */
+
+/* The ID for virtio_block */
+#define VIRTIO_ID_BLOCK        2
+
+/* Feature bits */
+#define VIRTIO_BLK_F_BARRIER   0       /* Does host support barriers? */
+#define VIRTIO_BLK_F_SIZE_MAX  1       /* Indicates maximum segment size */
+#define VIRTIO_BLK_F_SEG_MAX   2       /* Indicates maximum # of segments */
+
+/* The capacity (in 512-byte sectors).  8 bytes. */
+#define VIRTIO_CONFIG_BLK_F_CAPACITY   0
+/* The maximum segment size. 4 bytes. */
+#define VIRTIO_CONFIG_BLK_F_SIZE_MAX   0x08
+/* The maximum number of segments.  4 bytes. */
+#define VIRTIO_CONFIG_BLK_F_SEG_MAX    0x0A
+
+/* These two define direction. */
+#define VIRTIO_BLK_T_IN                0
+#define VIRTIO_BLK_T_OUT       1
+
+/* This bit says it's a scsi command, not an actual read or write. */
+#define VIRTIO_BLK_T_SCSI_CMD  2
+
+/* Barrier before this op. */
+#define VIRTIO_BLK_T_BARRIER   0x80000000
+
+/* This is the first element of the read scatter-gather list. */
+struct virtio_blk_outhdr
+{
+    /* VIRTIO_BLK_T* */
+    uint32_t type;
+    /* io priority. */
+    uint32_t ioprio;
+    /* Sector (ie. 512 byte offset) */
+    uint64_t sector;
+    /* Where to put reply. */
+    uint64_t id;
+};
+
+#define VIRTIO_BLK_S_OK                0
+#define VIRTIO_BLK_S_IOERR     1
+#define VIRTIO_BLK_S_UNSUPP    2
+
+/* This is the first element of the write scatter-gather list */
+struct virtio_blk_inhdr
+{
+    unsigned char status;
+};
+
+typedef struct VirtIOBlock
+{
+    VirtIODevice vdev;
+    BlockDriverState *bs;
+} VirtIOBlock;
+
+static VirtIOBlock *to_virtio_blk(VirtIODevice *vdev)
+{
+    return (VirtIOBlock *)vdev;
+}
+
+static void virtio_blk_handle_output(VirtIODevice *vdev, VirtQueue *vq)
+{
+    VirtIOBlock *s = to_virtio_blk(vdev);
+    VirtQueueElement elem;
+    unsigned int count;
+
+    while ((count = virtqueue_pop(vq, &elem)) != 0) {
+       struct virtio_blk_inhdr *in;
+       struct virtio_blk_outhdr *out;
+       unsigned int wlen;
+       off_t off;
+       int i;
+
+       out = (void *)elem.out_sg[0].iov_base;
+       in = (void *)elem.in_sg[elem.in_num - 1].iov_base;
+       off = out->sector;
+
+       if (out->type & VIRTIO_BLK_T_SCSI_CMD) {
+           wlen = sizeof(*in);
+           in->status = VIRTIO_BLK_S_UNSUPP;
+       } else if (out->type & VIRTIO_BLK_T_OUT) {
+           wlen = sizeof(*in);
+
+           for (i = 1; i < elem.out_num; i++) {
+               bdrv_write(s->bs, off,
+                          elem.out_sg[i].iov_base,
+                          elem.out_sg[i].iov_len / 512);
+               off += elem.out_sg[i].iov_len / 512;
+           }
+
+           in->status = VIRTIO_BLK_S_OK;
+       } else {
+           wlen = sizeof(*in);
+           
+           for (i = 0; i < elem.in_num - 1; i++) {
+               bdrv_read(s->bs, off,
+                         elem.in_sg[i].iov_base,
+                         elem.in_sg[i].iov_len / 512);
+               off += elem.in_sg[i].iov_len / 512;
+               wlen += elem.in_sg[i].iov_len;
+           }
+
+           in->status = VIRTIO_BLK_S_OK;
+       }
+
+       virtqueue_push(vq, &elem, wlen);
+       virtio_notify(vdev, vq);
+    }
+}
+
+static void virtio_blk_update_config(VirtIODevice *vdev, uint8_t *config)
+{
+    VirtIOBlock *s = to_virtio_blk(vdev);
+    int64_t capacity;
+    uint32_t v;
+
+    bdrv_get_geometry(s->bs, &capacity);
+    memcpy(config + VIRTIO_CONFIG_BLK_F_CAPACITY, &capacity, sizeof(capacity));
+
+    v = VIRTQUEUE_MAX_SIZE - 2;
+    memcpy(config + VIRTIO_CONFIG_BLK_F_SEG_MAX, &v, sizeof(v));
+}
+
+static uint32_t virtio_blk_get_features(VirtIODevice *vdev)
+{
+    return (1 << VIRTIO_BLK_F_SEG_MAX);
+}
+
+VirtIODevice *virtio_blk_init(PCIBus *bus, uint16_t vendor, uint16_t device,
+                             BlockDriverState *bs)
+{
+    VirtIOBlock *s;
+
+    s = (VirtIOBlock *)virtio_init_pci(bus, "virtio-blk", vendor, device,
+                                      vendor, VIRTIO_ID_BLOCK,
+                                      16, sizeof(VirtIOBlock));
+
+    s->vdev.update_config = virtio_blk_update_config;
+    s->vdev.get_features = virtio_blk_get_features;
+    s->bs = bs;
+
+    virtio_add_queue(&s->vdev, virtio_blk_handle_output);
+
+    return &s->vdev;
+}
diff --git a/qemu/vl.h b/qemu/vl.h
index fafcf09..249ede2 100644
--- a/qemu/vl.h
+++ b/qemu/vl.h
@@ -1396,6 +1396,9 @@ void vmchannel_init(CharDriverState *hd, uint32_t 
deviceid, uint32_t index);
 
 typedef struct VirtIODevice VirtIODevice;
 
+VirtIODevice *virtio_blk_init(PCIBus *bus, uint16_t vendor, uint16_t device,
+                             BlockDriverState *bs);
+
 /* buf = NULL means polling */
 typedef int ADBDeviceRequest(ADBDevice *d, uint8_t *buf_out,
                               const uint8_t *buf, int len);

-------------------------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc.
Still grepping through log files to find problems?  Stop.
Now Search log events and configuration files using AJAX and a browser.
Download your FREE copy of Splunk now >> http://get.splunk.com/
_______________________________________________
kvm-devel mailing list
kvm-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/kvm-devel

Reply via email to