diff --git a/Cargo.lock b/Cargo.lock index 2cb020920..4dcdaabec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4819,7 +4819,7 @@ dependencies = [ [[package]] name = "xous-kernel" -version = "0.9.17" +version = "0.9.18" dependencies = [ "bitflags", "crossbeam-channel", diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index 3d73439e8..ff8f981fb 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -4,7 +4,7 @@ description = "Core kernel for Xous, including task switching and memory managem license = "MIT OR Apache-2.0" edition = "2018" name = "xous-kernel" -version = "0.9.17" +version = "0.9.18" resolver = "2" # Dependency versions enforced by Cargo.lock. diff --git a/kernel/src/arch/riscv/mod.rs b/kernel/src/arch/riscv/mod.rs index ac1d1fb99..eb2d5f784 100644 --- a/kernel/src/arch/riscv/mod.rs +++ b/kernel/src/arch/riscv/mod.rs @@ -7,7 +7,6 @@ pub mod exception; pub mod irq; pub mod mem; pub mod process; -pub mod rand; pub mod syscall; pub use process::Thread; @@ -49,7 +48,6 @@ pub fn init() { sie::set_ssoft(); sie::set_sext(); } - rand::init(); } /// Put the core to sleep until an interrupt hits. Returns `true` diff --git a/kernel/src/arch/riscv/rand.rs b/kernel/src/arch/riscv/rand.rs deleted file mode 100644 index c742af7f9..000000000 --- a/kernel/src/arch/riscv/rand.rs +++ /dev/null @@ -1,69 +0,0 @@ -// SPDX-FileCopyrightText: 2020 Sean Cross -// SPDX-License-Identifier: Apache-2.0 - -use crate::mem::MemoryManager; -use utralib::generated::*; -use xous_kernel::{MemoryFlags, MemoryType, PID}; - -pub const TRNG_KERNEL: Trng = Trng { - // the HW device mapping is done in xous-rs/src/lib.rs/init() - // the manually chosen virtual address has to be in the top 4MiB as it is the only page shared among all processes - base: 0xffce_0000 as *mut usize, // see https://github.com/betrusted-io/xous-core/blob/master/docs/memory.md -}; - -pub struct Trng { - pub base: *mut usize, -} - -pub fn init() { - // Map the TRNG so that we can allocate names - // hardware guarantees that: - // - TRNG will automatically power on - // - Both TRNGs are enabled, with conservative defaults - // - Kernel FIFO will fill with TRNGs such that at least the next 512 calls to get_u32() will succeed without delay - // - The kernel will start a TRNG server - // - All further security decisions and policies are 100% delegated to this new server. - MemoryManager::with_mut(|memory_manager| { - memory_manager - .map_range( - utra::trng_kernel::HW_TRNG_KERNEL_BASE as *mut u8, - ((TRNG_KERNEL.base as u32) & !4095) as *mut u8, - 4096, - PID::new(1).unwrap(), - MemoryFlags::R | MemoryFlags::W, - MemoryType::Default, - ) - .expect("unable to map TRNG") - }); - - let trng_kernel_csr = CSR::new(TRNG_KERNEL.base as *mut u32); - if false { // raw random path - left in for debugging urandom, can strip out later - while trng_kernel_csr.rf(utra::trng_kernel::STATUS_AVAIL) == 0 {} - // discard the first entry, as it is always 0x0000_0000 - // this is because the read register is a pipeline stage behind the FIFO - // once the pipeline has been filled, there is no need to prime it again. - trng_kernel_csr.rf(utra::trng_kernel::DATA_DATA); - } else { // urandom path (recommended) - // simulations show this isn't strictly necessary, but I prefer to have it - // just in case a subtle bug in the reset logic leaves something deterministic - // in the connecting logic: the simulation coverage stops at the edge of the TRNG block. - for _ in 0..4 { - // wait until the urandom port is initialized - while trng_kernel_csr.rf(utra::trng_kernel::URANDOM_VALID_URANDOM_VALID) == 0 {} - // pull a dummy piece of data - trng_kernel_csr.rf(utra::trng_kernel::URANDOM_URANDOM); - } - } - -} - -pub fn get_u32() -> u32 { - let trng_kernel_csr = CSR::new(TRNG_KERNEL.base as *mut u32); - if false { // raw random path - while trng_kernel_csr.rf(utra::trng_kernel::STATUS_AVAIL) == 0 {} - trng_kernel_csr.rf(utra::trng_kernel::DATA_DATA) - } else { // urandom path (recommended) - while trng_kernel_csr.rf(utra::trng_kernel::URANDOM_VALID_URANDOM_VALID) == 0 {} - trng_kernel_csr.rf(utra::trng_kernel::URANDOM_URANDOM) - } -} diff --git a/kernel/src/debug/macros.rs b/kernel/src/debug/macros.rs new file mode 100644 index 000000000..f05f19877 --- /dev/null +++ b/kernel/src/debug/macros.rs @@ -0,0 +1,53 @@ +// SPDX-FileCopyrightText: 2020 Sean Cross +// SPDX-FileCopyrightText: 2022 Foundation Devices, Inc. +// SPDX-License-Identifier: Apache-2.0 + +/// Prints to the debug output directly. +#[cfg(baremetal)] +#[macro_export] +macro_rules! print { + ($($args:tt)+) => {{ + #[allow(unused_unsafe)] + unsafe { + use core::fmt::Write; + if let Some(stream) = crate::debug::shell::OUTPUT.as_mut() { + write!(stream, $($args)+).unwrap(); + } + } + }}; +} + +/// Prints to the debug output directly, with a newline. +#[cfg(baremetal)] +#[macro_export] +macro_rules! println { + () => ({ + print!("\r\n") + }); + ($fmt:expr) => ({ + print!(concat!($fmt, "\r\n")) + }); + ($fmt:expr, $($args:tt)+) => ({ + print!(concat!($fmt, "\r\n"), $($args)+) + }); +} + +#[cfg(feature = "debug-print")] +#[macro_export] +macro_rules! klog { + () => ({ + print!(" [{}:{}]", file!(), line!()) + }); + ($fmt:expr) => ({ + print!(concat!(" [{}:{} ", $fmt, "]"), file!(), line!()) + }); + ($fmt:expr, $($args:tt)+) => ({ + print!(concat!(" [{}:{} ", $fmt, "]"), file!(), line!(), $($args)+) + }); +} + +#[cfg(not(feature = "debug-print"))] +#[macro_export] +macro_rules! klog { + ($($args:tt)+) => {{}}; +} diff --git a/kernel/src/debug/mod.rs b/kernel/src/debug/mod.rs index 5204d6fef..04452ee3e 100644 --- a/kernel/src/debug/mod.rs +++ b/kernel/src/debug/mod.rs @@ -1,318 +1,7 @@ // SPDX-FileCopyrightText: 2020 Sean Cross // SPDX-License-Identifier: Apache-2.0 -#[cfg(baremetal)] -use core::fmt::{Error, Write}; -#[cfg(baremetal)] -use utralib::generated::*; - -#[cfg(baremetal)] -pub static mut DEBUG_OUTPUT: Option<&'static mut dyn Write> = None; - -pub use crate::arch::process::Process as ArchProcess; - #[macro_use] -#[cfg(all( - not(test), - baremetal, - any(feature = "debug-print", feature = "print-panics") -))] -pub mod debug_print_hardware { - // the HW device mapping is done in main.rs/init(); the virtual address has to be in the top 4MiB as it is the only page shared among all processes - pub const SUPERVISOR_UART_ADDR: *mut usize = 0xffcf_0000 as *mut usize; // see https://github.com/betrusted-io/xous-core/blob/master/docs/memory.md -} -#[cfg(all( - not(test), - baremetal, - any(feature = "debug-print", feature = "print-panics") -))] -pub use crate::debug::debug_print_hardware::SUPERVISOR_UART_ADDR; - -#[cfg(baremetal)] -#[macro_export] -macro_rules! print { - ($($args:tt)+) => {{ - #[allow(unused_unsafe)] - unsafe { - if let Some(mut stream) = crate::debug::DEBUG_OUTPUT.as_mut() { - write!(&mut stream, $($args)+).unwrap(); - } - } - }}; -} - -#[cfg(baremetal)] -#[macro_export] -macro_rules! println -{ - () => ({ - print!("\r\n") - }); - ($fmt:expr) => ({ - print!(concat!($fmt, "\r\n")) - }); - ($fmt:expr, $($args:tt)+) => ({ - print!(concat!($fmt, "\r\n"), $($args)+) - }); -} - -#[cfg(baremetal)] -pub struct Uart {} +mod macros; #[cfg(baremetal)] -pub static mut UART: Uart = Uart {}; - -#[cfg(all(baremetal, feature = "wrap-print"))] -static mut CHAR_COUNT: usize = 0; - -#[cfg(baremetal)] -impl Uart { - #[allow(dead_code)] - pub fn init(self) { - unsafe { DEBUG_OUTPUT = Some(&mut UART) }; - let mut uart_csr = CSR::new(crate::debug::SUPERVISOR_UART_ADDR as *mut u32); - uart_csr.rmwf(utra::uart::EV_ENABLE_RX, 1); - } - - pub fn putc(&self, c: u8) { - if unsafe { DEBUG_OUTPUT.is_none() } { - return; - } - - let mut uart_csr = CSR::new(crate::debug::SUPERVISOR_UART_ADDR as *mut u32); - // Wait until TXFULL is `0` - while uart_csr.r(utra::uart::TXFULL) != 0 {} - #[cfg(feature = "wrap-print")] - unsafe { - if c == b'\n' { - CHAR_COUNT = 0; - } else if CHAR_COUNT > 80 { - CHAR_COUNT = 0; - self.putc(b'\n'); - self.putc(b'\r'); - self.putc(b' '); - self.putc(b' '); - self.putc(b' '); - self.putc(b' '); - } else { - CHAR_COUNT += 1; - } - } - uart_csr.wfo(utra::uart::RXTX_RXTX, c as u32); - } - - #[allow(dead_code)] - pub fn getc(&self) -> Option { - if unsafe { DEBUG_OUTPUT.is_none() } { - return None; - } - let mut uart_csr = CSR::new(crate::debug::SUPERVISOR_UART_ADDR as *mut u32); - // If EV_PENDING_RX is 1, return the pending character. - // Otherwise, return None. - match uart_csr.rf(utra::uart::EV_PENDING_RX) { - 0 => None, - _ => { - let ret = Some(uart_csr.r(utra::uart::RXTX) as u8); - uart_csr.wfo(utra::uart::EV_PENDING_RX, 1); - ret - } - } - } -} - -#[cfg(all( - baremetal, - not(test), - any(feature = "debug-print", feature = "print-panics") -))] -pub fn irq(_irq_number: usize, _arg: *mut usize) { - let uart = Uart {}; - while let Some(b) = uart.getc() { - process_irq_character(b); - } - // uart.acknowledge_irq(); -} - -#[cfg(all( - baremetal, - not(test), - any(feature = "debug-print", feature = "print-panics") -))] -fn process_irq_character(b: u8) { - match b { - b'i' => { - println!("Interrupt handlers:"); - println!(" IRQ | Process | Handler | Argument"); - crate::services::SystemServices::with(|system_services| { - crate::irq::for_each_irq(|irq, pid, address, arg| { - println!( - " {}: {} @ {:x?} {:x?}", - irq, - system_services.process_name(*pid).unwrap_or(""), - address, - arg - ); - }); - }); - } - b'm' => { - println!("Printing memory page tables"); - crate::services::SystemServices::with(|system_services| { - let current_pid = system_services.current_pid(); - for process in &system_services.processes { - if !process.free() { - println!( - "PID {} {}:", - process.pid, - system_services.process_name(process.pid).unwrap_or("") - ); - process.activate().unwrap(); - crate::arch::mem::MemoryMapping::current().print_map(); - println!(); - } - } - system_services - .get_process(current_pid) - .unwrap() - .activate() - .unwrap(); - }); - } - b'p' => { - println!("Printing processes"); - crate::services::SystemServices::with(|system_services| { - let current_pid = system_services.current_pid(); - for process in &system_services.processes { - if !process.free() { - process.activate().unwrap(); - let mut connection_count = 0; - ArchProcess::with_inner(|process_inner| { - for conn in &process_inner.connection_map { - if conn.is_some() { - connection_count += 1; - } - } - }); - println!( - "{:?} conns:{}/32 {}", - process, - connection_count, - system_services.process_name(process.pid).unwrap_or("") - ); - } - } - system_services - .get_process(current_pid) - .unwrap() - .activate() - .unwrap(); - }); - } - b'P' => { - println!("Printing processes and threads"); - crate::services::SystemServices::with(|system_services| { - let current_pid = system_services.current_pid(); - for process in &system_services.processes { - if !process.free() { - println!( - "{:?} {}:", - process, - system_services.process_name(process.pid).unwrap_or("") - ); - process.activate().unwrap(); - crate::arch::process::Process::with_current_mut(|arch_process| { - arch_process.print_all_threads() - }); - println!(); - } - } - system_services - .get_process(current_pid) - .unwrap() - .activate() - .unwrap(); - }); - } - b'r' => { - println!("RAM usage:"); - let mut total_bytes = 0; - crate::services::SystemServices::with(|system_services| { - crate::mem::MemoryManager::with(|mm| { - for process in &system_services.processes { - if !process.free() { - let bytes_used = mm.ram_used_by(process.pid); - total_bytes += bytes_used; - println!( - " PID {:>3}: {:>4} k {}", - process.pid, - bytes_used / 1024, - system_services.process_name(process.pid).unwrap_or("") - ); - } - } - }); - }); - println!("{} k total", total_bytes / 1024); - } - b's' => { - println!("Servers in use:"); - crate::services::SystemServices::with(|system_services| { - println!(" idx | pid | process | sid"); - println!(" --- + --- + -------------------- | ------------------"); - for (idx, server) in system_services.servers.iter().enumerate() { - if let Some(s) = server { - println!( - " {:3} | {:3} | {:20} | {:x?}", - idx, - s.pid, - system_services.process_name(s.pid).unwrap_or(""), - s.sid - ); - } - } - }); - } - b'h' => { - println!("Xous Kernel Debug"); - println!("key | command"); - println!("--- + -----------------------"); - println!(" i | print irq handlers"); - println!(" m | print MMU page tables of all processes"); - println!(" p | print all processes"); - println!(" P | print all processes and threads"); - println!(" r | report RAM usage of all processes"); - println!(" s | print all allocated servers"); - } - _ => {} - } -} - -#[cfg(baremetal)] -impl Write for Uart { - fn write_str(&mut self, s: &str) -> Result<(), Error> { - for c in s.bytes() { - self.putc(c); - } - Ok(()) - } -} - -#[cfg(feature = "debug-print")] -#[macro_export] -macro_rules! klog -{ - () => ({ - print!(" [{}:{}]", file!(), line!()) - }); - ($fmt:expr) => ({ - print!(concat!(" [{}:{} ", $fmt, "]"), file!(), line!()) - }); - ($fmt:expr, $($args:tt)+) => ({ - print!(concat!(" [{}:{} ", $fmt, "]"), file!(), line!(), $($args)+) - }); -} - -#[cfg(not(feature = "debug-print"))] -#[macro_export] -macro_rules! klog { - ($($args:tt)+) => {{}}; -} +pub mod shell; diff --git a/kernel/src/debug/shell.rs b/kernel/src/debug/shell.rs new file mode 100644 index 000000000..86408ad97 --- /dev/null +++ b/kernel/src/debug/shell.rs @@ -0,0 +1,230 @@ +// SPDX-FileCopyrightText: 2020 Sean Cross +// SPDX-FileCopyrightText: 2022 Foundation Devices, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use core::fmt; +use crate::{ + args::KernelArguments, + io::{SerialWrite, SerialRead}, +}; + +/// Instance of the shell output. +pub static mut OUTPUT: Option = None; + +/// Shell output. +pub struct Output { + serial: &'static mut dyn SerialWrite, + #[cfg(feature = "wrap-print")] + character_count: usize, +} + +impl Output { + fn new(serial: &'static mut dyn SerialWrite) -> Output { + Output { + serial, + #[cfg(feature = "wrap-print")] + character_count: 0, + } + } +} + +impl fmt::Write for Output { + fn write_str(&mut self, s: &str) -> fmt::Result { + for c in s.bytes() { + #[cfg(feature = "wrap-print")] + if c == b'\n' { + self.character_count = 0; + } else if self.character_count > 80 { + self.character_count = 0; + self.uart.putc(b'\n'); + self.uart.putc(b'\r'); + self.uart.putc(b' '); + self.uart.putc(b' '); + self.uart.putc(b' '); + self.uart.putc(b' '); + } else { + self.character_count += 1; + } + + self.serial.putc(c); + } + Ok(()) + } +} + +/// Initialize the kernel shell. +/// +/// This should be called in platform initialization code. +pub fn init(serial: &'static mut dyn SerialWrite) { + unsafe { + OUTPUT = Some(Output::new(serial)) + } + + // Print the processed kernel arguments + let args = KernelArguments::get(); + println!("Kernel arguments:"); + for arg in args.iter() { + println!(" {}", arg); + } +} + +/// Process possible characters received through a serial interface. +/// +/// This should be called when a serial interface has new data, for example, +/// on an interrupt. +pub fn process_characters(serial: &mut R) { + while let Some(b) = serial.getc() { + handle_character(b); + } +} + +fn handle_character(b: u8) { + use crate::services::ArchProcess; + + match b { + b'i' => { + println!("Interrupt handlers:"); + println!(" IRQ | Process | Handler | Argument"); + crate::services::SystemServices::with(|system_services| { + crate::irq::for_each_irq(|irq, pid, address, arg| { + println!( + " {}: {} @ {:x?} {:x?}", + irq, + system_services.process_name(*pid).unwrap_or(""), + address, + arg + ); + }); + }); + } + b'm' => { + println!("Printing memory page tables"); + crate::services::SystemServices::with(|system_services| { + let current_pid = system_services.current_pid(); + for process in &system_services.processes { + if !process.free() { + println!( + "PID {} {}:", + process.pid, + system_services.process_name(process.pid).unwrap_or("") + ); + process.activate().unwrap(); + crate::arch::mem::MemoryMapping::current().print_map(); + println!(); + } + } + system_services + .get_process(current_pid) + .unwrap() + .activate() + .unwrap(); + }); + } + b'p' => { + println!("Printing processes"); + crate::services::SystemServices::with(|system_services| { + let current_pid = system_services.current_pid(); + for process in &system_services.processes { + if !process.free() { + process.activate().unwrap(); + let mut connection_count = 0; + ArchProcess::with_inner(|process_inner| { + for conn in &process_inner.connection_map { + if conn.is_some() { + connection_count += 1; + } + } + }); + println!( + "{:?} conns:{}/32 {}", + process, + connection_count, + system_services.process_name(process.pid).unwrap_or("") + ); + } + } + system_services + .get_process(current_pid) + .unwrap() + .activate() + .unwrap(); + }); + } + b'P' => { + println!("Printing processes and threads"); + crate::services::SystemServices::with(|system_services| { + let current_pid = system_services.current_pid(); + for process in &system_services.processes { + if !process.free() { + println!( + "{:?} {}:", + process, + system_services.process_name(process.pid).unwrap_or("") + ); + process.activate().unwrap(); + crate::arch::process::Process::with_current_mut(|arch_process| { + arch_process.print_all_threads() + }); + println!(); + } + } + system_services + .get_process(current_pid) + .unwrap() + .activate() + .unwrap(); + }); + } + b'r' => { + println!("RAM usage:"); + let mut total_bytes = 0; + crate::services::SystemServices::with(|system_services| { + crate::mem::MemoryManager::with(|mm| { + for process in &system_services.processes { + if !process.free() { + let bytes_used = mm.ram_used_by(process.pid); + total_bytes += bytes_used; + println!( + " PID {:>3}: {:>4} k {}", + process.pid, + bytes_used / 1024, + system_services.process_name(process.pid).unwrap_or("") + ); + } + } + }); + }); + println!("{} k total", total_bytes / 1024); + } + b's' => { + println!("Servers in use:"); + crate::services::SystemServices::with(|system_services| { + println!(" idx | pid | process | sid"); + println!(" --- + --- + -------------------- | ------------------"); + for (idx, server) in system_services.servers.iter().enumerate() { + if let Some(s) = server { + println!( + " {:3} | {:3} | {:20} | {:x?}", + idx, + s.pid, + system_services.process_name(s.pid).unwrap_or(""), + s.sid + ); + } + } + }); + } + b'h' => { + println!("Xous Kernel Debug"); + println!("key | command"); + println!("--- + -----------------------"); + println!(" i | print irq handlers"); + println!(" m | print MMU page tables of all processes"); + println!(" p | print all processes"); + println!(" P | print all processes and threads"); + println!(" r | report RAM usage of all processes"); + println!(" s | print all allocated servers"); + } + _ => {} + } +} diff --git a/kernel/src/io.rs b/kernel/src/io.rs new file mode 100644 index 000000000..9caa56b9e --- /dev/null +++ b/kernel/src/io.rs @@ -0,0 +1,14 @@ +// SPDX-FileCopyrightText: 2022 Foundation Devices, Inc. +// SPDX-License-Identifier: Apache-2.0 + +/// A trait for serial like drivers which are byte-oriented sinks. +pub trait SerialWrite { + /// Write a single byte. + fn putc(&mut self, b: u8); +} + +/// A trait for serial like drivers which allows reading from a source. +pub trait SerialRead { + /// Read a single byte. + fn getc(&mut self) -> Option; +} diff --git a/kernel/src/main.rs b/kernel/src/main.rs index 60db7711d..cefd0191d 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -18,9 +18,11 @@ mod arch; #[macro_use] mod args; +mod io; mod irq; mod macros; mod mem; +mod platform; mod server; mod services; mod syscall; @@ -59,49 +61,16 @@ pub unsafe extern "C" fn init(arg_offset: *const u32, init_offset: *const u32, r system_services.init_from_memory(init_offset, &args) }); - // Now that the memory manager is set up, perform any arch-specific initializations. + // Now that the memory manager is set up, perform any architecture and + // platform specific initializations. arch::init(); + platform::init(); - // Either map memory using a syscall, or if we're debugging the syscall - // handler then directly map it. - #[cfg(any(feature = "debug-print", feature = "print-panics"))] - { - use utralib::generated::*; - xous_kernel::claim_interrupt( - utra::uart::UART_IRQ, - debug::irq, - core::ptr::null_mut::(), - ) - .expect("Couldn't claim debug interrupt"); - // Map the serial port so println!() works as expected. - mem::MemoryManager::with_mut(|memory_manager| { - memory_manager - .map_range( - utra::uart::HW_UART_BASE as *mut u8, - ((debug::SUPERVISOR_UART_ADDR as u32) & !4095) as *mut u8, - 4096, - PID::new(1).unwrap(), - MemoryFlags::R | MemoryFlags::W, - MemoryType::Default, - ) - .expect("unable to map serial port") - }); - debug::Uart {}.init(); - println!("KMAIN (clean boot): Supervisor mode started..."); - println!("Claiming IRQ {} via syscall...", utra::uart::UART_IRQ); - print!("}} "); - - // Print the processed kernel arguments - let args = args::KernelArguments::get(); - println!("Kernel arguments:"); - for arg in args.iter() { - println!(" {}", arg); - } - } + println!("KMAIN (clean boot): Supervisor mode started..."); // rand::init() already clears the initial pipe, but pump the TRNG a little more out of no other reason than sheer paranoia - arch::rand::get_u32(); - arch::rand::get_u32(); + platform::rand::get_u32(); + platform::rand::get_u32(); } /// Loop through the SystemServices list to determine the next PID to be run. diff --git a/kernel/src/platform/mod.rs b/kernel/src/platform/mod.rs new file mode 100644 index 000000000..850735f37 --- /dev/null +++ b/kernel/src/platform/mod.rs @@ -0,0 +1,14 @@ +// SPDX-FileCopyrightText: 2022 Foundation Devices, Inc. +// SPDX-License-Identifier: Apache-2.0 + +#[cfg(any(feature="precursor", feature="renode"))] +pub mod precursor; + +pub mod rand; + +/// Platform specific initialization. +#[cfg(not(any(unix, windows)))] +pub fn init() { + #[cfg(any(feature="precursor", feature="renode"))] + self::precursor::init(); +} \ No newline at end of file diff --git a/kernel/src/platform/precursor/mod.rs b/kernel/src/platform/precursor/mod.rs new file mode 100644 index 000000000..40ffd80a0 --- /dev/null +++ b/kernel/src/platform/precursor/mod.rs @@ -0,0 +1,13 @@ +// SPDX-FileCopyrightText: 2022 Foundation Devices, Inc. +// SPDX-License-Identifier: Apache-2.0 + +#[cfg(any(feature = "debug-print", feature = "print-panics"))] +pub mod uart; +pub mod rand; + +/// Precursor specific initialization. +pub fn init() { + self::rand::init(); + #[cfg(any(feature = "debug-print", feature = "print-panics"))] + self::uart::init(); +} \ No newline at end of file diff --git a/kernel/src/platform/precursor/rand.rs b/kernel/src/platform/precursor/rand.rs new file mode 100644 index 000000000..227dd370f --- /dev/null +++ b/kernel/src/platform/precursor/rand.rs @@ -0,0 +1,98 @@ +// SPDX-FileCopyrightText: 2020 Sean Cross +// SPDX-FileCopyrightText: 2022 Foundation Devices, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use crate::mem::MemoryManager; +use utralib::generated::*; +use xous_kernel::{MemoryFlags, MemoryType, PID}; + +/// The manually chosen virtual address has to be in the top 4MiB as it is the +/// only page shared among all processes. +/// +/// See https://github.com/betrusted-io/xous-core/blob/master/docs/memory.md +pub const TRNG_KERNEL_ADDR: usize = 0xffce_0000; +pub static mut TRNG_KERNEL: Option = None; + +pub struct TrngKernel { + pub trng_kernel_csr: CSR, +} + +impl TrngKernel { + pub fn new(addr: usize) -> TrngKernel { + TrngKernel { + trng_kernel_csr: CSR::new(addr as *mut u32), + } + } + + pub fn init(&mut self) { + if false { // raw random path - left in for debugging urandom, can strip out later + while self.trng_kernel_csr.rf(utra::trng_kernel::STATUS_AVAIL) == 0 {} + // discard the first entry, as it is always 0x0000_0000 + // this is because the read register is a pipeline stage behind the FIFO + // once the pipeline has been filled, there is no need to prime it again. + self.trng_kernel_csr.rf(utra::trng_kernel::DATA_DATA); + } else { // urandom path (recommended) + // simulations show this isn't strictly necessary, but I prefer to have it + // just in case a subtle bug in the reset logic leaves something deterministic + // in the connecting logic: the simulation coverage stops at the edge of the TRNG block. + for _ in 0..4 { + // wait until the urandom port is initialized + while self.trng_kernel_csr.rf(utra::trng_kernel::URANDOM_VALID_URANDOM_VALID) == 0 {} + // pull a dummy piece of data + self.trng_kernel_csr.rf(utra::trng_kernel::URANDOM_URANDOM); + } + } + } + + pub fn get_u32(&mut self) -> u32 { + if false { // raw random path + while self.trng_kernel_csr.rf(utra::trng_kernel::STATUS_AVAIL) == 0 {} + self.trng_kernel_csr.rf(utra::trng_kernel::DATA_DATA) + } else { // urandom path (recommended) + while self.trng_kernel_csr.rf(utra::trng_kernel::URANDOM_VALID_URANDOM_VALID) == 0 {} + self.trng_kernel_csr.rf(utra::trng_kernel::URANDOM_URANDOM) + } + } +} + +/// Initialize TRNG driver. +/// +/// Needed so that the kernel can allocate names. +pub fn init() { + // Map the TRNG so that we can allocate names + // hardware guarantees that: + // - TRNG will automatically power on + // - Both TRNGs are enabled, with conservative defaults + // - Kernel FIFO will fill with TRNGs such that at least the next 512 calls to get_u32() will succeed without delay + // - The kernel will start a TRNG server + // - All further security decisions and policies are 100% delegated to this new server. + MemoryManager::with_mut(|memory_manager| { + memory_manager + .map_range( + utra::trng_kernel::HW_TRNG_KERNEL_BASE as *mut u8, + (TRNG_KERNEL_ADDR & !4095) as *mut u8, + 4096, + PID::new(1).unwrap(), + MemoryFlags::R | MemoryFlags::W, + MemoryType::Default, + ) + .expect("unable to map TRNG_KERNEL") + }); + + let mut trng_kernel = TrngKernel::new(TRNG_KERNEL_ADDR); + trng_kernel.init(); + + unsafe { + TRNG_KERNEL = Some(trng_kernel); + } +} + +/// Retrieve random `u32`. +pub fn get_u32() -> u32 { + unsafe { + TRNG_KERNEL + .as_mut() + .expect("TRNG_KERNEL driver not initialized") + .get_u32() + } +} diff --git a/kernel/src/platform/precursor/uart.rs b/kernel/src/platform/precursor/uart.rs new file mode 100644 index 000000000..0aa42c855 --- /dev/null +++ b/kernel/src/platform/precursor/uart.rs @@ -0,0 +1,103 @@ +// SPDX-FileCopyrightText: 2020 Sean Cross +// SPDX-FileCopyrightText: 2022 Foundation Devices, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + io::{SerialWrite, SerialRead}, + mem::MemoryManager, + debug::shell::process_characters, + PID, +}; +use utralib::generated::*; +use xous_kernel::{MemoryFlags, MemoryType}; + +/// UART virtual address. +/// +/// See https://github.com/betrusted-io/xous-core/blob/master/docs/memory.md +pub const UART_ADDR: usize = 0xffcf_0000; + +/// UART instance. +/// +/// Initialized by [`init`]. +pub static mut UART: Option = None; + +/// UART peripheral driver. +pub struct Uart { + uart_csr: CSR, + callback: fn(&mut Self), +} + +impl Uart { + pub fn new(addr: usize, callback: fn(&mut Self)) -> Uart { + Uart { + uart_csr: CSR::new(addr as *mut u32), + callback, + } + } + + pub fn init(&mut self) { + self.uart_csr.rmwf(utra::uart::EV_ENABLE_RX, 1); + } + + pub fn irq(_irq_number: usize, arg: *mut usize) { + let uart = unsafe { &mut *(arg as *mut Uart) }; + (uart.callback)(uart); + // uart.acknowledge_irq(); + } +} + +impl SerialWrite for Uart { + fn putc(&mut self, c: u8) { + // Wait until TXFULL is `0` + while self.uart_csr.r(utra::uart::TXFULL) != 0 {} + self.uart_csr.wfo(utra::uart::RXTX_RXTX, c as u32); + } +} + +impl SerialRead for Uart { + fn getc(&mut self) -> Option { + // If EV_PENDING_RX is 1, return the pending character. + // Otherwise, return None. + match self.uart_csr.rf(utra::uart::EV_PENDING_RX) { + 0 => None, + _ => { + let ret = Some(self.uart_csr.r(utra::uart::RXTX) as u8); + self.uart_csr.wfo(utra::uart::EV_PENDING_RX, 1); + ret + } + } + } +} + +/// Initialize UART driver and debug shell. +pub fn init() { + // Map the UART peripheral. + MemoryManager::with_mut(|memory_manager| { + memory_manager + .map_range( + utra::uart::HW_UART_BASE as *mut u8, + (UART_ADDR & !4095) as *mut u8, + 4096, + PID::new(1).unwrap(), + MemoryFlags::R | MemoryFlags::W, + MemoryType::Default, + ) + .expect("unable to map serial port") + }); + + let mut uart = Uart::new(UART_ADDR, process_characters); + uart.init(); + + unsafe { + UART = Some(uart); + crate::debug::shell::init(UART.as_mut().unwrap()); + + // Claim UART interrupt. + println!("Claiming IRQ {} via syscall...", utra::uart::UART_IRQ); + xous_kernel::claim_interrupt( + utra::uart::UART_IRQ, + Uart::irq, + (UART.as_mut().unwrap() as *mut Uart) as *mut usize, + ).expect("Couldn't claim debug interrupt"); + } +} diff --git a/kernel/src/platform/rand.rs b/kernel/src/platform/rand.rs new file mode 100644 index 000000000..22c5027c1 --- /dev/null +++ b/kernel/src/platform/rand.rs @@ -0,0 +1,11 @@ +// SPDX-FileCopyrightText: 2022 Foundation Devices, Inc. +// SPDX-License-Identifier: Apache-2.0 + +pub fn get_u32() -> u32 { + // hosted rand code is coupled with arch code. + #[cfg(any(windows, unix))] + let rand = crate::arch::rand::get_u32(); + #[cfg(any(feature="precursor", feature="renode"))] + let rand = crate::platform::precursor::rand::get_u32(); + rand +} \ No newline at end of file diff --git a/kernel/src/services.rs b/kernel/src/services.rs index bb9033b63..f6c1ac1e1 100644 --- a/kernel/src/services.rs +++ b/kernel/src/services.rs @@ -5,6 +5,7 @@ use crate::arch; use crate::arch::mem::MemoryMapping; pub use crate::arch::process::Process as ArchProcess; pub use crate::arch::process::Thread; +use crate::platform; use xous_kernel::arch::ProcessStartup; use xous_kernel::MemoryRange; @@ -1743,10 +1744,10 @@ impl SystemServices { /// any processes. pub fn create_server_id(&mut self) -> Result { let sid = SID::from_u32( - arch::rand::get_u32(), - arch::rand::get_u32(), - arch::rand::get_u32(), - arch::rand::get_u32(), + platform::rand::get_u32(), + platform::rand::get_u32(), + platform::rand::get_u32(), + platform::rand::get_u32(), ); Ok(sid) } diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 4b786863f..2946d1823 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -122,7 +122,7 @@ fn main() -> Result<(), Box> { "xous-susres@0.1.18", // ticktimer registers with susres to coordinate time continuity across sleeps "xous-ticktimer@0.1.17", // "well known" service: thread scheduling ].to_vec(); - let xous_kernel_remote = "xous-kernel@0.9.17"; + let xous_kernel_remote = "xous-kernel@0.9.18"; // ---- extract position independent args ---- let lkey = get_flag("--lkey")?; diff --git a/xtask/src/verifier.rs b/xtask/src/verifier.rs index 1a3ef9e3b..d6eb86821 100644 --- a/xtask/src/verifier.rs +++ b/xtask/src/verifier.rs @@ -11,7 +11,7 @@ pub fn check_project_consistency() -> Result<(), DynError> { let check_pkgs = [ // this set updates with kernel API changes "xous@0.9.23", - "xous-kernel@0.9.17", + "xous-kernel@0.9.18", "xous-ipc@0.9.23", "xous-api-log@0.1.17", "xous-api-names@0.9.18",