Skip to content

Commit cb84aec

Browse files
committed
--flash-rw-ec: Allow --dry-run
If you want to simulate flashing the EC but not actually do it. Goes through all the steps to make sure the execution plan is correct. Signed-off-by: Daniel Schaefer <[email protected]>
1 parent 9d01271 commit cb84aec

File tree

5 files changed

+78
-29
lines changed

5 files changed

+78
-29
lines changed

EXAMPLES.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -464,3 +464,23 @@ ESRT Entry 0
464464
Last Attempt Version: 0x108 (264)
465465
Last Attempt Status: Success
466466
```
467+
468+
## Flashing EC firmware
469+
470+
**IMPORTANT** Flashing EC firmware yourself is not recommended. It may render
471+
your hardware unbootable. Please update your firmware using the official BIOS
472+
update methods (Windows .exe, LVFS/FWUPD, EFI updater)!
473+
474+
This command has not been thoroughly tested on all Framework Computer systems
475+
476+
```
477+
# Simulate flashing RW (to see which blocks are updated)
478+
> framework_tool --flash-rw-ec ec.bin --dry-run
479+
480+
# Actually flash RW
481+
> framework_tool --flash-rw-ec ec.bin
482+
483+
# Boot into EC RW firmware (will crash your OS and reboot immediately)
484+
# EC will boot back into RO if the system turned off for 30s
485+
> framework_tool --reboot-ec jump-rw
486+
```

framework_lib/src/chromium_ec/mod.rs

Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -703,7 +703,7 @@ impl CrosEc {
703703
/// | 3C000 | 3FFFF | 04000 | Preserved |
704704
/// | 40000 | 3C000 | 39000 | RO Region |
705705
/// | 79000 | 79FFF | 01000 | Flash Flags |
706-
pub fn reflash(&self, data: &[u8], ft: EcFlashType) -> EcResult<()> {
706+
pub fn reflash(&self, data: &[u8], ft: EcFlashType, dry_run: bool) -> EcResult<()> {
707707
let mut res = Ok(());
708708
if ft == EcFlashType::Full || ft == EcFlashType::Ro {
709709
if let Some(version) = ec_binary::read_ec_version(data, true) {
@@ -724,11 +724,6 @@ impl CrosEc {
724724
}
725725
}
726726

727-
if ft == EcFlashType::Full || ft == EcFlashType::Ro {
728-
println!("For safety reasons flashing RO firmware is disabled.");
729-
return Ok(());
730-
}
731-
732727
println!("Unlocking flash");
733728
self.flash_notify(MecFlashNotify::AccessSpi)?;
734729
self.flash_notify(MecFlashNotify::FirmwareStart)?;
@@ -741,12 +736,12 @@ impl CrosEc {
741736
if ft == EcFlashType::Full || ft == EcFlashType::Rw {
742737
let rw_data = &data[FLASH_RW_BASE as usize..(FLASH_RW_BASE + FLASH_RW_SIZE) as usize];
743738

744-
println!("Erasing RW region");
745-
self.erase_ec_flash(FLASH_BASE + FLASH_RW_BASE, FLASH_RW_SIZE)?;
739+
println!("Erasing RW region{}", if dry_run { " (DRY RUN)" } else { "" });
740+
self.erase_ec_flash(FLASH_BASE + FLASH_RW_BASE, FLASH_RW_SIZE, dry_run)?;
746741
println!(" Done");
747742

748-
println!("Writing RW region");
749-
self.write_ec_flash(FLASH_BASE + FLASH_RW_BASE, rw_data)?;
743+
println!("Writing RW region{}", if dry_run { " (DRY RUN)" } else { "" });
744+
self.write_ec_flash(FLASH_BASE + FLASH_RW_BASE, rw_data, dry_run)?;
750745
println!(" Done");
751746

752747
println!("Verifying RW region");
@@ -763,11 +758,11 @@ impl CrosEc {
763758
let ro_data = &data[FLASH_RO_BASE as usize..(FLASH_RO_BASE + FLASH_RO_SIZE) as usize];
764759

765760
println!("Erasing RO region");
766-
self.erase_ec_flash(FLASH_BASE + FLASH_RO_BASE, FLASH_RO_SIZE)?;
761+
self.erase_ec_flash(FLASH_BASE + FLASH_RO_BASE, FLASH_RO_SIZE, dry_run)?;
767762
println!(" Done");
768763

769764
println!("Writing RO region");
770-
self.write_ec_flash(FLASH_BASE + FLASH_RO_BASE, ro_data)?;
765+
self.write_ec_flash(FLASH_BASE + FLASH_RO_BASE, ro_data, dry_run)?;
771766
println!(" Done");
772767

773768
println!("Verifying RO region");
@@ -791,7 +786,7 @@ impl CrosEc {
791786
}
792787

793788
/// Write a big section of EC flash. Must be unlocked already
794-
fn write_ec_flash(&self, addr: u32, data: &[u8]) -> EcResult<()> {
789+
fn write_ec_flash(&self, addr: u32, data: &[u8], dry_run: bool) -> EcResult<()> {
795790
// TODO: Use flash info to help guide ideal chunk size
796791
// let info = EcRequestFlashInfo {}.send_command(self)?;
797792
//let chunk_size = ((0x80 / info.write_ideal_size) * info.write_ideal_size) as usize;
@@ -821,10 +816,12 @@ impl CrosEc {
821816
}
822817

823818
let chunk = &data[offset..offset + cur_chunk_size];
824-
let res = self.write_ec_flash_chunk(addr + offset as u32, chunk);
825-
if let Err(err) = res {
826-
println!(" Failed to write chunk: {:?}", err);
827-
return Err(err);
819+
if !dry_run {
820+
let res = self.write_ec_flash_chunk(addr + offset as u32, chunk);
821+
if let Err(err) = res {
822+
println!(" Failed to write chunk: {:?}", err);
823+
return Err(err);
824+
}
828825
}
829826
}
830827
println!();
@@ -842,7 +839,7 @@ impl CrosEc {
842839
.send_command_extra(self, data)
843840
}
844841

845-
fn erase_ec_flash(&self, offset: u32, size: u32) -> EcResult<()> {
842+
fn erase_ec_flash(&self, offset: u32, size: u32, dry_run: bool) -> EcResult<()> {
846843
// Erasing a big section takes too long sometimes and the linux kernel driver times out, so
847844
// split it up into chunks. One chunk is 1/8 of EC ROM size.
848845
let chunk_size = 0x10000;
@@ -859,11 +856,13 @@ impl CrosEc {
859856
"EcRequestFlashErase (0x{:05X}, 0x{:05X})",
860857
cur_offset, cur_size
861858
);
862-
EcRequestFlashErase {
863-
offset: cur_offset,
864-
size: cur_size,
859+
if !dry_run {
860+
EcRequestFlashErase {
861+
offset: cur_offset,
862+
size: cur_size,
863+
}
864+
.send_command(self)?;
865865
}
866-
.send_command(self)?;
867866
cur_offset += chunk_size;
868867
}
869868
Ok(())

framework_lib/src/commandline/clap_std.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,11 +123,11 @@ struct ClapCli {
123123
#[arg(long)]
124124
dump_ec_flash: Option<std::path::PathBuf>,
125125

126-
/// Flash EC with new firmware from file
126+
/// Flash EC (RO+RW) with new firmware from file - may render your hardware unbootable!
127127
#[arg(long)]
128128
flash_ec: Option<std::path::PathBuf>,
129129

130-
/// Flash EC with new RO firmware from file
130+
/// Flash EC with new RO firmware from file - may render your hardware unbootable!
131131
#[arg(long)]
132132
flash_ro_ec: Option<std::path::PathBuf>,
133133

@@ -250,6 +250,14 @@ struct ClapCli {
250250
/// Run self-test to check if interaction with EC is possible
251251
#[arg(long, short)]
252252
test: bool,
253+
254+
/// Force execution of an unsafe command - may render your hardware unbootable!
255+
#[arg(long, short)]
256+
force: bool,
257+
258+
/// Simulate execution of a command (e.g. --flash-ec)
259+
#[arg(long)]
260+
dry_run: bool,
253261
}
254262

255263
/// Parse a list of commandline arguments and return the struct
@@ -424,6 +432,8 @@ pub fn parse(args: &[String]) -> Cli {
424432
pd_addrs,
425433
pd_ports,
426434
test: args.test,
435+
dry_run: args.dry_run,
436+
force: args.force,
427437
// TODO: Set help. Not very important because Clap handles this by itself
428438
help: false,
429439
// UEFI only for now. Don't need to handle

framework_lib/src/commandline/mod.rs

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,8 @@ pub struct Cli {
170170
pub flash_rw_ec: Option<String>,
171171
pub driver: Option<CrosEcDriverType>,
172172
pub test: bool,
173+
pub dry_run: bool,
174+
pub force: bool,
173175
pub intrusion: bool,
174176
pub inputdeck: bool,
175177
pub inputdeck_mode: Option<InputDeckModeArg>,
@@ -594,7 +596,7 @@ fn print_esrt() {
594596
}
595597
}
596598

597-
fn flash_ec(ec: &CrosEc, ec_bin_path: &str, flash_type: EcFlashType) {
599+
fn flash_ec(ec: &CrosEc, ec_bin_path: &str, flash_type: EcFlashType, dry_run: bool) {
598600
#[cfg(feature = "uefi")]
599601
let data = crate::uefi::fs::shell_read_file(ec_bin_path);
600602
#[cfg(not(feature = "uefi"))]
@@ -613,7 +615,7 @@ fn flash_ec(ec: &CrosEc, ec_bin_path: &str, flash_type: EcFlashType) {
613615
println!("File");
614616
println!(" Size: {:>20} B", data.len());
615617
println!(" Size: {:>20} KB", data.len() / 1024);
616-
if let Err(err) = ec.reflash(&data, flash_type) {
618+
if let Err(err) = ec.reflash(&data, flash_type, dry_run) {
617619
println!("Error: {:?}", err);
618620
} else {
619621
println!("Success!");
@@ -1107,11 +1109,19 @@ pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 {
11071109
// TODO: Should have progress indicator
11081110
dump_ec_flash(&ec, dump_path);
11091111
} else if let Some(ec_bin_path) = &args.flash_ec {
1110-
flash_ec(&ec, ec_bin_path, EcFlashType::Full);
1112+
if args.force {
1113+
flash_ec(&ec, ec_bin_path, EcFlashType::Full, args.dry_run);
1114+
} else {
1115+
error!("Flashing EC RO region is unsafe. Use --flash-ec-rw instead");
1116+
}
11111117
} else if let Some(ec_bin_path) = &args.flash_ro_ec {
1112-
flash_ec(&ec, ec_bin_path, EcFlashType::Ro);
1118+
if args.force {
1119+
flash_ec(&ec, ec_bin_path, EcFlashType::Ro, args.dry_run);
1120+
} else {
1121+
error!("Flashing EC RO region is unsafe. Use --flash-ec-rw instead");
1122+
}
11131123
} else if let Some(ec_bin_path) = &args.flash_rw_ec {
1114-
flash_ec(&ec, ec_bin_path, EcFlashType::Rw);
1124+
flash_ec(&ec, ec_bin_path, EcFlashType::Rw, args.dry_run);
11151125
} else if let Some(hash_file) = &args.hash {
11161126
println!("Hashing file: {}", hash_file);
11171127
#[cfg(feature = "uefi")]
@@ -1193,6 +1203,8 @@ Options:
11931203
--console <CONSOLE> Get EC console, choose whether recent or to follow the output [possible values: recent, follow]
11941204
--hash <HASH> Hash a file of arbitrary data
11951205
--flash-gpu-descriptor <MAGIC> <18 DIGIT SN> Overwrite the GPU bay descriptor SN and type.
1206+
-f, --force Force execution of an unsafe command - may render your hardware unbootable!
1207+
--dry-run Simulate execution of a command (e.g. --flash-ec)
11961208
-t, --test Run self-test to check if interaction with EC is possible
11971209
-h, --help Print help information
11981210
-b Print output one screen at a time

framework_lib/src/commandline/uefi.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,8 @@ pub fn parse(args: &[String]) -> Cli {
109109
pd_addrs: None,
110110
pd_ports: None,
111111
test: false,
112+
dry_run: false,
113+
force: false,
112114
help: false,
113115
flash_gpu_descriptor: None,
114116
allupdate: false,
@@ -504,6 +506,12 @@ pub fn parse(args: &[String]) -> Cli {
504506
} else if arg == "-t" || arg == "--test" {
505507
cli.test = true;
506508
found_an_option = true;
509+
} else if arg == "-f" || arg == "--force" {
510+
cli.force = true;
511+
found_an_option = true;
512+
} else if arg == "--dry-run" {
513+
cli.dry_run = true;
514+
found_an_option = true;
507515
} else if arg == "-h" || arg == "--help" {
508516
cli.help = true;
509517
found_an_option = true;

0 commit comments

Comments
 (0)