The bear is set for lpsw and lpswe but not for lpswey. Load PSW and all of its variants are only emulated by KVM if there's a pending machine check. Therefore the tests inject those but never open the masks to receive them. Signed-off-by: Janosch Frank --- tools/testing/selftests/kvm/Makefile.kvm | 1 + tools/testing/selftests/kvm/s390/bear.c | 184 +++++++++++++++++++++++ 2 files changed, 185 insertions(+) create mode 100644 tools/testing/selftests/kvm/s390/bear.c diff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm index 6471fa214a9f..9afb6479dbee 100644 --- a/tools/testing/selftests/kvm/Makefile.kvm +++ b/tools/testing/selftests/kvm/Makefile.kvm @@ -207,6 +207,7 @@ TEST_GEN_PROGS_s390 += s390/user_operexec TEST_GEN_PROGS_s390 += s390/keyop TEST_GEN_PROGS_s390 += rseq_test TEST_GEN_PROGS_s390 += s390/irq_routing +TEST_GEN_PROGS_s390 += s390/bear TEST_GEN_PROGS_riscv = $(TEST_GEN_PROGS_COMMON) TEST_GEN_PROGS_riscv += riscv/sbi_pmu_test diff --git a/tools/testing/selftests/kvm/s390/bear.c b/tools/testing/selftests/kvm/s390/bear.c new file mode 100644 index 000000000000..fee4999532ae --- /dev/null +++ b/tools/testing/selftests/kvm/s390/bear.c @@ -0,0 +1,184 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * LPSW(E|Y) bear tests. + * LPSW and LPSWE do set the bear but LPSWEY doesn't. + * + */ +#include +#include +#include +#include + +#include "test_util.h" +#include "kvm_util.h" +#include "kselftest.h" +#include "ucall_common.h" +#include "facility.h" + +static void guest_lpswey(void) +{ + extern void lpswey_dest_addr(void); + u64 psw[2] = {0x0400000180000000ULL, (uintptr_t)lpswey_dest_addr}; + u64 bear; + + asm volatile ( + " larl %%r3,lpswey_addr\n" + "lpswey_addr:\n" + " .insn siy,0xeb0000000071,%[psw],0\n" + " nop\n" + " nop\n" + ".globl lpswey_dest_addr \n" + "lpswey_dest_addr:\n" + " .insn s,0xb2010000,%[bear]\n" + " lg %%r4, %[bear]\n" + " nop\n" + " nop\n" + : [bear] "=Q" (bear) + : [psw] "T" (psw) + : "cc", "r3", "r4" + ); +} + +static void guest_lpswe(void) +{ + extern void lpswe_dest_addr(void); + u64 psw[2] = {0x0400000180000000ULL, (uintptr_t)lpswe_dest_addr}; + u64 bear; + + asm volatile ( + " larl %%r3,lpswe_addr\n" + "lpswe_addr:\n" + " lpswe %[psw]\n" + " nop\n" + " nop\n" + ".globl lpswe_dest_addr\n" + "lpswe_dest_addr:\n" + " .insn s,0xb2010000,%[bear]\n" + " lg %%r4, %[bear]\n" + " nop\n" + " nop\n" + : [bear] "=Q" (bear) + : [psw] "Q" (psw) + : "cc", "r3", "r4" + ); +} + +static void guest_lpsw(void) +{ + extern void lpsw_dest_addr(void); + u64 psw_short = (0x0400000180000000ULL | BIT(63 - 12) | + (uintptr_t)lpsw_dest_addr); + u64 bear; + + asm volatile ( + " larl %%r3,lpsw_addr\n" + "lpsw_addr:\n" + " lpsw %[psw]\n" + " nop\n" + " nop\n" + ".globl lpsw_dest_addr\n" + "lpsw_dest_addr:\n" + " .insn s,0xb2010000,%[bear]\n" + " lg %%r4, %[bear]\n" + " nop\n" + " nop\n" + : [bear] "=Q" (bear) + : [psw] "Q" (psw_short) + : "cc", "r3", "r4" + ); +} + +/* A machine check forces KVM to emulate PSW loading */ +static void inject_mcheck(struct kvm_vcpu *vcpu) +{ + struct kvm_s390_irq irq = {}; + int irqs; + + irq.type = KVM_S390_MCHK; + /* External damage mcheck */ + irq.u.mchk.cr14 = BIT(63 - 38); + irq.u.mchk.mcic = BIT(58); + irqs = __vcpu_ioctl(vcpu, KVM_S390_IRQ, &irq); + TEST_ASSERT(irqs >= 0, "Error injecting MCHECK errno %d", errno); +} + +static void test_lpswey(void) +{ + struct kvm_vcpu *vcpu; + struct kvm_run *run; + struct kvm_vm *vm; + + vm = vm_create_with_one_vcpu(&vcpu, guest_lpswey); + inject_mcheck(vcpu); + run = vcpu->run; + vcpu_run(vcpu); + ksft_test_result(run->s.regs.gprs[3] != run->s.regs.gprs[4], + "emulation: lpswey bear does not match\n"); + kvm_vm_free(vm); + + vm = vm_create_with_one_vcpu(&vcpu, guest_lpswey); + run = vcpu->run; + vcpu_run(vcpu); + ksft_test_result(run->s.regs.gprs[3] && + run->s.regs.gprs[3] != run->s.regs.gprs[4], + "interpretation: lpswey bear does not match\n"); + kvm_vm_free(vm); +} + +static void test_lpswe(void) +{ + struct kvm_vcpu *vcpu; + struct kvm_run *run; + struct kvm_vm *vm; + + vm = vm_create_with_one_vcpu(&vcpu, guest_lpswe); + inject_mcheck(vcpu); + run = vcpu->run; + vcpu_run(vcpu); + ksft_test_result(run->s.regs.gprs[3] == run->s.regs.gprs[4], + "emulation: lpswe bear matches\n"); + kvm_vm_free(vm); + + vm = vm_create_with_one_vcpu(&vcpu, guest_lpsw); + run = vcpu->run; + vcpu_run(vcpu); + ksft_test_result(run->s.regs.gprs[3] && + run->s.regs.gprs[3] == run->s.regs.gprs[4], + "interpretation: lpswe bear matches\n"); + kvm_vm_free(vm); +} + +static void test_lpsw(void) +{ + struct kvm_vcpu *vcpu; + struct kvm_run *run; + struct kvm_vm *vm; + + vm = vm_create_with_one_vcpu(&vcpu, guest_lpsw); + inject_mcheck(vcpu); + run = vcpu->run; + vcpu_run(vcpu); + ksft_test_result(run->s.regs.gprs[3] == run->s.regs.gprs[4], + "emulation: lpsw bear matches\n"); + kvm_vm_free(vm); + + vm = vm_create_with_one_vcpu(&vcpu, guest_lpsw); + run = vcpu->run; + vcpu_run(vcpu); + ksft_test_result(run->s.regs.gprs[3] && + run->s.regs.gprs[3] == run->s.regs.gprs[4], + "interpretation: lpsw bear matches\n"); + kvm_vm_free(vm); +} + +int main(int argc, char *argv[]) +{ + TEST_REQUIRE(test_facility(193)); + + ksft_print_header(); + ksft_set_plan(6); + test_lpsw(); + test_lpswe(); + test_lpswey(); + ksft_finished(); +} -- 2.51.0