DebugConsole: hookup zmodem to MemoryManager

- change zmodem uploads to get memory from the MemoryManager
- add a "-z" option to the "install" command to start a zmodem upload
  to generate the package contents to send to ProcessManager
- increase CNode headroom for loading package contents

Change-Id: I5f329cdd044368e5568ad891245d67a4a13f8468
GitOrigin-RevId: 2853cde48cb8232f3ba75fe7e8efdbd3032bcb66
This commit is contained in:
Sam Leffler
2022-05-11 00:46:58 +00:00
parent 09ed791c1e
commit 0a51bd8b59
5 changed files with 231 additions and 23 deletions

View File

@@ -31,9 +31,14 @@ component DebugConsole {
// Enable KataOS CAmkES support. // Enable KataOS CAmkES support.
attribute int kataos = true; attribute int kataos = true;
// Add a bunch of free slots for test code to use. // Install requires enough slots to hold dynamically allocated
attribute int cnode_headroom = 64; // 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 BUNDLE_IMAGE;
has copyregion UPLOAD;
} }

View File

@@ -14,10 +14,21 @@
use kata_io; use kata_io;
use kata_os_common::allocator; use kata_os_common::allocator;
use kata_os_common::logger::KataLogger; use kata_os_common::logger::KataLogger;
use kata_os_common::sel4_sys;
use kata_os_common::slot_allocator;
use kata_shell; use kata_shell;
use kata_uart_client; use kata_uart_client;
use log::trace; 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] #[no_mangle]
pub extern "C" fn pre_init() { pub extern "C" fn pre_init() {
static KATA_LOGGER: KataLogger = KataLogger; static KATA_LOGGER: KataLogger = KataLogger;
@@ -36,6 +47,16 @@ pub extern "C" fn pre_init() {
HEAP_MEMORY.len() 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. /// Entry point for DebugConsole. Runs the shell with UART IO.

View File

@@ -13,6 +13,7 @@ use kata_io as io;
use kata_line_reader::LineReader; use kata_line_reader::LineReader;
use kata_memory_interface::*; use kata_memory_interface::*;
use kata_os_common::sel4_sys; 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_install;
use kata_proc_interface::kata_pkg_mgmt_uninstall; use kata_proc_interface::kata_pkg_mgmt_uninstall;
use kata_proc_interface::kata_proc_ctrl_get_running_bundles; 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_oneshot;
use kata_timer_interface::timer_service_wait; use kata_timer_interface::timer_service_wait;
use sel4_sys::seL4_CNode_Delete;
use sel4_sys::seL4_CPtr; use sel4_sys::seL4_CPtr;
use sel4_sys::seL4_MinSchedContextBits; use sel4_sys::seL4_MinSchedContextBits;
use sel4_sys::seL4_ObjectType::*; use sel4_sys::seL4_ObjectType::*;
use sel4_sys::seL4_WordBits; use sel4_sys::seL4_WordBits;
use slot_allocator::KATA_CSPACE_SLOTS;
mod rz; mod rz;
extern "C" {
static SELF_CNODE: seL4_CPtr;
}
/// Error type indicating why a command line is not runnable. /// Error type indicating why a command line is not runnable.
enum CommandError { enum CommandError {
UnknownCommand, UnknownCommand,
BadArgs, BadArgs,
IO, IO,
Memory,
Formatter(fmt::Error), Formatter(fmt::Error),
} }
@@ -47,6 +56,7 @@ impl fmt::Display for CommandError {
CommandError::UnknownCommand => write!(f, "unknown command"), CommandError::UnknownCommand => write!(f, "unknown command"),
CommandError::BadArgs => write!(f, "invalid arguments"), CommandError::BadArgs => write!(f, "invalid arguments"),
CommandError::IO => write!(f, "input / output error"), CommandError::IO => write!(f, "input / output error"),
CommandError::Memory => write!(f, "memory allocation error"),
CommandError::Formatter(e) => write!(f, "{}", e), 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), "kvdelete" => kvdelete_command(&mut args, output),
"kvread" => kvread_command(&mut args, output), "kvread" => kvread_command(&mut args, output),
"kvwrite" => kvwrite_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), "loglevel" => loglevel_command(&mut args, output),
"malloc" => malloc_command(&mut args, output), "malloc" => malloc_command(&mut args, output),
"mfree" => mfree_command(&mut args, output), "mfree" => mfree_command(&mut args, output),
@@ -226,7 +236,7 @@ fn rz_command(
writeln!( writeln!(
output, output,
"size: {}, crc32: {}", "size: {}, crc32: {}",
upload.contents().len(), upload.len(),
hex::encode(upload.crc32().to_be_bytes()) hex::encode(upload.crc32().to_be_bytes())
)?; )?;
Ok(()) Ok(())
@@ -281,13 +291,53 @@ fn bundles_command(output: &mut dyn io::Write) -> Result<(), CommandError> {
Ok(()) Ok(())
} }
fn collect_from_zmodem(
input: &mut dyn io::BufRead,
mut output: &mut dyn io::Write,
) -> Option<ObjDescBundle> {
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( fn install_command(
_args: &mut dyn Iterator<Item = &str>, args: &mut dyn Iterator<Item = &str>,
output: &mut dyn io::Write, input: &mut dyn io::BufRead,
mut output: &mut dyn io::Write,
) -> Result<(), CommandError> { ) -> Result<(), CommandError> {
// TODO(sleffler): supply a real bundle (e.g. from serial) fn clear_slot(slot: seL4_CPtr) {
let pkg_buffer = &[0u8; 64]; unsafe {
match kata_pkg_mgmt_install(pkg_buffer) { 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) => { Ok(bundle_id) => {
writeln!(output, "Bundle \"{}\" installed", bundle_id)?; writeln!(output, "Bundle \"{}\" installed", bundle_id)?;
} }
@@ -295,6 +345,13 @@ fn install_command(
writeln!(output, "install failed: {:?}", status)?; 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(()) Ok(())
} }

View File

@@ -1,40 +1,167 @@
/// Wrapper types for fully-buffered ZMODEM receives. /// 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::crc32;
use crc::Hasher32; 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 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 zmodem;
use kata_io as io; use kata_io as io;
#[derive(Debug)]
enum UploadError {
PageMapFailed,
PageUnmapFailed,
MallocFailed,
}
impl From<UploadError> 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 { pub struct Upload {
digest: crc32::Digest, digest: crc32::Digest,
contents: Vec<u8>, 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 { impl Upload {
pub fn new() -> Upload { pub fn new() -> Self {
Upload { Upload {
digest: crc32::Digest::new(crc32::IEEE), 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 { pub fn crc32(&self) -> u32 {
self.digest.sum32() 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] { // Unmap the current page and reset state.
self.contents.as_slice() 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 { impl io::Write for Upload {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> { fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.digest.write(buf); let mut cursor = buf;
self.contents.extend_from_slice(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()) Ok(buf.len())
} }

View File

@@ -97,9 +97,6 @@ assembly {
connection seL4HardwareMMIO vc_dtcm(from ml_coordinator.dtcm, connection seL4HardwareMMIO vc_dtcm(from ml_coordinator.dtcm,
to vctop.dtcm); to vctop.dtcm);
connection seL4HardwareMMIO vc_elf(from ml_coordinator.elf_file,
to vc_payload.elf_file);
// TimerService // TimerService
connection seL4HardwareMMIO timer_csr(from timer_service.csr, connection seL4HardwareMMIO timer_csr(from timer_service.csr,
to timer.csr); to timer.csr);
@@ -123,7 +120,8 @@ assembly {
// component and copies data between components. // component and copies data between components.
connection seL4RPCOverMultiSharedData multi_memory( connection seL4RPCOverMultiSharedData multi_memory(
from debug_console.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, // TOOD(sleffler): from ml_coordinator.memory,
to memory_manager.memory); to memory_manager.memory);