summaryrefslogtreecommitdiff
path: root/arch/arm64/kernel
diff options
context:
space:
mode:
authorWill Deacon <will@kernel.org>2026-06-14 12:16:59 +0100
committerWill Deacon <will@kernel.org>2026-06-14 12:16:59 +0100
commit35d2b77d8dd76cfccf54cc0c6453584ea4f31224 (patch)
tree3f3850b5a08a10d3bacf8e8aecb2b67585cb4894 /arch/arm64/kernel
parent0fccc93585c11e594920e5d203d152e89bf16687 (diff)
parent987ec51e18419cc0ebf6f6fa1cfbfd149eca443d (diff)
downloadlinux-next-35d2b77d8dd76cfccf54cc0c6453584ea4f31224.tar.gz
linux-next-35d2b77d8dd76cfccf54cc0c6453584ea4f31224.zip
Merge branch 'for-next/fpsimd-cleanups' into for-next/core
* for-next/fpsimd-cleanups: arm64: fpsimd: Remove <asm/fpsimdmacros.h> arm64: fpsimd: Move SME save/restore inline arm64: fpsimd: Move sve_flush_live() inline arm64: fpsimd: Move SVE save/restore inline arm64: fpsimd: Use opaque type for SME state arm64: fpsimd: Use opaque type for SVE state arm64: fpsimd: Move fpsimd save/restore inline arm64: fpsimd: Split FPSR/FPCR from SVE save/restore arm64: sysreg: Add FPCR and FPSR arm64: fpsimd: Move sve_get_vl() and sme_get_vl() inline arm64: fpsimd: Use assembler for baseline SME instructions arm64: fpsimd: Use assembler for SVE instructions arm64: fpsimd: Remove sve_set_vq() and sme_set_vq() arm64: fpsimd: Fold sve_init_regs() into do_sve_acc() KVM: arm64: pkvm: Remove struct cpu_sve_state KVM: arm64: pkvm: Save host FPMR in host cpu context KVM: arm64: Don't override FFR save/restore argument KVM: arm64: Don't include <asm/fpsimdmacros.h> arm64: fpsimd: Fix type mismatch in sme_{save,load}_state() arm64: fpsimd: Fix type mismatch in sve_{save,load}_state()
Diffstat (limited to 'arch/arm64/kernel')
-rw-r--r--arch/arm64/kernel/Makefile2
-rw-r--r--arch/arm64/kernel/entry-common.c8
-rw-r--r--arch/arm64/kernel/entry-fpsimd.S134
-rw-r--r--arch/arm64/kernel/fpsimd.c90
4 files changed, 42 insertions, 192 deletions
diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
index 74b76bb70452..d2690c3ec528 100644
--- a/arch/arm64/kernel/Makefile
+++ b/arch/arm64/kernel/Makefile
@@ -27,7 +27,7 @@ KCOV_INSTRUMENT_idle.o := n
# Object file lists.
obj-y := debug-monitors.o entry.o irq.o fpsimd.o \
- entry-common.o entry-fpsimd.o process.o ptrace.o \
+ entry-common.o process.o ptrace.o \
setup.o signal.o sys.o stacktrace.o time.o traps.o \
io.o vdso.o hyp-stub.o psci.o cpu_ops.o \
return_address.o cpuinfo.o cpu_errata.o \
diff --git a/arch/arm64/kernel/entry-common.c b/arch/arm64/kernel/entry-common.c
index cb54335465f6..2352297330e1 100644
--- a/arch/arm64/kernel/entry-common.c
+++ b/arch/arm64/kernel/entry-common.c
@@ -237,12 +237,8 @@ static inline void fpsimd_syscall_enter(void)
if (!system_supports_sve())
return;
- if (test_thread_flag(TIF_SVE)) {
- unsigned int sve_vq_minus_one;
-
- sve_vq_minus_one = sve_vq_from_vl(task_get_sve_vl(current)) - 1;
- sve_flush_live(true, sve_vq_minus_one);
- }
+ if (test_thread_flag(TIF_SVE))
+ sve_flush_live();
/*
* Any live non-FPSIMD SVE state has been zeroed. Allow
diff --git a/arch/arm64/kernel/entry-fpsimd.S b/arch/arm64/kernel/entry-fpsimd.S
deleted file mode 100644
index 6325db1a2179..000000000000
--- a/arch/arm64/kernel/entry-fpsimd.S
+++ /dev/null
@@ -1,134 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * FP/SIMD state saving and restoring
- *
- * Copyright (C) 2012 ARM Ltd.
- * Author: Catalin Marinas <catalin.marinas@arm.com>
- */
-
-#include <linux/linkage.h>
-
-#include <asm/assembler.h>
-#include <asm/fpsimdmacros.h>
-
-/*
- * Save the FP registers.
- *
- * x0 - pointer to struct fpsimd_state
- */
-SYM_FUNC_START(fpsimd_save_state)
- fpsimd_save x0, 8
- ret
-SYM_FUNC_END(fpsimd_save_state)
-
-/*
- * Load the FP registers.
- *
- * x0 - pointer to struct fpsimd_state
- */
-SYM_FUNC_START(fpsimd_load_state)
- fpsimd_restore x0, 8
- ret
-SYM_FUNC_END(fpsimd_load_state)
-
-#ifdef CONFIG_ARM64_SVE
-
-/*
- * Save the SVE state
- *
- * x0 - pointer to buffer for state
- * x1 - pointer to storage for FPSR
- * x2 - Save FFR if non-zero
- */
-SYM_FUNC_START(sve_save_state)
- sve_save 0, x1, x2, 3
- ret
-SYM_FUNC_END(sve_save_state)
-
-/*
- * Load the SVE state
- *
- * x0 - pointer to buffer for state
- * x1 - pointer to storage for FPSR
- * x2 - Restore FFR if non-zero
- */
-SYM_FUNC_START(sve_load_state)
- sve_load 0, x1, x2, 4
- ret
-SYM_FUNC_END(sve_load_state)
-
-SYM_FUNC_START(sve_get_vl)
- _sve_rdvl 0, 1
- ret
-SYM_FUNC_END(sve_get_vl)
-
-SYM_FUNC_START(sve_set_vq)
- sve_load_vq x0, x1, x2
- ret
-SYM_FUNC_END(sve_set_vq)
-
-/*
- * Zero all SVE registers but the first 128-bits of each vector
- *
- * VQ must already be configured by caller, any further updates of VQ
- * will need to ensure that the register state remains valid.
- *
- * x0 = include FFR?
- * x1 = VQ - 1
- */
-SYM_FUNC_START(sve_flush_live)
- cbz x1, 1f // A VQ-1 of 0 is 128 bits so no extra Z state
- sve_flush_z
-1: sve_flush_p
- tbz x0, #0, 2f
- sve_flush_ffr
-2: ret
-SYM_FUNC_END(sve_flush_live)
-
-#endif /* CONFIG_ARM64_SVE */
-
-#ifdef CONFIG_ARM64_SME
-
-SYM_FUNC_START(sme_get_vl)
- _sme_rdsvl 0, 1
- ret
-SYM_FUNC_END(sme_get_vl)
-
-SYM_FUNC_START(sme_set_vq)
- sme_load_vq x0, x1, x2
- ret
-SYM_FUNC_END(sme_set_vq)
-
-/*
- * Save the ZA and ZT state
- *
- * x0 - pointer to buffer for state
- * x1 - number of ZT registers to save
- */
-SYM_FUNC_START(sme_save_state)
- _sme_rdsvl 2, 1 // x2 = VL/8
- sme_save_za 0, x2, 12 // Leaves x0 pointing to the end of ZA
-
- cbz x1, 1f
- _str_zt 0
-1:
- ret
-SYM_FUNC_END(sme_save_state)
-
-/*
- * Load the ZA and ZT state
- *
- * x0 - pointer to buffer for state
- * x1 - number of ZT registers to save
- */
-SYM_FUNC_START(sme_load_state)
- _sme_rdsvl 2, 1 // x2 = VL/8
- sme_load_za 0, x2, 12 // Leaves x0 pointing to the end of ZA
-
- cbz x1, 1f
- _ldr_zt 0
-1:
- ret
-SYM_FUNC_END(sme_load_state)
-
-#endif /* CONFIG_ARM64_SME */
diff --git a/arch/arm64/kernel/fpsimd.c b/arch/arm64/kernel/fpsimd.c
index 60a45d600b46..25dc5afe9ba0 100644
--- a/arch/arm64/kernel/fpsimd.c
+++ b/arch/arm64/kernel/fpsimd.c
@@ -377,8 +377,10 @@ static void task_fpsimd_load(void)
if (!thread_sm_enabled(&current->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);
+ if (test_thread_flag(TIF_SVE)) {
+ unsigned long vq = sve_vq_from_vl(task_get_sve_vl(current));
+ sysreg_clear_set_s(SYS_ZCR_EL1, ZCR_ELx_LEN, vq - 1);
+ }
restore_sve_regs = true;
restore_ffr = true;
@@ -403,8 +405,10 @@ static void task_fpsimd_load(void)
unsigned long sme_vl = task_get_sme_vl(current);
/* Ensure VL is set up for restoring data */
- if (test_thread_flag(TIF_SME))
- sme_set_vq(sve_vq_from_vl(sme_vl) - 1);
+ if (test_thread_flag(TIF_SME)) {
+ unsigned long vq = sve_vq_from_vl(sme_vl);
+ sysreg_clear_set_s(SYS_SMCR_EL1, SMCR_ELx_LEN, vq - 1);
+ }
write_sysreg_s(current->thread.svcr, SYS_SVCR);
@@ -421,9 +425,8 @@ static void task_fpsimd_load(void)
if (restore_sve_regs) {
WARN_ON_ONCE(current->thread.fp_type != FP_STATE_SVE);
- sve_load_state(sve_pffr(&current->thread),
- &current->thread.uw.fpsimd_state.fpsr,
- restore_ffr);
+ sve_load_state(current->thread.sve_state, restore_ffr);
+ fpsimd_load_common(&current->thread.uw.fpsimd_state);
} else {
WARN_ON_ONCE(current->thread.fp_type != FP_STATE_FPSIMD);
fpsimd_load_state(&current->thread.uw.fpsimd_state);
@@ -503,9 +506,8 @@ static void fpsimd_save_user_state(void)
return;
}
- sve_save_state((char *)last->sve_state +
- sve_ffr_offset(vl),
- &last->st->fpsr, save_ffr);
+ sve_save_state(last->sve_state, save_ffr);
+ fpsimd_save_common(last->st);
*last->fp_type = FP_STATE_SVE;
} else {
fpsimd_save_state(last->st);
@@ -636,7 +638,8 @@ static __uint128_t arm64_cpu_to_le128(__uint128_t x)
#define arm64_le128_to_cpu(x) arm64_cpu_to_le128(x)
-static void __fpsimd_to_sve(void *sst, struct user_fpsimd_state const *fst,
+static void __fpsimd_to_sve(struct arm64_sve_state *sst,
+ struct user_fpsimd_state const *fst,
unsigned int vq)
{
unsigned int i;
@@ -663,7 +666,7 @@ static void __fpsimd_to_sve(void *sst, struct user_fpsimd_state const *fst,
static inline void fpsimd_to_sve(struct task_struct *task)
{
unsigned int vq;
- void *sst = task->thread.sve_state;
+ struct arm64_sve_state *sst = task->thread.sve_state;
struct user_fpsimd_state const *fst = &task->thread.uw.fpsimd_state;
if (!system_supports_sve() && !system_supports_sme())
@@ -687,7 +690,7 @@ static inline void fpsimd_to_sve(struct task_struct *task)
static inline void sve_to_fpsimd(struct task_struct *task)
{
unsigned int vq, vl;
- void const *sst = task->thread.sve_state;
+ const struct arm64_sve_state *sst = task->thread.sve_state;
struct user_fpsimd_state *fst = &task->thread.uw.fpsimd_state;
unsigned int i;
__uint128_t const *p;
@@ -786,7 +789,7 @@ void fpsimd_sync_from_effective_state(struct task_struct *task)
void fpsimd_sync_to_effective_state_zeropad(struct task_struct *task)
{
unsigned int vq;
- void *sst = task->thread.sve_state;
+ struct arm64_sve_state *sst = task->thread.sve_state;
struct user_fpsimd_state const *fst = &task->thread.uw.fpsimd_state;
if (task->thread.fp_type != FP_STATE_SVE)
@@ -804,7 +807,8 @@ static int change_live_vector_length(struct task_struct *task,
{
unsigned int sve_vl = task_get_sve_vl(task);
unsigned int sme_vl = task_get_sme_vl(task);
- void *sve_state = NULL, *sme_state = NULL;
+ struct arm64_sve_state *sve_state = NULL;
+ struct arm64_sme_state *sme_state = NULL;
if (type == ARM64_VEC_SME)
sme_vl = vl;
@@ -1293,31 +1297,6 @@ void sme_suspend_exit(void)
#endif /* CONFIG_ARM64_SME */
-static void sve_init_regs(void)
-{
- /*
- * Convert the FPSIMD state to SVE, zeroing all the state that
- * is not shared with FPSIMD. If (as is likely) the current
- * state is live in the registers then do this there and
- * update our metadata for the current task including
- * disabling the trap, otherwise update our in-memory copy.
- * We are guaranteed to not be in streaming mode, we can only
- * take a SVE trap when not in streaming mode and we can't be
- * in streaming mode when taking a SME trap.
- */
- if (!test_thread_flag(TIF_FOREIGN_FPSTATE)) {
- unsigned long vq_minus_one =
- sve_vq_from_vl(task_get_sve_vl(current)) - 1;
- sve_set_vq(vq_minus_one);
- sve_flush_live(true, vq_minus_one);
- fpsimd_bind_task_to_cpu();
- } else {
- fpsimd_to_sve(current);
- current->thread.fp_type = FP_STATE_SVE;
- fpsimd_flush_task_state(current);
- }
-}
-
/*
* Trapped SVE access
*
@@ -1349,13 +1328,23 @@ void do_sve_acc(unsigned long esr, struct pt_regs *regs)
WARN_ON(1); /* SVE access shouldn't have trapped */
/*
- * Even if the task can have used streaming mode we can only
- * generate SVE access traps in normal SVE mode and
- * transitioning out of streaming mode may discard any
- * streaming mode state. Always clear the high bits to avoid
- * any potential errors tracking what is properly initialised.
+ * Convert the FPSIMD state to SVE. Stale SVE state can be present in
+ * registers or memory, so we must zero all state that is not shared
+ * with FPSIMD.
+ *
+ * SVE traps cannot be taken from streaming mode, so there cannot be
+ * any effective streaming mode SVE state.
*/
- sve_init_regs();
+ if (!test_thread_flag(TIF_FOREIGN_FPSTATE)) {
+ unsigned long vq = sve_vq_from_vl(task_get_sve_vl(current));
+ sysreg_clear_set_s(SYS_ZCR_EL1, ZCR_ELx_LEN, vq - 1);
+ sve_flush_live();
+ fpsimd_bind_task_to_cpu();
+ } else {
+ fpsimd_to_sve(current);
+ current->thread.fp_type = FP_STATE_SVE;
+ fpsimd_flush_task_state(current);
+ }
put_cpu_fpsimd_context();
}
@@ -1479,9 +1468,8 @@ void do_sme_acc(unsigned long esr, struct pt_regs *regs)
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);
+ unsigned long vq = sve_vq_from_vl(task_get_sme_vl(current));
+ sysreg_clear_set_s(SYS_SMCR_EL1, SMCR_ELx_LEN, vq - 1);
fpsimd_bind_task_to_cpu();
} else {
@@ -1656,8 +1644,8 @@ static void fpsimd_flush_thread_vl(enum vec_type type)
void fpsimd_flush_thread(void)
{
- void *sve_state = NULL;
- void *sme_state = NULL;
+ struct arm64_sve_state *sve_state = NULL;
+ struct arm64_sme_state *sme_state = NULL;
if (!system_supports_fpsimd())
return;