For ISM devices, use the vfio region to handle intercepted PCILG instructions. This will allow read I/Os intercepted from the guest to be performed as single operations that ensure the same non-MIO PCI instruction is used on the host as specified in the guest.
Signed-off-by: Matthew Rosato <mjros...@linux.ibm.com> --- hw/s390x/s390-pci-inst.c | 3 ++- hw/s390x/s390-pci-vfio.c | 53 ++++++++++++++++++++++++++++++++++++++++ include/hw/s390x/s390-pci-inst.h | 1 + include/hw/s390x/s390-pci-vfio.h | 8 ++++++ 4 files changed, 64 insertions(+), 1 deletion(-) diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c index 18a701f..97e9a7a 100644 --- a/hw/s390x/s390-pci-inst.c +++ b/hw/s390x/s390-pci-inst.c @@ -352,7 +352,7 @@ out: * @ptr: a pointer to a uint64_t data field * @len: the length of the valid data, must be 1,2,4 or 8 */ -static int zpci_endian_swap(uint64_t *ptr, uint8_t len) +int zpci_endian_swap(uint64_t *ptr, uint8_t len) { uint64_t data = *ptr; @@ -1494,5 +1494,6 @@ void zpci_assign_default_ops(S390PCIBusDevice *pbdev) void zpci_assign_ops_vfio_io_region(S390PCIBusDevice *pbdev) { + pbdev->ops.pcilg = s390_pci_vfio_pcilg; pbdev->ops.pcistb = s390_pci_vfio_pcistb; } diff --git a/hw/s390x/s390-pci-vfio.c b/hw/s390x/s390-pci-vfio.c index 33b24d4..6778ba4 100644 --- a/hw/s390x/s390-pci-vfio.c +++ b/hw/s390x/s390-pci-vfio.c @@ -339,6 +339,59 @@ int s390_pci_get_zpci_io_region(S390PCIBusDevice *pbdev) return ret; } +int s390_pci_vfio_pcilg(S390PCIBusDevice *pbdev, uint64_t *data, uint8_t pcias, + uint16_t len, uint64_t offset) +{ + struct vfio_region_zpci_io *region = pbdev->io_region; + VFIOPCIDevice *vfio_pci; + int ret; + + if (region == NULL) { + return -EIO; + } + + vfio_pci = container_of(pbdev->pdev, VFIOPCIDevice, pdev); + + /* Perform Length/Alignment checks */ + switch (pcias) { + case ZPCI_IO_BAR_MIN...ZPCI_IO_BAR_MAX: + if (!len || (len > (8 - (offset & 0x7)))) { + return -EINVAL; + } + region->req.gaddr = (uint64_t)data; + region->req.offset = offset; + region->req.len = len; + region->req.pcias = pcias; + region->req.flags = VFIO_ZPCI_IO_FLAG_READ; + + ret = pwrite(vfio_pci->vbasedev.fd, ®ion->req, + sizeof(struct vfio_zpci_io_req), + pbdev->io_region_op_offset); + if (ret != sizeof(struct vfio_zpci_io_req)) { + ret = -EIO; + } else { + ret = 0; + } + break; + case ZPCI_CONFIG_BAR: + if (!len || (len > (4 - (offset & 0x3))) || len == 3) { + return -EINVAL; + } + *data = pci_host_config_read_common( + pbdev->pdev, offset, pci_config_size(pbdev->pdev), len); + + if (zpci_endian_swap(data, len)) { + ret = -EINVAL; + } + ret = 0; + break; + default: + return -EFAULT; + } + + return ret; +} + int s390_pci_vfio_pcistb(S390PCIBusDevice *pbdev, S390CPU *cpu, uint64_t gaddr, uint8_t ar, uint8_t pcias, uint16_t len, uint64_t offset) diff --git a/include/hw/s390x/s390-pci-inst.h b/include/hw/s390x/s390-pci-inst.h index 7ed6175..fe368fb 100644 --- a/include/hw/s390x/s390-pci-inst.h +++ b/include/hw/s390x/s390-pci-inst.h @@ -101,6 +101,7 @@ typedef struct ZpciFib { int pci_dereg_irqs(S390PCIBusDevice *pbdev); void pci_dereg_ioat(S390PCIIOMMU *iommu); int clp_service_call(S390CPU *cpu, uint8_t r2, uintptr_t ra); +int zpci_endian_swap(uint64_t *ptr, uint8_t len); int pcilg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra); int pcistg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra); int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra); diff --git a/include/hw/s390x/s390-pci-vfio.h b/include/hw/s390x/s390-pci-vfio.h index f0a994f..d9fb3a4 100644 --- a/include/hw/s390x/s390-pci-vfio.h +++ b/include/hw/s390x/s390-pci-vfio.h @@ -22,6 +22,8 @@ S390PCIDMACount *s390_pci_start_dma_count(S390pciState *s, void s390_pci_end_dma_count(S390pciState *s, S390PCIDMACount *cnt); void s390_pci_get_clp_info(S390PCIBusDevice *pbdev); int s390_pci_get_zpci_io_region(S390PCIBusDevice *pbdev); +int s390_pci_vfio_pcilg(S390PCIBusDevice *pbdev, uint64_t *data, uint8_t pcias, + uint16_t len, uint64_t offset); int s390_pci_vfio_pcistb(S390PCIBusDevice *pbdev, S390CPU *cpu, uint64_t gaddr, uint8_t ar, uint8_t pcias, uint16_t len, uint64_t offset); @@ -42,6 +44,12 @@ static inline int s390_pci_get_zpci_io_region(S390PCIBusDevice *pbdev) { return -EINVAL; } +static inline int s390_pci_vfio_pcilg(S390PCIBusDevice *pbdev, uint64_t *data, + uint8_t pcias, uint16_t len, + uint64_t offset) +{ + return -EIO; +} static inline int s390_pci_vfio_pcistb(S390PCIBusDevice *pbdev, S390CPU *cpu, uint64_t gaddr, uint8_t ar, uint8_t pcias, uint16_t len, -- 1.8.3.1