From 9dbdb15abf8ffb9dbdd972d6bbbcea9a3591e2a3 Mon Sep 17 00:00:00 2001 From: Bradley Morgan Date: Sat, 3 Oct 2026 20:02:29 +0000 Subject: tashaboot: arm64 bootloader A small arm64 bootloader. No board code, no device tree porting, the architecture manual is the whole story: exception vectors in the fixed 16 slot layout (Table D1-7), ESR_ELx decoded by exception class (D1-2172), EL entry and eret chains per the programmers model (D1-2146), cache maintenance by set/way over the CLIDR_EL1 levels, semihosting for console and file io per DUI 0203, and the A64 boot protocol from Documentation/arch/arm64/booting.rst. The loader boots a stock mainline Image end to end on the qemu virt machine. Boot receipt with 7.3-rc3 (42MB Image): tashaboot 0.1 loaded 43450368 bytes at 40200000, entry 40200000 jumping [ 0.000000] Booting Linux on physical CPU 0x0000000000 [0x411fd070] [ 0.000000] Linux version 7.3.0-rc3 [ 0.000000] Machine model: linux,dummy-virt [ 0.000000] earlycon: pl11 MMIO32:0x0000000009000000 ... ---[ end Kernel panic - not syncing: VFS: Unable to mount root fs ]--- The panic is the expected end state, no root filesystem is handed over yet. The boot chain, state per stage, start to payload: +-----------+-----+--------------+----------------------------------+ | stage | EL | state | work | +-----------+-----+--------------+----------------------------------+ | firmware | any | MMU maybe on | x0 = dtb, jump in | +-----------+-----+--------------+----------------------------------+ | tashaboot | 3-2 | | SCR_EL3.NS = 1, eret to EL2 | +-----------+-----+--------------+----------------------------------+ | | 2 | virt scrub | HCR/CNTHCTL/CPTR/HSTR, CNTFRQ, | | | | | VBAR_EL2, MMU off, tlbi alle2 | +-----------+-----+--------------+----------------------------------+ | | 2 | | load Image over semihosting, | | | | | validate header, place per | | | | | booting.rst | +-----------+-----+--------------+----------------------------------+ | | 2 | caches clean | flush dcache, inval icache, | | | | | args ride x20/x21, regs last | +-----------+-----+--------------+----------------------------------+ | payload | 2 | fresh start | x0 = dtb, x1-x3 = 0, DAIF | | | | | masked, br to image entry | +-----------+-----+--------------+----------------------------------+ Two handoff bugs the kernel caught, both AAPCS clobbers in the final jump. Cache maintenance was called after the register setup, x0-x18 are caller saved, so tb_flush_dcache_all() wiped the dtb pointer and the kernel spun in setup_machine_fdt() with an invalid device tree blob. The flush helpers also clobbered x1 (u-boot's void call convention left mov x1, x0 in cache.S) which handed the kernel a wild x0. The arguments ride in x20/x21 across the cache calls now, callee saved, and the register setup is the last thing before the branch. What is missing on purpose: no SMP bringup (secondary cores park), no PSCI, no initrd or root filesystem handoff, single serial console. Those come next. Signed-off-by: Bradley Morgan --- test/payload.S | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 test/payload.S (limited to 'test/payload.S') diff --git a/test/payload.S b/test/payload.S new file mode 100644 index 0000000..d1891a2 --- /dev/null +++ b/test/payload.S @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * payload.S - tiny arm64 image for testing tashaboot. carries the real + * Image header from Documentation/arch/arm64/booting.rst, prints one + * line on the virt uart, parks. built with the same conventions the + * kernel itself uses so the header math in tashaboot is exercised for + * real, not against a fake. + * + * Copyright (C) 2026 Bradley Morgan + */ + +#include + +/* the header the bootloader validates */ +#define ARM64_IMAGE_MAGIC 0x644d5241 + +.section .text.head +.globl _start +_start: + /* code0/code1: branch over the header, like the kernel does */ + b 1f + .long 0 + + /* text_offset, 0x80000 like every kernel since forever */ + .quad 0x80000 + /* image_size, filled at build time by scripts/mkpayload.sh */ + .quad payload_end - _start + /* flags, bit 3 = anywhere in memory is fine */ + .quad (1 << 3) + .quad 0 + .quad 0 + .quad 0 + /* magic "ARM\x64" */ + .long ARM64_IMAGE_MAGIC + .long 0 +1: + /* the payload entry: x0 = dtb from the bootloader */ + ldr x1, =0x09000000 /* pl011 on qemu virt */ + adr x2, msg +2: ldrb w3, [x2], #1 + cbz w3, 3f + str w3, [x1] + b 2b +3: /* clean exit through semihosting, proof we got here */ + mov x0, #0x18 /* SYS_EXIT */ + ldr x1, =0x20026 /* ADP_Stopped_ApplicationExit */ + hlt #0xF000 + b 3b + +msg: .asciz "payload: alive, tashaboot jumped here\n" + .balign 8 +payload_end: -- cgit v1.2.3