Skip to content

Commit dc81d8f

Browse files
authored
Add ROM version of read-flash command (#812)
* Add `ROM` version of `read-flash` command * changelog entry * dumb
1 parent dad60cf commit dc81d8f

File tree

6 files changed

+116
-12
lines changed

6 files changed

+116
-12
lines changed

.github/workflows/hil.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,4 +134,4 @@ jobs:
134134
run: timeout 20 bash espflash/tests/scripts/write-bin.sh
135135

136136
- name: read-flash test
137-
run: timeout 20 bash espflash/tests/scripts/read-flash.sh
137+
run: timeout 30 bash espflash/tests/scripts/read-flash.sh

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1616
- [cargo-espflash]: Add `write-bin` subcommand (#789)
1717
- Add `--monitor` option to `write-bin`. (#783)
1818
- Add `watchdog-reset` strategy to `--after` subcommand (#779)
19+
- Add `ROM` version of `read-flash` command (#812)
1920

2021
### Changed
2122

espflash/src/cli/mod.rs

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -850,19 +850,26 @@ fn erase_partition(flasher: &mut Flasher, part: &Partition) -> Result<()> {
850850

851851
/// Read flash content and write it to a file
852852
pub fn read_flash(args: ReadFlashArgs, config: &Config) -> Result<()> {
853-
if args.connect_args.no_stub {
854-
return Err(Error::StubRequired.into());
855-
}
856-
857853
let mut flasher = connect(&args.connect_args, config, false, false)?;
858854
print_board_info(&mut flasher)?;
859-
flasher.read_flash(
860-
args.address,
861-
args.size,
862-
args.block_size,
863-
args.max_in_flight,
864-
args.file,
865-
)?;
855+
856+
if args.connect_args.no_stub {
857+
flasher.read_flash_rom(
858+
args.address,
859+
args.size,
860+
args.block_size,
861+
args.max_in_flight,
862+
args.file,
863+
)?;
864+
} else {
865+
flasher.read_flash(
866+
args.address,
867+
args.size,
868+
args.block_size,
869+
args.max_in_flight,
870+
args.file,
871+
)?;
872+
}
866873

867874
Ok(())
868875
}

espflash/src/connection/command.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ pub enum CommandType {
5656
EraseFlash = 0xD0,
5757
EraseRegion = 0xD1,
5858
ReadFlash = 0xD2,
59+
ReadFlashSlow = 0x0E, // ROM only, much slower than the stub read_flash
5960
RunUserCode = 0xD3,
6061
// Flash encryption debug mode supported command
6162
FlashEncryptedData = 0xD4,
@@ -189,6 +190,12 @@ pub enum Command<'a> {
189190
block_size: u32,
190191
max_in_flight: u32,
191192
},
193+
ReadFlashSlow {
194+
offset: u32,
195+
size: u32,
196+
block_size: u32,
197+
max_in_flight: u32,
198+
},
192199
RunUserCode,
193200
FlashDetect,
194201
GetSecurityInfo,
@@ -218,6 +225,7 @@ impl Command<'_> {
218225
Command::EraseFlash { .. } => CommandType::EraseFlash,
219226
Command::EraseRegion { .. } => CommandType::EraseRegion,
220227
Command::ReadFlash { .. } => CommandType::ReadFlash,
228+
Command::ReadFlashSlow { .. } => CommandType::ReadFlashSlow,
221229
Command::RunUserCode { .. } => CommandType::RunUserCode,
222230
Command::FlashDetect => CommandType::FlashDetect,
223231
Command::GetSecurityInfo => CommandType::GetSecurityInfo,
@@ -417,6 +425,22 @@ impl Command<'_> {
417425
writer.write_all(&block_size.to_le_bytes())?;
418426
writer.write_all(&(max_in_flight.to_le_bytes()))?;
419427
}
428+
Command::ReadFlashSlow {
429+
offset,
430+
size,
431+
block_size,
432+
max_in_flight,
433+
} => {
434+
// length
435+
writer.write_all(&(16u16.to_le_bytes()))?;
436+
// checksum
437+
writer.write_all(&(0u32.to_le_bytes()))?;
438+
// data
439+
writer.write_all(&offset.to_le_bytes())?;
440+
writer.write_all(&size.to_le_bytes())?;
441+
writer.write_all(&block_size.to_le_bytes())?;
442+
writer.write_all(&(max_in_flight.to_le_bytes()))?;
443+
}
420444
Command::RunUserCode => {
421445
write_basic(writer, &[], 0)?;
422446
}

espflash/src/flasher/mod.rs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1214,6 +1214,63 @@ impl Flasher {
12141214
Ok(())
12151215
}
12161216

1217+
pub fn read_flash_rom(
1218+
&mut self,
1219+
offset: u32,
1220+
size: u32,
1221+
block_size: u32,
1222+
max_in_flight: u32,
1223+
file_path: PathBuf,
1224+
) -> Result<(), Error> {
1225+
// ROM read limit per command
1226+
const BLOCK_LEN: usize = 64;
1227+
1228+
let mut data: Vec<u8> = Vec::new();
1229+
1230+
let mut file = fs::OpenOptions::new()
1231+
.write(true)
1232+
.truncate(true)
1233+
.create(true)
1234+
.open(&file_path)?;
1235+
1236+
let mut correct_offset = offset;
1237+
1238+
while data.len() < size as usize {
1239+
let block_len = std::cmp::min(BLOCK_LEN, size as usize - data.len());
1240+
1241+
correct_offset += data.len() as u32;
1242+
1243+
let response = self.connection.with_timeout(
1244+
CommandType::ReadFlashSlow.timeout(),
1245+
|connection| {
1246+
connection.command(Command::ReadFlashSlow {
1247+
offset: correct_offset,
1248+
size: block_len as u32,
1249+
block_size,
1250+
max_in_flight,
1251+
})
1252+
},
1253+
)?;
1254+
1255+
let payload: Vec<u8> = response.try_into()?;
1256+
1257+
assert!(payload.len() >= block_len);
1258+
1259+
// command always returns 64 byte buffer,
1260+
// regardless of how many bytes were actually read from flash
1261+
data.append(&mut payload[..block_len].to_vec());
1262+
}
1263+
1264+
file.write_all(&data)?;
1265+
1266+
info!(
1267+
"Flash content successfully read and written to '{}'!",
1268+
file_path.display()
1269+
);
1270+
1271+
Ok(())
1272+
}
1273+
12171274
pub fn read_flash(
12181275
&mut self,
12191276
offset: u32,

espflash/tests/scripts/read-flash.sh

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,21 @@ for len in "${lengths[@]}"; do
3333
echo "Verification failed: content does not match expected for length"
3434
exit 1
3535
fi
36+
37+
echo "Testing ROM read-flash with length: $len"
38+
result=$(espflash read-flash --no-stub 0 "$len" flash_content.bin 2>&1)
39+
echo "$result"
40+
41+
if ! cmp -s <(echo -ne "$EXPECTED") flash_content.bin; then
42+
echo "Verification failed: content does not match expected for length"
43+
exit 1
44+
fi
45+
46+
if [[ ! $result =~ "Flash content successfully read and written to" ]]; then
47+
echo "Failed to read $len bytes from flash"
48+
exit 1
49+
fi
50+
3651
done
3752

3853
echo "All read-flash tests passed!"

0 commit comments

Comments
 (0)