| 1 | // SPDX-License-Identifier: GPL-2.0-or-later | 
|---|
| 2 |  | 
|---|
| 3 | #include <linux/virtio_pci_modern.h> | 
|---|
| 4 | #include <linux/module.h> | 
|---|
| 5 | #include <linux/pci.h> | 
|---|
| 6 | #include <linux/delay.h> | 
|---|
| 7 |  | 
|---|
| 8 | /* | 
|---|
| 9 | * vp_modern_map_capability - map a part of virtio pci capability | 
|---|
| 10 | * @mdev: the modern virtio-pci device | 
|---|
| 11 | * @off: offset of the capability | 
|---|
| 12 | * @minlen: minimal length of the capability | 
|---|
| 13 | * @align: align requirement | 
|---|
| 14 | * @start: start from the capability | 
|---|
| 15 | * @size: map size | 
|---|
| 16 | * @len: the length that is actually mapped | 
|---|
| 17 | * @pa: physical address of the capability | 
|---|
| 18 | * | 
|---|
| 19 | * Returns the io address of for the part of the capability | 
|---|
| 20 | */ | 
|---|
| 21 | static void __iomem * | 
|---|
| 22 | vp_modern_map_capability(struct virtio_pci_modern_device *mdev, int off, | 
|---|
| 23 | size_t minlen, u32 align, u32 start, u32 size, | 
|---|
| 24 | size_t *len, resource_size_t *pa) | 
|---|
| 25 | { | 
|---|
| 26 | struct pci_dev *dev = mdev->pci_dev; | 
|---|
| 27 | u8 bar; | 
|---|
| 28 | u32 offset, length; | 
|---|
| 29 | void __iomem *p; | 
|---|
| 30 |  | 
|---|
| 31 | pci_read_config_byte(dev, where: off + offsetof(struct virtio_pci_cap, | 
|---|
| 32 | bar), | 
|---|
| 33 | val: &bar); | 
|---|
| 34 | pci_read_config_dword(dev, where: off + offsetof(struct virtio_pci_cap, offset), | 
|---|
| 35 | val: &offset); | 
|---|
| 36 | pci_read_config_dword(dev, where: off + offsetof(struct virtio_pci_cap, length), | 
|---|
| 37 | val: &length); | 
|---|
| 38 |  | 
|---|
| 39 | /* Check if the BAR may have changed since we requested the region. */ | 
|---|
| 40 | if (bar >= PCI_STD_NUM_BARS || !(mdev->modern_bars & (1 << bar))) { | 
|---|
| 41 | dev_err(&dev->dev, | 
|---|
| 42 | "virtio_pci: bar unexpectedly changed to %u\n", bar); | 
|---|
| 43 | return NULL; | 
|---|
| 44 | } | 
|---|
| 45 |  | 
|---|
| 46 | if (length <= start) { | 
|---|
| 47 | dev_err(&dev->dev, | 
|---|
| 48 | "virtio_pci: bad capability len %u (>%u expected)\n", | 
|---|
| 49 | length, start); | 
|---|
| 50 | return NULL; | 
|---|
| 51 | } | 
|---|
| 52 |  | 
|---|
| 53 | if (length - start < minlen) { | 
|---|
| 54 | dev_err(&dev->dev, | 
|---|
| 55 | "virtio_pci: bad capability len %u (>=%zu expected)\n", | 
|---|
| 56 | length, minlen); | 
|---|
| 57 | return NULL; | 
|---|
| 58 | } | 
|---|
| 59 |  | 
|---|
| 60 | length -= start; | 
|---|
| 61 |  | 
|---|
| 62 | if (start + offset < offset) { | 
|---|
| 63 | dev_err(&dev->dev, | 
|---|
| 64 | "virtio_pci: map wrap-around %u+%u\n", | 
|---|
| 65 | start, offset); | 
|---|
| 66 | return NULL; | 
|---|
| 67 | } | 
|---|
| 68 |  | 
|---|
| 69 | offset += start; | 
|---|
| 70 |  | 
|---|
| 71 | if (offset & (align - 1)) { | 
|---|
| 72 | dev_err(&dev->dev, | 
|---|
| 73 | "virtio_pci: offset %u not aligned to %u\n", | 
|---|
| 74 | offset, align); | 
|---|
| 75 | return NULL; | 
|---|
| 76 | } | 
|---|
| 77 |  | 
|---|
| 78 | if (length > size) | 
|---|
| 79 | length = size; | 
|---|
| 80 |  | 
|---|
| 81 | if (len) | 
|---|
| 82 | *len = length; | 
|---|
| 83 |  | 
|---|
| 84 | if (minlen + offset < minlen || | 
|---|
| 85 | minlen + offset > pci_resource_len(dev, bar)) { | 
|---|
| 86 | dev_err(&dev->dev, | 
|---|
| 87 | "virtio_pci: map virtio %zu@%u " | 
|---|
| 88 | "out of range on bar %i length %lu\n", | 
|---|
| 89 | minlen, offset, | 
|---|
| 90 | bar, (unsigned long)pci_resource_len(dev, bar)); | 
|---|
| 91 | return NULL; | 
|---|
| 92 | } | 
|---|
| 93 |  | 
|---|
| 94 | p = pci_iomap_range(dev, bar, offset, maxlen: length); | 
|---|
| 95 | if (!p) | 
|---|
| 96 | dev_err(&dev->dev, | 
|---|
| 97 | "virtio_pci: unable to map virtio %u@%u on bar %i\n", | 
|---|
| 98 | length, offset, bar); | 
|---|
| 99 | else if (pa) | 
|---|
| 100 | *pa = pci_resource_start(dev, bar) + offset; | 
|---|
| 101 |  | 
|---|
| 102 | return p; | 
|---|
| 103 | } | 
|---|
| 104 |  | 
|---|
| 105 | /** | 
|---|
| 106 | * virtio_pci_find_capability - walk capabilities to find device info. | 
|---|
| 107 | * @dev: the pci device | 
|---|
| 108 | * @cfg_type: the VIRTIO_PCI_CAP_* value we seek | 
|---|
| 109 | * @ioresource_types: IORESOURCE_MEM and/or IORESOURCE_IO. | 
|---|
| 110 | * @bars: the bitmask of BARs | 
|---|
| 111 | * | 
|---|
| 112 | * Returns offset of the capability, or 0. | 
|---|
| 113 | */ | 
|---|
| 114 | static inline int virtio_pci_find_capability(struct pci_dev *dev, u8 cfg_type, | 
|---|
| 115 | u32 ioresource_types, int *bars) | 
|---|
| 116 | { | 
|---|
| 117 | int pos; | 
|---|
| 118 |  | 
|---|
| 119 | for (pos = pci_find_capability(dev, PCI_CAP_ID_VNDR); | 
|---|
| 120 | pos > 0; | 
|---|
| 121 | pos = pci_find_next_capability(dev, pos, PCI_CAP_ID_VNDR)) { | 
|---|
| 122 | u8 type, bar; | 
|---|
| 123 | pci_read_config_byte(dev, where: pos + offsetof(struct virtio_pci_cap, | 
|---|
| 124 | cfg_type), | 
|---|
| 125 | val: &type); | 
|---|
| 126 | pci_read_config_byte(dev, where: pos + offsetof(struct virtio_pci_cap, | 
|---|
| 127 | bar), | 
|---|
| 128 | val: &bar); | 
|---|
| 129 |  | 
|---|
| 130 | /* Ignore structures with reserved BAR values */ | 
|---|
| 131 | if (bar >= PCI_STD_NUM_BARS) | 
|---|
| 132 | continue; | 
|---|
| 133 |  | 
|---|
| 134 | if (type == cfg_type) { | 
|---|
| 135 | if (pci_resource_len(dev, bar) && | 
|---|
| 136 | pci_resource_flags(dev, bar) & ioresource_types) { | 
|---|
| 137 | *bars |= (1 << bar); | 
|---|
| 138 | return pos; | 
|---|
| 139 | } | 
|---|
| 140 | } | 
|---|
| 141 | } | 
|---|
| 142 | return 0; | 
|---|
| 143 | } | 
|---|
| 144 |  | 
|---|
| 145 | /* This is part of the ABI.  Don't screw with it. */ | 
|---|
| 146 | static inline void check_offsets(void) | 
|---|
| 147 | { | 
|---|
| 148 | /* Note: disk space was harmed in compilation of this function. */ | 
|---|
| 149 | BUILD_BUG_ON(VIRTIO_PCI_CAP_VNDR != | 
|---|
| 150 | offsetof(struct virtio_pci_cap, cap_vndr)); | 
|---|
| 151 | BUILD_BUG_ON(VIRTIO_PCI_CAP_NEXT != | 
|---|
| 152 | offsetof(struct virtio_pci_cap, cap_next)); | 
|---|
| 153 | BUILD_BUG_ON(VIRTIO_PCI_CAP_LEN != | 
|---|
| 154 | offsetof(struct virtio_pci_cap, cap_len)); | 
|---|
| 155 | BUILD_BUG_ON(VIRTIO_PCI_CAP_CFG_TYPE != | 
|---|
| 156 | offsetof(struct virtio_pci_cap, cfg_type)); | 
|---|
| 157 | BUILD_BUG_ON(VIRTIO_PCI_CAP_BAR != | 
|---|
| 158 | offsetof(struct virtio_pci_cap, bar)); | 
|---|
| 159 | BUILD_BUG_ON(VIRTIO_PCI_CAP_OFFSET != | 
|---|
| 160 | offsetof(struct virtio_pci_cap, offset)); | 
|---|
| 161 | BUILD_BUG_ON(VIRTIO_PCI_CAP_LENGTH != | 
|---|
| 162 | offsetof(struct virtio_pci_cap, length)); | 
|---|
| 163 | BUILD_BUG_ON(VIRTIO_PCI_NOTIFY_CAP_MULT != | 
|---|
| 164 | offsetof(struct virtio_pci_notify_cap, | 
|---|
| 165 | notify_off_multiplier)); | 
|---|
| 166 | BUILD_BUG_ON(VIRTIO_PCI_COMMON_DFSELECT != | 
|---|
| 167 | offsetof(struct virtio_pci_common_cfg, | 
|---|
| 168 | device_feature_select)); | 
|---|
| 169 | BUILD_BUG_ON(VIRTIO_PCI_COMMON_DF != | 
|---|
| 170 | offsetof(struct virtio_pci_common_cfg, device_feature)); | 
|---|
| 171 | BUILD_BUG_ON(VIRTIO_PCI_COMMON_GFSELECT != | 
|---|
| 172 | offsetof(struct virtio_pci_common_cfg, | 
|---|
| 173 | guest_feature_select)); | 
|---|
| 174 | BUILD_BUG_ON(VIRTIO_PCI_COMMON_GF != | 
|---|
| 175 | offsetof(struct virtio_pci_common_cfg, guest_feature)); | 
|---|
| 176 | BUILD_BUG_ON(VIRTIO_PCI_COMMON_MSIX != | 
|---|
| 177 | offsetof(struct virtio_pci_common_cfg, msix_config)); | 
|---|
| 178 | BUILD_BUG_ON(VIRTIO_PCI_COMMON_NUMQ != | 
|---|
| 179 | offsetof(struct virtio_pci_common_cfg, num_queues)); | 
|---|
| 180 | BUILD_BUG_ON(VIRTIO_PCI_COMMON_STATUS != | 
|---|
| 181 | offsetof(struct virtio_pci_common_cfg, device_status)); | 
|---|
| 182 | BUILD_BUG_ON(VIRTIO_PCI_COMMON_CFGGENERATION != | 
|---|
| 183 | offsetof(struct virtio_pci_common_cfg, config_generation)); | 
|---|
| 184 | BUILD_BUG_ON(VIRTIO_PCI_COMMON_Q_SELECT != | 
|---|
| 185 | offsetof(struct virtio_pci_common_cfg, queue_select)); | 
|---|
| 186 | BUILD_BUG_ON(VIRTIO_PCI_COMMON_Q_SIZE != | 
|---|
| 187 | offsetof(struct virtio_pci_common_cfg, queue_size)); | 
|---|
| 188 | BUILD_BUG_ON(VIRTIO_PCI_COMMON_Q_MSIX != | 
|---|
| 189 | offsetof(struct virtio_pci_common_cfg, queue_msix_vector)); | 
|---|
| 190 | BUILD_BUG_ON(VIRTIO_PCI_COMMON_Q_ENABLE != | 
|---|
| 191 | offsetof(struct virtio_pci_common_cfg, queue_enable)); | 
|---|
| 192 | BUILD_BUG_ON(VIRTIO_PCI_COMMON_Q_NOFF != | 
|---|
| 193 | offsetof(struct virtio_pci_common_cfg, queue_notify_off)); | 
|---|
| 194 | BUILD_BUG_ON(VIRTIO_PCI_COMMON_Q_DESCLO != | 
|---|
| 195 | offsetof(struct virtio_pci_common_cfg, queue_desc_lo)); | 
|---|
| 196 | BUILD_BUG_ON(VIRTIO_PCI_COMMON_Q_DESCHI != | 
|---|
| 197 | offsetof(struct virtio_pci_common_cfg, queue_desc_hi)); | 
|---|
| 198 | BUILD_BUG_ON(VIRTIO_PCI_COMMON_Q_AVAILLO != | 
|---|
| 199 | offsetof(struct virtio_pci_common_cfg, queue_avail_lo)); | 
|---|
| 200 | BUILD_BUG_ON(VIRTIO_PCI_COMMON_Q_AVAILHI != | 
|---|
| 201 | offsetof(struct virtio_pci_common_cfg, queue_avail_hi)); | 
|---|
| 202 | BUILD_BUG_ON(VIRTIO_PCI_COMMON_Q_USEDLO != | 
|---|
| 203 | offsetof(struct virtio_pci_common_cfg, queue_used_lo)); | 
|---|
| 204 | BUILD_BUG_ON(VIRTIO_PCI_COMMON_Q_USEDHI != | 
|---|
| 205 | offsetof(struct virtio_pci_common_cfg, queue_used_hi)); | 
|---|
| 206 | BUILD_BUG_ON(VIRTIO_PCI_COMMON_Q_NDATA != | 
|---|
| 207 | offsetof(struct virtio_pci_modern_common_cfg, queue_notify_data)); | 
|---|
| 208 | BUILD_BUG_ON(VIRTIO_PCI_COMMON_Q_RESET != | 
|---|
| 209 | offsetof(struct virtio_pci_modern_common_cfg, queue_reset)); | 
|---|
| 210 | BUILD_BUG_ON(VIRTIO_PCI_COMMON_ADM_Q_IDX != | 
|---|
| 211 | offsetof(struct virtio_pci_modern_common_cfg, admin_queue_index)); | 
|---|
| 212 | BUILD_BUG_ON(VIRTIO_PCI_COMMON_ADM_Q_NUM != | 
|---|
| 213 | offsetof(struct virtio_pci_modern_common_cfg, admin_queue_num)); | 
|---|
| 214 | } | 
|---|
| 215 |  | 
|---|
| 216 | /* | 
|---|
| 217 | * vp_modern_probe: probe the modern virtio pci device, note that the | 
|---|
| 218 | * caller is required to enable PCI device before calling this function. | 
|---|
| 219 | * @mdev: the modern virtio-pci device | 
|---|
| 220 | * | 
|---|
| 221 | * Return 0 on succeed otherwise fail | 
|---|
| 222 | */ | 
|---|
| 223 | int vp_modern_probe(struct virtio_pci_modern_device *mdev) | 
|---|
| 224 | { | 
|---|
| 225 | struct pci_dev *pci_dev = mdev->pci_dev; | 
|---|
| 226 | int err, common, isr, notify, device; | 
|---|
| 227 | u32 notify_length; | 
|---|
| 228 | u32 notify_offset; | 
|---|
| 229 | int devid; | 
|---|
| 230 |  | 
|---|
| 231 | check_offsets(); | 
|---|
| 232 |  | 
|---|
| 233 | if (mdev->device_id_check) { | 
|---|
| 234 | devid = mdev->device_id_check(pci_dev); | 
|---|
| 235 | if (devid < 0) | 
|---|
| 236 | return devid; | 
|---|
| 237 | mdev->id.device = devid; | 
|---|
| 238 | } else { | 
|---|
| 239 | /* We only own devices >= 0x1000 and <= 0x107f: leave the rest. */ | 
|---|
| 240 | if (pci_dev->device < 0x1000 || pci_dev->device > 0x107f) | 
|---|
| 241 | return -ENODEV; | 
|---|
| 242 |  | 
|---|
| 243 | if (pci_dev->device < 0x1040) { | 
|---|
| 244 | /* Transitional devices: use the PCI subsystem device id as | 
|---|
| 245 | * virtio device id, same as legacy driver always did. | 
|---|
| 246 | */ | 
|---|
| 247 | mdev->id.device = pci_dev->subsystem_device; | 
|---|
| 248 | } else { | 
|---|
| 249 | /* Modern devices: simply use PCI device id, but start from 0x1040. */ | 
|---|
| 250 | mdev->id.device = pci_dev->device - 0x1040; | 
|---|
| 251 | } | 
|---|
| 252 | } | 
|---|
| 253 | mdev->id.vendor = pci_dev->subsystem_vendor; | 
|---|
| 254 |  | 
|---|
| 255 | /* check for a common config: if not, use legacy mode (bar 0). */ | 
|---|
| 256 | common = virtio_pci_find_capability(dev: pci_dev, VIRTIO_PCI_CAP_COMMON_CFG, | 
|---|
| 257 | IORESOURCE_IO | IORESOURCE_MEM, | 
|---|
| 258 | bars: &mdev->modern_bars); | 
|---|
| 259 | if (!common) { | 
|---|
| 260 | dev_info(&pci_dev->dev, | 
|---|
| 261 | "virtio_pci: leaving for legacy driver\n"); | 
|---|
| 262 | return -ENODEV; | 
|---|
| 263 | } | 
|---|
| 264 |  | 
|---|
| 265 | /* If common is there, these should be too... */ | 
|---|
| 266 | isr = virtio_pci_find_capability(dev: pci_dev, VIRTIO_PCI_CAP_ISR_CFG, | 
|---|
| 267 | IORESOURCE_IO | IORESOURCE_MEM, | 
|---|
| 268 | bars: &mdev->modern_bars); | 
|---|
| 269 | notify = virtio_pci_find_capability(dev: pci_dev, VIRTIO_PCI_CAP_NOTIFY_CFG, | 
|---|
| 270 | IORESOURCE_IO | IORESOURCE_MEM, | 
|---|
| 271 | bars: &mdev->modern_bars); | 
|---|
| 272 | if (!isr || !notify) { | 
|---|
| 273 | dev_err(&pci_dev->dev, | 
|---|
| 274 | "virtio_pci: missing capabilities %i/%i/%i\n", | 
|---|
| 275 | common, isr, notify); | 
|---|
| 276 | return -EINVAL; | 
|---|
| 277 | } | 
|---|
| 278 |  | 
|---|
| 279 | err = dma_set_mask_and_coherent(dev: &pci_dev->dev, | 
|---|
| 280 | mask: mdev->dma_mask ? : DMA_BIT_MASK(64)); | 
|---|
| 281 | if (err) | 
|---|
| 282 | err = dma_set_mask_and_coherent(dev: &pci_dev->dev, | 
|---|
| 283 | DMA_BIT_MASK(32)); | 
|---|
| 284 | if (err) | 
|---|
| 285 | dev_warn(&pci_dev->dev, "Failed to enable 64-bit or 32-bit DMA.  Trying to continue, but this might not work.\n"); | 
|---|
| 286 |  | 
|---|
| 287 | /* Device capability is only mandatory for devices that have | 
|---|
| 288 | * device-specific configuration. | 
|---|
| 289 | */ | 
|---|
| 290 | device = virtio_pci_find_capability(dev: pci_dev, VIRTIO_PCI_CAP_DEVICE_CFG, | 
|---|
| 291 | IORESOURCE_IO | IORESOURCE_MEM, | 
|---|
| 292 | bars: &mdev->modern_bars); | 
|---|
| 293 |  | 
|---|
| 294 | err = pci_request_selected_regions(pci_dev, mdev->modern_bars, | 
|---|
| 295 | "virtio-pci-modern"); | 
|---|
| 296 | if (err) | 
|---|
| 297 | return err; | 
|---|
| 298 |  | 
|---|
| 299 | err = -EINVAL; | 
|---|
| 300 | mdev->common = vp_modern_map_capability(mdev, off: common, | 
|---|
| 301 | minlen: sizeof(struct virtio_pci_common_cfg), align: 4, start: 0, | 
|---|
| 302 | offsetofend(struct virtio_pci_modern_common_cfg, | 
|---|
| 303 | admin_queue_num), | 
|---|
| 304 | len: &mdev->common_len, NULL); | 
|---|
| 305 | if (!mdev->common) | 
|---|
| 306 | goto err_map_common; | 
|---|
| 307 | mdev->isr = vp_modern_map_capability(mdev, off: isr, minlen: sizeof(u8), align: 1, | 
|---|
| 308 | start: 0, size: 1, | 
|---|
| 309 | NULL, NULL); | 
|---|
| 310 | if (!mdev->isr) | 
|---|
| 311 | goto err_map_isr; | 
|---|
| 312 |  | 
|---|
| 313 | /* Read notify_off_multiplier from config space. */ | 
|---|
| 314 | pci_read_config_dword(dev: pci_dev, | 
|---|
| 315 | where: notify + offsetof(struct virtio_pci_notify_cap, | 
|---|
| 316 | notify_off_multiplier), | 
|---|
| 317 | val: &mdev->notify_offset_multiplier); | 
|---|
| 318 | /* Read notify length and offset from config space. */ | 
|---|
| 319 | pci_read_config_dword(dev: pci_dev, | 
|---|
| 320 | where: notify + offsetof(struct virtio_pci_notify_cap, | 
|---|
| 321 | cap.length), | 
|---|
| 322 | val: ¬ify_length); | 
|---|
| 323 |  | 
|---|
| 324 | pci_read_config_dword(dev: pci_dev, | 
|---|
| 325 | where: notify + offsetof(struct virtio_pci_notify_cap, | 
|---|
| 326 | cap.offset), | 
|---|
| 327 | val: ¬ify_offset); | 
|---|
| 328 |  | 
|---|
| 329 | /* We don't know how many VQs we'll map, ahead of the time. | 
|---|
| 330 | * If notify length is small, map it all now. | 
|---|
| 331 | * Otherwise, map each VQ individually later. | 
|---|
| 332 | */ | 
|---|
| 333 | if ((u64)notify_length + (notify_offset % PAGE_SIZE) <= PAGE_SIZE) { | 
|---|
| 334 | mdev->notify_base = vp_modern_map_capability(mdev, off: notify, | 
|---|
| 335 | minlen: 2, align: 2, | 
|---|
| 336 | start: 0, size: notify_length, | 
|---|
| 337 | len: &mdev->notify_len, | 
|---|
| 338 | pa: &mdev->notify_pa); | 
|---|
| 339 | if (!mdev->notify_base) | 
|---|
| 340 | goto err_map_notify; | 
|---|
| 341 | } else { | 
|---|
| 342 | mdev->notify_map_cap = notify; | 
|---|
| 343 | } | 
|---|
| 344 |  | 
|---|
| 345 | /* Again, we don't know how much we should map, but PAGE_SIZE | 
|---|
| 346 | * is more than enough for all existing devices. | 
|---|
| 347 | */ | 
|---|
| 348 | if (device) { | 
|---|
| 349 | mdev->device = vp_modern_map_capability(mdev, off: device, minlen: 0, align: 4, | 
|---|
| 350 | start: 0, PAGE_SIZE, | 
|---|
| 351 | len: &mdev->device_len, | 
|---|
| 352 | NULL); | 
|---|
| 353 | if (!mdev->device) | 
|---|
| 354 | goto err_map_device; | 
|---|
| 355 | } | 
|---|
| 356 |  | 
|---|
| 357 | return 0; | 
|---|
| 358 |  | 
|---|
| 359 | err_map_device: | 
|---|
| 360 | if (mdev->notify_base) | 
|---|
| 361 | pci_iounmap(dev: pci_dev, mdev->notify_base); | 
|---|
| 362 | err_map_notify: | 
|---|
| 363 | pci_iounmap(dev: pci_dev, mdev->isr); | 
|---|
| 364 | err_map_isr: | 
|---|
| 365 | pci_iounmap(dev: pci_dev, mdev->common); | 
|---|
| 366 | err_map_common: | 
|---|
| 367 | pci_release_selected_regions(pci_dev, mdev->modern_bars); | 
|---|
| 368 | return err; | 
|---|
| 369 | } | 
|---|
| 370 | EXPORT_SYMBOL_GPL(vp_modern_probe); | 
|---|
| 371 |  | 
|---|
| 372 | /* | 
|---|
| 373 | * vp_modern_remove: remove and cleanup the modern virtio pci device | 
|---|
| 374 | * @mdev: the modern virtio-pci device | 
|---|
| 375 | */ | 
|---|
| 376 | void vp_modern_remove(struct virtio_pci_modern_device *mdev) | 
|---|
| 377 | { | 
|---|
| 378 | struct pci_dev *pci_dev = mdev->pci_dev; | 
|---|
| 379 |  | 
|---|
| 380 | if (mdev->device) | 
|---|
| 381 | pci_iounmap(dev: pci_dev, mdev->device); | 
|---|
| 382 | if (mdev->notify_base) | 
|---|
| 383 | pci_iounmap(dev: pci_dev, mdev->notify_base); | 
|---|
| 384 | pci_iounmap(dev: pci_dev, mdev->isr); | 
|---|
| 385 | pci_iounmap(dev: pci_dev, mdev->common); | 
|---|
| 386 | pci_release_selected_regions(pci_dev, mdev->modern_bars); | 
|---|
| 387 | } | 
|---|
| 388 | EXPORT_SYMBOL_GPL(vp_modern_remove); | 
|---|
| 389 |  | 
|---|
| 390 | /* | 
|---|
| 391 | * vp_modern_get_extended_features - get features from device | 
|---|
| 392 | * @mdev: the modern virtio-pci device | 
|---|
| 393 | * @features: the features array to be filled | 
|---|
| 394 | * | 
|---|
| 395 | * Fill the specified features array with the features read from the device | 
|---|
| 396 | */ | 
|---|
| 397 | void vp_modern_get_extended_features(struct virtio_pci_modern_device *mdev, | 
|---|
| 398 | u64 *features) | 
|---|
| 399 | { | 
|---|
| 400 | struct virtio_pci_common_cfg __iomem *cfg = mdev->common; | 
|---|
| 401 | int i; | 
|---|
| 402 |  | 
|---|
| 403 | virtio_features_zero(features); | 
|---|
| 404 | for (i = 0; i < VIRTIO_FEATURES_WORDS; i++) { | 
|---|
| 405 | u64 cur; | 
|---|
| 406 |  | 
|---|
| 407 | vp_iowrite32(value: i, addr: &cfg->device_feature_select); | 
|---|
| 408 | cur = vp_ioread32(addr: &cfg->device_feature); | 
|---|
| 409 | features[i >> 1] |= cur << (32 * (i & 1)); | 
|---|
| 410 | } | 
|---|
| 411 | } | 
|---|
| 412 | EXPORT_SYMBOL_GPL(vp_modern_get_extended_features); | 
|---|
| 413 |  | 
|---|
| 414 | /* | 
|---|
| 415 | * vp_modern_get_driver_features - get driver features from device | 
|---|
| 416 | * @mdev: the modern virtio-pci device | 
|---|
| 417 | * @features: the features array to be filled | 
|---|
| 418 | * | 
|---|
| 419 | * Fill the specified features array with the driver features read from the | 
|---|
| 420 | * device | 
|---|
| 421 | */ | 
|---|
| 422 | void | 
|---|
| 423 | vp_modern_get_driver_extended_features(struct virtio_pci_modern_device *mdev, | 
|---|
| 424 | u64 *features) | 
|---|
| 425 | { | 
|---|
| 426 | struct virtio_pci_common_cfg __iomem *cfg = mdev->common; | 
|---|
| 427 | int i; | 
|---|
| 428 |  | 
|---|
| 429 | virtio_features_zero(features); | 
|---|
| 430 | for (i = 0; i < VIRTIO_FEATURES_WORDS; i++) { | 
|---|
| 431 | u64 cur; | 
|---|
| 432 |  | 
|---|
| 433 | vp_iowrite32(value: i, addr: &cfg->guest_feature_select); | 
|---|
| 434 | cur = vp_ioread32(addr: &cfg->guest_feature); | 
|---|
| 435 | features[i >> 1] |= cur << (32 * (i & 1)); | 
|---|
| 436 | } | 
|---|
| 437 | } | 
|---|
| 438 | EXPORT_SYMBOL_GPL(vp_modern_get_driver_extended_features); | 
|---|
| 439 |  | 
|---|
| 440 | /* | 
|---|
| 441 | * vp_modern_set_extended_features - set features to device | 
|---|
| 442 | * @mdev: the modern virtio-pci device | 
|---|
| 443 | * @features: the features set to device | 
|---|
| 444 | */ | 
|---|
| 445 | void vp_modern_set_extended_features(struct virtio_pci_modern_device *mdev, | 
|---|
| 446 | const u64 *features) | 
|---|
| 447 | { | 
|---|
| 448 | struct virtio_pci_common_cfg __iomem *cfg = mdev->common; | 
|---|
| 449 | int i; | 
|---|
| 450 |  | 
|---|
| 451 | for (i = 0; i < VIRTIO_FEATURES_WORDS; i++) { | 
|---|
| 452 | u32 cur = features[i >> 1] >> (32 * (i & 1)); | 
|---|
| 453 |  | 
|---|
| 454 | vp_iowrite32(value: i, addr: &cfg->guest_feature_select); | 
|---|
| 455 | vp_iowrite32(value: cur, addr: &cfg->guest_feature); | 
|---|
| 456 | } | 
|---|
| 457 | } | 
|---|
| 458 | EXPORT_SYMBOL_GPL(vp_modern_set_extended_features); | 
|---|
| 459 |  | 
|---|
| 460 | /* | 
|---|
| 461 | * vp_modern_generation - get the device genreation | 
|---|
| 462 | * @mdev: the modern virtio-pci device | 
|---|
| 463 | * | 
|---|
| 464 | * Returns the genreation read from device | 
|---|
| 465 | */ | 
|---|
| 466 | u32 vp_modern_generation(struct virtio_pci_modern_device *mdev) | 
|---|
| 467 | { | 
|---|
| 468 | struct virtio_pci_common_cfg __iomem *cfg = mdev->common; | 
|---|
| 469 |  | 
|---|
| 470 | return vp_ioread8(addr: &cfg->config_generation); | 
|---|
| 471 | } | 
|---|
| 472 | EXPORT_SYMBOL_GPL(vp_modern_generation); | 
|---|
| 473 |  | 
|---|
| 474 | /* | 
|---|
| 475 | * vp_modern_get_status - get the device status | 
|---|
| 476 | * @mdev: the modern virtio-pci device | 
|---|
| 477 | * | 
|---|
| 478 | * Returns the status read from device | 
|---|
| 479 | */ | 
|---|
| 480 | u8 vp_modern_get_status(struct virtio_pci_modern_device *mdev) | 
|---|
| 481 | { | 
|---|
| 482 | struct virtio_pci_common_cfg __iomem *cfg = mdev->common; | 
|---|
| 483 |  | 
|---|
| 484 | return vp_ioread8(addr: &cfg->device_status); | 
|---|
| 485 | } | 
|---|
| 486 | EXPORT_SYMBOL_GPL(vp_modern_get_status); | 
|---|
| 487 |  | 
|---|
| 488 | /* | 
|---|
| 489 | * vp_modern_set_status - set status to device | 
|---|
| 490 | * @mdev: the modern virtio-pci device | 
|---|
| 491 | * @status: the status set to device | 
|---|
| 492 | */ | 
|---|
| 493 | void vp_modern_set_status(struct virtio_pci_modern_device *mdev, | 
|---|
| 494 | u8 status) | 
|---|
| 495 | { | 
|---|
| 496 | struct virtio_pci_common_cfg __iomem *cfg = mdev->common; | 
|---|
| 497 |  | 
|---|
| 498 | /* | 
|---|
| 499 | * Per memory-barriers.txt, wmb() is not needed to guarantee | 
|---|
| 500 | * that the cache coherent memory writes have completed | 
|---|
| 501 | * before writing to the MMIO region. | 
|---|
| 502 | */ | 
|---|
| 503 | vp_iowrite8(value: status, addr: &cfg->device_status); | 
|---|
| 504 | } | 
|---|
| 505 | EXPORT_SYMBOL_GPL(vp_modern_set_status); | 
|---|
| 506 |  | 
|---|
| 507 | /* | 
|---|
| 508 | * vp_modern_get_queue_reset - get the queue reset status | 
|---|
| 509 | * @mdev: the modern virtio-pci device | 
|---|
| 510 | * @index: queue index | 
|---|
| 511 | */ | 
|---|
| 512 | int vp_modern_get_queue_reset(struct virtio_pci_modern_device *mdev, u16 index) | 
|---|
| 513 | { | 
|---|
| 514 | struct virtio_pci_modern_common_cfg __iomem *cfg; | 
|---|
| 515 |  | 
|---|
| 516 | cfg = (struct virtio_pci_modern_common_cfg __iomem *)mdev->common; | 
|---|
| 517 |  | 
|---|
| 518 | vp_iowrite16(value: index, addr: &cfg->cfg.queue_select); | 
|---|
| 519 | return vp_ioread16(addr: &cfg->queue_reset); | 
|---|
| 520 | } | 
|---|
| 521 | EXPORT_SYMBOL_GPL(vp_modern_get_queue_reset); | 
|---|
| 522 |  | 
|---|
| 523 | /* | 
|---|
| 524 | * vp_modern_set_queue_reset - reset the queue | 
|---|
| 525 | * @mdev: the modern virtio-pci device | 
|---|
| 526 | * @index: queue index | 
|---|
| 527 | */ | 
|---|
| 528 | void vp_modern_set_queue_reset(struct virtio_pci_modern_device *mdev, u16 index) | 
|---|
| 529 | { | 
|---|
| 530 | struct virtio_pci_modern_common_cfg __iomem *cfg; | 
|---|
| 531 |  | 
|---|
| 532 | cfg = (struct virtio_pci_modern_common_cfg __iomem *)mdev->common; | 
|---|
| 533 |  | 
|---|
| 534 | vp_iowrite16(value: index, addr: &cfg->cfg.queue_select); | 
|---|
| 535 | vp_iowrite16(value: 1, addr: &cfg->queue_reset); | 
|---|
| 536 |  | 
|---|
| 537 | while (vp_ioread16(addr: &cfg->queue_reset)) | 
|---|
| 538 | msleep(msecs: 1); | 
|---|
| 539 |  | 
|---|
| 540 | while (vp_ioread16(addr: &cfg->cfg.queue_enable)) | 
|---|
| 541 | msleep(msecs: 1); | 
|---|
| 542 | } | 
|---|
| 543 | EXPORT_SYMBOL_GPL(vp_modern_set_queue_reset); | 
|---|
| 544 |  | 
|---|
| 545 | /* | 
|---|
| 546 | * vp_modern_queue_vector - set the MSIX vector for a specific virtqueue | 
|---|
| 547 | * @mdev: the modern virtio-pci device | 
|---|
| 548 | * @index: queue index | 
|---|
| 549 | * @vector: the queue vector | 
|---|
| 550 | * | 
|---|
| 551 | * Returns the queue vector read from the device | 
|---|
| 552 | */ | 
|---|
| 553 | u16 vp_modern_queue_vector(struct virtio_pci_modern_device *mdev, | 
|---|
| 554 | u16 index, u16 vector) | 
|---|
| 555 | { | 
|---|
| 556 | struct virtio_pci_common_cfg __iomem *cfg = mdev->common; | 
|---|
| 557 |  | 
|---|
| 558 | vp_iowrite16(value: index, addr: &cfg->queue_select); | 
|---|
| 559 | vp_iowrite16(value: vector, addr: &cfg->queue_msix_vector); | 
|---|
| 560 | /* Flush the write out to device */ | 
|---|
| 561 | return vp_ioread16(addr: &cfg->queue_msix_vector); | 
|---|
| 562 | } | 
|---|
| 563 | EXPORT_SYMBOL_GPL(vp_modern_queue_vector); | 
|---|
| 564 |  | 
|---|
| 565 | /* | 
|---|
| 566 | * vp_modern_config_vector - set the vector for config interrupt | 
|---|
| 567 | * @mdev: the modern virtio-pci device | 
|---|
| 568 | * @vector: the config vector | 
|---|
| 569 | * | 
|---|
| 570 | * Returns the config vector read from the device | 
|---|
| 571 | */ | 
|---|
| 572 | u16 vp_modern_config_vector(struct virtio_pci_modern_device *mdev, | 
|---|
| 573 | u16 vector) | 
|---|
| 574 | { | 
|---|
| 575 | struct virtio_pci_common_cfg __iomem *cfg = mdev->common; | 
|---|
| 576 |  | 
|---|
| 577 | /* Setup the vector used for configuration events */ | 
|---|
| 578 | vp_iowrite16(value: vector, addr: &cfg->msix_config); | 
|---|
| 579 | /* Verify we had enough resources to assign the vector */ | 
|---|
| 580 | /* Will also flush the write out to device */ | 
|---|
| 581 | return vp_ioread16(addr: &cfg->msix_config); | 
|---|
| 582 | } | 
|---|
| 583 | EXPORT_SYMBOL_GPL(vp_modern_config_vector); | 
|---|
| 584 |  | 
|---|
| 585 | /* | 
|---|
| 586 | * vp_modern_queue_address - set the virtqueue address | 
|---|
| 587 | * @mdev: the modern virtio-pci device | 
|---|
| 588 | * @index: the queue index | 
|---|
| 589 | * @desc_addr: address of the descriptor area | 
|---|
| 590 | * @driver_addr: address of the driver area | 
|---|
| 591 | * @device_addr: address of the device area | 
|---|
| 592 | */ | 
|---|
| 593 | void vp_modern_queue_address(struct virtio_pci_modern_device *mdev, | 
|---|
| 594 | u16 index, u64 desc_addr, u64 driver_addr, | 
|---|
| 595 | u64 device_addr) | 
|---|
| 596 | { | 
|---|
| 597 | struct virtio_pci_common_cfg __iomem *cfg = mdev->common; | 
|---|
| 598 |  | 
|---|
| 599 | vp_iowrite16(value: index, addr: &cfg->queue_select); | 
|---|
| 600 |  | 
|---|
| 601 | vp_iowrite64_twopart(val: desc_addr, lo: &cfg->queue_desc_lo, | 
|---|
| 602 | hi: &cfg->queue_desc_hi); | 
|---|
| 603 | vp_iowrite64_twopart(val: driver_addr, lo: &cfg->queue_avail_lo, | 
|---|
| 604 | hi: &cfg->queue_avail_hi); | 
|---|
| 605 | vp_iowrite64_twopart(val: device_addr, lo: &cfg->queue_used_lo, | 
|---|
| 606 | hi: &cfg->queue_used_hi); | 
|---|
| 607 | } | 
|---|
| 608 | EXPORT_SYMBOL_GPL(vp_modern_queue_address); | 
|---|
| 609 |  | 
|---|
| 610 | /* | 
|---|
| 611 | * vp_modern_set_queue_enable - enable a virtqueue | 
|---|
| 612 | * @mdev: the modern virtio-pci device | 
|---|
| 613 | * @index: the queue index | 
|---|
| 614 | * @enable: whether the virtqueue is enable or not | 
|---|
| 615 | */ | 
|---|
| 616 | void vp_modern_set_queue_enable(struct virtio_pci_modern_device *mdev, | 
|---|
| 617 | u16 index, bool enable) | 
|---|
| 618 | { | 
|---|
| 619 | vp_iowrite16(value: index, addr: &mdev->common->queue_select); | 
|---|
| 620 | vp_iowrite16(value: enable, addr: &mdev->common->queue_enable); | 
|---|
| 621 | } | 
|---|
| 622 | EXPORT_SYMBOL_GPL(vp_modern_set_queue_enable); | 
|---|
| 623 |  | 
|---|
| 624 | /* | 
|---|
| 625 | * vp_modern_get_queue_enable - enable a virtqueue | 
|---|
| 626 | * @mdev: the modern virtio-pci device | 
|---|
| 627 | * @index: the queue index | 
|---|
| 628 | * | 
|---|
| 629 | * Returns whether a virtqueue is enabled or not | 
|---|
| 630 | */ | 
|---|
| 631 | bool vp_modern_get_queue_enable(struct virtio_pci_modern_device *mdev, | 
|---|
| 632 | u16 index) | 
|---|
| 633 | { | 
|---|
| 634 | vp_iowrite16(value: index, addr: &mdev->common->queue_select); | 
|---|
| 635 |  | 
|---|
| 636 | return vp_ioread16(addr: &mdev->common->queue_enable); | 
|---|
| 637 | } | 
|---|
| 638 | EXPORT_SYMBOL_GPL(vp_modern_get_queue_enable); | 
|---|
| 639 |  | 
|---|
| 640 | /* | 
|---|
| 641 | * vp_modern_set_queue_size - set size for a virtqueue | 
|---|
| 642 | * @mdev: the modern virtio-pci device | 
|---|
| 643 | * @index: the queue index | 
|---|
| 644 | * @size: the size of the virtqueue | 
|---|
| 645 | */ | 
|---|
| 646 | void vp_modern_set_queue_size(struct virtio_pci_modern_device *mdev, | 
|---|
| 647 | u16 index, u16 size) | 
|---|
| 648 | { | 
|---|
| 649 | vp_iowrite16(value: index, addr: &mdev->common->queue_select); | 
|---|
| 650 | vp_iowrite16(value: size, addr: &mdev->common->queue_size); | 
|---|
| 651 |  | 
|---|
| 652 | } | 
|---|
| 653 | EXPORT_SYMBOL_GPL(vp_modern_set_queue_size); | 
|---|
| 654 |  | 
|---|
| 655 | /* | 
|---|
| 656 | * vp_modern_get_queue_size - get size for a virtqueue | 
|---|
| 657 | * @mdev: the modern virtio-pci device | 
|---|
| 658 | * @index: the queue index | 
|---|
| 659 | * | 
|---|
| 660 | * Returns the size of the virtqueue | 
|---|
| 661 | */ | 
|---|
| 662 | u16 vp_modern_get_queue_size(struct virtio_pci_modern_device *mdev, | 
|---|
| 663 | u16 index) | 
|---|
| 664 | { | 
|---|
| 665 | vp_iowrite16(value: index, addr: &mdev->common->queue_select); | 
|---|
| 666 |  | 
|---|
| 667 | return vp_ioread16(addr: &mdev->common->queue_size); | 
|---|
| 668 |  | 
|---|
| 669 | } | 
|---|
| 670 | EXPORT_SYMBOL_GPL(vp_modern_get_queue_size); | 
|---|
| 671 |  | 
|---|
| 672 | /* | 
|---|
| 673 | * vp_modern_get_num_queues - get the number of virtqueues | 
|---|
| 674 | * @mdev: the modern virtio-pci device | 
|---|
| 675 | * | 
|---|
| 676 | * Returns the number of virtqueues | 
|---|
| 677 | */ | 
|---|
| 678 | u16 vp_modern_get_num_queues(struct virtio_pci_modern_device *mdev) | 
|---|
| 679 | { | 
|---|
| 680 | return vp_ioread16(addr: &mdev->common->num_queues); | 
|---|
| 681 | } | 
|---|
| 682 | EXPORT_SYMBOL_GPL(vp_modern_get_num_queues); | 
|---|
| 683 |  | 
|---|
| 684 | /* | 
|---|
| 685 | * vp_modern_get_queue_notify_off - get notification offset for a virtqueue | 
|---|
| 686 | * @mdev: the modern virtio-pci device | 
|---|
| 687 | * @index: the queue index | 
|---|
| 688 | * | 
|---|
| 689 | * Returns the notification offset for a virtqueue | 
|---|
| 690 | */ | 
|---|
| 691 | static u16 vp_modern_get_queue_notify_off(struct virtio_pci_modern_device *mdev, | 
|---|
| 692 | u16 index) | 
|---|
| 693 | { | 
|---|
| 694 | vp_iowrite16(value: index, addr: &mdev->common->queue_select); | 
|---|
| 695 |  | 
|---|
| 696 | return vp_ioread16(addr: &mdev->common->queue_notify_off); | 
|---|
| 697 | } | 
|---|
| 698 |  | 
|---|
| 699 | /* | 
|---|
| 700 | * vp_modern_map_vq_notify - map notification area for a | 
|---|
| 701 | * specific virtqueue | 
|---|
| 702 | * @mdev: the modern virtio-pci device | 
|---|
| 703 | * @index: the queue index | 
|---|
| 704 | * @pa: the pointer to the physical address of the nofity area | 
|---|
| 705 | * | 
|---|
| 706 | * Returns the address of the notification area | 
|---|
| 707 | */ | 
|---|
| 708 | void __iomem *vp_modern_map_vq_notify(struct virtio_pci_modern_device *mdev, | 
|---|
| 709 | u16 index, resource_size_t *pa) | 
|---|
| 710 | { | 
|---|
| 711 | u16 off = vp_modern_get_queue_notify_off(mdev, index); | 
|---|
| 712 |  | 
|---|
| 713 | if (mdev->notify_base) { | 
|---|
| 714 | /* offset should not wrap */ | 
|---|
| 715 | if ((u64)off * mdev->notify_offset_multiplier + 2 | 
|---|
| 716 | > mdev->notify_len) { | 
|---|
| 717 | dev_warn(&mdev->pci_dev->dev, | 
|---|
| 718 | "bad notification offset %u (x %u) " | 
|---|
| 719 | "for queue %u > %zd", | 
|---|
| 720 | off, mdev->notify_offset_multiplier, | 
|---|
| 721 | index, mdev->notify_len); | 
|---|
| 722 | return NULL; | 
|---|
| 723 | } | 
|---|
| 724 | if (pa) | 
|---|
| 725 | *pa = mdev->notify_pa + | 
|---|
| 726 | off * mdev->notify_offset_multiplier; | 
|---|
| 727 | return mdev->notify_base + off * mdev->notify_offset_multiplier; | 
|---|
| 728 | } else { | 
|---|
| 729 | return vp_modern_map_capability(mdev, | 
|---|
| 730 | off: mdev->notify_map_cap, minlen: 2, align: 2, | 
|---|
| 731 | start: off * mdev->notify_offset_multiplier, size: 2, | 
|---|
| 732 | NULL, pa); | 
|---|
| 733 | } | 
|---|
| 734 | } | 
|---|
| 735 | EXPORT_SYMBOL_GPL(vp_modern_map_vq_notify); | 
|---|
| 736 |  | 
|---|
| 737 | u16 vp_modern_avq_num(struct virtio_pci_modern_device *mdev) | 
|---|
| 738 | { | 
|---|
| 739 | struct virtio_pci_modern_common_cfg __iomem *cfg; | 
|---|
| 740 |  | 
|---|
| 741 | cfg = (struct virtio_pci_modern_common_cfg __iomem *)mdev->common; | 
|---|
| 742 | return vp_ioread16(addr: &cfg->admin_queue_num); | 
|---|
| 743 | } | 
|---|
| 744 | EXPORT_SYMBOL_GPL(vp_modern_avq_num); | 
|---|
| 745 |  | 
|---|
| 746 | u16 vp_modern_avq_index(struct virtio_pci_modern_device *mdev) | 
|---|
| 747 | { | 
|---|
| 748 | struct virtio_pci_modern_common_cfg __iomem *cfg; | 
|---|
| 749 |  | 
|---|
| 750 | cfg = (struct virtio_pci_modern_common_cfg __iomem *)mdev->common; | 
|---|
| 751 | return vp_ioread16(addr: &cfg->admin_queue_index); | 
|---|
| 752 | } | 
|---|
| 753 | EXPORT_SYMBOL_GPL(vp_modern_avq_index); | 
|---|
| 754 |  | 
|---|
| 755 | MODULE_VERSION( "0.1"); | 
|---|
| 756 | MODULE_DESCRIPTION( "Modern Virtio PCI Device"); | 
|---|
| 757 | MODULE_AUTHOR( "Jason Wang <jasowang@redhat.com>"); | 
|---|
| 758 | MODULE_LICENSE( "GPL"); | 
|---|
| 759 |  | 
|---|