1use espflash::flasher::{FlashFrequency, FlashMode};
2use object::{
3 Endianness, Object, ObjectSection, elf::FileHeader32, elf::FileHeader64, elf::PT_LOAD,
4 read::elf::ElfFile, read::elf::FileHeader, read::elf::ProgramHeader,
5};
6use probe_rs_target::{InstructionSet, MemoryRange};
7use serde::{Deserialize, Serialize};
8
9use std::{
10 fs::File,
11 path::{Path, PathBuf},
12 str::FromStr,
13};
14
15use super::*;
16use crate::session::Session;
17
18#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone, Default)]
20pub struct BinOptions {
21 pub base_address: Option<u64>,
23 pub skip: u32,
25}
26
27#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone, Default)]
29pub struct IdfOptions {
30 pub bootloader: Option<PathBuf>,
32 pub partition_table: Option<PathBuf>,
34 pub target_app_partition: Option<String>,
36 pub flash_mode: Option<FlashMode>,
38 pub flash_frequency: Option<FlashFrequency>,
40}
41
42#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone, Default)]
44pub struct ElfOptions {
45 pub skip_sections: Vec<String>,
47}
48
49#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq, Clone, Copy)]
51pub enum FormatKind {
52 Bin,
56 Hex,
58 #[default]
60 Elf,
61 Idf,
64 Uf2,
66}
67
68impl FormatKind {
69 pub fn from_optional(s: Option<&str>) -> Result<Self, String> {
73 match s {
74 Some(format) => Self::from_str(format),
75 None => Ok(Self::default()),
76 }
77 }
78}
79
80impl FromStr for FormatKind {
81 type Err = String;
82
83 fn from_str(s: &str) -> Result<Self, Self::Err> {
84 match &s.to_lowercase()[..] {
85 "bin" | "binary" => Ok(Self::Bin),
86 "hex" | "ihex" | "intelhex" => Ok(Self::Hex),
87 "elf" => Ok(Self::Elf),
88 "uf2" => Ok(Self::Uf2),
89 "idf" | "esp-idf" | "espidf" => Ok(Self::Idf),
90 _ => Err(format!("Format '{s}' is unknown.")),
91 }
92 }
93}
94
95#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
97pub enum Format {
98 Bin(BinOptions),
102 Hex,
104 Elf(ElfOptions),
106 Idf(IdfOptions),
109 Uf2,
111}
112
113impl Default for Format {
114 fn default() -> Self {
115 Format::Elf(ElfOptions::default())
116 }
117}
118
119impl From<FormatKind> for Format {
120 fn from(kind: FormatKind) -> Self {
121 match kind {
122 FormatKind::Bin => Format::Bin(BinOptions::default()),
123 FormatKind::Hex => Format::Hex,
124 FormatKind::Elf => Format::Elf(ElfOptions::default()),
125 FormatKind::Uf2 => Format::Uf2,
126 FormatKind::Idf => Format::Idf(IdfOptions::default()),
127 }
128 }
129}
130
131#[derive(Debug, thiserror::Error, docsplay::Display)]
136pub enum FileDownloadError {
137 #[ignore_extra_doc_attributes]
139 Flash(#[from] FlashError),
142
143 IhexRead(#[from] ihex::ReaderError),
145
146 IO(#[from] std::io::Error),
148
149 Object(&'static str),
151
152 Elf(#[from] object::read::Error),
154
155 Idf(#[from] espflash::Error),
157
158 IdfUnsupported(String),
160
161 #[ignore_extra_doc_attributes]
163 NoLoadableSegments,
166
167 FlashSizeDetection(#[source] FlashError),
169
170 IncompatibleImage {
172 target: Vec<InstructionSet>,
174 image: InstructionSet,
176 },
177
178 IncompatibleImageChip {
180 target: String,
182 image_chips: Vec<String>,
184 },
185
186 Other(#[source] crate::Error),
188}
189
190fn print_instr_sets(instr_sets: &[InstructionSet]) -> String {
191 instr_sets
192 .iter()
193 .map(|instr_set| format!("{instr_set:?}"))
194 .collect::<Vec<_>>()
195 .join(", ")
196}
197
198#[derive(Default)]
211#[non_exhaustive]
212pub struct DownloadOptions<'p> {
213 pub progress: FlashProgress<'p>,
215 pub keep_unwritten_bytes: bool,
222 pub dry_run: bool,
224 pub do_chip_erase: bool,
228 pub skip_erase: bool,
231 pub preverify: bool,
233 pub verify: bool,
235 pub disable_double_buffering: bool,
237 pub preferred_algos: Vec<String>,
240}
241
242impl DownloadOptions<'_> {
243 pub fn new() -> Self {
245 Self::default()
246 }
247}
248
249pub fn build_loader(
253 session: &mut Session,
254 path: impl AsRef<Path>,
255 format: Format,
256 image_instruction_set: Option<InstructionSet>,
257) -> Result<FlashLoader, FileDownloadError> {
258 let mut loader = session.target().flash_loader();
260
261 let mut file = File::open(path).map_err(FileDownloadError::IO)?;
263
264 loader.load_image(session, &mut file, format, image_instruction_set)?;
265
266 Ok(loader)
267}
268
269pub fn download_file(
275 session: &mut Session,
276 path: impl AsRef<Path>,
277 format: impl Into<Format>,
278) -> Result<(), FileDownloadError> {
279 download_file_with_options(session, path, format, DownloadOptions::default())
280}
281
282pub fn download_file_with_options(
288 session: &mut Session,
289 path: impl AsRef<Path>,
290 format: impl Into<Format>,
291 options: DownloadOptions,
292) -> Result<(), FileDownloadError> {
293 let loader = build_loader(session, path, format.into(), None)?;
294
295 loader
296 .commit(session, options)
297 .map_err(FileDownloadError::Flash)
298}
299
300pub(super) struct ExtractedFlashData<'data> {
302 pub(super) section_names: Vec<String>,
303 pub(super) address: u32,
304 pub(super) data: &'data [u8],
305}
306
307impl std::fmt::Debug for ExtractedFlashData<'_> {
308 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
309 let mut helper = f.debug_struct("ExtractedFlashData");
310
311 helper
312 .field("name", &self.section_names)
313 .field("address", &self.address);
314
315 if self.data.len() > 10 {
316 helper
317 .field("data", &format!("[..] ({} bytes)", self.data.len()))
318 .finish()
319 } else {
320 helper.field("data", &self.data).finish()
321 }
322 }
323}
324
325fn extract_from_elf_inner<'data, T: FileHeader>(
326 elf_header: &T,
327 binary: ElfFile<'_, T>,
328 elf_data: &'data [u8],
329 options: &ElfOptions,
330) -> Result<Vec<ExtractedFlashData<'data>>, FileDownloadError> {
331 let endian = elf_header.endian()?;
332
333 let mut extracted_data = Vec::new();
334 for segment in elf_header.program_headers(elf_header.endian()?, elf_data)? {
335 let p_paddr: u64 = segment.p_paddr(endian).into();
337
338 let p_vaddr: u64 = segment.p_vaddr(endian).into();
339
340 let flags = segment.p_flags(endian);
341
342 let segment_data = segment
343 .data(endian, elf_data)
344 .map_err(|_| FileDownloadError::Object("Failed to access data for an ELF segment."))?;
345
346 let mut elf_section = Vec::new();
347
348 if !segment_data.is_empty() && segment.p_type(endian) == PT_LOAD {
349 tracing::info!(
350 "Found loadable segment, physical address: {:#010x}, virtual address: {:#010x}, flags: {:#x}",
351 p_paddr,
352 p_vaddr,
353 flags
354 );
355
356 let (segment_offset, segment_filesize) = segment.file_range(endian);
357
358 let sector = segment_offset..segment_offset + segment_filesize;
359
360 for section in binary.sections() {
361 let (section_offset, section_filesize) = match section.file_range() {
362 Some(range) => range,
363 None => continue,
364 };
365
366 if sector.contains_range(&(section_offset..section_offset + section_filesize)) {
367 let name = section.name()?;
368 if options.skip_sections.iter().any(|skip| skip == name) {
369 tracing::info!("Skipping section: {:?}", name);
370 continue;
371 }
372 tracing::info!("Matching section: {:?}", name);
373
374 #[cfg(feature = "hexdump")]
375 for line in hexdump::hexdump_iter(section.data()?) {
376 tracing::trace!("{}", line);
377 }
378
379 for (offset, relocation) in section.relocations() {
380 tracing::info!(
381 "Relocation: offset={}, relocation={:?}",
382 offset,
383 relocation
384 );
385 }
386
387 elf_section.push(name.to_owned());
388 }
389 }
390
391 if elf_section.is_empty() {
392 tracing::info!("Not adding segment, no matching sections found.");
393 } else {
394 let section_data =
395 &elf_data[segment_offset as usize..][..segment_filesize as usize];
396
397 extracted_data.push(ExtractedFlashData {
398 section_names: elf_section,
399 address: p_paddr as u32,
400 data: section_data,
401 });
402 }
403 }
404 }
405
406 Ok(extracted_data)
407}
408
409pub(super) fn extract_from_elf<'a>(
410 elf_data: &'a [u8],
411 options: &ElfOptions,
412) -> Result<Vec<ExtractedFlashData<'a>>, FileDownloadError> {
413 let file_kind = object::FileKind::parse(elf_data)?;
414
415 match file_kind {
416 object::FileKind::Elf32 => {
417 let elf_header = FileHeader32::<Endianness>::parse(elf_data)?;
418 let binary = object::read::elf::ElfFile::<FileHeader32<Endianness>>::parse(elf_data)?;
419 extract_from_elf_inner(elf_header, binary, elf_data, options)
420 }
421 object::FileKind::Elf64 => {
422 let elf_header = FileHeader64::<Endianness>::parse(elf_data)?;
423 let binary = object::read::elf::ElfFile::<FileHeader64<Endianness>>::parse(elf_data)?;
424 extract_from_elf_inner(elf_header, binary, elf_data, options)
425 }
426 _ => Err(FileDownloadError::Object("Unsupported file type")),
427 }
428}
429
430#[cfg(test)]
431mod tests {
432 use std::str::FromStr;
433
434 use super::FormatKind;
435
436 #[test]
437 fn parse_format() {
438 assert_eq!(FormatKind::from_str("hex"), Ok(FormatKind::Hex));
439 assert_eq!(FormatKind::from_str("Hex"), Ok(FormatKind::Hex));
440 assert_eq!(FormatKind::from_str("Ihex"), Ok(FormatKind::Hex));
441 assert_eq!(FormatKind::from_str("IHex"), Ok(FormatKind::Hex));
442 assert_eq!(FormatKind::from_str("iHex"), Ok(FormatKind::Hex));
443 assert_eq!(FormatKind::from_str("IntelHex"), Ok(FormatKind::Hex));
444 assert_eq!(FormatKind::from_str("intelhex"), Ok(FormatKind::Hex));
445 assert_eq!(FormatKind::from_str("intelHex"), Ok(FormatKind::Hex));
446 assert_eq!(FormatKind::from_str("Intelhex"), Ok(FormatKind::Hex));
447 assert_eq!(FormatKind::from_str("bin"), Ok(FormatKind::Bin));
448 assert_eq!(FormatKind::from_str("Bin"), Ok(FormatKind::Bin));
449 assert_eq!(FormatKind::from_str("binary"), Ok(FormatKind::Bin));
450 assert_eq!(FormatKind::from_str("Binary"), Ok(FormatKind::Bin));
451 assert_eq!(FormatKind::from_str("Elf"), Ok(FormatKind::Elf));
452 assert_eq!(FormatKind::from_str("elf"), Ok(FormatKind::Elf));
453 assert_eq!(FormatKind::from_str("idf"), Ok(FormatKind::Idf));
454 assert_eq!(FormatKind::from_str("espidf"), Ok(FormatKind::Idf));
455 assert_eq!(FormatKind::from_str("esp-idf"), Ok(FormatKind::Idf));
456 assert_eq!(FormatKind::from_str("ESP-IDF"), Ok(FormatKind::Idf));
457 assert_eq!(
458 FormatKind::from_str("elfbin"),
459 Err("Format 'elfbin' is unknown.".to_string())
460 );
461 assert_eq!(
462 FormatKind::from_str(""),
463 Err("Format '' is unknown.".to_string())
464 );
465 assert_eq!(
466 FormatKind::from_str("asdasdf"),
467 Err("Format 'asdasdf' is unknown.".to_string())
468 );
469 }
470}