xref: /qemu/tests/qtest/libqos/virtio.c (revision 58368113989403775496b3422f22094713703157)
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 <glib.h>
11 #include "libqtest.h"
12 #include "libqos/virtio.h"
13 
14 uint8_t qvirtio_config_readb(const QVirtioBus *bus, QVirtioDevice *d,
15                                                                 void *addr)
16 {
17     return bus->config_readb(d, addr);
18 }
19 
20 uint16_t qvirtio_config_readw(const QVirtioBus *bus, QVirtioDevice *d,
21                                                                 void *addr)
22 {
23     return bus->config_readw(d, addr);
24 }
25 
26 uint32_t qvirtio_config_readl(const QVirtioBus *bus, QVirtioDevice *d,
27                                                                 void *addr)
28 {
29     return bus->config_readl(d, addr);
30 }
31 
32 uint64_t qvirtio_config_readq(const QVirtioBus *bus, QVirtioDevice *d,
33                                                                 void *addr)
34 {
35     return bus->config_readq(d, addr);
36 }
37 
38 uint32_t qvirtio_get_features(const QVirtioBus *bus, QVirtioDevice *d)
39 {
40     return bus->get_features(d);
41 }
42 
43 void qvirtio_set_features(const QVirtioBus *bus, QVirtioDevice *d,
44                                                             uint32_t features)
45 {
46     bus->set_features(d, features);
47 }
48 
49 QVirtQueue *qvirtqueue_setup(const QVirtioBus *bus, QVirtioDevice *d,
50                                         QGuestAllocator *alloc, uint16_t index)
51 {
52     return bus->virtqueue_setup(d, alloc, index);
53 }
54 
55 void 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 
61 void 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 
67 void 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 
74 void 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 
81 bool qvirtio_wait_queue_isr(const QVirtioBus *bus, QVirtioDevice *d,
82                                             QVirtQueue *vq, uint64_t timeout)
83 {
84     do {
85         clock_step(10);
86         if (bus->get_queue_isr_status(d, vq)) {
87             break; /* It has ended */
88         }
89     } while (--timeout);
90 
91     return timeout != 0;
92 }
93 
94 bool qvirtio_wait_config_isr(const QVirtioBus *bus, QVirtioDevice *d,
95                                                             uint64_t timeout)
96 {
97     do {
98         clock_step(10);
99         if (bus->get_config_isr_status(d)) {
100             break; /* It has ended */
101         }
102     } while (--timeout);
103 
104     return timeout != 0;
105 }
106 
107 void qvring_init(const QGuestAllocator *alloc, QVirtQueue *vq, uint64_t addr)
108 {
109     int i;
110 
111     vq->desc = addr;
112     vq->avail = vq->desc + vq->size*sizeof(QVRingDesc);
113     vq->used = (uint64_t)((vq->avail + sizeof(uint16_t) * (3 + vq->size)
114         + vq->align - 1) & ~(vq->align - 1));
115 
116     for (i = 0; i < vq->size - 1; i++) {
117         /* vq->desc[i].addr */
118         writew(vq->desc + (16 * i), 0);
119         /* vq->desc[i].next */
120         writew(vq->desc + (16 * i) + 14, i + 1);
121     }
122 
123     /* vq->avail->flags */
124     writew(vq->avail, 0);
125     /* vq->avail->idx */
126     writew(vq->avail + 2, 0);
127 
128     /* vq->used->flags */
129     writew(vq->used, 0);
130 }
131 
132 QVRingIndirectDesc *qvring_indirect_desc_setup(QVirtioDevice *d,
133                                         QGuestAllocator *alloc, uint16_t elem)
134 {
135     int i;
136     QVRingIndirectDesc *indirect = g_malloc(sizeof(*indirect));
137 
138     indirect->index = 0;
139     indirect->elem = elem;
140     indirect->desc = guest_alloc(alloc, sizeof(QVRingDesc)*elem);
141 
142     for (i = 0; i < elem - 1; ++i) {
143         /* indirect->desc[i].addr */
144         writeq(indirect->desc + (16 * i), 0);
145         /* indirect->desc[i].flags */
146         writew(indirect->desc + (16 * i) + 12, QVRING_DESC_F_NEXT);
147         /* indirect->desc[i].next */
148         writew(indirect->desc + (16 * i) + 14, i + 1);
149     }
150 
151     return indirect;
152 }
153 
154 void qvring_indirect_desc_add(QVRingIndirectDesc *indirect, uint64_t data,
155                                                     uint32_t len, bool write)
156 {
157     uint16_t flags;
158 
159     g_assert_cmpint(indirect->index, <, indirect->elem);
160 
161     flags = readw(indirect->desc + (16 * indirect->index) + 12);
162 
163     if (write) {
164         flags |= QVRING_DESC_F_WRITE;
165     }
166 
167     /* indirect->desc[indirect->index].addr */
168     writeq(indirect->desc + (16 * indirect->index), data);
169     /* indirect->desc[indirect->index].len */
170     writel(indirect->desc + (16 * indirect->index) + 8, len);
171     /* indirect->desc[indirect->index].flags */
172     writew(indirect->desc + (16 * indirect->index) + 12, flags);
173 
174     indirect->index++;
175 }
176 
177 uint32_t qvirtqueue_add(QVirtQueue *vq, uint64_t data, uint32_t len, bool write,
178                                                                     bool next)
179 {
180     uint16_t flags = 0;
181     vq->num_free--;
182 
183     if (write) {
184         flags |= QVRING_DESC_F_WRITE;
185     }
186 
187     if (next) {
188         flags |= QVRING_DESC_F_NEXT;
189     }
190 
191     /* vq->desc[vq->free_head].addr */
192     writeq(vq->desc + (16 * vq->free_head), data);
193     /* vq->desc[vq->free_head].len */
194     writel(vq->desc + (16 * vq->free_head) + 8, len);
195     /* vq->desc[vq->free_head].flags */
196     writew(vq->desc + (16 * vq->free_head) + 12, flags);
197 
198     return vq->free_head++; /* Return and increase, in this order */
199 }
200 
201 uint32_t qvirtqueue_add_indirect(QVirtQueue *vq, QVRingIndirectDesc *indirect)
202 {
203     g_assert(vq->indirect);
204     g_assert_cmpint(vq->size, >=, indirect->elem);
205     g_assert_cmpint(indirect->index, ==, indirect->elem);
206 
207     vq->num_free--;
208 
209     /* vq->desc[vq->free_head].addr */
210     writeq(vq->desc + (16 * vq->free_head), indirect->desc);
211     /* vq->desc[vq->free_head].len */
212     writel(vq->desc + (16 * vq->free_head) + 8,
213                                         sizeof(QVRingDesc) * indirect->elem);
214     /* vq->desc[vq->free_head].flags */
215     writew(vq->desc + (16 * vq->free_head) + 12, QVRING_DESC_F_INDIRECT);
216 
217     return vq->free_head++; /* Return and increase, in this order */
218 }
219 
220 void qvirtqueue_kick(const QVirtioBus *bus, QVirtioDevice *d, QVirtQueue *vq,
221                                                             uint32_t free_head)
222 {
223     /* vq->avail->idx */
224     uint16_t idx = readl(vq->avail + 2);
225 
226     /* vq->avail->ring[idx % vq->size] */
227     writel(vq->avail + 4 + (2 * (idx % vq->size)), free_head);
228     /* vq->avail->idx */
229     writel(vq->avail + 2, idx + 1);
230 
231     bus->virtqueue_kick(d, vq);
232 }
233