qemu/tests/libqos/virtio-pci.c
<<
>>
Prefs
   1/*
   2 * libqos virtio PCI 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 "libqos/virtio-pci.h"
  14#include "libqos/pci.h"
  15#include "libqos/pci-pc.h"
  16#include "libqos/malloc.h"
  17#include "libqos/malloc-pc.h"
  18#include "standard-headers/linux/virtio_ring.h"
  19#include "standard-headers/linux/virtio_pci.h"
  20
  21#include "hw/pci/pci.h"
  22#include "hw/pci/pci_regs.h"
  23
  24typedef struct QVirtioPCIForeachData {
  25    void (*func)(QVirtioDevice *d, void *data);
  26    uint16_t device_type;
  27    bool has_slot;
  28    int slot;
  29    void *user_data;
  30} QVirtioPCIForeachData;
  31
  32void qvirtio_pci_device_free(QVirtioPCIDevice *dev)
  33{
  34    g_free(dev->pdev);
  35    g_free(dev);
  36}
  37
  38static QVirtioPCIDevice *qpcidevice_to_qvirtiodevice(QPCIDevice *pdev)
  39{
  40    QVirtioPCIDevice *vpcidev;
  41    vpcidev = g_malloc0(sizeof(*vpcidev));
  42
  43    if (pdev) {
  44        vpcidev->pdev = pdev;
  45        vpcidev->vdev.device_type =
  46                            qpci_config_readw(vpcidev->pdev, PCI_SUBSYSTEM_ID);
  47    }
  48
  49    vpcidev->config_msix_entry = -1;
  50
  51    return vpcidev;
  52}
  53
  54static void qvirtio_pci_foreach_callback(
  55                        QPCIDevice *dev, int devfn, void *data)
  56{
  57    QVirtioPCIForeachData *d = data;
  58    QVirtioPCIDevice *vpcidev = qpcidevice_to_qvirtiodevice(dev);
  59
  60    if (vpcidev->vdev.device_type == d->device_type &&
  61        (!d->has_slot || vpcidev->pdev->devfn == d->slot << 3)) {
  62        d->func(&vpcidev->vdev, d->user_data);
  63    } else {
  64        qvirtio_pci_device_free(vpcidev);
  65    }
  66}
  67
  68static void qvirtio_pci_assign_device(QVirtioDevice *d, void *data)
  69{
  70    QVirtioPCIDevice **vpcidev = data;
  71    assert(!*vpcidev);
  72    *vpcidev = (QVirtioPCIDevice *)d;
  73}
  74
  75#define CONFIG_BASE(dev) (VIRTIO_PCI_CONFIG_OFF((dev)->pdev->msix_enabled))
  76
  77static uint8_t qvirtio_pci_config_readb(QVirtioDevice *d, uint64_t off)
  78{
  79    QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
  80    return qpci_io_readb(dev->pdev, dev->bar, CONFIG_BASE(dev) + off);
  81}
  82
  83/* PCI is always read in little-endian order
  84 * but virtio ( < 1.0) is in guest order
  85 * so with a big-endian guest the order has been reversed,
  86 * reverse it again
  87 * virtio-1.0 is always little-endian, like PCI, but this
  88 * case will be managed inside qvirtio_is_big_endian()
  89 */
  90
  91static uint16_t qvirtio_pci_config_readw(QVirtioDevice *d, uint64_t off)
  92{
  93    QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
  94    uint16_t value;
  95
  96    value = qpci_io_readw(dev->pdev, dev->bar, CONFIG_BASE(dev) + off);
  97    if (qvirtio_is_big_endian(d)) {
  98        value = bswap16(value);
  99    }
 100    return value;
 101}
 102
 103static uint32_t qvirtio_pci_config_readl(QVirtioDevice *d, uint64_t off)
 104{
 105    QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
 106    uint32_t value;
 107
 108    value = qpci_io_readl(dev->pdev, dev->bar, CONFIG_BASE(dev) + off);
 109    if (qvirtio_is_big_endian(d)) {
 110        value = bswap32(value);
 111    }
 112    return value;
 113}
 114
 115static uint64_t qvirtio_pci_config_readq(QVirtioDevice *d, uint64_t off)
 116{
 117    QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
 118    uint64_t val;
 119
 120    val = qpci_io_readq(dev->pdev, dev->bar, CONFIG_BASE(dev) + off);
 121    if (qvirtio_is_big_endian(d)) {
 122        val = bswap64(val);
 123    }
 124
 125    return val;
 126}
 127
 128static uint32_t qvirtio_pci_get_features(QVirtioDevice *d)
 129{
 130    QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
 131    return qpci_io_readl(dev->pdev, dev->bar, VIRTIO_PCI_HOST_FEATURES);
 132}
 133
 134static void qvirtio_pci_set_features(QVirtioDevice *d, uint32_t features)
 135{
 136    QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
 137    qpci_io_writel(dev->pdev, dev->bar, VIRTIO_PCI_GUEST_FEATURES, features);
 138}
 139
 140static uint32_t qvirtio_pci_get_guest_features(QVirtioDevice *d)
 141{
 142    QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
 143    return qpci_io_readl(dev->pdev, dev->bar, VIRTIO_PCI_GUEST_FEATURES);
 144}
 145
 146static uint8_t qvirtio_pci_get_status(QVirtioDevice *d)
 147{
 148    QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
 149    return qpci_io_readb(dev->pdev, dev->bar, VIRTIO_PCI_STATUS);
 150}
 151
 152static void qvirtio_pci_set_status(QVirtioDevice *d, uint8_t status)
 153{
 154    QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
 155    qpci_io_writeb(dev->pdev, dev->bar, VIRTIO_PCI_STATUS, status);
 156}
 157
 158static bool qvirtio_pci_get_queue_isr_status(QVirtioDevice *d, QVirtQueue *vq)
 159{
 160    QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
 161    QVirtQueuePCI *vqpci = (QVirtQueuePCI *)vq;
 162    uint32_t data;
 163
 164    if (dev->pdev->msix_enabled) {
 165        g_assert_cmpint(vqpci->msix_entry, !=, -1);
 166        if (qpci_msix_masked(dev->pdev, vqpci->msix_entry)) {
 167            /* No ISR checking should be done if masked, but read anyway */
 168            return qpci_msix_pending(dev->pdev, vqpci->msix_entry);
 169        } else {
 170            data = readl(vqpci->msix_addr);
 171            if (data == vqpci->msix_data) {
 172                writel(vqpci->msix_addr, 0);
 173                return true;
 174            } else {
 175                return false;
 176            }
 177        }
 178    } else {
 179        return qpci_io_readb(dev->pdev, dev->bar, VIRTIO_PCI_ISR) & 1;
 180    }
 181}
 182
 183static bool qvirtio_pci_get_config_isr_status(QVirtioDevice *d)
 184{
 185    QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
 186    uint32_t data;
 187
 188    if (dev->pdev->msix_enabled) {
 189        g_assert_cmpint(dev->config_msix_entry, !=, -1);
 190        if (qpci_msix_masked(dev->pdev, dev->config_msix_entry)) {
 191            /* No ISR checking should be done if masked, but read anyway */
 192            return qpci_msix_pending(dev->pdev, dev->config_msix_entry);
 193        } else {
 194            data = readl(dev->config_msix_addr);
 195            if (data == dev->config_msix_data) {
 196                writel(dev->config_msix_addr, 0);
 197                return true;
 198            } else {
 199                return false;
 200            }
 201        }
 202    } else {
 203        return qpci_io_readb(dev->pdev, dev->bar, VIRTIO_PCI_ISR) & 2;
 204    }
 205}
 206
 207static void qvirtio_pci_queue_select(QVirtioDevice *d, uint16_t index)
 208{
 209    QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
 210    qpci_io_writeb(dev->pdev, dev->bar, VIRTIO_PCI_QUEUE_SEL, index);
 211}
 212
 213static uint16_t qvirtio_pci_get_queue_size(QVirtioDevice *d)
 214{
 215    QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
 216    return qpci_io_readw(dev->pdev, dev->bar, VIRTIO_PCI_QUEUE_NUM);
 217}
 218
 219static void qvirtio_pci_set_queue_address(QVirtioDevice *d, uint32_t pfn)
 220{
 221    QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
 222    qpci_io_writel(dev->pdev, dev->bar, VIRTIO_PCI_QUEUE_PFN, pfn);
 223}
 224
 225static QVirtQueue *qvirtio_pci_virtqueue_setup(QVirtioDevice *d,
 226                                        QGuestAllocator *alloc, uint16_t index)
 227{
 228    uint32_t feat;
 229    uint64_t addr;
 230    QVirtQueuePCI *vqpci;
 231
 232    vqpci = g_malloc0(sizeof(*vqpci));
 233    feat = qvirtio_pci_get_guest_features(d);
 234
 235    qvirtio_pci_queue_select(d, index);
 236    vqpci->vq.index = index;
 237    vqpci->vq.size = qvirtio_pci_get_queue_size(d);
 238    vqpci->vq.free_head = 0;
 239    vqpci->vq.num_free = vqpci->vq.size;
 240    vqpci->vq.align = VIRTIO_PCI_VRING_ALIGN;
 241    vqpci->vq.indirect = (feat & (1u << VIRTIO_RING_F_INDIRECT_DESC)) != 0;
 242    vqpci->vq.event = (feat & (1u << VIRTIO_RING_F_EVENT_IDX)) != 0;
 243
 244    vqpci->msix_entry = -1;
 245    vqpci->msix_addr = 0;
 246    vqpci->msix_data = 0x12345678;
 247
 248    /* Check different than 0 */
 249    g_assert_cmpint(vqpci->vq.size, !=, 0);
 250
 251    /* Check power of 2 */
 252    g_assert_cmpint(vqpci->vq.size & (vqpci->vq.size - 1), ==, 0);
 253
 254    addr = guest_alloc(alloc, qvring_size(vqpci->vq.size,
 255                                          VIRTIO_PCI_VRING_ALIGN));
 256    qvring_init(alloc, &vqpci->vq, addr);
 257    qvirtio_pci_set_queue_address(d, vqpci->vq.desc / VIRTIO_PCI_VRING_ALIGN);
 258
 259    return &vqpci->vq;
 260}
 261
 262static void qvirtio_pci_virtqueue_cleanup(QVirtQueue *vq,
 263                                          QGuestAllocator *alloc)
 264{
 265    QVirtQueuePCI *vqpci = container_of(vq, QVirtQueuePCI, vq);
 266
 267    guest_free(alloc, vq->desc);
 268    g_free(vqpci);
 269}
 270
 271static void qvirtio_pci_virtqueue_kick(QVirtioDevice *d, QVirtQueue *vq)
 272{
 273    QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
 274    qpci_io_writew(dev->pdev, dev->bar, VIRTIO_PCI_QUEUE_NOTIFY, vq->index);
 275}
 276
 277const QVirtioBus qvirtio_pci = {
 278    .config_readb = qvirtio_pci_config_readb,
 279    .config_readw = qvirtio_pci_config_readw,
 280    .config_readl = qvirtio_pci_config_readl,
 281    .config_readq = qvirtio_pci_config_readq,
 282    .get_features = qvirtio_pci_get_features,
 283    .set_features = qvirtio_pci_set_features,
 284    .get_guest_features = qvirtio_pci_get_guest_features,
 285    .get_status = qvirtio_pci_get_status,
 286    .set_status = qvirtio_pci_set_status,
 287    .get_queue_isr_status = qvirtio_pci_get_queue_isr_status,
 288    .get_config_isr_status = qvirtio_pci_get_config_isr_status,
 289    .queue_select = qvirtio_pci_queue_select,
 290    .get_queue_size = qvirtio_pci_get_queue_size,
 291    .set_queue_address = qvirtio_pci_set_queue_address,
 292    .virtqueue_setup = qvirtio_pci_virtqueue_setup,
 293    .virtqueue_cleanup = qvirtio_pci_virtqueue_cleanup,
 294    .virtqueue_kick = qvirtio_pci_virtqueue_kick,
 295};
 296
 297static void qvirtio_pci_foreach(QPCIBus *bus, uint16_t device_type,
 298                bool has_slot, int slot,
 299                void (*func)(QVirtioDevice *d, void *data), void *data)
 300{
 301    QVirtioPCIForeachData d = { .func = func,
 302                                .device_type = device_type,
 303                                .has_slot = has_slot,
 304                                .slot = slot,
 305                                .user_data = data };
 306
 307    qpci_device_foreach(bus, PCI_VENDOR_ID_REDHAT_QUMRANET, -1,
 308                        qvirtio_pci_foreach_callback, &d);
 309}
 310
 311QVirtioPCIDevice *qvirtio_pci_device_find(QPCIBus *bus, uint16_t device_type)
 312{
 313    QVirtioPCIDevice *dev = NULL;
 314
 315    qvirtio_pci_foreach(bus, device_type, false, 0,
 316                        qvirtio_pci_assign_device, &dev);
 317
 318    if (dev) {
 319        dev->vdev.bus = &qvirtio_pci;
 320    }
 321
 322    return dev;
 323}
 324
 325QVirtioPCIDevice *qvirtio_pci_device_find_slot(QPCIBus *bus,
 326                                               uint16_t device_type, int slot)
 327{
 328    QVirtioPCIDevice *dev = NULL;
 329
 330    qvirtio_pci_foreach(bus, device_type, true, slot,
 331                        qvirtio_pci_assign_device, &dev);
 332
 333    dev->vdev.bus = &qvirtio_pci;
 334
 335    return dev;
 336}
 337
 338void qvirtio_pci_device_enable(QVirtioPCIDevice *d)
 339{
 340    qpci_device_enable(d->pdev);
 341    d->bar = qpci_iomap(d->pdev, 0, NULL);
 342}
 343
 344void qvirtio_pci_device_disable(QVirtioPCIDevice *d)
 345{
 346    qpci_iounmap(d->pdev, d->bar);
 347}
 348
 349void qvirtqueue_pci_msix_setup(QVirtioPCIDevice *d, QVirtQueuePCI *vqpci,
 350                                        QGuestAllocator *alloc, uint16_t entry)
 351{
 352    uint16_t vector;
 353    uint32_t control;
 354    uint64_t off;
 355
 356    g_assert(d->pdev->msix_enabled);
 357    off = d->pdev->msix_table_off + (entry * 16);
 358
 359    g_assert_cmpint(entry, >=, 0);
 360    g_assert_cmpint(entry, <, qpci_msix_table_size(d->pdev));
 361    vqpci->msix_entry = entry;
 362
 363    vqpci->msix_addr = guest_alloc(alloc, 4);
 364    qpci_io_writel(d->pdev, d->pdev->msix_table_bar,
 365                   off + PCI_MSIX_ENTRY_LOWER_ADDR, vqpci->msix_addr & ~0UL);
 366    qpci_io_writel(d->pdev, d->pdev->msix_table_bar,
 367                   off + PCI_MSIX_ENTRY_UPPER_ADDR,
 368                   (vqpci->msix_addr >> 32) & ~0UL);
 369    qpci_io_writel(d->pdev, d->pdev->msix_table_bar,
 370                   off + PCI_MSIX_ENTRY_DATA, vqpci->msix_data);
 371
 372    control = qpci_io_readl(d->pdev, d->pdev->msix_table_bar,
 373                            off + PCI_MSIX_ENTRY_VECTOR_CTRL);
 374    qpci_io_writel(d->pdev, d->pdev->msix_table_bar,
 375                   off + PCI_MSIX_ENTRY_VECTOR_CTRL,
 376                   control & ~PCI_MSIX_ENTRY_CTRL_MASKBIT);
 377
 378    qvirtio_pci_queue_select(&d->vdev, vqpci->vq.index);
 379    qpci_io_writew(d->pdev, d->bar, VIRTIO_MSI_QUEUE_VECTOR, entry);
 380    vector = qpci_io_readw(d->pdev, d->bar, VIRTIO_MSI_QUEUE_VECTOR);
 381    g_assert_cmphex(vector, !=, VIRTIO_MSI_NO_VECTOR);
 382}
 383
 384void qvirtio_pci_set_msix_configuration_vector(QVirtioPCIDevice *d,
 385                                        QGuestAllocator *alloc, uint16_t entry)
 386{
 387    uint16_t vector;
 388    uint32_t control;
 389    uint64_t off;
 390
 391    g_assert(d->pdev->msix_enabled);
 392    off = d->pdev->msix_table_off + (entry * 16);
 393
 394    g_assert_cmpint(entry, >=, 0);
 395    g_assert_cmpint(entry, <, qpci_msix_table_size(d->pdev));
 396    d->config_msix_entry = entry;
 397
 398    d->config_msix_data = 0x12345678;
 399    d->config_msix_addr = guest_alloc(alloc, 4);
 400
 401    qpci_io_writel(d->pdev, d->pdev->msix_table_bar,
 402                   off + PCI_MSIX_ENTRY_LOWER_ADDR, d->config_msix_addr & ~0UL);
 403    qpci_io_writel(d->pdev, d->pdev->msix_table_bar,
 404                   off + PCI_MSIX_ENTRY_UPPER_ADDR,
 405                   (d->config_msix_addr >> 32) & ~0UL);
 406    qpci_io_writel(d->pdev, d->pdev->msix_table_bar,
 407                   off + PCI_MSIX_ENTRY_DATA, d->config_msix_data);
 408
 409    control = qpci_io_readl(d->pdev, d->pdev->msix_table_bar,
 410                            off + PCI_MSIX_ENTRY_VECTOR_CTRL);
 411    qpci_io_writel(d->pdev, d->pdev->msix_table_bar,
 412                   off + PCI_MSIX_ENTRY_VECTOR_CTRL,
 413                   control & ~PCI_MSIX_ENTRY_CTRL_MASKBIT);
 414
 415    qpci_io_writew(d->pdev, d->bar, VIRTIO_MSI_CONFIG_VECTOR, entry);
 416    vector = qpci_io_readw(d->pdev, d->bar, VIRTIO_MSI_CONFIG_VECTOR);
 417    g_assert_cmphex(vector, !=, VIRTIO_MSI_NO_VECTOR);
 418}
 419