refactor: 📦 Use better code structure

As inspired by andre-richter's tutorials.
This commit is contained in:
Berkus Decker 2023-07-12 03:03:08 +03:00 committed by Berkus Decker
parent 46d0c4cffc
commit 0e1c6669ac
22 changed files with 1128 additions and 612 deletions

View File

@ -183,6 +183,7 @@ script = [
'''
writefile ${GDB_CONNECT_FILE} "target extended-remote :5555\n"
appendfile ${GDB_CONNECT_FILE} "break *0x80000\n"
appendfile ${GDB_CONNECT_FILE} "break kernel_init\n"
appendfile ${GDB_CONNECT_FILE} "break kernel_main\n"
echo 🖌️ Generated GDB config file
'''

View File

@ -29,7 +29,7 @@ macro_rules! entry {
#[inline(always)]
pub unsafe fn __main() -> ! {
// type check the given path
let f: fn() -> ! = $path;
let f: unsafe fn() -> ! = $path;
f()
}

68
machine/src/console.rs Normal file
View File

@ -0,0 +1,68 @@
/*
* SPDX-License-Identifier: BlueOak-1.0.0
*/
#![allow(dead_code)]
use crate::sync::NullLock;
pub mod null_console;
//--------------------------------------------------------------------------------------------------
// Public Definitions
//--------------------------------------------------------------------------------------------------
/// Console interfaces.
pub mod interface {
use {crate::devices::SerialOps, core::fmt};
/// Console write functions.
pub trait Write {
/// Write a Rust format string.
fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result;
}
/// A trait that must be implemented by devices that are candidates for the
/// global console.
#[allow(unused_variables)]
pub trait ConsoleOps: SerialOps {
/// Send a character
fn write_char(&self, c: char);
/// Display a string
fn write_string(&self, string: &str);
/// Receive a character
fn read_char(&self) -> char;
}
pub trait ConsoleTools {
fn command_prompt<'a>(&self, buf: &'a mut [u8]) -> &'a [u8];
}
/// Trait alias for a full-fledged console.
pub trait All: Write + ConsoleOps + ConsoleTools {}
}
//--------------------------------------------------------------------------------------------------
// Global instances
//--------------------------------------------------------------------------------------------------
static CONSOLE: NullLock<&'static (dyn interface::All + Sync)> =
NullLock::new(&null_console::NULL_CONSOLE);
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
use crate::sync::interface::Mutex;
/// Register a new console.
pub fn register_console(new_console: &'static (dyn interface::All + Sync)) {
CONSOLE.lock(|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)
}

View File

@ -0,0 +1,54 @@
use crate::{console::interface, devices::SerialOps};
//--------------------------------------------------------------------------------------------------
// Public Definitions
//--------------------------------------------------------------------------------------------------
/// A dummy console that just ignores all I/O.
pub struct NullConsole;
//--------------------------------------------------------------------------------------------------
// Global instances
//--------------------------------------------------------------------------------------------------
pub static NULL_CONSOLE: NullConsole = NullConsole {};
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
impl interface::Write for NullConsole {
fn write_fmt(&self, args: core::fmt::Arguments) -> core::fmt::Result {
Ok(())
}
}
impl interface::ConsoleOps for NullConsole {
fn write_char(&self, _c: char) {}
fn write_string(&self, _string: &str) {}
fn read_char(&self) -> char {
' '
}
}
impl SerialOps for NullConsole {
fn read_byte(&self) -> u8 {
0
}
fn write_byte(&self, _byte: u8) {}
fn flush(&self) {}
fn clear_rx(&self) {}
}
impl interface::ConsoleTools for NullConsole {
fn command_prompt<'a>(&self, buf: &'a mut [u8]) -> &'a [u8] {
buf
}
}
impl interface::All for NullConsole {}

View File

@ -1,110 +1,112 @@
/*
* SPDX-License-Identifier: BlueOak-1.0.0
*/
#![allow(dead_code)]
use {
crate::{devices::SerialOps, platform},
crate::{
console::interface,
devices::{null_console::NullConsole, SerialOps},
platform::rpi3::{mini_uart::PreparedMiniUart, pl011_uart::PreparedPL011Uart},
sync::NullLock,
},
core::fmt,
};
/// A trait that must be implemented by devices that are candidates for the
/// global console.
#[allow(unused_variables)]
pub trait ConsoleOps: SerialOps {
/// Send a character
fn write_char(&self, c: char);
/// Display a string
fn write_string(&self, string: &str);
/// Receive a character
fn read_char(&self) -> char;
}
//--------------------------------------------------------------------------------------------------
// Private Definitions
//--------------------------------------------------------------------------------------------------
/// A dummy console that just ignores its inputs.
pub struct NullConsole;
impl Drop for NullConsole {
fn drop(&mut self) {}
}
impl ConsoleOps for NullConsole {
fn write_char(&self, _c: char) {}
fn write_string(&self, _string: &str) {}
fn read_char(&self) -> char {
' '
}
}
impl SerialOps for NullConsole {
fn read_byte(&self) -> u8 {
0
}
fn write_byte(&self, _byte: u8) {}
fn flush(&self) {}
fn clear_rx(&self) {}
}
/// Possible outputs which the console can store.
pub enum Output {
None(NullConsole),
MiniUart(platform::rpi3::mini_uart::PreparedMiniUart),
Uart(platform::rpi3::pl011_uart::PreparedPL011Uart),
}
/// Generate boilerplate for converting into one of Output enum values
macro output_from($name:ty, $optname:ident) {
impl From<$name> for Output {
fn from(instance: $name) -> Self {
Output::$optname(instance)
}
}
}
output_from!(NullConsole, None);
output_from!(platform::rpi3::mini_uart::PreparedMiniUart, MiniUart);
output_from!(platform::rpi3::pl011_uart::PreparedPL011Uart, Uart);
pub struct Console {
/// The mutex protected part.
struct ConsoleInner {
output: Output,
}
impl Default for Console {
fn default() -> Self {
Self::new()
}
//--------------------------------------------------------------------------------------------------
// Public Definitions
//--------------------------------------------------------------------------------------------------
/// The main struct.
pub struct Console {
inner: NullLock<ConsoleInner>,
}
impl Console {
//--------------------------------------------------------------------------------------------------
// Global instances
//--------------------------------------------------------------------------------------------------
static CONSOLE: Console = Console::new();
//--------------------------------------------------------------------------------------------------
// Private Code
//--------------------------------------------------------------------------------------------------
impl ConsoleInner {
pub const fn new() -> Self {
Console {
Self {
output: Output::None(NullConsole {}),
}
}
fn current_ptr(&self) -> &dyn ConsoleOps {
fn current_ptr(&self) -> &dyn interface::ConsoleOps {
match &self.output {
Output::None(i) => i,
Output::MiniUart(i) => i,
Output::Uart(i) => i,
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();
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));
}
}
impl interface::ConsoleTools for Console {
/// A command prompt.
pub fn command_prompt<'a>(&self, buf: &'a mut [u8]) -> &'a [u8] {
fn command_prompt<'a>(&self, buf: &'a mut [u8]) -> &'a [u8] {
use interface::ConsoleOps;
self.write_string("\n$> ");
let mut i = 0;
@ -129,47 +131,89 @@ impl Console {
}
}
impl Drop for Console {
fn drop(&mut self) {}
/// 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 ConsoleOps for Console {
impl interface::ConsoleOps for Console {
// @todo implement utf8 serialization here!
fn write_char(&self, c: char) {
self.current_ptr().write_char(c);
self.inner.lock(|con| con.current_ptr().write_char(c));
}
fn write_string(&self, string: &str) {
self.current_ptr().write_string(string);
self.inner
.lock(|con| con.current_ptr().write_string(string));
}
// @todo implement utf8 deserialization here!
fn read_char(&self) -> char {
self.current_ptr().read_char()
self.inner.lock(|con| con.current_ptr().read_char())
}
}
impl SerialOps for Console {
fn read_byte(&self) -> u8 {
self.current_ptr().read_byte()
self.inner.lock(|con| con.current_ptr().read_byte())
}
fn write_byte(&self, byte: u8) {
self.current_ptr().write_byte(byte)
self.inner.lock(|con| con.current_ptr().write_byte(byte))
}
fn flush(&self) {
self.current_ptr().flush()
self.inner.lock(|con| con.current_ptr().flush())
}
fn clear_rx(&self) {
self.current_ptr().clear_rx()
self.inner.lock(|con| con.current_ptr().clear_rx())
}
}
/// Implementing this trait enables usage of the format_args! macros, which in
/// turn are used to implement the kernel's print! and println! macros.
///
/// See src/macros.rs.
impl fmt::Write for Console {
fn write_str(&mut self, s: &str) -> fmt::Result {
self.current_ptr().write_string(s);
Ok(())
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);

View File

@ -1,10 +1,8 @@
/*
* SPDX-License-Identifier: BlueOak-1.0.0
*/
pub mod console;
// pub mod console;
// pub mod null_console;
pub mod serial;
pub use {
console::{Console, ConsoleOps},
serial::SerialOps,
};
pub use serial::SerialOps;

158
machine/src/drivers.rs Normal file
View File

@ -0,0 +1,158 @@
use crate::{
println,
sync::{interface::Mutex, NullLock},
};
//--------------------------------------------------------------------------------------------------
// Private Definitions
//--------------------------------------------------------------------------------------------------
const NUM_DRIVERS: usize = 5;
struct DriverManagerInner {
next_index: usize,
descriptors: [Option<DeviceDriverDescriptor>; NUM_DRIVERS],
}
//--------------------------------------------------------------------------------------------------
// Public Definitions
//--------------------------------------------------------------------------------------------------
pub mod interface {
pub trait DeviceDriver {
/// Return a compatibility string for identifying the driver.
fn compatible(&self) -> &'static str;
/// Called by the kernel to bring up the device.
/// The default implementation does nothing.
///
/// # Safety
///
/// - During init, drivers might do things with system-wide impact.
unsafe fn init(&self) -> Result<(), &'static str> {
Ok(())
}
}
}
/// Type to be used as an optional callback after a driver's init() has run.
pub type DeviceDriverPostInitCallback = unsafe fn() -> Result<(), &'static str>;
/// A descriptor for device drivers.
#[derive(Copy, Clone)]
pub struct DeviceDriverDescriptor {
device_driver: &'static (dyn interface::DeviceDriver + Sync),
post_init_callback: Option<DeviceDriverPostInitCallback>,
}
/// Provides device driver management functions.
pub struct DriverManager {
inner: NullLock<DriverManagerInner>,
}
//--------------------------------------------------------------------------------------------------
// Global instances
//--------------------------------------------------------------------------------------------------
static DRIVER_MANAGER: DriverManager = DriverManager::new();
//--------------------------------------------------------------------------------------------------
// Private Code
//--------------------------------------------------------------------------------------------------
impl DriverManagerInner {
pub const fn new() -> Self {
Self {
next_index: 0,
descriptors: [None; NUM_DRIVERS],
}
}
}
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
/// Return a reference to the global DriverManager.
pub fn driver_manager() -> &'static DriverManager {
&DRIVER_MANAGER
}
impl DeviceDriverDescriptor {
pub fn new(
device_driver: &'static (dyn interface::DeviceDriver + Sync),
post_init_callback: Option<DeviceDriverPostInitCallback>,
) -> Self {
Self {
device_driver,
post_init_callback,
}
}
}
impl DriverManager {
pub const fn new() -> Self {
Self {
inner: NullLock::new(DriverManagerInner::new()),
}
}
/// Register a device driver with the kernel.
pub fn register_driver(&self, descriptor: DeviceDriverDescriptor) {
self.inner.lock(|inner| {
assert!(inner.next_index < NUM_DRIVERS);
inner.descriptors[inner.next_index] = Some(descriptor);
inner.next_index += 1;
})
}
/// Helper for iterating over registered drivers.
fn for_each_descriptor(&self, f: impl FnMut(&DeviceDriverDescriptor)) {
self.inner.lock(|inner| {
inner
.descriptors
.iter()
.filter_map(|x| x.as_ref())
.for_each(f)
})
}
/// Fully initialize all drivers.
///
/// # Safety
///
/// - During init, drivers might do things with system-wide impact.
pub unsafe fn init_drivers(&self) {
self.for_each_descriptor(|descriptor| {
// 1. Initialize driver.
if let Err(x) = descriptor.device_driver.init() {
panic!(
"Error initializing driver: {}: {}",
descriptor.device_driver.compatible(),
x
);
}
// 2. Call corresponding post init callback.
if let Some(callback) = &descriptor.post_init_callback {
if let Err(x) = callback() {
panic!(
"Error during driver post-init callback: {}: {}",
descriptor.device_driver.compatible(),
x
);
}
}
});
}
/// Enumerate all registered device drivers.
pub fn enumerate(&self) {
let mut i: usize = 1;
self.for_each_descriptor(|descriptor| {
println!(" {}. {}", i, descriptor.device_driver.compatible());
i += 1;
});
}
}

View File

@ -16,26 +16,24 @@
#![allow(clippy::nonstandard_macro_braces)] // https://github.com/shepmaster/snafu/issues/296
#![allow(missing_docs)] // Temp: switch to deny
#![deny(warnings)]
#![allow(unused)]
#[cfg(not(target_arch = "aarch64"))]
use architecture_not_supported_sorry;
use {
buddy_alloc::{BuddyAlloc, BuddyAllocParam},
once_cell::unsync::Lazy,
platform::memory::map::virt::{DMA_HEAP_END, DMA_HEAP_START},
};
/// Architecture-specific code.
#[macro_use]
pub mod arch;
pub use arch::*;
pub mod console;
pub mod devices;
pub mod drivers;
pub mod macros;
pub mod memory;
mod mm;
pub mod mmio_deref_wrapper;
pub mod panic;
pub mod platform;
pub mod qemu;
@ -43,21 +41,18 @@ mod sync;
pub mod tests;
pub mod write_to;
/// The global console. Output of the kernel print! and println! macros goes here.
pub static CONSOLE: sync::NullLock<devices::Console> = sync::NullLock::new(devices::Console::new());
/// The global allocator for DMA-able memory. That is, memory which is tagged
/// non-cacheable in the page tables.
#[allow(dead_code)]
static DMA_ALLOCATOR: sync::NullLock<Lazy<BuddyAlloc>> =
sync::NullLock::new(Lazy::new(|| unsafe {
BuddyAlloc::new(BuddyAllocParam::new(
// @todo Init this after we loaded boot memory map
DMA_HEAP_START as *const u8,
DMA_HEAP_END - DMA_HEAP_START,
64,
))
}));
// The global allocator for DMA-able memory. That is, memory which is tagged
// non-cacheable in the page tables.
// #[allow(dead_code)]
// static DMA_ALLOCATOR: sync::NullLock<Lazy<BuddyAlloc>> =
// sync::NullLock::new(Lazy::new(|| unsafe {
// BuddyAlloc::new(BuddyAllocParam::new(
// // @todo Init this after we loaded boot memory map
// DMA_HEAP_START as *const u8,
// DMA_HEAP_END - DMA_HEAP_START,
// 64,
// ))
// }));
// Try the following arguments instead to see all mailbox operations
// fail. It will cause the allocator to use memory that is marked
// cacheable and therefore not DMA-safe. The answer from the VideoCore

View File

@ -23,11 +23,8 @@ macro_rules! println {
#[doc(hidden)]
#[cfg(not(any(test, qemu)))]
pub fn _print(args: core::fmt::Arguments) {
use core::fmt::Write;
crate::CONSOLE.lock(|c| {
c.write_fmt(args).unwrap();
})
use {crate::console::console, core::fmt::Write};
console().write_fmt(args).unwrap();
}
/// qemu-based tests use semihosting write0 syscall.

View File

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

View File

@ -2,54 +2,8 @@
* SPDX-License-Identifier: BlueOak-1.0.0
* Copyright (c) Berkus Decker <berkus+vesper@metta.systems>
*/
use core::{marker::PhantomData, ops};
//--------------------------------------------------------------------------------------------------
// Public Definitions
//--------------------------------------------------------------------------------------------------
pub mod rpi3;
#[cfg(any(feature = "rpi3", feature = "rpi4"))]
pub use rpi3::*;
pub struct MMIODerefWrapper<T> {
base_addr: usize,
phantom: PhantomData<fn() -> T>,
}
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
impl<T> MMIODerefWrapper<T> {
/// Create an instance.
///
/// # Safety
///
/// Unsafe, duh!
pub const unsafe fn new(start_addr: usize) -> Self {
Self {
base_addr: start_addr,
phantom: PhantomData,
}
}
}
/// Deref to RegisterBlock
///
/// Allows writing
/// ```
/// self.GPPUD.read()
/// ```
/// instead of something along the lines of
/// ```
/// unsafe { (*GPIO::ptr()).GPPUD.read() }
/// ```
impl<T> ops::Deref for MMIODerefWrapper<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
unsafe { &*(self.base_addr as *const _) }
}
}

View File

@ -6,8 +6,7 @@
*/
use {
super::BcmHost,
crate::platform::MMIODerefWrapper,
crate::{mmio_deref_wrapper::MMIODerefWrapper, platform::BcmHost},
core::marker::PhantomData,
tock_registers::{
fields::FieldValue,
@ -94,17 +93,17 @@ pub struct GPIO {
registers: Registers,
}
pub const GPIO_START: usize = 0x20_0000;
pub const GPIO_BASE: usize = BcmHost::get_peripheral_address() + 0x20_0000;
impl Default for GPIO {
fn default() -> GPIO {
// Default RPi3 GPIO base address
const GPIO_BASE: usize = BcmHost::get_peripheral_address() + GPIO_START;
unsafe { GPIO::new(GPIO_BASE) }
impl crate::drivers::interface::DeviceDriver for GPIO {
fn compatible(&self) -> &'static str {
Self::COMPATIBLE
}
}
impl GPIO {
pub const COMPATIBLE: &'static str = "BCM GPIO";
/// # Safety
///
/// Unsafe, duh!

View File

@ -0,0 +1,405 @@
/*
* SPDX-License-Identifier: MIT OR BlueOak-1.0.0
* Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com>
* Copyright (c) Berkus Decker <berkus+vesper@metta.systems>
* Original code distributed under MIT, additional changes are under BlueOak-1.0.0
*/
#[cfg(not(feature = "noserial"))]
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},
},
cfg_if::cfg_if,
core::{
convert::From,
fmt::{self, Arguments},
},
tock_registers::{
interfaces::ReadWriteable,
register_bitfields, register_structs,
registers::{ReadOnly, ReadWrite, WriteOnly},
},
};
// Auxiliary mini UART registers
//
// Descriptions taken from
// https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf
register_bitfields! {
u32,
/// Auxiliary enables
AUX_ENABLES [
/// If set the mini UART is enabled. The UART will immediately
/// start receiving data, especially if the UART1_RX line is
/// low.
/// If clear the mini UART is disabled. That also disables any
/// mini UART register access
MINI_UART_ENABLE OFFSET(0) NUMBITS(1) []
],
/// Mini Uart Interrupt Identify
AUX_MU_IIR [
/// Writing with bit 1 set will clear the receive FIFO
/// Writing with bit 2 set will clear the transmit FIFO
FIFO_CLEAR OFFSET(1) NUMBITS(2) [
Rx = 0b01,
Tx = 0b10,
All = 0b11
]
],
/// Mini Uart Line Control
AUX_MU_LCR [
/// Mode the UART works in
DATA_SIZE OFFSET(0) NUMBITS(2) [
SevenBit = 0b00,
EightBit = 0b11
]
],
/// Mini Uart Line Status
AUX_MU_LSR [
/// This bit is set if the transmit FIFO is empty and the transmitter is
/// idle. (Finished shifting out the last bit).
TX_IDLE OFFSET(6) NUMBITS(1) [],
/// This bit is set if the transmit FIFO can accept at least
/// one byte.
TX_EMPTY OFFSET(5) NUMBITS(1) [],
/// This bit is set if the receive FIFO holds at least 1
/// symbol.
DATA_READY OFFSET(0) NUMBITS(1) []
],
/// Mini Uart Extra Control
AUX_MU_CNTL [
/// If this bit is set the mini UART transmitter is enabled.
/// If this bit is clear the mini UART transmitter is disabled.
TX_EN OFFSET(1) NUMBITS(1) [
Disabled = 0,
Enabled = 1
],
/// If this bit is set the mini UART receiver is enabled.
/// If this bit is clear the mini UART receiver is disabled.
RX_EN OFFSET(0) NUMBITS(1) [
Disabled = 0,
Enabled = 1
]
],
/// Mini Uart Status
AUX_MU_STAT [
TX_DONE OFFSET(9) NUMBITS(1) [
No = 0,
Yes = 1
],
/// This bit is set if the transmit FIFO can accept at least
/// one byte.
SPACE_AVAILABLE OFFSET(1) NUMBITS(1) [
No = 0,
Yes = 1
],
/// This bit is set if the receive FIFO holds at least 1
/// symbol.
SYMBOL_AVAILABLE OFFSET(0) NUMBITS(1) [
No = 0,
Yes = 1
]
],
/// Mini Uart Baud rate
AUX_MU_BAUD [
/// Mini UART baud rate counter
RATE OFFSET(0) NUMBITS(16) []
]
}
register_structs! {
#[allow(non_snake_case)]
RegisterBlock {
// 0x00 - AUX_IRQ?
(0x00 => __reserved_1),
(0x04 => AUX_ENABLES: ReadWrite<u32, AUX_ENABLES::Register>),
(0x08 => __reserved_2),
(0x40 => AUX_MU_IO: ReadWrite<u32>),//Mini Uart I/O Data
(0x44 => AUX_MU_IER: WriteOnly<u32>),//Mini Uart Interrupt Enable
(0x48 => AUX_MU_IIR: WriteOnly<u32, AUX_MU_IIR::Register>),
(0x4c => AUX_MU_LCR: WriteOnly<u32, AUX_MU_LCR::Register>),
(0x50 => AUX_MU_MCR: WriteOnly<u32>),
(0x54 => AUX_MU_LSR: ReadOnly<u32, AUX_MU_LSR::Register>),
// 0x58 - AUX_MU_MSR
// 0x5c - AUX_MU_SCRATCH
(0x58 => __reserved_3),
(0x60 => AUX_MU_CNTL: WriteOnly<u32, AUX_MU_CNTL::Register>),
(0x64 => AUX_MU_STAT: ReadOnly<u32, AUX_MU_STAT::Register>),
(0x68 => AUX_MU_BAUD: WriteOnly<u32, AUX_MU_BAUD::Register>),
(0x6c => @END),
}
}
type Registers = MMIODerefWrapper<RegisterBlock>;
struct MiniUartInner {
registers: Registers,
}
pub struct MiniUart {
inner: NullLock<MiniUartInner>,
}
/// Divisor values for common baud rates
pub enum Rate {
Baud115200 = 270,
}
impl From<Rate> for u32 {
fn from(r: Rate) -> Self {
r as u32
}
}
// [temporary] Used in mmu.rs to set up local paging
pub const UART1_BASE: usize = BcmHost::get_peripheral_address() + 0x21_5000;
impl crate::drivers::interface::DeviceDriver for MiniUart {
fn compatible(&self) -> &'static str {
Self::COMPATIBLE
}
unsafe fn init(&self) -> Result<(), &'static str> {
self.inner.lock(|inner| inner.prepare())
}
}
impl MiniUart {
pub const COMPATIBLE: &'static str = "BCM MINI UART";
}
impl MiniUart {
/// Create an instance.
///
/// # Safety
///
/// - 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)),
}
}
/// GPIO pins should be set up first before enabling the UART
pub fn prepare_gpio(gpio: &gpio::GPIO) {
// Pin 14
const MINI_UART_TXD: gpio::Function = gpio::Function::Alt5;
// Pin 15
const MINI_UART_RXD: gpio::Function = gpio::Function::Alt5;
// map UART1 to GPIO pins
gpio.get_pin(14)
.into_alt(MINI_UART_TXD)
.set_pull_up_down(gpio::PullUpDown::Up);
gpio.get_pin(15)
.into_alt(MINI_UART_RXD)
.set_pull_up_down(gpio::PullUpDown::Up);
}
}
impl MiniUartInner {
/// Create an instance.
///
/// # Safety
///
/// - The user must ensure to provide a correct MMIO start address.
pub const unsafe fn new(base_addr: usize) -> Self {
Self {
registers: Registers::new(base_addr),
}
}
}
impl MiniUartInner {
/// Set baud rate and characteristics (115200 8N1) and map to GPIO
pub fn prepare(&self) -> Result<(), &'static str> {
// initialize UART
self.registers
.AUX_ENABLES
.modify(AUX_ENABLES::MINI_UART_ENABLE::SET);
self.registers.AUX_MU_IER.set(0);
self.registers.AUX_MU_CNTL.set(0);
self.registers
.AUX_MU_LCR
.write(AUX_MU_LCR::DATA_SIZE::EightBit);
self.registers.AUX_MU_MCR.set(0);
self.registers.AUX_MU_IER.set(0);
self.registers
.AUX_MU_BAUD
.write(AUX_MU_BAUD::RATE.val(Rate::Baud115200.into()));
// Clear FIFOs before using the device
self.registers.AUX_MU_IIR.write(AUX_MU_IIR::FIFO_CLEAR::All);
self.registers
.AUX_MU_CNTL
.write(AUX_MU_CNTL::RX_EN::Enabled + AUX_MU_CNTL::TX_EN::Enabled);
Ok(())
}
}
impl Drop for MiniUartInner {
fn drop(&mut self) {
self.registers
.AUX_ENABLES
.modify(AUX_ENABLES::MINI_UART_ENABLE::CLEAR);
// @todo disable gpio.PUD ?
}
}
impl SerialOps for MiniUartInner {
/// Receive a byte without console translation
fn read_byte(&self) -> u8 {
// wait until something is in the buffer
crate::arch::loop_until(|| {
self.registers
.AUX_MU_STAT
.is_set(AUX_MU_STAT::SYMBOL_AVAILABLE)
});
// read it and return
self.registers.AUX_MU_IO.get() as u8
}
fn write_byte(&self, b: u8) {
// wait until we can send
crate::arch::loop_until(|| {
self.registers
.AUX_MU_STAT
.is_set(AUX_MU_STAT::SPACE_AVAILABLE)
});
// write the character to the buffer
self.registers.AUX_MU_IO.set(b as u32);
}
/// Wait until the TX FIFO is empty, aka all characters have been put on the
/// line.
fn flush(&self) {
crate::arch::loop_until(|| self.registers.AUX_MU_STAT.is_set(AUX_MU_STAT::TX_DONE));
}
/// Consume input until RX FIFO is empty, aka all pending characters have been
/// consumed.
fn clear_rx(&self) {
crate::arch::loop_while(|| {
let pending = self
.registers
.AUX_MU_STAT
.is_set(AUX_MU_STAT::SYMBOL_AVAILABLE);
if pending {
self.read_byte();
}
pending
});
}
}
impl interface::ConsoleOps for MiniUartInner {
/// Send a character
fn write_char(&self, c: char) {
let mut b = [0u8; 4];
let _ = c.encode_utf8(&mut b);
for x in 0..c.len_utf8() {
self.write_byte(b[x]);
}
}
/// Display a string
fn write_string(&self, string: &str) {
for c in string.chars() {
// convert newline to carriage return + newline
if c == '\n' {
self.write_char('\r')
}
self.write_char(c);
}
}
/// Receive a character -- FIXME: needs a state machine to read UTF-8 chars!
fn read_char(&self) -> char {
let mut ret = self.read_byte() as char;
// convert carriage return to newline -- this doesn't work well for reading binaries...
if ret == '\r' {
ret = '\n'
}
ret
}
}
impl fmt::Write for MiniUartInner {
fn write_str(&mut self, s: &str) -> fmt::Result {
use interface::ConsoleOps;
self.write_string(s);
Ok(())
}
}
impl interface::Write for MiniUart {
fn write_fmt(&self, args: Arguments) -> fmt::Result {
self.inner.lock(|inner| fmt::Write::write_fmt(inner, args))
}
}
impl SerialOps for MiniUart {
fn read_byte(&self) -> u8 {
self.inner.lock(|inner| inner.read_byte())
}
fn write_byte(&self, byte: u8) {
self.inner.lock(|inner| inner.write_byte(byte))
}
fn flush(&self) {
self.inner.lock(|inner| inner.flush())
}
fn clear_rx(&self) {
self.inner.lock(|inner| inner.clear_rx())
}
}
// ??
impl interface::ConsoleOps for MiniUart {
fn write_char(&self, c: char) {
self.inner.lock(|inner| inner.write_char(c))
}
fn write_string(&self, string: &str) {
self.inner.lock(|inner| inner.write_string(string))
}
fn read_char(&self) -> char {
self.inner.lock(|inner| inner.read_char())
}
}
impl interface::ConsoleTools for MiniUart {
fn command_prompt<'a>(&self, buf: &'a mut [u8]) -> &'a [u8] {
todo!()
}
}
impl interface::All for MiniUart {}

View File

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

View File

@ -9,15 +9,16 @@
*/
use {
super::{
gpio,
mailbox::{self, Mailbox, MailboxOps},
BcmHost,
},
crate::{
arch::loop_while,
devices::{ConsoleOps, SerialOps},
platform::MMIODerefWrapper,
console::interface::ConsoleOps,
devices::SerialOps,
mmio_deref_wrapper::MMIODerefWrapper,
platform::{
device_driver::gpio,
mailbox::{self, Mailbox, MailboxOps},
BcmHost,
},
},
snafu::Snafu,
tock_registers::{

View File

@ -0,0 +1,77 @@
use {
crate::{console, drivers, platform::device_driver},
core::sync::atomic::{AtomicBool, Ordering},
};
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
/// Initialize the driver subsystem.
///
/// # Safety
///
/// See child function calls.
///
/// # Note
///
/// Using atomics here relieves us from needing to use `unsafe` for the static variable.
///
/// On `AArch64`, which is the only implemented architecture at the time of writing this,
/// [`AtomicBool::load`] and [`AtomicBool::store`] are lowered to ordinary load and store
/// instructions. They are therefore safe to use even with MMU + caching deactivated.
///
/// [`AtomicBool::load`]: core::sync::atomic::AtomicBool::load
/// [`AtomicBool::store`]: core::sync::atomic::AtomicBool::store
pub unsafe fn init() -> Result<(), &'static str> {
static INIT_DONE: AtomicBool = AtomicBool::new(false);
if INIT_DONE.load(Ordering::Relaxed) {
return Err("Init already done");
}
driver_gpio()?;
#[cfg(not(feature = "noserial"))]
driver_uart()?;
INIT_DONE.store(true, Ordering::Relaxed);
Ok(())
}
//--------------------------------------------------------------------------------------------------
// Global instances
//--------------------------------------------------------------------------------------------------
static MINI_UART: device_driver::MiniUart =
unsafe { device_driver::MiniUart::new(device_driver::UART1_BASE) };
// static PL011_UART: device_driver::PL011Uart = unsafe { device_driver::PL011Uart::default() };
static GPIO: device_driver::GPIO = unsafe { device_driver::GPIO::new(device_driver::GPIO_BASE) };
//--------------------------------------------------------------------------------------------------
// Private Code
//--------------------------------------------------------------------------------------------------
/// This must be called only after successful init of the UART driver.
fn post_init_uart() -> Result<(), &'static str> {
console::register_console(&MINI_UART);
Ok(())
}
// This must be called only after successful init of the GPIO driver.
fn post_init_gpio() -> Result<(), &'static str> {
device_driver::MiniUart::prepare_gpio(&GPIO);
Ok(())
}
fn driver_uart() -> Result<(), &'static str> {
let uart_descriptor = drivers::DeviceDriverDescriptor::new(&MINI_UART, Some(post_init_uart));
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));
drivers::driver_manager().register_driver(gpio_descriptor);
Ok(())
}

View File

@ -13,7 +13,7 @@
use {
super::BcmHost,
crate::{platform::MMIODerefWrapper, println, DMA_ALLOCATOR},
crate::{mmio_deref_wrapper::MMIODerefWrapper, println}, //DMA_ALLOCATOR
aarch64_cpu::asm::barrier,
core::{
alloc::{AllocError, Allocator, Layout},
@ -148,32 +148,36 @@ impl<const N_SLOTS: usize> MailboxStorage for LocalMailboxStorage<N_SLOTS> {
impl<const N_SLOTS: usize> MailboxStorage for DmaBackedMailboxStorage<N_SLOTS> {
fn new() -> Result<Self> {
use crate::platform::memory::map::virt::DMA_HEAP_START;
Ok(Self {
storage: DMA_ALLOCATOR
.lock(|a| {
a.allocate(
Layout::from_size_align(N_SLOTS * mem::size_of::<u32>(), 16)
.map_err(|_| AllocError)?,
)
})
.map_err(|_| MailboxError::Alloc)?
.as_mut_ptr() as *mut u32,
storage: DMA_HEAP_START
// storage: DMA_ALLOCATOR
// .lock(|a| {
// a.allocate(
// Layout::from_size_align(N_SLOTS * mem::size_of::<u32>(), 16)
// .map_err(|_| AllocError)?,
// )
// })
// .map_err(|_| MailboxError::Alloc)?
// .as_mut_ptr()
as *mut u32,
})
}
}
impl<const N_SLOTS: usize> Drop for DmaBackedMailboxStorage<N_SLOTS> {
fn drop(&mut self) {
DMA_ALLOCATOR
.lock::<_, Result<()>>(|a| unsafe {
#[allow(clippy::unit_arg)]
Ok(a.deallocate(
NonNull::new_unchecked(self.storage as *mut u8),
Layout::from_size_align(N_SLOTS * mem::size_of::<u32>(), 16)
.map_err(|_| MailboxError::Alloc)?,
))
})
.unwrap_or(())
// DMA_ALLOCATOR
// .lock::<_, Result<()>>(|a| unsafe {
// #[allow(clippy::unit_arg)]
// Ok(a.deallocate(
// NonNull::new_unchecked(self.storage as *mut u8),
// Layout::from_size_align(N_SLOTS * mem::size_of::<u32>(), 16)
// .map_err(|_| MailboxError::Alloc)?,
// ))
// })
// .unwrap_or(())
}
}

View File

@ -1,332 +0,0 @@
/*
* SPDX-License-Identifier: MIT OR BlueOak-1.0.0
* Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com>
* Copyright (c) Berkus Decker <berkus+vesper@metta.systems>
* Original code distributed under MIT, additional changes are under BlueOak-1.0.0
*/
#[cfg(not(feature = "noserial"))]
use tock_registers::interfaces::{Readable, Writeable};
use {
super::{gpio, BcmHost},
crate::{
devices::{ConsoleOps, SerialOps},
platform::MMIODerefWrapper,
},
cfg_if::cfg_if,
core::{convert::From, fmt},
tock_registers::{
interfaces::ReadWriteable,
register_bitfields, register_structs,
registers::{ReadOnly, ReadWrite, WriteOnly},
},
};
// Auxiliary mini UART registers
//
// Descriptions taken from
// https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf
register_bitfields! {
u32,
/// Auxiliary enables
AUX_ENABLES [
/// If set the mini UART is enabled. The UART will immediately
/// start receiving data, especially if the UART1_RX line is
/// low.
/// If clear the mini UART is disabled. That also disables any
/// mini UART register access
MINI_UART_ENABLE OFFSET(0) NUMBITS(1) []
],
/// Mini Uart Interrupt Identify
AUX_MU_IIR [
/// Writing with bit 1 set will clear the receive FIFO
/// Writing with bit 2 set will clear the transmit FIFO
FIFO_CLEAR OFFSET(1) NUMBITS(2) [
Rx = 0b01,
Tx = 0b10,
All = 0b11
]
],
/// Mini Uart Line Control
AUX_MU_LCR [
/// Mode the UART works in
DATA_SIZE OFFSET(0) NUMBITS(2) [
SevenBit = 0b00,
EightBit = 0b11
]
],
/// Mini Uart Line Status
AUX_MU_LSR [
/// This bit is set if the transmit FIFO is empty and the transmitter is
/// idle. (Finished shifting out the last bit).
TX_IDLE OFFSET(6) NUMBITS(1) [],
/// This bit is set if the transmit FIFO can accept at least
/// one byte.
TX_EMPTY OFFSET(5) NUMBITS(1) [],
/// This bit is set if the receive FIFO holds at least 1
/// symbol.
DATA_READY OFFSET(0) NUMBITS(1) []
],
/// Mini Uart Extra Control
AUX_MU_CNTL [
/// If this bit is set the mini UART transmitter is enabled.
/// If this bit is clear the mini UART transmitter is disabled.
TX_EN OFFSET(1) NUMBITS(1) [
Disabled = 0,
Enabled = 1
],
/// If this bit is set the mini UART receiver is enabled.
/// If this bit is clear the mini UART receiver is disabled.
RX_EN OFFSET(0) NUMBITS(1) [
Disabled = 0,
Enabled = 1
]
],
/// Mini Uart Status
AUX_MU_STAT [
TX_DONE OFFSET(9) NUMBITS(1) [
No = 0,
Yes = 1
],
/// This bit is set if the transmit FIFO can accept at least
/// one byte.
SPACE_AVAILABLE OFFSET(1) NUMBITS(1) [
No = 0,
Yes = 1
],
/// This bit is set if the receive FIFO holds at least 1
/// symbol.
SYMBOL_AVAILABLE OFFSET(0) NUMBITS(1) [
No = 0,
Yes = 1
]
],
/// Mini Uart Baud rate
AUX_MU_BAUD [
/// Mini UART baud rate counter
RATE OFFSET(0) NUMBITS(16) []
]
}
register_structs! {
#[allow(non_snake_case)]
RegisterBlock {
// 0x00 - AUX_IRQ?
(0x00 => __reserved_1),
(0x04 => AUX_ENABLES: ReadWrite<u32, AUX_ENABLES::Register>),
(0x08 => __reserved_2),
(0x40 => AUX_MU_IO: ReadWrite<u32>),//Mini Uart I/O Data
(0x44 => AUX_MU_IER: WriteOnly<u32>),//Mini Uart Interrupt Enable
(0x48 => AUX_MU_IIR: WriteOnly<u32, AUX_MU_IIR::Register>),
(0x4c => AUX_MU_LCR: WriteOnly<u32, AUX_MU_LCR::Register>),
(0x50 => AUX_MU_MCR: WriteOnly<u32>),
(0x54 => AUX_MU_LSR: ReadOnly<u32, AUX_MU_LSR::Register>),
// 0x58 - AUX_MU_MSR
// 0x5c - AUX_MU_SCRATCH
(0x58 => __reserved_3),
(0x60 => AUX_MU_CNTL: WriteOnly<u32, AUX_MU_CNTL::Register>),
(0x64 => AUX_MU_STAT: ReadOnly<u32, AUX_MU_STAT::Register>),
(0x68 => AUX_MU_BAUD: WriteOnly<u32, AUX_MU_BAUD::Register>),
(0x6c => @END),
}
}
type Registers = MMIODerefWrapper<RegisterBlock>;
pub struct MiniUart {
registers: Registers,
}
pub struct PreparedMiniUart(MiniUart);
/// Divisor values for common baud rates
pub enum Rate {
Baud115200 = 270,
}
impl From<Rate> for u32 {
fn from(r: Rate) -> Self {
r as u32
}
}
// [temporary] Used in mmu.rs to set up local paging
pub const UART1_START: usize = 0x21_5000;
impl Default for MiniUart {
fn default() -> Self {
const UART1_BASE: usize = BcmHost::get_peripheral_address() + UART1_START;
unsafe { MiniUart::new(UART1_BASE) }
}
}
impl MiniUart {
/// # Safety
///
/// Unsafe, duh!
pub const unsafe fn new(base_addr: usize) -> MiniUart {
MiniUart {
registers: Registers::new(base_addr),
}
}
}
impl MiniUart {
cfg_if! {
if #[cfg(not(feature = "noserial"))] {
/// Set baud rate and characteristics (115200 8N1) and map to GPIO
pub fn prepare(self, gpio: &gpio::GPIO) -> PreparedMiniUart {
// GPIO pins should be set up first before enabling the UART
// Pin 14
const MINI_UART_TXD: gpio::Function = gpio::Function::Alt5;
// Pin 15
const MINI_UART_RXD: gpio::Function = gpio::Function::Alt5;
// map UART1 to GPIO pins
gpio.get_pin(14).into_alt(MINI_UART_TXD).set_pull_up_down(gpio::PullUpDown::Up);
gpio.get_pin(15).into_alt(MINI_UART_RXD).set_pull_up_down(gpio::PullUpDown::Up);
// initialize UART
self.registers.AUX_ENABLES.modify(AUX_ENABLES::MINI_UART_ENABLE::SET);
self.registers.AUX_MU_IER.set(0);
self.registers.AUX_MU_CNTL.set(0);
self.registers.AUX_MU_LCR.write(AUX_MU_LCR::DATA_SIZE::EightBit);
self.registers.AUX_MU_MCR.set(0);
self.registers.AUX_MU_IER.set(0);
self.registers.AUX_MU_BAUD
.write(AUX_MU_BAUD::RATE.val(Rate::Baud115200.into()));
// Clear FIFOs before using the device
self.registers.AUX_MU_IIR.write(AUX_MU_IIR::FIFO_CLEAR::All);
self.registers.AUX_MU_CNTL
.write(AUX_MU_CNTL::RX_EN::Enabled + AUX_MU_CNTL::TX_EN::Enabled);
PreparedMiniUart(self)
}
} else {
pub fn prepare(self, _gpio: &gpio::GPIO) -> PreparedMiniUart {
PreparedMiniUart(self)
}
}
}
}
impl Drop for PreparedMiniUart {
fn drop(&mut self) {
self.0
.registers
.AUX_ENABLES
.modify(AUX_ENABLES::MINI_UART_ENABLE::CLEAR);
// @todo disable gpio.PUD ?
}
}
impl SerialOps for PreparedMiniUart {
cfg_if! {
if #[cfg(not(feature = "noserial"))] {
/// Receive a byte without console translation
fn read_byte(&self) -> u8 {
// wait until something is in the buffer
crate::arch::loop_until(|| self.0.registers.AUX_MU_STAT.is_set(AUX_MU_STAT::SYMBOL_AVAILABLE));
// read it and return
self.0.registers.AUX_MU_IO.get() as u8
}
fn write_byte(&self, b: u8) {
// wait until we can send
crate::arch::loop_until(|| self.0.registers.AUX_MU_STAT.is_set(AUX_MU_STAT::SPACE_AVAILABLE));
// write the character to the buffer
self.0.registers.AUX_MU_IO.set(b as u32);
}
/// Wait until the TX FIFO is empty, aka all characters have been put on the
/// line.
fn flush(&self) {
crate::arch::loop_until(|| self.0.registers.AUX_MU_STAT.is_set(AUX_MU_STAT::TX_DONE));
}
/// Consume input until RX FIFO is empty, aka all pending characters have been
/// consumed.
fn clear_rx(&self) {
crate::arch::loop_while(|| {
let pending = self.0.registers.AUX_MU_STAT.is_set(AUX_MU_STAT::SYMBOL_AVAILABLE);
if pending { self.read_byte(); }
pending
});
}
} else {
fn read_byte(&self) -> u8 { 0 }
fn write_byte(&self, _byte: u8) {}
fn flush(&self) {}
fn clear_rx(&self) {}
}
}
}
impl ConsoleOps for PreparedMiniUart {
cfg_if! {
if #[cfg(not(feature = "noserial"))] {
/// Send a character
fn write_char(&self, c: char) {
let mut b = [0u8; 4];
let _ = c.encode_utf8(&mut b);
for x in 0..c.len_utf8() {
self.write_byte(b[x]);
}
}
/// Display a string
fn write_string(&self, string: &str) {
for c in string.chars() {
// convert newline to carriage return + newline
if c == '\n' {
self.write_char('\r')
}
self.write_char(c);
}
}
/// Receive a character -- FIXME: needs a state machine to read UTF-8 chars!
fn read_char(&self) -> char {
let mut ret = self.read_byte() as char;
// convert carriage return to newline -- this doesn't work well for reading binaries...
if ret == '\r' {
ret = '\n'
}
ret
}
} else {
fn write_char(&self, _c: char) {}
fn write_string(&self, _string: &str) {}
fn read_char(&self) -> char {
'\n'
}
}
}
}
impl fmt::Write for PreparedMiniUart {
fn write_str(&mut self, s: &str) -> fmt::Result {
self.write_string(s);
Ok(())
}
}

View File

@ -5,13 +5,12 @@
#![allow(dead_code)]
pub mod device_driver;
pub mod display;
pub mod drivers;
pub mod fb;
pub mod gpio;
pub mod mailbox;
pub mod memory;
pub mod mini_uart;
pub mod pl011_uart;
pub mod power;
pub mod vc;

View File

@ -7,11 +7,11 @@
use {
super::{
gpio,
device_driver::gpio,
mailbox::{channel, Mailbox, MailboxOps},
BcmHost,
},
crate::platform::MMIODerefWrapper,
crate::mmio_deref_wrapper::MMIODerefWrapper,
snafu::Snafu,
tock_registers::{
interfaces::{Readable, Writeable},

View File

@ -6,10 +6,42 @@
use core::cell::UnsafeCell;
pub struct NullLock<T> {
//--------------------------------------------------------------------------------------------------
// 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.
@ -21,24 +53,30 @@ pub struct NullLock<T> {
/// 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> Sync for NullLock<T> {}
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> {
pub const fn new(data: T) -> NullLock<T> {
NullLock {
/// Create an instance.
pub const fn new(data: T) -> Self {
Self {
data: UnsafeCell::new(data),
}
}
}
impl<T> NullLock<T> {
pub fn lock<F, R>(&self, f: F) -> R
where
F: FnOnce(&mut T) -> R,
{
// In a real lock, there would be code around this line that ensures
// that this mutable reference will ever only be given out to one thread
// at a time.
f(unsafe { &mut *self.data.get() })
//------------------------------------------------------------------------------
// OS Interface Code
//------------------------------------------------------------------------------
impl<T> interface::Mutex for NullLock<T> {
type Data = T;
fn lock<R>(&self, f: impl FnOnce(&mut Self::Data) -> R) -> R {
// In a real lock, there would be code encapsulating this line that ensures that this
// mutable reference will ever only be given out once at a time.
let data = unsafe { &mut *self.data.get() };
f(data)
}
}

View File

@ -17,7 +17,7 @@
#![reexport_test_harness_main = "test_main"]
#![deny(missing_docs)]
#![deny(warnings)]
#![deny(unused)]
#![allow(unused)]
#![feature(allocator_api)]
#[cfg(not(test))]
@ -28,32 +28,43 @@ use {
cfg_if::cfg_if,
core::cell::UnsafeCell,
machine::{
arch, entry, memory,
arch,
console::console,
entry, memory,
platform::rpi3::{
display::{Color, DrawError},
mailbox::{channel, Mailbox, MailboxOps},
vc::VC,
},
println, CONSOLE,
println,
},
};
entry!(kernel_main);
entry!(kernel_init);
/// Kernel entry point.
/// `arch` crate is responsible for calling it.
// #[inline]
pub fn kernel_main() -> ! {
pub unsafe fn kernel_init() -> ! {
#[cfg(feature = "jtag")]
machine::arch::jtag::wait_debugger();
init_exception_traps();
if let Err(x) = machine::platform::drivers::init() {
panic!("Error initializing platform drivers: {}", x);
}
#[cfg(not(feature = "noserial"))]
init_uart_serial();
// Initialize all device drivers.
machine::drivers::driver_manager().init_drivers();
init_mmu();
init_exception_traps(); // @todo
init_mmu(); // @todo
kernel_main()
}
/// Safe kernel code.
// #[inline]
pub fn kernel_main() -> ! {
#[cfg(test)]
test_main();
@ -96,48 +107,44 @@ fn init_exception_traps() {
println!("[!] Exception traps set up");
}
#[cfg(not(feature = "noserial"))]
fn init_uart_serial() {
use machine::platform::rpi3::{gpio::GPIO, mini_uart::MiniUart, pl011_uart::PL011Uart};
// fn init_uart_serial() {
// use machine::platform::rpi3::{gpio::GPIO, mini_uart::MiniUart, pl011_uart::PL011Uart};
//
// let gpio = GPIO::default();
// let uart = MiniUart::default();
// let uart = uart.prepare(&gpio);
// // console::replace_with(uart.into());
//
// println!("[0] MiniUART is live!");
let gpio = GPIO::default();
let uart = MiniUart::default();
let uart = uart.prepare(&gpio);
CONSOLE.lock(|c| {
// Move uart into the global CONSOLE.
c.replace_with(uart.into());
});
// Then immediately switch to PL011 (just as an example)
println!("[0] MiniUART is live!");
// let uart = PL011Uart::default();
// Then immediately switch to PL011 (just as an example)
// uart.init() will reconfigure the GPIO, which causes a race against
// the MiniUart that is still putting out characters on the physical
// line that are already buffered in its TX FIFO.
//
// To ensure the CPU doesn't rewire the GPIO before the MiniUart has put
// its last character, explicitly flush it before rewiring.
//
// If you switch to an output that happens to not use the same pair of
// physical wires (e.g. the Framebuffer), you don't need to do this,
// because flush() is anyways called implicitly by replace_with(). This
// is just a special case.
// CONSOLE.lock(|c| c.flush());
let uart = PL011Uart::default();
// uart.init() will reconfigure the GPIO, which causes a race against
// the MiniUart that is still putting out characters on the physical
// line that are already buffered in its TX FIFO.
//
// To ensure the CPU doesn't rewire the GPIO before the MiniUart has put
// its last character, explicitly flush it before rewiring.
//
// If you switch to an output that happens to not use the same pair of
// physical wires (e.g. the Framebuffer), you don't need to do this,
// because flush() is anyways called implicitly by replace_with(). This
// is just a special case.
CONSOLE.lock(|c| c.flush());
match uart.prepare(&gpio) {
Ok(uart) => {
CONSOLE.lock(|c| {
// Move uart into the global CONSOLE.
c.replace_with(uart.into());
});
println!("[0] UART0 is live!");
}
Err(_) => println!("[0] Error switching to PL011 UART, continue with MiniUART"),
}
}
// match uart.prepare(&gpio) {
// Ok(uart) => {
// CONSOLE.lock(|c| {
// // Move uart into the global CONSOLE.
// c.replace_with(uart.into());
// });
// println!("[0] UART0 is live!");
// }
// Err(_) => println!("[0] Error switching to PL011 UART, continue with MiniUART"),
// }
// }
//------------------------------------------------------------
// Start a command prompt
@ -146,11 +153,9 @@ fn command_prompt() {
'cmd_loop: loop {
let mut buf = [0u8; 64];
match CONSOLE.lock(|c| c.command_prompt(&mut buf)) {
match console().command_prompt(&mut buf) {
b"mmu" => init_mmu(),
b"feats" => print_mmu_state_and_features(),
#[cfg(not(feature = "noserial"))]
b"uart" => init_uart_serial(),
b"disp" => check_display_init(),
b"trap" => check_data_abort_trap(),
b"map" => machine::platform::memory::mmu::virt_mem_layout().print_layout(),