mirror of
https://github.com/AmbiML/sparrow-kata-full.git
synced 2025-09-14 22:19:04 +00:00
Replaces processmanager with a one-app CAmkES assembly for the entire Kata OS
This also adds a skeleton for the DebugConsole CLI taking IO from a UART via some Rust wrapper functions, also defined in this change (kata-uart-client). Change-Id: I56856c14992010483da58c45f6550c0a4c9987b0 GitOrigin-RevId: e1b2d65ed3a7f627a9f7377caa407151fc943864
This commit is contained in:
49
apps/system/components/DebugConsole/Cargo.lock
generated
Normal file
49
apps/system/components/DebugConsole/Cargo.lock
generated
Normal file
@@ -0,0 +1,49 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "cty"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7313c0d620d0cb4dbd9d019e461a4beb501071ff46ec0ab933efb4daa76d73e3"
|
||||
|
||||
[[package]]
|
||||
name = "kata-debug-console"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"kata-shell",
|
||||
"kata-uart-client",
|
||||
"panic-halt",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kata-io"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "kata-line-reader"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"kata-io",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kata-shell"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"kata-io",
|
||||
"kata-line-reader",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kata-uart-client"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cty",
|
||||
"kata-io",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "panic-halt"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "de96540e0ebde571dc55c73d60ef407c653844e6f9a1e2fdbd40c07b9252d812"
|
9
apps/system/components/DebugConsole/Cargo.toml
Normal file
9
apps/system/components/DebugConsole/Cargo.toml
Normal file
@@ -0,0 +1,9 @@
|
||||
[workspace]
|
||||
|
||||
members = [
|
||||
"kata-debug-console",
|
||||
"kata-io",
|
||||
"kata-line-reader",
|
||||
"kata-shell",
|
||||
"kata-uart-client",
|
||||
]
|
6
apps/system/components/DebugConsole/DebugConsole.camkes
Normal file
6
apps/system/components/DebugConsole/DebugConsole.camkes
Normal file
@@ -0,0 +1,6 @@
|
||||
component DebugConsole {
|
||||
control;
|
||||
uses uart_inf uart;
|
||||
dataport Buf tx_dataport;
|
||||
dataport Buf rx_dataport;
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
[package]
|
||||
name = "kata-debug-console"
|
||||
version = "0.1.0"
|
||||
authors = ["Matt Harvey <mattharvey@google.com>"]
|
||||
edition = "2018"
|
||||
description = "Kata OS DebugConsole"
|
||||
|
||||
[dependencies]
|
||||
panic-halt = "0.2.0"
|
||||
kata-shell = { path = "../kata-shell" }
|
||||
kata-uart-client = { path = "../kata-uart-client" }
|
||||
|
||||
[lib]
|
||||
name = "kata_debug_console"
|
||||
path = "src/run.rs"
|
||||
crate-type = ["staticlib"]
|
@@ -0,0 +1,27 @@
|
||||
//! Kata OS command line interface
|
||||
|
||||
// This brief bootstrap of Rust-in-Kata prototypes a minimal modular design
|
||||
// for the DebugConsole CLI use case.
|
||||
//
|
||||
// * kata_io Read/Write interface (or move to std::, but that requires alloc)
|
||||
// * kata_uart_client implementation of the kata_io interface
|
||||
// * kata_line_reader
|
||||
// * kata_shell
|
||||
// * kata_debug_console main entry point fn run()
|
||||
|
||||
// std:: requires at least an allocator, which Kata does not have yet. For now
|
||||
// the CLI will be implemented with only core::.
|
||||
#![no_std]
|
||||
|
||||
extern crate panic_halt;
|
||||
|
||||
use kata_shell;
|
||||
use kata_uart_client;
|
||||
|
||||
/// Entry point for DebugConsole. Runs the shell with UART IO.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn run() -> ! {
|
||||
let mut tx = kata_uart_client::Tx {};
|
||||
let mut rx = kata_uart_client::Rx {};
|
||||
kata_shell::repl(&mut tx, &mut rx);
|
||||
}
|
5
apps/system/components/DebugConsole/kata-io/Cargo.toml
Normal file
5
apps/system/components/DebugConsole/kata-io/Cargo.toml
Normal file
@@ -0,0 +1,5 @@
|
||||
[package]
|
||||
name = "kata-io"
|
||||
version = "0.1.0"
|
||||
authors = ["Matt Harvey <mattharvey@google.com>"]
|
||||
edition = "2018"
|
35
apps/system/components/DebugConsole/kata-io/src/lib.rs
Normal file
35
apps/system/components/DebugConsole/kata-io/src/lib.rs
Normal file
@@ -0,0 +1,35 @@
|
||||
#![no_std]
|
||||
|
||||
pub struct Error;
|
||||
|
||||
/// Interface for the CLI to consume bytes.
|
||||
pub trait Read {
|
||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error>;
|
||||
}
|
||||
|
||||
/// Interface for the CLI to emit bytes.
|
||||
pub trait Write {
|
||||
fn write(&mut self, buf: &[u8]) -> Result<usize, Error>;
|
||||
}
|
||||
|
||||
/// Adapter for writing core::fmt formatted strings.
|
||||
impl core::fmt::Write for dyn Write + '_ {
|
||||
/// Writes the bytes of a &str to the underlying writer.
|
||||
fn write_str(&mut self, s: &str) -> core::fmt::Result {
|
||||
match self.write(s.as_bytes()) {
|
||||
Ok(_) => Ok(()),
|
||||
Err(_) => Err(core::fmt::Error),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl dyn Read + '_ {
|
||||
pub fn get_u8(&mut self) -> Result<u8, Error> {
|
||||
let mut buf: [u8; 1] = [0u8];
|
||||
let n_read = self.read(&mut buf)?;
|
||||
match n_read {
|
||||
1usize => Ok(buf[0]),
|
||||
_ => Err(Error),
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
[package]
|
||||
name = "kata-line-reader"
|
||||
version = "0.1.0"
|
||||
authors = ["Matt Harvey <mattharvey@google.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
kata-io = { path = "../kata-io" }
|
@@ -0,0 +1,78 @@
|
||||
#![no_std]
|
||||
|
||||
use core::fmt;
|
||||
|
||||
use kata_io as io;
|
||||
|
||||
const LINE_MAX: usize = 128;
|
||||
|
||||
pub enum LineReadError {
|
||||
IO(io::Error),
|
||||
Overflow,
|
||||
Encoding(core::str::Utf8Error),
|
||||
}
|
||||
|
||||
impl From<io::Error> for LineReadError {
|
||||
fn from(err: io::Error) -> LineReadError {
|
||||
LineReadError::IO(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<core::str::Utf8Error> for LineReadError {
|
||||
fn from(err: core::str::Utf8Error) -> LineReadError {
|
||||
LineReadError::Encoding(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for LineReadError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
LineReadError::IO(_) => write!(f, "IO error"),
|
||||
LineReadError::Overflow => write!(f, "line too long"),
|
||||
LineReadError::Encoding(_) => write!(f, "bad character encoding"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LineReader {
|
||||
// Owned by LineReader to facilitate static allocation.
|
||||
buf: [u8; LINE_MAX],
|
||||
}
|
||||
|
||||
impl LineReader {
|
||||
pub fn new() -> LineReader {
|
||||
LineReader {
|
||||
buf: [0u8; LINE_MAX],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_line(
|
||||
&mut self,
|
||||
output: &mut dyn io::Write,
|
||||
input: &mut dyn io::Read,
|
||||
) -> Result<&str, LineReadError> {
|
||||
const DEL: u8 = 127u8;
|
||||
const BACKSPACE: u8 = 8u8;
|
||||
let mut len = 0;
|
||||
while len < self.buf.len() {
|
||||
let mut c = input.get_u8()?;
|
||||
while c == DEL || c == BACKSPACE {
|
||||
if len > 0 {
|
||||
output.write(&[BACKSPACE, b' ', BACKSPACE])?;
|
||||
len -= 1;
|
||||
}
|
||||
c = input.get_u8()?;
|
||||
}
|
||||
if c == b'\r' || c == b'\n' {
|
||||
if len > 0 {
|
||||
output.write(&[b'\n'])?;
|
||||
}
|
||||
return Ok(core::str::from_utf8(&self.buf[0..len])?);
|
||||
}
|
||||
self.buf[len] = c;
|
||||
len += 1;
|
||||
output.write(&[c])?;
|
||||
}
|
||||
Err(LineReadError::Overflow)
|
||||
}
|
||||
}
|
@@ -0,0 +1,9 @@
|
||||
[package]
|
||||
name = "kata-shell"
|
||||
version = "0.1.0"
|
||||
authors = ["Matt Harvey <mattharvey@google.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
kata-io = { path = "../kata-io" }
|
||||
kata-line-reader = { path = "../kata-line-reader" }
|
127
apps/system/components/DebugConsole/kata-shell/src/lib.rs
Normal file
127
apps/system/components/DebugConsole/kata-shell/src/lib.rs
Normal file
@@ -0,0 +1,127 @@
|
||||
#![no_std]
|
||||
|
||||
use core::fmt;
|
||||
use core::fmt::Write;
|
||||
|
||||
use kata_io as io;
|
||||
use kata_line_reader::LineReader;
|
||||
|
||||
/// Error type indicating why a command line is not runnable.
|
||||
enum CommandError {
|
||||
UnknownCommand,
|
||||
BadArgs,
|
||||
Formatter(fmt::Error),
|
||||
}
|
||||
|
||||
impl fmt::Display for CommandError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
CommandError::UnknownCommand => write!(f, "unknown command"),
|
||||
CommandError::BadArgs => write!(f, "invalid arguments"),
|
||||
CommandError::Formatter(e) => write!(f, "{}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<core::num::ParseIntError> for CommandError {
|
||||
fn from(_err: core::num::ParseIntError) -> CommandError {
|
||||
CommandError::BadArgs
|
||||
}
|
||||
}
|
||||
|
||||
impl From<core::num::ParseFloatError> for CommandError {
|
||||
fn from(_err: core::num::ParseFloatError) -> CommandError {
|
||||
CommandError::BadArgs
|
||||
}
|
||||
}
|
||||
|
||||
impl From<fmt::Error> for CommandError {
|
||||
fn from(err: fmt::Error) -> CommandError {
|
||||
CommandError::Formatter(err)
|
||||
}
|
||||
}
|
||||
|
||||
/// Read-eval-print loop for the DebugConsole command line interface.
|
||||
pub fn repl(output: &mut dyn io::Write, input: &mut dyn io::Read) -> ! {
|
||||
let mut line_reader = LineReader::new();
|
||||
loop {
|
||||
// The PROMPT is the Hiragana representation of the word "kata."
|
||||
const PROMPT: &str = "かた ";
|
||||
let _ = output.write_str(PROMPT);
|
||||
match line_reader.read_line(output, input) {
|
||||
Ok(cmdline) => dispatch_command(cmdline, output),
|
||||
Err(e) => {
|
||||
let _ = write!(output, "\n{}\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, output: &mut dyn io::Write) {
|
||||
let mut args = cmdline.split_ascii_whitespace();
|
||||
match args.nth(0) {
|
||||
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 {
|
||||
"echo" => echo_command(cmdline, output),
|
||||
"add" => add_command(&mut args, output),
|
||||
"clear" => clear_command(output),
|
||||
_ => Err(CommandError::UnknownCommand),
|
||||
};
|
||||
if let Err(e) = result {
|
||||
let _ = write!(output, "{}\n", e);
|
||||
};
|
||||
}
|
||||
None => {
|
||||
let _ = output.write_str("\n");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// 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(write!(
|
||||
output,
|
||||
"{}\n",
|
||||
&cmdline[COMMAND_LENGTH..cmdline.len()]
|
||||
)?)
|
||||
}
|
||||
}
|
||||
|
||||
/// 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>,
|
||||
output: &mut dyn io::Write,
|
||||
) -> Result<(), CommandError> {
|
||||
if let Some(x_str) = args.nth(0) {
|
||||
if let Some(y_str) = args.nth(0) {
|
||||
let x = x_str.parse::<f32>()?;
|
||||
let y = y_str.parse::<f32>()?;
|
||||
return Ok(write!(output, "{}\n", x + y)?);
|
||||
}
|
||||
}
|
||||
Err(CommandError::BadArgs)
|
||||
}
|
||||
|
||||
/// 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")?)
|
||||
}
|
@@ -0,0 +1,9 @@
|
||||
[package]
|
||||
name = "kata-uart-client"
|
||||
version = "0.1.0"
|
||||
authors = ["Matt Harvey <mattharvey@google.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
cty = "0.2.1"
|
||||
kata-io = { path = "../kata-io" }
|
@@ -0,0 +1,37 @@
|
||||
#![no_std]
|
||||
|
||||
use kata_io as io;
|
||||
|
||||
// C interface to external UART driver.
|
||||
extern "C" {
|
||||
static rx_dataport: *mut cty::c_char;
|
||||
static tx_dataport: *mut cty::c_char;
|
||||
fn uart_rx(n: cty::size_t);
|
||||
fn uart_tx(n: cty::size_t);
|
||||
}
|
||||
|
||||
pub struct Rx {}
|
||||
|
||||
impl io::Read for Rx {
|
||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize, io::Error> {
|
||||
unsafe {
|
||||
uart_rx(buf.len());
|
||||
let port = core::slice::from_raw_parts(rx_dataport, buf.len());
|
||||
buf.copy_from_slice(&port);
|
||||
}
|
||||
Ok(buf.len())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Tx {}
|
||||
|
||||
impl io::Write for Tx {
|
||||
fn write(&mut self, buf: &[u8]) -> Result<usize, io::Error> {
|
||||
unsafe {
|
||||
let port = core::slice::from_raw_parts_mut(tx_dataport, buf.len());
|
||||
port.copy_from_slice(buf);
|
||||
uart_tx(buf.len());
|
||||
}
|
||||
Ok(buf.len())
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user