use crate::architecture::arm::sequences::DefaultArmSequence;
use crate::architecture::arm::{ApAddress, DpAddress};
use crate::config::{ChipInfo, MemoryRegion, RegistryError, Target, TargetSelector};
use crate::core::{Architecture, CoreState, SpecificCoreState};
use crate::{
architecture::{
arm::{
ap::{GenericAp, MemoryAp},
communication_interface::{ArmProbeInterface, MemoryApInformation},
memory::{Component, CoresightComponent},
ApInformation, SwoConfig, SwoReader,
},
riscv::communication_interface::RiscvCommunicationInterface,
},
config::DebugSequence,
};
use crate::{AttachMethod, Core, CoreType, Error, Probe};
use anyhow::anyhow;
use std::{fmt, time::Duration};
#[derive(Debug)]
pub struct Session {
target: Target,
interface: ArchitectureInterface,
cores: Vec<(SpecificCoreState, CoreState)>,
}
enum ArchitectureInterface {
Arm(Box<dyn ArmProbeInterface + 'static>),
Riscv(Box<RiscvCommunicationInterface>),
}
impl fmt::Debug for ArchitectureInterface {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
ArchitectureInterface::Arm(..) => f.write_str("ArchitectureInterface::Arm(..)"),
ArchitectureInterface::Riscv(iface) => f
.debug_tuple("ArchitectureInterface::Riscv")
.field(iface)
.finish(),
}
}
}
impl From<ArchitectureInterface> for Architecture {
fn from(value: ArchitectureInterface) -> Self {
match value {
ArchitectureInterface::Arm(_) => Architecture::Arm,
ArchitectureInterface::Riscv(_) => Architecture::Riscv,
}
}
}
impl ArchitectureInterface {
fn attach<'probe, 'target: 'probe>(
&'probe mut self,
core: &'probe mut SpecificCoreState,
core_state: &'probe mut CoreState,
target: &'target Target,
) -> Result<Core<'probe>, Error> {
match self {
ArchitectureInterface::Arm(state) => {
let config = target
.cores
.get(core_state.id())
.ok_or_else(|| Error::CoreNotFound(core_state.id()))?;
let arm_core_access_options = match &config.core_access_options {
probe_rs_target::CoreAccessOptions::Arm(opt) => opt,
probe_rs_target::CoreAccessOptions::Riscv(_) => {
unreachable!("This should never happen. Please file a bug if it does.")
}
};
let dp = match arm_core_access_options.psel {
0 => DpAddress::Default,
x => DpAddress::Multidrop(x),
};
let ap = ApAddress {
dp,
ap: arm_core_access_options.ap,
};
let memory = state.memory_interface(MemoryAp::new(ap))?;
core.attach_arm(core_state, memory, target)
}
ArchitectureInterface::Riscv(state) => core.attach_riscv(core_state, state),
}
}
}
impl Session {
pub(crate) fn new(
probe: Probe,
target: TargetSelector,
attach_method: AttachMethod,
permissions: Permissions,
) -> Result<Self, Error> {
let (mut probe, target) = get_target_from_selector(target, attach_method, probe)?;
let cores = target
.cores
.iter()
.enumerate()
.map(|(id, core)| {
(
SpecificCoreState::from_core_type(core.core_type),
Core::create_state(id, core.core_access_options.clone()),
)
})
.collect();
let mut session = match target.architecture() {
Architecture::Arm => {
let config = target.cores[0].clone();
let arm_core_access_options = match config.core_access_options {
probe_rs_target::CoreAccessOptions::Arm(opt) => opt,
probe_rs_target::CoreAccessOptions::Riscv(_) => {
unreachable!("This should never happen. Please file a bug if it does.")
}
};
let default_memory_ap = MemoryAp::new(ApAddress {
dp: match arm_core_access_options.psel {
0 => DpAddress::Default,
x => DpAddress::Multidrop(x),
},
ap: arm_core_access_options.ap,
});
let sequence_handle = match &target.debug_sequence {
DebugSequence::Arm(sequence) => sequence.clone(),
DebugSequence::Riscv(_) => {
panic!("Mismatch between architecture and sequence type!")
}
};
if AttachMethod::UnderReset == attach_method {
if let Some(dap_probe) = probe.try_as_dap_probe() {
sequence_handle.reset_hardware_assert(dap_probe)?;
} else {
log::info!(
"Custom reset sequences are not supported on {}.",
probe.get_name()
);
log::info!("Falling back to standard probe reset.");
probe.target_reset_assert()?;
}
}
probe.inner_attach()?;
let interface = probe.try_into_arm_interface().map_err(|(_, err)| err)?;
let mut interface = interface.initialize(sequence_handle.clone())?;
sequence_handle.debug_device_unlock(
&mut interface,
default_memory_ap,
&permissions,
)?;
{
for i in 0..target.cores.len() {
let config = target.cores[i].clone();
let arm_core_access_options = match config.core_access_options {
probe_rs_target::CoreAccessOptions::Arm(opt) => opt,
probe_rs_target::CoreAccessOptions::Riscv(_) => {
unreachable!(
"This should never happen. Please file a bug if it does."
)
}
};
let mem_ap = MemoryAp::new(ApAddress {
dp: match arm_core_access_options.psel {
0 => DpAddress::Default,
x => DpAddress::Multidrop(x),
},
ap: arm_core_access_options.ap,
});
let mut memory_interface = interface.memory_interface(mem_ap)?;
sequence_handle.debug_core_start(
&mut memory_interface,
config.core_type,
arm_core_access_options.debug_base,
arm_core_access_options.cti_base,
)?;
}
}
let session = if attach_method == AttachMethod::UnderReset {
{
let mut memory_interface = interface.memory_interface(default_memory_ap)?;
sequence_handle.reset_catch_set(
&mut memory_interface,
config.core_type,
arm_core_access_options.debug_base,
)?;
sequence_handle.reset_hardware_deassert(&mut memory_interface)?;
}
let mut session = Session {
target,
interface: ArchitectureInterface::Arm(interface),
cores,
};
{
let mut core = session.core(0)?;
core.wait_for_core_halted(Duration::from_millis(100))?;
}
{
let interface = session.get_arm_interface()?;
let mut memory_interface = interface.memory_interface(default_memory_ap)?;
sequence_handle.reset_catch_clear(
&mut memory_interface,
config.core_type,
arm_core_access_options.debug_base,
)?;
}
{
let mut core = session.core(0)?;
core.wait_for_core_halted(Duration::from_millis(100))?;
}
session
} else {
Session {
target,
interface: ArchitectureInterface::Arm(interface),
cores,
}
};
session
}
Architecture::Riscv => {
let sequence_handle = match &target.debug_sequence {
DebugSequence::Riscv(sequence) => sequence.clone(),
DebugSequence::Arm(_) => {
panic!("Mismatch between architecture and sequence type!")
}
};
probe.inner_attach()?;
let interface = probe
.try_into_riscv_interface()
.map_err(|(_probe, err)| err)?;
let mut session = Session {
target,
interface: ArchitectureInterface::Riscv(Box::new(interface)),
cores,
};
{
let mut core = session.core(0)?;
core.halt(Duration::from_millis(100))?;
}
sequence_handle.on_connect(session.get_riscv_interface()?)?;
session
}
};
session.clear_all_hw_breakpoints()?;
Ok(session)
}
pub fn auto_attach(
target: impl Into<TargetSelector>,
permissions: Permissions,
) -> Result<Session, Error> {
let probes = Probe::list_all();
let probe = probes
.get(0)
.ok_or(Error::UnableToOpenProbe("No probe was found"))?
.open()?;
probe.attach(target, permissions)
}
pub fn list_cores(&self) -> Vec<(usize, CoreType)> {
self.cores
.iter()
.map(|(t, _)| t.core_type())
.enumerate()
.collect()
}
pub fn core(&mut self, n: usize) -> Result<Core<'_>, Error> {
let (core, core_state) = self.cores.get_mut(n).ok_or(Error::CoreNotFound(n))?;
self.interface.attach(core, core_state, &self.target)
}
pub fn read_swo(&mut self) -> Result<Vec<u8>, Error> {
let interface = self.get_arm_interface()?;
interface.read_swo()
}
pub fn swo_reader(&mut self) -> Result<SwoReader, Error> {
let interface = self.get_arm_interface()?;
Ok(SwoReader::new(interface))
}
pub fn get_arm_interface(&mut self) -> Result<&mut Box<dyn ArmProbeInterface>, Error> {
let interface = match &mut self.interface {
ArchitectureInterface::Arm(state) => state,
_ => return Err(Error::ArchitectureRequired(&["ARMv7", "ARMv8"])),
};
Ok(interface)
}
fn get_riscv_interface(&mut self) -> Result<&mut Box<RiscvCommunicationInterface>, Error> {
let interface = match &mut self.interface {
ArchitectureInterface::Riscv(interface) => interface,
_ => return Err(Error::ArchitectureRequired(&["Riscv"])),
};
Ok(interface)
}
pub fn get_arm_components(&mut self) -> Result<Vec<CoresightComponent>, Error> {
let interface = self.get_arm_interface()?;
let mut components = Vec::new();
let dp = DpAddress::Default;
for ap_index in 0..(interface.num_access_ports(dp)? as u8) {
let ap_information = interface
.ap_information(GenericAp::new(ApAddress { dp, ap: ap_index }))?
.clone();
let component = match ap_information {
ApInformation::MemoryAp(MemoryApInformation {
debug_base_address: 0,
..
}) => Err(Error::Other(anyhow!("AP has a base address of 0"))),
ApInformation::MemoryAp(MemoryApInformation {
address,
debug_base_address,
..
}) => {
let ap = MemoryAp::new(address);
let mut memory = interface.memory_interface(ap)?;
let component = Component::try_parse(&mut memory, debug_base_address)
.map_err(Error::architecture_specific)?;
Ok(CoresightComponent::new(component, ap))
}
ApInformation::Other { address } => {
Err(Error::Other(anyhow!(
"AP {:#x?} is not a MemoryAP, unable to get ARM component.",
address
)))
}
};
match component {
Ok(component) => {
components.push(component);
}
Err(e) => {
log::info!("Not counting AP {} because of: {}", ap_index, e);
}
}
}
Ok(components)
}
pub fn target(&self) -> &Target {
&self.target
}
pub fn setup_swv(&mut self, core_index: usize, config: &SwoConfig) -> Result<(), Error> {
{
let interface = self.get_arm_interface()?;
interface.enable_swo(config)?;
}
{
let mut core = self.core(core_index)?;
crate::architecture::arm::component::enable_tracing(&mut core)?;
}
let components = self.get_arm_components()?;
let interface = self.get_arm_interface()?;
crate::architecture::arm::component::setup_swv(interface, &components, config)
}
pub fn disable_swv(&mut self, core_index: usize) -> Result<(), Error> {
crate::architecture::arm::component::disable_swv(&mut self.core(core_index)?)
}
pub fn add_swv_data_trace(&mut self, unit: usize, address: u32) -> Result<(), Error> {
let components = self.get_arm_components()?;
let interface = self.get_arm_interface()?;
crate::architecture::arm::component::add_swv_data_trace(
interface,
&components,
unit,
address,
)
}
pub fn remove_swv_data_trace(&mut self, unit: usize) -> Result<(), Error> {
let components = self.get_arm_components()?;
let interface = self.get_arm_interface()?;
crate::architecture::arm::component::remove_swv_data_trace(interface, &components, unit)
}
#[deprecated = "Use the Session::target function instead"]
pub fn memory_map(&self) -> &[MemoryRegion] {
&self.target.memory_map
}
pub fn architecture(&self) -> Architecture {
match self.interface {
ArchitectureInterface::Arm(_) => Architecture::Arm,
ArchitectureInterface::Riscv(_) => Architecture::Riscv,
}
}
pub fn clear_all_hw_breakpoints(&mut self) -> Result<(), Error> {
{ 0..self.cores.len() }.try_for_each(|n| {
self.core(n)
.and_then(|mut core| core.clear_all_hw_breakpoints())
})
}
}
static_assertions::assert_impl_all!(Session: Send);
impl Drop for Session {
fn drop(&mut self) {
if let Err(err) = { 0..self.cores.len() }.try_for_each(|i| {
self.core(i)
.and_then(|mut core| core.clear_all_hw_breakpoints())
}) {
log::warn!("Could not clear all hardware breakpoints: {:?}", err);
}
if let Err(err) = { 0..self.cores.len() }
.try_for_each(|i| self.core(i).and_then(|mut core| core.on_session_stop()))
{
log::warn!("Error during on_session_stop: {:?}", err);
}
if let Err(err) = { 0..self.cores.len() }.try_for_each(|i| {
let is_cortex_m = self.core(i)?.core_type().is_cortex_m();
if is_cortex_m {
self.disable_swv(i)
} else {
Ok(())
}
}) {
log::warn!("Could not stop core tracing: {:?}", err);
}
if let DebugSequence::Arm(sequence) = &self.target.debug_sequence {
let sequence = sequence.clone();
let interface = self.get_arm_interface().unwrap();
if sequence.debug_core_stop(interface).is_err() {
log::warn!("Failed to deconfigure device during shutdown");
}
}
}
}
fn get_target_from_selector(
target: TargetSelector,
attach_method: AttachMethod,
probe: Probe,
) -> Result<(Probe, Target), Error> {
let mut probe = probe;
let target = match target {
TargetSelector::Unspecified(name) => crate::config::get_target_by_name(name)?,
TargetSelector::Specified(target) => target,
TargetSelector::Auto => {
let mut found_chip = None;
if AttachMethod::UnderReset == attach_method {
probe.target_reset_assert()?;
}
probe.inner_attach()?;
if probe.has_arm_interface() {
match probe.try_into_arm_interface() {
Ok(interface) => {
let mut interface = interface.initialize(DefaultArmSequence::create())?;
let dp = DpAddress::Default;
let found_arm_chip = interface
.read_chip_info_from_rom_table(dp)
.unwrap_or_else(|e| {
log::info!("Error during auto-detection of ARM chips: {}", e);
None
});
found_chip = found_arm_chip.map(ChipInfo::from);
probe = interface.close();
}
Err((returned_probe, err)) => {
probe = returned_probe;
log::debug!("Error using ARM interface: {}", err);
}
}
} else {
log::debug!("No ARM interface was present. Skipping Riscv autodetect.");
}
if found_chip.is_none() && probe.has_riscv_interface() {
match probe.try_into_riscv_interface() {
Ok(mut interface) => {
let idcode = interface.read_idcode();
log::debug!("ID Code read over JTAG: {:x?}", idcode);
probe = interface.close();
}
Err((returned_probe, err)) => {
log::debug!("Error during autodetection of RISCV chips: {}", err);
probe = returned_probe;
}
}
} else {
log::debug!("No RISCV interface was present. Skipping Riscv autodetect.");
}
probe.target_reset_deassert()?;
if let Some(chip) = found_chip {
crate::config::get_target_by_chip_info(chip)?
} else {
return Err(Error::ChipNotFound(RegistryError::ChipAutodetectFailed));
}
}
};
Ok((probe, target))
}
#[non_exhaustive]
#[derive(Debug, Clone, Default)]
pub struct Permissions {
erase_all: bool,
}
impl Permissions {
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn allow_erase_all(self) -> Self {
Self {
erase_all: true,
..self
}
}
pub(crate) fn erase_all(&self) -> Result<(), crate::Error> {
if self.erase_all {
Ok(())
} else {
Err(crate::Error::MissingPermissions("erase_all".into()))
}
}
}