diff --git a/06_drivers_gpio_uart/Makefile b/06_drivers_gpio_uart/Makefile
index 1c2be3c7..2fe12d45 100644
--- a/06_drivers_gpio_uart/Makefile
+++ b/06_drivers_gpio_uart/Makefile
@@ -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:
diff --git a/06_drivers_gpio_uart/README.md b/06_drivers_gpio_uart/README.md
index a609b830..7614792c 100644
--- a/06_drivers_gpio_uart/README.md
+++ b/06_drivers_gpio_uart/README.md
@@ -2,9 +2,11 @@
## tl;dr
-Now that we enabled safe globals in the previous tutorial, the infrastructure is
-laid for adding the first real device drivers. We throw out the magic QEMU
-console and use a real UART now. Like serious embedded hackers do!
+Now that we enabled safe globals in the previous tutorial, the infrastructure is laid for adding the
+first real device drivers. We throw out the magic QEMU console and use a real UART now. Like serious
+embedded hackers do!
+
+## Notable additions
- For the first time, we will be able to run the code on the real hardware.
- Therefore, building is now differentiated between the **RPi 3** and the **RPi4**.
@@ -13,15 +15,14 @@ console and use a real UART now. Like serious embedded hackers do!
- `BSP=rpi4 make`
- `BSP=rpi4 make doc`
- Unfortunately, QEMU does not yet support the **RPi4**, so `BSP=rpi4 make qemu` won't work.
-- A `DeviceDriver` trait is added for abstracting `BSP` driver implementations
+- A `driver::interface::DeviceDriver` trait is added for abstracting `BSP` driver implementations
from kernel code.
-- Drivers are stored in `bsp/driver`, and can be reused between `BSP`s.
- - Introducing the `GPIO` driver, which pinmuxes the RPi's PL011 UART.
- - Most importantly, the `PL011Uart` driver: It implements the `Console`
- traits and is from now on used as the system console output.
-- `BSP`s now contain a`memory_map.rs`. In the specific case, they contain the
- RPi's MMIO addresses which are used to instantiate compatible device drivers
- from `bsp/driver`.
+- Drivers are stored in `src/bsp/device_driver`, and can be reused between `BSP`s.
+ - We introduce the `GPIO` driver, which pinmuxes the RPi's PL011 UART.
+ - Most importantly, the `PL011Uart` driver: It implements the `console::interface::*` traits and
+ is from now on used as the main system console output.
+- `BSP`s now contain a memory map in `src/bsp/memory.rs`. In the specific case, they contain the
+ Raspberry's `MMIO` addresses which are used to instantiate the respectivedevice drivers.
- We also modify the `panic!` handler, so that it does not anymore rely on `println!`, which uses
the globally-shared instance of the `UART` that might be locked when an error is encountered (for
now this can't happen due to the `NullLock`, but with a real lock it becomes an issue).
@@ -43,7 +44,7 @@ init_uart_clock=48000000
```
### Pi 3
-3. Copy the following files from the [Raspberry Pi firmware repo](https://github.com/raspberrypi/firmware/tree/master/boot) onto the SD card:
+3. Copy the following files from the [Raspberry Pi firmware repo](https://github.com/raspberrypi/firmware/tree/master/boot) onto the SD card:
- [bootcode.bin](https://github.com/raspberrypi/firmware/raw/master/boot/bootcode.bin)
- [fixup.dat](https://github.com/raspberrypi/firmware/raw/master/boot/fixup.dat)
- [start.elf](https://github.com/raspberrypi/firmware/raw/master/boot/start.elf)
@@ -51,15 +52,14 @@ init_uart_clock=48000000
### Pi 4
-3. Copy the following files from the [Raspberry Pi firmware repo](https://github.com/raspberrypi/firmware/tree/master/boot) onto the SD card:
+3. Copy the following files from the [Raspberry Pi firmware repo](https://github.com/raspberrypi/firmware/tree/master/boot) onto the SD card:
- [fixup4.dat](https://github.com/raspberrypi/firmware/raw/master/boot/fixup4.dat)
- [start4.elf](https://github.com/raspberrypi/firmware/raw/master/boot/start4.elf)
- [bcm2711-rpi-4-b.dtb](https://github.com/raspberrypi/firmware/raw/master/boot/bcm2711-rpi-4-b.dtb)
4. Run `BSP=rpi4 make` and copy the [kernel8.img](kernel8.img) onto the SD card.
-
-_**Note**: Should it not work on your RPi4, try renaming `start4.elf` to `start.elf` (without the 4) on the SD card._
-
+_**Note**: Should it not work on your RPi4, try renaming `start4.elf` to `start.elf` (without the 4)
+on the SD card._
### Common again
@@ -76,886 +76,12 @@ sudo screen /dev/ttyUSB0 230400
```console
[0] Booting on: Raspberry Pi 3
[1] Drivers loaded:
- 1. GPIO
- 2. PL011Uart
-[2] Chars written: 84
+ 1. BCM GPIO
+ 2. BCM PL011 UART
+[2] Chars written: 93
[3] Echoing input now
```
8. Exit screen by pressing ctrl-a ctrl-d or disconnecting the USB serial.
## Diff to previous
-```diff
-
-diff -uNr 05_safe_globals/Cargo.toml 06_drivers_gpio_uart/Cargo.toml
---- 05_safe_globals/Cargo.toml
-+++ 06_drivers_gpio_uart/Cargo.toml
-@@ -10,10 +10,11 @@
- # The features section is used to select the target board.
- [features]
- default = []
--bsp_rpi3 = ["cortex-a"]
--bsp_rpi4 = ["cortex-a"]
-+bsp_rpi3 = ["cortex-a", "register"]
-+bsp_rpi4 = ["cortex-a", "register"]
-
- [dependencies]
-
- # Optional dependencies
- cortex-a = { version = "2.9.x", optional = true }
-+register = { version = "0.5.x", optional = true }
-
-diff -uNr 05_safe_globals/src/arch/aarch64.rs 06_drivers_gpio_uart/src/arch/aarch64.rs
---- 05_safe_globals/src/arch/aarch64.rs
-+++ 06_drivers_gpio_uart/src/arch/aarch64.rs
-@@ -33,6 +33,15 @@
- // Implementation of the kernel's architecture abstraction 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.
- #[inline(always)]
- pub fn wait_forever() -> ! {
-
-diff -uNr 05_safe_globals/src/bsp/driver/bcm/bcm2xxx_gpio.rs 06_drivers_gpio_uart/src/bsp/driver/bcm/bcm2xxx_gpio.rs
---- 05_safe_globals/src/bsp/driver/bcm/bcm2xxx_gpio.rs
-+++ 06_drivers_gpio_uart/src/bsp/driver/bcm/bcm2xxx_gpio.rs
-@@ -0,0 +1,145 @@
-+// SPDX-License-Identifier: MIT OR Apache-2.0
-+//
-+// Copyright (c) 2018-2020 Andre Richter
-+
-+//! GPIO driver.
-+
-+use crate::{arch, arch::sync::NullLock, interface};
-+use core::ops;
-+use register::{mmio::ReadWrite, register_bitfields, register_structs};
-+
-+// GPIO registers.
-+//
-+// Descriptions taken from
-+// https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf
-+register_bitfields! {
-+ u32,
-+
-+ /// GPIO Function Select 1
-+ GPFSEL1 [
-+ /// Pin 15
-+ FSEL15 OFFSET(15) NUMBITS(3) [
-+ Input = 0b000,
-+ Output = 0b001,
-+ AltFunc0 = 0b100 // PL011 UART RX
-+
-+ ],
-+
-+ /// Pin 14
-+ FSEL14 OFFSET(12) NUMBITS(3) [
-+ Input = 0b000,
-+ Output = 0b001,
-+ AltFunc0 = 0b100 // PL011 UART TX
-+ ]
-+ ],
-+
-+ /// GPIO Pull-up/down Clock Register 0
-+ GPPUDCLK0 [
-+ /// Pin 15
-+ PUDCLK15 OFFSET(15) NUMBITS(1) [
-+ NoEffect = 0,
-+ AssertClock = 1
-+ ],
-+
-+ /// Pin 14
-+ PUDCLK14 OFFSET(14) NUMBITS(1) [
-+ NoEffect = 0,
-+ AssertClock = 1
-+ ]
-+ ]
-+}
-+
-+register_structs! {
-+ #[allow(non_snake_case)]
-+ RegisterBlock {
-+ (0x00 => GPFSEL0: ReadWrite),
-+ (0x04 => GPFSEL1: ReadWrite),
-+ (0x08 => GPFSEL2: ReadWrite),
-+ (0x0C => GPFSEL3: ReadWrite),
-+ (0x10 => GPFSEL4: ReadWrite),
-+ (0x14 => GPFSEL5: ReadWrite),
-+ (0x18 => _reserved1),
-+ (0x94 => GPPUD: ReadWrite),
-+ (0x98 => GPPUDCLK0: ReadWrite),
-+ (0x9C => GPPUDCLK1: ReadWrite),
-+ (0xA0 => @END),
-+ }
-+}
-+
-+/// The driver's private data.
-+struct GPIOInner {
-+ base_addr: usize,
-+}
-+
-+/// Deref to RegisterBlock.
-+impl ops::Deref for GPIOInner {
-+ type Target = RegisterBlock;
-+
-+ fn deref(&self) -> &Self::Target {
-+ unsafe { &*self.ptr() }
-+ }
-+}
-+
-+impl GPIOInner {
-+ const fn new(base_addr: usize) -> GPIOInner {
-+ GPIOInner { base_addr }
-+ }
-+
-+ /// Return a pointer to the register block.
-+ fn ptr(&self) -> *const RegisterBlock {
-+ self.base_addr as *const _
-+ }
-+}
-+
-+//--------------------------------------------------------------------------------------------------
-+// BSP-public
-+//--------------------------------------------------------------------------------------------------
-+use interface::sync::Mutex;
-+
-+/// The driver's main struct.
-+pub struct GPIO {
-+ inner: NullLock,
-+}
-+
-+impl GPIO {
-+ pub const unsafe fn new(base_addr: usize) -> GPIO {
-+ GPIO {
-+ inner: NullLock::new(GPIOInner::new(base_addr)),
-+ }
-+ }
-+
-+ /// Map PL011 UART as standard output.
-+ ///
-+ /// TX to pin 14
-+ /// RX to pin 15
-+ pub fn map_pl011_uart(&self) {
-+ let mut r = &self.inner;
-+ r.lock(|inner| {
-+ // Map to pins.
-+ inner
-+ .GPFSEL1
-+ .modify(GPFSEL1::FSEL14::AltFunc0 + GPFSEL1::FSEL15::AltFunc0);
-+
-+ // Enable pins 14 and 15.
-+ inner.GPPUD.set(0);
-+ arch::spin_for_cycles(150);
-+
-+ inner
-+ .GPPUDCLK0
-+ .write(GPPUDCLK0::PUDCLK14::AssertClock + GPPUDCLK0::PUDCLK15::AssertClock);
-+ arch::spin_for_cycles(150);
-+
-+ inner.GPPUDCLK0.set(0);
-+ })
-+ }
-+}
-+
-+//--------------------------------------------------------------------------------------------------
-+// OS interface implementations
-+//--------------------------------------------------------------------------------------------------
-+
-+impl interface::driver::DeviceDriver for GPIO {
-+ fn compatible(&self) -> &str {
-+ "GPIO"
-+ }
-+}
-
-diff -uNr 05_safe_globals/src/bsp/driver/bcm/bcm2xxx_pl011_uart.rs 06_drivers_gpio_uart/src/bsp/driver/bcm/bcm2xxx_pl011_uart.rs
---- 05_safe_globals/src/bsp/driver/bcm/bcm2xxx_pl011_uart.rs
-+++ 06_drivers_gpio_uart/src/bsp/driver/bcm/bcm2xxx_pl011_uart.rs
-@@ -0,0 +1,312 @@
-+// SPDX-License-Identifier: MIT OR Apache-2.0
-+//
-+// Copyright (c) 2018-2020 Andre Richter
-+
-+//! PL011 UART driver.
-+
-+use crate::{arch, arch::sync::NullLock, interface};
-+use core::{fmt, ops};
-+use register::{mmio::*, register_bitfields, register_structs};
-+
-+// PL011 UART registers.
-+//
-+// Descriptions taken from
-+// https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf
-+register_bitfields! {
-+ u32,
-+
-+ /// Flag Register
-+ FR [
-+ /// Transmit FIFO empty. The meaning of this bit depends on the state of the FEN bit in the
-+ /// Line Control Register, UARTLCR_ LCRH.
-+ ///
-+ /// If the FIFO is disabled, this bit is set when the transmit holding register is empty. If
-+ /// the FIFO is enabled, the TXFE bit is set when the transmit FIFO is empty. This bit does
-+ /// not indicate if there is data in the transmit shift register.
-+ TXFE OFFSET(7) NUMBITS(1) [],
-+
-+ /// Transmit FIFO full. The meaning of this bit depends on the state of the FEN bit in the
-+ /// UARTLCR_ LCRH Register.
-+ ///
-+ /// If the FIFO is disabled, this bit is set when the transmit holding register is full. If
-+ /// the FIFO is enabled, the TXFF bit is set when the transmit FIFO is full.
-+ TXFF OFFSET(5) NUMBITS(1) [],
-+
-+ /// Receive FIFO empty. The meaning of this bit depends on the state of the FEN bit in the
-+ /// UARTLCR_H Register.
-+ ///
-+ /// If the FIFO is disabled, this bit is set when the receive holding register is empty. If
-+ /// the FIFO is enabled, the RXFE bit is set when the receive FIFO is empty.
-+ RXFE OFFSET(4) NUMBITS(1) []
-+ ],
-+
-+ /// Integer Baud rate divisor
-+ IBRD [
-+ /// Integer Baud rate divisor
-+ IBRD OFFSET(0) NUMBITS(16) []
-+ ],
-+
-+ /// Fractional Baud rate divisor
-+ FBRD [
-+ /// Fractional Baud rate divisor
-+ FBRD OFFSET(0) NUMBITS(6) []
-+ ],
-+
-+ /// Line Control register
-+ LCRH [
-+ /// Word length. These bits indicate the number of data bits transmitted or received in a
-+ /// frame.
-+ WLEN OFFSET(5) NUMBITS(2) [
-+ FiveBit = 0b00,
-+ SixBit = 0b01,
-+ SevenBit = 0b10,
-+ EightBit = 0b11
-+ ],
-+
-+ /// Enable FIFOs:
-+ ///
-+ /// 0 = FIFOs are disabled (character mode) that is, the FIFOs become 1-byte-deep holding
-+ /// registers
-+ ///
-+ /// 1 = transmit and receive FIFO buffers are enabled (FIFO mode).
-+ FEN OFFSET(4) NUMBITS(1) [
-+ FifosDisabled = 0,
-+ FifosEnabled = 1
-+ ]
-+ ],
-+
-+ /// Control Register
-+ CR [
-+ /// Receive enable. If this bit is set to 1, the receive section of the UART is enabled.
-+ /// Data reception occurs for UART signals. When the UART is disabled in the middle of
-+ /// reception, it completes the current character before stopping.
-+ RXE OFFSET(9) NUMBITS(1) [
-+ Disabled = 0,
-+ Enabled = 1
-+ ],
-+
-+ /// Transmit enable. If this bit is set to 1, the transmit section of the UART is enabled.
-+ /// Data transmission occurs for UART signals. When the UART is disabled in the middle of
-+ /// transmission, it completes the current character before stopping.
-+ TXE OFFSET(8) NUMBITS(1) [
-+ Disabled = 0,
-+ Enabled = 1
-+ ],
-+
-+ /// UART enable
-+ UARTEN OFFSET(0) NUMBITS(1) [
-+ /// If the UART is disabled in the middle of transmission or reception, it completes the
-+ /// current character before stopping.
-+ Disabled = 0,
-+ Enabled = 1
-+ ]
-+ ],
-+
-+ /// Interrupt Clear Register
-+ ICR [
-+ /// Meta field for all pending interrupts
-+ ALL OFFSET(0) NUMBITS(11) []
-+ ]
-+}
-+
-+register_structs! {
-+ #[allow(non_snake_case)]
-+ pub RegisterBlock {
-+ (0x00 => DR: ReadWrite),
-+ (0x04 => _reserved1),
-+ (0x18 => FR: ReadOnly),
-+ (0x1c => _reserved2),
-+ (0x24 => IBRD: WriteOnly),
-+ (0x28 => FBRD: WriteOnly),
-+ (0x2c => LCRH: WriteOnly),
-+ (0x30 => CR: WriteOnly),
-+ (0x34 => _reserved3),
-+ (0x44 => ICR: WriteOnly),
-+ (0x48 => @END),
-+ }
-+}
-+
-+/// The driver's mutex protected part.
-+pub struct PL011UartInner {
-+ base_addr: usize,
-+ chars_written: usize,
-+ chars_read: usize,
-+}
-+
-+/// Deref to RegisterBlock.
-+///
-+/// Allows writing
-+/// ```
-+/// self.DR.read()
-+/// ```
-+/// instead of something along the lines of
-+/// ```
-+/// unsafe { (*PL011UartInner::ptr()).DR.read() }
-+/// ```
-+impl ops::Deref for PL011UartInner {
-+ type Target = RegisterBlock;
-+
-+ fn deref(&self) -> &Self::Target {
-+ unsafe { &*self.ptr() }
-+ }
-+}
-+
-+impl PL011UartInner {
-+ pub const unsafe fn new(base_addr: usize) -> PL011UartInner {
-+ PL011UartInner {
-+ base_addr,
-+ chars_written: 0,
-+ chars_read: 0,
-+ }
-+ }
-+
-+ /// Set up baud rate and characteristics.
-+ ///
-+ /// Results in 8N1 and 230400 baud (if the clk has been previously set to 48 MHz by the
-+ /// firmware).
-+ pub fn init(&self) {
-+ // Turn it off temporarily.
-+ self.CR.set(0);
-+
-+ self.ICR.write(ICR::ALL::CLEAR);
-+ self.IBRD.write(IBRD::IBRD.val(13));
-+ self.FBRD.write(FBRD::FBRD.val(2));
-+ self.LCRH
-+ .write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled); // 8N1 + Fifo on
-+ self.CR
-+ .write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled);
-+ }
-+
-+ /// Return a pointer to the register block.
-+ fn ptr(&self) -> *const RegisterBlock {
-+ self.base_addr as *const _
-+ }
-+
-+ /// Send a character.
-+ fn write_char(&mut self, c: char) {
-+ // Spin while TX FIFO full is set, waiting for an empty slot.
-+ while self.FR.matches_all(FR::TXFF::SET) {
-+ arch::nop();
-+ }
-+
-+ // Write the character to the buffer.
-+ self.DR.set(c as u32);
-+
-+ self.chars_written += 1;
-+ }
-+}
-+
-+/// 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 PL011UartInner {
-+ fn write_str(&mut self, s: &str) -> fmt::Result {
-+ for c in s.chars() {
-+ self.write_char(c);
-+ }
-+
-+ Ok(())
-+ }
-+}
-+
-+//--------------------------------------------------------------------------------------------------
-+// Export the inner struct so that BSPs can use it for the panic handler
-+//--------------------------------------------------------------------------------------------------
-+pub use PL011UartInner as PanicUart;
-+
-+//--------------------------------------------------------------------------------------------------
-+// BSP-public
-+//--------------------------------------------------------------------------------------------------
-+
-+/// The driver's main struct.
-+pub struct PL011Uart {
-+ inner: NullLock,
-+}
-+
-+impl PL011Uart {
-+ /// # Safety
-+ ///
-+ /// The user must ensure to provide the correct `base_addr`.
-+ pub const unsafe fn new(base_addr: usize) -> PL011Uart {
-+ PL011Uart {
-+ inner: NullLock::new(PL011UartInner::new(base_addr)),
-+ }
-+ }
-+}
-+
-+//--------------------------------------------------------------------------------------------------
-+// OS interface implementations
-+//--------------------------------------------------------------------------------------------------
-+use interface::sync::Mutex;
-+
-+impl interface::driver::DeviceDriver for PL011Uart {
-+ fn compatible(&self) -> &str {
-+ "PL011Uart"
-+ }
-+
-+ fn init(&self) -> interface::driver::Result {
-+ let mut r = &self.inner;
-+ r.lock(|inner| inner.init());
-+
-+ Ok(())
-+ }
-+}
-+
-+impl interface::console::Write for PL011Uart {
-+ /// Passthrough of `args` to the `core::fmt::Write` implementation, but guarded by a Mutex to
-+ /// serialize access.
-+ fn write_char(&self, c: char) {
-+ let mut r = &self.inner;
-+ r.lock(|inner| inner.write_char(c));
-+ }
-+
-+ fn write_fmt(&self, args: core::fmt::Arguments) -> fmt::Result {
-+ // Fully qualified syntax for the call to `core::fmt::Write::write:fmt()` to increase
-+ // readability.
-+ let mut r = &self.inner;
-+ r.lock(|inner| fmt::Write::write_fmt(inner, args))
-+ }
-+}
-+
-+impl interface::console::Read for PL011Uart {
-+ fn read_char(&self) -> char {
-+ let mut r = &self.inner;
-+ r.lock(|inner| {
-+ // Spin while RX FIFO empty is set.
-+ while inner.FR.matches_all(FR::RXFE::SET) {
-+ arch::nop();
-+ }
-+
-+ // Read one character.
-+ let mut ret = inner.DR.get() as u8 as char;
-+
-+ // Convert carrige return to newline.
-+ if ret == '\r' {
-+ ret = '\n'
-+ }
-+
-+ // Update statistics.
-+ inner.chars_read += 1;
-+
-+ ret
-+ })
-+ }
-+}
-+
-+impl interface::console::Statistics for PL011Uart {
-+ fn chars_written(&self) -> usize {
-+ let mut r = &self.inner;
-+ r.lock(|inner| inner.chars_written)
-+ }
-+
-+ fn chars_read(&self) -> usize {
-+ let mut r = &self.inner;
-+ r.lock(|inner| inner.chars_read)
-+ }
-+}
-
-diff -uNr 05_safe_globals/src/bsp/driver/bcm.rs 06_drivers_gpio_uart/src/bsp/driver/bcm.rs
---- 05_safe_globals/src/bsp/driver/bcm.rs
-+++ 06_drivers_gpio_uart/src/bsp/driver/bcm.rs
-@@ -0,0 +1,11 @@
-+// SPDX-License-Identifier: MIT OR Apache-2.0
-+//
-+// Copyright (c) 2018-2020 Andre Richter
-+
-+//! BCM driver top level.
-+
-+mod bcm2xxx_gpio;
-+mod bcm2xxx_pl011_uart;
-+
-+pub use bcm2xxx_gpio::GPIO;
-+pub use bcm2xxx_pl011_uart::{PL011Uart, PanicUart};
-
-diff -uNr 05_safe_globals/src/bsp/driver.rs 06_drivers_gpio_uart/src/bsp/driver.rs
---- 05_safe_globals/src/bsp/driver.rs
-+++ 06_drivers_gpio_uart/src/bsp/driver.rs
-@@ -0,0 +1,11 @@
-+// SPDX-License-Identifier: MIT OR Apache-2.0
-+//
-+// Copyright (c) 2018-2020 Andre Richter
-+
-+//! Drivers.
-+
-+#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))]
-+mod bcm;
-+
-+#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))]
-+pub use bcm::*;
-
-diff -uNr 05_safe_globals/src/bsp/rpi/memory_map.rs 06_drivers_gpio_uart/src/bsp/rpi/memory_map.rs
---- 05_safe_globals/src/bsp/rpi/memory_map.rs
-+++ 06_drivers_gpio_uart/src/bsp/rpi/memory_map.rs
-@@ -0,0 +1,18 @@
-+// SPDX-License-Identifier: MIT OR Apache-2.0
-+//
-+// Copyright (c) 2018-2020 Andre Richter
-+
-+//! The board's memory map.
-+
-+/// Physical devices.
-+#[rustfmt::skip]
-+pub mod mmio {
-+ #[cfg(feature = "bsp_rpi3")]
-+ pub const BASE: usize = 0x3F00_0000;
-+
-+ #[cfg(feature = "bsp_rpi4")]
-+ pub const BASE: usize = 0xFE00_0000;
-+
-+ pub const GPIO_BASE: usize = BASE + 0x0020_0000;
-+ pub const PL011_UART_BASE: usize = BASE + 0x0020_1000;
-+}
-
-diff -uNr 05_safe_globals/src/bsp/rpi.rs 06_drivers_gpio_uart/src/bsp/rpi.rs
---- 05_safe_globals/src/bsp/rpi.rs
-+++ 06_drivers_gpio_uart/src/bsp/rpi.rs
-@@ -4,7 +4,10 @@
-
- //! Board Support Package for the Raspberry Pi.
-
--use crate::{arch::sync::NullLock, interface};
-+mod memory_map;
-+
-+use super::driver;
-+use crate::interface;
- use core::fmt;
-
- /// Used by `arch` code to find the early boot core.
-@@ -13,108 +16,59 @@
- /// 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.
--///
--/// 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(0x3F20_1000 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(())
-- }
--}
--
- //--------------------------------------------------------------------------------------------------
--// BSP-public
-+// Global BSP driver instances
- //--------------------------------------------------------------------------------------------------
-
--/// The main struct.
--pub struct QEMUOutput {
-- inner: NullLock,
--}
--
--impl QEMUOutput {
-- pub const fn new() -> QEMUOutput {
-- QEMUOutput {
-- inner: NullLock::new(QEMUOutputInner::new()),
-- }
-- }
--}
-+static GPIO: driver::GPIO = unsafe { driver::GPIO::new(memory_map::mmio::GPIO_BASE) };
-+static PL011_UART: driver::PL011Uart =
-+ unsafe { driver::PL011Uart::new(memory_map::mmio::PL011_UART_BASE) };
-
- //--------------------------------------------------------------------------------------------------
--// OS interface implementations
-+// Implementation of the kernel's BSP calls
- //--------------------------------------------------------------------------------------------------
-
--/// 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(|inner| fmt::Write::write_fmt(inner, args))
-+/// Board identification.
-+pub fn board_name() -> &'static str {
-+ #[cfg(feature = "bsp_rpi3")]
-+ {
-+ "Raspberry Pi 3"
- }
--}
-
--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(|inner| inner.chars_written)
-+ #[cfg(feature = "bsp_rpi4")]
-+ {
-+ "Raspberry Pi 4"
- }
- }
-
--//--------------------------------------------------------------------------------------------------
--// Global instances
--//--------------------------------------------------------------------------------------------------
-+/// Return a reference to a `console::All` implementation.
-+pub fn console() -> &'static impl interface::console::All {
-+ &PL011_UART
-+}
-
--static QEMU_OUTPUT: QEMUOutput = QEMUOutput::new();
-+/// In case of a panic, the panic handler uses this function to take a last shot at printing
-+/// something before the system is halted.
-+///
-+/// # Safety
-+///
-+/// - Use only for printing during a panic.
-+pub unsafe fn panic_console_out() -> impl fmt::Write {
-+ let uart = driver::PanicUart::new(memory_map::mmio::PL011_UART_BASE);
-+ uart.init();
-+ uart
-+}
-
--//--------------------------------------------------------------------------------------------------
--// Implementation of the kernel's BSP calls
--//--------------------------------------------------------------------------------------------------
-+/// Return an array of references to all `DeviceDriver` compatible `BSP` drivers.
-+///
-+/// # Safety
-+///
-+/// The order of devices is the order in which `DeviceDriver::init()` is called.
-+pub fn device_drivers() -> [&'static dyn interface::driver::DeviceDriver; 2] {
-+ [&GPIO, &PL011_UART]
-+}
-
--/// Return a reference to a `console::All` implementation.
--pub fn console() -> &'static impl interface::console::All {
-- &QEMU_OUTPUT
-+/// BSP initialization code that runs after driver init.
-+pub fn post_driver_init() {
-+ // Configure PL011Uart's output pins.
-+ GPIO.map_pl011_uart();
- }
-
-diff -uNr 05_safe_globals/src/bsp.rs 06_drivers_gpio_uart/src/bsp.rs
---- 05_safe_globals/src/bsp.rs
-+++ 06_drivers_gpio_uart/src/bsp.rs
-@@ -4,6 +4,8 @@
-
- //! Conditional exporting of Board Support Packages.
-
-+mod driver;
-+
- #[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))]
- mod rpi;
-
-
-diff -uNr 05_safe_globals/src/interface.rs 06_drivers_gpio_uart/src/interface.rs
---- 05_safe_globals/src/interface.rs
-+++ 06_drivers_gpio_uart/src/interface.rs
-@@ -24,6 +24,9 @@
-
- /// Console write functions.
- pub trait Write {
-+ /// Write a single character.
-+ fn write_char(&self, c: char);
-+
- /// Write a Rust format string.
- fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result;
- }
-@@ -85,3 +88,20 @@
- fn lock(&mut self, f: impl FnOnce(&mut Self::Data) -> R) -> R;
- }
- }
-+
-+/// Driver interfaces.
-+pub mod driver {
-+ /// Driver result type, e.g. for indicating successful driver init.
-+ pub type Result = core::result::Result<(), ()>;
-+
-+ /// Device Driver functions.
-+ pub trait DeviceDriver {
-+ /// Return a compatibility string for identifying the driver.
-+ fn compatible(&self) -> &str;
-+
-+ /// Called by the kernel to bring up the device.
-+ fn init(&self) -> Result {
-+ Ok(())
-+ }
-+ }
-+}
-
-diff -uNr 05_safe_globals/src/main.rs 06_drivers_gpio_uart/src/main.rs
---- 05_safe_globals/src/main.rs
-+++ 06_drivers_gpio_uart/src/main.rs
-@@ -42,16 +42,48 @@
-
- /// Early init code.
- ///
-+/// Concerned with with initializing `BSP` and `arch` parts.
-+///
- /// # Safety
- ///
- /// - Only a single core must be active and running this function.
-+/// - The init calls in this function must appear in the correct order.
- unsafe fn kernel_init() -> ! {
-- use interface::console::Statistics;
-+ for i in bsp::device_drivers().iter() {
-+ if let Err(()) = i.init() {
-+ panic!("Error loading driver: {}", i.compatible())
-+ }
-+ }
-+ bsp::post_driver_init();
-+ // println! is usable from here on.
-+
-+ // Transition from unsafe to safe.
-+ kernel_main()
-+}
-+
-+/// The main function running after the early init.
-+fn kernel_main() -> ! {
-+ use interface::console::All;
-+
-+ // UART should be functional now. Wait for user to hit Enter.
-+ loop {
-+ if bsp::console().read_char() == '\n' {
-+ break;
-+ }
-+ }
-+
-+ println!("[0] Booting on: {}", bsp::board_name());
-
-- println!("[0] Hello from pure Rust!");
-+ println!("[1] Drivers loaded:");
-+ for (i, driver) in bsp::device_drivers().iter().enumerate() {
-+ println!(" {}. {}", i + 1, driver.compatible());
-+ }
-
-- println!("[1] Chars written: {}", bsp::console().chars_written());
-+ println!("[2] Chars written: {}", bsp::console().chars_written());
-+ println!("[3] Echoing input now");
-
-- println!("[2] Stopping here.");
-- arch::wait_forever()
-+ loop {
-+ let c = bsp::console().read_char();
-+ bsp::console().write_char(c);
-+ }
- }
-
-diff -uNr 05_safe_globals/src/panic_wait.rs 06_drivers_gpio_uart/src/panic_wait.rs
---- 05_safe_globals/src/panic_wait.rs
-+++ 06_drivers_gpio_uart/src/panic_wait.rs
-@@ -4,15 +4,31 @@
-
- //! A panic handler that infinitely waits.
-
--use crate::{arch, println};
--use core::panic::PanicInfo;
-+use crate::{arch, bsp};
-+use core::{fmt, panic::PanicInfo};
-+
-+fn _panic_print(args: fmt::Arguments) {
-+ use fmt::Write;
-+
-+ unsafe { bsp::panic_console_out().write_fmt(args).unwrap() };
-+}
-+
-+/// Prints with a newline - only use from the panic handler.
-+///
-+/// Carbon copy from https://doc.rust-lang.org/src/std/macros.rs.html
-+#[macro_export]
-+macro_rules! panic_println {
-+ ($($arg:tt)*) => ({
-+ _panic_print(format_args_nl!($($arg)*));
-+ })
-+}
-
- #[panic_handler]
- fn panic(info: &PanicInfo) -> ! {
- if let Some(args) = info.message() {
-- println!("\nKernel panic: {}", args);
-+ panic_println!("\nKernel panic: {}", args);
- } else {
-- println!("\nKernel panic!");
-+ panic_println!("\nKernel panic!");
- }
-
- arch::wait_forever()
-
-```
diff --git a/06_drivers_gpio_uart/kernel b/06_drivers_gpio_uart/kernel
index 22993e53..7f1534be 100755
Binary files a/06_drivers_gpio_uart/kernel and b/06_drivers_gpio_uart/kernel differ
diff --git a/06_drivers_gpio_uart/kernel8.img b/06_drivers_gpio_uart/kernel8.img
index 3af97dec..ce6278dd 100755
Binary files a/06_drivers_gpio_uart/kernel8.img and b/06_drivers_gpio_uart/kernel8.img differ
diff --git a/06_drivers_gpio_uart/src/arch/aarch64.rs b/06_drivers_gpio_uart/src/_arch/aarch64/cpu.rs
similarity index 62%
rename from 06_drivers_gpio_uart/src/arch/aarch64.rs
rename to 06_drivers_gpio_uart/src/_arch/aarch64/cpu.rs
index 1878b78c..c739a4d5 100644
--- a/06_drivers_gpio_uart/src/arch/aarch64.rs
+++ b/06_drivers_gpio_uart/src/_arch/aarch64/cpu.rs
@@ -2,13 +2,15 @@
//
// Copyright (c) 2018-2020 Andre Richter
-//! 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,19 +34,20 @@ pub unsafe extern "C" fn _start() -> ! {
}
//--------------------------------------------------------------------------------------------------
-// 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();
}
}
-/// Pause execution on the calling CPU core.
+/// Pause execution on the core.
#[inline(always)]
pub fn wait_forever() -> ! {
loop {
diff --git a/06_drivers_gpio_uart/src/_arch/aarch64/cpu/smp.rs b/06_drivers_gpio_uart/src/_arch/aarch64/cpu/smp.rs
new file mode 100644
index 00000000..8429e1d2
--- /dev/null
+++ b/06_drivers_gpio_uart/src/_arch/aarch64/cpu/smp.rs
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: MIT OR Apache-2.0
+//
+// Copyright (c) 2018-2020 Andre Richter
+
+//! Architectural symmetric multiprocessing.
+
+use cortex_a::regs::*;
+
+//--------------------------------------------------------------------------------------------------
+// Public Code
+//--------------------------------------------------------------------------------------------------
+
+/// Return the executing core's id.
+#[inline(always)]
+pub fn core_id() -> T
+where
+ T: From,
+{
+ const CORE_MASK: u64 = 0b11;
+
+ T::from((MPIDR_EL1.get() & CORE_MASK) as u8)
+}
diff --git a/06_drivers_gpio_uart/src/arch.rs b/06_drivers_gpio_uart/src/arch.rs
deleted file mode 100644
index 005f848b..00000000
--- a/06_drivers_gpio_uart/src/arch.rs
+++ /dev/null
@@ -1,11 +0,0 @@
-// SPDX-License-Identifier: MIT OR Apache-2.0
-//
-// Copyright (c) 2018-2020 Andre Richter
-
-//! 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::*;
diff --git a/06_drivers_gpio_uart/src/arch/aarch64/sync.rs b/06_drivers_gpio_uart/src/arch/aarch64/sync.rs
deleted file mode 100644
index 1d1e459f..00000000
--- a/06_drivers_gpio_uart/src/arch/aarch64/sync.rs
+++ /dev/null
@@ -1,53 +0,0 @@
-// SPDX-License-Identifier: MIT OR Apache-2.0
-//
-// Copyright (c) 2018-2020 Andre Richter
-
-//! 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 {
- data: UnsafeCell,
-}
-
-unsafe impl Send for NullLock {}
-unsafe impl Sync for NullLock {}
-
-impl NullLock {
- /// Wraps `data` into a new `NullLock`.
- pub const fn new(data: T) -> NullLock {
- NullLock {
- data: UnsafeCell::new(data),
- }
- }
-}
-
-//--------------------------------------------------------------------------------------------------
-// OS interface implementations
-//--------------------------------------------------------------------------------------------------
-
-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/06_drivers_gpio_uart/src/bsp.rs b/06_drivers_gpio_uart/src/bsp.rs
index 4d7861bb..3a5657ad 100644
--- a/06_drivers_gpio_uart/src/bsp.rs
+++ b/06_drivers_gpio_uart/src/bsp.rs
@@ -2,12 +2,12 @@
//
// Copyright (c) 2018-2020 Andre Richter
-//! Conditional exporting of Board Support Packages.
+//! Conditional re-exporting of Board Support Packages.
-mod driver;
+mod device_driver;
#[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::*;
diff --git a/06_drivers_gpio_uart/src/bsp/driver.rs b/06_drivers_gpio_uart/src/bsp/device_driver.rs
similarity index 93%
rename from 06_drivers_gpio_uart/src/bsp/driver.rs
rename to 06_drivers_gpio_uart/src/bsp/device_driver.rs
index f75093a5..4508e953 100644
--- a/06_drivers_gpio_uart/src/bsp/driver.rs
+++ b/06_drivers_gpio_uart/src/bsp/device_driver.rs
@@ -2,7 +2,7 @@
//
// Copyright (c) 2018-2020 Andre Richter
-//! Drivers.
+//! Device driver.
#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))]
mod bcm;
diff --git a/06_drivers_gpio_uart/src/bsp/driver/bcm.rs b/06_drivers_gpio_uart/src/bsp/device_driver/bcm.rs
similarity index 70%
rename from 06_drivers_gpio_uart/src/bsp/driver/bcm.rs
rename to 06_drivers_gpio_uart/src/bsp/device_driver/bcm.rs
index 40232f30..59071d5d 100644
--- a/06_drivers_gpio_uart/src/bsp/driver/bcm.rs
+++ b/06_drivers_gpio_uart/src/bsp/device_driver/bcm.rs
@@ -7,5 +7,5 @@
mod bcm2xxx_gpio;
mod bcm2xxx_pl011_uart;
-pub use bcm2xxx_gpio::GPIO;
-pub use bcm2xxx_pl011_uart::{PL011Uart, PanicUart};
+pub use bcm2xxx_gpio::*;
+pub use bcm2xxx_pl011_uart::*;
diff --git a/06_drivers_gpio_uart/src/bsp/driver/bcm/bcm2xxx_gpio.rs b/06_drivers_gpio_uart/src/bsp/device_driver/bcm/bcm2xxx_gpio.rs
similarity index 69%
rename from 06_drivers_gpio_uart/src/bsp/driver/bcm/bcm2xxx_gpio.rs
rename to 06_drivers_gpio_uart/src/bsp/device_driver/bcm/bcm2xxx_gpio.rs
index 1bcc9a64..0c17f498 100644
--- a/06_drivers_gpio_uart/src/bsp/driver/bcm/bcm2xxx_gpio.rs
+++ b/06_drivers_gpio_uart/src/bsp/device_driver/bcm/bcm2xxx_gpio.rs
@@ -2,11 +2,15 @@
//
// Copyright (c) 2018-2020 Andre Richter
-//! GPIO driver.
+//! GPIO Driver.
-use crate::{arch, arch::sync::NullLock, interface};
+use crate::{cpu, driver, synchronization, synchronization::NullLock};
use core::ops;
-use register::{mmio::ReadWrite, register_bitfields, register_structs};
+use register::{mmio::*, register_bitfields, register_structs};
+
+//--------------------------------------------------------------------------------------------------
+// Private Definitions
+//--------------------------------------------------------------------------------------------------
// GPIO registers.
//
@@ -66,12 +70,23 @@ register_structs! {
}
}
-/// The driver's private data.
struct GPIOInner {
base_addr: usize,
}
-/// Deref to RegisterBlock.
+//--------------------------------------------------------------------------------------------------
+// Public Definitions
+//--------------------------------------------------------------------------------------------------
+
+/// Representation of the GPIO HW.
+pub struct GPIO {
+ inner: NullLock,
+}
+
+//--------------------------------------------------------------------------------------------------
+// Private Code
+//--------------------------------------------------------------------------------------------------
+
impl ops::Deref for GPIOInner {
type Target = RegisterBlock;
@@ -81,29 +96,28 @@ impl ops::Deref for GPIOInner {
}
impl GPIOInner {
- const fn new(base_addr: usize) -> GPIOInner {
- GPIOInner { base_addr }
+ const fn new(base_addr: usize) -> Self {
+ Self { base_addr }
}
- /// Return a pointer to the register block.
+ /// Return a pointer to the associated MMIO register block.
fn ptr(&self) -> *const RegisterBlock {
self.base_addr as *const _
}
}
//--------------------------------------------------------------------------------------------------
-// BSP-public
+// Public Code
//--------------------------------------------------------------------------------------------------
-use interface::sync::Mutex;
-
-/// The driver's main struct.
-pub struct GPIO {
- inner: NullLock,
-}
impl GPIO {
- pub const unsafe fn new(base_addr: usize) -> GPIO {
- GPIO {
+ /// Create an instance.
+ ///
+ /// # Safety
+ ///
+ /// - The user must ensure to provide the correct `base_addr`.
+ pub const unsafe fn new(base_addr: usize) -> Self {
+ Self {
inner: NullLock::new(GPIOInner::new(base_addr)),
}
}
@@ -122,24 +136,25 @@ impl GPIO {
// Enable pins 14 and 15.
inner.GPPUD.set(0);
- arch::spin_for_cycles(150);
+ cpu::spin_for_cycles(150);
inner
.GPPUDCLK0
.write(GPPUDCLK0::PUDCLK14::AssertClock + GPPUDCLK0::PUDCLK15::AssertClock);
- arch::spin_for_cycles(150);
+ cpu::spin_for_cycles(150);
inner.GPPUDCLK0.set(0);
})
}
}
-//--------------------------------------------------------------------------------------------------
-// OS interface implementations
-//--------------------------------------------------------------------------------------------------
+//------------------------------------------------------------------------------
+// OS Interface Code
+//------------------------------------------------------------------------------
+use synchronization::interface::Mutex;
-impl interface::driver::DeviceDriver for GPIO {
+impl driver::interface::DeviceDriver for GPIO {
fn compatible(&self) -> &str {
- "GPIO"
+ "BCM GPIO"
}
}
diff --git a/06_drivers_gpio_uart/src/bsp/driver/bcm/bcm2xxx_pl011_uart.rs b/06_drivers_gpio_uart/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs
similarity index 89%
rename from 06_drivers_gpio_uart/src/bsp/driver/bcm/bcm2xxx_pl011_uart.rs
rename to 06_drivers_gpio_uart/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs
index 8a1d3a69..60dfb62f 100644
--- a/06_drivers_gpio_uart/src/bsp/driver/bcm/bcm2xxx_pl011_uart.rs
+++ b/06_drivers_gpio_uart/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs
@@ -4,10 +4,14 @@
//! PL011 UART driver.
-use crate::{arch, arch::sync::NullLock, interface};
+use crate::{console, cpu, driver, synchronization, synchronization::NullLock};
use core::{fmt, ops};
use register::{mmio::*, register_bitfields, register_structs};
+//--------------------------------------------------------------------------------------------------
+// Private Definitions
+//--------------------------------------------------------------------------------------------------
+
// PL011 UART registers.
//
// Descriptions taken from
@@ -109,6 +113,10 @@ register_bitfields! {
]
}
+//--------------------------------------------------------------------------------------------------
+// Public Definitions
+//--------------------------------------------------------------------------------------------------
+
register_structs! {
#[allow(non_snake_case)]
pub RegisterBlock {
@@ -126,13 +134,24 @@ register_structs! {
}
}
-/// The driver's mutex protected part.
pub struct PL011UartInner {
base_addr: usize,
chars_written: usize,
chars_read: usize,
}
+// Export the inner struct so that BSPs can use it for the panic handler.
+pub use PL011UartInner as PanicUart;
+
+/// Representation of the UART.
+pub struct PL011Uart {
+ inner: NullLock,
+}
+
+//--------------------------------------------------------------------------------------------------
+// Public Code
+//--------------------------------------------------------------------------------------------------
+
/// Deref to RegisterBlock.
///
/// Allows writing
@@ -152,8 +171,13 @@ impl ops::Deref for PL011UartInner {
}
impl PL011UartInner {
- pub const unsafe fn new(base_addr: usize) -> PL011UartInner {
- PL011UartInner {
+ /// Create an instance.
+ ///
+ /// # Safety
+ ///
+ /// - The user must ensure to provide the correct `base_addr`.
+ pub const unsafe fn new(base_addr: usize) -> Self {
+ Self {
base_addr,
chars_written: 0,
chars_read: 0,
@@ -164,7 +188,7 @@ impl PL011UartInner {
///
/// Results in 8N1 and 230400 baud (if the clk has been previously set to 48 MHz by the
/// firmware).
- pub fn init(&self) {
+ pub fn init(&mut self) {
// Turn it off temporarily.
self.CR.set(0);
@@ -186,7 +210,7 @@ impl PL011UartInner {
fn write_char(&mut self, c: char) {
// Spin while TX FIFO full is set, waiting for an empty slot.
while self.FR.matches_all(FR::TXFF::SET) {
- arch::nop();
+ cpu::nop();
}
// Write the character to the buffer.
@@ -215,42 +239,28 @@ impl fmt::Write for PL011UartInner {
}
}
-//--------------------------------------------------------------------------------------------------
-// Export the inner struct so that BSPs can use it for the panic handler
-//--------------------------------------------------------------------------------------------------
-pub use PL011UartInner as PanicUart;
-
-//--------------------------------------------------------------------------------------------------
-// BSP-public
-//--------------------------------------------------------------------------------------------------
-
-/// The driver's main struct.
-pub struct PL011Uart {
- inner: NullLock,
-}
-
impl PL011Uart {
/// # Safety
///
- /// The user must ensure to provide the correct `base_addr`.
- pub const unsafe fn new(base_addr: usize) -> PL011Uart {
- PL011Uart {
+ /// - The user must ensure to provide the correct `base_addr`.
+ pub const unsafe fn new(base_addr: usize) -> Self {
+ Self {
inner: NullLock::new(PL011UartInner::new(base_addr)),
}
}
}
-//--------------------------------------------------------------------------------------------------
-// OS interface implementations
-//--------------------------------------------------------------------------------------------------
-use interface::sync::Mutex;
+//------------------------------------------------------------------------------
+// OS Interface Code
+//------------------------------------------------------------------------------
+use synchronization::interface::Mutex;
-impl interface::driver::DeviceDriver for PL011Uart {
+impl driver::interface::DeviceDriver for PL011Uart {
fn compatible(&self) -> &str {
- "PL011Uart"
+ "BCM PL011 UART"
}
- fn init(&self) -> interface::driver::Result {
+ fn init(&self) -> Result<(), ()> {
let mut r = &self.inner;
r.lock(|inner| inner.init());
@@ -258,7 +268,7 @@ impl interface::driver::DeviceDriver for PL011Uart {
}
}
-impl interface::console::Write for PL011Uart {
+impl console::interface::Write for PL011Uart {
/// Passthrough of `args` to the `core::fmt::Write` implementation, but guarded by a Mutex to
/// serialize access.
fn write_char(&self, c: char) {
@@ -274,13 +284,13 @@ impl interface::console::Write for PL011Uart {
}
}
-impl interface::console::Read for PL011Uart {
+impl console::interface::Read for PL011Uart {
fn read_char(&self) -> char {
let mut r = &self.inner;
r.lock(|inner| {
// Spin while RX FIFO empty is set.
while inner.FR.matches_all(FR::RXFE::SET) {
- arch::nop();
+ cpu::nop();
}
// Read one character.
@@ -299,7 +309,7 @@ impl interface::console::Read for PL011Uart {
}
}
-impl interface::console::Statistics for PL011Uart {
+impl console::interface::Statistics for PL011Uart {
fn chars_written(&self) -> usize {
let mut r = &self.inner;
r.lock(|inner| inner.chars_written)
diff --git a/06_drivers_gpio_uart/src/bsp/raspberrypi.rs b/06_drivers_gpio_uart/src/bsp/raspberrypi.rs
new file mode 100644
index 00000000..c976cc29
--- /dev/null
+++ b/06_drivers_gpio_uart/src/bsp/raspberrypi.rs
@@ -0,0 +1,38 @@
+// SPDX-License-Identifier: MIT OR Apache-2.0
+//
+// Copyright (c) 2018-2020 Andre Richter
+
+//! Top-level BSP file for the Raspberry Pi 3 and 4.
+
+pub mod console;
+pub mod cpu;
+pub mod driver;
+pub mod memory;
+
+//--------------------------------------------------------------------------------------------------
+// Global instances
+//--------------------------------------------------------------------------------------------------
+use super::device_driver;
+
+static GPIO: device_driver::GPIO =
+ unsafe { device_driver::GPIO::new(memory::map::mmio::GPIO_BASE) };
+
+static PL011_UART: device_driver::PL011Uart =
+ unsafe { device_driver::PL011Uart::new(memory::map::mmio::PL011_UART_BASE) };
+
+//--------------------------------------------------------------------------------------------------
+// Public Code
+//--------------------------------------------------------------------------------------------------
+
+/// Board identification.
+pub fn board_name() -> &'static str {
+ #[cfg(feature = "bsp_rpi3")]
+ {
+ "Raspberry Pi 3"
+ }
+
+ #[cfg(feature = "bsp_rpi4")]
+ {
+ "Raspberry Pi 4"
+ }
+}
diff --git a/06_drivers_gpio_uart/src/bsp/raspberrypi/console.rs b/06_drivers_gpio_uart/src/bsp/raspberrypi/console.rs
new file mode 100644
index 00000000..061f9c1c
--- /dev/null
+++ b/06_drivers_gpio_uart/src/bsp/raspberrypi/console.rs
@@ -0,0 +1,30 @@
+// SPDX-License-Identifier: MIT OR Apache-2.0
+//
+// Copyright (c) 2018-2020 Andre Richter
+
+//! BSP console facilities.
+
+use super::{super::device_driver, memory::map};
+use crate::console;
+use core::fmt;
+
+//--------------------------------------------------------------------------------------------------
+// Public Code
+//--------------------------------------------------------------------------------------------------
+
+/// In case of a panic, the panic handler uses this function to take a last shot at printing
+/// something before the system is halted.
+///
+/// # Safety
+///
+/// - Use only for printing during a panic.
+pub unsafe fn panic_console_out() -> impl fmt::Write {
+ let mut uart = device_driver::PanicUart::new(map::mmio::PL011_UART_BASE);
+ uart.init();
+ uart
+}
+
+/// Return a reference to the console.
+pub fn console() -> &'static impl console::interface::All {
+ &super::PL011_UART
+}
diff --git a/06_drivers_gpio_uart/src/bsp/raspberrypi/cpu.rs b/06_drivers_gpio_uart/src/bsp/raspberrypi/cpu.rs
new file mode 100644
index 00000000..19db276e
--- /dev/null
+++ b/06_drivers_gpio_uart/src/bsp/raspberrypi/cpu.rs
@@ -0,0 +1,15 @@
+// SPDX-License-Identifier: MIT OR Apache-2.0
+//
+// Copyright (c) 2018-2020 Andre Richter
+
+//! 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;
diff --git a/06_drivers_gpio_uart/src/bsp/raspberrypi/driver.rs b/06_drivers_gpio_uart/src/bsp/raspberrypi/driver.rs
new file mode 100644
index 00000000..86526dc0
--- /dev/null
+++ b/06_drivers_gpio_uart/src/bsp/raspberrypi/driver.rs
@@ -0,0 +1,49 @@
+// SPDX-License-Identifier: MIT OR Apache-2.0
+//
+// Copyright (c) 2018-2020 Andre Richter
+
+//! BSP driver support.
+
+use crate::driver;
+
+//--------------------------------------------------------------------------------------------------
+// Public Definitions
+//--------------------------------------------------------------------------------------------------
+
+/// Device Driver Manager type.
+pub struct BSPDriverManager {
+ device_drivers: [&'static (dyn DeviceDriver + Sync); 2],
+}
+
+//--------------------------------------------------------------------------------------------------
+// Global instances
+//--------------------------------------------------------------------------------------------------
+
+static BSP_DRIVER_MANAGER: BSPDriverManager = BSPDriverManager {
+ device_drivers: [&super::GPIO, &super::PL011_UART],
+};
+
+//--------------------------------------------------------------------------------------------------
+// Public Code
+//--------------------------------------------------------------------------------------------------
+
+/// Return a reference to the driver manager.
+pub fn driver_manager() -> &'static impl driver::interface::DriverManager {
+ &BSP_DRIVER_MANAGER
+}
+
+//------------------------------------------------------------------------------
+// OS Interface Code
+//------------------------------------------------------------------------------
+use driver::interface::DeviceDriver;
+
+impl driver::interface::DriverManager for BSPDriverManager {
+ fn all_device_drivers(&self) -> &[&'static (dyn DeviceDriver + Sync)] {
+ &self.device_drivers[..]
+ }
+
+ fn post_device_driver_init(&self) {
+ // Configure PL011Uart's output pins.
+ super::GPIO.map_pl011_uart();
+ }
+}
diff --git a/06_drivers_gpio_uart/src/bsp/rpi/link.ld b/06_drivers_gpio_uart/src/bsp/raspberrypi/link.ld
similarity index 100%
rename from 06_drivers_gpio_uart/src/bsp/rpi/link.ld
rename to 06_drivers_gpio_uart/src/bsp/raspberrypi/link.ld
diff --git a/06_drivers_gpio_uart/src/bsp/raspberrypi/memory.rs b/06_drivers_gpio_uart/src/bsp/raspberrypi/memory.rs
new file mode 100644
index 00000000..7aef077a
--- /dev/null
+++ b/06_drivers_gpio_uart/src/bsp/raspberrypi/memory.rs
@@ -0,0 +1,36 @@
+// SPDX-License-Identifier: MIT OR Apache-2.0
+//
+// Copyright (c) 2018-2020 Andre Richter
+
+//! BSP Memory Management.
+
+//--------------------------------------------------------------------------------------------------
+// Public Definitions
+//--------------------------------------------------------------------------------------------------
+
+/// The board's memory map.
+#[rustfmt::skip]
+pub(super) mod map {
+ pub const GPIO_OFFSET: usize = 0x0020_0000;
+ pub const UART_OFFSET: usize = 0x0020_1000;
+
+ /// Physical devices.
+ #[cfg(feature = "bsp_rpi3")]
+ pub mod mmio {
+ use super::*;
+
+ pub const BASE: usize = 0x3F00_0000;
+ pub const GPIO_BASE: usize = BASE + GPIO_OFFSET;
+ pub const PL011_UART_BASE: usize = BASE + UART_OFFSET;
+ }
+
+ /// Physical devices.
+ #[cfg(feature = "bsp_rpi4")]
+ pub mod mmio {
+ use super::*;
+
+ pub const BASE: usize = 0xFE00_0000;
+ pub const GPIO_BASE: usize = BASE + GPIO_OFFSET;
+ pub const PL011_UART_BASE: usize = BASE + UART_OFFSET;
+ }
+}
diff --git a/06_drivers_gpio_uart/src/bsp/rpi.rs b/06_drivers_gpio_uart/src/bsp/rpi.rs
deleted file mode 100644
index 336db45a..00000000
--- a/06_drivers_gpio_uart/src/bsp/rpi.rs
+++ /dev/null
@@ -1,74 +0,0 @@
-// SPDX-License-Identifier: MIT OR Apache-2.0
-//
-// Copyright (c) 2018-2020 Andre Richter
-
-//! Board Support Package for the Raspberry Pi.
-
-mod memory_map;
-
-use super::driver;
-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;
-
-//--------------------------------------------------------------------------------------------------
-// Global BSP driver instances
-//--------------------------------------------------------------------------------------------------
-
-static GPIO: driver::GPIO = unsafe { driver::GPIO::new(memory_map::mmio::GPIO_BASE) };
-static PL011_UART: driver::PL011Uart =
- unsafe { driver::PL011Uart::new(memory_map::mmio::PL011_UART_BASE) };
-
-//--------------------------------------------------------------------------------------------------
-// Implementation of the kernel's BSP calls
-//--------------------------------------------------------------------------------------------------
-
-/// Board identification.
-pub fn board_name() -> &'static str {
- #[cfg(feature = "bsp_rpi3")]
- {
- "Raspberry Pi 3"
- }
-
- #[cfg(feature = "bsp_rpi4")]
- {
- "Raspberry Pi 4"
- }
-}
-
-/// Return a reference to a `console::All` implementation.
-pub fn console() -> &'static impl interface::console::All {
- &PL011_UART
-}
-
-/// In case of a panic, the panic handler uses this function to take a last shot at printing
-/// something before the system is halted.
-///
-/// # Safety
-///
-/// - Use only for printing during a panic.
-pub unsafe fn panic_console_out() -> impl fmt::Write {
- let uart = driver::PanicUart::new(memory_map::mmio::PL011_UART_BASE);
- uart.init();
- uart
-}
-
-/// Return an array of references to all `DeviceDriver` compatible `BSP` drivers.
-///
-/// # Safety
-///
-/// The order of devices is the order in which `DeviceDriver::init()` is called.
-pub fn device_drivers() -> [&'static dyn interface::driver::DeviceDriver; 2] {
- [&GPIO, &PL011_UART]
-}
-
-/// BSP initialization code that runs after driver init.
-pub fn post_driver_init() {
- // Configure PL011Uart's output pins.
- GPIO.map_pl011_uart();
-}
diff --git a/06_drivers_gpio_uart/src/bsp/rpi/memory_map.rs b/06_drivers_gpio_uart/src/bsp/rpi/memory_map.rs
deleted file mode 100644
index 6e0d6d80..00000000
--- a/06_drivers_gpio_uart/src/bsp/rpi/memory_map.rs
+++ /dev/null
@@ -1,18 +0,0 @@
-// SPDX-License-Identifier: MIT OR Apache-2.0
-//
-// Copyright (c) 2018-2020 Andre Richter
-
-//! The board's memory map.
-
-/// Physical devices.
-#[rustfmt::skip]
-pub mod mmio {
- #[cfg(feature = "bsp_rpi3")]
- pub const BASE: usize = 0x3F00_0000;
-
- #[cfg(feature = "bsp_rpi4")]
- pub const BASE: usize = 0xFE00_0000;
-
- pub const GPIO_BASE: usize = BASE + 0x0020_0000;
- pub const PL011_UART_BASE: usize = BASE + 0x0020_1000;
-}
diff --git a/06_drivers_gpio_uart/src/console.rs b/06_drivers_gpio_uart/src/console.rs
new file mode 100644
index 00000000..2644e12c
--- /dev/null
+++ b/06_drivers_gpio_uart/src/console.rs
@@ -0,0 +1,47 @@
+// SPDX-License-Identifier: MIT OR Apache-2.0
+//
+// Copyright (c) 2018-2020 Andre Richter
+
+//! System console.
+
+//--------------------------------------------------------------------------------------------------
+// Public Definitions
+//--------------------------------------------------------------------------------------------------
+
+/// Console interfaces.
+pub mod interface {
+ use core::fmt;
+
+ /// Console write functions.
+ pub trait Write {
+ /// Write a single character.
+ fn write_char(&self, c: char);
+
+ /// 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;
+}
diff --git a/06_drivers_gpio_uart/src/cpu.rs b/06_drivers_gpio_uart/src/cpu.rs
new file mode 100644
index 00000000..9c67c0e7
--- /dev/null
+++ b/06_drivers_gpio_uart/src/cpu.rs
@@ -0,0 +1,12 @@
+// SPDX-License-Identifier: MIT OR Apache-2.0
+//
+// Copyright (c) 2020 Andre Richter
+
+//! Processor code.
+
+#[cfg(target_arch = "aarch64")]
+#[path = "_arch/aarch64/cpu.rs"]
+mod arch_cpu;
+pub use arch_cpu::*;
+
+pub mod smp;
diff --git a/06_drivers_gpio_uart/src/cpu/smp.rs b/06_drivers_gpio_uart/src/cpu/smp.rs
new file mode 100644
index 00000000..b1428884
--- /dev/null
+++ b/06_drivers_gpio_uart/src/cpu/smp.rs
@@ -0,0 +1,10 @@
+// SPDX-License-Identifier: MIT OR Apache-2.0
+//
+// Copyright (c) 2018-2020 Andre Richter
+
+//! Symmetric multiprocessing.
+
+#[cfg(target_arch = "aarch64")]
+#[path = "../_arch/aarch64/cpu/smp.rs"]
+mod arch_cpu_smp;
+pub use arch_cpu_smp::*;
diff --git a/06_drivers_gpio_uart/src/driver.rs b/06_drivers_gpio_uart/src/driver.rs
new file mode 100644
index 00000000..c63b8301
--- /dev/null
+++ b/06_drivers_gpio_uart/src/driver.rs
@@ -0,0 +1,41 @@
+// SPDX-License-Identifier: MIT OR Apache-2.0
+//
+// Copyright (c) 2018-2020 Andre Richter
+
+//! Driver support.
+
+//--------------------------------------------------------------------------------------------------
+// Public Definitions
+//--------------------------------------------------------------------------------------------------
+
+/// Driver interfaces.
+pub mod interface {
+
+ /// Device Driver functions.
+ pub trait DeviceDriver {
+ /// Return a compatibility string for identifying the driver.
+ fn compatible(&self) -> &str;
+
+ /// Called by the kernel to bring up the device.
+ fn init(&self) -> Result<(), ()> {
+ Ok(())
+ }
+ }
+
+ /// Device driver management functions.
+ ///
+ /// The `BSP` is supposed to supply one global instance.
+ pub trait DriverManager {
+ /// Return a slice of references to all `BSP`-instantiated drivers.
+ ///
+ /// # Safety
+ ///
+ /// - The order of devices is the order in which `DeviceDriver::init()` is called.
+ fn all_device_drivers(&self) -> &[&'static (dyn DeviceDriver + Sync)];
+
+ /// Initialization code that runs after driver init.
+ ///
+ /// For example, device driver code that depends on other drivers already being online.
+ fn post_device_driver_init(&self);
+ }
+}
diff --git a/06_drivers_gpio_uart/src/interface.rs b/06_drivers_gpio_uart/src/interface.rs
deleted file mode 100644
index d7d0bfd4..00000000
--- a/06_drivers_gpio_uart/src/interface.rs
+++ /dev/null
@@ -1,107 +0,0 @@
-// SPDX-License-Identifier: MIT OR Apache-2.0
-//
-// Copyright (c) 2018-2020 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 {
- /// Write a single character.
- fn write_char(&self, c: char);
-
- /// 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> = 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;
- }
-}
-
-/// Driver interfaces.
-pub mod driver {
- /// Driver result type, e.g. for indicating successful driver init.
- pub type Result = core::result::Result<(), ()>;
-
- /// Device Driver functions.
- pub trait DeviceDriver {
- /// Return a compatibility string for identifying the driver.
- fn compatible(&self) -> &str;
-
- /// Called by the kernel to bring up the device.
- fn init(&self) -> Result {
- Ok(())
- }
- }
-}
diff --git a/06_drivers_gpio_uart/src/main.rs b/06_drivers_gpio_uart/src/main.rs
index 825e840b..d0f69d0f 100644
--- a/06_drivers_gpio_uart/src/main.rs
+++ b/06_drivers_gpio_uart/src/main.rs
@@ -5,56 +5,136 @@
// 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
+//! # TL;DR - Overview of important Kernel entities
//!
-//! - [Hardware-specific Board Support Packages] (`BSPs`).
-//! - [Architecture-specific code].
-//! - HW- and architecture-agnostic `kernel` code.
+//! - [`bsp::console::console()`] - Returns a reference to the kernel's [console interface].
+//! - [`bsp::driver::driver_manager()`] - Returns a reference to the kernel's [driver interface].
//!
-//! using the [`kernel::interface`] traits.
+//! [console interface]: ../libkernel/console/interface/index.html
+//! [driver interface]: ../libkernel/driver/interface/trait.DriverManager.html
//!
-//! [Hardware-specific Board Support Packages]: bsp/index.html
-//! [Architecture-specific code]: arch/index.html
-//! [`kernel::interface`]: interface/index.html
+//! # 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(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 driver;
mod memory;
mod panic_wait;
mod print;
+mod runtime_init;
+mod synchronization;
/// Early init code.
///
-/// Concerned with with initializing `BSP` and `arch` parts.
-///
/// # Safety
///
/// - Only a single core must be active and running this function.
/// - The init calls in this function must appear in the correct order.
unsafe fn kernel_init() -> ! {
- for i in bsp::device_drivers().iter() {
- if let Err(()) = i.init() {
+ use driver::interface::DriverManager;
+
+ for i in bsp::driver::driver_manager().all_device_drivers().iter() {
+ if i.init().is_err() {
panic!("Error loading driver: {}", i.compatible())
}
}
- bsp::post_driver_init();
+ bsp::driver::driver_manager().post_device_driver_init();
// println! is usable from here on.
// Transition from unsafe to safe.
@@ -63,11 +143,12 @@ unsafe fn kernel_init() -> ! {
/// The main function running after the early init.
fn kernel_main() -> ! {
- use interface::console::All;
+ use console::interface::All;
+ use driver::interface::DriverManager;
- // UART should be functional now. Wait for user to hit Enter.
+ // Wait for user to hit Enter.
loop {
- if bsp::console().read_char() == '\n' {
+ if bsp::console::console().read_char() == '\n' {
break;
}
}
@@ -75,15 +156,22 @@ fn kernel_main() -> ! {
println!("[0] Booting on: {}", bsp::board_name());
println!("[1] Drivers loaded:");
- for (i, driver) in bsp::device_drivers().iter().enumerate() {
+ for (i, driver) in bsp::driver::driver_manager()
+ .all_device_drivers()
+ .iter()
+ .enumerate()
+ {
println!(" {}. {}", i + 1, driver.compatible());
}
- println!("[2] Chars written: {}", bsp::console().chars_written());
+ println!(
+ "[2] Chars written: {}",
+ bsp::console::console().chars_written()
+ );
println!("[3] Echoing input now");
loop {
- let c = bsp::console().read_char();
- bsp::console().write_char(c);
+ let c = bsp::console::console().read_char();
+ bsp::console::console().write_char(c);
}
}
diff --git a/06_drivers_gpio_uart/src/memory.rs b/06_drivers_gpio_uart/src/memory.rs
index f551a2cc..71dc0292 100644
--- a/06_drivers_gpio_uart/src/memory.rs
+++ b/06_drivers_gpio_uart/src/memory.rs
@@ -6,6 +6,10 @@
use core::ops::Range;
+//--------------------------------------------------------------------------------------------------
+// Public Code
+//--------------------------------------------------------------------------------------------------
+
/// Zero out a memory region.
///
/// # Safety
diff --git a/06_drivers_gpio_uart/src/panic_wait.rs b/06_drivers_gpio_uart/src/panic_wait.rs
index 67cf1ee6..1386e1e2 100644
--- a/06_drivers_gpio_uart/src/panic_wait.rs
+++ b/06_drivers_gpio_uart/src/panic_wait.rs
@@ -4,13 +4,17 @@
//! A panic handler that infinitely waits.
-use crate::{arch, bsp};
+use crate::{bsp, cpu};
use core::{fmt, panic::PanicInfo};
+//--------------------------------------------------------------------------------------------------
+// Private Code
+//--------------------------------------------------------------------------------------------------
+
fn _panic_print(args: fmt::Arguments) {
use fmt::Write;
- unsafe { bsp::panic_console_out().write_fmt(args).unwrap() };
+ unsafe { bsp::console::panic_console_out().write_fmt(args).unwrap() };
}
/// Prints with a newline - only use from the panic handler.
@@ -31,5 +35,5 @@ fn panic(info: &PanicInfo) -> ! {
panic_println!("\nKernel panic!");
}
- arch::wait_forever()
+ cpu::wait_forever()
}
diff --git a/06_drivers_gpio_uart/src/print.rs b/06_drivers_gpio_uart/src/print.rs
index c84031ec..7763a0e9 100644
--- a/06_drivers_gpio_uart/src/print.rs
+++ b/06_drivers_gpio_uart/src/print.rs
@@ -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
diff --git a/06_drivers_gpio_uart/src/runtime_init.rs b/06_drivers_gpio_uart/src/runtime_init.rs
index 8132a20b..d517bd64 100644
--- a/06_drivers_gpio_uart/src/runtime_init.rs
+++ b/06_drivers_gpio_uart/src/runtime_init.rs
@@ -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.
///
diff --git a/06_drivers_gpio_uart/src/synchronization.rs b/06_drivers_gpio_uart/src/synchronization.rs
new file mode 100644
index 00000000..caa2794a
--- /dev/null
+++ b/06_drivers_gpio_uart/src/synchronization.rs
@@ -0,0 +1,91 @@
+// SPDX-License-Identifier: MIT OR Apache-2.0
+//
+// Copyright (c) 2020 Andre Richter
+
+//! 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> = 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(&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 {
+ data: UnsafeCell,
+}
+
+//--------------------------------------------------------------------------------------------------
+// Public Code
+//--------------------------------------------------------------------------------------------------
+
+unsafe impl Sync for NullLock {}
+
+impl NullLock {
+ /// Wraps `data` into a new `NullLock`.
+ pub const fn new(data: T) -> Self {
+ Self {
+ data: UnsafeCell::new(data),
+ }
+ }
+}
+
+//------------------------------------------------------------------------------
+// OS Interface Code
+//------------------------------------------------------------------------------
+
+impl interface::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.
+ let data = unsafe { &mut *self.data.get() };
+
+ f(data)
+ }
+}