These changes are the raw update to qemu-2.6.
[kvmfornfv.git] / qemu / tests / libqos / virtio.c
1 /*
2  * libqos virtio driver
3  *
4  * Copyright (c) 2014 Marc MarĂ­
5  *
6  * This work is licensed under the terms of the GNU GPL, version 2 or later.
7  * See the COPYING file in the top-level directory.
8  */
9
10 #include "qemu/osdep.h"
11 #include <glib.h>
12 #include "libqtest.h"
13 #include "libqos/virtio.h"
14
15 uint8_t qvirtio_config_readb(const QVirtioBus *bus, QVirtioDevice *d,
16                                                                 uint64_t addr)
17 {
18     return bus->config_readb(d, addr);
19 }
20
21 uint16_t qvirtio_config_readw(const QVirtioBus *bus, QVirtioDevice *d,
22                                                                 uint64_t addr)
23 {
24     return bus->config_readw(d, addr);
25 }
26
27 uint32_t qvirtio_config_readl(const QVirtioBus *bus, QVirtioDevice *d,
28                                                                 uint64_t addr)
29 {
30     return bus->config_readl(d, addr);
31 }
32
33 uint64_t qvirtio_config_readq(const QVirtioBus *bus, QVirtioDevice *d,
34                                                                 uint64_t addr)
35 {
36     return bus->config_readq(d, addr);
37 }
38
39 uint32_t qvirtio_get_features(const QVirtioBus *bus, QVirtioDevice *d)
40 {
41     return bus->get_features(d);
42 }
43
44 void qvirtio_set_features(const QVirtioBus *bus, QVirtioDevice *d,
45                                                             uint32_t features)
46 {
47     bus->set_features(d, features);
48 }
49
50 QVirtQueue *qvirtqueue_setup(const QVirtioBus *bus, QVirtioDevice *d,
51                                         QGuestAllocator *alloc, uint16_t index)
52 {
53     return bus->virtqueue_setup(d, alloc, index);
54 }
55
56 void qvirtio_reset(const QVirtioBus *bus, QVirtioDevice *d)
57 {
58     bus->set_status(d, QVIRTIO_RESET);
59     g_assert_cmphex(bus->get_status(d), ==, QVIRTIO_RESET);
60 }
61
62 void qvirtio_set_acknowledge(const QVirtioBus *bus, QVirtioDevice *d)
63 {
64     bus->set_status(d, bus->get_status(d) | QVIRTIO_ACKNOWLEDGE);
65     g_assert_cmphex(bus->get_status(d), ==, QVIRTIO_ACKNOWLEDGE);
66 }
67
68 void qvirtio_set_driver(const QVirtioBus *bus, QVirtioDevice *d)
69 {
70     bus->set_status(d, bus->get_status(d) | QVIRTIO_DRIVER);
71     g_assert_cmphex(bus->get_status(d), ==,
72                                     QVIRTIO_DRIVER | QVIRTIO_ACKNOWLEDGE);
73 }
74
75 void qvirtio_set_driver_ok(const QVirtioBus *bus, QVirtioDevice *d)
76 {
77     bus->set_status(d, bus->get_status(d) | QVIRTIO_DRIVER_OK);
78     g_assert_cmphex(bus->get_status(d), ==,
79                 QVIRTIO_DRIVER_OK | QVIRTIO_DRIVER | QVIRTIO_ACKNOWLEDGE);
80 }
81
82 void qvirtio_wait_queue_isr(const QVirtioBus *bus, QVirtioDevice *d,
83                             QVirtQueue *vq, gint64 timeout_us)
84 {
85     gint64 start_time = g_get_monotonic_time();
86
87     for (;;) {
88         clock_step(100);
89         if (bus->get_queue_isr_status(d, vq)) {
90             return;
91         }
92         g_assert(g_get_monotonic_time() - start_time <= timeout_us);
93     }
94 }
95
96 /* Wait for the status byte at given guest memory address to be set
97  *
98  * The virtqueue interrupt must not be raised, making this useful for testing
99  * event_index functionality.
100  */
101 uint8_t qvirtio_wait_status_byte_no_isr(const QVirtioBus *bus,
102                                         QVirtioDevice *d,
103                                         QVirtQueue *vq,
104                                         uint64_t addr,
105                                         gint64 timeout_us)
106 {
107     gint64 start_time = g_get_monotonic_time();
108     uint8_t val;
109
110     while ((val = readb(addr)) == 0xff) {
111         clock_step(100);
112         g_assert(!bus->get_queue_isr_status(d, vq));
113         g_assert(g_get_monotonic_time() - start_time <= timeout_us);
114     }
115     return val;
116 }
117
118 void qvirtio_wait_config_isr(const QVirtioBus *bus, QVirtioDevice *d,
119                              gint64 timeout_us)
120 {
121     gint64 start_time = g_get_monotonic_time();
122
123     for (;;) {
124         clock_step(100);
125         if (bus->get_config_isr_status(d)) {
126             return;
127         }
128         g_assert(g_get_monotonic_time() - start_time <= timeout_us);
129     }
130 }
131
132 void qvring_init(const QGuestAllocator *alloc, QVirtQueue *vq, uint64_t addr)
133 {
134     int i;
135
136     vq->desc = addr;
137     vq->avail = vq->desc + vq->size*sizeof(QVRingDesc);
138     vq->used = (uint64_t)((vq->avail + sizeof(uint16_t) * (3 + vq->size)
139         + vq->align - 1) & ~(vq->align - 1));
140
141     for (i = 0; i < vq->size - 1; i++) {
142         /* vq->desc[i].addr */
143         writew(vq->desc + (16 * i), 0);
144         /* vq->desc[i].next */
145         writew(vq->desc + (16 * i) + 14, i + 1);
146     }
147
148     /* vq->avail->flags */
149     writew(vq->avail, 0);
150     /* vq->avail->idx */
151     writew(vq->avail + 2, 0);
152     /* vq->avail->used_event */
153     writew(vq->avail + 4 + (2 * vq->size), 0);
154
155     /* vq->used->flags */
156     writew(vq->used, 0);
157     /* vq->used->avail_event */
158     writew(vq->used+2+(sizeof(struct QVRingUsedElem)*vq->size), 0);
159 }
160
161 QVRingIndirectDesc *qvring_indirect_desc_setup(QVirtioDevice *d,
162                                         QGuestAllocator *alloc, uint16_t elem)
163 {
164     int i;
165     QVRingIndirectDesc *indirect = g_malloc(sizeof(*indirect));
166
167     indirect->index = 0;
168     indirect->elem = elem;
169     indirect->desc = guest_alloc(alloc, sizeof(QVRingDesc)*elem);
170
171     for (i = 0; i < elem - 1; ++i) {
172         /* indirect->desc[i].addr */
173         writeq(indirect->desc + (16 * i), 0);
174         /* indirect->desc[i].flags */
175         writew(indirect->desc + (16 * i) + 12, QVRING_DESC_F_NEXT);
176         /* indirect->desc[i].next */
177         writew(indirect->desc + (16 * i) + 14, i + 1);
178     }
179
180     return indirect;
181 }
182
183 void qvring_indirect_desc_add(QVRingIndirectDesc *indirect, uint64_t data,
184                                                     uint32_t len, bool write)
185 {
186     uint16_t flags;
187
188     g_assert_cmpint(indirect->index, <, indirect->elem);
189
190     flags = readw(indirect->desc + (16 * indirect->index) + 12);
191
192     if (write) {
193         flags |= QVRING_DESC_F_WRITE;
194     }
195
196     /* indirect->desc[indirect->index].addr */
197     writeq(indirect->desc + (16 * indirect->index), data);
198     /* indirect->desc[indirect->index].len */
199     writel(indirect->desc + (16 * indirect->index) + 8, len);
200     /* indirect->desc[indirect->index].flags */
201     writew(indirect->desc + (16 * indirect->index) + 12, flags);
202
203     indirect->index++;
204 }
205
206 uint32_t qvirtqueue_add(QVirtQueue *vq, uint64_t data, uint32_t len, bool write,
207                                                                     bool next)
208 {
209     uint16_t flags = 0;
210     vq->num_free--;
211
212     if (write) {
213         flags |= QVRING_DESC_F_WRITE;
214     }
215
216     if (next) {
217         flags |= QVRING_DESC_F_NEXT;
218     }
219
220     /* vq->desc[vq->free_head].addr */
221     writeq(vq->desc + (16 * vq->free_head), data);
222     /* vq->desc[vq->free_head].len */
223     writel(vq->desc + (16 * vq->free_head) + 8, len);
224     /* vq->desc[vq->free_head].flags */
225     writew(vq->desc + (16 * vq->free_head) + 12, flags);
226
227     return vq->free_head++; /* Return and increase, in this order */
228 }
229
230 uint32_t qvirtqueue_add_indirect(QVirtQueue *vq, QVRingIndirectDesc *indirect)
231 {
232     g_assert(vq->indirect);
233     g_assert_cmpint(vq->size, >=, indirect->elem);
234     g_assert_cmpint(indirect->index, ==, indirect->elem);
235
236     vq->num_free--;
237
238     /* vq->desc[vq->free_head].addr */
239     writeq(vq->desc + (16 * vq->free_head), indirect->desc);
240     /* vq->desc[vq->free_head].len */
241     writel(vq->desc + (16 * vq->free_head) + 8,
242                                         sizeof(QVRingDesc) * indirect->elem);
243     /* vq->desc[vq->free_head].flags */
244     writew(vq->desc + (16 * vq->free_head) + 12, QVRING_DESC_F_INDIRECT);
245
246     return vq->free_head++; /* Return and increase, in this order */
247 }
248
249 void qvirtqueue_kick(const QVirtioBus *bus, QVirtioDevice *d, QVirtQueue *vq,
250                                                             uint32_t free_head)
251 {
252     /* vq->avail->idx */
253     uint16_t idx = readl(vq->avail + 2);
254     /* vq->used->flags */
255     uint16_t flags;
256     /* vq->used->avail_event */
257     uint16_t avail_event;
258
259     /* vq->avail->ring[idx % vq->size] */
260     writel(vq->avail + 4 + (2 * (idx % vq->size)), free_head);
261     /* vq->avail->idx */
262     writel(vq->avail + 2, idx + 1);
263
264     /* Must read after idx is updated */
265     flags = readw(vq->avail);
266     avail_event = readw(vq->used + 4 +
267                                 (sizeof(struct QVRingUsedElem) * vq->size));
268
269     /* < 1 because we add elements to avail queue one by one */
270     if ((flags & QVRING_USED_F_NO_NOTIFY) == 0 &&
271                             (!vq->event || (uint16_t)(idx-avail_event) < 1)) {
272         bus->virtqueue_kick(d, vq);
273     }
274 }
275
276 void qvirtqueue_set_used_event(QVirtQueue *vq, uint16_t idx)
277 {
278     g_assert(vq->event);
279
280     /* vq->avail->used_event */
281     writew(vq->avail + 4 + (2 * vq->size), idx);
282 }