diff --git a/tool/Cargo.lock b/tool/Cargo.lock index 1259f40..077ee4c 100644 --- a/tool/Cargo.lock +++ b/tool/Cargo.lock @@ -15,7 +15,7 @@ dependencies = [ [[package]] name = "system76_ectool" -version = "0.1.2" +version = "0.1.3" dependencies = [ "redox_hwio 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/tool/Cargo.toml b/tool/Cargo.toml index e3ca5e1..18e5315 100644 --- a/tool/Cargo.toml +++ b/tool/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "system76_ectool" -version = "0.1.2" +version = "0.1.3" edition = "2018" description = "System76 EC tool" license = "MIT" diff --git a/tool/src/ec.rs b/tool/src/ec.rs index 5b01134..8974739 100644 --- a/tool/src/ec.rs +++ b/tool/src/ec.rs @@ -231,6 +231,10 @@ impl<'a, T: Timeout> EcSpi<'a, T> { } impl<'a, T: Timeout> Spi for EcSpi<'a, T> { + fn target(&self) -> SpiTarget { + self.target + } + /// Disable SPI chip, must be done before and after a transaction unsafe fn reset(&mut self) -> Result<(), Error> { let flags = self.flags(false, true); diff --git a/tool/src/main.rs b/tool/src/main.rs index 60e89da..d9c4b83 100644 --- a/tool/src/main.rs +++ b/tool/src/main.rs @@ -93,7 +93,6 @@ unsafe fn flash_read(spi: &mut SpiRom, rom: &mut [u8], se unsafe fn flash_inner(ec: &mut Ec, firmware: &Firmware, target: SpiTarget, scratch: bool) -> Result<(), Error> { let rom_size = 128 * 1024; - let sector_size = 1024; let mut new_rom = firmware.data.to_vec(); while new_rom.len() < rom_size { @@ -105,6 +104,7 @@ unsafe fn flash_inner(ec: &mut Ec, firmware: &Firmware, target: SpiT &mut spi_bus, StdTimeout::new(Duration::new(1, 0)) ); + let sector_size = spi.sector_size(); let mut rom = vec![0xFF; rom_size]; flash_read(&mut spi, &mut rom, sector_size)?; @@ -168,8 +168,7 @@ unsafe fn flash_inner(ec: &mut Ec, firmware: &Firmware, target: SpiT Ok(()) } -unsafe fn flash(path: &str) -> Result<(), Error> { - let target = SpiTarget::Main; +unsafe fn flash(path: &str, target: SpiTarget) -> Result<(), Error> { let scratch = true; //TODO: remove unwraps @@ -258,7 +257,7 @@ unsafe fn info() -> Result<(), Error> { } println!(); } - + Ok(()) } @@ -300,6 +299,7 @@ unsafe fn fan_set(index: u8, duty: u8) -> Result<(), Error> { fn usage() { eprintln!(" console"); eprintln!(" flash [file]"); + eprintln!(" flash_backup [file]"); eprintln!(" fan [index] "); eprintln!(" info"); eprintln!(" print [message]"); @@ -352,7 +352,20 @@ fn main() { }, }, "flash" => match args.next() { - Some(path) => match unsafe { flash(&path) } { + Some(path) => match unsafe { flash(&path, SpiTarget::Main) } { + Ok(()) => (), + Err(err) => { + eprintln!("failed to flash '{}': {:X?}", path, err); + process::exit(1); + }, + }, + None => { + eprintln!("no file provided"); + process::exit(1); + } + }, + "flash_backup" => match args.next() { + Some(path) => match unsafe { flash(&path, SpiTarget::Backup) } { Ok(()) => (), Err(err) => { eprintln!("failed to flash '{}': {:X?}", path, err); diff --git a/tool/src/spi.rs b/tool/src/spi.rs index 99c336c..4e114fd 100644 --- a/tool/src/spi.rs +++ b/tool/src/spi.rs @@ -4,11 +4,13 @@ use crate::{ }; pub trait Spi { + fn target(&self) -> SpiTarget; unsafe fn reset(&mut self) -> Result<(), Error>; unsafe fn read(&mut self, data: &mut [u8]) -> Result; unsafe fn write(&mut self, data: &[u8]) -> Result; } +#[derive(Clone, Copy)] pub enum SpiTarget { Main, Backup, @@ -27,6 +29,13 @@ impl<'a, S: Spi, T: Timeout> SpiRom<'a, S, T> { } } + pub fn sector_size(&self) -> usize { + match self.spi.target() { + SpiTarget::Main => 1024, + SpiTarget::Backup => 4096, + } + } + pub unsafe fn status(&mut self) -> Result { let mut status = [0]; @@ -67,30 +76,21 @@ impl<'a, S: Spi, T: Timeout> SpiRom<'a, S, T> { Ok(()) } - pub unsafe fn erase_chip(&mut self) -> Result<(), Error> { - self.write_enable()?; - - self.spi.reset()?; - self.spi.write(&[0x60])?; - - // Poll status for busy unset - self.status_wait(1, 0)?; - - self.write_disable()?; - - Ok(()) - } - pub unsafe fn erase_sector(&mut self, address: u32) -> Result<(), Error> { if (address & 0xFF00_0000) > 0 { return Err(Error::Parameter); } + let instruction = match self.spi.target() { + SpiTarget::Main => 0xD7, + SpiTarget::Backup => 0x20, + }; + self.write_enable()?; self.spi.reset()?; self.spi.write(&[ - 0xD7, + instruction, (address >> 16) as u8, (address >> 8) as u8, address as u8, @@ -127,30 +127,55 @@ impl<'a, S: Spi, T: Timeout> SpiRom<'a, S, T> { self.write_enable()?; - for (i, word) in data.chunks(2).enumerate() { - let low = *word.get(0).unwrap_or(&0xFF); - let high = *word.get(1).unwrap_or(&0xFF); + match self.spi.target() { + SpiTarget::Main => for (i, word) in data.chunks(2).enumerate() { + let low = *word.get(0).unwrap_or(&0xFF); + let high = *word.get(1).unwrap_or(&0xFF); - self.spi.reset()?; - if i == 0 { - self.spi.write(&[ - 0xAD, - (address >> 16) as u8, - (address >> 8) as u8, - address as u8, - low, - high - ])?; - } else { - self.spi.write(&[ - 0xAD, - low, - high - ])?; - } + self.spi.reset()?; + if i == 0 { + self.spi.write(&[ + 0xAD, + (address >> 16) as u8, + (address >> 8) as u8, + address as u8, + low, + high + ])?; + } else { + self.spi.write(&[ + 0xAD, + low, + high + ])?; + } - // Poll status for busy unset - self.status_wait(1, 0)?; + // Poll status for busy unset + self.status_wait(1, 0)?; + }, + SpiTarget::Backup => for (i, page) in data.chunks(256).enumerate() { + let page_address = address + i as u32 * 256; + if page_address % 256 != 0 { + return Err(Error::Parameter); + } + + if i > 0 { + // Write enable clears after each page is written + self.write_enable()?; + } + + self.spi.reset()?; + self.spi.write(&[ + 0xF2, + (page_address >> 16) as u8, + (page_address >> 8) as u8, + page_address as u8, + ])?; + self.spi.write(&page)?; + + // Poll status for busy unset + self.status_wait(1, 0)?; + }, } self.write_disable()?;