diff --git a/apps/system/components/DebugConsole/DebugConsole.camkes b/apps/system/components/DebugConsole/DebugConsole.camkes index ed00500..711eb0b 100644 --- a/apps/system/components/DebugConsole/DebugConsole.camkes +++ b/apps/system/components/DebugConsole/DebugConsole.camkes @@ -31,9 +31,14 @@ component DebugConsole { // Enable KataOS CAmkES support. attribute int kataos = true; - // Add a bunch of free slots for test code to use. - attribute int cnode_headroom = 64; + // Install requires enough slots to hold dynamically allocated + // memory for package contents. Size this to handle up to 4MB + // (at 4KB / page) though that exceeds our target memory config. + attribute int cnode_headroom = 1024; - // Copyregions for loading bundle images. + // Copyregions for zmodem upload and for loading bundle images. + // Could do this with one region since upload never happens + // concurrently with bundle image loading. has copyregion BUNDLE_IMAGE; + has copyregion UPLOAD; } diff --git a/apps/system/components/DebugConsole/kata-debug-console/src/run.rs b/apps/system/components/DebugConsole/kata-debug-console/src/run.rs index ce02020..c4bddba 100644 --- a/apps/system/components/DebugConsole/kata-debug-console/src/run.rs +++ b/apps/system/components/DebugConsole/kata-debug-console/src/run.rs @@ -14,10 +14,21 @@ use kata_io; use kata_os_common::allocator; use kata_os_common::logger::KataLogger; +use kata_os_common::sel4_sys; +use kata_os_common::slot_allocator; use kata_shell; use kata_uart_client; use log::trace; +use sel4_sys::seL4_CPtr; + +use slot_allocator::KATA_CSPACE_SLOTS; + +extern "C" { + static SELF_CNODE_FIRST_SLOT: seL4_CPtr; + static SELF_CNODE_LAST_SLOT: seL4_CPtr; +} + #[no_mangle] pub extern "C" fn pre_init() { static KATA_LOGGER: KataLogger = KataLogger; @@ -36,6 +47,16 @@ pub extern "C" fn pre_init() { HEAP_MEMORY.len() ); } + + unsafe { + KATA_CSPACE_SLOTS.init( + /*first_slot=*/ SELF_CNODE_FIRST_SLOT, + /*size=*/ SELF_CNODE_LAST_SLOT - SELF_CNODE_FIRST_SLOT + ); + trace!("setup cspace slots: first slot {} free {}", + KATA_CSPACE_SLOTS.base_slot(), + KATA_CSPACE_SLOTS.free_slots()); + } } /// Entry point for DebugConsole. Runs the shell with UART IO. diff --git a/apps/system/components/DebugConsole/kata-shell/src/lib.rs b/apps/system/components/DebugConsole/kata-shell/src/lib.rs index 6f1ad37..ad426a8 100644 --- a/apps/system/components/DebugConsole/kata-shell/src/lib.rs +++ b/apps/system/components/DebugConsole/kata-shell/src/lib.rs @@ -13,6 +13,7 @@ use kata_io as io; use kata_line_reader::LineReader; use kata_memory_interface::*; use kata_os_common::sel4_sys; +use kata_os_common::slot_allocator; use kata_proc_interface::kata_pkg_mgmt_install; use kata_proc_interface::kata_pkg_mgmt_uninstall; use kata_proc_interface::kata_proc_ctrl_get_running_bundles; @@ -26,18 +27,26 @@ use kata_timer_interface::timer_service_completed_timers; use kata_timer_interface::timer_service_oneshot; use kata_timer_interface::timer_service_wait; +use sel4_sys::seL4_CNode_Delete; use sel4_sys::seL4_CPtr; use sel4_sys::seL4_MinSchedContextBits; use sel4_sys::seL4_ObjectType::*; use sel4_sys::seL4_WordBits; +use slot_allocator::KATA_CSPACE_SLOTS; + mod rz; +extern "C" { + static SELF_CNODE: seL4_CPtr; +} + /// Error type indicating why a command line is not runnable. enum CommandError { UnknownCommand, BadArgs, IO, + Memory, Formatter(fmt::Error), } @@ -47,6 +56,7 @@ impl fmt::Display for CommandError { CommandError::UnknownCommand => write!(f, "unknown command"), CommandError::BadArgs => write!(f, "invalid arguments"), CommandError::IO => write!(f, "input / output error"), + CommandError::Memory => write!(f, "memory allocation error"), CommandError::Formatter(e) => write!(f, "{}", e), } } @@ -118,7 +128,7 @@ fn dispatch_command(cmdline: &str, input: &mut dyn io::BufRead, output: &mut dyn "kvdelete" => kvdelete_command(&mut args, output), "kvread" => kvread_command(&mut args, output), "kvwrite" => kvwrite_command(&mut args, output), - "install" => install_command(&mut args, output), + "install" => install_command(&mut args, input, output), "loglevel" => loglevel_command(&mut args, output), "malloc" => malloc_command(&mut args, output), "mfree" => mfree_command(&mut args, output), @@ -226,7 +236,7 @@ fn rz_command( writeln!( output, "size: {}, crc32: {}", - upload.contents().len(), + upload.len(), hex::encode(upload.crc32().to_be_bytes()) )?; Ok(()) @@ -281,13 +291,53 @@ fn bundles_command(output: &mut dyn io::Write) -> Result<(), CommandError> { Ok(()) } +fn collect_from_zmodem( + input: &mut dyn io::BufRead, + mut output: &mut dyn io::Write, +) -> Option { + writeln!(output, "Starting zmodem upload...").ok()?; + let mut upload = rz::rz(input, &mut output).ok()?; + upload.finish(); + writeln!(output, "Received {} bytes of data, crc32 {}", + upload.len(), + hex::encode(upload.crc32().to_be_bytes())).ok()?; + Some(upload.frames().clone()) +} + fn install_command( - _args: &mut dyn Iterator, - output: &mut dyn io::Write, + args: &mut dyn Iterator, + input: &mut dyn io::BufRead, + mut output: &mut dyn io::Write, ) -> Result<(), CommandError> { - // TODO(sleffler): supply a real bundle (e.g. from serial) - let pkg_buffer = &[0u8; 64]; - match kata_pkg_mgmt_install(pkg_buffer) { + fn clear_slot(slot: seL4_CPtr) { + unsafe { + KATA_CSPACE_SLOTS.free(slot, 1); + seL4_CNode_Delete(SELF_CNODE, slot, seL4_WordBits as u8) + .expect("install"); + } + } + + // Collect/setup the package frames. If a -z arg is present a zmodem + // upload is used; otherwise we use some raw pages (for testing). + let mut pkg_contents = match args.next() { + Some("-z") => { + collect_from_zmodem(input, &mut output).ok_or(CommandError::IO)? + } + _ => { + // TODO: pattern-fill pages + kata_frame_alloc(8192).map_err(|_| CommandError::IO)? + } + }; + + // The frames are in SELF_CNODE; wrap them in a dynamically allocated + // CNode (as expected by kata_pgk_mgmt_install). + // TODO(sleffler): useful idiom, add to MemoryManager + let cnode_depth = pkg_contents.count_log2(); + let cnode = kata_cnode_alloc(cnode_depth) + .map_err(|_| CommandError::Memory)?; // XXX leaks pkg_contents + pkg_contents.move_objects_from_toplevel(cnode.objs[0].cptr, cnode_depth as u8) + .map_err(|_| CommandError::Memory)?; // XXX leaks pkg_contents + cnode + match kata_pkg_mgmt_install(&pkg_contents) { Ok(bundle_id) => { writeln!(output, "Bundle \"{}\" installed", bundle_id)?; } @@ -295,6 +345,13 @@ fn install_command( writeln!(output, "install failed: {:?}", status)?; } } + + // SecurityCoordinator owns the cnode & frames contained within but we + // still have a cap for the cnode in our top-level CNode; clean it up. + debug_assert!(cnode.cnode == unsafe { SELF_CNODE }); + sel4_sys::debug_assert_slot_cnode!(cnode.objs[0].cptr); + clear_slot(cnode.objs[0].cptr); + Ok(()) } diff --git a/apps/system/components/DebugConsole/kata-shell/src/rz.rs b/apps/system/components/DebugConsole/kata-shell/src/rz.rs index 1f263c4..7f57f4b 100644 --- a/apps/system/components/DebugConsole/kata-shell/src/rz.rs +++ b/apps/system/components/DebugConsole/kata-shell/src/rz.rs @@ -1,40 +1,167 @@ /// Wrapper types for fully-buffered ZMODEM receives. -use alloc::vec::Vec; +// TODO(sleffler): maybe extract the page-at-a-time support to it's own crate + +use alloc::vec; use crc::crc32; use crc::Hasher32; +use core::cmp; +use core::ptr; +use kata_memory_interface::kata_frame_alloc; +use kata_memory_interface::ObjDescBundle; +use kata_os_common::sel4_sys; use log; +use sel4_sys::seL4_CapRights; +use sel4_sys::seL4_CPtr; +use sel4_sys::seL4_PageBits; +use sel4_sys::seL4_WordBits; + +use sel4_sys::seL4_RISCV_Page_Map as seL4_Page_Map; +use sel4_sys::seL4_RISCV_Page_Unmap as seL4_Page_Unmap; +use sel4_sys::seL4_RISCV_VMAttributes::Default_VMAttributes as seL4_Default_VMAttributes; + use zmodem; use kata_io as io; +#[derive(Debug)] +enum UploadError { + PageMapFailed, + PageUnmapFailed, + MallocFailed, +} +impl From for io::Error { + fn from(_err: UploadError) -> io::Error { + io::Error + } +} + +// TODO(sleffler): use ObjDesc::size_bytes +const PAGE_SIZE: usize = 1 << seL4_PageBits; + +extern "C" { + static SELF_CNODE: seL4_CPtr; + static SELF_VSPACE_ROOT: seL4_CPtr; + static mut UPLOAD: [u8; PAGE_SIZE]; +} + pub struct Upload { digest: crc32::Digest, - contents: Vec, + frames: ObjDescBundle, // Page frames + mapped_page: *mut u8, // Currently mapped page frame + mapped_bytes: usize, // Bytes in mapped_frame, 0 =>'s no frame mapped + next_free: usize, // Next available byte in mapped frame } impl Upload { - pub fn new() -> Upload { + pub fn new() -> Self { Upload { digest: crc32::Digest::new(crc32::IEEE), - contents: Vec::new(), + frames: ObjDescBundle::new( + // Collect frames in the top-level CNode for now + unsafe { SELF_CNODE }, seL4_WordBits as u8, + vec![], + ), + mapped_page: unsafe { ptr::addr_of_mut!(UPLOAD[0]) }, + mapped_bytes: 0, // NB: nothing mapped + next_free: 0, } } - pub fn crc32(&self) -> u32 { self.digest.sum32() } + pub fn len(&self) -> usize { + (self.frames.count() * PAGE_SIZE) - (self.mapped_bytes - self.next_free) + } + pub fn finish(&mut self) { + self.unmap_current_frame().expect("finish"); + } + pub fn frames(&self) -> &ObjDescBundle { + &self.frames + } - pub fn contents(&self) -> &[u8] { - self.contents.as_slice() + // Unmap the current page and reset state. + fn unmap_current_frame(&mut self) -> Result<(), UploadError> { + if let Some(frame) = &self.frames.objs.last() { + unsafe { seL4_Page_Unmap(frame.cptr) } + .map_err(|_| UploadError::PageUnmapFailed)?; + } + // Try to combine this frame w/ the previous so large input + // data streams don't generate many singleton ObjDesc's. + self.frames.maybe_combine_last(); + + self.mapped_bytes = 0; + self.next_free = 0; + Ok(()) + } + + // Expand storage and map the new frame into our VSpace. + fn expand_and_map(&mut self) -> Result<(), UploadError> { + let new_page = kata_frame_alloc(PAGE_SIZE) + .map_err(|_| UploadError::MallocFailed)?; + // Verify the new frame is in the same CNode as previous. + assert_eq!(new_page.cnode, self.frames.cnode); + assert_eq!(new_page.depth, self.frames.depth); + self.frames.objs.push(new_page.objs[0]); + + let frame = &self.frames.objs.last().unwrap(); + unsafe { + seL4_Page_Map( + /*sel4_page=*/ frame.cptr, + /*seL4_pd=*/ SELF_VSPACE_ROOT, + /*vaddr=*/ self.mapped_page as usize, + seL4_CapRights::new( + // NB: RW 'cuz W-only silently gets upgraded by kernel + /*grant_reply=*/0, /*grant=*/0, /*read=1*/1, /*write=*/1, + ), + seL4_Default_VMAttributes, + ) + }.map_err(|_| UploadError::PageMapFailed)?; + self.mapped_bytes = PAGE_SIZE; + self.next_free = 0; + Ok(()) + } +} +impl Drop for Upload { + fn drop(&mut self) { + self.finish(); } } impl io::Write for Upload { fn write(&mut self, buf: &[u8]) -> io::Result { - self.digest.write(buf); - self.contents.extend_from_slice(buf); + let mut cursor = buf; + while cursor.len() > 0 { + let available_bytes = self.mapped_bytes - self.next_free; + if available_bytes > 0 { + // Fill the current frame (as space permits). + let region = unsafe { + core::slice::from_raw_parts_mut(self.mapped_page, self.mapped_bytes) + }; + let bytes_to_write = cmp::min(available_bytes, cursor.len()); + unsafe { + ptr::copy_nonoverlapping( + cursor.as_ptr(), + region[self.next_free..].as_mut_ptr(), + bytes_to_write + ) + }; + self.next_free += bytes_to_write; + cursor = &cursor[bytes_to_write..]; + + assert!(self.next_free <= self.mapped_bytes); + if self.next_free == self.mapped_bytes { + // Current frame is full; unmap and prepare for next. + self.unmap_current_frame()?; + } + } + if cursor.len() == 0 { break } + + // Allocate another frame and map it for write. + self.expand_and_map()?; + } + self.digest.write(buf); // Update crc32 calculation Ok(buf.len()) } diff --git a/apps/system/system.camkes b/apps/system/system.camkes index a7e0cd6..dfa16d8 100644 --- a/apps/system/system.camkes +++ b/apps/system/system.camkes @@ -97,9 +97,6 @@ assembly { connection seL4HardwareMMIO vc_dtcm(from ml_coordinator.dtcm, to vctop.dtcm); - connection seL4HardwareMMIO vc_elf(from ml_coordinator.elf_file, - to vc_payload.elf_file); - // TimerService connection seL4HardwareMMIO timer_csr(from timer_service.csr, to timer.csr); @@ -123,7 +120,8 @@ assembly { // component and copies data between components. connection seL4RPCOverMultiSharedData multi_memory( from debug_console.memory, - // TODO(sleffler): from process_manager.memory, + from process_manager.memory, + from security_coordinator.memory, // TOOD(sleffler): from ml_coordinator.memory, to memory_manager.memory);