1 // SPDX-License-Identifier: GPL-2.0-only 2 /* Test operation exception forwarding. 3 * 4 * Copyright IBM Corp. 2025 5 * 6 * Authors: 7 * Janosch Frank <frankja@linux.ibm.com> 8 */ 9 #include "kselftest.h" 10 #include "kvm_util.h" 11 #include "test_util.h" 12 #include "sie.h" 13 14 #include <linux/kvm.h> 15 16 static void guest_code_instr0(void) 17 { 18 asm(".word 0x0000"); 19 } 20 21 static void test_user_instr0(void) 22 { 23 struct kvm_vcpu *vcpu; 24 struct kvm_vm *vm; 25 int rc; 26 27 vm = vm_create_with_one_vcpu(&vcpu, guest_code_instr0); 28 rc = __vm_enable_cap(vm, KVM_CAP_S390_USER_INSTR0, 0); 29 TEST_ASSERT_EQ(0, rc); 30 31 vcpu_run(vcpu); 32 TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_S390_SIEIC); 33 TEST_ASSERT_EQ(vcpu->run->s390_sieic.icptcode, ICPT_OPEREXC); 34 TEST_ASSERT_EQ(vcpu->run->s390_sieic.ipa, 0); 35 36 kvm_vm_free(vm); 37 } 38 39 static void guest_code_user_operexec(void) 40 { 41 asm(".word 0x0807"); 42 } 43 44 static void test_user_operexec(void) 45 { 46 struct kvm_vcpu *vcpu; 47 struct kvm_vm *vm; 48 int rc; 49 50 vm = vm_create_with_one_vcpu(&vcpu, guest_code_user_operexec); 51 rc = __vm_enable_cap(vm, KVM_CAP_S390_USER_OPEREXEC, 0); 52 TEST_ASSERT_EQ(0, rc); 53 54 vcpu_run(vcpu); 55 TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_S390_SIEIC); 56 TEST_ASSERT_EQ(vcpu->run->s390_sieic.icptcode, ICPT_OPEREXC); 57 TEST_ASSERT_EQ(vcpu->run->s390_sieic.ipa, 0x0807); 58 59 kvm_vm_free(vm); 60 61 /* 62 * Since user_operexec is the superset it can be used for the 63 * 0 instruction. 64 */ 65 vm = vm_create_with_one_vcpu(&vcpu, guest_code_instr0); 66 rc = __vm_enable_cap(vm, KVM_CAP_S390_USER_OPEREXEC, 0); 67 TEST_ASSERT_EQ(0, rc); 68 69 vcpu_run(vcpu); 70 TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_S390_SIEIC); 71 TEST_ASSERT_EQ(vcpu->run->s390_sieic.icptcode, ICPT_OPEREXC); 72 TEST_ASSERT_EQ(vcpu->run->s390_sieic.ipa, 0); 73 74 kvm_vm_free(vm); 75 } 76 77 /* combine user_instr0 and user_operexec */ 78 static void test_user_operexec_combined(void) 79 { 80 struct kvm_vcpu *vcpu; 81 struct kvm_vm *vm; 82 int rc; 83 84 vm = vm_create_with_one_vcpu(&vcpu, guest_code_user_operexec); 85 rc = __vm_enable_cap(vm, KVM_CAP_S390_USER_INSTR0, 0); 86 TEST_ASSERT_EQ(0, rc); 87 rc = __vm_enable_cap(vm, KVM_CAP_S390_USER_OPEREXEC, 0); 88 TEST_ASSERT_EQ(0, rc); 89 90 vcpu_run(vcpu); 91 TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_S390_SIEIC); 92 TEST_ASSERT_EQ(vcpu->run->s390_sieic.icptcode, ICPT_OPEREXC); 93 TEST_ASSERT_EQ(vcpu->run->s390_sieic.ipa, 0x0807); 94 95 kvm_vm_free(vm); 96 97 /* Reverse enablement order */ 98 vm = vm_create_with_one_vcpu(&vcpu, guest_code_user_operexec); 99 rc = __vm_enable_cap(vm, KVM_CAP_S390_USER_OPEREXEC, 0); 100 TEST_ASSERT_EQ(0, rc); 101 rc = __vm_enable_cap(vm, KVM_CAP_S390_USER_INSTR0, 0); 102 TEST_ASSERT_EQ(0, rc); 103 104 vcpu_run(vcpu); 105 TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_S390_SIEIC); 106 TEST_ASSERT_EQ(vcpu->run->s390_sieic.icptcode, ICPT_OPEREXC); 107 TEST_ASSERT_EQ(vcpu->run->s390_sieic.ipa, 0x0807); 108 109 kvm_vm_free(vm); 110 } 111 112 /* 113 * Run all tests above. 114 * 115 * Enablement after VCPU has been added is automatically tested since 116 * we enable the capability after VCPU creation. 117 */ 118 static struct testdef { 119 const char *name; 120 void (*test)(void); 121 } testlist[] = { 122 { "instr0", test_user_instr0 }, 123 { "operexec", test_user_operexec }, 124 { "operexec_combined", test_user_operexec_combined}, 125 }; 126 127 int main(int argc, char *argv[]) 128 { 129 int idx; 130 131 TEST_REQUIRE(kvm_has_cap(KVM_CAP_S390_USER_INSTR0)); 132 133 ksft_print_header(); 134 ksft_set_plan(ARRAY_SIZE(testlist)); 135 for (idx = 0; idx < ARRAY_SIZE(testlist); idx++) { 136 testlist[idx].test(); 137 ksft_test_result_pass("%s\n", testlist[idx].name); 138 } 139 ksft_finished(); 140 } 141