summaryrefslogtreecommitdiff
path: root/arch/arm64/kernel
diff options
context:
space:
mode:
authorBradley Morgan <brads@mainlining.org>2026-10-03 22:58:47 +0000
committerBradley Morgan <brads@mainlining.org>2026-10-03 22:58:47 +0000
commit61af8d6209b395a03ceab156b6df6720d638048e (patch)
tree3dde763a13edcc56d9432e5403a3e1a6d30c5710 /arch/arm64/kernel
parent6b3fcc0def1e173c76943682dcf3cba6edcd3b55 (diff)
tashaboot: smp, psci, initrd, timer, cache by va
The bootloader now does the whole job of machine firmware it owns: boots 4 cpus, hands over an initrd, answers PSCI, and carries the delay and cache primitives the arch layer needs. SMP: the secondary pen is the Wait For Event mechanism from the manual (B2-144, D1-2255), each secondary watches its spin gate, WFE, the release writes the entry and SEVs, the recheck after each wake covers a release that lands between the load and the sleep. The gates land in the dtb cpu-release-addr slots, rewritten in place by a small walker, no libfdt, structure per the devicetree specification, values only, the properties themselves are fixed at build time like firmware shipping a fixed blob. PSCI 0.2 at EL2 (DEN 0022): the HVC trap arrives at the current EL SP_ELx sync slot (EC 0x16 in ESR_EL2, the vector layout Table D1-7), dispatch on the standard function ids, VERSION, CPU_ON writes the target gate and SEVs, CPU_OFF clears the gate and returns to the pen, SYSTEM_OFF and SYSTEM_RESET drive RMR_EL2.RR. On qemu the cores are held by the machine's own firmware and released through its PSCI (hvc with -kernel, smc with virtualization=on), the handler here is the real hardware path where the bootloader is the conduit. The initrd handoff: loaded at a fixed address clear of the image and dtb, the dtb /chosen carries linux,initrd-start and -end. Delays are the generic timer (D10), CNTFRQ_EL0 frequency, CNTVCT_EL0 count, busy wait, no interrupts. Cache maintenance by virtual address, dc cvac, dc ivac, dc civac, ic ivau with the barrier pairs the manual requires, the by VA form beats set and way when the range is known. Boot receipt, 4 cpus, el2, initrd: tashaboot 0.1 initrd at 46000000 [ 0.000000] Booting Linux on physical CPU 0x0000000000 [ 0.130621] smp: Brought up 1 node, 4 CPUs [ 1.830692] Run /init as init process tashaboot linux userspace reached cores: 4 BusyBox v1.37.0 built-in shell (ash) ~ # Signed-off-by: Bradley Morgan <brads@mainlining.org>
Diffstat (limited to 'arch/arm64/kernel')
-rw-r--r--arch/arm64/kernel/start.S78
1 files changed, 77 insertions, 1 deletions
diff --git a/arch/arm64/kernel/start.S b/arch/arm64/kernel/start.S
index 2d4b5c0..969830d 100644
--- a/arch/arm64/kernel/start.S
+++ b/arch/arm64/kernel/start.S
@@ -147,6 +147,11 @@ c_entry:
ldr x0, =__stack_top
mov sp, x0
+ /* export the spin gate array address for the dtb patcher */
+ adr x0, tb_spin_gates
+ adrp x1, tb_spin_gates_ptr
+ str x0, [x1, #:lo12:tb_spin_gates_ptr]
+
/* clear bss */
ldr x0, =__bss_start
ldr x1, =__bss_end
@@ -166,9 +171,44 @@ c_entry:
bl tashaboot_main
/* if main returns there is nothing sensible to do */
+/*
+ * the spin table pen, the Wait For Event mechanism from the manual
+ * (B2-144, D1-2255). each secondary watches its own gate, the
+ * cpu-release-addr the dtb names. WFE clears the event register and
+ * sleeps, the kernel writes the secondary entry to the gate, makes
+ * it visible, then SEV sets the event register on every PE. the load
+ * recheck after each wake covers a release that lands between the
+ * load and the WFE. entered with MMU and caches off, left the same.
+ */
+.globl park_ret
+park_ret:
park:
+ adr x0, tb_spin_gates
+ mrs x1, mpidr_el1
+ and x1, x1, #0xff /* affinity 0, the core number */
+ add x0, x0, x1, lsl #3 /* gate = gates + core * 8 */
+
+ /* diagnostic: stamp arrival, primary prints it later */
+ adr x3, tb_pen_stamps
+ strb w1, [x3, x1]
+ sevl
wfe
- b park
+ sevl
+ wfe
+
+1:
+ ldr x2, [x0]
+ cbnz x2, 2f
+ wfe
+ b 1b
+2:
+ mov x0, xzr /* secondaries enter with x0-x3 zero */
+ mov x1, xzr
+ mov x2, xzr
+ mov x3, xzr
+ dsb sy
+ isb
+ br x2
/*
* exception vectors, the armv8 layout: 16 slots, 128 bytes each, in
@@ -218,6 +258,19 @@ vectors:
.align 7
b exc_serr
+.pushsection .data.tb_spin, "aw"
+.align 3
+.globl tb_spin_gates
+tb_spin_gates:
+ .quad 0, 0, 0, 0, 0, 0, 0, 0
+.globl tb_spin_gates_ptr
+tb_spin_gates_ptr:
+ .quad 0
+.globl tb_pen_stamps
+tb_pen_stamps:
+ .byte 0, 0, 0, 0, 0, 0, 0, 0
+.popsection
+
exc_sync:
stp x29, x30, [sp, #-16]!
mov x29, sp
@@ -226,6 +279,10 @@ exc_sync:
cmp x3, #2
b.lt 1f
mrs x0, esr_el2
+ mrs x2, elr_el2
+ lsr x1, x0, #26
+ cmp x1, #0x16 /* HVC from lower EL */
+ b.eq hvc_from_el1
mrs x1, far_el2
b 2f
1:
@@ -237,6 +294,25 @@ exc_sync:
ldp x29, x30, [sp], #16
b park
+/*
+ * HVC from EL1, the PSCI conduit. x0-x3 are the PSCI args in the
+ * caller registers, dispatch and return in x0. ELR_EL2 is already
+ * the resume point, eret takes it back.
+ */
+hvc_from_el1:
+ stp x4, x5, [sp, #-16]!
+ stp x6, x7, [sp, #-16]!
+ stp x29, x30, [sp, #-16]!
+ mov x29, sp
+
+ bl tb_psci_dispatch
+
+ ldp x29, x30, [sp], #16
+ ldp x6, x7, [sp], #16
+ ldp x4, x5, [sp], #16
+ ldp x29, x30, [sp], #16
+ eret
+
exc_serr:
stp x29, x30, [sp, #-16]!
mov x29, sp