]> Zhao Yanbai Git Server - minix.git/commitdiff
virtio: virtio-blk driver
authorArne Welzel <arne.welzel@gmail.com>
Sat, 15 Dec 2012 12:54:11 +0000 (13:54 +0100)
committerBen Gras <ben@minix3.org>
Wed, 23 Jan 2013 09:03:03 +0000 (09:03 +0000)
drivers/Makefile
drivers/virtio_blk/Makefile [new file with mode: 0644]
drivers/virtio_blk/virtio_blk.c [new file with mode: 0644]
drivers/virtio_blk/virtio_blk.h [new file with mode: 0644]
etc/system.conf

index ad7bce8dc3a30ab9b38d4136c8926be051193380..8e3e66e4c6fc22077052e0f864b1bb4c868c242c 100644 (file)
@@ -18,7 +18,8 @@ SUBDIR= tty
 .if ${MACHINE_ARCH} == "i386"
 SUBDIR= ahci amddev atl2 at_wini audio dec21140A dp8390 dpeth \
        e1000 fbd filter floppy fxp hello lance log mmc orinoco pci printer \
-       random readclock rtl8139 rtl8169 ti1225 tty vbox acpi
+       random readclock rtl8139 rtl8169 ti1225 tty vbox acpi \
+       virtio_blk
 .endif
 
 .if ${MACHINE_ARCH} == "earm"
diff --git a/drivers/virtio_blk/Makefile b/drivers/virtio_blk/Makefile
new file mode 100644 (file)
index 0000000..95171fc
--- /dev/null
@@ -0,0 +1,12 @@
+# Makefile for virtio_blk device
+PROG=  virtio_blk
+SRCS=  virtio_blk.c
+
+DPADD+=        ${LIBBLOCKDRIVER} ${LIBSYS} ${LIBMTHREAD} ${LIBVIRTIO}
+LDADD+=        -lblockdriver -lsys -lmthread -lvirtio
+
+MAN=
+
+BINDIR?= /sbin
+
+.include <minix.service.mk>
diff --git a/drivers/virtio_blk/virtio_blk.c b/drivers/virtio_blk/virtio_blk.c
new file mode 100644 (file)
index 0000000..f62fd32
--- /dev/null
@@ -0,0 +1,756 @@
+/*
+ * virtio block driver for MINIX 3
+ *
+ * Copyright (c) 2013, A. Welzel, <arne.welzel@gmail.com>
+ *
+ * This software is released under the BSD license. See the LICENSE file
+ * included in the main directory of this source distribution for the
+ * license terms and conditions.
+ */
+
+#include <assert.h>
+
+#include <minix/drivers.h>
+#include <minix/blockdriver_mt.h>
+#include <minix/drvlib.h>
+#include <minix/virtio.h>
+#include <minix/sysutil.h>
+
+#include <sys/ioc_disk.h>
+
+#include "virtio_blk.h"
+
+#define mystatus(tid)  (status_vir[(tid)] & 0xFF)
+
+#define dprintf(s) do {                                                \
+       printf("%s: ", name);                                   \
+       printf s;                                               \
+       printf("\n");                                           \
+} while (0)
+
+/* Number of threads to use */
+#define VIRTIO_BLK_NUM_THREADS         4
+
+/* virtio-blk blocksize is always 512 bytes */
+#define VIRTIO_BLK_BLOCK_SIZE          512
+
+static const char *const name = "virtio-blk";
+
+/* static device handle */
+static struct virtio_device *blk_dev;
+
+static struct virtio_blk_config blk_config;
+
+struct virtio_feature blkf[] = {
+       { "barrier",    VIRTIO_BLK_F_BARRIER,   0,      0       },
+       { "sizemax",    VIRTIO_BLK_F_SIZE_MAX,  0,      0       },
+       { "segmax",     VIRTIO_BLK_F_SEG_MAX,   0,      0       },
+       { "geometry",   VIRTIO_BLK_F_GEOMETRY,  0,      0       },
+       { "read-only",  VIRTIO_BLK_F_RO,        0,      0       },
+       { "blocksize",  VIRTIO_BLK_F_BLK_SIZE,  0,      0       },
+       { "scsi",       VIRTIO_BLK_F_SCSI,      0,      0       },
+       { "flush",      VIRTIO_BLK_F_FLUSH,     0,      0       },
+       { "topology",   VIRTIO_BLK_F_TOPOLOGY,  0,      0       },
+       { "idbytes",    VIRTIO_BLK_ID_BYTES,    0,      0       }
+};
+
+/* State information */
+static int spurious_interrupt = 0;
+static int terminating = 0;
+static int open_count = 0;
+
+/* Partition magic */
+#define VIRTIO_BLK_SUB_PER_DRIVE       (NR_PARTITIONS * NR_PARTITIONS)
+struct device part[DEV_PER_DRIVE];
+struct device subpart[VIRTIO_BLK_SUB_PER_DRIVE];
+
+/* Headers for requests */
+static struct virtio_blk_outhdr *hdrs_vir;
+static phys_bytes hdrs_phys;
+
+/* Status bytes for requests.
+ *
+ * Usually a status is only one byte in length, but we need the lowest bit
+ * to propagate writable. For this reason we take u16_t and use a mask for
+ * the lower byte later.
+ */
+static u16_t *status_vir;
+static phys_bytes status_phys;
+
+/* Prototypes */
+static int virtio_blk_open(dev_t minor, int access);
+static int virtio_blk_close(dev_t minor);
+static ssize_t virtio_blk_transfer(dev_t minor, int write, u64_t position,
+                                  endpoint_t endpt, iovec_t *iovec,
+                                  unsigned int cnt, int flags);
+static int virtio_blk_ioctl(dev_t minor, unsigned int req, endpoint_t endpt,
+                           cp_grant_id_t grant);
+static struct device * virtio_blk_part(dev_t minor);
+static void virtio_blk_geometry(dev_t minor, struct partition *entry);
+static void virtio_blk_device_intr(void);
+static void virtio_blk_spurious_intr(void);
+static void virtio_blk_intr(unsigned int irqs);
+static int virtio_blk_device(dev_t minor, device_id_t *id);
+
+static int virtio_blk_flush(void);
+static void virtio_blk_terminate(void);
+static void virtio_blk_cleanup(void);
+static int virtio_blk_status2error(u8_t status);
+static int virtio_blk_alloc_requests(void);
+static void virtio_blk_free_requests(void);
+static int virtio_blk_feature_setup(void);
+static int virtio_blk_config(void);
+static int virtio_blk_probe(int skip);
+
+/* libblockdriver driver tab */
+static struct blockdriver virtio_blk_dtab  = {
+       BLOCKDRIVER_TYPE_DISK,
+       virtio_blk_open,
+       virtio_blk_close,
+       virtio_blk_transfer,
+       virtio_blk_ioctl,
+       NULL,           /* bdr_cleanup */
+       virtio_blk_part,
+       virtio_blk_geometry,
+       virtio_blk_intr,
+       NULL,           /* bdr_alarm */
+       NULL,           /* bdr_other */
+       virtio_blk_device
+};
+
+static int
+virtio_blk_open(dev_t minor, int access)
+{
+       struct device *dev = virtio_blk_part(minor);
+
+       /* Check if this device exists */
+       if (!dev)
+               return ENXIO;
+
+       /* Read only devices should only be mounted... read-only */
+       if ((access & W_BIT) && virtio_host_supports(blk_dev, VIRTIO_BLK_F_RO))
+               return EACCES;
+
+       /* Partition magic when opened the first time or re-opened after
+        * being fully closed
+        */
+       if (open_count == 0) {
+               memset(part, 0, sizeof(part));
+               memset(subpart, 0, sizeof(subpart));
+               part[0].dv_size = blk_config.capacity * VIRTIO_BLK_BLOCK_SIZE;
+               partition(&virtio_blk_dtab, 0, P_PRIMARY, 0 /* ATAPI */);
+               blockdriver_mt_set_workers(0, VIRTIO_BLK_NUM_THREADS);
+       }
+
+       open_count++;
+       return OK;
+}
+
+static int
+virtio_blk_close(dev_t minor)
+{
+       struct device *dev = virtio_blk_part(minor);
+
+       /* Check if this device exists */
+       if (!dev)
+               return ENXIO;
+
+       if (open_count == 0) {
+               dprintf(("Closing one too many times?"));
+               return EINVAL;
+       }
+
+       open_count--;
+
+       /* If fully closed, flush the device and set workes to 1 */
+       if (open_count == 0) {
+               virtio_blk_flush();
+               blockdriver_mt_set_workers(0, 1);
+       }
+
+       /* If supposed to terminate and fully closed, do it! */
+       if (terminating && open_count == 0)
+               virtio_blk_terminate();
+
+       return OK;
+}
+
+static int
+prepare_bufs(struct vumap_vir *vir, struct vumap_phys *phys, int cnt, int w)
+{
+       for (int i = 0; i < cnt ; i++) {
+
+               /* So you gave us a byte aligned buffer? Good job! */
+               if (phys[i].vp_addr & 1) {
+                       dprintf(("byte aligned %08lx", phys[i].vp_addr));
+                       return EINVAL;
+               }
+
+               /* Check if the buffer is good */
+               if (phys[i].vp_size != vir[i].vv_size) {
+                       dprintf(("Non-contig buf %08lx", phys[i].vp_addr));
+                       return EINVAL;
+               }
+
+               /* If write, the buffers only need to be read */
+               phys[i].vp_addr |= !w;
+       }
+
+       return OK;
+}
+
+static int
+prepare_vir_vec(endpoint_t endpt, struct vumap_vir *vir, iovec_s_t *iv,
+               int cnt, vir_bytes *size)
+{
+       /* This is pretty much the same as sum_iovec from AHCI,
+        * except that we don't support any iovecs where the size
+        * is not a multiple of 512
+        */
+       vir_bytes s, total = 0;
+       for (int i = 0; i < cnt; i++) {
+               s = iv[i].iov_size;
+
+               if (s == 0 || (s % VIRTIO_BLK_BLOCK_SIZE) || s > LONG_MAX) {
+                       dprintf(("bad iv[%d].iov_size (%lu) from %d", i, s,
+                                                                     endpt));
+                       return EINVAL;
+               }
+
+               total += s;
+
+               if (total > LONG_MAX) {
+                       dprintf(("total overflow from %d", endpt));
+                       return EINVAL;
+               }
+
+               if (endpt == SELF)
+                       vir[i].vv_addr = (vir_bytes)iv[i].iov_grant;
+               else
+                       vir[i].vv_grant = iv[i].iov_grant;
+
+               vir[i].vv_size = iv[i].iov_size;
+
+       }
+
+       *size = total;
+       return OK;
+}
+
+static ssize_t
+virtio_blk_transfer(dev_t minor, int write, u64_t position, endpoint_t endpt,
+                   iovec_t *iovec, unsigned int cnt, int flags)
+{
+       /* Need to translate vir to phys */
+       struct vumap_vir vir[NR_IOREQS];
+
+       /* Physical addresses of buffers, including header and trailer */
+       struct vumap_phys phys[NR_IOREQS + 2];
+
+       /* Which thread is doing the transfer? */
+       thread_id_t tid = blockdriver_mt_get_tid();
+
+       vir_bytes size = 0;
+       vir_bytes size_tmp = 0;
+       struct device *dv;
+       u64_t sector;
+       u64_t end_part;
+       int r, pcnt = sizeof(phys) / sizeof(phys[0]);
+
+       iovec_s_t *iv = (iovec_s_t *)iovec;
+       int access = write ? VUA_READ : VUA_WRITE;
+
+       /* Make sure we don't touch this one anymore */
+       iovec = NULL;
+
+       if (cnt > NR_IOREQS)
+               return EINVAL;
+
+       /* position greater than capacity? */
+       if (position >= blk_config.capacity * VIRTIO_BLK_BLOCK_SIZE)
+               return 0;
+
+       dv = virtio_blk_part(minor);
+
+       /* Does device exist? */
+       if (!dv)
+               return ENXIO;
+
+       position += dv->dv_base;
+       end_part = dv->dv_base + dv->dv_size;
+
+       /* Hmmm, AHCI tries to fix this up, but lets just say everything
+        * needs to be sector (512 byte) aligned...
+        */
+       if (position % VIRTIO_BLK_BLOCK_SIZE) {
+               dprintf(("Non sector-aligned access %016llx", position));
+               return EINVAL;
+       }
+
+       sector = position / VIRTIO_BLK_BLOCK_SIZE;
+
+       r = prepare_vir_vec(endpt, vir, iv, cnt, &size);
+
+       if (r != OK)
+               return r;
+
+       if (position >= end_part)
+               return 0;
+
+       /* Truncate if the partition is smaller than that */
+       if (position + size > end_part - 1) {
+               size = end_part - position;
+
+               /* Fix up later */
+               size_tmp = 0;
+               cnt = 0;
+       } else {
+               /* Use all buffers */
+               size_tmp = size;
+       }
+
+       /* Fix up the number of vectors if size was truncated */
+       while (size_tmp < size)
+               size_tmp += vir[cnt++].vv_size;
+
+       /* If the last vector was too big, just truncate it */
+       if (size_tmp > size) {
+               vir[cnt - 1].vv_size = vir[cnt -1].vv_size - (size_tmp - size);
+               size_tmp -= (size_tmp - size);
+       }
+
+       if (size % VIRTIO_BLK_BLOCK_SIZE) {
+               dprintf(("non-sector sized read (%lu) from %d", size, endpt));
+               return EINVAL;
+       }
+
+       /* Map vir to phys */
+       if ((r = sys_vumap(endpt, vir, cnt, 0, access,
+                          &phys[1], &pcnt)) != OK) {
+
+               dprintf(("Unable to map memory from %d (%d)", endpt, r));
+               return r;
+       }
+
+       /* Prepare the header */
+       memset(&hdrs_vir[tid], 0, sizeof(hdrs_vir[0]));
+
+       if (write)
+               hdrs_vir[tid].type = VIRTIO_BLK_T_OUT;
+       else
+               hdrs_vir[tid].type = VIRTIO_BLK_T_IN;
+
+       hdrs_vir[tid].ioprio = 0;
+       hdrs_vir[tid].sector = sector;
+
+       /* First the header */
+       phys[0].vp_addr = hdrs_phys + tid * sizeof(hdrs_vir[0]);
+       phys[0].vp_size = sizeof(hdrs_vir[0]);
+
+       /* Put the physical buffers into phys */
+       if ((r = prepare_bufs(vir, &phys[1], pcnt, write)) != OK)
+               return r;
+
+       /* Put the status at the end */
+       phys[pcnt + 1].vp_addr = status_phys + tid * sizeof(status_vir[0]);
+       phys[pcnt + 1].vp_size = sizeof(u8_t);
+
+       /* Status always needs write access */
+       phys[1 + pcnt].vp_addr |= 1;
+
+       /* Send addresses to queue */
+       virtio_to_queue(blk_dev, 0, phys, 2 + pcnt, &tid);
+
+       /* Wait for completion */
+       blockdriver_mt_sleep();
+
+       /* All was good */
+       if (mystatus(tid) == VIRTIO_BLK_S_OK)
+               return size;
+
+       /* Error path */
+       dprintf(("ERROR status=%02x sector=%llu len=%lx cnt=%d op=%s t=%d",
+                mystatus(tid), sector, size, pcnt,
+                write ? "write" : "read", tid));
+
+       return virtio_blk_status2error(mystatus(tid));
+}
+
+static int
+virtio_blk_ioctl(dev_t minor, unsigned int req, endpoint_t endpt,
+                cp_grant_id_t grant)
+{
+       switch (req) {
+
+       case DIOCOPENCT:
+               return sys_safecopyto(endpt, grant, 0,
+                       (vir_bytes) &open_count, sizeof(open_count));
+
+       case DIOCFLUSH:
+               return virtio_blk_flush();
+
+       }
+
+       return EINVAL;
+}
+
+static struct device *
+virtio_blk_part(dev_t minor)
+{
+       /* There's only a single drive attached to this device, alyways.
+        * Lets take some shortcuts...
+        */
+
+       /* Take care of d0 d0p0 ... */
+       if (minor < 5)
+               return &part[minor];
+
+       /* subparts start at 128 */
+       if (minor >= 128) {
+
+               /* Mask away upper bits */
+               minor = minor & 0x7F;
+
+               /* Only for the first disk */
+               if (minor > 15)
+                       return NULL;
+
+               return &subpart[minor];
+       }
+
+       return NULL;
+}
+
+static void
+virtio_blk_geometry(dev_t minor, struct partition *entry)
+{
+       /* Only for the drive */
+       if (minor != 0)
+               return;
+
+       /* Only if the host supports it */
+       if(!virtio_host_supports(blk_dev, VIRTIO_BLK_F_GEOMETRY))
+               return;
+
+       entry->cylinders = blk_config.geometry.cylinders;
+       entry->heads = blk_config.geometry.heads;
+       entry->sectors = blk_config.geometry.sectors;
+}
+
+static void
+virtio_blk_device_intr(void)
+{
+       thread_id_t *tid;
+
+       /* Multiple requests might have finished */
+       while (!virtio_from_queue(blk_dev, 0, (void**)&tid))
+               blockdriver_mt_wakeup(*tid);
+}
+
+static void
+virtio_blk_spurious_intr(void)
+{
+       /* Output a single message about spurious interrupts */
+       if (spurious_interrupt)
+               return;
+
+       dprintf(("Got spurious interrupt"));
+       spurious_interrupt = 1;
+}
+
+static void
+virtio_blk_intr(unsigned int irqs)
+{
+
+       if (virtio_had_irq(blk_dev))
+               virtio_blk_device_intr();
+       else
+               virtio_blk_spurious_intr();
+
+       virtio_irq_enable(blk_dev);
+}
+
+static int
+virtio_blk_device(dev_t minor, device_id_t *id)
+{
+       struct device *dev = virtio_blk_part(minor);
+
+       /* Check if this device exists */
+       if (!dev)
+               return ENXIO;
+
+       *id = 0;
+       return OK;
+}
+
+static int
+virtio_blk_flush(void)
+{
+       struct vumap_phys phys[2];
+       size_t phys_cnt = sizeof(phys) / sizeof(phys[0]);
+
+       /* Which thread is doing this request? */
+       thread_id_t tid = blockdriver_mt_get_tid();
+
+       /* Host may not support flushing */
+       if (!virtio_host_supports(blk_dev, VIRTIO_BLK_F_FLUSH))
+               return EOPNOTSUPP;
+
+       /* Prepare the header */
+       memset(&hdrs_vir[tid], 0, sizeof(hdrs_vir[0]));
+       hdrs_vir[tid].type = VIRTIO_BLK_T_FLUSH;
+
+       /* Let this be a barrier if the host supports it */
+       if (virtio_host_supports(blk_dev, VIRTIO_BLK_F_BARRIER))
+               hdrs_vir[tid].type |= VIRTIO_BLK_T_BARRIER;
+
+       /* Header and status for the queue */
+       phys[0].vp_addr = hdrs_phys + tid * sizeof(hdrs_vir[0]);
+       phys[0].vp_size = sizeof(hdrs_vir[0]);
+       phys[1].vp_addr = status_phys + tid * sizeof(status_vir[0]);
+       phys[1].vp_size = 1;
+
+       /* Status always needs write access */
+       phys[1].vp_addr |= 1;
+
+       /* Send flush request to queue */
+       virtio_to_queue(blk_dev, 0, phys, phys_cnt, &tid);
+
+       blockdriver_mt_sleep();
+
+       /* All was good */
+       if (mystatus(tid) == VIRTIO_BLK_S_OK)
+               return OK;
+
+       /* Error path */
+       dprintf(("ERROR status=%02x op=flush t=%d", mystatus(tid), tid));
+
+       return virtio_blk_status2error(mystatus(tid));
+}
+
+static void
+virtio_blk_terminate(void)
+{
+       /* Don't terminate if still opened */
+       if (open_count > 0)
+               return;
+
+       blockdriver_mt_terminate();
+}
+
+static void
+virtio_blk_cleanup(void)
+{
+       /* Just free the memory we allocated */
+       virtio_blk_free_requests();
+       virtio_reset_device(blk_dev);
+       virtio_free_queues(blk_dev);
+       virtio_free_device(blk_dev);
+       blk_dev = NULL;
+}
+
+static int
+virtio_blk_status2error(u8_t status)
+{
+       /* Convert a status from the host to an error */
+       switch (status) {
+               case VIRTIO_BLK_S_IOERR:
+                       return EIO;
+               case VIRTIO_BLK_S_UNSUPP:
+                       return ENOTSUP;
+               default:
+                       panic("%s: unknown status: %02x", name, status);
+       }
+       /* Never reached */
+       return OK;
+}
+
+static int
+virtio_blk_alloc_requests(void)
+{
+       /* Allocate memory for request headers and status field */
+
+       hdrs_vir = alloc_contig(VIRTIO_BLK_NUM_THREADS * sizeof(hdrs_vir[0]),
+                               AC_ALIGN4K, &hdrs_phys);
+
+       if (!hdrs_vir)
+               return ENOMEM;
+
+       status_vir = alloc_contig(VIRTIO_BLK_NUM_THREADS * sizeof(status_vir[0]),
+                                 AC_ALIGN4K, &status_phys);
+
+       if (!status_vir) {
+               free_contig(hdrs_vir, VIRTIO_BLK_NUM_THREADS * sizeof(hdrs_vir[0]));
+               return ENOMEM;
+       }
+
+       return OK;
+}
+
+static void
+virtio_blk_free_requests(void)
+{
+       free_contig(hdrs_vir, VIRTIO_BLK_NUM_THREADS * sizeof(hdrs_vir[0]));
+       free_contig(status_vir, VIRTIO_BLK_NUM_THREADS * sizeof(status_vir[0]));
+}
+
+static int
+virtio_blk_feature_setup(void)
+{
+       /* Feature setup for virtio-blk
+        *
+        * FIXME: Besides the geometry, everything is just debug output
+        * FIXME2: magic numbers
+        */
+       if (virtio_host_supports(blk_dev, VIRTIO_BLK_F_SEG_MAX)) {
+               blk_config.seg_max = virtio_sread32(blk_dev, 12);
+               dprintf(("Seg Max: %d", blk_config.seg_max));
+       }
+
+       if (virtio_host_supports(blk_dev, VIRTIO_BLK_F_GEOMETRY)) {
+               blk_config.geometry.cylinders = virtio_sread16(blk_dev, 16);
+               blk_config.geometry.heads = virtio_sread8(blk_dev, 18);
+               blk_config.geometry.sectors = virtio_sread8(blk_dev, 19);
+
+               dprintf(("Geometry: cyl=%d heads=%d sectors=%d",
+                                       blk_config.geometry.cylinders,
+                                       blk_config.geometry.heads,
+                                       blk_config.geometry.sectors));
+       }
+
+       if (virtio_host_supports(blk_dev, VIRTIO_BLK_F_SIZE_MAX))
+               dprintf(("Has size max"));
+
+       if (virtio_host_supports(blk_dev, VIRTIO_BLK_F_FLUSH))
+               dprintf(("Supports flushing"));
+
+       if (virtio_host_supports(blk_dev, VIRTIO_BLK_F_BLK_SIZE)) {
+               blk_config.blk_size = virtio_sread32(blk_dev, 20);
+               dprintf(("Block Size: %d", blk_config.blk_size));
+       }
+
+       if (virtio_host_supports(blk_dev, VIRTIO_BLK_F_BARRIER))
+               dprintf(("Supports barrier"));
+
+       return 0;
+}
+
+static int
+virtio_blk_config(void)
+{
+       u32_t sectors_low, sectors_high, size_mbs;
+
+       /* capacity is always there */
+       sectors_low = virtio_sread32(blk_dev, 0);
+       sectors_high = virtio_sread32(blk_dev, 4);
+       blk_config.capacity = ((u64_t)sectors_high << 32) | sectors_low;
+
+       /* If this gets truncated, you have a big disk... */
+       size_mbs = (u32_t)(blk_config.capacity * 512 / 1024 / 1024);
+       dprintf(("Capacity: %d MB", size_mbs));
+
+       /* do feature setup */
+       virtio_blk_feature_setup();
+       return 0;
+}
+
+static int
+virtio_blk_probe(int skip)
+{
+       int r;
+
+       /* sub device id for virtio-blk is 0x0002 */
+       blk_dev = virtio_setup_device(0x0002, name, blkf,
+                                     sizeof(blkf) / sizeof(blkf[0]),
+                                     VIRTIO_BLK_NUM_THREADS, skip);
+       if (!blk_dev)
+               return ENXIO;
+
+       /* virtio-blk has one queue only */
+       if ((r = virtio_alloc_queues(blk_dev, 1)) != OK) {
+               virtio_free_device(blk_dev);
+               return r;
+       }
+
+       /* Allocate memory for headers and status */
+       if ((r = virtio_blk_alloc_requests() != OK)) {
+               virtio_free_queues(blk_dev);
+               virtio_free_device(blk_dev);
+               return r;
+       }
+
+       virtio_blk_config();
+
+       /* Let the host now that we are ready */
+       virtio_device_ready(blk_dev);
+
+       virtio_irq_enable(blk_dev);
+
+       return OK;
+}
+
+static int
+sef_cb_init_fresh(int type, sef_init_info_t *info)
+{
+       long instance = 0;
+       int r;
+
+       env_parse("instance", "d", 0, &instance, 0, 255);
+
+       if ((r = virtio_blk_probe((int)instance)) == OK) {
+               blockdriver_announce(type);
+               return OK;
+       }
+
+       /* Error path */
+       if (r == ENXIO)
+               panic("%s: No device found", name);
+
+       if (r == ENOMEM)
+               panic("%s: Not enough memory", name);
+
+       panic("%s: Unexpected failure (%d)", name, r);
+}
+
+static void
+sef_cb_signal_handler(int signo)
+{
+       /* Ignore all signals but SIGTERM */
+       if (signo != SIGTERM)
+               return;
+
+       terminating = 1;
+       virtio_blk_terminate();
+
+       /* If we get a signal when completely closed, call
+        * exit(). We only leave the blockdriver_mt_task()
+        * loop after completing a request which is not the
+        * case for signals.
+        */
+       if (open_count == 0)
+               exit(0);
+}
+
+static void
+sef_local_startup(void)
+{
+       sef_setcb_init_fresh(sef_cb_init_fresh);
+       sef_setcb_init_lu(sef_cb_init_fresh);
+       sef_setcb_signal_handler(sef_cb_signal_handler);
+
+       sef_startup();
+}
+
+int
+main(int argc, char **argv)
+{
+       env_setargs(argc, argv);
+       sef_local_startup();
+
+       blockdriver_mt_task(&virtio_blk_dtab);
+
+       dprintf(("Terminating"));
+       virtio_blk_cleanup();
+
+       return OK;
+}
diff --git a/drivers/virtio_blk/virtio_blk.h b/drivers/virtio_blk/virtio_blk.h
new file mode 100644 (file)
index 0000000..28a0136
--- /dev/null
@@ -0,0 +1,124 @@
+#ifndef _LINUX_VIRTIO_BLK_H
+#define _LINUX_VIRTIO_BLK_H
+/* This header is BSD licensed so anyone can use the definitions to implement
+ * compatible drivers/servers.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of IBM nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL IBM OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE. */
+#if 0
+#include <linux/types.h>
+#include <linux/virtio_ids.h>
+#include <linux/virtio_config.h>
+#endif
+
+/* 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 */
+#define VIRTIO_BLK_F_GEOMETRY  4       /* Legacy geometry available  */
+#define VIRTIO_BLK_F_RO                5       /* Disk is read-only */
+#define VIRTIO_BLK_F_BLK_SIZE  6       /* Block size of disk is available*/
+#define VIRTIO_BLK_F_SCSI      7       /* Supports scsi command passthru */
+#define VIRTIO_BLK_F_FLUSH     9       /* Cache flush command support */
+#define VIRTIO_BLK_F_TOPOLOGY  10      /* Topology information is available */
+
+#define VIRTIO_BLK_ID_BYTES    20      /* ID string length */
+
+struct virtio_blk_config {
+       /* The capacity (in 512-byte sectors). */
+       u64_t capacity;
+       /* The maximum segment size (if VIRTIO_BLK_F_SIZE_MAX) */
+       u32_t size_max;
+       /* The maximum number of segments (if VIRTIO_BLK_F_SEG_MAX) */
+       u32_t seg_max;
+       /* geometry the device (if VIRTIO_BLK_F_GEOMETRY) */
+       struct virtio_blk_geometry {
+               u16_t cylinders;
+               u8_t heads;
+               u8_t sectors;
+       } geometry;
+
+       /* block size of device (if VIRTIO_BLK_F_BLK_SIZE) */
+       u32_t blk_size;
+
+       /* the next 4 entries are guarded by VIRTIO_BLK_F_TOPOLOGY  */
+       /* exponent for physical block per logical block. */
+       u8_t physical_block_exp;
+       /* alignment offset in logical blocks. */
+       u8_t alignment_offset;
+       /* minimum I/O size without performance penalty in logical blocks. */
+       u16_t min_io_size;
+       /* optimal sustained I/O size in logical blocks. */
+       u32_t opt_io_size;
+
+} __attribute__((packed));
+
+/*
+ * Command types
+ *
+ * Usage is a bit tricky as some bits are used as flags and some are not.
+ *
+ * Rules:
+ *   VIRTIO_BLK_T_OUT may be combined with VIRTIO_BLK_T_SCSI_CMD or
+ *   VIRTIO_BLK_T_BARRIER.  VIRTIO_BLK_T_FLUSH is a command of its own
+ *   and may not be combined with any of the other flags.
+ */
+
+/* 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
+
+/* Cache flush command */
+#define VIRTIO_BLK_T_FLUSH     4
+
+/* Get device ID command */
+#define VIRTIO_BLK_T_GET_ID    8
+
+/* 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* */
+       u32_t type;
+       /* io priority. */
+       u32_t ioprio;
+       /* Sector (ie. 512 byte offset) */
+       u64_t sector;
+};
+
+struct virtio_scsi_inhdr {
+       u32_t errors;
+       u32_t data_len;
+       u32_t sense_len;
+       u32_t residual;
+};
+
+/* And this is the final byte of the write scatter-gather list. */
+#define VIRTIO_BLK_S_OK                0
+#define VIRTIO_BLK_S_IOERR     1
+#define VIRTIO_BLK_S_UNSUPP    2
+#endif /* _LINUX_VIRTIO_BLK_H */
index 3ffaf7472e25b4db2c87de399340825817814cd8..de37c66b14f2a7a9cee06f3c171fc8edfd1a4a99 100644 (file)
@@ -349,6 +349,18 @@ service ahci
        ;
 };
 
+service virtio_blk
+{
+       system
+               UMAP
+               VUMAP
+               IRQCTL
+               DEVIO
+       ;
+
+       pci device      1af4/1001;
+};
+
 service at_wini
 {
        io      1f0:8           # Controller 0