mirror of
https://github.com/saymrwulf/xous-core.git
synced 2026-11-15 08:30:02 +00:00
big refactor of the swapper
- debug UART is now a static object. Less safe, kinda gross, but gets us debug access at any time! Probably fine, because once we turn off the feature all taht ugliness goes away. - use memory mapped structures for initialization of the swapper state
This commit is contained in:
parent
433cc7dfb7
commit
1bac53686c
1 changed files with 124 additions and 118 deletions
|
|
@ -1,51 +1,26 @@
|
|||
use core::fmt::{Error, Write};
|
||||
mod platform;
|
||||
|
||||
use core::fmt::{Error, Write};
|
||||
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
use loader::swap::{SwapSpec, SWAP_CFG_VADDR, SWAP_PT_VADDR, SWAP_RPT_VADDR};
|
||||
use num_traits::FromPrimitive;
|
||||
use platform::SwapMac;
|
||||
use utralib::*;
|
||||
|
||||
/// This trait defines a set of functions to get and receive MACs (message
|
||||
/// authentication codes, also referred to as the tag in AES-GCM-SIV.
|
||||
///
|
||||
/// The implementation of the MAC table depends on the details of the hardware;
|
||||
/// we cannot always guarantee that the MACs are memory mapped, as they could
|
||||
/// be stored in an off-chip SPI RAM that is accessible only through a register
|
||||
/// interface.
|
||||
pub trait SmtAccessor {
|
||||
/// Lookup the MAC corresponding to a given page in swap. Offsets are
|
||||
/// relative to the base of the swap region, and are given in units of pages, not bytes.
|
||||
fn lookup_mac(swap_page_offset: usize) -> [u8; 16];
|
||||
/// Store a MAC for a given page in swap.
|
||||
fn store_mac(swap_page_offset: usize, mac: &[u8; 16]);
|
||||
static DUART_CSR_PA: AtomicUsize = AtomicUsize::new(0);
|
||||
|
||||
pub struct PtPage {
|
||||
pub entries: [u32; 1024],
|
||||
}
|
||||
|
||||
/// An array of pointers to the SATPs (root page table) of all the processes.
|
||||
/// An array of pointers to the root page tables of all the processes.
|
||||
pub struct SwapPageTables {
|
||||
satps: &'static mut [usize],
|
||||
pub roots: &'static mut [PtPage],
|
||||
}
|
||||
/// This is an implementation for SMTs that are memory mapped. Directly mapped
|
||||
/// tables are just as lice of MACs
|
||||
pub struct SwapMacTableMemMap {
|
||||
macs: &'static mut [[u8; 16]],
|
||||
}
|
||||
impl SmtAccessor for SwapMacTableMemMap {
|
||||
fn lookup_mac(swap_page_offset: usize) -> [u8; 16] { todo!() }
|
||||
|
||||
fn store_mac(swap_page_offset: usize, mac: &[u8; 16]) { todo!() }
|
||||
}
|
||||
/// This is an implementation for SMTs that are accessible only through a SPI
|
||||
/// register interface. The base and bounds must be translated to SPI accesses
|
||||
/// in a hardware-specific manner.
|
||||
pub struct SwapMacTableSpi {
|
||||
base: usize,
|
||||
bounds: usize,
|
||||
}
|
||||
impl SmtAccessor for SwapMacTableSpi {
|
||||
fn lookup_mac(swap_page_offset: usize) -> [u8; 16] { todo!() }
|
||||
|
||||
fn store_mac(swap_page_offset: usize, mac: &[u8; 16]) { todo!() }
|
||||
}
|
||||
pub struct RuntimePageTracker {
|
||||
allocs: &'static mut [u8],
|
||||
pub allocs: &'static mut [u8],
|
||||
}
|
||||
|
||||
#[derive(Debug, num_derive::FromPrimitive, num_derive::ToPrimitive)]
|
||||
|
|
@ -61,34 +36,29 @@ pub enum Opcode {
|
|||
EvalTrim = 256,
|
||||
}
|
||||
|
||||
pub struct DebugUart {
|
||||
#[cfg(feature = "debug-print")]
|
||||
csr: CSR<u32>,
|
||||
}
|
||||
pub struct DebugUart {}
|
||||
impl DebugUart {
|
||||
#[cfg(feature = "debug-print")]
|
||||
pub fn new() -> Self {
|
||||
let debug_uart_mem = xous::syscall::map_memory(
|
||||
xous::MemoryAddress::new(utra::app_uart::HW_APP_UART_BASE),
|
||||
None,
|
||||
4096,
|
||||
xous::MemoryFlags::R | xous::MemoryFlags::W,
|
||||
)
|
||||
.expect("couldn't claim the debug UART");
|
||||
let debug_uart = CSR::new(debug_uart_mem.as_mut_ptr() as *mut u32);
|
||||
|
||||
Self { csr: debug_uart }
|
||||
}
|
||||
|
||||
#[cfg(feature = "debug-print")]
|
||||
pub fn putc(&mut self, c: u8) {
|
||||
// Wait until TXFULL is `0`
|
||||
while self.csr.r(utra::app_uart::TXFULL) != 0 {}
|
||||
self.csr.wfo(utra::app_uart::RXTX_RXTX, c as u32);
|
||||
}
|
||||
let mut csr_ptr = DUART_CSR_PA.load(Ordering::SeqCst);
|
||||
// map it if it doesn't exist
|
||||
if csr_ptr == 0 {
|
||||
let debug_uart_mem = xous::syscall::map_memory(
|
||||
xous::MemoryAddress::new(utra::app_uart::HW_APP_UART_BASE),
|
||||
None,
|
||||
4096,
|
||||
xous::MemoryFlags::R | xous::MemoryFlags::W,
|
||||
)
|
||||
.expect("couldn't claim the debug UART");
|
||||
DUART_CSR_PA.store(csr_ptr, Ordering::SeqCst);
|
||||
csr_ptr = debug_uart_mem.as_ptr() as usize;
|
||||
}
|
||||
let mut csr = CSR::new(csr_ptr as *mut u32);
|
||||
|
||||
#[cfg(not(feature = "debug-print"))]
|
||||
pub fn new() -> Self { Self {} }
|
||||
// Wait until TXFULL is `0`
|
||||
while csr.r(utra::app_uart::TXFULL) != 0 {}
|
||||
csr.wfo(utra::app_uart::RXTX_RXTX, c as u32);
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "debug-print"))]
|
||||
pub fn putc(&self, _c: u8) {}
|
||||
|
|
@ -106,10 +76,13 @@ impl Write for DebugUart {
|
|||
/// This structure contains shared state accessible between the userspace code and the blocking swap call
|
||||
/// handler.
|
||||
struct SwapperSharedState {
|
||||
duart: DebugUart,
|
||||
pub key: [u8; 32],
|
||||
pub pts: SwapPageTables,
|
||||
pub macs: SwapMac,
|
||||
pub rpt: RuntimePageTracker,
|
||||
}
|
||||
impl SwapperSharedState {
|
||||
pub fn new() -> Self { Self { duart: DebugUart::new() } }
|
||||
struct SharedStateStorage {
|
||||
pub inner: Option<SwapperSharedState>,
|
||||
}
|
||||
|
||||
/// blocking swap call handler
|
||||
|
|
@ -127,13 +100,58 @@ fn swap_handler(
|
|||
a7: usize,
|
||||
) {
|
||||
// safety: lots of footguns actually, but this is the only way to get this pointer into
|
||||
// our context. SwapperSharedState is a Rust structure that is aligned and initialized,
|
||||
// our context. SharedStateStorage is a Rust structure that is aligned and initialized,
|
||||
// so the cast is safe enough, but we have to be careful because this is executed in an
|
||||
// interrupt context: we can't wait on locks (they'll hang forever if they are locked).
|
||||
let ss = unsafe { &mut *(shared_state as *mut SwapperSharedState) };
|
||||
let sss = unsafe { &mut *(shared_state as *mut SharedStateStorage) };
|
||||
if sss.inner.is_none() {
|
||||
// Unearth all of our data trackers in the spots on the map where the loader should have buried them.
|
||||
|
||||
// safety: this is only safe because the loader initializes and aligns the SwapSpec structure:
|
||||
// - The SwapSpec structure is Repr(C), page-aligned, and fully initialized.
|
||||
// - Furthermore, SWAP_CFG_VADDR is already mapped into our address space by the loader; we don't
|
||||
// have to do mapping requests because it's already done for us!
|
||||
let swap_spec = unsafe { &*(SWAP_CFG_VADDR as *mut SwapSpec) };
|
||||
|
||||
// extract our key
|
||||
let mut key = [0u8; 32];
|
||||
key.copy_from_slice(&swap_spec.key);
|
||||
// swapper is not allowed to use `log` for debugging under most circumstances, because
|
||||
// the swapper can't send messages when handling a swap call. Instead, we use a local
|
||||
// debug UART to handle this. This needs to be enabled with the "debug-print" feature
|
||||
// and is mutually exclusive with the "gdb-stub" feature in the kernel since it uses
|
||||
// the same physical hardware.
|
||||
sss.inner = Some(SwapperSharedState {
|
||||
key,
|
||||
// safety: this is only safe because:
|
||||
// - the loader puts the swap root page table pages starting at SWAP_PT_VADDR
|
||||
// - all the page table entries are fully initialized and contains only representable data
|
||||
// - the length of the region is guaranteed by the loader
|
||||
pts: SwapPageTables {
|
||||
roots: unsafe {
|
||||
core::slice::from_raw_parts_mut(
|
||||
SWAP_PT_VADDR as *mut PtPage,
|
||||
swap_spec.pid_count as usize,
|
||||
)
|
||||
},
|
||||
},
|
||||
macs: SwapMac::new(swap_spec.mac_base as usize, swap_spec.mac_len as usize),
|
||||
// safety: this is only safe because the loader guarantees this raw pointer is initialized and
|
||||
// aligned correctly
|
||||
rpt: RuntimePageTracker {
|
||||
allocs: unsafe {
|
||||
core::slice::from_raw_parts_mut(
|
||||
SWAP_RPT_VADDR as *mut u8,
|
||||
swap_spec.rpt_len_bytes as usize,
|
||||
)
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
let ss = sss.inner.as_mut().expect("Shared state should be initialized");
|
||||
|
||||
let op: Option<Opcode> = FromPrimitive::from_usize(opcode);
|
||||
writeln!(ss.duart, "got Opcode: {:?}", op).ok();
|
||||
writeln!(DebugUart {}, "got Opcode: {:?}", op).ok();
|
||||
match op {
|
||||
Some(Opcode::WriteToSwap) => {
|
||||
let pid = a2 as u8;
|
||||
|
|
@ -144,6 +162,16 @@ fn swap_handler(
|
|||
let pid = a2 as u8;
|
||||
let vaddr_in_pid = a3;
|
||||
let vaddr_in_swap = a4;
|
||||
writeln!(
|
||||
DebugUart {},
|
||||
"rfs PID{}, vaddr_pid {:x}, vaddr_swap {:x}",
|
||||
pid,
|
||||
vaddr_in_pid,
|
||||
vaddr_in_swap
|
||||
)
|
||||
.ok();
|
||||
// walk the PT to find the swap data
|
||||
// ss.pts[pid as usize]
|
||||
}
|
||||
Some(Opcode::AllocateAdvisory) => {
|
||||
let advisories = [
|
||||
|
|
@ -153,82 +181,60 @@ fn swap_handler(
|
|||
];
|
||||
}
|
||||
_ => {
|
||||
writeln!(ss.duart, "Unimplemented or unknown opcode: {}", opcode).ok();
|
||||
writeln!(DebugUart {}, "Unimplemented or unknown opcode: {}", opcode).ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// swapper is not allowed to use `log` for debugging under most circumstances, because
|
||||
// the swapper can't send messages when handling a swap call. Instead, we use a local
|
||||
// debug UART to handle this. This needs to be enabled with the "debug-print" feature
|
||||
// and is mutually exclusive with the "gdb-stub" feature in the kernel since it uses
|
||||
// the same physical hardware.
|
||||
let mut ss = SwapperSharedState::new();
|
||||
|
||||
let sid = xous::create_server().unwrap();
|
||||
let mut sss = SharedStateStorage { inner: None };
|
||||
// Register the swapper with the kernel. Written as a raw syscall, since this is
|
||||
// the only instance of its use (no point in use-once code to wrap it).
|
||||
// This is an "early registration" which allows us to see debug output quickly,
|
||||
// even before we can constitute all of our shared state
|
||||
let (s0, s1, s2, s3) = sid.to_u32();
|
||||
let (spt_init, smt_base_init, smt_bounds_init, rpt_init) =
|
||||
xous::rsyscall(xous::SysCall::RegisterSwapper(
|
||||
s0,
|
||||
s1,
|
||||
s2,
|
||||
s3,
|
||||
swap_handler as *mut usize as usize,
|
||||
&mut ss as *mut SwapperSharedState as usize,
|
||||
))
|
||||
.and_then(|result| {
|
||||
if let xous::Result::Scalar5(spt, smt_base, smt_bounds, rpt, _) = result {
|
||||
Ok((spt, smt_base, smt_bounds, rpt))
|
||||
} else {
|
||||
panic!("Failed to register swapper");
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
// safety: this is only safe because the loader guarantees this raw pointer is initialized and aligned
|
||||
// correctly
|
||||
let spt = unsafe { &mut *(spt_init as *mut SwapPageTables) };
|
||||
#[cfg(feature = "precursor")]
|
||||
let smt = SwapMacTableMemMap {
|
||||
// safety: this is only safe because the loader guarantees memory-mapped SMT is initialized and
|
||||
// aligned and properly mapped into the swapper's memory space.
|
||||
macs: unsafe { core::slice::from_raw_parts_mut(smt_base_init as *mut [u8; 16], smt_bounds_init) },
|
||||
};
|
||||
#[cfg(feature = "cramium-soc")]
|
||||
let smt = SwapMacTableSpi { base: smt_base_init, bounds: smt_bounds_init };
|
||||
// safety: this is only safe because the loader guarantees this raw pointer is initialized and aligned
|
||||
// correctly
|
||||
let rpt = unsafe { &mut *(rpt_init as *mut RuntimePageTracker) };
|
||||
|
||||
writeln!(
|
||||
ss.duart,
|
||||
"Swap params: spt {:x}, base {:x}, bounds: {:x}, rpt: {:x}",
|
||||
spt_init, smt_base_init, smt_bounds_init, rpt_init
|
||||
)
|
||||
.ok();
|
||||
xous::rsyscall(xous::SysCall::RegisterSwapper(
|
||||
s0,
|
||||
s1,
|
||||
s2,
|
||||
s3,
|
||||
swap_handler as *mut usize as usize,
|
||||
&mut sss as *mut SharedStateStorage as usize,
|
||||
))
|
||||
.unwrap();
|
||||
|
||||
// init the log, but this is mostly unused.
|
||||
log_server::init_wait().unwrap();
|
||||
log::set_max_level(log::LevelFilter::Info);
|
||||
log::info!("my PID is {}", xous::process::id());
|
||||
// test the debug serial port
|
||||
writeln!(ss.duart, "Swapper started.").ok();
|
||||
|
||||
// check that we got called by the kernel -- this should happen really early in the process
|
||||
// due to the advisory allocs that start piling up very quickly
|
||||
if let Some(ss) = sss.inner {
|
||||
log::info!(
|
||||
"Swap params: key: {:x?}, root: {:x}, rpt: {:?}",
|
||||
ss.key,
|
||||
ss.pts.roots[0].entries[0],
|
||||
&ss.rpt.allocs[..8]
|
||||
);
|
||||
} else {
|
||||
log::info!("Swapper did not get called to initialize its shared state.")
|
||||
}
|
||||
|
||||
let mut msg_opt = None;
|
||||
loop {
|
||||
xous::reply_and_receive_next(sid, &mut msg_opt).unwrap();
|
||||
let msg = msg_opt.as_mut().unwrap();
|
||||
let op: Option<Opcode> = FromPrimitive::from_usize(msg.body.id());
|
||||
writeln!(ss.duart, "Swapper got {:?}", msg).ok();
|
||||
log::info!("Swapper got {:?}", msg);
|
||||
match op {
|
||||
Some(Opcode::WriteToSwap) => {
|
||||
unimplemented!();
|
||||
}
|
||||
// ... todo, other opcodes.
|
||||
_ => {
|
||||
write!(ss.duart, "Unknown opcode {:?}", op).ok();
|
||||
log::info!("Unknown opcode {:?}", op);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue