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