From 7363bd638ca7bb0d8899fb0bcc7fac7c4613ee45 Mon Sep 17 00:00:00 2001 From: Matt Harvey Date: Tue, 21 Sep 2021 21:50:21 -0700 Subject: [PATCH] rz shell command This change adds an "rz" command to the shell that calls a function rz(). It switches the IO from the shell to a ZMODEM receiver that produces a Rust upload object that collects the uploaded bytes and computes a CRC32 checksum on the fly. In a later change, we will retain the most recent payload in a shell-owned object until it is consumed by an "install" command. Limitations: * Sender hangup will cause the receiver to block forever and the prompt not to come back, since kata-uart-client does reads that block forever. * Logging must be set to a level higher than debug or messages from the zmodem crate itself corrupt the transfer. * The sender must rate limit to ~150 bytes/sec to avoid running too far ahead of the Renode UART, which drops bytes when the RX FIFO is full. * Uploading too big a payload will cause a kata_panic! unless the heap size is increased in kata-debug-console/src/run.rs Demo: KATA> loglevel error ERROR KATA> rz **B0100000023be50 [Ctrl-C] $ sz -O ~/random < /tmp/term | pv -L 150 > /tmp/term Sending: random Bytes Sent: 15360/ 16384 BPS:26805 ETA 00:00 150 B 0:00:01 [ 146 B/s] [ <=> Bytes Sent: 16384 BPS:144 ] Transfer complete 16.6KiB 0:01:53 [ 149 B/s] [ <=> ] $ stty sane -echo -icanon; socat - /tmp/term 2> /dev/null; stty sane; size: 16384, crc32: 991b1d60 KATA> [Ctrl-C] $ crc32 ~/random 991b1d60 Change-Id: I53252b821a829a667a23a9fd072f71c6955fdc1a GitOrigin-RevId: c818a35f186dcd8c083891bfaa84ad0a9f9fef7d --- .../DebugConsole/kata-shell/Cargo.toml | 3 ++ .../DebugConsole/kata-shell/src/lib.rs | 33 ++++++++++-- .../DebugConsole/kata-shell/src/rz.rs | 50 +++++++++++++++++++ 3 files changed, 83 insertions(+), 3 deletions(-) create mode 100644 apps/system/components/DebugConsole/kata-shell/src/rz.rs diff --git a/apps/system/components/DebugConsole/kata-shell/Cargo.toml b/apps/system/components/DebugConsole/kata-shell/Cargo.toml index 12cf3de..e176637 100644 --- a/apps/system/components/DebugConsole/kata-shell/Cargo.toml +++ b/apps/system/components/DebugConsole/kata-shell/Cargo.toml @@ -5,7 +5,9 @@ authors = ["Matt Harvey "] edition = "2018" [dependencies] +crc = { version = "1.4.0", default_features = false } cstr_core = "0.2" +hex = { version = "0.4.3", default-features = false, features = ["alloc"] } kata-io = { path = "../kata-io" } kata-line-reader = { path = "../kata-line-reader" } kata-proc-common = { path = "../../ProcessManager/kata-proc-common" } @@ -13,3 +15,4 @@ kata-security-interface = { path = "../../SecurityCoordinator/kata-security-inte kata-storage-interface = { path = "../../StorageManager/kata-storage-interface" } log = "0.4" postcard = { version = "0.7", features = ["alloc"], default-features = false } +zmodem = { path = "../zmodem" } diff --git a/apps/system/components/DebugConsole/kata-shell/src/lib.rs b/apps/system/components/DebugConsole/kata-shell/src/lib.rs index 003bd85..babd9f8 100644 --- a/apps/system/components/DebugConsole/kata-shell/src/lib.rs +++ b/apps/system/components/DebugConsole/kata-shell/src/lib.rs @@ -6,6 +6,7 @@ use alloc::vec::Vec; use core::fmt; use core::fmt::Write; use cstr_core::CString; +use hex; use postcard; use kata_io as io; @@ -15,10 +16,13 @@ use kata_storage_interface::kata_storage_delete; use kata_storage_interface::kata_storage_read; use kata_storage_interface::kata_storage_write; +mod rz; + /// Error type indicating why a command line is not runnable. enum CommandError { UnknownCommand, BadArgs, + IO, Formatter(fmt::Error), } @@ -27,6 +31,7 @@ impl fmt::Display for CommandError { match self { CommandError::UnknownCommand => write!(f, "unknown command"), CommandError::BadArgs => write!(f, "invalid arguments"), + CommandError::IO => write!(f, "input / output error"), CommandError::Formatter(e) => write!(f, "{}", e), } } @@ -50,6 +55,12 @@ impl From for CommandError { } } +impl From for CommandError { + fn from(_err: io::Error) -> CommandError { + CommandError::IO + } +} + /// 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(); @@ -57,7 +68,7 @@ pub fn repl(output: &mut dyn io::Write, input: &mut dyn io::Read) -> ! { const PROMPT: &str = "KATA> "; let _ = output.write_str(PROMPT); match line_reader.read_line(output, input) { - Ok(cmdline) => dispatch_command(cmdline, output), + Ok(cmdline) => dispatch_command(cmdline, input, output), Err(e) => { let _ = writeln!(output, "\n{}", e); } @@ -69,7 +80,7 @@ pub fn repl(output: &mut dyn io::Write, input: &mut dyn io::Read) -> ! { /// /// 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) { +fn dispatch_command(cmdline: &str, input: &mut dyn io::Read, output: &mut dyn io::Write) { let mut args = cmdline.split_ascii_whitespace(); match args.nth(0) { Some(command) => { @@ -88,6 +99,7 @@ fn dispatch_command(cmdline: &str, output: &mut dyn io::Write) { "kvwrite" => kvwrite_command(&mut args, output), "install" => install_command(&mut args, output), "loglevel" => loglevel_command(&mut args, output), + "rz" => rz_command(input, output), "ps" => ps_command(), "scecho" => scecho_command(cmdline, output), "start" => start_command(&mut args, output), @@ -155,7 +167,7 @@ fn scecho_command(cmdline: &str, output: &mut dyn io::Write) -> Result<(), Comma Ok(()) } -// Set/display the max log level for the DebugConsole. +/// Implements a command to configure the max log level for the DebugConsole. fn loglevel_command( args: &mut dyn Iterator, output: &mut dyn io::Write, @@ -175,6 +187,21 @@ 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::Read, + mut output: &mut dyn io::Write, +) -> Result<(), CommandError> { + let upload = rz::rz(input, &mut output)?; + writeln!( + output, + "size: {}, crc32: {}", + upload.contents().len(), + hex::encode(upload.crc32().to_be_bytes()) + )?; + Ok(()) +} + /// Implements a "ps" command that dumps seL4 scheduler state to the console. fn ps_command() -> Result<(), CommandError> { extern "C" { diff --git a/apps/system/components/DebugConsole/kata-shell/src/rz.rs b/apps/system/components/DebugConsole/kata-shell/src/rz.rs new file mode 100644 index 0000000..5962054 --- /dev/null +++ b/apps/system/components/DebugConsole/kata-shell/src/rz.rs @@ -0,0 +1,50 @@ +/// Wrapper types for fully-buffered ZMODEM receives. +use alloc::vec::Vec; + +use crc::crc32; +use crc::Hasher32; + +use zmodem; + +use kata_io as io; + +pub struct Upload { + digest: crc32::Digest, + contents: Vec, +} + +impl Upload { + pub fn new() -> Upload { + Upload { + digest: crc32::Digest::new(crc32::IEEE), + contents: Vec::new(), + } + } + + pub fn crc32(&self) -> u32 { + self.digest.sum32() + } + + pub fn contents(&self) -> &[u8] { + self.contents.as_slice() + } +} + +impl io::Write for Upload { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.digest.write(buf); + self.contents.extend_from_slice(buf); + Ok(buf.len()) + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +/// Receives using ZMODEM and wraps the result as an Upload. +pub fn rz(r: R, w: W) -> Result { + let mut upload = Upload::new(); + zmodem::recv::recv(r, w, &mut upload)?; + Ok(upload) +}