1*1cf4323eSThomas Huth /* 2*1cf4323eSThomas Huth * libqos virtio PCI driver 3*1cf4323eSThomas Huth * 4*1cf4323eSThomas Huth * Copyright (c) 2014 Marc Marí 5*1cf4323eSThomas Huth * 6*1cf4323eSThomas Huth * This work is licensed under the terms of the GNU GPL, version 2 or later. 7*1cf4323eSThomas Huth * See the COPYING file in the top-level directory. 8*1cf4323eSThomas Huth */ 9*1cf4323eSThomas Huth 10*1cf4323eSThomas Huth #include "qemu/osdep.h" 11*1cf4323eSThomas Huth #include "libqtest.h" 12*1cf4323eSThomas Huth #include "libqos/virtio.h" 13*1cf4323eSThomas Huth #include "libqos/virtio-pci.h" 14*1cf4323eSThomas Huth #include "libqos/pci.h" 15*1cf4323eSThomas Huth #include "libqos/pci-pc.h" 16*1cf4323eSThomas Huth #include "libqos/malloc.h" 17*1cf4323eSThomas Huth #include "libqos/malloc-pc.h" 18*1cf4323eSThomas Huth #include "libqos/qgraph.h" 19*1cf4323eSThomas Huth #include "standard-headers/linux/virtio_ring.h" 20*1cf4323eSThomas Huth #include "standard-headers/linux/virtio_pci.h" 21*1cf4323eSThomas Huth 22*1cf4323eSThomas Huth #include "hw/pci/pci.h" 23*1cf4323eSThomas Huth #include "hw/pci/pci_regs.h" 24*1cf4323eSThomas Huth 25*1cf4323eSThomas Huth #include "virtio-pci-modern.h" 26*1cf4323eSThomas Huth 27*1cf4323eSThomas Huth /* virtio-pci is a superclass of all virtio-xxx-pci devices; 28*1cf4323eSThomas Huth * the relation between virtio-pci and virtio-xxx-pci is implicit, 29*1cf4323eSThomas Huth * and therefore virtio-pci does not produce virtio and is not 30*1cf4323eSThomas Huth * reached by any edge, not even as a "contains" edge. 31*1cf4323eSThomas Huth * In facts, every device is a QVirtioPCIDevice with 32*1cf4323eSThomas Huth * additional fields, since every one has its own 33*1cf4323eSThomas Huth * number of queues and various attributes. 34*1cf4323eSThomas Huth * Virtio-pci provides default functions to start the 35*1cf4323eSThomas Huth * hw and destroy the object, and nodes that want to 36*1cf4323eSThomas Huth * override them should always remember to call the 37*1cf4323eSThomas Huth * original qvirtio_pci_destructor and qvirtio_pci_start_hw. 38*1cf4323eSThomas Huth */ 39*1cf4323eSThomas Huth 40*1cf4323eSThomas Huth #define CONFIG_BASE(dev) (VIRTIO_PCI_CONFIG_OFF((dev)->pdev->msix_enabled)) 41*1cf4323eSThomas Huth 42*1cf4323eSThomas Huth static uint8_t qvirtio_pci_config_readb(QVirtioDevice *d, uint64_t off) 43*1cf4323eSThomas Huth { 44*1cf4323eSThomas Huth QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); 45*1cf4323eSThomas Huth return qpci_io_readb(dev->pdev, dev->bar, CONFIG_BASE(dev) + off); 46*1cf4323eSThomas Huth } 47*1cf4323eSThomas Huth 48*1cf4323eSThomas Huth /* PCI is always read in little-endian order 49*1cf4323eSThomas Huth * but virtio ( < 1.0) is in guest order 50*1cf4323eSThomas Huth * so with a big-endian guest the order has been reversed, 51*1cf4323eSThomas Huth * reverse it again 52*1cf4323eSThomas Huth * virtio-1.0 is always little-endian, like PCI 53*1cf4323eSThomas Huth */ 54*1cf4323eSThomas Huth 55*1cf4323eSThomas Huth static uint16_t qvirtio_pci_config_readw(QVirtioDevice *d, uint64_t off) 56*1cf4323eSThomas Huth { 57*1cf4323eSThomas Huth QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); 58*1cf4323eSThomas Huth uint16_t value; 59*1cf4323eSThomas Huth 60*1cf4323eSThomas Huth value = qpci_io_readw(dev->pdev, dev->bar, CONFIG_BASE(dev) + off); 61*1cf4323eSThomas Huth if (qvirtio_is_big_endian(d)) { 62*1cf4323eSThomas Huth value = bswap16(value); 63*1cf4323eSThomas Huth } 64*1cf4323eSThomas Huth return value; 65*1cf4323eSThomas Huth } 66*1cf4323eSThomas Huth 67*1cf4323eSThomas Huth static uint32_t qvirtio_pci_config_readl(QVirtioDevice *d, uint64_t off) 68*1cf4323eSThomas Huth { 69*1cf4323eSThomas Huth QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); 70*1cf4323eSThomas Huth uint32_t value; 71*1cf4323eSThomas Huth 72*1cf4323eSThomas Huth value = qpci_io_readl(dev->pdev, dev->bar, CONFIG_BASE(dev) + off); 73*1cf4323eSThomas Huth if (qvirtio_is_big_endian(d)) { 74*1cf4323eSThomas Huth value = bswap32(value); 75*1cf4323eSThomas Huth } 76*1cf4323eSThomas Huth return value; 77*1cf4323eSThomas Huth } 78*1cf4323eSThomas Huth 79*1cf4323eSThomas Huth static uint64_t qvirtio_pci_config_readq(QVirtioDevice *d, uint64_t off) 80*1cf4323eSThomas Huth { 81*1cf4323eSThomas Huth QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); 82*1cf4323eSThomas Huth uint64_t val; 83*1cf4323eSThomas Huth 84*1cf4323eSThomas Huth val = qpci_io_readq(dev->pdev, dev->bar, CONFIG_BASE(dev) + off); 85*1cf4323eSThomas Huth if (qvirtio_is_big_endian(d)) { 86*1cf4323eSThomas Huth val = bswap64(val); 87*1cf4323eSThomas Huth } 88*1cf4323eSThomas Huth 89*1cf4323eSThomas Huth return val; 90*1cf4323eSThomas Huth } 91*1cf4323eSThomas Huth 92*1cf4323eSThomas Huth static uint64_t qvirtio_pci_get_features(QVirtioDevice *d) 93*1cf4323eSThomas Huth { 94*1cf4323eSThomas Huth QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); 95*1cf4323eSThomas Huth return qpci_io_readl(dev->pdev, dev->bar, VIRTIO_PCI_HOST_FEATURES); 96*1cf4323eSThomas Huth } 97*1cf4323eSThomas Huth 98*1cf4323eSThomas Huth static void qvirtio_pci_set_features(QVirtioDevice *d, uint64_t features) 99*1cf4323eSThomas Huth { 100*1cf4323eSThomas Huth QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); 101*1cf4323eSThomas Huth qpci_io_writel(dev->pdev, dev->bar, VIRTIO_PCI_GUEST_FEATURES, features); 102*1cf4323eSThomas Huth } 103*1cf4323eSThomas Huth 104*1cf4323eSThomas Huth static uint64_t qvirtio_pci_get_guest_features(QVirtioDevice *d) 105*1cf4323eSThomas Huth { 106*1cf4323eSThomas Huth QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); 107*1cf4323eSThomas Huth return qpci_io_readl(dev->pdev, dev->bar, VIRTIO_PCI_GUEST_FEATURES); 108*1cf4323eSThomas Huth } 109*1cf4323eSThomas Huth 110*1cf4323eSThomas Huth static uint8_t qvirtio_pci_get_status(QVirtioDevice *d) 111*1cf4323eSThomas Huth { 112*1cf4323eSThomas Huth QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); 113*1cf4323eSThomas Huth return qpci_io_readb(dev->pdev, dev->bar, VIRTIO_PCI_STATUS); 114*1cf4323eSThomas Huth } 115*1cf4323eSThomas Huth 116*1cf4323eSThomas Huth static void qvirtio_pci_set_status(QVirtioDevice *d, uint8_t status) 117*1cf4323eSThomas Huth { 118*1cf4323eSThomas Huth QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); 119*1cf4323eSThomas Huth qpci_io_writeb(dev->pdev, dev->bar, VIRTIO_PCI_STATUS, status); 120*1cf4323eSThomas Huth } 121*1cf4323eSThomas Huth 122*1cf4323eSThomas Huth static bool qvirtio_pci_get_queue_isr_status(QVirtioDevice *d, QVirtQueue *vq) 123*1cf4323eSThomas Huth { 124*1cf4323eSThomas Huth QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); 125*1cf4323eSThomas Huth QVirtQueuePCI *vqpci = (QVirtQueuePCI *)vq; 126*1cf4323eSThomas Huth uint32_t data; 127*1cf4323eSThomas Huth 128*1cf4323eSThomas Huth if (dev->pdev->msix_enabled) { 129*1cf4323eSThomas Huth g_assert_cmpint(vqpci->msix_entry, !=, -1); 130*1cf4323eSThomas Huth if (qpci_msix_masked(dev->pdev, vqpci->msix_entry)) { 131*1cf4323eSThomas Huth /* No ISR checking should be done if masked, but read anyway */ 132*1cf4323eSThomas Huth return qpci_msix_pending(dev->pdev, vqpci->msix_entry); 133*1cf4323eSThomas Huth } else { 134*1cf4323eSThomas Huth data = qtest_readl(dev->pdev->bus->qts, vqpci->msix_addr); 135*1cf4323eSThomas Huth if (data == vqpci->msix_data) { 136*1cf4323eSThomas Huth qtest_writel(dev->pdev->bus->qts, vqpci->msix_addr, 0); 137*1cf4323eSThomas Huth return true; 138*1cf4323eSThomas Huth } else { 139*1cf4323eSThomas Huth return false; 140*1cf4323eSThomas Huth } 141*1cf4323eSThomas Huth } 142*1cf4323eSThomas Huth } else { 143*1cf4323eSThomas Huth return qpci_io_readb(dev->pdev, dev->bar, VIRTIO_PCI_ISR) & 1; 144*1cf4323eSThomas Huth } 145*1cf4323eSThomas Huth } 146*1cf4323eSThomas Huth 147*1cf4323eSThomas Huth static bool qvirtio_pci_get_config_isr_status(QVirtioDevice *d) 148*1cf4323eSThomas Huth { 149*1cf4323eSThomas Huth QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); 150*1cf4323eSThomas Huth uint32_t data; 151*1cf4323eSThomas Huth 152*1cf4323eSThomas Huth if (dev->pdev->msix_enabled) { 153*1cf4323eSThomas Huth g_assert_cmpint(dev->config_msix_entry, !=, -1); 154*1cf4323eSThomas Huth if (qpci_msix_masked(dev->pdev, dev->config_msix_entry)) { 155*1cf4323eSThomas Huth /* No ISR checking should be done if masked, but read anyway */ 156*1cf4323eSThomas Huth return qpci_msix_pending(dev->pdev, dev->config_msix_entry); 157*1cf4323eSThomas Huth } else { 158*1cf4323eSThomas Huth data = qtest_readl(dev->pdev->bus->qts, dev->config_msix_addr); 159*1cf4323eSThomas Huth if (data == dev->config_msix_data) { 160*1cf4323eSThomas Huth qtest_writel(dev->pdev->bus->qts, dev->config_msix_addr, 0); 161*1cf4323eSThomas Huth return true; 162*1cf4323eSThomas Huth } else { 163*1cf4323eSThomas Huth return false; 164*1cf4323eSThomas Huth } 165*1cf4323eSThomas Huth } 166*1cf4323eSThomas Huth } else { 167*1cf4323eSThomas Huth return qpci_io_readb(dev->pdev, dev->bar, VIRTIO_PCI_ISR) & 2; 168*1cf4323eSThomas Huth } 169*1cf4323eSThomas Huth } 170*1cf4323eSThomas Huth 171*1cf4323eSThomas Huth static void qvirtio_pci_wait_config_isr_status(QVirtioDevice *d, 172*1cf4323eSThomas Huth gint64 timeout_us) 173*1cf4323eSThomas Huth { 174*1cf4323eSThomas Huth QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); 175*1cf4323eSThomas Huth gint64 start_time = g_get_monotonic_time(); 176*1cf4323eSThomas Huth 177*1cf4323eSThomas Huth do { 178*1cf4323eSThomas Huth g_assert(g_get_monotonic_time() - start_time <= timeout_us); 179*1cf4323eSThomas Huth qtest_clock_step(dev->pdev->bus->qts, 100); 180*1cf4323eSThomas Huth } while (!qvirtio_pci_get_config_isr_status(d)); 181*1cf4323eSThomas Huth } 182*1cf4323eSThomas Huth 183*1cf4323eSThomas Huth static void qvirtio_pci_queue_select(QVirtioDevice *d, uint16_t index) 184*1cf4323eSThomas Huth { 185*1cf4323eSThomas Huth QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); 186*1cf4323eSThomas Huth qpci_io_writeb(dev->pdev, dev->bar, VIRTIO_PCI_QUEUE_SEL, index); 187*1cf4323eSThomas Huth } 188*1cf4323eSThomas Huth 189*1cf4323eSThomas Huth static uint16_t qvirtio_pci_get_queue_size(QVirtioDevice *d) 190*1cf4323eSThomas Huth { 191*1cf4323eSThomas Huth QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); 192*1cf4323eSThomas Huth return qpci_io_readw(dev->pdev, dev->bar, VIRTIO_PCI_QUEUE_NUM); 193*1cf4323eSThomas Huth } 194*1cf4323eSThomas Huth 195*1cf4323eSThomas Huth static void qvirtio_pci_set_queue_address(QVirtioDevice *d, QVirtQueue *vq) 196*1cf4323eSThomas Huth { 197*1cf4323eSThomas Huth QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); 198*1cf4323eSThomas Huth uint64_t pfn = vq->desc / VIRTIO_PCI_VRING_ALIGN; 199*1cf4323eSThomas Huth 200*1cf4323eSThomas Huth qpci_io_writel(dev->pdev, dev->bar, VIRTIO_PCI_QUEUE_PFN, pfn); 201*1cf4323eSThomas Huth } 202*1cf4323eSThomas Huth 203*1cf4323eSThomas Huth QVirtQueue *qvirtio_pci_virtqueue_setup_common(QVirtioDevice *d, 204*1cf4323eSThomas Huth QGuestAllocator *alloc, 205*1cf4323eSThomas Huth uint16_t index) 206*1cf4323eSThomas Huth { 207*1cf4323eSThomas Huth uint64_t feat; 208*1cf4323eSThomas Huth uint64_t addr; 209*1cf4323eSThomas Huth QVirtQueuePCI *vqpci; 210*1cf4323eSThomas Huth QVirtioPCIDevice *qvpcidev = container_of(d, QVirtioPCIDevice, vdev); 211*1cf4323eSThomas Huth 212*1cf4323eSThomas Huth vqpci = g_malloc0(sizeof(*vqpci)); 213*1cf4323eSThomas Huth feat = d->bus->get_guest_features(d); 214*1cf4323eSThomas Huth 215*1cf4323eSThomas Huth d->bus->queue_select(d, index); 216*1cf4323eSThomas Huth vqpci->vq.vdev = d; 217*1cf4323eSThomas Huth vqpci->vq.index = index; 218*1cf4323eSThomas Huth vqpci->vq.size = d->bus->get_queue_size(d); 219*1cf4323eSThomas Huth vqpci->vq.free_head = 0; 220*1cf4323eSThomas Huth vqpci->vq.num_free = vqpci->vq.size; 221*1cf4323eSThomas Huth vqpci->vq.align = VIRTIO_PCI_VRING_ALIGN; 222*1cf4323eSThomas Huth vqpci->vq.indirect = feat & (1ull << VIRTIO_RING_F_INDIRECT_DESC); 223*1cf4323eSThomas Huth vqpci->vq.event = feat & (1ull << VIRTIO_RING_F_EVENT_IDX); 224*1cf4323eSThomas Huth 225*1cf4323eSThomas Huth vqpci->msix_entry = -1; 226*1cf4323eSThomas Huth vqpci->msix_addr = 0; 227*1cf4323eSThomas Huth vqpci->msix_data = 0x12345678; 228*1cf4323eSThomas Huth 229*1cf4323eSThomas Huth /* Check different than 0 */ 230*1cf4323eSThomas Huth g_assert_cmpint(vqpci->vq.size, !=, 0); 231*1cf4323eSThomas Huth 232*1cf4323eSThomas Huth /* Check power of 2 */ 233*1cf4323eSThomas Huth g_assert_cmpint(vqpci->vq.size & (vqpci->vq.size - 1), ==, 0); 234*1cf4323eSThomas Huth 235*1cf4323eSThomas Huth addr = guest_alloc(alloc, qvring_size(vqpci->vq.size, 236*1cf4323eSThomas Huth VIRTIO_PCI_VRING_ALIGN)); 237*1cf4323eSThomas Huth qvring_init(qvpcidev->pdev->bus->qts, alloc, &vqpci->vq, addr); 238*1cf4323eSThomas Huth d->bus->set_queue_address(d, &vqpci->vq); 239*1cf4323eSThomas Huth 240*1cf4323eSThomas Huth return &vqpci->vq; 241*1cf4323eSThomas Huth } 242*1cf4323eSThomas Huth 243*1cf4323eSThomas Huth void qvirtio_pci_virtqueue_cleanup_common(QVirtQueue *vq, 244*1cf4323eSThomas Huth QGuestAllocator *alloc) 245*1cf4323eSThomas Huth { 246*1cf4323eSThomas Huth QVirtQueuePCI *vqpci = container_of(vq, QVirtQueuePCI, vq); 247*1cf4323eSThomas Huth 248*1cf4323eSThomas Huth guest_free(alloc, vq->desc); 249*1cf4323eSThomas Huth g_free(vqpci); 250*1cf4323eSThomas Huth } 251*1cf4323eSThomas Huth 252*1cf4323eSThomas Huth static void qvirtio_pci_virtqueue_kick(QVirtioDevice *d, QVirtQueue *vq) 253*1cf4323eSThomas Huth { 254*1cf4323eSThomas Huth QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); 255*1cf4323eSThomas Huth qpci_io_writew(dev->pdev, dev->bar, VIRTIO_PCI_QUEUE_NOTIFY, vq->index); 256*1cf4323eSThomas Huth } 257*1cf4323eSThomas Huth 258*1cf4323eSThomas Huth static const QVirtioBus qvirtio_pci_legacy = { 259*1cf4323eSThomas Huth .config_readb = qvirtio_pci_config_readb, 260*1cf4323eSThomas Huth .config_readw = qvirtio_pci_config_readw, 261*1cf4323eSThomas Huth .config_readl = qvirtio_pci_config_readl, 262*1cf4323eSThomas Huth .config_readq = qvirtio_pci_config_readq, 263*1cf4323eSThomas Huth .get_features = qvirtio_pci_get_features, 264*1cf4323eSThomas Huth .set_features = qvirtio_pci_set_features, 265*1cf4323eSThomas Huth .get_guest_features = qvirtio_pci_get_guest_features, 266*1cf4323eSThomas Huth .get_status = qvirtio_pci_get_status, 267*1cf4323eSThomas Huth .set_status = qvirtio_pci_set_status, 268*1cf4323eSThomas Huth .get_queue_isr_status = qvirtio_pci_get_queue_isr_status, 269*1cf4323eSThomas Huth .wait_config_isr_status = qvirtio_pci_wait_config_isr_status, 270*1cf4323eSThomas Huth .queue_select = qvirtio_pci_queue_select, 271*1cf4323eSThomas Huth .get_queue_size = qvirtio_pci_get_queue_size, 272*1cf4323eSThomas Huth .set_queue_address = qvirtio_pci_set_queue_address, 273*1cf4323eSThomas Huth .virtqueue_setup = qvirtio_pci_virtqueue_setup_common, 274*1cf4323eSThomas Huth .virtqueue_cleanup = qvirtio_pci_virtqueue_cleanup_common, 275*1cf4323eSThomas Huth .virtqueue_kick = qvirtio_pci_virtqueue_kick, 276*1cf4323eSThomas Huth }; 277*1cf4323eSThomas Huth 278*1cf4323eSThomas Huth static void qvirtio_pci_set_config_vector(QVirtioPCIDevice *d, uint16_t entry) 279*1cf4323eSThomas Huth { 280*1cf4323eSThomas Huth uint16_t vector; 281*1cf4323eSThomas Huth 282*1cf4323eSThomas Huth qpci_io_writew(d->pdev, d->bar, VIRTIO_MSI_CONFIG_VECTOR, entry); 283*1cf4323eSThomas Huth vector = qpci_io_readw(d->pdev, d->bar, VIRTIO_MSI_CONFIG_VECTOR); 284*1cf4323eSThomas Huth g_assert_cmphex(vector, !=, VIRTIO_MSI_NO_VECTOR); 285*1cf4323eSThomas Huth } 286*1cf4323eSThomas Huth 287*1cf4323eSThomas Huth static void qvirtio_pci_set_queue_vector(QVirtioPCIDevice *d, uint16_t vq_idx, 288*1cf4323eSThomas Huth uint16_t entry) 289*1cf4323eSThomas Huth { 290*1cf4323eSThomas Huth uint16_t vector; 291*1cf4323eSThomas Huth 292*1cf4323eSThomas Huth qvirtio_pci_queue_select(&d->vdev, vq_idx); 293*1cf4323eSThomas Huth qpci_io_writew(d->pdev, d->bar, VIRTIO_MSI_QUEUE_VECTOR, entry); 294*1cf4323eSThomas Huth vector = qpci_io_readw(d->pdev, d->bar, VIRTIO_MSI_QUEUE_VECTOR); 295*1cf4323eSThomas Huth g_assert_cmphex(vector, !=, VIRTIO_MSI_NO_VECTOR); 296*1cf4323eSThomas Huth } 297*1cf4323eSThomas Huth 298*1cf4323eSThomas Huth static const QVirtioPCIMSIXOps qvirtio_pci_msix_ops_legacy = { 299*1cf4323eSThomas Huth .set_config_vector = qvirtio_pci_set_config_vector, 300*1cf4323eSThomas Huth .set_queue_vector = qvirtio_pci_set_queue_vector, 301*1cf4323eSThomas Huth }; 302*1cf4323eSThomas Huth 303*1cf4323eSThomas Huth void qvirtio_pci_device_enable(QVirtioPCIDevice *d) 304*1cf4323eSThomas Huth { 305*1cf4323eSThomas Huth qpci_device_enable(d->pdev); 306*1cf4323eSThomas Huth d->bar = qpci_iomap(d->pdev, d->bar_idx, NULL); 307*1cf4323eSThomas Huth } 308*1cf4323eSThomas Huth 309*1cf4323eSThomas Huth void qvirtio_pci_device_disable(QVirtioPCIDevice *d) 310*1cf4323eSThomas Huth { 311*1cf4323eSThomas Huth qpci_iounmap(d->pdev, d->bar); 312*1cf4323eSThomas Huth } 313*1cf4323eSThomas Huth 314*1cf4323eSThomas Huth void qvirtqueue_pci_msix_setup(QVirtioPCIDevice *d, QVirtQueuePCI *vqpci, 315*1cf4323eSThomas Huth QGuestAllocator *alloc, uint16_t entry) 316*1cf4323eSThomas Huth { 317*1cf4323eSThomas Huth uint32_t control; 318*1cf4323eSThomas Huth uint64_t off; 319*1cf4323eSThomas Huth 320*1cf4323eSThomas Huth g_assert(d->pdev->msix_enabled); 321*1cf4323eSThomas Huth off = d->pdev->msix_table_off + (entry * 16); 322*1cf4323eSThomas Huth 323*1cf4323eSThomas Huth g_assert_cmpint(entry, >=, 0); 324*1cf4323eSThomas Huth g_assert_cmpint(entry, <, qpci_msix_table_size(d->pdev)); 325*1cf4323eSThomas Huth vqpci->msix_entry = entry; 326*1cf4323eSThomas Huth 327*1cf4323eSThomas Huth vqpci->msix_addr = guest_alloc(alloc, 4); 328*1cf4323eSThomas Huth qpci_io_writel(d->pdev, d->pdev->msix_table_bar, 329*1cf4323eSThomas Huth off + PCI_MSIX_ENTRY_LOWER_ADDR, vqpci->msix_addr & ~0UL); 330*1cf4323eSThomas Huth qpci_io_writel(d->pdev, d->pdev->msix_table_bar, 331*1cf4323eSThomas Huth off + PCI_MSIX_ENTRY_UPPER_ADDR, 332*1cf4323eSThomas Huth (vqpci->msix_addr >> 32) & ~0UL); 333*1cf4323eSThomas Huth qpci_io_writel(d->pdev, d->pdev->msix_table_bar, 334*1cf4323eSThomas Huth off + PCI_MSIX_ENTRY_DATA, vqpci->msix_data); 335*1cf4323eSThomas Huth 336*1cf4323eSThomas Huth control = qpci_io_readl(d->pdev, d->pdev->msix_table_bar, 337*1cf4323eSThomas Huth off + PCI_MSIX_ENTRY_VECTOR_CTRL); 338*1cf4323eSThomas Huth qpci_io_writel(d->pdev, d->pdev->msix_table_bar, 339*1cf4323eSThomas Huth off + PCI_MSIX_ENTRY_VECTOR_CTRL, 340*1cf4323eSThomas Huth control & ~PCI_MSIX_ENTRY_CTRL_MASKBIT); 341*1cf4323eSThomas Huth 342*1cf4323eSThomas Huth d->msix_ops->set_queue_vector(d, vqpci->vq.index, entry); 343*1cf4323eSThomas Huth } 344*1cf4323eSThomas Huth 345*1cf4323eSThomas Huth void qvirtio_pci_set_msix_configuration_vector(QVirtioPCIDevice *d, 346*1cf4323eSThomas Huth QGuestAllocator *alloc, uint16_t entry) 347*1cf4323eSThomas Huth { 348*1cf4323eSThomas Huth uint32_t control; 349*1cf4323eSThomas Huth uint64_t off; 350*1cf4323eSThomas Huth 351*1cf4323eSThomas Huth g_assert(d->pdev->msix_enabled); 352*1cf4323eSThomas Huth off = d->pdev->msix_table_off + (entry * 16); 353*1cf4323eSThomas Huth 354*1cf4323eSThomas Huth g_assert_cmpint(entry, >=, 0); 355*1cf4323eSThomas Huth g_assert_cmpint(entry, <, qpci_msix_table_size(d->pdev)); 356*1cf4323eSThomas Huth d->config_msix_entry = entry; 357*1cf4323eSThomas Huth 358*1cf4323eSThomas Huth d->config_msix_data = 0x12345678; 359*1cf4323eSThomas Huth d->config_msix_addr = guest_alloc(alloc, 4); 360*1cf4323eSThomas Huth 361*1cf4323eSThomas Huth qpci_io_writel(d->pdev, d->pdev->msix_table_bar, 362*1cf4323eSThomas Huth off + PCI_MSIX_ENTRY_LOWER_ADDR, d->config_msix_addr & ~0UL); 363*1cf4323eSThomas Huth qpci_io_writel(d->pdev, d->pdev->msix_table_bar, 364*1cf4323eSThomas Huth off + PCI_MSIX_ENTRY_UPPER_ADDR, 365*1cf4323eSThomas Huth (d->config_msix_addr >> 32) & ~0UL); 366*1cf4323eSThomas Huth qpci_io_writel(d->pdev, d->pdev->msix_table_bar, 367*1cf4323eSThomas Huth off + PCI_MSIX_ENTRY_DATA, d->config_msix_data); 368*1cf4323eSThomas Huth 369*1cf4323eSThomas Huth control = qpci_io_readl(d->pdev, d->pdev->msix_table_bar, 370*1cf4323eSThomas Huth off + PCI_MSIX_ENTRY_VECTOR_CTRL); 371*1cf4323eSThomas Huth qpci_io_writel(d->pdev, d->pdev->msix_table_bar, 372*1cf4323eSThomas Huth off + PCI_MSIX_ENTRY_VECTOR_CTRL, 373*1cf4323eSThomas Huth control & ~PCI_MSIX_ENTRY_CTRL_MASKBIT); 374*1cf4323eSThomas Huth 375*1cf4323eSThomas Huth d->msix_ops->set_config_vector(d, entry); 376*1cf4323eSThomas Huth } 377*1cf4323eSThomas Huth 378*1cf4323eSThomas Huth void qvirtio_pci_destructor(QOSGraphObject *obj) 379*1cf4323eSThomas Huth { 380*1cf4323eSThomas Huth QVirtioPCIDevice *dev = (QVirtioPCIDevice *)obj; 381*1cf4323eSThomas Huth qvirtio_pci_device_disable(dev); 382*1cf4323eSThomas Huth g_free(dev->pdev); 383*1cf4323eSThomas Huth } 384*1cf4323eSThomas Huth 385*1cf4323eSThomas Huth void qvirtio_pci_start_hw(QOSGraphObject *obj) 386*1cf4323eSThomas Huth { 387*1cf4323eSThomas Huth QVirtioPCIDevice *dev = (QVirtioPCIDevice *)obj; 388*1cf4323eSThomas Huth qvirtio_pci_device_enable(dev); 389*1cf4323eSThomas Huth qvirtio_start_device(&dev->vdev); 390*1cf4323eSThomas Huth } 391*1cf4323eSThomas Huth 392*1cf4323eSThomas Huth static void qvirtio_pci_init_legacy(QVirtioPCIDevice *dev) 393*1cf4323eSThomas Huth { 394*1cf4323eSThomas Huth dev->vdev.device_type = qpci_config_readw(dev->pdev, PCI_SUBSYSTEM_ID); 395*1cf4323eSThomas Huth dev->bar_idx = 0; 396*1cf4323eSThomas Huth dev->vdev.bus = &qvirtio_pci_legacy; 397*1cf4323eSThomas Huth dev->msix_ops = &qvirtio_pci_msix_ops_legacy; 398*1cf4323eSThomas Huth dev->vdev.big_endian = qtest_big_endian(dev->pdev->bus->qts); 399*1cf4323eSThomas Huth } 400*1cf4323eSThomas Huth 401*1cf4323eSThomas Huth static void qvirtio_pci_init_from_pcidev(QVirtioPCIDevice *dev, QPCIDevice *pci_dev) 402*1cf4323eSThomas Huth { 403*1cf4323eSThomas Huth dev->pdev = pci_dev; 404*1cf4323eSThomas Huth dev->config_msix_entry = -1; 405*1cf4323eSThomas Huth 406*1cf4323eSThomas Huth if (!qvirtio_pci_init_virtio_1(dev)) { 407*1cf4323eSThomas Huth qvirtio_pci_init_legacy(dev); 408*1cf4323eSThomas Huth } 409*1cf4323eSThomas Huth 410*1cf4323eSThomas Huth /* each virtio-xxx-pci device should override at least this function */ 411*1cf4323eSThomas Huth dev->obj.get_driver = NULL; 412*1cf4323eSThomas Huth dev->obj.start_hw = qvirtio_pci_start_hw; 413*1cf4323eSThomas Huth dev->obj.destructor = qvirtio_pci_destructor; 414*1cf4323eSThomas Huth } 415*1cf4323eSThomas Huth 416*1cf4323eSThomas Huth void virtio_pci_init(QVirtioPCIDevice *dev, QPCIBus *bus, QPCIAddress * addr) 417*1cf4323eSThomas Huth { 418*1cf4323eSThomas Huth QPCIDevice *pci_dev = qpci_device_find(bus, addr->devfn); 419*1cf4323eSThomas Huth g_assert_nonnull(pci_dev); 420*1cf4323eSThomas Huth qvirtio_pci_init_from_pcidev(dev, pci_dev); 421*1cf4323eSThomas Huth } 422*1cf4323eSThomas Huth 423*1cf4323eSThomas Huth QVirtioPCIDevice *virtio_pci_new(QPCIBus *bus, QPCIAddress * addr) 424*1cf4323eSThomas Huth { 425*1cf4323eSThomas Huth QVirtioPCIDevice *dev; 426*1cf4323eSThomas Huth QPCIDevice *pci_dev = qpci_device_find(bus, addr->devfn); 427*1cf4323eSThomas Huth if (!pci_dev) { 428*1cf4323eSThomas Huth return NULL; 429*1cf4323eSThomas Huth } 430*1cf4323eSThomas Huth 431*1cf4323eSThomas Huth dev = g_new0(QVirtioPCIDevice, 1); 432*1cf4323eSThomas Huth qvirtio_pci_init_from_pcidev(dev, pci_dev); 433*1cf4323eSThomas Huth dev->obj.free = g_free; 434*1cf4323eSThomas Huth return dev; 435*1cf4323eSThomas Huth } 436