@ -2,35 +2,81 @@
## tl;dr
## tl;dr
- Now that we enabled safe globals in the previous tutorial, the infrastructure is laid for adding
- Drivers for the real `UART` and the `GPIO` controller are added.
the first real device drivers.
- **For the first time, we will be able to run the code on the real hardware** (scroll down for
- We throw out the magic QEMU console and use a real `UART` now. Like serious embedded hackers do!
instructions).
## Notable additions
## Introduction
- For the first time, we will be able to run the code on the real hardware.
Now that we enabled safe globals in the previous tutorial, the infrastructure is laid for adding the
- Therefore, building is now differentiated between the **RPi 3** and the **RPi4** .
first real device drivers. We throw out the magic QEMU console and introduce a `driver manager` ,
- By default, all `Makefile` targets will build for the **RPi 3** .
which allows the `BSP` to register device drivers with the `kernel` .
- In order to build for the the **RPi4** , prepend `BSP=rpi4` to each target. For example:
- `BSP=rpi4 make`
## Driver Manager
- `BSP=rpi4 make doc`
- Unfortunately, QEMU does not yet support the **RPi4** , so `BSP=rpi4 make qemu` won't work.
The first step consists of adding a `driver subsystem` to the kernel. The corresponding code will
- A `driver::interface::DeviceDriver` trait is added for abstracting `BSP` driver implementations
live in `src/driver.rs` . The subsystem introduces `interface::DeviceDriver` , a common trait that
from kernel code.
every device driver will need to implement and that is known to the kernel. A global
- Drivers are stored in `src/bsp/device_driver` , and can be reused between `BSP` s.
`DRIVER_MANAGER` instance (of type `DriverManager` ) that is instantiated in the same file serves as
- We introduce the `GPIO` driver, which pinmuxes (that is, routing signals from inside the `SoC`
the central entity that can be called to manage all things device drivers in the kernel. For
to actual HW pins) the RPi's PL011 UART.
example, by using the globally accessible `crate::driver::driver_manager().register_driver(...)` ,
- Note how this driver differentiates between **RPi 3** and **RPi4** . Their HW is different,
any code can can register an object with static lifetime that implements the
so we have to account for it in SW.
`interface::DeviceDriver` trait.
- Most importantly, the `PL011Uart` driver: It implements the `console::interface::*` traits and
is from now on used as the main system console output.
During kernel init, a call to `crate::driver::driver_manager().init_drivers(...)` will let the
- `BSP` s now contain a memory map in `src/bsp/raspberrypi/memory.rs` . In the specific case, they
driver manager loop over all registered drivers and kick off their initialization, and also execute
contain the Raspberry's `MMIO` addresses which are used to instantiate the respective device
an optional `post-init callback` that can be registered alongside the driver. For example, this
drivers.
mechanism is used to switch over to the `UART` driver as the main system console after the `UART`
driver has been initialized.
## BSP Driver Implementation
In `src/bsp/raspberrypi/driver.rs` , the function `init()` takes care of registering the `UART` and
`GPIO` drivers. It is therefore important that during kernel init, the correct order of (i) first
initializing the BSP driver subsystem, and only then (ii) calling the `driver_manager()` is
followed, like the following excerpt from `main.rs` shows:
```rust
unsafe fn kernel_init() -> ! {
// Initialize the BSP driver subsystem.
if let Err(x) = bsp::driver::init() {
panic!("Error initializing BSP driver subsystem: {}", x);
}
// Initialize all device drivers.
driver::driver_manager().init_drivers();
// println! is usable from here on.
```
The drivers themselves are stored in `src/bsp/device_driver` , and can be reused between `BSP` s. The
first driver added in these tutorials is the `PL011Uart` driver: It implements the
`console::interface::*` traits and is from now on used as the main system console. The second driver
is the `GPIO` driver, which pinmuxes (that is, routing signals from inside the `SoC` to actual HW
pins) the RPi's PL011 UART accordingly. Note how the `GPIO` driver differentiates between **RPi3**
and **RPi4** . Their HW is different, so we have to account for it in SW.
The `BSP` s now also contain a memory map in `src/bsp/raspberrypi/memory.rs` . It provides the
Raspberry's `MMIO` addresses which are used by the `BSP` to instantiate the respective device
drivers, so that the driver code knows where to find the device's registers in memory.
## Boot it from SD card
## Boot it from SD card
Some steps for preparing the SD card differ between RPi3 and RPi4, so be careful.
Since we have real `UART` output now, we can run the code on the real hardware. Building is
differentiated between the **RPi 3** and the **RPi4** due to before mentioned differences in the
`GPIO` driver. By default, all `Makefile` targets will build for the **RPi 3** . In order to build
for the the **RPi4** , prepend `BSP=rpi4` to each target. For example:
```console
$ BSP=rpi4 make
$ BSP=rpi4 make doc
```
Unfortunately, QEMU does not yet support the **RPi4** , so `BSP=rpi4 make qemu` won't work.
**Some steps for preparing the SD card differ between RPi3 and RPi4, so be careful in the
following.**
### Common for both
### Common for both
@ -1072,7 +1118,7 @@ diff -uNr 04_safe_globals/src/bsp/raspberrypi/console.rs 05_drivers_gpio_uart/sr
diff -uNr 04_safe_globals/src/bsp/raspberrypi/driver.rs 05_drivers_gpio_uart/src/bsp/raspberrypi/driver.rs
diff -uNr 04_safe_globals/src/bsp/raspberrypi/driver.rs 05_drivers_gpio_uart/src/bsp/raspberrypi/driver.rs
--- 04_safe_globals/src/bsp/raspberrypi/driver.rs
--- 04_safe_globals/src/bsp/raspberrypi/driver.rs
+++ 05_drivers_gpio_uart/src/bsp/raspberrypi/driver.rs
+++ 05_drivers_gpio_uart/src/bsp/raspberrypi/driver.rs
@@ -0,0 +1,55 @@
@@ -0,0 +1,71 @@
+// SPDX-License-Identifier: MIT OR Apache-2.0
+// SPDX-License-Identifier: MIT OR Apache-2.0
+//
+//
+// Copyright (c) 2018-2022 Andre Richter < andre.o.richter @ gmail . com >
+// Copyright (c) 2018-2022 Andre Richter < andre.o.richter @ gmail . com >
@ -1080,53 +1126,69 @@ diff -uNr 04_safe_globals/src/bsp/raspberrypi/driver.rs 05_drivers_gpio_uart/src
+//! BSP driver support.
+//! BSP driver support.
+
+
+use super::memory::map::mmio;
+use super::memory::map::mmio;
+use crate::{bsp::device_driver, driver};
+use crate::{bsp::device_driver, console, driver as generic_driver};
+use core::sync::atomic::{AtomicBool, Ordering};
+
+
+//--------------------------------------------------------------------------------------------------
+//--------------------------------------------------------------------------------------------------
+// Private Definition s
+// Global instance s
+//--------------------------------------------------------------------------------------------------
+//--------------------------------------------------------------------------------------------------
+
+
+/// Device Driver Manager type.
+static PL011_UART: device_driver::PL011Uart =
+struct BSPDriverManager {
+ unsafe { device_driver::PL011Uart::new(mmio::PL011_UART_START) };
+ device_drivers: [& 'static (dyn DeviceDriver + Sync); 2],
+static GPIO: device_driver::GPIO = unsafe { device_driver::GPIO::new(mmio::GPIO_START) };
+}
+
+
+//--------------------------------------------------------------------------------------------------
+//--------------------------------------------------------------------------------------------------
+// Global instances
+// Private Code
+//--------------------------------------------------------------------------------------------------
+//--------------------------------------------------------------------------------------------------
+
+
+pub(super) static PL011_UART: device_driver::PL011Uart =
+/// This must be called only after successful init of the UART driver.
+ unsafe { device_driver::PL011Uart::new(mmio::PL011_UART_START) };
+fn post_init_uart() -> Result< (), & 'static str> {
+ console::register_console(&PL011_UART);
+
+
+static GPIO: device_driver::GPIO = unsafe { device_driver::GPIO::new(mmio::GPIO_START) };
+ Ok(())
+}
+
+
+static BSP_DRIVER_MANAGER: BSPDriverManager = BSPDriverManager {
+/// This must be called only after successful init of the GPIO driver.
+ device_drivers: [& PL011_UART, & GPIO],
+fn post_init_gpio() -> Result< (), & 'static str> {
+};
+ GPIO.map_pl011_uart();
+ Ok(())
+}
+
+
+//--------------------------------------------------------------------------------------------------
+fn driver_uart() -> Result< (), & 'static str> {
+// Public Code
+ let uart_descriptor =
+//--------------------------------------------------------------------------------------------------
+ generic_driver::DeviceDriverDescriptor::new(& PL011_UART, Some(post_init_uart));
+ generic_driver::driver_manager().register_driver(uart_descriptor);
+
+
+/// Return a reference to the driver manager.
+ Ok(())
+pub fn driver_manager() -> & 'static impl driver::interface::DriverManager {
+ & BSP_DRIVER_MANAGER
+}
+}
+
+
+//------------------------------------------------------------------------------
+fn driver_gpio() -> Result< (), & 'static str> {
+// OS Interface Code
+ let gpio_descriptor = generic_driver::DeviceDriverDescriptor::new(& GPIO, Some(post_init_gpio));
+//------------------------------------------------------------------------------
+ generic_driver::driver_manager().register_driver(gpio_descriptor);
+use driver::interface::DeviceDriver;
+
+
+impl driver::interface::DriverManager for BSPDriverManager {
+ Ok(())
+ fn all_device_drivers(& self) -> & [& 'static (dyn DeviceDriver + Sync)] {
+ & self.device_drivers[..]
+}
+}
+
+
+ fn post_device_driver_init(& self) {
+//--------------------------------------------------------------------------------------------------
+ // Configure PL011Uart's output pins.
+// Public Code
+ GPIO.map_pl011_uart();
+//--------------------------------------------------------------------------------------------------
+
+/// Initialize the driver subsystem.
+///
+/// # Safety
+///
+/// See child function calls.
+pub unsafe fn init() -> Result< (), & 'static str> {
+ static INIT_DONE: AtomicBool = AtomicBool::new(false);
+ if INIT_DONE.load(Ordering::Relaxed) {
+ return Err("Init already done");
+ }
+ }
+
+ driver_uart()?;
+ driver_gpio()?;
+
+ INIT_DONE.store(true, Ordering::Relaxed);
+ Ok(())
+}
+}
diff -uNr 04_safe_globals/src/bsp/raspberrypi/memory.rs 05_drivers_gpio_uart/src/bsp/raspberrypi/memory.rs
diff -uNr 04_safe_globals/src/bsp/raspberrypi/memory.rs 05_drivers_gpio_uart/src/bsp/raspberrypi/memory.rs
@ -1174,9 +1236,11 @@ diff -uNr 04_safe_globals/src/bsp/raspberrypi/memory.rs 05_drivers_gpio_uart/src
diff -uNr 04_safe_globals/src/bsp/raspberrypi.rs 05_drivers_gpio_uart/src/bsp/raspberrypi.rs
diff -uNr 04_safe_globals/src/bsp/raspberrypi.rs 05_drivers_gpio_uart/src/bsp/raspberrypi.rs
--- 04_safe_globals/src/bsp/raspberrypi.rs
--- 04_safe_globals/src/bsp/raspberrypi.rs
+++ 05_drivers_gpio_uart/src/bsp/raspberrypi.rs
+++ 05_drivers_gpio_uart/src/bsp/raspberrypi.rs
@@ -6,3 +6,22 @@
@@ -4,5 +4,23 @@
//! Top-level BSP file for the Raspberry Pi 3 and 4.
pub mod console;
- pub mod console;
pub mod cpu;
pub mod cpu;
+pub mod driver;
+pub mod driver;
+pub mod memory;
+pub mod memory;
@ -1211,10 +1275,67 @@ diff -uNr 04_safe_globals/src/bsp.rs 05_drivers_gpio_uart/src/bsp.rs
mod raspberrypi;
mod raspberrypi;
diff -uNr 04_safe_globals/src/console/null_console.rs 05_drivers_gpio_uart/src/console/null_console.rs
--- 04_safe_globals/src/console/null_console.rs
+++ 05_drivers_gpio_uart/src/console/null_console.rs
@@ -0,0 +1,41 @@
+// SPDX-License-Identifier: MIT OR Apache-2.0
+//
+// Copyright (c) 2022 Andre Richter < andre.o.richter @ gmail . com >
+
+//! Null console.
+
+use super::interface;
+use core::fmt;
+
+//--------------------------------------------------------------------------------------------------
+// Public Definitions
+//--------------------------------------------------------------------------------------------------
+
+pub struct NullConsole;
+
+//--------------------------------------------------------------------------------------------------
+// Global instances
+//--------------------------------------------------------------------------------------------------
+
+pub static NULL_CONSOLE: NullConsole = NullConsole {};
+
+//--------------------------------------------------------------------------------------------------
+// Public Code
+//--------------------------------------------------------------------------------------------------
+
+impl interface::Write for NullConsole {
+ fn write_char(& self, _c: char) {}
+
+ fn write_fmt(& self, _args: fmt::Arguments) -> fmt::Result {
+ fmt::Result::Ok(())
+ }
+
+ fn flush(& self) {}
+}
+
+impl interface::Read for NullConsole {
+ fn clear_rx(& self) {}
+}
+
+impl interface::Statistics for NullConsole {}
+impl interface::All for NullConsole {}
diff -uNr 04_safe_globals/src/console.rs 05_drivers_gpio_uart/src/console.rs
diff -uNr 04_safe_globals/src/console.rs 05_drivers_gpio_uart/src/console.rs
--- 04_safe_globals/src/console.rs
--- 04_safe_globals/src/console.rs
+++ 05_drivers_gpio_uart/src/console.rs
+++ 05_drivers_gpio_uart/src/console.rs
@@ -16,8 +16,25 @@
@@ -4,7 +4,9 @@
//! System console.
-use crate::bsp;
+mod null_console;
+
+use crate::synchronization::{self, NullLock};
//--------------------------------------------------------------------------------------------------
// Public Definitions
@@ -16,8 +18,25 @@
/// Console write functions.
/// Console write functions.
pub trait Write {
pub trait Write {
@ -1240,7 +1361,7 @@ diff -uNr 04_safe_globals/src/console.rs 05_drivers_gpio_uart/src/console.rs
}
}
/// Console statistics.
/// Console statistics.
@@ -26,10 +43,15 @@
@@ -26,19 +45,37 @@
fn chars_written(& self) -> usize {
fn chars_written(& self) -> usize {
0
0
}
}
@ -1257,6 +1378,30 @@ diff -uNr 04_safe_globals/src/console.rs 05_drivers_gpio_uart/src/console.rs
}
}
//--------------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------
+// Global instances
+//--------------------------------------------------------------------------------------------------
+
+static CUR_CONSOLE: NullLock< & 'static (dyn interface::All + Sync)> =
+ NullLock::new(&null_console::NULL_CONSOLE);
+
+//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
+use synchronization::interface::Mutex;
+
+/// Register a new console.
+pub fn register_console(new_console: & 'static (dyn interface::All + Sync)) {
+ CUR_CONSOLE.lock(|con| *con = new_console);
+}
-/// Return a reference to the console.
+/// Return a reference to the currently registered console.
///
/// This is the global console used by all printing macros.
pub fn console() -> & 'static dyn interface::All {
- bsp::console::console()
+ CUR_CONSOLE.lock(|con| *con)
}
diff -uNr 04_safe_globals/src/cpu.rs 05_drivers_gpio_uart/src/cpu.rs
diff -uNr 04_safe_globals/src/cpu.rs 05_drivers_gpio_uart/src/cpu.rs
--- 04_safe_globals/src/cpu.rs
--- 04_safe_globals/src/cpu.rs
@ -1274,13 +1419,29 @@ diff -uNr 04_safe_globals/src/cpu.rs 05_drivers_gpio_uart/src/cpu.rs
diff -uNr 04_safe_globals/src/driver.rs 05_drivers_gpio_uart/src/driver.rs
diff -uNr 04_safe_globals/src/driver.rs 05_drivers_gpio_uart/src/driver.rs
--- 04_safe_globals/src/driver.rs
--- 04_safe_globals/src/driver.rs
+++ 05_drivers_gpio_uart/src/driver.rs
+++ 05_drivers_gpio_uart/src/driver.rs
@@ -0,0 +1,44 @@
@@ -0,0 +1,167 @@
+// SPDX-License-Identifier: MIT OR Apache-2.0
+// SPDX-License-Identifier: MIT OR Apache-2.0
+//
+//
+// Copyright (c) 2018-2022 Andre Richter < andre.o.richter @ gmail . com >
+// Copyright (c) 2018-2022 Andre Richter < andre.o.richter @ gmail . com >
+
+
+//! Driver support.
+//! Driver support.
+
+
+use crate::{
+ println,
+ synchronization::{interface::Mutex, NullLock},
+};
+
+//--------------------------------------------------------------------------------------------------
+// Private Definitions
+//--------------------------------------------------------------------------------------------------
+
+const NUM_DRIVERS: usize = 5;
+
+struct DriverManagerInner {
+ next_index: usize,
+ descriptors: [Option< DeviceDriverDescriptor > ; NUM_DRIVERS],
+}
+
+//--------------------------------------------------------------------------------------------------
+//--------------------------------------------------------------------------------------------------
+// Public Definitions
+// Public Definitions
+//--------------------------------------------------------------------------------------------------
+//--------------------------------------------------------------------------------------------------
@ -1301,22 +1462,129 @@ diff -uNr 04_safe_globals/src/driver.rs 05_drivers_gpio_uart/src/driver.rs
+ Ok(())
+ Ok(())
+ }
+ }
+ }
+ }
+}
+
+
+ /// Device driver management functions.
+/// Tpye to be used as an optional callback after a driver's init() has run.
+ ///
+pub type DeviceDriverPostInitCallback = unsafe fn() -> Result< (), & 'static str>;
+ /// The `BSP` is supposed to supply one global instance.
+
+ pub trait DriverManager {
+/// A descriptor for device drivers.
+ /// Return a slice of references to all `BSP` -instantiated drivers.
+#[derive(Copy, Clone)]
+pub struct DeviceDriverDescriptor {
+ device_driver: & 'static (dyn interface::DeviceDriver + Sync),
+ post_init_callback: Option< DeviceDriverPostInitCallback > ,
+}
+
+/// Provides device driver management functions.
+pub struct DriverManager {
+ inner: NullLock< DriverManagerInner > ,
+}
+
+//--------------------------------------------------------------------------------------------------
+// Global instances
+//--------------------------------------------------------------------------------------------------
+
+static DRIVER_MANAGER: DriverManager = DriverManager::new();
+
+//--------------------------------------------------------------------------------------------------
+// Private Code
+//--------------------------------------------------------------------------------------------------
+
+impl DriverManagerInner {
+ /// Create an instance.
+ pub const fn new() -> Self {
+ Self {
+ next_index: 0,
+ descriptors: [None; NUM_DRIVERS],
+ }
+ }
+}
+
+//--------------------------------------------------------------------------------------------------
+// Public Code
+//--------------------------------------------------------------------------------------------------
+
+impl DeviceDriverDescriptor {
+ /// Create an instance.
+ pub fn new(
+ device_driver: & 'static (dyn interface::DeviceDriver + Sync),
+ post_init_callback: Option< DeviceDriverPostInitCallback > ,
+ ) -> Self {
+ Self {
+ device_driver,
+ post_init_callback,
+ }
+ }
+}
+
+/// Return a reference to the global DriverManager.
+pub fn driver_manager() -> & 'static DriverManager {
+ & DRIVER_MANAGER
+}
+
+impl DriverManager {
+ /// Create an instance.
+ pub const fn new() -> Self {
+ Self {
+ inner: NullLock::new(DriverManagerInner::new()),
+ }
+ }
+
+ /// Register a device driver with the kernel.
+ pub fn register_driver(& self, descriptor: DeviceDriverDescriptor) {
+ self.inner.lock(|inner| {
+ inner.descriptors[inner.next_index] = Some(descriptor);
+ inner.next_index += 1;
+ })
+ }
+
+ /// Helper for iterating over registered drivers.
+ fn for_each_descriptor< 'a>(& 'a self, f: impl FnMut(& 'a DeviceDriverDescriptor)) {
+ self.inner.lock(|inner| {
+ inner
+ .descriptors
+ .iter()
+ .filter_map(|x| x.as_ref())
+ .for_each(f)
+ })
+ }
+
+ /// Fully initialize all drivers.
+ ///
+ ///
+ /// # Safety
+ /// # Safety
+ ///
+ ///
+ /// - The order of devices is the order in which `DeviceDriver::init()` is called.
+ /// - During init, drivers might do stuff with system-wide impact.
+ fn all_device_drivers(& self) -> & [& 'static (dyn DeviceDriver + Sync)];
+ pub unsafe fn init_drivers(& self) {
+ self.for_each_descriptor(|descriptor| {
+ // 1. Initialize driver.
+ if let Err(x) = descriptor.device_driver.init() {
+ panic!(
+ "Error initializing driver: {}: {}",
+ descriptor.device_driver.compatible(),
+ x
+ );
+ }
+
+
+ /// Initialization code that runs after driver init.
+ // 2. Call corresponding post init callback.
+ ///
+ if let Some(callback) = & descriptor.post_init_callback {
+ /// For example, device driver code that depends on other drivers already being online.
+ if let Err(x) = callback() {
+ fn post_device_driver_init(&self);
+ panic!(
+ "Error during driver post-init callback: {}: {}",
+ descriptor.device_driver.compatible(),
+ x
+ );
+ }
+ }
+ });
+ }
+
+ /// Enumerate all registered device drivers.
+ pub fn enumerate(& self) {
+ let mut i: usize = 1;
+ self.for_each_descriptor(|descriptor| {
+ println!(" {}. {}", i, descriptor.device_driver.compatible());
+
+ i += 1;
+ });
+ }
+ }
+}
+}
@ -1339,21 +1607,20 @@ diff -uNr 04_safe_globals/src/main.rs 05_drivers_gpio_uart/src/main.rs
mod panic_wait;
mod panic_wait;
mod print;
mod print;
mod synchronization;
mod synchronization;
@@ -125,13 +127,50 @@
@@ -125,13 +127,42 @@
/// # Safety
/// # Safety
///
///
/// - Only a single core must be active and running this function.
/// - Only a single core must be active and running this function.
+/// - The init calls in this function must appear in the correct order.
+/// - The init calls in this function must appear in the correct order.
unsafe fn kernel_init() -> ! {
unsafe fn kernel_init() -> ! {
- use console::console;
- use console::console;
+ use driver::interface::DriverManager;
+ // Initialize the BSP driver subsystem.
+
+ if let Err(x) = bsp::driver::init() {
+ for i in bsp::driver::driver_manager().all_device_drivers().iter() {
+ panic!("Error initializing BSP driver subsystem: {}", x);
+ if let Err(x) = i.init() {
+ panic!("Error loading driver: {}: {}", i.compatible(), x);
+ }
+ }
+ }
+
+ bsp::driver::driver_manager().post_device_driver_init();
+ // Initialize all device drivers.
+ driver::driver_manager().init_drivers();
+ // println! is usable from here on.
+ // println! is usable from here on.
- println!("[0] Hello from Rust!");
- println!("[0] Hello from Rust!");
@ -1365,7 +1632,6 @@ diff -uNr 04_safe_globals/src/main.rs 05_drivers_gpio_uart/src/main.rs
+/// The main function running after the early init.
+/// The main function running after the early init.
+fn kernel_main() -> ! {
+fn kernel_main() -> ! {
+ use console::console;
+ use console::console;
+ use driver::interface::DriverManager;
- println!("[2] Stopping here.");
- println!("[2] Stopping here.");
- cpu::wait_forever()
- cpu::wait_forever()
@ -1377,13 +1643,7 @@ diff -uNr 04_safe_globals/src/main.rs 05_drivers_gpio_uart/src/main.rs
+ println!("[1] Booting on: {}", bsp::board_name());
+ println!("[1] Booting on: {}", bsp::board_name());
+
+
+ println!("[2] Drivers loaded:");
+ println!("[2] Drivers loaded:");
+ for (i, driver) in bsp::driver::driver_manager()
+ driver::driver_manager().enumerate();
+ .all_device_drivers()
+ .iter()
+ .enumerate()
+ {
+ println!(" {}. {}", i + 1, driver.compatible());
+ }
+
+
+ println!("[3] Chars written: {}", console().chars_written());
+ println!("[3] Chars written: {}", console().chars_written());
+ println!("[4] Echoing input now");
+ println!("[4] Echoing input now");