From 004fdf12bc4193075c5fb6e9f09da15b5aff4f9d Mon Sep 17 00:00:00 2001 From: spacemeowx2 Date: Fri, 4 Dec 2020 19:23:54 +0800 Subject: [PATCH] feat: add partition bin --- Cargo.lock | 23 +++++ blflash/Cargo.toml | 2 + .../src/chip/bl602/cfg/partition_cfg_2M.toml | 94 ++++++++++++++++++ blflash/src/chip/bl602/image/boot2image.bin | Bin 0 -> 47504 bytes blflash/src/chip/bl602/mod.rs | 24 ++++- blflash/src/chip/mod.rs | 2 +- blflash/src/error.rs | 2 + blflash/src/flasher.rs | 3 +- blflash/src/image.rs | 0 blflash/src/image/mod.rs | 3 + blflash/src/image/partition.rs | 75 ++++++++++++++ blflash/src/lib.rs | 2 +- blflash/src/main.rs | 72 ++++++++++---- 13 files changed, 277 insertions(+), 25 deletions(-) create mode 100644 blflash/src/chip/bl602/cfg/partition_cfg_2M.toml create mode 100644 blflash/src/chip/bl602/image/boot2image.bin delete mode 100644 blflash/src/image.rs create mode 100644 blflash/src/image/mod.rs create mode 100644 blflash/src/image/partition.rs diff --git a/Cargo.lock b/Cargo.lock index 0d3f3ea..6cbc2de 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -87,9 +87,11 @@ name = "blflash" version = "0.1.0" dependencies = [ "byteorder", + "crc", "deku", "directories-next", "env_logger", + "hex", "indicatif", "log", "main_error", @@ -112,6 +114,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "build_const" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39092a32794787acd8525ee150305ff051b0aa6cc2abaf193924f5ab05425f39" + [[package]] name = "byteorder" version = "1.3.4" @@ -193,6 +201,15 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634" +[[package]] +name = "crc" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d663548de7f5cca343f1e0a48d14dcfb0e9eb4e079ec58883b7251539fa10aeb" +dependencies = [ + "build_const", +] + [[package]] name = "darling" version = "0.10.2" @@ -384,6 +401,12 @@ dependencies = [ "libc", ] +[[package]] +name = "hex" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "644f9158b2f133fd50f5fb3242878846d9eb792e445c893805ff0e3824006e35" + [[package]] name = "humantime" version = "2.0.1" diff --git a/blflash/Cargo.toml b/blflash/Cargo.toml index 84d4b77..3d2295b 100644 --- a/blflash/Cargo.toml +++ b/blflash/Cargo.toml @@ -26,3 +26,5 @@ byteorder = "1.3.4" sha2 = "0.9.2" structopt = { version = "0.3.21", features = ["paw"] } paw = "1.0.0" +crc = "1.8.1" +hex = "0.4.2" diff --git a/blflash/src/chip/bl602/cfg/partition_cfg_2M.toml b/blflash/src/chip/bl602/cfg/partition_cfg_2M.toml new file mode 100644 index 0000000..29de67e --- /dev/null +++ b/blflash/src/chip/bl602/cfg/partition_cfg_2M.toml @@ -0,0 +1,94 @@ +[pt_table] +#partition table is 4K in size +address0 = 0xE000 +address1 = 0xF000 + +[[pt_entry]] +type = 0 +name = "FW" +device = 0 +address0 = 0x10000 +size0 = 0xC8000 +address1 = 0xD8000 +size1 = 0x88000 +# compressed image must set len,normal image can left it to 0 +len = 0 + +[[pt_entry]] +type = 2 +name = "mfg" +device = 0 +address0 = 0x160000 +size0 = 0x32000 +address1 = 0 +size1 = 0 +# compressed image must set len,normal image can left it to 0 +len = 0 + +[[pt_entry]] +type = 3 +name = "media" +device = 0 +address0 = 0x192000 +size0 = 0x57000 +address1 = 0 +size1 = 0 +# compressed image must set len,normal image can left it to 0 +len = 0 + +[[pt_entry]] +type = 4 +name = "PSM" +device = 0 +address0 = 0x1E9000 +size0 = 0x8000 +address1 = 0 +size1 = 0 +# compressed image must set len,normal image can left it to 0 +len = 0 + +[[pt_entry]] +type = 5 +name = "KEY" +device = 0 +address0 = 0x1F1000 +size0 = 0x2000 +address1 = 0 +size1 = 0 +# compressed image must set len,normal image can left it to 0 +len = 0 + +[[pt_entry]] +type = 6 +name = "DATA" +device = 0 +address0 = 0x1F3000 +size0 = 0x5000 +address1 = 0 +size1 = 0 +# compressed image must set len,normal image can left it to 0 +len = 0 + + +[[pt_entry]] +type = 7 +name = "factory" +device = 0 +address0 = 0x1F8000 +size0 = 0x7000 +address1 = 0 +size1 = 0 +# compressed image must set len,normal image can left it to 0 +len = 0 + +#if user want to put RF calibration data on flash, uncomment following pt entry +#[[pt_entry]] +#type = 8 +#name = "rf_para" +#device = 0 +#address0 = 0x1FF000 +#size0 = 0x1000 +#address1 = 0 +#size1 = 0 +## compressed image must set len,normal image can left it to 0 +#len = 0 diff --git a/blflash/src/chip/bl602/image/boot2image.bin b/blflash/src/chip/bl602/image/boot2image.bin new file mode 100644 index 0000000000000000000000000000000000000000..24a3771990a7c2ae7634d01e454dd6221ab9d69a GIT binary patch literal 47504 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 literal 0 HcmV?d00001 diff --git a/blflash/src/chip/bl602/mod.rs b/blflash/src/chip/bl602/mod.rs index a9e19f5..9b20a9a 100644 --- a/blflash/src/chip/bl602/mod.rs +++ b/blflash/src/chip/bl602/mod.rs @@ -1,16 +1,38 @@ use super::{Chip, CodeSegment, FlashSegment}; +use crate::{Error, image::PartitionCfg}; +use deku::prelude::*; -const EFLASH_LOADER: &'static [u8] = include_bytes!("image/eflash_loader_40m.bin"); +pub const DEFAULT_PARTITION_CFG: &'static [u8] = include_bytes!("cfg/partition_cfg_2M.toml"); +pub const BOOT2IMAGE: &'static [u8] = include_bytes!("image/boot2image.bin"); +pub const EFLASH_LOADER: &'static [u8] = include_bytes!("image/eflash_loader_40m.bin"); const ROM_START: u32 = 0x23000000; // 16MB const ROM_END: u32 = 0x23000000 + 0x1000000; +#[derive(Copy, Clone)] pub struct Bl602; impl Bl602 { fn addr_is_flash(&self, addr: u32) -> bool { addr >= ROM_START && addr < ROM_END } + pub fn with_boot2( + &self, + mut partition_cfg: PartitionCfg, + bin: &[u8], + ) -> Result, Error> { + partition_cfg.update()?; + let partition_cfg = partition_cfg.to_bytes()?; + + let segments = vec![ + CodeSegment::from_slice(0x0, &BOOT2IMAGE), + CodeSegment::from_slice(0xe000, &partition_cfg), + CodeSegment::from_slice(0xf000, &partition_cfg), + // CodeSegment::from_slice(0x10000, &c), + // CodeSegment::from_slice(0x1f8000, &d), + ]; + todo!() + } } impl Chip for Bl602 { diff --git a/blflash/src/chip/mod.rs b/blflash/src/chip/mod.rs index ff509dc..9e1b916 100644 --- a/blflash/src/chip/mod.rs +++ b/blflash/src/chip/mod.rs @@ -1,4 +1,4 @@ -mod bl602; +pub mod bl602; pub use bl602::Bl602; pub use crate::elf::{FirmwareImage, CodeSegment}; pub use crate::flasher::FlashSegment; diff --git a/blflash/src/error.rs b/blflash/src/error.rs index 900baf7..9bcec8c 100644 --- a/blflash/src/error.rs +++ b/blflash/src/error.rs @@ -25,6 +25,8 @@ pub enum Error { RomError(RomError), #[error("Parse error")] ParseError(#[from] deku::error::DekuError), + #[error("Parse toml error")] + TomlError(#[from] toml::de::Error), } impl From for Error { diff --git a/blflash/src/flasher.rs b/blflash/src/flasher.rs index 87bee25..8ce3fc0 100644 --- a/blflash/src/flasher.rs +++ b/blflash/src/flasher.rs @@ -37,13 +37,14 @@ pub struct Flasher { impl Flasher { pub fn connect( + chip: impl Chip + 'static, serial: impl SerialPort + 'static, speed: Option, ) -> Result { let mut flasher = Flasher { connection: Connection::new(serial), boot_info: protocol::BootInfo::default(), - chip: Box::new(Bl602), + chip: Box::new(chip), }; flasher.connection.set_baud(speed.unwrap_or(DEFAULT_BAUDRATE))?; flasher.start_connection()?; diff --git a/blflash/src/image.rs b/blflash/src/image.rs deleted file mode 100644 index e69de29..0000000 diff --git a/blflash/src/image/mod.rs b/blflash/src/image/mod.rs new file mode 100644 index 0000000..a1d95ba --- /dev/null +++ b/blflash/src/image/mod.rs @@ -0,0 +1,3 @@ +mod partition; + +pub use partition::PartitionCfg; \ No newline at end of file diff --git a/blflash/src/image/partition.rs b/blflash/src/image/partition.rs new file mode 100644 index 0000000..0a0c19c --- /dev/null +++ b/blflash/src/image/partition.rs @@ -0,0 +1,75 @@ +use serde::Deserialize; +use deku::prelude::*; +use std::iter; +use std::io::Write; + + +#[derive(Debug, Deserialize, DekuWrite, Default)] +#[deku(magic = b"\x42\x46\x50\x54\x00\x00")] +pub struct PartitionCfg { + #[serde(skip)] + #[deku(update = "self.pt_entry.len()")] + pub entry_len: u32, + #[serde(skip)] + #[deku(update = "0")] + _unused1: u16, + #[serde(skip)] + #[deku(update = "self.header_checksum()")] + pub checksum: u32, + #[deku(skip)] + pub pt_table: Table, + pub pt_entry: Vec, + #[serde(skip)] + #[deku(update = "self.checksum()")] + pub file_checksum: u32, +} + +#[derive(Debug, Deserialize, DekuWrite, Default)] +pub struct Table { + pub address0: u32, + pub address1: u32, +} + +#[derive(Debug, Deserialize, DekuWrite, Default)] +pub struct Entry { + #[deku(bytes = "3")] + pub r#type: u32, + #[deku(writer = "Entry::write_name(name, output)")] + pub name: String, + pub address0: u32, + pub address1: u32, + pub size0: u32, + pub size1: u32, + pub len: u32, + #[serde(skip)] + pub _unused1: u32, +} + +impl PartitionCfg { + fn header_checksum(&self) -> u32 { + let data = self.to_bytes().unwrap(); + crc::crc32::checksum_ieee(&data[0..12]) + } + fn checksum(&self) -> u32 { + let data = self.to_bytes().unwrap(); + crc::crc32::checksum_ieee(&data[16..16+36*self.pt_entry.len()]) + } +} + +impl Entry { + fn write_name( + name: &str, + output: &mut BitVec, + ) -> Result<(), DekuError> { + if name.len() > 8 { + return Err(DekuError::Unexpected("name too long".to_string())) + } + let bytes = name + .bytes() + .chain(iter::repeat(0)) + .take(8 + 1) // last is null? + .collect::>(); + output.write_all(&bytes).unwrap(); + Ok(()) + } +} diff --git a/blflash/src/lib.rs b/blflash/src/lib.rs index 60dec7b..449b49e 100644 --- a/blflash/src/lib.rs +++ b/blflash/src/lib.rs @@ -1,7 +1,7 @@ mod config; mod flasher; mod connection; -mod chip; +pub mod chip; mod error; mod image; pub mod elf; diff --git a/blflash/src/main.rs b/blflash/src/main.rs index 6453ba5..8d40b52 100644 --- a/blflash/src/main.rs +++ b/blflash/src/main.rs @@ -1,6 +1,6 @@ use std::fs::read; -use blflash::{Config, Flasher, Error}; +use blflash::{Config, Flasher, Error, chip::bl602::{self, Bl602}}; use main_error::MainError; use serial::BaudRate; use env_logger::Env; @@ -15,6 +15,11 @@ struct FlashOpt { /// Bin file #[structopt(parse(from_os_str))] image: PathBuf, + /// Path to partition_cfg.toml, default to be partition/partition_cfg_2M.toml + partition_cfg: Option, + /// With boot2 + #[structopt(short, long)] + without_boot2: bool, } #[derive(StructOpt)] @@ -37,34 +42,59 @@ enum Opt { fn flash(opt: FlashOpt) -> Result<(), Error> { let serial = serial::open(&opt.port)?; - let mut flasher = Flasher::connect(serial, Some(BaudRate::Baud115200))?; + let chip = Bl602; - log::info!("Bootrom version: {}", flasher.boot_info().bootrom_version); - log::trace!("Boot info: {:x?}", flasher.boot_info()); - // use blflash::elf::CodeSegment; - // let a = read("image/boot2image.bin")?; - // let b = read("image/partition.bin")?; - // let c = read("image/fwimage.bin")?; - // let d = read("image/ro_params.dtb")?; - // let segments = vec![ - // CodeSegment::from_slice(0x0, &a), - // CodeSegment::from_slice(0xe000, &b), - // CodeSegment::from_slice(0xf000, &b), - // CodeSegment::from_slice(0x10000, &c), - // CodeSegment::from_slice(0x1f8000, &d), - // ]; - // flasher.load_segments(segments.into_iter())?; - let input_bytes = read(&opt.image)?; - flasher.load_elf_to_flash(&input_bytes)?; + if !opt.without_boot2 { + let partition_cfg = opt + .partition_cfg + .map(read) + .unwrap_or_else(|| Ok(bl602::DEFAULT_PARTITION_CFG.to_vec()))?; + let partition_cfg = toml::from_slice(&partition_cfg)?; + + let bin = read(&opt.image)?; + let segments = chip.with_boot2( + partition_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()); + + flasher.load_segments(segments.into_iter())?; + + 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()); + + let input_bytes = read(&opt.image)?; + flasher.load_elf_to_flash(&input_bytes)?; + + flasher.reset()?; + } - flasher.reset()?; log::info!("Success"); Ok(()) } fn check(opt: CheckOpt) -> Result<(), Error> { let serial = serial::open(&opt.port)?; - let mut flasher = Flasher::connect(serial, Some(BaudRate::Baud115200))?; + let mut flasher = Flasher::connect( + Bl602, + serial, + Some(BaudRate::Baud115200), + )?; log::info!("Bootrom version: {}", flasher.boot_info().bootrom_version); log::trace!("Boot info: {:x?}", flasher.boot_info());