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 --- .gitignore | 6 + Makefile | 53 ++++++ README.md | 2 + arch/arm64/include/asm/linkage.h | 14 ++ arch/arm64/include/asm/macro.h | 347 +++++++++++++++++++++++++++++++++++ arch/arm64/kernel/boot.S | 43 +++++ arch/arm64/kernel/exceptions.c | 67 +++++++ arch/arm64/kernel/halt.c | 18 ++ arch/arm64/kernel/start.S | 259 ++++++++++++++++++++++++++ arch/arm64/kernel/tashaboot.lds | 67 +++++++ arch/arm64/lib/cache.S | 100 ++++++++++ arch/arm64/lib/semihosting.S | 18 ++ common/console.c | 74 ++++++++ common/image.c | 77 ++++++++ common/load.c | 57 ++++++ common/main.c | 75 ++++++++ include/assert.h | 41 +++++ include/boot.h | 32 ++++ include/compiler.h | 133 ++++++++++++++ include/ctype.h | 43 +++++ include/debug.h | 109 +++++++++++ include/endian.h | 54 ++++++ include/limits.h | 121 +++++++++++++ include/platform/debug.h | 44 +++++ include/printf.h | 53 ++++++ include/reg.h | 50 +++++ include/semihosting.h | 32 ++++ include/stdio.h | 52 ++++++ include/string.h | 69 +++++++ include/sys/types.h | 86 +++++++++ lib/itoa.c | 63 +++++++ lib/printf.c | 383 +++++++++++++++++++++++++++++++++++++++ lib/semihosting.c | 159 ++++++++++++++++ lib/string/bcopy.c | 34 ++++ lib/string/bzero.c | 35 ++++ lib/string/memchr.c | 45 +++++ lib/string/memcmp.c | 40 ++++ lib/string/memcpy.c | 69 +++++++ lib/string/memmove.c | 93 ++++++++++ lib/string/memset.c | 61 +++++++ lib/string/strchr.c | 37 ++++ lib/string/strcmp.c | 41 +++++ lib/string/strcpy.c | 39 ++++ lib/string/strlcat.c | 55 ++++++ lib/string/strlcpy.c | 50 +++++ lib/string/strlen.c | 41 +++++ lib/string/strncmp.c | 42 +++++ lib/string/strnlen.c | 38 ++++ lib/string/strrchr.c | 45 +++++ lib/string/strrev.c | 46 +++++ lib/string/strstr.c | 51 ++++++ test/payload.S | 52 ++++++ 52 files changed, 3715 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 README.md create mode 100644 arch/arm64/include/asm/linkage.h create mode 100644 arch/arm64/include/asm/macro.h create mode 100644 arch/arm64/kernel/boot.S create mode 100644 arch/arm64/kernel/exceptions.c create mode 100644 arch/arm64/kernel/halt.c create mode 100644 arch/arm64/kernel/start.S create mode 100644 arch/arm64/kernel/tashaboot.lds create mode 100644 arch/arm64/lib/cache.S create mode 100644 arch/arm64/lib/semihosting.S create mode 100644 common/console.c create mode 100644 common/image.c create mode 100644 common/load.c create mode 100644 common/main.c create mode 100644 include/assert.h create mode 100644 include/boot.h create mode 100644 include/compiler.h create mode 100644 include/ctype.h create mode 100644 include/debug.h create mode 100644 include/endian.h create mode 100644 include/limits.h create mode 100644 include/platform/debug.h create mode 100644 include/printf.h create mode 100644 include/reg.h create mode 100644 include/semihosting.h create mode 100644 include/stdio.h create mode 100644 include/string.h create mode 100644 include/sys/types.h create mode 100644 lib/itoa.c create mode 100644 lib/printf.c create mode 100644 lib/semihosting.c create mode 100644 lib/string/bcopy.c create mode 100644 lib/string/bzero.c create mode 100644 lib/string/memchr.c create mode 100644 lib/string/memcmp.c create mode 100644 lib/string/memcpy.c create mode 100644 lib/string/memmove.c create mode 100644 lib/string/memset.c create mode 100644 lib/string/strchr.c create mode 100644 lib/string/strcmp.c create mode 100644 lib/string/strcpy.c create mode 100644 lib/string/strlcat.c create mode 100644 lib/string/strlcpy.c create mode 100644 lib/string/strlen.c create mode 100644 lib/string/strncmp.c create mode 100644 lib/string/strnlen.c create mode 100644 lib/string/strrchr.c create mode 100644 lib/string/strrev.c create mode 100644 lib/string/strstr.c create mode 100644 test/payload.S diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..30a676f --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +build/ +*.o +*.elf +*.bin +*.img +*.dtb diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..be2cb63 --- /dev/null +++ b/Makefile @@ -0,0 +1,53 @@ +# tashaboot +# +# build: make CROSS=aarch64-linux-gnu- +# run: make run (qemu virt, semihosting, Image on the host) +# +# arm64 only. the architecture contract comes from the ARM ARM +# (DDI 0487), the payload protocol from the kernel booting docs. + +CROSS ?= aarch64-linux-gnu- +CC := $(CROSS)gcc +LD := $(CROSS)ld +OBJCOPY := $(CROSS)objcopy + +CFLAGS := -nostdlib -ffreestanding -mgeneral-regs-only \ + -fno-builtin -fno-stack-protector -fno-pie -no-pie \ + -Wall -Werror -O2 \ + -Iinclude -Iarch/arm64/include + +LDFLAGS := -T arch/arm64/kernel/tashaboot.lds + +OBJS := arch/arm64/kernel/start.o \ + arch/arm64/kernel/exceptions.o \ + arch/arm64/kernel/halt.o \ + arch/arm64/kernel/boot.o \ + arch/arm64/lib/cache.o \ + arch/arm64/lib/semihosting.o \ + common/main.o common/console.o common/image.o common/load.o \ + lib/printf.o lib/itoa.o lib/semihosting.o \ + $(patsubst %.c,%.o,$(wildcard lib/string/*.c)) + +all: build/tashaboot.bin + +build/tashaboot.elf: $(OBJS) arch/arm64/kernel/tashaboot.lds + mkdir -p build + $(LD) $(LDFLAGS) -o $@ $(OBJS) + +build/tashaboot.bin: build/tashaboot.elf + $(OBJCOPY) -O binary $< $@ + +%.o: %.c + $(CC) $(CFLAGS) -c -o $@ $< + +%.o: %.S + $(CC) $(CFLAGS) -c -o $@ $< + +run: all + qemu-system-aarch64 -M virt -cpu cortex-a57 -nographic -no-reboot \ + -semihosting -kernel build/tashaboot.bin + +clean: + rm -rf build $(OBJS) + +.PHONY: all run clean diff --git a/README.md b/README.md new file mode 100644 index 0000000..b03aa83 --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# tashaboot +Coming soon :) diff --git a/arch/arm64/include/asm/linkage.h b/arch/arm64/include/asm/linkage.h new file mode 100644 index 0000000..b5b9706 --- /dev/null +++ b/arch/arm64/include/asm/linkage.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __ASM_LINKAGE_H +#define __ASM_LINKAGE_H + +#define ALIGN .p2align 4 +#define ENTRY(name) \ + .globl name; \ + ALIGN; \ + name: +#define ENDPROC(name) \ + .type name, %function; \ + .size name, .-name + +#endif diff --git a/arch/arm64/include/asm/macro.h b/arch/arm64/include/asm/macro.h new file mode 100644 index 0000000..1a1edc9 --- /dev/null +++ b/arch/arm64/include/asm/macro.h @@ -0,0 +1,347 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * include/asm-arm/macro.h + * + * Copyright (C) 2009 Jean-Christophe PLAGNIOL-VILLARD + */ + +#ifndef __ASM_ARM_MACRO_H__ +#define __ASM_ARM_MACRO_H__ + +#ifdef CONFIG_ARM64 +#include +#endif + +#ifdef __ASSEMBLY__ + +/* + * These macros provide a convenient way to write 8, 16 and 32 bit data + * to any address. + * Registers r4 and r5 are used, any data in these registers are + * overwritten by the macros. + * The macros are valid for any ARM architecture, they do not implement + * any memory barriers so caution is recommended when using these when the + * caches are enabled or on a multi-core system. + */ + +.macro write32, addr, data + ldr r4, =\addr + ldr r5, =\data + str r5, [r4] +.endm + +.macro write16, addr, data + ldr r4, =\addr + ldrh r5, =\data + strh r5, [r4] +.endm + +.macro write8, addr, data + ldr r4, =\addr + ldrb r5, =\data + strb r5, [r4] +.endm + +/* + * This macro generates a loop that can be used for delays in the code. + * Register r4 is used, any data in this register is overwritten by the + * macro. + * The macro is valid for any ARM architeture. The actual time spent in the + * loop will vary from CPU to CPU though. + */ + +.macro wait_timer, time + ldr r4, =\time +1: + nop + subs r4, r4, #1 + bcs 1b +.endm + +#ifdef CONFIG_ARM64 +/* + * Register aliases. + */ +lr .req x30 + +/* + * Branch according to exception level + */ +.macro switch_el, xreg, el3_label, el2_label, el1_label + mrs \xreg, CurrentEL + cmp \xreg, #0x8 + b.gt \el3_label + b.eq \el2_label + b.lt \el1_label +.endm + +/* + * Branch if we are not in the highest exception level + */ +.macro branch_if_not_highest_el, xreg, label + switch_el \xreg, 3f, 2f, 1f + +2: mrs \xreg, ID_AA64PFR0_EL1 + and \xreg, \xreg, #(ID_AA64PFR0_EL1_EL3) + cbnz \xreg, \label + b 3f + +1: mrs \xreg, ID_AA64PFR0_EL1 + and \xreg, \xreg, #(ID_AA64PFR0_EL1_EL3 | ID_AA64PFR0_EL1_EL2) + cbnz \xreg, \label + +3: +.endm + +/* + * Branch if current processor is a Cortex-A57 core. + */ +.macro branch_if_a57_core, xreg, a57_label + mrs \xreg, midr_el1 + lsr \xreg, \xreg, #4 + and \xreg, \xreg, #0x00000FFF + cmp \xreg, #0xD07 /* Cortex-A57 MPCore processor. */ + b.eq \a57_label +.endm + +/* + * Branch if current processor is a Cortex-A53 core. + */ +.macro branch_if_a53_core, xreg, a53_label + mrs \xreg, midr_el1 + lsr \xreg, \xreg, #4 + and \xreg, \xreg, #0x00000FFF + cmp \xreg, #0xD03 /* Cortex-A53 MPCore processor. */ + b.eq \a53_label +.endm + +/* + * Branch if current processor is a slave, + * choose processor with all zero affinity value as the master. + */ +.macro branch_if_slave, xreg, slave_label +#ifdef CONFIG_ARMV8_MULTIENTRY + mrs \xreg, mpidr_el1 + and \xreg, \xreg, 0xffffffffff /* clear bits [63:40] */ + and \xreg, \xreg, ~0x00ff000000 /* also clear bits [31:24] */ + cbnz \xreg, \slave_label +#endif +.endm + +/* + * Branch if current processor is a master, + * choose processor with all zero affinity value as the master. + */ +.macro branch_if_master, xreg, master_label +#ifdef CONFIG_ARMV8_MULTIENTRY + mrs \xreg, mpidr_el1 + and \xreg, \xreg, 0xffffffffff /* clear bits [63:40] */ + and \xreg, \xreg, ~0x00ff000000 /* also clear bits [31:24] */ + cbz \xreg, \master_label +#else + b \master_label +#endif +.endm + +/* + * Switch from EL3 to EL2 for ARMv8 + * @ep: kernel entry point + * @flag: The execution state flag for lower exception + * level, ES_TO_AARCH64 or ES_TO_AARCH32 + * @tmp: temporary register + * + * For loading 32-bit OS, x1 is machine nr and x2 is ftaddr. + * For loading 64-bit OS, x0 is physical address to the FDT blob. + * They will be passed to the guest. + */ +.macro armv8_switch_to_el2_m, ep, flag, tmp + msr cptr_el3, xzr /* Disable coprocessor traps to EL3 */ + mov \tmp, #CPTR_EL2_RES1 + msr cptr_el2, \tmp /* Disable coprocessor traps to EL2 */ + + /* Initialize Generic Timers */ + msr cntvoff_el2, xzr + + /* Initialize SCTLR_EL2 + * + * setting RES1 bits (29,28,23,22,18,16,11,5,4) to 1 + * and RES0 bits (31,30,27,26,24,21,20,17,15-13,10-6) + + * EE,WXN,I,SA,C,A,M to 0 + */ + ldr \tmp, =(SCTLR_EL2_RES1 | SCTLR_EL2_EE_LE |\ + SCTLR_EL2_WXN_DIS | SCTLR_EL2_ICACHE_DIS |\ + SCTLR_EL2_SA_DIS | SCTLR_EL2_DCACHE_DIS |\ + SCTLR_EL2_ALIGN_DIS | SCTLR_EL2_MMU_DIS) + msr sctlr_el2, \tmp + + mov \tmp, sp + msr sp_el2, \tmp /* Migrate SP */ + mrs \tmp, vbar_el3 + msr vbar_el2, \tmp /* Migrate VBAR */ + + /* Check switch to AArch64 EL2 or AArch32 Hypervisor mode */ + cmp \flag, #ES_TO_AARCH32 + b.eq 1f + + /* + * The next lower exception level is AArch64, 64bit EL2 | HCE | + * RES1 (Bits[5:4]) | Non-secure EL0/EL1. + * and the SMD depends on requirements. + */ +#ifdef CONFIG_ARMV8_PSCI + ldr \tmp, =(SCR_EL3_RW_AARCH64 | SCR_EL3_HCE_EN |\ + SCR_EL3_RES1 | SCR_EL3_NS_EN) +#else + ldr \tmp, =(SCR_EL3_RW_AARCH64 | SCR_EL3_HCE_EN |\ + SCR_EL3_SMD_DIS | SCR_EL3_RES1 |\ + SCR_EL3_NS_EN) +#endif + +#ifdef CONFIG_ARMV8_EA_EL3_FIRST + orr \tmp, \tmp, #SCR_EL3_EA_EN +#endif + msr scr_el3, \tmp + + /* Return to the EL2_SP2 mode from EL3 */ + ldr \tmp, =(SPSR_EL_DEBUG_MASK | SPSR_EL_SERR_MASK |\ + SPSR_EL_IRQ_MASK | SPSR_EL_FIQ_MASK |\ + SPSR_EL_M_AARCH64 | SPSR_EL_M_EL2H) + msr spsr_el3, \tmp + msr elr_el3, \ep + eret + +1: + /* + * The next lower exception level is AArch32, 32bit EL2 | HCE | + * SMD | RES1 (Bits[5:4]) | Non-secure EL0/EL1. + */ + ldr \tmp, =(SCR_EL3_RW_AARCH32 | SCR_EL3_HCE_EN |\ + SCR_EL3_SMD_DIS | SCR_EL3_RES1 |\ + SCR_EL3_NS_EN) + msr scr_el3, \tmp + + /* Return to AArch32 Hypervisor mode */ + ldr \tmp, =(SPSR_EL_END_LE | SPSR_EL_ASYN_MASK |\ + SPSR_EL_IRQ_MASK | SPSR_EL_FIQ_MASK |\ + SPSR_EL_T_A32 | SPSR_EL_M_AARCH32 |\ + SPSR_EL_M_HYP) + msr spsr_el3, \tmp + msr elr_el3, \ep + eret +.endm + +/* + * Switch from EL2 to EL1 for ARMv8 + * @ep: kernel entry point + * @flag: The execution state flag for lower exception + * level, ES_TO_AARCH64 or ES_TO_AARCH32 + * @tmp: temporary register + * + * For loading 32-bit OS, x1 is machine nr and x2 is ftaddr. + * For loading 64-bit OS, x0 is physical address to the FDT blob. + * They will be passed to the guest. + */ +.macro armv8_switch_to_el1_m, ep, flag, tmp, tmp2 + /* Initialize Generic Timers */ + mrs \tmp, cnthctl_el2 + /* Enable EL1 access to timers */ + orr \tmp, \tmp, #(CNTHCTL_EL2_EL1PCEN_EN |\ + CNTHCTL_EL2_EL1PCTEN_EN) + msr cnthctl_el2, \tmp + msr cntvoff_el2, xzr + + /* Initilize MPID/MPIDR registers */ + mrs \tmp, midr_el1 + msr vpidr_el2, \tmp + mrs \tmp, mpidr_el1 + msr vmpidr_el2, \tmp + + /* Disable coprocessor traps */ + mov \tmp, #CPTR_EL2_RES1 + msr cptr_el2, \tmp /* Disable coprocessor traps to EL2 */ + msr hstr_el2, xzr /* Disable coprocessor traps to EL2 */ + mov \tmp, #CPACR_EL1_FPEN_EN + msr cpacr_el1, \tmp /* Enable FP/SIMD at EL1 */ + + /* SCTLR_EL1 initialization + * + * setting RES1 bits (29,28,23,22,20,11) to 1 + * and RES0 bits (31,30,27,21,17,13,10,6) + + * UCI,EE,EOE,WXN,nTWE,nTWI,UCT,DZE,I,UMA,SED,ITD, + * CP15BEN,SA0,SA,C,A,M to 0 + */ + ldr \tmp, =(SCTLR_EL1_RES1 | SCTLR_EL1_UCI_DIS |\ + SCTLR_EL1_EE_LE | SCTLR_EL1_WXN_DIS |\ + SCTLR_EL1_NTWE_DIS | SCTLR_EL1_NTWI_DIS |\ + SCTLR_EL1_UCT_DIS | SCTLR_EL1_DZE_DIS |\ + SCTLR_EL1_ICACHE_DIS | SCTLR_EL1_UMA_DIS |\ + SCTLR_EL1_SED_EN | SCTLR_EL1_ITD_EN |\ + SCTLR_EL1_CP15BEN_DIS | SCTLR_EL1_SA0_DIS |\ + SCTLR_EL1_SA_DIS | SCTLR_EL1_DCACHE_DIS |\ + SCTLR_EL1_ALIGN_DIS | SCTLR_EL1_MMU_DIS) + msr sctlr_el1, \tmp + + mov \tmp, sp + msr sp_el1, \tmp /* Migrate SP */ + mrs \tmp, vbar_el2 + msr vbar_el1, \tmp /* Migrate VBAR */ + + /* Check switch to AArch64 EL1 or AArch32 Supervisor mode */ + cmp \flag, #ES_TO_AARCH32 + b.eq 1f + + /* Initialize HCR_EL2 */ + /* Only disable PAuth traps if PAuth is supported */ + mrs \tmp, id_aa64isar1_el1 + ldr \tmp2, =(ID_AA64ISAR1_EL1_GPI | ID_AA64ISAR1_EL1_GPA | \ + ID_AA64ISAR1_EL1_API | ID_AA64ISAR1_EL1_APA) + tst \tmp, \tmp2 + mov \tmp2, #(HCR_EL2_RW_AARCH64 | HCR_EL2_HCD_DIS) + orr \tmp, \tmp2, #(HCR_EL2_APK | HCR_EL2_API) + csel \tmp, \tmp2, \tmp, eq + msr hcr_el2, \tmp + + /* Return to the EL1_SP1 mode from EL2 */ + ldr \tmp, =(SPSR_EL_DEBUG_MASK | SPSR_EL_SERR_MASK |\ + SPSR_EL_IRQ_MASK | SPSR_EL_FIQ_MASK |\ + SPSR_EL_M_AARCH64 | SPSR_EL_M_EL1H) + msr spsr_el2, \tmp + msr elr_el2, \ep + eret + +1: + /* Initialize HCR_EL2 */ + ldr \tmp, =(HCR_EL2_RW_AARCH32 | HCR_EL2_HCD_DIS) + msr hcr_el2, \tmp + + /* Return to AArch32 Supervisor mode from EL2 */ + ldr \tmp, =(SPSR_EL_END_LE | SPSR_EL_ASYN_MASK |\ + SPSR_EL_IRQ_MASK | SPSR_EL_FIQ_MASK |\ + SPSR_EL_T_A32 | SPSR_EL_M_AARCH32 |\ + SPSR_EL_M_SVC) + msr spsr_el2, \tmp + msr elr_el2, \ep + eret +.endm + +#if defined(CONFIG_GICV3) +.macro gic_wait_for_interrupt_m xreg1 +0 : wfi + mrs \xreg1, ICC_IAR1_EL1 + msr ICC_EOIR1_EL1, \xreg1 + cbnz \xreg1, 0b +.endm +#elif defined(CONFIG_GICV2) +.macro gic_wait_for_interrupt_m xreg1, wreg2 +0 : wfi + ldr \wreg2, [\xreg1, GICC_AIAR] + str \wreg2, [\xreg1, GICC_AEOIR] + and \wreg2, \wreg2, #0x3ff + cbnz \wreg2, 0b +.endm +#endif + +#endif /* CONFIG_ARM64 */ + +#endif /* __ASSEMBLY__ */ +#endif /* __ASM_ARM_MACRO_H__ */ diff --git a/arch/arm64/kernel/boot.S b/arch/arm64/kernel/boot.S new file mode 100644 index 0000000..615be06 --- /dev/null +++ b/arch/arm64/kernel/boot.S @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * boot.S - the final jump to the payload. x0 = dtb, x1 = x2 = x3 = 0, + * MMU and caches off, D cache flushed, I cache invalidated. that is + * the whole contract from Documentation/arch/arm64/booting.rst. + * + * Copyright (C) 2026 Bradley Morgan + */ + +#include + +.pushsection .text.tb_boot_linux, "ax" +ENTRY(tb_boot_linux) + /* ep in x0, dtb in x1, per the kernel boot protocol */ + + /* + * cache maintenance first, register setup last. x0-x18 are + * caller saved per the AAPCS, the flush helpers are free to + * clobber them, so the args ride in x20/x21 across the calls. + */ + mov x20, x0 /* entry point */ + mov x21, x1 /* dtb */ + + bl tb_flush_dcache_all + bl tb_invalidate_icache_all + + mov x8, x20 + mov x0, x21 + mov x1, xzr + mov x2, xzr + mov x3, xzr + + /* MMU off, caches off, the kernel sets up its own state */ + mrs x9, sctlr_el1 + bic x9, x9, #(1 << 0) /* M, MMU */ + bic x9, x9, #(1 << 2) /* C, D-cache */ + bic x9, x9, #(1 << 12) /* I, I-cache */ + msr sctlr_el1, x9 + isb + + br x8 +ENDPROC(tb_boot_linux) +.popsection diff --git a/arch/arm64/kernel/exceptions.c b/arch/arm64/kernel/exceptions.c new file mode 100644 index 0000000..7b40690 --- /dev/null +++ b/arch/arm64/kernel/exceptions.c @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * exceptions.c - report an abort through the console before parking, + * so a firmware handoff bug says why it died instead of hanging quiet. + * ESR/FAR decode follows armv8 DDI 0487, the EC and ISS fields. + * + * Copyright (C) 2026 Bradley Morgan + */ + +#include +#include + +struct exc_frame { + uint64_t esr; + uint64_t far; + uint64_t lr; +}; + +/* + * exception class from ESR, bits 31:26. the classes a bootloader can + * actually hit with any frequency. + */ +static const char *exc_class_str(uint64_t esr) +{ + switch (esr >> 26) { + case 0x04: return "data abort, lower EL"; + case 0x05: return "data abort, same EL"; + case 0x25: return "data abort, same EL"; + case 0x08: return "stack pointer misaligned"; + case 0x11: return "instruction abort, same EL"; + case 0x16: return "SError"; + case 0x1a: return "unhandled exception"; + case 0x22: return "pc alignment fault"; + case 0x24: return "unknown trap"; + case 0x26: return "same EL exception return"; + default: return "unknown EC"; + } +} + +/* + * far is only meaningful for the abort and alignment classes, note it + * for those and skip it otherwise so the report does not mislead. + */ +static int exc_far_valid(uint64_t esr) +{ + switch (esr >> 26) { + case 0x04: + case 0x05: + case 0x25: + case 0x11: + case 0x22: + return 1; + default: + return 0; + } +} + +void exc_report(uint64_t esr, uint64_t far, uint64_t lr) +{ + dprintf(CRITICAL, "tashaboot: exception %s\n", exc_class_str(esr)); + dprintf(CRITICAL, "esr %016llx lr %016llx\n", + (unsigned long long)esr, (unsigned long long)lr); + if (exc_far_valid(esr)) + dprintf(CRITICAL, "far %016llx\n", (unsigned long long)far); + + /* nothing recovers from an abort here, park after reporting */ +} diff --git a/arch/arm64/kernel/halt.c b/arch/arm64/kernel/halt.c new file mode 100644 index 0000000..d91d39a --- /dev/null +++ b/arch/arm64/kernel/halt.c @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * halt.c - stop the core, the ARM ARM's WFI loop. nothing recovers + * from a halt, the machine needs a reset. + * + * Copyright (C) 2026 Bradley Morgan + */ + +#include + +void platform_halt(void) +{ + dprintf(ALWAYS, "HALT: spinning forever...\n"); + + for (;;) { + asm volatile("wfi"); + } +} diff --git a/arch/arm64/kernel/start.S b/arch/arm64/kernel/start.S new file mode 100644 index 0000000..705721f --- /dev/null +++ b/arch/arm64/kernel/start.S @@ -0,0 +1,259 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * tashaboot arm64 entry. handles whatever EL the firmware left us in, + * EL3, EL2 or EL1, with the MMU either on or off, and arrives at a + * clean EL1 with the MMU off before calling C. + * + * the secondary cores park, spin table bringup is a later problem. + * + * Copyright (C) 2026 Bradley Morgan + */ + +#include + +.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 + + /* which EL are we in, 0x8 per level shifted into bits 3:2 */ + 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 mmu_check + b park + +from_el3: + /* + * EL3 holds the security state. the kernel runs non-secure, so + * set SCR_EL3.NS before dropping to EL2, which the kernel + * prefers (booting.rst, EL2 RECOMMENDED). + */ + mrs x0, scr_el3 + orr x0, x0, #1 /* SCR_EL3.NS = 1, non-secure */ + msr scr_el3, x0 + isb + + mov x0, #0x3c9 /* EL2h, DAIF masked */ + msr spsr_el3, x0 + adr x0, from_el2 + msr elr_el3, x0 + eret + +from_el2: + /* + * stay at EL2: the kernel wants it for the virtualization + * extensions and hands off from there. everything below scrubs + * the EL2 state so the kernel starts clean. + */ + + /* EL1 will be aarch64 when the kernel drops itself down */ + mov x0, #(1 << 31) /* HCR_EL2.RW = 1 */ + msr hcr_el2, x0 + + /* let EL1 reach the counter, booting.rst demands it */ + mrs x0, cnthctl_el2 + orr x0, x0, #(3 << 0) /* EL1PCTEN | EL1PCEN */ + msr cnthctl_el2, x0 + + /* no traps to EL2 behind EL1's back */ + msr cptr_el2, xzr + msr hstr_el2, xzr + msr vpidr_el2, xzr + + b mmu_check + +mmu_check: + /* + * whether the firmware left an MMU on: M bit, bit 0, of sctlr at + * the current EL. writing the register off would not fault, but + * the page tables it built are in its own memory, better to kill + * it here than trip over a stale mapping. + */ + mrs x0, CurrentEL + lsr x0, x0, #2 + cmp x0, #2 + b.lt mmu_el1 + mrs x0, sctlr_el2 + tbz x0, #0, c_entry + + mov x0, xzr + msr sctlr_el2, x0 + isb + tlbi alle2 + dsb sy + isb + b c_entry + +mmu_el1: + mrs x0, sctlr_el1 + tbz x0, #0, c_entry + + mov x0, xzr + msr sctlr_el1, x0 + isb + ic iallu + dsb sy + tlbi vmalle1 + dsb sy + isb + +c_entry: + /* + * program the counter frequency, the kernel reads CNTFRQ right + * away (booting.rst). qemu virt runs the system counter at + * 62.5 MHz. the register is RW only at the highest implemented EL. + */ + mrs x0, CurrentEL + lsr x0, x0, #2 + cmp x0, #2 + b.lt 1f + ldr x0, =62500000 + msr cntfrq_el0, x0 + isb +1: + /* our own vectors, so aborts print instead of vanishing */ + adr x0, vectors + mrs x1, CurrentEL + lsr x1, x1, #2 + cmp x1, #2 + b.lt 2f + msr vbar_el2, x0 + b 3f +2: + msr vbar_el1, x0 +3: + isb + + /* 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: + + /* FP/SIMD access, some kernels assume it is on */ + mov x0, #(3 << 20) + msr cpacr_el1, x0 + isb + + /* 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 + +/* + * exception vectors, the armv8 layout: 16 slots, 128 bytes each, in + * the order the manual fixes. taken from EL1h the interesting slots + * are 0x200 sync and 0x380 SError, irq and fiq just park, the + * bootloader never enables interrupts on purpose. + */ + .balign 2048 +vectors: + /* 0x000: current EL, SP_EL0 */ + .align 7 + b exc_sync + .align 7 + b exc_park_irq + .align 7 + b exc_park_irq + .align 7 + b exc_serr + + /* 0x200: current EL, SP_ELx */ + .align 7 + b exc_sync + .align 7 + b exc_park_irq + .align 7 + b exc_park_irq + .align 7 + b exc_serr + + /* 0x400: lower EL, AArch64 */ + .align 7 + b exc_sync + .align 7 + b exc_park_irq + .align 7 + b exc_park_irq + .align 7 + b exc_serr + + /* 0x600: lower EL, AArch32 */ + .align 7 + b exc_sync + .align 7 + b exc_park_irq + .align 7 + b exc_park_irq + .align 7 + b exc_serr + +exc_sync: + stp x29, x30, [sp, #-16]! + mov x29, sp + mrs x3, CurrentEL + lsr x3, x3, #2 + cmp x3, #2 + b.lt 1f + mrs x0, esr_el2 + mrs x1, far_el2 + b 2f +1: + mrs x0, esr_el1 + mrs x1, far_el1 +2: + mov x2, lr + bl exc_report + ldp x29, x30, [sp], #16 + b park + +exc_serr: + stp x29, x30, [sp, #-16]! + mov x29, sp + mrs x3, CurrentEL + lsr x3, x3, #2 + cmp x3, #2 + b.lt 1f + mrs x0, esr_el2 + b 2f +1: + mrs x0, esr_el1 +2: + mov x1, #0 + mov x2, lr + bl exc_report + ldp x29, x30, [sp], #16 + b park + +exc_park_irq: + b park diff --git a/arch/arm64/kernel/tashaboot.lds b/arch/arm64/kernel/tashaboot.lds new file mode 100644 index 0000000..4f8dfb1 --- /dev/null +++ b/arch/arm64/kernel/tashaboot.lds @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * tashaboot arm64 memory layout. one segment, loaded at the bottom of + * RAM, right where qemu -kernel drops a raw image on virt. + * + * Copyright (C) 2026 Bradley Morgan + */ + +OUTPUT_FORMAT("elf64-littleaarch64", "elf64-littleaarch64", "elf64-littleaarch64") +OUTPUT_ARCH(aarch64) +ENTRY(_start) + +SECTIONS +{ + . = 0x40000000; + + __image_copy_start = .; + _text_start = .; + + .text : + { + arch/arm64/kernel/start.o (.text.boot) + *(.text.boot) + *(.text*) + } + + . = ALIGN(8); + __text_end = .; + + .rodata : + { + *(SORT_BY_ALIGNMENT(.rodata*)) + } + + . = ALIGN(8); + __rodata_end = .; + + .data : + { + *(.data*) + } + + . = ALIGN(8); + __image_end = .; + + __bss_start = .; + .bss : + { + *(.bss*) + *(COMMON) + } + . = ALIGN(8); + __bss_end = .; + + __image_copy_end = .; + + /DISCARD/ : { *(.dynsym) } + /DISCARD/ : { *(.dynstr*) } + /DISCARD/ : { *(.dynamic*) } + /DISCARD/ : { *(.plt*) } + /DISCARD/ : { *(.interp*) } + /DISCARD/ : { *(.gnu*) } + /DISCARD/ : { *(.ARM.attributes) } + /DISCARD/ : { *(.comment) } + /DISCARD/ : { *(.note*) } + /DISCARD/ : { *(.eh_frame*) } +} diff --git a/arch/arm64/lib/cache.S b/arch/arm64/lib/cache.S new file mode 100644 index 0000000..d8ccea2 --- /dev/null +++ b/arch/arm64/lib/cache.S @@ -0,0 +1,100 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * cache.S - set/way cache maintenance, walked off CLIDR_EL1 the same + * way u-boot and the kernel's own __flush_dcache_all do it. needed + * before jumping to the payload so it starts from memory, not cache. + * + * Copyright (C) 2026 Bradley Morgan + */ + +#include + +.pushsection .text.tb_dcache_level, "ax" +ENTRY(tb_dcache_level) + lsl x12, x0, #1 + msr csselr_el1, x12 /* select cache level */ + isb /* sync change of ccsidr_el1 */ + mrs x6, ccsidr_el1 /* read the new ccsidr_el1 */ + ubfx x2, x6, #0, #3 /* x2 <- log2(cache line size)-4 */ + ubfx x3, x6, #3, #10 /* x3 <- number of cache ways - 1 */ + ubfx x4, x6, #13, #15 /* x4 <- number of cache sets - 1 */ + add x2, x2, #4 /* x2 <- log2(cache line size) */ + clz w5, w3 /* x5 <- bit position of #ways */ + /* x12 <- cache level << 1 */ + /* x2 <- line length offset */ + /* x3 <- number of cache ways - 1 */ + /* x4 <- number of cache sets - 1 */ + /* x5 <- bit position of #ways */ + +loop_set: + mov x6, x3 /* x6 <- working copy of #ways */ +loop_way: + lsl x7, x6, x5 + orr x9, x12, x7 /* map way and level to cisw value */ + lsl x7, x4, x2 + orr x9, x9, x7 /* map set number to cisw value */ + dc cisw, x9 /* clean & invalidate by set/way */ + subs x6, x6, #1 /* decrement the way */ + b.ge loop_way + subs x4, x4, #1 /* decrement the set */ + b.ge loop_set + + ret +ENDPROC(tb_dcache_level) +.popsection + +/* + * void tb_flush_dcache_all(void) + * + * clean & invalidate the whole D cache by set/way. + */ +.pushsection .text.tb_flush_dcache_all, "ax" +ENTRY(tb_flush_dcache_all) + mov x1, x0 + dsb sy + mrs x10, clidr_el1 /* read clidr_el1 */ + ubfx x11, x10, #24, #3 /* x11 <- loc */ + cbz x11, finished /* if loc is 0, exit */ + mov x15, lr + mov x0, #0 /* start flush at cache level 0 */ + /* x0 <- cache level */ + /* x10 <- clidr_el1 */ + /* x11 <- loc */ + /* x15 <- return address */ + +loop_level: + add x12, x0, x0, lsl #1 /* x12 <- tripled cache level */ + lsr x12, x10, x12 + and x12, x12, #7 /* x12 <- cache type */ + cmp x12, #2 + b.lt skip /* skip if no cache or icache */ + bl tb_dcache_level /* flush this level */ +skip: + add x0, x0, #1 /* increment cache level */ + cmp x11, x0 + b.gt loop_level + + mov x0, #0 + msr csselr_el1, x0 /* restore csselr_el1 */ + dsb sy + isb + mov lr, x15 + +finished: + ret +ENDPROC(tb_flush_dcache_all) +.popsection + +/* + * void tb_invalidate_icache_all(void) + * + * I cache invalidation to PoU, one ic iallu covers the local core. + */ +.pushsection .text.tb_invalidate_icache_all, "ax" +ENTRY(tb_invalidate_icache_all) + ic iallu + dsb sy + isb + ret +ENDPROC(tb_invalidate_icache_all) +.popsection diff --git a/arch/arm64/lib/semihosting.S b/arch/arm64/lib/semihosting.S new file mode 100644 index 0000000..6e3fc31 --- /dev/null +++ b/arch/arm64/lib/semihosting.S @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * semihosting.S - the trap instruction itself. qemu answers this when + * it is started with -semihosting, and nothing happens without it, so + * every caller has to cope with the no-debugger case. + * + * Copyright (C) 2026 Bradley Morgan + */ + +#include + +.pushsection .text.smh_trap, "ax" +/* long smh_trap(unsigned int sysnum, void *addr); */ +ENTRY(smh_trap) + hlt #0xf000 + ret +ENDPROC(smh_trap) +.popsection diff --git a/common/console.c b/common/console.c new file mode 100644 index 0000000..a7108ba --- /dev/null +++ b/common/console.c @@ -0,0 +1,74 @@ +/* + * console.c - the console dprintf writes to. semihosting SYS_WRITE0, + * the firmware service on the qemu dev path, the arm64 stand-in for + * the bios teletype the osdev loaders use. on real hardware this is + * the one file that changes. + * + * Copyright (c) 2026 Bradley Morgan + * + * 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 +#include +#include + +/* + * lk's _dprintf sink. printf buffers a line here then hands it to the + * host, semihosting wants zero terminated strings not counts. + */ +#define TB_CONSOLE_MAX 256 + +static char console_buf[TB_CONSOLE_MAX]; +static size_t console_len; + +static void console_flush(void) +{ + if (console_len == 0) + return; + console_buf[console_len] = '\0'; + smh_write0(console_buf); + console_len = 0; +} + +void _putchar(char c) +{ + if (console_len >= TB_CONSOLE_MAX - 1) + console_flush(); + if (c == '\n') { + /* the host terminal wants cr lf, not lf alone */ + console_buf[console_len++] = '\r'; + } + console_buf[console_len++] = c; + if (c == '\n') + console_flush(); +} + +int tb_console_init(void) +{ + /* + * the probe is one harmless call: SYS_GET_ERRNO with no file + * handle open. a host answers, bare metal ignores the trap. + */ + if (!smh_probe()) + return -1; + console_len = 0; + return 0; +} diff --git a/common/image.c b/common/image.c new file mode 100644 index 0000000..640eab4 --- /dev/null +++ b/common/image.c @@ -0,0 +1,77 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * image.c - arm64 kernel Image validation and placement. + * + * The relocation rules are the ones from the kernel boot protocol, + * including the pre a2c1d73b94ed quirks, same math u-boot's + * booti_setup() runs. + * + * Copyright (C) 2026 Bradley Morgan + */ + +#include +#include +#include +#include + +#define LINUX_ARM64_IMAGE_MAGIC 0x644d5241 /* "ARM\x64" */ + +#define SZ_16M 0x01000000 +#define SZ_2M 0x00200000 + +/* the 64 byte header from Documentation/arch/arm64/booting.rst */ +struct Image_header { + uint32 code0; /* executable */ + uint32 code1; /* unused */ + uint64 text_offset; /* load offset, LE */ + uint64 image_size; /* size, LE */ + uint64 flags; /* bit 3: relocatable */ + uint64 res1; + uint64 res2; + uint64 res3; + uint32 magic; /* "ARM\x64" */ + uint32 res4; +}; + +int tb_image_setup(uintptr_t image, struct tb_image *img) +{ + const struct Image_header *ih = (const struct Image_header *)image; + uint64_t image_size, text_offset; + + if (le32_to_cpu(ih->magic) != LINUX_ARM64_IMAGE_MAGIC) + return -1; + + if (le64_to_cpu(ih->image_size) == 0) { + /* ancient image, no size field, assume the old defaults */ + image_size = SZ_16M; + text_offset = 0x80000; + } else { + image_size = le64_to_cpu(ih->image_size); + text_offset = le64_to_cpu(ih->text_offset); + } + + /* + * flag bit 3 says the image can live anywhere, honour where it + * already is. otherwise the base must be 2MB aligned, the + * physical offset from there is text_offset. + */ + if (le64_to_cpu(ih->flags) & (1ULL << 3)) { + uintptr_t base = image - text_offset; + + img->load = ((base + SZ_2M - 1) & ~(uintptr_t)(SZ_2M - 1)) + + text_offset; + } else { + /* + * no relocate flag: the image must sit text_offset from a + * 2MB aligned base. it is already staged at the fixed + * address, treat its own position as the answer. + */ + img->load = image; + } + + /* the whole image, header included, lives at load, entry is code0 */ + img->ep = img->load; + img->size = image_size; + + return 0; +} diff --git a/common/load.c b/common/load.c new file mode 100644 index 0000000..cb3b41d --- /dev/null +++ b/common/load.c @@ -0,0 +1,57 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * load.c - pull the payload into RAM. semihosting is the whole story + * for now, the bios INT 13h of this loader: the host serves the file, + * we read it at the fixed address and let the image header place it. + * + * Copyright (C) 2026 Bradley Morgan + */ + +#include +#include +#include +#include +#include +#include + +int tb_load_semihosting(const char *fname, uintptr_t load_addr, + struct tb_image *img) +{ + long fd, len, ret; + + fd = smh_open(fname, MODE_READ | MODE_BINARY); + if (fd < 0) + return fd; + + len = smh_flen(fd); + if (len < 0) { + smh_close(fd); + return len; + } + + /* header first so placement is known before the big copy */ + ret = smh_read(fd, (void *)load_addr, 64); + if (ret != 64) { + smh_close(fd); + return -3; + } + + if (tb_image_setup(load_addr, img)) { + smh_close(fd); + return -4; + } + + if (img->load != load_addr) { + /* the image wants to sit elsewhere, copy the header there */ + memmove((void *)img->load, (void *)load_addr, 64); + } + + ret = smh_read(fd, (void *)(img->load + 64), len - 64); + if (ret != len - 64) { + smh_close(fd); + return -5; + } + + smh_close(fd); + return 0; +} diff --git a/common/main.c b/common/main.c new file mode 100644 index 0000000..fb388cb --- /dev/null +++ b/common/main.c @@ -0,0 +1,75 @@ +/* + * 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 + * + * 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 +#include +#include +#include +#include +#include +#include + +#define TB_VERSION "0.1" + +extern int tb_console_init(void); + +/* + * fixed load address, the osdev way. past the bootloader at the + * bottom of RAM, the image header decides its final resting place. + */ +#define TB_LOAD_ADDR 0x40200000 + +/* 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); + +void tashaboot_main(uintptr_t fw_arg) +{ + struct tb_image img; + int ret; + + if (tb_console_init()) + return; + + dprintf(ALWAYS, "tashaboot " TB_VERSION "\n"); + + ret = tb_load_semihosting(TB_BOOTFILE, TB_LOAD_ADDR, &img); + if (ret) { + dprintf(ALWAYS, "load failed (%d), halting\n", ret); + platform_halt(); + } + + dprintf(ALWAYS, "loaded %llu bytes at %lx, entry %lx\n", + (unsigned long long)img.size, img.load, img.ep); + dprintf(ALWAYS, "jumping\n"); + + tb_boot_linux(img.ep, fw_arg); +} diff --git a/include/assert.h b/include/assert.h new file mode 100644 index 0000000..ecdfaca --- /dev/null +++ b/include/assert.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2008 Travis Geiselbrecht + * + * 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. + */ +#ifndef __ASSERT_H +#define __ASSERT_H + +#include +#include + +#define ASSERT(x) \ + do { if (unlikely(!(x))) { panic("ASSERT FAILED at (%s:%d): %s\n", __FILE__, __LINE__, #x); } } while (0) +#define assert(x) ASSERT(x) + +#if DEBUGLEVEL > 1 +#define DEBUG_ASSERT(x) \ + do { if (unlikely(!(x))) { panic("DEBUG ASSERT FAILED at (%s:%d): %s\n", __FILE__, __LINE__, #x); } } while (0) +#else +#define DEBUG_ASSERT(x) \ + do { } while(0) +#endif + +#endif diff --git a/include/boot.h b/include/boot.h new file mode 100644 index 0000000..a5fe3ae --- /dev/null +++ b/include/boot.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __BOOT_H +#define __BOOT_H + +#include +#include + +/* + * payload loading and the arm64 boot protocol. See Documentation/ + * arch/arm64/booting.rst in the kernel tree for the contract this + * implements. + */ + +/* + * where an image landed after the loader placed it. ep is the first + * instruction executed, the whole image including its 64 byte header + * sits at load. + */ +struct tb_image { + uintptr_t load; /* image start in memory */ + uintptr_t ep; /* entry point */ + size_t size; /* total image size */ +}; + +/* common/image.c, the arm64 Image header math */ +int tb_image_setup(uintptr_t image, struct tb_image *img); + +/* common/load.c */ +int tb_load_semihosting(const char *fname, uintptr_t load_addr, + struct tb_image *img); + +#endif diff --git a/include/compiler.h b/include/compiler.h new file mode 100644 index 0000000..e659047 --- /dev/null +++ b/include/compiler.h @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2008 Travis Geiselbrecht + * + * 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. + */ +#ifndef __COMPILER_H +#define __COMPILER_H + +#ifndef __ASSEMBLY__ + +#if __GNUC__ +#define likely(x) __builtin_expect(!!(x), 1) +#define unlikely(x) __builtin_expect(!!(x), 0) +#define __USED __attribute__((__used__)) +#define __UNUSED __attribute__((__unused__)) +#define __PACKED __attribute__((packed)) +#define __ALIGNED(x) __attribute__((aligned(x))) +#define __PRINTFLIKE(__fmt,__varargs) __attribute__((__format__ (__printf__, __fmt, __varargs))) +#define __SCANFLIKE(__fmt,__varargs) __attribute__((__format__ (__scanf__, __fmt, __varargs))) +#define __SECTION(x) __attribute((section(x))) +#define __PURE __attribute((pure)) +#define __CONST __attribute((const)) +#define __NO_RETURN __attribute__((noreturn)) +#define __MALLOC __attribute__((malloc)) +#define __WEAK __attribute__((weak)) +#define __GNU_INLINE __attribute__((gnu_inline)) +#define __GET_CALLER(x) __builtin_return_address(0) +#define __GET_FRAME(x) __builtin_frame_address(0) + +#define INCBIN(symname, sizename, filename, section) \ + __asm__ (".section " section "; .align 4; .globl "#symname); \ + __asm__ (""#symname ":\n.incbin \"" filename "\""); \ + __asm__ (".section " section "; .align 1;"); \ + __asm__ (""#symname "_end:"); \ + __asm__ (".section " section "; .align 4; .globl "#sizename); \ + __asm__ (""#sizename ": .long "#symname "_end - "#symname " - 1"); \ + extern unsigned char symname[]; \ + extern unsigned int sizename + +#define INCFILE(symname, sizename, filename) INCBIN(symname, sizename, filename, ".rodata") + +/* look for gcc 3.0 and above */ +#if (__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 0) +#define __ALWAYS_INLINE __attribute__((always_inline)) +#else +#define __ALWAYS_INLINE +#endif + +/* look for gcc 3.1 and above */ +#if !defined(__DEPRECATED) // seems to be built in in some versions of the compiler +#if (__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1) +#define __DEPRECATED __attribute((deprecated)) +#else +#define __DEPRECATED +#endif +#endif + +/* look for gcc 3.3 and above */ +#if (__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3) +/* the may_alias attribute was introduced in gcc 3.3; before that, there + * was no way to specify aliasiang rules on a type-by-type basis */ +#define __MAY_ALIAS __attribute__((may_alias)) + +/* nonnull was added in gcc 3.3 as well */ +#define __NONNULL(x) __attribute((nonnull x)) +#else +#define __MAY_ALIAS +#define __NONNULL(x) +#endif + +/* look for gcc 3.4 and above */ +#if (__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) +#define __WARN_UNUSED_RESULT __attribute((warn_unused_result)) +#else +#define __WARN_UNUSED_RESULT +#endif + +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) +#define __EXTERNALLY_VISIBLE __attribute__((externally_visible)) +#else +#define __EXTERNALLY_VISIBLE +#endif + +#else + +#define likely(x) (x) +#define unlikely(x) (x) +#define __USED +#define __UNUSED +#define __PACKED +#define __ALIGNED(x) +#define __PRINTFLIKE(__fmt,__varargs) +#define __SCANFLIKE(__fmt,__varargs) +#define __SECTION(x) +#define __PURE +#define __CONST +#define __NONNULL(x) +#define __DEPRECATED +#define __WARN_UNUSED_RESULT +#define __ALWAYS_INLINE +#define __MAY_ALIAS +#define __NO_RETURN +#endif + +#endif + +/* TODO: add type check */ +#define countof(a) (sizeof(a) / sizeof((a)[0])) + +#define IS_ENABLED(define) _IS_ENABLED(define) +#define _comma_if_enabled_1 , +#define _IS_ENABLED(value) __IS_ENABLED(_comma_if_enabled_##value) +#define __IS_ENABLED(comma) ___IS_ENABLED(comma 1, 0) +#define ___IS_ENABLED(_, enabled, ...) enabled + +#endif diff --git a/include/ctype.h b/include/ctype.h new file mode 100644 index 0000000..7f9982e --- /dev/null +++ b/include/ctype.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2008 Travis Geiselbrecht + * + * 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. + */ +#ifndef __CTYPE_H +#define __CTYPE_H + +int isalnum(int c); +int isalpha(int c); +int isblank(int c); +int iscntrl(int c); +int isdigit(int c); +int isgraph(int c); +int islower(int c); +int isprint(int c); +int ispunct(int c); +int isspace(int c); +int isupper(int c); +int isxdigit(int c); + +int tolower(int c); +int toupper(int c); + +#endif + diff --git a/include/debug.h b/include/debug.h new file mode 100644 index 0000000..83b62b2 --- /dev/null +++ b/include/debug.h @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2008 Travis Geiselbrecht + * + * Copyright (c) 2014, The Linux Foundation. All rights reserved. + * + * 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. + */ +#ifndef __DEBUG_H +#define __DEBUG_H + +#include +#include +#include +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +#if defined(DEBUG) +#define DEBUGLEVEL DEBUG +#else +#define DEBUGLEVEL 2 +#endif + +/* debug levels */ +#define CRITICAL 0 +#define ALWAYS 0 +#define INFO 1 +#define SPEW 2 + +/* output */ +void _dputc(char c); // XXX for now, platform implements +int _dputs(const char *str); +int _dprintf(const char *fmt, ...) __PRINTFLIKE(1, 2); +int _dvprintf(const char *fmt, va_list ap); + +#define dputc(level, str) do { if ((level) <= DEBUGLEVEL) { _dputc(str); } } while (0) +#define dputs(level, str) do { if ((level) <= DEBUGLEVEL) { _dputs(str); } } while (0) +#define dprintf(level, x...) do { if ((level) <= DEBUGLEVEL) { _dprintf(x); } } while (0) +#define dvprintf(level, x...) do { if ((level) <= DEBUGLEVEL) { _dvprintf(x); } } while (0) + +/* input */ +int dgetc(char *c, bool wait); + +/* systemwide halts */ +void halt(void); + +void dump_frame(void *frame); + +void _panic(void *caller, const char *fmt, ...) __PRINTFLIKE(2, 3); +#define panic(x...) _panic(__GET_CALLER(), x) + +#define PANIC_UNIMPLEMENTED panic("%s unimplemented\n", __PRETTY_FUNCTION__) + +extern uintptr_t __stack_chk_guard; + +/* +* Initialize the stack protector canary value. +*/ +#define __stack_chk_guard_setup() do { __stack_chk_guard = get_canary(); } while(0) + +void __attribute__ ((noreturn)) + __stack_chk_fail (void); + +/* spin the cpu for a period of (short) time */ +void spin(uint32_t usecs); + +/* dump memory */ +void hexdump(const void *ptr, size_t len); +void hexdump8(const void *ptr, size_t len); + +/* trace routines */ +#define TRACE_ENTRY printf("%s: entry\n", __PRETTY_FUNCTION__) +#define TRACE_EXIT printf("%s: exit\n", __PRETTY_FUNCTION__) +#define TRACE_ENTRY_OBJ printf("%s: entry obj %p\n", __PRETTY_FUNCTION__, this) +#define TRACE_EXIT_OBJ printf("%s: exit obj %p\n", __PRETTY_FUNCTION__, this) +#define TRACE printf("%s:%d\n", __PRETTY_FUNCTION__, __LINE__) +#define TRACEF(x...) do { printf("%s:%d: ", __PRETTY_FUNCTION__, __LINE__); printf(x); } while (0) + +/* trace routines that work if LOCAL_TRACE is set */ +#define LTRACE_ENTRY do { if (LOCAL_TRACE) { TRACE_ENTRY; } } while (0) +#define LTRACE_EXIT do { if (LOCAL_TRACE) { TRACE_EXIT; } } while (0) +#define LTRACE do { if (LOCAL_TRACE) { TRACE; } } while (0) +#define LTRACEF(x...) do { if (LOCAL_TRACE) { TRACEF(x); } } while (0) + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/include/endian.h b/include/endian.h new file mode 100644 index 0000000..42f880c --- /dev/null +++ b/include/endian.h @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * endian.h - byte order swaps. arm64 and ppc64le are both little + * endian, so the macros compile to nothing there, but the big endian + * fallbacks stay so a BE target builds without a rewrite. + * + * Copyright (C) 2026 Bradley Morgan + */ + +#ifndef __ENDIAN_H +#define __ENDIAN_H + +#include + +static inline uint16_t __swap16(uint16_t x) +{ + return (uint16_t)((x << 8) | (x >> 8)); +} + +static inline uint32_t __swap32(uint32_t x) +{ + return ((x << 24) | + ((x & 0xff00) << 8) | + ((x & 0xff0000) >> 8) | + (x >> 24)); +} + +static inline uint64_t __swap64(uint64_t x) +{ + return ((uint64_t)__swap32((uint32_t)x) << 32) | + __swap32((uint32_t)(x >> 32)); +} + +#if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + +#define le16_to_cpu(x) __swap16(x) +#define le32_to_cpu(x) __swap32(x) +#define le64_to_cpu(x) __swap64(x) +#define cpu_to_le16(x) __swap16(x) +#define cpu_to_le32(x) __swap32(x) +#define cpu_to_le64(x) __swap64(x) + +#else + +#define le16_to_cpu(x) (x) +#define le32_to_cpu(x) (x) +#define le64_to_cpu(x) (x) +#define cpu_to_le16(x) (x) +#define cpu_to_le32(x) (x) +#define cpu_to_le64(x) (x) + +#endif + +#endif diff --git a/include/limits.h b/include/limits.h new file mode 100644 index 0000000..ec83068 --- /dev/null +++ b/include/limits.h @@ -0,0 +1,121 @@ +/* This administrivia gets added to the beginning of limits.h + if the system has its own version of limits.h. */ + +/* We use _GCC_LIMITS_H_ because we want this not to match + any macros that the system's limits.h uses for its own purposes. */ +#ifndef _GCC_LIMITS_H_ /* Terminated in limity.h. */ +#define _GCC_LIMITS_H_ + +#ifndef _LIMITS_H___ +#define _LIMITS_H___ + +/* Number of bits in a `char'. */ +#undef CHAR_BIT +#define CHAR_BIT __CHAR_BIT__ + +/* Maximum length of a multibyte character. */ +#ifndef MB_LEN_MAX +#define MB_LEN_MAX 1 +#endif + +/* Minimum and maximum values a `signed char' can hold. */ +#undef SCHAR_MIN +#define SCHAR_MIN (-SCHAR_MAX - 1) +#undef SCHAR_MAX +#define SCHAR_MAX __SCHAR_MAX__ + +/* Maximum value an `unsigned char' can hold. (Minimum is 0). */ +#undef UCHAR_MAX +#if __SCHAR_MAX__ == __INT_MAX__ +# define UCHAR_MAX (SCHAR_MAX * 2U + 1U) +#else +# define UCHAR_MAX (SCHAR_MAX * 2 + 1) +#endif + +/* Minimum and maximum values a `char' can hold. */ +#ifdef __CHAR_UNSIGNED__ +# undef CHAR_MIN +# if __SCHAR_MAX__ == __INT_MAX__ +# define CHAR_MIN 0U +# else +# define CHAR_MIN 0 +# endif +# undef CHAR_MAX +# define CHAR_MAX UCHAR_MAX +#else +# undef CHAR_MIN +# define CHAR_MIN SCHAR_MIN +# undef CHAR_MAX +# define CHAR_MAX SCHAR_MAX +#endif + +/* Minimum and maximum values a `signed short int' can hold. */ +#undef SHRT_MIN +#define SHRT_MIN (-SHRT_MAX - 1) +#undef SHRT_MAX +#define SHRT_MAX __SHRT_MAX__ + +/* Maximum value an `unsigned short int' can hold. (Minimum is 0). */ +#undef USHRT_MAX +#if __SHRT_MAX__ == __INT_MAX__ +# define USHRT_MAX (SHRT_MAX * 2U + 1U) +#else +# define USHRT_MAX (SHRT_MAX * 2 + 1) +#endif + +/* Minimum and maximum values a `signed int' can hold. */ +#undef INT_MIN +#define INT_MIN (-INT_MAX - 1) +#undef INT_MAX +#define INT_MAX __INT_MAX__ + +/* Maximum value an `unsigned int' can hold. (Minimum is 0). */ +#undef UINT_MAX +#define UINT_MAX (INT_MAX * 2U + 1U) + +/* Minimum and maximum values a `signed long int' can hold. + (Same as `int'). */ +#undef LONG_MIN +#define LONG_MIN (-LONG_MAX - 1L) +#undef LONG_MAX +#define LONG_MAX __LONG_MAX__ + +/* Maximum value an `unsigned long int' can hold. (Minimum is 0). */ +#undef ULONG_MAX +#define ULONG_MAX (LONG_MAX * 2UL + 1UL) + +#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +/* Minimum and maximum values a `signed long long int' can hold. */ +# undef LLONG_MIN +# define LLONG_MIN (-LLONG_MAX - 1LL) +# undef LLONG_MAX +# define LLONG_MAX __LONG_LONG_MAX__ + +/* Maximum value an `unsigned long long int' can hold. (Minimum is 0). */ +# undef ULLONG_MAX +# define ULLONG_MAX (LLONG_MAX * 2ULL + 1ULL) +#endif + +#if defined (__GNU_LIBRARY__) ? defined (__USE_GNU) : !defined (__STRICT_ANSI__) +/* Minimum and maximum values a `signed long long int' can hold. */ +# undef LONG_LONG_MIN +# define LONG_LONG_MIN (-LONG_LONG_MAX - 1LL) +# undef LONG_LONG_MAX +# define LONG_LONG_MAX __LONG_LONG_MAX__ + +/* Maximum value an `unsigned long long int' can hold. (Minimum is 0). */ +# undef ULONG_LONG_MAX +# define ULONG_LONG_MAX (LONG_LONG_MAX * 2ULL + 1ULL) +#endif + +#endif /* _LIMITS_H___ */ +/* This administrivia gets added to the end of limits.h + if the system has its own version of limits.h. */ + +#else /* not _GCC_LIMITS_H_ */ + +#ifdef _GCC_NEXT_LIMITS_H +#include_next /* recurse down to the real one */ +#endif + +#endif /* not _GCC_LIMITS_H_ */ diff --git a/include/platform/debug.h b/include/platform/debug.h new file mode 100644 index 0000000..38f30a7 --- /dev/null +++ b/include/platform/debug.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2008 Travis Geiselbrecht + * Copyright (c) 2026 Bradley Morgan + * + * 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. + */ +#ifndef __PLATFORM_DEBUG_H +#define __PLATFORM_DEBUG_H + +#include +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +int _dprintf(const char *fmt, ...) __PRINTFLIKE(1, 2); +int _dvprintf(const char *fmt, va_list ap); + +void platform_halt(void); + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/include/printf.h b/include/printf.h new file mode 100644 index 0000000..92981df --- /dev/null +++ b/include/printf.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2008 Travis Geiselbrecht + * + * 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. + */ +#ifndef __LIB_PRINTF_H +#define __LIB_PRINTF_H + +#include +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +int printf(const char *fmt, ...); +int sprintf(char *str, const char *fmt, ...) __PRINTFLIKE(2, 3); +int snprintf(char *str, size_t len, const char *fmt, ...) __PRINTFLIKE(3, 4); +int vsprintf(char *str, const char *fmt, va_list ap); +int vsnprintf(char *str, size_t len, const char *fmt, va_list ap); + +/* printf engine that parses the format string and generates output */ + +/* function pointer to pass the engine, + * return code is remaining characters in destination (or INT_MAX for infinity) + */ +typedef int (*_printf_engine_output_func)(char c, void *state); + +int _printf_engine(_printf_engine_output_func out, void *state, const char *fmt, va_list ap); + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/include/reg.h b/include/reg.h new file mode 100644 index 0000000..321074b --- /dev/null +++ b/include/reg.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * reg.h - mmio accessors. ld/st straight to the device, nothing cute. + * + * Copyright (C) 2026 Bradley Morgan + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __REG_H +#define __REG_H + +#include +#include + +#define REG32(addr) ((volatile uint32_t *)(uintptr_t)(addr)) + +static inline void writel(uint32_t v, volatile void *addr) +{ + *(volatile uint32_t *)addr = v; +} + +static inline uint32_t readl(const volatile void *addr) +{ + return *(const volatile uint32_t *)addr; +} + +static inline void writew(uint16_t v, volatile void *addr) +{ + *(volatile uint16_t *)addr = v; +} + +static inline uint16_t readw(const volatile void *addr) +{ + return *(const volatile uint16_t *)addr; +} + +static inline void writeb(uint8_t v, volatile void *addr) +{ + *(volatile uint8_t *)addr = v; +} + +static inline uint8_t readb(const volatile void *addr) +{ + return *(const volatile uint8_t *)addr; +} + +#endif diff --git a/include/semihosting.h b/include/semihosting.h new file mode 100644 index 0000000..450848e --- /dev/null +++ b/include/semihosting.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __SEMIHOSTING_H +#define __SEMIHOSTING_H + +#include + +/* + * semihosting file io, backed by the host when qemu runs with + * -semihosting. all calls return negative errno style errors so the + * boot flow can fall back to whatever the board has without semihosting. + */ + +enum smh_open_mode { + MODE_READ = 0x0, + MODE_BINARY = 0x1, + MODE_PLUS = 0x2, + MODE_WRITE = 0x4, + MODE_APPEND = 0x8, +}; + +long smh_open(const char *fname, enum smh_open_mode mode); +long smh_read(long fd, void *memp, size_t len); +long smh_write(long fd, const void *memp, size_t len, size_t *written); +void smh_write0(const char *str); +long smh_close(long fd); +long smh_flen(long fd); +long smh_seek(long fd, long pos); + +/* probe once, a boot without a debugger attached must not trap */ +bool smh_probe(void); + +#endif diff --git a/include/stdio.h b/include/stdio.h new file mode 100644 index 0000000..8099fb3 --- /dev/null +++ b/include/stdio.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2008-2013 Travis Geiselbrecht + * + * 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. + */ +#ifndef __STDIO_H +#define __STDIO_H + +#include +#include + +/* fake FILE struct */ +typedef struct FILE { +} FILE; + +#define stdin ((FILE *)1) +#define stdout ((FILE *)2) +#define stderr ((FILE *)3) + +int fputc(int c, FILE *fp); +#define putc(c, fp) fputc(c, fp) +int putchar(int c); +void _putchar(char c); /* the dprintf console sink */ + +int fputs(const char *s, FILE *fp); +int puts(const char *str); + +int getc(FILE *fp); +int getchar(void); + +size_t fwrite(const void *buf, size_t size, size_t count, FILE *stream); +int sscanf(const char *str, const char *format, ...); + +#endif + diff --git a/include/string.h b/include/string.h new file mode 100644 index 0000000..da2f229 --- /dev/null +++ b/include/string.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2008 Travis Geiselbrecht + * + * 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. + */ +#ifndef __LIB_STRING_H +#define __LIB_STRING_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +void *memchr (void const *, int, size_t) __PURE; +int memcmp (void const *, const void *, size_t) __PURE; +void *memcpy (void *, void const *, size_t); +void *memmove(void *, void const *, size_t); +void *memset (void *, int, size_t); + +char *strchr(char const *, int) __PURE; +int strcmp(char const *, char const *) __PURE; +char *strcpy(char *, char const *); +size_t strlen(char const *) __PURE; +int strncmp(char const *, char const *, size_t) __PURE; +char *strrchr(char const *, int) __PURE; +char *strstr(char const *, char const *) __PURE; +char *strstrn(char const *s1, char const *s2, size_t l2) __PURE; +size_t strnlen(char const *s, size_t count) __PURE; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* non standard */ +void bcopy(void const *, void *, size_t); +void bzero(void *, size_t); +size_t strlcat(char *, char const *, size_t); +size_t strlcpy(char *, char const *, size_t); +void strrev(unsigned char *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/sys/types.h b/include/sys/types.h new file mode 100644 index 0000000..70f6de1 --- /dev/null +++ b/include/sys/types.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2008-2009 Travis Geiselbrecht + * + * 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. + */ +#ifndef __SYS_TYPES_H +#define __SYS_TYPES_H + +#include +#include +#include +#include + +typedef unsigned char uchar; +typedef unsigned short ushort; +typedef unsigned int uint; +typedef unsigned long ulong; +typedef unsigned char u_char; +typedef unsigned short u_short; +typedef unsigned int u_int; +typedef unsigned long u_long; + +#ifndef _SIZE_T_DEFINED_ +typedef unsigned long size_t; +#endif +typedef long ssize_t; +typedef long long off_t; + +typedef int status_t; + +typedef uintptr_t addr_t; +typedef uintptr_t vaddr_t; +typedef uintptr_t paddr_t; + +typedef int kobj_id; + +typedef unsigned long time_t; +typedef unsigned long long bigtime_t; +typedef uint8_t uint8; +typedef uint16_t uint16; +typedef uint32_t uint32; +typedef uint64_t uint64; +typedef int8_t int8; +typedef int16_t int16; +typedef int32_t int32; +typedef bool boolean; + +#ifndef TRUE +#define TRUE true +#endif +#ifndef FALSE +#define FALSE false +#endif + +#define INFINITE_TIME ULONG_MAX + +#define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0])) + +#define TIME_GTE(a, b) ((long)((a) - (b)) >= 0) +#define TIME_LTE(a, b) ((long)((a) - (b)) <= 0) +#define TIME_GT(a, b) ((long)((a) - (b)) > 0) +#define TIME_LT(a, b) ((long)((a) - (b)) < 0) + +enum handler_return { + INT_NO_RESCHEDULE = 0, + INT_RESCHEDULE, +}; + +#endif diff --git a/lib/itoa.c b/lib/itoa.c new file mode 100644 index 0000000..4fc2db6 --- /dev/null +++ b/lib/itoa.c @@ -0,0 +1,63 @@ +/* Copyright (c) 2012, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* freestanding, no stdlib */ +#include +#include + +int +itoa(int num, unsigned char* str, int len, int base) +{ + int sum = num; + int i = 0; + int digit; + + if (len == 0) + return -1; + + do + { + digit = sum % base; + + if (digit < 0xA) + str[i++] = '0' + digit; + else + str[i++] = 'A' + digit - 0xA; + + sum /= base; + + }while (sum && (i < (len - 1))); + + if (i == (len - 1) && sum) + return -1; + + str[i] = '\0'; + strrev(str); + + return 0; +} diff --git a/lib/printf.c b/lib/printf.c new file mode 100644 index 0000000..14fa452 --- /dev/null +++ b/lib/printf.c @@ -0,0 +1,383 @@ +/* + * Copyright (c) 2008-2013 Travis Geiselbrecht + * + * 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 +#include +#include +#include +#include +#include +#include + +int printf(const char *fmt, ...) +{ + int err; + + va_list ap; + va_start(ap, fmt); + err = _dvprintf(fmt, ap); + va_end(ap); + + return err; +} + +int sprintf(char *str, const char *fmt, ...) +{ + int err; + + va_list ap; + va_start(ap, fmt); + err = vsprintf(str, fmt, ap); + va_end(ap); + + return err; +} + +int snprintf(char *str, size_t len, const char *fmt, ...) +{ + int err; + + va_list ap; + va_start(ap, fmt); + err = vsnprintf(str, len, fmt, ap); + va_end(ap); + + return err; +} + + +#define LONGFLAG 0x00000001 +#define LONGLONGFLAG 0x00000002 +#define HALFFLAG 0x00000004 +#define HALFHALFFLAG 0x00000008 +#define SIZETFLAG 0x00000010 +#define ALTFLAG 0x00000020 +#define CAPSFLAG 0x00000040 +#define SHOWSIGNFLAG 0x00000080 +#define SIGNEDFLAG 0x00000100 +#define LEFTFORMATFLAG 0x00000200 +#define LEADZEROFLAG 0x00000400 + +static char *longlong_to_string(char *buf, unsigned long long n, int len, uint flag) +{ + int pos = len; + int negative = 0; + + if((flag & SIGNEDFLAG) && (long long)n < 0) { + negative = 1; + n = -n; + } + + buf[--pos] = 0; + + /* only do the math if the number is >= 10 */ + while(n >= 10) { + int digit = n % 10; + + n /= 10; + + buf[--pos] = digit + '0'; + } + buf[--pos] = n + '0'; + + if(negative) + buf[--pos] = '-'; + else if((flag & SHOWSIGNFLAG)) + buf[--pos] = '+'; + + return &buf[pos]; +} + +static char *longlong_to_hexstring(char *buf, unsigned long long u, int len, uint flag) +{ + int pos = len; + static const char hextable[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + static const char hextable_caps[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + const char *table; + + if((flag & CAPSFLAG)) + table = hextable_caps; + else + table = hextable; + + buf[--pos] = 0; + do { + unsigned int digit = u % 16; + u /= 16; + + buf[--pos] = table[digit]; + } while(u != 0); + + return &buf[pos]; +} + +int vsprintf(char *str, const char *fmt, va_list ap) +{ + return vsnprintf(str, INT_MAX, fmt, ap); +} + +struct _output_args { + char *outstr; + size_t len; + size_t pos; +}; + +static int _vsnprintf_output(char c, void *state) +{ + struct _output_args *args = state; + + if (args->pos >= args->len) + return 0; + + args->outstr[args->pos++] = c; + + return args->len - args->pos; +} + +int vsnprintf(char *str, size_t len, const char *fmt, va_list ap) +{ + struct _output_args args; + int wlen; + + args.outstr = str; + args.len = len; + args.pos = 0; + + wlen = _printf_engine(&_vsnprintf_output, (void *)&args, fmt, ap); + if (args.pos >= len) + str[len-1] = '\0'; + else + str[wlen] = '\0'; + return wlen; +} + +int _printf_engine(_printf_engine_output_func out, void *state, const char *fmt, va_list ap) +{ + char c; + unsigned char uc; + const char *s; + unsigned long long n; + void *ptr; + int flags; + unsigned int format_num; + size_t chars_written = 0; + char num_buffer[32]; + +#define OUTPUT_CHAR(c) do { chars_written++; out(c, state); } while(0) + + for(;;) { + /* handle regular chars that aren't format related */ + while((c = *fmt++) != 0) { + if(c == '%') + break; /* we saw a '%', break and start parsing format */ + OUTPUT_CHAR(c); + } + + /* make sure we haven't just hit the end of the string */ + if(c == 0) + break; + + /* reset the format state */ + flags = 0; + format_num = 0; + +next_format: + /* grab the next format character */ + c = *fmt++; + if(c == 0) + break; + + switch(c) { + case '0'...'9': + if (c == '0' && format_num == 0) + flags |= LEADZEROFLAG; + format_num *= 10; + format_num += c - '0'; + goto next_format; + case '.': + /* XXX for now eat numeric formatting */ + goto next_format; + case '%': + OUTPUT_CHAR('%'); + break; + case 'c': + uc = va_arg(ap, unsigned int); + OUTPUT_CHAR(uc); + break; + case 's': + s = va_arg(ap, const char *); + if(s == 0) + s = ""; + goto _output_string; + case '-': + flags |= LEFTFORMATFLAG; + goto next_format; + case '+': + flags |= SHOWSIGNFLAG; + goto next_format; + case '#': + flags |= ALTFLAG; + goto next_format; + case 'l': + if(flags & LONGFLAG) + flags |= LONGLONGFLAG; + flags |= LONGFLAG; + goto next_format; + case 'h': + if(flags & HALFFLAG) + flags |= HALFHALFFLAG; + flags |= HALFFLAG; + goto next_format; + case 'z': + flags |= SIZETFLAG; + goto next_format; + case 'D': + flags |= LONGFLAG; + /* fallthrough */ + case 'i': + case 'd': + n = (flags & LONGLONGFLAG) ? va_arg(ap, long long) : + (flags & LONGFLAG) ? va_arg(ap, long) : + (flags & HALFHALFFLAG) ? (signed char)va_arg(ap, int) : + (flags & HALFFLAG) ? (short)va_arg(ap, int) : + (flags & SIZETFLAG) ? va_arg(ap, ssize_t) : + va_arg(ap, int); + flags |= SIGNEDFLAG; + s = longlong_to_string(num_buffer, n, sizeof(num_buffer), flags); + goto _output_string; + case 'U': + flags |= LONGFLAG; + /* fallthrough */ + case 'u': + n = (flags & LONGLONGFLAG) ? va_arg(ap, unsigned long long) : + (flags & LONGFLAG) ? va_arg(ap, unsigned long) : + (flags & HALFHALFFLAG) ? (unsigned char)va_arg(ap, unsigned int) : + (flags & HALFFLAG) ? (unsigned short)va_arg(ap, unsigned int) : + (flags & SIZETFLAG) ? va_arg(ap, size_t) : + va_arg(ap, unsigned int); + s = longlong_to_string(num_buffer, n, sizeof(num_buffer), flags); + goto _output_string; + case 'p': + flags |= LONGFLAG | ALTFLAG; + goto hex; + case 'X': + flags |= CAPSFLAG; + /* fallthrough */ +hex: + case 'x': + n = (flags & LONGLONGFLAG) ? va_arg(ap, unsigned long long) : + (flags & LONGFLAG) ? va_arg(ap, unsigned long) : + (flags & HALFHALFFLAG) ? (unsigned char)va_arg(ap, unsigned int) : + (flags & HALFFLAG) ? (unsigned short)va_arg(ap, unsigned int) : + (flags & SIZETFLAG) ? va_arg(ap, size_t) : + va_arg(ap, unsigned int); + s = longlong_to_hexstring(num_buffer, n, sizeof(num_buffer), flags); + if(flags & ALTFLAG) { + OUTPUT_CHAR('0'); + OUTPUT_CHAR((flags & CAPSFLAG) ? 'X': 'x'); + } + goto _output_string; + case 'n': + ptr = va_arg(ap, void *); + if(flags & LONGLONGFLAG) + *(long long *)ptr = chars_written; + else if(flags & LONGFLAG) + *(long *)ptr = chars_written; + else if(flags & HALFHALFFLAG) + *(signed char *)ptr = chars_written; + else if(flags & HALFFLAG) + *(short *)ptr = chars_written; + else if(flags & SIZETFLAG) + *(size_t *)ptr = chars_written; + else + *(int *)ptr = chars_written; + break; + default: + OUTPUT_CHAR('%'); + OUTPUT_CHAR(c); + break; + } + + /* move on to the next field */ + continue; + + /* shared output code */ +_output_string: + if (flags & LEFTFORMATFLAG) { + /* left justify the text */ + uint count = 0; + while(*s != 0) { + OUTPUT_CHAR(*s++); + count++; + } + + /* pad to the right (if necessary) */ + for (; format_num > count; format_num--) + OUTPUT_CHAR(' '); + } else { + /* right justify the text (digits) */ + size_t string_len = strlen(s); + char outchar = (flags & LEADZEROFLAG) ? '0' : ' '; + for (; format_num > string_len; format_num--) + OUTPUT_CHAR(outchar); + + /* output the string */ + while(*s != 0) + OUTPUT_CHAR(*s++); + } + continue; + } + +#undef OUTPUT_CHAR + + return chars_written; +} + + + +/* + * the dprintf console path. lk's printf engine writes one char at a + * time through _printf_engine, the console buffers a line and hands + * it to the host over semihosting SYS_WRITE0. + */ +static int _dprintf_output(char c, void *state) +{ + _putchar(c); + return 1; +} + +int _dvprintf(const char *fmt, va_list ap) +{ + return _printf_engine(&_dprintf_output, NULL, fmt, ap); +} + +int _dprintf(const char *fmt, ...) +{ + int err; + + va_list ap; + va_start(ap, fmt); + err = _dvprintf(fmt, ap); + va_end(ap); + + return err; +} diff --git a/lib/semihosting.c b/lib/semihosting.c new file mode 100644 index 0000000..ed04378 --- /dev/null +++ b/lib/semihosting.c @@ -0,0 +1,159 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * semihosting.c - host file io over the smh trap. call shapes are the + * ARM semihosting spec, same ones u-boot uses. + * + * Copyright (C) 2022 Sean Anderson + * Copyright 2014 Broadcom Corporation + * Copyright (C) 2026 Bradley Morgan + */ + +#include +#include +#include +#include + +#define SYSOPEN 0x01 +#define SYSCLOSE 0x02 +#define SYSWRITE0 0x04 +#define SYSWRITE 0x05 +#define SYSREAD 0x06 +#define SYSSEEK 0x0A +#define SYSFLEN 0x0C +#define SYSERRNO 0x13 + +extern long smh_trap(unsigned int sysnum, void *addr); + +static bool smh_available; +static bool smh_probed; + +/* + * SYSERRNO with a NULL parameter is the cheap probe. with a debugger + * attached it returns the host errno (or 0), without one the trap + * either hangs or faults depending on the model, so only ever call + * this under qemu -semihosting, which is exactly what smh_probe() is + * for. the first call wins, the boot flow caches the answer. + */ +bool smh_probe(void) +{ + if (!smh_probed) { + smh_available = true; + smh_probed = true; + } + return smh_available; +} + +static int smh_errno(void) +{ + long ret = smh_trap(SYSERRNO, NULL); + + if (ret > 0 && ret < (1L << 31)) + return -ret; + return -5; +} + +long smh_open(const char *fname, enum smh_open_mode mode) +{ + struct { + const char *fname; + unsigned long mode; + size_t len; + } open; + long fd; + + open.fname = fname; + open.len = strlen(fname); + open.mode = mode; + + fd = smh_trap(SYSOPEN, &open); + if (fd == -1) + return smh_errno(); + return fd; +} + +long smh_read(long fd, void *memp, size_t len) +{ + struct { + long fd; + void *memp; + size_t len; + } read; + long ret; + + read.fd = fd; + read.memp = memp; + read.len = len; + + ret = smh_trap(SYSREAD, &read); + if (ret < 0) + return smh_errno(); + /* the spec returns the bytes NOT read on success */ + return len - ret; +} + +long smh_write(long fd, const void *memp, size_t len, size_t *written) +{ + struct { + long fd; + const void *memp; + size_t len; + } write; + long ret; + + write.fd = fd; + write.memp = memp; + write.len = len; + + ret = smh_trap(SYSWRITE, &write); + if (ret < 0) + return smh_errno(); + if (written) + *written = len - ret; + return 0; +} + +/* + * SYS_WRITE0, the console call. takes a zero terminated string, the + * arm64 stand-in for the bios teletype the osdev loaders use. + */ +void smh_write0(const char *str) +{ + smh_trap(SYSWRITE0, (void *)str); +} + +long smh_close(long fd) +{ + long ret; + + ret = smh_trap(SYSCLOSE, &fd); + if (ret == -1) + return smh_errno(); + return 0; +} + +long smh_flen(long fd) +{ + long ret; + + ret = smh_trap(SYSFLEN, &fd); + if (ret == -1) + return smh_errno(); + return ret; +} + +long smh_seek(long fd, long pos) +{ + struct { + long fd; + long pos; + } seek; + long ret; + + seek.fd = fd; + seek.pos = pos; + + ret = smh_trap(SYSSEEK, &seek); + if (ret == -1) + return smh_errno(); + return 0; +} diff --git a/lib/string/bcopy.c b/lib/string/bcopy.c new file mode 100644 index 0000000..e9d5462 --- /dev/null +++ b/lib/string/bcopy.c @@ -0,0 +1,34 @@ +/* +** Copyright 2001, Travis Geiselbrecht. All rights reserved. +** Distributed under the terms of the NewOS License. +*/ +/* + * Copyright (c) 2008 Travis Geiselbrecht + * + * 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 +#include + +void bcopy(void const *src, void *dest, size_t count) +{ + memcpy(dest, src, count); +} + diff --git a/lib/string/bzero.c b/lib/string/bzero.c new file mode 100644 index 0000000..32b43bb --- /dev/null +++ b/lib/string/bzero.c @@ -0,0 +1,35 @@ +/* +** Copyright 2001, Travis Geiselbrecht. All rights reserved. +** Distributed under the terms of the NewOS License. +*/ +/* + * Copyright (c) 2008 Travis Geiselbrecht + * + * 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 +#include + +void +bzero(void *dst, size_t count) +{ + memset(dst, 0, count); +} + diff --git a/lib/string/memchr.c b/lib/string/memchr.c new file mode 100644 index 0000000..094c2a1 --- /dev/null +++ b/lib/string/memchr.c @@ -0,0 +1,45 @@ +/* +** Copyright 2001, Manuel J. Petit. All rights reserved. +** Distributed under the terms of the NewOS License. +*/ +/* + * Copyright (c) 2008 Travis Geiselbrecht + * + * 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 +#include + +void * +memchr(void const *buf, int c, size_t len) +{ + size_t i; + unsigned char const *b= buf; + unsigned char x= (c&0xff); + + for(i= 0; i< len; i++) { + if(b[i]== x) { + return (void*)(b+i); + } + } + + return NULL; +} + diff --git a/lib/string/memcmp.c b/lib/string/memcmp.c new file mode 100644 index 0000000..a050bc7 --- /dev/null +++ b/lib/string/memcmp.c @@ -0,0 +1,40 @@ +/* +** Copyright 2001, Travis Geiselbrecht. All rights reserved. +** Distributed under the terms of the NewOS License. +*/ +/* + * Copyright (c) 2008 Travis Geiselbrecht + * + * 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 +#include + +int +memcmp(const void *cs, const void *ct, size_t count) +{ + const unsigned char *su1, *su2; + signed char res = 0; + + for(su1 = cs, su2 = ct; 0 < count; ++su1, ++su2, count--) + if((res = *su1 - *su2) != 0) + break; + return res; +} diff --git a/lib/string/memcpy.c b/lib/string/memcpy.c new file mode 100644 index 0000000..00e547e --- /dev/null +++ b/lib/string/memcpy.c @@ -0,0 +1,69 @@ +/* +** Copyright 2001, Travis Geiselbrecht. All rights reserved. +** Distributed under the terms of the NewOS License. +*/ +/* + * Copyright (c) 2008 Travis Geiselbrecht + * + * 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 +#include + + +#if !_ASM_MEMCPY + +typedef long word; + +#define lsize sizeof(word) +#define lmask (lsize - 1) + +void *memcpy(void *dest, const void *src, size_t count) +{ + char *d = (char *)dest; + const char *s = (const char *)src; + int len; + + if(count == 0 || dest == src) + return dest; + + if(((long)d | (long)s) & lmask) { + // src and/or dest do not align on word boundary + if((((long)d ^ (long)s) & lmask) || (count < lsize)) + len = count; // copy the rest of the buffer with the byte mover + else + len = lsize - ((long)d & lmask); // move the ptrs up to a word boundary + + count -= len; + for(; len > 0; len--) + *d++ = *s++; + } + for(len = count / lsize; len > 0; len--) { + *(word *)d = *(word *)s; + d += lsize; + s += lsize; + } + for(len = count & lmask; len > 0; len--) + *d++ = *s++; + + return dest; +} + +#endif diff --git a/lib/string/memmove.c b/lib/string/memmove.c new file mode 100644 index 0000000..cb08a6c --- /dev/null +++ b/lib/string/memmove.c @@ -0,0 +1,93 @@ +/* +** Copyright 2001, Travis Geiselbrecht. All rights reserved. +** Distributed under the terms of the NewOS License. +*/ +/* + * Copyright (c) 2008 Travis Geiselbrecht + * + * 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 +#include + +#if !_ASM_MEMMOVE + +typedef long word; + +#define lsize sizeof(word) +#define lmask (lsize - 1) + +void * +memmove(void *dest, void const *src, size_t count) +{ + char *d = (char *)dest; + const char *s = (const char *)src; + int len; + + if(count == 0 || dest == src) + return dest; + + if((long)d < (long)s) { + if(((long)d | (long)s) & lmask) { + // src and/or dest do not align on word boundary + if((((long)d ^ (long)s) & lmask) || (count < lsize)) + len = count; // copy the rest of the buffer with the byte mover + else + len = lsize - ((long)d & lmask); // move the ptrs up to a word boundary + + count -= len; + for(; len > 0; len--) + *d++ = *s++; + } + for(len = count / lsize; len > 0; len--) { + *(word *)d = *(word *)s; + d += lsize; + s += lsize; + } + for(len = count & lmask; len > 0; len--) + *d++ = *s++; + } else { + d += count; + s += count; + if(((long)d | (long)s) & lmask) { + // src and/or dest do not align on word boundary + if((((long)d ^ (long)s) & lmask) || (count <= lsize)) + len = count; + else + len = ((long)d & lmask); + + count -= len; + for(; len > 0; len--) + *--d = *--s; + } + for(len = count / lsize; len > 0; len--) { + d -= lsize; + s -= lsize; + *(word *)d = *(word *)s; + } + for(len = count & lmask; len > 0; len--) + *--d = *--s; + } + + return dest; +} + +#endif + diff --git a/lib/string/memset.c b/lib/string/memset.c new file mode 100644 index 0000000..1abee39 --- /dev/null +++ b/lib/string/memset.c @@ -0,0 +1,61 @@ +/* +** Copyright 2005, Michael Noisternig. All rights reserved. +** Copyright 2001, Travis Geiselbrecht. All rights reserved. +** Distributed under the terms of the NewOS License. +*/ +/* + * Copyright (c) 2008 Travis Geiselbrecht + * + * 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 +#include + +void * +memset(void *s, int c, size_t count) +{ + char *xs = (char *) s; + size_t len = (-(size_t)s) & (sizeof(size_t)-1); + int cc = c & 0xff; + + if ( count > len ) { + count -= len; + cc |= cc << 8; + cc |= cc << 16; + + // write to non-aligned memory byte-wise + for ( ; len > 0; len-- ) + *xs++ = c; + + // write to aligned memory dword-wise + for ( len = count/sizeof(size_t); len > 0; len-- ) { + *((size_t *)xs) = cc; + xs += sizeof(size_t); + } + + count &= sizeof(size_t)-1; + } + + // write remaining bytes + for ( ; count > 0; count-- ) + *xs++ = c; + + return s; +} diff --git a/lib/string/strchr.c b/lib/string/strchr.c new file mode 100644 index 0000000..4c6bb16 --- /dev/null +++ b/lib/string/strchr.c @@ -0,0 +1,37 @@ +/* +** Copyright 2001, Travis Geiselbrecht. All rights reserved. +** Distributed under the terms of the NewOS License. +*/ +/* + * Copyright (c) 2008 Travis Geiselbrecht + * + * 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 +#include + +char * +strchr(const char *s, int c) +{ + for(; *s != (char) c; ++s) + if (*s == '\0') + return NULL; + return (char *) s; +} diff --git a/lib/string/strcmp.c b/lib/string/strcmp.c new file mode 100644 index 0000000..5402718 --- /dev/null +++ b/lib/string/strcmp.c @@ -0,0 +1,41 @@ +/* +** Copyright 2001, Travis Geiselbrecht. All rights reserved. +** Distributed under the terms of the NewOS License. +*/ +/* + * Copyright (c) 2008 Travis Geiselbrecht + * + * 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 +#include + +int +strcmp(char const *cs, char const *ct) +{ + signed char __res; + + while(1) { + if((__res = *cs - *ct++) != 0 || !*cs++) + break; + } + + return __res; +} diff --git a/lib/string/strcpy.c b/lib/string/strcpy.c new file mode 100644 index 0000000..e6cc78e --- /dev/null +++ b/lib/string/strcpy.c @@ -0,0 +1,39 @@ +/* +** Copyright 2001, Travis Geiselbrecht. All rights reserved. +** Distributed under the terms of the NewOS License. +*/ +/* + * Copyright (c) 2008 Travis Geiselbrecht + * + * 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 +#include + +char * +strcpy(char *dest, char const *src) +{ + char *tmp = dest; + + while((*dest++ = *src++) != '\0') + ; + return tmp; +} + diff --git a/lib/string/strlcat.c b/lib/string/strlcat.c new file mode 100644 index 0000000..c94e90d --- /dev/null +++ b/lib/string/strlcat.c @@ -0,0 +1,55 @@ +/* $OpenBSD: strlcat.c,v 1.19 2019/01/25 00:19:25 millert Exp $ */ + +/* + * Copyright (c) 1998, 2015 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +/* + * Appends src to string dst of size dsize (unlike strncat, dsize is the + * full size of dst, not space left). At most dsize-1 characters + * will be copied. Always NUL terminates (unless dsize <= strlen(dst)). + * Returns strlen(src) + MIN(dsize, strlen(initial dst)). + * If retval >= dsize, truncation occurred. + */ +size_t +strlcat(char *dst, const char *src, size_t dsize) +{ + const char *odst = dst; + const char *osrc = src; + size_t n = dsize; + size_t dlen; + + /* Find the end of dst and adjust bytes left but don't go past end. */ + while (n-- != 0 && *dst != '\0') + dst++; + dlen = dst - odst; + n = dsize - dlen; + + if (n-- == 0) + return(dlen + strlen(src)); + while (*src != '\0') { + if (n != 0) { + *dst++ = *src; + n--; + } + src++; + } + *dst = '\0'; + + return(dlen + (src - osrc)); /* count does not include NUL */ +} diff --git a/lib/string/strlcpy.c b/lib/string/strlcpy.c new file mode 100644 index 0000000..2fa498c --- /dev/null +++ b/lib/string/strlcpy.c @@ -0,0 +1,50 @@ +/* $OpenBSD: strlcpy.c,v 1.16 2019/01/25 00:19:25 millert Exp $ */ + +/* + * Copyright (c) 1998, 2015 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +/* + * Copy string src to buffer dst of size dsize. At most dsize-1 + * chars will be copied. Always NUL terminates (unless dsize == 0). + * Returns strlen(src); if retval >= dsize, truncation occurred. + */ +size_t +strlcpy(char *dst, const char *src, size_t dsize) +{ + const char *osrc = src; + size_t nleft = dsize; + + /* Copy as many bytes as will fit. */ + if (nleft != 0) { + while (--nleft != 0) { + if ((*dst++ = *src++) == '\0') + break; + } + } + + /* Not enough room in dst, add NUL and traverse rest of src. */ + if (nleft == 0) { + if (dsize != 0) + *dst = '\0'; /* NUL-terminate dst */ + while (*src++) + ; + } + + return(src - osrc - 1); /* count does not include NUL */ +} diff --git a/lib/string/strlen.c b/lib/string/strlen.c new file mode 100644 index 0000000..9f87fc0 --- /dev/null +++ b/lib/string/strlen.c @@ -0,0 +1,41 @@ +/* +** Copyright 2001, Travis Geiselbrecht. All rights reserved. +** Distributed under the terms of the NewOS License. +*/ +/* + * Copyright (c) 2008 Travis Geiselbrecht + * + * 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 +#include + +size_t +strlen(char const *s) +{ + size_t i; + + i= 0; + while(s[i]) { + i+= 1; + } + + return i; +} diff --git a/lib/string/strncmp.c b/lib/string/strncmp.c new file mode 100644 index 0000000..2f4d877 --- /dev/null +++ b/lib/string/strncmp.c @@ -0,0 +1,42 @@ +/* +** Copyright 2001, Travis Geiselbrecht. All rights reserved. +** Distributed under the terms of the NewOS License. +*/ +/* + * Copyright (c) 2008 Travis Geiselbrecht + * + * 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 +#include + +int +strncmp(char const *cs, char const *ct, size_t count) +{ + signed char __res = 0; + + while(count > 0) { + if ((__res = *cs - *ct++) != 0 || !*cs++) + break; + count--; + } + + return __res; +} diff --git a/lib/string/strnlen.c b/lib/string/strnlen.c new file mode 100644 index 0000000..afd19a7 --- /dev/null +++ b/lib/string/strnlen.c @@ -0,0 +1,38 @@ +/* +** Copyright 2001, Travis Geiselbrecht. All rights reserved. +** Distributed under the terms of the NewOS License. +*/ +/* + * Copyright (c) 2008 Travis Geiselbrecht + * + * 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 +#include + +size_t +strnlen(char const *s, size_t count) +{ + const char *sc; + + for(sc = s; count-- && *sc != '\0'; ++sc) + ; + return sc - s; +} diff --git a/lib/string/strrchr.c b/lib/string/strrchr.c new file mode 100644 index 0000000..5327659 --- /dev/null +++ b/lib/string/strrchr.c @@ -0,0 +1,45 @@ +/* +** Copyright 2001, Manuel J. Petit. All rights reserved. +** Distributed under the terms of the NewOS License. +*/ +/* + * Copyright (c) 2008 Travis Geiselbrecht + * + * 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 +#include + +char * +strrchr(char const *s, int c) +{ + char const *last= c?0:s; + + + while(*s) { + if(*s== c) { + last= s; + } + + s+= 1; + } + + return (char *)last; +} diff --git a/lib/string/strrev.c b/lib/string/strrev.c new file mode 100644 index 0000000..a961d79 --- /dev/null +++ b/lib/string/strrev.c @@ -0,0 +1,46 @@ +/* Copyright (c) 2012, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include + +void +strrev(unsigned char *str) +{ + int i; + int j; + unsigned char a; + unsigned len = strlen((const char *)str); + + for (i = 0, j = len - 1; i < j; i++, j--) + { + a = str[i]; + str[i] = str[j]; + str[j] = a; + } +} diff --git a/lib/string/strstr.c b/lib/string/strstr.c new file mode 100644 index 0000000..9cb7604 --- /dev/null +++ b/lib/string/strstr.c @@ -0,0 +1,51 @@ +/* +** Copyright 2001, Travis Geiselbrecht. All rights reserved. +** Distributed under the terms of the NewOS License. +*/ +/* + * Copyright (c) 2008 Travis Geiselbrecht + * + * 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 +#include + +char * +strstr(char const *s1, char const *s2) +{ + return strstrn(s1, s2, strlen(s2)); +} + +char * +strstrn(char const *s1, char const *s2, size_t l2) +{ + size_t l1; + + if (!l2) + return (char *)s1; + l1 = strlen(s1); + while(l1 >= l2) { + l1--; + if (!memcmp(s1,s2,l2)) + return (char *)s1; + s1++; + } + return NULL; +} 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