implement flash

This commit is contained in:
spacemeowx2 2020-12-04 04:59:27 +08:00
parent 79cdf21fa9
commit e13e0a830e
8 changed files with 310 additions and 15 deletions

66
Cargo.lock generated
View File

@ -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"

View File

@ -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"

View File

@ -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<CodeSegment<'a>> {
image.segments().filter_map(|s| {
if self.addr_is_flash(s.addr) {
Some(CodeSegment {
addr: s.addr - ROM_START,
..s
})
} else {
None
}
}).collect()
}
}

View File

@ -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<CodeSegment<'a>>;
}

78
blflash/src/elf.rs Normal file
View File

@ -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<Self, &'static str> {
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<Item = CodeSegment<'a>> + '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<Ordering> {
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
}

View File

@ -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<u32, Error> {
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<usize, Error> {
fn load_segment_data(&mut self, reader: &mut impl Read) -> Result<u32, Error> {
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<protocol::BootInfo, Error> {
@ -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<u8>,
}
#[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<u8>,
}
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],
}
}

View File

@ -4,6 +4,7 @@ mod connection;
mod chip;
mod error;
mod image;
mod elf;
pub use config::Config;
pub use flasher::Flasher;

View File

@ -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(())
}