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 25 #define for_each_conduit(conduit) \ 26 for (conduit = HVC_INSN; conduit <= SMC_INSN; conduit++) 27 28 static 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 40 static 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 53 static 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 61 static 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 79 static 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 */ 98 static 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 120 static 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 133 static 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 146 static 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 */ 161 static 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 176 static 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\n", 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 */ 188 static 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 207 static 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 */ 226 static 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 245 static 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 256 int 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