KVM: arm64: Only insert reserved ranges when SMCCC filter is used
authorOliver Upton <oliver.upton@linux.dev>
Wed, 4 Oct 2023 23:49:46 +0000 (23:49 +0000)
committerOliver Upton <oliver.upton@linux.dev>
Thu, 5 Oct 2023 09:33:15 +0000 (09:33 +0000)
The reserved ranges are only useful for preventing userspace from
adding a rule that intersects with functions we must handle in KVM. If
userspace never writes to the SMCCC filter than this is all just wasted
work/memory.

Insert reserved ranges on the first call to KVM_ARM_VM_SMCCC_FILTER.

Reviewed-by: Marc Zyngier <maz@kernel.org>
Link: https://lore.kernel.org/r/20231004234947.207507-3-oliver.upton@linux.dev
Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
arch/arm64/kvm/hypercalls.c

index 35e0233..20a878c 100644 (file)
@@ -133,12 +133,10 @@ static bool kvm_smccc_test_fw_bmap(struct kvm_vcpu *vcpu, u32 func_id)
                                                   ARM_SMCCC_SMC_64,            \
                                                   0, ARM_SMCCC_FUNC_MASK)
 
-static void init_smccc_filter(struct kvm *kvm)
+static int kvm_smccc_filter_insert_reserved(struct kvm *kvm)
 {
        int r;
 
-       mt_init(&kvm->arch.smccc_filter);
-
        /*
         * Prevent userspace from handling any SMCCC calls in the architecture
         * range, avoiding the risk of misrepresenting Spectre mitigation status
@@ -148,14 +146,20 @@ static void init_smccc_filter(struct kvm *kvm)
                               SMC32_ARCH_RANGE_BEGIN, SMC32_ARCH_RANGE_END,
                               xa_mk_value(KVM_SMCCC_FILTER_HANDLE),
                               GFP_KERNEL_ACCOUNT);
-       WARN_ON_ONCE(r);
+       if (r)
+               goto out_destroy;
 
        r = mtree_insert_range(&kvm->arch.smccc_filter,
                               SMC64_ARCH_RANGE_BEGIN, SMC64_ARCH_RANGE_END,
                               xa_mk_value(KVM_SMCCC_FILTER_HANDLE),
                               GFP_KERNEL_ACCOUNT);
-       WARN_ON_ONCE(r);
+       if (r)
+               goto out_destroy;
 
+       return 0;
+out_destroy:
+       mtree_destroy(&kvm->arch.smccc_filter);
+       return r;
 }
 
 static bool kvm_smccc_filter_configured(struct kvm *kvm)
@@ -189,6 +193,12 @@ static int kvm_smccc_set_filter(struct kvm *kvm, struct kvm_smccc_filter __user
                goto out_unlock;
        }
 
+       if (!kvm_smccc_filter_configured(kvm)) {
+               r = kvm_smccc_filter_insert_reserved(kvm);
+               if (WARN_ON_ONCE(r))
+                       goto out_unlock;
+       }
+
        r = mtree_insert_range(&kvm->arch.smccc_filter, start, end,
                               xa_mk_value(filter.action), GFP_KERNEL_ACCOUNT);
        if (r)
@@ -392,7 +402,7 @@ void kvm_arm_init_hypercalls(struct kvm *kvm)
        smccc_feat->std_hyp_bmap = KVM_ARM_SMCCC_STD_HYP_FEATURES;
        smccc_feat->vendor_hyp_bmap = KVM_ARM_SMCCC_VENDOR_HYP_FEATURES;
 
-       init_smccc_filter(kvm);
+       mt_init(&kvm->arch.smccc_filter);
 }
 
 void kvm_arm_teardown_hypercalls(struct kvm *kvm)