xref: /linux/tools/testing/selftests/kvm/s390/user_operexec.c (revision 2ed2e359dea752e7758d29a423033031a0b96584)
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