qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

virtio.c (14101B)


      1 /*
      2  * libqos virtio driver
      3  *
      4  * Copyright (c) 2014 Marc MarĂ­
      5  *
      6  * This work is licensed under the terms of the GNU GPL, version 2 or later.
      7  * See the COPYING file in the top-level directory.
      8  */
      9 
     10 #include "qemu/osdep.h"
     11 #include "qemu/bswap.h"
     12 #include "../libqtest.h"
     13 #include "virtio.h"
     14 #include "standard-headers/linux/virtio_config.h"
     15 #include "standard-headers/linux/virtio_ring.h"
     16 
     17 /*
     18  * qtest_readX/writeX() functions transfer host endian from/to guest endian.
     19  * This works great for Legacy VIRTIO devices where we need guest endian
     20  * accesses.  For VIRTIO 1.0 the vring is little-endian so the automatic guest
     21  * endianness conversion is not wanted.
     22  *
     23  * The following qvirtio_readX/writeX() functions handle Legacy and VIRTIO 1.0
     24  * accesses seamlessly.
     25  */
     26 static uint16_t qvirtio_readw(QVirtioDevice *d, QTestState *qts, uint64_t addr)
     27 {
     28     uint16_t val = qtest_readw(qts, addr);
     29 
     30     if (d->features & (1ull << VIRTIO_F_VERSION_1) && qtest_big_endian(qts)) {
     31         val = bswap16(val);
     32     }
     33     return val;
     34 }
     35 
     36 static uint32_t qvirtio_readl(QVirtioDevice *d, QTestState *qts, uint64_t addr)
     37 {
     38     uint32_t val = qtest_readl(qts, addr);
     39 
     40     if (d->features & (1ull << VIRTIO_F_VERSION_1) && qtest_big_endian(qts)) {
     41         val = bswap32(val);
     42     }
     43     return val;
     44 }
     45 
     46 static void qvirtio_writew(QVirtioDevice *d, QTestState *qts,
     47                            uint64_t addr, uint16_t val)
     48 {
     49     if (d->features & (1ull << VIRTIO_F_VERSION_1) && qtest_big_endian(qts)) {
     50         val = bswap16(val);
     51     }
     52     qtest_writew(qts, addr, val);
     53 }
     54 
     55 static void qvirtio_writel(QVirtioDevice *d, QTestState *qts,
     56                            uint64_t addr, uint32_t val)
     57 {
     58     if (d->features & (1ull << VIRTIO_F_VERSION_1) && qtest_big_endian(qts)) {
     59         val = bswap32(val);
     60     }
     61     qtest_writel(qts, addr, val);
     62 }
     63 
     64 static void qvirtio_writeq(QVirtioDevice *d, QTestState *qts,
     65                            uint64_t addr, uint64_t val)
     66 {
     67     if (d->features & (1ull << VIRTIO_F_VERSION_1) && qtest_big_endian(qts)) {
     68         val = bswap64(val);
     69     }
     70     qtest_writeq(qts, addr, val);
     71 }
     72 
     73 uint8_t qvirtio_config_readb(QVirtioDevice *d, uint64_t addr)
     74 {
     75     g_assert_true(d->features_negotiated);
     76     return d->bus->config_readb(d, addr);
     77 }
     78 
     79 uint16_t qvirtio_config_readw(QVirtioDevice *d, uint64_t addr)
     80 {
     81     g_assert_true(d->features_negotiated);
     82     return d->bus->config_readw(d, addr);
     83 }
     84 
     85 uint32_t qvirtio_config_readl(QVirtioDevice *d, uint64_t addr)
     86 {
     87     g_assert_true(d->features_negotiated);
     88     return d->bus->config_readl(d, addr);
     89 }
     90 
     91 uint64_t qvirtio_config_readq(QVirtioDevice *d, uint64_t addr)
     92 {
     93     g_assert_true(d->features_negotiated);
     94     return d->bus->config_readq(d, addr);
     95 }
     96 
     97 uint64_t qvirtio_get_features(QVirtioDevice *d)
     98 {
     99     return d->bus->get_features(d);
    100 }
    101 
    102 void qvirtio_set_features(QVirtioDevice *d, uint64_t features)
    103 {
    104     g_assert(!(features & QVIRTIO_F_BAD_FEATURE));
    105 
    106     d->features = features;
    107     d->bus->set_features(d, features);
    108 
    109     /*
    110      * This could be a separate function for drivers that want to access
    111      * configuration space before setting FEATURES_OK, but no existing users
    112      * need that and it's less code for callers if this is done implicitly.
    113      */
    114     if (features & (1ull << VIRTIO_F_VERSION_1)) {
    115         uint8_t status = d->bus->get_status(d) |
    116                          VIRTIO_CONFIG_S_FEATURES_OK;
    117 
    118         d->bus->set_status(d, status);
    119         g_assert_cmphex(d->bus->get_status(d), ==, status);
    120     }
    121 
    122     d->features_negotiated = true;
    123 }
    124 
    125 QVirtQueue *qvirtqueue_setup(QVirtioDevice *d,
    126                              QGuestAllocator *alloc, uint16_t index)
    127 {
    128     g_assert_true(d->features_negotiated);
    129     return d->bus->virtqueue_setup(d, alloc, index);
    130 }
    131 
    132 void qvirtqueue_cleanup(const QVirtioBus *bus, QVirtQueue *vq,
    133                         QGuestAllocator *alloc)
    134 {
    135     return bus->virtqueue_cleanup(vq, alloc);
    136 }
    137 
    138 void qvirtio_reset(QVirtioDevice *d)
    139 {
    140     d->bus->set_status(d, 0);
    141     g_assert_cmphex(d->bus->get_status(d), ==, 0);
    142     d->features_negotiated = false;
    143 }
    144 
    145 void qvirtio_set_acknowledge(QVirtioDevice *d)
    146 {
    147     d->bus->set_status(d, d->bus->get_status(d) | VIRTIO_CONFIG_S_ACKNOWLEDGE);
    148     g_assert_cmphex(d->bus->get_status(d), ==, VIRTIO_CONFIG_S_ACKNOWLEDGE);
    149 }
    150 
    151 void qvirtio_set_driver(QVirtioDevice *d)
    152 {
    153     d->bus->set_status(d, d->bus->get_status(d) | VIRTIO_CONFIG_S_DRIVER);
    154     g_assert_cmphex(d->bus->get_status(d), ==,
    155                     VIRTIO_CONFIG_S_DRIVER | VIRTIO_CONFIG_S_ACKNOWLEDGE);
    156 }
    157 
    158 void qvirtio_set_driver_ok(QVirtioDevice *d)
    159 {
    160     d->bus->set_status(d, d->bus->get_status(d) | VIRTIO_CONFIG_S_DRIVER_OK);
    161     g_assert_cmphex(d->bus->get_status(d), ==, VIRTIO_CONFIG_S_DRIVER_OK |
    162                     VIRTIO_CONFIG_S_DRIVER | VIRTIO_CONFIG_S_ACKNOWLEDGE |
    163                     (d->features & (1ull << VIRTIO_F_VERSION_1) ?
    164                      VIRTIO_CONFIG_S_FEATURES_OK : 0));
    165 }
    166 
    167 void qvirtio_wait_queue_isr(QTestState *qts, QVirtioDevice *d,
    168                             QVirtQueue *vq, gint64 timeout_us)
    169 {
    170     gint64 start_time = g_get_monotonic_time();
    171 
    172     for (;;) {
    173         qtest_clock_step(qts, 100);
    174         if (d->bus->get_queue_isr_status(d, vq)) {
    175             return;
    176         }
    177         g_assert(g_get_monotonic_time() - start_time <= timeout_us);
    178     }
    179 }
    180 
    181 /* Wait for the status byte at given guest memory address to be set
    182  *
    183  * The virtqueue interrupt must not be raised, making this useful for testing
    184  * event_index functionality.
    185  */
    186 uint8_t qvirtio_wait_status_byte_no_isr(QTestState *qts, QVirtioDevice *d,
    187                                         QVirtQueue *vq,
    188                                         uint64_t addr,
    189                                         gint64 timeout_us)
    190 {
    191     gint64 start_time = g_get_monotonic_time();
    192     uint8_t val;
    193 
    194     while ((val = qtest_readb(qts, addr)) == 0xff) {
    195         qtest_clock_step(qts, 100);
    196         g_assert(!d->bus->get_queue_isr_status(d, vq));
    197         g_assert(g_get_monotonic_time() - start_time <= timeout_us);
    198     }
    199     return val;
    200 }
    201 
    202 /*
    203  * qvirtio_wait_used_elem:
    204  * @desc_idx: The next expected vq->desc[] index in the used ring
    205  * @len: A pointer that is filled with the length written into the buffer, may
    206  *       be NULL
    207  * @timeout_us: How many microseconds to wait before failing
    208  *
    209  * This function waits for the next completed request on the used ring.
    210  */
    211 void qvirtio_wait_used_elem(QTestState *qts, QVirtioDevice *d,
    212                             QVirtQueue *vq,
    213                             uint32_t desc_idx,
    214                             uint32_t *len,
    215                             gint64 timeout_us)
    216 {
    217     gint64 start_time = g_get_monotonic_time();
    218 
    219     for (;;) {
    220         uint32_t got_desc_idx;
    221 
    222         qtest_clock_step(qts, 100);
    223 
    224         if (d->bus->get_queue_isr_status(d, vq) &&
    225             qvirtqueue_get_buf(qts, vq, &got_desc_idx, len)) {
    226             g_assert_cmpint(got_desc_idx, ==, desc_idx);
    227             return;
    228         }
    229 
    230         g_assert(g_get_monotonic_time() - start_time <= timeout_us);
    231     }
    232 }
    233 
    234 void qvirtio_wait_config_isr(QVirtioDevice *d, gint64 timeout_us)
    235 {
    236     d->bus->wait_config_isr_status(d, timeout_us);
    237 }
    238 
    239 void qvring_init(QTestState *qts, const QGuestAllocator *alloc, QVirtQueue *vq,
    240                  uint64_t addr)
    241 {
    242     int i;
    243 
    244     vq->desc = addr;
    245     vq->avail = vq->desc + vq->size * sizeof(struct vring_desc);
    246     vq->used = (uint64_t)((vq->avail + sizeof(uint16_t) * (3 + vq->size)
    247         + vq->align - 1) & ~(vq->align - 1));
    248 
    249     for (i = 0; i < vq->size - 1; i++) {
    250         /* vq->desc[i].addr */
    251         qvirtio_writeq(vq->vdev, qts, vq->desc + (16 * i), 0);
    252         /* vq->desc[i].next */
    253         qvirtio_writew(vq->vdev, qts, vq->desc + (16 * i) + 14, i + 1);
    254     }
    255 
    256     /* vq->avail->flags */
    257     qvirtio_writew(vq->vdev, qts, vq->avail, 0);
    258     /* vq->avail->idx */
    259     qvirtio_writew(vq->vdev, qts, vq->avail + 2, 0);
    260     /* vq->avail->used_event */
    261     qvirtio_writew(vq->vdev, qts, vq->avail + 4 + (2 * vq->size), 0);
    262 
    263     /* vq->used->flags */
    264     qvirtio_writew(vq->vdev, qts, vq->used, 0);
    265     /* vq->used->idx */
    266     qvirtio_writew(vq->vdev, qts, vq->used + 2, 0);
    267     /* vq->used->avail_event */
    268     qvirtio_writew(vq->vdev, qts, vq->used + 2 +
    269                    sizeof(struct vring_used_elem) * vq->size, 0);
    270 }
    271 
    272 QVRingIndirectDesc *qvring_indirect_desc_setup(QTestState *qs, QVirtioDevice *d,
    273                                                QGuestAllocator *alloc,
    274                                                uint16_t elem)
    275 {
    276     int i;
    277     QVRingIndirectDesc *indirect = g_malloc(sizeof(*indirect));
    278 
    279     indirect->index = 0;
    280     indirect->elem = elem;
    281     indirect->desc = guest_alloc(alloc, sizeof(struct vring_desc) * elem);
    282 
    283     for (i = 0; i < elem - 1; ++i) {
    284         /* indirect->desc[i].addr */
    285         qvirtio_writeq(d, qs, indirect->desc + (16 * i), 0);
    286         /* indirect->desc[i].flags */
    287         qvirtio_writew(d, qs, indirect->desc + (16 * i) + 12,
    288                        VRING_DESC_F_NEXT);
    289         /* indirect->desc[i].next */
    290         qvirtio_writew(d, qs, indirect->desc + (16 * i) + 14, i + 1);
    291     }
    292 
    293     return indirect;
    294 }
    295 
    296 void qvring_indirect_desc_add(QVirtioDevice *d, QTestState *qts,
    297                               QVRingIndirectDesc *indirect,
    298                               uint64_t data, uint32_t len, bool write)
    299 {
    300     uint16_t flags;
    301 
    302     g_assert_cmpint(indirect->index, <, indirect->elem);
    303 
    304     flags = qvirtio_readw(d, qts, indirect->desc +
    305                                   (16 * indirect->index) + 12);
    306 
    307     if (write) {
    308         flags |= VRING_DESC_F_WRITE;
    309     }
    310 
    311     /* indirect->desc[indirect->index].addr */
    312     qvirtio_writeq(d, qts, indirect->desc + (16 * indirect->index), data);
    313     /* indirect->desc[indirect->index].len */
    314     qvirtio_writel(d, qts, indirect->desc + (16 * indirect->index) + 8, len);
    315     /* indirect->desc[indirect->index].flags */
    316     qvirtio_writew(d, qts, indirect->desc + (16 * indirect->index) + 12,
    317                    flags);
    318 
    319     indirect->index++;
    320 }
    321 
    322 uint32_t qvirtqueue_add(QTestState *qts, QVirtQueue *vq, uint64_t data,
    323                         uint32_t len, bool write, bool next)
    324 {
    325     uint16_t flags = 0;
    326     vq->num_free--;
    327 
    328     if (write) {
    329         flags |= VRING_DESC_F_WRITE;
    330     }
    331 
    332     if (next) {
    333         flags |= VRING_DESC_F_NEXT;
    334     }
    335 
    336     /* vq->desc[vq->free_head].addr */
    337     qvirtio_writeq(vq->vdev, qts, vq->desc + (16 * vq->free_head), data);
    338     /* vq->desc[vq->free_head].len */
    339     qvirtio_writel(vq->vdev, qts, vq->desc + (16 * vq->free_head) + 8, len);
    340     /* vq->desc[vq->free_head].flags */
    341     qvirtio_writew(vq->vdev, qts, vq->desc + (16 * vq->free_head) + 12, flags);
    342 
    343     return vq->free_head++; /* Return and increase, in this order */
    344 }
    345 
    346 uint32_t qvirtqueue_add_indirect(QTestState *qts, QVirtQueue *vq,
    347                                  QVRingIndirectDesc *indirect)
    348 {
    349     g_assert(vq->indirect);
    350     g_assert_cmpint(vq->size, >=, indirect->elem);
    351     g_assert_cmpint(indirect->index, ==, indirect->elem);
    352 
    353     vq->num_free--;
    354 
    355     /* vq->desc[vq->free_head].addr */
    356     qvirtio_writeq(vq->vdev, qts, vq->desc + (16 * vq->free_head),
    357                    indirect->desc);
    358     /* vq->desc[vq->free_head].len */
    359     qvirtio_writel(vq->vdev, qts, vq->desc + (16 * vq->free_head) + 8,
    360                    sizeof(struct vring_desc) * indirect->elem);
    361     /* vq->desc[vq->free_head].flags */
    362     qvirtio_writew(vq->vdev, qts, vq->desc + (16 * vq->free_head) + 12,
    363                    VRING_DESC_F_INDIRECT);
    364 
    365     return vq->free_head++; /* Return and increase, in this order */
    366 }
    367 
    368 void qvirtqueue_kick(QTestState *qts, QVirtioDevice *d, QVirtQueue *vq,
    369                      uint32_t free_head)
    370 {
    371     /* vq->avail->idx */
    372     uint16_t idx = qvirtio_readw(d, qts, vq->avail + 2);
    373     /* vq->used->flags */
    374     uint16_t flags;
    375     /* vq->used->avail_event */
    376     uint16_t avail_event;
    377 
    378     /* vq->avail->ring[idx % vq->size] */
    379     qvirtio_writew(d, qts, vq->avail + 4 + (2 * (idx % vq->size)), free_head);
    380     /* vq->avail->idx */
    381     qvirtio_writew(d, qts, vq->avail + 2, idx + 1);
    382 
    383     /* Must read after idx is updated */
    384     flags = qvirtio_readw(d, qts, vq->avail);
    385     avail_event = qvirtio_readw(d, qts, vq->used + 4 +
    386                                 sizeof(struct vring_used_elem) * vq->size);
    387 
    388     /* < 1 because we add elements to avail queue one by one */
    389     if ((flags & VRING_USED_F_NO_NOTIFY) == 0 &&
    390                             (!vq->event || (uint16_t)(idx-avail_event) < 1)) {
    391         d->bus->virtqueue_kick(d, vq);
    392     }
    393 }
    394 
    395 /*
    396  * qvirtqueue_get_buf:
    397  * @desc_idx: A pointer that is filled with the vq->desc[] index, may be NULL
    398  * @len: A pointer that is filled with the length written into the buffer, may
    399  *       be NULL
    400  *
    401  * This function gets the next used element if there is one ready.
    402  *
    403  * Returns: true if an element was ready, false otherwise
    404  */
    405 bool qvirtqueue_get_buf(QTestState *qts, QVirtQueue *vq, uint32_t *desc_idx,
    406                         uint32_t *len)
    407 {
    408     uint16_t idx;
    409     uint64_t elem_addr, addr;
    410 
    411     idx = qvirtio_readw(vq->vdev, qts,
    412                         vq->used + offsetof(struct vring_used, idx));
    413     if (idx == vq->last_used_idx) {
    414         return false;
    415     }
    416 
    417     elem_addr = vq->used +
    418         offsetof(struct vring_used, ring) +
    419         (vq->last_used_idx % vq->size) *
    420         sizeof(struct vring_used_elem);
    421 
    422     if (desc_idx) {
    423         addr = elem_addr + offsetof(struct vring_used_elem, id);
    424         *desc_idx = qvirtio_readl(vq->vdev, qts, addr);
    425     }
    426 
    427     if (len) {
    428         addr = elem_addr + offsetof(struct vring_used_elem, len);
    429         *len = qvirtio_readw(vq->vdev, qts, addr);
    430     }
    431 
    432     vq->last_used_idx++;
    433     return true;
    434 }
    435 
    436 void qvirtqueue_set_used_event(QTestState *qts, QVirtQueue *vq, uint16_t idx)
    437 {
    438     g_assert(vq->event);
    439 
    440     /* vq->avail->used_event */
    441     qvirtio_writew(vq->vdev, qts, vq->avail + 4 + (2 * vq->size), idx);
    442 }
    443 
    444 void qvirtio_start_device(QVirtioDevice *vdev)
    445 {
    446     qvirtio_reset(vdev);
    447     qvirtio_set_acknowledge(vdev);
    448     qvirtio_set_driver(vdev);
    449 }
    450 
    451 bool qvirtio_is_big_endian(QVirtioDevice *d)
    452 {
    453     return d->big_endian;
    454 }