Merge pull request #51 from rust-embedded/2020-03_refactor

Refactor for better modularity and more sensible namespaces
pull/54/head
Andre Richter 4 years ago committed by GitHub
commit f7ffb4a612
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -0,0 +1,98 @@
# Before we start
The following text is a 1:1 copy of the documentation that can be found at the top of the kernel's
main source code file in each tutorial. It describes the general structure of the source code, and
tries to convey the philosophy behind the respective approach. Please read it to make yourself
familiar with what you will encounter during the tutorials. It will help you to navigate the code
better and understand the differences and additions beteween the separate tutorials.
Please also note that the following text will reference source code files (e.g. `**/memory.rs`) or
functions that won't exist yet in the first bunch of the tutorials. They will be added gradually as
the tutorials advance.
Have fun!
## Code organization and architecture
The code is divided into different *modules*, each representing a typical **subsystem** of the
`kernel`. Top-level module files of subsystems reside directly in the `src` folder. For example,
`src/memory.rs` contains code that is concerned with all things memory management.
## Visibility of processor architecture code
Some of the `kernel`'s subsystems depend on low-level code that is specific to the target processor
architecture. For each supported processor architecture, there exists a subfolder in `src/_arch`,
for example, `src/_arch/aarch64`.
The architecture folders mirror the subsystem modules laid out in `src`. For example, architectural
code that belongs to the `kernel`'s 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.
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.
## BSP code
`BSP` stands for Board Support Package. `BSP` code is organized under `src/bsp.rs` and contains
target board specific definitions and functions. These are things such as the board's memory map or
instances of drivers for devices that are featured on the respective board.
Just like processor architecture code, the `BSP` code's module structure tries to mirror the
`kernel`'s subsystem modules, but there is no 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 interfaces
Both `arch` and `bsp` contain code that is conditionally compiled depending on the actual target and
board for which the kernel is compiled. For example, the `interrupt controller` hardware of the
`Raspberry Pi 3` and the `Raspberry Pi 4` is different, but we want the rest of the `kernel` code to
play nicely with any of the two without much hassle.
In order to provide a clean abstraction between `arch`, `bsp` and `generic kernel code`, `interface`
traits are provided *whenever possible* and *where it makes sense*. They are defined in the
respective subsystem module and help to enforce the idiom of *program to an interface, not an
implementation*. For example, there will be a common IRQ handling interface which the two different
interrupt controller `drivers` of both Raspberrys will implement, and only export the interface to
the rest of the `kernel`.
```
+-------------------+
| Interface (Trait) |
| |
+--+-------------+--+
^ ^
| |
| |
+----------+--+ +--+----------+
| kernel code | | bsp code |
| | | arch code |
+-------------+ +-------------+
```
# Summary
For a logical `kernel` subsystem, corresponding code can be distributed over several physical
locations. Here is an example for the **memory** subsystem:
- `src/memory.rs` and `src/memory/**/*`
- Common code that is agnostic of target processor architecture and `BSP` characteristics.
- Example: A function to zero a chunk of memory.
- Interfaces for the memory subsystem that are implemented by `arch` or `BSP` code.
- Example: An `MMU` interface that defines `MMU` function prototypes.
- `src/bsp/__board_name__/memory.rs` and `src/bsp/__board_name__/memory/**/*`
- `BSP` specific code.
- Example: The board's memory map (physical addresses of DRAM and MMIO devices).
- `src/_arch/__arch_name__/memory.rs` and `src/_arch/__arch_name__/memory/**/*`
- Processor architecture specific code.
- Example: Implementation of the `MMU` interface for the `__arch_name__` processor
architecture.
From a namespace perspective, **memory** subsystem code lives in:
- `crate::memory::*`
- `crate::bsp::memory::*`

@ -14,4 +14,3 @@ bsp_rpi3 = []
bsp_rpi4 = []
[dependencies]

@ -13,16 +13,16 @@ ifeq ($(BSP),rpi3)
OUTPUT = kernel8.img
QEMU_BINARY = qemu-system-aarch64
QEMU_MACHINE_TYPE = raspi3
QEMU_RELEASE_ARGS = -serial stdio -display none
LINKER_FILE = src/bsp/rpi/link.ld
QEMU_RELEASE_ARGS = -d in_asm -display none
LINKER_FILE = src/bsp/raspberrypi/link.ld
RUSTC_MISC_ARGS = -C target-cpu=cortex-a53
else ifeq ($(BSP),rpi4)
TARGET = aarch64-unknown-none-softfloat
OUTPUT = kernel8.img
# QEMU_BINARY = qemu-system-aarch64
# QEMU_MACHINE_TYPE =
# QEMU_RELEASE_ARGS = -serial stdio -display none
LINKER_FILE = src/bsp/rpi/link.ld
# QEMU_RELEASE_ARGS = -d in_asm -display none
LINKER_FILE = src/bsp/raspberrypi/link.ld
RUSTC_MISC_ARGS = -C target-cpu=cortex-a72
endif
@ -60,8 +60,7 @@ $(OUTPUT): $(CARGO_OUTPUT)
$(OBJCOPY_CMD) $< $(OUTPUT)
doc:
cargo xdoc --target=$(TARGET) --features bsp_$(BSP) --document-private-items
xdg-open target/$(TARGET)/doc/kernel/index.html
cargo xdoc --target=$(TARGET) --features bsp_$(BSP) --document-private-items --open
ifeq ($(QEMU_MACHINE_TYPE),)
qemu:

@ -2,11 +2,13 @@
## tl;dr
Project skeleton is set up; Code just halts all CPU cores executing the kernel code.
The project skeleton is set up; A small piece of assembly code runs that just halts all CPU cores
executing the kernel code.
- Toolchain: `cargo xbuild` tools (`xrustc`, `xclippy`) and the
`aarch64-unknown-none-softfloat` target are used for building `AArch64`
bare-metal code.
## Building
- Toolchain: `cargo xbuild` tools (`xrustc`, `xclippy`) and the `aarch64-unknown-none-softfloat`
target are used for building `AArch64` bare-metal code.
- `Makefile` targets:
- `doc`: Generate documentation.
- `qemu`: Run the `kernel` in QEMU
@ -15,17 +17,16 @@ Project skeleton is set up; Code just halts all CPU cores executing the kernel c
- `readelf`: Inspect the `ELF` output.
- `objdump`: Inspect the assembly.
- `nm`: Inspect the symbols.
- Code is organized into `kernel`, `arch` and `BSP` (Board Support Package)
parts.
- Conditional compilation includes respective `arch` and `BSP` according to
user-supplied arguments.
## Code to look at
- Custom `link.ld` linker script.
- Load address at `0x80_000`
- Only `.text` section.
- `main.rs`: Important [inner attributes]:
- `#![no_std]`, `#![no_main]`
- Assembly `_start()` function that executes `wfe` (Wait For Event), halting all
cores that are executing `_start()`.
- `cpu.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.
- Just waits infinitely for a cpu event.
@ -35,7 +36,7 @@ Project skeleton is set up; Code just halts all CPU cores executing the kernel c
In the project folder, invoke QEMU and observe the CPU core spinning on `wfe`:
```console
» make qemu
$ make qemu
[...]
IN:
0x00080000: d503205f wfe

Binary file not shown.

@ -2,15 +2,16 @@
//
// Copyright (c) 2018-2020 Andre Richter <andre.o.richter@gmail.com>
//! AArch64.
//! Architectural processor code.
global_asm!(include_str!("aarch64/start.S"));
// Assembly counterpart to this file.
global_asm!(include_str!("cpu.S"));
//--------------------------------------------------------------------------------------------------
// Implementation of the kernel's architecture abstraction code
// Public Code
//--------------------------------------------------------------------------------------------------
/// Pause execution on the calling CPU core.
/// Pause execution on the core.
#[inline(always)]
pub fn wait_forever() -> ! {
unsafe {

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

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

@ -2,6 +2,6 @@
//
// Copyright (c) 2018-2020 Andre Richter <andre.o.richter@gmail.com>
//! Board Support Package for the Raspberry Pi.
//! Top-level BSP file for the Raspberry Pi 3 and 4.
// Coming soon.

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

@ -5,20 +5,102 @@
// Rust embedded logo for `make doc`.
#![doc(html_logo_url = "https://git.io/JeGIp")]
//! The `kernel`
//! The `kernel` binary.
//!
//! # Code organization and architecture
//!
//! The code is divided into different *modules*, each representing a typical **subsystem** of the
//! `kernel`. Top-level module files of subsystems reside directly in the `src` folder. For example,
//! `src/memory.rs` contains code that is concerned with all things memory management.
//!
//! ## Visibility of processor architecture code
//!
//! Some of the `kernel`'s subsystems depend on low-level code that is specific to the target
//! processor architecture. For each supported processor architecture, there exists a subfolder in
//! `src/_arch`, for example, `src/_arch/aarch64`.
//!
//! The architecture folders mirror the subsystem modules laid out in `src`. For example,
//! architectural code that belongs to the `kernel`'s 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.
//!
//! 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.
//!
//! ## BSP code
//!
//! `BSP` stands for Board Support Package. `BSP` code is organized under `src/bsp.rs` and contains
//! target board specific definitions and functions. These are things such as the board's memory map
//! or instances of drivers for devices that are featured on the respective board.
//!
//! Just like processor architecture code, the `BSP` code's module structure tries to mirror the
//! `kernel`'s subsystem modules, but there is no 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 interfaces
//!
//! Both `arch` and `bsp` contain code that is conditionally compiled depending on the actual target
//! and board for which the kernel is compiled. For example, the `interrupt controller` hardware of
//! the `Raspberry Pi 3` and the `Raspberry Pi 4` is different, but we want the rest of the `kernel`
//! code to play nicely with any of the two without much hassle.
//!
//! In order to provide a clean abstraction between `arch`, `bsp` and `generic kernel code`,
//! `interface` traits are provided *whenever possible* and *where it makes sense*. They are defined
//! in the respective subsystem module and help to enforce the idiom of *program to an interface,
//! not an implementation*. For example, there will be a common IRQ handling interface which the two
//! different interrupt controller `drivers` of both Raspberrys will implement, and only export the
//! interface to the rest of the `kernel`.
//!
//! ```
//! +-------------------+
//! | Interface (Trait) |
//! | |
//! +--+-------------+--+
//! ^ ^
//! | |
//! | |
//! +----------+--+ +--+----------+
//! | kernel code | | bsp code |
//! | | | arch code |
//! +-------------+ +-------------+
//! ```
//!
//! # Summary
//!
//! For a logical `kernel` subsystem, corresponding code can be distributed over several physical
//! locations. Here is an example for the **memory** subsystem:
//!
//! - `src/memory.rs` and `src/memory/**/*`
//! - Common code that is agnostic of target processor architecture and `BSP` characteristics.
//! - Example: A function to zero a chunk of memory.
//! - Interfaces for the memory subsystem that are implemented by `arch` or `BSP` code.
//! - Example: An `MMU` interface that defines `MMU` function prototypes.
//! - `src/bsp/__board_name__/memory.rs` and `src/bsp/__board_name__/memory/**/*`
//! - `BSP` specific code.
//! - Example: The board's memory map (physical addresses of DRAM and MMIO devices).
//! - `src/_arch/__arch_name__/memory.rs` and `src/_arch/__arch_name__/memory/**/*`
//! - Processor architecture specific code.
//! - Example: Implementation of the `MMU` interface for the `__arch_name__` processor
//! architecture.
//!
//! From a namespace perspective, **memory** subsystem code lives in:
//!
//! - `crate::memory::*`
//! - `crate::bsp::memory::*`
#![feature(asm)]
#![feature(global_asm)]
#![no_main]
#![no_std]
// Conditionally includes the selected `architecture` code, which provides the `_start()` function,
// the first function to run.
mod arch;
// `mod cpu` provides the `_start()` function, the first function to run.
// Conditionally includes the selected `BSP` code.
mod bsp;
mod cpu;
mod panic_wait;
// Kernel code coming next tutorial.

@ -4,9 +4,10 @@
//! A panic handler that infinitely waits.
use crate::cpu;
use core::panic::PanicInfo;
#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
crate::arch::wait_forever()
cpu::wait_forever()
}

@ -13,16 +13,16 @@ ifeq ($(BSP),rpi3)
OUTPUT = kernel8.img
QEMU_BINARY = qemu-system-aarch64
QEMU_MACHINE_TYPE = raspi3
QEMU_RELEASE_ARGS = -serial stdio -display none
LINKER_FILE = src/bsp/rpi/link.ld
QEMU_RELEASE_ARGS = -d in_asm -display none
LINKER_FILE = src/bsp/raspberrypi/link.ld
RUSTC_MISC_ARGS = -C target-cpu=cortex-a53
else ifeq ($(BSP),rpi4)
TARGET = aarch64-unknown-none-softfloat
OUTPUT = kernel8.img
# QEMU_BINARY = qemu-system-aarch64
# QEMU_MACHINE_TYPE =
# QEMU_RELEASE_ARGS = -serial stdio -display none
LINKER_FILE = src/bsp/rpi/link.ld
# QEMU_RELEASE_ARGS = -d in_asm -display none
LINKER_FILE = src/bsp/raspberrypi/link.ld
RUSTC_MISC_ARGS = -C target-cpu=cortex-a72
endif
@ -60,8 +60,7 @@ $(OUTPUT): $(CARGO_OUTPUT)
$(OBJCOPY_CMD) $< $(OUTPUT)
doc:
cargo xdoc --target=$(TARGET) --features bsp_$(BSP) --document-private-items
xdg-open target/$(TARGET)/doc/kernel/index.html
cargo xdoc --target=$(TARGET) --features bsp_$(BSP) --document-private-items --open
ifeq ($(QEMU_MACHINE_TYPE),)
qemu:

@ -2,37 +2,30 @@
## tl;dr
We are calling into Rust code for the first time and zero the [bss] section.
Check out `make qemu` again to see the additional code run.
We extend `cpu.S` to call into Rust code for the first time. There,we zero the [bss] section before
execution is halted with a call to `panic()`. Check out `make qemu` again to see the additional code
run.
## Notable additions
- More sections in linker script:
- `.rodata`, `.data`
- `.bss`
- `_start()`:
- Halt core if core != `core0`.
- `core0` jumps to `runtime_init()` Rust function.
- `core0` jumps to the `runtime_init()` Rust function.
- `runtime_init()` in `runtime_init.rs`
- Zeros the `.bss` section.
- Calls `kernel_init()`, which calls `panic!()`, which eventually halts
`core0` as well.
- Calls `kernel_init()`, which calls `panic!()`, which eventually halts `core0` as well.
[bss]: https://en.wikipedia.org/wiki/.bss
## Diff to previous
```diff
diff -uNr 01_wait_forever/Cargo.toml 02_runtime_init/Cargo.toml
--- 01_wait_forever/Cargo.toml
+++ 02_runtime_init/Cargo.toml
@@ -14,4 +14,3 @@
bsp_rpi4 = []
[dependencies]
-
diff -uNr 01_wait_forever/src/arch/aarch64/start.S 02_runtime_init/src/arch/aarch64/start.S
--- 01_wait_forever/src/arch/aarch64/start.S
+++ 02_runtime_init/src/arch/aarch64/start.S
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
@ -52,9 +45,9 @@ diff -uNr 01_wait_forever/src/arch/aarch64/start.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/bsp/rpi/link.ld 02_runtime_init/src/bsp/rpi/link.ld
--- 01_wait_forever/src/bsp/rpi/link.ld
+++ 02_runtime_init/src/bsp/rpi/link.ld
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 @@
*(.text._start) *(.text*)
}
@ -84,18 +77,19 @@ diff -uNr 01_wait_forever/src/bsp/rpi/link.ld 02_runtime_init/src/bsp/rpi/link.l
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
@@ -16,9 +16,20 @@
// the first function to run.
mod arch;
@@ -97,10 +97,20 @@
#![no_main]
#![no_std]
+// `_start()` then calls `runtime_init()`, which on completion, jumps to `kernel_init()`.
+mod runtime_init;
+
// Conditionally includes the selected `BSP` code.
mod bsp;
-// `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()`.
mod bsp;
mod cpu;
+mod memory;
mod panic_wait;
+mod runtime_init;
-// Kernel code coming next tutorial.
+/// Early init code.
@ -110,7 +104,7 @@ 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,25 @@
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: MIT OR Apache-2.0
+//
+// Copyright (c) 2018-2020 Andre Richter <andre.o.richter@gmail.com>
@ -119,6 +113,10 @@ diff -uNr 01_wait_forever/src/memory.rs 02_runtime_init/src/memory.rs
+
+use core::ops::Range;
+
+//--------------------------------------------------------------------------------------------------
+// Public Code
+//--------------------------------------------------------------------------------------------------
+
+/// Zero out a memory region.
+///
+/// # Safety
@ -140,7 +138,7 @@ diff -uNr 01_wait_forever/src/memory.rs 02_runtime_init/src/memory.rs
diff -uNr 01_wait_forever/src/runtime_init.rs 02_runtime_init/src/runtime_init.rs
--- 01_wait_forever/src/runtime_init.rs
+++ 02_runtime_init/src/runtime_init.rs
@@ -0,0 +1,50 @@
@@ -0,0 +1,58 @@
+// SPDX-License-Identifier: MIT OR Apache-2.0
+//
+// Copyright (c) 2018-2020 Andre Richter <andre.o.richter@gmail.com>
@ -150,6 +148,10 @@ diff -uNr 01_wait_forever/src/runtime_init.rs 02_runtime_init/src/runtime_init.r
+use crate::memory;
+use core::ops::Range;
+
+//--------------------------------------------------------------------------------------------------
+// Private Code
+//--------------------------------------------------------------------------------------------------
+
+/// Return the range spanning the .bss section.
+///
+/// # Safety
@ -179,6 +181,10 @@ diff -uNr 01_wait_forever/src/runtime_init.rs 02_runtime_init/src/runtime_init.r
+ memory::zero_volatile(bss_range());
+}
+
+//--------------------------------------------------------------------------------------------------
+// Public Code
+//--------------------------------------------------------------------------------------------------
+
+/// Equivalent to `crt0` or `c0` code in C/C++ world. Clears the `bss` section, then jumps to kernel
+/// init code.
+///

Binary file not shown.

Binary file not shown.

@ -2,15 +2,16 @@
//
// Copyright (c) 2018-2020 Andre Richter <andre.o.richter@gmail.com>
//! AArch64.
//! Architectural processor code.
global_asm!(include_str!("aarch64/start.S"));
// Assembly counterpart to this file.
global_asm!(include_str!("cpu.S"));
//--------------------------------------------------------------------------------------------------
// Implementation of the kernel's architecture abstraction code
// Public Code
//--------------------------------------------------------------------------------------------------
/// Pause execution on the calling CPU core.
/// Pause execution on the core.
#[inline(always)]
pub fn wait_forever() -> ! {
unsafe {

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

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

@ -2,6 +2,6 @@
//
// Copyright (c) 2018-2020 Andre Richter <andre.o.richter@gmail.com>
//! Board Support Package for the Raspberry Pi.
//! Top-level BSP file for the Raspberry Pi 3 and 4.
// Coming soon.

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

@ -5,25 +5,106 @@
// Rust embedded logo for `make doc`.
#![doc(html_logo_url = "https://git.io/JeGIp")]
//! The `kernel`
//! The `kernel` binary.
//!
//! # Code organization and architecture
//!
//! The code is divided into different *modules*, each representing a typical **subsystem** of the
//! `kernel`. Top-level module files of subsystems reside directly in the `src` folder. For example,
//! `src/memory.rs` contains code that is concerned with all things memory management.
//!
//! ## Visibility of processor architecture code
//!
//! Some of the `kernel`'s subsystems depend on low-level code that is specific to the target
//! processor architecture. For each supported processor architecture, there exists a subfolder in
//! `src/_arch`, for example, `src/_arch/aarch64`.
//!
//! The architecture folders mirror the subsystem modules laid out in `src`. For example,
//! architectural code that belongs to the `kernel`'s 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.
//!
//! 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.
//!
//! ## BSP code
//!
//! `BSP` stands for Board Support Package. `BSP` code is organized under `src/bsp.rs` and contains
//! target board specific definitions and functions. These are things such as the board's memory map
//! or instances of drivers for devices that are featured on the respective board.
//!
//! Just like processor architecture code, the `BSP` code's module structure tries to mirror the
//! `kernel`'s subsystem modules, but there is no 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 interfaces
//!
//! Both `arch` and `bsp` contain code that is conditionally compiled depending on the actual target
//! and board for which the kernel is compiled. For example, the `interrupt controller` hardware of
//! the `Raspberry Pi 3` and the `Raspberry Pi 4` is different, but we want the rest of the `kernel`
//! code to play nicely with any of the two without much hassle.
//!
//! In order to provide a clean abstraction between `arch`, `bsp` and `generic kernel code`,
//! `interface` traits are provided *whenever possible* and *where it makes sense*. They are defined
//! in the respective subsystem module and help to enforce the idiom of *program to an interface,
//! not an implementation*. For example, there will be a common IRQ handling interface which the two
//! different interrupt controller `drivers` of both Raspberrys will implement, and only export the
//! interface to the rest of the `kernel`.
//!
//! ```
//! +-------------------+
//! | Interface (Trait) |
//! | |
//! +--+-------------+--+
//! ^ ^
//! | |
//! | |
//! +----------+--+ +--+----------+
//! | kernel code | | bsp code |
//! | | | arch code |
//! +-------------+ +-------------+
//! ```
//!
//! # Summary
//!
//! For a logical `kernel` subsystem, corresponding code can be distributed over several physical
//! locations. Here is an example for the **memory** subsystem:
//!
//! - `src/memory.rs` and `src/memory/**/*`
//! - Common code that is agnostic of target processor architecture and `BSP` characteristics.
//! - Example: A function to zero a chunk of memory.
//! - Interfaces for the memory subsystem that are implemented by `arch` or `BSP` code.
//! - Example: An `MMU` interface that defines `MMU` function prototypes.
//! - `src/bsp/__board_name__/memory.rs` and `src/bsp/__board_name__/memory/**/*`
//! - `BSP` specific code.
//! - Example: The board's memory map (physical addresses of DRAM and MMIO devices).
//! - `src/_arch/__arch_name__/memory.rs` and `src/_arch/__arch_name__/memory/**/*`
//! - Processor architecture specific code.
//! - Example: Implementation of the `MMU` interface for the `__arch_name__` processor
//! architecture.
//!
//! From a namespace perspective, **memory** subsystem code lives in:
//!
//! - `crate::memory::*`
//! - `crate::bsp::memory::*`
#![feature(asm)]
#![feature(global_asm)]
#![no_main]
#![no_std]
// Conditionally includes the selected `architecture` code, which provides the `_start()` function,
// the first function to run.
mod arch;
// `mod cpu` provides the `_start()` function, the first function to run. `_start()` then calls
// `runtime_init()`, which jumps to `kernel_init()`.
// `_start()` then calls `runtime_init()`, which on completion, jumps to `kernel_init()`.
mod runtime_init;
// Conditionally includes the selected `BSP` code.
mod bsp;
mod cpu;
mod memory;
mod panic_wait;
mod runtime_init;
/// Early init code.
///

@ -6,6 +6,10 @@
use core::ops::Range;
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
/// Zero out a memory region.
///
/// # Safety

@ -4,9 +4,10 @@
//! A panic handler that infinitely waits.
use crate::cpu;
use core::panic::PanicInfo;
#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
crate::arch::wait_forever()
cpu::wait_forever()
}

@ -7,6 +7,10 @@
use crate::memory;
use core::ops::Range;
//--------------------------------------------------------------------------------------------------
// Private Code
//--------------------------------------------------------------------------------------------------
/// Return the range spanning the .bss section.
///
/// # Safety
@ -36,6 +40,10 @@ unsafe fn zero_bss() {
memory::zero_volatile(bss_range());
}
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
/// Equivalent to `crt0` or `c0` code in C/C++ world. Clears the `bss` section, then jumps to kernel
/// init code.
///

@ -14,7 +14,7 @@ ifeq ($(BSP),rpi3)
QEMU_BINARY = qemu-system-aarch64
QEMU_MACHINE_TYPE = raspi3
QEMU_RELEASE_ARGS = -serial stdio -display none
LINKER_FILE = src/bsp/rpi/link.ld
LINKER_FILE = src/bsp/raspberrypi/link.ld
RUSTC_MISC_ARGS = -C target-cpu=cortex-a53
else ifeq ($(BSP),rpi4)
TARGET = aarch64-unknown-none-softfloat
@ -22,7 +22,7 @@ else ifeq ($(BSP),rpi4)
# QEMU_BINARY = qemu-system-aarch64
# QEMU_MACHINE_TYPE =
# QEMU_RELEASE_ARGS = -serial stdio -display none
LINKER_FILE = src/bsp/rpi/link.ld
LINKER_FILE = src/bsp/raspberrypi/link.ld
RUSTC_MISC_ARGS = -C target-cpu=cortex-a72
endif
@ -60,8 +60,7 @@ $(OUTPUT): $(CARGO_OUTPUT)
$(OBJCOPY_CMD) $< $(OUTPUT)
doc:
cargo xdoc --target=$(TARGET) --features bsp_$(BSP) --document-private-items
xdg-open target/$(TARGET)/doc/kernel/index.html
cargo xdoc --target=$(TARGET) --features bsp_$(BSP) --document-private-items --open
ifeq ($(QEMU_MACHINE_TYPE),)
qemu:

@ -2,52 +2,86 @@
## tl;dr
Introducing global `print!()` macros to enable "printf debugging" at the
earliest; To keep tutorial length reasonable, printing functions for now "abuse" a
QEMU property that lets us use the RPi's `UART` without setting it up properly;
Using the real hardware `UART` is enabled step-by-step in following tutorials.
Introducing global `print!()` macros to enable "printf debugging" at the earliest; To keep tutorial
length reasonable, printing functions for now "abuse" a QEMU property that lets us use the RPi's
`UART` without setting it up properly; Using the real hardware `UART` is enabled step-by-step in
following tutorials.
- `interface.rs` is introduced:
- Provides `Traits` for abstracting `kernel` from `BSP` and `arch` code.
- Panic handler `print!()`s supplied error messages.
- This is showcased in `main()`.
## Notable additions
### Test it
- `src/console.rs` introduces interface `Traits` for console commands.
- `src/bsp/rpi.rs` implements the interface for QEMU's emulated UART.
- The panic handler makes use of the new `print!()` to display user error messages.
QEMU is no longer running in assembly mode. It will from now on show the output
of the `console`.
## Test it
QEMU is no longer running in assembly mode. It will from now on show the output of the `console`.
```console
» make qemu
$ make qemu
[...]
Hello from Rust!
Kernel panic: Stopping here.
```
## Diff to previous
```diff
diff -uNr 02_runtime_init/src/bsp/rpi.rs 03_hacky_hello_world/src/bsp/rpi.rs
--- 02_runtime_init/src/bsp/rpi.rs
+++ 03_hacky_hello_world/src/bsp/rpi.rs
@@ -4,4 +4,35 @@
//! Board Support Package for the Raspberry Pi.
-// Coming soon.
+use crate::interface;
diff -uNr 02_runtime_init/Makefile 03_hacky_hello_world/Makefile
--- 02_runtime_init/Makefile
+++ 03_hacky_hello_world/Makefile
@@ -13,7 +13,7 @@
OUTPUT = kernel8.img
QEMU_BINARY = qemu-system-aarch64
QEMU_MACHINE_TYPE = raspi3
- QEMU_RELEASE_ARGS = -d in_asm -display none
+ QEMU_RELEASE_ARGS = -serial stdio -display none
LINKER_FILE = src/bsp/raspberrypi/link.ld
RUSTC_MISC_ARGS = -C target-cpu=cortex-a53
else ifeq ($(BSP),rpi4)
@@ -21,7 +21,7 @@
OUTPUT = kernel8.img
# QEMU_BINARY = qemu-system-aarch64
# QEMU_MACHINE_TYPE =
- # QEMU_RELEASE_ARGS = -d in_asm -display none
+ # QEMU_RELEASE_ARGS = -serial stdio -display none
LINKER_FILE = src/bsp/raspberrypi/link.ld
RUSTC_MISC_ARGS = -C target-cpu=cortex-a72
endif
diff -uNr 02_runtime_init/src/bsp/raspberrypi/console.rs 03_hacky_hello_world/src/bsp/raspberrypi/console.rs
--- 02_runtime_init/src/bsp/raspberrypi/console.rs
+++ 03_hacky_hello_world/src/bsp/raspberrypi/console.rs
@@ -0,0 +1,47 @@
+// SPDX-License-Identifier: MIT OR Apache-2.0
+//
+// Copyright (c) 2018-2020 Andre Richter <andre.o.richter@gmail.com>
+
+//! BSP console facilities.
+
+use crate::console;
+use core::fmt;
+
+//--------------------------------------------------------------------------------------------------
+// Private Definitions
+//--------------------------------------------------------------------------------------------------
+
+/// A mystical, magical device for generating QEMU output out of the void.
+struct QEMUOutput;
+
+/// Implementing `console::Write` enables usage of the `format_args!` macros, which in turn are used
+/// to implement the `kernel`'s `print!` and `println!` macros.
+//--------------------------------------------------------------------------------------------------
+// Private Code
+//--------------------------------------------------------------------------------------------------
+
+/// Implementing `core::fmt::Write` enables usage of the `format_args!` macros, which in turn are
+/// used to implement the `kernel`'s `print!` and `println!` macros. By implementing `write_str()`,
+/// we get `write_fmt()` automatically.
+///
+/// See [`src/print.rs`].
+///
+/// [`src/print.rs`]: ../../print/index.html
+impl interface::console::Write for QEMUOutput {
+impl fmt::Write for QEMUOutput {
+ fn write_str(&mut self, s: &str) -> fmt::Result {
+ for c in s.chars() {
+ unsafe {
@ -60,75 +94,53 @@ diff -uNr 02_runtime_init/src/bsp/rpi.rs 03_hacky_hello_world/src/bsp/rpi.rs
+}
+
+//--------------------------------------------------------------------------------------------------
+// Implementation of the kernel's BSP calls
+// Public Code
+//--------------------------------------------------------------------------------------------------
+
+/// Returns a ready-to-use `console::Write` implementation.
+pub fn console() -> impl interface::console::Write {
+/// Return a reference to the console.
+pub fn console() -> impl console::interface::Write {
+ QEMUOutput {}
+}
diff -uNr 02_runtime_init/src/interface.rs 03_hacky_hello_world/src/interface.rs
--- 02_runtime_init/src/interface.rs
+++ 03_hacky_hello_world/src/interface.rs
@@ -0,0 +1,37 @@
diff -uNr 02_runtime_init/src/bsp/raspberrypi.rs 03_hacky_hello_world/src/bsp/raspberrypi.rs
--- 02_runtime_init/src/bsp/raspberrypi.rs
+++ 03_hacky_hello_world/src/bsp/raspberrypi.rs
@@ -4,4 +4,4 @@
//! Top-level BSP file for the Raspberry Pi 3 and 4.
-// Coming soon.
+pub mod console;
diff -uNr 02_runtime_init/src/console.rs 03_hacky_hello_world/src/console.rs
--- 02_runtime_init/src/console.rs
+++ 03_hacky_hello_world/src/console.rs
@@ -0,0 +1,19 @@
+// SPDX-License-Identifier: MIT OR Apache-2.0
+//
+// Copyright (c) 2018-2020 Andre Richter <andre.o.richter@gmail.com>
+
+//! Trait definitions for coupling `kernel` and `BSP` code.
+//!
+//! ```
+//! +-------------------+
+//! | Interface (Trait) |
+//! | |
+//! +--+-------------+--+
+//! ^ ^
+//! | |
+//! | |
+//! +----------+--+ +--+----------+
+//! | Kernel code | | BSP Code |
+//! | | | |
+//! +-------------+ +-------------+
+//! ```
+
+/// System console operations.
+pub mod console {
+//! System console.
+
+//--------------------------------------------------------------------------------------------------
+// Public Definitions
+//--------------------------------------------------------------------------------------------------
+
+/// Console interfaces.
+pub mod interface {
+ /// Console write functions.
+ ///
+ /// `core::fmt::Write` is exactly what we need for now. Re-export it here because
+ /// implementing `console::Write` gives a better hint to the reader about the
+ /// intention.
+ pub use core::fmt::Write;
+
+ /// Console read functions.
+ pub trait Read {
+ /// Read a single character.
+ fn read_char(&self) -> char {
+ ' '
+ }
+ }
+}
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
@@ -6,9 +6,23 @@
#![doc(html_logo_url = "https://git.io/JeGIp")]
//! The `kernel`
+//!
+//! The `kernel` is composed by glueing together code from
+//!
+//! - [Hardware-specific Board Support Packages] (`BSPs`).
+//! - [Architecture-specific code].
+//! - HW- and architecture-agnostic `kernel` code.
+//!
+//! using the [`kernel::interface`] traits.
+//!
+//! [Hardware-specific Board Support Packages]: bsp/index.html
+//! [Architecture-specific code]: arch/index.html
+//! [`kernel::interface`]: interface/index.html
@@ -93,7 +93,9 @@
//! - `crate::bsp::memory::*`
#![feature(asm)]
+#![feature(format_args_nl)]
@ -137,23 +149,24 @@ diff -uNr 02_runtime_init/src/main.rs 03_hacky_hello_world/src/main.rs
#![no_main]
#![no_std]
@@ -22,8 +36,10 @@
// Conditionally includes the selected `BSP` code.
mod bsp;
@@ -101,9 +103,11 @@
// `runtime_init()`, which jumps to `kernel_init()`.
+mod interface;
mod bsp;
+mod console;
mod cpu;
mod memory;
mod panic_wait;
+mod print;
mod runtime_init;
/// Early init code.
///
@@ -31,5 +47,7 @@
@@ -112,5 +116,7 @@
///
/// - Only a single core must be active and running this function.
unsafe fn kernel_init() -> ! {
- panic!()
+ println!("Hello from Rust!");
+ println!("[0] Hello from Rust!");
+
+ panic!("Stopping here.")
}
@ -161,16 +174,16 @@ diff -uNr 02_runtime_init/src/main.rs 03_hacky_hello_world/src/main.rs
diff -uNr 02_runtime_init/src/panic_wait.rs 03_hacky_hello_world/src/panic_wait.rs
--- 02_runtime_init/src/panic_wait.rs
+++ 03_hacky_hello_world/src/panic_wait.rs
@@ -4,9 +4,16 @@
@@ -4,10 +4,16 @@
//! A panic handler that infinitely waits.
+use crate::{arch, println};
-use crate::cpu;
+use crate::{cpu, println};
use core::panic::PanicInfo;
#[panic_handler]
-fn panic(_info: &PanicInfo) -> ! {
- crate::arch::wait_forever()
+fn panic(info: &PanicInfo) -> ! {
+ if let Some(args) = info.message() {
+ println!("\nKernel panic: {}", args);
@ -178,29 +191,37 @@ diff -uNr 02_runtime_init/src/panic_wait.rs 03_hacky_hello_world/src/panic_wait.
+ println!("\nKernel panic!");
+ }
+
+ arch::wait_forever()
cpu::wait_forever()
}
diff -uNr 02_runtime_init/src/print.rs 03_hacky_hello_world/src/print.rs
--- 02_runtime_init/src/print.rs
+++ 03_hacky_hello_world/src/print.rs
@@ -0,0 +1,34 @@
@@ -0,0 +1,42 @@
+// SPDX-License-Identifier: MIT OR Apache-2.0
+//
+// Copyright (c) 2018-2020 Andre Richter <andre.o.richter@gmail.com>
+
+//! Printing facilities.
+
+use crate::{bsp, interface};
+use crate::{bsp, console};
+use core::fmt;
+
+//--------------------------------------------------------------------------------------------------
+// Private Code
+//--------------------------------------------------------------------------------------------------
+
+#[doc(hidden)]
+pub fn _print(args: fmt::Arguments) {
+ use interface::console::Write;
+ use console::interface::Write;
+
+ bsp::console().write_fmt(args).unwrap();
+ bsp::console::console().write_fmt(args).unwrap();
+}
+
+//--------------------------------------------------------------------------------------------------
+// Public Code
+//--------------------------------------------------------------------------------------------------
+
+/// Prints without a newline.
+///
+/// Carbon copy from https://doc.rust-lang.org/src/std/macros.rs.html

Binary file not shown.

Binary file not shown.

@ -2,15 +2,16 @@
//
// Copyright (c) 2018-2020 Andre Richter <andre.o.richter@gmail.com>
//! AArch64.
//! Architectural processor code.
global_asm!(include_str!("aarch64/start.S"));
// Assembly counterpart to this file.
global_asm!(include_str!("cpu.S"));
//--------------------------------------------------------------------------------------------------
// Implementation of the kernel's architecture abstraction code
// Public Code
//--------------------------------------------------------------------------------------------------
/// Pause execution on the calling CPU core.
/// Pause execution on the core.
#[inline(always)]
pub fn wait_forever() -> ! {
unsafe {

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

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

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

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

@ -1,38 +0,0 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2018-2020 Andre Richter <andre.o.richter@gmail.com>
//! Board Support Package for the Raspberry Pi.
use crate::interface;
use core::fmt;
/// A mystical, magical device for generating QEMU output out of the void.
struct QEMUOutput;
/// Implementing `console::Write` enables usage of the `format_args!` macros, which in turn are used
/// to implement the `kernel`'s `print!` and `println!` macros.
///
/// See [`src/print.rs`].
///
/// [`src/print.rs`]: ../../print/index.html
impl interface::console::Write for QEMUOutput {
fn write_str(&mut self, s: &str) -> fmt::Result {
for c in s.chars() {
unsafe {
core::ptr::write_volatile(0x3F20_1000 as *mut u8, c as u8);
}
}
Ok(())
}
}
//--------------------------------------------------------------------------------------------------
// Implementation of the kernel's BSP calls
//--------------------------------------------------------------------------------------------------
/// Returns a ready-to-use `console::Write` implementation.
pub fn console() -> impl interface::console::Write {
QEMUOutput {}
}

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

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

@ -1,37 +0,0 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2018-2020 Andre Richter <andre.o.richter@gmail.com>
//! Trait definitions for coupling `kernel` and `BSP` code.
//!
//! ```
//! +-------------------+
//! | Interface (Trait) |
//! | |
//! +--+-------------+--+
//! ^ ^
//! | |
//! | |
//! +----------+--+ +--+----------+
//! | Kernel code | | BSP Code |
//! | | | |
//! +-------------+ +-------------+
//! ```
/// System console operations.
pub mod console {
/// Console write functions.
///
/// `core::fmt::Write` is exactly what we need for now. Re-export it here because
/// implementing `console::Write` gives a better hint to the reader about the
/// intention.
pub use core::fmt::Write;
/// Console read functions.
pub trait Read {
/// Read a single character.
fn read_char(&self) -> char {
' '
}
}
}

@ -5,19 +5,92 @@
// Rust embedded logo for `make doc`.
#![doc(html_logo_url = "https://git.io/JeGIp")]
//! The `kernel`
//! The `kernel` binary.
//!
//! The `kernel` is composed by glueing together code from
//! # Code organization and architecture
//!
//! - [Hardware-specific Board Support Packages] (`BSPs`).
//! - [Architecture-specific code].
//! - HW- and architecture-agnostic `kernel` code.
//! The code is divided into different *modules*, each representing a typical **subsystem** of the
//! `kernel`. Top-level module files of subsystems reside directly in the `src` folder. For example,
//! `src/memory.rs` contains code that is concerned with all things memory management.
//!
//! using the [`kernel::interface`] traits.
//! ## Visibility of processor architecture code
//!
//! [Hardware-specific Board Support Packages]: bsp/index.html
//! [Architecture-specific code]: arch/index.html
//! [`kernel::interface`]: interface/index.html
//! Some of the `kernel`'s subsystems depend on low-level code that is specific to the target
//! processor architecture. For each supported processor architecture, there exists a subfolder in
//! `src/_arch`, for example, `src/_arch/aarch64`.
//!
//! The architecture folders mirror the subsystem modules laid out in `src`. For example,
//! architectural code that belongs to the `kernel`'s 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.
//!
//! 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.
//!
//! ## BSP code
//!
//! `BSP` stands for Board Support Package. `BSP` code is organized under `src/bsp.rs` and contains
//! target board specific definitions and functions. These are things such as the board's memory map
//! or instances of drivers for devices that are featured on the respective board.
//!
//! Just like processor architecture code, the `BSP` code's module structure tries to mirror the
//! `kernel`'s subsystem modules, but there is no 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 interfaces
//!
//! Both `arch` and `bsp` contain code that is conditionally compiled depending on the actual target
//! and board for which the kernel is compiled. For example, the `interrupt controller` hardware of
//! the `Raspberry Pi 3` and the `Raspberry Pi 4` is different, but we want the rest of the `kernel`
//! code to play nicely with any of the two without much hassle.
//!
//! In order to provide a clean abstraction between `arch`, `bsp` and `generic kernel code`,
//! `interface` traits are provided *whenever possible* and *where it makes sense*. They are defined
//! in the respective subsystem module and help to enforce the idiom of *program to an interface,
//! not an implementation*. For example, there will be a common IRQ handling interface which the two
//! different interrupt controller `drivers` of both Raspberrys will implement, and only export the
//! interface to the rest of the `kernel`.
//!
//! ```
//! +-------------------+
//! | Interface (Trait) |
//! | |
//! +--+-------------+--+
//! ^ ^
//! | |
//! | |
//! +----------+--+ +--+----------+
//! | kernel code | | bsp code |
//! | | | arch code |
//! +-------------+ +-------------+
//! ```
//!
//! # Summary
//!
//! For a logical `kernel` subsystem, corresponding code can be distributed over several physical
//! locations. Here is an example for the **memory** subsystem:
//!
//! - `src/memory.rs` and `src/memory/**/*`
//! - Common code that is agnostic of target processor architecture and `BSP` characteristics.
//! - Example: A function to zero a chunk of memory.
//! - Interfaces for the memory subsystem that are implemented by `arch` or `BSP` code.
//! - Example: An `MMU` interface that defines `MMU` function prototypes.
//! - `src/bsp/__board_name__/memory.rs` and `src/bsp/__board_name__/memory/**/*`
//! - `BSP` specific code.
//! - Example: The board's memory map (physical addresses of DRAM and MMIO devices).
//! - `src/_arch/__arch_name__/memory.rs` and `src/_arch/__arch_name__/memory/**/*`
//! - Processor architecture specific code.
//! - Example: Implementation of the `MMU` interface for the `__arch_name__` processor
//! architecture.
//!
//! From a namespace perspective, **memory** subsystem code lives in:
//!
//! - `crate::memory::*`
//! - `crate::bsp::memory::*`
#![feature(asm)]
#![feature(format_args_nl)]
@ -26,20 +99,16 @@
#![no_main]
#![no_std]
// Conditionally includes the selected `architecture` code, which provides the `_start()` function,
// the first function to run.
mod arch;
// `mod cpu` provides the `_start()` function, the first function to run. `_start()` then calls
// `runtime_init()`, which jumps to `kernel_init()`.
// `_start()` then calls `runtime_init()`, which on completion, jumps to `kernel_init()`.
mod runtime_init;
// Conditionally includes the selected `BSP` code.
mod bsp;
mod interface;
mod console;
mod cpu;
mod memory;
mod panic_wait;
mod print;
mod runtime_init;
/// Early init code.
///
@ -47,7 +116,7 @@ mod print;
///
/// - Only a single core must be active and running this function.
unsafe fn kernel_init() -> ! {
println!("Hello from Rust!");
println!("[0] Hello from Rust!");
panic!("Stopping here.")
}

@ -6,6 +6,10 @@
use core::ops::Range;
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
/// Zero out a memory region.
///
/// # Safety

@ -4,7 +4,7 @@
//! A panic handler that infinitely waits.
use crate::{arch, println};
use crate::{cpu, println};
use core::panic::PanicInfo;
#[panic_handler]
@ -15,5 +15,5 @@ fn panic(info: &PanicInfo) -> ! {
println!("\nKernel panic!");
}
arch::wait_forever()
cpu::wait_forever()
}

@ -4,16 +4,24 @@
//! Printing facilities.
use crate::{bsp, interface};
use crate::{bsp, console};
use core::fmt;
//--------------------------------------------------------------------------------------------------
// Private Code
//--------------------------------------------------------------------------------------------------
#[doc(hidden)]
pub fn _print(args: fmt::Arguments) {
use interface::console::Write;
use console::interface::Write;
bsp::console().write_fmt(args).unwrap();
bsp::console::console().write_fmt(args).unwrap();
}
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
/// Prints without a newline.
///
/// Carbon copy from https://doc.rust-lang.org/src/std/macros.rs.html

@ -7,6 +7,10 @@
use crate::memory;
use core::ops::Range;
//--------------------------------------------------------------------------------------------------
// Private Code
//--------------------------------------------------------------------------------------------------
/// Return the range spanning the .bss section.
///
/// # Safety
@ -36,6 +40,10 @@ unsafe fn zero_bss() {
memory::zero_volatile(bss_range());
}
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
/// Equivalent to `crt0` or `c0` code in C/C++ world. Clears the `bss` section, then jumps to kernel
/// init code.
///

@ -14,7 +14,7 @@ ifeq ($(BSP),rpi3)
QEMU_BINARY = qemu-system-aarch64
QEMU_MACHINE_TYPE = raspi3
QEMU_RELEASE_ARGS = -serial stdio -display none
LINKER_FILE = src/bsp/rpi/link.ld
LINKER_FILE = src/bsp/raspberrypi/link.ld
RUSTC_MISC_ARGS = -C target-cpu=cortex-a53
else ifeq ($(BSP),rpi4)
TARGET = aarch64-unknown-none-softfloat
@ -22,7 +22,7 @@ else ifeq ($(BSP),rpi4)
# QEMU_BINARY = qemu-system-aarch64
# QEMU_MACHINE_TYPE =
# QEMU_RELEASE_ARGS = -serial stdio -display none
LINKER_FILE = src/bsp/rpi/link.ld
LINKER_FILE = src/bsp/raspberrypi/link.ld
RUSTC_MISC_ARGS = -C target-cpu=cortex-a72
endif
@ -60,8 +60,7 @@ $(OUTPUT): $(CARGO_OUTPUT)
$(OBJCOPY_CMD) $< $(OUTPUT)
doc:
cargo xdoc --target=$(TARGET) --features bsp_$(BSP) --document-private-items
xdg-open target/$(TARGET)/doc/kernel/index.html
cargo xdoc --target=$(TARGET) --features bsp_$(BSP) --document-private-items --open
ifeq ($(QEMU_MACHINE_TYPE),)
qemu:

@ -2,8 +2,8 @@
## tl;dr
All hand-written assembly is replaced by Rust code from the [cortex-a] crate,
which provides zero-overhead abstractions and wraps the `unsafe` parts.
All hand-written assembly is replaced by Rust code from the [cortex-a] crate, which provides
zero-overhead abstractions and wraps the `unsafe` parts.
[cortex-a]: https://github.com/rust-embedded/cortex-a
@ -27,43 +27,49 @@ diff -uNr 03_hacky_hello_world/Cargo.toml 04_zero_overhead_abstraction/Cargo.tom
+# Optional dependencies
+cortex-a = { version = "2.9.x", optional = true }
diff -uNr 03_hacky_hello_world/src/arch/aarch64/start.S 04_zero_overhead_abstraction/src/arch/aarch64/start.S
--- 03_hacky_hello_world/src/arch/aarch64/start.S
+++ 04_zero_overhead_abstraction/src/arch/aarch64/start.S
@@ -1,21 +0,0 @@
-// SPDX-License-Identifier: MIT OR Apache-2.0
-//
-// Copyright (c) 2018-2020 Andre Richter <andre.o.richter@gmail.com>
-
-.section ".text._start"
-
-.global _start
-
-_start:
- mrs x1, mpidr_el1 // Read Multiprocessor Affinity Register
- and x1, x1, #3 // Clear all bits except [1:0], which hold core id
- cbz x1, 2f // Jump to label 2 if we are core 0
-1: wfe // Wait for event
- b 1b // In case an event happened, jump back to 1
-2: // If we are here, we are core0
- ldr x1, =_start // Load address of function "_start()"
- mov sp, x1 // Set start of stack to before our code, aka first
- // address before "_start()"
- bl runtime_init // Jump to the "runtime_init()" kernel function
- b 1b // We should never reach here. But just in case,
- // park this core aswell
diff -uNr 03_hacky_hello_world/src/_arch/aarch64/cpu/smp.rs 04_zero_overhead_abstraction/src/_arch/aarch64/cpu/smp.rs
--- 03_hacky_hello_world/src/_arch/aarch64/cpu/smp.rs
+++ 04_zero_overhead_abstraction/src/_arch/aarch64/cpu/smp.rs
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: MIT OR Apache-2.0
+//
+// Copyright (c) 2018-2020 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.rs 04_zero_overhead_abstraction/src/arch/aarch64.rs
--- 03_hacky_hello_world/src/arch/aarch64.rs
+++ 04_zero_overhead_abstraction/src/arch/aarch64.rs
@@ -4,7 +4,28 @@
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 @@
//! AArch64.
//! Architectural processor code.
-global_asm!(include_str!("aarch64/start.S"));
+use crate::bsp;
-// Assembly counterpart to this file.
-global_asm!(include_str!("cpu.S"));
+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.
@ -71,13 +77,15 @@ diff -uNr 03_hacky_hello_world/src/arch/aarch64.rs 04_zero_overhead_abstraction/
+/// # Safety
+///
+/// - Linker script must ensure to place this function at `0x80_000`.
+#[naked]
+#[no_mangle]
+pub unsafe extern "C" fn _start() -> ! {
+ const CORE_MASK: u64 = 0x3;
+ use crate::runtime_init;
+
+ if bsp::BOOT_CORE_ID == MPIDR_EL1.get() & CORE_MASK {
+ SP.set(bsp::BOOT_CORE_STACK_START);
+ crate::runtime_init::runtime_init()
+ // Expect the boot core to start in EL2.
+ if bsp::cpu::BOOT_CORE_ID == cpu::smp::core_id() {
+ SP.set(bsp::cpu::BOOT_CORE_STACK_START);
+ runtime_init::runtime_init()
+ } else {
+ // If not core0, infinitely wait for events.
+ wait_forever()
@ -85,9 +93,9 @@ diff -uNr 03_hacky_hello_world/src/arch/aarch64.rs 04_zero_overhead_abstraction/
+}
//--------------------------------------------------------------------------------------------------
// Implementation of the kernel's architecture abstraction code
@@ -13,9 +34,7 @@
/// Pause execution on the calling CPU core.
// Public Code
@@ -14,9 +40,7 @@
/// Pause execution on the core.
#[inline(always)]
pub fn wait_forever() -> ! {
- unsafe {
@ -99,52 +107,116 @@ diff -uNr 03_hacky_hello_world/src/arch/aarch64.rs 04_zero_overhead_abstraction/
}
}
diff -uNr 03_hacky_hello_world/src/bsp/rpi.rs 04_zero_overhead_abstraction/src/bsp/rpi.rs
--- 03_hacky_hello_world/src/bsp/rpi.rs
+++ 04_zero_overhead_abstraction/src/bsp/rpi.rs
@@ -7,6 +7,12 @@
use crate::interface;
use core::fmt;
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
@@ -1,21 +0,0 @@
-// SPDX-License-Identifier: MIT OR Apache-2.0
-//
-// Copyright (c) 2018-2020 Andre Richter <andre.o.richter@gmail.com>
-
-.section ".text._start"
-
-.global _start
-
-_start:
- mrs x1, mpidr_el1 // Read Multiprocessor Affinity Register
- and x1, x1, #3 // Clear all bits except [1:0], which hold core id
- cbz x1, 2f // Jump to label 2 if we are core 0
-1: wfe // Wait for event
- b 1b // In case an event happened, jump back to 1
-2: // If we are here, we are core0
- ldr x1, =_start // Load address of function "_start()"
- mov sp, x1 // Set start of stack to before our code, aka first
- // address before "_start()"
- bl runtime_init // Jump to the "runtime_init()" kernel function
- b 1b // We should never reach here. But just in case,
- // park this core aswell
diff -uNr 03_hacky_hello_world/src/bsp/raspberrypi/cpu.rs 04_zero_overhead_abstraction/src/bsp/raspberrypi/cpu.rs
--- 03_hacky_hello_world/src/bsp/raspberrypi/cpu.rs
+++ 04_zero_overhead_abstraction/src/bsp/raspberrypi/cpu.rs
@@ -0,0 +1,15 @@
+// SPDX-License-Identifier: MIT OR Apache-2.0
+//
+// Copyright (c) 2018-2020 Andre Richter <andre.o.richter@gmail.com>
+
+//! BSP Processor code.
+
+//--------------------------------------------------------------------------------------------------
+// Public Definitions
+//--------------------------------------------------------------------------------------------------
+
+/// Used by `arch` code to find the early boot core.
+pub const BOOT_CORE_ID: u64 = 0;
+pub const BOOT_CORE_ID: usize = 0;
+
+/// The early boot core's stack address.
+pub const BOOT_CORE_STACK_START: u64 = 0x80_000;
+
/// A mystical, magical device for generating QEMU output out of the void.
struct QEMUOutput;
diff -uNr 03_hacky_hello_world/src/bsp/raspberrypi.rs 04_zero_overhead_abstraction/src/bsp/raspberrypi.rs
--- 03_hacky_hello_world/src/bsp/raspberrypi.rs
+++ 04_zero_overhead_abstraction/src/bsp/raspberrypi.rs
@@ -5,3 +5,4 @@
//! Top-level BSP file for the Raspberry Pi 3 and 4.
pub mod console;
+pub mod cpu;
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 @@
+// SPDX-License-Identifier: MIT OR Apache-2.0
+//
+// Copyright (c) 2018-2020 Andre Richter <andre.o.richter@gmail.com>
+
+//! Symmetric multiprocessing.
+
+#[cfg(target_arch = "aarch64")]
+#[path = "../_arch/aarch64/cpu/smp.rs"]
+mod arch_cpu_smp;
+pub use arch_cpu_smp::*;
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::*;
+
+pub mod smp;
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
@@ -19,9 +19,7 @@
//! [Architecture-specific code]: arch/index.html
//! [`kernel::interface`]: interface/index.html
@@ -92,9 +92,8 @@
//! - `crate::memory::*`
//! - `crate::bsp::memory::*`
-#![feature(asm)]
#![feature(format_args_nl)]
-#![feature(global_asm)]
+#![feature(naked_functions)]
#![feature(panic_info_message)]
#![no_main]
#![no_std]
@@ -47,7 +45,8 @@
@@ -116,7 +115,8 @@
///
/// - Only a single core must be active and running this function.
unsafe fn kernel_init() -> ! {
- println!("Hello from Rust!");
- println!("[0] Hello from Rust!");
+ println!("[0] Hello from pure Rust!");
- panic!("Stopping here.")
+ println!("[1] Stopping here.");
+ arch::wait_forever()
+ cpu::wait_forever()
}
diff -uNr 03_hacky_hello_world/src/runtime_init.rs 04_zero_overhead_abstraction/src/runtime_init.rs
--- 03_hacky_hello_world/src/runtime_init.rs
+++ 04_zero_overhead_abstraction/src/runtime_init.rs
@@ -42,8 +42,7 @@
@@ -50,8 +50,7 @@
/// # Safety
///
/// - Only a single core must be active and running this function.

@ -2,13 +2,15 @@
//
// Copyright (c) 2018-2020 Andre Richter <andre.o.richter@gmail.com>
//! AArch64.
//! Architectural processor code.
pub mod sync;
use crate::bsp;
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.
@ -16,13 +18,15 @@ use cortex_a::{asm, regs::*};
/// # Safety
///
/// - Linker script must ensure to place this function at `0x80_000`.
#[naked]
#[no_mangle]
pub unsafe extern "C" fn _start() -> ! {
const CORE_MASK: u64 = 0x3;
use crate::runtime_init;
if bsp::BOOT_CORE_ID == MPIDR_EL1.get() & CORE_MASK {
SP.set(bsp::BOOT_CORE_STACK_START);
crate::runtime_init::runtime_init()
// Expect the boot core to start in EL2.
if bsp::cpu::BOOT_CORE_ID == cpu::smp::core_id() {
SP.set(bsp::cpu::BOOT_CORE_STACK_START);
runtime_init::runtime_init()
} else {
// If not core0, infinitely wait for events.
wait_forever()
@ -30,10 +34,10 @@ pub unsafe extern "C" fn _start() -> ! {
}
//--------------------------------------------------------------------------------------------------
// Implementation of the kernel's architecture abstraction code
// Public Code
//--------------------------------------------------------------------------------------------------
/// Pause execution on the calling CPU core.
/// Pause execution on the core.
#[inline(always)]
pub fn wait_forever() -> ! {
loop {

@ -0,0 +1,22 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2018-2020 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)
}

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

@ -1,40 +0,0 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2018-2020 Andre Richter <andre.o.richter@gmail.com>
//! AArch64.
use crate::bsp;
use cortex_a::{asm, regs::*};
/// 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 at `0x80_000`.
#[no_mangle]
pub unsafe extern "C" fn _start() -> ! {
const CORE_MASK: u64 = 0x3;
if bsp::BOOT_CORE_ID == MPIDR_EL1.get() & CORE_MASK {
SP.set(bsp::BOOT_CORE_STACK_START);
crate::runtime_init::runtime_init()
} else {
// If not core0, infinitely wait for events.
wait_forever()
}
}
//--------------------------------------------------------------------------------------------------
// Implementation of the kernel's architecture abstraction code
//--------------------------------------------------------------------------------------------------
/// Pause execution on the calling CPU core.
#[inline(always)]
pub fn wait_forever() -> ! {
loop {
asm::wfe()
}
}

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

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

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

@ -0,0 +1,15 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2018-2020 Andre Richter <andre.o.richter@gmail.com>
//! BSP Processor code.
//--------------------------------------------------------------------------------------------------
// Public Definitions
//--------------------------------------------------------------------------------------------------
/// Used by `arch` code to find the early boot core.
pub const BOOT_CORE_ID: usize = 0;
/// The early boot core's stack address.
pub const BOOT_CORE_STACK_START: u64 = 0x80_000;

@ -1,44 +0,0 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2018-2020 Andre Richter <andre.o.richter@gmail.com>
//! Board Support Package for the Raspberry Pi.
use crate::interface;
use core::fmt;
/// Used by `arch` code to find the early boot core.
pub const BOOT_CORE_ID: u64 = 0;
/// The early boot core's stack address.
pub const BOOT_CORE_STACK_START: u64 = 0x80_000;
/// A mystical, magical device for generating QEMU output out of the void.
struct QEMUOutput;
/// Implementing `console::Write` enables usage of the `format_args!` macros, which in turn are used
/// to implement the `kernel`'s `print!` and `println!` macros.
///
/// See [`src/print.rs`].
///
/// [`src/print.rs`]: ../../print/index.html
impl interface::console::Write for QEMUOutput {
fn write_str(&mut self, s: &str) -> fmt::Result {
for c in s.chars() {
unsafe {
core::ptr::write_volatile(0x3F20_1000 as *mut u8, c as u8);
}
}
Ok(())
}
}
//--------------------------------------------------------------------------------------------------
// Implementation of the kernel's BSP calls
//--------------------------------------------------------------------------------------------------
/// Returns a ready-to-use `console::Write` implementation.
pub fn console() -> impl interface::console::Write {
QEMUOutput {}
}

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

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

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

@ -1,37 +0,0 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2018-2020 Andre Richter <andre.o.richter@gmail.com>
//! Trait definitions for coupling `kernel` and `BSP` code.
//!
//! ```
//! +-------------------+
//! | Interface (Trait) |
//! | |
//! +--+-------------+--+
//! ^ ^
//! | |
//! | |
//! +----------+--+ +--+----------+
//! | Kernel code | | BSP Code |
//! | | | |
//! +-------------+ +-------------+
//! ```
/// System console operations.
pub mod console {
/// Console write functions.
///
/// `core::fmt::Write` is exactly what we need for now. Re-export it here because
/// implementing `console::Write` gives a better hint to the reader about the
/// intention.
pub use core::fmt::Write;
/// Console read functions.
pub trait Read {
/// Read a single character.
fn read_char(&self) -> char {
' '
}
}
}

@ -5,39 +5,109 @@
// Rust embedded logo for `make doc`.
#![doc(html_logo_url = "https://git.io/JeGIp")]
//! The `kernel`
//! The `kernel` binary.
//!
//! The `kernel` is composed by glueing together code from
//! # Code organization and architecture
//!
//! - [Hardware-specific Board Support Packages] (`BSPs`).
//! - [Architecture-specific code].
//! - HW- and architecture-agnostic `kernel` code.
//! The code is divided into different *modules*, each representing a typical **subsystem** of the
//! `kernel`. Top-level module files of subsystems reside directly in the `src` folder. For example,
//! `src/memory.rs` contains code that is concerned with all things memory management.
//!
//! using the [`kernel::interface`] traits.
//! ## Visibility of processor architecture code
//!
//! [Hardware-specific Board Support Packages]: bsp/index.html
//! [Architecture-specific code]: arch/index.html
//! [`kernel::interface`]: interface/index.html
//! Some of the `kernel`'s subsystems depend on low-level code that is specific to the target
//! processor architecture. For each supported processor architecture, there exists a subfolder in
//! `src/_arch`, for example, `src/_arch/aarch64`.
//!
//! The architecture folders mirror the subsystem modules laid out in `src`. For example,
//! architectural code that belongs to the `kernel`'s 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.
//!
//! 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.
//!
//! ## BSP code
//!
//! `BSP` stands for Board Support Package. `BSP` code is organized under `src/bsp.rs` and contains
//! target board specific definitions and functions. These are things such as the board's memory map
//! or instances of drivers for devices that are featured on the respective board.
//!
//! Just like processor architecture code, the `BSP` code's module structure tries to mirror the
//! `kernel`'s subsystem modules, but there is no 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 interfaces
//!
//! Both `arch` and `bsp` contain code that is conditionally compiled depending on the actual target
//! and board for which the kernel is compiled. For example, the `interrupt controller` hardware of
//! the `Raspberry Pi 3` and the `Raspberry Pi 4` is different, but we want the rest of the `kernel`
//! code to play nicely with any of the two without much hassle.
//!
//! In order to provide a clean abstraction between `arch`, `bsp` and `generic kernel code`,
//! `interface` traits are provided *whenever possible* and *where it makes sense*. They are defined
//! in the respective subsystem module and help to enforce the idiom of *program to an interface,
//! not an implementation*. For example, there will be a common IRQ handling interface which the two
//! different interrupt controller `drivers` of both Raspberrys will implement, and only export the
//! interface to the rest of the `kernel`.
//!
//! ```
//! +-------------------+
//! | Interface (Trait) |
//! | |
//! +--+-------------+--+
//! ^ ^
//! | |
//! | |
//! +----------+--+ +--+----------+
//! | kernel code | | bsp code |
//! | | | arch code |
//! +-------------+ +-------------+
//! ```
//!
//! # Summary
//!
//! For a logical `kernel` subsystem, corresponding code can be distributed over several physical
//! locations. Here is an example for the **memory** subsystem:
//!
//! - `src/memory.rs` and `src/memory/**/*`
//! - Common code that is agnostic of target processor architecture and `BSP` characteristics.
//! - Example: A function to zero a chunk of memory.
//! - Interfaces for the memory subsystem that are implemented by `arch` or `BSP` code.
//! - Example: An `MMU` interface that defines `MMU` function prototypes.
//! - `src/bsp/__board_name__/memory.rs` and `src/bsp/__board_name__/memory/**/*`
//! - `BSP` specific code.
//! - Example: The board's memory map (physical addresses of DRAM and MMIO devices).
//! - `src/_arch/__arch_name__/memory.rs` and `src/_arch/__arch_name__/memory/**/*`
//! - Processor architecture specific code.
//! - Example: Implementation of the `MMU` interface for the `__arch_name__` processor
//! architecture.
//!
//! From a namespace perspective, **memory** subsystem code lives in:
//!
//! - `crate::memory::*`
//! - `crate::bsp::memory::*`
#![feature(format_args_nl)]
#![feature(naked_functions)]
#![feature(panic_info_message)]
#![no_main]
#![no_std]
// Conditionally includes the selected `architecture` code, which provides the `_start()` function,
// the first function to run.
mod arch;
// `mod cpu` provides the `_start()` function, the first function to run. `_start()` then calls
// `runtime_init()`, which jumps to `kernel_init()`.
// `_start()` then calls `runtime_init()`, which on completion, jumps to `kernel_init()`.
mod runtime_init;
// Conditionally includes the selected `BSP` code.
mod bsp;
mod interface;
mod console;
mod cpu;
mod memory;
mod panic_wait;
mod print;
mod runtime_init;
/// Early init code.
///
@ -48,5 +118,5 @@ unsafe fn kernel_init() -> ! {
println!("[0] Hello from pure Rust!");
println!("[1] Stopping here.");
arch::wait_forever()
cpu::wait_forever()
}

@ -6,6 +6,10 @@
use core::ops::Range;
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
/// Zero out a memory region.
///
/// # Safety

@ -4,7 +4,7 @@
//! A panic handler that infinitely waits.
use crate::{arch, println};
use crate::{cpu, println};
use core::panic::PanicInfo;
#[panic_handler]
@ -15,5 +15,5 @@ fn panic(info: &PanicInfo) -> ! {
println!("\nKernel panic!");
}
arch::wait_forever()
cpu::wait_forever()
}

@ -4,16 +4,24 @@
//! Printing facilities.
use crate::{bsp, interface};
use crate::{bsp, console};
use core::fmt;
//--------------------------------------------------------------------------------------------------
// Private Code
//--------------------------------------------------------------------------------------------------
#[doc(hidden)]
pub fn _print(args: fmt::Arguments) {
use interface::console::Write;
use console::interface::Write;
bsp::console().write_fmt(args).unwrap();
bsp::console::console().write_fmt(args).unwrap();
}
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
/// Prints without a newline.
///
/// Carbon copy from https://doc.rust-lang.org/src/std/macros.rs.html

@ -7,6 +7,10 @@
use crate::memory;
use core::ops::Range;
//--------------------------------------------------------------------------------------------------
// Private Code
//--------------------------------------------------------------------------------------------------
/// Return the range spanning the .bss section.
///
/// # Safety
@ -36,6 +40,10 @@ unsafe fn zero_bss() {
memory::zero_volatile(bss_range());
}
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
/// Equivalent to `crt0` or `c0` code in C/C++ world. Clears the `bss` section, then jumps to kernel
/// init code.
///

@ -14,7 +14,7 @@ ifeq ($(BSP),rpi3)
QEMU_BINARY = qemu-system-aarch64
QEMU_MACHINE_TYPE = raspi3
QEMU_RELEASE_ARGS = -serial stdio -display none
LINKER_FILE = src/bsp/rpi/link.ld
LINKER_FILE = src/bsp/raspberrypi/link.ld
RUSTC_MISC_ARGS = -C target-cpu=cortex-a53
else ifeq ($(BSP),rpi4)
TARGET = aarch64-unknown-none-softfloat
@ -22,7 +22,7 @@ else ifeq ($(BSP),rpi4)
# QEMU_BINARY = qemu-system-aarch64
# QEMU_MACHINE_TYPE =
# QEMU_RELEASE_ARGS = -serial stdio -display none
LINKER_FILE = src/bsp/rpi/link.ld
LINKER_FILE = src/bsp/raspberrypi/link.ld
RUSTC_MISC_ARGS = -C target-cpu=cortex-a72
endif
@ -60,8 +60,7 @@ $(OUTPUT): $(CARGO_OUTPUT)
$(OBJCOPY_CMD) $< $(OUTPUT)
doc:
cargo xdoc --target=$(TARGET) --features bsp_$(BSP) --document-private-items
xdg-open target/$(TARGET)/doc/kernel/index.html
cargo xdoc --target=$(TARGET) --features bsp_$(BSP) --document-private-items --open
ifeq ($(QEMU_MACHINE_TYPE),)
qemu:

@ -1,37 +1,40 @@
# Tutorial 05 - Safe Globals
## A slightly longer tl;dr
## tl;dr
When we introduced the globally usable `print!` macros in [tutorial 03], we
cheated a bit. Calling `core::fmt`'s `write_fmt()` function, which takes an
`&mut self`, was only working because on each call, a new instance of
`QEMUOutput` was created.
A pseudo-lock is introduced; It is a first showcase of OS synchronization primitives and enables
safe access to a global data structure.
If we would want to preserve some state, e.g. statistics about the number of
characters written, we need to make a single global instance of `QEMUOutput` (in
Rust, using the `static` keyword).
## Mutable globals in Rust
A `static QEMU_OUTPUT`, however, would not allow to call functions taking `&mut
self`. For that, we would need a `static mut`, but calling functions that mutate
state on `static mut`s is unsafe. The Rust compiler's reasoning for this is that
it can then not prevent anymore that multiple cores/threads are mutating the
data concurrently (it is a global, so everyone can reference it from anywhere.
The borrow checker can't help here).
When we introduced the globally usable `print!` macros in [tutorial 03], we cheated a bit. Calling
`core::fmt`'s `write_fmt()` function, which takes an `&mut self`, was only working because on each
call, a new instance of `QEMUOutput` was created.
The solution to this problem is to wrap the global into a synchronization
primitive. In our case, a variant of a *MUTual EXclusion* primivite. `Mutex` is
introduced as a trait in `interfaces.rs`, and implemented by the name of
`NullLock` in `sync.rs` in the `arch` folder. For teaching purposes, to make the
code lean, 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.
If we would want to preserve some state, e.g. statistics about the number of characters written, we
need to make a single global instance of `QEMUOutput` (in Rust, using the `static` keyword).
Instead, it 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 reference types].
A `static QEMU_OUTPUT`, however, would not allow to call functions taking `&mut self`. For that, we
would need a `static mut`, but calling functions that mutate state on `static mut`s is unsafe. The
Rust compiler's reasoning for this is that it can then not prevent anymore that multiple
cores/threads are mutating the data concurrently (it is a global, so everyone can reference it from
anywhere. The borrow checker can't help here).
If you want to compare the `NullLock` to some real-world mutex implementations,
you can check out implemntations in the [spin crate] or the [parking lot crate].
The solution to this problem is to wrap the global into a synchronization primitive. In our case, a
variant of a *MUTual EXclusion* primivite. `Mutex` is introduced as a trait in `synchronization.rs`,
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.
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
reference types].
If you want to compare the `NullLock` to some real-world mutex implementations, you can check out
implemntations in the [spin crate] or the [parking lot crate].
[tutorial 03]: ../03_hacky_hello_world
[interior mutability]: https://doc.rust-lang.org/std/cell/index.html
@ -39,104 +42,33 @@ you can check out implemntations in the [spin crate] or the [parking lot crate].
[spin crate]: https://github.com/mvdnes/spin-rs
[parking lot crate]: https://github.com/Amanieu/parking_lot
### Test it
## Test it
```console
» make qemu
$ make qemu
[...]
[0] Hello from pure Rust!
[1] Chars written: 26
[1] Chars written: 27
[2] Stopping here.
```
## Diff to previous
```diff
diff -uNr 04_zero_overhead_abstraction/src/arch/aarch64/sync.rs 05_safe_globals/src/arch/aarch64/sync.rs
--- 04_zero_overhead_abstraction/src/arch/aarch64/sync.rs
+++ 05_safe_globals/src/arch/aarch64/sync.rs
@@ -0,0 +1,53 @@
+// SPDX-License-Identifier: MIT OR Apache-2.0
+//
+// Copyright (c) 2018-2020 Andre Richter <andre.o.richter@gmail.com>
+
+//! Synchronization primitives.
+
+use crate::interface;
+use core::cell::UnsafeCell;
+
+//--------------------------------------------------------------------------------------------------
+// Arch-public
+//--------------------------------------------------------------------------------------------------
+
+/// A pseudo-lock for teaching purposes.
+///
+/// Used to introduce [interior mutability].
+///
+/// In contrast to a real Mutex implementation, does not protect against concurrent access to the
+/// contained data. This part is preserved for later lessons.
+///
+/// The lock will only be used as long as it is safe to do so, i.e. as long as the kernel is
+/// executing single-threaded, aka only running on a single core with interrupts disabled.
+///
+/// [interior mutability]: https://doc.rust-lang.org/std/cell/index.html
+pub struct NullLock<T: ?Sized> {
+ data: UnsafeCell<T>,
+}
+
+unsafe impl<T: ?Sized + Send> Send for NullLock<T> {}
+unsafe impl<T: ?Sized + Send> Sync for NullLock<T> {}
+
+impl<T> NullLock<T> {
+ /// Wraps `data` into a new `NullLock`.
+ pub const fn new(data: T) -> NullLock<T> {
+ NullLock {
+ data: UnsafeCell::new(data),
+ }
+ }
+}
+
+//--------------------------------------------------------------------------------------------------
+// OS interface implementations
+//--------------------------------------------------------------------------------------------------
+
+impl<T> interface::sync::Mutex for &NullLock<T> {
+ type Data = T;
+
+ fn lock<R>(&mut self, f: impl FnOnce(&mut Self::Data) -> R) -> R {
+ // In a real lock, there would be code encapsulating this line that ensures that this
+ // mutable reference will ever only be given out once at a time.
+ f(unsafe { &mut *self.data.get() })
+ }
+}
diff -uNr 04_zero_overhead_abstraction/src/arch/aarch64.rs 05_safe_globals/src/arch/aarch64.rs
--- 04_zero_overhead_abstraction/src/arch/aarch64.rs
+++ 05_safe_globals/src/arch/aarch64.rs
@@ -4,6 +4,8 @@
//! AArch64.
+pub mod sync;
+
use crate::bsp;
use cortex_a::{asm, regs::*};
diff -uNr 04_zero_overhead_abstraction/src/bsp/rpi.rs 05_safe_globals/src/bsp/rpi.rs
--- 04_zero_overhead_abstraction/src/bsp/rpi.rs
+++ 05_safe_globals/src/bsp/rpi.rs
diff -uNr 04_zero_overhead_abstraction/src/bsp/raspberrypi/console.rs 05_safe_globals/src/bsp/raspberrypi/console.rs
--- 04_zero_overhead_abstraction/src/bsp/raspberrypi/console.rs
+++ 05_safe_globals/src/bsp/raspberrypi/console.rs
@@ -4,7 +4,7 @@
//! Board Support Package for the Raspberry Pi.
//! BSP console facilities.
-use crate::interface;
+use crate::{arch::sync::NullLock, interface};
-use crate::console;
+use crate::{console, synchronization, synchronization::NullLock};
use core::fmt;
/// Used by `arch` code to find the early boot core.
@@ -14,31 +14,107 @@
pub const BOOT_CORE_STACK_START: u64 = 0x80_000;
//--------------------------------------------------------------------------------------------------
@@ -12,25 +12,64 @@
//--------------------------------------------------------------------------------------------------
/// A mystical, magical device for generating QEMU output out of the void.
-struct QEMUOutput;
@ -146,6 +78,25 @@ diff -uNr 04_zero_overhead_abstraction/src/bsp/rpi.rs 05_safe_globals/src/bsp/rp
+ chars_written: usize,
+}
+
+//--------------------------------------------------------------------------------------------------
+// Public Definitions
+//--------------------------------------------------------------------------------------------------
+
+/// The main struct.
+pub struct QEMUOutput {
+ inner: NullLock<QEMUOutputInner>,
+}
+
+//--------------------------------------------------------------------------------------------------
+// Global instances
+//--------------------------------------------------------------------------------------------------
+
+static QEMU_OUTPUT: QEMUOutput = QEMUOutput::new();
//--------------------------------------------------------------------------------------------------
// Private Code
//--------------------------------------------------------------------------------------------------
+impl QEMUOutputInner {
+ const fn new() -> QEMUOutputInner {
+ QEMUOutputInner { chars_written: 0 }
@ -156,21 +107,21 @@ diff -uNr 04_zero_overhead_abstraction/src/bsp/rpi.rs 05_safe_globals/src/bsp/rp
+ unsafe {
+ core::ptr::write_volatile(0x3F20_1000 as *mut u8, c as u8);
+ }
+
+ self.chars_written += 1;
+ }
+}
-/// Implementing `console::Write` enables usage of the `format_args!` macros, which in turn are used
-/// to implement the `kernel`'s `print!` and `println!` macros.
+/// Implementing `core::fmt::Write` enables usage of the `format_args!` macros, which in turn are
+/// used to implement the `kernel`'s `print!` and `println!` macros. By implementing `write_str()`,
+/// we get `write_fmt()` automatically.
+///
+/// The function takes an `&mut self`, so it must be implemented for the inner struct.
+
/// Implementing `core::fmt::Write` enables usage of the `format_args!` macros, which in turn are
/// used to implement the `kernel`'s `print!` and `println!` macros. By implementing `write_str()`,
/// we get `write_fmt()` automatically.
///
+/// The function takes an `&mut self`, so it must be implemented for the inner struct.
+///
/// See [`src/print.rs`].
///
/// [`src/print.rs`]: ../../print/index.html
-impl interface::console::Write for QEMUOutput {
-impl fmt::Write for QEMUOutput {
+impl fmt::Write for QEMUOutputInner {
fn write_str(&mut self, s: &str) -> fmt::Result {
for c in s.chars() {
@ -184,22 +135,13 @@ diff -uNr 04_zero_overhead_abstraction/src/bsp/rpi.rs 05_safe_globals/src/bsp/rp
+ self.write_char(c);
}
+ self.chars_written += s.len();
+
Ok(())
}
}
@@ -41,7 +80,39 @@
// Public Code
//--------------------------------------------------------------------------------------------------
+// BSP-public
+//--------------------------------------------------------------------------------------------------
+
+/// The main struct.
+pub struct QEMUOutput {
+ inner: NullLock<QEMUOutputInner>,
+}
+
+impl QEMUOutput {
+ /// Create a new instance.
+ pub const fn new() -> QEMUOutput {
+ QEMUOutput {
+ inner: NullLock::new(QEMUOutputInner::new()),
@ -207,16 +149,22 @@ diff -uNr 04_zero_overhead_abstraction/src/bsp/rpi.rs 05_safe_globals/src/bsp/rp
+ }
+}
+
+//--------------------------------------------------------------------------------------------------
+// OS interface implementations
+//--------------------------------------------------------------------------------------------------
/// Return a reference to the console.
-pub fn console() -> impl console::interface::Write {
- QEMUOutput {}
+pub fn console() -> &'static impl console::interface::All {
+ &QEMU_OUTPUT
+}
+
+//------------------------------------------------------------------------------
+// OS Interface Code
+//------------------------------------------------------------------------------
+use synchronization::interface::Mutex;
+
+/// Passthrough of `args` to the `core::fmt::Write` implementation, but guarded by a Mutex to
+/// serialize access.
+impl interface::console::Write for QEMUOutput {
+impl console::interface::Write for QEMUOutput {
+ fn write_fmt(&self, args: core::fmt::Arguments) -> fmt::Result {
+ use interface::sync::Mutex;
+
+ // Fully qualified syntax for the call to `core::fmt::Write::write:fmt()` to increase
+ // readability.
+ let mut r = &self.inner;
@ -224,42 +172,20 @@ diff -uNr 04_zero_overhead_abstraction/src/bsp/rpi.rs 05_safe_globals/src/bsp/rp
+ }
+}
+
+impl interface::console::Read for QEMUOutput {}
+
+impl interface::console::Statistics for QEMUOutput {
+impl console::interface::Statistics for QEMUOutput {
+ fn chars_written(&self) -> usize {
+ use interface::sync::Mutex;
+
+ let mut r = &self.inner;
+ r.lock(|inner| inner.chars_written)
+ }
+}
+
+//--------------------------------------------------------------------------------------------------
+// Global instances
+//--------------------------------------------------------------------------------------------------
+
+static QEMU_OUTPUT: QEMUOutput = QEMUOutput::new();
+
+//--------------------------------------------------------------------------------------------------
// Implementation of the kernel's BSP calls
//--------------------------------------------------------------------------------------------------
-/// Returns a ready-to-use `console::Write` implementation.
-pub fn console() -> impl interface::console::Write {
- QEMUOutput {}
+/// Return a reference to a `console::All` implementation.
+pub fn console() -> &'static impl interface::console::All {
+ &QEMU_OUTPUT
}
diff -uNr 04_zero_overhead_abstraction/src/interface.rs 05_safe_globals/src/interface.rs
--- 04_zero_overhead_abstraction/src/interface.rs
+++ 05_safe_globals/src/interface.rs
@@ -20,12 +20,13 @@
diff -uNr 04_zero_overhead_abstraction/src/console.rs 05_safe_globals/src/console.rs
--- 04_zero_overhead_abstraction/src/console.rs
+++ 05_safe_globals/src/console.rs
@@ -10,10 +10,22 @@
/// System console operations.
pub mod console {
/// Console interfaces.
pub mod interface {
+ use core::fmt;
+
/// Console write functions.
@ -272,13 +198,6 @@ diff -uNr 04_zero_overhead_abstraction/src/interface.rs 05_safe_globals/src/inte
+ /// Write a Rust format string.
+ fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result;
+ }
/// Console read functions.
pub trait Read {
@@ -34,4 +35,53 @@
' '
}
}
+
+ /// Console statistics.
+ pub trait Statistics {
@ -286,21 +205,70 @@ diff -uNr 04_zero_overhead_abstraction/src/interface.rs 05_safe_globals/src/inte
+ fn chars_written(&self) -> usize {
+ 0
+ }
+
+ /// Return the number of characters read.
+ fn chars_read(&self) -> usize {
+ 0
+ }
+ }
+
+ /// Trait alias for a full-fledged console.
+ pub trait All = Write + Read + Statistics;
+}
+ pub trait All = Write + Statistics;
}
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
@@ -95,6 +95,7 @@
#![feature(format_args_nl)]
#![feature(naked_functions)]
#![feature(panic_info_message)]
+#![feature(trait_alias)]
#![no_main]
#![no_std]
@@ -108,6 +109,7 @@
mod panic_wait;
mod print;
mod runtime_init;
+mod synchronization;
/// Early init code.
///
@@ -115,8 +117,15 @@
///
/// - Only a single core must be active and running this function.
unsafe fn kernel_init() -> ! {
+ use console::interface::Statistics;
+
println!("[0] Hello from pure Rust!");
- println!("[1] Stopping here.");
+ println!(
+ "[1] Chars written: {}",
+ bsp::console::console().chars_written()
+ );
+
+ println!("[2] Stopping here.");
cpu::wait_forever()
}
diff -uNr 04_zero_overhead_abstraction/src/synchronization.rs 05_safe_globals/src/synchronization.rs
--- 04_zero_overhead_abstraction/src/synchronization.rs
+++ 05_safe_globals/src/synchronization.rs
@@ -0,0 +1,91 @@
+// SPDX-License-Identifier: MIT OR Apache-2.0
+//
+// Copyright (c) 2020 Andre Richter <andre.o.richter@gmail.com>
+
+//! Synchronization primitives.
+
+use core::cell::UnsafeCell;
+
+//--------------------------------------------------------------------------------------------------
+// Public Definitions
+//--------------------------------------------------------------------------------------------------
+
+/// Synchronization interfaces.
+pub mod interface {
+
+/// Synchronization primitives.
+pub mod sync {
+ /// Any object implementing this trait guarantees exclusive access to the data contained within
+ /// the mutex for the duration of the lock.
+ /// the Mutex for the duration of the provided closure.
+ ///
+ /// The trait follows the [Rust embedded WG's
+ /// proposal](https://github.com/korken89/wg/blob/master/rfcs/0377-mutex-trait.md) and therefore
@ -322,38 +290,58 @@ diff -uNr 04_zero_overhead_abstraction/src/interface.rs 05_safe_globals/src/inte
+ /// }
+ /// ```
+ pub trait Mutex {
+ /// Type of data encapsulated by the mutex.
+ /// The type of encapsulated data.
+ type Data;
+
+ /// Creates a critical section and grants temporary mutable access to the encapsulated data.
+ fn lock<R>(&mut self, f: impl FnOnce(&mut Self::Data) -> R) -> R;
+ }
}
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
@@ -21,6 +21,7 @@
#![feature(format_args_nl)]
#![feature(panic_info_message)]
+#![feature(trait_alias)]
#![no_main]
#![no_std]
@@ -45,8 +46,12 @@
///
/// - Only a single core must be active and running this function.
unsafe fn kernel_init() -> ! {
+ use interface::console::Statistics;
+}
+
println!("[0] Hello from pure Rust!");
- println!("[1] Stopping here.");
+ println!("[1] Chars written: {}", bsp::console().chars_written());
+/// A pseudo-lock for teaching purposes.
+///
+/// Used to introduce [interior mutability].
+///
+/// In contrast to a real Mutex implementation, does not protect against concurrent access from
+/// other cores to the contained data. This part is preserved for later lessons.
+///
+/// The lock will only be used as long as it is safe to do so, i.e. as long as the kernel is
+/// executing single-threaded, aka only running on a single core with interrupts disabled.
+///
+/// [interior mutability]: https://doc.rust-lang.org/std/cell/index.html
+pub struct NullLock<T: ?Sized> {
+ data: UnsafeCell<T>,
+}
+
+ println!("[2] Stopping here.");
arch::wait_forever()
}
+//--------------------------------------------------------------------------------------------------
+// Public Code
+//--------------------------------------------------------------------------------------------------
+
+unsafe impl<T: ?Sized> Sync for NullLock<T> {}
+
+impl<T> NullLock<T> {
+ /// Wraps `data` into a new `NullLock`.
+ pub const fn new(data: T) -> Self {
+ Self {
+ data: UnsafeCell::new(data),
+ }
+ }
+}
+
+//------------------------------------------------------------------------------
+// OS Interface Code
+//------------------------------------------------------------------------------
+
+impl<T> interface::Mutex for &NullLock<T> {
+ type Data = T;
+
+ fn lock<R>(&mut self, f: impl FnOnce(&mut Self::Data) -> R) -> R {
+ // In a real lock, there would be code encapsulating this line that ensures that this
+ // mutable reference will ever only be given out once at a time.
+ let data = unsafe { &mut *self.data.get() };
+
+ f(data)
+ }
+}
```

Binary file not shown.

Binary file not shown.

@ -2,13 +2,15 @@
//
// Copyright (c) 2018-2020 Andre Richter <andre.o.richter@gmail.com>
//! AArch64.
//! Architectural processor code.
pub mod sync;
use crate::bsp;
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.
@ -16,13 +18,15 @@ use cortex_a::{asm, regs::*};
/// # Safety
///
/// - Linker script must ensure to place this function at `0x80_000`.
#[naked]
#[no_mangle]
pub unsafe extern "C" fn _start() -> ! {
const CORE_MASK: u64 = 0x3;
use crate::runtime_init;
if bsp::BOOT_CORE_ID == MPIDR_EL1.get() & CORE_MASK {
SP.set(bsp::BOOT_CORE_STACK_START);
crate::relocate::relocate_self::<u64>()
// Expect the boot core to start in EL2.
if bsp::cpu::BOOT_CORE_ID == cpu::smp::core_id() {
SP.set(bsp::cpu::BOOT_CORE_STACK_START);
runtime_init::runtime_init()
} else {
// If not core0, infinitely wait for events.
wait_forever()
@ -30,19 +34,10 @@ pub unsafe extern "C" fn _start() -> ! {
}
//--------------------------------------------------------------------------------------------------
// Implementation of the kernel's architecture abstraction code
// Public Code
//--------------------------------------------------------------------------------------------------
pub use asm::nop;
/// Spin for `n` cycles.
pub fn spin_for_cycles(n: usize) {
for _ in 0..n {
asm::nop();
}
}
/// Pause execution on the calling CPU core.
/// Pause execution on the core.
#[inline(always)]
pub fn wait_forever() -> ! {
loop {

@ -0,0 +1,22 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2018-2020 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)
}

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

@ -1,53 +0,0 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2018-2020 Andre Richter <andre.o.richter@gmail.com>
//! Synchronization primitives.
use crate::interface;
use core::cell::UnsafeCell;
//--------------------------------------------------------------------------------------------------
// Arch-public
//--------------------------------------------------------------------------------------------------
/// A pseudo-lock for teaching purposes.
///
/// Used to introduce [interior mutability].
///
/// In contrast to a real Mutex implementation, does not protect against concurrent access to the
/// contained data. This part is preserved for later lessons.
///
/// The lock will only be used as long as it is safe to do so, i.e. as long as the kernel is
/// executing single-threaded, aka only running on a single core with interrupts disabled.
///
/// [interior mutability]: https://doc.rust-lang.org/std/cell/index.html
pub struct NullLock<T: ?Sized> {
data: UnsafeCell<T>,
}
unsafe impl<T: ?Sized + Send> Send for NullLock<T> {}
unsafe impl<T: ?Sized + Send> Sync for NullLock<T> {}
impl<T> NullLock<T> {
/// Wraps `data` into a new `NullLock`.
pub const fn new(data: T) -> NullLock<T> {
NullLock {
data: UnsafeCell::new(data),
}
}
}
//--------------------------------------------------------------------------------------------------
// OS interface implementations
//--------------------------------------------------------------------------------------------------
impl<T> interface::sync::Mutex for &NullLock<T> {
type Data = T;
fn lock<R>(&mut self, f: impl FnOnce(&mut Self::Data) -> R) -> R {
// In a real lock, there would be code encapsulating this line that ensures that this
// mutable reference will ever only be given out once at a time.
f(unsafe { &mut *self.data.get() })
}
}

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

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

@ -2,16 +2,14 @@
//
// Copyright (c) 2018-2020 Andre Richter <andre.o.richter@gmail.com>
//! Board Support Package for the Raspberry Pi.
//! BSP console facilities.
use crate::{arch::sync::NullLock, interface};
use crate::{console, synchronization, synchronization::NullLock};
use core::fmt;
/// Used by `arch` code to find the early boot core.
pub const BOOT_CORE_ID: u64 = 0;
/// The early boot core's stack address.
pub const BOOT_CORE_STACK_START: u64 = 0x80_000;
//--------------------------------------------------------------------------------------------------
// Private Definitions
//--------------------------------------------------------------------------------------------------
/// A mystical, magical device for generating QEMU output out of the void.
///
@ -20,6 +18,25 @@ struct QEMUOutputInner {
chars_written: usize,
}
//--------------------------------------------------------------------------------------------------
// Public Definitions
//--------------------------------------------------------------------------------------------------
/// The main struct.
pub struct QEMUOutput {
inner: NullLock<QEMUOutputInner>,
}
//--------------------------------------------------------------------------------------------------
// Global instances
//--------------------------------------------------------------------------------------------------
static QEMU_OUTPUT: QEMUOutput = QEMUOutput::new();
//--------------------------------------------------------------------------------------------------
// Private Code
//--------------------------------------------------------------------------------------------------
impl QEMUOutputInner {
const fn new() -> QEMUOutputInner {
QEMUOutputInner { chars_written: 0 }
@ -30,6 +47,8 @@ impl QEMUOutputInner {
unsafe {
core::ptr::write_volatile(0x3F20_1000 as *mut u8, c as u8);
}
self.chars_written += 1;
}
}
@ -53,22 +72,16 @@ impl fmt::Write for QEMUOutputInner {
self.write_char(c);
}
self.chars_written += s.len();
Ok(())
}
}
//--------------------------------------------------------------------------------------------------
// BSP-public
// Public Code
//--------------------------------------------------------------------------------------------------
/// The main struct.
pub struct QEMUOutput {
inner: NullLock<QEMUOutputInner>,
}
impl QEMUOutput {
/// Create a new instance.
pub const fn new() -> QEMUOutput {
QEMUOutput {
inner: NullLock::new(QEMUOutputInner::new()),
@ -76,16 +89,20 @@ impl QEMUOutput {
}
}
//--------------------------------------------------------------------------------------------------
// OS interface implementations
//--------------------------------------------------------------------------------------------------
/// Return a reference to the console.
pub fn console() -> &'static impl console::interface::All {
&QEMU_OUTPUT
}
//------------------------------------------------------------------------------
// OS Interface Code
//------------------------------------------------------------------------------
use synchronization::interface::Mutex;
/// Passthrough of `args` to the `core::fmt::Write` implementation, but guarded by a Mutex to
/// serialize access.
impl interface::console::Write for QEMUOutput {
impl console::interface::Write for QEMUOutput {
fn write_fmt(&self, args: core::fmt::Arguments) -> fmt::Result {
use interface::sync::Mutex;
// Fully qualified syntax for the call to `core::fmt::Write::write:fmt()` to increase
// readability.
let mut r = &self.inner;
@ -93,28 +110,9 @@ impl interface::console::Write for QEMUOutput {
}
}
impl interface::console::Read for QEMUOutput {}
impl interface::console::Statistics for QEMUOutput {
impl console::interface::Statistics for QEMUOutput {
fn chars_written(&self) -> usize {
use interface::sync::Mutex;
let mut r = &self.inner;
r.lock(|inner| inner.chars_written)
}
}
//--------------------------------------------------------------------------------------------------
// Global instances
//--------------------------------------------------------------------------------------------------
static QEMU_OUTPUT: QEMUOutput = QEMUOutput::new();
//--------------------------------------------------------------------------------------------------
// Implementation of the kernel's BSP calls
//--------------------------------------------------------------------------------------------------
/// Return a reference to a `console::All` implementation.
pub fn console() -> &'static impl interface::console::All {
&QEMU_OUTPUT
}

@ -0,0 +1,15 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2018-2020 Andre Richter <andre.o.richter@gmail.com>
//! BSP Processor code.
//--------------------------------------------------------------------------------------------------
// Public Definitions
//--------------------------------------------------------------------------------------------------
/// Used by `arch` code to find the early boot core.
pub const BOOT_CORE_ID: usize = 0;
/// The early boot core's stack address.
pub const BOOT_CORE_STACK_START: u64 = 0x80_000;

@ -0,0 +1,31 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2018-2020 Andre Richter <andre.o.richter@gmail.com>
//! System console.
//--------------------------------------------------------------------------------------------------
// Public Definitions
//--------------------------------------------------------------------------------------------------
/// Console interfaces.
pub mod interface {
use core::fmt;
/// Console write functions.
pub trait Write {
/// Write a Rust format string.
fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result;
}
/// Console statistics.
pub trait Statistics {
/// Return the number of characters written.
fn chars_written(&self) -> usize {
0
}
}
/// Trait alias for a full-fledged console.
pub trait All = Write + Statistics;
}

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

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

@ -1,87 +0,0 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2018-2020 Andre Richter <andre.o.richter@gmail.com>
//! Trait definitions for coupling `kernel` and `BSP` code.
//!
//! ```
//! +-------------------+
//! | Interface (Trait) |
//! | |
//! +--+-------------+--+
//! ^ ^
//! | |
//! | |
//! +----------+--+ +--+----------+
//! | Kernel code | | BSP Code |
//! | | | |
//! +-------------+ +-------------+
//! ```
/// System console operations.
pub mod console {
use core::fmt;
/// Console write functions.
pub trait Write {
/// Write a Rust format string.
fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result;
}
/// Console read functions.
pub trait Read {
/// Read a single character.
fn read_char(&self) -> char {
' '
}
}
/// Console statistics.
pub trait Statistics {
/// Return the number of characters written.
fn chars_written(&self) -> usize {
0
}
/// Return the number of characters read.
fn chars_read(&self) -> usize {
0
}
}
/// Trait alias for a full-fledged console.
pub trait All = Write + Read + Statistics;
}
/// Synchronization primitives.
pub mod sync {
/// Any object implementing this trait guarantees exclusive access to the data contained within
/// the mutex for the duration of the lock.
///
/// The trait follows the [Rust embedded WG's
/// proposal](https://github.com/korken89/wg/blob/master/rfcs/0377-mutex-trait.md) and therefore
/// provides some goodness such as [deadlock
/// prevention](https://github.com/korken89/wg/blob/master/rfcs/0377-mutex-trait.md#design-decisions-and-compatibility).
///
/// # Example
///
/// Since the lock function takes an `&mut self` to enable deadlock-prevention, the trait is
/// best implemented **for a reference to a container struct**, and has a usage pattern that
/// might feel strange at first:
///
/// ```
/// static MUT: Mutex<RefCell<i32>> = Mutex::new(RefCell::new(0));
///
/// fn foo() {
/// let mut r = &MUT; // Note that r is mutable
/// r.lock(|data| *data += 1);
/// }
/// ```
pub trait Mutex {
/// Type of data encapsulated by the mutex.
type Data;
/// Creates a critical section and grants temporary mutable access to the encapsulated data.
fn lock<R>(&mut self, f: impl FnOnce(&mut Self::Data) -> R) -> R;
}
}

@ -5,40 +5,111 @@
// Rust embedded logo for `make doc`.
#![doc(html_logo_url = "https://git.io/JeGIp")]
//! The `kernel`
//! The `kernel` binary.
//!
//! The `kernel` is composed by glueing together code from
//! # Code organization and architecture
//!
//! - [Hardware-specific Board Support Packages] (`BSPs`).
//! - [Architecture-specific code].
//! - HW- and architecture-agnostic `kernel` code.
//! The code is divided into different *modules*, each representing a typical **subsystem** of the
//! `kernel`. Top-level module files of subsystems reside directly in the `src` folder. For example,
//! `src/memory.rs` contains code that is concerned with all things memory management.
//!
//! using the [`kernel::interface`] traits.
//! ## Visibility of processor architecture code
//!
//! [Hardware-specific Board Support Packages]: bsp/index.html
//! [Architecture-specific code]: arch/index.html
//! [`kernel::interface`]: interface/index.html
//! Some of the `kernel`'s subsystems depend on low-level code that is specific to the target
//! processor architecture. For each supported processor architecture, there exists a subfolder in
//! `src/_arch`, for example, `src/_arch/aarch64`.
//!
//! The architecture folders mirror the subsystem modules laid out in `src`. For example,
//! architectural code that belongs to the `kernel`'s 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.
//!
//! 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.
//!
//! ## BSP code
//!
//! `BSP` stands for Board Support Package. `BSP` code is organized under `src/bsp.rs` and contains
//! target board specific definitions and functions. These are things such as the board's memory map
//! or instances of drivers for devices that are featured on the respective board.
//!
//! Just like processor architecture code, the `BSP` code's module structure tries to mirror the
//! `kernel`'s subsystem modules, but there is no 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 interfaces
//!
//! Both `arch` and `bsp` contain code that is conditionally compiled depending on the actual target
//! and board for which the kernel is compiled. For example, the `interrupt controller` hardware of
//! the `Raspberry Pi 3` and the `Raspberry Pi 4` is different, but we want the rest of the `kernel`
//! code to play nicely with any of the two without much hassle.
//!
//! In order to provide a clean abstraction between `arch`, `bsp` and `generic kernel code`,
//! `interface` traits are provided *whenever possible* and *where it makes sense*. They are defined
//! in the respective subsystem module and help to enforce the idiom of *program to an interface,
//! not an implementation*. For example, there will be a common IRQ handling interface which the two
//! different interrupt controller `drivers` of both Raspberrys will implement, and only export the
//! interface to the rest of the `kernel`.
//!
//! ```
//! +-------------------+
//! | Interface (Trait) |
//! | |
//! +--+-------------+--+
//! ^ ^
//! | |
//! | |
//! +----------+--+ +--+----------+
//! | kernel code | | bsp code |
//! | | | arch code |
//! +-------------+ +-------------+
//! ```
//!
//! # Summary
//!
//! For a logical `kernel` subsystem, corresponding code can be distributed over several physical
//! locations. Here is an example for the **memory** subsystem:
//!
//! - `src/memory.rs` and `src/memory/**/*`
//! - Common code that is agnostic of target processor architecture and `BSP` characteristics.
//! - Example: A function to zero a chunk of memory.
//! - Interfaces for the memory subsystem that are implemented by `arch` or `BSP` code.
//! - Example: An `MMU` interface that defines `MMU` function prototypes.
//! - `src/bsp/__board_name__/memory.rs` and `src/bsp/__board_name__/memory/**/*`
//! - `BSP` specific code.
//! - Example: The board's memory map (physical addresses of DRAM and MMIO devices).
//! - `src/_arch/__arch_name__/memory.rs` and `src/_arch/__arch_name__/memory/**/*`
//! - Processor architecture specific code.
//! - Example: Implementation of the `MMU` interface for the `__arch_name__` processor
//! architecture.
//!
//! From a namespace perspective, **memory** subsystem code lives in:
//!
//! - `crate::memory::*`
//! - `crate::bsp::memory::*`
#![feature(format_args_nl)]
#![feature(naked_functions)]
#![feature(panic_info_message)]
#![feature(trait_alias)]
#![no_main]
#![no_std]
// Conditionally includes the selected `architecture` code, which provides the `_start()` function,
// the first function to run.
mod arch;
// `mod cpu` provides the `_start()` function, the first function to run. `_start()` then calls
// `runtime_init()`, which jumps to `kernel_init()`.
// `_start()` then calls `runtime_init()`, which on completion, jumps to `kernel_init()`.
mod runtime_init;
// Conditionally includes the selected `BSP` code.
mod bsp;
mod interface;
mod console;
mod cpu;
mod memory;
mod panic_wait;
mod print;
mod runtime_init;
mod synchronization;
/// Early init code.
///
@ -46,12 +117,15 @@ mod print;
///
/// - Only a single core must be active and running this function.
unsafe fn kernel_init() -> ! {
use interface::console::Statistics;
use console::interface::Statistics;
println!("[0] Hello from pure Rust!");
println!("[1] Chars written: {}", bsp::console().chars_written());
println!(
"[1] Chars written: {}",
bsp::console::console().chars_written()
);
println!("[2] Stopping here.");
arch::wait_forever()
cpu::wait_forever()
}

@ -6,6 +6,10 @@
use core::ops::Range;
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
/// Zero out a memory region.
///
/// # Safety

@ -4,7 +4,7 @@
//! A panic handler that infinitely waits.
use crate::{arch, println};
use crate::{cpu, println};
use core::panic::PanicInfo;
#[panic_handler]
@ -15,5 +15,5 @@ fn panic(info: &PanicInfo) -> ! {
println!("\nKernel panic!");
}
arch::wait_forever()
cpu::wait_forever()
}

@ -4,16 +4,24 @@
//! Printing facilities.
use crate::{bsp, interface};
use crate::{bsp, console};
use core::fmt;
//--------------------------------------------------------------------------------------------------
// Private Code
//--------------------------------------------------------------------------------------------------
#[doc(hidden)]
pub fn _print(args: fmt::Arguments) {
use interface::console::Write;
use console::interface::Write;
bsp::console().write_fmt(args).unwrap();
bsp::console::console().write_fmt(args).unwrap();
}
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
/// Prints without a newline.
///
/// Carbon copy from https://doc.rust-lang.org/src/std/macros.rs.html

@ -7,6 +7,10 @@
use crate::memory;
use core::ops::Range;
//--------------------------------------------------------------------------------------------------
// Private Code
//--------------------------------------------------------------------------------------------------
/// Return the range spanning the .bss section.
///
/// # Safety
@ -36,6 +40,10 @@ unsafe fn zero_bss() {
memory::zero_volatile(bss_range());
}
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
/// Equivalent to `crt0` or `c0` code in C/C++ world. Clears the `bss` section, then jumps to kernel
/// init code.
///

@ -0,0 +1,91 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2020 Andre Richter <andre.o.richter@gmail.com>
//! Synchronization primitives.
use core::cell::UnsafeCell;
//--------------------------------------------------------------------------------------------------
// Public Definitions
//--------------------------------------------------------------------------------------------------
/// Synchronization interfaces.
pub mod interface {
/// Any object implementing this trait guarantees exclusive access to the data contained within
/// the Mutex for the duration of the provided closure.
///
/// The trait follows the [Rust embedded WG's
/// proposal](https://github.com/korken89/wg/blob/master/rfcs/0377-mutex-trait.md) and therefore
/// provides some goodness such as [deadlock
/// prevention](https://github.com/korken89/wg/blob/master/rfcs/0377-mutex-trait.md#design-decisions-and-compatibility).
///
/// # Example
///
/// Since the lock function takes an `&mut self` to enable deadlock-prevention, the trait is
/// best implemented **for a reference to a container struct**, and has a usage pattern that
/// might feel strange at first:
///
/// ```
/// static MUT: Mutex<RefCell<i32>> = Mutex::new(RefCell::new(0));
///
/// fn foo() {
/// let mut r = &MUT; // Note that r is mutable
/// r.lock(|data| *data += 1);
/// }
/// ```
pub trait Mutex {
/// The type of encapsulated data.
type Data;
/// Creates a critical section and grants temporary mutable access to the encapsulated data.
fn lock<R>(&mut self, f: impl FnOnce(&mut Self::Data) -> R) -> R;
}
}
/// A pseudo-lock for teaching purposes.
///
/// Used to introduce [interior mutability].
///
/// In contrast to a real Mutex implementation, does not protect against concurrent access from
/// other cores to the contained data. This part is preserved for later lessons.
///
/// The lock will only be used as long as it is safe to do so, i.e. as long as the kernel is
/// executing single-threaded, aka only running on a single core with interrupts disabled.
///
/// [interior mutability]: https://doc.rust-lang.org/std/cell/index.html
pub struct NullLock<T: ?Sized> {
data: UnsafeCell<T>,
}
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
unsafe impl<T: ?Sized> Sync for NullLock<T> {}
impl<T> NullLock<T> {
/// Wraps `data` into a new `NullLock`.
pub const fn new(data: T) -> Self {
Self {
data: UnsafeCell::new(data),
}
}
}
//------------------------------------------------------------------------------
// OS Interface Code
//------------------------------------------------------------------------------
impl<T> interface::Mutex for &NullLock<T> {
type Data = T;
fn lock<R>(&mut self, f: impl FnOnce(&mut Self::Data) -> R) -> R {
// In a real lock, there would be code encapsulating this line that ensures that this
// mutable reference will ever only be given out once at a time.
let data = unsafe { &mut *self.data.get() };
f(data)
}
}

@ -14,7 +14,7 @@ ifeq ($(BSP),rpi3)
QEMU_BINARY = qemu-system-aarch64
QEMU_MACHINE_TYPE = raspi3
QEMU_RELEASE_ARGS = -serial stdio -display none
LINKER_FILE = src/bsp/rpi/link.ld
LINKER_FILE = src/bsp/raspberrypi/link.ld
RUSTC_MISC_ARGS = -C target-cpu=cortex-a53
else ifeq ($(BSP),rpi4)
TARGET = aarch64-unknown-none-softfloat
@ -22,7 +22,7 @@ else ifeq ($(BSP),rpi4)
# QEMU_BINARY = qemu-system-aarch64
# QEMU_MACHINE_TYPE =
# QEMU_RELEASE_ARGS = -serial stdio -display none
LINKER_FILE = src/bsp/rpi/link.ld
LINKER_FILE = src/bsp/raspberrypi/link.ld
RUSTC_MISC_ARGS = -C target-cpu=cortex-a72
endif
@ -60,8 +60,7 @@ $(OUTPUT): $(CARGO_OUTPUT)
$(OBJCOPY_CMD) $< $(OUTPUT)
doc:
cargo xdoc --target=$(TARGET) --features bsp_$(BSP) --document-private-items
xdg-open target/$(TARGET)/doc/kernel/index.html
cargo xdoc --target=$(TARGET) --features bsp_$(BSP) --document-private-items --open
ifeq ($(QEMU_MACHINE_TYPE),)
qemu:

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

@ -2,14 +2,15 @@
//
// Copyright (c) 2018-2020 Andre Richter <andre.o.richter@gmail.com>
//! AArch64.
//! Architectural processor code.
pub mod sync;
mod time;
use crate::{bsp, interface};
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.
@ -17,13 +18,15 @@ use cortex_a::{asm, regs::*};
/// # Safety
///
/// - Linker script must ensure to place this function at `0x80_000`.
#[naked]
#[no_mangle]
pub unsafe extern "C" fn _start() -> ! {
const CORE_MASK: u64 = 0x3;
use crate::runtime_init;
if bsp::BOOT_CORE_ID == MPIDR_EL1.get() & CORE_MASK {
SP.set(bsp::BOOT_CORE_STACK_START);
crate::runtime_init::runtime_init()
// Expect the boot core to start in EL2.
if bsp::cpu::BOOT_CORE_ID == cpu::smp::core_id() {
SP.set(bsp::cpu::BOOT_CORE_STACK_START);
runtime_init::runtime_init()
} else {
// If not core0, infinitely wait for events.
wait_forever()
@ -31,30 +34,20 @@ pub unsafe extern "C" fn _start() -> ! {
}
//--------------------------------------------------------------------------------------------------
// Global instances
//--------------------------------------------------------------------------------------------------
static TIMER: time::Timer = time::Timer;
//--------------------------------------------------------------------------------------------------
// Implementation of the kernel's architecture abstraction code
// Public Code
//--------------------------------------------------------------------------------------------------
pub use asm::nop;
/// Spin for `n` cycles.
#[inline(always)]
pub fn spin_for_cycles(n: usize) {
for _ in 0..n {
asm::nop();
}
}
/// Return a reference to a `interface::time::TimeKeeper` implementation.
pub fn timer() -> &'static impl interface::time::Timer {
&TIMER
}
/// Pause execution on the calling CPU core.
/// Pause execution on the core.
#[inline(always)]
pub fn wait_forever() -> ! {
loop {

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

Loading…
Cancel
Save