Skip to main content

embedded_debugger_mcp/rtt/
manager.rs

1//! RTT manager implementation using probe-rs RTT API
2
3use crate::error::{DebugError, Result};
4use std::collections::HashMap;
5use std::sync::Arc;
6use std::path::Path;
7use tokio::sync::Mutex;
8use tracing::{debug, info, error, warn};
9use probe_rs::{Session, rtt::{Rtt, ScanRegion}, MemoryInterface};
10
11/// RTT manager for hardware communication with embedded targets  
12#[derive(Debug)]
13pub struct RttManager {
14    /// RTT attachment status
15    attached: bool,
16    /// Real RTT instance from probe-rs
17    rtt: Option<Rtt>,
18    /// Session reference for RTT operations
19    session: Option<Arc<Mutex<Session>>>,
20    /// Cached channel information from RTT
21    channels: HashMap<u32, ChannelInfo>,
22    /// Number of up channels discovered
23    up_channel_count: usize,
24    /// Number of down channels discovered
25    down_channel_count: usize,
26}
27
28#[derive(Debug, Clone)]
29pub struct ChannelInfo {
30    pub id: u32,
31    pub name: String,
32    pub direction: ChannelDirection,
33    pub mode: String,
34    pub buffer_size: usize,
35}
36
37#[derive(Debug, Clone)]
38pub enum ChannelDirection {
39    Up,   // Target to Host
40    Down, // Host to Target
41}
42
43impl Default for RttManager {
44    fn default() -> Self {
45        Self::new()
46    }
47}
48
49impl RttManager {
50    /// Create a new RTT manager
51    pub fn new() -> Self {
52        Self {
53            attached: false,
54            rtt: None,
55            session: None,
56            channels: HashMap::new(),
57            up_channel_count: 0,
58            down_channel_count: 0,
59        }
60    }
61
62    /// Enhanced attach method with ELF symbol detection first (probe-rs style)
63    /// This is the recommended method that follows probe-rs best practices
64    pub async fn attach_with_elf(
65        &mut self,
66        session: Arc<Mutex<Session>>,
67        firmware_path: &Path,
68    ) -> Result<()> {
69        info!("Starting enhanced RTT attachment with ELF symbol detection first");
70        debug!("Firmware path: {}", firmware_path.display());
71
72        // Phase 1: Try ELF symbol detection (primary method)
73        match crate::rtt::elf_parser::get_rtt_symbol_from_elf(firmware_path) {
74            Ok(symbol_addr) => {
75                info!("✅ Found _SEGGER_RTT symbol at 0x{:08X}, attempting direct connection", symbol_addr);
76                
77                // Try direct connection at symbol address
78                match self.try_rtt_at_address(session.clone(), symbol_addr).await {
79                    Ok(_) => {
80                        info!("🎯 RTT connected successfully using ELF symbol address!");
81                        return Ok(());
82                    }
83                    Err(e) => {
84                        warn!("RTT connection failed at symbol address 0x{:08X}: {}", symbol_addr, e);
85                        info!("Falling back to memory scanning...");
86                    }
87                }
88            }
89            Err(e) => {
90                info!("ELF symbol detection failed: {}", e);
91                info!("Proceeding with memory scanning fallback...");
92            }
93        }
94
95        // Phase 2: Memory scanning fallback (original method)
96        info!("Using memory scanning fallback approach");
97        self.attach(session, None, None).await
98    }
99
100    /// Try RTT connection at specific address (used for ELF symbol detection)
101    async fn try_rtt_at_address(
102        &mut self,
103        session: Arc<Mutex<Session>>,
104        address: u64,
105    ) -> Result<()> {
106        debug!("Attempting RTT connection at specific address: 0x{:08X}", address);
107
108        // Store session reference
109        self.session = Some(session.clone());
110
111        let mut session_guard = session.lock().await;
112        let mut core = session_guard.core(0).map_err(|e| {
113            error!("Failed to get core for RTT attachment: {}", e);
114            DebugError::RttError(format!("Failed to get core: {}", e))
115        })?;
116
117        // Validate control block at address first
118        let is_valid = self.validate_rtt_control_block_sync(&mut core, address)?;
119        if !is_valid {
120            return Err(DebugError::RttError(format!(
121                "Invalid RTT control block at address 0x{:08X} (magic identifier not found)",
122                address
123            )));
124        }
125        debug!("✅ RTT control block validated at 0x{:08X}", address);
126
127        // CRITICAL FIX: Use ScanRegion::Exact for direct address connection
128        debug!("Using ScanRegion::Exact for direct address connection...");
129        
130        let scan_region = ScanRegion::Exact(address);
131        let rtt_result = Rtt::attach_region(&mut core, &scan_region);
132
133        match rtt_result {
134            Ok(rtt) => {
135                info!("Successfully attached RTT at ELF symbol address 0x{:08X}!", address);
136                self.complete_attachment_sync(rtt)
137            }
138            Err(e) => {
139                error!("RTT attachment failed at address 0x{:08X}: {}", address, e);
140                Err(DebugError::RttError(format!(
141                    "RTT attachment failed at symbol address 0x{:08X}: {}",
142                    address, e
143                )))
144            }
145        }
146    }
147
148    /// Validate RTT control block at given address (probe-rs style validation)
149    fn validate_rtt_control_block_sync(
150        &self,
151        core: &mut probe_rs::Core<'_>,
152        address: u64,
153    ) -> Result<bool> {
154        debug!("Validating RTT control block at address 0x{:08X}", address);
155
156        // Read 16 bytes for RTT magic identifier
157        let mut id_buffer = [0u8; 16];
158        core.read(address, &mut id_buffer).map_err(|e| {
159            DebugError::RttError(format!("Failed to read RTT control block at 0x{:08X}: {}", address, e))
160        })?;
161
162        // Check for "SEGGER RTT" magic identifier
163        const RTT_ID: &[u8] = b"SEGGER RTT\0\0\0\0\0\0";
164        let is_valid = id_buffer == RTT_ID;
165
166        if is_valid {
167            debug!("✅ Valid RTT control block found at 0x{:08X}", address);
168        } else {
169            debug!("❌ Invalid RTT control block at 0x{:08X}, found: {:02X?}", address, &id_buffer[..10]);
170        }
171
172        Ok(is_valid)
173    }
174
175    /// Attach to RTT on target using probe-rs RTT API with enhanced detection
176    /// Priority: ELF symbol detection first, then memory scanning fallback
177    pub async fn attach(
178        &mut self, 
179        session: Arc<Mutex<Session>>,
180        control_block_address: Option<u64>,
181        memory_ranges: Option<Vec<(u64, u64)>>
182    ) -> Result<()> {
183        debug!("Attaching to RTT using probe-rs integration with enhanced detection");
184        
185        // Store session reference
186        self.session = Some(session.clone());
187        
188        // Note: memory_map not needed for probe-rs 0.25 attach_region API
189        
190        // Get the session and core to perform RTT attachment
191        let mut session_guard = session.lock().await;
192        let mut core = session_guard.core(0).map_err(|e| {
193            error!("Failed to get core for RTT attachment: {}", e);
194            DebugError::RttError(format!("Failed to get core: {}", e))
195        })?;
196        
197        // Check if target is running (important for RTT initialization)
198        let core_status = core.status().map_err(|e| {
199            error!("Failed to get core status: {}", e);
200            DebugError::RttError(format!("Failed to get core status: {}", e))
201        })?;
202        debug!("Core status before RTT attach: {:?}", core_status);
203        
204        // Build ScanRegion based on parameters
205        let scan_region = if let Some(cb_addr) = control_block_address {
206            info!("RTT scan: Using exact address: 0x{:08X}", cb_addr);
207            ScanRegion::Exact(cb_addr)
208        } else if let Some(ranges) = memory_ranges {
209            info!("RTT scan: Using custom memory ranges: {:?}", ranges);
210            let ranges = ranges.into_iter()
211                .map(|(start, end)| start..end)
212                .collect();
213            ScanRegion::Ranges(ranges)
214        } else {
215            info!("RTT scan: Using RAM scan (probe-rs default)");
216            ScanRegion::Ram
217        };
218        
219        // Try RTT attachment with appropriate scan region
220        debug!("Attempting RTT attach with scan region: {:?}", scan_region);
221        let rtt_result = Rtt::attach_region(&mut core, &scan_region);
222        
223        match rtt_result {
224            Ok(rtt) => {
225                info!("Successfully attached to RTT control block!");
226                self.complete_attachment_sync(rtt)
227            }
228            Err(e) => {
229                error!("RTT attachment failed: {}", e);
230                
231                // Provide detailed debugging information
232                let detailed_error = format!(
233                    "RTT attachment failed: {}\n\n\
234                    Debug Information:\n\
235                    - Core Status: {:?}\n\
236                    - Scan Region: {:?}\n\
237                    - Control Block Address: {:?}\n\n\
238                    Common Solutions:\n\
239                    - Make sure RTT is initialized on the target (defmt-rtt or rtt-target)\n\
240                    - Ensure target is running (not halted) during RTT initialization\n\
241                    - Check that firmware has sufficient time to initialize RTT\n\
242                    - Verify memory regions contain RTT control block\n\
243                    - For defmt: ensure defmt-rtt feature is enabled in firmware", 
244                    e, 
245                    core_status,
246                    scan_region,
247                    control_block_address
248                );
249                
250                Err(DebugError::RttError(detailed_error))
251            }
252        }
253    }
254    
255    /// Complete RTT attachment by discovering channels (synchronous version)
256    fn complete_attachment_sync(&mut self, mut rtt: Rtt) -> Result<()> {
257        // Clear any previous state
258        self.channels.clear();
259        
260        // Discover up channels (target to host)
261        let up_channels = rtt.up_channels();
262        self.up_channel_count = up_channels.len();
263        for i in 0..up_channels.len() {
264            if let Some(up_channel) = up_channels.get(i) {
265                let channel_info = ChannelInfo {
266                    id: i as u32,
267                    name: up_channel.name().unwrap_or(&format!("Up{}", i)).to_string(),
268                    direction: ChannelDirection::Up,
269                    mode: "RTT".to_string(), // Simplified as mode() requires &mut Core
270                    buffer_size: up_channel.buffer_size(),
271                };
272                self.channels.insert(i as u32, channel_info);
273                debug!("Discovered up channel {}: {} (size: {} bytes)", 
274                       i, up_channel.name().unwrap_or("unnamed"), up_channel.buffer_size());
275            }
276        }
277        
278        // Discover down channels (host to target)
279        let down_channels = rtt.down_channels();
280        self.down_channel_count = down_channels.len();
281        for i in 0..down_channels.len() {
282            if let Some(down_channel) = down_channels.get(i) {
283                let channel_info = ChannelInfo {
284                    id: i as u32,
285                    name: down_channel.name().unwrap_or(&format!("Down{}", i)).to_string(),
286                    direction: ChannelDirection::Down,
287                    mode: "RTT".to_string(), // Simplified as mode() requires &mut Core
288                    buffer_size: down_channel.buffer_size(),
289                };
290                // Use offset for down channels to avoid ID conflicts
291                self.channels.insert(1000 + i as u32, channel_info);
292                debug!("Discovered down channel {}: {} (size: {} bytes)", 
293                       i, down_channel.name().unwrap_or("unnamed"), down_channel.buffer_size());
294            }
295        }
296        
297        // Store the RTT instance
298        self.rtt = Some(rtt);
299        self.attached = true;
300        
301        info!("RTT attachment completed: {} up channels, {} down channels", 
302              self.up_channel_count, self.down_channel_count);
303        Ok(())
304    }
305
306    /// Detach from RTT
307    pub async fn detach(&mut self) -> Result<()> {
308        debug!("Detaching from RTT");
309        
310        self.attached = false;
311        self.rtt = None;
312        self.session = None;
313        self.channels.clear();
314        self.up_channel_count = 0;
315        self.down_channel_count = 0;
316        
317        info!("RTT detached successfully");
318        Ok(())
319    }
320
321    /// Read from RTT up channel using probe-rs RTT API
322    pub async fn read_channel(&mut self, channel: u32) -> Result<Vec<u8>> {
323        if !self.attached {
324            return Err(DebugError::RttError("RTT not attached".to_string()));
325        }
326
327        let session = self.session.as_ref()
328            .ok_or_else(|| DebugError::RttError("No session available".to_string()))?;
329        
330        let rtt = self.rtt.as_mut()
331            .ok_or_else(|| DebugError::RttError("No RTT instance available".to_string()))?;
332        
333        // Lock session and get core
334        let mut session_guard = session.lock().await;
335        let mut core = session_guard.core(0).map_err(|e| {
336            DebugError::RttError(format!("Failed to get core: {}", e))
337        })?;
338        
339        // Get the up channel (mutable reference)
340        let up_channels = rtt.up_channels();
341        let up_channel = up_channels.get_mut(channel as usize)
342            .ok_or_else(|| DebugError::RttError(format!("Up channel {} not found", channel)))?;
343        
344        // Read from RTT channel
345        let mut buffer = vec![0u8; 1024]; // Buffer for reading
346        match up_channel.read(&mut core, &mut buffer) {
347            Ok(bytes_read) => {
348                buffer.truncate(bytes_read);
349                if bytes_read > 0 {
350                    debug!("Read {} bytes from RTT up channel {}", bytes_read, channel);
351                }
352                Ok(buffer)
353            }
354            Err(e) => {
355                error!("Failed to read from RTT up channel {}: {}", channel, e);
356                Err(DebugError::RttError(format!("RTT read failed: {}", e)))
357            }
358        }
359    }
360
361    /// Write to RTT down channel using probe-rs RTT API
362    pub async fn write_channel(&mut self, channel: u32, data: &[u8]) -> Result<usize> {
363        if !self.attached {
364            return Err(DebugError::RttError("RTT not attached".to_string()));
365        }
366
367        let session = self.session.as_ref()
368            .ok_or_else(|| DebugError::RttError("No session available".to_string()))?;
369        
370        let rtt = self.rtt.as_mut()
371            .ok_or_else(|| DebugError::RttError("No RTT instance available".to_string()))?;
372        
373        // Lock session and get core
374        let mut session_guard = session.lock().await;
375        let mut core = session_guard.core(0).map_err(|e| {
376            DebugError::RttError(format!("Failed to get core: {}", e))
377        })?;
378        
379        // Get the down channel (mutable reference)
380        let down_channels = rtt.down_channels();
381        let down_channel = down_channels.get_mut(channel as usize)
382            .ok_or_else(|| DebugError::RttError(format!("Down channel {} not found", channel)))?;
383        
384        // Write to RTT channel
385        match down_channel.write(&mut core, data) {
386            Ok(bytes_written) => {
387                debug!("Wrote {} bytes to RTT down channel {}", bytes_written, channel);
388                info!("RTT Write Channel {}: {:?}", channel, String::from_utf8_lossy(&data[..bytes_written]));
389                Ok(bytes_written)
390            }
391            Err(e) => {
392                error!("Failed to write to RTT down channel {}: {}", channel, e);
393                Err(DebugError::RttError(format!("RTT write failed: {}", e)))
394            }
395        }
396    }
397
398    /// Get information about all RTT channels
399    pub fn get_channels(&self) -> Vec<&ChannelInfo> {
400        self.channels.values().collect()
401    }
402
403    /// Check if RTT is attached
404    pub fn is_attached(&self) -> bool {
405        self.attached
406    }
407
408    /// Get the number of available up channels
409    pub fn up_channel_count(&self) -> usize {
410        self.up_channel_count
411    }
412
413    /// Get the number of available down channels
414    pub fn down_channel_count(&self) -> usize {
415        self.down_channel_count
416    }
417}