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