Skip to main content

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