probe_rs/flashing/
loader.rs

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
25/// Helper trait for object safety.
26pub trait ImageReader: Read + Seek {}
27impl<T> ImageReader for T where T: Read + Seek {}
28
29/// Load and parse a firmware in a particular format, and add it to the flash loader.
30///
31/// Based on the image loader, probe-rs may apply certain transformations to the firmware.
32pub trait ImageLoader {
33    /// Loads the given image.
34    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
59/// Reads the data from the binary file and adds it to the loader without splitting it into flash instructions yet.
60struct 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        // Skip the specified bytes.
70        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            // If no base address is specified use the start of the boot memory.
77            // TODO: Implement this as soon as we know targets.
78            self.0.base_address.unwrap_or_default(),
79            &buf,
80        )?;
81
82        Ok(())
83    }
84}
85
86/// Prepares the data sections that have to be loaded into flash from an ELF file.
87/// This will validate the ELF file and transform all its data into sections but no flash loader commands yet.
88struct 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
139/// Reads the HEX data segments and adds them as loadable data blocks to the loader.
140/// This does not create any flash loader instructions yet.
141struct 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
177/// Prepares the data sections that have to be loaded into flash from an UF2 file.
178/// This will validate the UF2 file and transform all its data into sections but no flash loader commands yet.
179struct 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
210/// Loads an ELF file as an esp-idf application into the loader by converting the main application
211/// to the esp-idf bootloader format, appending it to the loader along with the bootloader and
212/// partition table.
213///
214/// This does not create any flash loader instructions yet.
215struct 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            // TODO: auto-detect the crystal frequency.
278            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/// Current boot information
326#[derive(Clone, Debug, Default)]
327pub enum BootInfo {
328    /// Loaded executable has a vector table in RAM
329    FromRam {
330        /// Address of the vector table in memory
331        vector_table_addr: u64,
332        /// All cores that should be reset and halted before any RAM access
333        cores_to_reset: Vec<String>,
334    },
335    /// Executable is either not loaded yet or will be booted conventionally (from flash etc.)
336    #[default]
337    Other,
338}
339
340/// `FlashLoader` is a struct which manages the flashing of any chunks of data onto any sections of flash.
341///
342/// Use [add_data()](FlashLoader::add_data) to add a chunk of data.
343/// Once you are done adding all your data, use `commit()` to flash the data.
344/// The flash loader will make sure to select the appropriate flash region for the right data chunks.
345/// Region crossing data chunks are allowed as long as the regions are contiguous.
346pub struct FlashLoader {
347    memory_map: Vec<MemoryRegion>,
348    builder: FlashBuilder,
349
350    /// Source of the flash description,
351    /// used for diagnostics.
352    source: TargetDescriptionSource,
353    /// Relevant for manually configured RAM booted executables, available only if given loader supports it
354    vector_table_addr: Option<u64>,
355
356    read_flasher_rtt: bool,
357}
358
359impl FlashLoader {
360    /// Create a new flash loader.
361    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    /// Enable reading RTT output from the flasher.
372    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    /// Retrieve available boot information
381    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    /// Check the given address range is completely covered by the memory map,
396    /// possibly by multiple memory regions.
397    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    /// Stages a chunk of data to be programmed.
415    ///
416    /// The chunk can cross flash boundaries as long as one flash region connects to another flash region.
417    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    /// Returns whether an address will be flashed with data
436    pub fn has_data_for_address(&self, address: u64) -> bool {
437        self.builder.has_data_in_range(&(address..address + 1))
438    }
439
440    /// Reads the image according to the file format and adds it to the loader.
441    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            // Get a unique list of core architectures
452            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            // Is the image compatible with any of the cores?
467            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    /// Verifies data on the device.
482    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        // Iterate all flash algorithms we need to use and do the flashing.
501        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    /// Writes all the stored data chunks to flash.
518    ///
519    /// Requires a session with an attached target that has a known flash algorithm.
520    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        // Iterate all flash algorithms we need to use and do the flashing.
548        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            // Program the data.
567            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            // If we are booting from RAM, it is important to reset and halt to guarantee a clear state
581            // Normally, flash algorithm loader performs reset and halt - does not happen here.
582            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        // Commit RAM last, because NVM flashing overwrites RAM
601        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(&region.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            // Attach to memory and core.
629            let mut core = session.core(region_core_index).map_err(FlashError::Core)?;
630
631            // If this is a RAM only flash, the core might still be running. This can be
632            // problematic if the instruction RAM is flashed while an application is running, so
633            // the core is halted here in any case.
634            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                // Write data to memory.
650                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        // Iterate over all memory regions, and program their data.
691
692        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        // Commit NVM first
699
700        // Iterate all NvmRegions and group them by flash algorithm.
701        // This avoids loading the same algorithm twice if it's used for two regions.
702        //
703        // This also ensures correct operation when chip erase is used. We assume doing a chip erase
704        // using a given algorithm erases all regions controlled by it. Therefore, we must do
705        // chip erase once per algorithm, not once per region. Otherwise subsequent chip erases will
706        // erase previous regions' flashed contents.
707        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 we have no data in this region, ignore it.
720            // This avoids uselessly initializing and deinitializing its flash algorithm.
721            // We do not check for alias regions here, as we'll work with them if data explicitly
722            // targets them.
723            if !self.builder.has_data_in_range(&region.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                &region,
738                target,
739                core_name,
740                opt_preferred_algos,
741            )?;
742
743            // We don't usually have more than a handful of regions, linear search should be fine.
744            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 the first flash algo doesn't support erase all, disable chip erase.
773            // TODO: we could sort by support but it's unlikely to make a difference.
774            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        // Iterate all flash algorithms to initialize a few things.
790        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            // We verified NVM regions before, in flasher.program().
852            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    /// Try to find a flash algorithm for the given NvmRegion.
873    /// Errors when:
874    /// - there's no algo for the region.
875    /// - there's multiple default algos for the region.
876    /// - there's multiple fitting algos but no default.
877    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 for algorithms that contain address range
899            .filter(|&fa| {
900                fa.flash_properties
901                    .address_range
902                    .contains_range(&region.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                // filter for defaults
915                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                    // Check whether there are any preferred algorithms which are valid and which
924                    // override the default algo(s).
925                    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                    // Preferred algo overrides default.
936                    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    /// Return data chunks stored in the `FlashLoader` as pairs of address and bytes.
955    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}