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