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