diff options
| author | Bradley Morgan <brads@mainlining.org> | 2026-10-04 05:04:50 +0000 |
|---|---|---|
| committer | Bradley Morgan <brads@mainlining.org> | 2026-10-04 05:04:50 +0000 |
| commit | 0567dd7e947d10a237405e4a9d965c57dd6b473e (patch) | |
| tree | 38cda1ba14e6e15730a2f0f32b48bd8d20bb284e /arch/arm64 | |
| parent | be4565f1fe3de7e16a48688ab8da39c433f0fee3 (diff) | |
tashaboot: serror resets, secondary release scrub, gic group truth
An SError while the loader runs means the machine is already
broken, handing the kernel a cpu that lost is worse than
stopping. The handler reports the syndrome then drives the
same reset domain PSCI SYSTEM_RESET does, with a park as the
fallback when the reset request is ignored.
Secondaries leave the pen in the manual's boot state now,
interrupts masked, and CNTVOFF_EL2 zeroed at EL2 so every PE
reads the same virtual counter. A loader cannot repair a per
cpu counter offset below EL2, and the kernel has no way to
repair it at all, whatever ran before could have left one.
The gic group registers are deliberately untouched. The
writes looked like firmware duty, but the group routing is
the secure world's: a non-secure loader's IGROUPR writes are
dropped on hardware implementing the security extension, and
on the emulator here they accept the write and the timer per
cpu interrupts stop reaching the kernel, the tick dies and
the boot hangs past the console handoff. Group config belongs
to the EL3 monitor, this loader runs without one, the comment
says so at the register level.
receipt: gic 8000000 off, smp brought up 1 node 4 cpus, run
/init, busybox shell, two consecutive boots, the pen scrub
exercised in the qemu spin table path.
Diffstat (limited to 'arch/arm64')
| -rw-r--r-- | arch/arm64/kernel/start.S | 27 | ||||
| -rw-r--r-- | arch/arm64/lib/gic.c | 14 |
2 files changed, 34 insertions, 7 deletions
diff --git a/arch/arm64/kernel/start.S b/arch/arm64/kernel/start.S index 6ee9941..3cf58d2 100644 --- a/arch/arm64/kernel/start.S +++ b/arch/arm64/kernel/start.S @@ -222,6 +222,24 @@ park: wfe b 1b 2: + /* interrupts masked at release, the manual's boot state */ + msr daifset, #0xf + /* + * every PE must read the same virtual counter. whatever + * ran before this loader could have left a per cpu offset + * in the virtual counter view, the kernel has no way to + * repair that itself. CNTVOFF_EL2 is writable at EL2 and + * the write holds for the EL1 virtual timer the kernel + * runs on. below EL2 it is out of reach, the reset value + * is the best a lower EL can do. + */ + mrs x4, CurrentEL + lsr x4, x4, #2 + cmp x4, #2 + b.lt 3f + msr cntvoff_el2, xzr + isb +3: mov x0, xzr /* secondaries enter with x0-x3 zero */ mov x1, xzr mov x2, xzr @@ -396,6 +414,15 @@ exc_serr: mov x1, #0 mov x2, lr bl exc_report + /* + * an SError while this loader runs means the machine is + * broken. handing the kernel a cpu that already lost is + * worse than stopping: report, then drive the reset domain + * the same way PSCI SYSTEM_RESET does. the reset call does + * not return, the park below is the fallback if a reset + * domain ignores the request. + */ + bl tb_system_reset ldp x29, x30, [sp], #16 b park diff --git a/arch/arm64/lib/gic.c b/arch/arm64/lib/gic.c index 11bb9cd..647e987 100644 --- a/arch/arm64/lib/gic.c +++ b/arch/arm64/lib/gic.c @@ -27,7 +27,6 @@ /* distributor registers, offsets from the GICD base */ #define GICD_CTLR 0x000 #define GICD_TYPER 0x004 -#define GICD_IGROUPR(n) (0x080 + (n) * 4) #define GICD_ISENABLER(n) (0x100 + (n) * 4) #define GICD_ICENABLER(n) (0x180 + (n) * 4) #define GICD_ICPENDR(n) (0x280 + (n) * 4) @@ -79,13 +78,14 @@ int tb_gic_init(uintptr_t gicd, uintptr_t gicc) lines = gicd_irq_lines(gicd); /* - * every interrupt in group 1, the non-secure group. the - * kernel does not see group 0 interrupts on non-secure - * hardware, and a bootloader that leaves any line in the - * secure group strands it. + * the group routing is deliberately untouched. the group + * registers are the secure world's, a non-secure loader's + * writes are dropped on hardware that implements the + * security extension, and on emulators that accept them + * the timer's per cpu interrupts stop reaching the + * kernel. group config belongs to the EL3 monitor, this + * loader runs without one. */ - for (n = 0; n < lines; n++) - writel(0xffffffff, REG32(gicd + GICD_IGROUPR(n))); /* no per interrupt enables, nothing pending */ for (n = 0; n < lines; n++) { |
