virtio: Add block driver support
authorTuomas Tynkkynen <tuomas.tynkkynen@iki.fi>
Mon, 15 Oct 2018 09:21:11 +0000 (02:21 -0700)
committerSimon Glass <sjg@chromium.org>
Wed, 14 Nov 2018 17:16:27 +0000 (09:16 -0800)
This adds virtio block device driver support.

Signed-off-by: Tuomas Tynkkynen <tuomas.tynkkynen@iki.fi>
Signed-off-by: Bin Meng <bmeng.cn@gmail.com>
Reviewed-by: Simon Glass <sjg@chromium.org>
drivers/virtio/Kconfig
drivers/virtio/Makefile
drivers/virtio/virtio_blk.c [new file with mode: 0644]
drivers/virtio/virtio_blk.h [new file with mode: 0644]

index e20dd69..b72477a 100644 (file)
@@ -36,4 +36,11 @@ config VIRTIO_NET
          This is the virtual net driver for virtio. It can be used with
          QEMU based targets.
 
+config VIRTIO_BLK
+       bool "virtio block driver"
+       depends on VIRTIO
+       help
+         This is the virtual block driver for virtio. It can be used with
+         QEMU based targets.
+
 endmenu
index b7764f1..5fe7428 100644 (file)
@@ -6,3 +6,4 @@
 obj-y += virtio-uclass.o virtio_ring.o
 obj-$(CONFIG_VIRTIO_MMIO) += virtio_mmio.o
 obj-$(CONFIG_VIRTIO_NET) += virtio_net.o
+obj-$(CONFIG_VIRTIO_BLK) += virtio_blk.o
diff --git a/drivers/virtio/virtio_blk.c b/drivers/virtio/virtio_blk.c
new file mode 100644 (file)
index 0000000..e793e34
--- /dev/null
@@ -0,0 +1,137 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2018, Tuomas Tynkkynen <tuomas.tynkkynen@iki.fi>
+ * Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com>
+ */
+
+#include <common.h>
+#include <blk.h>
+#include <dm.h>
+#include <virtio_types.h>
+#include <virtio.h>
+#include <virtio_ring.h>
+#include "virtio_blk.h"
+
+struct virtio_blk_priv {
+       struct virtqueue *vq;
+};
+
+static ulong virtio_blk_do_req(struct udevice *dev, u64 sector,
+                              lbaint_t blkcnt, void *buffer, u32 type)
+{
+       struct virtio_blk_priv *priv = dev_get_priv(dev);
+       unsigned int num_out = 0, num_in = 0;
+       struct virtio_sg *sgs[3];
+       u8 status;
+       int ret;
+
+       struct virtio_blk_outhdr out_hdr = {
+               .type = cpu_to_virtio32(dev, type),
+               .sector = cpu_to_virtio64(dev, sector),
+       };
+       struct virtio_sg hdr_sg = { &out_hdr, sizeof(out_hdr) };
+       struct virtio_sg data_sg = { buffer, blkcnt * 512 };
+       struct virtio_sg status_sg = { &status, sizeof(status) };
+
+       sgs[num_out++] = &hdr_sg;
+
+       if (type & VIRTIO_BLK_T_OUT)
+               sgs[num_out++] = &data_sg;
+       else
+               sgs[num_out + num_in++] = &data_sg;
+
+       sgs[num_out + num_in++] = &status_sg;
+
+       ret = virtqueue_add(priv->vq, sgs, num_out, num_in);
+       if (ret)
+               return ret;
+
+       virtqueue_kick(priv->vq);
+
+       while (!virtqueue_get_buf(priv->vq, NULL))
+               ;
+
+       return status == VIRTIO_BLK_S_OK ? blkcnt : -EIO;
+}
+
+static ulong virtio_blk_read(struct udevice *dev, lbaint_t start,
+                            lbaint_t blkcnt, void *buffer)
+{
+       return virtio_blk_do_req(dev, start, blkcnt, buffer,
+                                VIRTIO_BLK_T_IN);
+}
+
+static ulong virtio_blk_write(struct udevice *dev, lbaint_t start,
+                             lbaint_t blkcnt, const void *buffer)
+{
+       return virtio_blk_do_req(dev, start, blkcnt, (void *)buffer,
+                                VIRTIO_BLK_T_OUT);
+}
+
+static int virtio_blk_bind(struct udevice *dev)
+{
+       struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(dev->parent);
+       struct blk_desc *desc = dev_get_uclass_platdata(dev);
+       int devnum;
+
+       desc->if_type = IF_TYPE_VIRTIO;
+       /*
+        * Initialize the devnum to -ENODEV. This is to make sure that
+        * blk_next_free_devnum() works as expected, since the default
+        * value 0 is a valid devnum.
+        */
+       desc->devnum = -ENODEV;
+       devnum = blk_next_free_devnum(IF_TYPE_VIRTIO);
+       if (devnum < 0)
+               return devnum;
+       desc->devnum = devnum;
+       desc->part_type = PART_TYPE_UNKNOWN;
+       /*
+        * virtio mmio transport supplies string identification for us,
+        * while pci trnasport uses a 2-byte subvendor value.
+        */
+       if (uc_priv->vendor >> 16)
+               sprintf(desc->vendor, "%s", (char *)&uc_priv->vendor);
+       else
+               sprintf(desc->vendor, "%04x", uc_priv->vendor);
+       desc->bdev = dev;
+
+       /* Indicate what driver features we support */
+       virtio_driver_features_init(uc_priv, NULL, 0, NULL, 0);
+
+       return 0;
+}
+
+static int virtio_blk_probe(struct udevice *dev)
+{
+       struct virtio_blk_priv *priv = dev_get_priv(dev);
+       struct blk_desc *desc = dev_get_uclass_platdata(dev);
+       u64 cap;
+       int ret;
+
+       ret = virtio_find_vqs(dev, 1, &priv->vq);
+       if (ret)
+               return ret;
+
+       desc->blksz = 512;
+       virtio_cread(dev, struct virtio_blk_config, capacity, &cap);
+       desc->lba = cap;
+
+       return 0;
+}
+
+static const struct blk_ops virtio_blk_ops = {
+       .read   = virtio_blk_read,
+       .write  = virtio_blk_write,
+};
+
+U_BOOT_DRIVER(virtio_blk) = {
+       .name   = VIRTIO_BLK_DRV_NAME,
+       .id     = UCLASS_BLK,
+       .ops    = &virtio_blk_ops,
+       .bind   = virtio_blk_bind,
+       .probe  = virtio_blk_probe,
+       .remove = virtio_reset,
+       .priv_auto_alloc_size = sizeof(struct virtio_blk_priv),
+       .flags  = DM_FLAG_ACTIVE_DMA,
+};
diff --git a/drivers/virtio/virtio_blk.h b/drivers/virtio/virtio_blk.h
new file mode 100644 (file)
index 0000000..8d8e02f
--- /dev/null
@@ -0,0 +1,129 @@
+/* SPDX-License-Identifier: BSD-3-Clause */
+/*
+ * Copyright (C) 2018, Tuomas Tynkkynen <tuomas.tynkkynen@iki.fi>
+ * Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com>
+ *
+ * From Linux kernel include/uapi/linux/virtio_blk.h
+ */
+
+#ifndef _LINUX_VIRTIO_BLK_H
+#define _LINUX_VIRTIO_BLK_H
+
+/* Feature bits */
+#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_TOPOLOGY  10      /* Topology information is available */
+#define VIRTIO_BLK_F_MQ                12      /* Support more than one vq */
+
+/* Legacy feature bits */
+#ifndef VIRTIO_BLK_NO_LEGACY
+#define VIRTIO_BLK_F_BARRIER   0       /* Does host support barriers? */
+#define VIRTIO_BLK_F_SCSI      7       /* Supports scsi command passthru */
+#define VIRTIO_BLK_F_FLUSH     9       /* Flush command supported */
+#define VIRTIO_BLK_F_CONFIG_WCE        11      /* Writeback mode available in config */
+#ifndef __KERNEL__
+/* Old (deprecated) name for VIRTIO_BLK_F_FLUSH */
+#define VIRTIO_BLK_F_WCE       VIRTIO_BLK_F_FLUSH
+#endif
+#endif /* !VIRTIO_BLK_NO_LEGACY */
+
+#define VIRTIO_BLK_ID_BYTES    20      /* ID string length */
+
+struct __packed virtio_blk_config {
+       /* The capacity (in 512-byte sectors) */
+       __u64 capacity;
+       /* The maximum segment size (if VIRTIO_BLK_F_SIZE_MAX) */
+       __u32 size_max;
+       /* The maximum number of segments (if VIRTIO_BLK_F_SEG_MAX) */
+       __u32 seg_max;
+       /* geometry of the device (if VIRTIO_BLK_F_GEOMETRY) */
+       struct virtio_blk_geometry {
+               __u16 cylinders;
+               __u8 heads;
+               __u8 sectors;
+       } geometry;
+
+       /* block size of device (if VIRTIO_BLK_F_BLK_SIZE) */
+       __u32 blk_size;
+
+       /* the next 4 entries are guarded by VIRTIO_BLK_F_TOPOLOGY */
+       /* exponent for physical block per logical block */
+       __u8 physical_block_exp;
+       /* alignment offset in logical blocks */
+       __u8 alignment_offset;
+       /* minimum I/O size without performance penalty in logical blocks */
+       __u16 min_io_size;
+       /* optimal sustained I/O size in logical blocks */
+       __u32 opt_io_size;
+
+       /* writeback mode (if VIRTIO_BLK_F_CONFIG_WCE) */
+       __u8 wce;
+       __u8 unused;
+
+       /* number of vqs, only available when VIRTIO_BLK_F_MQ is set */
+       __u16 num_queues;
+};
+
+/*
+ * 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
+
+#ifndef VIRTIO_BLK_NO_LEGACY
+/* This bit says it's a scsi command, not an actual read or write */
+#define VIRTIO_BLK_T_SCSI_CMD  2
+#endif /* VIRTIO_BLK_NO_LEGACY */
+
+/* Cache flush command */
+#define VIRTIO_BLK_T_FLUSH     4
+
+/* Get device ID command */
+#define VIRTIO_BLK_T_GET_ID    8
+
+#ifndef VIRTIO_BLK_NO_LEGACY
+/* Barrier before this op */
+#define VIRTIO_BLK_T_BARRIER   0x80000000
+#endif /* !VIRTIO_BLK_NO_LEGACY */
+
+/*
+ * This comes first in the read scatter-gather list.
+ * For legacy virtio, if VIRTIO_F_ANY_LAYOUT is not negotiated,
+ * this is the first element of the read scatter-gather list.
+ */
+struct virtio_blk_outhdr {
+       /* VIRTIO_BLK_T* */
+       __virtio32 type;
+       /* io priority */
+       __virtio32 ioprio;
+       /* Sector (ie. 512 byte offset) */
+       __virtio64 sector;
+};
+
+#ifndef VIRTIO_BLK_NO_LEGACY
+struct virtio_scsi_inhdr {
+       __virtio32 errors;
+       __virtio32 data_len;
+       __virtio32 sense_len;
+       __virtio32 residual;
+};
+#endif /* !VIRTIO_BLK_NO_LEGACY */
+
+/* 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 */