diff --git a/Justfile b/Justfile index 41b4a88..c306a78 100644 --- a/Justfile +++ b/Justfile @@ -59,7 +59,7 @@ cb-eject: # Build default hw kernel build: - cargo make build + cargo make build-device cargo make kernel-binary # Clean project diff --git a/Makefile.toml b/Makefile.toml index 0aa9cb3..04cc955 100644 --- a/Makefile.toml +++ b/Makefile.toml @@ -42,9 +42,10 @@ VOLUME = { value = "/Volumes/BOOT", condition = { env_not_set = ["VOLUME"] } } # CARGO_MAKE_EXTEND_WORKSPACE_MAKEFILE = true -RUST_LIBS = "-Z build-std=compiler_builtins,core,alloc -Z build-std-features=compiler-builtins-mem" +RUST_STD = "-Zbuild-std=compiler_builtins,core,alloc" +RUST_STD_FEATURES = "-Zbuild-std-features=compiler-builtins-mem" TARGET_JSON = "${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/targets/${TARGET}.json" -PLATFORM_TARGET="--target=${TARGET_JSON} --features=${TARGET_FEATURES} ${RUST_LIBS}" +PLATFORM_TARGET="--target=${TARGET_JSON} --features=${TARGET_FEATURES}" DEVICE_FEATURES = "noserial" QEMU_FEATURES = "qemu,rpi3" @@ -88,15 +89,20 @@ dependencies = ["kernel-binary"] command = "cargo" args = ["modules", "tree"] -[tasks.build] -env = { "TARGET_FEATURES" = "${TARGET_BOARD}" } +[tasks.do-build] command = "cargo" -args = ["build", "@@split(PLATFORM_TARGET, )", "--release"] +args = ["build", "@@split(PLATFORM_TARGET, )", "@@remove-empty(RUST_STD)", "@@remove-empty(RUST_STD_FEATURES)", "--release"] + +[tasks.build] +disabled = true + +[tasks.build-device] +env = { "TARGET_FEATURES" = "${TARGET_BOARD}" } +run_task = "do-build" [tasks.build-qemu] env = { "TARGET_FEATURES" = "${QEMU_FEATURES}" } -command = "cargo" -args = ["build", "@@split(PLATFORM_TARGET, )", "--release"] +run_task = "do-build" [tasks.qemu-runner] dependencies = ["build-qemu", "kernel-binary"] @@ -115,7 +121,7 @@ args = ["expand", "@@split(PLATFORM_TARGET, )", "--release"] [tasks.test] env = { "TARGET_FEATURES" = "${QEMU_FEATURES}" } command = "cargo" -args = ["test", "@@split(PLATFORM_TARGET, )"] +args = ["test", "@@split(PLATFORM_TARGET, )", "@@remove-empty(RUST_STD)", "@@remove-empty(RUST_STD_FEATURES)"] [tasks.docs] env = { "TARGET_FEATURES" = "" } @@ -128,6 +134,8 @@ command = "cargo" args = ["clippy", "@@split(PLATFORM_TARGET, )", "@@remove-empty(CLIPPY_FEATURES)", "--", "--deny", "warnings", "--allow", "deprecated"] # These tasks are written in cargo-make's own script to make it portable across platforms (no `basename` on Windows) + +## Copy and prepare a given ELF file. Convert to binary output format. [tasks.custom-binary] env = { "BINARY_FILE" = "${BINARY_FILE}" } script_runner = "@duckscript" @@ -144,10 +152,12 @@ script = [ ] install_crate = { crate_name = "cargo-binutils", binary = "rust-objcopy", test_arg = ["--help"] } +## Copy and prepare binary with tests. [tasks.test-binary] env = { "BINARY_FILE" = "${CARGO_MAKE_TASK_ARGS}" } run_task = "custom-binary" +## Run binary with tests in QEMU. [tasks.test-runner] dependencies = ["test-binary"] script_runner = "@duckscript" @@ -159,6 +169,7 @@ script = [ ''' ] +## Generate GDB startup configuration file. [tasks.gdb-config] script_runner = "@duckscript" script = [ @@ -168,6 +179,7 @@ script = [ ''' ] +## Generate zellij configuration file. [tasks.zellij-config] dependencies = ["build-qemu", "kernel-binary"] script_runner = "@duckscript" @@ -196,6 +208,6 @@ script = [ ] [tasks.chainboot] -dependencies = ["build", "kernel-binary"] +dependencies = ["build-device", "kernel-binary"] command = "echo" args = ["\n***===***\n", "Run the following command in your terminal:\n", " ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/target/debug/chainofcommand ${CHAINBOOT_SERIAL} ${CHAINBOOT_BAUD} --kernel ${KERNEL_BIN}\n", "***===***\n\n"] diff --git a/bin/chainboot/Cargo.toml b/bin/chainboot/Cargo.toml index ee6bbac..4355cf7 100644 --- a/bin/chainboot/Cargo.toml +++ b/bin/chainboot/Cargo.toml @@ -12,15 +12,13 @@ edition = "2021" maintenance = { status = "experimental" } [features] -default = ["asm"] +default = [] # Build for running under QEMU with semihosting, so various halt/reboot options would for example quit QEMU instead. qemu = ["machine/qemu"] # Build for debugging it over JTAG/SWD connection - halts on first non-startup function start. jtag = ["machine/jtag"] # Dummy feature, ignored in this crate. noserial = [] -# Startup relocation code is implemented in assembly -asm = [] # Mutually exclusive features to choose a target board rpi3 = ["machine/rpi3"] rpi4 = ["machine/rpi4"] diff --git a/bin/chainboot/src/boot.rs b/bin/chainboot/src/boot.rs index 1de3b59..5576eaa 100644 --- a/bin/chainboot/src/boot.rs +++ b/bin/chainboot/src/boot.rs @@ -1,12 +1,7 @@ -// Assembly counterpart to this file. -#[cfg(feature = "asm")] -core::arch::global_asm!(include_str!("boot.s")); - -// This is quite impossible - the linker constants are resolved to fully constant offsets in asm -// version, but are image-relative symbols in rust, and I see no way to force it otherwise. +// Make first function small enough so that compiler doesn't try +// to crate a huge stack frame before we have a chance to set SP. #[no_mangle] #[link_section = ".text._start"] -#[cfg(not(feature = "asm"))] pub unsafe extern "C" fn _start() -> ! { use { core::cell::UnsafeCell, @@ -18,11 +13,29 @@ pub unsafe extern "C" fn _start() -> ! { const CORE_0: u64 = 0; const CORE_MASK: u64 = 0x3; - if CORE_0 == MPIDR_EL1.get() & CORE_MASK { + if CORE_0 != MPIDR_EL1.get() & CORE_MASK { // if not core0, infinitely wait for events endless_sleep() } + extern "Rust" { + // Stack top + static __boot_core_stack_end_exclusive: UnsafeCell<()>; + } + // Set stack pointer. + SP.set(__boot_core_stack_end_exclusive.get() as u64); + + reset(); +} + +#[no_mangle] +#[link_section = ".text._start"] +pub unsafe extern "C" fn reset() -> ! { + use core::{ + cell::UnsafeCell, + sync::{atomic, atomic::Ordering}, + }; + // These are a problem, because they are not interpreted as constants here. // Subsequently, this code tries to read values from not-yet-existing data locations. extern "Rust" { @@ -38,35 +51,52 @@ pub unsafe extern "C" fn _start() -> ! { static __boot_core_stack_end_exclusive: UnsafeCell<()>; } - // Set stack pointer. - SP.set(__boot_core_stack_end_exclusive.get() as u64); + // This tries to call memcpy() at a wrong linked address - the function is in relocated area! + + // Relocate the code. + // Emulate + // core::ptr::copy_nonoverlapping( + // __binary_nonzero_lma.get() as *const u64, + // __binary_nonzero_vma.get() as *mut u64, + // __binary_nonzero_vma_end_exclusive.get() as usize - __binary_nonzero_vma.get() as usize, + // ); + let binary_size = + __binary_nonzero_vma_end_exclusive.get() as usize - __binary_nonzero_vma.get() as usize; + local_memcpy( + __binary_nonzero_vma.get() as *mut u8, + __binary_nonzero_lma.get() as *const u8, + binary_size, + ); + + // This tries to call memset() at a wrong linked address - the function is in relocated area! // Zeroes the .bss section + // Emulate + // crate::stdmem::local_memset(__bss_start.get() as *mut u8, 0u8, __bss_size.get() as usize); let bss = core::slice::from_raw_parts_mut(__bss_start.get() as *mut u8, __bss_size.get() as usize); for i in bss { *i = 0; } - // Relocate the code - core::ptr::copy_nonoverlapping( - __binary_nonzero_lma.get() as *const u64, - __binary_nonzero_vma.get() as *mut u64, - (__binary_nonzero_vma_end_exclusive.get() as usize - __binary_nonzero_vma.get() as usize), - ); + // Don't cross this line with loads and stores. The initializations + // done above could be "invisible" to the compiler, because we write to the + // same memory location that is used by statics after this point. + // Additionally, we assume that no statics are accessed before this point. + atomic::compiler_fence(Ordering::SeqCst); - _start_rust(); -} - -//-------------------------------------------------------------------------------------------------- -// Public Code -//-------------------------------------------------------------------------------------------------- - -/// The Rust entry of the `kernel` binary. -/// -/// The function is called from the assembly `_start` function, keep it to support "asm" feature. -#[no_mangle] -#[inline(always)] -pub unsafe fn _start_rust(max_kernel_size: u64) -> ! { + let max_kernel_size = + __binary_nonzero_vma.get() as u64 - __boot_core_stack_end_exclusive.get() as u64; crate::kernel_init(max_kernel_size) } + +#[inline(always)] +#[link_section = ".text.boot"] +unsafe fn local_memcpy(mut dest: *mut u8, mut src: *const u8, n: usize) { + let dest_end = dest.add(n); + while dest < dest_end { + *dest = *src; + dest = dest.add(1); + src = src.add(1); + } +} diff --git a/bin/chainboot/src/boot.s b/bin/chainboot/src/boot.s deleted file mode 100644 index 2a31bf1..0000000 --- a/bin/chainboot/src/boot.s +++ /dev/null @@ -1,94 +0,0 @@ -// SPDX-License-Identifier: MIT OR Apache-2.0 -// -// Copyright (c) 2021 Andre Richter -// Modifications -// Copyright (c) 2021- Berkus - -//-------------------------------------------------------------------------------------------------- -// Definitions -//-------------------------------------------------------------------------------------------------- - -// Load the address of a symbol into a register, PC-relative. -// -// The symbol must lie within +/- 4 GiB of the Program Counter. -// -// # Resources -// -// - https://sourceware.org/binutils/docs-2.36/as/AArch64_002dRelocations.html -.macro ADR_REL register, symbol - adrp \register, \symbol - add \register, \register, #:lo12:\symbol -.endm - -// Load the address of a symbol into a register, absolute. -// -// # Resources -// -// - https://sourceware.org/binutils/docs-2.36/as/AArch64_002dRelocations.html -.macro ADR_ABS register, symbol - movz \register, #:abs_g2:\symbol - movk \register, #:abs_g1_nc:\symbol - movk \register, #:abs_g0_nc:\symbol -.endm - -//-------------------------------------------------------------------------------------------------- -// Public Code -//-------------------------------------------------------------------------------------------------- -.section .text._start - -//------------------------------------------------------------------------------ -// fn _start() -//------------------------------------------------------------------------------ -_start: - // Only proceed on the boot core. Park it otherwise. - mrs x1, MPIDR_EL1 - and x1, x1, 0b11 // core id mask - cmp x1, 0 // boot core id - b.ne .L_parking_loop - - // If execution reaches here, it is the boot core. - - // Initialize bss. - ADR_ABS x0, __bss_start - ADR_ABS x1, __bss_size - add x1, x0, x1 - -.L_bss_init_loop: - cmp x0, x1 - b.eq .L_relocate_binary - stp xzr, xzr, [x0], #16 - b .L_bss_init_loop - - // Next, relocate the binary. -.L_relocate_binary: - ADR_REL x0, __binary_nonzero_lma // The address the binary got loaded to. - ADR_ABS x1, __binary_nonzero_vma // The address the binary was linked to. - ADR_ABS x2, __binary_nonzero_vma_end_exclusive - sub x4, x1, x0 // Get difference between vma and lma as max size - -.L_copy_loop: - ldr x3, [x0], #8 - str x3, [x1], #8 - cmp x1, x2 - b.lo .L_copy_loop - - // Prepare the jump to Rust code. - // Set the stack pointer. - ADR_ABS x0, __rpi_phys_binary_load_addr - mov sp, x0 - - // Pass maximum kernel size as an argument to Rust init function. - mov x0, x4 - - // Jump to the relocated Rust code. - ADR_ABS x1, _start_rust - br x1 - - // Infinitely wait for events (aka "park the core"). -.L_parking_loop: - wfe - b .L_parking_loop - -.size _start, . - _start -.type _start, function -.global _start diff --git a/bin/chainboot/src/link.ld b/bin/chainboot/src/link.ld index cffdb92..eeac8d1 100644 --- a/bin/chainboot/src/link.ld +++ b/bin/chainboot/src/link.ld @@ -52,7 +52,7 @@ SECTIONS .text : { KEEP(*(.text._start)) - /* *(text.memcpy) -- only relevant for Rust relocator impl which is currently impossible */ + *(.text.stdmem) } :segment_start_code /* Align to 8 bytes, b/c relocating the binary is done in u64 chunks */ diff --git a/bin/chainboot/src/main.rs b/bin/chainboot/src/main.rs index 4ef586f..64cb362 100644 --- a/bin/chainboot/src/main.rs +++ b/bin/chainboot/src/main.rs @@ -25,7 +25,6 @@ mod boot; /// /// - Only a single core must be active and running this function. /// - The init calls in this function must appear in the correct order. -#[inline(always)] unsafe fn kernel_init(max_kernel_size: u64) -> ! { #[cfg(feature = "jtag")] machine::arch::jtag::wait_debugger(); diff --git a/bin/chainofcommand/Makefile.toml b/bin/chainofcommand/Makefile.toml index a9940a2..f07a50a 100644 --- a/bin/chainofcommand/Makefile.toml +++ b/bin/chainofcommand/Makefile.toml @@ -5,6 +5,9 @@ args = ["build"] [tasks.chainofcommand] dependencies = ["build"] +[tasks.build-device] +disabled = true + [tasks.test] command = "cargo" args = ["test"]