Refactoring

- Don't wildcard-import from arch modules. Make it explicit.
- Put translation table code into its own module.
- Put boot code in boot.rs instead of cpu.rs
- Other minor changes, most memory subsystem.
pull/102/head
Andre Richter 3 years ago
parent e815b34603
commit e7cb61b389
No known key found for this signature in database
GPG Key ID: 2116C1AB102F615E

@ -12,7 +12,7 @@ the tutorials advance.
Have fun!
## Code organization and architecture
# 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,
@ -25,15 +25,22 @@ architecture. For each supported processor architecture, there exists a subfolde
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 memory subsystem (`src/memory.rs`) would go into
`src/_arch/aarch64/memory.rs`. The latter file is directly included and re-exported in
`src/memory.rs`, so that the architectural code parts are transparent with respect to the code's
module organization. That means a public function `foo()` defined in `src/_arch/aarch64/memory.rs`
would be reachable as `crate::memory::foo()` only.
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_`.
The `_` in `_arch` denotes that this folder is not part of the standard module hierarchy. Rather,
it's contents are conditionally pulled into respective files using the `#[path =
"_arch/xxx/yyy.rs"]` attribute.
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
@ -42,9 +49,8 @@ target board specific definitions and functions. These are things such as the bo
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 transparent re-exporting this time. That means
whatever is provided must be called starting from the `bsp` namespace, e.g.
`bsp::driver::driver_manager()`.
`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
@ -96,3 +102,10 @@ 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`.

@ -34,8 +34,9 @@ export LINKER_FILE
RUSTFLAGS = -C link-arg=-T$(LINKER_FILE) $(RUSTC_MISC_ARGS)
RUSTFLAGS_PEDANTIC = $(RUSTFLAGS) -D warnings -D missing_docs
FEATURES = bsp_$(BSP)
COMPILER_ARGS = --target=$(TARGET) \
--features bsp_$(BSP) \
--features $(FEATURES) \
--release
RUSTC_CMD = cargo rustc $(COMPILER_ARGS)

@ -23,8 +23,8 @@
- Only `.text` section.
- `main.rs`: Important [inner attributes]:
- `#![no_std]`, `#![no_main]`
- `cpu.S`: Assembly `_start()` function that executes `wfe` (Wait For Event), halting all cores that
are executing `_start()`.
- `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,8 +0,0 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
//! Architectural processor code.
// Assembly counterpart to this file.
global_asm!(include_str!("cpu.S"));

@ -0,0 +1,15 @@
// 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
// Assembly counterpart to this file. Includes function _start().
global_asm!(include_str!("boot.S"));

@ -2,7 +2,7 @@
//
// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
//! Conditional re-exporting of Board Support Packages.
//! Conditional reexporting of Board Support Packages.
#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))]
mod raspberrypi;

@ -4,7 +4,4 @@
//! Processor code.
#[cfg(target_arch = "aarch64")]
#[path = "_arch/aarch64/cpu.rs"]
mod arch_cpu;
pub use arch_cpu::*;
mod boot;

@ -0,0 +1,9 @@
// 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;

@ -20,15 +20,22 @@
//! `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 memory subsystem (`src/memory.rs`) would go
//! into `src/_arch/aarch64/memory.rs`. The latter file is directly included and re-exported in
//! `src/memory.rs`, so that the architectural code parts are transparent with respect to the code's
//! module organization. That means a public function `foo()` defined in
//! `src/_arch/aarch64/memory.rs` would be reachable as `crate::memory::foo()` only.
//! 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_`.
//!
//! The `_` in `_arch` denotes that this folder is not part of the standard module hierarchy.
//! Rather, it's contents are conditionally pulled into respective files using the `#[path =
//! "_arch/xxx/yyy.rs"]` attribute.
//! 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
//!
@ -37,9 +44,8 @@
//! 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 transparent re-exporting this time. That means
//! whatever is provided must be called starting from the `bsp` namespace, e.g.
//! `bsp::driver::driver_manager()`.
//! `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
//!
@ -91,14 +97,17 @@
//!
//! - `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.S`.
#![feature(asm)]
#![feature(global_asm)]
#![no_main]
#![no_std]
// `mod cpu` provides the `_start()` function, the first function to run.
mod bsp;
mod cpu;
mod panic_wait;

@ -34,8 +34,9 @@ export LINKER_FILE
RUSTFLAGS = -C link-arg=-T$(LINKER_FILE) $(RUSTC_MISC_ARGS)
RUSTFLAGS_PEDANTIC = $(RUSTFLAGS) -D warnings -D missing_docs
FEATURES = bsp_$(BSP)
COMPILER_ARGS = --target=$(TARGET) \
--features bsp_$(BSP) \
--features $(FEATURES) \
--release
RUSTC_CMD = cargo rustc $(COMPILER_ARGS)

@ -2,7 +2,7 @@
## tl;dr
我们拓展了`cpu.S`在第一次启动的时候调用Rust代码。在Rust的代码中先清零了[bss] section然后通过调用`panic()`挂起CPU。再次运行`make qemu`看看新增加的代码是怎么运行的。
我们拓展了`boot.S`在第一次启动的时候调用Rust代码。在Rust的代码中先清零了[bss] section然后通过调用`panic()`挂起CPU。再次运行`make qemu`看看新增加的代码是怎么运行的。
## 值得注意的变化
@ -21,9 +21,23 @@
## 相比之前的变化diff
```diff
diff -uNr 01_wait_forever/src/_arch/aarch64/cpu.S 02_runtime_init/src/_arch/aarch64/cpu.S
--- 01_wait_forever/src/_arch/aarch64/cpu.S
+++ 02_runtime_init/src/_arch/aarch64/cpu.S
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"
+[profile.release]
+lto = true
+
# The features section is used to select the target board.
[features]
default = []
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
@ -43,10 +57,45 @@ diff -uNr 01_wait_forever/src/_arch/aarch64/cpu.S 02_runtime_init/src/_arch/aarc
+ b 1b // We should never reach here. But just in case,
+ // park this core aswell
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 @@
+// 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
+
+//--------------------------------------------------------------------------------------------------
+// Public Code
+//--------------------------------------------------------------------------------------------------
+
+/// Pause execution on the core.
+#[inline(always)]
+pub fn wait_forever() -> ! {
+ unsafe {
+ loop {
+ #[rustfmt::skip]
+ asm!(
+ "wfe",
+ options(nomem, nostack, preserves_flags)
+ );
+ }
+ }
+}
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
@@ -13,5 +13,24 @@
@@ -13,5 +13,27 @@
*(.text._start) *(.text*)
}
@ -66,22 +115,97 @@ diff -uNr 01_wait_forever/src/bsp/raspberrypi/link.ld 02_runtime_init/src/bsp/ra
+ __bss_start = .;
+ *(.bss*);
+ . = ALIGN(8);
+ __bss_end = .;
+
+ /* Fill for the bss == 0 case, so that __bss_start <= __bss_end_inclusive holds */
+ . += 8;
+ __bss_end_inclusive = . - 8;
+ }
+
/DISCARD/ : { *(.comment*) }
}
diff -uNr 01_wait_forever/src/bsp/raspberrypi/memory.rs 02_runtime_init/src/bsp/raspberrypi/memory.rs
--- 01_wait_forever/src/bsp/raspberrypi/memory.rs
+++ 02_runtime_init/src/bsp/raspberrypi/memory.rs
@@ -0,0 +1,37 @@
+// 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 __bss_start: UnsafeCell<u64>;
+ static __bss_end_inclusive: UnsafeCell<u64>;
+}
+
+//--------------------------------------------------------------------------------------------------
+// Public Code
+//--------------------------------------------------------------------------------------------------
+
+/// 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
+}
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 @@
//! Top-level BSP file for the Raspberry Pi 3 and 4.
-// Coming soon.
+pub mod memory;
diff -uNr 01_wait_forever/src/cpu.rs 02_runtime_init/src/cpu.rs
--- 01_wait_forever/src/cpu.rs
+++ 02_runtime_init/src/cpu.rs
@@ -4,4 +4,13 @@
//! Processor code.
+#[cfg(target_arch = "aarch64")]
+#[path = "_arch/aarch64/cpu.rs"]
+mod arch_cpu;
+
mod boot;
+
+//--------------------------------------------------------------------------------------------------
+// Architectural Public Reexports
+//--------------------------------------------------------------------------------------------------
+pub use arch_cpu::wait_forever;
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
@@ -97,10 +97,20 @@
#![no_main]
#![no_std]
@@ -102,6 +102,7 @@
//!
//! 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
-// `mod cpu` provides the `_start()` function, the first function to run.
+// `mod cpu` provides the `_start()` function, the first function to run. `_start()` then calls
+// `runtime_init()`, which jumps to `kernel_init()`.
@@ -112,6 +113,15 @@
mod bsp;
mod cpu;
@ -102,73 +226,70 @@ diff -uNr 01_wait_forever/src/main.rs 02_runtime_init/src/main.rs
diff -uNr 01_wait_forever/src/memory.rs 02_runtime_init/src/memory.rs
--- 01_wait_forever/src/memory.rs
+++ 02_runtime_init/src/memory.rs
@@ -0,0 +1,29 @@
@@ -0,0 +1,30 @@
+// SPDX-License-Identifier: MIT OR Apache-2.0
+//
+// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
+
+//! Memory Management.
+
+use core::ops::Range;
+use core::ops::RangeInclusive;
+
+//--------------------------------------------------------------------------------------------------
+// Public Code
+//--------------------------------------------------------------------------------------------------
+
+/// Zero out a memory region.
+/// 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: Range<*mut T>)
+pub unsafe fn zero_volatile<T>(range: RangeInclusive<*mut T>)
+where
+ T: From<u8>,
+{
+ let mut ptr = range.start;
+ let mut ptr = *range.start();
+ let end_inclusive = *range.end();
+
+ while ptr < range.end {
+ while ptr <= end_inclusive {
+ core::ptr::write_volatile(ptr, T::from(0));
+ ptr = ptr.offset(1);
+ }
+}
diff -uNr 01_wait_forever/src/panic_wait.rs 02_runtime_init/src/panic_wait.rs
--- 01_wait_forever/src/panic_wait.rs
+++ 02_runtime_init/src/panic_wait.rs
@@ -4,9 +4,10 @@
//! A panic handler that infinitely waits.
+use crate::cpu;
use core::panic::PanicInfo;
#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
- unimplemented!()
+ cpu::wait_forever()
}
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,58 @@
@@ -0,0 +1,38 @@
+// 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::memory;
+use core::ops::Range;
+use crate::{bsp, memory};
+
+//--------------------------------------------------------------------------------------------------
+// Private Code
+//--------------------------------------------------------------------------------------------------
+
+/// Return the range spanning the .bss section.
+///
+/// # Safety
+///
+/// - The symbol-provided addresses must be valid.
+/// - The symbol-provided addresses must be usize aligned.
+unsafe fn bss_range() -> Range<*mut usize> {
+ extern "C" {
+ // Boundaries of the .bss section, provided by linker script symbols.
+ static mut __bss_start: usize;
+ static mut __bss_end: usize;
+ }
+
+ Range {
+ start: &mut __bss_start,
+ end: &mut __bss_end,
+ }
+}
+
+/// Zero out the .bss section.
+///
+/// # Safety
@ -176,7 +297,7 @@ diff -uNr 01_wait_forever/src/runtime_init.rs 02_runtime_init/src/runtime_init.r
+/// - Must only be called pre `kernel_init()`.
+#[inline(always)]
+unsafe fn zero_bss() {
+ memory::zero_volatile(bss_range());
+ memory::zero_volatile(bsp::memory::bss_range_inclusive());
+}
+
+//--------------------------------------------------------------------------------------------------
@ -190,7 +311,7 @@ diff -uNr 01_wait_forever/src/runtime_init.rs 02_runtime_init/src/runtime_init.r
+///
+/// - Only a single core must be active and running this function.
+#[no_mangle]
+pub unsafe extern "C" fn runtime_init() -> ! {
+pub unsafe fn runtime_init() -> ! {
+ zero_bss();
+
+ crate::kernel_init()

@ -2,7 +2,7 @@
## tl;dr
- We extend `cpu.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.
@ -37,13 +37,44 @@ diff -uNr 01_wait_forever/Cargo.toml 02_runtime_init/Cargo.toml
[features]
default = []
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
_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
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
@@ -6,3 +6,21 @@
// Assembly counterpart to this file.
global_asm!(include_str!("cpu.S"));
@@ -0,0 +1,30 @@
+// 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
+
+//--------------------------------------------------------------------------------------------------
+// Public Code
@ -63,28 +94,6 @@ diff -uNr 01_wait_forever/src/_arch/aarch64/cpu.rs 02_runtime_init/src/_arch/aar
+ }
+}
diff -uNr 01_wait_forever/src/_arch/aarch64/cpu.S 02_runtime_init/src/_arch/aarch64/cpu.S
--- 01_wait_forever/src/_arch/aarch64/cpu.S
+++ 02_runtime_init/src/_arch/aarch64/cpu.S
@@ -7,5 +7,15 @@
.global _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
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
@ -169,16 +178,36 @@ diff -uNr 01_wait_forever/src/bsp/raspberrypi.rs 02_runtime_init/src/bsp/raspber
-// Coming soon.
+pub mod memory;
diff -uNr 01_wait_forever/src/cpu.rs 02_runtime_init/src/cpu.rs
--- 01_wait_forever/src/cpu.rs
+++ 02_runtime_init/src/cpu.rs
@@ -4,4 +4,13 @@
//! Processor code.
+#[cfg(target_arch = "aarch64")]
+#[path = "_arch/aarch64/cpu.rs"]
+mod arch_cpu;
+
mod boot;
+
+//--------------------------------------------------------------------------------------------------
+// Architectural Public Reexports
+//--------------------------------------------------------------------------------------------------
+pub use arch_cpu::wait_forever;
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
@@ -97,10 +97,20 @@
#![no_main]
#![no_std]
@@ -102,6 +102,7 @@
//!
//! 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()`].
-// `mod cpu` provides the `_start()` function, the first function to run.
+// `mod cpu` provides the `_start()` function, the first function to run. `_start()` then calls
+// `runtime_init()`, which jumps to `kernel_init()`.
#![feature(asm)]
#![feature(global_asm)]
@@ -110,6 +111,15 @@
mod bsp;
mod cpu;

@ -3,9 +3,13 @@
// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
//! Architectural processor code.
// Assembly counterpart to this file.
global_asm!(include_str!("cpu.S"));
//!
//! # Orientation
//!
//! Since arch modules are imported into generic modules using the path attribute, the path of this
//! file is:
//!
//! crate::cpu::arch_cpu
//--------------------------------------------------------------------------------------------------
// Public Code

@ -0,0 +1,15 @@
// 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
// Assembly counterpart to this file. Includes function _start().
global_asm!(include_str!("boot.S"));

@ -2,7 +2,7 @@
//
// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
//! Conditional re-exporting of Board Support Packages.
//! Conditional reexporting of Board Support Packages.
#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))]
mod raspberrypi;

@ -7,4 +7,10 @@
#[cfg(target_arch = "aarch64")]
#[path = "_arch/aarch64/cpu.rs"]
mod arch_cpu;
pub use arch_cpu::*;
mod boot;
//--------------------------------------------------------------------------------------------------
// Architectural Public Reexports
//--------------------------------------------------------------------------------------------------
pub use arch_cpu::wait_forever;

@ -0,0 +1,9 @@
// 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;

@ -20,15 +20,22 @@
//! `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 memory subsystem (`src/memory.rs`) would go
//! into `src/_arch/aarch64/memory.rs`. The latter file is directly included and re-exported in
//! `src/memory.rs`, so that the architectural code parts are transparent with respect to the code's
//! module organization. That means a public function `foo()` defined in
//! `src/_arch/aarch64/memory.rs` would be reachable as `crate::memory::foo()` only.
//! 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_`.
//!
//! The `_` in `_arch` denotes that this folder is not part of the standard module hierarchy.
//! Rather, it's contents are conditionally pulled into respective files using the `#[path =
//! "_arch/xxx/yyy.rs"]` attribute.
//! 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
//!
@ -37,9 +44,8 @@
//! 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 transparent re-exporting this time. That means
//! whatever is provided must be called starting from the `bsp` namespace, e.g.
//! `bsp::driver::driver_manager()`.
//! `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
//!
@ -91,15 +97,18 @@
//!
//! - `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.S`.
//! 2. Once finished with architectural setup, the arch code calls [`runtime_init::runtime_init()`].
#![feature(asm)]
#![feature(global_asm)]
#![no_main]
#![no_std]
// `mod cpu` provides the `_start()` function, the first function to run. `_start()` then calls
// `runtime_init()`, which jumps to `kernel_init()`.
mod bsp;
mod cpu;
mod memory;

@ -34,8 +34,9 @@ export LINKER_FILE
RUSTFLAGS = -C link-arg=-T$(LINKER_FILE) $(RUSTC_MISC_ARGS)
RUSTFLAGS_PEDANTIC = $(RUSTFLAGS) -D warnings -D missing_docs
FEATURES = bsp_$(BSP)
COMPILER_ARGS = --target=$(TARGET) \
--features bsp_$(BSP) \
--features $(FEATURES) \
--release
RUSTC_CMD = cargo rustc $(COMPILER_ARGS)

@ -139,8 +139,17 @@ 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
@@ -93,7 +93,9 @@
//! - `crate::bsp::memory::*`
@@ -100,19 +100,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`.
+//! 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
#![feature(asm)]
+#![feature(format_args_nl)]
@ -149,9 +158,6 @@ diff -uNr 02_runtime_init/src/main.rs 03_hacky_hello_world/src/main.rs
#![no_main]
#![no_std]
@@ -101,9 +103,11 @@
// `runtime_init()`, which jumps to `kernel_init()`.
mod bsp;
+mod console;
mod cpu;
@ -161,7 +167,7 @@ diff -uNr 02_runtime_init/src/main.rs 03_hacky_hello_world/src/main.rs
mod runtime_init;
/// Early init code.
@@ -112,5 +116,7 @@
@@ -121,5 +127,7 @@
///
/// - Only a single core must be active and running this function.
unsafe fn kernel_init() -> ! {
@ -202,7 +208,7 @@ diff -uNr 02_runtime_init/src/print.rs 03_hacky_hello_world/src/print.rs
+//
+// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
+
+//! Printing facilities.
+//! Printing.
+
+use crate::{bsp, console};
+use core::fmt;

@ -3,9 +3,13 @@
// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
//! Architectural processor code.
// Assembly counterpart to this file.
global_asm!(include_str!("cpu.S"));
//!
//! # Orientation
//!
//! Since arch modules are imported into generic modules using the path attribute, the path of this
//! file is:
//!
//! crate::cpu::arch_cpu
//--------------------------------------------------------------------------------------------------
// Public Code

@ -0,0 +1,15 @@
// 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
// Assembly counterpart to this file. Includes function _start().
global_asm!(include_str!("boot.S"));

@ -2,7 +2,7 @@
//
// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
//! Conditional re-exporting of Board Support Packages.
//! Conditional reexporting of Board Support Packages.
#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))]
mod raspberrypi;

@ -7,4 +7,10 @@
#[cfg(target_arch = "aarch64")]
#[path = "_arch/aarch64/cpu.rs"]
mod arch_cpu;
pub use arch_cpu::*;
mod boot;
//--------------------------------------------------------------------------------------------------
// Architectural Public Reexports
//--------------------------------------------------------------------------------------------------
pub use arch_cpu::wait_forever;

@ -0,0 +1,9 @@
// 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;

@ -20,15 +20,22 @@
//! `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 memory subsystem (`src/memory.rs`) would go
//! into `src/_arch/aarch64/memory.rs`. The latter file is directly included and re-exported in
//! `src/memory.rs`, so that the architectural code parts are transparent with respect to the code's
//! module organization. That means a public function `foo()` defined in
//! `src/_arch/aarch64/memory.rs` would be reachable as `crate::memory::foo()` only.
//! 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_`.
//!
//! The `_` in `_arch` denotes that this folder is not part of the standard module hierarchy.
//! Rather, it's contents are conditionally pulled into respective files using the `#[path =
//! "_arch/xxx/yyy.rs"]` attribute.
//! 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
//!
@ -37,9 +44,8 @@
//! 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 transparent re-exporting this time. That means
//! whatever is provided must be called starting from the `bsp` namespace, e.g.
//! `bsp::driver::driver_manager()`.
//! `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
//!
@ -91,6 +97,14 @@
//!
//! - `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
#![feature(asm)]
#![feature(format_args_nl)]
@ -99,9 +113,6 @@
#![no_main]
#![no_std]
// `mod cpu` provides the `_start()` function, the first function to run. `_start()` then calls
// `runtime_init()`, which jumps to `kernel_init()`.
mod bsp;
mod console;
mod cpu;

@ -2,7 +2,7 @@
//
// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
//! Printing facilities.
//! Printing.
use crate::{bsp, console};
use core::fmt;

@ -34,8 +34,9 @@ export LINKER_FILE
RUSTFLAGS = -C link-arg=-T$(LINKER_FILE) $(RUSTC_MISC_ARGS)
RUSTFLAGS_PEDANTIC = $(RUSTFLAGS) -D warnings -D missing_docs
FEATURES = bsp_$(BSP)
COMPILER_ARGS = --target=$(TARGET) \
--features bsp_$(BSP) \
--features $(FEATURES) \
--release
RUSTC_CMD = cargo rustc $(COMPILER_ARGS)

@ -23,47 +23,20 @@ diff -uNr 03_hacky_hello_world/Cargo.toml 04_zero_overhead_abstraction/Cargo.tom
+cortex-a = { version = "5.x.x" }
+
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,22 @@
+// SPDX-License-Identifier: MIT OR Apache-2.0
+//
+// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
+
+//! Architectural symmetric multiprocessing.
+
+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/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
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
@@ -4,8 +4,34 @@
//! Architectural processor code.
-// Assembly counterpart to this file.
-global_asm!(include_str!("cpu.S"));
-// Assembly counterpart to this file. Includes function _start().
-global_asm!(include_str!("boot.S"));
+use crate::{bsp, cpu};
+use cortex_a::{asm, regs::*};
+use cortex_a::regs::*;
+
+//--------------------------------------------------------------------------------------------------
+// Boot Code
+// Public Code
+//--------------------------------------------------------------------------------------------------
+
+/// The entry of the `kernel` binary.
@ -84,32 +57,13 @@ diff -uNr 03_hacky_hello_world/src/_arch/aarch64/cpu.rs 04_zero_overhead_abstrac
+ runtime_init::runtime_init()
+ } else {
+ // If not core0, infinitely wait for events.
+ wait_forever()
+ cpu::wait_forever()
+ }
+}
//--------------------------------------------------------------------------------------------------
// Public Code
@@ -14,13 +40,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/_arch/aarch64/cpu.S 04_zero_overhead_abstraction/src/_arch/aarch64/cpu.S
--- 03_hacky_hello_world/src/_arch/aarch64/cpu.S
+++ 04_zero_overhead_abstraction/src/_arch/aarch64/cpu.S
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
-//
@ -133,6 +87,69 @@ diff -uNr 03_hacky_hello_world/src/_arch/aarch64/cpu.S 04_zero_overhead_abstract
- 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
@ -193,7 +210,7 @@ diff -uNr 03_hacky_hello_world/src/bsp/raspberrypi.rs 04_zero_overhead_abstracti
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,10 @@
@@ -0,0 +1,14 @@
+// SPDX-License-Identifier: MIT OR Apache-2.0
+//
+// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
@ -202,25 +219,32 @@ diff -uNr 03_hacky_hello_world/src/cpu/smp.rs 04_zero_overhead_abstraction/src/c
+
+#[cfg(target_arch = "aarch64")]
+#[path = "../_arch/aarch64/cpu/smp.rs"]
+mod arch_cpu_smp;
+pub use arch_cpu_smp::*;
+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
@@ -8,3 +8,5 @@
#[path = "_arch/aarch64/cpu.rs"]
mod arch_cpu;
pub use arch_cpu::*;
+
@@ -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
@@ -92,9 +92,7 @@
//! - `crate::memory::*`
//! - `crate::bsp::memory::*`
@@ -106,9 +106,7 @@
//!
//! [`cpu::boot::arch_boot::_start()`]: cpu/boot/arch_boot/fn._start.html
-#![feature(asm)]
#![feature(format_args_nl)]
@ -228,7 +252,7 @@ diff -uNr 03_hacky_hello_world/src/main.rs 04_zero_overhead_abstraction/src/main
#![feature(panic_info_message)]
#![no_main]
#![no_std]
@@ -116,7 +114,8 @@
@@ -127,7 +125,8 @@
///
/// - Only a single core must be active and running this function.
unsafe fn kernel_init() -> ! {

@ -3,35 +3,15 @@
// 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 crate::{bsp, cpu};
use cortex_a::{asm, regs::*};
//--------------------------------------------------------------------------------------------------
// Boot 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.
wait_forever()
}
}
use cortex_a::asm;
//--------------------------------------------------------------------------------------------------
// Public Code

@ -0,0 +1,41 @@
// 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()
}
}

@ -3,6 +3,13 @@
// 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::*;

@ -2,7 +2,7 @@
//
// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
//! Conditional re-exporting of Board Support Packages.
//! Conditional reexporting of Board Support Packages.
#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))]
mod raspberrypi;

@ -7,6 +7,12 @@
#[cfg(target_arch = "aarch64")]
#[path = "_arch/aarch64/cpu.rs"]
mod arch_cpu;
pub use arch_cpu::*;
mod boot;
pub mod smp;
//--------------------------------------------------------------------------------------------------
// Architectural Public Reexports
//--------------------------------------------------------------------------------------------------
pub use arch_cpu::wait_forever;

@ -0,0 +1,9 @@
// 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;

@ -6,5 +6,9 @@
#[cfg(target_arch = "aarch64")]
#[path = "../_arch/aarch64/cpu/smp.rs"]
mod arch_cpu_smp;
pub use arch_cpu_smp::*;
mod arch_smp;
//--------------------------------------------------------------------------------------------------
// Architectural Public Reexports
//--------------------------------------------------------------------------------------------------
pub use arch_smp::core_id;

@ -20,15 +20,22 @@
//! `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 memory subsystem (`src/memory.rs`) would go
//! into `src/_arch/aarch64/memory.rs`. The latter file is directly included and re-exported in
//! `src/memory.rs`, so that the architectural code parts are transparent with respect to the code's
//! module organization. That means a public function `foo()` defined in
//! `src/_arch/aarch64/memory.rs` would be reachable as `crate::memory::foo()` only.
//! 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_`.
//!
//! The `_` in `_arch` denotes that this folder is not part of the standard module hierarchy.
//! Rather, it's contents are conditionally pulled into respective files using the `#[path =
//! "_arch/xxx/yyy.rs"]` attribute.
//! 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
//!
@ -37,9 +44,8 @@
//! 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 transparent re-exporting this time. That means
//! whatever is provided must be called starting from the `bsp` namespace, e.g.
//! `bsp::driver::driver_manager()`.
//! `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
//!
@ -91,15 +97,20 @@
//!
//! - `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
#![feature(format_args_nl)]
#![feature(panic_info_message)]
#![no_main]
#![no_std]
// `mod cpu` provides the `_start()` function, the first function to run. `_start()` then calls
// `runtime_init()`, which jumps to `kernel_init()`.
mod bsp;
mod console;
mod cpu;

@ -2,7 +2,7 @@
//
// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
//! Printing facilities.
//! Printing.
use crate::{bsp, console};
use core::fmt;

@ -34,8 +34,9 @@ export LINKER_FILE
RUSTFLAGS = -C link-arg=-T$(LINKER_FILE) $(RUSTC_MISC_ARGS)
RUSTFLAGS_PEDANTIC = $(RUSTFLAGS) -D warnings -D missing_docs
FEATURES = bsp_$(BSP)
COMPILER_ARGS = --target=$(TARGET) \
--features bsp_$(BSP) \
--features $(FEATURES) \
--release
RUSTC_CMD = cargo rustc $(COMPILER_ARGS)

@ -3,8 +3,8 @@
## tl;dr
- A pseudo-lock is introduced.
- It is a first showcase of OS synchronization primitives and enables safe access to a global data
structure.
- It is a first showcase of OS synchronization primitives and enables safe access to a global data
structure.
## Mutable globals in Rust
@ -26,9 +26,7 @@ variant of a *MUTual EXclusion* primitive. `Mutex` is introduced as a trait in `
and implemented by the `NullLock` in the same file. In order to make the code lean for teaching
purposes, it leaves out the actual architecture-specific logic for protection against concurrent
access, since we don't need it as long as the kernel only executes on a single core with interrupts
disabled. That is also why it is implemented in the same file as the interface itself. In later
tutorials, an implementation might move to the `_arch` once it pulls in arch-specific code that can
not be further abstracted.
disabled.
The `NullLock` focuses on showcasing the Rust core concept of [interior mutability]. Make sure to
read up on it. I also recommend to read this article about an [accurate mental model for Rust's
@ -213,7 +211,7 @@ diff -uNr 04_zero_overhead_abstraction/src/console.rs 05_safe_globals/src/consol
diff -uNr 04_zero_overhead_abstraction/src/main.rs 05_safe_globals/src/main.rs
--- 04_zero_overhead_abstraction/src/main.rs
+++ 05_safe_globals/src/main.rs
@@ -94,6 +94,7 @@
@@ -108,6 +108,7 @@
#![feature(format_args_nl)]
#![feature(panic_info_message)]
@ -221,7 +219,7 @@ diff -uNr 04_zero_overhead_abstraction/src/main.rs 05_safe_globals/src/main.rs
#![no_main]
#![no_std]
@@ -107,6 +108,7 @@
@@ -118,6 +119,7 @@
mod panic_wait;
mod print;
mod runtime_init;
@ -229,7 +227,7 @@ diff -uNr 04_zero_overhead_abstraction/src/main.rs 05_safe_globals/src/main.rs
/// Early init code.
///
@@ -114,8 +116,15 @@
@@ -125,8 +127,15 @@
///
/// - Only a single core must be active and running this function.
unsafe fn kernel_init() -> ! {

@ -3,35 +3,15 @@
// 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 crate::{bsp, cpu};
use cortex_a::{asm, regs::*};
//--------------------------------------------------------------------------------------------------
// Boot 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.
wait_forever()
}
}
use cortex_a::asm;
//--------------------------------------------------------------------------------------------------
// Public Code

@ -0,0 +1,41 @@
// 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()
}
}

@ -3,6 +3,13 @@
// 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::*;

@ -2,7 +2,7 @@
//
// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
//! Conditional re-exporting of Board Support Packages.
//! Conditional reexporting of Board Support Packages.
#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))]
mod raspberrypi;

@ -7,6 +7,12 @@
#[cfg(target_arch = "aarch64")]
#[path = "_arch/aarch64/cpu.rs"]
mod arch_cpu;
pub use arch_cpu::*;
mod boot;
pub mod smp;
//--------------------------------------------------------------------------------------------------
// Architectural Public Reexports
//--------------------------------------------------------------------------------------------------
pub use arch_cpu::wait_forever;

@ -0,0 +1,9 @@
// 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;

@ -6,5 +6,9 @@
#[cfg(target_arch = "aarch64")]
#[path = "../_arch/aarch64/cpu/smp.rs"]
mod arch_cpu_smp;
pub use arch_cpu_smp::*;
mod arch_smp;
//--------------------------------------------------------------------------------------------------
// Architectural Public Reexports
//--------------------------------------------------------------------------------------------------
pub use arch_smp::core_id;

@ -20,15 +20,22 @@
//! `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 memory subsystem (`src/memory.rs`) would go
//! into `src/_arch/aarch64/memory.rs`. The latter file is directly included and re-exported in
//! `src/memory.rs`, so that the architectural code parts are transparent with respect to the code's
//! module organization. That means a public function `foo()` defined in
//! `src/_arch/aarch64/memory.rs` would be reachable as `crate::memory::foo()` only.
//! 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_`.
//!
//! The `_` in `_arch` denotes that this folder is not part of the standard module hierarchy.
//! Rather, it's contents are conditionally pulled into respective files using the `#[path =
//! "_arch/xxx/yyy.rs"]` attribute.
//! 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
//!
@ -37,9 +44,8 @@
//! 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 transparent re-exporting this time. That means
//! whatever is provided must be called starting from the `bsp` namespace, e.g.
//! `bsp::driver::driver_manager()`.
//! `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
//!
@ -91,6 +97,14 @@
//!
//! - `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
#![feature(format_args_nl)]
#![feature(panic_info_message)]
@ -98,9 +112,6 @@
#![no_main]
#![no_std]
// `mod cpu` provides the `_start()` function, the first function to run. `_start()` then calls
// `runtime_init()`, which jumps to `kernel_init()`.
mod bsp;
mod console;
mod cpu;

@ -2,7 +2,7 @@
//
// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
//! Printing facilities.
//! Printing.
use crate::{bsp, console};
use core::fmt;

@ -40,8 +40,9 @@ export LINKER_FILE
RUSTFLAGS = -C link-arg=-T$(LINKER_FILE) $(RUSTC_MISC_ARGS)
RUSTFLAGS_PEDANTIC = $(RUSTFLAGS) -D warnings -D missing_docs
FEATURES = bsp_$(BSP)
COMPILER_ARGS = --target=$(TARGET) \
--features bsp_$(BSP) \
--features $(FEATURES) \
--release
RUSTC_CMD = cargo rustc $(COMPILER_ARGS)

@ -19,13 +19,16 @@
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.
- 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
is from now on used as the main system console output.
- `BSP`s now contain a memory map in `src/bsp/raspberrypi/memory.rs`. In the specific case, they contain the
Raspberry's `MMIO` addresses which are used to instantiate the respectivedevice drivers.
- `BSP`s now contain a memory map in `src/bsp/raspberrypi/memory.rs`. In the specific case, they
contain the Raspberry's `MMIO` addresses which are used to instantiate the respective device
drivers.
- We also modify the `panic!` handler, so that it does not anymore rely on `println!`, which uses
the globally-shared instance of the `UART` that might be locked when an error is encountered (for
now this can't happen due to the `NullLock`, but with a real lock it becomes an issue).
now, this can't happen due to the `NullLock`, but with a real lock it becomes an issue).
- Instead, it creates a new UART driver instance, re-initializes the device and uses that one to
print. This increases the chances that the system is able to print a final important message
before it suspends itself.
@ -145,7 +148,7 @@ diff -uNr 05_safe_globals/Makefile 06_drivers_gpio_uart/Makefile
# BSP-specific arguments
ifeq ($(BSP),rpi3)
TARGET = aarch64-unknown-none-softfloat
@@ -51,13 +57,23 @@
@@ -52,13 +58,23 @@
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
@ -171,7 +174,7 @@ diff -uNr 05_safe_globals/Makefile 06_drivers_gpio_uart/Makefile
all: $(KERNEL_BIN)
@@ -78,6 +94,9 @@
@@ -79,6 +95,9 @@
@$(DOCKER_QEMU) $(EXEC_QEMU) $(QEMU_RELEASE_ARGS) -kernel $(KERNEL_BIN)
endif
@ -185,7 +188,7 @@ diff -uNr 05_safe_globals/Makefile 06_drivers_gpio_uart/Makefile
diff -uNr 05_safe_globals/src/_arch/aarch64/cpu.rs 06_drivers_gpio_uart/src/_arch/aarch64/cpu.rs
--- 05_safe_globals/src/_arch/aarch64/cpu.rs
+++ 06_drivers_gpio_uart/src/_arch/aarch64/cpu.rs
@@ -37,6 +37,16 @@
@@ -17,6 +17,16 @@
// Public Code
//--------------------------------------------------------------------------------------------------
@ -1158,7 +1161,7 @@ diff -uNr 05_safe_globals/src/bsp.rs 06_drivers_gpio_uart/src/bsp.rs
+++ 06_drivers_gpio_uart/src/bsp.rs
@@ -4,6 +4,8 @@
//! Conditional re-exporting of Board Support Packages.
//! Conditional reexporting of Board Support Packages.
+mod device_driver;
+
@ -1169,7 +1172,7 @@ diff -uNr 05_safe_globals/src/bsp.rs 06_drivers_gpio_uart/src/bsp.rs
diff -uNr 05_safe_globals/src/console.rs 06_drivers_gpio_uart/src/console.rs
--- 05_safe_globals/src/console.rs
+++ 06_drivers_gpio_uart/src/console.rs
@@ -14,8 +14,26 @@
@@ -14,8 +14,25 @@
/// Console write functions.
pub trait Write {
@ -1179,8 +1182,7 @@ diff -uNr 05_safe_globals/src/console.rs 06_drivers_gpio_uart/src/console.rs
/// Write a Rust format string.
fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result;
+
+ /// Block execution until the last buffered character has been physically put on the TX
+ /// wire.
+ /// Block until the last buffered character has been physically put on the TX wire.
+ fn flush(&self);
+ }
+
@ -1196,7 +1198,7 @@ diff -uNr 05_safe_globals/src/console.rs 06_drivers_gpio_uart/src/console.rs
}
/// Console statistics.
@@ -24,8 +42,13 @@
@@ -24,8 +41,13 @@
fn chars_written(&self) -> usize {
0
}
@ -1212,6 +1214,16 @@ diff -uNr 05_safe_globals/src/console.rs 06_drivers_gpio_uart/src/console.rs
+ pub trait All = Write + Read + Statistics;
}
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,4 @@
//--------------------------------------------------------------------------------------------------
// Architectural Public Reexports
//--------------------------------------------------------------------------------------------------
-pub use arch_cpu::wait_forever;
+pub use arch_cpu::{nop, spin_for_cycles, wait_forever};
diff -uNr 05_safe_globals/src/driver.rs 06_drivers_gpio_uart/src/driver.rs
--- 05_safe_globals/src/driver.rs
+++ 06_drivers_gpio_uart/src/driver.rs
@ -1264,30 +1276,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
@@ -7,6 +7,14 @@
//! The `kernel` binary.
//!
+//! # TL;DR - Overview of important Kernel entities
+//!
+//! - [`bsp::console::console()`] - Returns a reference to the kernel's [console interface].
+//! - [`bsp::driver::driver_manager()`] - Returns a reference to the kernel's [driver interface].
+//!
+//! [console interface]: ../libkernel/console/interface/index.html
+//! [driver interface]: ../libkernel/driver/interface/trait.DriverManager.html
+//!
//! # Code organization and architecture
@@ -106,6 +106,7 @@
//!
//! The code is divided into different *modules*, each representing a typical **subsystem** of the
@@ -92,6 +100,7 @@
//! - `crate::memory::*`
//! - `crate::bsp::memory::*`
//! [`cpu::boot::arch_boot::_start()`]: cpu/boot/arch_boot/fn._start.html
+#![feature(const_fn_fn_ptr_basics)]
#![feature(format_args_nl)]
#![feature(panic_info_message)]
#![feature(trait_alias)]
@@ -104,6 +113,7 @@
@@ -115,6 +116,7 @@
mod bsp;
mod console;
mod cpu;
@ -1295,7 +1292,7 @@ diff -uNr 05_safe_globals/src/main.rs 06_drivers_gpio_uart/src/main.rs
mod memory;
mod panic_wait;
mod print;
@@ -115,16 +125,49 @@
@@ -126,16 +128,49 @@
/// # Safety
///
/// - Only a single core must be active and running this function.
@ -1303,7 +1300,8 @@ diff -uNr 05_safe_globals/src/main.rs 06_drivers_gpio_uart/src/main.rs
unsafe fn kernel_init() -> ! {
- use console::interface::Statistics;
+ use driver::interface::DriverManager;
+
- println!("[0] Hello from pure Rust!");
+ for i in bsp::driver::driver_manager().all_device_drivers().iter() {
+ if let Err(x) = i.init() {
+ panic!("Error loading driver: {}: {}", i.compatible(), x);
@ -1315,8 +1313,7 @@ diff -uNr 05_safe_globals/src/main.rs 06_drivers_gpio_uart/src/main.rs
+ // Transition from unsafe to safe.
+ kernel_main()
+}
- println!("[0] Hello from pure Rust!");
+
+/// The main function running after the early init.
+fn kernel_main() -> ! {
+ use bsp::console::console;

@ -3,35 +3,15 @@
// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
//! Architectural processor code.
use crate::{bsp, cpu};
use cortex_a::{asm, regs::*};
//--------------------------------------------------------------------------------------------------
// Boot 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.
wait_forever()
}
}
//!
//! # 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

@ -0,0 +1,41 @@
// 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()
}
}

@ -3,6 +3,13 @@
// 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::*;

@ -2,7 +2,7 @@
//
// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
//! Conditional re-exporting of Board Support Packages.
//! Conditional reexporting of Board Support Packages.
mod device_driver;

@ -20,8 +20,7 @@ pub mod interface {
/// Write a Rust format string.
fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result;
/// Block execution until the last buffered character has been physically put on the TX
/// wire.
/// Block until the last buffered character has been physically put on the TX wire.
fn flush(&self);
}

@ -7,6 +7,12 @@
#[cfg(target_arch = "aarch64")]
#[path = "_arch/aarch64/cpu.rs"]
mod arch_cpu;
pub use arch_cpu::*;
mod boot;
pub mod smp;
//--------------------------------------------------------------------------------------------------
// Architectural Public Reexports
//--------------------------------------------------------------------------------------------------
pub use arch_cpu::{nop, spin_for_cycles, wait_forever};

@ -0,0 +1,9 @@
// 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;

@ -6,5 +6,9 @@
#[cfg(target_arch = "aarch64")]
#[path = "../_arch/aarch64/cpu/smp.rs"]
mod arch_cpu_smp;
pub use arch_cpu_smp::*;
mod arch_smp;
//--------------------------------------------------------------------------------------------------
// Architectural Public Reexports
//--------------------------------------------------------------------------------------------------
pub use arch_smp::core_id;

@ -7,14 +7,6 @@
//! The `kernel` binary.
//!
//! # TL;DR - Overview of important Kernel entities
//!
//! - [`bsp::console::console()`] - Returns a reference to the kernel's [console interface].
//! - [`bsp::driver::driver_manager()`] - Returns a reference to the kernel's [driver interface].
//!
//! [console interface]: ../libkernel/console/interface/index.html
//! [driver interface]: ../libkernel/driver/interface/trait.DriverManager.html
//!
//! # Code organization and architecture
//!
//! The code is divided into different *modules*, each representing a typical **subsystem** of the
@ -28,15 +20,22 @@
//! `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 memory subsystem (`src/memory.rs`) would go
//! into `src/_arch/aarch64/memory.rs`. The latter file is directly included and re-exported in
//! `src/memory.rs`, so that the architectural code parts are transparent with respect to the code's
//! module organization. That means a public function `foo()` defined in
//! `src/_arch/aarch64/memory.rs` would be reachable as `crate::memory::foo()` only.
//! 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_`.
//!
//! The `_` in `_arch` denotes that this folder is not part of the standard module hierarchy.
//! Rather, it's contents are conditionally pulled into respective files using the `#[path =
//! "_arch/xxx/yyy.rs"]` attribute.
//! 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
//!
@ -45,9 +44,8 @@
//! 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 transparent re-exporting this time. That means
//! whatever is provided must be called starting from the `bsp` namespace, e.g.
//! `bsp::driver::driver_manager()`.
//! `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
//!
@ -99,6 +97,14 @@
//!
//! - `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
#![feature(const_fn_fn_ptr_basics)]
#![feature(format_args_nl)]
@ -107,9 +113,6 @@
#![no_main]
#![no_std]
// `mod cpu` provides the `_start()` function, the first function to run. `_start()` then calls
// `runtime_init()`, which jumps to `kernel_init()`.
mod bsp;
mod console;
mod cpu;

@ -2,7 +2,7 @@
//
// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
//! Printing facilities.
//! Printing.
use crate::{bsp, console};
use core::fmt;

@ -42,8 +42,9 @@ export LINKER_FILE
RUSTFLAGS = -C link-arg=-T$(LINKER_FILE) $(RUSTC_MISC_ARGS)
RUSTFLAGS_PEDANTIC = $(RUSTFLAGS) -D warnings -D missing_docs
FEATURES = bsp_$(BSP)
COMPILER_ARGS = --target=$(TARGET) \
--features bsp_$(BSP) \
--features $(FEATURES) \
--release
RUSTC_CMD = cargo rustc $(COMPILER_ARGS)

@ -24,20 +24,23 @@ tutorials in a quick manner.
Our chainloader is called `MiniLoad` and is inspired by [raspbootin].
You can try it with this tutorial already:
1. Depending on your target hardware:`make` or `BSP=rpi4 make`.
1. Depending on your target hardware, run:`make` or `BSP=rpi4 make`.
1. Copy `kernel8.img` to the SD card.
1. Execute `make chainboot` or `BSP=rpi4 make chainboot`.
1. Run `make chainboot` or `BSP=rpi4 make chainboot`.
1. Connect the USB serial to your host PC.
- Wiring diagram at [top-level README](../README.md#-usb-serial-output).
- Make sure that you **DID NOT** connect the power pin of the USB serial. Only RX/TX and GND.
1. Connect the RPi to the (USB) power cable.
1. Observe the loader fetching a kernel over `UART`:
> ❗ **NOTE**: By default, `make chainboot` tries to connect to `/dev/ttyUSB0`.
> Should the USB serial on your system have a different name, you have to provide it explicitly. For
> example:
>
> `DEV_SERIAL=/dev/tty.usbserial-0001 make chainboot`
> ❗ **NOTE**: `make chainboot` assumes a default serial device name of `/dev/ttyUSB0`. Depending on
> your host operating system, the device name might differ. For example, on `macOS`, it might be
> something like `/dev/tty.usbserial-0001`. In this case, please give the name explicitly:
```console
$ DEV_SERIAL=/dev/tty.usbserial-0001 make chainboot
```
[raspbootin]: https://github.com/mrvn/raspbootin
@ -127,7 +130,7 @@ diff -uNr 06_drivers_gpio_uart/Makefile 07_uart_chainloader/Makefile
endif
# Export for build.rs
@@ -67,13 +69,14 @@
@@ -68,13 +70,14 @@
ifeq ($(UNAME_S),Linux)
DOCKER_CMD_DEV = $(DOCKER_CMD_INTERACT) $(DOCKER_ARG_DEV)
@ -145,7 +148,7 @@ diff -uNr 06_drivers_gpio_uart/Makefile 07_uart_chainloader/Makefile
all: $(KERNEL_BIN)
@@ -87,15 +90,18 @@
@@ -88,15 +91,18 @@
$(DOC_CMD) --document-private-items --open
ifeq ($(QEMU_MACHINE_TYPE),)
@ -167,7 +170,7 @@ diff -uNr 06_drivers_gpio_uart/Makefile 07_uart_chainloader/Makefile
clippy:
RUSTFLAGS="$(RUSTFLAGS_PEDANTIC)" $(CLIPPY_CMD)
@@ -107,7 +113,10 @@
@@ -108,7 +114,10 @@
readelf --headers $(KERNEL_ELF)
objdump: $(KERNEL_ELF)
@ -180,10 +183,10 @@ diff -uNr 06_drivers_gpio_uart/Makefile 07_uart_chainloader/Makefile
nm: $(KERNEL_ELF)
@$(DOCKER_ELFTOOLS) $(NM_BINARY) --demangle --print-size $(KERNEL_ELF) | sort | rustfilt
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
@@ -22,11 +22,11 @@
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() -> ! {
@ -196,8 +199,12 @@ diff -uNr 06_drivers_gpio_uart/src/_arch/aarch64/cpu.rs 07_uart_chainloader/src/
+ relocate::relocate_self()
} else {
// If not core0, infinitely wait for events.
wait_forever()
@@ -54,3 +54,19 @@
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
@@ -34,3 +34,19 @@
asm::wfe()
}
}
@ -383,12 +390,28 @@ diff -uNr 06_drivers_gpio_uart/src/bsp/raspberrypi/memory.rs 07_uart_chainloader
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,4 +15,4 @@
//--------------------------------------------------------------------------------------------------
// Architectural Public Reexports
//--------------------------------------------------------------------------------------------------
-pub use arch_cpu::{nop, spin_for_cycles, wait_forever};
+pub use arch_cpu::{branch_to_raw_addr, nop, spin_for_cycles, wait_forever};
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
@@ -100,7 +100,9 @@
//! - `crate::memory::*`
//! - `crate::bsp::memory::*`
@@ -102,11 +102,14 @@
//!
//! 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
+#![feature(asm)]
#![feature(const_fn_fn_ptr_basics)]
@ -396,17 +419,7 @@ diff -uNr 06_drivers_gpio_uart/src/main.rs 07_uart_chainloader/src/main.rs
#![feature(format_args_nl)]
#![feature(panic_info_message)]
#![feature(trait_alias)]
@@ -108,7 +110,8 @@
#![no_std]
// `mod cpu` provides the `_start()` function, the first function to run. `_start()` then calls
-// `runtime_init()`, which jumps to `kernel_init()`.
+// `relocate::relocate_self()`. `relocate::relocate_self()` calls `runtime_init()`, which jumps to
+// `kernel_init()`.
mod bsp;
mod console;
@@ -117,6 +120,7 @@
@@ -120,6 +123,7 @@
mod memory;
mod panic_wait;
mod print;
@ -414,7 +427,7 @@ diff -uNr 06_drivers_gpio_uart/src/main.rs 07_uart_chainloader/src/main.rs
mod runtime_init;
mod synchronization;
@@ -145,29 +149,49 @@
@@ -148,29 +152,49 @@
fn kernel_main() -> ! {
use bsp::console::console;
use console::interface::All;
@ -478,7 +491,7 @@ diff -uNr 06_drivers_gpio_uart/src/main.rs 07_uart_chainloader/src/main.rs
+ println!("[ML] Loaded! Executing the payload now\n");
+ console().flush();
+
+ // Use black magic to get a function pointer.
+ // Use black magic to create a function pointer.
+ let kernel: fn() -> ! = unsafe { core::mem::transmute(kernel_addr) };
+
+ // Jump to loaded kernel!

@ -3,35 +3,15 @@
// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
//! Architectural processor code.
use crate::{bsp, cpu};
use cortex_a::{asm, regs::*};
//--------------------------------------------------------------------------------------------------
// Boot 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::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.
wait_forever()
}
}
//!
//! # 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

@ -0,0 +1,41 @@
// 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::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()
}
}

@ -3,6 +3,13 @@
// 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::*;

@ -2,7 +2,7 @@
//
// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
//! Conditional re-exporting of Board Support Packages.
//! Conditional reexporting of Board Support Packages.
mod device_driver;

@ -20,8 +20,7 @@ pub mod interface {
/// Write a Rust format string.
fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result;
/// Block execution until the last buffered character has been physically put on the TX
/// wire.
/// Block until the last buffered character has been physically put on the TX wire.
fn flush(&self);
}

@ -7,6 +7,12 @@
#[cfg(target_arch = "aarch64")]
#[path = "_arch/aarch64/cpu.rs"]
mod arch_cpu;
pub use arch_cpu::*;
mod boot;
pub mod smp;
//--------------------------------------------------------------------------------------------------
// Architectural Public Reexports
//--------------------------------------------------------------------------------------------------
pub use arch_cpu::{branch_to_raw_addr, nop, spin_for_cycles, wait_forever};

@ -0,0 +1,9 @@
// 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;

@ -6,5 +6,9 @@
#[cfg(target_arch = "aarch64")]
#[path = "../_arch/aarch64/cpu/smp.rs"]
mod arch_cpu_smp;
pub use arch_cpu_smp::*;
mod arch_smp;
//--------------------------------------------------------------------------------------------------
// Architectural Public Reexports
//--------------------------------------------------------------------------------------------------
pub use arch_smp::core_id;

@ -7,14 +7,6 @@
//! The `kernel` binary.
//!
//! # TL;DR - Overview of important Kernel entities
//!
//! - [`bsp::console::console()`] - Returns a reference to the kernel's [console interface].
//! - [`bsp::driver::driver_manager()`] - Returns a reference to the kernel's [driver interface].
//!
//! [console interface]: ../libkernel/console/interface/index.html
//! [driver interface]: ../libkernel/driver/interface/trait.DriverManager.html
//!
//! # Code organization and architecture
//!
//! The code is divided into different *modules*, each representing a typical **subsystem** of the
@ -28,15 +20,22 @@
//! `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 memory subsystem (`src/memory.rs`) would go
//! into `src/_arch/aarch64/memory.rs`. The latter file is directly included and re-exported in
//! `src/memory.rs`, so that the architectural code parts are transparent with respect to the code's
//! module organization. That means a public function `foo()` defined in
//! `src/_arch/aarch64/memory.rs` would be reachable as `crate::memory::foo()` only.
//! 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_`.
//!
//! The `_` in `_arch` denotes that this folder is not part of the standard module hierarchy.
//! Rather, it's contents are conditionally pulled into respective files using the `#[path =
//! "_arch/xxx/yyy.rs"]` attribute.
//! 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
//!
@ -45,9 +44,8 @@
//! 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 transparent re-exporting this time. That means
//! whatever is provided must be called starting from the `bsp` namespace, e.g.
//! `bsp::driver::driver_manager()`.
//! `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
//!
@ -99,6 +97,15 @@
//!
//! - `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 [`relocate::relocate_self()`].
//! 3. Finally, [`runtime_init::runtime_init()`] is called.
//!
//! [`cpu::boot::arch_boot::_start()`]: cpu/boot/arch_boot/fn._start.html
#![feature(asm)]
#![feature(const_fn_fn_ptr_basics)]
@ -109,10 +116,6 @@
#![no_main]
#![no_std]
// `mod cpu` provides the `_start()` function, the first function to run. `_start()` then calls
// `relocate::relocate_self()`. `relocate::relocate_self()` calls `runtime_init()`, which jumps to
// `kernel_init()`.
mod bsp;
mod console;
mod cpu;
@ -189,7 +192,7 @@ fn kernel_main() -> ! {
println!("[ML] Loaded! Executing the payload now\n");
console().flush();
// Use black magic to get a function pointer.
// Use black magic to create a function pointer.
let kernel: fn() -> ! = unsafe { core::mem::transmute(kernel_addr) };
// Jump to loaded kernel!

@ -2,7 +2,7 @@
//
// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
//! Printing facilities.
//! Printing.
use crate::{bsp, console};
use core::fmt;

@ -40,8 +40,9 @@ export LINKER_FILE
RUSTFLAGS = -C link-arg=-T$(LINKER_FILE) $(RUSTC_MISC_ARGS)
RUSTFLAGS_PEDANTIC = $(RUSTFLAGS) -D warnings -D missing_docs
FEATURES = bsp_$(BSP)
COMPILER_ARGS = --target=$(TARGET) \
--features bsp_$(BSP) \
--features $(FEATURES) \
--release
RUSTC_CMD = cargo rustc $(COMPILER_ARGS)

@ -2,7 +2,8 @@
## tl;dr
- We add abstractions for the architectural timer and implement them for `_arch/aarch64`.
- We add abstractions for timer hardware, and implement them for the ARM architectural timer in
`_arch/aarch64`.
- The new timer functions are used to annotate UART prints with timestamps, and to get rid of the
cycle-based delays in the `GPIO` and `UART` device drivers, which boosts accuracy.
- A `warn!()` macro is added.
@ -68,7 +69,7 @@ diff -uNr 07_uart_chainloader/Makefile 08_timestamps/Makefile
endif
# Export for build.rs
@@ -75,8 +73,7 @@
@@ -76,8 +74,7 @@
EXEC_QEMU = $(QEMU_BINARY) -M $(QEMU_MACHINE_TYPE)
EXEC_MINIPUSH = ruby ../utils/minipush.rb
@ -78,7 +79,7 @@ diff -uNr 07_uart_chainloader/Makefile 08_timestamps/Makefile
all: $(KERNEL_BIN)
@@ -90,18 +87,15 @@
@@ -91,18 +88,15 @@
$(DOC_CMD) --document-private-items --open
ifeq ($(QEMU_MACHINE_TYPE),)
@ -100,7 +101,7 @@ diff -uNr 07_uart_chainloader/Makefile 08_timestamps/Makefile
clippy:
RUSTFLAGS="$(RUSTFLAGS_PEDANTIC)" $(CLIPPY_CMD)
@@ -113,10 +107,7 @@
@@ -114,10 +108,7 @@
readelf --headers $(KERNEL_ELF)
objdump: $(KERNEL_ELF)
@ -113,10 +114,10 @@ diff -uNr 07_uart_chainloader/Makefile 08_timestamps/Makefile
nm: $(KERNEL_ELF)
@$(DOCKER_ELFTOOLS) $(NM_BINARY) --demangle --print-size $(KERNEL_ELF) | sort | rustfilt
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
+++ 08_timestamps/src/_arch/aarch64/cpu.rs
@@ -22,11 +22,11 @@
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() -> ! {
@ -129,8 +130,12 @@ diff -uNr 07_uart_chainloader/src/_arch/aarch64/cpu.rs 08_timestamps/src/_arch/a
+ runtime_init::runtime_init()
} else {
// If not core0, infinitely wait for events.
wait_forever()
@@ -39,14 +39,6 @@
cpu::wait_forever()
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
+++ 08_timestamps/src/_arch/aarch64/cpu.rs
@@ -19,14 +19,6 @@
pub use asm::nop;
@ -145,7 +150,7 @@ 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() -> ! {
@@ -54,19 +46,3 @@
@@ -34,19 +26,3 @@
asm::wfe()
}
}
@ -169,12 +174,19 @@ diff -uNr 07_uart_chainloader/src/_arch/aarch64/cpu.rs 08_timestamps/src/_arch/a
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
+++ 08_timestamps/src/_arch/aarch64/time.rs
@@ -0,0 +1,98 @@
@@ -0,0 +1,105 @@
+// SPDX-License-Identifier: MIT OR Apache-2.0
+//
+// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
+
+//! Architectural timer primitives.
+//!
+//! # Orientation
+//!
+//! Since arch modules are imported into generic modules using the path attribute, the path of this
+//! file is:
+//!
+//! crate::time::arch_time
+
+use crate::{time, warn};
+use core::time::Duration;
@ -227,7 +239,7 @@ diff -uNr 07_uart_chainloader/src/_arch/aarch64/time.rs 08_timestamps/src/_arch/
+ }
+
+ // Calculate the register compare value.
+ let frq = CNTFRQ_EL0.get() as u64;
+ let frq = CNTFRQ_EL0.get();
+ let x = match frq.checked_mul(duration.as_nanos() as u64) {
+ None => {
+ warn!("Spin duration too long, skipping");
@ -483,24 +495,28 @@ diff -uNr 07_uart_chainloader/src/bsp/raspberrypi/memory.rs 08_timestamps/src/bs
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,4 +15,4 @@
//--------------------------------------------------------------------------------------------------
// Architectural Public Reexports
//--------------------------------------------------------------------------------------------------
-pub use arch_cpu::{branch_to_raw_addr, nop, spin_for_cycles, wait_forever};
+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
@@ -11,9 +11,11 @@
//!
//! - [`bsp::console::console()`] - Returns a reference to the kernel's [console interface].
//! - [`bsp::driver::driver_manager()`] - Returns a reference to the kernel's [driver interface].
+//! - [`time::time_manager()`] - Returns a reference to the kernel's [timer interface].
@@ -102,14 +102,11 @@
//!
//! [console interface]: ../libkernel/console/interface/index.html
//! [driver interface]: ../libkernel/driver/interface/trait.DriverManager.html
+//! [timer interface]: ../libkernel/time/interface/trait.TimeManager.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.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()`].
//!
//! # Code organization and architecture
//!
@@ -100,9 +102,7 @@
//! - `crate::memory::*`
//! - `crate::bsp::memory::*`
//! [`cpu::boot::arch_boot::_start()`]: cpu/boot/arch_boot/fn._start.html
-#![feature(asm)]
#![feature(const_fn_fn_ptr_basics)]
@ -508,17 +524,7 @@ diff -uNr 07_uart_chainloader/src/main.rs 08_timestamps/src/main.rs
#![feature(format_args_nl)]
#![feature(panic_info_message)]
#![feature(trait_alias)]
@@ -110,8 +110,7 @@
#![no_std]
// `mod cpu` provides the `_start()` function, the first function to run. `_start()` then calls
-// `relocate::relocate_self()`. `relocate::relocate_self()` calls `runtime_init()`, which jumps to
-// `kernel_init()`.
+// `runtime_init()`, which jumps to `kernel_init()`.
mod bsp;
mod console;
@@ -120,9 +119,9 @@
@@ -123,9 +120,9 @@
mod memory;
mod panic_wait;
mod print;
@ -529,7 +535,7 @@ diff -uNr 07_uart_chainloader/src/main.rs 08_timestamps/src/main.rs
/// Early init code.
///
@@ -147,51 +146,31 @@
@@ -150,51 +147,31 @@
/// The main function running after the early init.
fn kernel_main() -> ! {
@ -593,7 +599,7 @@ diff -uNr 07_uart_chainloader/src/main.rs 08_timestamps/src/main.rs
+ // Test a failing timer case.
+ time::time_manager().spin_for(Duration::from_nanos(1));
- // Use black magic to get a function pointer.
- // Use black magic to create a function pointer.
- let kernel: fn() -> ! = unsafe { core::mem::transmute(kernel_addr) };
-
- // Jump to loaded kernel!
@ -760,7 +766,7 @@ diff -uNr 07_uart_chainloader/src/runtime_init.rs 08_timestamps/src/runtime_init
diff -uNr 07_uart_chainloader/src/time.rs 08_timestamps/src/time.rs
--- 07_uart_chainloader/src/time.rs
+++ 08_timestamps/src/time.rs
@@ -0,0 +1,35 @@
@@ -0,0 +1,37 @@
+// SPDX-License-Identifier: MIT OR Apache-2.0
+//
+// Copyright (c) 2020-2021 Andre Richter <andre.o.richter@gmail.com>
@ -770,7 +776,11 @@ diff -uNr 07_uart_chainloader/src/time.rs 08_timestamps/src/time.rs
+#[cfg(target_arch = "aarch64")]
+#[path = "_arch/aarch64/time.rs"]
+mod arch_time;
+pub use arch_time::*;
+
+//--------------------------------------------------------------------------------------------------
+// Architectural Public Reexports
+//--------------------------------------------------------------------------------------------------
+pub use arch_time::time_manager;
+
+//--------------------------------------------------------------------------------------------------
+// Public Definitions
@ -781,8 +791,6 @@ diff -uNr 07_uart_chainloader/src/time.rs 08_timestamps/src/time.rs
+ use core::time::Duration;
+
+ /// Time management functions.
+ ///
+ /// The `BSP` is supposed to supply one global instance.
+ pub trait TimeManager {
+ /// The timer's resolution.
+ fn resolution(&self) -> Duration;

@ -3,35 +3,15 @@
// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
//! Architectural processor code.
use crate::{bsp, cpu};
use cortex_a::{asm, regs::*};
//--------------------------------------------------------------------------------------------------
// Boot 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.
wait_forever()
}
}
//!
//! # 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

@ -0,0 +1,41 @@
// 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()
}
}

@ -3,6 +3,13 @@
// 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::*;

@ -3,6 +3,13 @@
// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
//! Architectural timer primitives.
//!
//! # Orientation
//!
//! Since arch modules are imported into generic modules using the path attribute, the path of this
//! file is:
//!
//! crate::time::arch_time
use crate::{time, warn};
use core::time::Duration;
@ -55,7 +62,7 @@ impl time::interface::TimeManager for GenericTimer {
}
// Calculate the register compare value.
let frq = CNTFRQ_EL0.get() as u64;
let frq = CNTFRQ_EL0.get();
let x = match frq.checked_mul(duration.as_nanos() as u64) {
None => {
warn!("Spin duration too long, skipping");

@ -2,7 +2,7 @@
//
// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
//! Conditional re-exporting of Board Support Packages.
//! Conditional reexporting of Board Support Packages.
mod device_driver;

@ -20,8 +20,7 @@ pub mod interface {
/// Write a Rust format string.
fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result;
/// Block execution until the last buffered character has been physically put on the TX
/// wire.
/// Block until the last buffered character has been physically put on the TX wire.
fn flush(&self);
}

@ -7,6 +7,12 @@
#[cfg(target_arch = "aarch64")]
#[path = "_arch/aarch64/cpu.rs"]
mod arch_cpu;
pub use arch_cpu::*;
mod boot;
pub mod smp;
//--------------------------------------------------------------------------------------------------
// Architectural Public Reexports
//--------------------------------------------------------------------------------------------------
pub use arch_cpu::{nop, wait_forever};

@ -0,0 +1,9 @@
// 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;

@ -6,5 +6,9 @@
#[cfg(target_arch = "aarch64")]
#[path = "../_arch/aarch64/cpu/smp.rs"]
mod arch_cpu_smp;
pub use arch_cpu_smp::*;
mod arch_smp;
//--------------------------------------------------------------------------------------------------
// Architectural Public Reexports
//--------------------------------------------------------------------------------------------------
pub use arch_smp::core_id;

@ -7,16 +7,6 @@
//! The `kernel` binary.
//!
//! # TL;DR - Overview of important Kernel entities
//!
//! - [`bsp::console::console()`] - Returns a reference to the kernel's [console interface].
//! - [`bsp::driver::driver_manager()`] - Returns a reference to the kernel's [driver interface].
//! - [`time::time_manager()`] - Returns a reference to the kernel's [timer interface].
//!
//! [console interface]: ../libkernel/console/interface/index.html
//! [driver interface]: ../libkernel/driver/interface/trait.DriverManager.html
//! [timer interface]: ../libkernel/time/interface/trait.TimeManager.html
//!
//! # Code organization and architecture
//!
//! The code is divided into different *modules*, each representing a typical **subsystem** of the
@ -30,15 +20,22 @@
//! `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 memory subsystem (`src/memory.rs`) would go
//! into `src/_arch/aarch64/memory.rs`. The latter file is directly included and re-exported in
//! `src/memory.rs`, so that the architectural code parts are transparent with respect to the code's
//! module organization. That means a public function `foo()` defined in
//! `src/_arch/aarch64/memory.rs` would be reachable as `crate::memory::foo()` only.
//! 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_`.
//!
//! The `_` in `_arch` denotes that this folder is not part of the standard module hierarchy.
//! Rather, it's contents are conditionally pulled into respective files using the `#[path =
//! "_arch/xxx/yyy.rs"]` attribute.
//! 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
//!
@ -47,9 +44,8 @@
//! 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 transparent re-exporting this time. That means
//! whatever is provided must be called starting from the `bsp` namespace, e.g.
//! `bsp::driver::driver_manager()`.
//! `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
//!
@ -101,6 +97,14 @@
//!
//! - `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
#![feature(const_fn_fn_ptr_basics)]
#![feature(format_args_nl)]
@ -109,9 +113,6 @@
#![no_main]
#![no_std]
// `mod cpu` provides the `_start()` function, the first function to run. `_start()` then calls
// `runtime_init()`, which jumps to `kernel_init()`.
mod bsp;
mod console;
mod cpu;

@ -2,7 +2,7 @@
//
// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
//! Printing facilities.
//! Printing.
use crate::{bsp, console};
use core::fmt;

@ -7,7 +7,11 @@
#[cfg(target_arch = "aarch64")]
#[path = "_arch/aarch64/time.rs"]
mod arch_time;
pub use arch_time::*;
//--------------------------------------------------------------------------------------------------
// Architectural Public Reexports
//--------------------------------------------------------------------------------------------------
pub use arch_time::time_manager;
//--------------------------------------------------------------------------------------------------
// Public Definitions
@ -18,8 +22,6 @@ pub mod interface {
use core::time::Duration;
/// Time management functions.
///
/// The `BSP` is supposed to supply one global instance.
pub trait TimeManager {
/// The timer's resolution.
fn resolution(&self) -> Duration;

@ -44,8 +44,9 @@ export LINKER_FILE
RUSTFLAGS = -C link-arg=-T$(LINKER_FILE) $(RUSTC_MISC_ARGS)
RUSTFLAGS_PEDANTIC = $(RUSTFLAGS) -D warnings -D missing_docs
FEATURES = bsp_$(BSP)
COMPILER_ARGS = --target=$(TARGET) \
--features bsp_$(BSP) \
--features $(FEATURES) \
--release
RUSTC_CMD = cargo rustc $(COMPILER_ARGS)

@ -287,8 +287,8 @@ Hence, please ensure the following order of connecting the devices to your box:
1. Connect the USB serial.
2. Afterwards, the Olimex debugger.
This way, Linux enumerates the devices accordingly. This has to be done only once. It is fine to
disconnect and connect the serial multiple times, e.g. for kicking off different `make jtagboot`
This way, the host OS enumerates the devices accordingly. This has to be done only once. It is fine
to disconnect and connect the serial multiple times, e.g. for kicking off different `make jtagboot`
runs, while keeping the debugger connected.
## Additional resources
@ -324,7 +324,7 @@ diff -uNr 08_timestamps/Makefile 09_hw_debug_JTAG/Makefile
LINKER_FILE = src/bsp/raspberrypi/link.ld
RUSTC_MISC_ARGS = -C target-cpu=cortex-a72
endif
@@ -58,9 +62,12 @@
@@ -59,9 +63,12 @@
DOCKER_CMD = docker run --rm -v $(shell pwd):/work/tutorial -w /work/tutorial
DOCKER_CMD_INTERACT = $(DOCKER_CMD) -i -t
DOCKER_ARG_DIR_UTILS = -v $(shell pwd)/../utils:/work/utils
@ -337,7 +337,7 @@ diff -uNr 08_timestamps/Makefile 09_hw_debug_JTAG/Makefile
DOCKER_ELFTOOLS = $(DOCKER_CMD) $(DOCKER_IMAGE)
# Dockerize commands that require USB device passthrough only on Linux
@@ -68,12 +75,17 @@
@@ -69,12 +76,17 @@
DOCKER_CMD_DEV = $(DOCKER_CMD_INTERACT) $(DOCKER_ARG_DEV)
DOCKER_CHAINBOOT = $(DOCKER_CMD_DEV) $(DOCKER_ARG_DIR_UTILS) $(DOCKER_IMAGE)
@ -356,7 +356,7 @@ diff -uNr 08_timestamps/Makefile 09_hw_debug_JTAG/Makefile
all: $(KERNEL_BIN)
@@ -97,6 +109,23 @@
@@ -98,6 +110,23 @@
chainboot: $(KERNEL_BIN)
@$(DOCKER_CHAINBOOT) $(EXEC_MINIPUSH) $(DEV_SERIAL) $(KERNEL_BIN)

@ -3,35 +3,15 @@
// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
//! Architectural processor code.
use crate::{bsp, cpu};
use cortex_a::{asm, regs::*};
//--------------------------------------------------------------------------------------------------
// Boot 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.
wait_forever()
}
}
//!
//! # 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

@ -0,0 +1,41 @@
// 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()
}
}

@ -3,6 +3,13 @@
// 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::*;

@ -3,6 +3,13 @@
// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
//! Architectural timer primitives.
//!
//! # Orientation
//!
//! Since arch modules are imported into generic modules using the path attribute, the path of this
//! file is:
//!
//! crate::time::arch_time
use crate::{time, warn};
use core::time::Duration;
@ -55,7 +62,7 @@ impl time::interface::TimeManager for GenericTimer {
}
// Calculate the register compare value.
let frq = CNTFRQ_EL0.get() as u64;
let frq = CNTFRQ_EL0.get();
let x = match frq.checked_mul(duration.as_nanos() as u64) {
None => {
warn!("Spin duration too long, skipping");

@ -2,7 +2,7 @@
//
// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
//! Conditional re-exporting of Board Support Packages.
//! Conditional reexporting of Board Support Packages.
mod device_driver;

@ -20,8 +20,7 @@ pub mod interface {
/// Write a Rust format string.
fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result;
/// Block execution until the last buffered character has been physically put on the TX
/// wire.
/// Block until the last buffered character has been physically put on the TX wire.
fn flush(&self);
}

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

Loading…
Cancel
Save