概述
嵌入式固件刷写涉及多种配置格式的解析:Boot 头(boot0/U-Boot)、分区表(MBR/GPT)、系统配置(sys_config)等。OpenixCLI 的 Config 模块负责解析这些与硬件初始化密切相关的数据结构,为刷写流程提供存储类型、分区信息、DRAM 参数等关键数据。
模块结构
src/config/ ├── mod.rs # 模块导出 ├── boot_header.rs # Boot0/U-Boot 头结构 ├── mbr_parser.rs # MBR 分区表解析 ├── partition.rs # 分区配置解析(INI 格式) └── sys_config.rs # 系统配置解析(DRAM 参数)
|
Boot 头结构解析
Boot0 头结构
Boot0 是全志芯片的第一阶段 bootloader,负责最基本的硬件初始化(如 DRAM)。
pub const BOOT0_MAGIC: &str = "eGON.BT0";
#[repr(C, packed)] #[derive(Debug, Clone, Copy)] pub struct Boot0Header { pub jump_instruction: u32,
pub magic: [u8; 8],
pub check_sum: u32,
pub length: u32,
pub pub_head_size: u32,
pub pub_head_vsn: [u8; 4],
pub ret_addr: u32,
pub run_addr: u32,
pub boot_cpu: u32,
pub platform: [u8; 8], }
|
内存布局(十六进制):
偏移 字段 大小 说明 0x00 jump_instruction 4 ARM 跳转指令 0x04 magic 8 "eGON.BT0" 0x0C check_sum 4 校验和 0x10 length 4 镜像长度 0x14 pub_head_size 4 头大小 0x18 pub_head_vsn 4 版本字符串 0x1C ret_addr 4 返回地址 0x20 run_addr 4 运行地址 0x24 boot_cpu 4 CPU 核编号 0x28 platform 8 平台标识 ...
|
U-Boot 头结构
U-Boot 是第二阶段 bootloader,包含更丰富的硬件配置。
pub const UBOOT_MAGIC: &str = "uboot";
#[repr(C, packed)] #[derive(Debug, Clone, Copy)] pub struct UBootBaseHeader { pub jump_instruction: u32, pub magic: [u8; 8], pub check_sum: u32, pub align_size: u32, pub length: u32, pub uboot_length: u32, pub version: [u8; 8], pub platform: [u8; 8], pub run_addr: u32, }
|
U-Boot 数据头结构
包含 DRAM 参数、GPIO 配置、工作模式等:
#[repr(C, packed)] #[derive(Debug, Clone, Copy)] pub struct UBootDataHeader { pub dram_para: [u32; 32],
pub run_clock: i32,
pub run_core_vol: i32,
pub uart_port: i32,
pub uart_gpio: [UBootNormalGpioCfg; 2],
pub twi_port: i32,
pub twi_gpio: [UBootNormalGpioCfg; 2],
pub work_mode: i32,
pub storage_type: i32, }
|
GPIO 配置结构
#[repr(C, packed)] #[derive(Debug, Clone, Copy)] pub struct UBootNormalGpioCfg { pub port: u8,
pub port_num: u8,
pub mul_sel: u8,
pub pull: u8,
pub drv_level: u8,
pub data: u8,
pub reserved: [u8; 2], }
|
工作模式常量
pub const WORK_MODE_USB_PRODUCT: u32 = 0x10;
pub const BOOT_FILE_MODE_NORMAL: u32 = 0; pub const BOOT_FILE_MODE_TOC: u32 = 1; pub const BOOT_FILE_MODE_RESERVED0: u32 = 2; pub const BOOT_FILE_MODE_RESERVED1: u32 = 3; pub const BOOT_FILE_MODE_PKG: u32 = 4;
pub fn get_sunxi_boot_file_mode_string(mode: u32) -> &'static str { match mode { BOOT_FILE_MODE_NORMAL => "Normal Boot File", BOOT_FILE_MODE_TOC => "TOC Boot File", BOOT_FILE_MODE_RESERVED0 => "Reserved Boot File 0", BOOT_FILE_MODE_RESERVED1 => "Reserved Boot File 1", BOOT_FILE_MODE_PKG => "Boot Package File", _ => "Unknown Boot File Type", } }
|
MBR 分区表解析
MBR 格式常量
pub const MBR_MAGIC: &str = "softw411";
pub const MBR_VERSION: u32 = 0x00000200;
pub const MBR_SIZE: usize = 16 * 1024;
pub const MBR_MAX_PART_CNT: usize = 120;
pub const PART_NAME_MAX_LEN: usize = 16;
pub const EFEX_CRC32_VALID_FLAG: u32 = 0x6a617603;
|
全志 MBR 与标准 MBR 的区别:
| 特性 |
标准 MBR |
全志 MBR |
| 大小 |
512 bytes |
16 KB |
| 最大分区数 |
4(主分区) |
120 |
| 魂数 |
无特定魔数 |
“softw411” |
| 分区名称 |
无 |
16 字符 |
| 支持 64 位地址 |
否(CHS) |
是(addrhi/addrlo) |
SunxiPartitionRaw 结构
#[repr(C, packed)] #[derive(Debug, Clone, Copy)] pub struct SunxiPartitionRaw { pub addrhi: u32,
pub addrlo: u32,
pub lenhi: u32,
pub lenlo: u32,
pub classname: [u8; PART_NAME_MAX_LEN],
pub name: [u8; PART_NAME_MAX_LEN],
pub user_type: u32,
pub keydata: u32,
pub ro: u32,
pub reserved: [u8; PART_SIZE_RES_LEN], }
|
关键方法:
impl SunxiPartitionRaw { pub fn address(&self) -> u64 { ((self.addrhi as u64) << 32) | (self.addrlo as u64) }
pub fn length(&self) -> u64 { ((self.lenhi as u64) << 32) | (self.lenlo as u64) }
pub fn readonly(&self) -> bool { self.ro != 0 }
pub fn name_str(&self) -> String { String::from_utf8_lossy(&self.name) .trim_end_matches('\0') .to_string() } }
|
SunxiMbrRaw 结构
#[repr(C, packed)] #[derive(Debug, Clone, Copy)] pub struct SunxiMbrRaw { pub crc32: u32,
pub version: u32,
pub magic: [u8; 8],
pub copy: u32,
pub index: u32,
pub part_count: u32,
pub stamp: u32,
pub partitions: [SunxiPartitionRaw; MBR_MAX_PART_CNT], }
|
MBR 内存布局
+-------------------------------------------------------------+ | Sunxi MBR Structure (16KB) | +-------------------------------------------------------------+ | Offset 0x00 | | +---------------------------------------------------------+ | | | CRC32 (4 bytes) | | | | Version (4 bytes) | | | | Magic: "softw411" (8 bytes) | | | | Copy (4 bytes) | | | | Index (4 bytes) | | | | Part Count (4 bytes) | | | | Stamp (4 bytes) | | | +---------------------------------------------------------+ | | Offset 0x24 | | +---------------------------------------------------------+ | | | Partition 0 (128 bytes) | | | | - addrhi, addrlo (8 bytes) | | | | - lenhi, lenlo (8 bytes) | | | | - classname (16 bytes) | | | | - name (16 bytes) | | | | - user_type, keydata, ro (12 bytes) | | | | - reserved (68 bytes) | | | +---------------------------------------------------------+ | | Offset 0xA4 | | +---------------------------------------------------------+ | | | Partition 1 (128 bytes) | | | +---------------------------------------------------------+ | | ... | | Offset 0xA4 + (119 * 128) = 0x3E44 | | +---------------------------------------------------------+ | | | Partition 119 (128 bytes) | | | +---------------------------------------------------------+ | | Offset 0x3FC4 - 0x4000 | | +---------------------------------------------------------+ | | | Padding/Unused | | | +---------------------------------------------------------+ | +-------------------------------------------------------------+
|
SunxiMbr 解析结构
#[derive(Debug, Clone)] pub struct SunxiMbr { pub crc32: u32, pub version: u32, pub magic: String, pub copy: u32, pub index: u32, pub part_count: u32, pub stamp: u32, pub partitions: Vec<SunxiPartition>, }
impl SunxiMbr { pub fn parse(data: &[u8]) -> Result<Self, &'static str> { let raw = SunxiMbrRaw::parse(data)?;
let mut partitions = Vec::with_capacity(raw.part_count as usize); for i in 0..raw.part_count as usize { let partition = SunxiPartition::from_raw(&raw.partitions[i]); partitions.push(partition); }
Ok(Self { crc32: raw.crc32, version: raw.version, magic: raw.magic_str(), copy: raw.copy, index: raw.index, part_count: raw.part_count, stamp: raw.stamp, partitions, }) }
pub fn to_mbr_info(&self) -> MbrInfo { MbrInfo { part_count: self.part_count, partitions: self.partitions.clone(), } } }
#[derive(Debug, Clone)] pub struct MbrInfo { pub part_count: u32, pub partitions: Vec<SunxiPartition>, }
|
分区配置解析(INI 格式)
PartitionConfig 结构
#[derive(Debug, Clone, Default)] pub struct PartitionConfig { pub name: String,
pub size: u64,
pub downloadfile: String,
pub user_type: u32,
pub keydata: bool,
pub encrypt: bool,
pub verify: bool,
pub readonly: bool, }
|
OpenixPartition 解析器
pub struct OpenixPartition { partitions: Vec<PartitionConfig>, }
impl OpenixPartition { pub fn parse_from_data(&mut self, data: &[u8]) -> bool { let content = String::from_utf8_lossy(data); self.parse_from_content(&content) }
fn parse_from_content(&mut self, content: &str) -> bool { self.partitions.clear();
let mut in_partition_section = false; let mut current_partition = PartitionConfig::default();
for line in content.lines() { let line = line.trim();
if line.is_empty() || line.starts_with(';') || line.starts_with("//") { continue; }
if line == "[partition_start]" { in_partition_section = true; continue; }
if line == "[partition]" { if !current_partition.name.is_empty() { self.partitions.push(current_partition.clone()); } current_partition = PartitionConfig::default(); in_partition_section = true; continue; }
if line.starts_with('[') && line.ends_with(']') { if line != "[partition]" && line != "[partition_start]" && line != "[mbr]" { in_partition_section = false; } continue; }
if in_partition_section { self.parse_partition_line(line, &mut current_partition); } }
if in_partition_section && !current_partition.name.is_empty() { self.partitions.push(current_partition); }
true } }
|
配置行解析
fn parse_partition_line(&self, line: &str, partition: &mut PartitionConfig) { let parts: Vec<&str> = line.splitn(2, '=').collect(); if parts.len() != 2 { return; }
let key = parts[0].trim(); let value = parts[1].trim(); let value = value.trim_matches('"');
match key { "name" => partition.name = value.to_string(), "size" => { partition.size = if value.starts_with("0x") || value.starts_with("0X") { u64::from_str_radix(&value[2..], 16).unwrap_or(0) } else { value.parse().unwrap_or(0) } } "downloadfile" => partition.downloadfile = value.to_string(), "user_type" => { partition.user_type = if value.starts_with("0x") || value.starts_with("0X") { u32::from_str_radix(&value[2..], 16).unwrap_or(0) } else { value.parse().unwrap_or(0) } } "keydata" => partition.keydata = value != "0", "encrypt" => partition.encrypt = value != "0", "verify" => partition.verify = value != "0", "ro" => partition.readonly = value != "0", _ => {} } }
|
INI 配置示例
[partition_start] [partition] name = boot size = 0x100000 downloadfile = "boot.fex" user_type = 0x8000 verify = 1
[partition] name = rootfs size = 0x8000000 downloadfile = "rootfs.fex" user_type = 0x8000 verify = 1
[partition] name = userdata size = 0x10000000 downloadfile = "userdata.fex" user_type = 0x8000 keydata = 0 encrypt = 0 verify = 1
|
系统配置解析
DramParamInfo 结构
#[repr(C, packed)] #[derive(Debug, Clone, Copy)] pub struct DramParamInfo { pub dram_init_flag: u32,
pub dram_update_flag: u32,
pub dram_para: [u32; 32], }
impl DramParamInfo { pub fn create_empty() -> Self { Self { dram_init_flag: 0, dram_update_flag: 0, dram_para: [0u32; 32], } }
pub fn serialize(&self) -> Vec<u8> { let size = std::mem::size_of::<DramParamInfo>(); let mut data = vec![0u8; size]; unsafe { std::ptr::copy_nonoverlapping( self as *const DramParamInfo as *const u8, data.as_mut_ptr(), size, ); } data } }
|
SysConfigParser
pub struct SysConfigParser;
impl SysConfigParser { pub fn parse(data: &[u8]) -> SysConfig { SysConfig { storage_type: Self::get_storage_type(data), } }
fn get_storage_type(data: &[u8]) -> u32 { if data.len() < 4 { return 0; } let ptr = data.as_ptr() as *const u32; unsafe { u32::from_le(*ptr) } }
pub fn get_storage_type_from_num(num: u32) -> StorageType { StorageType::from(num) } }
#[derive(Debug, Clone)] pub struct SysConfig { pub storage_type: u32, }
|
调用流程分析
MBR 解析流程
fn parse_mbr_for_flash(packer: &mut OpenixPacker) -> Result<MbrInfo, FlashError> { let mbr_data = packer.get_mbr()?;
let mbr = SunxiMbr::parse(&mbr_data) .map_err(|e| FlashError::MbrNotFound)?;
let mbr_info = mbr.to_mbr_info();
if mbr_info.part_count == 0 { return Err(FlashError::MbrNotFound); }
for part in &mbr_info.partitions { println!( "分区: {} @ 0x{:08x} ({} bytes)", part.name, part.address(), part.length() ); }
Ok(mbr_info) }
|
Boot 头解析流程
fn prepare_uboot_for_fel(uboot_data: &mut [u8]) -> Result<(), FlashError> { let header = UBootHeader::parse(uboot_data) .map_err(|_| FlashError::UbootNotFound)?;
if header.uboot_head.magic_str() != UBOOT_MAGIC { return Err(FlashError::UbootNotFound); }
UBootHeader::set_work_mode(uboot_data, WORK_MODE_USB_PRODUCT);
let storage_type = StorageType::from(header.uboot_data.storage_type as u32); println!("存储类型: {}", storage_type);
Ok(()) }
|
分区配置解析流程
fn parse_partition_config(data: &[u8]) -> Vec<PartitionConfig> { let mut parser = OpenixPartition::new(); parser.parse_from_data(data); parser.get_partitions().to_vec() }
fn print_partitions(partitions: &[PartitionConfig]) { for part in partitions { println!( "名称: {}, 大小: {} MB, 文件: {}", part.name, part.size / (1024 * 1024), part.downloadfile ); } }
|
代码亮点
1. 64 位地址的高低位拼接
pub fn address(&self) -> u64 { ((self.addrhi as u64) << 32) | (self.addrlo as u64) }
|
原理: 全志 MBR 使用 addrhi/addrlo 分离存储 64 位地址,需要手动拼接。
2. 段式 INI 解析
let mut in_partition_section = false;
for line in content.lines() { if line == "[partition_start]" { in_partition_section = true; continue; }
if line.starts_with('[') && line != "[partition]" { in_partition_section = false; continue; }
if in_partition_section { self.parse_partition_line(line, &mut current_partition); } }
|
3. 动态工作模式设置
pub fn set_work_mode(data: &mut [u8], mode: u32) { let data_offset = std::mem::size_of::<UBootBaseHeader>(); UBootDataHeader::set_work_mode(&mut data[data_offset..], mode); }
|
用途: FEL 模式下需要设置 work_mode = 0x10 使 U-Boot 进入 USB 产品模式。
4. 序列化实现
pub fn serialize(&self) -> Vec<u8> { let size = std::mem::size_of::<DramParamInfo>(); let mut data = vec![0u8; size]; unsafe { std::ptr::copy_nonoverlapping( self as *const DramParamInfo as *const u8, data.as_mut_ptr(), size, ); } data }
|
5. 十六进制数值解析
partition.size = if value.starts_with("0x") || value.starts_with("0X") { u64::from_str_radix(&value[2..], 16).unwrap_or(0) } else { value.parse().unwrap_or(0) };
|
实践示例
解析固件中的 MBR
use openixcli::firmware::OpenixPacker; use openixcli::config::mbr_parser::{SunxiMbr, MBR_SIZE};
fn main() -> Result<(), Box<dyn std::error::Error>> { let mut packer = OpenixPacker::new(); packer.load("firmware.fex")?;
let mbr_data = packer.get_mbr()?; println!("MBR 数据大小: {} bytes (预期 {} bytes)", mbr_data.len(), MBR_SIZE);
let mbr = SunxiMbr::parse(&mbr_data)?;
println!("MBR 魂数: {}", mbr.magic); println!("MBR 版本: 0x{:08x}", mbr.version); println!("分区数量: {}", mbr.part_count);
println!("\n分区列表:"); println!("{:-<60}", ""); println!("{:16} {:12} {:12}", "名称", "起始地址", "大小"); println!("{:-<60}", "");
for part in &mbr.partitions { println!( "{:16} 0x{:010x} {} MB", part.name, part.address(), part.length() / (1024 * 1024) ); }
Ok(()) }
|
输出示例:
MBR 数据大小: 16384 bytes (预期 16384 bytes) MBR 魂数: softw411 MBR 版本: 0x00000200 分区数量: 8
分区列表: ------------------------------------------------------------ 名称 起始地址 大小 ------------------------------------------------------------ boot 0x00000000 1 MB env 0x00100000 128 KB rootfs 0x00120000 128 MB userdata 0x08120000 256 MB misc 0x10120000 1 MB recovery 0x10220000 32 MB ...
|
解析 Boot0 头
use openixcli::config::boot_header::{Boot0Header, BOOT0_MAGIC};
fn analyze_boot0(boot0_data: &[u8]) { let header = Boot0Header::parse(boot0_data).unwrap();
println!("Boot0 分析:"); println!(" 魂数: {} ({})", header.magic_str(), if header.magic_str() == BOOT0_MAGIC { "有效" } else { "无效" }); println!(" 平台: {}", header.platform_str()); println!(" 镜像长度: {} bytes", header.length); println!(" 运行地址: 0x{:08x}", header.run_addr); println!(" 校验和: 0x{:08x}", header.check_sum); }
|
解析分区配置
use openixcli::config::partition::OpenixPartition;
fn parse_config(config_data: &[u8]) { let mut parser = OpenixPartition::new(); parser.parse_from_data(config_data);
println!("分区配置:"); for part in parser.get_partitions() { println!("\n 分区: {}", part.name); println!(" 大小: {} bytes ({} KB)", part.size, part.size / 1024); println!(" 文件: {}", part.downloadfile); println!(" 验证: {}", part.verify); println!(" 只读: {}", part.readonly); if part.keydata { println!(" 包含关键数据"); } } }
|
数据结构关系图
classDiagram
class Boot0Header {
+jump_instruction: u32
+magic: [u8; 8]
+check_sum: u32
+length: u32
+run_addr: u32
+platform: [u8; 8]
+parse(data)
+magic_str() String
}
class UBootHeader {
+uboot_head: UBootBaseHeader
+uboot_data: UBootDataHeader
+parse(data)
+set_work_mode(data, mode)
}
class UBootDataHeader {
+dram_para: [u32; 32]
+run_clock: i32
+uart_port: i32
+twi_port: i32
+work_mode: i32
+storage_type: i32
}
class SunxiMbr {
+crc32: u32
+version: u32
+magic: String
+part_count: u32
+partitions: Vec~SunxiPartition~
+parse(data)
+to_mbr_info() MbrInfo
}
class SunxiPartition {
+addrhi: u32
+addrlo: u32
+lenhi: u32
+lenlo: u32
+name: String
+user_type: u32
+address() u64
+length() u64
}
class PartitionConfig {
+name: String
+size: u64
+downloadfile: String
+verify: bool
+readonly: bool
}
class OpenixPartition {
+partitions: Vec~PartitionConfig~
+parse_from_data(data)
+get_partitions()
}
UBootHeader --> UBootBaseHeader
UBootHeader --> UBootDataHeader
SunxiMbr --> SunxiPartition
OpenixPartition --> PartitionConfig