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" 1246e0cf76SMarc Marí #include "libqtest.h" 13*a2ce7dbdSPaolo 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); 2631053587cSMarc Marí /* vq->used->avail_event */ 264bccd82b4SStefan Hajnoczi qvirtio_writew(vq->vdev, qts, vq->used + 2 + 265bccd82b4SStefan Hajnoczi sizeof(struct vring_used_elem) * vq->size, 0); 266bf3c63d2SMarc Marí } 267bf3c63d2SMarc Marí 2681999a70aSThomas Huth QVRingIndirectDesc *qvring_indirect_desc_setup(QTestState *qs, QVirtioDevice *d, 2691999a70aSThomas Huth QGuestAllocator *alloc, 2701999a70aSThomas Huth uint16_t elem) 271f294b029SMarc Marí { 272f294b029SMarc Marí int i; 273f294b029SMarc Marí QVRingIndirectDesc *indirect = g_malloc(sizeof(*indirect)); 274f294b029SMarc Marí 275f294b029SMarc Marí indirect->index = 0; 276f294b029SMarc Marí indirect->elem = elem; 277780b11a0SStefan Hajnoczi indirect->desc = guest_alloc(alloc, sizeof(struct vring_desc) * elem); 278f294b029SMarc Marí 279f294b029SMarc Marí for (i = 0; i < elem - 1; ++i) { 280f294b029SMarc Marí /* indirect->desc[i].addr */ 281bccd82b4SStefan Hajnoczi qvirtio_writeq(d, qs, indirect->desc + (16 * i), 0); 282f294b029SMarc Marí /* indirect->desc[i].flags */ 283bccd82b4SStefan Hajnoczi qvirtio_writew(d, qs, indirect->desc + (16 * i) + 12, 284bccd82b4SStefan Hajnoczi VRING_DESC_F_NEXT); 285f294b029SMarc Marí /* indirect->desc[i].next */ 286bccd82b4SStefan Hajnoczi qvirtio_writew(d, qs, indirect->desc + (16 * i) + 14, i + 1); 287f294b029SMarc Marí } 288f294b029SMarc Marí 289f294b029SMarc Marí return indirect; 290f294b029SMarc Marí } 291f294b029SMarc Marí 292bccd82b4SStefan Hajnoczi void qvring_indirect_desc_add(QVirtioDevice *d, QTestState *qts, 293bccd82b4SStefan Hajnoczi QVRingIndirectDesc *indirect, 2941999a70aSThomas Huth uint64_t data, uint32_t len, bool write) 295f294b029SMarc Marí { 296f294b029SMarc Marí uint16_t flags; 297f294b029SMarc Marí 298f294b029SMarc Marí g_assert_cmpint(indirect->index, <, indirect->elem); 299f294b029SMarc Marí 300bccd82b4SStefan Hajnoczi flags = qvirtio_readw(d, qts, indirect->desc + 301bccd82b4SStefan Hajnoczi (16 * indirect->index) + 12); 302f294b029SMarc Marí 303f294b029SMarc Marí if (write) { 304ee3b850aSStefan Hajnoczi flags |= VRING_DESC_F_WRITE; 305f294b029SMarc Marí } 306f294b029SMarc Marí 307f294b029SMarc Marí /* indirect->desc[indirect->index].addr */ 308bccd82b4SStefan Hajnoczi qvirtio_writeq(d, qts, indirect->desc + (16 * indirect->index), data); 309f294b029SMarc Marí /* indirect->desc[indirect->index].len */ 310bccd82b4SStefan Hajnoczi qvirtio_writel(d, qts, indirect->desc + (16 * indirect->index) + 8, len); 311f294b029SMarc Marí /* indirect->desc[indirect->index].flags */ 312bccd82b4SStefan Hajnoczi qvirtio_writew(d, qts, indirect->desc + (16 * indirect->index) + 12, 313bccd82b4SStefan Hajnoczi flags); 314f294b029SMarc Marí 315f294b029SMarc Marí indirect->index++; 316f294b029SMarc Marí } 317f294b029SMarc Marí 3181999a70aSThomas Huth uint32_t qvirtqueue_add(QTestState *qts, QVirtQueue *vq, uint64_t data, 3191999a70aSThomas Huth uint32_t len, bool write, bool next) 320bf3c63d2SMarc Marí { 321bf3c63d2SMarc Marí uint16_t flags = 0; 322bf3c63d2SMarc Marí vq->num_free--; 323bf3c63d2SMarc Marí 324bf3c63d2SMarc Marí if (write) { 325ee3b850aSStefan Hajnoczi flags |= VRING_DESC_F_WRITE; 326bf3c63d2SMarc Marí } 327bf3c63d2SMarc Marí 328bf3c63d2SMarc Marí if (next) { 329ee3b850aSStefan Hajnoczi flags |= VRING_DESC_F_NEXT; 330bf3c63d2SMarc Marí } 331bf3c63d2SMarc Marí 332bf3c63d2SMarc Marí /* vq->desc[vq->free_head].addr */ 333bccd82b4SStefan Hajnoczi qvirtio_writeq(vq->vdev, qts, vq->desc + (16 * vq->free_head), data); 334bf3c63d2SMarc Marí /* vq->desc[vq->free_head].len */ 335bccd82b4SStefan Hajnoczi qvirtio_writel(vq->vdev, qts, vq->desc + (16 * vq->free_head) + 8, len); 336bf3c63d2SMarc Marí /* vq->desc[vq->free_head].flags */ 337bccd82b4SStefan Hajnoczi qvirtio_writew(vq->vdev, qts, vq->desc + (16 * vq->free_head) + 12, flags); 338bf3c63d2SMarc Marí 339bf3c63d2SMarc Marí return vq->free_head++; /* Return and increase, in this order */ 340bf3c63d2SMarc Marí } 341bf3c63d2SMarc Marí 3421999a70aSThomas Huth uint32_t qvirtqueue_add_indirect(QTestState *qts, QVirtQueue *vq, 3431999a70aSThomas Huth QVRingIndirectDesc *indirect) 344f294b029SMarc Marí { 345f294b029SMarc Marí g_assert(vq->indirect); 346f294b029SMarc Marí g_assert_cmpint(vq->size, >=, indirect->elem); 347f294b029SMarc Marí g_assert_cmpint(indirect->index, ==, indirect->elem); 348f294b029SMarc Marí 349f294b029SMarc Marí vq->num_free--; 350f294b029SMarc Marí 351f294b029SMarc Marí /* vq->desc[vq->free_head].addr */ 352bccd82b4SStefan Hajnoczi qvirtio_writeq(vq->vdev, qts, vq->desc + (16 * vq->free_head), 353bccd82b4SStefan Hajnoczi indirect->desc); 354f294b029SMarc Marí /* vq->desc[vq->free_head].len */ 355bccd82b4SStefan Hajnoczi qvirtio_writel(vq->vdev, qts, vq->desc + (16 * vq->free_head) + 8, 356780b11a0SStefan Hajnoczi sizeof(struct vring_desc) * indirect->elem); 357f294b029SMarc Marí /* vq->desc[vq->free_head].flags */ 358bccd82b4SStefan Hajnoczi qvirtio_writew(vq->vdev, qts, vq->desc + (16 * vq->free_head) + 12, 3591999a70aSThomas Huth VRING_DESC_F_INDIRECT); 360f294b029SMarc Marí 361f294b029SMarc Marí return vq->free_head++; /* Return and increase, in this order */ 362f294b029SMarc Marí } 363f294b029SMarc Marí 3641999a70aSThomas Huth void qvirtqueue_kick(QTestState *qts, QVirtioDevice *d, QVirtQueue *vq, 3651999a70aSThomas Huth uint32_t free_head) 366bf3c63d2SMarc Marí { 367bf3c63d2SMarc Marí /* vq->avail->idx */ 368bccd82b4SStefan Hajnoczi uint16_t idx = qvirtio_readw(d, qts, vq->avail + 2); 3691053587cSMarc Marí /* vq->used->flags */ 3701053587cSMarc Marí uint16_t flags; 3711053587cSMarc Marí /* vq->used->avail_event */ 3721053587cSMarc Marí uint16_t avail_event; 373bf3c63d2SMarc Marí 374bf3c63d2SMarc Marí /* vq->avail->ring[idx % vq->size] */ 375bccd82b4SStefan Hajnoczi qvirtio_writew(d, qts, vq->avail + 4 + (2 * (idx % vq->size)), free_head); 376bf3c63d2SMarc Marí /* vq->avail->idx */ 377bccd82b4SStefan Hajnoczi qvirtio_writew(d, qts, vq->avail + 2, idx + 1); 378bf3c63d2SMarc Marí 3791053587cSMarc Marí /* Must read after idx is updated */ 380bccd82b4SStefan Hajnoczi flags = qvirtio_readw(d, qts, vq->avail); 381bccd82b4SStefan Hajnoczi avail_event = qvirtio_readw(d, qts, vq->used + 4 + 382780b11a0SStefan Hajnoczi sizeof(struct vring_used_elem) * vq->size); 3831053587cSMarc Marí 3841053587cSMarc Marí /* < 1 because we add elements to avail queue one by one */ 385ee3b850aSStefan Hajnoczi if ((flags & VRING_USED_F_NO_NOTIFY) == 0 && 3861053587cSMarc Marí (!vq->event || (uint16_t)(idx-avail_event) < 1)) { 3876b9cdf4cSLaurent Vivier d->bus->virtqueue_kick(d, vq); 388bf3c63d2SMarc Marí } 3891053587cSMarc Marí } 3901053587cSMarc Marí 391e77abbe9SStefan Hajnoczi /* 392e77abbe9SStefan Hajnoczi * qvirtqueue_get_buf: 393e77abbe9SStefan Hajnoczi * @desc_idx: A pointer that is filled with the vq->desc[] index, may be NULL 394be3a6781SGreg Kurz * @len: A pointer that is filled with the length written into the buffer, may 395be3a6781SGreg Kurz * be NULL 396e77abbe9SStefan Hajnoczi * 397e77abbe9SStefan Hajnoczi * This function gets the next used element if there is one ready. 398e77abbe9SStefan Hajnoczi * 399e77abbe9SStefan Hajnoczi * Returns: true if an element was ready, false otherwise 400e77abbe9SStefan Hajnoczi */ 4011999a70aSThomas Huth bool qvirtqueue_get_buf(QTestState *qts, QVirtQueue *vq, uint32_t *desc_idx, 4021999a70aSThomas Huth uint32_t *len) 403e77abbe9SStefan Hajnoczi { 404e77abbe9SStefan Hajnoczi uint16_t idx; 4051999a70aSThomas Huth uint64_t elem_addr, addr; 406e77abbe9SStefan Hajnoczi 407bccd82b4SStefan Hajnoczi idx = qvirtio_readw(vq->vdev, qts, 408bccd82b4SStefan Hajnoczi vq->used + offsetof(struct vring_used, idx)); 409e77abbe9SStefan Hajnoczi if (idx == vq->last_used_idx) { 410e77abbe9SStefan Hajnoczi return false; 411e77abbe9SStefan Hajnoczi } 412e77abbe9SStefan Hajnoczi 413e77abbe9SStefan Hajnoczi elem_addr = vq->used + 414e77abbe9SStefan Hajnoczi offsetof(struct vring_used, ring) + 415e77abbe9SStefan Hajnoczi (vq->last_used_idx % vq->size) * 416e77abbe9SStefan Hajnoczi sizeof(struct vring_used_elem); 417be3a6781SGreg Kurz 418be3a6781SGreg Kurz if (desc_idx) { 4191999a70aSThomas Huth addr = elem_addr + offsetof(struct vring_used_elem, id); 420bccd82b4SStefan Hajnoczi *desc_idx = qvirtio_readl(vq->vdev, qts, addr); 421e77abbe9SStefan Hajnoczi } 422e77abbe9SStefan Hajnoczi 423be3a6781SGreg Kurz if (len) { 4241999a70aSThomas Huth addr = elem_addr + offsetof(struct vring_used_elem, len); 425bccd82b4SStefan Hajnoczi *len = qvirtio_readw(vq->vdev, qts, addr); 426be3a6781SGreg Kurz } 427be3a6781SGreg Kurz 428e77abbe9SStefan Hajnoczi vq->last_used_idx++; 429e77abbe9SStefan Hajnoczi return true; 430e77abbe9SStefan Hajnoczi } 431e77abbe9SStefan Hajnoczi 4321999a70aSThomas Huth void qvirtqueue_set_used_event(QTestState *qts, QVirtQueue *vq, uint16_t idx) 4331053587cSMarc Marí { 4341053587cSMarc Marí g_assert(vq->event); 4351053587cSMarc Marí 4361053587cSMarc Marí /* vq->avail->used_event */ 437bccd82b4SStefan Hajnoczi qvirtio_writew(vq->vdev, qts, vq->avail + 4 + (2 * vq->size), idx); 4381053587cSMarc Marí } 4392f84a92eSThomas Huth 44034c97748SEmanuele Giuseppe Esposito void qvirtio_start_device(QVirtioDevice *vdev) 44134c97748SEmanuele Giuseppe Esposito { 44234c97748SEmanuele Giuseppe Esposito qvirtio_reset(vdev); 44334c97748SEmanuele Giuseppe Esposito qvirtio_set_acknowledge(vdev); 44434c97748SEmanuele Giuseppe Esposito qvirtio_set_driver(vdev); 44534c97748SEmanuele Giuseppe Esposito } 446d5006a45SPaolo Bonzini 447d5006a45SPaolo Bonzini bool qvirtio_is_big_endian(QVirtioDevice *d) 448d5006a45SPaolo Bonzini { 449d5006a45SPaolo Bonzini return d->big_endian; 450d5006a45SPaolo Bonzini } 451