diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b0dab5c..0c6e3774 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -230,6 +230,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Make the default flashing frequency target specific (#389) - Add note about permissions on Linux (#391) - Add a diagnostic to tell the user about the partition table format (#397) +- Add (some level of) SDM support (#832) ### Fixed diff --git a/espflash/src/cli/mod.rs b/espflash/src/cli/mod.rs index 85fc97a4..01c79ebd 100644 --- a/espflash/src/cli/mod.rs +++ b/espflash/src/cli/mod.rs @@ -572,17 +572,21 @@ pub fn parse_chip_rev(chip_rev: &str) -> Result { /// Print information about a chip pub fn print_board_info(flasher: &mut Flasher) -> Result<()> { let info = flasher.device_info()?; - print!("Chip type: {}", info.chip); + if let Some((major, minor)) = info.revision { println!(" (revision v{major}.{minor})"); } else { println!(); } + println!("Crystal frequency: {}", info.crystal_frequency); println!("Flash size: {}", info.flash_size); println!("Features: {}", info.features.join(", ")); - println!("MAC address: {}", info.mac_address); + + if let Some(mac) = info.mac_address { + println!("MAC address: {}", mac); + } Ok(()) } diff --git a/espflash/src/connection/mod.rs b/espflash/src/connection/mod.rs index 106c5a8c..d418360e 100644 --- a/espflash/src/connection/mod.rs +++ b/espflash/src/connection/mod.rs @@ -124,6 +124,7 @@ pub struct Connection { decoder: SlipDecoder, after_operation: ResetAfterOperation, before_operation: ResetBeforeOperation, + pub(crate) secure_download_mode: bool, } impl Connection { @@ -139,6 +140,7 @@ impl Connection { decoder: SlipDecoder::new(), after_operation, before_operation, + secure_download_mode: false, } } @@ -449,11 +451,13 @@ impl Connection { // - https://docs.espressif.com/projects/esptool/en/latest/esp32/advanced-topics/serial-protocol.html?highlight=md5#response-packet // - https://docs.espressif.com/projects/esptool/en/latest/esp32/advanced-topics/serial-protocol.html?highlight=md5#status-bytes // - https://docs.espressif.com/projects/esptool/en/latest/esp32/advanced-topics/serial-protocol.html?highlight=md5#verifying-uploaded-data - let status_len = if response.len() == 10 || response.len() == 26 { - 2 - } else { - 4 - }; + + let status_len = + if response.len() == 10 || response.len() == 26 { + 2 + } else { + 4 + }; let value = match response.len() { 10 | 12 => CommandResponseValue::ValueU32(u32::from_le_bytes( @@ -483,8 +487,8 @@ impl Connection { return_op: response[1], return_length: u16::from_le_bytes(response[2..][..2].try_into().unwrap()), value, - error: response[response.len() - status_len], - status: response[response.len() - status_len + 1], + error: response[response.len() - status_len + 1], + status: response[response.len() - status_len], }; Ok(Some(header)) @@ -524,11 +528,10 @@ impl Connection { pub fn command(&mut self, command: Command<'_>) -> Result { let ty = command.command_type(); self.write_command(command).for_command(ty)?; - for _ in 0..100 { match self.read_response().for_command(ty)? { Some(response) if response.return_op == ty as u8 => { - return if response.error != 0 { + return if response.status != 0 { let _error = self.flush(); Err(Error::RomError(RomError::new( command.command_type(), diff --git a/espflash/src/flasher/mod.rs b/espflash/src/flasher/mod.rs index 1dcee843..2d09ebd2 100644 --- a/espflash/src/flasher/mod.rs +++ b/espflash/src/flasher/mod.rs @@ -631,7 +631,7 @@ pub struct DeviceInfo { /// Device features pub features: Vec, /// MAC address - pub mac_address: String, + pub mac_address: Option, } /// Connect to and flash a target device @@ -674,6 +674,8 @@ impl Flasher { connection.begin()?; connection.set_timeout(DEFAULT_TIMEOUT)?; + let sdm = detect_sdm(&mut connection)?; + let detected_chip = if before_operation != ResetBeforeOperation::NoResetNoSync { // Detect which chip we are connected to. let detected_chip = detect_chip(&mut connection, use_stub)?; @@ -709,11 +711,18 @@ impl Flasher { // Load flash stub if enabled if use_stub { - info!("Using flash stub"); - flasher.load_stub()?; + if !sdm { + info!("Using flash stub"); + flasher.load_stub()?; + } else { + warn!("Stub is not supported in Secure Download Mode, setting --no-stub"); + flasher.use_stub = !use_stub; + } } - flasher.spi_autodetect()?; + if !sdm { + flasher.spi_autodetect()?; + } // Now that we have established a connection and detected the chip and flash // size, we can set the baud rate of the connection to the configured value. @@ -982,14 +991,25 @@ impl Flasher { let chip = self.chip(); let target = chip.into_target(); - let revision = Some(target.chip_revision(self.connection())?); + // chip_revision reads from efuse, which is not possible in Secure Download Mode + let revision = if !self.connection.secure_download_mode { + Some(target.chip_revision(self.connection())?) + } else { + None + }; + let crystal_frequency = target.crystal_freq(self.connection())?; let features = target .chip_features(self.connection())? .iter() .map(|s| s.to_string()) .collect::>(); - let mac_address = target.mac_address(self.connection())?; + + let mac_address = if !self.connection.secure_download_mode { + Some(target.mac_address(self.connection())?) + } else { + None + }; let info = DeviceInfo { chip, @@ -1099,10 +1119,17 @@ impl Flasher { let mut target = self .chip .flash_target(self.spi_params, self.use_stub, false, false); + target.begin(&mut self.connection).flashing()?; + for segment in segments { - target.write_segment(&mut self.connection, segment.borrow(), &mut progress)?; + if self.connection.secure_download_mode { + target.write_segment_sdm(&mut self.connection, segment.borrow(), &mut progress)?; + } else { + target.write_segment(&mut self.connection, segment.borrow(), &mut progress)?; + } } + target.finish(&mut self.connection, true).flashing()?; Ok(()) @@ -1382,3 +1409,14 @@ fn detect_chip(connection: &mut Connection, use_stub: bool) -> Result Result { + match connection.read_reg(CHIP_DETECT_MAGIC_REG_ADDR) { + Ok(_) => return Ok(false), + Err(_) => { + log::warn!("Secure Download Mode is enabled on this chip"); + connection.secure_download_mode = true; + return Ok(true); + } + }; +} diff --git a/espflash/src/targets/flash_target/esp32.rs b/espflash/src/targets/flash_target/esp32.rs index 9a24e054..31698757 100644 --- a/espflash/src/targets/flash_target/esp32.rs +++ b/espflash/src/targets/flash_target/esp32.rs @@ -66,7 +66,7 @@ impl FlashTarget for Esp32Target { spi_params: self.spi_attach_params, } }; - + connection.command(command) })?; @@ -135,6 +135,83 @@ impl FlashTarget for Esp32Target { Ok(()) } + fn write_segment_sdm( + &mut self, + connection: &mut Connection, + segment: Segment<'_>, + progress: &mut Option<&mut dyn ProgressCallbacks>, + ) -> Result<(), Error> { + let addr = segment.addr; + + let target = self.chip.into_target(); + let flash_write_size = target.flash_write_size(connection)?; + let block_count = segment.data.len().div_ceil(flash_write_size); + + connection.with_timeout( + CommandType::FlashBegin.timeout_for_size(segment.data.len() as u32), + |connection| { + connection.command(Command::FlashBegin { + size: segment.data.len() as u32, + blocks: block_count as u32, + block_size: flash_write_size as u32, + offset: addr, + supports_encryption: false, + })?; + Ok(()) + }, + )?; + + if let Some(cb) = progress.as_mut() { + cb.init(addr, block_count) + } + + let mut remaining_data = &segment.data[..]; + + for i in 0.. { + if remaining_data.is_empty() { + break; + } + + let block_size = std::cmp::min(flash_write_size, remaining_data.len()); + let block = &remaining_data[..block_size]; + + let padding_needed = flash_write_size.saturating_sub(block.len()); + + let block_vec = if padding_needed > 0 { + let mut owned = block.to_vec(); + owned.extend(std::iter::repeat(0xFF).take(padding_needed)); + owned + } else { + block.to_vec() + }; + + connection.with_timeout( + CommandType::FlashData.timeout_for_size(remaining_data.len() as u32), + |connection| { + connection.command(Command::FlashData { + data: &block_vec, + pad_to: 0, + pad_byte: 0, + sequence: i, + })?; + Ok(()) + }, + )?; + + remaining_data = &remaining_data[block_size..]; + + if let Some(cb) = progress.as_mut() { + cb.update(i as usize + 1) + } + } + + if let Some(cb) = progress.as_mut() { + cb.finish() + } + + Ok(()) + } + fn write_segment( &mut self, connection: &mut Connection, diff --git a/espflash/src/targets/flash_target/mod.rs b/espflash/src/targets/flash_target/mod.rs index 6d036c2d..c72b1099 100644 --- a/espflash/src/targets/flash_target/mod.rs +++ b/espflash/src/targets/flash_target/mod.rs @@ -28,6 +28,14 @@ pub trait FlashTarget { progress: &mut Option<&mut dyn ProgressCallbacks>, ) -> Result<(), Error>; + /// Write a segment to the target device in SDM mode + fn write_segment_sdm( + &mut self, + connection: &mut Connection, + segment: Segment<'_>, + progress: &mut Option<&mut dyn ProgressCallbacks>, + ) -> Result<(), Error>; + /// Complete the flashing operation fn finish(&mut self, connection: &mut Connection, reboot: bool) -> Result<(), Error>; } diff --git a/espflash/src/targets/flash_target/ram.rs b/espflash/src/targets/flash_target/ram.rs index ffee6441..c40cadd6 100644 --- a/espflash/src/targets/flash_target/ram.rs +++ b/espflash/src/targets/flash_target/ram.rs @@ -36,6 +36,15 @@ impl FlashTarget for RamTarget { Ok(()) } + fn write_segment_sdm( + &mut self, + _connection: &mut Connection, + _segment: Segment<'_>, + _progress: &mut Option<&mut dyn ProgressCallbacks>, + ) -> Result<(), Error> { + todo!() + } + fn write_segment( &mut self, connection: &mut Connection,