Skip to main content

probe_rs/flashing/
loader.rs

1use itertools::Itertools as _;
2use parking_lot::RwLock;
3use probe_rs_target::{
4    InstructionSet, MemoryRange, MemoryRegion, NvmRegion, RawFlashAlgorithm,
5    TargetDescriptionSource,
6};
7use std::io::{Read, Seek};
8use std::ops::Range;
9use std::sync::LazyLock;
10use std::time::Duration;
11use yaml_serde::Value;
12
13use super::builder::FlashBuilder;
14use super::{DownloadOptions, FileDownloadError, FlashError, Flasher};
15use crate::Target;
16use crate::flashing::progress::ProgressOperation;
17use crate::flashing::{FlashLayout, FlashProgress};
18use crate::memory::MemoryInterface;
19use crate::session::Session;
20
21/// A trait representing a firmware image format.
22///
23/// This trait can create a new [ImageLoader], which can be used to load firmware images for flashing.
24pub trait ImageFormat: Sync {
25    /// The list of format names supported by this loader factory.
26    fn formats(&self) -> &[&str];
27
28    /// Create a new image loader.
29    fn create_loader(&self, options: Option<Value>) -> Box<dyn ImageLoader>;
30}
31
32/// Helper trait for object safety.
33pub trait ImageReader: Read + Seek {}
34
35/// Load and parse a firmware in a particular format, and add it to the flash loader.
36///
37/// Based on the image loader, probe-rs may apply certain transformations to the firmware.
38pub trait ImageLoader {
39    /// Loads the given image.
40    fn load(
41        &self,
42        flash_loader: &mut FlashLoader,
43        session: &mut Session,
44        file: &mut dyn ImageReader,
45    ) -> Result<(), FileDownloadError>;
46}
47
48/// Helper function to turn format-specific errors into a [`FileDownloadError`].
49pub fn into_format_error<E>(format: &str, error: E) -> FileDownloadError
50where
51    E: std::error::Error + Send + Sync + 'static,
52{
53    FileDownloadError::ImageFormatSpecific {
54        format: format.to_string(),
55        source: Box::new(error),
56    }
57}
58
59impl<T> ImageReader for T where T: Read + Seek {}
60
61/// A list of all known image formats.
62static LOADERS: LazyLock<RwLock<Vec<&'static dyn ImageFormat>>> = LazyLock::new(|| {
63    #[allow(unused_mut)]
64    let mut image_formats: Vec<&'static dyn ImageFormat> = vec![];
65
66    #[cfg(feature = "builtin-formats")]
67    {
68        image_formats.extend_from_slice(&[
69            &ElfLoaderFactory,
70            &BinLoaderFactory,
71            &HexLoaderFactory,
72            &Uf2LoaderFactory,
73        ]);
74    }
75
76    RwLock::new(image_formats)
77});
78
79/// Returns an [image format][ImageFormat] by name.
80///
81/// # Arguments
82///
83/// * `format` - The name of the image format. An image format is recognised
84///   by the names it returns from the [formats][ImageFormat::formats] method.
85pub fn image_format(format: &str) -> Option<&'static dyn ImageFormat> {
86    LOADERS
87        .read()
88        .iter()
89        .find(|factory| factory.formats().contains(&format))
90        .copied()
91}
92
93/// Registers an [image format][ImageFormat].
94///
95/// # Arguments
96///
97/// * `factory` - The image format factory.
98pub(crate) fn register_image_format(factory: &'static dyn ImageFormat) {
99    LOADERS.write().push(factory);
100}
101
102#[cfg(feature = "builtin-formats")]
103mod builtin {
104    use ihex::Record;
105    use probe_rs_target::MemoryRange;
106    use std::io::SeekFrom;
107    use yaml_serde::Value;
108
109    use object::{
110        Endianness, Object, ObjectSection, elf::FileHeader32, elf::FileHeader64, elf::PT_LOAD,
111        read::elf::ElfFile, read::elf::FileHeader, read::elf::ProgramHeader,
112    };
113
114    use crate::flashing::loader::{FlashLoader, ImageFormat, ImageLoader, ImageReader};
115    use crate::flashing::{BinOptions, ElfOptions, FileDownloadError};
116    use crate::session::Session;
117
118    pub(super) struct ElfLoaderFactory;
119    pub(super) struct BinLoaderFactory;
120    pub(super) struct HexLoaderFactory;
121    pub(super) struct Uf2LoaderFactory;
122
123    impl ImageFormat for ElfLoaderFactory {
124        fn formats(&self) -> &[&str] {
125            &["elf"]
126        }
127
128        fn create_loader(&self, options: Option<Value>) -> Box<dyn ImageLoader> {
129            let options = options
130                .and_then(|value| yaml_serde::from_value(value).ok())
131                .unwrap_or_default();
132            Box::new(ElfLoader(options))
133        }
134    }
135    impl ImageFormat for BinLoaderFactory {
136        fn formats(&self) -> &[&str] {
137            &["bin", "binary"]
138        }
139
140        fn create_loader(&self, options: Option<Value>) -> Box<dyn ImageLoader> {
141            let options = options
142                .and_then(|value| yaml_serde::from_value(value).ok())
143                .unwrap_or_default();
144            Box::new(BinLoader(options))
145        }
146    }
147    impl ImageFormat for HexLoaderFactory {
148        fn formats(&self) -> &[&str] {
149            &["hex", "ihex", "intelhex"]
150        }
151
152        fn create_loader(&self, _options: Option<Value>) -> Box<dyn ImageLoader> {
153            Box::new(HexLoader)
154        }
155    }
156    impl ImageFormat for Uf2LoaderFactory {
157        fn formats(&self) -> &[&str] {
158            &["uf2"]
159        }
160
161        fn create_loader(&self, _options: Option<Value>) -> Box<dyn ImageLoader> {
162            Box::new(Uf2Loader)
163        }
164    }
165
166    impl ImageLoader for Box<dyn ImageLoader> {
167        fn load(
168            &self,
169            flash_loader: &mut FlashLoader,
170            session: &mut Session,
171            file: &mut dyn ImageReader,
172        ) -> Result<(), FileDownloadError> {
173            self.as_ref().load(flash_loader, session, file)
174        }
175    }
176
177    /// Reads the data from the binary file and adds it to the loader without splitting it into flash instructions yet.
178    pub struct BinLoader(pub BinOptions);
179
180    impl ImageLoader for BinLoader {
181        fn load(
182            &self,
183            flash_loader: &mut FlashLoader,
184            _session: &mut Session,
185            file: &mut dyn ImageReader,
186        ) -> Result<(), FileDownloadError> {
187            // Skip the specified bytes.
188            file.seek(SeekFrom::Start(u64::from(self.0.skip)))?;
189
190            let mut buf = Vec::new();
191            file.read_to_end(&mut buf)?;
192
193            flash_loader.add_data(
194                // If no base address is specified use the start of the boot memory.
195                // TODO: Implement this as soon as we know targets.
196                self.0.base_address.unwrap_or_default(),
197                &buf,
198            )?;
199
200            Ok(())
201        }
202    }
203
204    /// Prepares the data sections that have to be loaded into flash from an ELF file.
205    /// This will validate the ELF file and transform all its data into sections but no flash loader commands yet.
206    pub struct ElfLoader(pub ElfOptions);
207
208    impl ImageLoader for ElfLoader {
209        fn load(
210            &self,
211            flash_loader: &mut FlashLoader,
212            _session: &mut Session,
213            file: &mut dyn ImageReader,
214        ) -> Result<(), FileDownloadError> {
215            const VECTOR_TABLE_SECTION_NAME: &str = ".vector_table";
216            let mut elf_buffer = Vec::new();
217            file.read_to_end(&mut elf_buffer)?;
218
219            let extracted_data = extract_from_elf(&elf_buffer, &self.0)?;
220
221            if extracted_data.is_empty() {
222                tracing::warn!("No loadable segments were found in the ELF file.");
223                return Err(FileDownloadError::NoLoadableSegments);
224            }
225
226            tracing::info!("Found {} loadable sections:", extracted_data.len());
227
228            for section in &extracted_data {
229                let sources = &section.section_names;
230                for name in &section.section_names {
231                    if name == VECTOR_TABLE_SECTION_NAME {
232                        flash_loader.set_vector_table_addr(section.address as _);
233                    }
234                }
235
236                tracing::info!(
237                    "    {:?} at {:#010X} ({} byte{})",
238                    sources,
239                    section.address,
240                    section.data.len(),
241                    if section.data.len() == 1 { "" } else { "s" }
242                );
243            }
244
245            for data in extracted_data {
246                flash_loader.add_data(data.address.into(), data.data)?;
247            }
248
249            Ok(())
250        }
251    }
252
253    pub(super) fn extract_from_elf<'a>(
254        elf_data: &'a [u8],
255        options: &ElfOptions,
256    ) -> Result<Vec<ExtractedFlashData<'a>>, FileDownloadError> {
257        let file_kind = object::FileKind::parse(elf_data)?;
258
259        match file_kind {
260            object::FileKind::Elf32 => {
261                let elf_header = FileHeader32::<Endianness>::parse(elf_data)?;
262                let binary =
263                    object::read::elf::ElfFile::<FileHeader32<Endianness>>::parse(elf_data)?;
264                extract_from_elf_inner(elf_header, binary, elf_data, options)
265            }
266            object::FileKind::Elf64 => {
267                let elf_header = FileHeader64::<Endianness>::parse(elf_data)?;
268                let binary =
269                    object::read::elf::ElfFile::<FileHeader64<Endianness>>::parse(elf_data)?;
270                extract_from_elf_inner(elf_header, binary, elf_data, options)
271            }
272            _ => Err(FileDownloadError::Object("Unsupported file type")),
273        }
274    }
275
276    fn extract_from_elf_inner<'data, T: FileHeader>(
277        elf_header: &T,
278        binary: ElfFile<'_, T>,
279        elf_data: &'data [u8],
280        options: &ElfOptions,
281    ) -> Result<Vec<ExtractedFlashData<'data>>, FileDownloadError> {
282        let endian = elf_header.endian()?;
283
284        let mut extracted_data = Vec::new();
285        for segment in elf_header.program_headers(elf_header.endian()?, elf_data)? {
286            // Get the physical address of the segment. The data will be programmed to that location.
287            let p_paddr: u64 = segment.p_paddr(endian).into();
288
289            let p_vaddr: u64 = segment.p_vaddr(endian).into();
290
291            let flags = segment.p_flags(endian);
292
293            let segment_data = segment.data(endian, elf_data).map_err(|_| {
294                FileDownloadError::Object("Failed to access data for an ELF segment.")
295            })?;
296
297            let mut elf_section = Vec::new();
298
299            if !segment_data.is_empty() && segment.p_type(endian) == PT_LOAD {
300                tracing::info!(
301                    "Found loadable segment, physical address: {:#010x}, virtual address: {:#010x}, flags: {:#x}",
302                    p_paddr,
303                    p_vaddr,
304                    flags
305                );
306
307                let (segment_offset, segment_filesize) = segment.file_range(endian);
308
309                let sector = segment_offset..segment_offset + segment_filesize;
310
311                for section in binary.sections() {
312                    let (section_offset, section_filesize) = match section.file_range() {
313                        Some(range) => range,
314                        None => continue,
315                    };
316
317                    if sector.contains_range(&(section_offset..section_offset + section_filesize)) {
318                        let name = section.name()?;
319                        if options.skip_sections.iter().any(|skip| skip == name) {
320                            tracing::info!("Skipping section: {:?}", name);
321                            continue;
322                        }
323                        tracing::info!("Matching section: {:?}", name);
324
325                        #[cfg(feature = "hexdump")]
326                        for line in hexdump::hexdump_iter(section.data()?) {
327                            tracing::trace!("{}", line);
328                        }
329
330                        for (offset, relocation) in section.relocations() {
331                            tracing::info!(
332                                "Relocation: offset={}, relocation={:?}",
333                                offset,
334                                relocation
335                            );
336                        }
337
338                        elf_section.push(name.to_owned());
339                    }
340                }
341
342                if elf_section.is_empty() {
343                    tracing::info!("Not adding segment, no matching sections found.");
344                } else {
345                    let section_data =
346                        &elf_data[segment_offset as usize..][..segment_filesize as usize];
347
348                    extracted_data.push(ExtractedFlashData {
349                        section_names: elf_section,
350                        address: p_paddr as u32,
351                        data: section_data,
352                    });
353                }
354            }
355        }
356
357        Ok(extracted_data)
358    }
359
360    /// Flash data which was extracted from an ELF file.
361    pub(super) struct ExtractedFlashData<'data> {
362        pub(super) section_names: Vec<String>,
363        pub(super) address: u32,
364        pub(super) data: &'data [u8],
365    }
366
367    impl std::fmt::Debug for ExtractedFlashData<'_> {
368        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
369            let mut helper = f.debug_struct("ExtractedFlashData");
370
371            helper
372                .field("name", &self.section_names)
373                .field("address", &self.address);
374
375            if self.data.len() > 10 {
376                helper
377                    .field("data", &format!("[..] ({} bytes)", self.data.len()))
378                    .finish()
379            } else {
380                helper.field("data", &self.data).finish()
381            }
382        }
383    }
384
385    /// Reads the HEX data segments and adds them as loadable data blocks to the loader.
386    /// This does not create any flash loader instructions yet.
387    pub struct HexLoader;
388
389    impl ImageLoader for HexLoader {
390        fn load(
391            &self,
392            flash_loader: &mut FlashLoader,
393            _session: &mut Session,
394            file: &mut dyn ImageReader,
395        ) -> Result<(), FileDownloadError> {
396            let mut base_address = 0;
397
398            let mut data = String::new();
399            file.read_to_string(&mut data)?;
400
401            for record in ihex::Reader::new(&data) {
402                match record? {
403                    Record::Data { offset, value } => {
404                        let offset = base_address + offset as u64;
405                        flash_loader.add_data(offset, &value)?;
406                    }
407                    Record::ExtendedSegmentAddress(address) => {
408                        base_address = (address as u64) * 16;
409                    }
410                    Record::ExtendedLinearAddress(address) => {
411                        base_address = (address as u64) << 16;
412                    }
413
414                    Record::EndOfFile
415                    | Record::StartSegmentAddress { .. }
416                    | Record::StartLinearAddress(_) => {}
417                }
418            }
419            Ok(())
420        }
421    }
422
423    /// Prepares the data sections that have to be loaded into flash from an UF2 file.
424    /// This will validate the UF2 file and transform all its data into sections but no flash loader commands yet.
425    pub struct Uf2Loader;
426
427    impl ImageLoader for Uf2Loader {
428        fn load(
429            &self,
430            flash_loader: &mut FlashLoader,
431            _session: &mut Session,
432            file: &mut dyn ImageReader,
433        ) -> Result<(), FileDownloadError> {
434            let mut uf2_buffer = Vec::new();
435            file.read_to_end(&mut uf2_buffer)?;
436
437            let (converted, family_to_target) = uf2_decode::convert_from_uf2(&uf2_buffer).unwrap();
438            let target_addresses = family_to_target.values();
439            let num_sections = family_to_target.len();
440
441            if let Some(target_address) = target_addresses.min() {
442                tracing::info!("Found {} loadable sections:", num_sections);
443                if num_sections > 1 {
444                    tracing::warn!("More than 1 section found in UF2 file.  Using first section.");
445                }
446                flash_loader.add_data(*target_address, &converted)?;
447
448                Ok(())
449            } else {
450                tracing::warn!("No loadable segments were found in the UF2 file.");
451                Err(FileDownloadError::NoLoadableSegments)
452            }
453        }
454    }
455}
456
457#[cfg(feature = "builtin-formats")]
458pub use builtin::*;
459
460/// Current boot information
461#[derive(Clone, Debug, Default)]
462pub enum BootInfo {
463    /// Loaded executable has a vector table in RAM
464    FromRam {
465        /// Address of the vector table in memory
466        vector_table_addr: u64,
467        /// All cores that should be reset and halted before any RAM access
468        cores_to_reset: Vec<String>,
469    },
470    /// Executable is either not loaded yet or will be booted conventionally (from flash etc.)
471    #[default]
472    Other,
473}
474
475/// `FlashLoader` is a struct which manages the flashing of any chunks of data onto any sections of flash.
476///
477/// Use [add_data()](FlashLoader::add_data) to add a chunk of data.
478/// Once you are done adding all your data, use `commit()` to flash the data.
479/// The flash loader will make sure to select the appropriate flash region for the right data chunks.
480/// Region crossing data chunks are allowed as long as the regions are contiguous.
481pub struct FlashLoader {
482    memory_map: Vec<MemoryRegion>,
483    builder: FlashBuilder,
484
485    /// Source of the flash description,
486    /// used for diagnostics.
487    source: TargetDescriptionSource,
488    /// Relevant for manually configured RAM booted executables, available only if given loader supports it
489    vector_table_addr: Option<u64>,
490
491    read_flasher_rtt: bool,
492}
493
494impl FlashLoader {
495    /// Create a new flash loader.
496    pub fn new(memory_map: Vec<MemoryRegion>, source: TargetDescriptionSource) -> Self {
497        Self {
498            memory_map,
499            builder: FlashBuilder::new(),
500            source,
501            vector_table_addr: None,
502            read_flasher_rtt: false,
503        }
504    }
505
506    /// Retrieve the internal flash builder instance which also contains the raw memory regions to
507    /// flash.
508    pub fn flash_builder(&self) -> &FlashBuilder {
509        &self.builder
510    }
511
512    /// Enable reading RTT output from the flasher.
513    pub fn read_rtt_output(&mut self, read: bool) {
514        self.read_flasher_rtt = read;
515    }
516
517    /// Vector table address, if available for this flash operation.
518    pub fn vector_table_addr(&self) -> Option<u64> {
519        self.vector_table_addr
520    }
521
522    /// Set the vector table address.
523    pub fn set_vector_table_addr(&mut self, vector_table_addr: u64) {
524        self.vector_table_addr = Some(vector_table_addr);
525    }
526
527    /// Retrieve available boot information
528    pub fn boot_info(&self) -> BootInfo {
529        let Some(vector_table_addr) = self.vector_table_addr else {
530            return BootInfo::Other;
531        };
532
533        match Self::get_region_for_address(&self.memory_map, vector_table_addr) {
534            Some(MemoryRegion::Ram(region)) => BootInfo::FromRam {
535                vector_table_addr,
536                cores_to_reset: region.cores.clone(),
537            },
538            _ => BootInfo::Other,
539        }
540    }
541
542    /// Check the given address range is completely covered by the memory map,
543    /// possibly by multiple memory regions.
544    fn check_data_in_memory_map(&mut self, range: Range<u64>) -> Result<(), FlashError> {
545        let mut address = range.start;
546        while address < range.end {
547            match Self::get_region_for_address(&self.memory_map, address) {
548                Some(MemoryRegion::Nvm(region)) => address = region.range.end,
549                Some(MemoryRegion::Ram(region)) => address = region.range.end,
550                _ => {
551                    return Err(FlashError::NoSuitableNvm {
552                        range,
553                        description_source: self.source.clone(),
554                    });
555                }
556            }
557        }
558        Ok(())
559    }
560
561    /// Stages a chunk of data to be programmed.
562    ///
563    /// The chunk can cross flash boundaries as long as one flash region connects to another flash region.
564    pub fn add_data(&mut self, address: u64, data: &[u8]) -> Result<(), FlashError> {
565        tracing::trace!(
566            "Adding data at address {:#010x} with size {} bytes",
567            address,
568            data.len()
569        );
570
571        self.check_data_in_memory_map(address..address + data.len() as u64)?;
572        self.builder.add_data(address, data)
573    }
574
575    pub(super) fn get_region_for_address(
576        memory_map: &[MemoryRegion],
577        address: u64,
578    ) -> Option<&MemoryRegion> {
579        memory_map.iter().find(|region| region.contains(address))
580    }
581
582    /// Returns whether an address will be flashed with data
583    pub fn has_data_for_address(&self, address: u64) -> bool {
584        self.builder.has_data_in_range(&(address..address + 1))
585    }
586
587    /// Reads the image according to the file format and adds it to the loader.
588    pub fn load_image<T: Read + Seek>(
589        &mut self,
590        session: &mut Session,
591        file: &mut T,
592        format: impl ImageLoader,
593        image_instruction_set: Option<InstructionSet>,
594    ) -> Result<(), FileDownloadError> {
595        if let Some(instr_set) = image_instruction_set {
596            let mut target_archs = Vec::with_capacity(session.list_cores().len());
597
598            // Get a unique list of core architectures
599            for (core, _) in session.list_cores() {
600                match session.core(core) {
601                    Ok(mut core) => {
602                        if let Ok(set) = core.instruction_set()
603                            && !target_archs.contains(&set)
604                        {
605                            target_archs.push(set);
606                        }
607                    }
608                    Err(crate::Error::CoreDisabled(_)) => continue,
609                    Err(error) => return Err(FileDownloadError::Other(error)),
610                }
611            }
612
613            // Is the image compatible with any of the cores?
614            if !target_archs
615                .iter()
616                .any(|target| target.is_compatible(instr_set))
617            {
618                return Err(FileDownloadError::IncompatibleImage {
619                    target: target_archs,
620                    image: instr_set,
621                });
622            }
623        }
624
625        format.load(self, session, file)
626    }
627
628    /// Verifies data on the device.
629    pub fn verify(
630        &self,
631        session: &mut Session,
632        progress: &mut FlashProgress<'_>,
633    ) -> Result<(), FlashError> {
634        let mut algos = self.prepare_plan(session, false, &[])?;
635
636        for flasher in algos.iter_mut() {
637            let mut program_size = 0;
638            for region in flasher.regions.iter_mut() {
639                program_size += region
640                    .data
641                    .encoder(flasher.flash_algorithm.transfer_encoding, true)
642                    .program_size();
643            }
644            progress.add_progress_bar(ProgressOperation::Verify, Some(program_size));
645        }
646
647        // Iterate all flash algorithms we need to use and do the flashing.
648        for mut flasher in algos {
649            tracing::debug!(
650                "Verifying ranges for algo: {}",
651                flasher.flash_algorithm.name
652            );
653
654            if !flasher.verify(session, progress, true)? {
655                return Err(FlashError::Verify);
656            }
657        }
658
659        self.verify_ram(session)?;
660
661        Ok(())
662    }
663
664    /// Writes all the stored data chunks to flash.
665    ///
666    /// Requires a session with an attached target that has a known flash algorithm.
667    pub fn commit(
668        &self,
669        session: &mut Session,
670        mut options: DownloadOptions,
671    ) -> Result<(), FlashError> {
672        tracing::debug!("Committing FlashLoader!");
673        let mut algos = self.prepare_plan(
674            session,
675            options.keep_unwritten_bytes,
676            &options.preferred_algos,
677        )?;
678
679        if options.dry_run {
680            tracing::info!("Skipping programming, dry run!");
681
682            options.progress.failed_filling();
683            options.progress.failed_erasing();
684            options.progress.failed_programming();
685
686            return Ok(());
687        }
688
689        self.initialize(&mut algos, session, &mut options)?;
690
691        let mut do_chip_erase = options.do_chip_erase;
692        let mut did_chip_erase = false;
693
694        // Iterate all flash algorithms we need to use and do the flashing.
695        for mut flasher in algos {
696            tracing::debug!("Flashing ranges for algo: {}", flasher.flash_algorithm.name);
697
698            if do_chip_erase {
699                tracing::debug!("    Doing chip erase...");
700                flasher.run_erase_all(session, &mut options.progress)?;
701                do_chip_erase = false;
702                did_chip_erase = true;
703            }
704
705            let mut do_use_double_buffering = flasher.double_buffering_supported();
706            if do_use_double_buffering && options.disable_double_buffering {
707                tracing::info!(
708                    "Disabled double-buffering support for loader via passed option, though target supports it."
709                );
710                do_use_double_buffering = false;
711            }
712
713            // Program the data.
714            flasher.program(
715                session,
716                &mut options.progress,
717                options.keep_unwritten_bytes,
718                do_use_double_buffering,
719                options.skip_erase || did_chip_erase,
720                options.verify,
721            )?;
722        }
723
724        tracing::debug!("Committing RAM!");
725
726        if let BootInfo::FromRam { cores_to_reset, .. } = self.boot_info() {
727            // If we are booting from RAM, it is important to reset and halt to guarantee a clear state
728            // Normally, flash algorithm loader performs reset and halt - does not happen here.
729            tracing::debug!(
730                " -- action: vector table in RAM, assuming RAM boot, resetting and halting"
731            );
732            for (core_to_reset_index, _) in session
733                .target()
734                .cores
735                .clone()
736                .iter()
737                .enumerate()
738                .filter(|(_, c)| cores_to_reset.contains(&c.name))
739            {
740                session
741                    .core(core_to_reset_index)
742                    .and_then(|mut core| core.reset_and_halt(Duration::from_millis(500)))
743                    .map_err(FlashError::Core)?;
744            }
745        }
746
747        // Commit RAM last, because NVM flashing overwrites RAM
748        for region in self
749            .memory_map
750            .iter()
751            .filter_map(MemoryRegion::as_ram_region)
752        {
753            let ranges_in_region: Vec<_> = self.builder.data_in_range(&region.range).collect();
754
755            if ranges_in_region.is_empty() {
756                continue;
757            }
758
759            tracing::debug!(
760                "    region: {:#010X?} ({} bytes)",
761                region.range,
762                region.range.end - region.range.start
763            );
764
765            let region_core_index = session
766                .target()
767                .core_index_by_name(
768                    region
769                        .cores
770                        .first()
771                        .ok_or_else(|| FlashError::NoRamCoreAccess(region.clone()))?,
772                )
773                .unwrap();
774
775            // Attach to memory and core.
776            let mut core = session.core(region_core_index).map_err(FlashError::Core)?;
777
778            // If this is a RAM only flash, the core might still be running. This can be
779            // problematic if the instruction RAM is flashed while an application is running, so
780            // the core is halted here in any case.
781            if !core.core_halted().map_err(FlashError::Core)? {
782                tracing::debug!(
783                    "     -- action: core is not halted and RAM is being written, halting"
784                );
785                core.halt(Duration::from_millis(500))
786                    .map_err(FlashError::Core)?;
787            }
788
789            for (address, data) in ranges_in_region {
790                tracing::debug!(
791                    "     -- writing: {:#010X}..{:#010X} ({} bytes)",
792                    address,
793                    address + data.len() as u64,
794                    data.len()
795                );
796                // Write data to memory.
797                core.write(address, data).map_err(FlashError::Core)?;
798            }
799        }
800
801        if options.verify {
802            self.verify_ram(session)?;
803        }
804
805        Ok(())
806    }
807
808    fn prepare_plan(
809        &self,
810        session: &mut Session,
811        restore_unwritten_bytes: bool,
812        opt_preferred_algos: &[String],
813    ) -> Result<Vec<Flasher>, FlashError> {
814        tracing::debug!("Contents of builder:");
815        for (&address, data) in &self.builder.data {
816            tracing::debug!(
817                "    data: {:#010X}..{:#010X} ({} bytes)",
818                address,
819                address + data.len() as u64,
820                data.len()
821            );
822        }
823
824        tracing::debug!("Flash algorithms:");
825        for algorithm in &session.target().flash_algorithms {
826            let Range { start, end } = algorithm.flash_properties.address_range;
827
828            tracing::debug!(
829                "    algo {}: {:#010X}..{:#010X} ({} bytes)",
830                algorithm.name,
831                start,
832                end,
833                end - start
834            );
835        }
836
837        // Iterate over all memory regions, and program their data.
838
839        if self.memory_map != session.target().memory_map {
840            tracing::warn!("Memory map of flash loader does not match memory map of target!");
841        }
842
843        let mut algos = Vec::<Flasher>::new();
844
845        // Commit NVM first
846
847        // Iterate all NvmRegions and group them by flash algorithm.
848        // This avoids loading the same algorithm twice if it's used for two regions.
849        //
850        // This also ensures correct operation when chip erase is used. We assume doing a chip erase
851        // using a given algorithm erases all regions controlled by it. Therefore, we must do
852        // chip erase once per algorithm, not once per region. Otherwise subsequent chip erases will
853        // erase previous regions' flashed contents.
854        tracing::debug!("Regions:");
855        for region in self
856            .memory_map
857            .iter()
858            .filter_map(MemoryRegion::as_nvm_region)
859        {
860            tracing::debug!(
861                "    region: {:#010X?} ({} bytes)",
862                region.range,
863                region.range.end - region.range.start
864            );
865
866            // If we have no data in this region, ignore it.
867            // This avoids uselessly initializing and deinitializing its flash algorithm.
868            // We do not check for alias regions here, as we'll work with them if data explicitly
869            // targets them.
870            if !self.builder.has_data_in_range(&region.range) {
871                tracing::debug!("     -- empty, ignoring!");
872                continue;
873            }
874
875            let region = region.clone();
876
877            let Some(core_name) = region.cores.first() else {
878                return Err(FlashError::NoNvmCoreAccess(region));
879            };
880
881            let target = session.target();
882            let core = target.core_index_by_name(core_name).unwrap();
883            let algo = Self::get_flash_algorithm_for_region(
884                &region,
885                target,
886                core_name,
887                opt_preferred_algos,
888            )?;
889
890            // We don't usually have more than a handful of regions, linear search should be fine.
891            tracing::debug!("     -- using algorithm: {}", algo.name);
892            if let Some(entry) = algos
893                .iter_mut()
894                .find(|entry| entry.flash_algorithm.name == algo.name && entry.core_index == core)
895            {
896                entry.add_region(region, &self.builder, restore_unwritten_bytes)?;
897            } else {
898                let mut flasher = Flasher::new(target, core, algo)?;
899                flasher.add_region(region, &self.builder, restore_unwritten_bytes)?;
900
901                flasher.read_rtt_output(self.read_flasher_rtt);
902
903                algos.push(flasher);
904            }
905        }
906
907        Ok(algos)
908    }
909
910    fn initialize(
911        &self,
912        algos: &mut [Flasher],
913        session: &mut Session,
914        options: &mut DownloadOptions,
915    ) -> Result<(), FlashError> {
916        let mut phases = vec![];
917
918        for flasher in algos.iter() {
919            // If the first flash algo doesn't support erase all, disable chip erase.
920            // TODO: we could sort by support but it's unlikely to make a difference.
921            if options.do_chip_erase && !flasher.is_chip_erase_supported(session) {
922                options.do_chip_erase = false;
923                tracing::warn!(
924                    "Chip erase was the selected method to erase the sectors but this chip does not support chip erases (yet)."
925                );
926                tracing::warn!("A manual sector erase will be performed.");
927            }
928        }
929
930        if options.do_chip_erase {
931            options
932                .progress
933                .add_progress_bar(ProgressOperation::Erase, None);
934        }
935
936        // Iterate all flash algorithms to initialize a few things.
937        for flasher in algos.iter_mut() {
938            let mut phase_layout = FlashLayout::default();
939
940            let mut fill_size = 0;
941            let mut erase_size = 0;
942            let mut program_size = 0;
943
944            for region in flasher.regions.iter_mut() {
945                let layout = region.flash_layout();
946                phase_layout.merge_from(layout.clone());
947
948                erase_size += layout.sectors().iter().map(|s| s.size()).sum::<u64>();
949                fill_size += layout.fills().iter().map(|s| s.size()).sum::<u64>();
950                program_size += region
951                    .data
952                    .encoder(
953                        flasher.flash_algorithm.transfer_encoding,
954                        !options.keep_unwritten_bytes,
955                    )
956                    .program_size();
957            }
958
959            if options.keep_unwritten_bytes {
960                options
961                    .progress
962                    .add_progress_bar(ProgressOperation::Fill, Some(fill_size));
963            }
964            if !options.do_chip_erase {
965                options
966                    .progress
967                    .add_progress_bar(ProgressOperation::Erase, Some(erase_size));
968            }
969            options
970                .progress
971                .add_progress_bar(ProgressOperation::Program, Some(program_size));
972            if options.verify {
973                options
974                    .progress
975                    .add_progress_bar(ProgressOperation::Verify, Some(program_size));
976            }
977
978            phases.push(phase_layout);
979        }
980
981        options.progress.initialized(phases);
982
983        Ok(())
984    }
985
986    fn verify_ram(&self, session: &mut Session) -> Result<(), FlashError> {
987        tracing::debug!("Verifying RAM!");
988        for (&address, data) in &self.builder.data {
989            tracing::debug!(
990                "    data: {:#010X}..{:#010X} ({} bytes)",
991                address,
992                address + data.len() as u64,
993                data.len()
994            );
995
996            let associated_region = session.target().memory_region_by_address(address).unwrap();
997
998            // We verified NVM regions before, in flasher.program().
999            if !associated_region.is_ram() {
1000                continue;
1001            }
1002
1003            let core_name = associated_region.cores().first().unwrap();
1004            let core_index = session.target().core_index_by_name(core_name).unwrap();
1005            let mut core = session.core(core_index).map_err(FlashError::Core)?;
1006
1007            let mut written_data = vec![0; data.len()];
1008            core.read(address, &mut written_data)
1009                .map_err(FlashError::Core)?;
1010
1011            if data != &written_data {
1012                return Err(FlashError::Verify);
1013            }
1014        }
1015
1016        Ok(())
1017    }
1018
1019    /// Try to find a flash algorithm for the given NvmRegion.
1020    /// Errors when:
1021    /// - there's no algo for the region.
1022    /// - there's multiple default algos for the region.
1023    /// - there's multiple fitting algos but no default.
1024    pub(crate) fn get_flash_algorithm_for_region<'a>(
1025        region: &NvmRegion,
1026        target: &'a Target,
1027        core_name: &String,
1028        preferred_algos: &[String],
1029    ) -> Result<&'a RawFlashAlgorithm, FlashError> {
1030        let available = &target.flash_algorithms;
1031        tracing::debug!("Available algorithms:");
1032        for algorithm in available {
1033            tracing::debug!(
1034                "Algorithm: {} for {:?} @ 0x{:08x} - 0x{:08x}  default? {}",
1035                algorithm.name,
1036                algorithm.cores,
1037                algorithm.flash_properties.address_range.start,
1038                algorithm.flash_properties.address_range.end,
1039                algorithm.default
1040            );
1041        }
1042        let algorithms = target
1043            .flash_algorithms
1044            .iter()
1045            // filter for algorithms that contain address range
1046            .filter(|&fa| {
1047                fa.flash_properties
1048                    .address_range
1049                    .contains_range(&region.range)
1050                    && (fa.cores.is_empty() || fa.cores.contains(core_name))
1051            })
1052            .collect::<Vec<_>>();
1053
1054        match algorithms.len() {
1055            0 => Err(FlashError::NoFlashLoaderAlgorithmAttached {
1056                range: region.range.clone(),
1057                name: target.name.clone(),
1058            }),
1059            1 => Ok(algorithms[0]),
1060            _ => {
1061                // filter for defaults
1062                let defaults = algorithms
1063                    .iter()
1064                    .filter(|&fa| fa.default)
1065                    .collect::<Vec<_>>();
1066
1067                if !preferred_algos.is_empty() {
1068                    tracing::debug!("selecting preferred algorithm from: {:?}", preferred_algos);
1069                    let mut preferred_and_valid_algos = Vec::new();
1070                    // Check whether there are any preferred algorithms which are valid and which
1071                    // override the default algo(s).
1072                    for algo in algorithms.iter() {
1073                        if preferred_algos.iter().contains(&algo.name) {
1074                            preferred_and_valid_algos.push(algo);
1075                        }
1076                    }
1077                    if preferred_and_valid_algos.len() > 1 {
1078                        return Err(FlashError::MultiplePreferredAlgos {
1079                            region: region.clone(),
1080                        });
1081                    }
1082                    // Preferred algo overrides default.
1083                    if preferred_and_valid_algos.len() == 1 {
1084                        return Ok(preferred_and_valid_algos[0]);
1085                    }
1086                }
1087
1088                match defaults.len() {
1089                    0 => Err(FlashError::MultipleFlashLoaderAlgorithmsNoDefault {
1090                        region: region.clone(),
1091                    }),
1092                    1 => Ok(defaults[0]),
1093                    _ => Err(FlashError::MultipleDefaultFlashLoaderAlgorithms {
1094                        region: region.clone(),
1095                    }),
1096                }
1097            }
1098        }
1099    }
1100
1101    /// Return data chunks stored in the `FlashLoader` as pairs of address and bytes.
1102    pub fn data(&self) -> impl Iterator<Item = (u64, &[u8])> {
1103        self.builder
1104            .data
1105            .iter()
1106            .map(|(address, data)| (*address, data.as_slice()))
1107    }
1108}