GCSCRE0_EL1 and GCSPR_EL0 are live registers for EL0 on systems with GCS but are not saved and restored by cpu_do_suspend()/cpu_do_resume(). This means that the task that initiated suspend can return to userspace with UNKNOWN or incorrect values for GCSPR_EL0 and GCSCRE0_EL1 (many fields in GCSCRE0_EL1 reset to 0). This will in turn lead to ongoing corruption, including not restoring mode until a task with a different GCS mode is run. Fix this by saving and restoring the values over suspend. This increases the size of struct cpu_suspend_ctx by 16 bytes. Fixes: 5d8b172e7005 ("arm64: Add Kconfig for Guarded Control Stack (GCS)") Signed-off-by: Mark Brown Cc: stable@vger.kernel.org --- arch/arm64/include/asm/suspend.h | 2 +- arch/arm64/mm/proc.S | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/arch/arm64/include/asm/suspend.h b/arch/arm64/include/asm/suspend.h index eb9ce315f146..8e9c97aab15a 100644 --- a/arch/arm64/include/asm/suspend.h +++ b/arch/arm64/include/asm/suspend.h @@ -2,7 +2,7 @@ #ifndef __ASM_SUSPEND_H #define __ASM_SUSPEND_H -#define NR_CTX_REGS 16 +#define NR_CTX_REGS 18 #define NR_CALLEE_SAVED_REGS 12 /* diff --git a/arch/arm64/mm/proc.S b/arch/arm64/mm/proc.S index 664ea9e3b9ad..99aacf03367d 100644 --- a/arch/arm64/mm/proc.S +++ b/arch/arm64/mm/proc.S @@ -121,6 +121,12 @@ alternative_else_nop_endif alternative_if ARM64_HAS_S1POE mrs x2, REG_POR_EL0 str x2, [x0, #120] +alternative_else_nop_endif +alternative_if ARM64_HAS_GCS + mrs x2, REG_GCSPR_EL0 + str x2, [x0, #128] + mrs x2, REG_GCSCRE0_EL1 + str x2, [x0, #136] alternative_else_nop_endif ret SYM_FUNC_END(cpu_do_suspend) @@ -168,6 +174,12 @@ alternative_if ARM64_HAS_S1POE ldr x2, [x0, #120] msr REG_POR_EL0, x2 alternative_else_nop_endif +alternative_if ARM64_HAS_GCS + ldr x2, [x0, #128] + msr REG_GCSPR_EL0, x2 + ldr x2, [x0, #136] + msr REG_GCSCRE0_EL1, x2 +alternative_else_nop_endif msr sctlr_el1, x12 set_this_cpu_offset x13 -- 2.47.3