#![allow(dead_code)] #![allow(clippy::missing_safety_doc)] #![allow(clippy::needless_range_loop)] //extern crate glob; use hwio::{Io, Pio}; use serialport::{Error, ErrorKind, Result, TTYPort}; use std::any::Any; use std::env; use std::fs; use std::io::{self, Read, Write}; use std::process; use std::time::Duration; use std::thread; use glob::glob; use ecflash::EcFlash; const EC_KNOWN_IDS: &[u16] = &[ 0x5570, 0x8587, ]; #[repr(u8)] pub enum Address { CHIPID0 = 0, CHIPID1 = 1, CHIPVER = 2, INDAR0 = 4, INDAR1 = 5, INDAR2 = 6, INDAR3 = 7, INDDR = 8, ECMSADDR0 = 0x2E, ECMSADDR1 = 0x2F, ECMSDATA = 0x30, } pub trait Debugger { /// Set the debugger address fn address(&mut self, address: u8) -> Result<()>; /// Read data from the debugger port fn read(&mut self, data: &mut [u8]) -> Result; /// Write data to the debugger port fn write(&mut self, data: &[u8]) -> Result; /// Read data at a debugger address fn read_at(&mut self, address: Address, data: &mut [u8]) -> Result { self.address(address as u8)?; self.read(data) } /// Write data at a debugger address fn write_at(&mut self, address: Address, data: &[u8]) -> Result { self.address(address as u8)?; self.write(data) } /// Set EC memory snoop address fn ecms_address(&mut self, address: u16) -> Result<()> { self.write_at(Address::ECMSADDR1, &[(address >> 8) as u8])?; self.write_at(Address::ECMSADDR0, &[(address) as u8])?; Ok(()) } /// Read data from memory using EC-indirect mode fn ecms_read(&mut self, data: &mut [u8]) -> Result { self.read_at(Address::ECMSDATA, data) } /// Write data to memory using EC memory snoop fn ecms_write(&mut self, data: &[u8]) -> Result { self.write_at(Address::ECMSDATA, data) } /// Read data from memory at address using EC memory snoop fn ecms_read_at(&mut self, address: u16, data: &mut [u8]) -> Result { self.ecms_address(address)?; self.ecms_read(data) } /// Write data to memory at address using EC memory snoop fn ecms_write_at(&mut self, address: u16, data: &[u8]) -> Result { self.ecms_address(address)?; self.ecms_write(data) } } pub trait Smfi { /// Set indar1 register (special case for follow mode) fn flash_indar1(&mut self, data: u8) -> Result<()>; /// Set EC-indirect flash address fn flash_address(&mut self, address: u32) -> Result<()>; /// Read data from flash using EC-indirect mode fn flash_read(&mut self, data: &mut [u8]) -> Result; /// Write data to flash using EC-indirect mode fn flash_write(&mut self, data: &[u8]) -> Result; /// Read data from flash at address using EC-indirect mode fn flash_read_at(&mut self, address: u32, data: &mut [u8]) -> Result { self.flash_address(address)?; self.flash_read(data) } /// Write data to flash at address using EC-indirect mode fn flash_write_at(&mut self, address: u32, data: &[u8]) -> Result { self.flash_address(address)?; self.flash_write(data) } } impl Smfi for T where T: Debugger { /// Set indar1 register (special case for follow mode) fn flash_indar1(&mut self, data: u8) -> Result<()> { self.write_at(Address::INDAR1, &[data])?; Ok(()) } /// Set EC-indirect flash address fn flash_address(&mut self, address: u32) -> Result<()> { self.write_at(Address::INDAR3, &[(address >> 24) as u8])?; self.write_at(Address::INDAR2, &[(address >> 16) as u8])?; self.write_at(Address::INDAR1, &[(address >> 8) as u8])?; self.write_at(Address::INDAR0, &[(address) as u8])?; Ok(()) } /// Read data from flash using EC-indirect mode fn flash_read(&mut self, data: &mut [u8]) -> Result { self.read_at(Address::INDDR, data) } /// Write data to flash using EC-indirect mode fn flash_write(&mut self, data: &[u8]) -> Result { self.write_at(Address::INDDR, data) } } pub struct SpiBus<'a, T: Smfi> { port: &'a mut T, data: bool, } impl<'a, T: Smfi> SpiBus<'a, T> { pub fn new(port: &'a mut T, eflash: bool) -> Result { port.flash_address( if eflash { 0x7FFF_FE00 } else { 0xFFFF_FE00 }, )?; let mut spi = Self { port, data: false }; spi.reset()?; Ok(spi) } /// Disable SPI chip - should be done before and after each transaction pub fn reset(&mut self) -> Result<()> { if self.data { self.port.flash_indar1(0xFE)?; self.data = false; } self.port.flash_write(&[0])?; Ok(()) } /// Read from SPI chip directly using follow mode pub fn read(&mut self, data: &mut [u8]) -> Result { if !self.data { self.port.flash_indar1(0xFD)?; self.data = true; } self.port.flash_read(data) } /// Write to SPI chip directly using follow mode pub fn write(&mut self, data: &[u8]) -> Result { if !self.data { self.port.flash_indar1(0xFD)?; self.data = true; } self.port.flash_write(data) } } impl<'a, T: Smfi> Drop for SpiBus<'a, T> { fn drop(&mut self) { let _ = self.reset(); } } struct SpiRom<'a, 't, T: Smfi> { bus: &'a mut SpiBus<'t, T>, } impl<'a, 't, T: Smfi> SpiRom<'a, 't, T> { pub fn new(bus: &'a mut SpiBus<'t, T>) -> Self { Self { bus } } pub fn status(&mut self) -> Result { let mut status = [0]; self.bus.reset()?; self.bus.write(&[0x05])?; self.bus.read(&mut status)?; Ok(status[0]) } pub fn write_disable(&mut self) -> Result<()> { self.bus.reset()?; self.bus.write(&[0x04])?; // Poll status for busy and write enable flags //TODO: timeout while self.status()? & 3 != 0 {} Ok(()) } pub fn write_enable(&mut self) -> Result<()> { self.bus.reset()?; self.bus.write(&[0x06])?; // Poll status for busy and write enable flags //TODO: timeout while self.status()? & 3 != 2 {} Ok(()) } pub fn erase_chip(&mut self) -> Result<()> { self.write_enable()?; self.bus.reset()?; self.bus.write(&[0x60])?; // Poll status for busy flag //TODO: timeout while self.status()? & 1 != 0 {} self.write_disable()?; Ok(()) } pub fn erase_sector(&mut self, address: u32) -> Result { if (address & 0xFF00_0000) > 0 { return Err(Error::new( ErrorKind::InvalidInput, format!("address {:X} exceeds 24 bits", address) )); } self.write_enable()?; self.bus.reset()?; self.bus.write(&[ 0xD7, (address >> 16) as u8, (address >> 8) as u8, address as u8, ])?; // Poll status for busy flag //TODO: timeout while self.status()? & 1 != 0 {} self.write_disable()?; Ok(1024) } pub fn read_at(&mut self, address: u32, data: &mut [u8]) -> Result { if (address & 0xFF00_0000) > 0 { return Err(Error::new( ErrorKind::InvalidInput, format!("address {:X} exceeds 24 bits", address) )); } self.bus.reset()?; self.bus.write(&[ 0x0B, (address >> 16) as u8, (address >> 8) as u8, address as u8, 0, ])?; self.bus.read(data) } pub fn write_at(&mut self, address: u32, data: &[u8]) -> Result { if (address & 0xFF00_0000) > 0 { return Err(Error::new( ErrorKind::InvalidInput, format!("address {:X} exceeds 24 bits", address) )); } //TODO: Support programming with any length if (data.len() % 2) != 0 { return Err(Error::new( ErrorKind::InvalidInput, format!("length {} is not a multiple of 2", data.len()) )); } self.write_enable()?; for (i, word) in data.chunks_exact(2).enumerate() { self.bus.reset()?; if i == 0 { self.bus.write(&[ 0xAD, (address >> 16) as u8, (address >> 8) as u8, address as u8, word[0], word[1] ])?; } else { self.bus.write(&[ 0xAD, word[0], word[1] ])?; } // Poll status for busy flag //TODO: timeout while self.status()? & 1 != 0 {} } self.write_disable()?; Ok(data.len()) } } impl<'a, 't, T: Smfi> Drop for SpiRom<'a, 't, T> { fn drop(&mut self) { let _ = self.write_disable(); } } pub struct ParallelArduino { tty: TTYPort, buffer_size: usize, } impl ParallelArduino { /// Connect to parallel port arduino using provided port pub fn new>(path: S) -> Result { let tty = serialport::new(path.as_ref(), 1_000_000) .data_bits(serialport::DataBits::Eight) .flow_control(serialport::FlowControl::None) .parity(serialport::Parity::None) .stop_bits(serialport::StopBits::One) .timeout(Duration::new(1, 0)) .open_native()?; let mut port = Self { tty, buffer_size: 0 }; // Wait until programmer is ready thread::sleep(Duration::new(1, 0)); // Check that programmer is ready port.echo()?; // Read buffer size port.update_buffer_size()?; Ok(port) } fn echo(&mut self) -> Result<()> { self.tty.write_all(&[ b'E', 0, 0x76, ])?; let mut b = [0]; self.tty.read_exact(&mut b)?; if b[0] != 0x76 { return Err(Error::new( ErrorKind::InvalidInput, format!("received echo of {:02X} instead of {:02X}", b[0], 0x76) )); } Ok(()) } fn update_buffer_size(&mut self) -> Result<()> { self.tty.write_all(&[ b'B', 0, ])?; let mut b = [0; 1]; self.tty.read_exact(&mut b)?; // Size is recieved data + 1 self.buffer_size = (b[0] as usize) + 1; eprintln!("Buffer size: {}", self.buffer_size); Ok(()) } } impl Debugger for ParallelArduino { fn address(&mut self, address: u8) -> Result<()> { self.tty.write_all(&[ b'A', address, ])?; Ok(()) } fn read(&mut self, data: &mut [u8]) -> Result { for chunk in data.chunks_mut(self.buffer_size) { let param = (chunk.len() - 1) as u8; self.tty.write_all(&[ b'R', param, ])?; self.tty.read_exact(chunk)?; } Ok(data.len()) } fn write(&mut self, data: &[u8]) -> Result { for chunk in data.chunks(self.buffer_size) { let param = (chunk.len() - 1) as u8; self.tty.write_all(&[ b'W', param, ])?; self.tty.write_all(chunk)?; let mut b = [0]; self.tty.read_exact(&mut b)?; if b[0] != param { return Err(Error::new( ErrorKind::InvalidInput, format!("received ack of {:02X} instead of {:02X}", b[0], param) )); } } Ok(data.len()) } } pub struct I2EC { address: Pio, data: Pio, } impl I2EC { pub fn new() -> Result { //TODO: check EC ID using super i/o if unsafe { libc::iopl(3) } != 0 { return Err(Error::from( io::Error::last_os_error() )); } Ok(Self { address: Pio::new(0x2E), data: Pio::new(0x2F), }) } fn super_io_read(&mut self, reg: u8) -> u8 { self.address.write(reg); self.data.read() } fn super_io_write(&mut self, reg: u8, value: u8) { self.address.write(reg); self.data.write(value); } fn d2_read(&mut self, reg: u8) -> u8 { self.super_io_write(0x2E, reg); self.super_io_read(0x2F) } fn d2_write(&mut self, reg: u8, value: u8) { self.super_io_write(0x2E, reg); self.super_io_write(0x2F, value); } fn i2ec_read(&mut self, addr: u16) -> u8 { self.d2_write(0x11, (addr >> 8) as u8); self.d2_write(0x10, addr as u8); self.d2_read(0x12) } fn i2ec_write(&mut self, addr: u16, value: u8) { self.d2_write(0x11, (addr >> 8) as u8); self.d2_write(0x10, addr as u8); self.d2_write(0x12, value); } } pub struct Pmc { data: Pio, cmd: Pio, } impl Pmc { pub fn new(base: u16) -> Self { Self { data: Pio::new(base), cmd: Pio::new(base + 4), } } pub unsafe fn can_read(&mut self) -> bool { self.cmd.read() & 1 == 1 } pub unsafe fn can_write(&mut self) -> bool { self.cmd.read() & 2 == 0 } pub unsafe fn command(&mut self, data: u8) { //eprintln!("PMC command {:02X}", data); while ! self.can_write() {} self.cmd.write(data); } pub unsafe fn read(&mut self) -> u8 { //eprintln!("PMC read"); while ! self.can_read() {} self.data.read() } pub unsafe fn write(&mut self, data: u8) { //eprintln!("PMC write {:02X}", data); while ! self.can_write() {} self.data.write(data); } pub unsafe fn acpi_read(&mut self, address: u8) -> u8 { self.command(0x80); self.write(address); self.read() } pub unsafe fn acpi_write(&mut self, address: u8, data: u8) { self.command(0x81); self.write(address); self.write(data); } } impl Smfi for Pmc { /// Set indar1 register (special case for follow mode) fn flash_indar1(&mut self, data: u8) -> Result<()> { unsafe { self.acpi_write(Address::INDAR1 as u8, data); } Ok(()) } /// Set EC-indirect flash address fn flash_address(&mut self, address: u32) -> Result<()> { unsafe { self.acpi_write(Address::INDAR3 as u8, (address >> 24) as u8); self.acpi_write(Address::INDAR2 as u8, (address >> 16) as u8); self.acpi_write(Address::INDAR1 as u8, (address >> 8) as u8); self.acpi_write(Address::INDAR0 as u8, (address) as u8); } Ok(()) } /// Read data from flash using EC-indirect mode fn flash_read(&mut self, data: &mut [u8]) -> Result { for b in data.iter_mut() { unsafe { *b = self.acpi_read(Address::INDDR as u8); } } Ok(data.len()) } /// Write data to flash using EC-indirect mode fn flash_write(&mut self, data: &[u8]) -> Result { for b in data.iter() { unsafe { self.acpi_write(Address::INDDR as u8, *b); } } Ok(data.len()) } } fn isp_inner(port: &mut T, firmware: &[u8]) -> Result<()> { // There are two supported ROM sizes, 128KiB and 256KiB let rom_size = if firmware.len() > 128 * 1024 { 256 * 1024 } else { 128 * 1024 }; if firmware.len() > rom_size { return Err(Error::new( ErrorKind::InvalidInput, format!("firmware size {} exceeds rom size {}", firmware.len(), rom_size) )); } let mut spi_bus = SpiBus::new(port, true)?; let mut spi = SpiRom::new(&mut spi_bus); let mut rom = vec![0; rom_size]; { // Read entire ROM eprintln!("SPI read"); spi.read_at(0, &mut rom)?; } eprintln!("Saving ROM to backup.rom"); fs::write("backup.rom", &rom)?; let mut matches = true; for i in 0..rom.len() { if &rom[i] != firmware.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 + 1024] { if b != 0xFF { erased =false; break; } } if erased { eprintln!("SPI sector already erased {:06X}", address); address += 1024; } 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 { return Err(Error::new( ErrorKind::InvalidInput, format!("Failed to erase: {:X} is {:X} instead of {:X}", i, rom[i], 0xFF) )); } } //TODO: Set write disable on error // Program { // Auto address increment word program if (spi.bus.port as &mut dyn Any).is::() { spi.write_enable()?; { eprintln!("SPI AAI word program (accelerated)"); let port = (spi.bus.port as &mut dyn Any).downcast_mut::().unwrap(); for (i, chunk) in firmware.chunks(port.buffer_size).enumerate() { eprint!(" program {} / {}\r", i * port.buffer_size, firmware.len()); let param = (chunk.len() - 1) as u8; port.tty.write_all(&[ b'P', param ])?; port.tty.write_all(chunk)?; let mut b = [0]; port.tty.read_exact(&mut b)?; if b[0] != param { return Err(Error::new( ErrorKind::InvalidInput, format!("received ack of {:02X} instead of {:02X}", b[0], param) )); } } eprintln!(" program {} / {}", firmware.len(), firmware.len()); } spi.write_disable()?; } else { eprintln!("SPI AAI word program"); spi.write_at(0, firmware)?; } // Read entire ROM eprintln!("SPI read"); spi.read_at(0, &mut rom)?; } // Verify program for i in 0..rom.len() { if &rom[i] != firmware.get(i).unwrap_or(&0xFF) { return Err(Error::new( ErrorKind::InvalidInput, format!("Failed to program: {:X} is {:X} instead of {:X}", i, rom[i], firmware[i]) )); } } eprintln!("Successfully programmed SPI ROM"); Ok(()) } fn isp(internal: bool, file: &str) -> Result<()> { // Read firmware data let firmware = { let mut firmware = fs::read(file)?; // Truncate 0xFF bytes while firmware.last() == Some(&0xFF) { firmware.pop(); } // Make sure firmware length is a multiple of word size while firmware.len() % 2 != 0 { firmware.push(0xFF); } firmware }; if internal { unsafe { if libc::iopl(3) < 0 { eprintln!("Failed to get I/O permission: {}", io::Error::last_os_error()); process::exit(1); } //TODO: return error let _ec = EcFlash::new(true).expect("Failed to find EC"); // 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 mut pmc1 = Pmc::new(0x62); let mut pmc3 = Pmc::new(0x6A); // Enter scratch rom pmc1.command(0xEC); if pmc1.read() == 0x76 { eprintln!("Entered scratch ROM"); let res = isp_inner(&mut pmc3, &firmware); 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(); // Will currently power off system pmc3.command(0xEC); match res { Ok(()) => { eprintln!("Successfully flashed EC"); // Shut down process::Command::new("shutdown") .status() .expect("failed to run shutdown"); Ok(()) }, Err(err) => { eprintln!("Failed to flash EC: {}", err); Err(err) } } } else { //TODO: return error panic!("Failed to enter scratch ROM") } } } else { // Open first serial device we find let serial_device = glob("/dev/serial/by-id/usb-*") .unwrap() .next() .expect("No serial device found.") .unwrap(); println!("Using serial device: {:?}", serial_device.display()); let mut port = ParallelArduino::new(serial_device.to_str().unwrap())?; // Read ID let mut id = [0; 3]; port.address(0)?; port.read(&mut id[0..1])?; port.address(1)?; port.read(&mut id[1..2])?; port.address(2)?; port.read(&mut id[2..3])?; let ecid = ((id[0] as u16) << 8) | (id[1] as u16); eprintln!("ID: {:04X} VER: {}", ecid, id[2]); assert!(EC_KNOWN_IDS.contains(&ecid), "Unknown ID: {:04X}", ecid); isp_inner(&mut port, &firmware) } } fn main() { let mut file_opt = None; let mut internal = false; for arg in env::args().skip(1) { if arg == "--internal" { internal = true; } else { file_opt = Some(arg); } } //TODO: better errors let file = file_opt.expect("no firmware file provided"); isp(internal, &file).expect("failed to flash"); }