embedded_debugger_mcp/flash/
manager.rs1use crate::error::{Result, DebugError};
4use std::path::Path;
5use std::time::Instant;
6use tracing::{debug, info, warn};
7
8use probe_rs::{flashing::{self, FlashProgress}, Session, MemoryInterface};
10
11#[derive(Debug, Clone)]
13pub enum EraseType {
14 All,
16 Sectors { address: u64, size: usize },
18}
19
20#[derive(Debug, Clone)]
22pub enum FileFormat {
23 Auto,
24 Elf,
25 Hex,
26 Bin,
27}
28
29#[derive(Debug)]
31pub struct EraseResult {
32 pub erase_time_ms: u64,
33 pub sectors_erased: Option<usize>,
34}
35
36#[derive(Debug)]
38pub struct ProgramResult {
39 pub bytes_programmed: usize,
40 pub programming_time_ms: u64,
41 pub verification_result: Option<bool>,
42}
43
44#[derive(Debug)]
46pub struct VerifyResult {
47 pub success: bool,
48 pub bytes_verified: usize,
49 pub mismatches: Vec<VerifyMismatch>,
50}
51
52#[derive(Debug)]
54pub struct VerifyMismatch {
55 pub address: u64,
56 pub expected: u8,
57 pub actual: u8,
58}
59
60pub struct FlashManager;
62
63impl FlashManager {
64 pub fn new() -> Self {
66 Self
67 }
68
69 pub async fn erase_flash(
71 session: &mut Session,
72 erase_type: EraseType,
73 ) -> Result<EraseResult> {
74 let start_time = Instant::now();
75
76 match erase_type {
77 EraseType::All => {
78 debug!("Starting full flash erase");
79 flashing::erase_all(session, FlashProgress::empty())
80 .map_err(|e| DebugError::FlashOperationFailed(format!("Full erase failed: {}", e)))?;
81
82 info!("Full flash erase completed");
83 Ok(EraseResult {
84 erase_time_ms: start_time.elapsed().as_millis() as u64,
85 sectors_erased: None,
86 })
87 }
88 EraseType::Sectors { address, size } => {
89 debug!("Starting sector erase at 0x{:08X}, size: {} bytes", address, size);
90
91 let sector_size = 4096; let sector_count = (size + sector_size - 1) / sector_size;
94
95 let mut core = session.core(0)
97 .map_err(|e| DebugError::FlashOperationFailed(format!("Failed to get core: {}", e)))?;
98
99 let erase_data = vec![0xFFu8; size];
102 core.write(address, &erase_data)
103 .map_err(|e| DebugError::FlashOperationFailed(format!("Sector erase failed: {}", e)))?;
104
105 info!("Sector erase completed: {} sectors", sector_count);
106 Ok(EraseResult {
107 erase_time_ms: start_time.elapsed().as_millis() as u64,
108 sectors_erased: Some(sector_count),
109 })
110 }
111 }
112 }
113
114 pub async fn program_file(
116 session: &mut Session,
117 file_path: &Path,
118 format: FileFormat,
119 base_address: Option<u64>,
120 ) -> Result<ProgramResult> {
121 let start_time = Instant::now();
122
123 if !file_path.exists() {
125 return Err(DebugError::FlashOperationFailed(format!("File not found: {}", file_path.display())));
126 }
127
128 debug!("Programming file: {}", file_path.display());
129
130 let probe_format = match format {
132 FileFormat::Auto => {
133 match file_path.extension().and_then(|s| s.to_str()) {
135 Some("elf") => flashing::Format::Elf,
136 Some("hex") => flashing::Format::Hex,
137 Some("bin") => flashing::Format::Bin(probe_rs::flashing::BinOptions { base_address: None, skip: 0 }),
138 _ => return Err(DebugError::FlashOperationFailed("Cannot auto-detect file format".to_string())),
139 }
140 }
141 FileFormat::Elf => flashing::Format::Elf,
142 FileFormat::Hex => flashing::Format::Hex,
143 FileFormat::Bin => flashing::Format::Bin(probe_rs::flashing::BinOptions { base_address, skip: 0 }),
144 };
145
146 let mut options = flashing::DownloadOptions::default();
148 options.verify = true;
149 options.progress = None;
150
151 if matches!(probe_format, flashing::Format::Bin(_)) {
153 if let Some(addr) = base_address {
154 warn!("Base address specification for BIN files: 0x{:08X} - may require different API usage", addr);
156 }
157 }
158
159 flashing::download_file_with_options(session, file_path, probe_format, options)
161 .map_err(|e| DebugError::FlashOperationFailed(format!("Programming failed: {}", e)))?;
162
163 let elapsed = start_time.elapsed().as_millis() as u64;
164
165 info!("File programming completed in {}ms", elapsed);
166
167 let file_size = std::fs::metadata(file_path)
169 .map(|m| m.len() as usize)
170 .unwrap_or(0);
171
172 Ok(ProgramResult {
173 bytes_programmed: file_size,
174 programming_time_ms: elapsed,
175 verification_result: Some(true), })
177 }
178
179 pub async fn program_data(
181 session: &mut Session,
182 data: &[u8],
183 base_address: u64,
184 ) -> Result<ProgramResult> {
185 let start_time = Instant::now();
186
187 debug!("Programming {} bytes to address 0x{:08X}", data.len(), base_address);
188
189 let mut core = session.core(0)
191 .map_err(|e| DebugError::FlashOperationFailed(format!("Failed to get core: {}", e)))?;
192
193 core.write(base_address, data)
195 .map_err(|e| DebugError::FlashOperationFailed(format!("Failed to write data: {}", e)))?;
196
197 let elapsed = start_time.elapsed().as_millis() as u64;
198
199 info!("Data programming completed: {} bytes in {}ms", data.len(), elapsed);
200
201 Ok(ProgramResult {
202 bytes_programmed: data.len(),
203 programming_time_ms: elapsed,
204 verification_result: None, })
206 }
207
208 pub async fn verify_flash(
210 session: &mut Session,
211 expected_data: &[u8],
212 address: u64,
213 ) -> Result<VerifyResult> {
214 debug!("Verifying {} bytes at address 0x{:08X}", expected_data.len(), address);
215
216 let mut core = session.core(0)
217 .map_err(|e| DebugError::FlashOperationFailed(format!("Failed to get core: {}", e)))?;
218
219 let mut actual_data = vec![0u8; expected_data.len()];
221 core.read(address, &mut actual_data)
222 .map_err(|e| DebugError::FlashOperationFailed(format!("Failed to read flash: {}", e)))?;
223
224 let mut mismatches = Vec::new();
226 for (i, (expected, actual)) in expected_data.iter().zip(actual_data.iter()).enumerate() {
227 if expected != actual {
228 mismatches.push(VerifyMismatch {
229 address: address + i as u64,
230 expected: *expected,
231 actual: *actual,
232 });
233 }
234 }
235
236 let success = mismatches.is_empty();
237
238 if success {
239 info!("Flash verification successful: {} bytes", expected_data.len());
240 } else {
241 warn!("Flash verification failed: {} mismatches", mismatches.len());
242 }
243
244 Ok(VerifyResult {
245 success,
246 bytes_verified: expected_data.len(),
247 mismatches,
248 })
249 }
250}
251
252impl Default for FlashManager {
253 fn default() -> Self {
254 Self::new()
255 }
256}