]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
arm64: Use a positive cpucap for FP/SIMD
authorMark Rutland <mark.rutland@arm.com>
Mon, 16 Oct 2023 10:24:36 +0000 (11:24 +0100)
committerCatalin Marinas <catalin.marinas@arm.com>
Mon, 16 Oct 2023 13:17:03 +0000 (14:17 +0100)
Currently we have a negative cpucap which describes the *absence* of
FP/SIMD rather than *presence* of FP/SIMD. This largely works, but is
somewhat awkward relative to other cpucaps that describe the presence of
a feature, and it would be nicer to have a cpucap which describes the
presence of FP/SIMD:

* This will allow the cpucap to be treated as a standard
  ARM64_CPUCAP_SYSTEM_FEATURE, which can be detected with the standard
  has_cpuid_feature() function and ARM64_CPUID_FIELDS() description.

* This ensures that the cpucap will only transition from not-present to
  present, reducing the risk of unintentional and/or unsafe usage of
  FP/SIMD before cpucaps are finalized.

* This will allow using arm64_cpu_capabilities::cpu_enable() to enable
  the use of FP/SIMD later, with FP/SIMD being disabled at boot time
  otherwise. This will ensure that any unintentional and/or unsafe usage
  of FP/SIMD prior to this is trapped, and will ensure that FP/SIMD is
  never unintentionally enabled for userspace in mismatched big.LITTLE
  systems.

This patch replaces the negative ARM64_HAS_NO_FPSIMD cpucap with a
positive ARM64_HAS_FPSIMD cpucap, making changes as described above.
Note that as FP/SIMD will now be trapped when not supported system-wide,
do_fpsimd_acc() must handle these traps in the same way as for SVE and
SME. The commentary in fpsimd_restore_current_state() is updated to
describe the new scheme.

No users of system_supports_fpsimd() need to know that FP/SIMD is
available prior to alternatives being patched, so this is updated to
use alternative_has_cap_likely() to check for the ARM64_HAS_FPSIMD
cpucap, without generating code to test the system_cpucaps bitmap.

Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Reviewed-by: Mark Brown <broonie@kernel.org>
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/cpufeature.h
arch/arm64/include/asm/fpsimd.h
arch/arm64/kernel/cpufeature.c
arch/arm64/kernel/fpsimd.c
arch/arm64/mm/proc.S
arch/arm64/tools/cpucaps

index 1233a8ff96a88bb0c97872eb490ff113001bcceb..a1698945916ed82951644e63ce415d8a6da6434e 100644 (file)
@@ -760,7 +760,7 @@ static inline bool system_supports_mixed_endian(void)
 
 static __always_inline bool system_supports_fpsimd(void)
 {
-       return !cpus_have_const_cap(ARM64_HAS_NO_FPSIMD);
+       return alternative_has_cap_likely(ARM64_HAS_FPSIMD);
 }
 
 static inline bool system_uses_hw_pan(void)
index 0cbaa06b394a01a0b93bb17cb2feb310748daf78..c43ae9c013ec4636c712ce567c783033887f680e 100644 (file)
@@ -149,6 +149,7 @@ extern void sme_save_state(void *state, int zt);
 extern void sme_load_state(void const *state, int zt);
 
 struct arm64_cpu_capabilities;
+extern void cpu_enable_fpsimd(const struct arm64_cpu_capabilities *__unused);
 extern void cpu_enable_sve(const struct arm64_cpu_capabilities *__unused);
 extern void cpu_enable_sme(const struct arm64_cpu_capabilities *__unused);
 extern void cpu_enable_sme2(const struct arm64_cpu_capabilities *__unused);
index fb828f8c49e318ee87b53dfbe353c857688745fc..c493fa582a19001764619453d19bcf5d70650807 100644 (file)
@@ -1580,14 +1580,6 @@ static bool has_no_hw_prefetch(const struct arm64_cpu_capabilities *entry, int _
                MIDR_CPU_VAR_REV(1, MIDR_REVISION_MASK));
 }
 
-static bool has_no_fpsimd(const struct arm64_cpu_capabilities *entry, int __unused)
-{
-       u64 pfr0 = read_sanitised_ftr_reg(SYS_ID_AA64PFR0_EL1);
-
-       return cpuid_feature_extract_signed_field(pfr0,
-                                       ID_AA64PFR0_EL1_FP_SHIFT) < 0;
-}
-
 static bool has_cache_idc(const struct arm64_cpu_capabilities *entry,
                          int scope)
 {
@@ -2398,11 +2390,11 @@ static const struct arm64_cpu_capabilities arm64_features[] = {
                ARM64_CPUID_FIELDS(ID_AA64PFR0_EL1, CSV3, IMP)
        },
        {
-               /* FP/SIMD is not implemented */
-               .capability = ARM64_HAS_NO_FPSIMD,
-               .type = ARM64_CPUCAP_BOOT_RESTRICTED_CPU_LOCAL_FEATURE,
-               .min_field_value = 0,
-               .matches = has_no_fpsimd,
+               .capability = ARM64_HAS_FPSIMD,
+               .type = ARM64_CPUCAP_SYSTEM_FEATURE,
+               .matches = has_cpuid_feature,
+               .cpu_enable = cpu_enable_fpsimd,
+               ARM64_CPUID_FIELDS(ID_AA64PFR0_EL1, FP, IMP)
        },
 #ifdef CONFIG_ARM64_PMEM
        {
index 45ea9cabbaa41b532731f998460ce4b57ef81366..d286bcfc3f41d3b7889407576316c30c66880034 100644 (file)
@@ -1520,8 +1520,17 @@ void do_sme_acc(unsigned long esr, struct pt_regs *regs)
  */
 void do_fpsimd_acc(unsigned long esr, struct pt_regs *regs)
 {
-       /* TODO: implement lazy context saving/restoring */
-       WARN_ON(1);
+       /* Even if we chose not to use FPSIMD, the hardware could still trap: */
+       if (!system_supports_fpsimd()) {
+               force_signal_inject(SIGILL, ILL_ILLOPC, regs->pc, 0);
+               return;
+       }
+
+       /*
+        * When FPSIMD is enabled, we should never take a trap unless something
+        * has gone very wrong.
+        */
+       BUG();
 }
 
 /*
@@ -1762,13 +1771,23 @@ void fpsimd_bind_state_to_cpu(struct cpu_fp_state *state)
 void fpsimd_restore_current_state(void)
 {
        /*
-        * For the tasks that were created before we detected the absence of
-        * FP/SIMD, the TIF_FOREIGN_FPSTATE could be set via fpsimd_thread_switch(),
-        * e.g, init. This could be then inherited by the children processes.
-        * If we later detect that the system doesn't support FP/SIMD,
-        * we must clear the flag for  all the tasks to indicate that the
-        * FPSTATE is clean (as we can't have one) to avoid looping for ever in
-        * do_notify_resume().
+        * TIF_FOREIGN_FPSTATE is set on the init task and copied by
+        * arch_dup_task_struct() regardless of whether FP/SIMD is detected.
+        * Thus user threads can have this set even when FP/SIMD hasn't been
+        * detected.
+        *
+        * When FP/SIMD is detected, begin_new_exec() will set
+        * TIF_FOREIGN_FPSTATE via flush_thread() -> fpsimd_flush_thread(),
+        * and fpsimd_thread_switch() will set TIF_FOREIGN_FPSTATE when
+        * switching tasks. We detect FP/SIMD before we exec the first user
+        * process, ensuring this has TIF_FOREIGN_FPSTATE set and
+        * do_notify_resume() will call fpsimd_restore_current_state() to
+        * install the user FP/SIMD context.
+        *
+        * When FP/SIMD is not detected, nothing else will clear or set
+        * TIF_FOREIGN_FPSTATE prior to the first return to userspace, and
+        * we must clear TIF_FOREIGN_FPSTATE to avoid do_notify_resume()
+        * looping forever calling fpsimd_restore_current_state().
         */
        if (!system_supports_fpsimd()) {
                clear_thread_flag(TIF_FOREIGN_FPSTATE);
@@ -2101,6 +2120,13 @@ static inline void fpsimd_hotplug_init(void)
 static inline void fpsimd_hotplug_init(void) { }
 #endif
 
+void cpu_enable_fpsimd(const struct arm64_cpu_capabilities *__always_unused p)
+{
+       unsigned long enable = CPACR_EL1_FPEN_EL1EN | CPACR_EL1_FPEN_EL0EN;
+       write_sysreg(read_sysreg(CPACR_EL1) | enable, CPACR_EL1);
+       isb();
+}
+
 /*
  * FP/SIMD support code initialisation.
  */
index 14fdf645edc887776266d3553c0aeda7c75040da..f66c37a1610e1045272961dde543cda583452f53 100644 (file)
@@ -405,8 +405,7 @@ SYM_FUNC_START(__cpu_setup)
        tlbi    vmalle1                         // Invalidate local TLB
        dsb     nsh
 
-       mov     x1, #3 << 20
-       msr     cpacr_el1, x1                   // Enable FP/ASIMD
+       msr     cpacr_el1, xzr                  // Reset cpacr_el1
        mov     x1, #1 << 12                    // Reset mdscr_el1 and disable
        msr     mdscr_el1, x1                   // access to the DCC from EL0
        isb                                     // Unmask debug exceptions now,
index c3f06fdef60998d51c985ee167949a4de9a840e5..1adf562c914d91b6ea1f710ea2023c5e68235e43 100644 (file)
@@ -27,6 +27,7 @@ HAS_ECV_CNTPOFF
 HAS_EPAN
 HAS_EVT
 HAS_FGT
+HAS_FPSIMD
 HAS_GENERIC_AUTH
 HAS_GENERIC_AUTH_ARCH_QARMA3
 HAS_GENERIC_AUTH_ARCH_QARMA5
@@ -39,7 +40,6 @@ HAS_LDAPR
 HAS_LSE_ATOMICS
 HAS_MOPS
 HAS_NESTED_VIRT
-HAS_NO_FPSIMD
 HAS_NO_HW_PREFETCH
 HAS_PAN
 HAS_S1PIE