probe_rs/probe/
fake_probe.rs

1#![expect(missing_docs)] // Don't require docs for test code
2use crate::{
3    MemoryInterface, MemoryMappedRegister,
4    architecture::arm::{
5        ArmDebugInterface, ArmError, DapAccess, FullyQualifiedApAddress, RawDapAccess,
6        RegisterAddress, SwoAccess,
7        ap::memory_ap::mock::MockMemoryAp,
8        armv8m::Dhcsr,
9        communication_interface::{DapProbe, SwdSequence},
10        dp::{DpAddress, DpRegisterAddress},
11        memory::{ADIMemoryInterface, ArmMemoryInterface},
12        sequences::ArmDebugSequence,
13    },
14    probe::{DebugProbe, DebugProbeError, Probe, WireProtocol},
15};
16use object::{
17    Endianness, Object, ObjectSection,
18    elf::{FileHeader32, FileHeader64, PT_LOAD},
19    read::elf::{ElfFile, FileHeader, ProgramHeader},
20};
21use probe_rs_target::MemoryRange;
22use std::{
23    cell::RefCell,
24    collections::{BTreeSet, VecDeque},
25    fmt::Debug,
26    path::Path,
27    sync::Arc,
28};
29
30/// This is a mock probe which can be used for mocking things in tests or for dry runs.
31#[expect(clippy::type_complexity)]
32pub struct FakeProbe {
33    protocol: WireProtocol,
34    speed: u32,
35
36    dap_register_read_handler: Option<Box<dyn Fn(RegisterAddress) -> Result<u32, ArmError> + Send>>,
37
38    dap_register_write_handler:
39        Option<Box<dyn Fn(RegisterAddress, u32) -> Result<(), ArmError> + Send>>,
40
41    operations: RefCell<VecDeque<Operation>>,
42
43    memory_ap: MockedAp,
44}
45
46enum MockedAp {
47    /// Mock a memory AP
48    MemoryAp(MockMemoryAp),
49    /// Mock an ARM core behind a memory AP
50    Core(MockCore),
51}
52
53struct LoadableSegment {
54    physical_address: u64,
55    offset: u64,
56    size: u64,
57}
58
59impl LoadableSegment {
60    fn contains(&self, physical_address: u64, len: u64) -> bool {
61        physical_address >= self.physical_address
62            && physical_address < (self.physical_address + self.size - len)
63    }
64
65    fn load_addr(&self, physical_address: u64) -> u64 {
66        let offset_in_segment = physical_address - self.physical_address;
67        self.offset + offset_in_segment
68    }
69}
70
71struct MockCore {
72    dhcsr: Dhcsr,
73
74    /// Is the core halted?
75    is_halted: bool,
76
77    program_binary: Option<Vec<u8>>,
78    loadable_segments: Vec<LoadableSegment>,
79    endianness: Endianness,
80}
81
82impl MockCore {
83    pub fn new() -> Self {
84        Self {
85            dhcsr: Dhcsr(0),
86            is_halted: false,
87            program_binary: None,
88            loadable_segments: Vec::new(),
89            endianness: Endianness::Little,
90        }
91    }
92}
93
94impl SwdSequence for &mut MockCore {
95    fn swj_sequence(&mut self, _bit_len: u8, _bits: u64) -> Result<(), DebugProbeError> {
96        todo!()
97    }
98
99    fn swj_pins(
100        &mut self,
101        _pin_out: u32,
102        _pin_select: u32,
103        _pin_wait: u32,
104    ) -> Result<u32, DebugProbeError> {
105        todo!()
106    }
107}
108
109impl MemoryInterface<ArmError> for &mut MockCore {
110    fn read_8(&mut self, address: u64, data: &mut [u8]) -> Result<(), ArmError> {
111        let mut curr_seg: Option<&LoadableSegment> = None;
112
113        for (offset, val) in data.iter_mut().enumerate() {
114            let address = address + offset as u64;
115            println!("Read {address:#010x} = 0");
116
117            match self.program_binary {
118                Some(ref program_binary) => {
119                    if !curr_seg.is_some_and(|seg| seg.contains(address, 1)) {
120                        curr_seg = self
121                            .loadable_segments
122                            .iter()
123                            .find(|&seg| seg.contains(address, 1));
124                    }
125                    match curr_seg {
126                        Some(seg) => {
127                            *val = program_binary[seg.load_addr(address) as usize];
128                        }
129                        None => *val = 0,
130                    }
131                }
132                None => *val = 0,
133            }
134        }
135
136        Ok(())
137    }
138
139    fn read_16(&mut self, _address: u64, _data: &mut [u16]) -> Result<(), ArmError> {
140        todo!()
141    }
142
143    fn read_32(&mut self, address: u64, data: &mut [u32]) -> Result<(), ArmError> {
144        let mut curr_seg: Option<&LoadableSegment> = None;
145
146        for (offset, val) in data.iter_mut().enumerate() {
147            const U32_BYTES: usize = 4;
148            let address = address + (offset * U32_BYTES) as u64;
149
150            match address {
151                // DHCSR
152                Dhcsr::ADDRESS_OFFSET => {
153                    let mut dhcsr: u32 = self.dhcsr.into();
154
155                    if self.is_halted {
156                        dhcsr |= 1 << 17;
157                    }
158
159                    // Always set S_REGRDY, and say that a register value can
160                    // be read.
161                    dhcsr |= 1 << 16;
162
163                    *val = dhcsr;
164                    println!("Read  DHCSR: {address:#x} = {val:#x}");
165                }
166
167                address => {
168                    println!("Read {address:#010x} = 0");
169
170                    match self.program_binary {
171                        Some(ref program_binary) => {
172                            if !curr_seg.is_some_and(|seg| seg.contains(address, U32_BYTES as u64))
173                            {
174                                curr_seg = self
175                                    .loadable_segments
176                                    .iter()
177                                    .find(|&seg| seg.contains(address, U32_BYTES as u64));
178                            }
179                            match curr_seg {
180                                Some(seg) => {
181                                    let from = seg.load_addr(address) as usize;
182                                    let to = from + U32_BYTES;
183
184                                    let u32_as_bytes: [u8; U32_BYTES] =
185                                        program_binary[from..to].try_into().unwrap();
186
187                                    // Convert to _host_ (native) endianness.
188                                    *val = if self.endianness == Endianness::Little {
189                                        u32::from_le_bytes(u32_as_bytes)
190                                    } else {
191                                        u32::from_be_bytes(u32_as_bytes)
192                                    };
193                                }
194                                None => *val = 0,
195                            }
196                        }
197                        None => *val = 0,
198                    }
199                }
200            }
201        }
202
203        Ok(())
204    }
205
206    fn read_64(&mut self, _address: u64, _data: &mut [u64]) -> Result<(), ArmError> {
207        todo!()
208    }
209
210    fn write_8(&mut self, _address: u64, _data: &[u8]) -> Result<(), ArmError> {
211        todo!()
212    }
213
214    fn write_16(&mut self, _address: u64, _data: &[u16]) -> Result<(), ArmError> {
215        todo!()
216    }
217
218    fn write_32(&mut self, address: u64, data: &[u32]) -> Result<(), ArmError> {
219        for (i, word) in data.iter().enumerate() {
220            let address = address + (i as u64 * 4);
221
222            match address {
223                // DHCSR
224                Dhcsr::ADDRESS_OFFSET => {
225                    let dbg_key = (*word >> 16) & 0xffff;
226
227                    if dbg_key == 0xa05f {
228                        // Mask out dbg key
229                        self.dhcsr = Dhcsr::from(*word & 0xffff);
230                        println!("Write DHCSR = {word:#010x}");
231
232                        let request_halt = self.dhcsr.c_halt();
233
234                        self.is_halted = request_halt;
235
236                        if !self.dhcsr.c_halt() && self.dhcsr.c_debugen() && self.dhcsr.c_step() {
237                            tracing::debug!("MockCore: Single step requested, setting s_halt");
238                            self.is_halted = true;
239                        }
240                    }
241                }
242                _ => println!("Write {address:#010x} = {word:#010x}"),
243            }
244        }
245
246        Ok(())
247    }
248
249    fn write_64(&mut self, _address: u64, _data: &[u64]) -> Result<(), ArmError> {
250        todo!()
251    }
252
253    fn flush(&mut self) -> Result<(), ArmError> {
254        Ok(())
255    }
256
257    fn supports_native_64bit_access(&mut self) -> bool {
258        true
259    }
260
261    fn supports_8bit_transfers(&self) -> Result<bool, ArmError> {
262        todo!()
263    }
264}
265
266impl ArmMemoryInterface for &mut MockCore {
267    fn base_address(&mut self) -> Result<u64, ArmError> {
268        todo!()
269    }
270
271    fn fully_qualified_address(&self) -> FullyQualifiedApAddress {
272        todo!()
273    }
274
275    fn get_arm_debug_interface(&mut self) -> Result<&mut dyn ArmDebugInterface, DebugProbeError> {
276        todo!()
277    }
278
279    fn generic_status(&mut self) -> Result<crate::architecture::arm::ap::CSW, ArmError> {
280        todo!()
281    }
282
283    fn update_core_status(&mut self, _state: crate::CoreStatus) {}
284}
285
286#[derive(Debug, Clone, PartialEq)]
287pub enum Operation {
288    ReadRawApRegister {
289        ap: FullyQualifiedApAddress,
290        address: u64,
291        result: u32,
292    },
293}
294
295impl Debug for FakeProbe {
296    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
297        f.debug_struct("FakeProbe")
298            .field("protocol", &self.protocol)
299            .field("speed", &self.speed)
300            .finish_non_exhaustive()
301    }
302}
303
304impl FakeProbe {
305    /// Creates a new [`FakeProbe`] for mocking.
306    pub fn new() -> Self {
307        FakeProbe {
308            protocol: WireProtocol::Swd,
309            speed: 1000,
310
311            dap_register_read_handler: None,
312            dap_register_write_handler: None,
313
314            operations: RefCell::new(VecDeque::new()),
315
316            memory_ap: MockedAp::MemoryAp(MockMemoryAp::with_pattern()),
317        }
318    }
319
320    /// Fake probe with a mocked core
321    pub fn with_mocked_core() -> Self {
322        FakeProbe {
323            memory_ap: MockedAp::Core(MockCore::new()),
324            ..Self::default()
325        }
326    }
327
328    /// Fake probe with a mocked core
329    /// with access to an actual binary file.
330    pub fn with_mocked_core_and_binary(program_binary: &Path) -> Self {
331        let file_data = std::fs::read(program_binary).unwrap();
332        let file_data_slice = file_data.as_slice();
333
334        let file_kind = object::FileKind::parse(file_data.as_slice()).unwrap();
335        let core = match file_kind {
336            object::FileKind::Elf32 => core_with_binary(
337                object::read::elf::ElfFile::<FileHeader32<Endianness>>::parse(file_data_slice)
338                    .unwrap(),
339            ),
340            object::FileKind::Elf64 => core_with_binary(
341                object::read::elf::ElfFile::<FileHeader64<Endianness>>::parse(file_data_slice)
342                    .unwrap(),
343            ),
344            _ => {
345                unimplemented!("unsupported file format")
346            }
347        };
348
349        FakeProbe {
350            memory_ap: MockedAp::Core(core),
351            ..Self::default()
352        }
353    }
354
355    /// This sets the read handler for DAP register reads.
356    /// Can be used to hook into the read.
357    pub fn set_dap_register_read_handler(
358        &mut self,
359        handler: Box<dyn Fn(RegisterAddress) -> Result<u32, ArmError> + Send>,
360    ) {
361        self.dap_register_read_handler = Some(handler);
362    }
363
364    /// This sets the write handler for DAP register writes.
365    /// Can be used to hook into the write.
366    pub fn set_dap_register_write_handler(
367        &mut self,
368        handler: Box<dyn Fn(RegisterAddress, u32) -> Result<(), ArmError> + Send>,
369    ) {
370        self.dap_register_write_handler = Some(handler);
371    }
372
373    /// Makes a generic probe out of the [`FakeProbe`]
374    pub fn into_probe(self) -> Probe {
375        Probe::from_specific_probe(Box::new(self))
376    }
377
378    fn next_operation(&self) -> Option<Operation> {
379        self.operations.borrow_mut().pop_front()
380    }
381
382    fn read_raw_ap_register(
383        &mut self,
384        expected_ap: &FullyQualifiedApAddress,
385        expected_address: u64,
386    ) -> Result<u32, ArmError> {
387        let operation = self.next_operation();
388
389        match operation {
390            Some(Operation::ReadRawApRegister {
391                ap,
392                address,
393                result,
394            }) => {
395                assert_eq!(&ap, expected_ap);
396                assert_eq!(address, expected_address);
397
398                Ok(result)
399            }
400            None => panic!(
401                "No more operations expected, but got read_raw_ap_register ap={expected_ap:?}, address:{expected_address}"
402            ),
403            //other => panic!("Unexpected operation: {:?}", other),
404        }
405    }
406
407    pub fn expect_operation(&self, operation: Operation) {
408        self.operations.borrow_mut().push_back(operation);
409    }
410}
411
412fn core_with_binary<T: FileHeader>(elf_file: ElfFile<T>) -> MockCore {
413    let elf_header = elf_file.elf_header();
414    let elf_data = elf_file.data();
415    let endian = elf_header.endian().unwrap();
416
417    let mut loadable_sections = Vec::new();
418    for segment in elf_header.program_headers(endian, elf_data).unwrap() {
419        let physical_address = segment.p_paddr(endian).into();
420        let segment_data = segment.data(endian, elf_data).unwrap();
421
422        if !segment_data.is_empty() && segment.p_type(endian) == PT_LOAD {
423            let (segment_offset, segment_filesize) = segment.file_range(endian);
424            let segment_range = segment_offset..segment_offset + segment_filesize;
425
426            let mut found_section_in_segment = false;
427
428            for section in elf_file.sections() {
429                let (section_offset, section_filesize) = match section.file_range() {
430                    Some(range) => range,
431                    None => continue,
432                };
433
434                if segment_range
435                    .contains_range(&(section_offset..section_offset + section_filesize))
436                {
437                    found_section_in_segment = true;
438                    break;
439                }
440            }
441
442            if found_section_in_segment {
443                loadable_sections.push(LoadableSegment {
444                    physical_address,
445                    offset: segment_offset,
446                    size: segment_filesize,
447                });
448            }
449        }
450    }
451
452    let mut core = MockCore::new();
453    core.program_binary = Some(elf_data.to_owned());
454    core.loadable_segments = loadable_sections;
455    core.endianness = if elf_header.is_little_endian() {
456        Endianness::Little
457    } else {
458        Endianness::Big
459    };
460
461    core
462}
463
464impl Default for FakeProbe {
465    fn default() -> Self {
466        FakeProbe::new()
467    }
468}
469
470impl DebugProbe for FakeProbe {
471    /// Get human readable name for the probe
472    fn get_name(&self) -> &str {
473        "Mock probe for testing"
474    }
475
476    fn speed_khz(&self) -> u32 {
477        self.speed
478    }
479
480    fn set_speed(&mut self, speed_khz: u32) -> Result<u32, DebugProbeError> {
481        self.speed = speed_khz;
482
483        Ok(speed_khz)
484    }
485
486    fn attach(&mut self) -> Result<(), DebugProbeError> {
487        Ok(())
488    }
489
490    fn select_protocol(&mut self, protocol: WireProtocol) -> Result<(), DebugProbeError> {
491        self.protocol = protocol;
492
493        Ok(())
494    }
495
496    fn active_protocol(&self) -> Option<WireProtocol> {
497        Some(self.protocol)
498    }
499
500    /// Leave debug mode
501    fn detach(&mut self) -> Result<(), crate::Error> {
502        Ok(())
503    }
504
505    /// Resets the target device.
506    fn target_reset(&mut self) -> Result<(), DebugProbeError> {
507        Err(DebugProbeError::CommandNotSupportedByProbe {
508            command_name: "target_reset",
509        })
510    }
511
512    fn target_reset_assert(&mut self) -> Result<(), DebugProbeError> {
513        unimplemented!()
514    }
515
516    fn target_reset_deassert(&mut self) -> Result<(), DebugProbeError> {
517        Ok(())
518    }
519
520    fn into_probe(self: Box<Self>) -> Box<dyn DebugProbe> {
521        self
522    }
523
524    fn try_get_arm_debug_interface<'probe>(
525        self: Box<Self>,
526        sequence: Arc<dyn ArmDebugSequence>,
527    ) -> Result<Box<dyn ArmDebugInterface + 'probe>, (Box<dyn DebugProbe>, ArmError)> {
528        Ok(Box::new(FakeArmInterface::new(self, sequence)))
529    }
530
531    fn has_arm_interface(&self) -> bool {
532        true
533    }
534}
535
536impl RawDapAccess for FakeProbe {
537    /// Reads the DAP register on the specified port and address
538    fn raw_read_register(&mut self, address: RegisterAddress) -> Result<u32, ArmError> {
539        let handler = self.dap_register_read_handler.as_ref().unwrap();
540
541        handler(address)
542    }
543
544    /// Writes a value to the DAP register on the specified port and address
545    fn raw_write_register(&mut self, address: RegisterAddress, value: u32) -> Result<(), ArmError> {
546        let handler = self.dap_register_write_handler.as_ref().unwrap();
547
548        handler(address, value)
549    }
550
551    fn jtag_sequence(&mut self, _cycles: u8, _tms: bool, _tdi: u64) -> Result<(), DebugProbeError> {
552        todo!()
553    }
554
555    fn swj_sequence(&mut self, _bit_len: u8, _bits: u64) -> Result<(), DebugProbeError> {
556        todo!()
557    }
558
559    fn swj_pins(
560        &mut self,
561        _pin_out: u32,
562        _pin_select: u32,
563        _pin_wait: u32,
564    ) -> Result<u32, DebugProbeError> {
565        todo!()
566    }
567
568    fn into_probe(self: Box<Self>) -> Box<dyn DebugProbe> {
569        self
570    }
571
572    fn core_status_notification(&mut self, _: crate::CoreStatus) -> Result<(), DebugProbeError> {
573        Ok(())
574    }
575}
576
577#[derive(Debug)]
578struct FakeArmInterface {
579    probe: Box<FakeProbe>,
580    current_dp: Option<DpAddress>,
581}
582
583impl FakeArmInterface {
584    pub(crate) fn new(probe: Box<FakeProbe>, _sequence: Arc<dyn ArmDebugSequence>) -> Self {
585        FakeArmInterface {
586            probe,
587            current_dp: None,
588        }
589    }
590}
591
592impl SwdSequence for FakeArmInterface {
593    fn swj_sequence(&mut self, bit_len: u8, bits: u64) -> Result<(), DebugProbeError> {
594        self.probe.swj_sequence(bit_len, bits)?;
595
596        Ok(())
597    }
598
599    fn swj_pins(
600        &mut self,
601        pin_out: u32,
602        pin_select: u32,
603        pin_wait: u32,
604    ) -> Result<u32, DebugProbeError> {
605        let value = self.probe.swj_pins(pin_out, pin_select, pin_wait)?;
606
607        Ok(value)
608    }
609}
610
611impl ArmDebugInterface for FakeArmInterface {
612    fn memory_interface(
613        &mut self,
614        access_port_address: &FullyQualifiedApAddress,
615    ) -> Result<Box<dyn ArmMemoryInterface + '_>, ArmError> {
616        match self.probe.memory_ap {
617            MockedAp::MemoryAp(ref mut _memory_ap) => {
618                let memory = ADIMemoryInterface::new(self, access_port_address)?;
619
620                Ok(Box::new(memory) as _)
621            }
622            MockedAp::Core(ref mut core) => Ok(Box::new(core) as _),
623        }
624    }
625
626    fn access_ports(
627        &mut self,
628        dp: DpAddress,
629    ) -> Result<BTreeSet<FullyQualifiedApAddress>, ArmError> {
630        Ok(BTreeSet::from([FullyQualifiedApAddress::v1_with_dp(dp, 1)]))
631    }
632
633    fn close(self: Box<Self>) -> Probe {
634        Probe::from_attached_probe(self.probe)
635    }
636
637    fn current_debug_port(&self) -> Option<DpAddress> {
638        self.current_dp
639    }
640
641    fn select_debug_port(&mut self, dp: DpAddress) -> Result<(), ArmError> {
642        self.current_dp = Some(dp);
643        Ok(())
644    }
645
646    fn reinitialize(&mut self) -> Result<(), ArmError> {
647        Ok(())
648    }
649}
650
651impl SwoAccess for FakeArmInterface {
652    fn enable_swo(
653        &mut self,
654        _config: &crate::architecture::arm::SwoConfig,
655    ) -> Result<(), ArmError> {
656        unimplemented!()
657    }
658
659    fn disable_swo(&mut self) -> Result<(), ArmError> {
660        unimplemented!()
661    }
662
663    fn read_swo_timeout(&mut self, _timeout: std::time::Duration) -> Result<Vec<u8>, ArmError> {
664        unimplemented!()
665    }
666}
667
668impl DapAccess for FakeArmInterface {
669    fn read_raw_dp_register(
670        &mut self,
671        _dp: DpAddress,
672        _address: DpRegisterAddress,
673    ) -> Result<u32, ArmError> {
674        todo!()
675    }
676
677    fn write_raw_dp_register(
678        &mut self,
679        _dp: DpAddress,
680        _address: DpRegisterAddress,
681        _value: u32,
682    ) -> Result<(), ArmError> {
683        todo!()
684    }
685
686    fn read_raw_ap_register(
687        &mut self,
688        _ap: &FullyQualifiedApAddress,
689        _address: u64,
690    ) -> Result<u32, ArmError> {
691        self.probe.read_raw_ap_register(_ap, _address)
692    }
693
694    fn read_raw_ap_register_repeated(
695        &mut self,
696        _ap: &FullyQualifiedApAddress,
697        _address: u64,
698        _values: &mut [u32],
699    ) -> Result<(), ArmError> {
700        todo!()
701    }
702
703    fn write_raw_ap_register(
704        &mut self,
705        _ap: &FullyQualifiedApAddress,
706        _address: u64,
707        _value: u32,
708    ) -> Result<(), ArmError> {
709        todo!()
710    }
711
712    fn write_raw_ap_register_repeated(
713        &mut self,
714        _ap: &FullyQualifiedApAddress,
715        _address: u64,
716        _values: &[u32],
717    ) -> Result<(), ArmError> {
718        todo!()
719    }
720
721    fn try_dap_probe(&self) -> Option<&dyn DapProbe> {
722        None
723    }
724
725    fn try_dap_probe_mut(&mut self) -> Option<&mut dyn DapProbe> {
726        None
727    }
728}
729
730#[cfg(all(test, feature = "builtin-targets"))]
731mod test {
732    use super::FakeProbe;
733    use crate::Permissions;
734
735    #[test]
736    fn create_session_with_fake_probe() {
737        let fake_probe = FakeProbe::with_mocked_core();
738
739        let probe = fake_probe.into_probe();
740
741        probe
742            .attach("nrf51822_xxAC", Permissions::default())
743            .unwrap();
744    }
745}