diff --git a/blflash/src/connection.rs b/blflash/src/connection.rs index 3d1876c..2daa468 100644 --- a/blflash/src/connection.rs +++ b/blflash/src/connection.rs @@ -1,5 +1,5 @@ use crate::{Error, RomError}; -use byteorder::{LittleEndian, ReadBytesExt}; +use byteorder::{ByteOrder, LittleEndian, ReadBytesExt}; use std::io::{Cursor, Read, Write}; use std::thread::sleep; use std::time::Duration; @@ -10,17 +10,21 @@ pub const DEFAULT_BAUDRATE: BaudRate = BaudRate::Baud115200; pub struct Connection { serial: Box, - baud_rate: BaudRate, + baud_rate: Option, } impl Connection { pub fn new(serial: impl SerialPort + 'static) -> Self { Connection { serial: Box::new(serial), - baud_rate: DEFAULT_BAUDRATE, + baud_rate: None, } } + pub fn into_inner(self) -> Box { + self.serial + } + pub fn reset(&mut self) -> Result<(), Error> { self.serial.set_rts(false)?; sleep(Duration::from_millis(50)); @@ -51,7 +55,7 @@ impl Connection { } pub fn set_baud(&mut self, speed: BaudRate) -> Result<(), Error> { - self.baud_rate = speed; + self.baud_rate = Some(speed); self.serial .reconfigure(&|setup: &mut dyn SerialPortSettings| setup.set_baud_rate(speed))?; Ok(()) @@ -75,6 +79,11 @@ impl Connection { Ok(buf) } + pub fn read_response_with_payload(&mut self) -> Result, Error> { + let len = LittleEndian::read_u16(&self.read_response(2)?); + self.read_exact(len as usize) + } + pub fn read_response(&mut self, len: usize) -> Result, Error> { let resp = self.read_exact(2)?; match &resp[0..2] { @@ -101,7 +110,8 @@ impl Connection { } pub fn calc_duration_length(&mut self, duration: Duration) -> usize { - self.baud_rate.speed() / 10 / 1000 * (duration.as_millis() as usize) + self.baud_rate.unwrap_or(DEFAULT_BAUDRATE).speed() / 10 / 1000 + * (duration.as_millis() as usize) } pub fn write_all(&mut self, buf: &[u8]) -> Result<(), Error> { diff --git a/blflash/src/flasher.rs b/blflash/src/flasher.rs index 0a3f98e..56c7f74 100644 --- a/blflash/src/flasher.rs +++ b/blflash/src/flasher.rs @@ -1,40 +1,48 @@ use crate::chip::Chip; use crate::elf::FirmwareImage; use crate::Error; -use crate::{ - connection::{Connection, DEFAULT_BAUDRATE}, - elf::RomSegment, -}; +use crate::{connection::Connection, elf::RomSegment}; use deku::prelude::*; -use indicatif::HumanBytes; +use indicatif::{HumanBytes, ProgressBar, ProgressStyle}; use serial::{BaudRate, SerialPort}; use sha2::{Digest, Sha256}; -use std::thread::sleep; use std::{ - io::{Cursor, Read}, + io::{Cursor, Read, Write}, time::{Duration, Instant}, }; +use std::{ops::Range, thread::sleep}; + +fn get_bar(len: u64) -> ProgressBar { + let bar = ProgressBar::new(len); + bar.set_style( + ProgressStyle::default_bar() + .template(" {wide_bar} {bytes}/{total_bytes} {bytes_per_sec} {eta} ") + .progress_chars("#>-"), + ); + bar +} pub struct Flasher { connection: Connection, boot_info: protocol::BootInfo, chip: Box, + flash_speed: BaudRate, } impl Flasher { pub fn connect( chip: impl Chip + 'static, serial: impl SerialPort + 'static, - speed: Option, + initial_speed: BaudRate, + flash_speed: BaudRate, ) -> Result { let mut flasher = Flasher { connection: Connection::new(serial), boot_info: protocol::BootInfo::default(), chip: Box::new(chip), + flash_speed, }; - flasher - .connection - .set_baud(speed.unwrap_or(DEFAULT_BAUDRATE))?; + flasher.connection.set_baud(initial_speed)?; flasher.start_connection()?; flasher.connection.set_timeout(Duration::from_secs(10))?; flasher.boot_info = flasher.get_boot_info()?; @@ -42,6 +50,10 @@ impl Flasher { Ok(flasher) } + pub fn into_inner(self) -> Connection { + self.connection + } + pub fn boot_info(&self) -> &protocol::BootInfo { &self.boot_info } @@ -52,8 +64,6 @@ impl Flasher { segments: impl Iterator>, ) -> Result<(), Error> { self.load_eflash_loader()?; - self.connection.set_baud(BaudRate::BaudOther(2_000_000))?; - self.handshake()?; for segment in segments { let local_hash = Sha256::digest(&segment.data[0..segment.size() as usize]); @@ -83,14 +93,17 @@ impl Flasher { let start = Instant::now(); log::info!("Program flash... {:x}", local_hash); + let pb = get_bar(segment.size() as u64); loop { let size = self.flash_program(cur, &mut reader)?; // log::trace!("program {:x} {:x}", cur, size); cur += size; + pb.inc(size as u64); if size == 0 { break; } } + pb.finish_and_clear(); let elapsed = start.elapsed(); log::info!( "Program done {:?} {}/s", @@ -111,8 +124,6 @@ impl Flasher { segments: impl Iterator>, ) -> Result<(), Error> { self.load_eflash_loader()?; - self.connection.set_baud(BaudRate::BaudOther(2_000_000))?; - self.handshake()?; for segment in segments { let local_hash = Sha256::digest(&segment.data[0..segment.size() as usize]); @@ -154,6 +165,23 @@ impl Flasher { Ok(()) } + pub fn dump_flash(&mut self, range: Range, mut writer: impl Write) -> Result<(), Error> { + self.load_eflash_loader()?; + + const BLOCK_SIZE: usize = 4096; + let mut cur = range.start; + let pb = get_bar(range.len() as u64); + while cur < range.end { + let data = self.flash_read(cur, (range.end - cur).min(BLOCK_SIZE as u32))?; + writer.write_all(&data)?; + cur += data.len() as u32; + pb.inc(data.len() as u64); + } + pb.finish_and_clear(); + + Ok(()) + } + pub fn load_eflash_loader(&mut self) -> Result<(), Error> { let input = self.chip.get_eflash_loader().to_vec(); let len = input.len(); @@ -163,12 +191,15 @@ impl Flasher { let start = Instant::now(); log::info!("Sending eflash_loader..."); + let pb = get_bar(len as u64); loop { let size = self.load_segment_data(&mut reader)?; + pb.inc(size as u64); if size == 0 { break; } } + pb.finish_and_clear(); let elapsed = start.elapsed(); log::info!( "Finished {:?} {}/s", @@ -179,6 +210,9 @@ impl Flasher { self.check_image()?; self.run_image()?; sleep(Duration::from_millis(200)); + // TODO configurable + self.connection.set_baud(self.flash_speed)?; + self.handshake()?; Ok(()) } @@ -199,6 +233,16 @@ impl Flasher { Ok(data.digest) } + fn flash_read(&mut self, addr: u32, size: u32) -> Result, Error> { + let mut req = protocol::FlashRead { addr, size }; + req.update()?; + self.connection.write_all(&req.to_bytes()?)?; + self.connection.flush()?; + let data = self.connection.read_response_with_payload()?; + + Ok(data) + } + fn flash_program(&mut self, addr: u32, reader: &mut impl Read) -> Result { let mut data = vec![0u8; 4000]; let size = reader.read(&mut data)?; @@ -407,6 +451,21 @@ mod protocol { } } + #[derive(Debug, DekuWrite, Default)] + #[deku(magic = b"\x32\x00\x08\x00", endian = "little")] + pub struct FlashRead { + pub addr: u32, + pub size: u32, + } + + #[derive(Debug, DekuRead)] + #[deku(magic = b"\x32\x00\x08\x00", endian = "little")] + pub struct FlashReadResp { + pub len: u16, + #[deku(count = "len")] + pub data: Vec, + } + #[derive(Debug, DekuWrite, Default)] #[deku(magic = b"\x3d\x00\x08\x00", endian = "little")] pub struct Sha256Read { diff --git a/blflash/src/main.rs b/blflash/src/main.rs index c14e908..809ecf8 100644 --- a/blflash/src/main.rs +++ b/blflash/src/main.rs @@ -1,21 +1,35 @@ -use std::fs::read; - use blflash::{ - chip::bl602::{self, Bl602}, + chip::{ + bl602::{self, Bl602}, + Chip, + }, image::BootHeaderCfgFile, Config, Error, Flasher, }; use env_logger::Env; use main_error::MainError; -use serial::BaudRate; +use serial::{BaudRate, SerialPort}; +use std::fs::{read, File}; use std::path::PathBuf; use structopt::StructOpt; #[derive(StructOpt)] -struct FlashOpt { +struct Connection { /// Serial port #[structopt(short, long)] port: String, + /// Flash baud rate + #[structopt(short, long, default_value = "115200")] + baud_rate: usize, + /// Initial baud rate + #[structopt(long, default_value = "115200")] + initial_baud_rate: usize, +} + +#[derive(StructOpt)] +struct FlashOpt { + #[structopt(flatten)] + conn: Connection, /// Bin file #[structopt(parse(from_os_str))] image: PathBuf, @@ -35,25 +49,57 @@ struct FlashOpt { #[derive(StructOpt)] struct CheckOpt { - /// Serial port - #[structopt(short, long)] - port: String, + #[structopt(flatten)] + conn: Connection, /// Bin file #[structopt(parse(from_os_str))] image: PathBuf, } +#[derive(StructOpt)] +struct DumpOpt { + #[structopt(flatten)] + conn: Connection, + /// Output file + #[structopt(parse(from_os_str))] + output: PathBuf, + /// start address + #[structopt(default_value = "0")] + start: u32, + /// end address + #[structopt(default_value = "1048576")] + end: u32, +} + #[derive(StructOpt)] enum Opt { /// Flash image to serial Flash(FlashOpt), /// Check if the device's flash matches the image Check(CheckOpt), + /// Dump the whole flash to a file + Dump(DumpOpt), +} + +impl Connection { + fn open_serial(&self) -> Result { + let serial = serial::open(&self.port)?; + Ok(serial) + } + fn create_flasher(&self, chip: impl Chip + 'static) -> Result { + let serial = self.open_serial()?; + Flasher::connect( + chip, + serial, + BaudRate::from_speed(self.initial_baud_rate), + BaudRate::from_speed(self.baud_rate), + ) + } } fn flash(opt: FlashOpt) -> Result<(), Error> { - let serial = serial::open(&opt.port)?; let chip = Bl602; + let mut flasher = opt.conn.create_flasher(chip)?; if !opt.without_boot2 { let partition_cfg = opt @@ -69,7 +115,6 @@ fn flash(opt: FlashOpt) -> Result<(), Error> { let bin = read(&opt.image)?; let segments = chip.with_boot2(partition_cfg, boot_header_cfg, &bin)?; - let mut flasher = Flasher::connect(chip, serial, Some(BaudRate::Baud115200))?; log::info!("Bootrom version: {}", flasher.boot_info().bootrom_version); log::trace!("Boot info: {:x?}", flasher.boot_info()); @@ -78,8 +123,6 @@ fn flash(opt: FlashOpt) -> Result<(), Error> { flasher.reset()?; } else { - let mut flasher = Flasher::connect(chip, serial, Some(BaudRate::Baud115200))?; - log::info!("Bootrom version: {}", flasher.boot_info().bootrom_version); log::trace!("Boot info: {:x?}", flasher.boot_info()); @@ -90,12 +133,12 @@ fn flash(opt: FlashOpt) -> Result<(), Error> { } log::info!("Success"); + Ok(()) } fn check(opt: CheckOpt) -> Result<(), Error> { - let serial = serial::open(&opt.port)?; - let mut flasher = Flasher::connect(Bl602, serial, Some(BaudRate::Baud115200))?; + let mut flasher = opt.conn.create_flasher(Bl602)?; log::info!("Bootrom version: {}", flasher.boot_info().bootrom_version); log::trace!("Boot info: {:x?}", flasher.boot_info()); @@ -106,14 +149,31 @@ fn check(opt: CheckOpt) -> Result<(), Error> { Ok(()) } +fn dump(opt: DumpOpt) -> Result<(), Error> { + let mut flasher = opt.conn.create_flasher(Bl602)?; + + log::info!("Bootrom version: {}", flasher.boot_info().bootrom_version); + log::trace!("Boot info: {:x?}", flasher.boot_info()); + + let mut output = File::create(opt.output)?; + flasher.dump_flash(opt.start..opt.end, &mut output)?; + + log::info!("Success"); + + Ok(()) +} + #[paw::main] fn main(args: Opt) -> Result<(), MainError> { - env_logger::Builder::from_env(Env::default().default_filter_or("blflash=trace")).init(); + env_logger::Builder::from_env(Env::default().default_filter_or("blflash=trace")) + .format_timestamp(None) + .init(); let _config = Config::load(); match args { Opt::Flash(opt) => flash(opt)?, Opt::Check(opt) => check(opt)?, + Opt::Dump(opt) => dump(opt)?, }; Ok(())