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