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