Skip to content

Commit 34f66c4

Browse files
mrutland-armctmarinas
authored andcommitted
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 <[email protected]> Reviewed-by: Mark Brown <[email protected]> Cc: Suzuki K Poulose <[email protected]> Cc: Will Deacon <[email protected]> Signed-off-by: Catalin Marinas <[email protected]>
1 parent 14567ba commit 34f66c4

File tree

6 files changed

+44
-26
lines changed

6 files changed

+44
-26
lines changed

arch/arm64/include/asm/cpufeature.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -760,7 +760,7 @@ static inline bool system_supports_mixed_endian(void)
760760

761761
static __always_inline bool system_supports_fpsimd(void)
762762
{
763-
return !cpus_have_const_cap(ARM64_HAS_NO_FPSIMD);
763+
return alternative_has_cap_likely(ARM64_HAS_FPSIMD);
764764
}
765765

766766
static inline bool system_uses_hw_pan(void)

arch/arm64/include/asm/fpsimd.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ extern void sme_save_state(void *state, int zt);
149149
extern void sme_load_state(void const *state, int zt);
150150

151151
struct arm64_cpu_capabilities;
152+
extern void cpu_enable_fpsimd(const struct arm64_cpu_capabilities *__unused);
152153
extern void cpu_enable_sve(const struct arm64_cpu_capabilities *__unused);
153154
extern void cpu_enable_sme(const struct arm64_cpu_capabilities *__unused);
154155
extern void cpu_enable_sme2(const struct arm64_cpu_capabilities *__unused);

arch/arm64/kernel/cpufeature.c

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1580,14 +1580,6 @@ static bool has_no_hw_prefetch(const struct arm64_cpu_capabilities *entry, int _
15801580
MIDR_CPU_VAR_REV(1, MIDR_REVISION_MASK));
15811581
}
15821582

1583-
static bool has_no_fpsimd(const struct arm64_cpu_capabilities *entry, int __unused)
1584-
{
1585-
u64 pfr0 = read_sanitised_ftr_reg(SYS_ID_AA64PFR0_EL1);
1586-
1587-
return cpuid_feature_extract_signed_field(pfr0,
1588-
ID_AA64PFR0_EL1_FP_SHIFT) < 0;
1589-
}
1590-
15911583
static bool has_cache_idc(const struct arm64_cpu_capabilities *entry,
15921584
int scope)
15931585
{
@@ -2398,11 +2390,11 @@ static const struct arm64_cpu_capabilities arm64_features[] = {
23982390
ARM64_CPUID_FIELDS(ID_AA64PFR0_EL1, CSV3, IMP)
23992391
},
24002392
{
2401-
/* FP/SIMD is not implemented */
2402-
.capability = ARM64_HAS_NO_FPSIMD,
2403-
.type = ARM64_CPUCAP_BOOT_RESTRICTED_CPU_LOCAL_FEATURE,
2404-
.min_field_value = 0,
2405-
.matches = has_no_fpsimd,
2393+
.capability = ARM64_HAS_FPSIMD,
2394+
.type = ARM64_CPUCAP_SYSTEM_FEATURE,
2395+
.matches = has_cpuid_feature,
2396+
.cpu_enable = cpu_enable_fpsimd,
2397+
ARM64_CPUID_FIELDS(ID_AA64PFR0_EL1, FP, IMP)
24062398
},
24072399
#ifdef CONFIG_ARM64_PMEM
24082400
{

arch/arm64/kernel/fpsimd.c

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1520,8 +1520,17 @@ void do_sme_acc(unsigned long esr, struct pt_regs *regs)
15201520
*/
15211521
void do_fpsimd_acc(unsigned long esr, struct pt_regs *regs)
15221522
{
1523-
/* TODO: implement lazy context saving/restoring */
1524-
WARN_ON(1);
1523+
/* Even if we chose not to use FPSIMD, the hardware could still trap: */
1524+
if (!system_supports_fpsimd()) {
1525+
force_signal_inject(SIGILL, ILL_ILLOPC, regs->pc, 0);
1526+
return;
1527+
}
1528+
1529+
/*
1530+
* When FPSIMD is enabled, we should never take a trap unless something
1531+
* has gone very wrong.
1532+
*/
1533+
BUG();
15251534
}
15261535

15271536
/*
@@ -1762,13 +1771,23 @@ void fpsimd_bind_state_to_cpu(struct cpu_fp_state *state)
17621771
void fpsimd_restore_current_state(void)
17631772
{
17641773
/*
1765-
* For the tasks that were created before we detected the absence of
1766-
* FP/SIMD, the TIF_FOREIGN_FPSTATE could be set via fpsimd_thread_switch(),
1767-
* e.g, init. This could be then inherited by the children processes.
1768-
* If we later detect that the system doesn't support FP/SIMD,
1769-
* we must clear the flag for all the tasks to indicate that the
1770-
* FPSTATE is clean (as we can't have one) to avoid looping for ever in
1771-
* do_notify_resume().
1774+
* TIF_FOREIGN_FPSTATE is set on the init task and copied by
1775+
* arch_dup_task_struct() regardless of whether FP/SIMD is detected.
1776+
* Thus user threads can have this set even when FP/SIMD hasn't been
1777+
* detected.
1778+
*
1779+
* When FP/SIMD is detected, begin_new_exec() will set
1780+
* TIF_FOREIGN_FPSTATE via flush_thread() -> fpsimd_flush_thread(),
1781+
* and fpsimd_thread_switch() will set TIF_FOREIGN_FPSTATE when
1782+
* switching tasks. We detect FP/SIMD before we exec the first user
1783+
* process, ensuring this has TIF_FOREIGN_FPSTATE set and
1784+
* do_notify_resume() will call fpsimd_restore_current_state() to
1785+
* install the user FP/SIMD context.
1786+
*
1787+
* When FP/SIMD is not detected, nothing else will clear or set
1788+
* TIF_FOREIGN_FPSTATE prior to the first return to userspace, and
1789+
* we must clear TIF_FOREIGN_FPSTATE to avoid do_notify_resume()
1790+
* looping forever calling fpsimd_restore_current_state().
17721791
*/
17731792
if (!system_supports_fpsimd()) {
17741793
clear_thread_flag(TIF_FOREIGN_FPSTATE);
@@ -2101,6 +2120,13 @@ static inline void fpsimd_hotplug_init(void)
21012120
static inline void fpsimd_hotplug_init(void) { }
21022121
#endif
21032122

2123+
void cpu_enable_fpsimd(const struct arm64_cpu_capabilities *__always_unused p)
2124+
{
2125+
unsigned long enable = CPACR_EL1_FPEN_EL1EN | CPACR_EL1_FPEN_EL0EN;
2126+
write_sysreg(read_sysreg(CPACR_EL1) | enable, CPACR_EL1);
2127+
isb();
2128+
}
2129+
21042130
/*
21052131
* FP/SIMD support code initialisation.
21062132
*/

arch/arm64/mm/proc.S

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -405,8 +405,7 @@ SYM_FUNC_START(__cpu_setup)
405405
tlbi vmalle1 // Invalidate local TLB
406406
dsb nsh
407407

408-
mov x1, #3 << 20
409-
msr cpacr_el1, x1 // Enable FP/ASIMD
408+
msr cpacr_el1, xzr // Reset cpacr_el1
410409
mov x1, #1 << 12 // Reset mdscr_el1 and disable
411410
msr mdscr_el1, x1 // access to the DCC from EL0
412411
isb // Unmask debug exceptions now,

arch/arm64/tools/cpucaps

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ HAS_ECV_CNTPOFF
2727
HAS_EPAN
2828
HAS_EVT
2929
HAS_FGT
30+
HAS_FPSIMD
3031
HAS_GENERIC_AUTH
3132
HAS_GENERIC_AUTH_ARCH_QARMA3
3233
HAS_GENERIC_AUTH_ARCH_QARMA5
@@ -39,7 +40,6 @@ HAS_LDAPR
3940
HAS_LSE_ATOMICS
4041
HAS_MOPS
4142
HAS_NESTED_VIRT
42-
HAS_NO_FPSIMD
4343
HAS_NO_HW_PREFETCH
4444
HAS_PAN
4545
HAS_S1PIE

0 commit comments

Comments
 (0)