Skip to content

Commit 969f5ea

Browse files
committed
arm64: errata: Add workaround for Cortex-A76 erratum #1463225
Revisions of the Cortex-A76 CPU prior to r4p0 are affected by an erratum that can prevent interrupts from being taken when single-stepping. This patch implements a software workaround to prevent userspace from effectively being able to disable interrupts. Cc: <[email protected]> Cc: Marc Zyngier <[email protected]> Cc: Catalin Marinas <[email protected]> Signed-off-by: Will Deacon <[email protected]>
1 parent 3e29ead commit 969f5ea

File tree

6 files changed

+109
-1
lines changed

6 files changed

+109
-1
lines changed

Documentation/arm64/silicon-errata.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ stable kernels.
6262
| ARM | Cortex-A76 | #1165522 | ARM64_ERRATUM_1165522 |
6363
| ARM | Cortex-A76 | #1286807 | ARM64_ERRATUM_1286807 |
6464
| ARM | Neoverse-N1 | #1188873 | ARM64_ERRATUM_1188873 |
65+
| ARM | Cortex-A76 | #1463225 | ARM64_ERRATUM_1463225 |
6566
| ARM | MMU-500 | #841119,#826419 | N/A |
6667
| | | | |
6768
| Cavium | ThunderX ITS | #22375, #24313 | CAVIUM_ERRATUM_22375 |

arch/arm64/Kconfig

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -520,6 +520,24 @@ config ARM64_ERRATUM_1286807
520520

521521
If unsure, say Y.
522522

523+
config ARM64_ERRATUM_1463225
524+
bool "Cortex-A76: Software Step might prevent interrupt recognition"
525+
default y
526+
help
527+
This option adds a workaround for Arm Cortex-A76 erratum 1463225.
528+
529+
On the affected Cortex-A76 cores (r0p0 to r3p1), software stepping
530+
of a system call instruction (SVC) can prevent recognition of
531+
subsequent interrupts when software stepping is disabled in the
532+
exception handler of the system call and either kernel debugging
533+
is enabled or VHE is in use.
534+
535+
Work around the erratum by triggering a dummy step exception
536+
when handling a system call from a task that is being stepped
537+
in a VHE configuration of the kernel.
538+
539+
If unsure, say Y.
540+
523541
config CAVIUM_ERRATUM_22375
524542
bool "Cavium erratum 22375, 24313"
525543
default y

arch/arm64/include/asm/cpucaps.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,8 @@
6262
#define ARM64_HAS_GENERIC_AUTH_IMP_DEF 41
6363
#define ARM64_HAS_IRQ_PRIO_MASKING 42
6464
#define ARM64_HAS_DCPODP 43
65+
#define ARM64_WORKAROUND_1463225 44
6566

66-
#define ARM64_NCAPS 44
67+
#define ARM64_NCAPS 45
6768

6869
#endif /* __ASM_CPUCAPS_H */

arch/arm64/kernel/cpu_errata.c

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -502,6 +502,22 @@ static const struct midr_range arm64_ssb_cpus[] = {
502502
{},
503503
};
504504

505+
#ifdef CONFIG_ARM64_ERRATUM_1463225
506+
DEFINE_PER_CPU(int, __in_cortex_a76_erratum_1463225_wa);
507+
508+
static bool
509+
has_cortex_a76_erratum_1463225(const struct arm64_cpu_capabilities *entry,
510+
int scope)
511+
{
512+
u32 midr = read_cpuid_id();
513+
/* Cortex-A76 r0p0 - r3p1 */
514+
struct midr_range range = MIDR_RANGE(MIDR_CORTEX_A76, 0, 0, 3, 1);
515+
516+
WARN_ON(scope != SCOPE_LOCAL_CPU || preemptible());
517+
return is_midr_in_range(midr, &range) && is_kernel_in_hyp_mode();
518+
}
519+
#endif
520+
505521
static void __maybe_unused
506522
cpu_enable_cache_maint_trap(const struct arm64_cpu_capabilities *__unused)
507523
{
@@ -823,6 +839,14 @@ const struct arm64_cpu_capabilities arm64_errata[] = {
823839
.capability = ARM64_WORKAROUND_1165522,
824840
ERRATA_MIDR_RANGE(MIDR_CORTEX_A76, 0, 0, 2, 0),
825841
},
842+
#endif
843+
#ifdef CONFIG_ARM64_ERRATUM_1463225
844+
{
845+
.desc = "ARM erratum 1463225",
846+
.capability = ARM64_WORKAROUND_1463225,
847+
.type = ARM64_CPUCAP_LOCAL_CPU_ERRATUM,
848+
.matches = has_cortex_a76_erratum_1463225,
849+
},
826850
#endif
827851
{
828852
}

arch/arm64/kernel/syscall.c

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <linux/syscalls.h>
99

1010
#include <asm/daifflags.h>
11+
#include <asm/debug-monitors.h>
1112
#include <asm/fpsimd.h>
1213
#include <asm/syscall.h>
1314
#include <asm/thread_info.h>
@@ -60,6 +61,35 @@ static inline bool has_syscall_work(unsigned long flags)
6061
int syscall_trace_enter(struct pt_regs *regs);
6162
void syscall_trace_exit(struct pt_regs *regs);
6263

64+
#ifdef CONFIG_ARM64_ERRATUM_1463225
65+
DECLARE_PER_CPU(int, __in_cortex_a76_erratum_1463225_wa);
66+
67+
static void cortex_a76_erratum_1463225_svc_handler(void)
68+
{
69+
u32 reg, val;
70+
71+
if (!unlikely(test_thread_flag(TIF_SINGLESTEP)))
72+
return;
73+
74+
if (!unlikely(this_cpu_has_cap(ARM64_WORKAROUND_1463225)))
75+
return;
76+
77+
__this_cpu_write(__in_cortex_a76_erratum_1463225_wa, 1);
78+
reg = read_sysreg(mdscr_el1);
79+
val = reg | DBG_MDSCR_SS | DBG_MDSCR_KDE;
80+
write_sysreg(val, mdscr_el1);
81+
asm volatile("msr daifclr, #8");
82+
isb();
83+
84+
/* We will have taken a single-step exception by this point */
85+
86+
write_sysreg(reg, mdscr_el1);
87+
__this_cpu_write(__in_cortex_a76_erratum_1463225_wa, 0);
88+
}
89+
#else
90+
static void cortex_a76_erratum_1463225_svc_handler(void) { }
91+
#endif /* CONFIG_ARM64_ERRATUM_1463225 */
92+
6393
static void el0_svc_common(struct pt_regs *regs, int scno, int sc_nr,
6494
const syscall_fn_t syscall_table[])
6595
{
@@ -68,6 +98,7 @@ static void el0_svc_common(struct pt_regs *regs, int scno, int sc_nr,
6898
regs->orig_x0 = regs->regs[0];
6999
regs->syscallno = scno;
70100

101+
cortex_a76_erratum_1463225_svc_handler();
71102
local_daif_restore(DAIF_PROCCTX);
72103
user_exit();
73104

arch/arm64/mm/fault.c

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -810,13 +810,46 @@ void __init hook_debug_fault_code(int nr,
810810
debug_fault_info[nr].name = name;
811811
}
812812

813+
#ifdef CONFIG_ARM64_ERRATUM_1463225
814+
DECLARE_PER_CPU(int, __in_cortex_a76_erratum_1463225_wa);
815+
816+
static int __exception
817+
cortex_a76_erratum_1463225_debug_handler(struct pt_regs *regs)
818+
{
819+
if (user_mode(regs))
820+
return 0;
821+
822+
if (!__this_cpu_read(__in_cortex_a76_erratum_1463225_wa))
823+
return 0;
824+
825+
/*
826+
* We've taken a dummy step exception from the kernel to ensure
827+
* that interrupts are re-enabled on the syscall path. Return back
828+
* to cortex_a76_erratum_1463225_svc_handler() with debug exceptions
829+
* masked so that we can safely restore the mdscr and get on with
830+
* handling the syscall.
831+
*/
832+
regs->pstate |= PSR_D_BIT;
833+
return 1;
834+
}
835+
#else
836+
static int __exception
837+
cortex_a76_erratum_1463225_debug_handler(struct pt_regs *regs)
838+
{
839+
return 0;
840+
}
841+
#endif /* CONFIG_ARM64_ERRATUM_1463225 */
842+
813843
asmlinkage void __exception do_debug_exception(unsigned long addr_if_watchpoint,
814844
unsigned int esr,
815845
struct pt_regs *regs)
816846
{
817847
const struct fault_info *inf = esr_to_debug_fault_info(esr);
818848
unsigned long pc = instruction_pointer(regs);
819849

850+
if (cortex_a76_erratum_1463225_debug_handler(regs))
851+
return;
852+
820853
/*
821854
* Tell lockdep we disabled irqs in entry.S. Do nothing if they were
822855
* already disabled to preserve the last enabled/disabled addresses.

0 commit comments

Comments
 (0)