Merge "kata-shell; overhaul command processing"

GitOrigin-RevId: 6fe5b9078cd47083ae834e81482576272f7b9b7e
This commit is contained in:
Sam Leffler 2022-06-08 16:35:43 +00:00
parent 6d8badf052
commit 6850f3b3e0
8 changed files with 674 additions and 462 deletions

View File

@ -11,13 +11,28 @@ build = "build.rs"
sel4-config = { path = "../../kata-os-common/src/sel4-config" }
[features]
default = []
default = [
"TEST_GLOBAL_ALLOCATOR",
"TEST_MEMORY_MANAGER",
"TEST_ML_COORDINATOR",
"TEST_PANIC",
"TEST_TIMER_SERVICE",
]
CONFIG_DEBUG_BUILD = []
CONFIG_KERNEL_MCS = []
# Commands that are likely not useful
FRINGE_CMDS = []
# Runtime tests for various services (please keep sorted)
TEST_GLOBAL_ALLOCATOR = []
TEST_MEMORY_MANAGER = []
TEST_ML_COORDINATOR = []
TEST_PANIC = []
TEST_TIMER_SERVICE = []
[dependencies]
crc = { version = "1.4.0", default_features = false }
cpio = { git = "https://github.com/rcore-os/cpio" }
hashbrown = { version = "0.11", features = ["ahash-compile-time-rng"] }
hex = { version = "0.4.3", default-features = false, features = ["alloc"] }
kata-io = { path = "../kata-io" }
kata-line-reader = { path = "../kata-line-reader" }

View File

@ -0,0 +1,94 @@
// Infrequently used shell commands
extern crate alloc;
use alloc::vec::Vec;
use core::fmt::Write;
use crate::CmdFn;
use crate::CommandError;
use crate::rz;
use kata_io as io;
use kata_security_interface::kata_security_echo;
pub fn add_cmds(cmds: &mut HashMap::<&str, CmdFn>) {
cmds.extend([
("add", add_command as CmdFn),
("echo", echo_command as CmdFn),
("clear", clear_command as CmdFn),
("rz", rz_command as CmdFn),
("scecho", scecho_command as CmdFn),
]);
}
/// Implements a binary float addition command.
///
/// This is a toy to demonstrate that the CLI can operate on some very basic
/// dynamic input and that the Rust runtime provides floating point arithmetic
/// on integer-only hardware. It is also a prototype example of "command taking
/// arguments." It should be removed once actually useful system control
/// commands are implemented and done cribbing from it.
fn add_command(
args: &mut dyn Iterator<Item = &str>,
_input: &mut dyn io::BufRead,
output: &mut dyn io::Write,
_builtin_cpio: &[u8],
) -> Result<(), CommandError> {
let x_str = args.next().ok_or(CommandError::BadArgs)?;
let x = x_str.parse::<f32>()?;
let y_str = args.next().ok_or(CommandError::BadArgs)?;
let y = y_str.parse::<f32>()?;
return Ok(writeln!(output, "{}", x + y)?);
}
/// Implements a command that outputs the ANSI "clear console" sequence.
fn clear_command(
_args: &mut dyn Iterator<Item = &str>,
_input: &mut dyn io::BufRead,
output: &mut dyn io::Write,
_builtin_cpio: &[u8],
) -> Result<(), CommandError> {
Ok(output.write_str("\x1b\x63")?)
}
/// Implements an "echo" command which writes its arguments to output.
fn echo_command(
args: &mut dyn Iterator<Item = &str>,
_input: &mut dyn io::BufRead,
output: &mut dyn io::Write,
_builtin_cpio: &[u8],
) -> Result<(), CommandError> {
let value = args.collect::<Vec<&str>>().join(" ");
Ok(writeln!(output, "{}", &value)?)
}
/// Implements an "scecho" command that sends arguments to the Security Core's echo service.
fn scecho_command(
args: &mut dyn Iterator<Item = &str>,
_input: &mut dyn io::BufRead,
output: &mut dyn io::Write,
_builtin_cpio: &[u8],
) -> Result<(), CommandError> {
let request = args.collect::<Vec<&str>>().join(" ");
match kata_security_echo(&request) {
Ok(result) => writeln!(output, "{}", result)?,
Err(status) => writeln!(output, "ECHO replied {:?}", status)?,
}
Ok(())
}
/// Implements a command to receive a blob using ZMODEM.
fn rz_command(
_args: &mut dyn Iterator<Item = &str>,
input: &mut dyn io::BufRead,
mut output: &mut dyn io::Write,
_builtin_cpio: &[u8],
) -> Result<(), CommandError> {
let upload = rz::rz(input, &mut output)?;
writeln!(
output,
"size: {}, crc32: {}",
upload.len(),
hex::encode(upload.crc32().to_be_bytes())
)?;
Ok(())
}

View File

@ -1,11 +1,11 @@
#![no_std]
extern crate alloc;
use alloc::vec;
use alloc::vec::Vec;
use core::fmt;
use core::fmt::Write;
use cpio::CpioNewcReader;
use hashbrown::HashMap;
use kata_io as io;
use kata_line_reader::LineReader;
@ -18,31 +18,37 @@ 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_start;
use kata_proc_interface::kata_proc_ctrl_stop;
use kata_security_interface::kata_security_echo;
use kata_storage_interface::kata_storage_delete;
use kata_storage_interface::kata_storage_read;
use kata_storage_interface::kata_storage_write;
use kata_timer_interface::timer_service_completed_timers;
use kata_timer_interface::TimerServiceError;
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;
#[cfg(feature = "FRINGE_CMDS")]
mod fringe_cmds;
#[cfg(feature = "TEST_GLOBAL_ALLOCATOR")]
mod test_global_allocator;
#[cfg(feature = "TEST_MEMORY_MANAGER")]
mod test_memory_manager;
#[cfg(feature = "TEST_ML_COORDINATOR")]
mod test_ml_coordinator;
#[cfg(feature = "TEST_PANIC")]
mod test_panic;
#[cfg(feature = "TEST_TIMER_SERVICE")]
mod test_timer_service;
extern "C" {
static SELF_CNODE: seL4_CPtr;
}
/// Error type indicating why a command line is not runnable.
enum CommandError {
pub enum CommandError {
UnknownCommand,
BadArgs,
IO,
@ -92,95 +98,86 @@ impl From<io::Error> for CommandError {
}
}
type CmdFn = fn(
args: &mut dyn Iterator<Item = &str>,
input: &mut dyn io::BufRead,
output: &mut dyn io::Write,
builtin_cpio: &[u8]
) -> Result<(), CommandError>;
/// Read-eval-print loop for the DebugConsole command line interface.
pub fn repl<T: io::BufRead>(
output: &mut dyn io::Write,
input: &mut T,
builtin_cpio: &[u8],
) -> ! {
let mut cmds = HashMap::<&str, CmdFn>::new();
cmds.extend([
("builtins", builtins_command as CmdFn),
("bundles", bundles_command as CmdFn),
("kvdelete", kvdelete_command as CmdFn),
("kvread", kvread_command as CmdFn),
("kvwrite", kvwrite_command as CmdFn),
("install", install_command as CmdFn),
("loglevel", loglevel_command as CmdFn),
("mstats", mstats_command as CmdFn),
("ps", ps_command as CmdFn),
("start", start_command as CmdFn),
("stop", stop_command as CmdFn),
("uninstall", uninstall_command as CmdFn),
("state_mlcoord", state_mlcoord_command as CmdFn),
]);
#[cfg(feature = "FRINGE_CMDS")]
fringe_cmds::add_cmds(&mut cmds);
#[cfg(feature = "TEST_GLOBAL_ALLOCATOR")]
test_global_allocator::add_cmds(&mut cmds);
#[cfg(feature = "TEST_MEMORY_MANAGER")]
test_memory_manager::add_cmds(&mut cmds);
#[cfg(feature = "TEST_ML_COORDINATOR")]
test_ml_coordinator::add_cmds(&mut cmds);
#[cfg(feature = "TEST_PANIC")]
test_panic::add_cmds(&mut cmds);
#[cfg(feature = "TEST_TIMER_SERVICE")]
test_timer_service::add_cmds(&mut cmds);
let mut line_reader = LineReader::new();
loop {
const PROMPT: &str = "KATA> ";
let _ = output.write_str(PROMPT);
match line_reader.read_line(output, input) {
Ok(cmdline) => dispatch_command(cmdline, input, output, builtin_cpio),
Err(e) => {
let _ = writeln!(output, "\n{}", e);
Ok(cmdline) => {
let mut args = cmdline.split_ascii_whitespace();
match args.next() {
Some("?") | Some("help") => {
let mut keys: Vec<&str> = cmds.keys().copied().collect();
keys.sort();
for k in keys {
let _ = writeln!(output, "{}", k);
}
}
Some(cmd) => {
let result = cmds.get(cmd).map_or_else(
|| Err(CommandError::UnknownCommand),
|func| func(&mut args, input, output, builtin_cpio));
if let Err(e) = result {
let _ = writeln!(output, "{}", e);
};
}
None => { let _ = output.write_str("\n"); },
}
}
Err(e) => { let _ = writeln!(output, "\n{}", e); },
}
}
}
/// Runs a command line.
///
/// The line is split on whitespace. The first token is the command; the
/// remaining tokens are the arguments.
fn dispatch_command(
cmdline: &str,
input: &mut dyn io::BufRead,
output: &mut dyn io::Write,
builtin_cpio: &[u8],
) {
let mut args = cmdline.split_ascii_whitespace();
match args.next() {
Some(command) => {
// Statically binds command names to implementations fns, which are
// defined below.
//
// Since even the binding is static, it is fine for each command
// implementation to use its own preferred signature.
let result = match command {
"add" => add_command(&mut args, output),
"builtins" => builtins_command(&mut args, builtin_cpio, output),
"echo" => echo_command(cmdline, output),
"clear" => clear_command(output),
"bundles" => bundles_command(output),
"kvdelete" => kvdelete_command(&mut args, output),
"kvread" => kvread_command(&mut args, output),
"kvwrite" => kvwrite_command(&mut args, output),
"install" => install_command(&mut args, builtin_cpio, input, output),
"loglevel" => loglevel_command(&mut args, output),
"malloc" => malloc_command(&mut args, output),
"mfree" => mfree_command(&mut args, output),
"mstats" => mstats_command(&mut args, output),
"rz" => rz_command(input, output),
"ps" => ps_command(output),
"scecho" => scecho_command(cmdline, output),
"start" => start_command(&mut args, output),
"stop" => stop_command(&mut args, output),
"uninstall" => uninstall_command(&mut args, output),
"test_alloc" => test_alloc_command(output),
"test_alloc_error" => test_alloc_error_command(output),
"test_bootinfo" => test_bootinfo_command(output),
"test_mlcancel" => test_mlcancel_command(&mut args, output),
"test_mlexecute" => test_mlexecute_command(&mut args, output),
"test_mlperiodic" => test_mlperiodic_command(&mut args, output),
"test_obj_alloc" => test_obj_alloc_command(output),
"test_panic" => test_panic_command(),
"test_timer_async" => test_timer_async_command(&mut args, output),
"test_timer_blocking" => test_timer_blocking_command(&mut args, output),
"test_timer_completed" => test_timer_completed_command(output),
"state_mlcoord" => state_mlcoord_command(),
_ => Err(CommandError::UnknownCommand),
};
if let Err(e) = result {
let _ = writeln!(output, "{}", e);
};
}
None => {
let _ = output.write_str("\n");
}
};
}
/// Implements a "builtins" command that lists the contents of the built-in cpio archive.
fn builtins_command(
_args: &mut dyn Iterator<Item = &str>,
builtin_cpio: &[u8],
_input: &mut dyn io::BufRead,
output: &mut dyn io::Write,
builtin_cpio: &[u8],
) -> Result<(), CommandError> {
for e in CpioNewcReader::new(builtin_cpio) {
if e.is_err() {
@ -193,34 +190,12 @@ fn builtins_command(
Ok(())
}
/// Implements an "echo" command which writes its arguments to output.
fn echo_command(cmdline: &str, output: &mut dyn io::Write) -> Result<(), CommandError> {
const COMMAND_LENGTH: usize = 5; // "echo "
if cmdline.len() < COMMAND_LENGTH {
Ok(())
} else {
Ok(writeln!(
output,
"{}",
&cmdline[COMMAND_LENGTH..cmdline.len()]
)?)
}
}
/// Implements an "scecho" command that sends arguments to the Security Core's echo service.
fn scecho_command(cmdline: &str, output: &mut dyn io::Write) -> Result<(), CommandError> {
let (_, request) = cmdline.split_at(7); // 'scecho'
match kata_security_echo(request) {
Ok(result) => writeln!(output, "{}", result)?,
Err(status) => writeln!(output, "ECHO replied {:?}", status)?,
}
Ok(())
}
/// Implements a command to configure the max log level for the DebugConsole.
fn loglevel_command(
args: &mut dyn Iterator<Item = &str>,
_input: &mut dyn io::BufRead,
output: &mut dyn io::Write,
_builtin_cpio: &[u8],
) -> Result<(), CommandError> {
if let Some(level) = args.next() {
use log::LevelFilter;
@ -237,59 +212,30 @@ fn loglevel_command(
Ok(writeln!(output, "{}", log::max_level())?)
}
/// Implements a command to receive a blob using ZMODEM.
fn rz_command(
input: &mut dyn io::BufRead,
mut output: &mut dyn io::Write,
) -> Result<(), CommandError> {
let upload = rz::rz(input, &mut output)?;
writeln!(
output,
"size: {}, crc32: {}",
upload.len(),
hex::encode(upload.crc32().to_be_bytes())
)?;
Ok(())
}
/// Implements a "ps" command that dumps seL4 scheduler state to the console.
#[cfg(feature = "CONFIG_DEBUG_BUILD")]
fn ps_command(_output: &mut dyn io::Write) -> Result<(), CommandError> {
#[allow(unused_variables)]
fn ps_command(
_args: &mut dyn Iterator<Item = &str>,
_input: &mut dyn io::BufRead,
output: &mut dyn io::Write,
_builtin_cpio: &[u8],
) -> Result<(), CommandError> {
#[cfg(feature = "CONFIG_DEBUG_BUILD")]
unsafe {
kata_os_common::sel4_sys::seL4_DebugDumpScheduler();
Ok(())
}
Ok(())
}
#[cfg(not(feature = "CONFIG_DEBUG_BUILD"))]
fn ps_command(output: &mut dyn io::Write) -> Result<(), CommandError> {
#[cfg(not(feature = "CONFIG_DEBUG_BUILD"))]
Ok(writeln!(output, "Kernel support not configured!")?)
}
/// Implements a binary float addition command.
///
/// This is a toy to demonstrate that the CLI can operate on some very basic
/// dynamic input and that the Rust runtime provides floating point arithmetic
/// on integer-only hardware. It is also a prototype example of "command taking
/// arguments." It should be removed once actually useful system control
/// commands are implemented and done cribbing from it.
fn add_command(
args: &mut dyn Iterator<Item = &str>,
fn bundles_command(
_args: &mut dyn Iterator<Item = &str>,
_input: &mut dyn io::BufRead,
output: &mut dyn io::Write,
_builtin_cpio: &[u8],
) -> Result<(), CommandError> {
let x_str = args.next().ok_or(CommandError::BadArgs)?;
let x = x_str.parse::<f32>()?;
let y_str = args.next().ok_or(CommandError::BadArgs)?;
let y = y_str.parse::<f32>()?;
return Ok(writeln!(output, "{}", x + y)?);
}
/// Implements a command that outputs the ANSI "clear console" sequence.
fn clear_command(output: &mut dyn io::Write) -> Result<(), CommandError> {
Ok(output.write_str("\x1b\x63")?)
}
fn bundles_command(output: &mut dyn io::Write) -> Result<(), CommandError> {
match kata_proc_ctrl_get_running_bundles() {
Ok(bundle_ids) => {
writeln!(output, "{}", bundle_ids.join("\n"))?;
@ -343,9 +289,9 @@ fn collect_from_zmodem(
fn install_command(
args: &mut dyn Iterator<Item = &str>,
builtin_cpio: &[u8],
input: &mut dyn io::BufRead,
mut output: &mut dyn io::Write,
builtin_cpio: &[u8],
) -> Result<(), CommandError> {
fn clear_slot(slot: seL4_CPtr) {
unsafe {
@ -399,7 +345,9 @@ fn install_command(
fn uninstall_command(
args: &mut dyn Iterator<Item = &str>,
_input: &mut dyn io::BufRead,
output: &mut dyn io::Write,
_builtin_cpio: &[u8],
) -> Result<(), CommandError> {
let bundle_id = args.next().ok_or(CommandError::BadArgs)?;
match kata_pkg_mgmt_uninstall(bundle_id) {
@ -415,7 +363,9 @@ fn uninstall_command(
fn start_command(
args: &mut dyn Iterator<Item = &str>,
_input: &mut dyn io::BufRead,
output: &mut dyn io::Write,
_builtin_cpio: &[u8],
) -> Result<(), CommandError> {
let bundle_id = args.next().ok_or(CommandError::BadArgs)?;
match kata_proc_ctrl_start(bundle_id) {
@ -431,7 +381,9 @@ fn start_command(
fn stop_command(
args: &mut dyn Iterator<Item = &str>,
_input: &mut dyn io::BufRead,
output: &mut dyn io::Write,
_builtin_cpio: &[u8],
) -> Result<(), CommandError> {
let bundle_id = args.next().ok_or(CommandError::BadArgs)?;
match kata_proc_ctrl_stop(bundle_id) {
@ -447,7 +399,9 @@ fn stop_command(
fn kvdelete_command(
args: &mut dyn Iterator<Item = &str>,
_input: &mut dyn io::BufRead,
output: &mut dyn io::Write,
_builtin_cpio: &[u8],
) -> Result<(), CommandError> {
let key = args.next().ok_or(CommandError::BadArgs)?;
match kata_storage_delete(key) {
@ -463,7 +417,9 @@ fn kvdelete_command(
fn kvread_command(
args: &mut dyn Iterator<Item = &str>,
_input: &mut dyn io::BufRead,
output: &mut dyn io::Write,
_builtin_cpio: &[u8],
) -> Result<(), CommandError> {
let key = args.next().ok_or(CommandError::BadArgs)?;
match kata_storage_read(key) {
@ -479,7 +435,9 @@ fn kvread_command(
fn kvwrite_command(
args: &mut dyn Iterator<Item = &str>,
_input: &mut dyn io::BufRead,
output: &mut dyn io::Write,
_builtin_cpio: &[u8],
) -> Result<(), CommandError> {
let key = args.next().ok_or(CommandError::BadArgs)?;
let value = args.collect::<Vec<&str>>().join(" ");
@ -494,52 +452,6 @@ fn kvwrite_command(
Ok(())
}
fn malloc_command(
args: &mut dyn Iterator<Item = &str>,
output: &mut dyn io::Write,
) -> Result<(), CommandError> {
let space_str = args.next().ok_or(CommandError::BadArgs)?;
let space_bytes = space_str.parse::<usize>()?;
match kata_frame_alloc(space_bytes) {
Ok(frames) => {
writeln!(output, "Allocated {:?}", frames)?;
}
Err(status) => {
writeln!(output, "malloc failed: {:?}", status)?;
}
}
Ok(())
}
fn mfree_command(
args: &mut dyn Iterator<Item = &str>,
output: &mut dyn io::Write,
) -> Result<(), CommandError> {
extern "C" { static SELF_CNODE: seL4_CPtr; }
let cptr_str = args.next().ok_or(CommandError::BadArgs)?;
let count_str = args.next().ok_or(CommandError::BadArgs)?;
let frames = ObjDescBundle::new(
unsafe { SELF_CNODE },
seL4_WordBits as u8,
vec![
ObjDesc::new(
sel4_sys::seL4_RISCV_4K_Page,
count_str.parse::<usize>()?,
cptr_str.parse::<usize>()? as seL4_CPtr,
),
],
);
match kata_object_free_toplevel(&frames) {
Ok(_) => {
writeln!(output, "Free'd {:?}", frames)?;
}
Err(status) => {
writeln!(output, "mfree failed: {:?}", status)?;
}
}
Ok(())
}
fn mstats(output: &mut dyn io::Write, stats: &MemoryManagerStats)
-> Result<(), CommandError>
{
@ -556,7 +468,9 @@ fn mstats(output: &mut dyn io::Write, stats: &MemoryManagerStats)
fn mstats_command(
_args: &mut dyn Iterator<Item = &str>,
_input: &mut dyn io::BufRead,
output: &mut dyn io::Write,
_builtin_cpio: &[u8],
) -> Result<(), CommandError> {
match kata_memory_stats() {
Ok(stats) => { mstats(output, &stats)?; }
@ -565,275 +479,11 @@ fn mstats_command(
Ok(())
}
/// Implements a command that tests facilities that use the global allocator.
/// Shamelessly cribbed from https://os.phil-opp.com/heap-allocation/
fn test_alloc_command(output: &mut dyn io::Write) -> Result<(), CommandError> {
extern crate alloc;
use alloc::{boxed::Box, rc::Rc};
// allocate a number on the heap
let heap_value = Box::new(41);
writeln!(output, "heap_value at {:p}", heap_value).expect("Box failed");
// create a dynamically sized vector
let mut vec = Vec::new();
for i in 0..500 {
vec.push(i);
}
writeln!(output, "vec at {:p}", vec.as_slice()).expect("Vec failed");
// create a reference counted vector -> will be freed when count reaches 0
let reference_counted = Rc::new(vec![1, 2, 3]);
let cloned_reference = reference_counted.clone();
writeln!(
output,
"current reference count is {}",
Rc::strong_count(&cloned_reference)
)
.expect("Rc 1 failed");
core::mem::drop(reference_counted);
writeln!(
output,
"reference count is {} now",
Rc::strong_count(&cloned_reference)
)
.expect("Rc 2 failed");
Ok(writeln!(output, "All tests passed!")?)
}
/// Implements a command that tests the global allocator error handling.
fn test_alloc_error_command(output: &mut dyn io::Write) -> Result<(), CommandError> {
// Default heap holds 16KB.
let mut vec = Vec::with_capacity(16384);
for i in 0..16348 {
vec.push(i);
}
Ok(writeln!(output, "vec at {:p}", vec.as_slice())?)
}
fn test_bootinfo_command(output: &mut dyn io::Write) -> Result<(), CommandError> {
use kata_os_common::sel4_sys::seL4_BootInfo;
extern "C" {
fn sel4runtime_bootinfo() -> *const seL4_BootInfo;
}
let bootinfo_ref = unsafe { &*sel4runtime_bootinfo() };
writeln!(output, "{}:{} empty slots {}:{} untyped",
bootinfo_ref.empty.start, bootinfo_ref.empty.end,
bootinfo_ref.untyped.start, bootinfo_ref.untyped.end)?;
// NB: seL4_DebugCapIdentify is only available in debug builds
#[cfg(feature = "CONFIG_DEBUG_BUILD")]
for ut in bootinfo_ref.untyped.start..bootinfo_ref.untyped.end {
let cap_tag = unsafe { kata_os_common::sel4_sys::seL4_DebugCapIdentify(ut) };
assert_eq!(cap_tag, 2,
"expected untyped (2), got {} for cap at {}", cap_tag, ut);
}
Ok(())
}
/// Implements a command that tests panic handling.
fn test_panic_command() -> Result<(), CommandError> {
panic!("testing");
}
/// Implements a command that cancels an ML execution.
fn test_mlcancel_command(
args: &mut dyn Iterator<Item = &str>,
output: &mut dyn io::Write,
fn state_mlcoord_command(
_args: &mut dyn Iterator<Item = &str>,
_input: &mut dyn io::BufRead,
_output: &mut dyn io::Write,
_builtin_cpio: &[u8],
) -> Result<(), CommandError> {
let bundle_id = args.next().ok_or(CommandError::BadArgs)?;
let model_id = args.next().ok_or(CommandError::BadArgs)?;
if let Err(e) = kata_mlcoord_cancel(bundle_id, model_id) {
writeln!(output, "Cancel {:?} {:?} err: {:?}", bundle_id, model_id, e)?;
} else {
writeln!(output, "Cancelled {:?} {:?}", bundle_id, model_id)?;
}
Ok(())
}
/// Implements a command that runs a oneshot ML execution.
fn test_mlexecute_command(
args: &mut dyn Iterator<Item = &str>,
output: &mut dyn io::Write,
) -> Result<(), CommandError> {
let bundle_id = args.next().ok_or(CommandError::BadArgs)?;
let model_id = args.next().ok_or(CommandError::BadArgs)?;
if let Err(e) = kata_mlcoord_oneshot(bundle_id, model_id) {
writeln!(output, "Execute {:?} {:?} err: {:?}", bundle_id, model_id, e)?;
}
Ok(())
}
/// Implements a command that runs a periodic ML execution.
fn test_mlperiodic_command(
args: &mut dyn Iterator<Item = &str>,
output: &mut dyn io::Write,
) -> Result<(), CommandError> {
let bundle_id = args.next().ok_or(CommandError::BadArgs)?;
let model_id = args.next().ok_or(CommandError::BadArgs)?;
let rate_str = args.next().ok_or(CommandError::BadArgs)?;
let rate_in_ms = rate_str.parse::<u32>()?;
if let Err(e) = kata_mlcoord_periodic(bundle_id, model_id, rate_in_ms) {
writeln!(output, "Periodic {:?} {:?} err: {:?}", bundle_id, model_id, e)?;
}
Ok(())
}
fn test_obj_alloc_command(output: &mut dyn io::Write) -> Result<(), CommandError> {
let before_stats = kata_memory_stats().expect("before stats");
mstats(output, &before_stats)?;
fn check_alloc(output: &mut dyn io::Write,
name: &str,
res: Result<ObjDescBundle, MemoryManagerError>) {
match res {
Ok(obj) => {
if let Err(e) = kata_object_free_toplevel(&obj) {
let _ = writeln!(output, "free {} {:?} failed: {:?}", name, obj, e);
}
}
Err(e) => {
let _ = writeln!(output, "alloc {} failed: {:?}", name, e);
}
}
}
// NB: alloc+free immediately so we don't run out of top-level CNode slots
check_alloc(output, "untyped", kata_untyped_alloc(12)); // NB: 4KB
check_alloc(output, "tcb", kata_tcb_alloc());
check_alloc(output, "endpoint", kata_endpoint_alloc());
check_alloc(output, "notification", kata_notification_alloc());
check_alloc(output, "cnode", kata_cnode_alloc(5)); // NB: 32 slots
check_alloc(output, "frame", kata_frame_alloc(4096));
// check_alloc(output, "large frame", kata_frame_alloc(1024*1024));
check_alloc(output, "page table", kata_page_table_alloc());
#[cfg(feature = "CONFIG_KERNEL_MCS")]
check_alloc(output, "sched context",
kata_sched_context_alloc(seL4_MinSchedContextBits));
#[cfg(feature = "CONFIG_KERNEL_MCS")]
check_alloc(output, "reply", kata_reply_alloc());
let after_stats = kata_memory_stats().expect("after stats");
mstats(output, &after_stats)?;
assert_eq!(before_stats.allocated_bytes, after_stats.allocated_bytes);
assert_eq!(before_stats.free_bytes, after_stats.free_bytes);
// Batch allocate into a private CNode as we might to build a process.
const CNODE_DEPTH: usize = 7; // 128 slots
let cnode = kata_cnode_alloc(CNODE_DEPTH).unwrap(); // XXX handle error
let objs = ObjDescBundle::new(
cnode.objs[0].cptr,
CNODE_DEPTH as u8,
vec![
ObjDesc::new(seL4_TCBObject, 1, 0), // 1 tcb
ObjDesc::new(seL4_EndpointObject, 2, 1), // 2 endpoiints
ObjDesc::new(seL4_ReplyObject, 2, 3), // 2 replys
ObjDesc::new(seL4_SchedContextObject, // 1 sched context
seL4_MinSchedContextBits, 5),
ObjDesc::new(seL4_RISCV_4K_Page, 10, 6), // 10 4K pages
],
);
match kata_object_alloc(&objs) {
Ok(_) => {
writeln!(output, "Batch alloc ok: {:?}", objs)?;
if let Err(e) = kata_object_free(&objs) {
writeln!(output, "Batch free err: {:?}", e)?;
}
}
Err(e) => {
writeln!(output, "Batch alloc err: {:?} {:?}", objs, e)?;
}
}
if let Err(e) = kata_object_free_toplevel(&cnode) {
writeln!(output, "Cnode free err: {:?} {:?}", cnode, e)?;
}
// Batch allocate using the newer api that constructs a CNode based
// on the batch of objects specified.
match kata_object_alloc_in_cnode(
vec![
ObjDesc::new(seL4_TCBObject, 1, 0), // 1 tcb
ObjDesc::new(seL4_EndpointObject, 1, 1), // 1 endpoiints
ObjDesc::new(seL4_ReplyObject, 1, 2), // 1 replys
ObjDesc::new(seL4_SchedContextObject, // 1 sched context
seL4_MinSchedContextBits, 3),
ObjDesc::new(seL4_RISCV_4K_Page, 2, 4), // 2 4K pages
],
) {
Ok(objs) => {
writeln!(output, "kata_object_alloc_in_cnode ok: {:?}", objs)?;
if let Err(e) = kata_object_free_in_cnode(&objs) {
writeln!(output, "kata_object_free_in_cnode failed: {:?}", e)?;
}
}
Err(e) => {
writeln!(output, "kata_object_alloc_in_cnode failed: {:?}", e)?;
}
}
Ok(writeln!(output, "All tests passed!")?)
}
/// Implements a command that starts a timer, but does not wait on the
/// notification.
fn test_timer_async_command(
args: &mut dyn Iterator<Item = &str>,
output: &mut dyn io::Write,
) -> Result<(), CommandError> {
let id_str = args.next().ok_or(CommandError::BadArgs)?;
let id = id_str.parse::<u32>()?;
let time_str = args.next().ok_or(CommandError::BadArgs)?;
let time_ms = time_str.parse::<u32>()?;
writeln!(output, "Starting timer {} for {} ms.", id, time_ms)?;
match timer_service_oneshot(id, time_ms) {
TimerServiceError::TimerOk => (),
_ => return Err(CommandError::BadArgs),
}
timer_service_oneshot(id, time_ms);
Ok(())
}
/// Implements a command that starts a timer, blocking until the timer has
/// completed.
fn test_timer_blocking_command(
args: &mut dyn Iterator<Item = &str>,
output: &mut dyn io::Write,
) -> Result<(), CommandError> {
let time_str = args.next().ok_or(CommandError::BadArgs)?;
let time_ms = time_str.parse::<u32>()?;
writeln!(output, "Blocking {} ms waiting for timer.", time_ms)?;
// Set timer_id to 0, we don't need to use multiple timers here.
match timer_service_oneshot(0, time_ms) {
TimerServiceError::TimerOk => (),
_ => return Err(CommandError::BadArgs),
}
timer_service_wait();
return Ok(writeln!(output, "Timer completed.")?);
}
/// Implements a command that checks the completed timers.
fn test_timer_completed_command(
output: &mut dyn io::Write,
) -> Result<(), CommandError> {
return Ok(writeln!(output, "Timers completed: {:#032b}", timer_service_completed_timers())?);
}
fn state_mlcoord_command() -> Result<(), CommandError> {
return Ok(kata_mlcoord_debug_state());
}

View File

@ -0,0 +1,75 @@
// Global allocator shell test commands
extern crate alloc;
use alloc::vec;
use alloc::vec::Vec;
use core::fmt::Write;
use crate::CmdFn;
use crate::CommandError;
use crate::HashMap;
use kata_io as io;
pub fn add_cmds(cmds: &mut HashMap::<&str, CmdFn>) {
cmds.extend([
("test_alloc", alloc_command as CmdFn),
("test_alloc_error", alloc_error_command as CmdFn),
]);
}
/// Implements a command that tests facilities that use the global allocator.
/// Shamelessly cribbed from https://os.phil-opp.com/heap-allocation/
fn alloc_command(
_args: &mut dyn Iterator<Item = &str>,
_input: &mut dyn io::BufRead,
output: &mut dyn io::Write,
_builtin_cpio: &[u8],
) -> Result<(), CommandError> {
extern crate alloc;
use alloc::{boxed::Box, rc::Rc};
// allocate a number on the heap
let heap_value = Box::new(41);
writeln!(output, "heap_value at {:p}", heap_value).expect("Box failed");
// create a dynamically sized vector
let mut vec = Vec::new();
for i in 0..500 {
vec.push(i);
}
writeln!(output, "vec at {:p}", vec.as_slice()).expect("Vec failed");
// create a reference counted vector -> will be freed when count reaches 0
let reference_counted = Rc::new(vec![1, 2, 3]);
let cloned_reference = reference_counted.clone();
writeln!(
output,
"current reference count is {}",
Rc::strong_count(&cloned_reference)
)
.expect("Rc 1 failed");
core::mem::drop(reference_counted);
writeln!(
output,
"reference count is {} now",
Rc::strong_count(&cloned_reference)
)
.expect("Rc 2 failed");
Ok(writeln!(output, "All tests passed!")?)
}
/// Implements a command that tests the global allocator error handling.
fn alloc_error_command(
_args: &mut dyn Iterator<Item = &str>,
_input: &mut dyn io::BufRead,
output: &mut dyn io::Write,
_builtin_cpio: &[u8],
) -> Result<(), CommandError> {
// Default heap holds 16KB.
let mut vec = Vec::with_capacity(16384);
for i in 0..16384 {
vec.push(i);
}
Ok(writeln!(output, "vec at {:p}", vec.as_slice())?)
}

View File

@ -0,0 +1,204 @@
// MemoryManager service shell test commands
extern crate alloc;
use alloc::vec;
use core::fmt::Write;
use crate::CmdFn;
use crate::CommandError;
use crate::HashMap;
use crate::mstats;
use kata_io as io;
use kata_memory_interface::*;
use kata_os_common::sel4_sys;
use sel4_sys::seL4_CPtr;
use sel4_sys::seL4_MinSchedContextBits;
use sel4_sys::seL4_ObjectType::*;
use sel4_sys::seL4_WordBits;
pub fn add_cmds(cmds: &mut HashMap::<&str, CmdFn>) {
cmds.extend([
("test_bootinfo", bootinfo_command as CmdFn),
("test_malloc", malloc_command as CmdFn),
("test_mfree", mfree_command as CmdFn),
("test_obj_alloc", obj_alloc_command as CmdFn),
]);
}
fn bootinfo_command(
_args: &mut dyn Iterator<Item = &str>,
_input: &mut dyn io::BufRead,
output: &mut dyn io::Write,
_builtin_cpio: &[u8],
) -> Result<(), CommandError> {
use kata_os_common::sel4_sys::seL4_BootInfo;
extern "C" {
fn sel4runtime_bootinfo() -> *const seL4_BootInfo;
}
let bootinfo_ref = unsafe { &*sel4runtime_bootinfo() };
writeln!(output, "{}:{} empty slots {}:{} untyped",
bootinfo_ref.empty.start, bootinfo_ref.empty.end,
bootinfo_ref.untyped.start, bootinfo_ref.untyped.end)?;
// NB: seL4_DebugCapIdentify is only available in debug builds
#[cfg(feature = "CONFIG_DEBUG_BUILD")]
for ut in bootinfo_ref.untyped.start..bootinfo_ref.untyped.end {
let cap_tag = unsafe { kata_os_common::sel4_sys::seL4_DebugCapIdentify(ut) };
assert_eq!(cap_tag, 2,
"expected untyped (2), got {} for cap at {}", cap_tag, ut);
}
Ok(())
}
fn malloc_command(
args: &mut dyn Iterator<Item = &str>,
_input: &mut dyn io::BufRead,
output: &mut dyn io::Write,
_builtin_cpio: &[u8],
) -> Result<(), CommandError> {
let space_str = args.next().ok_or(CommandError::BadArgs)?;
let space_bytes = space_str.parse::<usize>()?;
match kata_frame_alloc(space_bytes) {
Ok(frames) => {
writeln!(output, "Allocated {:?}", frames)?;
}
Err(status) => {
writeln!(output, "malloc failed: {:?}", status)?;
}
}
Ok(())
}
fn mfree_command(
args: &mut dyn Iterator<Item = &str>,
_input: &mut dyn io::BufRead,
output: &mut dyn io::Write,
_builtin_cpio: &[u8],
) -> Result<(), CommandError> {
extern "C" { static SELF_CNODE: seL4_CPtr; }
let cptr_str = args.next().ok_or(CommandError::BadArgs)?;
let count_str = args.next().ok_or(CommandError::BadArgs)?;
let frames = ObjDescBundle::new(
unsafe { SELF_CNODE },
seL4_WordBits as u8,
vec![
ObjDesc::new(
sel4_sys::seL4_RISCV_4K_Page,
count_str.parse::<usize>()?,
cptr_str.parse::<usize>()? as seL4_CPtr,
),
],
);
match kata_object_free_toplevel(&frames) {
Ok(_) => {
writeln!(output, "Free'd {:?}", frames)?;
}
Err(status) => {
writeln!(output, "mfree failed: {:?}", status)?;
}
}
Ok(())
}
fn obj_alloc_command(
_args: &mut dyn Iterator<Item = &str>,
_input: &mut dyn io::BufRead,
output: &mut dyn io::Write,
_builtin_cpio: &[u8],
) -> Result<(), CommandError> {
let before_stats = kata_memory_stats().expect("before stats");
mstats(output, &before_stats)?;
fn check_alloc(output: &mut dyn io::Write,
name: &str,
res: Result<ObjDescBundle, MemoryManagerError>) {
match res {
Ok(obj) => {
if let Err(e) = kata_object_free_toplevel(&obj) {
let _ = writeln!(output, "free {} {:?} failed: {:?}", name, obj, e);
}
}
Err(e) => {
let _ = writeln!(output, "alloc {} failed: {:?}", name, e);
}
}
}
// NB: alloc+free immediately so we don't run out of top-level CNode slots
check_alloc(output, "untyped", kata_untyped_alloc(12)); // NB: 4KB
check_alloc(output, "tcb", kata_tcb_alloc());
check_alloc(output, "endpoint", kata_endpoint_alloc());
check_alloc(output, "notification", kata_notification_alloc());
check_alloc(output, "cnode", kata_cnode_alloc(5)); // NB: 32 slots
check_alloc(output, "frame", kata_frame_alloc(4096));
// check_alloc(output, "large frame", kata_frame_alloc(1024*1024));
check_alloc(output, "page table", kata_page_table_alloc());
#[cfg(feature = "CONFIG_KERNEL_MCS")]
check_alloc(output, "sched context",
kata_sched_context_alloc(seL4_MinSchedContextBits));
#[cfg(feature = "CONFIG_KERNEL_MCS")]
check_alloc(output, "reply", kata_reply_alloc());
let after_stats = kata_memory_stats().expect("after stats");
mstats(output, &after_stats)?;
assert_eq!(before_stats.allocated_bytes, after_stats.allocated_bytes);
assert_eq!(before_stats.free_bytes, after_stats.free_bytes);
// Batch allocate into a private CNode as we might to build a process.
const CNODE_DEPTH: usize = 7; // 128 slots
let cnode = kata_cnode_alloc(CNODE_DEPTH).unwrap(); // XXX handle error
let objs = ObjDescBundle::new(
cnode.objs[0].cptr,
CNODE_DEPTH as u8,
vec![
ObjDesc::new(seL4_TCBObject, 1, 0), // 1 tcb
ObjDesc::new(seL4_EndpointObject, 2, 1), // 2 endpoiints
ObjDesc::new(seL4_ReplyObject, 2, 3), // 2 replys
ObjDesc::new(seL4_SchedContextObject, // 1 sched context
seL4_MinSchedContextBits, 5),
ObjDesc::new(seL4_RISCV_4K_Page, 10, 6), // 10 4K pages
],
);
match kata_object_alloc(&objs) {
Ok(_) => {
writeln!(output, "Batch alloc ok: {:?}", objs)?;
if let Err(e) = kata_object_free(&objs) {
writeln!(output, "Batch free err: {:?}", e)?;
}
}
Err(e) => {
writeln!(output, "Batch alloc err: {:?} {:?}", objs, e)?;
}
}
if let Err(e) = kata_object_free_toplevel(&cnode) {
writeln!(output, "Cnode free err: {:?} {:?}", cnode, e)?;
}
// Batch allocate using the newer api that constructs a CNode based
// on the batch of objects specified.
match kata_object_alloc_in_cnode(
vec![
ObjDesc::new(seL4_TCBObject, 1, 0), // 1 tcb
ObjDesc::new(seL4_EndpointObject, 1, 1), // 1 endpoiints
ObjDesc::new(seL4_ReplyObject, 1, 2), // 1 replys
ObjDesc::new(seL4_SchedContextObject, // 1 sched context
seL4_MinSchedContextBits, 3),
ObjDesc::new(seL4_RISCV_4K_Page, 2, 4), // 2 4K pages
],
) {
Ok(objs) => {
writeln!(output, "kata_object_alloc_in_cnode ok: {:?}", objs)?;
if let Err(e) = kata_object_free_in_cnode(&objs) {
writeln!(output, "kata_object_free_in_cnode failed: {:?}", e)?;
}
}
Err(e) => {
writeln!(output, "kata_object_alloc_in_cnode failed: {:?}", e)?;
}
}
Ok(writeln!(output, "All tests passed!")?)
}

View File

@ -0,0 +1,71 @@
// MlCoordinator service shell test commands
use core::fmt::Write;
use crate::CmdFn;
use crate::CommandError;
use crate::HashMap;
use kata_io as io;
use kata_ml_interface::*;
pub fn add_cmds(cmds: &mut HashMap::<&str, CmdFn>) {
cmds.extend([
("test_mlcancel", mlcancel_command as CmdFn),
("test_mlexecute", mlexecute_command as CmdFn),
("test_mlperiodic", mlperiodic_command as CmdFn),
]);
}
/// Implements a command that cancels an ML execution.
fn mlcancel_command(
args: &mut dyn Iterator<Item = &str>,
_input: &mut dyn io::BufRead,
output: &mut dyn io::Write,
_builtin_cpio: &[u8],
) -> Result<(), CommandError> {
let bundle_id = args.next().ok_or(CommandError::BadArgs)?;
let model_id = args.next().ok_or(CommandError::BadArgs)?;
if let Err(e) = kata_mlcoord_cancel(bundle_id, model_id) {
writeln!(output, "Cancel {:?} {:?} err: {:?}", bundle_id, model_id, e)?;
} else {
writeln!(output, "Cancelled {:?} {:?}", bundle_id, model_id)?;
}
Ok(())
}
/// Implements a command that runs a oneshot ML execution.
fn mlexecute_command(
args: &mut dyn Iterator<Item = &str>,
_input: &mut dyn io::BufRead,
output: &mut dyn io::Write,
_builtin_cpio: &[u8],
) -> Result<(), CommandError> {
let bundle_id = args.next().ok_or(CommandError::BadArgs)?;
let model_id = args.next().ok_or(CommandError::BadArgs)?;
if let Err(e) = kata_mlcoord_oneshot(bundle_id, model_id) {
writeln!(output, "Execute {:?} {:?} err: {:?}", bundle_id, model_id, e)?;
}
Ok(())
}
/// Implements a command that runs a periodic ML execution.
fn mlperiodic_command(
args: &mut dyn Iterator<Item = &str>,
_input: &mut dyn io::BufRead,
output: &mut dyn io::Write,
_builtin_cpio: &[u8],
) -> Result<(), CommandError> {
let bundle_id = args.next().ok_or(CommandError::BadArgs)?;
let model_id = args.next().ok_or(CommandError::BadArgs)?;
let rate_str = args.next().ok_or(CommandError::BadArgs)?;
let rate_in_ms = rate_str.parse::<u32>()?;
if let Err(e) = kata_mlcoord_periodic(bundle_id, model_id, rate_in_ms) {
writeln!(output, "Periodic {:?} {:?} err: {:?}", bundle_id, model_id, e)?;
}
Ok(())
}

View File

@ -0,0 +1,23 @@
// Panic-related shell test commands
use crate::CmdFn;
use crate::CommandError;
use crate::HashMap;
use kata_io as io;
pub fn add_cmds(cmds: &mut HashMap::<&str, CmdFn>) {
cmds.extend([
("test_panic", panic_command as CmdFn),
]);
}
/// Implements a command that tests panic handling.
fn panic_command(
_args: &mut dyn Iterator<Item = &str>,
_input: &mut dyn io::BufRead,
_output: &mut dyn io::Write,
_builtin_cpio: &[u8],
) -> Result<(), CommandError> {
panic!("testing");
}

View File

@ -0,0 +1,80 @@
// TimerService shell test commands
use core::fmt::Write;
use crate::CmdFn;
use crate::CommandError;
use crate::HashMap;
use kata_io as io;
use kata_timer_interface::timer_service_completed_timers;
use kata_timer_interface::TimerServiceError;
use kata_timer_interface::timer_service_oneshot;
use kata_timer_interface::timer_service_wait;
pub fn add_cmds(cmds: &mut HashMap::<&str, CmdFn>) {
cmds.extend([
("test_timer_async", timer_async_command as CmdFn),
("test_timer_blocking", timer_blocking_command as CmdFn),
("test_timer_completed",timer_completed_command as CmdFn),
]);
}
/// Implements a command that starts a timer, but does not wait on the
/// notification.
fn timer_async_command(
args: &mut dyn Iterator<Item = &str>,
_input: &mut dyn io::BufRead,
output: &mut dyn io::Write,
_builtin_cpio: &[u8],
) -> Result<(), CommandError> {
let id_str = args.next().ok_or(CommandError::BadArgs)?;
let id = id_str.parse::<u32>()?;
let time_str = args.next().ok_or(CommandError::BadArgs)?;
let time_ms = time_str.parse::<u32>()?;
writeln!(output, "Starting timer {} for {} ms.", id, time_ms)?;
match timer_service_oneshot(id, time_ms) {
TimerServiceError::TimerOk => (),
_ => return Err(CommandError::BadArgs),
}
timer_service_oneshot(id, time_ms);
Ok(())
}
/// Implements a command that starts a timer, blocking until the timer has
/// completed.
fn timer_blocking_command(
args: &mut dyn Iterator<Item = &str>,
_input: &mut dyn io::BufRead,
output: &mut dyn io::Write,
_builtin_cpio: &[u8],
) -> Result<(), CommandError> {
let time_str = args.next().ok_or(CommandError::BadArgs)?;
let time_ms = time_str.parse::<u32>()?;
writeln!(output, "Blocking {} ms waiting for timer.", time_ms)?;
// Set timer_id to 0, we don't need to use multiple timers here.
match timer_service_oneshot(0, time_ms) {
TimerServiceError::TimerOk => (),
_ => return Err(CommandError::BadArgs),
}
timer_service_wait();
return Ok(writeln!(output, "Timer completed.")?);
}
/// Implements a command that checks the completed timers.
fn timer_completed_command(
_args: &mut dyn Iterator<Item = &str>,
_input: &mut dyn io::BufRead,
output: &mut dyn io::Write,
_builtin_cpio: &[u8],
) -> Result<(), CommandError> {
return Ok(writeln!(output, "Timers completed: {:#032b}", timer_service_completed_timers())?);
}