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