esp_hal/
debugger.rs

1//! Debugger utilities
2
3/// Checks if a debugger is connected.
4pub fn debugger_connected() -> bool {
5    #[cfg(xtensa)]
6    {
7        xtensa_lx::is_debugger_attached()
8    }
9
10    #[cfg(riscv)]
11    {
12        crate::peripherals::ASSIST_DEBUG::regs()
13            .cpu(0)
14            .debug_mode()
15            .read()
16            .debug_module_active()
17            .bit_is_set()
18    }
19
20    #[cfg(not(any(xtensa, riscv)))]
21    {
22        false
23    }
24}
25
26/// Set a word-sized data breakpoint at the given address.
27/// No breakpoint will be set when a debugger is currently attached if
28/// the `stack_guard_monitoring_with_debugger_connected` option is false.
29///
30/// Breakpoint 0 is used.
31///
32/// # Safety
33/// The address must be word aligned.
34pub unsafe fn set_stack_watchpoint(addr: usize) {
35    assert!(addr.is_multiple_of(4));
36
37    if cfg!(stack_guard_monitoring_with_debugger_connected)
38        || !crate::debugger::debugger_connected()
39    {
40        cfg_if::cfg_if! {
41            if #[cfg(xtensa)] {
42                let addr = addr & !0b11;
43                let dbreakc = 0b1111100 | (1 << 31); // bit 31 = STORE
44
45                unsafe {
46                    core::arch::asm!(
47                        "
48                        wsr {addr}, 144 // 144 = dbreaka0
49                        wsr {dbreakc}, 160 // 160 = dbreakc0
50                        ",
51                        addr = in(reg) addr,
52                        dbreakc = in(reg) dbreakc,
53                    );
54                }
55            } else {
56                unsafe { set_watchpoint(0, addr, 4); }
57            }
58        }
59    }
60}
61
62#[cfg(riscv)]
63pub(crate) static DEBUGGER_LOCK: esp_sync::RawMutex = esp_sync::RawMutex::new();
64
65#[cfg(riscv)]
66const NAPOT_MATCH: u8 = 1;
67
68#[cfg(riscv)]
69bitfield::bitfield! {
70    /// Only match type (0x2) triggers are supported.
71    #[derive(Clone, Copy, Default)]
72    pub(crate) struct Tdata1(u32);
73
74    /// Set this for configuring the selected trigger to fire right before a load operation with matching
75    /// data address is executed by the CPU.
76    pub bool, load, set_load: 0;
77
78    /// Set this for configuring the selected trigger to fire right before a store operation with matching
79    /// data address is executed by the CPU.
80    pub bool, store, set_store: 1;
81
82    /// Set this for configuring the selected trigger to fire right before an instruction with matching
83    /// virtual address is executed by the CPU.
84    pub bool, execute, set_execute: 2;
85
86    /// Set this for enabling selected trigger to operate in user mode.
87    pub bool, u, set_u: 3;
88
89    /// Set this for enabling selected trigger to operate in machine mode.
90    pub bool, m, set_m: 6;
91
92    /// Configures the selected trigger to perform one of the available matching operations on a
93    /// data/instruction address. Valid options are:
94    /// 0x0: exact byte match, i.e. address corresponding to one of the bytes in an access must match
95    /// the value of maddress exactly.
96    /// 0x1: NAPOT match, i.e. at least one of the bytes of an access must lie in the NAPOT region
97    /// specified in maddress.
98    /// Note: Writing a larger value will clip it to the largest possible value 0x1.
99    pub u8, _match, set_match: 10, 7;
100
101    /// Configures the selected trigger to perform one of the available actions when firing. Valid
102    /// options are:
103    /// 0x0: cause breakpoint exception.
104    /// 0x1: enter debug mode (only valid when dmode = 1)
105    /// Note: Writing an invalid value will set this to the default value 0x0.
106    pub u8, action, set_action: 15, 12;
107
108    /// This is found to be 1 if the selected trigger had fired previously. This bit is to be cleared manually.
109    pub bool, hit, set_hit: 20;
110
111    /// 0: Both Debug and M mode can write the tdata1 and tdata2 registers at the selected tselect.
112    /// 1: Only Debug Mode can write the tdata1 and tdata2 registers at the selected tselect. Writes from
113    /// other modes are ignored.
114    /// Note: Only writable from debug mode.
115    pub bool, dmode, set_dmode: 27;
116}
117
118#[cfg(riscv)]
119bitfield::bitfield! {
120    /// Only match type (0x2) triggers are supported.
121    #[derive(Clone, Copy, Default)]
122    pub(crate) struct Tcontrol(u32);
123
124    /// Current M mode trigger enable bit
125    pub bool, mte, set_mte: 3;
126
127    /// Previous M mode trigger enable bit
128    pub bool, mpte, set_mpte: 7;
129
130}
131
132#[cfg(riscv)]
133pub(crate) struct WatchPoint {
134    tdata1: u32,
135    tdata2: u32,
136}
137
138/// Clear the watchpoint
139#[cfg(riscv)]
140pub(crate) unsafe fn clear_watchpoint(id: u8) -> WatchPoint {
141    assert!(id < 4);
142
143    // tdata1 is a WARL(write any read legal) register. We can just write 0 to it.
144    let mut tdata1 = 0;
145    let mut tdata2 = 0;
146
147    DEBUGGER_LOCK.lock(|| unsafe {
148        core::arch::asm!(
149            "
150            csrw 0x7a0, {id} // tselect
151            csrrw {tdata1}, 0x7a1, {tdata2} // tdata1
152            csrr {tdata2}, 0x7a2 // tdata2
153            ", id = in(reg) id,
154            tdata1 = inout(reg) tdata1,
155            tdata2 = out(reg) tdata2,
156        );
157    });
158
159    WatchPoint { tdata1, tdata2 }
160}
161
162/// Clear the watchpoint
163#[cfg(riscv)]
164pub(crate) unsafe fn restore_watchpoint(id: u8, watchpoint: WatchPoint) {
165    DEBUGGER_LOCK.lock(|| unsafe {
166        core::arch::asm!(
167            "
168            csrw 0x7a0, {id} // tselect
169            csrw 0x7a1, {tdata1} // tdata1
170            csrw 0x7a2, {tdata2} // tdata2
171            ", id = in(reg) id,
172            tdata1 = in(reg) watchpoint.tdata1,
173            tdata2 = in(reg) watchpoint.tdata2,
174        );
175    });
176}
177
178/// Clear the watchpoint
179#[cfg(all(riscv, feature = "exception-handler"))]
180pub(crate) unsafe fn watchpoint_hit(id: u8) -> bool {
181    assert!(id < 4);
182    let mut tdata = Tdata1::default();
183
184    DEBUGGER_LOCK.lock(|| unsafe {
185        core::arch::asm!(
186            "
187            csrw 0x7a0, {id} // tselect
188            csrr {tdata}, 0x7a1 // tdata1
189            ", id = in(reg) id,
190            tdata = out(reg) tdata.0,
191        );
192    });
193
194    tdata.hit()
195}
196
197/// Set watchpoint and enable triggers.
198#[cfg(riscv)]
199pub(crate) unsafe fn set_watchpoint(id: u8, addr: usize, len: usize) {
200    assert!(id < 4);
201    assert!(len.is_power_of_two());
202    assert!(addr.is_multiple_of(len));
203
204    let z = len.trailing_zeros();
205    let mask = {
206        let mut mask: usize = 0;
207        for i in 0..z {
208            mask |= 1 << i;
209        }
210        mask
211    };
212
213    let napot_encoding = { mask & !(1 << (z - 1)) };
214    let addr = (addr & !mask) | napot_encoding;
215
216    let mut tdata = Tdata1::default();
217    tdata.set_m(true);
218    tdata.set_store(true);
219    tdata.set_match(NAPOT_MATCH);
220    let tdata: u32 = tdata.0;
221
222    let mut tcontrol = Tcontrol::default();
223    tcontrol.set_mte(true);
224    let tcontrol: u32 = tcontrol.0;
225
226    DEBUGGER_LOCK.lock(|| unsafe {
227        core::arch::asm!(
228            "
229            csrw 0x7a0, {id} // tselect
230            csrw 0x7a5, {tcontrol} // tcontrol
231            csrw 0x7a1, {tdata} // tdata1
232            csrw 0x7a2, {addr} // tdata2
233            ", id = in(reg) id,
234            addr = in(reg) addr,
235            tdata = in(reg) tdata,
236            tcontrol = in(reg) tcontrol,
237        );
238    });
239}