diff --git a/Cargo.lock b/Cargo.lock index cd75771..67f1577 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,13 +19,25 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "ahash" -version = "0.8.3" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" dependencies = [ "cfg-if", + "const-random", + "getrandom 0.3.1", "once_cell", "version_check", + "zerocopy 0.8.26", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", ] [[package]] @@ -338,10 +350,33 @@ dependencies = [ "iana-time-zone", "js-sys", "num-traits", + "pure-rust-locales", + "serde", "wasm-bindgen", "windows-targets 0.52.6", ] +[[package]] +name = "chrono-tz" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efdce149c370f133a071ca8ef6ea340b7b88748ab0810097a9e2976eaa34b4f3" +dependencies = [ + "chrono", + "chrono-tz-build", + "phf", +] + +[[package]] +name = "chrono-tz-build" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f10f8c9340e31fc120ff885fcdb54a0b48e474bbd77cab557f0c30a3e569402" +dependencies = [ + "parse-zoneinfo", + "phf_codegen", +] + [[package]] name = "cipher" version = "0.4.4" @@ -432,6 +467,26 @@ version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "520fbf3c07483f94e3e3ca9d0cfd913d7718ef2483d2cfd91c0d9e91474ab913" +[[package]] +name = "const-random" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" +dependencies = [ + "const-random-macro", +] + +[[package]] +name = "const-random-macro" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" +dependencies = [ + "getrandom 0.2.7", + "once_cell", + "tiny-keccak", +] + [[package]] name = "constant_time_eq" version = "0.3.0" @@ -543,6 +598,12 @@ version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + [[package]] name = "crypto-bigint" version = "0.5.5" @@ -803,6 +864,10 @@ dependencies = [ "nix", "parse_int", "rand", + "rhai", + "rhai-chrono", + "rhai-env", + "rhai-fs", "serde", "serde_json", "sha2", @@ -1808,6 +1873,9 @@ name = "once_cell" version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +dependencies = [ + "portable-atomic", +] [[package]] name = "p256" @@ -1892,6 +1960,15 @@ dependencies = [ "windows-sys 0.36.1", ] +[[package]] +name = "parse-zoneinfo" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f2a05b18d44e2957b88f96ba460715e295bc1d7510468a2f3d3b44535d26c24" +dependencies = [ + "regex", +] + [[package]] name = "parse_int" version = "0.6.0" @@ -1973,6 +2050,35 @@ dependencies = [ "sha2", ] +[[package]] +name = "phf" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" +dependencies = [ + "phf_shared", +] + +[[package]] +name = "phf_codegen" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" +dependencies = [ + "phf_generator", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" +dependencies = [ + "phf_shared", + "rand", +] + [[package]] name = "phf_shared" version = "0.11.3" @@ -2027,6 +2133,12 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" +[[package]] +name = "portable-atomic" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" + [[package]] name = "ppv-lite86" version = "0.2.16" @@ -2087,6 +2199,12 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "pure-rust-locales" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1190fd18ae6ce9e137184f207593877e70f39b015040156b1e05081cdfe3733a" + [[package]] name = "quote" version = "1.0.35" @@ -2171,6 +2289,35 @@ dependencies = [ "thiserror", ] +[[package]] +name = "regex" +version = "1.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12de2eff854e5fa4b1295edd650e227e9d8fb0c9e90b12e7f36d6a6811791a29" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49530408a136e16e5b486e883fbb6ba058e8e4e8ae6621a77b048b314336e629" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" + [[package]] name = "reqwest" version = "0.11.27" @@ -2224,6 +2371,69 @@ dependencies = [ "subtle", ] +[[package]] +name = "rhai" +version = "1.22.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2780e813b755850e50b178931aaf94ed24f6817f46aaaf5d21c13c12d939a249" +dependencies = [ + "ahash", + "bitflags 2.9.1", + "instant", + "num-traits", + "once_cell", + "rhai_codegen", + "serde", + "serde_json", + "smallvec 1.10.0", + "smartstring", + "thin-vec", +] + +[[package]] +name = "rhai-chrono" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22099214084b30d0fc59b98404ddaf0e9a6e9d85a5410f93d6e19639f166e524" +dependencies = [ + "chrono", + "chrono-tz", + "rhai", +] + +[[package]] +name = "rhai-env" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e328a0eea295867e893ee7888f161ed3197f8ab561b3cc050d493399c410bee8" +dependencies = [ + "rhai", + "serde", + "serde_json", +] + +[[package]] +name = "rhai-fs" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9af3f7717d7194033924473b3e4f0b3dbd569e3528837c3fbb00b4e1d1c6ff16" +dependencies = [ + "rhai", + "serde", + "serde_json", +] + +[[package]] +name = "rhai_codegen" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5a11a05ee1ce44058fa3d5961d05194fdbe3ad6b40f904af764d81b86450e6b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "ring" version = "0.16.20" @@ -2669,6 +2879,21 @@ name = "smallvec" version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" +dependencies = [ + "serde", +] + +[[package]] +name = "smartstring" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb72c633efbaa2dd666986505016c32c3044395ceaf881518399d2f4127ee29" +dependencies = [ + "autocfg", + "serde", + "static_assertions", + "version_check", +] [[package]] name = "smoltcp" @@ -2964,6 +3189,15 @@ dependencies = [ "libc", ] +[[package]] +name = "thin-vec" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "144f754d318415ac792f9d69fc87abbbfc043ce2ef041c60f16ad828f638717d" +dependencies = [ + "serde", +] + [[package]] name = "thiserror" version = "1.0.69" @@ -3022,6 +3256,15 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42657b1a6f4d817cda8e7a0ace261fe0cc946cf3a80314390b22cc61ae080792" +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + [[package]] name = "tinyvec" version = "1.8.0" diff --git a/Cargo.toml b/Cargo.toml index eca5a32..010b729 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,6 +46,10 @@ once_cell = "1.21.3" paste = "1.0.15" parse_int = "0.6" rand = "0.8.5" +rhai-chrono = { version = "^0" } +rhai-env = "0.1.2" +rhai-fs = { version = "0.1.3", features = ["metadata"] } +rhai = { version = "1.21.0", features = ["serde", "metadata", "debugging"]} serde = { version = "1.0", default-features = false, features = ["derive"] } serde-big-array = "0.5.1" serde_bytes = "0.11.17" diff --git a/check_this.sh b/check_this.sh new file mode 100755 index 0000000..4a4b0cf --- /dev/null +++ b/check_this.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +./target/debug/faux-mgs --interface eno1 start-host-flash-hash 0 +while true; do + ./target/debug/faux-mgs --interface eno1 get-host-flash-hash 0 + if [ $? -eq 0 ]; then + exit 0 + fi + sleep 1 +done diff --git a/faux-mgs/Cargo.toml b/faux-mgs/Cargo.toml index 75acd0f..9a406ab 100644 --- a/faux-mgs/Cargo.toml +++ b/faux-mgs/Cargo.toml @@ -14,6 +14,10 @@ hex.workspace = true humantime.workspace = true nix.workspace = true rand.workspace = true +rhai-chrono.workspace = true +rhai-env.workspace = true +rhai-fs.workspace = true +rhai.workspace = true parse_int.workspace = true serde.workspace = true serde_json.workspace = true diff --git a/faux-mgs/src/main.rs b/faux-mgs/src/main.rs index 3efdc2b..9132176 100644 --- a/faux-mgs/src/main.rs +++ b/faux-mgs/src/main.rs @@ -65,6 +65,7 @@ use uuid::Uuid; use zerocopy::IntoBytes; mod picocom_map; +mod rhai; mod usart; /// Command line program that can send MGS messages to a single SP. @@ -472,6 +473,11 @@ enum Command { GetHostFlashHash { slot: u16, }, + /// Script + Rhai { + script: PathBuf, + args: Vec, + }, } #[derive(Subcommand, Debug, Clone)] @@ -1732,6 +1738,11 @@ async fn run_command( let result = sp.get_host_flash_hash(slot).await?; Ok(Output::Lines(vec![format!("{result:x?}")])) } + Command::Rhai { script, args } => { + let result = + rhai::interpreter(Arc::new(sp), log, script, args).await?; + Ok(Output::Lines(vec![format!("{result:x?}")])) + } } } diff --git a/faux-mgs/src/rhai.rs b/faux-mgs/src/rhai.rs new file mode 100644 index 0000000..090e7bd --- /dev/null +++ b/faux-mgs/src/rhai.rs @@ -0,0 +1,351 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +use std::path::PathBuf; + +use anyhow::{anyhow, Context, Result}; +use gateway_sp_comms::SingleSp; +use slog::{crit, debug, error, info, trace, warn, Logger}; + +use gateway_messages::{SpComponent, RotBootInfo}; +use rhai::packages::{MoreStringPackage, Package}; +use rhai::{Dynamic, Engine, EvalAltResult, NativeCallContext, Scope}; +use rhai_chrono::ChronoPackage; +use rhai_env::EnvironmentPackage; +use rhai_fs::FilesystemPackage; +use std::sync::Arc; + +fn read_cmpa( + context: NativeCallContext, + sp: Arc, + rt: tokio::runtime::Handle, +) -> Result> { + rt.block_on(async { + match sp.read_rot_cmpa().await { + Ok(v) => Ok(serde_json::from_value::(v.into()).map_err( + |e| { + Box::new(EvalAltResult::ErrorRuntime( + format!("{}", e).into(), + context.call_position(), + )) + }, + )?), + Err(e) => Err(Box::new(EvalAltResult::ErrorRuntime( + format!("{}", e).into(), + context.call_position(), + ))), + } + }) +} + +fn read_cfpa( + context: NativeCallContext, + sp: Arc, + rt: tokio::runtime::Handle, +) -> Result> { + rt.block_on(async { + match sp.read_rot_active_cfpa().await { + Ok(v) => Ok(serde_json::from_value::(v.into()).map_err( + |e| { + Box::new(EvalAltResult::ErrorRuntime( + format!("{}", e).into(), + context.call_position(), + )) + }, + )?), + Err(e) => Err(Box::new(EvalAltResult::ErrorRuntime( + format!("{}", e).into(), + context.call_position(), + ))), + } + }) +} + +fn start_host_flash_hash( + context: NativeCallContext, + sp: Arc, + rt: tokio::runtime::Handle, + slot: u16, +) -> Result> { + rt.block_on(async { + match sp.start_host_flash_hash(slot).await { + Ok(v) => Ok(serde_json::from_value::(v.into()).map_err( + |e| { + Box::new(EvalAltResult::ErrorRuntime( + format!("{}", e).into(), + context.call_position(), + )) + }, + )?), + Err(e) => Err(Box::new(EvalAltResult::ErrorRuntime( + format!("{}", e).into(), + context.call_position(), + ))), + } + }) +} + +fn get_host_flash_hash( + context: NativeCallContext, + sp: Arc, + rt: tokio::runtime::Handle, + slot: u16, +) -> Result> { + rt.block_on(async { + match sp.get_host_flash_hash(slot).await { + Ok(v) => Ok(serde_json::from_value::(v.into()).map_err( + |e| { + Box::new(EvalAltResult::ErrorRuntime( + format!("{}", e).into(), + context.call_position(), + )) + }, + )?), + Err(e) => Err(Box::new(EvalAltResult::ErrorRuntime( + format!("{}", e).into(), + context.call_position(), + ))), + } + }) +} + +fn reset_component( + context: NativeCallContext, + sp: Arc, + rt: tokio::runtime::Handle, + component: &str, +) -> Result> { + let component = SpComponent::try_from(component).map_err(|e| { + Box::new(EvalAltResult::ErrorRuntime( + format!("{:?}", e).into(), + context.call_position(), + )) + })?; + rt.block_on(async { + sp.reset_component_prepare(component).await.map_err(|e| { + Box::new(EvalAltResult::ErrorRuntime( + format!("{}", e).into(), + context.call_position(), + )) + })?; + + sp.reset_component_trigger(component, false).await.map_err(|e| { + Box::new(EvalAltResult::ErrorRuntime( + format!("{}", e).into(), + context.call_position(), + )) + })?; + Ok(().into()) + }) +} + +/// Use a Rhai interpreter per SingleSp that can maintain a connection. +pub async fn interpreter( + sp: Arc, + log: Logger, + script: PathBuf, + script_args: Vec, +) -> Result<()> { + let thread_log = log.clone(); + let rot_info_rt = tokio::runtime::Handle::current(); + let cfpa_rt = tokio::runtime::Handle::current(); + let cmpa_rt = tokio::runtime::Handle::current(); + let start_host_flash_hash_rt = tokio::runtime::Handle::current(); + let get_host_flash_hash_rt = tokio::runtime::Handle::current(); + let reset_component_rt = tokio::runtime::Handle::current(); + let handle = std::thread::spawn(move || { + let log = thread_log; + // Create Engine + let mut engine = Engine::new(); + + // Setup file system access for scripts + let package = FilesystemPackage::new(); + package.register_into_engine(&mut engine); + + // Standard date formats + let package = ChronoPackage::new(); + package.register_into_engine(&mut engine); + + // Additional string functions + let package = MoreStringPackage::new(); + package.register_into_engine(&mut engine); + + // Setup env access for scripts + let package = EnvironmentPackage::new(); + package.register_into_engine(&mut engine); + + // Don't limit resources for now. + engine.set_max_expr_depths(0, 0); + + // Compile the script + let program = match std::fs::read_to_string(&script) { + Ok(content) => content, + Err(e) => { + return Err(anyhow!( + "failed to read {}: {}", + script.display(), + e + )); + } + }; + + let script_file_name: String = + script.file_name().unwrap().to_string_lossy().into(); + + // Construct argv for the script and canonicalize the script path. + let pb = std::fs::canonicalize(&script) + .context("Cannot canonicalize {&script}")?; + let script_dir = pb + .parent() + .context("Cannot get parent dir of {&script}")? + .display() + .to_string(); + let argv0 = pb.display().to_string(); + + let rot_info_sp = sp.clone(); + engine.register_fn("rot_boot_info", move |context: NativeCallContext| -> Result> { + rot_info_rt + .block_on(async { + match rot_info_sp.rot_state(3).await { + Ok(v) => { + let j = serde_json::to_value(v).map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("{}", e).into(), context.call_position())))?; + Ok(serde_json::from_value::(j).map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("{}", e).into(), context.call_position())))?) + } + Err(e) => Err(Box::new(EvalAltResult::ErrorRuntime(format!("{}", e).into(), context.call_position()))) + } + }) + }); + + let cmpa_sp = sp.clone(); + engine.register_fn( + "read_cmpa", + move |context: NativeCallContext| -> Result> { + read_cmpa(context, cmpa_sp.clone(), cmpa_rt.clone()) + }, + ); + + let cfpa_sp = sp.clone(); + engine.register_fn( + "read_cfpa", + move |context: NativeCallContext| -> Result> { + read_cfpa(context, cfpa_sp.clone(), cfpa_rt.clone()) + }, + ); + + let start_host_flash_hash_sp = sp.clone(); + engine.register_fn( + "start_host_flash_hash", + move |context: NativeCallContext, + slot: i64| + -> Result> { + start_host_flash_hash( + context, + start_host_flash_hash_sp.clone(), + start_host_flash_hash_rt.clone(), + slot as u16, + ) + }, + ); + + let get_host_flash_hash_sp = sp.clone(); + engine.register_fn( + "get_host_flash_hash", + move |context: NativeCallContext, + slot: i64| + -> Result> { + get_host_flash_hash( + context, + get_host_flash_hash_sp.clone(), + get_host_flash_hash_rt.clone(), + slot as u16, + ) + }, + ); + + let reset_component_sp = sp.clone(); + engine.register_fn( + "reset_component", + move |context: NativeCallContext, + component: &str| + -> Result> { + reset_component( + context, + reset_component_sp.clone(), + reset_component_rt.clone(), + component, + ) + }, + ); + + let rhai_log = log.clone(); + engine.on_debug(move |x, src, pos| { + let src: String = if let Some(src) = src { + std::path::Path::new(src) + .file_name() + .unwrap() + .to_string_lossy() + .into() + } else { + script_file_name.clone().into() + }; + let location = format!("{src}@{pos:?}"); + let x: Vec<&str> = x.trim_matches('"').splitn(2, '|').collect(); + let (level, msg) = if x.len() == 1 { + ("info".to_string(), x[0].to_string()) + } else { + let level = x[0].to_string().to_lowercase(); + let msg = x[1].to_string(); + match level.as_str() { + "trace" => ("trace".to_string(), msg), + "debug" => ("debug".to_string(), msg), + "info" => ("info".to_string(), msg), + "warn" => ("warn".to_string(), msg), + "error" => ("error".to_string(), msg), + "crit" => ("crit".to_string(), msg), + _ => ("debug".to_string(), format!("{}|{}", level, msg)), + } + }; + let msg = format!("{location} {msg}"); + match level.as_str() { + "crit" => crit!(rhai_log, "{msg}"), + "debug" => debug!(rhai_log, "{msg}"), + "error" => error!(rhai_log, "{msg}"), + "info" => info!(rhai_log, "{msg}"), + "trace" => trace!(rhai_log, "{msg}"), + "warn" => warn!(rhai_log, "{msg}"), + _ => unreachable!(), + } + }); + + match engine.compile(program) { + Ok(ast) => { + // These variables are visible in the script main() + let mut scope = Scope::new(); + let mut argv = vec![]; + argv.push(argv0); + argv.extend(script_args); + scope.push_dynamic("script_dir", script_dir.into()); + scope.push_dynamic("argv", argv.clone().into()); + scope.push_dynamic( + "rbi_default", + RotBootInfo::HIGHEST_KNOWN_VERSION.to_string().into(), + ); + match engine.call_fn::(&mut scope, &ast, "main", ()) { + Ok(_) => Ok(()), + Err(err) => Err(anyhow!("{err}")), + } + } + Err(e) => Err(anyhow!(format!( + "failed to parse {}: {:?}", + &script.display(), + e + ))), + } + }); + + match handle.join() { + Ok(result) => result, + Err(err) => Err(anyhow!("{:?}", err)), + } +} diff --git a/hash.rhai b/hash.rhai new file mode 100644 index 0000000..4226ef4 --- /dev/null +++ b/hash.rhai @@ -0,0 +1,17 @@ +fn main() { + reset_component("sp"); + start_host_flash_hash(0); + loop { + try + { + let hash = get_host_flash_hash(0); + print("result: "+ hash); + break; + } + catch + { + continue; + } + } + return 0; +}