18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Virtio PCI driver - modern (virtio 1.0) device support 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * This module allows virtio devices to be used over a virtual PCI device. 68c2ecf20Sopenharmony_ci * This can be used with QEMU based VMMs like KVM or Xen. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Copyright IBM Corp. 2007 98c2ecf20Sopenharmony_ci * Copyright Red Hat, Inc. 2014 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * Authors: 128c2ecf20Sopenharmony_ci * Anthony Liguori <aliguori@us.ibm.com> 138c2ecf20Sopenharmony_ci * Rusty Russell <rusty@rustcorp.com.au> 148c2ecf20Sopenharmony_ci * Michael S. Tsirkin <mst@redhat.com> 158c2ecf20Sopenharmony_ci */ 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <linux/delay.h> 188c2ecf20Sopenharmony_ci#define VIRTIO_PCI_NO_LEGACY 198c2ecf20Sopenharmony_ci#define VIRTIO_RING_NO_LEGACY 208c2ecf20Sopenharmony_ci#include "virtio_pci_common.h" 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci/* 238c2ecf20Sopenharmony_ci * Type-safe wrappers for io accesses. 248c2ecf20Sopenharmony_ci * Use these to enforce at compile time the following spec requirement: 258c2ecf20Sopenharmony_ci * 268c2ecf20Sopenharmony_ci * The driver MUST access each field using the “natural” access 278c2ecf20Sopenharmony_ci * method, i.e. 32-bit accesses for 32-bit fields, 16-bit accesses 288c2ecf20Sopenharmony_ci * for 16-bit fields and 8-bit accesses for 8-bit fields. 298c2ecf20Sopenharmony_ci */ 308c2ecf20Sopenharmony_cistatic inline u8 vp_ioread8(const u8 __iomem *addr) 318c2ecf20Sopenharmony_ci{ 328c2ecf20Sopenharmony_ci return ioread8(addr); 338c2ecf20Sopenharmony_ci} 348c2ecf20Sopenharmony_cistatic inline u16 vp_ioread16 (const __le16 __iomem *addr) 358c2ecf20Sopenharmony_ci{ 368c2ecf20Sopenharmony_ci return ioread16(addr); 378c2ecf20Sopenharmony_ci} 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistatic inline u32 vp_ioread32(const __le32 __iomem *addr) 408c2ecf20Sopenharmony_ci{ 418c2ecf20Sopenharmony_ci return ioread32(addr); 428c2ecf20Sopenharmony_ci} 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic inline void vp_iowrite8(u8 value, u8 __iomem *addr) 458c2ecf20Sopenharmony_ci{ 468c2ecf20Sopenharmony_ci iowrite8(value, addr); 478c2ecf20Sopenharmony_ci} 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic inline void vp_iowrite16(u16 value, __le16 __iomem *addr) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci iowrite16(value, addr); 528c2ecf20Sopenharmony_ci} 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic inline void vp_iowrite32(u32 value, __le32 __iomem *addr) 558c2ecf20Sopenharmony_ci{ 568c2ecf20Sopenharmony_ci iowrite32(value, addr); 578c2ecf20Sopenharmony_ci} 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic void vp_iowrite64_twopart(u64 val, 608c2ecf20Sopenharmony_ci __le32 __iomem *lo, __le32 __iomem *hi) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci vp_iowrite32((u32)val, lo); 638c2ecf20Sopenharmony_ci vp_iowrite32(val >> 32, hi); 648c2ecf20Sopenharmony_ci} 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic void __iomem *map_capability(struct pci_dev *dev, int off, 678c2ecf20Sopenharmony_ci size_t minlen, 688c2ecf20Sopenharmony_ci u32 align, 698c2ecf20Sopenharmony_ci u32 start, u32 size, 708c2ecf20Sopenharmony_ci size_t *len) 718c2ecf20Sopenharmony_ci{ 728c2ecf20Sopenharmony_ci u8 bar; 738c2ecf20Sopenharmony_ci u32 offset, length; 748c2ecf20Sopenharmony_ci void __iomem *p; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci pci_read_config_byte(dev, off + offsetof(struct virtio_pci_cap, 778c2ecf20Sopenharmony_ci bar), 788c2ecf20Sopenharmony_ci &bar); 798c2ecf20Sopenharmony_ci pci_read_config_dword(dev, off + offsetof(struct virtio_pci_cap, offset), 808c2ecf20Sopenharmony_ci &offset); 818c2ecf20Sopenharmony_ci pci_read_config_dword(dev, off + offsetof(struct virtio_pci_cap, length), 828c2ecf20Sopenharmony_ci &length); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci if (length <= start) { 858c2ecf20Sopenharmony_ci dev_err(&dev->dev, 868c2ecf20Sopenharmony_ci "virtio_pci: bad capability len %u (>%u expected)\n", 878c2ecf20Sopenharmony_ci length, start); 888c2ecf20Sopenharmony_ci return NULL; 898c2ecf20Sopenharmony_ci } 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci if (length - start < minlen) { 928c2ecf20Sopenharmony_ci dev_err(&dev->dev, 938c2ecf20Sopenharmony_ci "virtio_pci: bad capability len %u (>=%zu expected)\n", 948c2ecf20Sopenharmony_ci length, minlen); 958c2ecf20Sopenharmony_ci return NULL; 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci length -= start; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci if (start + offset < offset) { 1018c2ecf20Sopenharmony_ci dev_err(&dev->dev, 1028c2ecf20Sopenharmony_ci "virtio_pci: map wrap-around %u+%u\n", 1038c2ecf20Sopenharmony_ci start, offset); 1048c2ecf20Sopenharmony_ci return NULL; 1058c2ecf20Sopenharmony_ci } 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci offset += start; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci if (offset & (align - 1)) { 1108c2ecf20Sopenharmony_ci dev_err(&dev->dev, 1118c2ecf20Sopenharmony_ci "virtio_pci: offset %u not aligned to %u\n", 1128c2ecf20Sopenharmony_ci offset, align); 1138c2ecf20Sopenharmony_ci return NULL; 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci if (length > size) 1178c2ecf20Sopenharmony_ci length = size; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci if (len) 1208c2ecf20Sopenharmony_ci *len = length; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci if (minlen + offset < minlen || 1238c2ecf20Sopenharmony_ci minlen + offset > pci_resource_len(dev, bar)) { 1248c2ecf20Sopenharmony_ci dev_err(&dev->dev, 1258c2ecf20Sopenharmony_ci "virtio_pci: map virtio %zu@%u " 1268c2ecf20Sopenharmony_ci "out of range on bar %i length %lu\n", 1278c2ecf20Sopenharmony_ci minlen, offset, 1288c2ecf20Sopenharmony_ci bar, (unsigned long)pci_resource_len(dev, bar)); 1298c2ecf20Sopenharmony_ci return NULL; 1308c2ecf20Sopenharmony_ci } 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci p = pci_iomap_range(dev, bar, offset, length); 1338c2ecf20Sopenharmony_ci if (!p) 1348c2ecf20Sopenharmony_ci dev_err(&dev->dev, 1358c2ecf20Sopenharmony_ci "virtio_pci: unable to map virtio %u@%u on bar %i\n", 1368c2ecf20Sopenharmony_ci length, offset, bar); 1378c2ecf20Sopenharmony_ci return p; 1388c2ecf20Sopenharmony_ci} 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci/* virtio config->get_features() implementation */ 1418c2ecf20Sopenharmony_cistatic u64 vp_get_features(struct virtio_device *vdev) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci struct virtio_pci_device *vp_dev = to_vp_device(vdev); 1448c2ecf20Sopenharmony_ci u64 features; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci vp_iowrite32(0, &vp_dev->common->device_feature_select); 1478c2ecf20Sopenharmony_ci features = vp_ioread32(&vp_dev->common->device_feature); 1488c2ecf20Sopenharmony_ci vp_iowrite32(1, &vp_dev->common->device_feature_select); 1498c2ecf20Sopenharmony_ci features |= ((u64)vp_ioread32(&vp_dev->common->device_feature) << 32); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci return features; 1528c2ecf20Sopenharmony_ci} 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_cistatic void vp_transport_features(struct virtio_device *vdev, u64 features) 1558c2ecf20Sopenharmony_ci{ 1568c2ecf20Sopenharmony_ci struct virtio_pci_device *vp_dev = to_vp_device(vdev); 1578c2ecf20Sopenharmony_ci struct pci_dev *pci_dev = vp_dev->pci_dev; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci if ((features & BIT_ULL(VIRTIO_F_SR_IOV)) && 1608c2ecf20Sopenharmony_ci pci_find_ext_capability(pci_dev, PCI_EXT_CAP_ID_SRIOV)) 1618c2ecf20Sopenharmony_ci __virtio_set_bit(vdev, VIRTIO_F_SR_IOV); 1628c2ecf20Sopenharmony_ci} 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci/* virtio config->finalize_features() implementation */ 1658c2ecf20Sopenharmony_cistatic int vp_finalize_features(struct virtio_device *vdev) 1668c2ecf20Sopenharmony_ci{ 1678c2ecf20Sopenharmony_ci struct virtio_pci_device *vp_dev = to_vp_device(vdev); 1688c2ecf20Sopenharmony_ci u64 features = vdev->features; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci /* Give virtio_ring a chance to accept features. */ 1718c2ecf20Sopenharmony_ci vring_transport_features(vdev); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci /* Give virtio_pci a chance to accept features. */ 1748c2ecf20Sopenharmony_ci vp_transport_features(vdev, features); 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci if (!__virtio_test_bit(vdev, VIRTIO_F_VERSION_1)) { 1778c2ecf20Sopenharmony_ci dev_err(&vdev->dev, "virtio: device uses modern interface " 1788c2ecf20Sopenharmony_ci "but does not have VIRTIO_F_VERSION_1\n"); 1798c2ecf20Sopenharmony_ci return -EINVAL; 1808c2ecf20Sopenharmony_ci } 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci vp_iowrite32(0, &vp_dev->common->guest_feature_select); 1838c2ecf20Sopenharmony_ci vp_iowrite32((u32)vdev->features, &vp_dev->common->guest_feature); 1848c2ecf20Sopenharmony_ci vp_iowrite32(1, &vp_dev->common->guest_feature_select); 1858c2ecf20Sopenharmony_ci vp_iowrite32(vdev->features >> 32, &vp_dev->common->guest_feature); 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci return 0; 1888c2ecf20Sopenharmony_ci} 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci/* virtio config->get() implementation */ 1918c2ecf20Sopenharmony_cistatic void vp_get(struct virtio_device *vdev, unsigned offset, 1928c2ecf20Sopenharmony_ci void *buf, unsigned len) 1938c2ecf20Sopenharmony_ci{ 1948c2ecf20Sopenharmony_ci struct virtio_pci_device *vp_dev = to_vp_device(vdev); 1958c2ecf20Sopenharmony_ci u8 b; 1968c2ecf20Sopenharmony_ci __le16 w; 1978c2ecf20Sopenharmony_ci __le32 l; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci BUG_ON(offset + len > vp_dev->device_len); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci switch (len) { 2028c2ecf20Sopenharmony_ci case 1: 2038c2ecf20Sopenharmony_ci b = ioread8(vp_dev->device + offset); 2048c2ecf20Sopenharmony_ci memcpy(buf, &b, sizeof b); 2058c2ecf20Sopenharmony_ci break; 2068c2ecf20Sopenharmony_ci case 2: 2078c2ecf20Sopenharmony_ci w = cpu_to_le16(ioread16(vp_dev->device + offset)); 2088c2ecf20Sopenharmony_ci memcpy(buf, &w, sizeof w); 2098c2ecf20Sopenharmony_ci break; 2108c2ecf20Sopenharmony_ci case 4: 2118c2ecf20Sopenharmony_ci l = cpu_to_le32(ioread32(vp_dev->device + offset)); 2128c2ecf20Sopenharmony_ci memcpy(buf, &l, sizeof l); 2138c2ecf20Sopenharmony_ci break; 2148c2ecf20Sopenharmony_ci case 8: 2158c2ecf20Sopenharmony_ci l = cpu_to_le32(ioread32(vp_dev->device + offset)); 2168c2ecf20Sopenharmony_ci memcpy(buf, &l, sizeof l); 2178c2ecf20Sopenharmony_ci l = cpu_to_le32(ioread32(vp_dev->device + offset + sizeof l)); 2188c2ecf20Sopenharmony_ci memcpy(buf + sizeof l, &l, sizeof l); 2198c2ecf20Sopenharmony_ci break; 2208c2ecf20Sopenharmony_ci default: 2218c2ecf20Sopenharmony_ci BUG(); 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ci} 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci/* the config->set() implementation. it's symmetric to the config->get() 2268c2ecf20Sopenharmony_ci * implementation */ 2278c2ecf20Sopenharmony_cistatic void vp_set(struct virtio_device *vdev, unsigned offset, 2288c2ecf20Sopenharmony_ci const void *buf, unsigned len) 2298c2ecf20Sopenharmony_ci{ 2308c2ecf20Sopenharmony_ci struct virtio_pci_device *vp_dev = to_vp_device(vdev); 2318c2ecf20Sopenharmony_ci u8 b; 2328c2ecf20Sopenharmony_ci __le16 w; 2338c2ecf20Sopenharmony_ci __le32 l; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci BUG_ON(offset + len > vp_dev->device_len); 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci switch (len) { 2388c2ecf20Sopenharmony_ci case 1: 2398c2ecf20Sopenharmony_ci memcpy(&b, buf, sizeof b); 2408c2ecf20Sopenharmony_ci iowrite8(b, vp_dev->device + offset); 2418c2ecf20Sopenharmony_ci break; 2428c2ecf20Sopenharmony_ci case 2: 2438c2ecf20Sopenharmony_ci memcpy(&w, buf, sizeof w); 2448c2ecf20Sopenharmony_ci iowrite16(le16_to_cpu(w), vp_dev->device + offset); 2458c2ecf20Sopenharmony_ci break; 2468c2ecf20Sopenharmony_ci case 4: 2478c2ecf20Sopenharmony_ci memcpy(&l, buf, sizeof l); 2488c2ecf20Sopenharmony_ci iowrite32(le32_to_cpu(l), vp_dev->device + offset); 2498c2ecf20Sopenharmony_ci break; 2508c2ecf20Sopenharmony_ci case 8: 2518c2ecf20Sopenharmony_ci memcpy(&l, buf, sizeof l); 2528c2ecf20Sopenharmony_ci iowrite32(le32_to_cpu(l), vp_dev->device + offset); 2538c2ecf20Sopenharmony_ci memcpy(&l, buf + sizeof l, sizeof l); 2548c2ecf20Sopenharmony_ci iowrite32(le32_to_cpu(l), vp_dev->device + offset + sizeof l); 2558c2ecf20Sopenharmony_ci break; 2568c2ecf20Sopenharmony_ci default: 2578c2ecf20Sopenharmony_ci BUG(); 2588c2ecf20Sopenharmony_ci } 2598c2ecf20Sopenharmony_ci} 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_cistatic u32 vp_generation(struct virtio_device *vdev) 2628c2ecf20Sopenharmony_ci{ 2638c2ecf20Sopenharmony_ci struct virtio_pci_device *vp_dev = to_vp_device(vdev); 2648c2ecf20Sopenharmony_ci return vp_ioread8(&vp_dev->common->config_generation); 2658c2ecf20Sopenharmony_ci} 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci/* config->{get,set}_status() implementations */ 2688c2ecf20Sopenharmony_cistatic u8 vp_get_status(struct virtio_device *vdev) 2698c2ecf20Sopenharmony_ci{ 2708c2ecf20Sopenharmony_ci struct virtio_pci_device *vp_dev = to_vp_device(vdev); 2718c2ecf20Sopenharmony_ci return vp_ioread8(&vp_dev->common->device_status); 2728c2ecf20Sopenharmony_ci} 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_cistatic void vp_set_status(struct virtio_device *vdev, u8 status) 2758c2ecf20Sopenharmony_ci{ 2768c2ecf20Sopenharmony_ci struct virtio_pci_device *vp_dev = to_vp_device(vdev); 2778c2ecf20Sopenharmony_ci /* We should never be setting status to 0. */ 2788c2ecf20Sopenharmony_ci BUG_ON(status == 0); 2798c2ecf20Sopenharmony_ci vp_iowrite8(status, &vp_dev->common->device_status); 2808c2ecf20Sopenharmony_ci} 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_cistatic void vp_reset(struct virtio_device *vdev) 2838c2ecf20Sopenharmony_ci{ 2848c2ecf20Sopenharmony_ci struct virtio_pci_device *vp_dev = to_vp_device(vdev); 2858c2ecf20Sopenharmony_ci /* 0 status means a reset. */ 2868c2ecf20Sopenharmony_ci vp_iowrite8(0, &vp_dev->common->device_status); 2878c2ecf20Sopenharmony_ci /* After writing 0 to device_status, the driver MUST wait for a read of 2888c2ecf20Sopenharmony_ci * device_status to return 0 before reinitializing the device. 2898c2ecf20Sopenharmony_ci * This will flush out the status write, and flush in device writes, 2908c2ecf20Sopenharmony_ci * including MSI-X interrupts, if any. 2918c2ecf20Sopenharmony_ci */ 2928c2ecf20Sopenharmony_ci while (vp_ioread8(&vp_dev->common->device_status)) 2938c2ecf20Sopenharmony_ci msleep(1); 2948c2ecf20Sopenharmony_ci /* Flush pending VQ/configuration callbacks. */ 2958c2ecf20Sopenharmony_ci vp_synchronize_vectors(vdev); 2968c2ecf20Sopenharmony_ci} 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_cistatic u16 vp_config_vector(struct virtio_pci_device *vp_dev, u16 vector) 2998c2ecf20Sopenharmony_ci{ 3008c2ecf20Sopenharmony_ci /* Setup the vector used for configuration events */ 3018c2ecf20Sopenharmony_ci vp_iowrite16(vector, &vp_dev->common->msix_config); 3028c2ecf20Sopenharmony_ci /* Verify we had enough resources to assign the vector */ 3038c2ecf20Sopenharmony_ci /* Will also flush the write out to device */ 3048c2ecf20Sopenharmony_ci return vp_ioread16(&vp_dev->common->msix_config); 3058c2ecf20Sopenharmony_ci} 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_cistatic struct virtqueue *setup_vq(struct virtio_pci_device *vp_dev, 3088c2ecf20Sopenharmony_ci struct virtio_pci_vq_info *info, 3098c2ecf20Sopenharmony_ci unsigned index, 3108c2ecf20Sopenharmony_ci void (*callback)(struct virtqueue *vq), 3118c2ecf20Sopenharmony_ci const char *name, 3128c2ecf20Sopenharmony_ci bool ctx, 3138c2ecf20Sopenharmony_ci u16 msix_vec) 3148c2ecf20Sopenharmony_ci{ 3158c2ecf20Sopenharmony_ci struct virtio_pci_common_cfg __iomem *cfg = vp_dev->common; 3168c2ecf20Sopenharmony_ci struct virtqueue *vq; 3178c2ecf20Sopenharmony_ci u16 num, off; 3188c2ecf20Sopenharmony_ci int err; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci if (index >= vp_ioread16(&cfg->num_queues)) 3218c2ecf20Sopenharmony_ci return ERR_PTR(-ENOENT); 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci /* Select the queue we're interested in */ 3248c2ecf20Sopenharmony_ci vp_iowrite16(index, &cfg->queue_select); 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci /* Check if queue is either not available or already active. */ 3278c2ecf20Sopenharmony_ci num = vp_ioread16(&cfg->queue_size); 3288c2ecf20Sopenharmony_ci if (!num || vp_ioread16(&cfg->queue_enable)) 3298c2ecf20Sopenharmony_ci return ERR_PTR(-ENOENT); 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci if (num & (num - 1)) { 3328c2ecf20Sopenharmony_ci dev_warn(&vp_dev->pci_dev->dev, "bad queue size %u", num); 3338c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 3348c2ecf20Sopenharmony_ci } 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci /* get offset of notification word for this vq */ 3378c2ecf20Sopenharmony_ci off = vp_ioread16(&cfg->queue_notify_off); 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci info->msix_vector = msix_vec; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci /* create the vring */ 3428c2ecf20Sopenharmony_ci vq = vring_create_virtqueue(index, num, 3438c2ecf20Sopenharmony_ci SMP_CACHE_BYTES, &vp_dev->vdev, 3448c2ecf20Sopenharmony_ci true, true, ctx, 3458c2ecf20Sopenharmony_ci vp_notify, callback, name); 3468c2ecf20Sopenharmony_ci if (!vq) 3478c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci /* activate the queue */ 3508c2ecf20Sopenharmony_ci vp_iowrite16(virtqueue_get_vring_size(vq), &cfg->queue_size); 3518c2ecf20Sopenharmony_ci vp_iowrite64_twopart(virtqueue_get_desc_addr(vq), 3528c2ecf20Sopenharmony_ci &cfg->queue_desc_lo, &cfg->queue_desc_hi); 3538c2ecf20Sopenharmony_ci vp_iowrite64_twopart(virtqueue_get_avail_addr(vq), 3548c2ecf20Sopenharmony_ci &cfg->queue_avail_lo, &cfg->queue_avail_hi); 3558c2ecf20Sopenharmony_ci vp_iowrite64_twopart(virtqueue_get_used_addr(vq), 3568c2ecf20Sopenharmony_ci &cfg->queue_used_lo, &cfg->queue_used_hi); 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci if (vp_dev->notify_base) { 3598c2ecf20Sopenharmony_ci /* offset should not wrap */ 3608c2ecf20Sopenharmony_ci if ((u64)off * vp_dev->notify_offset_multiplier + 2 3618c2ecf20Sopenharmony_ci > vp_dev->notify_len) { 3628c2ecf20Sopenharmony_ci dev_warn(&vp_dev->pci_dev->dev, 3638c2ecf20Sopenharmony_ci "bad notification offset %u (x %u) " 3648c2ecf20Sopenharmony_ci "for queue %u > %zd", 3658c2ecf20Sopenharmony_ci off, vp_dev->notify_offset_multiplier, 3668c2ecf20Sopenharmony_ci index, vp_dev->notify_len); 3678c2ecf20Sopenharmony_ci err = -EINVAL; 3688c2ecf20Sopenharmony_ci goto err_map_notify; 3698c2ecf20Sopenharmony_ci } 3708c2ecf20Sopenharmony_ci vq->priv = (void __force *)vp_dev->notify_base + 3718c2ecf20Sopenharmony_ci off * vp_dev->notify_offset_multiplier; 3728c2ecf20Sopenharmony_ci } else { 3738c2ecf20Sopenharmony_ci vq->priv = (void __force *)map_capability(vp_dev->pci_dev, 3748c2ecf20Sopenharmony_ci vp_dev->notify_map_cap, 2, 2, 3758c2ecf20Sopenharmony_ci off * vp_dev->notify_offset_multiplier, 2, 3768c2ecf20Sopenharmony_ci NULL); 3778c2ecf20Sopenharmony_ci } 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci if (!vq->priv) { 3808c2ecf20Sopenharmony_ci err = -ENOMEM; 3818c2ecf20Sopenharmony_ci goto err_map_notify; 3828c2ecf20Sopenharmony_ci } 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci if (msix_vec != VIRTIO_MSI_NO_VECTOR) { 3858c2ecf20Sopenharmony_ci vp_iowrite16(msix_vec, &cfg->queue_msix_vector); 3868c2ecf20Sopenharmony_ci msix_vec = vp_ioread16(&cfg->queue_msix_vector); 3878c2ecf20Sopenharmony_ci if (msix_vec == VIRTIO_MSI_NO_VECTOR) { 3888c2ecf20Sopenharmony_ci err = -EBUSY; 3898c2ecf20Sopenharmony_ci goto err_assign_vector; 3908c2ecf20Sopenharmony_ci } 3918c2ecf20Sopenharmony_ci } 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci return vq; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_cierr_assign_vector: 3968c2ecf20Sopenharmony_ci if (!vp_dev->notify_base) 3978c2ecf20Sopenharmony_ci pci_iounmap(vp_dev->pci_dev, (void __iomem __force *)vq->priv); 3988c2ecf20Sopenharmony_cierr_map_notify: 3998c2ecf20Sopenharmony_ci vring_del_virtqueue(vq); 4008c2ecf20Sopenharmony_ci return ERR_PTR(err); 4018c2ecf20Sopenharmony_ci} 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_cistatic int vp_modern_find_vqs(struct virtio_device *vdev, unsigned nvqs, 4048c2ecf20Sopenharmony_ci struct virtqueue *vqs[], 4058c2ecf20Sopenharmony_ci vq_callback_t *callbacks[], 4068c2ecf20Sopenharmony_ci const char * const names[], const bool *ctx, 4078c2ecf20Sopenharmony_ci struct irq_affinity *desc) 4088c2ecf20Sopenharmony_ci{ 4098c2ecf20Sopenharmony_ci struct virtio_pci_device *vp_dev = to_vp_device(vdev); 4108c2ecf20Sopenharmony_ci struct virtqueue *vq; 4118c2ecf20Sopenharmony_ci int rc = vp_find_vqs(vdev, nvqs, vqs, callbacks, names, ctx, desc); 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci if (rc) 4148c2ecf20Sopenharmony_ci return rc; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci /* Select and activate all queues. Has to be done last: once we do 4178c2ecf20Sopenharmony_ci * this, there's no way to go back except reset. 4188c2ecf20Sopenharmony_ci */ 4198c2ecf20Sopenharmony_ci list_for_each_entry(vq, &vdev->vqs, list) { 4208c2ecf20Sopenharmony_ci vp_iowrite16(vq->index, &vp_dev->common->queue_select); 4218c2ecf20Sopenharmony_ci vp_iowrite16(1, &vp_dev->common->queue_enable); 4228c2ecf20Sopenharmony_ci } 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci return 0; 4258c2ecf20Sopenharmony_ci} 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_cistatic void del_vq(struct virtio_pci_vq_info *info) 4288c2ecf20Sopenharmony_ci{ 4298c2ecf20Sopenharmony_ci struct virtqueue *vq = info->vq; 4308c2ecf20Sopenharmony_ci struct virtio_pci_device *vp_dev = to_vp_device(vq->vdev); 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci vp_iowrite16(vq->index, &vp_dev->common->queue_select); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci if (vp_dev->msix_enabled) { 4358c2ecf20Sopenharmony_ci vp_iowrite16(VIRTIO_MSI_NO_VECTOR, 4368c2ecf20Sopenharmony_ci &vp_dev->common->queue_msix_vector); 4378c2ecf20Sopenharmony_ci /* Flush the write out to device */ 4388c2ecf20Sopenharmony_ci vp_ioread16(&vp_dev->common->queue_msix_vector); 4398c2ecf20Sopenharmony_ci } 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci if (!vp_dev->notify_base) 4428c2ecf20Sopenharmony_ci pci_iounmap(vp_dev->pci_dev, (void __force __iomem *)vq->priv); 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci vring_del_virtqueue(vq); 4458c2ecf20Sopenharmony_ci} 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_cistatic int virtio_pci_find_shm_cap(struct pci_dev *dev, u8 required_id, 4488c2ecf20Sopenharmony_ci u8 *bar, u64 *offset, u64 *len) 4498c2ecf20Sopenharmony_ci{ 4508c2ecf20Sopenharmony_ci int pos; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci for (pos = pci_find_capability(dev, PCI_CAP_ID_VNDR); pos > 0; 4538c2ecf20Sopenharmony_ci pos = pci_find_next_capability(dev, pos, PCI_CAP_ID_VNDR)) { 4548c2ecf20Sopenharmony_ci u8 type, cap_len, id; 4558c2ecf20Sopenharmony_ci u32 tmp32; 4568c2ecf20Sopenharmony_ci u64 res_offset, res_length; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci pci_read_config_byte(dev, pos + offsetof(struct virtio_pci_cap, 4598c2ecf20Sopenharmony_ci cfg_type), &type); 4608c2ecf20Sopenharmony_ci if (type != VIRTIO_PCI_CAP_SHARED_MEMORY_CFG) 4618c2ecf20Sopenharmony_ci continue; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci pci_read_config_byte(dev, pos + offsetof(struct virtio_pci_cap, 4648c2ecf20Sopenharmony_ci cap_len), &cap_len); 4658c2ecf20Sopenharmony_ci if (cap_len != sizeof(struct virtio_pci_cap64)) { 4668c2ecf20Sopenharmony_ci dev_err(&dev->dev, "%s: shm cap with bad size offset:" 4678c2ecf20Sopenharmony_ci " %d size: %d\n", __func__, pos, cap_len); 4688c2ecf20Sopenharmony_ci continue; 4698c2ecf20Sopenharmony_ci } 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci pci_read_config_byte(dev, pos + offsetof(struct virtio_pci_cap, 4728c2ecf20Sopenharmony_ci id), &id); 4738c2ecf20Sopenharmony_ci if (id != required_id) 4748c2ecf20Sopenharmony_ci continue; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci /* Type, and ID match, looks good */ 4778c2ecf20Sopenharmony_ci pci_read_config_byte(dev, pos + offsetof(struct virtio_pci_cap, 4788c2ecf20Sopenharmony_ci bar), bar); 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci /* Read the lower 32bit of length and offset */ 4818c2ecf20Sopenharmony_ci pci_read_config_dword(dev, pos + offsetof(struct virtio_pci_cap, 4828c2ecf20Sopenharmony_ci offset), &tmp32); 4838c2ecf20Sopenharmony_ci res_offset = tmp32; 4848c2ecf20Sopenharmony_ci pci_read_config_dword(dev, pos + offsetof(struct virtio_pci_cap, 4858c2ecf20Sopenharmony_ci length), &tmp32); 4868c2ecf20Sopenharmony_ci res_length = tmp32; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci /* and now the top half */ 4898c2ecf20Sopenharmony_ci pci_read_config_dword(dev, 4908c2ecf20Sopenharmony_ci pos + offsetof(struct virtio_pci_cap64, 4918c2ecf20Sopenharmony_ci offset_hi), &tmp32); 4928c2ecf20Sopenharmony_ci res_offset |= ((u64)tmp32) << 32; 4938c2ecf20Sopenharmony_ci pci_read_config_dword(dev, 4948c2ecf20Sopenharmony_ci pos + offsetof(struct virtio_pci_cap64, 4958c2ecf20Sopenharmony_ci length_hi), &tmp32); 4968c2ecf20Sopenharmony_ci res_length |= ((u64)tmp32) << 32; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci *offset = res_offset; 4998c2ecf20Sopenharmony_ci *len = res_length; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci return pos; 5028c2ecf20Sopenharmony_ci } 5038c2ecf20Sopenharmony_ci return 0; 5048c2ecf20Sopenharmony_ci} 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_cistatic bool vp_get_shm_region(struct virtio_device *vdev, 5078c2ecf20Sopenharmony_ci struct virtio_shm_region *region, u8 id) 5088c2ecf20Sopenharmony_ci{ 5098c2ecf20Sopenharmony_ci struct virtio_pci_device *vp_dev = to_vp_device(vdev); 5108c2ecf20Sopenharmony_ci struct pci_dev *pci_dev = vp_dev->pci_dev; 5118c2ecf20Sopenharmony_ci u8 bar; 5128c2ecf20Sopenharmony_ci u64 offset, len; 5138c2ecf20Sopenharmony_ci phys_addr_t phys_addr; 5148c2ecf20Sopenharmony_ci size_t bar_len; 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci if (!virtio_pci_find_shm_cap(pci_dev, id, &bar, &offset, &len)) 5178c2ecf20Sopenharmony_ci return false; 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci phys_addr = pci_resource_start(pci_dev, bar); 5208c2ecf20Sopenharmony_ci bar_len = pci_resource_len(pci_dev, bar); 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci if ((offset + len) < offset) { 5238c2ecf20Sopenharmony_ci dev_err(&pci_dev->dev, "%s: cap offset+len overflow detected\n", 5248c2ecf20Sopenharmony_ci __func__); 5258c2ecf20Sopenharmony_ci return false; 5268c2ecf20Sopenharmony_ci } 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci if (offset + len > bar_len) { 5298c2ecf20Sopenharmony_ci dev_err(&pci_dev->dev, "%s: bar shorter than cap offset+len\n", 5308c2ecf20Sopenharmony_ci __func__); 5318c2ecf20Sopenharmony_ci return false; 5328c2ecf20Sopenharmony_ci } 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci region->len = len; 5358c2ecf20Sopenharmony_ci region->addr = (u64) phys_addr + offset; 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci return true; 5388c2ecf20Sopenharmony_ci} 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_cistatic const struct virtio_config_ops virtio_pci_config_nodev_ops = { 5418c2ecf20Sopenharmony_ci .get = NULL, 5428c2ecf20Sopenharmony_ci .set = NULL, 5438c2ecf20Sopenharmony_ci .generation = vp_generation, 5448c2ecf20Sopenharmony_ci .get_status = vp_get_status, 5458c2ecf20Sopenharmony_ci .set_status = vp_set_status, 5468c2ecf20Sopenharmony_ci .reset = vp_reset, 5478c2ecf20Sopenharmony_ci .find_vqs = vp_modern_find_vqs, 5488c2ecf20Sopenharmony_ci .del_vqs = vp_del_vqs, 5498c2ecf20Sopenharmony_ci .get_features = vp_get_features, 5508c2ecf20Sopenharmony_ci .finalize_features = vp_finalize_features, 5518c2ecf20Sopenharmony_ci .bus_name = vp_bus_name, 5528c2ecf20Sopenharmony_ci .set_vq_affinity = vp_set_vq_affinity, 5538c2ecf20Sopenharmony_ci .get_vq_affinity = vp_get_vq_affinity, 5548c2ecf20Sopenharmony_ci .get_shm_region = vp_get_shm_region, 5558c2ecf20Sopenharmony_ci}; 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_cistatic const struct virtio_config_ops virtio_pci_config_ops = { 5588c2ecf20Sopenharmony_ci .get = vp_get, 5598c2ecf20Sopenharmony_ci .set = vp_set, 5608c2ecf20Sopenharmony_ci .generation = vp_generation, 5618c2ecf20Sopenharmony_ci .get_status = vp_get_status, 5628c2ecf20Sopenharmony_ci .set_status = vp_set_status, 5638c2ecf20Sopenharmony_ci .reset = vp_reset, 5648c2ecf20Sopenharmony_ci .find_vqs = vp_modern_find_vqs, 5658c2ecf20Sopenharmony_ci .del_vqs = vp_del_vqs, 5668c2ecf20Sopenharmony_ci .get_features = vp_get_features, 5678c2ecf20Sopenharmony_ci .finalize_features = vp_finalize_features, 5688c2ecf20Sopenharmony_ci .bus_name = vp_bus_name, 5698c2ecf20Sopenharmony_ci .set_vq_affinity = vp_set_vq_affinity, 5708c2ecf20Sopenharmony_ci .get_vq_affinity = vp_get_vq_affinity, 5718c2ecf20Sopenharmony_ci .get_shm_region = vp_get_shm_region, 5728c2ecf20Sopenharmony_ci}; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci/** 5758c2ecf20Sopenharmony_ci * virtio_pci_find_capability - walk capabilities to find device info. 5768c2ecf20Sopenharmony_ci * @dev: the pci device 5778c2ecf20Sopenharmony_ci * @cfg_type: the VIRTIO_PCI_CAP_* value we seek 5788c2ecf20Sopenharmony_ci * @ioresource_types: IORESOURCE_MEM and/or IORESOURCE_IO. 5798c2ecf20Sopenharmony_ci * @bars: the bitmask of BARs 5808c2ecf20Sopenharmony_ci * 5818c2ecf20Sopenharmony_ci * Returns offset of the capability, or 0. 5828c2ecf20Sopenharmony_ci */ 5838c2ecf20Sopenharmony_cistatic inline int virtio_pci_find_capability(struct pci_dev *dev, u8 cfg_type, 5848c2ecf20Sopenharmony_ci u32 ioresource_types, int *bars) 5858c2ecf20Sopenharmony_ci{ 5868c2ecf20Sopenharmony_ci int pos; 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci for (pos = pci_find_capability(dev, PCI_CAP_ID_VNDR); 5898c2ecf20Sopenharmony_ci pos > 0; 5908c2ecf20Sopenharmony_ci pos = pci_find_next_capability(dev, pos, PCI_CAP_ID_VNDR)) { 5918c2ecf20Sopenharmony_ci u8 type, bar; 5928c2ecf20Sopenharmony_ci pci_read_config_byte(dev, pos + offsetof(struct virtio_pci_cap, 5938c2ecf20Sopenharmony_ci cfg_type), 5948c2ecf20Sopenharmony_ci &type); 5958c2ecf20Sopenharmony_ci pci_read_config_byte(dev, pos + offsetof(struct virtio_pci_cap, 5968c2ecf20Sopenharmony_ci bar), 5978c2ecf20Sopenharmony_ci &bar); 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci /* Ignore structures with reserved BAR values */ 6008c2ecf20Sopenharmony_ci if (bar > 0x5) 6018c2ecf20Sopenharmony_ci continue; 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci if (type == cfg_type) { 6048c2ecf20Sopenharmony_ci if (pci_resource_len(dev, bar) && 6058c2ecf20Sopenharmony_ci pci_resource_flags(dev, bar) & ioresource_types) { 6068c2ecf20Sopenharmony_ci *bars |= (1 << bar); 6078c2ecf20Sopenharmony_ci return pos; 6088c2ecf20Sopenharmony_ci } 6098c2ecf20Sopenharmony_ci } 6108c2ecf20Sopenharmony_ci } 6118c2ecf20Sopenharmony_ci return 0; 6128c2ecf20Sopenharmony_ci} 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci/* This is part of the ABI. Don't screw with it. */ 6158c2ecf20Sopenharmony_cistatic inline void check_offsets(void) 6168c2ecf20Sopenharmony_ci{ 6178c2ecf20Sopenharmony_ci /* Note: disk space was harmed in compilation of this function. */ 6188c2ecf20Sopenharmony_ci BUILD_BUG_ON(VIRTIO_PCI_CAP_VNDR != 6198c2ecf20Sopenharmony_ci offsetof(struct virtio_pci_cap, cap_vndr)); 6208c2ecf20Sopenharmony_ci BUILD_BUG_ON(VIRTIO_PCI_CAP_NEXT != 6218c2ecf20Sopenharmony_ci offsetof(struct virtio_pci_cap, cap_next)); 6228c2ecf20Sopenharmony_ci BUILD_BUG_ON(VIRTIO_PCI_CAP_LEN != 6238c2ecf20Sopenharmony_ci offsetof(struct virtio_pci_cap, cap_len)); 6248c2ecf20Sopenharmony_ci BUILD_BUG_ON(VIRTIO_PCI_CAP_CFG_TYPE != 6258c2ecf20Sopenharmony_ci offsetof(struct virtio_pci_cap, cfg_type)); 6268c2ecf20Sopenharmony_ci BUILD_BUG_ON(VIRTIO_PCI_CAP_BAR != 6278c2ecf20Sopenharmony_ci offsetof(struct virtio_pci_cap, bar)); 6288c2ecf20Sopenharmony_ci BUILD_BUG_ON(VIRTIO_PCI_CAP_OFFSET != 6298c2ecf20Sopenharmony_ci offsetof(struct virtio_pci_cap, offset)); 6308c2ecf20Sopenharmony_ci BUILD_BUG_ON(VIRTIO_PCI_CAP_LENGTH != 6318c2ecf20Sopenharmony_ci offsetof(struct virtio_pci_cap, length)); 6328c2ecf20Sopenharmony_ci BUILD_BUG_ON(VIRTIO_PCI_NOTIFY_CAP_MULT != 6338c2ecf20Sopenharmony_ci offsetof(struct virtio_pci_notify_cap, 6348c2ecf20Sopenharmony_ci notify_off_multiplier)); 6358c2ecf20Sopenharmony_ci BUILD_BUG_ON(VIRTIO_PCI_COMMON_DFSELECT != 6368c2ecf20Sopenharmony_ci offsetof(struct virtio_pci_common_cfg, 6378c2ecf20Sopenharmony_ci device_feature_select)); 6388c2ecf20Sopenharmony_ci BUILD_BUG_ON(VIRTIO_PCI_COMMON_DF != 6398c2ecf20Sopenharmony_ci offsetof(struct virtio_pci_common_cfg, device_feature)); 6408c2ecf20Sopenharmony_ci BUILD_BUG_ON(VIRTIO_PCI_COMMON_GFSELECT != 6418c2ecf20Sopenharmony_ci offsetof(struct virtio_pci_common_cfg, 6428c2ecf20Sopenharmony_ci guest_feature_select)); 6438c2ecf20Sopenharmony_ci BUILD_BUG_ON(VIRTIO_PCI_COMMON_GF != 6448c2ecf20Sopenharmony_ci offsetof(struct virtio_pci_common_cfg, guest_feature)); 6458c2ecf20Sopenharmony_ci BUILD_BUG_ON(VIRTIO_PCI_COMMON_MSIX != 6468c2ecf20Sopenharmony_ci offsetof(struct virtio_pci_common_cfg, msix_config)); 6478c2ecf20Sopenharmony_ci BUILD_BUG_ON(VIRTIO_PCI_COMMON_NUMQ != 6488c2ecf20Sopenharmony_ci offsetof(struct virtio_pci_common_cfg, num_queues)); 6498c2ecf20Sopenharmony_ci BUILD_BUG_ON(VIRTIO_PCI_COMMON_STATUS != 6508c2ecf20Sopenharmony_ci offsetof(struct virtio_pci_common_cfg, device_status)); 6518c2ecf20Sopenharmony_ci BUILD_BUG_ON(VIRTIO_PCI_COMMON_CFGGENERATION != 6528c2ecf20Sopenharmony_ci offsetof(struct virtio_pci_common_cfg, config_generation)); 6538c2ecf20Sopenharmony_ci BUILD_BUG_ON(VIRTIO_PCI_COMMON_Q_SELECT != 6548c2ecf20Sopenharmony_ci offsetof(struct virtio_pci_common_cfg, queue_select)); 6558c2ecf20Sopenharmony_ci BUILD_BUG_ON(VIRTIO_PCI_COMMON_Q_SIZE != 6568c2ecf20Sopenharmony_ci offsetof(struct virtio_pci_common_cfg, queue_size)); 6578c2ecf20Sopenharmony_ci BUILD_BUG_ON(VIRTIO_PCI_COMMON_Q_MSIX != 6588c2ecf20Sopenharmony_ci offsetof(struct virtio_pci_common_cfg, queue_msix_vector)); 6598c2ecf20Sopenharmony_ci BUILD_BUG_ON(VIRTIO_PCI_COMMON_Q_ENABLE != 6608c2ecf20Sopenharmony_ci offsetof(struct virtio_pci_common_cfg, queue_enable)); 6618c2ecf20Sopenharmony_ci BUILD_BUG_ON(VIRTIO_PCI_COMMON_Q_NOFF != 6628c2ecf20Sopenharmony_ci offsetof(struct virtio_pci_common_cfg, queue_notify_off)); 6638c2ecf20Sopenharmony_ci BUILD_BUG_ON(VIRTIO_PCI_COMMON_Q_DESCLO != 6648c2ecf20Sopenharmony_ci offsetof(struct virtio_pci_common_cfg, queue_desc_lo)); 6658c2ecf20Sopenharmony_ci BUILD_BUG_ON(VIRTIO_PCI_COMMON_Q_DESCHI != 6668c2ecf20Sopenharmony_ci offsetof(struct virtio_pci_common_cfg, queue_desc_hi)); 6678c2ecf20Sopenharmony_ci BUILD_BUG_ON(VIRTIO_PCI_COMMON_Q_AVAILLO != 6688c2ecf20Sopenharmony_ci offsetof(struct virtio_pci_common_cfg, queue_avail_lo)); 6698c2ecf20Sopenharmony_ci BUILD_BUG_ON(VIRTIO_PCI_COMMON_Q_AVAILHI != 6708c2ecf20Sopenharmony_ci offsetof(struct virtio_pci_common_cfg, queue_avail_hi)); 6718c2ecf20Sopenharmony_ci BUILD_BUG_ON(VIRTIO_PCI_COMMON_Q_USEDLO != 6728c2ecf20Sopenharmony_ci offsetof(struct virtio_pci_common_cfg, queue_used_lo)); 6738c2ecf20Sopenharmony_ci BUILD_BUG_ON(VIRTIO_PCI_COMMON_Q_USEDHI != 6748c2ecf20Sopenharmony_ci offsetof(struct virtio_pci_common_cfg, queue_used_hi)); 6758c2ecf20Sopenharmony_ci} 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci/* the PCI probing function */ 6788c2ecf20Sopenharmony_ciint virtio_pci_modern_probe(struct virtio_pci_device *vp_dev) 6798c2ecf20Sopenharmony_ci{ 6808c2ecf20Sopenharmony_ci struct pci_dev *pci_dev = vp_dev->pci_dev; 6818c2ecf20Sopenharmony_ci int err, common, isr, notify, device; 6828c2ecf20Sopenharmony_ci u32 notify_length; 6838c2ecf20Sopenharmony_ci u32 notify_offset; 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci check_offsets(); 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci /* We only own devices >= 0x1000 and <= 0x107f: leave the rest. */ 6888c2ecf20Sopenharmony_ci if (pci_dev->device < 0x1000 || pci_dev->device > 0x107f) 6898c2ecf20Sopenharmony_ci return -ENODEV; 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci if (pci_dev->device < 0x1040) { 6928c2ecf20Sopenharmony_ci /* Transitional devices: use the PCI subsystem device id as 6938c2ecf20Sopenharmony_ci * virtio device id, same as legacy driver always did. 6948c2ecf20Sopenharmony_ci */ 6958c2ecf20Sopenharmony_ci vp_dev->vdev.id.device = pci_dev->subsystem_device; 6968c2ecf20Sopenharmony_ci } else { 6978c2ecf20Sopenharmony_ci /* Modern devices: simply use PCI device id, but start from 0x1040. */ 6988c2ecf20Sopenharmony_ci vp_dev->vdev.id.device = pci_dev->device - 0x1040; 6998c2ecf20Sopenharmony_ci } 7008c2ecf20Sopenharmony_ci vp_dev->vdev.id.vendor = pci_dev->subsystem_vendor; 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci /* check for a common config: if not, use legacy mode (bar 0). */ 7038c2ecf20Sopenharmony_ci common = virtio_pci_find_capability(pci_dev, VIRTIO_PCI_CAP_COMMON_CFG, 7048c2ecf20Sopenharmony_ci IORESOURCE_IO | IORESOURCE_MEM, 7058c2ecf20Sopenharmony_ci &vp_dev->modern_bars); 7068c2ecf20Sopenharmony_ci if (!common) { 7078c2ecf20Sopenharmony_ci dev_info(&pci_dev->dev, 7088c2ecf20Sopenharmony_ci "virtio_pci: leaving for legacy driver\n"); 7098c2ecf20Sopenharmony_ci return -ENODEV; 7108c2ecf20Sopenharmony_ci } 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci /* If common is there, these should be too... */ 7138c2ecf20Sopenharmony_ci isr = virtio_pci_find_capability(pci_dev, VIRTIO_PCI_CAP_ISR_CFG, 7148c2ecf20Sopenharmony_ci IORESOURCE_IO | IORESOURCE_MEM, 7158c2ecf20Sopenharmony_ci &vp_dev->modern_bars); 7168c2ecf20Sopenharmony_ci notify = virtio_pci_find_capability(pci_dev, VIRTIO_PCI_CAP_NOTIFY_CFG, 7178c2ecf20Sopenharmony_ci IORESOURCE_IO | IORESOURCE_MEM, 7188c2ecf20Sopenharmony_ci &vp_dev->modern_bars); 7198c2ecf20Sopenharmony_ci if (!isr || !notify) { 7208c2ecf20Sopenharmony_ci dev_err(&pci_dev->dev, 7218c2ecf20Sopenharmony_ci "virtio_pci: missing capabilities %i/%i/%i\n", 7228c2ecf20Sopenharmony_ci common, isr, notify); 7238c2ecf20Sopenharmony_ci return -EINVAL; 7248c2ecf20Sopenharmony_ci } 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci err = dma_set_mask_and_coherent(&pci_dev->dev, DMA_BIT_MASK(64)); 7278c2ecf20Sopenharmony_ci if (err) 7288c2ecf20Sopenharmony_ci err = dma_set_mask_and_coherent(&pci_dev->dev, 7298c2ecf20Sopenharmony_ci DMA_BIT_MASK(32)); 7308c2ecf20Sopenharmony_ci if (err) 7318c2ecf20Sopenharmony_ci dev_warn(&pci_dev->dev, "Failed to enable 64-bit or 32-bit DMA. Trying to continue, but this might not work.\n"); 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci /* Device capability is only mandatory for devices that have 7348c2ecf20Sopenharmony_ci * device-specific configuration. 7358c2ecf20Sopenharmony_ci */ 7368c2ecf20Sopenharmony_ci device = virtio_pci_find_capability(pci_dev, VIRTIO_PCI_CAP_DEVICE_CFG, 7378c2ecf20Sopenharmony_ci IORESOURCE_IO | IORESOURCE_MEM, 7388c2ecf20Sopenharmony_ci &vp_dev->modern_bars); 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci err = pci_request_selected_regions(pci_dev, vp_dev->modern_bars, 7418c2ecf20Sopenharmony_ci "virtio-pci-modern"); 7428c2ecf20Sopenharmony_ci if (err) 7438c2ecf20Sopenharmony_ci return err; 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci err = -EINVAL; 7468c2ecf20Sopenharmony_ci vp_dev->common = map_capability(pci_dev, common, 7478c2ecf20Sopenharmony_ci sizeof(struct virtio_pci_common_cfg), 4, 7488c2ecf20Sopenharmony_ci 0, sizeof(struct virtio_pci_common_cfg), 7498c2ecf20Sopenharmony_ci NULL); 7508c2ecf20Sopenharmony_ci if (!vp_dev->common) 7518c2ecf20Sopenharmony_ci goto err_map_common; 7528c2ecf20Sopenharmony_ci vp_dev->isr = map_capability(pci_dev, isr, sizeof(u8), 1, 7538c2ecf20Sopenharmony_ci 0, 1, 7548c2ecf20Sopenharmony_ci NULL); 7558c2ecf20Sopenharmony_ci if (!vp_dev->isr) 7568c2ecf20Sopenharmony_ci goto err_map_isr; 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci /* Read notify_off_multiplier from config space. */ 7598c2ecf20Sopenharmony_ci pci_read_config_dword(pci_dev, 7608c2ecf20Sopenharmony_ci notify + offsetof(struct virtio_pci_notify_cap, 7618c2ecf20Sopenharmony_ci notify_off_multiplier), 7628c2ecf20Sopenharmony_ci &vp_dev->notify_offset_multiplier); 7638c2ecf20Sopenharmony_ci /* Read notify length and offset from config space. */ 7648c2ecf20Sopenharmony_ci pci_read_config_dword(pci_dev, 7658c2ecf20Sopenharmony_ci notify + offsetof(struct virtio_pci_notify_cap, 7668c2ecf20Sopenharmony_ci cap.length), 7678c2ecf20Sopenharmony_ci ¬ify_length); 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci pci_read_config_dword(pci_dev, 7708c2ecf20Sopenharmony_ci notify + offsetof(struct virtio_pci_notify_cap, 7718c2ecf20Sopenharmony_ci cap.offset), 7728c2ecf20Sopenharmony_ci ¬ify_offset); 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci /* We don't know how many VQs we'll map, ahead of the time. 7758c2ecf20Sopenharmony_ci * If notify length is small, map it all now. 7768c2ecf20Sopenharmony_ci * Otherwise, map each VQ individually later. 7778c2ecf20Sopenharmony_ci */ 7788c2ecf20Sopenharmony_ci if ((u64)notify_length + (notify_offset % PAGE_SIZE) <= PAGE_SIZE) { 7798c2ecf20Sopenharmony_ci vp_dev->notify_base = map_capability(pci_dev, notify, 2, 2, 7808c2ecf20Sopenharmony_ci 0, notify_length, 7818c2ecf20Sopenharmony_ci &vp_dev->notify_len); 7828c2ecf20Sopenharmony_ci if (!vp_dev->notify_base) 7838c2ecf20Sopenharmony_ci goto err_map_notify; 7848c2ecf20Sopenharmony_ci } else { 7858c2ecf20Sopenharmony_ci vp_dev->notify_map_cap = notify; 7868c2ecf20Sopenharmony_ci } 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci /* Again, we don't know how much we should map, but PAGE_SIZE 7898c2ecf20Sopenharmony_ci * is more than enough for all existing devices. 7908c2ecf20Sopenharmony_ci */ 7918c2ecf20Sopenharmony_ci if (device) { 7928c2ecf20Sopenharmony_ci vp_dev->device = map_capability(pci_dev, device, 0, 4, 7938c2ecf20Sopenharmony_ci 0, PAGE_SIZE, 7948c2ecf20Sopenharmony_ci &vp_dev->device_len); 7958c2ecf20Sopenharmony_ci if (!vp_dev->device) 7968c2ecf20Sopenharmony_ci goto err_map_device; 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci vp_dev->vdev.config = &virtio_pci_config_ops; 7998c2ecf20Sopenharmony_ci } else { 8008c2ecf20Sopenharmony_ci vp_dev->vdev.config = &virtio_pci_config_nodev_ops; 8018c2ecf20Sopenharmony_ci } 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci vp_dev->config_vector = vp_config_vector; 8048c2ecf20Sopenharmony_ci vp_dev->setup_vq = setup_vq; 8058c2ecf20Sopenharmony_ci vp_dev->del_vq = del_vq; 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci return 0; 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_cierr_map_device: 8108c2ecf20Sopenharmony_ci if (vp_dev->notify_base) 8118c2ecf20Sopenharmony_ci pci_iounmap(pci_dev, vp_dev->notify_base); 8128c2ecf20Sopenharmony_cierr_map_notify: 8138c2ecf20Sopenharmony_ci pci_iounmap(pci_dev, vp_dev->isr); 8148c2ecf20Sopenharmony_cierr_map_isr: 8158c2ecf20Sopenharmony_ci pci_iounmap(pci_dev, vp_dev->common); 8168c2ecf20Sopenharmony_cierr_map_common: 8178c2ecf20Sopenharmony_ci return err; 8188c2ecf20Sopenharmony_ci} 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_civoid virtio_pci_modern_remove(struct virtio_pci_device *vp_dev) 8218c2ecf20Sopenharmony_ci{ 8228c2ecf20Sopenharmony_ci struct pci_dev *pci_dev = vp_dev->pci_dev; 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ci if (vp_dev->device) 8258c2ecf20Sopenharmony_ci pci_iounmap(pci_dev, vp_dev->device); 8268c2ecf20Sopenharmony_ci if (vp_dev->notify_base) 8278c2ecf20Sopenharmony_ci pci_iounmap(pci_dev, vp_dev->notify_base); 8288c2ecf20Sopenharmony_ci pci_iounmap(pci_dev, vp_dev->isr); 8298c2ecf20Sopenharmony_ci pci_iounmap(pci_dev, vp_dev->common); 8308c2ecf20Sopenharmony_ci pci_release_selected_regions(pci_dev, vp_dev->modern_bars); 8318c2ecf20Sopenharmony_ci} 832