Skip to content

Commit fc5643b

Browse files
committed
enable support for loader stubs (via --use-stub arg)
1 parent 312c115 commit fc5643b

20 files changed

+222
-14
lines changed

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

espflash/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ bin-dir = "{ bin }{ binary-ext }"
2929
pkg-fmt = "zip"
3030

3131
[dependencies]
32+
base64 = "0.13.0"
3233
binread = "2.2"
3334
bytemuck = { version = "1.11", features = ["derive"] }
3435
clap = { version = "3.2", features = ["derive"] }
@@ -47,6 +48,7 @@ parse_int = "0.6"
4748
regex = "1.6"
4849
serde = { version = "1.0", features = ["derive"] }
4950
serde-hex = "0.1"
51+
serde_json = "1.0.83"
5052
serde_plain = "1.0"
5153
serialport = "4.2"
5254
sha2 = "0.10"

espflash/src/cli/mod.rs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,16 +31,21 @@ pub mod monitor;
3131

3232
mod serial;
3333

34-
#[derive(Parser)]
34+
#[derive(Parser, Debug)]
3535
pub struct ConnectOpts {
3636
/// Serial port connected to target device
3737
pub serial: Option<String>,
38+
3839
/// Baud rate at which to flash target device
3940
#[clap(long)]
4041
pub speed: Option<u32>,
42+
43+
/// Use RAM stub for loading
44+
#[clap(long)]
45+
pub use_stub: bool,
4146
}
4247

43-
#[derive(Parser)]
48+
#[derive(Parser, Debug)]
4449
pub struct FlashOpts {
4550
/// Load the application to RAM instead of Flash
4651
#[clap(long)]
@@ -56,7 +61,7 @@ pub struct FlashOpts {
5661
pub monitor: bool,
5762
}
5863

59-
#[derive(Parser)]
64+
#[derive(Parser, Debug)]
6065
pub struct FlashConfigOpts {
6166
/// Flash mode to use
6267
#[clap(short = 'm', long, possible_values = FlashMode::VARIANTS, value_name = "MODE")]
@@ -69,7 +74,7 @@ pub struct FlashConfigOpts {
6974
pub flash_freq: Option<FlashFrequency>,
7075
}
7176

72-
#[derive(Parser)]
77+
#[derive(Parser, Debug)]
7378
pub struct PartitionTableOpts {
7479
/// Convert CSV parition table to binary representation
7580
#[clap(long, required_unless_present_any = ["info", "to-csv"])]
@@ -87,13 +92,15 @@ pub struct PartitionTableOpts {
8792
output: Option<PathBuf>,
8893
}
8994

90-
#[derive(Parser)]
95+
#[derive(Parser, Debug)]
9196
pub struct WriteBinToFlashOpts {
9297
/// Address at which to write the binary file
9398
#[clap(value_parser = parse_u32)]
9499
addr: u32,
100+
95101
/// File containing the binary data to write
96102
bin_file: String,
103+
97104
#[clap(flatten)]
98105
connect_opts: ConnectOpts,
99106
}
@@ -131,7 +138,7 @@ pub fn connect(opts: &ConnectOpts, config: &Config) -> Result<Flasher> {
131138
_ => unreachable!(),
132139
};
133140

134-
Ok(Flasher::connect(serial, port_info, opts.speed)?)
141+
Ok(Flasher::connect(serial, port_info, opts.speed, opts.use_stub)?)
135142
}
136143

137144
pub fn board_info(opts: ConnectOpts, config: Config) -> Result<()> {

espflash/src/connection.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ impl Connection {
8686
Err(Error::Connection(ConnectionError::ConnectionFailed))
8787
}
8888

89-
fn sync(&mut self) -> Result<(), Error> {
89+
pub(crate) fn sync(&mut self) -> Result<(), Error> {
9090
self.with_timeout(CommandType::Sync.timeout(), |connection| {
9191
connection.write_command(Command::Sync)?;
9292
connection.flush()?;
@@ -254,7 +254,7 @@ impl Connection {
254254
Ok(())
255255
}
256256

257-
fn read(&mut self, len: usize) -> Result<Option<Vec<u8>>, Error> {
257+
pub(crate) fn read(&mut self, len: usize) -> Result<Option<Vec<u8>>, Error> {
258258
let mut tmp = Vec::with_capacity(1024);
259259
loop {
260260
self.decoder.decode(&mut self.serial, &mut tmp)?;

espflash/src/elf.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use crate::{
2121

2222
pub const ESP_CHECKSUM_MAGIC: u8 = 0xef;
2323

24-
#[derive(Copy, Clone, EnumVariantNames)]
24+
#[derive(Copy, Clone, Debug, EnumVariantNames)]
2525
#[strum(serialize_all = "UPPERCASE")]
2626
pub enum FlashMode {
2727
Qio,

espflash/src/flasher.rs

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use crate::{
1111
elf::{ElfFirmwareImage, FirmwareImage, FlashFrequency, FlashMode, RomSegment},
1212
error::{ConnectionError, FlashDetectError, ResultExt},
1313
image_format::ImageFormatId,
14-
Error, PartitionTable,
14+
Error, PartitionTable, stubs::FlashStub,
1515
};
1616

1717
const DEFAULT_TIMEOUT: Duration = Duration::from_secs(3);
@@ -192,6 +192,7 @@ impl Flasher {
192192
serial: Box<dyn SerialPort>,
193193
port_info: UsbPortInfo,
194194
speed: Option<u32>,
195+
use_stub: bool,
195196
) -> Result<Self, Error> {
196197
// Establish a connection to the device using the default baud rate of 115,200
197198
// and timeout of 3 seconds.
@@ -209,6 +210,13 @@ impl Flasher {
209210
flash_size: FlashSize::Flash4Mb,
210211
spi_params: SpiAttachParams::default(),
211212
};
213+
214+
// Load flash stub if enabled
215+
if use_stub {
216+
println!("Using flash stub");
217+
flasher.load_stub()?;
218+
}
219+
212220
flasher.spi_autodetect()?;
213221

214222
// Now that we have established a connection and detected the chip and flash
@@ -228,11 +236,65 @@ impl Flasher {
228236
Ok(flasher)
229237
}
230238

239+
/// Load flash stub
240+
fn load_stub(&mut self) -> Result<(), Error> {
241+
242+
println!("Loading flash stub for chip: {:?}", self.chip);
243+
244+
// Load flash stub
245+
let stub = FlashStub::get(self.chip).unwrap();
246+
247+
let mut ram_target = self.chip.ram_target(Some(stub.entry()));
248+
ram_target.begin(&mut self.connection).flashing()?;
249+
250+
let (text_addr, text) = stub.text();
251+
println!("Write {} byte stub text", text.len());
252+
253+
ram_target.write_segment(
254+
&mut self.connection,
255+
RomSegment {
256+
addr: text_addr,
257+
data: Cow::Borrowed(&text),
258+
},
259+
)
260+
.flashing()?;
261+
262+
263+
let (data_addr, data) = stub.data();
264+
println!("Write {} byte stub data", data.len());
265+
266+
ram_target.write_segment(
267+
&mut self.connection,
268+
RomSegment {
269+
addr: data_addr,
270+
data: Cow::Borrowed(&data),
271+
},
272+
)
273+
.flashing()?;
274+
275+
println!("Finish stub write");
276+
ram_target.finish(&mut self.connection, true).flashing()?;
277+
278+
println!("Stub written...");
279+
280+
// Re-sync connection
281+
self.connection.sync()?;
282+
283+
// Re-detect chip to check stub is up
284+
let magic = self.connection.read_reg(CHIP_DETECT_MAGIC_REG_ADDR)?;
285+
let chip = Chip::from_magic(magic)?;
286+
println!("Re-detected chip: {:?}", chip);
287+
288+
Ok(())
289+
}
290+
231291
fn spi_autodetect(&mut self) -> Result<(), Error> {
232292
// Loop over all available SPI parameters until we find one that successfully
233293
// reads the flash size.
234294
for spi_params in TRY_SPI_PARAMS.iter().copied() {
235-
self.enable_flash(spi_params)?;
295+
if let Err(_e) = self.enable_flash(spi_params) {
296+
continue;
297+
}
236298
if let Some(flash_size) = self.flash_detect()? {
237299
// Flash detection was successful, so save the flash size and SPI parameters and
238300
// return.

espflash/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,5 @@ pub mod partition_table;
1919

2020
#[doc(hidden)]
2121
pub mod cli;
22+
23+
pub mod stubs;

espflash/src/main.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,25 +12,30 @@ use espflash::{
1212
use miette::{IntoDiagnostic, Result, WrapErr};
1313
use strum::VariantNames;
1414

15-
#[derive(Parser)]
15+
#[derive(Debug, Parser)]
1616
#[clap(version, propagate_version = true)]
1717
struct Opts {
1818
/// Image format to flash
1919
#[clap(long, possible_values = &["bootloader", "direct-boot"])]
2020
pub format: Option<String>,
21+
2122
#[clap(flatten)]
2223
pub flash_config_opts: FlashConfigOpts,
24+
2325
#[clap(flatten)]
2426
flash_opts: FlashOpts,
27+
2528
#[clap(flatten)]
2629
connect_opts: ConnectOpts,
30+
2731
/// ELF image to flash
2832
image: Option<String>,
33+
2934
#[clap(subcommand)]
3035
subcommand: Option<SubCommand>,
3136
}
3237

33-
#[derive(Parser)]
38+
#[derive(Debug, Parser)]
3439
pub enum SubCommand {
3540
/// Display information about the connected board and exit without flashing
3641
BoardInfo(ConnectOpts),
@@ -44,7 +49,7 @@ pub enum SubCommand {
4449
WriteBinToFlash(WriteBinToFlashOpts),
4550
}
4651

47-
#[derive(Parser)]
52+
#[derive(Debug, Parser)]
4853
pub struct SaveImageOpts {
4954
#[clap(flatten)]
5055
pub flash_config_opts: FlashConfigOpts,

espflash/src/stubs/mod.rs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
use serde::{Serialize, Deserialize};
2+
3+
use crate::Chip;
4+
5+
6+
#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
7+
pub struct FlashStub {
8+
entry: u32,
9+
text: String,
10+
text_start: u32,
11+
data: String,
12+
data_start: u32,
13+
}
14+
15+
const STUB_32: &str = include_str!("../../../stubs/stub_flasher_32.json");
16+
const STUB_32C2: &str = include_str!("../../../stubs/stub_flasher_32c2.json");
17+
const STUB_32C3: &str = include_str!("../../../stubs/stub_flasher_32c3.json");
18+
const STUB_32S2: &str = include_str!("../../../stubs/stub_flasher_32s2.json");
19+
const STUB_32S3: &str = include_str!("../../../stubs/stub_flasher_32s3.json");
20+
const STUB_8266: &str = include_str!("../../../stubs/stub_flasher_8266.json");
21+
22+
impl FlashStub {
23+
/// Fetch flash stub for the provided chip
24+
pub fn get(chip: Chip) -> Result<FlashStub, ()> {
25+
let s = match chip {
26+
Chip::Esp32 => STUB_32,
27+
Chip::Esp32c2 => STUB_32C2,
28+
Chip::Esp32c3 => STUB_32C3,
29+
Chip::Esp32s2 => STUB_32S2,
30+
Chip::Esp32s3 => STUB_32S3,
31+
Chip::Esp8266 => STUB_8266,
32+
};
33+
34+
let stub: FlashStub = serde_json::from_str(s)
35+
.map_err(|_| () )?;
36+
37+
Ok(stub)
38+
}
39+
40+
/// Fetch stub entry point
41+
pub fn entry(&self) -> u32 {
42+
self.entry
43+
}
44+
45+
/// Fetch text start address and bytes
46+
pub fn text(&self) -> (u32, Vec<u8>) {
47+
let v = base64::decode(&self.text).unwrap();
48+
(self.text_start, v)
49+
}
50+
51+
/// Fetch data start address and bytes
52+
pub fn data(&self) -> (u32, Vec<u8>) {
53+
let v = base64::decode(&self.data).unwrap();
54+
(self.data_start, v)
55+
}
56+
}

stubs/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
2+
Flasher stubs from https://github.com/espressif/esptool/tree/master/esptool/targets/stub_flasher

0 commit comments

Comments
 (0)