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