From f5f9c6ea11bc807664fdeb9354915c2c9cdcbd89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 24 Jun 2023 22:06:44 +0200 Subject: hw/s390x: Move KVM specific PV from hw/ to target/s390x/kvm/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Protected Virtualization (PV) is not a real hardware device: it is a feature of the firmware on s390x that is exposed to userspace via the KVM interface. Move the pv.c/pv.h files to target/s390x/kvm/ to make this clearer. Suggested-by: Thomas Huth Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20230624200644.23931-1-philmd@linaro.org> Signed-off-by: Thomas Huth --- MAINTAINERS | 2 - hw/s390x/ipl.c | 2 +- hw/s390x/meson.build | 1 - hw/s390x/pv.c | 360 ------------------------------------------- hw/s390x/s390-pci-kvm.c | 2 +- hw/s390x/s390-virtio-ccw.c | 2 +- hw/s390x/tod-kvm.c | 2 +- include/hw/s390x/pv.h | 96 ------------ target/s390x/arch_dump.c | 2 +- target/s390x/cpu-sysemu.c | 2 +- target/s390x/cpu_features.c | 2 +- target/s390x/cpu_models.c | 2 +- target/s390x/diag.c | 2 +- target/s390x/helper.c | 2 +- target/s390x/ioinst.c | 2 +- target/s390x/kvm/kvm.c | 2 +- target/s390x/kvm/meson.build | 1 + target/s390x/kvm/pv.c | 360 +++++++++++++++++++++++++++++++++++++++++++ target/s390x/kvm/pv.h | 96 ++++++++++++ 19 files changed, 469 insertions(+), 471 deletions(-) delete mode 100644 hw/s390x/pv.c delete mode 100644 include/hw/s390x/pv.h create mode 100644 target/s390x/kvm/pv.c create mode 100644 target/s390x/kvm/pv.h diff --git a/MAINTAINERS b/MAINTAINERS index 1817cfc..43bd9af 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -452,8 +452,6 @@ S: Supported F: target/s390x/kvm/ F: target/s390x/machine.c F: target/s390x/sigp.c -F: hw/s390x/pv.c -F: include/hw/s390x/pv.h F: gdb-xml/s390*.xml T: git https://github.com/borntraeger/qemu.git s390-next L: qemu-s390x@nongnu.org diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c index 8612684..515dcf5 100644 --- a/hw/s390x/ipl.c +++ b/hw/s390x/ipl.c @@ -26,7 +26,7 @@ #include "hw/s390x/vfio-ccw.h" #include "hw/s390x/css.h" #include "hw/s390x/ebcdic.h" -#include "hw/s390x/pv.h" +#include "target/s390x/kvm/pv.h" #include "hw/scsi/scsi.h" #include "hw/virtio/virtio-net.h" #include "ipl.h" diff --git a/hw/s390x/meson.build b/hw/s390x/meson.build index f291016..6fd0968 100644 --- a/hw/s390x/meson.build +++ b/hw/s390x/meson.build @@ -22,7 +22,6 @@ s390x_ss.add(when: 'CONFIG_KVM', if_true: files( 'tod-kvm.c', 's390-skeys-kvm.c', 's390-stattrib-kvm.c', - 'pv.c', 's390-pci-kvm.c', )) s390x_ss.add(when: 'CONFIG_TCG', if_true: files( diff --git a/hw/s390x/pv.c b/hw/s390x/pv.c deleted file mode 100644 index b63f378..0000000 --- a/hw/s390x/pv.c +++ /dev/null @@ -1,360 +0,0 @@ -/* - * Protected Virtualization functions - * - * Copyright IBM Corp. 2020 - * Author(s): - * Janosch Frank - * - * This work is licensed under the terms of the GNU GPL, version 2 or (at - * your option) any later version. See the COPYING file in the top-level - * directory. - */ -#include "qemu/osdep.h" - -#include - -#include "qemu/units.h" -#include "qapi/error.h" -#include "qemu/error-report.h" -#include "sysemu/kvm.h" -#include "sysemu/cpus.h" -#include "qom/object_interfaces.h" -#include "exec/confidential-guest-support.h" -#include "hw/s390x/ipl.h" -#include "hw/s390x/pv.h" -#include "hw/s390x/sclp.h" -#include "target/s390x/kvm/kvm_s390x.h" - -static bool info_valid; -static struct kvm_s390_pv_info_vm info_vm; -static struct kvm_s390_pv_info_dump info_dump; - -static int __s390_pv_cmd(uint32_t cmd, const char *cmdname, void *data) -{ - struct kvm_pv_cmd pv_cmd = { - .cmd = cmd, - .data = (uint64_t)data, - }; - int rc; - - do { - rc = kvm_vm_ioctl(kvm_state, KVM_S390_PV_COMMAND, &pv_cmd); - } while (rc == -EINTR); - - if (rc) { - error_report("KVM PV command %d (%s) failed: header rc %x rrc %x " - "IOCTL rc: %d", cmd, cmdname, pv_cmd.rc, pv_cmd.rrc, - rc); - } - return rc; -} - -/* - * This macro lets us pass the command as a string to the function so - * we can print it on an error. - */ -#define s390_pv_cmd(cmd, data) __s390_pv_cmd(cmd, #cmd, data) -#define s390_pv_cmd_exit(cmd, data) \ -{ \ - int rc; \ - \ - rc = __s390_pv_cmd(cmd, #cmd, data);\ - if (rc) { \ - exit(1); \ - } \ -} - -int s390_pv_query_info(void) -{ - struct kvm_s390_pv_info info = { - .header.id = KVM_PV_INFO_VM, - .header.len_max = sizeof(info.header) + sizeof(info.vm), - }; - int rc; - - /* Info API's first user is dump so they are bundled */ - if (!kvm_s390_get_protected_dump()) { - return 0; - } - - rc = s390_pv_cmd(KVM_PV_INFO, &info); - if (rc) { - error_report("KVM PV INFO cmd %x failed: %s", - info.header.id, strerror(-rc)); - return rc; - } - memcpy(&info_vm, &info.vm, sizeof(info.vm)); - - info.header.id = KVM_PV_INFO_DUMP; - info.header.len_max = sizeof(info.header) + sizeof(info.dump); - rc = s390_pv_cmd(KVM_PV_INFO, &info); - if (rc) { - error_report("KVM PV INFO cmd %x failed: %s", - info.header.id, strerror(-rc)); - return rc; - } - - memcpy(&info_dump, &info.dump, sizeof(info.dump)); - info_valid = true; - - return rc; -} - -int s390_pv_vm_enable(void) -{ - return s390_pv_cmd(KVM_PV_ENABLE, NULL); -} - -void s390_pv_vm_disable(void) -{ - s390_pv_cmd_exit(KVM_PV_DISABLE, NULL); -} - -static void *s390_pv_do_unprot_async_fn(void *p) -{ - s390_pv_cmd_exit(KVM_PV_ASYNC_CLEANUP_PERFORM, NULL); - return NULL; -} - -bool s390_pv_vm_try_disable_async(S390CcwMachineState *ms) -{ - /* - * t is only needed to create the thread; once qemu_thread_create - * returns, it can safely be discarded. - */ - QemuThread t; - - /* - * If the feature is not present or if the VM is not larger than 2 GiB, - * KVM_PV_ASYNC_CLEANUP_PREPARE fill fail; no point in attempting it. - */ - if ((MACHINE(ms)->maxram_size <= 2 * GiB) || - !kvm_check_extension(kvm_state, KVM_CAP_S390_PROTECTED_ASYNC_DISABLE)) { - return false; - } - if (s390_pv_cmd(KVM_PV_ASYNC_CLEANUP_PREPARE, NULL) != 0) { - return false; - } - - qemu_thread_create(&t, "async_cleanup", s390_pv_do_unprot_async_fn, NULL, - QEMU_THREAD_DETACHED); - - return true; -} - -int s390_pv_set_sec_parms(uint64_t origin, uint64_t length) -{ - struct kvm_s390_pv_sec_parm args = { - .origin = origin, - .length = length, - }; - - return s390_pv_cmd(KVM_PV_SET_SEC_PARMS, &args); -} - -/* - * Called for each component in the SE type IPL parameter block 0. - */ -int s390_pv_unpack(uint64_t addr, uint64_t size, uint64_t tweak) -{ - struct kvm_s390_pv_unp args = { - .addr = addr, - .size = size, - .tweak = tweak, - }; - - return s390_pv_cmd(KVM_PV_UNPACK, &args); -} - -void s390_pv_prep_reset(void) -{ - s390_pv_cmd_exit(KVM_PV_PREP_RESET, NULL); -} - -int s390_pv_verify(void) -{ - return s390_pv_cmd(KVM_PV_VERIFY, NULL); -} - -void s390_pv_unshare(void) -{ - s390_pv_cmd_exit(KVM_PV_UNSHARE_ALL, NULL); -} - -void s390_pv_inject_reset_error(CPUState *cs) -{ - int r1 = (cs->kvm_run->s390_sieic.ipa & 0x00f0) >> 4; - CPUS390XState *env = &S390_CPU(cs)->env; - - /* Report that we are unable to enter protected mode */ - env->regs[r1 + 1] = DIAG_308_RC_INVAL_FOR_PV; -} - -uint64_t kvm_s390_pv_dmp_get_size_cpu(void) -{ - return info_dump.dump_cpu_buffer_len; -} - -uint64_t kvm_s390_pv_dmp_get_size_completion_data(void) -{ - return info_dump.dump_config_finalize_len; -} - -uint64_t kvm_s390_pv_dmp_get_size_mem_state(void) -{ - return info_dump.dump_config_mem_buffer_per_1m; -} - -bool kvm_s390_pv_info_basic_valid(void) -{ - return info_valid; -} - -static int s390_pv_dump_cmd(uint64_t subcmd, uint64_t uaddr, uint64_t gaddr, - uint64_t len) -{ - struct kvm_s390_pv_dmp dmp = { - .subcmd = subcmd, - .buff_addr = uaddr, - .buff_len = len, - .gaddr = gaddr, - }; - int ret; - - ret = s390_pv_cmd(KVM_PV_DUMP, (void *)&dmp); - if (ret) { - error_report("KVM DUMP command %ld failed", subcmd); - } - return ret; -} - -int kvm_s390_dump_cpu(S390CPU *cpu, void *buff) -{ - struct kvm_s390_pv_dmp dmp = { - .subcmd = KVM_PV_DUMP_CPU, - .buff_addr = (uint64_t)buff, - .gaddr = 0, - .buff_len = info_dump.dump_cpu_buffer_len, - }; - struct kvm_pv_cmd pv = { - .cmd = KVM_PV_DUMP, - .data = (uint64_t)&dmp, - }; - - return kvm_vcpu_ioctl(CPU(cpu), KVM_S390_PV_CPU_COMMAND, &pv); -} - -int kvm_s390_dump_init(void) -{ - return s390_pv_dump_cmd(KVM_PV_DUMP_INIT, 0, 0, 0); -} - -int kvm_s390_dump_mem_state(uint64_t gaddr, size_t len, void *dest) -{ - return s390_pv_dump_cmd(KVM_PV_DUMP_CONFIG_STOR_STATE, (uint64_t)dest, - gaddr, len); -} - -int kvm_s390_dump_completion_data(void *buff) -{ - return s390_pv_dump_cmd(KVM_PV_DUMP_COMPLETE, (uint64_t)buff, 0, - info_dump.dump_config_finalize_len); -} - -#define TYPE_S390_PV_GUEST "s390-pv-guest" -OBJECT_DECLARE_SIMPLE_TYPE(S390PVGuest, S390_PV_GUEST) - -/** - * S390PVGuest: - * - * The S390PVGuest object is basically a dummy used to tell the - * confidential guest support system to use s390's PV mechanism. - * - * # $QEMU \ - * -object s390-pv-guest,id=pv0 \ - * -machine ...,confidential-guest-support=pv0 - */ -struct S390PVGuest { - ConfidentialGuestSupport parent_obj; -}; - -typedef struct S390PVGuestClass S390PVGuestClass; - -struct S390PVGuestClass { - ConfidentialGuestSupportClass parent_class; -}; - -/* - * If protected virtualization is enabled, the amount of data that the - * Read SCP Info Service Call can use is limited to one page. The - * available space also depends on the Extended-Length SCCB (ELS) - * feature which can take more buffer space to store feature - * information. This impacts the maximum number of CPUs supported in - * the machine. - */ -static uint32_t s390_pv_get_max_cpus(void) -{ - int offset_cpu = s390_has_feat(S390_FEAT_EXTENDED_LENGTH_SCCB) ? - offsetof(ReadInfo, entries) : SCLP_READ_SCP_INFO_FIXED_CPU_OFFSET; - - return (TARGET_PAGE_SIZE - offset_cpu) / sizeof(CPUEntry); -} - -static bool s390_pv_check_cpus(Error **errp) -{ - MachineState *ms = MACHINE(qdev_get_machine()); - uint32_t pv_max_cpus = s390_pv_get_max_cpus(); - - if (ms->smp.max_cpus > pv_max_cpus) { - error_setg(errp, "Protected VMs support a maximum of %d CPUs", - pv_max_cpus); - return false; - } - - return true; -} - -static bool s390_pv_guest_check(ConfidentialGuestSupport *cgs, Error **errp) -{ - return s390_pv_check_cpus(errp); -} - -int s390_pv_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) -{ - if (!object_dynamic_cast(OBJECT(cgs), TYPE_S390_PV_GUEST)) { - return 0; - } - - if (!s390_has_feat(S390_FEAT_UNPACK)) { - error_setg(errp, - "CPU model does not support Protected Virtualization"); - return -1; - } - - if (!s390_pv_guest_check(cgs, errp)) { - return -1; - } - - cgs->ready = true; - - return 0; -} - -OBJECT_DEFINE_TYPE_WITH_INTERFACES(S390PVGuest, - s390_pv_guest, - S390_PV_GUEST, - CONFIDENTIAL_GUEST_SUPPORT, - { TYPE_USER_CREATABLE }, - { NULL }) - -static void s390_pv_guest_class_init(ObjectClass *oc, void *data) -{ -} - -static void s390_pv_guest_init(Object *obj) -{ -} - -static void s390_pv_guest_finalize(Object *obj) -{ -} diff --git a/hw/s390x/s390-pci-kvm.c b/hw/s390x/s390-pci-kvm.c index 9134fe1..ff41e41 100644 --- a/hw/s390x/s390-pci-kvm.c +++ b/hw/s390x/s390-pci-kvm.c @@ -14,7 +14,7 @@ #include #include "kvm/kvm_s390x.h" -#include "hw/s390x/pv.h" +#include "target/s390x/kvm/pv.h" #include "hw/s390x/s390-pci-bus.h" #include "hw/s390x/s390-pci-kvm.h" #include "hw/s390x/s390-pci-inst.h" diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index 2dece8e..4516d73 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -42,7 +42,7 @@ #include "hw/s390x/tod.h" #include "sysemu/sysemu.h" #include "sysemu/cpus.h" -#include "hw/s390x/pv.h" +#include "target/s390x/kvm/pv.h" #include "migration/blocker.h" #include "qapi/visitor.h" diff --git a/hw/s390x/tod-kvm.c b/hw/s390x/tod-kvm.c index e2202da..9588b90 100644 --- a/hw/s390x/tod-kvm.c +++ b/hw/s390x/tod-kvm.c @@ -13,7 +13,7 @@ #include "qemu/module.h" #include "sysemu/runstate.h" #include "hw/s390x/tod.h" -#include "hw/s390x/pv.h" +#include "target/s390x/kvm/pv.h" #include "kvm/kvm_s390x.h" static void kvm_s390_get_tod_raw(S390TOD *tod, Error **errp) diff --git a/include/hw/s390x/pv.h b/include/hw/s390x/pv.h deleted file mode 100644 index 7b935e2..0000000 --- a/include/hw/s390x/pv.h +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Protected Virtualization header - * - * Copyright IBM Corp. 2020 - * Author(s): - * Janosch Frank - * - * This work is licensed under the terms of the GNU GPL, version 2 or (at - * your option) any later version. See the COPYING file in the top-level - * directory. - */ -#ifndef HW_S390_PV_H -#define HW_S390_PV_H - -#include "qapi/error.h" -#include "sysemu/kvm.h" -#include "hw/s390x/s390-virtio-ccw.h" - -#ifdef CONFIG_KVM -#include "cpu.h" - -static inline bool s390_is_pv(void) -{ - static S390CcwMachineState *ccw; - Object *obj; - - if (ccw) { - return ccw->pv; - } - - /* we have to bail out for the "none" machine */ - obj = object_dynamic_cast(qdev_get_machine(), - TYPE_S390_CCW_MACHINE); - if (!obj) { - return false; - } - ccw = S390_CCW_MACHINE(obj); - return ccw->pv; -} - -int s390_pv_query_info(void); -int s390_pv_vm_enable(void); -void s390_pv_vm_disable(void); -bool s390_pv_vm_try_disable_async(S390CcwMachineState *ms); -int s390_pv_set_sec_parms(uint64_t origin, uint64_t length); -int s390_pv_unpack(uint64_t addr, uint64_t size, uint64_t tweak); -void s390_pv_prep_reset(void); -int s390_pv_verify(void); -void s390_pv_unshare(void); -void s390_pv_inject_reset_error(CPUState *cs); -uint64_t kvm_s390_pv_dmp_get_size_cpu(void); -uint64_t kvm_s390_pv_dmp_get_size_mem_state(void); -uint64_t kvm_s390_pv_dmp_get_size_completion_data(void); -bool kvm_s390_pv_info_basic_valid(void); -int kvm_s390_dump_init(void); -int kvm_s390_dump_cpu(S390CPU *cpu, void *buff); -int kvm_s390_dump_mem_state(uint64_t addr, size_t len, void *dest); -int kvm_s390_dump_completion_data(void *buff); -#else /* CONFIG_KVM */ -static inline bool s390_is_pv(void) { return false; } -static inline int s390_pv_query_info(void) { return 0; } -static inline int s390_pv_vm_enable(void) { return 0; } -static inline void s390_pv_vm_disable(void) {} -static inline bool s390_pv_vm_try_disable_async(S390CcwMachineState *ms) { return false; } -static inline int s390_pv_set_sec_parms(uint64_t origin, uint64_t length) { return 0; } -static inline int s390_pv_unpack(uint64_t addr, uint64_t size, uint64_t tweak) { return 0; } -static inline void s390_pv_prep_reset(void) {} -static inline int s390_pv_verify(void) { return 0; } -static inline void s390_pv_unshare(void) {} -static inline void s390_pv_inject_reset_error(CPUState *cs) {}; -static inline uint64_t kvm_s390_pv_dmp_get_size_cpu(void) { return 0; } -static inline uint64_t kvm_s390_pv_dmp_get_size_mem_state(void) { return 0; } -static inline uint64_t kvm_s390_pv_dmp_get_size_completion_data(void) { return 0; } -static inline bool kvm_s390_pv_info_basic_valid(void) { return false; } -static inline int kvm_s390_dump_init(void) { return 0; } -static inline int kvm_s390_dump_cpu(S390CPU *cpu, void *buff) { return 0; } -static inline int kvm_s390_dump_mem_state(uint64_t addr, size_t len, - void *dest) { return 0; } -static inline int kvm_s390_dump_completion_data(void *buff) { return 0; } -#endif /* CONFIG_KVM */ - -int s390_pv_kvm_init(ConfidentialGuestSupport *cgs, Error **errp); -static inline int s390_pv_init(ConfidentialGuestSupport *cgs, Error **errp) -{ - if (!cgs) { - return 0; - } - if (kvm_enabled()) { - return s390_pv_kvm_init(cgs, errp); - } - - error_setg(errp, "Protected Virtualization requires KVM"); - return -1; -} - -#endif /* HW_S390_PV_H */ diff --git a/target/s390x/arch_dump.c b/target/s390x/arch_dump.c index cb98f48..51a2116 100644 --- a/target/s390x/arch_dump.c +++ b/target/s390x/arch_dump.c @@ -17,8 +17,8 @@ #include "s390x-internal.h" #include "elf.h" #include "sysemu/dump.h" -#include "hw/s390x/pv.h" #include "kvm/kvm_s390x.h" +#include "target/s390x/kvm/pv.h" struct S390xUserRegsStruct { uint64_t psw[2]; diff --git a/target/s390x/cpu-sysemu.c b/target/s390x/cpu-sysemu.c index 97d6c76..8112561 100644 --- a/target/s390x/cpu-sysemu.c +++ b/target/s390x/cpu-sysemu.c @@ -33,7 +33,7 @@ #include "qapi/qapi-visit-run-state.h" #include "sysemu/hw_accel.h" -#include "hw/s390x/pv.h" +#include "target/s390x/kvm/pv.h" #include "hw/boards.h" #include "sysemu/sysemu.h" #include "sysemu/tcg.h" diff --git a/target/s390x/cpu_features.c b/target/s390x/cpu_features.c index 2e4e11d..ebb155c 100644 --- a/target/s390x/cpu_features.c +++ b/target/s390x/cpu_features.c @@ -15,7 +15,7 @@ #include "qemu/module.h" #include "cpu_features.h" #ifndef CONFIG_USER_ONLY -#include "hw/s390x/pv.h" +#include "target/s390x/kvm/pv.h" #endif #define DEF_FEAT(_FEAT, _NAME, _TYPE, _BIT, _DESC) \ diff --git a/target/s390x/cpu_models.c b/target/s390x/cpu_models.c index ae8880e..42b52af 100644 --- a/target/s390x/cpu_models.c +++ b/target/s390x/cpu_models.c @@ -24,7 +24,7 @@ #include "qemu/qemu-print.h" #ifndef CONFIG_USER_ONLY #include "sysemu/sysemu.h" -#include "hw/s390x/pv.h" +#include "target/s390x/kvm/pv.h" #endif #define CPUDEF_INIT(_type, _gen, _ec_ga, _mha_pow, _hmfai, _name, _desc) \ diff --git a/target/s390x/diag.c b/target/s390x/diag.c index e5f0df1..8ce18e0 100644 --- a/target/s390x/diag.c +++ b/target/s390x/diag.c @@ -19,9 +19,9 @@ #include "sysemu/cpus.h" #include "hw/s390x/ipl.h" #include "hw/s390x/s390-virtio-ccw.h" -#include "hw/s390x/pv.h" #include "sysemu/kvm.h" #include "kvm/kvm_s390x.h" +#include "target/s390x/kvm/pv.h" #include "qemu/error-report.h" diff --git a/target/s390x/helper.c b/target/s390x/helper.c index 2b363aa..d76c063 100644 --- a/target/s390x/helper.c +++ b/target/s390x/helper.c @@ -24,7 +24,7 @@ #include "gdbstub/helpers.h" #include "qemu/timer.h" #include "hw/s390x/ioinst.h" -#include "hw/s390x/pv.h" +#include "target/s390x/kvm/pv.h" #include "sysemu/hw_accel.h" #include "sysemu/runstate.h" diff --git a/target/s390x/ioinst.c b/target/s390x/ioinst.c index 053aaab..bbe45a4 100644 --- a/target/s390x/ioinst.c +++ b/target/s390x/ioinst.c @@ -16,7 +16,7 @@ #include "hw/s390x/ioinst.h" #include "trace.h" #include "hw/s390x/s390-pci-bus.h" -#include "hw/s390x/pv.h" +#include "target/s390x/kvm/pv.h" /* All I/O instructions but chsc use the s format */ static uint64_t get_address_from_regs(CPUS390XState *env, uint32_t ipb, diff --git a/target/s390x/kvm/kvm.c b/target/s390x/kvm/kvm.c index 3ac7ec9..a9e5880 100644 --- a/target/s390x/kvm/kvm.c +++ b/target/s390x/kvm/kvm.c @@ -50,7 +50,7 @@ #include "exec/memattrs.h" #include "hw/s390x/s390-virtio-ccw.h" #include "hw/s390x/s390-virtio-hcall.h" -#include "hw/s390x/pv.h" +#include "target/s390x/kvm/pv.h" #ifndef DEBUG_KVM #define DEBUG_KVM 0 diff --git a/target/s390x/kvm/meson.build b/target/s390x/kvm/meson.build index 37253f7..d6aca59 100644 --- a/target/s390x/kvm/meson.build +++ b/target/s390x/kvm/meson.build @@ -1,5 +1,6 @@ s390x_ss.add(when: 'CONFIG_KVM', if_true: files( + 'pv.c', 'kvm.c' ), if_false: files( 'stubs.c' diff --git a/target/s390x/kvm/pv.c b/target/s390x/kvm/pv.c new file mode 100644 index 0000000..6a69be7 --- /dev/null +++ b/target/s390x/kvm/pv.c @@ -0,0 +1,360 @@ +/* + * Protected Virtualization functions + * + * Copyright IBM Corp. 2020 + * Author(s): + * Janosch Frank + * + * This work is licensed under the terms of the GNU GPL, version 2 or (at + * your option) any later version. See the COPYING file in the top-level + * directory. + */ +#include "qemu/osdep.h" + +#include + +#include "qemu/units.h" +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "sysemu/kvm.h" +#include "sysemu/cpus.h" +#include "qom/object_interfaces.h" +#include "exec/confidential-guest-support.h" +#include "hw/s390x/ipl.h" +#include "hw/s390x/sclp.h" +#include "target/s390x/kvm/kvm_s390x.h" +#include "target/s390x/kvm/pv.h" + +static bool info_valid; +static struct kvm_s390_pv_info_vm info_vm; +static struct kvm_s390_pv_info_dump info_dump; + +static int __s390_pv_cmd(uint32_t cmd, const char *cmdname, void *data) +{ + struct kvm_pv_cmd pv_cmd = { + .cmd = cmd, + .data = (uint64_t)data, + }; + int rc; + + do { + rc = kvm_vm_ioctl(kvm_state, KVM_S390_PV_COMMAND, &pv_cmd); + } while (rc == -EINTR); + + if (rc) { + error_report("KVM PV command %d (%s) failed: header rc %x rrc %x " + "IOCTL rc: %d", cmd, cmdname, pv_cmd.rc, pv_cmd.rrc, + rc); + } + return rc; +} + +/* + * This macro lets us pass the command as a string to the function so + * we can print it on an error. + */ +#define s390_pv_cmd(cmd, data) __s390_pv_cmd(cmd, #cmd, data) +#define s390_pv_cmd_exit(cmd, data) \ +{ \ + int rc; \ + \ + rc = __s390_pv_cmd(cmd, #cmd, data);\ + if (rc) { \ + exit(1); \ + } \ +} + +int s390_pv_query_info(void) +{ + struct kvm_s390_pv_info info = { + .header.id = KVM_PV_INFO_VM, + .header.len_max = sizeof(info.header) + sizeof(info.vm), + }; + int rc; + + /* Info API's first user is dump so they are bundled */ + if (!kvm_s390_get_protected_dump()) { + return 0; + } + + rc = s390_pv_cmd(KVM_PV_INFO, &info); + if (rc) { + error_report("KVM PV INFO cmd %x failed: %s", + info.header.id, strerror(-rc)); + return rc; + } + memcpy(&info_vm, &info.vm, sizeof(info.vm)); + + info.header.id = KVM_PV_INFO_DUMP; + info.header.len_max = sizeof(info.header) + sizeof(info.dump); + rc = s390_pv_cmd(KVM_PV_INFO, &info); + if (rc) { + error_report("KVM PV INFO cmd %x failed: %s", + info.header.id, strerror(-rc)); + return rc; + } + + memcpy(&info_dump, &info.dump, sizeof(info.dump)); + info_valid = true; + + return rc; +} + +int s390_pv_vm_enable(void) +{ + return s390_pv_cmd(KVM_PV_ENABLE, NULL); +} + +void s390_pv_vm_disable(void) +{ + s390_pv_cmd_exit(KVM_PV_DISABLE, NULL); +} + +static void *s390_pv_do_unprot_async_fn(void *p) +{ + s390_pv_cmd_exit(KVM_PV_ASYNC_CLEANUP_PERFORM, NULL); + return NULL; +} + +bool s390_pv_vm_try_disable_async(S390CcwMachineState *ms) +{ + /* + * t is only needed to create the thread; once qemu_thread_create + * returns, it can safely be discarded. + */ + QemuThread t; + + /* + * If the feature is not present or if the VM is not larger than 2 GiB, + * KVM_PV_ASYNC_CLEANUP_PREPARE fill fail; no point in attempting it. + */ + if ((MACHINE(ms)->maxram_size <= 2 * GiB) || + !kvm_check_extension(kvm_state, KVM_CAP_S390_PROTECTED_ASYNC_DISABLE)) { + return false; + } + if (s390_pv_cmd(KVM_PV_ASYNC_CLEANUP_PREPARE, NULL) != 0) { + return false; + } + + qemu_thread_create(&t, "async_cleanup", s390_pv_do_unprot_async_fn, NULL, + QEMU_THREAD_DETACHED); + + return true; +} + +int s390_pv_set_sec_parms(uint64_t origin, uint64_t length) +{ + struct kvm_s390_pv_sec_parm args = { + .origin = origin, + .length = length, + }; + + return s390_pv_cmd(KVM_PV_SET_SEC_PARMS, &args); +} + +/* + * Called for each component in the SE type IPL parameter block 0. + */ +int s390_pv_unpack(uint64_t addr, uint64_t size, uint64_t tweak) +{ + struct kvm_s390_pv_unp args = { + .addr = addr, + .size = size, + .tweak = tweak, + }; + + return s390_pv_cmd(KVM_PV_UNPACK, &args); +} + +void s390_pv_prep_reset(void) +{ + s390_pv_cmd_exit(KVM_PV_PREP_RESET, NULL); +} + +int s390_pv_verify(void) +{ + return s390_pv_cmd(KVM_PV_VERIFY, NULL); +} + +void s390_pv_unshare(void) +{ + s390_pv_cmd_exit(KVM_PV_UNSHARE_ALL, NULL); +} + +void s390_pv_inject_reset_error(CPUState *cs) +{ + int r1 = (cs->kvm_run->s390_sieic.ipa & 0x00f0) >> 4; + CPUS390XState *env = &S390_CPU(cs)->env; + + /* Report that we are unable to enter protected mode */ + env->regs[r1 + 1] = DIAG_308_RC_INVAL_FOR_PV; +} + +uint64_t kvm_s390_pv_dmp_get_size_cpu(void) +{ + return info_dump.dump_cpu_buffer_len; +} + +uint64_t kvm_s390_pv_dmp_get_size_completion_data(void) +{ + return info_dump.dump_config_finalize_len; +} + +uint64_t kvm_s390_pv_dmp_get_size_mem_state(void) +{ + return info_dump.dump_config_mem_buffer_per_1m; +} + +bool kvm_s390_pv_info_basic_valid(void) +{ + return info_valid; +} + +static int s390_pv_dump_cmd(uint64_t subcmd, uint64_t uaddr, uint64_t gaddr, + uint64_t len) +{ + struct kvm_s390_pv_dmp dmp = { + .subcmd = subcmd, + .buff_addr = uaddr, + .buff_len = len, + .gaddr = gaddr, + }; + int ret; + + ret = s390_pv_cmd(KVM_PV_DUMP, (void *)&dmp); + if (ret) { + error_report("KVM DUMP command %ld failed", subcmd); + } + return ret; +} + +int kvm_s390_dump_cpu(S390CPU *cpu, void *buff) +{ + struct kvm_s390_pv_dmp dmp = { + .subcmd = KVM_PV_DUMP_CPU, + .buff_addr = (uint64_t)buff, + .gaddr = 0, + .buff_len = info_dump.dump_cpu_buffer_len, + }; + struct kvm_pv_cmd pv = { + .cmd = KVM_PV_DUMP, + .data = (uint64_t)&dmp, + }; + + return kvm_vcpu_ioctl(CPU(cpu), KVM_S390_PV_CPU_COMMAND, &pv); +} + +int kvm_s390_dump_init(void) +{ + return s390_pv_dump_cmd(KVM_PV_DUMP_INIT, 0, 0, 0); +} + +int kvm_s390_dump_mem_state(uint64_t gaddr, size_t len, void *dest) +{ + return s390_pv_dump_cmd(KVM_PV_DUMP_CONFIG_STOR_STATE, (uint64_t)dest, + gaddr, len); +} + +int kvm_s390_dump_completion_data(void *buff) +{ + return s390_pv_dump_cmd(KVM_PV_DUMP_COMPLETE, (uint64_t)buff, 0, + info_dump.dump_config_finalize_len); +} + +#define TYPE_S390_PV_GUEST "s390-pv-guest" +OBJECT_DECLARE_SIMPLE_TYPE(S390PVGuest, S390_PV_GUEST) + +/** + * S390PVGuest: + * + * The S390PVGuest object is basically a dummy used to tell the + * confidential guest support system to use s390's PV mechanism. + * + * # $QEMU \ + * -object s390-pv-guest,id=pv0 \ + * -machine ...,confidential-guest-support=pv0 + */ +struct S390PVGuest { + ConfidentialGuestSupport parent_obj; +}; + +typedef struct S390PVGuestClass S390PVGuestClass; + +struct S390PVGuestClass { + ConfidentialGuestSupportClass parent_class; +}; + +/* + * If protected virtualization is enabled, the amount of data that the + * Read SCP Info Service Call can use is limited to one page. The + * available space also depends on the Extended-Length SCCB (ELS) + * feature which can take more buffer space to store feature + * information. This impacts the maximum number of CPUs supported in + * the machine. + */ +static uint32_t s390_pv_get_max_cpus(void) +{ + int offset_cpu = s390_has_feat(S390_FEAT_EXTENDED_LENGTH_SCCB) ? + offsetof(ReadInfo, entries) : SCLP_READ_SCP_INFO_FIXED_CPU_OFFSET; + + return (TARGET_PAGE_SIZE - offset_cpu) / sizeof(CPUEntry); +} + +static bool s390_pv_check_cpus(Error **errp) +{ + MachineState *ms = MACHINE(qdev_get_machine()); + uint32_t pv_max_cpus = s390_pv_get_max_cpus(); + + if (ms->smp.max_cpus > pv_max_cpus) { + error_setg(errp, "Protected VMs support a maximum of %d CPUs", + pv_max_cpus); + return false; + } + + return true; +} + +static bool s390_pv_guest_check(ConfidentialGuestSupport *cgs, Error **errp) +{ + return s390_pv_check_cpus(errp); +} + +int s390_pv_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) +{ + if (!object_dynamic_cast(OBJECT(cgs), TYPE_S390_PV_GUEST)) { + return 0; + } + + if (!s390_has_feat(S390_FEAT_UNPACK)) { + error_setg(errp, + "CPU model does not support Protected Virtualization"); + return -1; + } + + if (!s390_pv_guest_check(cgs, errp)) { + return -1; + } + + cgs->ready = true; + + return 0; +} + +OBJECT_DEFINE_TYPE_WITH_INTERFACES(S390PVGuest, + s390_pv_guest, + S390_PV_GUEST, + CONFIDENTIAL_GUEST_SUPPORT, + { TYPE_USER_CREATABLE }, + { NULL }) + +static void s390_pv_guest_class_init(ObjectClass *oc, void *data) +{ +} + +static void s390_pv_guest_init(Object *obj) +{ +} + +static void s390_pv_guest_finalize(Object *obj) +{ +} diff --git a/target/s390x/kvm/pv.h b/target/s390x/kvm/pv.h new file mode 100644 index 0000000..7b935e2 --- /dev/null +++ b/target/s390x/kvm/pv.h @@ -0,0 +1,96 @@ +/* + * Protected Virtualization header + * + * Copyright IBM Corp. 2020 + * Author(s): + * Janosch Frank + * + * This work is licensed under the terms of the GNU GPL, version 2 or (at + * your option) any later version. See the COPYING file in the top-level + * directory. + */ +#ifndef HW_S390_PV_H +#define HW_S390_PV_H + +#include "qapi/error.h" +#include "sysemu/kvm.h" +#include "hw/s390x/s390-virtio-ccw.h" + +#ifdef CONFIG_KVM +#include "cpu.h" + +static inline bool s390_is_pv(void) +{ + static S390CcwMachineState *ccw; + Object *obj; + + if (ccw) { + return ccw->pv; + } + + /* we have to bail out for the "none" machine */ + obj = object_dynamic_cast(qdev_get_machine(), + TYPE_S390_CCW_MACHINE); + if (!obj) { + return false; + } + ccw = S390_CCW_MACHINE(obj); + return ccw->pv; +} + +int s390_pv_query_info(void); +int s390_pv_vm_enable(void); +void s390_pv_vm_disable(void); +bool s390_pv_vm_try_disable_async(S390CcwMachineState *ms); +int s390_pv_set_sec_parms(uint64_t origin, uint64_t length); +int s390_pv_unpack(uint64_t addr, uint64_t size, uint64_t tweak); +void s390_pv_prep_reset(void); +int s390_pv_verify(void); +void s390_pv_unshare(void); +void s390_pv_inject_reset_error(CPUState *cs); +uint64_t kvm_s390_pv_dmp_get_size_cpu(void); +uint64_t kvm_s390_pv_dmp_get_size_mem_state(void); +uint64_t kvm_s390_pv_dmp_get_size_completion_data(void); +bool kvm_s390_pv_info_basic_valid(void); +int kvm_s390_dump_init(void); +int kvm_s390_dump_cpu(S390CPU *cpu, void *buff); +int kvm_s390_dump_mem_state(uint64_t addr, size_t len, void *dest); +int kvm_s390_dump_completion_data(void *buff); +#else /* CONFIG_KVM */ +static inline bool s390_is_pv(void) { return false; } +static inline int s390_pv_query_info(void) { return 0; } +static inline int s390_pv_vm_enable(void) { return 0; } +static inline void s390_pv_vm_disable(void) {} +static inline bool s390_pv_vm_try_disable_async(S390CcwMachineState *ms) { return false; } +static inline int s390_pv_set_sec_parms(uint64_t origin, uint64_t length) { return 0; } +static inline int s390_pv_unpack(uint64_t addr, uint64_t size, uint64_t tweak) { return 0; } +static inline void s390_pv_prep_reset(void) {} +static inline int s390_pv_verify(void) { return 0; } +static inline void s390_pv_unshare(void) {} +static inline void s390_pv_inject_reset_error(CPUState *cs) {}; +static inline uint64_t kvm_s390_pv_dmp_get_size_cpu(void) { return 0; } +static inline uint64_t kvm_s390_pv_dmp_get_size_mem_state(void) { return 0; } +static inline uint64_t kvm_s390_pv_dmp_get_size_completion_data(void) { return 0; } +static inline bool kvm_s390_pv_info_basic_valid(void) { return false; } +static inline int kvm_s390_dump_init(void) { return 0; } +static inline int kvm_s390_dump_cpu(S390CPU *cpu, void *buff) { return 0; } +static inline int kvm_s390_dump_mem_state(uint64_t addr, size_t len, + void *dest) { return 0; } +static inline int kvm_s390_dump_completion_data(void *buff) { return 0; } +#endif /* CONFIG_KVM */ + +int s390_pv_kvm_init(ConfidentialGuestSupport *cgs, Error **errp); +static inline int s390_pv_init(ConfidentialGuestSupport *cgs, Error **errp) +{ + if (!cgs) { + return 0; + } + if (kvm_enabled()) { + return s390_pv_kvm_init(cgs, errp); + } + + error_setg(errp, "Protected Virtualization requires KVM"); + return -1; +} + +#endif /* HW_S390_PV_H */ -- cgit v1.1