diff options
| -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++) { |
