diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index f37500d21..20be82381 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -15,6 +15,7 @@ xous-kernel = { package = "xous", version = "0.9.47", features = [ "forget-memory-messages", ] } utralib = { version = "0.1.20", optional = true, default_features = false } +rand_chacha = { version = "0.3.1", optional = true, default_features = false } [target.'cfg(any(windows,unix))'.dev-dependencies] xous-kernel = { package = "xous", version = "0.9.47", features = [ @@ -36,6 +37,8 @@ xous-kernel = { package = "xous", version = "0.9.47", features = [ critical-section = "1.1.1" [features] +cramium-soc = ["utralib/cramium-soc"] +cramium-fpga = ["utralib/cramium-fpga"] atsama5d27 = ["utralib/atsama5d27"] precursor = ["utralib/precursor"] hosted = ["utralib/hosted"] @@ -47,6 +50,11 @@ print-panics = [] report-memory = ["stats_alloc"] wrap-print = [] v2p = ["xous-kernel/v2p"] + +# patches for simulation targets ONLY. Applying these flags will result in totally broken security. +hwsim = [] +fake-rng = ["rand_chacha"] # works around lack of a real TRNG in the full-chip sim for now. This should go away for production. + # default = ["print-panics", "debug-print", "wrap-print"] default = ["print-panics"] diff --git a/kernel/src/arch/riscv/mod.rs b/kernel/src/arch/riscv/mod.rs index b4cf15f24..634f1b01a 100644 --- a/kernel/src/arch/riscv/mod.rs +++ b/kernel/src/arch/riscv/mod.rs @@ -12,15 +12,22 @@ pub mod panic; pub use process::Thread; +#[cfg(feature="precursor")] use utralib::generated::*; +#[cfg(feature="precursor")] use xous_kernel::{MemoryFlags, MemoryType, PID}; +#[cfg(feature="precursor")] use crate::mem::MemoryManager; +#[cfg(any(feature="cramium-soc", feature="cramium-fpga"))] +use xous_kernel::PID; +#[cfg(feature="precursor")] pub const WFI_KERNEL: Wfi = Wfi { // the manually chosen virtual address has to be in the top 4MiB as it is the only page shared among all processes base: 0xffcd_0000 as *mut usize, // see https://github.com/betrusted-io/xous-core/blob/master/docs/memory.md }; +#[cfg(feature="precursor")] pub struct Wfi { pub base: *mut usize, } @@ -30,6 +37,7 @@ pub fn current_pid() -> PID { } pub fn init() { + #[cfg(feature="precursor")] MemoryManager::with_mut(|memory_manager| { memory_manager .map_range( @@ -42,7 +50,9 @@ pub fn init() { ) .expect("unable to map WFI") }); + #[cfg(feature="precursor")] let mut wfi_kernel_csr = CSR::new(WFI_KERNEL.base as *mut u32); + #[cfg(feature="precursor")] wfi_kernel_csr.wfo(utra::wfi::IGNORE_LOCKED_IGNORE_LOCKED, 1); unsafe { @@ -54,14 +64,17 @@ pub fn init() { /// Put the core to sleep until an interrupt hits. Returns `true` /// to indicate the kernel should not exit. pub fn idle() -> bool { + #[cfg(feature="precursor")] let mut wfi_kernel_csr = CSR::new(WFI_KERNEL.base as *mut u32); // Issue `wfi`. This will return as soon as an external interrupt // is available. - if false { - // "traditional" path for stopping a clock - effectively a NOP, doesn't do anything - unsafe { riscv::asm::wfi() }; - } else { + #[cfg(any(feature="cramium-fpga", feature="cramium-soc"))] + // "traditional" path for stopping a clock + unsafe { riscv::asm::wfi() }; + + #[cfg(feature="precursor")] + { // this invokes Precusor-SoC specific path to gate clocks: // 1. ignore_locked prevents the chip from going into reset if the PLL goes unlocked wfi_kernel_csr.wfo(utra::wfi::IGNORE_LOCKED_IGNORE_LOCKED, 1); diff --git a/kernel/src/platform/cramium/mod.rs b/kernel/src/platform/cramium/mod.rs new file mode 100644 index 000000000..40ffd80a0 --- /dev/null +++ b/kernel/src/platform/cramium/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/cramium/rand.rs b/kernel/src/platform/cramium/rand.rs new file mode 100644 index 000000000..2b73349c7 --- /dev/null +++ b/kernel/src/platform/cramium/rand.rs @@ -0,0 +1,129 @@ +// SPDX-FileCopyrightText: 2020 Sean Cross +// SPDX-FileCopyrightText: 2022 Foundation Devices, Inc. +// SPDX-License-Identifier: Apache-2.0 + +#[cfg(not(feature="fake-rng"))] +use crate::mem::MemoryManager; +#[cfg(not(feature="fake-rng"))] +use utralib::generated::*; +#[cfg(not(feature="fake-rng"))] +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 +#[cfg(not(feature="fake-rng"))] +pub const TRNG_KERNEL_ADDR: usize = 0xffce_0000; +#[cfg(not(feature="fake-rng"))] +pub static mut TRNG_KERNEL: Option = None; +#[cfg(feature="fake-rng")] +use core::sync::atomic::{AtomicU32, Ordering}; +#[cfg(feature="fake-rng")] +static LOCAL_RNG_STATE: AtomicU32 = AtomicU32::new(2); + +#[cfg(not(feature="fake-rng"))] +pub struct TrngKernel { + pub trng_kernel_csr: CSR, +} + +#[cfg(not(feature="fake-rng"))] +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. +#[cfg(not(feature="fake-rng"))] +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`. +#[cfg(not(feature="fake-rng"))] +pub fn get_u32() -> u32 { + unsafe { + TRNG_KERNEL + .as_mut() + .expect("TRNG_KERNEL driver not initialized") + .get_u32() + } +} + +/// Initialize TRNG driver. +/// +/// Needed so that the kernel can allocate names. +#[cfg(feature="fake-rng")] +pub fn init() { +} + +#[cfg(feature="fake-rng")] +pub fn get_u32() -> u32 { + use rand_chacha::ChaCha8Rng; + use rand_chacha::rand_core::RngCore; + use rand_chacha::rand_core::SeedableRng; + + let mut rng = ChaCha8Rng::seed_from_u64(LOCAL_RNG_STATE.load(Ordering::SeqCst) as u64); + let r = rng.next_u32(); + LOCAL_RNG_STATE.store(rng.next_u64() as u32, Ordering::SeqCst); + r +} diff --git a/kernel/src/platform/cramium/uart.rs b/kernel/src/platform/cramium/uart.rs new file mode 100644 index 000000000..ef599af20 --- /dev/null +++ b/kernel/src/platform/cramium/uart.rs @@ -0,0 +1,169 @@ +// SPDX-FileCopyrightText: 2020 Sean Cross +// SPDX-FileCopyrightText: 2022 Foundation Devices, Inc. +// SPDX-License-Identifier: Apache-2.0 + +#[cfg(feature="cramium-fpga")] +use crate::{ + io::{SerialWrite, SerialRead}, + mem::MemoryManager, + debug::shell::process_characters, + PID, +}; +#[cfg(feature="cramium-fpga")] +use utralib::generated::*; +#[cfg(feature="cramium-fpga")] +use xous_kernel::{MemoryFlags, MemoryType}; + +#[cfg(feature="cramium-soc")] +use crate::{ + io::{SerialWrite, SerialRead}, + debug::shell::process_characters, +}; + +/// UART virtual address. +/// +/// See https://github.com/betrusted-io/xous-core/blob/master/docs/memory.md +pub const UART_ADDR: usize = 0xffcf_0000; +pub const IRQ0_ADDR: usize = UART_ADDR + 0x1000; + +/// UART instance. +/// +/// Initialized by [`init`]. +pub static mut UART: Option = None; + +/// UART peripheral driver. +#[cfg(feature="cramium-fpga")] +pub struct Uart { + uart_csr: CSR, + irq_csr: CSR, + callback: fn(&mut Self), +} + +#[cfg(feature="cramium-fpga")] +impl Uart { + pub fn new(addr: usize, irq_addr: usize, callback: fn(&mut Self)) -> Uart { + Uart { + uart_csr: CSR::new(addr as *mut u32), + irq_csr: CSR::new(irq_addr as *mut u32), + callback, + } + } + + pub fn init(&mut self) { + self.uart_csr.rmwf(utra::uart::EV_ENABLE_RX, 1); + self.irq_csr.rmwf(utra::irqarray0::EV_ENABLE_SOURCE0, 1); + } + + pub fn irq(_irq_number: usize, arg: *mut usize) { + let uart = unsafe { &mut *(arg as *mut Uart) }; + (uart.callback)(uart); + // uart.acknowledge_irq(); + } +} + +#[cfg(feature="cramium-fpga")] +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); + } +} + +#[cfg(feature="cramium-fpga")] +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); + self.irq_csr.wfo(utra::irqarray0::EV_PENDING_SOURCE0, 1); + ret + } + } + } +} + +/// Initialize UART driver and debug shell. +#[cfg(feature="cramium-fpga")] +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") + }); + // Map the IRQ0 handler + MemoryManager::with_mut(|memory_manager| { + memory_manager + .map_range( + utra::irqarray0::HW_IRQARRAY0_BASE as *mut u8, + (IRQ0_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, IRQ0_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"); + } +} + +#[cfg(feature="cramium-soc")] +pub fn init() { // there is no kernel UART yet...just a placeholder function + let uart = Uart::new(UART_ADDR, IRQ0_ADDR, process_characters); + unsafe{ + UART = Some(uart); + crate::debug::shell::init(UART.as_mut().unwrap()); + } +} + +#[cfg(feature="cramium-soc")] +pub struct Uart { +} + +#[cfg(feature="cramium-soc")] +impl Uart { + pub fn new(_addr: usize, _irq_addr: usize, _callback: fn(&mut Self)) -> Uart { + Uart { + } + } +} + +#[cfg(feature="cramium-soc")] +impl SerialWrite for Uart { + fn putc(&mut self, _c: u8) { + } +} + +#[cfg(feature="cramium-soc")] +impl SerialRead for Uart { + fn getc(&mut self) -> Option { + None + } +} \ No newline at end of file diff --git a/kernel/src/platform/mod.rs b/kernel/src/platform/mod.rs index 4d59ee072..f4c957235 100644 --- a/kernel/src/platform/mod.rs +++ b/kernel/src/platform/mod.rs @@ -7,6 +7,12 @@ pub mod precursor; #[cfg(any(feature = "atsama5d27"))] pub mod atsama5d2; +#[cfg(any(any(feature="cramium-soc", feature="cramium-fpga")))] +pub mod cramium; + +#[cfg(any(any(feature="cramium-soc", feature="cramium-fpga")))] +pub use cramium::rand; +#[cfg(not(any(feature="cramium-soc", feature="cramium-fpga")))] pub mod rand; /// Platform specific initialization. @@ -17,4 +23,7 @@ pub fn init() { #[cfg(any(feature = "atsama5d27"))] self::atsama5d2::init(); + + #[cfg(any(feature="cramium-fpga", feature="cramium-soc"))] + self::cramium::init(); }