File Index
Files and directories
Sign in
FILE

smccc_filter.c

File preview with syntax highlighting.

linux-6.19.14/tools/testing/selftests/kvm/arm64/smccc_filter.c C
  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
 25static 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
 40static void guest_main(uint32_t 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
 52static int __set_smccc_filter(struct kvm_vm *vm, uint32_t start, uint32_t 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
 65static void set_smccc_filter(struct kvm_vm *vm, uint32_t start, uint32_t 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
 73static 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
 92static 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 */
111static void test_filter_reserved_range(void)
112{
113	struct kvm_vcpu *vcpu;
114	struct kvm_vm *vm = setup_vm(&vcpu);
115	uint32_t 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
133static 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
146static 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
159static 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 */
174static 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
189static 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 */
201static 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
220static void expect_call_fwd_to_user(struct kvm_vcpu *vcpu, uint32_t 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 */
239static 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
258static 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
269int 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}
~/ Read-only file preview.