probe_rs/flashing/
loader.rs

1use espflash::flasher::{FlashData, FlashSettings, FlashSize};
2use espflash::image_format::idf::IdfBootloaderFormat;
3use ihex::Record;
4use probe_rs_target::{
5    InstructionSet, MemoryRange, MemoryRegion, NvmRegion, RawFlashAlgorithm,
6    TargetDescriptionSource,
7};
8use std::io::{Read, Seek, SeekFrom};
9use std::ops::Range;
10use std::str::FromStr;
11use std::time::Duration;
12
13use super::builder::FlashBuilder;
14use super::{
15    BinOptions, DownloadOptions, ElfOptions, FileDownloadError, FlashError, Flasher, IdfOptions,
16    extract_from_elf,
17};
18use crate::Target;
19use crate::config::DebugSequence;
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        let extracted_data = extract_from_elf(&elf_buffer, &self.0)?;
102
103        if extracted_data.is_empty() {
104            tracing::warn!("No loadable segments were found in the ELF file.");
105            return Err(FileDownloadError::NoLoadableSegments);
106        }
107
108        tracing::info!("Found {} loadable sections:", extracted_data.len());
109
110        for section in &extracted_data {
111            let source = match section.section_names.len() {
112                0 => "Unknown",
113                1 => section.section_names[0].as_str(),
114                _ => "Multiple sections",
115            };
116
117            if source == VECTOR_TABLE_SECTION_NAME {
118                flash_loader.set_vector_table_addr(section.address as _);
119            }
120
121            tracing::info!(
122                "    {} at {:#010X} ({} byte{})",
123                source,
124                section.address,
125                section.data.len(),
126                if section.data.len() == 1 { "" } else { "s" }
127            );
128        }
129
130        for data in extracted_data {
131            flash_loader.add_data(data.address.into(), data.data)?;
132        }
133
134        Ok(())
135    }
136}
137
138/// Reads the HEX data segments and adds them as loadable data blocks to the loader.
139/// This does not create any flash loader instructions yet.
140struct HexLoader;
141
142impl ImageLoader for HexLoader {
143    fn load(
144        &self,
145        flash_loader: &mut FlashLoader,
146        _session: &mut Session,
147        file: &mut dyn ImageReader,
148    ) -> Result<(), FileDownloadError> {
149        let mut base_address = 0;
150
151        let mut data = String::new();
152        file.read_to_string(&mut data)?;
153
154        for record in ihex::Reader::new(&data) {
155            match record? {
156                Record::Data { offset, value } => {
157                    let offset = base_address + offset as u64;
158                    flash_loader.add_data(offset, &value)?;
159                }
160                Record::ExtendedSegmentAddress(address) => {
161                    base_address = (address as u64) * 16;
162                }
163                Record::ExtendedLinearAddress(address) => {
164                    base_address = (address as u64) << 16;
165                }
166
167                Record::EndOfFile
168                | Record::StartSegmentAddress { .. }
169                | Record::StartLinearAddress(_) => {}
170            }
171        }
172        Ok(())
173    }
174}
175
176/// Prepares the data sections that have to be loaded into flash from an UF2 file.
177/// This will validate the UF2 file and transform all its data into sections but no flash loader commands yet.
178struct Uf2Loader;
179
180impl ImageLoader for Uf2Loader {
181    fn load(
182        &self,
183        flash_loader: &mut FlashLoader,
184        _session: &mut Session,
185        file: &mut dyn ImageReader,
186    ) -> Result<(), FileDownloadError> {
187        let mut uf2_buffer = Vec::new();
188        file.read_to_end(&mut uf2_buffer)?;
189
190        let (converted, family_to_target) = uf2_decode::convert_from_uf2(&uf2_buffer).unwrap();
191        let target_addresses = family_to_target.values();
192        let num_sections = family_to_target.len();
193
194        if let Some(target_address) = target_addresses.min() {
195            tracing::info!("Found {} loadable sections:", num_sections);
196            if num_sections > 1 {
197                tracing::warn!("More than 1 section found in UF2 file.  Using first section.");
198            }
199            flash_loader.add_data(*target_address, &converted)?;
200
201            Ok(())
202        } else {
203            tracing::warn!("No loadable segments were found in the UF2 file.");
204            Err(FileDownloadError::NoLoadableSegments)
205        }
206    }
207}
208
209/// Loads an ELF file as an esp-idf application into the loader by converting the main application
210/// to the esp-idf bootloader format, appending it to the loader along with the bootloader and
211/// partition table.
212///
213/// This does not create any flash loader instructions yet.
214struct IdfLoader(IdfOptions);
215
216impl ImageLoader for IdfLoader {
217    fn load(
218        &self,
219        flash_loader: &mut FlashLoader,
220        session: &mut Session,
221        file: &mut dyn ImageReader,
222    ) -> Result<(), FileDownloadError> {
223        let target = session.target();
224        let target_name = target
225            .name
226            .split_once('-')
227            .map(|(name, _)| name)
228            .unwrap_or(target.name.as_str());
229        let chip = espflash::target::Chip::from_str(target_name)
230            .map_err(|_| FileDownloadError::IdfUnsupported(target.name.to_string()))?;
231
232        let flash_size_result = session.halted_access(|session| {
233            // Figure out flash size from the memory map. We need a different bootloader for each size.
234            match session.target().debug_sequence.clone() {
235                DebugSequence::Riscv(sequence) => sequence.detect_flash_size(session),
236                DebugSequence::Xtensa(sequence) => sequence.detect_flash_size(session),
237                DebugSequence::Arm(_) => panic!("There are no ARM ESP targets."),
238            }
239        });
240
241        let flash_size = match flash_size_result.map_err(FileDownloadError::FlashSizeDetection)? {
242            Some(0x40000) => Some(FlashSize::_256Kb),
243            Some(0x80000) => Some(FlashSize::_512Kb),
244            Some(0x100000) => Some(FlashSize::_1Mb),
245            Some(0x200000) => Some(FlashSize::_2Mb),
246            Some(0x400000) => Some(FlashSize::_4Mb),
247            Some(0x800000) => Some(FlashSize::_8Mb),
248            Some(0x1000000) => Some(FlashSize::_16Mb),
249            Some(0x2000000) => Some(FlashSize::_32Mb),
250            Some(0x4000000) => Some(FlashSize::_64Mb),
251            Some(0x8000000) => Some(FlashSize::_128Mb),
252            Some(0x10000000) => Some(FlashSize::_256Mb),
253            _ => None,
254        };
255
256        let flash_data = FlashData::new(
257            {
258                let mut settings = FlashSettings::default();
259
260                settings.size = flash_size;
261
262                settings
263            },
264            0,
265            None,
266            chip,
267            // TODO: auto-detect the crystal frequency.
268            chip.default_xtal_frequency(),
269        );
270
271        let mut buf = Vec::new();
272        file.read_to_end(&mut buf)?;
273        let image = IdfBootloaderFormat::new(
274            &buf,
275            &flash_data,
276            self.0.partition_table.as_deref(),
277            self.0.bootloader.as_deref(),
278            None,
279            self.0.target_app_partition.as_deref(),
280        )?;
281
282        for data in image.flash_segments() {
283            flash_loader.add_data(data.addr.into(), &data.data)?;
284        }
285
286        Ok(())
287    }
288}
289
290/// Current boot information
291#[derive(Clone, Debug, Default)]
292pub enum BootInfo {
293    /// Loaded executable has a vector table in RAM
294    FromRam {
295        /// Address of the vector table in memory
296        vector_table_addr: u64,
297        /// All cores that should be reset and halted before any RAM access
298        cores_to_reset: Vec<String>,
299    },
300    /// Executable is either not loaded yet or will be booted conventionally (from flash etc.)
301    #[default]
302    Other,
303}
304
305/// `FlashLoader` is a struct which manages the flashing of any chunks of data onto any sections of flash.
306///
307/// Use [add_data()](FlashLoader::add_data) to add a chunk of data.
308/// Once you are done adding all your data, use `commit()` to flash the data.
309/// The flash loader will make sure to select the appropriate flash region for the right data chunks.
310/// Region crossing data chunks are allowed as long as the regions are contiguous.
311pub struct FlashLoader {
312    memory_map: Vec<MemoryRegion>,
313    builder: FlashBuilder,
314
315    /// Source of the flash description,
316    /// used for diagnostics.
317    source: TargetDescriptionSource,
318    /// Relevant for manually configured RAM booted executables, available only if given loader supports it
319    vector_table_addr: Option<u64>,
320}
321
322impl FlashLoader {
323    /// Create a new flash loader.
324    pub fn new(memory_map: Vec<MemoryRegion>, source: TargetDescriptionSource) -> Self {
325        Self {
326            memory_map,
327            builder: FlashBuilder::new(),
328            source,
329            vector_table_addr: None,
330        }
331    }
332
333    fn set_vector_table_addr(&mut self, vector_table_addr: u64) {
334        self.vector_table_addr = Some(vector_table_addr);
335    }
336
337    /// Retrieve available boot information
338    pub fn boot_info(&self) -> BootInfo {
339        let Some(vector_table_addr) = self.vector_table_addr else {
340            return BootInfo::Other;
341        };
342
343        match Self::get_region_for_address(&self.memory_map, vector_table_addr) {
344            Some(MemoryRegion::Ram(region)) => BootInfo::FromRam {
345                vector_table_addr,
346                cores_to_reset: region.cores.clone(),
347            },
348            _ => BootInfo::Other,
349        }
350    }
351
352    /// Check the given address range is completely covered by the memory map,
353    /// possibly by multiple memory regions.
354    fn check_data_in_memory_map(&mut self, range: Range<u64>) -> Result<(), FlashError> {
355        let mut address = range.start;
356        while address < range.end {
357            match Self::get_region_for_address(&self.memory_map, address) {
358                Some(MemoryRegion::Nvm(region)) => address = region.range.end,
359                Some(MemoryRegion::Ram(region)) => address = region.range.end,
360                _ => {
361                    return Err(FlashError::NoSuitableNvm {
362                        range,
363                        description_source: self.source.clone(),
364                    });
365                }
366            }
367        }
368        Ok(())
369    }
370
371    /// Stages a chunk of data to be programmed.
372    ///
373    /// The chunk can cross flash boundaries as long as one flash region connects to another flash region.
374    pub fn add_data(&mut self, address: u64, data: &[u8]) -> Result<(), FlashError> {
375        tracing::trace!(
376            "Adding data at address {:#010x} with size {} bytes",
377            address,
378            data.len()
379        );
380
381        self.check_data_in_memory_map(address..address + data.len() as u64)?;
382        self.builder.add_data(address, data)
383    }
384
385    pub(super) fn get_region_for_address(
386        memory_map: &[MemoryRegion],
387        address: u64,
388    ) -> Option<&MemoryRegion> {
389        memory_map.iter().find(|region| region.contains(address))
390    }
391
392    /// Returns whether an address will be flashed with data
393    pub fn has_data_for_address(&self, address: u64) -> bool {
394        self.builder.has_data_in_range(&(address..address + 1))
395    }
396
397    /// Reads the image according to the file format and adds it to the loader.
398    pub fn load_image<T: Read + Seek>(
399        &mut self,
400        session: &mut Session,
401        file: &mut T,
402        format: Format,
403        image_instruction_set: Option<InstructionSet>,
404    ) -> Result<(), FileDownloadError> {
405        if let Some(instr_set) = image_instruction_set {
406            let mut target_archs = Vec::with_capacity(session.list_cores().len());
407
408            // Get a unique list of core architectures
409            for (core, _) in session.list_cores() {
410                match session.core(core) {
411                    Ok(mut core) => {
412                        if let Ok(set) = core.instruction_set()
413                            && !target_archs.contains(&set)
414                        {
415                            target_archs.push(set);
416                        }
417                    }
418                    Err(crate::Error::CoreDisabled(_)) => continue,
419                    Err(error) => return Err(FileDownloadError::Other(error)),
420                }
421            }
422
423            // Is the image compatible with any of the cores?
424            if !target_archs
425                .iter()
426                .any(|target| target.is_compatible(instr_set))
427            {
428                return Err(FileDownloadError::IncompatibleImage {
429                    target: target_archs,
430                    image: instr_set,
431                });
432            }
433        }
434
435        format.load(self, session, file)
436    }
437
438    /// Verifies data on the device.
439    pub fn verify(
440        &self,
441        session: &mut Session,
442        progress: &mut FlashProgress<'_>,
443    ) -> Result<(), FlashError> {
444        let mut algos = self.prepare_plan(session, false)?;
445
446        for flasher in algos.iter_mut() {
447            let mut program_size = 0;
448            for region in flasher.regions.iter_mut() {
449                program_size += region
450                    .data
451                    .encoder(flasher.flash_algorithm.transfer_encoding, true)
452                    .program_size();
453            }
454            progress.add_progress_bar(ProgressOperation::Verify, Some(program_size));
455        }
456
457        // Iterate all flash algorithms we need to use and do the flashing.
458        for mut flasher in algos {
459            tracing::debug!(
460                "Verifying ranges for algo: {}",
461                flasher.flash_algorithm.name
462            );
463
464            if !flasher.verify(session, progress, true)? {
465                return Err(FlashError::Verify);
466            }
467        }
468
469        self.verify_ram(session)?;
470
471        Ok(())
472    }
473
474    /// Writes all the stored data chunks to flash.
475    ///
476    /// Requires a session with an attached target that has a known flash algorithm.
477    pub fn commit(
478        &self,
479        session: &mut Session,
480        mut options: DownloadOptions,
481    ) -> Result<(), FlashError> {
482        tracing::debug!("Committing FlashLoader!");
483        let mut algos = self.prepare_plan(session, options.keep_unwritten_bytes)?;
484
485        if options.dry_run {
486            tracing::info!("Skipping programming, dry run!");
487
488            options.progress.failed_filling();
489            options.progress.failed_erasing();
490            options.progress.failed_programming();
491
492            return Ok(());
493        }
494
495        self.initialize(&mut algos, session, &mut options)?;
496
497        let mut do_chip_erase = options.do_chip_erase;
498        let mut did_chip_erase = false;
499
500        // Iterate all flash algorithms we need to use and do the flashing.
501        for mut flasher in algos {
502            tracing::debug!("Flashing ranges for algo: {}", flasher.flash_algorithm.name);
503
504            if do_chip_erase {
505                tracing::debug!("    Doing chip erase...");
506                flasher.run_erase_all(session, &mut options.progress)?;
507                do_chip_erase = false;
508                did_chip_erase = true;
509            }
510
511            let mut do_use_double_buffering = flasher.double_buffering_supported();
512            if do_use_double_buffering && options.disable_double_buffering {
513                tracing::info!(
514                    "Disabled double-buffering support for loader via passed option, though target supports it."
515                );
516                do_use_double_buffering = false;
517            }
518
519            // Program the data.
520            flasher.program(
521                session,
522                &mut options.progress,
523                options.keep_unwritten_bytes,
524                do_use_double_buffering,
525                options.skip_erase || did_chip_erase,
526                options.verify,
527            )?;
528        }
529
530        tracing::debug!("Committing RAM!");
531
532        if let BootInfo::FromRam { cores_to_reset, .. } = self.boot_info() {
533            // If we are booting from RAM, it is important to reset and halt to guarantee a clear state
534            // Normally, flash algorithm loader performs reset and halt - does not happen here.
535            tracing::debug!(
536                " -- action: vector table in RAM, assuming RAM boot, resetting and halting"
537            );
538            for (core_to_reset_index, _) in session
539                .target()
540                .cores
541                .clone()
542                .iter()
543                .enumerate()
544                .filter(|(_, c)| cores_to_reset.contains(&c.name))
545            {
546                session
547                    .core(core_to_reset_index)
548                    .and_then(|mut core| core.reset_and_halt(Duration::from_millis(500)))
549                    .map_err(FlashError::Core)?;
550            }
551        }
552
553        // Commit RAM last, because NVM flashing overwrites RAM
554        for region in self
555            .memory_map
556            .iter()
557            .filter_map(MemoryRegion::as_ram_region)
558        {
559            let ranges_in_region: Vec<_> = self.builder.data_in_range(&region.range).collect();
560
561            if ranges_in_region.is_empty() {
562                continue;
563            }
564
565            tracing::debug!(
566                "    region: {:#010X?} ({} bytes)",
567                region.range,
568                region.range.end - region.range.start
569            );
570
571            let region_core_index = session
572                .target()
573                .core_index_by_name(
574                    region
575                        .cores
576                        .first()
577                        .ok_or_else(|| FlashError::NoRamCoreAccess(region.clone()))?,
578                )
579                .unwrap();
580
581            // Attach to memory and core.
582            let mut core = session.core(region_core_index).map_err(FlashError::Core)?;
583
584            // If this is a RAM only flash, the core might still be running. This can be
585            // problematic if the instruction RAM is flashed while an application is running, so
586            // the core is halted here in any case.
587            if !core.core_halted().map_err(FlashError::Core)? {
588                tracing::debug!(
589                    "     -- action: core is not halted and RAM is being written, halting"
590                );
591                core.halt(Duration::from_millis(500))
592                    .map_err(FlashError::Core)?;
593            }
594
595            for (address, data) in ranges_in_region {
596                tracing::debug!(
597                    "     -- writing: {:#010X}..{:#010X} ({} bytes)",
598                    address,
599                    address + data.len() as u64,
600                    data.len()
601                );
602                // Write data to memory.
603                core.write(address, data).map_err(FlashError::Core)?;
604            }
605        }
606
607        if options.verify {
608            self.verify_ram(session)?;
609        }
610
611        Ok(())
612    }
613
614    fn prepare_plan(
615        &self,
616        session: &mut Session,
617        restore_unwritten_bytes: bool,
618    ) -> Result<Vec<Flasher>, FlashError> {
619        tracing::debug!("Contents of builder:");
620        for (&address, data) in &self.builder.data {
621            tracing::debug!(
622                "    data: {:#010X}..{:#010X} ({} bytes)",
623                address,
624                address + data.len() as u64,
625                data.len()
626            );
627        }
628
629        tracing::debug!("Flash algorithms:");
630        for algorithm in &session.target().flash_algorithms {
631            let Range { start, end } = algorithm.flash_properties.address_range;
632
633            tracing::debug!(
634                "    algo {}: {:#010X}..{:#010X} ({} bytes)",
635                algorithm.name,
636                start,
637                end,
638                end - start
639            );
640        }
641
642        // Iterate over all memory regions, and program their data.
643
644        if self.memory_map != session.target().memory_map {
645            tracing::warn!("Memory map of flash loader does not match memory map of target!");
646        }
647
648        let mut algos = Vec::<Flasher>::new();
649
650        // Commit NVM first
651
652        // Iterate all NvmRegions and group them by flash algorithm.
653        // This avoids loading the same algorithm twice if it's used for two regions.
654        //
655        // This also ensures correct operation when chip erase is used. We assume doing a chip erase
656        // using a given algorithm erases all regions controlled by it. Therefore, we must do
657        // chip erase once per algorithm, not once per region. Otherwise subsequent chip erases will
658        // erase previous regions' flashed contents.
659        tracing::debug!("Regions:");
660        for region in self
661            .memory_map
662            .iter()
663            .filter_map(MemoryRegion::as_nvm_region)
664        {
665            tracing::debug!(
666                "    region: {:#010X?} ({} bytes)",
667                region.range,
668                region.range.end - region.range.start
669            );
670
671            // If we have no data in this region, ignore it.
672            // This avoids uselessly initializing and deinitializing its flash algorithm.
673            // We do not check for alias regions here, as we'll work with them if data explicitly
674            // targets them.
675            if !self.builder.has_data_in_range(&region.range) {
676                tracing::debug!("     -- empty, ignoring!");
677                continue;
678            }
679
680            let region = region.clone();
681
682            let Some(core_name) = region.cores.first() else {
683                return Err(FlashError::NoNvmCoreAccess(region));
684            };
685
686            let target = session.target();
687            let core = target.core_index_by_name(core_name).unwrap();
688            let algo = Self::get_flash_algorithm_for_region(&region, target, core_name)?;
689
690            // We don't usually have more than a handful of regions, linear search should be fine.
691            tracing::debug!("     -- using algorithm: {}", algo.name);
692            if let Some(entry) = algos
693                .iter_mut()
694                .find(|entry| entry.flash_algorithm.name == algo.name && entry.core_index == core)
695            {
696                entry.add_region(region, &self.builder, restore_unwritten_bytes)?;
697            } else {
698                let mut flasher = Flasher::new(target, core, algo)?;
699                flasher.add_region(region, &self.builder, restore_unwritten_bytes)?;
700                algos.push(flasher);
701            }
702        }
703
704        Ok(algos)
705    }
706
707    fn initialize(
708        &self,
709        algos: &mut [Flasher],
710        session: &mut Session,
711        options: &mut DownloadOptions,
712    ) -> Result<(), FlashError> {
713        let mut phases = vec![];
714
715        for flasher in algos.iter() {
716            // If the first flash algo doesn't support erase all, disable chip erase.
717            // TODO: we could sort by support but it's unlikely to make a difference.
718            if options.do_chip_erase && !flasher.is_chip_erase_supported(session) {
719                options.do_chip_erase = false;
720                tracing::warn!(
721                    "Chip erase was the selected method to erase the sectors but this chip does not support chip erases (yet)."
722                );
723                tracing::warn!("A manual sector erase will be performed.");
724            }
725        }
726
727        if options.do_chip_erase {
728            options
729                .progress
730                .add_progress_bar(ProgressOperation::Erase, None);
731        }
732
733        // Iterate all flash algorithms to initialize a few things.
734        for flasher in algos.iter_mut() {
735            let mut phase_layout = FlashLayout::default();
736
737            let mut fill_size = 0;
738            let mut erase_size = 0;
739            let mut program_size = 0;
740
741            for region in flasher.regions.iter_mut() {
742                let layout = region.flash_layout();
743                phase_layout.merge_from(layout.clone());
744
745                erase_size += layout.sectors().iter().map(|s| s.size()).sum::<u64>();
746                fill_size += layout.fills().iter().map(|s| s.size()).sum::<u64>();
747                program_size += region
748                    .data
749                    .encoder(
750                        flasher.flash_algorithm.transfer_encoding,
751                        !options.keep_unwritten_bytes,
752                    )
753                    .program_size();
754            }
755
756            if options.keep_unwritten_bytes {
757                options
758                    .progress
759                    .add_progress_bar(ProgressOperation::Fill, Some(fill_size));
760            }
761            if !options.do_chip_erase {
762                options
763                    .progress
764                    .add_progress_bar(ProgressOperation::Erase, Some(erase_size));
765            }
766            options
767                .progress
768                .add_progress_bar(ProgressOperation::Program, Some(program_size));
769            if options.verify {
770                options
771                    .progress
772                    .add_progress_bar(ProgressOperation::Verify, Some(program_size));
773            }
774
775            phases.push(phase_layout);
776        }
777
778        options.progress.initialized(phases);
779
780        Ok(())
781    }
782
783    fn verify_ram(&self, session: &mut Session) -> Result<(), FlashError> {
784        tracing::debug!("Verifying RAM!");
785        for (&address, data) in &self.builder.data {
786            tracing::debug!(
787                "    data: {:#010X}..{:#010X} ({} bytes)",
788                address,
789                address + data.len() as u64,
790                data.len()
791            );
792
793            let associated_region = session.target().memory_region_by_address(address).unwrap();
794
795            // We verified NVM regions before, in flasher.program().
796            if !associated_region.is_ram() {
797                continue;
798            }
799
800            let core_name = associated_region.cores().first().unwrap();
801            let core_index = session.target().core_index_by_name(core_name).unwrap();
802            let mut core = session.core(core_index).map_err(FlashError::Core)?;
803
804            let mut written_data = vec![0; data.len()];
805            core.read(address, &mut written_data)
806                .map_err(FlashError::Core)?;
807
808            if data != &written_data {
809                return Err(FlashError::Verify);
810            }
811        }
812
813        Ok(())
814    }
815
816    /// Try to find a flash algorithm for the given NvmRegion.
817    /// Errors when:
818    /// - there's no algo for the region.
819    /// - there's multiple default algos for the region.
820    /// - there's multiple fitting algos but no default.
821    pub(crate) fn get_flash_algorithm_for_region<'a>(
822        region: &NvmRegion,
823        target: &'a Target,
824        core_name: &String,
825    ) -> Result<&'a RawFlashAlgorithm, FlashError> {
826        let available = &target.flash_algorithms;
827        tracing::debug!("Available algorithms:");
828        for algorithm in available {
829            tracing::debug!(
830                "Algorithm: {} for {:?} @ 0x{:08x} - 0x{:08x}  default? {}",
831                algorithm.name,
832                algorithm.cores,
833                algorithm.flash_properties.address_range.start,
834                algorithm.flash_properties.address_range.end,
835                algorithm.default
836            );
837        }
838        let algorithms = target
839            .flash_algorithms
840            .iter()
841            // filter for algorithms that contain address range
842            .filter(|&fa| {
843                fa.flash_properties
844                    .address_range
845                    .contains_range(&region.range)
846                    && (fa.cores.is_empty() || fa.cores.contains(core_name))
847            })
848            .collect::<Vec<_>>();
849
850        match algorithms.len() {
851            0 => Err(FlashError::NoFlashLoaderAlgorithmAttached {
852                range: region.range.clone(),
853                name: target.name.clone(),
854            }),
855            1 => Ok(algorithms[0]),
856            _ => {
857                // filter for defaults
858                let defaults = algorithms
859                    .iter()
860                    .filter(|&fa| fa.default)
861                    .collect::<Vec<_>>();
862
863                match defaults.len() {
864                    0 => Err(FlashError::MultipleFlashLoaderAlgorithmsNoDefault {
865                        region: region.clone(),
866                    }),
867                    1 => Ok(defaults[0]),
868                    _ => Err(FlashError::MultipleDefaultFlashLoaderAlgorithms {
869                        region: region.clone(),
870                    }),
871                }
872            }
873        }
874    }
875
876    /// Return data chunks stored in the `FlashLoader` as pairs of address and bytes.
877    pub fn data(&self) -> impl Iterator<Item = (u64, &[u8])> {
878        self.builder
879            .data
880            .iter()
881            .map(|(address, data)| (*address, data.as_slice()))
882    }
883}