FILE
smccc_filter.c
File preview with syntax highlighting.
linux-6.19.14/tools/testing/selftests/kvm/arm64/smccc_filter.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.