From ef6dfe1654616327c7290162b75046b7a4c6608e Mon Sep 17 00:00:00 2001 From: Matt Keeter Date: Thu, 26 Mar 2026 16:14:23 -0400 Subject: [PATCH 1/6] hiffy net plumbing --- humility-core/src/core.rs | 1 + humility-core/src/hubris.rs | 33 +++ humility-doppel/src/lib.rs | 16 +- humility-dump-agent/src/hiffy.rs | 2 +- humility-hiffy/src/lib.rs | 447 +++++++++++++++++++++++-------- humility-net-core/src/lib.rs | 70 ++--- 6 files changed, 415 insertions(+), 154 deletions(-) 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-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<()> { From 295fae13a59e73e11e4d64e3cf5877c8002c11cd Mon Sep 17 00:00:00 2001 From: Ben Stoltz Date: Tue, 7 Apr 2026 09:09:53 -0700 Subject: [PATCH 2/6] Add humility-hif-assembler crate for HIF text program assembly Hubris I2C stress testing requires sending looping HIF programs to targets via the hiffy task. Today, each humility subcommand (i2c, pmbus, etc.) manually constructs Op sequences in Rust, making it difficult to script test programs from external tools. This crate introduces a text-based language for HIF programs with syntactic sugar for common operations and an assembler that resolves symbolic names against a Hubris archive. The assembler validates programs against target buffer sizes and embeds the archive's image ID in the output bundle so the runner can reject mismatched uploads. Key pieces: - Text parser with syntactic sugar for i2c_read/write, idol calls, repeat loops, sleep, bus scan, and raw op blocks - Single-pass assembler lowering to hif::Op bytecode - Idol call lowering with scalar argument encoding - TargetConfig: serializable intermediate form capturing buses, devices, muxes, sensors, HIF functions (with typed args and error codes), Idol interfaces, and buffer sizes - Archive loader extracting TargetConfig from DWARF debug info - Result decoder for postcard-encoded HIFFY_RSTACK output - Verify mode reporting program fit and resource estimates - Pre-generated JSON fixtures for gimlet-c and cosmo-b - 9 annotated example programs covering common test patterns - 59 tests (47 unit, 12 fixture-based) --- Cargo.lock | 16 + Cargo.toml | 1 + humility-hif-assembler/Cargo.toml | 17 + humility-hif-assembler/README.md | 224 + humility-hif-assembler/examples/bus-scan.hif | 10 + .../examples/idol-sensor-poll.hif | 16 + .../examples/multi-bus-interleave.hif | 17 + .../examples/register-dump.hif | 10 + .../examples/stress-multi-device.hif | 17 + .../examples/stress-mux-switching.hif | 18 + .../examples/stress-single-device.hif | 16 + .../examples/stress-with-sleep.hif | 17 + .../examples/write-read-verify.hif | 17 + humility-hif-assembler/fixtures/cosmo-b.json | 6635 +++++++++++++++++ humility-hif-assembler/fixtures/gimlet-c.json | 6331 ++++++++++++++++ humility-hif-assembler/src/archive.rs | 481 ++ humility-hif-assembler/src/assembler.rs | 701 ++ humility-hif-assembler/src/bundle.rs | 298 + humility-hif-assembler/src/error.rs | 114 + humility-hif-assembler/src/lib.rs | 199 + humility-hif-assembler/src/listing.rs | 69 + humility-hif-assembler/src/lower.rs | 646 ++ humility-hif-assembler/src/parser.rs | 851 +++ humility-hif-assembler/src/types.rs | 210 + .../tests/archive_integration.rs | 195 + humility-hif-assembler/tests/fixture_tests.rs | 213 + 26 files changed, 17339 insertions(+) create mode 100644 humility-hif-assembler/Cargo.toml create mode 100644 humility-hif-assembler/README.md create mode 100644 humility-hif-assembler/examples/bus-scan.hif create mode 100644 humility-hif-assembler/examples/idol-sensor-poll.hif create mode 100644 humility-hif-assembler/examples/multi-bus-interleave.hif create mode 100644 humility-hif-assembler/examples/register-dump.hif create mode 100644 humility-hif-assembler/examples/stress-multi-device.hif create mode 100644 humility-hif-assembler/examples/stress-mux-switching.hif create mode 100644 humility-hif-assembler/examples/stress-single-device.hif create mode 100644 humility-hif-assembler/examples/stress-with-sleep.hif create mode 100644 humility-hif-assembler/examples/write-read-verify.hif create mode 100644 humility-hif-assembler/fixtures/cosmo-b.json create mode 100644 humility-hif-assembler/fixtures/gimlet-c.json create mode 100644 humility-hif-assembler/src/archive.rs create mode 100644 humility-hif-assembler/src/assembler.rs create mode 100644 humility-hif-assembler/src/bundle.rs create mode 100644 humility-hif-assembler/src/error.rs create mode 100644 humility-hif-assembler/src/lib.rs create mode 100644 humility-hif-assembler/src/listing.rs create mode 100644 humility-hif-assembler/src/lower.rs create mode 100644 humility-hif-assembler/src/parser.rs create mode 100644 humility-hif-assembler/src/types.rs create mode 100644 humility-hif-assembler/tests/archive_integration.rs create mode 100644 humility-hif-assembler/tests/fixture_tests.rs diff --git a/Cargo.lock b/Cargo.lock index a8f02297a..445364eca 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2430,6 +2430,22 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "humility-hif-assembler" +version = "0.1.0" +dependencies = [ + "anyhow", + "hif", + "humility-core", + "humility-hiffy", + "humility-i2c", + "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..28f38d4c5 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", diff --git a/humility-hif-assembler/Cargo.toml b/humility-hif-assembler/Cargo.toml new file mode 100644 index 000000000..3d2422bf6 --- /dev/null +++ b/humility-hif-assembler/Cargo.toml @@ -0,0 +1,17 @@ +[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-hiffy.workspace = true +humility-i2c.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..d8fab1886 --- /dev/null +++ b/humility-hif-assembler/README.md @@ -0,0 +1,224 @@ +# 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. + +The text language provides syntactic sugar for common operations while +raw HIF instructions remain available for anything the sugar doesn't +cover. + +## Quick Start (planned CLI integration) + +The following commands are not yet implemented in `humility hiffy`. +They show the intended workflow once CLI integration lands. + +```bash +# Assemble and verify a program against a Hubris archive +humility -a gimlet-c-dev.zip hiffy --verify stress.hif + +# Assemble to a bundle file +humility -a gimlet-c-dev.zip hiffy --assemble stress.hif -o stress.hifb + +# Run on a target +humility -a gimlet-c-dev.zip -t c71 hiffy --run stress.hifb +``` + +## 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`, `m2`) come from the archive's +`app.toml`. Explicit `.` syntax (e.g. `3.H`) is +also accepted. + +### Idol RPC Calls + +``` +idol Sensor.get id=3 +idol Thermal.get_mode +``` + +(Idol lowering is not yet implemented.) + +### Loops + +``` +repeat 500 + i2c_read mid 0x48 reg=0x00 2 +end + +# With sleep between iterations +repeat 100 sleep=10ms + i2c_read mid 0x48 reg=0x00 2 +end +``` + +### Constants + +``` +.let TEMP_REG 0x00 +.let ITERATIONS 1000 + +repeat $ITERATIONS + i2c_read mid 0x48 reg=$TEMP_REG 2 +end +``` + +### Sleep + +``` +sleep 50ms +``` + +Values over 100ms are automatically split into multiple Sleep calls. + +### Raw Instructions + +For anything the sugar doesn't cover. Constants are expanded inside +raw blocks. + +``` +.let ADDR 0x48 +raw { + push $ADDR + push_none + push 2 + call I2cRead + drop_n 7 + done +} +``` + +Available raw instructions: `push`, `push16`, `push32`, `push_none`, +`drop`, `drop_n`, `swap`, `add`, `label`, `branch_gt`, `branch_gte`, +`branch_lt`, `call`, `done`. + +## 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) +- 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("gimlet-c-dev.zip")?; + +// From a checked-in fixture +let config: TargetConfig = + serde_json::from_str(&std::fs::read_to_string("fixtures/gimlet-c.json")?)?; + +let asm = HifAssembler::new(config); +``` + +Pre-generated fixtures in `fixtures/` allow tests to run without +access to a Hubris archive or build environment. + +## 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=$(cd ~/Oxide/src/hubris/master && \ + cargo -q xtask print --archive app/gimlet/rev-c-dev.toml) \ + 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 +``` + +## Relationship to humility-hiffy and RFD 659 + +This crate overlaps with code in `humility-hiffy`. The overlap is +intentional and designed for eventual convergence. + +### What overlaps + +| Capability | humility-hiffy | hif-assembler | +|---|---|---| +| DWARF function table discovery | `HiffyContext::new()` | `archive.rs extract_hiffy_functions()` | +| Function name/arg resolution | `HiffyFunction` + `get()` | `FunctionInfo` + alias table | +| Result decoding | `HiffyContext::results()` | `HifBundle::decode_results()` | +| I2C parameter resolution | `humility-i2c` `I2cArgs` | `ResolvedBus` + bus name map | +| Idol call construction | `idol_call_ops()` family | Not yet implemented | +| Program construction | Per-command Op building | Text parser + assembler | + +### Why the duplication exists + +`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`. + +### Convergence plan (RFD 659) + +RFD 659 proposes turning humility into a library. When that happens: + +- **`TargetConfig`** replaces the ad-hoc archive introspection + scattered through `HiffyContext::new()`. It becomes the + serializable contract between archive loading and program + construction. + +- **`HifAssembler`** replaces the per-command Op construction in + `cmd/i2c`, `cmd/pmbus`, `cmd/gpio`, etc. Each command becomes a + thin wrapper that parses CLI args into a HIF text program (or uses + `ProgramBuilder`) and hands it to the assembler. + +- **`decode_results()`** becomes the shared result parser, replacing + the inline `take_from_bytes` loop in `HiffyContext::results()`. + +- **`HiffyContext`** narrows to execution only: uploading bytecode + to a target (via probe or NetHiffy), kicking the hiffy task, and + reading back results. It no longer needs to know how programs are + constructed. + +### Design discipline + +To keep convergence clean: + +- This crate uses only public `HubrisArchive` APIs, never + `HiffyContext` internals. +- Types are `Serialize + Deserialize` so they work as file formats + and API contracts. +- The text language is a superset of what humility commands generate + today — any program humility builds internally can be expressed in + the text format. 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/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..f4af21a44 --- /dev/null +++ b/humility-hif-assembler/examples/stress-single-device.hif @@ -0,0 +1,16 @@ +# 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 SENSOR 0x48 +.let TEMP_REG 0x00 +.let ITERATIONS 200 + +repeat $ITERATIONS + i2c_read front $SENSOR 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/cosmo-b.json b/humility-hif-assembler/fixtures/cosmo-b.json new file mode 100644 index 000000000..617c58618 --- /dev/null +++ b/humility-hif-assembler/fixtures/cosmo-b.json @@ -0,0 +1,6635 @@ +{ + "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, + "reply": "u32", + "idempotent": true + }, + { + "name": "set_state", + "code": 2, + "args": [ + { + "name": "state", + "ty": "u32" + } + ], + "reply": "()", + "idempotent": true + }, + { + "name": "request_reset", + "code": 3, + "reply": "()", + "idempotent": true + }, + { + "name": "get_reset_reason", + "code": 4, + "reply": "ResetReason", + "idempotent": true + }, + { + "name": "set_reset_reason", + "code": 5, + "args": [ + { + "name": "reason", + "ty": "ResetReason" + } + ], + "reply": "()", + "idempotent": true + }, + { + "name": "reinitialize_dump_areas", + "code": 6, + "reply": "()", + "error": "DumpAgentError", + "idempotent": true + }, + { + "name": "get_dump_area", + "code": 7, + "args": [ + { + "name": "index", + "ty": "u8" + } + ], + "reply": "humpty::DumpArea", + "error": "DumpAgentError", + "idempotent": true + }, + { + "name": "claim_dump_area", + "code": 8, + "reply": "humpty::DumpArea", + "error": "DumpAgentError", + "idempotent": false + }, + { + "name": "dump_task", + "code": 9, + "args": [ + { + "name": "task_index", + "ty": "u32" + } + ], + "reply": "u8", + "error": "DumpAgentError", + "idempotent": false + }, + { + "name": "dump_task_region", + "code": 10, + "args": [ + { + "name": "task_index", + "ty": "u32" + }, + { + "name": "address", + "ty": "u32" + }, + { + "name": "length", + "ty": "u32" + } + ], + "reply": "u8", + "error": "DumpAgentError", + "idempotent": false + }, + { + "name": "reinitialize_dump_from", + "code": 11, + "args": [ + { + "name": "index", + "ty": "u8" + } + ], + "reply": "()", + "error": "DumpAgentError", + "idempotent": false + }, + { + "name": "restart_me_raw", + "code": 12, + "reply": "()", + "idempotent": true + }, + { + "name": "read_fault_counts", + "code": 13, + "reply": "()", + "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" + } + ], + "reply": "UdpMetadata", + "error": "task_net_api::RecvError", + "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" + } + ], + "reply": "()", + "error": "task_net_api::SendError", + "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" + } + ], + "reply": "u16", + "error": "ServerDeath", + "idempotent": false + }, + { + "name": "smi_write", + "code": 4, + "args": [ + { + "name": "phy", + "ty": "u8" + }, + { + "name": "register", + "ty": "u8" + }, + { + "name": "value", + "ty": "u16" + } + ], + "reply": "()", + "error": "ServerDeath", + "idempotent": false + }, + { + "name": "read_phy_reg", + "code": 5, + "args": [ + { + "name": "port", + "ty": "u8" + }, + { + "name": "page", + "ty": "u16" + }, + { + "name": "reg", + "ty": "u8" + } + ], + "reply": "u16", + "error": "PhyError", + "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" + } + ], + "reply": "()", + "error": "PhyError", + "idempotent": false + }, + { + "name": "read_ksz8463_mac_count", + "code": 7, + "reply": "usize", + "error": "KszError", + "idempotent": false + }, + { + "name": "read_ksz8463_mac", + "code": 8, + "args": [ + { + "name": "i", + "ty": "u16" + } + ], + "reply": "KszMacTableEntry", + "error": "KszError", + "idempotent": false + }, + { + "name": "read_ksz8463_reg", + "code": 9, + "args": [ + { + "name": "reg", + "ty": "u16" + } + ], + "reply": "u16", + "error": "KszError", + "idempotent": false + }, + { + "name": "get_mac_address", + "code": 10, + "reply": "MacAddress", + "idempotent": true + }, + { + "name": "get_spare_mac_addresses", + "code": 11, + "reply": "MacAddressBlock", + "idempotent": true + }, + { + "name": "management_link_status", + "code": 12, + "reply": "ManagementLinkStatus", + "error": "MgmtError", + "idempotent": false + }, + { + "name": "management_counters", + "code": 13, + "reply": "ManagementCounters", + "error": "MgmtError", + "idempotent": false + }, + { + "name": "trust_vlan", + "code": 14, + "args": [ + { + "name": "vid", + "ty": "VLanId" + }, + { + "name": "trust_until", + "ty": "u64" + } + ], + "reply": "()", + "error": "task_net_api::TrustError", + "idempotent": false + }, + { + "name": "distrust_vlan", + "code": 15, + "args": [ + { + "name": "vid", + "ty": "VLanId" + } + ], + "reply": "()", + "error": "task_net_api::TrustError", + "idempotent": false + } + ] + }, + { + "name": "Sys", + "task": "sys", + "task_id": 2, + "ops": [ + { + "name": "enable_clock_raw", + "code": 1, + "args": [ + { + "name": "peripheral", + "ty": "u32" + } + ], + "reply": "()", + "error": "RccError", + "idempotent": true + }, + { + "name": "disable_clock_raw", + "code": 2, + "args": [ + { + "name": "peripheral", + "ty": "u32" + } + ], + "reply": "()", + "error": "RccError", + "idempotent": true + }, + { + "name": "enter_reset_raw", + "code": 3, + "args": [ + { + "name": "peripheral", + "ty": "u32" + } + ], + "reply": "()", + "error": "RccError", + "idempotent": true + }, + { + "name": "leave_reset_raw", + "code": 4, + "args": [ + { + "name": "peripheral", + "ty": "u32" + } + ], + "reply": "()", + "error": "RccError", + "idempotent": true + }, + { + "name": "gpio_configure_raw", + "code": 5, + "args": [ + { + "name": "port", + "ty": "Port" + }, + { + "name": "pins", + "ty": "u16" + }, + { + "name": "packed_attributes", + "ty": "u16" + } + ], + "reply": "()", + "idempotent": true + }, + { + "name": "gpio_set_reset", + "code": 6, + "args": [ + { + "name": "port", + "ty": "Port" + }, + { + "name": "set_pins", + "ty": "u16" + }, + { + "name": "reset_pins", + "ty": "u16" + } + ], + "reply": "()", + "idempotent": true + }, + { + "name": "gpio_read_input", + "code": 7, + "args": [ + { + "name": "port", + "ty": "Port" + } + ], + "reply": "u16", + "idempotent": true + }, + { + "name": "gpio_toggle", + "code": 8, + "args": [ + { + "name": "port", + "ty": "Port" + }, + { + "name": "pins", + "ty": "u16" + } + ], + "reply": "()", + "error": "ServerDeath", + "idempotent": false + }, + { + "name": "read_uid", + "code": 9, + "reply": "[u32; 3]", + "idempotent": true + }, + { + "name": "gpio_irq_configure", + "code": 10, + "args": [ + { + "name": "mask", + "ty": "u32" + }, + { + "name": "sensitivity", + "ty": "Edge" + } + ], + "reply": "()", + "idempotent": true + }, + { + "name": "gpio_irq_control", + "code": 11, + "args": [ + { + "name": "mask", + "ty": "u32" + }, + { + "name": "op", + "ty": "IrqControl" + } + ], + "reply": "bool", + "error": "ServerDeath", + "idempotent": false + } + ] + }, + { + "name": "Spi", + "task": "spi2_driver", + "task_id": 3, + "ops": [ + { + "name": "read", + "code": 1, + "args": [ + { + "name": "device_index", + "ty": "u8" + } + ], + "reply": "()", + "error": "drv_spi_api::SpiError", + "leases": [ + { + "name": "sink", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": false + }, + { + "name": "write", + "code": 2, + "args": [ + { + "name": "device_index", + "ty": "u8" + } + ], + "reply": "()", + "error": "drv_spi_api::SpiError", + "leases": [ + { + "name": "source", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": false + }, + { + "name": "exchange", + "code": 3, + "args": [ + { + "name": "device_index", + "ty": "u8" + } + ], + "reply": "()", + "error": "drv_spi_api::SpiError", + "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" + } + ], + "reply": "()", + "error": "ServerDeath", + "idempotent": false + }, + { + "name": "release", + "code": 5, + "reply": "()", + "error": "ServerDeath", + "idempotent": false + } + ] + }, + { + "name": "Spi", + "task": "spi3_driver", + "task_id": 4, + "ops": [ + { + "name": "read", + "code": 1, + "args": [ + { + "name": "device_index", + "ty": "u8" + } + ], + "reply": "()", + "error": "drv_spi_api::SpiError", + "leases": [ + { + "name": "sink", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": false + }, + { + "name": "write", + "code": 2, + "args": [ + { + "name": "device_index", + "ty": "u8" + } + ], + "reply": "()", + "error": "drv_spi_api::SpiError", + "leases": [ + { + "name": "source", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": false + }, + { + "name": "exchange", + "code": 3, + "args": [ + { + "name": "device_index", + "ty": "u8" + } + ], + "reply": "()", + "error": "drv_spi_api::SpiError", + "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" + } + ], + "reply": "()", + "error": "ServerDeath", + "idempotent": false + }, + { + "name": "release", + "code": 5, + "reply": "()", + "error": "ServerDeath", + "idempotent": false + } + ] + }, + { + "name": "Packrat", + "task": "packrat", + "task_id": 6, + "ops": [ + { + "name": "get_mac_address_block", + "code": 1, + "reply": "MacAddressBlock", + "error": "CacheGetError", + "idempotent": true + }, + { + "name": "set_mac_address_block", + "code": 2, + "args": [ + { + "name": "macs", + "ty": "MacAddressBlock" + } + ], + "reply": "()", + "error": "CacheSetError", + "idempotent": true + }, + { + "name": "get_identity", + "code": 3, + "reply": "OxideIdentity", + "error": "CacheGetError", + "idempotent": true + }, + { + "name": "set_identity", + "code": 4, + "args": [ + { + "name": "macs", + "ty": "OxideIdentity" + } + ], + "reply": "()", + "error": "CacheSetError", + "idempotent": true + }, + { + "name": "get_next_boot_host_startup_options", + "code": 5, + "reply": "HostStartupOptions", + "idempotent": true + }, + { + "name": "set_next_boot_host_startup_options", + "code": 6, + "args": [ + { + "name": "startup_options", + "ty": "HostStartupOptions" + } + ], + "reply": "()", + "idempotent": true + }, + { + "name": "remove_spd", + "code": 7, + "args": [ + { + "name": "index", + "ty": "u8" + } + ], + "reply": "()", + "idempotent": true + }, + { + "name": "set_spd_eeprom", + "code": 8, + "args": [ + { + "name": "index", + "ty": "u8" + }, + { + "name": "offset", + "ty": "usize" + } + ], + "reply": "()", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": true + }, + { + "name": "get_spd_present", + "code": 9, + "args": [ + { + "name": "index", + "ty": "u8" + } + ], + "reply": "bool", + "idempotent": true + }, + { + "name": "get_spd_data", + "code": 10, + "args": [ + { + "name": "index", + "ty": "u8" + }, + { + "name": "offset", + "ty": "usize" + } + ], + "reply": "u8", + "idempotent": true + }, + { + "name": "get_full_spd_data", + "code": 11, + "args": [ + { + "name": "index", + "ty": "u8" + } + ], + "reply": "()", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": true + }, + { + "name": "set_ereport_restart_id", + "code": 12, + "args": [ + { + "name": "restart_id", + "ty": "u128" + } + ], + "reply": "()", + "error": "CacheSetError", + "idempotent": true + }, + { + "name": "deliver_encoded_ereport", + "code": 13, + "reply": "ereport_messages::Ena", + "error": "EreportWriteError", + "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" + } + ], + "reply": "usize", + "error": "EreportReadError", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": true + } + ] + }, + { + "name": "Rng", + "task": "rng_driver", + "task_id": 7, + "ops": [ + { + "name": "fill", + "code": 1, + "reply": "usize", + "error": "RngError", + "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" + } + ], + "reply": "()", + "error": "ThermalError", + "idempotent": false + }, + { + "name": "set_mode_auto", + "code": 2, + "reply": "()", + "error": "ThermalError", + "idempotent": false + }, + { + "name": "get_mode", + "code": 3, + "reply": "ThermalMode", + "error": "ThermalError", + "idempotent": false + }, + { + "name": "get_auto_state", + "code": 4, + "reply": "ThermalAutoState", + "error": "ThermalError", + "idempotent": false + }, + { + "name": "disable_watchdog", + "code": 5, + "reply": "()", + "error": "ThermalError", + "idempotent": false + }, + { + "name": "enable_watchdog", + "code": 6, + "args": [ + { + "name": "timeout_s", + "ty": "u8" + } + ], + "reply": "()", + "error": "ThermalError", + "idempotent": false + }, + { + "name": "set_pid", + "code": 7, + "args": [ + { + "name": "z", + "ty": "f32" + }, + { + "name": "p", + "ty": "f32" + }, + { + "name": "i", + "ty": "f32" + }, + { + "name": "d", + "ty": "f32" + } + ], + "reply": "()", + "error": "ThermalError", + "idempotent": false + }, + { + "name": "get_margin", + "code": 8, + "reply": "f32", + "error": "ThermalError", + "idempotent": false + }, + { + "name": "set_margin", + "code": 9, + "args": [ + { + "name": "margin", + "ty": "f32" + } + ], + "reply": "()", + "error": "ThermalError", + "idempotent": false + }, + { + "name": "update_dynamic_input", + "code": 10, + "args": [ + { + "name": "index", + "ty": "usize" + }, + { + "name": "model", + "ty": "ThermalProperties" + } + ], + "reply": "()", + "error": "ThermalError", + "idempotent": false + }, + { + "name": "remove_dynamic_input", + "code": 11, + "args": [ + { + "name": "index", + "ty": "usize" + } + ], + "reply": "()", + "error": "ThermalError", + "idempotent": false + }, + { + "name": "get_runtime", + "code": 12, + "reply": "u64", + "error": "ThermalError", + "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" + } + ], + "reply": "PmbusValue", + "error": "ResponseCode", + "idempotent": true + }, + { + "name": "read_mode", + "code": 2, + "args": [ + { + "name": "dev", + "ty": "Device" + }, + { + "name": "rail", + "ty": "u8" + }, + { + "name": "index", + "ty": "u32" + } + ], + "reply": "u8", + "error": "ResponseCode", + "idempotent": true + }, + { + "name": "phase_current", + "code": 3, + "args": [ + { + "name": "rail", + "ty": "SensorId" + }, + { + "name": "phase", + "ty": "u8" + } + ], + "reply": "f32", + "error": "ResponseCode", + "idempotent": true + }, + { + "name": "bmr491_event_log_read", + "code": 4, + "args": [ + { + "name": "index", + "ty": "u8" + } + ], + "reply": "Bmr491Event", + "error": "ResponseCode", + "idempotent": true + }, + { + "name": "bmr491_fault_log_clear", + "code": 5, + "reply": "()", + "error": "ResponseCode", + "idempotent": true + }, + { + "name": "bmr491_max_fault_event_index", + "code": 6, + "reply": "u8", + "error": "ResponseCode", + "idempotent": true + }, + { + "name": "bmr491_max_lifecycle_event_index", + "code": 7, + "reply": "u8", + "error": "ResponseCode", + "idempotent": true + }, + { + "name": "rendmp_blackbox_dump", + "code": 8, + "args": [ + { + "name": "addr", + "ty": "u8" + } + ], + "reply": "RenesasBlackbox", + "error": "ResponseCode", + "idempotent": true + }, + { + "name": "rendmp_dma_read", + "code": 9, + "args": [ + { + "name": "addr", + "ty": "u8" + }, + { + "name": "reg", + "ty": "u16" + } + ], + "reply": "u32", + "error": "ResponseCode", + "idempotent": true + }, + { + "name": "rendmp_dma_write", + "code": 10, + "args": [ + { + "name": "addr", + "ty": "u8" + }, + { + "name": "reg", + "ty": "u16" + }, + { + "name": "data", + "ty": "u32" + } + ], + "reply": "()", + "error": "ResponseCode", + "idempotent": true + }, + { + "name": "raw_pmbus_read_byte", + "code": 11, + "args": [ + { + "name": "index", + "ty": "u32" + }, + { + "name": "has_rail", + "ty": "bool" + }, + { + "name": "op", + "ty": "u8" + } + ], + "reply": "u8", + "error": "ResponseCode", + "idempotent": true + }, + { + "name": "raw_pmbus_read_word", + "code": 12, + "args": [ + { + "name": "index", + "ty": "u32" + }, + { + "name": "has_rail", + "ty": "bool" + }, + { + "name": "op", + "ty": "u8" + } + ], + "reply": "u16", + "error": "ResponseCode", + "idempotent": true + }, + { + "name": "raw_pmbus_read_word32", + "code": 13, + "args": [ + { + "name": "index", + "ty": "u32" + }, + { + "name": "has_rail", + "ty": "bool" + }, + { + "name": "op", + "ty": "u8" + } + ], + "reply": "u32", + "error": "ResponseCode", + "idempotent": true + }, + { + "name": "raw_pmbus_read_block", + "code": 14, + "args": [ + { + "name": "index", + "ty": "u32" + }, + { + "name": "has_rail", + "ty": "bool" + }, + { + "name": "op", + "ty": "u8" + } + ], + "reply": "RawPmbusBlock", + "error": "ResponseCode", + "idempotent": true + }, + { + "name": "raw_pmbus_set", + "code": 15, + "args": [ + { + "name": "index", + "ty": "u32" + }, + { + "name": "has_rail", + "ty": "bool" + }, + { + "name": "op", + "ty": "u8" + } + ], + "reply": "()", + "error": "ResponseCode", + "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" + } + ], + "reply": "()", + "error": "ResponseCode", + "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" + } + ], + "reply": "()", + "error": "ResponseCode", + "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" + } + ], + "reply": "()", + "error": "ResponseCode", + "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" + } + ], + "reply": "()", + "error": "ResponseCode", + "idempotent": true + } + ] + }, + { + "name": "Sequencer", + "task": "cosmo_seq", + "task_id": 11, + "ops": [ + { + "name": "get_state", + "code": 1, + "reply": "drv_cpu_power_state::PowerState", + "idempotent": true + }, + { + "name": "set_state", + "code": 2, + "args": [ + { + "name": "state", + "ty": "drv_cpu_power_state::PowerState" + } + ], + "reply": "drv_cpu_seq_api::Transition", + "error": "drv_cpu_seq_api::SeqError", + "idempotent": false + }, + { + "name": "set_state_with_reason", + "code": 3, + "args": [ + { + "name": "state", + "ty": "drv_cpu_power_state::PowerState" + }, + { + "name": "reason", + "ty": "StateChangeReason" + } + ], + "reply": "drv_cpu_seq_api::Transition", + "error": "drv_cpu_seq_api::SeqError", + "idempotent": false + }, + { + "name": "send_hardware_nmi", + "code": 4, + "reply": "()", + "error": "ServerDeath", + "idempotent": false + }, + { + "name": "read_fpga_regs", + "code": 5, + "reply": "[u8; 64]", + "idempotent": true + }, + { + "name": "last_post_code", + "code": 6, + "reply": "u32", + "idempotent": true + }, + { + "name": "post_code_buffer_len", + "code": 7, + "reply": "u32", + "idempotent": true + }, + { + "name": "get_post_code", + "code": 8, + "args": [ + { + "name": "index", + "ty": "u32" + } + ], + "reply": "u32", + "idempotent": true + }, + { + "name": "gpio_edge_count", + "code": 9, + "reply": "u32", + "idempotent": true + }, + { + "name": "gpio_cycle_count", + "code": 10, + "reply": "u32", + "idempotent": true + }, + { + "name": "enable_console_redirect", + "code": 11, + "reply": "()", + "idempotent": true + }, + { + "name": "disable_console_redirect", + "code": 12, + "reply": "()", + "idempotent": true + } + ] + }, + { + "name": "IgnitionFlash", + "task": "ignition_flash", + "task_id": 12, + "ops": [ + { + "name": "read_id", + "code": 1, + "reply": "[u8; 20]", + "error": "IgnitionFlashError", + "idempotent": false + }, + { + "name": "read_status", + "code": 2, + "reply": "u8", + "error": "IgnitionFlashError", + "idempotent": false + }, + { + "name": "bulk_erase", + "code": 3, + "reply": "()", + "error": "IgnitionFlashError", + "idempotent": false + }, + { + "name": "page_program", + "code": 4, + "args": [ + { + "name": "address", + "ty": "u32" + } + ], + "reply": "()", + "error": "IgnitionFlashError", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": false + }, + { + "name": "read", + "code": 5, + "args": [ + { + "name": "address", + "ty": "u32" + } + ], + "reply": "()", + "error": "IgnitionFlashError", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": false + }, + { + "name": "sector_erase", + "code": 6, + "args": [ + { + "name": "address", + "ty": "u32" + } + ], + "reply": "()", + "error": "IgnitionFlashError", + "idempotent": false + }, + { + "name": "select", + "code": 7, + "reply": "()", + "idempotent": false + }, + { + "name": "deselect", + "code": 8, + "reply": "()", + "idempotent": false + } + ] + }, + { + "name": "Spartan7Loader", + "task": "spartan7_loader", + "task_id": 13, + "ops": [ + { + "name": "ping", + "code": 1, + "reply": "()", + "idempotent": true + } + ] + }, + { + "name": "Hash", + "task": "hash_driver", + "task_id": 14, + "ops": [ + { + "name": "init_sha256", + "code": 1, + "reply": "()", + "error": "HashError", + "idempotent": false + }, + { + "name": "update", + "code": 2, + "args": [ + { + "name": "len", + "ty": "u32" + } + ], + "reply": "()", + "error": "HashError", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": false + }, + { + "name": "finalize_sha256", + "code": 3, + "reply": "[u8; crate::SHA256_SZ]", + "error": "HashError", + "idempotent": false + }, + { + "name": "digest_sha256", + "code": 4, + "args": [ + { + "name": "len", + "ty": "u32" + } + ], + "reply": "[u8; crate::SHA256_SZ]", + "error": "HashError", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": false + } + ] + }, + { + "name": "HostFlash", + "task": "hf", + "task_id": 15, + "ops": [ + { + "name": "read_id", + "code": 1, + "reply": "HfChipId", + "error": "HfError", + "idempotent": false + }, + { + "name": "capacity", + "code": 2, + "reply": "usize", + "error": "HfError", + "idempotent": false + }, + { + "name": "read_status", + "code": 3, + "reply": "u8", + "error": "HfError", + "idempotent": false + }, + { + "name": "bulk_erase", + "code": 4, + "args": [ + { + "name": "protect", + "ty": "HfProtectMode" + } + ], + "reply": "()", + "error": "HfError", + "idempotent": false + }, + { + "name": "page_program", + "code": 5, + "args": [ + { + "name": "address", + "ty": "u32" + }, + { + "name": "protect", + "ty": "HfProtectMode" + } + ], + "reply": "()", + "error": "HfError", + "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" + } + ], + "reply": "()", + "error": "HfError", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": false + }, + { + "name": "read", + "code": 7, + "args": [ + { + "name": "address", + "ty": "u32" + } + ], + "reply": "()", + "error": "HfError", + "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" + } + ], + "reply": "()", + "error": "HfError", + "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" + } + ], + "reply": "()", + "error": "HfError", + "idempotent": false + }, + { + "name": "sector_erase_dev", + "code": 10, + "args": [ + { + "name": "dev", + "ty": "HfDevSelect" + }, + { + "name": "address", + "ty": "u32" + }, + { + "name": "protect", + "ty": "HfProtectMode" + } + ], + "reply": "()", + "error": "HfError", + "idempotent": false + }, + { + "name": "get_mux", + "code": 11, + "reply": "HfMuxState", + "error": "HfError", + "idempotent": false + }, + { + "name": "set_mux", + "code": 12, + "args": [ + { + "name": "state", + "ty": "HfMuxState" + } + ], + "reply": "()", + "error": "HfError", + "idempotent": false + }, + { + "name": "get_dev", + "code": 13, + "reply": "HfDevSelect", + "error": "HfError", + "idempotent": false + }, + { + "name": "set_dev", + "code": 14, + "args": [ + { + "name": "dev", + "ty": "HfDevSelect" + } + ], + "reply": "()", + "error": "HfError", + "idempotent": false + }, + { + "name": "check_dev", + "code": 15, + "args": [ + { + "name": "dev", + "ty": "HfDevSelect" + } + ], + "reply": "()", + "error": "HfError", + "idempotent": false + }, + { + "name": "hash", + "code": 16, + "args": [ + { + "name": "address", + "ty": "u32" + }, + { + "name": "len", + "ty": "u32" + } + ], + "reply": "[u8; drv_hash_api::SHA256_SZ]", + "error": "HfError", + "idempotent": false + }, + { + "name": "hash_significant_bits", + "code": 17, + "args": [ + { + "name": "dev", + "ty": "HfDevSelect" + } + ], + "reply": "()", + "error": "HfError", + "idempotent": false + }, + { + "name": "get_cached_hash", + "code": 18, + "args": [ + { + "name": "dev", + "ty": "HfDevSelect" + } + ], + "reply": "[u8; drv_hash_api::SHA256_SZ]", + "error": "HfError", + "idempotent": false + }, + { + "name": "get_persistent_data", + "code": 19, + "reply": "HfPersistentData", + "error": "HfError", + "idempotent": false + }, + { + "name": "write_persistent_data", + "code": 20, + "args": [ + { + "name": "dev_select", + "ty": "HfDevSelect" + } + ], + "reply": "()", + "error": "HfError", + "idempotent": false + }, + { + "name": "apob_begin", + "code": 21, + "args": [ + { + "name": "length", + "ty": "u32" + }, + { + "name": "algorithm", + "ty": "ApobHash" + } + ], + "reply": "()", + "error": "ApobBeginError", + "idempotent": true + }, + { + "name": "apob_write", + "code": 22, + "args": [ + { + "name": "offset", + "ty": "u32" + } + ], + "reply": "()", + "error": "ApobWriteError", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": true + }, + { + "name": "apob_commit", + "code": 23, + "reply": "()", + "error": "ApobCommitError", + "idempotent": true + }, + { + "name": "apob_lock", + "code": 24, + "reply": "()", + "idempotent": true + }, + { + "name": "apob_read", + "code": 25, + "args": [ + { + "name": "offset", + "ty": "u32" + } + ], + "reply": "usize", + "error": "ApobReadError", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": true + }, + { + "name": "apob_clear", + "code": 26, + "reply": "()", + "error": "ApobClearError", + "idempotent": true + } + ] + }, + { + "name": "Update", + "task": "update_server", + "task_id": 16, + "ops": [ + { + "name": "block_size", + "code": 1, + "reply": "usize", + "error": "drv_update_api::UpdateError", + "idempotent": false + }, + { + "name": "prep_image_update", + "code": 2, + "reply": "()", + "error": "drv_update_api::UpdateError", + "idempotent": false + }, + { + "name": "write_one_block", + "code": 3, + "args": [ + { + "name": "block_num", + "ty": "usize" + } + ], + "reply": "()", + "error": "drv_update_api::UpdateError", + "leases": [ + { + "name": "block", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": false + }, + { + "name": "abort_update", + "code": 4, + "reply": "()", + "error": "drv_update_api::UpdateError", + "idempotent": false + }, + { + "name": "finish_image_update", + "code": 5, + "reply": "()", + "error": "drv_update_api::UpdateError", + "idempotent": false + }, + { + "name": "current_version", + "code": 6, + "reply": "ImageVersion", + "idempotent": true + }, + { + "name": "read_caboose_value", + "code": 7, + "args": [ + { + "name": "name", + "ty": "[u8; 4]" + } + ], + "reply": "u32", + "error": "CabooseError", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": true + }, + { + "name": "get_pending_boot_slot", + "code": 8, + "reply": "SlotId", + "idempotent": true + }, + { + "name": "set_pending_boot_slot", + "code": 9, + "args": [ + { + "name": "slot", + "ty": "SlotId" + } + ], + "reply": "()", + "error": "drv_update_api::UpdateError", + "idempotent": false + } + ] + }, + { + "name": "Sensor", + "task": "sensor", + "task_id": 17, + "ops": [ + { + "name": "get", + "code": 1, + "args": [ + { + "name": "id", + "ty": "SensorId" + } + ], + "reply": "f32", + "error": "SensorError", + "idempotent": true + }, + { + "name": "get_reading", + "code": 2, + "args": [ + { + "name": "id", + "ty": "SensorId" + } + ], + "reply": "Reading", + "error": "SensorError", + "idempotent": true + }, + { + "name": "get_raw_reading", + "code": 3, + "args": [ + { + "name": "id", + "ty": "SensorId" + } + ], + "reply": "Option<(Result, u64)>", + "idempotent": true + }, + { + "name": "get_last_data", + "code": 4, + "args": [ + { + "name": "id", + "ty": "SensorId" + } + ], + "reply": "Option<(f32, u64)>", + "idempotent": true + }, + { + "name": "get_last_nodata", + "code": 5, + "args": [ + { + "name": "id", + "ty": "SensorId" + } + ], + "reply": "Option<(NoData, u64)>", + "idempotent": true + }, + { + "name": "get_min", + "code": 6, + "args": [ + { + "name": "id", + "ty": "SensorId" + } + ], + "reply": "(f32, u64)", + "idempotent": true + }, + { + "name": "get_max", + "code": 7, + "args": [ + { + "name": "id", + "ty": "SensorId" + } + ], + "reply": "(f32, u64)", + "idempotent": true + }, + { + "name": "post", + "code": 8, + "args": [ + { + "name": "id", + "ty": "SensorId" + }, + { + "name": "value", + "ty": "f32" + }, + { + "name": "timestamp", + "ty": "u64" + } + ], + "reply": "()", + "idempotent": true + }, + { + "name": "nodata", + "code": 9, + "args": [ + { + "name": "id", + "ty": "SensorId" + }, + { + "name": "nodata", + "ty": "NoData" + }, + { + "name": "timestamp", + "ty": "u64" + } + ], + "reply": "()", + "idempotent": true + }, + { + "name": "get_nerrors", + "code": 10, + "args": [ + { + "name": "id", + "ty": "SensorId" + } + ], + "reply": "u32", + "idempotent": true + } + ] + }, + { + "name": "HostSpComms", + "task": "host_sp_comms", + "task_id": 18, + "ops": [ + { + "name": "set_status", + "code": 1, + "args": [ + { + "name": "status", + "ty": "u64" + } + ], + "reply": "()", + "error": "HostSpCommsError", + "idempotent": false + }, + { + "name": "get_status", + "code": 2, + "reply": "Status", + "error": "HostSpCommsError", + "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" + } + ], + "reply": "()", + "error": "ControlPlaneAgentError", + "idempotent": false + }, + { + "name": "get_host_phase2_data", + "code": 2, + "args": [ + { + "name": "image_hash", + "ty": "[u8; 32]" + }, + { + "name": "offset", + "ty": "u64" + } + ], + "reply": "usize", + "error": "ControlPlaneAgentError", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": false + }, + { + "name": "get_startup_options", + "code": 3, + "reply": "HostStartupOptions", + "error": "ControlPlaneAgentError", + "idempotent": false + }, + { + "name": "set_startup_options", + "code": 4, + "args": [ + { + "name": "startup_options", + "ty": "u64" + } + ], + "reply": "()", + "error": "ControlPlaneAgentError", + "idempotent": false + }, + { + "name": "identity", + "code": 5, + "reply": "OxideIdentity", + "idempotent": true + }, + { + "name": "get_uart_client", + "code": 6, + "reply": "UartClient", + "idempotent": true + }, + { + "name": "get_installinator_image_id", + "code": 7, + "reply": "usize", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": true + }, + { + "name": "set_humility_uart_client", + "code": 8, + "args": [ + { + "name": "attach", + "ty": "bool" + } + ], + "reply": "()", + "error": "ControlPlaneAgentError", + "idempotent": false + }, + { + "name": "uart_read", + "code": 9, + "reply": "usize", + "error": "ControlPlaneAgentError", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": false + }, + { + "name": "uart_write", + "code": 10, + "reply": "usize", + "error": "ControlPlaneAgentError", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": false + } + ] + }, + { + "name": "SpRot", + "task": "sprot", + "task_id": 22, + "ops": [ + { + "name": "status", + "code": 1, + "reply": "SprotStatus", + "error": "SprotError", + "idempotent": true + }, + { + "name": "io_stats", + "code": 2, + "reply": "SprotIoStats", + "error": "SprotError", + "idempotent": true + }, + { + "name": "rot_state", + "code": 3, + "reply": "RotState", + "error": "SprotError", + "idempotent": true + }, + { + "name": "pulse_cs", + "code": 4, + "args": [ + { + "name": "delay", + "ty": "u16" + } + ], + "reply": "PulseStatus", + "error": "SprotError", + "idempotent": false + }, + { + "name": "block_size", + "code": 5, + "reply": "u32", + "error": "SprotError", + "idempotent": true + }, + { + "name": "prep_image_update", + "code": 6, + "args": [ + { + "name": "target", + "ty": "UpdateTarget" + } + ], + "reply": "()", + "error": "SprotError", + "idempotent": true + }, + { + "name": "write_one_block", + "code": 7, + "args": [ + { + "name": "block_num", + "ty": "u32" + } + ], + "reply": "()", + "error": "SprotError", + "leases": [ + { + "name": "block", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": false + }, + { + "name": "abort_update", + "code": 8, + "reply": "()", + "error": "SprotError", + "idempotent": false + }, + { + "name": "finish_image_update", + "code": 9, + "reply": "()", + "error": "SprotError", + "idempotent": false + }, + { + "name": "switch_default_image", + "code": 10, + "args": [ + { + "name": "slot", + "ty": "SlotId" + }, + { + "name": "duration", + "ty": "SwitchDuration" + } + ], + "reply": "()", + "error": "SprotError", + "idempotent": true + }, + { + "name": "reset", + "code": 11, + "reply": "()", + "error": "SprotError", + "idempotent": true + }, + { + "name": "dump", + "code": 12, + "args": [ + { + "name": "address", + "ty": "u32" + } + ], + "reply": "()", + "error": "DumpOrSprotError", + "idempotent": true + }, + { + "name": "caboose_size", + "code": 13, + "args": [ + { + "name": "slot", + "ty": "SlotId" + } + ], + "reply": "u32", + "error": "RawCabooseOrSprotError", + "idempotent": true + }, + { + "name": "read_caboose_region", + "code": 14, + "args": [ + { + "name": "offset", + "ty": "u32" + }, + { + "name": "slot", + "ty": "SlotId" + } + ], + "reply": "()", + "error": "RawCabooseOrSprotError", + "leases": [ + { + "name": "out", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": true + }, + { + "name": "rot_boot_info", + "code": 15, + "reply": "RotBootInfo", + "error": "SprotError", + "idempotent": true + }, + { + "name": "cert_chain_len", + "code": 16, + "reply": "u32", + "error": "AttestOrSprotError", + "idempotent": true + }, + { + "name": "cert_len", + "code": 17, + "args": [ + { + "name": "index", + "ty": "u32" + } + ], + "reply": "u32", + "error": "AttestOrSprotError", + "idempotent": true + }, + { + "name": "cert", + "code": 18, + "args": [ + { + "name": "index", + "ty": "u32" + }, + { + "name": "offset", + "ty": "u32" + } + ], + "reply": "()", + "error": "AttestOrSprotError", + "leases": [ + { + "name": "dest", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": true + }, + { + "name": "record", + "code": 19, + "args": [ + { + "name": "algorithm", + "ty": "HashAlgorithm" + } + ], + "reply": "()", + "error": "AttestOrSprotError", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": false + }, + { + "name": "read_rot_page", + "code": 20, + "args": [ + { + "name": "page", + "ty": "RotPage" + } + ], + "reply": "()", + "error": "SprotError", + "leases": [ + { + "name": "dest", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": true + }, + { + "name": "log", + "code": 21, + "args": [ + { + "name": "offset", + "ty": "u32" + } + ], + "reply": "()", + "error": "AttestOrSprotError", + "leases": [ + { + "name": "dest", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": false + }, + { + "name": "log_len", + "code": 22, + "reply": "u32", + "error": "AttestOrSprotError", + "idempotent": true + }, + { + "name": "attest", + "code": 23, + "reply": "()", + "error": "AttestOrSprotError", + "leases": [ + { + "name": "nonce", + "ty": "[u8]", + "read": true, + "write": false + }, + { + "name": "dest", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": false + }, + { + "name": "attest_len", + "code": 24, + "reply": "u32", + "error": "AttestOrSprotError", + "idempotent": true + }, + { + "name": "enable_sp_slot_watchdog", + "code": 25, + "args": [ + { + "name": "time_ms", + "ty": "u32" + } + ], + "reply": "()", + "error": "SprotError", + "idempotent": false + }, + { + "name": "disable_sp_slot_watchdog", + "code": 26, + "reply": "()", + "error": "SprotError", + "idempotent": false + }, + { + "name": "sp_slot_watchdog_supported", + "code": 27, + "reply": "()", + "error": "SprotError", + "idempotent": false + }, + { + "name": "versioned_rot_boot_info", + "code": 28, + "args": [ + { + "name": "version", + "ty": "u8" + } + ], + "reply": "VersionedRotBootInfo", + "error": "SprotError", + "idempotent": true + }, + { + "name": "component_caboose_size", + "code": 29, + "args": [ + { + "name": "component", + "ty": "RotComponent" + }, + { + "name": "slot", + "ty": "SlotId" + } + ], + "reply": "u32", + "error": "RawCabooseOrSprotError", + "idempotent": true + }, + { + "name": "component_read_caboose_region", + "code": 30, + "args": [ + { + "name": "offset", + "ty": "u32" + }, + { + "name": "component", + "ty": "RotComponent" + }, + { + "name": "slot", + "ty": "SlotId" + } + ], + "reply": "()", + "error": "RawCabooseOrSprotError", + "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" + } + ], + "reply": "()", + "error": "SprotError", + "idempotent": true + }, + { + "name": "component_switch_default_image", + "code": 32, + "args": [ + { + "name": "component", + "ty": "RotComponent" + }, + { + "name": "slot", + "ty": "SlotId" + }, + { + "name": "duration", + "ty": "SwitchDuration" + } + ], + "reply": "()", + "error": "SprotError", + "idempotent": true + }, + { + "name": "lifecycle_state", + "code": 33, + "reply": "LifecycleState", + "error": "StateOrSprotError", + "idempotent": true + }, + { + "name": "tq_cert_chain_len", + "code": 34, + "reply": "u32", + "error": "AttestOrSprotError", + "idempotent": true + }, + { + "name": "tq_cert_len", + "code": 35, + "args": [ + { + "name": "index", + "ty": "u32" + } + ], + "reply": "u32", + "error": "AttestOrSprotError", + "idempotent": true + }, + { + "name": "tq_cert", + "code": 36, + "args": [ + { + "name": "index", + "ty": "u32" + }, + { + "name": "offset", + "ty": "u32" + } + ], + "reply": "()", + "error": "AttestOrSprotError", + "leases": [ + { + "name": "dest", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": true + }, + { + "name": "tq_sign", + "code": 37, + "reply": "()", + "error": "AttestOrSprotError", + "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, + "reply": "u32", + "error": "AttestOrSprotError", + "idempotent": true + } + ] + }, + { + "name": "Validate", + "task": "validate", + "task_id": 23, + "ops": [ + { + "name": "validate_i2c", + "code": 1, + "args": [ + { + "name": "index", + "ty": "u32" + } + ], + "reply": "ValidateOk", + "error": "ValidateError", + "idempotent": true + } + ] + }, + { + "name": "Vpd", + "task": "vpd", + "task_id": 24, + "ops": [ + { + "name": "read_tmp117_eeprom", + "code": 1, + "args": [ + { + "name": "index", + "ty": "u8" + } + ], + "reply": "[u8; 6]", + "error": "VpdError", + "idempotent": false + }, + { + "name": "read", + "code": 2, + "args": [ + { + "name": "index", + "ty": "u8" + }, + { + "name": "offset", + "ty": "u16" + } + ], + "reply": "[u8; 16]", + "error": "VpdError", + "idempotent": false + }, + { + "name": "write", + "code": 3, + "args": [ + { + "name": "index", + "ty": "u8" + }, + { + "name": "offset", + "ty": "u16" + }, + { + "name": "contents", + "ty": "u8" + } + ], + "reply": "()", + "error": "VpdError", + "idempotent": false + }, + { + "name": "is_locked", + "code": 4, + "args": [ + { + "name": "index", + "ty": "u8" + } + ], + "reply": "bool", + "error": "VpdError", + "idempotent": false + }, + { + "name": "permanently_lock", + "code": 5, + "args": [ + { + "name": "index", + "ty": "u8" + } + ], + "reply": "()", + "error": "VpdError", + "idempotent": false + }, + { + "name": "num_vpd_devices", + "code": 6, + "reply": "usize", + "idempotent": true + } + ] + }, + { + "name": "UserLeds", + "task": "user_leds", + "task_id": 25, + "ops": [ + { + "name": "led_on", + "code": 1, + "args": [ + { + "name": "index", + "ty": "usize" + } + ], + "reply": "()", + "error": "LedError", + "idempotent": true + }, + { + "name": "led_off", + "code": 2, + "args": [ + { + "name": "index", + "ty": "usize" + } + ], + "reply": "()", + "error": "LedError", + "idempotent": true + }, + { + "name": "led_toggle", + "code": 3, + "args": [ + { + "name": "index", + "ty": "usize" + } + ], + "reply": "()", + "error": "LedError", + "idempotent": true + }, + { + "name": "led_blink", + "code": 4, + "args": [ + { + "name": "index", + "ty": "usize" + } + ], + "reply": "()", + "error": "LedError", + "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" + } + ], + "reply": "[u8; DUMP_READ_SIZE]", + "error": "DumpAgentError", + "idempotent": false + }, + { + "name": "read_dump_into", + "code": 2, + "args": [ + { + "name": "index", + "ty": "u8" + }, + { + "name": "offset", + "ty": "u32" + } + ], + "reply": "usize", + "error": "DumpAgentError", + "leases": [ + { + "name": "out", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": false + }, + { + "name": "get_dump_area", + "code": 3, + "args": [ + { + "name": "index", + "ty": "u8" + } + ], + "reply": "DumpArea", + "error": "DumpAgentError", + "idempotent": false + }, + { + "name": "initialize_dump", + "code": 4, + "reply": "()", + "error": "DumpAgentError", + "idempotent": false + }, + { + "name": "add_dump_segment", + "code": 5, + "args": [ + { + "name": "address", + "ty": "u32" + }, + { + "name": "length", + "ty": "u32" + } + ], + "reply": "()", + "error": "DumpAgentError", + "idempotent": false + }, + { + "name": "take_dump", + "code": 6, + "reply": "()", + "error": "DumpAgentError", + "idempotent": false + }, + { + "name": "dump_task", + "code": 7, + "args": [ + { + "name": "task_index", + "ty": "u32" + } + ], + "reply": "u8", + "error": "DumpAgentError", + "idempotent": false + }, + { + "name": "dump_task_region", + "code": 8, + "args": [ + { + "name": "task_index", + "ty": "u32" + }, + { + "name": "start", + "ty": "u32" + }, + { + "name": "length", + "ty": "u32" + } + ], + "reply": "u8", + "error": "DumpAgentError", + "idempotent": false + }, + { + "name": "reinitialize_dump_from", + "code": 9, + "args": [ + { + "name": "index", + "ty": "u8" + } + ], + "reply": "()", + "error": "DumpAgentError", + "idempotent": false + } + ] + }, + { + "name": "CosmoSpd", + "task": "spd", + "task_id": 28, + "ops": [ + { + "name": "ping", + "code": 1, + "reply": "()", + "idempotent": true + } + ] + }, + { + "name": "AuxFlash", + "task": "auxflash", + "task_id": 29, + "ops": [ + { + "name": "read_id", + "code": 1, + "reply": "AuxFlashId", + "error": "AuxFlashError", + "idempotent": false + }, + { + "name": "slot_count", + "code": 2, + "reply": "u32", + "error": "AuxFlashError", + "idempotent": false + }, + { + "name": "slot_size", + "code": 3, + "reply": "u32", + "error": "AuxFlashError", + "idempotent": false + }, + { + "name": "read_status", + "code": 4, + "reply": "u8", + "error": "AuxFlashError", + "idempotent": false + }, + { + "name": "erase_slot", + "code": 5, + "args": [ + { + "name": "slot", + "ty": "u32" + } + ], + "reply": "()", + "error": "AuxFlashError", + "idempotent": false + }, + { + "name": "slot_sector_erase", + "code": 6, + "args": [ + { + "name": "slot", + "ty": "u32" + }, + { + "name": "offset", + "ty": "u32" + } + ], + "reply": "()", + "error": "AuxFlashError", + "idempotent": false + }, + { + "name": "read_slot_chck", + "code": 7, + "args": [ + { + "name": "slot", + "ty": "u32" + } + ], + "reply": "AuxFlashChecksum", + "error": "AuxFlashError", + "idempotent": false + }, + { + "name": "write_slot_with_offset", + "code": 8, + "args": [ + { + "name": "slot", + "ty": "u32" + }, + { + "name": "offset", + "ty": "u32" + } + ], + "reply": "()", + "error": "AuxFlashError", + "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" + } + ], + "reply": "()", + "error": "AuxFlashError", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": false + }, + { + "name": "scan_and_get_active_slot", + "code": 10, + "reply": "u32", + "error": "AuxFlashError", + "idempotent": false + }, + { + "name": "get_active_slot", + "code": 11, + "reply": "u32", + "error": "AuxFlashError", + "idempotent": false + }, + { + "name": "ensure_redundancy", + "code": 12, + "reply": "()", + "error": "AuxFlashError", + "idempotent": false + }, + { + "name": "get_blob_by_tag", + "code": 13, + "args": [ + { + "name": "name", + "ty": "[u8; 4]" + } + ], + "reply": "AuxFlashBlob", + "error": "AuxFlashError", + "idempotent": false + } + ] + }, + { + "name": "FmcDemo", + "task": "fmc_demo", + "task_id": 32, + "ops": [ + { + "name": "peek16", + "code": 1, + "args": [ + { + "name": "addr", + "ty": "u32" + } + ], + "reply": "u16", + "idempotent": false + }, + { + "name": "peek32", + "code": 2, + "args": [ + { + "name": "addr", + "ty": "u32" + } + ], + "reply": "u32", + "idempotent": false + }, + { + "name": "peek64", + "code": 3, + "args": [ + { + "name": "addr", + "ty": "u32" + } + ], + "reply": "u64", + "idempotent": false + }, + { + "name": "poke16", + "code": 4, + "args": [ + { + "name": "addr", + "ty": "u32" + }, + { + "name": "value", + "ty": "u16" + } + ], + "reply": "()", + "idempotent": false + }, + { + "name": "poke32", + "code": 5, + "args": [ + { + "name": "addr", + "ty": "u32" + }, + { + "name": "value", + "ty": "u32" + } + ], + "reply": "()", + "idempotent": false + }, + { + "name": "poke64", + "code": 6, + "args": [ + { + "name": "addr", + "ty": "u32" + }, + { + "name": "value", + "ty": "u64" + } + ], + "reply": "()", + "idempotent": false + }, + { + "name": "set_burst_enable", + "code": 7, + "args": [ + { + "name": "flag", + "ty": "bool" + } + ], + "reply": "()", + "idempotent": false + }, + { + "name": "set_write_enable", + "code": 8, + "args": [ + { + "name": "flag", + "ty": "bool" + } + ], + "reply": "()", + "idempotent": false + }, + { + "name": "set_write_fifo", + "code": 9, + "args": [ + { + "name": "flag", + "ty": "bool" + } + ], + "reply": "()", + "idempotent": false + }, + { + "name": "set_wait", + "code": 10, + "args": [ + { + "name": "flag", + "ty": "bool" + } + ], + "reply": "()", + "idempotent": false + }, + { + "name": "set_data_latency_cycles", + "code": 11, + "args": [ + { + "name": "n", + "ty": "u8" + } + ], + "reply": "()", + "idempotent": false + }, + { + "name": "set_clock_divider", + "code": 12, + "args": [ + { + "name": "n", + "ty": "u8" + } + ], + "reply": "()", + "idempotent": false + }, + { + "name": "set_bus_turnaround_cycles", + "code": 13, + "args": [ + { + "name": "n", + "ty": "u8" + } + ], + "reply": "()", + "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..e9939d2db --- /dev/null +++ b/humility-hif-assembler/fixtures/gimlet-c.json @@ -0,0 +1,6331 @@ +{ + "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, + "reply": "u32", + "idempotent": true + }, + { + "name": "set_state", + "code": 2, + "args": [ + { + "name": "state", + "ty": "u32" + } + ], + "reply": "()", + "idempotent": true + }, + { + "name": "request_reset", + "code": 3, + "reply": "()", + "idempotent": true + }, + { + "name": "get_reset_reason", + "code": 4, + "reply": "ResetReason", + "idempotent": true + }, + { + "name": "set_reset_reason", + "code": 5, + "args": [ + { + "name": "reason", + "ty": "ResetReason" + } + ], + "reply": "()", + "idempotent": true + }, + { + "name": "reinitialize_dump_areas", + "code": 6, + "reply": "()", + "error": "DumpAgentError", + "idempotent": true + }, + { + "name": "get_dump_area", + "code": 7, + "args": [ + { + "name": "index", + "ty": "u8" + } + ], + "reply": "humpty::DumpArea", + "error": "DumpAgentError", + "idempotent": true + }, + { + "name": "claim_dump_area", + "code": 8, + "reply": "humpty::DumpArea", + "error": "DumpAgentError", + "idempotent": false + }, + { + "name": "dump_task", + "code": 9, + "args": [ + { + "name": "task_index", + "ty": "u32" + } + ], + "reply": "u8", + "error": "DumpAgentError", + "idempotent": false + }, + { + "name": "dump_task_region", + "code": 10, + "args": [ + { + "name": "task_index", + "ty": "u32" + }, + { + "name": "address", + "ty": "u32" + }, + { + "name": "length", + "ty": "u32" + } + ], + "reply": "u8", + "error": "DumpAgentError", + "idempotent": false + }, + { + "name": "reinitialize_dump_from", + "code": 11, + "args": [ + { + "name": "index", + "ty": "u8" + } + ], + "reply": "()", + "error": "DumpAgentError", + "idempotent": false + }, + { + "name": "restart_me_raw", + "code": 12, + "reply": "()", + "idempotent": true + }, + { + "name": "read_fault_counts", + "code": 13, + "reply": "()", + "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" + } + ], + "reply": "UdpMetadata", + "error": "task_net_api::RecvError", + "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" + } + ], + "reply": "()", + "error": "task_net_api::SendError", + "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" + } + ], + "reply": "u16", + "error": "ServerDeath", + "idempotent": false + }, + { + "name": "smi_write", + "code": 4, + "args": [ + { + "name": "phy", + "ty": "u8" + }, + { + "name": "register", + "ty": "u8" + }, + { + "name": "value", + "ty": "u16" + } + ], + "reply": "()", + "error": "ServerDeath", + "idempotent": false + }, + { + "name": "read_phy_reg", + "code": 5, + "args": [ + { + "name": "port", + "ty": "u8" + }, + { + "name": "page", + "ty": "u16" + }, + { + "name": "reg", + "ty": "u8" + } + ], + "reply": "u16", + "error": "PhyError", + "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" + } + ], + "reply": "()", + "error": "PhyError", + "idempotent": false + }, + { + "name": "read_ksz8463_mac_count", + "code": 7, + "reply": "usize", + "error": "KszError", + "idempotent": false + }, + { + "name": "read_ksz8463_mac", + "code": 8, + "args": [ + { + "name": "i", + "ty": "u16" + } + ], + "reply": "KszMacTableEntry", + "error": "KszError", + "idempotent": false + }, + { + "name": "read_ksz8463_reg", + "code": 9, + "args": [ + { + "name": "reg", + "ty": "u16" + } + ], + "reply": "u16", + "error": "KszError", + "idempotent": false + }, + { + "name": "get_mac_address", + "code": 10, + "reply": "MacAddress", + "idempotent": true + }, + { + "name": "get_spare_mac_addresses", + "code": 11, + "reply": "MacAddressBlock", + "idempotent": true + }, + { + "name": "management_link_status", + "code": 12, + "reply": "ManagementLinkStatus", + "error": "MgmtError", + "idempotent": false + }, + { + "name": "management_counters", + "code": 13, + "reply": "ManagementCounters", + "error": "MgmtError", + "idempotent": false + }, + { + "name": "trust_vlan", + "code": 14, + "args": [ + { + "name": "vid", + "ty": "VLanId" + }, + { + "name": "trust_until", + "ty": "u64" + } + ], + "reply": "()", + "error": "task_net_api::TrustError", + "idempotent": false + }, + { + "name": "distrust_vlan", + "code": 15, + "args": [ + { + "name": "vid", + "ty": "VLanId" + } + ], + "reply": "()", + "error": "task_net_api::TrustError", + "idempotent": false + } + ] + }, + { + "name": "Sys", + "task": "sys", + "task_id": 2, + "ops": [ + { + "name": "enable_clock_raw", + "code": 1, + "args": [ + { + "name": "peripheral", + "ty": "u32" + } + ], + "reply": "()", + "error": "RccError", + "idempotent": true + }, + { + "name": "disable_clock_raw", + "code": 2, + "args": [ + { + "name": "peripheral", + "ty": "u32" + } + ], + "reply": "()", + "error": "RccError", + "idempotent": true + }, + { + "name": "enter_reset_raw", + "code": 3, + "args": [ + { + "name": "peripheral", + "ty": "u32" + } + ], + "reply": "()", + "error": "RccError", + "idempotent": true + }, + { + "name": "leave_reset_raw", + "code": 4, + "args": [ + { + "name": "peripheral", + "ty": "u32" + } + ], + "reply": "()", + "error": "RccError", + "idempotent": true + }, + { + "name": "gpio_configure_raw", + "code": 5, + "args": [ + { + "name": "port", + "ty": "Port" + }, + { + "name": "pins", + "ty": "u16" + }, + { + "name": "packed_attributes", + "ty": "u16" + } + ], + "reply": "()", + "idempotent": true + }, + { + "name": "gpio_set_reset", + "code": 6, + "args": [ + { + "name": "port", + "ty": "Port" + }, + { + "name": "set_pins", + "ty": "u16" + }, + { + "name": "reset_pins", + "ty": "u16" + } + ], + "reply": "()", + "idempotent": true + }, + { + "name": "gpio_read_input", + "code": 7, + "args": [ + { + "name": "port", + "ty": "Port" + } + ], + "reply": "u16", + "idempotent": true + }, + { + "name": "gpio_toggle", + "code": 8, + "args": [ + { + "name": "port", + "ty": "Port" + }, + { + "name": "pins", + "ty": "u16" + } + ], + "reply": "()", + "error": "ServerDeath", + "idempotent": false + }, + { + "name": "read_uid", + "code": 9, + "reply": "[u32; 3]", + "idempotent": true + }, + { + "name": "gpio_irq_configure", + "code": 10, + "args": [ + { + "name": "mask", + "ty": "u32" + }, + { + "name": "sensitivity", + "ty": "Edge" + } + ], + "reply": "()", + "idempotent": true + }, + { + "name": "gpio_irq_control", + "code": 11, + "args": [ + { + "name": "mask", + "ty": "u32" + }, + { + "name": "op", + "ty": "IrqControl" + } + ], + "reply": "bool", + "error": "ServerDeath", + "idempotent": false + } + ] + }, + { + "name": "Spi", + "task": "spi2_driver", + "task_id": 3, + "ops": [ + { + "name": "read", + "code": 1, + "args": [ + { + "name": "device_index", + "ty": "u8" + } + ], + "reply": "()", + "error": "drv_spi_api::SpiError", + "leases": [ + { + "name": "sink", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": false + }, + { + "name": "write", + "code": 2, + "args": [ + { + "name": "device_index", + "ty": "u8" + } + ], + "reply": "()", + "error": "drv_spi_api::SpiError", + "leases": [ + { + "name": "source", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": false + }, + { + "name": "exchange", + "code": 3, + "args": [ + { + "name": "device_index", + "ty": "u8" + } + ], + "reply": "()", + "error": "drv_spi_api::SpiError", + "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" + } + ], + "reply": "()", + "error": "ServerDeath", + "idempotent": false + }, + { + "name": "release", + "code": 5, + "reply": "()", + "error": "ServerDeath", + "idempotent": false + } + ] + }, + { + "name": "Packrat", + "task": "packrat", + "task_id": 6, + "ops": [ + { + "name": "get_mac_address_block", + "code": 1, + "reply": "MacAddressBlock", + "error": "CacheGetError", + "idempotent": true + }, + { + "name": "set_mac_address_block", + "code": 2, + "args": [ + { + "name": "macs", + "ty": "MacAddressBlock" + } + ], + "reply": "()", + "error": "CacheSetError", + "idempotent": true + }, + { + "name": "get_identity", + "code": 3, + "reply": "OxideIdentity", + "error": "CacheGetError", + "idempotent": true + }, + { + "name": "set_identity", + "code": 4, + "args": [ + { + "name": "macs", + "ty": "OxideIdentity" + } + ], + "reply": "()", + "error": "CacheSetError", + "idempotent": true + }, + { + "name": "get_next_boot_host_startup_options", + "code": 5, + "reply": "HostStartupOptions", + "idempotent": true + }, + { + "name": "set_next_boot_host_startup_options", + "code": 6, + "args": [ + { + "name": "startup_options", + "ty": "HostStartupOptions" + } + ], + "reply": "()", + "idempotent": true + }, + { + "name": "remove_spd", + "code": 7, + "args": [ + { + "name": "index", + "ty": "u8" + } + ], + "reply": "()", + "idempotent": true + }, + { + "name": "set_spd_eeprom", + "code": 8, + "args": [ + { + "name": "index", + "ty": "u8" + }, + { + "name": "offset", + "ty": "usize" + } + ], + "reply": "()", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": true + }, + { + "name": "get_spd_present", + "code": 9, + "args": [ + { + "name": "index", + "ty": "u8" + } + ], + "reply": "bool", + "idempotent": true + }, + { + "name": "get_spd_data", + "code": 10, + "args": [ + { + "name": "index", + "ty": "u8" + }, + { + "name": "offset", + "ty": "usize" + } + ], + "reply": "u8", + "idempotent": true + }, + { + "name": "get_full_spd_data", + "code": 11, + "args": [ + { + "name": "index", + "ty": "u8" + } + ], + "reply": "()", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": true + }, + { + "name": "set_ereport_restart_id", + "code": 12, + "args": [ + { + "name": "restart_id", + "ty": "u128" + } + ], + "reply": "()", + "error": "CacheSetError", + "idempotent": true + }, + { + "name": "deliver_encoded_ereport", + "code": 13, + "reply": "ereport_messages::Ena", + "error": "EreportWriteError", + "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" + } + ], + "reply": "usize", + "error": "EreportReadError", + "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" + } + ], + "reply": "()", + "error": "ThermalError", + "idempotent": false + }, + { + "name": "set_mode_auto", + "code": 2, + "reply": "()", + "error": "ThermalError", + "idempotent": false + }, + { + "name": "get_mode", + "code": 3, + "reply": "ThermalMode", + "error": "ThermalError", + "idempotent": false + }, + { + "name": "get_auto_state", + "code": 4, + "reply": "ThermalAutoState", + "error": "ThermalError", + "idempotent": false + }, + { + "name": "disable_watchdog", + "code": 5, + "reply": "()", + "error": "ThermalError", + "idempotent": false + }, + { + "name": "enable_watchdog", + "code": 6, + "args": [ + { + "name": "timeout_s", + "ty": "u8" + } + ], + "reply": "()", + "error": "ThermalError", + "idempotent": false + }, + { + "name": "set_pid", + "code": 7, + "args": [ + { + "name": "z", + "ty": "f32" + }, + { + "name": "p", + "ty": "f32" + }, + { + "name": "i", + "ty": "f32" + }, + { + "name": "d", + "ty": "f32" + } + ], + "reply": "()", + "error": "ThermalError", + "idempotent": false + }, + { + "name": "get_margin", + "code": 8, + "reply": "f32", + "error": "ThermalError", + "idempotent": false + }, + { + "name": "set_margin", + "code": 9, + "args": [ + { + "name": "margin", + "ty": "f32" + } + ], + "reply": "()", + "error": "ThermalError", + "idempotent": false + }, + { + "name": "update_dynamic_input", + "code": 10, + "args": [ + { + "name": "index", + "ty": "usize" + }, + { + "name": "model", + "ty": "ThermalProperties" + } + ], + "reply": "()", + "error": "ThermalError", + "idempotent": false + }, + { + "name": "remove_dynamic_input", + "code": 11, + "args": [ + { + "name": "index", + "ty": "usize" + } + ], + "reply": "()", + "error": "ThermalError", + "idempotent": false + }, + { + "name": "get_runtime", + "code": 12, + "reply": "u64", + "error": "ThermalError", + "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" + } + ], + "reply": "PmbusValue", + "error": "ResponseCode", + "idempotent": true + }, + { + "name": "read_mode", + "code": 2, + "args": [ + { + "name": "dev", + "ty": "Device" + }, + { + "name": "rail", + "ty": "u8" + }, + { + "name": "index", + "ty": "u32" + } + ], + "reply": "u8", + "error": "ResponseCode", + "idempotent": true + }, + { + "name": "phase_current", + "code": 3, + "args": [ + { + "name": "rail", + "ty": "SensorId" + }, + { + "name": "phase", + "ty": "u8" + } + ], + "reply": "f32", + "error": "ResponseCode", + "idempotent": true + }, + { + "name": "bmr491_event_log_read", + "code": 4, + "args": [ + { + "name": "index", + "ty": "u8" + } + ], + "reply": "Bmr491Event", + "error": "ResponseCode", + "idempotent": true + }, + { + "name": "bmr491_fault_log_clear", + "code": 5, + "reply": "()", + "error": "ResponseCode", + "idempotent": true + }, + { + "name": "bmr491_max_fault_event_index", + "code": 6, + "reply": "u8", + "error": "ResponseCode", + "idempotent": true + }, + { + "name": "bmr491_max_lifecycle_event_index", + "code": 7, + "reply": "u8", + "error": "ResponseCode", + "idempotent": true + }, + { + "name": "rendmp_blackbox_dump", + "code": 8, + "args": [ + { + "name": "addr", + "ty": "u8" + } + ], + "reply": "RenesasBlackbox", + "error": "ResponseCode", + "idempotent": true + }, + { + "name": "rendmp_dma_read", + "code": 9, + "args": [ + { + "name": "addr", + "ty": "u8" + }, + { + "name": "reg", + "ty": "u16" + } + ], + "reply": "u32", + "error": "ResponseCode", + "idempotent": true + }, + { + "name": "rendmp_dma_write", + "code": 10, + "args": [ + { + "name": "addr", + "ty": "u8" + }, + { + "name": "reg", + "ty": "u16" + }, + { + "name": "data", + "ty": "u32" + } + ], + "reply": "()", + "error": "ResponseCode", + "idempotent": true + }, + { + "name": "raw_pmbus_read_byte", + "code": 11, + "args": [ + { + "name": "index", + "ty": "u32" + }, + { + "name": "has_rail", + "ty": "bool" + }, + { + "name": "op", + "ty": "u8" + } + ], + "reply": "u8", + "error": "ResponseCode", + "idempotent": true + }, + { + "name": "raw_pmbus_read_word", + "code": 12, + "args": [ + { + "name": "index", + "ty": "u32" + }, + { + "name": "has_rail", + "ty": "bool" + }, + { + "name": "op", + "ty": "u8" + } + ], + "reply": "u16", + "error": "ResponseCode", + "idempotent": true + }, + { + "name": "raw_pmbus_read_word32", + "code": 13, + "args": [ + { + "name": "index", + "ty": "u32" + }, + { + "name": "has_rail", + "ty": "bool" + }, + { + "name": "op", + "ty": "u8" + } + ], + "reply": "u32", + "error": "ResponseCode", + "idempotent": true + }, + { + "name": "raw_pmbus_read_block", + "code": 14, + "args": [ + { + "name": "index", + "ty": "u32" + }, + { + "name": "has_rail", + "ty": "bool" + }, + { + "name": "op", + "ty": "u8" + } + ], + "reply": "RawPmbusBlock", + "error": "ResponseCode", + "idempotent": true + }, + { + "name": "raw_pmbus_set", + "code": 15, + "args": [ + { + "name": "index", + "ty": "u32" + }, + { + "name": "has_rail", + "ty": "bool" + }, + { + "name": "op", + "ty": "u8" + } + ], + "reply": "()", + "error": "ResponseCode", + "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" + } + ], + "reply": "()", + "error": "ResponseCode", + "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" + } + ], + "reply": "()", + "error": "ResponseCode", + "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" + } + ], + "reply": "()", + "error": "ResponseCode", + "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" + } + ], + "reply": "()", + "error": "ResponseCode", + "idempotent": true + } + ] + }, + { + "name": "Sequencer", + "task": "gimlet_seq", + "task_id": 10, + "ops": [ + { + "name": "get_state", + "code": 1, + "reply": "drv_cpu_power_state::PowerState", + "idempotent": true + }, + { + "name": "set_state", + "code": 2, + "args": [ + { + "name": "state", + "ty": "drv_cpu_power_state::PowerState" + } + ], + "reply": "drv_cpu_seq_api::Transition", + "error": "drv_cpu_seq_api::SeqError", + "idempotent": false + }, + { + "name": "set_state_with_reason", + "code": 3, + "args": [ + { + "name": "state", + "ty": "drv_cpu_power_state::PowerState" + }, + { + "name": "reason", + "ty": "StateChangeReason" + } + ], + "reply": "drv_cpu_seq_api::Transition", + "error": "drv_cpu_seq_api::SeqError", + "idempotent": false + }, + { + "name": "send_hardware_nmi", + "code": 4, + "reply": "()", + "error": "ServerDeath", + "idempotent": false + }, + { + "name": "read_fpga_regs", + "code": 5, + "reply": "[u8; 64]", + "idempotent": true + }, + { + "name": "last_post_code", + "code": 6, + "reply": "u32", + "idempotent": true + }, + { + "name": "post_code_buffer_len", + "code": 7, + "reply": "u32", + "idempotent": true + }, + { + "name": "get_post_code", + "code": 8, + "args": [ + { + "name": "index", + "ty": "u32" + } + ], + "reply": "u32", + "idempotent": true + }, + { + "name": "gpio_edge_count", + "code": 9, + "reply": "u32", + "idempotent": true + }, + { + "name": "gpio_cycle_count", + "code": 10, + "reply": "u32", + "idempotent": true + }, + { + "name": "enable_console_redirect", + "code": 11, + "reply": "()", + "idempotent": true + }, + { + "name": "disable_console_redirect", + "code": 12, + "reply": "()", + "idempotent": true + } + ] + }, + { + "name": "Hash", + "task": "hash_driver", + "task_id": 12, + "ops": [ + { + "name": "init_sha256", + "code": 1, + "reply": "()", + "error": "HashError", + "idempotent": false + }, + { + "name": "update", + "code": 2, + "args": [ + { + "name": "len", + "ty": "u32" + } + ], + "reply": "()", + "error": "HashError", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": false + }, + { + "name": "finalize_sha256", + "code": 3, + "reply": "[u8; crate::SHA256_SZ]", + "error": "HashError", + "idempotent": false + }, + { + "name": "digest_sha256", + "code": 4, + "args": [ + { + "name": "len", + "ty": "u32" + } + ], + "reply": "[u8; crate::SHA256_SZ]", + "error": "HashError", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": false + } + ] + }, + { + "name": "Rng", + "task": "rng_driver", + "task_id": 13, + "ops": [ + { + "name": "fill", + "code": 1, + "reply": "usize", + "error": "RngError", + "leases": [ + { + "name": "source", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": false + } + ] + }, + { + "name": "HostFlash", + "task": "hf", + "task_id": 14, + "ops": [ + { + "name": "read_id", + "code": 1, + "reply": "HfChipId", + "error": "HfError", + "idempotent": false + }, + { + "name": "capacity", + "code": 2, + "reply": "usize", + "error": "HfError", + "idempotent": false + }, + { + "name": "read_status", + "code": 3, + "reply": "u8", + "error": "HfError", + "idempotent": false + }, + { + "name": "bulk_erase", + "code": 4, + "args": [ + { + "name": "protect", + "ty": "HfProtectMode" + } + ], + "reply": "()", + "error": "HfError", + "idempotent": false + }, + { + "name": "page_program", + "code": 5, + "args": [ + { + "name": "address", + "ty": "u32" + }, + { + "name": "protect", + "ty": "HfProtectMode" + } + ], + "reply": "()", + "error": "HfError", + "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" + } + ], + "reply": "()", + "error": "HfError", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": false + }, + { + "name": "read", + "code": 7, + "args": [ + { + "name": "address", + "ty": "u32" + } + ], + "reply": "()", + "error": "HfError", + "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" + } + ], + "reply": "()", + "error": "HfError", + "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" + } + ], + "reply": "()", + "error": "HfError", + "idempotent": false + }, + { + "name": "sector_erase_dev", + "code": 10, + "args": [ + { + "name": "dev", + "ty": "HfDevSelect" + }, + { + "name": "address", + "ty": "u32" + }, + { + "name": "protect", + "ty": "HfProtectMode" + } + ], + "reply": "()", + "error": "HfError", + "idempotent": false + }, + { + "name": "get_mux", + "code": 11, + "reply": "HfMuxState", + "error": "HfError", + "idempotent": false + }, + { + "name": "set_mux", + "code": 12, + "args": [ + { + "name": "state", + "ty": "HfMuxState" + } + ], + "reply": "()", + "error": "HfError", + "idempotent": false + }, + { + "name": "get_dev", + "code": 13, + "reply": "HfDevSelect", + "error": "HfError", + "idempotent": false + }, + { + "name": "set_dev", + "code": 14, + "args": [ + { + "name": "dev", + "ty": "HfDevSelect" + } + ], + "reply": "()", + "error": "HfError", + "idempotent": false + }, + { + "name": "check_dev", + "code": 15, + "args": [ + { + "name": "dev", + "ty": "HfDevSelect" + } + ], + "reply": "()", + "error": "HfError", + "idempotent": false + }, + { + "name": "hash", + "code": 16, + "args": [ + { + "name": "address", + "ty": "u32" + }, + { + "name": "len", + "ty": "u32" + } + ], + "reply": "[u8; drv_hash_api::SHA256_SZ]", + "error": "HfError", + "idempotent": false + }, + { + "name": "hash_significant_bits", + "code": 17, + "args": [ + { + "name": "dev", + "ty": "HfDevSelect" + } + ], + "reply": "()", + "error": "HfError", + "idempotent": false + }, + { + "name": "get_cached_hash", + "code": 18, + "args": [ + { + "name": "dev", + "ty": "HfDevSelect" + } + ], + "reply": "[u8; drv_hash_api::SHA256_SZ]", + "error": "HfError", + "idempotent": false + }, + { + "name": "get_persistent_data", + "code": 19, + "reply": "HfPersistentData", + "error": "HfError", + "idempotent": false + }, + { + "name": "write_persistent_data", + "code": 20, + "args": [ + { + "name": "dev_select", + "ty": "HfDevSelect" + } + ], + "reply": "()", + "error": "HfError", + "idempotent": false + }, + { + "name": "apob_begin", + "code": 21, + "args": [ + { + "name": "length", + "ty": "u32" + }, + { + "name": "algorithm", + "ty": "ApobHash" + } + ], + "reply": "()", + "error": "ApobBeginError", + "idempotent": true + }, + { + "name": "apob_write", + "code": 22, + "args": [ + { + "name": "offset", + "ty": "u32" + } + ], + "reply": "()", + "error": "ApobWriteError", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": true + }, + { + "name": "apob_commit", + "code": 23, + "reply": "()", + "error": "ApobCommitError", + "idempotent": true + }, + { + "name": "apob_lock", + "code": 24, + "reply": "()", + "idempotent": true + }, + { + "name": "apob_read", + "code": 25, + "args": [ + { + "name": "offset", + "ty": "u32" + } + ], + "reply": "usize", + "error": "ApobReadError", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": true + }, + { + "name": "apob_clear", + "code": 26, + "reply": "()", + "error": "ApobClearError", + "idempotent": true + } + ] + }, + { + "name": "Update", + "task": "update_server", + "task_id": 15, + "ops": [ + { + "name": "block_size", + "code": 1, + "reply": "usize", + "error": "drv_update_api::UpdateError", + "idempotent": false + }, + { + "name": "prep_image_update", + "code": 2, + "reply": "()", + "error": "drv_update_api::UpdateError", + "idempotent": false + }, + { + "name": "write_one_block", + "code": 3, + "args": [ + { + "name": "block_num", + "ty": "usize" + } + ], + "reply": "()", + "error": "drv_update_api::UpdateError", + "leases": [ + { + "name": "block", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": false + }, + { + "name": "abort_update", + "code": 4, + "reply": "()", + "error": "drv_update_api::UpdateError", + "idempotent": false + }, + { + "name": "finish_image_update", + "code": 5, + "reply": "()", + "error": "drv_update_api::UpdateError", + "idempotent": false + }, + { + "name": "current_version", + "code": 6, + "reply": "ImageVersion", + "idempotent": true + }, + { + "name": "read_caboose_value", + "code": 7, + "args": [ + { + "name": "name", + "ty": "[u8; 4]" + } + ], + "reply": "u32", + "error": "CabooseError", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": true + }, + { + "name": "get_pending_boot_slot", + "code": 8, + "reply": "SlotId", + "idempotent": true + }, + { + "name": "set_pending_boot_slot", + "code": 9, + "args": [ + { + "name": "slot", + "ty": "SlotId" + } + ], + "reply": "()", + "error": "drv_update_api::UpdateError", + "idempotent": false + } + ] + }, + { + "name": "Sensor", + "task": "sensor", + "task_id": 16, + "ops": [ + { + "name": "get", + "code": 1, + "args": [ + { + "name": "id", + "ty": "SensorId" + } + ], + "reply": "f32", + "error": "SensorError", + "idempotent": true + }, + { + "name": "get_reading", + "code": 2, + "args": [ + { + "name": "id", + "ty": "SensorId" + } + ], + "reply": "Reading", + "error": "SensorError", + "idempotent": true + }, + { + "name": "get_raw_reading", + "code": 3, + "args": [ + { + "name": "id", + "ty": "SensorId" + } + ], + "reply": "Option<(Result, u64)>", + "idempotent": true + }, + { + "name": "get_last_data", + "code": 4, + "args": [ + { + "name": "id", + "ty": "SensorId" + } + ], + "reply": "Option<(f32, u64)>", + "idempotent": true + }, + { + "name": "get_last_nodata", + "code": 5, + "args": [ + { + "name": "id", + "ty": "SensorId" + } + ], + "reply": "Option<(NoData, u64)>", + "idempotent": true + }, + { + "name": "get_min", + "code": 6, + "args": [ + { + "name": "id", + "ty": "SensorId" + } + ], + "reply": "(f32, u64)", + "idempotent": true + }, + { + "name": "get_max", + "code": 7, + "args": [ + { + "name": "id", + "ty": "SensorId" + } + ], + "reply": "(f32, u64)", + "idempotent": true + }, + { + "name": "post", + "code": 8, + "args": [ + { + "name": "id", + "ty": "SensorId" + }, + { + "name": "value", + "ty": "f32" + }, + { + "name": "timestamp", + "ty": "u64" + } + ], + "reply": "()", + "idempotent": true + }, + { + "name": "nodata", + "code": 9, + "args": [ + { + "name": "id", + "ty": "SensorId" + }, + { + "name": "nodata", + "ty": "NoData" + }, + { + "name": "timestamp", + "ty": "u64" + } + ], + "reply": "()", + "idempotent": true + }, + { + "name": "get_nerrors", + "code": 10, + "args": [ + { + "name": "id", + "ty": "SensorId" + } + ], + "reply": "u32", + "idempotent": true + } + ] + }, + { + "name": "HostSpComms", + "task": "host_sp_comms", + "task_id": 17, + "ops": [ + { + "name": "set_status", + "code": 1, + "args": [ + { + "name": "status", + "ty": "u64" + } + ], + "reply": "()", + "error": "HostSpCommsError", + "idempotent": false + }, + { + "name": "get_status", + "code": 2, + "reply": "Status", + "error": "HostSpCommsError", + "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" + } + ], + "reply": "()", + "error": "ControlPlaneAgentError", + "idempotent": false + }, + { + "name": "get_host_phase2_data", + "code": 2, + "args": [ + { + "name": "image_hash", + "ty": "[u8; 32]" + }, + { + "name": "offset", + "ty": "u64" + } + ], + "reply": "usize", + "error": "ControlPlaneAgentError", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": false + }, + { + "name": "get_startup_options", + "code": 3, + "reply": "HostStartupOptions", + "error": "ControlPlaneAgentError", + "idempotent": false + }, + { + "name": "set_startup_options", + "code": 4, + "args": [ + { + "name": "startup_options", + "ty": "u64" + } + ], + "reply": "()", + "error": "ControlPlaneAgentError", + "idempotent": false + }, + { + "name": "identity", + "code": 5, + "reply": "OxideIdentity", + "idempotent": true + }, + { + "name": "get_uart_client", + "code": 6, + "reply": "UartClient", + "idempotent": true + }, + { + "name": "get_installinator_image_id", + "code": 7, + "reply": "usize", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": true + }, + { + "name": "set_humility_uart_client", + "code": 8, + "args": [ + { + "name": "attach", + "ty": "bool" + } + ], + "reply": "()", + "error": "ControlPlaneAgentError", + "idempotent": false + }, + { + "name": "uart_read", + "code": 9, + "reply": "usize", + "error": "ControlPlaneAgentError", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": false + }, + { + "name": "uart_write", + "code": 10, + "reply": "usize", + "error": "ControlPlaneAgentError", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": false + } + ] + }, + { + "name": "SpRot", + "task": "sprot", + "task_id": 21, + "ops": [ + { + "name": "status", + "code": 1, + "reply": "SprotStatus", + "error": "SprotError", + "idempotent": true + }, + { + "name": "io_stats", + "code": 2, + "reply": "SprotIoStats", + "error": "SprotError", + "idempotent": true + }, + { + "name": "rot_state", + "code": 3, + "reply": "RotState", + "error": "SprotError", + "idempotent": true + }, + { + "name": "pulse_cs", + "code": 4, + "args": [ + { + "name": "delay", + "ty": "u16" + } + ], + "reply": "PulseStatus", + "error": "SprotError", + "idempotent": false + }, + { + "name": "block_size", + "code": 5, + "reply": "u32", + "error": "SprotError", + "idempotent": true + }, + { + "name": "prep_image_update", + "code": 6, + "args": [ + { + "name": "target", + "ty": "UpdateTarget" + } + ], + "reply": "()", + "error": "SprotError", + "idempotent": true + }, + { + "name": "write_one_block", + "code": 7, + "args": [ + { + "name": "block_num", + "ty": "u32" + } + ], + "reply": "()", + "error": "SprotError", + "leases": [ + { + "name": "block", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": false + }, + { + "name": "abort_update", + "code": 8, + "reply": "()", + "error": "SprotError", + "idempotent": false + }, + { + "name": "finish_image_update", + "code": 9, + "reply": "()", + "error": "SprotError", + "idempotent": false + }, + { + "name": "switch_default_image", + "code": 10, + "args": [ + { + "name": "slot", + "ty": "SlotId" + }, + { + "name": "duration", + "ty": "SwitchDuration" + } + ], + "reply": "()", + "error": "SprotError", + "idempotent": true + }, + { + "name": "reset", + "code": 11, + "reply": "()", + "error": "SprotError", + "idempotent": true + }, + { + "name": "dump", + "code": 12, + "args": [ + { + "name": "address", + "ty": "u32" + } + ], + "reply": "()", + "error": "DumpOrSprotError", + "idempotent": true + }, + { + "name": "caboose_size", + "code": 13, + "args": [ + { + "name": "slot", + "ty": "SlotId" + } + ], + "reply": "u32", + "error": "RawCabooseOrSprotError", + "idempotent": true + }, + { + "name": "read_caboose_region", + "code": 14, + "args": [ + { + "name": "offset", + "ty": "u32" + }, + { + "name": "slot", + "ty": "SlotId" + } + ], + "reply": "()", + "error": "RawCabooseOrSprotError", + "leases": [ + { + "name": "out", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": true + }, + { + "name": "rot_boot_info", + "code": 15, + "reply": "RotBootInfo", + "error": "SprotError", + "idempotent": true + }, + { + "name": "cert_chain_len", + "code": 16, + "reply": "u32", + "error": "AttestOrSprotError", + "idempotent": true + }, + { + "name": "cert_len", + "code": 17, + "args": [ + { + "name": "index", + "ty": "u32" + } + ], + "reply": "u32", + "error": "AttestOrSprotError", + "idempotent": true + }, + { + "name": "cert", + "code": 18, + "args": [ + { + "name": "index", + "ty": "u32" + }, + { + "name": "offset", + "ty": "u32" + } + ], + "reply": "()", + "error": "AttestOrSprotError", + "leases": [ + { + "name": "dest", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": true + }, + { + "name": "record", + "code": 19, + "args": [ + { + "name": "algorithm", + "ty": "HashAlgorithm" + } + ], + "reply": "()", + "error": "AttestOrSprotError", + "leases": [ + { + "name": "data", + "ty": "[u8]", + "read": true, + "write": false + } + ], + "idempotent": false + }, + { + "name": "read_rot_page", + "code": 20, + "args": [ + { + "name": "page", + "ty": "RotPage" + } + ], + "reply": "()", + "error": "SprotError", + "leases": [ + { + "name": "dest", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": true + }, + { + "name": "log", + "code": 21, + "args": [ + { + "name": "offset", + "ty": "u32" + } + ], + "reply": "()", + "error": "AttestOrSprotError", + "leases": [ + { + "name": "dest", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": false + }, + { + "name": "log_len", + "code": 22, + "reply": "u32", + "error": "AttestOrSprotError", + "idempotent": true + }, + { + "name": "attest", + "code": 23, + "reply": "()", + "error": "AttestOrSprotError", + "leases": [ + { + "name": "nonce", + "ty": "[u8]", + "read": true, + "write": false + }, + { + "name": "dest", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": false + }, + { + "name": "attest_len", + "code": 24, + "reply": "u32", + "error": "AttestOrSprotError", + "idempotent": true + }, + { + "name": "enable_sp_slot_watchdog", + "code": 25, + "args": [ + { + "name": "time_ms", + "ty": "u32" + } + ], + "reply": "()", + "error": "SprotError", + "idempotent": false + }, + { + "name": "disable_sp_slot_watchdog", + "code": 26, + "reply": "()", + "error": "SprotError", + "idempotent": false + }, + { + "name": "sp_slot_watchdog_supported", + "code": 27, + "reply": "()", + "error": "SprotError", + "idempotent": false + }, + { + "name": "versioned_rot_boot_info", + "code": 28, + "args": [ + { + "name": "version", + "ty": "u8" + } + ], + "reply": "VersionedRotBootInfo", + "error": "SprotError", + "idempotent": true + }, + { + "name": "component_caboose_size", + "code": 29, + "args": [ + { + "name": "component", + "ty": "RotComponent" + }, + { + "name": "slot", + "ty": "SlotId" + } + ], + "reply": "u32", + "error": "RawCabooseOrSprotError", + "idempotent": true + }, + { + "name": "component_read_caboose_region", + "code": 30, + "args": [ + { + "name": "offset", + "ty": "u32" + }, + { + "name": "component", + "ty": "RotComponent" + }, + { + "name": "slot", + "ty": "SlotId" + } + ], + "reply": "()", + "error": "RawCabooseOrSprotError", + "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" + } + ], + "reply": "()", + "error": "SprotError", + "idempotent": true + }, + { + "name": "component_switch_default_image", + "code": 32, + "args": [ + { + "name": "component", + "ty": "RotComponent" + }, + { + "name": "slot", + "ty": "SlotId" + }, + { + "name": "duration", + "ty": "SwitchDuration" + } + ], + "reply": "()", + "error": "SprotError", + "idempotent": true + }, + { + "name": "lifecycle_state", + "code": 33, + "reply": "LifecycleState", + "error": "StateOrSprotError", + "idempotent": true + }, + { + "name": "tq_cert_chain_len", + "code": 34, + "reply": "u32", + "error": "AttestOrSprotError", + "idempotent": true + }, + { + "name": "tq_cert_len", + "code": 35, + "args": [ + { + "name": "index", + "ty": "u32" + } + ], + "reply": "u32", + "error": "AttestOrSprotError", + "idempotent": true + }, + { + "name": "tq_cert", + "code": 36, + "args": [ + { + "name": "index", + "ty": "u32" + }, + { + "name": "offset", + "ty": "u32" + } + ], + "reply": "()", + "error": "AttestOrSprotError", + "leases": [ + { + "name": "dest", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": true + }, + { + "name": "tq_sign", + "code": 37, + "reply": "()", + "error": "AttestOrSprotError", + "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, + "reply": "u32", + "error": "AttestOrSprotError", + "idempotent": true + } + ] + }, + { + "name": "Validate", + "task": "validate", + "task_id": 22, + "ops": [ + { + "name": "validate_i2c", + "code": 1, + "args": [ + { + "name": "index", + "ty": "u32" + } + ], + "reply": "ValidateOk", + "error": "ValidateError", + "idempotent": true + } + ] + }, + { + "name": "Vpd", + "task": "vpd", + "task_id": 23, + "ops": [ + { + "name": "read_tmp117_eeprom", + "code": 1, + "args": [ + { + "name": "index", + "ty": "u8" + } + ], + "reply": "[u8; 6]", + "error": "VpdError", + "idempotent": false + }, + { + "name": "read", + "code": 2, + "args": [ + { + "name": "index", + "ty": "u8" + }, + { + "name": "offset", + "ty": "u16" + } + ], + "reply": "[u8; 16]", + "error": "VpdError", + "idempotent": false + }, + { + "name": "write", + "code": 3, + "args": [ + { + "name": "index", + "ty": "u8" + }, + { + "name": "offset", + "ty": "u16" + }, + { + "name": "contents", + "ty": "u8" + } + ], + "reply": "()", + "error": "VpdError", + "idempotent": false + }, + { + "name": "is_locked", + "code": 4, + "args": [ + { + "name": "index", + "ty": "u8" + } + ], + "reply": "bool", + "error": "VpdError", + "idempotent": false + }, + { + "name": "permanently_lock", + "code": 5, + "args": [ + { + "name": "index", + "ty": "u8" + } + ], + "reply": "()", + "error": "VpdError", + "idempotent": false + }, + { + "name": "num_vpd_devices", + "code": 6, + "reply": "usize", + "idempotent": true + } + ] + }, + { + "name": "UserLeds", + "task": "user_leds", + "task_id": 24, + "ops": [ + { + "name": "led_on", + "code": 1, + "args": [ + { + "name": "index", + "ty": "usize" + } + ], + "reply": "()", + "error": "LedError", + "idempotent": true + }, + { + "name": "led_off", + "code": 2, + "args": [ + { + "name": "index", + "ty": "usize" + } + ], + "reply": "()", + "error": "LedError", + "idempotent": true + }, + { + "name": "led_toggle", + "code": 3, + "args": [ + { + "name": "index", + "ty": "usize" + } + ], + "reply": "()", + "error": "LedError", + "idempotent": true + }, + { + "name": "led_blink", + "code": 4, + "args": [ + { + "name": "index", + "ty": "usize" + } + ], + "reply": "()", + "error": "LedError", + "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" + } + ], + "reply": "[u8; DUMP_READ_SIZE]", + "error": "DumpAgentError", + "idempotent": false + }, + { + "name": "read_dump_into", + "code": 2, + "args": [ + { + "name": "index", + "ty": "u8" + }, + { + "name": "offset", + "ty": "u32" + } + ], + "reply": "usize", + "error": "DumpAgentError", + "leases": [ + { + "name": "out", + "ty": "[u8]", + "read": false, + "write": true + } + ], + "idempotent": false + }, + { + "name": "get_dump_area", + "code": 3, + "args": [ + { + "name": "index", + "ty": "u8" + } + ], + "reply": "DumpArea", + "error": "DumpAgentError", + "idempotent": false + }, + { + "name": "initialize_dump", + "code": 4, + "reply": "()", + "error": "DumpAgentError", + "idempotent": false + }, + { + "name": "add_dump_segment", + "code": 5, + "args": [ + { + "name": "address", + "ty": "u32" + }, + { + "name": "length", + "ty": "u32" + } + ], + "reply": "()", + "error": "DumpAgentError", + "idempotent": false + }, + { + "name": "take_dump", + "code": 6, + "reply": "()", + "error": "DumpAgentError", + "idempotent": false + }, + { + "name": "dump_task", + "code": 7, + "args": [ + { + "name": "task_index", + "ty": "u32" + } + ], + "reply": "u8", + "error": "DumpAgentError", + "idempotent": false + }, + { + "name": "dump_task_region", + "code": 8, + "args": [ + { + "name": "task_index", + "ty": "u32" + }, + { + "name": "start", + "ty": "u32" + }, + { + "name": "length", + "ty": "u32" + } + ], + "reply": "u8", + "error": "DumpAgentError", + "idempotent": false + }, + { + "name": "reinitialize_dump_from", + "code": 9, + "args": [ + { + "name": "index", + "ty": "u8" + } + ], + "reply": "()", + "error": "DumpAgentError", + "idempotent": false + } + ] + }, + { + "name": "Sbrmi", + "task": "sbrmi", + "task_id": 27, + "ops": [ + { + "name": "nthreads", + "code": 1, + "reply": "u8", + "error": "SbrmiError", + "idempotent": true + }, + { + "name": "enabled", + "code": 2, + "reply": "[u8; 16]", + "error": "SbrmiError", + "idempotent": true + }, + { + "name": "alert", + "code": 3, + "reply": "[u8; 16]", + "error": "SbrmiError", + "idempotent": true + }, + { + "name": "cpuid", + "code": 4, + "args": [ + { + "name": "thread", + "ty": "u8" + }, + { + "name": "eax", + "ty": "u32" + }, + { + "name": "ecx", + "ty": "u32" + } + ], + "reply": "[u32; 4]", + "error": "SbrmiError", + "idempotent": true + }, + { + "name": "rdmsr8", + "code": 5, + "args": [ + { + "name": "thread", + "ty": "u8" + }, + { + "name": "msr", + "ty": "u32" + } + ], + "reply": "u8", + "error": "SbrmiError", + "idempotent": true + }, + { + "name": "rdmsr16", + "code": 6, + "args": [ + { + "name": "thread", + "ty": "u8" + }, + { + "name": "msr", + "ty": "u32" + } + ], + "reply": "u16", + "error": "SbrmiError", + "idempotent": true + }, + { + "name": "rdmsr32", + "code": 7, + "args": [ + { + "name": "thread", + "ty": "u8" + }, + { + "name": "msr", + "ty": "u32" + } + ], + "reply": "u32", + "error": "SbrmiError", + "idempotent": true + }, + { + "name": "rdmsr64", + "code": 8, + "args": [ + { + "name": "thread", + "ty": "u8" + }, + { + "name": "msr", + "ty": "u32" + } + ], + "reply": "u64", + "error": "SbrmiError", + "idempotent": true + } + ] + } + ] +} \ 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..4a9a092fc --- /dev/null +++ b/humility-hif-assembler/src/archive.rs @@ -0,0 +1,481 @@ +// 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`. + pub fn from_archive(hubris: &HubrisArchive) -> Result { + 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(); + + ops.push(IdolOpInfo { + name: op_name.clone(), + code, + args, + reply, + error, + leases, + idempotent: op.idempotent, + }); + } + + interfaces.push(IdolInterfaceInfo { + name: iface.name.clone(), + task: module.name.clone(), + task_id: i as u32, + ops, + }); + } + + interfaces +} + +/// 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..0cc1326d1 --- /dev/null +++ b/humility-hif-assembler/src/assembler.rs @@ -0,0 +1,701 @@ +// 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, + pub warnings: Vec, +} + +/// 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 bundle = HifBundle { metadata, text, data: result.data }; + + Ok(AssembleOutput { bundle, warnings: result.warnings }) + } +} + +#[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(), + }], + reply: "f32".into(), + error: Some("SensorError".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(), + }, + ], + reply: "()".into(), + error: Some("SensorError".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.len() > 0); + 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/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..177d3fcf4 --- /dev/null +++ b/humility-hif-assembler/src/lib.rs @@ -0,0 +1,199 @@ +// 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 500 +//! i2c_read mid 0x48 reg=0x00 2 +//! end +//! +//! # With a sleep between iterations +//! repeat 100 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 1000 +//! +//! repeat $ITERATIONS +//! i2c_read mid 0x48 reg=$TEMP_REG 2 +//! end +//! ``` +//! +//! ### Sleep +//! +//! ```text +//! sleep 50ms # pause for 50 milliseconds (max 100ms per call) +//! ``` +//! +//! ### Raw Ops +//! +//! For anything the sugar doesn't cover, raw HIF instructions are +//! available: +//! +//! ```text +//! raw { +//! push 0x48 +//! push_none +//! push 2 +//! call i2c_read +//! drop_n 7 +//! done +//! } +//! ``` +//! +//! ## Compilation +//! +//! ```rust,ignore +//! let asm = HifAssembler::from_archive(&archive)?; +//! +//! // 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 bundle = asm.assemble("i2c_read mid 0x48 reg=0x00 2")?; +//! bundle.write_to_file("program.hifb")?; +//! ``` +//! +//! ## Building Programs Programmatically +//! +//! When generating programs from a test harness (e.g. PRNG-driven +//! fuzzing), use [`ProgramBuilder`] instead of text: +//! +//! ```rust,ignore +//! // ProgramBuilder API -- not yet implemented +//! let asm = HifAssembler::from_archive(&archive)?; +//! let mut prog = asm.builder(); +//! +//! prog.repeat(500, |body| { +//! body.i2c_read("mid", 0x48, Some(0x00), 2)?; +//! Ok(()) +//! })?; +//! +//! let bundle = prog.assemble()?; +//! ``` +//! +//! ## 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.) +//! +//! ## 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 bundle; +pub mod error; +pub mod listing; +pub mod lower; +pub mod parser; +pub mod types; + +pub use assembler::{AssembleOutput, HifAssembler, VerifyReport}; +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..b45187bbf --- /dev/null +++ b/humility-hif-assembler/src/listing.rs @@ -0,0 +1,69 @@ +// 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() } + } +} diff --git a/humility-hif-assembler/src/lower.rs b/humility-hif-assembler/src/lower.rs new file mode 100644 index 000000000..37b353578 --- /dev/null +++ b/humility-hif-assembler/src/lower.rs @@ -0,0 +1,646 @@ +// 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, 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)?; + 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(*address)); + 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::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"); + } + + let label = self.alloc_label(label_counter, line)?; + result.ops.push(push_smallest(*count)); + result.ops.push(hif::Op::Label(hif::Target(label))); + + 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::Swap); + 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)); + + // TODO: Add reply_size to IdolOpInfo from DWARF + result.warnings.push(format!( + "line {line}: {interface}.{operation} reply size \ + estimated as 0; results may be truncated for \ + large reply types" + )); + result.ops.push(push_smallest(0)); + + 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: u8, + mux: &Option, + ops: &mut Vec, + warnings: &mut Vec, + line: usize, + ) -> Result<()> { + let (controller, port_index) = self.resolve_bus(bus, 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(address)); + 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)) + } + } + } + + /// 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}'") + } + } + } +} + +// -- Free functions -- + +/// 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..d30c7cc71 --- /dev/null +++ b/humility-hif-assembler/src/parser.rs @@ -0,0 +1,851 @@ +// 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 }, +} + +/// A single statement in the HIF source. +#[derive(Debug, Clone)] +pub enum Statement { + I2cRead { + bus: BusRef, + address: u8, + mux: Option, + register: Option, + nbytes: u8, + }, + I2cWrite { + bus: BusRef, + address: u8, + mux: Option, + register: Option, + data: Vec, + }, + I2cScan { + bus: BusRef, + mux: Option, + }, + I2cRegScan { + bus: BusRef, + address: u8, + mux: Option, + }, + IdolCall { + interface: String, + operation: String, + args: Vec<(String, String)>, + }, + Sleep { + ms: u32, + }, + Repeat { + count: u32, + sleep_ms: Option, + body: 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)? + } + 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()) +} + +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_num_err::(tokens[1], line)?; + 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_num_err::(tokens[1], line)?; + 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_num_err::(tokens[1], line)?; + 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 `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, 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, 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:?}"), + } + } +} diff --git a/humility-hif-assembler/src/types.rs b/humility-hif-assembler/src/types.rs new file mode 100644 index 000000000..78a19e011 --- /dev/null +++ b/humility-hif-assembler/src/types.rs @@ -0,0 +1,210 @@ +// 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 { + pub name: String, + pub id: u8, + 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 { + pub text: usize, + pub data: usize, + pub rstack: usize, + 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, + /// Reply type name (e.g. "f32", "()", "SensorReading"). + pub reply: String, + /// Error type name, if the operation can fail (e.g. "SensorError"). + #[serde(default, skip_serializing_if = "Option::is_none")] + pub error: Option, + /// 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, +} + +/// 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/fixture_tests.rs b/humility-hif-assembler/tests/fixture_tests.rs new file mode 100644 index 000000000..70bf1878b --- /dev/null +++ b/humility-hif-assembler/tests/fixture_tests.rs @@ -0,0 +1,213 @@ +// 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_from_fixture() { + 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_stress_loop_from_fixture() { + let asm = HifAssembler::new(load_gimlet()); + let src = "repeat 500\n i2c_read mid 0x48 reg=0x00 2\nend"; + let out = asm.assemble(src).expect("assemble failed"); + assert!(out.bundle.fits_in_target()); + assert_eq!(out.bundle.metadata.estimated_results, Some(500)); +} + +#[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]; + + let src = format!( + "i2c_read front 0x{:02x} mux=0x{:02x}.{} 1", + dev.address, 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}"); +} From 4a38c64206839708d09105c1c7cdb2a28d59f4ea Mon Sep 17 00:00:00 2001 From: Ben Stoltz Date: Tue, 7 Apr 2026 16:22:25 -0700 Subject: [PATCH 3/6] Add --exec to humility hiffy, Idol reply sizes, and call statement MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Programs can now be executed on SP devices via `humility hiffy --exec program.hif`. The assembler produces the HIF bytecode; humility's HiffyContext handles execution and result retrieval. Results are fully decoded using DWARF type info — Idol calls show struct-level output identical to `humility hiffy -c`. Tested on grapefruit (probe) and sidecar (network via NetHiffy): I2C temperature reads, fan controller reads, multi-bus operations, SpRot Idol calls, and sensor polling — 230 operations across 5 test programs with zero errors. Key changes: - Generic `call` statement for invoking any HIF function by name, eliminating the need for per-function sugar - Fixed repeat loop counter pattern to match humility cmd/i2c (increment toward limit, not decrement) - Idol reply sizes computed from DWARF during archive extraction - `humility hiffy --exec` with full result decoding, JSON output, and --save-bundle for CI artifacts - Assembly-time program stats (transactions, bytes, mux switches) - ProgramBuilder API for Rust-driven program generation - Archive validation: checks for hiffy task and net feature - Sidecar and grapefruit fixtures; gimlet/cosmo regenerated --- Cargo.lock | 5 +- Cargo.toml | 1 + cmd/hiffy/Cargo.toml | 4 + cmd/hiffy/src/lib.rs | 361 +- humility-hif-assembler/Cargo.toml | 2 - humility-hif-assembler/README.md | 247 +- humility-hif-assembler/fixtures/README | 15 + humility-hif-assembler/fixtures/cosmo-b.json | 786 ++ humility-hif-assembler/fixtures/gimlet-c.json | 687 ++ .../fixtures/grapefruit.json | 5303 ++++++++++++++ .../fixtures/sidecar-b.json | 6302 +++++++++++++++++ humility-hif-assembler/src/archive.rs | 120 +- humility-hif-assembler/src/assembler.rs | 52 +- humility-hif-assembler/src/builder.rs | 340 + humility-hif-assembler/src/bundle.rs | 2 +- humility-hif-assembler/src/lib.rs | 62 +- humility-hif-assembler/src/lower.rs | 57 +- humility-hif-assembler/src/parser.rs | 58 + humility-hif-assembler/src/stats.rs | 310 + humility-hif-assembler/src/types.rs | 13 + 20 files changed, 14585 insertions(+), 142 deletions(-) create mode 100644 humility-hif-assembler/fixtures/README create mode 100644 humility-hif-assembler/fixtures/grapefruit.json create mode 100644 humility-hif-assembler/fixtures/sidecar-b.json create mode 100644 humility-hif-assembler/src/builder.rs create mode 100644 humility-hif-assembler/src/stats.rs diff --git a/Cargo.lock b/Cargo.lock index 445364eca..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]] @@ -2437,8 +2440,6 @@ dependencies = [ "anyhow", "hif", "humility-core", - "humility-hiffy", - "humility-i2c", "humility-idol", "idol", "postcard", diff --git a/Cargo.toml b/Cargo.toml index 28f38d4c5..87c4e4ea6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -118,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..bd74a94e6 100644 --- a/cmd/hiffy/src/lib.rs +++ b/cmd/hiffy/src/lib.rs @@ -48,13 +48,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_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 +106,22 @@ struct HiffyArgs { #[clap(long, short, use_value_delimiter = true, requires = "call")] arguments: Vec, + /// execute a .hif or .hifb program file + #[clap( + long, + conflicts_with_all = &["list", "listfuncs", "call"], + value_name = "FILE" + )] + exec: 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, @@ -233,10 +253,14 @@ fn hiffy(context: &mut ExecutionContext) -> Result<()> { // creation failure. (Note that -L will still create the HiffyContext, // even if run on a dump.) // - if subargs.call.is_some() && core.is_dump() { + 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 { @@ -321,7 +345,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,6 +380,339 @@ fn hiffy(context: &mut ExecutionContext) -> Result<()> { 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(), diff --git a/humility-hif-assembler/Cargo.toml b/humility-hif-assembler/Cargo.toml index 3d2422bf6..499fecb8c 100644 --- a/humility-hif-assembler/Cargo.toml +++ b/humility-hif-assembler/Cargo.toml @@ -9,8 +9,6 @@ anyhow = "1.0" hif.workspace = true idol.workspace = true humility.workspace = true -humility-hiffy.workspace = true -humility-i2c.workspace = true humility-idol.workspace = true postcard = { version = "0.7.0", features = ["alloc"] } serde = { version = "1.0", features = ["derive"] } diff --git a/humility-hif-assembler/README.md b/humility-hif-assembler/README.md index d8fab1886..bbd601672 100644 --- a/humility-hif-assembler/README.md +++ b/humility-hif-assembler/README.md @@ -10,24 +10,30 @@ 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. -The text language provides syntactic sugar for common operations while -raw HIF instructions remain available for anything the sugar doesn't -cover. +## Quick Start -## Quick Start (planned CLI integration) +```bash +# Execute a program on a target over the network +humility -a sidecar-b-lab.zip --ip fe80::..%3 hiffy --exec stress.hif -The following commands are not yet implemented in `humility hiffy`. -They show the intended workflow once CLI integration lands. +# Same thing, with JSON output for scripting +humility -a sidecar-b-lab.zip --ip fe80::..%3 hiffy --exec stress.hif --json -```bash -# Assemble and verify a program against a Hubris archive -humility -a gimlet-c-dev.zip hiffy --verify stress.hif +# Execute via probe +humility -a grapefruit.zip -p usb-1 hiffy --exec stress.hif + +# Save the assembled bundle as a CI artifact +humility -a sidecar-b-lab.zip --ip fe80::..%3 hiffy --exec stress.hif --save-bundle stress.hifb +``` -# Assemble to a bundle file -humility -a gimlet-c-dev.zip hiffy --assemble stress.hif -o stress.hifb +Network execution requires the `hiffy` task to have the `net` +feature enabled in the Hubris image. If it's missing, the assembler +warns: -# Run on a target -humility -a gimlet-c-dev.zip -t c71 hiffy --run stress.hifb +``` +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) ``` ## Text Format @@ -53,23 +59,46 @@ i2c_scan mid i2c_regscan mid 0x48 ``` -Bus names (`mid`, `front`, `rear`, `m2`) come from the archive's -`app.toml`. Explicit `.` syntax (e.g. `3.H`) is -also accepted. +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 ``` -(Idol lowering is not yet implemented.) +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 500 +repeat 200 i2c_read mid 0x48 reg=0x00 2 end @@ -79,14 +108,19 @@ repeat 100 sleep=10ms 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 ITERATIONS 1000 +.let SENSOR 0x48 +.let ITERATIONS 200 repeat $ITERATIONS - i2c_read mid 0x48 reg=$TEMP_REG 2 + i2c_read rear $SENSOR reg=$TEMP_REG 2 end ``` @@ -100,8 +134,7 @@ Values over 100ms are automatically split into multiple Sleep calls. ### Raw Instructions -For anything the sugar doesn't cover. Constants are expanded inside -raw blocks. +For anything else. Constants are expanded inside raw blocks: ``` .let ADDR 0x48 @@ -111,14 +144,73 @@ raw { push 2 call I2cRead drop_n 7 - done } ``` -Available raw instructions: `push`, `push16`, `push32`, `push_none`, -`drop`, `drop_n`, `swap`, `add`, `label`, `branch_gt`, `branch_gte`, +Available: `push`, `push16`, `push32`, `push_none`, `drop`, +`drop_n`, `swap`, `add`, `label`, `branch_gt`, `branch_gte`, `branch_lt`, `call`, `done`. +## Output + +### Human output (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 @@ -127,23 +219,35 @@ 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("gimlet-c-dev.zip")?; +let config = TargetConfig::from_archive_file("sidecar-b-lab.zip")?; -// From a checked-in fixture +// From a checked-in fixture (test data only) let config: TargetConfig = - serde_json::from_str(&std::fs::read_to_string("fixtures/gimlet-c.json")?)?; + serde_json::from_str(&std::fs::read_to_string("fixtures/sidecar-b.json")?)?; let asm = HifAssembler::new(config); ``` -Pre-generated fixtures in `fixtures/` allow tests to run without -access to a Hubris archive or build environment. +## 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 @@ -155,8 +259,7 @@ cargo test -p humility-hif-assembler --lib cargo test -p humility-hif-assembler --test fixture_tests # Integration tests (requires a built archive) -HUBRIS_ARCHIVE=$(cd ~/Oxide/src/hubris/master && \ - cargo -q xtask print --archive app/gimlet/rev-c-dev.toml) \ +HUBRIS_ARCHIVE=path/to/archive.zip \ cargo test -p humility-hif-assembler --test archive_integration # Regenerate a fixture from an archive @@ -165,23 +268,39 @@ HUBRIS_ARCHIVE=path/to/archive.zip GENERATE_FIXTURE=1 \ generate_fixture -- --nocapture ``` -## Relationship to humility-hiffy and RFD 659 +## Hubris Image Requirements -This crate overlaps with code in `humility-hiffy`. The overlap is -intentional and designed for eventual convergence. +For network execution (`--ip`), the Hubris image must have: -### What overlaps +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 } +``` -| Capability | humility-hiffy | hif-assembler | -|---|---|---| -| DWARF function table discovery | `HiffyContext::new()` | `archive.rs extract_hiffy_functions()` | -| Function name/arg resolution | `HiffyFunction` + `get()` | `FunctionInfo` + alias table | -| Result decoding | `HiffyContext::results()` | `HifBundle::decode_results()` | -| I2C parameter resolution | `humility-i2c` `I2cArgs` | `ResolvedBus` + bus name map | -| Idol call construction | `idol_call_ops()` family | Not yet implemented | -| Program construction | Per-command Op building | Text parser + assembler | +The hiffy task's priority must be lower than the net task's priority +(higher number = lower priority) to avoid priority inversion. -### Why the duplication exists +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 @@ -189,36 +308,8 @@ used as a library for offline program construction, fixture generation, or scripted test drivers. This crate provides those capabilities without modifying `humility-hiffy`. -### Convergence plan (RFD 659) - -RFD 659 proposes turning humility into a library. When that happens: - -- **`TargetConfig`** replaces the ad-hoc archive introspection - scattered through `HiffyContext::new()`. It becomes the - serializable contract between archive loading and program - construction. - -- **`HifAssembler`** replaces the per-command Op construction in - `cmd/i2c`, `cmd/pmbus`, `cmd/gpio`, etc. Each command becomes a - thin wrapper that parses CLI args into a HIF text program (or uses - `ProgramBuilder`) and hands it to the assembler. - -- **`decode_results()`** becomes the shared result parser, replacing - the inline `take_from_bytes` loop in `HiffyContext::results()`. - -- **`HiffyContext`** narrows to execution only: uploading bytecode - to a target (via probe or NetHiffy), kicking the hiffy task, and - reading back results. It no longer needs to know how programs are - constructed. - -### Design discipline - -To keep convergence clean: - -- This crate uses only public `HubrisArchive` APIs, never - `HiffyContext` internals. -- Types are `Serialize + Deserialize` so they work as file formats - and API contracts. -- The text language is a superset of what humility commands generate - today — any program humility builds internally can be expressed in - the text format. +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/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 index 617c58618..c5b40d398 100644 --- a/humility-hif-assembler/fixtures/cosmo-b.json +++ b/humility-hif-assembler/fixtures/cosmo-b.json @@ -2911,7 +2911,10 @@ { "name": "get_state", "code": 1, + "args_size": 0, "reply": "u32", + "reply_size": 4, + "encoding": "Zerocopy", "idempotent": true }, { @@ -2923,19 +2926,28 @@ "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 }, { @@ -2947,14 +2959,20 @@ "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 }, { @@ -2966,15 +2984,21 @@ "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 }, { @@ -2986,8 +3010,11 @@ "ty": "u32" } ], + "args_size": 4, "reply": "u8", + "reply_size": 1, "error": "DumpAgentError", + "encoding": "Hubpack", "idempotent": false }, { @@ -3007,8 +3034,11 @@ "ty": "u32" } ], + "args_size": 12, "reply": "u8", + "reply_size": 1, "error": "DumpAgentError", + "encoding": "Hubpack", "idempotent": false }, { @@ -3020,20 +3050,29 @@ "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", @@ -3064,8 +3103,11 @@ "ty": "LargePayloadBehavior" } ], + "args_size": 8, "reply": "UdpMetadata", + "reply_size": 24, "error": "task_net_api::RecvError", + "encoding": "Hubpack", "leases": [ { "name": "payload", @@ -3089,8 +3131,11 @@ "ty": "UdpMetadata" } ], + "args_size": 28, "reply": "()", + "reply_size": 0, "error": "task_net_api::SendError", + "encoding": "Hubpack", "leases": [ { "name": "payload", @@ -3114,8 +3159,11 @@ "ty": "u8" } ], + "args_size": 2, "reply": "u16", + "reply_size": 2, "error": "ServerDeath", + "encoding": "Zerocopy", "idempotent": false }, { @@ -3135,8 +3183,11 @@ "ty": "u16" } ], + "args_size": 4, "reply": "()", + "reply_size": 0, "error": "ServerDeath", + "encoding": "Zerocopy", "idempotent": false }, { @@ -3156,8 +3207,11 @@ "ty": "u8" } ], + "args_size": 4, "reply": "u16", + "reply_size": 2, "error": "PhyError", + "encoding": "Zerocopy", "idempotent": false }, { @@ -3181,15 +3235,21 @@ "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 }, { @@ -3201,8 +3261,11 @@ "ty": "u16" } ], + "args_size": 2, "reply": "KszMacTableEntry", + "reply_size": 8, "error": "KszError", + "encoding": "Zerocopy", "idempotent": false }, { @@ -3214,34 +3277,49 @@ "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 }, { @@ -3257,8 +3335,11 @@ "ty": "u64" } ], + "args_size": 16, "reply": "()", + "reply_size": 0, "error": "task_net_api::TrustError", + "encoding": "Hubpack", "idempotent": false }, { @@ -3270,8 +3351,11 @@ "ty": "VLanId" } ], + "args_size": 1, "reply": "()", + "reply_size": 0, "error": "task_net_api::TrustError", + "encoding": "Hubpack", "idempotent": false } ] @@ -3290,8 +3374,11 @@ "ty": "u32" } ], + "args_size": 4, "reply": "()", + "reply_size": 0, "error": "RccError", + "encoding": "Zerocopy", "idempotent": true }, { @@ -3303,8 +3390,11 @@ "ty": "u32" } ], + "args_size": 4, "reply": "()", + "reply_size": 0, "error": "RccError", + "encoding": "Zerocopy", "idempotent": true }, { @@ -3316,8 +3406,11 @@ "ty": "u32" } ], + "args_size": 4, "reply": "()", + "reply_size": 0, "error": "RccError", + "encoding": "Zerocopy", "idempotent": true }, { @@ -3329,8 +3422,11 @@ "ty": "u32" } ], + "args_size": 4, "reply": "()", + "reply_size": 0, "error": "RccError", + "encoding": "Zerocopy", "idempotent": true }, { @@ -3350,7 +3446,10 @@ "ty": "u16" } ], + "args_size": 5, "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", "idempotent": true }, { @@ -3370,7 +3469,10 @@ "ty": "u16" } ], + "args_size": 5, "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", "idempotent": true }, { @@ -3382,7 +3484,10 @@ "ty": "Port" } ], + "args_size": 1, "reply": "u16", + "reply_size": 2, + "encoding": "Zerocopy", "idempotent": true }, { @@ -3398,14 +3503,20 @@ "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 }, { @@ -3421,7 +3532,10 @@ "ty": "Edge" } ], + "args_size": 5, "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", "idempotent": true }, { @@ -3437,8 +3551,11 @@ "ty": "IrqControl" } ], + "args_size": 5, "reply": "bool", + "reply_size": 1, "error": "ServerDeath", + "encoding": "Zerocopy", "idempotent": false } ] @@ -3457,8 +3574,11 @@ "ty": "u8" } ], + "args_size": 1, "reply": "()", + "reply_size": 0, "error": "drv_spi_api::SpiError", + "encoding": "Zerocopy", "leases": [ { "name": "sink", @@ -3478,8 +3598,11 @@ "ty": "u8" } ], + "args_size": 1, "reply": "()", + "reply_size": 0, "error": "drv_spi_api::SpiError", + "encoding": "Zerocopy", "leases": [ { "name": "source", @@ -3499,8 +3622,11 @@ "ty": "u8" } ], + "args_size": 1, "reply": "()", + "reply_size": 0, "error": "drv_spi_api::SpiError", + "encoding": "Zerocopy", "leases": [ { "name": "source", @@ -3530,15 +3656,21 @@ "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 } ] @@ -3557,8 +3689,11 @@ "ty": "u8" } ], + "args_size": 1, "reply": "()", + "reply_size": 0, "error": "drv_spi_api::SpiError", + "encoding": "Zerocopy", "leases": [ { "name": "sink", @@ -3578,8 +3713,11 @@ "ty": "u8" } ], + "args_size": 1, "reply": "()", + "reply_size": 0, "error": "drv_spi_api::SpiError", + "encoding": "Zerocopy", "leases": [ { "name": "source", @@ -3599,8 +3737,11 @@ "ty": "u8" } ], + "args_size": 1, "reply": "()", + "reply_size": 0, "error": "drv_spi_api::SpiError", + "encoding": "Zerocopy", "leases": [ { "name": "source", @@ -3630,15 +3771,21 @@ "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 } ] @@ -3651,8 +3798,11 @@ { "name": "get_mac_address_block", "code": 1, + "args_size": 0, "reply": "MacAddressBlock", + "reply_size": 9, "error": "CacheGetError", + "encoding": "Zerocopy", "idempotent": true }, { @@ -3664,15 +3814,21 @@ "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 }, { @@ -3684,14 +3840,20 @@ "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 }, { @@ -3703,7 +3865,10 @@ "ty": "HostStartupOptions" } ], + "args_size": 8, "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", "idempotent": true }, { @@ -3715,7 +3880,10 @@ "ty": "u8" } ], + "args_size": 1, "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", "idempotent": true }, { @@ -3731,7 +3899,10 @@ "ty": "usize" } ], + "args_size": 5, "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", "leases": [ { "name": "data", @@ -3751,7 +3922,10 @@ "ty": "u8" } ], + "args_size": 1, "reply": "bool", + "reply_size": 1, + "encoding": "Zerocopy", "idempotent": true }, { @@ -3767,7 +3941,10 @@ "ty": "usize" } ], + "args_size": 5, "reply": "u8", + "reply_size": 1, + "encoding": "Zerocopy", "idempotent": true }, { @@ -3779,7 +3956,10 @@ "ty": "u8" } ], + "args_size": 1, "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", "leases": [ { "name": "data", @@ -3799,15 +3979,21 @@ "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", @@ -3843,8 +4029,11 @@ "ty": "ereport_messages::Ena" } ], + "args_size": 34, "reply": "usize", + "reply_size": 4, "error": "EreportReadError", + "encoding": "Zerocopy", "leases": [ { "name": "data", @@ -3865,8 +4054,11 @@ { "name": "fill", "code": 1, + "args_size": 0, "reply": "usize", + "reply_size": 4, "error": "RngError", + "encoding": "Zerocopy", "leases": [ { "name": "source", @@ -3893,36 +4085,51 @@ "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 }, { @@ -3934,8 +4141,11 @@ "ty": "u8" } ], + "args_size": 1, "reply": "()", + "reply_size": 0, "error": "ThermalError", + "encoding": "Zerocopy", "idempotent": false }, { @@ -3959,15 +4169,21 @@ "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 }, { @@ -3979,8 +4195,11 @@ "ty": "f32" } ], + "args_size": 4, "reply": "()", + "reply_size": 0, "error": "ThermalError", + "encoding": "Zerocopy", "idempotent": false }, { @@ -3996,8 +4215,11 @@ "ty": "ThermalProperties" } ], + "args_size": 20, "reply": "()", + "reply_size": 0, "error": "ThermalError", + "encoding": "Zerocopy", "idempotent": false }, { @@ -4009,15 +4231,21 @@ "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 } ] @@ -4048,8 +4276,11 @@ "ty": "Operation" } ], + "args_size": 8, "reply": "PmbusValue", + "reply_size": 34, "error": "ResponseCode", + "encoding": "Hubpack", "idempotent": true }, { @@ -4069,8 +4300,11 @@ "ty": "u32" } ], + "args_size": 8, "reply": "u8", + "reply_size": 1, "error": "ResponseCode", + "encoding": "Hubpack", "idempotent": true }, { @@ -4086,8 +4320,11 @@ "ty": "u8" } ], + "args_size": 8, "reply": "f32", + "reply_size": 4, "error": "ResponseCode", + "encoding": "Hubpack", "idempotent": true }, { @@ -4099,29 +4336,41 @@ "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 }, { @@ -4133,8 +4382,11 @@ "ty": "u8" } ], + "args_size": 1, "reply": "RenesasBlackbox", + "reply_size": 177, "error": "ResponseCode", + "encoding": "Hubpack", "idempotent": true }, { @@ -4150,8 +4402,11 @@ "ty": "u16" } ], + "args_size": 3, "reply": "u32", + "reply_size": 4, "error": "ResponseCode", + "encoding": "Zerocopy", "idempotent": true }, { @@ -4171,8 +4426,11 @@ "ty": "u32" } ], + "args_size": 7, "reply": "()", + "reply_size": 0, "error": "ResponseCode", + "encoding": "Zerocopy", "idempotent": true }, { @@ -4192,8 +4450,11 @@ "ty": "u8" } ], + "args_size": 8, "reply": "u8", + "reply_size": 1, "error": "ResponseCode", + "encoding": "Hubpack", "idempotent": true }, { @@ -4213,8 +4474,11 @@ "ty": "u8" } ], + "args_size": 8, "reply": "u16", + "reply_size": 2, "error": "ResponseCode", + "encoding": "Hubpack", "idempotent": true }, { @@ -4234,8 +4498,11 @@ "ty": "u8" } ], + "args_size": 8, "reply": "u32", + "reply_size": 4, "error": "ResponseCode", + "encoding": "Hubpack", "idempotent": true }, { @@ -4255,8 +4522,11 @@ "ty": "u8" } ], + "args_size": 8, "reply": "RawPmbusBlock", + "reply_size": 33, "error": "ResponseCode", + "encoding": "Hubpack", "idempotent": true }, { @@ -4276,8 +4546,11 @@ "ty": "u8" } ], + "args_size": 8, "reply": "()", + "reply_size": 0, "error": "ResponseCode", + "encoding": "Hubpack", "idempotent": true }, { @@ -4301,8 +4574,11 @@ "ty": "u8" } ], + "args_size": 8, "reply": "()", + "reply_size": 0, "error": "ResponseCode", + "encoding": "Hubpack", "idempotent": true }, { @@ -4326,8 +4602,11 @@ "ty": "u16" } ], + "args_size": 8, "reply": "()", + "reply_size": 0, "error": "ResponseCode", + "encoding": "Hubpack", "idempotent": true }, { @@ -4351,8 +4630,11 @@ "ty": "u32" } ], + "args_size": 12, "reply": "()", + "reply_size": 0, "error": "ResponseCode", + "encoding": "Hubpack", "idempotent": true }, { @@ -4376,8 +4658,11 @@ "ty": "RawPmbusBlock" } ], + "args_size": 40, "reply": "()", + "reply_size": 0, "error": "ResponseCode", + "encoding": "Hubpack", "idempotent": true } ] @@ -4390,7 +4675,10 @@ { "name": "get_state", "code": 1, + "args_size": 0, "reply": "drv_cpu_power_state::PowerState", + "reply_size": 1, + "encoding": "Zerocopy", "idempotent": true }, { @@ -4402,8 +4690,11 @@ "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 }, { @@ -4419,33 +4710,48 @@ "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 }, { @@ -4457,31 +4763,46 @@ "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 } ] @@ -4494,22 +4815,31 @@ { "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 }, { @@ -4521,8 +4851,11 @@ "ty": "u32" } ], + "args_size": 4, "reply": "()", + "reply_size": 0, "error": "IgnitionFlashError", + "encoding": "Zerocopy", "leases": [ { "name": "data", @@ -4542,8 +4875,11 @@ "ty": "u32" } ], + "args_size": 4, "reply": "()", + "reply_size": 0, "error": "IgnitionFlashError", + "encoding": "Zerocopy", "leases": [ { "name": "data", @@ -4563,20 +4899,29 @@ "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 } ] @@ -4589,7 +4934,10 @@ { "name": "ping", "code": 1, + "args_size": 0, "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", "idempotent": true } ] @@ -4602,8 +4950,11 @@ { "name": "init_sha256", "code": 1, + "args_size": 0, "reply": "()", + "reply_size": 0, "error": "HashError", + "encoding": "Zerocopy", "idempotent": false }, { @@ -4615,8 +4966,11 @@ "ty": "u32" } ], + "args_size": 4, "reply": "()", + "reply_size": 0, "error": "HashError", + "encoding": "Zerocopy", "leases": [ { "name": "data", @@ -4630,8 +4984,11 @@ { "name": "finalize_sha256", "code": 3, + "args_size": 0, "reply": "[u8; crate::SHA256_SZ]", + "reply_size": 32, "error": "HashError", + "encoding": "Zerocopy", "idempotent": false }, { @@ -4643,8 +5000,11 @@ "ty": "u32" } ], + "args_size": 4, "reply": "[u8; crate::SHA256_SZ]", + "reply_size": 32, "error": "HashError", + "encoding": "Zerocopy", "leases": [ { "name": "data", @@ -4665,22 +5025,31 @@ { "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 }, { @@ -4692,8 +5061,11 @@ "ty": "HfProtectMode" } ], + "args_size": 1, "reply": "()", + "reply_size": 0, "error": "HfError", + "encoding": "Zerocopy", "idempotent": false }, { @@ -4709,8 +5081,11 @@ "ty": "HfProtectMode" } ], + "args_size": 5, "reply": "()", + "reply_size": 0, "error": "HfError", + "encoding": "Zerocopy", "leases": [ { "name": "data", @@ -4738,8 +5113,11 @@ "ty": "HfProtectMode" } ], + "args_size": 6, "reply": "()", + "reply_size": 0, "error": "HfError", + "encoding": "Zerocopy", "leases": [ { "name": "data", @@ -4759,8 +5137,11 @@ "ty": "u32" } ], + "args_size": 4, "reply": "()", + "reply_size": 0, "error": "HfError", + "encoding": "Zerocopy", "leases": [ { "name": "data", @@ -4784,8 +5165,11 @@ "ty": "u32" } ], + "args_size": 5, "reply": "()", + "reply_size": 0, "error": "HfError", + "encoding": "Zerocopy", "leases": [ { "name": "data", @@ -4809,8 +5193,11 @@ "ty": "HfProtectMode" } ], + "args_size": 5, "reply": "()", + "reply_size": 0, "error": "HfError", + "encoding": "Zerocopy", "idempotent": false }, { @@ -4830,15 +5217,21 @@ "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 }, { @@ -4850,15 +5243,21 @@ "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 }, { @@ -4870,8 +5269,11 @@ "ty": "HfDevSelect" } ], + "args_size": 1, "reply": "()", + "reply_size": 0, "error": "HfError", + "encoding": "Zerocopy", "idempotent": false }, { @@ -4883,8 +5285,11 @@ "ty": "HfDevSelect" } ], + "args_size": 1, "reply": "()", + "reply_size": 0, "error": "HfError", + "encoding": "Zerocopy", "idempotent": false }, { @@ -4900,8 +5305,11 @@ "ty": "u32" } ], + "args_size": 8, "reply": "[u8; drv_hash_api::SHA256_SZ]", + "reply_size": 32, "error": "HfError", + "encoding": "Zerocopy", "idempotent": false }, { @@ -4913,8 +5321,11 @@ "ty": "HfDevSelect" } ], + "args_size": 1, "reply": "()", + "reply_size": 0, "error": "HfError", + "encoding": "Zerocopy", "idempotent": false }, { @@ -4926,15 +5337,21 @@ "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 }, { @@ -4946,8 +5363,11 @@ "ty": "HfDevSelect" } ], + "args_size": 1, "reply": "()", + "reply_size": 0, "error": "HfError", + "encoding": "Zerocopy", "idempotent": false }, { @@ -4963,8 +5383,11 @@ "ty": "ApobHash" } ], + "args_size": 40, "reply": "()", + "reply_size": 0, "error": "ApobBeginError", + "encoding": "Hubpack", "idempotent": true }, { @@ -4976,8 +5399,11 @@ "ty": "u32" } ], + "args_size": 4, "reply": "()", + "reply_size": 0, "error": "ApobWriteError", + "encoding": "Zerocopy", "leases": [ { "name": "data", @@ -4991,14 +5417,20 @@ { "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 }, { @@ -5010,8 +5442,11 @@ "ty": "u32" } ], + "args_size": 4, "reply": "usize", + "reply_size": 4, "error": "ApobReadError", + "encoding": "Zerocopy", "leases": [ { "name": "data", @@ -5025,8 +5460,11 @@ { "name": "apob_clear", "code": 26, + "args_size": 0, "reply": "()", + "reply_size": 0, "error": "ApobClearError", + "encoding": "Zerocopy", "idempotent": true } ] @@ -5039,15 +5477,21 @@ { "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 }, { @@ -5059,8 +5503,11 @@ "ty": "usize" } ], + "args_size": 4, "reply": "()", + "reply_size": 0, "error": "drv_update_api::UpdateError", + "encoding": "Zerocopy", "leases": [ { "name": "block", @@ -5074,21 +5521,30 @@ { "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 }, { @@ -5100,8 +5556,11 @@ "ty": "[u8; 4]" } ], + "args_size": 4, "reply": "u32", + "reply_size": 4, "error": "CabooseError", + "encoding": "Zerocopy", "leases": [ { "name": "data", @@ -5115,7 +5574,10 @@ { "name": "get_pending_boot_slot", "code": 8, + "args_size": 0, "reply": "SlotId", + "reply_size": 1, + "encoding": "Hubpack", "idempotent": true }, { @@ -5127,8 +5589,11 @@ "ty": "SlotId" } ], + "args_size": 1, "reply": "()", + "reply_size": 0, "error": "drv_update_api::UpdateError", + "encoding": "Hubpack", "idempotent": false } ] @@ -5147,8 +5612,11 @@ "ty": "SensorId" } ], + "args_size": 4, "reply": "f32", + "reply_size": 4, "error": "SensorError", + "encoding": "Hubpack", "idempotent": true }, { @@ -5160,8 +5628,11 @@ "ty": "SensorId" } ], + "args_size": 4, "reply": "Reading", + "reply_size": 12, "error": "SensorError", + "encoding": "Hubpack", "idempotent": true }, { @@ -5173,7 +5644,10 @@ "ty": "SensorId" } ], + "args_size": 4, "reply": "Option<(Result, u64)>", + "reply_size": 0, + "encoding": "Hubpack", "idempotent": true }, { @@ -5185,7 +5659,10 @@ "ty": "SensorId" } ], + "args_size": 4, "reply": "Option<(f32, u64)>", + "reply_size": 13, + "encoding": "Hubpack", "idempotent": true }, { @@ -5197,7 +5674,10 @@ "ty": "SensorId" } ], + "args_size": 4, "reply": "Option<(NoData, u64)>", + "reply_size": 0, + "encoding": "Hubpack", "idempotent": true }, { @@ -5209,7 +5689,10 @@ "ty": "SensorId" } ], + "args_size": 4, "reply": "(f32, u64)", + "reply_size": 12, + "encoding": "Hubpack", "idempotent": true }, { @@ -5221,7 +5704,10 @@ "ty": "SensorId" } ], + "args_size": 4, "reply": "(f32, u64)", + "reply_size": 12, + "encoding": "Hubpack", "idempotent": true }, { @@ -5241,7 +5727,10 @@ "ty": "u64" } ], + "args_size": 16, "reply": "()", + "reply_size": 0, + "encoding": "Hubpack", "idempotent": true }, { @@ -5261,7 +5750,10 @@ "ty": "u64" } ], + "args_size": 16, "reply": "()", + "reply_size": 0, + "encoding": "Hubpack", "idempotent": true }, { @@ -5273,7 +5765,10 @@ "ty": "SensorId" } ], + "args_size": 4, "reply": "u32", + "reply_size": 4, + "encoding": "Hubpack", "idempotent": true } ] @@ -5292,15 +5787,21 @@ "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 } ] @@ -5327,8 +5828,11 @@ "ty": "u8" } ], + "args_size": 41, "reply": "()", + "reply_size": 0, "error": "ControlPlaneAgentError", + "encoding": "Zerocopy", "idempotent": false }, { @@ -5344,8 +5848,11 @@ "ty": "u64" } ], + "args_size": 40, "reply": "usize", + "reply_size": 4, "error": "ControlPlaneAgentError", + "encoding": "Zerocopy", "leases": [ { "name": "data", @@ -5359,8 +5866,11 @@ { "name": "get_startup_options", "code": 3, + "args_size": 0, "reply": "HostStartupOptions", + "reply_size": 8, "error": "ControlPlaneAgentError", + "encoding": "Ssmarshal", "idempotent": false }, { @@ -5372,26 +5882,38 @@ "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", @@ -5411,15 +5933,21 @@ "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", @@ -5433,8 +5961,11 @@ { "name": "uart_write", "code": 10, + "args_size": 0, "reply": "usize", + "reply_size": 4, "error": "ControlPlaneAgentError", + "encoding": "Zerocopy", "leases": [ { "name": "data", @@ -5455,22 +5986,31 @@ { "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 }, { @@ -5482,15 +6022,21 @@ "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 }, { @@ -5502,8 +6048,11 @@ "ty": "UpdateTarget" } ], + "args_size": 1, "reply": "()", + "reply_size": 0, "error": "SprotError", + "encoding": "Hubpack", "idempotent": true }, { @@ -5515,8 +6064,11 @@ "ty": "u32" } ], + "args_size": 4, "reply": "()", + "reply_size": 0, "error": "SprotError", + "encoding": "Hubpack", "leases": [ { "name": "block", @@ -5530,15 +6082,21 @@ { "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 }, { @@ -5554,15 +6112,21 @@ "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 }, { @@ -5574,8 +6138,11 @@ "ty": "u32" } ], + "args_size": 4, "reply": "()", + "reply_size": 0, "error": "DumpOrSprotError", + "encoding": "Hubpack", "idempotent": true }, { @@ -5587,8 +6154,11 @@ "ty": "SlotId" } ], + "args_size": 1, "reply": "u32", + "reply_size": 4, "error": "RawCabooseOrSprotError", + "encoding": "Hubpack", "idempotent": true }, { @@ -5604,8 +6174,11 @@ "ty": "SlotId" } ], + "args_size": 8, "reply": "()", + "reply_size": 0, "error": "RawCabooseOrSprotError", + "encoding": "Hubpack", "leases": [ { "name": "out", @@ -5619,15 +6192,21 @@ { "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 }, { @@ -5639,8 +6218,11 @@ "ty": "u32" } ], + "args_size": 4, "reply": "u32", + "reply_size": 4, "error": "AttestOrSprotError", + "encoding": "Hubpack", "idempotent": true }, { @@ -5656,8 +6238,11 @@ "ty": "u32" } ], + "args_size": 8, "reply": "()", + "reply_size": 0, "error": "AttestOrSprotError", + "encoding": "Hubpack", "leases": [ { "name": "dest", @@ -5677,8 +6262,11 @@ "ty": "HashAlgorithm" } ], + "args_size": 0, "reply": "()", + "reply_size": 0, "error": "AttestOrSprotError", + "encoding": "Hubpack", "leases": [ { "name": "data", @@ -5698,8 +6286,11 @@ "ty": "RotPage" } ], + "args_size": 1, "reply": "()", + "reply_size": 0, "error": "SprotError", + "encoding": "Hubpack", "leases": [ { "name": "dest", @@ -5719,8 +6310,11 @@ "ty": "u32" } ], + "args_size": 4, "reply": "()", + "reply_size": 0, "error": "AttestOrSprotError", + "encoding": "Hubpack", "leases": [ { "name": "dest", @@ -5734,15 +6328,21 @@ { "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", @@ -5762,8 +6362,11 @@ { "name": "attest_len", "code": 24, + "args_size": 0, "reply": "u32", + "reply_size": 4, "error": "AttestOrSprotError", + "encoding": "Hubpack", "idempotent": true }, { @@ -5775,22 +6378,31 @@ "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 }, { @@ -5802,8 +6414,11 @@ "ty": "u8" } ], + "args_size": 1, "reply": "VersionedRotBootInfo", + "reply_size": 147, "error": "SprotError", + "encoding": "Hubpack", "idempotent": true }, { @@ -5819,8 +6434,11 @@ "ty": "SlotId" } ], + "args_size": 2, "reply": "u32", + "reply_size": 4, "error": "RawCabooseOrSprotError", + "encoding": "Hubpack", "idempotent": true }, { @@ -5840,8 +6458,11 @@ "ty": "SlotId" } ], + "args_size": 8, "reply": "()", + "reply_size": 0, "error": "RawCabooseOrSprotError", + "encoding": "Hubpack", "leases": [ { "name": "out", @@ -5865,8 +6486,11 @@ "ty": "SlotId" } ], + "args_size": 2, "reply": "()", + "reply_size": 0, "error": "SprotError", + "encoding": "Hubpack", "idempotent": true }, { @@ -5886,22 +6510,31 @@ "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 }, { @@ -5913,8 +6546,11 @@ "ty": "u32" } ], + "args_size": 4, "reply": "u32", + "reply_size": 4, "error": "AttestOrSprotError", + "encoding": "Hubpack", "idempotent": true }, { @@ -5930,8 +6566,11 @@ "ty": "u32" } ], + "args_size": 8, "reply": "()", + "reply_size": 0, "error": "AttestOrSprotError", + "encoding": "Hubpack", "leases": [ { "name": "dest", @@ -5945,8 +6584,11 @@ { "name": "tq_sign", "code": 37, + "args_size": 0, "reply": "()", + "reply_size": 0, "error": "AttestOrSprotError", + "encoding": "Hubpack", "leases": [ { "name": "tq", @@ -5966,8 +6608,11 @@ { "name": "tq_sign_len", "code": 38, + "args_size": 0, "reply": "u32", + "reply_size": 4, "error": "AttestOrSprotError", + "encoding": "Hubpack", "idempotent": true } ] @@ -5986,8 +6631,11 @@ "ty": "u32" } ], + "args_size": 4, "reply": "ValidateOk", + "reply_size": 1, "error": "ValidateError", + "encoding": "Zerocopy", "idempotent": true } ] @@ -6006,8 +6654,11 @@ "ty": "u8" } ], + "args_size": 1, "reply": "[u8; 6]", + "reply_size": 6, "error": "VpdError", + "encoding": "Zerocopy", "idempotent": false }, { @@ -6023,8 +6674,11 @@ "ty": "u16" } ], + "args_size": 3, "reply": "[u8; 16]", + "reply_size": 16, "error": "VpdError", + "encoding": "Zerocopy", "idempotent": false }, { @@ -6044,8 +6698,11 @@ "ty": "u8" } ], + "args_size": 4, "reply": "()", + "reply_size": 0, "error": "VpdError", + "encoding": "Zerocopy", "idempotent": false }, { @@ -6057,8 +6714,11 @@ "ty": "u8" } ], + "args_size": 1, "reply": "bool", + "reply_size": 1, "error": "VpdError", + "encoding": "Zerocopy", "idempotent": false }, { @@ -6070,14 +6730,20 @@ "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 } ] @@ -6096,8 +6762,11 @@ "ty": "usize" } ], + "args_size": 4, "reply": "()", + "reply_size": 0, "error": "LedError", + "encoding": "Zerocopy", "idempotent": true }, { @@ -6109,8 +6778,11 @@ "ty": "usize" } ], + "args_size": 4, "reply": "()", + "reply_size": 0, "error": "LedError", + "encoding": "Zerocopy", "idempotent": true }, { @@ -6122,8 +6794,11 @@ "ty": "usize" } ], + "args_size": 4, "reply": "()", + "reply_size": 0, "error": "LedError", + "encoding": "Zerocopy", "idempotent": true }, { @@ -6135,8 +6810,11 @@ "ty": "usize" } ], + "args_size": 4, "reply": "()", + "reply_size": 0, "error": "LedError", + "encoding": "Zerocopy", "idempotent": true } ] @@ -6159,8 +6837,11 @@ "ty": "u32" } ], + "args_size": 5, "reply": "[u8; DUMP_READ_SIZE]", + "reply_size": 256, "error": "DumpAgentError", + "encoding": "Zerocopy", "idempotent": false }, { @@ -6176,8 +6857,11 @@ "ty": "u32" } ], + "args_size": 5, "reply": "usize", + "reply_size": 4, "error": "DumpAgentError", + "encoding": "Zerocopy", "leases": [ { "name": "out", @@ -6197,15 +6881,21 @@ "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 }, { @@ -6221,15 +6911,21 @@ "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 }, { @@ -6241,8 +6937,11 @@ "ty": "u32" } ], + "args_size": 4, "reply": "u8", + "reply_size": 1, "error": "DumpAgentError", + "encoding": "Zerocopy", "idempotent": false }, { @@ -6262,8 +6961,11 @@ "ty": "u32" } ], + "args_size": 12, "reply": "u8", + "reply_size": 1, "error": "DumpAgentError", + "encoding": "Zerocopy", "idempotent": false }, { @@ -6275,8 +6977,11 @@ "ty": "u8" } ], + "args_size": 1, "reply": "()", + "reply_size": 0, "error": "DumpAgentError", + "encoding": "Hubpack", "idempotent": false } ] @@ -6289,7 +6994,10 @@ { "name": "ping", "code": 1, + "args_size": 0, "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", "idempotent": true } ] @@ -6302,29 +7010,41 @@ { "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 }, { @@ -6336,8 +7056,11 @@ "ty": "u32" } ], + "args_size": 4, "reply": "()", + "reply_size": 0, "error": "AuxFlashError", + "encoding": "Zerocopy", "idempotent": false }, { @@ -6353,8 +7076,11 @@ "ty": "u32" } ], + "args_size": 8, "reply": "()", + "reply_size": 0, "error": "AuxFlashError", + "encoding": "Zerocopy", "idempotent": false }, { @@ -6366,8 +7092,11 @@ "ty": "u32" } ], + "args_size": 4, "reply": "AuxFlashChecksum", + "reply_size": 32, "error": "AuxFlashError", + "encoding": "Zerocopy", "idempotent": false }, { @@ -6383,8 +7112,11 @@ "ty": "u32" } ], + "args_size": 8, "reply": "()", + "reply_size": 0, "error": "AuxFlashError", + "encoding": "Zerocopy", "leases": [ { "name": "data", @@ -6408,8 +7140,11 @@ "ty": "u32" } ], + "args_size": 8, "reply": "()", + "reply_size": 0, "error": "AuxFlashError", + "encoding": "Zerocopy", "leases": [ { "name": "data", @@ -6423,22 +7158,31 @@ { "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 }, { @@ -6450,8 +7194,11 @@ "ty": "[u8; 4]" } ], + "args_size": 4, "reply": "AuxFlashBlob", + "reply_size": 12, "error": "AuxFlashError", + "encoding": "Zerocopy", "idempotent": false } ] @@ -6470,7 +7217,10 @@ "ty": "u32" } ], + "args_size": 4, "reply": "u16", + "reply_size": 2, + "encoding": "Hubpack", "idempotent": false }, { @@ -6482,7 +7232,10 @@ "ty": "u32" } ], + "args_size": 4, "reply": "u32", + "reply_size": 4, + "encoding": "Hubpack", "idempotent": false }, { @@ -6494,7 +7247,10 @@ "ty": "u32" } ], + "args_size": 4, "reply": "u64", + "reply_size": 8, + "encoding": "Hubpack", "idempotent": false }, { @@ -6510,7 +7266,10 @@ "ty": "u16" } ], + "args_size": 8, "reply": "()", + "reply_size": 0, + "encoding": "Hubpack", "idempotent": false }, { @@ -6526,7 +7285,10 @@ "ty": "u32" } ], + "args_size": 8, "reply": "()", + "reply_size": 0, + "encoding": "Hubpack", "idempotent": false }, { @@ -6542,7 +7304,10 @@ "ty": "u64" } ], + "args_size": 16, "reply": "()", + "reply_size": 0, + "encoding": "Hubpack", "idempotent": false }, { @@ -6554,7 +7319,10 @@ "ty": "bool" } ], + "args_size": 1, "reply": "()", + "reply_size": 0, + "encoding": "Hubpack", "idempotent": false }, { @@ -6566,7 +7334,10 @@ "ty": "bool" } ], + "args_size": 1, "reply": "()", + "reply_size": 0, + "encoding": "Hubpack", "idempotent": false }, { @@ -6578,7 +7349,10 @@ "ty": "bool" } ], + "args_size": 1, "reply": "()", + "reply_size": 0, + "encoding": "Hubpack", "idempotent": false }, { @@ -6590,7 +7364,10 @@ "ty": "bool" } ], + "args_size": 1, "reply": "()", + "reply_size": 0, + "encoding": "Hubpack", "idempotent": false }, { @@ -6602,7 +7379,10 @@ "ty": "u8" } ], + "args_size": 1, "reply": "()", + "reply_size": 0, + "encoding": "Hubpack", "idempotent": false }, { @@ -6614,7 +7394,10 @@ "ty": "u8" } ], + "args_size": 1, "reply": "()", + "reply_size": 0, + "encoding": "Hubpack", "idempotent": false }, { @@ -6626,7 +7409,10 @@ "ty": "u8" } ], + "args_size": 1, "reply": "()", + "reply_size": 0, + "encoding": "Hubpack", "idempotent": false } ] diff --git a/humility-hif-assembler/fixtures/gimlet-c.json b/humility-hif-assembler/fixtures/gimlet-c.json index e9939d2db..8c91dc6a9 100644 --- a/humility-hif-assembler/fixtures/gimlet-c.json +++ b/humility-hif-assembler/fixtures/gimlet-c.json @@ -3048,7 +3048,10 @@ { "name": "get_state", "code": 1, + "args_size": 0, "reply": "u32", + "reply_size": 4, + "encoding": "Zerocopy", "idempotent": true }, { @@ -3060,19 +3063,28 @@ "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 }, { @@ -3084,14 +3096,20 @@ "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 }, { @@ -3103,15 +3121,21 @@ "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 }, { @@ -3123,8 +3147,11 @@ "ty": "u32" } ], + "args_size": 4, "reply": "u8", + "reply_size": 1, "error": "DumpAgentError", + "encoding": "Hubpack", "idempotent": false }, { @@ -3144,8 +3171,11 @@ "ty": "u32" } ], + "args_size": 12, "reply": "u8", + "reply_size": 1, "error": "DumpAgentError", + "encoding": "Hubpack", "idempotent": false }, { @@ -3157,20 +3187,29 @@ "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", @@ -3201,8 +3240,11 @@ "ty": "LargePayloadBehavior" } ], + "args_size": 8, "reply": "UdpMetadata", + "reply_size": 24, "error": "task_net_api::RecvError", + "encoding": "Hubpack", "leases": [ { "name": "payload", @@ -3226,8 +3268,11 @@ "ty": "UdpMetadata" } ], + "args_size": 28, "reply": "()", + "reply_size": 0, "error": "task_net_api::SendError", + "encoding": "Hubpack", "leases": [ { "name": "payload", @@ -3251,8 +3296,11 @@ "ty": "u8" } ], + "args_size": 2, "reply": "u16", + "reply_size": 2, "error": "ServerDeath", + "encoding": "Zerocopy", "idempotent": false }, { @@ -3272,8 +3320,11 @@ "ty": "u16" } ], + "args_size": 4, "reply": "()", + "reply_size": 0, "error": "ServerDeath", + "encoding": "Zerocopy", "idempotent": false }, { @@ -3293,8 +3344,11 @@ "ty": "u8" } ], + "args_size": 4, "reply": "u16", + "reply_size": 2, "error": "PhyError", + "encoding": "Zerocopy", "idempotent": false }, { @@ -3318,15 +3372,21 @@ "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 }, { @@ -3338,8 +3398,11 @@ "ty": "u16" } ], + "args_size": 2, "reply": "KszMacTableEntry", + "reply_size": 8, "error": "KszError", + "encoding": "Zerocopy", "idempotent": false }, { @@ -3351,34 +3414,49 @@ "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 }, { @@ -3394,8 +3472,11 @@ "ty": "u64" } ], + "args_size": 16, "reply": "()", + "reply_size": 0, "error": "task_net_api::TrustError", + "encoding": "Hubpack", "idempotent": false }, { @@ -3407,8 +3488,11 @@ "ty": "VLanId" } ], + "args_size": 1, "reply": "()", + "reply_size": 0, "error": "task_net_api::TrustError", + "encoding": "Hubpack", "idempotent": false } ] @@ -3427,8 +3511,11 @@ "ty": "u32" } ], + "args_size": 4, "reply": "()", + "reply_size": 0, "error": "RccError", + "encoding": "Zerocopy", "idempotent": true }, { @@ -3440,8 +3527,11 @@ "ty": "u32" } ], + "args_size": 4, "reply": "()", + "reply_size": 0, "error": "RccError", + "encoding": "Zerocopy", "idempotent": true }, { @@ -3453,8 +3543,11 @@ "ty": "u32" } ], + "args_size": 4, "reply": "()", + "reply_size": 0, "error": "RccError", + "encoding": "Zerocopy", "idempotent": true }, { @@ -3466,8 +3559,11 @@ "ty": "u32" } ], + "args_size": 4, "reply": "()", + "reply_size": 0, "error": "RccError", + "encoding": "Zerocopy", "idempotent": true }, { @@ -3487,7 +3583,10 @@ "ty": "u16" } ], + "args_size": 5, "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", "idempotent": true }, { @@ -3507,7 +3606,10 @@ "ty": "u16" } ], + "args_size": 5, "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", "idempotent": true }, { @@ -3519,7 +3621,10 @@ "ty": "Port" } ], + "args_size": 1, "reply": "u16", + "reply_size": 2, + "encoding": "Zerocopy", "idempotent": true }, { @@ -3535,14 +3640,20 @@ "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 }, { @@ -3558,7 +3669,10 @@ "ty": "Edge" } ], + "args_size": 5, "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", "idempotent": true }, { @@ -3574,8 +3688,11 @@ "ty": "IrqControl" } ], + "args_size": 5, "reply": "bool", + "reply_size": 1, "error": "ServerDeath", + "encoding": "Zerocopy", "idempotent": false } ] @@ -3594,8 +3711,11 @@ "ty": "u8" } ], + "args_size": 1, "reply": "()", + "reply_size": 0, "error": "drv_spi_api::SpiError", + "encoding": "Zerocopy", "leases": [ { "name": "sink", @@ -3615,8 +3735,11 @@ "ty": "u8" } ], + "args_size": 1, "reply": "()", + "reply_size": 0, "error": "drv_spi_api::SpiError", + "encoding": "Zerocopy", "leases": [ { "name": "source", @@ -3636,8 +3759,11 @@ "ty": "u8" } ], + "args_size": 1, "reply": "()", + "reply_size": 0, "error": "drv_spi_api::SpiError", + "encoding": "Zerocopy", "leases": [ { "name": "source", @@ -3667,15 +3793,21 @@ "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 } ] @@ -3688,8 +3820,11 @@ { "name": "get_mac_address_block", "code": 1, + "args_size": 0, "reply": "MacAddressBlock", + "reply_size": 9, "error": "CacheGetError", + "encoding": "Zerocopy", "idempotent": true }, { @@ -3701,15 +3836,21 @@ "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 }, { @@ -3721,14 +3862,20 @@ "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 }, { @@ -3740,7 +3887,10 @@ "ty": "HostStartupOptions" } ], + "args_size": 8, "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", "idempotent": true }, { @@ -3752,7 +3902,10 @@ "ty": "u8" } ], + "args_size": 1, "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", "idempotent": true }, { @@ -3768,7 +3921,10 @@ "ty": "usize" } ], + "args_size": 5, "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", "leases": [ { "name": "data", @@ -3788,7 +3944,10 @@ "ty": "u8" } ], + "args_size": 1, "reply": "bool", + "reply_size": 1, + "encoding": "Zerocopy", "idempotent": true }, { @@ -3804,7 +3963,10 @@ "ty": "usize" } ], + "args_size": 5, "reply": "u8", + "reply_size": 1, + "encoding": "Zerocopy", "idempotent": true }, { @@ -3816,7 +3978,10 @@ "ty": "u8" } ], + "args_size": 1, "reply": "()", + "reply_size": 0, + "encoding": "Zerocopy", "leases": [ { "name": "data", @@ -3836,15 +4001,21 @@ "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", @@ -3880,8 +4051,11 @@ "ty": "ereport_messages::Ena" } ], + "args_size": 34, "reply": "usize", + "reply_size": 4, "error": "EreportReadError", + "encoding": "Zerocopy", "leases": [ { "name": "data", @@ -3908,36 +4082,51 @@ "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 }, { @@ -3949,8 +4138,11 @@ "ty": "u8" } ], + "args_size": 1, "reply": "()", + "reply_size": 0, "error": "ThermalError", + "encoding": "Zerocopy", "idempotent": false }, { @@ -3974,15 +4166,21 @@ "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 }, { @@ -3994,8 +4192,11 @@ "ty": "f32" } ], + "args_size": 4, "reply": "()", + "reply_size": 0, "error": "ThermalError", + "encoding": "Zerocopy", "idempotent": false }, { @@ -4011,8 +4212,11 @@ "ty": "ThermalProperties" } ], + "args_size": 20, "reply": "()", + "reply_size": 0, "error": "ThermalError", + "encoding": "Zerocopy", "idempotent": false }, { @@ -4024,15 +4228,21 @@ "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 } ] @@ -4063,8 +4273,11 @@ "ty": "Operation" } ], + "args_size": 8, "reply": "PmbusValue", + "reply_size": 34, "error": "ResponseCode", + "encoding": "Hubpack", "idempotent": true }, { @@ -4084,8 +4297,11 @@ "ty": "u32" } ], + "args_size": 8, "reply": "u8", + "reply_size": 1, "error": "ResponseCode", + "encoding": "Hubpack", "idempotent": true }, { @@ -4101,8 +4317,11 @@ "ty": "u8" } ], + "args_size": 8, "reply": "f32", + "reply_size": 4, "error": "ResponseCode", + "encoding": "Hubpack", "idempotent": true }, { @@ -4114,29 +4333,41 @@ "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 }, { @@ -4148,8 +4379,11 @@ "ty": "u8" } ], + "args_size": 1, "reply": "RenesasBlackbox", + "reply_size": 177, "error": "ResponseCode", + "encoding": "Hubpack", "idempotent": true }, { @@ -4165,8 +4399,11 @@ "ty": "u16" } ], + "args_size": 3, "reply": "u32", + "reply_size": 4, "error": "ResponseCode", + "encoding": "Zerocopy", "idempotent": true }, { @@ -4186,8 +4423,11 @@ "ty": "u32" } ], + "args_size": 7, "reply": "()", + "reply_size": 0, "error": "ResponseCode", + "encoding": "Zerocopy", "idempotent": true }, { @@ -4207,8 +4447,11 @@ "ty": "u8" } ], + "args_size": 8, "reply": "u8", + "reply_size": 1, "error": "ResponseCode", + "encoding": "Hubpack", "idempotent": true }, { @@ -4228,8 +4471,11 @@ "ty": "u8" } ], + "args_size": 8, "reply": "u16", + "reply_size": 2, "error": "ResponseCode", + "encoding": "Hubpack", "idempotent": true }, { @@ -4249,8 +4495,11 @@ "ty": "u8" } ], + "args_size": 8, "reply": "u32", + "reply_size": 4, "error": "ResponseCode", + "encoding": "Hubpack", "idempotent": true }, { @@ -4270,8 +4519,11 @@ "ty": "u8" } ], + "args_size": 8, "reply": "RawPmbusBlock", + "reply_size": 33, "error": "ResponseCode", + "encoding": "Hubpack", "idempotent": true }, { @@ -4291,8 +4543,11 @@ "ty": "u8" } ], + "args_size": 8, "reply": "()", + "reply_size": 0, "error": "ResponseCode", + "encoding": "Hubpack", "idempotent": true }, { @@ -4316,8 +4571,11 @@ "ty": "u8" } ], + "args_size": 8, "reply": "()", + "reply_size": 0, "error": "ResponseCode", + "encoding": "Hubpack", "idempotent": true }, { @@ -4341,8 +4599,11 @@ "ty": "u16" } ], + "args_size": 8, "reply": "()", + "reply_size": 0, "error": "ResponseCode", + "encoding": "Hubpack", "idempotent": true }, { @@ -4366,8 +4627,11 @@ "ty": "u32" } ], + "args_size": 12, "reply": "()", + "reply_size": 0, "error": "ResponseCode", + "encoding": "Hubpack", "idempotent": true }, { @@ -4391,8 +4655,11 @@ "ty": "RawPmbusBlock" } ], + "args_size": 40, "reply": "()", + "reply_size": 0, "error": "ResponseCode", + "encoding": "Hubpack", "idempotent": true } ] @@ -4405,7 +4672,10 @@ { "name": "get_state", "code": 1, + "args_size": 0, "reply": "drv_cpu_power_state::PowerState", + "reply_size": 1, + "encoding": "Zerocopy", "idempotent": true }, { @@ -4417,8 +4687,11 @@ "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 }, { @@ -4434,33 +4707,48 @@ "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 }, { @@ -4472,31 +4760,46 @@ "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 } ] @@ -4509,8 +4812,11 @@ { "name": "init_sha256", "code": 1, + "args_size": 0, "reply": "()", + "reply_size": 0, "error": "HashError", + "encoding": "Zerocopy", "idempotent": false }, { @@ -4522,8 +4828,11 @@ "ty": "u32" } ], + "args_size": 4, "reply": "()", + "reply_size": 0, "error": "HashError", + "encoding": "Zerocopy", "leases": [ { "name": "data", @@ -4537,8 +4846,11 @@ { "name": "finalize_sha256", "code": 3, + "args_size": 0, "reply": "[u8; crate::SHA256_SZ]", + "reply_size": 32, "error": "HashError", + "encoding": "Zerocopy", "idempotent": false }, { @@ -4550,8 +4862,11 @@ "ty": "u32" } ], + "args_size": 4, "reply": "[u8; crate::SHA256_SZ]", + "reply_size": 32, "error": "HashError", + "encoding": "Zerocopy", "leases": [ { "name": "data", @@ -4572,8 +4887,11 @@ { "name": "fill", "code": 1, + "args_size": 0, "reply": "usize", + "reply_size": 4, "error": "RngError", + "encoding": "Zerocopy", "leases": [ { "name": "source", @@ -4594,22 +4912,31 @@ { "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 }, { @@ -4621,8 +4948,11 @@ "ty": "HfProtectMode" } ], + "args_size": 1, "reply": "()", + "reply_size": 0, "error": "HfError", + "encoding": "Zerocopy", "idempotent": false }, { @@ -4638,8 +4968,11 @@ "ty": "HfProtectMode" } ], + "args_size": 5, "reply": "()", + "reply_size": 0, "error": "HfError", + "encoding": "Zerocopy", "leases": [ { "name": "data", @@ -4667,8 +5000,11 @@ "ty": "HfProtectMode" } ], + "args_size": 6, "reply": "()", + "reply_size": 0, "error": "HfError", + "encoding": "Zerocopy", "leases": [ { "name": "data", @@ -4688,8 +5024,11 @@ "ty": "u32" } ], + "args_size": 4, "reply": "()", + "reply_size": 0, "error": "HfError", + "encoding": "Zerocopy", "leases": [ { "name": "data", @@ -4713,8 +5052,11 @@ "ty": "u32" } ], + "args_size": 5, "reply": "()", + "reply_size": 0, "error": "HfError", + "encoding": "Zerocopy", "leases": [ { "name": "data", @@ -4738,8 +5080,11 @@ "ty": "HfProtectMode" } ], + "args_size": 5, "reply": "()", + "reply_size": 0, "error": "HfError", + "encoding": "Zerocopy", "idempotent": false }, { @@ -4759,15 +5104,21 @@ "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 }, { @@ -4779,15 +5130,21 @@ "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 }, { @@ -4799,8 +5156,11 @@ "ty": "HfDevSelect" } ], + "args_size": 1, "reply": "()", + "reply_size": 0, "error": "HfError", + "encoding": "Zerocopy", "idempotent": false }, { @@ -4812,8 +5172,11 @@ "ty": "HfDevSelect" } ], + "args_size": 1, "reply": "()", + "reply_size": 0, "error": "HfError", + "encoding": "Zerocopy", "idempotent": false }, { @@ -4829,8 +5192,11 @@ "ty": "u32" } ], + "args_size": 8, "reply": "[u8; drv_hash_api::SHA256_SZ]", + "reply_size": 32, "error": "HfError", + "encoding": "Zerocopy", "idempotent": false }, { @@ -4842,8 +5208,11 @@ "ty": "HfDevSelect" } ], + "args_size": 1, "reply": "()", + "reply_size": 0, "error": "HfError", + "encoding": "Zerocopy", "idempotent": false }, { @@ -4855,15 +5224,21 @@ "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 }, { @@ -4875,8 +5250,11 @@ "ty": "HfDevSelect" } ], + "args_size": 1, "reply": "()", + "reply_size": 0, "error": "HfError", + "encoding": "Zerocopy", "idempotent": false }, { @@ -4892,8 +5270,11 @@ "ty": "ApobHash" } ], + "args_size": 40, "reply": "()", + "reply_size": 0, "error": "ApobBeginError", + "encoding": "Hubpack", "idempotent": true }, { @@ -4905,8 +5286,11 @@ "ty": "u32" } ], + "args_size": 4, "reply": "()", + "reply_size": 0, "error": "ApobWriteError", + "encoding": "Zerocopy", "leases": [ { "name": "data", @@ -4920,14 +5304,20 @@ { "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 }, { @@ -4939,8 +5329,11 @@ "ty": "u32" } ], + "args_size": 4, "reply": "usize", + "reply_size": 4, "error": "ApobReadError", + "encoding": "Zerocopy", "leases": [ { "name": "data", @@ -4954,8 +5347,11 @@ { "name": "apob_clear", "code": 26, + "args_size": 0, "reply": "()", + "reply_size": 0, "error": "ApobClearError", + "encoding": "Zerocopy", "idempotent": true } ] @@ -4968,15 +5364,21 @@ { "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 }, { @@ -4988,8 +5390,11 @@ "ty": "usize" } ], + "args_size": 4, "reply": "()", + "reply_size": 0, "error": "drv_update_api::UpdateError", + "encoding": "Zerocopy", "leases": [ { "name": "block", @@ -5003,21 +5408,30 @@ { "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 }, { @@ -5029,8 +5443,11 @@ "ty": "[u8; 4]" } ], + "args_size": 4, "reply": "u32", + "reply_size": 4, "error": "CabooseError", + "encoding": "Zerocopy", "leases": [ { "name": "data", @@ -5044,7 +5461,10 @@ { "name": "get_pending_boot_slot", "code": 8, + "args_size": 0, "reply": "SlotId", + "reply_size": 1, + "encoding": "Hubpack", "idempotent": true }, { @@ -5056,8 +5476,11 @@ "ty": "SlotId" } ], + "args_size": 1, "reply": "()", + "reply_size": 0, "error": "drv_update_api::UpdateError", + "encoding": "Hubpack", "idempotent": false } ] @@ -5076,8 +5499,11 @@ "ty": "SensorId" } ], + "args_size": 4, "reply": "f32", + "reply_size": 4, "error": "SensorError", + "encoding": "Hubpack", "idempotent": true }, { @@ -5089,8 +5515,11 @@ "ty": "SensorId" } ], + "args_size": 4, "reply": "Reading", + "reply_size": 12, "error": "SensorError", + "encoding": "Hubpack", "idempotent": true }, { @@ -5102,7 +5531,10 @@ "ty": "SensorId" } ], + "args_size": 4, "reply": "Option<(Result, u64)>", + "reply_size": 0, + "encoding": "Hubpack", "idempotent": true }, { @@ -5114,7 +5546,10 @@ "ty": "SensorId" } ], + "args_size": 4, "reply": "Option<(f32, u64)>", + "reply_size": 13, + "encoding": "Hubpack", "idempotent": true }, { @@ -5126,7 +5561,10 @@ "ty": "SensorId" } ], + "args_size": 4, "reply": "Option<(NoData, u64)>", + "reply_size": 0, + "encoding": "Hubpack", "idempotent": true }, { @@ -5138,7 +5576,10 @@ "ty": "SensorId" } ], + "args_size": 4, "reply": "(f32, u64)", + "reply_size": 12, + "encoding": "Hubpack", "idempotent": true }, { @@ -5150,7 +5591,10 @@ "ty": "SensorId" } ], + "args_size": 4, "reply": "(f32, u64)", + "reply_size": 12, + "encoding": "Hubpack", "idempotent": true }, { @@ -5170,7 +5614,10 @@ "ty": "u64" } ], + "args_size": 16, "reply": "()", + "reply_size": 0, + "encoding": "Hubpack", "idempotent": true }, { @@ -5190,7 +5637,10 @@ "ty": "u64" } ], + "args_size": 16, "reply": "()", + "reply_size": 0, + "encoding": "Hubpack", "idempotent": true }, { @@ -5202,7 +5652,10 @@ "ty": "SensorId" } ], + "args_size": 4, "reply": "u32", + "reply_size": 4, + "encoding": "Hubpack", "idempotent": true } ] @@ -5221,15 +5674,21 @@ "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 } ] @@ -5256,8 +5715,11 @@ "ty": "u8" } ], + "args_size": 41, "reply": "()", + "reply_size": 0, "error": "ControlPlaneAgentError", + "encoding": "Zerocopy", "idempotent": false }, { @@ -5273,8 +5735,11 @@ "ty": "u64" } ], + "args_size": 40, "reply": "usize", + "reply_size": 4, "error": "ControlPlaneAgentError", + "encoding": "Zerocopy", "leases": [ { "name": "data", @@ -5288,8 +5753,11 @@ { "name": "get_startup_options", "code": 3, + "args_size": 0, "reply": "HostStartupOptions", + "reply_size": 8, "error": "ControlPlaneAgentError", + "encoding": "Ssmarshal", "idempotent": false }, { @@ -5301,26 +5769,38 @@ "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", @@ -5340,15 +5820,21 @@ "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", @@ -5362,8 +5848,11 @@ { "name": "uart_write", "code": 10, + "args_size": 0, "reply": "usize", + "reply_size": 4, "error": "ControlPlaneAgentError", + "encoding": "Zerocopy", "leases": [ { "name": "data", @@ -5384,22 +5873,31 @@ { "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 }, { @@ -5411,15 +5909,21 @@ "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 }, { @@ -5431,8 +5935,11 @@ "ty": "UpdateTarget" } ], + "args_size": 1, "reply": "()", + "reply_size": 0, "error": "SprotError", + "encoding": "Hubpack", "idempotent": true }, { @@ -5444,8 +5951,11 @@ "ty": "u32" } ], + "args_size": 4, "reply": "()", + "reply_size": 0, "error": "SprotError", + "encoding": "Hubpack", "leases": [ { "name": "block", @@ -5459,15 +5969,21 @@ { "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 }, { @@ -5483,15 +5999,21 @@ "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 }, { @@ -5503,8 +6025,11 @@ "ty": "u32" } ], + "args_size": 4, "reply": "()", + "reply_size": 0, "error": "DumpOrSprotError", + "encoding": "Hubpack", "idempotent": true }, { @@ -5516,8 +6041,11 @@ "ty": "SlotId" } ], + "args_size": 1, "reply": "u32", + "reply_size": 4, "error": "RawCabooseOrSprotError", + "encoding": "Hubpack", "idempotent": true }, { @@ -5533,8 +6061,11 @@ "ty": "SlotId" } ], + "args_size": 8, "reply": "()", + "reply_size": 0, "error": "RawCabooseOrSprotError", + "encoding": "Hubpack", "leases": [ { "name": "out", @@ -5548,15 +6079,21 @@ { "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 }, { @@ -5568,8 +6105,11 @@ "ty": "u32" } ], + "args_size": 4, "reply": "u32", + "reply_size": 4, "error": "AttestOrSprotError", + "encoding": "Hubpack", "idempotent": true }, { @@ -5585,8 +6125,11 @@ "ty": "u32" } ], + "args_size": 8, "reply": "()", + "reply_size": 0, "error": "AttestOrSprotError", + "encoding": "Hubpack", "leases": [ { "name": "dest", @@ -5606,8 +6149,11 @@ "ty": "HashAlgorithm" } ], + "args_size": 0, "reply": "()", + "reply_size": 0, "error": "AttestOrSprotError", + "encoding": "Hubpack", "leases": [ { "name": "data", @@ -5627,8 +6173,11 @@ "ty": "RotPage" } ], + "args_size": 1, "reply": "()", + "reply_size": 0, "error": "SprotError", + "encoding": "Hubpack", "leases": [ { "name": "dest", @@ -5648,8 +6197,11 @@ "ty": "u32" } ], + "args_size": 4, "reply": "()", + "reply_size": 0, "error": "AttestOrSprotError", + "encoding": "Hubpack", "leases": [ { "name": "dest", @@ -5663,15 +6215,21 @@ { "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", @@ -5691,8 +6249,11 @@ { "name": "attest_len", "code": 24, + "args_size": 0, "reply": "u32", + "reply_size": 4, "error": "AttestOrSprotError", + "encoding": "Hubpack", "idempotent": true }, { @@ -5704,22 +6265,31 @@ "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 }, { @@ -5731,8 +6301,11 @@ "ty": "u8" } ], + "args_size": 1, "reply": "VersionedRotBootInfo", + "reply_size": 147, "error": "SprotError", + "encoding": "Hubpack", "idempotent": true }, { @@ -5748,8 +6321,11 @@ "ty": "SlotId" } ], + "args_size": 2, "reply": "u32", + "reply_size": 4, "error": "RawCabooseOrSprotError", + "encoding": "Hubpack", "idempotent": true }, { @@ -5769,8 +6345,11 @@ "ty": "SlotId" } ], + "args_size": 8, "reply": "()", + "reply_size": 0, "error": "RawCabooseOrSprotError", + "encoding": "Hubpack", "leases": [ { "name": "out", @@ -5794,8 +6373,11 @@ "ty": "SlotId" } ], + "args_size": 2, "reply": "()", + "reply_size": 0, "error": "SprotError", + "encoding": "Hubpack", "idempotent": true }, { @@ -5815,22 +6397,31 @@ "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 }, { @@ -5842,8 +6433,11 @@ "ty": "u32" } ], + "args_size": 4, "reply": "u32", + "reply_size": 4, "error": "AttestOrSprotError", + "encoding": "Hubpack", "idempotent": true }, { @@ -5859,8 +6453,11 @@ "ty": "u32" } ], + "args_size": 8, "reply": "()", + "reply_size": 0, "error": "AttestOrSprotError", + "encoding": "Hubpack", "leases": [ { "name": "dest", @@ -5874,8 +6471,11 @@ { "name": "tq_sign", "code": 37, + "args_size": 0, "reply": "()", + "reply_size": 0, "error": "AttestOrSprotError", + "encoding": "Hubpack", "leases": [ { "name": "tq", @@ -5895,8 +6495,11 @@ { "name": "tq_sign_len", "code": 38, + "args_size": 0, "reply": "u32", + "reply_size": 4, "error": "AttestOrSprotError", + "encoding": "Hubpack", "idempotent": true } ] @@ -5915,8 +6518,11 @@ "ty": "u32" } ], + "args_size": 4, "reply": "ValidateOk", + "reply_size": 1, "error": "ValidateError", + "encoding": "Zerocopy", "idempotent": true } ] @@ -5935,8 +6541,11 @@ "ty": "u8" } ], + "args_size": 1, "reply": "[u8; 6]", + "reply_size": 6, "error": "VpdError", + "encoding": "Zerocopy", "idempotent": false }, { @@ -5952,8 +6561,11 @@ "ty": "u16" } ], + "args_size": 3, "reply": "[u8; 16]", + "reply_size": 16, "error": "VpdError", + "encoding": "Zerocopy", "idempotent": false }, { @@ -5973,8 +6585,11 @@ "ty": "u8" } ], + "args_size": 4, "reply": "()", + "reply_size": 0, "error": "VpdError", + "encoding": "Zerocopy", "idempotent": false }, { @@ -5986,8 +6601,11 @@ "ty": "u8" } ], + "args_size": 1, "reply": "bool", + "reply_size": 1, "error": "VpdError", + "encoding": "Zerocopy", "idempotent": false }, { @@ -5999,14 +6617,20 @@ "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 } ] @@ -6025,8 +6649,11 @@ "ty": "usize" } ], + "args_size": 4, "reply": "()", + "reply_size": 0, "error": "LedError", + "encoding": "Zerocopy", "idempotent": true }, { @@ -6038,8 +6665,11 @@ "ty": "usize" } ], + "args_size": 4, "reply": "()", + "reply_size": 0, "error": "LedError", + "encoding": "Zerocopy", "idempotent": true }, { @@ -6051,8 +6681,11 @@ "ty": "usize" } ], + "args_size": 4, "reply": "()", + "reply_size": 0, "error": "LedError", + "encoding": "Zerocopy", "idempotent": true }, { @@ -6064,8 +6697,11 @@ "ty": "usize" } ], + "args_size": 4, "reply": "()", + "reply_size": 0, "error": "LedError", + "encoding": "Zerocopy", "idempotent": true } ] @@ -6088,8 +6724,11 @@ "ty": "u32" } ], + "args_size": 5, "reply": "[u8; DUMP_READ_SIZE]", + "reply_size": 256, "error": "DumpAgentError", + "encoding": "Zerocopy", "idempotent": false }, { @@ -6105,8 +6744,11 @@ "ty": "u32" } ], + "args_size": 5, "reply": "usize", + "reply_size": 4, "error": "DumpAgentError", + "encoding": "Zerocopy", "leases": [ { "name": "out", @@ -6126,15 +6768,21 @@ "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 }, { @@ -6150,15 +6798,21 @@ "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 }, { @@ -6170,8 +6824,11 @@ "ty": "u32" } ], + "args_size": 4, "reply": "u8", + "reply_size": 1, "error": "DumpAgentError", + "encoding": "Zerocopy", "idempotent": false }, { @@ -6191,8 +6848,11 @@ "ty": "u32" } ], + "args_size": 12, "reply": "u8", + "reply_size": 1, "error": "DumpAgentError", + "encoding": "Zerocopy", "idempotent": false }, { @@ -6204,8 +6864,11 @@ "ty": "u8" } ], + "args_size": 1, "reply": "()", + "reply_size": 0, "error": "DumpAgentError", + "encoding": "Hubpack", "idempotent": false } ] @@ -6218,22 +6881,31 @@ { "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 }, { @@ -6253,8 +6925,11 @@ "ty": "u32" } ], + "args_size": 9, "reply": "[u32; 4]", + "reply_size": 16, "error": "SbrmiError", + "encoding": "Zerocopy", "idempotent": true }, { @@ -6270,8 +6945,11 @@ "ty": "u32" } ], + "args_size": 5, "reply": "u8", + "reply_size": 1, "error": "SbrmiError", + "encoding": "Zerocopy", "idempotent": true }, { @@ -6287,8 +6965,11 @@ "ty": "u32" } ], + "args_size": 5, "reply": "u16", + "reply_size": 2, "error": "SbrmiError", + "encoding": "Zerocopy", "idempotent": true }, { @@ -6304,8 +6985,11 @@ "ty": "u32" } ], + "args_size": 5, "reply": "u32", + "reply_size": 4, "error": "SbrmiError", + "encoding": "Zerocopy", "idempotent": true }, { @@ -6321,8 +7005,11 @@ "ty": "u32" } ], + "args_size": 5, "reply": "u64", + "reply_size": 8, "error": "SbrmiError", + "encoding": "Zerocopy", "idempotent": true } ] 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 index 4a9a092fc..393364c73 100644 --- a/humility-hif-assembler/src/archive.rs +++ b/humility-hif-assembler/src/archive.rs @@ -17,7 +17,7 @@ use std::path::Path; -use anyhow::{Context, Result, anyhow}; +use anyhow::{anyhow, Context, Result}; use humility::hubris::{ HubrisArchive, HubrisArchiveDoneness, HubrisEncoding, HubrisGoff, @@ -57,7 +57,32 @@ impl TargetConfig { /// 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(); @@ -414,12 +439,33 @@ fn extract_idol_interfaces(hubris: &HubrisArchive) -> Vec { }) .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, }); @@ -436,6 +482,78 @@ fn extract_idol_interfaces(hubris: &HubrisArchive) -> Vec { 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 diff --git a/humility-hif-assembler/src/assembler.rs b/humility-hif-assembler/src/assembler.rs index 0cc1326d1..8ea0eff35 100644 --- a/humility-hif-assembler/src/assembler.rs +++ b/humility-hif-assembler/src/assembler.rs @@ -11,11 +11,11 @@ use std::collections::{BTreeSet, HashMap}; use std::fmt; -use anyhow::{Context, Result, bail}; +use anyhow::{bail, Context, Result}; -use crate::bundle::{BUNDLE_VERSION, BundleMetadata, HifBundle}; +use crate::bundle::{BundleMetadata, HifBundle, BUNDLE_VERSION}; use crate::error::{HifError, HifErrorKind}; -use crate::lower::{RSTACK_BYTES_PER_RESULT, normalize_function_name}; +use crate::lower::{normalize_function_name, RSTACK_BYTES_PER_RESULT}; use crate::parser::parse; // Re-export types so `crate::assembler::Foo` still works for @@ -39,7 +39,12 @@ pub struct HifAssembler { #[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. @@ -312,9 +317,11 @@ impl HifAssembler { 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, warnings: result.warnings }) + Ok(AssembleOutput { bundle, ops, warnings: result.warnings, stats }) } } @@ -423,8 +430,11 @@ mod tests { 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, }, @@ -445,8 +455,11 @@ mod tests { ty: "u64".into(), }, ], + args_size: 16, reply: "()".into(), + reply_size: 0, error: Some("SensorError".into()), + encoding: "Hubpack".into(), leases: vec![], idempotent: false, }, @@ -464,14 +477,13 @@ mod tests { 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.len() > 0); + 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()) - ); + assert!(out + .bundle + .metadata + .functions_used + .contains(&"i2c_read".to_string())); } #[test] @@ -605,12 +617,10 @@ mod tests { 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 { .. })) - ); + assert!(report + .errors + .iter() + .any(|e| matches!(&e.kind, HifErrorKind::TextOverflow { .. }))); } #[test] @@ -641,9 +651,11 @@ mod tests { 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()) - ); + assert!(out + .bundle + .metadata + .functions_used + .contains(&"Send".to_string())); } #[test] 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 index 7b88717c5..ca018037c 100644 --- a/humility-hif-assembler/src/bundle.rs +++ b/humility-hif-assembler/src/bundle.rs @@ -16,7 +16,7 @@ //! line followed by a newline, then the raw binary text and data //! sections. -use anyhow::{Context, Result, bail}; +use anyhow::{bail, Context, Result}; use hif::FunctionResult; use postcard::take_from_bytes; use serde::{Deserialize, Serialize}; diff --git a/humility-hif-assembler/src/lib.rs b/humility-hif-assembler/src/lib.rs index 177d3fcf4..871b97104 100644 --- a/humility-hif-assembler/src/lib.rs +++ b/humility-hif-assembler/src/lib.rs @@ -74,12 +74,12 @@ //! //! ```text //! # Repeat a block N times -//! repeat 500 +//! repeat 200 //! i2c_read mid 0x48 reg=0x00 2 //! end //! //! # With a sleep between iterations -//! repeat 100 sleep=10ms +//! repeat 50 sleep=10ms //! i2c_read mid 0x48 reg=0x00 2 //! i2c_read mid 0x49 reg=0x00 2 //! end @@ -92,7 +92,7 @@ //! //! ```text //! .let TEMP_REG 0x00 -//! .let ITERATIONS 1000 +//! .let ITERATIONS 200 //! //! repeat $ITERATIONS //! i2c_read mid 0x48 reg=$TEMP_REG 2 @@ -105,34 +105,45 @@ //! 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 anything the sugar doesn't cover, raw HIF instructions are -//! available: +//! For low-level control, raw HIF instructions are available. +//! Constants are expanded inside raw blocks: //! //! ```text //! raw { //! push 0x48 //! push_none //! push 2 -//! call i2c_read +//! call I2cRead //! drop_n 7 -//! done //! } //! ``` //! -//! ## Compilation +//! ## Assembly //! //! ```rust,ignore -//! let asm = HifAssembler::from_archive(&archive)?; +//! 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")?; +//! let report = asm.verify("repeat 100\n i2c_read mid 0x48 reg=0x00 2\nend"); //! println!("{}", report); //! //! // Assemble to a bundle -//! let bundle = asm.assemble("i2c_read mid 0x48 reg=0x00 2")?; -//! bundle.write_to_file("program.hifb")?; +//! let output = asm.assemble("i2c_read mid 0x48 reg=0x00 2")?; +//! output.bundle.write_to_file("program.hifb")?; +//! println!("{}", output.stats); //! ``` //! //! ## Building Programs Programmatically @@ -141,16 +152,12 @@ //! fuzzing), use [`ProgramBuilder`] instead of text: //! //! ```rust,ignore -//! // ProgramBuilder API -- not yet implemented -//! let asm = HifAssembler::from_archive(&archive)?; -//! let mut prog = asm.builder(); -//! -//! prog.repeat(500, |body| { -//! body.i2c_read("mid", 0x48, Some(0x00), 2)?; -//! Ok(()) -//! })?; -//! -//! let bundle = prog.assemble()?; +//! 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 @@ -180,20 +187,23 @@ 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, + BufferSizes, FunctionArg, FunctionError, FunctionInfo, I2cDeviceInfo, + I2cMuxInfo, I2cMuxSegment, IdolArgInfo, IdolInterfaceInfo, IdolLeaseInfo, + IdolOpInfo, ResolvedBus, SensorInfo, TargetConfig, I2C_WRITE_MAX_DATA, + MAX_LABELS, }; diff --git a/humility-hif-assembler/src/lower.rs b/humility-hif-assembler/src/lower.rs index 37b353578..e364c38a0 100644 --- a/humility-hif-assembler/src/lower.rs +++ b/humility-hif-assembler/src/lower.rs @@ -10,7 +10,7 @@ use std::collections::BTreeSet; -use anyhow::{Context, Result, bail}; +use anyhow::{bail, Context, Result}; use hif::TargetFunction; use crate::assembler::HifAssembler; @@ -204,6 +204,24 @@ impl HifAssembler { 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()); @@ -221,9 +239,25 @@ impl HifAssembler { 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(*count)); + 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)?; @@ -247,7 +281,8 @@ impl HifAssembler { } result.ops.push(push_smallest(1)); - result.ops.push(hif::Op::Swap); + 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 } => { @@ -343,13 +378,15 @@ impl HifAssembler { } result.ops.push(push_smallest(payload.len() as u32)); - // TODO: Add reply_size to IdolOpInfo from DWARF - result.warnings.push(format!( - "line {line}: {interface}.{operation} reply size \ - estimated as 0; results may be truncated for \ - large reply types" - )); - result.ops.push(push_smallest(0)); + 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)); diff --git a/humility-hif-assembler/src/parser.rs b/humility-hif-assembler/src/parser.rs index d30c7cc71..115d9ddd6 100644 --- a/humility-hif-assembler/src/parser.rs +++ b/humility-hif-assembler/src/parser.rs @@ -112,6 +112,11 @@ pub enum Statement { sleep_ms: Option, body: Vec>, }, + /// Call any HIF function by name with optional numeric arguments. + Call { + function: String, + args: Vec, + }, Raw { lines: Vec, }, @@ -249,6 +254,10 @@ fn parse_statement( *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, @@ -494,6 +503,25 @@ fn parse_sleep(tokens: &[&str], line: usize) -> Result { 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], @@ -848,4 +876,34 @@ mod tests { other => panic!("expected Repeat, 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..543cb69f9 --- /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 index 78a19e011..cb2f50307 100644 --- a/humility-hif-assembler/src/types.rs +++ b/humility-hif-assembler/src/types.rs @@ -174,11 +174,20 @@ pub struct IdolOpInfo { /// 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, @@ -196,6 +205,10 @@ pub struct IdolArgInfo { 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 { From 56f115970e37df7d15372cd41bfb1026c958c57c Mon Sep 17 00:00:00 2001 From: Ben Stoltz Date: Wed, 8 Apr 2026 13:59:13 -0700 Subject: [PATCH 4/6] Add offline --verify/--assemble and equivalent program tests The hiffy command now supports offline operations that don't require a target connection. --verify shows program stats and disassembled ops. --assemble produces a .hifb bundle file. Existing online operations (--exec, --call, -L) detect the need for a target and attach on demand. Changed hiffy's CommandKind from Attached to Unattached so offline operations bypass target attachment. Online operations give a clear error if no probe or IP is specified. Added 10 equivalent .hif examples that reproduce humility's hardcoded HIF programs (qspi, i2c, gpio, hash, idol, validate) and 18 tests covering both text and ProgramBuilder assembly. Fixed block comment style to match the project convention. --- cmd/hiffy/src/lib.rs | 154 ++++- .../counters-arg.ouray.34.fails.stderr | 2 +- .../counters-csv-full.ouray.34.fails.stderr | 2 +- .../counters-csv.ouray.34.fails.stderr | 2 +- .../counters-full.ouray.34.fails.stderr | 2 +- ...ounters-ipc-filtered.ouray.34.fails.stderr | 2 +- .../counters-ipc-full.ouray.34.fails.stderr | 2 +- .../counters-ipc.ouray.34.fails.stderr | 2 +- .../counters-json.ouray.34.fails.stderr | 2 +- .../counters-list.ouray.34.fails.stderr | 2 +- .../counters/counters.ouray.34.fails.stderr | 2 +- .../hiffy-list.flash-ram-mismatch.0.stderr | 1 - .../hiffy-list.flash-ram-mismatch.0.stdout | 341 +++++++++++ .../hiffy-list.flash-ram-mismatch.0.toml | 2 +- .../hiffy-list.ouray.34.fails.stderr | 2 +- .../cmd/hiffy-list/hiffy-list.ouray.55.stderr | 4 - .../cmd/hiffy-list/hiffy-list.ouray.55.toml | 2 +- .../cmd/hiffy-list/hiffy-list.ouray.61.stderr | 4 - .../cmd/hiffy-list/hiffy-list.ouray.61.toml | 2 +- .../cmd/hiffy-list/hiffy-list.ouray.62.stderr | 4 - .../cmd/hiffy-list/hiffy-list.ouray.62.toml | 2 +- .../hiffy-list.panic-on-boot.stderr | 1 - .../hiffy-list.panic-on-boot.stdout | 561 ++++++++++++++++++ .../hiffy-list/hiffy-list.panic-on-boot.toml | 2 +- .../host-panic.ouray.34.fails.stderr | 2 +- .../manifest/manifest.ouray.34.fails.stderr | 2 +- .../tests/cmd/map/map.ouray.34.fails.stderr | 2 +- .../readvar-list.ouray.34.fails.stderr | 2 +- .../readvar-ticks.ouray.34.fails.stderr | 2 +- .../ringbuf-arg.ouray.34.fails.stderr | 2 +- .../ringbuf-full-totals.ouray.34.fails.stderr | 2 +- .../cmd/ringbuf/ringbuf.ouray.34.fails.stderr | 2 +- .../sensors-read.ouray.34.fails.stderr | 2 +- .../cmd/sensors/sensors.ouray.34.fails.stderr | 2 +- .../tests/cmd/spd/spd.ouray.34.fails.stderr | 2 +- .../stackmargin.ouray.34.fails.stderr | 2 +- .../tasks-slvr.ouray.34.fails.stderr | 2 +- .../cmd/tasks/tasks.ouray.34.fails.stderr | 2 +- humility-hif-assembler/README.md | 81 ++- .../examples/equivalent-gpio-input.hif | 3 + .../examples/equivalent-gpio-toggle.hif | 3 + .../examples/equivalent-hash-digest.hif | 3 + .../examples/equivalent-i2c-read.hif | 2 + .../examples/equivalent-i2c-regscan.hif | 3 + .../examples/equivalent-i2c-scan.hif | 3 + .../examples/equivalent-idol-sensor-get.hif | 2 + .../examples/equivalent-qspi-read-id.hif | 3 + .../examples/equivalent-qspi-read-status.hif | 3 + .../examples/equivalent-validate-i2c.hif | 6 + humility-hif-assembler/src/archive.rs | 2 +- humility-hif-assembler/src/assembler.rs | 35 +- humility-hif-assembler/src/bundle.rs | 2 +- humility-hif-assembler/src/lib.rs | 16 +- humility-hif-assembler/src/listing.rs | 85 +++ humility-hif-assembler/src/lower.rs | 4 +- humility-hif-assembler/src/stats.rs | 6 +- humility-hif-assembler/src/types.rs | 7 + .../tests/equivalent_tests.rs | 294 +++++++++ 58 files changed, 1585 insertions(+), 108 deletions(-) create mode 100644 humility-hif-assembler/examples/equivalent-gpio-input.hif create mode 100644 humility-hif-assembler/examples/equivalent-gpio-toggle.hif create mode 100644 humility-hif-assembler/examples/equivalent-hash-digest.hif create mode 100644 humility-hif-assembler/examples/equivalent-i2c-read.hif create mode 100644 humility-hif-assembler/examples/equivalent-i2c-regscan.hif create mode 100644 humility-hif-assembler/examples/equivalent-i2c-scan.hif create mode 100644 humility-hif-assembler/examples/equivalent-idol-sensor-get.hif create mode 100644 humility-hif-assembler/examples/equivalent-qspi-read-id.hif create mode 100644 humility-hif-assembler/examples/equivalent-qspi-read-status.hif create mode 100644 humility-hif-assembler/examples/equivalent-validate-i2c.hif create mode 100644 humility-hif-assembler/tests/equivalent_tests.rs diff --git a/cmd/hiffy/src/lib.rs b/cmd/hiffy/src/lib.rs index bd74a94e6..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/. @@ -52,7 +53,7 @@ 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::*; @@ -106,14 +107,35 @@ struct HiffyArgs { #[clap(long, short, use_value_delimiter = true, requires = "call")] arguments: Vec, - /// execute a .hif or .hifb program file + /// execute a .hif or .hifb program file on a target #[clap( long, - conflicts_with_all = &["list", "listfuncs", "call"], + 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, @@ -219,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() { @@ -245,14 +276,25 @@ 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 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"); } @@ -289,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 { @@ -380,6 +422,76 @@ 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, @@ -718,10 +830,6 @@ pub fn init() -> 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-hif-assembler/README.md b/humility-hif-assembler/README.md index bbd601672..c45a3cf45 100644 --- a/humility-hif-assembler/README.md +++ b/humility-hif-assembler/README.md @@ -13,22 +13,31 @@ bundles with embedded image IDs for safe upload. ## Quick Start ```bash -# Execute a program on a target over the network -humility -a sidecar-b-lab.zip --ip fe80::..%3 hiffy --exec stress.hif +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 -# Same thing, with JSON output for scripting -humility -a sidecar-b-lab.zip --ip fe80::..%3 hiffy --exec stress.hif --json +# Unlock the sidecar's network interface +faux-mgs --interface axf2 \ + --discovery-addr "[${SP_IP%\%*}]:11111" \ + monorail unlock -t 3600sec -# Execute via probe -humility -a grapefruit.zip -p usb-1 hiffy --exec stress.hif +# Execute a program on a target over the network +humility -a $ARCHIVE --ip $SP_IP hiffy --exec stress.hif -# Save the assembled bundle as a CI artifact -humility -a sidecar-b-lab.zip --ip fe80::..%3 hiffy --exec stress.hif --save-bundle stress.hifb +# JSON output for scripting +humility -a $ARCHIVE --ip $SP_IP hiffy --exec stress.hif --json ``` -Network execution requires the `hiffy` task to have the `net` -feature enabled in the Hubris image. If it's missing, the assembler -warns: +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 @@ -36,6 +45,10 @@ 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. @@ -153,7 +166,51 @@ Available: `push`, `push16`, `push32`, `push_none`, `drop`, ## Output -### Human output (default) +### 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 (right +column), so it can be pasted into a `raw {}` block and re-assembled +to produce the same bytecode. Function IDs are resolved back to +names from the archive. + +``` +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 + drop # 05: 02 + push 1 # 06: 04 01 + push 1 # 08: 04 01 + push_none # 0a: 07 + push_none # 0b: 07 + push 0x48 # 0c: 04 48 + push 0 # 0e: 04 00 + push 2 # 10: 04 02 + call I2cRead # 12: 01 05 + drop_n 7 # 14: 03 07 + push 1 # 16: 04 01 + add # 18: 0a + push 5 # 19: 04 05 + branch_gt 0 # 1b: 10 00 + done # 1d: 14 +} +``` + +### Execution output (`--exec`, default) ``` humility: program: stress.hif 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/src/archive.rs b/humility-hif-assembler/src/archive.rs index 393364c73..ec3189606 100644 --- a/humility-hif-assembler/src/archive.rs +++ b/humility-hif-assembler/src/archive.rs @@ -17,7 +17,7 @@ use std::path::Path; -use anyhow::{anyhow, Context, Result}; +use anyhow::{Context, Result, anyhow}; use humility::hubris::{ HubrisArchive, HubrisArchiveDoneness, HubrisEncoding, HubrisGoff, diff --git a/humility-hif-assembler/src/assembler.rs b/humility-hif-assembler/src/assembler.rs index 8ea0eff35..25593219c 100644 --- a/humility-hif-assembler/src/assembler.rs +++ b/humility-hif-assembler/src/assembler.rs @@ -11,11 +11,11 @@ use std::collections::{BTreeSet, HashMap}; use std::fmt; -use anyhow::{bail, Context, Result}; +use anyhow::{Context, Result, bail}; -use crate::bundle::{BundleMetadata, HifBundle, BUNDLE_VERSION}; +use crate::bundle::{BUNDLE_VERSION, BundleMetadata, HifBundle}; use crate::error::{HifError, HifErrorKind}; -use crate::lower::{normalize_function_name, RSTACK_BYTES_PER_RESULT}; +use crate::lower::{RSTACK_BYTES_PER_RESULT, normalize_function_name}; use crate::parser::parse; // Re-export types so `crate::assembler::Foo` still works for @@ -479,11 +479,12 @@ mod tests { 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())); + assert!( + out.bundle + .metadata + .functions_used + .contains(&"i2c_read".to_string()) + ); } #[test] @@ -617,10 +618,12 @@ mod tests { 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 { .. }))); + assert!( + report + .errors + .iter() + .any(|e| matches!(&e.kind, HifErrorKind::TextOverflow { .. })) + ); } #[test] @@ -651,11 +654,9 @@ mod tests { 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())); + assert!( + out.bundle.metadata.functions_used.contains(&"Send".to_string()) + ); } #[test] diff --git a/humility-hif-assembler/src/bundle.rs b/humility-hif-assembler/src/bundle.rs index ca018037c..7b88717c5 100644 --- a/humility-hif-assembler/src/bundle.rs +++ b/humility-hif-assembler/src/bundle.rs @@ -16,7 +16,7 @@ //! line followed by a newline, then the raw binary text and data //! sections. -use anyhow::{bail, Context, Result}; +use anyhow::{Context, Result, bail}; use hif::FunctionResult; use postcard::take_from_bytes; use serde::{Deserialize, Serialize}; diff --git a/humility-hif-assembler/src/lib.rs b/humility-hif-assembler/src/lib.rs index 871b97104..dc6f5fac3 100644 --- a/humility-hif-assembler/src/lib.rs +++ b/humility-hif-assembler/src/lib.rs @@ -172,6 +172,14 @@ //! - 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 in trailing comments. The output is +//! wrapped in `raw { }` and is valid assembler input — paste it into +//! a `.hif` file and it will assemble to the same bytecode. Function +//! IDs are resolved back to names from the `TargetConfig`. +//! //! ## Relationship to humility-hiffy //! //! This crate intentionally overlaps with parts of `humility-hiffy` @@ -202,8 +210,8 @@ pub use bundle::{BundleMetadata, HifBundle, HifResult}; pub use error::HifError; pub use parser::{ParsedProgram, Statement}; pub use types::{ - BufferSizes, FunctionArg, FunctionError, FunctionInfo, I2cDeviceInfo, - I2cMuxInfo, I2cMuxSegment, IdolArgInfo, IdolInterfaceInfo, IdolLeaseInfo, - IdolOpInfo, ResolvedBus, SensorInfo, TargetConfig, I2C_WRITE_MAX_DATA, - MAX_LABELS, + 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 index b45187bbf..fc87f1528 100644 --- a/humility-hif-assembler/src/listing.rs +++ b/humility-hif-assembler/src/listing.rs @@ -66,4 +66,89 @@ impl HifAssembler { 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. + pub fn disassemble(&self, ops: &[hif::Op]) -> String { + use postcard::to_allocvec; + + let mut out = String::new(); + out.push_str("raw {\n"); + + let mut offset = 0usize; + for op in ops { + 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()); + + out.push_str(&format!(" {raw:<26} # {offset:02x}: {hex}\n",)); + offset += to_allocvec(op).map(|b| b.len()).unwrap_or(0); + } + + out.push_str("}\n"); + out + } +} + +/// 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 index e364c38a0..c67181b8e 100644 --- a/humility-hif-assembler/src/lower.rs +++ b/humility-hif-assembler/src/lower.rs @@ -10,7 +10,7 @@ use std::collections::BTreeSet; -use anyhow::{bail, Context, Result}; +use anyhow::{Context, Result, bail}; use hif::TargetFunction; use crate::assembler::HifAssembler; @@ -566,8 +566,6 @@ impl HifAssembler { } } -// -- Free functions -- - /// Encode an Idol argument value as little-endian bytes. fn encode_idol_arg( val: &str, diff --git a/humility-hif-assembler/src/stats.rs b/humility-hif-assembler/src/stats.rs index 543cb69f9..98378a531 100644 --- a/humility-hif-assembler/src/stats.rs +++ b/humility-hif-assembler/src/stats.rs @@ -215,9 +215,9 @@ fn track_mux_switch( } _ => {} } - // 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. + // 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)] diff --git a/humility-hif-assembler/src/types.rs b/humility-hif-assembler/src/types.rs index cb2f50307..b3aa13ddf 100644 --- a/humility-hif-assembler/src/types.rs +++ b/humility-hif-assembler/src/types.rs @@ -84,8 +84,11 @@ pub struct I2cMuxSegment { /// 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. @@ -117,9 +120,13 @@ pub struct FunctionError { /// 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, } diff --git a/humility-hif-assembler/tests/equivalent_tests.rs b/humility-hif-assembler/tests/equivalent_tests.rs new file mode 100644 index 000000000..d80099f6c --- /dev/null +++ b/humility-hif-assembler/tests/equivalent_tests.rs @@ -0,0 +1,294 @@ +// 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()); +} From 9294c349eee6ea0d894266663f575ccf55985a6e Mon Sep 17 00:00:00 2001 From: Ben Stoltz Date: Wed, 8 Apr 2026 14:33:55 -0700 Subject: [PATCH 5/6] Add symbolic I2C device names and improved disassembly I2C operations now accept device names from the archive's topology in place of hex addresses (e.g. `i2c_read mid sbtsi reg=0x01 2`). Names are resolved case-insensitively against the TargetConfig. Numeric addresses still work. The --verify disassembly now emits raw assembler syntax with postcard byte encoding in comments and resolved function names. The output is valid `raw {}` block syntax. Updated existing tests and one example to exercise both named and numeric address forms. --- .../examples/stress-single-device.hif | 3 +- humility-hif-assembler/src/archive.rs | 2 +- humility-hif-assembler/src/assembler.rs | 35 ++++---- humility-hif-assembler/src/bundle.rs | 2 +- humility-hif-assembler/src/lib.rs | 8 +- humility-hif-assembler/src/lower.rs | 84 +++++++++++++++++-- humility-hif-assembler/src/parser.rs | 57 +++++++++++-- humility-hif-assembler/tests/fixture_tests.rs | 52 ++++++++++-- 8 files changed, 197 insertions(+), 46 deletions(-) diff --git a/humility-hif-assembler/examples/stress-single-device.hif b/humility-hif-assembler/examples/stress-single-device.hif index f4af21a44..09483c587 100644 --- a/humility-hif-assembler/examples/stress-single-device.hif +++ b/humility-hif-assembler/examples/stress-single-device.hif @@ -7,10 +7,9 @@ # 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 - i2c_read front $SENSOR reg=$TEMP_REG 2 + i2c_read front tmp117 reg=$TEMP_REG 2 end diff --git a/humility-hif-assembler/src/archive.rs b/humility-hif-assembler/src/archive.rs index ec3189606..393364c73 100644 --- a/humility-hif-assembler/src/archive.rs +++ b/humility-hif-assembler/src/archive.rs @@ -17,7 +17,7 @@ use std::path::Path; -use anyhow::{Context, Result, anyhow}; +use anyhow::{anyhow, Context, Result}; use humility::hubris::{ HubrisArchive, HubrisArchiveDoneness, HubrisEncoding, HubrisGoff, diff --git a/humility-hif-assembler/src/assembler.rs b/humility-hif-assembler/src/assembler.rs index 25593219c..8ea0eff35 100644 --- a/humility-hif-assembler/src/assembler.rs +++ b/humility-hif-assembler/src/assembler.rs @@ -11,11 +11,11 @@ use std::collections::{BTreeSet, HashMap}; use std::fmt; -use anyhow::{Context, Result, bail}; +use anyhow::{bail, Context, Result}; -use crate::bundle::{BUNDLE_VERSION, BundleMetadata, HifBundle}; +use crate::bundle::{BundleMetadata, HifBundle, BUNDLE_VERSION}; use crate::error::{HifError, HifErrorKind}; -use crate::lower::{RSTACK_BYTES_PER_RESULT, normalize_function_name}; +use crate::lower::{normalize_function_name, RSTACK_BYTES_PER_RESULT}; use crate::parser::parse; // Re-export types so `crate::assembler::Foo` still works for @@ -479,12 +479,11 @@ mod tests { 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()) - ); + assert!(out + .bundle + .metadata + .functions_used + .contains(&"i2c_read".to_string())); } #[test] @@ -618,12 +617,10 @@ mod tests { 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 { .. })) - ); + assert!(report + .errors + .iter() + .any(|e| matches!(&e.kind, HifErrorKind::TextOverflow { .. }))); } #[test] @@ -654,9 +651,11 @@ mod tests { 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()) - ); + assert!(out + .bundle + .metadata + .functions_used + .contains(&"Send".to_string())); } #[test] diff --git a/humility-hif-assembler/src/bundle.rs b/humility-hif-assembler/src/bundle.rs index 7b88717c5..ca018037c 100644 --- a/humility-hif-assembler/src/bundle.rs +++ b/humility-hif-assembler/src/bundle.rs @@ -16,7 +16,7 @@ //! line followed by a newline, then the raw binary text and data //! sections. -use anyhow::{Context, Result, bail}; +use anyhow::{bail, Context, Result}; use hif::FunctionResult; use postcard::take_from_bytes; use serde::{Deserialize, Serialize}; diff --git a/humility-hif-assembler/src/lib.rs b/humility-hif-assembler/src/lib.rs index dc6f5fac3..548f0c109 100644 --- a/humility-hif-assembler/src/lib.rs +++ b/humility-hif-assembler/src/lib.rs @@ -210,8 +210,8 @@ 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, + BufferSizes, FunctionArg, FunctionError, FunctionInfo, I2cDeviceInfo, + I2cMuxInfo, I2cMuxSegment, IdolArgInfo, IdolInterfaceInfo, IdolLeaseInfo, + IdolOpInfo, ResolvedBus, SensorInfo, TargetConfig, I2C_WRITE_MAX_DATA, + MAX_LABELS, }; diff --git a/humility-hif-assembler/src/lower.rs b/humility-hif-assembler/src/lower.rs index c67181b8e..4a760c1ea 100644 --- a/humility-hif-assembler/src/lower.rs +++ b/humility-hif-assembler/src/lower.rs @@ -14,7 +14,7 @@ use anyhow::{Context, Result, bail}; use hif::TargetFunction; use crate::assembler::HifAssembler; -use crate::parser::{BusRef, Located, MuxSpec, Statement}; +use crate::parser::{BusRef, DeviceRef, Located, MuxSpec, Statement}; use crate::types::*; /// Internal assembly result: ops + data + stats + warnings. @@ -100,7 +100,7 @@ impl HifAssembler { self.emit_i2c_params( bus, - *address, + address, mux, &mut result.ops, &mut result.warnings, @@ -130,7 +130,7 @@ impl HifAssembler { self.emit_i2c_params( bus, - *address, + address, mux, &mut result.ops, &mut result.warnings, @@ -185,10 +185,17 @@ impl HifAssembler { 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(*address)); + 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))); @@ -417,17 +424,18 @@ impl HifAssembler { fn emit_i2c_params( &self, bus: &BusRef, - address: u8, + 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(address)); + ops.push(hif::Op::Push(addr)); Ok(()) } @@ -474,6 +482,70 @@ impl HifAssembler { } } + /// 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 `_`, diff --git a/humility-hif-assembler/src/parser.rs b/humility-hif-assembler/src/parser.rs index 115d9ddd6..7c6dd17d8 100644 --- a/humility-hif-assembler/src/parser.rs +++ b/humility-hif-assembler/src/parser.rs @@ -73,19 +73,29 @@ pub enum BusRef { 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: u8, + address: DeviceRef, mux: Option, register: Option, nbytes: u8, }, I2cWrite { bus: BusRef, - address: u8, + address: DeviceRef, mux: Option, register: Option, data: Vec, @@ -96,7 +106,7 @@ pub enum Statement { }, I2cRegScan { bus: BusRef, - address: u8, + address: DeviceRef, mux: Option, }, IdolCall { @@ -278,6 +288,15 @@ fn parse_bus_ref(s: &str) -> BusRef { 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=") @@ -313,7 +332,7 @@ fn parse_i2c_read(tokens: &[&str], line: usize) -> Result { } let bus = parse_bus_ref(tokens[0]); - let address = parse_num_err::(tokens[1], line)?; + let address = parse_device_ref(tokens[1]); let mut mux = None; let mut register = None; let mut nbytes_tok = None; @@ -363,7 +382,7 @@ fn parse_i2c_write( } let bus = parse_bus_ref(tokens[0]); - let address = parse_num_err::(tokens[1], line)?; + let address = parse_device_ref(tokens[1]); let mut mux = None; let mut register = None; let mut data_tok = None; @@ -437,7 +456,7 @@ fn parse_i2c_regscan( )); } let bus = parse_bus_ref(tokens[0]); - let address = parse_num_err::(tokens[1], line)?; + let address = parse_device_ref(tokens[1]); let mut mux = None; for &tok in &tokens[2..] { if tok.starts_with("mux=") { @@ -673,7 +692,7 @@ mod tests { match &prog.statements[0].value { Statement::I2cRead { bus, address, register, nbytes, .. } => { assert!(matches!(bus, BusRef::Named(n) if n == "mid")); - assert_eq!(*address, 0x48); + assert_eq!(*address, DeviceRef::Address(0x48)); assert_eq!(*register, Some(0x00)); assert_eq!(*nbytes, 2); } @@ -730,7 +749,7 @@ mod tests { assert_eq!(prog.statements.len(), 1); match &prog.statements[0].value { Statement::I2cRead { address, .. } => { - assert_eq!(*address, 0x48); + assert_eq!(*address, DeviceRef::Address(0x48)); } other => panic!("expected I2cRead, got {other:?}"), } @@ -877,6 +896,28 @@ mod tests { } } + #[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(); diff --git a/humility-hif-assembler/tests/fixture_tests.rs b/humility-hif-assembler/tests/fixture_tests.rs index 70bf1878b..7a6102fd5 100644 --- a/humility-hif-assembler/tests/fixture_tests.rs +++ b/humility-hif-assembler/tests/fixture_tests.rs @@ -53,7 +53,7 @@ fn fixture_loads_and_has_topology() { } #[test] -fn assemble_i2c_read_from_fixture() { +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"); @@ -62,12 +62,22 @@ fn assemble_i2c_read_from_fixture() { } #[test] -fn assemble_stress_loop_from_fixture() { +fn assemble_i2c_read_by_device_name() { + // sbtsi is unique on the mid bus let asm = HifAssembler::new(load_gimlet()); - let src = "repeat 500\n i2c_read mid 0x48 reg=0x00 2\nend"; + 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(500)); + assert_eq!(out.bundle.metadata.estimated_results, Some(200)); } #[test] @@ -78,9 +88,10 @@ fn assemble_muxed_read_from_fixture() { 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 0x{:02x} mux=0x{:02x}.{} 1", - dev.address, mux.address, seg.segment, + "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"); @@ -211,3 +222,32 @@ fn all_examples_assemble() { } 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()); +} From 4db669c268bf5093497bcb4310aeff9f10e33df5 Mon Sep 17 00:00:00 2001 From: Ben Stoltz Date: Wed, 8 Apr 2026 15:09:12 -0700 Subject: [PATCH 6/6] Add symbolic annotations to disassembly output The --verify disassembly now annotates ops with their role in context: I2C parameters are labeled with bus and device names resolved from the archive. Idol Send calls show the task name and op_code. Loop patterns are identified (loop_start, counter, limit). Updated README and lib.rs docs with annotated examples. --- humility-hif-assembler/README.md | 46 ++++--- humility-hif-assembler/src/archive.rs | 2 +- humility-hif-assembler/src/assembler.rs | 35 ++--- humility-hif-assembler/src/bundle.rs | 2 +- humility-hif-assembler/src/lib.rs | 17 +-- humility-hif-assembler/src/listing.rs | 123 +++++++++++++++++- .../tests/equivalent_tests.rs | 14 +- 7 files changed, 187 insertions(+), 52 deletions(-) diff --git a/humility-hif-assembler/README.md b/humility-hif-assembler/README.md index c45a3cf45..d56e6756d 100644 --- a/humility-hif-assembler/README.md +++ b/humility-hif-assembler/README.md @@ -170,10 +170,12 @@ Available: `push`, `push16`, `push32`, `push_none`, `drop`, 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 (right -column), so it can be pasted into a `raw {}` block and re-assembled -to produce the same bytecode. Function IDs are resolved back to -names from the archive. +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 @@ -191,25 +193,39 @@ Ops (31 bytes, 18 ops): raw { push 0 # 00: 04 00 push_none # 02: 07 - label 0 # 03: 00 00 + label 0 # 03: 00 00 loop_start drop # 05: 02 - push 1 # 06: 04 01 - push 1 # 08: 04 01 - push_none # 0a: 07 - push_none # 0b: 07 - push 0x48 # 0c: 04 48 - push 0 # 0e: 04 00 - push 2 # 10: 04 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 - push 5 # 19: 04 05 - branch_gt 0 # 1b: 10 00 + 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) ``` diff --git a/humility-hif-assembler/src/archive.rs b/humility-hif-assembler/src/archive.rs index 393364c73..ec3189606 100644 --- a/humility-hif-assembler/src/archive.rs +++ b/humility-hif-assembler/src/archive.rs @@ -17,7 +17,7 @@ use std::path::Path; -use anyhow::{anyhow, Context, Result}; +use anyhow::{Context, Result, anyhow}; use humility::hubris::{ HubrisArchive, HubrisArchiveDoneness, HubrisEncoding, HubrisGoff, diff --git a/humility-hif-assembler/src/assembler.rs b/humility-hif-assembler/src/assembler.rs index 8ea0eff35..25593219c 100644 --- a/humility-hif-assembler/src/assembler.rs +++ b/humility-hif-assembler/src/assembler.rs @@ -11,11 +11,11 @@ use std::collections::{BTreeSet, HashMap}; use std::fmt; -use anyhow::{bail, Context, Result}; +use anyhow::{Context, Result, bail}; -use crate::bundle::{BundleMetadata, HifBundle, BUNDLE_VERSION}; +use crate::bundle::{BUNDLE_VERSION, BundleMetadata, HifBundle}; use crate::error::{HifError, HifErrorKind}; -use crate::lower::{normalize_function_name, RSTACK_BYTES_PER_RESULT}; +use crate::lower::{RSTACK_BYTES_PER_RESULT, normalize_function_name}; use crate::parser::parse; // Re-export types so `crate::assembler::Foo` still works for @@ -479,11 +479,12 @@ mod tests { 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())); + assert!( + out.bundle + .metadata + .functions_used + .contains(&"i2c_read".to_string()) + ); } #[test] @@ -617,10 +618,12 @@ mod tests { 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 { .. }))); + assert!( + report + .errors + .iter() + .any(|e| matches!(&e.kind, HifErrorKind::TextOverflow { .. })) + ); } #[test] @@ -651,11 +654,9 @@ mod tests { 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())); + assert!( + out.bundle.metadata.functions_used.contains(&"Send".to_string()) + ); } #[test] diff --git a/humility-hif-assembler/src/bundle.rs b/humility-hif-assembler/src/bundle.rs index ca018037c..7b88717c5 100644 --- a/humility-hif-assembler/src/bundle.rs +++ b/humility-hif-assembler/src/bundle.rs @@ -16,7 +16,7 @@ //! line followed by a newline, then the raw binary text and data //! sections. -use anyhow::{bail, Context, Result}; +use anyhow::{Context, Result, bail}; use hif::FunctionResult; use postcard::take_from_bytes; use serde::{Deserialize, Serialize}; diff --git a/humility-hif-assembler/src/lib.rs b/humility-hif-assembler/src/lib.rs index 548f0c109..6ef24b621 100644 --- a/humility-hif-assembler/src/lib.rs +++ b/humility-hif-assembler/src/lib.rs @@ -175,10 +175,11 @@ //! ## Disassembly //! //! [`HifAssembler::disassemble`] formats ops as raw assembler syntax -//! with postcard byte encoding in trailing comments. The output is -//! wrapped in `raw { }` and is valid assembler input — paste it into -//! a `.hif` file and it will assemble to the same bytecode. Function -//! IDs are resolved back to names from the `TargetConfig`. +//! 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 //! @@ -210,8 +211,8 @@ pub use bundle::{BundleMetadata, HifBundle, HifResult}; pub use error::HifError; pub use parser::{ParsedProgram, Statement}; pub use types::{ - BufferSizes, FunctionArg, FunctionError, FunctionInfo, I2cDeviceInfo, - I2cMuxInfo, I2cMuxSegment, IdolArgInfo, IdolInterfaceInfo, IdolLeaseInfo, - IdolOpInfo, ResolvedBus, SensorInfo, TargetConfig, I2C_WRITE_MAX_DATA, - MAX_LABELS, + 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 index fc87f1528..fbb757c94 100644 --- a/humility-hif-assembler/src/listing.rs +++ b/humility-hif-assembler/src/listing.rs @@ -70,14 +70,19 @@ impl HifAssembler { /// 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 op in ops { + for (i, op) in ops.iter().enumerate() { let raw = format_op(op, self); let hex = to_allocvec(op) .map(|bytes| { @@ -89,7 +94,14 @@ impl HifAssembler { }) .unwrap_or_else(|_| "??".into()); - out.push_str(&format!(" {raw:<26} # {offset:02x}: {hex}\n",)); + 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); } @@ -98,6 +110,113 @@ impl HifAssembler { } } +/// 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 { diff --git a/humility-hif-assembler/tests/equivalent_tests.rs b/humility-hif-assembler/tests/equivalent_tests.rs index d80099f6c..3a82b38f9 100644 --- a/humility-hif-assembler/tests/equivalent_tests.rs +++ b/humility-hif-assembler/tests/equivalent_tests.rs @@ -72,10 +72,9 @@ 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(_))) - ); + 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 @@ -98,10 +97,9 @@ 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(_))) - ); + 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)))); }