xref: /openbmc/qemu/tests/qtest/libqos/virtio.c (revision 6a4180af9686830d88c387baab6d79563ce42a15)
11cf4323eSThomas Huth /*
21cf4323eSThomas Huth  * libqos virtio driver
31cf4323eSThomas Huth  *
41cf4323eSThomas Huth  * Copyright (c) 2014 Marc Marí
51cf4323eSThomas Huth  *
61cf4323eSThomas Huth  * This work is licensed under the terms of the GNU GPL, version 2 or later.
71cf4323eSThomas Huth  * See the COPYING file in the top-level directory.
81cf4323eSThomas Huth  */
91cf4323eSThomas Huth 
101cf4323eSThomas Huth #include "qemu/osdep.h"
111cf4323eSThomas Huth #include "qemu/bswap.h"
12907b5105SMarc-André Lureau #include "../libqtest.h"
13a2ce7dbdSPaolo Bonzini #include "virtio.h"
141cf4323eSThomas Huth #include "standard-headers/linux/virtio_config.h"
151cf4323eSThomas Huth #include "standard-headers/linux/virtio_ring.h"
161cf4323eSThomas Huth 
171cf4323eSThomas Huth /*
181cf4323eSThomas Huth  * qtest_readX/writeX() functions transfer host endian from/to guest endian.
191cf4323eSThomas Huth  * This works great for Legacy VIRTIO devices where we need guest endian
201cf4323eSThomas Huth  * accesses.  For VIRTIO 1.0 the vring is little-endian so the automatic guest
211cf4323eSThomas Huth  * endianness conversion is not wanted.
221cf4323eSThomas Huth  *
231cf4323eSThomas Huth  * The following qvirtio_readX/writeX() functions handle Legacy and VIRTIO 1.0
241cf4323eSThomas Huth  * accesses seamlessly.
251cf4323eSThomas Huth  */
qvirtio_readw(QVirtioDevice * d,QTestState * qts,uint64_t addr)261cf4323eSThomas Huth static uint16_t qvirtio_readw(QVirtioDevice *d, QTestState *qts, uint64_t addr)
271cf4323eSThomas Huth {
281cf4323eSThomas Huth     uint16_t val = qtest_readw(qts, addr);
291cf4323eSThomas Huth 
301cf4323eSThomas Huth     if (d->features & (1ull << VIRTIO_F_VERSION_1) && qtest_big_endian(qts)) {
311cf4323eSThomas Huth         val = bswap16(val);
321cf4323eSThomas Huth     }
331cf4323eSThomas Huth     return val;
341cf4323eSThomas Huth }
351cf4323eSThomas Huth 
qvirtio_readl(QVirtioDevice * d,QTestState * qts,uint64_t addr)361cf4323eSThomas Huth static uint32_t qvirtio_readl(QVirtioDevice *d, QTestState *qts, uint64_t addr)
371cf4323eSThomas Huth {
381cf4323eSThomas Huth     uint32_t val = qtest_readl(qts, addr);
391cf4323eSThomas Huth 
401cf4323eSThomas Huth     if (d->features & (1ull << VIRTIO_F_VERSION_1) && qtest_big_endian(qts)) {
411cf4323eSThomas Huth         val = bswap32(val);
421cf4323eSThomas Huth     }
431cf4323eSThomas Huth     return val;
441cf4323eSThomas Huth }
451cf4323eSThomas Huth 
qvirtio_writew(QVirtioDevice * d,QTestState * qts,uint64_t addr,uint16_t val)461cf4323eSThomas Huth static void qvirtio_writew(QVirtioDevice *d, QTestState *qts,
471cf4323eSThomas Huth                            uint64_t addr, uint16_t val)
481cf4323eSThomas Huth {
491cf4323eSThomas Huth     if (d->features & (1ull << VIRTIO_F_VERSION_1) && qtest_big_endian(qts)) {
501cf4323eSThomas Huth         val = bswap16(val);
511cf4323eSThomas Huth     }
521cf4323eSThomas Huth     qtest_writew(qts, addr, val);
531cf4323eSThomas Huth }
541cf4323eSThomas Huth 
qvirtio_writel(QVirtioDevice * d,QTestState * qts,uint64_t addr,uint32_t val)551cf4323eSThomas Huth static void qvirtio_writel(QVirtioDevice *d, QTestState *qts,
561cf4323eSThomas Huth                            uint64_t addr, uint32_t val)
571cf4323eSThomas Huth {
581cf4323eSThomas Huth     if (d->features & (1ull << VIRTIO_F_VERSION_1) && qtest_big_endian(qts)) {
591cf4323eSThomas Huth         val = bswap32(val);
601cf4323eSThomas Huth     }
611cf4323eSThomas Huth     qtest_writel(qts, addr, val);
621cf4323eSThomas Huth }
631cf4323eSThomas Huth 
qvirtio_writeq(QVirtioDevice * d,QTestState * qts,uint64_t addr,uint64_t val)641cf4323eSThomas Huth static void qvirtio_writeq(QVirtioDevice *d, QTestState *qts,
651cf4323eSThomas Huth                            uint64_t addr, uint64_t val)
661cf4323eSThomas Huth {
671cf4323eSThomas Huth     if (d->features & (1ull << VIRTIO_F_VERSION_1) && qtest_big_endian(qts)) {
681cf4323eSThomas Huth         val = bswap64(val);
691cf4323eSThomas Huth     }
701cf4323eSThomas Huth     qtest_writeq(qts, addr, val);
711cf4323eSThomas Huth }
721cf4323eSThomas Huth 
qvirtio_config_readb(QVirtioDevice * d,uint64_t addr)731cf4323eSThomas Huth uint8_t qvirtio_config_readb(QVirtioDevice *d, uint64_t addr)
741cf4323eSThomas Huth {
751cf4323eSThomas Huth     g_assert_true(d->features_negotiated);
761cf4323eSThomas Huth     return d->bus->config_readb(d, addr);
771cf4323eSThomas Huth }
781cf4323eSThomas Huth 
qvirtio_config_readw(QVirtioDevice * d,uint64_t addr)791cf4323eSThomas Huth uint16_t qvirtio_config_readw(QVirtioDevice *d, uint64_t addr)
801cf4323eSThomas Huth {
811cf4323eSThomas Huth     g_assert_true(d->features_negotiated);
821cf4323eSThomas Huth     return d->bus->config_readw(d, addr);
831cf4323eSThomas Huth }
841cf4323eSThomas Huth 
qvirtio_config_readl(QVirtioDevice * d,uint64_t addr)851cf4323eSThomas Huth uint32_t qvirtio_config_readl(QVirtioDevice *d, uint64_t addr)
861cf4323eSThomas Huth {
871cf4323eSThomas Huth     g_assert_true(d->features_negotiated);
881cf4323eSThomas Huth     return d->bus->config_readl(d, addr);
891cf4323eSThomas Huth }
901cf4323eSThomas Huth 
qvirtio_config_readq(QVirtioDevice * d,uint64_t addr)911cf4323eSThomas Huth uint64_t qvirtio_config_readq(QVirtioDevice *d, uint64_t addr)
921cf4323eSThomas Huth {
931cf4323eSThomas Huth     g_assert_true(d->features_negotiated);
941cf4323eSThomas Huth     return d->bus->config_readq(d, addr);
951cf4323eSThomas Huth }
961cf4323eSThomas Huth 
qvirtio_get_features(QVirtioDevice * d)971cf4323eSThomas Huth uint64_t qvirtio_get_features(QVirtioDevice *d)
981cf4323eSThomas Huth {
991cf4323eSThomas Huth     return d->bus->get_features(d);
1001cf4323eSThomas Huth }
1011cf4323eSThomas Huth 
qvirtio_set_features(QVirtioDevice * d,uint64_t features)1021cf4323eSThomas Huth void qvirtio_set_features(QVirtioDevice *d, uint64_t features)
1031cf4323eSThomas Huth {
1043bd869f3SAlex Bennée     g_assert(!(features & QVIRTIO_F_BAD_FEATURE));
1053bd869f3SAlex Bennée 
1061cf4323eSThomas Huth     d->features = features;
1071cf4323eSThomas Huth     d->bus->set_features(d, features);
1081cf4323eSThomas Huth 
1091cf4323eSThomas Huth     /*
1101cf4323eSThomas Huth      * This could be a separate function for drivers that want to access
1111cf4323eSThomas Huth      * configuration space before setting FEATURES_OK, but no existing users
1121cf4323eSThomas Huth      * need that and it's less code for callers if this is done implicitly.
1131cf4323eSThomas Huth      */
1141cf4323eSThomas Huth     if (features & (1ull << VIRTIO_F_VERSION_1)) {
1151cf4323eSThomas Huth         uint8_t status = d->bus->get_status(d) |
1161cf4323eSThomas Huth                          VIRTIO_CONFIG_S_FEATURES_OK;
1171cf4323eSThomas Huth 
1181cf4323eSThomas Huth         d->bus->set_status(d, status);
1191cf4323eSThomas Huth         g_assert_cmphex(d->bus->get_status(d), ==, status);
1201cf4323eSThomas Huth     }
1211cf4323eSThomas Huth 
1221cf4323eSThomas Huth     d->features_negotiated = true;
1231cf4323eSThomas Huth }
1241cf4323eSThomas Huth 
qvirtqueue_setup(QVirtioDevice * d,QGuestAllocator * alloc,uint16_t index)1251cf4323eSThomas Huth QVirtQueue *qvirtqueue_setup(QVirtioDevice *d,
1261cf4323eSThomas Huth                              QGuestAllocator *alloc, uint16_t index)
1271cf4323eSThomas Huth {
1281cf4323eSThomas Huth     g_assert_true(d->features_negotiated);
1291cf4323eSThomas Huth     return d->bus->virtqueue_setup(d, alloc, index);
1301cf4323eSThomas Huth }
1311cf4323eSThomas Huth 
qvirtqueue_cleanup(const QVirtioBus * bus,QVirtQueue * vq,QGuestAllocator * alloc)1321cf4323eSThomas Huth void qvirtqueue_cleanup(const QVirtioBus *bus, QVirtQueue *vq,
1331cf4323eSThomas Huth                         QGuestAllocator *alloc)
1341cf4323eSThomas Huth {
1351cf4323eSThomas Huth     return bus->virtqueue_cleanup(vq, alloc);
1361cf4323eSThomas Huth }
1371cf4323eSThomas Huth 
qvirtio_reset(QVirtioDevice * d)1381cf4323eSThomas Huth void qvirtio_reset(QVirtioDevice *d)
1391cf4323eSThomas Huth {
1401cf4323eSThomas Huth     d->bus->set_status(d, 0);
1411cf4323eSThomas Huth     g_assert_cmphex(d->bus->get_status(d), ==, 0);
1421cf4323eSThomas Huth     d->features_negotiated = false;
1431cf4323eSThomas Huth }
1441cf4323eSThomas Huth 
qvirtio_set_acknowledge(QVirtioDevice * d)1451cf4323eSThomas Huth void qvirtio_set_acknowledge(QVirtioDevice *d)
1461cf4323eSThomas Huth {
1471cf4323eSThomas Huth     d->bus->set_status(d, d->bus->get_status(d) | VIRTIO_CONFIG_S_ACKNOWLEDGE);
1481cf4323eSThomas Huth     g_assert_cmphex(d->bus->get_status(d), ==, VIRTIO_CONFIG_S_ACKNOWLEDGE);
1491cf4323eSThomas Huth }
1501cf4323eSThomas Huth 
qvirtio_set_driver(QVirtioDevice * d)1511cf4323eSThomas Huth void qvirtio_set_driver(QVirtioDevice *d)
1521cf4323eSThomas Huth {
1531cf4323eSThomas Huth     d->bus->set_status(d, d->bus->get_status(d) | VIRTIO_CONFIG_S_DRIVER);
1541cf4323eSThomas Huth     g_assert_cmphex(d->bus->get_status(d), ==,
1551cf4323eSThomas Huth                     VIRTIO_CONFIG_S_DRIVER | VIRTIO_CONFIG_S_ACKNOWLEDGE);
1561cf4323eSThomas Huth }
1571cf4323eSThomas Huth 
qvirtio_set_driver_ok(QVirtioDevice * d)1581cf4323eSThomas Huth void qvirtio_set_driver_ok(QVirtioDevice *d)
1591cf4323eSThomas Huth {
1601cf4323eSThomas Huth     d->bus->set_status(d, d->bus->get_status(d) | VIRTIO_CONFIG_S_DRIVER_OK);
1611cf4323eSThomas Huth     g_assert_cmphex(d->bus->get_status(d), ==, VIRTIO_CONFIG_S_DRIVER_OK |
1621cf4323eSThomas Huth                     VIRTIO_CONFIG_S_DRIVER | VIRTIO_CONFIG_S_ACKNOWLEDGE |
1631cf4323eSThomas Huth                     (d->features & (1ull << VIRTIO_F_VERSION_1) ?
1641cf4323eSThomas Huth                      VIRTIO_CONFIG_S_FEATURES_OK : 0));
1651cf4323eSThomas Huth }
1661cf4323eSThomas Huth 
qvirtio_wait_queue_isr(QTestState * qts,QVirtioDevice * d,QVirtQueue * vq,gint64 timeout_us)1671cf4323eSThomas Huth void qvirtio_wait_queue_isr(QTestState *qts, QVirtioDevice *d,
1681cf4323eSThomas Huth                             QVirtQueue *vq, gint64 timeout_us)
1691cf4323eSThomas Huth {
1701cf4323eSThomas Huth     gint64 start_time = g_get_monotonic_time();
1711cf4323eSThomas Huth 
1721cf4323eSThomas Huth     for (;;) {
1731cf4323eSThomas Huth         qtest_clock_step(qts, 100);
1741cf4323eSThomas Huth         if (d->bus->get_queue_isr_status(d, vq)) {
1751cf4323eSThomas Huth             return;
1761cf4323eSThomas Huth         }
1771cf4323eSThomas Huth         g_assert(g_get_monotonic_time() - start_time <= timeout_us);
1781cf4323eSThomas Huth     }
1791cf4323eSThomas Huth }
1801cf4323eSThomas Huth 
1811cf4323eSThomas Huth /* Wait for the status byte at given guest memory address to be set
1821cf4323eSThomas Huth  *
1831cf4323eSThomas Huth  * The virtqueue interrupt must not be raised, making this useful for testing
1841cf4323eSThomas Huth  * event_index functionality.
1851cf4323eSThomas Huth  */
qvirtio_wait_status_byte_no_isr(QTestState * qts,QVirtioDevice * d,QVirtQueue * vq,uint64_t addr,gint64 timeout_us)1861cf4323eSThomas Huth uint8_t qvirtio_wait_status_byte_no_isr(QTestState *qts, QVirtioDevice *d,
1871cf4323eSThomas Huth                                         QVirtQueue *vq,
1881cf4323eSThomas Huth                                         uint64_t addr,
1891cf4323eSThomas Huth                                         gint64 timeout_us)
1901cf4323eSThomas Huth {
1911cf4323eSThomas Huth     gint64 start_time = g_get_monotonic_time();
1921cf4323eSThomas Huth     uint8_t val;
1931cf4323eSThomas Huth 
1941cf4323eSThomas Huth     while ((val = qtest_readb(qts, addr)) == 0xff) {
1951cf4323eSThomas Huth         qtest_clock_step(qts, 100);
1961cf4323eSThomas Huth         g_assert(!d->bus->get_queue_isr_status(d, vq));
1971cf4323eSThomas Huth         g_assert(g_get_monotonic_time() - start_time <= timeout_us);
1981cf4323eSThomas Huth     }
1991cf4323eSThomas Huth     return val;
2001cf4323eSThomas Huth }
2011cf4323eSThomas Huth 
2021cf4323eSThomas Huth /*
2031cf4323eSThomas Huth  * qvirtio_wait_used_elem:
2041cf4323eSThomas Huth  * @desc_idx: The next expected vq->desc[] index in the used ring
2051cf4323eSThomas Huth  * @len: A pointer that is filled with the length written into the buffer, may
2061cf4323eSThomas Huth  *       be NULL
2071cf4323eSThomas Huth  * @timeout_us: How many microseconds to wait before failing
2081cf4323eSThomas Huth  *
2091cf4323eSThomas Huth  * This function waits for the next completed request on the used ring.
2101cf4323eSThomas Huth  */
qvirtio_wait_used_elem(QTestState * qts,QVirtioDevice * d,QVirtQueue * vq,uint32_t desc_idx,uint32_t * len,gint64 timeout_us)2111cf4323eSThomas Huth void qvirtio_wait_used_elem(QTestState *qts, QVirtioDevice *d,
2121cf4323eSThomas Huth                             QVirtQueue *vq,
2131cf4323eSThomas Huth                             uint32_t desc_idx,
2141cf4323eSThomas Huth                             uint32_t *len,
2151cf4323eSThomas Huth                             gint64 timeout_us)
2161cf4323eSThomas Huth {
2171cf4323eSThomas Huth     gint64 start_time = g_get_monotonic_time();
2181cf4323eSThomas Huth 
2191cf4323eSThomas Huth     for (;;) {
2201cf4323eSThomas Huth         uint32_t got_desc_idx;
2211cf4323eSThomas Huth 
2221cf4323eSThomas Huth         qtest_clock_step(qts, 100);
2231cf4323eSThomas Huth 
2241cf4323eSThomas Huth         if (d->bus->get_queue_isr_status(d, vq) &&
2251cf4323eSThomas Huth             qvirtqueue_get_buf(qts, vq, &got_desc_idx, len)) {
2261cf4323eSThomas Huth             g_assert_cmpint(got_desc_idx, ==, desc_idx);
2271cf4323eSThomas Huth             return;
2281cf4323eSThomas Huth         }
2291cf4323eSThomas Huth 
2301cf4323eSThomas Huth         g_assert(g_get_monotonic_time() - start_time <= timeout_us);
2311cf4323eSThomas Huth     }
2321cf4323eSThomas Huth }
2331cf4323eSThomas Huth 
qvirtio_wait_config_isr(QVirtioDevice * d,gint64 timeout_us)2341cf4323eSThomas Huth void qvirtio_wait_config_isr(QVirtioDevice *d, gint64 timeout_us)
2351cf4323eSThomas Huth {
2361cf4323eSThomas Huth     d->bus->wait_config_isr_status(d, timeout_us);
2371cf4323eSThomas Huth }
2381cf4323eSThomas Huth 
qvring_init(QTestState * qts,const QGuestAllocator * alloc,QVirtQueue * vq,uint64_t addr)2391cf4323eSThomas Huth void qvring_init(QTestState *qts, const QGuestAllocator *alloc, QVirtQueue *vq,
2401cf4323eSThomas Huth                  uint64_t addr)
2411cf4323eSThomas Huth {
2421cf4323eSThomas Huth     int i;
2431cf4323eSThomas Huth 
2441cf4323eSThomas Huth     vq->desc = addr;
2451cf4323eSThomas Huth     vq->avail = vq->desc + vq->size * sizeof(struct vring_desc);
2461cf4323eSThomas Huth     vq->used = (uint64_t)((vq->avail + sizeof(uint16_t) * (3 + vq->size)
2471cf4323eSThomas Huth         + vq->align - 1) & ~(vq->align - 1));
2481cf4323eSThomas Huth 
2491cf4323eSThomas Huth     for (i = 0; i < vq->size - 1; i++) {
2501cf4323eSThomas Huth         /* vq->desc[i].addr */
2511cf4323eSThomas Huth         qvirtio_writeq(vq->vdev, qts, vq->desc + (16 * i), 0);
2521cf4323eSThomas Huth         /* vq->desc[i].next */
2531cf4323eSThomas Huth         qvirtio_writew(vq->vdev, qts, vq->desc + (16 * i) + 14, i + 1);
2541cf4323eSThomas Huth     }
2551cf4323eSThomas Huth 
2561cf4323eSThomas Huth     /* vq->avail->flags */
2571cf4323eSThomas Huth     qvirtio_writew(vq->vdev, qts, vq->avail, 0);
2581cf4323eSThomas Huth     /* vq->avail->idx */
2591cf4323eSThomas Huth     qvirtio_writew(vq->vdev, qts, vq->avail + 2, 0);
2601cf4323eSThomas Huth     /* vq->avail->used_event */
2611cf4323eSThomas Huth     qvirtio_writew(vq->vdev, qts, vq->avail + 4 + (2 * vq->size), 0);
2621cf4323eSThomas Huth 
2631cf4323eSThomas Huth     /* vq->used->flags */
2641cf4323eSThomas Huth     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);
2671cf4323eSThomas Huth     /* vq->used->avail_event */
2683283843aSDaniel Henrique Barboza     qvirtio_writew(vq->vdev, qts, vq->used + 4 +
2691cf4323eSThomas Huth                    sizeof(struct vring_used_elem) * vq->size, 0);
2701cf4323eSThomas Huth }
2711cf4323eSThomas Huth 
qvring_indirect_desc_setup(QTestState * qs,QVirtioDevice * d,QGuestAllocator * alloc,uint16_t elem)2721cf4323eSThomas Huth QVRingIndirectDesc *qvring_indirect_desc_setup(QTestState *qs, QVirtioDevice *d,
2731cf4323eSThomas Huth                                                QGuestAllocator *alloc,
2741cf4323eSThomas Huth                                                uint16_t elem)
2751cf4323eSThomas Huth {
2761cf4323eSThomas Huth     int i;
2771cf4323eSThomas Huth     QVRingIndirectDesc *indirect = g_malloc(sizeof(*indirect));
2781cf4323eSThomas Huth 
2791cf4323eSThomas Huth     indirect->index = 0;
2801cf4323eSThomas Huth     indirect->elem = elem;
2811cf4323eSThomas Huth     indirect->desc = guest_alloc(alloc, sizeof(struct vring_desc) * elem);
2821cf4323eSThomas Huth 
2832791490dSDaniel Henrique Barboza     for (i = 0; i < elem; ++i) {
2841cf4323eSThomas Huth         /* indirect->desc[i].addr */
2851cf4323eSThomas Huth         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) {
2941cf4323eSThomas Huth             /* indirect->desc[i].flags */
2951cf4323eSThomas Huth             qvirtio_writew(d, qs, indirect->desc + (16 * i) + 12,
2961cf4323eSThomas Huth                            VRING_DESC_F_NEXT);
2972791490dSDaniel Henrique Barboza 
2981cf4323eSThomas Huth             /* indirect->desc[i].next */
2991cf4323eSThomas Huth             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         }
3041cf4323eSThomas Huth     }
3051cf4323eSThomas Huth 
3061cf4323eSThomas Huth     return indirect;
3071cf4323eSThomas Huth }
3081cf4323eSThomas Huth 
qvring_indirect_desc_add(QVirtioDevice * d,QTestState * qts,QVRingIndirectDesc * indirect,uint64_t data,uint32_t len,bool write)3091cf4323eSThomas Huth void qvring_indirect_desc_add(QVirtioDevice *d, QTestState *qts,
3101cf4323eSThomas Huth                               QVRingIndirectDesc *indirect,
3111cf4323eSThomas Huth                               uint64_t data, uint32_t len, bool write)
3121cf4323eSThomas Huth {
3131cf4323eSThomas Huth     uint16_t flags;
3141cf4323eSThomas Huth 
3151cf4323eSThomas Huth     g_assert_cmpint(indirect->index, <, indirect->elem);
3161cf4323eSThomas Huth 
3171cf4323eSThomas Huth     flags = qvirtio_readw(d, qts, indirect->desc +
3181cf4323eSThomas Huth                                   (16 * indirect->index) + 12);
3191cf4323eSThomas Huth 
3201cf4323eSThomas Huth     if (write) {
3211cf4323eSThomas Huth         flags |= VRING_DESC_F_WRITE;
3221cf4323eSThomas Huth     }
3231cf4323eSThomas Huth 
3241cf4323eSThomas Huth     /* indirect->desc[indirect->index].addr */
3251cf4323eSThomas Huth     qvirtio_writeq(d, qts, indirect->desc + (16 * indirect->index), data);
3261cf4323eSThomas Huth     /* indirect->desc[indirect->index].len */
3271cf4323eSThomas Huth     qvirtio_writel(d, qts, indirect->desc + (16 * indirect->index) + 8, len);
3281cf4323eSThomas Huth     /* indirect->desc[indirect->index].flags */
3291cf4323eSThomas Huth     qvirtio_writew(d, qts, indirect->desc + (16 * indirect->index) + 12,
3301cf4323eSThomas Huth                    flags);
3311cf4323eSThomas Huth 
3321cf4323eSThomas Huth     indirect->index++;
3331cf4323eSThomas Huth }
3341cf4323eSThomas Huth 
qvirtqueue_add(QTestState * qts,QVirtQueue * vq,uint64_t data,uint32_t len,bool write,bool next)3351cf4323eSThomas Huth uint32_t qvirtqueue_add(QTestState *qts, QVirtQueue *vq, uint64_t data,
3361cf4323eSThomas Huth                         uint32_t len, bool write, bool next)
3371cf4323eSThomas Huth {
3381cf4323eSThomas Huth     uint16_t flags = 0;
3391cf4323eSThomas Huth     vq->num_free--;
3401cf4323eSThomas Huth 
3411cf4323eSThomas Huth     if (write) {
3421cf4323eSThomas Huth         flags |= VRING_DESC_F_WRITE;
3431cf4323eSThomas Huth     }
3441cf4323eSThomas Huth 
3451cf4323eSThomas Huth     if (next) {
3461cf4323eSThomas Huth         flags |= VRING_DESC_F_NEXT;
3471cf4323eSThomas Huth     }
3481cf4323eSThomas Huth 
3491cf4323eSThomas Huth     /* vq->desc[vq->free_head].addr */
3501cf4323eSThomas Huth     qvirtio_writeq(vq->vdev, qts, vq->desc + (16 * vq->free_head), data);
3511cf4323eSThomas Huth     /* vq->desc[vq->free_head].len */
3521cf4323eSThomas Huth     qvirtio_writel(vq->vdev, qts, vq->desc + (16 * vq->free_head) + 8, len);
3531cf4323eSThomas Huth     /* vq->desc[vq->free_head].flags */
3541cf4323eSThomas Huth     qvirtio_writew(vq->vdev, qts, vq->desc + (16 * vq->free_head) + 12, flags);
3551cf4323eSThomas Huth 
3561cf4323eSThomas Huth     return vq->free_head++; /* Return and increase, in this order */
3571cf4323eSThomas Huth }
3581cf4323eSThomas Huth 
qvirtqueue_add_indirect(QTestState * qts,QVirtQueue * vq,QVRingIndirectDesc * indirect)3591cf4323eSThomas Huth uint32_t qvirtqueue_add_indirect(QTestState *qts, QVirtQueue *vq,
3601cf4323eSThomas Huth                                  QVRingIndirectDesc *indirect)
3611cf4323eSThomas Huth {
3621cf4323eSThomas Huth     g_assert(vq->indirect);
3631cf4323eSThomas Huth     g_assert_cmpint(vq->size, >=, indirect->elem);
3641cf4323eSThomas Huth     g_assert_cmpint(indirect->index, ==, indirect->elem);
3651cf4323eSThomas Huth 
3661cf4323eSThomas Huth     vq->num_free--;
3671cf4323eSThomas Huth 
3681cf4323eSThomas Huth     /* vq->desc[vq->free_head].addr */
3691cf4323eSThomas Huth     qvirtio_writeq(vq->vdev, qts, vq->desc + (16 * vq->free_head),
3701cf4323eSThomas Huth                    indirect->desc);
3711cf4323eSThomas Huth     /* vq->desc[vq->free_head].len */
3721cf4323eSThomas Huth     qvirtio_writel(vq->vdev, qts, vq->desc + (16 * vq->free_head) + 8,
3731cf4323eSThomas Huth                    sizeof(struct vring_desc) * indirect->elem);
3741cf4323eSThomas Huth     /* vq->desc[vq->free_head].flags */
3751cf4323eSThomas Huth     qvirtio_writew(vq->vdev, qts, vq->desc + (16 * vq->free_head) + 12,
3761cf4323eSThomas Huth                    VRING_DESC_F_INDIRECT);
3771cf4323eSThomas Huth 
3781cf4323eSThomas Huth     return vq->free_head++; /* Return and increase, in this order */
3791cf4323eSThomas Huth }
3801cf4323eSThomas Huth 
qvirtqueue_kick(QTestState * qts,QVirtioDevice * d,QVirtQueue * vq,uint32_t free_head)3811cf4323eSThomas Huth void qvirtqueue_kick(QTestState *qts, QVirtioDevice *d, QVirtQueue *vq,
3821cf4323eSThomas Huth                      uint32_t free_head)
3831cf4323eSThomas Huth {
3841cf4323eSThomas Huth     /* vq->avail->idx */
3851cf4323eSThomas Huth     uint16_t idx = qvirtio_readw(d, qts, vq->avail + 2);
3861cf4323eSThomas Huth     /* vq->used->flags */
3871cf4323eSThomas Huth     uint16_t flags;
3881cf4323eSThomas Huth     /* vq->used->avail_event */
3891cf4323eSThomas Huth     uint16_t avail_event;
3901cf4323eSThomas Huth 
3911cf4323eSThomas Huth     /* vq->avail->ring[idx % vq->size] */
3921cf4323eSThomas Huth     qvirtio_writew(d, qts, vq->avail + 4 + (2 * (idx % vq->size)), free_head);
3931cf4323eSThomas Huth     /* vq->avail->idx */
3941cf4323eSThomas Huth     qvirtio_writew(d, qts, vq->avail + 2, idx + 1);
3951cf4323eSThomas Huth 
3961cf4323eSThomas Huth     /* Must read after idx is updated */
397*66e41188SZheyu Ma     flags = qvirtio_readw(d, qts, vq->used);
3981cf4323eSThomas Huth     avail_event = qvirtio_readw(d, qts, vq->used + 4 +
3991cf4323eSThomas Huth                                 sizeof(struct vring_used_elem) * vq->size);
4001cf4323eSThomas Huth 
4011cf4323eSThomas Huth     /* < 1 because we add elements to avail queue one by one */
4021cf4323eSThomas Huth     if ((flags & VRING_USED_F_NO_NOTIFY) == 0 &&
4031cf4323eSThomas Huth                             (!vq->event || (uint16_t)(idx-avail_event) < 1)) {
4041cf4323eSThomas Huth         d->bus->virtqueue_kick(d, vq);
4051cf4323eSThomas Huth     }
4061cf4323eSThomas Huth }
4071cf4323eSThomas Huth 
4081cf4323eSThomas Huth /*
4091cf4323eSThomas Huth  * qvirtqueue_get_buf:
4101cf4323eSThomas Huth  * @desc_idx: A pointer that is filled with the vq->desc[] index, may be NULL
4111cf4323eSThomas Huth  * @len: A pointer that is filled with the length written into the buffer, may
4121cf4323eSThomas Huth  *       be NULL
4131cf4323eSThomas Huth  *
4141cf4323eSThomas Huth  * This function gets the next used element if there is one ready.
4151cf4323eSThomas Huth  *
4161cf4323eSThomas Huth  * Returns: true if an element was ready, false otherwise
4171cf4323eSThomas Huth  */
qvirtqueue_get_buf(QTestState * qts,QVirtQueue * vq,uint32_t * desc_idx,uint32_t * len)4181cf4323eSThomas Huth bool qvirtqueue_get_buf(QTestState *qts, QVirtQueue *vq, uint32_t *desc_idx,
4191cf4323eSThomas Huth                         uint32_t *len)
4201cf4323eSThomas Huth {
4211cf4323eSThomas Huth     uint16_t idx;
4221cf4323eSThomas Huth     uint64_t elem_addr, addr;
4231cf4323eSThomas Huth 
4241cf4323eSThomas Huth     idx = qvirtio_readw(vq->vdev, qts,
4251cf4323eSThomas Huth                         vq->used + offsetof(struct vring_used, idx));
4261cf4323eSThomas Huth     if (idx == vq->last_used_idx) {
4271cf4323eSThomas Huth         return false;
4281cf4323eSThomas Huth     }
4291cf4323eSThomas Huth 
4301cf4323eSThomas Huth     elem_addr = vq->used +
4311cf4323eSThomas Huth         offsetof(struct vring_used, ring) +
4321cf4323eSThomas Huth         (vq->last_used_idx % vq->size) *
4331cf4323eSThomas Huth         sizeof(struct vring_used_elem);
4341cf4323eSThomas Huth 
4351cf4323eSThomas Huth     if (desc_idx) {
4361cf4323eSThomas Huth         addr = elem_addr + offsetof(struct vring_used_elem, id);
4371cf4323eSThomas Huth         *desc_idx = qvirtio_readl(vq->vdev, qts, addr);
4381cf4323eSThomas Huth     }
4391cf4323eSThomas Huth 
4401cf4323eSThomas Huth     if (len) {
4411cf4323eSThomas Huth         addr = elem_addr + offsetof(struct vring_used_elem, len);
4421cf4323eSThomas Huth         *len = qvirtio_readw(vq->vdev, qts, addr);
4431cf4323eSThomas Huth     }
4441cf4323eSThomas Huth 
4451cf4323eSThomas Huth     vq->last_used_idx++;
4461cf4323eSThomas Huth     return true;
4471cf4323eSThomas Huth }
4481cf4323eSThomas Huth 
qvirtqueue_set_used_event(QTestState * qts,QVirtQueue * vq,uint16_t idx)4491cf4323eSThomas Huth void qvirtqueue_set_used_event(QTestState *qts, QVirtQueue *vq, uint16_t idx)
4501cf4323eSThomas Huth {
4511cf4323eSThomas Huth     g_assert(vq->event);
4521cf4323eSThomas Huth 
4531cf4323eSThomas Huth     /* vq->avail->used_event */
4541cf4323eSThomas Huth     qvirtio_writew(vq->vdev, qts, vq->avail + 4 + (2 * vq->size), idx);
4551cf4323eSThomas Huth }
4561cf4323eSThomas Huth 
qvirtio_start_device(QVirtioDevice * vdev)4571cf4323eSThomas Huth void qvirtio_start_device(QVirtioDevice *vdev)
4581cf4323eSThomas Huth {
4591cf4323eSThomas Huth     qvirtio_reset(vdev);
4601cf4323eSThomas Huth     qvirtio_set_acknowledge(vdev);
4611cf4323eSThomas Huth     qvirtio_set_driver(vdev);
4621cf4323eSThomas Huth }
4631cf4323eSThomas Huth 
qvirtio_is_big_endian(QVirtioDevice * d)4641cf4323eSThomas Huth bool qvirtio_is_big_endian(QVirtioDevice *d)
4651cf4323eSThomas Huth {
4661cf4323eSThomas Huth     return d->big_endian;
4671cf4323eSThomas Huth }
468