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 "facility.h"
10 #include "kselftest.h"
11 #include "kvm_util.h"
12 #include "test_util.h"
13 #include "sie.h"
14
15 #include <linux/kvm.h>
16
guest_code_instr0(void)17 static void guest_code_instr0(void)
18 {
19 asm(".word 0x0000");
20 }
21
test_user_instr0(void)22 static void test_user_instr0(void)
23 {
24 struct kvm_vcpu *vcpu;
25 struct kvm_vm *vm;
26 int rc;
27
28 vm = vm_create_with_one_vcpu(&vcpu, guest_code_instr0);
29 rc = __vm_enable_cap(vm, KVM_CAP_S390_USER_INSTR0, 0);
30 TEST_ASSERT_EQ(0, rc);
31
32 vcpu_run(vcpu);
33 TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_S390_SIEIC);
34 TEST_ASSERT_EQ(vcpu->run->s390_sieic.icptcode, ICPT_OPEREXC);
35 TEST_ASSERT_EQ(vcpu->run->s390_sieic.ipa, 0);
36
37 kvm_vm_free(vm);
38 }
39
guest_code_user_operexec(void)40 static void guest_code_user_operexec(void)
41 {
42 asm(".word 0x0807");
43 }
44
test_user_operexec(void)45 static void test_user_operexec(void)
46 {
47 struct kvm_vcpu *vcpu;
48 struct kvm_vm *vm;
49 int rc;
50
51 vm = vm_create_with_one_vcpu(&vcpu, guest_code_user_operexec);
52 rc = __vm_enable_cap(vm, KVM_CAP_S390_USER_OPEREXEC, 0);
53 TEST_ASSERT_EQ(0, rc);
54
55 vcpu_run(vcpu);
56 TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_S390_SIEIC);
57 TEST_ASSERT_EQ(vcpu->run->s390_sieic.icptcode, ICPT_OPEREXC);
58 TEST_ASSERT_EQ(vcpu->run->s390_sieic.ipa, 0x0807);
59
60 kvm_vm_free(vm);
61
62 /*
63 * Since user_operexec is the superset it can be used for the
64 * 0 instruction.
65 */
66 vm = vm_create_with_one_vcpu(&vcpu, guest_code_instr0);
67 rc = __vm_enable_cap(vm, KVM_CAP_S390_USER_OPEREXEC, 0);
68 TEST_ASSERT_EQ(0, rc);
69
70 vcpu_run(vcpu);
71 TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_S390_SIEIC);
72 TEST_ASSERT_EQ(vcpu->run->s390_sieic.icptcode, ICPT_OPEREXC);
73 TEST_ASSERT_EQ(vcpu->run->s390_sieic.ipa, 0);
74
75 kvm_vm_free(vm);
76 }
77
78 /* combine user_instr0 and user_operexec */
test_user_operexec_combined(void)79 static void test_user_operexec_combined(void)
80 {
81 struct kvm_vcpu *vcpu;
82 struct kvm_vm *vm;
83 int rc;
84
85 vm = vm_create_with_one_vcpu(&vcpu, guest_code_user_operexec);
86 rc = __vm_enable_cap(vm, KVM_CAP_S390_USER_INSTR0, 0);
87 TEST_ASSERT_EQ(0, rc);
88 rc = __vm_enable_cap(vm, KVM_CAP_S390_USER_OPEREXEC, 0);
89 TEST_ASSERT_EQ(0, rc);
90
91 vcpu_run(vcpu);
92 TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_S390_SIEIC);
93 TEST_ASSERT_EQ(vcpu->run->s390_sieic.icptcode, ICPT_OPEREXC);
94 TEST_ASSERT_EQ(vcpu->run->s390_sieic.ipa, 0x0807);
95
96 kvm_vm_free(vm);
97
98 /* Reverse enablement order */
99 vm = vm_create_with_one_vcpu(&vcpu, guest_code_user_operexec);
100 rc = __vm_enable_cap(vm, KVM_CAP_S390_USER_OPEREXEC, 0);
101 TEST_ASSERT_EQ(0, rc);
102 rc = __vm_enable_cap(vm, KVM_CAP_S390_USER_INSTR0, 0);
103 TEST_ASSERT_EQ(0, rc);
104
105 vcpu_run(vcpu);
106 TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_S390_SIEIC);
107 TEST_ASSERT_EQ(vcpu->run->s390_sieic.icptcode, ICPT_OPEREXC);
108 TEST_ASSERT_EQ(vcpu->run->s390_sieic.ipa, 0x0807);
109
110 kvm_vm_free(vm);
111 }
112
create_vm_without_sthyi(void)113 static struct kvm_vm *create_vm_without_sthyi(void)
114 {
115 struct kvm_s390_vm_cpu_processor info;
116 struct kvm_vm *vm;
117
118 vm = vm_create(1);
119
120 kvm_device_attr_get(vm->fd, KVM_S390_VM_CPU_MODEL,
121 KVM_S390_VM_CPU_PROCESSOR, &info);
122
123 clear_bit_inv(74, (unsigned long *)&info.fac_list);
124 kvm_device_attr_set(vm->fd, KVM_S390_VM_CPU_MODEL,
125 KVM_S390_VM_CPU_PROCESSOR, &info);
126
127 return vm;
128 }
129
test_user_instr0_no_stfle_74(void)130 static void test_user_instr0_no_stfle_74(void)
131 {
132 struct kvm_vcpu *vcpu;
133 struct kvm_vm *vm;
134 int rc;
135
136 vm = create_vm_without_sthyi();
137
138 rc = __vm_enable_cap(vm, KVM_CAP_S390_USER_INSTR0, 0);
139 TEST_ASSERT_EQ(0, rc);
140
141 vcpu = vm_vcpu_add(vm, 0, guest_code_instr0);
142
143 vcpu_run(vcpu);
144 TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_S390_SIEIC);
145 TEST_ASSERT_EQ(vcpu->run->s390_sieic.icptcode, ICPT_OPEREXC);
146 TEST_ASSERT_EQ(vcpu->run->s390_sieic.ipa, 0x0000);
147
148 kvm_vm_free(vm);
149 }
150
test_user_operexec_no_stfle_74(void)151 static void test_user_operexec_no_stfle_74(void)
152 {
153 struct kvm_vcpu *vcpu;
154 struct kvm_vm *vm;
155 int rc;
156
157 vm = create_vm_without_sthyi();
158
159 rc = __vm_enable_cap(vm, KVM_CAP_S390_USER_OPEREXEC, 0);
160 TEST_ASSERT_EQ(0, rc);
161
162 vcpu = vm_vcpu_add(vm, 0, guest_code_user_operexec);
163
164 vcpu_run(vcpu);
165 TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_S390_SIEIC);
166 TEST_ASSERT_EQ(vcpu->run->s390_sieic.icptcode, ICPT_OPEREXC);
167 TEST_ASSERT_EQ(vcpu->run->s390_sieic.ipa, 0x0807);
168
169 kvm_vm_free(vm);
170 }
171
test_instr0_combined_no_stfle_74(void)172 static void test_instr0_combined_no_stfle_74(void)
173 {
174 struct kvm_vcpu *vcpu;
175 struct kvm_vm *vm;
176 int rc;
177
178 vm = create_vm_without_sthyi();
179
180 rc = __vm_enable_cap(vm, KVM_CAP_S390_USER_INSTR0, 0);
181 TEST_ASSERT_EQ(0, rc);
182 rc = __vm_enable_cap(vm, KVM_CAP_S390_USER_OPEREXEC, 0);
183 TEST_ASSERT_EQ(0, rc);
184
185 vcpu = vm_vcpu_add(vm, 0, guest_code_instr0);
186
187 vcpu_run(vcpu);
188 TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_S390_SIEIC);
189 TEST_ASSERT_EQ(vcpu->run->s390_sieic.icptcode, ICPT_OPEREXC);
190 TEST_ASSERT_EQ(vcpu->run->s390_sieic.ipa, 0x0000);
191
192 kvm_vm_free(vm);
193 }
194
test_operexec_combined_no_stfle_74(void)195 static void test_operexec_combined_no_stfle_74(void)
196 {
197 struct kvm_vcpu *vcpu;
198 struct kvm_vm *vm;
199 int rc;
200
201 vm = create_vm_without_sthyi();
202
203 rc = __vm_enable_cap(vm, KVM_CAP_S390_USER_INSTR0, 0);
204 TEST_ASSERT_EQ(0, rc);
205 rc = __vm_enable_cap(vm, KVM_CAP_S390_USER_OPEREXEC, 0);
206 TEST_ASSERT_EQ(0, rc);
207
208 vcpu = vm_vcpu_add(vm, 0, guest_code_user_operexec);
209
210 vcpu_run(vcpu);
211 TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_S390_SIEIC);
212 TEST_ASSERT_EQ(vcpu->run->s390_sieic.icptcode, ICPT_OPEREXC);
213 TEST_ASSERT_EQ(vcpu->run->s390_sieic.ipa, 0x0807);
214
215 kvm_vm_free(vm);
216 }
217
218 /*
219 * Run all tests above.
220 *
221 * Enablement after VCPU has been added is automatically tested since
222 * we enable the capability after VCPU creation.
223 */
224 static struct testdef {
225 const char *name;
226 void (*test)(void);
227 } testlist[] = {
228 { "instr0", test_user_instr0 },
229 { "operexec", test_user_operexec },
230 { "operexec_combined", test_user_operexec_combined},
231 { "instr0_no_stfle_74", test_user_instr0_no_stfle_74 },
232 { "instr0_combined_no_stfle_74", test_instr0_combined_no_stfle_74 },
233 { "operexec_combined_no_stfle_74", test_operexec_combined_no_stfle_74 },
234 { "operexec_no_stfle_74", test_user_operexec_no_stfle_74 },
235 };
236
main(int argc,char * argv[])237 int main(int argc, char *argv[])
238 {
239 int idx;
240
241 TEST_REQUIRE(kvm_has_cap(KVM_CAP_S390_USER_INSTR0));
242
243 ksft_print_header();
244 ksft_set_plan(ARRAY_SIZE(testlist));
245 for (idx = 0; idx < ARRAY_SIZE(testlist); idx++) {
246 testlist[idx].test();
247 ksft_test_result_pass("%s\n", testlist[idx].name);
248 }
249 ksft_finished();
250 }
251