1pub 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
26pub 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); 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    #[derive(Clone, Copy, Default)]
72    pub(crate) struct Tdata1(u32);
73
74    pub bool, load, set_load: 0;
77
78    pub bool, store, set_store: 1;
81
82    pub bool, execute, set_execute: 2;
85
86    pub bool, u, set_u: 3;
88
89    pub bool, m, set_m: 6;
91
92    pub u8, _match, set_match: 10, 7;
100
101    pub u8, action, set_action: 15, 12;
107
108    pub bool, hit, set_hit: 20;
110
111    pub bool, dmode, set_dmode: 27;
116}
117
118#[cfg(riscv)]
119bitfield::bitfield! {
120    #[derive(Clone, Copy, Default)]
122    pub(crate) struct Tcontrol(u32);
123
124    pub bool, mte, set_mte: 3;
126
127    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#[cfg(riscv)]
140pub(crate) unsafe fn clear_watchpoint(id: u8) -> WatchPoint {
141    assert!(id < 4);
142
143    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#[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#[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#[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}