1#[cfg(feature = "alloc")]
8use alloc::vec::Vec;
9
10const MAX_SW_BREAKPOINTS: usize = 256;
12
13const MAX_HW_WATCHPOINTS: usize = 4;
15
16#[derive(Debug, Clone, Copy, Default)]
18struct SwBreakpoint {
19 addr: u64,
20 original_byte: u8,
21 active: bool,
22}
23
24#[derive(Debug, Clone, Copy, PartialEq, Eq)]
26pub(crate) enum WatchpointType {
27 Write,
28 Read,
29 Access,
30}
31
32#[derive(Debug, Clone, Copy)]
34struct HwWatchpoint {
35 addr: u64,
36 len: u8,
37 wp_type: WatchpointType,
38 active: bool,
39}
40
41impl Default for HwWatchpoint {
42 fn default() -> Self {
43 Self {
44 addr: 0,
45 len: 1,
46 wp_type: WatchpointType::Write,
47 active: false,
48 }
49 }
50}
51
52struct BreakpointManager {
54 sw_breakpoints: [SwBreakpoint; MAX_SW_BREAKPOINTS],
55 sw_count: usize,
56 hw_watchpoints: [HwWatchpoint; MAX_HW_WATCHPOINTS],
57}
58
59impl BreakpointManager {
60 const fn new() -> Self {
61 Self {
62 sw_breakpoints: [SwBreakpoint {
63 addr: 0,
64 original_byte: 0,
65 active: false,
66 }; MAX_SW_BREAKPOINTS],
67 sw_count: 0,
68 hw_watchpoints: [HwWatchpoint {
69 addr: 0,
70 len: 1,
71 wp_type: WatchpointType::Write,
72 active: false,
73 }; MAX_HW_WATCHPOINTS],
74 }
75 }
76
77 fn insert_sw_breakpoint(&mut self, addr: u64) -> bool {
78 for bp in &self.sw_breakpoints[..self.sw_count] {
80 if bp.addr == addr && bp.active {
81 return true; }
83 }
84
85 if self.sw_count >= MAX_SW_BREAKPOINTS {
86 return false;
87 }
88
89 let ptr = addr as *mut u8;
91 let original = unsafe { core::ptr::read_volatile(ptr) };
95 unsafe {
96 core::ptr::write_volatile(ptr, 0xCC);
97 }
98
99 self.sw_breakpoints[self.sw_count] = SwBreakpoint {
100 addr,
101 original_byte: original,
102 active: true,
103 };
104 self.sw_count += 1;
105 true
106 }
107
108 fn remove_sw_breakpoint(&mut self, addr: u64) -> bool {
109 for bp in &mut self.sw_breakpoints[..self.sw_count] {
110 if bp.addr == addr && bp.active {
111 let ptr = addr as *mut u8;
113 unsafe {
116 core::ptr::write_volatile(ptr, bp.original_byte);
117 }
118 bp.active = false;
119 return true;
120 }
121 }
122 false
123 }
124
125 fn insert_hw_watchpoint(&mut self, addr: u64, len: u8, wp_type: WatchpointType) -> bool {
126 for (idx, wp) in self.hw_watchpoints.iter_mut().enumerate() {
128 if !wp.active {
129 wp.addr = addr;
130 wp.len = len;
131 wp.wp_type = wp_type;
132 wp.active = true;
133
134 #[cfg(all(target_arch = "x86_64", target_os = "none"))]
135 set_debug_register(idx, addr, len, wp_type);
136 #[cfg(not(all(target_arch = "x86_64", target_os = "none")))]
137 let _ = idx;
138
139 return true;
140 }
141 }
142 false
143 }
144
145 fn remove_hw_watchpoint(&mut self, addr: u64, wp_type: WatchpointType) -> bool {
146 for (idx, wp) in self.hw_watchpoints.iter_mut().enumerate() {
147 if wp.active && wp.addr == addr && wp.wp_type == wp_type {
148 wp.active = false;
149
150 #[cfg(all(target_arch = "x86_64", target_os = "none"))]
151 clear_debug_register(idx);
152 #[cfg(not(all(target_arch = "x86_64", target_os = "none")))]
153 let _ = idx;
154
155 return true;
156 }
157 }
158 false
159 }
160}
161
162static BP_MANAGER: spin::Mutex<BreakpointManager> = spin::Mutex::new(BreakpointManager::new());
163
164#[cfg(all(target_arch = "x86_64", target_os = "none"))]
169fn set_debug_register(idx: usize, addr: u64, len: u8, wp_type: WatchpointType) {
170 unsafe {
173 match idx {
175 0 => core::arch::asm!("mov dr0, {}", in(reg) addr, options(nostack)),
176 1 => core::arch::asm!("mov dr1, {}", in(reg) addr, options(nostack)),
177 2 => core::arch::asm!("mov dr2, {}", in(reg) addr, options(nostack)),
178 3 => core::arch::asm!("mov dr3, {}", in(reg) addr, options(nostack)),
179 _ => return,
180 }
181
182 let mut dr7: u64;
184 core::arch::asm!("mov {}, dr7", out(reg) dr7, options(nostack));
185
186 dr7 |= 1 << (idx * 2);
188
189 let condition = match wp_type {
191 WatchpointType::Write => 0b01, WatchpointType::Read => 0b11, WatchpointType::Access => 0b11, };
195 let cond_shift = 16 + (idx * 4);
196 dr7 &= !(0b11 << cond_shift);
197 dr7 |= condition << cond_shift;
198
199 let len_bits = match len {
201 1 => 0b00,
202 2 => 0b01,
203 4 => 0b11,
204 8 => 0b10,
205 _ => 0b00,
206 };
207 let len_shift = 18 + (idx * 4);
208 dr7 &= !(0b11 << len_shift);
209 dr7 |= len_bits << len_shift;
210
211 core::arch::asm!("mov dr7, {}", in(reg) dr7, options(nostack));
212 }
213}
214
215#[cfg(all(target_arch = "x86_64", target_os = "none"))]
216fn clear_debug_register(idx: usize) {
217 unsafe {
219 let mut dr7: u64;
220 core::arch::asm!("mov {}, dr7", out(reg) dr7, options(nostack));
221
222 dr7 &= !(1 << (idx * 2));
224
225 let cond_shift = 16 + (idx * 4);
227 dr7 &= !(0b1111 << cond_shift);
228
229 core::arch::asm!("mov dr7, {}", in(reg) dr7, options(nostack));
230 }
231}
232
233fn parse_z_command(data: &[u8]) -> Option<(u8, u64, u64)> {
238 if data.is_empty() {
240 return None;
241 }
242
243 let parts: Vec<&[u8]> = data.split(|&b| b == b',').collect();
244 if parts.len() < 3 {
245 return None;
246 }
247
248 let bp_type = super::gdb_stub::hex_digit(parts[0][0])?;
249 let addr = super::gdb_stub::parse_hex_u64(parts[1])?;
250 let kind = super::gdb_stub::parse_hex_u64(parts[2])?;
251
252 Some((bp_type, addr, kind))
253}
254
255pub(crate) fn handle_insert(data: &[u8]) -> Option<Vec<u8>> {
257 let (bp_type, addr, kind) = parse_z_command(data)?;
258 let mut mgr = BP_MANAGER.lock();
259
260 match bp_type {
261 0 => {
263 if mgr.insert_sw_breakpoint(addr) {
264 Some(b"OK".to_vec())
265 } else {
266 Some(b"E01".to_vec())
267 }
268 }
269 1 => {
271 if mgr.insert_sw_breakpoint(addr) {
272 Some(b"OK".to_vec())
273 } else {
274 Some(b"E01".to_vec())
275 }
276 }
277 2 => {
279 if mgr.insert_hw_watchpoint(addr, kind as u8, WatchpointType::Write) {
280 Some(b"OK".to_vec())
281 } else {
282 Some(b"E01".to_vec())
283 }
284 }
285 3 => {
287 if mgr.insert_hw_watchpoint(addr, kind as u8, WatchpointType::Read) {
288 Some(b"OK".to_vec())
289 } else {
290 Some(b"E01".to_vec())
291 }
292 }
293 4 => {
295 if mgr.insert_hw_watchpoint(addr, kind as u8, WatchpointType::Access) {
296 Some(b"OK".to_vec())
297 } else {
298 Some(b"E01".to_vec())
299 }
300 }
301 _ => None,
302 }
303}
304
305pub(crate) fn handle_remove(data: &[u8]) -> Option<Vec<u8>> {
307 let (bp_type, addr, _kind) = parse_z_command(data)?;
308 let mut mgr = BP_MANAGER.lock();
309
310 match bp_type {
311 0 | 1 => {
312 if mgr.remove_sw_breakpoint(addr) {
313 Some(b"OK".to_vec())
314 } else {
315 Some(b"E01".to_vec())
316 }
317 }
318 2 => {
319 if mgr.remove_hw_watchpoint(addr, WatchpointType::Write) {
320 Some(b"OK".to_vec())
321 } else {
322 Some(b"E01".to_vec())
323 }
324 }
325 3 => {
326 if mgr.remove_hw_watchpoint(addr, WatchpointType::Read) {
327 Some(b"OK".to_vec())
328 } else {
329 Some(b"E01".to_vec())
330 }
331 }
332 4 => {
333 if mgr.remove_hw_watchpoint(addr, WatchpointType::Access) {
334 Some(b"OK".to_vec())
335 } else {
336 Some(b"E01".to_vec())
337 }
338 }
339 _ => None,
340 }
341}
342
343#[cfg(test)]
348mod tests {
349 use super::*;
350
351 #[test]
352 fn test_breakpoint_manager_creation() {
353 let mgr = BreakpointManager::new();
354 assert_eq!(mgr.sw_count, 0);
355 for wp in &mgr.hw_watchpoints {
356 assert!(!wp.active);
357 }
358 }
359
360 #[test]
361 fn test_watchpoint_type_eq() {
362 assert_eq!(WatchpointType::Write, WatchpointType::Write);
363 assert_ne!(WatchpointType::Write, WatchpointType::Read);
364 assert_ne!(WatchpointType::Read, WatchpointType::Access);
365 }
366
367 #[test]
368 fn test_parse_z_command() {
369 let result = parse_z_command(b"0,401000,1");
370 assert!(result.is_some());
371 let (tp, addr, kind) = result.unwrap();
372 assert_eq!(tp, 0);
373 assert_eq!(addr, 0x401000);
374 assert_eq!(kind, 1);
375 }
376
377 #[test]
378 fn test_parse_z_command_watchpoint() {
379 let result = parse_z_command(b"2,7ffe1234,4");
380 assert!(result.is_some());
381 let (tp, addr, kind) = result.unwrap();
382 assert_eq!(tp, 2);
383 assert_eq!(addr, 0x7FFE1234);
384 assert_eq!(kind, 4);
385 }
386
387 #[test]
388 fn test_parse_z_command_invalid() {
389 assert!(parse_z_command(b"").is_none());
390 assert!(parse_z_command(b"0").is_none());
391 }
392
393 #[test]
394 fn test_hw_watchpoint_slots() {
395 let mut mgr = BreakpointManager::new();
396 for i in 0..4 {
398 assert!(mgr.insert_hw_watchpoint(0x1000 + i * 8, 4, WatchpointType::Write));
399 }
400 assert!(!mgr.insert_hw_watchpoint(0x2000, 4, WatchpointType::Write));
402
403 assert!(mgr.remove_hw_watchpoint(0x1000, WatchpointType::Write));
405 assert!(mgr.insert_hw_watchpoint(0x2000, 4, WatchpointType::Write));
406 }
407
408 #[test]
409 fn test_remove_nonexistent_watchpoint() {
410 let mut mgr = BreakpointManager::new();
411 assert!(!mgr.remove_hw_watchpoint(0xDEAD, WatchpointType::Write));
412 }
413
414 #[test]
415 fn test_sw_breakpoint_default() {
416 let bp = SwBreakpoint::default();
417 assert_eq!(bp.addr, 0);
418 assert_eq!(bp.original_byte, 0);
419 assert!(!bp.active);
420 }
421
422 #[test]
423 fn test_hw_watchpoint_default() {
424 let wp = HwWatchpoint::default();
425 assert_eq!(wp.addr, 0);
426 assert_eq!(wp.len, 1);
427 assert_eq!(wp.wp_type, WatchpointType::Write);
428 assert!(!wp.active);
429 }
430
431 #[test]
432 fn test_handle_insert_parse() {
433 let result = parse_z_command(b"2,deadbeef,8");
435 assert!(result.is_some());
436 let (tp, addr, kind) = result.unwrap();
437 assert_eq!(tp, 2);
438 assert_eq!(addr, 0xDEADBEEF);
439 assert_eq!(kind, 8);
440 }
441}