From 81364a472e42a5e183f5db97e632fd46f85aa661 Mon Sep 17 00:00:00 2001 From: Matt Keeter Date: Tue, 16 Jun 2026 16:15:11 -0400 Subject: [PATCH 1/3] Add InMemoryCore --- Cargo.lock | 10 + Cargo.toml | 1 + cmd/dump/src/lib.rs | 17 +- cmd/hydrate/src/lib.rs | 64 +---- ...ered.control_plane_agent.overflow.0.stderr | 2 +- ...ounters-ipc-filtered.extern-regions.stderr | 2 +- .../counters-ipc-filtered.task.net.stderr | 2 +- .../counters-ipc-filtered.task.power.stderr | 2 +- .../counters-ipc-filtered.u16-ringbuf.stderr | 2 +- ...full.control_plane_agent.overflow.0.stderr | 2 +- .../counters-ipc-full.extern-regions.stderr | 2 +- .../counters-ipc-full.task.net.stderr | 2 +- .../counters-ipc-full.task.power.stderr | 2 +- .../counters-ipc-full.u16-ringbuf.stderr | 2 +- ...-ipc.control_plane_agent.overflow.0.stderr | 2 +- .../counters-ipc.extern-regions.stderr | 2 +- .../counters-ipc/counters-ipc.task.net.stderr | 2 +- .../counters-ipc.task.power.stderr | 2 +- .../counters-ipc.u16-ringbuf.stderr | 2 +- ...anic.control_plane_agent.overflow.0.stderr | 2 +- .../host-panic.extern-regions.stderr | 2 +- .../cmd/host-panic/host-panic.task.net.stderr | 2 +- .../host-panic/host-panic.task.power.stderr | 2 +- .../host-panic/host-panic.u16-ringbuf.stderr | 2 +- .../spd.control_plane_agent.overflow.0.stderr | 2 +- .../tests/cmd/spd/spd.extern-regions.stderr | 2 +- .../tests/cmd/spd/spd.task.net.stderr | 2 +- .../tests/cmd/spd/spd.task.power.stderr | 2 +- .../tests/cmd/spd/spd.u16-ringbuf.stderr | 2 +- ...slvr.control_plane_agent.overflow.0.stdout | 2 +- .../tasks-slvr/tasks-slvr.task.power.stdout | 2 +- .../tasks-slvr/tasks-slvr.u16-ringbuf.stdout | 2 +- humility-cli/src/lib.rs | 2 +- humility-core/Cargo.toml | 1 + humility-core/src/archive.rs | 59 ---- humility-core/src/core.rs | 11 +- humility-core/src/hubris.rs | 97 ++----- humility-core/src/lib.rs | 3 +- humility-core/src/{dump.rs => mem.rs} | 242 ++++++++-------- humility-dump-agent/src/lib.rs | 265 ++++++------------ humility-dump/src/lib.rs | 22 +- humility-net/src/lib.rs | 15 +- 42 files changed, 316 insertions(+), 549 deletions(-) delete mode 100644 humility-core/src/archive.rs rename humility-core/src/{dump.rs => mem.rs} (51%) diff --git a/Cargo.lock b/Cargo.lock index 6cc224fde..bba8dadb2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1094,6 +1094,15 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "datamap" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc78156ab8e5328a2172df6a8afea26a985e82a514b71af1efd773962257cae5" +dependencies = [ + "thiserror 2.0.18", +] + [[package]] name = "deflate64" version = "0.1.12" @@ -2836,6 +2845,7 @@ dependencies = [ "bitfield", "capstone", "clap", + "datamap", "gimli 0.33.0", "goblin", "hubpack", diff --git a/Cargo.toml b/Cargo.toml index ba5137c83..fdcb8e799 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -226,6 +226,7 @@ crossbeam-channel = "0.5.6" crossterm = "0.29.0" csv = "1.1.3" ctrlc = "3.1.5" +datamap = "0.1.0" env_logger = "0.11.10" gimli = "0.33.0" goblin = "0.10" diff --git a/cmd/dump/src/lib.rs b/cmd/dump/src/lib.rs index cbdc95a25..0ec20b2ce 100644 --- a/cmd/dump/src/lib.rs +++ b/cmd/dump/src/lib.rs @@ -70,11 +70,12 @@ use clap::{ArgGroup, Parser}; use humility::core::Core; use humility::hubris::*; use humility::log::{Logger, info}; +use humility::mem::InMemoryCore; use humility_arch_arm::ARMRegister; use humility_cli::{ExecutionContext, humility_cmd}; use humility_dump_agent::{ - DumpAgent, DumpAgentCore, DumpAgentExt, DumpArea, DumpBreakdown, - HiffyDumpAgent, UdpDumpAgent, task_areas, + DumpAgent, DumpAgentExt, DumpArea, DumpBreakdown, HiffyDumpAgent, + UdpDumpAgent, task_areas, }; use humpty::DumpTask; use indicatif::{HumanBytes, HumanDuration, ProgressBar, ProgressStyle}; @@ -341,7 +342,7 @@ fn get_dump_agent<'a>( fn read_dump<'a>( agent: &mut Box, area: Option, - out: &mut DumpAgentCore, + out: &mut humility::mem::InMemoryCore, subargs: &DumpArgs, log: &Logger, ) -> Result> { @@ -415,7 +416,7 @@ fn simulate_dump_via_agent( subargs: &DumpArgs, log: &Logger, ) -> Result<()> { - let mut out = DumpAgentCore::new(HubrisFlashMap::new(hubris)?); + let mut out = InMemoryCore::from_archive(hubris)?; let started = Some(Instant::now()); let mut area = subargs.extract.map(DumpArea::ByIndex); @@ -537,7 +538,7 @@ fn simulate_dump_via_agent( ncompressed += compressed; - out.add_ram_region(addr, compare); + out.add_ram_region(addr, compare)?; remain -= nbytes; nread += nbytes; @@ -650,7 +651,7 @@ fn extract_or_read_via_agent( subargs: &DumpArgs, log: &Logger, ) -> Result<()> { - let mut out = DumpAgentCore::new(HubrisFlashMap::new(hubris)?); + let mut out = InMemoryCore::from_archive(hubris)?; let started = Some(Instant::now()); let mut agent = get_dump_agent(hubris, core, subargs, log)?; @@ -726,7 +727,7 @@ fn dump_task_via_agent( subargs: &DumpArgs, log: &Logger, ) -> Result<()> { - let mut out = DumpAgentCore::new(HubrisFlashMap::new(hubris)?); + let mut out = InMemoryCore::from_archive(hubris)?; let started = Some(Instant::now()); let mut agent = get_dump_agent(hubris, core, subargs, log)?; @@ -835,7 +836,7 @@ fn dump_all( .unwrap(); info!(log, "dumping {task_name} (area {area})"); - let mut out = DumpAgentCore::new(HubrisFlashMap::new(hubris)?); + let mut out = InMemoryCore::from_archive(hubris)?; let started = Some(Instant::now()); let task = read_dump( &mut agent, diff --git a/cmd/hydrate/src/lib.rs b/cmd/hydrate/src/lib.rs index d7b783157..3961f1b26 100644 --- a/cmd/hydrate/src/lib.rs +++ b/cmd/hydrate/src/lib.rs @@ -20,10 +20,9 @@ use anyhow::{Context, Result, bail}; use clap::{ArgGroup, Parser}; use humility::{ - hubris::HubrisFlashMap, log::{info, warn}, + mem::InMemoryCore, }; -use humility_arch_arm::ARMRegister; use humility_cli::{ExecutionContext, humility_cmd}; use std::{collections::BTreeMap, io::Read, path::PathBuf}; @@ -41,62 +40,6 @@ pub struct HydrateArgs { file: PathBuf, } -struct DryCore { - flash: HubrisFlashMap, - mem: BTreeMap>, -} - -// Helper macro to stub out functions -macro_rules! unsupported{ - ($fn_name:ident($($arg_name:ident: $arg_type:ty),*)) => { - unsupported!($fn_name($($arg_name: $arg_type),*) -> Result<()>); - }; - ($fn_name:ident($($arg_name:ident: $arg_type:ty),*) -> $out:ty) => { - fn $fn_name(&mut self, $($arg_name: $arg_type),*) -> $out { - bail!(concat!( - "DryCore does not support ", - stringify!($fn_name))) - } - }; -} - -impl humility::core::Core for DryCore { - unsupported!(run()); - unsupported!(halt()); - unsupported!(write_8(_addr: u32, _data: &[u8])); - unsupported!(op_done()); - unsupported!(op_start()); - unsupported!(read_reg(_reg: ARMRegister) -> Result); - unsupported!(write_word_32(_addr: u32, _data: u32)); - - fn is_archive(&self) -> bool { - false - } - fn is_dump(&self) -> bool { - true // I guess? - } - fn is_net(&self) -> bool { - false - } - - fn read_8(&mut self, addr: u32, data: &mut [u8]) -> Result<()> { - if self.flash.read(addr, data).is_some() { - return Ok(()); - } - - let Some((base, mem)) = self.mem.range(0..=addr).next_back() else { - bail!("addr {addr:#08x} is below memory range"); - }; - let offset = (addr - base) as usize; - let end = offset + data.len(); - if end > mem.len() { - bail!("region is not large enough; {end:#x} > {:#x}", mem.len()); - } - data.copy_from_slice(&mem[offset..end]); - Ok(()) - } -} - fn run(subargs: HydrateArgs, context: &mut ExecutionContext) -> Result<()> { let f = std::fs::File::open(&subargs.file)?; let mut z = zip::ZipArchive::new(f)?; @@ -173,7 +116,10 @@ fn run(subargs: HydrateArgs, context: &mut ExecutionContext) -> Result<()> { dump ID wants {archive_id:02x?}" ); } - let mut core = DryCore { mem, flash: HubrisFlashMap::new(archive)? }; + let mut core = InMemoryCore::from_archive(archive)?; + for (addr, data) in mem { + core.add_ram_region(addr, data)?; + } let t = Some(humpty::DumpTask { magic: humpty::DUMP_TASK_MAGIC, diff --git a/humility-bin/tests/cmd/counters-ipc-filtered/counters-ipc-filtered.control_plane_agent.overflow.0.stderr b/humility-bin/tests/cmd/counters-ipc-filtered/counters-ipc-filtered.control_plane_agent.overflow.0.stderr index 79b62bebc..f6bbdd7a1 100644 --- a/humility-bin/tests/cmd/counters-ipc-filtered/counters-ipc-filtered.control_plane_agent.overflow.0.stderr +++ b/humility-bin/tests/cmd/counters-ipc-filtered/counters-ipc-filtered.control_plane_agent.overflow.0.stderr @@ -1,5 +1,5 @@ humility: attached to dump -humility: WARNING: failed to load task table: read of 4536 bytes from invalid address: 0x24000490 +humility: WARNING: failed to load task table: read of 4536 bytes failed: could not find addr 0x24000490 in memory humility: WARNING: no generations/restart counts will be displayed. humility: note: this may be a single-task dump. humility counters failed: no IPC counters found diff --git a/humility-bin/tests/cmd/counters-ipc-filtered/counters-ipc-filtered.extern-regions.stderr b/humility-bin/tests/cmd/counters-ipc-filtered/counters-ipc-filtered.extern-regions.stderr index 79b62bebc..f6bbdd7a1 100644 --- a/humility-bin/tests/cmd/counters-ipc-filtered/counters-ipc-filtered.extern-regions.stderr +++ b/humility-bin/tests/cmd/counters-ipc-filtered/counters-ipc-filtered.extern-regions.stderr @@ -1,5 +1,5 @@ humility: attached to dump -humility: WARNING: failed to load task table: read of 4536 bytes from invalid address: 0x24000490 +humility: WARNING: failed to load task table: read of 4536 bytes failed: could not find addr 0x24000490 in memory humility: WARNING: no generations/restart counts will be displayed. humility: note: this may be a single-task dump. humility counters failed: no IPC counters found diff --git a/humility-bin/tests/cmd/counters-ipc-filtered/counters-ipc-filtered.task.net.stderr b/humility-bin/tests/cmd/counters-ipc-filtered/counters-ipc-filtered.task.net.stderr index 79b62bebc..f6bbdd7a1 100644 --- a/humility-bin/tests/cmd/counters-ipc-filtered/counters-ipc-filtered.task.net.stderr +++ b/humility-bin/tests/cmd/counters-ipc-filtered/counters-ipc-filtered.task.net.stderr @@ -1,5 +1,5 @@ humility: attached to dump -humility: WARNING: failed to load task table: read of 4536 bytes from invalid address: 0x24000490 +humility: WARNING: failed to load task table: read of 4536 bytes failed: could not find addr 0x24000490 in memory humility: WARNING: no generations/restart counts will be displayed. humility: note: this may be a single-task dump. humility counters failed: no IPC counters found diff --git a/humility-bin/tests/cmd/counters-ipc-filtered/counters-ipc-filtered.task.power.stderr b/humility-bin/tests/cmd/counters-ipc-filtered/counters-ipc-filtered.task.power.stderr index b49cf79d4..64df28efe 100644 --- a/humility-bin/tests/cmd/counters-ipc-filtered/counters-ipc-filtered.task.power.stderr +++ b/humility-bin/tests/cmd/counters-ipc-filtered/counters-ipc-filtered.task.power.stderr @@ -1,5 +1,5 @@ humility: attached to dump -humility: WARNING: failed to load task table: read of 4704 bytes from invalid address: 0x24000490 +humility: WARNING: failed to load task table: read of 4704 bytes failed: could not find addr 0x24000490 in memory humility: WARNING: no generations/restart counts will be displayed. humility: note: this may be a single-task dump. humility counters failed: no IPC counters found diff --git a/humility-bin/tests/cmd/counters-ipc-filtered/counters-ipc-filtered.u16-ringbuf.stderr b/humility-bin/tests/cmd/counters-ipc-filtered/counters-ipc-filtered.u16-ringbuf.stderr index 20ed8f605..ba58a8aa0 100644 --- a/humility-bin/tests/cmd/counters-ipc-filtered/counters-ipc-filtered.u16-ringbuf.stderr +++ b/humility-bin/tests/cmd/counters-ipc-filtered/counters-ipc-filtered.u16-ringbuf.stderr @@ -1,4 +1,4 @@ humility: attached to dump -humility: WARNING: failed to load task table: read of 4704 bytes from invalid address: 0x24000490 +humility: WARNING: failed to load task table: read of 4704 bytes failed: could not find addr 0x24000490 in memory humility: WARNING: no generations/restart counts will be displayed. humility: note: this may be a single-task dump. diff --git a/humility-bin/tests/cmd/counters-ipc-full/counters-ipc-full.control_plane_agent.overflow.0.stderr b/humility-bin/tests/cmd/counters-ipc-full/counters-ipc-full.control_plane_agent.overflow.0.stderr index 3ff2e24a2..b6598cefd 100644 --- a/humility-bin/tests/cmd/counters-ipc-full/counters-ipc-full.control_plane_agent.overflow.0.stderr +++ b/humility-bin/tests/cmd/counters-ipc-full/counters-ipc-full.control_plane_agent.overflow.0.stderr @@ -1,4 +1,4 @@ humility: attached to dump -humility: WARNING: failed to load task table: read of 4536 bytes from invalid address: 0x24000490 +humility: WARNING: failed to load task table: read of 4536 bytes failed: could not find addr 0x24000490 in memory humility: WARNING: no generations/restart counts will be displayed. humility: note: this may be a single-task dump. diff --git a/humility-bin/tests/cmd/counters-ipc-full/counters-ipc-full.extern-regions.stderr b/humility-bin/tests/cmd/counters-ipc-full/counters-ipc-full.extern-regions.stderr index 79b62bebc..f6bbdd7a1 100644 --- a/humility-bin/tests/cmd/counters-ipc-full/counters-ipc-full.extern-regions.stderr +++ b/humility-bin/tests/cmd/counters-ipc-full/counters-ipc-full.extern-regions.stderr @@ -1,5 +1,5 @@ humility: attached to dump -humility: WARNING: failed to load task table: read of 4536 bytes from invalid address: 0x24000490 +humility: WARNING: failed to load task table: read of 4536 bytes failed: could not find addr 0x24000490 in memory humility: WARNING: no generations/restart counts will be displayed. humility: note: this may be a single-task dump. humility counters failed: no IPC counters found diff --git a/humility-bin/tests/cmd/counters-ipc-full/counters-ipc-full.task.net.stderr b/humility-bin/tests/cmd/counters-ipc-full/counters-ipc-full.task.net.stderr index 79b62bebc..f6bbdd7a1 100644 --- a/humility-bin/tests/cmd/counters-ipc-full/counters-ipc-full.task.net.stderr +++ b/humility-bin/tests/cmd/counters-ipc-full/counters-ipc-full.task.net.stderr @@ -1,5 +1,5 @@ humility: attached to dump -humility: WARNING: failed to load task table: read of 4536 bytes from invalid address: 0x24000490 +humility: WARNING: failed to load task table: read of 4536 bytes failed: could not find addr 0x24000490 in memory humility: WARNING: no generations/restart counts will be displayed. humility: note: this may be a single-task dump. humility counters failed: no IPC counters found diff --git a/humility-bin/tests/cmd/counters-ipc-full/counters-ipc-full.task.power.stderr b/humility-bin/tests/cmd/counters-ipc-full/counters-ipc-full.task.power.stderr index 20ed8f605..ba58a8aa0 100644 --- a/humility-bin/tests/cmd/counters-ipc-full/counters-ipc-full.task.power.stderr +++ b/humility-bin/tests/cmd/counters-ipc-full/counters-ipc-full.task.power.stderr @@ -1,4 +1,4 @@ humility: attached to dump -humility: WARNING: failed to load task table: read of 4704 bytes from invalid address: 0x24000490 +humility: WARNING: failed to load task table: read of 4704 bytes failed: could not find addr 0x24000490 in memory humility: WARNING: no generations/restart counts will be displayed. humility: note: this may be a single-task dump. diff --git a/humility-bin/tests/cmd/counters-ipc-full/counters-ipc-full.u16-ringbuf.stderr b/humility-bin/tests/cmd/counters-ipc-full/counters-ipc-full.u16-ringbuf.stderr index 20ed8f605..ba58a8aa0 100644 --- a/humility-bin/tests/cmd/counters-ipc-full/counters-ipc-full.u16-ringbuf.stderr +++ b/humility-bin/tests/cmd/counters-ipc-full/counters-ipc-full.u16-ringbuf.stderr @@ -1,4 +1,4 @@ humility: attached to dump -humility: WARNING: failed to load task table: read of 4704 bytes from invalid address: 0x24000490 +humility: WARNING: failed to load task table: read of 4704 bytes failed: could not find addr 0x24000490 in memory humility: WARNING: no generations/restart counts will be displayed. humility: note: this may be a single-task dump. diff --git a/humility-bin/tests/cmd/counters-ipc/counters-ipc.control_plane_agent.overflow.0.stderr b/humility-bin/tests/cmd/counters-ipc/counters-ipc.control_plane_agent.overflow.0.stderr index 3ff2e24a2..b6598cefd 100644 --- a/humility-bin/tests/cmd/counters-ipc/counters-ipc.control_plane_agent.overflow.0.stderr +++ b/humility-bin/tests/cmd/counters-ipc/counters-ipc.control_plane_agent.overflow.0.stderr @@ -1,4 +1,4 @@ humility: attached to dump -humility: WARNING: failed to load task table: read of 4536 bytes from invalid address: 0x24000490 +humility: WARNING: failed to load task table: read of 4536 bytes failed: could not find addr 0x24000490 in memory humility: WARNING: no generations/restart counts will be displayed. humility: note: this may be a single-task dump. diff --git a/humility-bin/tests/cmd/counters-ipc/counters-ipc.extern-regions.stderr b/humility-bin/tests/cmd/counters-ipc/counters-ipc.extern-regions.stderr index 79b62bebc..f6bbdd7a1 100644 --- a/humility-bin/tests/cmd/counters-ipc/counters-ipc.extern-regions.stderr +++ b/humility-bin/tests/cmd/counters-ipc/counters-ipc.extern-regions.stderr @@ -1,5 +1,5 @@ humility: attached to dump -humility: WARNING: failed to load task table: read of 4536 bytes from invalid address: 0x24000490 +humility: WARNING: failed to load task table: read of 4536 bytes failed: could not find addr 0x24000490 in memory humility: WARNING: no generations/restart counts will be displayed. humility: note: this may be a single-task dump. humility counters failed: no IPC counters found diff --git a/humility-bin/tests/cmd/counters-ipc/counters-ipc.task.net.stderr b/humility-bin/tests/cmd/counters-ipc/counters-ipc.task.net.stderr index 79b62bebc..f6bbdd7a1 100644 --- a/humility-bin/tests/cmd/counters-ipc/counters-ipc.task.net.stderr +++ b/humility-bin/tests/cmd/counters-ipc/counters-ipc.task.net.stderr @@ -1,5 +1,5 @@ humility: attached to dump -humility: WARNING: failed to load task table: read of 4536 bytes from invalid address: 0x24000490 +humility: WARNING: failed to load task table: read of 4536 bytes failed: could not find addr 0x24000490 in memory humility: WARNING: no generations/restart counts will be displayed. humility: note: this may be a single-task dump. humility counters failed: no IPC counters found diff --git a/humility-bin/tests/cmd/counters-ipc/counters-ipc.task.power.stderr b/humility-bin/tests/cmd/counters-ipc/counters-ipc.task.power.stderr index 20ed8f605..ba58a8aa0 100644 --- a/humility-bin/tests/cmd/counters-ipc/counters-ipc.task.power.stderr +++ b/humility-bin/tests/cmd/counters-ipc/counters-ipc.task.power.stderr @@ -1,4 +1,4 @@ humility: attached to dump -humility: WARNING: failed to load task table: read of 4704 bytes from invalid address: 0x24000490 +humility: WARNING: failed to load task table: read of 4704 bytes failed: could not find addr 0x24000490 in memory humility: WARNING: no generations/restart counts will be displayed. humility: note: this may be a single-task dump. diff --git a/humility-bin/tests/cmd/counters-ipc/counters-ipc.u16-ringbuf.stderr b/humility-bin/tests/cmd/counters-ipc/counters-ipc.u16-ringbuf.stderr index 20ed8f605..ba58a8aa0 100644 --- a/humility-bin/tests/cmd/counters-ipc/counters-ipc.u16-ringbuf.stderr +++ b/humility-bin/tests/cmd/counters-ipc/counters-ipc.u16-ringbuf.stderr @@ -1,4 +1,4 @@ humility: attached to dump -humility: WARNING: failed to load task table: read of 4704 bytes from invalid address: 0x24000490 +humility: WARNING: failed to load task table: read of 4704 bytes failed: could not find addr 0x24000490 in memory humility: WARNING: no generations/restart counts will be displayed. humility: note: this may be a single-task dump. diff --git a/humility-bin/tests/cmd/host-panic/host-panic.control_plane_agent.overflow.0.stderr b/humility-bin/tests/cmd/host-panic/host-panic.control_plane_agent.overflow.0.stderr index 8381d6e62..1f05506e1 100644 --- a/humility-bin/tests/cmd/host-panic/host-panic.control_plane_agent.overflow.0.stderr +++ b/humility-bin/tests/cmd/host-panic/host-panic.control_plane_agent.overflow.0.stderr @@ -1,2 +1,2 @@ humility: attached to dump -humility host failed: read of 24964 bytes from invalid address: 0x24021ff4 +humility host failed: read of 24964 bytes failed: could not find addr 0x24021ff4 in memory diff --git a/humility-bin/tests/cmd/host-panic/host-panic.extern-regions.stderr b/humility-bin/tests/cmd/host-panic/host-panic.extern-regions.stderr index a4c21f8f5..1b2a31218 100644 --- a/humility-bin/tests/cmd/host-panic/host-panic.extern-regions.stderr +++ b/humility-bin/tests/cmd/host-panic/host-panic.extern-regions.stderr @@ -1,2 +1,2 @@ humility: attached to dump -humility host failed: read of 4096 bytes from invalid address: 0x2402510d +humility host failed: read of 4096 bytes failed: could not find addr 0x2402510d in memory diff --git a/humility-bin/tests/cmd/host-panic/host-panic.task.net.stderr b/humility-bin/tests/cmd/host-panic/host-panic.task.net.stderr index f9ff702c2..33e0785d5 100644 --- a/humility-bin/tests/cmd/host-panic/host-panic.task.net.stderr +++ b/humility-bin/tests/cmd/host-panic/host-panic.task.net.stderr @@ -1,2 +1,2 @@ humility: attached to dump -humility host failed: read of 4096 bytes from invalid address: 0x2401d10d +humility host failed: read of 4096 bytes failed: could not find addr 0x2401d10d in memory diff --git a/humility-bin/tests/cmd/host-panic/host-panic.task.power.stderr b/humility-bin/tests/cmd/host-panic/host-panic.task.power.stderr index ac232f250..b0172103b 100644 --- a/humility-bin/tests/cmd/host-panic/host-panic.task.power.stderr +++ b/humility-bin/tests/cmd/host-panic/host-panic.task.power.stderr @@ -1,2 +1,2 @@ humility: attached to dump -humility host failed: read of 4096 bytes from invalid address: 0x24025b99 +humility host failed: read of 4096 bytes failed: could not find addr 0x24025b99 in memory diff --git a/humility-bin/tests/cmd/host-panic/host-panic.u16-ringbuf.stderr b/humility-bin/tests/cmd/host-panic/host-panic.u16-ringbuf.stderr index a5cfc36b4..a7a6c29af 100644 --- a/humility-bin/tests/cmd/host-panic/host-panic.u16-ringbuf.stderr +++ b/humility-bin/tests/cmd/host-panic/host-panic.u16-ringbuf.stderr @@ -1,2 +1,2 @@ humility: attached to dump -humility host failed: read of 24964 bytes from invalid address: 0x24021c1c +humility host failed: read of 24964 bytes failed: could not find addr 0x24021c1c in memory diff --git a/humility-bin/tests/cmd/spd/spd.control_plane_agent.overflow.0.stderr b/humility-bin/tests/cmd/spd/spd.control_plane_agent.overflow.0.stderr index 644041b50..270a55949 100644 --- a/humility-bin/tests/cmd/spd/spd.control_plane_agent.overflow.0.stderr +++ b/humility-bin/tests/cmd/spd/spd.control_plane_agent.overflow.0.stderr @@ -1,2 +1,2 @@ humility: attached to dump -humility spd failed: read of 8264 bytes from invalid address: 0x24004380 +humility spd failed: read of 8264 bytes failed: could not find addr 0x24004380 in memory diff --git a/humility-bin/tests/cmd/spd/spd.extern-regions.stderr b/humility-bin/tests/cmd/spd/spd.extern-regions.stderr index 99eac0db6..fd8fe32df 100644 --- a/humility-bin/tests/cmd/spd/spd.extern-regions.stderr +++ b/humility-bin/tests/cmd/spd/spd.extern-regions.stderr @@ -1,2 +1,2 @@ humility: attached to dump -humility spd failed: read of 8192 bytes from invalid address: 0x24004380 +humility spd failed: read of 8192 bytes failed: could not find addr 0x24004380 in memory diff --git a/humility-bin/tests/cmd/spd/spd.task.net.stderr b/humility-bin/tests/cmd/spd/spd.task.net.stderr index 91ab00b0c..c8e0593c7 100644 --- a/humility-bin/tests/cmd/spd/spd.task.net.stderr +++ b/humility-bin/tests/cmd/spd/spd.task.net.stderr @@ -1,2 +1,2 @@ humility: attached to dump -humility spd failed: read of 8192 bytes from invalid address: 0x24004798 +humility spd failed: read of 8192 bytes failed: could not find addr 0x24004798 in memory diff --git a/humility-bin/tests/cmd/spd/spd.task.power.stderr b/humility-bin/tests/cmd/spd/spd.task.power.stderr index 99eac0db6..fd8fe32df 100644 --- a/humility-bin/tests/cmd/spd/spd.task.power.stderr +++ b/humility-bin/tests/cmd/spd/spd.task.power.stderr @@ -1,2 +1,2 @@ humility: attached to dump -humility spd failed: read of 8192 bytes from invalid address: 0x24004380 +humility spd failed: read of 8192 bytes failed: could not find addr 0x24004380 in memory diff --git a/humility-bin/tests/cmd/spd/spd.u16-ringbuf.stderr b/humility-bin/tests/cmd/spd/spd.u16-ringbuf.stderr index 644041b50..270a55949 100644 --- a/humility-bin/tests/cmd/spd/spd.u16-ringbuf.stderr +++ b/humility-bin/tests/cmd/spd/spd.u16-ringbuf.stderr @@ -1,2 +1,2 @@ humility: attached to dump -humility spd failed: read of 8264 bytes from invalid address: 0x24004380 +humility spd failed: read of 8264 bytes failed: could not find addr 0x24004380 in memory diff --git a/humility-bin/tests/cmd/tasks-slvr/tasks-slvr.control_plane_agent.overflow.0.stdout b/humility-bin/tests/cmd/tasks-slvr/tasks-slvr.control_plane_agent.overflow.0.stdout index 2de5b4439..a1428c4ea 100644 --- a/humility-bin/tests/cmd/tasks-slvr/tasks-slvr.control_plane_agent.overflow.0.stdout +++ b/humility-bin/tests/cmd/tasks-slvr/tasks-slvr.control_plane_agent.overflow.0.stdout @@ -1,7 +1,7 @@ system time = 6193 ID TASK GEN PRI STATE 19 control_plane_agent 1 6 FAULT: stack overflow; sp=0x2402ff90 (was: ready) - could not read registers: read of 32 bytes from invalid address: 0x2402ff90 + could not read registers: read of 32 bytes failed: could not find addr 0x2402ff90 in memory guessing at stack trace using saved frame pointer | +---> 0x24030090 0x08027790 drv_lpc55_update_api::_::::deserialize diff --git a/humility-bin/tests/cmd/tasks-slvr/tasks-slvr.task.power.stdout b/humility-bin/tests/cmd/tasks-slvr/tasks-slvr.task.power.stdout index 2ec512e17..38dfb176e 100644 --- a/humility-bin/tests/cmd/tasks-slvr/tasks-slvr.task.power.stdout +++ b/humility-bin/tests/cmd/tasks-slvr/tasks-slvr.task.power.stdout @@ -1,7 +1,7 @@ system time = 76648 ID TASK GEN PRI STATE 8 power 0 6 FAULT: stack overflow; sp=0x24043fa0 (was: ready) - could not read registers: read of 32 bytes from invalid address: 0x24043fa0 + could not read registers: read of 32 bytes failed: could not find addr 0x24043fa0 in memory guessing at stack trace using saved frame pointer | +---> 0x24044040 0x08088a20 core::fmt::write diff --git a/humility-bin/tests/cmd/tasks-slvr/tasks-slvr.u16-ringbuf.stdout b/humility-bin/tests/cmd/tasks-slvr/tasks-slvr.u16-ringbuf.stdout index 8e8ba4244..6a0bf0a1a 100644 --- a/humility-bin/tests/cmd/tasks-slvr/tasks-slvr.u16-ringbuf.stdout +++ b/humility-bin/tests/cmd/tasks-slvr/tasks-slvr.u16-ringbuf.stdout @@ -1,7 +1,7 @@ system time = 23233 ID TASK GEN PRI STATE 10 gimlet_seq 12 4 FAULT: stack overflow; sp=0x24047fc0 (was: ready) - could not read registers: read of 32 bytes from invalid address: 0x24047fc0 + could not read registers: read of 32 bytes failed: could not find addr 0x24047fc0 in memory guessing at stack trace using saved frame pointer | +---> 0x240480c0 0x0807afd4 drv_gimlet_seq_server::ringbuf_entry_v3p3_sys_a0_vout diff --git a/humility-cli/src/lib.rs b/humility-cli/src/lib.rs index 6d42dd447..9cdd07d35 100644 --- a/humility-cli/src/lib.rs +++ b/humility-cli/src/lib.rs @@ -302,7 +302,7 @@ impl Cli { /// Attaches to a dump /// /// Reads from the `--dump` argument to pick a target file - pub fn attach_dump(&self) -> Result { + pub fn attach_dump(&self) -> Result { let core = if let Some(dump) = &self.dump { humility::core::attach_dump(dump, self.log())? } else { diff --git a/humility-core/Cargo.toml b/humility-core/Cargo.toml index 69e324550..2f9292a6a 100644 --- a/humility-core/Cargo.toml +++ b/humility-core/Cargo.toml @@ -11,6 +11,7 @@ auto_impl.workspace = true bitfield.workspace = true capstone.workspace = true clap.workspace = true +datamap.workspace = true gimli.workspace = true goblin.workspace = true hubpack.workspace = true diff --git a/humility-core/src/archive.rs b/humility-core/src/archive.rs deleted file mode 100644 index dbaaf730b..000000000 --- a/humility-core/src/archive.rs +++ /dev/null @@ -1,59 +0,0 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. - -use crate::core::Core; -use crate::hubris::HubrisArchive; -use crate::hubris::HubrisFlashMap; -use anyhow::{Result, anyhow, bail}; -use humility_arch_arm::ARMRegister; - -pub struct ArchiveCore { - flash: HubrisFlashMap, -} - -impl ArchiveCore { - pub(crate) fn new(hubris: &HubrisArchive) -> Result { - Ok(Self { flash: HubrisFlashMap::new(hubris)? }) - } - - fn read(&mut self, addr: u32, data: &mut [u8]) -> Result<()> { - self.flash.read(addr, data).ok_or_else(|| { - anyhow!( - "{addr:#x} is not in flash and therefore can't \ - be read from just an archive; did you mean to plug in a \ - probe or connect via the network?" - ) - }) - } -} - -impl Core for ArchiveCore { - fn is_archive(&self) -> bool { - true - } - - fn read_8(&mut self, addr: u32, data: &mut [u8]) -> Result<()> { - self.read(addr, data) - } - - fn read_reg(&mut self, reg: ARMRegister) -> Result { - bail!("cannot read register {} from an archive", reg); - } - - fn write_word_32(&mut self, _addr: u32, _data: u32) -> Result<()> { - bail!("cannot write a word to an archive"); - } - - fn write_8(&mut self, _addr: u32, _data: &[u8]) -> Result<()> { - bail!("cannot write a byte to an archive"); - } - - fn halt(&mut self) -> Result<()> { - Ok(()) - } - - fn run(&mut self) -> Result<()> { - Ok(()) - } -} diff --git a/humility-core/src/core.rs b/humility-core/src/core.rs index 3d5532bf3..dbc6b5488 100644 --- a/humility-core/src/core.rs +++ b/humility-core/src/core.rs @@ -4,9 +4,8 @@ use anyhow::{Result, bail}; -use crate::archive::ArchiveCore; -use crate::dump::DumpCore; use crate::hubris::*; +use crate::mem::InMemoryCore; use humility_arch_arm::ARMRegister; use humility_log::{Logger, info}; use std::str; @@ -85,8 +84,8 @@ pub enum NetAgent { Hiffy, } -pub fn attach_dump(dump: &str, log: &Logger) -> Result { - let core = DumpCore::new(dump)?; +pub fn attach_dump(dump: &str, log: &Logger) -> Result { + let core = InMemoryCore::from_dump(dump)?; info!(log, "attached to dump"); Ok(core) } @@ -94,8 +93,8 @@ pub fn attach_dump(dump: &str, log: &Logger) -> Result { pub fn attach_archive( hubris: &HubrisArchive, log: &Logger, -) -> Result { - let core = ArchiveCore::new(hubris)?; +) -> Result { + let core = InMemoryCore::from_archive(hubris)?; info!(log, "attached to archive"); Ok(core) } diff --git a/humility-core/src/hubris.rs b/humility-core/src/hubris.rs index f9a4745e0..a1a201faf 100644 --- a/humility-core/src/hubris.rs +++ b/humility-core/src/hubris.rs @@ -1049,74 +1049,8 @@ impl From<&str> for HubrisSensorKind { } } -#[derive(Clone)] -pub struct HubrisFlashMap { - /// Linear map of all flash memory - pub contents: Vec, - - /// Regions within that memory as offset + size tuples, indexed by address - pub regions: BTreeMap, -} - -impl HubrisFlashMap { - /// # Errors - /// - /// (Non-exhaustive list added when surprising error conditions were - /// discovered:) - /// - /// This will fail if the `HubrisArchive` is fake, i.e. contains zero bytes. - pub fn new(hubris: &HubrisArchive) -> Result { - // - // We want to read in the "final.elf" from our archive and use that - // to determine the memory that constitutes flash. - // - let contents = hubris.hubris_archive.extract_file("img/final.elf")?; - - let elf = Elf::parse(&contents).map_err(|e| { - anyhow!("failed to parse final.elf as an ELF file: {}", e) - })?; - - let regions = elf - .section_headers - .iter() - .filter(|shdr| { - shdr.sh_type == goblin::elf::section_header::SHT_PROGBITS - }) - .map(|shdr| { - ( - shdr.sh_addr as u32, - (shdr.sh_size as u32, shdr.sh_offset as usize), - ) - }) - .collect(); - - Ok(Self { contents, regions }) - } - - pub fn read(&self, addr: u32, data: &mut [u8]) -> Option<()> { - if let Some((&base, &(size, offset))) = - self.regions.range(..=addr).next_back() - && base <= addr - && base + size > addr - { - let start = (addr - base) as usize; - let roffs = offset + start; - - if start + data.len() <= size as usize { - data.copy_from_slice(&self.contents[roffs..roffs + data.len()]); - - return Some(()); - } - - let len = (size as usize) - start; - data[..len].copy_from_slice(&self.contents[roffs..roffs + len]); - - return self.read(addr + len as u32, &mut data[len..]); - } - - None - } -} +// Re-export for convenience +pub type HubrisDataMap = datamap::OwnedDataMap; #[derive(Debug, Deserialize)] pub struct HubrisFlashMeta { @@ -3648,6 +3582,33 @@ impl HubrisArchive { pub fn wants_reset_handoff_token(&self) -> bool { self.manifest.features.iter().any(|s| s == "measurement-handoff") } + + /// Builds a map from flash contents + pub fn flash_map(&self) -> Result { + // + // We want to read in the "final.elf" from our archive and use that + // to determine the memory that constitutes flash. + // + let contents = self.hubris_archive.extract_file("img/final.elf")?; + + let elf = Elf::parse(&contents).map_err(|e| { + anyhow!("failed to parse final.elf as an ELF file: {}", e) + })?; + + let mut map = HubrisDataMap::new(); + for shdr in elf.section_headers.iter().filter(|shdr| { + shdr.sh_type == goblin::elf::section_header::SHT_PROGBITS + }) { + map.insert( + shdr.sh_addr as u32, + contents[shdr.sh_offset as usize..][..shdr.sh_size as usize] + .to_vec(), + )?; + } + map.repack(); + + Ok(map) + } } /// Loader for a single ELF file diff --git a/humility-core/src/lib.rs b/humility-core/src/lib.rs index 4eef5ae1c..6a73720b2 100644 --- a/humility-core/src/lib.rs +++ b/humility-core/src/lib.rs @@ -1,10 +1,9 @@ // 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/. -pub mod archive; pub mod core; -pub mod dump; pub mod hubris; +pub mod mem; pub mod reflect; // Re-export `humility_log`, so clients don't need to import it (or `slog`) diff --git a/humility-core/src/dump.rs b/humility-core/src/mem.rs similarity index 51% rename from humility-core/src/dump.rs rename to humility-core/src/mem.rs index 9efa9339a..905fff916 100644 --- a/humility-core/src/dump.rs +++ b/humility-core/src/mem.rs @@ -2,27 +2,111 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. -use crate::core::Core; -use crate::hubris::OXIDE_NT_HUBRIS_REGISTERS; +use crate::{ + core::Core, + hubris::{HubrisArchive, HubrisDataMap, OXIDE_NT_HUBRIS_REGISTERS}, +}; use anyhow::{Context, Result, anyhow, bail}; use goblin::elf::Elf; use humility_arch_arm::ARMRegister; use num_traits::FromPrimitive; -use std::collections::{BTreeMap, HashMap}; -use std::fs::File; -use std::io::Read; - -pub struct DumpCore { - contents: Vec, - regions: BTreeMap, +use std::{collections::HashMap, fs::File, io::Read}; + +/// A core stored entirely in host memory +/// +/// This may include a [`HubrisDataMap`] containing data from flash, a separate +/// [`HubrisDataMap`] storing other data from memory, and a map of registers. +pub struct InMemoryCore { + /// Flash contents, loaded from an archive + /// + /// When reading, this is checked before `mem` + flash: Option, + + /// Memory contents, loaded from other sources + /// + /// Note that when a dump is loaded, `mem` includes both flash and RAM + /// (instead of storing data separately in `flash` and `mem`). This only + /// affects error messages. + mem: Option, + + /// Register values (if present) registers: Option>, } -impl DumpCore { - pub(crate) fn new(dump: &str) -> Result { - let mut file = File::open(dump)?; - let mut regions = BTreeMap::new(); +impl Core for InMemoryCore { + fn is_dump(&self) -> bool { + true + } + + fn is_archive(&self) -> bool { + // An archive dump only has the flash data available + self.flash.is_some() && self.mem.is_none() && self.registers.is_none() + } + + fn is_net(&self) -> bool { + false + } + + fn read_8(&mut self, addr: u32, data: &mut [u8]) -> Result<()> { + if self.flash.as_ref().is_some_and(|f| f.read_into(addr, data).is_ok()) + { + return Ok(()); + } + + if self.mem.as_ref().is_some_and(|f| f.read_into(addr, data).is_ok()) { + return Ok(()); + } + let r = format!("read of {} bytes failed", data.len()); + match (self.flash.is_some(), self.mem.is_some()) { + (true, true) => { + bail!("{r}: could not find addr {addr:#08x} in flash or memory") + } + (true, false) => { + bail!("{r}: could not find addr {addr:#08x} in flash") + } + (false, true) => { + bail!("{r}: could not find addr {addr:#08x} in memory") + } + (false, false) => { + bail!("{r}: core contains neither flash nor memory") + } + } + } + + fn read_reg(&mut self, reg: ARMRegister) -> Result { + match &self.registers { + Some(regs) => { + if let Some(val) = regs.get(®) { + Ok(*val) + } else { + bail!("register {} not found in in-memory core", reg); + } + } + None => bail!("in-memory core does not include register info"), + } + } + + fn write_word_32(&mut self, _addr: u32, _data: u32) -> Result<()> { + bail!("cannot write a word on an in-memory core"); + } + + fn write_8(&mut self, _addr: u32, _data: &[u8]) -> Result<()> { + bail!("cannot write a byte on an in-memory core"); + } + + fn halt(&mut self) -> Result<()> { + Ok(()) + } + + fn run(&mut self) -> Result<()> { + Ok(()) + } +} + +impl InMemoryCore { + pub fn from_dump(dump: &str) -> Result { + let mut file = File::open(dump)?; let mut contents = Vec::new(); file.read_to_end(&mut contents)?; @@ -30,16 +114,18 @@ impl DumpCore { anyhow!("failed to parse {} as an ELF file: {}", dump, e) })?; + let mut mem = HubrisDataMap::new(); for phdr in elf.program_headers.iter() { if phdr.p_type != goblin::elf::program_header::PT_LOAD { continue; } - - regions.insert( + mem.insert( phdr.p_vaddr as u32, - (phdr.p_memsz as u32, phdr.p_offset as usize), - ); + contents[phdr.p_offset as usize..][..phdr.p_memsz as usize] + .to_vec(), + )?; } + mem.repack(); let mut registers = None; if let Some(notes) = elf.iter_note_headers(&contents) { @@ -57,29 +143,37 @@ impl DumpCore { } } - Ok(Self { contents, regions, registers }) + Ok(Self { flash: None, mem: Some(mem), registers }) } - fn check_offset(&self, addr: u32, rsize: usize, offs: usize) -> Result<()> { - if rsize + offs <= self.contents.len() { - return Ok(()); + pub fn from_archive(archive: &HubrisArchive) -> Result { + Ok(Self { + flash: Some(archive.flash_map()?), + mem: None, + registers: None, + }) + } + + pub fn add_ram_region( + &mut self, + addr: u32, + contents: Vec, + ) -> Result<(), datamap::InsertError> { + if self.mem.is_none() { + self.mem = Some(HubrisDataMap::new()) } + self.mem.as_mut().unwrap().insert(addr, contents) + } - // - // This really shouldn't happen, as it means that we have a defined - // region in a program header for memory that wasn't in fact dumped. - // Still, this might occur if the dump is truncated or otherwise - // corrupt; offer a message pointing in that direction. - // - bail!( - "0x{:x} is valid, but offset in dump \ - (0x{:x}) + size (0x{:x}) exceeds max (0x{:x}); \ - is the dump truncated or otherwise corrupt?", - addr, - offs, - rsize, - self.contents.len() - ); + pub fn add_register(&mut self, reg: ARMRegister, val: u32) { + if self.registers.is_none() { + self.registers = Some(HashMap::new()); + } + self.registers.as_mut().unwrap().insert(reg, val); + } + + pub fn from_flash_map(flash: HubrisDataMap) -> Self { + Self { flash: Some(flash), mem: None, registers: None } } } @@ -114,79 +208,3 @@ fn load_registers(r: &[u8]) -> Result> { Ok(registers) } - -#[rustfmt::skip::macros(bail)] -impl Core for DumpCore { - fn read_8(&mut self, addr: u32, data: &mut [u8]) -> Result<()> { - let rsize = data.len(); - - if let Some((&base, &(size, offset))) = - self.regions.range(..=addr).next_back() - && base <= addr - && addr < (base + size) - { - if (addr - base) + rsize as u32 > size { - // - // The memory we want to read starts in this region but - // exceeds its bounds -- but if all of the memory we want - // is in fact represented in the dump, we don't want to - // return failure! We recurse into reading whatever won't - // be satisfied by this region, and (if that succeeds) - // fall through into reading this one. - // - let next = (size - (addr - base)) as usize; - - if next >= rsize - || self.read_8(base + size, &mut data[next..]).is_err() - { - bail!( - "0x{:x} is valid, but relative to base (0x{:x}), \ - offset (0x{:x}) exceeds max (0x{:x})", - addr, base, (addr - base) + rsize as u32, size - ); - } - } - - let offs = offset + (addr - base) as usize; - self.check_offset(addr, rsize, offs)?; - - data[..rsize].copy_from_slice(&self.contents[offs..rsize + offs]); - return Ok(()); - } - - bail!("read of {} bytes from invalid address: 0x{:x}", rsize, addr); - } - - fn read_reg(&mut self, reg: ARMRegister) -> Result { - match &self.registers { - Some(regs) => { - if let Some(val) = regs.get(®) { - Ok(*val) - } else { - bail!("register {} not found in dump", reg); - } - } - None => bail!("dump does not include register info"), - } - } - - fn write_word_32(&mut self, _addr: u32, _data: u32) -> Result<()> { - bail!("cannot write a word on a dump"); - } - - fn write_8(&mut self, _addr: u32, _data: &[u8]) -> Result<()> { - bail!("cannot write a byte on a dump"); - } - - fn halt(&mut self) -> Result<()> { - Ok(()) - } - - fn run(&mut self) -> Result<()> { - Ok(()) - } - - fn is_dump(&self) -> bool { - true - } -} diff --git a/humility-dump-agent/src/lib.rs b/humility-dump-agent/src/lib.rs index d36bc686b..14713d31c 100644 --- a/humility-dump-agent/src/lib.rs +++ b/humility-dump-agent/src/lib.rs @@ -19,8 +19,8 @@ use anyhow::{Context, Result, anyhow, bail}; use core::mem::size_of; use humility::{ core::Core, - hubris::HubrisFlashMap, log::{Logger, info}, + mem::InMemoryCore, }; use humility_arch_arm::ARMRegister; use humpty::{ @@ -30,10 +30,7 @@ use humpty::{ use indexmap::IndexMap; use indicatif::{HumanBytes, HumanDuration, ProgressBar, ProgressStyle}; use num_traits::FromPrimitive; -use std::{ - collections::{BTreeMap, HashMap}, - time::Instant, -}; +use std::time::Instant; use zerocopy::FromBytes; mod hiffy; @@ -136,209 +133,105 @@ impl DumpBreakdown { } } -// -// When using the dump agent, we create our own ersatz Core -// -pub struct DumpAgentCore { - flash: HubrisFlashMap, - ram_regions: BTreeMap>, - registers: HashMap, -} - -impl DumpAgentCore { - pub fn new(flash: HubrisFlashMap) -> DumpAgentCore { - Self { - flash, - ram_regions: Default::default(), - registers: Default::default(), - } - } - - pub fn add_ram_region(&mut self, addr: u32, contents: Vec) { - self.ram_regions.insert(addr, contents); - } - - pub fn add_register(&mut self, reg: ARMRegister, val: u32) { - self.registers.insert(reg, val); +fn process_dump( + out: &mut InMemoryCore, + headers: Vec, + total: u32, + dump: &[u8], + task: Option, +) -> Result { + let header = headers[0]; + let nsegments = header.nsegments; + let mut offset = nsegments as usize * size_of::(); + let mut breakdown = DumpBreakdown::new(headers, total); + + if offset > dump.len() { + bail!("in situ dump is short; missing {nsegments} segments"); } - fn read_flash(&self, addr: u32, data: &mut [u8]) -> Result<()> { - self.flash - .read(addr, data) - .ok_or_else(|| anyhow!("address 0x{:08x} not found", addr)) + if offset == dump.len() { + bail!("in situ dump is empty"); } - fn read(&mut self, addr: u32, data: &mut [u8]) -> Result<()> { - if let Some((&base, contents)) = - self.ram_regions.range(..=addr).next_back() - { - if base > addr || base + (contents.len() as u32) <= addr { - // - // We don't have this in RAM -- pull it out of flash. - // - return self.read_flash(addr, data); - } - - let start = (addr - base) as usize; - - if start + data.len() <= contents.len() { - // - // This region -- and only this region -- contains our RAM. - // Copy it and leave. - // - data.copy_from_slice(&contents[start..start + data.len()]); - return Ok(()); + while offset < dump.len() { + let segment = match DumpSegment::from(&dump[offset..]) { + Some(segment) => segment, + None => { + bail!("short read at offset {offset}"); } + }; - // - // This region contains our RAM, but there is more. Copy the bit - // that we want and recurse. - // - let len = contents.len() - start; - data[..len].copy_from_slice(&contents[start..contents.len()]); - self.read(addr + len as u32, &mut data[len..]) - } else { - self.read_flash(addr, data) - } - } - - fn process_dump( - &mut self, - headers: Vec, - total: u32, - dump: &[u8], - task: Option, - ) -> Result { - let header = headers[0]; - let nsegments = header.nsegments; - let mut offset = nsegments as usize * size_of::(); - let mut breakdown = DumpBreakdown::new(headers, total); - - if offset > dump.len() { - bail!("in situ dump is short; missing {nsegments} segments"); - } - - if offset == dump.len() { - bail!("in situ dump is empty"); - } - - while offset < dump.len() { - let segment = match DumpSegment::from(&dump[offset..]) { - Some(segment) => segment, - None => { - bail!("short read at offset {offset}"); - } - }; - - match segment { - DumpSegment::Task(t) => { - match task { - None => { - bail!("found unexpected task {t:?}"); - } - Some(task) if task != t => { - bail!( - "task mismatch: found {t:?}, expected {task:?}" - ); - } - _ => {} + match segment { + DumpSegment::Task(t) => { + match task { + None => { + bail!("found unexpected task {t:?}"); } - - offset += size_of::(); - continue; + Some(task) if task != t => { + bail!("task mismatch: found {t:?}, expected {task:?}"); + } + _ => {} } - DumpSegment::Register(reg) => { - // - // These are register values; slurp them and continue. - // - if let Some(register) = ARMRegister::from_u16(reg.register) - { - self.add_register(register, reg.value); - } else { - let r = reg.register; - bail!( - "unrecognized register {r:#x} at offset {offset}" - ); - } + offset += size_of::(); + continue; + } - breakdown.registers += size_of::() as u32; - offset += size_of::(); - continue; + DumpSegment::Register(reg) => { + // + // These are register values; slurp them and continue. + // + if let Some(register) = ARMRegister::from_u16(reg.register) { + out.add_register(register, reg.value); + } else { + let r = reg.register; + bail!("unrecognized register {r:#x} at offset {offset}"); } - DumpSegment::Data(data) => { - offset += size_of::(); + breakdown.registers += size_of::() as u32; + offset += size_of::(); + continue; + } - let len = data.uncompressed_length as usize; + DumpSegment::Data(data) => { + offset += size_of::(); - let mut contents = vec![0; len]; - let limit = offset + data.compressed_length as usize; + let len = data.uncompressed_length as usize; - humpty::DumpLzss::decompress_stack( - lzss::SliceReader::new(&dump[offset..limit]), - lzss::SliceWriter::new(&mut contents), - )?; + let mut contents = vec![0; len]; + let limit = offset + data.compressed_length as usize; - self.add_ram_region( - data.address, - contents[0..len].to_vec(), - ); - offset = limit; + humpty::DumpLzss::decompress_stack( + lzss::SliceReader::new(&dump[offset..limit]), + lzss::SliceWriter::new(&mut contents), + )?; - while offset < dump.len() - && dump[offset] == humpty::DUMP_SEGMENT_PAD - { - offset += 1; - } + contents.resize(len, 0u8); + out.add_ram_region(data.address, contents)?; + offset = limit; - breakdown.add_data( - data.compressed_length, - data.uncompressed_length, - offset - limit, - ); + while offset < dump.len() + && dump[offset] == humpty::DUMP_SEGMENT_PAD + { + offset += 1; } - DumpSegment::Unknown(signature) => { - bail!( - "unrecognized data with signature \ - {signature:x?} at offset {offset}" - ); - } + breakdown.add_data( + data.compressed_length, + data.uncompressed_length, + offset - limit, + ); } - } - Ok(breakdown) - } -} - -impl Core for DumpAgentCore { - fn read_8(&mut self, addr: u32, data: &mut [u8]) -> Result<()> { - self.read(addr, data) - } - - fn read_reg(&mut self, reg: ARMRegister) -> Result { - match self.registers.get(®) { - Some(val) => Ok(*val), - None => bail!("unexpected read from register {reg:?}"), + DumpSegment::Unknown(signature) => { + bail!( + "unrecognized data with signature \ + {signature:x?} at offset {offset}" + ); + } } } - - fn write_word_32(&mut self, _addr: u32, _data: u32) -> Result<()> { - bail!("cannot write a word over dump agent"); - } - - fn write_8(&mut self, _addr: u32, _data: &[u8]) -> Result<()> { - bail!("cannot write a byte over dump agent"); - } - - fn halt(&mut self) -> Result<()> { - bail!("unexpected call to halt"); - } - - fn run(&mut self) -> Result<()> { - bail!("unexpected call to run"); - } + Ok(breakdown) } //////////////////////////////////////////////////////////////////////////////// @@ -540,7 +433,7 @@ pub trait DumpAgentExt { fn read_dump( &mut self, area: Option, - out: &mut DumpAgentCore, + out: &mut InMemoryCore, verbose: bool, log: &Logger, ) -> Result<(Option, DumpBreakdown)> { @@ -636,7 +529,7 @@ pub trait DumpAgentExt { // // We have a dump! On to processing... // - let breakdown = out.process_dump(headers, total, &contents, task)?; + let breakdown = process_dump(out, headers, total, &contents, task)?; Ok((task, breakdown)) } diff --git a/humility-dump/src/lib.rs b/humility-dump/src/lib.rs index 504e11bf1..ad8043f4e 100644 --- a/humility-dump/src/lib.rs +++ b/humility-dump/src/lib.rs @@ -9,11 +9,11 @@ #![warn(missing_docs)] use humility::core::Core; -use humility::hubris::{HubrisArchive, HubrisFlashMap, HubrisTask}; +use humility::hubris::{HubrisArchive, HubrisTask}; use humility::log::{Logger, warn}; +use humility::mem::InMemoryCore; use humility_dump_agent::{ - DumpAgent, DumpAgentCore, DumpAgentExt, DumpArea, HiffyDumpAgent, - UdpDumpAgent, task_areas, + DumpAgent, DumpAgentExt, DumpArea, HiffyDumpAgent, UdpDumpAgent, task_areas, }; use std::fs::OpenOptions; use std::path::{Path, PathBuf}; @@ -38,9 +38,9 @@ pub enum DumpError { /// Tried to extract a system dump when a task dump was expected #[error("found system dump, expected task dump(s)")] SystemDump, - /// Error creating hubris flash map - #[error("error creating hubris flash map")] - FlashMap(#[source] anyhow::Error), + /// Error creating in-memory core + #[error("Error creating in-memory core")] + InMemoryCore(#[source] anyhow::Error), /// Error reading dump via agent #[error("error reading dump")] ReadDump(#[source] anyhow::Error), @@ -203,9 +203,8 @@ pub fn extract_all_task_dumps( .open(&filename) .map_err(DumpError::OpenError)?; - let mut out = DumpAgentCore::new( - HubrisFlashMap::new(hubris).map_err(DumpError::FlashMap)?, - ); + let mut out = InMemoryCore::from_archive(hubris) + .map_err(DumpError::InMemoryCore)?; let started = Some(Instant::now()); let task = agent @@ -243,9 +242,8 @@ fn extract_system_dump_via_agent( started: std::time::Instant, log: &Logger, ) -> Result<(), DumpError> { - let mut out = DumpAgentCore::new( - HubrisFlashMap::new(hubris).map_err(DumpError::FlashMap)?, - ); + let mut out = + InMemoryCore::from_archive(hubris).map_err(DumpError::InMemoryCore)?; let _ = agent .read_dump(None, &mut out, false, log) diff --git a/humility-net/src/lib.rs b/humility-net/src/lib.rs index fbd242c05..613fe9d6b 100644 --- a/humility-net/src/lib.rs +++ b/humility-net/src/lib.rs @@ -8,14 +8,13 @@ use anyhow::{Context, Result, anyhow, bail}; use humility::{ core::{Core, NetAgent}, hubris::{ - HubrisArchive, HubrisFlashMap, HubrisRegion, HubrisSocket, HubrisTask, + HubrisArchive, HubrisDataMap, HubrisRegion, HubrisSocket, HubrisTask, }, log::{Logger, info, warn}, + mem::InMemoryCore, }; use humility_arch_arm::ARMRegister; -use humility_dump_agent::{ - DumpAgent, DumpAgentCore, DumpAgentExt, DumpArea, UdpDumpAgent, -}; +use humility_dump_agent::{DumpAgent, DumpAgentExt, DumpArea, UdpDumpAgent}; use std::{ fmt, net::{Ipv6Addr, ToSocketAddrs, UdpSocket}, @@ -40,7 +39,7 @@ use std::{ pub struct NetCore { /// Socket to communicate with `udprpc`, or `None` if it's not present udprpc_socket: Option, - flash: HubrisFlashMap, + flash: HubrisDataMap, /// Socket to communicate with the dump agent, or `None` if it's not present dump_agent_socket: Option, @@ -104,7 +103,7 @@ impl NetCore { udprpc_socket, dump_agent_socket, hiffy_socket, - flash: HubrisFlashMap::new(hubris)?, + flash: hubris.flash_map()?, ram: None, // filled in below imageid: hubris.image_id().to_owned(), log: log.clone(), @@ -179,7 +178,7 @@ impl NetCore { } fn read(&mut self, addr: u32, data: &mut [u8]) -> Result<()> { - if self.flash.read(addr, data).is_some() { + if self.flash.read_into(addr, data).is_ok() { Ok(()) } else { self.read_ram(addr, data).with_context(|| format!( @@ -197,7 +196,7 @@ impl NetCore { bail!("dump agent cannot read RAM; is your Hubris archive too old?") }; - let mut agent_core = DumpAgentCore::new(self.flash.clone()); + let mut agent_core = InMemoryCore::from_flash_map(self.flash.clone()); let image_id = self.imageid.clone(); let log = self.log.clone(); From c22a7d8da47f20f9cee16e93e2509ab5c4f150ea Mon Sep 17 00:00:00 2001 From: Matt Keeter Date: Mon, 29 Jun 2026 10:53:41 -0400 Subject: [PATCH 2/3] Review comments --- cmd/diagnose/src/lib.rs | 2 +- cmd/registers/src/lib.rs | 4 ++-- cmd/sensors/src/lib.rs | 4 ++-- cmd/spd/src/lib.rs | 2 +- humility-bin/src/main.rs | 2 +- humility-cli/src/lib.rs | 2 +- humility-core/src/core.rs | 8 +++++--- humility-core/src/hubris.rs | 4 ++-- humility-core/src/mem.rs | 8 ++++---- 9 files changed, 19 insertions(+), 17 deletions(-) diff --git a/cmd/diagnose/src/lib.rs b/cmd/diagnose/src/lib.rs index 30a4c5a4e..56d74a3e6 100644 --- a/cmd/diagnose/src/lib.rs +++ b/cmd/diagnose/src/lib.rs @@ -152,7 +152,7 @@ fn diagnose( } } - if core.is_dump() { + if core.is_memory_core() { section("This Is A Core Dump"); println!("You're running this on a dump; that's all we can do."); println!("Connect to a live system for more output."); diff --git a/cmd/registers/src/lib.rs b/cmd/registers/src/lib.rs index 8acc2386b..2e7c030c3 100644 --- a/cmd/registers/src/lib.rs +++ b/cmd/registers/src/lib.rs @@ -240,7 +240,7 @@ fn registers( let core = &mut *context.cli.attach_live_or_dump(hubris.as_ref(), None)?; let mut regs = BTreeMap::new(); - if subargs.fp && !core.is_dump() { + if subargs.fp && !core.is_memory_core() { let mvfr = MVFR0::read(core)?; if mvfr.simd_registers() != 1 { @@ -346,7 +346,7 @@ fn registers( // kernel stacks; in classic Humility fashion, phrase // our hunch in the form of a question. // - if core.is_dump() && task == HubrisTask::Kernel { + if core.is_memory_core() && task == HubrisTask::Kernel { info!( log, "kernel stack missing; \ diff --git a/cmd/sensors/src/lib.rs b/cmd/sensors/src/lib.rs index aae26fa42..94b83597e 100644 --- a/cmd/sensors/src/lib.rs +++ b/cmd/sensors/src/lib.rs @@ -625,7 +625,7 @@ fn sensors(subargs: SensorsArgs, context: &mut ExecutionContext) -> Result<()> { let core = &mut *context.cli.attach_live_or_dump_booted(hubris)?; let mut reader: Box = match subargs.backend { Some(Backend::Hiffy) => { - if core.is_dump() { + if core.is_memory_core() { bail!("cannot use hiffy backend on dump"); } let context = HiffyContext::new(hubris, core, timeout, log)?; @@ -634,7 +634,7 @@ fn sensors(subargs: SensorsArgs, context: &mut ExecutionContext) -> Result<()> { Some(Backend::Readmem) => { Box::new(RamSensorReader::new(hubris, &sensors)?) } - None if core.is_dump() => { + None if core.is_memory_core() => { Box::new(RamSensorReader::new(hubris, &sensors)?) } None => match HiffyContext::new(hubris, core, timeout, log) { diff --git a/cmd/spd/src/lib.rs b/cmd/spd/src/lib.rs index 834f1de70..42456eb9e 100644 --- a/cmd/spd/src/lib.rs +++ b/cmd/spd/src/lib.rs @@ -358,7 +358,7 @@ fn spd(subargs: SpdArgs, context: &mut ExecutionContext) -> Result<()> { } Ok(()) - } else if core.is_dump() { + } else if core.is_memory_core() { bail!("cannot specify bus/controller on a dump"); } else { // At this point, the user wants to poll DDR SPDs directly over I2C. We diff --git a/humility-bin/src/main.rs b/humility-bin/src/main.rs index edcd34c59..68ddf7bd8 100644 --- a/humility-bin/src/main.rs +++ b/humility-bin/src/main.rs @@ -130,7 +130,7 @@ fn main() -> std::process::ExitCode { } else if let Ok(e) = env::var("HUMILITY_TARGET") { cli.target = Some(e); } else if let Ok(e) = env::var("HUMILITY_DUMP") { - cli.dump = Some(e); + cli.dump = Some(std::path::PathBuf::from(e)); } } diff --git a/humility-cli/src/lib.rs b/humility-cli/src/lib.rs index 9cdd07d35..4e22e8ca2 100644 --- a/humility-cli/src/lib.rs +++ b/humility-cli/src/lib.rs @@ -60,7 +60,7 @@ pub struct Cli { /// variable. Run "humility doc" for more information on debugging /// from a hubris dump. #[clap(long, short, group = "hubris")] - pub dump: Option, + pub dump: Option, /// IP address of remote Hubris instance. This may also be set via the /// HUMILITY_IP environment variable. Run "humility doc" for more diff --git a/humility-core/src/core.rs b/humility-core/src/core.rs index dbc6b5488..f9a34c616 100644 --- a/humility-core/src/core.rs +++ b/humility-core/src/core.rs @@ -8,7 +8,6 @@ use crate::hubris::*; use crate::mem::InMemoryCore; use humility_arch_arm::ARMRegister; use humility_log::{Logger, info}; -use std::str; use std::time::Duration; use thiserror::Error; @@ -21,7 +20,7 @@ pub trait Core { fn halt(&mut self) -> Result<()>; fn run(&mut self) -> Result<()>; - fn is_dump(&self) -> bool { + fn is_memory_core(&self) -> bool { false } @@ -84,7 +83,10 @@ pub enum NetAgent { Hiffy, } -pub fn attach_dump(dump: &str, log: &Logger) -> Result { +pub fn attach_dump( + dump: &std::path::PathBuf, + log: &Logger, +) -> Result { let core = InMemoryCore::from_dump(dump)?; info!(log, "attached to dump"); Ok(core) diff --git a/humility-core/src/hubris.rs b/humility-core/src/hubris.rs index a1a201faf..e894965fc 100644 --- a/humility-core/src/hubris.rs +++ b/humility-core/src/hubris.rs @@ -1606,12 +1606,12 @@ impl HubrisArchive { /// Returns a tuple of `(raw archive, dump task)`; the second field is /// `Some(..)` if this is a single-task dump. pub fn load_dump( - dumpfile: &str, + dumpfile: &std::path::PathBuf, ) -> Result<(RawHubrisArchive, Option)> { // We expect the dump to be an ELF core dump. let contents = fs::read(dumpfile)?; let elf = Elf::parse(&contents).map_err(|e| { - anyhow!("failed to parse {} as an ELF file: {}", dumpfile, e) + anyhow!("failed to parse {} as an ELF file: {}", dumpfile.display(), e) })?; let mut task_dump = None; diff --git a/humility-core/src/mem.rs b/humility-core/src/mem.rs index 905fff916..ced51c7a2 100644 --- a/humility-core/src/mem.rs +++ b/humility-core/src/mem.rs @@ -12,7 +12,7 @@ use humility_arch_arm::ARMRegister; use num_traits::FromPrimitive; use std::{collections::HashMap, fs::File, io::Read}; -/// A core stored entirely in host memory +/// A core stored entirely in the memory of the computer running Humility /// /// This may include a [`HubrisDataMap`] containing data from flash, a separate /// [`HubrisDataMap`] storing other data from memory, and a map of registers. @@ -34,7 +34,7 @@ pub struct InMemoryCore { } impl Core for InMemoryCore { - fn is_dump(&self) -> bool { + fn is_memory_core(&self) -> bool { true } @@ -105,13 +105,13 @@ impl Core for InMemoryCore { } impl InMemoryCore { - pub fn from_dump(dump: &str) -> Result { + pub fn from_dump(dump: &std::path::PathBuf) -> Result { let mut file = File::open(dump)?; let mut contents = Vec::new(); file.read_to_end(&mut contents)?; let elf = Elf::parse(&contents).map_err(|e| { - anyhow!("failed to parse {} as an ELF file: {}", dump, e) + anyhow!("failed to parse {} as an ELF file: {}", dump.display(), e) })?; let mut mem = HubrisDataMap::new(); From a3c7693067e29c7547c32fdf81a1d78af5fd11bd Mon Sep 17 00:00:00 2001 From: Matt Keeter Date: Tue, 30 Jun 2026 15:50:40 -0400 Subject: [PATCH 3/3] Thanks, Copilot --- humility-dump/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/humility-dump/src/lib.rs b/humility-dump/src/lib.rs index ad8043f4e..d30bdc5f1 100644 --- a/humility-dump/src/lib.rs +++ b/humility-dump/src/lib.rs @@ -39,7 +39,7 @@ pub enum DumpError { #[error("found system dump, expected task dump(s)")] SystemDump, /// Error creating in-memory core - #[error("Error creating in-memory core")] + #[error("error creating in-memory core")] InMemoryCore(#[source] anyhow::Error), /// Error reading dump via agent #[error("error reading dump")]