-
Notifications
You must be signed in to change notification settings - Fork 149
Support flashing in Secure Download Mode #990
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -57,6 +57,9 @@ pub(crate) const TRY_SPI_PARAMS: [SpiAttachParams; 2] = | |
| pub(crate) const FLASH_SECTOR_SIZE: usize = 0x1000; | ||
| pub(crate) const FLASH_WRITE_SIZE: usize = 0x400; | ||
|
|
||
| #[cfg(feature = "serialport")] | ||
| pub(crate) const BOOTLOADER_PROTECTION_ADDR: u32 = 0x8000; | ||
|
|
||
| /// Supported flash frequencies | ||
| /// | ||
| /// Note that not all frequencies are supported by each target device. | ||
|
|
@@ -924,9 +927,26 @@ impl Flasher { | |
| progress: &mut dyn ProgressCallbacks, | ||
| image_format: ImageFormat<'a>, | ||
| ) -> Result<(), Error> { | ||
| let mut target = | ||
| self.chip | ||
| .flash_target(self.spi_params, self.use_stub, self.verify, self.skip); | ||
| let (mut verify, mut skip) = (self.verify, self.skip); | ||
| if self.connection.secure_download_mode { | ||
| for segment in image_format.clone().flash_segments() { | ||
| if segment.addr < BOOTLOADER_PROTECTION_ADDR { | ||
| return Err(Error::SecureDownloadBootloaderProtection); | ||
| } | ||
| } | ||
|
Comment on lines
+932
to
+936
|
||
|
|
||
| if self.verify || self.skip { | ||
| warn!( | ||
| "Secure Download Mode enabled: --verify and --skip options are not available \ | ||
| (flash read operations are restricted)" | ||
| ); | ||
| } | ||
| (verify, skip) = (false, false); | ||
| } | ||
|
|
||
| let mut target = self | ||
| .chip | ||
| .flash_target(self.spi_params, self.use_stub, verify, skip); | ||
| target.begin(&mut self.connection).flashing()?; | ||
|
|
||
| // When the `cli` feature is enabled, display the image size information. | ||
|
|
@@ -987,16 +1007,26 @@ impl Flasher { | |
| segments: &[Segment<'_>], | ||
| progress: &mut dyn ProgressCallbacks, | ||
| ) -> Result<(), Error> { | ||
| let (mut verify, mut skip) = (self.verify, self.skip); | ||
| if self.connection.secure_download_mode { | ||
| return Err(Error::UnsupportedFeature { | ||
| chip: self.chip, | ||
| feature: "writing binaries in Secure Download Mode currently".into(), | ||
| }); | ||
| for segment in segments { | ||
| if segment.addr < BOOTLOADER_PROTECTION_ADDR { | ||
| return Err(Error::SecureDownloadBootloaderProtection); | ||
| } | ||
| } | ||
|
|
||
| if self.verify || self.skip { | ||
| warn!( | ||
| "Secure Download Mode enabled: --verify and --skip options are not available \ | ||
| (flash read operations are restricted)" | ||
| ); | ||
| } | ||
| (verify, skip) = (false, false); | ||
| } | ||
|
|
||
| let mut target = | ||
| self.chip | ||
| .flash_target(self.spi_params, self.use_stub, self.verify, self.skip); | ||
| let mut target = self | ||
| .chip | ||
| .flash_target(self.spi_params, self.use_stub, verify, skip); | ||
|
|
||
| target.begin(&mut self.connection).flashing()?; | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
|
|
@@ -3,7 +3,7 @@ | |||||||
| //! This module defines the traits and types used for flashing operations on a | ||||||||
| //! target device's flash memory. | ||||||||
| use std::io::Write; | ||||||||
| use std::{io::Write, iter::repeat_n}; | ||||||||
|
|
||||||||
| use flate2::{ | ||||||||
| Compression, | ||||||||
|
|
@@ -34,7 +34,7 @@ pub struct Esp32Target { | |||||||
| use_stub: bool, | ||||||||
| verify: bool, | ||||||||
| skip: bool, | ||||||||
| need_deflate_end: bool, | ||||||||
| need_flash_end: bool, | ||||||||
| } | ||||||||
|
|
||||||||
| impl Esp32Target { | ||||||||
|
|
@@ -52,7 +52,7 @@ impl Esp32Target { | |||||||
| use_stub, | ||||||||
| verify, | ||||||||
| skip, | ||||||||
| need_deflate_end: false, | ||||||||
| need_flash_end: false, | ||||||||
| } | ||||||||
| } | ||||||||
| } | ||||||||
|
|
@@ -79,7 +79,10 @@ impl FlashTarget for Esp32Target { | |||||||
| // | ||||||||
| // TODO: the stub doesn't appear to disable the watchdog on ESP32-S3, so we | ||||||||
| // explicitly disable the watchdog here. | ||||||||
| if connection.is_using_usb_serial_jtag() { | ||||||||
| // | ||||||||
| // NOTE: In Secure Download Mode, WRITE_REG commands are not allowed, so we | ||||||||
| // must skip the watchdog disable. | ||||||||
| if connection.is_using_usb_serial_jtag() && !connection.secure_download_mode { | ||||||||
| if let (Some(wdt_wprotect), Some(wdt_config0)) = | ||||||||
| (self.chip.wdt_wprotect(), self.chip.wdt_config0()) | ||||||||
| { | ||||||||
|
|
@@ -116,22 +119,15 @@ impl FlashTarget for Esp32Target { | |||||||
| md5_hasher.update(&segment.data); | ||||||||
| let checksum_md5 = md5_hasher.finalize(); | ||||||||
|
|
||||||||
| let mut encoder = ZlibEncoder::new(Vec::new(), Compression::best()); | ||||||||
| encoder.write_all(&segment.data)?; | ||||||||
| let compressed = encoder.finish()?; | ||||||||
| // use compression only when stub is loaded. | ||||||||
| let use_compression = self.use_stub; | ||||||||
|
|
||||||||
| let flash_write_size = self.chip.flash_write_size(); | ||||||||
| let block_count = compressed.len().div_ceil(flash_write_size); | ||||||||
| let erase_count = segment.data.len().div_ceil(FLASH_SECTOR_SIZE); | ||||||||
|
|
||||||||
| // round up to sector size | ||||||||
| let erase_size = (erase_count * FLASH_SECTOR_SIZE) as u32; | ||||||||
|
|
||||||||
| let chunks = compressed.chunks(flash_write_size); | ||||||||
| let num_chunks = chunks.len(); | ||||||||
|
|
||||||||
| progress.init(addr, num_chunks); | ||||||||
|
|
||||||||
| if self.skip { | ||||||||
| let flash_checksum_md5: u128 = connection.with_timeout( | ||||||||
| CommandType::FlashMd5.timeout_for_size(segment.data.len() as u32), | ||||||||
|
|
@@ -153,43 +149,91 @@ impl FlashTarget for Esp32Target { | |||||||
| } | ||||||||
| } | ||||||||
|
|
||||||||
| connection.with_timeout( | ||||||||
| CommandType::FlashDeflBegin.timeout_for_size(erase_size), | ||||||||
| |connection| { | ||||||||
| connection.command(Command::FlashDeflBegin { | ||||||||
| size: segment.data.len() as u32, | ||||||||
| blocks: block_count as u32, | ||||||||
| block_size: flash_write_size as u32, | ||||||||
| offset: addr, | ||||||||
| supports_encryption: self.chip != Chip::Esp32 && !self.use_stub, | ||||||||
| })?; | ||||||||
| Ok(()) | ||||||||
| }, | ||||||||
| )?; | ||||||||
| self.need_deflate_end = true; | ||||||||
| let data = if use_compression { | ||||||||
| let mut encoder = ZlibEncoder::new(Vec::new(), Compression::best()); | ||||||||
| encoder.write_all(&segment.data)?; | ||||||||
| encoder.finish()? | ||||||||
| } else { | ||||||||
| let mut data = segment.data.to_vec(); | ||||||||
| let padding = (flash_write_size - (data.len() % flash_write_size)) % flash_write_size; | ||||||||
| data.extend(repeat_n(0xff, padding)); | ||||||||
| data | ||||||||
|
Comment on lines
+157
to
+160
|
||||||||
| }; | ||||||||
|
|
||||||||
| // decode the chunks to see how much data the device will have to save | ||||||||
| let mut decoder = ZlibDecoder::new(Vec::new()); | ||||||||
| let mut decoded_size = 0; | ||||||||
| let block_count = data.len().div_ceil(flash_write_size); | ||||||||
| let chunks = data.chunks(flash_write_size); | ||||||||
| let num_chunks = chunks.len(); | ||||||||
|
|
||||||||
| for (i, block) in chunks.enumerate() { | ||||||||
| decoder.write_all(block)?; | ||||||||
| decoder.flush()?; | ||||||||
| let size = decoder.get_ref().len() - decoded_size; | ||||||||
| decoded_size = decoder.get_ref().len(); | ||||||||
| progress.init(addr, num_chunks); | ||||||||
|
|
||||||||
| if use_compression { | ||||||||
| connection.with_timeout( | ||||||||
| CommandType::FlashDeflBegin.timeout_for_size(erase_size), | ||||||||
| |connection| { | ||||||||
| connection.command(Command::FlashDeflBegin { | ||||||||
| size: segment.data.len() as u32, | ||||||||
| blocks: block_count as u32, | ||||||||
| block_size: flash_write_size as u32, | ||||||||
| offset: addr, | ||||||||
| supports_encryption: self.chip != Chip::Esp32 && !self.use_stub, | ||||||||
| })?; | ||||||||
| Ok(()) | ||||||||
| }, | ||||||||
| )?; | ||||||||
| self.need_flash_end = true; | ||||||||
| } else { | ||||||||
| connection.with_timeout( | ||||||||
| CommandType::FlashDeflData.timeout_for_size(size as u32), | ||||||||
| CommandType::FlashBegin.timeout_for_size(erase_size), | ||||||||
| |connection| { | ||||||||
| connection.command(Command::FlashDeflData { | ||||||||
| sequence: i as u32, | ||||||||
| pad_to: 0, | ||||||||
| pad_byte: 0xff, | ||||||||
| data: block, | ||||||||
| connection.command(Command::FlashBegin { | ||||||||
| size: erase_size, | ||||||||
| blocks: block_count as u32, | ||||||||
| block_size: flash_write_size as u32, | ||||||||
| offset: addr, | ||||||||
| supports_encryption: self.chip != Chip::Esp32, | ||||||||
| })?; | ||||||||
| Ok(()) | ||||||||
| }, | ||||||||
| )?; | ||||||||
|
||||||||
| )?; | |
| )?; | |
| self.need_flash_end = true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is absolutely valid, messed up my rebase and caused this.
Uh oh!
There was an error while loading. Please reload this page.