From 4ac92b45dc28b00c8e708c246a7a81d50e54e7a5 Mon Sep 17 00:00:00 2001 From: Andre Richter Date: Tue, 8 Oct 2019 21:50:48 +0200 Subject: [PATCH] Add code for tutorial 05 --- 05_safe_globals/.rustfmt.toml | 3 + 05_safe_globals/.vscode/settings.json | 4 + 05_safe_globals/Cargo.lock | 41 +++ 05_safe_globals/Cargo.toml | 19 ++ 05_safe_globals/Makefile | 76 +++++ 05_safe_globals/README.md | 337 +++++++++++++++++++++ 05_safe_globals/kernel | Bin 0 -> 79552 bytes 05_safe_globals/kernel8.img | Bin 0 -> 6869 bytes 05_safe_globals/src/bsp.rs | 11 + 05_safe_globals/src/bsp/rpi3.rs | 150 +++++++++ 05_safe_globals/src/bsp/rpi3/link.ld | 35 +++ 05_safe_globals/src/bsp/rpi3/panic_wait.rs | 19 ++ 05_safe_globals/src/bsp/rpi3/sync.rs | 47 +++ 05_safe_globals/src/interface.rs | 87 ++++++ 05_safe_globals/src/main.rs | 43 +++ 05_safe_globals/src/print.rs | 34 +++ 05_safe_globals/src/runtime_init.rs | 27 ++ 17 files changed, 933 insertions(+) create mode 100644 05_safe_globals/.rustfmt.toml create mode 100644 05_safe_globals/.vscode/settings.json create mode 100644 05_safe_globals/Cargo.lock create mode 100644 05_safe_globals/Cargo.toml create mode 100644 05_safe_globals/Makefile create mode 100644 05_safe_globals/README.md create mode 100755 05_safe_globals/kernel create mode 100755 05_safe_globals/kernel8.img create mode 100644 05_safe_globals/src/bsp.rs create mode 100644 05_safe_globals/src/bsp/rpi3.rs create mode 100644 05_safe_globals/src/bsp/rpi3/link.ld create mode 100644 05_safe_globals/src/bsp/rpi3/panic_wait.rs create mode 100644 05_safe_globals/src/bsp/rpi3/sync.rs create mode 100644 05_safe_globals/src/interface.rs create mode 100644 05_safe_globals/src/main.rs create mode 100644 05_safe_globals/src/print.rs create mode 100644 05_safe_globals/src/runtime_init.rs diff --git a/05_safe_globals/.rustfmt.toml b/05_safe_globals/.rustfmt.toml new file mode 100644 index 00000000..f255bc13 --- /dev/null +++ b/05_safe_globals/.rustfmt.toml @@ -0,0 +1,3 @@ +newline_style = "Unix" +edition = "2018" +format_code_in_doc_comments = true diff --git a/05_safe_globals/.vscode/settings.json b/05_safe_globals/.vscode/settings.json new file mode 100644 index 00000000..5fc0875d --- /dev/null +++ b/05_safe_globals/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + "rust.features": ["bsp_rpi3"], + "rust.all_targets": false, +} diff --git a/05_safe_globals/Cargo.lock b/05_safe_globals/Cargo.lock new file mode 100644 index 00000000..d9d11c3f --- /dev/null +++ b/05_safe_globals/Cargo.lock @@ -0,0 +1,41 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "cortex-a" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "register 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "kernel" +version = "0.1.0" +dependencies = [ + "cortex-a 2.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "r0" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "register" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "tock-registers 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tock-registers" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[metadata] +"checksum cortex-a 2.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cbb16c411ab74044f174746a6cbae67bcdebea126e376b5441e5986e6a6aa950" +"checksum r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2a38df5b15c8d5c7e8654189744d8e396bddc18ad48041a500ce52d6948941f" +"checksum register 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "469bb5ddde81d67fb8bba4e14d77689b8166cfd077abe7530591cefe29d05823" +"checksum tock-registers 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c758f5195a2e0df9d9fecf6f506506b2766ff74cf64db1e995c87e2761a5c3e2" diff --git a/05_safe_globals/Cargo.toml b/05_safe_globals/Cargo.toml new file mode 100644 index 00000000..a0734bd3 --- /dev/null +++ b/05_safe_globals/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "kernel" +version = "0.1.0" +authors = ["Andre Richter "] +edition = "2018" + +[package.metadata.cargo-xbuild] +sysroot_path = "../xbuild_sysroot" + +# The features section is used to select the target board. +[features] +default = [] +bsp_rpi3 = ["cortex-a"] + +[dependencies] +r0 = "0.2.*" + +# Optional dependencies +cortex-a = { version = "2.*", optional = true } diff --git a/05_safe_globals/Makefile b/05_safe_globals/Makefile new file mode 100644 index 00000000..a811b598 --- /dev/null +++ b/05_safe_globals/Makefile @@ -0,0 +1,76 @@ +## SPDX-License-Identifier: MIT +## +## Copyright (c) 2018-2019 Andre Richter + +# Default to the RPi3 +ifndef BSP + BSP = bsp_rpi3 +endif + +# BSP-specific arguments +ifeq ($(BSP),bsp_rpi3) + TARGET = aarch64-unknown-none-softfloat + OUTPUT = kernel8.img + QEMU_BINARY = qemu-system-aarch64 + QEMU_MACHINE_TYPE = raspi3 + QEMU_MISC_ARGS = -serial null -serial stdio + LINKER_FILE = src/bsp/rpi3/link.ld + RUSTC_MISC_ARGS = -C target-cpu=cortex-a53 +endif + +SOURCES = $(wildcard **/*.rs) $(wildcard **/*.S) $(wildcard **/*.ld) + +XRUSTC_CMD = cargo xrustc \ + --target=$(TARGET) \ + --features $(BSP) \ + --release \ + -- \ + -C link-arg=-T$(LINKER_FILE) \ + $(RUSTC_MISC_ARGS) + +CARGO_OUTPUT = target/$(TARGET)/release/kernel + +OBJCOPY_CMD = cargo objcopy \ + -- \ + --strip-all \ + -O binary + +CONTAINER_UTILS = rustembedded/osdev-utils + +DOCKER_CMD = docker run -it --rm +DOCKER_ARG_CURDIR = -v $(shell pwd):/work -w /work +DOCKER_EXEC_QEMU = $(QEMU_BINARY) -M $(QEMU_MACHINE_TYPE) -kernel $(OUTPUT) + +.PHONY: all qemu clippy clean readelf objdump nm + +all: clean $(OUTPUT) + +$(CARGO_OUTPUT): $(SOURCES) + RUSTFLAGS="-D warnings -D missing_docs" $(XRUSTC_CMD) + +$(OUTPUT): $(CARGO_OUTPUT) + cp $< . + $(OBJCOPY_CMD) $< $(OUTPUT) + +doc: + cargo xdoc --target=$(TARGET) --features $(BSP) --document-private-items + xdg-open target/$(TARGET)/doc/kernel/index.html + +qemu: all + $(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(CONTAINER_UTILS) \ + $(DOCKER_EXEC_QEMU) $(QEMU_MISC_ARGS) + +clippy: + cargo xclippy --target=$(TARGET) --features $(BSP) + +clean: + cargo clean + +readelf: + readelf -a kernel + +objdump: + cargo objdump --target $(TARGET) -- -disassemble -print-imm-hex kernel + +nm: + cargo nm --target $(TARGET) -- kernel | sort diff --git a/05_safe_globals/README.md b/05_safe_globals/README.md new file mode 100644 index 00000000..6d131590 --- /dev/null +++ b/05_safe_globals/README.md @@ -0,0 +1,337 @@ +# Tutorial 05 - Safe Globals + +## A slightly longer 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. + +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). + +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). + +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 `bsp` folder. For teaching purposes, to make the +code lean, it leaves out the actual platform-specific logic for protection +against concurrent access, since we don't need it as long as the kernel only +exeuts on a single core with interrupts disabled. + +Instead, it focuses on showcasing the 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 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 +[accurate mental model for Rust's reference types]: https://docs.rs/dtolnay/0.0.6/dtolnay/macro._02__reference_types.html +[spin crate]: https://github.com/mvdnes/spin-rs +[parking lot crate]: https://github.com/Amanieu/parking_lot + +## Diff to previous +```diff + +diff -uNr 04_zero_overhead_abstraction/src/bsp/rpi3/sync.rs 05_safe_globals/src/bsp/rpi3/sync.rs +--- 04_zero_overhead_abstraction/src/bsp/rpi3/sync.rs ++++ 05_safe_globals/src/bsp/rpi3/sync.rs +@@ -0,0 +1,47 @@ ++// SPDX-License-Identifier: MIT ++// ++// Copyright (c) 2018-2019 Andre Richter ++ ++//! Board-specific synchronization primitives. ++ ++use crate::interface; ++use core::cell::UnsafeCell; ++ ++/// 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 { ++ data: UnsafeCell, ++} ++ ++unsafe impl Send for NullLock {} ++unsafe impl Sync for NullLock {} ++ ++impl NullLock { ++ pub const fn new(data: T) -> NullLock { ++ NullLock { ++ data: UnsafeCell::new(data), ++ } ++ } ++} ++ ++impl interface::sync::Mutex for &NullLock { ++ type Data = T; ++ ++ fn lock(&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/bsp/rpi3.rs 05_safe_globals/src/bsp/rpi3.rs +--- 04_zero_overhead_abstraction/src/bsp/rpi3.rs ++++ 05_safe_globals/src/bsp/rpi3.rs +@@ -5,10 +5,12 @@ + //! Board Support Package for the Raspberry Pi 3. + + mod panic_wait; ++mod sync; + + use crate::interface; + use core::fmt; + use cortex_a::{asm, regs::*}; ++use sync::NullLock; + + /// The entry of the `kernel` binary. + /// +@@ -38,28 +40,100 @@ + } + + /// A mystical, magical device for generating QEMU output out of the void. +-struct QEMUOutput; ++/// ++/// The mutex protected part. ++struct QEMUOutputInner { ++ chars_written: usize, ++} + +-/// Implementing `console::Write` enables usage of the `format_args!` macros, ++impl QEMUOutputInner { ++ const fn new() -> QEMUOutputInner { ++ QEMUOutputInner { chars_written: 0 } ++ } ++ ++ /// Send a character. ++ fn write_char(&mut self, c: char) { ++ unsafe { ++ core::ptr::write_volatile(0x3F21_5040 as *mut u8, c as u8); ++ } ++ } ++} ++ ++/// 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. ++/// 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 QEMUOutputInner { + fn write_str(&mut self, s: &str) -> fmt::Result { + for c in s.chars() { +- unsafe { +- core::ptr::write_volatile(0x3F21_5040 as *mut u8, c as u8); ++ // Convert newline to carrige return + newline. ++ if c == ' +' { ++ self.write_char(' ') + } ++ ++ self.write_char(c); + } + ++ self.chars_written += s.len(); ++ + Ok(()) + } + } + + //////////////////////////////////////////////////////////////////////////////// ++// OS interface implementations ++//////////////////////////////////////////////////////////////////////////////// ++ ++/// The main struct. ++pub struct QEMUOutput { ++ inner: NullLock, ++} ++ ++impl QEMUOutput { ++ pub const fn new() -> QEMUOutput { ++ QEMUOutput { ++ inner: NullLock::new(QEMUOutputInner::new()), ++ } ++ } ++} ++ ++/// Passthrough of `args` to the `core::fmt::Write` implementation, but guarded ++/// by a Mutex to serialize access. ++impl interface::console::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; ++ r.lock(|i| fmt::Write::write_fmt(i, args)) ++ } ++} ++ ++impl interface::console::Read for QEMUOutput {} ++ ++impl interface::console::Statistics for QEMUOutput { ++ fn chars_written(&self) -> usize { ++ use interface::sync::Mutex; ++ ++ let mut r = &self.inner; ++ r.lock(|i| i.chars_written) ++ } ++} ++ ++//////////////////////////////////////////////////////////////////////////////// ++// Global instances ++//////////////////////////////////////////////////////////////////////////////// ++ ++static QEMU_OUTPUT: QEMUOutput = QEMUOutput::new(); ++ ++//////////////////////////////////////////////////////////////////////////////// + // Implementation of the kernel's BSP calls + //////////////////////////////////////////////////////////////////////////////// + +@@ -70,7 +144,7 @@ + } + } + +-/// 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,17 +20,68 @@ + + /// System console operations. + pub mod console { ++ use core::fmt; ++ + /// 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; ++ pub trait Write { ++ fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result; ++ } + + /// Console read functions. + pub trait Read { +- fn read_char(&mut self) -> char { ++ 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> = 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(&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 +@@ -15,6 +15,7 @@ + + #![feature(format_args_nl)] + #![feature(panic_info_message)] ++#![feature(trait_alias)] + #![no_main] + #![no_std] + +@@ -31,8 +32,12 @@ + + /// Entrypoint of the `kernel`. + fn kernel_entry() -> ! { ++ use interface::console::Statistics; ++ + println!("[0] Hello from pure Rust!"); + +- println!("[1] Stopping here."); ++ println!("[1] Chars written: {}", bsp::console().chars_written()); ++ ++ println!("[2] Stopping here."); + bsp::wait_forever() + } +``` diff --git a/05_safe_globals/kernel b/05_safe_globals/kernel new file mode 100755 index 0000000000000000000000000000000000000000..3b81d2269eab6826fe4f5b0eaf0e80c02e1792f2 GIT binary patch literal 79552 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 bA)amNCE*wGn63Puobr|UCxXdWX8HdE3@@(N literal 0 HcmV?d00001 diff --git a/05_safe_globals/kernel8.img b/05_safe_globals/kernel8.img new file mode 100755 index 0000000000000000000000000000000000000000..e9e9964e6fa2752e8ddde41819ce373436747a12 GIT binary patch literal 6869 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 + +//! Conditional exporting of Board Support Packages. + +#[cfg(feature = "bsp_rpi3")] +pub mod rpi3; + +#[cfg(feature = "bsp_rpi3")] +pub use rpi3::*; diff --git a/05_safe_globals/src/bsp/rpi3.rs b/05_safe_globals/src/bsp/rpi3.rs new file mode 100644 index 00000000..24d42d80 --- /dev/null +++ b/05_safe_globals/src/bsp/rpi3.rs @@ -0,0 +1,150 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Board Support Package for the Raspberry Pi 3. + +mod panic_wait; +mod sync; + +use crate::interface; +use core::fmt; +use cortex_a::{asm, regs::*}; +use sync::NullLock; + +/// 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() -> ! { + use crate::runtime_init; + + const CORE_0: u64 = 0; + const CORE_MASK: u64 = 0x3; + const STACK_START: u64 = 0x80_000; + + if CORE_0 == MPIDR_EL1.get() & CORE_MASK { + SP.set(STACK_START); + runtime_init::init() + } else { + // if not core0, infinitely wait for events + loop { + asm::wfe(); + } + } +} + +/// A mystical, magical device for generating QEMU output out of the void. +/// +/// The mutex protected part. +struct QEMUOutputInner { + chars_written: usize, +} + +impl QEMUOutputInner { + const fn new() -> QEMUOutputInner { + QEMUOutputInner { chars_written: 0 } + } + + /// Send a character. + fn write_char(&mut self, c: char) { + unsafe { + core::ptr::write_volatile(0x3F21_5040 as *mut u8, c as u8); + } + } +} + +/// 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 fmt::Write for QEMUOutputInner { + fn write_str(&mut self, s: &str) -> fmt::Result { + for c in s.chars() { + // Convert newline to carrige return + newline. + if c == '\n' { + self.write_char('\r') + } + + self.write_char(c); + } + + self.chars_written += s.len(); + + Ok(()) + } +} + +//////////////////////////////////////////////////////////////////////////////// +// OS interface implementations +//////////////////////////////////////////////////////////////////////////////// + +/// The main struct. +pub struct QEMUOutput { + inner: NullLock, +} + +impl QEMUOutput { + pub const fn new() -> QEMUOutput { + QEMUOutput { + inner: NullLock::new(QEMUOutputInner::new()), + } + } +} + +/// Passthrough of `args` to the `core::fmt::Write` implementation, but guarded +/// by a Mutex to serialize access. +impl interface::console::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; + r.lock(|i| fmt::Write::write_fmt(i, args)) + } +} + +impl interface::console::Read for QEMUOutput {} + +impl interface::console::Statistics for QEMUOutput { + fn chars_written(&self) -> usize { + use interface::sync::Mutex; + + let mut r = &self.inner; + r.lock(|i| i.chars_written) + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Global instances +//////////////////////////////////////////////////////////////////////////////// + +static QEMU_OUTPUT: QEMUOutput = QEMUOutput::new(); + +//////////////////////////////////////////////////////////////////////////////// +// Implementation of the kernel's BSP calls +//////////////////////////////////////////////////////////////////////////////// + +/// Park execution on the calling CPU core. +pub fn wait_forever() -> ! { + loop { + asm::wfe() + } +} + +/// Return a reference to a `console::All` implementation. +pub fn console() -> &'static impl interface::console::All { + &QEMU_OUTPUT +} diff --git a/05_safe_globals/src/bsp/rpi3/link.ld b/05_safe_globals/src/bsp/rpi3/link.ld new file mode 100644 index 00000000..235a0a0c --- /dev/null +++ b/05_safe_globals/src/bsp/rpi3/link.ld @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (c) 2018-2019 Andre Richter + */ + +SECTIONS +{ + /* Set current address to the value from which the RPi3 starts execution */ + . = 0x80000; + + .text : + { + *(.text._start) *(.text*) + } + + .rodata : + { + *(.rodata*) + } + + .data : + { + *(.data*) + } + + /* Align to 8 byte boundary */ + .bss ALIGN(8): + { + __bss_start = .; + *(.bss*); + __bss_end = .; + } + + /DISCARD/ : { *(.comment*) } +} diff --git a/05_safe_globals/src/bsp/rpi3/panic_wait.rs b/05_safe_globals/src/bsp/rpi3/panic_wait.rs new file mode 100644 index 00000000..05581c29 --- /dev/null +++ b/05_safe_globals/src/bsp/rpi3/panic_wait.rs @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! A panic handler that infinitely waits. + +use crate::println; +use core::panic::PanicInfo; + +#[panic_handler] +fn panic(info: &PanicInfo) -> ! { + if let Some(args) = info.message() { + println!("Kernel panic: {}", args); + } else { + println!("Kernel panic!"); + } + + super::wait_forever() +} diff --git a/05_safe_globals/src/bsp/rpi3/sync.rs b/05_safe_globals/src/bsp/rpi3/sync.rs new file mode 100644 index 00000000..9d737e74 --- /dev/null +++ b/05_safe_globals/src/bsp/rpi3/sync.rs @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Board-specific synchronization primitives. + +use crate::interface; +use core::cell::UnsafeCell; + +/// 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 { + data: UnsafeCell, +} + +unsafe impl Send for NullLock {} +unsafe impl Sync for NullLock {} + +impl NullLock { + pub const fn new(data: T) -> NullLock { + NullLock { + data: UnsafeCell::new(data), + } + } +} + +impl interface::sync::Mutex for &NullLock { + type Data = T; + + fn lock(&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 --git a/05_safe_globals/src/interface.rs b/05_safe_globals/src/interface.rs new file mode 100644 index 00000000..4878e6f3 --- /dev/null +++ b/05_safe_globals/src/interface.rs @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! 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 { + fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result; + } + + /// Console read functions. + pub trait Read { + 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> = 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(&mut self, f: impl FnOnce(&mut Self::Data) -> R) -> R; + } +} diff --git a/05_safe_globals/src/main.rs b/05_safe_globals/src/main.rs new file mode 100644 index 00000000..1c0564e0 --- /dev/null +++ b/05_safe_globals/src/main.rs @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +// Rust embedded logo for `make doc`. +#![doc(html_logo_url = "https://git.io/JeGIp")] + +//! The `kernel` +//! +//! The `kernel` is composed by glueing together hardware-specific Board Support +//! Package (`BSP`) code and hardware-agnostic `kernel` code through the +//! [`kernel::interface`] traits. +//! +//! [`kernel::interface`]: interface/index.html + +#![feature(format_args_nl)] +#![feature(panic_info_message)] +#![feature(trait_alias)] +#![no_main] +#![no_std] + +// This module conditionally includes the correct `BSP` which provides the +// `_start()` function, the first function to run. +mod bsp; + +// Afterwards, `BSP`'s early init code calls `runtime_init::init()` of this +// module, which on completion, jumps to `kernel_entry()`. +mod runtime_init; + +mod interface; +mod print; + +/// Entrypoint of the `kernel`. +fn kernel_entry() -> ! { + use interface::console::Statistics; + + println!("[0] Hello from pure Rust!"); + + println!("[1] Chars written: {}", bsp::console().chars_written()); + + println!("[2] Stopping here."); + bsp::wait_forever() +} diff --git a/05_safe_globals/src/print.rs b/05_safe_globals/src/print.rs new file mode 100644 index 00000000..c85d37e7 --- /dev/null +++ b/05_safe_globals/src/print.rs @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Printing facilities. + +use crate::bsp; +use crate::interface; +use core::fmt; + +/// Prints without a newline. +/// +/// Carbon copy from https://doc.rust-lang.org/src/std/macros.rs.html +#[macro_export] +macro_rules! print { + ($($arg:tt)*) => ($crate::print::_print(format_args!($($arg)*))); +} + +/// Prints with a newline. +/// +/// Carbon copy from https://doc.rust-lang.org/src/std/macros.rs.html +#[macro_export] +macro_rules! println { + () => ($crate::print!("\n")); + ($($arg:tt)*) => ({ + $crate::print::_print(format_args_nl!($($arg)*)); + }) +} + +pub fn _print(args: fmt::Arguments) { + use interface::console::Write; + + bsp::console().write_fmt(args).unwrap(); +} diff --git a/05_safe_globals/src/runtime_init.rs b/05_safe_globals/src/runtime_init.rs new file mode 100644 index 00000000..baac32b0 --- /dev/null +++ b/05_safe_globals/src/runtime_init.rs @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2018-2019 Andre Richter + +//! Rust runtime initialization code. + +/// Equivalent to `crt0` or `c0` code in C/C++ world. Clears the `bss` section, +/// then calls the kernel entry. +/// +/// Called from `BSP` code. +/// +/// # Safety +/// +/// - Only a single core must be active and running this function. +#[no_mangle] +pub unsafe fn init() -> ! { + extern "C" { + // Boundaries of the .bss section, provided by the linker script + static mut __bss_start: u64; + static mut __bss_end: u64; + } + + // Zero out the .bss section + r0::zero_bss(&mut __bss_start, &mut __bss_end); + + crate::kernel_entry() +}