1*60e7dadeSOliver Upton // SPDX-License-Identifier: GPL-2.0-only 2*60e7dadeSOliver Upton /* 3*60e7dadeSOliver Upton * smccc_filter - Tests for the SMCCC filter UAPI. 4*60e7dadeSOliver Upton * 5*60e7dadeSOliver Upton * Copyright (c) 2023 Google LLC 6*60e7dadeSOliver Upton * 7*60e7dadeSOliver Upton * This test includes: 8*60e7dadeSOliver Upton * - Tests that the UAPI constraints are upheld by KVM. For example, userspace 9*60e7dadeSOliver Upton * is prevented from filtering the architecture range of SMCCC calls. 10*60e7dadeSOliver Upton * - Test that the filter actions (DENIED, FWD_TO_USER) work as intended. 11*60e7dadeSOliver Upton */ 12*60e7dadeSOliver Upton 13*60e7dadeSOliver Upton #include <linux/arm-smccc.h> 14*60e7dadeSOliver Upton #include <linux/psci.h> 15*60e7dadeSOliver Upton #include <stdint.h> 16*60e7dadeSOliver Upton 17*60e7dadeSOliver Upton #include "processor.h" 18*60e7dadeSOliver Upton #include "test_util.h" 19*60e7dadeSOliver Upton 20*60e7dadeSOliver Upton enum smccc_conduit { 21*60e7dadeSOliver Upton HVC_INSN, 22*60e7dadeSOliver Upton SMC_INSN, 23*60e7dadeSOliver Upton }; 24*60e7dadeSOliver Upton 25*60e7dadeSOliver Upton #define for_each_conduit(conduit) \ 26*60e7dadeSOliver Upton for (conduit = HVC_INSN; conduit <= SMC_INSN; conduit++) 27*60e7dadeSOliver Upton 28*60e7dadeSOliver Upton static void guest_main(uint32_t func_id, enum smccc_conduit conduit) 29*60e7dadeSOliver Upton { 30*60e7dadeSOliver Upton struct arm_smccc_res res; 31*60e7dadeSOliver Upton 32*60e7dadeSOliver Upton if (conduit == SMC_INSN) 33*60e7dadeSOliver Upton smccc_smc(func_id, 0, 0, 0, 0, 0, 0, 0, &res); 34*60e7dadeSOliver Upton else 35*60e7dadeSOliver Upton smccc_hvc(func_id, 0, 0, 0, 0, 0, 0, 0, &res); 36*60e7dadeSOliver Upton 37*60e7dadeSOliver Upton GUEST_SYNC(res.a0); 38*60e7dadeSOliver Upton } 39*60e7dadeSOliver Upton 40*60e7dadeSOliver Upton static int __set_smccc_filter(struct kvm_vm *vm, uint32_t start, uint32_t nr_functions, 41*60e7dadeSOliver Upton enum kvm_smccc_filter_action action) 42*60e7dadeSOliver Upton { 43*60e7dadeSOliver Upton struct kvm_smccc_filter filter = { 44*60e7dadeSOliver Upton .base = start, 45*60e7dadeSOliver Upton .nr_functions = nr_functions, 46*60e7dadeSOliver Upton .action = action, 47*60e7dadeSOliver Upton }; 48*60e7dadeSOliver Upton 49*60e7dadeSOliver Upton return __kvm_device_attr_set(vm->fd, KVM_ARM_VM_SMCCC_CTRL, 50*60e7dadeSOliver Upton KVM_ARM_VM_SMCCC_FILTER, &filter); 51*60e7dadeSOliver Upton } 52*60e7dadeSOliver Upton 53*60e7dadeSOliver Upton static void set_smccc_filter(struct kvm_vm *vm, uint32_t start, uint32_t nr_functions, 54*60e7dadeSOliver Upton enum kvm_smccc_filter_action action) 55*60e7dadeSOliver Upton { 56*60e7dadeSOliver Upton int ret = __set_smccc_filter(vm, start, nr_functions, action); 57*60e7dadeSOliver Upton 58*60e7dadeSOliver Upton TEST_ASSERT(!ret, "failed to configure SMCCC filter: %d", ret); 59*60e7dadeSOliver Upton } 60*60e7dadeSOliver Upton 61*60e7dadeSOliver Upton static struct kvm_vm *setup_vm(struct kvm_vcpu **vcpu) 62*60e7dadeSOliver Upton { 63*60e7dadeSOliver Upton struct kvm_vcpu_init init; 64*60e7dadeSOliver Upton struct kvm_vm *vm; 65*60e7dadeSOliver Upton 66*60e7dadeSOliver Upton vm = vm_create(1); 67*60e7dadeSOliver Upton vm_ioctl(vm, KVM_ARM_PREFERRED_TARGET, &init); 68*60e7dadeSOliver Upton 69*60e7dadeSOliver Upton /* 70*60e7dadeSOliver Upton * Enable in-kernel emulation of PSCI to ensure that calls are denied 71*60e7dadeSOliver Upton * due to the SMCCC filter, not because of KVM. 72*60e7dadeSOliver Upton */ 73*60e7dadeSOliver Upton init.features[0] |= (1 << KVM_ARM_VCPU_PSCI_0_2); 74*60e7dadeSOliver Upton 75*60e7dadeSOliver Upton *vcpu = aarch64_vcpu_add(vm, 0, &init, guest_main); 76*60e7dadeSOliver Upton return vm; 77*60e7dadeSOliver Upton } 78*60e7dadeSOliver Upton 79*60e7dadeSOliver Upton static void test_pad_must_be_zero(void) 80*60e7dadeSOliver Upton { 81*60e7dadeSOliver Upton struct kvm_vcpu *vcpu; 82*60e7dadeSOliver Upton struct kvm_vm *vm = setup_vm(&vcpu); 83*60e7dadeSOliver Upton struct kvm_smccc_filter filter = { 84*60e7dadeSOliver Upton .base = PSCI_0_2_FN_PSCI_VERSION, 85*60e7dadeSOliver Upton .nr_functions = 1, 86*60e7dadeSOliver Upton .action = KVM_SMCCC_FILTER_DENY, 87*60e7dadeSOliver Upton .pad = { -1 }, 88*60e7dadeSOliver Upton }; 89*60e7dadeSOliver Upton int r; 90*60e7dadeSOliver Upton 91*60e7dadeSOliver Upton r = __kvm_device_attr_set(vm->fd, KVM_ARM_VM_SMCCC_CTRL, 92*60e7dadeSOliver Upton KVM_ARM_VM_SMCCC_FILTER, &filter); 93*60e7dadeSOliver Upton TEST_ASSERT(r < 0 && errno == EINVAL, 94*60e7dadeSOliver Upton "Setting filter with nonzero padding should return EINVAL"); 95*60e7dadeSOliver Upton } 96*60e7dadeSOliver Upton 97*60e7dadeSOliver Upton /* Ensure that userspace cannot filter the Arm Architecture SMCCC range */ 98*60e7dadeSOliver Upton static void test_filter_reserved_range(void) 99*60e7dadeSOliver Upton { 100*60e7dadeSOliver Upton struct kvm_vcpu *vcpu; 101*60e7dadeSOliver Upton struct kvm_vm *vm = setup_vm(&vcpu); 102*60e7dadeSOliver Upton int r; 103*60e7dadeSOliver Upton 104*60e7dadeSOliver Upton r = __set_smccc_filter(vm, ARM_SMCCC_ARCH_WORKAROUND_1, 105*60e7dadeSOliver Upton 1, KVM_SMCCC_FILTER_DENY); 106*60e7dadeSOliver Upton TEST_ASSERT(r < 0 && errno == EEXIST, 107*60e7dadeSOliver Upton "Attempt to filter reserved range should return EEXIST"); 108*60e7dadeSOliver Upton 109*60e7dadeSOliver Upton kvm_vm_free(vm); 110*60e7dadeSOliver Upton } 111*60e7dadeSOliver Upton 112*60e7dadeSOliver Upton static void test_invalid_nr_functions(void) 113*60e7dadeSOliver Upton { 114*60e7dadeSOliver Upton struct kvm_vcpu *vcpu; 115*60e7dadeSOliver Upton struct kvm_vm *vm = setup_vm(&vcpu); 116*60e7dadeSOliver Upton int r; 117*60e7dadeSOliver Upton 118*60e7dadeSOliver Upton r = __set_smccc_filter(vm, PSCI_0_2_FN64_CPU_ON, 0, KVM_SMCCC_FILTER_DENY); 119*60e7dadeSOliver Upton TEST_ASSERT(r < 0 && errno == EINVAL, 120*60e7dadeSOliver Upton "Attempt to filter 0 functions should return EINVAL"); 121*60e7dadeSOliver Upton 122*60e7dadeSOliver Upton kvm_vm_free(vm); 123*60e7dadeSOliver Upton } 124*60e7dadeSOliver Upton 125*60e7dadeSOliver Upton static void test_overflow_nr_functions(void) 126*60e7dadeSOliver Upton { 127*60e7dadeSOliver Upton struct kvm_vcpu *vcpu; 128*60e7dadeSOliver Upton struct kvm_vm *vm = setup_vm(&vcpu); 129*60e7dadeSOliver Upton int r; 130*60e7dadeSOliver Upton 131*60e7dadeSOliver Upton r = __set_smccc_filter(vm, ~0, ~0, KVM_SMCCC_FILTER_DENY); 132*60e7dadeSOliver Upton TEST_ASSERT(r < 0 && errno == EINVAL, 133*60e7dadeSOliver Upton "Attempt to overflow filter range should return EINVAL"); 134*60e7dadeSOliver Upton 135*60e7dadeSOliver Upton kvm_vm_free(vm); 136*60e7dadeSOliver Upton } 137*60e7dadeSOliver Upton 138*60e7dadeSOliver Upton static void test_reserved_action(void) 139*60e7dadeSOliver Upton { 140*60e7dadeSOliver Upton struct kvm_vcpu *vcpu; 141*60e7dadeSOliver Upton struct kvm_vm *vm = setup_vm(&vcpu); 142*60e7dadeSOliver Upton int r; 143*60e7dadeSOliver Upton 144*60e7dadeSOliver Upton r = __set_smccc_filter(vm, PSCI_0_2_FN64_CPU_ON, 1, -1); 145*60e7dadeSOliver Upton TEST_ASSERT(r < 0 && errno == EINVAL, 146*60e7dadeSOliver Upton "Attempt to use reserved filter action should return EINVAL"); 147*60e7dadeSOliver Upton 148*60e7dadeSOliver Upton kvm_vm_free(vm); 149*60e7dadeSOliver Upton } 150*60e7dadeSOliver Upton 151*60e7dadeSOliver Upton 152*60e7dadeSOliver Upton /* Test that overlapping configurations of the SMCCC filter are rejected */ 153*60e7dadeSOliver Upton static void test_filter_overlap(void) 154*60e7dadeSOliver Upton { 155*60e7dadeSOliver Upton struct kvm_vcpu *vcpu; 156*60e7dadeSOliver Upton struct kvm_vm *vm = setup_vm(&vcpu); 157*60e7dadeSOliver Upton int r; 158*60e7dadeSOliver Upton 159*60e7dadeSOliver Upton set_smccc_filter(vm, PSCI_0_2_FN64_CPU_ON, 1, KVM_SMCCC_FILTER_DENY); 160*60e7dadeSOliver Upton 161*60e7dadeSOliver Upton r = __set_smccc_filter(vm, PSCI_0_2_FN64_CPU_ON, 1, KVM_SMCCC_FILTER_DENY); 162*60e7dadeSOliver Upton TEST_ASSERT(r < 0 && errno == EEXIST, 163*60e7dadeSOliver Upton "Attempt to filter already configured range should return EEXIST"); 164*60e7dadeSOliver Upton 165*60e7dadeSOliver Upton kvm_vm_free(vm); 166*60e7dadeSOliver Upton } 167*60e7dadeSOliver Upton 168*60e7dadeSOliver Upton static void expect_call_denied(struct kvm_vcpu *vcpu) 169*60e7dadeSOliver Upton { 170*60e7dadeSOliver Upton struct ucall uc; 171*60e7dadeSOliver Upton 172*60e7dadeSOliver Upton if (get_ucall(vcpu, &uc) != UCALL_SYNC) 173*60e7dadeSOliver Upton TEST_FAIL("Unexpected ucall: %lu\n", uc.cmd); 174*60e7dadeSOliver Upton 175*60e7dadeSOliver Upton TEST_ASSERT(uc.args[1] == SMCCC_RET_NOT_SUPPORTED, 176*60e7dadeSOliver Upton "Unexpected SMCCC return code: %lu", uc.args[1]); 177*60e7dadeSOliver Upton } 178*60e7dadeSOliver Upton 179*60e7dadeSOliver Upton /* Denied SMCCC calls have a return code of SMCCC_RET_NOT_SUPPORTED */ 180*60e7dadeSOliver Upton static void test_filter_denied(void) 181*60e7dadeSOliver Upton { 182*60e7dadeSOliver Upton enum smccc_conduit conduit; 183*60e7dadeSOliver Upton struct kvm_vcpu *vcpu; 184*60e7dadeSOliver Upton struct kvm_vm *vm; 185*60e7dadeSOliver Upton 186*60e7dadeSOliver Upton for_each_conduit(conduit) { 187*60e7dadeSOliver Upton vm = setup_vm(&vcpu); 188*60e7dadeSOliver Upton 189*60e7dadeSOliver Upton set_smccc_filter(vm, PSCI_0_2_FN_PSCI_VERSION, 1, KVM_SMCCC_FILTER_DENY); 190*60e7dadeSOliver Upton vcpu_args_set(vcpu, 2, PSCI_0_2_FN_PSCI_VERSION, conduit); 191*60e7dadeSOliver Upton 192*60e7dadeSOliver Upton vcpu_run(vcpu); 193*60e7dadeSOliver Upton expect_call_denied(vcpu); 194*60e7dadeSOliver Upton 195*60e7dadeSOliver Upton kvm_vm_free(vm); 196*60e7dadeSOliver Upton } 197*60e7dadeSOliver Upton } 198*60e7dadeSOliver Upton 199*60e7dadeSOliver Upton static void expect_call_fwd_to_user(struct kvm_vcpu *vcpu, uint32_t func_id, 200*60e7dadeSOliver Upton enum smccc_conduit conduit) 201*60e7dadeSOliver Upton { 202*60e7dadeSOliver Upton struct kvm_run *run = vcpu->run; 203*60e7dadeSOliver Upton 204*60e7dadeSOliver Upton TEST_ASSERT(run->exit_reason == KVM_EXIT_HYPERCALL, 205*60e7dadeSOliver Upton "Unexpected exit reason: %u", run->exit_reason); 206*60e7dadeSOliver Upton TEST_ASSERT(run->hypercall.nr == func_id, 207*60e7dadeSOliver Upton "Unexpected SMCCC function: %llu", run->hypercall.nr); 208*60e7dadeSOliver Upton 209*60e7dadeSOliver Upton if (conduit == SMC_INSN) 210*60e7dadeSOliver Upton TEST_ASSERT(run->hypercall.flags & KVM_HYPERCALL_EXIT_SMC, 211*60e7dadeSOliver Upton "KVM_HYPERCALL_EXIT_SMC is not set"); 212*60e7dadeSOliver Upton else 213*60e7dadeSOliver Upton TEST_ASSERT(!(run->hypercall.flags & KVM_HYPERCALL_EXIT_SMC), 214*60e7dadeSOliver Upton "KVM_HYPERCAL_EXIT_SMC is set"); 215*60e7dadeSOliver Upton } 216*60e7dadeSOliver Upton 217*60e7dadeSOliver Upton /* SMCCC calls forwarded to userspace cause KVM_EXIT_HYPERCALL exits */ 218*60e7dadeSOliver Upton static void test_filter_fwd_to_user(void) 219*60e7dadeSOliver Upton { 220*60e7dadeSOliver Upton enum smccc_conduit conduit; 221*60e7dadeSOliver Upton struct kvm_vcpu *vcpu; 222*60e7dadeSOliver Upton struct kvm_vm *vm; 223*60e7dadeSOliver Upton 224*60e7dadeSOliver Upton for_each_conduit(conduit) { 225*60e7dadeSOliver Upton vm = setup_vm(&vcpu); 226*60e7dadeSOliver Upton 227*60e7dadeSOliver Upton set_smccc_filter(vm, PSCI_0_2_FN_PSCI_VERSION, 1, KVM_SMCCC_FILTER_FWD_TO_USER); 228*60e7dadeSOliver Upton vcpu_args_set(vcpu, 2, PSCI_0_2_FN_PSCI_VERSION, conduit); 229*60e7dadeSOliver Upton 230*60e7dadeSOliver Upton vcpu_run(vcpu); 231*60e7dadeSOliver Upton expect_call_fwd_to_user(vcpu, PSCI_0_2_FN_PSCI_VERSION, conduit); 232*60e7dadeSOliver Upton 233*60e7dadeSOliver Upton kvm_vm_free(vm); 234*60e7dadeSOliver Upton } 235*60e7dadeSOliver Upton } 236*60e7dadeSOliver Upton 237*60e7dadeSOliver Upton static bool kvm_supports_smccc_filter(void) 238*60e7dadeSOliver Upton { 239*60e7dadeSOliver Upton struct kvm_vm *vm = vm_create_barebones(); 240*60e7dadeSOliver Upton int r; 241*60e7dadeSOliver Upton 242*60e7dadeSOliver Upton r = __kvm_has_device_attr(vm->fd, KVM_ARM_VM_SMCCC_CTRL, KVM_ARM_VM_SMCCC_FILTER); 243*60e7dadeSOliver Upton 244*60e7dadeSOliver Upton kvm_vm_free(vm); 245*60e7dadeSOliver Upton return !r; 246*60e7dadeSOliver Upton } 247*60e7dadeSOliver Upton 248*60e7dadeSOliver Upton int main(void) 249*60e7dadeSOliver Upton { 250*60e7dadeSOliver Upton TEST_REQUIRE(kvm_supports_smccc_filter()); 251*60e7dadeSOliver Upton 252*60e7dadeSOliver Upton test_pad_must_be_zero(); 253*60e7dadeSOliver Upton test_invalid_nr_functions(); 254*60e7dadeSOliver Upton test_overflow_nr_functions(); 255*60e7dadeSOliver Upton test_reserved_action(); 256*60e7dadeSOliver Upton test_filter_reserved_range(); 257*60e7dadeSOliver Upton test_filter_overlap(); 258*60e7dadeSOliver Upton test_filter_denied(); 259*60e7dadeSOliver Upton test_filter_fwd_to_user(); 260*60e7dadeSOliver Upton } 261