KVM: arm64: Factor out firmware register handling from psci.c
authorRaghavendra Rao Ananta <rananta@google.com>
Mon, 2 May 2022 23:38:45 +0000 (23:38 +0000)
committerMarc Zyngier <maz@kernel.org>
Tue, 3 May 2022 13:48:54 +0000 (14:48 +0100)
Common hypercall firmware register handing is currently employed
by psci.c. Since the upcoming patches add more of these registers,
it's better to move the generic handling to hypercall.c for a
cleaner presentation.

While we are at it, collect all the firmware registers under
fw_reg_ids[] to help implement kvm_arm_get_fw_num_regs() and
kvm_arm_copy_fw_reg_indices() in a generic way. Also, define
KVM_REG_FEATURE_LEVEL_MASK using a GENMASK instead.

No functional change intended.

Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
Reviewed-by: Oliver Upton <oupton@google.com>
Reviewed-by: Gavin Shan <gshan@redhat.com>
[maz: fixed KVM_REG_FEATURE_LEVEL_MASK]
Signed-off-by: Marc Zyngier <maz@kernel.org>
Link: https://lore.kernel.org/r/20220502233853.1233742-2-rananta@google.com
arch/arm64/kvm/guest.c
arch/arm64/kvm/hypercalls.c
arch/arm64/kvm/psci.c
include/kvm/arm_hypercalls.h
include/kvm/arm_psci.h

index 7e15b03fbdf8efb769348079ac35a827bca2d08d..0d5cca56cbdada170208e927789c4f02c95ac5b7 100644 (file)
@@ -18,7 +18,7 @@
 #include <linux/string.h>
 #include <linux/vmalloc.h>
 #include <linux/fs.h>
-#include <kvm/arm_psci.h>
+#include <kvm/arm_hypercalls.h>
 #include <asm/cputype.h>
 #include <linux/uaccess.h>
 #include <asm/fpsimd.h>
index 202b8c455724bb0090b9d0f68e683bec0d696bfe..dd4e20b8d656529c58bc5f870af9d36896414f4a 100644 (file)
@@ -158,3 +158,187 @@ int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
        smccc_set_retval(vcpu, val[0], val[1], val[2], val[3]);
        return 1;
 }
+
+static const u64 kvm_arm_fw_reg_ids[] = {
+       KVM_REG_ARM_PSCI_VERSION,
+       KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1,
+       KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2,
+       KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3,
+};
+
+int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu)
+{
+       return ARRAY_SIZE(kvm_arm_fw_reg_ids);
+}
+
+int kvm_arm_copy_fw_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(kvm_arm_fw_reg_ids); i++) {
+               if (put_user(kvm_arm_fw_reg_ids[i], uindices++))
+                       return -EFAULT;
+       }
+
+       return 0;
+}
+
+#define KVM_REG_FEATURE_LEVEL_MASK     GENMASK(3, 0)
+
+/*
+ * Convert the workaround level into an easy-to-compare number, where higher
+ * values mean better protection.
+ */
+static int get_kernel_wa_level(u64 regid)
+{
+       switch (regid) {
+       case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
+               switch (arm64_get_spectre_v2_state()) {
+               case SPECTRE_VULNERABLE:
+                       return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_AVAIL;
+               case SPECTRE_MITIGATED:
+                       return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_AVAIL;
+               case SPECTRE_UNAFFECTED:
+                       return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_REQUIRED;
+               }
+               return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_AVAIL;
+       case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
+               switch (arm64_get_spectre_v4_state()) {
+               case SPECTRE_MITIGATED:
+                       /*
+                        * As for the hypercall discovery, we pretend we
+                        * don't have any FW mitigation if SSBS is there at
+                        * all times.
+                        */
+                       if (cpus_have_final_cap(ARM64_SSBS))
+                               return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
+                       fallthrough;
+               case SPECTRE_UNAFFECTED:
+                       return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED;
+               case SPECTRE_VULNERABLE:
+                       return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
+               }
+               break;
+       case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
+               switch (arm64_get_spectre_bhb_state()) {
+               case SPECTRE_VULNERABLE:
+                       return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_AVAIL;
+               case SPECTRE_MITIGATED:
+                       return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_AVAIL;
+               case SPECTRE_UNAFFECTED:
+                       return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_REQUIRED;
+               }
+               return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_AVAIL;
+       }
+
+       return -EINVAL;
+}
+
+int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
+{
+       void __user *uaddr = (void __user *)(long)reg->addr;
+       u64 val;
+
+       switch (reg->id) {
+       case KVM_REG_ARM_PSCI_VERSION:
+               val = kvm_psci_version(vcpu);
+               break;
+       case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
+       case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
+       case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
+               val = get_kernel_wa_level(reg->id) & KVM_REG_FEATURE_LEVEL_MASK;
+               break;
+       default:
+               return -ENOENT;
+       }
+
+       if (copy_to_user(uaddr, &val, KVM_REG_SIZE(reg->id)))
+               return -EFAULT;
+
+       return 0;
+}
+
+int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
+{
+       void __user *uaddr = (void __user *)(long)reg->addr;
+       u64 val;
+       int wa_level;
+
+       if (copy_from_user(&val, uaddr, KVM_REG_SIZE(reg->id)))
+               return -EFAULT;
+
+       switch (reg->id) {
+       case KVM_REG_ARM_PSCI_VERSION:
+       {
+               bool wants_02;
+
+               wants_02 = test_bit(KVM_ARM_VCPU_PSCI_0_2, vcpu->arch.features);
+
+               switch (val) {
+               case KVM_ARM_PSCI_0_1:
+                       if (wants_02)
+                               return -EINVAL;
+                       vcpu->kvm->arch.psci_version = val;
+                       return 0;
+               case KVM_ARM_PSCI_0_2:
+               case KVM_ARM_PSCI_1_0:
+               case KVM_ARM_PSCI_1_1:
+                       if (!wants_02)
+                               return -EINVAL;
+                       vcpu->kvm->arch.psci_version = val;
+                       return 0;
+               }
+               break;
+       }
+
+       case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
+       case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
+               if (val & ~KVM_REG_FEATURE_LEVEL_MASK)
+                       return -EINVAL;
+
+               if (get_kernel_wa_level(reg->id) < val)
+                       return -EINVAL;
+
+               return 0;
+
+       case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
+               if (val & ~(KVM_REG_FEATURE_LEVEL_MASK |
+                           KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_ENABLED))
+                       return -EINVAL;
+
+               /* The enabled bit must not be set unless the level is AVAIL. */
+               if ((val & KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_ENABLED) &&
+                   (val & KVM_REG_FEATURE_LEVEL_MASK) != KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_AVAIL)
+                       return -EINVAL;
+
+               /*
+                * Map all the possible incoming states to the only two we
+                * really want to deal with.
+                */
+               switch (val & KVM_REG_FEATURE_LEVEL_MASK) {
+               case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL:
+               case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_UNKNOWN:
+                       wa_level = KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
+                       break;
+               case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_AVAIL:
+               case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED:
+                       wa_level = KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED;
+                       break;
+               default:
+                       return -EINVAL;
+               }
+
+               /*
+                * We can deal with NOT_AVAIL on NOT_REQUIRED, but not the
+                * other way around.
+                */
+               if (get_kernel_wa_level(reg->id) < wa_level)
+                       return -EINVAL;
+
+               return 0;
+       default:
+               return -ENOENT;
+       }
+
+       return -EINVAL;
+}
index baac2b405f2356418d56be0ea1ac1c845e4c6675..346535169faa61e26d022339d284d8384520cb40 100644 (file)
@@ -436,186 +436,3 @@ int kvm_psci_call(struct kvm_vcpu *vcpu)
                return -EINVAL;
        }
 }
-
-int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu)
-{
-       return 4;               /* PSCI version and three workaround registers */
-}
-
-int kvm_arm_copy_fw_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
-{
-       if (put_user(KVM_REG_ARM_PSCI_VERSION, uindices++))
-               return -EFAULT;
-
-       if (put_user(KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1, uindices++))
-               return -EFAULT;
-
-       if (put_user(KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2, uindices++))
-               return -EFAULT;
-
-       if (put_user(KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3, uindices++))
-               return -EFAULT;
-
-       return 0;
-}
-
-#define KVM_REG_FEATURE_LEVEL_WIDTH    4
-#define KVM_REG_FEATURE_LEVEL_MASK     (BIT(KVM_REG_FEATURE_LEVEL_WIDTH) - 1)
-
-/*
- * Convert the workaround level into an easy-to-compare number, where higher
- * values mean better protection.
- */
-static int get_kernel_wa_level(u64 regid)
-{
-       switch (regid) {
-       case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
-               switch (arm64_get_spectre_v2_state()) {
-               case SPECTRE_VULNERABLE:
-                       return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_AVAIL;
-               case SPECTRE_MITIGATED:
-                       return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_AVAIL;
-               case SPECTRE_UNAFFECTED:
-                       return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_REQUIRED;
-               }
-               return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_AVAIL;
-       case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
-               switch (arm64_get_spectre_v4_state()) {
-               case SPECTRE_MITIGATED:
-                       /*
-                        * As for the hypercall discovery, we pretend we
-                        * don't have any FW mitigation if SSBS is there at
-                        * all times.
-                        */
-                       if (cpus_have_final_cap(ARM64_SSBS))
-                               return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
-                       fallthrough;
-               case SPECTRE_UNAFFECTED:
-                       return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED;
-               case SPECTRE_VULNERABLE:
-                       return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
-               }
-               break;
-       case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
-               switch (arm64_get_spectre_bhb_state()) {
-               case SPECTRE_VULNERABLE:
-                       return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_AVAIL;
-               case SPECTRE_MITIGATED:
-                       return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_AVAIL;
-               case SPECTRE_UNAFFECTED:
-                       return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_REQUIRED;
-               }
-               return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_AVAIL;
-       }
-
-       return -EINVAL;
-}
-
-int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
-{
-       void __user *uaddr = (void __user *)(long)reg->addr;
-       u64 val;
-
-       switch (reg->id) {
-       case KVM_REG_ARM_PSCI_VERSION:
-               val = kvm_psci_version(vcpu);
-               break;
-       case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
-       case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
-       case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
-               val = get_kernel_wa_level(reg->id) & KVM_REG_FEATURE_LEVEL_MASK;
-               break;
-       default:
-               return -ENOENT;
-       }
-
-       if (copy_to_user(uaddr, &val, KVM_REG_SIZE(reg->id)))
-               return -EFAULT;
-
-       return 0;
-}
-
-int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
-{
-       void __user *uaddr = (void __user *)(long)reg->addr;
-       u64 val;
-       int wa_level;
-
-       if (copy_from_user(&val, uaddr, KVM_REG_SIZE(reg->id)))
-               return -EFAULT;
-
-       switch (reg->id) {
-       case KVM_REG_ARM_PSCI_VERSION:
-       {
-               bool wants_02;
-
-               wants_02 = test_bit(KVM_ARM_VCPU_PSCI_0_2, vcpu->arch.features);
-
-               switch (val) {
-               case KVM_ARM_PSCI_0_1:
-                       if (wants_02)
-                               return -EINVAL;
-                       vcpu->kvm->arch.psci_version = val;
-                       return 0;
-               case KVM_ARM_PSCI_0_2:
-               case KVM_ARM_PSCI_1_0:
-               case KVM_ARM_PSCI_1_1:
-                       if (!wants_02)
-                               return -EINVAL;
-                       vcpu->kvm->arch.psci_version = val;
-                       return 0;
-               }
-               break;
-       }
-
-       case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
-       case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
-               if (val & ~KVM_REG_FEATURE_LEVEL_MASK)
-                       return -EINVAL;
-
-               if (get_kernel_wa_level(reg->id) < val)
-                       return -EINVAL;
-
-               return 0;
-
-       case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
-               if (val & ~(KVM_REG_FEATURE_LEVEL_MASK |
-                           KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_ENABLED))
-                       return -EINVAL;
-
-               /* The enabled bit must not be set unless the level is AVAIL. */
-               if ((val & KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_ENABLED) &&
-                   (val & KVM_REG_FEATURE_LEVEL_MASK) != KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_AVAIL)
-                       return -EINVAL;
-
-               /*
-                * Map all the possible incoming states to the only two we
-                * really want to deal with.
-                */
-               switch (val & KVM_REG_FEATURE_LEVEL_MASK) {
-               case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL:
-               case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_UNKNOWN:
-                       wa_level = KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
-                       break;
-               case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_AVAIL:
-               case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED:
-                       wa_level = KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED;
-                       break;
-               default:
-                       return -EINVAL;
-               }
-
-               /*
-                * We can deal with NOT_AVAIL on NOT_REQUIRED, but not the
-                * other way around.
-                */
-               if (get_kernel_wa_level(reg->id) < wa_level)
-                       return -EINVAL;
-
-               return 0;
-       default:
-               return -ENOENT;
-       }
-
-       return -EINVAL;
-}
index 0e2509d27910c1939877148db9812696ede8c5c6..5d38628a8d048c1895e9763f4df5a9945976144f 100644 (file)
@@ -40,4 +40,11 @@ static inline void smccc_set_retval(struct kvm_vcpu *vcpu,
        vcpu_set_reg(vcpu, 3, a3);
 }
 
+struct kvm_one_reg;
+
+int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu);
+int kvm_arm_copy_fw_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices);
+int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
+int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
+
 #endif
index 68b96c3826c36725aab572241b22cce9c94501d1..6e55b9283789b148f76030e63de34de03fc35cc0 100644 (file)
@@ -39,11 +39,4 @@ static inline int kvm_psci_version(struct kvm_vcpu *vcpu)
 
 int kvm_psci_call(struct kvm_vcpu *vcpu);
 
-struct kvm_one_reg;
-
-int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu);
-int kvm_arm_copy_fw_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices);
-int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
-int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
-
 #endif /* __KVM_ARM_PSCI_H__ */