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}