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