refactor: 📦 Restructure code

All modules are modified to unified model
(mod.rs file in module directory).
Arch imports use modules from arch/ namespace
explicitly as arch_xxx.
This commit is contained in:
Berkus Decker 2023-08-06 16:42:33 +03:00 committed by Berkus Decker
parent 577b0b74ee
commit 0f30bf00aa
62 changed files with 2149 additions and 504 deletions

View File

@ -6,7 +6,7 @@ pub unsafe extern "C" fn _start() -> ! {
use {
aarch64_cpu::registers::{MPIDR_EL1, SP},
core::cell::UnsafeCell,
machine::endless_sleep,
machine::cpu::endless_sleep,
tock_registers::interfaces::{Readable, Writeable},
};

View File

@ -10,7 +10,7 @@
use {
aarch64_cpu::asm::barrier,
core::hash::Hasher,
machine::{console::console, platform::rpi3::BcmHost, print, println},
machine::{console::console, platform::raspberrypi::BcmHost, print, println},
seahash::SeaHasher,
};
@ -24,14 +24,14 @@ mod boot;
/// - The init calls in this function must appear in the correct order.
unsafe fn kernel_init(max_kernel_size: u64) -> ! {
#[cfg(feature = "jtag")]
machine::arch::jtag::wait_debugger();
machine::debug::jtag::wait_debugger();
if let Err(x) = machine::platform::drivers::init() {
panic!("Error initializing platform drivers: {}", x);
}
// Initialize all device drivers.
machine::drivers::driver_manager().init_drivers();
machine::drivers::driver_manager().init_drivers_and_irqs();
// println! is usable from here on.

View File

@ -4,6 +4,8 @@ This directory contains code specific to a certain architecture.
Implementations of arch-specific kernel calls are also placed here.
One of the submodules will be exported based conditionally on target_arch. Currently, the code depending on it will import specific architecture explicitly, there are no default reexports.
----
For more information please re-read.

View File

@ -5,11 +5,11 @@
* Copyright (c) Berkus Decker <berkus+vesper@metta.systems>
*/
//! Low-level boot of the Raspberry's processor
//! Low-level boot of the ARMv8-A processor.
//! <http://infocenter.arm.com/help/topic/com.arm.doc.dai0527a/DAI0527A_baremetal_boot_code_for_ARMv8_A_processors.pdf>
use {
crate::endless_sleep,
super::endless_sleep,
aarch64_cpu::{asm, registers::*},
core::{
cell::UnsafeCell,

View File

@ -0,0 +1,15 @@
use aarch64_cpu::asm;
pub mod boot;
pub mod smp;
/// Expose CPU-specific no-op opcode.
pub use asm::nop;
/// Loop forever in sleep mode.
#[inline]
pub fn endless_sleep() -> ! {
loop {
asm::wfe();
}
}

View File

@ -0,0 +1 @@
pub fn core_id() {}

View File

@ -0,0 +1,34 @@
use {
crate::{exception::PrivilegeLevel, info},
aarch64_cpu::registers::*,
core::cell::UnsafeCell,
tock_registers::interfaces::Readable,
};
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
/// The processor's current privilege level.
pub fn current_privilege_level() -> (PrivilegeLevel, &'static str) {
let el = CurrentEL.read_as_enum(CurrentEL::EL);
match el {
Some(CurrentEL::EL::Value::EL3) => (PrivilegeLevel::Unknown, "EL3"),
Some(CurrentEL::EL::Value::EL2) => (PrivilegeLevel::Hypervisor, "EL2"),
Some(CurrentEL::EL::Value::EL1) => (PrivilegeLevel::Kernel, "EL1"),
Some(CurrentEL::EL::Value::EL0) => (PrivilegeLevel::User, "EL0"),
_ => (PrivilegeLevel::Unknown, "Unknown"),
}
}
pub fn handling_init() {
extern "Rust" {
static __EXCEPTION_VECTORS_START: UnsafeCell<()>;
}
unsafe {
super::traps::set_vbar_el1_checked(__EXCEPTION_VECTORS_START.get() as u64)
.expect("Vector table properly aligned!");
}
info!("[!] Exception traps set up");
}

View File

@ -27,6 +27,8 @@ use {
tock_registers::interfaces::{ReadWriteable, Readable, Writeable},
};
pub(crate) mod translation_table;
//--------------------------------------------------------------------------------------------------
// Private Definitions
//--------------------------------------------------------------------------------------------------
@ -138,9 +140,9 @@ impl interface::MMU for MemoryManagementUnit {
// Fail early if translation granule is not supported.
if unlikely(!ID_AA64MMFR0_EL1.matches_all(ID_AA64MMFR0_EL1::TGran64::Supported)) {
return Err(MMUEnableError::Other(
"Translation granule not supported in HW",
));
return Err(MMUEnableError::Other {
err: "Translation granule not supported in HW",
});
}
// Prepare the memory attribute indirection register.
@ -149,7 +151,7 @@ impl interface::MMU for MemoryManagementUnit {
// Populate translation tables.
KERNEL_TABLES
.populate_translation_table_entries()
.map_err(MMUEnableError::Other)?;
.map_err(|err| MMUEnableError::Other { err })?;
// Set the "Translation Table Base Register".
TTBR0_EL1.set_baddr(KERNEL_TABLES.phys_base_address());

View File

@ -1,9 +1,7 @@
use {
super::{mair, Granule512MiB, Granule64KiB},
crate::{
memory::mmu::{
arch_mmu::{mair, Granule512MiB, Granule64KiB},
AccessPermissions, AttributeFields, MemAttributes,
},
memory::mmu::{AccessPermissions, AttributeFields, MemAttributes},
platform,
},
core::convert,

View File

@ -6,7 +6,7 @@
//! Memory management functions for aarch64.
mod addr;
// pub mod mmu; included directly as arch_mmu from main memory.rs module
pub mod mmu;
pub use addr::{PhysAddr, VirtAddr};

View File

@ -5,49 +5,8 @@
//! Implementation of aarch64 kernel functions.
use aarch64_cpu::asm;
mod boot;
#[cfg(feature = "jtag")]
pub mod jtag;
pub mod cpu;
pub mod exception;
pub mod memory;
pub mod time;
pub mod traps;
/// Loop forever in sleep mode.
#[inline]
pub fn endless_sleep() -> ! {
loop {
asm::wfe();
}
}
/// Loop for a given number of `nop` instructions.
#[inline]
pub fn loop_delay(rounds: u32) {
for _ in 0..rounds {
asm::nop();
}
}
/// Loop until a passed function returns `true`.
#[inline]
pub fn loop_until<F: Fn() -> bool>(f: F) {
loop {
if f() {
break;
}
asm::nop();
}
}
/// Loop while a passed function returns `true`.
#[inline]
pub fn loop_while<F: Fn() -> bool>(f: F) {
loop {
if !f() {
break;
}
asm::nop();
}
}

View File

@ -12,7 +12,7 @@
//! crate::time::arch_time
use {
crate::{sync, warn},
crate::{synchronization, warn},
aarch64_cpu::{asm::barrier, registers::*},
core::{
num::{NonZeroU128, NonZeroU32, NonZeroU64},
@ -36,8 +36,9 @@ struct GenericTimerCounterValue(u64);
// Global instances
//--------------------------------------------------------------------------------------------------
static ARCH_TIMER_COUNTER_FREQUENCY: sync::NullLock<Lazy<NonZeroU32>> =
sync::NullLock::new(Lazy::new(|| {
// @todo use InitStateLock here
static ARCH_TIMER_COUNTER_FREQUENCY: synchronization::IRQSafeNullLock<Lazy<NonZeroU32>> =
synchronization::IRQSafeNullLock::new(Lazy::new(|| {
NonZeroU32::try_from(CNTFRQ_EL0.get() as u32).unwrap()
}));
@ -46,7 +47,7 @@ static ARCH_TIMER_COUNTER_FREQUENCY: sync::NullLock<Lazy<NonZeroU32>> =
//--------------------------------------------------------------------------------------------------
fn arch_timer_counter_frequency() -> NonZeroU32 {
use crate::sync::interface::Mutex;
use crate::synchronization::interface::Mutex;
ARCH_TIMER_COUNTER_FREQUENCY.lock(|inner| **inner)
}

View File

@ -50,7 +50,7 @@
//! to explicitly re-enable interrupts
use {
crate::{arch::endless_sleep, println},
crate::{cpu::endless_sleep, println},
aarch64_cpu::{
asm::barrier,
registers::{ESR_EL1, FAR_EL1, SPSR_EL1, VBAR_EL1},
@ -118,7 +118,7 @@ unsafe extern "C" fn default_exception_handler() -> ! {
println!("Unexpected exception. Halting CPU.");
#[cfg(not(qemu))]
endless_sleep();
super::cpu::endless_sleep();
#[cfg(qemu)]
qemu::semihosting::exit_failure()
}

View File

@ -6,5 +6,3 @@
#[cfg(target_arch = "aarch64")]
#[macro_use]
pub mod aarch64;
#[cfg(target_arch = "aarch64")]
pub use self::aarch64::*;

View File

@ -4,8 +4,6 @@
#![allow(dead_code)]
use crate::sync::NullLock;
pub mod null_console;
//--------------------------------------------------------------------------------------------------
@ -14,7 +12,7 @@ pub mod null_console;
/// Console interfaces.
pub mod interface {
use {crate::devices::SerialOps, core::fmt};
use {crate::devices::serial::SerialOps, core::fmt};
/// Console write functions.
pub trait Write {
@ -66,25 +64,25 @@ pub mod interface {
// Global instances
//--------------------------------------------------------------------------------------------------
static CONSOLE: NullLock<&'static (dyn interface::All + Sync)> =
NullLock::new(&null_console::NULL_CONSOLE);
static CONSOLE: InitStateLock<&'static (dyn interface::All + Sync)> =
InitStateLock::new(&null_console::NULL_CONSOLE);
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
use crate::sync::interface::Mutex;
use crate::synchronization::{interface::ReadWriteEx, InitStateLock};
/// Register a new console.
pub fn register_console(new_console: &'static (dyn interface::All + Sync)) {
CONSOLE.lock(|con| *con = new_console);
CONSOLE.write(|con| *con = new_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 {
CONSOLE.lock(|con| *con)
CONSOLE.read(|con| *con)
}
/// A command prompt.

View File

@ -1,4 +1,4 @@
use crate::{console::interface, devices::SerialOps};
use crate::{console::interface, devices::serial::SerialOps};
//--------------------------------------------------------------------------------------------------
// Public Definitions

10
machine/src/cpu/boot.rs Normal file
View File

@ -0,0 +1,10 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2021-2022 Andre Richter <andre.o.richter@gmail.com>
//! Boot code.
// Not used, arch/../cpu/boot.rs is used directly to generate boot code.
// #[cfg(target_arch = "aarch64")]
// #[path = "../arch/aarch64/cpu/boot.rs"]
// mod arch_boot;

48
machine/src/cpu/mod.rs Normal file
View File

@ -0,0 +1,48 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2020-2022 Andre Richter <andre.o.richter@gmail.com>
//! Processor code.
#[cfg(target_arch = "aarch64")]
use crate::arch::aarch64::cpu as arch_cpu;
pub mod smp;
//--------------------------------------------------------------------------------------------------
// Architectural Public Reexports
//--------------------------------------------------------------------------------------------------
pub use arch_cpu::{endless_sleep, nop};
// #[cfg(feature = "test_build")]
// pub use arch_cpu::{qemu_exit_failure, qemu_exit_success};
/// Loop for a given number of `nop` instructions.
#[inline]
pub fn loop_delay(rounds: u32) {
for _ in 0..rounds {
nop();
}
}
/// Loop until a passed function returns `true`.
#[inline]
pub fn loop_until<F: Fn() -> bool>(f: F) {
loop {
if f() {
break;
}
nop();
}
}
/// Loop while a passed function returns `true`.
#[inline]
pub fn loop_while<F: Fn() -> bool>(f: F) {
loop {
if !f() {
break;
}
nop();
}
}

13
machine/src/cpu/smp.rs Normal file
View File

@ -0,0 +1,13 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2018-2022 Andre Richter <andre.o.richter@gmail.com>
//! Symmetric multiprocessing.
#[cfg(target_arch = "aarch64")]
use crate::arch::aarch64::cpu::smp as arch_smp;
//--------------------------------------------------------------------------------------------------
// Architectural Public Reexports
//--------------------------------------------------------------------------------------------------
pub use arch_smp::core_id;

View File

@ -1,7 +1,7 @@
//! JTAG helper functions.
use {
aarch64_cpu::asm,
crate::cpu::nop,
core::ptr::{read_volatile, write_volatile},
};
@ -13,7 +13,7 @@ static mut WAIT_FLAG: bool = true;
/// from inside this function's frame to continue running.
pub fn wait_debugger() {
while unsafe { read_volatile(&WAIT_FLAG) } {
asm::nop();
nop();
}
// Reset the flag so that next jtag::wait_debugger() would block again.
unsafe { write_volatile(&mut WAIT_FLAG, true) }

2
machine/src/debug/mod.rs Normal file
View File

@ -0,0 +1,2 @@
#[cfg(feature = "jtag")]
pub mod jtag;

View File

@ -1,190 +1,190 @@
use {
crate::{
console::interface,
devices::{null_console::NullConsole, SerialOps},
platform::rpi3::{mini_uart::PreparedMiniUart, pl011_uart::PreparedPL011Uart},
sync::NullLock,
},
core::fmt,
};
//--------------------------------------------------------------------------------------------------
// Private Definitions
//--------------------------------------------------------------------------------------------------
/// The mutex protected part.
struct ConsoleInner {
output: Output,
}
//--------------------------------------------------------------------------------------------------
// Public Definitions
//--------------------------------------------------------------------------------------------------
/// The main struct.
pub struct Console {
inner: NullLock<ConsoleInner>,
}
//--------------------------------------------------------------------------------------------------
// Global instances
//--------------------------------------------------------------------------------------------------
static CONSOLE: Console = Console::new();
//--------------------------------------------------------------------------------------------------
// Private Code
//--------------------------------------------------------------------------------------------------
impl ConsoleInner {
pub const fn new() -> Self {
Self {
output: Output::None(NullConsole {}),
}
}
fn current_ptr(&self) -> &dyn interface::ConsoleOps {
match &self.output {
Output::None(inner) => inner,
Output::MiniUart(inner) => inner,
Output::Uart(inner) => inner,
}
}
/// Overwrite the current output. The old output will go out of scope and
/// its Drop function will be called.
pub fn replace_with(&mut self, new_output: Output) {
self.current_ptr().flush(); // crashed here with Data Abort
// ...with ESR 0x25/0x96000000
// ...with FAR 0x984f800000028
// ...with ELR 0x946a8
self.output = new_output;
}
}
/// Implementing `core::fmt::Write` enables usage of the `format_args!` macros, which in turn are
/// used to implement the `kernel`'s `print!` and `println!` macros. By implementing `write_str()`,
/// we get `write_fmt()` automatically.
/// See src/macros.rs.
///
/// The function takes an `&mut self`, so it must be implemented for the inner struct.
impl fmt::Write for ConsoleInner {
fn write_str(&mut self, s: &str) -> fmt::Result {
self.current_ptr().write_string(s);
// for c in s.chars() {
// // Convert newline to carrige return + newline.
// if c == '\n' {
// self.write_char('\r')
// }
//
// self.write_char(c);
// }
Ok(())
}
}
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
impl Console {
/// Create a new instance.
pub const fn new() -> Console {
Console {
inner: NullLock::new(ConsoleInner::new()),
}
}
pub fn replace_with(&mut self, new_output: Output) {
self.inner.lock(|inner| inner.replace_with(new_output));
}
}
/// The global console. Output of the kernel print! and println! macros goes here.
pub fn console() -> &'static dyn crate::console::interface::All {
&CONSOLE
}
//------------------------------------------------------------------------------
// OS Interface Code
//------------------------------------------------------------------------------
use crate::sync::interface::Mutex;
/// Passthrough of `args` to the `core::fmt::Write` implementation, but guarded by a Mutex to
/// serialize access.
impl interface::Write for Console {
fn write_fmt(&self, args: core::fmt::Arguments) -> fmt::Result {
self.inner.lock(|inner| fmt::Write::write_fmt(inner, args))
}
}
/// Dispatch the respective function to the currently stored output device.
impl interface::ConsoleOps for Console {
// @todo implement utf8 serialization here!
fn write_char(&self, c: char) {
self.inner.lock(|con| con.current_ptr().write_char(c));
}
fn write_string(&self, string: &str) {
self.inner
.lock(|con| con.current_ptr().write_string(string));
}
// @todo implement utf8 deserialization here!
fn read_char(&self) -> char {
self.inner.lock(|con| con.current_ptr().read_char())
}
}
impl SerialOps for Console {
fn read_byte(&self) -> u8 {
self.inner.lock(|con| con.current_ptr().read_byte())
}
fn write_byte(&self, byte: u8) {
self.inner.lock(|con| con.current_ptr().write_byte(byte))
}
fn flush(&self) {
self.inner.lock(|con| con.current_ptr().flush())
}
fn clear_rx(&self) {
self.inner.lock(|con| con.current_ptr().clear_rx())
}
}
impl interface::All for Console {}
impl Default for Console {
fn default() -> Self {
Self::new()
}
}
impl Drop for Console {
fn drop(&mut self) {}
}
//------------------------------------------------------------------------------
// Device Interface Code
//------------------------------------------------------------------------------
/// Possible outputs which the console can store.
enum Output {
None(NullConsole),
MiniUart(PreparedMiniUart),
Uart(PreparedPL011Uart),
}
/// Generate boilerplate for converting into one of Output enum values
macro make_from($optname:ident, $name:ty) {
impl From<$name> for Output {
fn from(instance: $name) -> Self {
Output::$optname(instance)
}
}
}
make_from!(None, NullConsole);
make_from!(MiniUart, PreparedMiniUart);
make_from!(Uart, PreparedPL011Uart);
// use {
// crate::{
// console::{interface, null_console::NullConsole},
// devices::serial::SerialOps,
// platform::raspberrypi::device_driver::{mini_uart::MiniUart, pl011_uart::PL011Uart},
// synchronization::IRQSafeNullLock,
// },
// core::fmt,
// };
//
// //--------------------------------------------------------------------------------------------------
// // Private Definitions
// //--------------------------------------------------------------------------------------------------
//
// /// The mutex protected part.
// struct ConsoleInner {
// output: Output,
// }
//
// //--------------------------------------------------------------------------------------------------
// // Public Definitions
// //--------------------------------------------------------------------------------------------------
//
// /// The main struct.
// pub struct Console {
// inner: IRQSafeNullLock<ConsoleInner>,
// }
//
// //--------------------------------------------------------------------------------------------------
// // Global instances
// //--------------------------------------------------------------------------------------------------
//
// static CONSOLE: Console = Console::new();
//
// //--------------------------------------------------------------------------------------------------
// // Private Code
// //--------------------------------------------------------------------------------------------------
//
// impl ConsoleInner {
// pub const fn new() -> Self {
// Self {
// output: Output::None(NullConsole {}),
// }
// }
//
// fn current_ptr(&self) -> &dyn interface::ConsoleOps {
// match &self.output {
// Output::None(inner) => inner,
// Output::MiniUart(inner) => inner,
// Output::Uart(inner) => inner,
// }
// }
//
// /// Overwrite the current output. The old output will go out of scope and
// /// its Drop function will be called.
// pub fn replace_with(&mut self, new_output: Output) {
// self.current_ptr().flush(); // crashed here with Data Abort
// // ...with ESR 0x25/0x96000000
// // ...with FAR 0x984f800000028
// // ...with ELR 0x946a8
//
// self.output = new_output;
// }
// }
//
// /// Implementing `core::fmt::Write` enables usage of the `format_args!` macros, which in turn are
// /// used to implement the `kernel`'s `print!` and `println!` macros. By implementing `write_str()`,
// /// we get `write_fmt()` automatically.
// /// See src/macros.rs.
// ///
// /// The function takes an `&mut self`, so it must be implemented for the inner struct.
// impl fmt::Write for ConsoleInner {
// fn write_str(&mut self, s: &str) -> fmt::Result {
// self.current_ptr().write_string(s);
// // for c in s.chars() {
// // // Convert newline to carrige return + newline.
// // if c == '\n' {
// // self.write_char('\r')
// // }
// //
// // self.write_char(c);
// // }
//
// Ok(())
// }
// }
//
// //--------------------------------------------------------------------------------------------------
// // Public Code
// //--------------------------------------------------------------------------------------------------
//
// impl Console {
// /// Create a new instance.
// pub const fn new() -> Console {
// Console {
// inner: NullLock::new(ConsoleInner::new()),
// }
// }
//
// pub fn replace_with(&mut self, new_output: Output) {
// self.inner.lock(|inner| inner.replace_with(new_output));
// }
// }
//
// /// The global console. Output of the kernel print! and println! macros goes here.
// pub fn console() -> &'static dyn crate::console::interface::All {
// &CONSOLE
// }
//
// //------------------------------------------------------------------------------
// // OS Interface Code
// //------------------------------------------------------------------------------
// use crate::synchronization::interface::Mutex;
//
// /// Passthrough of `args` to the `core::fmt::Write` implementation, but guarded by a Mutex to
// /// serialize access.
// impl interface::Write for Console {
// fn write_fmt(&self, args: core::fmt::Arguments) -> fmt::Result {
// self.inner.lock(|inner| fmt::Write::write_fmt(inner, args))
// }
// }
//
// /// Dispatch the respective function to the currently stored output device.
// impl interface::ConsoleOps for Console {
// // @todo implement utf8 serialization here!
// fn write_char(&self, c: char) {
// self.inner.lock(|con| con.current_ptr().write_char(c));
// }
//
// fn write_string(&self, string: &str) {
// self.inner
// .lock(|con| con.current_ptr().write_string(string));
// }
//
// // @todo implement utf8 deserialization here!
// fn read_char(&self) -> char {
// self.inner.lock(|con| con.current_ptr().read_char())
// }
// }
//
// impl SerialOps for Console {
// fn read_byte(&self) -> u8 {
// self.inner.lock(|con| con.current_ptr().read_byte())
// }
// fn write_byte(&self, byte: u8) {
// self.inner.lock(|con| con.current_ptr().write_byte(byte))
// }
// fn flush(&self) {
// self.inner.lock(|con| con.current_ptr().flush())
// }
// fn clear_rx(&self) {
// self.inner.lock(|con| con.current_ptr().clear_rx())
// }
// }
//
// impl interface::All for Console {}
//
// impl Default for Console {
// fn default() -> Self {
// Self::new()
// }
// }
//
// impl Drop for Console {
// fn drop(&mut self) {}
// }
//
// //------------------------------------------------------------------------------
// // Device Interface Code
// //------------------------------------------------------------------------------
//
// /// Possible outputs which the console can store.
// enum Output {
// None(NullConsole),
// MiniUart(MiniUart),
// Uart(PL011Uart),
// }
//
// /// Generate boilerplate for converting into one of Output enum values
// macro make_from($optname:ident, $name:ty) {
// impl From<$name> for Output {
// fn from(instance: $name) -> Self {
// Output::$optname(instance)
// }
// }
// }
//
// make_from!(None, NullConsole);
// make_from!(MiniUart, PreparedMiniUart);
// make_from!(Uart, PreparedPL011Uart);

View File

@ -1,8 +1,6 @@
/*
* SPDX-License-Identifier: BlueOak-1.0.0
*/
// pub mod console;
// pub mod null_console;
pub mod serial;
pub use serial::SerialOps;
pub mod console;
pub mod serial;

View File

@ -1,6 +1,6 @@
use crate::{
println,
sync::{interface::Mutex, NullLock},
exception, println,
synchronization::{interface::ReadWriteEx, IRQSafeNullLock, InitStateLock},
};
//--------------------------------------------------------------------------------------------------
@ -9,9 +9,12 @@ use crate::{
const NUM_DRIVERS: usize = 5;
struct DriverManagerInner {
struct DriverManagerInner<T>
where
T: 'static,
{
next_index: usize,
descriptors: [Option<DeviceDriverDescriptor>; NUM_DRIVERS],
descriptors: [Option<DeviceDriverDescriptor<T>>; NUM_DRIVERS],
}
//--------------------------------------------------------------------------------------------------
@ -20,6 +23,9 @@ struct DriverManagerInner {
pub mod interface {
pub trait DeviceDriver {
/// Different interrupt controllers might use different types for IRQ number.
type IRQNumberType: core::fmt::Display;
/// Return a compatibility string for identifying the driver.
fn compatible(&self) -> &'static str;
@ -32,6 +38,21 @@ pub mod interface {
unsafe fn init(&self) -> Result<(), &'static str> {
Ok(())
}
/// 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,
irq_number: &Self::IRQNumberType,
) -> Result<(), &'static str> {
panic!(
"Attempt to enable IRQ {} for device {}, but driver does not support this",
irq_number,
self.compatible()
)
}
}
}
@ -40,27 +61,37 @@ 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),
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 {
inner: NullLock<DriverManagerInner>,
pub struct DriverManager<T>
where
T: 'static,
{
inner: InitStateLock<DriverManagerInner<T>>,
}
//--------------------------------------------------------------------------------------------------
// Global instances
//--------------------------------------------------------------------------------------------------
static DRIVER_MANAGER: DriverManager = DriverManager::new();
static DRIVER_MANAGER: DriverManager<exception::asynchronous::IRQNumber> = DriverManager::new();
//--------------------------------------------------------------------------------------------------
// Private Code
//--------------------------------------------------------------------------------------------------
impl DriverManagerInner {
impl<T> DriverManagerInner<T>
where
T: 'static + Copy,
{
pub const fn new() -> Self {
Self {
next_index: 0,
@ -74,32 +105,37 @@ impl DriverManagerInner {
//--------------------------------------------------------------------------------------------------
/// Return a reference to the global DriverManager.
pub fn driver_manager() -> &'static DriverManager {
pub fn driver_manager() -> &'static DriverManager<exception::asynchronous::IRQNumber> {
&DRIVER_MANAGER
}
impl DeviceDriverDescriptor {
impl<T> DeviceDriverDescriptor<T> {
pub fn new(
device_driver: &'static (dyn interface::DeviceDriver + Sync),
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,
}
}
}
impl DriverManager {
impl<T> DriverManager<T>
where
T: core::fmt::Display + Copy,
{
pub const fn new() -> Self {
Self {
inner: NullLock::new(DriverManagerInner::new()),
inner: InitStateLock::new(DriverManagerInner::new()),
}
}
/// Register a device driver with the kernel.
pub fn register_driver(&self, descriptor: DeviceDriverDescriptor) {
self.inner.lock(|inner| {
pub fn register_driver(&self, descriptor: DeviceDriverDescriptor<T>) {
self.inner.write(|inner| {
assert!(inner.next_index < NUM_DRIVERS);
inner.descriptors[inner.next_index] = Some(descriptor);
inner.next_index += 1;
@ -107,8 +143,8 @@ impl DriverManager {
}
/// Helper for iterating over registered drivers.
fn for_each_descriptor(&self, f: impl FnMut(&DeviceDriverDescriptor)) {
self.inner.lock(|inner| {
fn for_each_descriptor(&self, f: impl FnMut(&DeviceDriverDescriptor<T>)) {
self.inner.read(|inner| {
inner
.descriptors
.iter()
@ -122,7 +158,7 @@ impl DriverManager {
/// # Safety
///
/// - During init, drivers might do things with system-wide impact.
pub unsafe fn init_drivers(&self) {
pub unsafe fn init_drivers_and_irqs(&self) {
self.for_each_descriptor(|descriptor| {
// 1. Initialize driver.
if let Err(x) = descriptor.device_driver.init() {
@ -144,6 +180,23 @@ impl DriverManager {
}
}
});
// 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.

View File

@ -0,0 +1,170 @@
mod null_irq_manager;
//--------------------------------------------------------------------------------------------------
// Public Definitions
//--------------------------------------------------------------------------------------------------
/// Interrupt number as defined by the BSP.
pub type IRQNumber = crate::platform::exception::asynchronous::IRQNumber;
/// Interrupt descriptor.
#[derive(Copy, Clone)]
pub struct IRQHandlerDescriptor<T>
where
T: Copy,
{
/// The IRQ number.
number: T,
/// Descriptive name.
name: &'static str,
/// Reference to handler trait object.
handler: &'static (dyn interface::IRQHandler + Sync),
}
/// IRQContext token.
///
/// An instance of this type indicates that the local core is currently executing in IRQ
/// context, aka executing an interrupt vector or subcalls of it.
///
/// Concept and implementation derived from the `CriticalSection` introduced in
/// <https://github.com/rust-embedded/bare-metal>
#[derive(Clone, Copy)]
pub struct IRQContext<'irq_context> {
_0: PhantomData<&'irq_context ()>,
}
/// Asynchronous exception handling interfaces.
pub mod interface {
/// Implemented by types that handle IRQs.
pub trait IRQHandler {
/// Called when the corresponding interrupt is asserted.
fn handle(&self) -> Result<(), &'static str>;
}
/// IRQ management functions.
///
/// The `BSP` is supposed to supply one global instance. Typically implemented by the
/// platform's interrupt controller.
pub trait IRQManager {
/// The IRQ number type depends on the implementation.
type IRQNumberType: Copy;
/// Register a handler.
fn register_handler(
&self,
irq_handler_descriptor: super::IRQHandlerDescriptor<Self::IRQNumberType>,
) -> Result<(), &'static str>;
/// Enable an interrupt in the controller.
fn enable(&self, irq_number: &Self::IRQNumberType);
/// Handle pending interrupts.
///
/// This function is called directly from the CPU's IRQ exception vector. On AArch64,
/// this means that the respective CPU core has disabled exception handling.
/// This function can therefore not be preempted and runs start to finish.
///
/// Takes an IRQContext token to ensure it can only be called from IRQ context.
#[allow(clippy::trivially_copy_pass_by_ref)]
fn handle_pending_irqs<'irq_context>(
&'irq_context self,
ic: &super::IRQContext<'irq_context>,
);
/// Print list of registered handlers.
fn print_handler(&self) {}
}
}
//--------------------------------------------------------------------------------------------------
// Global instances
//--------------------------------------------------------------------------------------------------
static IRQ_MANAGER: InitStateLock<
&'static (dyn interface::IRQManager<IRQNumberType = IRQNumber> + Sync),
> = InitStateLock::new(&null_irq_manager::NULL_IRQ_MANAGER);
use core::marker::PhantomData;
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
use crate::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.
///
/// # Safety
///
/// - This must only be called when the current core is in an interrupt context and will not
/// live beyond the end of it. That is, creation is allowed in interrupt vector functions. For
/// example, in the ARMv8-A case, in `extern "C" fn current_elx_irq()`.
/// - Note that the lifetime `'irq_context` of the returned instance is unconstrained. User code
/// must not be able to influence the lifetime picked for this type, since that might cause it
/// to be inferred to `'static`.
#[inline(always)]
pub unsafe fn new() -> Self {
IRQContext { _0: PhantomData }
}
}
/// 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
/// previous state before returning, so this is deemed safe.
#[inline(always)]
pub fn exec_with_irq_masked<T>(f: impl FnOnce() -> T) -> T {
let saved = local_irq_mask_save();
let ret = f();
local_irq_restore(saved);
ret
}
/// Register a new IRQ manager.
pub fn register_irq_manager(
new_manager: &'static (dyn interface::IRQManager<IRQNumberType = IRQNumber> + Sync),
) {
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 = IRQNumber> {
IRQ_MANAGER.read(|manager| *manager)
}

View File

@ -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");
}
}

View File

@ -0,0 +1,46 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2020-2022 Andre Richter <andre.o.richter@gmail.com>
//! Synchronous and asynchronous exception handling.
#[cfg(target_arch = "aarch64")]
use crate::arch::aarch64::exception as arch_exception;
pub mod asynchronous;
//--------------------------------------------------------------------------------------------------
// Architectural Public Reexports
//--------------------------------------------------------------------------------------------------
pub use arch_exception::{current_privilege_level, handling_init};
//--------------------------------------------------------------------------------------------------
// Public Definitions
//--------------------------------------------------------------------------------------------------
/// Kernel privilege levels.
#[allow(missing_docs)]
#[derive(Eq, PartialEq)]
pub enum PrivilegeLevel {
User,
Kernel,
Hypervisor,
Unknown,
}
//--------------------------------------------------------------------------------------------------
// Testing
//--------------------------------------------------------------------------------------------------
#[cfg(test)]
mod tests {
use super::*;
/// libmachine unit tests must execute in kernel mode.
#[test_case]
fn test_runner_executes_in_kernel_mode() {
let (level, _) = current_privilege_level();
assert!(level == PrivilegeLevel::Kernel)
}
}

View File

@ -29,24 +29,33 @@ use architecture_not_supported_sorry;
/// Architecture-specific code.
#[macro_use]
pub mod arch;
pub use arch::*;
pub mod console;
pub mod cpu;
pub mod debug;
pub mod devices;
pub mod drivers;
pub mod exception;
pub mod macros;
pub mod memory;
mod mm;
pub mod mmio_deref_wrapper;
pub mod panic;
pub mod platform;
pub mod qemu;
mod sync;
pub mod state;
mod synchronization;
pub mod tests;
pub mod time;
pub mod write_to;
/// Version string.
pub fn version() -> &'static str {
concat!(
env!("CARGO_PKG_NAME"),
" version ",
env!("CARGO_PKG_VERSION")
)
}
// The global allocator for DMA-able memory. That is, memory which is tagged
// non-cacheable in the page tables.
// #[allow(dead_code)]
@ -78,6 +87,8 @@ mod lib_tests {
/// Main for running tests.
#[no_mangle]
pub unsafe fn main() -> ! {
crate::exception::handling_init();
crate::platform::drivers::qemu_bring_up_console();
crate::test_main();
crate::qemu::semihosting::exit_success()
}

View File

@ -4,13 +4,13 @@ use {
fmt::{self, Formatter},
ops::RangeInclusive,
},
snafu::Snafu,
};
pub mod translation_table;
#[cfg(target_arch = "aarch64")]
#[path = "../arch/aarch64/memory/mmu.rs"]
mod arch_mmu;
use crate::arch::aarch64::memory::mmu as arch_mmu;
pub mod translation_table;
//--------------------------------------------------------------------------------------------------
// Architectural Public Reexports
@ -23,10 +23,12 @@ pub use arch_mmu::mmu;
/// MMU enable errors variants.
#[allow(missing_docs)]
#[derive(Debug)]
#[derive(Debug, Snafu)]
pub enum MMUEnableError {
#[snafu(display("MMU is already enabled"))]
AlreadyEnabled,
Other(&'static str),
#[snafu(display("{}", err))]
Other { err: &'static str },
}
/// Memory Management interfaces.
@ -126,15 +128,6 @@ pub struct KernelVirtualLayout<const NUM_SPECIAL_RANGES: usize> {
// Public Implementations
//--------------------------------------------------------------------------------------------------
impl fmt::Display for MMUEnableError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
MMUEnableError::AlreadyEnabled => write!(f, "MMU is already enabled"),
MMUEnableError::Other(x) => write!(f, "{}", x),
}
}
}
impl<const GRANULE_SIZE: usize> TranslationGranule<GRANULE_SIZE> {
/// The granule's size.
pub const SIZE: usize = Self::size_checked();

View File

@ -1,8 +1,7 @@
//! Translation table.
#[cfg(target_arch = "aarch64")]
#[path = "../../arch/aarch64/memory/mmu/translation_table.rs"]
mod arch_translation_table;
use crate::arch::aarch64::memory::mmu::translation_table as arch_translation_table;
//--------------------------------------------------------------------------------------------------
// Architectural Public Reexports

View File

@ -1,46 +0,0 @@
use core::{marker::PhantomData, ops};
//--------------------------------------------------------------------------------------------------
// Public Definitions
//--------------------------------------------------------------------------------------------------
pub struct MMIODerefWrapper<T> {
pub base_addr: usize, // @fixme why pub??
phantom: PhantomData<fn() -> T>,
}
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
impl<T> MMIODerefWrapper<T> {
/// Create an instance.
///
/// # Safety
///
/// You could specify any base address here, no checks.
pub const unsafe fn new(start_addr: usize) -> Self {
Self {
base_addr: start_addr,
phantom: PhantomData,
}
}
}
/// Deref to RegisterBlock
///
/// Allows writing
/// ```
/// self.GPPUD.read()
/// ```
/// instead of something along the lines of
/// ```
/// unsafe { (*GPIO::ptr()).GPPUD.read() }
/// ```
impl<T> ops::Deref for MMIODerefWrapper<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
unsafe { &*(self.base_addr as *const _) }
}
}

View File

@ -20,10 +20,11 @@ fn print_panic_info(info: &PanicInfo) {
}
pub fn handler(info: &PanicInfo) -> ! {
crate::exception::asynchronous::local_irq_mask();
// Protect against panic infinite loops if any of the following code panics itself.
panic_prevent_reenter();
print_panic_info(info);
crate::endless_sleep()
crate::cpu::endless_sleep()
}
/// We have two separate handlers because other crates may use machine crate as a dependency for
@ -69,5 +70,5 @@ fn panic_prevent_reenter() {
#[cfg(qemu)]
crate::qemu::semihosting::exit_failure();
#[cfg(not(qemu))]
crate::endless_sleep()
crate::cpu::endless_sleep()
}

View File

@ -3,7 +3,8 @@
* Copyright (c) Berkus Decker <berkus+vesper@metta.systems>
*/
pub mod rpi3;
#[cfg(any(feature = "rpi3", feature = "rpi4"))]
pub mod raspberrypi;
#[cfg(any(feature = "rpi3", feature = "rpi4"))]
pub use rpi3::*;
pub use raspberrypi::*;

View File

@ -0,0 +1,143 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2020-2022 Andre Richter <andre.o.richter@gmail.com>
//! GICC Driver - GIC CPU interface.
use {
crate::{bsp::device_driver::common::MMIODerefWrapper, exception},
tock_registers::{
interfaces::{Readable, Writeable},
register_bitfields, register_structs,
registers::ReadWrite,
},
};
//--------------------------------------------------------------------------------------------------
// Private Definitions
//--------------------------------------------------------------------------------------------------
register_bitfields! {
u32,
/// CPU Interface Control Register
CTLR [
Enable OFFSET(0) NUMBITS(1) []
],
/// Interrupt Priority Mask Register
PMR [
Priority OFFSET(0) NUMBITS(8) []
],
/// Interrupt Acknowledge Register
IAR [
InterruptID OFFSET(0) NUMBITS(10) []
],
/// End of Interrupt Register
EOIR [
EOIINTID OFFSET(0) NUMBITS(10) []
]
}
register_structs! {
#[allow(non_snake_case)]
pub RegisterBlock {
(0x000 => CTLR: ReadWrite<u32, CTLR::Register>),
(0x004 => PMR: ReadWrite<u32, PMR::Register>),
(0x008 => _reserved1),
(0x00C => IAR: ReadWrite<u32, IAR::Register>),
(0x010 => EOIR: ReadWrite<u32, EOIR::Register>),
(0x014 => @END),
}
}
/// Abstraction for the associated MMIO registers.
type Registers = MMIODerefWrapper<RegisterBlock>;
//--------------------------------------------------------------------------------------------------
// Public Definitions
//--------------------------------------------------------------------------------------------------
/// Representation of the GIC CPU interface.
pub struct GICC {
registers: Registers,
}
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
impl GICC {
/// Create an instance.
///
/// # Safety
///
/// - The user must ensure to provide a correct MMIO start address.
pub const unsafe fn new(mmio_start_addr: usize) -> Self {
Self {
registers: Registers::new(mmio_start_addr),
}
}
/// Accept interrupts of any priority.
///
/// Quoting the GICv2 Architecture Specification:
///
/// "Writing 255 to the GICC_PMR always sets it to the largest supported priority field
/// value."
///
/// # Safety
///
/// - GICC MMIO registers are banked per CPU core. It is therefore safe to have `&self` instead
/// of `&mut self`.
pub fn priority_accept_all(&self) {
self.registers.PMR.write(PMR::Priority.val(255)); // Comment in arch spec.
}
/// Enable the interface - start accepting IRQs.
///
/// # Safety
///
/// - GICC MMIO registers are banked per CPU core. It is therefore safe to have `&self` instead
/// of `&mut self`.
pub fn enable(&self) {
self.registers.CTLR.write(CTLR::Enable::SET);
}
/// Extract the number of the highest-priority pending IRQ.
///
/// Can only be called from IRQ context, which is ensured by taking an `IRQContext` token.
///
/// # Safety
///
/// - GICC MMIO registers are banked per CPU core. It is therefore safe to have `&self` instead
/// of `&mut self`.
#[allow(clippy::trivially_copy_pass_by_ref)]
pub fn pending_irq_number<'irq_context>(
&self,
_ic: &exception::asynchronous::IRQContext<'irq_context>,
) -> usize {
self.registers.IAR.read(IAR::InterruptID) as usize
}
/// Complete handling of the currently active IRQ.
///
/// Can only be called from IRQ context, which is ensured by taking an `IRQContext` token.
///
/// To be called after `pending_irq_number()`.
///
/// # Safety
///
/// - GICC MMIO registers are banked per CPU core. It is therefore safe to have `&self` instead
/// of `&mut self`.
#[allow(clippy::trivially_copy_pass_by_ref)]
pub fn mark_comleted<'irq_context>(
&self,
irq_number: u32,
_ic: &exception::asynchronous::IRQContext<'irq_context>,
) {
self.registers.EOIR.write(EOIR::EOIINTID.val(irq_number));
}
}

View File

@ -0,0 +1,202 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2020-2022 Andre Richter <andre.o.richter@gmail.com>
//! GICD Driver - GIC Distributor.
//!
//! # Glossary
//! - SPI - Shared Peripheral Interrupt.
use {
crate::{
bsp::device_driver::common::MMIODerefWrapper,
state,
synchronization::{self, IRQSafeNullLock},
},
tock_registers::{
interfaces::{Readable, Writeable},
register_bitfields, register_structs,
registers::{ReadOnly, ReadWrite},
},
};
//--------------------------------------------------------------------------------------------------
// Private Definitions
//--------------------------------------------------------------------------------------------------
register_bitfields! {
u32,
/// Distributor Control Register
CTLR [
Enable OFFSET(0) NUMBITS(1) []
],
/// Interrupt Controller Type Register
TYPER [
ITLinesNumber OFFSET(0) NUMBITS(5) []
],
/// Interrupt Processor Targets Registers
ITARGETSR [
Offset3 OFFSET(24) NUMBITS(8) [],
Offset2 OFFSET(16) NUMBITS(8) [],
Offset1 OFFSET(8) NUMBITS(8) [],
Offset0 OFFSET(0) NUMBITS(8) []
]
}
register_structs! {
#[allow(non_snake_case)]
SharedRegisterBlock {
(0x000 => CTLR: ReadWrite<u32, CTLR::Register>),
(0x004 => TYPER: ReadOnly<u32, TYPER::Register>),
(0x008 => _reserved1),
(0x104 => ISENABLER: [ReadWrite<u32>; 31]),
(0x180 => _reserved2),
(0x820 => ITARGETSR: [ReadWrite<u32, ITARGETSR::Register>; 248]),
(0xC00 => @END),
}
}
register_structs! {
#[allow(non_snake_case)]
BankedRegisterBlock {
(0x000 => _reserved1),
(0x100 => ISENABLER: ReadWrite<u32>),
(0x104 => _reserved2),
(0x800 => ITARGETSR: [ReadOnly<u32, ITARGETSR::Register>; 8]),
(0x820 => @END),
}
}
/// Abstraction for the non-banked parts of the associated MMIO registers.
type SharedRegisters = MMIODerefWrapper<SharedRegisterBlock>;
/// Abstraction for the banked parts of the associated MMIO registers.
type BankedRegisters = MMIODerefWrapper<BankedRegisterBlock>;
//--------------------------------------------------------------------------------------------------
// Public Definitions
//--------------------------------------------------------------------------------------------------
/// Representation of the GIC Distributor.
pub struct GICD {
/// Access to shared registers is guarded with a lock.
shared_registers: IRQSafeNullLock<SharedRegisters>,
/// Access to banked registers is unguarded.
banked_registers: BankedRegisters,
}
//--------------------------------------------------------------------------------------------------
// Private Code
//--------------------------------------------------------------------------------------------------
impl SharedRegisters {
/// Return the number of IRQs that this HW implements.
#[inline(always)]
fn num_irqs(&mut self) -> usize {
// Query number of implemented IRQs.
//
// Refer to GICv2 Architecture Specification, Section 4.3.2.
((self.TYPER.read(TYPER::ITLinesNumber) as usize) + 1) * 32
}
/// Return a slice of the implemented ITARGETSR.
#[inline(always)]
fn implemented_itargets_slice(&mut self) -> &[ReadWrite<u32, ITARGETSR::Register>] {
assert!(self.num_irqs() >= 36);
// Calculate the max index of the shared ITARGETSR array.
//
// The first 32 IRQs are private, so not included in `shared_registers`. Each ITARGETS
// register has four entries, so shift right by two. Subtract one because we start
// counting at zero.
let spi_itargetsr_max_index = ((self.num_irqs() - 32) >> 2) - 1;
// Rust automatically inserts slice range sanity check, i.e. max >= min.
&self.ITARGETSR[0..spi_itargetsr_max_index]
}
}
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
use synchronization::interface::Mutex;
impl GICD {
/// Create an instance.
///
/// # Safety
///
/// - The user must ensure to provide a correct MMIO start address.
pub const unsafe fn new(mmio_start_addr: usize) -> Self {
Self {
shared_registers: IRQSafeNullLock::new(SharedRegisters::new(mmio_start_addr)),
banked_registers: BankedRegisters::new(mmio_start_addr),
}
}
/// Use a banked ITARGETSR to retrieve the executing core's GIC target mask.
///
/// Quoting the GICv2 Architecture Specification:
///
/// "GICD_ITARGETSR0 to GICD_ITARGETSR7 are read-only, and each field returns a value that
/// corresponds only to the processor reading the register."
fn local_gic_target_mask(&self) -> u32 {
self.banked_registers.ITARGETSR[0].read(ITARGETSR::Offset0)
}
/// Route all SPIs to the boot core and enable the distributor.
pub fn boot_core_init(&self) {
assert!(
state::state_manager().is_init(),
"Only allowed during kernel init phase"
);
// Target all SPIs to the boot core only.
let mask = self.local_gic_target_mask();
self.shared_registers.lock(|regs| {
for i in regs.implemented_itargets_slice().iter() {
i.write(
ITARGETSR::Offset3.val(mask)
+ ITARGETSR::Offset2.val(mask)
+ ITARGETSR::Offset1.val(mask)
+ ITARGETSR::Offset0.val(mask),
);
}
regs.CTLR.write(CTLR::Enable::SET);
});
}
/// Enable an interrupt.
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
// (division by 32) and arrive at the index for the respective ISENABLER[i].
let enable_reg_index = irq_num >> 5;
let enable_bit: u32 = 1u32 << (irq_num % 32);
// Check if we are handling a private or shared IRQ.
match irq_num {
// Private.
0..=31 => {
let enable_reg = &self.banked_registers.ISENABLER;
enable_reg.set(enable_reg.get() | enable_bit);
}
// Shared.
_ => {
let enable_reg_index_shared = enable_reg_index - 1;
self.shared_registers.lock(|regs| {
let enable_reg = &regs.ISENABLER[enable_reg_index_shared];
enable_reg.set(enable_reg.get() | enable_bit);
});
}
}
}
}

View File

@ -0,0 +1,226 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2020-2022 Andre Richter <andre.o.richter@gmail.com>
//! GICv2 Driver - ARM Generic Interrupt Controller v2.
//!
//! The following is a collection of excerpts with useful information from
//! - `Programmer's Guide for ARMv8-A`
//! - `ARM Generic Interrupt Controller Architecture Specification`
//!
//! # Programmer's Guide - 10.6.1 Configuration
//!
//! The GIC is accessed as a memory-mapped peripheral.
//!
//! All cores can access the common Distributor, but the CPU interface is banked, that is, each core
//! uses the same address to access its own private CPU interface.
//!
//! It is not possible for a core to access the CPU interface of another core.
//!
//! # Architecture Specification - 10.6.2 Initialization
//!
//! Both the Distributor and the CPU interfaces are disabled at reset. The GIC must be initialized
//! after reset before it can deliver interrupts to the core.
//!
//! In the Distributor, software must configure the priority, target, security and enable individual
//! interrupts. The Distributor must subsequently be enabled through its control register
//! (GICD_CTLR). For each CPU interface, software must program the priority mask and preemption
//! settings.
//!
//! Each CPU interface block itself must be enabled through its control register (GICD_CTLR). This
//! prepares the GIC to deliver interrupts to the core.
//!
//! Before interrupts are expected in the core, software prepares the core to take interrupts by
//! setting a valid interrupt vector in the vector table, and clearing interrupt mask bits in
//! PSTATE, and setting the routing controls.
//!
//! The entire interrupt mechanism in the system can be disabled by disabling the Distributor.
//! Interrupt delivery to an individual core can be disabled by disabling its CPU interface.
//! Individual interrupts can also be disabled (or enabled) in the distributor.
//!
//! For an interrupt to reach the core, the individual interrupt, Distributor and CPU interface must
//! all be enabled. The interrupt also needs to be of sufficient priority, that is, higher than the
//! core's priority mask.
//!
//! # Architecture Specification - 1.4.2 Interrupt types
//!
//! - Peripheral interrupt
//! - Private Peripheral Interrupt (PPI)
//! - This is a peripheral interrupt that is specific to a single processor.
//! - Shared Peripheral Interrupt (SPI)
//! - This is a peripheral interrupt that the Distributor can route to any of a specified
//! combination of processors.
//!
//! - Software-generated interrupt (SGI)
//! - This is an interrupt generated by software writing to a GICD_SGIR register in the GIC. The
//! system uses SGIs for interprocessor communication.
//! - An SGI has edge-triggered properties. The software triggering of the interrupt is
//! equivalent to the edge transition of the interrupt request signal.
//! - When an SGI occurs in a multiprocessor implementation, the CPUID field in the Interrupt
//! Acknowledge Register, GICC_IAR, or the Aliased Interrupt Acknowledge Register, GICC_AIAR,
//! identifies the processor that requested the interrupt.
//!
//! # Architecture Specification - 2.2.1 Interrupt IDs
//!
//! Interrupts from sources are identified using ID numbers. Each CPU interface can see up to 1020
//! interrupts. The banking of SPIs and PPIs increases the total number of interrupts supported by
//! the Distributor.
//!
//! The GIC assigns interrupt ID numbers ID0-ID1019 as follows:
//! - Interrupt numbers 32..1019 are used for SPIs.
//! - Interrupt numbers 0..31 are used for interrupts that are private to a CPU interface. These
//! interrupts are banked in the Distributor.
//! - A banked interrupt is one where the Distributor can have multiple interrupts with the
//! same ID. A banked interrupt is identified uniquely by its ID number and its associated
//! CPU interface number. Of the banked interrupt IDs:
//! - 00..15 SGIs
//! - 16..31 PPIs
mod gicc;
mod gicd;
use crate::{
bsp::{self, device_driver::common::BoundedUsize},
cpu, driver, exception,
synchronization::{self, InitStateLock},
};
//--------------------------------------------------------------------------------------------------
// Private Definitions
//--------------------------------------------------------------------------------------------------
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 = BoundedUsize<{ GICv2::MAX_IRQ_NUMBER }>;
/// Representation of the GIC.
pub struct GICv2 {
/// The Distributor.
gicd: gicd::GICD,
/// The CPU Interface.
gicc: gicc::GICC,
/// Stores registered IRQ handlers. Writable only during kernel init. RO afterwards.
handler_table: InitStateLock<HandlerTable>,
}
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
impl GICv2 {
const MAX_IRQ_NUMBER: usize = 300; // Normally 1019, but keep it lower to save some space.
pub const COMPATIBLE: &'static str = "GICv2 (ARM Generic Interrupt Controller v2)";
/// Create an instance.
///
/// # Safety
///
/// - The user must ensure to provide a correct MMIO start address.
pub const unsafe fn new(gicd_mmio_start_addr: usize, gicc_mmio_start_addr: usize) -> Self {
Self {
gicd: gicd::GICD::new(gicd_mmio_start_addr),
gicc: gicc::GICC::new(gicc_mmio_start_addr),
handler_table: InitStateLock::new([None; IRQNumber::MAX_INCLUSIVE + 1]),
}
}
}
//------------------------------------------------------------------------------
// OS Interface Code
//------------------------------------------------------------------------------
use synchronization::interface::ReadWriteEx;
impl driver::interface::DeviceDriver for GICv2 {
type IRQNumberType = IRQNumber;
fn compatible(&self) -> &'static str {
Self::COMPATIBLE
}
unsafe fn init(&self) -> Result<(), &'static str> {
if bsp::cpu::BOOT_CORE_ID == cpu::smp::core_id() {
self.gicd.boot_core_init();
}
self.gicc.priority_accept_all();
self.gicc.enable();
Ok(())
}
}
impl exception::asynchronous::interface::IRQManager for GICv2 {
type IRQNumberType = IRQNumber;
fn register_handler(
&self,
irq_handler_descriptor: exception::asynchronous::IRQHandlerDescriptor<Self::IRQNumberType>,
) -> Result<(), &'static str> {
self.handler_table.write(|table| {
let irq_number = irq_handler_descriptor.number().get();
if table[irq_number].is_some() {
return Err("IRQ handler already registered");
}
table[irq_number] = Some(irq_handler_descriptor);
Ok(())
})
}
fn enable(&self, irq_number: &Self::IRQNumberType) {
self.gicd.enable(irq_number);
}
fn handle_pending_irqs<'irq_context>(
&'irq_context self,
ic: &exception::asynchronous::IRQContext<'irq_context>,
) {
// Extract the highest priority pending IRQ number from the Interrupt Acknowledge Register
// (IAR).
let irq_number = self.gicc.pending_irq_number(ic);
// Guard against spurious interrupts.
if irq_number > GICv2::MAX_IRQ_NUMBER {
return;
}
// Call the IRQ handler. Panic if there is none.
self.handler_table.read(|table| {
match table[irq_number] {
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");
}
}
});
// Signal completion of handling.
self.gicc.mark_comleted(irq_number as u32, ic);
}
fn print_handler(&self) {
use crate::info;
info!(" Peripheral handler:");
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());
}
}
});
}
}

View File

@ -0,0 +1,9 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2020-2022 Andre Richter <andre.o.richter@gmail.com>
//! ARM driver top level.
pub mod gicv2;
pub use gicv2::*;

View File

@ -6,7 +6,13 @@
*/
use {
crate::{mmio_deref_wrapper::MMIODerefWrapper, platform::BcmHost, time},
crate::{
platform::{
device_driver::{common::MMIODerefWrapper, IRQNumber},
BcmHost,
},
time,
},
core::{marker::PhantomData, time::Duration},
tock_registers::{
fields::FieldValue,
@ -96,6 +102,8 @@ pub struct GPIO {
pub const GPIO_BASE: usize = BcmHost::get_peripheral_address() + 0x20_0000;
impl crate::drivers::interface::DeviceDriver for GPIO {
type IRQNumberType = IRQNumber;
fn compatible(&self) -> &'static str {
Self::COMPATIBLE
}

View File

@ -0,0 +1,154 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2020-2022 Andre Richter <andre.o.richter@gmail.com>
//! Interrupt Controller Driver.
mod peripheral_ic;
use {
crate::{
drivers,
exception::{self, asynchronous::IRQHandlerDescriptor},
platform::device_driver::common::BoundedUsize,
},
core::fmt,
};
//--------------------------------------------------------------------------------------------------
// Private Definitions
//--------------------------------------------------------------------------------------------------
/// Wrapper struct for a bitmask indicating pending IRQ numbers.
struct PendingIRQs {
bitmask: u64,
}
//--------------------------------------------------------------------------------------------------
// Public Definitions
//--------------------------------------------------------------------------------------------------
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)]
#[allow(missing_docs)]
pub enum IRQNumber {
Local(LocalIRQ),
Peripheral(PeripheralIRQ),
}
/// Representation of the Interrupt Controller.
pub struct InterruptController {
periph: peripheral_ic::PeripheralIC,
}
//--------------------------------------------------------------------------------------------------
// Private Code
//--------------------------------------------------------------------------------------------------
impl PendingIRQs {
pub fn new(bitmask: u64) -> Self {
Self { bitmask }
}
}
impl Iterator for PendingIRQs {
type Item = usize;
fn next(&mut self) -> Option<Self::Item> {
if self.bitmask == 0 {
return None;
}
let next = self.bitmask.trailing_zeros() as usize;
self.bitmask &= self.bitmask.wrapping_sub(1);
Some(next)
}
}
//--------------------------------------------------------------------------------------------------
// 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;
const MAX_PERIPHERAL_IRQ_NUMBER: usize = 63;
pub const COMPATIBLE: &'static str = "BCM Interrupt Controller";
/// Create an instance.
///
/// # Safety
///
/// - The user must ensure to provide a correct MMIO start address.
pub const unsafe fn new(periph_mmio_start_addr: usize) -> Self {
Self {
periph: peripheral_ic::PeripheralIC::new(periph_mmio_start_addr),
}
}
}
//------------------------------------------------------------------------------
// OS Interface Code
//------------------------------------------------------------------------------
impl drivers::interface::DeviceDriver for InterruptController {
type IRQNumberType = IRQNumber;
fn compatible(&self) -> &'static str {
Self::COMPATIBLE
}
}
impl exception::asynchronous::interface::IRQManager for InterruptController {
type IRQNumberType = IRQNumber;
fn register_handler(
&self,
irq_handler_descriptor: exception::asynchronous::IRQHandlerDescriptor<Self::IRQNumberType>,
) -> Result<(), &'static str> {
match irq_handler_descriptor.number() {
IRQNumber::Local(_) => unimplemented!("Local IRQ controller not implemented."),
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) {
match irq {
IRQNumber::Local(_) => unimplemented!("Local IRQ controller not implemented."),
IRQNumber::Peripheral(pirq) => self.periph.enable(pirq),
}
}
fn handle_pending_irqs<'irq_context>(
&'irq_context self,
ic: &exception::asynchronous::IRQContext<'irq_context>,
) {
// It can only be a peripheral IRQ pending because enable() does not support local IRQs yet.
self.periph.handle_pending_irqs(ic)
}
fn print_handler(&self) {
self.periph.print_handler();
}
}

View File

@ -0,0 +1,172 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2020-2022 Andre Richter <andre.o.richter@gmail.com>
//! Peripheral Interrupt Controller Driver.
//!
//! # Resources
//!
//! - <https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf>
use {
super::{PendingIRQs, PeripheralIRQ},
crate::{
exception,
platform::device_driver::common::MMIODerefWrapper,
synchronization::{self, IRQSafeNullLock, InitStateLock},
},
tock_registers::{
interfaces::{Readable, Writeable},
register_structs,
registers::{ReadOnly, WriteOnly},
},
};
//--------------------------------------------------------------------------------------------------
// Private Definitions
//--------------------------------------------------------------------------------------------------
register_structs! {
#[allow(non_snake_case)]
WORegisterBlock {
(0x00 => _reserved1),
(0x10 => ENABLE_1: WriteOnly<u32>),
(0x14 => ENABLE_2: WriteOnly<u32>),
(0x18 => @END),
}
}
register_structs! {
#[allow(non_snake_case)]
RORegisterBlock {
(0x00 => _reserved1),
(0x04 => PENDING_1: ReadOnly<u32>),
(0x08 => PENDING_2: ReadOnly<u32>),
(0x0c => @END),
}
}
/// Abstraction for the WriteOnly parts of the associated MMIO registers.
type WriteOnlyRegisters = MMIODerefWrapper<WORegisterBlock>;
/// Abstraction for the ReadOnly parts of the associated MMIO registers.
type ReadOnlyRegisters = MMIODerefWrapper<RORegisterBlock>;
type HandlerTable = [Option<exception::asynchronous::IRQHandlerDescriptor<PeripheralIRQ>>;
PeripheralIRQ::MAX_INCLUSIVE + 1];
//--------------------------------------------------------------------------------------------------
// Public Definitions
//--------------------------------------------------------------------------------------------------
/// Representation of the peripheral interrupt controller.
pub struct PeripheralIC {
/// Access to write registers is guarded with a lock.
wo_registers: IRQSafeNullLock<WriteOnlyRegisters>,
/// Register read access is unguarded.
ro_registers: ReadOnlyRegisters,
/// Stores registered IRQ handlers. Writable only during kernel init. RO afterwards.
handler_table: InitStateLock<HandlerTable>,
}
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
impl PeripheralIC {
/// Create an instance.
///
/// # Safety
///
/// - The user must ensure to provide a correct MMIO start address.
pub const unsafe fn new(mmio_start_addr: usize) -> Self {
Self {
wo_registers: IRQSafeNullLock::new(WriteOnlyRegisters::new(mmio_start_addr)),
ro_registers: ReadOnlyRegisters::new(mmio_start_addr),
handler_table: InitStateLock::new([None; PeripheralIRQ::MAX_INCLUSIVE + 1]),
}
}
/// Query the list of pending IRQs.
fn pending_irqs(&self) -> PendingIRQs {
let pending_mask: u64 = (u64::from(self.ro_registers.PENDING_2.get()) << 32)
| u64::from(self.ro_registers.PENDING_1.get());
PendingIRQs::new(pending_mask)
}
}
//------------------------------------------------------------------------------
// OS Interface Code
//------------------------------------------------------------------------------
use synchronization::interface::{Mutex, ReadWriteEx};
impl exception::asynchronous::interface::IRQManager for PeripheralIC {
type IRQNumberType = PeripheralIRQ;
fn register_handler(
&self,
irq_handler_descriptor: exception::asynchronous::IRQHandlerDescriptor<Self::IRQNumberType>,
) -> Result<(), &'static str> {
self.handler_table.write(|table| {
let irq_number = irq_handler_descriptor.number().get();
if table[irq_number].is_some() {
return Err("IRQ handler already registered");
}
table[irq_number] = Some(irq_handler_descriptor);
Ok(())
})
}
fn enable(&self, irq: &Self::IRQNumberType) {
self.wo_registers.lock(|regs| {
let enable_reg = if irq.get() <= 31 {
&regs.ENABLE_1
} else {
&regs.ENABLE_2
};
let enable_bit: u32 = 1 << (irq.get() % 32);
// Writing a 1 to a bit will set the corresponding IRQ enable bit. All other IRQ enable
// bits are unaffected. So we don't need read and OR'ing here.
enable_reg.set(enable_bit);
});
}
fn handle_pending_irqs<'irq_context>(
&'irq_context self,
_ic: &exception::asynchronous::IRQContext<'irq_context>,
) {
self.handler_table.read(|table| {
for irq_number in self.pending_irqs() {
match table[irq_number] {
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");
}
}
}
})
}
fn print_handler(&self) {
use crate::info;
info!(" Peripheral handler:");
self.handler_table.read(|table| {
for (i, opt) in table.iter().enumerate() {
if let Some(handler) = opt {
info!(" {: >3}. {}", i, handler.name());
}
}
});
}
}

View File

@ -10,10 +10,13 @@ use tock_registers::interfaces::{Readable, Writeable};
use {
crate::{
console::interface,
devices::SerialOps,
mmio_deref_wrapper::MMIODerefWrapper,
platform::{device_driver::gpio, BcmHost},
sync::{interface::Mutex, NullLock},
devices::serial::SerialOps,
exception::asynchronous::IRQNumber,
platform::{
device_driver::{common::MMIODerefWrapper, gpio},
BcmHost,
},
synchronization::{interface::Mutex, IRQSafeNullLock},
},
cfg_if::cfg_if,
core::{
@ -155,7 +158,7 @@ struct MiniUartInner {
}
pub struct MiniUart {
inner: NullLock<MiniUartInner>,
inner: IRQSafeNullLock<MiniUartInner>,
}
/// Divisor values for common baud rates
@ -173,6 +176,8 @@ impl From<Rate> for u32 {
pub const UART1_BASE: usize = BcmHost::get_peripheral_address() + 0x21_5000;
impl crate::drivers::interface::DeviceDriver for MiniUart {
type IRQNumberType = IRQNumber;
fn compatible(&self) -> &'static str {
Self::COMPATIBLE
}
@ -192,7 +197,7 @@ impl MiniUart {
/// - The user must ensure to provide a correct MMIO start address.
pub const unsafe fn new(base_addr: usize) -> Self {
Self {
inner: NullLock::new(MiniUartInner::new(base_addr)),
inner: IRQSafeNullLock::new(MiniUartInner::new(base_addr)),
}
}
@ -255,7 +260,7 @@ impl MiniUartInner {
fn flush_internal(&self) {
use tock_registers::interfaces::Readable;
crate::arch::loop_until(|| self.registers.AUX_MU_STAT.is_set(AUX_MU_STAT::TX_DONE));
crate::cpu::loop_until(|| self.registers.AUX_MU_STAT.is_set(AUX_MU_STAT::TX_DONE));
}
}
@ -273,7 +278,7 @@ impl SerialOps for MiniUartInner {
fn read_byte(&self) -> u8 {
use tock_registers::interfaces::Readable;
// wait until something is in the buffer
crate::arch::loop_until(|| {
crate::cpu::loop_until(|| {
self.registers
.AUX_MU_STAT
.is_set(AUX_MU_STAT::SYMBOL_AVAILABLE)
@ -286,7 +291,7 @@ impl SerialOps for MiniUartInner {
fn write_byte(&self, b: u8) {
use tock_registers::interfaces::{Readable, Writeable};
// wait until we can send
crate::arch::loop_until(|| {
crate::cpu::loop_until(|| {
self.registers
.AUX_MU_STAT
.is_set(AUX_MU_STAT::SPACE_AVAILABLE)
@ -306,7 +311,7 @@ impl SerialOps for MiniUartInner {
/// consumed.
fn clear_rx(&self) {
use tock_registers::interfaces::Readable;
crate::arch::loop_while(|| {
crate::cpu::loop_while(|| {
let pending = self
.registers
.AUX_MU_STAT

View File

@ -0,0 +1,15 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2018-2022 Andre Richter <andre.o.richter@gmail.com>
//! BCM driver top level.
pub mod gpio;
#[cfg(feature = "rpi3")]
pub mod interrupt_controller;
pub mod mini_uart;
pub mod pl011_uart;
#[cfg(feature = "rpi3")]
pub use interrupt_controller::*;
pub use {gpio::*, mini_uart::*, pl011_uart::*};

View File

@ -10,16 +10,15 @@
use {
crate::{
arch::loop_while,
console::interface,
devices::SerialOps,
mmio_deref_wrapper::MMIODerefWrapper,
cpu::loop_while,
devices::serial::SerialOps,
platform::{
device_driver::gpio,
device_driver::{common::MMIODerefWrapper, gpio, IRQNumber},
mailbox::{self, Mailbox, MailboxOps},
BcmHost,
},
sync::{interface::Mutex, NullLock},
synchronization::{interface::Mutex, IRQSafeNullLock},
},
core::fmt::{self, Arguments},
snafu::Snafu,
@ -233,7 +232,7 @@ struct PL011UartInner {
//--------------------------------------------------------------------------------------------------
pub struct PL011Uart {
inner: NullLock<PL011UartInner>,
inner: IRQSafeNullLock<PL011UartInner>,
}
pub struct RateDivisors {
@ -290,7 +289,7 @@ impl PL011Uart {
/// - The user must ensure to provide a correct MMIO start address.
pub const unsafe fn new(base_addr: usize) -> Self {
Self {
inner: NullLock::new(PL011UartInner::new(base_addr)),
inner: IRQSafeNullLock::new(PL011UartInner::new(base_addr)),
}
}
@ -462,6 +461,8 @@ impl interface::Write for PL011Uart {
//--------------------------------------------------------------------------------------------------
impl crate::drivers::interface::DeviceDriver for PL011Uart {
type IRQNumberType = IRQNumber;
fn compatible(&self) -> &'static str {
Self::COMPATIBLE
}

View File

@ -0,0 +1,74 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2020-2022 Andre Richter <andre.o.richter@gmail.com>
//! Common device driver code.
use core::{fmt, marker::PhantomData, ops};
//--------------------------------------------------------------------------------------------------
// Public Definitions
//--------------------------------------------------------------------------------------------------
pub struct MMIODerefWrapper<T> {
pub base_addr: usize, // @todo unmake public, GPIO::Pin uses it
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
//--------------------------------------------------------------------------------------------------
impl<T> MMIODerefWrapper<T> {
/// Create an instance.
pub const fn new(base_addr: usize) -> Self {
Self {
base_addr,
phantom: PhantomData,
}
}
}
// Deref to RegisterBlock
///
/// Allows writing
/// ```
/// self.GPPUD.read()
/// ```
/// instead of something along the lines of
/// ```
/// unsafe { (*GPIO::ptr()).GPPUD.read() }
/// ```
impl<T> ops::Deref for MMIODerefWrapper<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
unsafe { &*(self.base_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)
}
}

View File

@ -0,0 +1,17 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2018-2022 Andre Richter <andre.o.richter@gmail.com>
//! Device driver.
#[cfg(feature = "rpi4")]
mod arm;
#[cfg(any(feature = "rpi3", feature = "rpi4"))]
mod bcm;
pub mod common;
#[cfg(feature = "rpi4")]
pub use arm::*;
#[cfg(any(feature = "rpi3", feature = "rpi4"))]
pub use bcm::*;

View File

@ -1,5 +1,8 @@
use {
crate::{console, drivers, platform::device_driver},
crate::{
console, drivers,
platform::{device_driver, memory::map::mmio},
},
core::sync::atomic::{AtomicBool, Ordering},
};
@ -37,6 +40,13 @@ pub unsafe fn init() -> Result<(), &'static str> {
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(test)]
pub fn qemu_bring_up_console() {
console::register_console(&PL011_UART);
}
//--------------------------------------------------------------------------------------------------
// Global instances
//--------------------------------------------------------------------------------------------------
@ -47,6 +57,14 @@ static PL011_UART: device_driver::PL011Uart =
unsafe { device_driver::PL011Uart::new(device_driver::UART0_BASE) };
static GPIO: device_driver::GPIO = unsafe { device_driver::GPIO::new(device_driver::GPIO_BASE) };
#[cfg(feature = "rpi3")]
static INTERRUPT_CONTROLLER: device_driver::InterruptController =
unsafe { device_driver::InterruptController::new(mmio::PERIPHERAL_IC_START) };
#[cfg(feature = "rpi4")]
static INTERRUPT_CONTROLLER: device_driver::GICv2 =
unsafe { device_driver::GICv2::new(mmio::GICD_START, mmio::GICC_START) };
//--------------------------------------------------------------------------------------------------
// Private Code
//--------------------------------------------------------------------------------------------------
@ -78,14 +96,14 @@ fn driver_uart() -> Result<(), &'static str> {
// drivers::driver_manager().register_driver(uart_descriptor);
let uart_descriptor =
drivers::DeviceDriverDescriptor::new(&PL011_UART, Some(post_init_pl011_uart));
drivers::DeviceDriverDescriptor::new(&PL011_UART, Some(post_init_pl011_uart), None);
drivers::driver_manager().register_driver(uart_descriptor);
Ok(())
}
fn driver_gpio() -> Result<(), &'static str> {
let gpio_descriptor = drivers::DeviceDriverDescriptor::new(&GPIO, Some(post_init_gpio));
let gpio_descriptor = drivers::DeviceDriverDescriptor::new(&GPIO, Some(post_init_gpio), None);
drivers::driver_manager().register_driver(gpio_descriptor);
Ok(())

View File

@ -0,0 +1,26 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2020-2022 Andre Richter <andre.o.richter@gmail.com>
//! Platform asynchronous exception handling.
//--------------------------------------------------------------------------------------------------
// Public Definitions
//--------------------------------------------------------------------------------------------------
/// Export for reuse in generic asynchronous.rs.
pub use crate::platform::device_driver::IRQNumber;
#[cfg(feature = "rpi3")]
pub(in crate::platform) mod irq_map {
use crate::platform::device_driver::{IRQNumber, PeripheralIRQ};
pub const PL011_UART: IRQNumber = IRQNumber::Peripheral(PeripheralIRQ::new(57));
}
#[cfg(feature = "rpi4")]
pub(in crate::platform) mod irq_map {
use crate::platform::device_driver::IRQNumber;
pub const PL011_UART: IRQNumber = IRQNumber::new(153);
}

View File

@ -0,0 +1 @@
pub mod asynchronous;

View File

@ -13,7 +13,7 @@
use {
super::BcmHost,
crate::{mmio_deref_wrapper::MMIODerefWrapper, println}, //DMA_ALLOCATOR
crate::{platform::device_driver::common::MMIODerefWrapper, println}, //DMA_ALLOCATOR
aarch64_cpu::asm::barrier,
core::{
alloc::{AllocError, Allocator, Layout},

View File

@ -41,7 +41,7 @@ pub static LAYOUT: KernelVirtualLayout<NUM_MEM_RANGES> = KernelVirtualLayout::ne
name: "Remapped Device MMIO",
virtual_range: remapped_mmio_range_inclusive,
physical_range_translation: Translation::Offset(
memory_map::phys::MMIO_BASE + 0x20_0000,
memory_map::mmio::MMIO_BASE + 0x20_0000,
),
attribute_fields: AttributeFields {
mem_attributes: MemAttributes::Device,
@ -74,7 +74,7 @@ pub static LAYOUT: KernelVirtualLayout<NUM_MEM_RANGES> = KernelVirtualLayout::ne
virtual_range: || {
RangeInclusive::new(
memory_map::phys::VIDEOMEM_BASE,
memory_map::phys::MMIO_BASE - 1,
memory_map::mmio::MMIO_BASE - 1,
)
},
physical_range_translation: Translation::Identity,
@ -107,8 +107,8 @@ fn remapped_mmio_range_inclusive() -> RangeInclusive<usize> {
}
fn mmio_range_inclusive() -> RangeInclusive<usize> {
RangeInclusive::new(memory_map::phys::MMIO_BASE, memory_map::phys::MMIO_END)
// RangeInclusive::new(map::phys::VIDEOMEM_BASE, map::phys::MMIO_END),
RangeInclusive::new(memory_map::mmio::MMIO_BASE, memory_map::mmio::MMIO_END)
// RangeInclusive::new(map::phys::VIDEOMEM_BASE, map::mmio::MMIO_END),
}
fn dma_range_inclusive() -> RangeInclusive<usize> {

View File

@ -43,7 +43,7 @@ extern "Rust" {
//--------------------------------------------------------------------------------------------------
/// System memory map.
/// This is a fixed memory map for RasPi3,
/// This is a fixed memory map for Raspberry Pi,
/// @todo we need to infer the memory map from the provided DTB.
#[rustfmt::skip]
pub mod map { // @todo only pub(super) for proper isolation!
@ -56,16 +56,52 @@ pub mod map { // @todo only pub(super) for proper isolation!
pub mod phys {
/// Base address of video (VC) memory.
pub const VIDEOMEM_BASE: usize = 0x3e00_0000;
}
pub const VIDEOCORE_MBOX_OFFSET: usize = 0x0000_B880;
pub const GPIO_OFFSET: usize = 0x0020_0000;
pub const UART_OFFSET: usize = 0x0020_1000;
pub const MINIUART_OFFSET: usize = 0x0021_5000;
/// Memory-mapped devices.
#[cfg(feature = "rpi3")]
pub mod mmio {
use super::*;
/// Base address of MMIO register range.
pub const MMIO_BASE: usize = 0x3F00_0000;
/// Base address of ARM<->VC mailbox area.
pub const VIDEOCORE_MBOX_BASE: usize = MMIO_BASE + 0x0000_B880;
pub const VIDEOCORE_MBOX_BASE: usize = MMIO_BASE + VIDEOCORE_MBOX_OFFSET;
/// Base address of GPIO registers.
pub const GPIO_BASE: usize = MMIO_BASE + 0x0020_0000;
pub const GPIO_BASE: usize = MMIO_BASE + GPIO_OFFSET;
/// Base address of regular UART.
pub const PL011_UART_BASE: usize = MMIO_BASE + 0x0020_1000;
pub const PL011_UART_BASE: usize = MMIO_BASE + UART_OFFSET;
/// Base address of MiniUART.
pub const MINI_UART_BASE: usize = MMIO_BASE + 0x0021_5000;
pub const MINI_UART_BASE: usize = MMIO_BASE + MINIUART_OFFSET;
/// Interrupt controller
pub const PERIPHERAL_IC_START: usize = MMIO_BASE + 0x0000_B200;
/// End of MMIO memory.
pub const MMIO_END: usize = super::END_INCLUSIVE;
}
/// Memory-mapped devices.
#[cfg(feature = "rpi4")]
pub mod mmio {
use super::*;
/// Base address of MMIO register range.
pub const MMIO_BASE: usize = 0xFE00_0000;
/// Base address of ARM<->VC mailbox area.
pub const VIDEOCORE_MBOX_BASE: usize = MMIO_BASE + VIDEOCORE_MBOX_OFFSET;
/// Base address of GPIO registers.
pub const GPIO_BASE: usize = MMIO_BASE + GPIO_OFFSET;
/// Base address of regular UART.
pub const PL011_UART_BASE: usize = MMIO_BASE + UART_OFFSET;
/// Base address of MiniUART.
pub const MINI_UART_BASE: usize = MMIO_BASE + MINIUART_OFFSET;
/// Interrupt controller
pub const GICD_START: usize = 0xFF84_1000;
pub const GICC_START: usize = 0xFF84_2000;
/// End of MMIO memory.
pub const MMIO_END: usize = super::END_INCLUSIVE;
}

View File

@ -8,6 +8,7 @@
pub mod device_driver;
pub mod display;
pub mod drivers;
pub mod exception;
pub mod fb;
pub mod mailbox;
pub mod memory;

View File

@ -11,7 +11,7 @@ use {
mailbox::{channel, Mailbox, MailboxOps},
BcmHost,
},
crate::mmio_deref_wrapper::MMIODerefWrapper,
crate::platform::device_driver::common::MMIODerefWrapper,
snafu::Snafu,
tock_registers::{
interfaces::{Readable, Writeable},
@ -116,6 +116,6 @@ impl Power {
val |= PM_PASSWORD | PM_RSTC_WRCFG_FULL_RESET;
self.registers.PM_RSTC.set(val);
crate::endless_sleep()
crate::cpu::endless_sleep()
}
}

View File

@ -8,7 +8,7 @@ use {
mailbox::{self, channel, response::VAL_LEN_FLAG, Mailbox, MailboxOps},
BcmHost,
},
crate::{platform::rpi3::mailbox::MailboxStorageRef, println},
crate::{platform::raspberrypi::mailbox::MailboxStorageRef, println},
core::convert::TryInto,
snafu::Snafu,
};

View File

@ -1,5 +0,0 @@
pub mod gpio;
pub mod mini_uart;
pub mod pl011_uart;
pub use {gpio::*, mini_uart::*, pl011_uart::*};

92
machine/src/state.rs Normal file
View File

@ -0,0 +1,92 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2020-2022 Andre Richter <andre.o.richter@gmail.com>
//! State information about the kernel itself.
use core::sync::atomic::{AtomicU8, Ordering};
//--------------------------------------------------------------------------------------------------
// Private Definitions
//--------------------------------------------------------------------------------------------------
/// Different stages in the kernel execution.
#[derive(Copy, Clone, Eq, PartialEq)]
enum State {
/// The kernel starts booting in this state.
Init,
/// The kernel transitions to this state when jumping to `kernel_main()` (at the end of
/// `kernel_init()`, after all init calls are done).
SingleCoreMain,
/// The kernel transitions to this state when it boots the secondary cores, aka switches
/// exectution mode to symmetric multiprocessing (SMP).
MultiCoreMain,
}
//--------------------------------------------------------------------------------------------------
// Public Definitions
//--------------------------------------------------------------------------------------------------
/// Maintains the kernel state and state transitions.
pub struct StateManager(AtomicU8);
//--------------------------------------------------------------------------------------------------
// Global instances
//--------------------------------------------------------------------------------------------------
static STATE_MANAGER: StateManager = StateManager::new();
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
/// Return a reference to the global StateManager.
pub fn state_manager() -> &'static StateManager {
&STATE_MANAGER
}
impl StateManager {
const INIT: u8 = 0;
const SINGLE_CORE_MAIN: u8 = 1;
const MULTI_CORE_MAIN: u8 = 2;
/// Create a new instance.
pub const fn new() -> Self {
Self(AtomicU8::new(Self::INIT))
}
/// Return the current state.
fn state(&self) -> State {
let state = self.0.load(Ordering::Acquire);
match state {
Self::INIT => State::Init,
Self::SINGLE_CORE_MAIN => State::SingleCoreMain,
Self::MULTI_CORE_MAIN => State::MultiCoreMain,
_ => panic!("Invalid KERNEL_STATE"),
}
}
/// Return if the kernel is init state.
pub fn is_init(&self) -> bool {
self.state() == State::Init
}
/// Transition from Init to SingleCoreMain.
pub fn transition_to_single_core_main(&self) {
if self
.0
.compare_exchange(
Self::INIT,
Self::SINGLE_CORE_MAIN,
Ordering::Acquire,
Ordering::Relaxed,
)
.is_err()
{
panic!("transition_to_single_core_main() called while state != Init");
}
}
}

View File

@ -1,82 +0,0 @@
/*
* SPDX-License-Identifier: MIT OR BlueOak-1.0.0
* Copyright (c) 2019 Andre Richter <andre.o.richter@gmail.com>
* Original code distributed under MIT, additional changes are under BlueOak-1.0.0
*/
use core::cell::UnsafeCell;
//--------------------------------------------------------------------------------------------------
// Public Definitions
//--------------------------------------------------------------------------------------------------
/// Synchronization interfaces.
pub mod interface {
/// Any object implementing this trait guarantees exclusive access to the data wrapped within
/// the Mutex for the duration of the provided closure.
pub trait Mutex {
/// The type of the data that is wrapped by this mutex.
type Data;
/// Locks the mutex and grants the closure temporary mutable access to the wrapped data.
fn lock<R>(&self, f: impl FnOnce(&mut Self::Data) -> R) -> R;
}
}
/// A pseudo-lock for teaching purposes.
///
/// In contrast to a real Mutex implementation, does not protect against concurrent access from
/// other cores to the contained data.
///
/// The lock can only be used as long as it is safe to do so, i.e. as long as the kernel is
/// executing single-threaded, aka only running on a single core with interrupts disabled.
pub struct NullLock<T>
where
T: ?Sized,
{
data: UnsafeCell<T>,
}
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
/// Since we are instantiating this struct as a static variable, which could
/// potentially be shared between different threads, we need to tell the compiler
/// that sharing of this struct is safe by marking it with the Sync trait.
///
/// At this point in time, we can do so without worrying, because the kernel
/// anyways runs on a single core and interrupts are disabled. In short, multiple
/// threads don't exist yet in our code.
///
/// Literature:
/// * <https://doc.rust-lang.org/beta/nomicon/send-and-sync.html>
/// * <https://doc.rust-lang.org/book/ch16-04-extensible-concurrency-sync-and-send.html>
unsafe impl<T> Send for NullLock<T> where T: ?Sized + Send {}
unsafe impl<T> Sync for NullLock<T> where T: ?Sized + Send {}
impl<T> NullLock<T> {
/// Create an instance.
pub const fn new(data: T) -> Self {
Self {
data: UnsafeCell::new(data),
}
}
}
//------------------------------------------------------------------------------
// OS Interface Code
//------------------------------------------------------------------------------
impl<T> interface::Mutex for NullLock<T> {
type Data = T;
fn lock<R>(&self, f: impl FnOnce(&mut Self::Data) -> R) -> R {
// In a real lock, there would be code encapsulating this line that ensures that this
// mutable reference will ever only be given out once at a time.
let data = unsafe { &mut *self.data.get() };
f(data)
}
}

View File

@ -0,0 +1,165 @@
/*
* SPDX-License-Identifier: MIT OR BlueOak-1.0.0
* Copyright (c) 2019 Andre Richter <andre.o.richter@gmail.com>
* Original code distributed under MIT, additional changes are under BlueOak-1.0.0
*/
use core::cell::UnsafeCell;
//--------------------------------------------------------------------------------------------------
// Public Definitions
//--------------------------------------------------------------------------------------------------
/// Synchronization interfaces.
pub mod interface {
/// Any object implementing this trait guarantees exclusive access to the data wrapped within
/// the Mutex for the duration of the provided closure.
pub trait Mutex {
/// The type of the data that is wrapped by this mutex.
type Data;
/// Locks the mutex and grants the closure temporary mutable access to the wrapped data.
fn lock<R>(&self, f: impl FnOnce(&mut Self::Data) -> R) -> R;
}
/// A reader-writer exclusion type.
///
/// The implementing object allows either a number of readers or at most one writer at any point
/// in time.
pub trait ReadWriteEx {
/// The type of encapsulated data.
type Data;
/// Grants temporary mutable access to the encapsulated data.
fn write<R>(&self, f: impl FnOnce(&mut Self::Data) -> R) -> R;
/// Grants temporary immutable access to the encapsulated data.
fn read<R>(&self, f: impl FnOnce(&Self::Data) -> R) -> R;
}
}
/// A pseudo-lock for teaching purposes.
///
/// In contrast to a real Mutex implementation, does not protect against concurrent access from
/// other cores to the contained data. This part is preserved for later lessons.
///
/// The lock will only be used as long as it is safe to do so, i.e. as long as the kernel is
/// executing on a single core.
pub struct IRQSafeNullLock<T>
where
T: ?Sized,
{
data: UnsafeCell<T>,
}
/// A pseudo-lock that is RW during the single-core kernel init phase and RO afterwards.
///
/// Intended to encapsulate data that is populated during kernel init when no concurrency exists.
pub struct InitStateLock<T>
where
T: ?Sized,
{
data: UnsafeCell<T>,
}
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
/// Since we are instantiating this struct as a static variable, which could
/// potentially be shared between different threads, we need to tell the compiler
/// that sharing of this struct is safe by marking it with the Sync trait.
///
/// At this point in time, we can do so without worrying, because the kernel
/// anyways runs on a single core and interrupts are disabled. In short, multiple
/// threads don't exist yet in our code.
///
/// Literature:
/// * <https://doc.rust-lang.org/beta/nomicon/send-and-sync.html>
/// * <https://doc.rust-lang.org/book/ch16-04-extensible-concurrency-sync-and-send.html>
unsafe impl<T> Send for IRQSafeNullLock<T> where T: ?Sized + Send {}
unsafe impl<T> Sync for IRQSafeNullLock<T> where T: ?Sized + Send {}
impl<T> IRQSafeNullLock<T> {
/// Create an instance.
pub const fn new(data: T) -> Self {
Self {
data: UnsafeCell::new(data),
}
}
}
unsafe impl<T> Send for InitStateLock<T> where T: ?Sized + Send {}
unsafe impl<T> Sync for InitStateLock<T> where T: ?Sized + Send {}
impl<T> InitStateLock<T> {
/// Create an instance.
pub const fn new(data: T) -> Self {
Self {
data: UnsafeCell::new(data),
}
}
}
//------------------------------------------------------------------------------
// OS Interface Code
//------------------------------------------------------------------------------
use crate::{exception, state};
impl<T> interface::Mutex for IRQSafeNullLock<T> {
type Data = T;
fn lock<R>(&self, f: impl FnOnce(&mut Self::Data) -> R) -> R {
// In a real lock, there would be code encapsulating this line that ensures that this
// mutable reference will ever only be given out once at a time.
let data = unsafe { &mut *self.data.get() };
// Execute the closure while IRQs are masked.
exception::asynchronous::exec_with_irq_masked(|| f(data))
}
}
impl<T> interface::ReadWriteEx for InitStateLock<T> {
type Data = T;
fn write<R>(&self, f: impl FnOnce(&mut Self::Data) -> R) -> R {
assert!(
state::state_manager().is_init(),
"InitStateLock::write called after kernel init phase"
);
assert!(
!exception::asynchronous::is_local_irq_masked(),
"InitStateLock::write called with IRQs unmasked"
);
let data = unsafe { &mut *self.data.get() };
f(data)
}
fn read<R>(&self, f: impl FnOnce(&Self::Data) -> R) -> R {
let data = unsafe { &*self.data.get() };
f(data)
}
}
//--------------------------------------------------------------------------------------------------
// Testing
//--------------------------------------------------------------------------------------------------
#[cfg(test)]
mod tests {
use super::*; //, test_macros::kernel_test};
/// InitStateLock must be transparent.
#[test_case]
fn init_state_lock_is_transparent() {
use core::mem::size_of;
assert_eq!(size_of::<InitStateLock<u64>>(), size_of::<u64>());
}
}

View File

@ -40,7 +40,7 @@ use {
arch,
console::console,
entry, info, memory,
platform::rpi3::{
platform::raspberrypi::{
display::{Color, DrawError},
mailbox::{channel, Mailbox, MailboxOps},
vc::VC,
@ -51,28 +51,43 @@ use {
entry!(kernel_init);
/// Kernel entry point.
/// Kernel early init code.
/// `arch` crate is responsible for calling it.
///
/// # Safety
///
/// - Only a single core must be active and running this function.
/// - The init calls in this function must appear in the correct order.
/// - The init calls in this function must appear in the correct order:
/// - MMU + Data caching must be activated at the earliest. Without it, any atomic operations,
/// e.g. the yet-to-be-introduced spinlocks in the device drivers (which currently employ
/// IRQSafeNullLocks instead of spinlocks), will fail to work (properly) on the RPi SoCs.
pub unsafe fn kernel_init() -> ! {
#[cfg(feature = "jtag")]
machine::arch::jtag::wait_debugger();
machine::debug::jtag::wait_debugger();
// init_exception_traps(); // @todo
//
// init_mmu(); // @todo
exception::handling_init();
if let Err(string) = memory::mmu::mmu().enable_mmu_and_caching() {
panic!("MMU: {}", string);
}
if let Err(x) = machine::platform::drivers::init() {
panic!("Error initializing platform drivers: {}", x);
}
// Initialize all device drivers.
machine::drivers::driver_manager().init_drivers();
machine::drivers::driver_manager().init_drivers_and_irqs();
init_exception_traps(); // @todo
// Unmask interrupts on the boot CPU core.
exception::asynchronous::local_irq_unmask();
init_mmu(); // @todo
// Announce conclusion of the kernel_init() phase.
state::state_manager().transition_to_single_core_main();
// Transition from unsafe to safe.
kernel_main()
}
@ -82,6 +97,9 @@ pub fn kernel_main() -> ! {
#[cfg(test)]
test_main();
// info!("{}", libkernel::version());
// info!("Booting on: {}", bsp::board_name());
info!(
"{} version {}",
env!("CARGO_PKG_NAME"),
@ -89,6 +107,15 @@ pub fn kernel_main() -> ! {
);
info!("Booting on: {}", machine::platform::BcmHost::board_name());
info!("MMU online. Special regions:");
bsp::memory::mmu::virt_mem_layout().print_layout();
let (_, privilege_level) = exception::current_privilege_level();
info!("Current privilege level: {}", privilege_level);
info!("Exception handling state:");
exception::asynchronous::print_state();
info!(
"Architectural timer resolution: {} ns",
time::time_manager().resolution().as_nanos()
@ -97,6 +124,9 @@ pub fn kernel_main() -> ! {
info!("Drivers loaded:");
machine::drivers::driver_manager().enumerate();
info!("Registered IRQ handlers:");
exception::asynchronous::irq_manager().print_handler();
// Test a failing timer case.
time::time_manager().spin_for(Duration::from_nanos(1));
@ -131,18 +161,6 @@ fn init_mmu() {
print_mmu_state_and_features();
}
fn init_exception_traps() {
extern "Rust" {
static __EXCEPTION_VECTORS_START: UnsafeCell<()>;
}
unsafe {
arch::traps::set_vbar_el1_checked(__EXCEPTION_VECTORS_START.get() as u64)
.expect("Vector table properly aligned!");
}
info!("[!] Exception traps set up");
}
//------------------------------------------------------------
// Start a command prompt
//------------------------------------------------------------
@ -198,7 +216,7 @@ fn reboot() -> ! {
info!("Bye, shutting down QEMU");
machine::qemu::semihosting::exit_success()
} else {
use machine::platform::rpi3::power::Power;
use machine::platform::raspberrypi::power::Power;
info!("Bye, going to reset now");
Power::default().reset()