Skip to main content

probe_rs/
session.rs

1use crate::{
2    Core, CoreType, Error,
3    architecture::{
4        arm::{
5            ArmError, FullyQualifiedApAddress, SwoReader,
6            communication_interface::ArmDebugInterface,
7            component::{TraceSink, get_arm_components},
8            dp::DpAddress,
9            memory::CoresightComponent,
10            sequences::{ArmDebugSequence, DefaultArmSequence},
11        },
12        riscv::{
13            communication_interface::{
14                RiscvCommunicationInterface, RiscvDebugInterfaceState, RiscvError,
15            },
16            dtm::mem_ap_dtm::MemApDtm,
17        },
18        xtensa::communication_interface::{
19            XtensaCommunicationInterface, XtensaDebugInterfaceState, XtensaError,
20        },
21    },
22    config::{CoreExt, DebugSequence, RegistryError, Target, TargetSelector, registry::Registry},
23    core::{Architecture, CombinedCoreState},
24    probe::{
25        AttachMethod, DebugProbeError, Probe, ProbeCreationError, WireProtocol,
26        fake_probe::FakeProbe, list::Lister,
27    },
28};
29use std::ops::DerefMut;
30use std::{fmt, sync::Arc, time::Duration};
31
32/// The `Session` struct represents an active debug session.
33///
34/// ## Creating a session
35/// The session can be created by calling the [Session::auto_attach()] function,
36/// which tries to automatically select a probe, and then connect to the target.
37///
38/// For more control, the [Probe::attach()] and [Probe::attach_under_reset()]
39/// methods can be used to open a `Session` from a specific [Probe].
40///
41/// # Usage
42/// The Session is the common handle that gives a user exclusive access to an active probe.
43/// You can create and share a session between threads to enable multiple stakeholders (e.g. GDB and RTT) to access the target taking turns, by using `Arc<FairMutex<Session>>`.
44///
45/// If you do so, make sure that both threads sleep in between tasks such that other stakeholders may take their turn.
46///
47/// To get access to a single [Core] from the `Session`, the [Session::core()] method can be used.
48/// Please see the [Session::core()] method for more usage guidelines.
49///
50#[derive(Debug)]
51pub struct Session {
52    target: Target,
53    interfaces: ArchitectureInterface,
54    cores: Vec<CombinedCoreState>,
55    configured_trace_sink: Option<TraceSink>,
56}
57
58/// The `SessionConfig` struct is used to configure a new `Session` during auto-attach.
59///
60/// ## Configuring auto attach
61/// The SessionConfig can be used to control the behavior of the auto-attach function.
62/// It should be used in the [Session::auto_attach()] method.
63/// This includes setting the speed of the probe and the protocol to use, as well as the permissions.
64///
65#[derive(Default, Debug)]
66pub struct SessionConfig {
67    /// Debug permissions
68    pub permissions: Permissions,
69    /// Speed of the WireProtocol in kHz
70    pub speed: Option<u32>,
71    /// WireProtocol to use
72    pub protocol: Option<WireProtocol>,
73}
74
75enum JtagInterface {
76    Riscv(RiscvDebugInterfaceState),
77    Xtensa(XtensaDebugInterfaceState),
78    Unknown,
79}
80
81impl JtagInterface {
82    /// Returns the debug module's intended architecture.
83    fn architecture(&self) -> Option<Architecture> {
84        match self {
85            JtagInterface::Riscv(_) => Some(Architecture::Riscv),
86            JtagInterface::Xtensa(_) => Some(Architecture::Xtensa),
87            JtagInterface::Unknown => None,
88        }
89    }
90}
91
92impl fmt::Debug for JtagInterface {
93    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
94        match self {
95            JtagInterface::Riscv(_) => f.write_str("Riscv(..)"),
96            JtagInterface::Xtensa(_) => f.write_str("Xtensa(..)"),
97            JtagInterface::Unknown => f.write_str("Unknown"),
98        }
99    }
100}
101
102// TODO: this is somewhat messy because I omitted separating the Probe out of the ARM interface.
103enum ArchitectureInterface {
104    Arm(Box<dyn ArmDebugInterface + 'static>),
105    /// ARM DAP with RISC-V cores reachable via mem-AP (e.g. RP2350).
106    ArmWithRiscv {
107        arm: Box<dyn ArmDebugInterface + 'static>,
108        /// Per core_id: Some((ap, state)) for RISC-V cores over mem-AP, None otherwise.
109        riscv_mem_ap_cores: Vec<Option<(FullyQualifiedApAddress, RiscvDebugInterfaceState)>>,
110    },
111    Jtag(Probe, Vec<JtagInterface>),
112}
113
114impl fmt::Debug for ArchitectureInterface {
115    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
116        match self {
117            ArchitectureInterface::Arm(_) => f.write_str("ArchitectureInterface::Arm(..)"),
118            ArchitectureInterface::ArmWithRiscv { .. } => {
119                f.write_str("ArchitectureInterface::ArmWithRiscv { .. }")
120            }
121            ArchitectureInterface::Jtag(_, ifaces) => f
122                .debug_tuple("ArchitectureInterface::Jtag(..)")
123                .field(ifaces)
124                .finish(),
125        }
126    }
127}
128
129impl ArchitectureInterface {
130    fn attach<'probe, 'target: 'probe>(
131        &'probe mut self,
132        target: &'probe Target,
133        combined_state: &'probe mut CombinedCoreState,
134    ) -> Result<Core<'probe>, Error> {
135        match self {
136            ArchitectureInterface::Arm(interface) => combined_state.attach_arm(target, interface),
137            ArchitectureInterface::ArmWithRiscv {
138                arm,
139                riscv_mem_ap_cores,
140            } => {
141                let core_id = combined_state.id();
142                if let Some(Some((ap, state))) = riscv_mem_ap_cores.get_mut(core_id) {
143                    let memory = arm.memory_interface(ap).map_err(Error::Arm)?;
144                    let dtm = MemApDtm::new(memory);
145                    let iface =
146                        RiscvCommunicationInterface::new(Box::new(dtm), &mut state.interface_state);
147                    combined_state.attach_riscv(target, iface)
148                } else {
149                    combined_state.attach_arm(target, arm)
150                }
151            }
152            ArchitectureInterface::Jtag(probe, ifaces) => {
153                let idx = combined_state.jtag_tap_index();
154                if let Some(probe) = probe.try_as_jtag_probe() {
155                    probe.select_target(idx)?;
156                }
157                match &mut ifaces[idx] {
158                    JtagInterface::Riscv(state) => {
159                        let factory = probe.try_get_riscv_interface_builder()?;
160                        let iface = factory.attach_auto(target, state)?;
161                        combined_state.attach_riscv(target, iface)
162                    }
163                    JtagInterface::Xtensa(state) => {
164                        let iface = probe.try_get_xtensa_interface(state)?;
165                        combined_state.attach_xtensa(target, iface)
166                    }
167                    JtagInterface::Unknown => {
168                        unreachable!(
169                            "Tried to attach to unknown interface {idx}. This should never happen."
170                        )
171                    }
172                }
173            }
174        }
175    }
176}
177
178impl Session {
179    /// Open a new session with a given debug target.
180    pub(crate) fn new(
181        probe: Probe,
182        target: TargetSelector,
183        attach_method: AttachMethod,
184        permissions: Permissions,
185        registry: &Registry,
186    ) -> Result<Self, Error> {
187        let (probe, target) = get_target_from_selector(target, attach_method, probe, registry)?;
188
189        let cores = target
190            .cores
191            .iter()
192            .enumerate()
193            .map(|(id, core)| {
194                Core::create_state(
195                    id,
196                    core.core_access_options.clone(),
197                    &target,
198                    core.core_type,
199                )
200            })
201            .collect();
202
203        // Use ARM DAP path when the target connects via SWD/DAP: either ARM cores or RISC-V cores
204        // over mem-AP (e.g. RP235x_riscv).
205        let mut session = if target.default_core().memory_ap().is_some() {
206            Self::attach_arm_debug_interface(probe, target, attach_method, permissions, cores)?
207        } else {
208            Self::attach_jtag(probe, target, attach_method, permissions, cores)?
209        };
210
211        session.clear_all_hw_breakpoints()?;
212
213        Ok(session)
214    }
215
216    fn attach_arm_debug_interface(
217        mut probe: Probe,
218        target: Target,
219        attach_method: AttachMethod,
220        permissions: Permissions,
221        cores: Vec<CombinedCoreState>,
222    ) -> Result<Self, Error> {
223        let default_core = target.default_core();
224
225        let default_memory_ap = default_core.memory_ap().ok_or_else(|| {
226            Error::Other(format!(
227                "Unable to connect to core {default_core:?}, no memory AP configured"
228            ))
229        })?;
230
231        let default_dp = default_memory_ap.dp();
232
233        // Use ARM sequence for DAP operations (unlock, reset). For RISC-V-over-mem-AP targets
234        // (e.g. RP235x_riscv) the target's debug_sequence is Riscv; use default ARM sequence.
235        let sequence_handle = match &target.debug_sequence {
236            DebugSequence::Arm(sequence) => sequence.clone(),
237            DebugSequence::Riscv(_) => DefaultArmSequence::create(),
238            _ => unreachable!("DAP path only used for ARM or RISC-V-over-mem-AP targets"),
239        };
240
241        if AttachMethod::UnderReset == attach_method {
242            let _span = tracing::debug_span!("Asserting hardware reset").entered();
243
244            if let Some(dap_probe) = probe.try_as_dap_probe() {
245                sequence_handle.reset_hardware_assert(dap_probe)?;
246            } else {
247                tracing::info!(
248                    "Custom reset sequences are not supported on {}.",
249                    probe.get_name()
250                );
251                tracing::info!("Falling back to standard probe reset.");
252                probe.target_reset_assert()?;
253            }
254        }
255
256        if let Some(jtag) = target.jtag.as_ref()
257            && let Some(scan_chain) = jtag.scan_chain.clone()
258            && let Some(probe) = probe.try_as_jtag_probe()
259        {
260            probe.set_expected_scan_chain(&scan_chain)?;
261        }
262
263        probe.attach_to_unspecified()?;
264        if probe.protocol() == Some(WireProtocol::Jtag)
265            && let Some(probe) = probe.try_as_jtag_probe()
266            && let Ok(chain) = probe.scan_chain()
267            && !chain.is_empty()
268        {
269            for core in &cores {
270                probe.select_target(core.jtag_tap_index())?;
271            }
272        }
273
274        let mut interface = probe
275            .try_into_arm_debug_interface(sequence_handle.clone())
276            .map_err(|(_, err)| err)?;
277
278        interface.select_debug_port(default_dp)?;
279
280        let unlock_span = tracing::debug_span!("debug_device_unlock").entered();
281
282        // Enable debug mode
283        let unlock_res =
284            sequence_handle.debug_device_unlock(&mut *interface, &default_memory_ap, &permissions);
285        drop(unlock_span);
286
287        match unlock_res {
288            Ok(()) => (),
289            // In case this happens after unlock. Try to re-attach the probe once.
290            Err(ArmError::ReAttachRequired) => {
291                Self::reattach_arm_interface(&mut interface, &sequence_handle)?;
292            }
293            Err(e) => return Err(Error::Arm(e)),
294        }
295
296        if attach_method == AttachMethod::UnderReset {
297            {
298                for core in &cores {
299                    if core.is_arm_core() {
300                        core.arm_reset_catch_set(&mut *interface)?;
301                    }
302                }
303
304                let reset_hardware_deassert =
305                    tracing::debug_span!("reset_hardware_deassert").entered();
306
307                // A timeout here indicates that the reset pin is probably not properly connected.
308                if let Err(e) =
309                    sequence_handle.reset_hardware_deassert(&mut *interface, &default_memory_ap)
310                {
311                    if matches!(e, ArmError::Timeout) {
312                        tracing::warn!(
313                            "Timeout while deasserting hardware reset pin. This indicates that the reset pin is not properly connected. Please check your hardware setup."
314                        );
315                    }
316
317                    return Err(e.into());
318                }
319                drop(reset_hardware_deassert);
320            }
321
322            // Now that hardware reset is de-asserted, for each core, setup debugging.
323            // (Some chips keep cores in power-down under hardware-reset.)
324            for core in &cores {
325                if core.is_arm_core() {
326                    core.enable_arm_debug(&mut *interface)?;
327                }
328            }
329
330            let interfaces = Self::build_arm_interfaces(&target, interface)?;
331            let mut session = Session {
332                target,
333                interfaces,
334                cores,
335                configured_trace_sink: None,
336            };
337
338            {
339                // Wait for the core to be halted. The core should be
340                // halted because we set the `reset_catch` earlier, which
341                // means that the core should stop when coming out of reset.
342                // Non-ARM cores (e.g. RISC-V over mem-AP) did not use reset catch; skip this path.
343
344                for core_id in 0..session.cores.len() {
345                    if !session.cores[core_id].is_arm_core() {
346                        continue;
347                    }
348                    let mut core = session
349                        .core(core_id)
350                        .inspect_err(|e| tracing::error!("Unable to get core {core_id}: {e}"))?;
351
352                    core.wait_for_core_halted(Duration::from_millis(100))
353                        .inspect_err(|e| {
354                            tracing::error!("Unable to wait for {core_id} halted: {e}")
355                        })?;
356
357                    core.reset_catch_clear().inspect_err(|e| {
358                        tracing::error!("Unable to clear catch for {core_id} : {e}")
359                    })?;
360                }
361            }
362
363            Ok(session)
364        } else {
365            // For each core, setup debugging
366            for core in &cores {
367                if core.is_arm_core() {
368                    core.enable_arm_debug(&mut *interface)?;
369                }
370            }
371
372            let interfaces = Self::build_arm_interfaces(&target, interface)?;
373            Ok(Session {
374                target,
375                interfaces,
376                cores,
377                configured_trace_sink: None,
378            })
379        }
380    }
381
382    /// Returns `Arm(interface)` for a normal Arm system, or `ArmWithRiscv { .. }` if the target has
383    /// RISC-V cores reachable via mem-AP (like on RP235x).
384    fn build_arm_interfaces(
385        target: &Target,
386        interface: Box<dyn ArmDebugInterface + 'static>,
387    ) -> Result<ArchitectureInterface, Error> {
388        use probe_rs_target::Architecture;
389        let mut riscv_mem_ap_cores: Vec<
390            Option<(FullyQualifiedApAddress, RiscvDebugInterfaceState)>,
391        > = target
392            .cores
393            .iter()
394            .map(|core| {
395                if core.core_type.architecture() == Architecture::Riscv {
396                    core.memory_ap()
397                        .map(|ap| (ap, RiscvDebugInterfaceState::new(Box::new(()))))
398                } else {
399                    None
400                }
401            })
402            .collect();
403        let has_riscv_mem_ap = riscv_mem_ap_cores.iter().any(Option::is_some);
404        if has_riscv_mem_ap {
405            let mut arm = interface;
406            for (ap, state) in riscv_mem_ap_cores.iter_mut().flatten() {
407                let memory = arm.memory_interface(ap).map_err(Error::Arm)?;
408                let dtm = MemApDtm::new(memory);
409                let mut iface =
410                    RiscvCommunicationInterface::new(Box::new(dtm), &mut state.interface_state);
411                iface.enter_debug_mode().map_err(Error::Riscv)?;
412            }
413            Ok(ArchitectureInterface::ArmWithRiscv {
414                arm,
415                riscv_mem_ap_cores,
416            })
417        } else {
418            Ok(ArchitectureInterface::Arm(interface))
419        }
420    }
421
422    fn attach_jtag(
423        mut probe: Probe,
424        target: Target,
425        _attach_method: AttachMethod,
426        _permissions: Permissions,
427        cores: Vec<CombinedCoreState>,
428    ) -> Result<Self, Error> {
429        // While we still don't support mixed architectures
430        // (they'd need per-core debug sequences), we can at least
431        // handle most of the setup in the same way.
432        if let Some(jtag) = target.jtag.as_ref()
433            && let Some(scan_chain) = jtag.scan_chain.clone()
434            && let Some(probe) = probe.try_as_jtag_probe()
435        {
436            if jtag.force_scan_chain {
437                // Bypass JTAG auto-detection entirely; use the scan chain from the target YAML.
438                // This is required for targets whose TAP does not respond to the standard IDCODE
439                // DR scan (e.g., some RISC-V cores during early power-up).
440                probe.set_scan_chain(&scan_chain)?;
441            } else {
442                probe.set_expected_scan_chain(&scan_chain)?;
443            }
444        }
445
446        probe.attach_to_unspecified()?;
447        if let Some(probe) = probe.try_as_jtag_probe()
448            && let Ok(chain) = probe.scan_chain()
449            && !chain.is_empty()
450        {
451            for core in &cores {
452                probe.select_target(core.jtag_tap_index())?;
453            }
454        }
455
456        // We try to guess the TAP number. Normally we trust the scan chain, but some probes are
457        // only quasi-JTAG (wch-link), so we'll have to work with at least 1, but if we're guessing
458        // we can also use the highest number specified in the target YAML.
459
460        // FIXME: This is terribly JTAG-specific. Since we don't really support anything else yet,
461        // it should be fine for now.
462        let highest_idx = cores.iter().map(|c| c.jtag_tap_index()).max().unwrap_or(0);
463        let tap_count = if let Some(probe) = probe.try_as_jtag_probe() {
464            match probe.scan_chain() {
465                Ok(scan_chain) => scan_chain.len().max(highest_idx + 1),
466                Err(_) => highest_idx + 1,
467            }
468        } else {
469            highest_idx + 1
470        };
471        let mut interfaces = std::iter::repeat_with(|| JtagInterface::Unknown)
472            .take(tap_count)
473            .collect::<Vec<_>>();
474
475        // Create a new interface by walking through the cores and initialising the TAPs that
476        // we find mentioned.
477        for core in cores.iter() {
478            let iface_idx = core.jtag_tap_index();
479
480            let core_arch = core.core_type().architecture();
481
482            if let Some(debug_arch) = interfaces[iface_idx].architecture() {
483                if core_arch == debug_arch {
484                    // Already initialised.
485                    continue;
486                }
487                return Err(Error::Probe(DebugProbeError::Other(format!(
488                    "{core_arch:?} core can not be mixed with a {debug_arch:?} debug module.",
489                ))));
490            }
491
492            interfaces[iface_idx] = match core_arch {
493                Architecture::Riscv => {
494                    let factory = probe.try_get_riscv_interface_builder()?;
495                    let mut state = factory.create_state();
496                    {
497                        let mut interface = factory.attach_auto(&target, &mut state)?;
498                        interface.enter_debug_mode()?;
499                    }
500
501                    JtagInterface::Riscv(state)
502                }
503                Architecture::Xtensa => JtagInterface::Xtensa(XtensaDebugInterfaceState::default()),
504                _ => {
505                    return Err(Error::Probe(DebugProbeError::Other(format!(
506                        "Unsupported core architecture {core_arch:?}",
507                    ))));
508                }
509            };
510        }
511
512        let interfaces = ArchitectureInterface::Jtag(probe, interfaces);
513
514        let mut session = Session {
515            target,
516            interfaces,
517            cores,
518            configured_trace_sink: None,
519        };
520
521        // Connect to the cores
522        match session.target.debug_sequence.clone() {
523            DebugSequence::Xtensa(_) => {}
524
525            DebugSequence::Riscv(sequence) => {
526                for core_id in 0..session.cores.len() {
527                    sequence.on_connect(&mut session.get_riscv_interface(core_id)?)?;
528                }
529            }
530            _ => unreachable!("Other architectures should have already been handled"),
531        };
532
533        Ok(session)
534    }
535
536    /// Automatically open a probe with the given session config.
537    fn auto_probe(session_config: &SessionConfig) -> Result<Probe, Error> {
538        // Get a list of all available debug probes.
539        let lister = Lister::new();
540
541        let probes = lister.list_all();
542
543        // Use the first probe found.
544        let mut probe = probes
545            .first()
546            .ok_or(Error::Probe(DebugProbeError::ProbeCouldNotBeCreated(
547                ProbeCreationError::NotFound,
548            )))?
549            .open()?;
550
551        // If the caller has specified speed or protocol in SessionConfig, set them
552        if let Some(speed) = session_config.speed {
553            probe.set_speed(speed)?;
554        }
555
556        if let Some(protocol) = session_config.protocol {
557            probe.select_protocol(protocol)?;
558        }
559        Ok(probe)
560    }
561
562    /// Automatically creates a session with the first connected probe found.
563    #[tracing::instrument(skip(target))]
564    pub fn auto_attach(
565        target: impl Into<TargetSelector>,
566        session_config: SessionConfig,
567    ) -> Result<Session, Error> {
568        // Attach to a chip.
569        Self::auto_probe(&session_config)?.attach(target, session_config.permissions)
570    }
571
572    /// Automatically creates a session with the first connected probe found
573    /// using the registry that was provided.
574    #[tracing::instrument(skip(target, registry))]
575    pub fn auto_attach_with_registry(
576        target: impl Into<TargetSelector>,
577        session_config: SessionConfig,
578        registry: &Registry,
579    ) -> Result<Session, Error> {
580        // Attach to a chip.
581        Self::auto_probe(&session_config)?.attach_with_registry(
582            target,
583            session_config.permissions,
584            registry,
585        )
586    }
587
588    /// Lists the available cores with their number and their type.
589    pub fn list_cores(&self) -> Vec<(usize, CoreType)> {
590        self.cores.iter().map(|t| (t.id(), t.core_type())).collect()
591    }
592
593    /// Get access to the session when all cores are halted.
594    ///
595    /// Any previously running cores will be resumed once the closure is executed.
596    pub fn halted_access<R>(
597        &mut self,
598        f: impl FnOnce(&mut Self) -> Result<R, Error>,
599    ) -> Result<R, Error> {
600        let mut resume_state = vec![];
601        for (core, _) in self.list_cores() {
602            let mut c = match self.core(core) {
603                Err(Error::CoreDisabled(_)) => continue,
604                other => other?,
605            };
606            if c.core_halted()? {
607                tracing::info!("Core {core} already halted");
608            } else {
609                tracing::info!("Halting core {core}...");
610                resume_state.push(core);
611                c.halt(Duration::from_millis(100))?;
612            }
613        }
614
615        let r = f(self);
616
617        for core in resume_state {
618            tracing::debug!("Resuming core...");
619            self.core(core)?.run()?;
620        }
621
622        r
623    }
624
625    fn interface_idx(&self, core: usize) -> Result<usize, Error> {
626        self.cores
627            .get(core)
628            .map(|c| c.jtag_tap_index())
629            .ok_or(Error::CoreNotFound(core))
630    }
631
632    /// Attaches to the core with the given number.
633    ///
634    /// ## Usage
635    /// Every time you want to perform an operation on the chip, you need to get the Core handle with the [Session::core()] method. This [Core] handle is merely a view into the core and provides a convenient API surface.
636    ///
637    /// All the state is stored in the [Session] handle.
638    ///
639    /// The first time you call [Session::core()] for a specific core, it will run the attach/init sequences and return a handle to the [Core].
640    ///
641    /// Every subsequent call is a no-op. It simply returns the handle for the user to use in further operations without calling any int sequences again.
642    ///
643    /// It is strongly advised to never store the [Core] handle for any significant duration! Free it as fast as possible such that other stakeholders can have access to the [Core] too.
644    ///
645    /// The idea behind this is: You need the smallest common denominator which you can share between threads. Since you sometimes need the [Core], sometimes the [Probe] or sometimes the [Target], the [Session] is the only common ground and the only handle you should actively store in your code.
646    //
647    // By design, this is called frequently in a session, therefore we limit tracing level to "trace" to avoid spamming the logs.
648    #[tracing::instrument(level = "trace", skip(self), name = "attach_to_core")]
649    pub fn core(&mut self, core_index: usize) -> Result<Core<'_>, Error> {
650        let combined_state = self
651            .cores
652            .get_mut(core_index)
653            .ok_or(Error::CoreNotFound(core_index))?;
654
655        self.interfaces
656            .attach(&self.target, combined_state)
657            .map_err(|e| {
658                if matches!(
659                    e,
660                    Error::Arm(ArmError::CoreDisabled)
661                        | Error::Xtensa(XtensaError::CoreDisabled)
662                        | Error::Riscv(RiscvError::HartUnavailable),
663                ) {
664                    // If the core is disabled, we can't attach to it.
665                    // We can't do anything about it, so we just translate
666                    // and return the error.
667                    // We'll retry at the next call.
668                    Error::CoreDisabled(core_index)
669                } else {
670                    e
671                }
672            })
673    }
674
675    /// Read available trace data from the specified data sink.
676    ///
677    /// This method is only supported for ARM-based targets, and will
678    /// return [ArmError::ArchitectureRequired] otherwise.
679    #[tracing::instrument(skip(self))]
680    pub fn read_trace_data(&mut self) -> Result<Vec<u8>, ArmError> {
681        let sink = self
682            .configured_trace_sink
683            .as_ref()
684            .ok_or(ArmError::TracingUnconfigured)?;
685
686        match sink {
687            TraceSink::Swo(_) => {
688                let interface = self.get_arm_interface()?;
689                interface.read_swo()
690            }
691
692            TraceSink::Tpiu(_) => {
693                panic!("Probe-rs does not yet support reading parallel trace ports");
694            }
695
696            TraceSink::TraceMemory => {
697                let components = self.get_arm_components(DpAddress::Default)?;
698                let interface = self.get_arm_interface()?;
699                crate::architecture::arm::component::read_trace_memory(interface, &components)
700            }
701        }
702    }
703
704    /// Returns an implementation of [std::io::Read] that wraps [SwoAccess::read_swo].
705    ///
706    /// The implementation buffers all available bytes from
707    /// [SwoAccess::read_swo] on each [std::io::Read::read],
708    /// minimizing the chance of a target-side overflow event on which
709    /// trace packets are lost.
710    ///
711    /// [SwoAccess::read_swo]: crate::architecture::arm::swo::SwoAccess
712    pub fn swo_reader(&mut self) -> Result<SwoReader<'_>, Error> {
713        let interface = self.get_arm_interface()?;
714        Ok(SwoReader::new(interface))
715    }
716
717    /// Get the Arm probe interface.
718    pub fn get_arm_interface(&mut self) -> Result<&mut dyn ArmDebugInterface, ArmError> {
719        let interface = match &mut self.interfaces {
720            ArchitectureInterface::Arm(state) => state.deref_mut(),
721            ArchitectureInterface::ArmWithRiscv { arm, .. } => arm.deref_mut(),
722            ArchitectureInterface::Jtag(..) => return Err(ArmError::NoArmTarget),
723        };
724
725        Ok(interface)
726    }
727
728    /// Get the RISC-V probe interface.
729    pub fn get_riscv_interface(
730        &mut self,
731        core_id: usize,
732    ) -> Result<RiscvCommunicationInterface<'_>, Error> {
733        // Get tap_idx first so its borrow of self ends before we borrow self.interfaces.
734        let tap_idx = self.interface_idx(core_id)?;
735        match &mut self.interfaces {
736            ArchitectureInterface::ArmWithRiscv {
737                arm,
738                riscv_mem_ap_cores,
739            } => {
740                if let Some(Some((ap, state))) = riscv_mem_ap_cores.get_mut(core_id) {
741                    let memory = arm.memory_interface(ap).map_err(Error::Arm)?;
742                    let dtm = MemApDtm::new(memory);
743                    Ok(RiscvCommunicationInterface::new(
744                        Box::new(dtm),
745                        &mut state.interface_state,
746                    ))
747                } else {
748                    Err(RiscvError::NoRiscvTarget.into())
749                }
750            }
751            ArchitectureInterface::Jtag(probe, ifaces) => {
752                if let Some(probe) = probe.try_as_jtag_probe() {
753                    probe.select_target(tap_idx)?;
754                }
755                if let JtagInterface::Riscv(state) = &mut ifaces[tap_idx] {
756                    let factory = probe.try_get_riscv_interface_builder()?;
757                    Ok(factory.attach_auto(&self.target, state)?)
758                } else {
759                    Err(RiscvError::NoRiscvTarget.into())
760                }
761            }
762            ArchitectureInterface::Arm(_) => Err(RiscvError::NoRiscvTarget.into()),
763        }
764    }
765
766    /// Get the Xtensa probe interface.
767    pub fn get_xtensa_interface(
768        &mut self,
769        core_id: usize,
770    ) -> Result<XtensaCommunicationInterface<'_>, Error> {
771        let tap_idx = self.interface_idx(core_id)?;
772        if let ArchitectureInterface::Jtag(probe, ifaces) = &mut self.interfaces {
773            if let Some(probe) = probe.try_as_jtag_probe() {
774                probe.select_target(tap_idx)?;
775            }
776            if let JtagInterface::Xtensa(state) = &mut ifaces[tap_idx] {
777                return Ok(probe.try_get_xtensa_interface(state)?);
778            }
779        }
780        Err(XtensaError::NoXtensaTarget.into())
781    }
782
783    #[tracing::instrument(skip_all)]
784    fn reattach_arm_interface(
785        interface: &mut Box<dyn ArmDebugInterface>,
786        debug_sequence: &Arc<dyn ArmDebugSequence>,
787    ) -> Result<(), Error> {
788        use crate::probe::DebugProbe;
789
790        let current_dp = interface.current_debug_port();
791
792        // In order to re-attach we need an owned instance to the interface
793        // but we only have &mut. We can work around that by first creating
794        // an instance of a Dummy and then swapping it out for the real one.
795        // perform the re-attach and then swap it back.
796        let mut tmp_interface = Box::<FakeProbe>::default()
797            .try_get_arm_debug_interface(DefaultArmSequence::create())
798            .unwrap();
799
800        std::mem::swap(interface, &mut tmp_interface);
801
802        tracing::debug!("Re-attaching Probe");
803        let mut probe = tmp_interface.close();
804        probe.detach()?;
805        probe.attach_to_unspecified()?;
806
807        let mut new_interface = probe
808            .try_into_arm_debug_interface(debug_sequence.clone())
809            .map_err(|(_, err)| err)?;
810
811        if let Some(current_dp) = current_dp {
812            new_interface.select_debug_port(current_dp)?;
813        }
814        // swap it back
815        std::mem::swap(interface, &mut new_interface);
816
817        tracing::debug!("Probe re-attached");
818        Ok(())
819    }
820
821    /// This function can be used to set up an application which was flashed to RAM.
822    pub fn prepare_running_on_ram(
823        &mut self,
824        vector_table_addr: u64,
825        core_id: usize,
826    ) -> Result<(), crate::Error> {
827        match self.target.debug_sequence.clone() {
828            DebugSequence::Arm(arm_debug_sequence) => {
829                arm_debug_sequence.prepare_running_on_ram(self, vector_table_addr, core_id)
830            }
831            DebugSequence::Riscv(riscv_debug_sequence) => {
832                riscv_debug_sequence.prepare_running_on_ram(self, vector_table_addr, core_id)
833            }
834            DebugSequence::Xtensa(xtensa_debug_sequence) => {
835                xtensa_debug_sequence.prepare_running_on_ram(self, vector_table_addr, core_id)
836            }
837        }
838    }
839
840    /// Check if the connected device has a debug erase sequence defined
841    pub fn has_sequence_erase_all(&self) -> bool {
842        match &self.target.debug_sequence {
843            DebugSequence::Arm(seq) => seq.debug_erase_sequence().is_some(),
844            // Currently, debug_erase_sequence is ARM (and ATSAM) specific
845            _ => false,
846        }
847    }
848
849    /// Erase all flash memory using the Device's Debug Erase Sequence if any
850    ///
851    /// # Returns
852    /// Ok(()) if the device provides a custom erase sequence and it succeeded.
853    ///
854    /// # Errors
855    /// NotImplemented if no custom erase sequence exists
856    /// Err(e) if the custom erase sequence failed
857    pub fn sequence_erase_all(&mut self) -> Result<(), Error> {
858        let interface_ref = match &mut self.interfaces {
859            ArchitectureInterface::Arm(i) => i.deref_mut(),
860            ArchitectureInterface::ArmWithRiscv { arm, .. } => arm.deref_mut(),
861            ArchitectureInterface::Jtag(..) => {
862                return Err(Error::NotImplemented(
863                    "Debug Erase Sequence is not implemented for non-ARM targets.",
864                ));
865            }
866        };
867
868        let DebugSequence::Arm(ref debug_sequence) = self.target.debug_sequence else {
869            unreachable!("This should never happen. Please file a bug if it does.");
870        };
871
872        let erase_sequence = debug_sequence
873            .debug_erase_sequence()
874            .ok_or(Error::Arm(ArmError::NotImplemented("Debug Erase Sequence")))?;
875
876        tracing::info!("Trying Debug Erase Sequence");
877        let erase_result = erase_sequence.erase_all(interface_ref);
878
879        match erase_result {
880            Ok(()) => (),
881            // In case this happens after unlock. Try to re-attach the probe once.
882            Err(ArmError::ReAttachRequired) => match &mut self.interfaces {
883                ArchitectureInterface::Arm(interface) => {
884                    Self::reattach_arm_interface(interface, debug_sequence)?;
885                    for core_state in &self.cores {
886                        core_state.enable_arm_debug(interface.deref_mut())?;
887                    }
888                }
889                ArchitectureInterface::ArmWithRiscv { arm, .. } => {
890                    Self::reattach_arm_interface(arm, debug_sequence)?;
891                    for core_state in &self.cores {
892                        core_state.enable_arm_debug(arm.deref_mut())?;
893                    }
894                }
895                ArchitectureInterface::Jtag(..) => {}
896            },
897            Err(e) => return Err(Error::Arm(e)),
898        }
899        tracing::info!("Device Erased Successfully");
900        Ok(())
901    }
902
903    /// Reads all the available ARM CoresightComponents of the currently attached target.
904    ///
905    /// This will recursively parse the Romtable of the attached target
906    /// and create a list of all the contained components.
907    pub fn get_arm_components(
908        &mut self,
909        dp: DpAddress,
910    ) -> Result<Vec<CoresightComponent>, ArmError> {
911        let interface = self.get_arm_interface()?;
912
913        get_arm_components(interface, dp)
914    }
915
916    /// Get the target description of the connected target.
917    pub fn target(&self) -> &Target {
918        &self.target
919    }
920
921    /// Configure the target and probe for serial wire view (SWV) tracing.
922    pub fn setup_tracing(
923        &mut self,
924        core_index: usize,
925        destination: TraceSink,
926    ) -> Result<(), Error> {
927        // Enable tracing on the target
928        {
929            let mut core = self.core(core_index)?;
930            crate::architecture::arm::component::enable_tracing(&mut core)?;
931        }
932
933        let sequence_handle = match &self.target.debug_sequence {
934            DebugSequence::Arm(sequence) => sequence.clone(),
935            _ => unreachable!("Mismatch between architecture and sequence type!"),
936        };
937
938        let components = self.get_arm_components(DpAddress::Default)?;
939        let interface = self.get_arm_interface()?;
940
941        // Configure SWO on the probe when the trace sink is configured for a serial output. Note
942        // that on some architectures, the TPIU is configured to drive SWO.
943        match destination {
944            TraceSink::Swo(ref config) => {
945                interface.enable_swo(config)?;
946            }
947            TraceSink::Tpiu(ref config) => {
948                interface.enable_swo(config)?;
949            }
950            TraceSink::TraceMemory => {}
951        }
952
953        sequence_handle.trace_start(interface, &components, &destination)?;
954        crate::architecture::arm::component::setup_tracing(interface, &components, &destination)?;
955
956        self.configured_trace_sink.replace(destination);
957
958        Ok(())
959    }
960
961    /// Configure the target to stop emitting SWV trace data.
962    #[tracing::instrument(skip(self))]
963    pub fn disable_swv(&mut self, core_index: usize) -> Result<(), Error> {
964        crate::architecture::arm::component::disable_swv(&mut self.core(core_index)?)
965    }
966
967    /// Begin tracing a memory address over SWV.
968    pub fn add_swv_data_trace(&mut self, unit: usize, address: u32) -> Result<(), ArmError> {
969        let components = self.get_arm_components(DpAddress::Default)?;
970        let interface = self.get_arm_interface()?;
971        crate::architecture::arm::component::add_swv_data_trace(
972            interface,
973            &components,
974            unit,
975            address,
976        )
977    }
978
979    /// Stop tracing from a given SWV unit
980    pub fn remove_swv_data_trace(&mut self, unit: usize) -> Result<(), ArmError> {
981        let components = self.get_arm_components(DpAddress::Default)?;
982        let interface = self.get_arm_interface()?;
983        crate::architecture::arm::component::remove_swv_data_trace(interface, &components, unit)
984    }
985
986    /// Return the `Architecture` of the currently connected chip.
987    /// For ArmWithRiscv (e.g. RP235x_riscv), returns the first core's architecture
988    /// so that RISC-V-only targets report Riscv rather than Arm.
989    pub fn architecture(&self) -> Architecture {
990        match &self.interfaces {
991            ArchitectureInterface::Arm(_) => Architecture::Arm,
992            ArchitectureInterface::ArmWithRiscv { .. } => {
993                self.target.cores[0].core_type.architecture()
994            }
995            ArchitectureInterface::Jtag(_, ifaces) => {
996                if let JtagInterface::Riscv(_) = &ifaces[0] {
997                    Architecture::Riscv
998                } else {
999                    Architecture::Xtensa
1000                }
1001            }
1002        }
1003    }
1004
1005    /// Clears all hardware breakpoints on all cores
1006    pub fn clear_all_hw_breakpoints(&mut self) -> Result<(), Error> {
1007        self.halted_access(|session| {
1008            { 0..session.cores.len() }.try_for_each(|core| {
1009                tracing::info!("Clearing breakpoints for core {core}");
1010
1011                match session.core(core) {
1012                    Ok(mut core) => core.clear_all_hw_breakpoints(),
1013                    Err(Error::CoreDisabled(_)) => Ok(()),
1014                    Err(Error::Riscv(
1015                        crate::architecture::riscv::communication_interface::RiscvError::Timeout,
1016                    )) => {
1017                        tracing::warn!(
1018                            "Core {core} attach or breakpoint clear timed out, skipping"
1019                        );
1020                        Ok(())
1021                    }
1022                    Err(err) => Err(err),
1023                }
1024            })
1025        })
1026    }
1027
1028    /// Resume all cores
1029    pub fn resume_all_cores(&mut self) -> Result<(), Error> {
1030        // Resume cores
1031        for core_id in 0..self.cores.len() {
1032            match self.core(core_id) {
1033                Ok(mut core) => {
1034                    if core.core_halted()? {
1035                        core.run()?;
1036                    }
1037                }
1038                Err(Error::CoreDisabled(i)) => tracing::debug!("Core {i} is disabled"),
1039                Err(error) => return Err(error),
1040            }
1041        }
1042
1043        Ok(())
1044    }
1045}
1046
1047// This test ensures that [Session] is fully [Send] + [Sync].
1048const _: fn() = || {
1049    fn assert_impl_all<T: ?Sized + Send>() {}
1050
1051    assert_impl_all::<Session>();
1052};
1053
1054impl Drop for Session {
1055    #[tracing::instrument(name = "session_drop", skip(self))]
1056    fn drop(&mut self) {
1057        if let Err(err) = self.clear_all_hw_breakpoints() {
1058            tracing::warn!(
1059                "Could not clear all hardware breakpoints: {:?}",
1060                anyhow::anyhow!(err)
1061            );
1062        }
1063
1064        // Call any necessary deconfiguration/shutdown hooks.
1065        if let Err(err) = { 0..self.cores.len() }.try_for_each(|core| match self.core(core) {
1066            Ok(mut core) => core.debug_core_stop(),
1067            Err(Error::CoreDisabled(_)) => Ok(()),
1068            Err(err) => Err(err),
1069        }) {
1070            tracing::warn!("Failed to deconfigure device during shutdown: {:?}", err);
1071        }
1072    }
1073}
1074
1075/// Determine the [Target] from a [TargetSelector].
1076///
1077/// If the selector is [TargetSelector::Unspecified], the target will be looked up in the registry.
1078/// If it its [TargetSelector::Auto], probe-rs will try to determine the target automatically, based on
1079/// information read from the chip.
1080fn get_target_from_selector(
1081    target: TargetSelector,
1082    attach_method: AttachMethod,
1083    mut probe: Probe,
1084    registry: &Registry,
1085) -> Result<(Probe, Target), Error> {
1086    let target = match target {
1087        TargetSelector::Unspecified(name) => registry.get_target_by_name(name)?,
1088        TargetSelector::Specified(target) => target,
1089        TargetSelector::Auto => {
1090            // At this point we do not know what the target is, so we cannot use the chip specific reset sequence.
1091            // Thus, we try just using a normal reset for target detection if we want to do so under reset.
1092            // This can of course fail, but target detection is a best effort, not a guarantee!
1093            if AttachMethod::UnderReset == attach_method {
1094                probe.target_reset_assert()?;
1095            }
1096            probe.attach_to_unspecified()?;
1097
1098            let (returned_probe, found_target) =
1099                crate::vendor::auto_determine_target(registry, probe)?;
1100            probe = returned_probe;
1101
1102            if AttachMethod::UnderReset == attach_method {
1103                // Now we can deassert reset in case we asserted it before.
1104                probe.target_reset_deassert()?;
1105            }
1106
1107            if let Some(target) = found_target {
1108                target
1109            } else {
1110                return Err(Error::ChipNotFound(RegistryError::ChipAutodetectFailed));
1111            }
1112        }
1113    };
1114
1115    Ok((probe, target))
1116}
1117
1118/// The `Permissions` struct represents what a [Session] is allowed to do with a target.
1119/// Some operations can be irreversible, so need to be explicitly allowed by the user.
1120///
1121/// # Example
1122///
1123/// ```
1124/// use probe_rs::Permissions;
1125///
1126/// let permissions = Permissions::new().allow_erase_all();
1127/// ```
1128#[non_exhaustive]
1129#[derive(Debug, Clone, Default)]
1130pub struct Permissions {
1131    /// When set to true, all memory of the chip may be erased or reset to factory default
1132    erase_all: bool,
1133}
1134
1135impl Permissions {
1136    /// Constructs a new permissions object with the default values
1137    pub fn new() -> Self {
1138        Self::default()
1139    }
1140
1141    /// Allow the session to erase all memory of the chip or reset it to factory default.
1142    ///
1143    /// # Warning
1144    /// This may irreversibly remove otherwise read-protected data from the device like security keys and 3rd party firmware.
1145    /// What happens exactly may differ per device and per probe-rs version.
1146    #[must_use]
1147    pub fn allow_erase_all(self) -> Self {
1148        Self {
1149            erase_all: true,
1150            ..self
1151        }
1152    }
1153
1154    pub(crate) fn erase_all(&self) -> Result<(), MissingPermissions> {
1155        if self.erase_all {
1156            Ok(())
1157        } else {
1158            Err(MissingPermissions("erase_all".into()))
1159        }
1160    }
1161}
1162
1163#[derive(Debug, Clone, thiserror::Error)]
1164#[error("An operation could not be performed because it lacked the permission to do so: {0}")]
1165pub struct MissingPermissions(pub String);