@@ -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;
@@ -1392,5 +1392,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;
}
@@ -337,6 +337,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)
@@ -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);
@@ -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,
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 <mjrosato@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(-)