Rework driver subsystem

This update significantly decouples the generic kernel code
from the BSP code.
Prior to this patch, the BSP had way too much business logic
that should have always been the generic kernel's concern.
pull/175/head
Andre Richter 2 years ago
parent 7014c0cdfd
commit 2e72a8408f
No known key found for this signature in database
GPG Key ID: 2116C1AB102F615E

@ -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 Definitions +// Global instances
+//-------------------------------------------------------------------------------------------------- +//--------------------------------------------------------------------------------------------------
+ +
+/// 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(())
+ } + }
+ } + }
+}
+
+/// Tpye to be used as an optional callback after a driver's init() has run.
+pub type DeviceDriverPostInitCallback = unsafe fn() -> Result<(), &'static str>;
+
+/// A descriptor for device 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)
+ })
+ }
+ +
+ /// Device driver management functions. + /// Fully initialize all drivers.
+ /// + ///
+ /// The `BSP` is supposed to supply one global instance. + /// # Safety
+ pub trait DriverManager { + ///
+ /// Return a slice of references to all `BSP`-instantiated drivers. + /// - During init, drivers might do stuff with system-wide impact.
+ /// + pub unsafe fn init_drivers(&self) {
+ /// # Safety + self.for_each_descriptor(|descriptor| {
+ /// + // 1. Initialize driver.
+ /// - The order of devices is the order in which `DeviceDriver::init()` is called. + if let Err(x) = descriptor.device_driver.init() {
+ fn all_device_drivers(&self) -> &[&'static (dyn DeviceDriver + Sync)]; + 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");

@ -4,7 +4,6 @@
//! Top-level BSP file for the Raspberry Pi 3 and 4. //! Top-level BSP file for the Raspberry Pi 3 and 4.
pub mod console;
pub mod cpu; pub mod cpu;
pub mod driver; pub mod driver;
pub mod memory; pub mod memory;

@ -5,51 +5,67 @@
//! 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 Definitions // Global instances
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
/// 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[..]
} //--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
fn post_device_driver_init(&self) { /// Initialize the driver subsystem.
// Configure PL011Uart's output pins. ///
GPIO.map_pl011_uart(); /// # 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(())
} }

@ -4,7 +4,9 @@
//! System console. //! System console.
use crate::bsp; mod null_console;
use crate::synchronization::{self, NullLock};
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
// Public Definitions // Public Definitions
@ -54,13 +56,26 @@ pub mod interface {
pub trait All: Write + Read + Statistics {} pub trait All: Write + Read + Statistics {}
} }
//--------------------------------------------------------------------------------------------------
// Global instances
//--------------------------------------------------------------------------------------------------
static CUR_CONSOLE: NullLock<&'static (dyn interface::All + Sync)> =
NullLock::new(&null_console::NULL_CONSOLE);
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
// Public Code // 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. /// This is the global console used by all printing macros.
pub fn console() -> &'static dyn interface::All { pub fn console() -> &'static dyn interface::All {
bsp::console::console() CUR_CONSOLE.lock(|con| *con)
} }

@ -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 {}

@ -4,6 +4,22 @@
//! 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
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
@ -24,21 +40,128 @@ pub mod interface {
Ok(()) Ok(())
} }
} }
}
/// Tpye to be used as an optional callback after a driver's init() has run.
pub type DeviceDriverPostInitCallback = unsafe fn() -> Result<(), &'static str>;
/// A descriptor for device 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()),
}
}
/// Device driver management functions. /// 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.
/// ///
/// The `BSP` is supposed to supply one global instance. /// # Safety
pub trait DriverManager { ///
/// Return a slice of references to all `BSP`-instantiated drivers. /// - During init, drivers might do stuff with system-wide impact.
/// pub unsafe fn init_drivers(&self) {
/// # Safety self.for_each_descriptor(|descriptor| {
/// // 1. Initialize driver.
/// - The order of devices is the order in which `DeviceDriver::init()` is called. if let Err(x) = descriptor.device_driver.init() {
fn all_device_drivers(&self) -> &[&'static (dyn DeviceDriver + Sync)]; 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;
});
} }
} }

@ -129,14 +129,13 @@ mod synchronization;
/// - 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 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.
// Transition from unsafe to safe. // Transition from unsafe to safe.
@ -146,7 +145,6 @@ unsafe fn kernel_init() -> ! {
/// 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!( println!(
"[0] {} version {}", "[0] {} version {}",
@ -156,13 +154,7 @@ fn kernel_main() -> ! {
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");

@ -369,6 +369,27 @@ diff -uNr 05_drivers_gpio_uart/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs 0
{} {}
} }
diff -uNr 05_drivers_gpio_uart/src/bsp/raspberrypi/console.rs 06_uart_chainloader/src/bsp/raspberrypi/console.rs
--- 05_drivers_gpio_uart/src/bsp/raspberrypi/console.rs
+++ 06_uart_chainloader/src/bsp/raspberrypi/console.rs
@@ -1,16 +0,0 @@
-// SPDX-License-Identifier: MIT OR Apache-2.0
-//
-// Copyright (c) 2018-2022 Andre Richter <andre.o.richter@gmail.com>
-
-//! BSP console facilities.
-
-use crate::console;
-
-//--------------------------------------------------------------------------------------------------
-// Public Code
-//--------------------------------------------------------------------------------------------------
-
-/// Return a reference to the console.
-pub fn console() -> &'static dyn console::interface::All {
- &super::driver::PL011_UART
-}
diff -uNr 05_drivers_gpio_uart/src/bsp/raspberrypi/kernel.ld 06_uart_chainloader/src/bsp/raspberrypi/kernel.ld diff -uNr 05_drivers_gpio_uart/src/bsp/raspberrypi/kernel.ld 06_uart_chainloader/src/bsp/raspberrypi/kernel.ld
--- 05_drivers_gpio_uart/src/bsp/raspberrypi/kernel.ld --- 05_drivers_gpio_uart/src/bsp/raspberrypi/kernel.ld
+++ 06_uart_chainloader/src/bsp/raspberrypi/kernel.ld +++ 06_uart_chainloader/src/bsp/raspberrypi/kernel.ld
@ -437,10 +458,41 @@ diff -uNr 05_drivers_gpio_uart/src/bsp/raspberrypi/memory.rs 06_uart_chainloader
+ map::BOARD_DEFAULT_LOAD_ADDRESS as _ + map::BOARD_DEFAULT_LOAD_ADDRESS as _
+} +}
diff -uNr 05_drivers_gpio_uart/src/driver.rs 06_uart_chainloader/src/driver.rs
--- 05_drivers_gpio_uart/src/driver.rs
+++ 06_uart_chainloader/src/driver.rs
@@ -4,10 +4,7 @@
//! Driver support.
-use crate::{
- println,
- synchronization::{interface::Mutex, NullLock},
-};
+use crate::synchronization::{interface::Mutex, NullLock};
//--------------------------------------------------------------------------------------------------
// Private Definitions
@@ -154,14 +151,4 @@
}
});
}
-
- /// 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;
- });
- }
}
diff -uNr 05_drivers_gpio_uart/src/main.rs 06_uart_chainloader/src/main.rs diff -uNr 05_drivers_gpio_uart/src/main.rs 06_uart_chainloader/src/main.rs
--- 05_drivers_gpio_uart/src/main.rs --- 05_drivers_gpio_uart/src/main.rs
+++ 06_uart_chainloader/src/main.rs +++ 06_uart_chainloader/src/main.rs
@@ -143,34 +143,55 @@ @@ -142,27 +142,55 @@
kernel_main() kernel_main()
} }
@ -454,7 +506,6 @@ diff -uNr 05_drivers_gpio_uart/src/main.rs 06_uart_chainloader/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!( - println!(
- "[0] {} version {}", - "[0] {} version {}",
@ -462,41 +513,35 @@ diff -uNr 05_drivers_gpio_uart/src/main.rs 06_uart_chainloader/src/main.rs
- env!("CARGO_PKG_VERSION") - env!("CARGO_PKG_VERSION")
- ); - );
- println!("[1] Booting on: {}", bsp::board_name()); - println!("[1] Booting on: {}", bsp::board_name());
-
- println!("[2] Drivers loaded:");
- for (i, driver) in bsp::driver::driver_manager()
- .all_device_drivers()
- .iter()
- .enumerate()
- {
- println!(" {}. {}", i + 1, driver.compatible());
+ println!("{}", MINILOAD_LOGO); + println!("{}", MINILOAD_LOGO);
+ println!("{:^37}", bsp::board_name()); + println!("{:^37}", bsp::board_name());
+ println!(); + println!();
+ println!("[ML] Requesting binary"); + println!("[ML] Requesting binary");
+ console().flush(); + console().flush();
+
- println!("[2] Drivers loaded:");
- driver::driver_manager().enumerate();
+ // Discard any spurious received characters before starting with the loader protocol. + // Discard any spurious received characters before starting with the loader protocol.
+ console().clear_rx(); + console().clear_rx();
+
+ // Notify `Minipush` to send the binary.
+ for _ in 0..3 {
+ console().write_char(3 as char);
}
- println!("[3] Chars written: {}", console().chars_written()); - println!("[3] Chars written: {}", console().chars_written());
- println!("[4] Echoing input now"); - println!("[4] Echoing input now");
+ // Read the binary's size. + // Notify `Minipush` to send the binary.
+ let mut size: u32 = u32::from(console().read_char() as u8); + for _ in 0..3 {
+ size |= u32::from(console().read_char() as u8) << 8; + console().write_char(3 as char);
+ size |= u32::from(console().read_char() as u8) << 16; + }
+ size |= u32::from(console().read_char() as u8) << 24;
- // Discard any spurious received characters before going into echo mode. - // Discard any spurious received characters before going into echo mode.
- console().clear_rx(); - console().clear_rx();
- loop { - loop {
- let c = console().read_char(); - let c = console().read_char();
- console().write_char(c); - console().write_char(c);
+ // Read the binary's size.
+ let mut size: u32 = u32::from(console().read_char() as u8);
+ size |= u32::from(console().read_char() as u8) << 8;
+ size |= u32::from(console().read_char() as u8) << 16;
+ size |= u32::from(console().read_char() as u8) << 24;
+
+ // Trust it's not too big. + // Trust it's not too big.
+ console().write_char('O'); + console().write_char('O');
+ console().write_char('K'); + console().write_char('K');

@ -4,7 +4,6 @@
//! Top-level BSP file for the Raspberry Pi 3 and 4. //! Top-level BSP file for the Raspberry Pi 3 and 4.
pub mod console;
pub mod cpu; pub mod cpu;
pub mod driver; pub mod driver;
pub mod memory; pub mod memory;

@ -1,16 +0,0 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2018-2022 Andre Richter <andre.o.richter@gmail.com>
//! BSP console facilities.
use crate::console;
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
/// Return a reference to the console.
pub fn console() -> &'static dyn console::interface::All {
&super::driver::PL011_UART
}

@ -5,51 +5,67 @@
//! 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 Definitions // Global instances
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
/// 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[..]
} //--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
fn post_device_driver_init(&self) { /// Initialize the driver subsystem.
// Configure PL011Uart's output pins. ///
GPIO.map_pl011_uart(); /// # 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(())
} }

@ -4,7 +4,9 @@
//! System console. //! System console.
use crate::bsp; mod null_console;
use crate::synchronization::{self, NullLock};
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
// Public Definitions // Public Definitions
@ -54,13 +56,26 @@ pub mod interface {
pub trait All: Write + Read + Statistics {} pub trait All: Write + Read + Statistics {}
} }
//--------------------------------------------------------------------------------------------------
// Global instances
//--------------------------------------------------------------------------------------------------
static CUR_CONSOLE: NullLock<&'static (dyn interface::All + Sync)> =
NullLock::new(&null_console::NULL_CONSOLE);
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
// Public Code // 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. /// This is the global console used by all printing macros.
pub fn console() -> &'static dyn interface::All { pub fn console() -> &'static dyn interface::All {
bsp::console::console() CUR_CONSOLE.lock(|con| *con)
} }

@ -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 {}

@ -4,6 +4,19 @@
//! Driver support. //! Driver support.
use crate::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
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
@ -24,21 +37,118 @@ pub mod interface {
Ok(()) Ok(())
} }
} }
}
/// Tpye to be used as an optional callback after a driver's init() has run.
pub type DeviceDriverPostInitCallback = unsafe fn() -> Result<(), &'static str>;
/// Device driver management functions. /// A descriptor for device 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.
/// ///
/// The `BSP` is supposed to supply one global instance. /// # Safety
pub trait DriverManager { ///
/// Return a slice of references to all `BSP`-instantiated drivers. /// - During init, drivers might do stuff with system-wide impact.
/// pub unsafe fn init_drivers(&self) {
/// # Safety self.for_each_descriptor(|descriptor| {
/// // 1. Initialize driver.
/// - The order of devices is the order in which `DeviceDriver::init()` is called. if let Err(x) = descriptor.device_driver.init() {
fn all_device_drivers(&self) -> &[&'static (dyn DeviceDriver + Sync)]; 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
);
}
}
});
} }
} }

@ -129,14 +129,13 @@ mod synchronization;
/// - 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 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.
// Transition from unsafe to safe. // Transition from unsafe to safe.

@ -501,6 +501,28 @@ diff -uNr 06_uart_chainloader/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs 07
{} {}
} }
diff -uNr 06_uart_chainloader/src/bsp/raspberrypi/driver.rs 07_timestamps/src/bsp/raspberrypi/driver.rs
--- 06_uart_chainloader/src/bsp/raspberrypi/driver.rs
+++ 07_timestamps/src/bsp/raspberrypi/driver.rs
@@ -57,6 +57,17 @@
/// # Safety
///
/// See child function calls.
+///
+/// # Note
+///
+/// Using atomics here relieves us from needing to use `unsafe` for the static variable.
+///
+/// On `AArch64`, which is the only implemented architecture at the time of writing this,
+/// [`AtomicBool::load`] and [`AtomicBool::store`] are lowered to ordinary load and store
+/// instructions. They are therefore safe to use even with MMU + caching deactivated.
+///
+/// [`AtomicBool::load`]: core::sync::atomic::AtomicBool::load
+/// [`AtomicBool::store`]: core::sync::atomic::AtomicBool::store
pub unsafe fn init() -> Result<(), &'static str> {
static INIT_DONE: AtomicBool = AtomicBool::new(false);
if INIT_DONE.load(Ordering::Relaxed) {
diff -uNr 06_uart_chainloader/src/bsp/raspberrypi/kernel.ld 07_timestamps/src/bsp/raspberrypi/kernel.ld diff -uNr 06_uart_chainloader/src/bsp/raspberrypi/kernel.ld 07_timestamps/src/bsp/raspberrypi/kernel.ld
--- 06_uart_chainloader/src/bsp/raspberrypi/kernel.ld --- 06_uart_chainloader/src/bsp/raspberrypi/kernel.ld
+++ 07_timestamps/src/bsp/raspberrypi/kernel.ld +++ 07_timestamps/src/bsp/raspberrypi/kernel.ld
@ -580,6 +602,37 @@ diff -uNr 06_uart_chainloader/src/cpu.rs 07_timestamps/src/cpu.rs
-#[cfg(feature = "bsp_rpi3")] -#[cfg(feature = "bsp_rpi3")]
-pub use arch_cpu::spin_for_cycles; -pub use arch_cpu::spin_for_cycles;
diff -uNr 06_uart_chainloader/src/driver.rs 07_timestamps/src/driver.rs
--- 06_uart_chainloader/src/driver.rs
+++ 07_timestamps/src/driver.rs
@@ -4,7 +4,10 @@
//! Driver support.
-use crate::synchronization::{interface::Mutex, NullLock};
+use crate::{
+ info,
+ synchronization::{interface::Mutex, NullLock},
+};
//--------------------------------------------------------------------------------------------------
// Private Definitions
@@ -151,4 +154,14 @@
}
});
}
+
+ /// Enumerate all registered device drivers.
+ pub fn enumerate(&self) {
+ let mut i: usize = 1;
+ self.for_each_descriptor(|descriptor| {
+ info!(" {}. {}", i, descriptor.device_driver.compatible());
+
+ i += 1;
+ });
+ }
}
diff -uNr 06_uart_chainloader/src/main.rs 07_timestamps/src/main.rs diff -uNr 06_uart_chainloader/src/main.rs 07_timestamps/src/main.rs
--- 06_uart_chainloader/src/main.rs --- 06_uart_chainloader/src/main.rs
+++ 07_timestamps/src/main.rs +++ 07_timestamps/src/main.rs
@ -604,7 +657,7 @@ diff -uNr 06_uart_chainloader/src/main.rs 07_timestamps/src/main.rs
/// Early init code. /// Early init code.
/// ///
@@ -143,55 +147,37 @@ @@ -142,55 +146,30 @@
kernel_main() kernel_main()
} }
@ -618,9 +671,7 @@ diff -uNr 06_uart_chainloader/src/main.rs 07_timestamps/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 core::time::Duration; -
+ use driver::interface::DriverManager;
- println!("{}", MINILOAD_LOGO); - println!("{}", MINILOAD_LOGO);
- println!("{:^37}", bsp::board_name()); - println!("{:^37}", bsp::board_name());
- println!(); - println!();
@ -633,26 +684,8 @@ diff -uNr 06_uart_chainloader/src/main.rs 07_timestamps/src/main.rs
- // Notify `Minipush` to send the binary. - // Notify `Minipush` to send the binary.
- for _ in 0..3 { - for _ in 0..3 {
- console().write_char(3 as char); - console().write_char(3 as char);
+ info!( - }
+ "{} version {}", + use core::time::Duration;
+ env!("CARGO_PKG_NAME"),
+ env!("CARGO_PKG_VERSION")
+ );
+ info!("Booting on: {}", bsp::board_name());
+
+ info!(
+ "Architectural timer resolution: {} ns",
+ time::time_manager().resolution().as_nanos()
+ );
+
+ info!("Drivers loaded:");
+ for (i, driver) in bsp::driver::driver_manager()
+ .all_device_drivers()
+ .iter()
+ .enumerate()
+ {
+ info!(" {}. {}", i + 1, driver.compatible());
}
- // Read the binary's size. - // Read the binary's size.
- let mut size: u32 = u32::from(console().read_char() as u8); - let mut size: u32 = u32::from(console().read_char() as u8);
@ -670,10 +703,29 @@ diff -uNr 06_uart_chainloader/src/main.rs 07_timestamps/src/main.rs
- for i in 0..size { - for i in 0..size {
- core::ptr::write_volatile(kernel_addr.offset(i as isize), console().read_char() as u8) - core::ptr::write_volatile(kernel_addr.offset(i as isize), console().read_char() as u8)
- } - }
- } + info!(
+ "{} version {}",
+ env!("CARGO_PKG_NAME"),
+ env!("CARGO_PKG_VERSION")
+ );
+ info!("Booting on: {}", bsp::board_name());
+
+ info!(
+ "Architectural timer resolution: {} ns",
+ time::time_manager().resolution().as_nanos()
+ );
+
+ info!("Drivers loaded:");
+ driver::driver_manager().enumerate();
+
+ // Test a failing timer case. + // Test a failing timer case.
+ time::time_manager().spin_for(Duration::from_nanos(1)); + time::time_manager().spin_for(Duration::from_nanos(1));
+
+ loop {
+ info!("Spinning for 1 second");
+ time::time_manager().spin_for(Duration::from_secs(1));
}
-
- println!("[ML] Loaded! Executing the payload now\n"); - println!("[ML] Loaded! Executing the payload now\n");
- console().flush(); - console().flush();
- -
@ -682,10 +734,6 @@ diff -uNr 06_uart_chainloader/src/main.rs 07_timestamps/src/main.rs
- -
- // Jump to loaded kernel! - // Jump to loaded kernel!
- kernel() - kernel()
+ loop {
+ info!("Spinning for 1 second");
+ time::time_manager().spin_for(Duration::from_secs(1));
+ }
} }
diff -uNr 06_uart_chainloader/src/panic_wait.rs 07_timestamps/src/panic_wait.rs diff -uNr 06_uart_chainloader/src/panic_wait.rs 07_timestamps/src/panic_wait.rs

@ -4,7 +4,6 @@
//! Top-level BSP file for the Raspberry Pi 3 and 4. //! Top-level BSP file for the Raspberry Pi 3 and 4.
pub mod console;
pub mod cpu; pub mod cpu;
pub mod driver; pub mod driver;
pub mod memory; pub mod memory;

@ -1,16 +0,0 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2018-2022 Andre Richter <andre.o.richter@gmail.com>
//! BSP console facilities.
use crate::console;
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
/// Return a reference to the console.
pub fn console() -> &'static dyn console::interface::All {
&super::driver::PL011_UART
}

@ -5,51 +5,78 @@
//! 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 Definitions // Global instances
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
/// 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[..]
} //--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
fn post_device_driver_init(&self) { /// Initialize the driver subsystem.
// Configure PL011Uart's output pins. ///
GPIO.map_pl011_uart(); /// # Safety
///
/// See child function calls.
///
/// # Note
///
/// Using atomics here relieves us from needing to use `unsafe` for the static variable.
///
/// On `AArch64`, which is the only implemented architecture at the time of writing this,
/// [`AtomicBool::load`] and [`AtomicBool::store`] are lowered to ordinary load and store
/// instructions. They are therefore safe to use even with MMU + caching deactivated.
///
/// [`AtomicBool::load`]: core::sync::atomic::AtomicBool::load
/// [`AtomicBool::store`]: core::sync::atomic::AtomicBool::store
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(())
} }

@ -4,7 +4,9 @@
//! System console. //! System console.
use crate::bsp; mod null_console;
use crate::synchronization::{self, NullLock};
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
// Public Definitions // Public Definitions
@ -54,13 +56,26 @@ pub mod interface {
pub trait All: Write + Read + Statistics {} pub trait All: Write + Read + Statistics {}
} }
//--------------------------------------------------------------------------------------------------
// Global instances
//--------------------------------------------------------------------------------------------------
static CUR_CONSOLE: NullLock<&'static (dyn interface::All + Sync)> =
NullLock::new(&null_console::NULL_CONSOLE);
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
// Public Code // 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. /// This is the global console used by all printing macros.
pub fn console() -> &'static dyn interface::All { pub fn console() -> &'static dyn interface::All {
bsp::console::console() CUR_CONSOLE.lock(|con| *con)
} }

@ -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 {}

@ -4,6 +4,22 @@
//! Driver support. //! Driver support.
use crate::{
info,
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
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
@ -24,21 +40,128 @@ pub mod interface {
Ok(()) Ok(())
} }
} }
}
/// Tpye to be used as an optional callback after a driver's init() has run.
pub type DeviceDriverPostInitCallback = unsafe fn() -> Result<(), &'static str>;
/// A descriptor for device 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()),
}
}
/// Device driver management functions. /// 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.
/// ///
/// The `BSP` is supposed to supply one global instance. /// # Safety
pub trait DriverManager { ///
/// Return a slice of references to all `BSP`-instantiated drivers. /// - During init, drivers might do stuff with system-wide impact.
/// pub unsafe fn init_drivers(&self) {
/// # Safety self.for_each_descriptor(|descriptor| {
/// // 1. Initialize driver.
/// - The order of devices is the order in which `DeviceDriver::init()` is called. if let Err(x) = descriptor.device_driver.init() {
fn all_device_drivers(&self) -> &[&'static (dyn DeviceDriver + Sync)]; 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| {
info!(" {}. {}", i, descriptor.device_driver.compatible());
i += 1;
});
} }
} }

@ -133,14 +133,13 @@ mod time;
/// - 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 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.
// Transition from unsafe to safe. // Transition from unsafe to safe.
@ -150,7 +149,6 @@ unsafe fn kernel_init() -> ! {
/// The main function running after the early init. /// The main function running after the early init.
fn kernel_main() -> ! { fn kernel_main() -> ! {
use core::time::Duration; use core::time::Duration;
use driver::interface::DriverManager;
info!( info!(
"{} version {}", "{} version {}",
@ -165,13 +163,7 @@ fn kernel_main() -> ! {
); );
info!("Drivers loaded:"); info!("Drivers loaded:");
for (i, driver) in bsp::driver::driver_manager() driver::driver_manager().enumerate();
.all_device_drivers()
.iter()
.enumerate()
{
info!(" {}. {}", i + 1, driver.compatible());
}
// Test a failing timer case. // Test a failing timer case.
time::time_manager().spin_for(Duration::from_nanos(1)); time::time_manager().spin_for(Duration::from_nanos(1));

@ -401,4 +401,26 @@ diff -uNr 07_timestamps/Makefile 08_hw_debug_JTAG/Makefile
## Testing targets ## Testing targets
##-------------------------------------------------------------------------------------------------- ##--------------------------------------------------------------------------------------------------
diff -uNr 07_timestamps/src/bsp/raspberrypi/driver.rs 08_hw_debug_JTAG/src/bsp/raspberrypi/driver.rs
--- 07_timestamps/src/bsp/raspberrypi/driver.rs
+++ 08_hw_debug_JTAG/src/bsp/raspberrypi/driver.rs
@@ -57,17 +57,6 @@
/// # Safety
///
/// See child function calls.
-///
-/// # Note
-///
-/// Using atomics here relieves us from needing to use `unsafe` for the static variable.
-///
-/// On `AArch64`, which is the only implemented architecture at the time of writing this,
-/// [`AtomicBool::load`] and [`AtomicBool::store`] are lowered to ordinary load and store
-/// instructions. They are therefore safe to use even with MMU + caching deactivated.
-///
-/// [`AtomicBool::load`]: core::sync::atomic::AtomicBool::load
-/// [`AtomicBool::store`]: core::sync::atomic::AtomicBool::store
pub unsafe fn init() -> Result<(), &'static str> {
static INIT_DONE: AtomicBool = AtomicBool::new(false);
if INIT_DONE.load(Ordering::Relaxed) {
``` ```

@ -4,7 +4,6 @@
//! Top-level BSP file for the Raspberry Pi 3 and 4. //! Top-level BSP file for the Raspberry Pi 3 and 4.
pub mod console;
pub mod cpu; pub mod cpu;
pub mod driver; pub mod driver;
pub mod memory; pub mod memory;

@ -1,16 +0,0 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2018-2022 Andre Richter <andre.o.richter@gmail.com>
//! BSP console facilities.
use crate::console;
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
/// Return a reference to the console.
pub fn console() -> &'static dyn console::interface::All {
&super::driver::PL011_UART
}

@ -5,51 +5,67 @@
//! 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 Definitions // Global instances
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
/// 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[..]
} //--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
fn post_device_driver_init(&self) { /// Initialize the driver subsystem.
// Configure PL011Uart's output pins. ///
GPIO.map_pl011_uart(); /// # 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(())
} }

@ -4,7 +4,9 @@
//! System console. //! System console.
use crate::bsp; mod null_console;
use crate::synchronization::{self, NullLock};
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
// Public Definitions // Public Definitions
@ -54,13 +56,26 @@ pub mod interface {
pub trait All: Write + Read + Statistics {} pub trait All: Write + Read + Statistics {}
} }
//--------------------------------------------------------------------------------------------------
// Global instances
//--------------------------------------------------------------------------------------------------
static CUR_CONSOLE: NullLock<&'static (dyn interface::All + Sync)> =
NullLock::new(&null_console::NULL_CONSOLE);
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
// Public Code // 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. /// This is the global console used by all printing macros.
pub fn console() -> &'static dyn interface::All { pub fn console() -> &'static dyn interface::All {
bsp::console::console() CUR_CONSOLE.lock(|con| *con)
} }

@ -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 {}

@ -4,6 +4,22 @@
//! Driver support. //! Driver support.
use crate::{
info,
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
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
@ -24,21 +40,128 @@ pub mod interface {
Ok(()) Ok(())
} }
} }
}
/// Tpye to be used as an optional callback after a driver's init() has run.
pub type DeviceDriverPostInitCallback = unsafe fn() -> Result<(), &'static str>;
/// A descriptor for device 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()),
}
}
/// Device driver management functions. /// 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.
/// ///
/// The `BSP` is supposed to supply one global instance. /// # Safety
pub trait DriverManager { ///
/// Return a slice of references to all `BSP`-instantiated drivers. /// - During init, drivers might do stuff with system-wide impact.
/// pub unsafe fn init_drivers(&self) {
/// # Safety self.for_each_descriptor(|descriptor| {
/// // 1. Initialize driver.
/// - The order of devices is the order in which `DeviceDriver::init()` is called. if let Err(x) = descriptor.device_driver.init() {
fn all_device_drivers(&self) -> &[&'static (dyn DeviceDriver + Sync)]; 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| {
info!(" {}. {}", i, descriptor.device_driver.compatible());
i += 1;
});
} }
} }

@ -133,14 +133,13 @@ mod time;
/// - 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 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.
// Transition from unsafe to safe. // Transition from unsafe to safe.
@ -150,7 +149,6 @@ unsafe fn kernel_init() -> ! {
/// The main function running after the early init. /// The main function running after the early init.
fn kernel_main() -> ! { fn kernel_main() -> ! {
use core::time::Duration; use core::time::Duration;
use driver::interface::DriverManager;
info!( info!(
"{} version {}", "{} version {}",
@ -165,13 +163,7 @@ fn kernel_main() -> ! {
); );
info!("Drivers loaded:"); info!("Drivers loaded:");
for (i, driver) in bsp::driver::driver_manager() driver::driver_manager().enumerate();
.all_device_drivers()
.iter()
.enumerate()
{
info!(" {}. {}", i + 1, driver.compatible());
}
// Test a failing timer case. // Test a failing timer case.
time::time_manager().spin_for(Duration::from_nanos(1)); time::time_manager().spin_for(Duration::from_nanos(1));

@ -519,15 +519,15 @@ diff -uNr 08_hw_debug_JTAG/src/main.rs 09_privilege_level/src/main.rs
mod panic_wait; mod panic_wait;
mod print; mod print;
mod synchronization; mod synchronization;
@@ -149,6 +150,7 @@ @@ -148,6 +149,7 @@
/// 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 core::time::Duration; use core::time::Duration;
use driver::interface::DriverManager;
@@ -159,6 +161,12 @@ info!(
@@ -157,6 +159,12 @@
); );
info!("Booting on: {}", bsp::board_name()); info!("Booting on: {}", bsp::board_name());
@ -540,9 +540,9 @@ diff -uNr 08_hw_debug_JTAG/src/main.rs 09_privilege_level/src/main.rs
info!( info!(
"Architectural timer resolution: {} ns", "Architectural timer resolution: {} ns",
time::time_manager().resolution().as_nanos() time::time_manager().resolution().as_nanos()
@@ -173,11 +181,15 @@ @@ -165,11 +173,15 @@
info!(" {}. {}", i + 1, driver.compatible()); info!("Drivers loaded:");
} driver::driver_manager().enumerate();
- // Test a failing timer case. - // Test a failing timer case.
- time::time_manager().spin_for(Duration::from_nanos(1)); - time::time_manager().spin_for(Duration::from_nanos(1));

@ -4,7 +4,6 @@
//! Top-level BSP file for the Raspberry Pi 3 and 4. //! Top-level BSP file for the Raspberry Pi 3 and 4.
pub mod console;
pub mod cpu; pub mod cpu;
pub mod driver; pub mod driver;
pub mod memory; pub mod memory;

@ -1,16 +0,0 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2018-2022 Andre Richter <andre.o.richter@gmail.com>
//! BSP console facilities.
use crate::console;
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
/// Return a reference to the console.
pub fn console() -> &'static dyn console::interface::All {
&super::driver::PL011_UART
}

@ -5,51 +5,67 @@
//! 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 Definitions // Global instances
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
/// 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[..]
} //--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
fn post_device_driver_init(&self) { /// Initialize the driver subsystem.
// Configure PL011Uart's output pins. ///
GPIO.map_pl011_uart(); /// # 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(())
} }

@ -4,7 +4,9 @@
//! System console. //! System console.
use crate::bsp; mod null_console;
use crate::synchronization::{self, NullLock};
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
// Public Definitions // Public Definitions
@ -54,13 +56,26 @@ pub mod interface {
pub trait All: Write + Read + Statistics {} pub trait All: Write + Read + Statistics {}
} }
//--------------------------------------------------------------------------------------------------
// Global instances
//--------------------------------------------------------------------------------------------------
static CUR_CONSOLE: NullLock<&'static (dyn interface::All + Sync)> =
NullLock::new(&null_console::NULL_CONSOLE);
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
// Public Code // 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. /// This is the global console used by all printing macros.
pub fn console() -> &'static dyn interface::All { pub fn console() -> &'static dyn interface::All {
bsp::console::console() CUR_CONSOLE.lock(|con| *con)
} }

@ -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 {}

@ -4,6 +4,22 @@
//! Driver support. //! Driver support.
use crate::{
info,
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
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
@ -24,21 +40,128 @@ pub mod interface {
Ok(()) Ok(())
} }
} }
}
/// Tpye to be used as an optional callback after a driver's init() has run.
pub type DeviceDriverPostInitCallback = unsafe fn() -> Result<(), &'static str>;
/// A descriptor for device 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()),
}
}
/// Device driver management functions. /// 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.
/// ///
/// The `BSP` is supposed to supply one global instance. /// # Safety
pub trait DriverManager { ///
/// Return a slice of references to all `BSP`-instantiated drivers. /// - During init, drivers might do stuff with system-wide impact.
/// pub unsafe fn init_drivers(&self) {
/// # Safety self.for_each_descriptor(|descriptor| {
/// // 1. Initialize driver.
/// - The order of devices is the order in which `DeviceDriver::init()` is called. if let Err(x) = descriptor.device_driver.init() {
fn all_device_drivers(&self) -> &[&'static (dyn DeviceDriver + Sync)]; 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| {
info!(" {}. {}", i, descriptor.device_driver.compatible());
i += 1;
});
} }
} }

@ -134,14 +134,13 @@ mod time;
/// - 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 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.
// Transition from unsafe to safe. // Transition from unsafe to safe.
@ -152,7 +151,6 @@ unsafe fn kernel_init() -> ! {
fn kernel_main() -> ! { fn kernel_main() -> ! {
use console::console; use console::console;
use core::time::Duration; use core::time::Duration;
use driver::interface::DriverManager;
info!( info!(
"{} version {}", "{} version {}",
@ -173,13 +171,7 @@ fn kernel_main() -> ! {
); );
info!("Drivers loaded:"); info!("Drivers loaded:");
for (i, driver) in bsp::driver::driver_manager() driver::driver_manager().enumerate();
.all_device_drivers()
.iter()
.enumerate()
{
info!(" {}. {}", i + 1, driver.compatible());
}
info!("Timer test, spinning for 1 second"); info!("Timer test, spinning for 1 second");
time::time_manager().spin_for(Duration::from_secs(1)); time::time_manager().spin_for(Duration::from_secs(1));

@ -296,7 +296,6 @@ Turning on virtual memory is now the first thing we do during kernel init:
```rust ```rust
unsafe fn kernel_init() -> ! { unsafe fn kernel_init() -> ! {
use driver::interface::DriverManager;
use memory::mmu::interface::MMU; use memory::mmu::interface::MMU;
if let Err(string) = memory::mmu::mmu().enable_mmu_and_caching() { if let Err(string) = memory::mmu::mmu().enable_mmu_and_caching() {
@ -1135,7 +1134,7 @@ diff -uNr 09_privilege_level/src/main.rs 10_virtual_mem_part1_identity_mapping/s
mod panic_wait; mod panic_wait;
mod print; mod print;
mod synchronization; mod synchronization;
@@ -132,9 +137,17 @@ @@ -132,8 +137,17 @@
/// # Safety /// # Safety
/// ///
/// - Only a single core must be active and running this function. /// - Only a single core must be active and running this function.
@ -1145,25 +1144,25 @@ diff -uNr 09_privilege_level/src/main.rs 10_virtual_mem_part1_identity_mapping/s
+/// e.g. the yet-to-be-introduced spinlocks in the device drivers (which currently employ +/// e.g. the yet-to-be-introduced spinlocks in the device drivers (which currently employ
+/// NullLocks instead of spinlocks), will fail to work (properly) on the RPi SoCs. +/// NullLocks instead of spinlocks), will fail to work (properly) on the RPi SoCs.
unsafe fn kernel_init() -> ! { unsafe fn kernel_init() -> ! {
use driver::interface::DriverManager;
+ use memory::mmu::interface::MMU; + use memory::mmu::interface::MMU;
+ +
+ if let Err(string) = memory::mmu::mmu().enable_mmu_and_caching() { + if let Err(string) = memory::mmu::mmu().enable_mmu_and_caching() {
+ panic!("MMU: {}", string); + panic!("MMU: {}", string);
+ } + }
+
for i in bsp::driver::driver_manager().all_device_drivers().iter() { // Initialize the BSP driver subsystem.
if let Err(x) = i.init() { if let Err(x) = bsp::driver::init() {
@@ -150,7 +163,7 @@ panic!("Error initializing BSP driver subsystem: {}", x);
@@ -149,7 +163,7 @@
/// 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 console::{console, interface::Write}; + use console::{console, interface::Write};
use core::time::Duration; use core::time::Duration;
use driver::interface::DriverManager;
@@ -161,6 +174,9 @@ info!(
@@ -159,6 +173,9 @@
); );
info!("Booting on: {}", bsp::board_name()); info!("Booting on: {}", bsp::board_name());
@ -1173,7 +1172,7 @@ diff -uNr 09_privilege_level/src/main.rs 10_virtual_mem_part1_identity_mapping/s
let (_, privilege_level) = exception::current_privilege_level(); let (_, privilege_level) = exception::current_privilege_level();
info!("Current privilege level: {}", privilege_level); info!("Current privilege level: {}", privilege_level);
@@ -184,6 +200,13 @@ @@ -176,6 +193,13 @@
info!("Timer test, spinning for 1 second"); info!("Timer test, spinning for 1 second");
time::time_manager().spin_for(Duration::from_secs(1)); time::time_manager().spin_for(Duration::from_secs(1));

@ -4,7 +4,6 @@
//! Top-level BSP file for the Raspberry Pi 3 and 4. //! Top-level BSP file for the Raspberry Pi 3 and 4.
pub mod console;
pub mod cpu; pub mod cpu;
pub mod driver; pub mod driver;
pub mod memory; pub mod memory;

@ -1,16 +0,0 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2018-2022 Andre Richter <andre.o.richter@gmail.com>
//! BSP console facilities.
use crate::console;
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
/// Return a reference to the console.
pub fn console() -> &'static dyn console::interface::All {
&super::driver::PL011_UART
}

@ -5,51 +5,67 @@
//! 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 Definitions // Global instances
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
/// 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[..]
} //--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
fn post_device_driver_init(&self) { /// Initialize the driver subsystem.
// Configure PL011Uart's output pins. ///
GPIO.map_pl011_uart(); /// # 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(())
} }

@ -4,7 +4,9 @@
//! System console. //! System console.
use crate::bsp; mod null_console;
use crate::synchronization::{self, NullLock};
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
// Public Definitions // Public Definitions
@ -54,13 +56,26 @@ pub mod interface {
pub trait All: Write + Read + Statistics {} pub trait All: Write + Read + Statistics {}
} }
//--------------------------------------------------------------------------------------------------
// Global instances
//--------------------------------------------------------------------------------------------------
static CUR_CONSOLE: NullLock<&'static (dyn interface::All + Sync)> =
NullLock::new(&null_console::NULL_CONSOLE);
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
// Public Code // 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. /// This is the global console used by all printing macros.
pub fn console() -> &'static dyn interface::All { pub fn console() -> &'static dyn interface::All {
bsp::console::console() CUR_CONSOLE.lock(|con| *con)
} }

@ -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 {}

@ -4,6 +4,22 @@
//! Driver support. //! Driver support.
use crate::{
info,
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
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
@ -24,21 +40,128 @@ pub mod interface {
Ok(()) Ok(())
} }
} }
}
/// Tpye to be used as an optional callback after a driver's init() has run.
pub type DeviceDriverPostInitCallback = unsafe fn() -> Result<(), &'static str>;
/// A descriptor for device 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()),
}
}
/// Device driver management functions. /// 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.
/// ///
/// The `BSP` is supposed to supply one global instance. /// # Safety
pub trait DriverManager { ///
/// Return a slice of references to all `BSP`-instantiated drivers. /// - During init, drivers might do stuff with system-wide impact.
/// pub unsafe fn init_drivers(&self) {
/// # Safety self.for_each_descriptor(|descriptor| {
/// // 1. Initialize driver.
/// - The order of devices is the order in which `DeviceDriver::init()` is called. if let Err(x) = descriptor.device_driver.init() {
fn all_device_drivers(&self) -> &[&'static (dyn DeviceDriver + Sync)]; 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| {
info!(" {}. {}", i, descriptor.device_driver.compatible());
i += 1;
});
} }
} }

@ -142,19 +142,19 @@ mod time;
/// e.g. the yet-to-be-introduced spinlocks in the device drivers (which currently employ /// e.g. the yet-to-be-introduced spinlocks in the device drivers (which currently employ
/// NullLocks instead of spinlocks), will fail to work (properly) on the RPi SoCs. /// NullLocks instead of spinlocks), will fail to work (properly) on the RPi SoCs.
unsafe fn kernel_init() -> ! { unsafe fn kernel_init() -> ! {
use driver::interface::DriverManager;
use memory::mmu::interface::MMU; use memory::mmu::interface::MMU;
if let Err(string) = memory::mmu::mmu().enable_mmu_and_caching() { if let Err(string) = memory::mmu::mmu().enable_mmu_and_caching() {
panic!("MMU: {}", string); panic!("MMU: {}", string);
} }
for i in bsp::driver::driver_manager().all_device_drivers().iter() { // Initialize the BSP driver subsystem.
if let Err(x) = i.init() { if let Err(x) = bsp::driver::init() {
panic!("Error loading driver: {}: {}", i.compatible(), x); panic!("Error initializing BSP driver subsystem: {}", 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.
// Transition from unsafe to safe. // Transition from unsafe to safe.
@ -165,7 +165,6 @@ unsafe fn kernel_init() -> ! {
fn kernel_main() -> ! { fn kernel_main() -> ! {
use console::{console, interface::Write}; use console::{console, interface::Write};
use core::time::Duration; use core::time::Duration;
use driver::interface::DriverManager;
info!( info!(
"{} version {}", "{} version {}",
@ -189,13 +188,7 @@ fn kernel_main() -> ! {
); );
info!("Drivers loaded:"); info!("Drivers loaded:");
for (i, driver) in bsp::driver::driver_manager() driver::driver_manager().enumerate();
.all_device_drivers()
.iter()
.enumerate()
{
info!(" {}. {}", i + 1, driver.compatible());
}
info!("Timer test, spinning for 1 second"); info!("Timer test, spinning for 1 second");
time::time_manager().spin_for(Duration::from_secs(1)); time::time_manager().spin_for(Duration::from_secs(1));

@ -1024,8 +1024,8 @@ diff -uNr 10_virtual_mem_part1_identity_mapping/src/exception.rs 11_exceptions_p
diff -uNr 10_virtual_mem_part1_identity_mapping/src/main.rs 11_exceptions_part1_groundwork/src/main.rs diff -uNr 10_virtual_mem_part1_identity_mapping/src/main.rs 11_exceptions_part1_groundwork/src/main.rs
--- 10_virtual_mem_part1_identity_mapping/src/main.rs --- 10_virtual_mem_part1_identity_mapping/src/main.rs
+++ 11_exceptions_part1_groundwork/src/main.rs +++ 11_exceptions_part1_groundwork/src/main.rs
@@ -145,6 +145,8 @@ @@ -144,6 +144,8 @@
use driver::interface::DriverManager; unsafe fn kernel_init() -> ! {
use memory::mmu::interface::MMU; use memory::mmu::interface::MMU;
+ exception::handling_init(); + exception::handling_init();
@ -1040,9 +1040,9 @@ diff -uNr 10_virtual_mem_part1_identity_mapping/src/main.rs 11_exceptions_part1_
- use console::{console, interface::Write}; - use console::{console, interface::Write};
+ use console::console; + use console::console;
use core::time::Duration; use core::time::Duration;
use driver::interface::DriverManager;
@@ -200,13 +202,28 @@ info!(
@@ -193,13 +195,28 @@
info!("Timer test, spinning for 1 second"); info!("Timer test, spinning for 1 second");
time::time_manager().spin_for(Duration::from_secs(1)); time::time_manager().spin_for(Duration::from_secs(1));

@ -4,7 +4,6 @@
//! Top-level BSP file for the Raspberry Pi 3 and 4. //! Top-level BSP file for the Raspberry Pi 3 and 4.
pub mod console;
pub mod cpu; pub mod cpu;
pub mod driver; pub mod driver;
pub mod memory; pub mod memory;

@ -1,16 +0,0 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2018-2022 Andre Richter <andre.o.richter@gmail.com>
//! BSP console facilities.
use crate::console;
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
/// Return a reference to the console.
pub fn console() -> &'static dyn console::interface::All {
&super::driver::PL011_UART
}

@ -5,51 +5,67 @@
//! 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 Definitions // Global instances
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
/// 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[..]
} //--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
fn post_device_driver_init(&self) { /// Initialize the driver subsystem.
// Configure PL011Uart's output pins. ///
GPIO.map_pl011_uart(); /// # 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(())
} }

@ -4,7 +4,9 @@
//! System console. //! System console.
use crate::bsp; mod null_console;
use crate::synchronization::{self, NullLock};
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
// Public Definitions // Public Definitions
@ -54,13 +56,26 @@ pub mod interface {
pub trait All: Write + Read + Statistics {} pub trait All: Write + Read + Statistics {}
} }
//--------------------------------------------------------------------------------------------------
// Global instances
//--------------------------------------------------------------------------------------------------
static CUR_CONSOLE: NullLock<&'static (dyn interface::All + Sync)> =
NullLock::new(&null_console::NULL_CONSOLE);
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
// Public Code // 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. /// This is the global console used by all printing macros.
pub fn console() -> &'static dyn interface::All { pub fn console() -> &'static dyn interface::All {
bsp::console::console() CUR_CONSOLE.lock(|con| *con)
} }

@ -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 {}

@ -4,6 +4,22 @@
//! Driver support. //! Driver support.
use crate::{
info,
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
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
@ -24,21 +40,128 @@ pub mod interface {
Ok(()) Ok(())
} }
} }
}
/// Tpye to be used as an optional callback after a driver's init() has run.
pub type DeviceDriverPostInitCallback = unsafe fn() -> Result<(), &'static str>;
/// A descriptor for device 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()),
}
}
/// Device driver management functions. /// 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.
/// ///
/// The `BSP` is supposed to supply one global instance. /// # Safety
pub trait DriverManager { ///
/// Return a slice of references to all `BSP`-instantiated drivers. /// - During init, drivers might do stuff with system-wide impact.
/// pub unsafe fn init_drivers(&self) {
/// # Safety self.for_each_descriptor(|descriptor| {
/// // 1. Initialize driver.
/// - The order of devices is the order in which `DeviceDriver::init()` is called. if let Err(x) = descriptor.device_driver.init() {
fn all_device_drivers(&self) -> &[&'static (dyn DeviceDriver + Sync)]; 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| {
info!(" {}. {}", i, descriptor.device_driver.compatible());
i += 1;
});
} }
} }

@ -142,7 +142,6 @@ mod time;
/// e.g. the yet-to-be-introduced spinlocks in the device drivers (which currently employ /// e.g. the yet-to-be-introduced spinlocks in the device drivers (which currently employ
/// NullLocks instead of spinlocks), will fail to work (properly) on the RPi SoCs. /// NullLocks instead of spinlocks), will fail to work (properly) on the RPi SoCs.
unsafe fn kernel_init() -> ! { unsafe fn kernel_init() -> ! {
use driver::interface::DriverManager;
use memory::mmu::interface::MMU; use memory::mmu::interface::MMU;
exception::handling_init(); exception::handling_init();
@ -151,12 +150,13 @@ unsafe fn kernel_init() -> ! {
panic!("MMU: {}", string); panic!("MMU: {}", string);
} }
for i in bsp::driver::driver_manager().all_device_drivers().iter() { // Initialize the BSP driver subsystem.
if let Err(x) = i.init() { if let Err(x) = bsp::driver::init() {
panic!("Error loading driver: {}: {}", i.compatible(), x); panic!("Error initializing BSP driver subsystem: {}", 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.
// Transition from unsafe to safe. // Transition from unsafe to safe.
@ -167,7 +167,6 @@ unsafe fn kernel_init() -> ! {
fn kernel_main() -> ! { fn kernel_main() -> ! {
use console::console; use console::console;
use core::time::Duration; use core::time::Duration;
use driver::interface::DriverManager;
info!( info!(
"{} version {}", "{} version {}",
@ -191,13 +190,7 @@ fn kernel_main() -> ! {
); );
info!("Drivers loaded:"); info!("Drivers loaded:");
for (i, driver) in bsp::driver::driver_manager() driver::driver_manager().enumerate();
.all_device_drivers()
.iter()
.enumerate()
{
info!(" {}. {}", i + 1, driver.compatible());
}
info!("Timer test, spinning for 1 second"); info!("Timer test, spinning for 1 second");
time::time_manager().spin_for(Duration::from_secs(1)); time::time_manager().spin_for(Duration::from_secs(1));

@ -291,7 +291,7 @@ test_boot: $(KERNEL_BIN)
## Helpers for unit and integration test targets ## Helpers for unit and integration test targets
##------------------------------------------------------------------------------ ##------------------------------------------------------------------------------
define KERNEL_TEST_RUNNER define KERNEL_TEST_RUNNER
#!/usr/bin/env bash #!/usr/bin/env bash
# The cargo test runner seems to change into the crate under test's directory. Therefore, ensure # The cargo test runner seems to change into the crate under test's directory. Therefore, ensure
# this script executes from the root. # this script executes from the root.

@ -266,10 +266,8 @@ implementation in `lib.rs`:
#[cfg(test)] #[cfg(test)]
#[no_mangle] #[no_mangle]
unsafe fn kernel_init() -> ! { unsafe fn kernel_init() -> ! {
use driver::interface::DriverManager;
exception::handling_init(); exception::handling_init();
bsp::driver::driver_manager().qemu_bring_up_console(); bsp::driver::qemu_bring_up_console();
test_main(); test_main();
@ -277,12 +275,12 @@ unsafe fn kernel_init() -> ! {
} }
``` ```
Note the call to `bsp::driver::driver_manager().qemu_bring_up_console()`. Since we are running all Note the call to `bsp::driver::qemu_bring_up_console()`. Since we are running all our tests inside
our tests inside `QEMU`, we need to ensure that whatever peripheral implements the kernel's `QEMU`, we need to ensure that whatever peripheral implements the kernel's `console` interface is
`console` interface is initialized, so that we can print from our tests. If you recall [tutorial initialized, so that we can print from our tests. If you recall [tutorial 03], bringing up
03], bringing up peripherals in `QEMU` might not need the full initialization as is needed on real peripherals in `QEMU` might not need the full initialization as is needed on real hardware (setting
hardware (setting clocks, config registers, etc...) due to the abstractions in `QEMU`'s emulation clocks, config registers, etc...) due to the abstractions in `QEMU`'s emulation code. So this is an
code. So this is an opportunity to cut down on setup code. opportunity to cut down on setup code.
[tutorial 03]: ../03_hacky_hello_world [tutorial 03]: ../03_hacky_hello_world
@ -622,15 +620,13 @@ your test code into individual chunks. For example, take a look at `tests/01_tim
#![test_runner(libkernel::test_runner)] #![test_runner(libkernel::test_runner)]
use core::time::Duration; use core::time::Duration;
use libkernel::{bsp, cpu, driver, exception, time}; use libkernel::{bsp, cpu, exception, time};
use test_macros::kernel_test; use test_macros::kernel_test;
#[no_mangle] #[no_mangle]
unsafe fn kernel_init() -> ! { unsafe fn kernel_init() -> ! {
use driver::interface::DriverManager;
exception::handling_init(); exception::handling_init();
bsp::driver::driver_manager().qemu_bring_up_console(); bsp::driver::qemu_bring_up_console();
// Depending on CPU arch, some timer bring-up code could go here. Not needed for the RPi. // Depending on CPU arch, some timer bring-up code could go here. Not needed for the RPi.
@ -643,12 +639,12 @@ unsafe fn kernel_init() -> ! {
#[kernel_test] #[kernel_test]
fn timer_is_counting() { fn timer_is_counting() {
assert!(time::time_manager().uptime().as_nanos() > 0) assert!(time::time_manager().uptime().as_nanos() > 0)
assert!(time::time_manager().resolution().as_nanos() < 100)
} }
/// Timer resolution must be sufficient. /// Timer resolution must be sufficient.
#[kernel_test] #[kernel_test]
fn timer_resolution_is_sufficient() { fn timer_resolution_is_sufficient() {
assert!(time::time_manager().resolution().as_nanos() > 0);
assert!(time::time_manager().resolution().as_nanos() < 100) assert!(time::time_manager().resolution().as_nanos() < 100)
} }
``` ```
@ -719,15 +715,14 @@ so the wanted outcome is a `panic!`. Here is the whole test (minus some inline c
mod panic_exit_success; mod panic_exit_success;
use libkernel::{bsp, cpu, driver, exception, info, memory, println}; use libkernel::{bsp, cpu, exception, info, memory, println};
#[no_mangle] #[no_mangle]
unsafe fn kernel_init() -> ! { unsafe fn kernel_init() -> ! {
use driver::interface::DriverManager;
use memory::mmu::interface::MMU; use memory::mmu::interface::MMU;
exception::handling_init(); exception::handling_init();
bsp::driver::driver_manager().qemu_bring_up_console(); bsp::driver::qemu_bring_up_console();
// This line will be printed as the test header. // This line will be printed as the test header.
println!("Testing synchronous exception handling by causing a page fault"); println!("Testing synchronous exception handling by causing a page fault");
@ -794,15 +789,14 @@ The subtest first sends `"ABC"` over the console to the kernel, and then expects
/// Console tests should time out on the I/O harness in case of panic. /// Console tests should time out on the I/O harness in case of panic.
mod panic_wait_forever; mod panic_wait_forever;
use libkernel::{bsp, console, cpu, driver, exception, print}; use libkernel::{bsp, console, cpu, exception, print};
#[no_mangle] #[no_mangle]
unsafe fn kernel_init() -> ! { unsafe fn kernel_init() -> ! {
use console::console; use console::console;
use driver::interface::DriverManager;
exception::handling_init(); exception::handling_init();
bsp::driver::driver_manager().qemu_bring_up_console(); bsp::driver::qemu_bring_up_console();
// Handshake // Handshake
assert_eq!(console().read_char(), 'A'); assert_eq!(console().read_char(), 'A');

@ -4,7 +4,6 @@
//! Top-level BSP file for the Raspberry Pi 3 and 4. //! Top-level BSP file for the Raspberry Pi 3 and 4.
pub mod console;
pub mod cpu; pub mod cpu;
pub mod driver; pub mod driver;
pub mod memory; pub mod memory;

@ -1,16 +0,0 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2018-2022 Andre Richter <andre.o.richter@gmail.com>
//! BSP console facilities.
use crate::console;
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
/// Return a reference to the console.
pub fn console() -> &'static dyn console::interface::All {
&super::driver::PL011_UART
}

@ -5,54 +5,74 @@
//! 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 Definitions // Global instances
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
/// 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[..]
} //--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
fn post_device_driver_init(&self) { /// Initialize the driver subsystem.
// Configure PL011Uart's output pins. ///
GPIO.map_pl011_uart(); /// # 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");
} }
#[cfg(feature = "test_build")] driver_uart()?;
fn qemu_bring_up_console(&self) {} driver_gpio()?;
INIT_DONE.store(true, Ordering::Relaxed);
Ok(())
}
/// Minimal code needed to bring up the console in QEMU (for testing only). This is often less steps
/// than on real hardware due to QEMU's abstractions.
#[cfg(feature = "test_build")]
pub fn qemu_bring_up_console() {
console::register_console(&PL011_UART);
} }

@ -4,7 +4,9 @@
//! System console. //! System console.
use crate::bsp; mod null_console;
use crate::synchronization::{self, NullLock};
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
// Public Definitions // Public Definitions
@ -54,13 +56,26 @@ pub mod interface {
pub trait All: Write + Read + Statistics {} pub trait All: Write + Read + Statistics {}
} }
//--------------------------------------------------------------------------------------------------
// Global instances
//--------------------------------------------------------------------------------------------------
static CUR_CONSOLE: NullLock<&'static (dyn interface::All + Sync)> =
NullLock::new(&null_console::NULL_CONSOLE);
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
// Public Code // 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. /// This is the global console used by all printing macros.
pub fn console() -> &'static dyn interface::All { pub fn console() -> &'static dyn interface::All {
bsp::console::console() CUR_CONSOLE.lock(|con| *con)
} }

@ -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 {}

@ -4,6 +4,22 @@
//! Driver support. //! Driver support.
use crate::{
info,
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
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
@ -24,26 +40,128 @@ pub mod interface {
Ok(()) Ok(())
} }
} }
}
/// Tpye to be used as an optional callback after a driver's init() has run.
pub type DeviceDriverPostInitCallback = unsafe fn() -> Result<(), &'static str>;
/// A descriptor for device 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],
}
}
}
/// Device driver management functions. //--------------------------------------------------------------------------------------------------
// 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.
/// ///
/// The `BSP` is supposed to supply one global instance. /// # Safety
pub trait DriverManager { ///
/// Return a slice of references to all `BSP`-instantiated drivers. /// - During init, drivers might do stuff with system-wide impact.
/// pub unsafe fn init_drivers(&self) {
/// # Safety self.for_each_descriptor(|descriptor| {
/// // 1. Initialize driver.
/// - The order of devices is the order in which `DeviceDriver::init()` is called. if let Err(x) = descriptor.device_driver.init() {
fn all_device_drivers(&self) -> &[&'static (dyn DeviceDriver + Sync)]; 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| {
info!(" {}. {}", i, descriptor.device_driver.compatible());
/// Minimal code needed to bring up the console in QEMU (for testing only). This is often i += 1;
/// less steps than on real hardware due to QEMU's abstractions. });
#[cfg(feature = "test_build")]
fn qemu_bring_up_console(&self);
} }
} }

@ -182,10 +182,8 @@ pub fn test_runner(tests: &[&test_types::UnitTest]) {
#[cfg(test)] #[cfg(test)]
#[no_mangle] #[no_mangle]
unsafe fn kernel_init() -> ! { unsafe fn kernel_init() -> ! {
use driver::interface::DriverManager;
exception::handling_init(); exception::handling_init();
bsp::driver::driver_manager().qemu_bring_up_console(); bsp::driver::qemu_bring_up_console();
test_main(); test_main();

@ -26,7 +26,6 @@ use libkernel::{bsp, console, driver, exception, info, memory, time};
/// NullLocks instead of spinlocks), will fail to work (properly) on the RPi SoCs. /// NullLocks instead of spinlocks), will fail to work (properly) on the RPi SoCs.
#[no_mangle] #[no_mangle]
unsafe fn kernel_init() -> ! { unsafe fn kernel_init() -> ! {
use driver::interface::DriverManager;
use memory::mmu::interface::MMU; use memory::mmu::interface::MMU;
exception::handling_init(); exception::handling_init();
@ -35,12 +34,13 @@ unsafe fn kernel_init() -> ! {
panic!("MMU: {}", string); panic!("MMU: {}", string);
} }
for i in bsp::driver::driver_manager().all_device_drivers().iter() { // Initialize the BSP driver subsystem.
if let Err(x) = i.init() { if let Err(x) = bsp::driver::init() {
panic!("Error loading driver: {}: {}", i.compatible(), x); panic!("Error initializing BSP driver subsystem: {}", 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.
// Transition from unsafe to safe. // Transition from unsafe to safe.
@ -50,7 +50,6 @@ unsafe fn kernel_init() -> ! {
/// 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;
info!("{}", libkernel::version()); info!("{}", libkernel::version());
info!("Booting on: {}", bsp::board_name()); info!("Booting on: {}", bsp::board_name());
@ -70,13 +69,7 @@ fn kernel_main() -> ! {
); );
info!("Drivers loaded:"); info!("Drivers loaded:");
for (i, driver) in bsp::driver::driver_manager() driver::driver_manager().enumerate();
.all_device_drivers()
.iter()
.enumerate()
{
info!(" {}. {}", i + 1, driver.compatible());
}
info!("Echoing input now"); info!("Echoing input now");

@ -11,15 +11,14 @@
/// Console tests should time out on the I/O harness in case of panic. /// Console tests should time out on the I/O harness in case of panic.
mod panic_wait_forever; mod panic_wait_forever;
use libkernel::{bsp, console, cpu, driver, exception, print}; use libkernel::{bsp, console, cpu, exception, print};
#[no_mangle] #[no_mangle]
unsafe fn kernel_init() -> ! { unsafe fn kernel_init() -> ! {
use console::console; use console::console;
use driver::interface::DriverManager;
exception::handling_init(); exception::handling_init();
bsp::driver::driver_manager().qemu_bring_up_console(); bsp::driver::qemu_bring_up_console();
// Handshake // Handshake
assert_eq!(console().read_char(), 'A'); assert_eq!(console().read_char(), 'A');

@ -11,15 +11,13 @@
#![test_runner(libkernel::test_runner)] #![test_runner(libkernel::test_runner)]
use core::time::Duration; use core::time::Duration;
use libkernel::{bsp, cpu, driver, exception, time}; use libkernel::{bsp, cpu, exception, time};
use test_macros::kernel_test; use test_macros::kernel_test;
#[no_mangle] #[no_mangle]
unsafe fn kernel_init() -> ! { unsafe fn kernel_init() -> ! {
use driver::interface::DriverManager;
exception::handling_init(); exception::handling_init();
bsp::driver::driver_manager().qemu_bring_up_console(); bsp::driver::qemu_bring_up_console();
// Depending on CPU arch, some timer bring-up code could go here. Not needed for the RPi. // Depending on CPU arch, some timer bring-up code could go here. Not needed for the RPi.

@ -17,15 +17,14 @@
/// or indirectly. /// or indirectly.
mod panic_exit_success; mod panic_exit_success;
use libkernel::{bsp, cpu, driver, exception, info, memory, println}; use libkernel::{bsp, cpu, exception, info, memory, println};
#[no_mangle] #[no_mangle]
unsafe fn kernel_init() -> ! { unsafe fn kernel_init() -> ! {
use driver::interface::DriverManager;
use memory::mmu::interface::MMU; use memory::mmu::interface::MMU;
exception::handling_init(); exception::handling_init();
bsp::driver::driver_manager().qemu_bring_up_console(); bsp::driver::qemu_bring_up_console();
// This line will be printed as the test header. // This line will be printed as the test header.
println!("Testing synchronous exception handling by causing a page fault"); println!("Testing synchronous exception handling by causing a page fault");

@ -12,7 +12,7 @@
mod panic_wait_forever; mod panic_wait_forever;
use core::arch::asm; use core::arch::asm;
use libkernel::{bsp, cpu, driver, exception, info, memory, println}; use libkernel::{bsp, cpu, exception, info, memory, println};
#[inline(never)] #[inline(never)]
fn nested_system_call() { fn nested_system_call() {
@ -30,11 +30,10 @@ fn nested_system_call() {
#[no_mangle] #[no_mangle]
unsafe fn kernel_init() -> ! { unsafe fn kernel_init() -> ! {
use driver::interface::DriverManager;
use memory::mmu::interface::MMU; use memory::mmu::interface::MMU;
exception::handling_init(); exception::handling_init();
bsp::driver::driver_manager().qemu_bring_up_console(); bsp::driver::qemu_bring_up_console();
// This line will be printed as the test header. // This line will be printed as the test header.
println!("Testing exception restore"); println!("Testing exception restore");

@ -291,7 +291,7 @@ test_boot: $(KERNEL_BIN)
## Helpers for unit and integration test targets ## Helpers for unit and integration test targets
##------------------------------------------------------------------------------ ##------------------------------------------------------------------------------
define KERNEL_TEST_RUNNER define KERNEL_TEST_RUNNER
#!/usr/bin/env bash #!/usr/bin/env bash
# The cargo test runner seems to change into the crate under test's directory. Therefore, ensure # The cargo test runner seems to change into the crate under test's directory. Therefore, ensure
# this script executes from the root. # this script executes from the root.

File diff suppressed because it is too large Load Diff

@ -79,20 +79,25 @@
mod gicc; mod gicc;
mod gicd; mod gicd;
use crate::{bsp, cpu, driver, exception, synchronization, synchronization::InitStateLock}; use crate::{
bsp::{self, device_driver::common::BoundedUsize},
cpu, driver, exception, synchronization,
synchronization::InitStateLock,
};
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
// Private Definitions // Private Definitions
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
type HandlerTable = [Option<exception::asynchronous::IRQDescriptor>; IRQNumber::NUM_TOTAL]; type HandlerTable = [Option<exception::asynchronous::IRQHandlerDescriptor<IRQNumber>>;
IRQNumber::MAX_INCLUSIVE + 1];
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
// Public Definitions // Public Definitions
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
/// Used for the associated type of trait [`exception::asynchronous::interface::IRQManager`]. /// Used for the associated type of trait [`exception::asynchronous::interface::IRQManager`].
pub type IRQNumber = exception::asynchronous::IRQNumber<{ GICv2::MAX_IRQ_NUMBER }>; pub type IRQNumber = BoundedUsize<{ GICv2::MAX_IRQ_NUMBER }>;
/// Representation of the GIC. /// Representation of the GIC.
pub struct GICv2 { pub struct GICv2 {
@ -124,7 +129,7 @@ impl GICv2 {
Self { Self {
gicd: gicd::GICD::new(gicd_mmio_start_addr), gicd: gicd::GICD::new(gicd_mmio_start_addr),
gicc: gicc::GICC::new(gicc_mmio_start_addr), gicc: gicc::GICC::new(gicc_mmio_start_addr),
handler_table: InitStateLock::new([None; IRQNumber::NUM_TOTAL]), handler_table: InitStateLock::new([None; IRQNumber::MAX_INCLUSIVE + 1]),
} }
} }
} }
@ -135,6 +140,8 @@ impl GICv2 {
use synchronization::interface::ReadWriteEx; use synchronization::interface::ReadWriteEx;
impl driver::interface::DeviceDriver for GICv2 { impl driver::interface::DeviceDriver for GICv2 {
type IRQNumberType = IRQNumber;
fn compatible(&self) -> &'static str { fn compatible(&self) -> &'static str {
Self::COMPATIBLE Self::COMPATIBLE
} }
@ -156,23 +163,22 @@ impl exception::asynchronous::interface::IRQManager for GICv2 {
fn register_handler( fn register_handler(
&self, &self,
irq_number: Self::IRQNumberType, irq_handler_descriptor: exception::asynchronous::IRQHandlerDescriptor<Self::IRQNumberType>,
descriptor: exception::asynchronous::IRQDescriptor,
) -> Result<(), &'static str> { ) -> Result<(), &'static str> {
self.handler_table.write(|table| { self.handler_table.write(|table| {
let irq_number = irq_number.get(); let irq_number = irq_handler_descriptor.number().get();
if table[irq_number].is_some() { if table[irq_number].is_some() {
return Err("IRQ handler already registered"); return Err("IRQ handler already registered");
} }
table[irq_number] = Some(descriptor); table[irq_number] = Some(irq_handler_descriptor);
Ok(()) Ok(())
}) })
} }
fn enable(&self, irq_number: Self::IRQNumberType) { fn enable(&self, irq_number: &Self::IRQNumberType) {
self.gicd.enable(irq_number); self.gicd.enable(irq_number);
} }
@ -195,7 +201,7 @@ impl exception::asynchronous::interface::IRQManager for GICv2 {
None => panic!("No handler registered for IRQ {}", irq_number), None => panic!("No handler registered for IRQ {}", irq_number),
Some(descriptor) => { Some(descriptor) => {
// Call the IRQ handler. Panics on failure. // Call the IRQ handler. Panics on failure.
descriptor.handler.handle().expect("Error handling IRQ"); descriptor.handler().handle().expect("Error handling IRQ");
} }
} }
}); });
@ -212,7 +218,7 @@ impl exception::asynchronous::interface::IRQManager for GICv2 {
self.handler_table.read(|table| { self.handler_table.read(|table| {
for (i, opt) in table.iter().skip(32).enumerate() { for (i, opt) in table.iter().skip(32).enumerate() {
if let Some(handler) = opt { if let Some(handler) = opt {
info!(" {: >3}. {}", i + 32, handler.name); info!(" {: >3}. {}", i + 32, handler.name());
} }
} }
}); });

@ -170,7 +170,7 @@ impl GICD {
} }
/// Enable an interrupt. /// Enable an interrupt.
pub fn enable(&self, irq_num: super::IRQNumber) { pub fn enable(&self, irq_num: &super::IRQNumber) {
let irq_num = irq_num.get(); let irq_num = irq_num.get();
// Each bit in the u32 enable register corresponds to one IRQ number. Shift right by 5 // Each bit in the u32 enable register corresponds to one IRQ number. Shift right by 5

@ -5,8 +5,8 @@
//! GPIO Driver. //! GPIO Driver.
use crate::{ use crate::{
bsp::device_driver::common::MMIODerefWrapper, driver, synchronization, bsp::device_driver::common::MMIODerefWrapper, driver, exception::asynchronous::IRQNumber,
synchronization::IRQSafeNullLock, synchronization, synchronization::IRQSafeNullLock,
}; };
use tock_registers::{ use tock_registers::{
interfaces::{ReadWriteable, Writeable}, interfaces::{ReadWriteable, Writeable},
@ -216,6 +216,8 @@ impl GPIO {
use synchronization::interface::Mutex; use synchronization::interface::Mutex;
impl driver::interface::DeviceDriver for GPIO { impl driver::interface::DeviceDriver for GPIO {
type IRQNumberType = IRQNumber;
fn compatible(&self) -> &'static str { fn compatible(&self) -> &'static str {
Self::COMPATIBLE Self::COMPATIBLE
} }

@ -6,7 +6,12 @@
mod peripheral_ic; mod peripheral_ic;
use crate::{driver, exception}; use crate::{
bsp::device_driver::common::BoundedUsize,
driver,
exception::{self, asynchronous::IRQHandlerDescriptor},
};
use core::fmt;
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
// Private Definitions // Private Definitions
@ -21,10 +26,8 @@ struct PendingIRQs {
// Public Definitions // Public Definitions
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
pub type LocalIRQ = pub type LocalIRQ = BoundedUsize<{ InterruptController::MAX_LOCAL_IRQ_NUMBER }>;
exception::asynchronous::IRQNumber<{ InterruptController::MAX_LOCAL_IRQ_NUMBER }>; pub type PeripheralIRQ = BoundedUsize<{ InterruptController::MAX_PERIPHERAL_IRQ_NUMBER }>;
pub type PeripheralIRQ =
exception::asynchronous::IRQNumber<{ InterruptController::MAX_PERIPHERAL_IRQ_NUMBER }>;
/// Used for the associated type of trait [`exception::asynchronous::interface::IRQManager`]. /// Used for the associated type of trait [`exception::asynchronous::interface::IRQManager`].
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
@ -67,6 +70,15 @@ impl Iterator for PendingIRQs {
// Public Code // Public Code
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
impl fmt::Display for IRQNumber {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Local(number) => write!(f, "Local({})", number),
Self::Peripheral(number) => write!(f, "Peripheral({})", number),
}
}
}
impl InterruptController { impl InterruptController {
// Restrict to 3 for now. This makes future code for local_ic.rs more straight forward. // Restrict to 3 for now. This makes future code for local_ic.rs more straight forward.
const MAX_LOCAL_IRQ_NUMBER: usize = 3; const MAX_LOCAL_IRQ_NUMBER: usize = 3;
@ -91,6 +103,8 @@ impl InterruptController {
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
impl driver::interface::DeviceDriver for InterruptController { impl driver::interface::DeviceDriver for InterruptController {
type IRQNumberType = IRQNumber;
fn compatible(&self) -> &'static str { fn compatible(&self) -> &'static str {
Self::COMPATIBLE Self::COMPATIBLE
} }
@ -101,16 +115,23 @@ impl exception::asynchronous::interface::IRQManager for InterruptController {
fn register_handler( fn register_handler(
&self, &self,
irq: Self::IRQNumberType, irq_handler_descriptor: exception::asynchronous::IRQHandlerDescriptor<Self::IRQNumberType>,
descriptor: exception::asynchronous::IRQDescriptor,
) -> Result<(), &'static str> { ) -> Result<(), &'static str> {
match irq { match irq_handler_descriptor.number() {
IRQNumber::Local(_) => unimplemented!("Local IRQ controller not implemented."), IRQNumber::Local(_) => unimplemented!("Local IRQ controller not implemented."),
IRQNumber::Peripheral(pirq) => self.periph.register_handler(pirq, descriptor), IRQNumber::Peripheral(pirq) => {
let periph_descriptor = IRQHandlerDescriptor::new(
pirq,
irq_handler_descriptor.name(),
irq_handler_descriptor.handler(),
);
self.periph.register_handler(periph_descriptor)
}
} }
} }
fn enable(&self, irq: Self::IRQNumberType) { fn enable(&self, irq: &Self::IRQNumberType) {
match irq { match irq {
IRQNumber::Local(_) => unimplemented!("Local IRQ controller not implemented."), IRQNumber::Local(_) => unimplemented!("Local IRQ controller not implemented."),
IRQNumber::Peripheral(pirq) => self.periph.enable(pirq), IRQNumber::Peripheral(pirq) => self.periph.enable(pirq),

@ -50,7 +50,8 @@ type WriteOnlyRegisters = MMIODerefWrapper<WORegisterBlock>;
/// Abstraction for the ReadOnly parts of the associated MMIO registers. /// Abstraction for the ReadOnly parts of the associated MMIO registers.
type ReadOnlyRegisters = MMIODerefWrapper<RORegisterBlock>; type ReadOnlyRegisters = MMIODerefWrapper<RORegisterBlock>;
type HandlerTable = [Option<exception::asynchronous::IRQDescriptor>; PeripheralIRQ::NUM_TOTAL]; type HandlerTable = [Option<exception::asynchronous::IRQHandlerDescriptor<PeripheralIRQ>>;
PeripheralIRQ::MAX_INCLUSIVE + 1];
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
// Public Definitions // Public Definitions
@ -82,7 +83,7 @@ impl PeripheralIC {
Self { Self {
wo_registers: IRQSafeNullLock::new(WriteOnlyRegisters::new(mmio_start_addr)), wo_registers: IRQSafeNullLock::new(WriteOnlyRegisters::new(mmio_start_addr)),
ro_registers: ReadOnlyRegisters::new(mmio_start_addr), ro_registers: ReadOnlyRegisters::new(mmio_start_addr),
handler_table: InitStateLock::new([None; PeripheralIRQ::NUM_TOTAL]), handler_table: InitStateLock::new([None; PeripheralIRQ::MAX_INCLUSIVE + 1]),
} }
} }
@ -105,23 +106,22 @@ impl exception::asynchronous::interface::IRQManager for PeripheralIC {
fn register_handler( fn register_handler(
&self, &self,
irq: Self::IRQNumberType, irq_handler_descriptor: exception::asynchronous::IRQHandlerDescriptor<Self::IRQNumberType>,
descriptor: exception::asynchronous::IRQDescriptor,
) -> Result<(), &'static str> { ) -> Result<(), &'static str> {
self.handler_table.write(|table| { self.handler_table.write(|table| {
let irq_number = irq.get(); let irq_number = irq_handler_descriptor.number().get();
if table[irq_number].is_some() { if table[irq_number].is_some() {
return Err("IRQ handler already registered"); return Err("IRQ handler already registered");
} }
table[irq_number] = Some(descriptor); table[irq_number] = Some(irq_handler_descriptor);
Ok(()) Ok(())
}) })
} }
fn enable(&self, irq: Self::IRQNumberType) { fn enable(&self, irq: &Self::IRQNumberType) {
self.wo_registers.lock(|regs| { self.wo_registers.lock(|regs| {
let enable_reg = if irq.get() <= 31 { let enable_reg = if irq.get() <= 31 {
&regs.ENABLE_1 &regs.ENABLE_1
@ -147,7 +147,7 @@ impl exception::asynchronous::interface::IRQManager for PeripheralIC {
None => panic!("No handler registered for IRQ {}", irq_number), None => panic!("No handler registered for IRQ {}", irq_number),
Some(descriptor) => { Some(descriptor) => {
// Call the IRQ handler. Panics on failure. // Call the IRQ handler. Panics on failure.
descriptor.handler.handle().expect("Error handling IRQ"); descriptor.handler().handle().expect("Error handling IRQ");
} }
} }
} }
@ -162,7 +162,7 @@ impl exception::asynchronous::interface::IRQManager for PeripheralIC {
self.handler_table.read(|table| { self.handler_table.read(|table| {
for (i, opt) in table.iter().enumerate() { for (i, opt) in table.iter().enumerate() {
if let Some(handler) = opt { if let Some(handler) = opt {
info!(" {: >3}. {}", i, handler.name); info!(" {: >3}. {}", i, handler.name());
} }
} }
}); });

@ -10,8 +10,11 @@
//! - <https://developer.arm.com/documentation/ddi0183/latest> //! - <https://developer.arm.com/documentation/ddi0183/latest>
use crate::{ use crate::{
bsp, bsp::device_driver::common::MMIODerefWrapper, console, cpu, driver, exception, bsp::device_driver::common::MMIODerefWrapper,
synchronization, synchronization::IRQSafeNullLock, console, cpu, driver,
exception::{self, asynchronous::IRQNumber},
synchronization,
synchronization::IRQSafeNullLock,
}; };
use core::fmt; use core::fmt;
use tock_registers::{ use tock_registers::{
@ -229,7 +232,6 @@ struct PL011UartInner {
/// Representation of the UART. /// Representation of the UART.
pub struct PL011Uart { pub struct PL011Uart {
inner: IRQSafeNullLock<PL011UartInner>, inner: IRQSafeNullLock<PL011UartInner>,
irq_number: bsp::device_driver::IRQNumber,
} }
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
@ -393,13 +395,9 @@ impl PL011Uart {
/// # Safety /// # Safety
/// ///
/// - The user must ensure to provide a correct MMIO start address. /// - The user must ensure to provide a correct MMIO start address.
pub const unsafe fn new( pub const unsafe fn new(mmio_start_addr: usize) -> Self {
mmio_start_addr: usize,
irq_number: bsp::device_driver::IRQNumber,
) -> Self {
Self { Self {
inner: IRQSafeNullLock::new(PL011UartInner::new(mmio_start_addr)), inner: IRQSafeNullLock::new(PL011UartInner::new(mmio_start_addr)),
irq_number,
} }
} }
} }
@ -410,6 +408,8 @@ impl PL011Uart {
use synchronization::interface::Mutex; use synchronization::interface::Mutex;
impl driver::interface::DeviceDriver for PL011Uart { impl driver::interface::DeviceDriver for PL011Uart {
type IRQNumberType = IRQNumber;
fn compatible(&self) -> &'static str { fn compatible(&self) -> &'static str {
Self::COMPATIBLE Self::COMPATIBLE
} }
@ -420,16 +420,16 @@ impl driver::interface::DeviceDriver for PL011Uart {
Ok(()) Ok(())
} }
fn register_and_enable_irq_handler(&'static self) -> Result<(), &'static str> { fn register_and_enable_irq_handler(
use exception::asynchronous::{irq_manager, IRQDescriptor}; &'static self,
irq_number: &Self::IRQNumberType,
) -> Result<(), &'static str> {
use exception::asynchronous::{irq_manager, IRQHandlerDescriptor};
let descriptor = IRQDescriptor { let descriptor = IRQHandlerDescriptor::new(*irq_number, Self::COMPATIBLE, self);
name: Self::COMPATIBLE,
handler: self,
};
irq_manager().register_handler(self.irq_number, descriptor)?; irq_manager().register_handler(descriptor)?;
irq_manager().enable(self.irq_number); irq_manager().enable(irq_number);
Ok(()) Ok(())
} }

@ -4,7 +4,7 @@
//! Common device driver code. //! Common device driver code.
use core::{marker::PhantomData, ops}; use core::{fmt, marker::PhantomData, ops};
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
// Public Definitions // Public Definitions
@ -15,6 +15,10 @@ pub struct MMIODerefWrapper<T> {
phantom: PhantomData<fn() -> T>, phantom: PhantomData<fn() -> T>,
} }
/// A wrapper type for usize with integrated range bound check.
#[derive(Copy, Clone)]
pub struct BoundedUsize<const MAX_INCLUSIVE: usize>(usize);
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
// Public Code // Public Code
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
@ -36,3 +40,25 @@ impl<T> ops::Deref for MMIODerefWrapper<T> {
unsafe { &*(self.start_addr as *const _) } unsafe { &*(self.start_addr as *const _) }
} }
} }
impl<const MAX_INCLUSIVE: usize> BoundedUsize<{ MAX_INCLUSIVE }> {
pub const MAX_INCLUSIVE: usize = MAX_INCLUSIVE;
/// Creates a new instance if number <= MAX_INCLUSIVE.
pub const fn new(number: usize) -> Self {
assert!(number <= MAX_INCLUSIVE);
Self(number)
}
/// Return the wrapped number.
pub const fn get(self) -> usize {
self.0
}
}
impl<const MAX_INCLUSIVE: usize> fmt::Display for BoundedUsize<{ MAX_INCLUSIVE }> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}

@ -4,7 +4,6 @@
//! Top-level BSP file for the Raspberry Pi 3 and 4. //! Top-level BSP file for the Raspberry Pi 3 and 4.
pub mod console;
pub mod cpu; pub mod cpu;
pub mod driver; pub mod driver;
pub mod exception; pub mod exception;

@ -1,16 +0,0 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2018-2022 Andre Richter <andre.o.richter@gmail.com>
//! BSP console facilities.
use crate::console;
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
/// Return a reference to the console.
pub fn console() -> &'static dyn console::interface::All {
&super::driver::PL011_UART
}

@ -5,68 +5,109 @@
//! BSP driver support. //! BSP driver support.
use super::{exception, memory::map::mmio}; use super::{exception, memory::map::mmio};
use crate::{bsp::device_driver, driver}; use crate::{
bsp::device_driver,
pub use device_driver::IRQNumber; console, driver as generic_driver,
exception::{self as generic_exception},
//-------------------------------------------------------------------------------------------------- };
// Private Definitions use core::sync::atomic::{AtomicBool, Ordering};
//--------------------------------------------------------------------------------------------------
/// Device Driver Manager type.
struct BSPDriverManager {
device_drivers: [&'static (dyn DeviceDriver + Sync); 3],
}
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
// Global instances // Global instances
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
pub(super) static PL011_UART: device_driver::PL011Uart = unsafe { static PL011_UART: device_driver::PL011Uart =
device_driver::PL011Uart::new( unsafe { device_driver::PL011Uart::new(mmio::PL011_UART_START) };
mmio::PL011_UART_START,
exception::asynchronous::irq_map::PL011_UART,
)
};
static GPIO: device_driver::GPIO = unsafe { device_driver::GPIO::new(mmio::GPIO_START) }; static GPIO: device_driver::GPIO = unsafe { device_driver::GPIO::new(mmio::GPIO_START) };
#[cfg(feature = "bsp_rpi3")] #[cfg(feature = "bsp_rpi3")]
pub(super) static INTERRUPT_CONTROLLER: device_driver::InterruptController = static INTERRUPT_CONTROLLER: device_driver::InterruptController =
unsafe { device_driver::InterruptController::new(mmio::PERIPHERAL_INTERRUPT_CONTROLLER_START) }; unsafe { device_driver::InterruptController::new(mmio::PERIPHERAL_IC_START) };
#[cfg(feature = "bsp_rpi4")] #[cfg(feature = "bsp_rpi4")]
pub(super) static INTERRUPT_CONTROLLER: device_driver::GICv2 = static INTERRUPT_CONTROLLER: device_driver::GICv2 =
unsafe { device_driver::GICv2::new(mmio::GICD_START, mmio::GICC_START) }; unsafe { device_driver::GICv2::new(mmio::GICD_START, mmio::GICC_START) };
static BSP_DRIVER_MANAGER: BSPDriverManager = BSPDriverManager {
device_drivers: [&PL011_UART, &GPIO, &INTERRUPT_CONTROLLER],
};
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
// Public Code // Private Code
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
/// Return a reference to the driver manager. /// This must be called only after successful init of the UART driver.
pub fn driver_manager() -> &'static impl driver::interface::DriverManager { fn post_init_uart() -> Result<(), &'static str> {
&BSP_DRIVER_MANAGER console::register_console(&PL011_UART);
Ok(())
} }
//------------------------------------------------------------------------------ /// This must be called only after successful init of the GPIO driver.
// OS Interface Code fn post_init_gpio() -> Result<(), &'static str> {
//------------------------------------------------------------------------------ GPIO.map_pl011_uart();
use driver::interface::DeviceDriver; Ok(())
}
impl driver::interface::DriverManager for BSPDriverManager { /// This must be called only after successful init of the interrupt controller driver.
fn all_device_drivers(&self) -> &[&'static (dyn DeviceDriver + Sync)] { fn post_init_interrupt_controller() -> Result<(), &'static str> {
&self.device_drivers[..] generic_exception::asynchronous::register_irq_manager(&INTERRUPT_CONTROLLER);
}
Ok(())
}
fn post_device_driver_init(&self) { fn driver_uart() -> Result<(), &'static str> {
// Configure PL011Uart's output pins. let uart_descriptor = generic_driver::DeviceDriverDescriptor::new(
GPIO.map_pl011_uart(); &PL011_UART,
Some(post_init_uart),
Some(exception::asynchronous::irq_map::PL011_UART),
);
generic_driver::driver_manager().register_driver(uart_descriptor);
Ok(())
}
fn driver_gpio() -> Result<(), &'static str> {
let gpio_descriptor =
generic_driver::DeviceDriverDescriptor::new(&GPIO, Some(post_init_gpio), None);
generic_driver::driver_manager().register_driver(gpio_descriptor);
Ok(())
}
fn driver_interrupt_controller() -> Result<(), &'static str> {
let interrupt_controller_descriptor = generic_driver::DeviceDriverDescriptor::new(
&INTERRUPT_CONTROLLER,
Some(post_init_interrupt_controller),
None,
);
generic_driver::driver_manager().register_driver(interrupt_controller_descriptor);
Ok(())
}
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
/// 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");
} }
#[cfg(feature = "test_build")] driver_uart()?;
fn qemu_bring_up_console(&self) {} driver_gpio()?;
driver_interrupt_controller()?;
INIT_DONE.store(true, Ordering::Relaxed);
Ok(())
}
/// Minimal code needed to bring up the console in QEMU (for testing only). This is often less steps
/// than on real hardware due to QEMU's abstractions.
#[cfg(feature = "test_build")]
pub fn qemu_bring_up_console() {
console::register_console(&PL011_UART);
} }

@ -4,12 +4,15 @@
//! BSP asynchronous exception handling. //! BSP asynchronous exception handling.
use crate::{bsp, bsp::driver, exception}; use crate::bsp;
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
// Public Definitions // Public Definitions
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
/// Export for reuse in generic asynchronous.rs.
pub use bsp::device_driver::IRQNumber;
#[cfg(feature = "bsp_rpi3")] #[cfg(feature = "bsp_rpi3")]
pub(in crate::bsp) mod irq_map { pub(in crate::bsp) mod irq_map {
use super::bsp::device_driver::{IRQNumber, PeripheralIRQ}; use super::bsp::device_driver::{IRQNumber, PeripheralIRQ};
@ -23,14 +26,3 @@ pub(in crate::bsp) mod irq_map {
pub const PL011_UART: IRQNumber = IRQNumber::new(153); pub const PL011_UART: IRQNumber = IRQNumber::new(153);
} }
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
/// Return a reference to the IRQ manager.
pub fn irq_manager() -> &'static impl exception::asynchronous::interface::IRQManager<
IRQNumberType = bsp::device_driver::IRQNumber,
> {
&driver::INTERRUPT_CONTROLLER
}

@ -73,11 +73,11 @@ pub(super) mod map {
pub mod mmio { pub mod mmio {
use super::*; use super::*;
pub const START: usize = 0x3F00_0000; pub const START: usize = 0x3F00_0000;
pub const PERIPHERAL_INTERRUPT_CONTROLLER_START: usize = START + 0x0000_B200; pub const PERIPHERAL_IC_START: usize = START + 0x0000_B200;
pub const GPIO_START: usize = START + GPIO_OFFSET; pub const GPIO_START: usize = START + GPIO_OFFSET;
pub const PL011_UART_START: usize = START + UART_OFFSET; pub const PL011_UART_START: usize = START + UART_OFFSET;
pub const END_INCLUSIVE: usize = 0x4000_FFFF; pub const END_INCLUSIVE: usize = 0x4000_FFFF;
} }
/// Physical devices. /// Physical devices.

@ -4,7 +4,9 @@
//! System console. //! System console.
use crate::bsp; mod null_console;
use crate::synchronization;
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
// Public Definitions // Public Definitions
@ -54,13 +56,26 @@ pub mod interface {
pub trait All: Write + Read + Statistics {} pub trait All: Write + Read + Statistics {}
} }
//--------------------------------------------------------------------------------------------------
// Global instances
//--------------------------------------------------------------------------------------------------
static CUR_CONSOLE: InitStateLock<&'static (dyn interface::All + Sync)> =
InitStateLock::new(&null_console::NULL_CONSOLE);
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
// Public Code // Public Code
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
use synchronization::{interface::ReadWriteEx, InitStateLock};
/// Register a new console.
pub fn register_console(new_console: &'static (dyn interface::All + Sync)) {
CUR_CONSOLE.write(|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. /// This is the global console used by all printing macros.
pub fn console() -> &'static dyn interface::All { pub fn console() -> &'static dyn interface::All {
bsp::console::console() CUR_CONSOLE.read(|con| *con)
} }

@ -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 {}

@ -4,6 +4,26 @@
//! Driver support. //! Driver support.
use crate::{
exception, info,
synchronization::{interface::ReadWriteEx, InitStateLock},
};
use core::fmt;
//--------------------------------------------------------------------------------------------------
// Private Definitions
//--------------------------------------------------------------------------------------------------
const NUM_DRIVERS: usize = 5;
struct DriverManagerInner<T>
where
T: 'static,
{
next_index: usize,
descriptors: [Option<DeviceDriverDescriptor<T>>; NUM_DRIVERS],
}
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
// Public Definitions // Public Definitions
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
@ -12,6 +32,9 @@
pub mod interface { pub mod interface {
/// Device Driver functions. /// Device Driver functions.
pub trait DeviceDriver { pub trait DeviceDriver {
/// Different interrupt controllers might use different types for IRQ number.
type IRQNumberType: super::fmt::Display;
/// Return a compatibility string for identifying the driver. /// Return a compatibility string for identifying the driver.
fn compatible(&self) -> &'static str; fn compatible(&self) -> &'static str;
@ -24,34 +47,175 @@ pub mod interface {
Ok(()) Ok(())
} }
/// Called by the kernel to register and enable the device's IRQ handlers, if any. /// Called by the kernel to register and enable the device's IRQ handler.
/// ///
/// Rust's type system will prevent a call to this function unless the calling instance /// Rust's type system will prevent a call to this function unless the calling instance
/// itself has static lifetime. /// itself has static lifetime.
fn register_and_enable_irq_handler(&'static self) -> Result<(), &'static str> { fn register_and_enable_irq_handler(
Ok(()) &'static self,
irq_number: &Self::IRQNumberType,
) -> Result<(), &'static str> {
panic!(
"Attempt to enable IRQ {} for device {}, but driver does not support this",
irq_number,
self.compatible()
)
}
}
}
/// Tpye to be used as an optional callback after a driver's init() has run.
pub type DeviceDriverPostInitCallback = unsafe fn() -> Result<(), &'static str>;
/// A descriptor for device drivers.
#[derive(Copy, Clone)]
pub struct DeviceDriverDescriptor<T>
where
T: 'static,
{
device_driver: &'static (dyn interface::DeviceDriver<IRQNumberType = T> + Sync),
post_init_callback: Option<DeviceDriverPostInitCallback>,
irq_number: Option<T>,
}
/// Provides device driver management functions.
pub struct DriverManager<T>
where
T: 'static,
{
inner: InitStateLock<DriverManagerInner<T>>,
}
//--------------------------------------------------------------------------------------------------
// Global instances
//--------------------------------------------------------------------------------------------------
static DRIVER_MANAGER: DriverManager<exception::asynchronous::IRQNumber> = DriverManager::new();
//--------------------------------------------------------------------------------------------------
// Private Code
//--------------------------------------------------------------------------------------------------
impl<T> DriverManagerInner<T>
where
T: 'static + Copy,
{
/// Create an instance.
pub const fn new() -> Self {
Self {
next_index: 0,
descriptors: [None; NUM_DRIVERS],
}
}
}
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
impl<T> DeviceDriverDescriptor<T> {
/// Create an instance.
pub fn new(
device_driver: &'static (dyn interface::DeviceDriver<IRQNumberType = T> + Sync),
post_init_callback: Option<DeviceDriverPostInitCallback>,
irq_number: Option<T>,
) -> Self {
Self {
device_driver,
post_init_callback,
irq_number,
}
}
}
/// Return a reference to the global DriverManager.
pub fn driver_manager() -> &'static DriverManager<exception::asynchronous::IRQNumber> {
&DRIVER_MANAGER
}
impl<T> DriverManager<T>
where
T: fmt::Display + Copy,
{
/// Create an instance.
pub const fn new() -> Self {
Self {
inner: InitStateLock::new(DriverManagerInner::new()),
} }
} }
/// Device driver management functions. /// Register a device driver with the kernel.
pub fn register_driver(&self, descriptor: DeviceDriverDescriptor<T>) {
self.inner.write(|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<T>)) {
self.inner.read(|inner| {
inner
.descriptors
.iter()
.filter_map(|x| x.as_ref())
.for_each(f)
})
}
/// Fully initialize all drivers and their interrupts handlers.
/// ///
/// The `BSP` is supposed to supply one global instance. /// # Safety
pub trait DriverManager { ///
/// Return a slice of references to all `BSP`-instantiated drivers. /// - During init, drivers might do stuff with system-wide impact.
/// pub unsafe fn init_drivers_and_irqs(&self) {
/// # Safety self.for_each_descriptor(|descriptor| {
/// // 1. Initialize driver.
/// - The order of devices is the order in which `DeviceDriver::init()` is called. if let Err(x) = descriptor.device_driver.init() {
fn all_device_drivers(&self) -> &[&'static (dyn DeviceDriver + Sync)]; 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
);
}
}
});
// 3. After all post-init callbacks were done, the interrupt controller should be
// registered and functional. So let drivers register with it now.
self.for_each_descriptor(|descriptor| {
if let Some(irq_number) = &descriptor.irq_number {
if let Err(x) = descriptor
.device_driver
.register_and_enable_irq_handler(irq_number)
{
panic!(
"Error during driver interrupt handler registration: {}: {}",
descriptor.device_driver.compatible(),
x
);
}
}
});
}
/// Enumerate all registered device drivers.
pub fn enumerate(&self) {
let mut i: usize = 1;
self.for_each_descriptor(|descriptor| {
info!(" {}. {}", i, descriptor.device_driver.compatible());
/// Minimal code needed to bring up the console in QEMU (for testing only). This is often i += 1;
/// less steps than on real hardware due to QEMU's abstractions. });
#[cfg(feature = "test_build")]
fn qemu_bring_up_console(&self);
} }
} }

@ -7,9 +7,10 @@
#[cfg(target_arch = "aarch64")] #[cfg(target_arch = "aarch64")]
#[path = "../_arch/aarch64/exception/asynchronous.rs"] #[path = "../_arch/aarch64/exception/asynchronous.rs"]
mod arch_asynchronous; mod arch_asynchronous;
mod null_irq_manager;
use crate::bsp; use crate::{bsp, synchronization};
use core::{fmt, marker::PhantomData}; use core::marker::PhantomData;
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
// Architectural Public Reexports // Architectural Public Reexports
@ -23,14 +24,23 @@ pub use arch_asynchronous::{
// Public Definitions // Public Definitions
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
/// Interrupt number as defined by the BSP.
pub type IRQNumber = bsp::exception::asynchronous::IRQNumber;
/// Interrupt descriptor. /// Interrupt descriptor.
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
pub struct IRQDescriptor { pub struct IRQHandlerDescriptor<T>
where
T: Copy,
{
/// The IRQ number.
number: T,
/// Descriptive name. /// Descriptive name.
pub name: &'static str, name: &'static str,
/// Reference to handler trait object. /// Reference to handler trait object.
pub handler: &'static (dyn interface::IRQHandler + Sync), handler: &'static (dyn interface::IRQHandler + Sync),
} }
/// IRQContext token. /// IRQContext token.
@ -60,17 +70,16 @@ pub mod interface {
/// platform's interrupt controller. /// platform's interrupt controller.
pub trait IRQManager { pub trait IRQManager {
/// The IRQ number type depends on the implementation. /// The IRQ number type depends on the implementation.
type IRQNumberType; type IRQNumberType: Copy;
/// Register a handler. /// Register a handler.
fn register_handler( fn register_handler(
&self, &self,
irq_number: Self::IRQNumberType, irq_handler_descriptor: super::IRQHandlerDescriptor<Self::IRQNumberType>,
descriptor: super::IRQDescriptor,
) -> Result<(), &'static str>; ) -> Result<(), &'static str>;
/// Enable an interrupt in the controller. /// Enable an interrupt in the controller.
fn enable(&self, irq_number: Self::IRQNumberType); fn enable(&self, irq_number: &Self::IRQNumberType);
/// Handle pending interrupts. /// Handle pending interrupts.
/// ///
@ -86,17 +95,55 @@ pub mod interface {
); );
/// Print list of registered handlers. /// Print list of registered handlers.
fn print_handler(&self); fn print_handler(&self) {}
} }
} }
/// A wrapper type for IRQ numbers with integrated range sanity check. //--------------------------------------------------------------------------------------------------
#[derive(Copy, Clone)] // Global instances
pub struct IRQNumber<const MAX_INCLUSIVE: usize>(usize); //--------------------------------------------------------------------------------------------------
static CUR_IRQ_MANAGER: InitStateLock<
&'static (dyn interface::IRQManager<IRQNumberType = IRQNumber> + Sync),
> = InitStateLock::new(&null_irq_manager::NULL_IRQ_MANAGER);
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
// Public Code // Public Code
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
use synchronization::{interface::ReadWriteEx, InitStateLock};
impl<T> IRQHandlerDescriptor<T>
where
T: Copy,
{
/// Create an instance.
pub const fn new(
number: T,
name: &'static str,
handler: &'static (dyn interface::IRQHandler + Sync),
) -> Self {
Self {
number,
name,
handler,
}
}
/// Return the number.
pub const fn number(&self) -> T {
self.number
}
/// Return the name.
pub const fn name(&self) -> &'static str {
self.name
}
/// Return the handler.
pub const fn handler(&self) -> &'static (dyn interface::IRQHandler + Sync) {
self.handler
}
}
impl<'irq_context> IRQContext<'irq_context> { impl<'irq_context> IRQContext<'irq_context> {
/// Creates an IRQContext token. /// Creates an IRQContext token.
@ -115,29 +162,6 @@ impl<'irq_context> IRQContext<'irq_context> {
} }
} }
impl<const MAX_INCLUSIVE: usize> IRQNumber<{ MAX_INCLUSIVE }> {
/// The total number of IRQs this type supports.
pub const NUM_TOTAL: usize = MAX_INCLUSIVE + 1;
/// Creates a new instance if number <= MAX_INCLUSIVE.
pub const fn new(number: usize) -> Self {
assert!(number <= MAX_INCLUSIVE);
Self(number)
}
/// Return the wrapped number.
pub const fn get(self) -> usize {
self.0
}
}
impl<const MAX_INCLUSIVE: usize> fmt::Display for IRQNumber<{ MAX_INCLUSIVE }> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}
/// Executes the provided closure while IRQs are masked on the executing core. /// Executes the provided closure while IRQs are masked on the executing core.
/// ///
/// While the function temporarily changes the HW state of the executing core, it restores it to the /// While the function temporarily changes the HW state of the executing core, it restores it to the
@ -151,9 +175,16 @@ pub fn exec_with_irq_masked<T>(f: impl FnOnce() -> T) -> T {
ret ret
} }
/// Return a reference to the IRQ manager. /// Register a new IRQ manager.
pub fn register_irq_manager(
new_manager: &'static (dyn interface::IRQManager<IRQNumberType = IRQNumber> + Sync),
) {
CUR_IRQ_MANAGER.write(|manager| *manager = new_manager);
}
/// Return a reference to the currently registered IRQ manager.
/// ///
/// This is the IRQ manager used by the architectural interrupt handling code. /// This is the IRQ manager used by the architectural interrupt handling code.
pub fn irq_manager() -> &'static dyn interface::IRQManager<IRQNumberType = bsp::driver::IRQNumber> { pub fn irq_manager() -> &'static dyn interface::IRQManager<IRQNumberType = IRQNumber> {
bsp::exception::asynchronous::irq_manager() CUR_IRQ_MANAGER.read(|manager| *manager)
} }

@ -0,0 +1,42 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2022 Andre Richter <andre.o.richter@gmail.com>
//! Null IRQ Manager.
use super::{interface, IRQContext, IRQHandlerDescriptor};
//--------------------------------------------------------------------------------------------------
// Public Definitions
//--------------------------------------------------------------------------------------------------
pub struct NullIRQManager;
//--------------------------------------------------------------------------------------------------
// Global instances
//--------------------------------------------------------------------------------------------------
pub static NULL_IRQ_MANAGER: NullIRQManager = NullIRQManager {};
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
impl interface::IRQManager for NullIRQManager {
type IRQNumberType = super::IRQNumber;
fn register_handler(
&self,
_descriptor: IRQHandlerDescriptor<Self::IRQNumberType>,
) -> Result<(), &'static str> {
panic!("No IRQ Manager registered yet");
}
fn enable(&self, _irq_number: &Self::IRQNumberType) {
panic!("No IRQ Manager registered yet");
}
fn handle_pending_irqs<'irq_context>(&'irq_context self, _ic: &IRQContext<'irq_context>) {
panic!("No IRQ Manager registered yet");
}
}

@ -183,10 +183,8 @@ pub fn test_runner(tests: &[&test_types::UnitTest]) {
#[cfg(test)] #[cfg(test)]
#[no_mangle] #[no_mangle]
unsafe fn kernel_init() -> ! { unsafe fn kernel_init() -> ! {
use driver::interface::DriverManager;
exception::handling_init(); exception::handling_init();
bsp::driver::driver_manager().qemu_bring_up_console(); bsp::driver::qemu_bring_up_console();
test_main(); test_main();

@ -13,7 +13,7 @@
#![no_main] #![no_main]
#![no_std] #![no_std]
use libkernel::{bsp, cpu, driver, exception, info, memory, state, time, warn}; use libkernel::{bsp, cpu, driver, exception, info, memory, state, time};
/// Early init code. /// Early init code.
/// ///
@ -26,7 +26,6 @@ use libkernel::{bsp, cpu, driver, exception, info, memory, state, time, warn};
/// IRQSafeNullLocks instead of spinlocks), will fail to work (properly) on the RPi SoCs. /// IRQSafeNullLocks instead of spinlocks), will fail to work (properly) on the RPi SoCs.
#[no_mangle] #[no_mangle]
unsafe fn kernel_init() -> ! { unsafe fn kernel_init() -> ! {
use driver::interface::DriverManager;
use memory::mmu::interface::MMU; use memory::mmu::interface::MMU;
exception::handling_init(); exception::handling_init();
@ -35,21 +34,14 @@ unsafe fn kernel_init() -> ! {
panic!("MMU: {}", string); panic!("MMU: {}", string);
} }
for i in bsp::driver::driver_manager().all_device_drivers().iter() { // Initialize the BSP driver subsystem.
if let Err(x) = i.init() { if let Err(x) = bsp::driver::init() {
panic!("Error loading driver: {}: {}", i.compatible(), x); panic!("Error initializing BSP driver subsystem: {}", x);
}
}
bsp::driver::driver_manager().post_device_driver_init();
// println! is usable from here on.
// Let device drivers register and enable their handlers with the interrupt controller.
for i in bsp::driver::driver_manager().all_device_drivers() {
if let Err(msg) = i.register_and_enable_irq_handler() {
warn!("Error registering IRQ handler: {}", msg);
}
} }
// Initialize all device drivers.
driver::driver_manager().init_drivers_and_irqs();
// Unmask interrupts on the boot CPU core. // Unmask interrupts on the boot CPU core.
exception::asynchronous::local_irq_unmask(); exception::asynchronous::local_irq_unmask();
@ -62,9 +54,6 @@ unsafe fn kernel_init() -> ! {
/// The main function running after the early init. /// The main function running after the early init.
fn kernel_main() -> ! { fn kernel_main() -> ! {
use driver::interface::DriverManager;
use exception::asynchronous::interface::IRQManager;
info!("{}", libkernel::version()); info!("{}", libkernel::version());
info!("Booting on: {}", bsp::board_name()); info!("Booting on: {}", bsp::board_name());
@ -83,16 +72,10 @@ fn kernel_main() -> ! {
); );
info!("Drivers loaded:"); info!("Drivers loaded:");
for (i, driver) in bsp::driver::driver_manager() driver::driver_manager().enumerate();
.all_device_drivers()
.iter()
.enumerate()
{
info!(" {}. {}", i + 1, driver.compatible());
}
info!("Registered IRQ handlers:"); info!("Registered IRQ handlers:");
bsp::exception::asynchronous::irq_manager().print_handler(); exception::asynchronous::irq_manager().print_handler();
info!("Echoing input now"); info!("Echoing input now");
cpu::wait_forever(); cpu::wait_forever();

@ -11,15 +11,14 @@
/// Console tests should time out on the I/O harness in case of panic. /// Console tests should time out on the I/O harness in case of panic.
mod panic_wait_forever; mod panic_wait_forever;
use libkernel::{bsp, console, cpu, driver, exception, print}; use libkernel::{bsp, console, cpu, exception, print};
#[no_mangle] #[no_mangle]
unsafe fn kernel_init() -> ! { unsafe fn kernel_init() -> ! {
use console::console; use console::console;
use driver::interface::DriverManager;
exception::handling_init(); exception::handling_init();
bsp::driver::driver_manager().qemu_bring_up_console(); bsp::driver::qemu_bring_up_console();
// Handshake // Handshake
assert_eq!(console().read_char(), 'A'); assert_eq!(console().read_char(), 'A');

@ -11,15 +11,13 @@
#![test_runner(libkernel::test_runner)] #![test_runner(libkernel::test_runner)]
use core::time::Duration; use core::time::Duration;
use libkernel::{bsp, cpu, driver, exception, time}; use libkernel::{bsp, cpu, exception, time};
use test_macros::kernel_test; use test_macros::kernel_test;
#[no_mangle] #[no_mangle]
unsafe fn kernel_init() -> ! { unsafe fn kernel_init() -> ! {
use driver::interface::DriverManager;
exception::handling_init(); exception::handling_init();
bsp::driver::driver_manager().qemu_bring_up_console(); bsp::driver::qemu_bring_up_console();
// Depending on CPU arch, some timer bring-up code could go here. Not needed for the RPi. // Depending on CPU arch, some timer bring-up code could go here. Not needed for the RPi.

@ -17,15 +17,14 @@
/// or indirectly. /// or indirectly.
mod panic_exit_success; mod panic_exit_success;
use libkernel::{bsp, cpu, driver, exception, info, memory, println}; use libkernel::{bsp, cpu, exception, info, memory, println};
#[no_mangle] #[no_mangle]
unsafe fn kernel_init() -> ! { unsafe fn kernel_init() -> ! {
use driver::interface::DriverManager;
use memory::mmu::interface::MMU; use memory::mmu::interface::MMU;
exception::handling_init(); exception::handling_init();
bsp::driver::driver_manager().qemu_bring_up_console(); bsp::driver::qemu_bring_up_console();
// This line will be printed as the test header. // This line will be printed as the test header.
println!("Testing synchronous exception handling by causing a page fault"); println!("Testing synchronous exception handling by causing a page fault");

@ -12,7 +12,7 @@
mod panic_wait_forever; mod panic_wait_forever;
use core::arch::asm; use core::arch::asm;
use libkernel::{bsp, cpu, driver, exception, info, memory, println}; use libkernel::{bsp, cpu, exception, info, memory, println};
#[inline(never)] #[inline(never)]
fn nested_system_call() { fn nested_system_call() {
@ -30,11 +30,10 @@ fn nested_system_call() {
#[no_mangle] #[no_mangle]
unsafe fn kernel_init() -> ! { unsafe fn kernel_init() -> ! {
use driver::interface::DriverManager;
use memory::mmu::interface::MMU; use memory::mmu::interface::MMU;
exception::handling_init(); exception::handling_init();
bsp::driver::driver_manager().qemu_bring_up_console(); bsp::driver::qemu_bring_up_console();
// This line will be printed as the test header. // This line will be printed as the test header.
println!("Testing exception restore"); println!("Testing exception restore");

@ -10,13 +10,12 @@
#![reexport_test_harness_main = "test_main"] #![reexport_test_harness_main = "test_main"]
#![test_runner(libkernel::test_runner)] #![test_runner(libkernel::test_runner)]
use libkernel::{bsp, cpu, driver, exception}; use libkernel::{bsp, cpu, exception};
use test_macros::kernel_test; use test_macros::kernel_test;
#[no_mangle] #[no_mangle]
unsafe fn kernel_init() -> ! { unsafe fn kernel_init() -> ! {
use driver::interface::DriverManager; bsp::driver::qemu_bring_up_console();
bsp::driver::driver_manager().qemu_bring_up_console();
exception::handling_init(); exception::handling_init();
exception::asynchronous::local_irq_unmask(); exception::asynchronous::local_irq_unmask();

@ -291,7 +291,7 @@ test_boot: $(KERNEL_BIN)
## Helpers for unit and integration test targets ## Helpers for unit and integration test targets
##------------------------------------------------------------------------------ ##------------------------------------------------------------------------------
define KERNEL_TEST_RUNNER define KERNEL_TEST_RUNNER
#!/usr/bin/env bash #!/usr/bin/env bash
# The cargo test runner seems to change into the crate under test's directory. Therefore, ensure # The cargo test runner seems to change into the crate under test's directory. Therefore, ensure
# this script executes from the root. # this script executes from the root.

File diff suppressed because it is too large Load Diff

@ -80,7 +80,8 @@ mod gicc;
mod gicd; mod gicd;
use crate::{ use crate::{
bsp, cpu, driver, exception, bsp::{self, device_driver::common::BoundedUsize},
cpu, driver, exception,
memory::{Address, Virtual}, memory::{Address, Virtual},
synchronization, synchronization,
synchronization::InitStateLock, synchronization::InitStateLock,
@ -90,14 +91,15 @@ use crate::{
// Private Definitions // Private Definitions
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
type HandlerTable = [Option<exception::asynchronous::IRQDescriptor>; IRQNumber::NUM_TOTAL]; type HandlerTable = [Option<exception::asynchronous::IRQHandlerDescriptor<IRQNumber>>;
IRQNumber::MAX_INCLUSIVE + 1];
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
// Public Definitions // Public Definitions
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
/// Used for the associated type of trait [`exception::asynchronous::interface::IRQManager`]. /// Used for the associated type of trait [`exception::asynchronous::interface::IRQManager`].
pub type IRQNumber = exception::asynchronous::IRQNumber<{ GICv2::MAX_IRQ_NUMBER }>; pub type IRQNumber = BoundedUsize<{ GICv2::MAX_IRQ_NUMBER }>;
/// Representation of the GIC. /// Representation of the GIC.
pub struct GICv2 { pub struct GICv2 {
@ -109,9 +111,6 @@ pub struct GICv2 {
/// Stores registered IRQ handlers. Writable only during kernel init. RO afterwards. /// Stores registered IRQ handlers. Writable only during kernel init. RO afterwards.
handler_table: InitStateLock<HandlerTable>, handler_table: InitStateLock<HandlerTable>,
/// Callback to be invoked after successful init.
post_init_callback: fn(),
} }
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
@ -131,13 +130,11 @@ impl GICv2 {
pub const unsafe fn new( pub const unsafe fn new(
gicd_mmio_start_addr: Address<Virtual>, gicd_mmio_start_addr: Address<Virtual>,
gicc_mmio_start_addr: Address<Virtual>, gicc_mmio_start_addr: Address<Virtual>,
post_init_callback: fn(),
) -> Self { ) -> Self {
Self { Self {
gicd: gicd::GICD::new(gicd_mmio_start_addr), gicd: gicd::GICD::new(gicd_mmio_start_addr),
gicc: gicc::GICC::new(gicc_mmio_start_addr), gicc: gicc::GICC::new(gicc_mmio_start_addr),
handler_table: InitStateLock::new([None; IRQNumber::NUM_TOTAL]), handler_table: InitStateLock::new([None; IRQNumber::MAX_INCLUSIVE + 1]),
post_init_callback,
} }
} }
} }
@ -148,6 +145,8 @@ impl GICv2 {
use synchronization::interface::ReadWriteEx; use synchronization::interface::ReadWriteEx;
impl driver::interface::DeviceDriver for GICv2 { impl driver::interface::DeviceDriver for GICv2 {
type IRQNumberType = IRQNumber;
fn compatible(&self) -> &'static str { fn compatible(&self) -> &'static str {
Self::COMPATIBLE Self::COMPATIBLE
} }
@ -160,8 +159,6 @@ impl driver::interface::DeviceDriver for GICv2 {
self.gicc.priority_accept_all(); self.gicc.priority_accept_all();
self.gicc.enable(); self.gicc.enable();
(self.post_init_callback)();
Ok(()) Ok(())
} }
} }
@ -171,23 +168,22 @@ impl exception::asynchronous::interface::IRQManager for GICv2 {
fn register_handler( fn register_handler(
&self, &self,
irq_number: Self::IRQNumberType, irq_handler_descriptor: exception::asynchronous::IRQHandlerDescriptor<Self::IRQNumberType>,
descriptor: exception::asynchronous::IRQDescriptor,
) -> Result<(), &'static str> { ) -> Result<(), &'static str> {
self.handler_table.write(|table| { self.handler_table.write(|table| {
let irq_number = irq_number.get(); let irq_number = irq_handler_descriptor.number().get();
if table[irq_number].is_some() { if table[irq_number].is_some() {
return Err("IRQ handler already registered"); return Err("IRQ handler already registered");
} }
table[irq_number] = Some(descriptor); table[irq_number] = Some(irq_handler_descriptor);
Ok(()) Ok(())
}) })
} }
fn enable(&self, irq_number: Self::IRQNumberType) { fn enable(&self, irq_number: &Self::IRQNumberType) {
self.gicd.enable(irq_number); self.gicd.enable(irq_number);
} }
@ -210,7 +206,7 @@ impl exception::asynchronous::interface::IRQManager for GICv2 {
None => panic!("No handler registered for IRQ {}", irq_number), None => panic!("No handler registered for IRQ {}", irq_number),
Some(descriptor) => { Some(descriptor) => {
// Call the IRQ handler. Panics on failure. // Call the IRQ handler. Panics on failure.
descriptor.handler.handle().expect("Error handling IRQ"); descriptor.handler().handle().expect("Error handling IRQ");
} }
} }
}); });
@ -227,7 +223,7 @@ impl exception::asynchronous::interface::IRQManager for GICv2 {
self.handler_table.read(|table| { self.handler_table.read(|table| {
for (i, opt) in table.iter().skip(32).enumerate() { for (i, opt) in table.iter().skip(32).enumerate() {
if let Some(handler) = opt { if let Some(handler) = opt {
info!(" {: >3}. {}", i + 32, handler.name); info!(" {: >3}. {}", i + 32, handler.name());
} }
} }
}); });

@ -172,7 +172,7 @@ impl GICD {
} }
/// Enable an interrupt. /// Enable an interrupt.
pub fn enable(&self, irq_num: super::IRQNumber) { pub fn enable(&self, irq_num: &super::IRQNumber) {
let irq_num = irq_num.get(); let irq_num = irq_num.get();
// Each bit in the u32 enable register corresponds to one IRQ number. Shift right by 5 // Each bit in the u32 enable register corresponds to one IRQ number. Shift right by 5

@ -7,6 +7,7 @@
use crate::{ use crate::{
bsp::device_driver::common::MMIODerefWrapper, bsp::device_driver::common::MMIODerefWrapper,
driver, driver,
exception::asynchronous::IRQNumber,
memory::{Address, Virtual}, memory::{Address, Virtual},
synchronization, synchronization,
synchronization::IRQSafeNullLock, synchronization::IRQSafeNullLock,
@ -122,7 +123,6 @@ struct GPIOInner {
/// Representation of the GPIO HW. /// Representation of the GPIO HW.
pub struct GPIO { pub struct GPIO {
inner: IRQSafeNullLock<GPIOInner>, inner: IRQSafeNullLock<GPIOInner>,
post_init_callback: fn(),
} }
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
@ -202,10 +202,9 @@ impl GPIO {
/// # Safety /// # Safety
/// ///
/// - The user must ensure to provide a correct MMIO start address. /// - The user must ensure to provide a correct MMIO start address.
pub const unsafe fn new(mmio_start_addr: Address<Virtual>, post_init_callback: fn()) -> Self { pub const unsafe fn new(mmio_start_addr: Address<Virtual>) -> Self {
Self { Self {
inner: IRQSafeNullLock::new(GPIOInner::new(mmio_start_addr)), inner: IRQSafeNullLock::new(GPIOInner::new(mmio_start_addr)),
post_init_callback,
} }
} }
@ -221,13 +220,9 @@ impl GPIO {
use synchronization::interface::Mutex; use synchronization::interface::Mutex;
impl driver::interface::DeviceDriver for GPIO { impl driver::interface::DeviceDriver for GPIO {
type IRQNumberType = IRQNumber;
fn compatible(&self) -> &'static str { fn compatible(&self) -> &'static str {
Self::COMPATIBLE Self::COMPATIBLE
} }
unsafe fn init(&self) -> Result<(), &'static str> {
(self.post_init_callback)();
Ok(())
}
} }

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

Loading…
Cancel
Save