Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / SLOF / lib / libvirtio / virtio-blk.c
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 <cpu.h>
15 #include <helpers.h>
16 #include "virtio.h"
17 #include "virtio-blk.h"
18
19 #define DEFAULT_SECTOR_SIZE 512
20
21 /**
22  * Initialize virtio-block device.
23  * @param  dev  pointer to virtio device information
24  */
25 int
26 virtioblk_init(struct virtio_device *dev)
27 {
28         struct vring_avail *vq_avail;
29         int blk_size = DEFAULT_SECTOR_SIZE;
30         int features;
31
32         /* Reset device */
33         // XXX That will clear the virtq base. We need to move
34         //     initializing it to here anyway
35         //
36         //       virtio_reset_device(dev);
37
38         /* Acknowledge device. */
39         virtio_set_status(dev, VIRTIO_STAT_ACKNOWLEDGE);
40
41         /* Tell HV that we know how to drive the device. */
42         virtio_set_status(dev, VIRTIO_STAT_ACKNOWLEDGE|VIRTIO_STAT_DRIVER);
43
44         /* Device specific setup - we support F_BLK_SIZE */
45         virtio_set_guest_features(dev,  VIRTIO_BLK_F_BLK_SIZE);
46
47         vq_avail = virtio_get_vring_avail(dev, 0);
48         vq_avail->flags = VRING_AVAIL_F_NO_INTERRUPT;
49         vq_avail->idx = 0;
50
51         /* Tell HV that setup succeeded */
52         virtio_set_status(dev, VIRTIO_STAT_ACKNOWLEDGE|VIRTIO_STAT_DRIVER
53                                 |VIRTIO_STAT_DRIVER_OK);
54
55         virtio_get_host_features(dev, &features);
56         if (features & VIRTIO_BLK_F_BLK_SIZE) {
57                 blk_size = virtio_get_config(dev,
58                                 offset_of(struct virtio_blk_cfg, blk_size),
59                                 sizeof(blk_size));
60         }
61
62         return blk_size;
63 }
64
65
66 /**
67  * Shutdown the virtio-block device.
68  * @param  dev  pointer to virtio device information
69  */
70 void
71 virtioblk_shutdown(struct virtio_device *dev)
72 {
73         /* Quiesce device */
74         virtio_set_status(dev, VIRTIO_STAT_FAILED);
75
76         /* Reset device */
77         virtio_reset_device(dev);
78 }
79
80
81 /**
82  * Read blocks
83  * @param  reg  pointer to "reg" property
84  * @param  buf  pointer to destination buffer
85  * @param  blocknum  block number of the first block that should be read
86  * @param  cnt  amount of blocks that should be read
87  * @return number of blocks that have been read successfully
88  */
89 int
90 virtioblk_read(struct virtio_device *dev, char *buf, long blocknum, long cnt)
91 {
92         struct vring_desc *desc;
93         int id;
94         static struct virtio_blk_req blkhdr;
95         //struct virtio_blk_config *blkconf;
96         uint64_t capacity;
97         uint32_t vq_size, time;
98         struct vring_desc *vq_desc;             /* Descriptor vring */
99         struct vring_avail *vq_avail;           /* "Available" vring */
100         struct vring_used *vq_used;             /* "Used" vring */
101         volatile uint8_t status = -1;
102         volatile uint16_t *current_used_idx;
103         uint16_t last_used_idx;
104         int blk_size = DEFAULT_SECTOR_SIZE;
105
106         //printf("virtioblk_read: dev=%p buf=%p blocknum=%li count=%li\n",
107         //      dev, buf, blocknum, cnt);
108
109         /* Check whether request is within disk capacity */
110         capacity = virtio_get_config(dev,
111                         offset_of(struct virtio_blk_cfg, capacity),
112                         sizeof(capacity));
113         if (blocknum + cnt - 1 > capacity) {
114                 puts("virtioblk_read: Access beyond end of device!");
115                 return 0;
116         }
117
118         blk_size = virtio_get_config(dev,
119                         offset_of(struct virtio_blk_cfg, blk_size),
120                         sizeof(blk_size));
121         if (blk_size % DEFAULT_SECTOR_SIZE) {
122                 fprintf(stderr, "virtio-blk: Unaligned sector read %d\n", blk_size);
123                 return 0;
124         }
125
126         vq_size = virtio_get_qsize(dev, 0);
127         vq_desc = virtio_get_vring_desc(dev, 0);
128         vq_avail = virtio_get_vring_avail(dev, 0);
129         vq_used = virtio_get_vring_used(dev, 0);
130
131         last_used_idx = vq_used->idx;
132         current_used_idx = &vq_used->idx;
133
134         /* Set up header */
135         blkhdr.type = VIRTIO_BLK_T_IN | VIRTIO_BLK_T_BARRIER;
136         blkhdr.ioprio = 1;
137         blkhdr.sector = blocknum * blk_size / DEFAULT_SECTOR_SIZE;
138
139         /* Determine descriptor index */
140         id = (vq_avail->idx * 3) % vq_size;
141
142         /* Set up virtqueue descriptor for header */
143         desc = &vq_desc[id];
144         desc->addr = (uint64_t)&blkhdr;
145         desc->len = sizeof(struct virtio_blk_req);
146         desc->flags = VRING_DESC_F_NEXT;
147         desc->next = (id + 1) % vq_size;
148
149         /* Set up virtqueue descriptor for data */
150         desc = &vq_desc[(id + 1) % vq_size];
151         desc->addr = (uint64_t)buf;
152         desc->len = cnt * blk_size;
153         desc->flags = VRING_DESC_F_NEXT | VRING_DESC_F_WRITE;
154         desc->next = (id + 2) % vq_size;
155
156         /* Set up virtqueue descriptor for status */
157         desc = &vq_desc[(id + 2) % vq_size];
158         desc->addr = (uint64_t)&status;
159         desc->len = 1;
160         desc->flags = VRING_DESC_F_WRITE;
161         desc->next = 0;
162
163         vq_avail->ring[vq_avail->idx % vq_size] = id;
164         mb();
165         vq_avail->idx += 1;
166
167         /* Tell HV that the queue is ready */
168         virtio_queue_notify(dev, 0);
169
170         /* Wait for host to consume the descriptor */
171         time = SLOF_GetTimer() + VIRTIO_TIMEOUT;
172         while (*current_used_idx == last_used_idx) {
173                 // do something better
174                 mb();
175                 if (time < SLOF_GetTimer())
176                         break;
177         }
178
179         if (status == 0)
180                 return cnt;
181
182         printf("virtioblk_read failed! status = %i\n", status);
183
184         return 0;
185 }