xref: /linux/tools/testing/selftests/kvm/aarch64/smccc_filter.c (revision 58f6259b7a08f8d47d4629609703d358b042f0fd)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * smccc_filter - Tests for the SMCCC filter UAPI.
4  *
5  * Copyright (c) 2023 Google LLC
6  *
7  * This test includes:
8  *  - Tests that the UAPI constraints are upheld by KVM. For example, userspace
9  *    is prevented from filtering the architecture range of SMCCC calls.
10  *  - Test that the filter actions (DENIED, FWD_TO_USER) work as intended.
11  */
12 
13 #include <linux/arm-smccc.h>
14 #include <linux/psci.h>
15 #include <stdint.h>
16 
17 #include "processor.h"
18 #include "test_util.h"
19 
20 enum smccc_conduit {
21 	HVC_INSN,
22 	SMC_INSN,
23 };
24 
25 #define for_each_conduit(conduit)					\
26 	for (conduit = HVC_INSN; conduit <= SMC_INSN; conduit++)
27 
28 static void guest_main(uint32_t func_id, enum smccc_conduit conduit)
29 {
30 	struct arm_smccc_res res;
31 
32 	if (conduit == SMC_INSN)
33 		smccc_smc(func_id, 0, 0, 0, 0, 0, 0, 0, &res);
34 	else
35 		smccc_hvc(func_id, 0, 0, 0, 0, 0, 0, 0, &res);
36 
37 	GUEST_SYNC(res.a0);
38 }
39 
40 static int __set_smccc_filter(struct kvm_vm *vm, uint32_t start, uint32_t nr_functions,
41 			      enum kvm_smccc_filter_action action)
42 {
43 	struct kvm_smccc_filter filter = {
44 		.base		= start,
45 		.nr_functions	= nr_functions,
46 		.action		= action,
47 	};
48 
49 	return __kvm_device_attr_set(vm->fd, KVM_ARM_VM_SMCCC_CTRL,
50 				     KVM_ARM_VM_SMCCC_FILTER, &filter);
51 }
52 
53 static void set_smccc_filter(struct kvm_vm *vm, uint32_t start, uint32_t nr_functions,
54 			     enum kvm_smccc_filter_action action)
55 {
56 	int ret = __set_smccc_filter(vm, start, nr_functions, action);
57 
58 	TEST_ASSERT(!ret, "failed to configure SMCCC filter: %d", ret);
59 }
60 
61 static struct kvm_vm *setup_vm(struct kvm_vcpu **vcpu)
62 {
63 	struct kvm_vcpu_init init;
64 	struct kvm_vm *vm;
65 
66 	vm = vm_create(1);
67 	vm_ioctl(vm, KVM_ARM_PREFERRED_TARGET, &init);
68 
69 	/*
70 	 * Enable in-kernel emulation of PSCI to ensure that calls are denied
71 	 * due to the SMCCC filter, not because of KVM.
72 	 */
73 	init.features[0] |= (1 << KVM_ARM_VCPU_PSCI_0_2);
74 
75 	*vcpu = aarch64_vcpu_add(vm, 0, &init, guest_main);
76 	return vm;
77 }
78 
79 static void test_pad_must_be_zero(void)
80 {
81 	struct kvm_vcpu *vcpu;
82 	struct kvm_vm *vm = setup_vm(&vcpu);
83 	struct kvm_smccc_filter filter = {
84 		.base		= PSCI_0_2_FN_PSCI_VERSION,
85 		.nr_functions	= 1,
86 		.action		= KVM_SMCCC_FILTER_DENY,
87 		.pad		= { -1 },
88 	};
89 	int r;
90 
91 	r = __kvm_device_attr_set(vm->fd, KVM_ARM_VM_SMCCC_CTRL,
92 				  KVM_ARM_VM_SMCCC_FILTER, &filter);
93 	TEST_ASSERT(r < 0 && errno == EINVAL,
94 		    "Setting filter with nonzero padding should return EINVAL");
95 }
96 
97 /* Ensure that userspace cannot filter the Arm Architecture SMCCC range */
98 static void test_filter_reserved_range(void)
99 {
100 	struct kvm_vcpu *vcpu;
101 	struct kvm_vm *vm = setup_vm(&vcpu);
102 	uint32_t smc64_fn;
103 	int r;
104 
105 	r = __set_smccc_filter(vm, ARM_SMCCC_ARCH_WORKAROUND_1,
106 			       1, KVM_SMCCC_FILTER_DENY);
107 	TEST_ASSERT(r < 0 && errno == EEXIST,
108 		    "Attempt to filter reserved range should return EEXIST");
109 
110 	smc64_fn = ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_64,
111 				      0, 0);
112 
113 	r = __set_smccc_filter(vm, smc64_fn, 1, KVM_SMCCC_FILTER_DENY);
114 	TEST_ASSERT(r < 0 && errno == EEXIST,
115 		    "Attempt to filter reserved range should return EEXIST");
116 
117 	kvm_vm_free(vm);
118 }
119 
120 static void test_invalid_nr_functions(void)
121 {
122 	struct kvm_vcpu *vcpu;
123 	struct kvm_vm *vm = setup_vm(&vcpu);
124 	int r;
125 
126 	r = __set_smccc_filter(vm, PSCI_0_2_FN64_CPU_ON, 0, KVM_SMCCC_FILTER_DENY);
127 	TEST_ASSERT(r < 0 && errno == EINVAL,
128 		    "Attempt to filter 0 functions should return EINVAL");
129 
130 	kvm_vm_free(vm);
131 }
132 
133 static void test_overflow_nr_functions(void)
134 {
135 	struct kvm_vcpu *vcpu;
136 	struct kvm_vm *vm = setup_vm(&vcpu);
137 	int r;
138 
139 	r = __set_smccc_filter(vm, ~0, ~0, KVM_SMCCC_FILTER_DENY);
140 	TEST_ASSERT(r < 0 && errno == EINVAL,
141 		    "Attempt to overflow filter range should return EINVAL");
142 
143 	kvm_vm_free(vm);
144 }
145 
146 static void test_reserved_action(void)
147 {
148 	struct kvm_vcpu *vcpu;
149 	struct kvm_vm *vm = setup_vm(&vcpu);
150 	int r;
151 
152 	r = __set_smccc_filter(vm, PSCI_0_2_FN64_CPU_ON, 1, -1);
153 	TEST_ASSERT(r < 0 && errno == EINVAL,
154 		    "Attempt to use reserved filter action should return EINVAL");
155 
156 	kvm_vm_free(vm);
157 }
158 
159 
160 /* Test that overlapping configurations of the SMCCC filter are rejected */
161 static void test_filter_overlap(void)
162 {
163 	struct kvm_vcpu *vcpu;
164 	struct kvm_vm *vm = setup_vm(&vcpu);
165 	int r;
166 
167 	set_smccc_filter(vm, PSCI_0_2_FN64_CPU_ON, 1, KVM_SMCCC_FILTER_DENY);
168 
169 	r = __set_smccc_filter(vm, PSCI_0_2_FN64_CPU_ON, 1, KVM_SMCCC_FILTER_DENY);
170 	TEST_ASSERT(r < 0 && errno == EEXIST,
171 		    "Attempt to filter already configured range should return EEXIST");
172 
173 	kvm_vm_free(vm);
174 }
175 
176 static void expect_call_denied(struct kvm_vcpu *vcpu)
177 {
178 	struct ucall uc;
179 
180 	if (get_ucall(vcpu, &uc) != UCALL_SYNC)
181 		TEST_FAIL("Unexpected ucall: %lu\n", uc.cmd);
182 
183 	TEST_ASSERT(uc.args[1] == SMCCC_RET_NOT_SUPPORTED,
184 		    "Unexpected SMCCC return code: %lu", uc.args[1]);
185 }
186 
187 /* Denied SMCCC calls have a return code of SMCCC_RET_NOT_SUPPORTED */
188 static void test_filter_denied(void)
189 {
190 	enum smccc_conduit conduit;
191 	struct kvm_vcpu *vcpu;
192 	struct kvm_vm *vm;
193 
194 	for_each_conduit(conduit) {
195 		vm = setup_vm(&vcpu);
196 
197 		set_smccc_filter(vm, PSCI_0_2_FN_PSCI_VERSION, 1, KVM_SMCCC_FILTER_DENY);
198 		vcpu_args_set(vcpu, 2, PSCI_0_2_FN_PSCI_VERSION, conduit);
199 
200 		vcpu_run(vcpu);
201 		expect_call_denied(vcpu);
202 
203 		kvm_vm_free(vm);
204 	}
205 }
206 
207 static void expect_call_fwd_to_user(struct kvm_vcpu *vcpu, uint32_t func_id,
208 				    enum smccc_conduit conduit)
209 {
210 	struct kvm_run *run = vcpu->run;
211 
212 	TEST_ASSERT(run->exit_reason == KVM_EXIT_HYPERCALL,
213 		    "Unexpected exit reason: %u", run->exit_reason);
214 	TEST_ASSERT(run->hypercall.nr == func_id,
215 		    "Unexpected SMCCC function: %llu", run->hypercall.nr);
216 
217 	if (conduit == SMC_INSN)
218 		TEST_ASSERT(run->hypercall.flags & KVM_HYPERCALL_EXIT_SMC,
219 			    "KVM_HYPERCALL_EXIT_SMC is not set");
220 	else
221 		TEST_ASSERT(!(run->hypercall.flags & KVM_HYPERCALL_EXIT_SMC),
222 			    "KVM_HYPERCALL_EXIT_SMC is set");
223 }
224 
225 /* SMCCC calls forwarded to userspace cause KVM_EXIT_HYPERCALL exits */
226 static void test_filter_fwd_to_user(void)
227 {
228 	enum smccc_conduit conduit;
229 	struct kvm_vcpu *vcpu;
230 	struct kvm_vm *vm;
231 
232 	for_each_conduit(conduit) {
233 		vm = setup_vm(&vcpu);
234 
235 		set_smccc_filter(vm, PSCI_0_2_FN_PSCI_VERSION, 1, KVM_SMCCC_FILTER_FWD_TO_USER);
236 		vcpu_args_set(vcpu, 2, PSCI_0_2_FN_PSCI_VERSION, conduit);
237 
238 		vcpu_run(vcpu);
239 		expect_call_fwd_to_user(vcpu, PSCI_0_2_FN_PSCI_VERSION, conduit);
240 
241 		kvm_vm_free(vm);
242 	}
243 }
244 
245 static bool kvm_supports_smccc_filter(void)
246 {
247 	struct kvm_vm *vm = vm_create_barebones();
248 	int r;
249 
250 	r = __kvm_has_device_attr(vm->fd, KVM_ARM_VM_SMCCC_CTRL, KVM_ARM_VM_SMCCC_FILTER);
251 
252 	kvm_vm_free(vm);
253 	return !r;
254 }
255 
256 int main(void)
257 {
258 	TEST_REQUIRE(kvm_supports_smccc_filter());
259 
260 	test_pad_must_be_zero();
261 	test_invalid_nr_functions();
262 	test_overflow_nr_functions();
263 	test_reserved_action();
264 	test_filter_reserved_range();
265 	test_filter_overlap();
266 	test_filter_denied();
267 	test_filter_fwd_to_user();
268 }
269