// SPDX-License-Identifier: GPL-2.0-only /* Test operation exception forwarding. * * Copyright IBM Corp. 2025 * * Authors: * Janosch Frank */ #include "kselftest.h" #include "kvm_util.h" #include "test_util.h" #include "sie.h" #include static void guest_code_instr0(void) { asm(".word 0x0000"); } static void test_user_instr0(void) { struct kvm_vcpu *vcpu; struct kvm_vm *vm; int rc; vm = vm_create_with_one_vcpu(&vcpu, guest_code_instr0); rc = __vm_enable_cap(vm, KVM_CAP_S390_USER_INSTR0, 0); TEST_ASSERT_EQ(0, rc); vcpu_run(vcpu); TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_S390_SIEIC); TEST_ASSERT_EQ(vcpu->run->s390_sieic.icptcode, ICPT_OPEREXC); TEST_ASSERT_EQ(vcpu->run->s390_sieic.ipa, 0); kvm_vm_free(vm); } static void guest_code_user_operexec(void) { asm(".word 0x0807"); } static void test_user_operexec(void) { struct kvm_vcpu *vcpu; struct kvm_vm *vm; int rc; vm = vm_create_with_one_vcpu(&vcpu, guest_code_user_operexec); rc = __vm_enable_cap(vm, KVM_CAP_S390_USER_OPEREXEC, 0); TEST_ASSERT_EQ(0, rc); vcpu_run(vcpu); TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_S390_SIEIC); TEST_ASSERT_EQ(vcpu->run->s390_sieic.icptcode, ICPT_OPEREXC); TEST_ASSERT_EQ(vcpu->run->s390_sieic.ipa, 0x0807); kvm_vm_free(vm); /* * Since user_operexec is the superset it can be used for the * 0 instruction. */ vm = vm_create_with_one_vcpu(&vcpu, guest_code_instr0); rc = __vm_enable_cap(vm, KVM_CAP_S390_USER_OPEREXEC, 0); TEST_ASSERT_EQ(0, rc); vcpu_run(vcpu); TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_S390_SIEIC); TEST_ASSERT_EQ(vcpu->run->s390_sieic.icptcode, ICPT_OPEREXC); TEST_ASSERT_EQ(vcpu->run->s390_sieic.ipa, 0); kvm_vm_free(vm); } /* combine user_instr0 and user_operexec */ static void test_user_operexec_combined(void) { struct kvm_vcpu *vcpu; struct kvm_vm *vm; int rc; vm = vm_create_with_one_vcpu(&vcpu, guest_code_user_operexec); rc = __vm_enable_cap(vm, KVM_CAP_S390_USER_INSTR0, 0); TEST_ASSERT_EQ(0, rc); rc = __vm_enable_cap(vm, KVM_CAP_S390_USER_OPEREXEC, 0); TEST_ASSERT_EQ(0, rc); vcpu_run(vcpu); TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_S390_SIEIC); TEST_ASSERT_EQ(vcpu->run->s390_sieic.icptcode, ICPT_OPEREXC); TEST_ASSERT_EQ(vcpu->run->s390_sieic.ipa, 0x0807); kvm_vm_free(vm); /* Reverse enablement order */ vm = vm_create_with_one_vcpu(&vcpu, guest_code_user_operexec); rc = __vm_enable_cap(vm, KVM_CAP_S390_USER_OPEREXEC, 0); TEST_ASSERT_EQ(0, rc); rc = __vm_enable_cap(vm, KVM_CAP_S390_USER_INSTR0, 0); TEST_ASSERT_EQ(0, rc); vcpu_run(vcpu); TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_S390_SIEIC); TEST_ASSERT_EQ(vcpu->run->s390_sieic.icptcode, ICPT_OPEREXC); TEST_ASSERT_EQ(vcpu->run->s390_sieic.ipa, 0x0807); kvm_vm_free(vm); } /* * Run all tests above. * * Enablement after VCPU has been added is automatically tested since * we enable the capability after VCPU creation. */ static struct testdef { const char *name; void (*test)(void); } testlist[] = { { "instr0", test_user_instr0 }, { "operexec", test_user_operexec }, { "operexec_combined", test_user_operexec_combined}, }; int main(int argc, char *argv[]) { int idx; TEST_REQUIRE(kvm_has_cap(KVM_CAP_S390_USER_INSTR0)); ksft_print_header(); ksft_set_plan(ARRAY_SIZE(testlist)); for (idx = 0; idx < ARRAY_SIZE(testlist); idx++) { testlist[idx].test(); ksft_test_result_pass("%s\n", testlist[idx].name); } ksft_finished(); }