arm64: kvm: Use cpus_have_final_cap() explicitly
authorMark Rutland <mark.rutland@arm.com>
Mon, 16 Oct 2023 10:24:32 +0000 (11:24 +0100)
committerCatalin Marinas <catalin.marinas@arm.com>
Mon, 16 Oct 2023 11:57:56 +0000 (12:57 +0100)
Much of the arm64 KVM code uses cpus_have_const_cap() to check for
cpucaps, but this is unnecessary and it would be preferable to use
cpus_have_final_cap().

For historical reasons, cpus_have_const_cap() is more complicated than
it needs to be. Before cpucaps are finalized, it will perform a bitmap
test of the system_cpucaps bitmap, and once cpucaps are finalized it
will use an alternative branch. This used to be necessary to handle some
race conditions in the window between cpucap detection and the
subsequent patching of alternatives and static branches, where different
branches could be out-of-sync with one another (or w.r.t. alternative
sequences). Now that we use alternative branches instead of static
branches, these are all patched atomically w.r.t. one another, and there
are only a handful of cases that need special care in the window between
cpucap detection and alternative patching.

Due to the above, it would be nice to remove cpus_have_const_cap(), and
migrate callers over to alternative_has_cap_*(), cpus_have_final_cap(),
or cpus_have_cap() depending on when their requirements. This will
remove redundant instructions and improve code generation, and will make
it easier to determine how each callsite will behave before, during, and
after alternative patching.

KVM is initialized after cpucaps have been finalized and alternatives
have been patched. Since commit:

  d86de40decaa14e6 ("arm64: cpufeature: upgrade hyp caps to final")

... use of cpus_have_const_cap() in hyp code is automatically converted
to use cpus_have_final_cap():

| static __always_inline bool cpus_have_const_cap(int num)
| {
|  if (is_hyp_code())
|  return cpus_have_final_cap(num);
|  else if (system_capabilities_finalized())
|  return __cpus_have_const_cap(num);
|  else
|  return cpus_have_cap(num);
| }

Thus, converting hyp code to use cpus_have_final_cap() directly will not
result in any functional change.

Non-hyp KVM code is also not executed until cpucaps have been finalized,
and it would be preferable to extent the same treatment to this code and
use cpus_have_final_cap() directly.

This patch converts instances of cpus_have_const_cap() in KVM-only code
over to cpus_have_final_cap(). As all of this code runs after cpucaps
have been finalized, there should be no functional change as a result of
this patch, but the redundant instructions generated by
cpus_have_const_cap() will be removed from the non-hyp KVM code.

Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Reviewed-by: Marc Zyngier <maz@kernel.org>
Cc: Oliver Upton <oliver.upton@linux.dev>
Cc: Suzuki K Poulose <suzuki.poulose@arm.com>
Cc: Will Deacon <will@kernel.org>
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
arch/arm64/include/asm/kvm_emulate.h
arch/arm64/include/asm/kvm_host.h
arch/arm64/include/asm/kvm_mmu.h
arch/arm64/kvm/arm.c
arch/arm64/kvm/guest.c
arch/arm64/kvm/hyp/pgtable.c
arch/arm64/kvm/mmu.c
arch/arm64/kvm/sys_regs.c
arch/arm64/kvm/vgic/vgic-v3.c

index 3d6725ff0bf6d24577c84c6814fcc814b0681fb8..cbd2f163a67d23a6596020f1451d80c6065500de 100644 (file)
@@ -71,14 +71,14 @@ static inline void vcpu_reset_hcr(struct kvm_vcpu *vcpu)
        vcpu->arch.hcr_el2 = HCR_GUEST_FLAGS;
        if (has_vhe() || has_hvhe())
                vcpu->arch.hcr_el2 |= HCR_E2H;
-       if (cpus_have_const_cap(ARM64_HAS_RAS_EXTN)) {
+       if (cpus_have_final_cap(ARM64_HAS_RAS_EXTN)) {
                /* route synchronous external abort exceptions to EL2 */
                vcpu->arch.hcr_el2 |= HCR_TEA;
                /* trap error record accesses */
                vcpu->arch.hcr_el2 |= HCR_TERR;
        }
 
-       if (cpus_have_const_cap(ARM64_HAS_STAGE2_FWB)) {
+       if (cpus_have_final_cap(ARM64_HAS_STAGE2_FWB)) {
                vcpu->arch.hcr_el2 |= HCR_FWB;
        } else {
                /*
index af06ccb7ee343304535c30b8855d6de8d8b1f4fc..e64d64e6ad449b34637f0243818d0e37971c5b93 100644 (file)
@@ -1052,7 +1052,7 @@ static inline void kvm_init_host_cpu_context(struct kvm_cpu_context *cpu_ctxt)
 
 static inline bool kvm_system_needs_idmapped_vectors(void)
 {
-       return cpus_have_const_cap(ARM64_SPECTRE_V3A);
+       return cpus_have_final_cap(ARM64_SPECTRE_V3A);
 }
 
 static inline void kvm_arch_sync_events(struct kvm *kvm) {}
index 96a80e8f62263eb30d04d8e0107807b75199f18a..27810667dec7d5953b9a80b01be701d5f6c164fa 100644 (file)
@@ -218,7 +218,7 @@ static inline void __clean_dcache_guest_page(void *va, size_t size)
         * faulting in pages. Furthermore, FWB implies IDC, so cleaning to
         * PoU is not required either in this case.
         */
-       if (cpus_have_const_cap(ARM64_HAS_STAGE2_FWB))
+       if (cpus_have_final_cap(ARM64_HAS_STAGE2_FWB))
                return;
 
        kvm_flush_dcache_to_poc(va, size);
index 4866b3f7b4ea3847d885e00cfac47a4d7abf9da3..4ea6c22250a51ca8369437f82ff4eff031636b67 100644 (file)
@@ -284,7 +284,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
                r = kvm_arm_pvtime_supported();
                break;
        case KVM_CAP_ARM_EL1_32BIT:
-               r = cpus_have_const_cap(ARM64_HAS_32BIT_EL1);
+               r = cpus_have_final_cap(ARM64_HAS_32BIT_EL1);
                break;
        case KVM_CAP_GUEST_DEBUG_HW_BPS:
                r = get_num_brps();
@@ -296,7 +296,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
                r = kvm_arm_support_pmu_v3();
                break;
        case KVM_CAP_ARM_INJECT_SERROR_ESR:
-               r = cpus_have_const_cap(ARM64_HAS_RAS_EXTN);
+               r = cpus_have_final_cap(ARM64_HAS_RAS_EXTN);
                break;
        case KVM_CAP_ARM_VM_IPA_SIZE:
                r = get_kvm_ipa_limit();
@@ -1207,7 +1207,7 @@ static int kvm_vcpu_init_check_features(struct kvm_vcpu *vcpu,
        if (!test_bit(KVM_ARM_VCPU_EL1_32BIT, &features))
                return 0;
 
-       if (!cpus_have_const_cap(ARM64_HAS_32BIT_EL1))
+       if (!cpus_have_final_cap(ARM64_HAS_32BIT_EL1))
                return -EINVAL;
 
        /* MTE is incompatible with AArch32 */
@@ -1777,7 +1777,7 @@ static void hyp_install_host_vector(void)
         * Call initialization code, and switch to the full blown HYP code.
         * If the cpucaps haven't been finalized yet, something has gone very
         * wrong, and hyp will crash and burn when it uses any
-        * cpus_have_const_cap() wrapper.
+        * cpus_have_*_cap() wrapper.
         */
        BUG_ON(!system_capabilities_finalized());
        params = this_cpu_ptr_nvhe_sym(kvm_init_params);
@@ -2310,7 +2310,7 @@ static int __init init_hyp_mode(void)
 
        if (is_protected_kvm_enabled()) {
                if (IS_ENABLED(CONFIG_ARM64_PTR_AUTH_KERNEL) &&
-                   cpus_have_const_cap(ARM64_HAS_ADDRESS_AUTH))
+                   cpus_have_final_cap(ARM64_HAS_ADDRESS_AUTH))
                        pkvm_hyp_init_ptrauth();
 
                init_cpu_logical_map();
index 95f6945c443252a2fc02934c49a1a9ab3bbfb530..a83c1d56a2940e00e0d5c23b91a467a2e5a2d167 100644 (file)
@@ -815,7 +815,7 @@ int __kvm_arm_vcpu_get_events(struct kvm_vcpu *vcpu,
                              struct kvm_vcpu_events *events)
 {
        events->exception.serror_pending = !!(vcpu->arch.hcr_el2 & HCR_VSE);
-       events->exception.serror_has_esr = cpus_have_const_cap(ARM64_HAS_RAS_EXTN);
+       events->exception.serror_has_esr = cpus_have_final_cap(ARM64_HAS_RAS_EXTN);
 
        if (events->exception.serror_pending && events->exception.serror_has_esr)
                events->exception.serror_esr = vcpu_get_vsesr(vcpu);
@@ -837,7 +837,7 @@ int __kvm_arm_vcpu_set_events(struct kvm_vcpu *vcpu,
        bool ext_dabt_pending = events->exception.ext_dabt_pending;
 
        if (serror_pending && has_esr) {
-               if (!cpus_have_const_cap(ARM64_HAS_RAS_EXTN))
+               if (!cpus_have_final_cap(ARM64_HAS_RAS_EXTN))
                        return -EINVAL;
 
                if (!((events->exception.serror_esr) & ~ESR_ELx_ISS_MASK))
index f155b8c9e98c7fbf1298f4ecf64c6826c76fdb23..799d2c204bb8afabe07224014f352bc3fd56fb69 100644 (file)
@@ -664,7 +664,7 @@ u64 kvm_get_vtcr(u64 mmfr0, u64 mmfr1, u32 phys_shift)
 
 static bool stage2_has_fwb(struct kvm_pgtable *pgt)
 {
-       if (!cpus_have_const_cap(ARM64_HAS_STAGE2_FWB))
+       if (!cpus_have_final_cap(ARM64_HAS_STAGE2_FWB))
                return false;
 
        return !(pgt->flags & KVM_PGTABLE_S2_NOFWB);
index 482280fe22d7c59d2539b9f0e758cfc95afad0ff..e6061fd174b0b4a7318b636d3316f5e5e938e3cc 100644 (file)
@@ -1578,7 +1578,7 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
 
        if (device)
                prot |= KVM_PGTABLE_PROT_DEVICE;
-       else if (cpus_have_const_cap(ARM64_HAS_CACHE_DIC))
+       else if (cpus_have_final_cap(ARM64_HAS_CACHE_DIC))
                prot |= KVM_PGTABLE_PROT_X;
 
        /*
index e92ec810d4494bac8ec83c7f23d0a360d9651493..9318b6939b788b12bbd9b7b2d48de62496cc50d3 100644 (file)
@@ -207,7 +207,7 @@ static bool access_dcsw(struct kvm_vcpu *vcpu,
         * CPU left in the system, and certainly not from non-secure
         * software).
         */
-       if (!cpus_have_const_cap(ARM64_HAS_STAGE2_FWB))
+       if (!cpus_have_final_cap(ARM64_HAS_STAGE2_FWB))
                kvm_set_way_flush(vcpu);
 
        return true;
index 3dfc8b84e03e67868ff49cb72a97695a9222ef2b..9465d3706ab9bcf8fb7fd72a8904d6b87ede574a 100644 (file)
@@ -684,7 +684,7 @@ int vgic_v3_probe(const struct gic_kvm_info *info)
        if (kvm_vgic_global_state.vcpu_base == 0)
                kvm_info("disabling GICv2 emulation\n");
 
-       if (cpus_have_const_cap(ARM64_WORKAROUND_CAVIUM_30115)) {
+       if (cpus_have_final_cap(ARM64_WORKAROUND_CAVIUM_30115)) {
                group0_trap = true;
                group1_trap = true;
        }