1use crate::WriteFlashFile;
2use crc::Algorithm;
3use memmap2::Mmap;
4use std::fs::File;
5use std::io::{BufRead, BufReader, Read, Seek, SeekFrom, Write};
6use std::num::ParseIntError;
7use std::path::Path;
8use tempfile::tempfile;
9
10#[derive(Debug, PartialEq, Eq, Clone)]
11pub enum FileType {
12 Bin,
13 Hex,
14 Elf,
15}
16
17pub const ELF_MAGIC: &[u8] = &[0x7F, 0x45, 0x4C, 0x46]; pub struct Utils;
20impl Utils {
21 pub fn str_to_u32(s: &str) -> Result<u32, ParseIntError> {
22 let s = s.trim();
23
24 let (num_str, multiplier) = match s.chars().last() {
25 Some('k') | Some('K') => (&s[..s.len() - 1], 1_000u32),
26 Some('m') | Some('M') => (&s[..s.len() - 1], 1_000_000u32),
27 Some('g') | Some('G') => (&s[..s.len() - 1], 1_000_000_000u32),
28 _ => (s, 1),
29 };
30
31 let unsigned: u32 = if let Some(hex) = num_str.strip_prefix("0x") {
32 u32::from_str_radix(hex, 16)?
33 } else if let Some(bin) = num_str.strip_prefix("0b") {
34 u32::from_str_radix(bin, 2)?
35 } else if let Some(oct) = num_str.strip_prefix("0o") {
36 u32::from_str_radix(oct, 8)?
37 } else {
38 num_str.parse()?
39 };
40
41 Ok(unsigned * multiplier)
42 }
43
44 pub(crate) fn get_file_crc32(file: &File) -> Result<u32, std::io::Error> {
45 const CRC_32_ALGO: Algorithm<u32> = Algorithm {
46 width: 32,
47 poly: 0x04C11DB7,
48 init: 0,
49 refin: true,
50 refout: true,
51 xorout: 0,
52 check: 0x2DFD2D88,
53 residue: 0,
54 };
55
56 const CRC: crc::Crc<u32> = crc::Crc::<u32>::new(&CRC_32_ALGO);
57 let mut reader = BufReader::new(file);
58
59 let mut digest = CRC.digest();
60
61 let mut buffer = [0u8; 4 * 1024];
62 loop {
63 let n = reader.read(&mut buffer)?;
64 if n == 0 {
65 break;
66 }
67 digest.update(&buffer[..n]);
68 }
69
70 let checksum = digest.finalize();
71 reader.seek(SeekFrom::Start(0))?;
72 Ok(checksum)
73 }
74
75 pub fn detect_file_type(path: &Path) -> Result<FileType, std::io::Error> {
77 if let Some(ext) = path.extension().and_then(|s| s.to_str()) {
78 match ext.to_lowercase().as_str() {
79 "bin" => return Ok(FileType::Bin),
80 "hex" => return Ok(FileType::Hex),
81 "elf" | "axf" => return Ok(FileType::Elf),
82 _ => {} }
84 }
85
86 let mut file = File::open(path)?;
88 let mut magic = [0u8; 4];
89 file.read_exact(&mut magic)?;
90
91 if magic == ELF_MAGIC {
92 return Ok(FileType::Elf);
93 }
94
95 Err(std::io::Error::new(
96 std::io::ErrorKind::InvalidInput,
97 "Unrecognized file type",
98 ))
99 }
100
101 pub fn parse_file_info(file_str: &str) -> Result<Vec<WriteFlashFile>, std::io::Error> {
103 let parts: Vec<_> = file_str.split('@').collect();
105 if parts.len() == 2 {
107 let addr = Self::str_to_u32(parts[1])
108 .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidInput, e))?;
109 let file = std::fs::File::open(parts[0])?;
110 let crc32 = Self::get_file_crc32(&file)?;
111
112 return Ok(vec![WriteFlashFile {
113 address: addr,
114 file,
115 crc32,
116 }]);
117 }
118
119 let file_type = Self::detect_file_type(Path::new(parts[0]))?;
120
121 match file_type {
122 FileType::Hex => Self::hex_to_write_flash_files(Path::new(parts[0])),
123 FileType::Elf => Self::elf_to_write_flash_files(Path::new(parts[0])),
124 FileType::Bin => Err(std::io::Error::new(
125 std::io::ErrorKind::InvalidInput,
126 "For binary files, please use the <file@address> format",
127 )),
128 }
129 }
130
131 pub fn calculate_crc32(data: &[u8]) -> u32 {
133 const CRC_32_ALGO: Algorithm<u32> = Algorithm {
134 width: 32,
135 poly: 0x04C11DB7,
136 init: 0,
137 refin: true,
138 refout: true,
139 xorout: 0,
140 check: 0,
141 residue: 0,
142 };
143 crc::Crc::<u32>::new(&CRC_32_ALGO).checksum(data)
144 }
145
146 pub fn hex_to_write_flash_files(
148 hex_file: &Path,
149 ) -> Result<Vec<WriteFlashFile>, std::io::Error> {
150 let mut write_flash_files: Vec<WriteFlashFile> = Vec::new();
151
152 let file = std::fs::File::open(hex_file)?;
153 let reader = std::io::BufReader::new(file);
154
155 let mut current_base_address = 0u32;
156 let mut current_temp_file: Option<File> = None;
157 let mut current_segment_start = 0u32;
158 let mut current_file_offset = 0u32;
159
160 for line in reader.lines() {
161 let line = line?;
162 let line = line.trim_end_matches('\r');
163 if line.is_empty() {
164 continue;
165 }
166
167 let ihex_record = ihex::Record::from_record_string(&line)
168 .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidInput, e))?;
169
170 match ihex_record {
171 ihex::Record::ExtendedLinearAddress(addr) => {
172 let new_base_address = (addr as u32) << 16;
173
174 if new_base_address != current_base_address && current_temp_file.is_some() {
176 if let Some(temp_file) = current_temp_file.take() {
178 Self::finalize_segment(
179 temp_file,
180 current_segment_start,
181 &mut write_flash_files,
182 )?;
183 }
184 current_file_offset = 0;
185 }
186
187 current_base_address = new_base_address;
188 }
189 ihex::Record::Data { offset, value } => {
190 let absolute_address = current_base_address + offset as u32;
191
192 if current_temp_file.is_none() {
194 current_temp_file = Some(tempfile()?);
195 current_segment_start = absolute_address;
196 current_file_offset = 0;
197 }
198
199 if let Some(ref mut temp_file) = current_temp_file {
200 let expected_file_offset = absolute_address - current_segment_start;
201
202 if expected_file_offset > current_file_offset {
204 let gap_size = expected_file_offset - current_file_offset;
205 let fill_data = vec![0xFF; gap_size as usize];
206 temp_file.write_all(&fill_data)?;
207 current_file_offset = expected_file_offset;
208 }
209
210 temp_file.write_all(&value)?;
212 current_file_offset += value.len() as u32;
213 }
214 }
215 ihex::Record::EndOfFile => {
216 if let Some(temp_file) = current_temp_file.take() {
218 Self::finalize_segment(
219 temp_file,
220 current_segment_start,
221 &mut write_flash_files,
222 )?;
223 }
224 break;
225 }
226 _ => {}
227 }
228 }
229
230 if let Some(temp_file) = current_temp_file.take() {
232 Self::finalize_segment(temp_file, current_segment_start, &mut write_flash_files)?;
233 }
234
235 Ok(write_flash_files)
236 }
237
238 pub fn elf_to_write_flash_files(
240 elf_file: &Path,
241 ) -> Result<Vec<WriteFlashFile>, std::io::Error> {
242 let mut write_flash_files: Vec<WriteFlashFile> = Vec::new();
243 const SECTOR_SIZE: u32 = 0x1000; const FILL_BYTE: u8 = 0xFF; let file = File::open(elf_file)?;
247 let mmap = unsafe { Mmap::map(&file)? };
248 let elf = goblin::elf::Elf::parse(&mmap[..])
249 .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidInput, e))?;
250
251 let mut load_segments: Vec<_> = elf
253 .program_headers
254 .iter()
255 .filter(|ph| {
256 ph.p_type == goblin::elf::program_header::PT_LOAD && ph.p_paddr < 0x2000_0000
257 })
258 .collect();
259 load_segments.sort_by_key(|ph| ph.p_paddr);
260
261 if load_segments.is_empty() {
262 return Ok(write_flash_files);
263 }
264
265 let mut current_file = tempfile()?;
266 let mut current_base = (load_segments[0].p_paddr as u32) & !(SECTOR_SIZE - 1);
267 let mut current_offset = 0; for ph in load_segments.iter() {
270 let vaddr = ph.p_paddr as u32;
271 let offset = ph.p_offset as usize;
272 let size = ph.p_filesz as usize;
273 let data = &mmap[offset..offset + size];
274
275 let segment_base = vaddr & !(SECTOR_SIZE - 1);
277
278 if segment_base > current_base + current_offset {
280 current_file.seek(std::io::SeekFrom::Start(0))?;
281 let crc32 = Self::get_file_crc32(¤t_file)?;
282 write_flash_files.push(WriteFlashFile {
283 address: current_base,
284 file: std::mem::replace(&mut current_file, tempfile()?),
285 crc32,
286 });
287 current_base = segment_base;
288 current_offset = 0;
289 }
290
291 let relative_offset = vaddr - current_base;
293
294 if current_offset < relative_offset {
296 let padding = relative_offset - current_offset;
297 current_file.write_all(&vec![FILL_BYTE; padding as usize])?;
298 current_offset = relative_offset;
299 }
300
301 current_file.write_all(data)?;
303 current_offset += size as u32;
304 }
305
306 if current_offset > 0 {
308 current_file.seek(std::io::SeekFrom::Start(0))?;
309 let crc32 = Self::get_file_crc32(¤t_file)?;
310 write_flash_files.push(WriteFlashFile {
311 address: current_base,
312 file: current_file,
313 crc32,
314 });
315 }
316
317 Ok(write_flash_files)
318 }
319
320 fn finalize_segment(
322 mut temp_file: File,
323 address: u32,
324 write_flash_files: &mut Vec<WriteFlashFile>,
325 ) -> Result<(), std::io::Error> {
326 temp_file.seek(std::io::SeekFrom::Start(0))?;
327 let crc32 = Self::get_file_crc32(&temp_file)?;
328 write_flash_files.push(WriteFlashFile {
329 address,
330 file: temp_file,
331 crc32,
332 });
333 Ok(())
334 }
335
336 pub fn parse_read_file_info(file_spec: &str) -> Result<crate::ReadFlashFile, std::io::Error> {
338 let Some((file_path, addr_size)) = file_spec.split_once('@') else {
339 return Err(std::io::Error::new(
340 std::io::ErrorKind::InvalidInput,
341 format!(
342 "Invalid format: {}. Expected: filename@address:size",
343 file_spec
344 ),
345 ));
346 };
347
348 let Some((address_str, size_str)) = addr_size.split_once(':') else {
349 return Err(std::io::Error::new(
350 std::io::ErrorKind::InvalidInput,
351 format!(
352 "Invalid address:size format: {}. Expected: address:size",
353 addr_size
354 ),
355 ));
356 };
357
358 let address = Self::str_to_u32(address_str).map_err(|e| {
359 std::io::Error::new(
360 std::io::ErrorKind::InvalidInput,
361 format!("Invalid address '{}': {}", address_str, e),
362 )
363 })?;
364
365 let size = Self::str_to_u32(size_str).map_err(|e| {
366 std::io::Error::new(
367 std::io::ErrorKind::InvalidInput,
368 format!("Invalid size '{}': {}", size_str, e),
369 )
370 })?;
371
372 Ok(crate::ReadFlashFile {
373 file_path: file_path.to_string(),
374 address,
375 size,
376 })
377 }
378
379 pub fn parse_erase_address(address_str: &str) -> Result<u32, std::io::Error> {
381 Self::str_to_u32(address_str).map_err(|e| {
382 std::io::Error::new(
383 std::io::ErrorKind::InvalidInput,
384 format!("Invalid address '{}': {}", address_str, e),
385 )
386 })
387 }
388
389 pub fn parse_erase_region(region_spec: &str) -> Result<crate::EraseRegionFile, std::io::Error> {
391 let Some((address_str, size_str)) = region_spec.split_once(':') else {
392 return Err(std::io::Error::new(
393 std::io::ErrorKind::InvalidInput,
394 format!(
395 "Invalid region format: {}. Expected: address:size",
396 region_spec
397 ),
398 ));
399 };
400
401 let address = Self::str_to_u32(address_str).map_err(|e| {
402 std::io::Error::new(
403 std::io::ErrorKind::InvalidInput,
404 format!("Invalid address '{}': {}", address_str, e),
405 )
406 })?;
407
408 let size = Self::str_to_u32(size_str).map_err(|e| {
409 std::io::Error::new(
410 std::io::ErrorKind::InvalidInput,
411 format!("Invalid size '{}': {}", size_str, e),
412 )
413 })?;
414
415 Ok(crate::EraseRegionFile { address, size })
416 }
417}
418
419#[cfg(test)]
420mod tests {
421 use super::*;
422 use std::io::{Read, Seek, SeekFrom, Write};
423 use tempfile::NamedTempFile;
424
425 #[test]
426 fn test_hex_to_bin_single_segment() {
427 let hex_content = ":0400000001020304F2\n:0410000005060708D2\n:00000001FF\n";
429
430 let mut temp_hex = NamedTempFile::new().unwrap();
431 temp_hex.write_all(hex_content.as_bytes()).unwrap();
432
433 let result = Utils::hex_to_write_flash_files(temp_hex.path()).unwrap();
434
435 assert_eq!(result.len(), 1);
437
438 let segment = &result[0];
439 assert_eq!(segment.address, 0x00000000);
440
441 let file_size = segment.file.metadata().unwrap().len() as usize;
443 assert_eq!(file_size, 0x1004);
444
445 let mut file_data = Vec::new();
447 let mut file = &segment.file;
448 file.read_to_end(&mut file_data).unwrap();
449
450 assert_eq!(&file_data[0..4], &[0x01, 0x02, 0x03, 0x04]);
453 assert!(file_data[4..0x1000].iter().all(|&b| b == 0xFF));
455 assert_eq!(&file_data[0x1000..0x1004], &[0x05, 0x06, 0x07, 0x08]);
457 }
458
459 #[test]
460 fn test_hex_to_bin_multiple_segments() {
461 let hex_content =
463 ":0400000001020304F2\n:020000040001F9\n:0400000011121314B2\n:00000001FF\n";
464
465 let mut temp_hex = NamedTempFile::new().unwrap();
466 temp_hex.write_all(hex_content.as_bytes()).unwrap();
467
468 let result = Utils::hex_to_write_flash_files(temp_hex.path()).unwrap();
469
470 assert_eq!(result.len(), 2);
472
473 assert_eq!(result[0].address, 0x00000000);
475 let file_size_0 = result[0].file.metadata().unwrap().len() as usize;
476 assert_eq!(file_size_0, 4);
477
478 let mut file_data_0 = Vec::new();
479 let mut file_0 = &result[0].file;
480 file_0.read_to_end(&mut file_data_0).unwrap();
481 assert_eq!(&file_data_0, &[0x01, 0x02, 0x03, 0x04]);
482
483 assert_eq!(result[1].address, 0x00010000);
485 let file_size_1 = result[1].file.metadata().unwrap().len() as usize;
486 assert_eq!(file_size_1, 4);
487
488 let mut file_data_1 = Vec::new();
489 let mut file_1 = &result[1].file;
490 file_1.read_to_end(&mut file_data_1).unwrap();
491 assert_eq!(&file_data_1, &[0x11, 0x12, 0x13, 0x14]);
492 }
493
494 #[test]
495 fn test_hex_to_bin_with_gaps() {
496 let hex_content = ":04000000AABBCCDDEE\n:04100000EEFF0011EE\n:00000001FF\n";
498
499 let mut temp_hex = NamedTempFile::new().unwrap();
500 temp_hex.write_all(hex_content.as_bytes()).unwrap();
501
502 let result = Utils::hex_to_write_flash_files(temp_hex.path()).unwrap();
503
504 println!("Number of segments: {}", result.len());
506 for (i, segment) in result.iter().enumerate() {
507 let file_size = segment.file.metadata().unwrap().len() as usize;
508 println!(
509 "Segment {}: address=0x{:08X}, size={}",
510 i, segment.address, file_size
511 );
512 }
513
514 assert_eq!(result.len(), 1);
516
517 let segment = &result[0];
518 assert_eq!(segment.address, 0x00000000);
519
520 let file_size = segment.file.metadata().unwrap().len() as usize;
522 println!(
523 "Expected size: 0x1004 ({}), Actual size: {}",
524 0x1004, file_size
525 );
526 assert_eq!(file_size, 0x1004);
527
528 let mut file_data = Vec::new();
530 let mut file = &segment.file;
531 file.read_to_end(&mut file_data).unwrap();
532
533 assert_eq!(&file_data[0..4], &[0xAA, 0xBB, 0xCC, 0xDD]);
535 assert!(file_data[4..0x1000].iter().all(|&b| b == 0xFF));
537 assert_eq!(&file_data[0x1000..0x1004], &[0xEE, 0xFF, 0x00, 0x11]);
539
540 let mut file = segment.file.try_clone().unwrap();
542 file.seek(SeekFrom::Start(4)).unwrap();
543 let mut gap_data = vec![0; 0x1000 - 4];
544 file.read_exact(&mut gap_data).unwrap();
545
546 assert!(gap_data.iter().all(|&b| b == 0xFF));
548 }
549
550 #[test]
551 fn test_hex_to_bin_complex_multi_segment() {
552 let hex_content = ":100000000102030405060708090A0B0C0D0E0F1068\n:08100000111213141516171844\n:020000040001F9\n:040000002122232472\n:041000003132333422\n:020000040010EA\n:080000004142434445464748D4\n:00000001FF\n";
554
555 let mut temp_hex = NamedTempFile::new().unwrap();
556 temp_hex.write_all(hex_content.as_bytes()).unwrap();
557
558 let result = Utils::hex_to_write_flash_files(temp_hex.path()).unwrap();
559
560 assert_eq!(result.len(), 3);
562
563 assert_eq!(result[0].address, 0x00000000);
565 let file_size_0 = result[0].file.metadata().unwrap().len() as usize;
566 assert_eq!(file_size_0, 0x1008); assert_eq!(result[1].address, 0x00010000);
570 let file_size_1 = result[1].file.metadata().unwrap().len() as usize;
571 assert_eq!(file_size_1, 0x1004); assert_eq!(result[2].address, 0x00100000);
575 let file_size_2 = result[2].file.metadata().unwrap().len() as usize;
576 assert_eq!(file_size_2, 8);
577
578 let mut file_data_0 = Vec::new();
580 let mut file_0 = &result[0].file;
581 file_0.read_to_end(&mut file_data_0).unwrap();
582
583 assert_eq!(
586 &file_data_0[0..16],
587 &[
588 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
589 0x0F, 0x10
590 ]
591 );
592 assert!(file_data_0[16..0x1000].iter().all(|&b| b == 0xFF));
594 assert_eq!(
596 &file_data_0[0x1000..0x1008],
597 &[0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18]
598 );
599 }
600
601 #[test]
602 fn test_str_to_u32() {
603 assert_eq!(Utils::str_to_u32("123").unwrap(), 123);
604 assert_eq!(Utils::str_to_u32("0x10").unwrap(), 16);
605 assert_eq!(Utils::str_to_u32("0b1010").unwrap(), 10);
606 assert_eq!(Utils::str_to_u32("0o17").unwrap(), 15);
607 assert_eq!(Utils::str_to_u32("1k").unwrap(), 1000);
608 assert_eq!(Utils::str_to_u32("1K").unwrap(), 1000);
609 assert_eq!(Utils::str_to_u32("1m").unwrap(), 1000000);
610 assert_eq!(Utils::str_to_u32("1M").unwrap(), 1000000);
611 }
612
613 #[test]
614 fn test_parse_read_file_info() {
615 let result = Utils::parse_read_file_info("output.bin@0x1000:0x100").unwrap();
616 assert_eq!(result.file_path, "output.bin");
617 assert_eq!(result.address, 0x1000);
618 assert_eq!(result.size, 0x100);
619
620 let result = Utils::parse_read_file_info("data.bin@0x20000000:1k").unwrap();
621 assert_eq!(result.file_path, "data.bin");
622 assert_eq!(result.address, 0x20000000);
623 assert_eq!(result.size, 1000);
624
625 assert!(Utils::parse_read_file_info("invalid_format").is_err());
627 assert!(Utils::parse_read_file_info("file@0x1000").is_err()); assert!(Utils::parse_read_file_info("file@invalid:0x100").is_err()); }
630
631 #[test]
632 fn test_parse_erase_address() {
633 assert_eq!(Utils::parse_erase_address("0x1000").unwrap(), 0x1000);
634 assert_eq!(Utils::parse_erase_address("1000").unwrap(), 1000);
635 assert_eq!(Utils::parse_erase_address("1k").unwrap(), 1000);
636
637 assert!(Utils::parse_erase_address("invalid").is_err());
639 }
640
641 #[test]
642 fn test_parse_erase_region() {
643 let result = Utils::parse_erase_region("0x1000:0x100").unwrap();
644 assert_eq!(result.address, 0x1000);
645 assert_eq!(result.size, 0x100);
646
647 let result = Utils::parse_erase_region("0x20000000:1k").unwrap();
648 assert_eq!(result.address, 0x20000000);
649 assert_eq!(result.size, 1000);
650
651 assert!(Utils::parse_erase_region("invalid_format").is_err());
653 assert!(Utils::parse_erase_region("0x1000").is_err()); assert!(Utils::parse_erase_region("invalid:0x100").is_err()); }
656}