Thomas Gleixner | f33f5fe | 2019-05-22 09:51:24 +0200 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
Michael S. Tsirkin | 38eb4a2 | 2014-12-07 18:41:16 +0200 | [diff] [blame] | 2 | /* |
Michael S. Tsirkin | a90fdce | 2014-12-08 12:31:02 +0200 | [diff] [blame] | 3 | * Virtio PCI driver - legacy device support |
Michael S. Tsirkin | 38eb4a2 | 2014-12-07 18:41:16 +0200 | [diff] [blame] | 4 | * |
| 5 | * This module allows virtio devices to be used over a virtual PCI device. |
| 6 | * This can be used with QEMU based VMMs like KVM or Xen. |
| 7 | * |
| 8 | * Copyright IBM Corp. 2007 |
Michael S. Tsirkin | a90fdce | 2014-12-08 12:31:02 +0200 | [diff] [blame] | 9 | * Copyright Red Hat, Inc. 2014 |
Michael S. Tsirkin | 38eb4a2 | 2014-12-07 18:41:16 +0200 | [diff] [blame] | 10 | * |
| 11 | * Authors: |
| 12 | * Anthony Liguori <aliguori@us.ibm.com> |
Michael S. Tsirkin | a90fdce | 2014-12-08 12:31:02 +0200 | [diff] [blame] | 13 | * Rusty Russell <rusty@rustcorp.com.au> |
| 14 | * Michael S. Tsirkin <mst@redhat.com> |
Michael S. Tsirkin | 38eb4a2 | 2014-12-07 18:41:16 +0200 | [diff] [blame] | 15 | */ |
| 16 | |
Wu Zongyong | d89c8169 | 2021-10-29 17:14:42 +0800 | [diff] [blame] | 17 | #include "linux/virtio_pci_legacy.h" |
Michael S. Tsirkin | 5f4c976 | 2014-12-08 16:39:45 +0200 | [diff] [blame] | 18 | #include "virtio_pci_common.h" |
Michael S. Tsirkin | 38eb4a2 | 2014-12-07 18:41:16 +0200 | [diff] [blame] | 19 | |
Michael S. Tsirkin | 38eb4a2 | 2014-12-07 18:41:16 +0200 | [diff] [blame] | 20 | /* virtio config->get_features() implementation */ |
| 21 | static u64 vp_get_features(struct virtio_device *vdev) |
| 22 | { |
| 23 | struct virtio_pci_device *vp_dev = to_vp_device(vdev); |
| 24 | |
| 25 | /* When someone needs more than 32 feature bits, we'll need to |
| 26 | * steal a bit to indicate that the rest are somewhere else. */ |
Wu Zongyong | d89c8169 | 2021-10-29 17:14:42 +0800 | [diff] [blame] | 27 | return vp_legacy_get_features(&vp_dev->ldev); |
Michael S. Tsirkin | 38eb4a2 | 2014-12-07 18:41:16 +0200 | [diff] [blame] | 28 | } |
| 29 | |
| 30 | /* virtio config->finalize_features() implementation */ |
| 31 | static int vp_finalize_features(struct virtio_device *vdev) |
| 32 | { |
| 33 | struct virtio_pci_device *vp_dev = to_vp_device(vdev); |
| 34 | |
| 35 | /* Give virtio_ring a chance to accept features. */ |
| 36 | vring_transport_features(vdev); |
| 37 | |
| 38 | /* Make sure we don't have any features > 32 bits! */ |
| 39 | BUG_ON((u32)vdev->features != vdev->features); |
| 40 | |
| 41 | /* We only support 32 feature bits. */ |
Wu Zongyong | d89c8169 | 2021-10-29 17:14:42 +0800 | [diff] [blame] | 42 | vp_legacy_set_features(&vp_dev->ldev, vdev->features); |
Michael S. Tsirkin | 38eb4a2 | 2014-12-07 18:41:16 +0200 | [diff] [blame] | 43 | |
| 44 | return 0; |
| 45 | } |
| 46 | |
| 47 | /* virtio config->get() implementation */ |
| 48 | static void vp_get(struct virtio_device *vdev, unsigned offset, |
| 49 | void *buf, unsigned len) |
| 50 | { |
| 51 | struct virtio_pci_device *vp_dev = to_vp_device(vdev); |
Wu Zongyong | d89c8169 | 2021-10-29 17:14:42 +0800 | [diff] [blame] | 52 | void __iomem *ioaddr = vp_dev->ldev.ioaddr + |
Dongli Zhang | e8d26f2 | 2018-12-10 08:54:34 +0800 | [diff] [blame] | 53 | VIRTIO_PCI_CONFIG_OFF(vp_dev->msix_enabled) + |
| 54 | offset; |
Michael S. Tsirkin | 38eb4a2 | 2014-12-07 18:41:16 +0200 | [diff] [blame] | 55 | u8 *ptr = buf; |
| 56 | int i; |
| 57 | |
| 58 | for (i = 0; i < len; i++) |
| 59 | ptr[i] = ioread8(ioaddr + i); |
| 60 | } |
| 61 | |
| 62 | /* the config->set() implementation. it's symmetric to the config->get() |
| 63 | * implementation */ |
| 64 | static void vp_set(struct virtio_device *vdev, unsigned offset, |
| 65 | const void *buf, unsigned len) |
| 66 | { |
| 67 | struct virtio_pci_device *vp_dev = to_vp_device(vdev); |
Wu Zongyong | d89c8169 | 2021-10-29 17:14:42 +0800 | [diff] [blame] | 68 | void __iomem *ioaddr = vp_dev->ldev.ioaddr + |
Dongli Zhang | e8d26f2 | 2018-12-10 08:54:34 +0800 | [diff] [blame] | 69 | VIRTIO_PCI_CONFIG_OFF(vp_dev->msix_enabled) + |
| 70 | offset; |
Michael S. Tsirkin | 38eb4a2 | 2014-12-07 18:41:16 +0200 | [diff] [blame] | 71 | const u8 *ptr = buf; |
| 72 | int i; |
| 73 | |
| 74 | for (i = 0; i < len; i++) |
| 75 | iowrite8(ptr[i], ioaddr + i); |
| 76 | } |
| 77 | |
| 78 | /* config->{get,set}_status() implementations */ |
| 79 | static u8 vp_get_status(struct virtio_device *vdev) |
| 80 | { |
| 81 | struct virtio_pci_device *vp_dev = to_vp_device(vdev); |
Wu Zongyong | d89c8169 | 2021-10-29 17:14:42 +0800 | [diff] [blame] | 82 | return vp_legacy_get_status(&vp_dev->ldev); |
Michael S. Tsirkin | 38eb4a2 | 2014-12-07 18:41:16 +0200 | [diff] [blame] | 83 | } |
| 84 | |
| 85 | static void vp_set_status(struct virtio_device *vdev, u8 status) |
| 86 | { |
| 87 | struct virtio_pci_device *vp_dev = to_vp_device(vdev); |
| 88 | /* We should never be setting status to 0. */ |
| 89 | BUG_ON(status == 0); |
Wu Zongyong | d89c8169 | 2021-10-29 17:14:42 +0800 | [diff] [blame] | 90 | vp_legacy_set_status(&vp_dev->ldev, status); |
Michael S. Tsirkin | 38eb4a2 | 2014-12-07 18:41:16 +0200 | [diff] [blame] | 91 | } |
| 92 | |
| 93 | static void vp_reset(struct virtio_device *vdev) |
| 94 | { |
| 95 | struct virtio_pci_device *vp_dev = to_vp_device(vdev); |
| 96 | /* 0 status means a reset. */ |
Wu Zongyong | d89c8169 | 2021-10-29 17:14:42 +0800 | [diff] [blame] | 97 | vp_legacy_set_status(&vp_dev->ldev, 0); |
Michael S. Tsirkin | 38eb4a2 | 2014-12-07 18:41:16 +0200 | [diff] [blame] | 98 | /* Flush out the status write, and flush in device writes, |
| 99 | * including MSi-X interrupts, if any. */ |
Wu Zongyong | d89c8169 | 2021-10-29 17:14:42 +0800 | [diff] [blame] | 100 | vp_legacy_get_status(&vp_dev->ldev); |
Jason Wang | 9e35276 | 2021-10-19 15:01:46 +0800 | [diff] [blame] | 101 | /* Disable VQ/configuration callbacks. */ |
| 102 | vp_disable_cbs(vdev); |
Michael S. Tsirkin | 38eb4a2 | 2014-12-07 18:41:16 +0200 | [diff] [blame] | 103 | } |
| 104 | |
| 105 | static u16 vp_config_vector(struct virtio_pci_device *vp_dev, u16 vector) |
| 106 | { |
Wu Zongyong | d89c8169 | 2021-10-29 17:14:42 +0800 | [diff] [blame] | 107 | return vp_legacy_config_vector(&vp_dev->ldev, vector); |
Michael S. Tsirkin | 38eb4a2 | 2014-12-07 18:41:16 +0200 | [diff] [blame] | 108 | } |
| 109 | |
| 110 | static struct virtqueue *setup_vq(struct virtio_pci_device *vp_dev, |
Michael S. Tsirkin | 0a9b3f4 | 2017-04-04 21:44:44 +0300 | [diff] [blame] | 111 | struct virtio_pci_vq_info *info, |
Michael S. Tsirkin | 38eb4a2 | 2014-12-07 18:41:16 +0200 | [diff] [blame] | 112 | unsigned index, |
| 113 | void (*callback)(struct virtqueue *vq), |
| 114 | const char *name, |
Michael S. Tsirkin | f94682d | 2017-03-06 18:32:29 +0200 | [diff] [blame] | 115 | bool ctx, |
Michael S. Tsirkin | 38eb4a2 | 2014-12-07 18:41:16 +0200 | [diff] [blame] | 116 | u16 msix_vec) |
| 117 | { |
| 118 | struct virtqueue *vq; |
Michael S. Tsirkin | 38eb4a2 | 2014-12-07 18:41:16 +0200 | [diff] [blame] | 119 | u16 num; |
| 120 | int err; |
Suzuki K Poulose | 6959920 | 2018-07-18 10:18:45 +0100 | [diff] [blame] | 121 | u64 q_pfn; |
Michael S. Tsirkin | 38eb4a2 | 2014-12-07 18:41:16 +0200 | [diff] [blame] | 122 | |
Michael S. Tsirkin | 38eb4a2 | 2014-12-07 18:41:16 +0200 | [diff] [blame] | 123 | /* Check if queue is either not available or already active. */ |
Wu Zongyong | d89c8169 | 2021-10-29 17:14:42 +0800 | [diff] [blame] | 124 | num = vp_legacy_get_queue_size(&vp_dev->ldev, index); |
| 125 | if (!num || vp_legacy_get_queue_enable(&vp_dev->ldev, index)) |
Michael S. Tsirkin | 38eb4a2 | 2014-12-07 18:41:16 +0200 | [diff] [blame] | 126 | return ERR_PTR(-ENOENT); |
| 127 | |
Michael S. Tsirkin | 0a9b3f4 | 2017-04-04 21:44:44 +0300 | [diff] [blame] | 128 | info->msix_vector = msix_vec; |
| 129 | |
Andy Lutomirski | 7a5589b | 2016-02-02 21:46:39 -0800 | [diff] [blame] | 130 | /* create the vring */ |
| 131 | vq = vring_create_virtqueue(index, num, |
| 132 | VIRTIO_PCI_VRING_ALIGN, &vp_dev->vdev, |
Michael S. Tsirkin | f94682d | 2017-03-06 18:32:29 +0200 | [diff] [blame] | 133 | true, false, ctx, |
| 134 | vp_notify, callback, name); |
Andy Lutomirski | 7a5589b | 2016-02-02 21:46:39 -0800 | [diff] [blame] | 135 | if (!vq) |
Michael S. Tsirkin | 38eb4a2 | 2014-12-07 18:41:16 +0200 | [diff] [blame] | 136 | return ERR_PTR(-ENOMEM); |
| 137 | |
Suzuki K Poulose | 6959920 | 2018-07-18 10:18:45 +0100 | [diff] [blame] | 138 | q_pfn = virtqueue_get_desc_addr(vq) >> VIRTIO_PCI_QUEUE_ADDR_SHIFT; |
| 139 | if (q_pfn >> 32) { |
| 140 | dev_err(&vp_dev->pci_dev->dev, |
王贇 | 6017599 | 2021-12-09 11:29:25 +0800 | [diff] [blame] | 141 | "platform bug: legacy virtio-pci must not be used with RAM above 0x%llxGB\n", |
Suzuki K Poulose | 6959920 | 2018-07-18 10:18:45 +0100 | [diff] [blame] | 142 | 0x1ULL << (32 + PAGE_SHIFT - 30)); |
| 143 | err = -E2BIG; |
| 144 | goto out_del_vq; |
| 145 | } |
| 146 | |
Michael S. Tsirkin | 38eb4a2 | 2014-12-07 18:41:16 +0200 | [diff] [blame] | 147 | /* activate the queue */ |
Wu Zongyong | d89c8169 | 2021-10-29 17:14:42 +0800 | [diff] [blame] | 148 | vp_legacy_set_queue_address(&vp_dev->ldev, index, q_pfn); |
Michael S. Tsirkin | 38eb4a2 | 2014-12-07 18:41:16 +0200 | [diff] [blame] | 149 | |
Wu Zongyong | d89c8169 | 2021-10-29 17:14:42 +0800 | [diff] [blame] | 150 | vq->priv = (void __force *)vp_dev->ldev.ioaddr + VIRTIO_PCI_QUEUE_NOTIFY; |
Michael S. Tsirkin | 38eb4a2 | 2014-12-07 18:41:16 +0200 | [diff] [blame] | 151 | |
| 152 | if (msix_vec != VIRTIO_MSI_NO_VECTOR) { |
Wu Zongyong | d89c8169 | 2021-10-29 17:14:42 +0800 | [diff] [blame] | 153 | msix_vec = vp_legacy_queue_vector(&vp_dev->ldev, index, msix_vec); |
Michael S. Tsirkin | 38eb4a2 | 2014-12-07 18:41:16 +0200 | [diff] [blame] | 154 | if (msix_vec == VIRTIO_MSI_NO_VECTOR) { |
| 155 | err = -EBUSY; |
Andy Lutomirski | 7a5589b | 2016-02-02 21:46:39 -0800 | [diff] [blame] | 156 | goto out_deactivate; |
Michael S. Tsirkin | 38eb4a2 | 2014-12-07 18:41:16 +0200 | [diff] [blame] | 157 | } |
| 158 | } |
| 159 | |
| 160 | return vq; |
| 161 | |
Andy Lutomirski | 7a5589b | 2016-02-02 21:46:39 -0800 | [diff] [blame] | 162 | out_deactivate: |
Wu Zongyong | d89c8169 | 2021-10-29 17:14:42 +0800 | [diff] [blame] | 163 | vp_legacy_set_queue_address(&vp_dev->ldev, index, 0); |
Suzuki K Poulose | 6959920 | 2018-07-18 10:18:45 +0100 | [diff] [blame] | 164 | out_del_vq: |
Andy Lutomirski | 7a5589b | 2016-02-02 21:46:39 -0800 | [diff] [blame] | 165 | vring_del_virtqueue(vq); |
Michael S. Tsirkin | 38eb4a2 | 2014-12-07 18:41:16 +0200 | [diff] [blame] | 166 | return ERR_PTR(err); |
| 167 | } |
| 168 | |
Michael S. Tsirkin | 0a9b3f4 | 2017-04-04 21:44:44 +0300 | [diff] [blame] | 169 | static void del_vq(struct virtio_pci_vq_info *info) |
Michael S. Tsirkin | 38eb4a2 | 2014-12-07 18:41:16 +0200 | [diff] [blame] | 170 | { |
Michael S. Tsirkin | 0a9b3f4 | 2017-04-04 21:44:44 +0300 | [diff] [blame] | 171 | struct virtqueue *vq = info->vq; |
Michael S. Tsirkin | 38eb4a2 | 2014-12-07 18:41:16 +0200 | [diff] [blame] | 172 | struct virtio_pci_device *vp_dev = to_vp_device(vq->vdev); |
Michael S. Tsirkin | 38eb4a2 | 2014-12-07 18:41:16 +0200 | [diff] [blame] | 173 | |
Michael S. Tsirkin | 2008c15 | 2017-04-04 21:09:20 +0300 | [diff] [blame] | 174 | if (vp_dev->msix_enabled) { |
Wu Zongyong | d89c8169 | 2021-10-29 17:14:42 +0800 | [diff] [blame] | 175 | vp_legacy_queue_vector(&vp_dev->ldev, vq->index, |
| 176 | VIRTIO_MSI_NO_VECTOR); |
Michael S. Tsirkin | 38eb4a2 | 2014-12-07 18:41:16 +0200 | [diff] [blame] | 177 | /* Flush the write out to device */ |
Wu Zongyong | d89c8169 | 2021-10-29 17:14:42 +0800 | [diff] [blame] | 178 | ioread8(vp_dev->ldev.ioaddr + VIRTIO_PCI_ISR); |
Michael S. Tsirkin | 38eb4a2 | 2014-12-07 18:41:16 +0200 | [diff] [blame] | 179 | } |
| 180 | |
Michael S. Tsirkin | 38eb4a2 | 2014-12-07 18:41:16 +0200 | [diff] [blame] | 181 | /* Select and deactivate the queue */ |
Wu Zongyong | d89c8169 | 2021-10-29 17:14:42 +0800 | [diff] [blame] | 182 | vp_legacy_set_queue_address(&vp_dev->ldev, vq->index, 0); |
Michael S. Tsirkin | 38eb4a2 | 2014-12-07 18:41:16 +0200 | [diff] [blame] | 183 | |
Andy Lutomirski | 7a5589b | 2016-02-02 21:46:39 -0800 | [diff] [blame] | 184 | vring_del_virtqueue(vq); |
Michael S. Tsirkin | 38eb4a2 | 2014-12-07 18:41:16 +0200 | [diff] [blame] | 185 | } |
| 186 | |
| 187 | static const struct virtio_config_ops virtio_pci_config_ops = { |
Jason Wang | 9e35276 | 2021-10-19 15:01:46 +0800 | [diff] [blame] | 188 | .enable_cbs = vp_enable_cbs, |
Michael S. Tsirkin | 38eb4a2 | 2014-12-07 18:41:16 +0200 | [diff] [blame] | 189 | .get = vp_get, |
| 190 | .set = vp_set, |
| 191 | .get_status = vp_get_status, |
| 192 | .set_status = vp_set_status, |
| 193 | .reset = vp_reset, |
| 194 | .find_vqs = vp_find_vqs, |
| 195 | .del_vqs = vp_del_vqs, |
| 196 | .get_features = vp_get_features, |
| 197 | .finalize_features = vp_finalize_features, |
| 198 | .bus_name = vp_bus_name, |
| 199 | .set_vq_affinity = vp_set_vq_affinity, |
Christoph Hellwig | bbaba47 | 2017-02-05 18:15:23 +0100 | [diff] [blame] | 200 | .get_vq_affinity = vp_get_vq_affinity, |
Michael S. Tsirkin | 38eb4a2 | 2014-12-07 18:41:16 +0200 | [diff] [blame] | 201 | }; |
| 202 | |
| 203 | /* the PCI probing function */ |
Michael S. Tsirkin | ff31d2e | 2015-01-13 11:23:32 +0200 | [diff] [blame] | 204 | int virtio_pci_legacy_probe(struct virtio_pci_device *vp_dev) |
Michael S. Tsirkin | 38eb4a2 | 2014-12-07 18:41:16 +0200 | [diff] [blame] | 205 | { |
Wu Zongyong | d89c8169 | 2021-10-29 17:14:42 +0800 | [diff] [blame] | 206 | struct virtio_pci_legacy_device *ldev = &vp_dev->ldev; |
Michael S. Tsirkin | ff31d2e | 2015-01-13 11:23:32 +0200 | [diff] [blame] | 207 | struct pci_dev *pci_dev = vp_dev->pci_dev; |
Gerd Hoffmann | 59a5b0f7 | 2015-06-24 07:54:15 +0200 | [diff] [blame] | 208 | int rc; |
Michael S. Tsirkin | 38eb4a2 | 2014-12-07 18:41:16 +0200 | [diff] [blame] | 209 | |
Wu Zongyong | d89c8169 | 2021-10-29 17:14:42 +0800 | [diff] [blame] | 210 | ldev->pci_dev = pci_dev; |
Michael S. Tsirkin | 38eb4a2 | 2014-12-07 18:41:16 +0200 | [diff] [blame] | 211 | |
Wu Zongyong | d89c8169 | 2021-10-29 17:14:42 +0800 | [diff] [blame] | 212 | rc = vp_legacy_probe(ldev); |
Gerd Hoffmann | 59a5b0f7 | 2015-06-24 07:54:15 +0200 | [diff] [blame] | 213 | if (rc) |
| 214 | return rc; |
| 215 | |
Wu Zongyong | d89c8169 | 2021-10-29 17:14:42 +0800 | [diff] [blame] | 216 | vp_dev->isr = ldev->isr; |
| 217 | vp_dev->vdev.id = ldev->id; |
Michael S. Tsirkin | 38eb4a2 | 2014-12-07 18:41:16 +0200 | [diff] [blame] | 218 | |
Michael S. Tsirkin | ff31d2e | 2015-01-13 11:23:32 +0200 | [diff] [blame] | 219 | vp_dev->vdev.config = &virtio_pci_config_ops; |
| 220 | |
Michael S. Tsirkin | 38eb4a2 | 2014-12-07 18:41:16 +0200 | [diff] [blame] | 221 | vp_dev->config_vector = vp_config_vector; |
| 222 | vp_dev->setup_vq = setup_vq; |
| 223 | vp_dev->del_vq = del_vq; |
| 224 | |
Michael S. Tsirkin | 38eb4a2 | 2014-12-07 18:41:16 +0200 | [diff] [blame] | 225 | return 0; |
Michael S. Tsirkin | 38eb4a2 | 2014-12-07 18:41:16 +0200 | [diff] [blame] | 226 | } |
| 227 | |
Michael S. Tsirkin | ff31d2e | 2015-01-13 11:23:32 +0200 | [diff] [blame] | 228 | void virtio_pci_legacy_remove(struct virtio_pci_device *vp_dev) |
Michael S. Tsirkin | 38eb4a2 | 2014-12-07 18:41:16 +0200 | [diff] [blame] | 229 | { |
Wu Zongyong | d89c8169 | 2021-10-29 17:14:42 +0800 | [diff] [blame] | 230 | struct virtio_pci_legacy_device *ldev = &vp_dev->ldev; |
Michael S. Tsirkin | 38eb4a2 | 2014-12-07 18:41:16 +0200 | [diff] [blame] | 231 | |
Wu Zongyong | d89c8169 | 2021-10-29 17:14:42 +0800 | [diff] [blame] | 232 | vp_legacy_remove(ldev); |
Michael S. Tsirkin | 38eb4a2 | 2014-12-07 18:41:16 +0200 | [diff] [blame] | 233 | } |