arm64: Use a positive cpucap for FP/SIMD

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>
This commit is contained in:
Mark Rutland 2023-10-16 11:24:36 +01:00 committed by Catalin Marinas
parent 14567ba42c
commit 34f66c4c4d
6 changed files with 44 additions and 26 deletions

View File

@ -760,7 +760,7 @@ static inline bool system_supports_mixed_endian(void)
static __always_inline bool system_supports_fpsimd(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) static inline bool system_uses_hw_pan(void)

View File

@ -149,6 +149,7 @@ extern void sme_save_state(void *state, int zt);
extern void sme_load_state(void const *state, int zt); extern void sme_load_state(void const *state, int zt);
struct arm64_cpu_capabilities; 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_sve(const struct arm64_cpu_capabilities *__unused);
extern void cpu_enable_sme(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); extern void cpu_enable_sme2(const struct arm64_cpu_capabilities *__unused);

View 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)); 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, static bool has_cache_idc(const struct arm64_cpu_capabilities *entry,
int scope) int scope)
{ {
@ -2398,11 +2390,11 @@ static const struct arm64_cpu_capabilities arm64_features[] = {
ARM64_CPUID_FIELDS(ID_AA64PFR0_EL1, CSV3, IMP) ARM64_CPUID_FIELDS(ID_AA64PFR0_EL1, CSV3, IMP)
}, },
{ {
/* FP/SIMD is not implemented */ .capability = ARM64_HAS_FPSIMD,
.capability = ARM64_HAS_NO_FPSIMD, .type = ARM64_CPUCAP_SYSTEM_FEATURE,
.type = ARM64_CPUCAP_BOOT_RESTRICTED_CPU_LOCAL_FEATURE, .matches = has_cpuid_feature,
.min_field_value = 0, .cpu_enable = cpu_enable_fpsimd,
.matches = has_no_fpsimd, ARM64_CPUID_FIELDS(ID_AA64PFR0_EL1, FP, IMP)
}, },
#ifdef CONFIG_ARM64_PMEM #ifdef CONFIG_ARM64_PMEM
{ {

View 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) void do_fpsimd_acc(unsigned long esr, struct pt_regs *regs)
{ {
/* TODO: implement lazy context saving/restoring */ /* Even if we chose not to use FPSIMD, the hardware could still trap: */
WARN_ON(1); 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) void fpsimd_restore_current_state(void)
{ {
/* /*
* For the tasks that were created before we detected the absence of * TIF_FOREIGN_FPSTATE is set on the init task and copied by
* FP/SIMD, the TIF_FOREIGN_FPSTATE could be set via fpsimd_thread_switch(), * arch_dup_task_struct() regardless of whether FP/SIMD is detected.
* e.g, init. This could be then inherited by the children processes. * Thus user threads can have this set even when FP/SIMD hasn't been
* If we later detect that the system doesn't support FP/SIMD, * detected.
* 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 * When FP/SIMD is detected, begin_new_exec() will set
* do_notify_resume(). * 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()) { if (!system_supports_fpsimd()) {
clear_thread_flag(TIF_FOREIGN_FPSTATE); clear_thread_flag(TIF_FOREIGN_FPSTATE);
@ -2101,6 +2120,13 @@ static inline void fpsimd_hotplug_init(void)
static inline void fpsimd_hotplug_init(void) { } static inline void fpsimd_hotplug_init(void) { }
#endif #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. * FP/SIMD support code initialisation.
*/ */

View File

@ -405,8 +405,7 @@ SYM_FUNC_START(__cpu_setup)
tlbi vmalle1 // Invalidate local TLB tlbi vmalle1 // Invalidate local TLB
dsb nsh dsb nsh
mov x1, #3 << 20 msr cpacr_el1, xzr // Reset cpacr_el1
msr cpacr_el1, x1 // Enable FP/ASIMD
mov x1, #1 << 12 // Reset mdscr_el1 and disable mov x1, #1 << 12 // Reset mdscr_el1 and disable
msr mdscr_el1, x1 // access to the DCC from EL0 msr mdscr_el1, x1 // access to the DCC from EL0
isb // Unmask debug exceptions now, isb // Unmask debug exceptions now,

View File

@ -27,6 +27,7 @@ HAS_ECV_CNTPOFF
HAS_EPAN HAS_EPAN
HAS_EVT HAS_EVT
HAS_FGT HAS_FGT
HAS_FPSIMD
HAS_GENERIC_AUTH HAS_GENERIC_AUTH
HAS_GENERIC_AUTH_ARCH_QARMA3 HAS_GENERIC_AUTH_ARCH_QARMA3
HAS_GENERIC_AUTH_ARCH_QARMA5 HAS_GENERIC_AUTH_ARCH_QARMA5
@ -39,7 +40,6 @@ HAS_LDAPR
HAS_LSE_ATOMICS HAS_LSE_ATOMICS
HAS_MOPS HAS_MOPS
HAS_NESTED_VIRT HAS_NESTED_VIRT
HAS_NO_FPSIMD
HAS_NO_HW_PREFETCH HAS_NO_HW_PREFETCH
HAS_PAN HAS_PAN
HAS_S1PIE HAS_S1PIE