mirror of
https://github.com/torvalds/linux
synced 2024-11-05 18:23:50 +00:00
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:
parent
14567ba42c
commit
34f66c4c4d
6 changed files with 44 additions and 26 deletions
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue