1#[cfg(feature = "alloc")]
10use alloc::vec::Vec;
11use core::sync::atomic::{AtomicBool, Ordering};
12
13use spin::Mutex;
14
15use crate::sync::once_lock::OnceLock;
16
17const COM2_BASE: u16 = 0x2F8;
19const COM2_DATA: u16 = COM2_BASE;
20const COM2_IER: u16 = COM2_BASE + 1;
21const COM2_FCR: u16 = COM2_BASE + 2;
22const COM2_LCR: u16 = COM2_BASE + 3;
23const COM2_MCR: u16 = COM2_BASE + 4;
24const COM2_LSR: u16 = COM2_BASE + 5;
25const COM2_DLL: u16 = COM2_BASE;
26const COM2_DLH: u16 = COM2_BASE + 1;
27
28const LSR_DATA_READY: u8 = 0x01;
30const LSR_TX_EMPTY: u8 = 0x20;
31
32const MAX_PACKET_SIZE: usize = 4096;
34
35static GDB_ACTIVE: AtomicBool = AtomicBool::new(false);
37
38#[derive(Debug, Clone, Copy, Default)]
40#[repr(C)]
41pub(crate) struct GdbRegisters {
42 pub(crate) rax: u64,
43 pub(crate) rbx: u64,
44 pub(crate) rcx: u64,
45 pub(crate) rdx: u64,
46 pub(crate) rsi: u64,
47 pub(crate) rdi: u64,
48 pub(crate) rbp: u64,
49 pub(crate) rsp: u64,
50 pub(crate) r8: u64,
51 pub(crate) r9: u64,
52 pub(crate) r10: u64,
53 pub(crate) r11: u64,
54 pub(crate) r12: u64,
55 pub(crate) r13: u64,
56 pub(crate) r14: u64,
57 pub(crate) r15: u64,
58 pub(crate) rip: u64,
59 pub(crate) rflags: u64,
60 pub(crate) cs: u64,
61 pub(crate) ss: u64,
62 pub(crate) ds: u64,
63 pub(crate) es: u64,
64 pub(crate) fs: u64,
65 pub(crate) gs: u64,
66}
67
68impl GdbRegisters {
69 const NUM_REGS: usize = 24;
70
71 fn reg_by_index(&self, idx: usize) -> Option<u64> {
72 match idx {
73 0 => Some(self.rax),
74 1 => Some(self.rbx),
75 2 => Some(self.rcx),
76 3 => Some(self.rdx),
77 4 => Some(self.rsi),
78 5 => Some(self.rdi),
79 6 => Some(self.rbp),
80 7 => Some(self.rsp),
81 8 => Some(self.r8),
82 9 => Some(self.r9),
83 10 => Some(self.r10),
84 11 => Some(self.r11),
85 12 => Some(self.r12),
86 13 => Some(self.r13),
87 14 => Some(self.r14),
88 15 => Some(self.r15),
89 16 => Some(self.rip),
90 17 => Some(self.rflags),
91 18 => Some(self.cs),
92 19 => Some(self.ss),
93 20 => Some(self.ds),
94 21 => Some(self.es),
95 22 => Some(self.fs),
96 23 => Some(self.gs),
97 _ => None,
98 }
99 }
100
101 fn set_reg_by_index(&mut self, idx: usize, val: u64) -> bool {
102 match idx {
103 0 => self.rax = val,
104 1 => self.rbx = val,
105 2 => self.rcx = val,
106 3 => self.rdx = val,
107 4 => self.rsi = val,
108 5 => self.rdi = val,
109 6 => self.rbp = val,
110 7 => self.rsp = val,
111 8 => self.r8 = val,
112 9 => self.r9 = val,
113 10 => self.r10 = val,
114 11 => self.r11 = val,
115 12 => self.r12 = val,
116 13 => self.r13 = val,
117 14 => self.r14 = val,
118 15 => self.r15 = val,
119 16 => self.rip = val,
120 17 => self.rflags = val,
121 18 => self.cs = val,
122 19 => self.ss = val,
123 20 => self.ds = val,
124 21 => self.es = val,
125 22 => self.fs = val,
126 23 => self.gs = val,
127 _ => return false,
128 }
129 true
130 }
131}
132
133struct GdbState {
135 registers: GdbRegisters,
136 connected: bool,
137 no_ack_mode: bool,
138 current_thread: u64,
140 thread_enum_index: usize,
142 thread_ids_cache: Vec<u64>,
144}
145
146impl GdbState {
147 fn new() -> Self {
148 Self {
149 registers: GdbRegisters::default(),
150 connected: false,
151 no_ack_mode: false,
152 current_thread: 1,
153 thread_enum_index: 0,
154 thread_ids_cache: Vec::new(),
155 }
156 }
157}
158
159#[cfg(feature = "alloc")]
161fn collect_thread_ids() -> Vec<u64> {
162 let mut ids = Vec::new();
165 for pid in 1..=256u64 {
166 if crate::sched::scheduler::get_task_ptr(pid).is_some() {
167 ids.push(pid);
168 }
169 }
170 if ids.is_empty() {
171 alloc::vec![1] } else {
173 ids
174 }
175}
176
177fn thread_exists(tid: u64) -> bool {
179 crate::sched::scheduler::get_task_ptr(tid).is_some() || tid == 1
180}
181
182#[cfg(feature = "alloc")]
184fn format_thread_id_hex(tid: u64) -> Vec<u8> {
185 if tid == 0 {
186 return alloc::vec![b'0'];
187 }
188 let mut result = Vec::new();
189 let val = tid;
190 let mut started = false;
191 for shift in (0..16).rev() {
192 let nibble = ((val >> (shift * 4)) & 0xF) as u8;
193 if nibble != 0 || started {
194 started = true;
195 result.push(if nibble < 10 {
196 b'0' + nibble
197 } else {
198 b'a' + nibble - 10
199 });
200 }
201 }
202 if result.is_empty() {
203 result.push(b'0');
204 }
205 result
206}
207
208#[cfg(all(feature = "alloc", target_arch = "x86_64"))]
210fn load_thread_registers(tid: u64) -> Option<GdbRegisters> {
211 let task_ptr = crate::sched::scheduler::get_task_ptr(tid)?;
212 let task = unsafe { task_ptr.as_ref() };
215 match &task.context {
216 crate::sched::task::TaskContext::X86_64(ctx) => Some(GdbRegisters {
217 rax: ctx.rax,
218 rbx: ctx.rbx,
219 rcx: ctx.rcx,
220 rdx: ctx.rdx,
221 rsi: ctx.rsi,
222 rdi: ctx.rdi,
223 rbp: ctx.rbp,
224 rsp: ctx.rsp,
225 r8: ctx.r8,
226 r9: ctx.r9,
227 r10: ctx.r10,
228 r11: ctx.r11,
229 r12: ctx.r12,
230 r13: ctx.r13,
231 r14: ctx.r14,
232 r15: ctx.r15,
233 rip: ctx.rip,
234 rflags: ctx.rflags,
235 cs: ctx.cs as u64,
236 ss: ctx.ss as u64,
237 ds: ctx.ds as u64,
238 es: ctx.es as u64,
239 fs: ctx.fs as u64,
240 gs: ctx.gs as u64,
241 }),
242 }
243}
244
245#[cfg(all(feature = "alloc", not(target_arch = "x86_64")))]
246fn load_thread_registers(_tid: u64) -> Option<GdbRegisters> {
247 None }
249
250static GDB_STATE: OnceLock<Mutex<GdbState>> = OnceLock::new();
251
252#[cfg(target_os = "none")]
257fn com2_init() {
258 unsafe {
261 outb(COM2_IER, 0x00);
263 outb(COM2_LCR, 0x80);
265 outb(COM2_DLL, 0x01);
267 outb(COM2_DLH, 0x00);
268 outb(COM2_LCR, 0x03);
270 outb(COM2_FCR, 0xC7);
272 outb(COM2_MCR, 0x0B);
274 }
275}
276
277#[cfg(not(target_os = "none"))]
278fn com2_init() {}
279
280#[cfg(target_os = "none")]
281unsafe fn outb(port: u16, val: u8) {
282 core::arch::asm!("out dx, al", in("dx") port, in("al") val, options(nostack, preserves_flags));
283}
284
285#[cfg(target_os = "none")]
286unsafe fn inb(port: u16) -> u8 {
287 let val: u8;
288 core::arch::asm!("in al, dx", out("al") val, in("dx") port, options(nostack, preserves_flags));
289 val
290}
291
292#[cfg(target_os = "none")]
293fn com2_read_byte() -> u8 {
294 unsafe {
296 while (inb(COM2_LSR) & LSR_DATA_READY) == 0 {
298 core::hint::spin_loop();
299 }
300 inb(COM2_DATA)
301 }
302}
303
304#[cfg(target_os = "none")]
305fn com2_write_byte(byte: u8) {
306 unsafe {
308 while (inb(COM2_LSR) & LSR_TX_EMPTY) == 0 {
310 core::hint::spin_loop();
311 }
312 outb(COM2_DATA, byte);
313 }
314}
315
316#[cfg(target_os = "none")]
317fn _com2_data_available() -> bool {
318 unsafe { (inb(COM2_LSR) & LSR_DATA_READY) != 0 }
320}
321
322#[cfg(not(target_os = "none"))]
323fn com2_read_byte() -> u8 {
324 0
325}
326
327#[cfg(not(target_os = "none"))]
328fn com2_write_byte(_byte: u8) {}
329
330fn compute_checksum(data: &[u8]) -> u8 {
335 let mut sum: u8 = 0;
336 for &b in data {
337 sum = sum.wrapping_add(b);
338 }
339 sum
340}
341
342fn hex_char(nibble: u8) -> u8 {
343 let n = nibble & 0x0F;
344 if n < 10 {
345 b'0' + n
346 } else {
347 b'a' + (n - 10)
348 }
349}
350
351pub(crate) fn hex_digit(c: u8) -> Option<u8> {
352 match c {
353 b'0'..=b'9' => Some(c - b'0'),
354 b'a'..=b'f' => Some(c - b'a' + 10),
355 b'A'..=b'F' => Some(c - b'A' + 10),
356 _ => None,
357 }
358}
359
360fn hex_to_u8(hi: u8, lo: u8) -> Option<u8> {
361 let h = hex_digit(hi)?;
362 let l = hex_digit(lo)?;
363 Some((h << 4) | l)
364}
365
366pub(crate) fn parse_hex_u64(s: &[u8]) -> Option<u64> {
368 if s.is_empty() {
369 return None;
370 }
371 let mut val: u64 = 0;
372 for &c in s {
373 let d = hex_digit(c)?;
374 val = val.checked_shl(4)?.wrapping_add(d as u64);
375 }
376 Some(val)
377}
378
379fn receive_packet() -> Option<Vec<u8>> {
382 loop {
384 let c = com2_read_byte();
385 if c == b'$' {
386 break;
387 }
388 if c == 0x03 {
389 return Some(alloc::vec![0x03]);
391 }
392 }
393
394 let mut buf = Vec::with_capacity(MAX_PACKET_SIZE);
395
396 loop {
398 let c = com2_read_byte();
399 if c == b'#' {
400 break;
401 }
402 if buf.len() >= MAX_PACKET_SIZE {
403 return None;
404 }
405 buf.push(c);
406 }
407
408 let hi = com2_read_byte();
410 let lo = com2_read_byte();
411 let checksum_received = hex_to_u8(hi, lo).unwrap_or(0);
412
413 let computed = compute_checksum(&buf);
414 if computed == checksum_received {
415 com2_write_byte(b'+');
417 Some(buf)
418 } else {
419 com2_write_byte(b'-');
421 None
422 }
423}
424
425fn send_packet(data: &[u8]) {
427 com2_write_byte(b'$');
428 for &b in data {
429 com2_write_byte(b);
430 }
431 com2_write_byte(b'#');
432 let cksum = compute_checksum(data);
433 com2_write_byte(hex_char(cksum >> 4));
434 com2_write_byte(hex_char(cksum & 0x0F));
435}
436
437fn send_ok() {
439 send_packet(b"OK");
440}
441
442fn send_error(code: u8) {
444 let mut buf = [b'E', 0, 0];
445 buf[1] = hex_char(code >> 4);
446 buf[2] = hex_char(code & 0x0F);
447 send_packet(&buf);
448}
449
450fn send_empty() {
452 send_packet(b"");
453}
454
455fn encode_hex_u64(val: u64, buf: &mut Vec<u8>) {
460 for i in 0..8 {
462 let byte = ((val >> (i * 8)) & 0xFF) as u8;
463 buf.push(hex_char(byte >> 4));
464 buf.push(hex_char(byte & 0x0F));
465 }
466}
467
468fn decode_hex_u64_le(data: &[u8]) -> Option<u64> {
469 if data.len() < 16 {
470 return None;
471 }
472 let mut val: u64 = 0;
473 for i in 0..8 {
474 let byte = hex_to_u8(data[i * 2], data[i * 2 + 1])?;
475 val |= (byte as u64) << (i * 8);
476 }
477 Some(val)
478}
479
480fn handle_read_registers(state: &GdbState) -> Vec<u8> {
486 let mut buf = Vec::with_capacity(GdbRegisters::NUM_REGS * 16);
487 for i in 0..GdbRegisters::NUM_REGS {
488 if let Some(val) = state.registers.reg_by_index(i) {
489 encode_hex_u64(val, &mut buf);
490 }
491 }
492 buf
493}
494
495fn handle_write_registers(state: &mut GdbState, data: &[u8]) -> bool {
497 if data.len() < GdbRegisters::NUM_REGS * 16 {
498 return false;
499 }
500 for i in 0..GdbRegisters::NUM_REGS {
501 let offset = i * 16;
502 if let Some(val) = decode_hex_u64_le(&data[offset..offset + 16]) {
503 state.registers.set_reg_by_index(i, val);
504 }
505 }
506 true
507}
508
509fn handle_read_single_reg(state: &GdbState, data: &[u8]) -> Option<Vec<u8>> {
511 let reg_num = parse_hex_u64(data)? as usize;
512 let val = state.registers.reg_by_index(reg_num)?;
513 let mut buf = Vec::with_capacity(16);
514 encode_hex_u64(val, &mut buf);
515 Some(buf)
516}
517
518fn handle_write_single_reg(state: &mut GdbState, data: &[u8]) -> bool {
520 let eq_pos = data.iter().position(|&c| c == b'=');
522 let eq_pos = match eq_pos {
523 Some(p) => p,
524 None => return false,
525 };
526
527 let reg_num = match parse_hex_u64(&data[..eq_pos]) {
528 Some(n) => n as usize,
529 None => return false,
530 };
531
532 let val = match decode_hex_u64_le(&data[eq_pos + 1..]) {
533 Some(v) => v,
534 None => return false,
535 };
536
537 state.registers.set_reg_by_index(reg_num, val)
538}
539
540fn handle_read_memory(data: &[u8]) -> Option<Vec<u8>> {
542 let comma = data.iter().position(|&c| c == b',')?;
544 let addr = parse_hex_u64(&data[..comma])?;
545 let len = parse_hex_u64(&data[comma + 1..])? as usize;
546
547 if len > MAX_PACKET_SIZE / 2 {
548 return None;
549 }
550
551 let mut buf = Vec::with_capacity(len * 2);
552
553 for i in 0..len {
554 let ptr = (addr + i as u64) as *const u8;
555 let byte = unsafe { core::ptr::read_volatile(ptr) };
559 buf.push(hex_char(byte >> 4));
560 buf.push(hex_char(byte & 0x0F));
561 }
562
563 Some(buf)
564}
565
566fn handle_write_memory(data: &[u8]) -> bool {
568 let comma = match data.iter().position(|&c| c == b',') {
570 Some(p) => p,
571 None => return false,
572 };
573 let colon = match data.iter().position(|&c| c == b':') {
574 Some(p) => p,
575 None => return false,
576 };
577
578 let addr = match parse_hex_u64(&data[..comma]) {
579 Some(a) => a,
580 None => return false,
581 };
582 let len = match parse_hex_u64(&data[comma + 1..colon]) {
583 Some(l) => l as usize,
584 None => return false,
585 };
586
587 let hex_data = &data[colon + 1..];
588 if hex_data.len() < len * 2 {
589 return false;
590 }
591
592 for i in 0..len {
593 let byte = match hex_to_u8(hex_data[i * 2], hex_data[i * 2 + 1]) {
594 Some(b) => b,
595 None => return false,
596 };
597 let ptr = (addr + i as u64) as *mut u8;
598 unsafe {
600 core::ptr::write_volatile(ptr, byte);
601 }
602 }
603
604 true
605}
606
607fn handle_halt_reason() -> Vec<u8> {
609 alloc::vec![b'S', b'0', b'5']
611}
612
613fn handle_query(_state: &mut GdbState, data: &[u8]) -> Option<Vec<u8>> {
615 if data.starts_with(b"Supported") {
616 return Some(
617 b"PacketSize=1000;QStartNoAckMode+;qXfer:features:read+;multiprocess-".to_vec(),
618 );
619 }
620 if data.starts_with(b"Attached") {
621 return Some(b"1".to_vec());
622 }
623 if data == b"fThreadInfo" {
624 _state.thread_ids_cache = collect_thread_ids();
626 _state.thread_enum_index = 0;
627 if _state.thread_ids_cache.is_empty() {
628 return Some(b"l".to_vec());
629 }
630 let mut response = alloc::vec![b'm'];
631 for (i, &tid) in _state.thread_ids_cache.iter().enumerate() {
632 if i > 0 {
633 response.push(b',');
634 }
635 response.extend_from_slice(&format_thread_id_hex(tid));
636 }
637 _state.thread_enum_index = _state.thread_ids_cache.len();
638 return Some(response);
639 }
640 if data == b"sThreadInfo" {
641 return Some(b"l".to_vec());
643 }
644 if data == b"C" {
645 let mut resp = b"QC".to_vec();
647 resp.extend_from_slice(&format_thread_id_hex(_state.current_thread));
648 return Some(resp);
649 }
650 if data.starts_with(b"Xfer:features:read:target.xml:") {
651 let xml = b"l<?xml version=\"1.0\"?>\
652 <!DOCTYPE target SYSTEM \"gdb-target.dtd\">\
653 <target version=\"1.0\">\
654 <architecture>i386:x86-64</architecture>\
655 </target>";
656 return Some(xml.to_vec());
657 }
658
659 if data.starts_with(b"StartNoAckMode") {
661 }
663
664 None
665}
666
667fn handle_set_command(state: &mut GdbState, data: &[u8]) -> Option<Vec<u8>> {
669 if data.starts_with(b"StartNoAckMode") {
670 state.no_ack_mode = true;
671 return Some(b"OK".to_vec());
672 }
673 None
674}
675
676fn handle_set_thread(state: &mut GdbState, data: &[u8]) -> Vec<u8> {
678 if data.is_empty() {
679 return b"E01".to_vec();
680 }
681 let _op = data[0]; let tid_str = &data[1..];
683 if tid_str.is_empty() {
684 return b"E01".to_vec();
685 }
686
687 let tid = if tid_str == b"-1" {
689 0xFFFF_FFFF_FFFF_FFFF
691 } else {
692 let mut val = 0u64;
693 for &b in tid_str {
694 let nibble = match b {
695 b'0'..=b'9' => b - b'0',
696 b'a'..=b'f' => b - b'a' + 10,
697 b'A'..=b'F' => b - b'A' + 10,
698 _ => return b"E01".to_vec(),
699 };
700 val = val.wrapping_shl(4) | nibble as u64;
701 }
702 val
703 };
704
705 if tid == 0 || tid == 0xFFFF_FFFF_FFFF_FFFF {
707 return b"OK".to_vec();
709 }
710
711 if thread_exists(tid) {
713 state.current_thread = tid;
714 b"OK".to_vec()
715 } else {
716 b"E01".to_vec()
717 }
718}
719
720fn handle_thread_alive(data: &[u8]) -> Vec<u8> {
722 if data.is_empty() {
723 return b"OK".to_vec();
724 }
725 let mut tid = 0u64;
726 for &b in data {
727 let nibble = match b {
728 b'0'..=b'9' => b - b'0',
729 b'a'..=b'f' => b - b'a' + 10,
730 b'A'..=b'F' => b - b'A' + 10,
731 _ => return b"E01".to_vec(),
732 };
733 tid = tid.wrapping_shl(4) | nibble as u64;
734 }
735 if thread_exists(tid) {
736 b"OK".to_vec()
737 } else {
738 b"E01".to_vec()
739 }
740}
741
742#[cfg(feature = "alloc")]
744fn handle_vattach(state: &mut GdbState, data: &[u8]) -> Vec<u8> {
745 if !data.starts_with(b"Attach;") {
747 return b"E01".to_vec();
748 }
749 let pid_hex = &data[7..];
750 let mut pid = 0u64;
751 for &b in pid_hex {
752 let nibble = match b {
753 b'0'..=b'9' => b - b'0',
754 b'a'..=b'f' => b - b'a' + 10,
755 b'A'..=b'F' => b - b'A' + 10,
756 _ => return b"E01".to_vec(),
757 };
758 pid = pid.wrapping_shl(4) | nibble as u64;
759 }
760 if thread_exists(pid) {
761 state.current_thread = pid;
762 b"S05".to_vec() } else {
764 b"E01".to_vec()
765 }
766}
767
768#[cfg(feature = "alloc")]
770fn handle_vkill(data: &[u8]) -> Vec<u8> {
771 if !data.starts_with(b"Kill;") {
772 return b"E01".to_vec();
773 }
774 let pid_hex = &data[5..];
775 let mut pid = 0u64;
776 for &b in pid_hex {
777 let nibble = match b {
778 b'0'..=b'9' => b - b'0',
779 b'a'..=b'f' => b - b'a' + 10,
780 b'A'..=b'F' => b - b'A' + 10,
781 _ => return b"E01".to_vec(),
782 };
783 pid = pid.wrapping_shl(4) | nibble as u64;
784 }
785 let process_id = crate::process::pcb::ProcessId(pid);
787 if crate::process::exit::kill_process(process_id, 9).is_ok() {
788 b"OK".to_vec()
789 } else {
790 b"E01".to_vec()
791 }
792}
793
794pub(crate) fn gdb_init() {
800 com2_init();
801
802 let state = GdbState::new();
803 let _ = GDB_STATE.set(Mutex::new(state));
804
805 #[cfg(target_os = "none")]
806 crate::serial_println!("[GDB] Stub initialized on COM2 (0x2F8), waiting for connection...");
807}
808
809pub(crate) fn is_gdb_active() -> bool {
811 GDB_ACTIVE.load(Ordering::Relaxed)
812}
813
814pub(crate) fn gdb_handle_exception(signal: u8, regs: &mut GdbRegisters) {
818 let state_lock = match GDB_STATE.get() {
819 Some(s) => s,
820 None => return,
821 };
822
823 {
824 let mut state = state_lock.lock();
825 state.registers = *regs;
826 state.connected = true;
827 }
828 GDB_ACTIVE.store(true, Ordering::Release);
829
830 let sig_reply = [b'S', hex_char(signal >> 4), hex_char(signal & 0x0F)];
832 send_packet(&sig_reply);
833
834 loop {
836 let packet = match receive_packet() {
837 Some(p) => p,
838 None => continue,
839 };
840
841 if packet.is_empty() {
842 send_empty();
843 continue;
844 }
845
846 let cmd = packet[0];
847 let args = &packet[1..];
848
849 match cmd {
850 0x03 => {
852 let reply = handle_halt_reason();
853 send_packet(&reply);
854 }
855 b'?' => {
857 let reply = handle_halt_reason();
858 send_packet(&reply);
859 }
860 b'g' => {
862 let state = state_lock.lock();
863 let reply = handle_read_registers(&state);
864 send_packet(&reply);
865 }
866 b'G' => {
868 let mut state = state_lock.lock();
869 if handle_write_registers(&mut state, args) {
870 send_ok();
871 } else {
872 send_error(0x01);
873 }
874 }
875 b'p' => {
877 let state = state_lock.lock();
878 match handle_read_single_reg(&state, args) {
879 Some(reply) => send_packet(&reply),
880 None => send_error(0x01),
881 }
882 }
883 b'P' => {
885 let mut state = state_lock.lock();
886 if handle_write_single_reg(&mut state, args) {
887 send_ok();
888 } else {
889 send_error(0x01);
890 }
891 }
892 b'm' => match handle_read_memory(args) {
894 Some(reply) => send_packet(&reply),
895 None => send_error(0x01),
896 },
897 b'M' => {
899 if handle_write_memory(args) {
900 send_ok();
901 } else {
902 send_error(0x01);
903 }
904 }
905 b'c' => {
907 if !args.is_empty() {
909 if let Some(addr) = parse_hex_u64(args) {
910 let mut state = state_lock.lock();
911 state.registers.rip = addr;
912 }
913 }
914 let state = state_lock.lock();
916 *regs = state.registers;
917 return;
918 }
919 b's' => {
921 let mut state = state_lock.lock();
923 if !args.is_empty() {
924 if let Some(addr) = parse_hex_u64(args) {
925 state.registers.rip = addr;
926 }
927 }
928 state.registers.rflags |= 0x100; *regs = state.registers;
930 return;
931 }
932 b'D' => {
934 send_ok();
935 GDB_ACTIVE.store(false, Ordering::Release);
936 let mut state = state_lock.lock();
937 state.connected = false;
938 *regs = state.registers;
939 return;
940 }
941 b'k' => {
943 GDB_ACTIVE.store(false, Ordering::Release);
944 let mut state = state_lock.lock();
945 state.connected = false;
946 *regs = state.registers;
947 return;
948 }
949 b'H' => {
951 let mut state = state_lock.lock();
952 let reply = handle_set_thread(&mut state, args);
953 send_packet(&reply);
954 }
955 b'T' => {
957 let reply = handle_thread_alive(args);
958 send_packet(&reply);
959 }
960 b'v' => {
962 if args.starts_with(b"Cont?") {
963 send_packet(b"vCont;c;s;t");
964 } else if args.starts_with(b"Cont;c") {
965 let state = state_lock.lock();
966 *regs = state.registers;
967 return;
968 } else if args.starts_with(b"Cont;s") {
969 let mut state = state_lock.lock();
970 state.registers.rflags |= 0x100;
971 *regs = state.registers;
972 return;
973 } else if args.starts_with(b"Kill;") {
974 let reply = handle_vkill(args);
975 send_packet(&reply);
976 GDB_ACTIVE.store(false, Ordering::Release);
977 let mut state = state_lock.lock();
978 state.connected = false;
979 *regs = state.registers;
980 return;
981 } else if args.starts_with(b"Attach;") {
982 let mut state = state_lock.lock();
983 let reply = handle_vattach(&mut state, args);
984 send_packet(&reply);
985 } else {
986 send_empty();
987 }
988 }
989 b'q' => {
991 let mut state = state_lock.lock();
992 match handle_query(&mut state, args) {
993 Some(reply) => send_packet(&reply),
994 None => send_empty(),
995 }
996 }
997 b'Q' => {
999 let mut state = state_lock.lock();
1000 match handle_set_command(&mut state, args) {
1001 Some(reply) => send_packet(&reply),
1002 None => send_empty(),
1003 }
1004 }
1005 b'Z' => {
1007 if let Some(reply) = crate::debug::breakpoint::handle_insert(args) {
1008 send_packet(&reply);
1009 } else {
1010 send_empty();
1011 }
1012 }
1013 b'z' => {
1015 if let Some(reply) = crate::debug::breakpoint::handle_remove(args) {
1016 send_packet(&reply);
1017 } else {
1018 send_empty();
1019 }
1020 }
1021 _ => {
1022 send_empty();
1023 }
1024 }
1025 }
1026}
1027
1028#[cfg(test)]
1033mod tests {
1034 #[allow(unused_imports)]
1035 use alloc::vec;
1036
1037 use super::*;
1038
1039 #[test]
1040 fn test_compute_checksum() {
1041 assert_eq!(compute_checksum(b"OK"), b'O'.wrapping_add(b'K'));
1042 assert_eq!(compute_checksum(b""), 0);
1043 assert_eq!(
1044 compute_checksum(b"S05"),
1045 b'S'.wrapping_add(b'0').wrapping_add(b'5')
1046 );
1047 }
1048
1049 #[test]
1050 fn test_hex_char() {
1051 assert_eq!(hex_char(0), b'0');
1052 assert_eq!(hex_char(9), b'9');
1053 assert_eq!(hex_char(10), b'a');
1054 assert_eq!(hex_char(15), b'f');
1055 }
1056
1057 #[test]
1058 fn test_hex_digit() {
1059 assert_eq!(hex_digit(b'0'), Some(0));
1060 assert_eq!(hex_digit(b'9'), Some(9));
1061 assert_eq!(hex_digit(b'a'), Some(10));
1062 assert_eq!(hex_digit(b'f'), Some(15));
1063 assert_eq!(hex_digit(b'A'), Some(10));
1064 assert_eq!(hex_digit(b'F'), Some(15));
1065 assert_eq!(hex_digit(b'g'), None);
1066 }
1067
1068 #[test]
1069 fn test_hex_to_u8() {
1070 assert_eq!(hex_to_u8(b'0', b'0'), Some(0x00));
1071 assert_eq!(hex_to_u8(b'f', b'f'), Some(0xFF));
1072 assert_eq!(hex_to_u8(b'4', b'2'), Some(0x42));
1073 assert_eq!(hex_to_u8(b'g', b'0'), None);
1074 }
1075
1076 #[test]
1077 fn test_parse_hex_u64() {
1078 assert_eq!(parse_hex_u64(b"0"), Some(0));
1079 assert_eq!(parse_hex_u64(b"ff"), Some(0xFF));
1080 assert_eq!(parse_hex_u64(b"deadbeef"), Some(0xDEADBEEF));
1081 assert_eq!(parse_hex_u64(b""), None);
1082 }
1083
1084 #[test]
1085 fn test_encode_decode_u64_le() {
1086 let val: u64 = 0x0102030405060708;
1087 let mut buf = Vec::new();
1088 encode_hex_u64(val, &mut buf);
1089 let decoded = decode_hex_u64_le(&buf);
1090 assert_eq!(decoded, Some(val));
1091 }
1092
1093 #[test]
1094 fn test_registers_default() {
1095 let regs = GdbRegisters::default();
1096 assert_eq!(regs.rax, 0);
1097 assert_eq!(regs.rip, 0);
1098 assert_eq!(regs.rflags, 0);
1099 }
1100
1101 #[test]
1102 fn test_register_read_write() {
1103 let mut regs = GdbRegisters::default();
1104 assert!(regs.set_reg_by_index(0, 0x42));
1105 assert_eq!(regs.reg_by_index(0), Some(0x42));
1106 assert_eq!(regs.rax, 0x42);
1107
1108 assert!(regs.set_reg_by_index(16, 0xDEAD));
1109 assert_eq!(regs.rip, 0xDEAD);
1110
1111 assert!(!regs.set_reg_by_index(99, 0));
1112 assert_eq!(regs.reg_by_index(99), None);
1113 }
1114
1115 #[test]
1116 fn test_handle_read_registers() {
1117 let state = GdbState::new();
1118 let reply = handle_read_registers(&state);
1119 assert_eq!(reply.len(), GdbRegisters::NUM_REGS * 16);
1120 assert!(reply.iter().all(|&b| b == b'0'));
1122 }
1123
1124 #[test]
1125 fn test_handle_write_registers() {
1126 let mut state = GdbState::new();
1127 let mut data = Vec::new();
1129 for _ in 0..GdbRegisters::NUM_REGS {
1130 encode_hex_u64(0x42, &mut data);
1131 }
1132 assert!(handle_write_registers(&mut state, &data));
1133 assert_eq!(state.registers.rax, 0x42);
1134 assert_eq!(state.registers.rip, 0x42);
1135 }
1136
1137 #[test]
1138 fn test_handle_single_reg() {
1139 let mut state = GdbState::new();
1140 state.registers.rax = 0xCAFE;
1141
1142 let reply = handle_read_single_reg(&state, b"0").unwrap();
1143 assert_eq!(reply.len(), 16);
1144
1145 let mut cmd = Vec::new();
1147 cmd.extend_from_slice(b"0=");
1148 encode_hex_u64(0x1234, &mut cmd);
1149 assert!(handle_write_single_reg(&mut state, &cmd));
1150 assert_eq!(state.registers.rax, 0x1234);
1151 }
1152
1153 #[test]
1154 fn test_halt_reason() {
1155 let reply = handle_halt_reason();
1156 assert_eq!(&reply, b"S05");
1157 }
1158
1159 #[test]
1160 fn test_handle_set_thread_any() {
1161 let mut state = GdbState::new();
1162 let reply = handle_set_thread(&mut state, b"g0");
1163 assert_eq!(&reply, b"OK");
1164 }
1165
1166 #[test]
1167 fn test_handle_set_thread_all() {
1168 let mut state = GdbState::new();
1169 let reply = handle_set_thread(&mut state, b"g-1");
1170 assert_eq!(&reply, b"OK");
1171 }
1172
1173 #[test]
1174 fn test_handle_set_thread_specific() {
1175 let mut state = GdbState::new();
1176 let reply = handle_set_thread(&mut state, b"g1");
1178 assert_eq!(&reply, b"OK");
1179 assert_eq!(state.current_thread, 1);
1180 }
1181
1182 #[test]
1183 fn test_handle_set_thread_empty_error() {
1184 let mut state = GdbState::new();
1185 let reply = handle_set_thread(&mut state, b"");
1186 assert_eq!(&reply, b"E01");
1187 }
1188
1189 #[test]
1190 fn test_handle_set_thread_continue_op() {
1191 let mut state = GdbState::new();
1192 let reply = handle_set_thread(&mut state, b"c1");
1193 assert_eq!(&reply, b"OK");
1194 }
1195
1196 #[test]
1197 fn test_format_thread_id_hex() {
1198 assert_eq!(format_thread_id_hex(0), vec![b'0']);
1199 assert_eq!(format_thread_id_hex(1), vec![b'1']);
1200 assert_eq!(format_thread_id_hex(0xFF), vec![b'f', b'f']);
1201 assert_eq!(format_thread_id_hex(0x1234), vec![b'1', b'2', b'3', b'4']);
1202 }
1203
1204 #[test]
1205 fn test_gdb_state_default_thread() {
1206 let state = GdbState::new();
1207 assert_eq!(state.current_thread, 1);
1208 }
1209
1210 #[test]
1211 fn test_collect_thread_ids_fallback() {
1212 let ids = collect_thread_ids();
1214 assert!(!ids.is_empty());
1215 }
1216}