probe_rs/architecture/arm/core/
mod.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
//! The different ARM core implementations with all constants and custom handling.

use serde::{Deserialize, Serialize};

use crate::{
    core::{BreakpointCause, RegisterValue},
    memory_mapped_bitfield_register,
    semihosting::SemihostingCommand,
    CoreStatus, HaltReason,
};

use super::memory::ArmMemoryInterface;

pub mod armv6m;
pub mod armv7a;
pub mod armv7m;
pub mod armv8a;
pub mod armv8m;

pub(crate) mod armv7a_debug_regs;
pub(crate) mod armv8a_debug_regs;
pub(crate) mod cortex_m;
pub(crate) mod instructions;
pub(crate) mod registers;

/// Core information data which is downloaded from the target, represents its state and can be used for debugging.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Dump {
    /// The register values at the time of the dump.
    pub regs: [u32; 16],
    stack_addr: u32,
    stack: Vec<u8>,
}

impl Dump {
    /// Create a new dump from a SP and a stack dump with zeroed out registers.
    pub fn new(stack_addr: u32, stack: Vec<u8>) -> Dump {
        Dump {
            regs: [0u32; 16],
            stack_addr,
            stack,
        }
    }
}

memory_mapped_bitfield_register! {
    pub struct Dfsr(u32);
    0xE000_ED30, "DFSR",
    /// Indicates an asynchronous debug event generated because of **EDBGRQ** being asserted:
    /// * `false`: no **EDBGRQ** debug event.
    /// * `true`: **EDBGRQ** debug event.
    pub external, set_external: 4;
    /// Indicates whether a vector catch debug event was generated:
    /// * `false`: no vector catch debug event generated.
    /// * `true`: vector catch debug event generated.
    ///
    /// The corresponding FSR shows the primary cause of the exception.
    pub vcatch, set_vcatch: 3;
    /// Indicates a debug event generated by the DWT:
    /// * `false`: no debug events generated by the DWT.
    /// * `true`: at least one debug event generated by the DWT.
    pub dwttrap, set_dwttrap: 2;
    /// Indicates a debug event generated by BKPT instruction execution or a breakpoint match in the BPU:
    /// * `false`: no breakpoint debug event.
    /// * `true`: at least one breakpoint debug event.
    pub bkpt, set_bkpt: 1;
    /// Indicates a debug event generated by a C_HALT or C_STEP request, triggered by a write to the DHCSR:
    /// * `false`: no active halt request debug event.
    /// * `true`: halt request debug event active.
    pub halted, set_halted: 0;
}

impl Dfsr {
    fn clear_all() -> Self {
        Dfsr(0b11111)
    }

    /// This only returns the correct halt_reason for armv(x)-m variants. The armv(x)-a variants have their own implementation.
    // TODO: The different implementations between -m and -a can do with cleanup/refactoring.
    fn halt_reason(&self) -> HaltReason {
        if self.0 == 0 {
            // No bit is set
            HaltReason::Unknown
        } else if self.0.count_ones() > 1 {
            tracing::debug!("DFSR: {:?}", self);

            // We cannot identify why the chip halted,
            // it could be for multiple reasons.

            // For debuggers, it's important to know if
            // the core halted because of a breakpoint.
            // Because of this, we still return breakpoint
            // even if other reasons are possible as well.
            if self.bkpt() {
                HaltReason::Breakpoint(BreakpointCause::Unknown)
            } else {
                HaltReason::Multiple
            }
        } else if self.bkpt() {
            HaltReason::Breakpoint(BreakpointCause::Unknown)
        } else if self.external() {
            HaltReason::External
        } else if self.dwttrap() {
            HaltReason::Watchpoint
        } else if self.halted() {
            HaltReason::Request
        } else if self.vcatch() {
            HaltReason::Exception
        } else {
            // We check that exactly one bit is set, so we should hit one of the cases above.
            panic!("This should not happen. Please open a bug report.")
        }
    }
}

impl From<u32> for Dfsr {
    fn from(val: u32) -> Self {
        // Ensure that all unused bits are set to zero
        // This makes it possible to check the number of
        // set bits using count_ones().
        Dfsr(val & 0b11111)
    }
}

impl From<Dfsr> for u32 {
    fn from(register: Dfsr) -> Self {
        register.0
    }
}

/// The state cache of a Cortex-M core.
///
/// This state is used internally to not having to poll the core constantly.
#[derive(Debug)]
pub struct CortexMState {
    initialized: bool,

    hw_breakpoints_enabled: bool,

    current_state: CoreStatus,

    fp_present: bool,

    /// The semihosting command that was decoded at the current program counter
    semihosting_command: Option<SemihostingCommand>,
}

impl CortexMState {
    pub(crate) fn new() -> Self {
        Self {
            initialized: false,
            hw_breakpoints_enabled: false,
            current_state: CoreStatus::Unknown,
            fp_present: false,
            semihosting_command: None,
        }
    }

    fn initialize(&mut self) {
        self.initialized = true;
    }

    fn initialized(&self) -> bool {
        self.initialized
    }
}

/// The state cache of a Cortex-A core.
///
/// This state is used internally to not having to poll the core constantly.
#[derive(Debug)]
pub struct CortexAState {
    initialized: bool,

    current_state: CoreStatus,

    // Is the core currently in a 64-bit mode?
    is_64_bit: bool,

    register_cache: Vec<Option<(RegisterValue, bool)>>,

    // Number of floating point registers
    fp_reg_count: usize,
}

impl CortexAState {
    pub(crate) fn new() -> Self {
        Self {
            initialized: false,
            current_state: CoreStatus::Unknown,
            is_64_bit: false,
            register_cache: vec![],
            fp_reg_count: 0,
        }
    }

    fn initialize(&mut self) {
        self.initialized = true;
    }

    fn initialized(&self) -> bool {
        self.initialized
    }
}

/// Core implementations should call this function when they
/// wish to update the [`CoreStatus`] of their core.
///
/// It will reflect the core status to the probe/memory interface if
/// the status has changed, and will replace `current_status` with
/// `new_status`.
pub fn update_core_status<P: ArmMemoryInterface + ?Sized, T: core::ops::DerefMut<Target = P>>(
    probe: &mut T,
    current_status: &mut CoreStatus,
    new_status: CoreStatus,
) {
    if *current_status != new_status {
        probe.deref_mut().update_core_status(new_status);
    }
    *current_status = new_status;
}