qemu/tests/libqos/virtio.c
<<
>>
Prefs
   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 <glib.h>
  11#include "libqtest.h"
  12#include "libqos/virtio.h"
  13
  14uint8_t qvirtio_config_readb(const QVirtioBus *bus, QVirtioDevice *d,
  15                                                                uint64_t addr)
  16{
  17    return bus->config_readb(d, addr);
  18}
  19
  20uint16_t qvirtio_config_readw(const QVirtioBus *bus, QVirtioDevice *d,
  21                                                                uint64_t addr)
  22{
  23    return bus->config_readw(d, addr);
  24}
  25
  26uint32_t qvirtio_config_readl(const QVirtioBus *bus, QVirtioDevice *d,
  27                                                                uint64_t addr)
  28{
  29    return bus->config_readl(d, addr);
  30}
  31
  32uint64_t qvirtio_config_readq(const QVirtioBus *bus, QVirtioDevice *d,
  33                                                                uint64_t addr)
  34{
  35    return bus->config_readq(d, addr);
  36}
  37
  38uint32_t qvirtio_get_features(const QVirtioBus *bus, QVirtioDevice *d)
  39{
  40    return bus->get_features(d);
  41}
  42
  43void qvirtio_set_features(const QVirtioBus *bus, QVirtioDevice *d,
  44                                                            uint32_t features)
  45{
  46    bus->set_features(d, features);
  47}
  48
  49QVirtQueue *qvirtqueue_setup(const QVirtioBus *bus, QVirtioDevice *d,
  50                                        QGuestAllocator *alloc, uint16_t index)
  51{
  52    return bus->virtqueue_setup(d, alloc, index);
  53}
  54
  55void qvirtio_reset(const QVirtioBus *bus, QVirtioDevice *d)
  56{
  57    bus->set_status(d, QVIRTIO_RESET);
  58    g_assert_cmphex(bus->get_status(d), ==, QVIRTIO_RESET);
  59}
  60
  61void qvirtio_set_acknowledge(const QVirtioBus *bus, QVirtioDevice *d)
  62{
  63    bus->set_status(d, bus->get_status(d) | QVIRTIO_ACKNOWLEDGE);
  64    g_assert_cmphex(bus->get_status(d), ==, QVIRTIO_ACKNOWLEDGE);
  65}
  66
  67void qvirtio_set_driver(const QVirtioBus *bus, QVirtioDevice *d)
  68{
  69    bus->set_status(d, bus->get_status(d) | QVIRTIO_DRIVER);
  70    g_assert_cmphex(bus->get_status(d), ==,
  71                                    QVIRTIO_DRIVER | QVIRTIO_ACKNOWLEDGE);
  72}
  73
  74void qvirtio_set_driver_ok(const QVirtioBus *bus, QVirtioDevice *d)
  75{
  76    bus->set_status(d, bus->get_status(d) | QVIRTIO_DRIVER_OK);
  77    g_assert_cmphex(bus->get_status(d), ==,
  78                QVIRTIO_DRIVER_OK | QVIRTIO_DRIVER | QVIRTIO_ACKNOWLEDGE);
  79}
  80
  81void qvirtio_wait_queue_isr(const QVirtioBus *bus, QVirtioDevice *d,
  82                            QVirtQueue *vq, gint64 timeout_us)
  83{
  84    gint64 start_time = g_get_monotonic_time();
  85
  86    for (;;) {
  87        clock_step(100);
  88        if (bus->get_queue_isr_status(d, vq)) {
  89            return;
  90        }
  91        g_assert(g_get_monotonic_time() - start_time <= timeout_us);
  92    }
  93}
  94
  95/* Wait for the status byte at given guest memory address to be set
  96 *
  97 * The virtqueue interrupt must not be raised, making this useful for testing
  98 * event_index functionality.
  99 */
 100uint8_t qvirtio_wait_status_byte_no_isr(const QVirtioBus *bus,
 101                                        QVirtioDevice *d,
 102                                        QVirtQueue *vq,
 103                                        uint64_t addr,
 104                                        gint64 timeout_us)
 105{
 106    gint64 start_time = g_get_monotonic_time();
 107    uint8_t val;
 108
 109    while ((val = readb(addr)) == 0xff) {
 110        clock_step(100);
 111        g_assert(!bus->get_queue_isr_status(d, vq));
 112        g_assert(g_get_monotonic_time() - start_time <= timeout_us);
 113    }
 114    return val;
 115}
 116
 117void qvirtio_wait_config_isr(const QVirtioBus *bus, QVirtioDevice *d,
 118                             gint64 timeout_us)
 119{
 120    gint64 start_time = g_get_monotonic_time();
 121
 122    for (;;) {
 123        clock_step(100);
 124        if (bus->get_config_isr_status(d)) {
 125            return;
 126        }
 127        g_assert(g_get_monotonic_time() - start_time <= timeout_us);
 128    }
 129}
 130
 131void qvring_init(const QGuestAllocator *alloc, QVirtQueue *vq, uint64_t addr)
 132{
 133    int i;
 134
 135    vq->desc = addr;
 136    vq->avail = vq->desc + vq->size*sizeof(QVRingDesc);
 137    vq->used = (uint64_t)((vq->avail + sizeof(uint16_t) * (3 + vq->size)
 138        + vq->align - 1) & ~(vq->align - 1));
 139
 140    for (i = 0; i < vq->size - 1; i++) {
 141        /* vq->desc[i].addr */
 142        writew(vq->desc + (16 * i), 0);
 143        /* vq->desc[i].next */
 144        writew(vq->desc + (16 * i) + 14, i + 1);
 145    }
 146
 147    /* vq->avail->flags */
 148    writew(vq->avail, 0);
 149    /* vq->avail->idx */
 150    writew(vq->avail + 2, 0);
 151    /* vq->avail->used_event */
 152    writew(vq->avail + 4 + (2 * vq->size), 0);
 153
 154    /* vq->used->flags */
 155    writew(vq->used, 0);
 156    /* vq->used->avail_event */
 157    writew(vq->used+2+(sizeof(struct QVRingUsedElem)*vq->size), 0);
 158}
 159
 160QVRingIndirectDesc *qvring_indirect_desc_setup(QVirtioDevice *d,
 161                                        QGuestAllocator *alloc, uint16_t elem)
 162{
 163    int i;
 164    QVRingIndirectDesc *indirect = g_malloc(sizeof(*indirect));
 165
 166    indirect->index = 0;
 167    indirect->elem = elem;
 168    indirect->desc = guest_alloc(alloc, sizeof(QVRingDesc)*elem);
 169
 170    for (i = 0; i < elem - 1; ++i) {
 171        /* indirect->desc[i].addr */
 172        writeq(indirect->desc + (16 * i), 0);
 173        /* indirect->desc[i].flags */
 174        writew(indirect->desc + (16 * i) + 12, QVRING_DESC_F_NEXT);
 175        /* indirect->desc[i].next */
 176        writew(indirect->desc + (16 * i) + 14, i + 1);
 177    }
 178
 179    return indirect;
 180}
 181
 182void qvring_indirect_desc_add(QVRingIndirectDesc *indirect, uint64_t data,
 183                                                    uint32_t len, bool write)
 184{
 185    uint16_t flags;
 186
 187    g_assert_cmpint(indirect->index, <, indirect->elem);
 188
 189    flags = readw(indirect->desc + (16 * indirect->index) + 12);
 190
 191    if (write) {
 192        flags |= QVRING_DESC_F_WRITE;
 193    }
 194
 195    /* indirect->desc[indirect->index].addr */
 196    writeq(indirect->desc + (16 * indirect->index), data);
 197    /* indirect->desc[indirect->index].len */
 198    writel(indirect->desc + (16 * indirect->index) + 8, len);
 199    /* indirect->desc[indirect->index].flags */
 200    writew(indirect->desc + (16 * indirect->index) + 12, flags);
 201
 202    indirect->index++;
 203}
 204
 205uint32_t qvirtqueue_add(QVirtQueue *vq, uint64_t data, uint32_t len, bool write,
 206                                                                    bool next)
 207{
 208    uint16_t flags = 0;
 209    vq->num_free--;
 210
 211    if (write) {
 212        flags |= QVRING_DESC_F_WRITE;
 213    }
 214
 215    if (next) {
 216        flags |= QVRING_DESC_F_NEXT;
 217    }
 218
 219    /* vq->desc[vq->free_head].addr */
 220    writeq(vq->desc + (16 * vq->free_head), data);
 221    /* vq->desc[vq->free_head].len */
 222    writel(vq->desc + (16 * vq->free_head) + 8, len);
 223    /* vq->desc[vq->free_head].flags */
 224    writew(vq->desc + (16 * vq->free_head) + 12, flags);
 225
 226    return vq->free_head++; /* Return and increase, in this order */
 227}
 228
 229uint32_t qvirtqueue_add_indirect(QVirtQueue *vq, QVRingIndirectDesc *indirect)
 230{
 231    g_assert(vq->indirect);
 232    g_assert_cmpint(vq->size, >=, indirect->elem);
 233    g_assert_cmpint(indirect->index, ==, indirect->elem);
 234
 235    vq->num_free--;
 236
 237    /* vq->desc[vq->free_head].addr */
 238    writeq(vq->desc + (16 * vq->free_head), indirect->desc);
 239    /* vq->desc[vq->free_head].len */
 240    writel(vq->desc + (16 * vq->free_head) + 8,
 241                                        sizeof(QVRingDesc) * indirect->elem);
 242    /* vq->desc[vq->free_head].flags */
 243    writew(vq->desc + (16 * vq->free_head) + 12, QVRING_DESC_F_INDIRECT);
 244
 245    return vq->free_head++; /* Return and increase, in this order */
 246}
 247
 248void qvirtqueue_kick(const QVirtioBus *bus, QVirtioDevice *d, QVirtQueue *vq,
 249                                                            uint32_t free_head)
 250{
 251    /* vq->avail->idx */
 252    uint16_t idx = readl(vq->avail + 2);
 253    /* vq->used->flags */
 254    uint16_t flags;
 255    /* vq->used->avail_event */
 256    uint16_t avail_event;
 257
 258    /* vq->avail->ring[idx % vq->size] */
 259    writel(vq->avail + 4 + (2 * (idx % vq->size)), free_head);
 260    /* vq->avail->idx */
 261    writel(vq->avail + 2, idx + 1);
 262
 263    /* Must read after idx is updated */
 264    flags = readw(vq->avail);
 265    avail_event = readw(vq->used + 4 +
 266                                (sizeof(struct QVRingUsedElem) * vq->size));
 267
 268    /* < 1 because we add elements to avail queue one by one */
 269    if ((flags & QVRING_USED_F_NO_NOTIFY) == 0 &&
 270                            (!vq->event || (uint16_t)(idx-avail_event) < 1)) {
 271        bus->virtqueue_kick(d, vq);
 272    }
 273}
 274
 275void qvirtqueue_set_used_event(QVirtQueue *vq, uint16_t idx)
 276{
 277    g_assert(vq->event);
 278
 279    /* vq->avail->used_event */
 280    writew(vq->avail + 4 + (2 * vq->size), idx);
 281}
 282