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 "qemu/osdep.h"
  11#include "libqtest.h"
  12#include "libqos/virtio.h"
  13#include "standard-headers/linux/virtio_config.h"
  14#include "standard-headers/linux/virtio_ring.h"
  15
  16uint8_t qvirtio_config_readb(QVirtioDevice *d, uint64_t addr)
  17{
  18    return d->bus->config_readb(d, addr);
  19}
  20
  21uint16_t qvirtio_config_readw(QVirtioDevice *d, uint64_t addr)
  22{
  23    return d->bus->config_readw(d, addr);
  24}
  25
  26uint32_t qvirtio_config_readl(QVirtioDevice *d, uint64_t addr)
  27{
  28    return d->bus->config_readl(d, addr);
  29}
  30
  31uint64_t qvirtio_config_readq(QVirtioDevice *d, uint64_t addr)
  32{
  33    return d->bus->config_readq(d, addr);
  34}
  35
  36uint32_t qvirtio_get_features(QVirtioDevice *d)
  37{
  38    return d->bus->get_features(d);
  39}
  40
  41void qvirtio_set_features(QVirtioDevice *d, uint32_t features)
  42{
  43    d->bus->set_features(d, features);
  44}
  45
  46QVirtQueue *qvirtqueue_setup(QVirtioDevice *d,
  47                             QGuestAllocator *alloc, uint16_t index)
  48{
  49    return d->bus->virtqueue_setup(d, alloc, index);
  50}
  51
  52void qvirtqueue_cleanup(const QVirtioBus *bus, QVirtQueue *vq,
  53                        QGuestAllocator *alloc)
  54{
  55    return bus->virtqueue_cleanup(vq, alloc);
  56}
  57
  58void qvirtio_reset(QVirtioDevice *d)
  59{
  60    d->bus->set_status(d, 0);
  61    g_assert_cmphex(d->bus->get_status(d), ==, 0);
  62}
  63
  64void qvirtio_set_acknowledge(QVirtioDevice *d)
  65{
  66    d->bus->set_status(d, d->bus->get_status(d) | VIRTIO_CONFIG_S_ACKNOWLEDGE);
  67    g_assert_cmphex(d->bus->get_status(d), ==, VIRTIO_CONFIG_S_ACKNOWLEDGE);
  68}
  69
  70void qvirtio_set_driver(QVirtioDevice *d)
  71{
  72    d->bus->set_status(d, d->bus->get_status(d) | VIRTIO_CONFIG_S_DRIVER);
  73    g_assert_cmphex(d->bus->get_status(d), ==,
  74                    VIRTIO_CONFIG_S_DRIVER | VIRTIO_CONFIG_S_ACKNOWLEDGE);
  75}
  76
  77void qvirtio_set_driver_ok(QVirtioDevice *d)
  78{
  79    d->bus->set_status(d, d->bus->get_status(d) | VIRTIO_CONFIG_S_DRIVER_OK);
  80    g_assert_cmphex(d->bus->get_status(d), ==, VIRTIO_CONFIG_S_DRIVER_OK |
  81                    VIRTIO_CONFIG_S_DRIVER | VIRTIO_CONFIG_S_ACKNOWLEDGE);
  82}
  83
  84void qvirtio_wait_queue_isr(QVirtioDevice *d,
  85                            QVirtQueue *vq, gint64 timeout_us)
  86{
  87    gint64 start_time = g_get_monotonic_time();
  88
  89    for (;;) {
  90        clock_step(100);
  91        if (d->bus->get_queue_isr_status(d, vq)) {
  92            return;
  93        }
  94        g_assert(g_get_monotonic_time() - start_time <= timeout_us);
  95    }
  96}
  97
  98/* Wait for the status byte at given guest memory address to be set
  99 *
 100 * The virtqueue interrupt must not be raised, making this useful for testing
 101 * event_index functionality.
 102 */
 103uint8_t qvirtio_wait_status_byte_no_isr(QVirtioDevice *d,
 104                                        QVirtQueue *vq,
 105                                        uint64_t addr,
 106                                        gint64 timeout_us)
 107{
 108    gint64 start_time = g_get_monotonic_time();
 109    uint8_t val;
 110
 111    while ((val = readb(addr)) == 0xff) {
 112        clock_step(100);
 113        g_assert(!d->bus->get_queue_isr_status(d, vq));
 114        g_assert(g_get_monotonic_time() - start_time <= timeout_us);
 115    }
 116    return val;
 117}
 118
 119/*
 120 * qvirtio_wait_used_elem:
 121 * @desc_idx: The next expected vq->desc[] index in the used ring
 122 * @len: A pointer that is filled with the length written into the buffer, may
 123 *       be NULL
 124 * @timeout_us: How many microseconds to wait before failing
 125 *
 126 * This function waits for the next completed request on the used ring.
 127 */
 128void qvirtio_wait_used_elem(QVirtioDevice *d,
 129                            QVirtQueue *vq,
 130                            uint32_t desc_idx,
 131                            uint32_t *len,
 132                            gint64 timeout_us)
 133{
 134    gint64 start_time = g_get_monotonic_time();
 135
 136    for (;;) {
 137        uint32_t got_desc_idx;
 138
 139        clock_step(100);
 140
 141        if (d->bus->get_queue_isr_status(d, vq) &&
 142            qvirtqueue_get_buf(vq, &got_desc_idx, len)) {
 143            g_assert_cmpint(got_desc_idx, ==, desc_idx);
 144            return;
 145        }
 146
 147        g_assert(g_get_monotonic_time() - start_time <= timeout_us);
 148    }
 149}
 150
 151void qvirtio_wait_config_isr(QVirtioDevice *d, gint64 timeout_us)
 152{
 153    gint64 start_time = g_get_monotonic_time();
 154
 155    for (;;) {
 156        clock_step(100);
 157        if (d->bus->get_config_isr_status(d)) {
 158            return;
 159        }
 160        g_assert(g_get_monotonic_time() - start_time <= timeout_us);
 161    }
 162}
 163
 164void qvring_init(const QGuestAllocator *alloc, QVirtQueue *vq, uint64_t addr)
 165{
 166    int i;
 167
 168    vq->desc = addr;
 169    vq->avail = vq->desc + vq->size * sizeof(struct vring_desc);
 170    vq->used = (uint64_t)((vq->avail + sizeof(uint16_t) * (3 + vq->size)
 171        + vq->align - 1) & ~(vq->align - 1));
 172
 173    for (i = 0; i < vq->size - 1; i++) {
 174        /* vq->desc[i].addr */
 175        writeq(vq->desc + (16 * i), 0);
 176        /* vq->desc[i].next */
 177        writew(vq->desc + (16 * i) + 14, i + 1);
 178    }
 179
 180    /* vq->avail->flags */
 181    writew(vq->avail, 0);
 182    /* vq->avail->idx */
 183    writew(vq->avail + 2, 0);
 184    /* vq->avail->used_event */
 185    writew(vq->avail + 4 + (2 * vq->size), 0);
 186
 187    /* vq->used->flags */
 188    writew(vq->used, 0);
 189    /* vq->used->avail_event */
 190    writew(vq->used + 2 + sizeof(struct vring_used_elem) * vq->size, 0);
 191}
 192
 193QVRingIndirectDesc *qvring_indirect_desc_setup(QVirtioDevice *d,
 194                                        QGuestAllocator *alloc, uint16_t elem)
 195{
 196    int i;
 197    QVRingIndirectDesc *indirect = g_malloc(sizeof(*indirect));
 198
 199    indirect->index = 0;
 200    indirect->elem = elem;
 201    indirect->desc = guest_alloc(alloc, sizeof(struct vring_desc) * elem);
 202
 203    for (i = 0; i < elem - 1; ++i) {
 204        /* indirect->desc[i].addr */
 205        writeq(indirect->desc + (16 * i), 0);
 206        /* indirect->desc[i].flags */
 207        writew(indirect->desc + (16 * i) + 12, VRING_DESC_F_NEXT);
 208        /* indirect->desc[i].next */
 209        writew(indirect->desc + (16 * i) + 14, i + 1);
 210    }
 211
 212    return indirect;
 213}
 214
 215void qvring_indirect_desc_add(QVRingIndirectDesc *indirect, uint64_t data,
 216                                                    uint32_t len, bool write)
 217{
 218    uint16_t flags;
 219
 220    g_assert_cmpint(indirect->index, <, indirect->elem);
 221
 222    flags = readw(indirect->desc + (16 * indirect->index) + 12);
 223
 224    if (write) {
 225        flags |= VRING_DESC_F_WRITE;
 226    }
 227
 228    /* indirect->desc[indirect->index].addr */
 229    writeq(indirect->desc + (16 * indirect->index), data);
 230    /* indirect->desc[indirect->index].len */
 231    writel(indirect->desc + (16 * indirect->index) + 8, len);
 232    /* indirect->desc[indirect->index].flags */
 233    writew(indirect->desc + (16 * indirect->index) + 12, flags);
 234
 235    indirect->index++;
 236}
 237
 238uint32_t qvirtqueue_add(QVirtQueue *vq, uint64_t data, uint32_t len, bool write,
 239                                                                    bool next)
 240{
 241    uint16_t flags = 0;
 242    vq->num_free--;
 243
 244    if (write) {
 245        flags |= VRING_DESC_F_WRITE;
 246    }
 247
 248    if (next) {
 249        flags |= VRING_DESC_F_NEXT;
 250    }
 251
 252    /* vq->desc[vq->free_head].addr */
 253    writeq(vq->desc + (16 * vq->free_head), data);
 254    /* vq->desc[vq->free_head].len */
 255    writel(vq->desc + (16 * vq->free_head) + 8, len);
 256    /* vq->desc[vq->free_head].flags */
 257    writew(vq->desc + (16 * vq->free_head) + 12, flags);
 258
 259    return vq->free_head++; /* Return and increase, in this order */
 260}
 261
 262uint32_t qvirtqueue_add_indirect(QVirtQueue *vq, QVRingIndirectDesc *indirect)
 263{
 264    g_assert(vq->indirect);
 265    g_assert_cmpint(vq->size, >=, indirect->elem);
 266    g_assert_cmpint(indirect->index, ==, indirect->elem);
 267
 268    vq->num_free--;
 269
 270    /* vq->desc[vq->free_head].addr */
 271    writeq(vq->desc + (16 * vq->free_head), indirect->desc);
 272    /* vq->desc[vq->free_head].len */
 273    writel(vq->desc + (16 * vq->free_head) + 8,
 274           sizeof(struct vring_desc) * indirect->elem);
 275    /* vq->desc[vq->free_head].flags */
 276    writew(vq->desc + (16 * vq->free_head) + 12, VRING_DESC_F_INDIRECT);
 277
 278    return vq->free_head++; /* Return and increase, in this order */
 279}
 280
 281void qvirtqueue_kick(QVirtioDevice *d, QVirtQueue *vq, uint32_t free_head)
 282{
 283    /* vq->avail->idx */
 284    uint16_t idx = readw(vq->avail + 2);
 285    /* vq->used->flags */
 286    uint16_t flags;
 287    /* vq->used->avail_event */
 288    uint16_t avail_event;
 289
 290    /* vq->avail->ring[idx % vq->size] */
 291    writew(vq->avail + 4 + (2 * (idx % vq->size)), free_head);
 292    /* vq->avail->idx */
 293    writew(vq->avail + 2, idx + 1);
 294
 295    /* Must read after idx is updated */
 296    flags = readw(vq->avail);
 297    avail_event = readw(vq->used + 4 +
 298                                sizeof(struct vring_used_elem) * vq->size);
 299
 300    /* < 1 because we add elements to avail queue one by one */
 301    if ((flags & VRING_USED_F_NO_NOTIFY) == 0 &&
 302                            (!vq->event || (uint16_t)(idx-avail_event) < 1)) {
 303        d->bus->virtqueue_kick(d, vq);
 304    }
 305}
 306
 307/*
 308 * qvirtqueue_get_buf:
 309 * @desc_idx: A pointer that is filled with the vq->desc[] index, may be NULL
 310 * @len: A pointer that is filled with the length written into the buffer, may
 311 *       be NULL
 312 *
 313 * This function gets the next used element if there is one ready.
 314 *
 315 * Returns: true if an element was ready, false otherwise
 316 */
 317bool qvirtqueue_get_buf(QVirtQueue *vq, uint32_t *desc_idx, uint32_t *len)
 318{
 319    uint16_t idx;
 320    uint64_t elem_addr;
 321
 322    idx = readw(vq->used + offsetof(struct vring_used, idx));
 323    if (idx == vq->last_used_idx) {
 324        return false;
 325    }
 326
 327    elem_addr = vq->used +
 328        offsetof(struct vring_used, ring) +
 329        (vq->last_used_idx % vq->size) *
 330        sizeof(struct vring_used_elem);
 331
 332    if (desc_idx) {
 333        *desc_idx = readl(elem_addr + offsetof(struct vring_used_elem, id));
 334    }
 335
 336    if (len) {
 337        *len = readw(elem_addr + offsetof(struct vring_used_elem, len));
 338    }
 339
 340    vq->last_used_idx++;
 341    return true;
 342}
 343
 344void qvirtqueue_set_used_event(QVirtQueue *vq, uint16_t idx)
 345{
 346    g_assert(vq->event);
 347
 348    /* vq->avail->used_event */
 349    writew(vq->avail + 4 + (2 * vq->size), idx);
 350}
 351
 352/*
 353 * qvirtio_get_dev_type:
 354 * Returns: the preferred virtio bus/device type for the current architecture.
 355 */
 356const char *qvirtio_get_dev_type(void)
 357{
 358    const char *arch = qtest_get_arch();
 359
 360    if (g_str_equal(arch, "arm") || g_str_equal(arch, "aarch64")) {
 361        return "device";  /* for virtio-mmio */
 362    } else if (g_str_equal(arch, "s390x")) {
 363        return "ccw";
 364    } else {
 365        return "pci";
 366    }
 367}
 368