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
guest_code_instr0(void)16 static void guest_code_instr0(void)
17 {
18 asm(".word 0x0000");
19 }
20
test_user_instr0(void)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
guest_code_user_operexec(void)39 static void guest_code_user_operexec(void)
40 {
41 asm(".word 0x0807");
42 }
43
test_user_operexec(void)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 */
test_user_operexec_combined(void)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
main(int argc,char * argv[])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