* [PATCH v2 01/13] arm64/fpsimd: Avoid RES0 bits in the SME trap handler
2025-04-09 16:39 [PATCH v2 00/13] arm64: Preparatory FPSIMD/SVE/SME fixes Mark Rutland
@ 2025-04-09 16:39 ` Mark Rutland
2025-04-09 16:39 ` [PATCH v2 02/13] arm64/fpsimd: Remove unused fpsimd_force_sync_to_sve() Mark Rutland
` (12 subsequent siblings)
13 siblings, 0 replies; 18+ messages in thread
From: Mark Rutland @ 2025-04-09 16:39 UTC (permalink / raw)
To: linux-arm-kernel; +Cc: ardb, broonie, catalin.marinas, mark.rutland, maz, will
The SME trap handler consumes RES0 bits from the ESR when determining
the reason for the trap, and depends upon those bits reading as zero.
This may break in future when those RES0 bits are allocated a meaning
and stop reading as zero.
For SME traps taken with ESR_ELx.EC == 0b011101, the specific reason for
the trap is indicated by ESR_ELx.ISS.SMTC ("SME Trap Code"). This field
occupies bits [2:0] of ESR_ELx.ISS, and as of ARM DDI 0487 L.a, bits
[24:3] of ESR_ELx.ISS are RES0. ESR_ELx.ISS itself occupies bits [24:0]
of ESR_ELx.
Extract the SMTC field specifically, matching the way we handle ESR_ELx
fields elsewhere, and ensuring that the handler is future-proof.
Fixes: 8bd7f91c03d886f4 ("arm64/sme: Implement traps and syscall handling for SME")
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Marc Zyngier <maz@kernel.org>
Cc: Mark Brown <broonie@kernel.org>
Cc: Will Deacon <will@kernel.org>
Reviewed-by: Mark Brown <broonie@kernel.org>
---
arch/arm64/include/asm/esr.h | 14 ++++++++------
arch/arm64/kernel/fpsimd.c | 2 +-
2 files changed, 9 insertions(+), 7 deletions(-)
diff --git a/arch/arm64/include/asm/esr.h b/arch/arm64/include/asm/esr.h
index d1b1a33f9a8b0..63aed18a933fe 100644
--- a/arch/arm64/include/asm/esr.h
+++ b/arch/arm64/include/asm/esr.h
@@ -371,12 +371,14 @@
/*
* ISS values for SME traps
*/
-
-#define ESR_ELx_SME_ISS_SME_DISABLED 0
-#define ESR_ELx_SME_ISS_ILL 1
-#define ESR_ELx_SME_ISS_SM_DISABLED 2
-#define ESR_ELx_SME_ISS_ZA_DISABLED 3
-#define ESR_ELx_SME_ISS_ZT_DISABLED 4
+#define ESR_ELx_SME_ISS_SMTC_MASK GENMASK(2, 0)
+#define ESR_ELx_SME_ISS_SMTC(esr) ((esr) & ESR_ELx_SME_ISS_SMTC_MASK)
+
+#define ESR_ELx_SME_ISS_SMTC_SME_DISABLED 0
+#define ESR_ELx_SME_ISS_SMTC_ILL 1
+#define ESR_ELx_SME_ISS_SMTC_SM_DISABLED 2
+#define ESR_ELx_SME_ISS_SMTC_ZA_DISABLED 3
+#define ESR_ELx_SME_ISS_SMTC_ZT_DISABLED 4
/* ISS field definitions for MOPS exceptions */
#define ESR_ELx_MOPS_ISS_MEM_INST (UL(1) << 24)
diff --git a/arch/arm64/kernel/fpsimd.c b/arch/arm64/kernel/fpsimd.c
index 8370d55f03533..1ee5f330b8ed3 100644
--- a/arch/arm64/kernel/fpsimd.c
+++ b/arch/arm64/kernel/fpsimd.c
@@ -1436,7 +1436,7 @@ void do_sme_acc(unsigned long esr, struct pt_regs *regs)
* If this not a trap due to SME being disabled then something
* is being used in the wrong mode, report as SIGILL.
*/
- if (ESR_ELx_ISS(esr) != ESR_ELx_SME_ISS_SME_DISABLED) {
+ if (ESR_ELx_SME_ISS_SMTC(esr) != ESR_ELx_SME_ISS_SMTC_SME_DISABLED) {
force_signal_inject(SIGILL, ILL_ILLOPC, regs->pc, 0);
return;
}
--
2.30.2
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH v2 02/13] arm64/fpsimd: Remove unused fpsimd_force_sync_to_sve()
2025-04-09 16:39 [PATCH v2 00/13] arm64: Preparatory FPSIMD/SVE/SME fixes Mark Rutland
2025-04-09 16:39 ` [PATCH v2 01/13] arm64/fpsimd: Avoid RES0 bits in the SME trap handler Mark Rutland
@ 2025-04-09 16:39 ` Mark Rutland
2025-04-09 17:32 ` Mark Brown
2025-04-09 16:40 ` [PATCH v2 03/13] arm64/fpsimd: Remove redundant SVE trap manipulation Mark Rutland
` (11 subsequent siblings)
13 siblings, 1 reply; 18+ messages in thread
From: Mark Rutland @ 2025-04-09 16:39 UTC (permalink / raw)
To: linux-arm-kernel; +Cc: ardb, broonie, catalin.marinas, mark.rutland, maz, will
There have been no users of fpsimd_force_sync_to_sve() since commit:
bbc6172eefdb276b ("arm64/fpsimd: SME no longer requires SVE register state")
Remove fpsimd_force_sync_to_sve().
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Marc Zyngier <maz@kernel.org>
Cc: Mark Brown <broonie@kernel.org>
Cc: Will Deacon <will@kernel.org>
---
arch/arm64/include/asm/fpsimd.h | 1 -
arch/arm64/kernel/fpsimd.c | 14 --------------
2 files changed, 15 deletions(-)
diff --git a/arch/arm64/include/asm/fpsimd.h b/arch/arm64/include/asm/fpsimd.h
index 564bc09b3e06d..9c3e88ec873ab 100644
--- a/arch/arm64/include/asm/fpsimd.h
+++ b/arch/arm64/include/asm/fpsimd.h
@@ -196,7 +196,6 @@ struct vl_info {
extern void sve_alloc(struct task_struct *task, bool flush);
extern void fpsimd_release_task(struct task_struct *task);
extern void fpsimd_sync_to_sve(struct task_struct *task);
-extern void fpsimd_force_sync_to_sve(struct task_struct *task);
extern void sve_sync_to_fpsimd(struct task_struct *task);
extern void sve_sync_from_fpsimd_zeropad(struct task_struct *task);
diff --git a/arch/arm64/kernel/fpsimd.c b/arch/arm64/kernel/fpsimd.c
index 1ee5f330b8ed3..1391a491f2226 100644
--- a/arch/arm64/kernel/fpsimd.c
+++ b/arch/arm64/kernel/fpsimd.c
@@ -758,20 +758,6 @@ void sve_alloc(struct task_struct *task, bool flush)
kzalloc(sve_state_size(task), GFP_KERNEL);
}
-
-/*
- * Force the FPSIMD state shared with SVE to be updated in the SVE state
- * even if the SVE state is the current active state.
- *
- * This should only be called by ptrace. task must be non-runnable.
- * task->thread.sve_state must point to at least sve_state_size(task)
- * bytes of allocated kernel memory.
- */
-void fpsimd_force_sync_to_sve(struct task_struct *task)
-{
- fpsimd_to_sve(task);
-}
-
/*
* Ensure that task->thread.sve_state is up to date with respect to
* the user task, irrespective of when SVE is in use or not.
--
2.30.2
^ permalink raw reply related [flat|nested] 18+ messages in thread* Re: [PATCH v2 02/13] arm64/fpsimd: Remove unused fpsimd_force_sync_to_sve()
2025-04-09 16:39 ` [PATCH v2 02/13] arm64/fpsimd: Remove unused fpsimd_force_sync_to_sve() Mark Rutland
@ 2025-04-09 17:32 ` Mark Brown
0 siblings, 0 replies; 18+ messages in thread
From: Mark Brown @ 2025-04-09 17:32 UTC (permalink / raw)
To: Mark Rutland; +Cc: linux-arm-kernel, ardb, catalin.marinas, maz, will
[-- Attachment #1: Type: text/plain, Size: 311 bytes --]
On Wed, Apr 09, 2025 at 05:39:59PM +0100, Mark Rutland wrote:
> There have been no users of fpsimd_force_sync_to_sve() since commit:
>
> bbc6172eefdb276b ("arm64/fpsimd: SME no longer requires SVE register state")
>
> Remove fpsimd_force_sync_to_sve().
Reviewed-by: Mark Brown <broonie@kernel.org>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH v2 03/13] arm64/fpsimd: Remove redundant SVE trap manipulation
2025-04-09 16:39 [PATCH v2 00/13] arm64: Preparatory FPSIMD/SVE/SME fixes Mark Rutland
2025-04-09 16:39 ` [PATCH v2 01/13] arm64/fpsimd: Avoid RES0 bits in the SME trap handler Mark Rutland
2025-04-09 16:39 ` [PATCH v2 02/13] arm64/fpsimd: Remove unused fpsimd_force_sync_to_sve() Mark Rutland
@ 2025-04-09 16:40 ` Mark Rutland
2025-04-09 16:40 ` [PATCH v2 04/13] arm64/fpsimd: Remove opportunistic freeing of SME state Mark Rutland
` (10 subsequent siblings)
13 siblings, 0 replies; 18+ messages in thread
From: Mark Rutland @ 2025-04-09 16:40 UTC (permalink / raw)
To: linux-arm-kernel; +Cc: ardb, broonie, catalin.marinas, mark.rutland, maz, will
When task_fpsimd_load() loads the saved FPSIMD/SVE/SME state, it
configures EL0 SVE traps by calling sve_user_{enable,disable}(). This is
unnecessary, and this is suspicious/confusing as task_fpsimd_load() does
not configure EL0 SME traps.
All calls to task_fpsimd_load() are followed by a call to
fpsimd_bind_task_to_cpu(), where the latter configures traps for SVE and
SME dependent upon the current values of TIF_SVE and TIF_SME, overriding
any trap configuration performed by task_fpsimd_load().
The calls to sve_user_{enable,disable}() calls in task_fpsimd_load()
have been redundant (though benign) since they were introduced in
commit:
a0136be443d51803 ("arm64/fpsimd: Load FP state based on recorded data type")
Remove the unnecessary and confusing SVE trap manipulation.
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Marc Zyngier <maz@kernel.org>
Cc: Mark Brown <broonie@kernel.org>
Cc: Will Deacon <will@kernel.org>
Reviewed-by: Mark Brown <broonie@kernel.org>
---
arch/arm64/kernel/fpsimd.c | 8 +++-----
1 file changed, 3 insertions(+), 5 deletions(-)
diff --git a/arch/arm64/kernel/fpsimd.c b/arch/arm64/kernel/fpsimd.c
index 1391a491f2226..757445d72e5b7 100644
--- a/arch/arm64/kernel/fpsimd.c
+++ b/arch/arm64/kernel/fpsimd.c
@@ -366,13 +366,11 @@ static void task_fpsimd_load(void)
switch (current->thread.fp_type) {
case FP_STATE_FPSIMD:
/* Stop tracking SVE for this task until next use. */
- if (test_and_clear_thread_flag(TIF_SVE))
- sve_user_disable();
+ clear_thread_flag(TIF_SVE);
break;
case FP_STATE_SVE:
- if (!thread_sm_enabled(¤t->thread) &&
- !WARN_ON_ONCE(!test_and_set_thread_flag(TIF_SVE)))
- sve_user_enable();
+ if (!thread_sm_enabled(¤t->thread))
+ WARN_ON_ONCE(!test_and_set_thread_flag(TIF_SVE));
if (test_thread_flag(TIF_SVE))
sve_set_vq(sve_vq_from_vl(task_get_sve_vl(current)) - 1);
--
2.30.2
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH v2 04/13] arm64/fpsimd: Remove opportunistic freeing of SME state
2025-04-09 16:39 [PATCH v2 00/13] arm64: Preparatory FPSIMD/SVE/SME fixes Mark Rutland
` (2 preceding siblings ...)
2025-04-09 16:40 ` [PATCH v2 03/13] arm64/fpsimd: Remove redundant SVE trap manipulation Mark Rutland
@ 2025-04-09 16:40 ` Mark Rutland
2025-04-09 16:40 ` [PATCH v2 05/13] arm64/fpsimd: Discard stale CPU state when handling SME traps Mark Rutland
` (9 subsequent siblings)
13 siblings, 0 replies; 18+ messages in thread
From: Mark Rutland @ 2025-04-09 16:40 UTC (permalink / raw)
To: linux-arm-kernel; +Cc: ardb, broonie, catalin.marinas, mark.rutland, maz, will
When a task's SVE vector length (NSVL) is changed, and the task happens
to have SVCR.{SM,ZA}=={0,0}, vec_set_vector_length() opportunistically
frees the task's sme_state and clears TIF_SME.
The opportunistic freeing was added with no rationale in commit:
d4d5be94a8787242 ("arm64/fpsimd: Ensure SME storage is allocated after SVE VL changes")
That commit fixed an unrelated problem where the task's sve_state was
freed while it could be used to store streaming mode register state,
where the fix was to re-allocate the task's sve_state.
There is no need to free and/or reallocate the task's sme_state when the
SVE vector length changes, and there is no need to clear TIF_SME. Given
the SME vector length (SVL) doesn't change, the task's sme_state remains
correctly sized.
Remove the unnecessary opportunistic freeing of the task's sme_state
when the task's SVE vector length is changed. The task's sme_state is
still freed when the SME vector length is changed.
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Marc Zyngier <maz@kernel.org>
Cc: Mark Brown <broonie@kernel.org>
Cc: Will Deacon <will@kernel.org>
Reviewed-by: Mark Brown <broonie@kernel.org>
---
arch/arm64/kernel/fpsimd.c | 17 ++++-------------
1 file changed, 4 insertions(+), 13 deletions(-)
diff --git a/arch/arm64/kernel/fpsimd.c b/arch/arm64/kernel/fpsimd.c
index 757445d72e5b7..128774015772a 100644
--- a/arch/arm64/kernel/fpsimd.c
+++ b/arch/arm64/kernel/fpsimd.c
@@ -868,19 +868,10 @@ int vec_set_vector_length(struct task_struct *task, enum vec_type type,
task->thread.fp_type = FP_STATE_FPSIMD;
}
- if (system_supports_sme()) {
- if (type == ARM64_VEC_SME ||
- !(task->thread.svcr & (SVCR_SM_MASK | SVCR_ZA_MASK))) {
- /*
- * We are changing the SME VL or weren't using
- * SME anyway, discard the state and force a
- * reallocation.
- */
- task->thread.svcr &= ~(SVCR_SM_MASK |
- SVCR_ZA_MASK);
- clear_tsk_thread_flag(task, TIF_SME);
- free_sme = true;
- }
+ if (system_supports_sme() && type == ARM64_VEC_SME) {
+ task->thread.svcr &= ~(SVCR_SM_MASK | SVCR_ZA_MASK);
+ clear_tsk_thread_flag(task, TIF_SME);
+ free_sme = true;
}
if (task == current)
--
2.30.2
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH v2 05/13] arm64/fpsimd: Discard stale CPU state when handling SME traps
2025-04-09 16:39 [PATCH v2 00/13] arm64: Preparatory FPSIMD/SVE/SME fixes Mark Rutland
` (3 preceding siblings ...)
2025-04-09 16:40 ` [PATCH v2 04/13] arm64/fpsimd: Remove opportunistic freeing of SME state Mark Rutland
@ 2025-04-09 16:40 ` Mark Rutland
2025-04-09 16:40 ` [PATCH v2 06/13] arm64/fpsimd: Don't corrupt FPMR when streaming mode changes Mark Rutland
` (8 subsequent siblings)
13 siblings, 0 replies; 18+ messages in thread
From: Mark Rutland @ 2025-04-09 16:40 UTC (permalink / raw)
To: linux-arm-kernel; +Cc: ardb, broonie, catalin.marinas, mark.rutland, maz, will
From: Mark Brown <broonie@kernel.org>
The logic for handling SME traps manipulates saved FPSIMD/SVE/SME state
incorrectly, and a race with preemption can result in a task having
TIF_SME set and TIF_FOREIGN_FPSTATE clear even though the live CPU state
is stale (e.g. with SME traps enabled). This can result in warnings from
do_sme_acc() where SME traps are not expected while TIF_SME is set:
| /* With TIF_SME userspace shouldn't generate any traps */
| if (test_and_set_thread_flag(TIF_SME))
| WARN_ON(1);
This is very similar to the SVE issue we fixed in commit:
751ecf6afd6568ad ("arm64/sve: Discard stale CPU state when handling SVE traps")
The race can occur when the SME trap handler is preempted before and
after manipulating the saved FPSIMD/SVE/SME state, starting and ending on
the same CPU, e.g.
| void do_sme_acc(unsigned long esr, struct pt_regs *regs)
| {
| // Trap on CPU 0 with TIF_SME clear, SME traps enabled
| // task->fpsimd_cpu is 0.
| // per_cpu_ptr(&fpsimd_last_state, 0) is task.
|
| ...
|
| // Preempted; migrated from CPU 0 to CPU 1.
| // TIF_FOREIGN_FPSTATE is set.
|
| get_cpu_fpsimd_context();
|
| /* With TIF_SME userspace shouldn't generate any traps */
| if (test_and_set_thread_flag(TIF_SME))
| WARN_ON(1);
|
| if (!test_thread_flag(TIF_FOREIGN_FPSTATE)) {
| unsigned long vq_minus_one =
| sve_vq_from_vl(task_get_sme_vl(current)) - 1;
| sme_set_vq(vq_minus_one);
|
| fpsimd_bind_task_to_cpu();
| }
|
| put_cpu_fpsimd_context();
|
| // Preempted; migrated from CPU 1 to CPU 0.
| // task->fpsimd_cpu is still 0
| // If per_cpu_ptr(&fpsimd_last_state, 0) is still task then:
| // - Stale HW state is reused (with SME traps enabled)
| // - TIF_FOREIGN_FPSTATE is cleared
| // - A return to userspace skips HW state restore
| }
Fix the case where the state is not live and TIF_FOREIGN_FPSTATE is set
by calling fpsimd_flush_task_state() to detach from the saved CPU
state. This ensures that a subsequent context switch will not reuse the
stale CPU state, and will instead set TIF_FOREIGN_FPSTATE, forcing the
new state to be reloaded from memory prior to a return to userspace.
Note: this was originallly posted as [1].
Fixes: 8bd7f91c03d8 ("arm64/sme: Implement traps and syscall handling for SME")
Reported-by: Mark Rutland <mark.rutland@arm.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
Link: https://lore.kernel.org/linux-arm-kernel/20241204-arm64-sme-reenable-v2-1-bae87728251d@kernel.org/ # [1]
[ Rutland: rewrite commit message ]
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Marc Zyngier <maz@kernel.org>
Cc: Will Deacon <will@kernel.org>
---
arch/arm64/kernel/fpsimd.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/arch/arm64/kernel/fpsimd.c b/arch/arm64/kernel/fpsimd.c
index 128774015772a..b4858d85292b4 100644
--- a/arch/arm64/kernel/fpsimd.c
+++ b/arch/arm64/kernel/fpsimd.c
@@ -1435,6 +1435,8 @@ void do_sme_acc(unsigned long esr, struct pt_regs *regs)
sme_set_vq(vq_minus_one);
fpsimd_bind_task_to_cpu();
+ } else {
+ fpsimd_flush_task_state(current);
}
put_cpu_fpsimd_context();
--
2.30.2
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH v2 06/13] arm64/fpsimd: Don't corrupt FPMR when streaming mode changes
2025-04-09 16:39 [PATCH v2 00/13] arm64: Preparatory FPSIMD/SVE/SME fixes Mark Rutland
` (4 preceding siblings ...)
2025-04-09 16:40 ` [PATCH v2 05/13] arm64/fpsimd: Discard stale CPU state when handling SME traps Mark Rutland
@ 2025-04-09 16:40 ` Mark Rutland
2025-04-09 16:40 ` [PATCH v2 07/13] arm64/fpsimd: Avoid clobbering kernel FPSIMD state with SMSTOP Mark Rutland
` (7 subsequent siblings)
13 siblings, 0 replies; 18+ messages in thread
From: Mark Rutland @ 2025-04-09 16:40 UTC (permalink / raw)
To: linux-arm-kernel; +Cc: ardb, broonie, catalin.marinas, mark.rutland, maz, will
From: Mark Brown <broonie@kernel.org>
When the effective value of PSTATE.SM is changed from 0 to 1 or from 1
to 0 by any method, an entry or exit to/from streaming SVE mode is
performed, and hardware automatically resets a number of registers. As
of ARM DDI 0487 L.a, this means:
* All implemented bits of the SVE vector registers are set to zero.
* All implemented bits of the SVE predicate registers are set to zero.
* All implemented bits of FFR are set to zero, if FFR is implemented in
the new mode.
* FPSR is set to 0x0000_0000_0800_009f.
* FPMR is set to 0, if FPMR is implemented.
Currently task_fpsimd_load() restores FPMR before restoring SVCR (which
is an accessor for PSTATE.{SM,ZA}), and so the restored value of FPMR
may be clobbered if the restored value of PSTATE.SM happens to differ
from the initial value of PSTATE.SM.
Fix this by moving the restore of FPMR later.
Note: this was originally posted as [1].
Fixes: 203f2b95a882 ("arm64/fpsimd: Support FEAT_FPMR")
Signed-off-by: Mark Brown <broonie@kernel.org>
Link: https://lore.kernel.org/linux-arm-kernel/20241204-arm64-sme-reenable-v2-2-bae87728251d@kernel.org/ # [1]
[ Rutland: rewrite commit message ]
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
---
arch/arm64/kernel/fpsimd.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/arch/arm64/kernel/fpsimd.c b/arch/arm64/kernel/fpsimd.c
index b4858d85292b4..b19736f354a71 100644
--- a/arch/arm64/kernel/fpsimd.c
+++ b/arch/arm64/kernel/fpsimd.c
@@ -359,9 +359,6 @@ static void task_fpsimd_load(void)
WARN_ON(preemptible());
WARN_ON(test_thread_flag(TIF_KERNEL_FPSTATE));
- if (system_supports_fpmr())
- write_sysreg_s(current->thread.uw.fpmr, SYS_FPMR);
-
if (system_supports_sve() || system_supports_sme()) {
switch (current->thread.fp_type) {
case FP_STATE_FPSIMD:
@@ -411,6 +408,9 @@ static void task_fpsimd_load(void)
restore_ffr = system_supports_fa64();
}
+ if (system_supports_fpmr())
+ write_sysreg_s(current->thread.uw.fpmr, SYS_FPMR);
+
if (restore_sve_regs) {
WARN_ON_ONCE(current->thread.fp_type != FP_STATE_SVE);
sve_load_state(sve_pffr(¤t->thread),
--
2.30.2
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH v2 07/13] arm64/fpsimd: Avoid clobbering kernel FPSIMD state with SMSTOP
2025-04-09 16:39 [PATCH v2 00/13] arm64: Preparatory FPSIMD/SVE/SME fixes Mark Rutland
` (5 preceding siblings ...)
2025-04-09 16:40 ` [PATCH v2 06/13] arm64/fpsimd: Don't corrupt FPMR when streaming mode changes Mark Rutland
@ 2025-04-09 16:40 ` Mark Rutland
2025-04-09 16:40 ` [PATCH v2 08/13] arm64/fpsimd: Reset FPMR upon exec() Mark Rutland
` (6 subsequent siblings)
13 siblings, 0 replies; 18+ messages in thread
From: Mark Rutland @ 2025-04-09 16:40 UTC (permalink / raw)
To: linux-arm-kernel; +Cc: ardb, broonie, catalin.marinas, mark.rutland, maz, will
On system with SME, a thread's kernel FPSIMD state may be erroneously
clobbered during a context switch immediately after that state is
restored. Systems without SME are unaffected.
If the CPU happens to be in streaming SVE mode before a context switch
to a thread with kernel FPSIMD state, fpsimd_thread_switch() will
restore the kernel FPSIMD state using fpsimd_load_kernel_state() while
the CPU is still in streaming SVE mode. When fpsimd_thread_switch()
subsequently calls fpsimd_flush_cpu_state(), this will execute an
SMSTOP, causing an exit from streaming SVE mode. The exit from
streaming SVE mode will cause the hardware to reset a number of
FPSIMD/SVE/SME registers, clobbering the FPSIMD state.
Fix this by calling fpsimd_flush_cpu_state() before restoring the kernel
FPSIMD state.
Fixes: e92bee9f861b466c ("arm64/fpsimd: Avoid erroneous elide of user state reload")
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Cc: Ard Biesheuvel <ardb@kernel.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Mark Brown <broonie@kernel.org>
Cc: Will Deacon <will@kernel.org>
Reviewed-by: Mark Brown <broonie@kernel.org>
---
arch/arm64/kernel/fpsimd.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm64/kernel/fpsimd.c b/arch/arm64/kernel/fpsimd.c
index b19736f354a71..4a0b0bb3a3fad 100644
--- a/arch/arm64/kernel/fpsimd.c
+++ b/arch/arm64/kernel/fpsimd.c
@@ -1550,8 +1550,8 @@ void fpsimd_thread_switch(struct task_struct *next)
fpsimd_save_user_state();
if (test_tsk_thread_flag(next, TIF_KERNEL_FPSTATE)) {
- fpsimd_load_kernel_state(next);
fpsimd_flush_cpu_state();
+ fpsimd_load_kernel_state(next);
} else {
/*
* Fix up TIF_FOREIGN_FPSTATE to correctly describe next's
--
2.30.2
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH v2 08/13] arm64/fpsimd: Reset FPMR upon exec()
2025-04-09 16:39 [PATCH v2 00/13] arm64: Preparatory FPSIMD/SVE/SME fixes Mark Rutland
` (6 preceding siblings ...)
2025-04-09 16:40 ` [PATCH v2 07/13] arm64/fpsimd: Avoid clobbering kernel FPSIMD state with SMSTOP Mark Rutland
@ 2025-04-09 16:40 ` Mark Rutland
2025-04-09 16:40 ` [PATCH v2 09/13] arm64/fpsimd: Fix merging of FPSIMD state during signal return Mark Rutland
` (5 subsequent siblings)
13 siblings, 0 replies; 18+ messages in thread
From: Mark Rutland @ 2025-04-09 16:40 UTC (permalink / raw)
To: linux-arm-kernel; +Cc: ardb, broonie, catalin.marinas, mark.rutland, maz, will
An exec() is expected to reset all FPSIMD/SVE/SME state, and barring
special handling of the vector lengths, the state is expected to reset
to zero. This reset is handled in fpsimd_flush_thread(), which the core
exec() code calls via flush_thread().
When support was added for FPMR, no logic was added to
fpsimd_flush_thread() to reset the FPMR value, and thus it is
erroneously inherited across an exec().
Add the missing reset of FPMR.
Fixes: 203f2b95a882dc46 ("arm64/fpsimd: Support FEAT_FPMR")
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Marc Zyngier <maz@kernel.org>
Cc: Mark Brown <broonie@kernel.org>
Cc: Will Deacon <will@kernel.org>
Reviewed-by: Mark Brown <broonie@kernel.org>
---
arch/arm64/kernel/fpsimd.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/arch/arm64/kernel/fpsimd.c b/arch/arm64/kernel/fpsimd.c
index 4a0b0bb3a3fad..0b6fda5b7bad5 100644
--- a/arch/arm64/kernel/fpsimd.c
+++ b/arch/arm64/kernel/fpsimd.c
@@ -1638,6 +1638,9 @@ void fpsimd_flush_thread(void)
current->thread.svcr = 0;
}
+ if (system_supports_fpmr())
+ current->thread.uw.fpmr = 0;
+
current->thread.fp_type = FP_STATE_FPSIMD;
put_cpu_fpsimd_context();
--
2.30.2
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH v2 09/13] arm64/fpsimd: Fix merging of FPSIMD state during signal return
2025-04-09 16:39 [PATCH v2 00/13] arm64: Preparatory FPSIMD/SVE/SME fixes Mark Rutland
` (7 preceding siblings ...)
2025-04-09 16:40 ` [PATCH v2 08/13] arm64/fpsimd: Reset FPMR upon exec() Mark Rutland
@ 2025-04-09 16:40 ` Mark Rutland
2025-04-09 16:40 ` [PATCH v2 10/13] arm64/fpsimd: Add fpsimd_save_and_flush_current_state() Mark Rutland
` (4 subsequent siblings)
13 siblings, 0 replies; 18+ messages in thread
From: Mark Rutland @ 2025-04-09 16:40 UTC (permalink / raw)
To: linux-arm-kernel; +Cc: ardb, broonie, catalin.marinas, mark.rutland, maz, will
For backwards compatibility reasons, when a signal return occurs which
restores SVE state, the effective lower 128 bits of each of the SVE
vector registers are restored from the corresponding FPSIMD vector
register in the FPSIMD signal frame, overriding the values in the SVE
signal frame. This is intended to be the case regardless of streaming
mode.
To make this happen, restore_sve_fpsimd_context() uses
fpsimd_update_current_state() to merge the lower 128 bits from the
FPSIMD signal frame into the SVE register state. Unfortunately,
fpsimd_update_current_state() performs this merging dependent upon
TIF_SVE, which is not always correct for streaming SVE register state:
* When restoring non-streaming SVE register state there is no observable
problem, as the signal return code configures TIF_SVE and the saved
fp_type to match before calling fpsimd_update_current_state(), which
observes either:
- TIF_SVE set AND fp_type == FP_STATE_SVE
- TIF_SVE clear AND fp_type == FP_STATE_FPSIMD
* On systems which have SME but not SVE, TIF_SVE cannot be set. Thus the
merging will never happen for the streaming SVE register state.
* On systems which have SVE and SME, TIF_SVE can be set and cleared
independently of PSTATE.SM. Thus the merging may or may not happen for
streaming SVE register state.
As TIF_SVE can be cleared non-deterministically during syscalls
(including at the start of sigreturn()), the merging may occur
non-deterministically from the perspective of userspace.
This logic has been broken since its introduction in commit:
85ed24dad2904f7c ("arm64/sme: Implement streaming SVE signal handling")
... at which point both fpsimd_signal_preserve_current_state() and
fpsimd_update_current_state() only checked TIF SVE. When PSTATE.SM==1
and TIF_SVE was clear, signal delivery would place stale FPSIMD state
into the FPSIMD signal frame, and signal return would not merge this
into the restored register state.
Subsequently, signal delivery was fixed as part of commit:
61da7c8e2a602f66 ("arm64/signal: Don't assume that TIF_SVE means we saved SVE state")
... but signal restore was not given a corresponding fix, and when
TIF_SVE was clear, signal restore would still fail to merge the FPSIMD
state into the restored SVE register state. The 'Fixes' tag did not
indicate that this had been broken since its introduction.
Fix this by merging the FPSIMD state dependent upon the saved fp_type,
matching what we (currently) do during signal delivery.
As described above, when backporting this commit, it will also be
necessary to backport commit:
61da7c8e2a602f66 ("arm64/signal: Don't assume that TIF_SVE means we saved SVE state")
... and prior to commit:
baa8515281b30861 ("arm64/fpsimd: Track the saved FPSIMD state type separately to TIF_SVE")
... it will be necessary for fpsimd_signal_preserve_current_state() and
fpsimd_update_current_state() to consider both TIF_SVE and
thread_sm_enabled(¤t->thread), in place of the saved fp_type.
Fixes: 85ed24dad2904f7c ("arm64/sme: Implement streaming SVE signal handling")
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Marc Zyngier <maz@kernel.org>
Cc: Mark Brown <broonie@kernel.org>
Cc: Will Deacon <will@kernel.org>
Reviewed-by: Mark Brown <broonie@kernel.org>
---
arch/arm64/kernel/fpsimd.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm64/kernel/fpsimd.c b/arch/arm64/kernel/fpsimd.c
index 0b6fda5b7bad5..11f21809d3b7d 100644
--- a/arch/arm64/kernel/fpsimd.c
+++ b/arch/arm64/kernel/fpsimd.c
@@ -1781,7 +1781,7 @@ void fpsimd_update_current_state(struct user_fpsimd_state const *state)
get_cpu_fpsimd_context();
current->thread.uw.fpsimd_state = *state;
- if (test_thread_flag(TIF_SVE))
+ if (current->thread.fp_type == FP_STATE_SVE)
fpsimd_to_sve(current);
task_fpsimd_load();
--
2.30.2
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH v2 10/13] arm64/fpsimd: Add fpsimd_save_and_flush_current_state()
2025-04-09 16:39 [PATCH v2 00/13] arm64: Preparatory FPSIMD/SVE/SME fixes Mark Rutland
` (8 preceding siblings ...)
2025-04-09 16:40 ` [PATCH v2 09/13] arm64/fpsimd: Fix merging of FPSIMD state during signal return Mark Rutland
@ 2025-04-09 16:40 ` Mark Rutland
2025-04-09 16:40 ` [PATCH v2 11/13] arm64/fpsimd: signal32: Always save+flush state early Mark Rutland
` (3 subsequent siblings)
13 siblings, 0 replies; 18+ messages in thread
From: Mark Rutland @ 2025-04-09 16:40 UTC (permalink / raw)
To: linux-arm-kernel; +Cc: ardb, broonie, catalin.marinas, mark.rutland, maz, will
When the current task's FPSIMD/SVE/SME state may be live on *any* CPU in
the system, special care must be taken when manipulating that state, as
this manipulation can race with preemption and/or asynchronous usage of
FPSIMD/SVE/SME (e.g. kernel-mode NEON in softirq handlers).
Even when manipulation is is protected with get_cpu_fpsimd_context() and
get_cpu_fpsimd_context(), the logic necessary when the state is live on
the current CPU can be wildly different from the logic necessary when
the state is not live on the current CPU. A number of historical and
extant issues result from failing to handle these cases consistetntly
and/or correctly.
To make it easier to get such manipulation correct, add a new
fpsimd_save_and_flush_current_state() helper function, which ensures
that the current task's state has been saved to memory and any stale
state on any CPU has been "flushed" such that is not live on any CPU in
the system. This will allow code to safely manipulate the saved state
without risk of races.
Subsequent patches will use the new function.
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Marc Zyngier <maz@kernel.org>
Cc: Mark Brown <broonie@kernel.org>
Cc: Will Deacon <will@kernel.org>
Reviewed-by: Mark Brown <broonie@kernel.org>
---
arch/arm64/include/asm/fpsimd.h | 1 +
arch/arm64/kernel/fpsimd.c | 11 +++++++++++
2 files changed, 12 insertions(+)
diff --git a/arch/arm64/include/asm/fpsimd.h b/arch/arm64/include/asm/fpsimd.h
index 9c3e88ec873ab..1a18f851b6144 100644
--- a/arch/arm64/include/asm/fpsimd.h
+++ b/arch/arm64/include/asm/fpsimd.h
@@ -96,6 +96,7 @@ struct cpu_fp_state {
extern void fpsimd_bind_state_to_cpu(struct cpu_fp_state *fp_state);
extern void fpsimd_flush_task_state(struct task_struct *target);
+extern void fpsimd_save_and_flush_current_state(void);
extern void fpsimd_save_and_flush_cpu_state(void);
static inline bool thread_sm_enabled(struct thread_struct *thread)
diff --git a/arch/arm64/kernel/fpsimd.c b/arch/arm64/kernel/fpsimd.c
index 11f21809d3b7d..ea07c4577f17e 100644
--- a/arch/arm64/kernel/fpsimd.c
+++ b/arch/arm64/kernel/fpsimd.c
@@ -1819,6 +1819,17 @@ void fpsimd_flush_task_state(struct task_struct *t)
barrier();
}
+void fpsimd_save_and_flush_current_state(void)
+{
+ if (!system_supports_fpsimd())
+ return;
+
+ get_cpu_fpsimd_context();
+ fpsimd_save_user_state();
+ fpsimd_flush_task_state(current);
+ put_cpu_fpsimd_context();
+}
+
/*
* Save the FPSIMD state to memory and invalidate cpu view.
* This function must be called with preemption disabled.
--
2.30.2
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH v2 11/13] arm64/fpsimd: signal32: Always save+flush state early
2025-04-09 16:39 [PATCH v2 00/13] arm64: Preparatory FPSIMD/SVE/SME fixes Mark Rutland
` (9 preceding siblings ...)
2025-04-09 16:40 ` [PATCH v2 10/13] arm64/fpsimd: Add fpsimd_save_and_flush_current_state() Mark Rutland
@ 2025-04-09 16:40 ` Mark Rutland
2025-04-09 16:40 ` [PATCH v2 12/13] arm64/fpsimd: signal: " Mark Rutland
` (2 subsequent siblings)
13 siblings, 0 replies; 18+ messages in thread
From: Mark Rutland @ 2025-04-09 16:40 UTC (permalink / raw)
To: linux-arm-kernel; +Cc: ardb, broonie, catalin.marinas, mark.rutland, maz, will
There are several issues with the way the native signal handling code
manipulates FPSIMD/SVE/SME state. To fix those issues, subsequent
patches will rework the native signal handling code to always save+flush
the current task's FPSIMD/SVE/SME state before manipulating that state.
In preparation for those changes, rework the compat signal handling code
to save+flush the current task's FPSIMD state before manipulating it.
Subsequent patches will remove fpsimd_signal_preserve_current_state()
and fpsimd_update_current_state(). Compat tasks can only have FPSIMD
state, and cannot have any SVE or SME state. Thus, the SVE state
manipulation present in fpsimd_signal_preserve_current_state() and
fpsimd_update_current_state() is not necessary, and it is safe to
directly manipulate current->thread.uw.fpsimd_state once it has been
saved+flushed.
Use fpsimd_save_and_flush_current_state() to save+flush the state for
both signal delivery and signal return, before the state is manipulated
in any way. While it would be safe for compat_restore_vfp_context() to
use fpsimd_flush_task_state(current), there are several extant issues in
the native signal code resulting from incorrect use of
fpsimd_flush_task_state(), and for consistency it is preferable to use
fpsimd_save_and_flush_current_state().
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Marc Zyngier <maz@kernel.org>
Cc: Mark Brown <broonie@kernel.org>
Cc: Will Deacon <will@kernel.org>
Reviewed-by: Mark Brown <broonie@kernel.org>
---
arch/arm64/kernel/signal32.c | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)
diff --git a/arch/arm64/kernel/signal32.c b/arch/arm64/kernel/signal32.c
index 81e798b6dadaf..bb3b526ff43f7 100644
--- a/arch/arm64/kernel/signal32.c
+++ b/arch/arm64/kernel/signal32.c
@@ -103,7 +103,7 @@ static int compat_preserve_vfp_context(struct compat_vfp_sigframe __user *frame)
* Note that this also saves V16-31, which aren't visible
* in AArch32.
*/
- fpsimd_signal_preserve_current_state();
+ fpsimd_save_and_flush_current_state();
/* Place structure header on the stack */
__put_user_error(magic, &frame->magic, err);
@@ -169,14 +169,17 @@ static int compat_restore_vfp_context(struct compat_vfp_sigframe __user *frame)
fpsimd.fpsr = fpscr & VFP_FPSCR_STAT_MASK;
fpsimd.fpcr = fpscr & VFP_FPSCR_CTRL_MASK;
+ if (err)
+ return -EFAULT;
+
/*
* We don't need to touch the exception register, so
* reload the hardware state.
*/
- if (!err)
- fpsimd_update_current_state(&fpsimd);
+ fpsimd_save_and_flush_current_state();
+ current->thread.uw.fpsimd_state = fpsimd;
- return err ? -EFAULT : 0;
+ return 0;
}
static int compat_restore_sigframe(struct pt_regs *regs,
--
2.30.2
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH v2 12/13] arm64/fpsimd: signal: Always save+flush state early
2025-04-09 16:39 [PATCH v2 00/13] arm64: Preparatory FPSIMD/SVE/SME fixes Mark Rutland
` (10 preceding siblings ...)
2025-04-09 16:40 ` [PATCH v2 11/13] arm64/fpsimd: signal32: Always save+flush state early Mark Rutland
@ 2025-04-09 16:40 ` Mark Rutland
2025-04-09 16:40 ` [PATCH v2 13/13] arm64/fpsimd: signal: Simplify preserve_tpidr2_context() Mark Rutland
2025-04-09 17:17 ` [PATCH v2 00/13] arm64: Preparatory FPSIMD/SVE/SME fixes Catalin Marinas
13 siblings, 0 replies; 18+ messages in thread
From: Mark Rutland @ 2025-04-09 16:40 UTC (permalink / raw)
To: linux-arm-kernel; +Cc: ardb, broonie, catalin.marinas, mark.rutland, maz, will
There are several issues with the way the native signal handling code
manipulates FPSIMD/SVE/SME state, described in detail below. These
issues largely result from races with preemption and inconsistent
handling of live state vs saved state.
Known issues with native FPSIMD/SVE/SME state management include:
* On systems with FPMR, the code to save/restore the FPMR accesses the
register while it is not owned by the current task. Consequently, this
may corrupt the FPMR of the current task and/or may corrupt the FPMR
of an unrelated task. The FPMR save/restore has been broken since it
was introduced in commit:
8c46def44409fc91 ("arm64/signal: Add FPMR signal handling")
* On systems with SME, setup_return() modifies both the live register
state and the saved state register state regardless of whether the
task's state is live, and without holding the cpu fpsimd context.
Consequently:
- This may corrupt the state an unrelated task which has PSTATE.SM set
and/or PSTATE.ZA set.
- The task may enter the signal handler in streaming mode, and or with
ZA storage enabled unexpectedly.
- The task may enter the signal handler in non-streaming SVE mode with
stale SVE register state, which may have been inherited from
streaming SVE mode unexpectedly. Where the streaming and
non-streaming vector lengths differ, this may be packed into
registers arbitrarily.
This logic has been broken since it was introduced in commit:
40a8e87bb32855b3 ("arm64/sme: Disable ZA and streaming mode when handling signals")
Further incorrect manipulation of state was added in commits:
ea64baacbc36a0d5 ("arm64/signal: Flush FPSIMD register state when disabling streaming mode")
baa8515281b30861 ("arm64/fpsimd: Track the saved FPSIMD state type separately to TIF_SVE")
* Several restoration functions use fpsimd_flush_task_state() to discard
the live FPSIMD/SVE/SME while the in-memory copy is stale.
When a subset of the FPSIMD/SVE/SME state is restored, the remainder
may be non-deterministically reset to a stale snapshot from some
arbitrary point in the past.
This non-deterministic discarding was introduced in commit:
8cd969d28fd2848d ("arm64/sve: Signal handling support")
As of that commit, when TIF_SVE was initially clear, failure to
restore the SVE signal frame could reset the FPSIMD registers to a
stale snapshot.
The pattern of discarding unsaved state was subsequently copied into
restoration functions for some new state in commits:
39782210eb7e8763 ("arm64/sme: Implement ZA signal handling")
ee072cf708048c0d ("arm64/sme: Implement signal handling for ZT")
* On systems with SME/SME2, the entire FPSIMD/SVE/SME state may be
loaded onto the CPU redundantly. Either restore_fpsimd_context() or
restore_sve_fpsimd_context() will load the entire FPSIMD/SVE/SME state
via fpsimd_update_current_state() before restore_za_context() and
restore_zt_context() each discard the state via
fpsimd_flush_task_state().
This is purely redundant work, and not a functional bug.
To fix these issues, rework the native signal handling code to always
save+flush the current task's FPSIMD/SVE/SME state before manipulating
that state. This avoids races with preemption and ensures that state is
manipulated consistently regardless of whether it happened to be live
prior to manipulation. This largely involes:
* Using fpsimd_save_and_flush_current_state() to save+flush the state
for both signal delivery and signal return, before the state is
manipulated in any way.
* Removing fpsimd_signal_preserve_current_state() and updating
preserve_fpsimd_context() to explicitly ensure that the FPSIMD state
is up-to-date, as preserve_fpsimd_context() is the only consumer of
the FPSIMD state during signal delivery.
* Modifying fpsimd_update_current_state() to not reload the FPSIMD state
onto the CPU. Ideally we'd remove fpsimd_update_current_state()
entirely, but I've left that for subsequent patches as there are a
number of of other problems with the FPSIMD<->SVE conversion helpers
that should be addressed at the same time. For now I've removed the
misleading comment.
For setup_return(), we need to decide (for ABI reasons) whether signal
delivery should have all the side-effects of an SMSTOP. For now I've
left a TODO comment, as there are other questions in this area that I'll
address with subsequent patches.
Fixes: 8c46def44409fc91 ("arm64/signal: Add FPMR signal handling")
Fixes: 40a8e87bb32855b3 ("arm64/sme: Disable ZA and streaming mode when handling signals")
Fixes: ea64baacbc36a0d5 ("arm64/signal: Flush FPSIMD register state when disabling streaming mode")
Fixes: baa8515281b30861 ("arm64/fpsimd: Track the saved FPSIMD state type separately to TIF_SVE")
Fixes: 8cd969d28fd2848d ("arm64/sve: Signal handling support")
Fixes: 39782210eb7e8763 ("arm64/sme: Implement ZA signal handling")
Fixes: ee072cf708048c0d ("arm64/sme: Implement signal handling for ZT")
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Marc Zyngier <maz@kernel.org>
Cc: Mark Brown <broonie@kernel.org>
Cc: Will Deacon <will@kernel.org>
Reviewed-by: Mark Brown <broonie@kernel.org>
---
arch/arm64/include/asm/fpsimd.h | 1 -
arch/arm64/kernel/fpsimd.c | 28 --------------
arch/arm64/kernel/signal.c | 66 ++++++---------------------------
3 files changed, 12 insertions(+), 83 deletions(-)
diff --git a/arch/arm64/include/asm/fpsimd.h b/arch/arm64/include/asm/fpsimd.h
index 1a18f851b6144..b7adb2c72204b 100644
--- a/arch/arm64/include/asm/fpsimd.h
+++ b/arch/arm64/include/asm/fpsimd.h
@@ -76,7 +76,6 @@ extern void fpsimd_load_state(struct user_fpsimd_state *state);
extern void fpsimd_thread_switch(struct task_struct *next);
extern void fpsimd_flush_thread(void);
-extern void fpsimd_signal_preserve_current_state(void);
extern void fpsimd_preserve_current_state(void);
extern void fpsimd_restore_current_state(void);
extern void fpsimd_update_current_state(struct user_fpsimd_state const *state);
diff --git a/arch/arm64/kernel/fpsimd.c b/arch/arm64/kernel/fpsimd.c
index ea07c4577f17e..b0874402f7ecc 100644
--- a/arch/arm64/kernel/fpsimd.c
+++ b/arch/arm64/kernel/fpsimd.c
@@ -1662,18 +1662,6 @@ void fpsimd_preserve_current_state(void)
put_cpu_fpsimd_context();
}
-/*
- * Like fpsimd_preserve_current_state(), but ensure that
- * current->thread.uw.fpsimd_state is updated so that it can be copied to
- * the signal frame.
- */
-void fpsimd_signal_preserve_current_state(void)
-{
- fpsimd_preserve_current_state();
- if (current->thread.fp_type == FP_STATE_SVE)
- sve_to_fpsimd(current);
-}
-
/*
* Associate current's FPSIMD context with this cpu
* The caller must have ownership of the cpu FPSIMD context before calling
@@ -1766,30 +1754,14 @@ void fpsimd_restore_current_state(void)
put_cpu_fpsimd_context();
}
-/*
- * Load an updated userland FPSIMD state for 'current' from memory and set the
- * flag that indicates that the FPSIMD register contents are the most recent
- * FPSIMD state of 'current'. This is used by the signal code to restore the
- * register state when returning from a signal handler in FPSIMD only cases,
- * any SVE context will be discarded.
- */
void fpsimd_update_current_state(struct user_fpsimd_state const *state)
{
if (WARN_ON(!system_supports_fpsimd()))
return;
- get_cpu_fpsimd_context();
-
current->thread.uw.fpsimd_state = *state;
if (current->thread.fp_type == FP_STATE_SVE)
fpsimd_to_sve(current);
-
- task_fpsimd_load();
- fpsimd_bind_task_to_cpu();
-
- clear_thread_flag(TIF_FOREIGN_FPSTATE);
-
- put_cpu_fpsimd_context();
}
/*
diff --git a/arch/arm64/kernel/signal.c b/arch/arm64/kernel/signal.c
index a7c37afb4ebeb..0de9c452c6c0e 100644
--- a/arch/arm64/kernel/signal.c
+++ b/arch/arm64/kernel/signal.c
@@ -250,6 +250,8 @@ static int preserve_fpsimd_context(struct fpsimd_context __user *ctx)
¤t->thread.uw.fpsimd_state;
int err;
+ sve_sync_to_fpsimd(current);
+
/* copy the FP and status/control registers */
err = __copy_to_user(ctx->vregs, fpsimd->vregs, sizeof(fpsimd->vregs));
__put_user_error(fpsimd->fpsr, &ctx->fpsr, err);
@@ -291,8 +293,6 @@ static int preserve_fpmr_context(struct fpmr_context __user *ctx)
{
int err = 0;
- current->thread.uw.fpmr = read_sysreg_s(SYS_FPMR);
-
__put_user_error(FPMR_MAGIC, &ctx->head.magic, err);
__put_user_error(sizeof(*ctx), &ctx->head.size, err);
__put_user_error(current->thread.uw.fpmr, &ctx->fpmr, err);
@@ -310,7 +310,7 @@ static int restore_fpmr_context(struct user_ctxs *user)
__get_user_error(fpmr, &user->fpmr->fpmr, err);
if (!err)
- write_sysreg_s(fpmr, SYS_FPMR);
+ current->thread.uw.fpmr = fpmr;
return err;
}
@@ -372,11 +372,6 @@ static int preserve_sve_context(struct sve_context __user *ctx)
err |= __copy_to_user(&ctx->__reserved, reserved, sizeof(reserved));
if (vq) {
- /*
- * This assumes that the SVE state has already been saved to
- * the task struct by calling the function
- * fpsimd_signal_preserve_current_state().
- */
err |= __copy_to_user((char __user *)ctx + SVE_SIG_REGS_OFFSET,
current->thread.sve_state,
SVE_SIG_REGS_SIZE(vq));
@@ -432,16 +427,6 @@ static int restore_sve_fpsimd_context(struct user_ctxs *user)
if (user->sve_size < SVE_SIG_CONTEXT_SIZE(vq))
return -EINVAL;
- /*
- * Careful: we are about __copy_from_user() directly into
- * thread.sve_state with preemption enabled, so protection is
- * needed to prevent a racing context switch from writing stale
- * registers back over the new data.
- */
-
- fpsimd_flush_task_state(current);
- /* From now, fpsimd_thread_switch() won't touch thread.sve_state */
-
sve_alloc(current, true);
if (!current->thread.sve_state) {
clear_thread_flag(TIF_SVE);
@@ -541,11 +526,6 @@ static int preserve_za_context(struct za_context __user *ctx)
err |= __copy_to_user(&ctx->__reserved, reserved, sizeof(reserved));
if (vq) {
- /*
- * This assumes that the ZA state has already been saved to
- * the task struct by calling the function
- * fpsimd_signal_preserve_current_state().
- */
err |= __copy_to_user((char __user *)ctx + ZA_SIG_REGS_OFFSET,
current->thread.sme_state,
ZA_SIG_REGS_SIZE(vq));
@@ -580,16 +560,6 @@ static int restore_za_context(struct user_ctxs *user)
if (user->za_size < ZA_SIG_CONTEXT_SIZE(vq))
return -EINVAL;
- /*
- * Careful: we are about __copy_from_user() directly into
- * thread.sme_state with preemption enabled, so protection is
- * needed to prevent a racing context switch from writing stale
- * registers back over the new data.
- */
-
- fpsimd_flush_task_state(current);
- /* From now, fpsimd_thread_switch() won't touch thread.sve_state */
-
sme_alloc(current, true);
if (!current->thread.sme_state) {
current->thread.svcr &= ~SVCR_ZA_MASK;
@@ -627,11 +597,6 @@ static int preserve_zt_context(struct zt_context __user *ctx)
BUILD_BUG_ON(sizeof(ctx->__reserved) != sizeof(reserved));
err |= __copy_to_user(&ctx->__reserved, reserved, sizeof(reserved));
- /*
- * This assumes that the ZT state has already been saved to
- * the task struct by calling the function
- * fpsimd_signal_preserve_current_state().
- */
err |= __copy_to_user((char __user *)ctx + ZT_SIG_REGS_OFFSET,
thread_zt_state(¤t->thread),
ZT_SIG_REGS_SIZE(1));
@@ -657,16 +622,6 @@ static int restore_zt_context(struct user_ctxs *user)
if (nregs != 1)
return -EINVAL;
- /*
- * Careful: we are about __copy_from_user() directly into
- * thread.zt_state with preemption enabled, so protection is
- * needed to prevent a racing context switch from writing stale
- * registers back over the new data.
- */
-
- fpsimd_flush_task_state(current);
- /* From now, fpsimd_thread_switch() won't touch ZT in thread state */
-
err = __copy_from_user(thread_zt_state(¤t->thread),
(char __user const *)user->zt +
ZT_SIG_REGS_OFFSET,
@@ -1017,6 +972,8 @@ static int restore_sigframe(struct pt_regs *regs,
*/
forget_syscall(regs);
+ fpsimd_save_and_flush_current_state();
+
err |= !valid_user_regs(®s->user_regs, current);
if (err == 0)
err = parse_user_sigframe(&user, sf);
@@ -1510,8 +1467,11 @@ static int setup_return(struct pt_regs *regs, struct ksignal *ksig,
/*
* If we were in streaming mode the saved register
* state was SVE but we will exit SM and use the
- * FPSIMD register state - flush the saved FPSIMD
- * register state in case it gets loaded.
+ * FPSIMD register state.
+ *
+ * TODO: decide if this should behave as SMSTOP (e.g. reset
+ * FPSR + FPMR), or whether this should only clear the scalable
+ * registers + ZA state.
*/
if (current->thread.svcr & SVCR_SM_MASK) {
memset(¤t->thread.uw.fpsimd_state, 0,
@@ -1519,9 +1479,7 @@ static int setup_return(struct pt_regs *regs, struct ksignal *ksig,
current->thread.fp_type = FP_STATE_FPSIMD;
}
- current->thread.svcr &= ~(SVCR_ZA_MASK |
- SVCR_SM_MASK);
- sme_smstop();
+ current->thread.svcr &= ~(SVCR_ZA_MASK | SVCR_SM_MASK);
}
return 0;
@@ -1535,7 +1493,7 @@ static int setup_rt_frame(int usig, struct ksignal *ksig, sigset_t *set,
struct user_access_state ua_state;
int err = 0;
- fpsimd_signal_preserve_current_state();
+ fpsimd_save_and_flush_current_state();
if (get_sigframe(&user, ksig, regs))
return 1;
--
2.30.2
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH v2 13/13] arm64/fpsimd: signal: Simplify preserve_tpidr2_context()
2025-04-09 16:39 [PATCH v2 00/13] arm64: Preparatory FPSIMD/SVE/SME fixes Mark Rutland
` (11 preceding siblings ...)
2025-04-09 16:40 ` [PATCH v2 12/13] arm64/fpsimd: signal: " Mark Rutland
@ 2025-04-09 16:40 ` Mark Rutland
2025-04-09 17:17 ` [PATCH v2 00/13] arm64: Preparatory FPSIMD/SVE/SME fixes Catalin Marinas
13 siblings, 0 replies; 18+ messages in thread
From: Mark Rutland @ 2025-04-09 16:40 UTC (permalink / raw)
To: linux-arm-kernel; +Cc: ardb, broonie, catalin.marinas, mark.rutland, maz, will
During a context-switch, tls_thread_switch() reads and writes a task's
thread_struct::tpidr2_el0 field. Other code shouldn't access this field
for an active task, as such accesses would form a data-race with a
concurrent context-switch.
The usage in preserve_tpidr2_context() is suspicious, but benign as any
race with a context switch will write the same value back to
current->thread.tpidr2_el0.
Make this clearer and match restore_tpidr2_context() by using a
temporary variable instead, avoiding the (benign) data-race.
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Marc Zyngier <maz@kernel.org>
Cc: Mark Brown <broonie@kernel.org>
Cc: Will Deacon <will@kernel.org>
Reviewed-by: Mark Brown <broonie@kernel.org>
---
arch/arm64/kernel/signal.c | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/arch/arm64/kernel/signal.c b/arch/arm64/kernel/signal.c
index 0de9c452c6c0e..73f1ab56d81b2 100644
--- a/arch/arm64/kernel/signal.c
+++ b/arch/arm64/kernel/signal.c
@@ -478,13 +478,12 @@ extern int preserve_sve_context(void __user *ctx);
static int preserve_tpidr2_context(struct tpidr2_context __user *ctx)
{
+ u64 tpidr2_el0 = read_sysreg_s(SYS_TPIDR2_EL0);
int err = 0;
- current->thread.tpidr2_el0 = read_sysreg_s(SYS_TPIDR2_EL0);
-
__put_user_error(TPIDR2_MAGIC, &ctx->head.magic, err);
__put_user_error(sizeof(*ctx), &ctx->head.size, err);
- __put_user_error(current->thread.tpidr2_el0, &ctx->tpidr2, err);
+ __put_user_error(tpidr2_el0, &ctx->tpidr2, err);
return err;
}
--
2.30.2
^ permalink raw reply related [flat|nested] 18+ messages in thread* Re: [PATCH v2 00/13] arm64: Preparatory FPSIMD/SVE/SME fixes
2025-04-09 16:39 [PATCH v2 00/13] arm64: Preparatory FPSIMD/SVE/SME fixes Mark Rutland
` (12 preceding siblings ...)
2025-04-09 16:40 ` [PATCH v2 13/13] arm64/fpsimd: signal: Simplify preserve_tpidr2_context() Mark Rutland
@ 2025-04-09 17:17 ` Catalin Marinas
2025-04-29 19:46 ` Will Deacon
13 siblings, 1 reply; 18+ messages in thread
From: Catalin Marinas @ 2025-04-09 17:17 UTC (permalink / raw)
To: linux-arm-kernel, Mark Rutland; +Cc: Will Deacon, ardb, broonie, maz
On Wed, 09 Apr 2025 17:39:57 +0100, Mark Rutland wrote:
> These patches fix a number of problems in the FPSIMD/SVE/SME code, as a
> step towards re-enabling SME support. Additional fixes/changes will be
> necessary before we can re-enable SME support. I intend to follow up
> with more patches in the near future.
>
> I'm hoping these patches as-is are largely uncontroversial, though I'm
> afraid they've only seen light/targeted testing so far, so any testing
> would be much appreciated.
>
> [...]
I added these patches to for-next/sme-fixes (and for-kernelci) for wider
exposure. Not queued for upstream yet, I need to review and discuss with
Will whether we target 6.15 or 6.16.
Thanks!
--
Catalin
^ permalink raw reply [flat|nested] 18+ messages in thread* Re: [PATCH v2 00/13] arm64: Preparatory FPSIMD/SVE/SME fixes
2025-04-09 17:17 ` [PATCH v2 00/13] arm64: Preparatory FPSIMD/SVE/SME fixes Catalin Marinas
@ 2025-04-29 19:46 ` Will Deacon
2025-04-30 13:24 ` Mark Rutland
0 siblings, 1 reply; 18+ messages in thread
From: Will Deacon @ 2025-04-29 19:46 UTC (permalink / raw)
To: Catalin Marinas; +Cc: linux-arm-kernel, Mark Rutland, ardb, broonie, maz
On Wed, Apr 09, 2025 at 06:17:09PM +0100, Catalin Marinas wrote:
> On Wed, 09 Apr 2025 17:39:57 +0100, Mark Rutland wrote:
> > These patches fix a number of problems in the FPSIMD/SVE/SME code, as a
> > step towards re-enabling SME support. Additional fixes/changes will be
> > necessary before we can re-enable SME support. I intend to follow up
> > with more patches in the near future.
> >
> > I'm hoping these patches as-is are largely uncontroversial, though I'm
> > afraid they've only seen light/targeted testing so far, so any testing
> > would be much appreciated.
> >
> > [...]
>
> I added these patches to for-next/sme-fixes (and for-kernelci) for wider
> exposure. Not queued for upstream yet, I need to review and discuss with
> Will whether we target 6.15 or 6.16.
FYI: I see the following warning from 'allnoconfig' with these patches
applied:
arch/arm64/kernel/fpsimd.c:676:13: warning: unused function 'sve_to_fpsimd' [-Wunused-function]
676 | static void sve_to_fpsimd(struct task_struct *task)
| ^~~~~~~~~~~~~
1 warning generated.
It's easy enough to move that inside the CONFIG_ARM64_SVE guards, but
it's a little strange that fpsimd_to_sve() is more widely referenced
and harder to move.
Will
^ permalink raw reply [flat|nested] 18+ messages in thread* Re: [PATCH v2 00/13] arm64: Preparatory FPSIMD/SVE/SME fixes
2025-04-29 19:46 ` Will Deacon
@ 2025-04-30 13:24 ` Mark Rutland
0 siblings, 0 replies; 18+ messages in thread
From: Mark Rutland @ 2025-04-30 13:24 UTC (permalink / raw)
To: Will Deacon; +Cc: Catalin Marinas, linux-arm-kernel, ardb, broonie, maz
On Tue, Apr 29, 2025 at 08:46:01PM +0100, Will Deacon wrote:
> On Wed, Apr 09, 2025 at 06:17:09PM +0100, Catalin Marinas wrote:
> > On Wed, 09 Apr 2025 17:39:57 +0100, Mark Rutland wrote:
> > > These patches fix a number of problems in the FPSIMD/SVE/SME code, as a
> > > step towards re-enabling SME support. Additional fixes/changes will be
> > > necessary before we can re-enable SME support. I intend to follow up
> > > with more patches in the near future.
> > >
> > > I'm hoping these patches as-is are largely uncontroversial, though I'm
> > > afraid they've only seen light/targeted testing so far, so any testing
> > > would be much appreciated.
> > >
> > > [...]
> >
> > I added these patches to for-next/sme-fixes (and for-kernelci) for wider
> > exposure. Not queued for upstream yet, I need to review and discuss with
> > Will whether we target 6.15 or 6.16.
>
> FYI: I see the following warning from 'allnoconfig' with these patches
> applied:
>
>
> arch/arm64/kernel/fpsimd.c:676:13: warning: unused function 'sve_to_fpsimd' [-Wunused-function]
> 676 | static void sve_to_fpsimd(struct task_struct *task)
> | ^~~~~~~~~~~~~
> 1 warning generated.
Argh, sorry about this.
That's a side-effect of fpsimd_signal_preserve_current_state() being
removed in commit:
929fa99b1215966f ("arm64/fpsimd: signal: Always save+flush state early")
... since fpsimd_signal_preserve_current_state() was always defined and
would (conditionally) call sve_to_fpsimd().
> It's easy enough to move that inside the CONFIG_ARM64_SVE guards, but
> it's a little strange that fpsimd_to_sve() is more widely referenced
> and harder to move.
Yeah, this is a bit messy. I think no matter how we clean things up
we'll end up with a reference to fpsimd_to_sve() via do_sve_acc(). We
want do_sve_acc() to be defined regardless of CONFIG_ARM64_SVE so that
we get consistent trap/SIGILL behaviour when either CONFIG_ARM64_SVE=n
or SVE isn't supported by all PEs, and I don't think we want to move the
bulk of that logic under ifdeffery.
The simplest fix for now would be to mark fpsimd_to_sve() and
sve_to_fpsimd() as 'static inline' or '__maybe_unused', and the former
would be more consistent with the rest of the file.
I'll spin a fix shortly.
Mark.
^ permalink raw reply [flat|nested] 18+ messages in thread