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