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
20enum 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
28static 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
40static 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
53static 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
61static 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
79static 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 */
98static 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
120static 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
133static 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
146static 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 */
161static 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
176static 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", 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 */
188static 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
207static 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 */
226static 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
245static 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
256int 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