kernel integrations for cramium

This commit is contained in:
bunnie 2023-07-17 18:49:21 +08:00
parent ed7bdf2589
commit 3c13516d57
6 changed files with 345 additions and 4 deletions

View file

@ -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"]

View file

@ -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);

View file

@ -0,0 +1,13 @@
// SPDX-FileCopyrightText: 2022 Foundation Devices, Inc. <hello@foundationdevices.com>
// 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();
}

View file

@ -0,0 +1,129 @@
// SPDX-FileCopyrightText: 2020 Sean Cross <sean@xobs.io>
// SPDX-FileCopyrightText: 2022 Foundation Devices, Inc. <hello@foundationdevices.com>
// 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<TrngKernel> = 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<u32>,
}
#[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
}

View file

@ -0,0 +1,169 @@
// SPDX-FileCopyrightText: 2020 Sean Cross <sean@xobs.io>
// SPDX-FileCopyrightText: 2022 Foundation Devices, Inc. <hello@foundationdevices.com>
// 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<Uart> = None;
/// UART peripheral driver.
#[cfg(feature="cramium-fpga")]
pub struct Uart {
uart_csr: CSR<u32>,
irq_csr: CSR<u32>,
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<u8> {
// 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<u8> {
None
}
}

View file

@ -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();
}