From cbf3c8f0f9467d10b899de967240e5cd2209a88f Mon Sep 17 00:00:00 2001 From: Matt Keeter Date: Thu, 11 Jun 2026 10:00:32 -0400 Subject: [PATCH 1/7] Move Core::vid_pid to ProbeCore --- cmd/hydrate/src/lib.rs | 3 --- humility-core/src/core.rs | 4 ---- humility-probes-core/src/probe_rs.rs | 8 ++++---- humility-probes-core/src/unattached.rs | 4 ---- 4 files changed, 4 insertions(+), 15 deletions(-) diff --git a/cmd/hydrate/src/lib.rs b/cmd/hydrate/src/lib.rs index df4ffc3bb..2814d77b7 100644 --- a/cmd/hydrate/src/lib.rs +++ b/cmd/hydrate/src/lib.rs @@ -83,9 +83,6 @@ impl humility::core::Core for DryCore { fn is_net(&self) -> bool { false } - fn vid_pid(&self) -> Option<(u16, u16)> { - None - } fn read_8(&mut self, addr: u32, data: &mut [u8]) -> Result<()> { if self.flash.read(addr, data).is_some() { diff --git a/humility-core/src/core.rs b/humility-core/src/core.rs index f40a91a84..0df05a8e3 100644 --- a/humility-core/src/core.rs +++ b/humility-core/src/core.rs @@ -17,10 +17,6 @@ use thiserror::Error; pub trait Core { fn info(&self) -> (String, Option); - fn vid_pid(&self) -> Option<(u16, u16)> { - None - } - fn read_8(&mut self, addr: u32, data: &mut [u8]) -> Result<()>; fn read_reg(&mut self, reg: ARMRegister) -> Result; fn write_reg(&mut self, reg: ARMRegister, value: u32) -> Result<()>; diff --git a/humility-probes-core/src/probe_rs.rs b/humility-probes-core/src/probe_rs.rs index 5deb3c6ad..f9241c4d0 100644 --- a/humility-probes-core/src/probe_rs.rs +++ b/humility-probes-core/src/probe_rs.rs @@ -238,10 +238,6 @@ impl Core for ProbeCore { (ident, self.serial_number.clone()) } - fn vid_pid(&self) -> Option<(u16, u16)> { - Some((self.vendor_id, self.product_id)) - } - fn read_word_32(&mut self, addr: u32) -> Result { trace!(self.log, "reading word at {:x}", addr); let mut rval = 0; @@ -484,4 +480,8 @@ impl ProbeCore { self.halted = true; Ok(()) } + + pub fn vid_pid(&self) -> Option<(u16, u16)> { + Some((self.vendor_id, self.product_id)) + } } diff --git a/humility-probes-core/src/unattached.rs b/humility-probes-core/src/unattached.rs index ce1c522cd..d6e7cc867 100644 --- a/humility-probes-core/src/unattached.rs +++ b/humility-probes-core/src/unattached.rs @@ -36,10 +36,6 @@ impl Core for UnattachedCore { (ident, self.serial_number.clone()) } - fn vid_pid(&self) -> Option<(u16, u16)> { - Some((self.vendor_id, self.product_id)) - } - fn read_8(&mut self, _addr: u32, _data: &mut [u8]) -> Result<()> { bail!("Core::read_8 unimplemented when unattached!"); } From 243bf37d4b83c682f0f83abd4bc14d0e5d234429 Mon Sep 17 00:00:00 2001 From: Matt Keeter Date: Thu, 11 Jun 2026 10:02:12 -0400 Subject: [PATCH 2/7] Move Core::step to ProbeCore --- cmd/hydrate/src/lib.rs | 1 - humility-core/src/archive.rs | 4 ---- humility-core/src/core.rs | 1 - humility-core/src/dump.rs | 4 ---- humility-dump-agent/src/lib.rs | 4 ---- humility-net-core/src/lib.rs | 4 ---- humility-probes-core/src/probe_rs.rs | 12 ++++++------ humility-probes-core/src/unattached.rs | 4 ---- 8 files changed, 6 insertions(+), 28 deletions(-) diff --git a/cmd/hydrate/src/lib.rs b/cmd/hydrate/src/lib.rs index 2814d77b7..5be345262 100644 --- a/cmd/hydrate/src/lib.rs +++ b/cmd/hydrate/src/lib.rs @@ -63,7 +63,6 @@ macro_rules! unsupported{ impl humility::core::Core for DryCore { unsupported!(run()); unsupported!(halt()); - unsupported!(step()); unsupported!(write_8(_addr: u32, _data: &[u8])); unsupported!(op_done()); unsupported!(op_start()); diff --git a/humility-core/src/archive.rs b/humility-core/src/archive.rs index 036f06c16..fa438ab57 100644 --- a/humility-core/src/archive.rs +++ b/humility-core/src/archive.rs @@ -64,8 +64,4 @@ impl Core for ArchiveCore { fn run(&mut self) -> Result<()> { Ok(()) } - - fn step(&mut self) -> Result<()> { - bail!("can't step an archive"); - } } diff --git a/humility-core/src/core.rs b/humility-core/src/core.rs index 0df05a8e3..ced49a9fc 100644 --- a/humility-core/src/core.rs +++ b/humility-core/src/core.rs @@ -25,7 +25,6 @@ pub trait Core { fn halt(&mut self) -> Result<()>; fn run(&mut self) -> Result<()>; - fn step(&mut self) -> Result<()>; fn is_dump(&self) -> bool { false } diff --git a/humility-core/src/dump.rs b/humility-core/src/dump.rs index 2955727ca..9b17dd8aa 100644 --- a/humility-core/src/dump.rs +++ b/humility-core/src/dump.rs @@ -194,10 +194,6 @@ impl Core for DumpCore { Ok(()) } - fn step(&mut self) -> Result<()> { - bail!("can't step a dump"); - } - fn is_dump(&self) -> bool { true } diff --git a/humility-dump-agent/src/lib.rs b/humility-dump-agent/src/lib.rs index ce7a0fd65..e4c6793d7 100644 --- a/humility-dump-agent/src/lib.rs +++ b/humility-dump-agent/src/lib.rs @@ -347,10 +347,6 @@ impl Core for DumpAgentCore { fn run(&mut self) -> Result<()> { bail!("unexpected call to run"); } - - fn step(&mut self) -> Result<()> { - bail!("can't step over dump agent"); - } } //////////////////////////////////////////////////////////////////////////////// diff --git a/humility-net-core/src/lib.rs b/humility-net-core/src/lib.rs index b30be791b..9e96112d6 100644 --- a/humility-net-core/src/lib.rs +++ b/humility-net-core/src/lib.rs @@ -371,10 +371,6 @@ impl Core for NetCore { fn run(&mut self) -> Result<()> { Ok(()) } - - fn step(&mut self) -> Result<()> { - bail!("can't step over network"); - } } pub fn attach_net( diff --git a/humility-probes-core/src/probe_rs.rs b/humility-probes-core/src/probe_rs.rs index f9241c4d0..fb70ea7fe 100644 --- a/humility-probes-core/src/probe_rs.rs +++ b/humility-probes-core/src/probe_rs.rs @@ -351,12 +351,6 @@ impl Core for ProbeCore { Ok(()) } - fn step(&mut self) -> Result<()> { - let mut core = self.session.core(0)?; - core.step()?; - Ok(()) - } - fn op_start(&mut self) -> Result<()> { self.halt()?; @@ -484,4 +478,10 @@ impl ProbeCore { pub fn vid_pid(&self) -> Option<(u16, u16)> { Some((self.vendor_id, self.product_id)) } + + pub fn step(&mut self) -> Result<()> { + let mut core = self.session.core(0)?; + core.step()?; + Ok(()) + } } diff --git a/humility-probes-core/src/unattached.rs b/humility-probes-core/src/unattached.rs index d6e7cc867..920fbe1b4 100644 --- a/humility-probes-core/src/unattached.rs +++ b/humility-probes-core/src/unattached.rs @@ -63,10 +63,6 @@ impl Core for UnattachedCore { fn run(&mut self) -> Result<()> { bail!("Core::run unimplemented when unattached!"); } - - fn step(&mut self) -> Result<()> { - bail!("Core::step unimplemented when unattached!"); - } } impl UnattachedCore { From 0e1f03ad6c59307a490fac355a4f98cac434ad97 Mon Sep 17 00:00:00 2001 From: Matt Keeter Date: Thu, 11 Jun 2026 10:06:50 -0400 Subject: [PATCH 3/7] Move Core::write_reg to ProbeCore --- Cargo.lock | 2 ++ cmd/hydrate/src/lib.rs | 1 - cmd/rebootleby/Cargo.toml | 3 ++- cmd/rebootleby/src/lib.rs | 3 ++- cmd/stmsecure/Cargo.toml | 1 + cmd/stmsecure/src/lib.rs | 25 ++++++++++++----------- humility-core/src/archive.rs | 4 ---- humility-core/src/core.rs | 1 - humility-core/src/dump.rs | 4 ---- humility-dump-agent/src/lib.rs | 4 ---- humility-net-core/src/lib.rs | 4 ---- humility-probes-core/src/probe_rs.rs | 28 +++++++++++++------------- humility-probes-core/src/unattached.rs | 4 ---- 13 files changed, 34 insertions(+), 50 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 24185ab41..0cfddae6f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2541,6 +2541,7 @@ dependencies = [ "humility-cli", "humility-core", "humility-cortex", + "humility-probes-core", "zip", ] @@ -2723,6 +2724,7 @@ dependencies = [ "humility-arch-arm", "humility-cli", "humility-core", + "humility-probes-core", "parse_int", ] diff --git a/cmd/hydrate/src/lib.rs b/cmd/hydrate/src/lib.rs index 5be345262..16f080d2e 100644 --- a/cmd/hydrate/src/lib.rs +++ b/cmd/hydrate/src/lib.rs @@ -67,7 +67,6 @@ impl humility::core::Core for DryCore { unsupported!(op_done()); unsupported!(op_start()); unsupported!(read_reg(_reg: ARMRegister) -> Result); - unsupported!(write_reg(_reg: ARMRegister, _value: u32)); unsupported!(write_word_32(_addr: u32, _data: u32)); fn info(&self) -> (String, Option) { diff --git a/cmd/rebootleby/Cargo.toml b/cmd/rebootleby/Cargo.toml index cf386c764..4e1fc37d8 100644 --- a/cmd/rebootleby/Cargo.toml +++ b/cmd/rebootleby/Cargo.toml @@ -7,8 +7,9 @@ description = "Do something kind of like embootleby to recover a device with bad [dependencies] humility.workspace = true humility-arch-arm.workspace = true -humility-cortex.workspace = true humility-cli.workspace = true +humility-cortex.workspace = true +humility-probes-core.workspace = true anyhow.workspace = true clap.workspace = true diff --git a/cmd/rebootleby/src/lib.rs b/cmd/rebootleby/src/lib.rs index b2e9fcfa4..0ad6f01c0 100644 --- a/cmd/rebootleby/src/lib.rs +++ b/cmd/rebootleby/src/lib.rs @@ -18,6 +18,7 @@ use humility::{ }; use humility_arch_arm::ARMRegister; use humility_cli::{ExecutionContext, humility_cmd}; +use humility_probes_core::ProbeCore; use std::io::Read; use std::ops::RangeInclusive; use std::path::PathBuf; @@ -151,7 +152,7 @@ const LOCKOUTSETTINGS: u32 = 0x0; const BANKENABLESETTINGS: u32 = 0xaa0; struct FlashHack<'a> { - core: &'a mut dyn Core, + core: &'a mut ProbeCore, log: Logger, } diff --git a/cmd/stmsecure/Cargo.toml b/cmd/stmsecure/Cargo.toml index d553537ad..840f84106 100644 --- a/cmd/stmsecure/Cargo.toml +++ b/cmd/stmsecure/Cargo.toml @@ -12,3 +12,4 @@ parse_int.workspace = true humility.workspace = true humility-arch-arm.workspace = true humility-cli.workspace = true +humility-probes-core.workspace = true diff --git a/cmd/stmsecure/src/lib.rs b/cmd/stmsecure/src/lib.rs index da0a5a95b..c91e430fa 100644 --- a/cmd/stmsecure/src/lib.rs +++ b/cmd/stmsecure/src/lib.rs @@ -32,6 +32,7 @@ use clap::Parser; use humility::core::Core; use humility_arch_arm::ARMRegister; use humility_cli::{ExecutionContext, humility_cmd}; +use humility_probes_core::ProbeCore; const FLASH_OPT_KEY1: u32 = 0x0819_2A3B; const FLASH_OPT_KEY2: u32 = 0x4C5D_6E7F; @@ -90,19 +91,19 @@ pub enum StmSecureCmd { SwapBanks, } -fn stmsecure_unlock_flash(core: &mut dyn Core) -> Result<()> { +fn stmsecure_unlock_flash(core: &mut ProbeCore) -> Result<()> { core.write_word_32(FLASH_KEYR1, FLASH_KEY1)?; core.write_word_32(FLASH_KEYR1, FLASH_KEY2)?; Ok(()) } -fn stmsecure_unlock_option(core: &mut dyn Core) -> Result<()> { +fn stmsecure_unlock_option(core: &mut ProbeCore) -> Result<()> { core.write_word_32(FLASH_OPT_KEYR, FLASH_OPT_KEY1)?; core.write_word_32(FLASH_OPT_KEYR, FLASH_OPT_KEY2)?; Ok(()) } -fn stmsecure_commit_option(core: &mut dyn Core) -> Result<()> { +fn stmsecure_commit_option(core: &mut ProbeCore) -> Result<()> { // set start bit core.write_word_32(FLASH_OPT_CR, 0x2)?; @@ -115,7 +116,7 @@ fn stmsecure_commit_option(core: &mut dyn Core) -> Result<()> { Ok(()) } -fn stmsecure_rdpset(core: &mut dyn Core) -> Result<()> { +fn stmsecure_rdpset(core: &mut ProbeCore) -> Result<()> { println!("setting rdp to level 1 (You will not be able to read the flash)"); stmsecure_unlock_option(core)?; let optsr = core.read_word_32(FLASH_OPTSR_CUR)?; @@ -125,13 +126,13 @@ fn stmsecure_rdpset(core: &mut dyn Core) -> Result<()> { Ok(()) } -fn stmsecure_rdpunset_nocommit(core: &mut dyn Core) -> Result<()> { +fn stmsecure_rdpunset_nocommit(core: &mut ProbeCore) -> Result<()> { let optsr = core.read_word_32(FLASH_OPTSR_CUR)?; core.write_word_32(FLASH_OPTSR_PRG, (optsr & !0x0000_ff00) | 0x0000_aa00)?; Ok(()) } -fn stmsecure_rdpunset(core: &mut dyn Core) -> Result<()> { +fn stmsecure_rdpunset(core: &mut ProbeCore) -> Result<()> { println!( "setting rdp level to 0. This may also erase the flash depending on your system settings!" @@ -143,7 +144,7 @@ fn stmsecure_rdpunset(core: &mut dyn Core) -> Result<()> { Ok(()) } -fn stmsecure_lockbit_set(core: &mut dyn Core) -> Result<()> { +fn stmsecure_lockbit_set(core: &mut ProbeCore) -> Result<()> { println!("Setting the secure option bit"); stmsecure_unlock_option(core)?; let optsr = core.read_word_32(FLASH_OPTSR_CUR)?; @@ -153,7 +154,7 @@ fn stmsecure_lockbit_set(core: &mut dyn Core) -> Result<()> { Ok(()) } -fn stmsecure_lockbit_unset(core: &mut dyn Core) -> Result<()> { +fn stmsecure_lockbit_unset(core: &mut ProbeCore) -> Result<()> { println!("Unsetting the secure option bit"); stmsecure_unlock_option(core)?; let optsr = core.read_word_32(FLASH_OPTSR_CUR)?; @@ -163,7 +164,7 @@ fn stmsecure_lockbit_unset(core: &mut dyn Core) -> Result<()> { Ok(()) } -fn stmsecure_status(core: &mut dyn Core) -> Result<()> { +fn stmsecure_status(core: &mut ProbeCore) -> Result<()> { let optsr = core.read_word_32(FLASH_OPTSR_CUR)?; let rdp = (optsr & 0x0000_ff00) >> 8; let sec_en = (optsr & 0x20_0000) == 0x20_0000; @@ -182,7 +183,7 @@ fn stmsecure_status(core: &mut dyn Core) -> Result<()> { } fn stmsecure_setsecureregion( - core: &mut dyn Core, + core: &mut ProbeCore, address: u32, size: u32, commit: bool, @@ -253,7 +254,7 @@ fn stmsecure_setsecureregion( Ok(()) } -fn stmsecure_unsetsecureregion(core: &mut dyn Core) -> Result<()> { +fn stmsecure_unsetsecureregion(core: &mut ProbeCore) -> Result<()> { println!("Unsetting the secure region. This will erase the bank!"); // This sequence is from the manual section 4.3.10 @@ -283,7 +284,7 @@ fn stmsecure_unsetsecureregion(core: &mut dyn Core) -> Result<()> { Ok(()) } -fn stmsecure_swapbanks(core: &mut dyn Core) -> Result<()> { +fn stmsecure_swapbanks(core: &mut ProbeCore) -> Result<()> { println!("Swapping banks"); stmsecure_unlock_option(core)?; let optsr = core.read_word_32(FLASH_OPTSR_CUR)?; diff --git a/humility-core/src/archive.rs b/humility-core/src/archive.rs index fa438ab57..5f33938c7 100644 --- a/humility-core/src/archive.rs +++ b/humility-core/src/archive.rs @@ -45,10 +45,6 @@ impl Core for ArchiveCore { bail!("cannot read register {} from an archive", reg); } - fn write_reg(&mut self, reg: ARMRegister, _value: u32) -> Result<()> { - bail!("cannot write register {} to an archive", reg); - } - fn write_word_32(&mut self, _addr: u32, _data: u32) -> Result<()> { bail!("cannot write a word to an archive"); } diff --git a/humility-core/src/core.rs b/humility-core/src/core.rs index ced49a9fc..bd65524cb 100644 --- a/humility-core/src/core.rs +++ b/humility-core/src/core.rs @@ -19,7 +19,6 @@ pub trait Core { fn read_8(&mut self, addr: u32, data: &mut [u8]) -> Result<()>; fn read_reg(&mut self, reg: ARMRegister) -> Result; - fn write_reg(&mut self, reg: ARMRegister, value: u32) -> Result<()>; fn write_word_32(&mut self, addr: u32, data: u32) -> Result<()>; fn write_8(&mut self, addr: u32, data: &[u8]) -> Result<()>; diff --git a/humility-core/src/dump.rs b/humility-core/src/dump.rs index 9b17dd8aa..56d8c9904 100644 --- a/humility-core/src/dump.rs +++ b/humility-core/src/dump.rs @@ -174,10 +174,6 @@ impl Core for DumpCore { } } - fn write_reg(&mut self, _reg: ARMRegister, _value: u32) -> Result<()> { - bail!("cannot write register on a dump"); - } - fn write_word_32(&mut self, _addr: u32, _data: u32) -> Result<()> { bail!("cannot write a word on a dump"); } diff --git a/humility-dump-agent/src/lib.rs b/humility-dump-agent/src/lib.rs index e4c6793d7..80b567594 100644 --- a/humility-dump-agent/src/lib.rs +++ b/humility-dump-agent/src/lib.rs @@ -328,10 +328,6 @@ impl Core for DumpAgentCore { } } - fn write_reg(&mut self, reg: ARMRegister, _value: u32) -> Result<()> { - bail!("cannot write register {} over dump agent", reg); - } - fn write_word_32(&mut self, _addr: u32, _data: u32) -> Result<()> { bail!("cannot write a word over dump agent"); } diff --git a/humility-net-core/src/lib.rs b/humility-net-core/src/lib.rs index 9e96112d6..896de9a03 100644 --- a/humility-net-core/src/lib.rs +++ b/humility-net-core/src/lib.rs @@ -352,10 +352,6 @@ impl Core for NetCore { bail!("cannot read register {} over network", reg); } - fn write_reg(&mut self, reg: ARMRegister, _value: u32) -> Result<()> { - bail!("cannot write register {} over network", reg); - } - fn write_word_32(&mut self, _addr: u32, _data: u32) -> Result<()> { bail!("cannot write a word over network"); } diff --git a/humility-probes-core/src/probe_rs.rs b/humility-probes-core/src/probe_rs.rs index fb70ea7fe..759188829 100644 --- a/humility-probes-core/src/probe_rs.rs +++ b/humility-probes-core/src/probe_rs.rs @@ -305,20 +305,6 @@ impl Core for ProbeCore { ))?) } - fn write_reg(&mut self, reg: ARMRegister, value: u32) -> Result<()> { - let mut core = self.session.core(0)?; - use num_traits::ToPrimitive; - - core.write_core_reg( - Into::::into( - ARMRegister::to_u16(®).unwrap(), - ), - value, - )?; - - Ok(()) - } - fn write_word_32(&mut self, addr: u32, data: u32) -> Result<()> { let mut core = self.session.core(0)?; core.write_word_32(addr.into(), data)?; @@ -484,4 +470,18 @@ impl ProbeCore { core.step()?; Ok(()) } + + pub fn write_reg(&mut self, reg: ARMRegister, value: u32) -> Result<()> { + let mut core = self.session.core(0)?; + use num_traits::ToPrimitive; + + core.write_core_reg( + Into::::into( + ARMRegister::to_u16(®).unwrap(), + ), + value, + )?; + + Ok(()) + } } diff --git a/humility-probes-core/src/unattached.rs b/humility-probes-core/src/unattached.rs index 920fbe1b4..ec10f770a 100644 --- a/humility-probes-core/src/unattached.rs +++ b/humility-probes-core/src/unattached.rs @@ -44,10 +44,6 @@ impl Core for UnattachedCore { bail!("Core::read_reg unimplemented when unattached!"); } - fn write_reg(&mut self, _reg: ARMRegister, _value: u32) -> Result<()> { - bail!("Core::write_reg unimplemented when unattached!"); - } - fn write_word_32(&mut self, _addr: u32, _data: u32) -> Result<()> { bail!("Core::write_word_32 unimplemented when unattached!"); } From 94e97d5bf05b55e0a930f2bd5f475c8eb060d3e1 Mon Sep 17 00:00:00 2001 From: Matt Keeter Date: Thu, 11 Jun 2026 10:27:23 -0400 Subject: [PATCH 4/7] Remove subcommands when `probes` feature is disabled --- humility-bin/Cargo.toml | 30 +++++++++++++++++++--------- humility-bin/build.rs | 43 ++++++++++++++++++++++++++++++---------- humility-bin/src/main.rs | 17 ++++++++++------ humility-cli/src/lib.rs | 36 ++++++++++++++++++--------------- 4 files changed, 85 insertions(+), 41 deletions(-) diff --git a/humility-bin/Cargo.toml b/humility-bin/Cargo.toml index 0c69eaf8d..f4fa905cd 100644 --- a/humility-bin/Cargo.toml +++ b/humility-bin/Cargo.toml @@ -40,7 +40,6 @@ cmd-console-proxy = { workspace = true } cmd-counters = { workspace = true } cmd-dashboard = { workspace = true } cmd-diagnose = { workspace = true } -cmd-debugmailbox = { workspace = true, optional = true } cmd-discover = { workspace = true } cmd-doc = { workspace = true } cmd-dump = { workspace = true } @@ -48,7 +47,6 @@ cmd-tofino-eeprom = { workspace = true } cmd-ereport = { workspace = true } cmd-exec = { workspace = true } cmd-extract = { workspace = true } -cmd-flash = { workspace = true, optional = true } cmd-gimlet = { workspace = true } cmd-gpio = { workspace = true } cmd-hash = { workspace = true } @@ -59,7 +57,6 @@ cmd-i2c = { workspace = true } cmd-ibc = { workspace = true } cmd-jefe = { workspace = true } cmd-lpc55gpio = { workspace = true } -cmd-lsusb = { workspace = true, optional = true } cmd-manifest = { workspace = true } cmd-map = { workspace = true } cmd-monorail = { workspace = true } @@ -68,13 +65,10 @@ cmd-net = { workspace = true } cmd-pmbus = { workspace = true } cmd-power = { workspace = true } cmd-powershelf = { workspace = true } -cmd-probe = { workspace = true } cmd-qspi = { workspace = true } cmd-readmem = { workspace = true } cmd-readvar = { workspace = true } -cmd-rebootleby.workspace = true cmd-registers = { workspace = true } -cmd-reset = { workspace = true, optional = true } cmd-rencm = { workspace = true } cmd-rendmp = { workspace = true } cmd-ringbuf = { workspace = true } @@ -84,12 +78,20 @@ cmd-spctrl = { workspace = true } cmd-spd = { workspace = true } cmd-spi = { workspace = true } cmd-stackmargin = { workspace = true } -cmd-stmsecure = { workspace = true } cmd-tasks = { workspace = true } cmd-test = { workspace = true } cmd-validate = { workspace = true } cmd-vpd = { workspace = true } -cmd-writeword = { workspace = true } + +# Subcommand which require the `probes` feature +cmd-debugmailbox = { workspace = true, optional = true } +cmd-flash = { workspace = true, optional = true } +cmd-lsusb = { workspace = true, optional = true } +cmd-probe = { workspace = true, optional = true } +cmd-rebootleby = { workspace = true, optional = true } +cmd-reset = { workspace = true, optional = true } +cmd-stmsecure = { workspace = true, optional = true } +cmd-writeword = { workspace = true, optional = true } anyhow = { workspace = true } bitfield = { workspace = true } @@ -114,7 +116,17 @@ trycmd = { workspace = true } [features] default = ["probes"] -probes = ["humility-cli/probes", "cmd-reset", "cmd-lsusb", "cmd-flash", "cmd-debugmailbox"] +probes = [ + "humility-cli/probes", + "cmd-debugmailbox", + "cmd-flash", + "cmd-lsusb", + "cmd-probe", + "cmd-rebootleby", + "cmd-reset", + "cmd-stmsecure", + "cmd-writeword", +] [[bin]] name = "humility" diff --git a/humility-bin/build.rs b/humility-bin/build.rs index 0de12dba2..319801bd2 100644 --- a/humility-bin/build.rs +++ b/humility-bin/build.rs @@ -32,19 +32,42 @@ pub enum Subcommand {{ "## )?; - // - let banned = if std::env::var_os("CARGO_FEATURE_PROBES").is_none() { - // These are everything that can potentially pull in libusb - ["probe-rs", "humility-probes-core", "rusb"].into_iter().collect() - } else { - BTreeSet::new() - }; + let (banned_deps, disabled_cmds) = + if std::env::var_os("CARGO_FEATURE_PROBES").is_none() { + ( + // These are everything that can potentially pull in libusb + ["probe-rs", "humility-probes-core", "rusb"] + .into_iter() + .collect(), + // We also look at which commands are enabled by the `probes` + // feature on the `humility-bin` crate, then remove them. + metadata + .workspace_members + .iter() + .find(|p| metadata[p].name == "humility-bin") + .map(|p| metadata[p].clone()) + .unwrap() + .features["probes"] + .iter() + .flat_map(|p| { + p.strip_prefix("cmd-") + .map(|p| format!("humility-cmd-{p}")) + }) + .collect::>(), + ) + } else { + (BTreeSet::new(), BTreeSet::new()) + }; + for id in &metadata.workspace_members { - let package = - metadata.packages.iter().find(|p| &p.id == id).unwrap().clone(); + let package = metadata[id].clone(); if let Some(cmd) = package.name.strip_prefix("humility-cmd-") - && !package.dependencies.iter().any(|d| banned.contains(&*d.name)) + && !package + .dependencies + .iter() + .any(|d| banned_deps.contains(&*d.name)) + && !disabled_cmds.contains(package.name.as_str()) { cmds.insert(cmd.to_string()); } diff --git a/humility-bin/src/main.rs b/humility-bin/src/main.rs index df977f24e..edcd34c59 100644 --- a/humility-bin/src/main.rs +++ b/humility-bin/src/main.rs @@ -10,12 +10,11 @@ use humility::log::{info, warn}; use humility_cli::{Cli, ExecutionContext, env::Environment}; use tracing_subscriber::{EnvFilter, filter::LevelFilter}; -/// Main CLI entry point -/// -/// The distinction between [`Cli`] and [`OuterCli`] is necessary to break a -/// dependency loop: the `Cli` is stored in the [`ExecutionContext`]; commands -/// need to take the `ExecutionContext`; and the `OuterCli` needs to depend on -/// commands. +#[cfg_attr(feature = "probes", doc = "Debugger for Hubris")] +#[cfg_attr( + not(feature = "probes"), + doc = "Debugger for Hubris (probeless build)" +)] #[derive(Parser, Debug)] #[clap( name = "humility", max_term_width = 80, @@ -23,6 +22,12 @@ use tracing_subscriber::{EnvFilter, filter::LevelFilter}; disable_version_flag = true, )] struct OuterCli { + /// Inner [`Cli`] object + /// + /// The distinction between [`Cli`] and [`OuterCli`] is necessary to break a + /// dependency loop: the `Cli` is stored in the [`ExecutionContext`]; commands + /// need to take the `ExecutionContext`; and the `OuterCli` needs to depend on + /// commands. #[clap(flatten)] cli: Cli, diff --git a/humility-cli/src/lib.rs b/humility-cli/src/lib.rs index 09f4ff6c6..73f16beb3 100644 --- a/humility-cli/src/lib.rs +++ b/humility-cli/src/lib.rs @@ -182,7 +182,16 @@ impl Cli { ) .map(|b| Box::new(b) as Box) } else { - self.attach_probe(hubris).map(|b| Box::new(b) as Box) + #[cfg(feature = "probes")] + { + self.attach_probe(hubris).map(|b| Box::new(b) as Box) + } + #[cfg(not(feature = "probes"))] + { + Err(anyhow!( + "probes feature is missing; ip address is required" + )) + } }?; if let Some(validate) = validate { let Some(hubris) = hubris else { @@ -228,20 +237,6 @@ impl Cli { ) } - // If the `probes` feature is disabled, then we don't have access to - // `ProbeCore`, but we still need to return a concrete type that implements - // `Core` (for everything else to typecheck). - // - // We'll have the signature return an `ArchiveCore` instead, with the - // knowledge that it will never actually be used. - #[cfg(not(feature = "probes"))] - pub fn attach_probe( - &self, - _hubris: Option<&HubrisArchive>, - ) -> Result { - bail!("Did not build with probes!"); - } - /// Attaches to a either a dump or a live system, depending on CLI arguments /// /// The `hubris` archive is mandatory if `validate` is `Some(..)` or if we @@ -267,7 +262,16 @@ impl Cli { ) .map(|b| Box::new(b) as Box) } else { - self.attach_probe(hubris).map(|b| Box::new(b) as Box) + #[cfg(feature = "probes")] + { + self.attach_probe(hubris).map(|b| Box::new(b) as Box) + } + #[cfg(not(feature = "probes"))] + { + Err(anyhow!( + "probes feature is missing; ip address or dump is required" + )) + } }?; if let Some(validate) = validate { let Some(hubris) = hubris else { From 7a15d5bc45b0aa6ee1650ac58a90e99acc025fb6 Mon Sep 17 00:00:00 2001 From: Matt Keeter Date: Fri, 12 Jun 2026 09:43:16 -0400 Subject: [PATCH 5/7] Feedback from Eliza --- humility-bin/build.rs | 43 ++++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/humility-bin/build.rs b/humility-bin/build.rs index 319801bd2..4fff88697 100644 --- a/humility-bin/build.rs +++ b/humility-bin/build.rs @@ -34,27 +34,28 @@ pub enum Subcommand {{ let (banned_deps, disabled_cmds) = if std::env::var_os("CARGO_FEATURE_PROBES").is_none() { - ( - // These are everything that can potentially pull in libusb - ["probe-rs", "humility-probes-core", "rusb"] - .into_iter() - .collect(), - // We also look at which commands are enabled by the `probes` - // feature on the `humility-bin` crate, then remove them. - metadata - .workspace_members - .iter() - .find(|p| metadata[p].name == "humility-bin") - .map(|p| metadata[p].clone()) - .unwrap() - .features["probes"] - .iter() - .flat_map(|p| { - p.strip_prefix("cmd-") - .map(|p| format!("humility-cmd-{p}")) - }) - .collect::>(), - ) + // These are everything that can potentially pull in libusb + let banned_deps = ["probe-rs", "humility-probes-core", "rusb"] + .into_iter() + .collect(); + // We also look at which commands are enabled by the `probes` + // feature on the `humility-bin` crate, then remove them. + let disabled_cmds = metadata + .workspace_members + .iter() + .find(|p| metadata[p].name == "humility-bin") + .map(|p| metadata[p].clone()) + .expect( + "could not find package named `humility-bin` while \ + executing its own build script; has it been renamed?", + ) + .features["probes"] + .iter() + .flat_map(|p| { + p.strip_prefix("cmd-").map(|p| format!("humility-cmd-{p}")) + }) + .collect::>(); + (banned_deps, disabled_cmds) } else { (BTreeSet::new(), BTreeSet::new()) }; From e9b8f5234d5600252639f80dc3bcc9d2e1a2487d Mon Sep 17 00:00:00 2001 From: Matt Keeter Date: Wed, 17 Jun 2026 13:41:22 -0400 Subject: [PATCH 6/7] More notes from review --- cmd/diagnose/src/lib.rs | 3 +-- cmd/hydrate/src/lib.rs | 3 --- cmd/probe/src/lib.rs | 4 +-- humility-cli/src/lib.rs | 6 +++-- humility-core/src/archive.rs | 4 --- humility-core/src/core.rs | 2 -- humility-core/src/dump.rs | 4 --- humility-dump-agent/src/lib.rs | 4 --- humility-net-core/src/lib.rs | 4 --- humility-probes-core/src/probe_rs.rs | 35 ++++++++++++++++++-------- humility-probes-core/src/unattached.rs | 9 ------- 11 files changed, 31 insertions(+), 47 deletions(-) diff --git a/cmd/diagnose/src/lib.rs b/cmd/diagnose/src/lib.rs index 67a4a6f4b..f366cdbb1 100644 --- a/cmd/diagnose/src/lib.rs +++ b/cmd/diagnose/src/lib.rs @@ -152,8 +152,7 @@ fn diagnose( } } - // This is a bit of a hack, but, we can detect core dumps by going... - if core.info().0 == "core dump" { + if core.is_dump() { 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/hydrate/src/lib.rs b/cmd/hydrate/src/lib.rs index 16f080d2e..583625470 100644 --- a/cmd/hydrate/src/lib.rs +++ b/cmd/hydrate/src/lib.rs @@ -69,9 +69,6 @@ impl humility::core::Core for DryCore { unsupported!(read_reg(_reg: ARMRegister) -> Result); unsupported!(write_word_32(_addr: u32, _data: u32)); - fn info(&self) -> (String, Option) { - ("DryCore".to_owned(), None) - } fn is_archive(&self) -> bool { false } diff --git a/cmd/probe/src/lib.rs b/cmd/probe/src/lib.rs index a21c518e1..bfe42da2c 100644 --- a/cmd/probe/src/lib.rs +++ b/cmd/probe/src/lib.rs @@ -135,8 +135,8 @@ fn probecmd(subargs: ProbeArgs, context: &mut ExecutionContext) -> Result<()> { if subargs.environment { match (info.1, core.vid_pid()) { - (Some(ref serial), Some((vid, pid))) => { - println!("HUMILITY_PROBE={vid:04x}:{pid:04x}:{serial}"); + (Some(ref serial), vidpid) => { + println!("HUMILITY_PROBE={vidpid}:{serial}"); return Ok(()); } _ => { diff --git a/humility-cli/src/lib.rs b/humility-cli/src/lib.rs index 73f16beb3..ffdd00968 100644 --- a/humility-cli/src/lib.rs +++ b/humility-cli/src/lib.rs @@ -189,7 +189,8 @@ impl Cli { #[cfg(not(feature = "probes"))] { Err(anyhow!( - "probes feature is missing; ip address is required" + "humility was compiled without the \"probes\" feature; \ + an IP address or dump is required" )) } }?; @@ -269,7 +270,8 @@ impl Cli { #[cfg(not(feature = "probes"))] { Err(anyhow!( - "probes feature is missing; ip address or dump is required" + "humility was compiled without the \"probes\" feature; \ + an IP address or dump is required" )) } }?; diff --git a/humility-core/src/archive.rs b/humility-core/src/archive.rs index 5f33938c7..dbaaf730b 100644 --- a/humility-core/src/archive.rs +++ b/humility-core/src/archive.rs @@ -33,10 +33,6 @@ impl Core for ArchiveCore { true } - fn info(&self) -> (String, Option) { - ("archive".to_string(), None) - } - fn read_8(&mut self, addr: u32, data: &mut [u8]) -> Result<()> { self.read(addr, data) } diff --git a/humility-core/src/core.rs b/humility-core/src/core.rs index bd65524cb..3d5532bf3 100644 --- a/humility-core/src/core.rs +++ b/humility-core/src/core.rs @@ -15,8 +15,6 @@ use thiserror::Error; #[auto_impl::auto_impl(&mut)] // adds `impl Core for &mut C` pub trait Core { - fn info(&self) -> (String, Option); - fn read_8(&mut self, addr: u32, data: &mut [u8]) -> Result<()>; fn read_reg(&mut self, reg: ARMRegister) -> Result; fn write_word_32(&mut self, addr: u32, data: u32) -> Result<()>; diff --git a/humility-core/src/dump.rs b/humility-core/src/dump.rs index 56d8c9904..9efa9339a 100644 --- a/humility-core/src/dump.rs +++ b/humility-core/src/dump.rs @@ -117,10 +117,6 @@ fn load_registers(r: &[u8]) -> Result> { #[rustfmt::skip::macros(bail)] impl Core for DumpCore { - fn info(&self) -> (String, Option) { - ("core dump".to_string(), None) - } - fn read_8(&mut self, addr: u32, data: &mut [u8]) -> Result<()> { let rsize = data.len(); diff --git a/humility-dump-agent/src/lib.rs b/humility-dump-agent/src/lib.rs index 80b567594..d36bc686b 100644 --- a/humility-dump-agent/src/lib.rs +++ b/humility-dump-agent/src/lib.rs @@ -313,10 +313,6 @@ impl DumpAgentCore { } impl Core for DumpAgentCore { - fn info(&self) -> (String, Option) { - panic!("unexpected call to DumpAgentCore info"); - } - fn read_8(&mut self, addr: u32, data: &mut [u8]) -> Result<()> { self.read(addr, data) } diff --git a/humility-net-core/src/lib.rs b/humility-net-core/src/lib.rs index 896de9a03..c4128d7aa 100644 --- a/humility-net-core/src/lib.rs +++ b/humility-net-core/src/lib.rs @@ -315,10 +315,6 @@ impl NetCore { #[rustfmt::skip::macros(bail)] impl Core for NetCore { - fn info(&self) -> (String, Option) { - ("connected remotely".to_string(), None) - } - fn is_net(&self) -> bool { true } diff --git a/humility-probes-core/src/probe_rs.rs b/humility-probes-core/src/probe_rs.rs index 759188829..f9fd1154c 100644 --- a/humility-probes-core/src/probe_rs.rs +++ b/humility-probes-core/src/probe_rs.rs @@ -229,15 +229,6 @@ pub const CORE_MAX_READSIZE: usize = 65536; // 64K ought to be enough for anyone #[rustfmt::skip::macros(anyhow, bail)] impl Core for ProbeCore { - fn info(&self) -> (String, Option) { - let ident = format!( - "{}, VID {:04x}, PID {:04x}", - self.identifier, self.vendor_id, self.product_id - ); - - (ident, self.serial_number.clone()) - } - fn read_word_32(&mut self, addr: u32) -> Result { trace!(self.log, "reading word at {:x}", addr); let mut rval = 0; @@ -366,6 +357,18 @@ pub enum LoadError { FlashError(#[from] probe_rs::flashing::FlashError), } +#[derive(Copy, Clone, Debug)] +pub struct VidPid { + pub vid: u16, + pub pid: u16, +} + +impl std::fmt::Display for VidPid { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:04x}:{:04x}", self.vid, self.pid) + } +} + impl ProbeCore { pub fn load(&mut self, elf_data: &[u8]) -> Result<(), LoadError> { if !self.can_flash { @@ -461,8 +464,8 @@ impl ProbeCore { Ok(()) } - pub fn vid_pid(&self) -> Option<(u16, u16)> { - Some((self.vendor_id, self.product_id)) + pub fn vid_pid(&self) -> VidPid { + VidPid { vid: self.vendor_id, pid: self.product_id } } pub fn step(&mut self) -> Result<()> { @@ -484,4 +487,14 @@ impl ProbeCore { Ok(()) } + + /// Returns a tuple of `(formatted probe info, serial number)` + pub fn info(&self) -> (String, Option) { + let ident = format!( + "{}, VID {:04x}, PID {:04x}", + self.identifier, self.vendor_id, self.product_id + ); + + (ident, self.serial_number.clone()) + } } diff --git a/humility-probes-core/src/unattached.rs b/humility-probes-core/src/unattached.rs index ec10f770a..eccd4fd9b 100644 --- a/humility-probes-core/src/unattached.rs +++ b/humility-probes-core/src/unattached.rs @@ -27,15 +27,6 @@ impl UnattachedCore { } impl Core for UnattachedCore { - fn info(&self) -> (String, Option) { - let ident = format!( - "{}, VID {:04x}, PID {:04x}", - self.identifier, self.vendor_id, self.product_id - ); - - (ident, self.serial_number.clone()) - } - fn read_8(&mut self, _addr: u32, _data: &mut [u8]) -> Result<()> { bail!("Core::read_8 unimplemented when unattached!"); } From 6beb7b6f4f0b04a6bc1fc45dac953972f11e41c7 Mon Sep 17 00:00:00 2001 From: Matt Keeter Date: Wed, 17 Jun 2026 14:03:12 -0400 Subject: [PATCH 7/7] Remove UnattachedCore (yay) --- cmd/reset/src/lib.rs | 19 ++++++- humility-probes-core/src/lib.rs | 18 ++----- humility-probes-core/src/unattached.rs | 68 -------------------------- 3 files changed, 20 insertions(+), 85 deletions(-) delete mode 100644 humility-probes-core/src/unattached.rs diff --git a/cmd/reset/src/lib.rs b/cmd/reset/src/lib.rs index 719b41b7a..e2bac4750 100644 --- a/cmd/reset/src/lib.rs +++ b/cmd/reset/src/lib.rs @@ -101,7 +101,7 @@ fn reset(subargs: ResetArgs, context: &mut ExecutionContext) -> Result<()> { Behavior::Reset => c.reset(), } } else { - let mut c = humility_probes_core::attach_to_probe( + let mut probe = humility_probes_core::attach_to_probe( probe, context.cli.speed, log, @@ -110,7 +110,22 @@ fn reset(subargs: ResetArgs, context: &mut ExecutionContext) -> Result<()> { Behavior::Halt | Behavior::ResetWithHandoff(..) => { unreachable!() } - Behavior::Reset => c.reset(), + Behavior::Reset => { + probe + .target_reset_assert() + .map_err(anyhow::Error::from) + .and_then(|()| { + // The closest available documentation on hold time is + // a comment giving a timeout + // https://open-cmsis-pack.github.io/Open-CMSIS-Pack-Spec/main/html/debug_description.html#resetHardwareDeassert + std::thread::sleep(std::time::Duration::from_millis( + 1000, + )); + + probe.target_reset_deassert()?; + Ok(()) + }) + } } }; diff --git a/humility-probes-core/src/lib.rs b/humility-probes-core/src/lib.rs index b272f0d5e..608308b7e 100644 --- a/humility-probes-core/src/lib.rs +++ b/humility-probes-core/src/lib.rs @@ -17,10 +17,8 @@ use humility::{ use anyhow::{Result, anyhow, bail}; mod probe_rs; -mod unattached; pub use probe_rs::ProbeCore; -pub use unattached::UnattachedCore; fn parse_probe(probe: &str) -> (&str, Option) { if probe.contains('-') { @@ -136,7 +134,7 @@ pub fn attach_to_probe( probe: &str, speed_khz: Option, log: &Logger, -) -> Result { +) -> Result<::probe_rs::probe::Probe> { let (probe, index) = parse_probe(probe); match probe { @@ -149,26 +147,16 @@ pub fn attach_to_probe( } info!(log, "Opened probe {}", probe_info.identifier); - Ok(UnattachedCore::new( - probe, - probe_info.identifier.clone(), - probe_info.vendor_id, - probe_info.product_id, - probe_info.serial_number, - )) + Ok(probe) } "auto" => attach_to_probe("usb", speed_khz, log), _ => match probe.parse::() { Ok(selector) => { let vidpid = probe; - let vid = selector.vendor_id; - let pid = selector.product_id; - let serial = selector.serial_number.clone(); let probe = open_probe_from_selector(selector, speed_khz)?; - let name = probe.get_name(); info!(log, "Opened {vidpid}"); - Ok(UnattachedCore::new(probe, name, vid, pid, serial)) + Ok(probe) } Err(_) => Err(anyhow!("unrecognized probe: {}", probe)), }, diff --git a/humility-probes-core/src/unattached.rs b/humility-probes-core/src/unattached.rs deleted file mode 100644 index eccd4fd9b..000000000 --- a/humility-probes-core/src/unattached.rs +++ /dev/null @@ -1,68 +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 anyhow::{Result, bail}; -use humility::core::Core; -use humility_arch_arm::ARMRegister; - -pub struct UnattachedCore { - pub probe: probe_rs::probe::Probe, - pub identifier: String, - pub vendor_id: u16, - pub product_id: u16, - pub serial_number: Option, -} - -impl UnattachedCore { - pub(crate) fn new( - probe: probe_rs::probe::Probe, - identifier: String, - vendor_id: u16, - product_id: u16, - serial_number: Option, - ) -> Self { - Self { probe, identifier, vendor_id, product_id, serial_number } - } -} - -impl Core for UnattachedCore { - fn read_8(&mut self, _addr: u32, _data: &mut [u8]) -> Result<()> { - bail!("Core::read_8 unimplemented when unattached!"); - } - - fn read_reg(&mut self, _reg: ARMRegister) -> Result { - bail!("Core::read_reg unimplemented when unattached!"); - } - - fn write_word_32(&mut self, _addr: u32, _data: u32) -> Result<()> { - bail!("Core::write_word_32 unimplemented when unattached!"); - } - - fn write_8(&mut self, _addr: u32, _data: &[u8]) -> Result<()> { - bail!("Core::write_8 unimplemented when unattached!"); - } - - fn halt(&mut self) -> Result<()> { - bail!("Core::halt unimplemented when unattached!"); - } - - fn run(&mut self) -> Result<()> { - bail!("Core::run unimplemented when unattached!"); - } -} - -impl UnattachedCore { - pub fn reset(&mut self) -> Result<()> { - self.probe.target_reset_assert()?; - - // The closest available documentation on hold time is - // a comment giving a timeout - // https://open-cmsis-pack.github.io/Open-CMSIS-Pack-Spec/main/html/debug_description.html#resetHardwareDeassert - std::thread::sleep(std::time::Duration::from_millis(1000)); - - self.probe.target_reset_deassert()?; - - Ok(()) - } -}