diff options
author | Tuomas Tynkkynen | 2018-10-15 04:21:11 -0500 |
---|---|---|
committer | Simon Glass | 2018-11-14 11:16:27 -0600 |
commit | f4802209e59da759b2d1bfcf136d6855984a5d05 (patch) | |
tree | 31194bd1811f0efc4792c4f763375fead29d1c21 /drivers | |
parent | 4ad54ec4d5c98a297f8df2fe9a630a534557f717 (diff) | |
download | u-boot-f4802209e59da759b2d1bfcf136d6855984a5d05.tar.gz u-boot-f4802209e59da759b2d1bfcf136d6855984a5d05.tar.xz u-boot-f4802209e59da759b2d1bfcf136d6855984a5d05.zip |
virtio: Add block driver support
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>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/virtio/Kconfig | 7 | ||||
-rw-r--r-- | drivers/virtio/Makefile | 1 | ||||
-rw-r--r-- | drivers/virtio/virtio_blk.c | 137 | ||||
-rw-r--r-- | drivers/virtio/virtio_blk.h | 129 |
4 files changed, 274 insertions, 0 deletions
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 | |||
36 | This is the virtual net driver for virtio. It can be used with | 36 | This is the virtual net driver for virtio. It can be used with |
37 | QEMU based targets. | 37 | QEMU based targets. |
38 | 38 | ||
39 | config VIRTIO_BLK | ||
40 | bool "virtio block driver" | ||
41 | depends on VIRTIO | ||
42 | help | ||
43 | This is the virtual block driver for virtio. It can be used with | ||
44 | QEMU based targets. | ||
45 | |||
39 | endmenu | 46 | 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 @@ | |||
6 | obj-y += virtio-uclass.o virtio_ring.o | 6 | obj-y += virtio-uclass.o virtio_ring.o |
7 | obj-$(CONFIG_VIRTIO_MMIO) += virtio_mmio.o | 7 | obj-$(CONFIG_VIRTIO_MMIO) += virtio_mmio.o |
8 | obj-$(CONFIG_VIRTIO_NET) += virtio_net.o | 8 | obj-$(CONFIG_VIRTIO_NET) += virtio_net.o |
9 | 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 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0+ | ||
2 | /* | ||
3 | * Copyright (C) 2018, Tuomas Tynkkynen <tuomas.tynkkynen@iki.fi> | ||
4 | * Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com> | ||
5 | */ | ||
6 | |||
7 | #include <common.h> | ||
8 | #include <blk.h> | ||
9 | #include <dm.h> | ||
10 | #include <virtio_types.h> | ||
11 | #include <virtio.h> | ||
12 | #include <virtio_ring.h> | ||
13 | #include "virtio_blk.h" | ||
14 | |||
15 | struct virtio_blk_priv { | ||
16 | struct virtqueue *vq; | ||
17 | }; | ||
18 | |||
19 | static ulong virtio_blk_do_req(struct udevice *dev, u64 sector, | ||
20 | lbaint_t blkcnt, void *buffer, u32 type) | ||
21 | { | ||
22 | struct virtio_blk_priv *priv = dev_get_priv(dev); | ||
23 | unsigned int num_out = 0, num_in = 0; | ||
24 | struct virtio_sg *sgs[3]; | ||
25 | u8 status; | ||
26 | int ret; | ||
27 | |||
28 | struct virtio_blk_outhdr out_hdr = { | ||
29 | .type = cpu_to_virtio32(dev, type), | ||
30 | .sector = cpu_to_virtio64(dev, sector), | ||
31 | }; | ||
32 | struct virtio_sg hdr_sg = { &out_hdr, sizeof(out_hdr) }; | ||
33 | struct virtio_sg data_sg = { buffer, blkcnt * 512 }; | ||
34 | struct virtio_sg status_sg = { &status, sizeof(status) }; | ||
35 | |||
36 | sgs[num_out++] = &hdr_sg; | ||
37 | |||
38 | if (type & VIRTIO_BLK_T_OUT) | ||
39 | sgs[num_out++] = &data_sg; | ||
40 | else | ||
41 | sgs[num_out + num_in++] = &data_sg; | ||
42 | |||
43 | sgs[num_out + num_in++] = &status_sg; | ||
44 | |||
45 | ret = virtqueue_add(priv->vq, sgs, num_out, num_in); | ||
46 | if (ret) | ||
47 | return ret; | ||
48 | |||
49 | virtqueue_kick(priv->vq); | ||
50 | |||
51 | while (!virtqueue_get_buf(priv->vq, NULL)) | ||
52 | ; | ||
53 | |||
54 | return status == VIRTIO_BLK_S_OK ? blkcnt : -EIO; | ||
55 | } | ||
56 | |||
57 | static ulong virtio_blk_read(struct udevice *dev, lbaint_t start, | ||
58 | lbaint_t blkcnt, void *buffer) | ||
59 | { | ||
60 | return virtio_blk_do_req(dev, start, blkcnt, buffer, | ||
61 | VIRTIO_BLK_T_IN); | ||
62 | } | ||
63 | |||
64 | static ulong virtio_blk_write(struct udevice *dev, lbaint_t start, | ||
65 | lbaint_t blkcnt, const void *buffer) | ||
66 | { | ||
67 | return virtio_blk_do_req(dev, start, blkcnt, (void *)buffer, | ||
68 | VIRTIO_BLK_T_OUT); | ||
69 | } | ||
70 | |||
71 | static int virtio_blk_bind(struct udevice *dev) | ||
72 | { | ||
73 | struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(dev->parent); | ||
74 | struct blk_desc *desc = dev_get_uclass_platdata(dev); | ||
75 | int devnum; | ||
76 | |||
77 | desc->if_type = IF_TYPE_VIRTIO; | ||
78 | /* | ||
79 | * Initialize the devnum to -ENODEV. This is to make sure that | ||
80 | * blk_next_free_devnum() works as expected, since the default | ||
81 | * value 0 is a valid devnum. | ||
82 | */ | ||
83 | desc->devnum = -ENODEV; | ||
84 | devnum = blk_next_free_devnum(IF_TYPE_VIRTIO); | ||
85 | if (devnum < 0) | ||
86 | return devnum; | ||
87 | desc->devnum = devnum; | ||
88 | desc->part_type = PART_TYPE_UNKNOWN; | ||
89 | /* | ||
90 | * virtio mmio transport supplies string identification for us, | ||
91 | * while pci trnasport uses a 2-byte subvendor value. | ||
92 | */ | ||
93 | if (uc_priv->vendor >> 16) | ||
94 | sprintf(desc->vendor, "%s", (char *)&uc_priv->vendor); | ||
95 | else | ||
96 | sprintf(desc->vendor, "%04x", uc_priv->vendor); | ||
97 | desc->bdev = dev; | ||
98 | |||
99 | /* Indicate what driver features we support */ | ||
100 | virtio_driver_features_init(uc_priv, NULL, 0, NULL, 0); | ||
101 | |||
102 | return 0; | ||
103 | } | ||
104 | |||
105 | static int virtio_blk_probe(struct udevice *dev) | ||
106 | { | ||
107 | struct virtio_blk_priv *priv = dev_get_priv(dev); | ||
108 | struct blk_desc *desc = dev_get_uclass_platdata(dev); | ||
109 | u64 cap; | ||
110 | int ret; | ||
111 | |||
112 | ret = virtio_find_vqs(dev, 1, &priv->vq); | ||
113 | if (ret) | ||
114 | return ret; | ||
115 | |||
116 | desc->blksz = 512; | ||
117 | virtio_cread(dev, struct virtio_blk_config, capacity, &cap); | ||
118 | desc->lba = cap; | ||
119 | |||
120 | return 0; | ||
121 | } | ||
122 | |||
123 | static const struct blk_ops virtio_blk_ops = { | ||
124 | .read = virtio_blk_read, | ||
125 | .write = virtio_blk_write, | ||
126 | }; | ||
127 | |||
128 | U_BOOT_DRIVER(virtio_blk) = { | ||
129 | .name = VIRTIO_BLK_DRV_NAME, | ||
130 | .id = UCLASS_BLK, | ||
131 | .ops = &virtio_blk_ops, | ||
132 | .bind = virtio_blk_bind, | ||
133 | .probe = virtio_blk_probe, | ||
134 | .remove = virtio_reset, | ||
135 | .priv_auto_alloc_size = sizeof(struct virtio_blk_priv), | ||
136 | .flags = DM_FLAG_ACTIVE_DMA, | ||
137 | }; | ||
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 @@ | |||
1 | /* SPDX-License-Identifier: BSD-3-Clause */ | ||
2 | /* | ||
3 | * Copyright (C) 2018, Tuomas Tynkkynen <tuomas.tynkkynen@iki.fi> | ||
4 | * Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com> | ||
5 | * | ||
6 | * From Linux kernel include/uapi/linux/virtio_blk.h | ||
7 | */ | ||
8 | |||
9 | #ifndef _LINUX_VIRTIO_BLK_H | ||
10 | #define _LINUX_VIRTIO_BLK_H | ||
11 | |||
12 | /* Feature bits */ | ||
13 | #define VIRTIO_BLK_F_SIZE_MAX 1 /* Indicates maximum segment size */ | ||
14 | #define VIRTIO_BLK_F_SEG_MAX 2 /* Indicates maximum # of segments */ | ||
15 | #define VIRTIO_BLK_F_GEOMETRY 4 /* Legacy geometry available */ | ||
16 | #define VIRTIO_BLK_F_RO 5 /* Disk is read-only */ | ||
17 | #define VIRTIO_BLK_F_BLK_SIZE 6 /* Block size of disk is available */ | ||
18 | #define VIRTIO_BLK_F_TOPOLOGY 10 /* Topology information is available */ | ||
19 | #define VIRTIO_BLK_F_MQ 12 /* Support more than one vq */ | ||
20 | |||
21 | /* Legacy feature bits */ | ||
22 | #ifndef VIRTIO_BLK_NO_LEGACY | ||
23 | #define VIRTIO_BLK_F_BARRIER 0 /* Does host support barriers? */ | ||
24 | #define VIRTIO_BLK_F_SCSI 7 /* Supports scsi command passthru */ | ||
25 | #define VIRTIO_BLK_F_FLUSH 9 /* Flush command supported */ | ||
26 | #define VIRTIO_BLK_F_CONFIG_WCE 11 /* Writeback mode available in config */ | ||
27 | #ifndef __KERNEL__ | ||
28 | /* Old (deprecated) name for VIRTIO_BLK_F_FLUSH */ | ||
29 | #define VIRTIO_BLK_F_WCE VIRTIO_BLK_F_FLUSH | ||
30 | #endif | ||
31 | #endif /* !VIRTIO_BLK_NO_LEGACY */ | ||
32 | |||
33 | #define VIRTIO_BLK_ID_BYTES 20 /* ID string length */ | ||
34 | |||
35 | struct __packed virtio_blk_config { | ||
36 | /* The capacity (in 512-byte sectors) */ | ||
37 | __u64 capacity; | ||
38 | /* The maximum segment size (if VIRTIO_BLK_F_SIZE_MAX) */ | ||
39 | __u32 size_max; | ||
40 | /* The maximum number of segments (if VIRTIO_BLK_F_SEG_MAX) */ | ||
41 | __u32 seg_max; | ||
42 | /* geometry of the device (if VIRTIO_BLK_F_GEOMETRY) */ | ||
43 | struct virtio_blk_geometry { | ||
44 | __u16 cylinders; | ||
45 | __u8 heads; | ||
46 | __u8 sectors; | ||
47 | } geometry; | ||
48 | |||
49 | /* block size of device (if VIRTIO_BLK_F_BLK_SIZE) */ | ||
50 | __u32 blk_size; | ||
51 | |||
52 | /* the next 4 entries are guarded by VIRTIO_BLK_F_TOPOLOGY */ | ||
53 | /* exponent for physical block per logical block */ | ||
54 | __u8 physical_block_exp; | ||
55 | /* alignment offset in logical blocks */ | ||
56 | __u8 alignment_offset; | ||
57 | /* minimum I/O size without performance penalty in logical blocks */ | ||
58 | __u16 min_io_size; | ||
59 | /* optimal sustained I/O size in logical blocks */ | ||
60 | __u32 opt_io_size; | ||
61 | |||
62 | /* writeback mode (if VIRTIO_BLK_F_CONFIG_WCE) */ | ||
63 | __u8 wce; | ||
64 | __u8 unused; | ||
65 | |||
66 | /* number of vqs, only available when VIRTIO_BLK_F_MQ is set */ | ||
67 | __u16 num_queues; | ||
68 | }; | ||
69 | |||
70 | /* | ||
71 | * Command types | ||
72 | * | ||
73 | * Usage is a bit tricky as some bits are used as flags and some are not. | ||
74 | * | ||
75 | * Rules: | ||
76 | * VIRTIO_BLK_T_OUT may be combined with VIRTIO_BLK_T_SCSI_CMD or | ||
77 | * VIRTIO_BLK_T_BARRIER. VIRTIO_BLK_T_FLUSH is a command of its own | ||
78 | * and may not be combined with any of the other flags. | ||
79 | */ | ||
80 | |||
81 | /* These two define direction */ | ||
82 | #define VIRTIO_BLK_T_IN 0 | ||
83 | #define VIRTIO_BLK_T_OUT 1 | ||
84 | |||
85 | #ifndef VIRTIO_BLK_NO_LEGACY | ||
86 | /* This bit says it's a scsi command, not an actual read or write */ | ||
87 | #define VIRTIO_BLK_T_SCSI_CMD 2 | ||
88 | #endif /* VIRTIO_BLK_NO_LEGACY */ | ||
89 | |||
90 | /* Cache flush command */ | ||
91 | #define VIRTIO_BLK_T_FLUSH 4 | ||
92 | |||
93 | /* Get device ID command */ | ||
94 | #define VIRTIO_BLK_T_GET_ID 8 | ||
95 | |||
96 | #ifndef VIRTIO_BLK_NO_LEGACY | ||
97 | /* Barrier before this op */ | ||
98 | #define VIRTIO_BLK_T_BARRIER 0x80000000 | ||
99 | #endif /* !VIRTIO_BLK_NO_LEGACY */ | ||
100 | |||
101 | /* | ||
102 | * This comes first in the read scatter-gather list. | ||
103 | * For legacy virtio, if VIRTIO_F_ANY_LAYOUT is not negotiated, | ||
104 | * this is the first element of the read scatter-gather list. | ||
105 | */ | ||
106 | struct virtio_blk_outhdr { | ||
107 | /* VIRTIO_BLK_T* */ | ||
108 | __virtio32 type; | ||
109 | /* io priority */ | ||
110 | __virtio32 ioprio; | ||
111 | /* Sector (ie. 512 byte offset) */ | ||
112 | __virtio64 sector; | ||
113 | }; | ||
114 | |||
115 | #ifndef VIRTIO_BLK_NO_LEGACY | ||
116 | struct virtio_scsi_inhdr { | ||
117 | __virtio32 errors; | ||
118 | __virtio32 data_len; | ||
119 | __virtio32 sense_len; | ||
120 | __virtio32 residual; | ||
121 | }; | ||
122 | #endif /* !VIRTIO_BLK_NO_LEGACY */ | ||
123 | |||
124 | /* And this is the final byte of the write scatter-gather list */ | ||
125 | #define VIRTIO_BLK_S_OK 0 | ||
126 | #define VIRTIO_BLK_S_IOERR 1 | ||
127 | #define VIRTIO_BLK_S_UNSUPP 2 | ||
128 | |||
129 | #endif /* _LINUX_VIRTIO_BLK_H */ | ||