Revert to assembly boot code

It is too risky to rely on the compiler to not insert any operations using the
stack.

Having a stack-setting call in Rust using the cortex-a crate as the first action
in a Rust-only _start() function does not work if you're subsequently using the
stack, because the compiler often inserts the operations to make room on the
stack to prepare a function call BEFORE the call to set the stack, which crashes
the boot process.

Hence, keep on using a small piece of assembly boot code throughout.
pull/110/head
Andre Richter 3 years ago
parent 6a9af3c202
commit 2432c0d283
No known key found for this signature in database
GPG Key ID: 2116C1AB102F615E

@ -23,8 +23,9 @@ indent_size = 4
[*.rs]
indent_size = 4
[*.S]
indent_size = 4
[*.s]
indent_style = tab
indent_size = 8
[*.sh]
indent_size = 4

@ -4,6 +4,9 @@ version = "0.1.0"
authors = ["Andre Richter <andre.o.richter@gmail.com>"]
edition = "2018"
[profile.release]
lto = true
[features]
default = []
bsp_rpi3 = []

@ -23,7 +23,7 @@
- Only `.text` section.
- `main.rs`: Important [inner attributes]:
- `#![no_std]`, `#![no_main]`
- `boot.S`: Assembly `_start()` function that executes `wfe` (Wait For Event), halting all cores
- `boot.s`: Assembly `_start()` function that executes `wfe` (Wait For Event), halting all cores
that are executing `_start()`.
- We (have to) define a `#[panic_handler]` function to make the compiler happy.
- Make it `unimplemented!()` because it will be stripped out since it is not used.

@ -1,11 +0,0 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
.section ".text._start"
.global _start
_start:
1: wfe // Wait for event
b 1b // In case an event happened, jump back to 1

@ -11,5 +11,5 @@
//!
//! crate::cpu::boot::arch_boot
// Assembly counterpart to this file. Includes function _start().
global_asm!(include_str!("boot.S"));
// Assembly counterpart to this file.
global_asm!(include_str!("boot.s"));

@ -0,0 +1,20 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2021 Andre Richter <andre.o.richter@gmail.com>
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
.section .text._start
//------------------------------------------------------------------------------
// fn _start()
//------------------------------------------------------------------------------
_start:
// Infinitely wait for events (aka "park the core").
1: wfe
b 1b
.size _start, . - _start
.type _start, function
.global _start

@ -23,6 +23,5 @@ SECTIONS
.text :
{
KEEP(*(.text._start))
*(.text*)
} :segment_rx
}

@ -100,10 +100,8 @@
//!
//! # Boot flow
//!
//! 1. The kernel's entry point is the function [`cpu::boot::arch_boot::_start()`].
//! - It is implemented in `src/_arch/__arch_name__/cpu/boot.S`.
//!
//! [`cpu::boot::arch_boot::_start()`]: ../src/kernel/cpu/up/_arch/aarch64/cpu/boot.rs.html
//! 1. The kernel's entry point is the function `cpu::boot::arch_boot::_start()`.
//! - It is implemented in `src/_arch/__arch_name__/cpu/boot.s`.
#![feature(asm)]
#![feature(global_asm)]

@ -1,6 +1,34 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "cortex-a"
version = "5.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ecefc30975eb87afc5a810d4b2305c0ec29e607ea97e51b2ecd80766e9268d28"
dependencies = [
"register",
]
[[package]]
name = "kernel"
version = "0.1.0"
dependencies = [
"cortex-a",
]
[[package]]
name = "register"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4a247de29ab7cc8f5006cfe775c4a81c704f9914c5e2a79696862e643135433"
dependencies = [
"tock-registers",
]
[[package]]
name = "tock-registers"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f521a79accce68c417c9c77ce22108056b626126da1932f7e2e9b5bbffee0cea"

@ -17,3 +17,7 @@ bsp_rpi4 = []
##--------------------------------------------------------------------------------------------------
[dependencies]
# Platform specific dependencies
[target.'cfg(target_arch = "aarch64")'.dependencies]
cortex-a = { version = "5.x.x" }

@ -2,22 +2,28 @@
## tl;dr
- We extend `boot.S` to call into Rust code for the first time. There, we zero the [bss] section
- We extend `boot.s` to call into Rust code for the first time. There, we zero the [bss] section
before execution is halted with a call to `panic()`.
- Check out `make qemu` again to see the additional code run.
## Notable additions
- More sections in linker script:
- `.rodata`, `.got`, `.data`, `.bss`
- `_start()`:
- Halt core if core != `core0`.
- `core0` jumps to the `runtime_init()` Rust function.
- `runtime_init()` in `runtime_init.rs`
- More additions to the linker script:
- New sections: `.rodata`, `.got`, `.data`, `.bss`.
- A dedicated place for linking boot-time arguments that need to be read by `_start()`.
- `_start()` in `_arch/__arch_name__/cpu/boot.s`:
1. Halt core if core != core0.
1. Set up the `stack pointer`.
1. Jump to the `_start_rust()` function, defined in `arch/__arch_name__/cpu/boot.rs`.
- `runtime_init()` in `runtime_init.rs`:
- Zeros the `.bss` section.
- Calls `kernel_init()`, which calls `panic!()`, which eventually halts `core0` as well.
- Calls `kernel_init()`, which calls `panic!()`, which eventually halts core0 as well.
- The library now uses the [cortex-a] crate, which provides zero-overhead abstractions and wraps
`unsafe` parts when dealing with the CPU's resources.
- See it in action in `_arch/__arch_name__/cpu.rs`.
[bss]: https://en.wikipedia.org/wiki/.bss
[cortex-a]: https://github.com/rust-embedded/cortex-a
## Diff to previous
```diff
@ -25,16 +31,14 @@
diff -uNr 01_wait_forever/Cargo.toml 02_runtime_init/Cargo.toml
--- 01_wait_forever/Cargo.toml
+++ 02_runtime_init/Cargo.toml
@@ -4,6 +4,9 @@
authors = ["Andre Richter <andre.o.richter@gmail.com>"]
edition = "2018"
@@ -17,3 +17,7 @@
##--------------------------------------------------------------------------------------------------
+[profile.release]
+lto = true
[dependencies]
+
[features]
default = []
bsp_rpi3 = []
+# Platform specific dependencies
+[target.'cfg(target_arch = "aarch64")'.dependencies]
+cortex-a = { version = "5.x.x" }
diff -uNr 01_wait_forever/Makefile 02_runtime_init/Makefile
--- 01_wait_forever/Makefile
@ -49,32 +53,78 @@ diff -uNr 01_wait_forever/Makefile 02_runtime_init/Makefile
nm: $(KERNEL_ELF)
diff -uNr 01_wait_forever/src/_arch/aarch64/cpu/boot.S 02_runtime_init/src/_arch/aarch64/cpu/boot.S
--- 01_wait_forever/src/_arch/aarch64/cpu/boot.S
+++ 02_runtime_init/src/_arch/aarch64/cpu/boot.S
@@ -7,5 +7,15 @@
.global _start
diff -uNr 01_wait_forever/src/_arch/aarch64/cpu/boot.rs 02_runtime_init/src/_arch/aarch64/cpu/boot.rs
--- 01_wait_forever/src/_arch/aarch64/cpu/boot.rs
+++ 02_runtime_init/src/_arch/aarch64/cpu/boot.rs
@@ -11,5 +11,23 @@
//!
//! crate::cpu::boot::arch_boot
+use crate::runtime_init;
+
// Assembly counterpart to this file.
global_asm!(include_str!("boot.s"));
+
+//--------------------------------------------------------------------------------------------------
+// Public Code
+//--------------------------------------------------------------------------------------------------
+
+/// The Rust entry of the `kernel` binary.
+///
+/// The function is called from the assembly `_start` function.
+///
+/// # Safety
+///
+/// - The `bss` section is not initialized yet. The code must not use or reference it in any way.
+#[no_mangle]
+pub unsafe fn _start_rust() -> ! {
+ runtime_init::runtime_init()
+}
diff -uNr 01_wait_forever/src/_arch/aarch64/cpu/boot.s 02_runtime_init/src/_arch/aarch64/cpu/boot.s
--- 01_wait_forever/src/_arch/aarch64/cpu/boot.s
+++ 02_runtime_init/src/_arch/aarch64/cpu/boot.s
@@ -3,6 +3,12 @@
// Copyright (c) 2021 Andre Richter <andre.o.richter@gmail.com>
//--------------------------------------------------------------------------------------------------
+// Definitions
+//--------------------------------------------------------------------------------------------------
+
+.equ _core_id_mask, 0b11
+
+//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
.section .text._start
@@ -11,6 +17,22 @@
// fn _start()
//------------------------------------------------------------------------------
_start:
-1: wfe // Wait for event
- b 1b // In case an event happened, jump back to 1
+ mrs x1, mpidr_el1 // Read Multiprocessor Affinity Register
+ and x1, x1, #3 // Clear all bits except [1:0], which hold core id
+ cbz x1, 2f // Jump to label 2 if we are core 0
+1: wfe // Wait for event
+ b 1b // In case an event happened, jump back to 1
+2: // If we are here, we are core0
+ ldr x1, =_start // Load address of function "_start()"
+ mov sp, x1 // Set start of stack to before our code, aka first
+ // address before "_start()"
+ bl runtime_init // Jump to the "runtime_init()" kernel function
+ b 1b // We should never reach here. But just in case,
+ // park this core aswell
+ // Only proceed on the boot core. Park it otherwise.
+ mrs x1, MPIDR_EL1
+ and x1, x1, _core_id_mask
+ ldr x2, BOOT_CORE_ID // provided by bsp/__board_name__/cpu.rs
+ cmp x1, x2
+ b.ne 1f
+
+ // If execution reaches here, it is the boot core. Now, prepare the jump to Rust code.
+
+ // Set the stack pointer.
+ ldr x0, =__boot_core_stack_end_exclusive
+ mov sp, x0
+
+ // Jump to Rust code.
+ b _start_rust
+
// Infinitely wait for events (aka "park the core").
1: wfe
b 1b
diff -uNr 01_wait_forever/src/_arch/aarch64/cpu.rs 02_runtime_init/src/_arch/aarch64/cpu.rs
--- 01_wait_forever/src/_arch/aarch64/cpu.rs
+++ 02_runtime_init/src/_arch/aarch64/cpu.rs
@@ -0,0 +1,30 @@
@@ -0,0 +1,26 @@
+// SPDX-License-Identifier: MIT OR Apache-2.0
+//
+// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
@ -88,6 +138,8 @@ diff -uNr 01_wait_forever/src/_arch/aarch64/cpu.rs 02_runtime_init/src/_arch/aar
+//!
+//! crate::cpu::arch_cpu
+
+use cortex_a::asm;
+
+//--------------------------------------------------------------------------------------------------
+// Public Code
+//--------------------------------------------------------------------------------------------------
@ -95,21 +147,34 @@ diff -uNr 01_wait_forever/src/_arch/aarch64/cpu.rs 02_runtime_init/src/_arch/aar
+/// Pause execution on the core.
+#[inline(always)]
+pub fn wait_forever() -> ! {
+ unsafe {
+ loop {
+ #[rustfmt::skip]
+ asm!(
+ "wfe",
+ options(nomem, nostack, preserves_flags)
+ );
+ }
+ loop {
+ asm::wfe()
+ }
+}
diff -uNr 01_wait_forever/src/bsp/raspberrypi/cpu.rs 02_runtime_init/src/bsp/raspberrypi/cpu.rs
--- 01_wait_forever/src/bsp/raspberrypi/cpu.rs
+++ 02_runtime_init/src/bsp/raspberrypi/cpu.rs
@@ -0,0 +1,14 @@
+// SPDX-License-Identifier: MIT OR Apache-2.0
+//
+// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
+
+//! BSP Processor code.
+
+//--------------------------------------------------------------------------------------------------
+// Public Definitions
+//--------------------------------------------------------------------------------------------------
+
+/// Used by `arch` code to find the early boot core.
+#[no_mangle]
+#[link_section = ".text._start_arguments"]
+pub static BOOT_CORE_ID: u64 = 0;
diff -uNr 01_wait_forever/src/bsp/raspberrypi/link.ld 02_runtime_init/src/bsp/raspberrypi/link.ld
--- 01_wait_forever/src/bsp/raspberrypi/link.ld
+++ 02_runtime_init/src/bsp/raspberrypi/link.ld
@@ -11,6 +11,7 @@
@@ -11,17 +11,52 @@
PHDRS
{
segment_rx PT_LOAD FLAGS(5); /* 5 == RX */
@ -117,8 +182,13 @@ diff -uNr 01_wait_forever/src/bsp/raspberrypi/link.ld 02_runtime_init/src/bsp/ra
}
SECTIONS
@@ -18,11 +19,30 @@
{
. = __rpi_load_addr;
+ /* ^ */
+ /* | stack */
+ /* | growth */
+ /* | direction */
+ __boot_core_stack_end_exclusive = .; /* | */
/***********************************************************************************************
- * Code
@ -127,7 +197,16 @@ diff -uNr 01_wait_forever/src/bsp/raspberrypi/link.ld 02_runtime_init/src/bsp/ra
.text :
{
KEEP(*(.text._start))
*(.text*)
+
+ /* Special constants (or statics in Rust speak) needed by _start().
+ *
+ * They are placed in close proximity to _start() from where they will be read. This ensures
+ * that position-independent, PC-relative loads can be emitted.
+ */
+ *(.text._start_arguments)
+
+ *(.text._start_rust) /* The Rust entry point */
+ *(.text*) /* Everything else */
} :segment_rx
+
+ .rodata : ALIGN(8) { *(.rodata*) } :segment_rx
@ -195,11 +274,12 @@ diff -uNr 01_wait_forever/src/bsp/raspberrypi/memory.rs 02_runtime_init/src/bsp/
diff -uNr 01_wait_forever/src/bsp/raspberrypi.rs 02_runtime_init/src/bsp/raspberrypi.rs
--- 01_wait_forever/src/bsp/raspberrypi.rs
+++ 02_runtime_init/src/bsp/raspberrypi.rs
@@ -4,4 +4,4 @@
@@ -4,4 +4,5 @@
//! Top-level BSP file for the Raspberry Pi 3 and 4.
-// Coming soon.
+pub mod cpu;
+pub mod memory;
diff -uNr 01_wait_forever/src/cpu.rs 02_runtime_init/src/cpu.rs
@ -223,18 +303,18 @@ diff -uNr 01_wait_forever/src/cpu.rs 02_runtime_init/src/cpu.rs
diff -uNr 01_wait_forever/src/main.rs 02_runtime_init/src/main.rs
--- 01_wait_forever/src/main.rs
+++ 02_runtime_init/src/main.rs
@@ -102,8 +102,10 @@
@@ -102,14 +102,25 @@
//!
//! 1. The kernel's entry point is the function [`cpu::boot::arch_boot::_start()`].
//! - It is implemented in `src/_arch/__arch_name__/cpu/boot.S`.
//! 1. The kernel's entry point is the function `cpu::boot::arch_boot::_start()`.
//! - It is implemented in `src/_arch/__arch_name__/cpu/boot.s`.
+//! 2. Once finished with architectural setup, the arch code calls [`runtime_init::runtime_init()`].
//!
//! [`cpu::boot::arch_boot::_start()`]: ../src/kernel/cpu/up/_arch/aarch64/cpu/boot.rs.html
+//!
+//! [`runtime_init::runtime_init()`]: runtime_init/fn.runtime_init.html
#![feature(asm)]
-#![feature(asm)]
#![feature(global_asm)]
@@ -112,6 +114,15 @@
#![no_main]
#![no_std]
mod bsp;
mod cpu;
@ -306,7 +386,7 @@ diff -uNr 01_wait_forever/src/panic_wait.rs 02_runtime_init/src/panic_wait.rs
diff -uNr 01_wait_forever/src/runtime_init.rs 02_runtime_init/src/runtime_init.rs
--- 01_wait_forever/src/runtime_init.rs
+++ 02_runtime_init/src/runtime_init.rs
@@ -0,0 +1,38 @@
@@ -0,0 +1,37 @@
+// SPDX-License-Identifier: MIT OR Apache-2.0
+//
+// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
@ -339,7 +419,6 @@ diff -uNr 01_wait_forever/src/runtime_init.rs 02_runtime_init/src/runtime_init.r
+/// # Safety
+///
+/// - Only a single core must be active and running this function.
+#[no_mangle]
+pub unsafe fn runtime_init() -> ! {
+ zero_bss();
+

@ -11,6 +11,8 @@
//!
//! crate::cpu::arch_cpu
use cortex_a::asm;
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
@ -18,13 +20,7 @@
/// Pause execution on the core.
#[inline(always)]
pub fn wait_forever() -> ! {
unsafe {
loop {
#[rustfmt::skip]
asm!(
"wfe",
options(nomem, nostack, preserves_flags)
);
}
loop {
asm::wfe()
}
}

@ -1,21 +0,0 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
.section ".text._start"
.global _start
_start:
mrs x1, mpidr_el1 // Read Multiprocessor Affinity Register
and x1, x1, #3 // Clear all bits except [1:0], which hold core id
cbz x1, 2f // Jump to label 2 if we are core 0
1: wfe // Wait for event
b 1b // In case an event happened, jump back to 1
2: // If we are here, we are core0
ldr x1, =_start // Load address of function "_start()"
mov sp, x1 // Set start of stack to before our code, aka first
// address before "_start()"
bl runtime_init // Jump to the "runtime_init()" kernel function
b 1b // We should never reach here. But just in case,
// park this core aswell

@ -11,5 +11,23 @@
//!
//! crate::cpu::boot::arch_boot
// Assembly counterpart to this file. Includes function _start().
global_asm!(include_str!("boot.S"));
use crate::runtime_init;
// Assembly counterpart to this file.
global_asm!(include_str!("boot.s"));
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
/// The Rust entry of the `kernel` binary.
///
/// The function is called from the assembly `_start` function.
///
/// # Safety
///
/// - The `bss` section is not initialized yet. The code must not use or reference it in any way.
#[no_mangle]
pub unsafe fn _start_rust() -> ! {
runtime_init::runtime_init()
}

@ -0,0 +1,42 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2021 Andre Richter <andre.o.richter@gmail.com>
//--------------------------------------------------------------------------------------------------
// Definitions
//--------------------------------------------------------------------------------------------------
.equ _core_id_mask, 0b11
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
.section .text._start
//------------------------------------------------------------------------------
// fn _start()
//------------------------------------------------------------------------------
_start:
// Only proceed on the boot core. Park it otherwise.
mrs x1, MPIDR_EL1
and x1, x1, _core_id_mask
ldr x2, BOOT_CORE_ID // provided by bsp/__board_name__/cpu.rs
cmp x1, x2
b.ne 1f
// If execution reaches here, it is the boot core. Now, prepare the jump to Rust code.
// Set the stack pointer.
ldr x0, =__boot_core_stack_end_exclusive
mov sp, x0
// Jump to Rust code.
b _start_rust
// Infinitely wait for events (aka "park the core").
1: wfe
b 1b
.size _start, . - _start
.type _start, function
.global _start

@ -4,4 +4,5 @@
//! Top-level BSP file for the Raspberry Pi 3 and 4.
pub mod cpu;
pub mod memory;

@ -9,4 +9,6 @@
//--------------------------------------------------------------------------------------------------
/// Used by `arch` code to find the early boot core.
pub const BOOT_CORE_ID: u64 = 0;
#[no_mangle]
#[link_section = ".text._start_arguments"]
pub static BOOT_CORE_ID: u64 = 0;

@ -17,6 +17,11 @@ PHDRS
SECTIONS
{
. = __rpi_load_addr;
/* ^ */
/* | stack */
/* | growth */
/* | direction */
__boot_core_stack_end_exclusive = .; /* | */
/***********************************************************************************************
* Code + RO Data + Global Offset Table
@ -24,7 +29,16 @@ SECTIONS
.text :
{
KEEP(*(.text._start))
*(.text*)
/* Special constants (or statics in Rust speak) needed by _start().
*
* They are placed in close proximity to _start() from where they will be read. This ensures
* that position-independent, PC-relative loads can be emitted.
*/
*(.text._start_arguments)
*(.text._start_rust) /* The Rust entry point */
*(.text*) /* Everything else */
} :segment_rx
.rodata : ALIGN(8) { *(.rodata*) } :segment_rx

@ -100,14 +100,12 @@
//!
//! # Boot flow
//!
//! 1. The kernel's entry point is the function [`cpu::boot::arch_boot::_start()`].
//! - It is implemented in `src/_arch/__arch_name__/cpu/boot.S`.
//! 1. The kernel's entry point is the function `cpu::boot::arch_boot::_start()`.
//! - It is implemented in `src/_arch/__arch_name__/cpu/boot.s`.
//! 2. Once finished with architectural setup, the arch code calls [`runtime_init::runtime_init()`].
//!
//! [`cpu::boot::arch_boot::_start()`]: ../src/kernel/cpu/up/_arch/aarch64/cpu/boot.rs.html
//! [`runtime_init::runtime_init()`]: runtime_init/fn.runtime_init.html
#![feature(asm)]
#![feature(global_asm)]
#![no_main]
#![no_std]

@ -30,7 +30,6 @@ unsafe fn zero_bss() {
/// # Safety
///
/// - Only a single core must be active and running this function.
#[no_mangle]
pub unsafe fn runtime_init() -> ! {
zero_bss();

@ -1,6 +1,34 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "cortex-a"
version = "5.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ecefc30975eb87afc5a810d4b2305c0ec29e607ea97e51b2ecd80766e9268d28"
dependencies = [
"register",
]
[[package]]
name = "kernel"
version = "0.1.0"
dependencies = [
"cortex-a",
]
[[package]]
name = "register"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4a247de29ab7cc8f5006cfe775c4a81c704f9914c5e2a79696862e643135433"
dependencies = [
"tock-registers",
]
[[package]]
name = "tock-registers"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f521a79accce68c417c9c77ce22108056b626126da1932f7e2e9b5bbffee0cea"

@ -17,3 +17,8 @@ bsp_rpi4 = []
##--------------------------------------------------------------------------------------------------
[dependencies]
# Platform specific dependencies
[target.'cfg(target_arch = "aarch64")'.dependencies]
cortex-a = { version = "5.x.x" }

@ -4,8 +4,8 @@
- Introducing global `print!()` macros to enable "printf debugging" at the earliest.
- To keep tutorial length reasonable, printing functions for now "abuse" a QEMU property that lets
us use the RPi's `UART` without setting it up properly.
- Using the real hardware `UART` is enabled step-by-step in following tutorials.
us use the Raspberry's `UART` without setting it up properly.
- Using the real hardware `UART` is enabled step-by-step in following tutorials.
## Notable additions
@ -28,6 +28,15 @@ Kernel panic: Stopping here.
## Diff to previous
```diff
diff -uNr 02_runtime_init/Cargo.toml 03_hacky_hello_world/Cargo.toml
--- 02_runtime_init/Cargo.toml
+++ 03_hacky_hello_world/Cargo.toml
@@ -21,3 +21,4 @@
# Platform specific dependencies
[target.'cfg(target_arch = "aarch64")'.dependencies]
cortex-a = { version = "5.x.x" }
+
diff -uNr 02_runtime_init/Makefile 03_hacky_hello_world/Makefile
--- 02_runtime_init/Makefile
+++ 03_hacky_hello_world/Makefile
@ -105,11 +114,12 @@ diff -uNr 02_runtime_init/src/bsp/raspberrypi/console.rs 03_hacky_hello_world/sr
diff -uNr 02_runtime_init/src/bsp/raspberrypi.rs 03_hacky_hello_world/src/bsp/raspberrypi.rs
--- 02_runtime_init/src/bsp/raspberrypi.rs
+++ 03_hacky_hello_world/src/bsp/raspberrypi.rs
@@ -4,4 +4,5 @@
@@ -4,5 +4,6 @@
//! Top-level BSP file for the Raspberry Pi 3 and 4.
+pub mod console;
pub mod cpu;
pub mod memory;
diff -uNr 02_runtime_init/src/console.rs 03_hacky_hello_world/src/console.rs
@ -139,19 +149,10 @@ diff -uNr 02_runtime_init/src/console.rs 03_hacky_hello_world/src/console.rs
diff -uNr 02_runtime_init/src/main.rs 03_hacky_hello_world/src/main.rs
--- 02_runtime_init/src/main.rs
+++ 03_hacky_hello_world/src/main.rs
@@ -101,21 +101,25 @@
//! # Boot flow
//!
//! 1. The kernel's entry point is the function [`cpu::boot::arch_boot::_start()`].
-//! - It is implemented in `src/_arch/__arch_name__/cpu/boot.S`.
+//! - It is implemented in `src/_arch/__arch_name__/cpu/boot.rs`.
//! 2. Once finished with architectural setup, the arch code calls [`runtime_init::runtime_init()`].
@@ -106,14 +106,18 @@
//!
-//! [`cpu::boot::arch_boot::_start()`]: ../src/kernel/cpu/up/_arch/aarch64/cpu/boot.rs.html
+//! [`cpu::boot::arch_boot::_start()`]: cpu/boot/arch_boot/fn._start.html
//! [`runtime_init::runtime_init()`]: runtime_init/fn.runtime_init.html
#![feature(asm)]
+#![feature(format_args_nl)]
#![feature(global_asm)]
+#![feature(panic_info_message)]
@ -167,7 +168,7 @@ diff -uNr 02_runtime_init/src/main.rs 03_hacky_hello_world/src/main.rs
mod runtime_init;
/// Early init code.
@@ -124,5 +128,7 @@
@@ -122,5 +126,7 @@
///
/// - Only a single core must be active and running this function.
unsafe fn kernel_init() -> ! {

@ -11,6 +11,8 @@
//!
//! crate::cpu::arch_cpu
use cortex_a::asm;
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
@ -18,13 +20,7 @@
/// Pause execution on the core.
#[inline(always)]
pub fn wait_forever() -> ! {
unsafe {
loop {
#[rustfmt::skip]
asm!(
"wfe",
options(nomem, nostack, preserves_flags)
);
}
loop {
asm::wfe()
}
}

@ -1,21 +0,0 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
.section ".text._start"
.global _start
_start:
mrs x1, mpidr_el1 // Read Multiprocessor Affinity Register
and x1, x1, #3 // Clear all bits except [1:0], which hold core id
cbz x1, 2f // Jump to label 2 if we are core 0
1: wfe // Wait for event
b 1b // In case an event happened, jump back to 1
2: // If we are here, we are core0
ldr x1, =_start // Load address of function "_start()"
mov sp, x1 // Set start of stack to before our code, aka first
// address before "_start()"
bl runtime_init // Jump to the "runtime_init()" kernel function
b 1b // We should never reach here. But just in case,
// park this core aswell

@ -11,5 +11,23 @@
//!
//! crate::cpu::boot::arch_boot
// Assembly counterpart to this file. Includes function _start().
global_asm!(include_str!("boot.S"));
use crate::runtime_init;
// Assembly counterpart to this file.
global_asm!(include_str!("boot.s"));
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
/// The Rust entry of the `kernel` binary.
///
/// The function is called from the assembly `_start` function.
///
/// # Safety
///
/// - The `bss` section is not initialized yet. The code must not use or reference it in any way.
#[no_mangle]
pub unsafe fn _start_rust() -> ! {
runtime_init::runtime_init()
}

@ -0,0 +1,42 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2021 Andre Richter <andre.o.richter@gmail.com>
//--------------------------------------------------------------------------------------------------
// Definitions
//--------------------------------------------------------------------------------------------------
.equ _core_id_mask, 0b11
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
.section .text._start
//------------------------------------------------------------------------------
// fn _start()
//------------------------------------------------------------------------------
_start:
// Only proceed on the boot core. Park it otherwise.
mrs x1, MPIDR_EL1
and x1, x1, _core_id_mask
ldr x2, BOOT_CORE_ID // provided by bsp/__board_name__/cpu.rs
cmp x1, x2
b.ne 1f
// If execution reaches here, it is the boot core. Now, prepare the jump to Rust code.
// Set the stack pointer.
ldr x0, =__boot_core_stack_end_exclusive
mov sp, x0
// Jump to Rust code.
b _start_rust
// Infinitely wait for events (aka "park the core").
1: wfe
b 1b
.size _start, . - _start
.type _start, function
.global _start

@ -5,4 +5,5 @@
//! Top-level BSP file for the Raspberry Pi 3 and 4.
pub mod console;
pub mod cpu;
pub mod memory;

@ -2,13 +2,13 @@
//
// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
//! Symmetric multiprocessing.
#[cfg(target_arch = "aarch64")]
#[path = "../_arch/aarch64/cpu/smp.rs"]
mod arch_smp;
//! BSP Processor code.
//--------------------------------------------------------------------------------------------------
// Architectural Public Reexports
// Public Definitions
//--------------------------------------------------------------------------------------------------
pub use arch_smp::core_id;
/// Used by `arch` code to find the early boot core.
#[no_mangle]
#[link_section = ".text._start_arguments"]
pub static BOOT_CORE_ID: u64 = 0;

@ -17,6 +17,11 @@ PHDRS
SECTIONS
{
. = __rpi_load_addr;
/* ^ */
/* | stack */
/* | growth */
/* | direction */
__boot_core_stack_end_exclusive = .; /* | */
/***********************************************************************************************
* Code + RO Data + Global Offset Table
@ -24,7 +29,16 @@ SECTIONS
.text :
{
KEEP(*(.text._start))
*(.text*)
/* Special constants (or statics in Rust speak) needed by _start().
*
* They are placed in close proximity to _start() from where they will be read. This ensures
* that position-independent, PC-relative loads can be emitted.
*/
*(.text._start_arguments)
*(.text._start_rust) /* The Rust entry point */
*(.text*) /* Everything else */
} :segment_rx
.rodata : ALIGN(8) { *(.rodata*) } :segment_rx

@ -100,14 +100,12 @@
//!
//! # Boot flow
//!
//! 1. The kernel's entry point is the function [`cpu::boot::arch_boot::_start()`].
//! - It is implemented in `src/_arch/__arch_name__/cpu/boot.rs`.
//! 1. The kernel's entry point is the function `cpu::boot::arch_boot::_start()`.
//! - It is implemented in `src/_arch/__arch_name__/cpu/boot.s`.
//! 2. Once finished with architectural setup, the arch code calls [`runtime_init::runtime_init()`].
//!
//! [`cpu::boot::arch_boot::_start()`]: cpu/boot/arch_boot/fn._start.html
//! [`runtime_init::runtime_init()`]: runtime_init/fn.runtime_init.html
#![feature(asm)]
#![feature(format_args_nl)]
#![feature(global_asm)]
#![feature(panic_info_message)]

@ -30,7 +30,6 @@ unsafe fn zero_bss() {
/// # Safety
///
/// - Only a single core must be active and running this function.
#[no_mangle]
pub unsafe fn runtime_init() -> ! {
zero_bss();

@ -1,7 +0,0 @@
{
"editor.formatOnSave": true,
"editor.rulers": [100],
"rust-analyzer.checkOnSave.overrideCommand": ["make", "check"],
"rust-analyzer.cargo.target": "aarch64-unknown-none-softfloat",
"rust-analyzer.cargo.features": ["bsp_rpi3"]
}

@ -1,34 +0,0 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "cortex-a"
version = "5.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ecefc30975eb87afc5a810d4b2305c0ec29e607ea97e51b2ecd80766e9268d28"
dependencies = [
"register",
]
[[package]]
name = "kernel"
version = "0.1.0"
dependencies = [
"cortex-a",
]
[[package]]
name = "register"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4a247de29ab7cc8f5006cfe775c4a81c704f9914c5e2a79696862e643135433"
dependencies = [
"tock-registers",
]
[[package]]
name = "tock-registers"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f521a79accce68c417c9c77ce22108056b626126da1932f7e2e9b5bbffee0cea"

@ -1,24 +0,0 @@
[package]
name = "kernel"
version = "0.1.0"
authors = ["Andre Richter <andre.o.richter@gmail.com>"]
edition = "2018"
[profile.release]
lto = true
[features]
default = []
bsp_rpi3 = []
bsp_rpi4 = []
##--------------------------------------------------------------------------------------------------
## Dependencies
##--------------------------------------------------------------------------------------------------
[dependencies]
# Platform specific dependencies
[target.'cfg(target_arch = "aarch64")'.dependencies]
cortex-a = { version = "5.x.x" }

@ -1,115 +0,0 @@
## SPDX-License-Identifier: MIT OR Apache-2.0
##
## Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
include ../utils/color.mk.in
# Default to the RPi3
BSP ?= rpi3
# BSP-specific arguments
ifeq ($(BSP),rpi3)
TARGET = aarch64-unknown-none-softfloat
KERNEL_BIN = kernel8.img
QEMU_BINARY = qemu-system-aarch64
QEMU_MACHINE_TYPE = raspi3
QEMU_RELEASE_ARGS = -serial stdio -display none
OBJDUMP_BINARY = aarch64-none-elf-objdump
NM_BINARY = aarch64-none-elf-nm
READELF_BINARY = aarch64-none-elf-readelf
LINKER_FILE = src/bsp/raspberrypi/link.ld
RUSTC_MISC_ARGS = -C target-cpu=cortex-a53
else ifeq ($(BSP),rpi4)
TARGET = aarch64-unknown-none-softfloat
KERNEL_BIN = kernel8.img
QEMU_BINARY = qemu-system-aarch64
QEMU_MACHINE_TYPE =
QEMU_RELEASE_ARGS = -serial stdio -display none
OBJDUMP_BINARY = aarch64-none-elf-objdump
NM_BINARY = aarch64-none-elf-nm
READELF_BINARY = aarch64-none-elf-readelf
LINKER_FILE = src/bsp/raspberrypi/link.ld
RUSTC_MISC_ARGS = -C target-cpu=cortex-a72
endif
# Export for build.rs
export LINKER_FILE
QEMU_MISSING_STRING = "This board is not yet supported for QEMU."
RUSTFLAGS = -C link-arg=-T$(LINKER_FILE) $(RUSTC_MISC_ARGS)
RUSTFLAGS_PEDANTIC = $(RUSTFLAGS) -D warnings -D missing_docs
FEATURES = --features bsp_$(BSP)
COMPILER_ARGS = --target=$(TARGET) \
$(FEATURES) \
--release
RUSTC_CMD = cargo rustc $(COMPILER_ARGS)
DOC_CMD = cargo doc $(COMPILER_ARGS)
CLIPPY_CMD = cargo clippy $(COMPILER_ARGS)
CHECK_CMD = cargo check $(COMPILER_ARGS)
OBJCOPY_CMD = rust-objcopy \
--strip-all \
-O binary
KERNEL_ELF = target/$(TARGET)/release/kernel
DOCKER_IMAGE = rustembedded/osdev-utils
DOCKER_CMD = docker run --rm -v $(shell pwd):/work/tutorial -w /work/tutorial
DOCKER_CMD_INTERACT = $(DOCKER_CMD) -i -t
DOCKER_QEMU = $(DOCKER_CMD_INTERACT) $(DOCKER_IMAGE)
DOCKER_TOOLS = $(DOCKER_CMD) $(DOCKER_IMAGE)
EXEC_QEMU = $(QEMU_BINARY) -M $(QEMU_MACHINE_TYPE)
.PHONY: all $(KERNEL_ELF) $(KERNEL_BIN) doc qemu clippy clean readelf objdump nm check
all: $(KERNEL_BIN)
$(KERNEL_ELF):
$(call colorecho, "\nCompiling kernel - $(BSP)")
@RUSTFLAGS="$(RUSTFLAGS_PEDANTIC)" $(RUSTC_CMD)
$(KERNEL_BIN): $(KERNEL_ELF)
@$(OBJCOPY_CMD) $(KERNEL_ELF) $(KERNEL_BIN)
doc:
$(call colorecho, "\nGenerating docs")
@$(DOC_CMD) --document-private-items --open
ifeq ($(QEMU_MACHINE_TYPE),)
qemu:
$(call colorecho, "\n$(QEMU_MISSING_STRING)")
else
qemu: $(KERNEL_BIN)
$(call colorecho, "\nLaunching QEMU")
@$(DOCKER_QEMU) $(EXEC_QEMU) $(QEMU_RELEASE_ARGS) -kernel $(KERNEL_BIN)
endif
clippy:
@RUSTFLAGS="$(RUSTFLAGS_PEDANTIC)" $(CLIPPY_CMD)
clean:
rm -rf target $(KERNEL_BIN)
readelf: $(KERNEL_ELF)
$(call colorecho, "\nLaunching readelf")
@$(DOCKER_TOOLS) $(READELF_BINARY) --headers $(KERNEL_ELF)
objdump: $(KERNEL_ELF)
$(call colorecho, "\nLaunching objdump")
@$(DOCKER_TOOLS) $(OBJDUMP_BINARY) --disassemble --demangle \
--section .text \
--section .rodata \
--section .got \
$(KERNEL_ELF) | rustfilt
nm: $(KERNEL_ELF)
$(call colorecho, "\nLaunching nm")
@$(DOCKER_TOOLS) $(NM_BINARY) --demangle --print-size $(KERNEL_ELF) | sort | rustfilt
# For rust-analyzer
check:
@RUSTFLAGS="$(RUSTFLAGS)" $(CHECK_CMD) --message-format=json

@ -1,301 +0,0 @@
# Tutorial 04 - Zero Overhead Abstraction
## tl;dr
- All hand-written assembly is replaced by Rust code from the [cortex-a] crate, which provides
zero-overhead abstractions and wraps the `unsafe` parts.
[cortex-a]: https://github.com/rust-embedded/cortex-a
## Diff to previous
```diff
diff -uNr 03_hacky_hello_world/Cargo.toml 04_zero_overhead_abstraction/Cargo.toml
--- 03_hacky_hello_world/Cargo.toml
+++ 04_zero_overhead_abstraction/Cargo.toml
@@ -17,3 +17,8 @@
##--------------------------------------------------------------------------------------------------
[dependencies]
+
+# Platform specific dependencies
+[target.'cfg(target_arch = "aarch64")'.dependencies]
+cortex-a = { version = "5.x.x" }
+
diff -uNr 03_hacky_hello_world/src/_arch/aarch64/cpu/boot.rs 04_zero_overhead_abstraction/src/_arch/aarch64/cpu/boot.rs
--- 03_hacky_hello_world/src/_arch/aarch64/cpu/boot.rs
+++ 04_zero_overhead_abstraction/src/_arch/aarch64/cpu/boot.rs
@@ -11,5 +11,31 @@
//!
//! crate::cpu::boot::arch_boot
-// Assembly counterpart to this file. Includes function _start().
-global_asm!(include_str!("boot.S"));
+use crate::{bsp, cpu};
+use cortex_a::regs::*;
+
+//--------------------------------------------------------------------------------------------------
+// Public Code
+//--------------------------------------------------------------------------------------------------
+
+/// The entry of the `kernel` binary.
+///
+/// The function must be named `_start`, because the linker is looking for this exact name.
+///
+/// # Safety
+///
+/// - Linker script must ensure to place this function where it is expected by the target machine.
+/// - We have to hope that the compiler omits any stack pointer usage before the stack pointer is
+/// actually set (`SP.set()`).
+#[no_mangle]
+pub unsafe fn _start() -> ! {
+ use crate::runtime_init;
+
+ if bsp::cpu::BOOT_CORE_ID == cpu::smp::core_id() {
+ SP.set(bsp::memory::boot_core_stack_end() as u64);
+ runtime_init::runtime_init()
+ } else {
+ // If not core0, infinitely wait for events.
+ cpu::wait_forever()
+ }
+}
diff -uNr 03_hacky_hello_world/src/_arch/aarch64/cpu/boot.S 04_zero_overhead_abstraction/src/_arch/aarch64/cpu/boot.S
--- 03_hacky_hello_world/src/_arch/aarch64/cpu/boot.S
+++ 04_zero_overhead_abstraction/src/_arch/aarch64/cpu/boot.S
@@ -1,21 +0,0 @@
-// SPDX-License-Identifier: MIT OR Apache-2.0
-//
-// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
-
-.section ".text._start"
-
-.global _start
-
-_start:
- mrs x1, mpidr_el1 // Read Multiprocessor Affinity Register
- and x1, x1, #3 // Clear all bits except [1:0], which hold core id
- cbz x1, 2f // Jump to label 2 if we are core 0
-1: wfe // Wait for event
- b 1b // In case an event happened, jump back to 1
-2: // If we are here, we are core0
- ldr x1, =_start // Load address of function "_start()"
- mov sp, x1 // Set start of stack to before our code, aka first
- // address before "_start()"
- bl runtime_init // Jump to the "runtime_init()" kernel function
- b 1b // We should never reach here. But just in case,
- // park this core aswell
diff -uNr 03_hacky_hello_world/src/_arch/aarch64/cpu/smp.rs 04_zero_overhead_abstraction/src/_arch/aarch64/cpu/smp.rs
--- 03_hacky_hello_world/src/_arch/aarch64/cpu/smp.rs
+++ 04_zero_overhead_abstraction/src/_arch/aarch64/cpu/smp.rs
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: MIT OR Apache-2.0
+//
+// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
+
+//! Architectural symmetric multiprocessing.
+//!
+//! # Orientation
+//!
+//! Since arch modules are imported into generic modules using the path attribute, the path of this
+//! file is:
+//!
+//! crate::cpu::smp::arch_smp
+
+use cortex_a::regs::*;
+
+//--------------------------------------------------------------------------------------------------
+// Public Code
+//--------------------------------------------------------------------------------------------------
+
+/// Return the executing core's id.
+#[inline(always)]
+pub fn core_id<T>() -> T
+where
+ T: From<u8>,
+{
+ const CORE_MASK: u64 = 0b11;
+
+ T::from((MPIDR_EL1.get() & CORE_MASK) as u8)
+}
diff -uNr 03_hacky_hello_world/src/_arch/aarch64/cpu.rs 04_zero_overhead_abstraction/src/_arch/aarch64/cpu.rs
--- 03_hacky_hello_world/src/_arch/aarch64/cpu.rs
+++ 04_zero_overhead_abstraction/src/_arch/aarch64/cpu.rs
@@ -11,6 +11,8 @@
//!
//! crate::cpu::arch_cpu
+use cortex_a::asm;
+
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
@@ -18,13 +20,7 @@
/// Pause execution on the core.
#[inline(always)]
pub fn wait_forever() -> ! {
- unsafe {
- loop {
- #[rustfmt::skip]
- asm!(
- "wfe",
- options(nomem, nostack, preserves_flags)
- );
- }
+ loop {
+ asm::wfe()
}
}
diff -uNr 03_hacky_hello_world/src/bsp/raspberrypi/cpu.rs 04_zero_overhead_abstraction/src/bsp/raspberrypi/cpu.rs
--- 03_hacky_hello_world/src/bsp/raspberrypi/cpu.rs
+++ 04_zero_overhead_abstraction/src/bsp/raspberrypi/cpu.rs
@@ -0,0 +1,12 @@
+// SPDX-License-Identifier: MIT OR Apache-2.0
+//
+// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
+
+//! BSP Processor code.
+
+//--------------------------------------------------------------------------------------------------
+// Public Definitions
+//--------------------------------------------------------------------------------------------------
+
+/// Used by `arch` code to find the early boot core.
+pub const BOOT_CORE_ID: u64 = 0;
diff -uNr 03_hacky_hello_world/src/bsp/raspberrypi/link.ld 04_zero_overhead_abstraction/src/bsp/raspberrypi/link.ld
--- 03_hacky_hello_world/src/bsp/raspberrypi/link.ld
+++ 04_zero_overhead_abstraction/src/bsp/raspberrypi/link.ld
@@ -21,6 +21,7 @@
/***********************************************************************************************
* Code + RO Data + Global Offset Table
***********************************************************************************************/
+ __rx_start = .;
.text :
{
KEEP(*(.text._start))
diff -uNr 03_hacky_hello_world/src/bsp/raspberrypi/memory.rs 04_zero_overhead_abstraction/src/bsp/raspberrypi/memory.rs
--- 03_hacky_hello_world/src/bsp/raspberrypi/memory.rs
+++ 04_zero_overhead_abstraction/src/bsp/raspberrypi/memory.rs
@@ -12,14 +12,36 @@
// Symbols from the linker script.
extern "Rust" {
+ static __rx_start: UnsafeCell<()>;
+
static __bss_start: UnsafeCell<u64>;
static __bss_end_inclusive: UnsafeCell<u64>;
}
//--------------------------------------------------------------------------------------------------
+// Private Code
+//--------------------------------------------------------------------------------------------------
+
+/// Start address of the Read+Execute (RX) range.
+///
+/// # Safety
+///
+/// - Value is provided by the linker script and must be trusted as-is.
+#[inline(always)]
+fn rx_start() -> usize {
+ unsafe { __rx_start.get() as usize }
+}
+
+//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
+/// Exclusive end address of the boot core's stack.
+#[inline(always)]
+pub fn boot_core_stack_end() -> usize {
+ rx_start()
+}
+
/// Return the inclusive range spanning the .bss section.
///
/// # Safety
diff -uNr 03_hacky_hello_world/src/bsp/raspberrypi.rs 04_zero_overhead_abstraction/src/bsp/raspberrypi.rs
--- 03_hacky_hello_world/src/bsp/raspberrypi.rs
+++ 04_zero_overhead_abstraction/src/bsp/raspberrypi.rs
@@ -5,4 +5,5 @@
//! Top-level BSP file for the Raspberry Pi 3 and 4.
pub mod console;
+pub mod cpu;
pub mod memory;
diff -uNr 03_hacky_hello_world/src/cpu/smp.rs 04_zero_overhead_abstraction/src/cpu/smp.rs
--- 03_hacky_hello_world/src/cpu/smp.rs
+++ 04_zero_overhead_abstraction/src/cpu/smp.rs
@@ -0,0 +1,14 @@
+// SPDX-License-Identifier: MIT OR Apache-2.0
+//
+// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
+
+//! Symmetric multiprocessing.
+
+#[cfg(target_arch = "aarch64")]
+#[path = "../_arch/aarch64/cpu/smp.rs"]
+mod arch_smp;
+
+//--------------------------------------------------------------------------------------------------
+// Architectural Public Reexports
+//--------------------------------------------------------------------------------------------------
+pub use arch_smp::core_id;
diff -uNr 03_hacky_hello_world/src/cpu.rs 04_zero_overhead_abstraction/src/cpu.rs
--- 03_hacky_hello_world/src/cpu.rs
+++ 04_zero_overhead_abstraction/src/cpu.rs
@@ -10,6 +10,8 @@
mod boot;
+pub mod smp;
+
//--------------------------------------------------------------------------------------------------
// Architectural Public Reexports
//--------------------------------------------------------------------------------------------------
diff -uNr 03_hacky_hello_world/src/main.rs 04_zero_overhead_abstraction/src/main.rs
--- 03_hacky_hello_world/src/main.rs
+++ 04_zero_overhead_abstraction/src/main.rs
@@ -107,9 +107,7 @@
//! [`cpu::boot::arch_boot::_start()`]: cpu/boot/arch_boot/fn._start.html
//! [`runtime_init::runtime_init()`]: runtime_init/fn.runtime_init.html
-#![feature(asm)]
#![feature(format_args_nl)]
-#![feature(global_asm)]
#![feature(panic_info_message)]
#![no_main]
#![no_std]
@@ -128,7 +126,8 @@
///
/// - Only a single core must be active and running this function.
unsafe fn kernel_init() -> ! {
- println!("[0] Hello from Rust!");
+ println!("[0] Hello from pure Rust!");
- panic!("Stopping here.")
+ println!("[1] Stopping here.");
+ cpu::wait_forever()
}
diff -uNr 03_hacky_hello_world/src/runtime_init.rs 04_zero_overhead_abstraction/src/runtime_init.rs
--- 03_hacky_hello_world/src/runtime_init.rs
+++ 04_zero_overhead_abstraction/src/runtime_init.rs
@@ -30,7 +30,6 @@
/// # Safety
///
/// - Only a single core must be active and running this function.
-#[no_mangle]
pub unsafe fn runtime_init() -> ! {
zero_bss();
```

@ -1,8 +0,0 @@
use std::env;
fn main() {
let linker_file = env::var("LINKER_FILE").unwrap();
println!("cargo:rerun-if-changed={}", linker_file);
println!("cargo:rerun-if-changed=build.rs");
}

@ -1,26 +0,0 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
//! Architectural processor code.
//!
//! # Orientation
//!
//! Since arch modules are imported into generic modules using the path attribute, the path of this
//! file is:
//!
//! crate::cpu::arch_cpu
use cortex_a::asm;
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
/// Pause execution on the core.
#[inline(always)]
pub fn wait_forever() -> ! {
loop {
asm::wfe()
}
}

@ -1,41 +0,0 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2021 Andre Richter <andre.o.richter@gmail.com>
//! Architectural boot code.
//!
//! # Orientation
//!
//! Since arch modules are imported into generic modules using the path attribute, the path of this
//! file is:
//!
//! crate::cpu::boot::arch_boot
use crate::{bsp, cpu};
use cortex_a::regs::*;
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
/// The entry of the `kernel` binary.
///
/// The function must be named `_start`, because the linker is looking for this exact name.
///
/// # Safety
///
/// - Linker script must ensure to place this function where it is expected by the target machine.
/// - We have to hope that the compiler omits any stack pointer usage before the stack pointer is
/// actually set (`SP.set()`).
#[no_mangle]
pub unsafe fn _start() -> ! {
use crate::runtime_init;
if bsp::cpu::BOOT_CORE_ID == cpu::smp::core_id() {
SP.set(bsp::memory::boot_core_stack_end() as u64);
runtime_init::runtime_init()
} else {
// If not core0, infinitely wait for events.
cpu::wait_forever()
}
}

@ -1,29 +0,0 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
//! Architectural symmetric multiprocessing.
//!
//! # Orientation
//!
//! Since arch modules are imported into generic modules using the path attribute, the path of this
//! file is:
//!
//! crate::cpu::smp::arch_smp
use cortex_a::regs::*;
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
/// Return the executing core's id.
#[inline(always)]
pub fn core_id<T>() -> T
where
T: From<u8>,
{
const CORE_MASK: u64 = 0b11;
T::from((MPIDR_EL1.get() & CORE_MASK) as u8)
}

@ -1,11 +0,0 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
//! Conditional reexporting of Board Support Packages.
#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))]
mod raspberrypi;
#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))]
pub use raspberrypi::*;

@ -1,9 +0,0 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
//! Top-level BSP file for the Raspberry Pi 3 and 4.
pub mod console;
pub mod cpu;
pub mod memory;

@ -1,47 +0,0 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
//! BSP console facilities.
use crate::console;
use core::fmt;
//--------------------------------------------------------------------------------------------------
// Private Definitions
//--------------------------------------------------------------------------------------------------
/// A mystical, magical device for generating QEMU output out of the void.
struct QEMUOutput;
//--------------------------------------------------------------------------------------------------
// Private Code
//--------------------------------------------------------------------------------------------------
/// Implementing `core::fmt::Write` enables usage of the `format_args!` macros, which in turn are
/// used to implement the `kernel`'s `print!` and `println!` macros. By implementing `write_str()`,
/// we get `write_fmt()` automatically.
///
/// See [`src/print.rs`].
///
/// [`src/print.rs`]: ../../print/index.html
impl fmt::Write for QEMUOutput {
fn write_str(&mut self, s: &str) -> fmt::Result {
for c in s.chars() {
unsafe {
core::ptr::write_volatile(0x3F20_1000 as *mut u8, c as u8);
}
}
Ok(())
}
}
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
/// Return a reference to the console.
pub fn console() -> impl console::interface::Write {
QEMUOutput {}
}

@ -1,49 +0,0 @@
/* SPDX-License-Identifier: MIT OR Apache-2.0
*
* Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
*/
/* The address at which the the kernel binary will be loaded by the Raspberry's firmware */
__rpi_load_addr = 0x80000;
ENTRY(__rpi_load_addr)
PHDRS
{
segment_rx PT_LOAD FLAGS(5); /* 5 == RX */
segment_rw PT_LOAD FLAGS(6); /* 6 == RW */
}
SECTIONS
{
. = __rpi_load_addr;
/***********************************************************************************************
* Code + RO Data + Global Offset Table
***********************************************************************************************/
__rx_start = .;
.text :
{
KEEP(*(.text._start))
*(.text*)
} :segment_rx
.rodata : ALIGN(8) { *(.rodata*) } :segment_rx
.got : ALIGN(8) { *(.got) } :segment_rx
/***********************************************************************************************
* Data + BSS
***********************************************************************************************/
.data : { *(.data*) } :segment_rw
/* Section is zeroed in u64 chunks, align start and end to 8 bytes */
.bss : ALIGN(8)
{
__bss_start = .;
*(.bss*);
. = ALIGN(8);
. += 8; /* Fill for the bss == 0 case, so that __bss_start <= __bss_end_inclusive holds */
__bss_end_inclusive = . - 8;
} :NONE
}

@ -1,59 +0,0 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
//! BSP Memory Management.
use core::{cell::UnsafeCell, ops::RangeInclusive};
//--------------------------------------------------------------------------------------------------
// Private Definitions
//--------------------------------------------------------------------------------------------------
// Symbols from the linker script.
extern "Rust" {
static __rx_start: UnsafeCell<()>;
static __bss_start: UnsafeCell<u64>;
static __bss_end_inclusive: UnsafeCell<u64>;
}
//--------------------------------------------------------------------------------------------------
// Private Code
//--------------------------------------------------------------------------------------------------
/// Start address of the Read+Execute (RX) range.
///
/// # Safety
///
/// - Value is provided by the linker script and must be trusted as-is.
#[inline(always)]
fn rx_start() -> usize {
unsafe { __rx_start.get() as usize }
}
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
/// Exclusive end address of the boot core's stack.
#[inline(always)]
pub fn boot_core_stack_end() -> usize {
rx_start()
}
/// Return the inclusive range spanning the .bss section.
///
/// # Safety
///
/// - Values are provided by the linker script and must be trusted as-is.
/// - The linker-provided addresses must be u64 aligned.
pub fn bss_range_inclusive() -> RangeInclusive<*mut u64> {
let range;
unsafe {
range = RangeInclusive::new(__bss_start.get(), __bss_end_inclusive.get());
}
assert!(!range.is_empty());
range
}

@ -1,19 +0,0 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
//! System console.
//--------------------------------------------------------------------------------------------------
// Public Definitions
//--------------------------------------------------------------------------------------------------
/// Console interfaces.
pub mod interface {
/// Console write functions.
///
/// `core::fmt::Write` is exactly what we need for now. Re-export it here because
/// implementing `console::Write` gives a better hint to the reader about the
/// intention.
pub use core::fmt::Write;
}

@ -1,18 +0,0 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2020-2021 Andre Richter <andre.o.richter@gmail.com>
//! Processor code.
#[cfg(target_arch = "aarch64")]
#[path = "_arch/aarch64/cpu.rs"]
mod arch_cpu;
mod boot;
pub mod smp;
//--------------------------------------------------------------------------------------------------
// Architectural Public Reexports
//--------------------------------------------------------------------------------------------------
pub use arch_cpu::wait_forever;

@ -1,9 +0,0 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2021 Andre Richter <andre.o.richter@gmail.com>
//! Boot code.
#[cfg(target_arch = "aarch64")]
#[path = "../_arch/aarch64/cpu/boot.rs"]
mod arch_boot;

@ -1,133 +0,0 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
// Rust embedded logo for `make doc`.
#![doc(html_logo_url = "https://git.io/JeGIp")]
//! The `kernel` binary.
//!
//! # Code organization and architecture
//!
//! The code is divided into different *modules*, each representing a typical **subsystem** of the
//! `kernel`. Top-level module files of subsystems reside directly in the `src` folder. For example,
//! `src/memory.rs` contains code that is concerned with all things memory management.
//!
//! ## Visibility of processor architecture code
//!
//! Some of the `kernel`'s subsystems depend on low-level code that is specific to the target
//! processor architecture. For each supported processor architecture, there exists a subfolder in
//! `src/_arch`, for example, `src/_arch/aarch64`.
//!
//! The architecture folders mirror the subsystem modules laid out in `src`. For example,
//! architectural code that belongs to the `kernel`'s MMU subsystem (`src/memory/mmu.rs`) would go
//! into `src/_arch/aarch64/memory/mmu.rs`. The latter file is loaded as a module in
//! `src/memory/mmu.rs` using the `path attribute`. Usually, the chosen module name is the generic
//! module's name prefixed with `arch_`.
//!
//! For example, this is the top of `src/memory/mmu.rs`:
//!
//! ```
//! #[cfg(target_arch = "aarch64")]
//! #[path = "../_arch/aarch64/memory/mmu.rs"]
//! mod arch_mmu;
//! ```
//!
//! Often times, items from the `arch_ module` will be publicly reexported by the parent module.
//! This way, each architecture specific module can provide its implementation of an item, while the
//! caller must not be concerned which architecture has been conditionally compiled.
//!
//! ## BSP code
//!
//! `BSP` stands for Board Support Package. `BSP` code is organized under `src/bsp.rs` and contains
//! target board specific definitions and functions. These are things such as the board's memory map
//! or instances of drivers for devices that are featured on the respective board.
//!
//! Just like processor architecture code, the `BSP` code's module structure tries to mirror the
//! `kernel`'s subsystem modules, but there is no reexporting this time. That means whatever is
//! provided must be called starting from the `bsp` namespace, e.g. `bsp::driver::driver_manager()`.
//!
//! ## Kernel interfaces
//!
//! Both `arch` and `bsp` contain code that is conditionally compiled depending on the actual target
//! and board for which the kernel is compiled. For example, the `interrupt controller` hardware of
//! the `Raspberry Pi 3` and the `Raspberry Pi 4` is different, but we want the rest of the `kernel`
//! code to play nicely with any of the two without much hassle.
//!
//! In order to provide a clean abstraction between `arch`, `bsp` and `generic kernel code`,
//! `interface` traits are provided *whenever possible* and *where it makes sense*. They are defined
//! in the respective subsystem module and help to enforce the idiom of *program to an interface,
//! not an implementation*. For example, there will be a common IRQ handling interface which the two
//! different interrupt controller `drivers` of both Raspberrys will implement, and only export the
//! interface to the rest of the `kernel`.
//!
//! ```
//! +-------------------+
//! | Interface (Trait) |
//! | |
//! +--+-------------+--+
//! ^ ^
//! | |
//! | |
//! +----------+--+ +--+----------+
//! | kernel code | | bsp code |
//! | | | arch code |
//! +-------------+ +-------------+
//! ```
//!
//! # Summary
//!
//! For a logical `kernel` subsystem, corresponding code can be distributed over several physical
//! locations. Here is an example for the **memory** subsystem:
//!
//! - `src/memory.rs` and `src/memory/**/*`
//! - Common code that is agnostic of target processor architecture and `BSP` characteristics.
//! - Example: A function to zero a chunk of memory.
//! - Interfaces for the memory subsystem that are implemented by `arch` or `BSP` code.
//! - Example: An `MMU` interface that defines `MMU` function prototypes.
//! - `src/bsp/__board_name__/memory.rs` and `src/bsp/__board_name__/memory/**/*`
//! - `BSP` specific code.
//! - Example: The board's memory map (physical addresses of DRAM and MMIO devices).
//! - `src/_arch/__arch_name__/memory.rs` and `src/_arch/__arch_name__/memory/**/*`
//! - Processor architecture specific code.
//! - Example: Implementation of the `MMU` interface for the `__arch_name__` processor
//! architecture.
//!
//! From a namespace perspective, **memory** subsystem code lives in:
//!
//! - `crate::memory::*`
//! - `crate::bsp::memory::*`
//!
//! # Boot flow
//!
//! 1. The kernel's entry point is the function [`cpu::boot::arch_boot::_start()`].
//! - It is implemented in `src/_arch/__arch_name__/cpu/boot.rs`.
//! 2. Once finished with architectural setup, the arch code calls [`runtime_init::runtime_init()`].
//!
//! [`cpu::boot::arch_boot::_start()`]: cpu/boot/arch_boot/fn._start.html
//! [`runtime_init::runtime_init()`]: runtime_init/fn.runtime_init.html
#![feature(format_args_nl)]
#![feature(panic_info_message)]
#![no_main]
#![no_std]
mod bsp;
mod console;
mod cpu;
mod memory;
mod panic_wait;
mod print;
mod runtime_init;
/// Early init code.
///
/// # Safety
///
/// - Only a single core must be active and running this function.
unsafe fn kernel_init() -> ! {
println!("[0] Hello from pure Rust!");
println!("[1] Stopping here.");
cpu::wait_forever()
}

@ -1,30 +0,0 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
//! Memory Management.
use core::ops::RangeInclusive;
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
/// Zero out an inclusive memory range.
///
/// # Safety
///
/// - `range.start` and `range.end` must be valid.
/// - `range.start` and `range.end` must be `T` aligned.
pub unsafe fn zero_volatile<T>(range: RangeInclusive<*mut T>)
where
T: From<u8>,
{
let mut ptr = *range.start();
let end_inclusive = *range.end();
while ptr <= end_inclusive {
core::ptr::write_volatile(ptr, T::from(0));
ptr = ptr.offset(1);
}
}

@ -1,19 +0,0 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
//! A panic handler that infinitely waits.
use crate::{cpu, println};
use core::panic::PanicInfo;
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
if let Some(args) = info.message() {
println!("\nKernel panic: {}", args);
} else {
println!("\nKernel panic!");
}
cpu::wait_forever()
}

@ -1,38 +0,0 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
//! Printing.
use crate::{bsp, console};
use core::fmt;
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
#[doc(hidden)]
pub fn _print(args: fmt::Arguments) {
use console::interface::Write;
bsp::console::console().write_fmt(args).unwrap();
}
/// Prints without a newline.
///
/// Carbon copy from <https://doc.rust-lang.org/src/std/macros.rs.html>
#[macro_export]
macro_rules! print {
($($arg:tt)*) => ($crate::print::_print(format_args!($($arg)*)));
}
/// Prints with a newline.
///
/// Carbon copy from <https://doc.rust-lang.org/src/std/macros.rs.html>
#[macro_export]
macro_rules! println {
() => ($crate::print!("\n"));
($($arg:tt)*) => ({
$crate::print::_print(format_args_nl!($($arg)*));
})
}

@ -1,37 +0,0 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
//! Rust runtime initialization code.
use crate::{bsp, memory};
//--------------------------------------------------------------------------------------------------
// Private Code
//--------------------------------------------------------------------------------------------------
/// Zero out the .bss section.
///
/// # Safety
///
/// - Must only be called pre `kernel_init()`.
#[inline(always)]
unsafe fn zero_bss() {
memory::zero_volatile(bsp::memory::bss_range_inclusive());
}
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
/// Equivalent to `crt0` or `c0` code in C/C++ world. Clears the `bss` section, then jumps to kernel
/// init code.
///
/// # Safety
///
/// - Only a single core must be active and running this function.
pub unsafe fn runtime_init() -> ! {
zero_bss();
crate::kernel_init()
}

@ -11,31 +11,23 @@
//!
//! crate::cpu::boot::arch_boot
use crate::{bsp, cpu};
use cortex_a::regs::*;
use crate::runtime_init;
// Assembly counterpart to this file.
global_asm!(include_str!("boot.s"));
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
/// The entry of the `kernel` binary.
/// The Rust entry of the `kernel` binary.
///
/// The function must be named `_start`, because the linker is looking for this exact name.
/// The function is called from the assembly `_start` function.
///
/// # Safety
///
/// - Linker script must ensure to place this function where it is expected by the target machine.
/// - We have to hope that the compiler omits any stack pointer usage before the stack pointer is
/// actually set (`SP.set()`).
/// - The `bss` section is not initialized yet. The code must not use or reference it in any way.
#[no_mangle]
pub unsafe fn _start() -> ! {
use crate::runtime_init;
if bsp::cpu::BOOT_CORE_ID == cpu::smp::core_id() {
SP.set(bsp::memory::boot_core_stack_end() as u64);
runtime_init::runtime_init()
} else {
// If not core0, infinitely wait for events.
cpu::wait_forever()
}
pub unsafe fn _start_rust() -> ! {
runtime_init::runtime_init()
}

@ -0,0 +1,42 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2021 Andre Richter <andre.o.richter@gmail.com>
//--------------------------------------------------------------------------------------------------
// Definitions
//--------------------------------------------------------------------------------------------------
.equ _core_id_mask, 0b11
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
.section .text._start
//------------------------------------------------------------------------------
// fn _start()
//------------------------------------------------------------------------------
_start:
// Only proceed on the boot core. Park it otherwise.
mrs x1, MPIDR_EL1
and x1, x1, _core_id_mask
ldr x2, BOOT_CORE_ID // provided by bsp/__board_name__/cpu.rs
cmp x1, x2
b.ne 1f
// If execution reaches here, it is the boot core. Now, prepare the jump to Rust code.
// Set the stack pointer.
ldr x0, =__boot_core_stack_end_exclusive
mov sp, x0
// Jump to Rust code.
b _start_rust
// Infinitely wait for events (aka "park the core").
1: wfe
b 1b
.size _start, . - _start
.type _start, function
.global _start

@ -1,29 +0,0 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
//! Architectural symmetric multiprocessing.
//!
//! # Orientation
//!
//! Since arch modules are imported into generic modules using the path attribute, the path of this
//! file is:
//!
//! crate::cpu::smp::arch_smp
use cortex_a::regs::*;
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
/// Return the executing core's id.
#[inline(always)]
pub fn core_id<T>() -> T
where
T: From<u8>,
{
const CORE_MASK: u64 = 0b11;
T::from((MPIDR_EL1.get() & CORE_MASK) as u8)
}

@ -9,4 +9,6 @@
//--------------------------------------------------------------------------------------------------
/// Used by `arch` code to find the early boot core.
pub const BOOT_CORE_ID: u64 = 0;
#[no_mangle]
#[link_section = ".text._start_arguments"]
pub static BOOT_CORE_ID: u64 = 0;

@ -17,15 +17,28 @@ PHDRS
SECTIONS
{
. = __rpi_load_addr;
/* ^ */
/* | stack */
/* | growth */
/* | direction */
__boot_core_stack_end_exclusive = .; /* | */
/***********************************************************************************************
* Code + RO Data + Global Offset Table
***********************************************************************************************/
__rx_start = .;
.text :
{
KEEP(*(.text._start))
*(.text*)
/* Special constants (or statics in Rust speak) needed by _start().
*
* They are placed in close proximity to _start() from where they will be read. This ensures
* that position-independent, PC-relative loads can be emitted.
*/
*(.text._start_arguments)
*(.text._start_rust) /* The Rust entry point */
*(.text*) /* Everything else */
} :segment_rx
.rodata : ALIGN(8) { *(.rodata*) } :segment_rx

@ -12,36 +12,14 @@ use core::{cell::UnsafeCell, ops::RangeInclusive};
// Symbols from the linker script.
extern "Rust" {
static __rx_start: UnsafeCell<()>;
static __bss_start: UnsafeCell<u64>;
static __bss_end_inclusive: UnsafeCell<u64>;
}
//--------------------------------------------------------------------------------------------------
// Private Code
//--------------------------------------------------------------------------------------------------
/// Start address of the Read+Execute (RX) range.
///
/// # Safety
///
/// - Value is provided by the linker script and must be trusted as-is.
#[inline(always)]
fn rx_start() -> usize {
unsafe { __rx_start.get() as usize }
}
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
/// Exclusive end address of the boot core's stack.
#[inline(always)]
pub fn boot_core_stack_end() -> usize {
rx_start()
}
/// Return the inclusive range spanning the .bss section.
///
/// # Safety

@ -10,8 +10,6 @@ mod arch_cpu;
mod boot;
pub mod smp;
//--------------------------------------------------------------------------------------------------
// Architectural Public Reexports
//--------------------------------------------------------------------------------------------------

@ -1,14 +0,0 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
//! Symmetric multiprocessing.
#[cfg(target_arch = "aarch64")]
#[path = "../_arch/aarch64/cpu/smp.rs"]
mod arch_smp;
//--------------------------------------------------------------------------------------------------
// Architectural Public Reexports
//--------------------------------------------------------------------------------------------------
pub use arch_smp::core_id;

@ -100,14 +100,14 @@
//!
//! # Boot flow
//!
//! 1. The kernel's entry point is the function [`cpu::boot::arch_boot::_start()`].
//! - It is implemented in `src/_arch/__arch_name__/cpu/boot.rs`.
//! 1. The kernel's entry point is the function `cpu::boot::arch_boot::_start()`.
//! - It is implemented in `src/_arch/__arch_name__/cpu/boot.s`.
//! 2. Once finished with architectural setup, the arch code calls [`runtime_init::runtime_init()`].
//!
//! [`cpu::boot::arch_boot::_start()`]: cpu/boot/arch_boot/fn._start.html
//! [`runtime_init::runtime_init()`]: runtime_init/fn.runtime_init.html
#![feature(format_args_nl)]
#![feature(global_asm)]
#![feature(panic_info_message)]
#![feature(trait_alias)]
#![no_main]

@ -18,7 +18,8 @@
- A `driver::interface::DeviceDriver` trait is added for abstracting `BSP` driver implementations
from kernel code.
- Drivers are stored in `src/bsp/device_driver`, and can be reused between `BSP`s.
- We introduce the `GPIO` driver, which pinmuxes the RPi's PL011 UART.
- We introduce the `GPIO` driver, which pinmuxes (that is, routing signals from inside the `SoC`
to actual HW pins) the RPi's PL011 UART.
- Note how this driver differentiates between **RPi 3** and **RPi4**. Their HW is different,
so we have to account for it in SW.
- Most importantly, the `PL011Uart` driver: It implements the `console::interface::*` traits and
@ -1110,7 +1111,7 @@ diff -uNr 05_safe_globals/src/bsp/raspberrypi/driver.rs 06_drivers_gpio_uart/src
diff -uNr 05_safe_globals/src/bsp/raspberrypi/memory.rs 06_drivers_gpio_uart/src/bsp/raspberrypi/memory.rs
--- 05_safe_globals/src/bsp/raspberrypi/memory.rs
+++ 06_drivers_gpio_uart/src/bsp/raspberrypi/memory.rs
@@ -19,6 +19,38 @@
@@ -17,6 +17,38 @@
}
//--------------------------------------------------------------------------------------------------
@ -1146,7 +1147,7 @@ diff -uNr 05_safe_globals/src/bsp/raspberrypi/memory.rs 06_drivers_gpio_uart/src
+}
+
+//--------------------------------------------------------------------------------------------------
// Private Code
// Public Code
//--------------------------------------------------------------------------------------------------
@ -1249,7 +1250,7 @@ diff -uNr 05_safe_globals/src/console.rs 06_drivers_gpio_uart/src/console.rs
diff -uNr 05_safe_globals/src/cpu.rs 06_drivers_gpio_uart/src/cpu.rs
--- 05_safe_globals/src/cpu.rs
+++ 06_drivers_gpio_uart/src/cpu.rs
@@ -15,4 +15,7 @@
@@ -13,4 +13,7 @@
//--------------------------------------------------------------------------------------------------
// Architectural Public Reexports
//--------------------------------------------------------------------------------------------------
@ -1311,15 +1312,15 @@ diff -uNr 05_safe_globals/src/driver.rs 06_drivers_gpio_uart/src/driver.rs
diff -uNr 05_safe_globals/src/main.rs 06_drivers_gpio_uart/src/main.rs
--- 05_safe_globals/src/main.rs
+++ 06_drivers_gpio_uart/src/main.rs
@@ -107,6 +107,8 @@
//! [`cpu::boot::arch_boot::_start()`]: cpu/boot/arch_boot/fn._start.html
@@ -106,6 +106,8 @@
//!
//! [`runtime_init::runtime_init()`]: runtime_init/fn.runtime_init.html
+#![allow(clippy::clippy::upper_case_acronyms)]
+#![feature(const_fn_fn_ptr_basics)]
#![feature(format_args_nl)]
#![feature(global_asm)]
#![feature(panic_info_message)]
#![feature(trait_alias)]
@@ -116,6 +118,7 @@
mod bsp;
mod console;

@ -11,31 +11,23 @@
//!
//! crate::cpu::boot::arch_boot
use crate::{bsp, cpu};
use cortex_a::regs::*;
use crate::runtime_init;
// Assembly counterpart to this file.
global_asm!(include_str!("boot.s"));
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
/// The entry of the `kernel` binary.
/// The Rust entry of the `kernel` binary.
///
/// The function must be named `_start`, because the linker is looking for this exact name.
/// The function is called from the assembly `_start` function.
///
/// # Safety
///
/// - Linker script must ensure to place this function where it is expected by the target machine.
/// - We have to hope that the compiler omits any stack pointer usage before the stack pointer is
/// actually set (`SP.set()`).
/// - The `bss` section is not initialized yet. The code must not use or reference it in any way.
#[no_mangle]
pub unsafe fn _start() -> ! {
use crate::runtime_init;
if bsp::cpu::BOOT_CORE_ID == cpu::smp::core_id() {
SP.set(bsp::memory::boot_core_stack_end() as u64);
runtime_init::runtime_init()
} else {
// If not core0, infinitely wait for events.
cpu::wait_forever()
}
pub unsafe fn _start_rust() -> ! {
runtime_init::runtime_init()
}

@ -0,0 +1,42 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2021 Andre Richter <andre.o.richter@gmail.com>
//--------------------------------------------------------------------------------------------------
// Definitions
//--------------------------------------------------------------------------------------------------
.equ _core_id_mask, 0b11
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
.section .text._start
//------------------------------------------------------------------------------
// fn _start()
//------------------------------------------------------------------------------
_start:
// Only proceed on the boot core. Park it otherwise.
mrs x1, MPIDR_EL1
and x1, x1, _core_id_mask
ldr x2, BOOT_CORE_ID // provided by bsp/__board_name__/cpu.rs
cmp x1, x2
b.ne 1f
// If execution reaches here, it is the boot core. Now, prepare the jump to Rust code.
// Set the stack pointer.
ldr x0, =__boot_core_stack_end_exclusive
mov sp, x0
// Jump to Rust code.
b _start_rust
// Infinitely wait for events (aka "park the core").
1: wfe
b 1b
.size _start, . - _start
.type _start, function
.global _start

@ -1,29 +0,0 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
//! Architectural symmetric multiprocessing.
//!
//! # Orientation
//!
//! Since arch modules are imported into generic modules using the path attribute, the path of this
//! file is:
//!
//! crate::cpu::smp::arch_smp
use cortex_a::regs::*;
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
/// Return the executing core's id.
#[inline(always)]
pub fn core_id<T>() -> T
where
T: From<u8>,
{
const CORE_MASK: u64 = 0b11;
T::from((MPIDR_EL1.get() & CORE_MASK) as u8)
}

@ -9,4 +9,6 @@
//--------------------------------------------------------------------------------------------------
/// Used by `arch` code to find the early boot core.
pub const BOOT_CORE_ID: u64 = 0;
#[no_mangle]
#[link_section = ".text._start_arguments"]
pub static BOOT_CORE_ID: u64 = 0;

@ -17,15 +17,28 @@ PHDRS
SECTIONS
{
. = __rpi_load_addr;
/* ^ */
/* | stack */
/* | growth */
/* | direction */
__boot_core_stack_end_exclusive = .; /* | */
/***********************************************************************************************
* Code + RO Data + Global Offset Table
***********************************************************************************************/
__rx_start = .;
.text :
{
KEEP(*(.text._start))
*(.text*)
/* Special constants (or statics in Rust speak) needed by _start().
*
* They are placed in close proximity to _start() from where they will be read. This ensures
* that position-independent, PC-relative loads can be emitted.
*/
*(.text._start_arguments)
*(.text._start_rust) /* The Rust entry point */
*(.text*) /* Everything else */
} :segment_rx
.rodata : ALIGN(8) { *(.rodata*) } :segment_rx

@ -12,8 +12,6 @@ use core::{cell::UnsafeCell, ops::RangeInclusive};
// Symbols from the linker script.
extern "Rust" {
static __rx_start: UnsafeCell<()>;
static __bss_start: UnsafeCell<u64>;
static __bss_end_inclusive: UnsafeCell<u64>;
}
@ -50,30 +48,10 @@ pub(super) mod map {
}
}
//--------------------------------------------------------------------------------------------------
// Private Code
//--------------------------------------------------------------------------------------------------
/// Start address of the Read+Execute (RX) range.
///
/// # Safety
///
/// - Value is provided by the linker script and must be trusted as-is.
#[inline(always)]
fn rx_start() -> usize {
unsafe { __rx_start.get() as usize }
}
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
/// Exclusive end address of the boot core's stack.
#[inline(always)]
pub fn boot_core_stack_end() -> usize {
rx_start()
}
/// Return the inclusive range spanning the .bss section.
///
/// # Safety

@ -10,8 +10,6 @@ mod arch_cpu;
mod boot;
pub mod smp;
//--------------------------------------------------------------------------------------------------
// Architectural Public Reexports
//--------------------------------------------------------------------------------------------------

@ -1,14 +0,0 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
//! Symmetric multiprocessing.
#[cfg(target_arch = "aarch64")]
#[path = "../_arch/aarch64/cpu/smp.rs"]
mod arch_smp;
//--------------------------------------------------------------------------------------------------
// Architectural Public Reexports
//--------------------------------------------------------------------------------------------------
pub use arch_smp::core_id;

@ -100,16 +100,16 @@
//!
//! # Boot flow
//!
//! 1. The kernel's entry point is the function [`cpu::boot::arch_boot::_start()`].
//! - It is implemented in `src/_arch/__arch_name__/cpu/boot.rs`.
//! 1. The kernel's entry point is the function `cpu::boot::arch_boot::_start()`.
//! - It is implemented in `src/_arch/__arch_name__/cpu/boot.s`.
//! 2. Once finished with architectural setup, the arch code calls [`runtime_init::runtime_init()`].
//!
//! [`cpu::boot::arch_boot::_start()`]: cpu/boot/arch_boot/fn._start.html
//! [`runtime_init::runtime_init()`]: runtime_init/fn.runtime_init.html
#![allow(clippy::clippy::upper_case_acronyms)]
#![feature(const_fn_fn_ptr_basics)]
#![feature(format_args_nl)]
#![feature(global_asm)]
#![feature(panic_info_message)]
#![feature(trait_alias)]
#![no_main]

@ -24,7 +24,7 @@ ifeq ($(BSP),rpi3)
NM_BINARY = aarch64-none-elf-nm
READELF_BINARY = aarch64-none-elf-readelf
LINKER_FILE = src/bsp/raspberrypi/link.ld
RUSTC_MISC_ARGS = -C target-cpu=cortex-a53 -C relocation-model=pic
RUSTC_MISC_ARGS = -C target-cpu=cortex-a53
CHAINBOOT_DEMO_PAYLOAD = demo_payload_rpi3.img
else ifeq ($(BSP),rpi4)
TARGET = aarch64-unknown-none-softfloat
@ -36,7 +36,7 @@ else ifeq ($(BSP),rpi4)
NM_BINARY = aarch64-none-elf-nm
READELF_BINARY = aarch64-none-elf-readelf
LINKER_FILE = src/bsp/raspberrypi/link.ld
RUSTC_MISC_ARGS = -C target-cpu=cortex-a72 -C relocation-model=pic
RUSTC_MISC_ARGS = -C target-cpu=cortex-a72
CHAINBOOT_DEMO_PAYLOAD = demo_payload_rpi4.img
endif

@ -3,22 +3,34 @@
## tl;dr
- Running from an SD card was a nice experience, but it would be extremely tedious to do it for
every new binary. Let's write a [chainloader] using [position independent code].
every new binary. So let's write a [chainloader].
- This will be the last binary you need to put on the SD card. Each following tutorial will provide
a `chainboot` target in the `Makefile` that lets you conveniently load the kernel over `UART`.
[chainloader]: https://en.wikipedia.org/wiki/Chain_loading
[position independent code]: https://en.wikipedia.org/wiki/Position-independent_code
## Note
Please note that there is a lot of stuff going on in this tutorial that is very hard to grasp by
only looking at the source code changes.
Please note that there is stuff going on in this tutorial that is very hard to grasp by only looking
at the source code changes.
The gist of it is that in `boot.s`, we are writing a piece of [position independent code] which
automatically determines where the firmware has loaded the binary (`0x8_0000`), and where it was
linked to (`0x200_0000`, see `link.ld`). The binary then copies itself from loaded to linked address
(aka "relocating" itself), and then jumps to the relocated version of `_start_rust()`.
Since the chainloader has put itself "out of the way" now, it can now receive another kernel binary
from the `UART` and copy it to the standard load address of the RPi firmware at `0x8_0000`. Finally,
it jumps to `0x8_0000` and the newly loaded binary transparently executes as if it had been loaded
from SD card all along.
Please bear with me until I find the time to write it all down here elaborately. For the time being,
please see this tutorial as an enabler for a convenience feature that allows booting the following
tutorials in a quick manner.
[position independent code]: https://en.wikipedia.org/wiki/Position-independent_code
## Install and test it
Our chainloader is called `MiniLoad` and is inspired by [raspbootin].
@ -71,34 +83,36 @@ Minipush 1.0
[3] Echoing input now
```
In this tutorial, a version of the kernel from the previous tutorial is loaded
for demo purposes. In subsequent tuts, it will be the working directory's
kernel.
In this tutorial, a version of the kernel from the previous tutorial is loaded for demo purposes. In
subsequent tutorials, it will be the working directory's kernel.
## Test it
The `Makefile` in this tutorial has an additional target, `qemuasm`, that lets
you nicely observe the jump from the loaded address (`0x80_XXX`) to the
relocated code at (`0x0200_0XXX`):
The `Makefile` in this tutorial has an additional target, `qemuasm`, that lets you nicely observe
how the kernel, after relocating itself, jumps the load address region (`0x80_XXX`) to the relocated
code at (`0x0200_0XXX`):
```console
$ make qemuasm
[...]
N:
0x00080030: 58000140 ldr x0, #0x80058
0x00080034: 9100001f mov sp, x0
0x00080038: 58000141 ldr x1, #0x80060
0x0008003c: d61f0020 br x1
----------------
IN:
0x0008098c: b0000008 adrp x8, #0x81000
0x00080990: b0000000 adrp x0, #0x81000
0x00080994: 912a8000 add x0, x0, #0xaa0
0x00080998: f9471908 ldr x8, [x8, #0xe30]
0x0008099c: d63f0100 blr x8
0x02000070: 9400044c bl #0x20011a0
----------------
IN:
0x02000b1c: b0000008 adrp x8, #0x2001000
0x02000b20: b0000009 adrp x9, #0x2001000
0x02000b24: f9475d08 ldr x8, [x8, #0xeb8]
0x02000b28: f9476129 ldr x9, [x9, #0xec0]
0x02000b2c: eb08013f cmp x9, x8
0x02000b30: 540000c2 b.hs #0x2000b48
0x020011a0: 90000008 adrp x8, #0x2001000
0x020011a4: 90000009 adrp x9, #0x2001000
0x020011a8: f9446508 ldr x8, [x8, #0x8c8]
0x020011ac: f9446929 ldr x9, [x9, #0x8d0]
0x020011b0: eb08013f cmp x9, x8
0x020011b4: 54000109 b.ls #0x20011d4
[...]
```
@ -110,22 +124,18 @@ Binary files 06_drivers_gpio_uart/demo_payload_rpi4.img and 07_uart_chainloader/
diff -uNr 06_drivers_gpio_uart/Makefile 07_uart_chainloader/Makefile
--- 06_drivers_gpio_uart/Makefile
+++ 07_uart_chainloader/Makefile
@@ -24,7 +24,8 @@
NM_BINARY = aarch64-none-elf-nm
@@ -25,6 +25,7 @@
READELF_BINARY = aarch64-none-elf-readelf
LINKER_FILE = src/bsp/raspberrypi/link.ld
- RUSTC_MISC_ARGS = -C target-cpu=cortex-a53
+ RUSTC_MISC_ARGS = -C target-cpu=cortex-a53 -C relocation-model=pic
RUSTC_MISC_ARGS = -C target-cpu=cortex-a53
+ CHAINBOOT_DEMO_PAYLOAD = demo_payload_rpi3.img
else ifeq ($(BSP),rpi4)
TARGET = aarch64-unknown-none-softfloat
KERNEL_BIN = kernel8.img
@@ -35,7 +36,8 @@
NM_BINARY = aarch64-none-elf-nm
@@ -36,6 +37,7 @@
READELF_BINARY = aarch64-none-elf-readelf
LINKER_FILE = src/bsp/raspberrypi/link.ld
- RUSTC_MISC_ARGS = -C target-cpu=cortex-a72
+ RUSTC_MISC_ARGS = -C target-cpu=cortex-a72 -C relocation-model=pic
RUSTC_MISC_ARGS = -C target-cpu=cortex-a72
+ CHAINBOOT_DEMO_PAYLOAD = demo_payload_rpi4.img
endif
@ -166,47 +176,47 @@ diff -uNr 06_drivers_gpio_uart/Makefile 07_uart_chainloader/Makefile
clippy:
@RUSTFLAGS="$(RUSTFLAGS_PEDANTIC)" $(CLIPPY_CMD)
diff -uNr 06_drivers_gpio_uart/src/_arch/aarch64/cpu/boot.rs 07_uart_chainloader/src/_arch/aarch64/cpu/boot.rs
--- 06_drivers_gpio_uart/src/_arch/aarch64/cpu/boot.rs
+++ 07_uart_chainloader/src/_arch/aarch64/cpu/boot.rs
@@ -29,11 +29,11 @@
/// actually set (`SP.set()`).
#[no_mangle]
pub unsafe fn _start() -> ! {
- use crate::runtime_init;
+ use crate::relocate;
if bsp::cpu::BOOT_CORE_ID == cpu::smp::core_id() {
SP.set(bsp::memory::boot_core_stack_end() as u64);
- runtime_init::runtime_init()
+ relocate::relocate_self()
} else {
// If not core0, infinitely wait for events.
cpu::wait_forever()
diff -uNr 06_drivers_gpio_uart/src/_arch/aarch64/cpu.rs 07_uart_chainloader/src/_arch/aarch64/cpu.rs
--- 06_drivers_gpio_uart/src/_arch/aarch64/cpu.rs
+++ 07_uart_chainloader/src/_arch/aarch64/cpu.rs
@@ -35,3 +35,19 @@
asm::wfe()
}
}
diff -uNr 06_drivers_gpio_uart/src/_arch/aarch64/cpu/boot.s 07_uart_chainloader/src/_arch/aarch64/cpu/boot.s
--- 06_drivers_gpio_uart/src/_arch/aarch64/cpu/boot.s
+++ 07_uart_chainloader/src/_arch/aarch64/cpu/boot.s
@@ -22,20 +22,31 @@
and x1, x1, _core_id_mask
ldr x2, BOOT_CORE_ID // provided by bsp/__board_name__/cpu.rs
cmp x1, x2
- b.ne 1f
+ b.ne 2f
- // If execution reaches here, it is the boot core. Now, prepare the jump to Rust code.
+ // If execution reaches here, it is the boot core.
+
+/// Branch to a raw integer value.
+///
+/// # Safety
+///
+/// - This is highly unsafe. Use with care.
+#[inline(always)]
+pub unsafe fn branch_to_raw_addr(addr: usize) -> ! {
+ asm!(
+ "blr {destination:x}",
+ destination = in(reg) addr,
+ options(nomem, nostack)
+ );
+ // Next, relocate the binary.
+ adr x0, __binary_nonzero_start // The address the binary got loaded to.
+ ldr x1, =__binary_nonzero_start // The address the binary was linked to.
+ ldr x2, =__binary_nonzero_end_exclusive
+
+ core::intrinsics::unreachable()
+}
+1: ldr x3, [x0], #8
+ str x3, [x1], #8
+ cmp x1, x2
+ b.lo 1b
// Set the stack pointer.
ldr x0, =__boot_core_stack_end_exclusive
mov sp, x0
- // Jump to Rust code.
- b _start_rust
+ // Jump to the relocated Rust code.
+ ldr x1, =_start_rust
+ br x1
// Infinitely wait for events (aka "park the core").
-1: wfe
- b 1b
+2: wfe
+ b 2b
.size _start, . - _start
.type _start, function
diff -uNr 06_drivers_gpio_uart/src/bsp/device_driver/bcm/bcm2xxx_gpio.rs 07_uart_chainloader/src/bsp/device_driver/bcm/bcm2xxx_gpio.rs
--- 06_drivers_gpio_uart/src/bsp/device_driver/bcm/bcm2xxx_gpio.rs
@ -268,58 +278,46 @@ diff -uNr 06_drivers_gpio_uart/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs 0
diff -uNr 06_drivers_gpio_uart/src/bsp/raspberrypi/link.ld 07_uart_chainloader/src/bsp/raspberrypi/link.ld
--- 06_drivers_gpio_uart/src/bsp/raspberrypi/link.ld
+++ 07_uart_chainloader/src/bsp/raspberrypi/link.ld
@@ -16,12 +16,13 @@
@@ -16,7 +16,8 @@
SECTIONS
{
- . = __rpi_load_addr;
+ /* Set the link address to 32 MiB */
+ . = 0x2000000;
/* ^ */
/* | stack */
/* | growth */
@@ -26,6 +27,7 @@
/***********************************************************************************************
* Code + RO Data + Global Offset Table
***********************************************************************************************/
- __rx_start = .;
+ __binary_start = .;
+ __binary_nonzero_start = .;
.text :
{
KEEP(*(.text._start))
@@ -46,4 +47,10 @@
. += 8; /* Fill for the bss == 0 case, so that __bss_start <= __bss_end_inclusive holds */
__bss_end_inclusive = . - 8;
} :NONE
+
@@ -49,8 +51,12 @@
***********************************************************************************************/
.data : { *(.data*) } :segment_rw
+ /* Fill up to 8 byte, b/c relocating the binary is done in u64 chunks */
+ . = ALIGN(8);
+ __binary_end_inclusive = . - 8;
+ __binary_nonzero_end_exclusive = .;
+
+ __runtime_init_reloc = runtime_init;
}
/* Section is zeroed in u64 chunks, align start and end to 8 bytes */
- .bss : ALIGN(8)
+ .bss :
{
__bss_start = .;
*(.bss*);
diff -uNr 06_drivers_gpio_uart/src/bsp/raspberrypi/memory.rs 07_uart_chainloader/src/bsp/raspberrypi/memory.rs
--- 06_drivers_gpio_uart/src/bsp/raspberrypi/memory.rs
+++ 07_uart_chainloader/src/bsp/raspberrypi/memory.rs
@@ -12,10 +12,12 @@
// Symbols from the linker script.
extern "Rust" {
- static __rx_start: UnsafeCell<()>;
-
+ static __binary_start: UnsafeCell<u64>;
static __bss_start: UnsafeCell<u64>;
static __bss_end_inclusive: UnsafeCell<u64>;
+ static __binary_end_inclusive: UnsafeCell<u64>;
+
+ static __runtime_init_reloc: UnsafeCell<u64>;
}
//--------------------------------------------------------------------------------------------------
@@ -25,9 +27,12 @@
@@ -23,9 +23,10 @@
/// The board's physical memory map.
#[rustfmt::skip]
pub(super) mod map {
+ pub const BOOT_CORE_STACK_END: usize = 0x8_0000;
+
+ pub const BOARD_DEFAULT_LOAD_ADDRESS: usize = 0x8_0000;
- pub const GPIO_OFFSET: usize = 0x0020_0000;
@ -329,111 +327,34 @@ diff -uNr 06_drivers_gpio_uart/src/bsp/raspberrypi/memory.rs 07_uart_chainloader
/// Physical devices.
#[cfg(feature = "bsp_rpi3")]
@@ -51,36 +56,44 @@
}
//--------------------------------------------------------------------------------------------------
-// Private Code
+// Public Code
@@ -52,7 +53,13 @@
// Public Code
//--------------------------------------------------------------------------------------------------
-/// Start address of the Read+Execute (RX) range.
+/// Exclusive end address of the boot core's stack.
+#[inline(always)]
+pub fn boot_core_stack_end() -> usize {
+ map::BOOT_CORE_STACK_END
+}
+
-/// Return the inclusive range spanning the .bss section.
+/// The address on which the Raspberry firmware loads every binary by default.
+#[inline(always)]
+pub fn board_default_load_addr() -> *const u64 {
+ map::BOARD_DEFAULT_LOAD_ADDRESS as _
+}
+
+/// Return the inclusive range spanning the relocated kernel binary.
///
/// # Safety
///
-/// - Value is provided by the linker script and must be trusted as-is.
-#[inline(always)]
-fn rx_start() -> usize {
- unsafe { __rx_start.get() as usize }
+/// - Values are provided by the linker script and must be trusted as-is.
+/// - The linker-provided addresses must be u64 aligned.
+pub fn relocated_binary_range_inclusive() -> RangeInclusive<*mut u64> {
+ unsafe { RangeInclusive::new(__binary_start.get(), __binary_end_inclusive.get()) }
}
-//--------------------------------------------------------------------------------------------------
-// Public Code
-//--------------------------------------------------------------------------------------------------
-
-/// Exclusive end address of the boot core's stack.
+/// The relocated address of function `runtime_init()`.
#[inline(always)]
-pub fn boot_core_stack_end() -> usize {
- rx_start()
+pub fn relocated_runtime_init_addr() -> *const u64 {
+ unsafe { __runtime_init_reloc.get() as _ }
}
-/// Return the inclusive range spanning the .bss section.
+/// Return the inclusive range spanning the relocated .bss section.
///
/// # Safety
///
/// - Values are provided by the linker script and must be trusted as-is.
/// - The linker-provided addresses must be u64 aligned.
-pub fn bss_range_inclusive() -> RangeInclusive<*mut u64> {
+pub fn relocated_bss_range_inclusive() -> RangeInclusive<*mut u64> {
let range;
unsafe {
range = RangeInclusive::new(__bss_start.get(), __bss_end_inclusive.get());
diff -uNr 06_drivers_gpio_uart/src/cpu.rs 07_uart_chainloader/src/cpu.rs
--- 06_drivers_gpio_uart/src/cpu.rs
+++ 07_uart_chainloader/src/cpu.rs
@@ -15,7 +15,7 @@
//--------------------------------------------------------------------------------------------------
// Architectural Public Reexports
//--------------------------------------------------------------------------------------------------
-pub use arch_cpu::{nop, wait_forever};
+pub use arch_cpu::{branch_to_raw_addr, nop, wait_forever};
#[cfg(feature = "bsp_rpi3")]
pub use arch_cpu::spin_for_cycles;
diff -uNr 06_drivers_gpio_uart/src/main.rs 07_uart_chainloader/src/main.rs
--- 06_drivers_gpio_uart/src/main.rs
+++ 07_uart_chainloader/src/main.rs
@@ -102,13 +102,17 @@
//!
//! 1. The kernel's entry point is the function [`cpu::boot::arch_boot::_start()`].
//! - It is implemented in `src/_arch/__arch_name__/cpu/boot.rs`.
-//! 2. Once finished with architectural setup, the arch code calls [`runtime_init::runtime_init()`].
+//! 2. Once finished with architectural setup, the arch code calls [`relocate::relocate_self()`].
+//! 3. Finally, [`runtime_init::runtime_init()`] is called.
//!
//! [`cpu::boot::arch_boot::_start()`]: cpu/boot/arch_boot/fn._start.html
+//! [`relocate::relocate_self()`]: relocate/fn.relocate_self.html
@@ -107,6 +107,7 @@
//! [`runtime_init::runtime_init()`]: runtime_init/fn.runtime_init.html
#![allow(clippy::clippy::upper_case_acronyms)]
+#![feature(asm)]
#![feature(const_fn_fn_ptr_basics)]
+#![feature(core_intrinsics)]
#![feature(format_args_nl)]
#![feature(panic_info_message)]
#![feature(trait_alias)]
@@ -122,6 +126,7 @@
mod memory;
mod panic_wait;
mod print;
+mod relocate;
mod runtime_init;
mod synchronization;
@@ -150,29 +155,49 @@
#![feature(global_asm)]
@@ -150,29 +151,49 @@
fn kernel_main() -> ! {
use bsp::console::console;
use console::interface::All;
@ -504,81 +425,6 @@ diff -uNr 06_drivers_gpio_uart/src/main.rs 07_uart_chainloader/src/main.rs
+ kernel()
}
diff -uNr 06_drivers_gpio_uart/src/relocate.rs 07_uart_chainloader/src/relocate.rs
--- 06_drivers_gpio_uart/src/relocate.rs
+++ 07_uart_chainloader/src/relocate.rs
@@ -0,0 +1,49 @@
+// SPDX-License-Identifier: MIT OR Apache-2.0
+//
+// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
+
+//! Relocation code.
+
+use crate::{bsp, cpu};
+
+//--------------------------------------------------------------------------------------------------
+// Public Code
+//--------------------------------------------------------------------------------------------------
+
+/// Relocates the own binary from `bsp::memory::board_default_load_addr()` to the `__binary_start`
+/// address from the linker script.
+///
+/// # Safety
+///
+/// - Only a single core must be active and running this function.
+/// - Function must not use the `bss` section.
+#[inline(never)]
+pub unsafe fn relocate_self() -> ! {
+ let range = bsp::memory::relocated_binary_range_inclusive();
+ let mut relocated_binary_start_addr = *range.start();
+ let relocated_binary_end_addr_inclusive = *range.end();
+
+ // The address of where the previous firmware loaded us.
+ let mut current_binary_start_addr = bsp::memory::board_default_load_addr();
+
+ // Copy the whole binary.
+ while relocated_binary_start_addr <= relocated_binary_end_addr_inclusive {
+ core::ptr::write_volatile(
+ relocated_binary_start_addr,
+ core::ptr::read_volatile(current_binary_start_addr),
+ );
+ relocated_binary_start_addr = relocated_binary_start_addr.offset(1);
+ current_binary_start_addr = current_binary_start_addr.offset(1);
+ }
+
+ // The following function calls realize an "absolute jump" to `runtime_init::runtime_init()` by
+ // forcing an indirection through the global offset table (GOT), so that execution continues
+ // from the relocated binary.
+ //
+ // Without the indirection through the assembly, the address of `runtime_init()` would be
+ // calculated as a relative offset from the current program counter, since we are compiling as
+ // `position independent code`. This would cause us to keep executing from the address to which
+ // the firmware loaded us, instead of the relocated position.
+ let relocated_runtime_init_addr = bsp::memory::relocated_runtime_init_addr() as usize;
+ cpu::branch_to_raw_addr(relocated_runtime_init_addr)
+}
diff -uNr 06_drivers_gpio_uart/src/runtime_init.rs 07_uart_chainloader/src/runtime_init.rs
--- 06_drivers_gpio_uart/src/runtime_init.rs
+++ 07_uart_chainloader/src/runtime_init.rs
@@ -17,7 +17,7 @@
/// - Must only be called pre `kernel_init()`.
#[inline(always)]
unsafe fn zero_bss() {
- memory::zero_volatile(bsp::memory::bss_range_inclusive());
+ memory::zero_volatile(bsp::memory::relocated_bss_range_inclusive());
}
//--------------------------------------------------------------------------------------------------
@@ -30,6 +30,7 @@
/// # Safety
///
/// - Only a single core must be active and running this function.
+#[no_mangle]
pub unsafe fn runtime_init() -> ! {
zero_bss();
diff -uNr 06_drivers_gpio_uart/update.sh 07_uart_chainloader/update.sh
--- 06_drivers_gpio_uart/update.sh
+++ 07_uart_chainloader/update.sh

@ -35,19 +35,3 @@ pub fn wait_forever() -> ! {
asm::wfe()
}
}
/// Branch to a raw integer value.
///
/// # Safety
///
/// - This is highly unsafe. Use with care.
#[inline(always)]
pub unsafe fn branch_to_raw_addr(addr: usize) -> ! {
asm!(
"blr {destination:x}",
destination = in(reg) addr,
options(nomem, nostack)
);
core::intrinsics::unreachable()
}

@ -11,31 +11,23 @@
//!
//! crate::cpu::boot::arch_boot
use crate::{bsp, cpu};
use cortex_a::regs::*;
use crate::runtime_init;
// Assembly counterpart to this file.
global_asm!(include_str!("boot.s"));
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
/// The entry of the `kernel` binary.
/// The Rust entry of the `kernel` binary.
///
/// The function must be named `_start`, because the linker is looking for this exact name.
/// The function is called from the assembly `_start` function.
///
/// # Safety
///
/// - Linker script must ensure to place this function where it is expected by the target machine.
/// - We have to hope that the compiler omits any stack pointer usage before the stack pointer is
/// actually set (`SP.set()`).
/// - The `bss` section is not initialized yet. The code must not use or reference it in any way.
#[no_mangle]
pub unsafe fn _start() -> ! {
use crate::relocate;
if bsp::cpu::BOOT_CORE_ID == cpu::smp::core_id() {
SP.set(bsp::memory::boot_core_stack_end() as u64);
relocate::relocate_self()
} else {
// If not core0, infinitely wait for events.
cpu::wait_forever()
}
pub unsafe fn _start_rust() -> ! {
runtime_init::runtime_init()
}

@ -0,0 +1,53 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2021 Andre Richter <andre.o.richter@gmail.com>
//--------------------------------------------------------------------------------------------------
// Definitions
//--------------------------------------------------------------------------------------------------
.equ _core_id_mask, 0b11
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
.section .text._start
//------------------------------------------------------------------------------
// fn _start()
//------------------------------------------------------------------------------
_start:
// Only proceed on the boot core. Park it otherwise.
mrs x1, MPIDR_EL1
and x1, x1, _core_id_mask
ldr x2, BOOT_CORE_ID // provided by bsp/__board_name__/cpu.rs
cmp x1, x2
b.ne 2f
// If execution reaches here, it is the boot core.
// Next, relocate the binary.
adr x0, __binary_nonzero_start // The address the binary got loaded to.
ldr x1, =__binary_nonzero_start // The address the binary was linked to.
ldr x2, =__binary_nonzero_end_exclusive
1: ldr x3, [x0], #8
str x3, [x1], #8
cmp x1, x2
b.lo 1b
// Set the stack pointer.
ldr x0, =__boot_core_stack_end_exclusive
mov sp, x0
// Jump to the relocated Rust code.
ldr x1, =_start_rust
br x1
// Infinitely wait for events (aka "park the core").
2: wfe
b 2b
.size _start, . - _start
.type _start, function
.global _start

@ -1,29 +0,0 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
//! Architectural symmetric multiprocessing.
//!
//! # Orientation
//!
//! Since arch modules are imported into generic modules using the path attribute, the path of this
//! file is:
//!
//! crate::cpu::smp::arch_smp
use cortex_a::regs::*;
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
/// Return the executing core's id.
#[inline(always)]
pub fn core_id<T>() -> T
where
T: From<u8>,
{
const CORE_MASK: u64 = 0b11;
T::from((MPIDR_EL1.get() & CORE_MASK) as u8)
}

@ -9,4 +9,6 @@
//--------------------------------------------------------------------------------------------------
/// Used by `arch` code to find the early boot core.
pub const BOOT_CORE_ID: u64 = 0;
#[no_mangle]
#[link_section = ".text._start_arguments"]
pub static BOOT_CORE_ID: u64 = 0;

@ -18,15 +18,29 @@ SECTIONS
{
/* Set the link address to 32 MiB */
. = 0x2000000;
/* ^ */
/* | stack */
/* | growth */
/* | direction */
__boot_core_stack_end_exclusive = .; /* | */
/***********************************************************************************************
* Code + RO Data + Global Offset Table
***********************************************************************************************/
__binary_start = .;
__binary_nonzero_start = .;
.text :
{
KEEP(*(.text._start))
*(.text*)
/* Special constants (or statics in Rust speak) needed by _start().
*
* They are placed in close proximity to _start() from where they will be read. This ensures
* that position-independent, PC-relative loads can be emitted.
*/
*(.text._start_arguments)
*(.text._start_rust) /* The Rust entry point */
*(.text*) /* Everything else */
} :segment_rx
.rodata : ALIGN(8) { *(.rodata*) } :segment_rx
@ -37,8 +51,12 @@ SECTIONS
***********************************************************************************************/
.data : { *(.data*) } :segment_rw
/* Fill up to 8 byte, b/c relocating the binary is done in u64 chunks */
. = ALIGN(8);
__binary_nonzero_end_exclusive = .;
/* Section is zeroed in u64 chunks, align start and end to 8 bytes */
.bss : ALIGN(8)
.bss :
{
__bss_start = .;
*(.bss*);
@ -47,10 +65,4 @@ SECTIONS
. += 8; /* Fill for the bss == 0 case, so that __bss_start <= __bss_end_inclusive holds */
__bss_end_inclusive = . - 8;
} :NONE
/* Fill up to 8 byte, b/c relocating the binary is done in u64 chunks */
. = ALIGN(8);
__binary_end_inclusive = . - 8;
__runtime_init_reloc = runtime_init;
}

@ -12,12 +12,8 @@ use core::{cell::UnsafeCell, ops::RangeInclusive};
// Symbols from the linker script.
extern "Rust" {
static __binary_start: UnsafeCell<u64>;
static __bss_start: UnsafeCell<u64>;
static __bss_end_inclusive: UnsafeCell<u64>;
static __binary_end_inclusive: UnsafeCell<u64>;
static __runtime_init_reloc: UnsafeCell<u64>;
}
//--------------------------------------------------------------------------------------------------
@ -27,8 +23,6 @@ extern "Rust" {
/// The board's physical memory map.
#[rustfmt::skip]
pub(super) mod map {
pub const BOOT_CORE_STACK_END: usize = 0x8_0000;
pub const BOARD_DEFAULT_LOAD_ADDRESS: usize = 0x8_0000;
pub const GPIO_OFFSET: usize = 0x0020_0000;
@ -59,41 +53,19 @@ pub(super) mod map {
// Public Code
//--------------------------------------------------------------------------------------------------
/// Exclusive end address of the boot core's stack.
#[inline(always)]
pub fn boot_core_stack_end() -> usize {
map::BOOT_CORE_STACK_END
}
/// The address on which the Raspberry firmware loads every binary by default.
#[inline(always)]
pub fn board_default_load_addr() -> *const u64 {
map::BOARD_DEFAULT_LOAD_ADDRESS as _
}
/// Return the inclusive range spanning the relocated kernel binary.
///
/// # Safety
///
/// - Values are provided by the linker script and must be trusted as-is.
/// - The linker-provided addresses must be u64 aligned.
pub fn relocated_binary_range_inclusive() -> RangeInclusive<*mut u64> {
unsafe { RangeInclusive::new(__binary_start.get(), __binary_end_inclusive.get()) }
}
/// The relocated address of function `runtime_init()`.
#[inline(always)]
pub fn relocated_runtime_init_addr() -> *const u64 {
unsafe { __runtime_init_reloc.get() as _ }
}
/// Return the inclusive range spanning the relocated .bss section.
///
/// # Safety
///
/// - Values are provided by the linker script and must be trusted as-is.
/// - The linker-provided addresses must be u64 aligned.
pub fn relocated_bss_range_inclusive() -> RangeInclusive<*mut u64> {
pub fn bss_range_inclusive() -> RangeInclusive<*mut u64> {
let range;
unsafe {
range = RangeInclusive::new(__bss_start.get(), __bss_end_inclusive.get());

@ -10,12 +10,10 @@ mod arch_cpu;
mod boot;
pub mod smp;
//--------------------------------------------------------------------------------------------------
// Architectural Public Reexports
//--------------------------------------------------------------------------------------------------
pub use arch_cpu::{branch_to_raw_addr, nop, wait_forever};
pub use arch_cpu::{nop, wait_forever};
#[cfg(feature = "bsp_rpi3")]
pub use arch_cpu::spin_for_cycles;

@ -1,14 +0,0 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
//! Symmetric multiprocessing.
#[cfg(target_arch = "aarch64")]
#[path = "../_arch/aarch64/cpu/smp.rs"]
mod arch_smp;
//--------------------------------------------------------------------------------------------------
// Architectural Public Reexports
//--------------------------------------------------------------------------------------------------
pub use arch_smp::core_id;

@ -100,20 +100,17 @@
//!
//! # Boot flow
//!
//! 1. The kernel's entry point is the function [`cpu::boot::arch_boot::_start()`].
//! - It is implemented in `src/_arch/__arch_name__/cpu/boot.rs`.
//! 2. Once finished with architectural setup, the arch code calls [`relocate::relocate_self()`].
//! 3. Finally, [`runtime_init::runtime_init()`] is called.
//! 1. The kernel's entry point is the function `cpu::boot::arch_boot::_start()`.
//! - It is implemented in `src/_arch/__arch_name__/cpu/boot.s`.
//! 2. Once finished with architectural setup, the arch code calls [`runtime_init::runtime_init()`].
//!
//! [`cpu::boot::arch_boot::_start()`]: cpu/boot/arch_boot/fn._start.html
//! [`relocate::relocate_self()`]: relocate/fn.relocate_self.html
//! [`runtime_init::runtime_init()`]: runtime_init/fn.runtime_init.html
#![allow(clippy::clippy::upper_case_acronyms)]
#![feature(asm)]
#![feature(const_fn_fn_ptr_basics)]
#![feature(core_intrinsics)]
#![feature(format_args_nl)]
#![feature(global_asm)]
#![feature(panic_info_message)]
#![feature(trait_alias)]
#![no_main]
@ -126,7 +123,6 @@ mod driver;
mod memory;
mod panic_wait;
mod print;
mod relocate;
mod runtime_init;
mod synchronization;

@ -1,49 +0,0 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
//! Relocation code.
use crate::{bsp, cpu};
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
/// Relocates the own binary from `bsp::memory::board_default_load_addr()` to the `__binary_start`
/// address from the linker script.
///
/// # Safety
///
/// - Only a single core must be active and running this function.
/// - Function must not use the `bss` section.
#[inline(never)]
pub unsafe fn relocate_self() -> ! {
let range = bsp::memory::relocated_binary_range_inclusive();
let mut relocated_binary_start_addr = *range.start();
let relocated_binary_end_addr_inclusive = *range.end();
// The address of where the previous firmware loaded us.
let mut current_binary_start_addr = bsp::memory::board_default_load_addr();
// Copy the whole binary.
while relocated_binary_start_addr <= relocated_binary_end_addr_inclusive {
core::ptr::write_volatile(
relocated_binary_start_addr,
core::ptr::read_volatile(current_binary_start_addr),
);
relocated_binary_start_addr = relocated_binary_start_addr.offset(1);
current_binary_start_addr = current_binary_start_addr.offset(1);
}
// The following function calls realize an "absolute jump" to `runtime_init::runtime_init()` by
// forcing an indirection through the global offset table (GOT), so that execution continues
// from the relocated binary.
//
// Without the indirection through the assembly, the address of `runtime_init()` would be
// calculated as a relative offset from the current program counter, since we are compiling as
// `position independent code`. This would cause us to keep executing from the address to which
// the firmware loaded us, instead of the relocated position.
let relocated_runtime_init_addr = bsp::memory::relocated_runtime_init_addr() as usize;
cpu::branch_to_raw_addr(relocated_runtime_init_addr)
}

@ -17,7 +17,7 @@ use crate::{bsp, memory};
/// - Must only be called pre `kernel_init()`.
#[inline(always)]
unsafe fn zero_bss() {
memory::zero_volatile(bsp::memory::relocated_bss_range_inclusive());
memory::zero_volatile(bsp::memory::bss_range_inclusive());
}
//--------------------------------------------------------------------------------------------------
@ -30,7 +30,6 @@ unsafe fn zero_bss() {
/// # Safety
///
/// - Only a single core must be active and running this function.
#[no_mangle]
pub unsafe fn runtime_init() -> ! {
zero_bss();

@ -49,23 +49,19 @@ Binary files 07_uart_chainloader/demo_payload_rpi4.img and 08_timestamps/demo_pa
diff -uNr 07_uart_chainloader/Makefile 08_timestamps/Makefile
--- 07_uart_chainloader/Makefile
+++ 08_timestamps/Makefile
@@ -24,8 +24,7 @@
NM_BINARY = aarch64-none-elf-nm
@@ -25,7 +25,6 @@
READELF_BINARY = aarch64-none-elf-readelf
LINKER_FILE = src/bsp/raspberrypi/link.ld
- RUSTC_MISC_ARGS = -C target-cpu=cortex-a53 -C relocation-model=pic
RUSTC_MISC_ARGS = -C target-cpu=cortex-a53
- CHAINBOOT_DEMO_PAYLOAD = demo_payload_rpi3.img
+ RUSTC_MISC_ARGS = -C target-cpu=cortex-a53
else ifeq ($(BSP),rpi4)
TARGET = aarch64-unknown-none-softfloat
KERNEL_BIN = kernel8.img
@@ -36,8 +35,7 @@
NM_BINARY = aarch64-none-elf-nm
@@ -37,7 +36,6 @@
READELF_BINARY = aarch64-none-elf-readelf
LINKER_FILE = src/bsp/raspberrypi/link.ld
- RUSTC_MISC_ARGS = -C target-cpu=cortex-a72 -C relocation-model=pic
RUSTC_MISC_ARGS = -C target-cpu=cortex-a72
- CHAINBOOT_DEMO_PAYLOAD = demo_payload_rpi4.img
+ RUSTC_MISC_ARGS = -C target-cpu=cortex-a72
endif
# Export for build.rs
@ -97,23 +93,47 @@ diff -uNr 07_uart_chainloader/Makefile 08_timestamps/Makefile
clippy:
@RUSTFLAGS="$(RUSTFLAGS_PEDANTIC)" $(CLIPPY_CMD)
diff -uNr 07_uart_chainloader/src/_arch/aarch64/cpu/boot.rs 08_timestamps/src/_arch/aarch64/cpu/boot.rs
--- 07_uart_chainloader/src/_arch/aarch64/cpu/boot.rs
+++ 08_timestamps/src/_arch/aarch64/cpu/boot.rs
@@ -29,11 +29,11 @@
/// actually set (`SP.set()`).
#[no_mangle]
pub unsafe fn _start() -> ! {
- use crate::relocate;
+ use crate::runtime_init;
if bsp::cpu::BOOT_CORE_ID == cpu::smp::core_id() {
SP.set(bsp::memory::boot_core_stack_end() as u64);
- relocate::relocate_self()
+ runtime_init::runtime_init()
} else {
// If not core0, infinitely wait for events.
cpu::wait_forever()
diff -uNr 07_uart_chainloader/src/_arch/aarch64/cpu/boot.s 08_timestamps/src/_arch/aarch64/cpu/boot.s
--- 07_uart_chainloader/src/_arch/aarch64/cpu/boot.s
+++ 08_timestamps/src/_arch/aarch64/cpu/boot.s
@@ -22,31 +22,20 @@
and x1, x1, _core_id_mask
ldr x2, BOOT_CORE_ID // provided by bsp/__board_name__/cpu.rs
cmp x1, x2
- b.ne 2f
+ b.ne 1f
- // If execution reaches here, it is the boot core.
-
- // Next, relocate the binary.
- adr x0, __binary_nonzero_start // The address the binary got loaded to.
- ldr x1, =__binary_nonzero_start // The address the binary was linked to.
- ldr x2, =__binary_nonzero_end_exclusive
-
-1: ldr x3, [x0], #8
- str x3, [x1], #8
- cmp x1, x2
- b.lo 1b
+ // If execution reaches here, it is the boot core. Now, prepare the jump to Rust code.
// Set the stack pointer.
ldr x0, =__boot_core_stack_end_exclusive
mov sp, x0
- // Jump to the relocated Rust code.
- ldr x1, =_start_rust
- br x1
+ // Jump to Rust code.
+ b _start_rust
// Infinitely wait for events (aka "park the core").
-2: wfe
- b 2b
+1: wfe
+ b 1b
.size _start, . - _start
.type _start, function
diff -uNr 07_uart_chainloader/src/_arch/aarch64/cpu.rs 08_timestamps/src/_arch/aarch64/cpu.rs
--- 07_uart_chainloader/src/_arch/aarch64/cpu.rs
@ -134,26 +154,6 @@ diff -uNr 07_uart_chainloader/src/_arch/aarch64/cpu.rs 08_timestamps/src/_arch/a
/// Pause execution on the core.
#[inline(always)]
pub fn wait_forever() -> ! {
@@ -35,19 +26,3 @@
asm::wfe()
}
}
-
-/// Branch to a raw integer value.
-///
-/// # Safety
-///
-/// - This is highly unsafe. Use with care.
-#[inline(always)]
-pub unsafe fn branch_to_raw_addr(addr: usize) -> ! {
- asm!(
- "blr {destination:x}",
- destination = in(reg) addr,
- options(nomem, nostack)
- );
-
- core::intrinsics::unreachable()
-}
diff -uNr 07_uart_chainloader/src/_arch/aarch64/time.rs 08_timestamps/src/_arch/aarch64/time.rs
--- 07_uart_chainloader/src/_arch/aarch64/time.rs
@ -361,58 +361,46 @@ diff -uNr 07_uart_chainloader/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs 08
diff -uNr 07_uart_chainloader/src/bsp/raspberrypi/link.ld 08_timestamps/src/bsp/raspberrypi/link.ld
--- 07_uart_chainloader/src/bsp/raspberrypi/link.ld
+++ 08_timestamps/src/bsp/raspberrypi/link.ld
@@ -16,13 +16,12 @@
@@ -16,8 +16,7 @@
SECTIONS
{
- /* Set the link address to 32 MiB */
- . = 0x2000000;
+ . = __rpi_load_addr;
/* ^ */
/* | stack */
/* | growth */
@@ -27,7 +26,6 @@
/***********************************************************************************************
* Code + RO Data + Global Offset Table
***********************************************************************************************/
- __binary_start = .;
+ __rx_start = .;
- __binary_nonzero_start = .;
.text :
{
KEEP(*(.text._start))
@@ -47,10 +46,4 @@
. += 8; /* Fill for the bss == 0 case, so that __bss_start <= __bss_end_inclusive holds */
__bss_end_inclusive = . - 8;
} :NONE
-
@@ -51,12 +49,8 @@
***********************************************************************************************/
.data : { *(.data*) } :segment_rw
- /* Fill up to 8 byte, b/c relocating the binary is done in u64 chunks */
- . = ALIGN(8);
- __binary_end_inclusive = . - 8;
- __binary_nonzero_end_exclusive = .;
-
- __runtime_init_reloc = runtime_init;
}
/* Section is zeroed in u64 chunks, align start and end to 8 bytes */
- .bss :
+ .bss : ALIGN(8)
{
__bss_start = .;
*(.bss*);
diff -uNr 07_uart_chainloader/src/bsp/raspberrypi/memory.rs 08_timestamps/src/bsp/raspberrypi/memory.rs
--- 07_uart_chainloader/src/bsp/raspberrypi/memory.rs
+++ 08_timestamps/src/bsp/raspberrypi/memory.rs
@@ -12,12 +12,10 @@
// Symbols from the linker script.
extern "Rust" {
- static __binary_start: UnsafeCell<u64>;
+ static __rx_start: UnsafeCell<()>;
+
static __bss_start: UnsafeCell<u64>;
static __bss_end_inclusive: UnsafeCell<u64>;
- static __binary_end_inclusive: UnsafeCell<u64>;
-
- static __runtime_init_reloc: UnsafeCell<u64>;
}
//--------------------------------------------------------------------------------------------------
@@ -27,12 +25,9 @@
@@ -23,10 +23,9 @@
/// The board's physical memory map.
#[rustfmt::skip]
pub(super) mod map {
- pub const BOOT_CORE_STACK_END: usize = 0x8_0000;
-
- pub const BOARD_DEFAULT_LOAD_ADDRESS: usize = 0x8_0000;
- pub const GPIO_OFFSET: usize = 0x0020_0000;
@ -422,114 +410,53 @@ diff -uNr 07_uart_chainloader/src/bsp/raspberrypi/memory.rs 08_timestamps/src/bs
/// Physical devices.
#[cfg(feature = "bsp_rpi3")]
@@ -56,44 +51,36 @@
}
//--------------------------------------------------------------------------------------------------
-// Public Code
+// Private Code
@@ -53,13 +52,7 @@
// Public Code
//--------------------------------------------------------------------------------------------------
-/// Exclusive end address of the boot core's stack.
-#[inline(always)]
-pub fn boot_core_stack_end() -> usize {
- map::BOOT_CORE_STACK_END
-}
-
-/// The address on which the Raspberry firmware loads every binary by default.
-#[inline(always)]
-pub fn board_default_load_addr() -> *const u64 {
- map::BOARD_DEFAULT_LOAD_ADDRESS as _
-}
-
-/// Return the inclusive range spanning the relocated kernel binary.
+/// Start address of the Read+Execute (RX) range.
///
/// # Safety
///
-/// - Values are provided by the linker script and must be trusted as-is.
-/// - The linker-provided addresses must be u64 aligned.
-pub fn relocated_binary_range_inclusive() -> RangeInclusive<*mut u64> {
- unsafe { RangeInclusive::new(__binary_start.get(), __binary_end_inclusive.get()) }
+/// - Value is provided by the linker script and must be trusted as-is.
+#[inline(always)]
+fn rx_start() -> usize {
+ unsafe { __rx_start.get() as usize }
}
-/// The relocated address of function `runtime_init()`.
+//--------------------------------------------------------------------------------------------------
+// Public Code
+//--------------------------------------------------------------------------------------------------
+
+/// Exclusive end address of the boot core's stack.
#[inline(always)]
-pub fn relocated_runtime_init_addr() -> *const u64 {
- unsafe { __runtime_init_reloc.get() as _ }
+pub fn boot_core_stack_end() -> usize {
+ rx_start()
}
-/// Return the inclusive range spanning the relocated .bss section.
+/// Return the inclusive range spanning the .bss section.
///
/// # Safety
///
/// - Values are provided by the linker script and must be trusted as-is.
/// - The linker-provided addresses must be u64 aligned.
-pub fn relocated_bss_range_inclusive() -> RangeInclusive<*mut u64> {
+pub fn bss_range_inclusive() -> RangeInclusive<*mut u64> {
let range;
unsafe {
range = RangeInclusive::new(__bss_start.get(), __bss_end_inclusive.get());
diff -uNr 07_uart_chainloader/src/cpu.rs 08_timestamps/src/cpu.rs
--- 07_uart_chainloader/src/cpu.rs
+++ 08_timestamps/src/cpu.rs
@@ -15,7 +15,4 @@
//--------------------------------------------------------------------------------------------------
@@ -14,6 +14,3 @@
// Architectural Public Reexports
//--------------------------------------------------------------------------------------------------
-pub use arch_cpu::{branch_to_raw_addr, nop, wait_forever};
pub use arch_cpu::{nop, wait_forever};
-
-#[cfg(feature = "bsp_rpi3")]
-pub use arch_cpu::spin_for_cycles;
+pub use arch_cpu::{nop, wait_forever};
diff -uNr 07_uart_chainloader/src/main.rs 08_timestamps/src/main.rs
--- 07_uart_chainloader/src/main.rs
+++ 08_timestamps/src/main.rs
@@ -102,17 +102,13 @@
//!
//! 1. The kernel's entry point is the function [`cpu::boot::arch_boot::_start()`].
//! - It is implemented in `src/_arch/__arch_name__/cpu/boot.rs`.
-//! 2. Once finished with architectural setup, the arch code calls [`relocate::relocate_self()`].
-//! 3. Finally, [`runtime_init::runtime_init()`] is called.
+//! 2. Once finished with architectural setup, the arch code calls [`runtime_init::runtime_init()`].
//!
//! [`cpu::boot::arch_boot::_start()`]: cpu/boot/arch_boot/fn._start.html
-//! [`relocate::relocate_self()`]: relocate/fn.relocate_self.html
@@ -107,7 +107,6 @@
//! [`runtime_init::runtime_init()`]: runtime_init/fn.runtime_init.html
#![allow(clippy::clippy::upper_case_acronyms)]
-#![feature(asm)]
#![feature(const_fn_fn_ptr_basics)]
-#![feature(core_intrinsics)]
#![feature(format_args_nl)]
#![feature(panic_info_message)]
#![feature(trait_alias)]
@@ -126,9 +122,9 @@
mod memory;
mod panic_wait;
#![feature(global_asm)]
@@ -125,6 +124,7 @@
mod print;
-mod relocate;
mod runtime_init;
mod synchronization;
+mod time;
/// Early init code.
///
@@ -153,51 +149,31 @@
@@ -149,51 +149,31 @@
/// The main function running after the early init.
fn kernel_main() -> ! {
@ -680,81 +607,6 @@ diff -uNr 07_uart_chainloader/src/print.rs 08_timestamps/src/print.rs
+ })
+}
diff -uNr 07_uart_chainloader/src/relocate.rs 08_timestamps/src/relocate.rs
--- 07_uart_chainloader/src/relocate.rs
+++ 08_timestamps/src/relocate.rs
@@ -1,49 +0,0 @@
-// SPDX-License-Identifier: MIT OR Apache-2.0
-//
-// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
-
-//! Relocation code.
-
-use crate::{bsp, cpu};
-
-//--------------------------------------------------------------------------------------------------
-// Public Code
-//--------------------------------------------------------------------------------------------------
-
-/// Relocates the own binary from `bsp::memory::board_default_load_addr()` to the `__binary_start`
-/// address from the linker script.
-///
-/// # Safety
-///
-/// - Only a single core must be active and running this function.
-/// - Function must not use the `bss` section.
-#[inline(never)]
-pub unsafe fn relocate_self() -> ! {
- let range = bsp::memory::relocated_binary_range_inclusive();
- let mut relocated_binary_start_addr = *range.start();
- let relocated_binary_end_addr_inclusive = *range.end();
-
- // The address of where the previous firmware loaded us.
- let mut current_binary_start_addr = bsp::memory::board_default_load_addr();
-
- // Copy the whole binary.
- while relocated_binary_start_addr <= relocated_binary_end_addr_inclusive {
- core::ptr::write_volatile(
- relocated_binary_start_addr,
- core::ptr::read_volatile(current_binary_start_addr),
- );
- relocated_binary_start_addr = relocated_binary_start_addr.offset(1);
- current_binary_start_addr = current_binary_start_addr.offset(1);
- }
-
- // The following function calls realize an "absolute jump" to `runtime_init::runtime_init()` by
- // forcing an indirection through the global offset table (GOT), so that execution continues
- // from the relocated binary.
- //
- // Without the indirection through the assembly, the address of `runtime_init()` would be
- // calculated as a relative offset from the current program counter, since we are compiling as
- // `position independent code`. This would cause us to keep executing from the address to which
- // the firmware loaded us, instead of the relocated position.
- let relocated_runtime_init_addr = bsp::memory::relocated_runtime_init_addr() as usize;
- cpu::branch_to_raw_addr(relocated_runtime_init_addr)
-}
diff -uNr 07_uart_chainloader/src/runtime_init.rs 08_timestamps/src/runtime_init.rs
--- 07_uart_chainloader/src/runtime_init.rs
+++ 08_timestamps/src/runtime_init.rs
@@ -17,7 +17,7 @@
/// - Must only be called pre `kernel_init()`.
#[inline(always)]
unsafe fn zero_bss() {
- memory::zero_volatile(bsp::memory::relocated_bss_range_inclusive());
+ memory::zero_volatile(bsp::memory::bss_range_inclusive());
}
//--------------------------------------------------------------------------------------------------
@@ -30,7 +30,6 @@
/// # Safety
///
/// - Only a single core must be active and running this function.
-#[no_mangle]
pub unsafe fn runtime_init() -> ! {
zero_bss();
diff -uNr 07_uart_chainloader/src/time.rs 08_timestamps/src/time.rs
--- 07_uart_chainloader/src/time.rs
+++ 08_timestamps/src/time.rs

@ -11,31 +11,23 @@
//!
//! crate::cpu::boot::arch_boot
use crate::{bsp, cpu};
use cortex_a::regs::*;
use crate::runtime_init;
// Assembly counterpart to this file.
global_asm!(include_str!("boot.s"));
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
/// The entry of the `kernel` binary.
/// The Rust entry of the `kernel` binary.
///
/// The function must be named `_start`, because the linker is looking for this exact name.
/// The function is called from the assembly `_start` function.
///
/// # Safety
///
/// - Linker script must ensure to place this function where it is expected by the target machine.
/// - We have to hope that the compiler omits any stack pointer usage before the stack pointer is
/// actually set (`SP.set()`).
/// - The `bss` section is not initialized yet. The code must not use or reference it in any way.
#[no_mangle]
pub unsafe fn _start() -> ! {
use crate::runtime_init;
if bsp::cpu::BOOT_CORE_ID == cpu::smp::core_id() {
SP.set(bsp::memory::boot_core_stack_end() as u64);
runtime_init::runtime_init()
} else {
// If not core0, infinitely wait for events.
cpu::wait_forever()
}
pub unsafe fn _start_rust() -> ! {
runtime_init::runtime_init()
}

@ -0,0 +1,42 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2021 Andre Richter <andre.o.richter@gmail.com>
//--------------------------------------------------------------------------------------------------
// Definitions
//--------------------------------------------------------------------------------------------------
.equ _core_id_mask, 0b11
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
.section .text._start
//------------------------------------------------------------------------------
// fn _start()
//------------------------------------------------------------------------------
_start:
// Only proceed on the boot core. Park it otherwise.
mrs x1, MPIDR_EL1
and x1, x1, _core_id_mask
ldr x2, BOOT_CORE_ID // provided by bsp/__board_name__/cpu.rs
cmp x1, x2
b.ne 1f
// If execution reaches here, it is the boot core. Now, prepare the jump to Rust code.
// Set the stack pointer.
ldr x0, =__boot_core_stack_end_exclusive
mov sp, x0
// Jump to Rust code.
b _start_rust
// Infinitely wait for events (aka "park the core").
1: wfe
b 1b
.size _start, . - _start
.type _start, function
.global _start

@ -1,29 +0,0 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
//! Architectural symmetric multiprocessing.
//!
//! # Orientation
//!
//! Since arch modules are imported into generic modules using the path attribute, the path of this
//! file is:
//!
//! crate::cpu::smp::arch_smp
use cortex_a::regs::*;
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
/// Return the executing core's id.
#[inline(always)]
pub fn core_id<T>() -> T
where
T: From<u8>,
{
const CORE_MASK: u64 = 0b11;
T::from((MPIDR_EL1.get() & CORE_MASK) as u8)
}

@ -9,4 +9,6 @@
//--------------------------------------------------------------------------------------------------
/// Used by `arch` code to find the early boot core.
pub const BOOT_CORE_ID: u64 = 0;
#[no_mangle]
#[link_section = ".text._start_arguments"]
pub static BOOT_CORE_ID: u64 = 0;

@ -17,15 +17,28 @@ PHDRS
SECTIONS
{
. = __rpi_load_addr;
/* ^ */
/* | stack */
/* | growth */
/* | direction */
__boot_core_stack_end_exclusive = .; /* | */
/***********************************************************************************************
* Code + RO Data + Global Offset Table
***********************************************************************************************/
__rx_start = .;
.text :
{
KEEP(*(.text._start))
*(.text*)
/* Special constants (or statics in Rust speak) needed by _start().
*
* They are placed in close proximity to _start() from where they will be read. This ensures
* that position-independent, PC-relative loads can be emitted.
*/
*(.text._start_arguments)
*(.text._start_rust) /* The Rust entry point */
*(.text*) /* Everything else */
} :segment_rx
.rodata : ALIGN(8) { *(.rodata*) } :segment_rx

@ -12,8 +12,6 @@ use core::{cell::UnsafeCell, ops::RangeInclusive};
// Symbols from the linker script.
extern "Rust" {
static __rx_start: UnsafeCell<()>;
static __bss_start: UnsafeCell<u64>;
static __bss_end_inclusive: UnsafeCell<u64>;
}
@ -50,30 +48,10 @@ pub(super) mod map {
}
}
//--------------------------------------------------------------------------------------------------
// Private Code
//--------------------------------------------------------------------------------------------------
/// Start address of the Read+Execute (RX) range.
///
/// # Safety
///
/// - Value is provided by the linker script and must be trusted as-is.
#[inline(always)]
fn rx_start() -> usize {
unsafe { __rx_start.get() as usize }
}
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
/// Exclusive end address of the boot core's stack.
#[inline(always)]
pub fn boot_core_stack_end() -> usize {
rx_start()
}
/// Return the inclusive range spanning the .bss section.
///
/// # Safety

@ -10,8 +10,6 @@ mod arch_cpu;
mod boot;
pub mod smp;
//--------------------------------------------------------------------------------------------------
// Architectural Public Reexports
//--------------------------------------------------------------------------------------------------

@ -1,14 +0,0 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
//! Symmetric multiprocessing.
#[cfg(target_arch = "aarch64")]
#[path = "../_arch/aarch64/cpu/smp.rs"]
mod arch_smp;
//--------------------------------------------------------------------------------------------------
// Architectural Public Reexports
//--------------------------------------------------------------------------------------------------
pub use arch_smp::core_id;

@ -100,16 +100,16 @@
//!
//! # Boot flow
//!
//! 1. The kernel's entry point is the function [`cpu::boot::arch_boot::_start()`].
//! - It is implemented in `src/_arch/__arch_name__/cpu/boot.rs`.
//! 1. The kernel's entry point is the function `cpu::boot::arch_boot::_start()`.
//! - It is implemented in `src/_arch/__arch_name__/cpu/boot.s`.
//! 2. Once finished with architectural setup, the arch code calls [`runtime_init::runtime_init()`].
//!
//! [`cpu::boot::arch_boot::_start()`]: cpu/boot/arch_boot/fn._start.html
//! [`runtime_init::runtime_init()`]: runtime_init/fn.runtime_init.html
#![allow(clippy::clippy::upper_case_acronyms)]
#![feature(const_fn_fn_ptr_basics)]
#![feature(format_args_nl)]
#![feature(global_asm)]
#![feature(panic_info_message)]
#![feature(trait_alias)]
#![no_main]

@ -54,6 +54,7 @@ infrastructure for JTAG debugging around it.
We need to add another line to the `config.txt` file from the SD Card:
```toml
arm_64bit=1
init_uart_clock=48000000
enable_jtag_gpio=1
```
@ -234,15 +235,16 @@ $ make gdb
[...]
>>> target remote :3333 # Connect to OpenOCD, core0
>>> load # Load the kernel into the RPi's DRAM over JTAG.
Loading section .text, size 0x2340 lma 0x80000
Loading section .rodata, size 0xc2d lma 0x82340
Loading section .data, size 0x20 lma 0x82f70
Start address 0x80000, load size 12173
Transfer rate: 65 KB/sec, 4057 bytes/write.
Loading section .text, size 0x2454 lma 0x80000
Loading section .rodata, size 0xa1d lma 0x82460
Loading section .got, size 0x10 lma 0x82e80
Loading section .data, size 0x20 lma 0x82e90
Start address 0x0000000000080000, load size 11937
Transfer rate: 63 KB/sec, 2984 bytes/write.
>>> set $pc = 0x80000 # Set RPI's program counter to the start of the
# kernel binary.
>>> break main.rs:70
Breakpoint 1 at 0x80108: file src/main.rs, line 153.
>>> break main.rs:158
Breakpoint 1 at 0x8025c: file src/main.rs, line 158.
>>> cont
>>> step # Single-step through the kernel
>>> step

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save