1*d08f68b8SStefan Hajnoczi /* 2*d08f68b8SStefan Hajnoczi * libqos VIRTIO 1.0 PCI driver 3*d08f68b8SStefan Hajnoczi * 4*d08f68b8SStefan Hajnoczi * Copyright (c) 2019 Red Hat, Inc 5*d08f68b8SStefan Hajnoczi * 6*d08f68b8SStefan Hajnoczi * This work is licensed under the terms of the GNU GPL, version 2 or later. 7*d08f68b8SStefan Hajnoczi * See the COPYING file in the top-level directory. 8*d08f68b8SStefan Hajnoczi */ 9*d08f68b8SStefan Hajnoczi 10*d08f68b8SStefan Hajnoczi #include "qemu/osdep.h" 11*d08f68b8SStefan Hajnoczi #include "standard-headers/linux/pci_regs.h" 12*d08f68b8SStefan Hajnoczi #include "standard-headers/linux/virtio_pci.h" 13*d08f68b8SStefan Hajnoczi #include "standard-headers/linux/virtio_config.h" 14*d08f68b8SStefan Hajnoczi #include "virtio-pci-modern.h" 15*d08f68b8SStefan Hajnoczi 16*d08f68b8SStefan Hajnoczi static uint8_t config_readb(QVirtioDevice *d, uint64_t addr) 17*d08f68b8SStefan Hajnoczi { 18*d08f68b8SStefan Hajnoczi QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); 19*d08f68b8SStefan Hajnoczi return qpci_io_readb(dev->pdev, dev->bar, dev->device_cfg_offset + addr); 20*d08f68b8SStefan Hajnoczi } 21*d08f68b8SStefan Hajnoczi 22*d08f68b8SStefan Hajnoczi static uint16_t config_readw(QVirtioDevice *d, uint64_t addr) 23*d08f68b8SStefan Hajnoczi { 24*d08f68b8SStefan Hajnoczi QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); 25*d08f68b8SStefan Hajnoczi return qpci_io_readw(dev->pdev, dev->bar, dev->device_cfg_offset + addr); 26*d08f68b8SStefan Hajnoczi } 27*d08f68b8SStefan Hajnoczi 28*d08f68b8SStefan Hajnoczi static uint32_t config_readl(QVirtioDevice *d, uint64_t addr) 29*d08f68b8SStefan Hajnoczi { 30*d08f68b8SStefan Hajnoczi QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); 31*d08f68b8SStefan Hajnoczi return qpci_io_readl(dev->pdev, dev->bar, dev->device_cfg_offset + addr); 32*d08f68b8SStefan Hajnoczi } 33*d08f68b8SStefan Hajnoczi 34*d08f68b8SStefan Hajnoczi static uint64_t config_readq(QVirtioDevice *d, uint64_t addr) 35*d08f68b8SStefan Hajnoczi { 36*d08f68b8SStefan Hajnoczi QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); 37*d08f68b8SStefan Hajnoczi return qpci_io_readq(dev->pdev, dev->bar, dev->device_cfg_offset + addr); 38*d08f68b8SStefan Hajnoczi } 39*d08f68b8SStefan Hajnoczi 40*d08f68b8SStefan Hajnoczi static uint64_t get_features(QVirtioDevice *d) 41*d08f68b8SStefan Hajnoczi { 42*d08f68b8SStefan Hajnoczi QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); 43*d08f68b8SStefan Hajnoczi uint64_t lo, hi; 44*d08f68b8SStefan Hajnoczi 45*d08f68b8SStefan Hajnoczi qpci_io_writel(dev->pdev, dev->bar, dev->common_cfg_offset + 46*d08f68b8SStefan Hajnoczi offsetof(struct virtio_pci_common_cfg, 47*d08f68b8SStefan Hajnoczi device_feature_select), 48*d08f68b8SStefan Hajnoczi 0); 49*d08f68b8SStefan Hajnoczi lo = qpci_io_readl(dev->pdev, dev->bar, dev->common_cfg_offset + 50*d08f68b8SStefan Hajnoczi offsetof(struct virtio_pci_common_cfg, device_feature)); 51*d08f68b8SStefan Hajnoczi 52*d08f68b8SStefan Hajnoczi qpci_io_writel(dev->pdev, dev->bar, dev->common_cfg_offset + 53*d08f68b8SStefan Hajnoczi offsetof(struct virtio_pci_common_cfg, 54*d08f68b8SStefan Hajnoczi device_feature_select), 55*d08f68b8SStefan Hajnoczi 1); 56*d08f68b8SStefan Hajnoczi hi = qpci_io_readl(dev->pdev, dev->bar, dev->common_cfg_offset + 57*d08f68b8SStefan Hajnoczi offsetof(struct virtio_pci_common_cfg, device_feature)); 58*d08f68b8SStefan Hajnoczi 59*d08f68b8SStefan Hajnoczi return (hi << 32) | lo; 60*d08f68b8SStefan Hajnoczi } 61*d08f68b8SStefan Hajnoczi 62*d08f68b8SStefan Hajnoczi static void set_features(QVirtioDevice *d, uint64_t features) 63*d08f68b8SStefan Hajnoczi { 64*d08f68b8SStefan Hajnoczi QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); 65*d08f68b8SStefan Hajnoczi 66*d08f68b8SStefan Hajnoczi /* Drivers must enable VIRTIO 1.0 or else use the Legacy interface */ 67*d08f68b8SStefan Hajnoczi g_assert_cmphex(features & (1ull << VIRTIO_F_VERSION_1), !=, 0); 68*d08f68b8SStefan Hajnoczi 69*d08f68b8SStefan Hajnoczi qpci_io_writel(dev->pdev, dev->bar, dev->common_cfg_offset + 70*d08f68b8SStefan Hajnoczi offsetof(struct virtio_pci_common_cfg, 71*d08f68b8SStefan Hajnoczi guest_feature_select), 72*d08f68b8SStefan Hajnoczi 0); 73*d08f68b8SStefan Hajnoczi qpci_io_writel(dev->pdev, dev->bar, dev->common_cfg_offset + 74*d08f68b8SStefan Hajnoczi offsetof(struct virtio_pci_common_cfg, 75*d08f68b8SStefan Hajnoczi guest_feature), 76*d08f68b8SStefan Hajnoczi features); 77*d08f68b8SStefan Hajnoczi qpci_io_writel(dev->pdev, dev->bar, dev->common_cfg_offset + 78*d08f68b8SStefan Hajnoczi offsetof(struct virtio_pci_common_cfg, 79*d08f68b8SStefan Hajnoczi guest_feature_select), 80*d08f68b8SStefan Hajnoczi 1); 81*d08f68b8SStefan Hajnoczi qpci_io_writel(dev->pdev, dev->bar, dev->common_cfg_offset + 82*d08f68b8SStefan Hajnoczi offsetof(struct virtio_pci_common_cfg, 83*d08f68b8SStefan Hajnoczi guest_feature), 84*d08f68b8SStefan Hajnoczi features >> 32); 85*d08f68b8SStefan Hajnoczi } 86*d08f68b8SStefan Hajnoczi 87*d08f68b8SStefan Hajnoczi static uint64_t get_guest_features(QVirtioDevice *d) 88*d08f68b8SStefan Hajnoczi { 89*d08f68b8SStefan Hajnoczi QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); 90*d08f68b8SStefan Hajnoczi uint64_t lo, hi; 91*d08f68b8SStefan Hajnoczi 92*d08f68b8SStefan Hajnoczi qpci_io_writel(dev->pdev, dev->bar, dev->common_cfg_offset + 93*d08f68b8SStefan Hajnoczi offsetof(struct virtio_pci_common_cfg, 94*d08f68b8SStefan Hajnoczi guest_feature_select), 95*d08f68b8SStefan Hajnoczi 0); 96*d08f68b8SStefan Hajnoczi lo = qpci_io_readl(dev->pdev, dev->bar, dev->common_cfg_offset + 97*d08f68b8SStefan Hajnoczi offsetof(struct virtio_pci_common_cfg, guest_feature)); 98*d08f68b8SStefan Hajnoczi 99*d08f68b8SStefan Hajnoczi qpci_io_writel(dev->pdev, dev->bar, dev->common_cfg_offset + 100*d08f68b8SStefan Hajnoczi offsetof(struct virtio_pci_common_cfg, 101*d08f68b8SStefan Hajnoczi guest_feature_select), 102*d08f68b8SStefan Hajnoczi 1); 103*d08f68b8SStefan Hajnoczi hi = qpci_io_readl(dev->pdev, dev->bar, dev->common_cfg_offset + 104*d08f68b8SStefan Hajnoczi offsetof(struct virtio_pci_common_cfg, guest_feature)); 105*d08f68b8SStefan Hajnoczi 106*d08f68b8SStefan Hajnoczi return (hi << 32) | lo; 107*d08f68b8SStefan Hajnoczi } 108*d08f68b8SStefan Hajnoczi 109*d08f68b8SStefan Hajnoczi static uint8_t get_status(QVirtioDevice *d) 110*d08f68b8SStefan Hajnoczi { 111*d08f68b8SStefan Hajnoczi QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); 112*d08f68b8SStefan Hajnoczi 113*d08f68b8SStefan Hajnoczi return qpci_io_readb(dev->pdev, dev->bar, dev->common_cfg_offset + 114*d08f68b8SStefan Hajnoczi offsetof(struct virtio_pci_common_cfg, 115*d08f68b8SStefan Hajnoczi device_status)); 116*d08f68b8SStefan Hajnoczi } 117*d08f68b8SStefan Hajnoczi 118*d08f68b8SStefan Hajnoczi static void set_status(QVirtioDevice *d, uint8_t status) 119*d08f68b8SStefan Hajnoczi { 120*d08f68b8SStefan Hajnoczi QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); 121*d08f68b8SStefan Hajnoczi 122*d08f68b8SStefan Hajnoczi return qpci_io_writeb(dev->pdev, dev->bar, dev->common_cfg_offset + 123*d08f68b8SStefan Hajnoczi offsetof(struct virtio_pci_common_cfg, 124*d08f68b8SStefan Hajnoczi device_status), 125*d08f68b8SStefan Hajnoczi status); 126*d08f68b8SStefan Hajnoczi } 127*d08f68b8SStefan Hajnoczi 128*d08f68b8SStefan Hajnoczi static bool get_msix_status(QVirtioPCIDevice *dev, uint32_t msix_entry, 129*d08f68b8SStefan Hajnoczi uint32_t msix_addr, uint32_t msix_data) 130*d08f68b8SStefan Hajnoczi { 131*d08f68b8SStefan Hajnoczi uint32_t data; 132*d08f68b8SStefan Hajnoczi 133*d08f68b8SStefan Hajnoczi g_assert_cmpint(msix_entry, !=, -1); 134*d08f68b8SStefan Hajnoczi if (qpci_msix_masked(dev->pdev, msix_entry)) { 135*d08f68b8SStefan Hajnoczi /* No ISR checking should be done if masked, but read anyway */ 136*d08f68b8SStefan Hajnoczi return qpci_msix_pending(dev->pdev, msix_entry); 137*d08f68b8SStefan Hajnoczi } 138*d08f68b8SStefan Hajnoczi 139*d08f68b8SStefan Hajnoczi data = qtest_readl(dev->pdev->bus->qts, msix_addr); 140*d08f68b8SStefan Hajnoczi if (data == msix_data) { 141*d08f68b8SStefan Hajnoczi qtest_writel(dev->pdev->bus->qts, msix_addr, 0); 142*d08f68b8SStefan Hajnoczi return true; 143*d08f68b8SStefan Hajnoczi } else { 144*d08f68b8SStefan Hajnoczi return false; 145*d08f68b8SStefan Hajnoczi } 146*d08f68b8SStefan Hajnoczi } 147*d08f68b8SStefan Hajnoczi 148*d08f68b8SStefan Hajnoczi static bool get_queue_isr_status(QVirtioDevice *d, QVirtQueue *vq) 149*d08f68b8SStefan Hajnoczi { 150*d08f68b8SStefan Hajnoczi QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); 151*d08f68b8SStefan Hajnoczi 152*d08f68b8SStefan Hajnoczi if (dev->pdev->msix_enabled) { 153*d08f68b8SStefan Hajnoczi QVirtQueuePCI *vqpci = container_of(vq, QVirtQueuePCI, vq); 154*d08f68b8SStefan Hajnoczi 155*d08f68b8SStefan Hajnoczi return get_msix_status(dev, vqpci->msix_entry, vqpci->msix_addr, 156*d08f68b8SStefan Hajnoczi vqpci->msix_data); 157*d08f68b8SStefan Hajnoczi } 158*d08f68b8SStefan Hajnoczi 159*d08f68b8SStefan Hajnoczi return qpci_io_readb(dev->pdev, dev->bar, dev->isr_cfg_offset) & 1; 160*d08f68b8SStefan Hajnoczi } 161*d08f68b8SStefan Hajnoczi 162*d08f68b8SStefan Hajnoczi static bool get_config_isr_status(QVirtioDevice *d) 163*d08f68b8SStefan Hajnoczi { 164*d08f68b8SStefan Hajnoczi QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); 165*d08f68b8SStefan Hajnoczi 166*d08f68b8SStefan Hajnoczi if (dev->pdev->msix_enabled) { 167*d08f68b8SStefan Hajnoczi return get_msix_status(dev, dev->config_msix_entry, 168*d08f68b8SStefan Hajnoczi dev->config_msix_addr, dev->config_msix_data); 169*d08f68b8SStefan Hajnoczi } 170*d08f68b8SStefan Hajnoczi 171*d08f68b8SStefan Hajnoczi return qpci_io_readb(dev->pdev, dev->bar, dev->isr_cfg_offset) & 2; 172*d08f68b8SStefan Hajnoczi } 173*d08f68b8SStefan Hajnoczi 174*d08f68b8SStefan Hajnoczi static void wait_config_isr_status(QVirtioDevice *d, gint64 timeout_us) 175*d08f68b8SStefan Hajnoczi { 176*d08f68b8SStefan Hajnoczi QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); 177*d08f68b8SStefan Hajnoczi gint64 start_time = g_get_monotonic_time(); 178*d08f68b8SStefan Hajnoczi 179*d08f68b8SStefan Hajnoczi do { 180*d08f68b8SStefan Hajnoczi g_assert(g_get_monotonic_time() - start_time <= timeout_us); 181*d08f68b8SStefan Hajnoczi qtest_clock_step(dev->pdev->bus->qts, 100); 182*d08f68b8SStefan Hajnoczi } while (!get_config_isr_status(d)); 183*d08f68b8SStefan Hajnoczi } 184*d08f68b8SStefan Hajnoczi 185*d08f68b8SStefan Hajnoczi static void queue_select(QVirtioDevice *d, uint16_t index) 186*d08f68b8SStefan Hajnoczi { 187*d08f68b8SStefan Hajnoczi QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); 188*d08f68b8SStefan Hajnoczi 189*d08f68b8SStefan Hajnoczi qpci_io_writew(dev->pdev, dev->bar, dev->common_cfg_offset + 190*d08f68b8SStefan Hajnoczi offsetof(struct virtio_pci_common_cfg, queue_select), 191*d08f68b8SStefan Hajnoczi index); 192*d08f68b8SStefan Hajnoczi } 193*d08f68b8SStefan Hajnoczi 194*d08f68b8SStefan Hajnoczi static uint16_t get_queue_size(QVirtioDevice *d) 195*d08f68b8SStefan Hajnoczi { 196*d08f68b8SStefan Hajnoczi QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); 197*d08f68b8SStefan Hajnoczi 198*d08f68b8SStefan Hajnoczi return qpci_io_readw(dev->pdev, dev->bar, dev->common_cfg_offset + 199*d08f68b8SStefan Hajnoczi offsetof(struct virtio_pci_common_cfg, queue_size)); 200*d08f68b8SStefan Hajnoczi } 201*d08f68b8SStefan Hajnoczi 202*d08f68b8SStefan Hajnoczi static void set_queue_address(QVirtioDevice *d, QVirtQueue *vq) 203*d08f68b8SStefan Hajnoczi { 204*d08f68b8SStefan Hajnoczi QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); 205*d08f68b8SStefan Hajnoczi 206*d08f68b8SStefan Hajnoczi qpci_io_writel(dev->pdev, dev->bar, dev->common_cfg_offset + 207*d08f68b8SStefan Hajnoczi offsetof(struct virtio_pci_common_cfg, queue_desc_lo), 208*d08f68b8SStefan Hajnoczi vq->desc); 209*d08f68b8SStefan Hajnoczi qpci_io_writel(dev->pdev, dev->bar, dev->common_cfg_offset + 210*d08f68b8SStefan Hajnoczi offsetof(struct virtio_pci_common_cfg, queue_desc_hi), 211*d08f68b8SStefan Hajnoczi vq->desc >> 32); 212*d08f68b8SStefan Hajnoczi 213*d08f68b8SStefan Hajnoczi qpci_io_writel(dev->pdev, dev->bar, dev->common_cfg_offset + 214*d08f68b8SStefan Hajnoczi offsetof(struct virtio_pci_common_cfg, queue_avail_lo), 215*d08f68b8SStefan Hajnoczi vq->avail); 216*d08f68b8SStefan Hajnoczi qpci_io_writel(dev->pdev, dev->bar, dev->common_cfg_offset + 217*d08f68b8SStefan Hajnoczi offsetof(struct virtio_pci_common_cfg, queue_avail_hi), 218*d08f68b8SStefan Hajnoczi vq->avail >> 32); 219*d08f68b8SStefan Hajnoczi 220*d08f68b8SStefan Hajnoczi qpci_io_writel(dev->pdev, dev->bar, dev->common_cfg_offset + 221*d08f68b8SStefan Hajnoczi offsetof(struct virtio_pci_common_cfg, queue_used_lo), 222*d08f68b8SStefan Hajnoczi vq->used); 223*d08f68b8SStefan Hajnoczi qpci_io_writel(dev->pdev, dev->bar, dev->common_cfg_offset + 224*d08f68b8SStefan Hajnoczi offsetof(struct virtio_pci_common_cfg, queue_used_hi), 225*d08f68b8SStefan Hajnoczi vq->used >> 32); 226*d08f68b8SStefan Hajnoczi } 227*d08f68b8SStefan Hajnoczi 228*d08f68b8SStefan Hajnoczi static QVirtQueue *virtqueue_setup(QVirtioDevice *d, QGuestAllocator *alloc, 229*d08f68b8SStefan Hajnoczi uint16_t index) 230*d08f68b8SStefan Hajnoczi { 231*d08f68b8SStefan Hajnoczi QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); 232*d08f68b8SStefan Hajnoczi QVirtQueue *vq; 233*d08f68b8SStefan Hajnoczi QVirtQueuePCI *vqpci; 234*d08f68b8SStefan Hajnoczi uint16_t notify_off; 235*d08f68b8SStefan Hajnoczi 236*d08f68b8SStefan Hajnoczi vq = qvirtio_pci_virtqueue_setup_common(d, alloc, index); 237*d08f68b8SStefan Hajnoczi vqpci = container_of(vq, QVirtQueuePCI, vq); 238*d08f68b8SStefan Hajnoczi 239*d08f68b8SStefan Hajnoczi notify_off = qpci_io_readw(dev->pdev, dev->bar, dev->common_cfg_offset + 240*d08f68b8SStefan Hajnoczi offsetof(struct virtio_pci_common_cfg, 241*d08f68b8SStefan Hajnoczi queue_notify_off)); 242*d08f68b8SStefan Hajnoczi 243*d08f68b8SStefan Hajnoczi vqpci->notify_offset = dev->notify_cfg_offset + 244*d08f68b8SStefan Hajnoczi notify_off * dev->notify_off_multiplier; 245*d08f68b8SStefan Hajnoczi 246*d08f68b8SStefan Hajnoczi qpci_io_writew(dev->pdev, dev->bar, dev->common_cfg_offset + 247*d08f68b8SStefan Hajnoczi offsetof(struct virtio_pci_common_cfg, queue_enable), 1); 248*d08f68b8SStefan Hajnoczi 249*d08f68b8SStefan Hajnoczi return vq; 250*d08f68b8SStefan Hajnoczi } 251*d08f68b8SStefan Hajnoczi 252*d08f68b8SStefan Hajnoczi static void virtqueue_kick(QVirtioDevice *d, QVirtQueue *vq) 253*d08f68b8SStefan Hajnoczi { 254*d08f68b8SStefan Hajnoczi QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); 255*d08f68b8SStefan Hajnoczi QVirtQueuePCI *vqpci = container_of(vq, QVirtQueuePCI, vq); 256*d08f68b8SStefan Hajnoczi 257*d08f68b8SStefan Hajnoczi qpci_io_writew(dev->pdev, dev->bar, vqpci->notify_offset, vq->index); 258*d08f68b8SStefan Hajnoczi } 259*d08f68b8SStefan Hajnoczi 260*d08f68b8SStefan Hajnoczi static const QVirtioBus qvirtio_pci_virtio_1 = { 261*d08f68b8SStefan Hajnoczi .config_readb = config_readb, 262*d08f68b8SStefan Hajnoczi .config_readw = config_readw, 263*d08f68b8SStefan Hajnoczi .config_readl = config_readl, 264*d08f68b8SStefan Hajnoczi .config_readq = config_readq, 265*d08f68b8SStefan Hajnoczi .get_features = get_features, 266*d08f68b8SStefan Hajnoczi .set_features = set_features, 267*d08f68b8SStefan Hajnoczi .get_guest_features = get_guest_features, 268*d08f68b8SStefan Hajnoczi .get_status = get_status, 269*d08f68b8SStefan Hajnoczi .set_status = set_status, 270*d08f68b8SStefan Hajnoczi .get_queue_isr_status = get_queue_isr_status, 271*d08f68b8SStefan Hajnoczi .wait_config_isr_status = wait_config_isr_status, 272*d08f68b8SStefan Hajnoczi .queue_select = queue_select, 273*d08f68b8SStefan Hajnoczi .get_queue_size = get_queue_size, 274*d08f68b8SStefan Hajnoczi .set_queue_address = set_queue_address, 275*d08f68b8SStefan Hajnoczi .virtqueue_setup = virtqueue_setup, 276*d08f68b8SStefan Hajnoczi .virtqueue_cleanup = qvirtio_pci_virtqueue_cleanup_common, 277*d08f68b8SStefan Hajnoczi .virtqueue_kick = virtqueue_kick, 278*d08f68b8SStefan Hajnoczi }; 279*d08f68b8SStefan Hajnoczi 280*d08f68b8SStefan Hajnoczi static void set_config_vector(QVirtioPCIDevice *d, uint16_t entry) 281*d08f68b8SStefan Hajnoczi { 282*d08f68b8SStefan Hajnoczi uint16_t vector; 283*d08f68b8SStefan Hajnoczi 284*d08f68b8SStefan Hajnoczi qpci_io_writew(d->pdev, d->bar, d->common_cfg_offset + 285*d08f68b8SStefan Hajnoczi offsetof(struct virtio_pci_common_cfg, msix_config), entry); 286*d08f68b8SStefan Hajnoczi vector = qpci_io_readw(d->pdev, d->bar, d->common_cfg_offset + 287*d08f68b8SStefan Hajnoczi offsetof(struct virtio_pci_common_cfg, 288*d08f68b8SStefan Hajnoczi msix_config)); 289*d08f68b8SStefan Hajnoczi g_assert_cmphex(vector, !=, VIRTIO_MSI_NO_VECTOR); 290*d08f68b8SStefan Hajnoczi } 291*d08f68b8SStefan Hajnoczi 292*d08f68b8SStefan Hajnoczi static void set_queue_vector(QVirtioPCIDevice *d, uint16_t vq_idx, 293*d08f68b8SStefan Hajnoczi uint16_t entry) 294*d08f68b8SStefan Hajnoczi { 295*d08f68b8SStefan Hajnoczi uint16_t vector; 296*d08f68b8SStefan Hajnoczi 297*d08f68b8SStefan Hajnoczi queue_select(&d->vdev, vq_idx); 298*d08f68b8SStefan Hajnoczi qpci_io_writew(d->pdev, d->bar, d->common_cfg_offset + 299*d08f68b8SStefan Hajnoczi offsetof(struct virtio_pci_common_cfg, queue_msix_vector), 300*d08f68b8SStefan Hajnoczi entry); 301*d08f68b8SStefan Hajnoczi vector = qpci_io_readw(d->pdev, d->bar, d->common_cfg_offset + 302*d08f68b8SStefan Hajnoczi offsetof(struct virtio_pci_common_cfg, 303*d08f68b8SStefan Hajnoczi queue_msix_vector)); 304*d08f68b8SStefan Hajnoczi g_assert_cmphex(vector, !=, VIRTIO_MSI_NO_VECTOR); 305*d08f68b8SStefan Hajnoczi } 306*d08f68b8SStefan Hajnoczi 307*d08f68b8SStefan Hajnoczi static const QVirtioPCIMSIXOps qvirtio_pci_msix_ops_virtio_1 = { 308*d08f68b8SStefan Hajnoczi .set_config_vector = set_config_vector, 309*d08f68b8SStefan Hajnoczi .set_queue_vector = set_queue_vector, 310*d08f68b8SStefan Hajnoczi }; 311*d08f68b8SStefan Hajnoczi 312*d08f68b8SStefan Hajnoczi static bool probe_device_type(QVirtioPCIDevice *dev) 313*d08f68b8SStefan Hajnoczi { 314*d08f68b8SStefan Hajnoczi uint16_t vendor_id; 315*d08f68b8SStefan Hajnoczi uint16_t device_id; 316*d08f68b8SStefan Hajnoczi 317*d08f68b8SStefan Hajnoczi /* "Drivers MUST match devices with the PCI Vendor ID 0x1AF4" */ 318*d08f68b8SStefan Hajnoczi vendor_id = qpci_config_readw(dev->pdev, PCI_VENDOR_ID); 319*d08f68b8SStefan Hajnoczi if (vendor_id != 0x1af4) { 320*d08f68b8SStefan Hajnoczi return false; 321*d08f68b8SStefan Hajnoczi } 322*d08f68b8SStefan Hajnoczi 323*d08f68b8SStefan Hajnoczi /* 324*d08f68b8SStefan Hajnoczi * "Any PCI device with ... PCI Device ID 0x1000 through 0x107F inclusive 325*d08f68b8SStefan Hajnoczi * is a virtio device" 326*d08f68b8SStefan Hajnoczi */ 327*d08f68b8SStefan Hajnoczi device_id = qpci_config_readw(dev->pdev, PCI_DEVICE_ID); 328*d08f68b8SStefan Hajnoczi if (device_id < 0x1000 || device_id > 0x107f) { 329*d08f68b8SStefan Hajnoczi return false; 330*d08f68b8SStefan Hajnoczi } 331*d08f68b8SStefan Hajnoczi 332*d08f68b8SStefan Hajnoczi /* 333*d08f68b8SStefan Hajnoczi * "Devices MAY utilize a Transitional PCI Device ID range, 0x1000 to 334*d08f68b8SStefan Hajnoczi * 0x103F depending on the device type" 335*d08f68b8SStefan Hajnoczi */ 336*d08f68b8SStefan Hajnoczi if (device_id < 0x1040) { 337*d08f68b8SStefan Hajnoczi /* 338*d08f68b8SStefan Hajnoczi * "Transitional devices MUST have the PCI Subsystem Device ID matching 339*d08f68b8SStefan Hajnoczi * the Virtio Device ID" 340*d08f68b8SStefan Hajnoczi */ 341*d08f68b8SStefan Hajnoczi dev->vdev.device_type = qpci_config_readw(dev->pdev, PCI_SUBSYSTEM_ID); 342*d08f68b8SStefan Hajnoczi } else { 343*d08f68b8SStefan Hajnoczi /* 344*d08f68b8SStefan Hajnoczi * "The PCI Device ID is calculated by adding 0x1040 to the Virtio 345*d08f68b8SStefan Hajnoczi * Device ID" 346*d08f68b8SStefan Hajnoczi */ 347*d08f68b8SStefan Hajnoczi dev->vdev.device_type = device_id - 0x1040; 348*d08f68b8SStefan Hajnoczi } 349*d08f68b8SStefan Hajnoczi 350*d08f68b8SStefan Hajnoczi return true; 351*d08f68b8SStefan Hajnoczi } 352*d08f68b8SStefan Hajnoczi 353*d08f68b8SStefan Hajnoczi /* Find the first VIRTIO 1.0 PCI structure for a given type */ 354*d08f68b8SStefan Hajnoczi static bool find_structure(QVirtioPCIDevice *dev, uint8_t cfg_type, 355*d08f68b8SStefan Hajnoczi uint8_t *bar, uint32_t *offset, uint32_t *length, 356*d08f68b8SStefan Hajnoczi uint8_t *cfg_addr) 357*d08f68b8SStefan Hajnoczi { 358*d08f68b8SStefan Hajnoczi uint8_t addr = 0; 359*d08f68b8SStefan Hajnoczi 360*d08f68b8SStefan Hajnoczi while ((addr = qpci_find_capability(dev->pdev, PCI_CAP_ID_VNDR, 361*d08f68b8SStefan Hajnoczi addr)) != 0) { 362*d08f68b8SStefan Hajnoczi uint8_t type; 363*d08f68b8SStefan Hajnoczi 364*d08f68b8SStefan Hajnoczi type = qpci_config_readb(dev->pdev, 365*d08f68b8SStefan Hajnoczi addr + offsetof(struct virtio_pci_cap, cfg_type)); 366*d08f68b8SStefan Hajnoczi if (type != cfg_type) { 367*d08f68b8SStefan Hajnoczi continue; 368*d08f68b8SStefan Hajnoczi } 369*d08f68b8SStefan Hajnoczi 370*d08f68b8SStefan Hajnoczi *bar = qpci_config_readb(dev->pdev, 371*d08f68b8SStefan Hajnoczi addr + offsetof(struct virtio_pci_cap, bar)); 372*d08f68b8SStefan Hajnoczi *offset = qpci_config_readl(dev->pdev, 373*d08f68b8SStefan Hajnoczi addr + offsetof(struct virtio_pci_cap, offset)); 374*d08f68b8SStefan Hajnoczi *length = qpci_config_readl(dev->pdev, 375*d08f68b8SStefan Hajnoczi addr + offsetof(struct virtio_pci_cap, length)); 376*d08f68b8SStefan Hajnoczi if (cfg_addr) { 377*d08f68b8SStefan Hajnoczi *cfg_addr = addr; 378*d08f68b8SStefan Hajnoczi } 379*d08f68b8SStefan Hajnoczi 380*d08f68b8SStefan Hajnoczi return true; 381*d08f68b8SStefan Hajnoczi } 382*d08f68b8SStefan Hajnoczi 383*d08f68b8SStefan Hajnoczi return false; 384*d08f68b8SStefan Hajnoczi } 385*d08f68b8SStefan Hajnoczi 386*d08f68b8SStefan Hajnoczi static bool probe_device_layout(QVirtioPCIDevice *dev) 387*d08f68b8SStefan Hajnoczi { 388*d08f68b8SStefan Hajnoczi uint8_t bar; 389*d08f68b8SStefan Hajnoczi uint8_t cfg_addr; 390*d08f68b8SStefan Hajnoczi uint32_t length; 391*d08f68b8SStefan Hajnoczi 392*d08f68b8SStefan Hajnoczi /* 393*d08f68b8SStefan Hajnoczi * Due to the qpci_iomap() API we only support devices that put all 394*d08f68b8SStefan Hajnoczi * structures in the same PCI BAR. Luckily this is true with QEMU. 395*d08f68b8SStefan Hajnoczi */ 396*d08f68b8SStefan Hajnoczi 397*d08f68b8SStefan Hajnoczi if (!find_structure(dev, VIRTIO_PCI_CAP_COMMON_CFG, &bar, 398*d08f68b8SStefan Hajnoczi &dev->common_cfg_offset, &length, NULL)) { 399*d08f68b8SStefan Hajnoczi return false; 400*d08f68b8SStefan Hajnoczi } 401*d08f68b8SStefan Hajnoczi dev->bar_idx = bar; 402*d08f68b8SStefan Hajnoczi 403*d08f68b8SStefan Hajnoczi if (!find_structure(dev, VIRTIO_PCI_CAP_NOTIFY_CFG, &bar, 404*d08f68b8SStefan Hajnoczi &dev->notify_cfg_offset, &length, &cfg_addr)) { 405*d08f68b8SStefan Hajnoczi return false; 406*d08f68b8SStefan Hajnoczi } 407*d08f68b8SStefan Hajnoczi g_assert_cmphex(bar, ==, dev->bar_idx); 408*d08f68b8SStefan Hajnoczi 409*d08f68b8SStefan Hajnoczi dev->notify_off_multiplier = qpci_config_readl(dev->pdev, 410*d08f68b8SStefan Hajnoczi cfg_addr + offsetof(struct virtio_pci_notify_cap, 411*d08f68b8SStefan Hajnoczi notify_off_multiplier)); 412*d08f68b8SStefan Hajnoczi 413*d08f68b8SStefan Hajnoczi if (!find_structure(dev, VIRTIO_PCI_CAP_ISR_CFG, &bar, 414*d08f68b8SStefan Hajnoczi &dev->isr_cfg_offset, &length, NULL)) { 415*d08f68b8SStefan Hajnoczi return false; 416*d08f68b8SStefan Hajnoczi } 417*d08f68b8SStefan Hajnoczi g_assert_cmphex(bar, ==, dev->bar_idx); 418*d08f68b8SStefan Hajnoczi 419*d08f68b8SStefan Hajnoczi if (!find_structure(dev, VIRTIO_PCI_CAP_DEVICE_CFG, &bar, 420*d08f68b8SStefan Hajnoczi &dev->device_cfg_offset, &length, NULL)) { 421*d08f68b8SStefan Hajnoczi return false; 422*d08f68b8SStefan Hajnoczi } 423*d08f68b8SStefan Hajnoczi g_assert_cmphex(bar, ==, dev->bar_idx); 424*d08f68b8SStefan Hajnoczi 425*d08f68b8SStefan Hajnoczi return true; 426*d08f68b8SStefan Hajnoczi } 427*d08f68b8SStefan Hajnoczi 428*d08f68b8SStefan Hajnoczi /* Probe a VIRTIO 1.0 device */ 429*d08f68b8SStefan Hajnoczi bool qvirtio_pci_init_virtio_1(QVirtioPCIDevice *dev) 430*d08f68b8SStefan Hajnoczi { 431*d08f68b8SStefan Hajnoczi if (!probe_device_type(dev)) { 432*d08f68b8SStefan Hajnoczi return false; 433*d08f68b8SStefan Hajnoczi } 434*d08f68b8SStefan Hajnoczi 435*d08f68b8SStefan Hajnoczi if (!probe_device_layout(dev)) { 436*d08f68b8SStefan Hajnoczi return false; 437*d08f68b8SStefan Hajnoczi } 438*d08f68b8SStefan Hajnoczi 439*d08f68b8SStefan Hajnoczi dev->vdev.bus = &qvirtio_pci_virtio_1; 440*d08f68b8SStefan Hajnoczi dev->msix_ops = &qvirtio_pci_msix_ops_virtio_1; 441*d08f68b8SStefan Hajnoczi dev->vdev.big_endian = false; 442*d08f68b8SStefan Hajnoczi return true; 443*d08f68b8SStefan Hajnoczi } 444