Skip to content

Commit 5398109

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 5398109

File tree

4 files changed

+137
-55
lines changed

4 files changed

+137
-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+
SecureDownloadBootloaderProection,
386390
}
387391

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

espflash/src/flasher/mod.rs

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -924,9 +924,26 @@ impl Flasher {
924924
progress: &mut dyn ProgressCallbacks,
925925
image_format: ImageFormat<'a>,
926926
) -> Result<(), Error> {
927-
let mut target =
928-
self.chip
929-
.flash_target(self.spi_params, self.use_stub, self.verify, self.skip);
927+
let (mut verify, mut skip) = (self.verify, self.skip);
928+
if self.connection.secure_download_mode {
929+
for segment in image_format.clone().flash_segments() {
930+
if segment.addr < 0x8000 {
931+
return Err(Error::SecureDownloadBootloaderProection);
932+
}
933+
}
934+
935+
if self.verify || self.skip {
936+
warn!(
937+
"Secure Download Mode enabled: --verify and --skip options are not available \
938+
(flash read operations are restricted)"
939+
);
940+
}
941+
(verify, skip) = (false, false);
942+
}
943+
944+
let mut target = self
945+
.chip
946+
.flash_target(self.spi_params, self.use_stub, verify, skip);
930947
target.begin(&mut self.connection).flashing()?;
931948

932949
// When the `cli` feature is enabled, display the image size information.
@@ -987,16 +1004,26 @@ impl Flasher {
9871004
segments: &[Segment<'_>],
9881005
progress: &mut dyn ProgressCallbacks,
9891006
) -> Result<(), Error> {
1007+
let (mut verify, mut skip) = (self.verify, self.skip);
9901008
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-
});
1009+
for segment in segments {
1010+
if segment.addr < 0x8000 {
1011+
return Err(Error::SecureDownloadBootloaderProection);
1012+
}
1013+
}
1014+
1015+
if self.verify || self.skip {
1016+
warn!(
1017+
"Secure Download Mode enabled: --verify and --skip options are not available \
1018+
(flash read operations are restricted)"
1019+
);
1020+
}
1021+
(verify, skip) = (false, false);
9951022
}
9961023

997-
let mut target =
998-
self.chip
999-
.flash_target(self.spi_params, self.use_stub, self.verify, self.skip);
1024+
let mut target = self
1025+
.chip
1026+
.flash_target(self.spi_params, self.use_stub, verify, skip);
10001027

10011028
target.begin(&mut self.connection).flashing()?;
10021029

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)