Jeremy Soller 657437e1ce
Faster flashing with SMFI (#32)
* WIP: support for new flashing API

* Add SPI flashing support to tool

* Add timeouts when flashing with ectool

* Test SPI reading

* Use chunks for SPI commands

* Sanity test of flash size

* Read rom in sectors

* Relocate memmap region, remove PMC3

* Use ectool to flash

* Remove debugging of spi command

* Fix flashing over smfi
2020-02-26 09:04:40 -07:00

307 lines
7.2 KiB
Rust

use ectool::{
Ec,
Error,
Firmware,
SpiRom,
Timeout,
};
use std::{
env,
fs,
io,
process,
str,
time::{Duration, Instant},
thread,
};
pub struct StdTimeout {
instant: Instant,
duration: Duration,
}
impl StdTimeout {
pub fn new(duration: Duration) -> Self {
StdTimeout {
instant: Instant::now(),
duration
}
}
}
impl Timeout for StdTimeout {
fn reset(&mut self) {
self.instant = Instant::now();
}
fn running(&self) -> bool {
self.instant.elapsed() < self.duration
}
}
unsafe fn iopl() {
extern {
fn iopl(level: isize) -> isize;
}
if iopl(3) < 0 {
eprintln!("failed to get I/O permission: {}", io::Error::last_os_error());
process::exit(1);
}
}
unsafe fn console() -> Result<(), Error> {
iopl();
let mut ec = Ec::new(
StdTimeout::new(Duration::new(1, 0)),
)?;
let mut head = ec.debug(0) as usize;
loop {
let tail = ec.debug(0) as usize;
if tail == 0 || head == tail {
thread::sleep(Duration::from_millis(1));
} else {
while head != tail {
head += 1;
if head >= 256 { head = 1; }
let c = ec.debug(head as u8);
print!("{}", c as char);
}
}
}
}
unsafe fn flash_inner(ec: &mut Ec<StdTimeout>, firmware: &Firmware) -> Result<(), Error> {
let rom_size = 128 * 1024;
let sector_size = 1024;
let mut spi_bus = ec.spi(true)?;
let mut spi = SpiRom::new(
&mut spi_bus,
StdTimeout::new(Duration::new(1, 0))
);
// Read entire ROM
let mut rom = vec![0; rom_size];
eprintln!("SPI read");
spi.read_at(0, &mut rom)?;
eprintln!("Saving ROM to backup.rom");
fs::write("backup.rom", &rom).map_err(|_| Error::Verify)?;
let mut matches = true;
for i in 0..rom.len() {
if &rom[i] != firmware.data.get(i).unwrap_or(&0xFF) {
matches = false;
break;
}
}
if matches {
eprintln!("ROM matches specified firmware");
return Ok(());
}
{
// Chip erase
// eprintln!("SPI chip erase");
// spi.erase_chip()?;
// Sector erase
let mut address = 0;
while address < rom_size {
let mut erased = true;
for &b in &rom[address..address + sector_size] {
if b != 0xFF {
erased =false;
break;
}
}
if erased {
eprintln!("SPI sector already erased {:06X}", address);
address += sector_size;
} else {
eprintln!("SPI sector erase {:06X}", address);
address += spi.erase_sector(address as u32)?;
}
}
// Read entire ROM
eprintln!("SPI read");
spi.read_at(0, &mut rom)?;
}
// Verify chip erase
for i in 0..rom.len() {
if rom[i] != 0xFF {
eprintln!("Failed to erase: {:X} is {:X} instead of {:X}", i, rom[i], 0xFF);
return Err(Error::Verify);
}
}
// Program
{
eprintln!("SPI AAI word program");
spi.write_at(0, &firmware.data)?;
// Read entire ROM
eprintln!("SPI read");
spi.read_at(0, &mut rom)?;
}
// Verify program
for i in 0..rom.len() {
if &rom[i] != firmware.data.get(i).unwrap_or(&0xFF) {
eprintln!("Failed to program: {:X} is {:X} instead of {:X}", i, rom[i], firmware.data[i]);
return Err(Error::Verify);
}
}
eprintln!("Successfully programmed SPI ROM");
Ok(())
}
unsafe fn flash(path: &str) -> Result<(), Error> {
//TODO: remove unwraps
let firmware_data = fs::read(path).unwrap();
let firmware = Firmware::new(&firmware_data).unwrap();
println!("file board: {:?}", str::from_utf8(firmware.board));
println!("file version: {:?}", str::from_utf8(firmware.version));
iopl();
let mut ec = Ec::new(
StdTimeout::new(Duration::new(1, 0)),
)?;
{
let mut data = [0; 256];
let size = ec.board(&mut data)?;
let ec_board = &data[..size];
println!("ec board: {:?}", str::from_utf8(ec_board));
if ec_board != firmware.board {
panic!("file board does not match ec board");
}
}
{
let mut data = [0; 256];
let size = ec.version(&mut data)?;
let ec_version = &data[..size];
println!("ec version: {:?}", str::from_utf8(ec_version));
}
// Wait for any key releases
eprintln!("Waiting 5 seconds for all keys to be released");
thread::sleep(Duration::new(5, 0));
eprintln!("Sync");
let _ = process::Command::new("sync").status();
let res = flash_inner(&mut ec, &firmware);
eprintln!("Result: {:X?}", res);
eprintln!("Sync");
let _ = process::Command::new("sync").status();
eprintln!("System will shut off in 5 seconds");
thread::sleep(Duration::new(5, 0));
eprintln!("Sync");
let _ = process::Command::new("sync").status();
ec.reset()?;
res
}
unsafe fn info() -> Result<(), Error> {
iopl();
let mut ec = Ec::new(
StdTimeout::new(Duration::new(1, 0)),
)?;
{
print!("board: ");
let mut data = [0; 256];
let size = ec.board(&mut data)?;
for &b in data[..size].iter() {
print!("{}", b as char);
}
println!();
}
{
print!("version: ");
let mut data = [0; 256];
let size = ec.version(&mut data)?;
for &b in data[..size].iter() {
print!("{}", b as char);
}
println!();
}
Ok(())
}
fn usage() {
eprintln!(" console");
eprintln!(" flash [file]");
eprintln!(" info");
}
fn main() {
let mut args = env::args().skip(1);
match args.next() {
Some(arg) => match arg.as_str() {
"console" => match unsafe { console() } {
Ok(()) => (),
Err(err) => {
eprintln!("failed to read console: {:X?}", err);
process::exit(1);
},
},
"flash" => match args.next() {
Some(path) => match unsafe { flash(&path) } {
Ok(()) => (),
Err(err) => {
eprintln!("failed to flash '{}': {:X?}", path, err);
process::exit(1);
},
},
None => {
eprintln!("no file provided");
process::exit(1);
}
},
"info" => match unsafe { info() } {
Ok(()) => (),
Err(err) => {
eprintln!("failed to read info: {:X?}", err);
process::exit(1);
},
},
_ => {
eprintln!("unknown subcommand '{}'", arg);
usage();
process::exit(1);
},
},
None => {
eprintln!("no subcommand provided");
usage();
process::exit(1);
},
}
}