probe_rs/
session.rs

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