1pub 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
17pub 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); 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 #[derive(Clone, Copy, Default)]
64 pub(crate) struct Tdata1(u32);
65
66 pub bool, load, set_load: 0;
69
70 pub bool, store, set_store: 1;
73
74 pub bool, execute, set_execute: 2;
77
78 pub bool, u, set_u: 3;
80
81 pub bool, m, set_m: 6;
83
84 pub u8, _match, set_match: 10, 7;
92
93 pub u8, action, set_action: 15, 12;
99
100 pub bool, hit, set_hit: 20;
102
103 pub bool, dmode, set_dmode: 27;
108}
109
110#[cfg(riscv)]
111bitfield::bitfield! {
112 #[derive(Clone, Copy, Default)]
114 pub(crate) struct Tcontrol(u32);
115
116 pub bool, mte, set_mte: 3;
118
119 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#[cfg(riscv)]
132pub(crate) unsafe fn clear_watchpoint(id: u8) -> WatchPoint {
133 assert!(id < 4);
134
135 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#[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#[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#[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}