Skip to content

Commit 8d9973e

Browse files
authored
Image header improvements and bugfixes (#375)
* Image header improvements and bugfixes * Review fixes
1 parent 21f1fa1 commit 8d9973e

File tree

7 files changed

+218
-133
lines changed

7 files changed

+218
-133
lines changed

espflash/src/flasher/mod.rs

Lines changed: 87 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,21 @@ pub enum FlashFrequency {
6868
_80Mhz,
6969
}
7070

71+
impl FlashFrequency {
72+
/// Encodes flash frequency into the format used by the bootloader.
73+
pub fn encode_flash_frequency(self: FlashFrequency, chip: Chip) -> Result<u8, Error> {
74+
let encodings = chip.into_target().flash_frequency_encodings();
75+
if let Some(&f) = encodings.get(&self) {
76+
Ok(f)
77+
} else {
78+
Err(Error::UnsupportedFlashFrequency {
79+
chip,
80+
frequency: self,
81+
})
82+
}
83+
}
84+
}
85+
7186
/// Supported flash modes
7287
#[cfg_attr(feature = "cli", derive(clap::ValueEnum))]
7388
#[derive(Copy, Clone, Debug, Default, EnumVariantNames)]
@@ -93,50 +108,95 @@ pub enum FlashMode {
93108
#[non_exhaustive]
94109
#[repr(u8)]
95110
#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
111+
#[doc(alias("esp_image_flash_size_t"))]
96112
pub enum FlashSize {
97113
/// 256 KB
98-
_256Kb = 0x12,
114+
_256Kb,
99115
/// 512 KB
100-
_512Kb = 0x13,
116+
_512Kb,
101117
/// 1 MB
102-
_1Mb = 0x14,
118+
_1Mb,
103119
/// 2 MB
104-
_2Mb = 0x15,
120+
_2Mb,
105121
/// 4 MB
106122
#[default]
107-
_4Mb = 0x16,
123+
_4Mb,
108124
/// 8 MB
109-
_8Mb = 0x17,
125+
_8Mb,
110126
/// 16 MB
111-
_16Mb = 0x18,
127+
_16Mb,
112128
/// 32 MB
113-
_32Mb = 0x19,
129+
_32Mb,
114130
/// 64 MB
115-
_64Mb = 0x1a,
131+
_64Mb,
116132
/// 128 MB
117-
_128Mb = 0x21,
133+
_128Mb,
134+
/// 256 MB
135+
_256Mb,
118136
}
119137

120138
impl FlashSize {
121-
/// Create a `FlashSize` from an `u8`
122-
fn from(value: u8) -> Result<FlashSize, Error> {
139+
/// Encodes flash size into the format used by the bootloader.
140+
///
141+
/// ## Values:
142+
/// * [Esp8266](https://docs.espressif.com/projects/esptool/en/latest/esp8266/advanced-topics/firmware-image-format.html#file-header)
143+
/// * [Others](https://docs.espressif.com/projects/esptool/en/latest/esp32s3/advanced-topics/firmware-image-format.html#file-header)
144+
pub const fn encode_flash_size(self: FlashSize, chip: Chip) -> Result<u8, Error> {
145+
use FlashSize::*;
146+
147+
let encoded = match chip {
148+
Chip::Esp8266 => match self {
149+
_256Kb => 1,
150+
_512Kb => 0,
151+
_1Mb => 2,
152+
_2Mb => 3,
153+
_4Mb => 4,
154+
// Currently not supported
155+
// _2Mb_c1 => 5,
156+
// _4Mb_c1 => 6,
157+
_8Mb => 8,
158+
_16Mb => 9,
159+
_ => return Err(Error::UnsupportedFlash(self as u8)),
160+
},
161+
_ => match self {
162+
_1Mb => 0,
163+
_2Mb => 1,
164+
_4Mb => 2,
165+
_8Mb => 3,
166+
_16Mb => 4,
167+
_32Mb => 5,
168+
_64Mb => 6,
169+
_128Mb => 7,
170+
_256Mb => 8,
171+
_ => return Err(Error::UnsupportedFlash(self as u8)),
172+
},
173+
};
174+
175+
Ok(encoded)
176+
}
177+
178+
/// Create a [FlashSize] from an [u8]
179+
///
180+
/// [source](https://github.com/espressif/esptool/blob/f4d2510e2c897621884f433ef3f191e8fc5ff184/esptool/cmds.py#L42)
181+
const fn from_detected(value: u8) -> Result<FlashSize, Error> {
123182
match value {
124-
0x12 => Ok(FlashSize::_256Kb),
125-
0x13 => Ok(FlashSize::_512Kb),
126-
0x14 => Ok(FlashSize::_1Mb),
127-
0x15 => Ok(FlashSize::_2Mb),
128-
0x16 => Ok(FlashSize::_4Mb),
129-
0x17 => Ok(FlashSize::_8Mb),
130-
0x18 => Ok(FlashSize::_16Mb),
131-
0x19 => Ok(FlashSize::_32Mb),
132-
0x1a => Ok(FlashSize::_64Mb),
133-
0x21 => Ok(FlashSize::_128Mb),
183+
0x12 | 0x32 => Ok(FlashSize::_256Kb),
184+
0x13 | 0x33 => Ok(FlashSize::_512Kb),
185+
0x14 | 0x34 => Ok(FlashSize::_1Mb),
186+
0x15 | 0x35 => Ok(FlashSize::_2Mb),
187+
0x16 | 0x36 => Ok(FlashSize::_4Mb),
188+
0x17 | 0x37 => Ok(FlashSize::_8Mb),
189+
0x18 | 0x38 => Ok(FlashSize::_16Mb),
190+
0x19 | 0x39 => Ok(FlashSize::_32Mb),
191+
0x20 | 0x1A | 0x3A => Ok(FlashSize::_64Mb),
192+
0x21 | 0x1B => Ok(FlashSize::_128Mb),
193+
0x22 | 0x1C => Ok(FlashSize::_256Mb),
134194
_ => Err(Error::UnsupportedFlash(value)),
135195
}
136196
}
137197

138-
/// Return the flash size in bytes
139-
pub fn size(self) -> u32 {
198+
/// Returns the flash size in bytes
199+
pub const fn size(self) -> u32 {
140200
match self {
141201
FlashSize::_256Kb => 0x0040000,
142202
FlashSize::_512Kb => 0x0080000,
@@ -148,6 +208,7 @@ impl FlashSize {
148208
FlashSize::_32Mb => 0x2000000,
149209
FlashSize::_64Mb => 0x4000000,
150210
FlashSize::_128Mb => 0x8000000,
211+
FlashSize::_256Mb => 0x10000000,
151212
}
152213
}
153214
}
@@ -450,15 +511,15 @@ impl Flasher {
450511
return Ok(None);
451512
}
452513

453-
let flash_size = match FlashSize::from(size_id) {
514+
let flash_size = match FlashSize::from_detected(size_id) {
454515
Ok(size) => size,
455516
Err(_) => {
456517
warn!(
457518
"Could not detect flash size (FlashID=0x{:02X}, SizeID=0x{:02X}), defaulting to 4MB",
458519
flash_id,
459520
size_id
460521
);
461-
FlashSize::_4Mb
522+
FlashSize::default()
462523
}
463524
};
464525

espflash/src/image_format/esp8266.rs

Lines changed: 12 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,7 @@ use crate::{
77
error::Error,
88
flasher::{FlashFrequency, FlashMode, FlashSize},
99
image_format::{
10-
encode_flash_frequency, update_checksum, EspCommonHeader, ImageFormat, SegmentHeader,
11-
ESP_CHECKSUM_MAGIC, ESP_MAGIC,
10+
update_checksum, ImageFormat, ImageHeader, SegmentHeader, ESP_CHECKSUM_MAGIC, ESP_MAGIC,
1211
},
1312
targets::Chip,
1413
};
@@ -39,22 +38,25 @@ impl<'a> Esp8266Format<'a> {
3938

4039
// Common header
4140
let flash_mode = flash_mode.unwrap_or_default() as u8;
42-
let flash_freq = flash_freq.unwrap_or_default();
43-
let flash_size = flash_size.unwrap_or_default();
44-
let flash_config =
45-
encode_flash_size(flash_size)? + encode_flash_frequency(Chip::Esp8266, flash_freq)?;
4641
let segment_count = image.ram_segments(Chip::Esp8266).count() as u8;
4742

48-
let header = EspCommonHeader {
43+
let mut header = ImageHeader {
4944
magic: ESP_MAGIC,
5045
segment_count,
5146
flash_mode,
52-
flash_config,
5347
entry: image.entry(),
48+
..Default::default()
5449
};
55-
common_data.write_all(bytes_of(&header))?;
50+
header.write_flash_config(
51+
flash_size.unwrap_or_default(),
52+
flash_freq.unwrap_or_default(),
53+
Chip::Esp8266,
54+
)?;
55+
56+
// Esp8266 does not have extended header
57+
let mut total_len = ImageHeader::COMMON_HEADER_LEN;
58+
common_data.write_all(&bytes_of(&header)[0..total_len as usize])?;
5659

57-
let mut total_len = 8;
5860
let mut checksum = ESP_CHECKSUM_MAGIC;
5961

6062
for segment in image.ram_segments(Chip::Esp8266) {
@@ -163,21 +165,6 @@ fn merge_rom_segments<'a>(
163165
})
164166
}
165167

166-
fn encode_flash_size(size: FlashSize) -> Result<u8, Error> {
167-
use FlashSize::*;
168-
169-
match size {
170-
_256Kb => Ok(0x10),
171-
_512Kb => Ok(0x00),
172-
_1Mb => Ok(0x20),
173-
_2Mb => Ok(0x30),
174-
_4Mb => Ok(0x40),
175-
_8Mb => Ok(0x80),
176-
_16Mb => Ok(0x90),
177-
_ => Err(Error::UnsupportedFlash(size as u8)),
178-
}
179-
}
180-
181168
#[cfg(test)]
182169
mod tests {
183170
use std::fs;

espflash/src/image_format/idf_bootloader.rs

Lines changed: 19 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
use std::{borrow::Cow, io::Write, iter::once};
1+
use std::{borrow::Cow, io::Write, iter::once, mem::size_of};
22

3-
use bytemuck::{bytes_of, from_bytes, Pod, Zeroable};
3+
use bytemuck::{bytes_of, from_bytes};
44
use esp_idf_part::{PartitionTable, Type};
55
use sha2::{Digest, Sha256};
66

@@ -9,29 +9,15 @@ use crate::{
99
error::Error,
1010
flasher::{FlashFrequency, FlashMode, FlashSize},
1111
image_format::{
12-
encode_flash_frequency, update_checksum, EspCommonHeader, ImageFormat, SegmentHeader,
13-
ESP_CHECKSUM_MAGIC, ESP_MAGIC, WP_PIN_DISABLED,
12+
update_checksum, ImageFormat, ImageHeader, SegmentHeader, ESP_CHECKSUM_MAGIC, ESP_MAGIC,
13+
WP_PIN_DISABLED,
1414
},
1515
targets::{Chip, Esp32Params},
1616
};
1717

1818
const IROM_ALIGN: u32 = 0x10000;
1919
const SEG_HEADER_LEN: u32 = 8;
2020

21-
/// Wrapper for the extended header used by the ESP-IDF bootloader
22-
#[derive(Debug, Default, Clone, Copy, Pod, Zeroable)]
23-
#[repr(C)]
24-
struct ExtendedHeader {
25-
wp_pin: u8,
26-
clk_q_drv: u8,
27-
d_cs_drv: u8,
28-
gd_wp_drv: u8,
29-
chip_id: u16,
30-
min_rev: u8,
31-
padding: [u8; 8],
32-
append_digest: u8,
33-
}
34-
3521
/// Image format for ESP32 family chips using the second-stage bootloader from
3622
/// ESP-IDF
3723
pub struct IdfBootloaderFormat<'a> {
@@ -62,52 +48,38 @@ impl<'a> IdfBootloaderFormat<'a> {
6248
Cow::Borrowed(params.default_bootloader)
6349
};
6450

65-
let mut data = Vec::new();
66-
6751
// fetch the generated header from the bootloader
68-
let mut header: EspCommonHeader = *from_bytes(&bootloader[0..8]);
52+
let mut header: ImageHeader = *from_bytes(&bootloader[0..size_of::<ImageHeader>()]);
6953
if header.magic != ESP_MAGIC {
7054
return Err(Error::InvalidBootloader);
7155
}
7256

7357
// update the header if a user has specified any custom arguments
7458
if let Some(mode) = flash_mode {
7559
header.flash_mode = mode as u8;
76-
bootloader.to_mut()[2] = bytes_of(&header)[2];
7760
}
7861

79-
match (flash_size, flash_freq) {
80-
(Some(s), Some(f)) => {
81-
header.flash_config = encode_flash_size(s)? + encode_flash_frequency(chip, f)?;
82-
bootloader.to_mut()[3] = bytes_of(&header)[3];
83-
}
84-
(Some(s), None) => {
85-
header.flash_config = encode_flash_size(s)? + (header.flash_config & 0x0F);
86-
bootloader.to_mut()[3] = bytes_of(&header)[3];
87-
}
88-
(None, Some(f)) => {
89-
header.flash_config =
90-
(header.flash_config & 0xF0) + encode_flash_frequency(chip, f)?;
91-
bootloader.to_mut()[3] = bytes_of(&header)[3];
92-
}
93-
(None, None) => {} // nothing to update
94-
}
62+
header.write_flash_config(
63+
flash_size.unwrap_or_default(),
64+
flash_freq.unwrap_or_default(),
65+
chip,
66+
)?;
67+
68+
bootloader.to_mut().splice(
69+
0..size_of::<ImageHeader>(),
70+
bytes_of(&header).iter().copied(),
71+
);
9572

9673
// write the header of the app
9774
// use the same settings as the bootloader
9875
// just update the entry point
9976
header.entry = image.entry();
100-
data.write_all(bytes_of(&header))?;
101-
102-
let extended_header = ExtendedHeader {
103-
wp_pin: WP_PIN_DISABLED,
104-
chip_id: params.chip_id,
105-
append_digest: 1,
10677

107-
..ExtendedHeader::default()
108-
};
78+
header.wp_pin = WP_PIN_DISABLED;
79+
header.chip_id = params.chip_id;
80+
header.append_digest = 1;
10981

110-
data.write_all(bytes_of(&extended_header))?;
82+
let mut data = bytes_of(&header).to_vec();
11183

11284
let flash_segments: Vec<_> = merge_adjacent_segments(image.rom_segments(chip).collect());
11385
let mut ram_segments: Vec<_> = merge_adjacent_segments(image.ram_segments(chip).collect());
@@ -240,23 +212,6 @@ impl<'a> ImageFormat<'a> for IdfBootloaderFormat<'a> {
240212
}
241213
}
242214

243-
/// Enconde the flash size into the format used by the bootloader.
244-
fn encode_flash_size(size: FlashSize) -> Result<u8, Error> {
245-
use FlashSize::*;
246-
247-
match size {
248-
_1Mb => Ok(0x00),
249-
_2Mb => Ok(0x10),
250-
_4Mb => Ok(0x20),
251-
_8Mb => Ok(0x30),
252-
_16Mb => Ok(0x40),
253-
_32Mb => Ok(0x19),
254-
_64Mb => Ok(0x1a),
255-
_128Mb => Ok(0x21),
256-
_ => Err(Error::UnsupportedFlash(size as u8)),
257-
}
258-
}
259-
260215
/// Actual alignment (in data bytes) required for a segment header: positioned
261216
/// so that after we write the next 8 byte header, file_offs % IROM_ALIGN ==
262217
/// segment.addr % IROM_ALIGN

0 commit comments

Comments
 (0)