diff --git a/Cargo.lock b/Cargo.lock index a8f02297a..7630fe693 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1693,11 +1693,14 @@ dependencies = [ "humility-cli", "humility-cmd", "humility-core", + "humility-hif-assembler", "humility-hiffy", "humility-idol", "idol", "indexmap 1.9.3", "parse_int", + "postcard", + "serde_json", ] [[package]] @@ -2430,6 +2433,20 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "humility-hif-assembler" +version = "0.1.0" +dependencies = [ + "anyhow", + "hif", + "humility-core", + "humility-idol", + "idol", + "postcard", + "serde", + "serde_json", +] + [[package]] name = "humility-hiffy" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 4b587608f..87c4e4ea6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ members = [ "humility-core", "humility-doppel", "humility-dump-agent", + "humility-hif-assembler", "humility-hiffy", "humility-i2c", "humility-idol", @@ -117,6 +118,7 @@ capstone = {git = "https://github.com/oxidecomputer/capstone-rs.git"} # Local `path`-based deps humility = { path = "./humility-core", package = "humility-core" } +humility-hif-assembler = { path = "./humility-hif-assembler" } humility-arch-arm = { path = "./humility-arch-arm" } humility-cortex = { path = "./humility-arch-cortex" } humility-cmd = { path = "./humility-cmd", default-features = false } diff --git a/cmd/hiffy/Cargo.toml b/cmd/hiffy/Cargo.toml index a43b746fb..df3c9f5a6 100644 --- a/cmd/hiffy/Cargo.toml +++ b/cmd/hiffy/Cargo.toml @@ -14,8 +14,12 @@ parse_int.workspace = true hif.workspace = true idol.workspace = true +postcard = { version = "0.7.0", features = ["alloc"] } +serde_json.workspace = true + humility.workspace = true humility-cmd.workspace = true humility-cli.workspace = true +humility-hif-assembler.workspace = true humility-hiffy.workspace = true humility-idol.workspace = true diff --git a/cmd/hiffy/src/lib.rs b/cmd/hiffy/src/lib.rs index 13d8332e3..fbc7d54fe 100644 --- a/cmd/hiffy/src/lib.rs +++ b/cmd/hiffy/src/lib.rs @@ -1,3 +1,4 @@ +// // 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/. @@ -48,13 +49,17 @@ use ::idol::syntax::{Operation, Reply}; use anyhow::{Context, Result, bail}; use clap::{CommandFactory, Parser}; +use humility::core::Core; use humility::hubris::*; use humility::warn; use humility_cli::{ExecutionContext, Subcommand}; -use humility_cmd::{Archive, Attach, Command, CommandKind, Dumper, Validate}; +use humility_cmd::{Archive, Command, CommandKind, Dumper}; +use humility_hif_assembler::assembler::TargetConfig; +use humility_hif_assembler::bundle::HifBundle; use humility_hiffy::*; use humility_idol as idol; use std::io::Read; +use std::time::Instant; #[derive(Parser, Debug)] #[clap(name = "hiffy", about = env!("CARGO_PKG_DESCRIPTION"))] @@ -102,6 +107,43 @@ struct HiffyArgs { #[clap(long, short, use_value_delimiter = true, requires = "call")] arguments: Vec, + /// execute a .hif or .hifb program file on a target + #[clap( + long, + conflicts_with_all = &["list", "listfuncs", "call", "verify", "assemble"], + value_name = "FILE" + )] + exec: Option, + + /// verify a .hif program (no target needed) + #[clap( + long, + conflicts_with_all = &["list", "listfuncs", "call", "exec", "assemble"], + value_name = "FILE" + )] + verify: Option, + + /// assemble a .hif program to a .hifb bundle (no target needed) + #[clap( + long, + conflicts_with_all = &["list", "listfuncs", "call", "exec", "verify"], + value_name = "FILE", + requires = "bundle-output", + )] + assemble: Option, + + /// output file for --assemble + #[clap(long, value_name = "FILE")] + bundle_output: Option, + + /// write assembled bundle to file (use with --exec on a .hif file) + #[clap(long, requires = "exec", value_name = "FILE")] + save_bundle: Option, + + /// output results as JSON + #[clap(long)] + json: bool, + /// filter for list output #[clap(use_value_delimiter = true)] filter: Vec, @@ -199,21 +241,30 @@ pub fn hiffy_list(hubris: &HubrisArchive, filter: Vec) -> Result<()> { } fn hiffy(context: &mut ExecutionContext) -> Result<()> { - let core = &mut **context.core.as_mut().unwrap(); let Subcommand::Other(subargs) = context.cli.cmd.as_ref().unwrap(); let hubris = context.archive.as_ref().unwrap(); let subargs = HiffyArgs::try_parse_from(subargs)?; + // + // Offline operations: these only need the archive, not a live + // target. Handle them first so we can return before attaching. + // if subargs.list { hiffy_list(hubris, subargs.filter)?; return Ok(()); - } else if !subargs.filter.is_empty() { - // - // It is likely that the user has provided an argument to a HIF - // call without specifying -a; generate a message that tries to - // point them in the right direction. - // + } + + if let Some(ref path) = subargs.verify { + return hiffy_verify(hubris, path); + } + + if let Some(ref path) = subargs.assemble { + let output = subargs.bundle_output.as_ref().unwrap(); // required by clap + return hiffy_assemble(hubris, path, output); + } + + if !subargs.filter.is_empty() { bail!( "extraneous command line argument; missing {}?", if subargs.call.is_some() { @@ -225,18 +276,33 @@ fn hiffy(context: &mut ExecutionContext) -> Result<()> { } // - // Before we create our HiffyContext, check to see if this is a call and - // we're on a dump; running call on a dump always fails (obviously?), but - // in the event that we have a HIF mismatch (or any other failure to - // create the HiffyContext) *and* we're running call on a dump, we would - // rather fail with the dump message rather than with the HiffyContext - // creation failure. (Note that -L will still create the HiffyContext, - // even if run on a dump.) + // Online operations: these need a live target. In Unattached + // mode, the core may be None or archive-only; we need to attach + // to a live target for --exec, --call, and -L. // - if subargs.call.is_some() && core.is_dump() { + if context.cli.probe.is_none() && context.cli.ip.is_none() { + bail!( + "this operation requires a target connection; \ + specify a probe (-p) or network address (--ip)" + ); + } + + { + let cli = &context.cli; + let hubris_ref = context.archive.as_ref().unwrap(); + context.core = Some(humility_cmd::attach_live(cli, hubris_ref)?); + } + + let core = &mut **context.core.as_mut().unwrap(); + + if (subargs.call.is_some() || subargs.exec.is_some()) && core.is_dump() { bail!("can't make HIF calls on a dump"); } + if let Some(ref exec_path) = subargs.exec { + return hiffy_exec(hubris, core, &subargs, exec_path); + } + let mut context = HiffyContext::new(hubris, core, subargs.timeout)?; if let Some(call) = subargs.call { @@ -265,7 +331,7 @@ fn hiffy(context: &mut ExecutionContext) -> Result<()> { let op = idol::IdolOperation::new(hubris, func[0], func[1], task)?; - // Very special-case handling: if someone didn't specify `--input`, but + // Special-case handling: if someone didn't specify `--input`, but // is piping data into the `humility` command, then we use `stdin` as // the input source. let input = if let Some(input) = subargs.input { @@ -321,7 +387,7 @@ fn hiffy(context: &mut ExecutionContext) -> Result<()> { } if !subargs.listfuncs { - bail!("expected one of -l, -L, or -c"); + bail!("expected one of -l, -L, -c, or --exec"); } let funcs = context.functions(); @@ -356,15 +422,414 @@ fn hiffy(context: &mut ExecutionContext) -> Result<()> { Ok(()) } +/// Verify a .hif program offline (no target needed). +fn hiffy_verify(hubris: &HubrisArchive, path: &str) -> Result<()> { + let source = std::fs::read_to_string(path) + .with_context(|| format!("reading {path}"))?; + + let config = TargetConfig::from_archive(hubris) + .context("extracting target config from archive")?; + let asm = humility_hif_assembler::assembler::HifAssembler::new(config); + + let report = asm.verify(&source); + print!("{report}"); + + if !report.ok { + bail!("verification failed"); + } + + let output = + asm.assemble(&source).with_context(|| format!("assembling {path}"))?; + println!("{}", output.stats); + + for w in &output.warnings { + humility::msg!("warning: {w}"); + } + + // Disassemble: show ops in raw syntax with hex comments + println!( + "Ops ({} bytes, {} ops):\n{}", + output.bundle.text.len(), + output.ops.len(), + asm.disassemble(&output.ops), + ); + + Ok(()) +} + +/// Assemble a .hif program to a .hifb bundle offline (no target needed). +fn hiffy_assemble( + hubris: &HubrisArchive, + path: &str, + output_path: &str, +) -> Result<()> { + let source = std::fs::read_to_string(path) + .with_context(|| format!("reading {path}"))?; + + let config = TargetConfig::from_archive(hubris) + .context("extracting target config from archive")?; + let asm = humility_hif_assembler::assembler::HifAssembler::new(config); + let output = + asm.assemble(&source).with_context(|| format!("assembling {path}"))?; + + for w in &output.warnings { + humility::msg!("warning: {w}"); + } + + output + .bundle + .write_to_file(output_path) + .with_context(|| format!("writing bundle to {output_path}"))?; + + humility::msg!( + "assembled {} ({} bytes text, {} estimated results)", + output_path, + output.bundle.text.len(), + output.stats.total_i2c_transactions() + output.stats.idol_calls, + ); + println!("{}", output.stats); + + Ok(()) +} + +/// Execute a .hif (text) or .hifb (bundle) program on the target. +fn hiffy_exec( + hubris: &HubrisArchive, + core: &mut dyn Core, + subargs: &HiffyArgs, + path: &str, +) -> Result<()> { + let is_bundle = path.ends_with(".hifb"); + + let (ops, stats, source_text) = if is_bundle { + // Load pre-assembled bundle + let bundle = HifBundle::read_from_file(path) + .with_context(|| format!("reading bundle {path}"))?; + + // Validate image ID + if let Some(image_id) = hubris.image_id() { + bundle.validate_image_id(image_id)?; + } + + let ops: Vec = postcard::from_bytes(&bundle.text) + .context("deserializing ops from bundle")?; + let source = bundle.metadata.source_text.clone(); + + // Compute stats from source if available + let stats = source.as_ref().and_then(|src| { + humility_hif_assembler::parser::parse(src).ok().map(|prog| { + humility_hif_assembler::stats::compute_stats(&prog.statements) + }) + }); + + (ops, stats, source) + } else { + // Assemble from .hif text + let source = std::fs::read_to_string(path) + .with_context(|| format!("reading {path}"))?; + + let config = TargetConfig::from_archive(hubris) + .context("extracting target config from archive")?; + let asm = humility_hif_assembler::assembler::HifAssembler::new(config); + let output = asm + .assemble(&source) + .with_context(|| format!("assembling {path}"))?; + + for w in &output.warnings { + humility::msg!("warning: {w}"); + } + + // Save bundle if requested + if let Some(ref save_path) = subargs.save_bundle { + output + .bundle + .write_to_file(save_path) + .with_context(|| format!("writing bundle to {save_path}"))?; + humility::msg!("saved bundle to {save_path}"); + } + + (output.ops, Some(output.stats), Some(source)) + }; + + // Print program info + if let Some(ref stats) = stats { + humility::msg!("program: {path}"); + humility::msg!( + "expected: {} I2C transactions, {} Idol calls", + stats.total_i2c_transactions(), + stats.idol_calls, + ); + } + + // Build result kind list for decoding. Each result-producing + // statement maps to a kind (Idol or Raw). + let result_kinds = + source_text.as_ref().and_then(|src| build_result_kinds(src).ok()); + + // Execute via HiffyContext + let mut hctx = HiffyContext::new(hubris, core, subargs.timeout)?; + let start = Instant::now(); + let results = hctx.run(core, &ops, None)?; + let elapsed = start.elapsed(); + + // Count successes and errors + let n_ok = results.iter().filter(|r| r.is_ok()).count(); + let n_err = results.iter().filter(|r| r.is_err()).count(); + + if subargs.json { + // JSON output with decoded samples + let mut decoded_samples = vec![]; + let sample_indices = result_sample_indices(&results, 3, 3); + for i in &sample_indices { + let decoded = + decode_result(hubris, &results[*i], result_kinds.as_ref(), *i); + decoded_samples.push(serde_json::json!({ + "index": i, + "value": decoded, + })); + } + + let json = serde_json::json!({ + "ok": n_err == 0, + "results": results.len(), + "successes": n_ok, + "errors": n_err, + "elapsed_ms": elapsed.as_millis() as u64, + "stats": stats.as_ref().map(|s| serde_json::json!({ + "i2c_transactions": s.total_i2c_transactions(), + "i2c_read_bytes": s.i2c_read_bytes, + "i2c_write_bytes": s.i2c_write_bytes, + "idol_calls": s.idol_calls, + "mux_switches": s.mux_switches, + "buses": s.buses_touched, + })), + "samples": decoded_samples, + "source": source_text, + }); + println!("{}", serde_json::to_string_pretty(&json)?); + } else { + // Human output + humility::msg!( + "executed in {:.1}ms: {} results ({} ok, {} err)", + elapsed.as_secs_f64() * 1000.0, + results.len(), + n_ok, + n_err, + ); + + if n_err > 0 { + // Show all errors (up to 10), decoded + let mut shown = 0; + for (i, result) in results.iter().enumerate() { + if result.is_err() { + let decoded = + decode_result(hubris, result, result_kinds.as_ref(), i); + humility::msg!(" [{i}] {decoded}"); + shown += 1; + if shown >= 10 { + humility::msg!( + " ... and {} more errors", + n_err - shown, + ); + break; + } + } + } + } + + // Show first and last success as samples + if n_ok > 0 && results.len() <= 50 { + // For small result sets, show all + for (i, result) in results.iter().enumerate() { + let decoded = + decode_result(hubris, result, result_kinds.as_ref(), i); + humility::msg!(" [{i}] {decoded}"); + } + } else if n_ok > 0 { + // For large result sets, show first and last + let decoded = + decode_result(hubris, &results[0], result_kinds.as_ref(), 0); + humility::msg!(" [0] {decoded}"); + if results.len() > 2 { + humility::msg!( + " ... ({} more results) ...", + results.len() - 2 + ); + } + let last = results.len() - 1; + let decoded = decode_result( + hubris, + &results[last], + result_kinds.as_ref(), + last, + ); + humility::msg!(" [{last}] {decoded}"); + } + } + + if n_err > 0 { + bail!( + "program completed with {} error{} out of {} results", + n_err, + if n_err == 1 { "" } else { "s" }, + results.len(), + ); + } + + Ok(()) +} + +/// What produced a given result — used for decoding. +#[derive(Clone)] +enum ResultKind { + /// An Idol call: (interface, operation). + Idol(String, String), + /// Raw bytes (I2C, QSPI, etc.) + Raw, +} + +/// Build a list of result kinds by walking the parsed program and +/// expanding loops. One entry per expected result, in order. +fn build_result_kinds(source: &str) -> Result> { + let parsed = humility_hif_assembler::parser::parse(source) + .map_err(|e| anyhow::anyhow!("{e}"))?; + let mut kinds = vec![]; + walk_for_kinds(&parsed.statements, &mut kinds); + Ok(kinds) +} + +fn walk_for_kinds( + stmts: &[humility_hif_assembler::parser::Located< + humility_hif_assembler::parser::Statement, + >], + kinds: &mut Vec, +) { + use humility_hif_assembler::parser::Statement; + + for stmt in stmts { + match &stmt.value { + Statement::IdolCall { interface, operation, .. } => { + kinds.push(ResultKind::Idol( + interface.clone(), + operation.clone(), + )); + } + Statement::I2cRead { .. } + | Statement::I2cWrite { .. } + | Statement::I2cScan { .. } + | Statement::I2cRegScan { .. } + | Statement::Call { .. } => { + kinds.push(ResultKind::Raw); + } + Statement::Repeat { count, body, .. } => { + let start = kinds.len(); + walk_for_kinds(body, kinds); + let body_kinds: Vec<_> = kinds[start..].to_vec(); + for _ in 1..*count { + kinds.extend(body_kinds.iter().cloned()); + } + } + Statement::Sleep { .. } | Statement::Raw { .. } => {} + } + } +} + +/// Decode a single result for display. +/// +/// For Idol calls, this creates an `IdolOperation` via DWARF lookup +/// on each call. This is fine for sampled results but would benefit +/// from caching if we ever decode all results in a large set. +fn decode_result( + hubris: &HubrisArchive, + result: &Result, humility_hiffy::IpcError>, + kinds: Option<&Vec>, + index: usize, +) -> String { + let kind = kinds.and_then(|k| k.get(index)); + + match (result, kind) { + (Ok(bytes), Some(ResultKind::Idol(iface, op_name))) => { + if let Ok(op) = + idol::IdolOperation::new(hubris, iface, op_name, None) + { + match hiffy_decode( + hubris, + &op, + Ok::<_, IpcError>(bytes.clone()), + ) { + Ok(decoded) => format!( + "{iface}.{op_name}() => {}", + hiffy_format_result(hubris, decoded) + ), + Err(_) => { + format!("{iface}.{op_name}() => Ok({:02x?})", bytes) + } + } + } else { + format!("Ok({:02x?})", bytes) + } + } + (Err(e), Some(ResultKind::Idol(iface, op_name))) => { + if let Ok(op) = + idol::IdolOperation::new(hubris, iface, op_name, None) + { + match hiffy_decode(hubris, &op, Err::, _>(*e)) { + Ok(decoded) => format!( + "{iface}.{op_name}() => {}", + hiffy_format_result(hubris, decoded) + ), + Err(_) => { + format!("{iface}.{op_name}() => Err({e:?})") + } + } + } else { + format!("Err({e:?})") + } + } + (Ok(bytes), _) => format!("Ok({:02x?})", bytes), + (Err(e), _) => format!("Err({e:?})"), + } +} + +/// Pick sample indices: first N errors, first M successes, last success. +fn result_sample_indices( + results: &[Result, humility_hiffy::IpcError>], + max_errors: usize, + max_ok: usize, +) -> Vec { + let mut indices = vec![]; + let mut err_count = 0; + let mut ok_count = 0; + let mut last_ok = None; + + for (i, r) in results.iter().enumerate() { + if r.is_err() && err_count < max_errors { + indices.push(i); + err_count += 1; + } else if r.is_ok() { + if ok_count < max_ok { + indices.push(i); + } + ok_count += 1; + last_ok = Some(i); + } + } + + // Always include the last success if not already there + if let Some(last) = last_ok.filter(|l| !indices.contains(l)) { + indices.push(last); + } + + indices.sort(); + indices.dedup(); + indices +} + pub fn init() -> Command { Command { app: HiffyArgs::command(), name: "hiffy", run: hiffy, - kind: CommandKind::Attached { - archive: Archive::Required, - attach: Attach::Any, - validate: Validate::Booted, - }, + kind: CommandKind::Unattached { archive: Archive::Required }, } } diff --git a/humility-bin/tests/cmd/counters-arg/counters-arg.ouray.34.fails.stderr b/humility-bin/tests/cmd/counters-arg/counters-arg.ouray.34.fails.stderr index 8c270888d..b8fdbf6eb 100644 --- a/humility-bin/tests/cmd/counters-arg/counters-arg.ouray.34.fails.stderr +++ b/humility-bin/tests/cmd/counters-arg/counters-arg.ouray.34.fails.stderr @@ -1 +1 @@ -humility counters failed: must provide a Hubris archive or dump +humility counters failed: must provide a Hubris archive, dump, or name diff --git a/humility-bin/tests/cmd/counters-csv-full/counters-csv-full.ouray.34.fails.stderr b/humility-bin/tests/cmd/counters-csv-full/counters-csv-full.ouray.34.fails.stderr index 8c270888d..b8fdbf6eb 100644 --- a/humility-bin/tests/cmd/counters-csv-full/counters-csv-full.ouray.34.fails.stderr +++ b/humility-bin/tests/cmd/counters-csv-full/counters-csv-full.ouray.34.fails.stderr @@ -1 +1 @@ -humility counters failed: must provide a Hubris archive or dump +humility counters failed: must provide a Hubris archive, dump, or name diff --git a/humility-bin/tests/cmd/counters-csv/counters-csv.ouray.34.fails.stderr b/humility-bin/tests/cmd/counters-csv/counters-csv.ouray.34.fails.stderr index 8c270888d..b8fdbf6eb 100644 --- a/humility-bin/tests/cmd/counters-csv/counters-csv.ouray.34.fails.stderr +++ b/humility-bin/tests/cmd/counters-csv/counters-csv.ouray.34.fails.stderr @@ -1 +1 @@ -humility counters failed: must provide a Hubris archive or dump +humility counters failed: must provide a Hubris archive, dump, or name diff --git a/humility-bin/tests/cmd/counters-full/counters-full.ouray.34.fails.stderr b/humility-bin/tests/cmd/counters-full/counters-full.ouray.34.fails.stderr index 8c270888d..b8fdbf6eb 100644 --- a/humility-bin/tests/cmd/counters-full/counters-full.ouray.34.fails.stderr +++ b/humility-bin/tests/cmd/counters-full/counters-full.ouray.34.fails.stderr @@ -1 +1 @@ -humility counters failed: must provide a Hubris archive or dump +humility counters failed: must provide a Hubris archive, dump, or name diff --git a/humility-bin/tests/cmd/counters-ipc-filtered/counters-ipc-filtered.ouray.34.fails.stderr b/humility-bin/tests/cmd/counters-ipc-filtered/counters-ipc-filtered.ouray.34.fails.stderr index 8c270888d..b8fdbf6eb 100644 --- a/humility-bin/tests/cmd/counters-ipc-filtered/counters-ipc-filtered.ouray.34.fails.stderr +++ b/humility-bin/tests/cmd/counters-ipc-filtered/counters-ipc-filtered.ouray.34.fails.stderr @@ -1 +1 @@ -humility counters failed: must provide a Hubris archive or dump +humility counters failed: must provide a Hubris archive, dump, or name diff --git a/humility-bin/tests/cmd/counters-ipc-full/counters-ipc-full.ouray.34.fails.stderr b/humility-bin/tests/cmd/counters-ipc-full/counters-ipc-full.ouray.34.fails.stderr index 8c270888d..b8fdbf6eb 100644 --- a/humility-bin/tests/cmd/counters-ipc-full/counters-ipc-full.ouray.34.fails.stderr +++ b/humility-bin/tests/cmd/counters-ipc-full/counters-ipc-full.ouray.34.fails.stderr @@ -1 +1 @@ -humility counters failed: must provide a Hubris archive or dump +humility counters failed: must provide a Hubris archive, dump, or name diff --git a/humility-bin/tests/cmd/counters-ipc/counters-ipc.ouray.34.fails.stderr b/humility-bin/tests/cmd/counters-ipc/counters-ipc.ouray.34.fails.stderr index 8c270888d..b8fdbf6eb 100644 --- a/humility-bin/tests/cmd/counters-ipc/counters-ipc.ouray.34.fails.stderr +++ b/humility-bin/tests/cmd/counters-ipc/counters-ipc.ouray.34.fails.stderr @@ -1 +1 @@ -humility counters failed: must provide a Hubris archive or dump +humility counters failed: must provide a Hubris archive, dump, or name diff --git a/humility-bin/tests/cmd/counters-json/counters-json.ouray.34.fails.stderr b/humility-bin/tests/cmd/counters-json/counters-json.ouray.34.fails.stderr index 8c270888d..b8fdbf6eb 100644 --- a/humility-bin/tests/cmd/counters-json/counters-json.ouray.34.fails.stderr +++ b/humility-bin/tests/cmd/counters-json/counters-json.ouray.34.fails.stderr @@ -1 +1 @@ -humility counters failed: must provide a Hubris archive or dump +humility counters failed: must provide a Hubris archive, dump, or name diff --git a/humility-bin/tests/cmd/counters-list/counters-list.ouray.34.fails.stderr b/humility-bin/tests/cmd/counters-list/counters-list.ouray.34.fails.stderr index 8c270888d..b8fdbf6eb 100644 --- a/humility-bin/tests/cmd/counters-list/counters-list.ouray.34.fails.stderr +++ b/humility-bin/tests/cmd/counters-list/counters-list.ouray.34.fails.stderr @@ -1 +1 @@ -humility counters failed: must provide a Hubris archive or dump +humility counters failed: must provide a Hubris archive, dump, or name diff --git a/humility-bin/tests/cmd/counters/counters.ouray.34.fails.stderr b/humility-bin/tests/cmd/counters/counters.ouray.34.fails.stderr index 8c270888d..b8fdbf6eb 100644 --- a/humility-bin/tests/cmd/counters/counters.ouray.34.fails.stderr +++ b/humility-bin/tests/cmd/counters/counters.ouray.34.fails.stderr @@ -1 +1 @@ -humility counters failed: must provide a Hubris archive or dump +humility counters failed: must provide a Hubris archive, dump, or name diff --git a/humility-bin/tests/cmd/hiffy-list/hiffy-list.flash-ram-mismatch.0.stderr b/humility-bin/tests/cmd/hiffy-list/hiffy-list.flash-ram-mismatch.0.stderr index f9ddfc68f..da2ede4a0 100644 --- a/humility-bin/tests/cmd/hiffy-list/hiffy-list.flash-ram-mismatch.0.stderr +++ b/humility-bin/tests/cmd/hiffy-list/hiffy-list.flash-ram-mismatch.0.stderr @@ -1,2 +1 @@ humility: attached to dump -humility hiffy failed: target does not appear booted and PC 0x1ff0a6e0 is unknown; is system executing boot ROM or other program? diff --git a/humility-bin/tests/cmd/hiffy-list/hiffy-list.flash-ram-mismatch.0.stdout b/humility-bin/tests/cmd/hiffy-list/hiffy-list.flash-ram-mismatch.0.stdout index e69de29bb..6fd8cb603 100644 --- a/humility-bin/tests/cmd/hiffy-list/hiffy-list.flash-ram-mismatch.0.stdout +++ b/humility-bin/tests/cmd/hiffy-list/hiffy-list.flash-ram-mismatch.0.stdout @@ -0,0 +1,341 @@ +INTERFACE TASK +Jefe jefe + | + +--> Jefe.get_state + | u32 + | + +--> Jefe.set_state + | state u32 + | () + | + +--> Jefe.request_reset + | () + | + +--> Jefe.get_reset_reason + | ResetReason + | + +--> Jefe.set_reset_reason + reason ResetReason + () + +INTERFACE TASK +Net net + | + +--> Net.recv_packet + | socket SocketName + | large_payload_behavior LargePayloadBehavior + | UdpMetadata + | RecvError + | + +--> Net.send_packet + | socket SocketName + | metadata UdpMetadata + | () + | SendError + | + +--> Net.smi_read + | phy u8 + | register u8 + | u16 + | + +--> Net.smi_write + | phy u8 + | register u8 + | value u16 + | () + | + +--> Net.read_phy_reg + | port u8 + | page u16 + | reg u8 + | u16 + | PhyError + | + +--> Net.write_phy_reg + | port u8 + | page u16 + | reg u8 + | value u16 + | () + | PhyError + | + +--> Net.read_ksz8463_mac_count + | usize + | KszError + | + +--> Net.read_ksz8463_mac + | i u16 + | KszMacTableEntry + | KszError + | + +--> Net.read_ksz8463_reg + | reg u16 + | u16 + | KszError + | + +--> Net.get_mac_address + | MacAddress + | + +--> Net.management_link_status + | ManagementLinkStatus + | MgmtError + | + +--> Net.management_counters + ManagementCounters + MgmtError + +INTERFACE TASK +Sys sys + | + +--> Sys.enable_clock_raw + | peripheral u32 + | () + | RccError + | + +--> Sys.disable_clock_raw + | peripheral u32 + | () + | RccError + | + +--> Sys.enter_reset_raw + | peripheral u32 + | () + | RccError + | + +--> Sys.leave_reset_raw + | peripheral u32 + | () + | RccError + | + +--> Sys.gpio_configure_raw + | port Port + | pins u16 + | packed_attributes u16 + | () + | GpioError + | + +--> Sys.gpio_set_reset + | port Port + | set_pins u16 + | reset_pins u16 + | () + | GpioError + | + +--> Sys.gpio_read_input + | port Port + | u16 + | GpioError + | + +--> Sys.gpio_toggle + port Port + pins u16 + () + GpioError + +INTERFACE TASK +Spi spi4_driver + | + +--> Spi.read + | device_index u8 + | () + | SpiError + | + +--> Spi.write + | device_index u8 + | () + | SpiError + | + +--> Spi.exchange + | device_index u8 + | () + | SpiError + | + +--> Spi.lock + | device_index u8 + | cs_state CsState + | () + | SpiError + | + +--> Spi.release + () + SpiError + +INTERFACE TASK +Spi spi2_driver + | + +--> Spi.read + | device_index u8 + | () + | SpiError + | + +--> Spi.write + | device_index u8 + | () + | SpiError + | + +--> Spi.exchange + | device_index u8 + | () + | SpiError + | + +--> Spi.lock + | device_index u8 + | cs_state CsState + | () + | SpiError + | + +--> Spi.release + () + SpiError + +INTERFACE TASK +Thermal thermal + | + +--> Thermal.set_mode_manual + | initial_pwm u8 + | () + | ThermalError + | + +--> Thermal.set_mode_auto + | initial_pwm u8 + | () + | ThermalError + | + +--> Thermal.set_fan_pwm + | index u8 + | pwm u8 + | () + | ThermalError + | + +--> Thermal.disable_watchdog + | () + | ThermalError + | + +--> Thermal.enable_watchdog + timeout_s u8 + () + ThermalError + +INTERFACE TASK +Sequencer gimlet_seq + | + +--> Sequencer.get_state + | PowerState + | SeqError + | + +--> Sequencer.set_state + | state PowerState + | () + | SeqError + | + +--> Sequencer.fans_on + | () + | SeqError + | + +--> Sequencer.fans_off + () + SeqError + +INTERFACE TASK +Hash hash_driver + | + +--> Hash.init_sha256 + | () + | HashError + | + +--> Hash.update + | len u32 + | () + | HashError + | + +--> Hash.finalize_sha256 + | [u8; crate::SHA256_SZ] + | HashError + | + +--> Hash.digest_sha256 + len u32 + [u8; crate::SHA256_SZ] + HashError + +INTERFACE TASK +HostFlash hf + | + +--> HostFlash.read_id + | [u8; 20] + | HfError + | + +--> HostFlash.read_status + | u8 + | HfError + | + +--> HostFlash.bulk_erase + | () + | HfError + | + +--> HostFlash.page_program + | address u32 + | () + | HfError + | + +--> HostFlash.read + | address u32 + | () + | HfError + | + +--> HostFlash.sector_erase + | address u32 + | () + | HfError + | + +--> HostFlash.get_mux + | HfMuxState + | HfError + | + +--> HostFlash.set_mux + | state HfMuxState + | () + | HfError + | + +--> HostFlash.get_dev + | HfDevSelect + | HfError + | + +--> HostFlash.set_dev + | dev HfDevSelect + | () + | HfError + | + +--> HostFlash.hash + address u32 + len u32 + [u8; crate::SHA256_SZ] + HfError + +INTERFACE TASK +Sensor sensor + | + +--> Sensor.get + | id SensorId + | f32 + | SensorError + | + +--> Sensor.post + | id SensorId + | value f32 + | () + | SensorError + | + +--> Sensor.nodata + id SensorId + nodata NoData + () + SensorError + +INTERFACE TASK +Validate validate + | + +--> Validate.validate_i2c + index usize + ValidateOk + ValidateError + diff --git a/humility-bin/tests/cmd/hiffy-list/hiffy-list.flash-ram-mismatch.0.toml b/humility-bin/tests/cmd/hiffy-list/hiffy-list.flash-ram-mismatch.0.toml index 24b451925..857b3fc24 100644 --- a/humility-bin/tests/cmd/hiffy-list/hiffy-list.flash-ram-mismatch.0.toml +++ b/humility-bin/tests/cmd/hiffy-list/hiffy-list.flash-ram-mismatch.0.toml @@ -8,4 +8,4 @@ fs.base = "../cores" bin.name = "humility" args = "-d hubris.core.flash-ram-mismatch.0 hiffy --list" -status.code = 1 +status.code = 0 diff --git a/humility-bin/tests/cmd/hiffy-list/hiffy-list.ouray.34.fails.stderr b/humility-bin/tests/cmd/hiffy-list/hiffy-list.ouray.34.fails.stderr index b603c28d3..d94780f1c 100644 --- a/humility-bin/tests/cmd/hiffy-list/hiffy-list.ouray.34.fails.stderr +++ b/humility-bin/tests/cmd/hiffy-list/hiffy-list.ouray.34.fails.stderr @@ -1 +1 @@ -humility hiffy failed: must provide a Hubris archive or dump +humility hiffy failed: must provide a Hubris archive, dump, or name diff --git a/humility-bin/tests/cmd/hiffy-list/hiffy-list.ouray.55.stderr b/humility-bin/tests/cmd/hiffy-list/hiffy-list.ouray.55.stderr index e70fab4e1..da2ede4a0 100644 --- a/humility-bin/tests/cmd/hiffy-list/hiffy-list.ouray.55.stderr +++ b/humility-bin/tests/cmd/hiffy-list/hiffy-list.ouray.55.stderr @@ -1,5 +1 @@ humility: attached to dump -humility hiffy failed: failed to read TASK_TABLE_SIZE - -Caused by: - read of 4 bytes from invalid address: 0x20000008 diff --git a/humility-bin/tests/cmd/hiffy-list/hiffy-list.ouray.55.toml b/humility-bin/tests/cmd/hiffy-list/hiffy-list.ouray.55.toml index eb880b197..d21d52f62 100644 --- a/humility-bin/tests/cmd/hiffy-list/hiffy-list.ouray.55.toml +++ b/humility-bin/tests/cmd/hiffy-list/hiffy-list.ouray.55.toml @@ -8,4 +8,4 @@ fs.base = "../cores" bin.name = "humility" args = "-d hubris.core.ouray.55 hiffy --list" -status.code = 1 +status.code = 0 diff --git a/humility-bin/tests/cmd/hiffy-list/hiffy-list.ouray.61.stderr b/humility-bin/tests/cmd/hiffy-list/hiffy-list.ouray.61.stderr index e70fab4e1..da2ede4a0 100644 --- a/humility-bin/tests/cmd/hiffy-list/hiffy-list.ouray.61.stderr +++ b/humility-bin/tests/cmd/hiffy-list/hiffy-list.ouray.61.stderr @@ -1,5 +1 @@ humility: attached to dump -humility hiffy failed: failed to read TASK_TABLE_SIZE - -Caused by: - read of 4 bytes from invalid address: 0x20000008 diff --git a/humility-bin/tests/cmd/hiffy-list/hiffy-list.ouray.61.toml b/humility-bin/tests/cmd/hiffy-list/hiffy-list.ouray.61.toml index 41081c9e4..b0f6a87a9 100644 --- a/humility-bin/tests/cmd/hiffy-list/hiffy-list.ouray.61.toml +++ b/humility-bin/tests/cmd/hiffy-list/hiffy-list.ouray.61.toml @@ -8,4 +8,4 @@ fs.base = "../cores" bin.name = "humility" args = "-d hubris.core.ouray.61 hiffy --list" -status.code = 1 +status.code = 0 diff --git a/humility-bin/tests/cmd/hiffy-list/hiffy-list.ouray.62.stderr b/humility-bin/tests/cmd/hiffy-list/hiffy-list.ouray.62.stderr index e70fab4e1..da2ede4a0 100644 --- a/humility-bin/tests/cmd/hiffy-list/hiffy-list.ouray.62.stderr +++ b/humility-bin/tests/cmd/hiffy-list/hiffy-list.ouray.62.stderr @@ -1,5 +1 @@ humility: attached to dump -humility hiffy failed: failed to read TASK_TABLE_SIZE - -Caused by: - read of 4 bytes from invalid address: 0x20000008 diff --git a/humility-bin/tests/cmd/hiffy-list/hiffy-list.ouray.62.toml b/humility-bin/tests/cmd/hiffy-list/hiffy-list.ouray.62.toml index 7331ec694..867de29fe 100644 --- a/humility-bin/tests/cmd/hiffy-list/hiffy-list.ouray.62.toml +++ b/humility-bin/tests/cmd/hiffy-list/hiffy-list.ouray.62.toml @@ -8,4 +8,4 @@ fs.base = "../cores" bin.name = "humility" args = "-d hubris.core.ouray.62 hiffy --list" -status.code = 1 +status.code = 0 diff --git a/humility-bin/tests/cmd/hiffy-list/hiffy-list.panic-on-boot.stderr b/humility-bin/tests/cmd/hiffy-list/hiffy-list.panic-on-boot.stderr index 32b7e6272..da2ede4a0 100644 --- a/humility-bin/tests/cmd/hiffy-list/hiffy-list.panic-on-boot.stderr +++ b/humility-bin/tests/cmd/hiffy-list/hiffy-list.panic-on-boot.stderr @@ -1,2 +1 @@ humility: attached to dump -humility hiffy failed: kernel has panicked on boot: "panicked at 'assertion failed: `(left == right)`/n left: `1`,/n right: `2`', app/gimlet/src/main.rs:118:5" diff --git a/humility-bin/tests/cmd/hiffy-list/hiffy-list.panic-on-boot.stdout b/humility-bin/tests/cmd/hiffy-list/hiffy-list.panic-on-boot.stdout index e69de29bb..34d65ad03 100644 --- a/humility-bin/tests/cmd/hiffy-list/hiffy-list.panic-on-boot.stdout +++ b/humility-bin/tests/cmd/hiffy-list/hiffy-list.panic-on-boot.stdout @@ -0,0 +1,561 @@ +INTERFACE TASK +Jefe jefe + | + +--> Jefe.get_state + | u32 + | + +--> Jefe.set_state + | state u32 + | () + | + +--> Jefe.request_reset + | () + | + +--> Jefe.get_reset_reason + | ResetReason + | + +--> Jefe.set_reset_reason + reason ResetReason + () + +INTERFACE TASK +Net net + | + +--> Net.recv_packet + | socket SocketName + | large_payload_behavior LargePayloadBehavior + | UdpMetadata + | RecvError + | + +--> Net.send_packet + | socket SocketName + | metadata UdpMetadata + | () + | SendError + | + +--> Net.smi_read + | phy u8 + | register u8 + | u16 + | + +--> Net.smi_write + | phy u8 + | register u8 + | value u16 + | () + | + +--> Net.read_phy_reg + | port u8 + | page u16 + | reg u8 + | u16 + | PhyError + | + +--> Net.write_phy_reg + | port u8 + | page u16 + | reg u8 + | value u16 + | () + | PhyError + | + +--> Net.read_ksz8463_mac_count + | usize + | KszError + | + +--> Net.read_ksz8463_mac + | i u16 + | KszMacTableEntry + | KszError + | + +--> Net.read_ksz8463_reg + | reg u16 + | u16 + | KszError + | + +--> Net.get_mac_address + | MacAddress + | + +--> Net.get_spare_mac_addresses + | MacAddressBlock + | + +--> Net.management_link_status + | ManagementLinkStatus + | MgmtError + | + +--> Net.management_counters + ManagementCounters + MgmtError + +INTERFACE TASK +Sys sys + | + +--> Sys.enable_clock_raw + | peripheral u32 + | () + | RccError + | + +--> Sys.disable_clock_raw + | peripheral u32 + | () + | RccError + | + +--> Sys.enter_reset_raw + | peripheral u32 + | () + | RccError + | + +--> Sys.leave_reset_raw + | peripheral u32 + | () + | RccError + | + +--> Sys.gpio_configure_raw + | port Port + | pins u16 + | packed_attributes u16 + | () + | GpioError + | + +--> Sys.gpio_set_reset + | port Port + | set_pins u16 + | reset_pins u16 + | () + | GpioError + | + +--> Sys.gpio_read_input + | port Port + | u16 + | GpioError + | + +--> Sys.gpio_toggle + port Port + pins u16 + () + GpioError + +INTERFACE TASK +Spi spi4_driver + | + +--> Spi.read + | device_index u8 + | () + | SpiError + | + +--> Spi.write + | device_index u8 + | () + | SpiError + | + +--> Spi.exchange + | device_index u8 + | () + | SpiError + | + +--> Spi.lock + | device_index u8 + | cs_state CsState + | () + | SpiError + | + +--> Spi.release + () + SpiError + +INTERFACE TASK +Spi spi2_driver + | + +--> Spi.read + | device_index u8 + | () + | SpiError + | + +--> Spi.write + | device_index u8 + | () + | SpiError + | + +--> Spi.exchange + | device_index u8 + | () + | SpiError + | + +--> Spi.lock + | device_index u8 + | cs_state CsState + | () + | SpiError + | + +--> Spi.release + () + SpiError + +INTERFACE TASK +Thermal thermal + | + +--> Thermal.set_mode_manual + | initial_pwm u8 + | () + | ThermalError + | + +--> Thermal.set_mode_auto + | () + | ThermalError + | + +--> Thermal.get_mode + | ThermalMode + | ThermalError + | + +--> Thermal.get_auto_state + | ThermalAutoState + | ThermalError + | + +--> Thermal.set_fan_pwm + | index u8 + | pwm u8 + | () + | ThermalError + | + +--> Thermal.disable_watchdog + | () + | ThermalError + | + +--> Thermal.enable_watchdog + | timeout_s u8 + | () + | ThermalError + | + +--> Thermal.set_pid + | z f32 + | p f32 + | i f32 + | d f32 + | () + | ThermalError + | + +--> Thermal.get_margin + | f32 + | ThermalError + | + +--> Thermal.set_margin + | margin f32 + | () + | ThermalError + | + +--> Thermal.update_dynamic_input + | index usize + | time u64 + | model ThermalProperties + | temperature Celsius + | () + | ThermalError + | + +--> Thermal.remove_dynamic_input + | index usize + | () + | ThermalError + | + +--> Thermal.get_runtime + u64 + ThermalError + +INTERFACE TASK +Sequencer gimlet_seq + | + +--> Sequencer.get_state + | PowerState + | SeqError + | + +--> Sequencer.set_state + | state PowerState + | () + | SeqError + | + +--> Sequencer.fans_on + | () + | SeqError + | + +--> Sequencer.fans_off + () + SeqError + +INTERFACE TASK +Hash hash_driver + | + +--> Hash.init_sha256 + | () + | HashError + | + +--> Hash.update + | len u32 + | () + | HashError + | + +--> Hash.finalize_sha256 + | [u8; crate::SHA256_SZ] + | HashError + | + +--> Hash.digest_sha256 + len u32 + [u8; crate::SHA256_SZ] + HashError + +INTERFACE TASK +HostFlash hf + | + +--> HostFlash.read_id + | [u8; 20] + | HfError + | + +--> HostFlash.capacity + | usize + | HfError + | + +--> HostFlash.read_status + | u8 + | HfError + | + +--> HostFlash.bulk_erase + | () + | HfError + | + +--> HostFlash.page_program + | address u32 + | () + | HfError + | + +--> HostFlash.read + | address u32 + | () + | HfError + | + +--> HostFlash.sector_erase + | address u32 + | () + | HfError + | + +--> HostFlash.get_mux + | HfMuxState + | HfError + | + +--> HostFlash.set_mux + | state HfMuxState + | () + | HfError + | + +--> HostFlash.get_dev + | HfDevSelect + | HfError + | + +--> HostFlash.set_dev + | dev HfDevSelect + | () + | HfError + | + +--> HostFlash.hash + address u32 + len u32 + [u8; crate::SHA256_SZ] + HfError + +INTERFACE TASK +Update update_server + | + +--> Update.block_size + | usize + | UpdateError + | + +--> Update.prep_image_update + | image_type UpdateTarget + | () + | UpdateError + | + +--> Update.write_one_block + | block_num usize + | () + | UpdateError + | + +--> Update.abort_update + | () + | UpdateError + | + +--> Update.finish_image_update + | () + | UpdateError + | + +--> Update.current_version + ImageVersion + +INTERFACE TASK +Sensor sensor + | + +--> Sensor.get + | id SensorId + | f32 + | SensorError + | + +--> Sensor.get_reading + | id SensorId + | Reading + | SensorError + | + +--> Sensor.post + | id SensorId + | value f32 + | timestamp u64 + | () + | SensorError + | + +--> Sensor.nodata + | id SensorId + | nodata NoData + | timestamp u64 + | () + | SensorError + | + +--> Sensor.get_nerrors + id SensorId + u32 + SensorError + +INTERFACE TASK +HostSpComms host_sp_comms + | + +--> HostSpComms.set_status + | status u64 + | () + | HostSpCommsError + | + +--> HostSpComms.get_status + Status + HostSpCommsError + +INTERFACE TASK +ControlPlaneAgent control_plane_agent + | + +--> ControlPlaneAgent.fetch_host_phase2_data + | image_hash [u8; 32] + | offset u64 + | notification_bit u8 + | () + | ControlPlaneAgentError + | + +--> ControlPlaneAgent.get_host_phase2_data + | image_hash [u8; 32] + | offset u64 + | usize + | ControlPlaneAgentError + | + +--> ControlPlaneAgent.get_startup_options + | HostStartupOptions + | ControlPlaneAgentError + | + +--> ControlPlaneAgent.set_startup_options + | startup_options u64 + | () + | ControlPlaneAgentError + | + +--> ControlPlaneAgent.identity + | Identity + | + +--> ControlPlaneAgent.get_uart_client + | UartClient + | + +--> ControlPlaneAgent.set_humility_uart_client + | attach bool + | () + | ControlPlaneAgentError + | + +--> ControlPlaneAgent.uart_read + | usize + | ControlPlaneAgentError + | + +--> ControlPlaneAgent.uart_write + usize + ControlPlaneAgentError + +INTERFACE TASK +SpRot sprot + | + +--> SpRot.send_recv + | msgtype MsgType + | Received + | SprotError + | + +--> SpRot.send_recv_retries + | msgtype MsgType + | attempts u16 + | Received + | SprotError + | + +--> SpRot.status + | Status + | SprotError + | + +--> SpRot.pulse_cs + | delay u16 + | PulseStatus + | SprotError + | + +--> SpRot.rot_sink + | count u16 + | size u16 + | SinkStatus + | SprotError + | + +--> SpRot.block_size + | usize + | SprotError + | + +--> SpRot.prep_image_update + | image_type UpdateTarget + | () + | SprotError + | + +--> SpRot.write_one_block + | block_num u32 + | () + | SprotError + | + +--> SpRot.abort_update + | () + | SprotError + | + +--> SpRot.finish_image_update + | () + | SprotError + | + +--> SpRot.current_version + ImageVersion + SprotError + +INTERFACE TASK +Validate validate + | + +--> Validate.validate_i2c + index usize + ValidateOk + ValidateError + +INTERFACE TASK +Vpd vpd + | + +--> Vpd.read_tmp117_eeprom + | index u8 + | [u8; 6] + | VpdError + | + +--> Vpd.read + | index u8 + | offset u16 + | [u8; 16] + | VpdError + | + +--> Vpd.write + index u8 + offset u16 + contents u8 + () + VpdError + diff --git a/humility-bin/tests/cmd/hiffy-list/hiffy-list.panic-on-boot.toml b/humility-bin/tests/cmd/hiffy-list/hiffy-list.panic-on-boot.toml index 6d358224a..ca1aa8623 100644 --- a/humility-bin/tests/cmd/hiffy-list/hiffy-list.panic-on-boot.toml +++ b/humility-bin/tests/cmd/hiffy-list/hiffy-list.panic-on-boot.toml @@ -8,4 +8,4 @@ fs.base = "../cores" bin.name = "humility" args = "-d hubris.core.panic-on-boot hiffy --list" -status.code = 1 +status.code = 0 diff --git a/humility-bin/tests/cmd/host-panic/host-panic.ouray.34.fails.stderr b/humility-bin/tests/cmd/host-panic/host-panic.ouray.34.fails.stderr index d5ebc57fa..f1dce3cf6 100644 --- a/humility-bin/tests/cmd/host-panic/host-panic.ouray.34.fails.stderr +++ b/humility-bin/tests/cmd/host-panic/host-panic.ouray.34.fails.stderr @@ -1 +1 @@ -humility host failed: must provide a Hubris archive or dump +humility host failed: must provide a Hubris archive, dump, or name diff --git a/humility-bin/tests/cmd/manifest/manifest.ouray.34.fails.stderr b/humility-bin/tests/cmd/manifest/manifest.ouray.34.fails.stderr index 27665384e..64ef0b251 100644 --- a/humility-bin/tests/cmd/manifest/manifest.ouray.34.fails.stderr +++ b/humility-bin/tests/cmd/manifest/manifest.ouray.34.fails.stderr @@ -1 +1 @@ -humility manifest failed: must provide a Hubris archive or dump +humility manifest failed: must provide a Hubris archive, dump, or name diff --git a/humility-bin/tests/cmd/map/map.ouray.34.fails.stderr b/humility-bin/tests/cmd/map/map.ouray.34.fails.stderr index a512abc2e..646cbea35 100644 --- a/humility-bin/tests/cmd/map/map.ouray.34.fails.stderr +++ b/humility-bin/tests/cmd/map/map.ouray.34.fails.stderr @@ -1 +1 @@ -humility map failed: must provide a Hubris archive or dump +humility map failed: must provide a Hubris archive, dump, or name diff --git a/humility-bin/tests/cmd/readvar-list/readvar-list.ouray.34.fails.stderr b/humility-bin/tests/cmd/readvar-list/readvar-list.ouray.34.fails.stderr index 706a4972b..7f743be5f 100644 --- a/humility-bin/tests/cmd/readvar-list/readvar-list.ouray.34.fails.stderr +++ b/humility-bin/tests/cmd/readvar-list/readvar-list.ouray.34.fails.stderr @@ -1 +1 @@ -humility readvar failed: must provide a Hubris archive or dump +humility readvar failed: must provide a Hubris archive, dump, or name diff --git a/humility-bin/tests/cmd/readvar-ticks/readvar-ticks.ouray.34.fails.stderr b/humility-bin/tests/cmd/readvar-ticks/readvar-ticks.ouray.34.fails.stderr index 706a4972b..7f743be5f 100644 --- a/humility-bin/tests/cmd/readvar-ticks/readvar-ticks.ouray.34.fails.stderr +++ b/humility-bin/tests/cmd/readvar-ticks/readvar-ticks.ouray.34.fails.stderr @@ -1 +1 @@ -humility readvar failed: must provide a Hubris archive or dump +humility readvar failed: must provide a Hubris archive, dump, or name diff --git a/humility-bin/tests/cmd/ringbuf-arg/ringbuf-arg.ouray.34.fails.stderr b/humility-bin/tests/cmd/ringbuf-arg/ringbuf-arg.ouray.34.fails.stderr index 434d367da..4867cc8cd 100644 --- a/humility-bin/tests/cmd/ringbuf-arg/ringbuf-arg.ouray.34.fails.stderr +++ b/humility-bin/tests/cmd/ringbuf-arg/ringbuf-arg.ouray.34.fails.stderr @@ -1 +1 @@ -humility ringbuf failed: must provide a Hubris archive or dump +humility ringbuf failed: must provide a Hubris archive, dump, or name diff --git a/humility-bin/tests/cmd/ringbuf-full-totals/ringbuf-full-totals.ouray.34.fails.stderr b/humility-bin/tests/cmd/ringbuf-full-totals/ringbuf-full-totals.ouray.34.fails.stderr index 434d367da..4867cc8cd 100644 --- a/humility-bin/tests/cmd/ringbuf-full-totals/ringbuf-full-totals.ouray.34.fails.stderr +++ b/humility-bin/tests/cmd/ringbuf-full-totals/ringbuf-full-totals.ouray.34.fails.stderr @@ -1 +1 @@ -humility ringbuf failed: must provide a Hubris archive or dump +humility ringbuf failed: must provide a Hubris archive, dump, or name diff --git a/humility-bin/tests/cmd/ringbuf/ringbuf.ouray.34.fails.stderr b/humility-bin/tests/cmd/ringbuf/ringbuf.ouray.34.fails.stderr index 434d367da..4867cc8cd 100644 --- a/humility-bin/tests/cmd/ringbuf/ringbuf.ouray.34.fails.stderr +++ b/humility-bin/tests/cmd/ringbuf/ringbuf.ouray.34.fails.stderr @@ -1 +1 @@ -humility ringbuf failed: must provide a Hubris archive or dump +humility ringbuf failed: must provide a Hubris archive, dump, or name diff --git a/humility-bin/tests/cmd/sensors-read/sensors-read.ouray.34.fails.stderr b/humility-bin/tests/cmd/sensors-read/sensors-read.ouray.34.fails.stderr index b4ed02fa5..7cc59ed5f 100644 --- a/humility-bin/tests/cmd/sensors-read/sensors-read.ouray.34.fails.stderr +++ b/humility-bin/tests/cmd/sensors-read/sensors-read.ouray.34.fails.stderr @@ -1 +1 @@ -humility sensors failed: must provide a Hubris archive or dump +humility sensors failed: must provide a Hubris archive, dump, or name diff --git a/humility-bin/tests/cmd/sensors/sensors.ouray.34.fails.stderr b/humility-bin/tests/cmd/sensors/sensors.ouray.34.fails.stderr index b4ed02fa5..7cc59ed5f 100644 --- a/humility-bin/tests/cmd/sensors/sensors.ouray.34.fails.stderr +++ b/humility-bin/tests/cmd/sensors/sensors.ouray.34.fails.stderr @@ -1 +1 @@ -humility sensors failed: must provide a Hubris archive or dump +humility sensors failed: must provide a Hubris archive, dump, or name diff --git a/humility-bin/tests/cmd/spd/spd.ouray.34.fails.stderr b/humility-bin/tests/cmd/spd/spd.ouray.34.fails.stderr index 0f634baa4..668912cc8 100644 --- a/humility-bin/tests/cmd/spd/spd.ouray.34.fails.stderr +++ b/humility-bin/tests/cmd/spd/spd.ouray.34.fails.stderr @@ -1 +1 @@ -humility spd failed: must provide a Hubris archive or dump +humility spd failed: must provide a Hubris archive, dump, or name diff --git a/humility-bin/tests/cmd/stackmargin/stackmargin.ouray.34.fails.stderr b/humility-bin/tests/cmd/stackmargin/stackmargin.ouray.34.fails.stderr index 7effde4be..856288bfe 100644 --- a/humility-bin/tests/cmd/stackmargin/stackmargin.ouray.34.fails.stderr +++ b/humility-bin/tests/cmd/stackmargin/stackmargin.ouray.34.fails.stderr @@ -1 +1 @@ -humility stackmargin failed: must provide a Hubris archive or dump +humility stackmargin failed: must provide a Hubris archive, dump, or name diff --git a/humility-bin/tests/cmd/tasks-slvr/tasks-slvr.ouray.34.fails.stderr b/humility-bin/tests/cmd/tasks-slvr/tasks-slvr.ouray.34.fails.stderr index 90f5b92d8..d0bdc0220 100644 --- a/humility-bin/tests/cmd/tasks-slvr/tasks-slvr.ouray.34.fails.stderr +++ b/humility-bin/tests/cmd/tasks-slvr/tasks-slvr.ouray.34.fails.stderr @@ -1 +1 @@ -humility tasks failed: must provide a Hubris archive or dump +humility tasks failed: must provide a Hubris archive, dump, or name diff --git a/humility-bin/tests/cmd/tasks/tasks.ouray.34.fails.stderr b/humility-bin/tests/cmd/tasks/tasks.ouray.34.fails.stderr index 90f5b92d8..d0bdc0220 100644 --- a/humility-bin/tests/cmd/tasks/tasks.ouray.34.fails.stderr +++ b/humility-bin/tests/cmd/tasks/tasks.ouray.34.fails.stderr @@ -1 +1 @@ -humility tasks failed: must provide a Hubris archive or dump +humility tasks failed: must provide a Hubris archive, dump, or name diff --git a/humility-core/src/core.rs b/humility-core/src/core.rs index 26f1fc2a0..98a21cc3d 100644 --- a/humility-core/src/core.rs +++ b/humility-core/src/core.rs @@ -126,6 +126,7 @@ pub trait Core { pub enum NetAgent { UdpRpc, DumpAgent, + Hiffy, } pub fn attach_dump( diff --git a/humility-core/src/hubris.rs b/humility-core/src/hubris.rs index 651e4ea54..eb3f9c452 100644 --- a/humility-core/src/hubris.rs +++ b/humility-core/src/hubris.rs @@ -6708,6 +6708,39 @@ impl HubrisModule { } } } + + /// Looks up enum variants by name, casting to a particular integer type + pub fn get_enum_variants_by_name + TryFrom>( + &self, + hubris: &HubrisArchive, + name: &str, + ) -> Result> + where + >::Error: std::error::Error + Send + Sync + 'static, + >::Error: std::error::Error + Send + Sync + 'static, + { + let Some(enum_ty) = self.lookup_enum_byname(hubris, name)? else { + bail!("could not find enum `{name}`"); + }; + enum_ty + .variants + .iter() + .map(|v| { + let Some(tag) = v.tag else { + bail!("variant `{}` has no tag", v.name); + }; + let t = match tag { + Tag::Signed(i) => T::try_from(i).with_context(|| { + format!("variant tag {i} for {} does not fit", v.name) + })?, + Tag::Unsigned(i) => T::try_from(i).with_context(|| { + format!("variant tag {i} for {} does not fit", v.name) + })?, + }; + Ok((v.name.clone(), t)) + }) + .collect::>>() + } } #[derive(Copy, Clone, Debug, Default)] diff --git a/humility-doppel/src/lib.rs b/humility-doppel/src/lib.rs index 9c68c06ca..c1824b0c8 100644 --- a/humility-doppel/src/lib.rs +++ b/humility-doppel/src/lib.rs @@ -38,7 +38,7 @@ use humility::reflect::{self, Base, Load, Ptr, Value}; use indexmap::IndexMap; use std::convert::TryInto; use std::fmt; -use zerocopy::{AsBytes, LittleEndian, U16, U64}; +use zerocopy::{AsBytes, LittleEndian, U16, U32, U64}; #[derive(Copy, Clone, Debug, Eq, PartialEq, Load)] pub struct TaskDesc { @@ -429,6 +429,20 @@ pub struct RpcHeader { pub nbytes: U16, } +/// Double of the RPC types from `hiffy` (with the `net` feature enabled) +pub mod hiffy { + use super::*; + + #[derive(Copy, Clone, Debug, AsBytes)] + #[repr(C)] + pub struct RpcHeader { + pub image_id: U64, + pub version: U16, + pub operation: U16, + pub arg: U32, + } +} + impl humility::reflect::Load for CountedRingbuf { fn from_value(v: &Value) -> Result { let rb_struct = v.as_struct()?; diff --git a/humility-dump-agent/src/hiffy.rs b/humility-dump-agent/src/hiffy.rs index b56d0804b..303fd03d1 100644 --- a/humility-dump-agent/src/hiffy.rs +++ b/humility-dump-agent/src/hiffy.rs @@ -182,7 +182,7 @@ impl DumpAgent for HiffyDumpAgent<'_> { // returned data size and the Hiffy context's `rdata` array size. let op = self.hubris.get_idol_command("DumpAgent.read_dump")?; let rsize = self.hubris.lookup_type(op.ok)?.size(self.hubris)?; - let chunksize = (self.context.rdata_size() / rsize) - 1; + let chunksize = (self.context.rstack_size() / rsize) - 1; let mut rval = vec![]; loop { diff --git a/humility-hif-assembler/Cargo.toml b/humility-hif-assembler/Cargo.toml new file mode 100644 index 000000000..499fecb8c --- /dev/null +++ b/humility-hif-assembler/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "humility-hif-assembler" +version = "0.1.0" +edition = "2021" +description = "Assembler for HIF (Hubris Interchange Format) text programs" + +[dependencies] +anyhow = "1.0" +hif.workspace = true +idol.workspace = true +humility.workspace = true +humility-idol.workspace = true +postcard = { version = "0.7.0", features = ["alloc"] } +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" diff --git a/humility-hif-assembler/README.md b/humility-hif-assembler/README.md new file mode 100644 index 000000000..d56e6756d --- /dev/null +++ b/humility-hif-assembler/README.md @@ -0,0 +1,388 @@ +# humility-hif-assembler + +Assembler for HIF (Hubris Interchange Format) text programs. + +## Overview + +This crate translates text-based HIF programs into bytecode that the +`hiffy` task on a Hubris SP or RoT can execute. It resolves symbolic +names (bus names, function names, device addresses) against a Hubris +archive, validates programs against target buffer sizes, and produces +bundles with embedded image IDs for safe upload. + +## Quick Start + +```bash +SP_IP=fe80::aa40:25ff:fe05:0500%3 +ARCHIVE=build-sidecar-b-lab-image-default.zip + +# Verify a program offline (no target needed) — shows stats and ops +humility -a $ARCHIVE hiffy --verify stress.hif + +# Assemble to a bundle file (no target needed) +humility -a $ARCHIVE hiffy --assemble stress.hif --bundle-output stress.hifb + +# Unlock the sidecar's network interface +faux-mgs --interface axf2 \ + --discovery-addr "[${SP_IP%\%*}]:11111" \ + monorail unlock -t 3600sec + +# Execute a program on a target over the network +humility -a $ARCHIVE --ip $SP_IP hiffy --exec stress.hif + +# JSON output for scripting +humility -a $ARCHIVE --ip $SP_IP hiffy --exec stress.hif --json +``` + +The assembler checks the archive's `app.toml` to verify that the +target image can accept HIF programs over the network. Specifically, +it checks that the `hiffy` task has the `net` feature enabled. If +it's missing, the assembler warns at assembly time: + +``` +warning: hiffy task does not have 'net' feature; network execution +requires adding features = ["net", "vlan"] and a hiffy socket to +the app.toml (probe execution still works) +``` + +This check runs during `--verify`, `--assemble`, and `--exec`, so +you find out before attempting a network connection that the image +won't support it. + +## Text Format + +Programs are line-oriented with `#` comments and `.let` constants. + +### I2C Operations + +``` +# Read 2 bytes from register 0x00 of device 0x48 on the "mid" bus +i2c_read mid 0x48 reg=0x00 2 + +# With mux routing +i2c_read front 0x50 mux=0x70.1 reg=0x00 16 + +# Write bytes +i2c_write mid 0x48 reg=0x01 0x00,0x80 + +# Scan all addresses on a bus +i2c_scan mid + +# Scan all registers of a device +i2c_regscan mid 0x48 +``` + +Bus names (`mid`, `front`, `rear`, `northeast0`, etc.) come from the +archive's `app.toml`. Explicit `.` syntax (e.g. +`3.H`) is also accepted. + +### Generic Function Calls + +Any HIF function can be called by name with optional numeric +arguments. This covers QSPI, GPIO, Hash, SPI, and any future +functions without needing per-function sugar: + +``` +# No args +call QspiReadId + +# With args +call GpioInput 5 +``` + +Function names come from the `HIFFY_FUNCTIONS` table in the archive +(extracted from DWARF). Both CamelCase (`QspiReadId`) and +snake_case (`qspi_read_id`) are accepted. + +### Idol RPC Calls + +``` +idol Sensor.get id=3 +idol SpRot.status +idol Thermal.get_mode +idol Power.read_mode dev=0 rail=0 index=0 +``` + +The assembler resolves interface and operation names from the +`.idolatry` sections in the archive, encodes arguments, and emits +the appropriate `Send` call. Reply sizes are computed from DWARF +so results are properly captured. + +### Loops + +``` +repeat 200 + i2c_read mid 0x48 reg=0x00 2 +end + +# With sleep between iterations +repeat 100 sleep=10ms + i2c_read mid 0x48 reg=0x00 2 +end +``` + +Loops consume one of the four available HIF labels per nesting +level. Iteration count is limited by RSTACK capacity (~250-680 +results depending on result size). + +### Constants + +``` +.let TEMP_REG 0x00 +.let SENSOR 0x48 +.let ITERATIONS 200 + +repeat $ITERATIONS + i2c_read rear $SENSOR reg=$TEMP_REG 2 +end +``` + +### Sleep + +``` +sleep 50ms +``` + +Values over 100ms are automatically split into multiple Sleep calls. + +### Raw Instructions + +For anything else. Constants are expanded inside raw blocks: + +``` +.let ADDR 0x48 +raw { + push $ADDR + push_none + push 2 + call I2cRead + drop_n 7 +} +``` + +Available: `push`, `push16`, `push32`, `push_none`, `drop`, +`drop_n`, `swap`, `add`, `label`, `branch_gt`, `branch_gte`, +`branch_lt`, `call`, `done`. + +## Output + +### Verify output (`--verify`) + +Shows program stats, expected resource usage, and a disassembly +of the generated HIF ops. The disassembly uses the assembler's +raw syntax (left column) with postcard byte encoding and symbolic +annotations (right column). The left column can be pasted into a +`raw {}` block and re-assembled to produce the same bytecode. +Function IDs are resolved back to names, push values are annotated +with their role (controller, port, address, etc.), and bus/device +names are shown where they can be inferred. + +``` +OK + + text: 31 / 4096 bytes + rstack: 40 / 2048 bytes (est. 5 results) + labels: 1 / 4 + functions: i2c_read + buses: northeast1 +I2C transactions: 5 + reads: 5 (10 bytes) +Buses: northeast1 + +Ops (31 bytes, 18 ops): +raw { + push 0 # 00: 04 00 + push_none # 02: 07 + label 0 # 03: 00 00 loop_start + drop # 05: 02 + push 1 # 06: 04 01 controller (northeast0) + push 1 # 08: 04 01 port + push_none # 0a: 07 mux + push_none # 0b: 07 segment + push 0x48 # 0c: 04 48 address (tmp117) + push 0 # 0e: 04 00 register + push 2 # 10: 04 02 nbytes + call I2cRead # 12: 01 05 + drop_n 7 # 14: 03 07 + push 1 # 16: 04 01 + add # 18: 0a counter += 1 + push 5 # 19: 04 05 limit + branch_gt 0 # 1b: 10 00 loop + done # 1d: 14 +} +``` + +Idol calls are annotated with task and operation info: + +``` +raw { + push 7 # 00: 04 07 task (sprot) + push 1 # 02: 04 01 op_code + push 0 # 04: 04 00 + push 0x18 # 06: 04 18 + call Send # 08: 01 01 + drop_n 4 # 0a: 03 04 + done # 0c: 14 +} +``` + +### Execution output (`--exec`, default) + +``` +humility: program: stress.hif +humility: expected: 50 I2C transactions, 0 Idol calls +humility: executed in 658.2ms: 50 results (50 ok, 0 err) +humility: [0] Ok([0c, cf]) +humility: ... (48 more results) ... +humility: [49] Ok([0c, cf]) +``` + +Idol results are fully decoded using DWARF type info: + +``` +humility: [0] SpRot.status() => SprotStatus { rot: RotStatus { ... }, sp: SpStatus { ... } } +``` + +### JSON output (`--json`) + +```json +{ + "ok": true, + "results": 50, + "successes": 50, + "errors": 0, + "elapsed_ms": 658, + "stats": { + "i2c_transactions": 50, + "i2c_read_bytes": 100, + "buses": ["northeast1"] + }, + "samples": [ + {"index": 0, "value": "Ok([0c, cf])"}, + {"index": 49, "value": "Ok([0c, cf])"} + ] +} +``` + +### Exit codes + +- 0: program ran, no errors +- 1: program ran, errors detected (use `--json` for details) +- Non-zero: assembly or execution failed + +## Scripting + +```bash +#!/bin/bash +result=$(humility -a $ARCHIVE --ip $SP hiffy --exec stress.hif --json) +errors=$(echo "$result" | jq .errors) + +if [ "$errors" -gt 0 ]; then + echo "Failure after $(echo "$result" | jq .successes) successes" + humility -a $ARCHIVE --ip $SP ringbuf i2c_driver + humility -a $ARCHIVE --ip $SP tasks +fi +``` + +## TargetConfig + +`TargetConfig` captures everything the assembler needs from a Hubris +archive in a single serializable struct: + +- Image ID +- I2C bus topology (buses, devices, muxes, sensors) +- HIF function table (names, IDs, argument types, error codes) +- Idol interfaces (operations, argument/reply types, sizes, encoding) +- Buffer sizes (HIFFY_TEXT, HIFFY_DATA, HIFFY_RSTACK) + +It can be extracted from an archive or loaded from a JSON fixture: + +```rust +// From an archive +let config = TargetConfig::from_archive_file("sidecar-b-lab.zip")?; + +// From a checked-in fixture (test data only) +let config: TargetConfig = + serde_json::from_str(&std::fs::read_to_string("fixtures/sidecar-b.json")?)?; + +let asm = HifAssembler::new(config); +``` + +## ProgramBuilder + +For generating programs from Rust (e.g., PRNG-driven fuzz testing): + +```rust +let mut prog = ProgramBuilder::new(); +prog.comment("temperature stress test"); +prog.repeat(200, |body| { + body.i2c_read("rear", 0x48, Some(0x00), 2); +}); +let source = prog.finish(); +let output = asm.assemble(&source)?; +``` + +## Testing + +```bash +# Unit tests (no archive needed) +cargo test -p humility-hif-assembler --lib + +# Fixture-based tests (no archive needed, uses checked-in JSON) +cargo test -p humility-hif-assembler --test fixture_tests + +# Integration tests (requires a built archive) +HUBRIS_ARCHIVE=path/to/archive.zip \ + cargo test -p humility-hif-assembler --test archive_integration + +# Regenerate a fixture from an archive +HUBRIS_ARCHIVE=path/to/archive.zip GENERATE_FIXTURE=1 \ + cargo test -p humility-hif-assembler --test archive_integration \ + generate_fixture -- --nocapture +``` + +## Hubris Image Requirements + +For network execution (`--ip`), the Hubris image must have: + +1. The `hiffy` task with `features = ["net", "vlan"]` +2. A `net` task-slot on the hiffy task +3. A `socket` notification on the hiffy task +4. A UDP socket configured for hiffy: + +```toml +# In the dev.toml or lab.toml overlay: +[tasks.hiffy] +features = ["net", "vlan"] +task-slots = ["net"] +notifications = ["socket"] + +[config.net.sockets.hiffy] +kind = "udp" +owner = {name = "hiffy", notification = "socket"} +port = 11115 +tx = { packets = 3, bytes = 32 } +rx = { packets = 1, bytes = 4096 } +``` + +The hiffy task's priority must be lower than the net task's priority +(higher number = lower priority) to avoid priority inversion. + +Probe execution (`-p`) works with any image that has a hiffy task. + +## Relationship to humility-hiffy and RFD 659 + +This crate overlaps with code in `humility-hiffy`. The overlap is +intentional and designed for eventual convergence. + +`humility-hiffy` is tightly coupled to a live target connection +(`Core` trait) and the humility CLI dispatch model. It cannot be +used as a library for offline program construction, fixture +generation, or scripted test drivers. This crate provides those +capabilities without modifying `humility-hiffy`. + +RFD 659 proposes turning humility into a library. When that happens, +`TargetConfig` could become the serializable contract between archive +loading and program construction, and `HifAssembler` replaces the +per-command Op construction scattered across humility's subcommands. +See `lib.rs` module docs for details. diff --git a/humility-hif-assembler/examples/bus-scan.hif b/humility-hif-assembler/examples/bus-scan.hif new file mode 100644 index 000000000..497f37774 --- /dev/null +++ b/humility-hif-assembler/examples/bus-scan.hif @@ -0,0 +1,10 @@ +# Bus scan: discover all responding devices. +# +# Probes addresses 0-127 on the mid bus. Each address gets a +# 1-byte read; responding devices return Ok, absent devices +# return Err(NoDevice). +# +# Target: gimlet (mid bus, I2C3 port H) +# Expected results: 128 (mix of Ok and Err) + +i2c_scan mid diff --git a/humility-hif-assembler/examples/equivalent-gpio-input.hif b/humility-hif-assembler/examples/equivalent-gpio-input.hif new file mode 100644 index 000000000..97d7a8841 --- /dev/null +++ b/humility-hif-assembler/examples/equivalent-gpio-input.hif @@ -0,0 +1,3 @@ +# Equivalent to: humility gpio --input +# GpioInput takes a pin index (u16) +call GpioInput 5 diff --git a/humility-hif-assembler/examples/equivalent-gpio-toggle.hif b/humility-hif-assembler/examples/equivalent-gpio-toggle.hif new file mode 100644 index 000000000..68011ee72 --- /dev/null +++ b/humility-hif-assembler/examples/equivalent-gpio-toggle.hif @@ -0,0 +1,3 @@ +# Equivalent to: humility gpio --toggle +# GpioToggle takes (pin_index: u16, mask: u8) +call GpioToggle 5 1 diff --git a/humility-hif-assembler/examples/equivalent-hash-digest.hif b/humility-hif-assembler/examples/equivalent-hash-digest.hif new file mode 100644 index 000000000..bcef9d461 --- /dev/null +++ b/humility-hif-assembler/examples/equivalent-hash-digest.hif @@ -0,0 +1,3 @@ +# Equivalent to: humility hash --digest +# HashDigest takes a length argument +call HashDigest 32 diff --git a/humility-hif-assembler/examples/equivalent-i2c-read.hif b/humility-hif-assembler/examples/equivalent-i2c-read.hif new file mode 100644 index 000000000..d8c58aeae --- /dev/null +++ b/humility-hif-assembler/examples/equivalent-i2c-read.hif @@ -0,0 +1,2 @@ +# Equivalent to: humility i2c --bus mid --device 0x48 --register 0x00 +i2c_read mid 0x48 reg=0x00 1 diff --git a/humility-hif-assembler/examples/equivalent-i2c-regscan.hif b/humility-hif-assembler/examples/equivalent-i2c-regscan.hif new file mode 100644 index 000000000..fa031fe06 --- /dev/null +++ b/humility-hif-assembler/examples/equivalent-i2c-regscan.hif @@ -0,0 +1,3 @@ +# Equivalent to: humility i2c --bus mid --device 0x48 --scan +# Scans registers 0x00-0xFF, reads 1 byte from each +i2c_regscan mid 0x48 diff --git a/humility-hif-assembler/examples/equivalent-i2c-scan.hif b/humility-hif-assembler/examples/equivalent-i2c-scan.hif new file mode 100644 index 000000000..033e578b2 --- /dev/null +++ b/humility-hif-assembler/examples/equivalent-i2c-scan.hif @@ -0,0 +1,3 @@ +# Equivalent to: humility i2c --scan --bus mid +# Scans addresses 0-127, reads 1 byte from each +i2c_scan mid diff --git a/humility-hif-assembler/examples/equivalent-idol-sensor-get.hif b/humility-hif-assembler/examples/equivalent-idol-sensor-get.hif new file mode 100644 index 000000000..b0edefb80 --- /dev/null +++ b/humility-hif-assembler/examples/equivalent-idol-sensor-get.hif @@ -0,0 +1,2 @@ +# Equivalent to: humility hiffy -c Sensor.get -a id=0 +idol Sensor.get id=0 diff --git a/humility-hif-assembler/examples/equivalent-qspi-read-id.hif b/humility-hif-assembler/examples/equivalent-qspi-read-id.hif new file mode 100644 index 000000000..4997cbb4d --- /dev/null +++ b/humility-hif-assembler/examples/equivalent-qspi-read-id.hif @@ -0,0 +1,3 @@ +# Equivalent to: humility qspi (read ID) +# Original: vec![Op::Call(qspi_read_id.id), Op::Done] +call QspiReadId diff --git a/humility-hif-assembler/examples/equivalent-qspi-read-status.hif b/humility-hif-assembler/examples/equivalent-qspi-read-status.hif new file mode 100644 index 000000000..8673fb169 --- /dev/null +++ b/humility-hif-assembler/examples/equivalent-qspi-read-status.hif @@ -0,0 +1,3 @@ +# Equivalent to: humility qspi --status +# Original: vec![Op::Call(qspi_read_status.id), Op::Done] +call QspiReadStatus diff --git a/humility-hif-assembler/examples/equivalent-validate-i2c.hif b/humility-hif-assembler/examples/equivalent-validate-i2c.hif new file mode 100644 index 000000000..2c44f9d06 --- /dev/null +++ b/humility-hif-assembler/examples/equivalent-validate-i2c.hif @@ -0,0 +1,6 @@ +# Equivalent to: humility validate +# Calls Validate.validate_i2c for each device index +idol Validate.validate_i2c index=0 +idol Validate.validate_i2c index=1 +idol Validate.validate_i2c index=2 +idol Validate.validate_i2c index=3 diff --git a/humility-hif-assembler/examples/idol-sensor-poll.hif b/humility-hif-assembler/examples/idol-sensor-poll.hif new file mode 100644 index 000000000..7c1013bce --- /dev/null +++ b/humility-hif-assembler/examples/idol-sensor-poll.hif @@ -0,0 +1,16 @@ +# Idol sensor poll: read sensor values via the Sensor task. +# +# Instead of direct I2C reads, this goes through the Idol RPC +# interface to the Sensor task, which returns cached readings. +# This exercises the Send path and is useful for verifying that +# the sensor task is updating values. +# +# Target: gimlet (sensor task) +# Expected results: 100 sensor readings (as f32) + +.let SENSOR_ID 0 +.let ITERATIONS 100 + +repeat $ITERATIONS + idol Sensor.get id=$SENSOR_ID +end diff --git a/humility-hif-assembler/examples/multi-bus-interleave.hif b/humility-hif-assembler/examples/multi-bus-interleave.hif new file mode 100644 index 000000000..de59fb763 --- /dev/null +++ b/humility-hif-assembler/examples/multi-bus-interleave.hif @@ -0,0 +1,17 @@ +# Multi-bus interleave: alternate reads across different I2C buses. +# +# Reads from mid and front buses in alternation. Since these +# are on different I2C controllers (I2C3 and I2C2), the hardware +# can potentially overlap transactions. The hiffy task serializes +# them, but this pattern reveals any cross-bus contention in the +# I2C server task. +# +# Target: gimlet (mid bus + front bus) +# Expected results: 200 (100 per bus) + +.let ITERATIONS 100 + +repeat $ITERATIONS + i2c_read mid 0x24 reg=0x00 2 # tps546b24a on mid + i2c_read front 0x48 reg=0x00 2 # tmp117 on front +end diff --git a/humility-hif-assembler/examples/register-dump.hif b/humility-hif-assembler/examples/register-dump.hif new file mode 100644 index 000000000..fe382ee2a --- /dev/null +++ b/humility-hif-assembler/examples/register-dump.hif @@ -0,0 +1,10 @@ +# Register dump: read all 256 registers of a device. +# +# Scans registers 0x00-0xFF of a TPS546B24A power controller, +# reading 1 byte from each. Useful for capturing a device's +# full register state for debugging or comparison. +# +# Target: gimlet (mid bus, tps546b24a at 0x24) +# Expected results: 256 x 1-byte readings + +i2c_regscan mid 0x24 diff --git a/humility-hif-assembler/examples/stress-multi-device.hif b/humility-hif-assembler/examples/stress-multi-device.hif new file mode 100644 index 000000000..8dd91bc3a --- /dev/null +++ b/humility-hif-assembler/examples/stress-multi-device.hif @@ -0,0 +1,17 @@ +# Stress test: round-robin across multiple devices on one bus. +# +# Reads temperature from three TMP117 sensors on the front bus +# in a tight loop. This exercises bus arbitration under load +# when the thermal task is also polling these devices at 1 Hz. +# +# Target: gimlet (front bus, TMP117 at 0x48/0x49/0x4a) +# Expected results: 240 readings (80 per device) + +.let TEMP_REG 0x00 +.let ITERATIONS 80 + +repeat $ITERATIONS + i2c_read front 0x48 reg=$TEMP_REG 2 + i2c_read front 0x49 reg=$TEMP_REG 2 + i2c_read front 0x4a reg=$TEMP_REG 2 +end diff --git a/humility-hif-assembler/examples/stress-mux-switching.hif b/humility-hif-assembler/examples/stress-mux-switching.hif new file mode 100644 index 000000000..b4b950f22 --- /dev/null +++ b/humility-hif-assembler/examples/stress-mux-switching.hif @@ -0,0 +1,18 @@ +# Stress test: mux switching overhead. +# +# Alternates between two mux segments on the front bus, forcing +# a mux switch on every other read. This measures the overhead +# of PCA9545 mux reconfiguration under load. +# +# Target: gimlet (front bus, mux 0x01, segments 1 and 2) +# Expected results: 200 readings (100 per segment) + +.let VPD_ADDR 0x50 +.let ITERATIONS 100 + +repeat $ITERATIONS + # Read from sharkfin A (segment 1) + i2c_read front $VPD_ADDR mux=0x01.1 1 + # Read from sharkfin B (segment 2) — forces mux switch + i2c_read front $VPD_ADDR mux=0x01.2 1 +end diff --git a/humility-hif-assembler/examples/stress-single-device.hif b/humility-hif-assembler/examples/stress-single-device.hif new file mode 100644 index 000000000..09483c587 --- /dev/null +++ b/humility-hif-assembler/examples/stress-single-device.hif @@ -0,0 +1,15 @@ +# Stress test: tight loop reading one temperature sensor. +# +# Reads the temperature register of a TMP117 on the front bus +# as fast as the hiffy task can execute. No sleep between +# iterations — this measures maximum I2C transaction rate. +# +# Target: gimlet (front bus, TMP117 at 0x48) +# Expected results: 200 x 2-byte temperature readings + +.let TEMP_REG 0x00 +.let ITERATIONS 200 + +repeat $ITERATIONS + i2c_read front tmp117 reg=$TEMP_REG 2 +end diff --git a/humility-hif-assembler/examples/stress-with-sleep.hif b/humility-hif-assembler/examples/stress-with-sleep.hif new file mode 100644 index 000000000..536da2a97 --- /dev/null +++ b/humility-hif-assembler/examples/stress-with-sleep.hif @@ -0,0 +1,17 @@ +# Stress test with controlled rate. +# +# Same as stress-single-device but with a 10ms sleep between +# iterations. At ~100 reads/second, this runs for about 10 +# seconds. The sleep lets the thermal task's 1 Hz polling +# interleave naturally. +# +# Target: gimlet (front bus, TMP117 at 0x48) +# Expected results: 200 x 2-byte temperature readings + +.let SENSOR 0x48 +.let TEMP_REG 0x00 +.let ITERATIONS 200 + +repeat $ITERATIONS sleep=10ms + i2c_read front $SENSOR reg=$TEMP_REG 2 +end diff --git a/humility-hif-assembler/examples/write-read-verify.hif b/humility-hif-assembler/examples/write-read-verify.hif new file mode 100644 index 000000000..82ecfbf7e --- /dev/null +++ b/humility-hif-assembler/examples/write-read-verify.hif @@ -0,0 +1,17 @@ +# Write-read-verify: write a config register then read it back. +# +# Writes to the TMP117 configuration register, then reads it +# back to verify the write took effect. The result validator +# can compare the read-back against the written value. +# +# Target: gimlet (front bus, TMP117 at 0x48) +# Expected results: 2 (1 write result + 1 read result) + +.let SENSOR 0x48 +.let CONFIG_REG 0x01 + +# Write configuration: 0x02, 0x20 (example config bytes) +i2c_write front $SENSOR reg=$CONFIG_REG 0x02,0x20 + +# Read it back — should match what we wrote +i2c_read front $SENSOR reg=$CONFIG_REG 2 diff --git a/humility-hif-assembler/fixtures/README b/humility-hif-assembler/fixtures/README new file mode 100644 index 000000000..1b52e8728 --- /dev/null +++ b/humility-hif-assembler/fixtures/README @@ -0,0 +1,15 @@ +These JSON files are snapshots of TargetConfig extracted from Hubris +archives at a point in time. They are used as test data for the +assembler's unit and fixture tests, allowing tests to run without +access to a Hubris archive or build environment. + +These fixtures WILL go stale as Hubris evolves (Idol interfaces +change, devices are added, features are renamed). They should not +be used as a source of truth for what a live target supports. + +To regenerate from a current archive: + + HUBRIS_ARCHIVE=path/to/archive.zip GENERATE_FIXTURE=1 \ + cargo test -p humility-hif-assembler \ + --test archive_integration generate_fixture \ + -- --nocapture diff --git a/humility-hif-assembler/fixtures/cosmo-b.json b/humility-hif-assembler/fixtures/cosmo-b.json new file mode 100644 index 000000000..c5b40d398 --- /dev/null +++ b/humility-hif-assembler/fixtures/cosmo-b.json @@ -0,0 +1,7421 @@ +{ + "image_id": [ + 217, + 26, + 168, + 121, + 231, + 52, + 189, + 12 + ], + "board": "cosmo-b", + "buses": [ + { + "name": "main", + "controller": 2, + "port_index": 0, + "port_name": "F", + "devices": [ + { + "address": 112, + "device": "oximux16", + "description": "Main FPGA virtual mux", + "removable": false + } + ], + "muxes": [ + { + "address": 1, + "segments": [ + { + "segment": 1, + "devices": [ + { + "address": 106, + "device": "nvme_bmc", + "name": "M2_A", + "description": "M.2 A NVMe Basic Management Command", + "removable": true, + "sensors": [ + { + "name": "M2_A", + "kind": "Temperature" + } + ] + } + ] + }, + { + "segment": 2, + "devices": [ + { + "address": 106, + "device": "nvme_bmc", + "name": "M2_B", + "description": "M.2 B NVMe Basic Management Command", + "removable": true, + "sensors": [ + { + "name": "M2_B", + "kind": "Temperature" + } + ] + } + ] + }, + { + "segment": 4, + "devices": [ + { + "address": 60, + "device": "sbrmi", + "name": "RMI", + "description": "CPU via SB-RMI", + "removable": false + }, + { + "address": 76, + "device": "sbtsi", + "name": "CPU", + "description": "CPU temperature sensor", + "removable": false, + "sensors": [ + { + "name": "CPU", + "kind": "Temperature" + } + ] + } + ] + }, + { + "segment": 7, + "devices": [ + { + "address": 80, + "device": "at24csw080", + "name": "fan_vpd", + "description": "Fan VPD", + "removable": true + } + ] + }, + { + "segment": 8, + "devices": [ + { + "address": 76, + "device": "tmp451", + "name": "t6", + "description": "T6 temperature sensor", + "removable": false, + "sensors": [ + { + "name": "t6", + "kind": "Temperature" + } + ] + } + ] + } + ] + } + ] + }, + { + "name": "mid", + "controller": 3, + "port_index": 0, + "port_name": "H", + "devices": [ + { + "address": 36, + "device": "tps546b24a", + "name": "v3p3_sp_a2", + "description": "A2 3.3V rail", + "removable": false, + "sensors": [ + { + "name": "V3P3_SP_A2", + "kind": "Temperature" + }, + { + "name": "V3P3_SP_A2", + "kind": "Current" + }, + { + "name": "V3P3_SP_A2", + "kind": "Voltage" + } + ] + }, + { + "address": 39, + "device": "tps546b24a", + "name": "v5p0_sys_a2", + "description": "A2 5V rail", + "removable": false, + "sensors": [ + { + "name": "V5_SYS_A2", + "kind": "Temperature" + }, + { + "name": "V5_SYS_A2", + "kind": "Current" + }, + { + "name": "V5_SYS_A2", + "kind": "Voltage" + } + ] + }, + { + "address": 41, + "device": "tps546b24a", + "name": "v1p8_sys_a2", + "description": "A2 1.8V rail", + "removable": false, + "sensors": [ + { + "name": "V1P8_SYS_A2", + "kind": "Temperature" + }, + { + "name": "V1P8_SYS_A2", + "kind": "Current" + }, + { + "name": "V1P8_SYS_A2", + "kind": "Voltage" + } + ] + }, + { + "address": 58, + "device": "max5970", + "name": "m2", + "description": "M.2 hot plug controller", + "removable": false, + "sensors": [ + { + "name": "V3P3_M2A_A0HP", + "kind": "Current" + }, + { + "name": "V3P3_M2B_A0HP", + "kind": "Current" + }, + { + "name": "V3P3_M2A_A0HP", + "kind": "Voltage" + }, + { + "name": "V3P3_M2B_A0HP", + "kind": "Voltage" + } + ] + }, + { + "address": 84, + "device": "ltc4282", + "name": "mcio", + "description": "12V MCIO hot plug controller", + "removable": false, + "sensors": [ + { + "name": "V12_MCIO_A0HP", + "kind": "Current" + }, + { + "name": "V12_MCIO_A0HP", + "kind": "Voltage" + } + ] + }, + { + "address": 86, + "device": "ltc4282", + "name": "dimm_hsc_ghijkl", + "description": "DIMM GHIJKL hot plug controller", + "removable": false, + "sensors": [ + { + "name": "V12_DDR5_GHIJKL_A0", + "kind": "Current" + }, + { + "name": "V12_DDR5_GHIJKL_A0", + "kind": "Voltage" + } + ] + }, + { + "address": 85, + "device": "ltc4282", + "name": "dimm_hsc_abcdef", + "description": "DIMM ABCDEF hot plug controller", + "removable": false, + "sensors": [ + { + "name": "V12_DDR5_ABCDEF_A0", + "kind": "Current" + }, + { + "name": "V12_DDR5_ABCDEF_A0", + "kind": "Voltage" + } + ] + }, + { + "address": 117, + "device": "raa229620a", + "description": "South power controller (Core 0, SOC)", + "removable": false, + "sensors": [ + { + "name": "VDDCR_CPU0_A0", + "kind": "Temperature" + }, + { + "name": "VDDCR_SOC_A0", + "kind": "Temperature" + }, + { + "name": "VDDCR_CPU0_A0", + "kind": "Power" + }, + { + "name": "VDDCR_SOC_A0", + "kind": "Power" + }, + { + "name": "VDDCR_CPU0_A0", + "kind": "Current" + }, + { + "name": "VDDCR_SOC_A0", + "kind": "Current" + }, + { + "name": "VDDCR_CPU0_A0", + "kind": "Voltage" + }, + { + "name": "VDDCR_SOC_A0", + "kind": "Voltage" + } + ] + }, + { + "address": 118, + "device": "raa229620a", + "description": "North power controller (Core 1, VDDIO)", + "removable": false, + "sensors": [ + { + "name": "VDDCR_CPU1_A0", + "kind": "Temperature" + }, + { + "name": "VDDIO_SP5_A0", + "kind": "Temperature" + }, + { + "name": "VDDCR_CPU1_A0", + "kind": "Power" + }, + { + "name": "VDDIO_SP5_A0", + "kind": "Power" + }, + { + "name": "VDDCR_CPU1_A0", + "kind": "Current" + }, + { + "name": "VDDIO_SP5_A0", + "kind": "Current" + }, + { + "name": "VDDCR_CPU1_A0", + "kind": "Voltage" + }, + { + "name": "VDDIO_SP5_A0", + "kind": "Voltage" + } + ] + }, + { + "address": 92, + "device": "isl68224", + "description": "SP5 power controller (V1P1, V1P8, V3P3)", + "removable": false, + "sensors": [ + { + "name": "V1P1_SP5_A0", + "kind": "Current" + }, + { + "name": "V1P8_SP5_A1", + "kind": "Current" + }, + { + "name": "V3P3_SP5_A1", + "kind": "Current" + }, + { + "name": "V1P1_SP5_A0", + "kind": "Voltage" + }, + { + "name": "V1P8_SP5_A1", + "kind": "Voltage" + }, + { + "name": "V3P3_SP5_A1", + "kind": "Voltage" + } + ] + } + ] + }, + { + "name": "rear", + "controller": 4, + "port_index": 0, + "port_name": "F", + "devices": [ + { + "address": 57, + "device": "max5970", + "description": "NIC hot swap", + "removable": false, + "sensors": [ + { + "name": "V12P0_NIC_A0HP", + "kind": "Current" + }, + { + "name": "V5P0_NIC_A0HP", + "kind": "Current" + }, + { + "name": "V12P0_NIC_A0HP", + "kind": "Voltage" + }, + { + "name": "V5P0_NIC_A0HP", + "kind": "Voltage" + } + ] + }, + { + "address": 37, + "device": "tps546b24a", + "name": "v0p96_nic", + "description": "T6 power controller", + "removable": false, + "sensors": [ + { + "name": "V0P96_NIC_VDD_A0HP", + "kind": "Temperature" + }, + { + "name": "V0P96_NIC_VDD_A0HP", + "kind": "Current" + }, + { + "name": "V0P96_NIC_VDD_A0HP", + "kind": "Voltage" + } + ] + }, + { + "address": 72, + "device": "tmp117", + "name": "Northwest", + "description": "Northwest temperature sensor", + "removable": true, + "sensors": [ + { + "name": "Northwest", + "kind": "Temperature" + } + ] + }, + { + "address": 73, + "device": "tmp117", + "name": "North", + "description": "North temperature sensor", + "removable": true, + "sensors": [ + { + "name": "North", + "kind": "Temperature" + } + ] + }, + { + "address": 74, + "device": "tmp117", + "name": "Northeast", + "description": "Northeast temperature sensor", + "removable": true, + "sensors": [ + { + "name": "Northeast", + "kind": "Temperature" + } + ] + }, + { + "address": 32, + "device": "max31790", + "description": "Fan controller", + "removable": false, + "sensors": [ + { + "name": "Southeast", + "kind": "Speed" + }, + { + "name": "Northeast", + "kind": "Speed" + }, + { + "name": "South", + "kind": "Speed" + }, + { + "name": "North", + "kind": "Speed" + }, + { + "name": "Southwest", + "kind": "Speed" + }, + { + "name": "Northwest", + "kind": "Speed" + } + ] + }, + { + "address": 103, + "device": "bmr491", + "name": "IBC", + "description": "Intermediate bus converter", + "removable": false, + "sensors": [ + { + "name": "V12_SYS_A2", + "kind": "Temperature" + }, + { + "name": "V12_SYS_A2", + "kind": "Power" + }, + { + "name": "V12_SYS_A2", + "kind": "Current" + }, + { + "name": "V12_SYS_A2", + "kind": "Voltage" + } + ] + }, + { + "address": 80, + "device": "at24csw080", + "name": "local_vpd", + "description": "Cosmo VPD", + "removable": false + }, + { + "address": 17, + "device": "lm5066i", + "description": "Fan hot swap controller (east)", + "removable": false, + "sensors": [ + { + "name": "V54P5_FAN_EAST", + "kind": "Temperature" + }, + { + "name": "V54P5_FAN_EAST", + "kind": "Current" + }, + { + "name": "V54P5_FAN_EAST", + "kind": "Voltage" + } + ] + }, + { + "address": 18, + "device": "lm5066i", + "description": "Fan hot swap controller (central)", + "removable": false, + "sensors": [ + { + "name": "V54P5_FAN_CENTRAL", + "kind": "Temperature" + }, + { + "name": "V54P5_FAN_CENTRAL", + "kind": "Current" + }, + { + "name": "V54P5_FAN_CENTRAL", + "kind": "Voltage" + } + ] + }, + { + "address": 19, + "device": "lm5066i", + "description": "Fan hot swap controller (west)", + "removable": false, + "sensors": [ + { + "name": "V54P5_FAN_WEST", + "kind": "Temperature" + }, + { + "name": "V54P5_FAN_WEST", + "kind": "Current" + }, + { + "name": "V54P5_FAN_WEST", + "kind": "Voltage" + } + ] + }, + { + "address": 20, + "device": "adm127x", + "description": "Sled hot swap controller", + "removable": false, + "sensors": [ + { + "name": "V54P5_IBC_A3", + "kind": "Temperature" + }, + { + "name": "V54P5_IBC_A3", + "kind": "Current" + }, + { + "name": "V54P5_IBC_A3", + "kind": "Voltage" + } + ] + } + ] + }, + { + "name": "front", + "controller": 1, + "port_index": 0, + "port_name": "B", + "devices": [ + { + "address": 112, + "device": "oximux16", + "description": "Front FPGA virtual mux", + "removable": false + } + ], + "muxes": [ + { + "address": 1, + "segments": [ + { + "segment": 1, + "devices": [ + { + "address": 80, + "device": "at24csw080", + "name": "sharkfin_a_vpd", + "description": "U.2 Sharkfin A VPD", + "removable": true + }, + { + "address": 56, + "device": "max5970", + "name": "sharkfin_a_hsc", + "description": "U.2 Sharkfin A hot swap controller", + "removable": true, + "sensors": [ + { + "name": "V12_U2A_A0", + "kind": "Current" + }, + { + "name": "V3P3_U2A_A0", + "kind": "Current" + }, + { + "name": "V12_U2A_A0", + "kind": "Voltage" + }, + { + "name": "V3P3_U2A_A0", + "kind": "Voltage" + } + ] + }, + { + "address": 106, + "device": "nvme_bmc", + "name": "U2_N0", + "description": "U.2 A NVMe Basic Management Command", + "removable": true, + "sensors": [ + { + "name": "U2_N0", + "kind": "Temperature" + } + ] + } + ] + }, + { + "segment": 2, + "devices": [ + { + "address": 80, + "device": "at24csw080", + "name": "sharkfin_b_vpd", + "description": "U.2 Sharkfin B VPD", + "removable": true + }, + { + "address": 56, + "device": "max5970", + "name": "sharkfin_b_hsc", + "description": "U.2 Sharkfin B hot swap controller", + "removable": true, + "sensors": [ + { + "name": "V12_U2B_A0", + "kind": "Current" + }, + { + "name": "V3P3_U2B_A0", + "kind": "Current" + }, + { + "name": "V12_U2B_A0", + "kind": "Voltage" + }, + { + "name": "V3P3_U2B_A0", + "kind": "Voltage" + } + ] + }, + { + "address": 106, + "device": "nvme_bmc", + "name": "U2_N1", + "description": "U.2 B NVMe Basic Management Control", + "removable": true, + "sensors": [ + { + "name": "U2_N1", + "kind": "Temperature" + } + ] + } + ] + }, + { + "segment": 3, + "devices": [ + { + "address": 80, + "device": "at24csw080", + "name": "sharkfin_c_vpd", + "description": "U.2 Sharkfin C VPD", + "removable": true + }, + { + "address": 56, + "device": "max5970", + "name": "sharkfin_c_hsc", + "description": "U.2 Sharkfin C hot swap controller", + "removable": true, + "sensors": [ + { + "name": "V12_U2C_A0", + "kind": "Current" + }, + { + "name": "V3P3_U2C_A0", + "kind": "Current" + }, + { + "name": "V12_U2C_A0", + "kind": "Voltage" + }, + { + "name": "V3P3_U2C_A0", + "kind": "Voltage" + } + ] + }, + { + "address": 106, + "device": "nvme_bmc", + "name": "U2_N2", + "description": "U.2 C NVMe Basic Management Control", + "removable": true, + "sensors": [ + { + "name": "U2_N2", + "kind": "Temperature" + } + ] + } + ] + }, + { + "segment": 4, + "devices": [ + { + "address": 80, + "device": "at24csw080", + "name": "sharkfin_d_vpd", + "description": "U.2 Sharkfin D VPD", + "removable": true + }, + { + "address": 56, + "device": "max5970", + "name": "sharkfin_d_hsc", + "description": "U.2 Sharkfin D hot swap controller", + "removable": true, + "sensors": [ + { + "name": "V12_U2D_A0", + "kind": "Current" + }, + { + "name": "V3P3_U2D_A0", + "kind": "Current" + }, + { + "name": "V12_U2D_A0", + "kind": "Voltage" + }, + { + "name": "V3P3_U2D_A0", + "kind": "Voltage" + } + ] + }, + { + "address": 106, + "device": "nvme_bmc", + "name": "U2_N3", + "description": "U.2 D NVMe Basic Management Control", + "removable": true, + "sensors": [ + { + "name": "U2_N3", + "kind": "Temperature" + } + ] + } + ] + }, + { + "segment": 5, + "devices": [ + { + "address": 80, + "device": "at24csw080", + "name": "sharkfin_e_vpd", + "description": "U.2 Sharkfin E VPD", + "removable": true + }, + { + "address": 56, + "device": "max5970", + "name": "sharkfin_e_hsc", + "description": "U.2 Sharkfin E hot swap controller", + "removable": true, + "sensors": [ + { + "name": "V12_U2E_A0", + "kind": "Current" + }, + { + "name": "V3P3_U2E_A0", + "kind": "Current" + }, + { + "name": "V12_U2E_A0", + "kind": "Voltage" + }, + { + "name": "V3P3_U2E_A0", + "kind": "Voltage" + } + ] + }, + { + "address": 106, + "device": "nvme_bmc", + "name": "U2_N4", + "description": "U.2 E NVMe Basic Management Control", + "removable": true, + "sensors": [ + { + "name": "U2_N4", + "kind": "Temperature" + } + ] + } + ] + }, + { + "segment": 6, + "devices": [ + { + "address": 80, + "device": "at24csw080", + "name": "sharkfin_f_vpd", + "description": "U.2 Sharkfin F VPD", + "removable": true + }, + { + "address": 56, + "device": "max5970", + "name": "sharkfin_f_hsc", + "description": "U.2 Sharkfin F hot swap controller", + "removable": true, + "sensors": [ + { + "name": "V12_U2F_A0", + "kind": "Current" + }, + { + "name": "V3P3_U2F_A0", + "kind": "Current" + }, + { + "name": "V12_U2F_A0", + "kind": "Voltage" + }, + { + "name": "V3P3_U2F_A0", + "kind": "Voltage" + } + ] + }, + { + "address": 106, + "device": "nvme_bmc", + "name": "U2_N5", + "description": "U.2 F NVMe Basic Management Control", + "removable": true, + "sensors": [ + { + "name": "U2_N5", + "kind": "Temperature" + } + ] + } + ] + }, + { + "segment": 9, + "devices": [ + { + "address": 80, + "device": "at24csw080", + "name": "sharkfin_g_vpd", + "description": "U.2 Sharkfin G VPD", + "removable": true + }, + { + "address": 56, + "device": "max5970", + "name": "sharkfin_g_hsc", + "description": "U.2 Sharkfin G hot swap controller", + "removable": true, + "sensors": [ + { + "name": "V12_U2G_A0", + "kind": "Current" + }, + { + "name": "V3P3_U2G_A0", + "kind": "Current" + }, + { + "name": "V12_U2G_A0", + "kind": "Voltage" + }, + { + "name": "V3P3_U2G_A0", + "kind": "Voltage" + } + ] + }, + { + "address": 106, + "device": "nvme_bmc", + "name": "U2_N6", + "description": "U.2 G NVMe Basic Management Control", + "removable": true, + "sensors": [ + { + "name": "U2_N6", + "kind": "Temperature" + } + ] + } + ] + }, + { + "segment": 10, + "devices": [ + { + "address": 80, + "device": "at24csw080", + "name": "sharkfin_h_vpd", + "description": "U.2 Sharkfin H VPD", + "removable": true + }, + { + "address": 56, + "device": "max5970", + "name": "sharkfin_h_hsc", + "description": "U.2 Sharkfin H hot swap controller", + "removable": true, + "sensors": [ + { + "name": "V12_U2H_A0", + "kind": "Current" + }, + { + "name": "V3P3_U2H_A0", + "kind": "Current" + }, + { + "name": "V12_U2H_A0", + "kind": "Voltage" + }, + { + "name": "V3P3_U2H_A0", + "kind": "Voltage" + } + ] + }, + { + "address": 106, + "device": "nvme_bmc", + "name": "U2_N7", + "description": "U.2 H NVMe Basic Management Control", + "removable": true, + "sensors": [ + { + "name": "U2_N7", + "kind": "Temperature" + } + ] + } + ] + }, + { + "segment": 11, + "devices": [ + { + "address": 80, + "device": "at24csw080", + "name": "sharkfin_i_vpd", + "description": "U.2 Sharkfin I VPD", + "removable": true + }, + { + "address": 56, + "device": "max5970", + "name": "sharkfin_i_hsc", + "description": "U.2 Sharkfin I hot swap controller", + "removable": true, + "sensors": [ + { + "name": "V12_U2I_A0", + "kind": "Current" + }, + { + "name": "V3P3_U2I_A0", + "kind": "Current" + }, + { + "name": "V12_U2I_A0", + "kind": "Voltage" + }, + { + "name": "V3P3_U2I_A0", + "kind": "Voltage" + } + ] + }, + { + "address": 106, + "device": "nvme_bmc", + "name": "U2_N8", + "description": "U.2 I NVMe Basic Management Control", + "removable": true, + "sensors": [ + { + "name": "U2_N8", + "kind": "Temperature" + } + ] + } + ] + }, + { + "segment": 12, + "devices": [ + { + "address": 80, + "device": "at24csw080", + "name": "sharkfin_j_vpd", + "description": "U.2 Sharkfin J VPD", + "removable": true + }, + { + "address": 56, + "device": "max5970", + "name": "sharkfin_j_hsc", + "description": "U.2 Sharkfin J hot swap controller", + "removable": true, + "sensors": [ + { + "name": "V12_U2J_A0", + "kind": "Current" + }, + { + "name": "V3P3_U2J_A0", + "kind": "Current" + }, + { + "name": "V12_U2J_A0", + "kind": "Voltage" + }, + { + "name": "V3P3_U2J_A0", + "kind": "Voltage" + } + ] + }, + { + "address": 106, + "device": "nvme_bmc", + "name": "U2_N9", + "description": "U.2 J NVMe Basic Management Control", + "removable": true, + "sensors": [ + { + "name": "U2_N9", + "kind": "Temperature" + } + ] + } + ] + }, + { + "segment": 13, + "devices": [ + { + "address": 72, + "device": "tmp117", + "name": "Southwest", + "description": "Southwest temperature sensor", + "removable": true, + "sensors": [ + { + "name": "Southwest", + "kind": "Temperature" + } + ] + }, + { + "address": 73, + "device": "tmp117", + "name": "South", + "description": "South temperature sensor", + "removable": true, + "sensors": [ + { + "name": "South", + "kind": "Temperature" + } + ] + }, + { + "address": 74, + "device": "tmp117", + "name": "Southeast", + "description": "Southeast temperature sensor", + "removable": true, + "sensors": [ + { + "name": "Southeast", + "kind": "Temperature" + } + ] + } + ] + } + ] + } + ] + } + ], + "functions": [ + { + "name": "Sleep", + "id": 0, + "arg_count": 1, + "args": [ + { + "name": "arg0", + "ty": "u16", + "size": 2 + } + ] + }, + { + "name": "Send", + "id": 1, + "arg_count": 4, + "args": [ + { + "name": "__0", + "ty": "Task", + "size": 1 + }, + { + "name": "__1", + "ty": "u16", + "size": 2 + }, + { + "name": "__2", + "ty": "Buffer", + "size": 1 + }, + { + "name": "__3", + "ty": "u32", + "size": 4 + } + ] + }, + { + "name": "SendLeaseRead", + "id": 2, + "arg_count": 5, + "args": [ + { + "name": "__0", + "ty": "Task", + "size": 1 + }, + { + "name": "__1", + "ty": "u16", + "size": 2 + }, + { + "name": "__2", + "ty": "Buffer", + "size": 1 + }, + { + "name": "__3", + "ty": "u32", + "size": 4 + }, + { + "name": "__4", + "ty": "u32", + "size": 4 + } + ] + }, + { + "name": "SendLeaseReadWrite", + "id": 3, + "arg_count": 6, + "args": [ + { + "name": "__0", + "ty": "Task", + "size": 1 + }, + { + "name": "__1", + "ty": "u16", + "size": 2 + }, + { + "name": "__2", + "ty": "Buffer", + "size": 1 + }, + { + "name": "__3", + "ty": "u32", + "size": 4 + }, + { + "name": "__4", + "ty": "u32", + "size": 4 + }, + { + "name": "__5", + "ty": "u32", + "size": 4 + } + ] + }, + { + "name": "SendLeaseWrite", + "id": 4, + "arg_count": 5, + "args": [ + { + "name": "__0", + "ty": "Task", + "size": 1 + }, + { + "name": "__1", + "ty": "u16", + "size": 2 + }, + { + "name": "__2", + "ty": "Buffer", + "size": 1 + }, + { + "name": "__3", + "ty": "u32", + "size": 4 + }, + { + "name": "__4", + "ty": "u32", + "size": 4 + } + ] + }, + { + "name": "I2cRead", + "id": 5, + "arg_count": 7, + "args": [ + { + "name": "__0", + "ty": "Controller", + "size": 1 + }, + { + "name": "__1", + "ty": "PortIndex", + "size": 1 + }, + { + "name": "__2", + "ty": "Mux", + "size": 1 + }, + { + "name": "__3", + "ty": "Segment", + "size": 1 + }, + { + "name": "__4", + "ty": "u8", + "size": 1 + }, + { + "name": "__5", + "ty": "u8", + "size": 1 + }, + { + "name": "__6", + "ty": "u32", + "size": 4 + } + ], + "errors": [ + { + "code": 1, + "name": "BadResponse" + }, + { + "code": 2, + "name": "BadArg" + }, + { + "code": 3, + "name": "NoDevice" + }, + { + "code": 4, + "name": "BadController" + }, + { + "code": 5, + "name": "ReservedAddress" + }, + { + "code": 6, + "name": "BadPort" + }, + { + "code": 7, + "name": "NoRegister" + }, + { + "code": 8, + "name": "BadMux" + }, + { + "code": 9, + "name": "BadSegment" + }, + { + "code": 10, + "name": "MuxNotFound" + }, + { + "code": 11, + "name": "SegmentNotFound" + }, + { + "code": 12, + "name": "SegmentDisconnected" + }, + { + "code": 13, + "name": "MuxDisconnected" + }, + { + "code": 14, + "name": "MuxMissing" + }, + { + "code": 15, + "name": "BadMuxRegister" + }, + { + "code": 16, + "name": "BusReset" + }, + { + "code": 17, + "name": "BusResetMux" + }, + { + "code": 18, + "name": "BusLocked" + }, + { + "code": 19, + "name": "BusLockedMux" + }, + { + "code": 20, + "name": "ControllerBusy" + }, + { + "code": 21, + "name": "BusError" + }, + { + "code": 22, + "name": "BadDeviceState" + }, + { + "code": 23, + "name": "OperationNotSupported" + }, + { + "code": 24, + "name": "IllegalLeaseCount" + }, + { + "code": 25, + "name": "TooMuchData" + } + ] + }, + { + "name": "I2cWrite", + "id": 6, + "arg_count": 8, + "args": [ + { + "name": "__0", + "ty": "Controller", + "size": 1 + }, + { + "name": "__1", + "ty": "PortIndex", + "size": 1 + }, + { + "name": "__2", + "ty": "Mux", + "size": 1 + }, + { + "name": "__3", + "ty": "Segment", + "size": 1 + }, + { + "name": "__4", + "ty": "u8", + "size": 1 + }, + { + "name": "__5", + "ty": "u8", + "size": 1 + }, + { + "name": "__6", + "ty": "Buffer", + "size": 1 + }, + { + "name": "__7", + "ty": "u32", + "size": 4 + } + ], + "errors": [ + { + "code": 1, + "name": "BadResponse" + }, + { + "code": 2, + "name": "BadArg" + }, + { + "code": 3, + "name": "NoDevice" + }, + { + "code": 4, + "name": "BadController" + }, + { + "code": 5, + "name": "ReservedAddress" + }, + { + "code": 6, + "name": "BadPort" + }, + { + "code": 7, + "name": "NoRegister" + }, + { + "code": 8, + "name": "BadMux" + }, + { + "code": 9, + "name": "BadSegment" + }, + { + "code": 10, + "name": "MuxNotFound" + }, + { + "code": 11, + "name": "SegmentNotFound" + }, + { + "code": 12, + "name": "SegmentDisconnected" + }, + { + "code": 13, + "name": "MuxDisconnected" + }, + { + "code": 14, + "name": "MuxMissing" + }, + { + "code": 15, + "name": "BadMuxRegister" + }, + { + "code": 16, + "name": "BusReset" + }, + { + "code": 17, + "name": "BusResetMux" + }, + { + "code": 18, + "name": "BusLocked" + }, + { + "code": 19, + "name": "BusLockedMux" + }, + { + "code": 20, + "name": "ControllerBusy" + }, + { + "code": 21, + "name": "BusError" + }, + { + "code": 22, + "name": "BadDeviceState" + }, + { + "code": 23, + "name": "OperationNotSupported" + }, + { + "code": 24, + "name": "IllegalLeaseCount" + }, + { + "code": 25, + "name": "TooMuchData" + } + ] + }, + { + "name": "I2cBulkWrite", + "id": 7, + "arg_count": 8, + "args": [ + { + "name": "__0", + "ty": "Controller", + "size": 1 + }, + { + "name": "__1", + "ty": "PortIndex", + "size": 1 + }, + { + "name": "__2", + "ty": "Mux", + "size": 1 + }, + { + "name": "__3", + "ty": "Segment", + "size": 1 + }, + { + "name": "__4", + "ty": "u8", + "size": 1 + }, + { + "name": "__5", + "ty": "u8", + "size": 1 + }, + { + "name": "__6", + "ty": "u32", + "size": 4 + }, + { + "name": "__7", + "ty": "u32", + "size": 4 + } + ], + "errors": [ + { + "code": 1, + "name": "BadResponse" + }, + { + "code": 2, + "name": "BadArg" + }, + { + "code": 3, + "name": "NoDevice" + }, + { + "code": 4, + "name": "BadController" + }, + { + "code": 5, + "name": "ReservedAddress" + }, + { + "code": 6, + "name": "BadPort" + }, + { + "code": 7, + "name": "NoRegister" + }, + { + "code": 8, + "name": "BadMux" + }, + { + "code": 9, + "name": "BadSegment" + }, + { + "code": 10, + "name": "MuxNotFound" + }, + { + "code": 11, + "name": "SegmentNotFound" + }, + { + "code": 12, + "name": "SegmentDisconnected" + }, + { + "code": 13, + "name": "MuxDisconnected" + }, + { + "code": 14, + "name": "MuxMissing" + }, + { + "code": 15, + "name": "BadMuxRegister" + }, + { + "code": 16, + "name": "BusReset" + }, + { + "code": 17, + "name": "BusResetMux" + }, + { + "code": 18, + "name": "BusLocked" + }, + { + "code": 19, + "name": "BusLockedMux" + }, + { + "code": 20, + "name": "ControllerBusy" + }, + { + "code": 21, + "name": "BusError" + }, + { + "code": 22, + "name": "BadDeviceState" + }, + { + "code": 23, + "name": "OperationNotSupported" + }, + { + "code": 24, + "name": "IllegalLeaseCount" + }, + { + "code": 25, + "name": "TooMuchData" + } + ] + }, + { + "name": "GpioInput", + "id": 8, + "arg_count": 1, + "args": [ + { + "name": "arg0", + "ty": "Port", + "size": 1 + } + ] + }, + { + "name": "GpioToggle", + "id": 9, + "arg_count": 2, + "args": [ + { + "name": "__0", + "ty": "Port", + "size": 1 + }, + { + "name": "__1", + "ty": "u8", + "size": 1 + } + ] + }, + { + "name": "GpioSet", + "id": 10, + "arg_count": 2, + "args": [ + { + "name": "__0", + "ty": "Port", + "size": 1 + }, + { + "name": "__1", + "ty": "u8", + "size": 1 + } + ] + }, + { + "name": "GpioReset", + "id": 11, + "arg_count": 2, + "args": [ + { + "name": "__0", + "ty": "Port", + "size": 1 + }, + { + "name": "__1", + "ty": "u8", + "size": 1 + } + ] + }, + { + "name": "GpioConfigure", + "id": 12, + "arg_count": 7, + "args": [ + { + "name": "__0", + "ty": "Port", + "size": 1 + }, + { + "name": "__1", + "ty": "u8", + "size": 1 + }, + { + "name": "__2", + "ty": "Mode", + "size": 1 + }, + { + "name": "__3", + "ty": "OutputType", + "size": 1 + }, + { + "name": "__4", + "ty": "Speed", + "size": 1 + }, + { + "name": "__5", + "ty": "Pull", + "size": 1 + }, + { + "name": "__6", + "ty": "Alternate", + "size": 1 + } + ] + }, + { + "name": "SpiRead", + "id": 13, + "arg_count": 4, + "args": [ + { + "name": "__0", + "ty": "Task", + "size": 1 + }, + { + "name": "__1", + "ty": "u8", + "size": 1 + }, + { + "name": "__2", + "ty": "u32", + "size": 4 + }, + { + "name": "__3", + "ty": "u32", + "size": 4 + } + ], + "errors": [ + { + "code": 1, + "name": "BadTransferSize" + }, + { + "code": 4, + "name": "TaskRestarted" + } + ] + }, + { + "name": "SpiWrite", + "id": 14, + "arg_count": 3, + "args": [ + { + "name": "__0", + "ty": "Task", + "size": 1 + }, + { + "name": "__1", + "ty": "u8", + "size": 1 + }, + { + "name": "__2", + "ty": "u32", + "size": 4 + } + ], + "errors": [ + { + "code": 1, + "name": "BadTransferSize" + }, + { + "code": 4, + "name": "TaskRestarted" + } + ] + }, + { + "name": "QspiReadId", + "id": 15, + "arg_count": 0, + "errors": [ + { + "code": 1, + "name": "WriteEnableFailed" + }, + { + "code": 2, + "name": "HashBadRange" + }, + { + "code": 3, + "name": "HashError" + }, + { + "code": 4, + "name": "HashNotConfigured" + }, + { + "code": 5, + "name": "NotMuxedToSP" + }, + { + "code": 6, + "name": "Sector0IsReserved" + }, + { + "code": 7, + "name": "NoPersistentData" + }, + { + "code": 8, + "name": "MonotonicCounterOverflow" + }, + { + "code": 9, + "name": "BadChipId" + }, + { + "code": 10, + "name": "BadAddress" + }, + { + "code": 11, + "name": "QspiTimeout" + }, + { + "code": 12, + "name": "QspiTransferError" + }, + { + "code": 13, + "name": "HashUncalculated" + }, + { + "code": 14, + "name": "RecalculateHash" + }, + { + "code": 15, + "name": "HashInProgress" + }, + { + "code": 16, + "name": "BadCapacity" + }, + { + "code": 17, + "name": "ServerRestarted" + } + ] + }, + { + "name": "QspiReadStatus", + "id": 16, + "arg_count": 0, + "errors": [ + { + "code": 1, + "name": "WriteEnableFailed" + }, + { + "code": 2, + "name": "HashBadRange" + }, + { + "code": 3, + "name": "HashError" + }, + { + "code": 4, + "name": "HashNotConfigured" + }, + { + "code": 5, + "name": "NotMuxedToSP" + }, + { + "code": 6, + "name": "Sector0IsReserved" + }, + { + "code": 7, + "name": "NoPersistentData" + }, + { + "code": 8, + "name": "MonotonicCounterOverflow" + }, + { + "code": 9, + "name": "BadChipId" + }, + { + "code": 10, + "name": "BadAddress" + }, + { + "code": 11, + "name": "QspiTimeout" + }, + { + "code": 12, + "name": "QspiTransferError" + }, + { + "code": 13, + "name": "HashUncalculated" + }, + { + "code": 14, + "name": "RecalculateHash" + }, + { + "code": 15, + "name": "HashInProgress" + }, + { + "code": 16, + "name": "BadCapacity" + }, + { + "code": 17, + "name": "ServerRestarted" + } + ] + }, + { + "name": "QspiBulkErase", + "id": 17, + "arg_count": 0, + "errors": [ + { + "code": 1, + "name": "WriteEnableFailed" + }, + { + "code": 2, + "name": "HashBadRange" + }, + { + "code": 3, + "name": "HashError" + }, + { + "code": 4, + "name": "HashNotConfigured" + }, + { + "code": 5, + "name": "NotMuxedToSP" + }, + { + "code": 6, + "name": "Sector0IsReserved" + }, + { + "code": 7, + "name": "NoPersistentData" + }, + { + "code": 8, + "name": "MonotonicCounterOverflow" + }, + { + "code": 9, + "name": "BadChipId" + }, + { + "code": 10, + "name": "BadAddress" + }, + { + "code": 11, + "name": "QspiTimeout" + }, + { + "code": 12, + "name": "QspiTransferError" + }, + { + "code": 13, + "name": "HashUncalculated" + }, + { + "code": 14, + "name": "RecalculateHash" + }, + { + "code": 15, + "name": "HashInProgress" + }, + { + "code": 16, + "name": "BadCapacity" + }, + { + "code": 17, + "name": "ServerRestarted" + } + ] + }, + { + "name": "QspiPageProgram", + "id": 18, + "arg_count": 3, + "args": [ + { + "name": "__0", + "ty": "u32", + "size": 4 + }, + { + "name": "__1", + "ty": "u32", + "size": 4 + }, + { + "name": "__2", + "ty": "u32", + "size": 4 + } + ], + "errors": [ + { + "code": 1, + "name": "WriteEnableFailed" + }, + { + "code": 2, + "name": "HashBadRange" + }, + { + "code": 3, + "name": "HashError" + }, + { + "code": 4, + "name": "HashNotConfigured" + }, + { + "code": 5, + "name": "NotMuxedToSP" + }, + { + "code": 6, + "name": "Sector0IsReserved" + }, + { + "code": 7, + "name": "NoPersistentData" + }, + { + "code": 8, + "name": "MonotonicCounterOverflow" + }, + { + "code": 9, + "name": "BadChipId" + }, + { + "code": 10, + "name": "BadAddress" + }, + { + "code": 11, + "name": "QspiTimeout" + }, + { + "code": 12, + "name": "QspiTransferError" + }, + { + "code": 13, + "name": "HashUncalculated" + }, + { + "code": 14, + "name": "RecalculateHash" + }, + { + "code": 15, + "name": "HashInProgress" + }, + { + "code": 16, + "name": "BadCapacity" + }, + { + "code": 17, + "name": "ServerRestarted" + } + ] + }, + { + "name": "QspiPageProgramSector0", + "id": 19, + "arg_count": 3, + "args": [ + { + "name": "__0", + "ty": "u32", + "size": 4 + }, + { + "name": "__1", + "ty": "u32", + "size": 4 + }, + { + "name": "__2", + "ty": "u32", + "size": 4 + } + ], + "errors": [ + { + "code": 1, + "name": "WriteEnableFailed" + }, + { + "code": 2, + "name": "HashBadRange" + }, + { + "code": 3, + "name": "HashError" + }, + { + "code": 4, + "name": "HashNotConfigured" + }, + { + "code": 5, + "name": "NotMuxedToSP" + }, + { + "code": 6, + "name": "Sector0IsReserved" + }, + { + "code": 7, + "name": "NoPersistentData" + }, + { + "code": 8, + "name": "MonotonicCounterOverflow" + }, + { + "code": 9, + "name": "BadChipId" + }, + { + "code": 10, + "name": "BadAddress" + }, + { + "code": 11, + "name": "QspiTimeout" + }, + { + "code": 12, + "name": "QspiTransferError" + }, + { + "code": 13, + "name": "HashUncalculated" + }, + { + "code": 14, + "name": "RecalculateHash" + }, + { + "code": 15, + "name": "HashInProgress" + }, + { + "code": 16, + "name": "BadCapacity" + }, + { + "code": 17, + "name": "ServerRestarted" + } + ] + }, + { + "name": "QspiRead", + "id": 20, + "arg_count": 2, + "args": [ + { + "name": "__0", + "ty": "u32", + "size": 4 + }, + { + "name": "__1", + "ty": "u32", + "size": 4 + } + ], + "errors": [ + { + "code": 1, + "name": "WriteEnableFailed" + }, + { + "code": 2, + "name": "HashBadRange" + }, + { + "code": 3, + "name": "HashError" + }, + { + "code": 4, + "name": "HashNotConfigured" + }, + { + "code": 5, + "name": "NotMuxedToSP" + }, + { + "code": 6, + "name": "Sector0IsReserved" + }, + { + "code": 7, + "name": "NoPersistentData" + }, + { + "code": 8, + "name": "MonotonicCounterOverflow" + }, + { + "code": 9, + "name": "BadChipId" + }, + { + "code": 10, + "name": "BadAddress" + }, + { + "code": 11, + "name": "QspiTimeout" + }, + { + "code": 12, + "name": "QspiTransferError" + }, + { + "code": 13, + "name": "HashUncalculated" + }, + { + "code": 14, + "name": "RecalculateHash" + }, + { + "code": 15, + "name": "HashInProgress" + }, + { + "code": 16, + "name": "BadCapacity" + }, + { + "code": 17, + "name": "ServerRestarted" + } + ] + }, + { + "name": "QspiSectorErase", + "id": 21, + "arg_count": 1, + "args": [ + { + "name": "arg0", + "ty": "u32", + "size": 4 + } + ], + "errors": [ + { + "code": 1, + "name": "WriteEnableFailed" + }, + { + "code": 2, + "name": "HashBadRange" + }, + { + "code": 3, + "name": "HashError" + }, + { + "code": 4, + "name": "HashNotConfigured" + }, + { + "code": 5, + "name": "NotMuxedToSP" + }, + { + "code": 6, + "name": "Sector0IsReserved" + }, + { + "code": 7, + "name": "NoPersistentData" + }, + { + "code": 8, + "name": "MonotonicCounterOverflow" + }, + { + "code": 9, + "name": "BadChipId" + }, + { + "code": 10, + "name": "BadAddress" + }, + { + "code": 11, + "name": "QspiTimeout" + }, + { + "code": 12, + "name": "QspiTransferError" + }, + { + "code": 13, + "name": "HashUncalculated" + }, + { + "code": 14, + "name": "RecalculateHash" + }, + { + "code": 15, + "name": "HashInProgress" + }, + { + "code": 16, + "name": "BadCapacity" + }, + { + "code": 17, + "name": "ServerRestarted" + } + ] + }, + { + "name": "QspiSector0Erase", + "id": 22, + "arg_count": 0, + "errors": [ + { + "code": 1, + "name": "WriteEnableFailed" + }, + { + "code": 2, + "name": "HashBadRange" + }, + { + "code": 3, + "name": "HashError" + }, + { + "code": 4, + "name": "HashNotConfigured" + }, + { + "code": 5, + "name": "NotMuxedToSP" + }, + { + "code": 6, + "name": "Sector0IsReserved" + }, + { + "code": 7, + "name": "NoPersistentData" + }, + { + "code": 8, + "name": "MonotonicCounterOverflow" + }, + { + "code": 9, + "name": "BadChipId" + }, + { + "code": 10, + "name": "BadAddress" + }, + { + "code": 11, + "name": "QspiTimeout" + }, + { + "code": 12, + "name": "QspiTransferError" + }, + { + "code": 13, + "name": "HashUncalculated" + }, + { + "code": 14, + "name": "RecalculateHash" + }, + { + "code": 15, + "name": "HashInProgress" + }, + { + "code": 16, + "name": "BadCapacity" + }, + { + "code": 17, + "name": "ServerRestarted" + } + ] + }, + { + "name": "QspiVerify", + "id": 23, + "arg_count": 3, + "args": [ + { + "name": "__0", + "ty": "u32", + "size": 4 + }, + { + "name": "__1", + "ty": "u32", + "size": 4 + }, + { + "name": "__2", + "ty": "u32", + "size": 4 + } + ], + "errors": [ + { + "code": 1, + "name": "WriteEnableFailed" + }, + { + "code": 2, + "name": "HashBadRange" + }, + { + "code": 3, + "name": "HashError" + }, + { + "code": 4, + "name": "HashNotConfigured" + }, + { + "code": 5, + "name": "NotMuxedToSP" + }, + { + "code": 6, + "name": "Sector0IsReserved" + }, + { + "code": 7, + "name": "NoPersistentData" + }, + { + "code": 8, + "name": "MonotonicCounterOverflow" + }, + { + "code": 9, + "name": "BadChipId" + }, + { + "code": 10, + "name": "BadAddress" + }, + { + "code": 11, + "name": "QspiTimeout" + }, + { + "code": 12, + "name": "QspiTransferError" + }, + { + "code": 13, + "name": "HashUncalculated" + }, + { + "code": 14, + "name": "RecalculateHash" + }, + { + "code": 15, + "name": "HashInProgress" + }, + { + "code": 16, + "name": "BadCapacity" + }, + { + "code": 17, + "name": "ServerRestarted" + } + ] + }, + { + "name": "QspiHash", + "id": 24, + "arg_count": 2, + "args": [ + { + "name": "__0", + "ty": "u32", + "size": 4 + }, + { + "name": "__1", + "ty": "u32", + "size": 4 + } + ], + "errors": [ + { + "code": 1, + "name": "WriteEnableFailed" + }, + { + "code": 2, + "name": "HashBadRange" + }, + { + "code": 3, + "name": "HashError" + }, + { + "code": 4, + "name": "HashNotConfigured" + }, + { + "code": 5, + "name": "NotMuxedToSP" + }, + { + "code": 6, + "name": "Sector0IsReserved" + }, + { + "code": 7, + "name": "NoPersistentData" + }, + { + "code": 8, + "name": "MonotonicCounterOverflow" + }, + { + "code": 9, + "name": "BadChipId" + }, + { + "code": 10, + "name": "BadAddress" + }, + { + "code": 11, + "name": "QspiTimeout" + }, + { + "code": 12, + "name": "QspiTransferError" + }, + { + "code": 13, + "name": "HashUncalculated" + }, + { + "code": 14, + "name": "RecalculateHash" + }, + { + "code": 15, + "name": "HashInProgress" + }, + { + "code": 16, + "name": "BadCapacity" + }, + { + "code": 17, + "name": "ServerRestarted" + } + ] + }, + { + "name": "HashDigest", + "id": 25, + "arg_count": 1, + "args": [ + { + "name": "arg0", + "ty": "u32", + "size": 4 + } + ], + "errors": [ + { + "code": 1, + "name": "NotInitialized" + }, + { + "code": 2, + "name": "InvalidState" + }, + { + "code": 3, + "name": "Busy" + }, + { + "code": 4, + "name": "NoData" + }, + { + "code": 5, + "name": "ServerRestarted" + } + ] + }, + { + "name": "HashInit", + "id": 26, + "arg_count": 0, + "errors": [ + { + "code": 1, + "name": "NotInitialized" + }, + { + "code": 2, + "name": "InvalidState" + }, + { + "code": 3, + "name": "Busy" + }, + { + "code": 4, + "name": "NoData" + }, + { + "code": 5, + "name": "ServerRestarted" + } + ] + }, + { + "name": "HashUpdate", + "id": 27, + "arg_count": 1, + "args": [ + { + "name": "arg0", + "ty": "u32", + "size": 4 + } + ], + "errors": [ + { + "code": 1, + "name": "NotInitialized" + }, + { + "code": 2, + "name": "InvalidState" + }, + { + "code": 3, + "name": "Busy" + }, + { + "code": 4, + "name": "NoData" + }, + { + "code": 5, + "name": "ServerRestarted" + } + ] + }, + { + "name": "HashFinalize", + "id": 28, + "arg_count": 0, + "errors": [ + { + "code": 1, + "name": "NotInitialized" + }, + { + "code": 2, + "name": "InvalidState" + }, + { + "code": 3, + "name": "Busy" + }, + { + "code": 4, + "name": "NoData" + }, + { + "code": 5, + "name": "ServerRestarted" + } + ] + } + ], + "buffer_sizes": { + "text": 4096, + "data": 20480, + "rstack": 2048, + "scratch": 1025 + }, + "idol_interfaces": [ + { + "name": "Jefe", + "task": "jefe", + "task_id": 0, + "ops": [ + { + "name": "get_state", + "code": 1, + "args_size": 0, + "reply": "u32", + "reply_size": 4, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "set_state", + "code": 2, + "args": [ + { + "name": "state", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "request_reset", + "code": 3, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "get_reset_reason", + "code": 4, + "args_size": 0, + "reply": "ResetReason", + "reply_size": 5, + "encoding": "Ssmarshal", + "idempotent": true + }, + { + "name": "set_reset_reason", + "code": 5, + "args": [ + { + "name": "reason", + "ty": "ResetReason" + } + ], + "args_size": 8, + "reply": "()", + "reply_size": 0, + "encoding": "Ssmarshal", + "idempotent": true + }, + { + "name": "reinitialize_dump_areas", + "code": 6, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "DumpAgentError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "get_dump_area", + "code": 7, + "args": [ + { + "name": "index", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "humpty::DumpArea", + "reply_size": 10, + "error": "DumpAgentError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "claim_dump_area", + "code": 8, + "args_size": 0, + "reply": "humpty::DumpArea", + "reply_size": 10, + "error": "DumpAgentError", + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "dump_task", + "code": 9, + "args": [ + { + "name": "task_index", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "u8", + "reply_size": 1, + "error": "DumpAgentError", + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "dump_task_region", + "code": 10, + "args": [ + { + "name": "task_index", + "ty": "u32" + }, + { + "name": "address", + "ty": "u32" + }, + { + "name": "length", + "ty": "u32" + } + ], + "args_size": 12, + "reply": "u8", + "reply_size": 1, + "error": "DumpAgentError", + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "reinitialize_dump_from", + "code": 11, + "args": [ + { + "name": "index", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "DumpAgentError", + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "restart_me_raw", + "code": 12, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "read_fault_counts", + "code": 13, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[usize; hubris_num_tasks::NUM_TASKS]", + "read": false, + "write": true + } + ], + "idempotent": true + } + ] + }, + { + "name": "Net", + "task": "net", + "task_id": 1, + "ops": [ + { + "name": "recv_packet", + "code": 1, + "args": [ + { + "name": "socket", + "ty": "SocketName" + }, + { + "name": "large_payload_behavior", + "ty": "LargePayloadBehavior" + } + ], + "args_size": 8, + "reply": "UdpMetadata", + "reply_size": 24, + "error": "task_net_api::RecvError", + "encoding": "Hubpack", + "leases": [ + { + "name": "payload", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": false + }, + { + "name": "send_packet", + "code": 2, + "args": [ + { + "name": "socket", + "ty": "SocketName" + }, + { + "name": "metadata", + "ty": "UdpMetadata" + } + ], + "args_size": 28, + "reply": "()", + "reply_size": 0, + "error": "task_net_api::SendError", + "encoding": "Hubpack", + "leases": [ + { + "name": "payload", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": false + }, + { + "name": "smi_read", + "code": 3, + "args": [ + { + "name": "phy", + "ty": "u8" + }, + { + "name": "register", + "ty": "u8" + } + ], + "args_size": 2, + "reply": "u16", + "reply_size": 2, + "error": "ServerDeath", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "smi_write", + "code": 4, + "args": [ + { + "name": "phy", + "ty": "u8" + }, + { + "name": "register", + "ty": "u8" + }, + { + "name": "value", + "ty": "u16" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "ServerDeath", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "read_phy_reg", + "code": 5, + "args": [ + { + "name": "port", + "ty": "u8" + }, + { + "name": "page", + "ty": "u16" + }, + { + "name": "reg", + "ty": "u8" + } + ], + "args_size": 4, + "reply": "u16", + "reply_size": 2, + "error": "PhyError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "write_phy_reg", + "code": 6, + "args": [ + { + "name": "port", + "ty": "u8" + }, + { + "name": "page", + "ty": "u16" + }, + { + "name": "reg", + "ty": "u8" + }, + { + "name": "value", + "ty": "u16" + } + ], + "args_size": 6, + "reply": "()", + "reply_size": 0, + "error": "PhyError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "read_ksz8463_mac_count", + "code": 7, + "args_size": 0, + "reply": "usize", + "reply_size": 4, + "error": "KszError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "read_ksz8463_mac", + "code": 8, + "args": [ + { + "name": "i", + "ty": "u16" + } + ], + "args_size": 2, + "reply": "KszMacTableEntry", + "reply_size": 8, + "error": "KszError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "read_ksz8463_reg", + "code": 9, + "args": [ + { + "name": "reg", + "ty": "u16" + } + ], + "args_size": 2, + "reply": "u16", + "reply_size": 2, + "error": "KszError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "get_mac_address", + "code": 10, + "args_size": 0, + "reply": "MacAddress", + "reply_size": 6, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "get_spare_mac_addresses", + "code": 11, + "args_size": 0, + "reply": "MacAddressBlock", + "reply_size": 9, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "management_link_status", + "code": 12, + "args_size": 0, + "reply": "ManagementLinkStatus", + "reply_size": 6, + "error": "MgmtError", + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "management_counters", + "code": 13, + "args_size": 0, + "reply": "ManagementCounters", + "reply_size": 105, + "error": "MgmtError", + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "trust_vlan", + "code": 14, + "args": [ + { + "name": "vid", + "ty": "VLanId" + }, + { + "name": "trust_until", + "ty": "u64" + } + ], + "args_size": 16, + "reply": "()", + "reply_size": 0, + "error": "task_net_api::TrustError", + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "distrust_vlan", + "code": 15, + "args": [ + { + "name": "vid", + "ty": "VLanId" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "task_net_api::TrustError", + "encoding": "Hubpack", + "idempotent": false + } + ] + }, + { + "name": "Sys", + "task": "sys", + "task_id": 2, + "ops": [ + { + "name": "enable_clock_raw", + "code": 1, + "args": [ + { + "name": "peripheral", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "RccError", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "disable_clock_raw", + "code": 2, + "args": [ + { + "name": "peripheral", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "RccError", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "enter_reset_raw", + "code": 3, + "args": [ + { + "name": "peripheral", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "RccError", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "leave_reset_raw", + "code": 4, + "args": [ + { + "name": "peripheral", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "RccError", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "gpio_configure_raw", + "code": 5, + "args": [ + { + "name": "port", + "ty": "Port" + }, + { + "name": "pins", + "ty": "u16" + }, + { + "name": "packed_attributes", + "ty": "u16" + } + ], + "args_size": 5, + "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "gpio_set_reset", + "code": 6, + "args": [ + { + "name": "port", + "ty": "Port" + }, + { + "name": "set_pins", + "ty": "u16" + }, + { + "name": "reset_pins", + "ty": "u16" + } + ], + "args_size": 5, + "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "gpio_read_input", + "code": 7, + "args": [ + { + "name": "port", + "ty": "Port" + } + ], + "args_size": 1, + "reply": "u16", + "reply_size": 2, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "gpio_toggle", + "code": 8, + "args": [ + { + "name": "port", + "ty": "Port" + }, + { + "name": "pins", + "ty": "u16" + } + ], + "args_size": 3, + "reply": "()", + "reply_size": 0, + "error": "ServerDeath", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "read_uid", + "code": 9, + "args_size": 0, + "reply": "[u32; 3]", + "reply_size": 0, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "gpio_irq_configure", + "code": 10, + "args": [ + { + "name": "mask", + "ty": "u32" + }, + { + "name": "sensitivity", + "ty": "Edge" + } + ], + "args_size": 5, + "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "gpio_irq_control", + "code": 11, + "args": [ + { + "name": "mask", + "ty": "u32" + }, + { + "name": "op", + "ty": "IrqControl" + } + ], + "args_size": 5, + "reply": "bool", + "reply_size": 1, + "error": "ServerDeath", + "encoding": "Zerocopy", + "idempotent": false + } + ] + }, + { + "name": "Spi", + "task": "spi2_driver", + "task_id": 3, + "ops": [ + { + "name": "read", + "code": 1, + "args": [ + { + "name": "device_index", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "drv_spi_api::SpiError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "sink", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": false + }, + { + "name": "write", + "code": 2, + "args": [ + { + "name": "device_index", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "drv_spi_api::SpiError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "source", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": false + }, + { + "name": "exchange", + "code": 3, + "args": [ + { + "name": "device_index", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "drv_spi_api::SpiError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "source", + "ty": "[u8]", + "read": true, + "write": false + }, + { + "name": "sink", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": false + }, + { + "name": "lock", + "code": 4, + "args": [ + { + "name": "device_index", + "ty": "u8" + }, + { + "name": "cs_state", + "ty": "CsState" + } + ], + "args_size": 2, + "reply": "()", + "reply_size": 0, + "error": "ServerDeath", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "release", + "code": 5, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "ServerDeath", + "encoding": "Zerocopy", + "idempotent": false + } + ] + }, + { + "name": "Spi", + "task": "spi3_driver", + "task_id": 4, + "ops": [ + { + "name": "read", + "code": 1, + "args": [ + { + "name": "device_index", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "drv_spi_api::SpiError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "sink", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": false + }, + { + "name": "write", + "code": 2, + "args": [ + { + "name": "device_index", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "drv_spi_api::SpiError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "source", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": false + }, + { + "name": "exchange", + "code": 3, + "args": [ + { + "name": "device_index", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "drv_spi_api::SpiError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "source", + "ty": "[u8]", + "read": true, + "write": false + }, + { + "name": "sink", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": false + }, + { + "name": "lock", + "code": 4, + "args": [ + { + "name": "device_index", + "ty": "u8" + }, + { + "name": "cs_state", + "ty": "CsState" + } + ], + "args_size": 2, + "reply": "()", + "reply_size": 0, + "error": "ServerDeath", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "release", + "code": 5, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "ServerDeath", + "encoding": "Zerocopy", + "idempotent": false + } + ] + }, + { + "name": "Packrat", + "task": "packrat", + "task_id": 6, + "ops": [ + { + "name": "get_mac_address_block", + "code": 1, + "args_size": 0, + "reply": "MacAddressBlock", + "reply_size": 9, + "error": "CacheGetError", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "set_mac_address_block", + "code": 2, + "args": [ + { + "name": "macs", + "ty": "MacAddressBlock" + } + ], + "args_size": 9, + "reply": "()", + "reply_size": 0, + "error": "CacheSetError", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "get_identity", + "code": 3, + "args_size": 0, + "reply": "OxideIdentity", + "reply_size": 26, + "error": "CacheGetError", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "set_identity", + "code": 4, + "args": [ + { + "name": "macs", + "ty": "OxideIdentity" + } + ], + "args_size": 26, + "reply": "()", + "reply_size": 0, + "error": "CacheSetError", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "get_next_boot_host_startup_options", + "code": 5, + "args_size": 0, + "reply": "HostStartupOptions", + "reply_size": 8, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "set_next_boot_host_startup_options", + "code": 6, + "args": [ + { + "name": "startup_options", + "ty": "HostStartupOptions" + } + ], + "args_size": 8, + "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "remove_spd", + "code": 7, + "args": [ + { + "name": "index", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "set_spd_eeprom", + "code": 8, + "args": [ + { + "name": "index", + "ty": "u8" + }, + { + "name": "offset", + "ty": "usize" + } + ], + "args_size": 5, + "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": true + }, + { + "name": "get_spd_present", + "code": 9, + "args": [ + { + "name": "index", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "bool", + "reply_size": 1, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "get_spd_data", + "code": 10, + "args": [ + { + "name": "index", + "ty": "u8" + }, + { + "name": "offset", + "ty": "usize" + } + ], + "args_size": 5, + "reply": "u8", + "reply_size": 1, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "get_full_spd_data", + "code": 11, + "args": [ + { + "name": "index", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": true + }, + { + "name": "set_ereport_restart_id", + "code": 12, + "args": [ + { + "name": "restart_id", + "ty": "u128" + } + ], + "args_size": 16, + "reply": "()", + "reply_size": 0, + "error": "CacheSetError", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "deliver_encoded_ereport", + "code": 13, + "args_size": 0, + "reply": "ereport_messages::Ena", + "reply_size": 8, + "error": "EreportWriteError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": true + }, + { + "name": "read_ereports", + "code": 14, + "args": [ + { + "name": "request_id", + "ty": "ereport_messages::RequestIdV0" + }, + { + "name": "restart_id", + "ty": "ereport_messages::RestartId" + }, + { + "name": "start_ena", + "ty": "ereport_messages::Ena" + }, + { + "name": "limit", + "ty": "u8" + }, + { + "name": "committed_ena", + "ty": "ereport_messages::Ena" + } + ], + "args_size": 34, + "reply": "usize", + "reply_size": 4, + "error": "EreportReadError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": true + } + ] + }, + { + "name": "Rng", + "task": "rng_driver", + "task_id": 7, + "ops": [ + { + "name": "fill", + "code": 1, + "args_size": 0, + "reply": "usize", + "reply_size": 4, + "error": "RngError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "source", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": false + } + ] + }, + { + "name": "Thermal", + "task": "thermal", + "task_id": 8, + "ops": [ + { + "name": "set_mode_manual", + "code": 1, + "args": [ + { + "name": "initial_pwm", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "ThermalError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "set_mode_auto", + "code": 2, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "ThermalError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "get_mode", + "code": 3, + "args_size": 0, + "reply": "ThermalMode", + "reply_size": 1, + "error": "ThermalError", + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "get_auto_state", + "code": 4, + "args_size": 0, + "reply": "ThermalAutoState", + "reply_size": 1, + "error": "ThermalError", + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "disable_watchdog", + "code": 5, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "ThermalError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "enable_watchdog", + "code": 6, + "args": [ + { + "name": "timeout_s", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "ThermalError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "set_pid", + "code": 7, + "args": [ + { + "name": "z", + "ty": "f32" + }, + { + "name": "p", + "ty": "f32" + }, + { + "name": "i", + "ty": "f32" + }, + { + "name": "d", + "ty": "f32" + } + ], + "args_size": 16, + "reply": "()", + "reply_size": 0, + "error": "ThermalError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "get_margin", + "code": 8, + "args_size": 0, + "reply": "f32", + "reply_size": 4, + "error": "ThermalError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "set_margin", + "code": 9, + "args": [ + { + "name": "margin", + "ty": "f32" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "ThermalError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "update_dynamic_input", + "code": 10, + "args": [ + { + "name": "index", + "ty": "usize" + }, + { + "name": "model", + "ty": "ThermalProperties" + } + ], + "args_size": 20, + "reply": "()", + "reply_size": 0, + "error": "ThermalError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "remove_dynamic_input", + "code": 11, + "args": [ + { + "name": "index", + "ty": "usize" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "ThermalError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "get_runtime", + "code": 12, + "args_size": 0, + "reply": "u64", + "reply_size": 8, + "error": "ThermalError", + "encoding": "Zerocopy", + "idempotent": false + } + ] + }, + { + "name": "Power", + "task": "power", + "task_id": 9, + "ops": [ + { + "name": "pmbus_read", + "code": 1, + "args": [ + { + "name": "dev", + "ty": "Device" + }, + { + "name": "rail", + "ty": "u8" + }, + { + "name": "index", + "ty": "u32" + }, + { + "name": "op", + "ty": "Operation" + } + ], + "args_size": 8, + "reply": "PmbusValue", + "reply_size": 34, + "error": "ResponseCode", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "read_mode", + "code": 2, + "args": [ + { + "name": "dev", + "ty": "Device" + }, + { + "name": "rail", + "ty": "u8" + }, + { + "name": "index", + "ty": "u32" + } + ], + "args_size": 8, + "reply": "u8", + "reply_size": 1, + "error": "ResponseCode", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "phase_current", + "code": 3, + "args": [ + { + "name": "rail", + "ty": "SensorId" + }, + { + "name": "phase", + "ty": "u8" + } + ], + "args_size": 8, + "reply": "f32", + "reply_size": 4, + "error": "ResponseCode", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "bmr491_event_log_read", + "code": 4, + "args": [ + { + "name": "index", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "Bmr491Event", + "reply_size": 24, + "error": "ResponseCode", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "bmr491_fault_log_clear", + "code": 5, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "ResponseCode", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "bmr491_max_fault_event_index", + "code": 6, + "args_size": 0, + "reply": "u8", + "reply_size": 1, + "error": "ResponseCode", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "bmr491_max_lifecycle_event_index", + "code": 7, + "args_size": 0, + "reply": "u8", + "reply_size": 1, + "error": "ResponseCode", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "rendmp_blackbox_dump", + "code": 8, + "args": [ + { + "name": "addr", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "RenesasBlackbox", + "reply_size": 177, + "error": "ResponseCode", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "rendmp_dma_read", + "code": 9, + "args": [ + { + "name": "addr", + "ty": "u8" + }, + { + "name": "reg", + "ty": "u16" + } + ], + "args_size": 3, + "reply": "u32", + "reply_size": 4, + "error": "ResponseCode", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "rendmp_dma_write", + "code": 10, + "args": [ + { + "name": "addr", + "ty": "u8" + }, + { + "name": "reg", + "ty": "u16" + }, + { + "name": "data", + "ty": "u32" + } + ], + "args_size": 7, + "reply": "()", + "reply_size": 0, + "error": "ResponseCode", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "raw_pmbus_read_byte", + "code": 11, + "args": [ + { + "name": "index", + "ty": "u32" + }, + { + "name": "has_rail", + "ty": "bool" + }, + { + "name": "op", + "ty": "u8" + } + ], + "args_size": 8, + "reply": "u8", + "reply_size": 1, + "error": "ResponseCode", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "raw_pmbus_read_word", + "code": 12, + "args": [ + { + "name": "index", + "ty": "u32" + }, + { + "name": "has_rail", + "ty": "bool" + }, + { + "name": "op", + "ty": "u8" + } + ], + "args_size": 8, + "reply": "u16", + "reply_size": 2, + "error": "ResponseCode", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "raw_pmbus_read_word32", + "code": 13, + "args": [ + { + "name": "index", + "ty": "u32" + }, + { + "name": "has_rail", + "ty": "bool" + }, + { + "name": "op", + "ty": "u8" + } + ], + "args_size": 8, + "reply": "u32", + "reply_size": 4, + "error": "ResponseCode", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "raw_pmbus_read_block", + "code": 14, + "args": [ + { + "name": "index", + "ty": "u32" + }, + { + "name": "has_rail", + "ty": "bool" + }, + { + "name": "op", + "ty": "u8" + } + ], + "args_size": 8, + "reply": "RawPmbusBlock", + "reply_size": 33, + "error": "ResponseCode", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "raw_pmbus_set", + "code": 15, + "args": [ + { + "name": "index", + "ty": "u32" + }, + { + "name": "has_rail", + "ty": "bool" + }, + { + "name": "op", + "ty": "u8" + } + ], + "args_size": 8, + "reply": "()", + "reply_size": 0, + "error": "ResponseCode", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "raw_pmbus_write_byte", + "code": 16, + "args": [ + { + "name": "index", + "ty": "u32" + }, + { + "name": "has_rail", + "ty": "bool" + }, + { + "name": "op", + "ty": "u8" + }, + { + "name": "data", + "ty": "u8" + } + ], + "args_size": 8, + "reply": "()", + "reply_size": 0, + "error": "ResponseCode", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "raw_pmbus_write_word", + "code": 17, + "args": [ + { + "name": "index", + "ty": "u32" + }, + { + "name": "has_rail", + "ty": "bool" + }, + { + "name": "op", + "ty": "u8" + }, + { + "name": "data", + "ty": "u16" + } + ], + "args_size": 8, + "reply": "()", + "reply_size": 0, + "error": "ResponseCode", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "raw_pmbus_write_word32", + "code": 18, + "args": [ + { + "name": "index", + "ty": "u32" + }, + { + "name": "has_rail", + "ty": "bool" + }, + { + "name": "op", + "ty": "u8" + }, + { + "name": "data", + "ty": "u32" + } + ], + "args_size": 12, + "reply": "()", + "reply_size": 0, + "error": "ResponseCode", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "raw_pmbus_write_block", + "code": 19, + "args": [ + { + "name": "index", + "ty": "u32" + }, + { + "name": "has_rail", + "ty": "bool" + }, + { + "name": "op", + "ty": "u8" + }, + { + "name": "data", + "ty": "RawPmbusBlock" + } + ], + "args_size": 40, + "reply": "()", + "reply_size": 0, + "error": "ResponseCode", + "encoding": "Hubpack", + "idempotent": true + } + ] + }, + { + "name": "Sequencer", + "task": "cosmo_seq", + "task_id": 11, + "ops": [ + { + "name": "get_state", + "code": 1, + "args_size": 0, + "reply": "drv_cpu_power_state::PowerState", + "reply_size": 1, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "set_state", + "code": 2, + "args": [ + { + "name": "state", + "ty": "drv_cpu_power_state::PowerState" + } + ], + "args_size": 1, + "reply": "drv_cpu_seq_api::Transition", + "reply_size": 1, + "error": "drv_cpu_seq_api::SeqError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "set_state_with_reason", + "code": 3, + "args": [ + { + "name": "state", + "ty": "drv_cpu_power_state::PowerState" + }, + { + "name": "reason", + "ty": "StateChangeReason" + } + ], + "args_size": 2, + "reply": "drv_cpu_seq_api::Transition", + "reply_size": 1, + "error": "drv_cpu_seq_api::SeqError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "send_hardware_nmi", + "code": 4, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "ServerDeath", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "read_fpga_regs", + "code": 5, + "args_size": 0, + "reply": "[u8; 64]", + "reply_size": 0, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "last_post_code", + "code": 6, + "args_size": 0, + "reply": "u32", + "reply_size": 4, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "post_code_buffer_len", + "code": 7, + "args_size": 0, + "reply": "u32", + "reply_size": 4, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "get_post_code", + "code": 8, + "args": [ + { + "name": "index", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "u32", + "reply_size": 4, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "gpio_edge_count", + "code": 9, + "args_size": 0, + "reply": "u32", + "reply_size": 4, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "gpio_cycle_count", + "code": 10, + "args_size": 0, + "reply": "u32", + "reply_size": 4, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "enable_console_redirect", + "code": 11, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "disable_console_redirect", + "code": 12, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", + "idempotent": true + } + ] + }, + { + "name": "IgnitionFlash", + "task": "ignition_flash", + "task_id": 12, + "ops": [ + { + "name": "read_id", + "code": 1, + "args_size": 0, + "reply": "[u8; 20]", + "reply_size": 20, + "error": "IgnitionFlashError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "read_status", + "code": 2, + "args_size": 0, + "reply": "u8", + "reply_size": 1, + "error": "IgnitionFlashError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "bulk_erase", + "code": 3, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "IgnitionFlashError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "page_program", + "code": 4, + "args": [ + { + "name": "address", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "IgnitionFlashError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": false + }, + { + "name": "read", + "code": 5, + "args": [ + { + "name": "address", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "IgnitionFlashError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": false + }, + { + "name": "sector_erase", + "code": 6, + "args": [ + { + "name": "address", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "IgnitionFlashError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "select", + "code": 7, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "deselect", + "code": 8, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", + "idempotent": false + } + ] + }, + { + "name": "Spartan7Loader", + "task": "spartan7_loader", + "task_id": 13, + "ops": [ + { + "name": "ping", + "code": 1, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", + "idempotent": true + } + ] + }, + { + "name": "Hash", + "task": "hash_driver", + "task_id": 14, + "ops": [ + { + "name": "init_sha256", + "code": 1, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "HashError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "update", + "code": 2, + "args": [ + { + "name": "len", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "HashError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": false + }, + { + "name": "finalize_sha256", + "code": 3, + "args_size": 0, + "reply": "[u8; crate::SHA256_SZ]", + "reply_size": 32, + "error": "HashError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "digest_sha256", + "code": 4, + "args": [ + { + "name": "len", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "[u8; crate::SHA256_SZ]", + "reply_size": 32, + "error": "HashError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": false + } + ] + }, + { + "name": "HostFlash", + "task": "hf", + "task_id": 15, + "ops": [ + { + "name": "read_id", + "code": 1, + "args_size": 0, + "reply": "HfChipId", + "reply_size": 20, + "error": "HfError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "capacity", + "code": 2, + "args_size": 0, + "reply": "usize", + "reply_size": 4, + "error": "HfError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "read_status", + "code": 3, + "args_size": 0, + "reply": "u8", + "reply_size": 1, + "error": "HfError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "bulk_erase", + "code": 4, + "args": [ + { + "name": "protect", + "ty": "HfProtectMode" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "HfError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "page_program", + "code": 5, + "args": [ + { + "name": "address", + "ty": "u32" + }, + { + "name": "protect", + "ty": "HfProtectMode" + } + ], + "args_size": 5, + "reply": "()", + "reply_size": 0, + "error": "HfError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": false + }, + { + "name": "page_program_dev", + "code": 6, + "args": [ + { + "name": "dev", + "ty": "HfDevSelect" + }, + { + "name": "address", + "ty": "u32" + }, + { + "name": "protect", + "ty": "HfProtectMode" + } + ], + "args_size": 6, + "reply": "()", + "reply_size": 0, + "error": "HfError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": false + }, + { + "name": "read", + "code": 7, + "args": [ + { + "name": "address", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "HfError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": false + }, + { + "name": "read_dev", + "code": 8, + "args": [ + { + "name": "dev", + "ty": "HfDevSelect" + }, + { + "name": "address", + "ty": "u32" + } + ], + "args_size": 5, + "reply": "()", + "reply_size": 0, + "error": "HfError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": false + }, + { + "name": "sector_erase", + "code": 9, + "args": [ + { + "name": "address", + "ty": "u32" + }, + { + "name": "protect", + "ty": "HfProtectMode" + } + ], + "args_size": 5, + "reply": "()", + "reply_size": 0, + "error": "HfError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "sector_erase_dev", + "code": 10, + "args": [ + { + "name": "dev", + "ty": "HfDevSelect" + }, + { + "name": "address", + "ty": "u32" + }, + { + "name": "protect", + "ty": "HfProtectMode" + } + ], + "args_size": 6, + "reply": "()", + "reply_size": 0, + "error": "HfError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "get_mux", + "code": 11, + "args_size": 0, + "reply": "HfMuxState", + "reply_size": 1, + "error": "HfError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "set_mux", + "code": 12, + "args": [ + { + "name": "state", + "ty": "HfMuxState" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "HfError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "get_dev", + "code": 13, + "args_size": 0, + "reply": "HfDevSelect", + "reply_size": 1, + "error": "HfError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "set_dev", + "code": 14, + "args": [ + { + "name": "dev", + "ty": "HfDevSelect" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "HfError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "check_dev", + "code": 15, + "args": [ + { + "name": "dev", + "ty": "HfDevSelect" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "HfError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "hash", + "code": 16, + "args": [ + { + "name": "address", + "ty": "u32" + }, + { + "name": "len", + "ty": "u32" + } + ], + "args_size": 8, + "reply": "[u8; drv_hash_api::SHA256_SZ]", + "reply_size": 32, + "error": "HfError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "hash_significant_bits", + "code": 17, + "args": [ + { + "name": "dev", + "ty": "HfDevSelect" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "HfError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "get_cached_hash", + "code": 18, + "args": [ + { + "name": "dev", + "ty": "HfDevSelect" + } + ], + "args_size": 1, + "reply": "[u8; drv_hash_api::SHA256_SZ]", + "reply_size": 32, + "error": "HfError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "get_persistent_data", + "code": 19, + "args_size": 0, + "reply": "HfPersistentData", + "reply_size": 1, + "error": "HfError", + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "write_persistent_data", + "code": 20, + "args": [ + { + "name": "dev_select", + "ty": "HfDevSelect" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "HfError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "apob_begin", + "code": 21, + "args": [ + { + "name": "length", + "ty": "u32" + }, + { + "name": "algorithm", + "ty": "ApobHash" + } + ], + "args_size": 40, + "reply": "()", + "reply_size": 0, + "error": "ApobBeginError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "apob_write", + "code": 22, + "args": [ + { + "name": "offset", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "ApobWriteError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": true + }, + { + "name": "apob_commit", + "code": 23, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "ApobCommitError", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "apob_lock", + "code": 24, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "apob_read", + "code": 25, + "args": [ + { + "name": "offset", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "usize", + "reply_size": 4, + "error": "ApobReadError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": true + }, + { + "name": "apob_clear", + "code": 26, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "ApobClearError", + "encoding": "Zerocopy", + "idempotent": true + } + ] + }, + { + "name": "Update", + "task": "update_server", + "task_id": 16, + "ops": [ + { + "name": "block_size", + "code": 1, + "args_size": 0, + "reply": "usize", + "reply_size": 4, + "error": "drv_update_api::UpdateError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "prep_image_update", + "code": 2, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "drv_update_api::UpdateError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "write_one_block", + "code": 3, + "args": [ + { + "name": "block_num", + "ty": "usize" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "drv_update_api::UpdateError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "block", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": false + }, + { + "name": "abort_update", + "code": 4, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "drv_update_api::UpdateError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "finish_image_update", + "code": 5, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "drv_update_api::UpdateError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "current_version", + "code": 6, + "args_size": 0, + "reply": "ImageVersion", + "reply_size": 8, + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "read_caboose_value", + "code": 7, + "args": [ + { + "name": "name", + "ty": "[u8; 4]" + } + ], + "args_size": 4, + "reply": "u32", + "reply_size": 4, + "error": "CabooseError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": true + }, + { + "name": "get_pending_boot_slot", + "code": 8, + "args_size": 0, + "reply": "SlotId", + "reply_size": 1, + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "set_pending_boot_slot", + "code": 9, + "args": [ + { + "name": "slot", + "ty": "SlotId" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "drv_update_api::UpdateError", + "encoding": "Hubpack", + "idempotent": false + } + ] + }, + { + "name": "Sensor", + "task": "sensor", + "task_id": 17, + "ops": [ + { + "name": "get", + "code": 1, + "args": [ + { + "name": "id", + "ty": "SensorId" + } + ], + "args_size": 4, + "reply": "f32", + "reply_size": 4, + "error": "SensorError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "get_reading", + "code": 2, + "args": [ + { + "name": "id", + "ty": "SensorId" + } + ], + "args_size": 4, + "reply": "Reading", + "reply_size": 12, + "error": "SensorError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "get_raw_reading", + "code": 3, + "args": [ + { + "name": "id", + "ty": "SensorId" + } + ], + "args_size": 4, + "reply": "Option<(Result, u64)>", + "reply_size": 0, + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "get_last_data", + "code": 4, + "args": [ + { + "name": "id", + "ty": "SensorId" + } + ], + "args_size": 4, + "reply": "Option<(f32, u64)>", + "reply_size": 13, + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "get_last_nodata", + "code": 5, + "args": [ + { + "name": "id", + "ty": "SensorId" + } + ], + "args_size": 4, + "reply": "Option<(NoData, u64)>", + "reply_size": 0, + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "get_min", + "code": 6, + "args": [ + { + "name": "id", + "ty": "SensorId" + } + ], + "args_size": 4, + "reply": "(f32, u64)", + "reply_size": 12, + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "get_max", + "code": 7, + "args": [ + { + "name": "id", + "ty": "SensorId" + } + ], + "args_size": 4, + "reply": "(f32, u64)", + "reply_size": 12, + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "post", + "code": 8, + "args": [ + { + "name": "id", + "ty": "SensorId" + }, + { + "name": "value", + "ty": "f32" + }, + { + "name": "timestamp", + "ty": "u64" + } + ], + "args_size": 16, + "reply": "()", + "reply_size": 0, + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "nodata", + "code": 9, + "args": [ + { + "name": "id", + "ty": "SensorId" + }, + { + "name": "nodata", + "ty": "NoData" + }, + { + "name": "timestamp", + "ty": "u64" + } + ], + "args_size": 16, + "reply": "()", + "reply_size": 0, + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "get_nerrors", + "code": 10, + "args": [ + { + "name": "id", + "ty": "SensorId" + } + ], + "args_size": 4, + "reply": "u32", + "reply_size": 4, + "encoding": "Hubpack", + "idempotent": true + } + ] + }, + { + "name": "HostSpComms", + "task": "host_sp_comms", + "task_id": 18, + "ops": [ + { + "name": "set_status", + "code": 1, + "args": [ + { + "name": "status", + "ty": "u64" + } + ], + "args_size": 8, + "reply": "()", + "reply_size": 0, + "error": "HostSpCommsError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "get_status", + "code": 2, + "args_size": 0, + "reply": "Status", + "reply_size": 8, + "error": "HostSpCommsError", + "encoding": "Zerocopy", + "idempotent": false + } + ] + }, + { + "name": "ControlPlaneAgent", + "task": "control_plane_agent", + "task_id": 21, + "ops": [ + { + "name": "fetch_host_phase2_data", + "code": 1, + "args": [ + { + "name": "image_hash", + "ty": "[u8; 32]" + }, + { + "name": "offset", + "ty": "u64" + }, + { + "name": "notification_bit", + "ty": "u8" + } + ], + "args_size": 41, + "reply": "()", + "reply_size": 0, + "error": "ControlPlaneAgentError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "get_host_phase2_data", + "code": 2, + "args": [ + { + "name": "image_hash", + "ty": "[u8; 32]" + }, + { + "name": "offset", + "ty": "u64" + } + ], + "args_size": 40, + "reply": "usize", + "reply_size": 4, + "error": "ControlPlaneAgentError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": false + }, + { + "name": "get_startup_options", + "code": 3, + "args_size": 0, + "reply": "HostStartupOptions", + "reply_size": 8, + "error": "ControlPlaneAgentError", + "encoding": "Ssmarshal", + "idempotent": false + }, + { + "name": "set_startup_options", + "code": 4, + "args": [ + { + "name": "startup_options", + "ty": "u64" + } + ], + "args_size": 8, + "reply": "()", + "reply_size": 0, + "error": "ControlPlaneAgentError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "identity", + "code": 5, + "args_size": 0, + "reply": "OxideIdentity", + "reply_size": 26, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "get_uart_client", + "code": 6, + "args_size": 0, + "reply": "UartClient", + "reply_size": 1, + "encoding": "Ssmarshal", + "idempotent": true + }, + { + "name": "get_installinator_image_id", + "code": 7, + "args_size": 0, + "reply": "usize", + "reply_size": 4, + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": true + }, + { + "name": "set_humility_uart_client", + "code": 8, + "args": [ + { + "name": "attach", + "ty": "bool" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "ControlPlaneAgentError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "uart_read", + "code": 9, + "args_size": 0, + "reply": "usize", + "reply_size": 4, + "error": "ControlPlaneAgentError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": false + }, + { + "name": "uart_write", + "code": 10, + "args_size": 0, + "reply": "usize", + "reply_size": 4, + "error": "ControlPlaneAgentError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": false + } + ] + }, + { + "name": "SpRot", + "task": "sprot", + "task_id": 22, + "ops": [ + { + "name": "status", + "code": 1, + "args_size": 0, + "reply": "SprotStatus", + "reply_size": 24, + "error": "SprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "io_stats", + "code": 2, + "args_size": 0, + "reply": "SprotIoStats", + "reply_size": 68, + "error": "SprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "rot_state", + "code": 3, + "args_size": 0, + "reply": "RotState", + "reply_size": 138, + "error": "SprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "pulse_cs", + "code": 4, + "args": [ + { + "name": "delay", + "ty": "u16" + } + ], + "args_size": 2, + "reply": "PulseStatus", + "reply_size": 2, + "error": "SprotError", + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "block_size", + "code": 5, + "args_size": 0, + "reply": "u32", + "reply_size": 4, + "error": "SprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "prep_image_update", + "code": 6, + "args": [ + { + "name": "target", + "ty": "UpdateTarget" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "SprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "write_one_block", + "code": 7, + "args": [ + { + "name": "block_num", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "SprotError", + "encoding": "Hubpack", + "leases": [ + { + "name": "block", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": false + }, + { + "name": "abort_update", + "code": 8, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "SprotError", + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "finish_image_update", + "code": 9, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "SprotError", + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "switch_default_image", + "code": 10, + "args": [ + { + "name": "slot", + "ty": "SlotId" + }, + { + "name": "duration", + "ty": "SwitchDuration" + } + ], + "args_size": 2, + "reply": "()", + "reply_size": 0, + "error": "SprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "reset", + "code": 11, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "SprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "dump", + "code": 12, + "args": [ + { + "name": "address", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "DumpOrSprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "caboose_size", + "code": 13, + "args": [ + { + "name": "slot", + "ty": "SlotId" + } + ], + "args_size": 1, + "reply": "u32", + "reply_size": 4, + "error": "RawCabooseOrSprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "read_caboose_region", + "code": 14, + "args": [ + { + "name": "offset", + "ty": "u32" + }, + { + "name": "slot", + "ty": "SlotId" + } + ], + "args_size": 8, + "reply": "()", + "reply_size": 0, + "error": "RawCabooseOrSprotError", + "encoding": "Hubpack", + "leases": [ + { + "name": "out", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": true + }, + { + "name": "rot_boot_info", + "code": 15, + "args_size": 0, + "reply": "RotBootInfo", + "reply_size": 72, + "error": "SprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "cert_chain_len", + "code": 16, + "args_size": 0, + "reply": "u32", + "reply_size": 4, + "error": "AttestOrSprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "cert_len", + "code": 17, + "args": [ + { + "name": "index", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "u32", + "reply_size": 4, + "error": "AttestOrSprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "cert", + "code": 18, + "args": [ + { + "name": "index", + "ty": "u32" + }, + { + "name": "offset", + "ty": "u32" + } + ], + "args_size": 8, + "reply": "()", + "reply_size": 0, + "error": "AttestOrSprotError", + "encoding": "Hubpack", + "leases": [ + { + "name": "dest", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": true + }, + { + "name": "record", + "code": 19, + "args": [ + { + "name": "algorithm", + "ty": "HashAlgorithm" + } + ], + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "AttestOrSprotError", + "encoding": "Hubpack", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": false + }, + { + "name": "read_rot_page", + "code": 20, + "args": [ + { + "name": "page", + "ty": "RotPage" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "SprotError", + "encoding": "Hubpack", + "leases": [ + { + "name": "dest", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": true + }, + { + "name": "log", + "code": 21, + "args": [ + { + "name": "offset", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "AttestOrSprotError", + "encoding": "Hubpack", + "leases": [ + { + "name": "dest", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": false + }, + { + "name": "log_len", + "code": 22, + "args_size": 0, + "reply": "u32", + "reply_size": 4, + "error": "AttestOrSprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "attest", + "code": 23, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "AttestOrSprotError", + "encoding": "Hubpack", + "leases": [ + { + "name": "nonce", + "ty": "[u8]", + "read": true, + "write": false + }, + { + "name": "dest", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": false + }, + { + "name": "attest_len", + "code": 24, + "args_size": 0, + "reply": "u32", + "reply_size": 4, + "error": "AttestOrSprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "enable_sp_slot_watchdog", + "code": 25, + "args": [ + { + "name": "time_ms", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "SprotError", + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "disable_sp_slot_watchdog", + "code": 26, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "SprotError", + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "sp_slot_watchdog_supported", + "code": 27, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "SprotError", + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "versioned_rot_boot_info", + "code": 28, + "args": [ + { + "name": "version", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "VersionedRotBootInfo", + "reply_size": 147, + "error": "SprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "component_caboose_size", + "code": 29, + "args": [ + { + "name": "component", + "ty": "RotComponent" + }, + { + "name": "slot", + "ty": "SlotId" + } + ], + "args_size": 2, + "reply": "u32", + "reply_size": 4, + "error": "RawCabooseOrSprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "component_read_caboose_region", + "code": 30, + "args": [ + { + "name": "offset", + "ty": "u32" + }, + { + "name": "component", + "ty": "RotComponent" + }, + { + "name": "slot", + "ty": "SlotId" + } + ], + "args_size": 8, + "reply": "()", + "reply_size": 0, + "error": "RawCabooseOrSprotError", + "encoding": "Hubpack", + "leases": [ + { + "name": "out", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": true + }, + { + "name": "component_prep_image_update", + "code": 31, + "args": [ + { + "name": "component", + "ty": "RotComponent" + }, + { + "name": "slot", + "ty": "SlotId" + } + ], + "args_size": 2, + "reply": "()", + "reply_size": 0, + "error": "SprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "component_switch_default_image", + "code": 32, + "args": [ + { + "name": "component", + "ty": "RotComponent" + }, + { + "name": "slot", + "ty": "SlotId" + }, + { + "name": "duration", + "ty": "SwitchDuration" + } + ], + "args_size": 3, + "reply": "()", + "reply_size": 0, + "error": "SprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "lifecycle_state", + "code": 33, + "args_size": 0, + "reply": "LifecycleState", + "reply_size": 1, + "error": "StateOrSprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "tq_cert_chain_len", + "code": 34, + "args_size": 0, + "reply": "u32", + "reply_size": 4, + "error": "AttestOrSprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "tq_cert_len", + "code": 35, + "args": [ + { + "name": "index", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "u32", + "reply_size": 4, + "error": "AttestOrSprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "tq_cert", + "code": 36, + "args": [ + { + "name": "index", + "ty": "u32" + }, + { + "name": "offset", + "ty": "u32" + } + ], + "args_size": 8, + "reply": "()", + "reply_size": 0, + "error": "AttestOrSprotError", + "encoding": "Hubpack", + "leases": [ + { + "name": "dest", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": true + }, + { + "name": "tq_sign", + "code": 37, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "AttestOrSprotError", + "encoding": "Hubpack", + "leases": [ + { + "name": "tq", + "ty": "[u8]", + "read": true, + "write": false + }, + { + "name": "dest", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": false + }, + { + "name": "tq_sign_len", + "code": 38, + "args_size": 0, + "reply": "u32", + "reply_size": 4, + "error": "AttestOrSprotError", + "encoding": "Hubpack", + "idempotent": true + } + ] + }, + { + "name": "Validate", + "task": "validate", + "task_id": 23, + "ops": [ + { + "name": "validate_i2c", + "code": 1, + "args": [ + { + "name": "index", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "ValidateOk", + "reply_size": 1, + "error": "ValidateError", + "encoding": "Zerocopy", + "idempotent": true + } + ] + }, + { + "name": "Vpd", + "task": "vpd", + "task_id": 24, + "ops": [ + { + "name": "read_tmp117_eeprom", + "code": 1, + "args": [ + { + "name": "index", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "[u8; 6]", + "reply_size": 6, + "error": "VpdError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "read", + "code": 2, + "args": [ + { + "name": "index", + "ty": "u8" + }, + { + "name": "offset", + "ty": "u16" + } + ], + "args_size": 3, + "reply": "[u8; 16]", + "reply_size": 16, + "error": "VpdError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "write", + "code": 3, + "args": [ + { + "name": "index", + "ty": "u8" + }, + { + "name": "offset", + "ty": "u16" + }, + { + "name": "contents", + "ty": "u8" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "VpdError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "is_locked", + "code": 4, + "args": [ + { + "name": "index", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "bool", + "reply_size": 1, + "error": "VpdError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "permanently_lock", + "code": 5, + "args": [ + { + "name": "index", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "VpdError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "num_vpd_devices", + "code": 6, + "args_size": 0, + "reply": "usize", + "reply_size": 4, + "encoding": "Zerocopy", + "idempotent": true + } + ] + }, + { + "name": "UserLeds", + "task": "user_leds", + "task_id": 25, + "ops": [ + { + "name": "led_on", + "code": 1, + "args": [ + { + "name": "index", + "ty": "usize" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "LedError", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "led_off", + "code": 2, + "args": [ + { + "name": "index", + "ty": "usize" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "LedError", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "led_toggle", + "code": 3, + "args": [ + { + "name": "index", + "ty": "usize" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "LedError", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "led_blink", + "code": 4, + "args": [ + { + "name": "index", + "ty": "usize" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "LedError", + "encoding": "Zerocopy", + "idempotent": true + } + ] + }, + { + "name": "DumpAgent", + "task": "dump_agent", + "task_id": 26, + "ops": [ + { + "name": "read_dump", + "code": 1, + "args": [ + { + "name": "index", + "ty": "u8" + }, + { + "name": "offset", + "ty": "u32" + } + ], + "args_size": 5, + "reply": "[u8; DUMP_READ_SIZE]", + "reply_size": 256, + "error": "DumpAgentError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "read_dump_into", + "code": 2, + "args": [ + { + "name": "index", + "ty": "u8" + }, + { + "name": "offset", + "ty": "u32" + } + ], + "args_size": 5, + "reply": "usize", + "reply_size": 4, + "error": "DumpAgentError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "out", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": false + }, + { + "name": "get_dump_area", + "code": 3, + "args": [ + { + "name": "index", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "DumpArea", + "reply_size": 10, + "error": "DumpAgentError", + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "initialize_dump", + "code": 4, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "DumpAgentError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "add_dump_segment", + "code": 5, + "args": [ + { + "name": "address", + "ty": "u32" + }, + { + "name": "length", + "ty": "u32" + } + ], + "args_size": 8, + "reply": "()", + "reply_size": 0, + "error": "DumpAgentError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "take_dump", + "code": 6, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "DumpAgentError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "dump_task", + "code": 7, + "args": [ + { + "name": "task_index", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "u8", + "reply_size": 1, + "error": "DumpAgentError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "dump_task_region", + "code": 8, + "args": [ + { + "name": "task_index", + "ty": "u32" + }, + { + "name": "start", + "ty": "u32" + }, + { + "name": "length", + "ty": "u32" + } + ], + "args_size": 12, + "reply": "u8", + "reply_size": 1, + "error": "DumpAgentError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "reinitialize_dump_from", + "code": 9, + "args": [ + { + "name": "index", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "DumpAgentError", + "encoding": "Hubpack", + "idempotent": false + } + ] + }, + { + "name": "CosmoSpd", + "task": "spd", + "task_id": 28, + "ops": [ + { + "name": "ping", + "code": 1, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", + "idempotent": true + } + ] + }, + { + "name": "AuxFlash", + "task": "auxflash", + "task_id": 29, + "ops": [ + { + "name": "read_id", + "code": 1, + "args_size": 0, + "reply": "AuxFlashId", + "reply_size": 11, + "error": "AuxFlashError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "slot_count", + "code": 2, + "args_size": 0, + "reply": "u32", + "reply_size": 4, + "error": "AuxFlashError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "slot_size", + "code": 3, + "args_size": 0, + "reply": "u32", + "reply_size": 4, + "error": "AuxFlashError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "read_status", + "code": 4, + "args_size": 0, + "reply": "u8", + "reply_size": 1, + "error": "AuxFlashError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "erase_slot", + "code": 5, + "args": [ + { + "name": "slot", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "AuxFlashError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "slot_sector_erase", + "code": 6, + "args": [ + { + "name": "slot", + "ty": "u32" + }, + { + "name": "offset", + "ty": "u32" + } + ], + "args_size": 8, + "reply": "()", + "reply_size": 0, + "error": "AuxFlashError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "read_slot_chck", + "code": 7, + "args": [ + { + "name": "slot", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "AuxFlashChecksum", + "reply_size": 32, + "error": "AuxFlashError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "write_slot_with_offset", + "code": 8, + "args": [ + { + "name": "slot", + "ty": "u32" + }, + { + "name": "offset", + "ty": "u32" + } + ], + "args_size": 8, + "reply": "()", + "reply_size": 0, + "error": "AuxFlashError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": false + }, + { + "name": "read_slot_with_offset", + "code": 9, + "args": [ + { + "name": "slot", + "ty": "u32" + }, + { + "name": "offset", + "ty": "u32" + } + ], + "args_size": 8, + "reply": "()", + "reply_size": 0, + "error": "AuxFlashError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": false + }, + { + "name": "scan_and_get_active_slot", + "code": 10, + "args_size": 0, + "reply": "u32", + "reply_size": 4, + "error": "AuxFlashError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "get_active_slot", + "code": 11, + "args_size": 0, + "reply": "u32", + "reply_size": 4, + "error": "AuxFlashError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "ensure_redundancy", + "code": 12, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "AuxFlashError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "get_blob_by_tag", + "code": 13, + "args": [ + { + "name": "name", + "ty": "[u8; 4]" + } + ], + "args_size": 4, + "reply": "AuxFlashBlob", + "reply_size": 12, + "error": "AuxFlashError", + "encoding": "Zerocopy", + "idempotent": false + } + ] + }, + { + "name": "FmcDemo", + "task": "fmc_demo", + "task_id": 32, + "ops": [ + { + "name": "peek16", + "code": 1, + "args": [ + { + "name": "addr", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "u16", + "reply_size": 2, + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "peek32", + "code": 2, + "args": [ + { + "name": "addr", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "u32", + "reply_size": 4, + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "peek64", + "code": 3, + "args": [ + { + "name": "addr", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "u64", + "reply_size": 8, + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "poke16", + "code": 4, + "args": [ + { + "name": "addr", + "ty": "u32" + }, + { + "name": "value", + "ty": "u16" + } + ], + "args_size": 8, + "reply": "()", + "reply_size": 0, + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "poke32", + "code": 5, + "args": [ + { + "name": "addr", + "ty": "u32" + }, + { + "name": "value", + "ty": "u32" + } + ], + "args_size": 8, + "reply": "()", + "reply_size": 0, + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "poke64", + "code": 6, + "args": [ + { + "name": "addr", + "ty": "u32" + }, + { + "name": "value", + "ty": "u64" + } + ], + "args_size": 16, + "reply": "()", + "reply_size": 0, + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "set_burst_enable", + "code": 7, + "args": [ + { + "name": "flag", + "ty": "bool" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "set_write_enable", + "code": 8, + "args": [ + { + "name": "flag", + "ty": "bool" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "set_write_fifo", + "code": 9, + "args": [ + { + "name": "flag", + "ty": "bool" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "set_wait", + "code": 10, + "args": [ + { + "name": "flag", + "ty": "bool" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "set_data_latency_cycles", + "code": 11, + "args": [ + { + "name": "n", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "set_clock_divider", + "code": 12, + "args": [ + { + "name": "n", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "set_bus_turnaround_cycles", + "code": 13, + "args": [ + { + "name": "n", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "encoding": "Hubpack", + "idempotent": false + } + ] + } + ] +} \ No newline at end of file diff --git a/humility-hif-assembler/fixtures/gimlet-c.json b/humility-hif-assembler/fixtures/gimlet-c.json new file mode 100644 index 000000000..8c91dc6a9 --- /dev/null +++ b/humility-hif-assembler/fixtures/gimlet-c.json @@ -0,0 +1,7018 @@ +{ + "image_id": [ + 93, + 189, + 115, + 8, + 27, + 233, + 158, + 209 + ], + "board": "gimlet-c", + "buses": [ + { + "name": "mid", + "controller": 3, + "port_index": 0, + "port_name": "H", + "devices": [ + { + "address": 36, + "device": "tps546b24a", + "name": "v3p3_sp_a2", + "description": "A2 3.3V rail", + "removable": false, + "sensors": [ + { + "name": "V3P3_SP_A2", + "kind": "Temperature" + }, + { + "name": "V3P3_SP_A2", + "kind": "Current" + }, + { + "name": "V3P3_SP_A2", + "kind": "Voltage" + } + ] + }, + { + "address": 38, + "device": "tps546b24a", + "name": "v3p3_sys_a0", + "description": "A0 3.3V rail", + "removable": false, + "sensors": [ + { + "name": "V3P3_SYS_A0", + "kind": "Temperature" + }, + { + "name": "V3P3_SYS_A0", + "kind": "Current" + }, + { + "name": "V3P3_SYS_A0", + "kind": "Voltage" + } + ] + }, + { + "address": 39, + "device": "tps546b24a", + "name": "v5p0_sys_a2", + "description": "A2 5V rail", + "removable": false, + "sensors": [ + { + "name": "V5_SYS_A2", + "kind": "Temperature" + }, + { + "name": "V5_SYS_A2", + "kind": "Current" + }, + { + "name": "V5_SYS_A2", + "kind": "Voltage" + } + ] + }, + { + "address": 41, + "device": "tps546b24a", + "name": "v1p8_sys_a2", + "description": "A2 1.8V rail", + "removable": false, + "sensors": [ + { + "name": "V1P8_SYS_A2", + "kind": "Temperature" + }, + { + "name": "V1P8_SYS_A2", + "kind": "Current" + }, + { + "name": "V1P8_SYS_A2", + "kind": "Voltage" + } + ] + }, + { + "address": 58, + "device": "max5970", + "name": "m2", + "description": "M.2 hot plug controller", + "removable": false, + "sensors": [ + { + "name": "V3P3_M2A_A0HP", + "kind": "Current" + }, + { + "name": "V3P3_M2B_A0HP", + "kind": "Current" + }, + { + "name": "V3P3_M2A_A0HP", + "kind": "Voltage" + }, + { + "name": "V3P3_M2B_A0HP", + "kind": "Voltage" + } + ] + }, + { + "address": 60, + "device": "sbrmi", + "name": "RMI", + "description": "CPU via SB-RMI", + "removable": false + }, + { + "address": 76, + "device": "sbtsi", + "name": "CPU", + "description": "CPU temperature sensor", + "removable": false, + "sensors": [ + { + "name": "CPU", + "kind": "Temperature" + } + ] + }, + { + "address": 88, + "device": "idt8a34003", + "description": "Clock generator", + "removable": false + }, + { + "address": 90, + "device": "raa229618", + "description": "CPU power controller", + "removable": false, + "sensors": [ + { + "name": "VDD_VCORE", + "kind": "Temperature" + }, + { + "name": "VDD_MEM_ABCD", + "kind": "Temperature" + }, + { + "name": "VDD_VCORE", + "kind": "Power" + }, + { + "name": "VDD_MEM_ABCD", + "kind": "Power" + }, + { + "name": "VDD_VCORE", + "kind": "Current" + }, + { + "name": "VDD_MEM_ABCD", + "kind": "Current" + }, + { + "name": "VDD_VCORE", + "kind": "Voltage" + }, + { + "name": "VDD_MEM_ABCD", + "kind": "Voltage" + } + ] + }, + { + "address": 91, + "device": "raa229618", + "description": "SoC power controller", + "removable": false, + "sensors": [ + { + "name": "VDDCR_SOC", + "kind": "Temperature" + }, + { + "name": "VDD_MEM_EFGH", + "kind": "Temperature" + }, + { + "name": "VDDCR_SOC", + "kind": "Power" + }, + { + "name": "VDD_MEM_EFGH", + "kind": "Power" + }, + { + "name": "VDDCR_SOC", + "kind": "Current" + }, + { + "name": "VDD_MEM_EFGH", + "kind": "Current" + }, + { + "name": "VDDCR_SOC", + "kind": "Voltage" + }, + { + "name": "VDD_MEM_EFGH", + "kind": "Voltage" + } + ] + }, + { + "address": 92, + "device": "isl68224", + "description": "DIMM/SP3 1.8V A0 power controller", + "removable": false, + "sensors": [ + { + "name": "VPP_ABCD", + "kind": "Current" + }, + { + "name": "VPP_EFGH", + "kind": "Current" + }, + { + "name": "V1P8_SP3", + "kind": "Current" + }, + { + "name": "VPP_ABCD", + "kind": "Voltage" + }, + { + "name": "VPP_EFGH", + "kind": "Voltage" + }, + { + "name": "V1P8_SP3", + "kind": "Voltage" + } + ] + }, + { + "address": 24, + "device": "tse2004av", + "name": "DIMM_A0", + "description": "DIMM A0", + "removable": true, + "sensors": [ + { + "name": "DIMM_A0", + "kind": "Temperature" + } + ] + }, + { + "address": 25, + "device": "tse2004av", + "name": "DIMM_A1", + "description": "DIMM A1", + "removable": true, + "sensors": [ + { + "name": "DIMM_A1", + "kind": "Temperature" + } + ] + }, + { + "address": 26, + "device": "tse2004av", + "name": "DIMM_B0", + "description": "DIMM B0", + "removable": true, + "sensors": [ + { + "name": "DIMM_B0", + "kind": "Temperature" + } + ] + }, + { + "address": 27, + "device": "tse2004av", + "name": "DIMM_B1", + "description": "DIMM B1", + "removable": true, + "sensors": [ + { + "name": "DIMM_B1", + "kind": "Temperature" + } + ] + }, + { + "address": 28, + "device": "tse2004av", + "name": "DIMM_C0", + "description": "DIMM C0", + "removable": true, + "sensors": [ + { + "name": "DIMM_C0", + "kind": "Temperature" + } + ] + }, + { + "address": 29, + "device": "tse2004av", + "name": "DIMM_C1", + "description": "DIMM C1", + "removable": true, + "sensors": [ + { + "name": "DIMM_C1", + "kind": "Temperature" + } + ] + }, + { + "address": 30, + "device": "tse2004av", + "name": "DIMM_D0", + "description": "DIMM D0", + "removable": true, + "sensors": [ + { + "name": "DIMM_D0", + "kind": "Temperature" + } + ] + }, + { + "address": 31, + "device": "tse2004av", + "name": "DIMM_D1", + "description": "DIMM D1", + "removable": true, + "sensors": [ + { + "name": "DIMM_D1", + "kind": "Temperature" + } + ] + } + ] + }, + { + "name": "rear", + "controller": 4, + "port_index": 0, + "port_name": "F", + "devices": [ + { + "address": 16, + "device": "adm127x", + "description": "Fan hot swap controller", + "removable": false, + "sensors": [ + { + "name": "V54_FAN", + "kind": "Temperature" + }, + { + "name": "V54_FAN", + "kind": "Current" + }, + { + "name": "V54_FAN", + "kind": "Voltage" + } + ] + }, + { + "address": 20, + "device": "adm127x", + "description": "Sled hot swap controller", + "removable": false, + "sensors": [ + { + "name": "V54_HS_OUTPUT", + "kind": "Temperature" + }, + { + "name": "V54_HS_OUTPUT", + "kind": "Current" + }, + { + "name": "V54_HS_OUTPUT", + "kind": "Voltage" + } + ] + }, + { + "address": 32, + "device": "max31790", + "description": "Fan controller", + "removable": false, + "sensors": [ + { + "name": "Southeast", + "kind": "Speed" + }, + { + "name": "Northeast", + "kind": "Speed" + }, + { + "name": "South", + "kind": "Speed" + }, + { + "name": "North", + "kind": "Speed" + }, + { + "name": "Southwest", + "kind": "Speed" + }, + { + "name": "Northwest", + "kind": "Speed" + } + ] + }, + { + "address": 37, + "device": "tps546b24a", + "name": "v0p96_nic", + "description": "T6 power controller", + "removable": false, + "sensors": [ + { + "name": "V0P96_NIC_VDD_A0HP", + "kind": "Temperature" + }, + { + "name": "V0P96_NIC_VDD_A0HP", + "kind": "Current" + }, + { + "name": "V0P96_NIC_VDD_A0HP", + "kind": "Voltage" + } + ] + }, + { + "address": 72, + "device": "tmp117", + "name": "Northeast", + "description": "Northeast temperature sensor", + "removable": true, + "sensors": [ + { + "name": "Northeast", + "kind": "Temperature" + } + ] + }, + { + "address": 73, + "device": "tmp117", + "name": "North", + "description": "North temperature sensor", + "removable": true, + "sensors": [ + { + "name": "North", + "kind": "Temperature" + } + ] + }, + { + "address": 74, + "device": "tmp117", + "name": "Northwest", + "description": "Northwest temperature sensor", + "removable": true, + "sensors": [ + { + "name": "Northwest", + "kind": "Temperature" + } + ] + }, + { + "address": 103, + "device": "bmr491", + "name": "IBC", + "description": "Intermediate bus converter", + "removable": false, + "sensors": [ + { + "name": "V12_SYS_A2", + "kind": "Temperature" + }, + { + "name": "V12_SYS_A2", + "kind": "Power" + }, + { + "name": "V12_SYS_A2", + "kind": "Current" + }, + { + "name": "V12_SYS_A2", + "kind": "Voltage" + } + ] + }, + { + "address": 24, + "device": "tse2004av", + "name": "DIMM_E0", + "description": "DIMM E0", + "removable": true, + "sensors": [ + { + "name": "DIMM_E0", + "kind": "Temperature" + } + ] + }, + { + "address": 25, + "device": "tse2004av", + "name": "DIMM_E1", + "description": "DIMM E1", + "removable": true, + "sensors": [ + { + "name": "DIMM_E1", + "kind": "Temperature" + } + ] + }, + { + "address": 26, + "device": "tse2004av", + "name": "DIMM_F0", + "description": "DIMM F0", + "removable": true, + "sensors": [ + { + "name": "DIMM_F0", + "kind": "Temperature" + } + ] + }, + { + "address": 27, + "device": "tse2004av", + "name": "DIMM_F1", + "description": "DIMM F1", + "removable": true, + "sensors": [ + { + "name": "DIMM_F1", + "kind": "Temperature" + } + ] + }, + { + "address": 28, + "device": "tse2004av", + "name": "DIMM_G0", + "description": "DIMM G0", + "removable": true, + "sensors": [ + { + "name": "DIMM_G0", + "kind": "Temperature" + } + ] + }, + { + "address": 29, + "device": "tse2004av", + "name": "DIMM_G1", + "description": "DIMM G1", + "removable": true, + "sensors": [ + { + "name": "DIMM_G1", + "kind": "Temperature" + } + ] + }, + { + "address": 30, + "device": "tse2004av", + "name": "DIMM_H0", + "description": "DIMM H0", + "removable": true, + "sensors": [ + { + "name": "DIMM_H0", + "kind": "Temperature" + } + ] + }, + { + "address": 31, + "device": "tse2004av", + "name": "DIMM_H1", + "description": "DIMM H1", + "removable": true, + "sensors": [ + { + "name": "DIMM_H1", + "kind": "Temperature" + } + ] + } + ] + }, + { + "name": "m2", + "controller": 2, + "port_index": 0, + "port_name": "B", + "devices": [ + { + "address": 115, + "device": "pca9545", + "description": "M.2 mux", + "removable": false + } + ], + "muxes": [ + { + "address": 1, + "segments": [ + { + "segment": 1, + "devices": [ + { + "address": 106, + "device": "m2_hp_only", + "name": "M2_A", + "description": "M.2 A NVMe Basic Management Command", + "removable": true, + "sensors": [ + { + "name": "M2_A", + "kind": "Temperature" + } + ] + } + ] + }, + { + "segment": 2, + "devices": [ + { + "address": 106, + "device": "m2_hp_only", + "name": "M2_B", + "description": "M.2 B NVMe Basic Management Command", + "removable": true, + "sensors": [ + { + "name": "M2_B", + "kind": "Temperature" + } + ] + } + ] + }, + { + "segment": 3, + "devices": [ + { + "address": 80, + "device": "at24csw080", + "name": "fan_vpd", + "description": "Fan VPD", + "removable": true + } + ] + }, + { + "segment": 4, + "devices": [ + { + "address": 76, + "device": "tmp451", + "name": "t6", + "description": "T6 temperature sensor", + "removable": false, + "sensors": [ + { + "name": "t6", + "kind": "Temperature" + } + ] + } + ] + } + ] + } + ] + }, + { + "name": "front", + "controller": 2, + "port_index": 1, + "port_name": "F", + "devices": [ + { + "address": 72, + "device": "tmp117", + "name": "Southwest", + "description": "Southwest temperature sensor", + "removable": true, + "sensors": [ + { + "name": "Southwest", + "kind": "Temperature" + } + ] + }, + { + "address": 73, + "device": "tmp117", + "name": "South", + "description": "South temperature sensor", + "removable": true, + "sensors": [ + { + "name": "South", + "kind": "Temperature" + } + ] + }, + { + "address": 74, + "device": "tmp117", + "name": "Southeast", + "description": "Southeast temperature sensor", + "removable": true, + "sensors": [ + { + "name": "Southeast", + "kind": "Temperature" + } + ] + }, + { + "address": 112, + "device": "pca9545", + "description": "U.2 ABCD mux", + "removable": false + }, + { + "address": 113, + "device": "pca9545", + "description": "U.2 EFGH mux", + "removable": false + }, + { + "address": 114, + "device": "pca9545", + "description": "U.2 IJ/FRUID mux", + "removable": false + } + ], + "muxes": [ + { + "address": 1, + "segments": [ + { + "segment": 1, + "devices": [ + { + "address": 80, + "device": "at24csw080", + "name": "sharkfin_a_vpd", + "description": "U.2 Sharkfin A VPD", + "removable": true + }, + { + "address": 56, + "device": "max5970", + "name": "sharkfin_a_hsc", + "description": "U.2 Sharkfin A hot swap controller", + "removable": true, + "sensors": [ + { + "name": "V12_U2A_A0", + "kind": "Current" + }, + { + "name": "V3P3_U2A_A0", + "kind": "Current" + }, + { + "name": "V12_U2A_A0", + "kind": "Voltage" + }, + { + "name": "V3P3_U2A_A0", + "kind": "Voltage" + } + ] + }, + { + "address": 106, + "device": "nvme_bmc", + "name": "U2_N0", + "description": "U.2 A NVMe Basic Management Command", + "removable": true, + "sensors": [ + { + "name": "U2_N0", + "kind": "Temperature" + } + ] + } + ] + }, + { + "segment": 2, + "devices": [ + { + "address": 80, + "device": "at24csw080", + "name": "sharkfin_b_vpd", + "description": "U.2 Sharkfin B VPD", + "removable": true + }, + { + "address": 56, + "device": "max5970", + "name": "sharkfin_b_hsc", + "description": "U.2 Sharkfin B hot swap controller", + "removable": true, + "sensors": [ + { + "name": "V12_U2B_A0", + "kind": "Current" + }, + { + "name": "V3P3_U2B_A0", + "kind": "Current" + }, + { + "name": "V12_U2B_A0", + "kind": "Voltage" + }, + { + "name": "V3P3_U2B_A0", + "kind": "Voltage" + } + ] + }, + { + "address": 106, + "device": "nvme_bmc", + "name": "U2_N1", + "description": "U.2 B NVMe Basic Management Control", + "removable": true, + "sensors": [ + { + "name": "U2_N1", + "kind": "Temperature" + } + ] + } + ] + }, + { + "segment": 3, + "devices": [ + { + "address": 80, + "device": "at24csw080", + "name": "sharkfin_c_vpd", + "description": "U.2 Sharkfin C VPD", + "removable": true + }, + { + "address": 56, + "device": "max5970", + "name": "sharkfin_c_hsc", + "description": "U.2 Sharkfin C hot swap controller", + "removable": true, + "sensors": [ + { + "name": "V12_U2C_A0", + "kind": "Current" + }, + { + "name": "V3P3_U2C_A0", + "kind": "Current" + }, + { + "name": "V12_U2C_A0", + "kind": "Voltage" + }, + { + "name": "V3P3_U2C_A0", + "kind": "Voltage" + } + ] + }, + { + "address": 106, + "device": "nvme_bmc", + "name": "U2_N2", + "description": "U.2 C NVMe Basic Management Control", + "removable": true, + "sensors": [ + { + "name": "U2_N2", + "kind": "Temperature" + } + ] + } + ] + }, + { + "segment": 4, + "devices": [ + { + "address": 80, + "device": "at24csw080", + "name": "sharkfin_d_vpd", + "description": "U.2 Sharkfin D VPD", + "removable": true + }, + { + "address": 56, + "device": "max5970", + "name": "sharkfin_d_hsc", + "description": "U.2 Sharkfin D hot swap controller", + "removable": true, + "sensors": [ + { + "name": "V12_U2D_A0", + "kind": "Current" + }, + { + "name": "V3P3_U2D_A0", + "kind": "Current" + }, + { + "name": "V12_U2D_A0", + "kind": "Voltage" + }, + { + "name": "V3P3_U2D_A0", + "kind": "Voltage" + } + ] + }, + { + "address": 106, + "device": "nvme_bmc", + "name": "U2_N3", + "description": "U.2 D NVMe Basic Management Control", + "removable": true, + "sensors": [ + { + "name": "U2_N3", + "kind": "Temperature" + } + ] + } + ] + } + ] + }, + { + "address": 2, + "segments": [ + { + "segment": 1, + "devices": [ + { + "address": 80, + "device": "at24csw080", + "name": "sharkfin_e_vpd", + "description": "U.2 Sharkfin E VPD", + "removable": true + }, + { + "address": 56, + "device": "max5970", + "name": "sharkfin_e_hsc", + "description": "U.2 Sharkfin E hot swap controller", + "removable": true, + "sensors": [ + { + "name": "V12_U2E_A0", + "kind": "Current" + }, + { + "name": "V3P3_U2E_A0", + "kind": "Current" + }, + { + "name": "V12_U2E_A0", + "kind": "Voltage" + }, + { + "name": "V3P3_U2E_A0", + "kind": "Voltage" + } + ] + }, + { + "address": 106, + "device": "nvme_bmc", + "name": "U2_N4", + "description": "U.2 E NVMe Basic Management Control", + "removable": true, + "sensors": [ + { + "name": "U2_N4", + "kind": "Temperature" + } + ] + } + ] + }, + { + "segment": 2, + "devices": [ + { + "address": 80, + "device": "at24csw080", + "name": "sharkfin_f_vpd", + "description": "U.2 Sharkfin F VPD", + "removable": true + }, + { + "address": 56, + "device": "max5970", + "name": "sharkfin_f_hsc", + "description": "U.2 Sharkfin F hot swap controller", + "removable": true, + "sensors": [ + { + "name": "V12_U2F_A0", + "kind": "Current" + }, + { + "name": "V3P3_U2F_A0", + "kind": "Current" + }, + { + "name": "V12_U2F_A0", + "kind": "Voltage" + }, + { + "name": "V3P3_U2F_A0", + "kind": "Voltage" + } + ] + }, + { + "address": 106, + "device": "nvme_bmc", + "name": "U2_N5", + "description": "U.2 F NVMe Basic Management Control", + "removable": true, + "sensors": [ + { + "name": "U2_N5", + "kind": "Temperature" + } + ] + } + ] + }, + { + "segment": 3, + "devices": [ + { + "address": 80, + "device": "at24csw080", + "name": "sharkfin_g_vpd", + "description": "U.2 Sharkfin G VPD", + "removable": true + }, + { + "address": 56, + "device": "max5970", + "name": "sharkfin_g_hsc", + "description": "U.2 Sharkfin G hot swap controller", + "removable": true, + "sensors": [ + { + "name": "V12_U2G_A0", + "kind": "Current" + }, + { + "name": "V3P3_U2G_A0", + "kind": "Current" + }, + { + "name": "V12_U2G_A0", + "kind": "Voltage" + }, + { + "name": "V3P3_U2G_A0", + "kind": "Voltage" + } + ] + }, + { + "address": 106, + "device": "nvme_bmc", + "name": "U2_N6", + "description": "U.2 G NVMe Basic Management Control", + "removable": true, + "sensors": [ + { + "name": "U2_N6", + "kind": "Temperature" + } + ] + } + ] + }, + { + "segment": 4, + "devices": [ + { + "address": 80, + "device": "at24csw080", + "name": "sharkfin_h_vpd", + "description": "U.2 Sharkfin H VPD", + "removable": true + }, + { + "address": 56, + "device": "max5970", + "name": "sharkfin_h_hsc", + "description": "U.2 Sharkfin H hot swap controller", + "removable": true, + "sensors": [ + { + "name": "V12_U2H_A0", + "kind": "Current" + }, + { + "name": "V3P3_U2H_A0", + "kind": "Current" + }, + { + "name": "V12_U2H_A0", + "kind": "Voltage" + }, + { + "name": "V3P3_U2H_A0", + "kind": "Voltage" + } + ] + }, + { + "address": 106, + "device": "nvme_bmc", + "name": "U2_N7", + "description": "U.2 H NVMe Basic Management Control", + "removable": true, + "sensors": [ + { + "name": "U2_N7", + "kind": "Temperature" + } + ] + } + ] + } + ] + }, + { + "address": 3, + "segments": [ + { + "segment": 1, + "devices": [ + { + "address": 80, + "device": "at24csw080", + "name": "sharkfin_i_vpd", + "description": "U.2 Sharkfin I VPD", + "removable": true + }, + { + "address": 56, + "device": "max5970", + "name": "sharkfin_i_hsc", + "description": "U.2 Sharkfin I hot swap controller", + "removable": true, + "sensors": [ + { + "name": "V12_U2I_A0", + "kind": "Current" + }, + { + "name": "V3P3_U2I_A0", + "kind": "Current" + }, + { + "name": "V12_U2I_A0", + "kind": "Voltage" + }, + { + "name": "V3P3_U2I_A0", + "kind": "Voltage" + } + ] + }, + { + "address": 106, + "device": "nvme_bmc", + "name": "U2_N8", + "description": "U.2 I NVMe Basic Management Control", + "removable": true, + "sensors": [ + { + "name": "U2_N8", + "kind": "Temperature" + } + ] + } + ] + }, + { + "segment": 2, + "devices": [ + { + "address": 80, + "device": "at24csw080", + "name": "sharkfin_j_vpd", + "description": "U.2 Sharkfin J VPD", + "removable": true + }, + { + "address": 56, + "device": "max5970", + "name": "sharkfin_j_hsc", + "description": "U.2 Sharkfin J hot swap controller", + "removable": true, + "sensors": [ + { + "name": "V12_U2J_A0", + "kind": "Current" + }, + { + "name": "V3P3_U2J_A0", + "kind": "Current" + }, + { + "name": "V12_U2J_A0", + "kind": "Voltage" + }, + { + "name": "V3P3_U2J_A0", + "kind": "Voltage" + } + ] + }, + { + "address": 106, + "device": "nvme_bmc", + "name": "U2_N9", + "description": "U.2 J NVMe Basic Management Control", + "removable": true, + "sensors": [ + { + "name": "U2_N9", + "kind": "Temperature" + } + ] + } + ] + }, + { + "segment": 4, + "devices": [ + { + "address": 80, + "device": "at24csw080", + "name": "local_vpd", + "description": "Gimlet VPD", + "removable": false + } + ] + } + ] + } + ] + } + ], + "functions": [ + { + "name": "Sleep", + "id": 0, + "arg_count": 1, + "args": [ + { + "name": "arg0", + "ty": "u16", + "size": 2 + } + ] + }, + { + "name": "Send", + "id": 1, + "arg_count": 4, + "args": [ + { + "name": "__0", + "ty": "Task", + "size": 1 + }, + { + "name": "__1", + "ty": "u16", + "size": 2 + }, + { + "name": "__2", + "ty": "Buffer", + "size": 1 + }, + { + "name": "__3", + "ty": "u32", + "size": 4 + } + ] + }, + { + "name": "SendLeaseRead", + "id": 2, + "arg_count": 5, + "args": [ + { + "name": "__0", + "ty": "Task", + "size": 1 + }, + { + "name": "__1", + "ty": "u16", + "size": 2 + }, + { + "name": "__2", + "ty": "Buffer", + "size": 1 + }, + { + "name": "__3", + "ty": "u32", + "size": 4 + }, + { + "name": "__4", + "ty": "u32", + "size": 4 + } + ] + }, + { + "name": "SendLeaseReadWrite", + "id": 3, + "arg_count": 6, + "args": [ + { + "name": "__0", + "ty": "Task", + "size": 1 + }, + { + "name": "__1", + "ty": "u16", + "size": 2 + }, + { + "name": "__2", + "ty": "Buffer", + "size": 1 + }, + { + "name": "__3", + "ty": "u32", + "size": 4 + }, + { + "name": "__4", + "ty": "u32", + "size": 4 + }, + { + "name": "__5", + "ty": "u32", + "size": 4 + } + ] + }, + { + "name": "SendLeaseWrite", + "id": 4, + "arg_count": 5, + "args": [ + { + "name": "__0", + "ty": "Task", + "size": 1 + }, + { + "name": "__1", + "ty": "u16", + "size": 2 + }, + { + "name": "__2", + "ty": "Buffer", + "size": 1 + }, + { + "name": "__3", + "ty": "u32", + "size": 4 + }, + { + "name": "__4", + "ty": "u32", + "size": 4 + } + ] + }, + { + "name": "I2cRead", + "id": 5, + "arg_count": 7, + "args": [ + { + "name": "__0", + "ty": "Controller", + "size": 1 + }, + { + "name": "__1", + "ty": "PortIndex", + "size": 1 + }, + { + "name": "__2", + "ty": "Mux", + "size": 1 + }, + { + "name": "__3", + "ty": "Segment", + "size": 1 + }, + { + "name": "__4", + "ty": "u8", + "size": 1 + }, + { + "name": "__5", + "ty": "u8", + "size": 1 + }, + { + "name": "__6", + "ty": "u32", + "size": 4 + } + ], + "errors": [ + { + "code": 1, + "name": "BadResponse" + }, + { + "code": 2, + "name": "BadArg" + }, + { + "code": 3, + "name": "NoDevice" + }, + { + "code": 4, + "name": "BadController" + }, + { + "code": 5, + "name": "ReservedAddress" + }, + { + "code": 6, + "name": "BadPort" + }, + { + "code": 7, + "name": "NoRegister" + }, + { + "code": 8, + "name": "BadMux" + }, + { + "code": 9, + "name": "BadSegment" + }, + { + "code": 10, + "name": "MuxNotFound" + }, + { + "code": 11, + "name": "SegmentNotFound" + }, + { + "code": 12, + "name": "SegmentDisconnected" + }, + { + "code": 13, + "name": "MuxDisconnected" + }, + { + "code": 14, + "name": "MuxMissing" + }, + { + "code": 15, + "name": "BadMuxRegister" + }, + { + "code": 16, + "name": "BusReset" + }, + { + "code": 17, + "name": "BusResetMux" + }, + { + "code": 18, + "name": "BusLocked" + }, + { + "code": 19, + "name": "BusLockedMux" + }, + { + "code": 20, + "name": "ControllerBusy" + }, + { + "code": 21, + "name": "BusError" + }, + { + "code": 22, + "name": "BadDeviceState" + }, + { + "code": 23, + "name": "OperationNotSupported" + }, + { + "code": 24, + "name": "IllegalLeaseCount" + }, + { + "code": 25, + "name": "TooMuchData" + } + ] + }, + { + "name": "I2cWrite", + "id": 6, + "arg_count": 8, + "args": [ + { + "name": "__0", + "ty": "Controller", + "size": 1 + }, + { + "name": "__1", + "ty": "PortIndex", + "size": 1 + }, + { + "name": "__2", + "ty": "Mux", + "size": 1 + }, + { + "name": "__3", + "ty": "Segment", + "size": 1 + }, + { + "name": "__4", + "ty": "u8", + "size": 1 + }, + { + "name": "__5", + "ty": "u8", + "size": 1 + }, + { + "name": "__6", + "ty": "Buffer", + "size": 1 + }, + { + "name": "__7", + "ty": "u32", + "size": 4 + } + ], + "errors": [ + { + "code": 1, + "name": "BadResponse" + }, + { + "code": 2, + "name": "BadArg" + }, + { + "code": 3, + "name": "NoDevice" + }, + { + "code": 4, + "name": "BadController" + }, + { + "code": 5, + "name": "ReservedAddress" + }, + { + "code": 6, + "name": "BadPort" + }, + { + "code": 7, + "name": "NoRegister" + }, + { + "code": 8, + "name": "BadMux" + }, + { + "code": 9, + "name": "BadSegment" + }, + { + "code": 10, + "name": "MuxNotFound" + }, + { + "code": 11, + "name": "SegmentNotFound" + }, + { + "code": 12, + "name": "SegmentDisconnected" + }, + { + "code": 13, + "name": "MuxDisconnected" + }, + { + "code": 14, + "name": "MuxMissing" + }, + { + "code": 15, + "name": "BadMuxRegister" + }, + { + "code": 16, + "name": "BusReset" + }, + { + "code": 17, + "name": "BusResetMux" + }, + { + "code": 18, + "name": "BusLocked" + }, + { + "code": 19, + "name": "BusLockedMux" + }, + { + "code": 20, + "name": "ControllerBusy" + }, + { + "code": 21, + "name": "BusError" + }, + { + "code": 22, + "name": "BadDeviceState" + }, + { + "code": 23, + "name": "OperationNotSupported" + }, + { + "code": 24, + "name": "IllegalLeaseCount" + }, + { + "code": 25, + "name": "TooMuchData" + } + ] + }, + { + "name": "I2cBulkWrite", + "id": 7, + "arg_count": 8, + "args": [ + { + "name": "__0", + "ty": "Controller", + "size": 1 + }, + { + "name": "__1", + "ty": "PortIndex", + "size": 1 + }, + { + "name": "__2", + "ty": "Mux", + "size": 1 + }, + { + "name": "__3", + "ty": "Segment", + "size": 1 + }, + { + "name": "__4", + "ty": "u8", + "size": 1 + }, + { + "name": "__5", + "ty": "u8", + "size": 1 + }, + { + "name": "__6", + "ty": "u32", + "size": 4 + }, + { + "name": "__7", + "ty": "u32", + "size": 4 + } + ], + "errors": [ + { + "code": 1, + "name": "BadResponse" + }, + { + "code": 2, + "name": "BadArg" + }, + { + "code": 3, + "name": "NoDevice" + }, + { + "code": 4, + "name": "BadController" + }, + { + "code": 5, + "name": "ReservedAddress" + }, + { + "code": 6, + "name": "BadPort" + }, + { + "code": 7, + "name": "NoRegister" + }, + { + "code": 8, + "name": "BadMux" + }, + { + "code": 9, + "name": "BadSegment" + }, + { + "code": 10, + "name": "MuxNotFound" + }, + { + "code": 11, + "name": "SegmentNotFound" + }, + { + "code": 12, + "name": "SegmentDisconnected" + }, + { + "code": 13, + "name": "MuxDisconnected" + }, + { + "code": 14, + "name": "MuxMissing" + }, + { + "code": 15, + "name": "BadMuxRegister" + }, + { + "code": 16, + "name": "BusReset" + }, + { + "code": 17, + "name": "BusResetMux" + }, + { + "code": 18, + "name": "BusLocked" + }, + { + "code": 19, + "name": "BusLockedMux" + }, + { + "code": 20, + "name": "ControllerBusy" + }, + { + "code": 21, + "name": "BusError" + }, + { + "code": 22, + "name": "BadDeviceState" + }, + { + "code": 23, + "name": "OperationNotSupported" + }, + { + "code": 24, + "name": "IllegalLeaseCount" + }, + { + "code": 25, + "name": "TooMuchData" + } + ] + }, + { + "name": "GpioInput", + "id": 8, + "arg_count": 1, + "args": [ + { + "name": "arg0", + "ty": "Port", + "size": 1 + } + ] + }, + { + "name": "GpioToggle", + "id": 9, + "arg_count": 2, + "args": [ + { + "name": "__0", + "ty": "Port", + "size": 1 + }, + { + "name": "__1", + "ty": "u8", + "size": 1 + } + ] + }, + { + "name": "GpioSet", + "id": 10, + "arg_count": 2, + "args": [ + { + "name": "__0", + "ty": "Port", + "size": 1 + }, + { + "name": "__1", + "ty": "u8", + "size": 1 + } + ] + }, + { + "name": "GpioReset", + "id": 11, + "arg_count": 2, + "args": [ + { + "name": "__0", + "ty": "Port", + "size": 1 + }, + { + "name": "__1", + "ty": "u8", + "size": 1 + } + ] + }, + { + "name": "GpioConfigure", + "id": 12, + "arg_count": 7, + "args": [ + { + "name": "__0", + "ty": "Port", + "size": 1 + }, + { + "name": "__1", + "ty": "u8", + "size": 1 + }, + { + "name": "__2", + "ty": "Mode", + "size": 1 + }, + { + "name": "__3", + "ty": "OutputType", + "size": 1 + }, + { + "name": "__4", + "ty": "Speed", + "size": 1 + }, + { + "name": "__5", + "ty": "Pull", + "size": 1 + }, + { + "name": "__6", + "ty": "Alternate", + "size": 1 + } + ] + }, + { + "name": "SpiRead", + "id": 13, + "arg_count": 4, + "args": [ + { + "name": "__0", + "ty": "Task", + "size": 1 + }, + { + "name": "__1", + "ty": "u8", + "size": 1 + }, + { + "name": "__2", + "ty": "u32", + "size": 4 + }, + { + "name": "__3", + "ty": "u32", + "size": 4 + } + ], + "errors": [ + { + "code": 1, + "name": "BadTransferSize" + }, + { + "code": 4, + "name": "TaskRestarted" + } + ] + }, + { + "name": "SpiWrite", + "id": 14, + "arg_count": 3, + "args": [ + { + "name": "__0", + "ty": "Task", + "size": 1 + }, + { + "name": "__1", + "ty": "u8", + "size": 1 + }, + { + "name": "__2", + "ty": "u32", + "size": 4 + } + ], + "errors": [ + { + "code": 1, + "name": "BadTransferSize" + }, + { + "code": 4, + "name": "TaskRestarted" + } + ] + }, + { + "name": "QspiReadId", + "id": 15, + "arg_count": 0, + "errors": [ + { + "code": 1, + "name": "WriteEnableFailed" + }, + { + "code": 2, + "name": "HashBadRange" + }, + { + "code": 3, + "name": "HashError" + }, + { + "code": 4, + "name": "HashNotConfigured" + }, + { + "code": 5, + "name": "NotMuxedToSP" + }, + { + "code": 6, + "name": "Sector0IsReserved" + }, + { + "code": 7, + "name": "NoPersistentData" + }, + { + "code": 8, + "name": "MonotonicCounterOverflow" + }, + { + "code": 9, + "name": "BadChipId" + }, + { + "code": 10, + "name": "BadAddress" + }, + { + "code": 11, + "name": "QspiTimeout" + }, + { + "code": 12, + "name": "QspiTransferError" + }, + { + "code": 13, + "name": "HashUncalculated" + }, + { + "code": 14, + "name": "RecalculateHash" + }, + { + "code": 15, + "name": "HashInProgress" + }, + { + "code": 16, + "name": "BadCapacity" + }, + { + "code": 17, + "name": "ServerRestarted" + } + ] + }, + { + "name": "QspiReadStatus", + "id": 16, + "arg_count": 0, + "errors": [ + { + "code": 1, + "name": "WriteEnableFailed" + }, + { + "code": 2, + "name": "HashBadRange" + }, + { + "code": 3, + "name": "HashError" + }, + { + "code": 4, + "name": "HashNotConfigured" + }, + { + "code": 5, + "name": "NotMuxedToSP" + }, + { + "code": 6, + "name": "Sector0IsReserved" + }, + { + "code": 7, + "name": "NoPersistentData" + }, + { + "code": 8, + "name": "MonotonicCounterOverflow" + }, + { + "code": 9, + "name": "BadChipId" + }, + { + "code": 10, + "name": "BadAddress" + }, + { + "code": 11, + "name": "QspiTimeout" + }, + { + "code": 12, + "name": "QspiTransferError" + }, + { + "code": 13, + "name": "HashUncalculated" + }, + { + "code": 14, + "name": "RecalculateHash" + }, + { + "code": 15, + "name": "HashInProgress" + }, + { + "code": 16, + "name": "BadCapacity" + }, + { + "code": 17, + "name": "ServerRestarted" + } + ] + }, + { + "name": "QspiBulkErase", + "id": 17, + "arg_count": 0, + "errors": [ + { + "code": 1, + "name": "WriteEnableFailed" + }, + { + "code": 2, + "name": "HashBadRange" + }, + { + "code": 3, + "name": "HashError" + }, + { + "code": 4, + "name": "HashNotConfigured" + }, + { + "code": 5, + "name": "NotMuxedToSP" + }, + { + "code": 6, + "name": "Sector0IsReserved" + }, + { + "code": 7, + "name": "NoPersistentData" + }, + { + "code": 8, + "name": "MonotonicCounterOverflow" + }, + { + "code": 9, + "name": "BadChipId" + }, + { + "code": 10, + "name": "BadAddress" + }, + { + "code": 11, + "name": "QspiTimeout" + }, + { + "code": 12, + "name": "QspiTransferError" + }, + { + "code": 13, + "name": "HashUncalculated" + }, + { + "code": 14, + "name": "RecalculateHash" + }, + { + "code": 15, + "name": "HashInProgress" + }, + { + "code": 16, + "name": "BadCapacity" + }, + { + "code": 17, + "name": "ServerRestarted" + } + ] + }, + { + "name": "QspiPageProgram", + "id": 18, + "arg_count": 3, + "args": [ + { + "name": "__0", + "ty": "u32", + "size": 4 + }, + { + "name": "__1", + "ty": "u32", + "size": 4 + }, + { + "name": "__2", + "ty": "u32", + "size": 4 + } + ], + "errors": [ + { + "code": 1, + "name": "WriteEnableFailed" + }, + { + "code": 2, + "name": "HashBadRange" + }, + { + "code": 3, + "name": "HashError" + }, + { + "code": 4, + "name": "HashNotConfigured" + }, + { + "code": 5, + "name": "NotMuxedToSP" + }, + { + "code": 6, + "name": "Sector0IsReserved" + }, + { + "code": 7, + "name": "NoPersistentData" + }, + { + "code": 8, + "name": "MonotonicCounterOverflow" + }, + { + "code": 9, + "name": "BadChipId" + }, + { + "code": 10, + "name": "BadAddress" + }, + { + "code": 11, + "name": "QspiTimeout" + }, + { + "code": 12, + "name": "QspiTransferError" + }, + { + "code": 13, + "name": "HashUncalculated" + }, + { + "code": 14, + "name": "RecalculateHash" + }, + { + "code": 15, + "name": "HashInProgress" + }, + { + "code": 16, + "name": "BadCapacity" + }, + { + "code": 17, + "name": "ServerRestarted" + } + ] + }, + { + "name": "QspiPageProgramSector0", + "id": 19, + "arg_count": 3, + "args": [ + { + "name": "__0", + "ty": "u32", + "size": 4 + }, + { + "name": "__1", + "ty": "u32", + "size": 4 + }, + { + "name": "__2", + "ty": "u32", + "size": 4 + } + ], + "errors": [ + { + "code": 1, + "name": "WriteEnableFailed" + }, + { + "code": 2, + "name": "HashBadRange" + }, + { + "code": 3, + "name": "HashError" + }, + { + "code": 4, + "name": "HashNotConfigured" + }, + { + "code": 5, + "name": "NotMuxedToSP" + }, + { + "code": 6, + "name": "Sector0IsReserved" + }, + { + "code": 7, + "name": "NoPersistentData" + }, + { + "code": 8, + "name": "MonotonicCounterOverflow" + }, + { + "code": 9, + "name": "BadChipId" + }, + { + "code": 10, + "name": "BadAddress" + }, + { + "code": 11, + "name": "QspiTimeout" + }, + { + "code": 12, + "name": "QspiTransferError" + }, + { + "code": 13, + "name": "HashUncalculated" + }, + { + "code": 14, + "name": "RecalculateHash" + }, + { + "code": 15, + "name": "HashInProgress" + }, + { + "code": 16, + "name": "BadCapacity" + }, + { + "code": 17, + "name": "ServerRestarted" + } + ] + }, + { + "name": "QspiRead", + "id": 20, + "arg_count": 2, + "args": [ + { + "name": "__0", + "ty": "u32", + "size": 4 + }, + { + "name": "__1", + "ty": "u32", + "size": 4 + } + ], + "errors": [ + { + "code": 1, + "name": "WriteEnableFailed" + }, + { + "code": 2, + "name": "HashBadRange" + }, + { + "code": 3, + "name": "HashError" + }, + { + "code": 4, + "name": "HashNotConfigured" + }, + { + "code": 5, + "name": "NotMuxedToSP" + }, + { + "code": 6, + "name": "Sector0IsReserved" + }, + { + "code": 7, + "name": "NoPersistentData" + }, + { + "code": 8, + "name": "MonotonicCounterOverflow" + }, + { + "code": 9, + "name": "BadChipId" + }, + { + "code": 10, + "name": "BadAddress" + }, + { + "code": 11, + "name": "QspiTimeout" + }, + { + "code": 12, + "name": "QspiTransferError" + }, + { + "code": 13, + "name": "HashUncalculated" + }, + { + "code": 14, + "name": "RecalculateHash" + }, + { + "code": 15, + "name": "HashInProgress" + }, + { + "code": 16, + "name": "BadCapacity" + }, + { + "code": 17, + "name": "ServerRestarted" + } + ] + }, + { + "name": "QspiSectorErase", + "id": 21, + "arg_count": 1, + "args": [ + { + "name": "arg0", + "ty": "u32", + "size": 4 + } + ], + "errors": [ + { + "code": 1, + "name": "WriteEnableFailed" + }, + { + "code": 2, + "name": "HashBadRange" + }, + { + "code": 3, + "name": "HashError" + }, + { + "code": 4, + "name": "HashNotConfigured" + }, + { + "code": 5, + "name": "NotMuxedToSP" + }, + { + "code": 6, + "name": "Sector0IsReserved" + }, + { + "code": 7, + "name": "NoPersistentData" + }, + { + "code": 8, + "name": "MonotonicCounterOverflow" + }, + { + "code": 9, + "name": "BadChipId" + }, + { + "code": 10, + "name": "BadAddress" + }, + { + "code": 11, + "name": "QspiTimeout" + }, + { + "code": 12, + "name": "QspiTransferError" + }, + { + "code": 13, + "name": "HashUncalculated" + }, + { + "code": 14, + "name": "RecalculateHash" + }, + { + "code": 15, + "name": "HashInProgress" + }, + { + "code": 16, + "name": "BadCapacity" + }, + { + "code": 17, + "name": "ServerRestarted" + } + ] + }, + { + "name": "QspiSector0Erase", + "id": 22, + "arg_count": 0, + "errors": [ + { + "code": 1, + "name": "WriteEnableFailed" + }, + { + "code": 2, + "name": "HashBadRange" + }, + { + "code": 3, + "name": "HashError" + }, + { + "code": 4, + "name": "HashNotConfigured" + }, + { + "code": 5, + "name": "NotMuxedToSP" + }, + { + "code": 6, + "name": "Sector0IsReserved" + }, + { + "code": 7, + "name": "NoPersistentData" + }, + { + "code": 8, + "name": "MonotonicCounterOverflow" + }, + { + "code": 9, + "name": "BadChipId" + }, + { + "code": 10, + "name": "BadAddress" + }, + { + "code": 11, + "name": "QspiTimeout" + }, + { + "code": 12, + "name": "QspiTransferError" + }, + { + "code": 13, + "name": "HashUncalculated" + }, + { + "code": 14, + "name": "RecalculateHash" + }, + { + "code": 15, + "name": "HashInProgress" + }, + { + "code": 16, + "name": "BadCapacity" + }, + { + "code": 17, + "name": "ServerRestarted" + } + ] + }, + { + "name": "QspiVerify", + "id": 23, + "arg_count": 3, + "args": [ + { + "name": "__0", + "ty": "u32", + "size": 4 + }, + { + "name": "__1", + "ty": "u32", + "size": 4 + }, + { + "name": "__2", + "ty": "u32", + "size": 4 + } + ], + "errors": [ + { + "code": 1, + "name": "WriteEnableFailed" + }, + { + "code": 2, + "name": "HashBadRange" + }, + { + "code": 3, + "name": "HashError" + }, + { + "code": 4, + "name": "HashNotConfigured" + }, + { + "code": 5, + "name": "NotMuxedToSP" + }, + { + "code": 6, + "name": "Sector0IsReserved" + }, + { + "code": 7, + "name": "NoPersistentData" + }, + { + "code": 8, + "name": "MonotonicCounterOverflow" + }, + { + "code": 9, + "name": "BadChipId" + }, + { + "code": 10, + "name": "BadAddress" + }, + { + "code": 11, + "name": "QspiTimeout" + }, + { + "code": 12, + "name": "QspiTransferError" + }, + { + "code": 13, + "name": "HashUncalculated" + }, + { + "code": 14, + "name": "RecalculateHash" + }, + { + "code": 15, + "name": "HashInProgress" + }, + { + "code": 16, + "name": "BadCapacity" + }, + { + "code": 17, + "name": "ServerRestarted" + } + ] + }, + { + "name": "QspiHash", + "id": 24, + "arg_count": 2, + "args": [ + { + "name": "__0", + "ty": "u32", + "size": 4 + }, + { + "name": "__1", + "ty": "u32", + "size": 4 + } + ], + "errors": [ + { + "code": 1, + "name": "WriteEnableFailed" + }, + { + "code": 2, + "name": "HashBadRange" + }, + { + "code": 3, + "name": "HashError" + }, + { + "code": 4, + "name": "HashNotConfigured" + }, + { + "code": 5, + "name": "NotMuxedToSP" + }, + { + "code": 6, + "name": "Sector0IsReserved" + }, + { + "code": 7, + "name": "NoPersistentData" + }, + { + "code": 8, + "name": "MonotonicCounterOverflow" + }, + { + "code": 9, + "name": "BadChipId" + }, + { + "code": 10, + "name": "BadAddress" + }, + { + "code": 11, + "name": "QspiTimeout" + }, + { + "code": 12, + "name": "QspiTransferError" + }, + { + "code": 13, + "name": "HashUncalculated" + }, + { + "code": 14, + "name": "RecalculateHash" + }, + { + "code": 15, + "name": "HashInProgress" + }, + { + "code": 16, + "name": "BadCapacity" + }, + { + "code": 17, + "name": "ServerRestarted" + } + ] + }, + { + "name": "HashDigest", + "id": 25, + "arg_count": 1, + "args": [ + { + "name": "arg0", + "ty": "u32", + "size": 4 + } + ], + "errors": [ + { + "code": 1, + "name": "NotInitialized" + }, + { + "code": 2, + "name": "InvalidState" + }, + { + "code": 3, + "name": "Busy" + }, + { + "code": 4, + "name": "NoData" + }, + { + "code": 5, + "name": "ServerRestarted" + } + ] + }, + { + "name": "HashInit", + "id": 26, + "arg_count": 0, + "errors": [ + { + "code": 1, + "name": "NotInitialized" + }, + { + "code": 2, + "name": "InvalidState" + }, + { + "code": 3, + "name": "Busy" + }, + { + "code": 4, + "name": "NoData" + }, + { + "code": 5, + "name": "ServerRestarted" + } + ] + }, + { + "name": "HashUpdate", + "id": 27, + "arg_count": 1, + "args": [ + { + "name": "arg0", + "ty": "u32", + "size": 4 + } + ], + "errors": [ + { + "code": 1, + "name": "NotInitialized" + }, + { + "code": 2, + "name": "InvalidState" + }, + { + "code": 3, + "name": "Busy" + }, + { + "code": 4, + "name": "NoData" + }, + { + "code": 5, + "name": "ServerRestarted" + } + ] + }, + { + "name": "HashFinalize", + "id": 28, + "arg_count": 0, + "errors": [ + { + "code": 1, + "name": "NotInitialized" + }, + { + "code": 2, + "name": "InvalidState" + }, + { + "code": 3, + "name": "Busy" + }, + { + "code": 4, + "name": "NoData" + }, + { + "code": 5, + "name": "ServerRestarted" + } + ] + } + ], + "buffer_sizes": { + "text": 4096, + "data": 20480, + "rstack": 2048, + "scratch": 1025 + }, + "idol_interfaces": [ + { + "name": "Jefe", + "task": "jefe", + "task_id": 0, + "ops": [ + { + "name": "get_state", + "code": 1, + "args_size": 0, + "reply": "u32", + "reply_size": 4, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "set_state", + "code": 2, + "args": [ + { + "name": "state", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "request_reset", + "code": 3, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "get_reset_reason", + "code": 4, + "args_size": 0, + "reply": "ResetReason", + "reply_size": 5, + "encoding": "Ssmarshal", + "idempotent": true + }, + { + "name": "set_reset_reason", + "code": 5, + "args": [ + { + "name": "reason", + "ty": "ResetReason" + } + ], + "args_size": 8, + "reply": "()", + "reply_size": 0, + "encoding": "Ssmarshal", + "idempotent": true + }, + { + "name": "reinitialize_dump_areas", + "code": 6, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "DumpAgentError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "get_dump_area", + "code": 7, + "args": [ + { + "name": "index", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "humpty::DumpArea", + "reply_size": 10, + "error": "DumpAgentError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "claim_dump_area", + "code": 8, + "args_size": 0, + "reply": "humpty::DumpArea", + "reply_size": 10, + "error": "DumpAgentError", + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "dump_task", + "code": 9, + "args": [ + { + "name": "task_index", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "u8", + "reply_size": 1, + "error": "DumpAgentError", + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "dump_task_region", + "code": 10, + "args": [ + { + "name": "task_index", + "ty": "u32" + }, + { + "name": "address", + "ty": "u32" + }, + { + "name": "length", + "ty": "u32" + } + ], + "args_size": 12, + "reply": "u8", + "reply_size": 1, + "error": "DumpAgentError", + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "reinitialize_dump_from", + "code": 11, + "args": [ + { + "name": "index", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "DumpAgentError", + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "restart_me_raw", + "code": 12, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "read_fault_counts", + "code": 13, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[usize; hubris_num_tasks::NUM_TASKS]", + "read": false, + "write": true + } + ], + "idempotent": true + } + ] + }, + { + "name": "Net", + "task": "net", + "task_id": 1, + "ops": [ + { + "name": "recv_packet", + "code": 1, + "args": [ + { + "name": "socket", + "ty": "SocketName" + }, + { + "name": "large_payload_behavior", + "ty": "LargePayloadBehavior" + } + ], + "args_size": 8, + "reply": "UdpMetadata", + "reply_size": 24, + "error": "task_net_api::RecvError", + "encoding": "Hubpack", + "leases": [ + { + "name": "payload", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": false + }, + { + "name": "send_packet", + "code": 2, + "args": [ + { + "name": "socket", + "ty": "SocketName" + }, + { + "name": "metadata", + "ty": "UdpMetadata" + } + ], + "args_size": 28, + "reply": "()", + "reply_size": 0, + "error": "task_net_api::SendError", + "encoding": "Hubpack", + "leases": [ + { + "name": "payload", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": false + }, + { + "name": "smi_read", + "code": 3, + "args": [ + { + "name": "phy", + "ty": "u8" + }, + { + "name": "register", + "ty": "u8" + } + ], + "args_size": 2, + "reply": "u16", + "reply_size": 2, + "error": "ServerDeath", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "smi_write", + "code": 4, + "args": [ + { + "name": "phy", + "ty": "u8" + }, + { + "name": "register", + "ty": "u8" + }, + { + "name": "value", + "ty": "u16" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "ServerDeath", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "read_phy_reg", + "code": 5, + "args": [ + { + "name": "port", + "ty": "u8" + }, + { + "name": "page", + "ty": "u16" + }, + { + "name": "reg", + "ty": "u8" + } + ], + "args_size": 4, + "reply": "u16", + "reply_size": 2, + "error": "PhyError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "write_phy_reg", + "code": 6, + "args": [ + { + "name": "port", + "ty": "u8" + }, + { + "name": "page", + "ty": "u16" + }, + { + "name": "reg", + "ty": "u8" + }, + { + "name": "value", + "ty": "u16" + } + ], + "args_size": 6, + "reply": "()", + "reply_size": 0, + "error": "PhyError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "read_ksz8463_mac_count", + "code": 7, + "args_size": 0, + "reply": "usize", + "reply_size": 4, + "error": "KszError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "read_ksz8463_mac", + "code": 8, + "args": [ + { + "name": "i", + "ty": "u16" + } + ], + "args_size": 2, + "reply": "KszMacTableEntry", + "reply_size": 8, + "error": "KszError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "read_ksz8463_reg", + "code": 9, + "args": [ + { + "name": "reg", + "ty": "u16" + } + ], + "args_size": 2, + "reply": "u16", + "reply_size": 2, + "error": "KszError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "get_mac_address", + "code": 10, + "args_size": 0, + "reply": "MacAddress", + "reply_size": 6, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "get_spare_mac_addresses", + "code": 11, + "args_size": 0, + "reply": "MacAddressBlock", + "reply_size": 9, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "management_link_status", + "code": 12, + "args_size": 0, + "reply": "ManagementLinkStatus", + "reply_size": 6, + "error": "MgmtError", + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "management_counters", + "code": 13, + "args_size": 0, + "reply": "ManagementCounters", + "reply_size": 105, + "error": "MgmtError", + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "trust_vlan", + "code": 14, + "args": [ + { + "name": "vid", + "ty": "VLanId" + }, + { + "name": "trust_until", + "ty": "u64" + } + ], + "args_size": 16, + "reply": "()", + "reply_size": 0, + "error": "task_net_api::TrustError", + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "distrust_vlan", + "code": 15, + "args": [ + { + "name": "vid", + "ty": "VLanId" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "task_net_api::TrustError", + "encoding": "Hubpack", + "idempotent": false + } + ] + }, + { + "name": "Sys", + "task": "sys", + "task_id": 2, + "ops": [ + { + "name": "enable_clock_raw", + "code": 1, + "args": [ + { + "name": "peripheral", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "RccError", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "disable_clock_raw", + "code": 2, + "args": [ + { + "name": "peripheral", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "RccError", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "enter_reset_raw", + "code": 3, + "args": [ + { + "name": "peripheral", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "RccError", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "leave_reset_raw", + "code": 4, + "args": [ + { + "name": "peripheral", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "RccError", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "gpio_configure_raw", + "code": 5, + "args": [ + { + "name": "port", + "ty": "Port" + }, + { + "name": "pins", + "ty": "u16" + }, + { + "name": "packed_attributes", + "ty": "u16" + } + ], + "args_size": 5, + "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "gpio_set_reset", + "code": 6, + "args": [ + { + "name": "port", + "ty": "Port" + }, + { + "name": "set_pins", + "ty": "u16" + }, + { + "name": "reset_pins", + "ty": "u16" + } + ], + "args_size": 5, + "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "gpio_read_input", + "code": 7, + "args": [ + { + "name": "port", + "ty": "Port" + } + ], + "args_size": 1, + "reply": "u16", + "reply_size": 2, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "gpio_toggle", + "code": 8, + "args": [ + { + "name": "port", + "ty": "Port" + }, + { + "name": "pins", + "ty": "u16" + } + ], + "args_size": 3, + "reply": "()", + "reply_size": 0, + "error": "ServerDeath", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "read_uid", + "code": 9, + "args_size": 0, + "reply": "[u32; 3]", + "reply_size": 0, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "gpio_irq_configure", + "code": 10, + "args": [ + { + "name": "mask", + "ty": "u32" + }, + { + "name": "sensitivity", + "ty": "Edge" + } + ], + "args_size": 5, + "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "gpio_irq_control", + "code": 11, + "args": [ + { + "name": "mask", + "ty": "u32" + }, + { + "name": "op", + "ty": "IrqControl" + } + ], + "args_size": 5, + "reply": "bool", + "reply_size": 1, + "error": "ServerDeath", + "encoding": "Zerocopy", + "idempotent": false + } + ] + }, + { + "name": "Spi", + "task": "spi2_driver", + "task_id": 3, + "ops": [ + { + "name": "read", + "code": 1, + "args": [ + { + "name": "device_index", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "drv_spi_api::SpiError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "sink", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": false + }, + { + "name": "write", + "code": 2, + "args": [ + { + "name": "device_index", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "drv_spi_api::SpiError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "source", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": false + }, + { + "name": "exchange", + "code": 3, + "args": [ + { + "name": "device_index", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "drv_spi_api::SpiError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "source", + "ty": "[u8]", + "read": true, + "write": false + }, + { + "name": "sink", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": false + }, + { + "name": "lock", + "code": 4, + "args": [ + { + "name": "device_index", + "ty": "u8" + }, + { + "name": "cs_state", + "ty": "CsState" + } + ], + "args_size": 2, + "reply": "()", + "reply_size": 0, + "error": "ServerDeath", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "release", + "code": 5, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "ServerDeath", + "encoding": "Zerocopy", + "idempotent": false + } + ] + }, + { + "name": "Packrat", + "task": "packrat", + "task_id": 6, + "ops": [ + { + "name": "get_mac_address_block", + "code": 1, + "args_size": 0, + "reply": "MacAddressBlock", + "reply_size": 9, + "error": "CacheGetError", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "set_mac_address_block", + "code": 2, + "args": [ + { + "name": "macs", + "ty": "MacAddressBlock" + } + ], + "args_size": 9, + "reply": "()", + "reply_size": 0, + "error": "CacheSetError", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "get_identity", + "code": 3, + "args_size": 0, + "reply": "OxideIdentity", + "reply_size": 26, + "error": "CacheGetError", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "set_identity", + "code": 4, + "args": [ + { + "name": "macs", + "ty": "OxideIdentity" + } + ], + "args_size": 26, + "reply": "()", + "reply_size": 0, + "error": "CacheSetError", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "get_next_boot_host_startup_options", + "code": 5, + "args_size": 0, + "reply": "HostStartupOptions", + "reply_size": 8, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "set_next_boot_host_startup_options", + "code": 6, + "args": [ + { + "name": "startup_options", + "ty": "HostStartupOptions" + } + ], + "args_size": 8, + "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "remove_spd", + "code": 7, + "args": [ + { + "name": "index", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "set_spd_eeprom", + "code": 8, + "args": [ + { + "name": "index", + "ty": "u8" + }, + { + "name": "offset", + "ty": "usize" + } + ], + "args_size": 5, + "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": true + }, + { + "name": "get_spd_present", + "code": 9, + "args": [ + { + "name": "index", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "bool", + "reply_size": 1, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "get_spd_data", + "code": 10, + "args": [ + { + "name": "index", + "ty": "u8" + }, + { + "name": "offset", + "ty": "usize" + } + ], + "args_size": 5, + "reply": "u8", + "reply_size": 1, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "get_full_spd_data", + "code": 11, + "args": [ + { + "name": "index", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": true + }, + { + "name": "set_ereport_restart_id", + "code": 12, + "args": [ + { + "name": "restart_id", + "ty": "u128" + } + ], + "args_size": 16, + "reply": "()", + "reply_size": 0, + "error": "CacheSetError", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "deliver_encoded_ereport", + "code": 13, + "args_size": 0, + "reply": "ereport_messages::Ena", + "reply_size": 8, + "error": "EreportWriteError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": true + }, + { + "name": "read_ereports", + "code": 14, + "args": [ + { + "name": "request_id", + "ty": "ereport_messages::RequestIdV0" + }, + { + "name": "restart_id", + "ty": "ereport_messages::RestartId" + }, + { + "name": "start_ena", + "ty": "ereport_messages::Ena" + }, + { + "name": "limit", + "ty": "u8" + }, + { + "name": "committed_ena", + "ty": "ereport_messages::Ena" + } + ], + "args_size": 34, + "reply": "usize", + "reply_size": 4, + "error": "EreportReadError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": true + } + ] + }, + { + "name": "Thermal", + "task": "thermal", + "task_id": 7, + "ops": [ + { + "name": "set_mode_manual", + "code": 1, + "args": [ + { + "name": "initial_pwm", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "ThermalError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "set_mode_auto", + "code": 2, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "ThermalError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "get_mode", + "code": 3, + "args_size": 0, + "reply": "ThermalMode", + "reply_size": 1, + "error": "ThermalError", + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "get_auto_state", + "code": 4, + "args_size": 0, + "reply": "ThermalAutoState", + "reply_size": 1, + "error": "ThermalError", + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "disable_watchdog", + "code": 5, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "ThermalError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "enable_watchdog", + "code": 6, + "args": [ + { + "name": "timeout_s", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "ThermalError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "set_pid", + "code": 7, + "args": [ + { + "name": "z", + "ty": "f32" + }, + { + "name": "p", + "ty": "f32" + }, + { + "name": "i", + "ty": "f32" + }, + { + "name": "d", + "ty": "f32" + } + ], + "args_size": 16, + "reply": "()", + "reply_size": 0, + "error": "ThermalError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "get_margin", + "code": 8, + "args_size": 0, + "reply": "f32", + "reply_size": 4, + "error": "ThermalError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "set_margin", + "code": 9, + "args": [ + { + "name": "margin", + "ty": "f32" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "ThermalError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "update_dynamic_input", + "code": 10, + "args": [ + { + "name": "index", + "ty": "usize" + }, + { + "name": "model", + "ty": "ThermalProperties" + } + ], + "args_size": 20, + "reply": "()", + "reply_size": 0, + "error": "ThermalError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "remove_dynamic_input", + "code": 11, + "args": [ + { + "name": "index", + "ty": "usize" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "ThermalError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "get_runtime", + "code": 12, + "args_size": 0, + "reply": "u64", + "reply_size": 8, + "error": "ThermalError", + "encoding": "Zerocopy", + "idempotent": false + } + ] + }, + { + "name": "Power", + "task": "power", + "task_id": 8, + "ops": [ + { + "name": "pmbus_read", + "code": 1, + "args": [ + { + "name": "dev", + "ty": "Device" + }, + { + "name": "rail", + "ty": "u8" + }, + { + "name": "index", + "ty": "u32" + }, + { + "name": "op", + "ty": "Operation" + } + ], + "args_size": 8, + "reply": "PmbusValue", + "reply_size": 34, + "error": "ResponseCode", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "read_mode", + "code": 2, + "args": [ + { + "name": "dev", + "ty": "Device" + }, + { + "name": "rail", + "ty": "u8" + }, + { + "name": "index", + "ty": "u32" + } + ], + "args_size": 8, + "reply": "u8", + "reply_size": 1, + "error": "ResponseCode", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "phase_current", + "code": 3, + "args": [ + { + "name": "rail", + "ty": "SensorId" + }, + { + "name": "phase", + "ty": "u8" + } + ], + "args_size": 8, + "reply": "f32", + "reply_size": 4, + "error": "ResponseCode", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "bmr491_event_log_read", + "code": 4, + "args": [ + { + "name": "index", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "Bmr491Event", + "reply_size": 24, + "error": "ResponseCode", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "bmr491_fault_log_clear", + "code": 5, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "ResponseCode", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "bmr491_max_fault_event_index", + "code": 6, + "args_size": 0, + "reply": "u8", + "reply_size": 1, + "error": "ResponseCode", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "bmr491_max_lifecycle_event_index", + "code": 7, + "args_size": 0, + "reply": "u8", + "reply_size": 1, + "error": "ResponseCode", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "rendmp_blackbox_dump", + "code": 8, + "args": [ + { + "name": "addr", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "RenesasBlackbox", + "reply_size": 177, + "error": "ResponseCode", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "rendmp_dma_read", + "code": 9, + "args": [ + { + "name": "addr", + "ty": "u8" + }, + { + "name": "reg", + "ty": "u16" + } + ], + "args_size": 3, + "reply": "u32", + "reply_size": 4, + "error": "ResponseCode", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "rendmp_dma_write", + "code": 10, + "args": [ + { + "name": "addr", + "ty": "u8" + }, + { + "name": "reg", + "ty": "u16" + }, + { + "name": "data", + "ty": "u32" + } + ], + "args_size": 7, + "reply": "()", + "reply_size": 0, + "error": "ResponseCode", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "raw_pmbus_read_byte", + "code": 11, + "args": [ + { + "name": "index", + "ty": "u32" + }, + { + "name": "has_rail", + "ty": "bool" + }, + { + "name": "op", + "ty": "u8" + } + ], + "args_size": 8, + "reply": "u8", + "reply_size": 1, + "error": "ResponseCode", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "raw_pmbus_read_word", + "code": 12, + "args": [ + { + "name": "index", + "ty": "u32" + }, + { + "name": "has_rail", + "ty": "bool" + }, + { + "name": "op", + "ty": "u8" + } + ], + "args_size": 8, + "reply": "u16", + "reply_size": 2, + "error": "ResponseCode", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "raw_pmbus_read_word32", + "code": 13, + "args": [ + { + "name": "index", + "ty": "u32" + }, + { + "name": "has_rail", + "ty": "bool" + }, + { + "name": "op", + "ty": "u8" + } + ], + "args_size": 8, + "reply": "u32", + "reply_size": 4, + "error": "ResponseCode", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "raw_pmbus_read_block", + "code": 14, + "args": [ + { + "name": "index", + "ty": "u32" + }, + { + "name": "has_rail", + "ty": "bool" + }, + { + "name": "op", + "ty": "u8" + } + ], + "args_size": 8, + "reply": "RawPmbusBlock", + "reply_size": 33, + "error": "ResponseCode", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "raw_pmbus_set", + "code": 15, + "args": [ + { + "name": "index", + "ty": "u32" + }, + { + "name": "has_rail", + "ty": "bool" + }, + { + "name": "op", + "ty": "u8" + } + ], + "args_size": 8, + "reply": "()", + "reply_size": 0, + "error": "ResponseCode", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "raw_pmbus_write_byte", + "code": 16, + "args": [ + { + "name": "index", + "ty": "u32" + }, + { + "name": "has_rail", + "ty": "bool" + }, + { + "name": "op", + "ty": "u8" + }, + { + "name": "data", + "ty": "u8" + } + ], + "args_size": 8, + "reply": "()", + "reply_size": 0, + "error": "ResponseCode", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "raw_pmbus_write_word", + "code": 17, + "args": [ + { + "name": "index", + "ty": "u32" + }, + { + "name": "has_rail", + "ty": "bool" + }, + { + "name": "op", + "ty": "u8" + }, + { + "name": "data", + "ty": "u16" + } + ], + "args_size": 8, + "reply": "()", + "reply_size": 0, + "error": "ResponseCode", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "raw_pmbus_write_word32", + "code": 18, + "args": [ + { + "name": "index", + "ty": "u32" + }, + { + "name": "has_rail", + "ty": "bool" + }, + { + "name": "op", + "ty": "u8" + }, + { + "name": "data", + "ty": "u32" + } + ], + "args_size": 12, + "reply": "()", + "reply_size": 0, + "error": "ResponseCode", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "raw_pmbus_write_block", + "code": 19, + "args": [ + { + "name": "index", + "ty": "u32" + }, + { + "name": "has_rail", + "ty": "bool" + }, + { + "name": "op", + "ty": "u8" + }, + { + "name": "data", + "ty": "RawPmbusBlock" + } + ], + "args_size": 40, + "reply": "()", + "reply_size": 0, + "error": "ResponseCode", + "encoding": "Hubpack", + "idempotent": true + } + ] + }, + { + "name": "Sequencer", + "task": "gimlet_seq", + "task_id": 10, + "ops": [ + { + "name": "get_state", + "code": 1, + "args_size": 0, + "reply": "drv_cpu_power_state::PowerState", + "reply_size": 1, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "set_state", + "code": 2, + "args": [ + { + "name": "state", + "ty": "drv_cpu_power_state::PowerState" + } + ], + "args_size": 1, + "reply": "drv_cpu_seq_api::Transition", + "reply_size": 1, + "error": "drv_cpu_seq_api::SeqError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "set_state_with_reason", + "code": 3, + "args": [ + { + "name": "state", + "ty": "drv_cpu_power_state::PowerState" + }, + { + "name": "reason", + "ty": "StateChangeReason" + } + ], + "args_size": 2, + "reply": "drv_cpu_seq_api::Transition", + "reply_size": 1, + "error": "drv_cpu_seq_api::SeqError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "send_hardware_nmi", + "code": 4, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "ServerDeath", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "read_fpga_regs", + "code": 5, + "args_size": 0, + "reply": "[u8; 64]", + "reply_size": 0, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "last_post_code", + "code": 6, + "args_size": 0, + "reply": "u32", + "reply_size": 4, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "post_code_buffer_len", + "code": 7, + "args_size": 0, + "reply": "u32", + "reply_size": 4, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "get_post_code", + "code": 8, + "args": [ + { + "name": "index", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "u32", + "reply_size": 4, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "gpio_edge_count", + "code": 9, + "args_size": 0, + "reply": "u32", + "reply_size": 4, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "gpio_cycle_count", + "code": 10, + "args_size": 0, + "reply": "u32", + "reply_size": 4, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "enable_console_redirect", + "code": 11, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "disable_console_redirect", + "code": 12, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", + "idempotent": true + } + ] + }, + { + "name": "Hash", + "task": "hash_driver", + "task_id": 12, + "ops": [ + { + "name": "init_sha256", + "code": 1, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "HashError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "update", + "code": 2, + "args": [ + { + "name": "len", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "HashError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": false + }, + { + "name": "finalize_sha256", + "code": 3, + "args_size": 0, + "reply": "[u8; crate::SHA256_SZ]", + "reply_size": 32, + "error": "HashError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "digest_sha256", + "code": 4, + "args": [ + { + "name": "len", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "[u8; crate::SHA256_SZ]", + "reply_size": 32, + "error": "HashError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": false + } + ] + }, + { + "name": "Rng", + "task": "rng_driver", + "task_id": 13, + "ops": [ + { + "name": "fill", + "code": 1, + "args_size": 0, + "reply": "usize", + "reply_size": 4, + "error": "RngError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "source", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": false + } + ] + }, + { + "name": "HostFlash", + "task": "hf", + "task_id": 14, + "ops": [ + { + "name": "read_id", + "code": 1, + "args_size": 0, + "reply": "HfChipId", + "reply_size": 20, + "error": "HfError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "capacity", + "code": 2, + "args_size": 0, + "reply": "usize", + "reply_size": 4, + "error": "HfError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "read_status", + "code": 3, + "args_size": 0, + "reply": "u8", + "reply_size": 1, + "error": "HfError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "bulk_erase", + "code": 4, + "args": [ + { + "name": "protect", + "ty": "HfProtectMode" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "HfError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "page_program", + "code": 5, + "args": [ + { + "name": "address", + "ty": "u32" + }, + { + "name": "protect", + "ty": "HfProtectMode" + } + ], + "args_size": 5, + "reply": "()", + "reply_size": 0, + "error": "HfError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": false + }, + { + "name": "page_program_dev", + "code": 6, + "args": [ + { + "name": "dev", + "ty": "HfDevSelect" + }, + { + "name": "address", + "ty": "u32" + }, + { + "name": "protect", + "ty": "HfProtectMode" + } + ], + "args_size": 6, + "reply": "()", + "reply_size": 0, + "error": "HfError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": false + }, + { + "name": "read", + "code": 7, + "args": [ + { + "name": "address", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "HfError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": false + }, + { + "name": "read_dev", + "code": 8, + "args": [ + { + "name": "dev", + "ty": "HfDevSelect" + }, + { + "name": "address", + "ty": "u32" + } + ], + "args_size": 5, + "reply": "()", + "reply_size": 0, + "error": "HfError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": false + }, + { + "name": "sector_erase", + "code": 9, + "args": [ + { + "name": "address", + "ty": "u32" + }, + { + "name": "protect", + "ty": "HfProtectMode" + } + ], + "args_size": 5, + "reply": "()", + "reply_size": 0, + "error": "HfError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "sector_erase_dev", + "code": 10, + "args": [ + { + "name": "dev", + "ty": "HfDevSelect" + }, + { + "name": "address", + "ty": "u32" + }, + { + "name": "protect", + "ty": "HfProtectMode" + } + ], + "args_size": 6, + "reply": "()", + "reply_size": 0, + "error": "HfError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "get_mux", + "code": 11, + "args_size": 0, + "reply": "HfMuxState", + "reply_size": 1, + "error": "HfError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "set_mux", + "code": 12, + "args": [ + { + "name": "state", + "ty": "HfMuxState" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "HfError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "get_dev", + "code": 13, + "args_size": 0, + "reply": "HfDevSelect", + "reply_size": 1, + "error": "HfError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "set_dev", + "code": 14, + "args": [ + { + "name": "dev", + "ty": "HfDevSelect" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "HfError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "check_dev", + "code": 15, + "args": [ + { + "name": "dev", + "ty": "HfDevSelect" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "HfError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "hash", + "code": 16, + "args": [ + { + "name": "address", + "ty": "u32" + }, + { + "name": "len", + "ty": "u32" + } + ], + "args_size": 8, + "reply": "[u8; drv_hash_api::SHA256_SZ]", + "reply_size": 32, + "error": "HfError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "hash_significant_bits", + "code": 17, + "args": [ + { + "name": "dev", + "ty": "HfDevSelect" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "HfError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "get_cached_hash", + "code": 18, + "args": [ + { + "name": "dev", + "ty": "HfDevSelect" + } + ], + "args_size": 1, + "reply": "[u8; drv_hash_api::SHA256_SZ]", + "reply_size": 32, + "error": "HfError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "get_persistent_data", + "code": 19, + "args_size": 0, + "reply": "HfPersistentData", + "reply_size": 1, + "error": "HfError", + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "write_persistent_data", + "code": 20, + "args": [ + { + "name": "dev_select", + "ty": "HfDevSelect" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "HfError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "apob_begin", + "code": 21, + "args": [ + { + "name": "length", + "ty": "u32" + }, + { + "name": "algorithm", + "ty": "ApobHash" + } + ], + "args_size": 40, + "reply": "()", + "reply_size": 0, + "error": "ApobBeginError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "apob_write", + "code": 22, + "args": [ + { + "name": "offset", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "ApobWriteError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": true + }, + { + "name": "apob_commit", + "code": 23, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "ApobCommitError", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "apob_lock", + "code": 24, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "apob_read", + "code": 25, + "args": [ + { + "name": "offset", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "usize", + "reply_size": 4, + "error": "ApobReadError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": true + }, + { + "name": "apob_clear", + "code": 26, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "ApobClearError", + "encoding": "Zerocopy", + "idempotent": true + } + ] + }, + { + "name": "Update", + "task": "update_server", + "task_id": 15, + "ops": [ + { + "name": "block_size", + "code": 1, + "args_size": 0, + "reply": "usize", + "reply_size": 4, + "error": "drv_update_api::UpdateError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "prep_image_update", + "code": 2, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "drv_update_api::UpdateError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "write_one_block", + "code": 3, + "args": [ + { + "name": "block_num", + "ty": "usize" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "drv_update_api::UpdateError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "block", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": false + }, + { + "name": "abort_update", + "code": 4, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "drv_update_api::UpdateError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "finish_image_update", + "code": 5, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "drv_update_api::UpdateError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "current_version", + "code": 6, + "args_size": 0, + "reply": "ImageVersion", + "reply_size": 8, + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "read_caboose_value", + "code": 7, + "args": [ + { + "name": "name", + "ty": "[u8; 4]" + } + ], + "args_size": 4, + "reply": "u32", + "reply_size": 4, + "error": "CabooseError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": true + }, + { + "name": "get_pending_boot_slot", + "code": 8, + "args_size": 0, + "reply": "SlotId", + "reply_size": 1, + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "set_pending_boot_slot", + "code": 9, + "args": [ + { + "name": "slot", + "ty": "SlotId" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "drv_update_api::UpdateError", + "encoding": "Hubpack", + "idempotent": false + } + ] + }, + { + "name": "Sensor", + "task": "sensor", + "task_id": 16, + "ops": [ + { + "name": "get", + "code": 1, + "args": [ + { + "name": "id", + "ty": "SensorId" + } + ], + "args_size": 4, + "reply": "f32", + "reply_size": 4, + "error": "SensorError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "get_reading", + "code": 2, + "args": [ + { + "name": "id", + "ty": "SensorId" + } + ], + "args_size": 4, + "reply": "Reading", + "reply_size": 12, + "error": "SensorError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "get_raw_reading", + "code": 3, + "args": [ + { + "name": "id", + "ty": "SensorId" + } + ], + "args_size": 4, + "reply": "Option<(Result, u64)>", + "reply_size": 0, + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "get_last_data", + "code": 4, + "args": [ + { + "name": "id", + "ty": "SensorId" + } + ], + "args_size": 4, + "reply": "Option<(f32, u64)>", + "reply_size": 13, + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "get_last_nodata", + "code": 5, + "args": [ + { + "name": "id", + "ty": "SensorId" + } + ], + "args_size": 4, + "reply": "Option<(NoData, u64)>", + "reply_size": 0, + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "get_min", + "code": 6, + "args": [ + { + "name": "id", + "ty": "SensorId" + } + ], + "args_size": 4, + "reply": "(f32, u64)", + "reply_size": 12, + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "get_max", + "code": 7, + "args": [ + { + "name": "id", + "ty": "SensorId" + } + ], + "args_size": 4, + "reply": "(f32, u64)", + "reply_size": 12, + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "post", + "code": 8, + "args": [ + { + "name": "id", + "ty": "SensorId" + }, + { + "name": "value", + "ty": "f32" + }, + { + "name": "timestamp", + "ty": "u64" + } + ], + "args_size": 16, + "reply": "()", + "reply_size": 0, + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "nodata", + "code": 9, + "args": [ + { + "name": "id", + "ty": "SensorId" + }, + { + "name": "nodata", + "ty": "NoData" + }, + { + "name": "timestamp", + "ty": "u64" + } + ], + "args_size": 16, + "reply": "()", + "reply_size": 0, + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "get_nerrors", + "code": 10, + "args": [ + { + "name": "id", + "ty": "SensorId" + } + ], + "args_size": 4, + "reply": "u32", + "reply_size": 4, + "encoding": "Hubpack", + "idempotent": true + } + ] + }, + { + "name": "HostSpComms", + "task": "host_sp_comms", + "task_id": 17, + "ops": [ + { + "name": "set_status", + "code": 1, + "args": [ + { + "name": "status", + "ty": "u64" + } + ], + "args_size": 8, + "reply": "()", + "reply_size": 0, + "error": "HostSpCommsError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "get_status", + "code": 2, + "args_size": 0, + "reply": "Status", + "reply_size": 8, + "error": "HostSpCommsError", + "encoding": "Zerocopy", + "idempotent": false + } + ] + }, + { + "name": "ControlPlaneAgent", + "task": "control_plane_agent", + "task_id": 20, + "ops": [ + { + "name": "fetch_host_phase2_data", + "code": 1, + "args": [ + { + "name": "image_hash", + "ty": "[u8; 32]" + }, + { + "name": "offset", + "ty": "u64" + }, + { + "name": "notification_bit", + "ty": "u8" + } + ], + "args_size": 41, + "reply": "()", + "reply_size": 0, + "error": "ControlPlaneAgentError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "get_host_phase2_data", + "code": 2, + "args": [ + { + "name": "image_hash", + "ty": "[u8; 32]" + }, + { + "name": "offset", + "ty": "u64" + } + ], + "args_size": 40, + "reply": "usize", + "reply_size": 4, + "error": "ControlPlaneAgentError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": false + }, + { + "name": "get_startup_options", + "code": 3, + "args_size": 0, + "reply": "HostStartupOptions", + "reply_size": 8, + "error": "ControlPlaneAgentError", + "encoding": "Ssmarshal", + "idempotent": false + }, + { + "name": "set_startup_options", + "code": 4, + "args": [ + { + "name": "startup_options", + "ty": "u64" + } + ], + "args_size": 8, + "reply": "()", + "reply_size": 0, + "error": "ControlPlaneAgentError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "identity", + "code": 5, + "args_size": 0, + "reply": "OxideIdentity", + "reply_size": 26, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "get_uart_client", + "code": 6, + "args_size": 0, + "reply": "UartClient", + "reply_size": 1, + "encoding": "Ssmarshal", + "idempotent": true + }, + { + "name": "get_installinator_image_id", + "code": 7, + "args_size": 0, + "reply": "usize", + "reply_size": 4, + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": true + }, + { + "name": "set_humility_uart_client", + "code": 8, + "args": [ + { + "name": "attach", + "ty": "bool" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "ControlPlaneAgentError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "uart_read", + "code": 9, + "args_size": 0, + "reply": "usize", + "reply_size": 4, + "error": "ControlPlaneAgentError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": false + }, + { + "name": "uart_write", + "code": 10, + "args_size": 0, + "reply": "usize", + "reply_size": 4, + "error": "ControlPlaneAgentError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": false + } + ] + }, + { + "name": "SpRot", + "task": "sprot", + "task_id": 21, + "ops": [ + { + "name": "status", + "code": 1, + "args_size": 0, + "reply": "SprotStatus", + "reply_size": 24, + "error": "SprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "io_stats", + "code": 2, + "args_size": 0, + "reply": "SprotIoStats", + "reply_size": 68, + "error": "SprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "rot_state", + "code": 3, + "args_size": 0, + "reply": "RotState", + "reply_size": 138, + "error": "SprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "pulse_cs", + "code": 4, + "args": [ + { + "name": "delay", + "ty": "u16" + } + ], + "args_size": 2, + "reply": "PulseStatus", + "reply_size": 2, + "error": "SprotError", + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "block_size", + "code": 5, + "args_size": 0, + "reply": "u32", + "reply_size": 4, + "error": "SprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "prep_image_update", + "code": 6, + "args": [ + { + "name": "target", + "ty": "UpdateTarget" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "SprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "write_one_block", + "code": 7, + "args": [ + { + "name": "block_num", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "SprotError", + "encoding": "Hubpack", + "leases": [ + { + "name": "block", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": false + }, + { + "name": "abort_update", + "code": 8, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "SprotError", + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "finish_image_update", + "code": 9, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "SprotError", + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "switch_default_image", + "code": 10, + "args": [ + { + "name": "slot", + "ty": "SlotId" + }, + { + "name": "duration", + "ty": "SwitchDuration" + } + ], + "args_size": 2, + "reply": "()", + "reply_size": 0, + "error": "SprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "reset", + "code": 11, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "SprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "dump", + "code": 12, + "args": [ + { + "name": "address", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "DumpOrSprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "caboose_size", + "code": 13, + "args": [ + { + "name": "slot", + "ty": "SlotId" + } + ], + "args_size": 1, + "reply": "u32", + "reply_size": 4, + "error": "RawCabooseOrSprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "read_caboose_region", + "code": 14, + "args": [ + { + "name": "offset", + "ty": "u32" + }, + { + "name": "slot", + "ty": "SlotId" + } + ], + "args_size": 8, + "reply": "()", + "reply_size": 0, + "error": "RawCabooseOrSprotError", + "encoding": "Hubpack", + "leases": [ + { + "name": "out", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": true + }, + { + "name": "rot_boot_info", + "code": 15, + "args_size": 0, + "reply": "RotBootInfo", + "reply_size": 72, + "error": "SprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "cert_chain_len", + "code": 16, + "args_size": 0, + "reply": "u32", + "reply_size": 4, + "error": "AttestOrSprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "cert_len", + "code": 17, + "args": [ + { + "name": "index", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "u32", + "reply_size": 4, + "error": "AttestOrSprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "cert", + "code": 18, + "args": [ + { + "name": "index", + "ty": "u32" + }, + { + "name": "offset", + "ty": "u32" + } + ], + "args_size": 8, + "reply": "()", + "reply_size": 0, + "error": "AttestOrSprotError", + "encoding": "Hubpack", + "leases": [ + { + "name": "dest", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": true + }, + { + "name": "record", + "code": 19, + "args": [ + { + "name": "algorithm", + "ty": "HashAlgorithm" + } + ], + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "AttestOrSprotError", + "encoding": "Hubpack", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": false + }, + { + "name": "read_rot_page", + "code": 20, + "args": [ + { + "name": "page", + "ty": "RotPage" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "SprotError", + "encoding": "Hubpack", + "leases": [ + { + "name": "dest", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": true + }, + { + "name": "log", + "code": 21, + "args": [ + { + "name": "offset", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "AttestOrSprotError", + "encoding": "Hubpack", + "leases": [ + { + "name": "dest", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": false + }, + { + "name": "log_len", + "code": 22, + "args_size": 0, + "reply": "u32", + "reply_size": 4, + "error": "AttestOrSprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "attest", + "code": 23, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "AttestOrSprotError", + "encoding": "Hubpack", + "leases": [ + { + "name": "nonce", + "ty": "[u8]", + "read": true, + "write": false + }, + { + "name": "dest", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": false + }, + { + "name": "attest_len", + "code": 24, + "args_size": 0, + "reply": "u32", + "reply_size": 4, + "error": "AttestOrSprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "enable_sp_slot_watchdog", + "code": 25, + "args": [ + { + "name": "time_ms", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "SprotError", + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "disable_sp_slot_watchdog", + "code": 26, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "SprotError", + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "sp_slot_watchdog_supported", + "code": 27, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "SprotError", + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "versioned_rot_boot_info", + "code": 28, + "args": [ + { + "name": "version", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "VersionedRotBootInfo", + "reply_size": 147, + "error": "SprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "component_caboose_size", + "code": 29, + "args": [ + { + "name": "component", + "ty": "RotComponent" + }, + { + "name": "slot", + "ty": "SlotId" + } + ], + "args_size": 2, + "reply": "u32", + "reply_size": 4, + "error": "RawCabooseOrSprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "component_read_caboose_region", + "code": 30, + "args": [ + { + "name": "offset", + "ty": "u32" + }, + { + "name": "component", + "ty": "RotComponent" + }, + { + "name": "slot", + "ty": "SlotId" + } + ], + "args_size": 8, + "reply": "()", + "reply_size": 0, + "error": "RawCabooseOrSprotError", + "encoding": "Hubpack", + "leases": [ + { + "name": "out", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": true + }, + { + "name": "component_prep_image_update", + "code": 31, + "args": [ + { + "name": "component", + "ty": "RotComponent" + }, + { + "name": "slot", + "ty": "SlotId" + } + ], + "args_size": 2, + "reply": "()", + "reply_size": 0, + "error": "SprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "component_switch_default_image", + "code": 32, + "args": [ + { + "name": "component", + "ty": "RotComponent" + }, + { + "name": "slot", + "ty": "SlotId" + }, + { + "name": "duration", + "ty": "SwitchDuration" + } + ], + "args_size": 3, + "reply": "()", + "reply_size": 0, + "error": "SprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "lifecycle_state", + "code": 33, + "args_size": 0, + "reply": "LifecycleState", + "reply_size": 1, + "error": "StateOrSprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "tq_cert_chain_len", + "code": 34, + "args_size": 0, + "reply": "u32", + "reply_size": 4, + "error": "AttestOrSprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "tq_cert_len", + "code": 35, + "args": [ + { + "name": "index", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "u32", + "reply_size": 4, + "error": "AttestOrSprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "tq_cert", + "code": 36, + "args": [ + { + "name": "index", + "ty": "u32" + }, + { + "name": "offset", + "ty": "u32" + } + ], + "args_size": 8, + "reply": "()", + "reply_size": 0, + "error": "AttestOrSprotError", + "encoding": "Hubpack", + "leases": [ + { + "name": "dest", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": true + }, + { + "name": "tq_sign", + "code": 37, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "AttestOrSprotError", + "encoding": "Hubpack", + "leases": [ + { + "name": "tq", + "ty": "[u8]", + "read": true, + "write": false + }, + { + "name": "dest", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": false + }, + { + "name": "tq_sign_len", + "code": 38, + "args_size": 0, + "reply": "u32", + "reply_size": 4, + "error": "AttestOrSprotError", + "encoding": "Hubpack", + "idempotent": true + } + ] + }, + { + "name": "Validate", + "task": "validate", + "task_id": 22, + "ops": [ + { + "name": "validate_i2c", + "code": 1, + "args": [ + { + "name": "index", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "ValidateOk", + "reply_size": 1, + "error": "ValidateError", + "encoding": "Zerocopy", + "idempotent": true + } + ] + }, + { + "name": "Vpd", + "task": "vpd", + "task_id": 23, + "ops": [ + { + "name": "read_tmp117_eeprom", + "code": 1, + "args": [ + { + "name": "index", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "[u8; 6]", + "reply_size": 6, + "error": "VpdError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "read", + "code": 2, + "args": [ + { + "name": "index", + "ty": "u8" + }, + { + "name": "offset", + "ty": "u16" + } + ], + "args_size": 3, + "reply": "[u8; 16]", + "reply_size": 16, + "error": "VpdError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "write", + "code": 3, + "args": [ + { + "name": "index", + "ty": "u8" + }, + { + "name": "offset", + "ty": "u16" + }, + { + "name": "contents", + "ty": "u8" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "VpdError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "is_locked", + "code": 4, + "args": [ + { + "name": "index", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "bool", + "reply_size": 1, + "error": "VpdError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "permanently_lock", + "code": 5, + "args": [ + { + "name": "index", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "VpdError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "num_vpd_devices", + "code": 6, + "args_size": 0, + "reply": "usize", + "reply_size": 4, + "encoding": "Zerocopy", + "idempotent": true + } + ] + }, + { + "name": "UserLeds", + "task": "user_leds", + "task_id": 24, + "ops": [ + { + "name": "led_on", + "code": 1, + "args": [ + { + "name": "index", + "ty": "usize" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "LedError", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "led_off", + "code": 2, + "args": [ + { + "name": "index", + "ty": "usize" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "LedError", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "led_toggle", + "code": 3, + "args": [ + { + "name": "index", + "ty": "usize" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "LedError", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "led_blink", + "code": 4, + "args": [ + { + "name": "index", + "ty": "usize" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "LedError", + "encoding": "Zerocopy", + "idempotent": true + } + ] + }, + { + "name": "DumpAgent", + "task": "dump_agent", + "task_id": 25, + "ops": [ + { + "name": "read_dump", + "code": 1, + "args": [ + { + "name": "index", + "ty": "u8" + }, + { + "name": "offset", + "ty": "u32" + } + ], + "args_size": 5, + "reply": "[u8; DUMP_READ_SIZE]", + "reply_size": 256, + "error": "DumpAgentError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "read_dump_into", + "code": 2, + "args": [ + { + "name": "index", + "ty": "u8" + }, + { + "name": "offset", + "ty": "u32" + } + ], + "args_size": 5, + "reply": "usize", + "reply_size": 4, + "error": "DumpAgentError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "out", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": false + }, + { + "name": "get_dump_area", + "code": 3, + "args": [ + { + "name": "index", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "DumpArea", + "reply_size": 10, + "error": "DumpAgentError", + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "initialize_dump", + "code": 4, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "DumpAgentError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "add_dump_segment", + "code": 5, + "args": [ + { + "name": "address", + "ty": "u32" + }, + { + "name": "length", + "ty": "u32" + } + ], + "args_size": 8, + "reply": "()", + "reply_size": 0, + "error": "DumpAgentError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "take_dump", + "code": 6, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "DumpAgentError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "dump_task", + "code": 7, + "args": [ + { + "name": "task_index", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "u8", + "reply_size": 1, + "error": "DumpAgentError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "dump_task_region", + "code": 8, + "args": [ + { + "name": "task_index", + "ty": "u32" + }, + { + "name": "start", + "ty": "u32" + }, + { + "name": "length", + "ty": "u32" + } + ], + "args_size": 12, + "reply": "u8", + "reply_size": 1, + "error": "DumpAgentError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "reinitialize_dump_from", + "code": 9, + "args": [ + { + "name": "index", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "DumpAgentError", + "encoding": "Hubpack", + "idempotent": false + } + ] + }, + { + "name": "Sbrmi", + "task": "sbrmi", + "task_id": 27, + "ops": [ + { + "name": "nthreads", + "code": 1, + "args_size": 0, + "reply": "u8", + "reply_size": 1, + "error": "SbrmiError", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "enabled", + "code": 2, + "args_size": 0, + "reply": "[u8; 16]", + "reply_size": 16, + "error": "SbrmiError", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "alert", + "code": 3, + "args_size": 0, + "reply": "[u8; 16]", + "reply_size": 16, + "error": "SbrmiError", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "cpuid", + "code": 4, + "args": [ + { + "name": "thread", + "ty": "u8" + }, + { + "name": "eax", + "ty": "u32" + }, + { + "name": "ecx", + "ty": "u32" + } + ], + "args_size": 9, + "reply": "[u32; 4]", + "reply_size": 16, + "error": "SbrmiError", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "rdmsr8", + "code": 5, + "args": [ + { + "name": "thread", + "ty": "u8" + }, + { + "name": "msr", + "ty": "u32" + } + ], + "args_size": 5, + "reply": "u8", + "reply_size": 1, + "error": "SbrmiError", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "rdmsr16", + "code": 6, + "args": [ + { + "name": "thread", + "ty": "u8" + }, + { + "name": "msr", + "ty": "u32" + } + ], + "args_size": 5, + "reply": "u16", + "reply_size": 2, + "error": "SbrmiError", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "rdmsr32", + "code": 7, + "args": [ + { + "name": "thread", + "ty": "u8" + }, + { + "name": "msr", + "ty": "u32" + } + ], + "args_size": 5, + "reply": "u32", + "reply_size": 4, + "error": "SbrmiError", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "rdmsr64", + "code": 8, + "args": [ + { + "name": "thread", + "ty": "u8" + }, + { + "name": "msr", + "ty": "u32" + } + ], + "args_size": 5, + "reply": "u64", + "reply_size": 8, + "error": "SbrmiError", + "encoding": "Zerocopy", + "idempotent": true + } + ] + } + ] +} \ No newline at end of file diff --git a/humility-hif-assembler/fixtures/grapefruit.json b/humility-hif-assembler/fixtures/grapefruit.json new file mode 100644 index 000000000..9fb2e4589 --- /dev/null +++ b/humility-hif-assembler/fixtures/grapefruit.json @@ -0,0 +1,5303 @@ +{ + "image_id": [ + 198, + 72, + 233, + 236, + 72, + 61, + 68, + 175 + ], + "board": "grapefruit", + "buses": [ + { + "name": "fpga", + "controller": 1, + "port_index": 0, + "port_name": "B" + } + ], + "functions": [ + { + "name": "Sleep", + "id": 0, + "arg_count": 1, + "args": [ + { + "name": "arg0", + "ty": "u16", + "size": 2 + } + ] + }, + { + "name": "Send", + "id": 1, + "arg_count": 4, + "args": [ + { + "name": "__0", + "ty": "Task", + "size": 1 + }, + { + "name": "__1", + "ty": "u16", + "size": 2 + }, + { + "name": "__2", + "ty": "Buffer", + "size": 1 + }, + { + "name": "__3", + "ty": "u32", + "size": 4 + } + ] + }, + { + "name": "SendLeaseRead", + "id": 2, + "arg_count": 5, + "args": [ + { + "name": "__0", + "ty": "Task", + "size": 1 + }, + { + "name": "__1", + "ty": "u16", + "size": 2 + }, + { + "name": "__2", + "ty": "Buffer", + "size": 1 + }, + { + "name": "__3", + "ty": "u32", + "size": 4 + }, + { + "name": "__4", + "ty": "u32", + "size": 4 + } + ] + }, + { + "name": "SendLeaseReadWrite", + "id": 3, + "arg_count": 6, + "args": [ + { + "name": "__0", + "ty": "Task", + "size": 1 + }, + { + "name": "__1", + "ty": "u16", + "size": 2 + }, + { + "name": "__2", + "ty": "Buffer", + "size": 1 + }, + { + "name": "__3", + "ty": "u32", + "size": 4 + }, + { + "name": "__4", + "ty": "u32", + "size": 4 + }, + { + "name": "__5", + "ty": "u32", + "size": 4 + } + ] + }, + { + "name": "SendLeaseWrite", + "id": 4, + "arg_count": 5, + "args": [ + { + "name": "__0", + "ty": "Task", + "size": 1 + }, + { + "name": "__1", + "ty": "u16", + "size": 2 + }, + { + "name": "__2", + "ty": "Buffer", + "size": 1 + }, + { + "name": "__3", + "ty": "u32", + "size": 4 + }, + { + "name": "__4", + "ty": "u32", + "size": 4 + } + ] + }, + { + "name": "I2cRead", + "id": 5, + "arg_count": 7, + "args": [ + { + "name": "__0", + "ty": "Controller", + "size": 1 + }, + { + "name": "__1", + "ty": "PortIndex", + "size": 1 + }, + { + "name": "__2", + "ty": "Mux", + "size": 1 + }, + { + "name": "__3", + "ty": "Segment", + "size": 1 + }, + { + "name": "__4", + "ty": "u8", + "size": 1 + }, + { + "name": "__5", + "ty": "u8", + "size": 1 + }, + { + "name": "__6", + "ty": "u32", + "size": 4 + } + ], + "errors": [ + { + "code": 1, + "name": "BadResponse" + }, + { + "code": 2, + "name": "BadArg" + }, + { + "code": 3, + "name": "NoDevice" + }, + { + "code": 4, + "name": "BadController" + }, + { + "code": 5, + "name": "ReservedAddress" + }, + { + "code": 6, + "name": "BadPort" + }, + { + "code": 7, + "name": "NoRegister" + }, + { + "code": 8, + "name": "BadMux" + }, + { + "code": 9, + "name": "BadSegment" + }, + { + "code": 10, + "name": "MuxNotFound" + }, + { + "code": 11, + "name": "SegmentNotFound" + }, + { + "code": 12, + "name": "SegmentDisconnected" + }, + { + "code": 13, + "name": "MuxDisconnected" + }, + { + "code": 14, + "name": "MuxMissing" + }, + { + "code": 15, + "name": "BadMuxRegister" + }, + { + "code": 16, + "name": "BusReset" + }, + { + "code": 17, + "name": "BusResetMux" + }, + { + "code": 18, + "name": "BusLocked" + }, + { + "code": 19, + "name": "BusLockedMux" + }, + { + "code": 20, + "name": "ControllerBusy" + }, + { + "code": 21, + "name": "BusError" + }, + { + "code": 22, + "name": "BadDeviceState" + }, + { + "code": 23, + "name": "OperationNotSupported" + }, + { + "code": 24, + "name": "IllegalLeaseCount" + }, + { + "code": 25, + "name": "TooMuchData" + } + ] + }, + { + "name": "I2cWrite", + "id": 6, + "arg_count": 8, + "args": [ + { + "name": "__0", + "ty": "Controller", + "size": 1 + }, + { + "name": "__1", + "ty": "PortIndex", + "size": 1 + }, + { + "name": "__2", + "ty": "Mux", + "size": 1 + }, + { + "name": "__3", + "ty": "Segment", + "size": 1 + }, + { + "name": "__4", + "ty": "u8", + "size": 1 + }, + { + "name": "__5", + "ty": "u8", + "size": 1 + }, + { + "name": "__6", + "ty": "Buffer", + "size": 1 + }, + { + "name": "__7", + "ty": "u32", + "size": 4 + } + ], + "errors": [ + { + "code": 1, + "name": "BadResponse" + }, + { + "code": 2, + "name": "BadArg" + }, + { + "code": 3, + "name": "NoDevice" + }, + { + "code": 4, + "name": "BadController" + }, + { + "code": 5, + "name": "ReservedAddress" + }, + { + "code": 6, + "name": "BadPort" + }, + { + "code": 7, + "name": "NoRegister" + }, + { + "code": 8, + "name": "BadMux" + }, + { + "code": 9, + "name": "BadSegment" + }, + { + "code": 10, + "name": "MuxNotFound" + }, + { + "code": 11, + "name": "SegmentNotFound" + }, + { + "code": 12, + "name": "SegmentDisconnected" + }, + { + "code": 13, + "name": "MuxDisconnected" + }, + { + "code": 14, + "name": "MuxMissing" + }, + { + "code": 15, + "name": "BadMuxRegister" + }, + { + "code": 16, + "name": "BusReset" + }, + { + "code": 17, + "name": "BusResetMux" + }, + { + "code": 18, + "name": "BusLocked" + }, + { + "code": 19, + "name": "BusLockedMux" + }, + { + "code": 20, + "name": "ControllerBusy" + }, + { + "code": 21, + "name": "BusError" + }, + { + "code": 22, + "name": "BadDeviceState" + }, + { + "code": 23, + "name": "OperationNotSupported" + }, + { + "code": 24, + "name": "IllegalLeaseCount" + }, + { + "code": 25, + "name": "TooMuchData" + } + ] + }, + { + "name": "I2cBulkWrite", + "id": 7, + "arg_count": 8, + "args": [ + { + "name": "__0", + "ty": "Controller", + "size": 1 + }, + { + "name": "__1", + "ty": "PortIndex", + "size": 1 + }, + { + "name": "__2", + "ty": "Mux", + "size": 1 + }, + { + "name": "__3", + "ty": "Segment", + "size": 1 + }, + { + "name": "__4", + "ty": "u8", + "size": 1 + }, + { + "name": "__5", + "ty": "u8", + "size": 1 + }, + { + "name": "__6", + "ty": "u32", + "size": 4 + }, + { + "name": "__7", + "ty": "u32", + "size": 4 + } + ], + "errors": [ + { + "code": 1, + "name": "BadResponse" + }, + { + "code": 2, + "name": "BadArg" + }, + { + "code": 3, + "name": "NoDevice" + }, + { + "code": 4, + "name": "BadController" + }, + { + "code": 5, + "name": "ReservedAddress" + }, + { + "code": 6, + "name": "BadPort" + }, + { + "code": 7, + "name": "NoRegister" + }, + { + "code": 8, + "name": "BadMux" + }, + { + "code": 9, + "name": "BadSegment" + }, + { + "code": 10, + "name": "MuxNotFound" + }, + { + "code": 11, + "name": "SegmentNotFound" + }, + { + "code": 12, + "name": "SegmentDisconnected" + }, + { + "code": 13, + "name": "MuxDisconnected" + }, + { + "code": 14, + "name": "MuxMissing" + }, + { + "code": 15, + "name": "BadMuxRegister" + }, + { + "code": 16, + "name": "BusReset" + }, + { + "code": 17, + "name": "BusResetMux" + }, + { + "code": 18, + "name": "BusLocked" + }, + { + "code": 19, + "name": "BusLockedMux" + }, + { + "code": 20, + "name": "ControllerBusy" + }, + { + "code": 21, + "name": "BusError" + }, + { + "code": 22, + "name": "BadDeviceState" + }, + { + "code": 23, + "name": "OperationNotSupported" + }, + { + "code": 24, + "name": "IllegalLeaseCount" + }, + { + "code": 25, + "name": "TooMuchData" + } + ] + }, + { + "name": "GpioInput", + "id": 8, + "arg_count": 1, + "args": [ + { + "name": "arg0", + "ty": "Port", + "size": 1 + } + ] + }, + { + "name": "GpioToggle", + "id": 9, + "arg_count": 2, + "args": [ + { + "name": "__0", + "ty": "Port", + "size": 1 + }, + { + "name": "__1", + "ty": "u8", + "size": 1 + } + ] + }, + { + "name": "GpioSet", + "id": 10, + "arg_count": 2, + "args": [ + { + "name": "__0", + "ty": "Port", + "size": 1 + }, + { + "name": "__1", + "ty": "u8", + "size": 1 + } + ] + }, + { + "name": "GpioReset", + "id": 11, + "arg_count": 2, + "args": [ + { + "name": "__0", + "ty": "Port", + "size": 1 + }, + { + "name": "__1", + "ty": "u8", + "size": 1 + } + ] + }, + { + "name": "GpioConfigure", + "id": 12, + "arg_count": 7, + "args": [ + { + "name": "__0", + "ty": "Port", + "size": 1 + }, + { + "name": "__1", + "ty": "u8", + "size": 1 + }, + { + "name": "__2", + "ty": "Mode", + "size": 1 + }, + { + "name": "__3", + "ty": "OutputType", + "size": 1 + }, + { + "name": "__4", + "ty": "Speed", + "size": 1 + }, + { + "name": "__5", + "ty": "Pull", + "size": 1 + }, + { + "name": "__6", + "ty": "Alternate", + "size": 1 + } + ] + }, + { + "name": "QspiReadId", + "id": 13, + "arg_count": 0, + "errors": [ + { + "code": 1, + "name": "WriteEnableFailed" + }, + { + "code": 2, + "name": "HashBadRange" + }, + { + "code": 3, + "name": "HashError" + }, + { + "code": 4, + "name": "HashNotConfigured" + }, + { + "code": 5, + "name": "NotMuxedToSP" + }, + { + "code": 6, + "name": "Sector0IsReserved" + }, + { + "code": 7, + "name": "NoPersistentData" + }, + { + "code": 8, + "name": "MonotonicCounterOverflow" + }, + { + "code": 9, + "name": "BadChipId" + }, + { + "code": 10, + "name": "BadAddress" + }, + { + "code": 11, + "name": "QspiTimeout" + }, + { + "code": 12, + "name": "QspiTransferError" + }, + { + "code": 13, + "name": "HashUncalculated" + }, + { + "code": 14, + "name": "RecalculateHash" + }, + { + "code": 15, + "name": "HashInProgress" + }, + { + "code": 16, + "name": "BadCapacity" + }, + { + "code": 17, + "name": "ServerRestarted" + } + ] + }, + { + "name": "QspiReadStatus", + "id": 14, + "arg_count": 0, + "errors": [ + { + "code": 1, + "name": "WriteEnableFailed" + }, + { + "code": 2, + "name": "HashBadRange" + }, + { + "code": 3, + "name": "HashError" + }, + { + "code": 4, + "name": "HashNotConfigured" + }, + { + "code": 5, + "name": "NotMuxedToSP" + }, + { + "code": 6, + "name": "Sector0IsReserved" + }, + { + "code": 7, + "name": "NoPersistentData" + }, + { + "code": 8, + "name": "MonotonicCounterOverflow" + }, + { + "code": 9, + "name": "BadChipId" + }, + { + "code": 10, + "name": "BadAddress" + }, + { + "code": 11, + "name": "QspiTimeout" + }, + { + "code": 12, + "name": "QspiTransferError" + }, + { + "code": 13, + "name": "HashUncalculated" + }, + { + "code": 14, + "name": "RecalculateHash" + }, + { + "code": 15, + "name": "HashInProgress" + }, + { + "code": 16, + "name": "BadCapacity" + }, + { + "code": 17, + "name": "ServerRestarted" + } + ] + }, + { + "name": "QspiBulkErase", + "id": 15, + "arg_count": 0, + "errors": [ + { + "code": 1, + "name": "WriteEnableFailed" + }, + { + "code": 2, + "name": "HashBadRange" + }, + { + "code": 3, + "name": "HashError" + }, + { + "code": 4, + "name": "HashNotConfigured" + }, + { + "code": 5, + "name": "NotMuxedToSP" + }, + { + "code": 6, + "name": "Sector0IsReserved" + }, + { + "code": 7, + "name": "NoPersistentData" + }, + { + "code": 8, + "name": "MonotonicCounterOverflow" + }, + { + "code": 9, + "name": "BadChipId" + }, + { + "code": 10, + "name": "BadAddress" + }, + { + "code": 11, + "name": "QspiTimeout" + }, + { + "code": 12, + "name": "QspiTransferError" + }, + { + "code": 13, + "name": "HashUncalculated" + }, + { + "code": 14, + "name": "RecalculateHash" + }, + { + "code": 15, + "name": "HashInProgress" + }, + { + "code": 16, + "name": "BadCapacity" + }, + { + "code": 17, + "name": "ServerRestarted" + } + ] + }, + { + "name": "QspiPageProgram", + "id": 16, + "arg_count": 3, + "args": [ + { + "name": "__0", + "ty": "u32", + "size": 4 + }, + { + "name": "__1", + "ty": "u32", + "size": 4 + }, + { + "name": "__2", + "ty": "u32", + "size": 4 + } + ], + "errors": [ + { + "code": 1, + "name": "WriteEnableFailed" + }, + { + "code": 2, + "name": "HashBadRange" + }, + { + "code": 3, + "name": "HashError" + }, + { + "code": 4, + "name": "HashNotConfigured" + }, + { + "code": 5, + "name": "NotMuxedToSP" + }, + { + "code": 6, + "name": "Sector0IsReserved" + }, + { + "code": 7, + "name": "NoPersistentData" + }, + { + "code": 8, + "name": "MonotonicCounterOverflow" + }, + { + "code": 9, + "name": "BadChipId" + }, + { + "code": 10, + "name": "BadAddress" + }, + { + "code": 11, + "name": "QspiTimeout" + }, + { + "code": 12, + "name": "QspiTransferError" + }, + { + "code": 13, + "name": "HashUncalculated" + }, + { + "code": 14, + "name": "RecalculateHash" + }, + { + "code": 15, + "name": "HashInProgress" + }, + { + "code": 16, + "name": "BadCapacity" + }, + { + "code": 17, + "name": "ServerRestarted" + } + ] + }, + { + "name": "QspiPageProgramSector0", + "id": 17, + "arg_count": 3, + "args": [ + { + "name": "__0", + "ty": "u32", + "size": 4 + }, + { + "name": "__1", + "ty": "u32", + "size": 4 + }, + { + "name": "__2", + "ty": "u32", + "size": 4 + } + ], + "errors": [ + { + "code": 1, + "name": "WriteEnableFailed" + }, + { + "code": 2, + "name": "HashBadRange" + }, + { + "code": 3, + "name": "HashError" + }, + { + "code": 4, + "name": "HashNotConfigured" + }, + { + "code": 5, + "name": "NotMuxedToSP" + }, + { + "code": 6, + "name": "Sector0IsReserved" + }, + { + "code": 7, + "name": "NoPersistentData" + }, + { + "code": 8, + "name": "MonotonicCounterOverflow" + }, + { + "code": 9, + "name": "BadChipId" + }, + { + "code": 10, + "name": "BadAddress" + }, + { + "code": 11, + "name": "QspiTimeout" + }, + { + "code": 12, + "name": "QspiTransferError" + }, + { + "code": 13, + "name": "HashUncalculated" + }, + { + "code": 14, + "name": "RecalculateHash" + }, + { + "code": 15, + "name": "HashInProgress" + }, + { + "code": 16, + "name": "BadCapacity" + }, + { + "code": 17, + "name": "ServerRestarted" + } + ] + }, + { + "name": "QspiRead", + "id": 18, + "arg_count": 2, + "args": [ + { + "name": "__0", + "ty": "u32", + "size": 4 + }, + { + "name": "__1", + "ty": "u32", + "size": 4 + } + ], + "errors": [ + { + "code": 1, + "name": "WriteEnableFailed" + }, + { + "code": 2, + "name": "HashBadRange" + }, + { + "code": 3, + "name": "HashError" + }, + { + "code": 4, + "name": "HashNotConfigured" + }, + { + "code": 5, + "name": "NotMuxedToSP" + }, + { + "code": 6, + "name": "Sector0IsReserved" + }, + { + "code": 7, + "name": "NoPersistentData" + }, + { + "code": 8, + "name": "MonotonicCounterOverflow" + }, + { + "code": 9, + "name": "BadChipId" + }, + { + "code": 10, + "name": "BadAddress" + }, + { + "code": 11, + "name": "QspiTimeout" + }, + { + "code": 12, + "name": "QspiTransferError" + }, + { + "code": 13, + "name": "HashUncalculated" + }, + { + "code": 14, + "name": "RecalculateHash" + }, + { + "code": 15, + "name": "HashInProgress" + }, + { + "code": 16, + "name": "BadCapacity" + }, + { + "code": 17, + "name": "ServerRestarted" + } + ] + }, + { + "name": "QspiSectorErase", + "id": 19, + "arg_count": 1, + "args": [ + { + "name": "arg0", + "ty": "u32", + "size": 4 + } + ], + "errors": [ + { + "code": 1, + "name": "WriteEnableFailed" + }, + { + "code": 2, + "name": "HashBadRange" + }, + { + "code": 3, + "name": "HashError" + }, + { + "code": 4, + "name": "HashNotConfigured" + }, + { + "code": 5, + "name": "NotMuxedToSP" + }, + { + "code": 6, + "name": "Sector0IsReserved" + }, + { + "code": 7, + "name": "NoPersistentData" + }, + { + "code": 8, + "name": "MonotonicCounterOverflow" + }, + { + "code": 9, + "name": "BadChipId" + }, + { + "code": 10, + "name": "BadAddress" + }, + { + "code": 11, + "name": "QspiTimeout" + }, + { + "code": 12, + "name": "QspiTransferError" + }, + { + "code": 13, + "name": "HashUncalculated" + }, + { + "code": 14, + "name": "RecalculateHash" + }, + { + "code": 15, + "name": "HashInProgress" + }, + { + "code": 16, + "name": "BadCapacity" + }, + { + "code": 17, + "name": "ServerRestarted" + } + ] + }, + { + "name": "QspiSector0Erase", + "id": 20, + "arg_count": 0, + "errors": [ + { + "code": 1, + "name": "WriteEnableFailed" + }, + { + "code": 2, + "name": "HashBadRange" + }, + { + "code": 3, + "name": "HashError" + }, + { + "code": 4, + "name": "HashNotConfigured" + }, + { + "code": 5, + "name": "NotMuxedToSP" + }, + { + "code": 6, + "name": "Sector0IsReserved" + }, + { + "code": 7, + "name": "NoPersistentData" + }, + { + "code": 8, + "name": "MonotonicCounterOverflow" + }, + { + "code": 9, + "name": "BadChipId" + }, + { + "code": 10, + "name": "BadAddress" + }, + { + "code": 11, + "name": "QspiTimeout" + }, + { + "code": 12, + "name": "QspiTransferError" + }, + { + "code": 13, + "name": "HashUncalculated" + }, + { + "code": 14, + "name": "RecalculateHash" + }, + { + "code": 15, + "name": "HashInProgress" + }, + { + "code": 16, + "name": "BadCapacity" + }, + { + "code": 17, + "name": "ServerRestarted" + } + ] + }, + { + "name": "QspiVerify", + "id": 21, + "arg_count": 3, + "args": [ + { + "name": "__0", + "ty": "u32", + "size": 4 + }, + { + "name": "__1", + "ty": "u32", + "size": 4 + }, + { + "name": "__2", + "ty": "u32", + "size": 4 + } + ], + "errors": [ + { + "code": 1, + "name": "WriteEnableFailed" + }, + { + "code": 2, + "name": "HashBadRange" + }, + { + "code": 3, + "name": "HashError" + }, + { + "code": 4, + "name": "HashNotConfigured" + }, + { + "code": 5, + "name": "NotMuxedToSP" + }, + { + "code": 6, + "name": "Sector0IsReserved" + }, + { + "code": 7, + "name": "NoPersistentData" + }, + { + "code": 8, + "name": "MonotonicCounterOverflow" + }, + { + "code": 9, + "name": "BadChipId" + }, + { + "code": 10, + "name": "BadAddress" + }, + { + "code": 11, + "name": "QspiTimeout" + }, + { + "code": 12, + "name": "QspiTransferError" + }, + { + "code": 13, + "name": "HashUncalculated" + }, + { + "code": 14, + "name": "RecalculateHash" + }, + { + "code": 15, + "name": "HashInProgress" + }, + { + "code": 16, + "name": "BadCapacity" + }, + { + "code": 17, + "name": "ServerRestarted" + } + ] + }, + { + "name": "QspiHash", + "id": 22, + "arg_count": 2, + "args": [ + { + "name": "__0", + "ty": "u32", + "size": 4 + }, + { + "name": "__1", + "ty": "u32", + "size": 4 + } + ], + "errors": [ + { + "code": 1, + "name": "WriteEnableFailed" + }, + { + "code": 2, + "name": "HashBadRange" + }, + { + "code": 3, + "name": "HashError" + }, + { + "code": 4, + "name": "HashNotConfigured" + }, + { + "code": 5, + "name": "NotMuxedToSP" + }, + { + "code": 6, + "name": "Sector0IsReserved" + }, + { + "code": 7, + "name": "NoPersistentData" + }, + { + "code": 8, + "name": "MonotonicCounterOverflow" + }, + { + "code": 9, + "name": "BadChipId" + }, + { + "code": 10, + "name": "BadAddress" + }, + { + "code": 11, + "name": "QspiTimeout" + }, + { + "code": 12, + "name": "QspiTransferError" + }, + { + "code": 13, + "name": "HashUncalculated" + }, + { + "code": 14, + "name": "RecalculateHash" + }, + { + "code": 15, + "name": "HashInProgress" + }, + { + "code": 16, + "name": "BadCapacity" + }, + { + "code": 17, + "name": "ServerRestarted" + } + ] + }, + { + "name": "HashDigest", + "id": 23, + "arg_count": 1, + "args": [ + { + "name": "arg0", + "ty": "u32", + "size": 4 + } + ], + "errors": [ + { + "code": 1, + "name": "NotInitialized" + }, + { + "code": 2, + "name": "InvalidState" + }, + { + "code": 3, + "name": "Busy" + }, + { + "code": 4, + "name": "NoData" + }, + { + "code": 5, + "name": "ServerRestarted" + } + ] + }, + { + "name": "HashInit", + "id": 24, + "arg_count": 0, + "errors": [ + { + "code": 1, + "name": "NotInitialized" + }, + { + "code": 2, + "name": "InvalidState" + }, + { + "code": 3, + "name": "Busy" + }, + { + "code": 4, + "name": "NoData" + }, + { + "code": 5, + "name": "ServerRestarted" + } + ] + }, + { + "name": "HashUpdate", + "id": 25, + "arg_count": 1, + "args": [ + { + "name": "arg0", + "ty": "u32", + "size": 4 + } + ], + "errors": [ + { + "code": 1, + "name": "NotInitialized" + }, + { + "code": 2, + "name": "InvalidState" + }, + { + "code": 3, + "name": "Busy" + }, + { + "code": 4, + "name": "NoData" + }, + { + "code": 5, + "name": "ServerRestarted" + } + ] + }, + { + "name": "HashFinalize", + "id": 26, + "arg_count": 0, + "errors": [ + { + "code": 1, + "name": "NotInitialized" + }, + { + "code": 2, + "name": "InvalidState" + }, + { + "code": 3, + "name": "Busy" + }, + { + "code": 4, + "name": "NoData" + }, + { + "code": 5, + "name": "ServerRestarted" + } + ] + } + ], + "buffer_sizes": { + "text": 4096, + "data": 20480, + "rstack": 2048, + "scratch": 1025 + }, + "idol_interfaces": [ + { + "name": "Jefe", + "task": "jefe", + "task_id": 0, + "ops": [ + { + "name": "get_state", + "code": 1, + "args_size": 0, + "reply": "u32", + "reply_size": 4, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "set_state", + "code": 2, + "args": [ + { + "name": "state", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "request_reset", + "code": 3, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "get_reset_reason", + "code": 4, + "args_size": 0, + "reply": "ResetReason", + "reply_size": 5, + "encoding": "Ssmarshal", + "idempotent": true + }, + { + "name": "set_reset_reason", + "code": 5, + "args": [ + { + "name": "reason", + "ty": "ResetReason" + } + ], + "args_size": 8, + "reply": "()", + "reply_size": 0, + "encoding": "Ssmarshal", + "idempotent": true + }, + { + "name": "reinitialize_dump_areas", + "code": 6, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "DumpAgentError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "get_dump_area", + "code": 7, + "args": [ + { + "name": "index", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "humpty::DumpArea", + "reply_size": 10, + "error": "DumpAgentError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "claim_dump_area", + "code": 8, + "args_size": 0, + "reply": "humpty::DumpArea", + "reply_size": 10, + "error": "DumpAgentError", + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "dump_task", + "code": 9, + "args": [ + { + "name": "task_index", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "u8", + "reply_size": 1, + "error": "DumpAgentError", + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "dump_task_region", + "code": 10, + "args": [ + { + "name": "task_index", + "ty": "u32" + }, + { + "name": "address", + "ty": "u32" + }, + { + "name": "length", + "ty": "u32" + } + ], + "args_size": 12, + "reply": "u8", + "reply_size": 1, + "error": "DumpAgentError", + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "reinitialize_dump_from", + "code": 11, + "args": [ + { + "name": "index", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "DumpAgentError", + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "restart_me_raw", + "code": 12, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "read_fault_counts", + "code": 13, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[usize; hubris_num_tasks::NUM_TASKS]", + "read": false, + "write": true + } + ], + "idempotent": true + } + ] + }, + { + "name": "Sys", + "task": "sys", + "task_id": 1, + "ops": [ + { + "name": "enable_clock_raw", + "code": 1, + "args": [ + { + "name": "peripheral", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "RccError", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "disable_clock_raw", + "code": 2, + "args": [ + { + "name": "peripheral", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "RccError", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "enter_reset_raw", + "code": 3, + "args": [ + { + "name": "peripheral", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "RccError", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "leave_reset_raw", + "code": 4, + "args": [ + { + "name": "peripheral", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "RccError", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "gpio_configure_raw", + "code": 5, + "args": [ + { + "name": "port", + "ty": "Port" + }, + { + "name": "pins", + "ty": "u16" + }, + { + "name": "packed_attributes", + "ty": "u16" + } + ], + "args_size": 5, + "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "gpio_set_reset", + "code": 6, + "args": [ + { + "name": "port", + "ty": "Port" + }, + { + "name": "set_pins", + "ty": "u16" + }, + { + "name": "reset_pins", + "ty": "u16" + } + ], + "args_size": 5, + "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "gpio_read_input", + "code": 7, + "args": [ + { + "name": "port", + "ty": "Port" + } + ], + "args_size": 1, + "reply": "u16", + "reply_size": 2, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "gpio_toggle", + "code": 8, + "args": [ + { + "name": "port", + "ty": "Port" + }, + { + "name": "pins", + "ty": "u16" + } + ], + "args_size": 3, + "reply": "()", + "reply_size": 0, + "error": "ServerDeath", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "read_uid", + "code": 9, + "args_size": 0, + "reply": "[u32; 3]", + "reply_size": 0, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "gpio_irq_configure", + "code": 10, + "args": [ + { + "name": "mask", + "ty": "u32" + }, + { + "name": "sensitivity", + "ty": "Edge" + } + ], + "args_size": 5, + "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "gpio_irq_control", + "code": 11, + "args": [ + { + "name": "mask", + "ty": "u32" + }, + { + "name": "op", + "ty": "IrqControl" + } + ], + "args_size": 5, + "reply": "bool", + "reply_size": 1, + "error": "ServerDeath", + "encoding": "Zerocopy", + "idempotent": false + } + ] + }, + { + "name": "Spi", + "task": "spi2_driver", + "task_id": 2, + "ops": [ + { + "name": "read", + "code": 1, + "args": [ + { + "name": "device_index", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "drv_spi_api::SpiError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "sink", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": false + }, + { + "name": "write", + "code": 2, + "args": [ + { + "name": "device_index", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "drv_spi_api::SpiError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "source", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": false + }, + { + "name": "exchange", + "code": 3, + "args": [ + { + "name": "device_index", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "drv_spi_api::SpiError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "source", + "ty": "[u8]", + "read": true, + "write": false + }, + { + "name": "sink", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": false + }, + { + "name": "lock", + "code": 4, + "args": [ + { + "name": "device_index", + "ty": "u8" + }, + { + "name": "cs_state", + "ty": "CsState" + } + ], + "args_size": 2, + "reply": "()", + "reply_size": 0, + "error": "ServerDeath", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "release", + "code": 5, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "ServerDeath", + "encoding": "Zerocopy", + "idempotent": false + } + ] + }, + { + "name": "UserLeds", + "task": "user_leds", + "task_id": 4, + "ops": [ + { + "name": "led_on", + "code": 1, + "args": [ + { + "name": "index", + "ty": "usize" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "LedError", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "led_off", + "code": 2, + "args": [ + { + "name": "index", + "ty": "usize" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "LedError", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "led_toggle", + "code": 3, + "args": [ + { + "name": "index", + "ty": "usize" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "LedError", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "led_blink", + "code": 4, + "args": [ + { + "name": "index", + "ty": "usize" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "LedError", + "encoding": "Zerocopy", + "idempotent": true + } + ] + }, + { + "name": "Packrat", + "task": "packrat", + "task_id": 5, + "ops": [ + { + "name": "get_mac_address_block", + "code": 1, + "args_size": 0, + "reply": "MacAddressBlock", + "reply_size": 9, + "error": "CacheGetError", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "set_mac_address_block", + "code": 2, + "args": [ + { + "name": "macs", + "ty": "MacAddressBlock" + } + ], + "args_size": 9, + "reply": "()", + "reply_size": 0, + "error": "CacheSetError", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "get_identity", + "code": 3, + "args_size": 0, + "reply": "OxideIdentity", + "reply_size": 26, + "error": "CacheGetError", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "set_identity", + "code": 4, + "args": [ + { + "name": "macs", + "ty": "OxideIdentity" + } + ], + "args_size": 26, + "reply": "()", + "reply_size": 0, + "error": "CacheSetError", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "get_next_boot_host_startup_options", + "code": 5, + "args_size": 0, + "reply": "HostStartupOptions", + "reply_size": 8, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "set_next_boot_host_startup_options", + "code": 6, + "args": [ + { + "name": "startup_options", + "ty": "HostStartupOptions" + } + ], + "args_size": 8, + "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "remove_spd", + "code": 7, + "args": [ + { + "name": "index", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "set_spd_eeprom", + "code": 8, + "args": [ + { + "name": "index", + "ty": "u8" + }, + { + "name": "offset", + "ty": "usize" + } + ], + "args_size": 5, + "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": true + }, + { + "name": "get_spd_present", + "code": 9, + "args": [ + { + "name": "index", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "bool", + "reply_size": 1, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "get_spd_data", + "code": 10, + "args": [ + { + "name": "index", + "ty": "u8" + }, + { + "name": "offset", + "ty": "usize" + } + ], + "args_size": 5, + "reply": "u8", + "reply_size": 1, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "get_full_spd_data", + "code": 11, + "args": [ + { + "name": "index", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": true + }, + { + "name": "set_ereport_restart_id", + "code": 12, + "args": [ + { + "name": "restart_id", + "ty": "u128" + } + ], + "args_size": 16, + "reply": "()", + "reply_size": 0, + "error": "CacheSetError", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "deliver_encoded_ereport", + "code": 13, + "args_size": 0, + "reply": "ereport_messages::Ena", + "reply_size": 8, + "error": "EreportWriteError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": true + }, + { + "name": "read_ereports", + "code": 14, + "args": [ + { + "name": "request_id", + "ty": "ereport_messages::RequestIdV0" + }, + { + "name": "restart_id", + "ty": "ereport_messages::RestartId" + }, + { + "name": "start_ena", + "ty": "ereport_messages::Ena" + }, + { + "name": "limit", + "ty": "u8" + }, + { + "name": "committed_ena", + "ty": "ereport_messages::Ena" + } + ], + "args_size": 34, + "reply": "usize", + "reply_size": 4, + "error": "EreportReadError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": true + } + ] + }, + { + "name": "Validate", + "task": "validate", + "task_id": 7, + "ops": [ + { + "name": "validate_i2c", + "code": 1, + "args": [ + { + "name": "index", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "ValidateOk", + "reply_size": 1, + "error": "ValidateError", + "encoding": "Zerocopy", + "idempotent": true + } + ] + }, + { + "name": "Hash", + "task": "hash_driver", + "task_id": 8, + "ops": [ + { + "name": "init_sha256", + "code": 1, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "HashError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "update", + "code": 2, + "args": [ + { + "name": "len", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "HashError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": false + }, + { + "name": "finalize_sha256", + "code": 3, + "args_size": 0, + "reply": "[u8; crate::SHA256_SZ]", + "reply_size": 32, + "error": "HashError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "digest_sha256", + "code": 4, + "args": [ + { + "name": "len", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "[u8; crate::SHA256_SZ]", + "reply_size": 32, + "error": "HashError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": false + } + ] + }, + { + "name": "DumpAgent", + "task": "dump_agent", + "task_id": 10, + "ops": [ + { + "name": "read_dump", + "code": 1, + "args": [ + { + "name": "index", + "ty": "u8" + }, + { + "name": "offset", + "ty": "u32" + } + ], + "args_size": 5, + "reply": "[u8; DUMP_READ_SIZE]", + "reply_size": 256, + "error": "DumpAgentError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "read_dump_into", + "code": 2, + "args": [ + { + "name": "index", + "ty": "u8" + }, + { + "name": "offset", + "ty": "u32" + } + ], + "args_size": 5, + "reply": "usize", + "reply_size": 4, + "error": "DumpAgentError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "out", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": false + }, + { + "name": "get_dump_area", + "code": 3, + "args": [ + { + "name": "index", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "DumpArea", + "reply_size": 10, + "error": "DumpAgentError", + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "initialize_dump", + "code": 4, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "DumpAgentError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "add_dump_segment", + "code": 5, + "args": [ + { + "name": "address", + "ty": "u32" + }, + { + "name": "length", + "ty": "u32" + } + ], + "args_size": 8, + "reply": "()", + "reply_size": 0, + "error": "DumpAgentError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "take_dump", + "code": 6, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "DumpAgentError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "dump_task", + "code": 7, + "args": [ + { + "name": "task_index", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "u8", + "reply_size": 1, + "error": "DumpAgentError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "dump_task_region", + "code": 8, + "args": [ + { + "name": "task_index", + "ty": "u32" + }, + { + "name": "start", + "ty": "u32" + }, + { + "name": "length", + "ty": "u32" + } + ], + "args_size": 12, + "reply": "u8", + "reply_size": 1, + "error": "DumpAgentError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "reinitialize_dump_from", + "code": 9, + "args": [ + { + "name": "index", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "DumpAgentError", + "encoding": "Hubpack", + "idempotent": false + } + ] + }, + { + "name": "SpRot", + "task": "sprot", + "task_id": 11, + "ops": [ + { + "name": "status", + "code": 1, + "args_size": 0, + "reply": "SprotStatus", + "reply_size": 24, + "error": "SprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "io_stats", + "code": 2, + "args_size": 0, + "reply": "SprotIoStats", + "reply_size": 68, + "error": "SprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "rot_state", + "code": 3, + "args_size": 0, + "reply": "RotState", + "reply_size": 138, + "error": "SprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "pulse_cs", + "code": 4, + "args": [ + { + "name": "delay", + "ty": "u16" + } + ], + "args_size": 2, + "reply": "PulseStatus", + "reply_size": 2, + "error": "SprotError", + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "block_size", + "code": 5, + "args_size": 0, + "reply": "u32", + "reply_size": 4, + "error": "SprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "prep_image_update", + "code": 6, + "args": [ + { + "name": "target", + "ty": "UpdateTarget" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "SprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "write_one_block", + "code": 7, + "args": [ + { + "name": "block_num", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "SprotError", + "encoding": "Hubpack", + "leases": [ + { + "name": "block", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": false + }, + { + "name": "abort_update", + "code": 8, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "SprotError", + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "finish_image_update", + "code": 9, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "SprotError", + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "switch_default_image", + "code": 10, + "args": [ + { + "name": "slot", + "ty": "SlotId" + }, + { + "name": "duration", + "ty": "SwitchDuration" + } + ], + "args_size": 2, + "reply": "()", + "reply_size": 0, + "error": "SprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "reset", + "code": 11, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "SprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "dump", + "code": 12, + "args": [ + { + "name": "address", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "DumpOrSprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "caboose_size", + "code": 13, + "args": [ + { + "name": "slot", + "ty": "SlotId" + } + ], + "args_size": 1, + "reply": "u32", + "reply_size": 4, + "error": "RawCabooseOrSprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "read_caboose_region", + "code": 14, + "args": [ + { + "name": "offset", + "ty": "u32" + }, + { + "name": "slot", + "ty": "SlotId" + } + ], + "args_size": 8, + "reply": "()", + "reply_size": 0, + "error": "RawCabooseOrSprotError", + "encoding": "Hubpack", + "leases": [ + { + "name": "out", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": true + }, + { + "name": "rot_boot_info", + "code": 15, + "args_size": 0, + "reply": "RotBootInfo", + "reply_size": 72, + "error": "SprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "cert_chain_len", + "code": 16, + "args_size": 0, + "reply": "u32", + "reply_size": 4, + "error": "AttestOrSprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "cert_len", + "code": 17, + "args": [ + { + "name": "index", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "u32", + "reply_size": 4, + "error": "AttestOrSprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "cert", + "code": 18, + "args": [ + { + "name": "index", + "ty": "u32" + }, + { + "name": "offset", + "ty": "u32" + } + ], + "args_size": 8, + "reply": "()", + "reply_size": 0, + "error": "AttestOrSprotError", + "encoding": "Hubpack", + "leases": [ + { + "name": "dest", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": true + }, + { + "name": "record", + "code": 19, + "args": [ + { + "name": "algorithm", + "ty": "HashAlgorithm" + } + ], + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "AttestOrSprotError", + "encoding": "Hubpack", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": false + }, + { + "name": "read_rot_page", + "code": 20, + "args": [ + { + "name": "page", + "ty": "RotPage" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "SprotError", + "encoding": "Hubpack", + "leases": [ + { + "name": "dest", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": true + }, + { + "name": "log", + "code": 21, + "args": [ + { + "name": "offset", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "AttestOrSprotError", + "encoding": "Hubpack", + "leases": [ + { + "name": "dest", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": false + }, + { + "name": "log_len", + "code": 22, + "args_size": 0, + "reply": "u32", + "reply_size": 4, + "error": "AttestOrSprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "attest", + "code": 23, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "AttestOrSprotError", + "encoding": "Hubpack", + "leases": [ + { + "name": "nonce", + "ty": "[u8]", + "read": true, + "write": false + }, + { + "name": "dest", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": false + }, + { + "name": "attest_len", + "code": 24, + "args_size": 0, + "reply": "u32", + "reply_size": 4, + "error": "AttestOrSprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "enable_sp_slot_watchdog", + "code": 25, + "args": [ + { + "name": "time_ms", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "SprotError", + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "disable_sp_slot_watchdog", + "code": 26, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "SprotError", + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "sp_slot_watchdog_supported", + "code": 27, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "SprotError", + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "versioned_rot_boot_info", + "code": 28, + "args": [ + { + "name": "version", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "VersionedRotBootInfo", + "reply_size": 147, + "error": "SprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "component_caboose_size", + "code": 29, + "args": [ + { + "name": "component", + "ty": "RotComponent" + }, + { + "name": "slot", + "ty": "SlotId" + } + ], + "args_size": 2, + "reply": "u32", + "reply_size": 4, + "error": "RawCabooseOrSprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "component_read_caboose_region", + "code": 30, + "args": [ + { + "name": "offset", + "ty": "u32" + }, + { + "name": "component", + "ty": "RotComponent" + }, + { + "name": "slot", + "ty": "SlotId" + } + ], + "args_size": 8, + "reply": "()", + "reply_size": 0, + "error": "RawCabooseOrSprotError", + "encoding": "Hubpack", + "leases": [ + { + "name": "out", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": true + }, + { + "name": "component_prep_image_update", + "code": 31, + "args": [ + { + "name": "component", + "ty": "RotComponent" + }, + { + "name": "slot", + "ty": "SlotId" + } + ], + "args_size": 2, + "reply": "()", + "reply_size": 0, + "error": "SprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "component_switch_default_image", + "code": 32, + "args": [ + { + "name": "component", + "ty": "RotComponent" + }, + { + "name": "slot", + "ty": "SlotId" + }, + { + "name": "duration", + "ty": "SwitchDuration" + } + ], + "args_size": 3, + "reply": "()", + "reply_size": 0, + "error": "SprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "lifecycle_state", + "code": 33, + "args_size": 0, + "reply": "LifecycleState", + "reply_size": 1, + "error": "StateOrSprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "tq_cert_chain_len", + "code": 34, + "args_size": 0, + "reply": "u32", + "reply_size": 4, + "error": "AttestOrSprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "tq_cert_len", + "code": 35, + "args": [ + { + "name": "index", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "u32", + "reply_size": 4, + "error": "AttestOrSprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "tq_cert", + "code": 36, + "args": [ + { + "name": "index", + "ty": "u32" + }, + { + "name": "offset", + "ty": "u32" + } + ], + "args_size": 8, + "reply": "()", + "reply_size": 0, + "error": "AttestOrSprotError", + "encoding": "Hubpack", + "leases": [ + { + "name": "dest", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": true + }, + { + "name": "tq_sign", + "code": 37, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "AttestOrSprotError", + "encoding": "Hubpack", + "leases": [ + { + "name": "tq", + "ty": "[u8]", + "read": true, + "write": false + }, + { + "name": "dest", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": false + }, + { + "name": "tq_sign_len", + "code": 38, + "args_size": 0, + "reply": "u32", + "reply_size": 4, + "error": "AttestOrSprotError", + "encoding": "Hubpack", + "idempotent": true + } + ] + }, + { + "name": "Sequencer", + "task": "grapefruit_seq", + "task_id": 12, + "ops": [ + { + "name": "get_state", + "code": 1, + "args_size": 0, + "reply": "drv_cpu_power_state::PowerState", + "reply_size": 1, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "set_state", + "code": 2, + "args": [ + { + "name": "state", + "ty": "drv_cpu_power_state::PowerState" + } + ], + "args_size": 1, + "reply": "drv_cpu_seq_api::Transition", + "reply_size": 1, + "error": "drv_cpu_seq_api::SeqError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "set_state_with_reason", + "code": 3, + "args": [ + { + "name": "state", + "ty": "drv_cpu_power_state::PowerState" + }, + { + "name": "reason", + "ty": "StateChangeReason" + } + ], + "args_size": 2, + "reply": "drv_cpu_seq_api::Transition", + "reply_size": 1, + "error": "drv_cpu_seq_api::SeqError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "send_hardware_nmi", + "code": 4, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "ServerDeath", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "read_fpga_regs", + "code": 5, + "args_size": 0, + "reply": "[u8; 64]", + "reply_size": 0, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "last_post_code", + "code": 6, + "args_size": 0, + "reply": "u32", + "reply_size": 4, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "post_code_buffer_len", + "code": 7, + "args_size": 0, + "reply": "u32", + "reply_size": 4, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "get_post_code", + "code": 8, + "args": [ + { + "name": "index", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "u32", + "reply_size": 4, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "gpio_edge_count", + "code": 9, + "args_size": 0, + "reply": "u32", + "reply_size": 4, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "gpio_cycle_count", + "code": 10, + "args_size": 0, + "reply": "u32", + "reply_size": 4, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "enable_console_redirect", + "code": 11, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "disable_console_redirect", + "code": 12, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", + "idempotent": true + } + ] + }, + { + "name": "Spartan7Loader", + "task": "spartan7_loader", + "task_id": 13, + "ops": [ + { + "name": "ping", + "code": 1, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", + "idempotent": true + } + ] + }, + { + "name": "Update", + "task": "update_server", + "task_id": 14, + "ops": [ + { + "name": "block_size", + "code": 1, + "args_size": 0, + "reply": "usize", + "reply_size": 4, + "error": "drv_update_api::UpdateError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "prep_image_update", + "code": 2, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "drv_update_api::UpdateError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "write_one_block", + "code": 3, + "args": [ + { + "name": "block_num", + "ty": "usize" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "drv_update_api::UpdateError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "block", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": false + }, + { + "name": "abort_update", + "code": 4, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "drv_update_api::UpdateError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "finish_image_update", + "code": 5, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "drv_update_api::UpdateError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "current_version", + "code": 6, + "args_size": 0, + "reply": "ImageVersion", + "reply_size": 8, + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "read_caboose_value", + "code": 7, + "args": [ + { + "name": "name", + "ty": "[u8; 4]" + } + ], + "args_size": 4, + "reply": "u32", + "reply_size": 4, + "error": "CabooseError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": true + }, + { + "name": "get_pending_boot_slot", + "code": 8, + "args_size": 0, + "reply": "SlotId", + "reply_size": 1, + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "set_pending_boot_slot", + "code": 9, + "args": [ + { + "name": "slot", + "ty": "SlotId" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "drv_update_api::UpdateError", + "encoding": "Hubpack", + "idempotent": false + } + ] + }, + { + "name": "Sensor", + "task": "sensor", + "task_id": 15, + "ops": [ + { + "name": "get", + "code": 1, + "args": [ + { + "name": "id", + "ty": "SensorId" + } + ], + "args_size": 4, + "reply": "f32", + "reply_size": 4, + "error": "SensorError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "get_reading", + "code": 2, + "args": [ + { + "name": "id", + "ty": "SensorId" + } + ], + "args_size": 4, + "reply": "Reading", + "reply_size": 12, + "error": "SensorError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "get_raw_reading", + "code": 3, + "args": [ + { + "name": "id", + "ty": "SensorId" + } + ], + "args_size": 4, + "reply": "Option<(Result, u64)>", + "reply_size": 0, + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "get_last_data", + "code": 4, + "args": [ + { + "name": "id", + "ty": "SensorId" + } + ], + "args_size": 4, + "reply": "Option<(f32, u64)>", + "reply_size": 13, + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "get_last_nodata", + "code": 5, + "args": [ + { + "name": "id", + "ty": "SensorId" + } + ], + "args_size": 4, + "reply": "Option<(NoData, u64)>", + "reply_size": 0, + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "get_min", + "code": 6, + "args": [ + { + "name": "id", + "ty": "SensorId" + } + ], + "args_size": 4, + "reply": "(f32, u64)", + "reply_size": 12, + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "get_max", + "code": 7, + "args": [ + { + "name": "id", + "ty": "SensorId" + } + ], + "args_size": 4, + "reply": "(f32, u64)", + "reply_size": 12, + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "post", + "code": 8, + "args": [ + { + "name": "id", + "ty": "SensorId" + }, + { + "name": "value", + "ty": "f32" + }, + { + "name": "timestamp", + "ty": "u64" + } + ], + "args_size": 16, + "reply": "()", + "reply_size": 0, + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "nodata", + "code": 9, + "args": [ + { + "name": "id", + "ty": "SensorId" + }, + { + "name": "nodata", + "ty": "NoData" + }, + { + "name": "timestamp", + "ty": "u64" + } + ], + "args_size": 16, + "reply": "()", + "reply_size": 0, + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "get_nerrors", + "code": 10, + "args": [ + { + "name": "id", + "ty": "SensorId" + } + ], + "args_size": 4, + "reply": "u32", + "reply_size": 4, + "encoding": "Hubpack", + "idempotent": true + } + ] + }, + { + "name": "HostSpComms", + "task": "host_sp_comms", + "task_id": 16, + "ops": [ + { + "name": "set_status", + "code": 1, + "args": [ + { + "name": "status", + "ty": "u64" + } + ], + "args_size": 8, + "reply": "()", + "reply_size": 0, + "error": "HostSpCommsError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "get_status", + "code": 2, + "args_size": 0, + "reply": "Status", + "reply_size": 8, + "error": "HostSpCommsError", + "encoding": "Zerocopy", + "idempotent": false + } + ] + }, + { + "name": "ControlPlaneAgent", + "task": "control_plane_agent", + "task_id": 17, + "ops": [ + { + "name": "fetch_host_phase2_data", + "code": 1, + "args": [ + { + "name": "image_hash", + "ty": "[u8; 32]" + }, + { + "name": "offset", + "ty": "u64" + }, + { + "name": "notification_bit", + "ty": "u8" + } + ], + "args_size": 41, + "reply": "()", + "reply_size": 0, + "error": "ControlPlaneAgentError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "get_host_phase2_data", + "code": 2, + "args": [ + { + "name": "image_hash", + "ty": "[u8; 32]" + }, + { + "name": "offset", + "ty": "u64" + } + ], + "args_size": 40, + "reply": "usize", + "reply_size": 4, + "error": "ControlPlaneAgentError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": false + }, + { + "name": "get_startup_options", + "code": 3, + "args_size": 0, + "reply": "HostStartupOptions", + "reply_size": 8, + "error": "ControlPlaneAgentError", + "encoding": "Ssmarshal", + "idempotent": false + }, + { + "name": "set_startup_options", + "code": 4, + "args": [ + { + "name": "startup_options", + "ty": "u64" + } + ], + "args_size": 8, + "reply": "()", + "reply_size": 0, + "error": "ControlPlaneAgentError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "identity", + "code": 5, + "args_size": 0, + "reply": "OxideIdentity", + "reply_size": 26, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "get_uart_client", + "code": 6, + "args_size": 0, + "reply": "UartClient", + "reply_size": 1, + "encoding": "Ssmarshal", + "idempotent": true + }, + { + "name": "get_installinator_image_id", + "code": 7, + "args_size": 0, + "reply": "usize", + "reply_size": 4, + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": true + }, + { + "name": "set_humility_uart_client", + "code": 8, + "args": [ + { + "name": "attach", + "ty": "bool" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "ControlPlaneAgentError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "uart_read", + "code": 9, + "args_size": 0, + "reply": "usize", + "reply_size": 4, + "error": "ControlPlaneAgentError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": false + }, + { + "name": "uart_write", + "code": 10, + "args_size": 0, + "reply": "usize", + "reply_size": 4, + "error": "ControlPlaneAgentError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": false + } + ] + }, + { + "name": "AuxFlash", + "task": "auxflash", + "task_id": 18, + "ops": [ + { + "name": "read_id", + "code": 1, + "args_size": 0, + "reply": "AuxFlashId", + "reply_size": 11, + "error": "AuxFlashError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "slot_count", + "code": 2, + "args_size": 0, + "reply": "u32", + "reply_size": 4, + "error": "AuxFlashError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "slot_size", + "code": 3, + "args_size": 0, + "reply": "u32", + "reply_size": 4, + "error": "AuxFlashError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "read_status", + "code": 4, + "args_size": 0, + "reply": "u8", + "reply_size": 1, + "error": "AuxFlashError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "erase_slot", + "code": 5, + "args": [ + { + "name": "slot", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "AuxFlashError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "slot_sector_erase", + "code": 6, + "args": [ + { + "name": "slot", + "ty": "u32" + }, + { + "name": "offset", + "ty": "u32" + } + ], + "args_size": 8, + "reply": "()", + "reply_size": 0, + "error": "AuxFlashError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "read_slot_chck", + "code": 7, + "args": [ + { + "name": "slot", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "AuxFlashChecksum", + "reply_size": 32, + "error": "AuxFlashError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "write_slot_with_offset", + "code": 8, + "args": [ + { + "name": "slot", + "ty": "u32" + }, + { + "name": "offset", + "ty": "u32" + } + ], + "args_size": 8, + "reply": "()", + "reply_size": 0, + "error": "AuxFlashError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": false + }, + { + "name": "read_slot_with_offset", + "code": 9, + "args": [ + { + "name": "slot", + "ty": "u32" + }, + { + "name": "offset", + "ty": "u32" + } + ], + "args_size": 8, + "reply": "()", + "reply_size": 0, + "error": "AuxFlashError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": false + }, + { + "name": "scan_and_get_active_slot", + "code": 10, + "args_size": 0, + "reply": "u32", + "reply_size": 4, + "error": "AuxFlashError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "get_active_slot", + "code": 11, + "args_size": 0, + "reply": "u32", + "reply_size": 4, + "error": "AuxFlashError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "ensure_redundancy", + "code": 12, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "AuxFlashError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "get_blob_by_tag", + "code": 13, + "args": [ + { + "name": "name", + "ty": "[u8; 4]" + } + ], + "args_size": 4, + "reply": "AuxFlashBlob", + "reply_size": 12, + "error": "AuxFlashError", + "encoding": "Zerocopy", + "idempotent": false + } + ] + }, + { + "name": "Net", + "task": "net", + "task_id": 19, + "ops": [ + { + "name": "recv_packet", + "code": 1, + "args": [ + { + "name": "socket", + "ty": "SocketName" + }, + { + "name": "large_payload_behavior", + "ty": "LargePayloadBehavior" + } + ], + "args_size": 8, + "reply": "UdpMetadata", + "reply_size": 24, + "error": "task_net_api::RecvError", + "encoding": "Hubpack", + "leases": [ + { + "name": "payload", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": false + }, + { + "name": "send_packet", + "code": 2, + "args": [ + { + "name": "socket", + "ty": "SocketName" + }, + { + "name": "metadata", + "ty": "UdpMetadata" + } + ], + "args_size": 28, + "reply": "()", + "reply_size": 0, + "error": "task_net_api::SendError", + "encoding": "Hubpack", + "leases": [ + { + "name": "payload", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": false + }, + { + "name": "smi_read", + "code": 3, + "args": [ + { + "name": "phy", + "ty": "u8" + }, + { + "name": "register", + "ty": "u8" + } + ], + "args_size": 2, + "reply": "u16", + "reply_size": 2, + "error": "ServerDeath", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "smi_write", + "code": 4, + "args": [ + { + "name": "phy", + "ty": "u8" + }, + { + "name": "register", + "ty": "u8" + }, + { + "name": "value", + "ty": "u16" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "ServerDeath", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "read_phy_reg", + "code": 5, + "args": [ + { + "name": "port", + "ty": "u8" + }, + { + "name": "page", + "ty": "u16" + }, + { + "name": "reg", + "ty": "u8" + } + ], + "args_size": 4, + "reply": "u16", + "reply_size": 2, + "error": "PhyError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "write_phy_reg", + "code": 6, + "args": [ + { + "name": "port", + "ty": "u8" + }, + { + "name": "page", + "ty": "u16" + }, + { + "name": "reg", + "ty": "u8" + }, + { + "name": "value", + "ty": "u16" + } + ], + "args_size": 6, + "reply": "()", + "reply_size": 0, + "error": "PhyError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "read_ksz8463_mac_count", + "code": 7, + "args_size": 0, + "reply": "usize", + "reply_size": 4, + "error": "KszError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "read_ksz8463_mac", + "code": 8, + "args": [ + { + "name": "i", + "ty": "u16" + } + ], + "args_size": 2, + "reply": "KszMacTableEntry", + "reply_size": 8, + "error": "KszError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "read_ksz8463_reg", + "code": 9, + "args": [ + { + "name": "reg", + "ty": "u16" + } + ], + "args_size": 2, + "reply": "u16", + "reply_size": 2, + "error": "KszError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "get_mac_address", + "code": 10, + "args_size": 0, + "reply": "MacAddress", + "reply_size": 6, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "get_spare_mac_addresses", + "code": 11, + "args_size": 0, + "reply": "MacAddressBlock", + "reply_size": 9, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "management_link_status", + "code": 12, + "args_size": 0, + "reply": "ManagementLinkStatus", + "reply_size": 6, + "error": "MgmtError", + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "management_counters", + "code": 13, + "args_size": 0, + "reply": "ManagementCounters", + "reply_size": 105, + "error": "MgmtError", + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "trust_vlan", + "code": 14, + "args": [ + { + "name": "vid", + "ty": "VLanId" + }, + { + "name": "trust_until", + "ty": "u64" + } + ], + "args_size": 16, + "reply": "()", + "reply_size": 0, + "error": "task_net_api::TrustError", + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "distrust_vlan", + "code": 15, + "args": [ + { + "name": "vid", + "ty": "VLanId" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "task_net_api::TrustError", + "encoding": "Hubpack", + "idempotent": false + } + ] + }, + { + "name": "FmcDemo", + "task": "fmc_demo", + "task_id": 23, + "ops": [ + { + "name": "peek16", + "code": 1, + "args": [ + { + "name": "addr", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "u16", + "reply_size": 2, + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "peek32", + "code": 2, + "args": [ + { + "name": "addr", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "u32", + "reply_size": 4, + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "peek64", + "code": 3, + "args": [ + { + "name": "addr", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "u64", + "reply_size": 8, + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "poke16", + "code": 4, + "args": [ + { + "name": "addr", + "ty": "u32" + }, + { + "name": "value", + "ty": "u16" + } + ], + "args_size": 8, + "reply": "()", + "reply_size": 0, + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "poke32", + "code": 5, + "args": [ + { + "name": "addr", + "ty": "u32" + }, + { + "name": "value", + "ty": "u32" + } + ], + "args_size": 8, + "reply": "()", + "reply_size": 0, + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "poke64", + "code": 6, + "args": [ + { + "name": "addr", + "ty": "u32" + }, + { + "name": "value", + "ty": "u64" + } + ], + "args_size": 16, + "reply": "()", + "reply_size": 0, + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "set_burst_enable", + "code": 7, + "args": [ + { + "name": "flag", + "ty": "bool" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "set_write_enable", + "code": 8, + "args": [ + { + "name": "flag", + "ty": "bool" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "set_write_fifo", + "code": 9, + "args": [ + { + "name": "flag", + "ty": "bool" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "set_wait", + "code": 10, + "args": [ + { + "name": "flag", + "ty": "bool" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "set_data_latency_cycles", + "code": 11, + "args": [ + { + "name": "n", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "set_clock_divider", + "code": 12, + "args": [ + { + "name": "n", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "set_bus_turnaround_cycles", + "code": 13, + "args": [ + { + "name": "n", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "encoding": "Hubpack", + "idempotent": false + } + ] + }, + { + "name": "HostFlash", + "task": "hf", + "task_id": 24, + "ops": [ + { + "name": "read_id", + "code": 1, + "args_size": 0, + "reply": "HfChipId", + "reply_size": 20, + "error": "HfError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "capacity", + "code": 2, + "args_size": 0, + "reply": "usize", + "reply_size": 4, + "error": "HfError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "read_status", + "code": 3, + "args_size": 0, + "reply": "u8", + "reply_size": 1, + "error": "HfError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "bulk_erase", + "code": 4, + "args": [ + { + "name": "protect", + "ty": "HfProtectMode" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "HfError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "page_program", + "code": 5, + "args": [ + { + "name": "address", + "ty": "u32" + }, + { + "name": "protect", + "ty": "HfProtectMode" + } + ], + "args_size": 5, + "reply": "()", + "reply_size": 0, + "error": "HfError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": false + }, + { + "name": "page_program_dev", + "code": 6, + "args": [ + { + "name": "dev", + "ty": "HfDevSelect" + }, + { + "name": "address", + "ty": "u32" + }, + { + "name": "protect", + "ty": "HfProtectMode" + } + ], + "args_size": 6, + "reply": "()", + "reply_size": 0, + "error": "HfError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": false + }, + { + "name": "read", + "code": 7, + "args": [ + { + "name": "address", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "HfError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": false + }, + { + "name": "read_dev", + "code": 8, + "args": [ + { + "name": "dev", + "ty": "HfDevSelect" + }, + { + "name": "address", + "ty": "u32" + } + ], + "args_size": 5, + "reply": "()", + "reply_size": 0, + "error": "HfError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": false + }, + { + "name": "sector_erase", + "code": 9, + "args": [ + { + "name": "address", + "ty": "u32" + }, + { + "name": "protect", + "ty": "HfProtectMode" + } + ], + "args_size": 5, + "reply": "()", + "reply_size": 0, + "error": "HfError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "sector_erase_dev", + "code": 10, + "args": [ + { + "name": "dev", + "ty": "HfDevSelect" + }, + { + "name": "address", + "ty": "u32" + }, + { + "name": "protect", + "ty": "HfProtectMode" + } + ], + "args_size": 6, + "reply": "()", + "reply_size": 0, + "error": "HfError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "get_mux", + "code": 11, + "args_size": 0, + "reply": "HfMuxState", + "reply_size": 1, + "error": "HfError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "set_mux", + "code": 12, + "args": [ + { + "name": "state", + "ty": "HfMuxState" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "HfError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "get_dev", + "code": 13, + "args_size": 0, + "reply": "HfDevSelect", + "reply_size": 1, + "error": "HfError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "set_dev", + "code": 14, + "args": [ + { + "name": "dev", + "ty": "HfDevSelect" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "HfError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "check_dev", + "code": 15, + "args": [ + { + "name": "dev", + "ty": "HfDevSelect" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "HfError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "hash", + "code": 16, + "args": [ + { + "name": "address", + "ty": "u32" + }, + { + "name": "len", + "ty": "u32" + } + ], + "args_size": 8, + "reply": "[u8; drv_hash_api::SHA256_SZ]", + "reply_size": 32, + "error": "HfError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "hash_significant_bits", + "code": 17, + "args": [ + { + "name": "dev", + "ty": "HfDevSelect" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "HfError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "get_cached_hash", + "code": 18, + "args": [ + { + "name": "dev", + "ty": "HfDevSelect" + } + ], + "args_size": 1, + "reply": "[u8; drv_hash_api::SHA256_SZ]", + "reply_size": 32, + "error": "HfError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "get_persistent_data", + "code": 19, + "args_size": 0, + "reply": "HfPersistentData", + "reply_size": 1, + "error": "HfError", + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "write_persistent_data", + "code": 20, + "args": [ + { + "name": "dev_select", + "ty": "HfDevSelect" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "HfError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "apob_begin", + "code": 21, + "args": [ + { + "name": "length", + "ty": "u32" + }, + { + "name": "algorithm", + "ty": "ApobHash" + } + ], + "args_size": 40, + "reply": "()", + "reply_size": 0, + "error": "ApobBeginError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "apob_write", + "code": 22, + "args": [ + { + "name": "offset", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "ApobWriteError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": true + }, + { + "name": "apob_commit", + "code": 23, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "ApobCommitError", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "apob_lock", + "code": 24, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "apob_read", + "code": 25, + "args": [ + { + "name": "offset", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "usize", + "reply_size": 4, + "error": "ApobReadError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": true + }, + { + "name": "apob_clear", + "code": 26, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "ApobClearError", + "encoding": "Zerocopy", + "idempotent": true + } + ] + }, + { + "name": "Rng", + "task": "rng_driver", + "task_id": 26, + "ops": [ + { + "name": "fill", + "code": 1, + "args_size": 0, + "reply": "usize", + "reply_size": 4, + "error": "RngError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "source", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": false + } + ] + }, + { + "name": "Ereportulator", + "task": "ereportulator", + "task_id": 27, + "ops": [ + { + "name": "fake_ereport", + "code": 1, + "args": [ + { + "name": "n", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "ae35_fault", + "code": 2, + "args": [ + { + "name": "n", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "houston_we_have_a_problem", + "code": 3, + "args": [ + { + "name": "n", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "set_fake_vpd", + "code": 4, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "task_packrat_api::CacheSetError", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "panicme", + "code": 5, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", + "idempotent": true + } + ] + } + ] +} \ No newline at end of file diff --git a/humility-hif-assembler/fixtures/sidecar-b.json b/humility-hif-assembler/fixtures/sidecar-b.json new file mode 100644 index 000000000..e94c568f8 --- /dev/null +++ b/humility-hif-assembler/fixtures/sidecar-b.json @@ -0,0 +1,6302 @@ +{ + "image_id": [ + 43, + 84, + 64, + 254, + 22, + 204, + 10, + 159 + ], + "board": "sidecar-b", + "buses": [ + { + "name": "northeast0", + "controller": 1, + "port_index": 0, + "port_name": "B1", + "devices": [ + { + "address": 16, + "device": "adm127x", + "description": "Fan 1 hot swap controller", + "removable": false, + "sensors": [ + { + "name": "V54_FAN1", + "kind": "Temperature" + }, + { + "name": "V54_FAN1", + "kind": "Current" + }, + { + "name": "V54_FAN1", + "kind": "Voltage" + } + ] + }, + { + "address": 35, + "device": "max31790", + "name": "East", + "description": "Fan 0/1 controller", + "removable": false, + "sensors": [ + { + "name": "ESE_fan0", + "kind": "Speed" + }, + { + "name": "ENE_fan0", + "kind": "Speed" + }, + { + "name": "SE_fan1", + "kind": "Speed" + }, + { + "name": "NE_fan1", + "kind": "Speed" + } + ] + }, + { + "address": 73, + "device": "tmp117", + "name": "NNE", + "description": "North-northeast temperature sensor", + "removable": true, + "sensors": [ + { + "name": "NNE", + "kind": "Temperature" + } + ] + }, + { + "address": 99, + "device": "raa229618", + "description": "TF2 VDD rail", + "removable": false, + "sensors": [ + { + "name": "V0P8_TF2_VDD_CORE", + "kind": "Temperature" + }, + { + "name": "V0P8_TF2_VDD_CORE", + "kind": "Current" + }, + { + "name": "V0P8_TF2_VDD_CORE", + "kind": "Voltage" + } + ] + }, + { + "address": 112, + "device": "pca9545", + "description": "Northeast fan mux", + "removable": false + } + ], + "muxes": [ + { + "address": 1, + "segments": [ + { + "segment": 1, + "devices": [ + { + "address": 80, + "device": "at24csw080", + "description": "Fan 1 FRUID", + "removable": true + } + ] + }, + { + "segment": 2, + "devices": [ + { + "address": 80, + "device": "at24csw080", + "description": "Fan 0 FRUID", + "removable": true + } + ] + } + ] + } + ] + }, + { + "name": "northeast1", + "controller": 1, + "port_index": 1, + "port_name": "B2", + "devices": [ + { + "address": 19, + "device": "adm127x", + "description": "Fan 0 hot swap controller", + "removable": false, + "sensors": [ + { + "name": "V54_FAN0", + "kind": "Temperature" + }, + { + "name": "V54_FAN0", + "kind": "Current" + }, + { + "name": "V54_FAN0", + "kind": "Voltage" + } + ] + }, + { + "address": 26, + "device": "tps546b24a", + "description": "V3P3_SYS rail", + "removable": false, + "sensors": [ + { + "name": "V3P3_SYS", + "kind": "Temperature" + }, + { + "name": "V3P3_SYS", + "kind": "Current" + }, + { + "name": "V3P3_SYS", + "kind": "Voltage" + } + ] + }, + { + "address": 72, + "device": "tmp117", + "name": "Northeast", + "description": "Northeast temperature sensor", + "removable": true, + "sensors": [ + { + "name": "Northeast", + "kind": "Temperature" + } + ] + } + ] + }, + { + "name": "front_io", + "controller": 2, + "port_index": 0, + "port_name": "F", + "devices": [ + { + "address": 80, + "device": "at24csw080", + "description": "Front IO board FRUID", + "removable": true + }, + { + "address": 115, + "device": "pca9538", + "description": "Front IO GPIO expander", + "removable": true + }, + { + "address": 10, + "device": "pca9956b", + "name": "front_leds_left", + "description": "Front IO LED driver (left)", + "removable": true + }, + { + "address": 11, + "device": "pca9956b", + "name": "front_leds_right", + "description": "Front IO LED driver (right)", + "removable": true + }, + { + "address": 27, + "device": "tps546b24a", + "description": "Front IO V3P3_SYS_A2 rail", + "removable": true, + "sensors": [ + { + "name": "V3P3_SYS_A2", + "kind": "Temperature" + }, + { + "name": "V3P3_SYS_A2", + "kind": "Current" + }, + { + "name": "V3P3_SYS_A2", + "kind": "Voltage" + } + ] + }, + { + "address": 25, + "device": "tps546b24a", + "description": "Front IO V3P3_QSFP0_A0 rail", + "removable": true, + "sensors": [ + { + "name": "V3P3_QSFP0_A0", + "kind": "Temperature" + }, + { + "name": "V3P3_QSFP0_A0", + "kind": "Current" + }, + { + "name": "V3P3_QSFP0_A0", + "kind": "Voltage" + } + ] + }, + { + "address": 26, + "device": "tps546b24a", + "description": "Front IO V3P3_QSFP1_A0 rail", + "removable": true, + "sensors": [ + { + "name": "V3P3_QSFP1_A0", + "kind": "Temperature" + }, + { + "name": "V3P3_QSFP1_A0", + "kind": "Current" + }, + { + "name": "V3P3_QSFP1_A0", + "kind": "Voltage" + } + ] + } + ] + }, + { + "name": "frontgps", + "controller": 2, + "port_index": 1, + "port_name": "H" + }, + { + "name": "northwest0", + "controller": 3, + "port_index": 0, + "port_name": "C", + "devices": [ + { + "address": 22, + "device": "adm127x", + "description": "54V hot swap controller", + "removable": false, + "sensors": [ + { + "name": "V54_HSC", + "kind": "Temperature" + }, + { + "name": "V54_HSC", + "kind": "Current" + }, + { + "name": "V54_HSC", + "kind": "Voltage" + } + ] + }, + { + "address": 25, + "device": "tps546b24a", + "description": "V5P0_SYS rail", + "removable": false, + "sensors": [ + { + "name": "V5P0_SYS", + "kind": "Temperature" + }, + { + "name": "V5P0_SYS", + "kind": "Current" + }, + { + "name": "V5P0_SYS", + "kind": "Voltage" + } + ] + }, + { + "address": 72, + "device": "tmp117", + "name": "NNW", + "description": "North-northwest temperature sensor", + "removable": true, + "sensors": [ + { + "name": "NNW", + "kind": "Temperature" + } + ] + }, + { + "address": 76, + "device": "tmp451", + "name": "tf2", + "description": "TF2 temperature sensor", + "removable": false, + "sensors": [ + { + "name": "tf2", + "kind": "Temperature" + } + ] + }, + { + "address": 96, + "device": "raa229618", + "description": "TF2 VDDA rail", + "removable": false, + "sensors": [ + { + "name": "V0P9_TF2_VDDT", + "kind": "Temperature" + }, + { + "name": "V1P5_TF2_VDDA", + "kind": "Temperature" + }, + { + "name": "V0P9_TF2_VDDT", + "kind": "Current" + }, + { + "name": "V1P5_TF2_VDDA", + "kind": "Current" + }, + { + "name": "V0P9_TF2_VDDT", + "kind": "Voltage" + }, + { + "name": "V1P5_TF2_VDDA", + "kind": "Voltage" + } + ] + }, + { + "address": 103, + "device": "bmr491", + "name": "IBC", + "description": "Intermediate bus converter", + "removable": false, + "sensors": [ + { + "name": "V12P0_SYS", + "kind": "Temperature" + }, + { + "name": "V12P0_SYS", + "kind": "Power" + }, + { + "name": "V12P0_SYS", + "kind": "Current" + }, + { + "name": "V12P0_SYS", + "kind": "Voltage" + } + ] + } + ] + }, + { + "name": "northwest1", + "controller": 3, + "port_index": 1, + "port_name": "H", + "devices": [ + { + "address": 19, + "device": "adm127x", + "description": "Fan 2 hot swap controller", + "removable": false, + "sensors": [ + { + "name": "V54_FAN2", + "kind": "Temperature" + }, + { + "name": "V54_FAN2", + "kind": "Current" + }, + { + "name": "V54_FAN2", + "kind": "Voltage" + } + ] + }, + { + "address": 16, + "device": "adm127x", + "description": "Fan 3 hot swap controller", + "removable": false, + "sensors": [ + { + "name": "V54_FAN3", + "kind": "Temperature" + }, + { + "name": "V54_FAN3", + "kind": "Current" + }, + { + "name": "V54_FAN3", + "kind": "Voltage" + } + ] + }, + { + "address": 73, + "device": "tmp117", + "name": "Northwest", + "description": "Northwest temperature sensor", + "removable": true, + "sensors": [ + { + "name": "Northwest", + "kind": "Temperature" + } + ] + }, + { + "address": 32, + "device": "max31790", + "name": "West", + "description": "Fan 2/3 controller", + "removable": false, + "sensors": [ + { + "name": "SW_fan2", + "kind": "Speed" + }, + { + "name": "NW_fan2", + "kind": "Speed" + }, + { + "name": "WSW_fan3", + "kind": "Speed" + }, + { + "name": "WNW_fan3", + "kind": "Speed" + } + ] + }, + { + "address": 112, + "device": "pca9545", + "description": "Northwest fan mux", + "removable": false + } + ], + "muxes": [ + { + "address": 1, + "segments": [ + { + "segment": 1, + "devices": [ + { + "address": 80, + "device": "at24csw080", + "description": "Fan 3 FRUID", + "removable": true + } + ] + }, + { + "segment": 2, + "devices": [ + { + "address": 80, + "device": "at24csw080", + "description": "Fan 2 FRUID", + "removable": true + } + ] + } + ] + } + ] + }, + { + "name": "south2", + "controller": 4, + "port_index": 0, + "port_name": "D", + "devices": [ + { + "address": 80, + "device": "at24csw080", + "name": "local_vpd", + "description": "Mainboard FRUID", + "removable": false + } + ] + }, + { + "name": "south0", + "controller": 4, + "port_index": 1, + "port_name": "F", + "devices": [ + { + "address": 98, + "device": "isl68224", + "description": "VDD[A]18 rail", + "removable": false, + "sensors": [ + { + "name": "V1P8_TF2_VDD", + "kind": "Temperature" + }, + { + "name": "V1P8_TF2_VDDA", + "kind": "Temperature" + }, + { + "name": "V1P8_TF2_VDD", + "kind": "Current" + }, + { + "name": "V1P8_TF2_VDDA", + "kind": "Current" + }, + { + "name": "V1P8_TF2_VDD", + "kind": "Voltage" + }, + { + "name": "V1P8_TF2_VDDA", + "kind": "Voltage" + } + ] + }, + { + "address": 84, + "device": "ltc4282", + "description": "Front I/O hotswap controller", + "removable": false, + "sensors": [ + { + "name": "V12P0_FRONT_IO", + "kind": "Current" + }, + { + "name": "V12P0_FRONT_IO", + "kind": "Voltage" + } + ] + }, + { + "address": 88, + "device": "idt8a34001", + "description": "Clock generator", + "removable": false + }, + { + "address": 74, + "device": "tmp117", + "name": "South", + "description": "South temperature sensor", + "removable": true, + "sensors": [ + { + "name": "South", + "kind": "Temperature" + } + ] + }, + { + "address": 72, + "device": "tmp117", + "name": "Southeast", + "description": "Southeast temperature sensor", + "removable": true, + "sensors": [ + { + "name": "Southeast", + "kind": "Temperature" + } + ] + }, + { + "address": 73, + "device": "tmp117", + "name": "Southwest", + "description": "Southwest temperature sensor", + "removable": true, + "sensors": [ + { + "name": "Southwest", + "kind": "Temperature" + } + ] + } + ] + }, + { + "name": "south1", + "controller": 4, + "port_index": 2, + "port_name": "H", + "devices": [ + { + "address": 27, + "device": "tps546b24a", + "description": "V1P0_MGMT rail", + "removable": false, + "sensors": [ + { + "name": "V1P0_MGMT", + "kind": "Temperature" + }, + { + "name": "V1P0_MGMT", + "kind": "Current" + }, + { + "name": "V1P0_MGMT", + "kind": "Voltage" + } + ] + }, + { + "address": 28, + "device": "tps546b24a", + "description": "V1P8_SYS rail", + "removable": false, + "sensors": [ + { + "name": "V1P8_SYS", + "kind": "Temperature" + }, + { + "name": "V1P8_SYS", + "kind": "Current" + }, + { + "name": "V1P8_SYS", + "kind": "Voltage" + } + ] + }, + { + "address": 76, + "device": "tmp451", + "name": "vsc7448", + "description": "VSC7448 temperature sensor", + "removable": false, + "sensors": [ + { + "name": "vsc7448", + "kind": "Temperature" + } + ] + } + ] + } + ], + "functions": [ + { + "name": "Sleep", + "id": 0, + "arg_count": 1, + "args": [ + { + "name": "arg0", + "ty": "u16", + "size": 2 + } + ] + }, + { + "name": "Send", + "id": 1, + "arg_count": 4, + "args": [ + { + "name": "__0", + "ty": "Task", + "size": 1 + }, + { + "name": "__1", + "ty": "u16", + "size": 2 + }, + { + "name": "__2", + "ty": "Buffer", + "size": 1 + }, + { + "name": "__3", + "ty": "u32", + "size": 4 + } + ] + }, + { + "name": "SendLeaseRead", + "id": 2, + "arg_count": 5, + "args": [ + { + "name": "__0", + "ty": "Task", + "size": 1 + }, + { + "name": "__1", + "ty": "u16", + "size": 2 + }, + { + "name": "__2", + "ty": "Buffer", + "size": 1 + }, + { + "name": "__3", + "ty": "u32", + "size": 4 + }, + { + "name": "__4", + "ty": "u32", + "size": 4 + } + ] + }, + { + "name": "SendLeaseReadWrite", + "id": 3, + "arg_count": 6, + "args": [ + { + "name": "__0", + "ty": "Task", + "size": 1 + }, + { + "name": "__1", + "ty": "u16", + "size": 2 + }, + { + "name": "__2", + "ty": "Buffer", + "size": 1 + }, + { + "name": "__3", + "ty": "u32", + "size": 4 + }, + { + "name": "__4", + "ty": "u32", + "size": 4 + }, + { + "name": "__5", + "ty": "u32", + "size": 4 + } + ] + }, + { + "name": "SendLeaseWrite", + "id": 4, + "arg_count": 5, + "args": [ + { + "name": "__0", + "ty": "Task", + "size": 1 + }, + { + "name": "__1", + "ty": "u16", + "size": 2 + }, + { + "name": "__2", + "ty": "Buffer", + "size": 1 + }, + { + "name": "__3", + "ty": "u32", + "size": 4 + }, + { + "name": "__4", + "ty": "u32", + "size": 4 + } + ] + }, + { + "name": "I2cRead", + "id": 5, + "arg_count": 7, + "args": [ + { + "name": "__0", + "ty": "Controller", + "size": 1 + }, + { + "name": "__1", + "ty": "PortIndex", + "size": 1 + }, + { + "name": "__2", + "ty": "Mux", + "size": 1 + }, + { + "name": "__3", + "ty": "Segment", + "size": 1 + }, + { + "name": "__4", + "ty": "u8", + "size": 1 + }, + { + "name": "__5", + "ty": "u8", + "size": 1 + }, + { + "name": "__6", + "ty": "u32", + "size": 4 + } + ], + "errors": [ + { + "code": 1, + "name": "BadResponse" + }, + { + "code": 2, + "name": "BadArg" + }, + { + "code": 3, + "name": "NoDevice" + }, + { + "code": 4, + "name": "BadController" + }, + { + "code": 5, + "name": "ReservedAddress" + }, + { + "code": 6, + "name": "BadPort" + }, + { + "code": 7, + "name": "NoRegister" + }, + { + "code": 8, + "name": "BadMux" + }, + { + "code": 9, + "name": "BadSegment" + }, + { + "code": 10, + "name": "MuxNotFound" + }, + { + "code": 11, + "name": "SegmentNotFound" + }, + { + "code": 12, + "name": "SegmentDisconnected" + }, + { + "code": 13, + "name": "MuxDisconnected" + }, + { + "code": 14, + "name": "MuxMissing" + }, + { + "code": 15, + "name": "BadMuxRegister" + }, + { + "code": 16, + "name": "BusReset" + }, + { + "code": 17, + "name": "BusResetMux" + }, + { + "code": 18, + "name": "BusLocked" + }, + { + "code": 19, + "name": "BusLockedMux" + }, + { + "code": 20, + "name": "ControllerBusy" + }, + { + "code": 21, + "name": "BusError" + }, + { + "code": 22, + "name": "BadDeviceState" + }, + { + "code": 23, + "name": "OperationNotSupported" + }, + { + "code": 24, + "name": "IllegalLeaseCount" + }, + { + "code": 25, + "name": "TooMuchData" + } + ] + }, + { + "name": "I2cWrite", + "id": 6, + "arg_count": 8, + "args": [ + { + "name": "__0", + "ty": "Controller", + "size": 1 + }, + { + "name": "__1", + "ty": "PortIndex", + "size": 1 + }, + { + "name": "__2", + "ty": "Mux", + "size": 1 + }, + { + "name": "__3", + "ty": "Segment", + "size": 1 + }, + { + "name": "__4", + "ty": "u8", + "size": 1 + }, + { + "name": "__5", + "ty": "u8", + "size": 1 + }, + { + "name": "__6", + "ty": "Buffer", + "size": 1 + }, + { + "name": "__7", + "ty": "u32", + "size": 4 + } + ], + "errors": [ + { + "code": 1, + "name": "BadResponse" + }, + { + "code": 2, + "name": "BadArg" + }, + { + "code": 3, + "name": "NoDevice" + }, + { + "code": 4, + "name": "BadController" + }, + { + "code": 5, + "name": "ReservedAddress" + }, + { + "code": 6, + "name": "BadPort" + }, + { + "code": 7, + "name": "NoRegister" + }, + { + "code": 8, + "name": "BadMux" + }, + { + "code": 9, + "name": "BadSegment" + }, + { + "code": 10, + "name": "MuxNotFound" + }, + { + "code": 11, + "name": "SegmentNotFound" + }, + { + "code": 12, + "name": "SegmentDisconnected" + }, + { + "code": 13, + "name": "MuxDisconnected" + }, + { + "code": 14, + "name": "MuxMissing" + }, + { + "code": 15, + "name": "BadMuxRegister" + }, + { + "code": 16, + "name": "BusReset" + }, + { + "code": 17, + "name": "BusResetMux" + }, + { + "code": 18, + "name": "BusLocked" + }, + { + "code": 19, + "name": "BusLockedMux" + }, + { + "code": 20, + "name": "ControllerBusy" + }, + { + "code": 21, + "name": "BusError" + }, + { + "code": 22, + "name": "BadDeviceState" + }, + { + "code": 23, + "name": "OperationNotSupported" + }, + { + "code": 24, + "name": "IllegalLeaseCount" + }, + { + "code": 25, + "name": "TooMuchData" + } + ] + }, + { + "name": "I2cBulkWrite", + "id": 7, + "arg_count": 8, + "args": [ + { + "name": "__0", + "ty": "Controller", + "size": 1 + }, + { + "name": "__1", + "ty": "PortIndex", + "size": 1 + }, + { + "name": "__2", + "ty": "Mux", + "size": 1 + }, + { + "name": "__3", + "ty": "Segment", + "size": 1 + }, + { + "name": "__4", + "ty": "u8", + "size": 1 + }, + { + "name": "__5", + "ty": "u8", + "size": 1 + }, + { + "name": "__6", + "ty": "u32", + "size": 4 + }, + { + "name": "__7", + "ty": "u32", + "size": 4 + } + ], + "errors": [ + { + "code": 1, + "name": "BadResponse" + }, + { + "code": 2, + "name": "BadArg" + }, + { + "code": 3, + "name": "NoDevice" + }, + { + "code": 4, + "name": "BadController" + }, + { + "code": 5, + "name": "ReservedAddress" + }, + { + "code": 6, + "name": "BadPort" + }, + { + "code": 7, + "name": "NoRegister" + }, + { + "code": 8, + "name": "BadMux" + }, + { + "code": 9, + "name": "BadSegment" + }, + { + "code": 10, + "name": "MuxNotFound" + }, + { + "code": 11, + "name": "SegmentNotFound" + }, + { + "code": 12, + "name": "SegmentDisconnected" + }, + { + "code": 13, + "name": "MuxDisconnected" + }, + { + "code": 14, + "name": "MuxMissing" + }, + { + "code": 15, + "name": "BadMuxRegister" + }, + { + "code": 16, + "name": "BusReset" + }, + { + "code": 17, + "name": "BusResetMux" + }, + { + "code": 18, + "name": "BusLocked" + }, + { + "code": 19, + "name": "BusLockedMux" + }, + { + "code": 20, + "name": "ControllerBusy" + }, + { + "code": 21, + "name": "BusError" + }, + { + "code": 22, + "name": "BadDeviceState" + }, + { + "code": 23, + "name": "OperationNotSupported" + }, + { + "code": 24, + "name": "IllegalLeaseCount" + }, + { + "code": 25, + "name": "TooMuchData" + } + ] + }, + { + "name": "GpioInput", + "id": 8, + "arg_count": 1, + "args": [ + { + "name": "arg0", + "ty": "Port", + "size": 1 + } + ] + }, + { + "name": "GpioToggle", + "id": 9, + "arg_count": 2, + "args": [ + { + "name": "__0", + "ty": "Port", + "size": 1 + }, + { + "name": "__1", + "ty": "u8", + "size": 1 + } + ] + }, + { + "name": "GpioSet", + "id": 10, + "arg_count": 2, + "args": [ + { + "name": "__0", + "ty": "Port", + "size": 1 + }, + { + "name": "__1", + "ty": "u8", + "size": 1 + } + ] + }, + { + "name": "GpioReset", + "id": 11, + "arg_count": 2, + "args": [ + { + "name": "__0", + "ty": "Port", + "size": 1 + }, + { + "name": "__1", + "ty": "u8", + "size": 1 + } + ] + }, + { + "name": "GpioConfigure", + "id": 12, + "arg_count": 7, + "args": [ + { + "name": "__0", + "ty": "Port", + "size": 1 + }, + { + "name": "__1", + "ty": "u8", + "size": 1 + }, + { + "name": "__2", + "ty": "Mode", + "size": 1 + }, + { + "name": "__3", + "ty": "OutputType", + "size": 1 + }, + { + "name": "__4", + "ty": "Speed", + "size": 1 + }, + { + "name": "__5", + "ty": "Pull", + "size": 1 + }, + { + "name": "__6", + "ty": "Alternate", + "size": 1 + } + ] + } + ], + "buffer_sizes": { + "text": 4096, + "data": 20480, + "rstack": 2048, + "scratch": 1025 + }, + "idol_interfaces": [ + { + "name": "Jefe", + "task": "jefe", + "task_id": 0, + "ops": [ + { + "name": "get_state", + "code": 1, + "args_size": 0, + "reply": "u32", + "reply_size": 4, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "set_state", + "code": 2, + "args": [ + { + "name": "state", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "request_reset", + "code": 3, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "get_reset_reason", + "code": 4, + "args_size": 0, + "reply": "ResetReason", + "reply_size": 5, + "encoding": "Ssmarshal", + "idempotent": true + }, + { + "name": "set_reset_reason", + "code": 5, + "args": [ + { + "name": "reason", + "ty": "ResetReason" + } + ], + "args_size": 8, + "reply": "()", + "reply_size": 0, + "encoding": "Ssmarshal", + "idempotent": true + }, + { + "name": "reinitialize_dump_areas", + "code": 6, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "DumpAgentError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "get_dump_area", + "code": 7, + "args": [ + { + "name": "index", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "humpty::DumpArea", + "reply_size": 10, + "error": "DumpAgentError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "claim_dump_area", + "code": 8, + "args_size": 0, + "reply": "humpty::DumpArea", + "reply_size": 10, + "error": "DumpAgentError", + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "dump_task", + "code": 9, + "args": [ + { + "name": "task_index", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "u8", + "reply_size": 1, + "error": "DumpAgentError", + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "dump_task_region", + "code": 10, + "args": [ + { + "name": "task_index", + "ty": "u32" + }, + { + "name": "address", + "ty": "u32" + }, + { + "name": "length", + "ty": "u32" + } + ], + "args_size": 12, + "reply": "u8", + "reply_size": 1, + "error": "DumpAgentError", + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "reinitialize_dump_from", + "code": 11, + "args": [ + { + "name": "index", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "DumpAgentError", + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "restart_me_raw", + "code": 12, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "read_fault_counts", + "code": 13, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[usize; hubris_num_tasks::NUM_TASKS]", + "read": false, + "write": true + } + ], + "idempotent": true + } + ] + }, + { + "name": "Sys", + "task": "sys", + "task_id": 1, + "ops": [ + { + "name": "enable_clock_raw", + "code": 1, + "args": [ + { + "name": "peripheral", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "RccError", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "disable_clock_raw", + "code": 2, + "args": [ + { + "name": "peripheral", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "RccError", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "enter_reset_raw", + "code": 3, + "args": [ + { + "name": "peripheral", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "RccError", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "leave_reset_raw", + "code": 4, + "args": [ + { + "name": "peripheral", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "RccError", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "gpio_configure_raw", + "code": 5, + "args": [ + { + "name": "port", + "ty": "Port" + }, + { + "name": "pins", + "ty": "u16" + }, + { + "name": "packed_attributes", + "ty": "u16" + } + ], + "args_size": 5, + "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "gpio_set_reset", + "code": 6, + "args": [ + { + "name": "port", + "ty": "Port" + }, + { + "name": "set_pins", + "ty": "u16" + }, + { + "name": "reset_pins", + "ty": "u16" + } + ], + "args_size": 5, + "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "gpio_read_input", + "code": 7, + "args": [ + { + "name": "port", + "ty": "Port" + } + ], + "args_size": 1, + "reply": "u16", + "reply_size": 2, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "gpio_toggle", + "code": 8, + "args": [ + { + "name": "port", + "ty": "Port" + }, + { + "name": "pins", + "ty": "u16" + } + ], + "args_size": 3, + "reply": "()", + "reply_size": 0, + "error": "ServerDeath", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "read_uid", + "code": 9, + "args_size": 0, + "reply": "[u32; 3]", + "reply_size": 0, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "gpio_irq_configure", + "code": 10, + "args": [ + { + "name": "mask", + "ty": "u32" + }, + { + "name": "sensitivity", + "ty": "Edge" + } + ], + "args_size": 5, + "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "gpio_irq_control", + "code": 11, + "args": [ + { + "name": "mask", + "ty": "u32" + }, + { + "name": "op", + "ty": "IrqControl" + } + ], + "args_size": 5, + "reply": "bool", + "reply_size": 1, + "error": "ServerDeath", + "encoding": "Zerocopy", + "idempotent": false + } + ] + }, + { + "name": "Rng", + "task": "rng_driver", + "task_id": 2, + "ops": [ + { + "name": "fill", + "code": 1, + "args_size": 0, + "reply": "usize", + "reply_size": 4, + "error": "RngError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "source", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": false + } + ] + }, + { + "name": "Update", + "task": "update_server", + "task_id": 3, + "ops": [ + { + "name": "block_size", + "code": 1, + "args_size": 0, + "reply": "usize", + "reply_size": 4, + "error": "drv_update_api::UpdateError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "prep_image_update", + "code": 2, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "drv_update_api::UpdateError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "write_one_block", + "code": 3, + "args": [ + { + "name": "block_num", + "ty": "usize" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "drv_update_api::UpdateError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "block", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": false + }, + { + "name": "abort_update", + "code": 4, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "drv_update_api::UpdateError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "finish_image_update", + "code": 5, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "drv_update_api::UpdateError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "current_version", + "code": 6, + "args_size": 0, + "reply": "ImageVersion", + "reply_size": 8, + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "read_caboose_value", + "code": 7, + "args": [ + { + "name": "name", + "ty": "[u8; 4]" + } + ], + "args_size": 4, + "reply": "u32", + "reply_size": 4, + "error": "CabooseError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": true + }, + { + "name": "get_pending_boot_slot", + "code": 8, + "args_size": 0, + "reply": "SlotId", + "reply_size": 1, + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "set_pending_boot_slot", + "code": 9, + "args": [ + { + "name": "slot", + "ty": "SlotId" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "drv_update_api::UpdateError", + "encoding": "Hubpack", + "idempotent": false + } + ] + }, + { + "name": "AuxFlash", + "task": "auxflash", + "task_id": 4, + "ops": [ + { + "name": "read_id", + "code": 1, + "args_size": 0, + "reply": "AuxFlashId", + "reply_size": 11, + "error": "AuxFlashError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "slot_count", + "code": 2, + "args_size": 0, + "reply": "u32", + "reply_size": 4, + "error": "AuxFlashError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "slot_size", + "code": 3, + "args_size": 0, + "reply": "u32", + "reply_size": 4, + "error": "AuxFlashError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "read_status", + "code": 4, + "args_size": 0, + "reply": "u8", + "reply_size": 1, + "error": "AuxFlashError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "erase_slot", + "code": 5, + "args": [ + { + "name": "slot", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "AuxFlashError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "slot_sector_erase", + "code": 6, + "args": [ + { + "name": "slot", + "ty": "u32" + }, + { + "name": "offset", + "ty": "u32" + } + ], + "args_size": 8, + "reply": "()", + "reply_size": 0, + "error": "AuxFlashError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "read_slot_chck", + "code": 7, + "args": [ + { + "name": "slot", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "AuxFlashChecksum", + "reply_size": 32, + "error": "AuxFlashError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "write_slot_with_offset", + "code": 8, + "args": [ + { + "name": "slot", + "ty": "u32" + }, + { + "name": "offset", + "ty": "u32" + } + ], + "args_size": 8, + "reply": "()", + "reply_size": 0, + "error": "AuxFlashError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": false + }, + { + "name": "read_slot_with_offset", + "code": 9, + "args": [ + { + "name": "slot", + "ty": "u32" + }, + { + "name": "offset", + "ty": "u32" + } + ], + "args_size": 8, + "reply": "()", + "reply_size": 0, + "error": "AuxFlashError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": false + }, + { + "name": "scan_and_get_active_slot", + "code": 10, + "args_size": 0, + "reply": "u32", + "reply_size": 4, + "error": "AuxFlashError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "get_active_slot", + "code": 11, + "args_size": 0, + "reply": "u32", + "reply_size": 4, + "error": "AuxFlashError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "ensure_redundancy", + "code": 12, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "AuxFlashError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "get_blob_by_tag", + "code": 13, + "args": [ + { + "name": "name", + "ty": "[u8; 4]" + } + ], + "args_size": 4, + "reply": "AuxFlashBlob", + "reply_size": 12, + "error": "AuxFlashError", + "encoding": "Zerocopy", + "idempotent": false + } + ] + }, + { + "name": "Net", + "task": "net", + "task_id": 5, + "ops": [ + { + "name": "recv_packet", + "code": 1, + "args": [ + { + "name": "socket", + "ty": "SocketName" + }, + { + "name": "large_payload_behavior", + "ty": "LargePayloadBehavior" + } + ], + "args_size": 8, + "reply": "UdpMetadata", + "reply_size": 24, + "error": "task_net_api::RecvError", + "encoding": "Hubpack", + "leases": [ + { + "name": "payload", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": false + }, + { + "name": "send_packet", + "code": 2, + "args": [ + { + "name": "socket", + "ty": "SocketName" + }, + { + "name": "metadata", + "ty": "UdpMetadata" + } + ], + "args_size": 28, + "reply": "()", + "reply_size": 0, + "error": "task_net_api::SendError", + "encoding": "Hubpack", + "leases": [ + { + "name": "payload", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": false + }, + { + "name": "smi_read", + "code": 3, + "args": [ + { + "name": "phy", + "ty": "u8" + }, + { + "name": "register", + "ty": "u8" + } + ], + "args_size": 2, + "reply": "u16", + "reply_size": 2, + "error": "ServerDeath", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "smi_write", + "code": 4, + "args": [ + { + "name": "phy", + "ty": "u8" + }, + { + "name": "register", + "ty": "u8" + }, + { + "name": "value", + "ty": "u16" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "ServerDeath", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "read_phy_reg", + "code": 5, + "args": [ + { + "name": "port", + "ty": "u8" + }, + { + "name": "page", + "ty": "u16" + }, + { + "name": "reg", + "ty": "u8" + } + ], + "args_size": 4, + "reply": "u16", + "reply_size": 2, + "error": "PhyError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "write_phy_reg", + "code": 6, + "args": [ + { + "name": "port", + "ty": "u8" + }, + { + "name": "page", + "ty": "u16" + }, + { + "name": "reg", + "ty": "u8" + }, + { + "name": "value", + "ty": "u16" + } + ], + "args_size": 6, + "reply": "()", + "reply_size": 0, + "error": "PhyError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "read_ksz8463_mac_count", + "code": 7, + "args_size": 0, + "reply": "usize", + "reply_size": 4, + "error": "KszError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "read_ksz8463_mac", + "code": 8, + "args": [ + { + "name": "i", + "ty": "u16" + } + ], + "args_size": 2, + "reply": "KszMacTableEntry", + "reply_size": 8, + "error": "KszError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "read_ksz8463_reg", + "code": 9, + "args": [ + { + "name": "reg", + "ty": "u16" + } + ], + "args_size": 2, + "reply": "u16", + "reply_size": 2, + "error": "KszError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "get_mac_address", + "code": 10, + "args_size": 0, + "reply": "MacAddress", + "reply_size": 6, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "get_spare_mac_addresses", + "code": 11, + "args_size": 0, + "reply": "MacAddressBlock", + "reply_size": 9, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "management_link_status", + "code": 12, + "args_size": 0, + "reply": "ManagementLinkStatus", + "reply_size": 6, + "error": "MgmtError", + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "management_counters", + "code": 13, + "args_size": 0, + "reply": "ManagementCounters", + "reply_size": 105, + "error": "MgmtError", + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "trust_vlan", + "code": 14, + "args": [ + { + "name": "vid", + "ty": "VLanId" + }, + { + "name": "trust_until", + "ty": "u64" + } + ], + "args_size": 16, + "reply": "()", + "reply_size": 0, + "error": "task_net_api::TrustError", + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "distrust_vlan", + "code": 15, + "args": [ + { + "name": "vid", + "ty": "VLanId" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "task_net_api::TrustError", + "encoding": "Hubpack", + "idempotent": false + } + ] + }, + { + "name": "ControlPlaneAgent", + "task": "control_plane_agent", + "task_id": 6, + "ops": [ + { + "name": "fetch_host_phase2_data", + "code": 1, + "args": [ + { + "name": "image_hash", + "ty": "[u8; 32]" + }, + { + "name": "offset", + "ty": "u64" + }, + { + "name": "notification_bit", + "ty": "u8" + } + ], + "args_size": 41, + "reply": "()", + "reply_size": 0, + "error": "ControlPlaneAgentError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "get_host_phase2_data", + "code": 2, + "args": [ + { + "name": "image_hash", + "ty": "[u8; 32]" + }, + { + "name": "offset", + "ty": "u64" + } + ], + "args_size": 40, + "reply": "usize", + "reply_size": 4, + "error": "ControlPlaneAgentError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": false + }, + { + "name": "get_startup_options", + "code": 3, + "args_size": 0, + "reply": "HostStartupOptions", + "reply_size": 8, + "error": "ControlPlaneAgentError", + "encoding": "Ssmarshal", + "idempotent": false + }, + { + "name": "set_startup_options", + "code": 4, + "args": [ + { + "name": "startup_options", + "ty": "u64" + } + ], + "args_size": 8, + "reply": "()", + "reply_size": 0, + "error": "ControlPlaneAgentError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "identity", + "code": 5, + "args_size": 0, + "reply": "OxideIdentity", + "reply_size": 26, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "get_uart_client", + "code": 6, + "args_size": 0, + "reply": "UartClient", + "reply_size": 1, + "encoding": "Ssmarshal", + "idempotent": true + }, + { + "name": "get_installinator_image_id", + "code": 7, + "args_size": 0, + "reply": "usize", + "reply_size": 4, + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": true + }, + { + "name": "set_humility_uart_client", + "code": 8, + "args": [ + { + "name": "attach", + "ty": "bool" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "ControlPlaneAgentError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "uart_read", + "code": 9, + "args_size": 0, + "reply": "usize", + "reply_size": 4, + "error": "ControlPlaneAgentError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": false + }, + { + "name": "uart_write", + "code": 10, + "args_size": 0, + "reply": "usize", + "reply_size": 4, + "error": "ControlPlaneAgentError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": false + } + ] + }, + { + "name": "SpRot", + "task": "sprot", + "task_id": 7, + "ops": [ + { + "name": "status", + "code": 1, + "args_size": 0, + "reply": "SprotStatus", + "reply_size": 24, + "error": "SprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "io_stats", + "code": 2, + "args_size": 0, + "reply": "SprotIoStats", + "reply_size": 68, + "error": "SprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "rot_state", + "code": 3, + "args_size": 0, + "reply": "RotState", + "reply_size": 138, + "error": "SprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "pulse_cs", + "code": 4, + "args": [ + { + "name": "delay", + "ty": "u16" + } + ], + "args_size": 2, + "reply": "PulseStatus", + "reply_size": 2, + "error": "SprotError", + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "block_size", + "code": 5, + "args_size": 0, + "reply": "u32", + "reply_size": 4, + "error": "SprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "prep_image_update", + "code": 6, + "args": [ + { + "name": "target", + "ty": "UpdateTarget" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "SprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "write_one_block", + "code": 7, + "args": [ + { + "name": "block_num", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "SprotError", + "encoding": "Hubpack", + "leases": [ + { + "name": "block", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": false + }, + { + "name": "abort_update", + "code": 8, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "SprotError", + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "finish_image_update", + "code": 9, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "SprotError", + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "switch_default_image", + "code": 10, + "args": [ + { + "name": "slot", + "ty": "SlotId" + }, + { + "name": "duration", + "ty": "SwitchDuration" + } + ], + "args_size": 2, + "reply": "()", + "reply_size": 0, + "error": "SprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "reset", + "code": 11, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "SprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "dump", + "code": 12, + "args": [ + { + "name": "address", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "DumpOrSprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "caboose_size", + "code": 13, + "args": [ + { + "name": "slot", + "ty": "SlotId" + } + ], + "args_size": 1, + "reply": "u32", + "reply_size": 4, + "error": "RawCabooseOrSprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "read_caboose_region", + "code": 14, + "args": [ + { + "name": "offset", + "ty": "u32" + }, + { + "name": "slot", + "ty": "SlotId" + } + ], + "args_size": 8, + "reply": "()", + "reply_size": 0, + "error": "RawCabooseOrSprotError", + "encoding": "Hubpack", + "leases": [ + { + "name": "out", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": true + }, + { + "name": "rot_boot_info", + "code": 15, + "args_size": 0, + "reply": "RotBootInfo", + "reply_size": 72, + "error": "SprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "cert_chain_len", + "code": 16, + "args_size": 0, + "reply": "u32", + "reply_size": 4, + "error": "AttestOrSprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "cert_len", + "code": 17, + "args": [ + { + "name": "index", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "u32", + "reply_size": 4, + "error": "AttestOrSprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "cert", + "code": 18, + "args": [ + { + "name": "index", + "ty": "u32" + }, + { + "name": "offset", + "ty": "u32" + } + ], + "args_size": 8, + "reply": "()", + "reply_size": 0, + "error": "AttestOrSprotError", + "encoding": "Hubpack", + "leases": [ + { + "name": "dest", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": true + }, + { + "name": "record", + "code": 19, + "args": [ + { + "name": "algorithm", + "ty": "HashAlgorithm" + } + ], + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "AttestOrSprotError", + "encoding": "Hubpack", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": false + }, + { + "name": "read_rot_page", + "code": 20, + "args": [ + { + "name": "page", + "ty": "RotPage" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "SprotError", + "encoding": "Hubpack", + "leases": [ + { + "name": "dest", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": true + }, + { + "name": "log", + "code": 21, + "args": [ + { + "name": "offset", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "AttestOrSprotError", + "encoding": "Hubpack", + "leases": [ + { + "name": "dest", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": false + }, + { + "name": "log_len", + "code": 22, + "args_size": 0, + "reply": "u32", + "reply_size": 4, + "error": "AttestOrSprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "attest", + "code": 23, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "AttestOrSprotError", + "encoding": "Hubpack", + "leases": [ + { + "name": "nonce", + "ty": "[u8]", + "read": true, + "write": false + }, + { + "name": "dest", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": false + }, + { + "name": "attest_len", + "code": 24, + "args_size": 0, + "reply": "u32", + "reply_size": 4, + "error": "AttestOrSprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "enable_sp_slot_watchdog", + "code": 25, + "args": [ + { + "name": "time_ms", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "SprotError", + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "disable_sp_slot_watchdog", + "code": 26, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "SprotError", + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "sp_slot_watchdog_supported", + "code": 27, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "SprotError", + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "versioned_rot_boot_info", + "code": 28, + "args": [ + { + "name": "version", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "VersionedRotBootInfo", + "reply_size": 147, + "error": "SprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "component_caboose_size", + "code": 29, + "args": [ + { + "name": "component", + "ty": "RotComponent" + }, + { + "name": "slot", + "ty": "SlotId" + } + ], + "args_size": 2, + "reply": "u32", + "reply_size": 4, + "error": "RawCabooseOrSprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "component_read_caboose_region", + "code": 30, + "args": [ + { + "name": "offset", + "ty": "u32" + }, + { + "name": "component", + "ty": "RotComponent" + }, + { + "name": "slot", + "ty": "SlotId" + } + ], + "args_size": 8, + "reply": "()", + "reply_size": 0, + "error": "RawCabooseOrSprotError", + "encoding": "Hubpack", + "leases": [ + { + "name": "out", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": true + }, + { + "name": "component_prep_image_update", + "code": 31, + "args": [ + { + "name": "component", + "ty": "RotComponent" + }, + { + "name": "slot", + "ty": "SlotId" + } + ], + "args_size": 2, + "reply": "()", + "reply_size": 0, + "error": "SprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "component_switch_default_image", + "code": 32, + "args": [ + { + "name": "component", + "ty": "RotComponent" + }, + { + "name": "slot", + "ty": "SlotId" + }, + { + "name": "duration", + "ty": "SwitchDuration" + } + ], + "args_size": 3, + "reply": "()", + "reply_size": 0, + "error": "SprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "lifecycle_state", + "code": 33, + "args_size": 0, + "reply": "LifecycleState", + "reply_size": 1, + "error": "StateOrSprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "tq_cert_chain_len", + "code": 34, + "args_size": 0, + "reply": "u32", + "reply_size": 4, + "error": "AttestOrSprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "tq_cert_len", + "code": 35, + "args": [ + { + "name": "index", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "u32", + "reply_size": 4, + "error": "AttestOrSprotError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "tq_cert", + "code": 36, + "args": [ + { + "name": "index", + "ty": "u32" + }, + { + "name": "offset", + "ty": "u32" + } + ], + "args_size": 8, + "reply": "()", + "reply_size": 0, + "error": "AttestOrSprotError", + "encoding": "Hubpack", + "leases": [ + { + "name": "dest", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": true + }, + { + "name": "tq_sign", + "code": 37, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "AttestOrSprotError", + "encoding": "Hubpack", + "leases": [ + { + "name": "tq", + "ty": "[u8]", + "read": true, + "write": false + }, + { + "name": "dest", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": false + }, + { + "name": "tq_sign_len", + "code": 38, + "args_size": 0, + "reply": "u32", + "reply_size": 4, + "error": "AttestOrSprotError", + "encoding": "Hubpack", + "idempotent": true + } + ] + }, + { + "name": "Monorail", + "task": "monorail", + "task_id": 10, + "ops": [ + { + "name": "get_port_status", + "code": 1, + "args": [ + { + "name": "port", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "drv_monorail_api::PortStatus", + "reply_size": 7, + "error": "drv_monorail_api::MonorailError", + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "get_port_counters", + "code": 2, + "args": [ + { + "name": "port", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "drv_monorail_api::PortCounters", + "reply_size": 26, + "error": "drv_monorail_api::MonorailError", + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "reset_port_counters", + "code": 3, + "args": [ + { + "name": "port", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "drv_monorail_api::MonorailError", + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "get_phy_status", + "code": 4, + "args": [ + { + "name": "port", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "drv_monorail_api::PhyStatus", + "reply_size": 3, + "error": "drv_monorail_api::MonorailError", + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "read_phy_reg", + "code": 5, + "args": [ + { + "name": "port", + "ty": "u8" + }, + { + "name": "page", + "ty": "u16" + }, + { + "name": "reg", + "ty": "u8" + } + ], + "args_size": 4, + "reply": "u16", + "reply_size": 2, + "error": "drv_monorail_api::MonorailError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "write_phy_reg", + "code": 6, + "args": [ + { + "name": "port", + "ty": "u8" + }, + { + "name": "page", + "ty": "u16" + }, + { + "name": "reg", + "ty": "u8" + }, + { + "name": "value", + "ty": "u16" + } + ], + "args_size": 6, + "reply": "()", + "reply_size": 0, + "error": "drv_monorail_api::MonorailError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "read_vsc7448_reg", + "code": 7, + "args": [ + { + "name": "addr", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "u32", + "reply_size": 4, + "error": "drv_monorail_api::MonorailError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "write_vsc7448_reg", + "code": 8, + "args": [ + { + "name": "addr", + "ty": "u32" + }, + { + "name": "value", + "ty": "u32" + } + ], + "args_size": 8, + "reply": "()", + "reply_size": 0, + "error": "drv_monorail_api::MonorailError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "read_vsc8504_sd6g_patch", + "code": 9, + "args_size": 0, + "reply": "drv_monorail_api::TeslaSerdes6gPatch", + "reply_size": 38, + "error": "drv_monorail_api::MonorailError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "read_vsc8504_sd6g_ob_config", + "code": 10, + "args_size": 0, + "reply": "drv_monorail_api::TeslaSerdes6gObConfig", + "reply_size": 5, + "error": "drv_monorail_api::MonorailError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "write_vsc8504_sd6g_ob_config", + "code": 11, + "args": [ + { + "name": "ob_post0", + "ty": "u8" + }, + { + "name": "ob_post1", + "ty": "u8" + }, + { + "name": "ob_prec", + "ty": "u8" + }, + { + "name": "ob_sr_h", + "ty": "bool" + }, + { + "name": "ob_sr", + "ty": "u8" + } + ], + "args_size": 5, + "reply": "()", + "reply_size": 0, + "error": "drv_monorail_api::MonorailError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "read_vsc8562_sd6g_ob_cfg", + "code": 12, + "args_size": 0, + "reply": "drv_monorail_api::Sd6gObCfg", + "reply_size": 7, + "error": "drv_monorail_api::MonorailError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "write_vsc8562_sd6g_ob_cfg", + "code": 13, + "args": [ + { + "name": "ob_ena1v_mode", + "ty": "u8" + }, + { + "name": "ob_pol", + "ty": "u8" + }, + { + "name": "ob_post0", + "ty": "u8" + }, + { + "name": "ob_post1", + "ty": "u8" + }, + { + "name": "ob_sr_h", + "ty": "u8" + }, + { + "name": "ob_resistor_ctr", + "ty": "u8" + }, + { + "name": "ob_sr", + "ty": "u8" + } + ], + "args_size": 7, + "reply": "()", + "reply_size": 0, + "error": "drv_monorail_api::MonorailError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "read_vsc8562_sd6g_ob_cfg1", + "code": 14, + "args_size": 0, + "reply": "drv_monorail_api::Sd6gObCfg1", + "reply_size": 2, + "error": "drv_monorail_api::MonorailError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "write_vsc8562_sd6g_ob_cfg1", + "code": 15, + "args": [ + { + "name": "ob_ena_cas", + "ty": "u8" + }, + { + "name": "ob_lev", + "ty": "u8" + } + ], + "args_size": 2, + "reply": "()", + "reply_size": 0, + "error": "drv_monorail_api::MonorailError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "read_vsc7448_mac_count", + "code": 16, + "args_size": 0, + "reply": "usize", + "reply_size": 4, + "error": "drv_monorail_api::MonorailError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "read_vsc7448_next_mac", + "code": 17, + "args_size": 0, + "reply": "drv_monorail_api::MacTableEntry", + "reply_size": 8, + "error": "drv_monorail_api::MonorailError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "reinit", + "code": 18, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "drv_monorail_api::MonorailError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "unlock_vlans", + "code": 19, + "args": [ + { + "name": "unlock_until", + "ty": "u64" + } + ], + "args_size": 8, + "reply": "()", + "reply_size": 0, + "error": "drv_monorail_api::MonorailError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "lock_vlans", + "code": 20, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "drv_monorail_api::MonorailError", + "encoding": "Zerocopy", + "idempotent": false + } + ] + }, + { + "name": "Sensor", + "task": "sensor", + "task_id": 13, + "ops": [ + { + "name": "get", + "code": 1, + "args": [ + { + "name": "id", + "ty": "SensorId" + } + ], + "args_size": 4, + "reply": "f32", + "reply_size": 4, + "error": "SensorError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "get_reading", + "code": 2, + "args": [ + { + "name": "id", + "ty": "SensorId" + } + ], + "args_size": 4, + "reply": "Reading", + "reply_size": 12, + "error": "SensorError", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "get_raw_reading", + "code": 3, + "args": [ + { + "name": "id", + "ty": "SensorId" + } + ], + "args_size": 4, + "reply": "Option<(Result, u64)>", + "reply_size": 0, + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "get_last_data", + "code": 4, + "args": [ + { + "name": "id", + "ty": "SensorId" + } + ], + "args_size": 4, + "reply": "Option<(f32, u64)>", + "reply_size": 13, + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "get_last_nodata", + "code": 5, + "args": [ + { + "name": "id", + "ty": "SensorId" + } + ], + "args_size": 4, + "reply": "Option<(NoData, u64)>", + "reply_size": 0, + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "get_min", + "code": 6, + "args": [ + { + "name": "id", + "ty": "SensorId" + } + ], + "args_size": 4, + "reply": "(f32, u64)", + "reply_size": 12, + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "get_max", + "code": 7, + "args": [ + { + "name": "id", + "ty": "SensorId" + } + ], + "args_size": 4, + "reply": "(f32, u64)", + "reply_size": 12, + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "post", + "code": 8, + "args": [ + { + "name": "id", + "ty": "SensorId" + }, + { + "name": "value", + "ty": "f32" + }, + { + "name": "timestamp", + "ty": "u64" + } + ], + "args_size": 16, + "reply": "()", + "reply_size": 0, + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "nodata", + "code": 9, + "args": [ + { + "name": "id", + "ty": "SensorId" + }, + { + "name": "nodata", + "ty": "NoData" + }, + { + "name": "timestamp", + "ty": "u64" + } + ], + "args_size": 16, + "reply": "()", + "reply_size": 0, + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "get_nerrors", + "code": 10, + "args": [ + { + "name": "id", + "ty": "SensorId" + } + ], + "args_size": 4, + "reply": "u32", + "reply_size": 4, + "encoding": "Hubpack", + "idempotent": true + } + ] + }, + { + "name": "Fpga", + "task": "ecp5_mainboard", + "task_id": 14, + "ops": [ + { + "name": "device_enabled", + "code": 1, + "args": [ + { + "name": "device_index", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "bool", + "reply_size": 1, + "error": "FpgaError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "set_device_enabled", + "code": 2, + "args": [ + { + "name": "device_index", + "ty": "u8" + }, + { + "name": "enabled", + "ty": "bool" + } + ], + "args_size": 2, + "reply": "()", + "reply_size": 0, + "error": "FpgaError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "reset_device", + "code": 3, + "args": [ + { + "name": "device_index", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "FpgaError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "device_state", + "code": 4, + "args": [ + { + "name": "device_index", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "DeviceState", + "reply_size": 1, + "error": "FpgaError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "device_id", + "code": 5, + "args": [ + { + "name": "device_index", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "u32", + "reply_size": 4, + "error": "FpgaError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "start_bitstream_load", + "code": 6, + "args": [ + { + "name": "device_index", + "ty": "u8" + }, + { + "name": "bitstream_type", + "ty": "BitstreamType" + } + ], + "args_size": 2, + "reply": "()", + "reply_size": 0, + "error": "FpgaError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "continue_bitstream_load", + "code": 7, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "FpgaError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": false + }, + { + "name": "finish_bitstream_load", + "code": 8, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "FpgaError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "user_design_enabled", + "code": 9, + "args": [ + { + "name": "device_index", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "bool", + "reply_size": 1, + "error": "FpgaError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "set_user_design_enabled", + "code": 10, + "args": [ + { + "name": "device_index", + "ty": "u8" + }, + { + "name": "enabled", + "ty": "bool" + } + ], + "args_size": 2, + "reply": "()", + "reply_size": 0, + "error": "FpgaError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "reset_user_design", + "code": 11, + "args": [ + { + "name": "device_index", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "FpgaError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "user_design_read", + "code": 12, + "args": [ + { + "name": "device_index", + "ty": "u8" + }, + { + "name": "op", + "ty": "ReadOp" + }, + { + "name": "addr", + "ty": "u16" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "FpgaError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": false + }, + { + "name": "user_design_write", + "code": 13, + "args": [ + { + "name": "device_index", + "ty": "u8" + }, + { + "name": "op", + "ty": "WriteOp" + }, + { + "name": "addr", + "ty": "u16" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "FpgaError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": false + }, + { + "name": "user_design_read_reg", + "code": 14, + "args": [ + { + "name": "device_index", + "ty": "u8" + }, + { + "name": "addr", + "ty": "u16" + } + ], + "args_size": 3, + "reply": "u8", + "reply_size": 1, + "error": "FpgaError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "user_design_write_reg", + "code": 15, + "args": [ + { + "name": "device_index", + "ty": "u8" + }, + { + "name": "op", + "ty": "WriteOp" + }, + { + "name": "addr", + "ty": "u16" + }, + { + "name": "value", + "ty": "u8" + } + ], + "args_size": 5, + "reply": "()", + "reply_size": 0, + "error": "FpgaError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "lock", + "code": 16, + "args": [ + { + "name": "device_index", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "FpgaError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "release", + "code": 17, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "FpgaError", + "encoding": "Zerocopy", + "idempotent": false + } + ] + }, + { + "name": "Fpga", + "task": "ecp5_front_io", + "task_id": 15, + "ops": [ + { + "name": "device_enabled", + "code": 1, + "args": [ + { + "name": "device_index", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "bool", + "reply_size": 1, + "error": "FpgaError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "set_device_enabled", + "code": 2, + "args": [ + { + "name": "device_index", + "ty": "u8" + }, + { + "name": "enabled", + "ty": "bool" + } + ], + "args_size": 2, + "reply": "()", + "reply_size": 0, + "error": "FpgaError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "reset_device", + "code": 3, + "args": [ + { + "name": "device_index", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "FpgaError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "device_state", + "code": 4, + "args": [ + { + "name": "device_index", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "DeviceState", + "reply_size": 1, + "error": "FpgaError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "device_id", + "code": 5, + "args": [ + { + "name": "device_index", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "u32", + "reply_size": 4, + "error": "FpgaError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "start_bitstream_load", + "code": 6, + "args": [ + { + "name": "device_index", + "ty": "u8" + }, + { + "name": "bitstream_type", + "ty": "BitstreamType" + } + ], + "args_size": 2, + "reply": "()", + "reply_size": 0, + "error": "FpgaError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "continue_bitstream_load", + "code": 7, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "FpgaError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": false + }, + { + "name": "finish_bitstream_load", + "code": 8, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "FpgaError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "user_design_enabled", + "code": 9, + "args": [ + { + "name": "device_index", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "bool", + "reply_size": 1, + "error": "FpgaError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "set_user_design_enabled", + "code": 10, + "args": [ + { + "name": "device_index", + "ty": "u8" + }, + { + "name": "enabled", + "ty": "bool" + } + ], + "args_size": 2, + "reply": "()", + "reply_size": 0, + "error": "FpgaError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "reset_user_design", + "code": 11, + "args": [ + { + "name": "device_index", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "FpgaError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "user_design_read", + "code": 12, + "args": [ + { + "name": "device_index", + "ty": "u8" + }, + { + "name": "op", + "ty": "ReadOp" + }, + { + "name": "addr", + "ty": "u16" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "FpgaError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": false + }, + { + "name": "user_design_write", + "code": 13, + "args": [ + { + "name": "device_index", + "ty": "u8" + }, + { + "name": "op", + "ty": "WriteOp" + }, + { + "name": "addr", + "ty": "u16" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "FpgaError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": false + }, + { + "name": "user_design_read_reg", + "code": 14, + "args": [ + { + "name": "device_index", + "ty": "u8" + }, + { + "name": "addr", + "ty": "u16" + } + ], + "args_size": 3, + "reply": "u8", + "reply_size": 1, + "error": "FpgaError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "user_design_write_reg", + "code": 15, + "args": [ + { + "name": "device_index", + "ty": "u8" + }, + { + "name": "op", + "ty": "WriteOp" + }, + { + "name": "addr", + "ty": "u16" + }, + { + "name": "value", + "ty": "u8" + } + ], + "args_size": 5, + "reply": "()", + "reply_size": 0, + "error": "FpgaError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "lock", + "code": 16, + "args": [ + { + "name": "device_index", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "FpgaError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "release", + "code": 17, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "FpgaError", + "encoding": "Zerocopy", + "idempotent": false + } + ] + }, + { + "name": "Transceivers", + "task": "transceivers", + "task_id": 16, + "ops": [ + { + "name": "get_module_status", + "code": 1, + "args_size": 0, + "reply": "ModuleStatus", + "reply_size": 32, + "error": "TransceiversError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "set_system_led_on", + "code": 2, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "TransceiversError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "set_system_led_off", + "code": 3, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "TransceiversError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "set_system_led_blink", + "code": 4, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "TransceiversError", + "encoding": "Zerocopy", + "idempotent": false + } + ] + }, + { + "name": "Packrat", + "task": "packrat", + "task_id": 17, + "ops": [ + { + "name": "get_mac_address_block", + "code": 1, + "args_size": 0, + "reply": "MacAddressBlock", + "reply_size": 9, + "error": "CacheGetError", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "set_mac_address_block", + "code": 2, + "args": [ + { + "name": "macs", + "ty": "MacAddressBlock" + } + ], + "args_size": 9, + "reply": "()", + "reply_size": 0, + "error": "CacheSetError", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "get_identity", + "code": 3, + "args_size": 0, + "reply": "OxideIdentity", + "reply_size": 26, + "error": "CacheGetError", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "set_identity", + "code": 4, + "args": [ + { + "name": "macs", + "ty": "OxideIdentity" + } + ], + "args_size": 26, + "reply": "()", + "reply_size": 0, + "error": "CacheSetError", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "get_next_boot_host_startup_options", + "code": 5, + "args_size": 0, + "reply": "HostStartupOptions", + "reply_size": 8, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "set_next_boot_host_startup_options", + "code": 6, + "args": [ + { + "name": "startup_options", + "ty": "HostStartupOptions" + } + ], + "args_size": 8, + "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "remove_spd", + "code": 7, + "args": [ + { + "name": "index", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "set_spd_eeprom", + "code": 8, + "args": [ + { + "name": "index", + "ty": "u8" + }, + { + "name": "offset", + "ty": "usize" + } + ], + "args_size": 5, + "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": true + }, + { + "name": "get_spd_present", + "code": 9, + "args": [ + { + "name": "index", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "bool", + "reply_size": 1, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "get_spd_data", + "code": 10, + "args": [ + { + "name": "index", + "ty": "u8" + }, + { + "name": "offset", + "ty": "usize" + } + ], + "args_size": 5, + "reply": "u8", + "reply_size": 1, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "get_full_spd_data", + "code": 11, + "args": [ + { + "name": "index", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": true + }, + { + "name": "set_ereport_restart_id", + "code": 12, + "args": [ + { + "name": "restart_id", + "ty": "u128" + } + ], + "args_size": 16, + "reply": "()", + "reply_size": 0, + "error": "CacheSetError", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "deliver_encoded_ereport", + "code": 13, + "args_size": 0, + "reply": "ereport_messages::Ena", + "reply_size": 8, + "error": "EreportWriteError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": true + }, + { + "name": "read_ereports", + "code": 14, + "args": [ + { + "name": "request_id", + "ty": "ereport_messages::RequestIdV0" + }, + { + "name": "restart_id", + "ty": "ereport_messages::RestartId" + }, + { + "name": "start_ena", + "ty": "ereport_messages::Ena" + }, + { + "name": "limit", + "ty": "u8" + }, + { + "name": "committed_ena", + "ty": "ereport_messages::Ena" + } + ], + "args_size": 34, + "reply": "usize", + "reply_size": 4, + "error": "EreportReadError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": true + } + ] + }, + { + "name": "Sequencer", + "task": "sequencer", + "task_id": 18, + "ops": [ + { + "name": "tofino_seq_policy", + "code": 1, + "args_size": 0, + "reply": "TofinoSequencerPolicy", + "reply_size": 1, + "error": "SeqError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "set_tofino_seq_policy", + "code": 2, + "args": [ + { + "name": "policy", + "ty": "TofinoSequencerPolicy" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "SeqError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "tofino_seq_state", + "code": 3, + "args_size": 0, + "reply": "TofinoSeqState", + "reply_size": 1, + "error": "SeqError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "tofino_seq_error", + "code": 4, + "args_size": 0, + "reply": "TofinoSeqError", + "reply_size": 1, + "error": "SeqError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "tofino_seq_error_step", + "code": 5, + "args_size": 0, + "reply": "TofinoSeqStep", + "reply_size": 1, + "error": "SeqError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "clear_tofino_seq_error", + "code": 6, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "SeqError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "tofino_power_rails", + "code": 7, + "args_size": 0, + "reply": "[drv_fpga_user_api::power_rail::RawPowerRailState; 6]", + "reply_size": 6, + "error": "SeqError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "tofino_pcie_hotplug_ctrl", + "code": 8, + "args_size": 0, + "reply": "u8", + "reply_size": 1, + "error": "SeqError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "set_tofino_pcie_hotplug_ctrl", + "code": 9, + "args": [ + { + "name": "mask", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "SeqError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "clear_tofino_pcie_hotplug_ctrl", + "code": 10, + "args": [ + { + "name": "mask", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "SeqError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "tofino_pcie_hotplug_status", + "code": 11, + "args_size": 0, + "reply": "u8", + "reply_size": 1, + "error": "SeqError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "tofino_pcie_reset", + "code": 12, + "args_size": 0, + "reply": "TofinoPcieReset", + "reply_size": 1, + "error": "SeqError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "set_tofino_pcie_reset", + "code": 13, + "args": [ + { + "name": "reset", + "ty": "TofinoPcieReset" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "SeqError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "tofino_pcie_link_up", + "code": 14, + "args_size": 0, + "reply": "bool", + "reply_size": 1, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "is_clock_config_loaded", + "code": 15, + "args_size": 0, + "reply": "bool", + "reply_size": 1, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "load_clock_config", + "code": 16, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "SeqError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "front_io_board_present", + "code": 17, + "args_size": 0, + "reply": "bool", + "reply_size": 1, + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "front_io_board_ready", + "code": 18, + "args_size": 0, + "reply": "bool", + "reply_size": 1, + "error": "SeqError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "reset_front_io_phy", + "code": 19, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "SeqError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "set_front_io_phy_osc_state", + "code": 20, + "args": [ + { + "name": "good", + "ty": "bool" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "SeqError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "tofino_debug_port_state", + "code": 21, + "args_size": 0, + "reply": "DebugPortState", + "reply_size": 1, + "error": "SeqError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "set_tofino_debug_port_state", + "code": 22, + "args": [ + { + "name": "state", + "ty": "DebugPortState" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "SeqError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "tofino_read_direct", + "code": 23, + "args": [ + { + "name": "segment", + "ty": "DirectBarSegment" + }, + { + "name": "offset", + "ty": "u32" + } + ], + "args_size": 8, + "reply": "u32", + "reply_size": 4, + "error": "FpgaError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "tofino_write_direct", + "code": 24, + "args": [ + { + "name": "segment", + "ty": "DirectBarSegment" + }, + { + "name": "offset", + "ty": "u32" + }, + { + "name": "value", + "ty": "u32" + } + ], + "args_size": 12, + "reply": "()", + "reply_size": 0, + "error": "SeqError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "spi_eeprom_idcode", + "code": 25, + "args_size": 0, + "reply": "u32", + "reply_size": 4, + "error": "SeqError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "spi_eeprom_status", + "code": 26, + "args_size": 0, + "reply": "u8", + "reply_size": 1, + "error": "SeqError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "read_spi_eeprom_bytes", + "code": 27, + "args": [ + { + "name": "offset", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "SeqError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": false + }, + { + "name": "write_spi_eeprom_bytes", + "code": 28, + "args": [ + { + "name": "offset", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "SeqError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": false + }, + { + "name": "mainboard_controller_ready", + "code": 29, + "args_size": 0, + "reply": "bool", + "reply_size": 1, + "error": "SeqError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "fan_module_status", + "code": 30, + "args": [ + { + "name": "module", + "ty": "FanModuleIndex" + } + ], + "args_size": 1, + "reply": "FanModuleStatus", + "reply_size": 1, + "error": "SeqError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "fan_module_presence", + "code": 31, + "args_size": 0, + "reply": "FanModulePresence", + "reply_size": 4, + "error": "SeqError", + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "fan_module_led_off", + "code": 32, + "args": [ + { + "name": "module", + "ty": "FanModuleIndex" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "SeqError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "fan_module_led_on", + "code": 33, + "args": [ + { + "name": "module", + "ty": "FanModuleIndex" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "SeqError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "fan_module_led_blink", + "code": 34, + "args": [ + { + "name": "module", + "ty": "FanModuleIndex" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "SeqError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "fan_module_enable", + "code": 35, + "args": [ + { + "name": "module", + "ty": "FanModuleIndex" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "SeqError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "fan_module_disable", + "code": 36, + "args": [ + { + "name": "module", + "ty": "FanModuleIndex" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "SeqError", + "encoding": "Zerocopy", + "idempotent": false + } + ] + }, + { + "name": "Thermal", + "task": "thermal", + "task_id": 19, + "ops": [ + { + "name": "set_mode_manual", + "code": 1, + "args": [ + { + "name": "initial_pwm", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "ThermalError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "set_mode_auto", + "code": 2, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "ThermalError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "get_mode", + "code": 3, + "args_size": 0, + "reply": "ThermalMode", + "reply_size": 1, + "error": "ThermalError", + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "get_auto_state", + "code": 4, + "args_size": 0, + "reply": "ThermalAutoState", + "reply_size": 1, + "error": "ThermalError", + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "disable_watchdog", + "code": 5, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "ThermalError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "enable_watchdog", + "code": 6, + "args": [ + { + "name": "timeout_s", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "ThermalError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "set_pid", + "code": 7, + "args": [ + { + "name": "z", + "ty": "f32" + }, + { + "name": "p", + "ty": "f32" + }, + { + "name": "i", + "ty": "f32" + }, + { + "name": "d", + "ty": "f32" + } + ], + "args_size": 16, + "reply": "()", + "reply_size": 0, + "error": "ThermalError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "get_margin", + "code": 8, + "args_size": 0, + "reply": "f32", + "reply_size": 4, + "error": "ThermalError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "set_margin", + "code": 9, + "args": [ + { + "name": "margin", + "ty": "f32" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "ThermalError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "update_dynamic_input", + "code": 10, + "args": [ + { + "name": "index", + "ty": "usize" + }, + { + "name": "model", + "ty": "ThermalProperties" + } + ], + "args_size": 20, + "reply": "()", + "reply_size": 0, + "error": "ThermalError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "remove_dynamic_input", + "code": 11, + "args": [ + { + "name": "index", + "ty": "usize" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "ThermalError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "get_runtime", + "code": 12, + "args_size": 0, + "reply": "u64", + "reply_size": 8, + "error": "ThermalError", + "encoding": "Zerocopy", + "idempotent": false + } + ] + }, + { + "name": "Power", + "task": "power", + "task_id": 20, + "ops": [ + { + "name": "pmbus_read", + "code": 1, + "args": [ + { + "name": "dev", + "ty": "Device" + }, + { + "name": "rail", + "ty": "u8" + }, + { + "name": "index", + "ty": "u32" + }, + { + "name": "op", + "ty": "Operation" + } + ], + "args_size": 8, + "reply": "PmbusValue", + "reply_size": 34, + "error": "ResponseCode", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "read_mode", + "code": 2, + "args": [ + { + "name": "dev", + "ty": "Device" + }, + { + "name": "rail", + "ty": "u8" + }, + { + "name": "index", + "ty": "u32" + } + ], + "args_size": 8, + "reply": "u8", + "reply_size": 1, + "error": "ResponseCode", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "phase_current", + "code": 3, + "args": [ + { + "name": "rail", + "ty": "SensorId" + }, + { + "name": "phase", + "ty": "u8" + } + ], + "args_size": 8, + "reply": "f32", + "reply_size": 4, + "error": "ResponseCode", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "bmr491_event_log_read", + "code": 4, + "args": [ + { + "name": "index", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "Bmr491Event", + "reply_size": 24, + "error": "ResponseCode", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "bmr491_fault_log_clear", + "code": 5, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "ResponseCode", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "bmr491_max_fault_event_index", + "code": 6, + "args_size": 0, + "reply": "u8", + "reply_size": 1, + "error": "ResponseCode", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "bmr491_max_lifecycle_event_index", + "code": 7, + "args_size": 0, + "reply": "u8", + "reply_size": 1, + "error": "ResponseCode", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "rendmp_blackbox_dump", + "code": 8, + "args": [ + { + "name": "addr", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "RenesasBlackbox", + "reply_size": 177, + "error": "ResponseCode", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "rendmp_dma_read", + "code": 9, + "args": [ + { + "name": "addr", + "ty": "u8" + }, + { + "name": "reg", + "ty": "u16" + } + ], + "args_size": 3, + "reply": "u32", + "reply_size": 4, + "error": "ResponseCode", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "rendmp_dma_write", + "code": 10, + "args": [ + { + "name": "addr", + "ty": "u8" + }, + { + "name": "reg", + "ty": "u16" + }, + { + "name": "data", + "ty": "u32" + } + ], + "args_size": 7, + "reply": "()", + "reply_size": 0, + "error": "ResponseCode", + "encoding": "Zerocopy", + "idempotent": true + }, + { + "name": "raw_pmbus_read_byte", + "code": 11, + "args": [ + { + "name": "index", + "ty": "u32" + }, + { + "name": "has_rail", + "ty": "bool" + }, + { + "name": "op", + "ty": "u8" + } + ], + "args_size": 8, + "reply": "u8", + "reply_size": 1, + "error": "ResponseCode", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "raw_pmbus_read_word", + "code": 12, + "args": [ + { + "name": "index", + "ty": "u32" + }, + { + "name": "has_rail", + "ty": "bool" + }, + { + "name": "op", + "ty": "u8" + } + ], + "args_size": 8, + "reply": "u16", + "reply_size": 2, + "error": "ResponseCode", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "raw_pmbus_read_word32", + "code": 13, + "args": [ + { + "name": "index", + "ty": "u32" + }, + { + "name": "has_rail", + "ty": "bool" + }, + { + "name": "op", + "ty": "u8" + } + ], + "args_size": 8, + "reply": "u32", + "reply_size": 4, + "error": "ResponseCode", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "raw_pmbus_read_block", + "code": 14, + "args": [ + { + "name": "index", + "ty": "u32" + }, + { + "name": "has_rail", + "ty": "bool" + }, + { + "name": "op", + "ty": "u8" + } + ], + "args_size": 8, + "reply": "RawPmbusBlock", + "reply_size": 33, + "error": "ResponseCode", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "raw_pmbus_set", + "code": 15, + "args": [ + { + "name": "index", + "ty": "u32" + }, + { + "name": "has_rail", + "ty": "bool" + }, + { + "name": "op", + "ty": "u8" + } + ], + "args_size": 8, + "reply": "()", + "reply_size": 0, + "error": "ResponseCode", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "raw_pmbus_write_byte", + "code": 16, + "args": [ + { + "name": "index", + "ty": "u32" + }, + { + "name": "has_rail", + "ty": "bool" + }, + { + "name": "op", + "ty": "u8" + }, + { + "name": "data", + "ty": "u8" + } + ], + "args_size": 8, + "reply": "()", + "reply_size": 0, + "error": "ResponseCode", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "raw_pmbus_write_word", + "code": 17, + "args": [ + { + "name": "index", + "ty": "u32" + }, + { + "name": "has_rail", + "ty": "bool" + }, + { + "name": "op", + "ty": "u8" + }, + { + "name": "data", + "ty": "u16" + } + ], + "args_size": 8, + "reply": "()", + "reply_size": 0, + "error": "ResponseCode", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "raw_pmbus_write_word32", + "code": 18, + "args": [ + { + "name": "index", + "ty": "u32" + }, + { + "name": "has_rail", + "ty": "bool" + }, + { + "name": "op", + "ty": "u8" + }, + { + "name": "data", + "ty": "u32" + } + ], + "args_size": 12, + "reply": "()", + "reply_size": 0, + "error": "ResponseCode", + "encoding": "Hubpack", + "idempotent": true + }, + { + "name": "raw_pmbus_write_block", + "code": 19, + "args": [ + { + "name": "index", + "ty": "u32" + }, + { + "name": "has_rail", + "ty": "bool" + }, + { + "name": "op", + "ty": "u8" + }, + { + "name": "data", + "ty": "RawPmbusBlock" + } + ], + "args_size": 40, + "reply": "()", + "reply_size": 0, + "error": "ResponseCode", + "encoding": "Hubpack", + "idempotent": true + } + ] + }, + { + "name": "Validate", + "task": "validate", + "task_id": 21, + "ops": [ + { + "name": "validate_i2c", + "code": 1, + "args": [ + { + "name": "index", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "ValidateOk", + "reply_size": 1, + "error": "ValidateError", + "encoding": "Zerocopy", + "idempotent": true + } + ] + }, + { + "name": "Ignition", + "task": "ignition", + "task_id": 22, + "ops": [ + { + "name": "port_count", + "code": 1, + "args_size": 0, + "reply": "u8", + "reply_size": 1, + "error": "drv_ignition_api::IgnitionError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "presence_summary", + "code": 2, + "args_size": 0, + "reply": "u64", + "reply_size": 8, + "error": "drv_ignition_api::IgnitionError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "port_state", + "code": 3, + "args": [ + { + "name": "port", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "drv_ignition_api::PortState", + "reply_size": 8, + "error": "drv_ignition_api::IgnitionError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "always_transmit", + "code": 4, + "args": [ + { + "name": "port", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "bool", + "reply_size": 1, + "error": "drv_ignition_api::IgnitionError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "set_always_transmit", + "code": 5, + "args": [ + { + "name": "port", + "ty": "u8" + }, + { + "name": "enabled", + "ty": "bool" + } + ], + "args_size": 2, + "reply": "()", + "reply_size": 0, + "error": "drv_ignition_api::IgnitionError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "counters", + "code": 6, + "args": [ + { + "name": "port", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "drv_ignition_api::Counters", + "reply_size": 4, + "error": "drv_ignition_api::IgnitionError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "transceiver_events", + "code": 7, + "args": [ + { + "name": "port", + "ty": "u8" + }, + { + "name": "txr", + "ty": "drv_ignition_api::TransceiverSelect" + } + ], + "args_size": 2, + "reply": "u8", + "reply_size": 1, + "error": "drv_ignition_api::IgnitionError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "clear_transceiver_events", + "code": 8, + "args": [ + { + "name": "port", + "ty": "u8" + }, + { + "name": "txr", + "ty": "drv_ignition_api::TransceiverSelect" + } + ], + "args_size": 2, + "reply": "()", + "reply_size": 0, + "error": "drv_ignition_api::IgnitionError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "link_events", + "code": 9, + "args": [ + { + "name": "port", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "[u8; 3]", + "reply_size": 3, + "error": "drv_ignition_api::IgnitionError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "send_request", + "code": 10, + "args": [ + { + "name": "port", + "ty": "u8" + }, + { + "name": "request", + "ty": "drv_ignition_api::Request" + } + ], + "args_size": 2, + "reply": "()", + "reply_size": 0, + "error": "drv_ignition_api::IgnitionError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "all_port_state", + "code": 11, + "args_size": 0, + "reply": "[drv_ignition_api::PortState; 40]", + "reply_size": 320, + "error": "drv_ignition_api::IgnitionError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "all_link_events", + "code": 12, + "args_size": 0, + "reply": "[[u8; 3]; 40]", + "reply_size": 120, + "error": "drv_ignition_api::IgnitionError", + "encoding": "Zerocopy", + "idempotent": false + } + ] + }, + { + "name": "Vpd", + "task": "vpd", + "task_id": 23, + "ops": [ + { + "name": "read_tmp117_eeprom", + "code": 1, + "args": [ + { + "name": "index", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "[u8; 6]", + "reply_size": 6, + "error": "VpdError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "read", + "code": 2, + "args": [ + { + "name": "index", + "ty": "u8" + }, + { + "name": "offset", + "ty": "u16" + } + ], + "args_size": 3, + "reply": "[u8; 16]", + "reply_size": 16, + "error": "VpdError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "write", + "code": 3, + "args": [ + { + "name": "index", + "ty": "u8" + }, + { + "name": "offset", + "ty": "u16" + }, + { + "name": "contents", + "ty": "u8" + } + ], + "args_size": 4, + "reply": "()", + "reply_size": 0, + "error": "VpdError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "is_locked", + "code": 4, + "args": [ + { + "name": "index", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "bool", + "reply_size": 1, + "error": "VpdError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "permanently_lock", + "code": 5, + "args": [ + { + "name": "index", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "VpdError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "num_vpd_devices", + "code": 6, + "args_size": 0, + "reply": "usize", + "reply_size": 4, + "encoding": "Zerocopy", + "idempotent": true + } + ] + }, + { + "name": "DumpAgent", + "task": "dump_agent", + "task_id": 24, + "ops": [ + { + "name": "read_dump", + "code": 1, + "args": [ + { + "name": "index", + "ty": "u8" + }, + { + "name": "offset", + "ty": "u32" + } + ], + "args_size": 5, + "reply": "[u8; DUMP_READ_SIZE]", + "reply_size": 256, + "error": "DumpAgentError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "read_dump_into", + "code": 2, + "args": [ + { + "name": "index", + "ty": "u8" + }, + { + "name": "offset", + "ty": "u32" + } + ], + "args_size": 5, + "reply": "usize", + "reply_size": 4, + "error": "DumpAgentError", + "encoding": "Zerocopy", + "leases": [ + { + "name": "out", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": false + }, + { + "name": "get_dump_area", + "code": 3, + "args": [ + { + "name": "index", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "DumpArea", + "reply_size": 10, + "error": "DumpAgentError", + "encoding": "Hubpack", + "idempotent": false + }, + { + "name": "initialize_dump", + "code": 4, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "DumpAgentError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "add_dump_segment", + "code": 5, + "args": [ + { + "name": "address", + "ty": "u32" + }, + { + "name": "length", + "ty": "u32" + } + ], + "args_size": 8, + "reply": "()", + "reply_size": 0, + "error": "DumpAgentError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "take_dump", + "code": 6, + "args_size": 0, + "reply": "()", + "reply_size": 0, + "error": "DumpAgentError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "dump_task", + "code": 7, + "args": [ + { + "name": "task_index", + "ty": "u32" + } + ], + "args_size": 4, + "reply": "u8", + "reply_size": 1, + "error": "DumpAgentError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "dump_task_region", + "code": 8, + "args": [ + { + "name": "task_index", + "ty": "u32" + }, + { + "name": "start", + "ty": "u32" + }, + { + "name": "length", + "ty": "u32" + } + ], + "args_size": 12, + "reply": "u8", + "reply_size": 1, + "error": "DumpAgentError", + "encoding": "Zerocopy", + "idempotent": false + }, + { + "name": "reinitialize_dump_from", + "code": 9, + "args": [ + { + "name": "index", + "ty": "u8" + } + ], + "args_size": 1, + "reply": "()", + "reply_size": 0, + "error": "DumpAgentError", + "encoding": "Hubpack", + "idempotent": false + } + ] + } + ] +} \ No newline at end of file diff --git a/humility-hif-assembler/src/archive.rs b/humility-hif-assembler/src/archive.rs new file mode 100644 index 000000000..ec3189606 --- /dev/null +++ b/humility-hif-assembler/src/archive.rs @@ -0,0 +1,599 @@ +// 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/. + +//! Extract a [`TargetConfig`] from a Hubris archive. +//! +//! This module bridges the gap between humility's `HubrisArchive` and +//! the assembler's `TargetConfig`. It reads: +//! +//! - Image ID from the archive +//! - I2C bus topology from `manifest.i2c_buses` +//! - HIF function table from DWARF debug info (`HIFFY_FUNCTIONS`) +//! - Buffer sizes from HIFFY_TEXT/DATA/RSTACK variable sizes +//! +//! The DWARF walking logic mirrors `HiffyContext::new()` in +//! `humility-hiffy` but does not require a live target connection. + +use std::path::Path; + +use anyhow::{Context, Result, anyhow}; + +use humility::hubris::{ + HubrisArchive, HubrisArchiveDoneness, HubrisEncoding, HubrisGoff, + HubrisSensorDevice, HubrisTask, +}; + +use crate::types::{ + BufferSizes, FunctionArg, FunctionError, FunctionInfo, I2cDeviceInfo, + I2cMuxInfo, I2cMuxSegment, IdolArgInfo, IdolInterfaceInfo, IdolLeaseInfo, + IdolOpInfo, ResolvedBus, SensorInfo, TargetConfig, +}; + +impl TargetConfig { + /// Load a [`TargetConfig`] from a Hubris archive file (.zip). + /// + /// This reads the archive, parses DWARF debug info, and extracts + /// everything the assembler needs. No live target is required. + /// + /// ```rust,ignore + /// let config = TargetConfig::from_archive_file( + /// "target/gimlet-c-dev/dist/default/build-gimlet-c-dev-image-default.zip" + /// )?; + /// let asm = HifAssembler::new(config); + /// ``` + pub fn from_archive_file(path: impl AsRef) -> Result { + let path = path.as_ref(); + let path_str = + path.to_str().ok_or_else(|| anyhow!("non-UTF-8 archive path"))?; + + let mut hubris = HubrisArchive::new()?; + hubris + .load(path_str, HubrisArchiveDoneness::Cook) + .with_context(|| format!("loading archive {}", path.display()))?; + + Self::from_archive(&hubris) + } + + /// Extract a [`TargetConfig`] from an already-loaded + /// `HubrisArchive`. + /// + /// Validates that the archive contains a usable hiffy task and + /// reports warnings for missing network features. + pub fn from_archive(hubris: &HubrisArchive) -> Result { + // Validate that the archive has a hiffy task + let hiffy_task = hubris.lookup_task("hiffy").ok_or_else(|| { + anyhow!( + "hiffy task not found in archive; \ + this image cannot execute HIF programs" + ) + })?; + + // Check for net feature (needed for NetHiffy over network) + match hubris.does_task_have_feature(hiffy_task, "net") { + Ok(true) => {} + _ => { + eprintln!( + "warning: hiffy task does not have 'net' feature; \ + network execution requires adding \ + features = [\"net\", \"vlan\"] and a hiffy \ + socket to the app.toml (probe execution \ + still works)" + ); + } + } + + let image_id = + hubris.image_id().map(|id| id.to_vec()).unwrap_or_default(); + + let board = hubris + .manifest + .board + .clone() + .unwrap_or_else(|| "unknown".to_string()); + + let buses = extract_i2c_buses(hubris); + let functions = extract_hiffy_functions(hubris)?; + let (buffer_sizes, buf_warnings) = extract_buffer_sizes(hubris)?; + + for w in &buf_warnings { + eprintln!("warning: {w}"); + } + + let idol_interfaces = extract_idol_interfaces(hubris); + + Ok(TargetConfig { + image_id, + board, + buses, + functions, + buffer_sizes, + idol_interfaces, + }) + } +} + +/// Extract I2C bus definitions from the archive manifest, including +/// device topology and mux structure. +fn extract_i2c_buses(hubris: &HubrisArchive) -> Vec { + use std::collections::BTreeMap; + + // Build bus list from controllers + let mut buses: Vec = hubris + .manifest + .i2c_buses + .iter() + .filter_map(|bus| { + if bus.target { + return None; + } + let name = bus.name.as_ref()?; + Some(ResolvedBus { + name: name.clone(), + controller: bus.controller, + port_index: bus.port.index, + port_name: bus.port.name.clone(), + devices: vec![], + muxes: vec![], + }) + }) + .collect(); + + // Build a sensor lookup: i2c_device index -> Vec + let mut sensor_map: BTreeMap> = BTreeMap::new(); + for sensor in &hubris.manifest.sensors { + if let HubrisSensorDevice::I2c(idx) = &sensor.device { + sensor_map.entry(*idx).or_default().push(SensorInfo { + name: sensor.name.clone(), + kind: format!("{:?}", sensor.kind), + }); + } + } + + // Populate devices onto their buses + for (dev_idx, dev) in hubris.manifest.i2c_devices.iter().enumerate() { + let sensors = sensor_map.remove(&dev_idx).unwrap_or_default(); + + let info = I2cDeviceInfo { + address: dev.address, + device: dev.device.clone(), + name: dev.name.clone(), + description: dev.description.clone(), + removable: dev.removable, + sensors, + }; + + // Find the matching bus + let bus = buses.iter_mut().find(|b| { + b.controller == dev.controller + && b.port_name.eq_ignore_ascii_case(&dev.port.name) + }); + + let bus = match bus { + Some(b) => b, + None => continue, // device on an unknown/target bus + }; + + match (dev.mux, dev.segment) { + (Some(mux_addr), Some(segment)) => { + // Device is behind a mux + let mux = bus.muxes.iter_mut().find(|m| m.address == mux_addr); + let mux = match mux { + Some(m) => m, + None => { + bus.muxes.push(I2cMuxInfo { + address: mux_addr, + segments: vec![], + }); + bus.muxes.last_mut().unwrap() + } + }; + let seg = + mux.segments.iter_mut().find(|s| s.segment == segment); + match seg { + Some(s) => s.devices.push(info), + None => { + mux.segments.push(I2cMuxSegment { + segment, + devices: vec![info], + }); + } + } + } + _ => { + // Direct device on the bus + bus.devices.push(info); + } + } + } + + // Sort mux segments for deterministic output + for bus in &mut buses { + for mux in &mut bus.muxes { + mux.segments.sort_by_key(|s| s.segment); + } + bus.muxes.sort_by_key(|m| m.address); + } + + buses +} + +/// Extract HIF function table from DWARF debug info. +/// +/// This replicates the DWARF walking logic from `HiffyContext::new()` +/// (humility-hiffy/src/lib.rs lines 271-343) without requiring a live +/// target. +fn extract_hiffy_functions( + hubris: &HubrisArchive, +) -> Result> { + // Look up HIFFY_FUNCTIONS definition. May be in definitions or + // variables depending on compiler version (see hubris#2169). + let goff = hubris + .lookup_definition("HIFFY_FUNCTIONS") + .or_else(|_| hubris.lookup_variable("HIFFY_FUNCTIONS").map(|v| &v.goff)) + .copied() + .context( + "HIFFY_FUNCTIONS not found in archive; \ + does this image include the hiffy task?", + )?; + + // Navigate: Option<&[fn]> -> Some variant -> pointer -> enum + let option_enum = + hubris.lookup_enum(goff).context("HIFFY_FUNCTIONS is not an enum")?; + let some_goff = option_enum + .lookup_variant_byname("Some")? + .goff + .ok_or_else(|| anyhow!("HIFFY_FUNCTIONS Some variant has no type"))?; + + let ptr_struct = hubris + .lookup_struct(some_goff) + .context("HIFFY_FUNCTIONS Some is not a struct")?; + let ptr_goff = ptr_struct + .lookup_member("__0") + .context("HIFFY_FUNCTIONS Some has no __0 member")? + .goff; + let deref_goff = hubris + .lookup_ptrtype(ptr_goff) + .context("HIFFY_FUNCTIONS pointer dereference failed")?; + let functions_enum = hubris + .lookup_enum(deref_goff) + .context("HIFFY_FUNCTIONS target is not an enum")?; + + // Iterate variants in program order — the index IS the function ID. + let mut result = Vec::with_capacity(functions_enum.variants.len()); + for (id, variant) in functions_enum.variants.iter().enumerate() { + let id = u8::try_from(id).context("more than 255 HIF functions")?; + + let (args, errors) = match variant.goff { + Some(goff) => extract_function_signature(hubris, goff)?, + None => (vec![], vec![]), + }; + + result.push(FunctionInfo { + name: variant.name.to_string(), + id, + arg_count: args.len(), + args, + errors, + }); + } + + Ok(result) +} + +/// Extract argument and error info for a HIF function from DWARF. +/// +/// Each function variant wraps a 2-tuple of (args, error_type). +/// The args member (__0) is either: +/// - A struct (multiple args): extract each member's name and type +/// - A zero-size basetype (no args): empty vec +/// - Anything else (single arg): one unnamed arg +/// +/// The error member (__1) is either: +/// - An enum: extract variant names and tag values +/// - A basetype: ignored (no structured errors) +fn extract_function_signature( + hubris: &HubrisArchive, + goff: HubrisGoff, +) -> Result<(Vec, Vec)> { + let sig = hubris + .lookup_struct(goff) + .context("function variant is not a struct")?; + + // Extract args from __0 + let args_goff = + sig.lookup_member("__0").context("function has no __0 member")?.goff; + + let args = if let Ok(args_struct) = hubris.lookup_struct(args_goff) { + args_struct + .members + .iter() + .map(|m| FunctionArg { + name: m.name.clone(), + ty: resolve_type_name(hubris, m.goff), + size: resolve_type_size(hubris, m.goff), + }) + .collect() + } else { + match hubris.lookup_basetype(args_goff) { + Ok(basetype) if basetype.size == 0 => vec![], + _ => vec![FunctionArg { + name: "arg0".to_string(), + ty: resolve_type_name(hubris, args_goff), + size: resolve_type_size(hubris, args_goff), + }], + } + }; + + // Extract errors from __1 + let err_goff = + sig.lookup_member("__1").context("function has no __1 member")?.goff; + + let errors = if let Ok(err_enum) = hubris.lookup_enum(err_goff) { + err_enum + .variants + .iter() + .filter_map(|v| { + let tag = v.tag?; + Some(FunctionError { + code: u32::try_from(tag).ok()?, + name: v.name.to_string(), + }) + }) + .collect() + } else { + vec![] + }; + + Ok((args, errors)) +} + +/// Best-effort type name resolution from a DWARF goff. +fn resolve_type_name(hubris: &HubrisArchive, goff: HubrisGoff) -> String { + if let Ok(s) = hubris.lookup_struct(goff) { + return s.name.clone(); + } + if let Ok(e) = hubris.lookup_enum(goff) { + return e.name.clone(); + } + if let Ok(b) = hubris.lookup_basetype(goff) { + return match b.encoding { + HubrisEncoding::Unsigned => format!("u{}", b.size * 8), + HubrisEncoding::Signed => format!("i{}", b.size * 8), + HubrisEncoding::Float => format!("f{}", b.size * 8), + HubrisEncoding::Bool => "bool".to_string(), + HubrisEncoding::Unknown => format!("unknown{}", b.size * 8), + }; + } + "?".to_string() +} + +/// Best-effort type size resolution from a DWARF goff. +fn resolve_type_size( + hubris: &HubrisArchive, + goff: HubrisGoff, +) -> Option { + if let Ok(s) = hubris.lookup_struct(goff) { + return Some(s.size); + } + if let Ok(e) = hubris.lookup_enum(goff) { + return Some(e.size); + } + if let Ok(b) = hubris.lookup_basetype(goff) { + return Some(b.size); + } + None +} + +/// Extract Idol interface definitions from all tasks in the archive. +fn extract_idol_interfaces(hubris: &HubrisArchive) -> Vec { + let mut interfaces = vec![]; + + for i in 0..hubris.ntasks() { + let task = HubrisTask::Task(i as u32); + let module = match hubris.lookup_module(task) { + Ok(m) => m, + Err(_) => continue, + }; + + let iface = match &module.iface { + Some(iface) => iface, + None => continue, + }; + + let mut ops = vec![]; + for (idx, (op_name, op)) in iface.ops.iter().enumerate() { + let code = (idx + 1) as u16; // Idol ops are 1-based + + let args: Vec = op + .args + .iter() + .map(|(name, attr)| IdolArgInfo { + name: name.clone(), + ty: attr.ty.0.clone(), + }) + .collect(); + + let (reply, error) = match &op.reply { + idol::syntax::Reply::Result { ok, err } => { + let err_name = match err { + idol::syntax::Error::CLike(ty) => ty.0.clone(), + idol::syntax::Error::Complex(ty) => ty.0.clone(), + idol::syntax::Error::ServerDeath => { + "ServerDeath".to_string() + } + }; + (ok.ty.0.clone(), Some(err_name)) + } + idol::syntax::Reply::Simple(ty) => (ty.ty.0.clone(), None), + }; + + let leases: Vec = op + .leases + .iter() + .map(|(name, lease)| IdolLeaseInfo { + name: name.clone(), + ty: lease.ty.0.clone(), + read: lease.read, + write: lease.write, + }) + .collect(); + + let encoding = format!("{:?}", op.encoding); + + // Compute args_size and reply_size from DWARF. + let args_size = + compute_args_size(hubris, module, &iface.name, op_name); + let ok_type_name = match &op.reply { + idol::syntax::Reply::Result { ok, .. } => ok.ty.0.as_str(), + idol::syntax::Reply::Simple(ty) => ty.ty.0.as_str(), + }; + let reply_size = compute_reply_size( + hubris, + module, + &iface.name, + op_name, + ok_type_name, + &op.encoding, + ); + + ops.push(IdolOpInfo { + name: op_name.clone(), + code, + args, + args_size, + reply, + reply_size, + error, + encoding, + leases, + idempotent: op.idempotent, + }); + } + + interfaces.push(IdolInterfaceInfo { + name: iface.name.clone(), + task: module.name.clone(), + task_id: i as u32, + ops, + }); + } + + interfaces +} + +/// Compute the args struct size for an Idol operation. +/// +/// The args struct is named `{Interface}_{Operation}_ARGS` and lives +/// in the implementing task's module. +fn compute_args_size( + hubris: &HubrisArchive, + module: &humility::hubris::HubrisModule, + iface_name: &str, + op_name: &str, +) -> usize { + let struct_name = format!("{}_{}_ARGS", iface_name, op_name); + module + .lookup_struct_byname(hubris, &struct_name) + .ok() + .flatten() + .map(|s| s.size) + .unwrap_or(0) +} + +/// Compute the reply payload size for an Idol operation. +/// +/// Mirrors the lookup chain from `humility-idol::lookup_reply` and +/// `IdolOperation::reply_size()`: +/// 1. Look up the ok type name as basetype, enum, struct +/// 2. Fall back to `{Interface}_{Operation}_REPLY` +/// 3. Compute size based on encoding +fn compute_reply_size( + hubris: &HubrisArchive, + module: &humility::hubris::HubrisModule, + iface_name: &str, + op_name: &str, + ok_type_name: &str, + encoding: &idol::syntax::Encoding, +) -> usize { + // Find the ok type's goff using the same chain as humility-idol + let ok_goff = hubris + .lookup_basetype_byname(ok_type_name) + .ok() + .copied() + .or_else(|| { + module + .lookup_enum_byname(hubris, ok_type_name) + .ok() + .flatten() + .map(|e| e.goff) + }) + .or_else(|| { + module + .lookup_struct_byname(hubris, ok_type_name) + .ok() + .flatten() + .map(|s| s.goff) + }) + .or_else(|| { + let t = format!("{}_{}_REPLY", iface_name, op_name); + hubris.lookup_struct_byname(&t).ok().map(|s| s.goff) + }); + + match ok_goff { + Some(goff) => match encoding { + idol::syntax::Encoding::Zerocopy => { + hubris.typesize(goff).unwrap_or(0) + } + idol::syntax::Encoding::Ssmarshal + | idol::syntax::Encoding::Hubpack => { + hubris.hubpack_serialized_maxsize(goff).unwrap_or(0) + } + }, + None => 0, + } +} + +/// Extract HIFFY buffer sizes from variable sizes in DWARF. +/// +/// Returns the sizes and a list of warnings for any variables that +/// could not be found (where defaults were used). +fn extract_buffer_sizes( + hubris: &HubrisArchive, +) -> Result<(BufferSizes, Vec)> { + let mut warnings = Vec::new(); + + let text_size = lookup_var_size(hubris, "HIFFY_TEXT", 2048, &mut warnings); + let data_size = lookup_var_size(hubris, "HIFFY_DATA", 2048, &mut warnings); + let rstack_size = + lookup_var_size(hubris, "HIFFY_RSTACK", 2048, &mut warnings); + let scratch_size = + lookup_var_size(hubris, "HIFFY_SCRATCH", 512, &mut warnings); + + Ok(( + BufferSizes { + text: text_size, + data: data_size, + rstack: rstack_size, + scratch: scratch_size, + }, + warnings, + )) +} + +fn lookup_var_size( + hubris: &HubrisArchive, + name: &str, + default: usize, + warnings: &mut Vec, +) -> usize { + match hubris.lookup_variable(name) { + Ok(v) => v.size, + Err(_) => { + warnings.push(format!( + "{name} not found in archive, using default {default}" + )); + default + } + } +} diff --git a/humility-hif-assembler/src/assembler.rs b/humility-hif-assembler/src/assembler.rs new file mode 100644 index 000000000..25593219c --- /dev/null +++ b/humility-hif-assembler/src/assembler.rs @@ -0,0 +1,714 @@ +// 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/. + +//! HIF assembler: the public API for assembling and verifying +//! programs. +//! +//! Types are defined in [`types`](crate::types). Lowering logic is +//! in [`lower`](crate::lower). + +use std::collections::{BTreeSet, HashMap}; +use std::fmt; + +use anyhow::{Context, Result, bail}; + +use crate::bundle::{BUNDLE_VERSION, BundleMetadata, HifBundle}; +use crate::error::{HifError, HifErrorKind}; +use crate::lower::{RSTACK_BYTES_PER_RESULT, normalize_function_name}; +use crate::parser::parse; + +// Re-export types so `crate::assembler::Foo` still works for +// existing imports in tests and other modules. +pub use crate::types::*; + +/// The assembler, loaded from a [`TargetConfig`]. +/// +/// Create one per target, then call [`assemble`](Self::assemble) or +/// [`verify`](Self::verify) for each program. +pub struct HifAssembler { + pub(crate) config: TargetConfig, + pub(crate) buses: HashMap, + /// Functions by their canonical name (from DWARF). + pub(crate) functions: HashMap, + /// Normalized aliases: `strip('_').lowercase()` -> canonical name. + pub(crate) function_aliases: HashMap, +} + +/// Output of a successful assembly. +#[derive(Debug)] +pub struct AssembleOutput { + pub bundle: HifBundle, + /// The assembled ops, before serialization. Use these directly + /// with `HiffyContext::run()` to avoid a deserialize round-trip. + pub ops: Vec, + pub warnings: Vec, + /// Expected program statistics, computed from the source. + pub stats: crate::stats::ProgramStats, +} + +/// Output of `verify` -- assembly stats without binary output. +#[derive(Debug)] +pub struct VerifyReport { + /// Whether the program assembled without errors. + pub ok: bool, + /// Errors found during assembly. + pub errors: Vec, + /// Non-fatal warnings. + pub warnings: Vec, + /// Assembled text size in bytes (0 if errors). + pub text_bytes: usize, + /// Target HIFFY_TEXT limit. + pub text_limit: usize, + /// Estimated number of results the program produces. + pub estimated_results: usize, + /// Estimated result bytes. + pub estimated_rstack_bytes: usize, + /// Target HIFFY_RSTACK limit. + pub rstack_limit: usize, + /// HIF functions referenced. + pub functions_used: BTreeSet, + /// I2C buses referenced. + pub buses_used: BTreeSet, + /// Number of labels (loop nesting depth) used. + pub labels_used: usize, +} + +impl fmt::Display for VerifyReport { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if self.ok { + writeln!(f, "OK")?; + } else { + writeln!(f, "ERRORS:")?; + for err in &self.errors { + writeln!(f, " {err}")?; + } + } + for w in &self.warnings { + writeln!(f, " warning: {w}")?; + } + writeln!(f)?; + writeln!( + f, + " text: {:>5} / {} bytes{}", + self.text_bytes, + self.text_limit, + if self.text_bytes > self.text_limit { + " ** OVERFLOW **" + } else { + "" + } + )?; + writeln!( + f, + " rstack: {:>5} / {} bytes (est. {} results){}", + self.estimated_rstack_bytes, + self.rstack_limit, + self.estimated_results, + if self.estimated_rstack_bytes > self.rstack_limit { + " ** OVERFLOW **" + } else { + "" + } + )?; + writeln!(f, " labels: {:>5} / {MAX_LABELS}", self.labels_used)?; + if !self.functions_used.is_empty() { + writeln!( + f, + " functions: {}", + self.functions_used + .iter() + .cloned() + .collect::>() + .join(", ") + )?; + } + if !self.buses_used.is_empty() { + writeln!( + f, + " buses: {}", + self.buses_used.iter().cloned().collect::>().join(", ") + )?; + } + Ok(()) + } +} + +impl HifAssembler { + /// Create an assembler from a [`TargetConfig`]. + /// + /// This is the primary constructor. For archive-based + /// construction, use [`TargetConfig::from_archive_file`] or + /// [`TargetConfig::from_archive`] and pass the result here. + pub fn new(config: TargetConfig) -> Self { + let buses = + config.buses.iter().map(|b| (b.name.clone(), b.clone())).collect(); + let functions: HashMap = config + .functions + .iter() + .map(|f| (f.name.clone(), f.clone())) + .collect(); + + let mut function_aliases = HashMap::new(); + for name in functions.keys() { + let normalized = normalize_function_name(name); + function_aliases.insert(normalized, name.clone()); + } + + HifAssembler { config, buses, functions, function_aliases } + } + + /// The target configuration this assembler was built from. + pub fn target_config(&self) -> &TargetConfig { + &self.config + } + + /// List known I2C buses for this target. + pub fn list_buses(&self) -> Vec<&ResolvedBus> { + let mut buses: Vec<_> = self.buses.values().collect(); + buses.sort_by_key(|b| &b.name); + buses + } + + /// List HIF functions available on this target. + pub fn list_functions(&self) -> Vec<&FunctionInfo> { + let mut funcs: Vec<_> = self.functions.values().collect(); + funcs.sort_by_key(|f| f.id); + funcs + } + + /// Image ID from the archive. + pub fn image_id(&self) -> &[u8] { + &self.config.image_id + } + + /// Board name from the archive. + pub fn board(&self) -> &str { + &self.config.board + } + + /// Target buffer sizes. + pub fn buffer_sizes(&self) -> &BufferSizes { + &self.config.buffer_sizes + } + + /// Verify a program: assemble it and report stats and errors, but + /// don't produce binary output. + pub fn verify(&self, source: &str) -> VerifyReport { + let mut report = VerifyReport { + ok: false, + errors: vec![], + warnings: vec![], + text_bytes: 0, + text_limit: self.config.buffer_sizes.text, + estimated_results: 0, + estimated_rstack_bytes: 0, + rstack_limit: self.config.buffer_sizes.rstack, + functions_used: BTreeSet::new(), + buses_used: BTreeSet::new(), + labels_used: 0, + }; + + let parsed = match parse(source) { + Ok(p) => p, + Err(e) => { + report.errors.push(e); + return report; + } + }; + + match self.lower_program(&parsed) { + Ok(result) => { + report.warnings = result.warnings; + report.functions_used = result.functions_used; + report.buses_used = result.buses_used; + report.labels_used = result.labels_used; + report.estimated_results = result.estimated_results; + report.estimated_rstack_bytes = + result.estimated_results * RSTACK_BYTES_PER_RESULT; + + match postcard::to_allocvec::>(&result.ops) { + Ok(bytes) => { + report.text_bytes = bytes.len(); + } + Err(e) => { + report.errors.push(HifError { + line: 0, + col: None, + kind: HifErrorKind::Parse(format!( + "serialization failed: {e}" + )), + }); + } + } + } + Err(e) => { + report.errors.push(HifError { + line: 0, + col: None, + kind: HifErrorKind::Parse(format!("{e}")), + }); + } + } + + if report.text_bytes > report.text_limit { + report.errors.push(HifError { + line: 0, + col: None, + kind: HifErrorKind::TextOverflow { + program_bytes: report.text_bytes, + limit: report.text_limit, + }, + }); + } + if report.estimated_rstack_bytes > report.rstack_limit { + report.errors.push(HifError { + line: 0, + col: None, + kind: HifErrorKind::RstackOverflow { + estimated_bytes: report.estimated_rstack_bytes, + limit: report.rstack_limit, + }, + }); + } + if report.labels_used > MAX_LABELS { + report.errors.push(HifError { + line: 0, + col: None, + kind: HifErrorKind::LabelOverflow { + used: report.labels_used, + max: MAX_LABELS, + }, + }); + } + + report.ok = report.errors.is_empty(); + report + } + + /// Assemble a program from source text into a [`HifBundle`]. + pub fn assemble(&self, source: &str) -> Result { + let parsed = parse(source).map_err(|e| anyhow::anyhow!("{e}"))?; + let result = self.lower_program(&parsed)?; + + let text = postcard::to_allocvec(&result.ops) + .context("serializing HIF bytecode")?; + + if text.len() > self.config.buffer_sizes.text { + bail!( + "assembled program is {} bytes, \ + exceeds HIFFY_TEXT limit of {}", + text.len(), + self.config.buffer_sizes.text, + ); + } + + let metadata = BundleMetadata { + version: BUNDLE_VERSION, + image_id: self.config.image_id.clone(), + board: self.config.board.clone(), + text_size: text.len(), + data_size: result.data.len(), + target_text_size: self.config.buffer_sizes.text, + target_rstack_size: self.config.buffer_sizes.rstack, + functions_used: result.functions_used.into_iter().collect(), + source: None, + estimated_results: Some(result.estimated_results), + source_text: Some(source.to_string()), + }; + + let stats = crate::stats::compute_stats(&parsed.statements); + let ops = result.ops; + let bundle = HifBundle { metadata, text, data: result.data }; + + Ok(AssembleOutput { bundle, ops, warnings: result.warnings, stats }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + /// A minimal target config for testing. + fn test_config() -> TargetConfig { + TargetConfig { + image_id: vec![0xDE, 0xAD], + board: "test-board".into(), + buses: vec![ + ResolvedBus { + name: "mid".into(), + controller: 3, + port_index: 0, + port_name: "H".into(), + devices: vec![I2cDeviceInfo { + address: 0x48, + device: "sbtsi".into(), + name: Some("CPU".into()), + description: "CPU temperature".into(), + removable: false, + sensors: vec![SensorInfo { + name: "CPU".into(), + kind: "Temperature".into(), + }], + }], + muxes: vec![], + }, + ResolvedBus { + name: "front".into(), + controller: 2, + port_index: 1, + port_name: "F".into(), + devices: vec![], + muxes: vec![I2cMuxInfo { + address: 0x70, + segments: vec![I2cMuxSegment { + segment: 1, + devices: vec![I2cDeviceInfo { + address: 0x50, + device: "at24csw080".into(), + name: None, + description: "U.2 A VPD".into(), + removable: true, + sensors: vec![], + }], + }], + }], + }, + ], + // Names match the DWARF enum variants from a real Hubris + // hiffy task (PascalCase). The alias table handles + // snake_case lookups (e.g. i2c_read -> I2cRead). + functions: vec![ + FunctionInfo { + name: "Sleep".into(), + id: 0, + arg_count: 1, + args: vec![FunctionArg { + name: "ms".into(), + ty: "u32".into(), + size: Some(4), + }], + errors: vec![], + }, + FunctionInfo { + name: "Send".into(), + id: 1, + arg_count: 4, + args: vec![], + errors: vec![], + }, + FunctionInfo { + name: "I2cRead".into(), + id: 5, + arg_count: 7, + args: vec![], + errors: vec![], + }, + FunctionInfo { + name: "I2cWrite".into(), + id: 6, + arg_count: 8, + args: vec![], + errors: vec![], + }, + ], + buffer_sizes: BufferSizes { + text: 2048, + data: 2048, + rstack: 2048, + scratch: 512, + }, + idol_interfaces: vec![IdolInterfaceInfo { + name: "Sensor".into(), + task: "sensor".into(), + task_id: 7, + ops: vec![ + IdolOpInfo { + name: "get".into(), + code: 1, + args: vec![IdolArgInfo { + name: "id".into(), + ty: "SensorId".into(), + }], + args_size: 4, + reply: "f32".into(), + reply_size: 4, + error: Some("SensorError".into()), + encoding: "Hubpack".into(), + leases: vec![], + idempotent: true, + }, + IdolOpInfo { + name: "post".into(), + code: 8, + args: vec![ + IdolArgInfo { + name: "id".into(), + ty: "SensorId".into(), + }, + IdolArgInfo { + name: "value".into(), + ty: "f32".into(), + }, + IdolArgInfo { + name: "timestamp".into(), + ty: "u64".into(), + }, + ], + args_size: 16, + reply: "()".into(), + reply_size: 0, + error: Some("SensorError".into()), + encoding: "Hubpack".into(), + leases: vec![], + idempotent: false, + }, + ], + }], + } + } + + fn test_assembler() -> HifAssembler { + HifAssembler::new(test_config()) + } + + #[test] + fn assemble_simple_i2c_read() { + let c = test_assembler(); + let out = c.assemble("i2c_read mid 0x48 reg=0x00 2").unwrap(); + assert!(out.bundle.fits_in_target()); + assert!(!out.bundle.text.is_empty()); + assert_eq!(out.bundle.metadata.image_id, vec![0xDE, 0xAD]); + assert!( + out.bundle + .metadata + .functions_used + .contains(&"i2c_read".to_string()) + ); + } + + #[test] + fn assemble_i2c_read_with_mux() { + let c = test_assembler(); + let out = + c.assemble("i2c_read front 0x50 mux=0x70.1 reg=0x00 16").unwrap(); + assert!(out.bundle.fits_in_target()); + } + + #[test] + fn assemble_i2c_write() { + let c = test_assembler(); + let out = c.assemble("i2c_write mid 0x48 reg=0x01 0x00,0x80").unwrap(); + assert!(out.bundle.fits_in_target()); + } + + #[test] + fn assemble_i2c_write_too_many_bytes() { + let c = test_assembler(); + let bytes = (0..17) + .map(|i| format!("0x{:02x}", i)) + .collect::>() + .join(","); + let src = format!("i2c_write mid 0x48 {bytes}"); + let result = c.assemble(&src); + assert!(result.is_err()); + let msg = format!("{}", result.unwrap_err()); + assert!(msg.contains("maximum is 16"), "got: {msg}"); + } + + #[test] + fn assemble_repeat_loop() { + let c = test_assembler(); + let out = c + .assemble("repeat 10\n i2c_read mid 0x48 reg=0x00 2\nend") + .unwrap(); + assert!(out.bundle.fits_in_target()); + assert_eq!(out.bundle.metadata.estimated_results, Some(10)); + } + + #[test] + fn assemble_repeat_with_sleep() { + let c = test_assembler(); + let out = c + .assemble( + "repeat 5 sleep=50ms\n i2c_read mid 0x48 reg=0x00 2\nend", + ) + .unwrap(); + assert!(out.bundle.fits_in_target()); + } + + #[test] + fn assemble_repeat_zero_is_error() { + let c = test_assembler(); + let result = c.assemble("repeat 0\n i2c_read mid 0x48 2\nend"); + assert!(result.is_err()); + } + + #[test] + fn assemble_nested_repeat() { + let c = test_assembler(); + let src = "\ + repeat 10\n\ + repeat 5\n\ + i2c_read mid 0x48 2\n\ + end\n\ + end"; + let out = c.assemble(src).unwrap(); + assert_eq!(out.bundle.metadata.estimated_results, Some(50)); + } + + #[test] + fn assemble_sleep_over_100ms() { + let c = test_assembler(); + let out = c.assemble("sleep 250ms").unwrap(); + assert!(out.bundle.fits_in_target()); + } + + #[test] + fn assemble_raw_block() { + let c = test_assembler(); + let out = + c.assemble("raw {\n push 0x48\n push_none\n done\n}").unwrap(); + assert!(out.bundle.fits_in_target()); + } + + #[test] + fn assemble_raw_call_by_name() { + let c = test_assembler(); + let out = c.assemble("raw {\n call i2c_read\n done\n}").unwrap(); + assert!(out.bundle.fits_in_target()); + } + + #[test] + fn assemble_unknown_bus_is_error() { + let c = test_assembler(); + assert!(c.assemble("i2c_read nonexistent 0x48 2").is_err()); + } + + #[test] + fn assemble_unknown_function_is_error() { + let c = test_assembler(); + assert!(c.assemble("raw {\n call nonexistent\n}").is_err()); + } + + #[test] + fn verify_reports_stats() { + let c = test_assembler(); + let report = + c.verify("repeat 100\n i2c_read mid 0x48 reg=0x00 2\nend"); + assert!(report.ok); + assert_eq!(report.estimated_results, 100); + assert!(report.text_bytes > 0); + assert!(report.functions_used.contains("i2c_read")); + assert!(report.buses_used.contains("mid")); + assert_eq!(report.labels_used, 1); + } + + #[test] + fn verify_catches_overflow() { + let config = TargetConfig { + buffer_sizes: BufferSizes { + text: 10, + data: 10, + rstack: 10, + scratch: 10, + }, + ..test_config() + }; + let c = HifAssembler::new(config); + let report = c.verify("i2c_read mid 0x48 reg=0x00 2"); + assert!(!report.ok); + assert!( + report + .errors + .iter() + .any(|e| matches!(&e.kind, HifErrorKind::TextOverflow { .. })) + ); + } + + #[test] + fn target_config_roundtrip() { + let config = test_config(); + let json = serde_json::to_string(&config).unwrap(); + let parsed: TargetConfig = serde_json::from_str(&json).unwrap(); + assert_eq!(parsed.board, config.board); + assert_eq!(parsed.image_id, config.image_id); + assert_eq!(parsed.buses.len(), config.buses.len()); + assert_eq!(parsed.functions.len(), config.functions.len()); + } + + #[test] + fn explicit_bus_unknown_port_warns() { + let c = test_assembler(); + let report = c.verify("i2c_read 3.Z 0x48 2"); + assert!(report.ok); + assert!( + report.warnings.iter().any(|w| w.contains("not found")), + "expected warning about unknown port, got: {:?}", + report.warnings + ); + } + + #[test] + fn assemble_idol_call() { + let c = test_assembler(); + let out = c.assemble("idol Sensor.get id=3").unwrap(); + assert!(out.bundle.fits_in_target()); + assert!( + out.bundle.metadata.functions_used.contains(&"Send".to_string()) + ); + } + + #[test] + fn assemble_idol_call_multi_arg() { + let c = test_assembler(); + let out = c + .assemble("idol Sensor.post id=0 value=25.5 timestamp=1000") + .unwrap(); + assert!(out.bundle.fits_in_target()); + } + + #[test] + fn assemble_idol_unknown_interface() { + let c = test_assembler(); + let result = c.assemble("idol Nonexistent.get id=3"); + assert!(result.is_err()); + let msg = format!("{}", result.unwrap_err()); + assert!(msg.contains("unknown Idol interface"), "got: {msg}"); + } + + #[test] + fn assemble_idol_unknown_operation() { + let c = test_assembler(); + let result = c.assemble("idol Sensor.nonexistent id=3"); + assert!(result.is_err()); + let msg = format!("{}", result.unwrap_err()); + assert!(msg.contains("unknown operation"), "got: {msg}"); + } + + #[test] + fn assemble_idol_missing_arg() { + let c = test_assembler(); + let result = c.assemble("idol Sensor.get"); + assert!(result.is_err()); + let msg = format!("{}", result.unwrap_err()); + assert!(msg.contains("missing argument"), "got: {msg}"); + } + + #[test] + fn assemble_idol_extra_arg() { + let c = test_assembler(); + let result = c.assemble("idol Sensor.get id=3 extra=4"); + assert!(result.is_err()); + let msg = format!("{}", result.unwrap_err()); + assert!(msg.contains("unexpected argument"), "got: {msg}"); + } + + #[test] + fn assemble_idol_in_loop() { + let c = test_assembler(); + let out = c.assemble("repeat 10\n idol Sensor.get id=3\nend").unwrap(); + assert!(out.bundle.fits_in_target()); + assert_eq!(out.bundle.metadata.estimated_results, Some(10)); + } +} diff --git a/humility-hif-assembler/src/builder.rs b/humility-hif-assembler/src/builder.rs new file mode 100644 index 000000000..42176256c --- /dev/null +++ b/humility-hif-assembler/src/builder.rs @@ -0,0 +1,340 @@ +// 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/. + +//! Programmatic HIF program construction. +//! +//! [`ProgramBuilder`] provides a Rust API for generating HIF programs +//! without writing text. It emits the text format internally so the +//! same assembler validation and lowering pipeline is used. +//! +//! Primary use case: PRNG-driven fuzz and stress test generators. +//! +//! ```rust,ignore +//! let asm = HifAssembler::new(config); +//! let mut prog = ProgramBuilder::new(); +//! +//! prog.i2c_read("mid", 0x48, Some(0x00), 2); +//! prog.repeat(100, |body| { +//! body.i2c_read("front", 0x49, None, 1); +//! body.sleep_ms(10); +//! }); +//! prog.idol_call("Sensor", "get", &[("id", "0")]); +//! +//! let output = asm.assemble(&prog.finish())?; +//! ``` + +use std::fmt::Write; + +/// Builder for constructing HIF programs programmatically. +/// +/// Methods mirror the text language sugar. Call [`finish`](Self::finish) +/// to get the text source, then pass it to +/// [`HifAssembler::assemble`](crate::assembler::HifAssembler::assemble). +pub struct ProgramBuilder { + lines: Vec, + indent: usize, +} + +impl ProgramBuilder { + pub fn new() -> Self { + ProgramBuilder { lines: vec![], indent: 0 } + } + + /// Emit the assembled text source. + pub fn finish(&self) -> String { + self.lines.join("\n") + } + + /// Define a constant. + pub fn constant(&mut self, name: &str, value: &str) -> &mut Self { + self.push_line(&format!(".let {name} {value}")); + self + } + + /// Read bytes from an I2C device. + pub fn i2c_read( + &mut self, + bus: &str, + address: u8, + register: Option, + nbytes: u8, + ) -> &mut Self { + let mut line = format!("i2c_read {bus} 0x{address:02x}"); + if let Some(reg) = register { + write!(line, " reg=0x{reg:02x}").unwrap(); + } + write!(line, " {nbytes}").unwrap(); + self.push_line(&line); + self + } + + /// Read bytes from an I2C device behind a mux. + pub fn i2c_read_mux( + &mut self, + bus: &str, + address: u8, + mux_addr: u8, + segment: u8, + register: Option, + nbytes: u8, + ) -> &mut Self { + let mut line = format!( + "i2c_read {bus} 0x{address:02x} mux=0x{mux_addr:02x}.{segment}" + ); + if let Some(reg) = register { + write!(line, " reg=0x{reg:02x}").unwrap(); + } + write!(line, " {nbytes}").unwrap(); + self.push_line(&line); + self + } + + /// Write bytes to an I2C device. + pub fn i2c_write( + &mut self, + bus: &str, + address: u8, + register: Option, + data: &[u8], + ) -> &mut Self { + let mut line = format!("i2c_write {bus} 0x{address:02x}"); + if let Some(reg) = register { + write!(line, " reg=0x{reg:02x}").unwrap(); + } + let bytes: Vec = + data.iter().map(|b| format!("0x{b:02x}")).collect(); + write!(line, " {}", bytes.join(",")).unwrap(); + self.push_line(&line); + self + } + + /// Scan all addresses on a bus. + pub fn i2c_scan(&mut self, bus: &str) -> &mut Self { + self.push_line(&format!("i2c_scan {bus}")); + self + } + + /// Scan all registers of a device. + pub fn i2c_regscan(&mut self, bus: &str, address: u8) -> &mut Self { + self.push_line(&format!("i2c_regscan {bus} 0x{address:02x}")); + self + } + + /// Call an Idol interface operation. + pub fn idol_call( + &mut self, + interface: &str, + operation: &str, + args: &[(&str, &str)], + ) -> &mut Self { + let mut line = format!("idol {interface}.{operation}"); + for (k, v) in args { + write!(line, " {k}={v}").unwrap(); + } + self.push_line(&line); + self + } + + /// Sleep for a number of milliseconds. + pub fn sleep_ms(&mut self, ms: u32) -> &mut Self { + self.push_line(&format!("sleep {ms}ms")); + self + } + + /// Repeat a block of operations. + /// + /// The closure receives a nested builder for the loop body. + pub fn repeat( + &mut self, + count: u32, + body: impl FnOnce(&mut ProgramBuilder), + ) -> &mut Self { + self.push_line(&format!("repeat {count}")); + self.indent += 1; + let mut inner = ProgramBuilder { lines: vec![], indent: self.indent }; + body(&mut inner); + self.lines.extend(inner.lines); + self.indent -= 1; + self.push_line("end"); + self + } + + /// Repeat with a sleep between iterations. + pub fn repeat_with_sleep( + &mut self, + count: u32, + sleep_ms: u32, + body: impl FnOnce(&mut ProgramBuilder), + ) -> &mut Self { + self.push_line(&format!("repeat {count} sleep={sleep_ms}ms")); + self.indent += 1; + let mut inner = ProgramBuilder { lines: vec![], indent: self.indent }; + body(&mut inner); + self.lines.extend(inner.lines); + self.indent -= 1; + self.push_line("end"); + self + } + + /// Call any HIF function by name with optional numeric arguments. + pub fn call(&mut self, function: &str, args: &[u32]) -> &mut Self { + let mut line = format!("call {function}"); + for arg in args { + line.push_str(&format!(" {arg}")); + } + self.push_line(&line); + self + } + + /// Add a raw HIF instruction line. + pub fn raw_op(&mut self, instruction: &str) -> &mut Self { + self.push_line(instruction); + self + } + + /// Add a comment. + pub fn comment(&mut self, text: &str) -> &mut Self { + self.push_line(&format!("# {text}")); + self + } + + fn push_line(&mut self, line: &str) { + let indent = " ".repeat(self.indent); + self.lines.push(format!("{indent}{line}")); + } +} + +impl Default for ProgramBuilder { + fn default() -> Self { + Self::new() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn build_simple_read() { + let mut prog = ProgramBuilder::new(); + prog.i2c_read("mid", 0x48, Some(0x00), 2); + assert_eq!(prog.finish(), "i2c_read mid 0x48 reg=0x00 2"); + } + + #[test] + fn build_muxed_read() { + let mut prog = ProgramBuilder::new(); + prog.i2c_read_mux("front", 0x50, 0x70, 1, Some(0x00), 16); + assert_eq!(prog.finish(), "i2c_read front 0x50 mux=0x70.1 reg=0x00 16"); + } + + #[test] + fn build_write() { + let mut prog = ProgramBuilder::new(); + prog.i2c_write("mid", 0x48, Some(0x01), &[0x00, 0x80]); + assert_eq!(prog.finish(), "i2c_write mid 0x48 reg=0x01 0x00,0x80"); + } + + #[test] + fn build_repeat_loop() { + let mut prog = ProgramBuilder::new(); + prog.repeat(100, |body| { + body.i2c_read("mid", 0x48, Some(0x00), 2); + }); + let src = prog.finish(); + assert!(src.contains("repeat 100")); + assert!(src.contains(" i2c_read mid 0x48 reg=0x00 2")); + assert!(src.contains("end")); + } + + #[test] + fn build_nested_repeat() { + let mut prog = ProgramBuilder::new(); + prog.repeat(10, |outer| { + outer.repeat(5, |inner| { + inner.i2c_read("mid", 0x48, None, 1); + }); + }); + let src = prog.finish(); + assert!(src.contains(" i2c_read")); + } + + #[test] + fn build_idol_call() { + let mut prog = ProgramBuilder::new(); + prog.idol_call("Sensor", "get", &[("id", "0")]); + assert_eq!(prog.finish(), "idol Sensor.get id=0"); + } + + #[test] + fn build_complex_program() { + let mut prog = ProgramBuilder::new(); + prog.comment("stress test"); + prog.constant("ADDR", "0x48"); + prog.repeat_with_sleep(50, 10, |body| { + body.i2c_read("mid", 0x48, Some(0x00), 2); + body.idol_call("Sensor", "get", &[("id", "0")]); + }); + + let src = prog.finish(); + assert!(src.contains("# stress test")); + assert!(src.contains(".let ADDR 0x48")); + assert!(src.contains("repeat 50 sleep=10ms")); + assert!(src.contains("end")); + } + + #[test] + fn build_and_assemble() { + // Integration: build a program and assemble it + use crate::assembler::HifAssembler; + use crate::types::*; + + let config = TargetConfig { + image_id: vec![0xDE, 0xAD], + board: "test".into(), + buses: vec![ResolvedBus { + name: "mid".into(), + controller: 3, + port_index: 0, + port_name: "H".into(), + devices: vec![], + muxes: vec![], + }], + functions: vec![ + FunctionInfo { + name: "Sleep".into(), + id: 0, + arg_count: 1, + args: vec![], + errors: vec![], + }, + FunctionInfo { + name: "I2cRead".into(), + id: 5, + arg_count: 7, + args: vec![], + errors: vec![], + }, + ], + buffer_sizes: BufferSizes { + text: 2048, + data: 2048, + rstack: 2048, + scratch: 512, + }, + idol_interfaces: vec![], + }; + + let asm = HifAssembler::new(config); + let mut prog = ProgramBuilder::new(); + prog.repeat(10, |body| { + body.i2c_read("mid", 0x48, Some(0x00), 2); + body.sleep_ms(5); + }); + + let out = asm.assemble(&prog.finish()).expect("assemble failed"); + assert!(out.bundle.fits_in_target()); + assert_eq!(out.bundle.metadata.estimated_results, Some(10)); + } +} diff --git a/humility-hif-assembler/src/bundle.rs b/humility-hif-assembler/src/bundle.rs new file mode 100644 index 000000000..7b88717c5 --- /dev/null +++ b/humility-hif-assembler/src/bundle.rs @@ -0,0 +1,298 @@ +// 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/. + +//! Assembled HIF program bundle: metadata + binary bytecode. +//! +//! A bundle is the output of assembly. It contains: +//! +//! - [`BundleMetadata`] — JSON-serializable header with image ID, board +//! name, sizes, and provenance. +//! - `text` — postcard-serialized HIF ops, ready for `HIFFY_TEXT`. +//! - `data` — optional payload for `HIFFY_DATA` (used by bulk writes). +//! +//! The bundle can be written to a file (`.hifb` by convention) and +//! loaded later for execution. The file format is a JSON metadata +//! line followed by a newline, then the raw binary text and data +//! sections. + +use anyhow::{Context, Result, bail}; +use hif::FunctionResult; +use postcard::take_from_bytes; +use serde::{Deserialize, Serialize}; +use std::fmt; +use std::io::Write; +use std::path::Path; + +/// Metadata embedded in every assembled bundle. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct BundleMetadata { + /// Bundle format version. + pub version: u32, + /// Image ID from the Hubris archive used for assembly. + /// The runner must check this against the target before uploading. + pub image_id: Vec, + /// Board name from the archive (e.g. "cosmo-b-dev"). + pub board: String, + /// Assembled program size in bytes (length of `text`). + pub text_size: usize, + /// Data section size in bytes (length of `data`). + pub data_size: usize, + /// Maximum HIFFY_TEXT on this target. + pub target_text_size: usize, + /// Maximum HIFFY_RSTACK on this target. + pub target_rstack_size: usize, + /// HIF functions referenced by this program. + pub functions_used: Vec, + /// Source file or description, if known. + pub source: Option, + /// Estimated number of results the program will produce. + pub estimated_results: Option, + /// Original source text, for traceability. + pub source_text: Option, +} + +/// A assembled HIF program ready for upload and execution. +#[derive(Debug, Clone)] +pub struct HifBundle { + pub metadata: BundleMetadata, + /// Postcard-serialized HIF ops for HIFFY_TEXT. + pub text: Vec, + /// Optional data for HIFFY_DATA. + pub data: Vec, +} + +/// A single result from a HIF function call. +#[derive(Debug, Clone)] +pub enum HifResult { + /// Function returned successfully with payload bytes. + Success(Vec), + /// Function returned an error code. + Error(u32), +} + +impl HifResult { + pub fn is_ok(&self) -> bool { + matches!(self, HifResult::Success(_)) + } + + pub fn is_err(&self) -> bool { + matches!(self, HifResult::Error(_)) + } + + /// Get the payload bytes if successful. + pub fn payload(&self) -> Option<&[u8]> { + match self { + HifResult::Success(data) => Some(data), + HifResult::Error(_) => None, + } + } + + /// Get the error code if failed. + pub fn error_code(&self) -> Option { + match self { + HifResult::Success(_) => None, + HifResult::Error(code) => Some(*code), + } + } +} + +impl fmt::Display for HifResult { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + HifResult::Success(data) => { + write!(f, "Ok({:02x?})", data) + } + HifResult::Error(code) => { + write!(f, "Err({})", code) + } + } + } +} + +/// Current bundle file format version. +pub const BUNDLE_VERSION: u32 = 1; + +impl HifBundle { + /// Check whether the assembled program fits in the target's + /// HIFFY_TEXT buffer. + pub fn fits_in_target(&self) -> bool { + self.text.len() <= self.metadata.target_text_size + } + + /// Validate that this bundle's image ID matches the given target + /// image ID. + pub fn validate_image_id(&self, target_id: &[u8]) -> Result<()> { + if self.metadata.image_id != target_id { + bail!( + "image ID mismatch: bundle was assembled for {:02x?}, \ + target is {:02x?}", + self.metadata.image_id, + target_id, + ); + } + Ok(()) + } + + /// Decode results from the HIFFY_RSTACK after program execution. + /// + /// The `rstack` bytes are the raw contents of the `HIFFY_RSTACK` + /// buffer read from the target after the program completes. + /// Results are postcard-encoded `FunctionResult` values terminated + /// by `FunctionResult::Done`. + /// + /// ```rust,ignore + /// let results = HifBundle::decode_results(&rstack_bytes)?; + /// for (i, r) in results.iter().enumerate() { + /// println!("[{i}] {r}"); + /// } + /// ``` + pub fn decode_results(rstack: &[u8]) -> Result> { + let mut results = vec![]; + let mut remaining = rstack; + + loop { + if remaining.is_empty() { + break; + } + + let (rval, next) = take_from_bytes::(remaining) + .map_err(|e| { + anyhow::anyhow!("decoding result {}: {e}", results.len()) + })?; + + match rval { + FunctionResult::Done => break, + FunctionResult::Success(payload) => { + results.push(HifResult::Success(payload.to_vec())); + } + FunctionResult::Failure(code) => { + results.push(HifResult::Error(code)); + } + } + + remaining = next; + } + + Ok(results) + } + + /// Write the bundle to a file. + /// + /// Format: JSON metadata line, newline, then binary text section, + /// then binary data section. + pub fn write_to_file(&self, path: impl AsRef) -> Result<()> { + let path = path.as_ref(); + let mut f = std::fs::File::create(path) + .with_context(|| format!("creating {}", path.display()))?; + let meta_json = serde_json::to_string(&self.metadata)?; + f.write_all(meta_json.as_bytes())?; + f.write_all(b"\n")?; + f.write_all(&self.text)?; + f.write_all(&self.data)?; + Ok(()) + } + + /// Read a bundle from a file. + pub fn read_from_file(path: impl AsRef) -> Result { + let path = path.as_ref(); + let contents = std::fs::read(path) + .with_context(|| format!("reading {}", path.display()))?; + + let newline_pos = contents + .iter() + .position(|&b| b == b'\n') + .context("bundle file missing metadata line")?; + + let meta_json = &contents[..newline_pos]; + let metadata: BundleMetadata = serde_json::from_slice(meta_json) + .context("parsing bundle metadata")?; + + let binary = &contents[newline_pos + 1..]; + if binary.len() < metadata.text_size { + bail!( + "bundle file is truncated: expected at least {} bytes \ + after metadata, got {}", + metadata.text_size, + binary.len(), + ); + } + let text = binary[..metadata.text_size].to_vec(); + let data = binary[metadata.text_size..].to_vec(); + + Ok(HifBundle { metadata, text, data }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use postcard::to_allocvec; + + fn encode_results(results: &[FunctionResult]) -> Vec { + let mut buf = vec![]; + for r in results { + buf.extend_from_slice( + &to_allocvec(r).expect("encoding FunctionResult"), + ); + } + buf + } + + #[test] + fn decode_empty_rstack() { + let rstack = encode_results(&[FunctionResult::Done]); + let results = HifBundle::decode_results(&rstack).unwrap(); + assert!(results.is_empty()); + } + + #[test] + fn decode_success_results() { + let rstack = encode_results(&[ + FunctionResult::Success(&[0x1a, 0x80]), + FunctionResult::Success(&[0x1b, 0x00]), + FunctionResult::Done, + ]); + let results = HifBundle::decode_results(&rstack).unwrap(); + assert_eq!(results.len(), 2); + assert!(results[0].is_ok()); + assert_eq!(results[0].payload().unwrap(), &[0x1a, 0x80]); + assert_eq!(results[1].payload().unwrap(), &[0x1b, 0x00]); + } + + #[test] + fn decode_error_results() { + let rstack = encode_results(&[ + FunctionResult::Failure(3), // NoDevice + FunctionResult::Done, + ]); + let results = HifBundle::decode_results(&rstack).unwrap(); + assert_eq!(results.len(), 1); + assert!(results[0].is_err()); + assert_eq!(results[0].error_code(), Some(3)); + } + + #[test] + fn decode_mixed_results() { + let rstack = encode_results(&[ + FunctionResult::Success(&[0x48]), + FunctionResult::Failure(3), + FunctionResult::Success(&[0x49]), + FunctionResult::Done, + ]); + let results = HifBundle::decode_results(&rstack).unwrap(); + assert_eq!(results.len(), 3); + assert!(results[0].is_ok()); + assert!(results[1].is_err()); + assert!(results[2].is_ok()); + } + + #[test] + fn decode_display() { + let ok = HifResult::Success(vec![0x1a, 0x80]); + assert_eq!(format!("{ok}"), "Ok([1a, 80])"); + + let err = HifResult::Error(3); + assert_eq!(format!("{err}"), "Err(3)"); + } +} diff --git a/humility-hif-assembler/src/error.rs b/humility-hif-assembler/src/error.rs new file mode 100644 index 000000000..a442e5cb4 --- /dev/null +++ b/humility-hif-assembler/src/error.rs @@ -0,0 +1,114 @@ +// 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/. + +//! Error types for the HIF assembler. + +use std::fmt; + +/// An error discovered during parsing or assembly. +#[derive(Debug, Clone)] +pub struct HifError { + pub line: usize, + /// Column (1-based character offset), if known. + pub col: Option, + pub kind: HifErrorKind, +} + +#[derive(Debug, Clone)] +pub enum HifErrorKind { + /// Syntax error in the source text. + Parse(String), + /// Reference to an unknown I2C bus name. + UnknownBus(String), + /// Reference to an unknown HIF function. + UnknownFunction(String), + /// Reference to an unknown Idol interface or operation. + UnknownIdolOp { interface: String, operation: String }, + /// Argument type or count mismatch for an Idol call. + IdolArgError(String), + /// Program text exceeds HIFFY_TEXT buffer. + TextOverflow { program_bytes: usize, limit: usize }, + /// Estimated results exceed HIFFY_RSTACK buffer. + RstackOverflow { estimated_bytes: usize, limit: usize }, + /// Too many nested loops (max 4 labels). + LabelOverflow { used: usize, max: usize }, + /// Unmatched `repeat`/`end` block. + UnmatchedBlock(String), + /// Undefined constant reference. + UndefinedConstant(String), + /// Invalid numeric literal. + InvalidNumber(String), + /// A `.let` name shadows a built-in or prior definition. + ShadowedName(String), +} + +impl fmt::Display for HifError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if self.line == 0 { + // Non-source errors (limit checks, serialization failures) + write!(f, "{}", self.kind) + } else { + match self.col { + Some(col) => { + write!(f, "line {}:{}: {}", self.line, col, self.kind) + } + None => write!(f, "line {}: {}", self.line, self.kind), + } + } + } +} + +impl fmt::Display for HifErrorKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Parse(msg) => write!(f, "parse error: {msg}"), + Self::UnknownBus(name) => { + write!(f, "unknown I2C bus '{name}'") + } + Self::UnknownFunction(name) => { + write!(f, "unknown HIF function '{name}'") + } + Self::UnknownIdolOp { interface, operation } => { + write!(f, "unknown Idol operation '{interface}.{operation}'") + } + Self::IdolArgError(msg) => { + write!(f, "Idol argument error: {msg}") + } + Self::TextOverflow { program_bytes, limit } => { + write!( + f, + "program is {program_bytes} bytes, \ + exceeds HIFFY_TEXT limit of {limit} bytes" + ) + } + Self::RstackOverflow { estimated_bytes, limit } => { + write!( + f, + "estimated result size is {estimated_bytes} bytes, \ + exceeds HIFFY_RSTACK limit of {limit} bytes" + ) + } + Self::LabelOverflow { used, max } => { + write!( + f, + "program uses {used} labels, exceeds maximum of {max}" + ) + } + Self::UnmatchedBlock(tok) => { + write!(f, "unmatched '{tok}'") + } + Self::UndefinedConstant(name) => { + write!(f, "undefined constant '${name}'") + } + Self::InvalidNumber(s) => { + write!(f, "invalid number '{s}'") + } + Self::ShadowedName(name) => { + write!(f, "'{name}' shadows a built-in or prior definition") + } + } + } +} + +impl std::error::Error for HifError {} diff --git a/humility-hif-assembler/src/lib.rs b/humility-hif-assembler/src/lib.rs new file mode 100644 index 000000000..6ef24b621 --- /dev/null +++ b/humility-hif-assembler/src/lib.rs @@ -0,0 +1,218 @@ +// 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/. + +//! # humility-hif-assembler +//! +//! Assemble HIF (Hubris Interchange Format) programs from a text-based +//! language into bytecode that can be executed by the `hiffy` task on a +//! Hubris target. +//! +//! ## Overview +//! +//! A HIF program describes operations to run on a Hubris SP or RoT. +//! The source text is written in terms of bus names, device addresses, +//! and Idol interface calls. The assembler resolves these symbolic +//! references against a Hubris archive (`.zip`), producing bytecode +//! bound to that specific image. +//! +//! The archive supplies: +//! +//! - **Image ID** — embedded in the assembled bundle so the runner can +//! reject mismatched uploads. +//! - **HIF function table** — maps names like `i2c_read` to numeric IDs +//! (extracted from DWARF debug info in the `hiffy` task). +//! - **I2C topology** — bus names, controller indices, port indices, mux +//! configurations, and known device addresses. +//! - **Idol interfaces** — task IDs, operation codes, argument encoding, +//! and reply sizes for RPC calls. +//! - **Buffer sizes** — `HIFFY_TEXT`, `HIFFY_RSTACK`, etc. so the +//! assembler can reject programs that won't fit. +//! +//! ## Text Format +//! +//! Programs use a line-oriented syntax with comments (`#`), constants +//! (`.let`), and sugar for common operations. +//! +//! ### I2C Operations +//! +//! ```text +//! # Read 2 bytes from register 0x00 of device 0x48 on the "mid" bus +//! i2c_read mid 0x48 reg=0x00 2 +//! +//! # Read through a mux: bus, address, mux=., reg, nbytes +//! i2c_read front 0x50 mux=0x70.1 reg=0x00 16 +//! +//! # Write bytes to a register +//! i2c_write mid 0x48 reg=0x01 0x00,0x80 +//! +//! # Scan all addresses on a bus (like `humility i2c --scan`) +//! i2c_scan mid +//! +//! # Scan all registers of a device (like `humility i2c -s `) +//! i2c_regscan mid 0x48 +//! ``` +//! +//! Bus names (e.g. `mid`, `front`, `rear`, `m2`) come from the I2C +//! bus definitions in the archive's `app.toml`. You can also use +//! explicit `.` syntax (e.g. `3.H`). +//! +//! ### Idol RPC Calls +//! +//! ```text +//! # Call an Idol interface operation +//! idol Sensor.get id=3 +//! idol Thermal.get_mode +//! idol Validate.validate_i2c index=0 +//! ``` +//! +//! The assembler resolves the interface and operation names from the +//! `.idolatry` sections in the archive, encodes arguments, and emits +//! the appropriate `Send` call sequence. +//! +//! ### Loops +//! +//! ```text +//! # Repeat a block N times +//! repeat 200 +//! i2c_read mid 0x48 reg=0x00 2 +//! end +//! +//! # With a sleep between iterations +//! repeat 50 sleep=10ms +//! i2c_read mid 0x48 reg=0x00 2 +//! i2c_read mid 0x49 reg=0x00 2 +//! end +//! ``` +//! +//! Loops assemble to `Label`/`BranchGreaterThan` pairs, consuming one +//! of the four available HIF labels per nesting level. +//! +//! ### Constants +//! +//! ```text +//! .let TEMP_REG 0x00 +//! .let ITERATIONS 200 +//! +//! repeat $ITERATIONS +//! i2c_read mid 0x48 reg=$TEMP_REG 2 +//! end +//! ``` +//! +//! ### Sleep +//! +//! ```text +//! sleep 50ms # pause for 50 milliseconds (max 100ms per call) +//! ``` +//! +//! ### Generic Function Calls +//! +//! Any HIF function can be called by name with optional numeric +//! arguments: +//! +//! ```text +//! call QspiReadId +//! call GpioInput 5 +//! ``` +//! +//! ### Raw Ops +//! +//! For low-level control, raw HIF instructions are available. +//! Constants are expanded inside raw blocks: +//! +//! ```text +//! raw { +//! push 0x48 +//! push_none +//! push 2 +//! call I2cRead +//! drop_n 7 +//! } +//! ``` +//! +//! ## Assembly +//! +//! ```rust,ignore +//! let config = TargetConfig::from_archive_file("sidecar-b-lab.zip")?; +//! let asm = HifAssembler::new(config); +//! +//! // Verify a program without producing binary output +//! let report = asm.verify("repeat 100\n i2c_read mid 0x48 reg=0x00 2\nend"); +//! println!("{}", report); +//! +//! // Assemble to a bundle +//! let output = asm.assemble("i2c_read mid 0x48 reg=0x00 2")?; +//! output.bundle.write_to_file("program.hifb")?; +//! println!("{}", output.stats); +//! ``` +//! +//! ## Building Programs Programmatically +//! +//! When generating programs from a test harness (e.g. PRNG-driven +//! fuzzing), use [`ProgramBuilder`] instead of text: +//! +//! ```rust,ignore +//! let mut prog = ProgramBuilder::new(); +//! prog.repeat(200, |body| { +//! body.i2c_read("rear", 0x48, Some(0x00), 2); +//! body.call("QspiReadStatus", &[]); +//! }); +//! let output = asm.assemble(&prog.finish())?; +//! ``` +//! +//! ## Verify Mode +//! +//! [`HifAssembler::verify`] assembles the program and returns a +//! [`VerifyReport`] without producing binary output. The report +//! includes: +//! +//! - Whether the program text fits in `HIFFY_TEXT` +//! - Estimated result count and whether it fits in `HIFFY_RSTACK` +//! - Functions and buses referenced +//! - Warnings (e.g. addressing a device not in the archive config) +//! - Errors (unknown bus, unknown function, label overflow, etc.) +//! +//! ## Disassembly +//! +//! [`HifAssembler::disassemble`] formats ops as raw assembler syntax +//! with postcard byte encoding and symbolic annotations in trailing +//! comments. The left column is valid `raw { }` input. Function +//! IDs are resolved back to names, push values are annotated with +//! their role (controller, port, address, etc.), and bus/device +//! names are shown where they can be inferred from the TargetConfig. +//! +//! ## Relationship to humility-hiffy +//! +//! This crate intentionally overlaps with parts of `humility-hiffy` +//! (DWARF function discovery, result decoding, I2C parameter +//! resolution). The overlap exists because `humility-hiffy` is +//! coupled to a live target connection and cannot be used for offline +//! program construction or fixture generation. +//! +//! The types here (`TargetConfig`, `FunctionInfo`, `HifResult`) are +//! designed as the serializable contracts that a future humility +//! library rework (RFD 659) can adopt. See `README.md` for the full +//! convergence plan. + +pub mod archive; +pub mod assembler; +pub mod builder; +pub mod bundle; +pub mod error; +pub mod listing; +pub mod lower; +pub mod parser; +pub mod stats; +pub mod types; + +pub use assembler::{AssembleOutput, HifAssembler, VerifyReport}; +pub use builder::ProgramBuilder; +pub use bundle::{BundleMetadata, HifBundle, HifResult}; +pub use error::HifError; +pub use parser::{ParsedProgram, Statement}; +pub use types::{ + BufferSizes, FunctionArg, FunctionError, FunctionInfo, I2C_WRITE_MAX_DATA, + I2cDeviceInfo, I2cMuxInfo, I2cMuxSegment, IdolArgInfo, IdolInterfaceInfo, + IdolLeaseInfo, IdolOpInfo, MAX_LABELS, ResolvedBus, SensorInfo, + TargetConfig, +}; diff --git a/humility-hif-assembler/src/listing.rs b/humility-hif-assembler/src/listing.rs new file mode 100644 index 000000000..fbb757c94 --- /dev/null +++ b/humility-hif-assembler/src/listing.rs @@ -0,0 +1,273 @@ +// 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/. + +//! Listing commands: inspect what a Hubris archive offers for HIF +//! program assembly. +//! +//! These functions extract and format information from the archive +//! without needing a target connection. They are useful for +//! discovering bus names, available functions, and Idol interfaces +//! before writing a program. + +use crate::assembler::HifAssembler; +use crate::types::{FunctionInfo, ResolvedBus}; +use std::fmt; + +/// A formatted listing of I2C buses. +pub struct BusListing<'a> { + pub buses: Vec<&'a ResolvedBus>, +} + +impl fmt::Display for BusListing<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + writeln!( + f, + "{:<16} {:>4} {:<6} {:<6}", + "BUS", "CTRL", "PORT", "NAME" + )?; + for bus in &self.buses { + writeln!( + f, + "{:<16} {:>4} {:<6} {}", + bus.name, bus.controller, bus.port_index, bus.port_name, + )?; + } + Ok(()) + } +} + +/// A formatted listing of HIF functions. +pub struct FunctionListing<'a> { + pub functions: Vec<&'a FunctionInfo>, +} + +impl fmt::Display for FunctionListing<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + writeln!(f, "{:>3} {:<24} {:>4}", "ID", "NAME", "ARGS")?; + for func in &self.functions { + writeln!( + f, + "{:>3} {:<24} {:>4}", + func.id, func.name, func.arg_count, + )?; + } + Ok(()) + } +} + +impl HifAssembler { + /// Produce a formatted listing of I2C buses. + pub fn bus_listing(&self) -> BusListing<'_> { + BusListing { buses: self.list_buses() } + } + + /// Produce a formatted listing of HIF functions. + pub fn function_listing(&self) -> FunctionListing<'_> { + FunctionListing { functions: self.list_functions() } + } + + /// Disassemble ops into raw syntax with hex and symbolic comments. + /// + /// The output is wrapped in `raw { }` and is valid assembler input. + /// Each line has the raw op, byte offset, hex encoding, and a + /// symbolic annotation where the op's role can be inferred from + /// context (e.g. "controller (mid)" for a push before I2cRead). + pub fn disassemble(&self, ops: &[hif::Op]) -> String { + use postcard::to_allocvec; + + let annotations = annotate_ops(ops, self); + + let mut out = String::new(); + out.push_str("raw {\n"); + + let mut offset = 0usize; + for (i, op) in ops.iter().enumerate() { + let raw = format_op(op, self); + let hex = to_allocvec(op) + .map(|bytes| { + bytes + .iter() + .map(|b| format!("{b:02x}")) + .collect::>() + .join(" ") + }) + .unwrap_or_else(|_| "??".into()); + + let annotation = annotations + .get(i) + .map(|s| format!(" {s}")) + .unwrap_or_default(); + + out.push_str(&format!( + " {raw:<26} # {offset:02x}: {hex}{annotation}\n", + )); + offset += to_allocvec(op).map(|b| b.len()).unwrap_or(0); + } + + out.push_str("}\n"); + out + } +} + +/// Produce symbolic annotations for each op by recognizing patterns. +fn annotate_ops(ops: &[hif::Op], asm: &HifAssembler) -> Vec { + let mut notes: Vec = vec![String::new(); ops.len()]; + + // Find I2cRead/Write call patterns: 7 pushes + Call + DropN(7) + let i2c_read_id = asm + .list_functions() + .iter() + .find(|f| f.name.eq_ignore_ascii_case("I2cRead")) + .map(|f| f.id); + let i2c_write_id = asm + .list_functions() + .iter() + .find(|f| f.name.eq_ignore_ascii_case("I2cWrite")) + .map(|f| f.id); + + for (i, op) in ops.iter().enumerate() { + if let hif::Op::Call(hif::TargetFunction(id)) = op { + if Some(*id) == i2c_read_id || Some(*id) == i2c_write_id { + // Annotate the 7 pushes before this call + if i >= 7 { + let labels = [ + "controller", + "port", + "mux", + "segment", + "address", + "register", + if Some(*id) == i2c_read_id { + "nbytes" + } else { + "data/len" + }, + ]; + for (j, label) in labels.iter().enumerate() { + let idx = i - 7 + j; + // Enrich with bus/device names where possible + let extra = match (j, &ops[idx]) { + (0, hif::Op::Push(ctrl)) => { + // Find bus name for this controller + asm.target_config() + .buses + .iter() + .find(|b| b.controller == *ctrl) + .map(|b| format!(" ({})", b.name)) + .unwrap_or_default() + } + (4, hif::Op::Push(addr)) => { + // Find device name for this address + // on any bus (best effort) + asm.target_config() + .buses + .iter() + .flat_map(|b| b.devices.iter()) + .find(|d| d.address == *addr) + .map(|d| format!(" ({})", d.device)) + .unwrap_or_default() + } + _ => String::new(), + }; + notes[idx] = format!("{label}{extra}"); + } + } + } + + // Annotate Send calls (Idol): task_id, op_code + let send_id = asm + .list_functions() + .iter() + .find(|f| f.name == "Send") + .map(|f| f.id); + if Some(*id) == send_id && i >= 4 { + if let hif::Op::Push(task_id) = ops[i - 4] { + let task_name = asm + .target_config() + .idol_interfaces + .iter() + .find(|iface| iface.task_id == task_id as u32) + .map(|iface| iface.task.as_str()) + .unwrap_or("?"); + notes[i - 4] = format!("task ({task_name})"); + } + notes[i - 3] = "op_code".into(); + } + } + + // Annotate loop patterns + if let hif::Op::BranchGreaterThan(_) + | hif::Op::BranchGreaterThanOrEqualTo(_) = op + { + if i >= 2 { + if let hif::Op::Add = ops[i - 2] { + notes[i - 2] = "counter += 1".into(); + notes[i - 1] = "limit".into(); + notes[i] = "loop".into(); + } + } + } + + if let hif::Op::Label(_) = op { + notes[i] = "loop_start".into(); + } + } + + notes +} + +/// Format a single op as raw assembler syntax. +fn format_op(op: &hif::Op, asm: &HifAssembler) -> String { + match op { + hif::Op::Push(v) => { + if *v > 9 { + format!("push 0x{v:02x}") + } else { + format!("push {v}") + } + } + hif::Op::Push16(v) => format!("push16 0x{v:04x}"), + hif::Op::Push32(v) => format!("push32 0x{v:08x}"), + hif::Op::PushNone => "push_none".into(), + hif::Op::Drop => "drop".into(), + hif::Op::DropN(n) => format!("drop_n {n}"), + hif::Op::Swap => "swap".into(), + hif::Op::Dup => "dup".into(), + hif::Op::Add => "add".into(), + hif::Op::And => "and".into(), + hif::Op::Or => "or".into(), + hif::Op::Xor => "xor".into(), + hif::Op::Expand32 => "expand32".into(), + hif::Op::Collect32 => "collect32".into(), + hif::Op::Label(hif::Target(t)) => format!("label {t}"), + hif::Op::BranchGreaterThan(hif::Target(t)) => { + format!("branch_gt {t}") + } + hif::Op::BranchGreaterThanOrEqualTo(hif::Target(t)) => { + format!("branch_gte {t}") + } + hif::Op::BranchLessThan(hif::Target(t)) => { + format!("branch_lt {t}") + } + hif::Op::BranchLessThanOrEqualTo(hif::Target(t)) => { + format!("branch_lte {t}") + } + hif::Op::BranchEqualTo(hif::Target(t)) => { + format!("branch_eq {t}") + } + hif::Op::BranchAlways(hif::Target(t)) => { + format!("branch_always {t}") + } + hif::Op::Call(hif::TargetFunction(id)) => { + let name = asm + .list_functions() + .iter() + .find(|f| f.id == *id) + .map(|f| f.name.as_str()) + .unwrap_or("?"); + format!("call {name}") + } + hif::Op::Done => "done".into(), + } +} diff --git a/humility-hif-assembler/src/lower.rs b/humility-hif-assembler/src/lower.rs new file mode 100644 index 000000000..4a760c1ea --- /dev/null +++ b/humility-hif-assembler/src/lower.rs @@ -0,0 +1,753 @@ +// 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/. + +//! Statement lowering: translate parsed statements into HIF bytecode. +//! +//! This module contains the `impl HifAssembler` methods that lower +//! individual statements to `hif::Op` sequences, plus helper functions +//! for encoding Idol arguments and parsing raw ops. + +use std::collections::BTreeSet; + +use anyhow::{Context, Result, bail}; +use hif::TargetFunction; + +use crate::assembler::HifAssembler; +use crate::parser::{BusRef, DeviceRef, Located, MuxSpec, Statement}; +use crate::types::*; + +/// Internal assembly result: ops + data + stats + warnings. +pub(crate) struct LowerResult { + pub ops: Vec, + pub data: Vec, + pub warnings: Vec, + pub functions_used: BTreeSet, + pub buses_used: BTreeSet, + pub labels_used: usize, + pub estimated_results: usize, +} + +/// Rough per-result overhead in the HIFFY_RSTACK buffer. +/// +/// Each `FunctionResult::Success` is postcard-encoded with a +/// discriminant byte, length prefix, and payload. For small I2C +/// reads (1-4 bytes), 8 bytes per result is a reasonable estimate. +pub(crate) const RSTACK_BYTES_PER_RESULT: usize = 8; + +impl HifAssembler { + pub(crate) fn lower_program( + &self, + parsed: &crate::parser::ParsedProgram, + ) -> Result { + let mut result = LowerResult { + ops: vec![], + data: vec![], + warnings: vec![], + functions_used: BTreeSet::new(), + buses_used: BTreeSet::new(), + labels_used: 0, + estimated_results: 0, + }; + let mut label_counter: u8 = 0; + + self.lower_statements( + &parsed.statements, + &mut result, + &mut label_counter, + 0, + )?; + + result.ops.push(hif::Op::Done); + result.labels_used = label_counter as usize; + + Ok(result) + } + + fn lower_statements( + &self, + stmts: &[Located], + result: &mut LowerResult, + label_counter: &mut u8, + depth: usize, + ) -> Result<()> { + for stmt in stmts { + self.lower_statement( + &stmt.value, + stmt.line, + result, + label_counter, + depth, + )?; + } + Ok(()) + } + + fn lower_statement( + &self, + stmt: &Statement, + line: usize, + result: &mut LowerResult, + label_counter: &mut u8, + depth: usize, + ) -> Result<()> { + match stmt { + Statement::I2cRead { bus, address, mux, register, nbytes } => { + let func = self.require_function("i2c_read", line)?; + self.track_bus(bus, result); + result.functions_used.insert("i2c_read".to_string()); + result.estimated_results += 1; + + self.emit_i2c_params( + bus, + address, + mux, + &mut result.ops, + &mut result.warnings, + line, + )?; + match register { + Some(r) => result.ops.push(hif::Op::Push(*r)), + None => result.ops.push(hif::Op::PushNone), + } + result.ops.push(hif::Op::Push(*nbytes)); + result.ops.push(hif::Op::Call(TargetFunction(func.id))); + result.ops.push(hif::Op::DropN(7)); + } + Statement::I2cWrite { bus, address, mux, register, data } => { + if data.len() > I2C_WRITE_MAX_DATA { + bail!( + "line {line}: i2c_write has {} data bytes, \ + maximum is {I2C_WRITE_MAX_DATA}", + data.len(), + ); + } + + let func = self.require_function("i2c_write", line)?; + self.track_bus(bus, result); + result.functions_used.insert("i2c_write".to_string()); + result.estimated_results += 1; + + self.emit_i2c_params( + bus, + address, + mux, + &mut result.ops, + &mut result.warnings, + line, + )?; + match register { + Some(r) => result.ops.push(hif::Op::Push(*r)), + None => result.ops.push(hif::Op::PushNone), + } + for byte in data { + result.ops.push(hif::Op::Push(*byte)); + } + result.ops.push(push_smallest(data.len() as u32)); + result.ops.push(hif::Op::Call(TargetFunction(func.id))); + result.ops.push(hif::Op::DropN((7 + data.len()) as u8)); + } + Statement::I2cScan { bus, mux } => { + let func = self.require_function("i2c_read", line)?; + let label = self.alloc_label(label_counter, line)?; + self.track_bus(bus, result); + result.functions_used.insert("i2c_read".to_string()); + result.estimated_results += 128; + + let (controller, port_index) = + self.resolve_bus(bus, line, &mut result.warnings)?; + result.ops.push(hif::Op::Push(controller)); + result.ops.push(hif::Op::Push(port_index)); + self.emit_mux_params(mux, &mut result.ops); + result.ops.push(hif::Op::PushNone); // reg + result.ops.push(hif::Op::Push(0)); // address counter + result.ops.push(hif::Op::PushNone); // sentinel + result.ops.push(hif::Op::Label(hif::Target(label))); + result.ops.push(hif::Op::Drop); + result.ops.push(hif::Op::Swap); + result.ops.push(hif::Op::Push(1)); + result.ops.push(hif::Op::Call(TargetFunction(func.id))); + result.ops.push(hif::Op::Drop); + result.ops.push(hif::Op::Swap); + result.ops.push(hif::Op::Push(1)); + result.ops.push(hif::Op::Add); + result.ops.push(hif::Op::Push(128)); + result.ops.push(hif::Op::BranchGreaterThanOrEqualTo( + hif::Target(label), + )); + } + Statement::I2cRegScan { bus, address, mux } => { + let func = self.require_function("i2c_read", line)?; + let label = self.alloc_label(label_counter, line)?; + self.track_bus(bus, result); + result.functions_used.insert("i2c_read".to_string()); + result.estimated_results += 256; + + let (controller, port_index) = + self.resolve_bus(bus, line, &mut result.warnings)?; + let addr = self.resolve_device( + bus, + address, + mux, + line, + &mut result.warnings, + )?; + result.ops.push(hif::Op::Push(controller)); + result.ops.push(hif::Op::Push(port_index)); + self.emit_mux_params(mux, &mut result.ops); + result.ops.push(hif::Op::Push(addr)); + result.ops.push(hif::Op::Push(0)); + result.ops.push(hif::Op::PushNone); + result.ops.push(hif::Op::Label(hif::Target(label))); + result.ops.push(hif::Op::Drop); + result.ops.push(hif::Op::Push(1)); + result.ops.push(hif::Op::Call(TargetFunction(func.id))); + result.ops.push(hif::Op::Add); + result.ops.push(hif::Op::Push(0xff)); + result.ops.push(hif::Op::BranchGreaterThanOrEqualTo( + hif::Target(label), + )); + } + Statement::IdolCall { interface, operation, args } => { + self.lower_idol_call(interface, operation, args, line, result)?; + } + Statement::Call { function, args } => { + // HIF functions receive the entire stack (stack[0..sp]) + // and use positional args from the top. Args pushed + // here sit on top of any loop counter or other values + // below; functions must only read their expected arg + // count from the top of the stack. + let func = self.require_function(function, line)?; + result.functions_used.insert(function.clone()); + result.estimated_results += 1; + + for arg in args { + result.ops.push(push_smallest(*arg)); + } + result.ops.push(hif::Op::Call(TargetFunction(func.id))); + if !args.is_empty() { + result.ops.push(hif::Op::DropN(args.len() as u8)); + } + } + Statement::Sleep { ms } => { + let func = self.require_function("Sleep", line)?; + result.functions_used.insert("Sleep".to_string()); + let mut remaining = *ms; + while remaining > 0 { + let chunk = remaining.min(100); + result.ops.push(push_smallest(chunk)); + result.ops.push(hif::Op::Call(TargetFunction(func.id))); + result.ops.push(hif::Op::Drop); + remaining -= chunk; + } + } + Statement::Repeat { count, sleep_ms, body } => { + if *count == 0 { + bail!("line {line}: repeat count must be at least 1"); + } + + // Loop pattern (matches humility cmd/i2c): + // Push(0) # counter = 0 + // PushNone # sentinel for first Drop + // Label(N) + // Drop # drop previous limit (or sentinel) + // ... body ... + // Push(1) + // Add # counter += 1 + // Push(count) # limit + // BranchGreaterThan(N) # branch if limit > counter + // + // Stack at branch: [counter, limit] + // BranchGreaterThan reads but doesn't pop. + // Next iteration's Drop removes the limit. + let label = self.alloc_label(label_counter, line)?; + result.ops.push(push_smallest(0)); // counter + result.ops.push(hif::Op::PushNone); // sentinel + result.ops.push(hif::Op::Label(hif::Target(label))); + result.ops.push(hif::Op::Drop); // drop limit/sentinel + + let before = result.estimated_results; + self.lower_statements(body, result, label_counter, depth + 1)?; + let body_results = result.estimated_results - before; + result.estimated_results += + body_results * (*count as usize - 1); + + if let Some(ms) = sleep_ms { + let func = self + .require_function("Sleep", line) + .context("repeat sleep= requires Sleep function")?; + result.functions_used.insert("Sleep".to_string()); + let mut remaining = *ms; + while remaining > 0 { + let chunk = remaining.min(100); + result.ops.push(push_smallest(chunk)); + result.ops.push(hif::Op::Call(TargetFunction(func.id))); + result.ops.push(hif::Op::Drop); + remaining -= chunk; + } + } + + result.ops.push(push_smallest(1)); + result.ops.push(hif::Op::Add); // counter += 1 + result.ops.push(push_smallest(*count)); // limit + result.ops.push(hif::Op::BranchGreaterThan(hif::Target(label))); + } + Statement::Raw { lines } => { + for raw_line in lines { + let raw_line = raw_line.trim(); + if raw_line.is_empty() { + continue; + } + let op = self.parse_raw_op(raw_line, line)?; + result.ops.push(op); + } + } + } + Ok(()) + } + + fn lower_idol_call( + &self, + interface: &str, + operation: &str, + args: &[(String, String)], + line: usize, + result: &mut LowerResult, + ) -> Result<()> { + let iface = self + .config + .idol_interfaces + .iter() + .find(|i| i.name == interface) + .ok_or_else(|| { + anyhow::anyhow!( + "line {line}: unknown Idol interface '{interface}'" + ) + })?; + + let op = iface.ops.iter().find(|o| o.name == operation).ok_or_else( + || { + anyhow::anyhow!( + "line {line}: unknown operation \ + '{interface}.{operation}'" + ) + }, + )?; + + let (func_name, has_read_lease, has_write_lease) = match ( + op.leases.iter().any(|l| l.write), + op.leases.iter().any(|l| l.read), + ) { + (false, false) => ("Send", false, false), + (false, true) => ("SendLeaseRead", false, true), + (true, false) => ("SendLeaseWrite", true, false), + (true, true) => ("SendLeaseReadWrite", true, true), + }; + let func = self.require_function(func_name, line)?; + result.functions_used.insert(func_name.to_string()); + result.estimated_results += 1; + + let mut arg_map: std::collections::HashMap<&str, &str> = + args.iter().map(|(k, v)| (k.as_str(), v.as_str())).collect(); + + let mut payload = Vec::new(); + for expected_arg in &op.args { + let val_str = arg_map + .remove(expected_arg.name.as_str()) + .ok_or_else(|| { + anyhow::anyhow!( + "line {line}: {interface}.{operation} \ + missing argument '{}'", + expected_arg.name, + ) + })?; + + let bytes = encode_idol_arg( + val_str, + &expected_arg.ty, + &expected_arg.name, + line, + )?; + payload.extend_from_slice(&bytes); + } + + if let Some((extra, _)) = arg_map.iter().next() { + bail!( + "line {line}: {interface}.{operation} \ + unexpected argument '{extra}'" + ); + } + + result.ops.push(push_smallest(iface.task_id)); + result.ops.push(push_smallest(op.code as u32)); + for byte in &payload { + result.ops.push(hif::Op::Push(*byte)); + } + result.ops.push(push_smallest(payload.len() as u32)); + + if op.reply_size == 0 && op.reply != "()" { + result.warnings.push(format!( + "line {line}: {interface}.{operation} reply type \ + '{}' size could not be determined; \ + results may be truncated", + op.reply, + )); + } + result.ops.push(push_smallest(op.reply_size as u32)); + + if has_write_lease { + result.ops.push(push_smallest(0)); + } + if has_read_lease { + result.ops.push(push_smallest(0)); + } + + result.ops.push(hif::Op::Call(TargetFunction(func.id))); + + let frame_size = 2 + + payload.len() + + 1 + + 1 + + if has_write_lease { 1 } else { 0 } + + if has_read_lease { 1 } else { 0 }; + result.ops.push(hif::Op::DropN(frame_size as u8)); + + Ok(()) + } + + fn track_bus(&self, bus: &BusRef, result: &mut LowerResult) { + if let BusRef::Named(name) = bus { + result.buses_used.insert(name.clone()); + } + } + + fn emit_i2c_params( + &self, + bus: &BusRef, + address: &DeviceRef, + mux: &Option, + ops: &mut Vec, + warnings: &mut Vec, + line: usize, + ) -> Result<()> { + let (controller, port_index) = self.resolve_bus(bus, line, warnings)?; + let addr = self.resolve_device(bus, address, mux, line, warnings)?; + ops.push(hif::Op::Push(controller)); + ops.push(hif::Op::Push(port_index)); + self.emit_mux_params(mux, ops); + ops.push(hif::Op::Push(addr)); + Ok(()) + } + + fn emit_mux_params(&self, mux: &Option, ops: &mut Vec) { + match mux { + Some(m) => { + ops.push(hif::Op::Push(m.address)); + ops.push(hif::Op::Push(m.segment)); + } + None => { + ops.push(hif::Op::PushNone); + ops.push(hif::Op::PushNone); + } + } + } + + pub(crate) fn resolve_bus( + &self, + bus: &BusRef, + line: usize, + warnings: &mut Vec, + ) -> Result<(u8, u8)> { + match bus { + BusRef::Named(name) => { + let b = self.buses.get(name).ok_or_else(|| { + anyhow::anyhow!("line {line}: unknown I2C bus '{name}'") + })?; + Ok((b.controller, b.port_index)) + } + BusRef::Explicit { controller, port } => { + for b in self.buses.values() { + if b.controller == *controller + && b.port_name.eq_ignore_ascii_case(port) + { + return Ok((b.controller, b.port_index)); + } + } + warnings.push(format!( + "line {line}: explicit bus {controller}.{port} \ + not found in target config, using port index 0" + )); + Ok((*controller, 0)) + } + } + } + + /// Resolve a device reference to a numeric I2C address. + fn resolve_device( + &self, + bus: &BusRef, + device: &DeviceRef, + mux: &Option, + line: usize, + _warnings: &mut Vec, + ) -> Result { + match device { + DeviceRef::Address(addr) => Ok(*addr), + DeviceRef::Named(name) => { + // Search for the device by part name or human name + // in the specified bus (or all buses if bus is inferred). + let bus_name = match bus { + BusRef::Named(n) => Some(n.as_str()), + BusRef::Explicit { .. } => None, + }; + + for b in self.config.buses.iter() { + if let Some(bn) = bus_name { + if b.name != bn { + continue; + } + } + + // Search direct devices + for d in &b.devices { + if d.device.eq_ignore_ascii_case(name) + || d.name + .as_deref() + .map(|n| n.eq_ignore_ascii_case(name)) + .unwrap_or(false) + { + // If muxed, skip direct devices + if mux.is_some() { + continue; + } + return Ok(d.address); + } + } + + // Search muxed devices + for m in &b.muxes { + for seg in &m.segments { + for d in &seg.devices { + if d.device.eq_ignore_ascii_case(name) + || d.name + .as_deref() + .map(|n| n.eq_ignore_ascii_case(name)) + .unwrap_or(false) + { + return Ok(d.address); + } + } + } + } + } + + bail!("line {line}: unknown I2C device '{name}'") + } + } + } + + /// Look up a HIF function by name. + /// + /// Tries exact match first, then normalized match (strip `_`, + /// lowercase). + pub(crate) fn require_function( + &self, + name: &str, + line: usize, + ) -> Result<&FunctionInfo> { + if let Some(f) = self.functions.get(name) { + return Ok(f); + } + let normalized = normalize_function_name(name); + if let Some(canonical) = self.function_aliases.get(&normalized) { + if let Some(f) = self.functions.get(canonical) { + return Ok(f); + } + } + Err(anyhow::anyhow!("line {line}: unknown HIF function '{name}'")) + } + + fn alloc_label(&self, counter: &mut u8, line: usize) -> Result { + if *counter as usize >= MAX_LABELS { + bail!( + "line {line}: program uses {} labels, \ + exceeds maximum of {MAX_LABELS}", + *counter as usize + 1, + ); + } + let label = *counter; + *counter += 1; + Ok(label) + } + + fn parse_raw_op(&self, line: &str, src_line: usize) -> Result { + let tokens: Vec<&str> = line.split_whitespace().collect(); + if tokens.is_empty() { + bail!("line {src_line}: empty raw op"); + } + match tokens[0] { + "push" => { + let v = raw_arg::(&tokens, 1, src_line)?; + Ok(hif::Op::Push(v)) + } + "push16" => { + let v = raw_arg::(&tokens, 1, src_line)?; + Ok(hif::Op::Push16(v)) + } + "push32" => { + let v = raw_arg::(&tokens, 1, src_line)?; + Ok(hif::Op::Push32(v)) + } + "push_none" => Ok(hif::Op::PushNone), + "drop" => Ok(hif::Op::Drop), + "drop_n" => { + let v = raw_arg::(&tokens, 1, src_line)?; + Ok(hif::Op::DropN(v)) + } + "swap" => Ok(hif::Op::Swap), + "add" => Ok(hif::Op::Add), + "label" => { + let v = raw_arg::(&tokens, 1, src_line)?; + Ok(hif::Op::Label(hif::Target(v))) + } + "branch_gt" => { + let v = raw_arg::(&tokens, 1, src_line)?; + Ok(hif::Op::BranchGreaterThan(hif::Target(v))) + } + "branch_gte" => { + let v = raw_arg::(&tokens, 1, src_line)?; + Ok(hif::Op::BranchGreaterThanOrEqualTo(hif::Target(v))) + } + "branch_lt" => { + let v = raw_arg::(&tokens, 1, src_line)?; + Ok(hif::Op::BranchLessThan(hif::Target(v))) + } + "call" => { + if let Ok(id) = crate::parser::parse_num::(tokens[1]) { + Ok(hif::Op::Call(TargetFunction(id))) + } else { + let func = self.require_function(tokens[1], src_line)?; + Ok(hif::Op::Call(TargetFunction(func.id))) + } + } + "done" => Ok(hif::Op::Done), + other => { + bail!("line {src_line}: unknown raw op '{other}'") + } + } + } +} + +/// Encode an Idol argument value as little-endian bytes. +fn encode_idol_arg( + val: &str, + ty: &str, + arg_name: &str, + line: usize, +) -> Result> { + match ty { + "u8" => { + let v = parse_idol_num::(val, arg_name, line)?; + Ok(vec![v]) + } + "u16" => { + let v = parse_idol_num::(val, arg_name, line)?; + Ok(v.to_le_bytes().to_vec()) + } + "u32" | "usize" => { + let v = parse_idol_num::(val, arg_name, line)?; + Ok(v.to_le_bytes().to_vec()) + } + "u64" => { + let v = parse_idol_num::(val, arg_name, line)?; + Ok(v.to_le_bytes().to_vec()) + } + "i8" => { + let v = parse_idol_num::(val, arg_name, line)?; + Ok(vec![v]) + } + "i16" => { + let v = parse_idol_num::(val, arg_name, line)?; + Ok(v.to_le_bytes().to_vec()) + } + "i32" | "isize" => { + let v = parse_idol_num::(val, arg_name, line)?; + Ok(v.to_le_bytes().to_vec()) + } + "i64" => { + let v = parse_idol_num::(val, arg_name, line)?; + Ok(v.to_le_bytes().to_vec()) + } + "f32" => { + let v: f32 = val.parse().map_err(|_| { + anyhow::anyhow!( + "line {line}: bad f32 value '{val}' \ + for arg '{arg_name}'" + ) + })?; + Ok(v.to_le_bytes().to_vec()) + } + "bool" => { + let v = match val { + "true" | "1" => 1u8, + "false" | "0" => 0u8, + _ => bail!( + "line {line}: bad bool value '{val}' for arg \ + '{arg_name}' (expected true/false/0/1)" + ), + }; + Ok(vec![v]) + } + _ => { + if let Ok(v) = crate::parser::parse_num::(val) { + Ok(v.to_le_bytes().to_vec()) + } else { + bail!( + "line {line}: cannot encode '{val}' as type \ + '{ty}' for arg '{arg_name}'; use a numeric value" + ) + } + } + } +} + +fn parse_idol_num(val: &str, arg_name: &str, line: usize) -> Result +where + T: TryFrom, +{ + crate::parser::parse_num::(val).map_err(|_| { + anyhow::anyhow!( + "line {line}: bad numeric value '{val}' for arg '{arg_name}'" + ) + }) +} + +/// Normalize a function name for alias matching. +/// +/// Strips underscores and lowercases: `i2c_read` -> `i2cread`, +/// `I2cRead` -> `i2cread`, `Sleep` -> `sleep`. +pub(crate) fn normalize_function_name(name: &str) -> String { + name.replace('_', "").to_lowercase() +} + +pub(crate) fn push_smallest(val: u32) -> hif::Op { + if val <= u8::MAX as u32 { + hif::Op::Push(val as u8) + } else if val <= u16::MAX as u32 { + hif::Op::Push16(val as u16) + } else { + hif::Op::Push32(val) + } +} + +fn raw_arg>( + tokens: &[&str], + idx: usize, + line: usize, +) -> Result { + let s = tokens.get(idx).ok_or_else(|| { + anyhow::anyhow!("line {line}: '{}' requires an argument", tokens[0]) + })?; + crate::parser::parse_num::(s) + .map_err(|_| anyhow::anyhow!("line {line}: invalid number '{s}'")) +} diff --git a/humility-hif-assembler/src/parser.rs b/humility-hif-assembler/src/parser.rs new file mode 100644 index 000000000..7c6dd17d8 --- /dev/null +++ b/humility-hif-assembler/src/parser.rs @@ -0,0 +1,950 @@ +// 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/. + +//! Parser for the HIF text language. +//! +//! The parser operates on the source text before any archive-dependent +//! resolution. It produces a [`ParsedProgram`] containing +//! [`Statement`] values that the assembler then lowers into HIF ops. +//! +//! ## Syntax Summary +//! +//! ```text +//! # Comment (to end of line) +//! +//! .let NAME value # define a constant +//! +//! i2c_read [mux=.] [reg=] +//! i2c_write [mux=.] [reg=] [,...] +//! i2c_scan [mux=.] +//! i2c_regscan [mux=.] +//! +//! idol [arg=val ...] +//! +//! sleep ms +//! +//! repeat [sleep=ms] +//! +//! end +//! +//! raw { +//! +//! } +//! ``` + +use crate::error::{HifError, HifErrorKind}; +use std::collections::HashMap; + +/// A parsed but unresolved HIF program. +/// +/// Constants (`.let`) are expanded during parsing; they do not appear +/// in the statement list. The `constants` map is retained for +/// informational purposes only (e.g., listing what was defined). +#[derive(Debug, Clone)] +pub struct ParsedProgram { + pub statements: Vec>, + /// Constants that were defined. Values have already been + /// substituted into statements during parsing. + pub constants: HashMap, +} + +/// A statement with its source line number. +#[derive(Debug, Clone)] +pub struct Located { + pub line: usize, + pub value: T, +} + +/// Mux specifier: address and segment. +#[derive(Debug, Clone)] +pub struct MuxSpec { + pub address: u8, + pub segment: u8, +} + +/// A parsed I2C bus reference — either a named bus or explicit +/// controller.port. +#[derive(Debug, Clone)] +pub enum BusRef { + /// A named bus from app.toml (e.g. "mid", "front"). + Named(String), + /// Explicit controller and port name (e.g. "3.H"). + Explicit { controller: u8, port: String }, +} + +/// An I2C device reference — either a numeric address or a symbolic +/// device name that the assembler resolves from the TargetConfig. +#[derive(Debug, Clone, PartialEq)] +pub enum DeviceRef { + /// Numeric I2C address (e.g. 0x48). + Address(u8), + /// Device name to resolve (e.g. "tmp117", "v3p3_sys"). + Named(String), +} + +/// A single statement in the HIF source. +#[derive(Debug, Clone)] +pub enum Statement { + I2cRead { + bus: BusRef, + address: DeviceRef, + mux: Option, + register: Option, + nbytes: u8, + }, + I2cWrite { + bus: BusRef, + address: DeviceRef, + mux: Option, + register: Option, + data: Vec, + }, + I2cScan { + bus: BusRef, + mux: Option, + }, + I2cRegScan { + bus: BusRef, + address: DeviceRef, + mux: Option, + }, + IdolCall { + interface: String, + operation: String, + args: Vec<(String, String)>, + }, + Sleep { + ms: u32, + }, + Repeat { + count: u32, + sleep_ms: Option, + body: Vec>, + }, + /// Call any HIF function by name with optional numeric arguments. + Call { + function: String, + args: Vec, + }, + Raw { + lines: Vec, + }, +} + +/// Parse a HIF source string into a [`ParsedProgram`]. +/// +/// Constants (`.let`) are expanded eagerly during parsing: any `$NAME` +/// token in subsequent lines is replaced with the constant's value +/// before the line is parsed. +pub fn parse(source: &str) -> Result { + let mut constants = HashMap::new(); + let lines: Vec<(usize, &str)> = + source.lines().enumerate().map(|(i, l)| (i + 1, l)).collect(); + + let statements = parse_block(&lines, &mut constants, &mut 0)?; + + Ok(ParsedProgram { statements, constants }) +} + +fn parse_block( + lines: &[(usize, &str)], + constants: &mut HashMap, + pos: &mut usize, +) -> Result>, HifError> { + let mut stmts = vec![]; + + while *pos < lines.len() { + let (line_num, raw_line) = lines[*pos]; + let line = strip_comment(raw_line).trim(); + + if line.is_empty() { + *pos += 1; + continue; + } + + // Expand constants + let line = expand_constants(line, constants, line_num)?; + let line = line.trim(); + + if line == "end" { + // Caller handles this + return Ok(stmts); + } + + // Check for raw block: first token is "raw", line ends with "{" + // Raw blocks skip syntactic sugar but still expand $CONSTANTS. + let is_raw = { + let tokens: Vec<&str> = line.split_whitespace().collect(); + tokens.first() == Some(&"raw") && line.trim_end().ends_with('{') + }; + if is_raw { + *pos += 1; + let mut raw_lines = vec![]; + while *pos < lines.len() { + let (raw_line_num, rl) = lines[*pos]; + let rl = strip_comment(rl).trim(); + if rl == "}" { + *pos += 1; + break; + } + // Expand constants inside raw blocks + let rl = expand_constants(rl, constants, raw_line_num)?; + raw_lines.push(rl.trim().to_string()); + *pos += 1; + } + stmts.push(Located { + line: line_num, + value: Statement::Raw { lines: raw_lines }, + }); + continue; + } + + let stmt = parse_statement(line, line_num, lines, constants, pos)?; + if let Some(s) = stmt { + stmts.push(Located { line: line_num, value: s }); + } + // parse_statement advances pos for repeat blocks; otherwise we + // advance here. + } + + Ok(stmts) +} + +fn parse_statement( + line: &str, + line_num: usize, + lines: &[(usize, &str)], + constants: &mut HashMap, + pos: &mut usize, +) -> Result, HifError> { + let tokens: Vec<&str> = line.split_whitespace().collect(); + if tokens.is_empty() { + *pos += 1; + return Ok(None); + } + + let result = match tokens[0] { + ".let" => { + if tokens.len() != 3 { + return Err(mkerr( + line_num, + HifErrorKind::Parse(".let requires NAME VALUE".into()), + )); + } + constants.insert(tokens[1].to_string(), tokens[2].to_string()); + *pos += 1; + return Ok(None); + } + "i2c_read" => { + *pos += 1; + parse_i2c_read(&tokens[1..], line_num)? + } + "i2c_write" => { + *pos += 1; + parse_i2c_write(&tokens[1..], line_num)? + } + "i2c_scan" => { + *pos += 1; + parse_i2c_scan(&tokens[1..], line_num)? + } + "i2c_regscan" => { + *pos += 1; + parse_i2c_regscan(&tokens[1..], line_num)? + } + "idol" => { + *pos += 1; + parse_idol(&tokens[1..], line_num)? + } + "sleep" => { + *pos += 1; + parse_sleep(&tokens[1..], line_num)? + } + "repeat" => { + *pos += 1; + parse_repeat(&tokens[1..], line_num, lines, constants, pos)? + } + "call" => { + *pos += 1; + parse_call(&tokens[1..], line_num)? + } + other => { + return Err(mkerr( + line_num, + HifErrorKind::Parse(format!("unknown statement '{other}'")), + )); + } + }; + + Ok(Some(result)) +} + +fn parse_bus_ref(s: &str) -> BusRef { + if let Some((ctrl, port)) = s.split_once('.') { + if let Ok(c) = parse_num::(ctrl) { + return BusRef::Explicit { controller: c, port: port.to_string() }; + } + } + BusRef::Named(s.to_string()) +} + +/// Parse a device reference: either a numeric address (0x48, 72) +/// or a symbolic name (tmp117, v3p3_sys). +fn parse_device_ref(s: &str) -> DeviceRef { + match parse_num::(s) { + Ok(addr) => DeviceRef::Address(addr), + Err(_) => DeviceRef::Named(s.to_string()), + } +} + +fn parse_mux(s: &str) -> Result { + let val = s + .strip_prefix("mux=") + .ok_or_else(|| format!("expected mux=., got '{s}'"))?; + let (addr, seg) = val + .split_once('.') + .ok_or_else(|| format!("mux requires addr.segment, got '{val}'"))?; + Ok(MuxSpec { + address: parse_num::(addr) + .map_err(|_| format!("bad mux address '{addr}'"))?, + segment: parse_num::(seg) + .map_err(|_| format!("bad mux segment '{seg}'"))?, + }) +} + +fn parse_reg(s: &str) -> Result { + let val = s + .strip_prefix("reg=") + .ok_or_else(|| format!("expected reg=, got '{s}'"))?; + parse_num::(val).map_err(|_| format!("bad register '{val}'")) +} + +/// Parse `i2c_read [mux=...] [reg=...] ` +fn parse_i2c_read(tokens: &[&str], line: usize) -> Result { + if tokens.len() < 3 { + return Err(mkerr( + line, + HifErrorKind::Parse( + "i2c_read requires: [mux=...] [reg=...] " + .into(), + ), + )); + } + + let bus = parse_bus_ref(tokens[0]); + let address = parse_device_ref(tokens[1]); + let mut mux = None; + let mut register = None; + let mut nbytes_tok = None; + + for &tok in &tokens[2..] { + if tok.starts_with("mux=") { + mux = Some( + parse_mux(tok) + .map_err(|e| mkerr(line, HifErrorKind::Parse(e)))?, + ); + } else if tok.starts_with("reg=") { + register = Some( + parse_reg(tok) + .map_err(|e| mkerr(line, HifErrorKind::Parse(e)))?, + ); + } else { + nbytes_tok = Some(tok); + } + } + + let nbytes = match nbytes_tok { + Some(t) => parse_num_err::(t, line)?, + None => { + return Err(mkerr( + line, + HifErrorKind::Parse("i2c_read missing ".into()), + )); + } + }; + + Ok(Statement::I2cRead { bus, address, mux, register, nbytes }) +} + +/// Parse `i2c_write [mux=...] [reg=...] [,...]` +fn parse_i2c_write( + tokens: &[&str], + line: usize, +) -> Result { + if tokens.len() < 3 { + return Err(mkerr( + line, + HifErrorKind::Parse( + "i2c_write requires: [mux=...] [reg=...] " + .into(), + ), + )); + } + + let bus = parse_bus_ref(tokens[0]); + let address = parse_device_ref(tokens[1]); + let mut mux = None; + let mut register = None; + let mut data_tok = None; + + for &tok in &tokens[2..] { + if tok.starts_with("mux=") { + mux = Some( + parse_mux(tok) + .map_err(|e| mkerr(line, HifErrorKind::Parse(e)))?, + ); + } else if tok.starts_with("reg=") { + register = Some( + parse_reg(tok) + .map_err(|e| mkerr(line, HifErrorKind::Parse(e)))?, + ); + } else { + data_tok = Some(tok); + } + } + + let data = match data_tok { + Some(t) => t + .split(',') + .map(|b| { + parse_num::(b).map_err(|_| { + mkerr(line, HifErrorKind::InvalidNumber(b.to_string())) + }) + }) + .collect::, _>>()?, + None => { + return Err(mkerr( + line, + HifErrorKind::Parse("i2c_write missing data bytes".into()), + )); + } + }; + + Ok(Statement::I2cWrite { bus, address, mux, register, data }) +} + +/// Parse `i2c_scan [mux=...]` +fn parse_i2c_scan(tokens: &[&str], line: usize) -> Result { + if tokens.is_empty() { + return Err(mkerr( + line, + HifErrorKind::Parse("i2c_scan requires ".into()), + )); + } + let bus = parse_bus_ref(tokens[0]); + let mut mux = None; + for &tok in &tokens[1..] { + if tok.starts_with("mux=") { + mux = Some( + parse_mux(tok) + .map_err(|e| mkerr(line, HifErrorKind::Parse(e)))?, + ); + } + } + Ok(Statement::I2cScan { bus, mux }) +} + +/// Parse `i2c_regscan [mux=...]` +fn parse_i2c_regscan( + tokens: &[&str], + line: usize, +) -> Result { + if tokens.len() < 2 { + return Err(mkerr( + line, + HifErrorKind::Parse("i2c_regscan requires ".into()), + )); + } + let bus = parse_bus_ref(tokens[0]); + let address = parse_device_ref(tokens[1]); + let mut mux = None; + for &tok in &tokens[2..] { + if tok.starts_with("mux=") { + mux = Some( + parse_mux(tok) + .map_err(|e| mkerr(line, HifErrorKind::Parse(e)))?, + ); + } + } + Ok(Statement::I2cRegScan { bus, address, mux }) +} + +/// Parse `idol [arg=val ...]` +fn parse_idol(tokens: &[&str], line: usize) -> Result { + if tokens.is_empty() { + return Err(mkerr( + line, + HifErrorKind::Parse("idol requires ".into()), + )); + } + let parts: Vec<&str> = tokens[0].split('.').collect(); + if parts.len() != 2 { + return Err(mkerr( + line, + HifErrorKind::Parse(format!( + "idol call must be Interface.Operation, got '{}'", + tokens[0] + )), + )); + } + let mut args = vec![]; + for &tok in &tokens[1..] { + if let Some((k, v)) = tok.split_once('=') { + args.push((k.to_string(), v.to_string())); + } else { + return Err(mkerr( + line, + HifErrorKind::Parse(format!( + "idol arguments must be key=value, got '{tok}'" + )), + )); + } + } + Ok(Statement::IdolCall { + interface: parts[0].to_string(), + operation: parts[1].to_string(), + args, + }) +} + +/// Parse `sleep ms` +fn parse_sleep(tokens: &[&str], line: usize) -> Result { + if tokens.is_empty() { + return Err(mkerr( + line, + HifErrorKind::Parse("sleep requires ms".into()), + )); + } + let s = tokens[0]; + let ms_str = s.strip_suffix("ms").unwrap_or(s); + let ms = parse_num::(ms_str) + .map_err(|_| mkerr(line, HifErrorKind::InvalidNumber(s.to_string())))?; + Ok(Statement::Sleep { ms }) +} + +/// Parse `call [arg ...]` +fn parse_call(tokens: &[&str], line: usize) -> Result { + if tokens.is_empty() { + return Err(mkerr( + line, + HifErrorKind::Parse("call requires [args...]".into()), + )); + } + let function = tokens[0].to_string(); + let mut args = vec![]; + for &tok in &tokens[1..] { + let v = parse_num::(tok).map_err(|_| { + mkerr(line, HifErrorKind::InvalidNumber(tok.to_string())) + })?; + args.push(v); + } + Ok(Statement::Call { function, args }) +} + +/// Parse `repeat [sleep=ms]` and its body up to `end`. +fn parse_repeat( + tokens: &[&str], + line: usize, + lines: &[(usize, &str)], + constants: &mut HashMap, + pos: &mut usize, +) -> Result { + if tokens.is_empty() { + return Err(mkerr( + line, + HifErrorKind::Parse("repeat requires ".into()), + )); + } + let count = parse_num::(tokens[0]).map_err(|_| { + mkerr(line, HifErrorKind::InvalidNumber(tokens[0].to_string())) + })?; + let mut sleep_ms = None; + for &tok in &tokens[1..] { + if let Some(val) = tok.strip_prefix("sleep=") { + let ms_str = val.strip_suffix("ms").unwrap_or(val); + sleep_ms = Some(parse_num::(ms_str).map_err(|_| { + mkerr(line, HifErrorKind::InvalidNumber(val.to_string())) + })?); + } + } + + let body = parse_block(lines, constants, pos)?; + + // parse_block returns when it hits "end" or EOF. + // Consume the "end" line, or error if we hit EOF. + if *pos < lines.len() { + let (_, end_line) = lines[*pos]; + if strip_comment(end_line).trim() == "end" { + *pos += 1; + } else { + return Err(mkerr( + line, + HifErrorKind::UnmatchedBlock("repeat".into()), + )); + } + } else { + return Err(mkerr( + line, + HifErrorKind::UnmatchedBlock("repeat (missing 'end')".into()), + )); + } + + Ok(Statement::Repeat { count, sleep_ms, body }) +} + +// -- Utilities -- + +fn strip_comment(line: &str) -> &str { + match line.find('#') { + Some(i) => &line[..i], + None => line, + } +} + +fn expand_constants( + line: &str, + constants: &HashMap, + line_num: usize, +) -> Result { + if !line.contains('$') { + return Ok(line.to_string()); + } + let mut result = line.to_string(); + // Iterate over all $NAME references. We do a simple scan for + // '$' followed by word characters. + while let Some(start) = result.find('$') { + let rest = &result[start + 1..]; + let end = rest + .find(|c: char| !c.is_alphanumeric() && c != '_') + .unwrap_or(rest.len()); + let name = &rest[..end]; + if name.is_empty() { + break; + } + match constants.get(name) { + Some(val) => { + result = format!( + "{}{}{}", + &result[..start], + val, + &result[start + 1 + end..] + ); + } + None => { + // Column is 1-based; start is 0-based offset in the line + return Err(mkerr_col( + line_num, + start + 1, + HifErrorKind::UndefinedConstant(name.to_string()), + )); + } + } + } + Ok(result) +} + +fn mkerr(line: usize, kind: HifErrorKind) -> HifError { + HifError { line, col: None, kind } +} + +fn mkerr_col(line: usize, col: usize, kind: HifErrorKind) -> HifError { + HifError { line, col: Some(col), kind } +} + +/// Parse a numeric literal, accepting `0x` hex, `0b` binary, `0o` octal, +/// and plain decimal. +/// +/// Returns `Err(())` on failure; callers wrap this with context via +/// `parse_num_err`. +#[allow(clippy::result_unit_err)] +pub fn parse_num>(s: &str) -> Result { + let val = if let Some(hex) = + s.strip_prefix("0x").or_else(|| s.strip_prefix("0X")) + { + u64::from_str_radix(hex, 16).map_err(|_| ())? + } else if let Some(bin) = + s.strip_prefix("0b").or_else(|| s.strip_prefix("0B")) + { + u64::from_str_radix(bin, 2).map_err(|_| ())? + } else if let Some(oct) = + s.strip_prefix("0o").or_else(|| s.strip_prefix("0O")) + { + u64::from_str_radix(oct, 8).map_err(|_| ())? + } else { + s.parse::().map_err(|_| ())? + }; + T::try_from(val).map_err(|_| ()) +} + +fn parse_num_err>(s: &str, line: usize) -> Result { + parse_num::(s) + .map_err(|_| mkerr(line, HifErrorKind::InvalidNumber(s.to_string()))) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn parse_simple_i2c_read() { + let prog = parse("i2c_read mid 0x48 reg=0x00 2").unwrap(); + assert_eq!(prog.statements.len(), 1); + match &prog.statements[0].value { + Statement::I2cRead { bus, address, register, nbytes, .. } => { + assert!(matches!(bus, BusRef::Named(n) if n == "mid")); + assert_eq!(*address, DeviceRef::Address(0x48)); + assert_eq!(*register, Some(0x00)); + assert_eq!(*nbytes, 2); + } + other => panic!("expected I2cRead, got {other:?}"), + } + } + + #[test] + fn parse_i2c_read_with_mux() { + let prog = parse("i2c_read front 0x50 mux=0x70.1 reg=0x00 16").unwrap(); + match &prog.statements[0].value { + Statement::I2cRead { mux, .. } => { + let mux = mux.as_ref().unwrap(); + assert_eq!(mux.address, 0x70); + assert_eq!(mux.segment, 1); + } + other => panic!("expected I2cRead, got {other:?}"), + } + } + + #[test] + fn parse_explicit_bus() { + let prog = parse("i2c_read 3.H 0x48 2").unwrap(); + match &prog.statements[0].value { + Statement::I2cRead { bus, .. } => { + assert!(matches!( + bus, + BusRef::Explicit { controller: 3, port } + if port == "H" + )); + } + other => panic!("expected I2cRead, got {other:?}"), + } + } + + #[test] + fn parse_repeat_block() { + let src = "repeat 10\n i2c_read mid 0x48 2\nend\n"; + let prog = parse(src).unwrap(); + assert_eq!(prog.statements.len(), 1); + match &prog.statements[0].value { + Statement::Repeat { count, body, .. } => { + assert_eq!(*count, 10); + assert_eq!(body.len(), 1); + } + other => panic!("expected Repeat, got {other:?}"), + } + } + + #[test] + fn parse_constants() { + let src = ".let ADDR 0x48\ni2c_read mid $ADDR 2\n"; + let prog = parse(src).unwrap(); + assert_eq!(prog.statements.len(), 1); + match &prog.statements[0].value { + Statement::I2cRead { address, .. } => { + assert_eq!(*address, DeviceRef::Address(0x48)); + } + other => panic!("expected I2cRead, got {other:?}"), + } + } + + #[test] + fn parse_idol_call() { + let prog = parse("idol Sensor.get id=3").unwrap(); + match &prog.statements[0].value { + Statement::IdolCall { interface, operation, args } => { + assert_eq!(interface, "Sensor"); + assert_eq!(operation, "get"); + assert_eq!(args.len(), 1); + assert_eq!(args[0], ("id".to_string(), "3".to_string())); + } + other => panic!("expected IdolCall, got {other:?}"), + } + } + + #[test] + fn parse_sleep() { + let prog = parse("sleep 50ms").unwrap(); + match &prog.statements[0].value { + Statement::Sleep { ms } => assert_eq!(*ms, 50), + other => panic!("expected Sleep, got {other:?}"), + } + } + + #[test] + fn parse_comment_and_blank() { + let src = "# this is a comment\n\ni2c_read mid 0x48 2\n"; + let prog = parse(src).unwrap(); + assert_eq!(prog.statements.len(), 1); + } + + #[test] + fn undefined_constant_is_error() { + let result = parse("i2c_read mid $MISSING 2"); + assert!(result.is_err()); + } + + #[test] + fn parse_nested_repeat() { + let src = "\ + repeat 10\n\ + repeat 5\n\ + i2c_read mid 0x48 2\n\ + end\n\ + end\n"; + let prog = parse(src).unwrap(); + assert_eq!(prog.statements.len(), 1); + match &prog.statements[0].value { + Statement::Repeat { body, .. } => { + assert_eq!(body.len(), 1); + assert!(matches!(&body[0].value, Statement::Repeat { .. })); + } + other => panic!("expected Repeat, got {other:?}"), + } + } + + #[test] + fn parse_raw_block() { + let src = "raw {\n push 0x48\n push_none\n done\n}\n"; + let prog = parse(src).unwrap(); + assert_eq!(prog.statements.len(), 1); + match &prog.statements[0].value { + Statement::Raw { lines } => { + assert_eq!(lines.len(), 3); + } + other => panic!("expected Raw, got {other:?}"), + } + } + + #[test] + fn parse_raw_block_expands_constants() { + let src = ".let ADDR 0x48\nraw {\n push $ADDR\n done\n}\n"; + let prog = parse(src).unwrap(); + match &prog.statements[0].value { + Statement::Raw { lines } => { + assert_eq!(lines[0], "push 0x48"); + } + other => panic!("expected Raw, got {other:?}"), + } + } + + #[test] + fn parse_raw_block_undefined_constant_is_error() { + let src = "raw {\n push $MISSING\n}\n"; + let result = parse(src); + assert!(result.is_err()); + } + + #[test] + fn parse_raw_block_extra_spaces() { + // "raw {" with extra spaces should also work + let src = "raw {\n push 1\n}\n"; + let prog = parse(src).unwrap(); + assert_eq!(prog.statements.len(), 1); + } + + #[test] + fn parse_i2c_write() { + let prog = parse("i2c_write mid 0x48 reg=0x01 0x00,0x80").unwrap(); + match &prog.statements[0].value { + Statement::I2cWrite { data, register, .. } => { + assert_eq!(*register, Some(0x01)); + assert_eq!(data, &[0x00, 0x80]); + } + other => panic!("expected I2cWrite, got {other:?}"), + } + } + + #[test] + fn parse_missing_end_is_error() { + let src = "repeat 10\n i2c_read mid 0x48 2\n"; + let result = parse(src); + assert!(result.is_err()); + let err = result.unwrap_err(); + assert!( + matches!(&err.kind, HifErrorKind::UnmatchedBlock(_)), + "expected UnmatchedBlock, got {err:?}" + ); + } + + #[test] + fn parse_sleep_without_suffix() { + // sleep without "ms" should also parse (bare number = ms) + let prog = parse("sleep 50").unwrap(); + match &prog.statements[0].value { + Statement::Sleep { ms } => assert_eq!(*ms, 50), + other => panic!("expected Sleep, got {other:?}"), + } + } + + #[test] + fn parse_repeat_with_sleep() { + let src = "repeat 5 sleep=10ms\n i2c_read mid 0x48 2\nend\n"; + let prog = parse(src).unwrap(); + match &prog.statements[0].value { + Statement::Repeat { sleep_ms, .. } => { + assert_eq!(*sleep_ms, Some(10)); + } + other => panic!("expected Repeat, got {other:?}"), + } + } + + #[test] + fn parse_symbolic_device_name() { + let prog = parse("i2c_read mid tmp117 reg=0x00 2").unwrap(); + match &prog.statements[0].value { + Statement::I2cRead { address, .. } => { + assert_eq!(*address, DeviceRef::Named("tmp117".into())); + } + other => panic!("expected I2cRead, got {other:?}"), + } + } + + #[test] + fn parse_numeric_address_still_works() { + let prog = parse("i2c_read mid 0x48 reg=0x00 2").unwrap(); + match &prog.statements[0].value { + Statement::I2cRead { address, .. } => { + assert_eq!(*address, DeviceRef::Address(0x48)); + } + other => panic!("expected I2cRead, got {other:?}"), + } + } + + #[test] + fn parse_call_no_args() { + let prog = parse("call QspiReadId").unwrap(); + match &prog.statements[0].value { + Statement::Call { function, args } => { + assert_eq!(function, "QspiReadId"); + assert!(args.is_empty()); + } + other => panic!("expected Call, got {other:?}"), + } + } + + #[test] + fn parse_call_with_args() { + let prog = parse("call SpiRead 0x10 0x20 256").unwrap(); + match &prog.statements[0].value { + Statement::Call { function, args } => { + assert_eq!(function, "SpiRead"); + assert_eq!(args, &[0x10, 0x20, 256]); + } + other => panic!("expected Call, got {other:?}"), + } + } + + #[test] + fn parse_call_missing_function() { + let result = parse("call"); + assert!(result.is_err()); + } +} diff --git a/humility-hif-assembler/src/stats.rs b/humility-hif-assembler/src/stats.rs new file mode 100644 index 000000000..98378a531 --- /dev/null +++ b/humility-hif-assembler/src/stats.rs @@ -0,0 +1,310 @@ +// 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/. + +//! Program statistics computed at assembly time. +//! +//! Since we assembled the program, we know exactly what it will do +//! on the target. This module computes expected stats from the +//! parsed program, without needing any target feedback. + +use serde::{Deserialize, Serialize}; + +use crate::parser::{BusRef, Located, MuxSpec, Statement}; + +/// Expected statistics for a HIF program, computed from the source. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +pub struct ProgramStats { + /// Total I2C read transactions. + pub i2c_reads: usize, + /// Total I2C write transactions. + pub i2c_writes: usize, + /// Total bytes expected to be read from I2C. + pub i2c_read_bytes: usize, + /// Total bytes expected to be written to I2C. + pub i2c_write_bytes: usize, + /// Total I2C bus scans (each scans 128 addresses). + pub i2c_scans: usize, + /// Total I2C register scans (each scans 256 registers). + pub i2c_reg_scans: usize, + /// Estimated mux switch operations. + pub mux_switches: usize, + /// Total Idol RPC calls. + pub idol_calls: usize, + /// Total sleep time in milliseconds. + pub sleep_ms: u64, + /// Distinct I2C buses touched. + pub buses_touched: Vec, +} + +impl ProgramStats { + /// Total I2C transactions (reads + writes + scans). + pub fn total_i2c_transactions(&self) -> usize { + self.i2c_reads + + self.i2c_writes + + self.i2c_scans * 128 + + self.i2c_reg_scans * 256 + } + + /// Total I2C bytes (read + write). + pub fn total_i2c_bytes(&self) -> usize { + self.i2c_read_bytes + self.i2c_write_bytes + } +} + +impl std::fmt::Display for ProgramStats { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + writeln!(f, "I2C transactions: {}", self.total_i2c_transactions())?; + if self.i2c_reads > 0 { + writeln!( + f, + " reads: {} ({} bytes)", + self.i2c_reads, self.i2c_read_bytes + )?; + } + if self.i2c_writes > 0 { + writeln!( + f, + " writes: {} ({} bytes)", + self.i2c_writes, self.i2c_write_bytes + )?; + } + if self.i2c_scans > 0 { + writeln!( + f, + " scans: {} ({}x128 addr)", + self.i2c_scans, self.i2c_scans + )?; + } + if self.i2c_reg_scans > 0 { + writeln!( + f, + " reg scans: {} ({}x256 reg)", + self.i2c_reg_scans, self.i2c_reg_scans + )?; + } + if self.mux_switches > 0 { + writeln!(f, " mux switches: {}", self.mux_switches)?; + } + if self.idol_calls > 0 { + writeln!(f, "Idol calls: {}", self.idol_calls)?; + } + if self.sleep_ms > 0 { + writeln!(f, "Sleep: {}ms", self.sleep_ms)?; + } + if !self.buses_touched.is_empty() { + writeln!(f, "Buses: {}", self.buses_touched.join(", "))?; + } + Ok(()) + } +} + +/// Compute expected stats from a parsed program. +pub fn compute_stats(stmts: &[Located]) -> ProgramStats { + let mut stats = ProgramStats::default(); + walk_statements(stmts, 1, &mut stats, None); + stats.buses_touched.sort(); + stats.buses_touched.dedup(); + stats +} + +fn walk_statements( + stmts: &[Located], + multiplier: usize, + stats: &mut ProgramStats, + prev_mux: Option<&MuxSpec>, +) { + let mut last_mux: Option<&MuxSpec> = prev_mux; + + for stmt in stmts { + match &stmt.value { + Statement::I2cRead { bus, mux, nbytes, .. } => { + stats.i2c_reads += multiplier; + stats.i2c_read_bytes += *nbytes as usize * multiplier; + track_bus(bus, stats); + track_mux_switch( + mux.as_ref(), + &mut last_mux, + multiplier, + stats, + ); + } + Statement::I2cWrite { bus, mux, data, .. } => { + stats.i2c_writes += multiplier; + stats.i2c_write_bytes += data.len() * multiplier; + track_bus(bus, stats); + track_mux_switch( + mux.as_ref(), + &mut last_mux, + multiplier, + stats, + ); + } + Statement::I2cScan { bus, mux } => { + stats.i2c_scans += multiplier; + track_bus(bus, stats); + track_mux_switch( + mux.as_ref(), + &mut last_mux, + multiplier, + stats, + ); + } + Statement::I2cRegScan { bus, mux, .. } => { + stats.i2c_reg_scans += multiplier; + track_bus(bus, stats); + track_mux_switch( + mux.as_ref(), + &mut last_mux, + multiplier, + stats, + ); + } + Statement::IdolCall { .. } => { + stats.idol_calls += multiplier; + } + Statement::Call { .. } => { + // Generic function calls — counted in estimated_results + // by the lowering pass but not categorized here. + } + Statement::Sleep { ms } => { + stats.sleep_ms += *ms as u64 * multiplier as u64; + } + Statement::Repeat { count, sleep_ms, body } => { + walk_statements( + body, + multiplier * *count as usize, + stats, + last_mux, + ); + if let Some(ms) = sleep_ms { + stats.sleep_ms += + *ms as u64 * multiplier as u64 * *count as u64; + } + } + Statement::Raw { .. } => { + // Can't analyze raw ops + } + } + } +} + +fn track_bus(bus: &BusRef, stats: &mut ProgramStats) { + if let BusRef::Named(name) = bus { + if !stats.buses_touched.contains(name) { + stats.buses_touched.push(name.clone()); + } + } +} + +fn track_mux_switch( + current: Option<&MuxSpec>, + last: &mut Option<&MuxSpec>, + multiplier: usize, + stats: &mut ProgramStats, +) { + match (current, *last) { + (Some(cur), Some(prev)) => { + if cur.address != prev.address || cur.segment != prev.segment { + stats.mux_switches += multiplier; + } + } + (Some(_), None) => { + // First muxed access — the mux needs to be configured + stats.mux_switches += multiplier; + } + _ => {} + } + // Note: We can't update last_mux to point to current because of + // lifetime issues with the Statement borrow. For accurate mux + // switch counting across iterations, this is an approximation. +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::parser::parse; + + fn stats_for(src: &str) -> ProgramStats { + let prog = parse(src).unwrap(); + compute_stats(&prog.statements) + } + + #[test] + fn single_read() { + let s = stats_for("i2c_read mid 0x48 reg=0x00 2"); + assert_eq!(s.i2c_reads, 1); + assert_eq!(s.i2c_read_bytes, 2); + assert_eq!(s.total_i2c_transactions(), 1); + } + + #[test] + fn repeat_loop() { + let s = stats_for("repeat 100\n i2c_read mid 0x48 reg=0x00 2\nend"); + assert_eq!(s.i2c_reads, 100); + assert_eq!(s.i2c_read_bytes, 200); + } + + #[test] + fn multi_device() { + let s = stats_for( + "repeat 50\n i2c_read mid 0x48 2\n i2c_read mid 0x49 2\nend", + ); + assert_eq!(s.i2c_reads, 100); + assert_eq!(s.total_i2c_transactions(), 100); + } + + #[test] + fn write_stats() { + let s = stats_for("i2c_write mid 0x48 reg=0x01 0x00,0x80"); + assert_eq!(s.i2c_writes, 1); + assert_eq!(s.i2c_write_bytes, 2); + } + + #[test] + fn scan_stats() { + let s = stats_for("i2c_scan mid"); + assert_eq!(s.i2c_scans, 1); + assert_eq!(s.total_i2c_transactions(), 128); + } + + #[test] + fn sleep_stats() { + let s = stats_for("repeat 10 sleep=50ms\n i2c_read mid 0x48 2\nend"); + assert_eq!(s.sleep_ms, 500); + } + + #[test] + fn idol_stats() { + let s = stats_for("repeat 20\n idol Sensor.get id=0\nend"); + assert_eq!(s.idol_calls, 20); + } + + #[test] + fn multi_bus() { + let s = stats_for("i2c_read mid 0x48 2\ni2c_read front 0x49 2"); + assert_eq!(s.buses_touched.len(), 2); + } + + #[test] + fn mux_switching() { + let s = stats_for( + "repeat 10\n\ + i2c_read front 0x50 mux=0x70.1 1\n\ + i2c_read front 0x50 mux=0x70.2 1\n\ + end", + ); + assert_eq!(s.i2c_reads, 20); + // At least some mux switches detected + assert!(s.mux_switches > 0); + } + + #[test] + fn display() { + let s = stats_for("repeat 100\n i2c_read mid 0x48 reg=0x00 2\nend"); + let output = format!("{s}"); + assert!(output.contains("I2C transactions: 100")); + assert!(output.contains("reads: 100 (200 bytes)")); + assert!(output.contains("Buses: mid")); + } +} diff --git a/humility-hif-assembler/src/types.rs b/humility-hif-assembler/src/types.rs new file mode 100644 index 000000000..b3aa13ddf --- /dev/null +++ b/humility-hif-assembler/src/types.rs @@ -0,0 +1,230 @@ +// 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/. + +//! Data types for the HIF assembler. +//! +//! These types describe the target configuration, I2C topology, HIF +//! functions, and Idol interfaces. They are all `Serialize + +//! Deserialize` so they can be used as JSON fixtures and API +//! contracts. + +use serde::{Deserialize, Serialize}; + +/// Maximum number of data bytes in a single HIF i2c_write call. +/// +/// This matches the 17-byte write buffer in the hiffy task's +/// `i2c_write` function (16 data bytes + 1 optional register byte). +/// See `task/hiffy/src/stm32h7.rs` line 252. +pub const I2C_WRITE_MAX_DATA: usize = 16; + +/// Maximum number of HIF labels (loop nesting depth). +pub const MAX_LABELS: usize = 4; + +/// Resolved I2C bus: the numeric values needed by HIF i2c_read/write, +/// plus the devices and muxes known to be on this bus. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ResolvedBus { + pub name: String, + pub controller: u8, + pub port_index: u8, + pub port_name: String, + /// Devices directly on this bus (no mux). + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub devices: Vec, + /// Muxes on this bus, each with their own devices per segment. + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub muxes: Vec, +} + +/// A known I2C device on a bus. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct I2cDeviceInfo { + /// I2C address (7-bit). + pub address: u8, + /// Part name (e.g. "tmp117", "tps546b24a", "max5970"). + pub device: String, + /// Human-readable name if assigned (e.g. "Southeast temperature"). + #[serde(default, skip_serializing_if = "Option::is_none")] + pub name: Option, + /// Description from the config. + pub description: String, + /// Whether this device may be physically absent. + #[serde(default)] + pub removable: bool, + /// Sensor names associated with this device, if any. + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub sensors: Vec, +} + +/// A sensor associated with an I2C device. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct SensorInfo { + pub name: String, + pub kind: String, +} + +/// An I2C mux on a bus. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct I2cMuxInfo { + /// Mux I2C address. + pub address: u8, + /// Devices behind each segment. Key is segment number. + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub segments: Vec, +} + +/// One segment of a mux, with the devices behind it. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct I2cMuxSegment { + pub segment: u8, + pub devices: Vec, +} + +/// Information about a HIF function available on the target. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct FunctionInfo { + /// Function name from DWARF (PascalCase, e.g. "I2cRead"). + pub name: String, + /// Function ID (index in the HIFFY_FUNCTIONS enum). + pub id: u8, + /// Number of arguments the function expects. + pub arg_count: usize, + /// Argument names and types, in order. Empty if type info + /// couldn't be extracted from DWARF. + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub args: Vec, + /// Error variant names by numeric code. + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub errors: Vec, +} + +/// A named, typed argument to a HIF function. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct FunctionArg { + pub name: String, + /// Type description (e.g. "u8", "Option", "Controller"). + pub ty: String, + /// Size in bytes, if known. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub size: Option, +} + +/// A named error code returned by a HIF function. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct FunctionError { + pub code: u32, + pub name: String, +} + +/// Target buffer sizes extracted from the archive. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct BufferSizes { + /// HIFFY_TEXT: program bytecode buffer (bytes). + pub text: usize, + /// HIFFY_DATA: input data buffer for bulk writes (bytes). + pub data: usize, + /// HIFFY_RSTACK: return stack for function results (bytes). + pub rstack: usize, + /// HIFFY_SCRATCH: workspace for function execution (bytes). + pub scratch: usize, +} + +/// Everything the assembler needs to know about a target. +/// +/// This is the intermediate form between a Hubris archive and the +/// assembler. It can be: +/// +/// - Extracted from a `HubrisArchive` via +/// [`TargetConfig::from_archive_file`] or +/// [`TargetConfig::from_archive`]. +/// - Constructed directly for unit testing. +/// - Serialized to JSON for caching or inspection. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct TargetConfig { + /// Image ID from the Hubris archive. + pub image_id: Vec, + /// Board name (e.g. "gimlet-c-dev", "cosmo-b-dev"). + pub board: String, + /// Known I2C buses on this target. + pub buses: Vec, + /// HIF functions available in the hiffy task. + pub functions: Vec, + /// Target buffer sizes. + pub buffer_sizes: BufferSizes, + /// Idol interfaces available on this target. + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub idol_interfaces: Vec, +} + +/// An Idol interface exposed by a Hubris task. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct IdolInterfaceInfo { + /// Interface name (e.g. "Sensor", "Thermal", "Validate"). + pub name: String, + /// Task name that implements this interface (e.g. "sensor", "thermal"). + pub task: String, + /// Task ID (index in the Hubris task table). + pub task_id: u32, + /// Operations, in declaration order. The operation code is + /// index + 1 (1-based, per Idol convention). + pub ops: Vec, +} + +/// A single operation in an Idol interface. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct IdolOpInfo { + /// Operation name (e.g. "get", "set_mode", "validate_i2c"). + pub name: String, + /// Operation code (1-based index in the interface). + pub code: u16, + /// Arguments, in declaration order. + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub args: Vec, + /// Total argument payload size in bytes. + #[serde(default)] + pub args_size: usize, + /// Reply type name (e.g. "f32", "()", "SensorReading"). + pub reply: String, + /// Reply payload size in bytes (needed by the Send HIF function). + #[serde(default)] + pub reply_size: usize, + /// Error type name, if the operation can fail (e.g. "SensorError"). + #[serde(default, skip_serializing_if = "Option::is_none")] + pub error: Option, + /// Encoding used for arguments and replies. + #[serde(default = "default_encoding")] + pub encoding: String, + /// Leases (read/write buffers), if any. + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub leases: Vec, + /// Whether clients should auto-retry on server death. + #[serde(default)] + pub idempotent: bool, +} + +/// An argument to an Idol operation. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct IdolArgInfo { + /// Argument name. + pub name: String, + /// Type name (e.g. "u32", "SensorId", "bool"). + pub ty: String, +} + +fn default_encoding() -> String { + "hubpack".to_string() +} + +/// A lease (buffer) parameter for an Idol operation. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct IdolLeaseInfo { + /// Lease name. + pub name: String, + /// Type being leased. + pub ty: String, + /// Server can read from this lease. + pub read: bool, + /// Server can write to this lease. + pub write: bool, +} diff --git a/humility-hif-assembler/tests/archive_integration.rs b/humility-hif-assembler/tests/archive_integration.rs new file mode 100644 index 000000000..59c48e524 --- /dev/null +++ b/humility-hif-assembler/tests/archive_integration.rs @@ -0,0 +1,195 @@ +// 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/. + +//! Integration tests that load real Hubris archives. +//! +//! These tests are skipped if the archives aren't present (they are +//! build artifacts, not checked in). Set HUBRIS_ARCHIVE to point +//! at a specific archive, or the test will look in common locations. + +use std::path::PathBuf; + +use humility_hif_assembler::assembler::{HifAssembler, TargetConfig}; + +/// Find a Hubris archive for testing. +/// +/// Set `HUBRIS_ARCHIVE` to a `.zip` path. Tests are skipped if unset. +/// +/// ```bash +/// # Build an archive and run integration tests: +/// cd ~/Oxide/src/hubris/master +/// cargo xtask dist app/gimlet/rev-c-dev.toml +/// HUBRIS_ARCHIVE=$(cargo -q xtask print --archive app/gimlet/rev-c-dev.toml) \ +/// cargo test -p humility-hif-assembler --test archive_integration +/// ``` +fn find_archive() -> Option { + let path = std::env::var("HUBRIS_ARCHIVE").ok()?; + if std::path::Path::new(&path).exists() { + Some(path) + } else { + eprintln!("HUBRIS_ARCHIVE={path} does not exist"); + None + } +} + +#[test] +fn load_archive_and_list() { + let path = match find_archive() { + Some(p) => p, + None => { + eprintln!( + "skipping: no Hubris archive found; \ + set HUBRIS_ARCHIVE or build a dev image" + ); + return; + } + }; + + eprintln!("loading archive: {path}"); + let config = + TargetConfig::from_archive_file(&path).expect("failed to load archive"); + + eprintln!("board: {}", config.board); + eprintln!("image_id: {:02x?}", config.image_id); + assert!(!config.image_id.is_empty(), "image ID should not be empty"); + assert!(!config.board.is_empty(), "board name should not be empty"); + + // Should have I2C buses + eprintln!("buses ({}):", config.buses.len()); + for bus in &config.buses { + eprintln!( + " {:<12} controller={} port={}:{}", + bus.name, bus.controller, bus.port_name, bus.port_index, + ); + } + assert!(!config.buses.is_empty(), "should have at least one I2C bus"); + + // Should have HIF functions + eprintln!("functions ({}):", config.functions.len()); + for f in &config.functions { + eprintln!(" [{:>2}] {:<24} args={}", f.id, f.name, f.arg_count); + } + assert!( + config.functions.len() >= 3, + "expected at least Sleep, Send, and one I2C function" + ); + + // Should include known functions + let names: Vec<&str> = + config.functions.iter().map(|f| f.name.as_str()).collect(); + assert!(names.contains(&"Sleep"), "missing Sleep function"); + assert!(names.contains(&"Send"), "missing Send function"); + + // Buffer sizes should be reasonable + eprintln!( + "buffers: text={} data={} rstack={} scratch={}", + config.buffer_sizes.text, + config.buffer_sizes.data, + config.buffer_sizes.rstack, + config.buffer_sizes.scratch, + ); + assert!(config.buffer_sizes.text >= 256, "text buffer too small"); + assert!(config.buffer_sizes.rstack >= 64, "rstack buffer too small"); + + // Should serialize to JSON and back + let json = serde_json::to_string_pretty(&config) + .expect("failed to serialize TargetConfig"); + eprintln!("config JSON size: {} bytes", json.len()); + let roundtrip: TargetConfig = + serde_json::from_str(&json).expect("failed to deserialize"); + assert_eq!(roundtrip.board, config.board); + assert_eq!(roundtrip.functions.len(), config.functions.len()); +} + +#[test] +fn assemble_against_real_archive() { + let path = match find_archive() { + Some(p) => p, + None => { + eprintln!("skipping: no Hubris archive found"); + return; + } + }; + + let config = + TargetConfig::from_archive_file(&path).expect("failed to load archive"); + + // Pick the first bus for testing + let bus_name = match config.buses.first() { + Some(b) => b.name.clone(), + None => { + eprintln!("skipping: no I2C buses in archive"); + return; + } + }; + + let asm = HifAssembler::new(config); + + // Simple I2C read + let src = format!("i2c_read {bus_name} 0x48 reg=0x00 2"); + let out = asm.assemble(&src).expect("assemble failed"); + assert!(out.bundle.fits_in_target()); + eprintln!( + "simple read: {} bytes text, image_id={:02x?}", + out.bundle.text.len(), + out.bundle.metadata.image_id, + ); + + // Loop + let src = format!("repeat 100\n i2c_read {bus_name} 0x48 reg=0x00 2\nend"); + let out = asm.assemble(&src).expect("assemble loop failed"); + assert!(out.bundle.fits_in_target()); + assert_eq!(out.bundle.metadata.estimated_results, Some(100)); + eprintln!("loop: {} bytes text", out.bundle.text.len()); + + // Verify mode + let report = asm.verify(&format!( + "repeat 100\n i2c_read {bus_name} 0x48 reg=0x00 2\nend" + )); + assert!(report.ok); + eprintln!("verify:\n{report}"); +} + +/// Generate a fixture JSON file from a real archive. +/// +/// Run with: +/// ```bash +/// HUBRIS_ARCHIVE=path/to/archive.zip \ +/// GENERATE_FIXTURE=1 \ +/// cargo test -p humility-hif-assembler --test archive_integration \ +/// generate_fixture -- --nocapture +/// ``` +#[test] +fn generate_fixture() { + if std::env::var("GENERATE_FIXTURE").is_err() { + return; + } + + let path = match find_archive() { + Some(p) => p, + None => { + eprintln!("skipping: no Hubris archive found"); + return; + } + }; + + let config = + TargetConfig::from_archive_file(&path).expect("failed to load archive"); + + let json = + serde_json::to_string_pretty(&config).expect("failed to serialize"); + + let fixture_name = format!("{}.json", config.board); + let fixture_dir: PathBuf = + [env!("CARGO_MANIFEST_DIR"), "fixtures"].iter().collect(); + let fixture_path = fixture_dir.join(&fixture_name); + + std::fs::create_dir_all(&fixture_dir).ok(); + std::fs::write(&fixture_path, &json).expect("failed to write fixture"); + eprintln!( + "wrote fixture: {} ({} bytes)", + fixture_path.display(), + json.len(), + ); +} diff --git a/humility-hif-assembler/tests/equivalent_tests.rs b/humility-hif-assembler/tests/equivalent_tests.rs new file mode 100644 index 000000000..3a82b38f9 --- /dev/null +++ b/humility-hif-assembler/tests/equivalent_tests.rs @@ -0,0 +1,292 @@ +// 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/. + +//! Tests that verify our assembler produces the same ops as +//! humility's hardcoded HIF programs. +//! +//! Each test assembles an equivalent .hif program and checks +//! the generated ops match the expected pattern. + +use std::path::PathBuf; + +use humility_hif_assembler::assembler::{HifAssembler, TargetConfig}; + +fn fixture_dir() -> PathBuf { + [env!("CARGO_MANIFEST_DIR"), "fixtures"].iter().collect() +} + +fn examples_dir() -> PathBuf { + [env!("CARGO_MANIFEST_DIR"), "examples"].iter().collect() +} + +fn load_gimlet() -> TargetConfig { + let path = fixture_dir().join("gimlet-c.json"); + let json = std::fs::read_to_string(&path) + .unwrap_or_else(|e| panic!("reading {}: {e}", path.display())); + serde_json::from_str(&json) + .unwrap_or_else(|e| panic!("parsing {}: {e}", path.display())) +} + +fn assemble(config: TargetConfig, source: &str) -> Vec { + let asm = HifAssembler::new(config); + let output = asm.assemble(source).unwrap_or_else(|e| { + panic!("assembling failed: {e}\nsource:\n{source}") + }); + output.ops +} + +fn assemble_file(config: TargetConfig, name: &str) -> Vec { + let path = examples_dir().join(name); + let source = std::fs::read_to_string(&path) + .unwrap_or_else(|e| panic!("reading {}: {e}", path.display())); + assemble(config, &source) +} + +// +// Equivalent program tests: +// Tedious testing to make sure that we can reproduce all of the +// hard-coded HIF programs that exist in humility prior to the +// introduction of the HIF assembler. +// + +#[test] +fn equivalent_qspi_read_id() { + let ops = assemble_file(load_gimlet(), "equivalent-qspi-read-id.hif"); + // Should be: Call(QspiReadId), Done + assert!(ops.iter().any(|op| matches!(op, hif::Op::Call(_)))); + assert_eq!(*ops.last().unwrap(), hif::Op::Done); + // No loops, no drops (zero-arg function) + assert!(!ops.iter().any(|op| matches!(op, hif::Op::Label(_)))); +} + +#[test] +fn equivalent_qspi_read_status() { + let ops = assemble_file(load_gimlet(), "equivalent-qspi-read-status.hif"); + assert!(ops.iter().any(|op| matches!(op, hif::Op::Call(_)))); + assert_eq!(*ops.last().unwrap(), hif::Op::Done); +} + +#[test] +fn equivalent_i2c_scan() { + let ops = assemble_file(load_gimlet(), "equivalent-i2c-scan.hif"); + // Should have: Label, BranchGreaterThanOrEqualTo (scan loop) + assert!(ops.iter().any(|op| matches!(op, hif::Op::Label(_)))); + assert!(ops + .iter() + .any(|op| matches!(op, hif::Op::BranchGreaterThanOrEqualTo(_)))); + // Should call I2cRead + assert!(ops.iter().any(|op| matches!(op, hif::Op::Call(_)))); + // Should push 128 as the scan limit + assert!(ops.iter().any(|op| matches!(op, hif::Op::Push(128)))); +} + +#[test] +fn equivalent_i2c_read() { + let ops = assemble_file(load_gimlet(), "equivalent-i2c-read.hif"); + // Simple: push params, Call, DropN(7), Done + assert!(ops.iter().any(|op| matches!(op, hif::Op::Call(_)))); + assert!(ops.iter().any(|op| matches!(op, hif::Op::DropN(7)))); + assert_eq!(*ops.last().unwrap(), hif::Op::Done); + // No loops + assert!(!ops.iter().any(|op| matches!(op, hif::Op::Label(_)))); +} + +#[test] +fn equivalent_i2c_regscan() { + let ops = assemble_file(load_gimlet(), "equivalent-i2c-regscan.hif"); + // Should have: Label, BranchGreaterThanOrEqualTo (regscan loop) + assert!(ops.iter().any(|op| matches!(op, hif::Op::Label(_)))); + assert!(ops + .iter() + .any(|op| matches!(op, hif::Op::BranchGreaterThanOrEqualTo(_)))); + // Should push 0xff as the scan limit + assert!(ops.iter().any(|op| matches!(op, hif::Op::Push(0xff)))); +} + +#[test] +fn equivalent_gpio_input() { + let ops = assemble_file(load_gimlet(), "equivalent-gpio-input.hif"); + // call GpioInput 5 -> Push(5), Call, DropN(1), Done + assert!(ops.iter().any(|op| matches!(op, hif::Op::Push(5)))); + assert!(ops.iter().any(|op| matches!(op, hif::Op::Call(_)))); + assert!(ops.iter().any(|op| matches!(op, hif::Op::DropN(1)))); +} + +#[test] +fn equivalent_gpio_toggle() { + let ops = assemble_file(load_gimlet(), "equivalent-gpio-toggle.hif"); + // call GpioToggle 5 1 -> Push(5), Push(1), Call, DropN(2), Done + assert!(ops.iter().any(|op| matches!(op, hif::Op::Push(5)))); + assert!(ops.iter().any(|op| matches!(op, hif::Op::Call(_)))); + assert!(ops.iter().any(|op| matches!(op, hif::Op::DropN(2)))); +} + +#[test] +fn equivalent_hash_digest() { + let ops = assemble_file(load_gimlet(), "equivalent-hash-digest.hif"); + // call HashDigest 32 -> Push(32), Call, DropN(1), Done + assert!(ops.iter().any(|op| matches!(op, hif::Op::Push(32)))); + assert!(ops.iter().any(|op| matches!(op, hif::Op::Call(_)))); +} + +#[test] +fn equivalent_idol_sensor_get() { + let ops = assemble_file(load_gimlet(), "equivalent-idol-sensor-get.hif"); + // Idol call: push task_id, op_code, payload, sizes, Call(Send) + assert!(ops.iter().any(|op| matches!(op, hif::Op::Call(_)))); + // Should have a DropN to clean up the frame + assert!(ops.iter().any(|op| matches!(op, hif::Op::DropN(_)))); +} + +#[test] +fn equivalent_validate_i2c() { + let ops = assemble_file(load_gimlet(), "equivalent-validate-i2c.hif"); + // 4 Idol calls, each with Call + DropN + let call_count = + ops.iter().filter(|op| matches!(op, hif::Op::Call(_))).count(); + assert_eq!(call_count, 4, "expected 4 Idol calls"); +} + +#[test] +fn all_equivalent_examples_assemble() { + let config = load_gimlet(); + let asm = HifAssembler::new(config); + let dir = examples_dir(); + + let mut count = 0; + for entry in std::fs::read_dir(&dir).unwrap() { + let entry = entry.unwrap(); + let path = entry.path(); + let name = path.file_name().unwrap().to_string_lossy(); + if name.starts_with("equivalent-") && name.ends_with(".hif") { + let source = std::fs::read_to_string(&path).unwrap(); + let report = asm.verify(&source); + assert!(report.ok, "{name}: {report}",); + count += 1; + } + } + assert!( + count >= 8, + "expected at least 8 equivalent examples, found {count}" + ); +} + +// ProgramBuilder equivalents: +// These show how a Rust program can construct the the pre-assembler +// existing HIF programs using the ProgramBuilder API. + +#[test] +fn builder_qspi_read_id() { + use humility_hif_assembler::builder::ProgramBuilder; + + let mut prog = ProgramBuilder::new(); + prog.call("QspiReadId", &[]); + + let ops = assemble(load_gimlet(), &prog.finish()); + assert!(ops.iter().any(|op| matches!(op, hif::Op::Call(_)))); +} + +#[test] +fn builder_i2c_stress() { + use humility_hif_assembler::builder::ProgramBuilder; + + let mut prog = ProgramBuilder::new(); + prog.comment("I2C stress: 100 reads on mid bus"); + prog.repeat(100, |body| { + body.i2c_read("mid", 0x48, Some(0x00), 2); + }); + + let config = load_gimlet(); + let asm = HifAssembler::new(config); + let output = asm.assemble(&prog.finish()).unwrap(); + assert!(output.bundle.fits_in_target()); + assert_eq!(output.stats.i2c_reads, 100); + assert_eq!(output.stats.i2c_read_bytes, 200); +} + +#[test] +fn builder_multi_bus_interleave() { + use humility_hif_assembler::builder::ProgramBuilder; + + let mut prog = ProgramBuilder::new(); + prog.repeat(50, |body| { + body.i2c_read("mid", 0x24, Some(0x00), 2); + body.i2c_read("rear", 0x48, Some(0x00), 2); + }); + + let config = load_gimlet(); + let asm = HifAssembler::new(config); + let output = asm.assemble(&prog.finish()).unwrap(); + assert_eq!(output.stats.i2c_reads, 100); + assert_eq!(output.stats.buses_touched.len(), 2); +} + +#[test] +fn builder_idol_sensor_loop() { + use humility_hif_assembler::builder::ProgramBuilder; + + let mut prog = ProgramBuilder::new(); + prog.repeat(20, |body| { + body.idol_call("Sensor", "get", &[("id", "0")]); + }); + + let config = load_gimlet(); + let asm = HifAssembler::new(config); + let output = asm.assemble(&prog.finish()).unwrap(); + assert_eq!(output.stats.idol_calls, 20); +} + +#[test] +fn builder_mixed_i2c_and_idol() { + use humility_hif_assembler::builder::ProgramBuilder; + + let mut prog = ProgramBuilder::new(); + prog.comment("Mixed stress: I2C + Idol"); + prog.repeat(30, |body| { + body.i2c_read("mid", 0x48, Some(0x00), 2); + body.idol_call("Sensor", "get", &[("id", "0")]); + body.sleep_ms(5); + }); + + let config = load_gimlet(); + let asm = HifAssembler::new(config); + let output = asm.assemble(&prog.finish()).unwrap(); + assert_eq!(output.stats.i2c_reads, 30); + assert_eq!(output.stats.idol_calls, 30); + assert_eq!(output.stats.sleep_ms, 150); +} + +#[test] +fn builder_mux_switching() { + use humility_hif_assembler::builder::ProgramBuilder; + + let mut prog = ProgramBuilder::new(); + prog.repeat(20, |body| { + body.i2c_read_mux("front", 0x50, 0x70, 1, None, 1); + body.i2c_read_mux("front", 0x50, 0x70, 2, None, 1); + }); + + let config = load_gimlet(); + let asm = HifAssembler::new(config); + let output = asm.assemble(&prog.finish()).unwrap(); + assert_eq!(output.stats.i2c_reads, 40); + assert!(output.stats.mux_switches > 0); +} + +#[test] +fn builder_call_generic_functions() { + use humility_hif_assembler::builder::ProgramBuilder; + + // Show that any HIF function can be called by name + let mut prog = ProgramBuilder::new(); + prog.call("QspiReadId", &[]); + prog.call("QspiReadStatus", &[]); + prog.call("HashInit", &[]); + prog.call("HashDigest", &[32]); + + let config = load_gimlet(); + let asm = HifAssembler::new(config); + let output = asm.assemble(&prog.finish()).unwrap(); + assert!(output.bundle.fits_in_target()); +} diff --git a/humility-hif-assembler/tests/fixture_tests.rs b/humility-hif-assembler/tests/fixture_tests.rs new file mode 100644 index 000000000..7a6102fd5 --- /dev/null +++ b/humility-hif-assembler/tests/fixture_tests.rs @@ -0,0 +1,253 @@ +// 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/. + +//! Tests that load checked-in TargetConfig fixtures. +//! +//! These tests don't need a Hubris archive — they use pre-generated +//! JSON files from `fixtures/`. + +use std::path::PathBuf; + +use humility_hif_assembler::assembler::{HifAssembler, TargetConfig}; + +fn fixture_dir() -> PathBuf { + [env!("CARGO_MANIFEST_DIR"), "fixtures"].iter().collect() +} + +fn load_fixture(name: &str) -> TargetConfig { + let path = fixture_dir().join(name); + let json = std::fs::read_to_string(&path) + .unwrap_or_else(|e| panic!("reading {}: {e}", path.display())); + serde_json::from_str(&json) + .unwrap_or_else(|e| panic!("parsing {}: {e}", path.display())) +} + +fn examples_dir() -> PathBuf { + [env!("CARGO_MANIFEST_DIR"), "examples"].iter().collect() +} + +fn load_gimlet() -> TargetConfig { + load_fixture("gimlet-c.json") +} + +#[test] +fn fixture_loads_and_has_topology() { + let config = load_gimlet(); + assert_eq!(config.board, "gimlet-c"); + assert!(!config.image_id.is_empty()); + assert!(config.buses.len() >= 4); + assert!(config.functions.len() >= 10); + + // Mid bus should have direct devices + let mid = config.buses.iter().find(|b| b.name == "mid").unwrap(); + assert!(!mid.devices.is_empty()); + + // Front bus should have muxes + let front = config.buses.iter().find(|b| b.name == "front").unwrap(); + assert!(!front.muxes.is_empty()); + + // Devices should have sensors + let has_sensors = mid.devices.iter().any(|d| !d.sensors.is_empty()); + assert!(has_sensors, "expected some devices with sensors on mid bus"); +} + +#[test] +fn assemble_i2c_read_by_address() { + let asm = HifAssembler::new(load_gimlet()); + let out = + asm.assemble("i2c_read mid 0x48 reg=0x00 2").expect("assemble failed"); + assert!(out.bundle.fits_in_target()); + assert!(!out.bundle.metadata.image_id.is_empty()); +} + +#[test] +fn assemble_i2c_read_by_device_name() { + // sbtsi is unique on the mid bus + let asm = HifAssembler::new(load_gimlet()); + let out = asm + .assemble("i2c_read mid sbtsi reg=0x01 2") + .expect("assemble with device name failed"); + assert!(out.bundle.fits_in_target()); +} + +#[test] +fn assemble_stress_loop_by_name() { + let asm = HifAssembler::new(load_gimlet()); + let src = "repeat 200\n i2c_read rear max31790 reg=0x18 2\nend"; + let out = asm.assemble(src).expect("assemble failed"); + assert!(out.bundle.fits_in_target()); + assert_eq!(out.bundle.metadata.estimated_results, Some(200)); +} + +#[test] +fn assemble_muxed_read_from_fixture() { + let config = load_gimlet(); + let front = config.buses.iter().find(|b| b.name == "front").unwrap(); + let mux = &front.muxes[0]; + let seg = &mux.segments[0]; + let dev = &seg.devices[0]; + + // Use the device part name instead of hex address + let src = format!( + "i2c_read front {} mux=0x{:02x}.{} 1", + dev.device, mux.address, seg.segment, + ); + let asm = HifAssembler::new(config); + let out = asm.assemble(&src).expect("assemble muxed read failed"); + assert!(out.bundle.fits_in_target()); +} + +#[test] +fn assemble_bus_scan_from_fixture() { + let asm = HifAssembler::new(load_gimlet()); + let report = asm.verify("i2c_scan mid"); + assert!(report.ok); + assert_eq!(report.estimated_results, 128); +} + +#[test] +fn assemble_multi_bus_from_fixture() { + let config = load_gimlet(); + let bus_names: Vec<&str> = + config.buses.iter().map(|b| b.name.as_str()).collect(); + + // Read from each bus in sequence + let mut lines = vec![]; + for name in &bus_names { + lines.push(format!("i2c_read {name} 0x48 2")); + } + let src = lines.join("\n"); + + let asm = HifAssembler::new(config); + let out = asm.assemble(&src).expect("assemble multi-bus failed"); + assert!(out.bundle.fits_in_target()); +} + +#[test] +fn verify_reports_all_buses() { + let config = load_gimlet(); + let bus_names: Vec = + config.buses.iter().map(|b| b.name.clone()).collect(); + + let mut lines = vec![]; + for name in &bus_names { + lines.push(format!("i2c_read {name} 0x48 2")); + } + let src = lines.join("\n"); + + let asm = HifAssembler::new(config); + let report = asm.verify(&src); + assert!(report.ok); + for name in &bus_names { + assert!( + report.buses_used.contains(name), + "expected bus '{name}' in report" + ); + } +} + +#[test] +fn cosmo_fixture_loads() { + let path = fixture_dir().join("cosmo-b.json"); + if !path.exists() { + eprintln!("skipping: cosmo-b.json not found"); + return; + } + let config = load_fixture("cosmo-b.json"); + assert_eq!(config.board, "cosmo-b"); + assert!(!config.buses.is_empty()); + + let asm = HifAssembler::new(config); + let report = asm.verify("i2c_read front 0x48 2"); + assert!(report.ok, "verify failed: {report}"); +} + +#[test] +fn assemble_idol_sensor_get() { + let asm = HifAssembler::new(load_gimlet()); + let out = asm + .assemble("idol Sensor.get id=0") + .expect("assemble idol Sensor.get failed"); + assert!(out.bundle.fits_in_target()); +} + +#[test] +fn assemble_idol_thermal_get_mode() { + let config = load_gimlet(); + let has_thermal = + config.idol_interfaces.iter().any(|i| i.name == "Thermal"); + if !has_thermal { + eprintln!("skipping: no Thermal interface in fixture"); + return; + } + let asm = HifAssembler::new(config); + let out = asm + .assemble("idol Thermal.get_mode") + .expect("assemble idol Thermal.get_mode failed"); + assert!(out.bundle.fits_in_target()); +} + +#[test] +fn assemble_idol_in_stress_loop() { + let asm = HifAssembler::new(load_gimlet()); + let src = "repeat 50\n idol Sensor.get id=0\nend"; + let out = asm.assemble(src).expect("assemble idol loop failed"); + assert!(out.bundle.fits_in_target()); + assert_eq!(out.bundle.metadata.estimated_results, Some(50)); +} + +#[test] +fn all_examples_assemble() { + let asm = HifAssembler::new(load_gimlet()); + let dir = examples_dir(); + + let mut count = 0; + for entry in std::fs::read_dir(&dir) + .unwrap_or_else(|e| panic!("reading {}: {e}", dir.display())) + { + let entry = entry.unwrap(); + let path = entry.path(); + if path.extension().map(|e| e == "hif").unwrap_or(false) { + let source = std::fs::read_to_string(&path) + .unwrap_or_else(|e| panic!("reading {}: {e}", path.display())); + let report = asm.verify(&source); + assert!( + report.ok, + "{}: {report}", + path.file_name().unwrap().to_string_lossy(), + ); + count += 1; + } + } + assert!(count >= 8, "expected at least 8 examples, found {count}"); +} + +#[test] +fn assemble_symbolic_device_name() { + let config = load_gimlet(); + // gimlet mid bus has tps546b24a at 0x24 + let asm = HifAssembler::new(config); + let out = asm + .assemble("i2c_read mid tps546b24a reg=0x00 1") + .expect("assemble with device name failed"); + assert!(out.bundle.fits_in_target()); +} + +#[test] +fn assemble_symbolic_device_name_case_insensitive() { + let config = load_gimlet(); + let asm = HifAssembler::new(config); + let out = asm + .assemble("i2c_read mid TPS546B24A reg=0x00 1") + .expect("assemble with uppercase device name failed"); + assert!(out.bundle.fits_in_target()); +} + +#[test] +fn assemble_unknown_device_name_fails() { + let config = load_gimlet(); + let asm = HifAssembler::new(config); + let result = asm.assemble("i2c_read mid nonexistent_device reg=0x00 1"); + assert!(result.is_err()); +} diff --git a/humility-hiffy/src/lib.rs b/humility-hiffy/src/lib.rs index 84da6c718..c3238e0b9 100644 --- a/humility-hiffy/src/lib.rs +++ b/humility-hiffy/src/lib.rs @@ -9,7 +9,7 @@ use hif::*; use humility::core::{Core, NetAgent}; use humility::hubris::*; use humility::reflect::{self, Load, Value}; -use humility_doppel::{RpcHeader, SchedState, StaticCell, TaskState}; +use humility_doppel::{RpcHeader, SchedState, StaticCell, TaskState, hiffy}; use humility_idol as idol; pub use humility_idol::IpcError; use postcard::{take_from_bytes, to_slice}; @@ -34,6 +34,22 @@ enum State { pub struct HiffyContext<'a> { hubris: &'a HubrisArchive, + scratch_size: usize, + timeout: u32, + state: State, + functions: HiffyFunctions, + hiffy: HiffyImpl<'a>, +} + +// Constants when running with the `NetUdpRpc` impl, which runs the program on +// the host computer. These values are much larger than anything we'd see on +// the embedded system, so we can run any program. +const HIFFY_NET_TEXT_SIZE: usize = 65536; +const HIFFY_NET_RSTACK_SIZE: usize = 65536; +const HIFFY_NET_SCRATCH_SIZE: usize = 65536; + +/// Variables used when interacting with the `hiffy` task +struct HiffyVars<'a> { ready: &'a HubrisVariable, kick: &'a HubrisVariable, text: &'a HubrisVariable, @@ -42,12 +58,40 @@ pub struct HiffyContext<'a> { requests: &'a HubrisVariable, errors: &'a HubrisVariable, failure: &'a HubrisVariable, - scratch_size: usize, - timeout: u32, - state: State, - functions: HiffyFunctions, - rpc_results: Vec, IpcError>>, - rpc_reply_type: Option<&'a HubrisEnum>, +} + +impl<'a> HiffyVars<'a> { + fn new(hubris: &'a HubrisArchive) -> Result { + Ok(Self { + ready: HiffyContext::variable(hubris, "HIFFY_READY", true)?, + kick: HiffyContext::variable(hubris, "HIFFY_KICK", true)?, + text: HiffyContext::variable(hubris, "HIFFY_TEXT", false)?, + data: HiffyContext::variable(hubris, "HIFFY_DATA", false)?, + rstack: HiffyContext::variable(hubris, "HIFFY_RSTACK", false)?, + requests: HiffyContext::variable(hubris, "HIFFY_REQUESTS", true)?, + errors: HiffyContext::variable(hubris, "HIFFY_ERRORS", true)?, + failure: HiffyContext::variable(hubris, "HIFFY_FAILURE", false)?, + }) + } +} + +enum HiffyImpl<'a> { + /// We are physically attached with a debugger + Debugger(HiffyVars<'a>), + /// We are communicating using the `udprpc` task + /// + /// In this mode, we must run the HIF program locally (on the big computer), + /// and perform `send` function calls with network messages. + NetUdpRpc { + results: Vec, IpcError>>, + reply_type: &'a HubrisEnum, + }, + /// We are communicating using the `hiffy` task + NetHiffy { + vars: HiffyVars<'a>, + ops: HiffyNetOps, + errs: HashMap, + }, } #[derive(Clone, Debug)] @@ -205,7 +249,57 @@ impl<'a> HiffyContext<'a> { core: &mut dyn Core, timeout: u32, ) -> Result> { - if !core.is_net() { + let hiffy = if core.is_net() { + if let Some(hiffy_task) = hubris.lookup_task("hiffy") + && hubris.does_task_have_feature(hiffy_task, "net").unwrap() + { + // We will get enum variants by finding the `enum RpcOp` in the + // archive, then getting its variant tags. + let hiffy_task = hubris.lookup_module(hiffy_task)?; + let errs = hiffy_task + .get_enum_variants_by_name(hubris, "RpcReply")? + .into_iter() + .map(|(name, tag)| (tag, name)) + .collect(); + let tags = + hiffy_task.get_enum_variants_by_name(hubris, "RpcOp")?; + let get_tag = |name| -> Result { + tags.get(name) + .ok_or_else(|| anyhow!("no variant with name `{name}`")) + .cloned() + }; + HiffyImpl::NetHiffy { + vars: HiffyVars::new(hubris)?, + ops: HiffyNetOps { + write_text: get_tag("WriteHiffyText")?, + write_data: get_tag("HiffyWriteData")?, + kick: get_tag("HiffyKick")?, + }, + errs, + } + } else { + let reply_type = { + let rpc_task = + hubris.lookup_task("udprpc").ok_or_else(|| { + anyhow!( + "Could not find `udprpc` task in this image. \ + Only -dev and -lab images include `udprpc`; \ + if you are running a production image, it is \ + not available" + ) + })?; + hubris + .lookup_module(rpc_task)? + .lookup_enum_byname(hubris, "RpcReply")? + .ok_or_else(|| anyhow!("failed to find RpcReply"))? + }; + HiffyImpl::NetUdpRpc { results: vec![], reply_type } + } + } else { + HiffyImpl::Debugger(HiffyVars::new(hubris)?) + }; + + if !matches!(hiffy, HiffyImpl::NetUdpRpc { .. }) { core.op_start()?; let (major, minor) = ( @@ -243,29 +337,29 @@ impl<'a> HiffyContext<'a> { } } - let scratch_size = match ( - core.is_net(), - Self::variable(hubris, "HIFFY_SCRATCH", false), - ) { - (false, Ok(scratch)) => { - let mut buf: Vec = vec![]; - buf.resize_with(scratch.size, Default::default); - - core.op_start()?; - core.read_8(scratch.addr, buf.as_mut_slice())?; - core.op_done()?; - - let def = hubris.lookup_struct(scratch.goff)?; - let val: Value = - Value::Struct(reflect::load_struct(hubris, &buf, def, 0)?); - let scratch_cell: StaticCell = StaticCell::from_value(&val)?; - scratch_cell.cell.value.as_array()?.len() - } - _ => { - // Backwards/network compatibility - // Previous versions stored a 256 byte array on the stack - 256 - } + let scratch_size = if matches!(hiffy, HiffyImpl::NetUdpRpc { .. }) { + HIFFY_NET_SCRATCH_SIZE + } else { + // Get the size of the HIFFY_SCRATCH variable, falling back to 256 + // bytes for older images (which used a fixed-size stack array) + Self::variable(hubris, "HIFFY_SCRATCH", false) + .map(|scratch| -> Result { + let mut buf: Vec = vec![]; + buf.resize_with(scratch.size, Default::default); + + core.op_start()?; + core.read_8(scratch.addr, buf.as_mut_slice())?; + core.op_done()?; + + let def = hubris.lookup_struct(scratch.goff)?; + let val: Value = Value::Struct(reflect::load_struct( + hubris, &buf, def, 0, + )?); + let scratch_cell: StaticCell = + StaticCell::from_value(&val)?; + Ok(scratch_cell.cell.value.as_array()?.len()) + }) + .unwrap_or(Ok(256))? }; let mut function_map = HashMap::new(); @@ -344,51 +438,39 @@ impl<'a> HiffyContext<'a> { Ok(Self { hubris, - ready: Self::variable(hubris, "HIFFY_READY", true)?, - kick: Self::variable(hubris, "HIFFY_KICK", true)?, - text: Self::variable(hubris, "HIFFY_TEXT", false)?, - data: Self::variable(hubris, "HIFFY_DATA", false)?, - rstack: Self::variable(hubris, "HIFFY_RSTACK", false)?, - requests: Self::variable(hubris, "HIFFY_REQUESTS", true)?, - errors: Self::variable(hubris, "HIFFY_ERRORS", true)?, - failure: Self::variable(hubris, "HIFFY_FAILURE", false)?, + hiffy, scratch_size, timeout, state: State::Initialized, functions: HiffyFunctions(function_map), - rpc_reply_type: if core.is_net() { - let rpc_task = - hubris.lookup_task("udprpc").ok_or_else(|| { - anyhow!( - "Could not find `udprpc` task in this image. \ - Only -dev and -lab images include `udprpc`; \ - are you running a production image?" - ) - })?; - - Some( - hubris - .lookup_module(rpc_task)? - .lookup_enum_byname(hubris, "RpcReply")? - .ok_or_else(|| anyhow!("failed to find RpcReply"))?, - ) - } else { - None - }, - rpc_results: Vec::new(), }) } pub fn data_size(&self) -> usize { - self.data.size + match &self.hiffy { + HiffyImpl::Debugger(vars) | HiffyImpl::NetHiffy { vars, .. } => { + vars.data.size + } + HiffyImpl::NetUdpRpc { .. } => 0, // not supported + } } pub fn text_size(&self) -> usize { - self.text.size + match &self.hiffy { + HiffyImpl::Debugger(vars) | HiffyImpl::NetHiffy { vars, .. } => { + vars.text.size + } + HiffyImpl::NetUdpRpc { .. } => HIFFY_NET_TEXT_SIZE, + } } - pub fn rdata_size(&self) -> usize { - self.rstack.size + pub fn rstack_size(&self) -> usize { + match &self.hiffy { + HiffyImpl::Debugger(vars) | HiffyImpl::NetHiffy { vars, .. } => { + vars.rstack.size + } + HiffyImpl::NetUdpRpc { .. } => HIFFY_NET_RSTACK_SIZE, + } } /// @@ -396,7 +478,7 @@ impl<'a> HiffyContext<'a> { /// pub fn ops_size(&self, ops: &[Op]) -> Result { let mut text: Vec = vec![]; - text.resize_with(self.text.size, Default::default); + text.resize_with(self.text_size(), Default::default); let mut total = 0; for op in ops { @@ -436,6 +518,10 @@ impl<'a> HiffyContext<'a> { fn perform_rpc(&mut self, core: &mut dyn Core, ops: &[Op]) -> Result<()> { let send = self.get_function("Send", 4).context("could not find Send")?; + let HiffyImpl::NetUdpRpc { results, reply_type } = &mut self.hiffy + else { + bail!("cannot call perform_rpc on this hiffy implementation"); + }; // Bail out immediately if the program makes a call other than Send if ops.iter().any(|op| matches!(*op, Op::Call(id) if id != send.id)) { @@ -445,18 +531,13 @@ impl<'a> HiffyContext<'a> { // Find the socket that we'll be using to communicate let udprpc = self.hubris.manifest.get_socket_by_task("udprpc")?; - // Pick values that are much larger than we'd ever see on a machine - const HIFFY_TEXT_SIZE: usize = 65536; - const HIFFY_RSTACK_SIZE: usize = 65536; - const HIFFY_SCRATCH_SIZE: usize = 65536; - // hard-coded values in task/hiffy/src/main.rs const NLABELS: usize = 4; let mut stack = [None; 32]; - let mut rstack = vec![0u8; HIFFY_RSTACK_SIZE]; - let mut scratch = vec![0u8; HIFFY_SCRATCH_SIZE]; - let mut text = vec![0u8; HIFFY_TEXT_SIZE]; + let mut rstack = vec![0u8; HIFFY_NET_RSTACK_SIZE]; + let mut scratch = vec![0u8; HIFFY_NET_SCRATCH_SIZE]; + let mut text = vec![0u8; HIFFY_NET_TEXT_SIZE]; // Serialize opcodes into `text` let buf = &mut text.as_mut_slice(); @@ -705,7 +786,7 @@ impl<'a> HiffyContext<'a> { if let Err(e) = v { bail!("Hiffy execution error: {e:?}"); } - assert_eq!(self.rpc_results.len(), 0); + assert_eq!(results.len(), 0); HIFFY_SEND_WORKSPACE.with(|workspace| { let workspace = workspace.borrow(); @@ -721,11 +802,10 @@ impl<'a> HiffyContext<'a> { // want to continue processing in this case; toss our error. // if buf[0] != 0 { - let rpc_reply_type = self.rpc_reply_type.unwrap(); // TODO: this assumes that the reply enum can be represented // by a u8 (buf[0] is a u8) and will not work with larger // discriminants, or signed discriminants. - match rpc_reply_type + match reply_type .lookup_variant_by_tag(Tag::from(buf[0])) { Some(e) => { @@ -758,9 +838,9 @@ impl<'a> HiffyContext<'a> { let rval = u32::from_be_bytes(buf[1..5].try_into().unwrap()); if rval == 0 { - self.rpc_results.push(Ok(buf[5..].to_vec())); + results.push(Ok(buf[5..].to_vec())); } else { - self.rpc_results.push(Err(IpcError::from(rval))); + results.push(Err(IpcError::from(rval))); } } // Dummy values for errors and requests, since we'll instantly return @@ -939,28 +1019,43 @@ impl<'a> HiffyContext<'a> { } } - if core.is_net() { - if data.is_some() { - bail!( - "cannot execute HIF operations with local data \ - over the network" + let (vars, writer) = match &self.hiffy { + HiffyImpl::NetUdpRpc { .. } => { + if data.is_some() { + bail!( + "cannot execute HIF operations with local data \ + over the network" + ); + } + return self.perform_rpc(core, ops); + } + HiffyImpl::NetHiffy { vars, ops, errs } => { + let image_id = u64::from_le_bytes( + self.hubris.image_id().unwrap().try_into().unwrap(), ); + let buf_size = self + .hubris + .manifest + .get_socket_by_task("hiffy") + .expect("missing socket for `hiffy` task?") + .rx + .bytes; + (vars, HiffyWrite::Net { image_id, buf_size, ops: *ops, errs }) } - - return self.perform_rpc(core, ops); - } + HiffyImpl::Debugger(vars) => (vars, HiffyWrite::Debugger(vars)), + }; if let Some(data) = data - && data.len() > self.data.size + && data.len() > self.data_size() { bail!( "data size ({}) exceeds maximum data size ({})", data.len(), - self.data.size + self.data_size() ); } - let mut text = vec![0u8; self.text.size]; + let mut text = vec![0u8; self.text_size()]; core.op_start()?; @@ -1022,6 +1117,13 @@ impl<'a> HiffyContext<'a> { expected in normal firmware." ); syscall_observed = true; + } else if matches!(self.hiffy, HiffyImpl::NetHiffy { .. }) { + // When using the hiffy network backend, we usually see the task as + // `ready` (`SchedState::Runnable`), which doesn't count as "the + // task has started" according to the rules of `has_task_started`. + // We'll be optimistic and assume that it's running; if it's not + // running, then it won't reply to packets! + syscall_observed = true; } let mut lap = 0; @@ -1034,7 +1136,7 @@ impl<'a> HiffyContext<'a> { if has_task_started(self.hubris, core, hiffy_task.unwrap())? { syscall_observed = true; } - } else if core.read_word_32(self.ready.addr)? == 1 { + } else if core.read_word_32(vars.ready.addr)? == 1 { ready = true; break; } @@ -1084,16 +1186,16 @@ impl<'a> HiffyContext<'a> { } } - core.write_8(self.text.addr, &buf[0..current])?; + writer.write_8(core, Var::Text, &buf[0..current])?; if let Some(data) = data { - core.write_8(self.data.addr, data)?; + writer.write_8(core, Var::Data, data)?; } - let prev_errors_count = core.read_word_32(self.errors.addr)?; - let prev_requests_count = core.read_word_32(self.requests.addr)?; + let prev_errors_count = core.read_word_32(vars.errors.addr)?; + let prev_requests_count = core.read_word_32(vars.requests.addr)?; - core.write_word_32(self.kick.addr, 1)?; + writer.kick(core)?; self.state = State::Kicked { kick_time: Instant::now(), @@ -1127,19 +1229,22 @@ impl<'a> HiffyContext<'a> { bail!("invalid state for waiting: {:?}", self.state); }; - // - // If this is over the network, our calls are already done by the - // time we're here; immediately transition to `ResultsReady`. - // - if core.is_net() { - self.state = State::ResultsReady; - return Ok(true); - } + let vars = match &self.hiffy { + HiffyImpl::Debugger(vars) | HiffyImpl::NetHiffy { vars, .. } => { + vars + } + HiffyImpl::NetUdpRpc { .. } => { + // If this is over the network, our calls are already done by + // the time we're here; transition to `ResultsReady` now. + self.state = State::ResultsReady; + return Ok(true); + } + }; core.op_start()?; - let new_requests_count = core.read_word_32(self.requests.addr)?; - let new_errors_count = core.read_word_32(self.errors.addr)?; + let new_requests_count = core.read_word_32(vars.requests.addr)?; + let new_errors_count = core.read_word_32(vars.errors.addr)?; core.op_done()?; @@ -1157,10 +1262,10 @@ impl<'a> HiffyContext<'a> { // HIFFY_FAILURE to provide some additional context. // let mut buf: Vec = vec![]; - buf.resize_with(self.failure.size, Default::default); + buf.resize_with(vars.failure.size, Default::default); core.op_start()?; - let r = core.read_8(self.failure.addr, buf.as_mut_slice()); + let r = core.read_8(vars.failure.addr, buf.as_mut_slice()); core.op_done()?; match r { @@ -1171,7 +1276,7 @@ impl<'a> HiffyContext<'a> { }; let hubris = self.hubris; - let f = hubris.printfmt(&buf, self.failure.goff, fmt)?; + let f = hubris.printfmt(&buf, vars.failure.goff, fmt)?; // If Hiffy reports `Invalid`, this could be due to a // patch version mismatch, i.e. Humility trying to use @@ -1225,19 +1330,24 @@ impl<'a> HiffyContext<'a> { bail!("invalid state for consuming results: {:?}", self.state); } - if core.is_net() { - let results = std::mem::take(&mut self.rpc_results); - self.state = State::ResultsConsumed; - return Ok(results); - } + let vars = match &mut self.hiffy { + HiffyImpl::Debugger(vars) | HiffyImpl::NetHiffy { vars, .. } => { + vars + } + HiffyImpl::NetUdpRpc { results, .. } => { + let results = std::mem::take(results); + self.state = State::ResultsConsumed; + return Ok(results); + } + }; let mut rstack: Vec = vec![]; - rstack.resize_with(self.rstack.size, Default::default); + rstack.resize_with(vars.rstack.size, Default::default); core.op_start()?; let mut rvec = vec![]; - core.read_8(self.rstack.addr, rstack.as_mut_slice())?; + core.read_8(vars.rstack.addr, rstack.as_mut_slice())?; core.op_done()?; @@ -1268,10 +1378,6 @@ impl<'a> HiffyContext<'a> { Ok(rvec) } - pub fn rstack_size(&self) -> usize { - self.rstack.size - } - pub fn scratch_size(&self) -> usize { self.scratch_size } @@ -1574,3 +1680,108 @@ fn check_leases( } Ok(()) } + +/// Abstraction for writing hiffy values over multiple transports +enum HiffyWrite<'a, 'b> { + Debugger(&'a HiffyVars<'b>), + Net { + image_id: u64, + buf_size: usize, + ops: HiffyNetOps, + errs: &'a HashMap, + }, +} + +/// Opcodes used by the `hiffy` network backend +#[derive(Copy, Clone)] +struct HiffyNetOps { + write_text: u16, + write_data: u16, + kick: u16, +} + +/// Variable to write with a [`HiffyWriter`] +enum Var { + Text, + Data, +} + +impl<'a, 'b> HiffyWrite<'a, 'b> { + /// Writes a buffer to a particular variable + /// + /// The caller is responsible for making sure that the buffer fits + fn write_8(&self, core: &mut dyn Core, v: Var, data: &[u8]) -> Result<()> { + match self { + HiffyWrite::Debugger(vars) => { + let addr = match v { + Var::Text => vars.text.addr, + Var::Data => vars.data.addr, + }; + core.write_8(addr, data) + } + HiffyWrite::Net { image_id, buf_size, ops, errs } => { + let op = match v { + Var::Text => ops.write_text, + Var::Data => ops.write_data, + }; + // Chosen to be smaller than packet size + let Some(chunk_size) = buf_size + .checked_sub(std::mem::size_of::()) + else { + bail!("buffer size {buf_size} is smaller than `RpcHeader`"); + }; + for (i, chunk) in data.chunks(chunk_size).enumerate() { + let offset = u32::try_from(i * chunk_size).unwrap(); + let header = hiffy::RpcHeader { + image_id: (*image_id).into(), + version: 1.into(), + operation: op.into(), + arg: offset.into(), + }; + let mut packet = header.as_bytes().to_vec(); + packet.extend(chunk); + core.send(packet.as_bytes(), NetAgent::Hiffy) + .context("failed to send write op to hiffy")?; + Self::net_recv(core, errs)?; + } + Ok(()) + } + } + } + fn kick(&self, core: &mut dyn Core) -> Result<()> { + match self { + HiffyWrite::Debugger(vars) => core.write_word_32(vars.kick.addr, 1), + HiffyWrite::Net { image_id, ops, errs, .. } => { + let header = hiffy::RpcHeader { + image_id: (*image_id).into(), + version: 1.into(), + operation: ops.kick.into(), + arg: 0.into(), + }; + core.send(header.as_bytes(), NetAgent::Hiffy) + .context("failed to send OP_KICK to hiffy")?; + Self::net_recv(core, errs)?; + Ok(()) + } + } + } + fn net_recv(core: &mut dyn Core, errs: &HashMap) -> Result<()> { + let mut buf = [0u8; 64]; + let n = core + .recv(&mut buf, NetAgent::Hiffy) + .context("failed to receive reply from hiffy")?; + if n == 0 { + bail!("got empty packet"); + } + match buf[0] { + 0 => Ok(()), + i => { + if let Some(v) = errs.get(&i) { + bail!("received error {v} ({i}): {:x?}", &buf[1..n]) + } else { + bail!("received unknown error {i}: {:x?}", &buf[1..n]) + } + } + } + } +} diff --git a/humility-net-core/src/lib.rs b/humility-net-core/src/lib.rs index c342357e6..e55e37069 100644 --- a/humility-net-core/src/lib.rs +++ b/humility-net-core/src/lib.rs @@ -39,6 +39,9 @@ pub struct NetCore { /// Socket to communicate with the dump agent, or `None` if it's not present dump_agent_socket: Option, + /// Socket to communicate with `hiffy`, or `None` if it's not present + hiffy_socket: Option, + /// Map of RAM regions, or `None` if the dump agent can't read RAM ram: Option>, @@ -77,9 +80,20 @@ impl NetCore { .map(open_socket) .transpose()?; + // We'll look up the `hiffy` task by name, because it doesn't implement + // a particular interface. If someone named it something else, that's + // their problem. + let hiffy_socket = hubris + .manifest + .get_socket_by_task("hiffy") + .ok() + .map(open_socket) + .transpose()?; + let mut out = Self { udprpc_socket, dump_agent_socket, + hiffy_socket, flash: HubrisFlashMap::new(hubris)?, ram: None, // filled in below imageid: hubris @@ -278,6 +292,23 @@ impl NetCore { Ok(()) } + + fn get_socket_for(&self, target: NetAgent) -> Result<&UdpSocket> { + match target { + NetAgent::UdpRpc => self + .udprpc_socket + .as_ref() + .ok_or_else(|| anyhow!("no `udprpc` socket")), + NetAgent::DumpAgent => self + .dump_agent_socket + .as_ref() + .ok_or_else(|| anyhow!("no dump agent socket")), + NetAgent::Hiffy => self + .hiffy_socket + .as_ref() + .ok_or_else(|| anyhow!("no `hiffy` socket")), + } + } } #[rustfmt::skip::macros(bail)] @@ -297,47 +328,18 @@ impl Core for NetCore { if let Some(d) = self.dump_agent_socket.as_ref() { d.set_read_timeout(Some(timeout))?; } + if let Some(d) = self.hiffy_socket.as_ref() { + d.set_read_timeout(Some(timeout))?; + } Ok(()) } fn send(&self, buf: &[u8], target: NetAgent) -> Result { - match target { - NetAgent::UdpRpc => { - if let Some(d) = self.udprpc_socket.as_ref() { - d.send(buf) - } else { - bail!("no `udprpc` socket"); - } - } - NetAgent::DumpAgent => { - if let Some(d) = self.dump_agent_socket.as_ref() { - d.send(buf) - } else { - bail!("no dump agent socket") - } - } - } - .map_err(anyhow::Error::from) + self.get_socket_for(target)?.send(buf).map_err(anyhow::Error::from) } fn recv(&self, buf: &mut [u8], target: NetAgent) -> Result { - match target { - NetAgent::UdpRpc => { - if let Some(d) = self.udprpc_socket.as_ref() { - d.recv(buf) - } else { - bail!("no `udprpc` socket"); - } - } - NetAgent::DumpAgent => { - if let Some(d) = self.dump_agent_socket.as_ref() { - d.recv(buf) - } else { - bail!("no dump agent socket") - } - } - } - .map_err(anyhow::Error::from) + self.get_socket_for(target)?.recv(buf).map_err(anyhow::Error::from) } fn read_8(&mut self, addr: u32, data: &mut [u8]) -> Result<()> {