Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for erasing otadata #229

Merged
merged 3 commits into from
Sep 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion cargo-espflash/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,10 +140,15 @@ fn main() -> Result<()> {
.init();

// Extract subcommand
let CargoSubCommand::Espflash(opts) = opts.subcommand;
let CargoSubCommand::Espflash(mut opts) = opts.subcommand;

debug!("subcommand options: {:?}", opts);

// `erase_otadata` requires `use_stub`
if opts.flash_opts.erase_otadata {
opts.connect_opts.use_stub = true;
}

// Load configuration and metadata
let config = Config::load()?;
let metadata = CargoEspFlashMeta::load("Cargo.toml")?;
Expand Down Expand Up @@ -223,6 +228,7 @@ fn flash(
opts.build_opts.flash_config_opts.flash_mode,
opts.build_opts.flash_config_opts.flash_size,
opts.build_opts.flash_config_opts.flash_freq,
opts.flash_opts.erase_otadata,
)?;
}

Expand Down
30 changes: 28 additions & 2 deletions espflash/src/cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,10 @@ use crate::{
cli::monitor::monitor,
cli::serial::get_serial_port_info,
elf::{ElfFirmwareImage, FlashFrequency, FlashMode},
error::Error,
error::{Error, NoOtadataError},
flasher::FlashSize,
Chip, Flasher, ImageFormatId, InvalidPartitionTable, PartitionTable,
partition_table, Chip, Flasher, ImageFormatId, InvalidPartitionTable, MissingPartitionTable,
PartitionTable,
};

pub mod config;
Expand Down Expand Up @@ -59,6 +60,10 @@ pub struct FlashOpts {
/// Open a serial monitor after flashing
#[clap(long)]
pub monitor: bool,
/// Erase the OTADATA partition
/// This is useful when using multiple OTA partitions and still wanting to be able to reflash via espflash
#[clap(long)]
pub erase_otadata: bool,
maximeborges marked this conversation as resolved.
Show resolved Hide resolved
}

#[derive(Debug, Clone, Parser)]
Expand Down Expand Up @@ -280,6 +285,7 @@ pub fn flash_elf_image(
flash_mode: Option<FlashMode>,
flash_size: Option<FlashSize>,
flash_freq: Option<FlashFrequency>,
erase_otadata: bool,
) -> Result<()> {
// If the '--bootloader' option is provided, load the binary file at the
// specified path.
Expand Down Expand Up @@ -307,6 +313,26 @@ pub fn flash_elf_image(
None
};

if erase_otadata {
let partition_table = match &partition_table {
Some(partition_table) => partition_table,
None => return Err((MissingPartitionTable {}).into()),
};

let otadata = match partition_table.find_by_subtype(
partition_table::Type::CoreType(partition_table::CoreType::Data),
partition_table::SubType::Data(partition_table::DataType::Ota),
) {
Some(otadata) => otadata,
None => return Err((NoOtadataError {}).into()),
};

let offset = otadata.offset();
let size = otadata.size();

flasher.erase_region(offset, size)?;
}

// Load the ELF data, optionally using the provider bootloader/partition
// table/image format, to the device's flash memory.
flasher.load_elf_to_flash_with_format(
Expand Down
26 changes: 25 additions & 1 deletion espflash/src/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use strum_macros::Display;
const DEFAULT_TIMEOUT: Duration = Duration::from_secs(3);
const ERASE_REGION_TIMEOUT_PER_MB: Duration = Duration::from_secs(30);
const ERASE_WRITE_TIMEOUT_PER_MB: Duration = Duration::from_secs(40);
const ERASE_CHIP_TIMEOUT: Duration = Duration::from_secs(120);
const MEM_END_TIMEOUT: Duration = Duration::from_millis(50);
const SYNC_TIMEOUT: Duration = Duration::from_millis(100);

Expand All @@ -34,13 +35,17 @@ pub enum CommandType {
FlashDeflateEnd = 0x12,
FlashMd5 = 0x13,
FlashDetect = 0x9f,
// Some commands supported by stub only
EraseFlash = 0xd0,
EraseRegion = 0xd1,
}

impl CommandType {
pub fn timeout(&self) -> Duration {
match self {
CommandType::MemEnd => MEM_END_TIMEOUT,
CommandType::Sync => SYNC_TIMEOUT,
CommandType::EraseFlash => ERASE_CHIP_TIMEOUT,
_ => DEFAULT_TIMEOUT,
}
}
Expand All @@ -54,7 +59,7 @@ impl CommandType {
)
}
match self {
CommandType::FlashBegin | CommandType::FlashDeflateBegin => {
CommandType::FlashBegin | CommandType::FlashDeflateBegin | CommandType::EraseRegion => {
calc_timeout(ERASE_REGION_TIMEOUT_PER_MB, size)
}
CommandType::FlashData | CommandType::FlashDeflateData => {
Expand Down Expand Up @@ -138,6 +143,11 @@ pub enum Command<'a> {
reboot: bool,
},
FlashDetect,
EraseFlash,
EraseRegion {
offset: u32,
size: u32,
},
}

impl<'a> Command<'a> {
Expand All @@ -159,6 +169,8 @@ impl<'a> Command<'a> {
Command::FlashDeflateData { .. } => CommandType::FlashDeflateData,
Command::FlashDeflateEnd { .. } => CommandType::FlashDeflateEnd,
Command::FlashDetect => CommandType::FlashDetect,
Command::EraseFlash { .. } => CommandType::EraseFlash,
Command::EraseRegion { .. } => CommandType::EraseRegion,
}
}

Expand Down Expand Up @@ -319,6 +331,18 @@ impl<'a> Command<'a> {
Command::FlashDetect => {
write_basic(writer, &[], 0)?;
}
Command::EraseFlash => {
write_basic(writer, &[], 0)?;
}
Command::EraseRegion { offset, size } => {
// length
writer.write_all(&(8u16.to_le_bytes()))?;
// checksum
writer.write_all(&(0u32.to_le_bytes()))?;
// data
writer.write_all(&offset.to_le_bytes())?;
writer.write_all(&size.to_le_bytes())?;
}
};
Ok(())
}
Expand Down
15 changes: 15 additions & 0 deletions espflash/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,9 @@ pub enum PartitionTableError {
#[error(transparent)]
#[diagnostic(transparent)]
InvalidPartitionTable(#[from] InvalidPartitionTable),
#[error(transparent)]
#[diagnostic(transparent)]
MissingPartitionTable(#[from] MissingPartitionTable),
}

#[derive(Debug, Error, Diagnostic)]
Expand Down Expand Up @@ -536,7 +539,14 @@ impl NoAppError {
}
}
}
#[derive(Debug, Error, Diagnostic)]
#[error("No otadata partition was found")]
#[diagnostic(
code(espflash::partition_table::no_otadata),
help("Partition table must contain an otadata partition when trying to erase it")
)]

pub struct NoOtadataError;
#[derive(Debug, Error, Diagnostic)]
#[error("Unaligned partition")]
#[diagnostic(code(espflash::partition_table::unaligned))]
Expand Down Expand Up @@ -576,6 +586,11 @@ pub struct NoEndMarker;
#[diagnostic(code(espflash::partition_table::invalid_partition_table))]
pub struct InvalidPartitionTable;

#[derive(Debug, Error, Diagnostic)]
#[error("Missing partition table")]
#[diagnostic(code(espflash::partition_table::missing_partition_table))]
pub struct MissingPartitionTable;

#[derive(Debug, Error)]
#[error("{0}")]
pub struct ElfError(&'static str);
Expand Down
12 changes: 12 additions & 0 deletions espflash/src/flasher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -635,6 +635,18 @@ impl Flasher {
pub fn get_usb_pid(&self) -> Result<u16, Error> {
self.connection.get_usb_pid()
}

pub fn erase_region(&mut self, offset: u32, size: u32) -> Result<(), Error> {
debug!("Erasing region of 0x{:x}B at 0x{:08x}", size, offset);

self.connection
.with_timeout(CommandType::EraseRegion.timeout(), |connection| {
connection.command(Command::EraseRegion { offset, size })
})?;
std::thread::sleep(Duration::from_secs_f32(0.05));
self.connection.flush()?;
Ok(())
}
}

pub(crate) fn get_erase_size(offset: usize, size: usize) -> usize {
Expand Down
2 changes: 1 addition & 1 deletion espflash/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
pub use chip::Chip;
pub use cli::config::Config;
pub use elf::{FlashFrequency, FlashMode};
pub use error::{Error, InvalidPartitionTable};
pub use error::{Error, InvalidPartitionTable, MissingPartitionTable};
pub use flasher::{FlashSize, Flasher};
pub use image_format::ImageFormatId;
pub use partition_table::PartitionTable;
Expand Down
5 changes: 5 additions & 0 deletions espflash/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@ fn main() -> Result<()> {

debug!("options: {:?}", opts);

if opts.flash_opts.erase_otadata {
opts.connect_opts.use_stub = true;
}
maximeborges marked this conversation as resolved.
Show resolved Hide resolved

// Setup logging
tracing_subscriber::fmt()
.with_env_filter(EnvFilter::from_default_env().add_directive(opts.log_level.into()))
Expand Down Expand Up @@ -164,6 +168,7 @@ fn flash(opts: Opts, config: Config) -> Result<()> {
opts.flash_config_opts.flash_mode,
opts.flash_config_opts.flash_size,
opts.flash_config_opts.flash_freq,
opts.flash_opts.erase_otadata,
)?;
}

Expand Down
10 changes: 10 additions & 0 deletions espflash/src/partition_table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,12 @@ impl PartitionTable {
self.partitions.iter().find(|&p| p.ty == ty)
}

pub fn find_by_subtype(&self, ty: Type, sub_type: SubType) -> Option<&Partition> {
self.partitions
.iter()
.find(|&p| p.ty == ty && p.sub_type == sub_type)
}

fn validate(&self, source: &str) -> Result<(), PartitionTableError> {
for partition in &self.partitions {
if let Some(line) = &partition.line {
Expand Down Expand Up @@ -662,6 +668,10 @@ impl Partition {
self.offset
}

pub fn size(&self) -> u32 {
self.size
}

pub fn flags(&self) -> Option<Flags> {
self.flags
}
Expand Down