Skip to content

Commit 067f040

Browse files
author
User
committed
Support flashing in Secure Download Mode
* With secure download enabled, the stub is not utilized so compressed flashing is unavailble. In this situation, use the uncompressed flashing commands. * If secure download is enabled, prevent flashing data to addresses below 0x8000 to prevent overwriting the bootloader and bricking the device
1 parent 99d6d69 commit 067f040

File tree

4 files changed

+140
-55
lines changed

4 files changed

+140
-55
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1010
### Added
1111

1212
- eFuse write support (#962)
13+
- Support flashing in secure download mode (#990)
1314

1415
### Changed
1516

espflash/src/error.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,10 @@ pub enum Error {
383383
/// Tried to use an unsupported eFuse coding scheme
384384
#[error("Tried to use an unsupported eFuse coding scheme: {0}")]
385385
UnsupportedEfuseCodingScheme(String),
386+
387+
/// Tried to write to address < 0x8000 with Secure Download enabled
388+
#[error("Tried to write to address < 0x8000 with Secure Download enabled")]
389+
SecureDownloadBootloaderProtection,
386390
}
387391

388392
#[cfg(feature = "serialport")]

espflash/src/flasher/mod.rs

Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ pub(crate) const TRY_SPI_PARAMS: [SpiAttachParams; 2] =
5757
pub(crate) const FLASH_SECTOR_SIZE: usize = 0x1000;
5858
pub(crate) const FLASH_WRITE_SIZE: usize = 0x400;
5959

60+
#[cfg(feature = "serialport")]
61+
pub(crate) const BOOTLOADER_PROTECTION_ADDR: u32 = 0x8000;
62+
6063
/// Supported flash frequencies
6164
///
6265
/// Note that not all frequencies are supported by each target device.
@@ -924,9 +927,26 @@ impl Flasher {
924927
progress: &mut dyn ProgressCallbacks,
925928
image_format: ImageFormat<'a>,
926929
) -> Result<(), Error> {
927-
let mut target =
928-
self.chip
929-
.flash_target(self.spi_params, self.use_stub, self.verify, self.skip);
930+
let (mut verify, mut skip) = (self.verify, self.skip);
931+
if self.connection.secure_download_mode {
932+
for segment in image_format.clone().flash_segments() {
933+
if segment.addr < BOOTLOADER_PROTECTION_ADDR {
934+
return Err(Error::SecureDownloadBootloaderProtection);
935+
}
936+
}
937+
938+
if self.verify || self.skip {
939+
warn!(
940+
"Secure Download Mode enabled: --verify and --skip options are not available \
941+
(flash read operations are restricted)"
942+
);
943+
}
944+
(verify, skip) = (false, false);
945+
}
946+
947+
let mut target = self
948+
.chip
949+
.flash_target(self.spi_params, self.use_stub, verify, skip);
930950
target.begin(&mut self.connection).flashing()?;
931951

932952
// When the `cli` feature is enabled, display the image size information.
@@ -987,16 +1007,26 @@ impl Flasher {
9871007
segments: &[Segment<'_>],
9881008
progress: &mut dyn ProgressCallbacks,
9891009
) -> Result<(), Error> {
1010+
let (mut verify, mut skip) = (self.verify, self.skip);
9901011
if self.connection.secure_download_mode {
991-
return Err(Error::UnsupportedFeature {
992-
chip: self.chip,
993-
feature: "writing binaries in Secure Download Mode currently".into(),
994-
});
1012+
for segment in segments {
1013+
if segment.addr < BOOTLOADER_PROTECTION_ADDR {
1014+
return Err(Error::SecureDownloadBootloaderProtection);
1015+
}
1016+
}
1017+
1018+
if self.verify || self.skip {
1019+
warn!(
1020+
"Secure Download Mode enabled: --verify and --skip options are not available \
1021+
(flash read operations are restricted)"
1022+
);
1023+
}
1024+
(verify, skip) = (false, false);
9951025
}
9961026

997-
let mut target =
998-
self.chip
999-
.flash_target(self.spi_params, self.use_stub, self.verify, self.skip);
1027+
let mut target = self
1028+
.chip
1029+
.flash_target(self.spi_params, self.use_stub, verify, skip);
10001030

10011031
target.begin(&mut self.connection).flashing()?;
10021032

espflash/src/target/flash_target/esp32.rs

Lines changed: 95 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
//! This module defines the traits and types used for flashing operations on a
44
//! target device's flash memory.
55
6-
use std::io::Write;
6+
use std::{io::Write, iter::repeat_n};
77

88
use flate2::{
99
Compression,
@@ -34,7 +34,7 @@ pub struct Esp32Target {
3434
use_stub: bool,
3535
verify: bool,
3636
skip: bool,
37-
need_deflate_end: bool,
37+
need_flash_end: bool,
3838
}
3939

4040
impl Esp32Target {
@@ -52,7 +52,7 @@ impl Esp32Target {
5252
use_stub,
5353
verify,
5454
skip,
55-
need_deflate_end: false,
55+
need_flash_end: false,
5656
}
5757
}
5858
}
@@ -79,7 +79,10 @@ impl FlashTarget for Esp32Target {
7979
//
8080
// TODO: the stub doesn't appear to disable the watchdog on ESP32-S3, so we
8181
// explicitly disable the watchdog here.
82-
if connection.is_using_usb_serial_jtag() {
82+
//
83+
// NOTE: In Secure Download Mode, WRITE_REG commands are not allowed, so we
84+
// must skip the watchdog disable.
85+
if connection.is_using_usb_serial_jtag() && !connection.secure_download_mode {
8386
if let (Some(wdt_wprotect), Some(wdt_config0)) =
8487
(self.chip.wdt_wprotect(), self.chip.wdt_config0())
8588
{
@@ -116,22 +119,15 @@ impl FlashTarget for Esp32Target {
116119
md5_hasher.update(&segment.data);
117120
let checksum_md5 = md5_hasher.finalize();
118121

119-
let mut encoder = ZlibEncoder::new(Vec::new(), Compression::best());
120-
encoder.write_all(&segment.data)?;
121-
let compressed = encoder.finish()?;
122+
// use compression only when stub is loaded.
123+
let use_compression = self.use_stub;
122124

123125
let flash_write_size = self.chip.flash_write_size();
124-
let block_count = compressed.len().div_ceil(flash_write_size);
125126
let erase_count = segment.data.len().div_ceil(FLASH_SECTOR_SIZE);
126127

127128
// round up to sector size
128129
let erase_size = (erase_count * FLASH_SECTOR_SIZE) as u32;
129130

130-
let chunks = compressed.chunks(flash_write_size);
131-
let num_chunks = chunks.len();
132-
133-
progress.init(addr, num_chunks);
134-
135131
if self.skip {
136132
let flash_checksum_md5: u128 = connection.with_timeout(
137133
CommandType::FlashMd5.timeout_for_size(segment.data.len() as u32),
@@ -153,43 +149,91 @@ impl FlashTarget for Esp32Target {
153149
}
154150
}
155151

156-
connection.with_timeout(
157-
CommandType::FlashDeflBegin.timeout_for_size(erase_size),
158-
|connection| {
159-
connection.command(Command::FlashDeflBegin {
160-
size: segment.data.len() as u32,
161-
blocks: block_count as u32,
162-
block_size: flash_write_size as u32,
163-
offset: addr,
164-
supports_encryption: self.chip != Chip::Esp32 && !self.use_stub,
165-
})?;
166-
Ok(())
167-
},
168-
)?;
169-
self.need_deflate_end = true;
152+
let data = if use_compression {
153+
let mut encoder = ZlibEncoder::new(Vec::new(), Compression::best());
154+
encoder.write_all(&segment.data)?;
155+
encoder.finish()?
156+
} else {
157+
let mut data = segment.data.to_vec();
158+
let padding = (flash_write_size - (data.len() % flash_write_size)) % flash_write_size;
159+
data.extend(repeat_n(0xff, padding));
160+
data
161+
};
170162

171-
// decode the chunks to see how much data the device will have to save
172-
let mut decoder = ZlibDecoder::new(Vec::new());
173-
let mut decoded_size = 0;
163+
let block_count = data.len().div_ceil(flash_write_size);
164+
let chunks = data.chunks(flash_write_size);
165+
let num_chunks = chunks.len();
174166

175-
for (i, block) in chunks.enumerate() {
176-
decoder.write_all(block)?;
177-
decoder.flush()?;
178-
let size = decoder.get_ref().len() - decoded_size;
179-
decoded_size = decoder.get_ref().len();
167+
progress.init(addr, num_chunks);
180168

169+
if use_compression {
170+
connection.with_timeout(
171+
CommandType::FlashDeflBegin.timeout_for_size(erase_size),
172+
|connection| {
173+
connection.command(Command::FlashDeflBegin {
174+
size: segment.data.len() as u32,
175+
blocks: block_count as u32,
176+
block_size: flash_write_size as u32,
177+
offset: addr,
178+
supports_encryption: self.chip != Chip::Esp32 && !self.use_stub,
179+
})?;
180+
Ok(())
181+
},
182+
)?;
183+
self.need_flash_end = true;
184+
} else {
181185
connection.with_timeout(
182-
CommandType::FlashDeflData.timeout_for_size(size as u32),
186+
CommandType::FlashBegin.timeout_for_size(erase_size),
183187
|connection| {
184-
connection.command(Command::FlashDeflData {
185-
sequence: i as u32,
186-
pad_to: 0,
187-
pad_byte: 0xff,
188-
data: block,
188+
connection.command(Command::FlashBegin {
189+
size: erase_size,
190+
blocks: block_count as u32,
191+
block_size: flash_write_size as u32,
192+
offset: addr,
193+
supports_encryption: self.chip != Chip::Esp32,
189194
})?;
190195
Ok(())
191196
},
192197
)?;
198+
}
199+
200+
// decode the chunks to see how much data the device will have to save
201+
let mut decoder = ZlibDecoder::new(Vec::new());
202+
let mut decoded_size = 0;
203+
204+
for (i, block) in chunks.enumerate() {
205+
if use_compression {
206+
decoder.write_all(block)?;
207+
decoder.flush()?;
208+
let size = decoder.get_ref().len() - decoded_size;
209+
decoded_size = decoder.get_ref().len();
210+
211+
connection.with_timeout(
212+
CommandType::FlashDeflData.timeout_for_size(size as u32),
213+
|connection| {
214+
connection.command(Command::FlashDeflData {
215+
sequence: i as u32,
216+
pad_to: 0,
217+
pad_byte: 0xff,
218+
data: block,
219+
})?;
220+
Ok(())
221+
},
222+
)?;
223+
} else {
224+
connection.with_timeout(
225+
CommandType::FlashData.timeout_for_size(block.len() as u32),
226+
|connection| {
227+
connection.command(Command::FlashData {
228+
sequence: i as u32,
229+
pad_to: flash_write_size,
230+
pad_byte: 0xff,
231+
data: block,
232+
})?;
233+
Ok(())
234+
},
235+
)?;
236+
}
193237

194238
progress.update(i + 1)
195239
}
@@ -220,10 +264,16 @@ impl FlashTarget for Esp32Target {
220264
}
221265

222266
fn finish(&mut self, connection: &mut Connection, reboot: bool) -> Result<(), Error> {
223-
if self.need_deflate_end {
224-
connection.with_timeout(CommandType::FlashDeflEnd.timeout(), |connection| {
225-
connection.command(Command::FlashDeflEnd { reboot: false })
226-
})?;
267+
if self.need_flash_end {
268+
if self.use_stub {
269+
connection.with_timeout(CommandType::FlashDeflEnd.timeout(), |connection| {
270+
connection.command(Command::FlashDeflEnd { reboot: false })
271+
})?;
272+
} else {
273+
connection.with_timeout(CommandType::FlashEnd.timeout(), |connection| {
274+
connection.command(Command::FlashEnd { reboot: false })
275+
})?;
276+
}
227277
}
228278

229279
if reboot {

0 commit comments

Comments
 (0)