| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Virtio PCI driver - modern (virtio 1.0) device support |
|---|
| 3 | 4 | * |
|---|
| .. | .. |
|---|
| 11 | 12 | * Anthony Liguori <aliguori@us.ibm.com> |
|---|
| 12 | 13 | * Rusty Russell <rusty@rustcorp.com.au> |
|---|
| 13 | 14 | * Michael S. Tsirkin <mst@redhat.com> |
|---|
| 14 | | - * |
|---|
| 15 | | - * This work is licensed under the terms of the GNU GPL, version 2 or later. |
|---|
| 16 | | - * See the COPYING file in the top-level directory. |
|---|
| 17 | | - * |
|---|
| 18 | 15 | */ |
|---|
| 19 | 16 | |
|---|
| 20 | 17 | #include <linux/delay.h> |
|---|
| 21 | 18 | #define VIRTIO_PCI_NO_LEGACY |
|---|
| 19 | +#define VIRTIO_RING_NO_LEGACY |
|---|
| 22 | 20 | #include "virtio_pci_common.h" |
|---|
| 23 | 21 | |
|---|
| 24 | 22 | /* |
|---|
| .. | .. |
|---|
| 29 | 27 | * method, i.e. 32-bit accesses for 32-bit fields, 16-bit accesses |
|---|
| 30 | 28 | * for 16-bit fields and 8-bit accesses for 8-bit fields. |
|---|
| 31 | 29 | */ |
|---|
| 32 | | -static inline u8 vp_ioread8(u8 __iomem *addr) |
|---|
| 30 | +static inline u8 vp_ioread8(const u8 __iomem *addr) |
|---|
| 33 | 31 | { |
|---|
| 34 | 32 | return ioread8(addr); |
|---|
| 35 | 33 | } |
|---|
| 36 | | -static inline u16 vp_ioread16 (__le16 __iomem *addr) |
|---|
| 34 | +static inline u16 vp_ioread16 (const __le16 __iomem *addr) |
|---|
| 37 | 35 | { |
|---|
| 38 | 36 | return ioread16(addr); |
|---|
| 39 | 37 | } |
|---|
| 40 | 38 | |
|---|
| 41 | | -static inline u32 vp_ioread32(__le32 __iomem *addr) |
|---|
| 39 | +static inline u32 vp_ioread32(const __le32 __iomem *addr) |
|---|
| 42 | 40 | { |
|---|
| 43 | 41 | return ioread32(addr); |
|---|
| 44 | 42 | } |
|---|
| .. | .. |
|---|
| 446 | 444 | vring_del_virtqueue(vq); |
|---|
| 447 | 445 | } |
|---|
| 448 | 446 | |
|---|
| 447 | +static int virtio_pci_find_shm_cap(struct pci_dev *dev, u8 required_id, |
|---|
| 448 | + u8 *bar, u64 *offset, u64 *len) |
|---|
| 449 | +{ |
|---|
| 450 | + int pos; |
|---|
| 451 | + |
|---|
| 452 | + for (pos = pci_find_capability(dev, PCI_CAP_ID_VNDR); pos > 0; |
|---|
| 453 | + pos = pci_find_next_capability(dev, pos, PCI_CAP_ID_VNDR)) { |
|---|
| 454 | + u8 type, cap_len, id; |
|---|
| 455 | + u32 tmp32; |
|---|
| 456 | + u64 res_offset, res_length; |
|---|
| 457 | + |
|---|
| 458 | + pci_read_config_byte(dev, pos + offsetof(struct virtio_pci_cap, |
|---|
| 459 | + cfg_type), &type); |
|---|
| 460 | + if (type != VIRTIO_PCI_CAP_SHARED_MEMORY_CFG) |
|---|
| 461 | + continue; |
|---|
| 462 | + |
|---|
| 463 | + pci_read_config_byte(dev, pos + offsetof(struct virtio_pci_cap, |
|---|
| 464 | + cap_len), &cap_len); |
|---|
| 465 | + if (cap_len != sizeof(struct virtio_pci_cap64)) { |
|---|
| 466 | + dev_err(&dev->dev, "%s: shm cap with bad size offset:" |
|---|
| 467 | + " %d size: %d\n", __func__, pos, cap_len); |
|---|
| 468 | + continue; |
|---|
| 469 | + } |
|---|
| 470 | + |
|---|
| 471 | + pci_read_config_byte(dev, pos + offsetof(struct virtio_pci_cap, |
|---|
| 472 | + id), &id); |
|---|
| 473 | + if (id != required_id) |
|---|
| 474 | + continue; |
|---|
| 475 | + |
|---|
| 476 | + /* Type, and ID match, looks good */ |
|---|
| 477 | + pci_read_config_byte(dev, pos + offsetof(struct virtio_pci_cap, |
|---|
| 478 | + bar), bar); |
|---|
| 479 | + |
|---|
| 480 | + /* Read the lower 32bit of length and offset */ |
|---|
| 481 | + pci_read_config_dword(dev, pos + offsetof(struct virtio_pci_cap, |
|---|
| 482 | + offset), &tmp32); |
|---|
| 483 | + res_offset = tmp32; |
|---|
| 484 | + pci_read_config_dword(dev, pos + offsetof(struct virtio_pci_cap, |
|---|
| 485 | + length), &tmp32); |
|---|
| 486 | + res_length = tmp32; |
|---|
| 487 | + |
|---|
| 488 | + /* and now the top half */ |
|---|
| 489 | + pci_read_config_dword(dev, |
|---|
| 490 | + pos + offsetof(struct virtio_pci_cap64, |
|---|
| 491 | + offset_hi), &tmp32); |
|---|
| 492 | + res_offset |= ((u64)tmp32) << 32; |
|---|
| 493 | + pci_read_config_dword(dev, |
|---|
| 494 | + pos + offsetof(struct virtio_pci_cap64, |
|---|
| 495 | + length_hi), &tmp32); |
|---|
| 496 | + res_length |= ((u64)tmp32) << 32; |
|---|
| 497 | + |
|---|
| 498 | + *offset = res_offset; |
|---|
| 499 | + *len = res_length; |
|---|
| 500 | + |
|---|
| 501 | + return pos; |
|---|
| 502 | + } |
|---|
| 503 | + return 0; |
|---|
| 504 | +} |
|---|
| 505 | + |
|---|
| 506 | +static bool vp_get_shm_region(struct virtio_device *vdev, |
|---|
| 507 | + struct virtio_shm_region *region, u8 id) |
|---|
| 508 | +{ |
|---|
| 509 | + struct virtio_pci_device *vp_dev = to_vp_device(vdev); |
|---|
| 510 | + struct pci_dev *pci_dev = vp_dev->pci_dev; |
|---|
| 511 | + u8 bar; |
|---|
| 512 | + u64 offset, len; |
|---|
| 513 | + phys_addr_t phys_addr; |
|---|
| 514 | + size_t bar_len; |
|---|
| 515 | + |
|---|
| 516 | + if (!virtio_pci_find_shm_cap(pci_dev, id, &bar, &offset, &len)) |
|---|
| 517 | + return false; |
|---|
| 518 | + |
|---|
| 519 | + phys_addr = pci_resource_start(pci_dev, bar); |
|---|
| 520 | + bar_len = pci_resource_len(pci_dev, bar); |
|---|
| 521 | + |
|---|
| 522 | + if ((offset + len) < offset) { |
|---|
| 523 | + dev_err(&pci_dev->dev, "%s: cap offset+len overflow detected\n", |
|---|
| 524 | + __func__); |
|---|
| 525 | + return false; |
|---|
| 526 | + } |
|---|
| 527 | + |
|---|
| 528 | + if (offset + len > bar_len) { |
|---|
| 529 | + dev_err(&pci_dev->dev, "%s: bar shorter than cap offset+len\n", |
|---|
| 530 | + __func__); |
|---|
| 531 | + return false; |
|---|
| 532 | + } |
|---|
| 533 | + |
|---|
| 534 | + region->len = len; |
|---|
| 535 | + region->addr = (u64) phys_addr + offset; |
|---|
| 536 | + |
|---|
| 537 | + return true; |
|---|
| 538 | +} |
|---|
| 539 | + |
|---|
| 449 | 540 | static const struct virtio_config_ops virtio_pci_config_nodev_ops = { |
|---|
| 450 | 541 | .get = NULL, |
|---|
| 451 | 542 | .set = NULL, |
|---|
| .. | .. |
|---|
| 460 | 551 | .bus_name = vp_bus_name, |
|---|
| 461 | 552 | .set_vq_affinity = vp_set_vq_affinity, |
|---|
| 462 | 553 | .get_vq_affinity = vp_get_vq_affinity, |
|---|
| 554 | + .get_shm_region = vp_get_shm_region, |
|---|
| 463 | 555 | }; |
|---|
| 464 | 556 | |
|---|
| 465 | 557 | static const struct virtio_config_ops virtio_pci_config_ops = { |
|---|
| .. | .. |
|---|
| 476 | 568 | .bus_name = vp_bus_name, |
|---|
| 477 | 569 | .set_vq_affinity = vp_set_vq_affinity, |
|---|
| 478 | 570 | .get_vq_affinity = vp_get_vq_affinity, |
|---|
| 571 | + .get_shm_region = vp_get_shm_region, |
|---|
| 479 | 572 | }; |
|---|
| 480 | 573 | |
|---|
| 481 | 574 | /** |
|---|
| .. | .. |
|---|
| 483 | 576 | * @dev: the pci device |
|---|
| 484 | 577 | * @cfg_type: the VIRTIO_PCI_CAP_* value we seek |
|---|
| 485 | 578 | * @ioresource_types: IORESOURCE_MEM and/or IORESOURCE_IO. |
|---|
| 579 | + * @bars: the bitmask of BARs |
|---|
| 486 | 580 | * |
|---|
| 487 | 581 | * Returns offset of the capability, or 0. |
|---|
| 488 | 582 | */ |
|---|