feat: ✨ Add chainboot boot loader
This commit is contained in:
parent
3c57c6e2df
commit
cfe4a230de
|
@ -20,6 +20,23 @@ version = "1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "chainboot"
|
||||||
|
version = "0.0.1"
|
||||||
|
dependencies = [
|
||||||
|
"bit_field",
|
||||||
|
"bitflags",
|
||||||
|
"cfg-if",
|
||||||
|
"cortex-a",
|
||||||
|
"machine",
|
||||||
|
"r0",
|
||||||
|
"seahash",
|
||||||
|
"snafu",
|
||||||
|
"tock-registers",
|
||||||
|
"usize_conversions",
|
||||||
|
"ux",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cortex-a"
|
name = "cortex-a"
|
||||||
version = "7.0.0"
|
version = "7.0.0"
|
||||||
|
@ -106,6 +123,12 @@ version = "1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bd7a31eed1591dcbc95d92ad7161908e72f4677f8fabf2a32ca49b4237cbf211"
|
checksum = "bd7a31eed1591dcbc95d92ad7161908e72f4677f8fabf2a32ca49b4237cbf211"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "seahash"
|
||||||
|
version = "4.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "snafu"
|
name = "snafu"
|
||||||
version = "0.7.0"
|
version = "0.7.0"
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
[workspace]
|
[workspace]
|
||||||
members = [
|
members = [
|
||||||
"nucleus"
|
"nucleus",
|
||||||
|
"bin/chainboot"
|
||||||
]
|
]
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
|
|
21
Justfile
21
Justfile
|
@ -6,6 +6,13 @@ zellij:
|
||||||
cargo make zellij-nucleus
|
cargo make zellij-nucleus
|
||||||
zellij --layout-path emulation/layout.zellij
|
zellij --layout-path emulation/layout.zellij
|
||||||
|
|
||||||
|
# Build and run chainboot in QEMU with serial port emulation
|
||||||
|
zellij-cb:
|
||||||
|
# Connect to it via chainofcommand to load an actual kernel
|
||||||
|
# TODO: actually run chainofcommand in a zellij session too
|
||||||
|
cargo make zellij-cb
|
||||||
|
zellij --layout-path emulation/layout.zellij
|
||||||
|
|
||||||
# Build and run kernel in QEMU
|
# Build and run kernel in QEMU
|
||||||
qemu:
|
qemu:
|
||||||
cargo make qemu
|
cargo make qemu
|
||||||
|
@ -14,6 +21,11 @@ qemu:
|
||||||
qemu-gdb:
|
qemu-gdb:
|
||||||
cargo make qemu-gdb
|
cargo make qemu-gdb
|
||||||
|
|
||||||
|
# Build and run chainboot in QEMU
|
||||||
|
qemu-cb:
|
||||||
|
# Connect to it via chainofcommand to load an actual kernel
|
||||||
|
cargo make qemu-cb
|
||||||
|
|
||||||
# Build and write kernel to an SD Card
|
# Build and write kernel to an SD Card
|
||||||
device:
|
device:
|
||||||
cargo make sdcard
|
cargo make sdcard
|
||||||
|
@ -22,6 +34,11 @@ device:
|
||||||
device-eject:
|
device-eject:
|
||||||
cargo make sdeject
|
cargo make sdeject
|
||||||
|
|
||||||
|
# Build and write chainboot to an SD Card, then eject the SD Card volume
|
||||||
|
cb-eject:
|
||||||
|
cd bin/chainboot
|
||||||
|
cargo make cb-eject
|
||||||
|
|
||||||
# Build default hw kernel
|
# Build default hw kernel
|
||||||
build:
|
build:
|
||||||
cargo make build
|
cargo make build
|
||||||
|
@ -61,6 +78,10 @@ openocd:
|
||||||
gdb:
|
gdb:
|
||||||
cargo make gdb
|
cargo make gdb
|
||||||
|
|
||||||
|
# Build and run chainboot in GDB using openocd or QEMU as target (gdb port 5555)
|
||||||
|
gdb-cb:
|
||||||
|
cargo make gdb-cb
|
||||||
|
|
||||||
# Build and print all symbols in the kernel
|
# Build and print all symbols in the kernel
|
||||||
nm:
|
nm:
|
||||||
cargo make nm
|
cargo make nm
|
||||||
|
|
|
@ -95,6 +95,13 @@ env = { "TARGET_FEATURES" = "${QEMU_FEATURES}" }
|
||||||
command = "cargo"
|
command = "cargo"
|
||||||
args = ["build", "@@split(PLATFORM_TARGET, )", "--release"]
|
args = ["build", "@@split(PLATFORM_TARGET, )", "--release"]
|
||||||
|
|
||||||
|
[tasks.qemu-runner]
|
||||||
|
dependencies = ["build-qemu", "kernel-binary"]
|
||||||
|
env = { "TARGET_FEATURES" = "${QEMU_FEATURES}" }
|
||||||
|
script = [
|
||||||
|
"${QEMU} ${QEMU_OPTS} ${QEMU_RUNNER_OPTS} -dtb ${TARGET_DTB} -kernel ${KERNEL_BIN}"
|
||||||
|
]
|
||||||
|
|
||||||
[tasks.expand]
|
[tasks.expand]
|
||||||
env = { "TARGET_FEATURES" = "" }
|
env = { "TARGET_FEATURES" = "" }
|
||||||
command = "cargo"
|
command = "cargo"
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
[package]
|
||||||
|
name = "chainboot"
|
||||||
|
version = "0.0.1"
|
||||||
|
authors = ["Berkus Decker <berkus+vesper@metta.systems>"]
|
||||||
|
description = "Chain boot loader"
|
||||||
|
license = "BlueOak-1.0.0"
|
||||||
|
categories = ["no-std", "embedded", "os"]
|
||||||
|
publish = false
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[badges]
|
||||||
|
maintenance = { status = "experimental" }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["asm"]
|
||||||
|
# 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"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
machine = { path = "../../machine" }
|
||||||
|
r0 = "1.0"
|
||||||
|
cortex-a = "7.0"
|
||||||
|
tock-registers = "0.7"
|
||||||
|
ux = { version = "0.1", default-features = false }
|
||||||
|
usize_conversions = "0.2"
|
||||||
|
bit_field = "0.10"
|
||||||
|
bitflags = "1.3"
|
||||||
|
cfg-if = "1.0"
|
||||||
|
snafu = { version = "0.7", default-features = false }
|
||||||
|
seahash = "4.1"
|
|
@ -0,0 +1,52 @@
|
||||||
|
[env]
|
||||||
|
CHAINBOOT_ELF = "${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/target/${TARGET}/release/chainboot"
|
||||||
|
CHAINBOOT_BIN = "${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/target/chainboot.bin"
|
||||||
|
|
||||||
|
[tasks.kernel-binary]
|
||||||
|
env = { "BINARY_FILE" = "${CHAINBOOT_ELF}" }
|
||||||
|
run_task = "custom-binary"
|
||||||
|
|
||||||
|
[tasks.hopper]
|
||||||
|
disabled = true
|
||||||
|
|
||||||
|
[tasks.zellij-nucleus]
|
||||||
|
disabled = true
|
||||||
|
|
||||||
|
[tasks.zellij-cb]
|
||||||
|
env = { "KERNEL_BIN" = "${CHAINBOOT_BIN}", "QEMU_OPTS" = "${QEMU_OPTS} ${QEMU_DISASM_OPTS}" }
|
||||||
|
run_task = "zellij-config"
|
||||||
|
|
||||||
|
[tasks.zellij-cb-gdb]
|
||||||
|
env = { "KERNEL_BIN" = "${CHAINBOOT_BIN}", "QEMU_OPTS" = "${QEMU_OPTS} ${QEMU_DISASM_OPTS} ${QEMU_GDB_OPTS}", "TARGET_BOARD" = "rpi3", "TARGET_DTB" = "${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/targets/bcm2710-rpi-3-b-plus.dtb" }
|
||||||
|
run_task = "zellij-config"
|
||||||
|
|
||||||
|
[tasks.qemu]
|
||||||
|
disabled = true
|
||||||
|
|
||||||
|
[tasks.qemu-cb]
|
||||||
|
env = { "QEMU_RUNNER_OPTS" = "${QEMU_SERIAL_OPTS}", "KERNEL_BIN" = "${CHAINBOOT_BIN}", "TARGET_DTB" = "${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/targets/bcm2710-rpi-3-b-plus.dtb" }
|
||||||
|
extend = "qemu-runner"
|
||||||
|
|
||||||
|
[tasks.gdb]
|
||||||
|
disabled = true
|
||||||
|
|
||||||
|
[tasks.gdb-cb]
|
||||||
|
dependencies = ["build", "kernel-binary", "gdb-config"]
|
||||||
|
env = { "RUST_GDB" = "${GDB}" }
|
||||||
|
script = [
|
||||||
|
"rust-gdb -x ${GDB_CONNECT_FILE} ${CHAINBOOT_ELF}"
|
||||||
|
]
|
||||||
|
|
||||||
|
[tasks.sdcard]
|
||||||
|
dependencies = ["build", "kernel-binary"]
|
||||||
|
script_runner = "@duckscript"
|
||||||
|
script = [
|
||||||
|
'''
|
||||||
|
kernelImage = set "chain_boot_rpi4.img"
|
||||||
|
cp ${CHAINBOOT_BIN} ${VOLUME}/${kernelImage}
|
||||||
|
echo "Copied chainboot to ${VOLUME}/${kernelImage}"
|
||||||
|
'''
|
||||||
|
]
|
||||||
|
|
||||||
|
[tasks.cb-eject]
|
||||||
|
dependencies = ["sdeject"]
|
|
@ -0,0 +1,6 @@
|
||||||
|
const LINKER_SCRIPT: &str = "bin/chainboot/src/link.ld";
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
println!("cargo:rerun-if-changed={}", LINKER_SCRIPT);
|
||||||
|
println!("cargo:rustc-link-arg=--script={}", LINKER_SCRIPT);
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
// 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.
|
||||||
|
#[no_mangle]
|
||||||
|
#[link_section = ".text._start"]
|
||||||
|
#[cfg(not(feature = "asm"))]
|
||||||
|
pub unsafe extern "C" fn _start() -> ! {
|
||||||
|
use {
|
||||||
|
cortex_a::registers::{MPIDR_EL1, SP},
|
||||||
|
machine::endless_sleep,
|
||||||
|
tock_registers::interfaces::{Readable, Writeable},
|
||||||
|
};
|
||||||
|
|
||||||
|
const CORE_0: u64 = 0;
|
||||||
|
const CORE_MASK: u64 = 0x3;
|
||||||
|
|
||||||
|
if CORE_0 == MPIDR_EL1.get() & CORE_MASK {
|
||||||
|
// if not core0, infinitely wait for events
|
||||||
|
endless_sleep()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 "C" {
|
||||||
|
// Boundaries of the .bss section, provided by the linker script
|
||||||
|
static mut __bss_start: u64;
|
||||||
|
static mut __bss_end_exclusive: u64;
|
||||||
|
// Load address of the kernel binary
|
||||||
|
static mut __binary_nonzero_lma: u64;
|
||||||
|
// Address to relocate to and image size
|
||||||
|
static mut __binary_nonzero_vma: u64;
|
||||||
|
static mut __binary_nonzero_vma_end_exclusive: u64;
|
||||||
|
// Stack top
|
||||||
|
static mut __boot_core_stack_end_exclusive: u64;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set stack pointer.
|
||||||
|
SP.set(&mut __boot_core_stack_end_exclusive as *mut u64 as u64);
|
||||||
|
|
||||||
|
// Zeroes the .bss section
|
||||||
|
r0::zero_bss(&mut __bss_start, &mut __bss_end_exclusive);
|
||||||
|
|
||||||
|
// Relocate the code
|
||||||
|
core::ptr::copy_nonoverlapping(
|
||||||
|
&mut __binary_nonzero_lma as *const u64,
|
||||||
|
&mut __binary_nonzero_vma as *mut u64,
|
||||||
|
(&mut __binary_nonzero_vma_end_exclusive as *mut u64 as u64
|
||||||
|
- &mut __binary_nonzero_vma as *mut u64 as u64) as usize,
|
||||||
|
);
|
||||||
|
|
||||||
|
_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]
|
||||||
|
pub unsafe fn _start_rust(max_kernel_size: u64) -> ! {
|
||||||
|
crate::kernel_init(max_kernel_size)
|
||||||
|
}
|
|
@ -0,0 +1,93 @@
|
||||||
|
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||||
|
//
|
||||||
|
// Copyright (c) 2021 Andre Richter <andre.o.richter@gmail.com>
|
||||||
|
// Modifications
|
||||||
|
// Copyright (c) 2021- Berkus <berkus+github@metta.systems>
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------------------------
|
||||||
|
// 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_end_exclusive
|
||||||
|
|
||||||
|
.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
|
|
@ -0,0 +1,98 @@
|
||||||
|
/* SPDX-License-Identifier: MIT OR Apache-2.0
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
|
||||||
|
* Copyright (c) 2021- Berkus <berkus+github@metta.systems>
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Information from:
|
||||||
|
* [Output Section Address](https://sourceware.org/binutils/docs/ld/Output-Section-Address.html)
|
||||||
|
* [Output Section LMA](https://sourceware.org/binutils/docs/ld/Output-Section-LMA.html)
|
||||||
|
* [Output Section Attributes](https://sourceware.org/binutils/docs/ld/Output-Section-Attributes.html#Output-Section-Attributes)
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* The physical address at which the the kernel binary will be loaded by the Raspberry's firmware */
|
||||||
|
__rpi_phys_binary_load_addr = 0x80000;
|
||||||
|
|
||||||
|
|
||||||
|
ENTRY(__rpi_phys_binary_load_addr)
|
||||||
|
|
||||||
|
/* Flags:
|
||||||
|
* 4 == R
|
||||||
|
* 5 == RX
|
||||||
|
* 6 == RW
|
||||||
|
*
|
||||||
|
* Segments are marked PT_LOAD below so that the ELF file provides virtual and physical addresses.
|
||||||
|
* It doesn't mean all of them need actually be loaded.
|
||||||
|
*/
|
||||||
|
PHDRS
|
||||||
|
{
|
||||||
|
segment_boot_core_stack PT_LOAD FLAGS(6);
|
||||||
|
segment_start_code PT_LOAD FLAGS(5);
|
||||||
|
segment_code PT_LOAD FLAGS(5);
|
||||||
|
segment_data PT_LOAD FLAGS(6);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTIONS
|
||||||
|
{
|
||||||
|
/***********************************************************************************************
|
||||||
|
* Boot Core Stack
|
||||||
|
***********************************************************************************************/
|
||||||
|
.boot_core_stack (NOLOAD) :
|
||||||
|
{
|
||||||
|
/* ^ */
|
||||||
|
/* | stack */
|
||||||
|
. += __rpi_phys_binary_load_addr; /* | growth */
|
||||||
|
/* | direction */
|
||||||
|
__boot_core_stack_end_exclusive = .; /* | */
|
||||||
|
} :segment_boot_core_stack
|
||||||
|
|
||||||
|
. = __rpi_phys_binary_load_addr;
|
||||||
|
|
||||||
|
.text :
|
||||||
|
{
|
||||||
|
KEEP(*(.text._start))
|
||||||
|
/* *(text.memcpy) -- only relevant for Rust relocator impl which is currently impossible */
|
||||||
|
} :segment_start_code
|
||||||
|
|
||||||
|
/* Align to 8 bytes, b/c relocating the binary is done in u64 chunks */
|
||||||
|
. = ALIGN(8);
|
||||||
|
|
||||||
|
__binary_nonzero_lma = .;
|
||||||
|
|
||||||
|
/* Set the link address to 32 MiB */
|
||||||
|
/* This dictates the max size of the loadable kernel. */
|
||||||
|
. += 0x2000000;
|
||||||
|
|
||||||
|
/***********************************************************************************************
|
||||||
|
* Code + RO Data + Global Offset Table
|
||||||
|
***********************************************************************************************/
|
||||||
|
__binary_nonzero_vma = .;
|
||||||
|
.text : AT (ADDR(.text) + SIZEOF(.text))
|
||||||
|
{
|
||||||
|
*(.text._start_rust) /* The Rust entry point */
|
||||||
|
/* *(text.memcpy) -- only relevant for Rust relocator impl which is currently impossible */
|
||||||
|
*(.text*) /* Everything else */
|
||||||
|
} :segment_code
|
||||||
|
|
||||||
|
.rodata : ALIGN(8) { *(.rodata*) } :segment_code
|
||||||
|
.got : ALIGN(8) { *(.got) } :segment_code
|
||||||
|
|
||||||
|
/***********************************************************************************************
|
||||||
|
* Data + BSS
|
||||||
|
***********************************************************************************************/
|
||||||
|
.data : { *(.data*) } :segment_data
|
||||||
|
|
||||||
|
/* Fill up to 8 bytes, b/c relocating the binary is done in u64 chunks */
|
||||||
|
. = ALIGN(8);
|
||||||
|
__binary_nonzero_vma_end_exclusive = .;
|
||||||
|
|
||||||
|
/* Section is zeroed in pairs of u64. Align start and end to 16 bytes */
|
||||||
|
.bss (NOLOAD) : ALIGN(16)
|
||||||
|
{
|
||||||
|
__bss_start = .;
|
||||||
|
*(.bss*);
|
||||||
|
. = ALIGN(16);
|
||||||
|
__bss_end_exclusive = .;
|
||||||
|
} :segment_data
|
||||||
|
}
|
|
@ -0,0 +1,153 @@
|
||||||
|
// Based on miniload by @andre-richter
|
||||||
|
#![feature(format_args_nl)]
|
||||||
|
#![feature(custom_test_frameworks)]
|
||||||
|
#![test_runner(machine::tests::test_runner)]
|
||||||
|
#![reexport_test_harness_main = "test_main"]
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
use {
|
||||||
|
core::{hash::Hasher, panic::PanicInfo},
|
||||||
|
cortex_a::asm::barrier,
|
||||||
|
machine::{
|
||||||
|
devices::SerialOps,
|
||||||
|
platform::rpi3::{gpio::GPIO, pl011_uart::PL011Uart, BcmHost},
|
||||||
|
print, println, CONSOLE,
|
||||||
|
},
|
||||||
|
seahash::SeaHasher,
|
||||||
|
};
|
||||||
|
|
||||||
|
mod boot;
|
||||||
|
|
||||||
|
/// Early init code.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// - Only a single core must be active and running this function.
|
||||||
|
/// - The init calls in this function must appear in the correct order.
|
||||||
|
unsafe fn kernel_init(max_kernel_size: u64) -> ! {
|
||||||
|
#[cfg(feature = "jtag")]
|
||||||
|
machine::arch::jtag::wait_debugger();
|
||||||
|
|
||||||
|
let gpio = GPIO::default();
|
||||||
|
let uart = PL011Uart::default();
|
||||||
|
let uart = uart.prepare(&gpio).expect("What could go wrong?");
|
||||||
|
CONSOLE.lock(|c| {
|
||||||
|
// Move uart into the global CONSOLE.
|
||||||
|
c.replace_with(uart.into());
|
||||||
|
});
|
||||||
|
|
||||||
|
// println! is usable from here on.
|
||||||
|
|
||||||
|
// Transition from unsafe to safe.
|
||||||
|
kernel_main(max_kernel_size)
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://onlineasciitools.com/convert-text-to-ascii-art (FIGlet) with `cricket` font
|
||||||
|
const LOGO: &str = r#"
|
||||||
|
__ __ __ __
|
||||||
|
.----| |--.---.-|__.-----| |--.-----.-----| |_
|
||||||
|
| __| | _ | | | _ | _ | _ | _|
|
||||||
|
|____|__|__|___._|__|__|__|_____|_____|_____|____|
|
||||||
|
"#;
|
||||||
|
|
||||||
|
fn read_u64() -> u64 {
|
||||||
|
CONSOLE.lock(|c| {
|
||||||
|
let mut val: u64 = u64::from(c.read_byte());
|
||||||
|
val |= u64::from(c.read_byte()) << 8;
|
||||||
|
val |= u64::from(c.read_byte()) << 16;
|
||||||
|
val |= u64::from(c.read_byte()) << 24;
|
||||||
|
val |= u64::from(c.read_byte()) << 32;
|
||||||
|
val |= u64::from(c.read_byte()) << 40;
|
||||||
|
val |= u64::from(c.read_byte()) << 48;
|
||||||
|
val |= u64::from(c.read_byte()) << 56;
|
||||||
|
val
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The main function running after the early init.
|
||||||
|
fn kernel_main(max_kernel_size: u64) -> ! {
|
||||||
|
#[cfg(test)]
|
||||||
|
test_main();
|
||||||
|
|
||||||
|
print!("{}", LOGO);
|
||||||
|
println!("{:>51}\n", BcmHost::board_name());
|
||||||
|
println!("[<<] Requesting kernel image...");
|
||||||
|
|
||||||
|
let kernel_addr: *mut u8 = BcmHost::kernel_load_address() as *mut u8;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
CONSOLE.lock(|c| c.flush());
|
||||||
|
|
||||||
|
// Discard any spurious received characters before starting with the loader protocol.
|
||||||
|
CONSOLE.lock(|c| c.clear_rx());
|
||||||
|
|
||||||
|
// Notify `chainofcommand` to send the binary.
|
||||||
|
for _ in 0..3 {
|
||||||
|
CONSOLE.lock(|c| c.write_byte(3u8));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the binary's size.
|
||||||
|
let size = read_u64();
|
||||||
|
|
||||||
|
// Check the size to fit RAM
|
||||||
|
if size > max_kernel_size {
|
||||||
|
println!("ERR Kernel image too big (over {} bytes)", max_kernel_size);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
print!("OK");
|
||||||
|
|
||||||
|
// We use seahash, simple and with no_std implementation.
|
||||||
|
let mut hasher = SeaHasher::new();
|
||||||
|
|
||||||
|
// Read the kernel byte by byte.
|
||||||
|
for i in 0..size {
|
||||||
|
let val = CONSOLE.lock(|c| c.read_byte());
|
||||||
|
unsafe {
|
||||||
|
core::ptr::write_volatile(kernel_addr.offset(i as isize), val);
|
||||||
|
}
|
||||||
|
let written = unsafe { core::ptr::read_volatile(kernel_addr.offset(i as isize)) };
|
||||||
|
hasher.write_u8(written);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the binary's checksum.
|
||||||
|
let checksum = read_u64();
|
||||||
|
|
||||||
|
let valid = hasher.finish() == checksum;
|
||||||
|
if !valid {
|
||||||
|
println!("ERR Kernel image checksum mismatch");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
print!("OK");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"[<<] Loaded! Executing the payload now from {:p}\n",
|
||||||
|
kernel_addr
|
||||||
|
);
|
||||||
|
CONSOLE.lock(|c| c.flush());
|
||||||
|
|
||||||
|
// Use black magic to create a function pointer.
|
||||||
|
let kernel: fn() -> ! = unsafe { core::mem::transmute(kernel_addr) };
|
||||||
|
|
||||||
|
// Force everything to complete before we jump.
|
||||||
|
unsafe { barrier::isb(barrier::SY) };
|
||||||
|
|
||||||
|
// Jump to loaded kernel!
|
||||||
|
kernel()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(test))]
|
||||||
|
#[panic_handler]
|
||||||
|
fn panicked(info: &PanicInfo) -> ! {
|
||||||
|
machine::panic::handler(info)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
#[panic_handler]
|
||||||
|
fn panicked(info: &PanicInfo) -> ! {
|
||||||
|
machine::panic::handler_for_tests(info)
|
||||||
|
}
|
|
@ -2,7 +2,7 @@
|
||||||
name = "machine"
|
name = "machine"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
authors = ["Berkus Decker <berkus+vesper@metta.systems>"]
|
authors = ["Berkus Decker <berkus+vesper@metta.systems>"]
|
||||||
description = "Vesper nanokernel shared code library."
|
description = "Vesper nanokernel shared code library, useful also for the chainboot loader."
|
||||||
documentation = "https://docs.metta.systems/vesper"
|
documentation = "https://docs.metta.systems/vesper"
|
||||||
homepage = "https://github.com/metta-systems/vesper"
|
homepage = "https://github.com/metta-systems/vesper"
|
||||||
repository = "https://github.com/metta-systems/vesper"
|
repository = "https://github.com/metta-systems/vesper"
|
||||||
|
|
|
@ -7,25 +7,27 @@
|
||||||
env = { "BINARY_FILE" = "${KERNEL_ELF}" }
|
env = { "BINARY_FILE" = "${KERNEL_ELF}" }
|
||||||
run_task = "custom-binary"
|
run_task = "custom-binary"
|
||||||
|
|
||||||
[tasks.qemu-runner]
|
|
||||||
dependencies = ["build-qemu", "kernel-binary"]
|
|
||||||
env = { "TARGET_FEATURES" = "${QEMU_FEATURES}" }
|
|
||||||
script = [
|
|
||||||
"${QEMU} ${QEMU_OPTS} ${QEMU_RUNNER_OPTS} -dtb ${TARGET_DTB} -kernel ${KERNEL_BIN}"
|
|
||||||
]
|
|
||||||
|
|
||||||
[tasks.qemu]
|
[tasks.qemu]
|
||||||
extend = "qemu-runner"
|
|
||||||
env = { "QEMU_RUNNER_OPTS" = "${QEMU_SERIAL_OPTS}", "TARGET_DTB" = "${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/targets/bcm2710-rpi-3-b-plus.dtb" }
|
env = { "QEMU_RUNNER_OPTS" = "${QEMU_SERIAL_OPTS}", "TARGET_DTB" = "${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/targets/bcm2710-rpi-3-b-plus.dtb" }
|
||||||
|
extend = "qemu-runner"
|
||||||
|
|
||||||
|
[tasks.qemu-cb]
|
||||||
|
disabled = true
|
||||||
|
|
||||||
[tasks.qemu-gdb]
|
[tasks.qemu-gdb]
|
||||||
extend = "qemu-runner"
|
|
||||||
env = { "QEMU_RUNNER_OPTS" = "${QEMU_SERIAL_OPTS} ${QEMU_GDB_OPTS}", "TARGET_DTB" = "${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/targets/bcm2710-rpi-3-b-plus.dtb" }
|
env = { "QEMU_RUNNER_OPTS" = "${QEMU_SERIAL_OPTS} ${QEMU_GDB_OPTS}", "TARGET_DTB" = "${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/targets/bcm2710-rpi-3-b-plus.dtb" }
|
||||||
|
extend = "qemu-runner"
|
||||||
|
|
||||||
[tasks.zellij-nucleus]
|
[tasks.zellij-nucleus]
|
||||||
env = { "KERNEL_BIN" = "${KERNEL_BIN}" }
|
env = { "KERNEL_BIN" = "${KERNEL_BIN}" }
|
||||||
run_task = "zellij-config"
|
run_task = "zellij-config"
|
||||||
|
|
||||||
|
[tasks.zellij-cb]
|
||||||
|
disabled = true
|
||||||
|
|
||||||
|
[tasks.zellij-cb-gdb]
|
||||||
|
disabled = true
|
||||||
|
|
||||||
[tasks.gdb-config]
|
[tasks.gdb-config]
|
||||||
script_runner = "@duckscript"
|
script_runner = "@duckscript"
|
||||||
script = [
|
script = [
|
||||||
|
@ -44,6 +46,9 @@ script = [
|
||||||
"rust-gdb -x ${GDB_CONNECT_FILE} ${KERNEL_ELF}"
|
"rust-gdb -x ${GDB_CONNECT_FILE} ${KERNEL_ELF}"
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[tasks.gdb-cb]
|
||||||
|
disabled = true
|
||||||
|
|
||||||
[tasks.nm]
|
[tasks.nm]
|
||||||
dependencies = ["build", "kernel-binary"]
|
dependencies = ["build", "kernel-binary"]
|
||||||
script = [
|
script = [
|
||||||
|
@ -62,6 +67,9 @@ script = [
|
||||||
'''
|
'''
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[tasks.cb-eject]
|
||||||
|
disabled = true
|
||||||
|
|
||||||
[tasks.hopper]
|
[tasks.hopper]
|
||||||
dependencies = ["build", "kernel-binary"]
|
dependencies = ["build", "kernel-binary"]
|
||||||
# The cmd line below causes a bug in hopper, see https://www.dropbox.com/s/zyw5mfx0bepcjb1/hopperv4-RAW-bug.mov?dl=0
|
# The cmd line below causes a bug in hopper, see https://www.dropbox.com/s/zyw5mfx0bepcjb1/hopperv4-RAW-bug.mov?dl=0
|
||||||
|
|
Loading…
Reference in New Issue