From 1bac53686ce8d72b6fc588f5f3d4e60235ebe8d9 Mon Sep 17 00:00:00 2001 From: bunnie Date: Thu, 2 May 2024 22:18:01 +0800 Subject: [PATCH] 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 --- services/xous-swapper/src/main.rs | 242 +++++++++++++++--------------- 1 file changed, 124 insertions(+), 118 deletions(-) diff --git a/services/xous-swapper/src/main.rs b/services/xous-swapper/src/main.rs index 975d7cae9..52e7f3589 100644 --- a/services/xous-swapper/src/main.rs +++ b/services/xous-swapper/src/main.rs @@ -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, -} +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, } /// 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 = 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 = 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); } } }