mirror of
https://github.com/spacemeowx2/blflash.git
synced 2024-10-04 05:39:47 +00:00
implement flash
This commit is contained in:
parent
79cdf21fa9
commit
e13e0a830e
66
Cargo.lock
generated
66
Cargo.lock
generated
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -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
78
blflash/src/elf.rs
Normal 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
|
||||
}
|
@ -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],
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ mod connection;
|
||||
mod chip;
|
||||
mod error;
|
||||
mod image;
|
||||
mod elf;
|
||||
|
||||
pub use config::Config;
|
||||
pub use flasher::Flasher;
|
||||
|
@ -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(())
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user