use itertools::Itertools as _;
use parking_lot::RwLock;
use probe_rs_target::{
InstructionSet, MemoryRange, MemoryRegion, NvmRegion, RawFlashAlgorithm,
TargetDescriptionSource,
};
use std::io::{Read, Seek};
use std::ops::Range;
use std::sync::LazyLock;
use std::time::Duration;
use yaml_serde::Value;
use super::builder::FlashBuilder;
use super::{DownloadOptions, FileDownloadError, FlashError, Flasher};
use crate::Target;
use crate::flashing::progress::ProgressOperation;
use crate::flashing::{FlashLayout, FlashProgress};
use crate::memory::MemoryInterface;
use crate::session::Session;
pub trait ImageFormat: Sync {
fn formats(&self) -> &[&str];
fn create_loader(&self, options: Option<Value>) -> Box<dyn ImageLoader>;
}
pub trait ImageReader: Read + Seek {}
pub trait ImageLoader {
fn load(
&self,
flash_loader: &mut FlashLoader,
session: &mut Session,
file: &mut dyn ImageReader,
) -> Result<(), FileDownloadError>;
}
pub fn into_format_error<E>(format: &str, error: E) -> FileDownloadError
where
E: std::error::Error + Send + Sync + 'static,
{
FileDownloadError::ImageFormatSpecific {
format: format.to_string(),
source: Box::new(error),
}
}
impl<T> ImageReader for T where T: Read + Seek {}
static LOADERS: LazyLock<RwLock<Vec<&'static dyn ImageFormat>>> = LazyLock::new(|| {
#[allow(unused_mut)]
let mut image_formats: Vec<&'static dyn ImageFormat> = vec![];
#[cfg(feature = "builtin-formats")]
{
image_formats.extend_from_slice(&[
&ElfLoaderFactory,
&BinLoaderFactory,
&HexLoaderFactory,
&Uf2LoaderFactory,
]);
}
RwLock::new(image_formats)
});
pub fn image_format(format: &str) -> Option<&'static dyn ImageFormat> {
LOADERS
.read()
.iter()
.find(|factory| factory.formats().contains(&format))
.copied()
}
pub(crate) fn register_image_format(factory: &'static dyn ImageFormat) {
LOADERS.write().push(factory);
}
#[cfg(feature = "builtin-formats")]
mod builtin {
use ihex::Record;
use probe_rs_target::MemoryRange;
use std::io::SeekFrom;
use yaml_serde::Value;
use object::{
Endianness, Object, ObjectSection, elf::FileHeader32, elf::FileHeader64, elf::PT_LOAD,
read::elf::ElfFile, read::elf::FileHeader, read::elf::ProgramHeader,
};
use crate::flashing::loader::{FlashLoader, ImageFormat, ImageLoader, ImageReader};
use crate::flashing::{BinOptions, ElfOptions, FileDownloadError};
use crate::session::Session;
pub(super) struct ElfLoaderFactory;
pub(super) struct BinLoaderFactory;
pub(super) struct HexLoaderFactory;
pub(super) struct Uf2LoaderFactory;
impl ImageFormat for ElfLoaderFactory {
fn formats(&self) -> &[&str] {
&["elf"]
}
fn create_loader(&self, options: Option<Value>) -> Box<dyn ImageLoader> {
let options = options
.and_then(|value| yaml_serde::from_value(value).ok())
.unwrap_or_default();
Box::new(ElfLoader(options))
}
}
impl ImageFormat for BinLoaderFactory {
fn formats(&self) -> &[&str] {
&["bin", "binary"]
}
fn create_loader(&self, options: Option<Value>) -> Box<dyn ImageLoader> {
let options = options
.and_then(|value| yaml_serde::from_value(value).ok())
.unwrap_or_default();
Box::new(BinLoader(options))
}
}
impl ImageFormat for HexLoaderFactory {
fn formats(&self) -> &[&str] {
&["hex", "ihex", "intelhex"]
}
fn create_loader(&self, _options: Option<Value>) -> Box<dyn ImageLoader> {
Box::new(HexLoader)
}
}
impl ImageFormat for Uf2LoaderFactory {
fn formats(&self) -> &[&str] {
&["uf2"]
}
fn create_loader(&self, _options: Option<Value>) -> Box<dyn ImageLoader> {
Box::new(Uf2Loader)
}
}
impl ImageLoader for Box<dyn ImageLoader> {
fn load(
&self,
flash_loader: &mut FlashLoader,
session: &mut Session,
file: &mut dyn ImageReader,
) -> Result<(), FileDownloadError> {
self.as_ref().load(flash_loader, session, file)
}
}
pub struct BinLoader(pub BinOptions);
impl ImageLoader for BinLoader {
fn load(
&self,
flash_loader: &mut FlashLoader,
_session: &mut Session,
file: &mut dyn ImageReader,
) -> Result<(), FileDownloadError> {
file.seek(SeekFrom::Start(u64::from(self.0.skip)))?;
let mut buf = Vec::new();
file.read_to_end(&mut buf)?;
flash_loader.add_data(
self.0.base_address.unwrap_or_default(),
&buf,
)?;
Ok(())
}
}
pub struct ElfLoader(pub ElfOptions);
impl ImageLoader for ElfLoader {
fn load(
&self,
flash_loader: &mut FlashLoader,
_session: &mut Session,
file: &mut dyn ImageReader,
) -> Result<(), FileDownloadError> {
const VECTOR_TABLE_SECTION_NAME: &str = ".vector_table";
let mut elf_buffer = Vec::new();
file.read_to_end(&mut elf_buffer)?;
let extracted_data = extract_from_elf(&elf_buffer, &self.0)?;
if extracted_data.is_empty() {
tracing::warn!("No loadable segments were found in the ELF file.");
return Err(FileDownloadError::NoLoadableSegments);
}
tracing::info!("Found {} loadable sections:", extracted_data.len());
for section in &extracted_data {
let sources = §ion.section_names;
for name in §ion.section_names {
if name == VECTOR_TABLE_SECTION_NAME {
flash_loader.set_vector_table_addr(section.address as _);
}
}
tracing::info!(
" {:?} at {:#010X} ({} byte{})",
sources,
section.address,
section.data.len(),
if section.data.len() == 1 { "" } else { "s" }
);
}
for data in extracted_data {
flash_loader.add_data(data.address.into(), data.data)?;
}
Ok(())
}
}
pub(super) fn extract_from_elf<'a>(
elf_data: &'a [u8],
options: &ElfOptions,
) -> Result<Vec<ExtractedFlashData<'a>>, FileDownloadError> {
let file_kind = object::FileKind::parse(elf_data)?;
match file_kind {
object::FileKind::Elf32 => {
let elf_header = FileHeader32::<Endianness>::parse(elf_data)?;
let binary =
object::read::elf::ElfFile::<FileHeader32<Endianness>>::parse(elf_data)?;
extract_from_elf_inner(elf_header, binary, elf_data, options)
}
object::FileKind::Elf64 => {
let elf_header = FileHeader64::<Endianness>::parse(elf_data)?;
let binary =
object::read::elf::ElfFile::<FileHeader64<Endianness>>::parse(elf_data)?;
extract_from_elf_inner(elf_header, binary, elf_data, options)
}
_ => Err(FileDownloadError::Object("Unsupported file type")),
}
}
fn extract_from_elf_inner<'data, T: FileHeader>(
elf_header: &T,
binary: ElfFile<'_, T>,
elf_data: &'data [u8],
options: &ElfOptions,
) -> Result<Vec<ExtractedFlashData<'data>>, FileDownloadError> {
let endian = elf_header.endian()?;
let mut extracted_data = Vec::new();
for segment in elf_header.program_headers(elf_header.endian()?, elf_data)? {
let p_paddr: u64 = segment.p_paddr(endian).into();
let p_vaddr: u64 = segment.p_vaddr(endian).into();
let flags = segment.p_flags(endian);
let segment_data = segment.data(endian, elf_data).map_err(|_| {
FileDownloadError::Object("Failed to access data for an ELF segment.")
})?;
let mut elf_section = Vec::new();
if !segment_data.is_empty() && segment.p_type(endian) == PT_LOAD {
tracing::info!(
"Found loadable segment, physical address: {:#010x}, virtual address: {:#010x}, flags: {:#x}",
p_paddr,
p_vaddr,
flags
);
let (segment_offset, segment_filesize) = segment.file_range(endian);
let sector = segment_offset..segment_offset + segment_filesize;
for section in binary.sections() {
let (section_offset, section_filesize) = match section.file_range() {
Some(range) => range,
None => continue,
};
if sector.contains_range(&(section_offset..section_offset + section_filesize)) {
let name = section.name()?;
if options.skip_sections.iter().any(|skip| skip == name) {
tracing::info!("Skipping section: {:?}", name);
continue;
}
tracing::info!("Matching section: {:?}", name);
#[cfg(feature = "hexdump")]
for line in hexdump::hexdump_iter(section.data()?) {
tracing::trace!("{}", line);
}
for (offset, relocation) in section.relocations() {
tracing::info!(
"Relocation: offset={}, relocation={:?}",
offset,
relocation
);
}
elf_section.push(name.to_owned());
}
}
if elf_section.is_empty() {
tracing::info!("Not adding segment, no matching sections found.");
} else {
let section_data =
&elf_data[segment_offset as usize..][..segment_filesize as usize];
extracted_data.push(ExtractedFlashData {
section_names: elf_section,
address: p_paddr as u32,
data: section_data,
});
}
}
}
Ok(extracted_data)
}
pub(super) struct ExtractedFlashData<'data> {
pub(super) section_names: Vec<String>,
pub(super) address: u32,
pub(super) data: &'data [u8],
}
impl std::fmt::Debug for ExtractedFlashData<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut helper = f.debug_struct("ExtractedFlashData");
helper
.field("name", &self.section_names)
.field("address", &self.address);
if self.data.len() > 10 {
helper
.field("data", &format!("[..] ({} bytes)", self.data.len()))
.finish()
} else {
helper.field("data", &self.data).finish()
}
}
}
pub struct HexLoader;
impl ImageLoader for HexLoader {
fn load(
&self,
flash_loader: &mut FlashLoader,
_session: &mut Session,
file: &mut dyn ImageReader,
) -> Result<(), FileDownloadError> {
let mut base_address = 0;
let mut data = String::new();
file.read_to_string(&mut data)?;
for record in ihex::Reader::new(&data) {
match record? {
Record::Data { offset, value } => {
let offset = base_address + offset as u64;
flash_loader.add_data(offset, &value)?;
}
Record::ExtendedSegmentAddress(address) => {
base_address = (address as u64) * 16;
}
Record::ExtendedLinearAddress(address) => {
base_address = (address as u64) << 16;
}
Record::EndOfFile
| Record::StartSegmentAddress { .. }
| Record::StartLinearAddress(_) => {}
}
}
Ok(())
}
}
pub struct Uf2Loader;
impl ImageLoader for Uf2Loader {
fn load(
&self,
flash_loader: &mut FlashLoader,
_session: &mut Session,
file: &mut dyn ImageReader,
) -> Result<(), FileDownloadError> {
let mut uf2_buffer = Vec::new();
file.read_to_end(&mut uf2_buffer)?;
let (converted, family_to_target) = uf2_decode::convert_from_uf2(&uf2_buffer).unwrap();
let target_addresses = family_to_target.values();
let num_sections = family_to_target.len();
if let Some(target_address) = target_addresses.min() {
tracing::info!("Found {} loadable sections:", num_sections);
if num_sections > 1 {
tracing::warn!("More than 1 section found in UF2 file. Using first section.");
}
flash_loader.add_data(*target_address, &converted)?;
Ok(())
} else {
tracing::warn!("No loadable segments were found in the UF2 file.");
Err(FileDownloadError::NoLoadableSegments)
}
}
}
}
#[cfg(feature = "builtin-formats")]
pub use builtin::*;
#[derive(Clone, Debug, Default)]
pub enum BootInfo {
FromRam {
vector_table_addr: u64,
cores_to_reset: Vec<String>,
},
#[default]
Other,
}
pub struct FlashLoader {
memory_map: Vec<MemoryRegion>,
builder: FlashBuilder,
source: TargetDescriptionSource,
vector_table_addr: Option<u64>,
read_flasher_rtt: bool,
}
impl FlashLoader {
pub fn new(memory_map: Vec<MemoryRegion>, source: TargetDescriptionSource) -> Self {
Self {
memory_map,
builder: FlashBuilder::new(),
source,
vector_table_addr: None,
read_flasher_rtt: false,
}
}
pub fn flash_builder(&self) -> &FlashBuilder {
&self.builder
}
pub fn read_rtt_output(&mut self, read: bool) {
self.read_flasher_rtt = read;
}
pub fn vector_table_addr(&self) -> Option<u64> {
self.vector_table_addr
}
pub fn set_vector_table_addr(&mut self, vector_table_addr: u64) {
self.vector_table_addr = Some(vector_table_addr);
}
pub fn boot_info(&self) -> BootInfo {
let Some(vector_table_addr) = self.vector_table_addr else {
return BootInfo::Other;
};
match Self::get_region_for_address(&self.memory_map, vector_table_addr) {
Some(MemoryRegion::Ram(region)) => BootInfo::FromRam {
vector_table_addr,
cores_to_reset: region.cores.clone(),
},
_ => BootInfo::Other,
}
}
fn check_data_in_memory_map(&mut self, range: Range<u64>) -> Result<(), FlashError> {
let mut address = range.start;
while address < range.end {
match Self::get_region_for_address(&self.memory_map, address) {
Some(MemoryRegion::Nvm(region)) => address = region.range.end,
Some(MemoryRegion::Ram(region)) => address = region.range.end,
_ => {
return Err(FlashError::NoSuitableNvm {
range,
description_source: self.source.clone(),
});
}
}
}
Ok(())
}
pub fn add_data(&mut self, address: u64, data: &[u8]) -> Result<(), FlashError> {
tracing::trace!(
"Adding data at address {:#010x} with size {} bytes",
address,
data.len()
);
self.check_data_in_memory_map(address..address + data.len() as u64)?;
self.builder.add_data(address, data)
}
pub(super) fn get_region_for_address(
memory_map: &[MemoryRegion],
address: u64,
) -> Option<&MemoryRegion> {
memory_map.iter().find(|region| region.contains(address))
}
pub fn has_data_for_address(&self, address: u64) -> bool {
self.builder.has_data_in_range(&(address..address + 1))
}
pub fn load_image<T: Read + Seek>(
&mut self,
session: &mut Session,
file: &mut T,
format: impl ImageLoader,
image_instruction_set: Option<InstructionSet>,
) -> Result<(), FileDownloadError> {
if let Some(instr_set) = image_instruction_set {
let mut target_archs = Vec::with_capacity(session.list_cores().len());
for (core, _) in session.list_cores() {
match session.core(core) {
Ok(mut core) => {
if let Ok(set) = core.instruction_set()
&& !target_archs.contains(&set)
{
target_archs.push(set);
}
}
Err(crate::Error::CoreDisabled(_)) => continue,
Err(error) => return Err(FileDownloadError::Other(error)),
}
}
if !target_archs
.iter()
.any(|target| target.is_compatible(instr_set))
{
return Err(FileDownloadError::IncompatibleImage {
target: target_archs,
image: instr_set,
});
}
}
format.load(self, session, file)
}
pub fn verify(
&self,
session: &mut Session,
progress: &mut FlashProgress<'_>,
) -> Result<(), FlashError> {
let mut algos = self.prepare_plan(session, false, &[])?;
for flasher in algos.iter_mut() {
let mut program_size = 0;
for region in flasher.regions.iter_mut() {
program_size += region
.data
.encoder(flasher.flash_algorithm.transfer_encoding, true)
.program_size();
}
progress.add_progress_bar(ProgressOperation::Verify, Some(program_size));
}
for mut flasher in algos {
tracing::debug!(
"Verifying ranges for algo: {}",
flasher.flash_algorithm.name
);
if !flasher.verify(session, progress, true)? {
return Err(FlashError::Verify);
}
}
self.verify_ram(session)?;
Ok(())
}
pub fn commit(
&self,
session: &mut Session,
mut options: DownloadOptions,
) -> Result<(), FlashError> {
tracing::debug!("Committing FlashLoader!");
let mut algos = self.prepare_plan(
session,
options.keep_unwritten_bytes,
&options.preferred_algos,
)?;
if options.dry_run {
tracing::info!("Skipping programming, dry run!");
options.progress.failed_filling();
options.progress.failed_erasing();
options.progress.failed_programming();
return Ok(());
}
self.initialize(&mut algos, session, &mut options)?;
let mut do_chip_erase = options.do_chip_erase;
let mut did_chip_erase = false;
for mut flasher in algos {
tracing::debug!("Flashing ranges for algo: {}", flasher.flash_algorithm.name);
if do_chip_erase {
tracing::debug!(" Doing chip erase...");
flasher.run_erase_all(session, &mut options.progress)?;
do_chip_erase = false;
did_chip_erase = true;
}
let mut do_use_double_buffering = flasher.double_buffering_supported();
if do_use_double_buffering && options.disable_double_buffering {
tracing::info!(
"Disabled double-buffering support for loader via passed option, though target supports it."
);
do_use_double_buffering = false;
}
flasher.program(
session,
&mut options.progress,
options.keep_unwritten_bytes,
do_use_double_buffering,
options.skip_erase || did_chip_erase,
options.verify,
)?;
}
tracing::debug!("Committing RAM!");
if let BootInfo::FromRam { cores_to_reset, .. } = self.boot_info() {
tracing::debug!(
" -- action: vector table in RAM, assuming RAM boot, resetting and halting"
);
for (core_to_reset_index, _) in session
.target()
.cores
.clone()
.iter()
.enumerate()
.filter(|(_, c)| cores_to_reset.contains(&c.name))
{
session
.core(core_to_reset_index)
.and_then(|mut core| core.reset_and_halt(Duration::from_millis(500)))
.map_err(FlashError::Core)?;
}
}
for region in self
.memory_map
.iter()
.filter_map(MemoryRegion::as_ram_region)
{
let ranges_in_region: Vec<_> = self.builder.data_in_range(®ion.range).collect();
if ranges_in_region.is_empty() {
continue;
}
tracing::debug!(
" region: {:#010X?} ({} bytes)",
region.range,
region.range.end - region.range.start
);
let region_core_index = session
.target()
.core_index_by_name(
region
.cores
.first()
.ok_or_else(|| FlashError::NoRamCoreAccess(region.clone()))?,
)
.unwrap();
let mut core = session.core(region_core_index).map_err(FlashError::Core)?;
if !core.core_halted().map_err(FlashError::Core)? {
tracing::debug!(
" -- action: core is not halted and RAM is being written, halting"
);
core.halt(Duration::from_millis(500))
.map_err(FlashError::Core)?;
}
for (address, data) in ranges_in_region {
tracing::debug!(
" -- writing: {:#010X}..{:#010X} ({} bytes)",
address,
address + data.len() as u64,
data.len()
);
core.write(address, data).map_err(FlashError::Core)?;
}
}
if options.verify {
self.verify_ram(session)?;
}
Ok(())
}
fn prepare_plan(
&self,
session: &mut Session,
restore_unwritten_bytes: bool,
opt_preferred_algos: &[String],
) -> Result<Vec<Flasher>, FlashError> {
tracing::debug!("Contents of builder:");
for (&address, data) in &self.builder.data {
tracing::debug!(
" data: {:#010X}..{:#010X} ({} bytes)",
address,
address + data.len() as u64,
data.len()
);
}
tracing::debug!("Flash algorithms:");
for algorithm in &session.target().flash_algorithms {
let Range { start, end } = algorithm.flash_properties.address_range;
tracing::debug!(
" algo {}: {:#010X}..{:#010X} ({} bytes)",
algorithm.name,
start,
end,
end - start
);
}
if self.memory_map != session.target().memory_map {
tracing::warn!("Memory map of flash loader does not match memory map of target!");
}
let mut algos = Vec::<Flasher>::new();
tracing::debug!("Regions:");
for region in self
.memory_map
.iter()
.filter_map(MemoryRegion::as_nvm_region)
{
tracing::debug!(
" region: {:#010X?} ({} bytes)",
region.range,
region.range.end - region.range.start
);
if !self.builder.has_data_in_range(®ion.range) {
tracing::debug!(" -- empty, ignoring!");
continue;
}
let region = region.clone();
let Some(core_name) = region.cores.first() else {
return Err(FlashError::NoNvmCoreAccess(region));
};
let target = session.target();
let core = target.core_index_by_name(core_name).unwrap();
let algo = Self::get_flash_algorithm_for_region(
®ion,
target,
core_name,
opt_preferred_algos,
)?;
tracing::debug!(" -- using algorithm: {}", algo.name);
if let Some(entry) = algos
.iter_mut()
.find(|entry| entry.flash_algorithm.name == algo.name && entry.core_index == core)
{
entry.add_region(region, &self.builder, restore_unwritten_bytes)?;
} else {
let mut flasher = Flasher::new(target, core, algo)?;
flasher.add_region(region, &self.builder, restore_unwritten_bytes)?;
flasher.read_rtt_output(self.read_flasher_rtt);
algos.push(flasher);
}
}
Ok(algos)
}
fn initialize(
&self,
algos: &mut [Flasher],
session: &mut Session,
options: &mut DownloadOptions,
) -> Result<(), FlashError> {
let mut phases = vec![];
for flasher in algos.iter() {
if options.do_chip_erase && !flasher.is_chip_erase_supported(session) {
options.do_chip_erase = false;
tracing::warn!(
"Chip erase was the selected method to erase the sectors but this chip does not support chip erases (yet)."
);
tracing::warn!("A manual sector erase will be performed.");
}
}
if options.do_chip_erase {
options
.progress
.add_progress_bar(ProgressOperation::Erase, None);
}
for flasher in algos.iter_mut() {
let mut phase_layout = FlashLayout::default();
let mut fill_size = 0;
let mut erase_size = 0;
let mut program_size = 0;
for region in flasher.regions.iter_mut() {
let layout = region.flash_layout();
phase_layout.merge_from(layout.clone());
erase_size += layout.sectors().iter().map(|s| s.size()).sum::<u64>();
fill_size += layout.fills().iter().map(|s| s.size()).sum::<u64>();
program_size += region
.data
.encoder(
flasher.flash_algorithm.transfer_encoding,
!options.keep_unwritten_bytes,
)
.program_size();
}
if options.keep_unwritten_bytes {
options
.progress
.add_progress_bar(ProgressOperation::Fill, Some(fill_size));
}
if !options.do_chip_erase {
options
.progress
.add_progress_bar(ProgressOperation::Erase, Some(erase_size));
}
options
.progress
.add_progress_bar(ProgressOperation::Program, Some(program_size));
if options.verify {
options
.progress
.add_progress_bar(ProgressOperation::Verify, Some(program_size));
}
phases.push(phase_layout);
}
options.progress.initialized(phases);
Ok(())
}
fn verify_ram(&self, session: &mut Session) -> Result<(), FlashError> {
tracing::debug!("Verifying RAM!");
for (&address, data) in &self.builder.data {
tracing::debug!(
" data: {:#010X}..{:#010X} ({} bytes)",
address,
address + data.len() as u64,
data.len()
);
let associated_region = session.target().memory_region_by_address(address).unwrap();
if !associated_region.is_ram() {
continue;
}
let core_name = associated_region.cores().first().unwrap();
let core_index = session.target().core_index_by_name(core_name).unwrap();
let mut core = session.core(core_index).map_err(FlashError::Core)?;
let mut written_data = vec![0; data.len()];
core.read(address, &mut written_data)
.map_err(FlashError::Core)?;
if data != &written_data {
return Err(FlashError::Verify);
}
}
Ok(())
}
pub(crate) fn get_flash_algorithm_for_region<'a>(
region: &NvmRegion,
target: &'a Target,
core_name: &String,
preferred_algos: &[String],
) -> Result<&'a RawFlashAlgorithm, FlashError> {
let available = &target.flash_algorithms;
tracing::debug!("Available algorithms:");
for algorithm in available {
tracing::debug!(
"Algorithm: {} for {:?} @ 0x{:08x} - 0x{:08x} default? {}",
algorithm.name,
algorithm.cores,
algorithm.flash_properties.address_range.start,
algorithm.flash_properties.address_range.end,
algorithm.default
);
}
let algorithms = target
.flash_algorithms
.iter()
.filter(|&fa| {
fa.flash_properties
.address_range
.contains_range(®ion.range)
&& (fa.cores.is_empty() || fa.cores.contains(core_name))
})
.collect::<Vec<_>>();
match algorithms.len() {
0 => Err(FlashError::NoFlashLoaderAlgorithmAttached {
range: region.range.clone(),
name: target.name.clone(),
}),
1 => Ok(algorithms[0]),
_ => {
let defaults = algorithms
.iter()
.filter(|&fa| fa.default)
.collect::<Vec<_>>();
if !preferred_algos.is_empty() {
tracing::debug!("selecting preferred algorithm from: {:?}", preferred_algos);
let mut preferred_and_valid_algos = Vec::new();
for algo in algorithms.iter() {
if preferred_algos.iter().contains(&algo.name) {
preferred_and_valid_algos.push(algo);
}
}
if preferred_and_valid_algos.len() > 1 {
return Err(FlashError::MultiplePreferredAlgos {
region: region.clone(),
});
}
if preferred_and_valid_algos.len() == 1 {
return Ok(preferred_and_valid_algos[0]);
}
}
match defaults.len() {
0 => Err(FlashError::MultipleFlashLoaderAlgorithmsNoDefault {
region: region.clone(),
}),
1 => Ok(defaults[0]),
_ => Err(FlashError::MultipleDefaultFlashLoaderAlgorithms {
region: region.clone(),
}),
}
}
}
}
pub fn data(&self) -> impl Iterator<Item = (u64, &[u8])> {
self.builder
.data
.iter()
.map(|(address, data)| (*address, data.as_slice()))
}
}