1 /******************************************************************************
2 * Copyright (c) 2011 IBM Corporation
3 * All rights reserved.
4 * This program and the accompanying materials
5 * are made available under the terms of the BSD License
6 * which accompanies this distribution, and is available at
7 * http://www.opensource.org/licenses/bsd-license.php
8 *
9 * Contributors:
10 * IBM Corporation - initial implementation
11 *****************************************************************************/
12
13 #include <stdio.h>
14 #include <stdbool.h>
15 #include <cpu.h>
16 #include <helpers.h>
17 #include <byteorder.h>
18 #include "virtio.h"
19 #include "virtio-blk.h"
20 #include "virtio-internal.h"
21
22 #define DEFAULT_SECTOR_SIZE 512
23 #define DRIVER_FEATURE_SUPPORT (VIRTIO_BLK_F_BLK_SIZE | VIRTIO_F_VERSION_1)
24
25 /**
26 * Initialize virtio-block device.
27 * @param dev pointer to virtio device information
28 */
29 int
virtioblk_init(struct virtio_device * dev)30 virtioblk_init(struct virtio_device *dev)
31 {
32 struct vqs *vq;
33 int blk_size = DEFAULT_SECTOR_SIZE;
34 uint64_t features;
35 int status = VIRTIO_STAT_ACKNOWLEDGE;
36
37 /* Reset device */
38 virtio_reset_device(dev);
39
40 /* Acknowledge device. */
41 virtio_set_status(dev, status);
42
43 /* Tell HV that we know how to drive the device. */
44 status |= VIRTIO_STAT_DRIVER;
45 virtio_set_status(dev, status);
46
47 if (dev->features & VIRTIO_F_VERSION_1) {
48 /* Negotiate features and sets FEATURES_OK if successful */
49 if (virtio_negotiate_guest_features(dev, DRIVER_FEATURE_SUPPORT))
50 goto dev_error;
51
52 virtio_get_status(dev, &status);
53 } else {
54 /* Device specific setup - we support F_BLK_SIZE */
55 virtio_set_guest_features(dev, VIRTIO_BLK_F_BLK_SIZE);
56 }
57
58 vq = virtio_queue_init_vq(dev, 0);
59 if (!vq)
60 goto dev_error;
61
62 /* Tell HV that setup succeeded */
63 status |= VIRTIO_STAT_DRIVER_OK;
64 virtio_set_status(dev, status);
65
66 features = virtio_get_host_features(dev);
67 if (features & VIRTIO_BLK_F_BLK_SIZE) {
68 blk_size = virtio_get_config(dev,
69 offset_of(struct virtio_blk_cfg, blk_size),
70 sizeof(blk_size));
71 }
72
73 return blk_size;
74 dev_error:
75 printf("%s: failed\n", __func__);
76 status |= VIRTIO_STAT_FAILED;
77 virtio_set_status(dev, status);
78 return 0;
79 }
80
81
82 /**
83 * Shutdown the virtio-block device.
84 * @param dev pointer to virtio device information
85 */
86 void
virtioblk_shutdown(struct virtio_device * dev)87 virtioblk_shutdown(struct virtio_device *dev)
88 {
89 /* Quiesce device */
90 virtio_set_status(dev, VIRTIO_STAT_FAILED);
91
92 /* Reset device */
93 virtio_reset_device(dev);
94 }
95
fill_blk_hdr(struct virtio_blk_req * blkhdr,bool is_modern,uint32_t type,uint32_t ioprio,uint32_t sector)96 static void fill_blk_hdr(struct virtio_blk_req *blkhdr, bool is_modern,
97 uint32_t type, uint32_t ioprio, uint32_t sector)
98 {
99 if (is_modern) {
100 blkhdr->type = cpu_to_le32(type);
101 blkhdr->ioprio = cpu_to_le32(ioprio);
102 blkhdr->sector = cpu_to_le64(sector);
103 } else {
104 blkhdr->type = type;
105 blkhdr->ioprio = ioprio;
106 blkhdr->sector = sector;
107 }
108 }
109
110 /**
111 * Read / write blocks
112 * @param reg pointer to "reg" property
113 * @param buf pointer to destination buffer
114 * @param blocknum block number of the first block that should be transfered
115 * @param cnt amount of blocks that should be transfered
116 * @param type VIRTIO_BLK_T_OUT for write, VIRTIO_BLK_T_IN for read transfers
117 * @return number of blocks that have been transfered successfully
118 */
119 int
virtioblk_transfer(struct virtio_device * dev,char * buf,uint64_t blocknum,long cnt,unsigned int type)120 virtioblk_transfer(struct virtio_device *dev, char *buf, uint64_t blocknum,
121 long cnt, unsigned int type)
122 {
123 int id;
124 static struct virtio_blk_req blkhdr;
125 //struct virtio_blk_config *blkconf;
126 uint64_t capacity;
127 uint32_t time;
128 struct vqs *vq = &dev->vq[0];
129 volatile uint8_t status = -1;
130 volatile uint16_t *current_used_idx;
131 uint16_t last_used_idx, avail_idx;
132 int blk_size = DEFAULT_SECTOR_SIZE;
133
134 //printf("virtioblk_transfer: dev=%p buf=%p blocknum=%lli cnt=%li type=%i\n",
135 // dev, buf, blocknum, cnt, type);
136
137 /* Check whether request is within disk capacity */
138 capacity = virtio_get_config(dev,
139 offset_of(struct virtio_blk_cfg, capacity),
140 sizeof(capacity));
141 if (blocknum + cnt - 1 > capacity) {
142 puts("virtioblk_transfer: Access beyond end of device!");
143 return 0;
144 }
145
146 blk_size = virtio_get_config(dev,
147 offset_of(struct virtio_blk_cfg, blk_size),
148 sizeof(blk_size));
149 if (blk_size % DEFAULT_SECTOR_SIZE) {
150 fprintf(stderr, "virtio-blk: Unaligned sector size %d\n", blk_size);
151 return 0;
152 }
153
154 avail_idx = virtio_modern16_to_cpu(dev, vq->avail->idx);
155
156 last_used_idx = vq->used->idx;
157 current_used_idx = &vq->used->idx;
158
159 /* Set up header */
160 fill_blk_hdr(&blkhdr, dev->features, type | VIRTIO_BLK_T_BARRIER,
161 1, blocknum * blk_size / DEFAULT_SECTOR_SIZE);
162
163 /* Determine descriptor index */
164 id = (avail_idx * 3) % vq->size;
165
166 /* Set up virtqueue descriptor for header */
167 virtio_fill_desc(vq, id, dev->features, (uint64_t)&blkhdr,
168 sizeof(struct virtio_blk_req),
169 VRING_DESC_F_NEXT, id + 1);
170
171 /* Set up virtqueue descriptor for data */
172 virtio_fill_desc(vq, id + 1, dev->features, (uint64_t)buf,
173 cnt * blk_size,
174 VRING_DESC_F_NEXT | ((type & 1) ? 0 : VRING_DESC_F_WRITE),
175 id + 2);
176
177 /* Set up virtqueue descriptor for status */
178 virtio_fill_desc(vq, id + 2, dev->features,
179 (uint64_t)&status, 1,
180 VRING_DESC_F_WRITE, 0);
181
182 vq->avail->ring[avail_idx % vq->size] = virtio_cpu_to_modern16 (dev, id);
183 mb();
184 vq->avail->idx = virtio_cpu_to_modern16(dev, avail_idx + 1);
185
186 /* Tell HV that the queue is ready */
187 virtio_queue_notify(dev, 0);
188
189 /* Wait for host to consume the descriptor */
190 time = SLOF_GetTimer() + VIRTIO_TIMEOUT;
191 while (*current_used_idx == last_used_idx) {
192 // do something better
193 mb();
194 if (time < SLOF_GetTimer())
195 break;
196 }
197
198 virtio_free_desc(vq, id, dev->features);
199 virtio_free_desc(vq, id + 1, dev->features);
200 virtio_free_desc(vq, id + 2, dev->features);
201
202 if (status == 0)
203 return cnt;
204
205 printf("virtioblk_transfer failed! type=%i, status = %i\n",
206 type, status);
207
208 return 0;
209 }
210