62547e
From 62fbb66d18f598d0896164383aab465e093fb0c1 Mon Sep 17 00:00:00 2001
62547e
From: Matthew Rosato <mjrosato@linux.ibm.com>
62547e
Date: Fri, 2 Sep 2022 13:27:32 -0400
62547e
Subject: [PATCH 07/42] s390x/pci: enable for load/store interpretation
62547e
MIME-Version: 1.0
62547e
Content-Type: text/plain; charset=UTF-8
62547e
Content-Transfer-Encoding: 8bit
62547e
62547e
RH-Author: Cédric Le Goater <clg@redhat.com>
62547e
RH-MergeRequest: 226: s390: Enhanced Interpretation for PCI Functions and Secure Execution guest dump
62547e
RH-Bugzilla: 1664378 2043909
62547e
RH-Acked-by: Thomas Huth <thuth@redhat.com>
62547e
RH-Acked-by: Cornelia Huck <cohuck@redhat.com>
62547e
RH-Acked-by: Jon Maloy <jmaloy@redhat.com>
62547e
RH-Commit: [7/41] 3a96e901e295bb9e0c530638c45b5da5d60c00bd
62547e
62547e
If the ZPCI_OP ioctl reports that is is available and usable, then the
62547e
underlying KVM host will enable load/store intepretation for any guest
62547e
device without a SHM bit in the guest function handle.  For a device that
62547e
will be using interpretation support, ensure the guest function handle
62547e
matches the host function handle; this value is re-checked every time the
62547e
guest issues a SET PCI FN to enable the guest device as it is the only
62547e
opportunity to reflect function handle changes.
62547e
62547e
By default, unless interpret=off is specified, interpretation support will
62547e
always be assumed and exploited if the necessary ioctl and features are
62547e
available on the host kernel.  When these are unavailable, we will silently
62547e
revert to the interception model; this allows existing guest configurations
62547e
to work unmodified on hosts with and without zPCI interpretation support,
62547e
allowing QEMU to choose the best support model available.
62547e
62547e
Signed-off-by: Matthew Rosato <mjrosato@linux.ibm.com>
62547e
Acked-by: Thomas Huth <thuth@redhat.com>
62547e
Message-Id: <20220902172737.170349-4-mjrosato@linux.ibm.com>
62547e
Signed-off-by: Thomas Huth <thuth@redhat.com>
62547e
(cherry picked from commit dd1d5fd9684beeb0c14c39f497ef2aa9ac683aa7)
62547e
Signed-off-by: Cédric Le Goater <clg@redhat.com>
62547e
---
62547e
 hw/s390x/meson.build            |  1 +
62547e
 hw/s390x/s390-pci-bus.c         | 66 ++++++++++++++++++++++++++++++++-
62547e
 hw/s390x/s390-pci-inst.c        | 16 ++++++++
62547e
 hw/s390x/s390-pci-kvm.c         | 22 +++++++++++
62547e
 include/hw/s390x/s390-pci-bus.h |  1 +
62547e
 include/hw/s390x/s390-pci-kvm.h | 24 ++++++++++++
62547e
 target/s390x/kvm/kvm.c          |  7 ++++
62547e
 target/s390x/kvm/kvm_s390x.h    |  1 +
62547e
 8 files changed, 137 insertions(+), 1 deletion(-)
62547e
 create mode 100644 hw/s390x/s390-pci-kvm.c
62547e
 create mode 100644 include/hw/s390x/s390-pci-kvm.h
62547e
62547e
diff --git a/hw/s390x/meson.build b/hw/s390x/meson.build
62547e
index 28484256ec..6e6e47fcda 100644
62547e
--- a/hw/s390x/meson.build
62547e
+++ b/hw/s390x/meson.build
62547e
@@ -23,6 +23,7 @@ s390x_ss.add(when: 'CONFIG_KVM', if_true: files(
62547e
   's390-skeys-kvm.c',
62547e
   's390-stattrib-kvm.c',
62547e
   'pv.c',
62547e
+  's390-pci-kvm.c',
62547e
 ))
62547e
 s390x_ss.add(when: 'CONFIG_TCG', if_true: files(
62547e
   'tod-tcg.c',
62547e
diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c
62547e
index 01b58ebc70..18bfae0465 100644
62547e
--- a/hw/s390x/s390-pci-bus.c
62547e
+++ b/hw/s390x/s390-pci-bus.c
62547e
@@ -16,6 +16,7 @@
62547e
 #include "qapi/visitor.h"
62547e
 #include "hw/s390x/s390-pci-bus.h"
62547e
 #include "hw/s390x/s390-pci-inst.h"
62547e
+#include "hw/s390x/s390-pci-kvm.h"
62547e
 #include "hw/s390x/s390-pci-vfio.h"
62547e
 #include "hw/pci/pci_bus.h"
62547e
 #include "hw/qdev-properties.h"
62547e
@@ -971,12 +972,51 @@ static void s390_pci_update_subordinate(PCIDevice *dev, uint32_t nr)
62547e
     }
62547e
 }
62547e
 
62547e
+static int s390_pci_interp_plug(S390pciState *s, S390PCIBusDevice *pbdev)
62547e
+{
62547e
+    uint32_t idx, fh;
62547e
+
62547e
+    if (!s390_pci_get_host_fh(pbdev, &fh)) {
62547e
+        return -EPERM;
62547e
+    }
62547e
+
62547e
+    /*
62547e
+     * The host device is already in an enabled state, but we always present
62547e
+     * the initial device state to the guest as disabled (ZPCI_FS_DISABLED).
62547e
+     * Therefore, mask off the enable bit from the passthrough handle until
62547e
+     * the guest issues a CLP SET PCI FN later to enable the device.
62547e
+     */
62547e
+    pbdev->fh = fh & ~FH_MASK_ENABLE;
62547e
+
62547e
+    /* Next, see if the idx is already in-use */
62547e
+    idx = pbdev->fh & FH_MASK_INDEX;
62547e
+    if (pbdev->idx != idx) {
62547e
+        if (s390_pci_find_dev_by_idx(s, idx)) {
62547e
+            return -EINVAL;
62547e
+        }
62547e
+        /*
62547e
+         * Update the idx entry with the passed through idx
62547e
+         * If the relinquished idx is lower than next_idx, use it
62547e
+         * to replace next_idx
62547e
+         */
62547e
+        g_hash_table_remove(s->zpci_table, &pbdev->idx);
62547e
+        if (idx < s->next_idx) {
62547e
+            s->next_idx = idx;
62547e
+        }
62547e
+        pbdev->idx = idx;
62547e
+        g_hash_table_insert(s->zpci_table, &pbdev->idx, pbdev);
62547e
+    }
62547e
+
62547e
+    return 0;
62547e
+}
62547e
+
62547e
 static void s390_pcihost_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
62547e
                               Error **errp)
62547e
 {
62547e
     S390pciState *s = S390_PCI_HOST_BRIDGE(hotplug_dev);
62547e
     PCIDevice *pdev = NULL;
62547e
     S390PCIBusDevice *pbdev = NULL;
62547e
+    int rc;
62547e
 
62547e
     if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_BRIDGE)) {
62547e
         PCIBridge *pb = PCI_BRIDGE(dev);
62547e
@@ -1022,12 +1062,35 @@ static void s390_pcihost_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
62547e
         set_pbdev_info(pbdev);
62547e
 
62547e
         if (object_dynamic_cast(OBJECT(dev), "vfio-pci")) {
62547e
-            pbdev->fh |= FH_SHM_VFIO;
62547e
+            /*
62547e
+             * By default, interpretation is always requested; if the available
62547e
+             * facilities indicate it is not available, fallback to the
62547e
+             * interception model.
62547e
+             */
62547e
+            if (pbdev->interp) {
62547e
+                if (s390_pci_kvm_interp_allowed()) {
62547e
+                    rc = s390_pci_interp_plug(s, pbdev);
62547e
+                    if (rc) {
62547e
+                        error_setg(errp, "Plug failed for zPCI device in "
62547e
+                                   "interpretation mode: %d", rc);
62547e
+                        return;
62547e
+                    }
62547e
+                } else {
62547e
+                    DPRINTF("zPCI interpretation facilities missing.\n");
62547e
+                    pbdev->interp = false;
62547e
+                }
62547e
+            }
62547e
             pbdev->iommu->dma_limit = s390_pci_start_dma_count(s, pbdev);
62547e
             /* Fill in CLP information passed via the vfio region */
62547e
             s390_pci_get_clp_info(pbdev);
62547e
+            if (!pbdev->interp) {
62547e
+                /* Do vfio passthrough but intercept for I/O */
62547e
+                pbdev->fh |= FH_SHM_VFIO;
62547e
+            }
62547e
         } else {
62547e
             pbdev->fh |= FH_SHM_EMUL;
62547e
+            /* Always intercept emulated devices */
62547e
+            pbdev->interp = false;
62547e
         }
62547e
 
62547e
         if (s390_pci_msix_init(pbdev)) {
62547e
@@ -1360,6 +1423,7 @@ static Property s390_pci_device_properties[] = {
62547e
     DEFINE_PROP_UINT16("uid", S390PCIBusDevice, uid, UID_UNDEFINED),
62547e
     DEFINE_PROP_S390_PCI_FID("fid", S390PCIBusDevice, fid),
62547e
     DEFINE_PROP_STRING("target", S390PCIBusDevice, target),
62547e
+    DEFINE_PROP_BOOL("interpret", S390PCIBusDevice, interp, true),
62547e
     DEFINE_PROP_END_OF_LIST(),
62547e
 };
62547e
 
62547e
diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c
62547e
index 6d400d4147..651ec38635 100644
62547e
--- a/hw/s390x/s390-pci-inst.c
62547e
+++ b/hw/s390x/s390-pci-inst.c
62547e
@@ -18,6 +18,8 @@
62547e
 #include "sysemu/hw_accel.h"
62547e
 #include "hw/s390x/s390-pci-inst.h"
62547e
 #include "hw/s390x/s390-pci-bus.h"
62547e
+#include "hw/s390x/s390-pci-kvm.h"
62547e
+#include "hw/s390x/s390-pci-vfio.h"
62547e
 #include "hw/s390x/tod.h"
62547e
 
62547e
 #ifndef DEBUG_S390PCI_INST
62547e
@@ -246,6 +248,20 @@ int clp_service_call(S390CPU *cpu, uint8_t r2, uintptr_t ra)
62547e
                 goto out;
62547e
             }
62547e
 
62547e
+            /*
62547e
+             * Take this opportunity to make sure we still have an accurate
62547e
+             * host fh.  It's possible part of the handle changed while the
62547e
+             * device was disabled to the guest (e.g. vfio hot reset for
62547e
+             * ISM during plug)
62547e
+             */
62547e
+            if (pbdev->interp) {
62547e
+                /* Take this opportunity to make sure we are sync'd with host */
62547e
+                if (!s390_pci_get_host_fh(pbdev, &pbdev->fh) ||
62547e
+                    !(pbdev->fh & FH_MASK_ENABLE)) {
62547e
+                    stw_p(&ressetpci->hdr.rsp, CLP_RC_SETPCIFN_FH);
62547e
+                    goto out;
62547e
+                }
62547e
+            }
62547e
             pbdev->fh |= FH_MASK_ENABLE;
62547e
             pbdev->state = ZPCI_FS_ENABLED;
62547e
             stl_p(&ressetpci->fh, pbdev->fh);
62547e
diff --git a/hw/s390x/s390-pci-kvm.c b/hw/s390x/s390-pci-kvm.c
62547e
new file mode 100644
62547e
index 0000000000..0f16104a74
62547e
--- /dev/null
62547e
+++ b/hw/s390x/s390-pci-kvm.c
62547e
@@ -0,0 +1,22 @@
62547e
+/*
62547e
+ * s390 zPCI KVM interfaces
62547e
+ *
62547e
+ * Copyright 2022 IBM Corp.
62547e
+ * Author(s): Matthew Rosato <mjrosato@linux.ibm.com>
62547e
+ *
62547e
+ * This work is licensed under the terms of the GNU GPL, version 2 or (at
62547e
+ * your option) any later version. See the COPYING file in the top-level
62547e
+ * directory.
62547e
+ */
62547e
+
62547e
+#include "qemu/osdep.h"
62547e
+
62547e
+#include "kvm/kvm_s390x.h"
62547e
+#include "hw/s390x/pv.h"
62547e
+#include "hw/s390x/s390-pci-kvm.h"
62547e
+#include "cpu_models.h"
62547e
+
62547e
+bool s390_pci_kvm_interp_allowed(void)
62547e
+{
62547e
+    return kvm_s390_get_zpci_op() && !s390_is_pv();
62547e
+}
62547e
diff --git a/include/hw/s390x/s390-pci-bus.h b/include/hw/s390x/s390-pci-bus.h
62547e
index da3cde2bb4..a9843dfe97 100644
62547e
--- a/include/hw/s390x/s390-pci-bus.h
62547e
+++ b/include/hw/s390x/s390-pci-bus.h
62547e
@@ -350,6 +350,7 @@ struct S390PCIBusDevice {
62547e
     IndAddr *indicator;
62547e
     bool pci_unplug_request_processed;
62547e
     bool unplug_requested;
62547e
+    bool interp;
62547e
     QTAILQ_ENTRY(S390PCIBusDevice) link;
62547e
 };
62547e
 
62547e
diff --git a/include/hw/s390x/s390-pci-kvm.h b/include/hw/s390x/s390-pci-kvm.h
62547e
new file mode 100644
62547e
index 0000000000..80a2e7d0ca
62547e
--- /dev/null
62547e
+++ b/include/hw/s390x/s390-pci-kvm.h
62547e
@@ -0,0 +1,24 @@
62547e
+/*
62547e
+ * s390 PCI KVM interfaces
62547e
+ *
62547e
+ * Copyright 2022 IBM Corp.
62547e
+ * Author(s): Matthew Rosato <mjrosato@linux.ibm.com>
62547e
+ *
62547e
+ * This work is licensed under the terms of the GNU GPL, version 2 or (at
62547e
+ * your option) any later version. See the COPYING file in the top-level
62547e
+ * directory.
62547e
+ */
62547e
+
62547e
+#ifndef HW_S390_PCI_KVM_H
62547e
+#define HW_S390_PCI_KVM_H
62547e
+
62547e
+#ifdef CONFIG_KVM
62547e
+bool s390_pci_kvm_interp_allowed(void);
62547e
+#else
62547e
+static inline bool s390_pci_kvm_interp_allowed(void)
62547e
+{
62547e
+    return false;
62547e
+}
62547e
+#endif
62547e
+
62547e
+#endif
62547e
diff --git a/target/s390x/kvm/kvm.c b/target/s390x/kvm/kvm.c
62547e
index ba04997da1..30712487d4 100644
62547e
--- a/target/s390x/kvm/kvm.c
62547e
+++ b/target/s390x/kvm/kvm.c
62547e
@@ -158,6 +158,7 @@ static int cap_ri;
62547e
 static int cap_hpage_1m;
62547e
 static int cap_vcpu_resets;
62547e
 static int cap_protected;
62547e
+static int cap_zpci_op;
62547e
 
62547e
 static bool mem_op_storage_key_support;
62547e
 
62547e
@@ -363,6 +364,7 @@ int kvm_arch_init(MachineState *ms, KVMState *s)
62547e
     cap_s390_irq = kvm_check_extension(s, KVM_CAP_S390_INJECT_IRQ);
62547e
     cap_vcpu_resets = kvm_check_extension(s, KVM_CAP_S390_VCPU_RESETS);
62547e
     cap_protected = kvm_check_extension(s, KVM_CAP_S390_PROTECTED);
62547e
+    cap_zpci_op = kvm_check_extension(s, KVM_CAP_S390_ZPCI_OP);
62547e
 
62547e
     kvm_vm_enable_cap(s, KVM_CAP_S390_USER_SIGP, 0);
62547e
     kvm_vm_enable_cap(s, KVM_CAP_S390_VECTOR_REGISTERS, 0);
62547e
@@ -2579,3 +2581,8 @@ bool kvm_arch_cpu_check_are_resettable(void)
62547e
 {
62547e
     return true;
62547e
 }
62547e
+
62547e
+int kvm_s390_get_zpci_op(void)
62547e
+{
62547e
+    return cap_zpci_op;
62547e
+}
62547e
diff --git a/target/s390x/kvm/kvm_s390x.h b/target/s390x/kvm/kvm_s390x.h
62547e
index 05a5e1e6f4..aaae8570de 100644
62547e
--- a/target/s390x/kvm/kvm_s390x.h
62547e
+++ b/target/s390x/kvm/kvm_s390x.h
62547e
@@ -27,6 +27,7 @@ void kvm_s390_vcpu_interrupt_pre_save(S390CPU *cpu);
62547e
 int kvm_s390_vcpu_interrupt_post_load(S390CPU *cpu);
62547e
 int kvm_s390_get_hpage_1m(void);
62547e
 int kvm_s390_get_ri(void);
62547e
+int kvm_s390_get_zpci_op(void);
62547e
 int kvm_s390_get_clock(uint8_t *tod_high, uint64_t *tod_clock);
62547e
 int kvm_s390_get_clock_ext(uint8_t *tod_high, uint64_t *tod_clock);
62547e
 int kvm_s390_set_clock(uint8_t tod_high, uint64_t tod_clock);
62547e
-- 
62547e
2.37.3
62547e