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" 12*907b5105SMarc-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í { 104583349d1SEmanuele Giuseppe Esposito d->features = features; 1056b9cdf4cSLaurent Vivier d->bus->set_features(d, features); 106c0f79698SStefan Hajnoczi 107c0f79698SStefan Hajnoczi /* 108c0f79698SStefan Hajnoczi * This could be a separate function for drivers that want to access 109c0f79698SStefan Hajnoczi * configuration space before setting FEATURES_OK, but no existing users 110c0f79698SStefan Hajnoczi * need that and it's less code for callers if this is done implicitly. 111c0f79698SStefan Hajnoczi */ 112c0f79698SStefan Hajnoczi if (features & (1ull << VIRTIO_F_VERSION_1)) { 113c0f79698SStefan Hajnoczi uint8_t status = d->bus->get_status(d) | 114c0f79698SStefan Hajnoczi VIRTIO_CONFIG_S_FEATURES_OK; 115c0f79698SStefan Hajnoczi 116c0f79698SStefan Hajnoczi d->bus->set_status(d, status); 117c0f79698SStefan Hajnoczi g_assert_cmphex(d->bus->get_status(d), ==, status); 118c0f79698SStefan Hajnoczi } 119c0f79698SStefan Hajnoczi 12056140fbbSStefan Hajnoczi d->features_negotiated = true; 121bf3c63d2SMarc Marí } 122bf3c63d2SMarc Marí 1236b9cdf4cSLaurent Vivier QVirtQueue *qvirtqueue_setup(QVirtioDevice *d, 124bf3c63d2SMarc Marí QGuestAllocator *alloc, uint16_t index) 125bf3c63d2SMarc Marí { 12656140fbbSStefan Hajnoczi g_assert_true(d->features_negotiated); 1276b9cdf4cSLaurent Vivier return d->bus->virtqueue_setup(d, alloc, index); 128bf3c63d2SMarc Marí } 129bf3c63d2SMarc Marí 130f1d3b991SStefan Hajnoczi void qvirtqueue_cleanup(const QVirtioBus *bus, QVirtQueue *vq, 131f1d3b991SStefan Hajnoczi QGuestAllocator *alloc) 132f1d3b991SStefan Hajnoczi { 133f1d3b991SStefan Hajnoczi return bus->virtqueue_cleanup(vq, alloc); 134f1d3b991SStefan Hajnoczi } 135f1d3b991SStefan Hajnoczi 1366b9cdf4cSLaurent Vivier void qvirtio_reset(QVirtioDevice *d) 13746e0cf76SMarc Marí { 1386b9cdf4cSLaurent Vivier d->bus->set_status(d, 0); 1396b9cdf4cSLaurent Vivier g_assert_cmphex(d->bus->get_status(d), ==, 0); 14056140fbbSStefan Hajnoczi d->features_negotiated = false; 14146e0cf76SMarc Marí } 14246e0cf76SMarc Marí 1436b9cdf4cSLaurent Vivier void qvirtio_set_acknowledge(QVirtioDevice *d) 14446e0cf76SMarc Marí { 1456b9cdf4cSLaurent Vivier d->bus->set_status(d, d->bus->get_status(d) | VIRTIO_CONFIG_S_ACKNOWLEDGE); 1466b9cdf4cSLaurent Vivier g_assert_cmphex(d->bus->get_status(d), ==, VIRTIO_CONFIG_S_ACKNOWLEDGE); 14746e0cf76SMarc Marí } 14846e0cf76SMarc Marí 1496b9cdf4cSLaurent Vivier void qvirtio_set_driver(QVirtioDevice *d) 15046e0cf76SMarc Marí { 1516b9cdf4cSLaurent Vivier d->bus->set_status(d, d->bus->get_status(d) | VIRTIO_CONFIG_S_DRIVER); 1526b9cdf4cSLaurent Vivier g_assert_cmphex(d->bus->get_status(d), ==, 1531373a4c2SStefan Hajnoczi VIRTIO_CONFIG_S_DRIVER | VIRTIO_CONFIG_S_ACKNOWLEDGE); 15446e0cf76SMarc Marí } 155bf3c63d2SMarc Marí 1566b9cdf4cSLaurent Vivier void qvirtio_set_driver_ok(QVirtioDevice *d) 157bf3c63d2SMarc Marí { 1586b9cdf4cSLaurent Vivier d->bus->set_status(d, d->bus->get_status(d) | VIRTIO_CONFIG_S_DRIVER_OK); 1596b9cdf4cSLaurent Vivier g_assert_cmphex(d->bus->get_status(d), ==, VIRTIO_CONFIG_S_DRIVER_OK | 160c0f79698SStefan Hajnoczi VIRTIO_CONFIG_S_DRIVER | VIRTIO_CONFIG_S_ACKNOWLEDGE | 161c0f79698SStefan Hajnoczi (d->features & (1ull << VIRTIO_F_VERSION_1) ? 162c0f79698SStefan Hajnoczi VIRTIO_CONFIG_S_FEATURES_OK : 0)); 163bf3c63d2SMarc Marí } 164bf3c63d2SMarc Marí 165b57ebd57SThomas Huth void qvirtio_wait_queue_isr(QTestState *qts, QVirtioDevice *d, 16670556264SStefan Hajnoczi QVirtQueue *vq, gint64 timeout_us) 16758368113SMarc Marí { 16870556264SStefan Hajnoczi gint64 start_time = g_get_monotonic_time(); 16970556264SStefan Hajnoczi 17070556264SStefan Hajnoczi for (;;) { 171b57ebd57SThomas Huth qtest_clock_step(qts, 100); 1726b9cdf4cSLaurent Vivier if (d->bus->get_queue_isr_status(d, vq)) { 17370556264SStefan Hajnoczi return; 17458368113SMarc Marí } 17570556264SStefan Hajnoczi g_assert(g_get_monotonic_time() - start_time <= timeout_us); 17670556264SStefan Hajnoczi } 17758368113SMarc Marí } 17858368113SMarc Marí 179e8c81b4dSStefan Hajnoczi /* Wait for the status byte at given guest memory address to be set 180e8c81b4dSStefan Hajnoczi * 181e8c81b4dSStefan Hajnoczi * The virtqueue interrupt must not be raised, making this useful for testing 182e8c81b4dSStefan Hajnoczi * event_index functionality. 183e8c81b4dSStefan Hajnoczi */ 1841999a70aSThomas Huth uint8_t qvirtio_wait_status_byte_no_isr(QTestState *qts, QVirtioDevice *d, 185e8c81b4dSStefan Hajnoczi QVirtQueue *vq, 186e8c81b4dSStefan Hajnoczi uint64_t addr, 187e8c81b4dSStefan Hajnoczi gint64 timeout_us) 188e8c81b4dSStefan Hajnoczi { 189e8c81b4dSStefan Hajnoczi gint64 start_time = g_get_monotonic_time(); 190e8c81b4dSStefan Hajnoczi uint8_t val; 191e8c81b4dSStefan Hajnoczi 192b57ebd57SThomas Huth while ((val = qtest_readb(qts, addr)) == 0xff) { 193b57ebd57SThomas Huth qtest_clock_step(qts, 100); 1946b9cdf4cSLaurent Vivier g_assert(!d->bus->get_queue_isr_status(d, vq)); 195e8c81b4dSStefan Hajnoczi g_assert(g_get_monotonic_time() - start_time <= timeout_us); 196e8c81b4dSStefan Hajnoczi } 197e8c81b4dSStefan Hajnoczi return val; 198e8c81b4dSStefan Hajnoczi } 199e8c81b4dSStefan Hajnoczi 200e77abbe9SStefan Hajnoczi /* 201e77abbe9SStefan Hajnoczi * qvirtio_wait_used_elem: 202e77abbe9SStefan Hajnoczi * @desc_idx: The next expected vq->desc[] index in the used ring 203be3a6781SGreg Kurz * @len: A pointer that is filled with the length written into the buffer, may 204be3a6781SGreg Kurz * be NULL 205e77abbe9SStefan Hajnoczi * @timeout_us: How many microseconds to wait before failing 206e77abbe9SStefan Hajnoczi * 207e77abbe9SStefan Hajnoczi * This function waits for the next completed request on the used ring. 208e77abbe9SStefan Hajnoczi */ 2091999a70aSThomas Huth void qvirtio_wait_used_elem(QTestState *qts, QVirtioDevice *d, 210e77abbe9SStefan Hajnoczi QVirtQueue *vq, 211e77abbe9SStefan Hajnoczi uint32_t desc_idx, 212be3a6781SGreg Kurz uint32_t *len, 213e77abbe9SStefan Hajnoczi gint64 timeout_us) 214e77abbe9SStefan Hajnoczi { 215e77abbe9SStefan Hajnoczi gint64 start_time = g_get_monotonic_time(); 216e77abbe9SStefan Hajnoczi 217e77abbe9SStefan Hajnoczi for (;;) { 218e77abbe9SStefan Hajnoczi uint32_t got_desc_idx; 219e77abbe9SStefan Hajnoczi 220b57ebd57SThomas Huth qtest_clock_step(qts, 100); 221e77abbe9SStefan Hajnoczi 222e77abbe9SStefan Hajnoczi if (d->bus->get_queue_isr_status(d, vq) && 2231999a70aSThomas Huth qvirtqueue_get_buf(qts, vq, &got_desc_idx, len)) { 224e77abbe9SStefan Hajnoczi g_assert_cmpint(got_desc_idx, ==, desc_idx); 225e77abbe9SStefan Hajnoczi return; 226e77abbe9SStefan Hajnoczi } 227e77abbe9SStefan Hajnoczi 228e77abbe9SStefan Hajnoczi g_assert(g_get_monotonic_time() - start_time <= timeout_us); 229e77abbe9SStefan Hajnoczi } 230e77abbe9SStefan Hajnoczi } 231e77abbe9SStefan Hajnoczi 2326b9cdf4cSLaurent Vivier void qvirtio_wait_config_isr(QVirtioDevice *d, gint64 timeout_us) 233bf3c63d2SMarc Marí { 234b57ebd57SThomas Huth d->bus->wait_config_isr_status(d, timeout_us); 235bf3c63d2SMarc Marí } 236bf3c63d2SMarc Marí 2378b898f59SThomas Huth void qvring_init(QTestState *qts, const QGuestAllocator *alloc, QVirtQueue *vq, 2388b898f59SThomas Huth uint64_t addr) 239bf3c63d2SMarc Marí { 240bf3c63d2SMarc Marí int i; 241bf3c63d2SMarc Marí 242bf3c63d2SMarc Marí vq->desc = addr; 243780b11a0SStefan Hajnoczi vq->avail = vq->desc + vq->size * sizeof(struct vring_desc); 244bf3c63d2SMarc Marí vq->used = (uint64_t)((vq->avail + sizeof(uint16_t) * (3 + vq->size) 245bf3c63d2SMarc Marí + vq->align - 1) & ~(vq->align - 1)); 246bf3c63d2SMarc Marí 247bf3c63d2SMarc Marí for (i = 0; i < vq->size - 1; i++) { 248bf3c63d2SMarc Marí /* vq->desc[i].addr */ 249bccd82b4SStefan Hajnoczi qvirtio_writeq(vq->vdev, qts, vq->desc + (16 * i), 0); 250bf3c63d2SMarc Marí /* vq->desc[i].next */ 251bccd82b4SStefan Hajnoczi qvirtio_writew(vq->vdev, qts, vq->desc + (16 * i) + 14, i + 1); 252bf3c63d2SMarc Marí } 253bf3c63d2SMarc Marí 254bf3c63d2SMarc Marí /* vq->avail->flags */ 255bccd82b4SStefan Hajnoczi qvirtio_writew(vq->vdev, qts, vq->avail, 0); 256bf3c63d2SMarc Marí /* vq->avail->idx */ 257bccd82b4SStefan Hajnoczi qvirtio_writew(vq->vdev, qts, vq->avail + 2, 0); 2581053587cSMarc Marí /* vq->avail->used_event */ 259bccd82b4SStefan Hajnoczi qvirtio_writew(vq->vdev, qts, vq->avail + 4 + (2 * vq->size), 0); 260bf3c63d2SMarc Marí 261bf3c63d2SMarc Marí /* vq->used->flags */ 262bccd82b4SStefan Hajnoczi qvirtio_writew(vq->vdev, qts, vq->used, 0); 2635b4f72f5SAlex Bennée /* vq->used->idx */ 2645b4f72f5SAlex Bennée qvirtio_writew(vq->vdev, qts, vq->used + 2, 0); 2651053587cSMarc Marí /* vq->used->avail_event */ 266bccd82b4SStefan Hajnoczi qvirtio_writew(vq->vdev, qts, vq->used + 2 + 267bccd82b4SStefan Hajnoczi sizeof(struct vring_used_elem) * vq->size, 0); 268bf3c63d2SMarc Marí } 269bf3c63d2SMarc Marí 2701999a70aSThomas Huth QVRingIndirectDesc *qvring_indirect_desc_setup(QTestState *qs, QVirtioDevice *d, 2711999a70aSThomas Huth QGuestAllocator *alloc, 2721999a70aSThomas Huth uint16_t elem) 273f294b029SMarc Marí { 274f294b029SMarc Marí int i; 275f294b029SMarc Marí QVRingIndirectDesc *indirect = g_malloc(sizeof(*indirect)); 276f294b029SMarc Marí 277f294b029SMarc Marí indirect->index = 0; 278f294b029SMarc Marí indirect->elem = elem; 279780b11a0SStefan Hajnoczi indirect->desc = guest_alloc(alloc, sizeof(struct vring_desc) * elem); 280f294b029SMarc Marí 281f294b029SMarc Marí for (i = 0; i < elem - 1; ++i) { 282f294b029SMarc Marí /* indirect->desc[i].addr */ 283bccd82b4SStefan Hajnoczi qvirtio_writeq(d, qs, indirect->desc + (16 * i), 0); 284f294b029SMarc Marí /* indirect->desc[i].flags */ 285bccd82b4SStefan Hajnoczi qvirtio_writew(d, qs, indirect->desc + (16 * i) + 12, 286bccd82b4SStefan Hajnoczi VRING_DESC_F_NEXT); 287f294b029SMarc Marí /* indirect->desc[i].next */ 288bccd82b4SStefan Hajnoczi qvirtio_writew(d, qs, indirect->desc + (16 * i) + 14, i + 1); 289f294b029SMarc Marí } 290f294b029SMarc Marí 291f294b029SMarc Marí return indirect; 292f294b029SMarc Marí } 293f294b029SMarc Marí 294bccd82b4SStefan Hajnoczi void qvring_indirect_desc_add(QVirtioDevice *d, QTestState *qts, 295bccd82b4SStefan Hajnoczi QVRingIndirectDesc *indirect, 2961999a70aSThomas Huth uint64_t data, uint32_t len, bool write) 297f294b029SMarc Marí { 298f294b029SMarc Marí uint16_t flags; 299f294b029SMarc Marí 300f294b029SMarc Marí g_assert_cmpint(indirect->index, <, indirect->elem); 301f294b029SMarc Marí 302bccd82b4SStefan Hajnoczi flags = qvirtio_readw(d, qts, indirect->desc + 303bccd82b4SStefan Hajnoczi (16 * indirect->index) + 12); 304f294b029SMarc Marí 305f294b029SMarc Marí if (write) { 306ee3b850aSStefan Hajnoczi flags |= VRING_DESC_F_WRITE; 307f294b029SMarc Marí } 308f294b029SMarc Marí 309f294b029SMarc Marí /* indirect->desc[indirect->index].addr */ 310bccd82b4SStefan Hajnoczi qvirtio_writeq(d, qts, indirect->desc + (16 * indirect->index), data); 311f294b029SMarc Marí /* indirect->desc[indirect->index].len */ 312bccd82b4SStefan Hajnoczi qvirtio_writel(d, qts, indirect->desc + (16 * indirect->index) + 8, len); 313f294b029SMarc Marí /* indirect->desc[indirect->index].flags */ 314bccd82b4SStefan Hajnoczi qvirtio_writew(d, qts, indirect->desc + (16 * indirect->index) + 12, 315bccd82b4SStefan Hajnoczi flags); 316f294b029SMarc Marí 317f294b029SMarc Marí indirect->index++; 318f294b029SMarc Marí } 319f294b029SMarc Marí 3201999a70aSThomas Huth uint32_t qvirtqueue_add(QTestState *qts, QVirtQueue *vq, uint64_t data, 3211999a70aSThomas Huth uint32_t len, bool write, bool next) 322bf3c63d2SMarc Marí { 323bf3c63d2SMarc Marí uint16_t flags = 0; 324bf3c63d2SMarc Marí vq->num_free--; 325bf3c63d2SMarc Marí 326bf3c63d2SMarc Marí if (write) { 327ee3b850aSStefan Hajnoczi flags |= VRING_DESC_F_WRITE; 328bf3c63d2SMarc Marí } 329bf3c63d2SMarc Marí 330bf3c63d2SMarc Marí if (next) { 331ee3b850aSStefan Hajnoczi flags |= VRING_DESC_F_NEXT; 332bf3c63d2SMarc Marí } 333bf3c63d2SMarc Marí 334bf3c63d2SMarc Marí /* vq->desc[vq->free_head].addr */ 335bccd82b4SStefan Hajnoczi qvirtio_writeq(vq->vdev, qts, vq->desc + (16 * vq->free_head), data); 336bf3c63d2SMarc Marí /* vq->desc[vq->free_head].len */ 337bccd82b4SStefan Hajnoczi qvirtio_writel(vq->vdev, qts, vq->desc + (16 * vq->free_head) + 8, len); 338bf3c63d2SMarc Marí /* vq->desc[vq->free_head].flags */ 339bccd82b4SStefan Hajnoczi qvirtio_writew(vq->vdev, qts, vq->desc + (16 * vq->free_head) + 12, flags); 340bf3c63d2SMarc Marí 341bf3c63d2SMarc Marí return vq->free_head++; /* Return and increase, in this order */ 342bf3c63d2SMarc Marí } 343bf3c63d2SMarc Marí 3441999a70aSThomas Huth uint32_t qvirtqueue_add_indirect(QTestState *qts, QVirtQueue *vq, 3451999a70aSThomas Huth QVRingIndirectDesc *indirect) 346f294b029SMarc Marí { 347f294b029SMarc Marí g_assert(vq->indirect); 348f294b029SMarc Marí g_assert_cmpint(vq->size, >=, indirect->elem); 349f294b029SMarc Marí g_assert_cmpint(indirect->index, ==, indirect->elem); 350f294b029SMarc Marí 351f294b029SMarc Marí vq->num_free--; 352f294b029SMarc Marí 353f294b029SMarc Marí /* vq->desc[vq->free_head].addr */ 354bccd82b4SStefan Hajnoczi qvirtio_writeq(vq->vdev, qts, vq->desc + (16 * vq->free_head), 355bccd82b4SStefan Hajnoczi indirect->desc); 356f294b029SMarc Marí /* vq->desc[vq->free_head].len */ 357bccd82b4SStefan Hajnoczi qvirtio_writel(vq->vdev, qts, vq->desc + (16 * vq->free_head) + 8, 358780b11a0SStefan Hajnoczi sizeof(struct vring_desc) * indirect->elem); 359f294b029SMarc Marí /* vq->desc[vq->free_head].flags */ 360bccd82b4SStefan Hajnoczi qvirtio_writew(vq->vdev, qts, vq->desc + (16 * vq->free_head) + 12, 3611999a70aSThomas Huth VRING_DESC_F_INDIRECT); 362f294b029SMarc Marí 363f294b029SMarc Marí return vq->free_head++; /* Return and increase, in this order */ 364f294b029SMarc Marí } 365f294b029SMarc Marí 3661999a70aSThomas Huth void qvirtqueue_kick(QTestState *qts, QVirtioDevice *d, QVirtQueue *vq, 3671999a70aSThomas Huth uint32_t free_head) 368bf3c63d2SMarc Marí { 369bf3c63d2SMarc Marí /* vq->avail->idx */ 370bccd82b4SStefan Hajnoczi uint16_t idx = qvirtio_readw(d, qts, vq->avail + 2); 3711053587cSMarc Marí /* vq->used->flags */ 3721053587cSMarc Marí uint16_t flags; 3731053587cSMarc Marí /* vq->used->avail_event */ 3741053587cSMarc Marí uint16_t avail_event; 375bf3c63d2SMarc Marí 376bf3c63d2SMarc Marí /* vq->avail->ring[idx % vq->size] */ 377bccd82b4SStefan Hajnoczi qvirtio_writew(d, qts, vq->avail + 4 + (2 * (idx % vq->size)), free_head); 378bf3c63d2SMarc Marí /* vq->avail->idx */ 379bccd82b4SStefan Hajnoczi qvirtio_writew(d, qts, vq->avail + 2, idx + 1); 380bf3c63d2SMarc Marí 3811053587cSMarc Marí /* Must read after idx is updated */ 382bccd82b4SStefan Hajnoczi flags = qvirtio_readw(d, qts, vq->avail); 383bccd82b4SStefan Hajnoczi avail_event = qvirtio_readw(d, qts, vq->used + 4 + 384780b11a0SStefan Hajnoczi sizeof(struct vring_used_elem) * vq->size); 3851053587cSMarc Marí 3861053587cSMarc Marí /* < 1 because we add elements to avail queue one by one */ 387ee3b850aSStefan Hajnoczi if ((flags & VRING_USED_F_NO_NOTIFY) == 0 && 3881053587cSMarc Marí (!vq->event || (uint16_t)(idx-avail_event) < 1)) { 3896b9cdf4cSLaurent Vivier d->bus->virtqueue_kick(d, vq); 390bf3c63d2SMarc Marí } 3911053587cSMarc Marí } 3921053587cSMarc Marí 393e77abbe9SStefan Hajnoczi /* 394e77abbe9SStefan Hajnoczi * qvirtqueue_get_buf: 395e77abbe9SStefan Hajnoczi * @desc_idx: A pointer that is filled with the vq->desc[] index, may be NULL 396be3a6781SGreg Kurz * @len: A pointer that is filled with the length written into the buffer, may 397be3a6781SGreg Kurz * be NULL 398e77abbe9SStefan Hajnoczi * 399e77abbe9SStefan Hajnoczi * This function gets the next used element if there is one ready. 400e77abbe9SStefan Hajnoczi * 401e77abbe9SStefan Hajnoczi * Returns: true if an element was ready, false otherwise 402e77abbe9SStefan Hajnoczi */ 4031999a70aSThomas Huth bool qvirtqueue_get_buf(QTestState *qts, QVirtQueue *vq, uint32_t *desc_idx, 4041999a70aSThomas Huth uint32_t *len) 405e77abbe9SStefan Hajnoczi { 406e77abbe9SStefan Hajnoczi uint16_t idx; 4071999a70aSThomas Huth uint64_t elem_addr, addr; 408e77abbe9SStefan Hajnoczi 409bccd82b4SStefan Hajnoczi idx = qvirtio_readw(vq->vdev, qts, 410bccd82b4SStefan Hajnoczi vq->used + offsetof(struct vring_used, idx)); 411e77abbe9SStefan Hajnoczi if (idx == vq->last_used_idx) { 412e77abbe9SStefan Hajnoczi return false; 413e77abbe9SStefan Hajnoczi } 414e77abbe9SStefan Hajnoczi 415e77abbe9SStefan Hajnoczi elem_addr = vq->used + 416e77abbe9SStefan Hajnoczi offsetof(struct vring_used, ring) + 417e77abbe9SStefan Hajnoczi (vq->last_used_idx % vq->size) * 418e77abbe9SStefan Hajnoczi sizeof(struct vring_used_elem); 419be3a6781SGreg Kurz 420be3a6781SGreg Kurz if (desc_idx) { 4211999a70aSThomas Huth addr = elem_addr + offsetof(struct vring_used_elem, id); 422bccd82b4SStefan Hajnoczi *desc_idx = qvirtio_readl(vq->vdev, qts, addr); 423e77abbe9SStefan Hajnoczi } 424e77abbe9SStefan Hajnoczi 425be3a6781SGreg Kurz if (len) { 4261999a70aSThomas Huth addr = elem_addr + offsetof(struct vring_used_elem, len); 427bccd82b4SStefan Hajnoczi *len = qvirtio_readw(vq->vdev, qts, addr); 428be3a6781SGreg Kurz } 429be3a6781SGreg Kurz 430e77abbe9SStefan Hajnoczi vq->last_used_idx++; 431e77abbe9SStefan Hajnoczi return true; 432e77abbe9SStefan Hajnoczi } 433e77abbe9SStefan Hajnoczi 4341999a70aSThomas Huth void qvirtqueue_set_used_event(QTestState *qts, QVirtQueue *vq, uint16_t idx) 4351053587cSMarc Marí { 4361053587cSMarc Marí g_assert(vq->event); 4371053587cSMarc Marí 4381053587cSMarc Marí /* vq->avail->used_event */ 439bccd82b4SStefan Hajnoczi qvirtio_writew(vq->vdev, qts, vq->avail + 4 + (2 * vq->size), idx); 4401053587cSMarc Marí } 4412f84a92eSThomas Huth 44234c97748SEmanuele Giuseppe Esposito void qvirtio_start_device(QVirtioDevice *vdev) 44334c97748SEmanuele Giuseppe Esposito { 44434c97748SEmanuele Giuseppe Esposito qvirtio_reset(vdev); 44534c97748SEmanuele Giuseppe Esposito qvirtio_set_acknowledge(vdev); 44634c97748SEmanuele Giuseppe Esposito qvirtio_set_driver(vdev); 44734c97748SEmanuele Giuseppe Esposito } 448d5006a45SPaolo Bonzini 449d5006a45SPaolo Bonzini bool qvirtio_is_big_endian(QVirtioDevice *d) 450d5006a45SPaolo Bonzini { 451d5006a45SPaolo Bonzini return d->big_endian; 452d5006a45SPaolo Bonzini } 453