refactor: 📦 Use better code structure
As inspired by andre-richter's tutorials.
This commit is contained in:
parent
46d0c4cffc
commit
0e1c6669ac
|
@ -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
|
||||
'''
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -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 {}
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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 _) }
|
||||
}
|
||||
}
|
|
@ -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 _) }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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!
|
|
@ -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 {}
|
|
@ -0,0 +1,5 @@
|
|||
pub mod gpio;
|
||||
pub mod mini_uart;
|
||||
pub mod pl011_uart;
|
||||
|
||||
pub use {gpio::*, mini_uart::*, pl011_uart::*};
|
|
@ -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::{
|
|
@ -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(())
|
||||
}
|
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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},
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(),
|
||||
|
|
Loading…
Reference in New Issue