146e0cf76SMarc Marí /* 246e0cf76SMarc Marí * libqos virtio driver 346e0cf76SMarc Marí * 446e0cf76SMarc Marí * Copyright (c) 2014 Marc Marí 546e0cf76SMarc Marí * 646e0cf76SMarc Marí * This work is licensed under the terms of the GNU GPL, version 2 or later. 746e0cf76SMarc Marí * See the COPYING file in the top-level directory. 846e0cf76SMarc Marí */ 946e0cf76SMarc Marí 10681c28a3SPeter Maydell #include "qemu/osdep.h" 11bccd82b4SStefan Hajnoczi #include "qemu/bswap.h" 12907b5105SMarc-André Lureau #include "../libqtest.h" 13a2ce7dbdSPaolo Bonzini #include "virtio.h" 141373a4c2SStefan Hajnoczi #include "standard-headers/linux/virtio_config.h" 15ee3b850aSStefan Hajnoczi #include "standard-headers/linux/virtio_ring.h" 1646e0cf76SMarc Marí 17bccd82b4SStefan Hajnoczi /* 18bccd82b4SStefan Hajnoczi * qtest_readX/writeX() functions transfer host endian from/to guest endian. 19bccd82b4SStefan Hajnoczi * This works great for Legacy VIRTIO devices where we need guest endian 20bccd82b4SStefan Hajnoczi * accesses. For VIRTIO 1.0 the vring is little-endian so the automatic guest 21bccd82b4SStefan Hajnoczi * endianness conversion is not wanted. 22bccd82b4SStefan Hajnoczi * 23bccd82b4SStefan Hajnoczi * The following qvirtio_readX/writeX() functions handle Legacy and VIRTIO 1.0 24bccd82b4SStefan Hajnoczi * accesses seamlessly. 25bccd82b4SStefan Hajnoczi */ 26bccd82b4SStefan Hajnoczi static uint16_t qvirtio_readw(QVirtioDevice *d, QTestState *qts, uint64_t addr) 27bccd82b4SStefan Hajnoczi { 28bccd82b4SStefan Hajnoczi uint16_t val = qtest_readw(qts, addr); 29bccd82b4SStefan Hajnoczi 30bccd82b4SStefan Hajnoczi if (d->features & (1ull << VIRTIO_F_VERSION_1) && qtest_big_endian(qts)) { 31bccd82b4SStefan Hajnoczi val = bswap16(val); 32bccd82b4SStefan Hajnoczi } 33bccd82b4SStefan Hajnoczi return val; 34bccd82b4SStefan Hajnoczi } 35bccd82b4SStefan Hajnoczi 36bccd82b4SStefan Hajnoczi static uint32_t qvirtio_readl(QVirtioDevice *d, QTestState *qts, uint64_t addr) 37bccd82b4SStefan Hajnoczi { 38bccd82b4SStefan Hajnoczi uint32_t val = qtest_readl(qts, addr); 39bccd82b4SStefan Hajnoczi 40bccd82b4SStefan Hajnoczi if (d->features & (1ull << VIRTIO_F_VERSION_1) && qtest_big_endian(qts)) { 41bccd82b4SStefan Hajnoczi val = bswap32(val); 42bccd82b4SStefan Hajnoczi } 43bccd82b4SStefan Hajnoczi return val; 44bccd82b4SStefan Hajnoczi } 45bccd82b4SStefan Hajnoczi 46bccd82b4SStefan Hajnoczi static void qvirtio_writew(QVirtioDevice *d, QTestState *qts, 47bccd82b4SStefan Hajnoczi uint64_t addr, uint16_t val) 48bccd82b4SStefan Hajnoczi { 49bccd82b4SStefan Hajnoczi if (d->features & (1ull << VIRTIO_F_VERSION_1) && qtest_big_endian(qts)) { 50bccd82b4SStefan Hajnoczi val = bswap16(val); 51bccd82b4SStefan Hajnoczi } 52bccd82b4SStefan Hajnoczi qtest_writew(qts, addr, val); 53bccd82b4SStefan Hajnoczi } 54bccd82b4SStefan Hajnoczi 55bccd82b4SStefan Hajnoczi static void qvirtio_writel(QVirtioDevice *d, QTestState *qts, 56bccd82b4SStefan Hajnoczi uint64_t addr, uint32_t val) 57bccd82b4SStefan Hajnoczi { 58bccd82b4SStefan Hajnoczi if (d->features & (1ull << VIRTIO_F_VERSION_1) && qtest_big_endian(qts)) { 59bccd82b4SStefan Hajnoczi val = bswap32(val); 60bccd82b4SStefan Hajnoczi } 61bccd82b4SStefan Hajnoczi qtest_writel(qts, addr, val); 62bccd82b4SStefan Hajnoczi } 63bccd82b4SStefan Hajnoczi 64bccd82b4SStefan Hajnoczi static void qvirtio_writeq(QVirtioDevice *d, QTestState *qts, 65bccd82b4SStefan Hajnoczi uint64_t addr, uint64_t val) 66bccd82b4SStefan Hajnoczi { 67bccd82b4SStefan Hajnoczi if (d->features & (1ull << VIRTIO_F_VERSION_1) && qtest_big_endian(qts)) { 68bccd82b4SStefan Hajnoczi val = bswap64(val); 69bccd82b4SStefan Hajnoczi } 70bccd82b4SStefan Hajnoczi qtest_writeq(qts, addr, val); 71bccd82b4SStefan Hajnoczi } 72bccd82b4SStefan Hajnoczi 736b9cdf4cSLaurent Vivier uint8_t qvirtio_config_readb(QVirtioDevice *d, uint64_t addr) 7446e0cf76SMarc Marí { 7556140fbbSStefan Hajnoczi g_assert_true(d->features_negotiated); 766b9cdf4cSLaurent Vivier return d->bus->config_readb(d, addr); 7746e0cf76SMarc Marí } 7846e0cf76SMarc Marí 796b9cdf4cSLaurent Vivier uint16_t qvirtio_config_readw(QVirtioDevice *d, uint64_t addr) 8046e0cf76SMarc Marí { 8156140fbbSStefan Hajnoczi g_assert_true(d->features_negotiated); 826b9cdf4cSLaurent Vivier return d->bus->config_readw(d, addr); 8346e0cf76SMarc Marí } 8446e0cf76SMarc Marí 856b9cdf4cSLaurent Vivier uint32_t qvirtio_config_readl(QVirtioDevice *d, uint64_t addr) 8646e0cf76SMarc Marí { 8756140fbbSStefan Hajnoczi g_assert_true(d->features_negotiated); 886b9cdf4cSLaurent Vivier return d->bus->config_readl(d, addr); 8946e0cf76SMarc Marí } 9046e0cf76SMarc Marí 916b9cdf4cSLaurent Vivier uint64_t qvirtio_config_readq(QVirtioDevice *d, uint64_t addr) 9246e0cf76SMarc Marí { 9356140fbbSStefan Hajnoczi g_assert_true(d->features_negotiated); 946b9cdf4cSLaurent Vivier return d->bus->config_readq(d, addr); 9546e0cf76SMarc Marí } 9646e0cf76SMarc Marí 97a9340358SStefan Hajnoczi uint64_t qvirtio_get_features(QVirtioDevice *d) 98bf3c63d2SMarc Marí { 996b9cdf4cSLaurent Vivier return d->bus->get_features(d); 100bf3c63d2SMarc Marí } 101bf3c63d2SMarc Marí 102a9340358SStefan Hajnoczi void qvirtio_set_features(QVirtioDevice *d, uint64_t features) 103bf3c63d2SMarc Marí { 1043bd869f3SAlex Bennée g_assert(!(features & QVIRTIO_F_BAD_FEATURE)); 1053bd869f3SAlex Bennée 106583349d1SEmanuele Giuseppe Esposito d->features = features; 1076b9cdf4cSLaurent Vivier d->bus->set_features(d, features); 108c0f79698SStefan Hajnoczi 109c0f79698SStefan Hajnoczi /* 110c0f79698SStefan Hajnoczi * This could be a separate function for drivers that want to access 111c0f79698SStefan Hajnoczi * configuration space before setting FEATURES_OK, but no existing users 112c0f79698SStefan Hajnoczi * need that and it's less code for callers if this is done implicitly. 113c0f79698SStefan Hajnoczi */ 114c0f79698SStefan Hajnoczi if (features & (1ull << VIRTIO_F_VERSION_1)) { 115c0f79698SStefan Hajnoczi uint8_t status = d->bus->get_status(d) | 116c0f79698SStefan Hajnoczi VIRTIO_CONFIG_S_FEATURES_OK; 117c0f79698SStefan Hajnoczi 118c0f79698SStefan Hajnoczi d->bus->set_status(d, status); 119c0f79698SStefan Hajnoczi g_assert_cmphex(d->bus->get_status(d), ==, status); 120c0f79698SStefan Hajnoczi } 121c0f79698SStefan Hajnoczi 12256140fbbSStefan Hajnoczi d->features_negotiated = true; 123bf3c63d2SMarc Marí } 124bf3c63d2SMarc Marí 1256b9cdf4cSLaurent Vivier QVirtQueue *qvirtqueue_setup(QVirtioDevice *d, 126bf3c63d2SMarc Marí QGuestAllocator *alloc, uint16_t index) 127bf3c63d2SMarc Marí { 12856140fbbSStefan Hajnoczi g_assert_true(d->features_negotiated); 1296b9cdf4cSLaurent Vivier return d->bus->virtqueue_setup(d, alloc, index); 130bf3c63d2SMarc Marí } 131bf3c63d2SMarc Marí 132f1d3b991SStefan Hajnoczi void qvirtqueue_cleanup(const QVirtioBus *bus, QVirtQueue *vq, 133f1d3b991SStefan Hajnoczi QGuestAllocator *alloc) 134f1d3b991SStefan Hajnoczi { 135f1d3b991SStefan Hajnoczi return bus->virtqueue_cleanup(vq, alloc); 136f1d3b991SStefan Hajnoczi } 137f1d3b991SStefan Hajnoczi 1386b9cdf4cSLaurent Vivier void qvirtio_reset(QVirtioDevice *d) 13946e0cf76SMarc Marí { 1406b9cdf4cSLaurent Vivier d->bus->set_status(d, 0); 1416b9cdf4cSLaurent Vivier g_assert_cmphex(d->bus->get_status(d), ==, 0); 14256140fbbSStefan Hajnoczi d->features_negotiated = false; 14346e0cf76SMarc Marí } 14446e0cf76SMarc Marí 1456b9cdf4cSLaurent Vivier void qvirtio_set_acknowledge(QVirtioDevice *d) 14646e0cf76SMarc Marí { 1476b9cdf4cSLaurent Vivier d->bus->set_status(d, d->bus->get_status(d) | VIRTIO_CONFIG_S_ACKNOWLEDGE); 1486b9cdf4cSLaurent Vivier g_assert_cmphex(d->bus->get_status(d), ==, VIRTIO_CONFIG_S_ACKNOWLEDGE); 14946e0cf76SMarc Marí } 15046e0cf76SMarc Marí 1516b9cdf4cSLaurent Vivier void qvirtio_set_driver(QVirtioDevice *d) 15246e0cf76SMarc Marí { 1536b9cdf4cSLaurent Vivier d->bus->set_status(d, d->bus->get_status(d) | VIRTIO_CONFIG_S_DRIVER); 1546b9cdf4cSLaurent Vivier g_assert_cmphex(d->bus->get_status(d), ==, 1551373a4c2SStefan Hajnoczi VIRTIO_CONFIG_S_DRIVER | VIRTIO_CONFIG_S_ACKNOWLEDGE); 15646e0cf76SMarc Marí } 157bf3c63d2SMarc Marí 1586b9cdf4cSLaurent Vivier void qvirtio_set_driver_ok(QVirtioDevice *d) 159bf3c63d2SMarc Marí { 1606b9cdf4cSLaurent Vivier d->bus->set_status(d, d->bus->get_status(d) | VIRTIO_CONFIG_S_DRIVER_OK); 1616b9cdf4cSLaurent Vivier g_assert_cmphex(d->bus->get_status(d), ==, VIRTIO_CONFIG_S_DRIVER_OK | 162c0f79698SStefan Hajnoczi VIRTIO_CONFIG_S_DRIVER | VIRTIO_CONFIG_S_ACKNOWLEDGE | 163c0f79698SStefan Hajnoczi (d->features & (1ull << VIRTIO_F_VERSION_1) ? 164c0f79698SStefan Hajnoczi VIRTIO_CONFIG_S_FEATURES_OK : 0)); 165bf3c63d2SMarc Marí } 166bf3c63d2SMarc Marí 167b57ebd57SThomas Huth void qvirtio_wait_queue_isr(QTestState *qts, QVirtioDevice *d, 16870556264SStefan Hajnoczi QVirtQueue *vq, gint64 timeout_us) 16958368113SMarc Marí { 17070556264SStefan Hajnoczi gint64 start_time = g_get_monotonic_time(); 17170556264SStefan Hajnoczi 17270556264SStefan Hajnoczi for (;;) { 173b57ebd57SThomas Huth qtest_clock_step(qts, 100); 1746b9cdf4cSLaurent Vivier if (d->bus->get_queue_isr_status(d, vq)) { 17570556264SStefan Hajnoczi return; 17658368113SMarc Marí } 17770556264SStefan Hajnoczi g_assert(g_get_monotonic_time() - start_time <= timeout_us); 17870556264SStefan Hajnoczi } 17958368113SMarc Marí } 18058368113SMarc Marí 181e8c81b4dSStefan Hajnoczi /* Wait for the status byte at given guest memory address to be set 182e8c81b4dSStefan Hajnoczi * 183e8c81b4dSStefan Hajnoczi * The virtqueue interrupt must not be raised, making this useful for testing 184e8c81b4dSStefan Hajnoczi * event_index functionality. 185e8c81b4dSStefan Hajnoczi */ 1861999a70aSThomas Huth uint8_t qvirtio_wait_status_byte_no_isr(QTestState *qts, QVirtioDevice *d, 187e8c81b4dSStefan Hajnoczi QVirtQueue *vq, 188e8c81b4dSStefan Hajnoczi uint64_t addr, 189e8c81b4dSStefan Hajnoczi gint64 timeout_us) 190e8c81b4dSStefan Hajnoczi { 191e8c81b4dSStefan Hajnoczi gint64 start_time = g_get_monotonic_time(); 192e8c81b4dSStefan Hajnoczi uint8_t val; 193e8c81b4dSStefan Hajnoczi 194b57ebd57SThomas Huth while ((val = qtest_readb(qts, addr)) == 0xff) { 195b57ebd57SThomas Huth qtest_clock_step(qts, 100); 1966b9cdf4cSLaurent Vivier g_assert(!d->bus->get_queue_isr_status(d, vq)); 197e8c81b4dSStefan Hajnoczi g_assert(g_get_monotonic_time() - start_time <= timeout_us); 198e8c81b4dSStefan Hajnoczi } 199e8c81b4dSStefan Hajnoczi return val; 200e8c81b4dSStefan Hajnoczi } 201e8c81b4dSStefan Hajnoczi 202e77abbe9SStefan Hajnoczi /* 203e77abbe9SStefan Hajnoczi * qvirtio_wait_used_elem: 204e77abbe9SStefan Hajnoczi * @desc_idx: The next expected vq->desc[] index in the used ring 205be3a6781SGreg Kurz * @len: A pointer that is filled with the length written into the buffer, may 206be3a6781SGreg Kurz * be NULL 207e77abbe9SStefan Hajnoczi * @timeout_us: How many microseconds to wait before failing 208e77abbe9SStefan Hajnoczi * 209e77abbe9SStefan Hajnoczi * This function waits for the next completed request on the used ring. 210e77abbe9SStefan Hajnoczi */ 2111999a70aSThomas Huth void qvirtio_wait_used_elem(QTestState *qts, QVirtioDevice *d, 212e77abbe9SStefan Hajnoczi QVirtQueue *vq, 213e77abbe9SStefan Hajnoczi uint32_t desc_idx, 214be3a6781SGreg Kurz uint32_t *len, 215e77abbe9SStefan Hajnoczi gint64 timeout_us) 216e77abbe9SStefan Hajnoczi { 217e77abbe9SStefan Hajnoczi gint64 start_time = g_get_monotonic_time(); 218e77abbe9SStefan Hajnoczi 219e77abbe9SStefan Hajnoczi for (;;) { 220e77abbe9SStefan Hajnoczi uint32_t got_desc_idx; 221e77abbe9SStefan Hajnoczi 222b57ebd57SThomas Huth qtest_clock_step(qts, 100); 223e77abbe9SStefan Hajnoczi 224e77abbe9SStefan Hajnoczi if (d->bus->get_queue_isr_status(d, vq) && 2251999a70aSThomas Huth qvirtqueue_get_buf(qts, vq, &got_desc_idx, len)) { 226e77abbe9SStefan Hajnoczi g_assert_cmpint(got_desc_idx, ==, desc_idx); 227e77abbe9SStefan Hajnoczi return; 228e77abbe9SStefan Hajnoczi } 229e77abbe9SStefan Hajnoczi 230e77abbe9SStefan Hajnoczi g_assert(g_get_monotonic_time() - start_time <= timeout_us); 231e77abbe9SStefan Hajnoczi } 232e77abbe9SStefan Hajnoczi } 233e77abbe9SStefan Hajnoczi 2346b9cdf4cSLaurent Vivier void qvirtio_wait_config_isr(QVirtioDevice *d, gint64 timeout_us) 235bf3c63d2SMarc Marí { 236b57ebd57SThomas Huth d->bus->wait_config_isr_status(d, timeout_us); 237bf3c63d2SMarc Marí } 238bf3c63d2SMarc Marí 2398b898f59SThomas Huth void qvring_init(QTestState *qts, const QGuestAllocator *alloc, QVirtQueue *vq, 2408b898f59SThomas Huth uint64_t addr) 241bf3c63d2SMarc Marí { 242bf3c63d2SMarc Marí int i; 243bf3c63d2SMarc Marí 244bf3c63d2SMarc Marí vq->desc = addr; 245780b11a0SStefan Hajnoczi vq->avail = vq->desc + vq->size * sizeof(struct vring_desc); 246bf3c63d2SMarc Marí vq->used = (uint64_t)((vq->avail + sizeof(uint16_t) * (3 + vq->size) 247bf3c63d2SMarc Marí + vq->align - 1) & ~(vq->align - 1)); 248bf3c63d2SMarc Marí 249bf3c63d2SMarc Marí for (i = 0; i < vq->size - 1; i++) { 250bf3c63d2SMarc Marí /* vq->desc[i].addr */ 251bccd82b4SStefan Hajnoczi qvirtio_writeq(vq->vdev, qts, vq->desc + (16 * i), 0); 252bf3c63d2SMarc Marí /* vq->desc[i].next */ 253bccd82b4SStefan Hajnoczi qvirtio_writew(vq->vdev, qts, vq->desc + (16 * i) + 14, i + 1); 254bf3c63d2SMarc Marí } 255bf3c63d2SMarc Marí 256bf3c63d2SMarc Marí /* vq->avail->flags */ 257bccd82b4SStefan Hajnoczi qvirtio_writew(vq->vdev, qts, vq->avail, 0); 258bf3c63d2SMarc Marí /* vq->avail->idx */ 259bccd82b4SStefan Hajnoczi qvirtio_writew(vq->vdev, qts, vq->avail + 2, 0); 2601053587cSMarc Marí /* vq->avail->used_event */ 261bccd82b4SStefan Hajnoczi qvirtio_writew(vq->vdev, qts, vq->avail + 4 + (2 * vq->size), 0); 262bf3c63d2SMarc Marí 263bf3c63d2SMarc Marí /* vq->used->flags */ 264bccd82b4SStefan Hajnoczi qvirtio_writew(vq->vdev, qts, vq->used, 0); 2655b4f72f5SAlex Bennée /* vq->used->idx */ 2665b4f72f5SAlex Bennée qvirtio_writew(vq->vdev, qts, vq->used + 2, 0); 2671053587cSMarc Marí /* vq->used->avail_event */ 2683283843aSDaniel Henrique Barboza qvirtio_writew(vq->vdev, qts, vq->used + 4 + 269bccd82b4SStefan Hajnoczi sizeof(struct vring_used_elem) * vq->size, 0); 270bf3c63d2SMarc Marí } 271bf3c63d2SMarc Marí 2721999a70aSThomas Huth QVRingIndirectDesc *qvring_indirect_desc_setup(QTestState *qs, QVirtioDevice *d, 2731999a70aSThomas Huth QGuestAllocator *alloc, 2741999a70aSThomas Huth uint16_t elem) 275f294b029SMarc Marí { 276f294b029SMarc Marí int i; 277f294b029SMarc Marí QVRingIndirectDesc *indirect = g_malloc(sizeof(*indirect)); 278f294b029SMarc Marí 279f294b029SMarc Marí indirect->index = 0; 280f294b029SMarc Marí indirect->elem = elem; 281780b11a0SStefan Hajnoczi indirect->desc = guest_alloc(alloc, sizeof(struct vring_desc) * elem); 282f294b029SMarc Marí 2832791490dSDaniel Henrique Barboza for (i = 0; i < elem; ++i) { 284f294b029SMarc Marí /* indirect->desc[i].addr */ 285bccd82b4SStefan Hajnoczi qvirtio_writeq(d, qs, indirect->desc + (16 * i), 0); 2862791490dSDaniel Henrique Barboza 2872791490dSDaniel Henrique Barboza /* 2882791490dSDaniel Henrique Barboza * If it's not the last element of the ring, set 2892791490dSDaniel Henrique Barboza * the chain (VRING_DESC_F_NEXT) flag and 2902791490dSDaniel Henrique Barboza * desc->next. Clear the last element - there's 2912791490dSDaniel Henrique Barboza * no guarantee that guest_alloc() will do it. 2922791490dSDaniel Henrique Barboza */ 2932791490dSDaniel Henrique Barboza if (i != elem - 1) { 294f294b029SMarc Marí /* indirect->desc[i].flags */ 295bccd82b4SStefan Hajnoczi qvirtio_writew(d, qs, indirect->desc + (16 * i) + 12, 296bccd82b4SStefan Hajnoczi VRING_DESC_F_NEXT); 2972791490dSDaniel Henrique Barboza 298f294b029SMarc Marí /* indirect->desc[i].next */ 299bccd82b4SStefan Hajnoczi qvirtio_writew(d, qs, indirect->desc + (16 * i) + 14, i + 1); 3002791490dSDaniel Henrique Barboza } else { 3012791490dSDaniel Henrique Barboza qvirtio_writew(d, qs, indirect->desc + (16 * i) + 12, 0); 3022791490dSDaniel Henrique Barboza qvirtio_writew(d, qs, indirect->desc + (16 * i) + 14, 0); 3032791490dSDaniel Henrique Barboza } 304f294b029SMarc Marí } 305f294b029SMarc Marí 306f294b029SMarc Marí return indirect; 307f294b029SMarc Marí } 308f294b029SMarc Marí 309bccd82b4SStefan Hajnoczi void qvring_indirect_desc_add(QVirtioDevice *d, QTestState *qts, 310bccd82b4SStefan Hajnoczi QVRingIndirectDesc *indirect, 3111999a70aSThomas Huth uint64_t data, uint32_t len, bool write) 312f294b029SMarc Marí { 313f294b029SMarc Marí uint16_t flags; 314f294b029SMarc Marí 315f294b029SMarc Marí g_assert_cmpint(indirect->index, <, indirect->elem); 316f294b029SMarc Marí 317bccd82b4SStefan Hajnoczi flags = qvirtio_readw(d, qts, indirect->desc + 318bccd82b4SStefan Hajnoczi (16 * indirect->index) + 12); 319f294b029SMarc Marí 320f294b029SMarc Marí if (write) { 321ee3b850aSStefan Hajnoczi flags |= VRING_DESC_F_WRITE; 322f294b029SMarc Marí } 323f294b029SMarc Marí 324f294b029SMarc Marí /* indirect->desc[indirect->index].addr */ 325bccd82b4SStefan Hajnoczi qvirtio_writeq(d, qts, indirect->desc + (16 * indirect->index), data); 326f294b029SMarc Marí /* indirect->desc[indirect->index].len */ 327bccd82b4SStefan Hajnoczi qvirtio_writel(d, qts, indirect->desc + (16 * indirect->index) + 8, len); 328f294b029SMarc Marí /* indirect->desc[indirect->index].flags */ 329bccd82b4SStefan Hajnoczi qvirtio_writew(d, qts, indirect->desc + (16 * indirect->index) + 12, 330bccd82b4SStefan Hajnoczi flags); 331f294b029SMarc Marí 332f294b029SMarc Marí indirect->index++; 333f294b029SMarc Marí } 334f294b029SMarc Marí 3351999a70aSThomas Huth uint32_t qvirtqueue_add(QTestState *qts, QVirtQueue *vq, uint64_t data, 3361999a70aSThomas Huth uint32_t len, bool write, bool next) 337bf3c63d2SMarc Marí { 338bf3c63d2SMarc Marí uint16_t flags = 0; 339bf3c63d2SMarc Marí vq->num_free--; 340bf3c63d2SMarc Marí 341bf3c63d2SMarc Marí if (write) { 342ee3b850aSStefan Hajnoczi flags |= VRING_DESC_F_WRITE; 343bf3c63d2SMarc Marí } 344bf3c63d2SMarc Marí 345bf3c63d2SMarc Marí if (next) { 346ee3b850aSStefan Hajnoczi flags |= VRING_DESC_F_NEXT; 347bf3c63d2SMarc Marí } 348bf3c63d2SMarc Marí 349bf3c63d2SMarc Marí /* vq->desc[vq->free_head].addr */ 350bccd82b4SStefan Hajnoczi qvirtio_writeq(vq->vdev, qts, vq->desc + (16 * vq->free_head), data); 351bf3c63d2SMarc Marí /* vq->desc[vq->free_head].len */ 352bccd82b4SStefan Hajnoczi qvirtio_writel(vq->vdev, qts, vq->desc + (16 * vq->free_head) + 8, len); 353bf3c63d2SMarc Marí /* vq->desc[vq->free_head].flags */ 354bccd82b4SStefan Hajnoczi qvirtio_writew(vq->vdev, qts, vq->desc + (16 * vq->free_head) + 12, flags); 355bf3c63d2SMarc Marí 356bf3c63d2SMarc Marí return vq->free_head++; /* Return and increase, in this order */ 357bf3c63d2SMarc Marí } 358bf3c63d2SMarc Marí 3591999a70aSThomas Huth uint32_t qvirtqueue_add_indirect(QTestState *qts, QVirtQueue *vq, 3601999a70aSThomas Huth QVRingIndirectDesc *indirect) 361f294b029SMarc Marí { 362f294b029SMarc Marí g_assert(vq->indirect); 363f294b029SMarc Marí g_assert_cmpint(vq->size, >=, indirect->elem); 364f294b029SMarc Marí g_assert_cmpint(indirect->index, ==, indirect->elem); 365f294b029SMarc Marí 366f294b029SMarc Marí vq->num_free--; 367f294b029SMarc Marí 368f294b029SMarc Marí /* vq->desc[vq->free_head].addr */ 369bccd82b4SStefan Hajnoczi qvirtio_writeq(vq->vdev, qts, vq->desc + (16 * vq->free_head), 370bccd82b4SStefan Hajnoczi indirect->desc); 371f294b029SMarc Marí /* vq->desc[vq->free_head].len */ 372bccd82b4SStefan Hajnoczi qvirtio_writel(vq->vdev, qts, vq->desc + (16 * vq->free_head) + 8, 373780b11a0SStefan Hajnoczi sizeof(struct vring_desc) * indirect->elem); 374f294b029SMarc Marí /* vq->desc[vq->free_head].flags */ 375bccd82b4SStefan Hajnoczi qvirtio_writew(vq->vdev, qts, vq->desc + (16 * vq->free_head) + 12, 3761999a70aSThomas Huth VRING_DESC_F_INDIRECT); 377f294b029SMarc Marí 378f294b029SMarc Marí return vq->free_head++; /* Return and increase, in this order */ 379f294b029SMarc Marí } 380f294b029SMarc Marí 3811999a70aSThomas Huth void qvirtqueue_kick(QTestState *qts, QVirtioDevice *d, QVirtQueue *vq, 3821999a70aSThomas Huth uint32_t free_head) 383bf3c63d2SMarc Marí { 384bf3c63d2SMarc Marí /* vq->avail->idx */ 385bccd82b4SStefan Hajnoczi uint16_t idx = qvirtio_readw(d, qts, vq->avail + 2); 3861053587cSMarc Marí /* vq->used->flags */ 3871053587cSMarc Marí uint16_t flags; 3881053587cSMarc Marí /* vq->used->avail_event */ 3891053587cSMarc Marí uint16_t avail_event; 390bf3c63d2SMarc Marí 391bf3c63d2SMarc Marí /* vq->avail->ring[idx % vq->size] */ 392bccd82b4SStefan Hajnoczi qvirtio_writew(d, qts, vq->avail + 4 + (2 * (idx % vq->size)), free_head); 393bf3c63d2SMarc Marí /* vq->avail->idx */ 394bccd82b4SStefan Hajnoczi qvirtio_writew(d, qts, vq->avail + 2, idx + 1); 395bf3c63d2SMarc Marí 3961053587cSMarc Marí /* Must read after idx is updated */ 397*66e41188SZheyu Ma flags = qvirtio_readw(d, qts, vq->used); 398bccd82b4SStefan Hajnoczi avail_event = qvirtio_readw(d, qts, vq->used + 4 + 399780b11a0SStefan Hajnoczi sizeof(struct vring_used_elem) * vq->size); 4001053587cSMarc Marí 4011053587cSMarc Marí /* < 1 because we add elements to avail queue one by one */ 402ee3b850aSStefan Hajnoczi if ((flags & VRING_USED_F_NO_NOTIFY) == 0 && 4031053587cSMarc Marí (!vq->event || (uint16_t)(idx-avail_event) < 1)) { 4046b9cdf4cSLaurent Vivier d->bus->virtqueue_kick(d, vq); 405bf3c63d2SMarc Marí } 4061053587cSMarc Marí } 4071053587cSMarc Marí 408e77abbe9SStefan Hajnoczi /* 409e77abbe9SStefan Hajnoczi * qvirtqueue_get_buf: 410e77abbe9SStefan Hajnoczi * @desc_idx: A pointer that is filled with the vq->desc[] index, may be NULL 411be3a6781SGreg Kurz * @len: A pointer that is filled with the length written into the buffer, may 412be3a6781SGreg Kurz * be NULL 413e77abbe9SStefan Hajnoczi * 414e77abbe9SStefan Hajnoczi * This function gets the next used element if there is one ready. 415e77abbe9SStefan Hajnoczi * 416e77abbe9SStefan Hajnoczi * Returns: true if an element was ready, false otherwise 417e77abbe9SStefan Hajnoczi */ 4181999a70aSThomas Huth bool qvirtqueue_get_buf(QTestState *qts, QVirtQueue *vq, uint32_t *desc_idx, 4191999a70aSThomas Huth uint32_t *len) 420e77abbe9SStefan Hajnoczi { 421e77abbe9SStefan Hajnoczi uint16_t idx; 4221999a70aSThomas Huth uint64_t elem_addr, addr; 423e77abbe9SStefan Hajnoczi 424bccd82b4SStefan Hajnoczi idx = qvirtio_readw(vq->vdev, qts, 425bccd82b4SStefan Hajnoczi vq->used + offsetof(struct vring_used, idx)); 426e77abbe9SStefan Hajnoczi if (idx == vq->last_used_idx) { 427e77abbe9SStefan Hajnoczi return false; 428e77abbe9SStefan Hajnoczi } 429e77abbe9SStefan Hajnoczi 430e77abbe9SStefan Hajnoczi elem_addr = vq->used + 431e77abbe9SStefan Hajnoczi offsetof(struct vring_used, ring) + 432e77abbe9SStefan Hajnoczi (vq->last_used_idx % vq->size) * 433e77abbe9SStefan Hajnoczi sizeof(struct vring_used_elem); 434be3a6781SGreg Kurz 435be3a6781SGreg Kurz if (desc_idx) { 4361999a70aSThomas Huth addr = elem_addr + offsetof(struct vring_used_elem, id); 437bccd82b4SStefan Hajnoczi *desc_idx = qvirtio_readl(vq->vdev, qts, addr); 438e77abbe9SStefan Hajnoczi } 439e77abbe9SStefan Hajnoczi 440be3a6781SGreg Kurz if (len) { 4411999a70aSThomas Huth addr = elem_addr + offsetof(struct vring_used_elem, len); 442bccd82b4SStefan Hajnoczi *len = qvirtio_readw(vq->vdev, qts, addr); 443be3a6781SGreg Kurz } 444be3a6781SGreg Kurz 445e77abbe9SStefan Hajnoczi vq->last_used_idx++; 446e77abbe9SStefan Hajnoczi return true; 447e77abbe9SStefan Hajnoczi } 448e77abbe9SStefan Hajnoczi 4491999a70aSThomas Huth void qvirtqueue_set_used_event(QTestState *qts, QVirtQueue *vq, uint16_t idx) 4501053587cSMarc Marí { 4511053587cSMarc Marí g_assert(vq->event); 4521053587cSMarc Marí 4531053587cSMarc Marí /* vq->avail->used_event */ 454bccd82b4SStefan Hajnoczi qvirtio_writew(vq->vdev, qts, vq->avail + 4 + (2 * vq->size), idx); 4551053587cSMarc Marí } 4562f84a92eSThomas Huth 45734c97748SEmanuele Giuseppe Esposito void qvirtio_start_device(QVirtioDevice *vdev) 45834c97748SEmanuele Giuseppe Esposito { 45934c97748SEmanuele Giuseppe Esposito qvirtio_reset(vdev); 46034c97748SEmanuele Giuseppe Esposito qvirtio_set_acknowledge(vdev); 46134c97748SEmanuele Giuseppe Esposito qvirtio_set_driver(vdev); 46234c97748SEmanuele Giuseppe Esposito } 463d5006a45SPaolo Bonzini 464d5006a45SPaolo Bonzini bool qvirtio_is_big_endian(QVirtioDevice *d) 465d5006a45SPaolo Bonzini { 466d5006a45SPaolo Bonzini return d->big_endian; 467d5006a45SPaolo Bonzini } 468