cachepc-qemu

Fork of AMDESE/qemu with changes for cachepc side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-qemu
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

virtio-mmio.c (8943B)


      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 "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);