Merge branch kvm-arm64/smccc-filtering into kvmarm-master/next
authorMarc Zyngier <maz@kernel.org>
Fri, 21 Apr 2023 08:43:38 +0000 (09:43 +0100)
committerMarc Zyngier <maz@kernel.org>
Fri, 21 Apr 2023 08:44:32 +0000 (09:44 +0100)
* kvm-arm64/smccc-filtering:
  : .
  : SMCCC call filtering and forwarding to userspace, courtesy of
  : Oliver Upton. From the cover letter:
  :
  : "The Arm SMCCC is rather prescriptive in regards to the allocation of
  : SMCCC function ID ranges. Many of the hypercall ranges have an
  : associated specification from Arm (FF-A, PSCI, SDEI, etc.) with some
  : room for vendor-specific implementations.
  :
  : The ever-expanding SMCCC surface leaves a lot of work within KVM for
  : providing new features. Furthermore, KVM implements its own
  : vendor-specific ABI, with little room for other implementations (like
  : Hyper-V, for example). Rather than cramming it all into the kernel we
  : should provide a way for userspace to handle hypercalls."
  : .
  KVM: selftests: Fix spelling mistake "KVM_HYPERCAL_EXIT_SMC" -> "KVM_HYPERCALL_EXIT_SMC"
  KVM: arm64: Test that SMC64 arch calls are reserved
  KVM: arm64: Prevent userspace from handling SMC64 arch range
  KVM: arm64: Expose SMC/HVC width to userspace
  KVM: selftests: Add test for SMCCC filter
  KVM: selftests: Add a helper for SMCCC calls with SMC instruction
  KVM: arm64: Let errors from SMCCC emulation to reach userspace
  KVM: arm64: Return NOT_SUPPORTED to guest for unknown PSCI version
  KVM: arm64: Introduce support for userspace SMCCC filtering
  KVM: arm64: Add support for KVM_EXIT_HYPERCALL
  KVM: arm64: Use a maple tree to represent the SMCCC filter
  KVM: arm64: Refactor hvc filtering to support different actions
  KVM: arm64: Start handling SMCs from EL1
  KVM: arm64: Rename SMC/HVC call handler to reflect reality
  KVM: arm64: Add vm fd device attribute accessors
  KVM: arm64: Add a helper to check if a VM has ran once
  KVM: x86: Redefine 'longmode' as a flag for KVM_EXIT_HYPERCALL

Signed-off-by: Marc Zyngier <maz@kernel.org>
1  2 
Documentation/virt/kvm/api.rst
arch/arm64/include/asm/kvm_host.h
arch/arm64/include/uapi/asm/kvm.h
arch/arm64/kvm/arm.c
arch/arm64/kvm/hypercalls.c
arch/arm64/kvm/pmu-emul.c
arch/arm64/kvm/psci.c
include/uapi/linux/kvm.h
tools/testing/selftests/kvm/lib/aarch64/processor.c

Simple merge
@@@ -224,11 -222,8 +225,12 @@@ struct kvm_arch 
  #define KVM_ARCH_FLAG_EL1_32BIT                               4
        /* PSCI SYSTEM_SUSPEND enabled for the guest */
  #define KVM_ARCH_FLAG_SYSTEM_SUSPEND_ENABLED          5
 +      /* VM counter offset */
 +#define KVM_ARCH_FLAG_VM_COUNTER_OFFSET                       6
 +      /* Timer PPIs made immutable */
 +#define KVM_ARCH_FLAG_TIMER_PPIS_IMMUTABLE            7
 -#define KVM_ARCH_FLAG_SMCCC_FILTER_CONFIGURED         6
+       /* SMCCC filter initialized for the VM */
++#define KVM_ARCH_FLAG_SMCCC_FILTER_CONFIGURED         8
        unsigned long flags;
  
        /*
Simple merge
@@@ -1517,13 -1502,18 +1540,25 @@@ long kvm_arch_vm_ioctl(struct file *fil
                        return -EFAULT;
                return kvm_vm_ioctl_mte_copy_tags(kvm, &copy_tags);
        }
 +      case KVM_ARM_SET_COUNTER_OFFSET: {
 +              struct kvm_arm_counter_offset offset;
 +
 +              if (copy_from_user(&offset, argp, sizeof(offset)))
 +                      return -EFAULT;
 +              return kvm_vm_ioctl_set_counter_offset(kvm, &offset);
 +      }
+       case KVM_HAS_DEVICE_ATTR: {
+               if (copy_from_user(&attr, argp, sizeof(attr)))
+                       return -EFAULT;
+               return kvm_vm_has_attr(kvm, &attr);
+       }
+       case KVM_SET_DEVICE_ATTR: {
+               if (copy_from_user(&attr, argp, sizeof(attr)))
+                       return -EFAULT;
+               return kvm_vm_set_attr(kvm, &attr);
+       }
        default:
                return -EINVAL;
        }
@@@ -121,7 -121,136 +121,136 @@@ static bool kvm_smccc_test_fw_bmap(stru
        }
  }
  
- int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
+ #define SMC32_ARCH_RANGE_BEGIN        ARM_SMCCC_VERSION_FUNC_ID
+ #define SMC32_ARCH_RANGE_END  ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL,         \
+                                                  ARM_SMCCC_SMC_32,            \
+                                                  0, ARM_SMCCC_FUNC_MASK)
+ #define SMC64_ARCH_RANGE_BEGIN        ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL,         \
+                                                  ARM_SMCCC_SMC_64,            \
+                                                  0, 0)
+ #define SMC64_ARCH_RANGE_END  ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL,         \
+                                                  ARM_SMCCC_SMC_64,            \
+                                                  0, ARM_SMCCC_FUNC_MASK)
+ static void init_smccc_filter(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
+        * to the guest.
+        */
+       r = mtree_insert_range(&kvm->arch.smccc_filter,
+                              SMC32_ARCH_RANGE_BEGIN, SMC32_ARCH_RANGE_END,
+                              xa_mk_value(KVM_SMCCC_FILTER_HANDLE),
+                              GFP_KERNEL_ACCOUNT);
+       WARN_ON_ONCE(r);
+       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);
+ }
+ static int kvm_smccc_set_filter(struct kvm *kvm, struct kvm_smccc_filter __user *uaddr)
+ {
+       const void *zero_page = page_to_virt(ZERO_PAGE(0));
+       struct kvm_smccc_filter filter;
+       u32 start, end;
+       int r;
+       if (copy_from_user(&filter, uaddr, sizeof(filter)))
+               return -EFAULT;
+       if (memcmp(filter.pad, zero_page, sizeof(filter.pad)))
+               return -EINVAL;
+       start = filter.base;
+       end = start + filter.nr_functions - 1;
+       if (end < start || filter.action >= NR_SMCCC_FILTER_ACTIONS)
+               return -EINVAL;
 -      mutex_lock(&kvm->lock);
++      mutex_lock(&kvm->arch.config_lock);
+       if (kvm_vm_has_ran_once(kvm)) {
+               r = -EBUSY;
+               goto out_unlock;
+       }
+       r = mtree_insert_range(&kvm->arch.smccc_filter, start, end,
+                              xa_mk_value(filter.action), GFP_KERNEL_ACCOUNT);
+       if (r)
+               goto out_unlock;
+       set_bit(KVM_ARCH_FLAG_SMCCC_FILTER_CONFIGURED, &kvm->arch.flags);
+ out_unlock:
 -      mutex_unlock(&kvm->lock);
++      mutex_unlock(&kvm->arch.config_lock);
+       return r;
+ }
+ static u8 kvm_smccc_filter_get_action(struct kvm *kvm, u32 func_id)
+ {
+       unsigned long idx = func_id;
+       void *val;
+       if (!test_bit(KVM_ARCH_FLAG_SMCCC_FILTER_CONFIGURED, &kvm->arch.flags))
+               return KVM_SMCCC_FILTER_HANDLE;
+       /*
+        * But where's the error handling, you say?
+        *
+        * mt_find() returns NULL if no entry was found, which just so happens
+        * to match KVM_SMCCC_FILTER_HANDLE.
+        */
+       val = mt_find(&kvm->arch.smccc_filter, &idx, idx);
+       return xa_to_value(val);
+ }
+ static u8 kvm_smccc_get_action(struct kvm_vcpu *vcpu, u32 func_id)
+ {
+       /*
+        * Intervening actions in the SMCCC filter take precedence over the
+        * pseudo-firmware register bitmaps.
+        */
+       u8 action = kvm_smccc_filter_get_action(vcpu->kvm, func_id);
+       if (action != KVM_SMCCC_FILTER_HANDLE)
+               return action;
+       if (kvm_smccc_test_fw_bmap(vcpu, func_id) ||
+           kvm_smccc_default_allowed(func_id))
+               return KVM_SMCCC_FILTER_HANDLE;
+       return KVM_SMCCC_FILTER_DENY;
+ }
+ static void kvm_prepare_hypercall_exit(struct kvm_vcpu *vcpu, u32 func_id)
+ {
+       u8 ec = ESR_ELx_EC(kvm_vcpu_get_esr(vcpu));
+       struct kvm_run *run = vcpu->run;
+       u64 flags = 0;
+       if (ec == ESR_ELx_EC_SMC32 || ec == ESR_ELx_EC_SMC64)
+               flags |= KVM_HYPERCALL_EXIT_SMC;
+       if (!kvm_vcpu_trap_il_is32bit(vcpu))
+               flags |= KVM_HYPERCALL_EXIT_16BIT;
+       run->exit_reason = KVM_EXIT_HYPERCALL;
+       run->hypercall = (typeof(run->hypercall)) {
+               .nr     = func_id,
+               .flags  = flags,
+       };
+ }
+ int kvm_smccc_call_handler(struct kvm_vcpu *vcpu)
  {
        struct kvm_smccc_features *smccc_feat = &vcpu->kvm->arch.smccc_feat;
        u32 func_id = smccc_get_function(vcpu);
@@@ -377,10 -525,9 +525,9 @@@ static int kvm_arm_set_fw_reg_bmap(stru
        if (val & ~fw_reg_features)
                return -EINVAL;
  
 -      mutex_lock(&kvm->lock);
 +      mutex_lock(&kvm->arch.config_lock);
  
-       if (test_bit(KVM_ARCH_FLAG_HAS_RAN_ONCE, &kvm->arch.flags) &&
-           val != *fw_reg_bmap) {
+       if (kvm_vm_has_ran_once(kvm) && val != *fw_reg_bmap) {
                ret = -EBUSY;
                goto out;
        }
@@@ -958,8 -961,12 +958,8 @@@ int kvm_arm_pmu_v3_set_attr(struct kvm_
                     filter.action != KVM_PMU_EVENT_DENY))
                        return -EINVAL;
  
-               if (test_bit(KVM_ARCH_FLAG_HAS_RAN_ONCE, &kvm->arch.flags))
 -              mutex_lock(&kvm->lock);
 -
 -              if (kvm_vm_has_ran_once(kvm)) {
 -                      mutex_unlock(&kvm->lock);
++              if (kvm_vm_has_ran_once(kvm))
                        return -EBUSY;
 -              }
  
                if (!kvm->arch.pmu_filter) {
                        kvm->arch.pmu_filter = bitmap_alloc(nr_events, GFP_KERNEL_ACCOUNT);
Simple merge
Simple merge