Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,118 changes: 931 additions & 187 deletions Cargo.lock

Large diffs are not rendered by default.

20 changes: 14 additions & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -104,12 +104,23 @@ tlvc-text = { git = "https://github.com/oxidecomputer/tlvc", version = "0.4.0" }
turin-post-decoder = { git = "https://github.com/oxidecomputer/turin-post-decoder" }
vsc7448-info = { git = "https://github.com/oxidecomputer/vsc7448.git" }
vsc7448-types = { git = "https://github.com/oxidecomputer/vsc7448.git" }
# This will ideally be merged soon
nusb = { git = "https://github.com/oxidecomputer/nusb.git", branch = "add_illumos" }

#
# We depend on the oxide-stable branch of Oxide's fork of probe-rs to assure
# that we can float necessary patches on probe-rs.
#
probe-rs = { git = "https://github.com/oxidecomputer/probe-rs.git", branch = "oxide-v0.12.0" }
# As of this commit the patches are pretty minimal: an updated nusb branch
# with illumos support and some tweaks to avoid the need to pull in
# serialport. The dream would still be to run a regular probe-rs release.
#
# We also still require CMSIS DAP V1 (HID based) because not all probes are
# updated to firmware that works with CMSIS DAP V2. probe-rs is smart enough
# to try CMSIS DAP v2 if it can so we should only be using HID as a last
# resort. It would still be really nice to eventually drop that feature so
# we don't have to keep using our hidapi-rs fork for illumos fixes.
probe-rs = { git = "https://github.com/oxidecomputer/probe-rs.git", branch = "oxide_v0.31.0", default-features = false, features = ["builtin-targets", "cmsisdap_v1"] }

# Local `path`-based deps
humility = { path = "./humility-core", package = "humility-core" }
Expand Down Expand Up @@ -236,7 +247,6 @@ raw-cpuid = { version = "11.0.0", features = ["display"] }
rayon = "1.7"
regex = "1.5.5"
ron = "0.12"
rusb = "0.8.1"
rustc-demangle = "0.1.21"
scroll = "0.13"
serde = { version = "1.0.228", features = ["derive"] }
Expand All @@ -253,6 +263,8 @@ termimad = "0.34"
termios = "0.3" # not usable on windows!
thiserror = "2.0"
toml = { version = "1.1.2", features = ["preserve_order"] }
# probe-rs now uses tracing
tracing-subscriber = { version = "0.3.23", features = ["fmt", "env-filter"] }
trycmd = "0.13.2"
winapi = "0.3.9"
zerocopy = { version = "0.8", features = ["derive"] }
Expand All @@ -265,7 +277,3 @@ debug = true
inherits = "release"
debug = false
debug-assertions = true

[patch.crates-io]
libusb1-sys = { git = "https://github.com/oxidecomputer/rusb", branch = "probe-rs-0.12-libusb-v1.0.26" }
hidapi = { git = "https://github.com/oxidecomputer/hidapi-rs", branch = "oxide-stable" }
75 changes: 29 additions & 46 deletions cmd/debugmailbox/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,18 @@ use anyhow::{Context, Result, bail};
use byteorder::{ByteOrder, LittleEndian, WriteBytesExt};
use clap::Parser;
use humility_cli::{ExecutionContext, humility_cmd};
use probe_rs::{
DebugProbeError, DebugProbeSelector, Probe,
architecture::arm::{ApAddress, ArmProbeInterface, DapError, DpAddress},
use probe_rs::architecture::arm::{
ArmDebugInterface, ArmError, DapError, FullyQualifiedApAddress,
sequences::DefaultArmSequence,
};
use probe_rs::probe::DebugProbeSelector;

// The debug mailbox registers
// See 51.5.5.1 of Rev 2.4 of the LPC55 manual
const CSW: u8 = 0x0;
const REQUEST: u8 = 0x4;
const RETURN: u8 = 0x8;
const IDR: u8 = 0xFC;
const CSW: u64 = 0x0;
const REQUEST: u64 = 0x4;
const RETURN: u64 = 0x8;
const IDR: u64 = 0xFC;

// Are we talking to the wrong chip?
const SP_IDR: u32 = 0x54770002;
Expand Down Expand Up @@ -103,9 +104,9 @@ enum DebugMailboxCmd {
}

fn poll_raw_ap_register(
probe: &mut Box<dyn ArmProbeInterface + '_>,
ap: &ApAddress,
addr: u8,
probe: &mut Box<dyn ArmDebugInterface + '_>,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wondered if we could remove the Box here (in favor of a &mut dyn ArmDebugInterface), but poking at it shows that it would ripple through a bunch of places, so out of scope for this PR.

ap: &FullyQualifiedApAddress,
addr: u64,
mut f: impl FnMut(u32) -> Result<bool>,
mut timeout_ms: usize,
) -> Result<u32> {
Expand All @@ -117,30 +118,13 @@ fn poll_raw_ap_register(
// this initial delay.
std::thread::sleep(std::time::Duration::from_millis(10));

match probe.read_raw_ap_register(*ap, addr) {
match probe.read_raw_ap_register(ap, addr) {
Ok(val) => match f(val) {
Ok(true) => return Ok(val),
Ok(false) => (),
Err(e) => return Err(e),
},
Err(DebugProbeError::ArchitectureSpecific(arch_err)) => {
match arch_err.downcast::<DapError>() {
Ok(dap_err) => match *dap_err {
DapError::WaitResponse => {}
e => {
return Err(DebugProbeError::ArchitectureSpecific(
e.into(),
)
.into());
}
},
Err(e) => {
return Err(
DebugProbeError::ArchitectureSpecific(e).into()
);
}
}
}
Err(ArmError::Dap(DapError::WaitResponse)) => {}
Err(x) => return Err(x.into()),
}

Expand All @@ -154,12 +138,12 @@ fn poll_raw_ap_register(
}

fn alive<'a>(
probe: &mut Box<dyn ArmProbeInterface + 'a>,
addr: &ApAddress,
probe: &mut Box<dyn ArmDebugInterface + 'a>,
addr: &FullyQualifiedApAddress,
reset: bool,
) -> Result<()> {
if reset {
probe.write_raw_ap_register(*addr, CSW, 0x21)?;
probe.write_raw_ap_register(addr, CSW, 0x21)?;
println!("Resetting chip via SYSREQRESET!");
}

Expand All @@ -170,11 +154,11 @@ fn alive<'a>(
}

fn write_request_reg<'a>(
probe: &mut Box<dyn ArmProbeInterface + 'a>,
addr: &ApAddress,
probe: &mut Box<dyn ArmDebugInterface + 'a>,
addr: &FullyQualifiedApAddress,
val: u32,
) -> Result<()> {
probe.write_raw_ap_register(*addr, REQUEST, val)?;
probe.write_raw_ap_register(addr, REQUEST, val)?;

let _ = poll_raw_ap_register(
probe,
Expand All @@ -197,8 +181,8 @@ fn write_request_reg<'a>(
}

fn write_req<'a>(
probe: &mut Box<dyn ArmProbeInterface + 'a>,
addr: &ApAddress,
probe: &mut Box<dyn ArmDebugInterface + 'a>,
addr: &FullyQualifiedApAddress,
command: DMCommand,
args: &[u32],
) -> Result<Vec<u32>> {
Expand Down Expand Up @@ -257,8 +241,8 @@ fn write_req<'a>(
}

fn read_return<'a>(
probe: &mut Box<dyn ArmProbeInterface + 'a>,
addr: &ApAddress,
probe: &mut Box<dyn ArmDebugInterface + 'a>,
addr: &FullyQualifiedApAddress,
) -> Result<u32> {
poll_raw_ap_register(probe, addr, RETURN, |_| Ok(true), 1000)
.context("Reading debugmailbox RETURN register")
Expand All @@ -269,14 +253,15 @@ fn debugmailboxcmd(
context: &mut ExecutionContext,
) -> Result<()> {
// Get a list of all available debug probes.
let probes = Probe::list_all();
let list = probe_rs::probe::list::Lister::new();
let probes = list.list_all();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah, I love the smell of fresh API design in the morning


if probes.is_empty() {
bail!("No probes found!");
}

let mut probe = match &context.cli.probe {
Some(p) => match TryInto::<DebugProbeSelector>::try_into(p.clone()) {
Some(p) => match p.parse::<DebugProbeSelector>() {
Ok(selector) => {
let vid = selector.vendor_id;
let pid = selector.product_id;
Expand All @@ -301,12 +286,10 @@ fn debugmailboxcmd(

probe.attach_to_unspecified()?;
let mut iface = probe
.try_into_arm_interface()
.unwrap()
.initialize_unspecified()
.try_into_arm_debug_interface(DefaultArmSequence::create())
.unwrap();

let dm_port = ApAddress { dp: DpAddress::Default, ap: 2 };
let dm_port = FullyQualifiedApAddress::v1_with_default_dp(2);

// Check if this is a debug mailbox. This is based on the sequence from
// the LPC55 Debug Mailbox user manual
Expand All @@ -315,7 +298,7 @@ fn debugmailboxcmd(
// handles this for us

// Check the IDR
let val = iface.read_raw_ap_register(dm_port, IDR)?;
let val = iface.read_raw_ap_register(&dm_port, IDR)?;

if val != DM_ID {
if val == SP_IDR {
Expand Down
2 changes: 1 addition & 1 deletion cmd/lsusb/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ humility-log.workspace = true

anyhow.workspace = true
clap.workspace = true
rusb.workspace = true
nusb.workspace = true
89 changes: 51 additions & 38 deletions cmd/lsusb/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@
//! `humility lsusb` will show you Humility's view of the USB devices available
//! on the system, to help you choose probes and/or diagnose permissions issues.

use anyhow::{Context, Result, anyhow};
use anyhow::{Context, Result};
use clap::Parser;
use humility_cli::{ExecutionContext, humility_cmd};
use humility_log::{info, warn};
use nusb::{MaybeFuture, descriptors::language_id::US_ENGLISH};
use std::collections::HashMap;
use std::time::Duration;

Expand All @@ -34,17 +35,18 @@ fn lsusb(_args: LsUsbArgs, context: &mut ExecutionContext) -> Result<()> {
HashMap::new()
};

let devices = rusb::devices()?;
let devices = nusb::list_devices().wait()?;
let mut successes = vec![];
let mut failures = vec![];
for dev in devices.iter() {
for dev in devices {
match list1(&dev) {
Ok(summary) => successes.push(summary),
Err(e) => {
failures.push((
dev.bus_number(),
dev.address(),
dev.port_number(),
// `bus_id` as an &'str is common across all OSes
dev.bus_id().to_owned(),
dev.device_address(),
dev.port_chain()[0],
e,
));
}
Expand Down Expand Up @@ -76,11 +78,13 @@ fn lsusb(_args: LsUsbArgs, context: &mut ExecutionContext) -> Result<()> {
}

if !failures.is_empty() {
failures.sort_by_key(|(b, a, p, _)| (*b, *a, *p));
failures.sort_by(|lhs, rhs| {
(&lhs.0, lhs.1, lhs.2).cmp(&(&rhs.0, rhs.1, rhs.2))
});
info!(log, "--- failures ---");
info!(log, "could not access {} devices:", failures.len());
for (bus, addr, port, e) in failures {
info!(log, "bus {bus}, addr {addr}, port {port}: {e}");
info!(log, "bus {bus}, addr {addr}, port {port}: {e:#}");
}
}

Expand Down Expand Up @@ -109,36 +113,45 @@ fn lsusb(_args: LsUsbArgs, context: &mut ExecutionContext) -> Result<()> {
Ok(())
}

fn list1(
dev: &rusb::Device<impl rusb::UsbContext>,
) -> Result<(String, String)> {
const TIMEOUT: Duration = Duration::from_secs(1);

let desc = dev.device_descriptor()?;
let vid = desc.vendor_id();
let pid = desc.product_id();

let handle = match dev.open() {
Ok(handle) => handle,
Err(e) => {
return Err(anyhow!("{vid:04x}:{pid:04x}:???\topen failed: {e}"));
}
};
let lang = *handle
.read_languages(TIMEOUT)?
.iter()
.find(|lang| lang.primary_language() == rusb::PrimaryLanguage::English)
.ok_or_else(|| anyhow!("can't find English strings"))?;

let man = handle
.read_manufacturer_string(lang, &desc, TIMEOUT)
.unwrap_or_else(|_| "(manufacturer unknown)".to_string());
let prod = handle
.read_product_string(lang, &desc, TIMEOUT)
.unwrap_or_else(|_| "(product unknown)".to_string());
let serial = handle
.read_serial_number_string(lang, &desc, TIMEOUT)
.unwrap_or_else(|_| "(serial unknown)".to_string());
fn list1(dev: &nusb::DeviceInfo) -> Result<(String, String)> {
let timeout = Duration::from_millis(100);
let vid = dev.vendor_id();
let pid = dev.product_id();

let dev = dev
.open()
.wait()
.with_context(|| format!("open failed for {vid:04x}:{pid:04x}:???"))?;
let dev_descriptor = dev.device_descriptor();

let languages: Vec<u16> = dev
.get_string_descriptor_supported_languages(timeout)
.wait()
.map(|i| i.collect())
.unwrap_or_default();

let language = languages.first().copied().unwrap_or(US_ENGLISH);

let man = dev_descriptor
.manufacturer_string_index()
.and_then(|i| {
dev.get_string_descriptor(i, language, timeout).wait().ok()
})
.unwrap_or_else(|| "(manufacturer unknown)".to_string());

let prod = dev_descriptor
.product_string_index()
.and_then(|i| {
dev.get_string_descriptor(i, language, timeout).wait().ok()
})
.unwrap_or_else(|| "(product unknown)".to_string());

let serial = dev_descriptor
.serial_number_string_index()
.and_then(|i| {
dev.get_string_descriptor(i, language, timeout).wait().ok()
})
.unwrap_or_else(|| "(serial unknown)".to_string());

Ok((format!("{vid:04x}:{pid:04x}:{serial}"), format!("{man}\t{prod}")))
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/rendmp/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ use std::io::Write;
use std::io::prelude::*;
use std::thread;
use std::time::{Duration, Instant};
use strum::VariantNames;
use strum::VariantNames as _;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

spooky

use strum_macros::VariantNames;
use zerocopy::{FromBytes, IntoBytes};

Expand Down
3 changes: 2 additions & 1 deletion humility-bin/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
[package]
name = "humility-bin"
edition.workspace = true
version = "0.12.18"
version = "0.13.0"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚀

license = "MPL-2.0"

[build-dependencies]
Expand Down Expand Up @@ -106,6 +106,7 @@ parse_int = { workspace = true }
scroll = { workspace = true }
serde = { workspace = true }
toml = { workspace = true }
tracing-subscriber = { workspace = true }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we're not using env_logger, then it can be removed (but I think we do want to keep it, see comment above)


[dev-dependencies]
trycmd = { workspace = true }
Expand Down
Loading
Loading