Skip to content

Commit 9b2c5ef

Browse files
authored
Merge pull request #72 from esp-rs/direct-boot
add support for flashing direct-boot images to esp32c3
2 parents e90e6db + 417e436 commit 9b2c5ef

File tree

15 files changed

+278
-77
lines changed

15 files changed

+278
-77
lines changed

cargo-espflash/src/main.rs

+29-11
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use std::{
88
use cargo_metadata::Message;
99
use clap::{App, Arg, ArgMatches, SubCommand};
1010
use error::Error;
11-
use espflash::{Chip, Config, FirmwareImage, Flasher, PartitionTable};
11+
use espflash::{Chip, Config, FirmwareImage, Flasher, ImageFormatId, PartitionTable};
1212
use miette::{IntoDiagnostic, Result, WrapErr};
1313
use monitor::monitor;
1414
use package_metadata::CargoEspFlashMeta;
@@ -17,6 +17,7 @@ use serial::{BaudRate, FlowControl, SerialPort};
1717
use crate::cargo_config::CargoConfig;
1818
use crate::error::NoTargetError;
1919
use crate::{cargo_config::parse_cargo_config, error::UnsupportedTargetError};
20+
use std::str::FromStr;
2021

2122
mod cargo_config;
2223
mod error;
@@ -42,6 +43,11 @@ fn main() -> Result<()> {
4243
.takes_value(true)
4344
.value_name("FEATURES")
4445
.help("Comma delimited list of build features"),
46+
Arg::with_name("format")
47+
.long("format")
48+
.takes_value(true)
49+
.value_name("image format")
50+
.help("Image format to flash"),
4551
];
4652
let connect_args = [Arg::with_name("serial")
4753
.takes_value(true)
@@ -228,12 +234,18 @@ fn flash(
228234
None
229235
};
230236

237+
let image_format = matches
238+
.value_of("format")
239+
.map(ImageFormatId::from_str)
240+
.transpose()?
241+
.or(metadata.format);
242+
231243
// Read the ELF data from the build path and load it to the target.
232244
let elf_data = fs::read(path).into_diagnostic()?;
233245
if matches.is_present("ram") {
234246
flasher.load_elf_to_ram(&elf_data)?;
235247
} else {
236-
flasher.load_elf_to_flash(&elf_data, bootloader, partition_table)?;
248+
flasher.load_elf_to_flash(&elf_data, bootloader, partition_table, image_format)?;
237249
}
238250
println!("\nFlashing has completed!");
239251

@@ -266,13 +278,6 @@ fn build(
266278
cargo_config: &CargoConfig,
267279
chip: Option<Chip>,
268280
) -> Result<PathBuf> {
269-
// The 'build-std' unstable cargo feature is required to enable
270-
// cross-compilation. If it has not been set then we cannot build the
271-
// application.
272-
if !cargo_config.has_build_std() {
273-
return Err(Error::NoBuildStd.into());
274-
};
275-
276281
let target = cargo_config
277282
.target()
278283
.ok_or_else(|| NoTargetError::new(chip))?;
@@ -281,6 +286,13 @@ fn build(
281286
return Err(Error::UnsupportedTarget(UnsupportedTargetError::new(target, chip)).into());
282287
}
283288
}
289+
// The 'build-std' unstable cargo feature is required to enable
290+
// cross-compilation for xtensa targets.
291+
// If it has not been set then we cannot build the
292+
// application.
293+
if !cargo_config.has_build_std() && target.starts_with("xtensa-") {
294+
return Err(Error::NoBuildStd.into());
295+
};
284296

285297
// Build the list of arguments to pass to 'cargo build'.
286298
let mut args = vec![];
@@ -356,7 +368,7 @@ fn build(
356368
fn save_image(
357369
matches: &ArgMatches,
358370
_config: Config,
359-
_metadata: CargoEspFlashMeta,
371+
metadata: CargoEspFlashMeta,
360372
cargo_config: CargoConfig,
361373
) -> Result<()> {
362374
let target = cargo_config
@@ -370,7 +382,13 @@ fn save_image(
370382

371383
let image = FirmwareImage::from_data(&elf_data)?;
372384

373-
let flash_image = chip.get_flash_image(&image, None, None, None)?;
385+
let image_format = matches
386+
.value_of("format")
387+
.map(ImageFormatId::from_str)
388+
.transpose()?
389+
.or(metadata.format);
390+
391+
let flash_image = chip.get_flash_image(&image, None, None, image_format, None)?;
374392
let parts: Vec<_> = flash_image.ota_segments().collect();
375393

376394
let out_path = matches.value_of("file").unwrap();

cargo-espflash/src/package_metadata.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::error::{Error, TomlError};
22
use cargo_toml::Manifest;
3+
use espflash::ImageFormatId;
34
use miette::{IntoDiagnostic, Result, WrapErr};
45
use serde::Deserialize;
56
use std::fs::read_to_string;
@@ -9,6 +10,7 @@ use std::path::Path;
910
pub struct CargoEspFlashMeta {
1011
pub partition_table: Option<String>,
1112
pub bootloader: Option<String>,
13+
pub format: Option<ImageFormatId>,
1214
}
1315

1416
#[derive(Clone, Debug, Default, Deserialize)]

espflash/src/chip/esp32/esp32.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use std::ops::Range;
22

33
use super::Esp32Params;
4+
use crate::error::UnsupportedImageFormatError;
45
use crate::{
56
chip::{bytes_to_mac_addr, Chip, ChipType, ReadEFuse, SpiRegisters},
67
connection::Connection,
@@ -117,6 +118,7 @@ impl ChipType for Esp32 {
117118
bootloader: Option<Vec<u8>>,
118119
partition_table: Option<PartitionTable>,
119120
image_format: ImageFormatId,
121+
_chip_revision: Option<u32>,
120122
) -> Result<Box<dyn ImageFormat<'a> + 'a>, Error> {
121123
match image_format {
122124
ImageFormatId::Bootloader => Ok(Box::new(Esp32BootloaderFormat::new(
@@ -126,9 +128,7 @@ impl ChipType for Esp32 {
126128
partition_table,
127129
bootloader,
128130
)?)),
129-
ImageFormatId::DirectBoot => {
130-
todo!()
131-
}
131+
_ => Err(UnsupportedImageFormatError::new(image_format, Chip::Esp32, None).into()),
132132
}
133133
}
134134

espflash/src/chip/esp32/esp32c3.rs

+10-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
use std::ops::Range;
22

33
use super::Esp32Params;
4+
use crate::error::UnsupportedImageFormatError;
5+
use crate::image_format::Esp32DirectBootFormat;
46
use crate::{
57
chip::{bytes_to_mac_addr, ChipType, ReadEFuse, SpiRegisters},
68
connection::Connection,
@@ -70,18 +72,22 @@ impl ChipType for Esp32c3 {
7072
bootloader: Option<Vec<u8>>,
7173
partition_table: Option<PartitionTable>,
7274
image_format: ImageFormatId,
75+
chip_revision: Option<u32>,
7376
) -> Result<Box<dyn ImageFormat<'a> + 'a>, Error> {
74-
match image_format {
75-
ImageFormatId::Bootloader => Ok(Box::new(Esp32BootloaderFormat::new(
77+
match (image_format, chip_revision) {
78+
(ImageFormatId::Bootloader, _) => Ok(Box::new(Esp32BootloaderFormat::new(
7679
image,
7780
Chip::Esp32c3,
7881
PARAMS,
7982
partition_table,
8083
bootloader,
8184
)?)),
82-
ImageFormatId::DirectBoot => {
83-
todo!()
85+
(ImageFormatId::DirectBoot, None | Some(3..)) => {
86+
Ok(Box::new(Esp32DirectBootFormat::new(image)?))
8487
}
88+
_ => Err(
89+
UnsupportedImageFormatError::new(image_format, Chip::Esp32c3, chip_revision).into(),
90+
),
8591
}
8692
}
8793

espflash/src/chip/esp32/esp32s2.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use std::ops::Range;
22

33
use super::Esp32Params;
4+
use crate::error::UnsupportedImageFormatError;
45
use crate::{
56
chip::{bytes_to_mac_addr, ChipType, ReadEFuse, SpiRegisters},
67
connection::Connection,
@@ -94,6 +95,7 @@ impl ChipType for Esp32s2 {
9495
bootloader: Option<Vec<u8>>,
9596
partition_table: Option<PartitionTable>,
9697
image_format: ImageFormatId,
98+
_chip_revision: Option<u32>,
9799
) -> Result<Box<dyn ImageFormat<'a> + 'a>, Error> {
98100
match image_format {
99101
ImageFormatId::Bootloader => Ok(Box::new(Esp32BootloaderFormat::new(
@@ -103,9 +105,7 @@ impl ChipType for Esp32s2 {
103105
partition_table,
104106
bootloader,
105107
)?)),
106-
ImageFormatId::DirectBoot => {
107-
todo!()
108-
}
108+
_ => Err(UnsupportedImageFormatError::new(image_format, Chip::Esp32s2, None).into()),
109109
}
110110
}
111111

espflash/src/chip/esp8266.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,11 @@ impl ChipType for Esp8266 {
4747
_bootloader: Option<Vec<u8>>,
4848
_partition_table: Option<PartitionTable>,
4949
image_format: ImageFormatId,
50+
_chip_revision: Option<u32>,
5051
) -> Result<Box<dyn ImageFormat<'a> + 'a>, Error> {
5152
match image_format {
5253
ImageFormatId::Bootloader => Ok(Box::new(Esp8266Format::new(image)?)),
53-
_ => Err(UnsupportedImageFormatError::new(image_format, Chip::Esp8266).into()),
54+
_ => Err(UnsupportedImageFormatError::new(image_format, Chip::Esp8266, None).into()),
5455
}
5556
}
5657

espflash/src/chip/mod.rs

+25-9
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ pub trait ChipType {
5353
bootloader: Option<Vec<u8>>,
5454
partition_table: Option<PartitionTable>,
5555
image_format: ImageFormatId,
56+
chip_revision: Option<u32>,
5657
) -> Result<Box<dyn ImageFormat<'a> + 'a>, Error>;
5758

5859
/// Read the MAC address of the connected chip.
@@ -158,20 +159,35 @@ impl Chip {
158159
bootloader: Option<Vec<u8>>,
159160
partition_table: Option<PartitionTable>,
160161
image_format: Option<ImageFormatId>,
162+
chip_revision: Option<u32>,
161163
) -> Result<Box<dyn ImageFormat<'a> + 'a>, Error> {
162164
let image_format = image_format.unwrap_or_else(|| self.default_image_format());
163165

164166
match self {
165-
Chip::Esp32 => {
166-
Esp32::get_flash_segments(image, bootloader, partition_table, image_format)
167+
Chip::Esp32 => Esp32::get_flash_segments(
168+
image,
169+
bootloader,
170+
partition_table,
171+
image_format,
172+
chip_revision,
173+
),
174+
Chip::Esp32c3 => Esp32c3::get_flash_segments(
175+
image,
176+
bootloader,
177+
partition_table,
178+
image_format,
179+
chip_revision,
180+
),
181+
Chip::Esp32s2 => Esp32s2::get_flash_segments(
182+
image,
183+
bootloader,
184+
partition_table,
185+
image_format,
186+
chip_revision,
187+
),
188+
Chip::Esp8266 => {
189+
Esp8266::get_flash_segments(image, None, None, image_format, chip_revision)
167190
}
168-
Chip::Esp32c3 => {
169-
Esp32c3::get_flash_segments(image, bootloader, partition_table, image_format)
170-
}
171-
Chip::Esp32s2 => {
172-
Esp32s2::get_flash_segments(image, bootloader, partition_table, image_format)
173-
}
174-
Chip::Esp8266 => Esp8266::get_flash_segments(image, None, None, image_format),
175191
}
176192
}
177193

espflash/src/elf.rs

+39-24
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use crate::error::{ElfError, Error};
66
use crate::flasher::FlashSize;
77
use std::fmt::{Debug, Formatter};
88
use std::mem::take;
9+
use std::ops::AddAssign;
910
use xmas_elf::sections::{SectionData, ShType};
1011
use xmas_elf::ElfFile;
1112

@@ -88,7 +89,7 @@ impl<'a> FirmwareImage<'a> {
8889
}
8990
}
9091

91-
#[derive(Eq, Clone)]
92+
#[derive(Eq, Clone, Default)]
9293
/// A segment of code from the source elf
9394
pub struct CodeSegment<'a> {
9495
pub addr: u32,
@@ -97,21 +98,12 @@ pub struct CodeSegment<'a> {
9798

9899
impl<'a> CodeSegment<'a> {
99100
pub fn new(addr: u32, data: &'a [u8]) -> Self {
100-
// pad to 4 byte
101-
let padding = (4 - data.len() % 4) % 4;
102-
if padding == 0 {
103-
CodeSegment {
104-
addr,
105-
data: Cow::Borrowed(data),
106-
}
107-
} else {
108-
let mut data = data.to_vec();
109-
data.extend_from_slice(&[0; 4][0..padding]);
110-
CodeSegment {
111-
addr,
112-
data: Cow::Owned(data),
113-
}
114-
}
101+
let mut segment = CodeSegment {
102+
addr,
103+
data: Cow::Borrowed(data),
104+
};
105+
segment.pad_align(4);
106+
segment
115107
}
116108

117109
/// Split of the first `count` bytes into a new segment, adjusting the remaining segment as needed
@@ -142,19 +134,41 @@ impl<'a> CodeSegment<'a> {
142134
}
143135
}
144136

145-
pub fn add(&mut self, extend: &[u8]) {
146-
let mut data = take(&mut self.data).into_owned();
147-
data.extend_from_slice(extend);
148-
self.data = Cow::Owned(data);
149-
}
150-
151137
pub fn size(&self) -> u32 {
152138
self.data.len() as u32
153139
}
154140

155141
pub fn data(&self) -> &[u8] {
156142
self.data.as_ref()
157143
}
144+
145+
pub fn pad_align(&mut self, align: usize) {
146+
let padding = (align - self.data.len() % align) % align;
147+
if padding > 0 {
148+
let mut data = take(&mut self.data).into_owned();
149+
data.extend_from_slice(&[0; 4][0..padding]);
150+
self.data = Cow::Owned(data);
151+
}
152+
}
153+
}
154+
155+
impl<'a> AddAssign<&'_ [u8]> for CodeSegment<'a> {
156+
fn add_assign(&mut self, rhs: &'_ [u8]) {
157+
let mut data = take(&mut self.data).into_owned();
158+
data.extend_from_slice(rhs);
159+
self.data = Cow::Owned(data);
160+
}
161+
}
162+
163+
impl<'a> AddAssign<&'_ CodeSegment<'_>> for CodeSegment<'a> {
164+
fn add_assign(&mut self, rhs: &'_ CodeSegment<'_>) {
165+
let mut data = take(&mut self.data).into_owned();
166+
// pad or truncate
167+
#[allow(clippy::suspicious_op_assign_impl)]
168+
data.resize((rhs.addr - self.addr) as usize, 0);
169+
data.extend_from_slice(rhs.data());
170+
self.data = Cow::Owned(data);
171+
}
158172
}
159173

160174
impl Debug for CodeSegment<'_> {
@@ -184,6 +198,7 @@ impl Ord for CodeSegment<'_> {
184198
}
185199
}
186200

201+
#[derive(Clone)]
187202
/// A segment of data to write to the flash
188203
pub struct RomSegment<'a> {
189204
pub addr: u32,
@@ -219,14 +234,14 @@ pub fn update_checksum(data: &[u8], mut checksum: u8) -> u8 {
219234
checksum
220235
}
221236

222-
pub fn merge_segments(mut segments: Vec<CodeSegment>) -> Vec<CodeSegment> {
237+
pub fn merge_adjacent_segments(mut segments: Vec<CodeSegment>) -> Vec<CodeSegment> {
223238
segments.sort();
224239

225240
let mut merged: Vec<CodeSegment> = Vec::with_capacity(segments.len());
226241
for segment in segments {
227242
match merged.last_mut() {
228243
Some(last) if last.addr + last.size() == segment.addr => {
229-
last.add(segment.data());
244+
*last += segment.data();
230245
}
231246
_ => {
232247
merged.push(segment);

0 commit comments

Comments
 (0)