diff --git a/cargo-espflash/src/main.rs b/cargo-espflash/src/main.rs index 9850f7f6..6b166e24 100644 --- a/cargo-espflash/src/main.rs +++ b/cargo-espflash/src/main.rs @@ -8,7 +8,7 @@ use std::{ use cargo_metadata::Message; use clap::{App, Arg, ArgMatches, SubCommand}; use error::Error; -use espflash::{Chip, Config, FirmwareImage, Flasher, PartitionTable}; +use espflash::{Chip, Config, FirmwareImage, Flasher, ImageFormatId, PartitionTable}; use miette::{IntoDiagnostic, Result, WrapErr}; use monitor::monitor; use package_metadata::CargoEspFlashMeta; @@ -17,6 +17,7 @@ use serial::{BaudRate, FlowControl, SerialPort}; use crate::cargo_config::CargoConfig; use crate::error::NoTargetError; use crate::{cargo_config::parse_cargo_config, error::UnsupportedTargetError}; +use std::str::FromStr; mod cargo_config; mod error; @@ -42,6 +43,11 @@ fn main() -> Result<()> { .takes_value(true) .value_name("FEATURES") .help("Comma delimited list of build features"), + Arg::with_name("format") + .long("format") + .takes_value(true) + .value_name("image format") + .help("Image format to flash"), ]; let connect_args = [Arg::with_name("serial") .takes_value(true) @@ -228,12 +234,18 @@ fn flash( None }; + let image_format = matches + .value_of("format") + .map(ImageFormatId::from_str) + .transpose()? + .or(metadata.format); + // Read the ELF data from the build path and load it to the target. let elf_data = fs::read(path).into_diagnostic()?; if matches.is_present("ram") { flasher.load_elf_to_ram(&elf_data)?; } else { - flasher.load_elf_to_flash(&elf_data, bootloader, partition_table)?; + flasher.load_elf_to_flash(&elf_data, bootloader, partition_table, image_format)?; } println!("\nFlashing has completed!"); @@ -266,13 +278,6 @@ fn build( cargo_config: &CargoConfig, chip: Option, ) -> Result { - // The 'build-std' unstable cargo feature is required to enable - // cross-compilation. If it has not been set then we cannot build the - // application. - if !cargo_config.has_build_std() { - return Err(Error::NoBuildStd.into()); - }; - let target = cargo_config .target() .ok_or_else(|| NoTargetError::new(chip))?; @@ -281,6 +286,13 @@ fn build( return Err(Error::UnsupportedTarget(UnsupportedTargetError::new(target, chip)).into()); } } + // The 'build-std' unstable cargo feature is required to enable + // cross-compilation for xtensa targets. + // If it has not been set then we cannot build the + // application. + if !cargo_config.has_build_std() && target.starts_with("xtensa-") { + return Err(Error::NoBuildStd.into()); + }; // Build the list of arguments to pass to 'cargo build'. let mut args = vec![]; @@ -356,7 +368,7 @@ fn build( fn save_image( matches: &ArgMatches, _config: Config, - _metadata: CargoEspFlashMeta, + metadata: CargoEspFlashMeta, cargo_config: CargoConfig, ) -> Result<()> { let target = cargo_config @@ -370,7 +382,13 @@ fn save_image( let image = FirmwareImage::from_data(&elf_data)?; - let flash_image = chip.get_flash_image(&image, None, None, None)?; + let image_format = matches + .value_of("format") + .map(ImageFormatId::from_str) + .transpose()? + .or(metadata.format); + + let flash_image = chip.get_flash_image(&image, None, None, image_format, None)?; let parts: Vec<_> = flash_image.ota_segments().collect(); let out_path = matches.value_of("file").unwrap(); diff --git a/cargo-espflash/src/package_metadata.rs b/cargo-espflash/src/package_metadata.rs index 7b62d022..a231b2ed 100644 --- a/cargo-espflash/src/package_metadata.rs +++ b/cargo-espflash/src/package_metadata.rs @@ -1,5 +1,6 @@ use crate::error::{Error, TomlError}; use cargo_toml::Manifest; +use espflash::ImageFormatId; use miette::{IntoDiagnostic, Result, WrapErr}; use serde::Deserialize; use std::fs::read_to_string; @@ -9,6 +10,7 @@ use std::path::Path; pub struct CargoEspFlashMeta { pub partition_table: Option, pub bootloader: Option, + pub format: Option, } #[derive(Clone, Debug, Default, Deserialize)] diff --git a/espflash/src/chip/esp32/esp32.rs b/espflash/src/chip/esp32/esp32.rs index e7939625..b0d8d821 100644 --- a/espflash/src/chip/esp32/esp32.rs +++ b/espflash/src/chip/esp32/esp32.rs @@ -1,6 +1,7 @@ use std::ops::Range; use super::Esp32Params; +use crate::error::UnsupportedImageFormatError; use crate::{ chip::{bytes_to_mac_addr, Chip, ChipType, ReadEFuse, SpiRegisters}, connection::Connection, @@ -117,6 +118,7 @@ impl ChipType for Esp32 { bootloader: Option>, partition_table: Option, image_format: ImageFormatId, + _chip_revision: Option, ) -> Result + 'a>, Error> { match image_format { ImageFormatId::Bootloader => Ok(Box::new(Esp32BootloaderFormat::new( @@ -126,9 +128,7 @@ impl ChipType for Esp32 { partition_table, bootloader, )?)), - ImageFormatId::DirectBoot => { - todo!() - } + _ => Err(UnsupportedImageFormatError::new(image_format, Chip::Esp32, None).into()), } } diff --git a/espflash/src/chip/esp32/esp32c3.rs b/espflash/src/chip/esp32/esp32c3.rs index 0eec50ea..286889dc 100644 --- a/espflash/src/chip/esp32/esp32c3.rs +++ b/espflash/src/chip/esp32/esp32c3.rs @@ -1,6 +1,8 @@ use std::ops::Range; use super::Esp32Params; +use crate::error::UnsupportedImageFormatError; +use crate::image_format::Esp32DirectBootFormat; use crate::{ chip::{bytes_to_mac_addr, ChipType, ReadEFuse, SpiRegisters}, connection::Connection, @@ -70,18 +72,22 @@ impl ChipType for Esp32c3 { bootloader: Option>, partition_table: Option, image_format: ImageFormatId, + chip_revision: Option, ) -> Result + 'a>, Error> { - match image_format { - ImageFormatId::Bootloader => Ok(Box::new(Esp32BootloaderFormat::new( + match (image_format, chip_revision) { + (ImageFormatId::Bootloader, _) => Ok(Box::new(Esp32BootloaderFormat::new( image, Chip::Esp32c3, PARAMS, partition_table, bootloader, )?)), - ImageFormatId::DirectBoot => { - todo!() + (ImageFormatId::DirectBoot, None | Some(3..)) => { + Ok(Box::new(Esp32DirectBootFormat::new(image)?)) } + _ => Err( + UnsupportedImageFormatError::new(image_format, Chip::Esp32c3, chip_revision).into(), + ), } } diff --git a/espflash/src/chip/esp32/esp32s2.rs b/espflash/src/chip/esp32/esp32s2.rs index cd658c81..fd2c9779 100644 --- a/espflash/src/chip/esp32/esp32s2.rs +++ b/espflash/src/chip/esp32/esp32s2.rs @@ -1,6 +1,7 @@ use std::ops::Range; use super::Esp32Params; +use crate::error::UnsupportedImageFormatError; use crate::{ chip::{bytes_to_mac_addr, ChipType, ReadEFuse, SpiRegisters}, connection::Connection, @@ -94,6 +95,7 @@ impl ChipType for Esp32s2 { bootloader: Option>, partition_table: Option, image_format: ImageFormatId, + _chip_revision: Option, ) -> Result + 'a>, Error> { match image_format { ImageFormatId::Bootloader => Ok(Box::new(Esp32BootloaderFormat::new( @@ -103,9 +105,7 @@ impl ChipType for Esp32s2 { partition_table, bootloader, )?)), - ImageFormatId::DirectBoot => { - todo!() - } + _ => Err(UnsupportedImageFormatError::new(image_format, Chip::Esp32s2, None).into()), } } diff --git a/espflash/src/chip/esp8266.rs b/espflash/src/chip/esp8266.rs index 00ef6d58..722e1b7f 100644 --- a/espflash/src/chip/esp8266.rs +++ b/espflash/src/chip/esp8266.rs @@ -47,10 +47,11 @@ impl ChipType for Esp8266 { _bootloader: Option>, _partition_table: Option, image_format: ImageFormatId, + _chip_revision: Option, ) -> Result + 'a>, Error> { match image_format { ImageFormatId::Bootloader => Ok(Box::new(Esp8266Format::new(image)?)), - _ => Err(UnsupportedImageFormatError::new(image_format, Chip::Esp8266).into()), + _ => Err(UnsupportedImageFormatError::new(image_format, Chip::Esp8266, None).into()), } } diff --git a/espflash/src/chip/mod.rs b/espflash/src/chip/mod.rs index 8595e345..f62c230a 100644 --- a/espflash/src/chip/mod.rs +++ b/espflash/src/chip/mod.rs @@ -53,6 +53,7 @@ pub trait ChipType { bootloader: Option>, partition_table: Option, image_format: ImageFormatId, + chip_revision: Option, ) -> Result + 'a>, Error>; /// Read the MAC address of the connected chip. @@ -158,20 +159,35 @@ impl Chip { bootloader: Option>, partition_table: Option, image_format: Option, + chip_revision: Option, ) -> Result + 'a>, Error> { let image_format = image_format.unwrap_or_else(|| self.default_image_format()); match self { - Chip::Esp32 => { - Esp32::get_flash_segments(image, bootloader, partition_table, image_format) + Chip::Esp32 => Esp32::get_flash_segments( + image, + bootloader, + partition_table, + image_format, + chip_revision, + ), + Chip::Esp32c3 => Esp32c3::get_flash_segments( + image, + bootloader, + partition_table, + image_format, + chip_revision, + ), + Chip::Esp32s2 => Esp32s2::get_flash_segments( + image, + bootloader, + partition_table, + image_format, + chip_revision, + ), + Chip::Esp8266 => { + Esp8266::get_flash_segments(image, None, None, image_format, chip_revision) } - Chip::Esp32c3 => { - Esp32c3::get_flash_segments(image, bootloader, partition_table, image_format) - } - Chip::Esp32s2 => { - Esp32s2::get_flash_segments(image, bootloader, partition_table, image_format) - } - Chip::Esp8266 => Esp8266::get_flash_segments(image, None, None, image_format), } } diff --git a/espflash/src/elf.rs b/espflash/src/elf.rs index 3f963d89..c247e9a1 100644 --- a/espflash/src/elf.rs +++ b/espflash/src/elf.rs @@ -6,6 +6,7 @@ use crate::error::{ElfError, Error}; use crate::flasher::FlashSize; use std::fmt::{Debug, Formatter}; use std::mem::take; +use std::ops::AddAssign; use xmas_elf::sections::{SectionData, ShType}; use xmas_elf::ElfFile; @@ -88,7 +89,7 @@ impl<'a> FirmwareImage<'a> { } } -#[derive(Eq, Clone)] +#[derive(Eq, Clone, Default)] /// A segment of code from the source elf pub struct CodeSegment<'a> { pub addr: u32, @@ -97,21 +98,12 @@ pub struct CodeSegment<'a> { impl<'a> CodeSegment<'a> { pub fn new(addr: u32, data: &'a [u8]) -> Self { - // pad to 4 byte - let padding = (4 - data.len() % 4) % 4; - if padding == 0 { - CodeSegment { - addr, - data: Cow::Borrowed(data), - } - } else { - let mut data = data.to_vec(); - data.extend_from_slice(&[0; 4][0..padding]); - CodeSegment { - addr, - data: Cow::Owned(data), - } - } + let mut segment = CodeSegment { + addr, + data: Cow::Borrowed(data), + }; + segment.pad_align(4); + segment } /// Split of the first `count` bytes into a new segment, adjusting the remaining segment as needed @@ -142,12 +134,6 @@ impl<'a> CodeSegment<'a> { } } - pub fn add(&mut self, extend: &[u8]) { - let mut data = take(&mut self.data).into_owned(); - data.extend_from_slice(extend); - self.data = Cow::Owned(data); - } - pub fn size(&self) -> u32 { self.data.len() as u32 } @@ -155,6 +141,34 @@ impl<'a> CodeSegment<'a> { pub fn data(&self) -> &[u8] { self.data.as_ref() } + + pub fn pad_align(&mut self, align: usize) { + let padding = (align - self.data.len() % align) % align; + if padding > 0 { + let mut data = take(&mut self.data).into_owned(); + data.extend_from_slice(&[0; 4][0..padding]); + self.data = Cow::Owned(data); + } + } +} + +impl<'a> AddAssign<&'_ [u8]> for CodeSegment<'a> { + fn add_assign(&mut self, rhs: &'_ [u8]) { + let mut data = take(&mut self.data).into_owned(); + data.extend_from_slice(rhs); + self.data = Cow::Owned(data); + } +} + +impl<'a> AddAssign<&'_ CodeSegment<'_>> for CodeSegment<'a> { + fn add_assign(&mut self, rhs: &'_ CodeSegment<'_>) { + let mut data = take(&mut self.data).into_owned(); + // pad or truncate + #[allow(clippy::suspicious_op_assign_impl)] + data.resize((rhs.addr - self.addr) as usize, 0); + data.extend_from_slice(rhs.data()); + self.data = Cow::Owned(data); + } } impl Debug for CodeSegment<'_> { @@ -184,6 +198,7 @@ impl Ord for CodeSegment<'_> { } } +#[derive(Clone)] /// A segment of data to write to the flash pub struct RomSegment<'a> { pub addr: u32, @@ -219,14 +234,14 @@ pub fn update_checksum(data: &[u8], mut checksum: u8) -> u8 { checksum } -pub fn merge_segments(mut segments: Vec) -> Vec { +pub fn merge_adjacent_segments(mut segments: Vec) -> Vec { segments.sort(); let mut merged: Vec = Vec::with_capacity(segments.len()); for segment in segments { match merged.last_mut() { Some(last) if last.addr + last.size() == segment.addr => { - last.add(segment.data()); + *last += segment.data(); } _ => { merged.push(segment); diff --git a/espflash/src/error.rs b/espflash/src/error.rs index 2bfa5562..03442fc7 100644 --- a/espflash/src/error.rs +++ b/espflash/src/error.rs @@ -6,7 +6,7 @@ use miette::{Diagnostic, SourceOffset, SourceSpan}; use slip_codec::Error as SlipError; use std::fmt::{Display, Formatter}; use std::io; -use strum::AsStaticRef; +use strum::{AsStaticRef, VariantNames}; use thiserror::Error; #[derive(Error, Debug, Diagnostic)] @@ -51,6 +51,21 @@ pub enum Error { #[error(transparent)] #[diagnostic(transparent)] UnsupportedImageFormat(#[from] UnsupportedImageFormatError), + #[error("Unrecognized image format {0}")] + #[diagnostic( + code(espflash::unknown_format), + help("The following image formats are {}", ImageFormatId::VARIANTS.join(", ")) + )] + UnknownImageFormat(String), + #[error("binary is not setup correct to support direct boot")] + #[diagnostic( + code(espflash::invalid_direct_boot), + help( + "See the following page for documentation on how to setup your binary for direct boot: +https://github.com/espressif/esp32c3-direct-boot-example" + ) + )] + InvalidDirectBootBinary, } #[derive(Error, Debug, Diagnostic)] @@ -482,20 +497,58 @@ impl From for FlashDetectError { } } -#[derive(Debug, Error, Diagnostic)] -#[error("Image format {format} is not supported by the {chip}")] -#[diagnostic( - code(espflash::unsupported_image_format), - help("The following image formats are supported by the {}: {}", self.chip, self.supported_formats()) -)] +#[derive(Debug)] pub struct UnsupportedImageFormatError { format: ImageFormatId, chip: Chip, + revision: Option, +} + +impl Display for UnsupportedImageFormatError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!( + f, + "Image format {} is not supported by the {}", + self.format, self.chip + )?; + if let Some(revision) = self.revision { + write!(f, " revision {}", revision)?; + } + Ok(()) + } +} + +impl std::error::Error for UnsupportedImageFormatError {} + +impl Diagnostic for UnsupportedImageFormatError { + fn code<'a>(&'a self) -> Option> { + Some(Box::new("espflash::unsupported_image_format")) + } + + fn help<'a>(&'a self) -> Option> { + let str = if self.chip == Chip::Esp32c3 && self.format == ImageFormatId::DirectBoot { + format!( + "The {}: only supports direct-boot starting with revision 3", + self.chip, + ) + } else { + format!( + "The following image formats are supported by the {}: {}", + self.chip, + self.supported_formats() + ) + }; + Some(Box::new(str)) + } } impl UnsupportedImageFormatError { - pub fn new(format: ImageFormatId, chip: Chip) -> Self { - UnsupportedImageFormatError { format, chip } + pub fn new(format: ImageFormatId, chip: Chip, revision: Option) -> Self { + UnsupportedImageFormatError { + format, + chip, + revision, + } } fn supported_formats(&self) -> String { diff --git a/espflash/src/flasher.rs b/espflash/src/flasher.rs index b0307102..2b378cb2 100644 --- a/espflash/src/flasher.rs +++ b/espflash/src/flasher.rs @@ -4,6 +4,7 @@ use bytemuck::{__core::time::Duration, bytes_of, Pod, Zeroable}; use serial::{BaudRate, SystemPort}; use strum_macros::Display; +use crate::image_format::ImageFormatId; use crate::{ chip::Chip, connection::Connection, @@ -533,6 +534,7 @@ impl Flasher { elf_data: &[u8], bootloader: Option>, partition_table: Option, + image_format: Option, ) -> Result<(), Error> { let mut image = FirmwareImage::from_data(elf_data)?; image.flash_size = self.flash_size(); @@ -540,9 +542,13 @@ impl Flasher { let mut target = self.chip.flash_target(self.spi_params); target.begin(&mut self.connection, &image).flashing()?; - let flash_image = self - .chip - .get_flash_image(&image, bootloader, partition_table, None)?; + let flash_image = self.chip.get_flash_image( + &image, + bootloader, + partition_table, + image_format, + self.chip.chip_revision(&mut self.connection)?, + )?; for segment in flash_image.flash_segments() { target diff --git a/espflash/src/image_format/esp32bootloader.rs b/espflash/src/image_format/esp32bootloader.rs index 1622f763..c386324e 100644 --- a/espflash/src/image_format/esp32bootloader.rs +++ b/espflash/src/image_format/esp32bootloader.rs @@ -6,7 +6,8 @@ use sha2::{Digest, Sha256}; use crate::{ chip::Esp32Params, elf::{ - merge_segments, update_checksum, CodeSegment, FirmwareImage, RomSegment, ESP_CHECKSUM_MAGIC, + merge_adjacent_segments, update_checksum, CodeSegment, FirmwareImage, RomSegment, + ESP_CHECKSUM_MAGIC, }, error::{Error, FlashDetectError}, flasher::FlashSize, @@ -62,8 +63,8 @@ impl<'a> Esp32BootloaderFormat<'a> { let mut checksum = ESP_CHECKSUM_MAGIC; - let flash_segments: Vec<_> = merge_segments(image.rom_segments(chip).collect()); - let mut ram_segments: Vec<_> = merge_segments(image.ram_segments(chip).collect()); + let flash_segments: Vec<_> = merge_adjacent_segments(image.rom_segments(chip).collect()); + let mut ram_segments: Vec<_> = merge_adjacent_segments(image.ram_segments(chip).collect()); let mut segment_count = 0; diff --git a/espflash/src/image_format/esp32directboot.rs b/espflash/src/image_format/esp32directboot.rs new file mode 100644 index 00000000..ede15606 --- /dev/null +++ b/espflash/src/image_format/esp32directboot.rs @@ -0,0 +1,55 @@ +use crate::elf::CodeSegment; +use crate::{ + elf::{FirmwareImage, RomSegment}, + error::Error, + image_format::ImageFormat, +}; +use std::iter::once; + +/// Image format for esp32 family chips using a 2nd stage bootloader +pub struct Esp32DirectBootFormat<'a> { + segment: RomSegment<'a>, +} + +impl<'a> Esp32DirectBootFormat<'a> { + pub fn new(image: &'a FirmwareImage) -> Result { + let mut segment = image + .segments() + .map(|mut segment| { + // map address to the first 4MB + segment.addr %= 0x400000; + segment + }) + .fold(CodeSegment::default(), |mut a, b| { + a += &b; + a + }); + segment.pad_align(4); + + if segment.addr != 0 + || segment.data()[0..8] != [0x1d, 0x04, 0xdb, 0xae, 0x1d, 0x04, 0xdb, 0xae] + { + return Err(Error::InvalidDirectBootBinary); + } + + Ok(Self { + segment: segment.into(), + }) + } +} + +impl<'a> ImageFormat<'a> for Esp32DirectBootFormat<'a> { + fn flash_segments<'b>(&'b self) -> Box> + 'b> + where + 'a: 'b, + { + Box::new(once(self.segment.borrow())) + } + + fn ota_segments<'b>(&'b self) -> Box> + 'b> + where + 'a: 'b, + { + Box::new(once(self.segment.borrow())) + } +} diff --git a/espflash/src/image_format/mod.rs b/espflash/src/image_format/mod.rs index ee0e52fb..1ae41340 100644 --- a/espflash/src/image_format/mod.rs +++ b/espflash/src/image_format/mod.rs @@ -1,12 +1,17 @@ mod esp32bootloader; +mod esp32directboot; mod esp8266; use crate::elf::RomSegment; use bytemuck::{Pod, Zeroable}; pub use esp32bootloader::*; +pub use esp32directboot::*; pub use esp8266::*; -use strum_macros::{AsStaticStr, Display, EnumString}; +use crate::error::Error; +use serde::Deserialize; +use std::str::FromStr; +use strum_macros::{AsStaticStr, Display, EnumVariantNames}; const ESP_MAGIC: u8 = 0xE9; const WP_PIN_DISABLED: u8 = 0xEE; @@ -42,10 +47,24 @@ pub trait ImageFormat<'a> { 'a: 'b; } -#[derive(Debug, Copy, Clone, Eq, PartialEq, Display, EnumString, AsStaticStr)] +#[derive( + Debug, Copy, Clone, Eq, PartialEq, Display, AsStaticStr, EnumVariantNames, Deserialize, +)] +#[strum(serialize_all = "kebab-case")] +#[serde(rename_all = "kebab-case")] pub enum ImageFormatId { - #[strum(serialize = "bootloader")] Bootloader, - #[strum(serialize = "direct-boot")] DirectBoot, } + +impl FromStr for ImageFormatId { + type Err = Error; + + fn from_str(s: &str) -> Result { + match s { + "bootloader" => Ok(Self::Bootloader), + "direct-boot" => Ok(Self::DirectBoot), + _ => Err(Error::UnknownImageFormat(s.into())), + } + } +} diff --git a/espflash/src/lib.rs b/espflash/src/lib.rs index 5d5c1a33..aedc8fdc 100644 --- a/espflash/src/lib.rs +++ b/espflash/src/lib.rs @@ -14,4 +14,5 @@ pub use config::Config; pub use elf::FirmwareImage; pub use error::Error; pub use flasher::Flasher; +pub use image_format::ImageFormatId; pub use partition_table::PartitionTable; diff --git a/espflash/src/main.rs b/espflash/src/main.rs index 5738d25a..c29aaab3 100644 --- a/espflash/src/main.rs +++ b/espflash/src/main.rs @@ -1,13 +1,14 @@ use std::fs::{read, read_to_string}; -use espflash::{Config, Error, Flasher, PartitionTable}; +use espflash::{Config, Error, Flasher, ImageFormatId, PartitionTable}; use miette::{IntoDiagnostic, Result, WrapErr}; use pico_args::Arguments; use serial::{BaudRate, FlowControl, SerialPort}; +use std::str::FromStr; #[allow(clippy::unnecessary_wraps)] fn help() -> Result<()> { - println!("Usage: espflash [--board-info] [--ram] [--partition-table partition.csv] [--bootloader boot.bin] "); + println!("Usage: espflash [--board-info] [--ram] [--partition-table partition.csv] [--bootloader boot.bin] [--format "); Ok(()) } @@ -27,6 +28,9 @@ fn main() -> Result<()> { let partition_table_path = args .opt_value_from_str::<_, String>("--partition-table") .into_diagnostic()?; + let image_format_string = args + .opt_value_from_str::<_, String>("--format") + .into_diagnostic()?; let mut serial: Option = args.opt_free_from_str().into_diagnostic()?; let mut elf: Option = args.opt_free_from_str().into_diagnostic()?; @@ -83,6 +87,10 @@ fn main() -> Result<()> { bootloader_path.unwrap() ) })?; + let image_format = image_format_string + .as_deref() + .map(ImageFormatId::from_str) + .transpose()?; let partition_table = partition_table_path .as_deref() .map(|path| { @@ -97,7 +105,7 @@ fn main() -> Result<()> { partition_table_path.unwrap() ) })?; - flasher.load_elf_to_flash(&input_bytes, bootloader, partition_table)?; + flasher.load_elf_to_flash(&input_bytes, bootloader, partition_table, image_format)?; } Ok(())