1use espflash::flasher::{FlashData, FlashSettings, FlashSize};
2use espflash::image_format::idf::{IdfBootloaderFormat, check_idf_bootloader};
3use ihex::Record;
4use itertools::Itertools as _;
5use probe_rs_target::{
6 InstructionSet, MemoryRange, MemoryRegion, NvmRegion, RawFlashAlgorithm,
7 TargetDescriptionSource,
8};
9use std::io::{Read, Seek, SeekFrom};
10use std::ops::Range;
11use std::str::FromStr;
12use std::time::Duration;
13
14use super::builder::FlashBuilder;
15use super::{
16 BinOptions, DownloadOptions, ElfOptions, FileDownloadError, FlashError, Flasher, IdfOptions,
17 extract_from_elf,
18};
19use crate::Target;
20use crate::flashing::progress::ProgressOperation;
21use crate::flashing::{FlashLayout, FlashProgress, Format};
22use crate::memory::MemoryInterface;
23use crate::session::Session;
24
25pub trait ImageReader: Read + Seek {}
27impl<T> ImageReader for T where T: Read + Seek {}
28
29pub trait ImageLoader {
33 fn load(
35 &self,
36 flash_loader: &mut FlashLoader,
37 session: &mut Session,
38 file: &mut dyn ImageReader,
39 ) -> Result<(), FileDownloadError>;
40}
41
42impl ImageLoader for Format {
43 fn load(
44 &self,
45 flash_loader: &mut FlashLoader,
46 session: &mut Session,
47 file: &mut dyn ImageReader,
48 ) -> Result<(), FileDownloadError> {
49 match self {
50 Format::Bin(options) => BinLoader(options.clone()).load(flash_loader, session, file),
51 Format::Elf(options) => ElfLoader(options.clone()).load(flash_loader, session, file),
52 Format::Hex => HexLoader.load(flash_loader, session, file),
53 Format::Idf(options) => IdfLoader(options.clone()).load(flash_loader, session, file),
54 Format::Uf2 => Uf2Loader.load(flash_loader, session, file),
55 }
56 }
57}
58
59struct BinLoader(BinOptions);
61
62impl ImageLoader for BinLoader {
63 fn load(
64 &self,
65 flash_loader: &mut FlashLoader,
66 _session: &mut Session,
67 file: &mut dyn ImageReader,
68 ) -> Result<(), FileDownloadError> {
69 file.seek(SeekFrom::Start(u64::from(self.0.skip)))?;
71
72 let mut buf = Vec::new();
73 file.read_to_end(&mut buf)?;
74
75 flash_loader.add_data(
76 self.0.base_address.unwrap_or_default(),
79 &buf,
80 )?;
81
82 Ok(())
83 }
84}
85
86struct ElfLoader(ElfOptions);
89
90impl ImageLoader for ElfLoader {
91 fn load(
92 &self,
93 flash_loader: &mut FlashLoader,
94 session: &mut Session,
95 file: &mut dyn ImageReader,
96 ) -> Result<(), FileDownloadError> {
97 const VECTOR_TABLE_SECTION_NAME: &str = ".vector_table";
98 let mut elf_buffer = Vec::new();
99 file.read_to_end(&mut elf_buffer)?;
100
101 check_chip_compatibility_from_elf_metadata(session, &elf_buffer)?;
102 let extracted_data = extract_from_elf(&elf_buffer, &self.0)?;
103
104 if extracted_data.is_empty() {
105 tracing::warn!("No loadable segments were found in the ELF file.");
106 return Err(FileDownloadError::NoLoadableSegments);
107 }
108
109 tracing::info!("Found {} loadable sections:", extracted_data.len());
110
111 for section in &extracted_data {
112 let source = match section.section_names.len() {
113 0 => "Unknown",
114 1 => section.section_names[0].as_str(),
115 _ => "Multiple sections",
116 };
117
118 if source == VECTOR_TABLE_SECTION_NAME {
119 flash_loader.set_vector_table_addr(section.address as _);
120 }
121
122 tracing::info!(
123 " {} at {:#010X} ({} byte{})",
124 source,
125 section.address,
126 section.data.len(),
127 if section.data.len() == 1 { "" } else { "s" }
128 );
129 }
130
131 for data in extracted_data {
132 flash_loader.add_data(data.address.into(), data.data)?;
133 }
134
135 Ok(())
136 }
137}
138
139struct HexLoader;
142
143impl ImageLoader for HexLoader {
144 fn load(
145 &self,
146 flash_loader: &mut FlashLoader,
147 _session: &mut Session,
148 file: &mut dyn ImageReader,
149 ) -> Result<(), FileDownloadError> {
150 let mut base_address = 0;
151
152 let mut data = String::new();
153 file.read_to_string(&mut data)?;
154
155 for record in ihex::Reader::new(&data) {
156 match record? {
157 Record::Data { offset, value } => {
158 let offset = base_address + offset as u64;
159 flash_loader.add_data(offset, &value)?;
160 }
161 Record::ExtendedSegmentAddress(address) => {
162 base_address = (address as u64) * 16;
163 }
164 Record::ExtendedLinearAddress(address) => {
165 base_address = (address as u64) << 16;
166 }
167
168 Record::EndOfFile
169 | Record::StartSegmentAddress { .. }
170 | Record::StartLinearAddress(_) => {}
171 }
172 }
173 Ok(())
174 }
175}
176
177struct Uf2Loader;
180
181impl ImageLoader for Uf2Loader {
182 fn load(
183 &self,
184 flash_loader: &mut FlashLoader,
185 _session: &mut Session,
186 file: &mut dyn ImageReader,
187 ) -> Result<(), FileDownloadError> {
188 let mut uf2_buffer = Vec::new();
189 file.read_to_end(&mut uf2_buffer)?;
190
191 let (converted, family_to_target) = uf2_decode::convert_from_uf2(&uf2_buffer).unwrap();
192 let target_addresses = family_to_target.values();
193 let num_sections = family_to_target.len();
194
195 if let Some(target_address) = target_addresses.min() {
196 tracing::info!("Found {} loadable sections:", num_sections);
197 if num_sections > 1 {
198 tracing::warn!("More than 1 section found in UF2 file. Using first section.");
199 }
200 flash_loader.add_data(*target_address, &converted)?;
201
202 Ok(())
203 } else {
204 tracing::warn!("No loadable segments were found in the UF2 file.");
205 Err(FileDownloadError::NoLoadableSegments)
206 }
207 }
208}
209
210struct IdfLoader(IdfOptions);
216
217impl ImageLoader for IdfLoader {
218 fn load(
219 &self,
220 flash_loader: &mut FlashLoader,
221 session: &mut Session,
222 file: &mut dyn ImageReader,
223 ) -> Result<(), FileDownloadError> {
224 let target = session.target();
225 let target_name = target
226 .name
227 .split_once('-')
228 .map(|(name, _)| name)
229 .unwrap_or(target.name.as_str());
230 let chip = espflash::target::Chip::from_str(target_name)
231 .map_err(|_| FileDownloadError::IdfUnsupported(target.name.to_string()))?;
232
233 let mut algo = Flasher::new(target, 0, &target.flash_algorithms[0])
234 .map_err(FileDownloadError::Flash)?;
235
236 session
237 .core(0)
238 .unwrap()
239 .reset_and_halt(Duration::from_millis(500))
240 .map_err(FlashError::ResetAndHalt)
241 .map_err(FileDownloadError::FlashSizeDetection)?;
242
243 let flash_size_result = algo
244 .run_verify(session, &mut FlashProgress::empty(), |flasher, _| {
245 flasher.read_flash_size()
246 })
247 .map_err(FileDownloadError::FlashSizeDetection)?;
248
249 let flash_size = match flash_size_result {
250 0x40000 => Some(FlashSize::_256Kb),
251 0x80000 => Some(FlashSize::_512Kb),
252 0x100000 => Some(FlashSize::_1Mb),
253 0x200000 => Some(FlashSize::_2Mb),
254 0x400000 => Some(FlashSize::_4Mb),
255 0x800000 => Some(FlashSize::_8Mb),
256 0x1000000 => Some(FlashSize::_16Mb),
257 0x2000000 => Some(FlashSize::_32Mb),
258 0x4000000 => Some(FlashSize::_64Mb),
259 0x8000000 => Some(FlashSize::_128Mb),
260 0x10000000 => Some(FlashSize::_256Mb),
261 _ => None,
262 };
263
264 let flash_data = FlashData::new(
265 {
266 let mut settings = FlashSettings::default();
267
268 settings.size = flash_size;
269 settings.freq = self.0.flash_frequency;
270 settings.mode = self.0.flash_mode;
271
272 settings
273 },
274 0,
275 None,
276 chip,
277 chip.default_xtal_frequency(),
279 );
280
281 let mut elf_buffer = Vec::new();
282 file.read_to_end(&mut elf_buffer)?;
283
284 check_idf_bootloader(&elf_buffer).map_err(|e| {
285 FileDownloadError::Idf(espflash::Error::AppDescriptorNotPresent(e.to_string()))
286 })?;
287 check_chip_compatibility_from_elf_metadata(session, &elf_buffer)?;
288
289 let image = IdfBootloaderFormat::new(
290 &elf_buffer,
291 &flash_data,
292 self.0.partition_table.as_deref(),
293 self.0.bootloader.as_deref(),
294 None,
295 self.0.target_app_partition.as_deref(),
296 )?;
297
298 for data in image.flash_segments() {
299 flash_loader.add_data(data.addr.into(), &data.data)?;
300 }
301
302 Ok(())
303 }
304}
305
306fn check_chip_compatibility_from_elf_metadata(
307 session: &Session,
308 elf_data: &[u8],
309) -> Result<(), FileDownloadError> {
310 let esp_metadata = espflash::image_format::Metadata::from_bytes(Some(elf_data));
311
312 if let Some(chip_name) = esp_metadata.chip_name() {
313 let target = session.target();
314 if chip_name != target.name {
315 return Err(FileDownloadError::IncompatibleImageChip {
316 target: target.name.clone(),
317 image_chips: vec![chip_name.to_string()],
318 });
319 }
320 }
321
322 Ok(())
323}
324
325#[derive(Clone, Debug, Default)]
327pub enum BootInfo {
328 FromRam {
330 vector_table_addr: u64,
332 cores_to_reset: Vec<String>,
334 },
335 #[default]
337 Other,
338}
339
340pub struct FlashLoader {
347 memory_map: Vec<MemoryRegion>,
348 builder: FlashBuilder,
349
350 source: TargetDescriptionSource,
353 vector_table_addr: Option<u64>,
355
356 read_flasher_rtt: bool,
357}
358
359impl FlashLoader {
360 pub fn new(memory_map: Vec<MemoryRegion>, source: TargetDescriptionSource) -> Self {
362 Self {
363 memory_map,
364 builder: FlashBuilder::new(),
365 source,
366 vector_table_addr: None,
367 read_flasher_rtt: false,
368 }
369 }
370
371 pub fn read_rtt_output(&mut self, read: bool) {
373 self.read_flasher_rtt = read;
374 }
375
376 fn set_vector_table_addr(&mut self, vector_table_addr: u64) {
377 self.vector_table_addr = Some(vector_table_addr);
378 }
379
380 pub fn boot_info(&self) -> BootInfo {
382 let Some(vector_table_addr) = self.vector_table_addr else {
383 return BootInfo::Other;
384 };
385
386 match Self::get_region_for_address(&self.memory_map, vector_table_addr) {
387 Some(MemoryRegion::Ram(region)) => BootInfo::FromRam {
388 vector_table_addr,
389 cores_to_reset: region.cores.clone(),
390 },
391 _ => BootInfo::Other,
392 }
393 }
394
395 fn check_data_in_memory_map(&mut self, range: Range<u64>) -> Result<(), FlashError> {
398 let mut address = range.start;
399 while address < range.end {
400 match Self::get_region_for_address(&self.memory_map, address) {
401 Some(MemoryRegion::Nvm(region)) => address = region.range.end,
402 Some(MemoryRegion::Ram(region)) => address = region.range.end,
403 _ => {
404 return Err(FlashError::NoSuitableNvm {
405 range,
406 description_source: self.source.clone(),
407 });
408 }
409 }
410 }
411 Ok(())
412 }
413
414 pub fn add_data(&mut self, address: u64, data: &[u8]) -> Result<(), FlashError> {
418 tracing::trace!(
419 "Adding data at address {:#010x} with size {} bytes",
420 address,
421 data.len()
422 );
423
424 self.check_data_in_memory_map(address..address + data.len() as u64)?;
425 self.builder.add_data(address, data)
426 }
427
428 pub(super) fn get_region_for_address(
429 memory_map: &[MemoryRegion],
430 address: u64,
431 ) -> Option<&MemoryRegion> {
432 memory_map.iter().find(|region| region.contains(address))
433 }
434
435 pub fn has_data_for_address(&self, address: u64) -> bool {
437 self.builder.has_data_in_range(&(address..address + 1))
438 }
439
440 pub fn load_image<T: Read + Seek>(
442 &mut self,
443 session: &mut Session,
444 file: &mut T,
445 format: Format,
446 image_instruction_set: Option<InstructionSet>,
447 ) -> Result<(), FileDownloadError> {
448 if let Some(instr_set) = image_instruction_set {
449 let mut target_archs = Vec::with_capacity(session.list_cores().len());
450
451 for (core, _) in session.list_cores() {
453 match session.core(core) {
454 Ok(mut core) => {
455 if let Ok(set) = core.instruction_set()
456 && !target_archs.contains(&set)
457 {
458 target_archs.push(set);
459 }
460 }
461 Err(crate::Error::CoreDisabled(_)) => continue,
462 Err(error) => return Err(FileDownloadError::Other(error)),
463 }
464 }
465
466 if !target_archs
468 .iter()
469 .any(|target| target.is_compatible(instr_set))
470 {
471 return Err(FileDownloadError::IncompatibleImage {
472 target: target_archs,
473 image: instr_set,
474 });
475 }
476 }
477
478 format.load(self, session, file)
479 }
480
481 pub fn verify(
483 &self,
484 session: &mut Session,
485 progress: &mut FlashProgress<'_>,
486 ) -> Result<(), FlashError> {
487 let mut algos = self.prepare_plan(session, false, &[])?;
488
489 for flasher in algos.iter_mut() {
490 let mut program_size = 0;
491 for region in flasher.regions.iter_mut() {
492 program_size += region
493 .data
494 .encoder(flasher.flash_algorithm.transfer_encoding, true)
495 .program_size();
496 }
497 progress.add_progress_bar(ProgressOperation::Verify, Some(program_size));
498 }
499
500 for mut flasher in algos {
502 tracing::debug!(
503 "Verifying ranges for algo: {}",
504 flasher.flash_algorithm.name
505 );
506
507 if !flasher.verify(session, progress, true)? {
508 return Err(FlashError::Verify);
509 }
510 }
511
512 self.verify_ram(session)?;
513
514 Ok(())
515 }
516
517 pub fn commit(
521 &self,
522 session: &mut Session,
523 mut options: DownloadOptions,
524 ) -> Result<(), FlashError> {
525 tracing::debug!("Committing FlashLoader!");
526 let mut algos = self.prepare_plan(
527 session,
528 options.keep_unwritten_bytes,
529 &options.preferred_algos,
530 )?;
531
532 if options.dry_run {
533 tracing::info!("Skipping programming, dry run!");
534
535 options.progress.failed_filling();
536 options.progress.failed_erasing();
537 options.progress.failed_programming();
538
539 return Ok(());
540 }
541
542 self.initialize(&mut algos, session, &mut options)?;
543
544 let mut do_chip_erase = options.do_chip_erase;
545 let mut did_chip_erase = false;
546
547 for mut flasher in algos {
549 tracing::debug!("Flashing ranges for algo: {}", flasher.flash_algorithm.name);
550
551 if do_chip_erase {
552 tracing::debug!(" Doing chip erase...");
553 flasher.run_erase_all(session, &mut options.progress)?;
554 do_chip_erase = false;
555 did_chip_erase = true;
556 }
557
558 let mut do_use_double_buffering = flasher.double_buffering_supported();
559 if do_use_double_buffering && options.disable_double_buffering {
560 tracing::info!(
561 "Disabled double-buffering support for loader via passed option, though target supports it."
562 );
563 do_use_double_buffering = false;
564 }
565
566 flasher.program(
568 session,
569 &mut options.progress,
570 options.keep_unwritten_bytes,
571 do_use_double_buffering,
572 options.skip_erase || did_chip_erase,
573 options.verify,
574 )?;
575 }
576
577 tracing::debug!("Committing RAM!");
578
579 if let BootInfo::FromRam { cores_to_reset, .. } = self.boot_info() {
580 tracing::debug!(
583 " -- action: vector table in RAM, assuming RAM boot, resetting and halting"
584 );
585 for (core_to_reset_index, _) in session
586 .target()
587 .cores
588 .clone()
589 .iter()
590 .enumerate()
591 .filter(|(_, c)| cores_to_reset.contains(&c.name))
592 {
593 session
594 .core(core_to_reset_index)
595 .and_then(|mut core| core.reset_and_halt(Duration::from_millis(500)))
596 .map_err(FlashError::Core)?;
597 }
598 }
599
600 for region in self
602 .memory_map
603 .iter()
604 .filter_map(MemoryRegion::as_ram_region)
605 {
606 let ranges_in_region: Vec<_> = self.builder.data_in_range(®ion.range).collect();
607
608 if ranges_in_region.is_empty() {
609 continue;
610 }
611
612 tracing::debug!(
613 " region: {:#010X?} ({} bytes)",
614 region.range,
615 region.range.end - region.range.start
616 );
617
618 let region_core_index = session
619 .target()
620 .core_index_by_name(
621 region
622 .cores
623 .first()
624 .ok_or_else(|| FlashError::NoRamCoreAccess(region.clone()))?,
625 )
626 .unwrap();
627
628 let mut core = session.core(region_core_index).map_err(FlashError::Core)?;
630
631 if !core.core_halted().map_err(FlashError::Core)? {
635 tracing::debug!(
636 " -- action: core is not halted and RAM is being written, halting"
637 );
638 core.halt(Duration::from_millis(500))
639 .map_err(FlashError::Core)?;
640 }
641
642 for (address, data) in ranges_in_region {
643 tracing::debug!(
644 " -- writing: {:#010X}..{:#010X} ({} bytes)",
645 address,
646 address + data.len() as u64,
647 data.len()
648 );
649 core.write(address, data).map_err(FlashError::Core)?;
651 }
652 }
653
654 if options.verify {
655 self.verify_ram(session)?;
656 }
657
658 Ok(())
659 }
660
661 fn prepare_plan(
662 &self,
663 session: &mut Session,
664 restore_unwritten_bytes: bool,
665 opt_preferred_algos: &[String],
666 ) -> Result<Vec<Flasher>, FlashError> {
667 tracing::debug!("Contents of builder:");
668 for (&address, data) in &self.builder.data {
669 tracing::debug!(
670 " data: {:#010X}..{:#010X} ({} bytes)",
671 address,
672 address + data.len() as u64,
673 data.len()
674 );
675 }
676
677 tracing::debug!("Flash algorithms:");
678 for algorithm in &session.target().flash_algorithms {
679 let Range { start, end } = algorithm.flash_properties.address_range;
680
681 tracing::debug!(
682 " algo {}: {:#010X}..{:#010X} ({} bytes)",
683 algorithm.name,
684 start,
685 end,
686 end - start
687 );
688 }
689
690 if self.memory_map != session.target().memory_map {
693 tracing::warn!("Memory map of flash loader does not match memory map of target!");
694 }
695
696 let mut algos = Vec::<Flasher>::new();
697
698 tracing::debug!("Regions:");
708 for region in self
709 .memory_map
710 .iter()
711 .filter_map(MemoryRegion::as_nvm_region)
712 {
713 tracing::debug!(
714 " region: {:#010X?} ({} bytes)",
715 region.range,
716 region.range.end - region.range.start
717 );
718
719 if !self.builder.has_data_in_range(®ion.range) {
724 tracing::debug!(" -- empty, ignoring!");
725 continue;
726 }
727
728 let region = region.clone();
729
730 let Some(core_name) = region.cores.first() else {
731 return Err(FlashError::NoNvmCoreAccess(region));
732 };
733
734 let target = session.target();
735 let core = target.core_index_by_name(core_name).unwrap();
736 let algo = Self::get_flash_algorithm_for_region(
737 ®ion,
738 target,
739 core_name,
740 opt_preferred_algos,
741 )?;
742
743 tracing::debug!(" -- using algorithm: {}", algo.name);
745 if let Some(entry) = algos
746 .iter_mut()
747 .find(|entry| entry.flash_algorithm.name == algo.name && entry.core_index == core)
748 {
749 entry.add_region(region, &self.builder, restore_unwritten_bytes)?;
750 } else {
751 let mut flasher = Flasher::new(target, core, algo)?;
752 flasher.add_region(region, &self.builder, restore_unwritten_bytes)?;
753
754 flasher.read_rtt_output(self.read_flasher_rtt);
755
756 algos.push(flasher);
757 }
758 }
759
760 Ok(algos)
761 }
762
763 fn initialize(
764 &self,
765 algos: &mut [Flasher],
766 session: &mut Session,
767 options: &mut DownloadOptions,
768 ) -> Result<(), FlashError> {
769 let mut phases = vec![];
770
771 for flasher in algos.iter() {
772 if options.do_chip_erase && !flasher.is_chip_erase_supported(session) {
775 options.do_chip_erase = false;
776 tracing::warn!(
777 "Chip erase was the selected method to erase the sectors but this chip does not support chip erases (yet)."
778 );
779 tracing::warn!("A manual sector erase will be performed.");
780 }
781 }
782
783 if options.do_chip_erase {
784 options
785 .progress
786 .add_progress_bar(ProgressOperation::Erase, None);
787 }
788
789 for flasher in algos.iter_mut() {
791 let mut phase_layout = FlashLayout::default();
792
793 let mut fill_size = 0;
794 let mut erase_size = 0;
795 let mut program_size = 0;
796
797 for region in flasher.regions.iter_mut() {
798 let layout = region.flash_layout();
799 phase_layout.merge_from(layout.clone());
800
801 erase_size += layout.sectors().iter().map(|s| s.size()).sum::<u64>();
802 fill_size += layout.fills().iter().map(|s| s.size()).sum::<u64>();
803 program_size += region
804 .data
805 .encoder(
806 flasher.flash_algorithm.transfer_encoding,
807 !options.keep_unwritten_bytes,
808 )
809 .program_size();
810 }
811
812 if options.keep_unwritten_bytes {
813 options
814 .progress
815 .add_progress_bar(ProgressOperation::Fill, Some(fill_size));
816 }
817 if !options.do_chip_erase {
818 options
819 .progress
820 .add_progress_bar(ProgressOperation::Erase, Some(erase_size));
821 }
822 options
823 .progress
824 .add_progress_bar(ProgressOperation::Program, Some(program_size));
825 if options.verify {
826 options
827 .progress
828 .add_progress_bar(ProgressOperation::Verify, Some(program_size));
829 }
830
831 phases.push(phase_layout);
832 }
833
834 options.progress.initialized(phases);
835
836 Ok(())
837 }
838
839 fn verify_ram(&self, session: &mut Session) -> Result<(), FlashError> {
840 tracing::debug!("Verifying RAM!");
841 for (&address, data) in &self.builder.data {
842 tracing::debug!(
843 " data: {:#010X}..{:#010X} ({} bytes)",
844 address,
845 address + data.len() as u64,
846 data.len()
847 );
848
849 let associated_region = session.target().memory_region_by_address(address).unwrap();
850
851 if !associated_region.is_ram() {
853 continue;
854 }
855
856 let core_name = associated_region.cores().first().unwrap();
857 let core_index = session.target().core_index_by_name(core_name).unwrap();
858 let mut core = session.core(core_index).map_err(FlashError::Core)?;
859
860 let mut written_data = vec![0; data.len()];
861 core.read(address, &mut written_data)
862 .map_err(FlashError::Core)?;
863
864 if data != &written_data {
865 return Err(FlashError::Verify);
866 }
867 }
868
869 Ok(())
870 }
871
872 pub(crate) fn get_flash_algorithm_for_region<'a>(
878 region: &NvmRegion,
879 target: &'a Target,
880 core_name: &String,
881 preferred_algos: &[String],
882 ) -> Result<&'a RawFlashAlgorithm, FlashError> {
883 let available = &target.flash_algorithms;
884 tracing::debug!("Available algorithms:");
885 for algorithm in available {
886 tracing::debug!(
887 "Algorithm: {} for {:?} @ 0x{:08x} - 0x{:08x} default? {}",
888 algorithm.name,
889 algorithm.cores,
890 algorithm.flash_properties.address_range.start,
891 algorithm.flash_properties.address_range.end,
892 algorithm.default
893 );
894 }
895 let algorithms = target
896 .flash_algorithms
897 .iter()
898 .filter(|&fa| {
900 fa.flash_properties
901 .address_range
902 .contains_range(®ion.range)
903 && (fa.cores.is_empty() || fa.cores.contains(core_name))
904 })
905 .collect::<Vec<_>>();
906
907 match algorithms.len() {
908 0 => Err(FlashError::NoFlashLoaderAlgorithmAttached {
909 range: region.range.clone(),
910 name: target.name.clone(),
911 }),
912 1 => Ok(algorithms[0]),
913 _ => {
914 let defaults = algorithms
916 .iter()
917 .filter(|&fa| fa.default)
918 .collect::<Vec<_>>();
919
920 if !preferred_algos.is_empty() {
921 tracing::debug!("selecting preferred algorithm from: {:?}", preferred_algos);
922 let mut preferred_and_valid_algos = Vec::new();
923 for algo in algorithms.iter() {
926 if preferred_algos.iter().contains(&algo.name) {
927 preferred_and_valid_algos.push(algo);
928 }
929 }
930 if preferred_and_valid_algos.len() > 1 {
931 return Err(FlashError::MultiplePreferredAlgos {
932 region: region.clone(),
933 });
934 }
935 if preferred_and_valid_algos.len() == 1 {
937 return Ok(preferred_and_valid_algos[0]);
938 }
939 }
940
941 match defaults.len() {
942 0 => Err(FlashError::MultipleFlashLoaderAlgorithmsNoDefault {
943 region: region.clone(),
944 }),
945 1 => Ok(defaults[0]),
946 _ => Err(FlashError::MultipleDefaultFlashLoaderAlgorithms {
947 region: region.clone(),
948 }),
949 }
950 }
951 }
952 }
953
954 pub fn data(&self) -> impl Iterator<Item = (u64, &[u8])> {
956 self.builder
957 .data
958 .iter()
959 .map(|(address, data)| (*address, data.as_slice()))
960 }
961}