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
- Now that we enabled safe globals in the previous tutorial, the infrastructure is laid for adding
the first real device drivers.
- We throw out the magic QEMU console and use a real `UART` now. Like serious embedded hackers do!
## Notable additions
- For the first time, we will be able to run the code on the real hardware.
- Therefore, building is now differentiated between the **RPi 3** and the **RPi4**.
- 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:
- `BSP=rpi4 make`
- `BSP=rpi4 make doc`
- Unfortunately, QEMU does not yet support the **RPi4**, so `BSP=rpi4 make qemu` won't work.
- A `driver::interface::DeviceDriver` trait is added for abstracting `BSP` driver implementations
from kernel code.
- Drivers are stored in `src/bsp/device_driver`, and can be reused between `BSP`s.
- We introduce the `GPIO` driver, which pinmuxes (that is, routing signals from inside the `SoC`
to actual HW pins) the RPi's PL011 UART.
- Note how this driver differentiates between **RPi 3** and **RPi4**. Their HW is different,
so we have to account for it in SW.
- Most importantly, the `PL011Uart` driver: It implements the `console::interface::*` traits and
is from now on used as the main system console output.
- `BSP`s now contain a memory map in `src/bsp/raspberrypi/memory.rs`. In the specific case, they
contain the Raspberry's `MMIO` addresses which are used to instantiate the respective device
drivers.
- Drivers for the real `UART` and the `GPIO` controller are added.
- **For the first time, we will be able to run the code on the real hardware** (scroll down for
instructions).
## Introduction
Now that we enabled safe globals in the previous tutorial, the infrastructure is laid for adding the
first real device drivers. We throw out the magic QEMU console and introduce a `driver manager`,
which allows the `BSP` to register device drivers with the `kernel`.
## Driver Manager
The first step consists of adding a `driver subsystem` to the kernel. The corresponding code will
live in `src/driver.rs`. The subsystem introduces `interface::DeviceDriver`, a common trait that
every device driver will need to implement and that is known to the kernel. A global
`DRIVER_MANAGER` instance (of type `DriverManager`) that is instantiated in the same file serves as
the central entity that can be called to manage all things device drivers in the kernel. For
example, by using the globally accessible `crate::driver::driver_manager().register_driver(...)`,
any code can can register an object with static lifetime that implements the
`interface::DeviceDriver` trait.
During kernel init, a call to `crate::driver::driver_manager().init_drivers(...)` will let the
driver manager loop over all registered drivers and kick off their initialization, and also execute
an optional `post-init callback` that can be registered alongside the driver. For example, this
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
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
@ -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
--- 04_safe_globals/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
+//
+// 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.
+
+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.
+struct BSPDriverManager {
+ device_drivers: [&'static (dyn DeviceDriver + Sync); 2],
+}
+static PL011_UART: device_driver::PL011Uart =
+ unsafe { device_driver::PL011Uart::new(mmio::PL011_UART_START) };
+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 =
+ unsafe { device_driver::PL011Uart::new(mmio::PL011_UART_START) };
+/// This must be called only after successful init of the UART driver.
+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 {
+ device_drivers: [&PL011_UART, &GPIO],
+};
+/// This must be called only after successful init of the GPIO driver.
+fn post_init_gpio() -> Result<(), &'static str> {
+ GPIO.map_pl011_uart();
+ Ok(())
+}
+
+//--------------------------------------------------------------------------------------------------
+// Public Code
+//--------------------------------------------------------------------------------------------------
+fn driver_uart() -> Result<(), &'static str> {
+ 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.
+pub fn driver_manager() -> &'static impl driver::interface::DriverManager {
+ &BSP_DRIVER_MANAGER
+ Ok(())
+}
+
+//------------------------------------------------------------------------------
+// OS Interface Code
+//------------------------------------------------------------------------------
+use driver::interface::DeviceDriver;
+fn driver_gpio() -> Result<(), &'static str> {
+ let gpio_descriptor = generic_driver::DeviceDriverDescriptor::new(&GPIO, Some(post_init_gpio));
+ generic_driver::driver_manager().register_driver(gpio_descriptor);
+
+impl driver::interface::DriverManager for BSPDriverManager {
+ fn all_device_drivers(&self) -> &[&'static (dyn DeviceDriver + Sync)] {
+ &self.device_drivers[..]
+ }
+ Ok(())
+}
+
+ fn post_device_driver_init(&self) {
+ // Configure PL011Uart's output pins.
+ GPIO.map_pl011_uart();
+//--------------------------------------------------------------------------------------------------
+// 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");
+ }
+
+ 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
@ -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
--- 04_safe_globals/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 driver;
+pub mod memory;
@ -1211,10 +1275,67 @@ diff -uNr 04_safe_globals/src/bsp.rs 05_drivers_gpio_uart/src/bsp.rs
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
--- 04_safe_globals/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.
pub trait Write {
@ -1240,7 +1361,7 @@ diff -uNr 04_safe_globals/src/console.rs 05_drivers_gpio_uart/src/console.rs
}
/// Console statistics.
@@ -26,10 +43,15 @@
@@ -26,19 +45,37 @@
fn chars_written(&self) -> usize {
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
--- 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
--- 04_safe_globals/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
+//
+// Copyright (c) 2018-2022 Andre Richter <andre.o.richter@gmail.com>
+
+//! 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
+//--------------------------------------------------------------------------------------------------
@ -1301,22 +1462,129 @@ diff -uNr 04_safe_globals/src/driver.rs 05_drivers_gpio_uart/src/driver.rs
+ 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.
+ pub trait DriverManager {
+ /// Return a slice of references to all `BSP`-instantiated drivers.
+ ///
+ /// # Safety
+ ///
+ /// - The order of devices is the order in which `DeviceDriver::init()` is called.
+ fn all_device_drivers(&self) -> &[&'static (dyn DeviceDriver + Sync)];
+ /// # Safety
+ ///
+ /// - During init, drivers might do stuff with system-wide impact.
+ pub unsafe fn init_drivers(&self) {
+ self.for_each_descriptor(|descriptor| {
+ // 1. Initialize driver.
+ if let Err(x) = descriptor.device_driver.init() {
+ panic!(
+ "Error initializing driver: {}: {}",
+ descriptor.device_driver.compatible(),
+ x
+ );
+ }
+
+ /// Initialization code that runs after driver init.
+ ///
+ /// For example, device driver code that depends on other drivers already being online.
+ fn post_device_driver_init(&self);
+ // 2. Call corresponding post init callback.
+ if let Some(callback) = &descriptor.post_init_callback {
+ if let Err(x) = callback() {
+ 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 print;
mod synchronization;
@@ -125,13 +127,50 @@
@@ -125,13 +127,42 @@
/// # Safety
///
/// - Only a single core must be active and running this function.
+/// - The init calls in this function must appear in the correct order.
unsafe fn kernel_init() -> ! {
- use console::console;
+ use driver::interface::DriverManager;
+
+ for i in bsp::driver::driver_manager().all_device_drivers().iter() {
+ if let Err(x) = i.init() {
+ panic!("Error loading driver: {}: {}", i.compatible(), x);
+ }
+ // Initialize the BSP driver subsystem.
+ if let Err(x) = bsp::driver::init() {
+ 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!("[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.
+fn kernel_main() -> ! {
+ use console::console;
+ use driver::interface::DriverManager;
- println!("[2] Stopping here.");
- 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!("[2] Drivers loaded:");
+ for (i, driver) in bsp::driver::driver_manager()
+ .all_device_drivers()
+ .iter()
+ .enumerate()
+ {
+ println!(" {}. {}", i + 1, driver.compatible());
+ }
+ driver::driver_manager().enumerate();
+
+ println!("[3] Chars written: {}", console().chars_written());
+ println!("[4] Echoing input now");

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

@ -5,51 +5,67 @@
//! BSP driver support.
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.
struct BSPDriverManager {
device_drivers: [&'static (dyn DeviceDriver + Sync); 2],
}
static PL011_UART: device_driver::PL011Uart =
unsafe { device_driver::PL011Uart::new(mmio::PL011_UART_START) };
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 =
unsafe { device_driver::PL011Uart::new(mmio::PL011_UART_START) };
/// This must be called only after successful init of the UART driver.
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 {
device_drivers: [&PL011_UART, &GPIO],
};
/// This must be called only after successful init of the GPIO driver.
fn post_init_gpio() -> Result<(), &'static str> {
GPIO.map_pl011_uart();
Ok(())
}
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
fn driver_uart() -> Result<(), &'static str> {
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.
pub fn driver_manager() -> &'static impl driver::interface::DriverManager {
&BSP_DRIVER_MANAGER
Ok(())
}
//------------------------------------------------------------------------------
// OS Interface Code
//------------------------------------------------------------------------------
use driver::interface::DeviceDriver;
fn driver_gpio() -> Result<(), &'static str> {
let gpio_descriptor = generic_driver::DeviceDriverDescriptor::new(&GPIO, Some(post_init_gpio));
generic_driver::driver_manager().register_driver(gpio_descriptor);
impl driver::interface::DriverManager for BSPDriverManager {
fn all_device_drivers(&self) -> &[&'static (dyn DeviceDriver + Sync)] {
&self.device_drivers[..]
}
Ok(())
}
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
fn post_device_driver_init(&self) {
// Configure PL011Uart's output pins.
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(())
}

@ -4,7 +4,9 @@
//! System console.
use crate::bsp;
mod null_console;
use crate::synchronization::{self, NullLock};
//--------------------------------------------------------------------------------------------------
// Public Definitions
@ -54,13 +56,26 @@ pub mod interface {
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
//--------------------------------------------------------------------------------------------------
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)
}

@ -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.
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
//--------------------------------------------------------------------------------------------------
@ -24,21 +40,128 @@ pub mod interface {
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.
pub trait DriverManager {
/// Return a slice of references to all `BSP`-instantiated drivers.
///
/// # Safety
///
/// - The order of devices is the order in which `DeviceDriver::init()` is called.
fn all_device_drivers(&self) -> &[&'static (dyn DeviceDriver + Sync)];
/// # Safety
///
/// - During init, drivers might do stuff with system-wide impact.
pub unsafe fn init_drivers(&self) {
self.for_each_descriptor(|descriptor| {
// 1. Initialize driver.
if let Err(x) = descriptor.device_driver.init() {
panic!(
"Error initializing driver: {}: {}",
descriptor.device_driver.compatible(),
x
);
}
/// Initialization code that runs after driver init.
///
/// For example, device driver code that depends on other drivers already being online.
fn post_device_driver_init(&self);
// 2. Call corresponding post init callback.
if let Some(callback) = &descriptor.post_init_callback {
if let Err(x) = callback() {
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.
/// - The init calls in this function must appear in the correct order.
unsafe fn kernel_init() -> ! {
use driver::interface::DriverManager;
for i in bsp::driver::driver_manager().all_device_drivers().iter() {
if let Err(x) = i.init() {
panic!("Error loading driver: {}: {}", i.compatible(), x);
}
// Initialize the BSP driver subsystem.
if let Err(x) = bsp::driver::init() {
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.
// Transition from unsafe to safe.
@ -146,7 +145,6 @@ unsafe fn kernel_init() -> ! {
/// The main function running after the early init.
fn kernel_main() -> ! {
use console::console;
use driver::interface::DriverManager;
println!(
"[0] {} version {}",
@ -156,13 +154,7 @@ fn kernel_main() -> ! {
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());
}
driver::driver_manager().enumerate();
println!("[3] Chars written: {}", console().chars_written());
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
--- 05_drivers_gpio_uart/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 _
+}
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
--- 05_drivers_gpio_uart/src/main.rs
+++ 06_uart_chainloader/src/main.rs
@@ -143,34 +143,55 @@
@@ -142,27 +142,55 @@
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.
fn kernel_main() -> ! {
use console::console;
- use driver::interface::DriverManager;
- println!(
- "[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")
- );
- 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!("{:^37}", bsp::board_name());
+ println!();
+ println!("[ML] Requesting binary");
+ console().flush();
+
- println!("[2] Drivers loaded:");
- driver::driver_manager().enumerate();
+ // Discard any spurious received characters before starting with the loader protocol.
+ 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!("[4] Echoing input now");
+ // 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;
+ // Notify `Minipush` to send the binary.
+ for _ in 0..3 {
+ console().write_char(3 as char);
+ }
- // Discard any spurious received characters before going into echo mode.
- console().clear_rx();
- loop {
- let c = console().read_char();
- 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.
+ console().write_char('O');
+ console().write_char('K');

@ -4,7 +4,6 @@
//! Top-level BSP file for the Raspberry Pi 3 and 4.
pub mod console;
pub mod cpu;
pub mod driver;
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.
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.
struct BSPDriverManager {
device_drivers: [&'static (dyn DeviceDriver + Sync); 2],
}
static PL011_UART: device_driver::PL011Uart =
unsafe { device_driver::PL011Uart::new(mmio::PL011_UART_START) };
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 =
unsafe { device_driver::PL011Uart::new(mmio::PL011_UART_START) };
/// This must be called only after successful init of the UART driver.
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 {
device_drivers: [&PL011_UART, &GPIO],
};
/// This must be called only after successful init of the GPIO driver.
fn post_init_gpio() -> Result<(), &'static str> {
GPIO.map_pl011_uart();
Ok(())
}
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
fn driver_uart() -> Result<(), &'static str> {
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.
pub fn driver_manager() -> &'static impl driver::interface::DriverManager {
&BSP_DRIVER_MANAGER
Ok(())
}
//------------------------------------------------------------------------------
// OS Interface Code
//------------------------------------------------------------------------------
use driver::interface::DeviceDriver;
fn driver_gpio() -> Result<(), &'static str> {
let gpio_descriptor = generic_driver::DeviceDriverDescriptor::new(&GPIO, Some(post_init_gpio));
generic_driver::driver_manager().register_driver(gpio_descriptor);
impl driver::interface::DriverManager for BSPDriverManager {
fn all_device_drivers(&self) -> &[&'static (dyn DeviceDriver + Sync)] {
&self.device_drivers[..]
}
Ok(())
}
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
fn post_device_driver_init(&self) {
// Configure PL011Uart's output pins.
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(())
}

@ -4,7 +4,9 @@
//! System console.
use crate::bsp;
mod null_console;
use crate::synchronization::{self, NullLock};
//--------------------------------------------------------------------------------------------------
// Public Definitions
@ -54,13 +56,26 @@ pub mod interface {
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
//--------------------------------------------------------------------------------------------------
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)
}

@ -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.
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
//--------------------------------------------------------------------------------------------------
@ -24,21 +37,118 @@ pub mod interface {
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.
pub trait DriverManager {
/// Return a slice of references to all `BSP`-instantiated drivers.
///
/// # Safety
///
/// - The order of devices is the order in which `DeviceDriver::init()` is called.
fn all_device_drivers(&self) -> &[&'static (dyn DeviceDriver + Sync)];
/// # Safety
///
/// - During init, drivers might do stuff with system-wide impact.
pub unsafe fn init_drivers(&self) {
self.for_each_descriptor(|descriptor| {
// 1. Initialize driver.
if let Err(x) = descriptor.device_driver.init() {
panic!(
"Error initializing driver: {}: {}",
descriptor.device_driver.compatible(),
x
);
}
/// Initialization code that runs after driver init.
///
/// For example, device driver code that depends on other drivers already being online.
fn post_device_driver_init(&self);
// 2. Call corresponding post init callback.
if let Some(callback) = &descriptor.post_init_callback {
if let Err(x) = callback() {
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.
/// - The init calls in this function must appear in the correct order.
unsafe fn kernel_init() -> ! {
use driver::interface::DriverManager;
for i in bsp::driver::driver_manager().all_device_drivers().iter() {
if let Err(x) = i.init() {
panic!("Error loading driver: {}: {}", i.compatible(), x);
}
// Initialize the BSP driver subsystem.
if let Err(x) = bsp::driver::init() {
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.
// 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
--- 06_uart_chainloader/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")]
-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
--- 06_uart_chainloader/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.
///
@@ -143,55 +147,37 @@
@@ -142,55 +146,30 @@
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.
fn kernel_main() -> ! {
- use console::console;
+ use core::time::Duration;
+ use driver::interface::DriverManager;
-
- println!("{}", MINILOAD_LOGO);
- println!("{:^37}", bsp::board_name());
- println!();
@ -633,26 +684,8 @@ diff -uNr 06_uart_chainloader/src/main.rs 07_timestamps/src/main.rs
- // Notify `Minipush` to send the binary.
- for _ in 0..3 {
- console().write_char(3 as char);
+ 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:");
+ for (i, driver) in bsp::driver::driver_manager()
+ .all_device_drivers()
+ .iter()
+ .enumerate()
+ {
+ info!(" {}. {}", i + 1, driver.compatible());
}
- }
+ use core::time::Duration;
- // Read the binary's size.
- 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 {
- 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.
+ 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");
- console().flush();
-
@ -682,10 +734,6 @@ diff -uNr 06_uart_chainloader/src/main.rs 07_timestamps/src/main.rs
-
- // Jump to loaded 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

@ -4,7 +4,6 @@
//! Top-level BSP file for the Raspberry Pi 3 and 4.
pub mod console;
pub mod cpu;
pub mod driver;
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.
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.
struct BSPDriverManager {
device_drivers: [&'static (dyn DeviceDriver + Sync); 2],
}
static PL011_UART: device_driver::PL011Uart =
unsafe { device_driver::PL011Uart::new(mmio::PL011_UART_START) };
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 =
unsafe { device_driver::PL011Uart::new(mmio::PL011_UART_START) };
/// This must be called only after successful init of the UART driver.
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 {
device_drivers: [&PL011_UART, &GPIO],
};
/// This must be called only after successful init of the GPIO driver.
fn post_init_gpio() -> Result<(), &'static str> {
GPIO.map_pl011_uart();
Ok(())
}
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
fn driver_uart() -> Result<(), &'static str> {
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.
pub fn driver_manager() -> &'static impl driver::interface::DriverManager {
&BSP_DRIVER_MANAGER
Ok(())
}
//------------------------------------------------------------------------------
// OS Interface Code
//------------------------------------------------------------------------------
use driver::interface::DeviceDriver;
fn driver_gpio() -> Result<(), &'static str> {
let gpio_descriptor = generic_driver::DeviceDriverDescriptor::new(&GPIO, Some(post_init_gpio));
generic_driver::driver_manager().register_driver(gpio_descriptor);
impl driver::interface::DriverManager for BSPDriverManager {
fn all_device_drivers(&self) -> &[&'static (dyn DeviceDriver + Sync)] {
&self.device_drivers[..]
}
Ok(())
}
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
fn post_device_driver_init(&self) {
// Configure PL011Uart's output pins.
GPIO.map_pl011_uart();
/// Initialize the driver subsystem.
///
/// # 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.
use crate::bsp;
mod null_console;
use crate::synchronization::{self, NullLock};
//--------------------------------------------------------------------------------------------------
// Public Definitions
@ -54,13 +56,26 @@ pub mod interface {
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
//--------------------------------------------------------------------------------------------------
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)
}

@ -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.
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
//--------------------------------------------------------------------------------------------------
@ -24,21 +40,128 @@ pub mod interface {
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.
pub trait DriverManager {
/// Return a slice of references to all `BSP`-instantiated drivers.
///
/// # Safety
///
/// - The order of devices is the order in which `DeviceDriver::init()` is called.
fn all_device_drivers(&self) -> &[&'static (dyn DeviceDriver + Sync)];
/// # Safety
///
/// - During init, drivers might do stuff with system-wide impact.
pub unsafe fn init_drivers(&self) {
self.for_each_descriptor(|descriptor| {
// 1. Initialize driver.
if let Err(x) = descriptor.device_driver.init() {
panic!(
"Error initializing driver: {}: {}",
descriptor.device_driver.compatible(),
x
);
}
/// Initialization code that runs after driver init.
///
/// For example, device driver code that depends on other drivers already being online.
fn post_device_driver_init(&self);
// 2. Call corresponding post init callback.
if let Some(callback) = &descriptor.post_init_callback {
if let Err(x) = callback() {
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.
/// - The init calls in this function must appear in the correct order.
unsafe fn kernel_init() -> ! {
use driver::interface::DriverManager;
for i in bsp::driver::driver_manager().all_device_drivers().iter() {
if let Err(x) = i.init() {
panic!("Error loading driver: {}: {}", i.compatible(), x);
}
// Initialize the BSP driver subsystem.
if let Err(x) = bsp::driver::init() {
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.
// Transition from unsafe to safe.
@ -150,7 +149,6 @@ unsafe fn kernel_init() -> ! {
/// The main function running after the early init.
fn kernel_main() -> ! {
use core::time::Duration;
use driver::interface::DriverManager;
info!(
"{} version {}",
@ -165,13 +163,7 @@ fn kernel_main() -> ! {
);
info!("Drivers loaded:");
for (i, driver) in bsp::driver::driver_manager()
.all_device_drivers()
.iter()
.enumerate()
{
info!(" {}. {}", i + 1, driver.compatible());
}
driver::driver_manager().enumerate();
// Test a failing timer case.
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
##--------------------------------------------------------------------------------------------------
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.
pub mod console;
pub mod cpu;
pub mod driver;
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.
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.
struct BSPDriverManager {
device_drivers: [&'static (dyn DeviceDriver + Sync); 2],
}
static PL011_UART: device_driver::PL011Uart =
unsafe { device_driver::PL011Uart::new(mmio::PL011_UART_START) };
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 =
unsafe { device_driver::PL011Uart::new(mmio::PL011_UART_START) };
/// This must be called only after successful init of the UART driver.
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 {
device_drivers: [&PL011_UART, &GPIO],
};
/// This must be called only after successful init of the GPIO driver.
fn post_init_gpio() -> Result<(), &'static str> {
GPIO.map_pl011_uart();
Ok(())
}
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
fn driver_uart() -> Result<(), &'static str> {
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.
pub fn driver_manager() -> &'static impl driver::interface::DriverManager {
&BSP_DRIVER_MANAGER
Ok(())
}
//------------------------------------------------------------------------------
// OS Interface Code
//------------------------------------------------------------------------------
use driver::interface::DeviceDriver;
fn driver_gpio() -> Result<(), &'static str> {
let gpio_descriptor = generic_driver::DeviceDriverDescriptor::new(&GPIO, Some(post_init_gpio));
generic_driver::driver_manager().register_driver(gpio_descriptor);
impl driver::interface::DriverManager for BSPDriverManager {
fn all_device_drivers(&self) -> &[&'static (dyn DeviceDriver + Sync)] {
&self.device_drivers[..]
}
Ok(())
}
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
fn post_device_driver_init(&self) {
// Configure PL011Uart's output pins.
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(())
}

@ -4,7 +4,9 @@
//! System console.
use crate::bsp;
mod null_console;
use crate::synchronization::{self, NullLock};
//--------------------------------------------------------------------------------------------------
// Public Definitions
@ -54,13 +56,26 @@ pub mod interface {
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
//--------------------------------------------------------------------------------------------------
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)
}

@ -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.
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
//--------------------------------------------------------------------------------------------------
@ -24,21 +40,128 @@ pub mod interface {
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.
pub trait DriverManager {
/// Return a slice of references to all `BSP`-instantiated drivers.
///
/// # Safety
///
/// - The order of devices is the order in which `DeviceDriver::init()` is called.
fn all_device_drivers(&self) -> &[&'static (dyn DeviceDriver + Sync)];
/// # Safety
///
/// - During init, drivers might do stuff with system-wide impact.
pub unsafe fn init_drivers(&self) {
self.for_each_descriptor(|descriptor| {
// 1. Initialize driver.
if let Err(x) = descriptor.device_driver.init() {
panic!(
"Error initializing driver: {}: {}",
descriptor.device_driver.compatible(),
x
);
}
/// Initialization code that runs after driver init.
///
/// For example, device driver code that depends on other drivers already being online.
fn post_device_driver_init(&self);
// 2. Call corresponding post init callback.
if let Some(callback) = &descriptor.post_init_callback {
if let Err(x) = callback() {
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.
/// - The init calls in this function must appear in the correct order.
unsafe fn kernel_init() -> ! {
use driver::interface::DriverManager;
for i in bsp::driver::driver_manager().all_device_drivers().iter() {
if let Err(x) = i.init() {
panic!("Error loading driver: {}: {}", i.compatible(), x);
}
// Initialize the BSP driver subsystem.
if let Err(x) = bsp::driver::init() {
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.
// Transition from unsafe to safe.
@ -150,7 +149,6 @@ unsafe fn kernel_init() -> ! {
/// The main function running after the early init.
fn kernel_main() -> ! {
use core::time::Duration;
use driver::interface::DriverManager;
info!(
"{} version {}",
@ -165,13 +163,7 @@ fn kernel_main() -> ! {
);
info!("Drivers loaded:");
for (i, driver) in bsp::driver::driver_manager()
.all_device_drivers()
.iter()
.enumerate()
{
info!(" {}. {}", i + 1, driver.compatible());
}
driver::driver_manager().enumerate();
// Test a failing timer case.
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 print;
mod synchronization;
@@ -149,6 +150,7 @@
@@ -148,6 +149,7 @@
/// The main function running after the early init.
fn kernel_main() -> ! {
+ use console::console;
use core::time::Duration;
use driver::interface::DriverManager;
@@ -159,6 +161,12 @@
info!(
@@ -157,6 +159,12 @@
);
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!(
"Architectural timer resolution: {} ns",
time::time_manager().resolution().as_nanos()
@@ -173,11 +181,15 @@
info!(" {}. {}", i + 1, driver.compatible());
}
@@ -165,11 +173,15 @@
info!("Drivers loaded:");
driver::driver_manager().enumerate();
- // Test a failing timer case.
- time::time_manager().spin_for(Duration::from_nanos(1));

@ -4,7 +4,6 @@
//! Top-level BSP file for the Raspberry Pi 3 and 4.
pub mod console;
pub mod cpu;
pub mod driver;
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.
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.
struct BSPDriverManager {
device_drivers: [&'static (dyn DeviceDriver + Sync); 2],
}
static PL011_UART: device_driver::PL011Uart =
unsafe { device_driver::PL011Uart::new(mmio::PL011_UART_START) };
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 =
unsafe { device_driver::PL011Uart::new(mmio::PL011_UART_START) };
/// This must be called only after successful init of the UART driver.
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 {
device_drivers: [&PL011_UART, &GPIO],
};
/// This must be called only after successful init of the GPIO driver.
fn post_init_gpio() -> Result<(), &'static str> {
GPIO.map_pl011_uart();
Ok(())
}
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
fn driver_uart() -> Result<(), &'static str> {
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.
pub fn driver_manager() -> &'static impl driver::interface::DriverManager {
&BSP_DRIVER_MANAGER
Ok(())
}
//------------------------------------------------------------------------------
// OS Interface Code
//------------------------------------------------------------------------------
use driver::interface::DeviceDriver;
fn driver_gpio() -> Result<(), &'static str> {
let gpio_descriptor = generic_driver::DeviceDriverDescriptor::new(&GPIO, Some(post_init_gpio));
generic_driver::driver_manager().register_driver(gpio_descriptor);
impl driver::interface::DriverManager for BSPDriverManager {
fn all_device_drivers(&self) -> &[&'static (dyn DeviceDriver + Sync)] {
&self.device_drivers[..]
}
Ok(())
}
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
fn post_device_driver_init(&self) {
// Configure PL011Uart's output pins.
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(())
}

@ -4,7 +4,9 @@
//! System console.
use crate::bsp;
mod null_console;
use crate::synchronization::{self, NullLock};
//--------------------------------------------------------------------------------------------------
// Public Definitions
@ -54,13 +56,26 @@ pub mod interface {
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
//--------------------------------------------------------------------------------------------------
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)
}

@ -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.
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
//--------------------------------------------------------------------------------------------------
@ -24,21 +40,128 @@ pub mod interface {
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.
pub trait DriverManager {
/// Return a slice of references to all `BSP`-instantiated drivers.
///
/// # Safety
///
/// - The order of devices is the order in which `DeviceDriver::init()` is called.
fn all_device_drivers(&self) -> &[&'static (dyn DeviceDriver + Sync)];
/// # Safety
///
/// - During init, drivers might do stuff with system-wide impact.
pub unsafe fn init_drivers(&self) {
self.for_each_descriptor(|descriptor| {
// 1. Initialize driver.
if let Err(x) = descriptor.device_driver.init() {
panic!(
"Error initializing driver: {}: {}",
descriptor.device_driver.compatible(),
x
);
}
/// Initialization code that runs after driver init.
///
/// For example, device driver code that depends on other drivers already being online.
fn post_device_driver_init(&self);
// 2. Call corresponding post init callback.
if let Some(callback) = &descriptor.post_init_callback {
if let Err(x) = callback() {
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.
/// - The init calls in this function must appear in the correct order.
unsafe fn kernel_init() -> ! {
use driver::interface::DriverManager;
for i in bsp::driver::driver_manager().all_device_drivers().iter() {
if let Err(x) = i.init() {
panic!("Error loading driver: {}: {}", i.compatible(), x);
}
// Initialize the BSP driver subsystem.
if let Err(x) = bsp::driver::init() {
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.
// Transition from unsafe to safe.
@ -152,7 +151,6 @@ unsafe fn kernel_init() -> ! {
fn kernel_main() -> ! {
use console::console;
use core::time::Duration;
use driver::interface::DriverManager;
info!(
"{} version {}",
@ -173,13 +171,7 @@ fn kernel_main() -> ! {
);
info!("Drivers loaded:");
for (i, driver) in bsp::driver::driver_manager()
.all_device_drivers()
.iter()
.enumerate()
{
info!(" {}. {}", i + 1, driver.compatible());
}
driver::driver_manager().enumerate();
info!("Timer test, spinning for 1 second");
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
unsafe fn kernel_init() -> ! {
use driver::interface::DriverManager;
use memory::mmu::interface::MMU;
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 print;
mod synchronization;
@@ -132,9 +137,17 @@
@@ -132,8 +137,17 @@
/// # Safety
///
/// - 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
+/// NullLocks instead of spinlocks), will fail to work (properly) on the RPi SoCs.
unsafe fn kernel_init() -> ! {
use driver::interface::DriverManager;
+ use memory::mmu::interface::MMU;
+
+ if let Err(string) = memory::mmu::mmu().enable_mmu_and_caching() {
+ panic!("MMU: {}", string);
+ }
for i in bsp::driver::driver_manager().all_device_drivers().iter() {
if let Err(x) = i.init() {
@@ -150,7 +163,7 @@
+
// Initialize the BSP driver subsystem.
if let Err(x) = bsp::driver::init() {
panic!("Error initializing BSP driver subsystem: {}", x);
@@ -149,7 +163,7 @@
/// The main function running after the early init.
fn kernel_main() -> ! {
- use console::console;
+ use console::{console, interface::Write};
use core::time::Duration;
use driver::interface::DriverManager;
@@ -161,6 +174,9 @@
info!(
@@ -159,6 +173,9 @@
);
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();
info!("Current privilege level: {}", privilege_level);
@@ -184,6 +200,13 @@
@@ -176,6 +193,13 @@
info!("Timer test, spinning for 1 second");
time::time_manager().spin_for(Duration::from_secs(1));

@ -4,7 +4,6 @@
//! Top-level BSP file for the Raspberry Pi 3 and 4.
pub mod console;
pub mod cpu;
pub mod driver;
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.
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.
struct BSPDriverManager {
device_drivers: [&'static (dyn DeviceDriver + Sync); 2],
}
static PL011_UART: device_driver::PL011Uart =
unsafe { device_driver::PL011Uart::new(mmio::PL011_UART_START) };
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 =
unsafe { device_driver::PL011Uart::new(mmio::PL011_UART_START) };
/// This must be called only after successful init of the UART driver.
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 {
device_drivers: [&PL011_UART, &GPIO],
};
/// This must be called only after successful init of the GPIO driver.
fn post_init_gpio() -> Result<(), &'static str> {
GPIO.map_pl011_uart();
Ok(())
}
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
fn driver_uart() -> Result<(), &'static str> {
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.
pub fn driver_manager() -> &'static impl driver::interface::DriverManager {
&BSP_DRIVER_MANAGER
Ok(())
}
//------------------------------------------------------------------------------
// OS Interface Code
//------------------------------------------------------------------------------
use driver::interface::DeviceDriver;
fn driver_gpio() -> Result<(), &'static str> {
let gpio_descriptor = generic_driver::DeviceDriverDescriptor::new(&GPIO, Some(post_init_gpio));
generic_driver::driver_manager().register_driver(gpio_descriptor);
impl driver::interface::DriverManager for BSPDriverManager {
fn all_device_drivers(&self) -> &[&'static (dyn DeviceDriver + Sync)] {
&self.device_drivers[..]
}
Ok(())
}
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
fn post_device_driver_init(&self) {
// Configure PL011Uart's output pins.
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(())
}

@ -4,7 +4,9 @@
//! System console.
use crate::bsp;
mod null_console;
use crate::synchronization::{self, NullLock};
//--------------------------------------------------------------------------------------------------
// Public Definitions
@ -54,13 +56,26 @@ pub mod interface {
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
//--------------------------------------------------------------------------------------------------
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)
}

@ -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.
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
//--------------------------------------------------------------------------------------------------
@ -24,21 +40,128 @@ pub mod interface {
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.
pub trait DriverManager {
/// Return a slice of references to all `BSP`-instantiated drivers.
///
/// # Safety
///
/// - The order of devices is the order in which `DeviceDriver::init()` is called.
fn all_device_drivers(&self) -> &[&'static (dyn DeviceDriver + Sync)];
/// # Safety
///
/// - During init, drivers might do stuff with system-wide impact.
pub unsafe fn init_drivers(&self) {
self.for_each_descriptor(|descriptor| {
// 1. Initialize driver.
if let Err(x) = descriptor.device_driver.init() {
panic!(
"Error initializing driver: {}: {}",
descriptor.device_driver.compatible(),
x
);
}
/// Initialization code that runs after driver init.
///
/// For example, device driver code that depends on other drivers already being online.
fn post_device_driver_init(&self);
// 2. Call corresponding post init callback.
if let Some(callback) = &descriptor.post_init_callback {
if let Err(x) = callback() {
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
/// NullLocks instead of spinlocks), will fail to work (properly) on the RPi SoCs.
unsafe fn kernel_init() -> ! {
use driver::interface::DriverManager;
use memory::mmu::interface::MMU;
if let Err(string) = memory::mmu::mmu().enable_mmu_and_caching() {
panic!("MMU: {}", string);
}
for i in bsp::driver::driver_manager().all_device_drivers().iter() {
if let Err(x) = i.init() {
panic!("Error loading driver: {}: {}", i.compatible(), x);
}
// Initialize the BSP driver subsystem.
if let Err(x) = bsp::driver::init() {
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.
// Transition from unsafe to safe.
@ -165,7 +165,6 @@ unsafe fn kernel_init() -> ! {
fn kernel_main() -> ! {
use console::{console, interface::Write};
use core::time::Duration;
use driver::interface::DriverManager;
info!(
"{} version {}",
@ -189,13 +188,7 @@ fn kernel_main() -> ! {
);
info!("Drivers loaded:");
for (i, driver) in bsp::driver::driver_manager()
.all_device_drivers()
.iter()
.enumerate()
{
info!(" {}. {}", i + 1, driver.compatible());
}
driver::driver_manager().enumerate();
info!("Timer test, spinning for 1 second");
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
--- 10_virtual_mem_part1_identity_mapping/src/main.rs
+++ 11_exceptions_part1_groundwork/src/main.rs
@@ -145,6 +145,8 @@
use driver::interface::DriverManager;
@@ -144,6 +144,8 @@
unsafe fn kernel_init() -> ! {
use memory::mmu::interface::MMU;
+ 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;
use core::time::Duration;
use driver::interface::DriverManager;
@@ -200,13 +202,28 @@
info!(
@@ -193,13 +195,28 @@
info!("Timer test, spinning for 1 second");
time::time_manager().spin_for(Duration::from_secs(1));

@ -4,7 +4,6 @@
//! Top-level BSP file for the Raspberry Pi 3 and 4.
pub mod console;
pub mod cpu;
pub mod driver;
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.
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.
struct BSPDriverManager {
device_drivers: [&'static (dyn DeviceDriver + Sync); 2],
}
static PL011_UART: device_driver::PL011Uart =
unsafe { device_driver::PL011Uart::new(mmio::PL011_UART_START) };
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 =
unsafe { device_driver::PL011Uart::new(mmio::PL011_UART_START) };
/// This must be called only after successful init of the UART driver.
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 {
device_drivers: [&PL011_UART, &GPIO],
};
/// This must be called only after successful init of the GPIO driver.
fn post_init_gpio() -> Result<(), &'static str> {
GPIO.map_pl011_uart();
Ok(())
}
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
fn driver_uart() -> Result<(), &'static str> {
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.
pub fn driver_manager() -> &'static impl driver::interface::DriverManager {
&BSP_DRIVER_MANAGER
Ok(())
}
//------------------------------------------------------------------------------
// OS Interface Code
//------------------------------------------------------------------------------
use driver::interface::DeviceDriver;
fn driver_gpio() -> Result<(), &'static str> {
let gpio_descriptor = generic_driver::DeviceDriverDescriptor::new(&GPIO, Some(post_init_gpio));
generic_driver::driver_manager().register_driver(gpio_descriptor);
impl driver::interface::DriverManager for BSPDriverManager {
fn all_device_drivers(&self) -> &[&'static (dyn DeviceDriver + Sync)] {
&self.device_drivers[..]
}
Ok(())
}
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
fn post_device_driver_init(&self) {
// Configure PL011Uart's output pins.
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(())
}

@ -4,7 +4,9 @@
//! System console.
use crate::bsp;
mod null_console;
use crate::synchronization::{self, NullLock};
//--------------------------------------------------------------------------------------------------
// Public Definitions
@ -54,13 +56,26 @@ pub mod interface {
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
//--------------------------------------------------------------------------------------------------
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)
}

@ -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.
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
//--------------------------------------------------------------------------------------------------
@ -24,21 +40,128 @@ pub mod interface {
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.
pub trait DriverManager {
/// Return a slice of references to all `BSP`-instantiated drivers.
///
/// # Safety
///
/// - The order of devices is the order in which `DeviceDriver::init()` is called.
fn all_device_drivers(&self) -> &[&'static (dyn DeviceDriver + Sync)];
/// # Safety
///
/// - During init, drivers might do stuff with system-wide impact.
pub unsafe fn init_drivers(&self) {
self.for_each_descriptor(|descriptor| {
// 1. Initialize driver.
if let Err(x) = descriptor.device_driver.init() {
panic!(
"Error initializing driver: {}: {}",
descriptor.device_driver.compatible(),
x
);
}
/// Initialization code that runs after driver init.
///
/// For example, device driver code that depends on other drivers already being online.
fn post_device_driver_init(&self);
// 2. Call corresponding post init callback.
if let Some(callback) = &descriptor.post_init_callback {
if let Err(x) = callback() {
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
/// NullLocks instead of spinlocks), will fail to work (properly) on the RPi SoCs.
unsafe fn kernel_init() -> ! {
use driver::interface::DriverManager;
use memory::mmu::interface::MMU;
exception::handling_init();
@ -151,12 +150,13 @@ unsafe fn kernel_init() -> ! {
panic!("MMU: {}", string);
}
for i in bsp::driver::driver_manager().all_device_drivers().iter() {
if let Err(x) = i.init() {
panic!("Error loading driver: {}: {}", i.compatible(), x);
}
// Initialize the BSP driver subsystem.
if let Err(x) = bsp::driver::init() {
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.
// Transition from unsafe to safe.
@ -167,7 +167,6 @@ unsafe fn kernel_init() -> ! {
fn kernel_main() -> ! {
use console::console;
use core::time::Duration;
use driver::interface::DriverManager;
info!(
"{} version {}",
@ -191,13 +190,7 @@ fn kernel_main() -> ! {
);
info!("Drivers loaded:");
for (i, driver) in bsp::driver::driver_manager()
.all_device_drivers()
.iter()
.enumerate()
{
info!(" {}. {}", i + 1, driver.compatible());
}
driver::driver_manager().enumerate();
info!("Timer test, spinning for 1 second");
time::time_manager().spin_for(Duration::from_secs(1));

@ -291,7 +291,7 @@ test_boot: $(KERNEL_BIN)
## Helpers for unit and integration test targets
##------------------------------------------------------------------------------
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
# this script executes from the root.

@ -266,10 +266,8 @@ implementation in `lib.rs`:
#[cfg(test)]
#[no_mangle]
unsafe fn kernel_init() -> ! {
use driver::interface::DriverManager;
exception::handling_init();
bsp::driver::driver_manager().qemu_bring_up_console();
bsp::driver::qemu_bring_up_console();
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
our tests inside `QEMU`, we need to ensure that whatever peripheral implements the kernel's
`console` interface is initialized, so that we can print from our tests. If you recall [tutorial
03], bringing up peripherals in `QEMU` might not need the full initialization as is needed on real
hardware (setting clocks, config registers, etc...) due to the abstractions in `QEMU`'s emulation
code. So this is an opportunity to cut down on setup code.
Note the call to `bsp::driver::qemu_bring_up_console()`. Since we are running all our tests inside
`QEMU`, we need to ensure that whatever peripheral implements the kernel's `console` interface is
initialized, so that we can print from our tests. If you recall [tutorial 03], bringing up
peripherals in `QEMU` might not need the full initialization as is needed on real hardware (setting
clocks, config registers, etc...) due to the abstractions in `QEMU`'s emulation code. So this is an
opportunity to cut down on setup code.
[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)]
use core::time::Duration;
use libkernel::{bsp, cpu, driver, exception, time};
use libkernel::{bsp, cpu, exception, time};
use test_macros::kernel_test;
#[no_mangle]
unsafe fn kernel_init() -> ! {
use driver::interface::DriverManager;
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.
@ -643,12 +639,12 @@ unsafe fn kernel_init() -> ! {
#[kernel_test]
fn timer_is_counting() {
assert!(time::time_manager().uptime().as_nanos() > 0)
assert!(time::time_manager().resolution().as_nanos() < 100)
}
/// Timer resolution must be sufficient.
#[kernel_test]
fn timer_resolution_is_sufficient() {
assert!(time::time_manager().resolution().as_nanos() > 0);
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;
use libkernel::{bsp, cpu, driver, exception, info, memory, println};
use libkernel::{bsp, cpu, exception, info, memory, println};
#[no_mangle]
unsafe fn kernel_init() -> ! {
use driver::interface::DriverManager;
use memory::mmu::interface::MMU;
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.
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.
mod panic_wait_forever;
use libkernel::{bsp, console, cpu, driver, exception, print};
use libkernel::{bsp, console, cpu, exception, print};
#[no_mangle]
unsafe fn kernel_init() -> ! {
use console::console;
use driver::interface::DriverManager;
exception::handling_init();
bsp::driver::driver_manager().qemu_bring_up_console();
bsp::driver::qemu_bring_up_console();
// Handshake
assert_eq!(console().read_char(), 'A');

@ -4,7 +4,6 @@
//! Top-level BSP file for the Raspberry Pi 3 and 4.
pub mod console;
pub mod cpu;
pub mod driver;
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.
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.
struct BSPDriverManager {
device_drivers: [&'static (dyn DeviceDriver + Sync); 2],
}
static PL011_UART: device_driver::PL011Uart =
unsafe { device_driver::PL011Uart::new(mmio::PL011_UART_START) };
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 =
unsafe { device_driver::PL011Uart::new(mmio::PL011_UART_START) };
/// This must be called only after successful init of the UART driver.
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 {
device_drivers: [&PL011_UART, &GPIO],
};
/// This must be called only after successful init of the GPIO driver.
fn post_init_gpio() -> Result<(), &'static str> {
GPIO.map_pl011_uart();
Ok(())
}
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
fn driver_uart() -> Result<(), &'static str> {
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.
pub fn driver_manager() -> &'static impl driver::interface::DriverManager {
&BSP_DRIVER_MANAGER
Ok(())
}
//------------------------------------------------------------------------------
// OS Interface Code
//------------------------------------------------------------------------------
use driver::interface::DeviceDriver;
fn driver_gpio() -> Result<(), &'static str> {
let gpio_descriptor = generic_driver::DeviceDriverDescriptor::new(&GPIO, Some(post_init_gpio));
generic_driver::driver_manager().register_driver(gpio_descriptor);
impl driver::interface::DriverManager for BSPDriverManager {
fn all_device_drivers(&self) -> &[&'static (dyn DeviceDriver + Sync)] {
&self.device_drivers[..]
}
Ok(())
}
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
fn post_device_driver_init(&self) {
// Configure PL011Uart's output pins.
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");
}
#[cfg(feature = "test_build")]
fn qemu_bring_up_console(&self) {}
driver_uart()?;
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.
use crate::bsp;
mod null_console;
use crate::synchronization::{self, NullLock};
//--------------------------------------------------------------------------------------------------
// Public Definitions
@ -54,13 +56,26 @@ pub mod interface {
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
//--------------------------------------------------------------------------------------------------
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)
}

@ -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.
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
//--------------------------------------------------------------------------------------------------
@ -24,26 +40,128 @@ pub mod interface {
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.
pub trait DriverManager {
/// Return a slice of references to all `BSP`-instantiated drivers.
///
/// # Safety
///
/// - The order of devices is the order in which `DeviceDriver::init()` is called.
fn all_device_drivers(&self) -> &[&'static (dyn DeviceDriver + Sync)];
/// # Safety
///
/// - During init, drivers might do stuff with system-wide impact.
pub unsafe fn init_drivers(&self) {
self.for_each_descriptor(|descriptor| {
// 1. Initialize driver.
if let Err(x) = descriptor.device_driver.init() {
panic!(
"Error initializing driver: {}: {}",
descriptor.device_driver.compatible(),
x
);
}
/// Initialization code that runs after driver init.
///
/// For example, device driver code that depends on other drivers already being online.
fn post_device_driver_init(&self);
// 2. Call corresponding post init callback.
if let Some(callback) = &descriptor.post_init_callback {
if let Err(x) = callback() {
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
/// less steps than on real hardware due to QEMU's abstractions.
#[cfg(feature = "test_build")]
fn qemu_bring_up_console(&self);
i += 1;
});
}
}

@ -182,10 +182,8 @@ pub fn test_runner(tests: &[&test_types::UnitTest]) {
#[cfg(test)]
#[no_mangle]
unsafe fn kernel_init() -> ! {
use driver::interface::DriverManager;
exception::handling_init();
bsp::driver::driver_manager().qemu_bring_up_console();
bsp::driver::qemu_bring_up_console();
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.
#[no_mangle]
unsafe fn kernel_init() -> ! {
use driver::interface::DriverManager;
use memory::mmu::interface::MMU;
exception::handling_init();
@ -35,12 +34,13 @@ unsafe fn kernel_init() -> ! {
panic!("MMU: {}", string);
}
for i in bsp::driver::driver_manager().all_device_drivers().iter() {
if let Err(x) = i.init() {
panic!("Error loading driver: {}: {}", i.compatible(), x);
}
// Initialize the BSP driver subsystem.
if let Err(x) = bsp::driver::init() {
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.
// Transition from unsafe to safe.
@ -50,7 +50,6 @@ unsafe fn kernel_init() -> ! {
/// The main function running after the early init.
fn kernel_main() -> ! {
use console::console;
use driver::interface::DriverManager;
info!("{}", libkernel::version());
info!("Booting on: {}", bsp::board_name());
@ -70,13 +69,7 @@ fn kernel_main() -> ! {
);
info!("Drivers loaded:");
for (i, driver) in bsp::driver::driver_manager()
.all_device_drivers()
.iter()
.enumerate()
{
info!(" {}. {}", i + 1, driver.compatible());
}
driver::driver_manager().enumerate();
info!("Echoing input now");

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

@ -11,15 +11,13 @@
#![test_runner(libkernel::test_runner)]
use core::time::Duration;
use libkernel::{bsp, cpu, driver, exception, time};
use libkernel::{bsp, cpu, exception, time};
use test_macros::kernel_test;
#[no_mangle]
unsafe fn kernel_init() -> ! {
use driver::interface::DriverManager;
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.

@ -17,15 +17,14 @@
/// or indirectly.
mod panic_exit_success;
use libkernel::{bsp, cpu, driver, exception, info, memory, println};
use libkernel::{bsp, cpu, exception, info, memory, println};
#[no_mangle]
unsafe fn kernel_init() -> ! {
use driver::interface::DriverManager;
use memory::mmu::interface::MMU;
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.
println!("Testing synchronous exception handling by causing a page fault");

@ -12,7 +12,7 @@
mod panic_wait_forever;
use core::arch::asm;
use libkernel::{bsp, cpu, driver, exception, info, memory, println};
use libkernel::{bsp, cpu, exception, info, memory, println};
#[inline(never)]
fn nested_system_call() {
@ -30,11 +30,10 @@ fn nested_system_call() {
#[no_mangle]
unsafe fn kernel_init() -> ! {
use driver::interface::DriverManager;
use memory::mmu::interface::MMU;
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.
println!("Testing exception restore");

@ -291,7 +291,7 @@ test_boot: $(KERNEL_BIN)
## Helpers for unit and integration test targets
##------------------------------------------------------------------------------
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
# this script executes from the root.

File diff suppressed because it is too large Load Diff

@ -79,20 +79,25 @@
mod gicc;
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
//--------------------------------------------------------------------------------------------------
type HandlerTable = [Option<exception::asynchronous::IRQDescriptor>; IRQNumber::NUM_TOTAL];
type HandlerTable = [Option<exception::asynchronous::IRQHandlerDescriptor<IRQNumber>>;
IRQNumber::MAX_INCLUSIVE + 1];
//--------------------------------------------------------------------------------------------------
// Public Definitions
//--------------------------------------------------------------------------------------------------
/// 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.
pub struct GICv2 {
@ -124,7 +129,7 @@ impl GICv2 {
Self {
gicd: gicd::GICD::new(gicd_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;
impl driver::interface::DeviceDriver for GICv2 {
type IRQNumberType = IRQNumber;
fn compatible(&self) -> &'static str {
Self::COMPATIBLE
}
@ -156,23 +163,22 @@ impl exception::asynchronous::interface::IRQManager for GICv2 {
fn register_handler(
&self,
irq_number: Self::IRQNumberType,
descriptor: exception::asynchronous::IRQDescriptor,
irq_handler_descriptor: exception::asynchronous::IRQHandlerDescriptor<Self::IRQNumberType>,
) -> Result<(), &'static str> {
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() {
return Err("IRQ handler already registered");
}
table[irq_number] = Some(descriptor);
table[irq_number] = Some(irq_handler_descriptor);
Ok(())
})
}
fn enable(&self, irq_number: Self::IRQNumberType) {
fn enable(&self, irq_number: &Self::IRQNumberType) {
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),
Some(descriptor) => {
// 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| {
for (i, opt) in table.iter().skip(32).enumerate() {
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.
pub fn enable(&self, irq_num: super::IRQNumber) {
pub fn enable(&self, irq_num: &super::IRQNumber) {
let irq_num = irq_num.get();
// Each bit in the u32 enable register corresponds to one IRQ number. Shift right by 5

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

@ -6,7 +6,12 @@
mod peripheral_ic;
use crate::{driver, exception};
use crate::{
bsp::device_driver::common::BoundedUsize,
driver,
exception::{self, asynchronous::IRQHandlerDescriptor},
};
use core::fmt;
//--------------------------------------------------------------------------------------------------
// Private Definitions
@ -21,10 +26,8 @@ struct PendingIRQs {
// Public Definitions
//--------------------------------------------------------------------------------------------------
pub type LocalIRQ =
exception::asynchronous::IRQNumber<{ InterruptController::MAX_LOCAL_IRQ_NUMBER }>;
pub type PeripheralIRQ =
exception::asynchronous::IRQNumber<{ InterruptController::MAX_PERIPHERAL_IRQ_NUMBER }>;
pub type LocalIRQ = BoundedUsize<{ InterruptController::MAX_LOCAL_IRQ_NUMBER }>;
pub type PeripheralIRQ = BoundedUsize<{ InterruptController::MAX_PERIPHERAL_IRQ_NUMBER }>;
/// Used for the associated type of trait [`exception::asynchronous::interface::IRQManager`].
#[derive(Copy, Clone)]
@ -67,6 +70,15 @@ impl Iterator for PendingIRQs {
// 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 {
// Restrict to 3 for now. This makes future code for local_ic.rs more straight forward.
const MAX_LOCAL_IRQ_NUMBER: usize = 3;
@ -91,6 +103,8 @@ impl InterruptController {
//------------------------------------------------------------------------------
impl driver::interface::DeviceDriver for InterruptController {
type IRQNumberType = IRQNumber;
fn compatible(&self) -> &'static str {
Self::COMPATIBLE
}
@ -101,16 +115,23 @@ impl exception::asynchronous::interface::IRQManager for InterruptController {
fn register_handler(
&self,
irq: Self::IRQNumberType,
descriptor: exception::asynchronous::IRQDescriptor,
irq_handler_descriptor: exception::asynchronous::IRQHandlerDescriptor<Self::IRQNumberType>,
) -> Result<(), &'static str> {
match irq {
match irq_handler_descriptor.number() {
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 {
IRQNumber::Local(_) => unimplemented!("Local IRQ controller not implemented."),
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.
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
@ -82,7 +83,7 @@ impl PeripheralIC {
Self {
wo_registers: IRQSafeNullLock::new(WriteOnlyRegisters::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(
&self,
irq: Self::IRQNumberType,
descriptor: exception::asynchronous::IRQDescriptor,
irq_handler_descriptor: exception::asynchronous::IRQHandlerDescriptor<Self::IRQNumberType>,
) -> Result<(), &'static str> {
self.handler_table.write(|table| {
let irq_number = irq.get();
let irq_number = irq_handler_descriptor.number().get();
if table[irq_number].is_some() {
return Err("IRQ handler already registered");
}
table[irq_number] = Some(descriptor);
table[irq_number] = Some(irq_handler_descriptor);
Ok(())
})
}
fn enable(&self, irq: Self::IRQNumberType) {
fn enable(&self, irq: &Self::IRQNumberType) {
self.wo_registers.lock(|regs| {
let enable_reg = if irq.get() <= 31 {
&regs.ENABLE_1
@ -147,7 +147,7 @@ impl exception::asynchronous::interface::IRQManager for PeripheralIC {
None => panic!("No handler registered for IRQ {}", irq_number),
Some(descriptor) => {
// 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| {
for (i, opt) in table.iter().enumerate() {
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>
use crate::{
bsp, bsp::device_driver::common::MMIODerefWrapper, console, cpu, driver, exception,
synchronization, synchronization::IRQSafeNullLock,
bsp::device_driver::common::MMIODerefWrapper,
console, cpu, driver,
exception::{self, asynchronous::IRQNumber},
synchronization,
synchronization::IRQSafeNullLock,
};
use core::fmt;
use tock_registers::{
@ -229,7 +232,6 @@ struct PL011UartInner {
/// Representation of the UART.
pub struct PL011Uart {
inner: IRQSafeNullLock<PL011UartInner>,
irq_number: bsp::device_driver::IRQNumber,
}
//--------------------------------------------------------------------------------------------------
@ -393,13 +395,9 @@ impl PL011Uart {
/// # Safety
///
/// - The user must ensure to provide a correct MMIO start address.
pub const unsafe fn new(
mmio_start_addr: usize,
irq_number: bsp::device_driver::IRQNumber,
) -> Self {
pub const unsafe fn new(mmio_start_addr: usize) -> Self {
Self {
inner: IRQSafeNullLock::new(PL011UartInner::new(mmio_start_addr)),
irq_number,
}
}
}
@ -410,6 +408,8 @@ impl PL011Uart {
use synchronization::interface::Mutex;
impl driver::interface::DeviceDriver for PL011Uart {
type IRQNumberType = IRQNumber;
fn compatible(&self) -> &'static str {
Self::COMPATIBLE
}
@ -420,16 +420,16 @@ impl driver::interface::DeviceDriver for PL011Uart {
Ok(())
}
fn register_and_enable_irq_handler(&'static self) -> Result<(), &'static str> {
use exception::asynchronous::{irq_manager, IRQDescriptor};
fn register_and_enable_irq_handler(
&'static self,
irq_number: &Self::IRQNumberType,
) -> Result<(), &'static str> {
use exception::asynchronous::{irq_manager, IRQHandlerDescriptor};
let descriptor = IRQDescriptor {
name: Self::COMPATIBLE,
handler: self,
};
let descriptor = IRQHandlerDescriptor::new(*irq_number, Self::COMPATIBLE, self);
irq_manager().register_handler(self.irq_number, descriptor)?;
irq_manager().enable(self.irq_number);
irq_manager().register_handler(descriptor)?;
irq_manager().enable(irq_number);
Ok(())
}

@ -4,7 +4,7 @@
//! Common device driver code.
use core::{marker::PhantomData, ops};
use core::{fmt, marker::PhantomData, ops};
//--------------------------------------------------------------------------------------------------
// Public Definitions
@ -15,6 +15,10 @@ pub struct MMIODerefWrapper<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
//--------------------------------------------------------------------------------------------------
@ -36,3 +40,25 @@ impl<T> ops::Deref for MMIODerefWrapper<T> {
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.
pub mod console;
pub mod cpu;
pub mod driver;
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.
use super::{exception, memory::map::mmio};
use crate::{bsp::device_driver, driver};
pub use device_driver::IRQNumber;
//--------------------------------------------------------------------------------------------------
// Private Definitions
//--------------------------------------------------------------------------------------------------
/// Device Driver Manager type.
struct BSPDriverManager {
device_drivers: [&'static (dyn DeviceDriver + Sync); 3],
}
use crate::{
bsp::device_driver,
console, driver as generic_driver,
exception::{self as generic_exception},
};
use core::sync::atomic::{AtomicBool, Ordering};
//--------------------------------------------------------------------------------------------------
// Global instances
//--------------------------------------------------------------------------------------------------
pub(super) static PL011_UART: device_driver::PL011Uart = unsafe {
device_driver::PL011Uart::new(
mmio::PL011_UART_START,
exception::asynchronous::irq_map::PL011_UART,
)
};
static PL011_UART: device_driver::PL011Uart =
unsafe { device_driver::PL011Uart::new(mmio::PL011_UART_START) };
static GPIO: device_driver::GPIO = unsafe { device_driver::GPIO::new(mmio::GPIO_START) };
#[cfg(feature = "bsp_rpi3")]
pub(super) static INTERRUPT_CONTROLLER: device_driver::InterruptController =
unsafe { device_driver::InterruptController::new(mmio::PERIPHERAL_INTERRUPT_CONTROLLER_START) };
static INTERRUPT_CONTROLLER: device_driver::InterruptController =
unsafe { device_driver::InterruptController::new(mmio::PERIPHERAL_IC_START) };
#[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) };
static BSP_DRIVER_MANAGER: BSPDriverManager = BSPDriverManager {
device_drivers: [&PL011_UART, &GPIO, &INTERRUPT_CONTROLLER],
};
//--------------------------------------------------------------------------------------------------
// Public Code
// Private Code
//--------------------------------------------------------------------------------------------------
/// Return a reference to the driver manager.
pub fn driver_manager() -> &'static impl driver::interface::DriverManager {
&BSP_DRIVER_MANAGER
/// This must be called only after successful init of the UART driver.
fn post_init_uart() -> Result<(), &'static str> {
console::register_console(&PL011_UART);
Ok(())
}
//------------------------------------------------------------------------------
// OS Interface Code
//------------------------------------------------------------------------------
use driver::interface::DeviceDriver;
/// This must be called only after successful init of the GPIO driver.
fn post_init_gpio() -> Result<(), &'static str> {
GPIO.map_pl011_uart();
Ok(())
}
impl driver::interface::DriverManager for BSPDriverManager {
fn all_device_drivers(&self) -> &[&'static (dyn DeviceDriver + Sync)] {
&self.device_drivers[..]
}
/// This must be called only after successful init of the interrupt controller driver.
fn post_init_interrupt_controller() -> Result<(), &'static str> {
generic_exception::asynchronous::register_irq_manager(&INTERRUPT_CONTROLLER);
Ok(())
}
fn post_device_driver_init(&self) {
// Configure PL011Uart's output pins.
GPIO.map_pl011_uart();
fn driver_uart() -> Result<(), &'static str> {
let uart_descriptor = generic_driver::DeviceDriverDescriptor::new(
&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")]
fn qemu_bring_up_console(&self) {}
driver_uart()?;
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.
use crate::{bsp, bsp::driver, exception};
use crate::bsp;
//--------------------------------------------------------------------------------------------------
// Public Definitions
//--------------------------------------------------------------------------------------------------
/// Export for reuse in generic asynchronous.rs.
pub use bsp::device_driver::IRQNumber;
#[cfg(feature = "bsp_rpi3")]
pub(in crate::bsp) mod irq_map {
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);
}
//--------------------------------------------------------------------------------------------------
// 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 {
use super::*;
pub const START: usize = 0x3F00_0000;
pub const PERIPHERAL_INTERRUPT_CONTROLLER_START: usize = START + 0x0000_B200;
pub const GPIO_START: usize = START + GPIO_OFFSET;
pub const PL011_UART_START: usize = START + UART_OFFSET;
pub const END_INCLUSIVE: usize = 0x4000_FFFF;
pub const START: usize = 0x3F00_0000;
pub const PERIPHERAL_IC_START: usize = START + 0x0000_B200;
pub const GPIO_START: usize = START + GPIO_OFFSET;
pub const PL011_UART_START: usize = START + UART_OFFSET;
pub const END_INCLUSIVE: usize = 0x4000_FFFF;
}
/// Physical devices.

@ -4,7 +4,9 @@
//! System console.
use crate::bsp;
mod null_console;
use crate::synchronization;
//--------------------------------------------------------------------------------------------------
// Public Definitions
@ -54,13 +56,26 @@ pub mod interface {
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
//--------------------------------------------------------------------------------------------------
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.
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.
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
//--------------------------------------------------------------------------------------------------
@ -12,6 +32,9 @@
pub mod interface {
/// Device Driver functions.
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.
fn compatible(&self) -> &'static str;
@ -24,34 +47,175 @@ pub mod interface {
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
/// itself has static lifetime.
fn register_and_enable_irq_handler(&'static self) -> Result<(), &'static str> {
Ok(())
fn register_and_enable_irq_handler(
&'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.
pub trait DriverManager {
/// Return a slice of references to all `BSP`-instantiated drivers.
///
/// # Safety
///
/// - The order of devices is the order in which `DeviceDriver::init()` is called.
fn all_device_drivers(&self) -> &[&'static (dyn DeviceDriver + Sync)];
/// # Safety
///
/// - During init, drivers might do stuff with system-wide impact.
pub unsafe fn init_drivers_and_irqs(&self) {
self.for_each_descriptor(|descriptor| {
// 1. Initialize driver.
if let Err(x) = descriptor.device_driver.init() {
panic!(
"Error initializing driver: {}: {}",
descriptor.device_driver.compatible(),
x
);
}
/// Initialization code that runs after driver init.
///
/// For example, device driver code that depends on other drivers already being online.
fn post_device_driver_init(&self);
// 2. Call corresponding post init callback.
if let Some(callback) = &descriptor.post_init_callback {
if let Err(x) = callback() {
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
/// less steps than on real hardware due to QEMU's abstractions.
#[cfg(feature = "test_build")]
fn qemu_bring_up_console(&self);
i += 1;
});
}
}

@ -7,9 +7,10 @@
#[cfg(target_arch = "aarch64")]
#[path = "../_arch/aarch64/exception/asynchronous.rs"]
mod arch_asynchronous;
mod null_irq_manager;
use crate::bsp;
use core::{fmt, marker::PhantomData};
use crate::{bsp, synchronization};
use core::marker::PhantomData;
//--------------------------------------------------------------------------------------------------
// Architectural Public Reexports
@ -23,14 +24,23 @@ pub use arch_asynchronous::{
// Public Definitions
//--------------------------------------------------------------------------------------------------
/// Interrupt number as defined by the BSP.
pub type IRQNumber = bsp::exception::asynchronous::IRQNumber;
/// Interrupt descriptor.
#[derive(Copy, Clone)]
pub struct IRQDescriptor {
pub struct IRQHandlerDescriptor<T>
where
T: Copy,
{
/// The IRQ number.
number: T,
/// Descriptive name.
pub name: &'static str,
name: &'static str,
/// Reference to handler trait object.
pub handler: &'static (dyn interface::IRQHandler + Sync),
handler: &'static (dyn interface::IRQHandler + Sync),
}
/// IRQContext token.
@ -60,17 +70,16 @@ pub mod interface {
/// platform's interrupt controller.
pub trait IRQManager {
/// The IRQ number type depends on the implementation.
type IRQNumberType;
type IRQNumberType: Copy;
/// Register a handler.
fn register_handler(
&self,
irq_number: Self::IRQNumberType,
descriptor: super::IRQDescriptor,
irq_handler_descriptor: super::IRQHandlerDescriptor<Self::IRQNumberType>,
) -> Result<(), &'static str>;
/// Enable an interrupt in the controller.
fn enable(&self, irq_number: Self::IRQNumberType);
fn enable(&self, irq_number: &Self::IRQNumberType);
/// Handle pending interrupts.
///
@ -86,17 +95,55 @@ pub mod interface {
);
/// 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)]
pub struct IRQNumber<const MAX_INCLUSIVE: usize>(usize);
//--------------------------------------------------------------------------------------------------
// Global instances
//--------------------------------------------------------------------------------------------------
static CUR_IRQ_MANAGER: InitStateLock<
&'static (dyn interface::IRQManager<IRQNumberType = IRQNumber> + Sync),
> = InitStateLock::new(&null_irq_manager::NULL_IRQ_MANAGER);
//--------------------------------------------------------------------------------------------------
// 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> {
/// 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.
///
/// 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
}
/// 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.
pub fn irq_manager() -> &'static dyn interface::IRQManager<IRQNumberType = bsp::driver::IRQNumber> {
bsp::exception::asynchronous::irq_manager()
pub fn irq_manager() -> &'static dyn interface::IRQManager<IRQNumberType = IRQNumber> {
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)]
#[no_mangle]
unsafe fn kernel_init() -> ! {
use driver::interface::DriverManager;
exception::handling_init();
bsp::driver::driver_manager().qemu_bring_up_console();
bsp::driver::qemu_bring_up_console();
test_main();

@ -13,7 +13,7 @@
#![no_main]
#![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.
///
@ -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.
#[no_mangle]
unsafe fn kernel_init() -> ! {
use driver::interface::DriverManager;
use memory::mmu::interface::MMU;
exception::handling_init();
@ -35,21 +34,14 @@ unsafe fn kernel_init() -> ! {
panic!("MMU: {}", string);
}
for i in bsp::driver::driver_manager().all_device_drivers().iter() {
if let Err(x) = i.init() {
panic!("Error loading driver: {}: {}", i.compatible(), 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 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_and_irqs();
// Unmask interrupts on the boot CPU core.
exception::asynchronous::local_irq_unmask();
@ -62,9 +54,6 @@ unsafe fn kernel_init() -> ! {
/// The main function running after the early init.
fn kernel_main() -> ! {
use driver::interface::DriverManager;
use exception::asynchronous::interface::IRQManager;
info!("{}", libkernel::version());
info!("Booting on: {}", bsp::board_name());
@ -83,16 +72,10 @@ fn kernel_main() -> ! {
);
info!("Drivers loaded:");
for (i, driver) in bsp::driver::driver_manager()
.all_device_drivers()
.iter()
.enumerate()
{
info!(" {}. {}", i + 1, driver.compatible());
}
driver::driver_manager().enumerate();
info!("Registered IRQ handlers:");
bsp::exception::asynchronous::irq_manager().print_handler();
exception::asynchronous::irq_manager().print_handler();
info!("Echoing input now");
cpu::wait_forever();

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

@ -11,15 +11,13 @@
#![test_runner(libkernel::test_runner)]
use core::time::Duration;
use libkernel::{bsp, cpu, driver, exception, time};
use libkernel::{bsp, cpu, exception, time};
use test_macros::kernel_test;
#[no_mangle]
unsafe fn kernel_init() -> ! {
use driver::interface::DriverManager;
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.

@ -17,15 +17,14 @@
/// or indirectly.
mod panic_exit_success;
use libkernel::{bsp, cpu, driver, exception, info, memory, println};
use libkernel::{bsp, cpu, exception, info, memory, println};
#[no_mangle]
unsafe fn kernel_init() -> ! {
use driver::interface::DriverManager;
use memory::mmu::interface::MMU;
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.
println!("Testing synchronous exception handling by causing a page fault");

@ -12,7 +12,7 @@
mod panic_wait_forever;
use core::arch::asm;
use libkernel::{bsp, cpu, driver, exception, info, memory, println};
use libkernel::{bsp, cpu, exception, info, memory, println};
#[inline(never)]
fn nested_system_call() {
@ -30,11 +30,10 @@ fn nested_system_call() {
#[no_mangle]
unsafe fn kernel_init() -> ! {
use driver::interface::DriverManager;
use memory::mmu::interface::MMU;
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.
println!("Testing exception restore");

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

@ -291,7 +291,7 @@ test_boot: $(KERNEL_BIN)
## Helpers for unit and integration test targets
##------------------------------------------------------------------------------
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
# this script executes from the root.

File diff suppressed because it is too large Load Diff

@ -80,7 +80,8 @@ mod gicc;
mod gicd;
use crate::{
bsp, cpu, driver, exception,
bsp::{self, device_driver::common::BoundedUsize},
cpu, driver, exception,
memory::{Address, Virtual},
synchronization,
synchronization::InitStateLock,
@ -90,14 +91,15 @@ use crate::{
// Private Definitions
//--------------------------------------------------------------------------------------------------
type HandlerTable = [Option<exception::asynchronous::IRQDescriptor>; IRQNumber::NUM_TOTAL];
type HandlerTable = [Option<exception::asynchronous::IRQHandlerDescriptor<IRQNumber>>;
IRQNumber::MAX_INCLUSIVE + 1];
//--------------------------------------------------------------------------------------------------
// Public Definitions
//--------------------------------------------------------------------------------------------------
/// 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.
pub struct GICv2 {
@ -109,9 +111,6 @@ pub struct GICv2 {
/// Stores registered IRQ handlers. Writable only during kernel init. RO afterwards.
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(
gicd_mmio_start_addr: Address<Virtual>,
gicc_mmio_start_addr: Address<Virtual>,
post_init_callback: fn(),
) -> Self {
Self {
gicd: gicd::GICD::new(gicd_mmio_start_addr),
gicc: gicc::GICC::new(gicc_mmio_start_addr),
handler_table: InitStateLock::new([None; IRQNumber::NUM_TOTAL]),
post_init_callback,
handler_table: InitStateLock::new([None; IRQNumber::MAX_INCLUSIVE + 1]),
}
}
}
@ -148,6 +145,8 @@ impl GICv2 {
use synchronization::interface::ReadWriteEx;
impl driver::interface::DeviceDriver for GICv2 {
type IRQNumberType = IRQNumber;
fn compatible(&self) -> &'static str {
Self::COMPATIBLE
}
@ -160,8 +159,6 @@ impl driver::interface::DeviceDriver for GICv2 {
self.gicc.priority_accept_all();
self.gicc.enable();
(self.post_init_callback)();
Ok(())
}
}
@ -171,23 +168,22 @@ impl exception::asynchronous::interface::IRQManager for GICv2 {
fn register_handler(
&self,
irq_number: Self::IRQNumberType,
descriptor: exception::asynchronous::IRQDescriptor,
irq_handler_descriptor: exception::asynchronous::IRQHandlerDescriptor<Self::IRQNumberType>,
) -> Result<(), &'static str> {
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() {
return Err("IRQ handler already registered");
}
table[irq_number] = Some(descriptor);
table[irq_number] = Some(irq_handler_descriptor);
Ok(())
})
}
fn enable(&self, irq_number: Self::IRQNumberType) {
fn enable(&self, irq_number: &Self::IRQNumberType) {
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),
Some(descriptor) => {
// 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| {
for (i, opt) in table.iter().skip(32).enumerate() {
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.
pub fn enable(&self, irq_num: super::IRQNumber) {
pub fn enable(&self, irq_num: &super::IRQNumber) {
let irq_num = irq_num.get();
// Each bit in the u32 enable register corresponds to one IRQ number. Shift right by 5

@ -7,6 +7,7 @@
use crate::{
bsp::device_driver::common::MMIODerefWrapper,
driver,
exception::asynchronous::IRQNumber,
memory::{Address, Virtual},
synchronization,
synchronization::IRQSafeNullLock,
@ -122,7 +123,6 @@ struct GPIOInner {
/// Representation of the GPIO HW.
pub struct GPIO {
inner: IRQSafeNullLock<GPIOInner>,
post_init_callback: fn(),
}
//--------------------------------------------------------------------------------------------------
@ -202,10 +202,9 @@ impl GPIO {
/// # Safety
///
/// - 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 {
inner: IRQSafeNullLock::new(GPIOInner::new(mmio_start_addr)),
post_init_callback,
}
}
@ -221,13 +220,9 @@ impl GPIO {
use synchronization::interface::Mutex;
impl driver::interface::DeviceDriver for GPIO {
type IRQNumberType = IRQNumber;
fn compatible(&self) -> &'static str {
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