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:
parent
577b0b74ee
commit
0f30bf00aa
|
@ -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},
|
||||
};
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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,
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
pub fn core_id() {}
|
|
@ -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");
|
||||
}
|
|
@ -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());
|
|
@ -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,
|
||||
|
|
|
@ -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};
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -6,5 +6,3 @@
|
|||
#[cfg(target_arch = "aarch64")]
|
||||
#[macro_use]
|
||||
pub mod aarch64;
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
pub use self::aarch64::*;
|
||||
|
|
|
@ -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.
|
|
@ -1,4 +1,4 @@
|
|||
use crate::{console::interface, devices::SerialOps};
|
||||
use crate::{console::interface, devices::serial::SerialOps};
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
// Public Definitions
|
||||
|
|
|
@ -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;
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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;
|
|
@ -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) }
|
|
@ -0,0 +1,2 @@
|
|||
#[cfg(feature = "jtag")]
|
||||
pub mod jtag;
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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();
|
|
@ -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
|
||||
|
|
|
@ -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 _) }
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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::*;
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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 = ®s.ISENABLER[enable_reg_index_shared];
|
||||
enable_reg.set(enable_reg.get() | enable_bit);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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::*;
|
|
@ -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
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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 {
|
||||
®s.ENABLE_1
|
||||
} else {
|
||||
®s.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());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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::*};
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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::*;
|
|
@ -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(())
|
|
@ -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);
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
pub mod asynchronous;
|
|
@ -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},
|
|
@ -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> {
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
|
@ -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()
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
};
|
|
@ -1,5 +0,0 @@
|
|||
pub mod gpio;
|
||||
pub mod mini_uart;
|
||||
pub mod pl011_uart;
|
||||
|
||||
pub use {gpio::*, mini_uart::*, pl011_uart::*};
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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>());
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
|
|
Loading…
Reference in New Issue