diff options
| author | Bradley Morgan <brads@mainlining.org> | 2026-10-04 10:42:10 +0000 |
|---|---|---|
| committer | Bradley Morgan <brads@mainlining.org> | 2026-10-04 10:46:46 +0000 |
| commit | 5ff54a875642962fc857cee00bde17f9a465f1fa (patch) | |
| tree | b265a12d538167c3bbb86b24118a7ad7a062fc74 /common/main.c | |
holy shit it's here, Tashaboot, based from arm arm, enjoy
reading this masterpiece
Signed-off-by: Bradley Morgan <brads@mainlining.org>
Diffstat (limited to 'common/main.c')
| -rw-r--r-- | common/main.c | 300 |
1 files changed, 300 insertions, 0 deletions
diff --git a/common/main.c b/common/main.c new file mode 100644 index 0000000..ea85be0 --- /dev/null +++ b/common/main.c @@ -0,0 +1,300 @@ +/* + * main.c - the C entry. console up first, then load the payload and + * jump. called from start.S with x0 = whatever the firmware passed. + * + * the osdev model, arm64: the firmware services do the work, the + * kernel goes at a fixed known address, whatever x0 we were handed + * goes straight through to the payload. + * + * Copyright (c) 2026 Bradley Morgan <brads@mainlining.org> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include <string.h> +#include <stdio.h> +#include <sys/types.h> +#include <stdint.h> +#include <debug.h> +#include <semihosting.h> +#include <boot.h> + +#define TB_VERSION "0.1" +#define TB_INITRD_ADDR 0x46000000ULL +#define TB_INITRD_FILE "initrd.cpio.gz" + +extern int tb_console_init(void); +extern void tb_system_reset(void); + +/* + * the payload goes 16MB clear of wherever this bootloader is + * actually running, ADR knows the runtime base and qemu is free + * to place us anywhere. hardcoding 0x40200000 smashed our own + * image when qemu loaded us there. + */ +extern char __image_copy_end[]; +#define TB_LOAD_ADDR ((uintptr_t)__image_copy_end + (16ULL << 20)) + +/* the file semihosting serves as the payload */ +#define TB_BOOTFILE "Image" + +extern void __NO_RETURN tb_boot_linux(uintptr_t ep, uintptr_t fw_arg); +extern uintptr_t tb_dtb_relocate(uintptr_t dtb, void *scratch, + size_t scratch_size, size_t grow); +extern int tb_gic_init(uintptr_t gicd, uintptr_t gicc); +extern int tb_dtb_add_chosen_prop(uintptr_t dtb, const char *name, + const void *val, size_t len); +static size_t initrd_size; + +void tashaboot_main(uintptr_t fw_arg) +{ + struct tb_image img; + int ret; + + /* + * the console uart the machine named, before the first + * print. the base is the first reg pair of the pl011 + * node, the same walk the gic used, no board hardcodes. + */ + { + extern int tb_dtb_find_reg0(uintptr_t dtb, + const char *name, + uintptr_t *addr, size_t *size); + extern void tb_console_set_pl011(uintptr_t base); + uintptr_t uart = 0; + size_t usz = 0; + + if (tb_dtb_find_reg0(fw_arg, "pl011", &uart, &usz) == 0) + tb_console_set_pl011(uart); + } + +#ifdef TB_HW_RECEIPT + { + uint64_t midr, el, cntfrq, mpidr; + + asm volatile("mrs %0, midr_el1" : "=r"(midr)); + asm volatile("mrs %0, CurrentEL" : "=r"(el)); + asm volatile("mrs %0, cntfrq_el0" : "=r"(cntfrq)); + asm volatile("mrs %0, mpidr_el1" : "=r"(mpidr)); + + tb_console_init(); + dprintf(ALWAYS, "tashaboot on real hardware\n"); + dprintf(ALWAYS, "midr %llx el %llx cntfrq %llx mpidr %llx\n", + (unsigned long long)midr, + (unsigned long long)el >> 2, + (unsigned long long)cntfrq, + (unsigned long long)mpidr); + + /* let the console drain before the reset domain hits */ + for (volatile int i = 0; i < 100000000; i++) + ; + + tb_system_reset(); + } +#endif + + if (tb_console_init()) + return; + + dprintf(ALWAYS, "tashaboot " TB_VERSION "\n"); + +#ifdef TB_ENABLE_MMU + { + extern int tb_mmu_enable(void); + extern int tb_mmu_selftest(void); + extern void tb_mmu_disable(void); + + if (tb_mmu_enable() == 0) { + if (tb_mmu_selftest() == 0) + dprintf(ALWAYS, "mmu: identity map on\n"); + else + dprintf(ALWAYS, "mmu: self test failed, " + "running unmapped\n"); + tb_mmu_disable(); + } + } +#endif + + { + /* report the RAM we actually live in, before any mmu */ + extern int tb_dtb_patch_memory(uintptr_t dtb, + uint64_t base, + uint64_t size); + int r; + + r = 0; (void)r; + + { + uint32_t *cells = (uint32_t *)(fw_arg + 0x16c); + int i; + + dprintf(ALWAYS, "cells after:"); + for (i = 0; i < 4; i++) + dprintf(ALWAYS, " %08x", cells[i]); + dprintf(ALWAYS, "\n"); + } + } + + { + /* spin table gates into the dtb, one per cpu node */ + extern unsigned long *tb_spin_gates_ptr; + extern int tb_dtb_patch_spin_table(uintptr_t dtb, + uintptr_t *gates, + int ngates); + int n; + + if (tb_spin_gates_ptr) { + extern unsigned char tb_pen_stamps[8]; + int c; + + n = tb_dtb_patch_spin_table(fw_arg, + tb_spin_gates_ptr, 8); + dprintf(ALWAYS, "dtb: %d release addrs patched\n", n); + + /* who made it to the pen */ + for (c = 1; c < 8; c++) { + if (tb_pen_stamps[c]) + break; + } + dprintf(ALWAYS, "pen: %s\n", + c < 8 ? "secondaries waiting" : + "no secondaries parked"); + } + } + + ret = tb_load_semihosting(TB_BOOTFILE, TB_LOAD_ADDR, &img); + if (ret) { + dprintf(ALWAYS, "load failed (%d), halting\n", ret); + platform_halt(); + } + + /* + * firmware owns the devicetree it hands the kernel. ours + * relocates into scratch with grow room, then the chosen + * properties are added there and the kernel gets the new + * address, the same flow libfdt firmware uses. + */ + { + /* + * the scratch lives at a fixed free address, clear of + * our image, the payload, and the kernel relocation + * zone. a bss array would sit inside 0x40080000+ and + * the kernel overwrites it while copying itself. + */ + uint8_t *dtb_scratch = (uint8_t *)0x45000000ULL; + uintptr_t newdtb; + + newdtb = tb_dtb_relocate(fw_arg, dtb_scratch, + 0x10000, 0x200); + if (!newdtb) { + dprintf(ALWAYS, "dtb: relocate failed\n"); + platform_halt(); + } + + fw_arg = newdtb; + + /* + * the interrupt controller the machine told us + * about, found by name, the reg pair read with the + * root cell counts. the gic goes into the defined + * off state before the kernel brings its own irq + * handling up. + */ + { + extern int tb_dtb_find_reg0(uintptr_t dtb, + const char *name, + uintptr_t *addr, + size_t *size); + uintptr_t gicd = 0; + uintptr_t gicc = 0; + size_t sz = 0; + + if (tb_dtb_find_reg0(fw_arg, "intc", &gicd, &sz) == 0 && + sz >= 0x10000) { + gicc = gicd + 0x10000; + tb_gic_init(gicd, gicc); + dprintf(ALWAYS, "gic: %lx off\n", + (unsigned long)gicd); + } + } + } + + /* + * the initrd rides after the kernel, the dtb /chosen carries + * linux,initrd-start and -end, both already patched in place + * with this layout. + */ + { + int r = tb_load_raw(TB_INITRD_FILE, TB_INITRD_ADDR, + &initrd_size); + + if (r == 0) + dprintf(ALWAYS, "initrd at %lx, %lx bytes\n", + (unsigned long)TB_INITRD_ADDR, + (unsigned long)initrd_size); + else + dprintf(ALWAYS, "no initrd (%d)\n", r); + } + + /* + * the chosen properties, written now that the initrd size + * is known. the cells are big endian, the fdt is a big + * endian format end to end. + */ + { + uint8_t start_cells[8], end_cells[8]; + uint64_t start = TB_INITRD_ADDR; + uint64_t end = TB_INITRD_ADDR + initrd_size; + int a, b; + + for (int i = 0; i < 8; i++) { + start_cells[i] = (uint8_t)(start >> (56 - 8 * i)); + end_cells[i] = (uint8_t)(end >> (56 - 8 * i)); + } + a = tb_dtb_add_chosen_prop(fw_arg, "linux,initrd-start", + start_cells, 8); + b = tb_dtb_add_chosen_prop(fw_arg, "linux,initrd-end", + end_cells, 8); + dprintf(ALWAYS, "dtb: initrd props %d %d\n", a, b); + } + + + + dprintf(ALWAYS, "loaded %llu bytes at %lx, entry %lx\n", + (unsigned long long)img.size, img.load, img.ep); + +#ifdef TB_TEST_SMC + { + register uint64_t r0 asm("x0") = 0x84000000; + register uint64_t r1 asm("x1") = 0; + register uint64_t r2 asm("x2") = 0; + register uint64_t r3 asm("x3") = 0; + + asm volatile("smc #0" + : "+r"(r0), "+r"(r1), "+r"(r2), "+r"(r3)); + dprintf(ALWAYS, "smc conduit: psci version %lx\n", + (unsigned long)r0); + } +#endif + + dprintf(ALWAYS, "jumping\n"); + + tb_boot_linux(img.ep, fw_arg); +} |
