77c23f
From e6474080e3816e82e87c545a3d22db77c55ab053 Mon Sep 17 00:00:00 2001
77c23f
From: Thomas Huth <thuth@redhat.com>
77c23f
Date: Fri, 29 May 2020 05:54:04 -0400
77c23f
Subject: [PATCH 22/42] s390x: protvirt: Support unpack facility
77c23f
77c23f
RH-Author: Thomas Huth <thuth@redhat.com>
77c23f
Message-id: <20200529055420.16855-23-thuth@redhat.com>
77c23f
Patchwork-id: 97045
77c23f
O-Subject: [RHEL-8.3.0 qemu-kvm PATCH v2 22/38] s390x: protvirt: Support unpack facility
77c23f
Bugzilla: 1828317
77c23f
RH-Acked-by: Claudio Imbrenda <cimbrend@redhat.com>
77c23f
RH-Acked-by: Cornelia Huck <cohuck@redhat.com>
77c23f
RH-Acked-by: David Hildenbrand <david@redhat.com>
77c23f
77c23f
From: Janosch Frank <frankja@linux.ibm.com>
77c23f
77c23f
The unpack facility provides the means to setup a protected guest. A
77c23f
protected guest cannot be introspected by the hypervisor or any
77c23f
user/administrator of the machine it is running on.
77c23f
77c23f
Protected guests are encrypted at rest and need a special boot
77c23f
mechanism via diag308 subcode 8 and 10.
77c23f
77c23f
Code 8 sets the PV specific IPLB which is retained separately from
77c23f
those set via code 5.
77c23f
77c23f
Code 10 is used to unpack the VM into protected memory, verify its
77c23f
integrity and start it.
77c23f
77c23f
Signed-off-by: Janosch Frank <frankja@linux.ibm.com>
77c23f
Co-developed-by: Christian Borntraeger <borntraeger@de.ibm.com> [Changes
77c23f
to machine]
77c23f
Reviewed-by: David Hildenbrand <david@redhat.com>
77c23f
Reviewed-by: Claudio Imbrenda <imbrenda@linux.ibm.com>
77c23f
Reviewed-by: Cornelia Huck <cohuck@redhat.com>
77c23f
Message-Id: <20200323083606.24520-1-frankja@linux.ibm.com>
77c23f
[CH: fixed up KVM_PV_VM_ -> KVM_PV_]
77c23f
Signed-off-by: Cornelia Huck <cohuck@redhat.com>
77c23f
(cherry picked from commit c3347ed0d2ee42a7dcf7bfe7f9c3884a9596727a)
77c23f
Signed-off-by: Danilo C. L. de Paula <ddepaula@redhat.com>
77c23f
---
77c23f
 MAINTAINERS                         |   2 +
77c23f
 hw/s390x/Makefile.objs              |   1 +
77c23f
 hw/s390x/ipl.c                      |  59 +++++++++++++-
77c23f
 hw/s390x/ipl.h                      |  91 ++++++++++++++++++++-
77c23f
 hw/s390x/pv.c                       |  98 +++++++++++++++++++++++
77c23f
 hw/s390x/s390-virtio-ccw.c          | 119 +++++++++++++++++++++++++++-
77c23f
 include/hw/s390x/pv.h               |  55 +++++++++++++
77c23f
 include/hw/s390x/s390-virtio-ccw.h  |   1 +
77c23f
 target/s390x/cpu.c                  |   1 +
77c23f
 target/s390x/cpu_features_def.inc.h |   1 +
77c23f
 target/s390x/diag.c                 |  39 ++++++++-
77c23f
 target/s390x/kvm-stub.c             |   5 ++
77c23f
 target/s390x/kvm.c                  |   5 ++
77c23f
 target/s390x/kvm_s390x.h            |   1 +
77c23f
 14 files changed, 468 insertions(+), 10 deletions(-)
77c23f
 create mode 100644 hw/s390x/pv.c
77c23f
 create mode 100644 include/hw/s390x/pv.h
77c23f
77c23f
diff --git a/MAINTAINERS b/MAINTAINERS
77c23f
index 49d5d44edc..2742c95575 100644
77c23f
--- a/MAINTAINERS
77c23f
+++ b/MAINTAINERS
77c23f
@@ -385,6 +385,8 @@ F: target/s390x/machine.c
77c23f
 F: target/s390x/sigp.c
77c23f
 F: target/s390x/cpu_features*.[ch]
77c23f
 F: target/s390x/cpu_models.[ch]
77c23f
+F: hw/s390x/pv.c
77c23f
+F: include/hw/s390x/pv.h
77c23f
 F: hw/intc/s390_flic.c
77c23f
 F: hw/intc/s390_flic_kvm.c
77c23f
 F: include/hw/s390x/s390_flic.h
77c23f
diff --git a/hw/s390x/Makefile.objs b/hw/s390x/Makefile.objs
77c23f
index e02ed80b68..a46a1c7894 100644
77c23f
--- a/hw/s390x/Makefile.objs
77c23f
+++ b/hw/s390x/Makefile.objs
77c23f
@@ -31,6 +31,7 @@ obj-y += tod-qemu.o
77c23f
 obj-$(CONFIG_KVM) += tod-kvm.o
77c23f
 obj-$(CONFIG_KVM) += s390-skeys-kvm.o
77c23f
 obj-$(CONFIG_KVM) += s390-stattrib-kvm.o
77c23f
+obj-$(CONFIG_KVM) += pv.o
77c23f
 obj-y += s390-ccw.o
77c23f
 obj-y += ap-device.o
77c23f
 obj-y += ap-bridge.o
77c23f
diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c
77c23f
index fa0409dc23..586d95b5b6 100644
77c23f
--- a/hw/s390x/ipl.c
77c23f
+++ b/hw/s390x/ipl.c
77c23f
@@ -1,10 +1,11 @@
77c23f
 /*
77c23f
  * bootloader support
77c23f
  *
77c23f
- * Copyright IBM, Corp. 2012
77c23f
+ * Copyright IBM, Corp. 2012, 2020
77c23f
  *
77c23f
  * Authors:
77c23f
  *  Christian Borntraeger <borntraeger@de.ibm.com>
77c23f
+ *  Janosch Frank <frankja@linux.ibm.com>
77c23f
  *
77c23f
  * This work is licensed under the terms of the GNU GPL, version 2 or (at your
77c23f
  * option) any later version.  See the COPYING file in the top-level directory.
77c23f
@@ -27,6 +28,7 @@
77c23f
 #include "hw/s390x/vfio-ccw.h"
77c23f
 #include "hw/s390x/css.h"
77c23f
 #include "hw/s390x/ebcdic.h"
77c23f
+#include "hw/s390x/pv.h"
77c23f
 #include "ipl.h"
77c23f
 #include "qemu/error-report.h"
77c23f
 #include "qemu/config-file.h"
77c23f
@@ -557,12 +559,31 @@ void s390_ipl_update_diag308(IplParameterBlock *iplb)
77c23f
 {
77c23f
     S390IPLState *ipl = get_ipl_device();
77c23f
 
77c23f
-    ipl->iplb = *iplb;
77c23f
-    ipl->iplb_valid = true;
77c23f
+    /*
77c23f
+     * The IPLB set and retrieved by subcodes 8/9 is completely
77c23f
+     * separate from the one managed via subcodes 5/6.
77c23f
+     */
77c23f
+    if (iplb->pbt == S390_IPL_TYPE_PV) {
77c23f
+        ipl->iplb_pv = *iplb;
77c23f
+        ipl->iplb_valid_pv = true;
77c23f
+    } else {
77c23f
+        ipl->iplb = *iplb;
77c23f
+        ipl->iplb_valid = true;
77c23f
+    }
77c23f
     ipl->netboot = is_virtio_net_device(iplb);
77c23f
     update_machine_ipl_properties(iplb);
77c23f
 }
77c23f
 
77c23f
+IplParameterBlock *s390_ipl_get_iplb_pv(void)
77c23f
+{
77c23f
+    S390IPLState *ipl = get_ipl_device();
77c23f
+
77c23f
+    if (!ipl->iplb_valid_pv) {
77c23f
+        return NULL;
77c23f
+    }
77c23f
+    return &ipl->iplb_pv;
77c23f
+}
77c23f
+
77c23f
 IplParameterBlock *s390_ipl_get_iplb(void)
77c23f
 {
77c23f
     S390IPLState *ipl = get_ipl_device();
77c23f
@@ -651,6 +672,38 @@ static void s390_ipl_prepare_qipl(S390CPU *cpu)
77c23f
     cpu_physical_memory_unmap(addr, len, 1, len);
77c23f
 }
77c23f
 
77c23f
+int s390_ipl_prepare_pv_header(void)
77c23f
+{
77c23f
+    IplParameterBlock *ipib = s390_ipl_get_iplb_pv();
77c23f
+    IPLBlockPV *ipib_pv = &ipib->pv;
77c23f
+    void *hdr = g_malloc(ipib_pv->pv_header_len);
77c23f
+    int rc;
77c23f
+
77c23f
+    cpu_physical_memory_read(ipib_pv->pv_header_addr, hdr,
77c23f
+                             ipib_pv->pv_header_len);
77c23f
+    rc = s390_pv_set_sec_parms((uintptr_t)hdr,
77c23f
+                               ipib_pv->pv_header_len);
77c23f
+    g_free(hdr);
77c23f
+    return rc;
77c23f
+}
77c23f
+
77c23f
+int s390_ipl_pv_unpack(void)
77c23f
+{
77c23f
+    IplParameterBlock *ipib = s390_ipl_get_iplb_pv();
77c23f
+    IPLBlockPV *ipib_pv = &ipib->pv;
77c23f
+    int i, rc = 0;
77c23f
+
77c23f
+    for (i = 0; i < ipib_pv->num_comp; i++) {
77c23f
+        rc = s390_pv_unpack(ipib_pv->components[i].addr,
77c23f
+                            TARGET_PAGE_ALIGN(ipib_pv->components[i].size),
77c23f
+                            ipib_pv->components[i].tweak_pref);
77c23f
+        if (rc) {
77c23f
+            break;
77c23f
+        }
77c23f
+    }
77c23f
+    return rc;
77c23f
+}
77c23f
+
77c23f
 void s390_ipl_prepare_cpu(S390CPU *cpu)
77c23f
 {
77c23f
     S390IPLState *ipl = get_ipl_device();
77c23f
diff --git a/hw/s390x/ipl.h b/hw/s390x/ipl.h
77c23f
index a5665e6bfd..89b3044d7a 100644
77c23f
--- a/hw/s390x/ipl.h
77c23f
+++ b/hw/s390x/ipl.h
77c23f
@@ -1,8 +1,9 @@
77c23f
 /*
77c23f
  * s390 IPL device
77c23f
  *
77c23f
- * Copyright 2015 IBM Corp.
77c23f
+ * Copyright 2015, 2020 IBM Corp.
77c23f
  * Author(s): Zhang Fan <bjfanzh@cn.ibm.com>
77c23f
+ * Janosch Frank <frankja@linux.ibm.com>
77c23f
  *
77c23f
  * This work is licensed under the terms of the GNU GPL, version 2 or (at
77c23f
  * your option) any later version. See the COPYING file in the top-level
77c23f
@@ -15,6 +16,24 @@
77c23f
 #include "cpu.h"
77c23f
 #include "hw/qdev-core.h"
77c23f
 
77c23f
+struct IPLBlockPVComp {
77c23f
+    uint64_t tweak_pref;
77c23f
+    uint64_t addr;
77c23f
+    uint64_t size;
77c23f
+} QEMU_PACKED;
77c23f
+typedef struct IPLBlockPVComp IPLBlockPVComp;
77c23f
+
77c23f
+struct IPLBlockPV {
77c23f
+    uint8_t  reserved18[87];    /* 0x18 */
77c23f
+    uint8_t  version;           /* 0x6f */
77c23f
+    uint32_t reserved70;        /* 0x70 */
77c23f
+    uint32_t num_comp;          /* 0x74 */
77c23f
+    uint64_t pv_header_addr;    /* 0x78 */
77c23f
+    uint64_t pv_header_len;     /* 0x80 */
77c23f
+    struct IPLBlockPVComp components[];
77c23f
+} QEMU_PACKED;
77c23f
+typedef struct IPLBlockPV IPLBlockPV;
77c23f
+
77c23f
 struct IplBlockCcw {
77c23f
     uint8_t  reserved0[85];
77c23f
     uint8_t  ssid;
77c23f
@@ -71,6 +90,7 @@ union IplParameterBlock {
77c23f
         union {
77c23f
             IplBlockCcw ccw;
77c23f
             IplBlockFcp fcp;
77c23f
+            IPLBlockPV pv;
77c23f
             IplBlockQemuScsi scsi;
77c23f
         };
77c23f
     } QEMU_PACKED;
77c23f
@@ -85,8 +105,11 @@ typedef union IplParameterBlock IplParameterBlock;
77c23f
 
77c23f
 int s390_ipl_set_loadparm(uint8_t *loadparm);
77c23f
 void s390_ipl_update_diag308(IplParameterBlock *iplb);
77c23f
+int s390_ipl_prepare_pv_header(void);
77c23f
+int s390_ipl_pv_unpack(void);
77c23f
 void s390_ipl_prepare_cpu(S390CPU *cpu);
77c23f
 IplParameterBlock *s390_ipl_get_iplb(void);
77c23f
+IplParameterBlock *s390_ipl_get_iplb_pv(void);
77c23f
 
77c23f
 enum s390_reset {
77c23f
     /* default is a reset not triggered by a CPU e.g. issued by QMP */
77c23f
@@ -94,6 +117,7 @@ enum s390_reset {
77c23f
     S390_RESET_REIPL,
77c23f
     S390_RESET_MODIFIED_CLEAR,
77c23f
     S390_RESET_LOAD_NORMAL,
77c23f
+    S390_RESET_PV,
77c23f
 };
77c23f
 void s390_ipl_reset_request(CPUState *cs, enum s390_reset reset_type);
77c23f
 void s390_ipl_get_reset_request(CPUState **cs, enum s390_reset *reset_type);
77c23f
@@ -133,6 +157,7 @@ struct S390IPLState {
77c23f
     /*< private >*/
77c23f
     DeviceState parent_obj;
77c23f
     IplParameterBlock iplb;
77c23f
+    IplParameterBlock iplb_pv;
77c23f
     QemuIplParameters qipl;
77c23f
     uint64_t start_addr;
77c23f
     uint64_t compat_start_addr;
77c23f
@@ -140,6 +165,7 @@ struct S390IPLState {
77c23f
     uint64_t compat_bios_start_addr;
77c23f
     bool enforce_bios;
77c23f
     bool iplb_valid;
77c23f
+    bool iplb_valid_pv;
77c23f
     bool netboot;
77c23f
     /* reset related properties don't have to be migrated or reset */
77c23f
     enum s390_reset reset_type;
77c23f
@@ -162,6 +188,8 @@ QEMU_BUILD_BUG_MSG(offsetof(S390IPLState, iplb) & 3, "alignment of iplb wrong");
77c23f
 #define DIAG_308_RC_OK              0x0001
77c23f
 #define DIAG_308_RC_NO_CONF         0x0102
77c23f
 #define DIAG_308_RC_INVALID         0x0402
77c23f
+#define DIAG_308_RC_NO_PV_CONF      0x0902
77c23f
+#define DIAG_308_RC_INVAL_FOR_PV    0x0a02
77c23f
 
77c23f
 #define DIAG308_RESET_MOD_CLR       0
77c23f
 #define DIAG308_RESET_LOAD_NORM     1
77c23f
@@ -169,12 +197,17 @@ QEMU_BUILD_BUG_MSG(offsetof(S390IPLState, iplb) & 3, "alignment of iplb wrong");
77c23f
 #define DIAG308_LOAD_NORMAL_DUMP    4
77c23f
 #define DIAG308_SET                 5
77c23f
 #define DIAG308_STORE               6
77c23f
+#define DIAG308_PV_SET              8
77c23f
+#define DIAG308_PV_STORE            9
77c23f
+#define DIAG308_PV_START            10
77c23f
 
77c23f
 #define S390_IPL_TYPE_FCP 0x00
77c23f
 #define S390_IPL_TYPE_CCW 0x02
77c23f
+#define S390_IPL_TYPE_PV 0x05
77c23f
 #define S390_IPL_TYPE_QEMU_SCSI 0xff
77c23f
 
77c23f
 #define S390_IPLB_HEADER_LEN 8
77c23f
+#define S390_IPLB_MIN_PV_LEN 148
77c23f
 #define S390_IPLB_MIN_CCW_LEN 200
77c23f
 #define S390_IPLB_MIN_FCP_LEN 384
77c23f
 #define S390_IPLB_MIN_QEMU_SCSI_LEN 200
77c23f
@@ -184,6 +217,62 @@ static inline bool iplb_valid_len(IplParameterBlock *iplb)
77c23f
     return be32_to_cpu(iplb->len) <= sizeof(IplParameterBlock);
77c23f
 }
77c23f
 
77c23f
+static inline bool ipl_valid_pv_components(IplParameterBlock *iplb)
77c23f
+{
77c23f
+    IPLBlockPV *ipib_pv = &iplb->pv;
77c23f
+    int i;
77c23f
+
77c23f
+    if (ipib_pv->num_comp == 0) {
77c23f
+        return false;
77c23f
+    }
77c23f
+
77c23f
+    for (i = 0; i < ipib_pv->num_comp; i++) {
77c23f
+        /* Addr must be 4k aligned */
77c23f
+        if (ipib_pv->components[i].addr & ~TARGET_PAGE_MASK) {
77c23f
+            return false;
77c23f
+        }
77c23f
+
77c23f
+        /* Tweak prefix is monotonically increasing with each component */
77c23f
+        if (i < ipib_pv->num_comp - 1 &&
77c23f
+            ipib_pv->components[i].tweak_pref >=
77c23f
+            ipib_pv->components[i + 1].tweak_pref) {
77c23f
+            return false;
77c23f
+        }
77c23f
+    }
77c23f
+    return true;
77c23f
+}
77c23f
+
77c23f
+static inline bool ipl_valid_pv_header(IplParameterBlock *iplb)
77c23f
+{
77c23f
+        IPLBlockPV *ipib_pv = &iplb->pv;
77c23f
+
77c23f
+        if (ipib_pv->pv_header_len > 2 * TARGET_PAGE_SIZE) {
77c23f
+            return false;
77c23f
+        }
77c23f
+
77c23f
+        if (!address_space_access_valid(&address_space_memory,
77c23f
+                                        ipib_pv->pv_header_addr,
77c23f
+                                        ipib_pv->pv_header_len,
77c23f
+                                        false,
77c23f
+                                        MEMTXATTRS_UNSPECIFIED)) {
77c23f
+            return false;
77c23f
+        }
77c23f
+
77c23f
+        return true;
77c23f
+}
77c23f
+
77c23f
+static inline bool iplb_valid_pv(IplParameterBlock *iplb)
77c23f
+{
77c23f
+    if (iplb->pbt != S390_IPL_TYPE_PV ||
77c23f
+        be32_to_cpu(iplb->len) < S390_IPLB_MIN_PV_LEN) {
77c23f
+        return false;
77c23f
+    }
77c23f
+    if (!ipl_valid_pv_header(iplb)) {
77c23f
+        return false;
77c23f
+    }
77c23f
+    return ipl_valid_pv_components(iplb);
77c23f
+}
77c23f
+
77c23f
 static inline bool iplb_valid(IplParameterBlock *iplb)
77c23f
 {
77c23f
     switch (iplb->pbt) {
77c23f
diff --git a/hw/s390x/pv.c b/hw/s390x/pv.c
77c23f
new file mode 100644
77c23f
index 0000000000..a40a844806
77c23f
--- /dev/null
77c23f
+++ b/hw/s390x/pv.c
77c23f
@@ -0,0 +1,98 @@
77c23f
+/*
77c23f
+ * Protected Virtualization functions
77c23f
+ *
77c23f
+ * Copyright IBM Corp. 2020
77c23f
+ * Author(s):
77c23f
+ *  Janosch Frank <frankja@linux.ibm.com>
77c23f
+ *
77c23f
+ * This work is licensed under the terms of the GNU GPL, version 2 or (at
77c23f
+ * your option) any later version. See the COPYING file in the top-level
77c23f
+ * directory.
77c23f
+ */
77c23f
+#include "qemu/osdep.h"
77c23f
+
77c23f
+#include <linux/kvm.h>
77c23f
+
77c23f
+#include "qemu/error-report.h"
77c23f
+#include "sysemu/kvm.h"
77c23f
+#include "hw/s390x/pv.h"
77c23f
+
77c23f
+static int __s390_pv_cmd(uint32_t cmd, const char *cmdname, void *data)
77c23f
+{
77c23f
+    struct kvm_pv_cmd pv_cmd = {
77c23f
+        .cmd = cmd,
77c23f
+        .data = (uint64_t)data,
77c23f
+    };
77c23f
+    int rc = kvm_vm_ioctl(kvm_state, KVM_S390_PV_COMMAND, &pv_cmd);
77c23f
+
77c23f
+    if (rc) {
77c23f
+        error_report("KVM PV command %d (%s) failed: header rc %x rrc %x "
77c23f
+                     "IOCTL rc: %d", cmd, cmdname, pv_cmd.rc, pv_cmd.rrc,
77c23f
+                     rc);
77c23f
+    }
77c23f
+    return rc;
77c23f
+}
77c23f
+
77c23f
+/*
77c23f
+ * This macro lets us pass the command as a string to the function so
77c23f
+ * we can print it on an error.
77c23f
+ */
77c23f
+#define s390_pv_cmd(cmd, data) __s390_pv_cmd(cmd, #cmd, data);
77c23f
+#define s390_pv_cmd_exit(cmd, data)    \
77c23f
+{                                      \
77c23f
+    int rc;                            \
77c23f
+                                       \
77c23f
+    rc = __s390_pv_cmd(cmd, #cmd, data);\
77c23f
+    if (rc) {                          \
77c23f
+        exit(1);                       \
77c23f
+    }                                  \
77c23f
+}
77c23f
+
77c23f
+int s390_pv_vm_enable(void)
77c23f
+{
77c23f
+    return s390_pv_cmd(KVM_PV_ENABLE, NULL);
77c23f
+}
77c23f
+
77c23f
+void s390_pv_vm_disable(void)
77c23f
+{
77c23f
+     s390_pv_cmd_exit(KVM_PV_DISABLE, NULL);
77c23f
+}
77c23f
+
77c23f
+int s390_pv_set_sec_parms(uint64_t origin, uint64_t length)
77c23f
+{
77c23f
+    struct kvm_s390_pv_sec_parm args = {
77c23f
+        .origin = origin,
77c23f
+        .length = length,
77c23f
+    };
77c23f
+
77c23f
+    return s390_pv_cmd(KVM_PV_SET_SEC_PARMS, &args);
77c23f
+}
77c23f
+
77c23f
+/*
77c23f
+ * Called for each component in the SE type IPL parameter block 0.
77c23f
+ */
77c23f
+int s390_pv_unpack(uint64_t addr, uint64_t size, uint64_t tweak)
77c23f
+{
77c23f
+    struct kvm_s390_pv_unp args = {
77c23f
+        .addr = addr,
77c23f
+        .size = size,
77c23f
+        .tweak = tweak,
77c23f
+    };
77c23f
+
77c23f
+    return s390_pv_cmd(KVM_PV_UNPACK, &args);
77c23f
+}
77c23f
+
77c23f
+void s390_pv_perf_clear_reset(void)
77c23f
+{
77c23f
+    s390_pv_cmd_exit(KVM_PV_PREP_RESET, NULL);
77c23f
+}
77c23f
+
77c23f
+int s390_pv_verify(void)
77c23f
+{
77c23f
+    return s390_pv_cmd(KVM_PV_VERIFY, NULL);
77c23f
+}
77c23f
+
77c23f
+void s390_pv_unshare(void)
77c23f
+{
77c23f
+    s390_pv_cmd_exit(KVM_PV_UNSHARE_ALL, NULL);
77c23f
+}
77c23f
diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c
77c23f
index 4ea01c53c0..82da1d9ab5 100644
77c23f
--- a/hw/s390x/s390-virtio-ccw.c
77c23f
+++ b/hw/s390x/s390-virtio-ccw.c
77c23f
@@ -1,9 +1,10 @@
77c23f
 /*
77c23f
  * virtio ccw machine
77c23f
  *
77c23f
- * Copyright 2012 IBM Corp.
77c23f
+ * Copyright 2012, 2020 IBM Corp.
77c23f
  * Copyright (c) 2009 Alexander Graf <agraf@suse.de>
77c23f
  * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com>
77c23f
+ *            Janosch Frank <frankja@linux.ibm.com>
77c23f
  *
77c23f
  * This work is licensed under the terms of the GNU GPL, version 2 or (at
77c23f
  * your option) any later version. See the COPYING file in the top-level
77c23f
@@ -41,6 +42,8 @@
77c23f
 #include "hw/qdev-properties.h"
77c23f
 #include "hw/s390x/tod.h"
77c23f
 #include "sysemu/sysemu.h"
77c23f
+#include "hw/s390x/pv.h"
77c23f
+#include <linux/kvm.h>
77c23f
 
77c23f
 S390CPU *s390_cpu_addr2state(uint16_t cpu_addr)
77c23f
 {
77c23f
@@ -318,10 +321,78 @@ static inline void s390_do_cpu_ipl(CPUState *cs, run_on_cpu_data arg)
77c23f
     s390_cpu_set_state(S390_CPU_STATE_OPERATING, cpu);
77c23f
 }
77c23f
 
77c23f
+static void s390_machine_unprotect(S390CcwMachineState *ms)
77c23f
+{
77c23f
+    s390_pv_vm_disable();
77c23f
+    ms->pv = false;
77c23f
+}
77c23f
+
77c23f
+static int s390_machine_protect(S390CcwMachineState *ms)
77c23f
+{
77c23f
+    int rc;
77c23f
+
77c23f
+    /* Create SE VM */
77c23f
+    rc = s390_pv_vm_enable();
77c23f
+    if (rc) {
77c23f
+        return rc;
77c23f
+    }
77c23f
+
77c23f
+    ms->pv = true;
77c23f
+
77c23f
+    /* Set SE header and unpack */
77c23f
+    rc = s390_ipl_prepare_pv_header();
77c23f
+    if (rc) {
77c23f
+        goto out_err;
77c23f
+    }
77c23f
+
77c23f
+    /* Decrypt image */
77c23f
+    rc = s390_ipl_pv_unpack();
77c23f
+    if (rc) {
77c23f
+        goto out_err;
77c23f
+    }
77c23f
+
77c23f
+    /* Verify integrity */
77c23f
+    rc = s390_pv_verify();
77c23f
+    if (rc) {
77c23f
+        goto out_err;
77c23f
+    }
77c23f
+    return rc;
77c23f
+
77c23f
+out_err:
77c23f
+    s390_machine_unprotect(ms);
77c23f
+    return rc;
77c23f
+}
77c23f
+
77c23f
+static void s390_machine_inject_pv_error(CPUState *cs)
77c23f
+{
77c23f
+    int r1 = (cs->kvm_run->s390_sieic.ipa & 0x00f0) >> 4;
77c23f
+    CPUS390XState *env = &S390_CPU(cs)->env;
77c23f
+
77c23f
+    /* Report that we are unable to enter protected mode */
77c23f
+    env->regs[r1 + 1] = DIAG_308_RC_INVAL_FOR_PV;
77c23f
+}
77c23f
+
77c23f
+static void s390_pv_prepare_reset(S390CcwMachineState *ms)
77c23f
+{
77c23f
+    CPUState *cs;
77c23f
+
77c23f
+    if (!s390_is_pv()) {
77c23f
+        return;
77c23f
+    }
77c23f
+    /* Unsharing requires all cpus to be stopped */
77c23f
+    CPU_FOREACH(cs) {
77c23f
+        s390_cpu_set_state(S390_CPU_STATE_STOPPED, S390_CPU(cs));
77c23f
+    }
77c23f
+    s390_pv_unshare();
77c23f
+    s390_pv_perf_clear_reset();
77c23f
+}
77c23f
+
77c23f
 static void s390_machine_reset(MachineState *machine)
77c23f
 {
77c23f
+    S390CcwMachineState *ms = S390_CCW_MACHINE(machine);
77c23f
     enum s390_reset reset_type;
77c23f
     CPUState *cs, *t;
77c23f
+    S390CPU *cpu;
77c23f
 
77c23f
     /* get the reset parameters, reset them once done */
77c23f
     s390_ipl_get_reset_request(&cs, &reset_type);
77c23f
@@ -329,9 +400,15 @@ static void s390_machine_reset(MachineState *machine)
77c23f
     /* all CPUs are paused and synchronized at this point */
77c23f
     s390_cmma_reset();
77c23f
 
77c23f
+    cpu = S390_CPU(cs);
77c23f
+
77c23f
     switch (reset_type) {
77c23f
     case S390_RESET_EXTERNAL:
77c23f
     case S390_RESET_REIPL:
77c23f
+        if (s390_is_pv()) {
77c23f
+            s390_machine_unprotect(ms);
77c23f
+        }
77c23f
+
77c23f
         qemu_devices_reset();
77c23f
         s390_crypto_reset();
77c23f
 
77c23f
@@ -339,22 +416,56 @@ static void s390_machine_reset(MachineState *machine)
77c23f
         run_on_cpu(cs, s390_do_cpu_ipl, RUN_ON_CPU_NULL);
77c23f
         break;
77c23f
     case S390_RESET_MODIFIED_CLEAR:
77c23f
+        /*
77c23f
+         * Susbsystem reset needs to be done before we unshare memory
77c23f
+         * and lose access to VIRTIO structures in guest memory.
77c23f
+         */
77c23f
+        subsystem_reset();
77c23f
+        s390_crypto_reset();
77c23f
+        s390_pv_prepare_reset(ms);
77c23f
         CPU_FOREACH(t) {
77c23f
             run_on_cpu(t, s390_do_cpu_full_reset, RUN_ON_CPU_NULL);
77c23f
         }
77c23f
-        subsystem_reset();
77c23f
-        s390_crypto_reset();
77c23f
         run_on_cpu(cs, s390_do_cpu_load_normal, RUN_ON_CPU_NULL);
77c23f
         break;
77c23f
     case S390_RESET_LOAD_NORMAL:
77c23f
+        /*
77c23f
+         * Susbsystem reset needs to be done before we unshare memory
77c23f
+         * and lose access to VIRTIO structures in guest memory.
77c23f
+         */
77c23f
+        subsystem_reset();
77c23f
+        s390_pv_prepare_reset(ms);
77c23f
         CPU_FOREACH(t) {
77c23f
             if (t == cs) {
77c23f
                 continue;
77c23f
             }
77c23f
             run_on_cpu(t, s390_do_cpu_reset, RUN_ON_CPU_NULL);
77c23f
         }
77c23f
-        subsystem_reset();
77c23f
         run_on_cpu(cs, s390_do_cpu_initial_reset, RUN_ON_CPU_NULL);
77c23f
+        run_on_cpu(cs, s390_do_cpu_load_normal, RUN_ON_CPU_NULL);
77c23f
+        break;
77c23f
+    case S390_RESET_PV: /* Subcode 10 */
77c23f
+        subsystem_reset();
77c23f
+        s390_crypto_reset();
77c23f
+
77c23f
+        CPU_FOREACH(t) {
77c23f
+            if (t == cs) {
77c23f
+                continue;
77c23f
+            }
77c23f
+            run_on_cpu(t, s390_do_cpu_full_reset, RUN_ON_CPU_NULL);
77c23f
+        }
77c23f
+        run_on_cpu(cs, s390_do_cpu_reset, RUN_ON_CPU_NULL);
77c23f
+
77c23f
+        if (s390_machine_protect(ms)) {
77c23f
+            s390_machine_inject_pv_error(cs);
77c23f
+            /*
77c23f
+             * Continue after the diag308 so the guest knows something
77c23f
+             * went wrong.
77c23f
+             */
77c23f
+            s390_cpu_set_state(S390_CPU_STATE_OPERATING, cpu);
77c23f
+            return;
77c23f
+        }
77c23f
+
77c23f
         run_on_cpu(cs, s390_do_cpu_load_normal, RUN_ON_CPU_NULL);
77c23f
         break;
77c23f
     default:
77c23f
diff --git a/include/hw/s390x/pv.h b/include/hw/s390x/pv.h
77c23f
new file mode 100644
77c23f
index 0000000000..c6cb360f2f
77c23f
--- /dev/null
77c23f
+++ b/include/hw/s390x/pv.h
77c23f
@@ -0,0 +1,55 @@
77c23f
+/*
77c23f
+ * Protected Virtualization header
77c23f
+ *
77c23f
+ * Copyright IBM Corp. 2020
77c23f
+ * Author(s):
77c23f
+ *  Janosch Frank <frankja@linux.ibm.com>
77c23f
+ *
77c23f
+ * This work is licensed under the terms of the GNU GPL, version 2 or (at
77c23f
+ * your option) any later version. See the COPYING file in the top-level
77c23f
+ * directory.
77c23f
+ */
77c23f
+#ifndef HW_S390_PV_H
77c23f
+#define HW_S390_PV_H
77c23f
+
77c23f
+#ifdef CONFIG_KVM
77c23f
+#include "hw/s390x/s390-virtio-ccw.h"
77c23f
+
77c23f
+static inline bool s390_is_pv(void)
77c23f
+{
77c23f
+    static S390CcwMachineState *ccw;
77c23f
+    Object *obj;
77c23f
+
77c23f
+    if (ccw) {
77c23f
+        return ccw->pv;
77c23f
+    }
77c23f
+
77c23f
+    /* we have to bail out for the "none" machine */
77c23f
+    obj = object_dynamic_cast(qdev_get_machine(),
77c23f
+                              TYPE_S390_CCW_MACHINE);
77c23f
+    if (!obj) {
77c23f
+        return false;
77c23f
+    }
77c23f
+    ccw = S390_CCW_MACHINE(obj);
77c23f
+    return ccw->pv;
77c23f
+}
77c23f
+
77c23f
+int s390_pv_vm_enable(void);
77c23f
+void s390_pv_vm_disable(void);
77c23f
+int s390_pv_set_sec_parms(uint64_t origin, uint64_t length);
77c23f
+int s390_pv_unpack(uint64_t addr, uint64_t size, uint64_t tweak);
77c23f
+void s390_pv_perf_clear_reset(void);
77c23f
+int s390_pv_verify(void);
77c23f
+void s390_pv_unshare(void);
77c23f
+#else /* CONFIG_KVM */
77c23f
+static inline bool s390_is_pv(void) { return false; }
77c23f
+static inline int s390_pv_vm_enable(void) { return 0; }
77c23f
+static inline void s390_pv_vm_disable(void) {}
77c23f
+static inline int s390_pv_set_sec_parms(uint64_t origin, uint64_t length) { return 0; }
77c23f
+static inline int s390_pv_unpack(uint64_t addr, uint64_t size, uint64_t tweak) { return 0; }
77c23f
+static inline void s390_pv_perf_clear_reset(void) {}
77c23f
+static inline int s390_pv_verify(void) { return 0; }
77c23f
+static inline void s390_pv_unshare(void) {}
77c23f
+#endif /* CONFIG_KVM */
77c23f
+
77c23f
+#endif /* HW_S390_PV_H */
77c23f
diff --git a/include/hw/s390x/s390-virtio-ccw.h b/include/hw/s390x/s390-virtio-ccw.h
77c23f
index 8aa27199c9..cd1dccc6e3 100644
77c23f
--- a/include/hw/s390x/s390-virtio-ccw.h
77c23f
+++ b/include/hw/s390x/s390-virtio-ccw.h
77c23f
@@ -28,6 +28,7 @@ typedef struct S390CcwMachineState {
77c23f
     /*< public >*/
77c23f
     bool aes_key_wrap;
77c23f
     bool dea_key_wrap;
77c23f
+    bool pv;
77c23f
     uint8_t loadparm[8];
77c23f
 } S390CcwMachineState;
77c23f
 
77c23f
diff --git a/target/s390x/cpu.c b/target/s390x/cpu.c
77c23f
index c0dd502b84..8f38cd8e6f 100644
77c23f
--- a/target/s390x/cpu.c
77c23f
+++ b/target/s390x/cpu.c
77c23f
@@ -37,6 +37,7 @@
77c23f
 #include "sysemu/hw_accel.h"
77c23f
 #include "hw/qdev-properties.h"
77c23f
 #ifndef CONFIG_USER_ONLY
77c23f
+#include "hw/s390x/pv.h"
77c23f
 #include "hw/boards.h"
77c23f
 #include "sysemu/arch_init.h"
77c23f
 #include "sysemu/sysemu.h"
77c23f
diff --git a/target/s390x/cpu_features_def.inc.h b/target/s390x/cpu_features_def.inc.h
77c23f
index 31dff0d84e..60db28351d 100644
77c23f
--- a/target/s390x/cpu_features_def.inc.h
77c23f
+++ b/target/s390x/cpu_features_def.inc.h
77c23f
@@ -107,6 +107,7 @@ DEF_FEAT(DEFLATE_BASE, "deflate-base", STFL, 151, "Deflate-conversion facility (
77c23f
 DEF_FEAT(VECTOR_PACKED_DECIMAL_ENH, "vxpdeh", STFL, 152, "Vector-Packed-Decimal-Enhancement Facility")
77c23f
 DEF_FEAT(MSA_EXT_9, "msa9-base", STFL, 155, "Message-security-assist-extension-9 facility (excluding subfunctions)")
77c23f
 DEF_FEAT(ETOKEN, "etoken", STFL, 156, "Etoken facility")
77c23f
+DEF_FEAT(UNPACK, "unpack", STFL, 161, "Unpack facility")
77c23f
 
77c23f
 /* Features exposed via SCLP SCCB Byte 80 - 98  (bit numbers relative to byte-80) */
77c23f
 DEF_FEAT(SIE_GSLS, "gsls", SCLP_CONF_CHAR, 40, "SIE: Guest-storage-limit-suppression facility")
77c23f
diff --git a/target/s390x/diag.c b/target/s390x/diag.c
77c23f
index 8aba6341f9..b2cbefb8cf 100644
77c23f
--- a/target/s390x/diag.c
77c23f
+++ b/target/s390x/diag.c
77c23f
@@ -20,6 +20,8 @@
77c23f
 #include "sysemu/cpus.h"
77c23f
 #include "hw/s390x/ipl.h"
77c23f
 #include "hw/s390x/s390-virtio-ccw.h"
77c23f
+#include "hw/s390x/pv.h"
77c23f
+#include "kvm_s390x.h"
77c23f
 
77c23f
 int handle_diag_288(CPUS390XState *env, uint64_t r1, uint64_t r3)
77c23f
 {
77c23f
@@ -52,6 +54,10 @@ int handle_diag_288(CPUS390XState *env, uint64_t r1, uint64_t r3)
77c23f
 static int diag308_parm_check(CPUS390XState *env, uint64_t r1, uint64_t addr,
77c23f
                               uintptr_t ra, bool write)
77c23f
 {
77c23f
+    /* Handled by the Ultravisor */
77c23f
+    if (s390_is_pv()) {
77c23f
+        return 0;
77c23f
+    }
77c23f
     if ((r1 & 1) || (addr & ~TARGET_PAGE_MASK)) {
77c23f
         s390_program_interrupt(env, PGM_SPECIFICATION, ra);
77c23f
         return -1;
77c23f
@@ -67,6 +73,7 @@ static int diag308_parm_check(CPUS390XState *env, uint64_t r1, uint64_t addr,
77c23f
 
77c23f
 void handle_diag_308(CPUS390XState *env, uint64_t r1, uint64_t r3, uintptr_t ra)
77c23f
 {
77c23f
+    bool valid;
77c23f
     CPUState *cs = env_cpu(env);
77c23f
     uint64_t addr =  env->regs[r1];
77c23f
     uint64_t subcode = env->regs[r3];
77c23f
@@ -82,6 +89,11 @@ void handle_diag_308(CPUS390XState *env, uint64_t r1, uint64_t r3, uintptr_t ra)
77c23f
         return;
77c23f
     }
77c23f
 
77c23f
+    if (subcode >= DIAG308_PV_SET && !s390_has_feat(S390_FEAT_UNPACK)) {
77c23f
+        s390_program_interrupt(env, PGM_SPECIFICATION, ra);
77c23f
+        return;
77c23f
+    }
77c23f
+
77c23f
     switch (subcode) {
77c23f
     case DIAG308_RESET_MOD_CLR:
77c23f
         s390_ipl_reset_request(cs, S390_RESET_MODIFIED_CLEAR);
77c23f
@@ -94,6 +106,7 @@ void handle_diag_308(CPUS390XState *env, uint64_t r1, uint64_t r3, uintptr_t ra)
77c23f
         s390_ipl_reset_request(cs, S390_RESET_REIPL);
77c23f
         break;
77c23f
     case DIAG308_SET:
77c23f
+    case DIAG308_PV_SET:
77c23f
         if (diag308_parm_check(env, r1, addr, ra, false)) {
77c23f
             return;
77c23f
         }
77c23f
@@ -106,7 +119,8 @@ void handle_diag_308(CPUS390XState *env, uint64_t r1, uint64_t r3, uintptr_t ra)
77c23f
 
77c23f
         cpu_physical_memory_read(addr, iplb, be32_to_cpu(iplb->len));
77c23f
 
77c23f
-        if (!iplb_valid(iplb)) {
77c23f
+        valid = subcode == DIAG308_PV_SET ? iplb_valid_pv(iplb) : iplb_valid(iplb);
77c23f
+        if (!valid) {
77c23f
             env->regs[r1 + 1] = DIAG_308_RC_INVALID;
77c23f
             goto out;
77c23f
         }
77c23f
@@ -117,10 +131,15 @@ out:
77c23f
         g_free(iplb);
77c23f
         return;
77c23f
     case DIAG308_STORE:
77c23f
+    case DIAG308_PV_STORE:
77c23f
         if (diag308_parm_check(env, r1, addr, ra, true)) {
77c23f
             return;
77c23f
         }
77c23f
-        iplb = s390_ipl_get_iplb();
77c23f
+        if (subcode == DIAG308_PV_STORE) {
77c23f
+            iplb = s390_ipl_get_iplb_pv();
77c23f
+        } else {
77c23f
+            iplb = s390_ipl_get_iplb();
77c23f
+        }
77c23f
         if (iplb) {
77c23f
             cpu_physical_memory_write(addr, iplb, be32_to_cpu(iplb->len));
77c23f
             env->regs[r1 + 1] = DIAG_308_RC_OK;
77c23f
@@ -128,6 +147,22 @@ out:
77c23f
             env->regs[r1 + 1] = DIAG_308_RC_NO_CONF;
77c23f
         }
77c23f
         return;
77c23f
+    case DIAG308_PV_START:
77c23f
+        iplb = s390_ipl_get_iplb_pv();
77c23f
+        if (!iplb) {
77c23f
+            env->regs[r1 + 1] = DIAG_308_RC_NO_PV_CONF;
77c23f
+            return;
77c23f
+        }
77c23f
+
77c23f
+        if (kvm_s390_get_hpage_1m()) {
77c23f
+            error_report("Protected VMs can currently not be backed with "
77c23f
+                         "huge pages");
77c23f
+            env->regs[r1 + 1] = DIAG_308_RC_INVAL_FOR_PV;
77c23f
+            return;
77c23f
+        }
77c23f
+
77c23f
+        s390_ipl_reset_request(cs, S390_RESET_PV);
77c23f
+        break;
77c23f
     default:
77c23f
         s390_program_interrupt(env, PGM_SPECIFICATION, ra);
77c23f
         break;
77c23f
diff --git a/target/s390x/kvm-stub.c b/target/s390x/kvm-stub.c
77c23f
index c4cd497f85..aa185017a2 100644
77c23f
--- a/target/s390x/kvm-stub.c
77c23f
+++ b/target/s390x/kvm-stub.c
77c23f
@@ -39,6 +39,11 @@ int kvm_s390_vcpu_interrupt_post_load(S390CPU *cpu)
77c23f
     return 0;
77c23f
 }
77c23f
 
77c23f
+int kvm_s390_get_hpage_1m(void)
77c23f
+{
77c23f
+    return 0;
77c23f
+}
77c23f
+
77c23f
 int kvm_s390_get_ri(void)
77c23f
 {
77c23f
     return 0;
77c23f
diff --git a/target/s390x/kvm.c b/target/s390x/kvm.c
77c23f
index 75d82af6fc..9a0be13959 100644
77c23f
--- a/target/s390x/kvm.c
77c23f
+++ b/target/s390x/kvm.c
77c23f
@@ -321,6 +321,11 @@ void kvm_s390_set_max_pagesize(uint64_t pagesize, Error **errp)
77c23f
     cap_hpage_1m = 1;
77c23f
 }
77c23f
 
77c23f
+int kvm_s390_get_hpage_1m(void)
77c23f
+{
77c23f
+    return cap_hpage_1m;
77c23f
+}
77c23f
+
77c23f
 static void ccw_machine_class_foreach(ObjectClass *oc, void *opaque)
77c23f
 {
77c23f
     MachineClass *mc = MACHINE_CLASS(oc);
77c23f
diff --git a/target/s390x/kvm_s390x.h b/target/s390x/kvm_s390x.h
77c23f
index 0b21789796..dea813f450 100644
77c23f
--- a/target/s390x/kvm_s390x.h
77c23f
+++ b/target/s390x/kvm_s390x.h
77c23f
@@ -23,6 +23,7 @@ void kvm_s390_program_interrupt(S390CPU *cpu, uint16_t code);
77c23f
 int kvm_s390_set_cpu_state(S390CPU *cpu, uint8_t cpu_state);
77c23f
 void kvm_s390_vcpu_interrupt_pre_save(S390CPU *cpu);
77c23f
 int kvm_s390_vcpu_interrupt_post_load(S390CPU *cpu);
77c23f
+int kvm_s390_get_hpage_1m(void);
77c23f
 int kvm_s390_get_ri(void);
77c23f
 int kvm_s390_get_gs(void);
77c23f
 int kvm_s390_get_clock(uint8_t *tod_high, uint64_t *tod_clock);
77c23f
-- 
77c23f
2.27.0
77c23f