mirror of
https://github.com/spacemeowx2/blflash.git
synced 2025-02-22 06:41:18 +00:00
feat: implement cargo-blflash
This commit is contained in:
parent
23742b5c98
commit
c2d38374e9
214
Cargo.lock
generated
214
Cargo.lock
generated
@ -133,9 +133,12 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"blflash",
|
||||
"cargo-project",
|
||||
"color-eyre",
|
||||
"env_logger",
|
||||
"main_error",
|
||||
"pico-args",
|
||||
"paw",
|
||||
"serial",
|
||||
"structopt",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -153,6 +156,12 @@ dependencies = [
|
||||
"toml 0.4.10",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.66"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "0.1.10"
|
||||
@ -180,6 +189,33 @@ dependencies = [
|
||||
"vec_map",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "color-eyre"
|
||||
version = "0.5.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b29030875fd8376e4a28ef497790d5b4a7843d8d1396bf08ce46f5eec562c5c"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"color-spantrace",
|
||||
"eyre",
|
||||
"indenter",
|
||||
"once_cell",
|
||||
"owo-colors",
|
||||
"tracing-error",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "color-spantrace"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6eee477a4a8a72f4addd4de416eb56d54bc307b284d6601bafdee1f4ea462d1"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"owo-colors",
|
||||
"tracing-core",
|
||||
"tracing-error",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "console"
|
||||
version = "0.13.0"
|
||||
@ -317,6 +353,16 @@ dependencies = [
|
||||
"termcolor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "eyre"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f29abf4740a4778632fe27a4f681ef5b7a6f659aeba3330ac66f48e20cfa3b7"
|
||||
dependencies = [
|
||||
"indenter",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "failure"
|
||||
version = "0.1.8"
|
||||
@ -351,6 +397,19 @@ version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ba62103ce691c2fd80fbae2213dfdda9ce60804973ac6b6e97de818ea7f52c8"
|
||||
|
||||
[[package]]
|
||||
name = "generator"
|
||||
version = "0.6.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8cdc09201b2e8ca1b19290cf7e65de2246b8e91fb6874279722189c4de7b94dc"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"log",
|
||||
"rustc_version",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.4"
|
||||
@ -420,6 +479,12 @@ version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||
|
||||
[[package]]
|
||||
name = "indenter"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68b772bb6a163b53b7e02b2d4735e0b399df6fc68e74374dbea85cd22a36e9db"
|
||||
|
||||
[[package]]
|
||||
name = "indicatif"
|
||||
version = "0.15.0"
|
||||
@ -441,6 +506,12 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "0.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
@ -462,6 +533,19 @@ dependencies = [
|
||||
"cfg-if 0.1.10",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "loom"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a0e8460f2f2121162705187214720353c517b97bdfb3494c0b1e33d83ebe4bed"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.10",
|
||||
"generator",
|
||||
"scoped-tls",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "main_error"
|
||||
version = "0.1.1"
|
||||
@ -505,12 +589,24 @@ version = "0.22.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8d3b63360ec3cb337817c2dbd47ab4a0f170d285d8e5a2064600f3def1402397"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0"
|
||||
|
||||
[[package]]
|
||||
name = "opaque-debug"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
|
||||
|
||||
[[package]]
|
||||
name = "owo-colors"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13370dae44474229701bb69b90b4f4dca6404cb0357a2d50d635f1171dc3aa7b"
|
||||
|
||||
[[package]]
|
||||
name = "parse_int"
|
||||
version = "0.4.0"
|
||||
@ -548,10 +644,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f0b59668fe80c5afe998f0c0bf93322bf2cd66cafeeb80581f291716f3467f2"
|
||||
|
||||
[[package]]
|
||||
name = "pico-args"
|
||||
version = "0.3.4"
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28b9b4df73455c861d7cbf8be42f01d3b373ed7f02e378d55fa84eafc6f638b1"
|
||||
checksum = "6b063f57ec186e6140e2b8b6921e5f1bd89c7356dda5b33acc5401203ca6131c"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error"
|
||||
@ -650,6 +746,42 @@ version = "0.1.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e3bad0ee36814ca07d7968269dd4b7ec89ec2da10c4bb613928d3077083c232"
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
|
||||
dependencies = [
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
|
||||
|
||||
[[package]]
|
||||
name = "scoped-tls"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2"
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
|
||||
dependencies = [
|
||||
"semver-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver-parser"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.117"
|
||||
@ -670,6 +802,17 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.61"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fceb2595057b6891a4ee808f70054bd2d12f0e97f1cbb78689b59f676df325a"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serial"
|
||||
version = "0.4.0"
|
||||
@ -725,6 +868,16 @@ dependencies = [
|
||||
"opaque-debug",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sharded-slab"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b4921be914e16899a80adefb821f8ddb7974e3f1250223575a44ed994882127"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"loom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.8.0"
|
||||
@ -875,6 +1028,59 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing"
|
||||
version = "0.1.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f47026cdc4080c07e49b37087de021820269d996f581aac150ef9e5583eefe3"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"pin-project-lite",
|
||||
"tracing-attributes",
|
||||
"tracing-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-attributes"
|
||||
version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "80e0ccfc3378da0cce270c946b676a376943f5cd16aeba64568e7939806f4ada"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-core"
|
||||
version = "0.1.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f50de3927f93d202783f4513cda820ab47ef17f624b03c096e86ef00c67e6b5f"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-error"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4d7c0b83d4a500748fa5879461652b361edf5c9d51ede2a2ac03875ca185e24"
|
||||
dependencies = [
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-subscriber"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1fa8f0c8f4c594e4fc9debc1990deab13238077271ba84dd853d54902ee3401"
|
||||
dependencies = [
|
||||
"sharded-slab",
|
||||
"thread_local",
|
||||
"tracing-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.12.0"
|
||||
|
@ -24,6 +24,10 @@ impl Bl602 {
|
||||
}
|
||||
|
||||
impl Chip for Bl602 {
|
||||
fn target(&self) -> &'static str {
|
||||
"riscv32imac-unknown-none-elf"
|
||||
}
|
||||
|
||||
fn get_eflash_loader(&self) -> &[u8] {
|
||||
EFLASH_LOADER
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ use crate::Error;
|
||||
pub use bl602::Bl602;
|
||||
|
||||
pub trait Chip {
|
||||
fn target(&self) -> &'static str;
|
||||
fn get_eflash_loader(&self) -> &[u8];
|
||||
fn get_flash_segment<'a>(&self, code_segment: CodeSegment<'a>) -> Option<RomSegment<'a>>;
|
||||
fn with_boot2(
|
||||
|
@ -1,34 +0,0 @@
|
||||
use directories_next::ProjectDirs;
|
||||
use serde::Deserialize;
|
||||
use std::fs::read;
|
||||
|
||||
#[derive(Debug, Deserialize, Default)]
|
||||
pub struct Config {
|
||||
#[serde(default)]
|
||||
pub connection: Connection,
|
||||
#[serde(default)]
|
||||
pub build: Build,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Default)]
|
||||
pub struct Connection {
|
||||
pub serial: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Default)]
|
||||
pub struct Build {
|
||||
pub tool: Option<String>,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
/// Load the config from config file
|
||||
pub fn load() -> Self {
|
||||
let dirs = ProjectDirs::from("rs", "bl", "blflash").unwrap();
|
||||
let file = dirs.config_dir().join("blflash.toml");
|
||||
if let Ok(data) = read(&file) {
|
||||
toml::from_slice(&data).unwrap()
|
||||
} else {
|
||||
Self::default()
|
||||
}
|
||||
}
|
||||
}
|
@ -1,11 +1,240 @@
|
||||
pub mod chip;
|
||||
mod config;
|
||||
mod connection;
|
||||
pub mod elf;
|
||||
mod error;
|
||||
mod flasher;
|
||||
pub mod image;
|
||||
|
||||
pub use config::Config;
|
||||
pub use error::{Error, RomError};
|
||||
pub use flasher::Flasher;
|
||||
|
||||
use crate::{
|
||||
chip::{
|
||||
bl602::{self, Bl602},
|
||||
Chip,
|
||||
},
|
||||
elf::{FirmwareImage, RomSegment},
|
||||
image::BootHeaderCfgFile,
|
||||
};
|
||||
use serial::{BaudRate, CharSize, FlowControl, Parity, SerialPort, SerialPortSettings, StopBits};
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
fs::{read, File},
|
||||
path::PathBuf,
|
||||
};
|
||||
use structopt::StructOpt;
|
||||
|
||||
#[derive(StructOpt)]
|
||||
pub struct Connection {
|
||||
/// Serial port
|
||||
#[structopt(short, long)]
|
||||
pub port: String,
|
||||
/// Flash baud rate
|
||||
#[structopt(short, long, default_value = "1000000")]
|
||||
pub baud_rate: usize,
|
||||
/// Initial baud rate
|
||||
#[structopt(long, default_value = "115200")]
|
||||
pub initial_baud_rate: usize,
|
||||
}
|
||||
|
||||
#[derive(StructOpt)]
|
||||
pub struct Boot2Opt {
|
||||
/// Path to partition_cfg.toml, default to be partition/partition_cfg_2M.toml
|
||||
#[structopt(long, parse(from_os_str))]
|
||||
pub partition_cfg: Option<PathBuf>,
|
||||
/// Path to efuse_bootheader_cfg.conf
|
||||
#[structopt(long, parse(from_os_str))]
|
||||
pub boot_header_cfg: Option<PathBuf>,
|
||||
/// Path to ro_params.dtb
|
||||
#[structopt(long, parse(from_os_str))]
|
||||
pub dtb: Option<PathBuf>,
|
||||
/// Without boot2
|
||||
#[structopt(short, long)]
|
||||
pub without_boot2: bool,
|
||||
}
|
||||
|
||||
#[derive(StructOpt)]
|
||||
pub struct FlashOpt {
|
||||
#[structopt(flatten)]
|
||||
pub conn: Connection,
|
||||
/// Bin file
|
||||
#[structopt(parse(from_os_str))]
|
||||
pub image: PathBuf,
|
||||
/// Don't skip if hash matches
|
||||
#[structopt(short, long)]
|
||||
pub force: bool,
|
||||
#[structopt(flatten)]
|
||||
pub boot: Boot2Opt,
|
||||
}
|
||||
|
||||
#[derive(StructOpt)]
|
||||
pub struct CheckOpt {
|
||||
#[structopt(flatten)]
|
||||
pub conn: Connection,
|
||||
/// Bin file
|
||||
#[structopt(parse(from_os_str))]
|
||||
pub image: PathBuf,
|
||||
#[structopt(flatten)]
|
||||
pub boot: Boot2Opt,
|
||||
}
|
||||
|
||||
#[derive(StructOpt)]
|
||||
pub struct DumpOpt {
|
||||
#[structopt(flatten)]
|
||||
pub conn: Connection,
|
||||
/// Output file
|
||||
#[structopt(parse(from_os_str))]
|
||||
pub output: PathBuf,
|
||||
/// start address
|
||||
#[structopt(parse(try_from_str = parse_int::parse), default_value = "0")]
|
||||
pub start: u32,
|
||||
/// end address
|
||||
#[structopt(parse(try_from_str = parse_int::parse), default_value = "0x100000")]
|
||||
pub end: u32,
|
||||
}
|
||||
|
||||
#[derive(StructOpt)]
|
||||
pub 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 {
|
||||
pub fn open_serial(&self) -> Result<impl SerialPort, Error> {
|
||||
let mut serial = serial::open(&self.port)?;
|
||||
serial.reconfigure(&|setup: &mut dyn SerialPortSettings| {
|
||||
setup.set_char_size(CharSize::Bits8);
|
||||
setup.set_stop_bits(StopBits::Stop1);
|
||||
setup.set_parity(Parity::ParityNone);
|
||||
setup.set_flow_control(FlowControl::FlowNone);
|
||||
Ok(())
|
||||
})?;
|
||||
Ok(serial)
|
||||
}
|
||||
pub fn create_flasher(&self, chip: impl Chip + 'static) -> Result<Flasher, Error> {
|
||||
let serial = self.open_serial()?;
|
||||
Flasher::connect(
|
||||
chip,
|
||||
serial,
|
||||
BaudRate::from_speed(self.initial_baud_rate),
|
||||
BaudRate::from_speed(self.baud_rate),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Boot2Opt {
|
||||
pub fn with_boot2<'a>(
|
||||
self,
|
||||
chip: &'a dyn Chip,
|
||||
image: &[u8],
|
||||
) -> Result<Vec<RomSegment<'a>>, Error> {
|
||||
let partition_cfg = self
|
||||
.partition_cfg
|
||||
.map(read)
|
||||
.unwrap_or_else(|| Ok(bl602::DEFAULT_PARTITION_CFG.to_vec()))?;
|
||||
let boot_header_cfg = self
|
||||
.boot_header_cfg
|
||||
.map(read)
|
||||
.unwrap_or_else(|| Ok(bl602::DEFAULT_BOOTHEADER_CFG.to_vec()))?;
|
||||
let partition_cfg = toml::from_slice(&partition_cfg)?;
|
||||
let BootHeaderCfgFile { boot_header_cfg } = toml::from_slice(&boot_header_cfg)?;
|
||||
let ro_params = self
|
||||
.dtb
|
||||
.map(read)
|
||||
.unwrap_or_else(|| Ok(bl602::RO_PARAMS.to_vec()))?;
|
||||
|
||||
let segments = chip.with_boot2(partition_cfg, boot_header_cfg, ro_params, image)?;
|
||||
|
||||
Ok(segments)
|
||||
}
|
||||
pub fn make_segment<'a>(
|
||||
self,
|
||||
_chip: &'a dyn Chip,
|
||||
image: Vec<u8>,
|
||||
) -> Result<RomSegment<'a>, Error> {
|
||||
let boot_header_cfg = self
|
||||
.boot_header_cfg
|
||||
.map(read)
|
||||
.unwrap_or_else(|| Ok(bl602::DEFAULT_BOOTHEADER_CFG.to_vec()))?;
|
||||
let BootHeaderCfgFile {
|
||||
mut boot_header_cfg,
|
||||
} = toml::from_slice(&boot_header_cfg)?;
|
||||
let img = boot_header_cfg.make_image(0x2000, image)?;
|
||||
|
||||
Ok(RomSegment::from_vec(0x0, img))
|
||||
}
|
||||
pub fn get_segments<'a>(
|
||||
self,
|
||||
chip: &'a dyn Chip,
|
||||
image: Vec<u8>,
|
||||
) -> Result<Vec<RomSegment<'a>>, Error> {
|
||||
Ok(if self.without_boot2 {
|
||||
vec![self.make_segment(chip, Vec::from(image))?]
|
||||
} else {
|
||||
self.with_boot2(chip, &image)?
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_image<'a>(chip: &dyn Chip, image: &'a [u8]) -> Result<Cow<'a, [u8]>, Error> {
|
||||
Ok(if image[0..4] == [0x7f, 0x45, 0x4c, 0x46] {
|
||||
log::trace!("Detect ELF");
|
||||
// ELF
|
||||
let firmware_image = FirmwareImage::from_data(image).map_err(|_| Error::InvalidElf)?;
|
||||
Cow::Owned(firmware_image.to_flash_bin(chip))
|
||||
} else {
|
||||
// bin
|
||||
Cow::Borrowed(image)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn flash(opt: FlashOpt) -> Result<(), Error> {
|
||||
let chip = Bl602;
|
||||
let image = read(&opt.image)?;
|
||||
let image = read_image(&chip, &image)?;
|
||||
|
||||
let mut flasher = opt.conn.create_flasher(chip)?;
|
||||
log::info!("Bootrom version: {}", flasher.boot_info().bootrom_version);
|
||||
log::trace!("Boot info: {:x?}", flasher.boot_info());
|
||||
|
||||
let segments = opt.boot.get_segments(&chip, Vec::from(image))?;
|
||||
flasher.load_segments(opt.force, segments.into_iter())?;
|
||||
flasher.reset()?;
|
||||
|
||||
log::info!("Success");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn check(opt: CheckOpt) -> Result<(), Error> {
|
||||
let chip = Bl602;
|
||||
let image = read(&opt.image)?;
|
||||
let image = read_image(&chip, &image)?;
|
||||
|
||||
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 segments = opt.boot.get_segments(&chip, Vec::from(image))?;
|
||||
flasher.check_segments(segments.into_iter())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn dump(opt: DumpOpt) -> Result<(), Error> {
|
||||
let mut output = File::create(opt.output)?;
|
||||
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());
|
||||
|
||||
flasher.dump_flash(opt.start..opt.end, &mut output)?;
|
||||
|
||||
log::info!("Success");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1,243 +1,12 @@
|
||||
use blflash::{
|
||||
chip::{
|
||||
bl602::{self, Bl602},
|
||||
Chip,
|
||||
},
|
||||
elf::{FirmwareImage, RomSegment},
|
||||
image::BootHeaderCfgFile,
|
||||
Config, Error, Flasher,
|
||||
};
|
||||
use blflash::{check, dump, flash, Opt};
|
||||
use env_logger::Env;
|
||||
use main_error::MainError;
|
||||
use serial::{BaudRate, CharSize, FlowControl, Parity, SerialPort, SerialPortSettings, StopBits};
|
||||
use std::path::PathBuf;
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
fs::{read, File},
|
||||
};
|
||||
use structopt::StructOpt;
|
||||
|
||||
#[derive(StructOpt)]
|
||||
struct Connection {
|
||||
/// Serial port
|
||||
#[structopt(short, long)]
|
||||
port: String,
|
||||
/// Flash baud rate
|
||||
#[structopt(short, long, default_value = "1000000")]
|
||||
baud_rate: usize,
|
||||
/// Initial baud rate
|
||||
#[structopt(long, default_value = "115200")]
|
||||
initial_baud_rate: usize,
|
||||
}
|
||||
|
||||
#[derive(StructOpt)]
|
||||
struct Boot2Opt {
|
||||
/// Path to partition_cfg.toml, default to be partition/partition_cfg_2M.toml
|
||||
#[structopt(long, parse(from_os_str))]
|
||||
partition_cfg: Option<PathBuf>,
|
||||
/// Path to efuse_bootheader_cfg.conf
|
||||
#[structopt(long, parse(from_os_str))]
|
||||
boot_header_cfg: Option<PathBuf>,
|
||||
/// Path to ro_params.dtb
|
||||
#[structopt(long, parse(from_os_str))]
|
||||
dtb: Option<PathBuf>,
|
||||
/// Without boot2
|
||||
#[structopt(short, long)]
|
||||
without_boot2: bool,
|
||||
}
|
||||
|
||||
#[derive(StructOpt)]
|
||||
struct FlashOpt {
|
||||
#[structopt(flatten)]
|
||||
conn: Connection,
|
||||
/// Bin file
|
||||
#[structopt(parse(from_os_str))]
|
||||
image: PathBuf,
|
||||
/// Don't skip if hash matches
|
||||
#[structopt(short, long)]
|
||||
force: bool,
|
||||
#[structopt(flatten)]
|
||||
boot: Boot2Opt,
|
||||
}
|
||||
|
||||
#[derive(StructOpt)]
|
||||
struct CheckOpt {
|
||||
#[structopt(flatten)]
|
||||
conn: Connection,
|
||||
/// Bin file
|
||||
#[structopt(parse(from_os_str))]
|
||||
image: PathBuf,
|
||||
#[structopt(flatten)]
|
||||
boot: Boot2Opt,
|
||||
}
|
||||
|
||||
#[derive(StructOpt)]
|
||||
struct DumpOpt {
|
||||
#[structopt(flatten)]
|
||||
conn: Connection,
|
||||
/// Output file
|
||||
#[structopt(parse(from_os_str))]
|
||||
output: PathBuf,
|
||||
/// start address
|
||||
#[structopt(parse(try_from_str = parse_int::parse), default_value = "0")]
|
||||
start: u32,
|
||||
/// end address
|
||||
#[structopt(parse(try_from_str = parse_int::parse), default_value = "0x100000")]
|
||||
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<impl SerialPort, Error> {
|
||||
let mut serial = serial::open(&self.port)?;
|
||||
serial.reconfigure(&|setup: &mut dyn SerialPortSettings| {
|
||||
setup.set_char_size(CharSize::Bits8);
|
||||
setup.set_stop_bits(StopBits::Stop1);
|
||||
setup.set_parity(Parity::ParityNone);
|
||||
setup.set_flow_control(FlowControl::FlowNone);
|
||||
Ok(())
|
||||
})?;
|
||||
Ok(serial)
|
||||
}
|
||||
fn create_flasher(&self, chip: impl Chip + 'static) -> Result<Flasher, Error> {
|
||||
let serial = self.open_serial()?;
|
||||
Flasher::connect(
|
||||
chip,
|
||||
serial,
|
||||
BaudRate::from_speed(self.initial_baud_rate),
|
||||
BaudRate::from_speed(self.baud_rate),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Boot2Opt {
|
||||
fn with_boot2<'a>(
|
||||
self,
|
||||
chip: &'a dyn Chip,
|
||||
image: &[u8],
|
||||
) -> Result<Vec<RomSegment<'a>>, Error> {
|
||||
let partition_cfg = self
|
||||
.partition_cfg
|
||||
.map(read)
|
||||
.unwrap_or_else(|| Ok(bl602::DEFAULT_PARTITION_CFG.to_vec()))?;
|
||||
let boot_header_cfg = self
|
||||
.boot_header_cfg
|
||||
.map(read)
|
||||
.unwrap_or_else(|| Ok(bl602::DEFAULT_BOOTHEADER_CFG.to_vec()))?;
|
||||
let partition_cfg = toml::from_slice(&partition_cfg)?;
|
||||
let BootHeaderCfgFile { boot_header_cfg } = toml::from_slice(&boot_header_cfg)?;
|
||||
let ro_params = self
|
||||
.dtb
|
||||
.map(read)
|
||||
.unwrap_or_else(|| Ok(bl602::RO_PARAMS.to_vec()))?;
|
||||
|
||||
let segments = chip.with_boot2(partition_cfg, boot_header_cfg, ro_params, image)?;
|
||||
|
||||
Ok(segments)
|
||||
}
|
||||
fn make_segment<'a>(
|
||||
self,
|
||||
_chip: &'a dyn Chip,
|
||||
image: Vec<u8>,
|
||||
) -> Result<RomSegment<'a>, Error> {
|
||||
let boot_header_cfg = self
|
||||
.boot_header_cfg
|
||||
.map(read)
|
||||
.unwrap_or_else(|| Ok(bl602::DEFAULT_BOOTHEADER_CFG.to_vec()))?;
|
||||
let BootHeaderCfgFile {
|
||||
mut boot_header_cfg,
|
||||
} = toml::from_slice(&boot_header_cfg)?;
|
||||
let img = boot_header_cfg.make_image(0x2000, image)?;
|
||||
|
||||
Ok(RomSegment::from_vec(0x0, img))
|
||||
}
|
||||
fn get_segments<'a>(
|
||||
self,
|
||||
chip: &'a dyn Chip,
|
||||
image: Vec<u8>,
|
||||
) -> Result<Vec<RomSegment<'a>>, Error> {
|
||||
Ok(if self.without_boot2 {
|
||||
vec![self.make_segment(chip, Vec::from(image))?]
|
||||
} else {
|
||||
self.with_boot2(chip, &image)?
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn read_image<'a>(chip: &dyn Chip, image: &'a [u8]) -> Result<Cow<'a, [u8]>, Error> {
|
||||
Ok(if image[0..4] == [0x7f, 0x45, 0x4c, 0x46] {
|
||||
log::trace!("Detect ELF");
|
||||
// ELF
|
||||
let firmware_image = FirmwareImage::from_data(image).map_err(|_| Error::InvalidElf)?;
|
||||
Cow::Owned(firmware_image.to_flash_bin(chip))
|
||||
} else {
|
||||
// bin
|
||||
Cow::Borrowed(image)
|
||||
})
|
||||
}
|
||||
|
||||
fn flash(opt: FlashOpt) -> Result<(), Error> {
|
||||
let chip = Bl602;
|
||||
let image = read(&opt.image)?;
|
||||
let image = read_image(&chip, &image)?;
|
||||
|
||||
let mut flasher = opt.conn.create_flasher(chip)?;
|
||||
log::info!("Bootrom version: {}", flasher.boot_info().bootrom_version);
|
||||
log::trace!("Boot info: {:x?}", flasher.boot_info());
|
||||
|
||||
let segments = opt.boot.get_segments(&chip, Vec::from(image))?;
|
||||
flasher.load_segments(opt.force, segments.into_iter())?;
|
||||
flasher.reset()?;
|
||||
|
||||
log::info!("Success");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn check(opt: CheckOpt) -> Result<(), Error> {
|
||||
let chip = Bl602;
|
||||
let image = read(&opt.image)?;
|
||||
let image = read_image(&chip, &image)?;
|
||||
|
||||
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 segments = opt.boot.get_segments(&chip, Vec::from(image))?;
|
||||
flasher.check_segments(segments.into_iter())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn dump(opt: DumpOpt) -> Result<(), Error> {
|
||||
let mut output = File::create(opt.output)?;
|
||||
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());
|
||||
|
||||
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"))
|
||||
.format_timestamp(None)
|
||||
.init();
|
||||
let _config = Config::load();
|
||||
|
||||
match args {
|
||||
Opt::Flash(opt) => flash(opt)?,
|
||||
|
@ -12,5 +12,8 @@ repository = "https://github.com/spacemeowx2/blflash"
|
||||
cargo-project = "0.2.4"
|
||||
blflash = { version = "*", path = "../blflash" }
|
||||
main_error = "0.1.1"
|
||||
pico-args = "0.3.4"
|
||||
serial = "0.4"
|
||||
color-eyre = "0.5.10"
|
||||
structopt = "0.3.21"
|
||||
paw = "1.0.0"
|
||||
env_logger = "0.8.2"
|
||||
|
@ -1,3 +1,147 @@
|
||||
fn main() {
|
||||
println!("Hello, world!");
|
||||
use std::path::PathBuf;
|
||||
use std::process::{exit, Command, ExitStatus, Stdio};
|
||||
|
||||
use blflash::{
|
||||
chip::{Bl602, Chip},
|
||||
flash, Boot2Opt, Connection, FlashOpt,
|
||||
};
|
||||
use cargo_project::{Artifact, Profile, Project};
|
||||
use color_eyre::{Report, Result};
|
||||
use env_logger::Env;
|
||||
use structopt::StructOpt;
|
||||
|
||||
#[derive(StructOpt)]
|
||||
struct BlflashOpt {
|
||||
#[structopt(flatten)]
|
||||
conn: Connection,
|
||||
/// Don't skip if hash matches
|
||||
#[structopt(short, long)]
|
||||
force: bool,
|
||||
#[structopt(flatten)]
|
||||
boot: Boot2Opt,
|
||||
#[structopt(long)]
|
||||
release: bool,
|
||||
#[structopt(long)]
|
||||
example: Option<String>,
|
||||
#[structopt(long)]
|
||||
features: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(StructOpt)]
|
||||
enum Opt {
|
||||
Blflash(BlflashOpt),
|
||||
}
|
||||
|
||||
fn blflash_main(args: BlflashOpt) -> Result<()> {
|
||||
let chip = Bl602;
|
||||
let target = chip.target();
|
||||
|
||||
let status = build(args.release, &args.example, &args.features, target);
|
||||
if !status.success() {
|
||||
exit_with_process_status(status)
|
||||
}
|
||||
|
||||
let path = get_artifact_path(target, args.release, &args.example)
|
||||
.expect("Could not find the build artifact path");
|
||||
|
||||
let flash_opt = FlashOpt {
|
||||
conn: args.conn,
|
||||
image: path,
|
||||
force: args.force,
|
||||
boot: args.boot,
|
||||
};
|
||||
|
||||
flash(flash_opt)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[paw::main]
|
||||
fn main(args: Opt) -> Result<()> {
|
||||
env_logger::Builder::from_env(Env::default().default_filter_or("blflash=trace"))
|
||||
.format_timestamp(None)
|
||||
.init();
|
||||
|
||||
match args {
|
||||
Opt::Blflash(opt) => blflash_main(opt),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_artifact_path(target: &str, release: bool, example: &Option<String>) -> Result<PathBuf> {
|
||||
let project = Project::query(".").unwrap();
|
||||
|
||||
let artifact = match example {
|
||||
Some(example) => Artifact::Example(example.as_str()),
|
||||
None => Artifact::Bin(project.name()),
|
||||
};
|
||||
|
||||
let profile = if release {
|
||||
Profile::Release
|
||||
} else {
|
||||
Profile::Dev
|
||||
};
|
||||
|
||||
let host = "x86_64-unknown-linux-gnu";
|
||||
project
|
||||
.path(artifact, profile, Some(target), host)
|
||||
.map_err(Report::msg)
|
||||
}
|
||||
|
||||
fn build(
|
||||
release: bool,
|
||||
example: &Option<String>,
|
||||
features: &Option<String>,
|
||||
target: &str,
|
||||
) -> ExitStatus {
|
||||
let mut args: Vec<String> = vec![];
|
||||
|
||||
if release {
|
||||
args.push("--release".to_string());
|
||||
}
|
||||
|
||||
match example {
|
||||
Some(example) => {
|
||||
args.push("--example".to_string());
|
||||
args.push(example.to_string());
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
|
||||
match features {
|
||||
Some(features) => {
|
||||
args.push("--features".to_string());
|
||||
args.push(features.to_string());
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
|
||||
let mut command = Command::new("cargo");
|
||||
|
||||
args.push("--target".to_string());
|
||||
args.push(target.to_string());
|
||||
|
||||
command
|
||||
.arg("build")
|
||||
.args(args)
|
||||
.stdout(Stdio::inherit())
|
||||
.stderr(Stdio::inherit())
|
||||
.spawn()
|
||||
.unwrap()
|
||||
.wait()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn exit_with_process_status(status: ExitStatus) -> ! {
|
||||
use std::os::unix::process::ExitStatusExt;
|
||||
let code = status.code().or_else(|| status.signal()).unwrap_or(1);
|
||||
|
||||
exit(code)
|
||||
}
|
||||
|
||||
#[cfg(not(unix))]
|
||||
fn exit_with_process_status(status: ExitStatus) -> ! {
|
||||
let code = status.code().unwrap_or(1);
|
||||
|
||||
exit(code)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user