From f4802209e59da759b2d1bfcf136d6855984a5d05 Mon Sep 17 00:00:00 2001 From: Tuomas Tynkkynen Date: Mon, 15 Oct 2018 02:21:11 -0700 Subject: [PATCH] virtio: Add block driver support This adds virtio block device driver support. Signed-off-by: Tuomas Tynkkynen Signed-off-by: Bin Meng Reviewed-by: Simon Glass --- drivers/virtio/Kconfig | 7 ++ drivers/virtio/Makefile | 1 + drivers/virtio/virtio_blk.c | 137 ++++++++++++++++++++++++++++++++++++ drivers/virtio/virtio_blk.h | 129 +++++++++++++++++++++++++++++++++ 4 files changed, 274 insertions(+) create mode 100644 drivers/virtio/virtio_blk.c create mode 100644 drivers/virtio/virtio_blk.h diff --git a/drivers/virtio/Kconfig b/drivers/virtio/Kconfig index e20dd69395..b72477ae28 100644 --- a/drivers/virtio/Kconfig +++ b/drivers/virtio/Kconfig @@ -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 diff --git a/drivers/virtio/Makefile b/drivers/virtio/Makefile index b7764f161e..5fe742815b 100644 --- a/drivers/virtio/Makefile +++ b/drivers/virtio/Makefile @@ -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 index 0000000000..e793e34e83 --- /dev/null +++ b/drivers/virtio/virtio_blk.c @@ -0,0 +1,137 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2018, Tuomas Tynkkynen + * Copyright (C) 2018, Bin Meng + */ + +#include +#include +#include +#include +#include +#include +#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 index 0000000000..8d8e02fa2e --- /dev/null +++ b/drivers/virtio/virtio_blk.h @@ -0,0 +1,129 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Copyright (C) 2018, Tuomas Tynkkynen + * Copyright (C) 2018, Bin Meng + * + * 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 */ -- 2.39.5