From e13e0a830ecdb25ad949b5f2f8cfe244e3f2a627 Mon Sep 17 00:00:00 2001 From: spacemeowx2 Date: Fri, 4 Dec 2020 04:59:27 +0800 Subject: [PATCH] implement flash --- Cargo.lock | 66 ++++++++++++++++ blflash/Cargo.toml | 1 + blflash/src/chip/bl602/mod.rs | 25 +++++- blflash/src/chip/mod.rs | 2 + blflash/src/elf.rs | 78 +++++++++++++++++++ blflash/src/flasher.rs | 138 ++++++++++++++++++++++++++++++++-- blflash/src/lib.rs | 1 + blflash/src/main.rs | 14 ++-- 8 files changed, 310 insertions(+), 15 deletions(-) create mode 100644 blflash/src/elf.rs diff --git a/Cargo.lock b/Cargo.lock index 836e1c3..847e7c6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -81,11 +81,21 @@ dependencies = [ "pico-args", "serde", "serial", + "sha2", "thiserror", "toml 0.5.7", "xmas-elf", ] +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + [[package]] name = "byteorder" version = "1.3.4" @@ -146,6 +156,12 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "cpuid-bool" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634" + [[package]] name = "darling" version = "0.10.2" @@ -203,6 +219,15 @@ dependencies = [ "syn", ] +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + [[package]] name = "directories-next" version = "1.0.3" @@ -277,6 +302,16 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ba62103ce691c2fd80fbae2213dfdda9ce60804973ac6b6e97de818ea7f52c8" +[[package]] +name = "generic-array" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "getrandom" version = "0.1.15" @@ -397,6 +432,12 @@ version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d3b63360ec3cb337817c2dbd47ab4a0f170d285d8e5a2064600f3def1402397" +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + [[package]] name = "pico-args" version = "0.3.4" @@ -538,6 +579,19 @@ dependencies = [ "serial-core", ] +[[package]] +name = "sha2" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e7aab86fe2149bad8c507606bdb3f4ef5e7b2380eb92350f56122cca72a42a8" +dependencies = [ + "block-buffer", + "cfg-if 1.0.0", + "cpuid-bool", + "digest", + "opaque-debug", +] + [[package]] name = "strsim" version = "0.9.3" @@ -648,6 +702,12 @@ dependencies = [ "serde", ] +[[package]] +name = "typenum" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" + [[package]] name = "unicode-width" version = "0.1.8" @@ -660,6 +720,12 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" +[[package]] +name = "version_check" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" + [[package]] name = "wasi" version = "0.9.0+wasi-snapshot-preview1" diff --git a/blflash/Cargo.toml b/blflash/Cargo.toml index be49478..7dc8568 100644 --- a/blflash/Cargo.toml +++ b/blflash/Cargo.toml @@ -24,3 +24,4 @@ log = "0.4.11" env_logger = "0.8.2" deku = "0.9.1" byteorder = "1.3.4" +sha2 = "0.9.2" diff --git a/blflash/src/chip/bl602/mod.rs b/blflash/src/chip/bl602/mod.rs index f361883..f6a17b8 100644 --- a/blflash/src/chip/bl602/mod.rs +++ b/blflash/src/chip/bl602/mod.rs @@ -1,11 +1,32 @@ -use super::Chip; +use super::{Chip, CodeSegment, FirmwareImage}; -pub const EFLASH_LOADER: &'static [u8] = include_bytes!("eflash_loader_40m.bin"); +const EFLASH_LOADER: &'static [u8] = include_bytes!("eflash_loader_40m.bin"); +const ROM_START: u32 = 0x21000000; +const ROM_END: u32 = 0x21000000 + 0x20000; pub struct Bl602; +impl Bl602 { + fn addr_is_flash(&self, addr: u32) -> bool { + addr >= ROM_START && addr < ROM_END + } +} + impl Chip for Bl602 { fn get_eflash_loader(&self) -> &[u8] { EFLASH_LOADER } + + fn get_flash_segments<'a>(&self, image: &'a FirmwareImage) -> Vec> { + image.segments().filter_map(|s| { + if self.addr_is_flash(s.addr) { + Some(CodeSegment { + addr: s.addr - ROM_START, + ..s + }) + } else { + None + } + }).collect() + } } diff --git a/blflash/src/chip/mod.rs b/blflash/src/chip/mod.rs index e5d26b8..092751c 100644 --- a/blflash/src/chip/mod.rs +++ b/blflash/src/chip/mod.rs @@ -1,6 +1,8 @@ mod bl602; pub use bl602::Bl602; +pub use crate::elf::{FirmwareImage, CodeSegment}; pub trait Chip { fn get_eflash_loader(&self) -> &[u8]; + fn get_flash_segments<'a>(&self, image: &'a FirmwareImage) -> Vec>; } diff --git a/blflash/src/elf.rs b/blflash/src/elf.rs new file mode 100644 index 0000000..84c28cf --- /dev/null +++ b/blflash/src/elf.rs @@ -0,0 +1,78 @@ +use std::borrow::Cow; +use std::cmp::Ordering; + +use xmas_elf::program::{SegmentData, Type}; +use xmas_elf::ElfFile; + +pub struct FirmwareImage<'a> { + pub entry: u32, + pub elf: ElfFile<'a>, +} + +impl<'a> FirmwareImage<'a> { + pub fn from_data(data: &'a [u8]) -> Result { + Ok(Self::from_elf(ElfFile::new(data)?)) + } + + pub fn from_elf(elf: ElfFile<'a>) -> Self { + FirmwareImage { + entry: elf.header.pt2.entry_point() as u32, + elf, + } + } + + pub fn entry(&self) -> u32 { + self.elf.header.pt2.entry_point() as u32 + } + + pub fn segments(&'a self) -> impl Iterator> + 'a { + self.elf + .program_iter() + .filter(|header| { + header.file_size() > 0 && header.get_type() == Ok(Type::Load) && header.offset() > 0 + }) + .flat_map(move |header| { + let addr = header.virtual_addr() as u32; + let size = header.file_size() as u32; + let data = match header.get_data(&self.elf) { + Ok(SegmentData::Undefined(data)) => data, + _ => return None, + }; + Some(CodeSegment { addr, data, size }) + }) + } +} + +#[derive(Debug, Ord, Eq)] +/// A segment of code from the source elf +pub struct CodeSegment<'a> { + pub addr: u32, + pub size: u32, + pub data: &'a [u8], +} + +impl PartialEq for CodeSegment<'_> { + fn eq(&self, other: &Self) -> bool { + self.addr.eq(&other.addr) + } +} + +impl PartialOrd for CodeSegment<'_> { + fn partial_cmp(&self, other: &Self) -> Option { + self.addr.partial_cmp(&other.addr) + } +} + +/// A segment of data to write to the flash +pub struct RomSegment<'a> { + pub addr: u32, + pub data: Cow<'a, [u8]>, +} + +pub fn update_checksum(data: &[u8], mut checksum: u8) -> u8 { + for byte in data { + checksum ^= *byte; + } + + checksum +} \ No newline at end of file diff --git a/blflash/src/flasher.rs b/blflash/src/flasher.rs index 83a58eb..22676f2 100644 --- a/blflash/src/flasher.rs +++ b/blflash/src/flasher.rs @@ -1,11 +1,13 @@ use crate::connection::{Connection, DEFAULT_BAUDRATE}; use crate::Error; use crate::chip::{Chip, Bl602}; -use crate::image::BinReader; +use crate::elf::FirmwareImage; use serial::{BaudRate, SerialPort}; use std::{time::{Duration, Instant}, io::{Cursor, Read}}; use deku::prelude::*; use indicatif::{HumanBytes}; +use sha2::{Sha256, Digest}; +use std::thread::sleep; pub struct Flasher { connection: Connection, @@ -35,13 +37,43 @@ impl Flasher { &self.boot_info } - pub fn load_elf_to_flash(&mut self, _input: &[u8]) -> Result<(), Error> { - let loader = self.chip.get_eflash_loader().to_vec(); - self.load_bin(&loader)?; + pub fn load_elf_to_flash(&mut self, elf_data: &[u8]) -> Result<(), Error> { + self.load_eflash_loader()?; + self.handshake()?; + + let image = FirmwareImage::from_data(elf_data).map_err(|_| Error::InvalidElf)?; + for segment in self.chip.get_flash_segments(&image) { + if segment.size != segment.data.len() as u32 { + log::warn!("size mismatch {} != {}", segment.size, segment.data.len()); + } + log::info!("Erase flash addr: {:x} size: {}", segment.addr, segment.size); + self.flash_erase(segment.addr, segment.addr + segment.size)?; + let local_hash = Sha256::digest(&segment.data[0..segment.size as usize]); + + let mut reader = Cursor::new(segment.data); + let mut cur = segment.addr; + + log::info!("Program flash... {:x}", local_hash); + loop { + let size = self.flash_program(cur, &mut reader)?; + if size == 0 { + break + } + cur += size; + } + self.flash_program_check()?; + log::info!("Program done"); + + let sha256 = self.sha256_read(segment.addr, segment.size)?; + if sha256 != &local_hash[..] { + log::warn!("sha256 not match: {:x?} != {:x?}", sha256, local_hash); + } + } Ok(()) } - pub fn load_bin(&mut self, input: &[u8]) -> Result<(), Error> { + pub fn load_eflash_loader(&mut self) -> Result<(), Error> { + let input = self.chip.get_eflash_loader().to_vec(); let len = input.len(); let mut reader = Cursor::new(input); self.load_boot_header(&mut reader)?; @@ -60,6 +92,62 @@ impl Flasher { self.check_image()?; self.run_image()?; + sleep(Duration::from_millis(200)); + + Ok(()) + } + + fn sha256_read(&mut self, addr: u32, len: u32) -> Result<[u8; 32], Error> { + let mut req = protocol::Sha256Read { + addr, + len, + }; + req.update()?; + self.connection.write_all(&req.to_bytes()?)?; + self.connection.flush()?; + + let data = self.connection.read_response(34)?; + let (_, data) = protocol::Sha256ReadResp::from_bytes((&data, 0))?; + + Ok(data.digest) + } + + fn flash_program_check(&mut self) -> Result<(), Error> { + self.connection.write_all(protocol::FLASH_PROGRAM_CHRCK)?; + self.connection.flush()?; + self.connection.read_response(0)?; + Ok(()) + } + + fn flash_program(&mut self, addr: u32, reader: &mut impl Read) -> Result { + let mut data = vec![0u8; 4000]; + let size = reader.read(&mut data)?; + if size == 0 { + return Ok(0) + } + data.truncate(size); + let mut req = protocol::FlashProgram { + addr, + data, + ..Default::default() + }; + req.update()?; + self.connection.write_all(&req.to_bytes()?)?; + self.connection.flush()?; + self.connection.read_response(0)?; + + Ok(size as u32) + } + + fn flash_erase(&mut self, start: u32, end: u32) -> Result<(), Error> { + let mut req = protocol::FlashErase { + start, + end, + }; + req.update()?; + self.connection.write_all(&req.to_bytes()?)?; + self.connection.flush()?; + self.connection.read_response(0)?; Ok(()) } @@ -112,7 +200,7 @@ impl Flasher { Ok(()) } - fn load_segment_data(&mut self, reader: &mut impl Read) -> Result { + fn load_segment_data(&mut self, reader: &mut impl Read) -> Result { let mut segment_data = vec![0u8; 4000]; let size = reader.read(&mut segment_data)?; if size == 0 { @@ -128,7 +216,7 @@ impl Flasher { self.connection.flush()?; self.connection.read_response(0)?; - Ok(size) + Ok(size as u32) } fn get_boot_info(&mut self) -> Result { @@ -182,6 +270,7 @@ mod protocol { pub const GET_BOOT_INFO: &[u8] = &[0x10, 0x00, 0x00, 0x00]; pub const CHECK_IMAGE: &[u8] = &[0x19, 0x00, 0x00, 0x00]; pub const RUN_IMAGE: &[u8] = &[0x1a, 0x00, 0x00, 0x00]; + pub const FLASH_PROGRAM_CHRCK: &[u8] = &[0x3a, 0x00, 0x00, 0x00]; pub const LOAD_BOOT_HEADER_LEN: usize = 176; pub const LOAD_SEGMENT_HEADER_LEN: usize = 16; @@ -217,4 +306,39 @@ mod protocol { pub segment_data_len: u16, pub segment_data: Vec, } + + #[derive(Debug, DekuWrite, Default)] + #[deku(magic = b"\x30\x00\x08\x00", endian = "little")] + pub struct FlashErase { + pub start: u32, + pub end: u32, + } + + #[derive(Debug, DekuWrite, Default)] + #[deku(magic = b"\x31\x00", endian = "little")] + pub struct FlashProgram { + #[deku(update = "self.len()")] + pub len: u16, + pub addr: u32, + pub data: Vec, + } + + impl FlashProgram { + fn len(&self) -> u16 { + self.data.len() as u16 + 4 + } + } + + #[derive(Debug, DekuWrite, Default)] + #[deku(magic = b"\x3d\x00\x08\x00", endian = "little")] + pub struct Sha256Read { + pub addr: u32, + pub len: u32, + } + + #[derive(Debug, DekuRead)] + #[deku(magic = b"\x20\x00")] + pub struct Sha256ReadResp { + pub digest: [u8; 32], + } } diff --git a/blflash/src/lib.rs b/blflash/src/lib.rs index bda3b53..7c884c6 100644 --- a/blflash/src/lib.rs +++ b/blflash/src/lib.rs @@ -4,6 +4,7 @@ mod connection; mod chip; mod error; mod image; +mod elf; pub use config::Config; pub use flasher::Flasher; diff --git a/blflash/src/main.rs b/blflash/src/main.rs index 3a41214..d643df7 100644 --- a/blflash/src/main.rs +++ b/blflash/src/main.rs @@ -32,6 +32,12 @@ fn main() -> Result<(), MainError> { serial = config.connection.serial; } + let input: String = match elf { + Some(input) => input, + _ => return help(), + }; + let input_bytes = read(&input)?; + let serial: String = match serial { Some(serial) => serial, _ => return help(), @@ -41,13 +47,9 @@ fn main() -> Result<(), MainError> { let mut flasher = Flasher::connect(serial, Some(BaudRate::BaudOther(500_000)))?; log::info!("Bootrom version: {}", flasher.boot_info().bootrom_version); - - let input: String = match elf { - Some(input) => input, - _ => return help(), - }; - let input_bytes = read(&input)?; flasher.load_elf_to_flash(&input_bytes)?; + log::info!("Success"); + Ok(()) }