mirror of
https://github.com/AmbiML/sparrow-kata-full.git
synced 2025-04-27 18:35:32 +00:00
DebugConsole: add autostart script support & make uart optional
- make kata-uart-client use dependent on a new "sparrow_uart_support" feature; this is needed for headless operation and for platforms without a working uart driver - add a mechanism where an "autostart.repl" file is fetched from the builtins (if present) and passed through the shell; output goes to the uart if configured, otherwise the kernel or /dev/nnull - add a new "source" shell command that interprets the contents of a builtins file as console input - rework the command interpreter to support autostart & source - move the logging hookup to kata-debug-console so the system builds when no uart support is conffigured (need to add fallback to debug syscalls in case that works when no driver is present) Change-Id: I5e6725c93488a48d212dfaca425ede37cbdb72e5 GitOrigin-RevId: 6f360cab71ea103af52e3c68ca240fc16e0f20bb
This commit is contained in:
parent
d866234653
commit
88841cb7a7
@ -27,10 +27,10 @@ component DebugConsole {
|
||||
dataport Buf(0x1000000) cpio_archive;
|
||||
|
||||
dataport Buf tx_dataport;
|
||||
uses rust_write_inf uart_write;
|
||||
maybe uses rust_write_inf uart_write;
|
||||
|
||||
dataport Buf rx_dataport;
|
||||
uses rust_read_inf uart_read;
|
||||
maybe uses rust_read_inf uart_read;
|
||||
|
||||
provides LoggerInterface logger;
|
||||
uses MemoryInterface memory;
|
||||
|
@ -20,18 +20,28 @@ edition = "2021"
|
||||
description = "Kata OS DebugConsole"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
default = [
|
||||
"autostart_support",
|
||||
"sparrow_uart_support",
|
||||
]
|
||||
sparrow_uart_support = ["kata-uart-client"]
|
||||
autostart_support = ["default-uart-client"]
|
||||
# Log level is Info unless LOG_DEBUG or LOG_TRACE are specified
|
||||
LOG_DEBUG = []
|
||||
LOG_TRACE = []
|
||||
|
||||
[dependencies]
|
||||
panic-halt = "0.2.0"
|
||||
cpio = { git = "https://github.com/rcore-os/cpio" }
|
||||
# Disable default so we don't pull in CString which requires an allocator
|
||||
cstr_core = { version = "0.2.3", default-features = false }
|
||||
cty = "0.2.1"
|
||||
default-uart-client = { path = "../default-uart-client", optional = true }
|
||||
kata-io = { path = "../kata-io" }
|
||||
kata-os-common = { path = "../../kata-os-common" }
|
||||
kata-shell = { path = "../kata-shell" }
|
||||
kata-uart-client = { path = "../kata-uart-client" }
|
||||
kata-uart-client = { path = "../kata-uart-client", optional = true }
|
||||
log = { version = "0.4", features = ["release_max_level_info"] }
|
||||
panic-halt = "0.2.0"
|
||||
|
||||
[lib]
|
||||
name = "kata_debug_console"
|
||||
|
@ -24,8 +24,12 @@
|
||||
//! * kata_debug_console main entry point fn run()
|
||||
|
||||
#![no_std]
|
||||
#![allow(clippy::missing_safety_doc)]
|
||||
|
||||
use core::fmt::Write;
|
||||
use core::slice;
|
||||
use cpio::CpioNewcReader;
|
||||
use cstr_core::CStr;
|
||||
use kata_os_common::camkes::Camkes;
|
||||
use log::LevelFilter;
|
||||
|
||||
@ -51,14 +55,82 @@ pub unsafe extern "C" fn pre_init() {
|
||||
CAMKES.pre_init(INIT_LOG_LEVEL, &mut HEAP_MEMORY);
|
||||
}
|
||||
|
||||
/// Entry point for DebugConsole. Runs the shell with UART IO.
|
||||
// Returns a trait-compatible Tx based on the selected features.
|
||||
// NB: must use "return expr;" to avoid confusing the compiler.
|
||||
fn get_tx() -> impl kata_io::Write {
|
||||
#[cfg(feature = "sparrow_uart_support")]
|
||||
return kata_uart_client::Tx::new();
|
||||
|
||||
#[cfg(not(feature = "sparrow_uart_support"))]
|
||||
return default_uart_client::Tx::new();
|
||||
}
|
||||
|
||||
/// Console logging interface.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn run() -> ! {
|
||||
pub unsafe extern "C" fn logger_log(level: u8, msg: *const cstr_core::c_char) {
|
||||
use log::Level;
|
||||
let l = match level {
|
||||
x if x == Level::Error as u8 => Level::Error,
|
||||
x if x == Level::Warn as u8 => Level::Warn,
|
||||
x if x == Level::Info as u8 => Level::Info,
|
||||
x if x == Level::Debug as u8 => Level::Debug,
|
||||
_ => Level::Trace,
|
||||
};
|
||||
if l <= log::max_level() {
|
||||
// TODO(sleffler): is the uart driver ok w/ multiple writers?
|
||||
// TODO(sleffler): fallback to seL4_DebugPutChar?
|
||||
let output: &mut dyn kata_io::Write = &mut get_tx();
|
||||
let _ = writeln!(output, "{}", CStr::from_ptr(msg).to_str().unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
// If the builtins archive includes an "autostart.repl" file it is run
|
||||
// through the shell with output sent either to the console or /dev/null
|
||||
// depending on the feature selection.
|
||||
#[cfg(feature = "autostart_support")]
|
||||
fn run_autostart_shell(cpio_archive_ref: &[u8]) {
|
||||
const AUTOSTART_NAME: &str = "autostart.repl";
|
||||
|
||||
let mut autostart_script: Option<&[u8]> = None;
|
||||
let reader = CpioNewcReader::new(cpio_archive_ref);
|
||||
for e in reader {
|
||||
if e.is_err() {
|
||||
break;
|
||||
}
|
||||
let entry = e.unwrap();
|
||||
if entry.name == AUTOSTART_NAME {
|
||||
autostart_script = Some(entry.data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if let Some(script) = autostart_script {
|
||||
// Rx data comes from the embedded script
|
||||
// Tx data goes to either the uart or /dev/null
|
||||
let mut rx = kata_io::BufReader::new(default_uart_client::Rx::new(script));
|
||||
kata_shell::repl_eof(&mut get_tx(), &mut rx, cpio_archive_ref);
|
||||
}
|
||||
}
|
||||
|
||||
// Runs an interactive shell using the Sparrow UART.
|
||||
#[cfg(feature = "sparrow_uart_support")]
|
||||
fn run_sparrow_shell(cpio_archive_ref: &[u8]) -> ! {
|
||||
let mut tx = kata_uart_client::Tx::new();
|
||||
let mut rx = kata_io::BufReader::new(kata_uart_client::Rx::new());
|
||||
kata_shell::repl(&mut tx, &mut rx, cpio_archive_ref);
|
||||
}
|
||||
|
||||
/// Entry point for DebugConsole. Optionally runs an autostart script
|
||||
/// after which it runs an interactive shell with UART IO.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn run() {
|
||||
let cpio_archive_ref = unsafe {
|
||||
// XXX want begin-end or begin+size instead of a fixed-size block
|
||||
slice::from_raw_parts(cpio_archive, 16777216)
|
||||
};
|
||||
kata_shell::repl(&mut tx, &mut rx, cpio_archive_ref);
|
||||
|
||||
#[cfg(feature = "autostart_support")]
|
||||
run_autostart_shell(cpio_archive_ref);
|
||||
|
||||
#[cfg(feature = "sparrow_uart_support")]
|
||||
run_sparrow_shell(cpio_archive_ref);
|
||||
}
|
||||
|
@ -53,6 +53,7 @@ TEST_UART = []
|
||||
[dependencies]
|
||||
crc = { version = "1.4.0", default_features = false }
|
||||
cpio = { git = "https://github.com/rcore-os/cpio" }
|
||||
default-uart-client = { path = "../default-uart-client" }
|
||||
hashbrown = { version = "0.11", features = ["ahash-compile-time-rng"] }
|
||||
hex = { version = "0.4.3", default-features = false, features = ["alloc"] }
|
||||
kata-io = { path = "../kata-io" }
|
||||
|
@ -114,8 +114,7 @@ type CmdFn = fn(
|
||||
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]) -> ! {
|
||||
fn get_cmds() -> HashMap<&'static str, CmdFn> {
|
||||
let mut cmds = HashMap::<&str, CmdFn>::new();
|
||||
cmds.extend([
|
||||
("builtins", builtins_command as CmdFn),
|
||||
@ -129,6 +128,7 @@ pub fn repl<T: io::BufRead>(output: &mut dyn io::Write, input: &mut T, builtin_c
|
||||
("mdebug", mdebug_command as CmdFn),
|
||||
("mstats", mstats_command as CmdFn),
|
||||
("ps", ps_command as CmdFn),
|
||||
("source", source_command as CmdFn),
|
||||
("start", start_command as CmdFn),
|
||||
("stop", stop_command as CmdFn),
|
||||
("uninstall", uninstall_command as CmdFn),
|
||||
@ -152,35 +152,49 @@ pub fn repl<T: io::BufRead>(output: &mut dyn io::Write, input: &mut T, builtin_c
|
||||
#[cfg(feature = "TEST_UART")]
|
||||
test_uart::add_cmds(&mut cmds);
|
||||
|
||||
cmds
|
||||
}
|
||||
|
||||
pub fn eval<T: io::BufRead>(
|
||||
cmdline: &str,
|
||||
cmds: &HashMap<&str, CmdFn>,
|
||||
output: &mut dyn io::Write,
|
||||
input: &mut T,
|
||||
builtin_cpio: &[u8],
|
||||
) {
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 cmds = get_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) => {
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(cmdline) => eval(cmdline, &cmds, output, input, builtin_cpio),
|
||||
Err(e) => {
|
||||
let _ = writeln!(output, "\n{}", e);
|
||||
}
|
||||
@ -188,6 +202,48 @@ pub fn repl<T: io::BufRead>(output: &mut dyn io::Write, input: &mut T, builtin_c
|
||||
}
|
||||
}
|
||||
|
||||
/// Stripped down repl for running automation scripts. Like repl but prints
|
||||
/// each cmd line and stops at EOF/error.
|
||||
pub fn repl_eof<T: io::BufRead>(output: &mut dyn io::Write, input: &mut T, builtin_cpio: &[u8]) {
|
||||
let cmds = get_cmds();
|
||||
let mut line_reader = LineReader::new();
|
||||
while let Ok(cmdline) = line_reader.read_line(output, input) {
|
||||
// NB: LineReader echo's input
|
||||
eval(cmdline, &cmds, output, input, builtin_cpio);
|
||||
}
|
||||
}
|
||||
|
||||
/// Implements a "source" command that interprets commands from a file
|
||||
/// in the built-in cpio archive.
|
||||
fn source_command(
|
||||
args: &mut dyn Iterator<Item = &str>,
|
||||
_input: &mut dyn io::BufRead,
|
||||
output: &mut dyn io::Write,
|
||||
builtin_cpio: &[u8],
|
||||
) -> Result<(), CommandError> {
|
||||
for script_name in args {
|
||||
let mut script_data: Option<&[u8]> = None;
|
||||
for e in CpioNewcReader::new(builtin_cpio) {
|
||||
if e.is_err() {
|
||||
writeln!(output, "cpio error")?;
|
||||
break; // NB: iterator does not terminate on error
|
||||
}
|
||||
let entry = e.unwrap();
|
||||
if entry.name == script_name {
|
||||
script_data = Some(entry.data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if let Some(data) = script_data {
|
||||
let mut script_input = kata_io::BufReader::new(default_uart_client::Rx::new(data));
|
||||
repl_eof(output, &mut script_input, builtin_cpio);
|
||||
} else {
|
||||
writeln!(output, "{}: not found", script_name)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Implements a "builtins" command that lists the contents of the built-in cpio archive.
|
||||
fn builtins_command(
|
||||
_args: &mut dyn Iterator<Item = &str>,
|
||||
|
@ -19,8 +19,5 @@ authors = ["Matt Harvey <mattharvey@google.com>"]
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
# Disable default so we don't pull in CString which requires an allocator
|
||||
cstr_core = { version = "0.2.3", default-features = false }
|
||||
cty = "0.2.1"
|
||||
kata-io = { path = "../kata-io" }
|
||||
log = { version = "0.4", features = ["release_max_level_info"] }
|
||||
|
@ -14,29 +14,8 @@
|
||||
|
||||
#![no_std]
|
||||
|
||||
use core::fmt::Write;
|
||||
use cstr_core::CStr;
|
||||
use kata_io as io;
|
||||
|
||||
/// Console logging interface.
|
||||
#[no_mangle]
|
||||
#[allow(clippy::missing_safety_doc)]
|
||||
pub unsafe extern "C" fn logger_log(level: u8, msg: *const cstr_core::c_char) {
|
||||
use log::Level;
|
||||
let l = match level {
|
||||
x if x == Level::Error as u8 => Level::Error,
|
||||
x if x == Level::Warn as u8 => Level::Warn,
|
||||
x if x == Level::Info as u8 => Level::Info,
|
||||
x if x == Level::Debug as u8 => Level::Debug,
|
||||
_ => Level::Trace,
|
||||
};
|
||||
if l <= log::max_level() {
|
||||
// TODO(sleffler): is the uart driver ok w/ multiple writers?
|
||||
let output: &mut dyn io::Write = &mut self::Tx::new();
|
||||
let _ = writeln!(output, "{}", CStr::from_ptr(msg).to_str().unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
const DATAPORT_SIZE: usize = 4096;
|
||||
|
||||
pub struct Rx {
|
||||
|
Loading…
Reference in New Issue
Block a user