Skip to main content

esp_hal/
debugger.rs

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