1use rmcp::{
6 tool, tool_handler, tool_router, ServerHandler,
7 handler::server::{router::tool::ToolRouter, tool::Parameters},
8 model::*,
9 ErrorData as McpError,
10 service::RequestContext,
11 RoleServer,
12};
13use tracing::{debug, error, info, warn};
14use std::future::Future;
15use std::collections::HashMap;
16use std::sync::Arc;
17use tokio::sync::RwLock;
18
19use super::types::*;
20use crate::rtt::RttManager;
22
23use probe_rs::probe::list::Lister;
25use probe_rs::{Session, Permissions, CoreStatus, MemoryInterface, RegisterValue};
26
27#[derive(Debug)]
29pub struct DebugSession {
30 pub session_id: String,
31 pub probe_identifier: String,
32 pub target_chip: String,
33 pub created_at: chrono::DateTime<chrono::Utc>,
34 pub session: Arc<tokio::sync::Mutex<Session>>,
35 pub rtt_manager: Arc<tokio::sync::Mutex<RttManager>>,
36}
37
38#[derive(Clone)]
40pub struct EmbeddedDebuggerToolHandler {
41 #[allow(dead_code)]
42 tool_router: ToolRouter<EmbeddedDebuggerToolHandler>,
43 sessions: Arc<RwLock<HashMap<String, Arc<DebugSession>>>>,
44 max_sessions: usize,
45}
46
47impl EmbeddedDebuggerToolHandler {
48 pub fn new(max_sessions: usize) -> Self {
49 Self {
50 tool_router: Self::tool_router(),
51 sessions: Arc::new(RwLock::new(HashMap::new())),
52 max_sessions,
53 }
54 }
55}
56
57impl Default for EmbeddedDebuggerToolHandler {
58 fn default() -> Self {
59 Self::new(5)
60 }
61}
62
63#[tool_router]
64impl EmbeddedDebuggerToolHandler {
65 #[tool(description = "List all available debug probes (J-Link, ST-Link, DAPLink, etc.)")]
70 async fn list_probes(&self, Parameters(_args): Parameters<ListProbesArgs>) -> Result<CallToolResult, McpError> {
71 debug!("Listing available debug probes");
72
73 let probes = Lister::new().list_all();
75 let message = if probes.is_empty() {
76 "No debug probes found.\n\nPlease ensure your probe is connected and drivers are installed.\nSupported probes: J-Link, ST-Link, DAPLink, Black Magic Probe".to_string()
77 } else {
78 let mut result = format!("Found {} debug probe(s):\n\n", probes.len());
79
80 for (i, probe) in probes.iter().enumerate() {
81 result.push_str(&format!("{}. {}\n", i + 1, probe.identifier));
82 result.push_str(&format!(" VID:PID = {:04X}:{:04X}\n", probe.vendor_id, probe.product_id));
83
84 if let Some(serial) = &probe.serial_number {
85 result.push_str(&format!(" Serial: {}\n", serial));
86 }
87
88 result.push_str(&format!(" Probe Type: {:?}\n", probe.probe_type()));
89 result.push('\n');
90 }
91
92 result
93 };
94
95 info!("Listed {} debug probes", probes.len());
96 Ok(CallToolResult::success(vec![Content::text(message)]))
97 }
98
99 #[tool(description = "Connect to a debug probe and target chip")]
100 async fn connect(&self, Parameters(args): Parameters<ConnectArgs>) -> Result<CallToolResult, McpError> {
101 debug!("Connecting to probe '{}' and target '{}'", args.probe_selector, args.target_chip);
102
103 {
105 let sessions = self.sessions.read().await;
106 if sessions.len() >= self.max_sessions {
107 let error_msg = format!("Session limit exceeded. Maximum {} sessions allowed.", self.max_sessions);
108 return Err(McpError::internal_error(error_msg, None));
109 }
110 }
111
112 let probes = Lister::new().list_all();
114
115 if probes.is_empty() {
116 return Err(McpError::internal_error(
117 "❌ No debug probes found\n\nPlease connect a supported probe (J-Link, ST-Link, DAPLink, etc.)".to_string(),
118 None
119 ));
120 }
121
122 let selected_probe = if args.probe_selector.to_lowercase() == "auto" {
123 probes.first()
124 } else {
125 probes.iter().find(|p| p.identifier.contains(&args.probe_selector))
126 };
127
128 match selected_probe {
129 Some(probe_info) => {
130 info!("Opening probe: {}", probe_info.identifier);
131 match probe_info.open() {
132 Ok(probe) => {
133 info!("Attaching to target: {}", args.target_chip);
134 match probe.attach(&args.target_chip, Permissions::default()) {
135 Ok(session) => {
136 let session_id = format!("session_{}", chrono::Utc::now().timestamp_millis());
137
138 let debug_session = DebugSession {
139 session_id: session_id.clone(),
140 probe_identifier: probe_info.identifier.clone(),
141 target_chip: args.target_chip.clone(),
142 created_at: chrono::Utc::now(),
143 session: Arc::new(tokio::sync::Mutex::new(session)),
144 rtt_manager: Arc::new(tokio::sync::Mutex::new(RttManager::new())),
145 };
146
147 {
149 let mut sessions = self.sessions.write().await;
150 sessions.insert(session_id.clone(), Arc::new(debug_session));
151 }
152
153 let message = format!(
154 "✅ Debug session established!\n\n\
155 Session ID: {}\n\
156 Probe: {} (VID:PID = {:04X}:{:04X})\n\
157 Target: {}\n\
158 Connected at: {}\n\n\
159 Target connection established and ready for debugging.\n\
160 Use this session ID for all debug operations.",
161 session_id,
162 probe_info.identifier,
163 probe_info.vendor_id, probe_info.product_id,
164 args.target_chip,
165 chrono::Utc::now().format("%Y-%m-%d %H:%M:%S UTC")
166 );
167
168 info!("Created debug session: {}", session_id);
169 Ok(CallToolResult::success(vec![Content::text(message)]))
170 }
171 Err(e) => {
172 error!("Failed to attach to target '{}': {}", args.target_chip, e);
173 let error_msg = format!(
174 "❌ Failed to attach to target '{}'\n\n\
175 Error: {}\n\n\
176 Suggestions:\n\
177 - Check target chip name (try: STM32F407VGTx, nRF52840_xxAA)\n\
178 - Ensure target is powered and connected\n\
179 - Verify SWD/JTAG connections",
180 args.target_chip, e
181 );
182 Err(McpError::internal_error(error_msg, None))
183 }
184 }
185 }
186 Err(e) => {
187 error!("Failed to open probe '{}': {}", probe_info.identifier, e);
188 let error_msg = format!(
189 "❌ Failed to open probe '{}'\n\nError: {}\n\n\
190 Suggestions:\n\
191 - Check probe drivers installation\n\
192 - Verify USB connection\n\
193 - Try disconnecting and reconnecting probe",
194 probe_info.identifier, e
195 );
196 Err(McpError::internal_error(error_msg, None))
197 }
198 }
199 }
200 None => {
201 let available_probes: Vec<String> = probes
202 .iter()
203 .map(|p| format!("- {}", p.identifier))
204 .collect();
205
206 let error_msg = format!(
207 "❌ Probe '{}' not found\n\n\
208 Available probes:\n{}\n\n\
209 Use 'auto' to connect to first available probe.",
210 args.probe_selector,
211 available_probes.join("\n")
212 );
213 Err(McpError::internal_error(error_msg, None))
214 }
215 }
216 }
217
218 #[tool(description = "Disconnect from a debug session")]
219 async fn disconnect(&self, Parameters(args): Parameters<DisconnectArgs>) -> Result<CallToolResult, McpError> {
220 debug!("Disconnecting session: {}", args.session_id);
221
222 let removed_session = {
224 let mut sessions = self.sessions.write().await;
225 sessions.remove(&args.session_id)
226 };
227
228 match removed_session {
229 Some(session) => {
230 let message = format!(
231 "✅ Debug session disconnected successfully\n\n\
232 Session ID: {}\n\
233 Probe: {}\n\
234 Target: {}\n\
235 Duration: {:.1} minutes\n\n\
236 probe-rs Session resources have been cleaned up.",
237 args.session_id,
238 session.probe_identifier,
239 session.target_chip,
240 (chrono::Utc::now() - session.created_at).num_seconds() as f64 / 60.0
241 );
242
243 info!("Disconnected debug session: {}", args.session_id);
244 Ok(CallToolResult::success(vec![Content::text(message)]))
245 }
246 None => {
247 let error_msg = format!("❌ Session '{}' not found\n\nUse 'list_sessions' to see active sessions", args.session_id);
248 Err(McpError::internal_error(error_msg, None))
249 }
250 }
251 }
252
253 #[tool(description = "Get basic information about a debug session")]
254 async fn probe_info(&self, Parameters(args): Parameters<ProbeInfoArgs>) -> Result<CallToolResult, McpError> {
255 debug!("Getting probe info for session: {}", args.session_id);
256
257 let session_arc = {
259 let sessions = self.sessions.read().await;
260 match sessions.get(&args.session_id) {
261 Some(session) => session.clone(),
262 None => {
263 let error_msg = format!("❌ Session '{}' not found\n\nUse 'connect' to establish a debug session first", args.session_id);
264 return Err(McpError::internal_error(error_msg, None));
265 }
266 }
267 };
268
269 let duration_minutes = (chrono::Utc::now() - session_arc.created_at).num_seconds() as f64 / 60.0;
271
272 let message = format!(
273 "📊 Debug Session Information\n\n\
274 Probe Information:\n\
275 - Identifier: {}\n\
276 - Connected: true\n\n\
277 Target Information:\n\
278 - Chip: {}\n\n\
279 Session Status:\n\
280 - Session ID: {}\n\
281 - Created: {}\n\
282 - Duration: {:.1} minutes\n\n\
283 Session is active and ready for operations.",
284 session_arc.probe_identifier,
285 session_arc.target_chip,
286 args.session_id,
287 session_arc.created_at.format("%Y-%m-%d %H:%M:%S UTC"),
288 duration_minutes
289 );
290
291 info!("Retrieved probe info for session: {}", args.session_id);
292 Ok(CallToolResult::success(vec![Content::text(message)]))
293 }
294
295 #[tool(description = "Halt the target CPU execution")]
300 async fn halt(&self, Parameters(args): Parameters<HaltArgs>) -> Result<CallToolResult, McpError> {
301 debug!("Halting target for session: {}", args.session_id);
302
303 let session_arc = {
304 let sessions = self.sessions.read().await;
305 match sessions.get(&args.session_id) {
306 Some(session) => session.clone(),
307 None => {
308 let error_msg = format!("❌ Session '{}' not found\n\nUse 'connect' to establish a debug session first", args.session_id);
309 return Err(McpError::internal_error(error_msg, None));
310 }
311 }
312 };
313
314 {
316 let mut session = session_arc.session.lock().await;
317 let mut core = match session.core(0) {
318 Ok(core) => core,
319 Err(e) => {
320 error!("Failed to get core for session {}: {}", args.session_id, e);
321 return Err(McpError::internal_error(format!("Failed to get core: {}", e), None));
322 }
323 };
324
325 match core.halt(std::time::Duration::from_millis(1000)) {
326 Ok(_) => {
327 match core.status() {
329 Ok(_status) => {
330 let pc = core.read_core_reg(core.program_counter()).map(|v: RegisterValue| v.try_into().unwrap_or(0u32)).unwrap_or(0);
331 let sp = core.read_core_reg(core.stack_pointer()).map(|v: RegisterValue| v.try_into().unwrap_or(0u32)).unwrap_or(0);
332
333 let message = format!(
334 "✅ Target halted successfully!\n\n\
335 Session ID: {}\n\
336 PC: 0x{:08X}\n\
337 SP: 0x{:08X}\n\
338 State: Halted\n",
339 args.session_id, pc, sp
340 );
341
342 info!("Halt completed for session: {}", args.session_id);
343 Ok(CallToolResult::success(vec![Content::text(message)]))
344 }
345 Err(e) => {
346 warn!("Failed to get status after halt: {}", e);
347 let message = format!(
348 "✅ Target halted successfully!\n\n\
349 Session ID: {}\n\
350 State: Halted\n",
351 args.session_id
352 );
353 Ok(CallToolResult::success(vec![Content::text(message)]))
354 }
355 }
356 }
357 Err(e) => {
358 error!("Failed to halt target for session {}: {}", args.session_id, e);
359 Err(McpError::internal_error(format!("Failed to halt target: {}", e), None))
360 }
361 }
362 }
363 }
364
365 #[tool(description = "Resume target CPU execution")]
366 async fn run(&self, Parameters(args): Parameters<RunArgs>) -> Result<CallToolResult, McpError> {
367 debug!("Running target for session: {}", args.session_id);
368
369 let session_arc = {
370 let sessions = self.sessions.read().await;
371 match sessions.get(&args.session_id) {
372 Some(session) => session.clone(),
373 None => {
374 let error_msg = format!("❌ Session '{}' not found\n\nUse 'connect' to establish a debug session first", args.session_id);
375 return Err(McpError::internal_error(error_msg, None));
376 }
377 }
378 };
379
380 {
382 let mut session = session_arc.session.lock().await;
383 let mut core = match session.core(0) {
384 Ok(core) => core,
385 Err(e) => {
386 error!("Failed to get core for session {}: {}", args.session_id, e);
387 return Err(McpError::internal_error(format!("Failed to get core: {}", e), None));
388 }
389 };
390
391 match core.run() {
392 Ok(_) => {
393 let message = format!(
394 "✅ Target resumed execution successfully!\n\n\
395 Session ID: {}\n\
396 Status: Running\n\n\
397 The target is now executing code. Use 'halt' to stop execution.",
398 args.session_id
399 );
400
401 info!("Run completed for session: {}", args.session_id);
402 Ok(CallToolResult::success(vec![Content::text(message)]))
403 }
404 Err(e) => {
405 error!("Failed to run target for session {}: {}", args.session_id, e);
406 Err(McpError::internal_error(format!("Failed to run target: {}", e), None))
407 }
408 }
409 }
410 }
411
412 #[tool(description = "Reset the target CPU")]
413 async fn reset(&self, Parameters(args): Parameters<ResetArgs>) -> Result<CallToolResult, McpError> {
414 debug!("Resetting target for session: {}", args.session_id);
415
416 let session_arc = {
417 let sessions = self.sessions.read().await;
418 match sessions.get(&args.session_id) {
419 Some(session) => session.clone(),
420 None => {
421 let error_msg = format!("❌ Session '{}' not found\n\nUse 'connect' to establish a debug session first", args.session_id);
422 return Err(McpError::internal_error(error_msg, None));
423 }
424 }
425 };
426
427 {
429 let mut session = session_arc.session.lock().await;
430 let mut core = match session.core(0) {
431 Ok(core) => core,
432 Err(e) => {
433 error!("Failed to get core for session {}: {}", args.session_id, e);
434 return Err(McpError::internal_error(format!("Failed to get core: {}", e), None));
435 }
436 };
437
438 match core.reset() {
439 Ok(_) => {
440 if args.halt_after_reset {
441 match core.halt(std::time::Duration::from_millis(1000)) {
442 Ok(_) => {},
443 Err(e) => warn!("Failed to halt after reset: {}", e),
444 }
445 }
446
447 let pc = core.read_core_reg(core.program_counter()).map(|v: RegisterValue| v.try_into().unwrap_or(0u32)).unwrap_or(0);
448 let sp = core.read_core_reg(core.stack_pointer()).map(|v: RegisterValue| v.try_into().unwrap_or(0u32)).unwrap_or(0);
449
450 let message = format!(
451 "✅ Target reset completed successfully!\n\n\
452 Session ID: {}\n\
453 Reset type: {}\n\
454 Halted after reset: {}\n\
455 PC: 0x{:08X}\n\
456 SP: 0x{:08X}\n\
457 State: {}\n",
458 args.session_id,
459 args.reset_type,
460 args.halt_after_reset,
461 pc, sp,
462 if args.halt_after_reset { "Halted" } else { "Running" }
463 );
464
465 info!("Reset completed for session: {}", args.session_id);
466 Ok(CallToolResult::success(vec![Content::text(message)]))
467 }
468 Err(e) => {
469 error!("Failed to reset target for session {}: {}", args.session_id, e);
470 Err(McpError::internal_error(format!("Failed to reset target: {}", e), None))
471 }
472 }
473 }
474 }
475
476 #[tool(description = "Execute a single instruction step")]
477 async fn step(&self, Parameters(args): Parameters<StepArgs>) -> Result<CallToolResult, McpError> {
478 debug!("Single stepping target for session: {}", args.session_id);
479
480 let session_arc = {
481 let sessions = self.sessions.read().await;
482 match sessions.get(&args.session_id) {
483 Some(session) => session.clone(),
484 None => {
485 let error_msg = format!("❌ Session '{}' not found\n\nUse 'connect' to establish a debug session first", args.session_id);
486 return Err(McpError::internal_error(error_msg, None));
487 }
488 }
489 };
490
491 {
493 let mut session = session_arc.session.lock().await;
494 let mut core = match session.core(0) {
495 Ok(core) => core,
496 Err(e) => {
497 error!("Failed to get core for session {}: {}", args.session_id, e);
498 return Err(McpError::internal_error(format!("Failed to get core: {}", e), None));
499 }
500 };
501
502 match core.step() {
503 Ok(_) => {
504 let pc = core.read_core_reg(core.program_counter()).map(|v: RegisterValue| v.try_into().unwrap_or(0u32)).unwrap_or(0);
505 let sp = core.read_core_reg(core.stack_pointer()).map(|v: RegisterValue| v.try_into().unwrap_or(0u32)).unwrap_or(0);
506
507 let message = format!(
508 "✅ Single step completed successfully!\n\n\
509 Session ID: {}\n\
510 PC: 0x{:08X}\n\
511 SP: 0x{:08X}\n\
512 State: Halted\n",
513 args.session_id, pc, sp
514 );
515
516 info!("Step completed for session: {}", args.session_id);
517 Ok(CallToolResult::success(vec![Content::text(message)]))
518 }
519 Err(e) => {
520 error!("Failed to step target for session {}: {}", args.session_id, e);
521 Err(McpError::internal_error(format!("Failed to step target: {}", e), None))
522 }
523 }
524 }
525 }
526
527 #[tool(description = "Get current status of the target CPU and debug session")]
528 async fn get_status(&self, Parameters(args): Parameters<GetStatusArgs>) -> Result<CallToolResult, McpError> {
529 debug!("Getting status for session: {}", args.session_id);
530
531 let session_arc = {
532 let sessions = self.sessions.read().await;
533 match sessions.get(&args.session_id) {
534 Some(session) => session.clone(),
535 None => {
536 let error_msg = format!("❌ Session '{}' not found\n\nUse 'connect' to establish a debug session first", args.session_id);
537 return Err(McpError::internal_error(error_msg, None));
538 }
539 }
540 };
541
542 {
544 let mut session = session_arc.session.lock().await;
545 let mut core = match session.core(0) {
546 Ok(core) => core,
547 Err(e) => {
548 error!("Failed to get core for session {}: {}", args.session_id, e);
549 return Err(McpError::internal_error(format!("Failed to get core: {}", e), None));
550 }
551 };
552
553 match core.status() {
554 Ok(status) => {
555 let pc = core.read_core_reg(core.program_counter()).map(|v: RegisterValue| v.try_into().unwrap_or(0u32)).unwrap_or(0);
556 let sp = core.read_core_reg(core.stack_pointer()).map(|v: RegisterValue| v.try_into().unwrap_or(0u32)).unwrap_or(0);
557
558 let is_halted = matches!(status, CoreStatus::Halted(_));
559 let halt_reason = match status {
560 CoreStatus::Halted(reason) => format!("{:?}", reason),
561 CoreStatus::Running => "N/A".to_string(),
562 _ => "Unknown".to_string(),
563 };
564
565 let message = format!(
566 "📊 Debug Session Status\n\n\
567 Core Information:\n\
568 - PC: 0x{:08X}\n\
569 - SP: 0x{:08X}\n\
570 - State: {}\n\
571 - Halt reason: {}\n\n\
572 Session Information:\n\
573 - ID: {}\n\
574 - Connected: true\n\
575 - Target: {}\n\
576 - Probe: {}\n\
577 - Duration: {:.1} minutes\n",
578 pc, sp,
579 if is_halted { "Halted" } else { "Running" },
580 halt_reason,
581 args.session_id,
582 session_arc.target_chip,
583 session_arc.probe_identifier,
584 (chrono::Utc::now() - session_arc.created_at).num_seconds() as f64 / 60.0
585 );
586
587 Ok(CallToolResult::success(vec![Content::text(message)]))
588 }
589 Err(e) => {
590 error!("Failed to get core status for session {}: {}", args.session_id, e);
591 Err(McpError::internal_error(format!("Failed to get core status: {}", e), None))
592 }
593 }
594 }
595 }
596
597 #[tool(description = "Read memory from the target")]
602 async fn read_memory(&self, Parameters(args): Parameters<ReadMemoryArgs>) -> Result<CallToolResult, McpError> {
603 debug!("Reading memory for session: {} at address {}", args.session_id, args.address);
604
605 let address = match parse_address(&args.address) {
607 Ok(addr) => addr,
608 Err(e) => {
609 error!("Invalid address '{}': {}", args.address, e);
610 return Err(McpError::internal_error(format!("Invalid address '{}': {}", args.address, e), None));
611 }
612 };
613
614 let session_arc = {
615 let sessions = self.sessions.read().await;
616 match sessions.get(&args.session_id) {
617 Some(session) => session.clone(),
618 None => {
619 let error_msg = format!("❌ Session '{}' not found\n\nUse 'connect' to establish a debug session first", args.session_id);
620 return Err(McpError::internal_error(error_msg, None));
621 }
622 }
623 };
624
625 {
627 let mut session = session_arc.session.lock().await;
628 let mut core = match session.core(0) {
629 Ok(core) => core,
630 Err(e) => {
631 error!("Failed to get core for session {}: {}", args.session_id, e);
632 return Err(McpError::internal_error(format!("Failed to get core: {}", e), None));
633 }
634 };
635
636 let mut data = vec![0u8; args.size as usize];
637 match core.read(address, &mut data) {
638 Ok(_) => {
639 debug!("Read {} bytes from address 0x{:08X}", data.len(), address);
640
641 let formatted_data = format_memory_data(&data, &args.format, address);
642 let message = format!(
643 "📖 Memory read completed successfully!\n\n\
644 Session ID: {}\n\
645 Address: 0x{:08X}\n\
646 Size: {} bytes\n\
647 Format: {}\n\n\
648 Data:\n{}",
649 args.session_id, address, args.size, args.format, formatted_data
650 );
651
652 info!("Memory read completed for session: {}", args.session_id);
653 Ok(CallToolResult::success(vec![Content::text(message)]))
654 }
655 Err(e) => {
656 error!("Failed to read memory for session {}: {}", args.session_id, e);
657 Err(McpError::internal_error(format!("Failed to read memory: {}", e), None))
658 }
659 }
660 }
661 }
662
663 #[tool(description = "Write memory to the target")]
664 async fn write_memory(&self, Parameters(args): Parameters<WriteMemoryArgs>) -> Result<CallToolResult, McpError> {
665 debug!("Writing memory for session: {} at address {}", args.session_id, args.address);
666
667 let address = match parse_address(&args.address) {
669 Ok(addr) => addr,
670 Err(e) => {
671 error!("Invalid address '{}': {}", args.address, e);
672 return Err(McpError::internal_error(format!("Invalid address '{}': {}", args.address, e), None));
673 }
674 };
675
676 let data = match parse_data(&args.data, &args.format) {
678 Ok(data) => data,
679 Err(e) => {
680 error!("Invalid data '{}': {}", args.data, e);
681 return Err(McpError::internal_error(format!("Invalid data '{}': {}", args.data, e), None));
682 }
683 };
684
685 let session_arc = {
686 let sessions = self.sessions.read().await;
687 match sessions.get(&args.session_id) {
688 Some(session) => session.clone(),
689 None => {
690 let error_msg = format!("❌ Session '{}' not found\n\nUse 'connect' to establish a debug session first", args.session_id);
691 return Err(McpError::internal_error(error_msg, None));
692 }
693 }
694 };
695
696 {
698 let mut session = session_arc.session.lock().await;
699 let mut core = match session.core(0) {
700 Ok(core) => core,
701 Err(e) => {
702 error!("Failed to get core for session {}: {}", args.session_id, e);
703 return Err(McpError::internal_error(format!("Failed to get core: {}", e), None));
704 }
705 };
706
707 match core.write(address, &data) {
708 Ok(_) => {
709 let message = format!(
710 "✏️ Memory write completed successfully!\n\n\
711 Session ID: {}\n\
712 Address: 0x{:08X}\n\
713 Data: {}\n\
714 Format: {}\n\
715 Bytes written: {}",
716 args.session_id, address, args.data, args.format, data.len()
717 );
718
719 info!("Memory write completed for session: {}", args.session_id);
720 Ok(CallToolResult::success(vec![Content::text(message)]))
721 }
722 Err(e) => {
723 error!("Failed to write memory for session {}: {}", args.session_id, e);
724 Err(McpError::internal_error(format!("Failed to write memory: {}", e), None))
725 }
726 }
727 }
728 }
729
730 #[tool(description = "Set a breakpoint at the specified address")]
735 async fn set_breakpoint(&self, Parameters(args): Parameters<SetBreakpointArgs>) -> Result<CallToolResult, McpError> {
736 debug!("Setting breakpoint for session: {} at address {}", args.session_id, args.address);
737
738 let address = match parse_address(&args.address) {
740 Ok(addr) => addr,
741 Err(e) => {
742 error!("Invalid address '{}': {}", args.address, e);
743 return Err(McpError::internal_error(format!("Invalid address '{}': {}", args.address, e), None));
744 }
745 };
746
747 let session_arc = {
748 let sessions = self.sessions.read().await;
749 match sessions.get(&args.session_id) {
750 Some(session) => session.clone(),
751 None => {
752 let error_msg = format!("❌ Session '{}' not found\n\nUse 'connect' to establish a debug session first", args.session_id);
753 return Err(McpError::internal_error(error_msg, None));
754 }
755 }
756 };
757
758 {
760 let mut session = session_arc.session.lock().await;
761 let mut core = match session.core(0) {
762 Ok(core) => core,
763 Err(e) => {
764 error!("Failed to get core for session {}: {}", args.session_id, e);
765 return Err(McpError::internal_error(format!("Failed to get core: {}", e), None));
766 }
767 };
768
769 match core.set_hw_breakpoint(address) {
770 Ok(_) => {
771 let message = format!(
772 "🎯 Breakpoint set successfully!\n\n\
773 Session ID: {}\n\
774 Address: 0x{:08X}\n\
775 Type: Hardware breakpoint\n\n\
776 The target will halt when execution reaches this address.",
777 args.session_id, address
778 );
779
780 info!("Breakpoint set for session: {} at 0x{:08X}", args.session_id, address);
781 Ok(CallToolResult::success(vec![Content::text(message)]))
782 }
783 Err(e) => {
784 error!("Failed to set breakpoint for session {}: {}", args.session_id, e);
785 Err(McpError::internal_error(format!("Failed to set breakpoint: {}", e), None))
786 }
787 }
788 }
789 }
790
791 #[tool(description = "Clear a breakpoint at the specified address")]
792 async fn clear_breakpoint(&self, Parameters(args): Parameters<ClearBreakpointArgs>) -> Result<CallToolResult, McpError> {
793 debug!("Clearing breakpoint for session: {} at address {}", args.session_id, args.address);
794
795 let address = match parse_address(&args.address) {
797 Ok(addr) => addr,
798 Err(e) => {
799 error!("Invalid address '{}': {}", args.address, e);
800 return Err(McpError::internal_error(format!("Invalid address '{}': {}", args.address, e), None));
801 }
802 };
803
804 let session_arc = {
805 let sessions = self.sessions.read().await;
806 match sessions.get(&args.session_id) {
807 Some(session) => session.clone(),
808 None => {
809 let error_msg = format!("❌ Session '{}' not found\n\nUse 'connect' to establish a debug session first", args.session_id);
810 return Err(McpError::internal_error(error_msg, None));
811 }
812 }
813 };
814
815 {
817 let mut session = session_arc.session.lock().await;
818 let mut core = match session.core(0) {
819 Ok(core) => core,
820 Err(e) => {
821 error!("Failed to get core for session {}: {}", args.session_id, e);
822 return Err(McpError::internal_error(format!("Failed to get core: {}", e), None));
823 }
824 };
825
826 match core.clear_hw_breakpoint(address) {
827 Ok(_) => {
828 let message = format!(
829 "🎯 Breakpoint cleared successfully!\n\n\
830 Session ID: {}\n\
831 Address: 0x{:08X}\n\n\
832 The breakpoint has been removed.",
833 args.session_id, address
834 );
835
836 info!("Breakpoint cleared for session: {} at 0x{:08X}", args.session_id, address);
837 Ok(CallToolResult::success(vec![Content::text(message)]))
838 }
839 Err(e) => {
840 error!("Failed to clear breakpoint for session {}: {}", args.session_id, e);
841 Err(McpError::internal_error(format!("Failed to clear breakpoint: {}", e), None))
842 }
843 }
844 }
845 }
846
847 #[tool(description = "Attach to RTT (Real-Time Transfer) for communication with target")]
852 async fn rtt_attach(&self, Parameters(args): Parameters<RttAttachArgs>) -> Result<CallToolResult, McpError> {
853 debug!("Attaching RTT for session: {}", args.session_id);
854
855 let session_arc = {
857 let sessions = self.sessions.read().await;
858 match sessions.get(&args.session_id) {
859 Some(session) => session.clone(),
860 None => {
861 let error_msg = format!("❌ Session '{}' not found\n\nUse 'connect' to establish a debug session first", args.session_id);
862 return Err(McpError::internal_error(error_msg, None));
863 }
864 }
865 };
866
867 let control_block_address = if let Some(addr_str) = args.control_block_address {
869 match parse_address(&addr_str) {
870 Ok(addr) => Some(addr),
871 Err(e) => {
872 let error_msg = format!("❌ Invalid control block address '{}': {}", addr_str, e);
873 return Err(McpError::internal_error(error_msg, None));
874 }
875 }
876 } else {
877 None
878 };
879
880 let memory_ranges = if let Some(ranges) = args.memory_ranges {
882 let mut parsed_ranges = Vec::new();
883 for range in ranges {
884 let start = parse_address(&range.start).map_err(|e| {
885 McpError::internal_error(format!("Invalid start address '{}': {}", range.start, e), None)
886 })?;
887 let end = parse_address(&range.end).map_err(|e| {
888 McpError::internal_error(format!("Invalid end address '{}': {}", range.end, e), None)
889 })?;
890 parsed_ranges.push((start, end));
891 }
892 Some(parsed_ranges)
893 } else {
894 None
895 };
896
897 {
899 let mut rtt_manager = session_arc.rtt_manager.lock().await;
900 match rtt_manager.attach(session_arc.session.clone(), control_block_address, memory_ranges).await {
901 Ok(_) => {
902 let up_channels = rtt_manager.up_channel_count();
903 let down_channels = rtt_manager.down_channel_count();
904
905 let message = format!(
906 "✅ RTT attached successfully!\n\n\
907 Session ID: {}\n\
908 Up Channels (Target→Host): {}\n\
909 Down Channels (Host→Target): {}\n\n\
910 RTT is now ready for real-time communication with the target.\n\
911 Use 'rtt_read' to read from target and 'rtt_write' to send data to target.",
912 args.session_id, up_channels, down_channels
913 );
914
915 info!("RTT attached successfully for session: {}", args.session_id);
916 Ok(CallToolResult::success(vec![Content::text(message)]))
917 }
918 Err(e) => {
919 error!("Failed to attach RTT for session {}: {}", args.session_id, e);
920 let error_msg = format!(
921 "❌ Failed to attach RTT\n\n\
922 Session ID: {}\n\
923 Error: {}\n\n\
924 Suggestions:\n\
925 - Ensure the target firmware has RTT enabled and initialized\n\
926 - Check that the target is halted\n\
927 - Verify memory ranges if specified\n\
928 - Try different control block address if known",
929 args.session_id, e
930 );
931 Err(McpError::internal_error(error_msg, None))
932 }
933 }
934 }
935 }
936
937 #[tool(description = "Detach from RTT communication")]
938 async fn rtt_detach(&self, Parameters(args): Parameters<RttDetachArgs>) -> Result<CallToolResult, McpError> {
939 debug!("Detaching RTT for session: {}", args.session_id);
940
941 let session_arc = {
943 let sessions = self.sessions.read().await;
944 match sessions.get(&args.session_id) {
945 Some(session) => session.clone(),
946 None => {
947 let error_msg = format!("❌ Session '{}' not found\n\nUse 'connect' to establish a debug session first", args.session_id);
948 return Err(McpError::internal_error(error_msg, None));
949 }
950 }
951 };
952
953 {
955 let mut rtt_manager = session_arc.rtt_manager.lock().await;
956 match rtt_manager.detach().await {
957 Ok(_) => {
958 let message = format!(
959 "✅ RTT detached successfully\n\n\
960 Session ID: {}\n\n\
961 RTT communication has been closed.",
962 args.session_id
963 );
964
965 info!("RTT detached successfully for session: {}", args.session_id);
966 Ok(CallToolResult::success(vec![Content::text(message)]))
967 }
968 Err(e) => {
969 error!("Failed to detach RTT for session {}: {}", args.session_id, e);
970 let error_msg = format!("❌ Failed to detach RTT: {}", e);
971 Err(McpError::internal_error(error_msg, None))
972 }
973 }
974 }
975 }
976
977 #[tool(description = "Read data from RTT up channel (target to host)")]
978 async fn rtt_read(&self, Parameters(args): Parameters<RttReadArgs>) -> Result<CallToolResult, McpError> {
979 debug!("Reading from RTT channel {} for session: {}", args.channel, args.session_id);
980
981 let session_arc = {
983 let sessions = self.sessions.read().await;
984 match sessions.get(&args.session_id) {
985 Some(session) => session.clone(),
986 None => {
987 let error_msg = format!("❌ Session '{}' not found\n\nUse 'connect' to establish a debug session first", args.session_id);
988 return Err(McpError::internal_error(error_msg, None));
989 }
990 }
991 };
992
993 {
995 let mut rtt_manager = session_arc.rtt_manager.lock().await;
996 if !rtt_manager.is_attached() {
997 let error_msg = format!("❌ RTT not attached for session '{}'\n\nUse 'rtt_attach' first", args.session_id);
998 return Err(McpError::internal_error(error_msg, None));
999 }
1000
1001 match rtt_manager.read_channel(args.channel).await {
1002 Ok(data) => {
1003 let data_len = data.len();
1004 let data_str = if data.is_empty() {
1005 "No data available".to_string()
1006 } else {
1007 match String::from_utf8(data.clone()) {
1009 Ok(text) => {
1010 if text.chars().all(|c| c.is_ascii_graphic() || c.is_ascii_whitespace()) {
1011 format!("Text: {}", text)
1012 } else {
1013 format!("Mixed: {} (hex: {})", text, hex::encode(&data))
1014 }
1015 }
1016 Err(_) => format!("Binary data (hex): {}", hex::encode(&data))
1017 }
1018 };
1019
1020 let message = format!(
1021 "📥 RTT Read from Channel {}\n\n\
1022 Session ID: {}\n\
1023 Bytes Read: {}\n\n\
1024 Data:\n{}",
1025 args.channel, args.session_id, data_len, data_str
1026 );
1027
1028 debug!("Read {} bytes from RTT channel {} for session: {}", data_len, args.channel, args.session_id);
1029 Ok(CallToolResult::success(vec![Content::text(message)]))
1030 }
1031 Err(e) => {
1032 error!("Failed to read from RTT channel {} for session {}: {}", args.channel, args.session_id, e);
1033 let error_msg = format!(
1034 "❌ Failed to read from RTT channel {}\n\n\
1035 Session ID: {}\n\
1036 Error: {}",
1037 args.channel, args.session_id, e
1038 );
1039 Err(McpError::internal_error(error_msg, None))
1040 }
1041 }
1042 }
1043 }
1044
1045 #[tool(description = "Write data to RTT down channel (host to target)")]
1046 async fn rtt_write(&self, Parameters(args): Parameters<RttWriteArgs>) -> Result<CallToolResult, McpError> {
1047 debug!("Writing to RTT channel {} for session: {}", args.channel, args.session_id);
1048
1049 let session_arc = {
1051 let sessions = self.sessions.read().await;
1052 match sessions.get(&args.session_id) {
1053 Some(session) => session.clone(),
1054 None => {
1055 let error_msg = format!("❌ Session '{}' not found\n\nUse 'connect' to establish a debug session first", args.session_id);
1056 return Err(McpError::internal_error(error_msg, None));
1057 }
1058 }
1059 };
1060
1061 let data_bytes = match args.encoding.as_str() {
1063 "utf8" => args.data.as_bytes().to_vec(),
1064 "hex" => {
1065 match hex::decode(&args.data) {
1066 Ok(bytes) => bytes,
1067 Err(e) => {
1068 let error_msg = format!("❌ Invalid hex data '{}': {}", args.data, e);
1069 return Err(McpError::internal_error(error_msg, None));
1070 }
1071 }
1072 }
1073 "binary" => {
1074 let binary_str = args.data.replace(' ', "");
1076 if binary_str.len() % 8 != 0 {
1077 let error_msg = format!("❌ Binary data must be multiple of 8 bits: '{}'", args.data);
1078 return Err(McpError::internal_error(error_msg, None));
1079 }
1080
1081 let mut bytes = Vec::new();
1082 for chunk in binary_str.chars().collect::<Vec<_>>().chunks(8) {
1083 let byte_str: String = chunk.iter().collect();
1084 match u8::from_str_radix(&byte_str, 2) {
1085 Ok(byte) => bytes.push(byte),
1086 Err(e) => {
1087 let error_msg = format!("❌ Invalid binary byte '{}': {}", byte_str, e);
1088 return Err(McpError::internal_error(error_msg, None));
1089 }
1090 }
1091 }
1092 bytes
1093 }
1094 _ => {
1095 let error_msg = format!("❌ Unsupported encoding '{}'. Use 'utf8', 'hex', or 'binary'", args.encoding);
1096 return Err(McpError::internal_error(error_msg, None));
1097 }
1098 };
1099
1100 {
1102 let mut rtt_manager = session_arc.rtt_manager.lock().await;
1103 if !rtt_manager.is_attached() {
1104 let error_msg = format!("❌ RTT not attached for session '{}'\n\nUse 'rtt_attach' first", args.session_id);
1105 return Err(McpError::internal_error(error_msg, None));
1106 }
1107
1108 match rtt_manager.write_channel(args.channel, &data_bytes).await {
1109 Ok(bytes_written) => {
1110 let message = format!(
1111 "📤 RTT Write to Channel {}\n\n\
1112 Session ID: {}\n\
1113 Data: {}\n\
1114 Encoding: {}\n\
1115 Bytes Written: {}\n\n\
1116 Data sent successfully to target.",
1117 args.channel, args.session_id, args.data, args.encoding, bytes_written
1118 );
1119
1120 info!("Wrote {} bytes to RTT channel {} for session: {}", bytes_written, args.channel, args.session_id);
1121 Ok(CallToolResult::success(vec![Content::text(message)]))
1122 }
1123 Err(e) => {
1124 error!("Failed to write to RTT channel {} for session {}: {}", args.channel, args.session_id, e);
1125 let error_msg = format!(
1126 "❌ Failed to write to RTT channel {}\n\n\
1127 Session ID: {}\n\
1128 Error: {}",
1129 args.channel, args.session_id, e
1130 );
1131 Err(McpError::internal_error(error_msg, None))
1132 }
1133 }
1134 }
1135 }
1136
1137 #[tool(description = "List available RTT channels")]
1138 async fn rtt_channels(&self, Parameters(args): Parameters<RttChannelsArgs>) -> Result<CallToolResult, McpError> {
1139 debug!("Listing RTT channels for session: {}", args.session_id);
1140
1141 let session_arc = {
1143 let sessions = self.sessions.read().await;
1144 match sessions.get(&args.session_id) {
1145 Some(session) => session.clone(),
1146 None => {
1147 let error_msg = format!("❌ Session '{}' not found\n\nUse 'connect' to establish a debug session first", args.session_id);
1148 return Err(McpError::internal_error(error_msg, None));
1149 }
1150 }
1151 };
1152
1153 {
1155 let rtt_manager = session_arc.rtt_manager.lock().await;
1156 if !rtt_manager.is_attached() {
1157 let error_msg = format!("❌ RTT not attached for session '{}'\n\nUse 'rtt_attach' first", args.session_id);
1158 return Err(McpError::internal_error(error_msg, None));
1159 }
1160
1161 let channels = rtt_manager.get_channels();
1162 let channel_count = channels.len();
1163
1164 if channels.is_empty() {
1165 let message = format!(
1166 "📋 RTT Channels\n\n\
1167 Session ID: {}\n\n\
1168 No RTT channels available.",
1169 args.session_id
1170 );
1171 return Ok(CallToolResult::success(vec![Content::text(message)]));
1172 }
1173
1174 let mut message = format!("📋 RTT Channels\n\nSession ID: {}\n\n", args.session_id);
1175
1176 let mut up_channels = Vec::new();
1178 let mut down_channels = Vec::new();
1179
1180 for channel in &channels {
1181 match channel.direction {
1182 crate::rtt::ChannelDirection::Up => up_channels.push(channel),
1183 crate::rtt::ChannelDirection::Down => down_channels.push(channel),
1184 }
1185 }
1186
1187 if !up_channels.is_empty() {
1188 message.push_str("📥 Up Channels (Target → Host):\n");
1189 for channel in up_channels {
1190 message.push_str(&format!(
1191 " {}. {} (Size: {} bytes, Mode: {})\n",
1192 channel.id, channel.name, channel.buffer_size, channel.mode
1193 ));
1194 }
1195 message.push('\n');
1196 }
1197
1198 if !down_channels.is_empty() {
1199 message.push_str("📤 Down Channels (Host → Target):\n");
1200 for channel in down_channels {
1201 message.push_str(&format!(
1202 " {}. {} (Size: {} bytes, Mode: {})\n",
1203 channel.id, channel.name, channel.buffer_size, channel.mode
1204 ));
1205 }
1206 }
1207
1208 info!("Listed {} RTT channels for session: {}", channel_count, args.session_id);
1209 Ok(CallToolResult::success(vec![Content::text(message)]))
1210 }
1211 }
1212
1213 #[tool(description = "Erase flash memory sectors or entire chip")]
1218 async fn flash_erase(&self, Parameters(args): Parameters<FlashEraseArgs>) -> Result<CallToolResult, McpError> {
1219 debug!("Flash erase for session: {}, type: {}", args.session_id, args.erase_type);
1220
1221 let session_arc = {
1222 let sessions = self.sessions.read().await;
1223 match sessions.get(&args.session_id) {
1224 Some(session) => session.clone(),
1225 None => {
1226 let error_msg = format!("❌ Session '{}' not found\n\nUse 'connect' to establish a debug session first", args.session_id);
1227 return Err(McpError::internal_error(error_msg, None));
1228 }
1229 }
1230 };
1231
1232 let erase_type = match args.erase_type.as_str() {
1234 "all" => crate::flash::EraseType::All,
1235 "sectors" => {
1236 let address = match args.address {
1237 Some(addr_str) => parse_address(&addr_str).map_err(|e| McpError::internal_error(e, None))?,
1238 None => return Err(McpError::internal_error("Address required for sector erase".to_string(), None)),
1239 };
1240 let size = match args.size {
1241 Some(sz) => sz as usize,
1242 None => return Err(McpError::internal_error("Size required for sector erase".to_string(), None)),
1243 };
1244 crate::flash::EraseType::Sectors { address, size }
1245 }
1246 _ => return Err(McpError::internal_error(format!("Invalid erase type: {}", args.erase_type), None)),
1247 };
1248
1249 {
1251 let mut session = session_arc.session.lock().await;
1252 match crate::flash::FlashManager::erase_flash(&mut session, erase_type).await {
1253 Ok(result) => {
1254 let message = format!(
1255 "✅ Flash erase completed successfully!\n\n\
1256 Session ID: {}\n\
1257 Erase Type: {}\n\
1258 Duration: {}ms\n\
1259 {}\n\n\
1260 Flash memory has been erased and is ready for programming.",
1261 args.session_id,
1262 args.erase_type,
1263 result.erase_time_ms,
1264 match result.sectors_erased {
1265 Some(count) => format!("Sectors Erased: {}", count),
1266 None => "Full chip erased".to_string(),
1267 }
1268 );
1269
1270 info!("Flash erase completed for session: {}", args.session_id);
1271 Ok(CallToolResult::success(vec![Content::text(message)]))
1272 }
1273 Err(e) => {
1274 error!("Flash erase failed for session {}: {}", args.session_id, e);
1275 let error_msg = format!(
1276 "❌ Flash erase failed\n\n\
1277 Session ID: {}\n\
1278 Error: {}\n\n\
1279 Suggestions:\n\
1280 - Check if flash is write-protected\n\
1281 - Ensure target is halted\n\
1282 - Verify flash address range",
1283 args.session_id, e
1284 );
1285 Err(McpError::internal_error(error_msg, None))
1286 }
1287 }
1288 }
1289 }
1290
1291 #[tool(description = "Program file to flash memory (supports ELF, HEX, BIN)")]
1292 async fn flash_program(&self, Parameters(args): Parameters<FlashProgramArgs>) -> Result<CallToolResult, McpError> {
1293 debug!("Flash program for session: {}, file: {}", args.session_id, args.file_path);
1294
1295 let session_arc = {
1296 let sessions = self.sessions.read().await;
1297 match sessions.get(&args.session_id) {
1298 Some(session) => session.clone(),
1299 None => {
1300 let error_msg = format!("❌ Session '{}' not found\n\nUse 'connect' to establish a debug session first", args.session_id);
1301 return Err(McpError::internal_error(error_msg, None));
1302 }
1303 }
1304 };
1305
1306 let file_path = std::path::Path::new(&args.file_path);
1308 let format = match args.format.as_str() {
1309 "auto" => crate::flash::FileFormat::Auto,
1310 "elf" => crate::flash::FileFormat::Elf,
1311 "hex" => crate::flash::FileFormat::Hex,
1312 "bin" => crate::flash::FileFormat::Bin,
1313 _ => return Err(McpError::internal_error(format!("Unsupported format: {}", args.format), None)),
1314 };
1315
1316 let base_address = if let Some(addr_str) = args.base_address {
1318 Some(parse_address(&addr_str).map_err(|e| McpError::internal_error(e, None))?)
1319 } else {
1320 None
1321 };
1322
1323 {
1325 let mut session = session_arc.session.lock().await;
1326 match crate::flash::FlashManager::program_file(&mut session, file_path, format, base_address).await {
1327 Ok(result) => {
1328 let message = format!(
1329 "✅ Flash programming completed successfully!\n\n\
1330 Session ID: {}\n\
1331 File: {}\n\
1332 Format: {}\n\
1333 Bytes Programmed: {}\n\
1334 Duration: {}ms\n\
1335 Verification: {}\n\n\
1336 Firmware has been programmed to flash memory.",
1337 args.session_id,
1338 args.file_path,
1339 args.format,
1340 result.bytes_programmed,
1341 result.programming_time_ms,
1342 match result.verification_result {
1343 Some(true) => "✅ Passed",
1344 Some(false) => "❌ Failed",
1345 None => "Not performed",
1346 }
1347 );
1348
1349 info!("Flash programming completed for session: {}", args.session_id);
1350 Ok(CallToolResult::success(vec![Content::text(message)]))
1351 }
1352 Err(e) => {
1353 error!("Flash programming failed for session {}: {}", args.session_id, e);
1354 let error_msg = format!(
1355 "❌ Flash programming failed\n\n\
1356 Session ID: {}\n\
1357 File: {}\n\
1358 Error: {}\n\n\
1359 Suggestions:\n\
1360 - Check file exists and is readable\n\
1361 - Verify file format is correct\n\
1362 - Ensure flash is erased first\n\
1363 - Check target memory map",
1364 args.session_id, args.file_path, e
1365 );
1366 Err(McpError::internal_error(error_msg, None))
1367 }
1368 }
1369 }
1370 }
1371
1372 #[tool(description = "Verify flash memory contents")]
1373 async fn flash_verify(&self, Parameters(args): Parameters<FlashVerifyArgs>) -> Result<CallToolResult, McpError> {
1374 debug!("Flash verify for session: {}", args.session_id);
1375
1376 let session_arc = {
1377 let sessions = self.sessions.read().await;
1378 match sessions.get(&args.session_id) {
1379 Some(session) => session.clone(),
1380 None => {
1381 let error_msg = format!("❌ Session '{}' not found\n\nUse 'connect' to establish a debug session first", args.session_id);
1382 return Err(McpError::internal_error(error_msg, None));
1383 }
1384 }
1385 };
1386
1387 let address = parse_address(&args.address).map_err(|e| McpError::internal_error(e, None))?;
1389
1390 let expected_data = if let Some(file_path) = &args.file_path {
1392 std::fs::read(file_path)
1394 .map_err(|e| McpError::internal_error(format!("Failed to read file {}: {}", file_path, e), None))?
1395 } else if let Some(hex_data) = &args.data {
1396 match parse_data(hex_data, "hex") {
1398 Ok(data) => data,
1399 Err(e) => return Err(McpError::internal_error(format!("Invalid hex data: {}", e), None)),
1400 }
1401 } else {
1402 return Err(McpError::internal_error("Either file_path or data must be provided".to_string(), None));
1403 };
1404
1405 let expected_data = if expected_data.len() > args.size as usize {
1407 &expected_data[..args.size as usize]
1408 } else {
1409 &expected_data
1410 };
1411
1412 {
1414 let mut session = session_arc.session.lock().await;
1415 match crate::flash::FlashManager::verify_flash(&mut session, expected_data, address).await {
1416 Ok(result) => {
1417 let message = if result.success {
1418 format!(
1419 "✅ Flash verification successful!\n\n\
1420 Session ID: {}\n\
1421 Address: 0x{:08X}\n\
1422 Bytes Verified: {}\n\n\
1423 All flash contents match expected data.",
1424 args.session_id, address, result.bytes_verified
1425 )
1426 } else {
1427 let mut message = format!(
1428 "❌ Flash verification failed!\n\n\
1429 Session ID: {}\n\
1430 Address: 0x{:08X}\n\
1431 Bytes Verified: {}\n\
1432 Mismatches: {}\n\n\
1433 First {} mismatches:\n",
1434 args.session_id, address, result.bytes_verified, result.mismatches.len(),
1435 std::cmp::min(10, result.mismatches.len())
1436 );
1437
1438 for (i, mismatch) in result.mismatches.iter().take(10).enumerate() {
1439 message.push_str(&format!(
1440 " {}. 0x{:08X}: expected 0x{:02X}, got 0x{:02X}\n",
1441 i + 1, mismatch.address, mismatch.expected, mismatch.actual
1442 ));
1443 }
1444
1445 if result.mismatches.len() > 10 {
1446 message.push_str(&format!(" ... and {} more mismatches\n", result.mismatches.len() - 10));
1447 }
1448
1449 message
1450 };
1451
1452 info!("Flash verification completed for session: {}", args.session_id);
1453 Ok(CallToolResult::success(vec![Content::text(message)]))
1454 }
1455 Err(e) => {
1456 error!("Flash verification failed for session {}: {}", args.session_id, e);
1457 let error_msg = format!(
1458 "❌ Flash verification error\n\n\
1459 Session ID: {}\n\
1460 Error: {}",
1461 args.session_id, e
1462 );
1463 Err(McpError::internal_error(error_msg, None))
1464 }
1465 }
1466 }
1467 }
1468
1469 #[tool(description = "Complete firmware deployment: erase, program, verify, run and attach RTT")]
1470 async fn run_firmware(&self, Parameters(args): Parameters<RunFirmwareArgs>) -> Result<CallToolResult, McpError> {
1471 debug!("Run firmware for session: {}, file: {}", args.session_id, args.file_path);
1472
1473 let session_arc = {
1474 let sessions = self.sessions.read().await;
1475 match sessions.get(&args.session_id) {
1476 Some(session) => session.clone(),
1477 None => {
1478 let error_msg = format!("❌ Session '{}' not found\n\nUse 'connect' to establish a debug session first", args.session_id);
1479 return Err(McpError::internal_error(error_msg, None));
1480 }
1481 }
1482 };
1483
1484 let mut status_messages = Vec::new();
1485 let start_time = std::time::Instant::now();
1486
1487 status_messages.push("🔄 Step 1/5: Erasing flash memory...".to_string());
1489 {
1490 let mut session = session_arc.session.lock().await;
1491 match crate::flash::FlashManager::erase_flash(&mut session, crate::flash::EraseType::All).await {
1492 Ok(_) => status_messages.push("✅ Flash erased successfully".to_string()),
1493 Err(e) => {
1494 let error_msg = format!("❌ Flash erase failed: {}", e);
1495 status_messages.push(error_msg.clone());
1496 return Err(McpError::internal_error(format!("{}\n\n{}", status_messages.join("\n"), error_msg), None));
1497 }
1498 }
1499 }
1500
1501 status_messages.push("🔄 Step 2/5: Programming firmware...".to_string());
1503 let format = match args.format.as_str() {
1504 "auto" => crate::flash::FileFormat::Auto,
1505 "elf" => crate::flash::FileFormat::Elf,
1506 "hex" => crate::flash::FileFormat::Hex,
1507 "bin" => crate::flash::FileFormat::Bin,
1508 _ => return Err(McpError::internal_error(format!("Unsupported format: {}", args.format), None)),
1509 };
1510
1511 {
1512 let mut session = session_arc.session.lock().await;
1513 match crate::flash::FlashManager::program_file(&mut session, std::path::Path::new(&args.file_path), format, None).await {
1514 Ok(result) => status_messages.push(format!("✅ Programmed {} bytes", result.bytes_programmed)),
1515 Err(e) => {
1516 let error_msg = format!("❌ Programming failed: {}", e);
1517 status_messages.push(error_msg.clone());
1518 return Err(McpError::internal_error(format!("{}\n\n{}", status_messages.join("\n"), error_msg), None));
1519 }
1520 }
1521 }
1522
1523 if args.reset_after_flash {
1525 status_messages.push("🔄 Step 3/5: Resetting target...".to_string());
1526 {
1527 let mut session = session_arc.session.lock().await;
1528 let mut core = match session.core(0) {
1529 Ok(core) => core,
1530 Err(e) => return Err(McpError::internal_error(format!("Failed to get core: {}", e), None)),
1531 };
1532
1533 match core.reset() {
1534 Ok(_) => {
1535 status_messages.push("✅ Target reset successfully".to_string());
1536 match core.run() {
1538 Ok(_) => status_messages.push("✅ Target running".to_string()),
1539 Err(e) => warn!("Failed to run after reset: {}", e),
1540 }
1541 }
1542 Err(e) => {
1543 let error_msg = format!("❌ Reset failed: {}", e);
1544 status_messages.push(error_msg.clone());
1545 return Err(McpError::internal_error(format!("{}\n\n{}", status_messages.join("\n"), error_msg), None));
1546 }
1547 }
1548 }
1549 }
1550
1551 if args.attach_rtt {
1553 status_messages.push("🔄 Step 4/5: Attaching RTT (probe-rs style)...".to_string());
1554
1555 info!("Allowing target firmware to fully initialize RTT control block...");
1557 tokio::time::sleep(tokio::time::Duration::from_millis(2000)).await; info!("Giving target additional time to initialize RTT control block...");
1561 tokio::time::sleep(tokio::time::Duration::from_millis(1000)).await;
1562
1563 let mut rtt_attached = false;
1565 let max_attempts = 8; let mut attempt = 1;
1567
1568 while attempt <= max_attempts && !rtt_attached {
1569 let delay_ms = 1000 + (attempt - 1) * 500;
1571 info!("RTT attach attempt {}/{}, waiting {}ms for RTT control block...", attempt, max_attempts, delay_ms);
1572 tokio::time::sleep(tokio::time::Duration::from_millis(delay_ms as u64)).await;
1573
1574 tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
1576
1577 let mut rtt_manager = session_arc.rtt_manager.lock().await;
1579 let rtt_result = match attempt {
1580 1..=2 => {
1581 debug!("RTT attempt {}: Using ELF symbol detection (probe-rs style)", attempt);
1583 rtt_manager.attach_with_elf(session_arc.session.clone(), std::path::Path::new(&args.file_path)).await
1584 }
1585 3..=5 => {
1586 debug!("RTT attempt {}: Using standard memory map scan", attempt);
1588 rtt_manager.attach(session_arc.session.clone(), None, None).await
1589 }
1590 6..=7 => {
1591 debug!("RTT attempt {}: Using STM32G4 specific memory ranges", attempt);
1593 let stm32g4_ranges = vec![
1594 (0x20000000, 0x20004000), (0x20004000, 0x20008000), (0x20008000, 0x2000A000), ];
1598 rtt_manager.attach(session_arc.session.clone(), None, Some(stm32g4_ranges)).await
1599 }
1600 _ => {
1601 let cb_addr = 0x20000000;
1603 debug!("RTT attempt {}: Using specific control block address 0x{:08X}", attempt, cb_addr);
1604 rtt_manager.attach(session_arc.session.clone(), Some(cb_addr), None).await
1605 }
1606 };
1607
1608 match rtt_result {
1609 Ok(_) => {
1610 let up_channels = rtt_manager.up_channel_count();
1611 let down_channels = rtt_manager.down_channel_count();
1612 status_messages.push(format!("✅ RTT attached on attempt {} ({} up, {} down channels)", attempt, up_channels, down_channels));
1613 info!("RTT successfully attached after {} attempts!", attempt);
1614 rtt_attached = true;
1615 }
1616 Err(e) => {
1617 if attempt == max_attempts {
1618 status_messages.push(format!("⚠️ RTT attach failed after {} attempts: {}", max_attempts, e));
1620 warn!("RTT attachment failed completely after {} attempts", max_attempts);
1621 } else {
1622 debug!("RTT attach attempt {}/{} failed: {}, retrying with different strategy...", attempt, max_attempts, e);
1623 }
1624 }
1625 }
1626 attempt += 1;
1627 }
1628
1629 if rtt_attached {
1631 info!("RTT connected successfully, allowing channel stabilization...");
1632 tokio::time::sleep(tokio::time::Duration::from_millis(500)).await;
1633 }
1634 }
1635
1636 status_messages.push("🔄 Step 5/5: Finalizing...".to_string());
1637 let elapsed = start_time.elapsed();
1638
1639 let message = format!(
1640 "🚀 Firmware deployment completed!\n\n\
1641 Session ID: {}\n\
1642 File: {}\n\
1643 Format: {}\n\
1644 Total Time: {:.1}s\n\n\
1645 Status:\n{}\n\n\
1646 ✅ Firmware is now running on target.\n\
1647 {}",
1648 args.session_id,
1649 args.file_path,
1650 args.format,
1651 elapsed.as_secs_f64(),
1652 status_messages.join("\n"),
1653 if args.attach_rtt { "Use 'rtt_read' to monitor target output." } else { "Use 'rtt_attach' to enable real-time communication." }
1654 );
1655
1656 info!("Firmware deployment completed for session: {} in {:.1}s", args.session_id, elapsed.as_secs_f64());
1657 Ok(CallToolResult::success(vec![Content::text(message)]))
1658 }
1659}
1660
1661fn parse_address(addr_str: &str) -> Result<u64, String> {
1667 let addr_str = addr_str.trim();
1668
1669 if addr_str.starts_with("0x") || addr_str.starts_with("0X") {
1670 u64::from_str_radix(&addr_str[2..], 16)
1671 .map_err(|e| format!("Invalid hex address: {}", e))
1672 } else {
1673 addr_str.parse::<u64>()
1674 .map_err(|e| format!("Invalid decimal address: {}", e))
1675 }
1676}
1677
1678fn parse_data(data_str: &str, format: &str) -> Result<Vec<u8>, String> {
1680 match format {
1681 "hex" => {
1682 let clean_str = data_str.replace(" ", "").replace("0x", "").replace("0X", "");
1684 if clean_str.len() % 2 != 0 {
1685 return Err("Hex data must have even number of characters".to_string());
1686 }
1687
1688 (0..clean_str.len())
1689 .step_by(2)
1690 .map(|i| u8::from_str_radix(&clean_str[i..i+2], 16))
1691 .collect::<Result<Vec<_>, _>>()
1692 .map_err(|e| format!("Invalid hex data: {}", e))
1693 }
1694 "ascii" => Ok(data_str.as_bytes().to_vec()),
1695 "words32" => {
1696 let words: Result<Vec<u32>, _> = data_str
1697 .split_whitespace()
1698 .map(|s| {
1699 if s.starts_with("0x") || s.starts_with("0X") {
1700 u32::from_str_radix(&s[2..], 16)
1701 } else {
1702 s.parse::<u32>()
1703 }
1704 })
1705 .collect();
1706
1707 match words {
1708 Ok(words) => {
1709 let mut data = Vec::new();
1710 for word in words {
1711 data.extend_from_slice(&word.to_le_bytes());
1712 }
1713 Ok(data)
1714 }
1715 Err(e) => Err(format!("Invalid word32 data: {}", e)),
1716 }
1717 }
1718 "words16" => {
1719 let words: Result<Vec<u16>, _> = data_str
1720 .split_whitespace()
1721 .map(|s| {
1722 if s.starts_with("0x") || s.starts_with("0X") {
1723 u16::from_str_radix(&s[2..], 16)
1724 } else {
1725 s.parse::<u16>()
1726 }
1727 })
1728 .collect();
1729
1730 match words {
1731 Ok(words) => {
1732 let mut data = Vec::new();
1733 for word in words {
1734 data.extend_from_slice(&word.to_le_bytes());
1735 }
1736 Ok(data)
1737 }
1738 Err(e) => Err(format!("Invalid word16 data: {}", e)),
1739 }
1740 }
1741 _ => Err(format!("Unsupported data format: {}", format)),
1742 }
1743}
1744
1745fn format_memory_data(data: &[u8], format: &str, base_address: u64) -> String {
1747 match format {
1748 "hex" => {
1749 let mut result = String::new();
1750 for (i, chunk) in data.chunks(16).enumerate() {
1751 let addr = base_address + (i * 16) as u64;
1752 result.push_str(&format!("0x{:08X}: ", addr));
1753
1754 for (j, byte) in chunk.iter().enumerate() {
1756 if j == 8 { result.push(' '); }
1757 result.push_str(&format!("{:02X} ", byte));
1758 }
1759
1760 if chunk.len() < 16 {
1762 let padding = (16 - chunk.len()) * 3 + (if chunk.len() <= 8 { 1 } else { 0 });
1763 result.push_str(&" ".repeat(padding));
1764 }
1765
1766 result.push_str("| ");
1768 for byte in chunk {
1769 if byte.is_ascii_graphic() || *byte == b' ' {
1770 result.push(*byte as char);
1771 } else {
1772 result.push('.');
1773 }
1774 }
1775 result.push('\n');
1776 }
1777 result
1778 }
1779 "binary" => {
1780 data.iter()
1781 .map(|b| format!("{:08b}", b))
1782 .collect::<Vec<_>>()
1783 .join(" ")
1784 }
1785 "words32" => {
1786 let mut result = String::new();
1787 for (i, chunk) in data.chunks(4).enumerate() {
1788 if chunk.len() == 4 {
1789 let addr = base_address + (i * 4) as u64;
1790 let word = u32::from_le_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]);
1791 result.push_str(&format!("0x{:08X}: 0x{:08X}\n", addr, word));
1792 }
1793 }
1794 result
1795 }
1796 "words16" => {
1797 let mut result = String::new();
1798 for (i, chunk) in data.chunks(2).enumerate() {
1799 if chunk.len() == 2 {
1800 let addr = base_address + (i * 2) as u64;
1801 let word = u16::from_le_bytes([chunk[0], chunk[1]]);
1802 result.push_str(&format!("0x{:08X}: 0x{:04X}\n", addr, word));
1803 }
1804 }
1805 result
1806 }
1807 "ascii" => {
1808 String::from_utf8_lossy(data).to_string()
1809 }
1810 _ => {
1811 format_memory_data(data, "hex", base_address)
1813 }
1814 }
1815}
1816
1817#[tool_handler]
1818impl ServerHandler for EmbeddedDebuggerToolHandler {
1819 fn get_info(&self) -> ServerInfo {
1820 ServerInfo {
1821 protocol_version: ProtocolVersion::V_2024_11_05,
1822 capabilities: ServerCapabilities::builder().enable_tools().build(),
1823 server_info: Implementation::from_build_env(),
1824 instructions: Some("Complete embedded debugging and flash programming MCP server supporting ARM Cortex-M, RISC-V, and other architectures via probe-rs. Provides comprehensive debugging and flash programming capabilities including probe detection, target connection, memory operations, breakpoints, RTT communication, and flash programming with real hardware integration. All 22 tools available: list_probes, connect, disconnect, probe_info, halt, run, reset, step, get_status, read_memory, write_memory, set_breakpoint, clear_breakpoint, rtt_attach, rtt_detach, rtt_read, rtt_write, rtt_channels, flash_erase, flash_program, flash_verify, run_firmware.".to_string()),
1825 }
1826 }
1827
1828 async fn initialize(
1829 &self,
1830 _request: InitializeRequestParam,
1831 _context: RequestContext<RoleServer>,
1832 ) -> Result<InitializeResult, McpError> {
1833 info!("Complete Embedded Debugger MCP server initialized with all 22 tools (18 debug + 4 flash)");
1834 Ok(self.get_info())
1835 }
1836}