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/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 df4ffc3bb..583625470 100644 --- a/cmd/hydrate/src/lib.rs +++ b/cmd/hydrate/src/lib.rs @@ -63,17 +63,12 @@ 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()); 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) { - ("DryCore".to_owned(), None) - } fn is_archive(&self) -> bool { false } @@ -83,9 +78,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/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/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/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/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-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..4fff88697 100644 --- a/humility-bin/build.rs +++ b/humility-bin/build.rs @@ -32,19 +32,43 @@ 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 + 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()) + }; + 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..ffdd00968 100644 --- a/humility-cli/src/lib.rs +++ b/humility-cli/src/lib.rs @@ -182,7 +182,17 @@ 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!( + "humility was compiled without the \"probes\" feature; \ + an IP address or dump is required" + )) + } }?; if let Some(validate) = validate { let Some(hubris) = hubris else { @@ -228,20 +238,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 +263,17 @@ 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!( + "humility was compiled without the \"probes\" feature; \ + an IP address or dump is required" + )) + } }?; if let Some(validate) = validate { let Some(hubris) = hubris else { diff --git a/humility-core/src/archive.rs b/humility-core/src/archive.rs index 036f06c16..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) } @@ -45,10 +41,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"); } @@ -64,8 +56,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 f40a91a84..3d5532bf3 100644 --- a/humility-core/src/core.rs +++ b/humility-core/src/core.rs @@ -15,21 +15,13 @@ use thiserror::Error; #[auto_impl::auto_impl(&mut)] // adds `impl Core for &mut C` 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<()>; fn write_word_32(&mut self, addr: u32, data: u32) -> Result<()>; fn write_8(&mut self, addr: u32, data: &[u8]) -> Result<()>; 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..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(); @@ -174,10 +170,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"); } @@ -194,10 +186,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..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) } @@ -328,10 +324,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"); } @@ -347,10 +339,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..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 } @@ -352,10 +348,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"); } @@ -371,10 +363,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/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/probe_rs.rs b/humility-probes-core/src/probe_rs.rs index 5deb3c6ad..f9fd1154c 100644 --- a/humility-probes-core/src/probe_rs.rs +++ b/humility-probes-core/src/probe_rs.rs @@ -229,19 +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 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; @@ -309,20 +296,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)?; @@ -355,12 +328,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()?; @@ -390,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 { @@ -484,4 +463,38 @@ impl ProbeCore { self.halted = true; Ok(()) } + + pub fn vid_pid(&self) -> VidPid { + VidPid { vid: self.vendor_id, pid: self.product_id } + } + + pub fn step(&mut self) -> Result<()> { + let mut core = self.session.core(0)?; + 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(()) + } + + /// 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 deleted file mode 100644 index ce1c522cd..000000000 --- a/humility-probes-core/src/unattached.rs +++ /dev/null @@ -1,89 +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 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 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!"); - } - - fn read_reg(&mut self, _reg: ARMRegister) -> Result { - 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!"); - } - - 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!"); - } - - fn step(&mut self) -> Result<()> { - bail!("Core::step 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(()) - } -}