qemu/tests/qtest/libqos/virtio-mmio.c
<<
>>
Prefs
   1/*
   2 * libqos virtio MMIO 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 "qemu/module.h"
  13#include "virtio.h"
  14#include "virtio-mmio.h"
  15#include "libqos-malloc.h"
  16#include "qgraph.h"
  17#include "standard-headers/linux/virtio_ring.h"
  18
  19static uint8_t qvirtio_mmio_config_readb(QVirtioDevice *d, uint64_t off)
  20{
  21    QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev);
  22    return qtest_readb(dev->qts, dev->addr + QVIRTIO_MMIO_DEVICE_SPECIFIC + off);
  23}
  24
  25static uint16_t qvirtio_mmio_config_readw(QVirtioDevice *d, uint64_t off)
  26{
  27    QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev);
  28    return qtest_readw(dev->qts, dev->addr + QVIRTIO_MMIO_DEVICE_SPECIFIC + off);
  29}
  30
  31static uint32_t qvirtio_mmio_config_readl(QVirtioDevice *d, uint64_t off)
  32{
  33    QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev);
  34    return qtest_readl(dev->qts, dev->addr + QVIRTIO_MMIO_DEVICE_SPECIFIC + off);
  35}
  36
  37static uint64_t qvirtio_mmio_config_readq(QVirtioDevice *d, uint64_t off)
  38{
  39    QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev);
  40    return qtest_readq(dev->qts, dev->addr + QVIRTIO_MMIO_DEVICE_SPECIFIC + off);
  41}
  42
  43static uint64_t qvirtio_mmio_get_features(QVirtioDevice *d)
  44{
  45    QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev);
  46    uint64_t lo;
  47    uint64_t hi = 0;
  48
  49    qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_HOST_FEATURES_SEL, 0);
  50    lo = qtest_readl(dev->qts, dev->addr + QVIRTIO_MMIO_HOST_FEATURES);
  51
  52    if (dev->version >= 2) {
  53        qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_HOST_FEATURES_SEL, 1);
  54        hi = qtest_readl(dev->qts, dev->addr + QVIRTIO_MMIO_HOST_FEATURES);
  55    }
  56
  57    return (hi << 32) | lo;
  58}
  59
  60static void qvirtio_mmio_set_features(QVirtioDevice *d, uint64_t features)
  61{
  62    QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev);
  63    dev->features = features;
  64    qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_GUEST_FEATURES_SEL, 0);
  65    qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_GUEST_FEATURES, features);
  66
  67    if (dev->version >= 2) {
  68        qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_GUEST_FEATURES_SEL, 1);
  69        qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_GUEST_FEATURES,
  70                     features >> 32);
  71    }
  72}
  73
  74static uint64_t qvirtio_mmio_get_guest_features(QVirtioDevice *d)
  75{
  76    QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev);
  77    return dev->features;
  78}
  79
  80static uint8_t qvirtio_mmio_get_status(QVirtioDevice *d)
  81{
  82    QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev);
  83    return (uint8_t)qtest_readl(dev->qts, dev->addr + QVIRTIO_MMIO_DEVICE_STATUS);
  84}
  85
  86static void qvirtio_mmio_set_status(QVirtioDevice *d, uint8_t status)
  87{
  88    QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev);
  89    qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_DEVICE_STATUS, (uint32_t)status);
  90}
  91
  92static bool qvirtio_mmio_get_queue_isr_status(QVirtioDevice *d, QVirtQueue *vq)
  93{
  94    QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev);
  95    uint32_t isr;
  96
  97    isr = qtest_readl(dev->qts, dev->addr + QVIRTIO_MMIO_INTERRUPT_STATUS) & 1;
  98    if (isr != 0) {
  99        qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_INTERRUPT_ACK, 1);
 100        return true;
 101    }
 102
 103    return false;
 104}
 105
 106static bool qvirtio_mmio_get_config_isr_status(QVirtioDevice *d)
 107{
 108    QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev);
 109    uint32_t isr;
 110
 111    isr = qtest_readl(dev->qts, dev->addr + QVIRTIO_MMIO_INTERRUPT_STATUS) & 2;
 112    if (isr != 0) {
 113        qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_INTERRUPT_ACK, 2);
 114        return true;
 115    }
 116
 117    return false;
 118}
 119
 120static void qvirtio_mmio_wait_config_isr_status(QVirtioDevice *d,
 121                                                gint64 timeout_us)
 122{
 123    QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev);
 124    gint64 start_time = g_get_monotonic_time();
 125
 126    do {
 127        g_assert(g_get_monotonic_time() - start_time <= timeout_us);
 128        qtest_clock_step(dev->qts, 100);
 129    } while (!qvirtio_mmio_get_config_isr_status(d));
 130}
 131
 132static void qvirtio_mmio_queue_select(QVirtioDevice *d, uint16_t index)
 133{
 134    QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev);
 135    qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_QUEUE_SEL, (uint32_t)index);
 136
 137    g_assert_cmphex(qtest_readl(dev->qts, dev->addr + QVIRTIO_MMIO_QUEUE_PFN), ==, 0);
 138}
 139
 140static uint16_t qvirtio_mmio_get_queue_size(QVirtioDevice *d)
 141{
 142    QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev);
 143    return (uint16_t)qtest_readl(dev->qts, dev->addr + QVIRTIO_MMIO_QUEUE_NUM_MAX);
 144}
 145
 146static void qvirtio_mmio_set_queue_address(QVirtioDevice *d, QVirtQueue *vq)
 147{
 148    QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev);
 149    uint64_t pfn = vq->desc / dev->page_size;
 150
 151    qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_QUEUE_PFN, pfn);
 152}
 153
 154static QVirtQueue *qvirtio_mmio_virtqueue_setup(QVirtioDevice *d,
 155                                        QGuestAllocator *alloc, uint16_t index)
 156{
 157    QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev);
 158    QVirtQueue *vq;
 159    uint64_t addr;
 160
 161    vq = g_malloc0(sizeof(*vq));
 162    vq->vdev = d;
 163    qvirtio_mmio_queue_select(d, index);
 164    qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_QUEUE_ALIGN, dev->page_size);
 165
 166    vq->index = index;
 167    vq->size = qvirtio_mmio_get_queue_size(d);
 168    vq->free_head = 0;
 169    vq->num_free = vq->size;
 170    vq->align = dev->page_size;
 171    vq->indirect = dev->features & (1ull << VIRTIO_RING_F_INDIRECT_DESC);
 172    vq->event = dev->features & (1ull << VIRTIO_RING_F_EVENT_IDX);
 173
 174    qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_QUEUE_NUM, vq->size);
 175
 176    /* Check different than 0 */
 177    g_assert_cmpint(vq->size, !=, 0);
 178
 179    /* Check power of 2 */
 180    g_assert_cmpint(vq->size & (vq->size - 1), ==, 0);
 181
 182    addr = guest_alloc(alloc, qvring_size(vq->size, dev->page_size));
 183    qvring_init(dev->qts, alloc, vq, addr);
 184    qvirtio_mmio_set_queue_address(d, vq);
 185
 186    return vq;
 187}
 188
 189static void qvirtio_mmio_virtqueue_cleanup(QVirtQueue *vq,
 190                                           QGuestAllocator *alloc)
 191{
 192    guest_free(alloc, vq->desc);
 193    g_free(vq);
 194}
 195
 196static void qvirtio_mmio_virtqueue_kick(QVirtioDevice *d, QVirtQueue *vq)
 197{
 198    QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev);
 199    qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_QUEUE_NOTIFY, vq->index);
 200}
 201
 202const QVirtioBus qvirtio_mmio = {
 203    .config_readb = qvirtio_mmio_config_readb,
 204    .config_readw = qvirtio_mmio_config_readw,
 205    .config_readl = qvirtio_mmio_config_readl,
 206    .config_readq = qvirtio_mmio_config_readq,
 207    .get_features = qvirtio_mmio_get_features,
 208    .set_features = qvirtio_mmio_set_features,
 209    .get_guest_features = qvirtio_mmio_get_guest_features,
 210    .get_status = qvirtio_mmio_get_status,
 211    .set_status = qvirtio_mmio_set_status,
 212    .get_queue_isr_status = qvirtio_mmio_get_queue_isr_status,
 213    .wait_config_isr_status = qvirtio_mmio_wait_config_isr_status,
 214    .queue_select = qvirtio_mmio_queue_select,
 215    .get_queue_size = qvirtio_mmio_get_queue_size,
 216    .set_queue_address = qvirtio_mmio_set_queue_address,
 217    .virtqueue_setup = qvirtio_mmio_virtqueue_setup,
 218    .virtqueue_cleanup = qvirtio_mmio_virtqueue_cleanup,
 219    .virtqueue_kick = qvirtio_mmio_virtqueue_kick,
 220};
 221
 222static void *qvirtio_mmio_get_driver(void *obj, const char *interface)
 223{
 224    QVirtioMMIODevice *virtio_mmio = obj;
 225    if (!g_strcmp0(interface, "virtio-bus")) {
 226        return &virtio_mmio->vdev;
 227    }
 228    fprintf(stderr, "%s not present in virtio-mmio\n", interface);
 229    g_assert_not_reached();
 230}
 231
 232static void qvirtio_mmio_start_hw(QOSGraphObject *obj)
 233{
 234    QVirtioMMIODevice *dev = (QVirtioMMIODevice *) obj;
 235    qvirtio_start_device(&dev->vdev);
 236}
 237
 238void qvirtio_mmio_init_device(QVirtioMMIODevice *dev, QTestState *qts,
 239                              uint64_t addr, uint32_t page_size)
 240{
 241    uint32_t magic;
 242    magic = qtest_readl(qts, addr + QVIRTIO_MMIO_MAGIC_VALUE);
 243    g_assert(magic == ('v' | 'i' << 8 | 'r' << 16 | 't' << 24));
 244
 245    dev->version = qtest_readl(qts, addr + QVIRTIO_MMIO_VERSION);
 246    g_assert(dev->version == 1 || dev->version == 2);
 247
 248    dev->qts = qts;
 249    dev->addr = addr;
 250    dev->page_size = page_size;
 251    dev->vdev.device_type = qtest_readl(qts, addr + QVIRTIO_MMIO_DEVICE_ID);
 252    dev->vdev.bus = &qvirtio_mmio;
 253
 254    qtest_writel(qts, addr + QVIRTIO_MMIO_GUEST_PAGE_SIZE, page_size);
 255
 256    dev->obj.get_driver = qvirtio_mmio_get_driver;
 257    dev->obj.start_hw = qvirtio_mmio_start_hw;
 258}
 259
 260static void virtio_mmio_register_nodes(void)
 261{
 262    qos_node_create_driver("virtio-mmio", NULL);
 263    qos_node_produces("virtio-mmio", "virtio-bus");
 264}
 265
 266libqos_init(virtio_mmio_register_nodes);
 267