summaryrefslogtreecommitdiff
path: root/arch/arm64/kernel/start.S
diff options
context:
space:
mode:
authorBradley Morgan <brads@mainlining.org>2026-10-03 18:57:38 +0000
committerBradley Morgan <brads@mainlining.org>2026-10-03 18:57:38 +0000
commit00fc82f17cff9295387d516384ecdc443acb1b22 (patch)
tree798bef0185878286028fe0eaf7594616a66903e4 /arch/arm64/kernel/start.S
parent7e8514efb64774b6a635b87ae945e519a7c38551 (diff)
tashaboot: arm64 bringup, semihosting payload loader
consoles on the pl011 the dtb points at, walks the memory node and chosen, loads an arm64 kernel Image over semihosting and jumps to it with the documented register state. string functions, printf and libfdt are vendored from lk (lk2nd) and u-boot so the libc surface is the one those projects already proved. built and booted on qemu virt, payload exits clean through semihosting.
Diffstat (limited to 'arch/arm64/kernel/start.S')
-rw-r--r--arch/arm64/kernel/start.S94
1 files changed, 94 insertions, 0 deletions
diff --git a/arch/arm64/kernel/start.S b/arch/arm64/kernel/start.S
new file mode 100644
index 0000000..776744c
--- /dev/null
+++ b/arch/arm64/kernel/start.S
@@ -0,0 +1,94 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * tashaboot arm64 entry. firmware drops us here, usually at EL2 on
+ * qemu virt. we park the secondaries, drop to EL1, set the stack and
+ * hand the dtb pointer to C.
+ *
+ * Copyright (C) 2026 Bradley Morgan <brads@mainlining.org>
+ */
+
+#include <asm/macro.h>
+
+.section .text.boot
+.globl _start
+_start:
+ b reset
+
+ .balign 8
+.globl _text_base
+_text_base:
+ .quad 0x40000000
+
+reset:
+ /* keep the dtb pointer before anything clobbers x0 */
+ mov x19, x0
+
+ /* park secondary cores, they have nothing to do yet */
+ mrs x0, mpidr_el1
+ and x0, x0, #0xff
+ cbnz x0, park
+
+ /* figure out which EL we got */
+ mrs x0, CurrentEL
+ lsr x0, x0, #2
+ cmp x0, #3
+ b.eq from_el3
+ cmp x0, #2
+ b.eq from_el2
+ cmp x0, #1
+ b.eq el1_entry
+ b park
+
+from_el3:
+ /* EL3 is a dead end for us, drop through the EL2 path if we can */
+ b park
+
+from_el2:
+ /* EL1 must be aarch64 */
+ mov x0, #(1 << 31)
+ msr hcr_el2, x0
+
+ /* scrub the EL2 state so EL1 starts clean */
+ msr cptr_el2, xzr
+ msr hstr_el2, xzr
+ mrs x0, cnthctl_el2
+ orr x0, x0, #(3 << 0)
+ msr cnthctl_el2, x0
+ msr vpidr_el2, xzr
+
+ mov x0, #0x3c5
+ msr scr_el3, x0
+
+ /* no MMU, no caches at EL1 yet */
+ msr sctlr_el1, xzr
+ isb
+
+ /* SPSR for EL1h with interrupts masked */
+ mov x0, #0x3c5
+ msr spsr_el2, x0
+ adr x0, el1_entry
+ msr elr_el2, x0
+ eret
+
+el1_entry:
+ /* stack for the bootloader, grows down from the image end */
+ ldr x0, =__image_end
+ mov sp, x0
+
+ /* clear bss */
+ ldr x0, =__bss_start
+ ldr x1, =__bss_end
+1: cmp x0, x1
+ b.hs 2f
+ str xzr, [x0], #8
+ b 1b
+2:
+
+ /* dtb pointer into C arg 0 */
+ mov x0, x19
+ bl tashaboot_main
+
+ /* if main returns there is nothing sensible to do */
+park:
+ wfe
+ b park