mirror of
https://github.com/AmbiML/sparrow-kata-full.git
synced 2025-07-13 14:04:19 +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(0x1000000) cpio_archive;
|
||||||
|
|
||||||
dataport Buf tx_dataport;
|
dataport Buf tx_dataport;
|
||||||
uses rust_write_inf uart_write;
|
maybe uses rust_write_inf uart_write;
|
||||||
|
|
||||||
dataport Buf rx_dataport;
|
dataport Buf rx_dataport;
|
||||||
uses rust_read_inf uart_read;
|
maybe uses rust_read_inf uart_read;
|
||||||
|
|
||||||
provides LoggerInterface logger;
|
provides LoggerInterface logger;
|
||||||
uses MemoryInterface memory;
|
uses MemoryInterface memory;
|
||||||
|
@ -20,18 +20,28 @@ edition = "2021"
|
|||||||
description = "Kata OS DebugConsole"
|
description = "Kata OS DebugConsole"
|
||||||
|
|
||||||
[features]
|
[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 level is Info unless LOG_DEBUG or LOG_TRACE are specified
|
||||||
LOG_DEBUG = []
|
LOG_DEBUG = []
|
||||||
LOG_TRACE = []
|
LOG_TRACE = []
|
||||||
|
|
||||||
[dependencies]
|
[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-io = { path = "../kata-io" }
|
||||||
kata-os-common = { path = "../../kata-os-common" }
|
kata-os-common = { path = "../../kata-os-common" }
|
||||||
kata-shell = { path = "../kata-shell" }
|
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"] }
|
log = { version = "0.4", features = ["release_max_level_info"] }
|
||||||
|
panic-halt = "0.2.0"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "kata_debug_console"
|
name = "kata_debug_console"
|
||||||
|
@ -24,8 +24,12 @@
|
|||||||
//! * kata_debug_console main entry point fn run()
|
//! * kata_debug_console main entry point fn run()
|
||||||
|
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
#![allow(clippy::missing_safety_doc)]
|
||||||
|
|
||||||
|
use core::fmt::Write;
|
||||||
use core::slice;
|
use core::slice;
|
||||||
|
use cpio::CpioNewcReader;
|
||||||
|
use cstr_core::CStr;
|
||||||
use kata_os_common::camkes::Camkes;
|
use kata_os_common::camkes::Camkes;
|
||||||
use log::LevelFilter;
|
use log::LevelFilter;
|
||||||
|
|
||||||
@ -51,14 +55,82 @@ pub unsafe extern "C" fn pre_init() {
|
|||||||
CAMKES.pre_init(INIT_LOG_LEVEL, &mut HEAP_MEMORY);
|
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]
|
#[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 tx = kata_uart_client::Tx::new();
|
||||||
let mut rx = kata_io::BufReader::new(kata_uart_client::Rx::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 {
|
let cpio_archive_ref = unsafe {
|
||||||
// XXX want begin-end or begin+size instead of a fixed-size block
|
// XXX want begin-end or begin+size instead of a fixed-size block
|
||||||
slice::from_raw_parts(cpio_archive, 16777216)
|
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]
|
[dependencies]
|
||||||
crc = { version = "1.4.0", default_features = false }
|
crc = { version = "1.4.0", default_features = false }
|
||||||
cpio = { git = "https://github.com/rcore-os/cpio" }
|
cpio = { git = "https://github.com/rcore-os/cpio" }
|
||||||
|
default-uart-client = { path = "../default-uart-client" }
|
||||||
hashbrown = { version = "0.11", features = ["ahash-compile-time-rng"] }
|
hashbrown = { version = "0.11", features = ["ahash-compile-time-rng"] }
|
||||||
hex = { version = "0.4.3", default-features = false, features = ["alloc"] }
|
hex = { version = "0.4.3", default-features = false, features = ["alloc"] }
|
||||||
kata-io = { path = "../kata-io" }
|
kata-io = { path = "../kata-io" }
|
||||||
|
@ -114,8 +114,7 @@ type CmdFn = fn(
|
|||||||
builtin_cpio: &[u8],
|
builtin_cpio: &[u8],
|
||||||
) -> Result<(), CommandError>;
|
) -> Result<(), CommandError>;
|
||||||
|
|
||||||
/// Read-eval-print loop for the DebugConsole command line interface.
|
fn get_cmds() -> HashMap<&'static str, CmdFn> {
|
||||||
pub fn repl<T: io::BufRead>(output: &mut dyn io::Write, input: &mut T, builtin_cpio: &[u8]) -> ! {
|
|
||||||
let mut cmds = HashMap::<&str, CmdFn>::new();
|
let mut cmds = HashMap::<&str, CmdFn>::new();
|
||||||
cmds.extend([
|
cmds.extend([
|
||||||
("builtins", builtins_command as CmdFn),
|
("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),
|
("mdebug", mdebug_command as CmdFn),
|
||||||
("mstats", mstats_command as CmdFn),
|
("mstats", mstats_command as CmdFn),
|
||||||
("ps", ps_command as CmdFn),
|
("ps", ps_command as CmdFn),
|
||||||
|
("source", source_command as CmdFn),
|
||||||
("start", start_command as CmdFn),
|
("start", start_command as CmdFn),
|
||||||
("stop", stop_command as CmdFn),
|
("stop", stop_command as CmdFn),
|
||||||
("uninstall", uninstall_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")]
|
#[cfg(feature = "TEST_UART")]
|
||||||
test_uart::add_cmds(&mut cmds);
|
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();
|
let mut line_reader = LineReader::new();
|
||||||
loop {
|
loop {
|
||||||
const PROMPT: &str = "KATA> ";
|
const PROMPT: &str = "KATA> ";
|
||||||
let _ = output.write_str(PROMPT);
|
let _ = output.write_str(PROMPT);
|
||||||
match line_reader.read_line(output, input) {
|
match line_reader.read_line(output, input) {
|
||||||
Ok(cmdline) => {
|
Ok(cmdline) => eval(cmdline, &cmds, output, input, builtin_cpio),
|
||||||
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) => {
|
Err(e) => {
|
||||||
let _ = writeln!(output, "\n{}", 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.
|
/// Implements a "builtins" command that lists the contents of the built-in cpio archive.
|
||||||
fn builtins_command(
|
fn builtins_command(
|
||||||
_args: &mut dyn Iterator<Item = &str>,
|
_args: &mut dyn Iterator<Item = &str>,
|
||||||
|
@ -19,8 +19,5 @@ authors = ["Matt Harvey <mattharvey@google.com>"]
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[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"
|
cty = "0.2.1"
|
||||||
kata-io = { path = "../kata-io" }
|
kata-io = { path = "../kata-io" }
|
||||||
log = { version = "0.4", features = ["release_max_level_info"] }
|
|
||||||
|
@ -14,29 +14,8 @@
|
|||||||
|
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
use core::fmt::Write;
|
|
||||||
use cstr_core::CStr;
|
|
||||||
use kata_io as io;
|
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;
|
const DATAPORT_SIZE: usize = 4096;
|
||||||
|
|
||||||
pub struct Rx {
|
pub struct Rx {
|
||||||
|
Loading…
Reference in New Issue
Block a user