From 5b80c8d7f21e35e06d6ef4e6882f46d2ebbf3928 Mon Sep 17 00:00:00 2001 From: YdrMaster Date: Thu, 30 Jun 2022 09:26:59 +0800 Subject: [PATCH] feat: new function to ignore some `HeaderError` Signed-off-by: YdrMaster --- CHANGELOG.md | 35 ++++++++++++++ Cargo.toml | 2 +- README.md | 5 +- docs/README_EN.md | 36 +++++++++++++++ examples/qemu-virt.rs | 17 +++---- src/header.rs | 104 ++++++++++++++++++++++++++++++------------ src/lib.rs | 23 ++++++++-- 7 files changed, 177 insertions(+), 45 deletions(-) create mode 100644 CHANGELOG.md create mode 100644 docs/README_EN.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..81b8782 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,35 @@ +# 更新日志 Change Log + +## v0.1.2 + +- 功能 + - 增加一个接收谓词闭包的构造函数,支持忽略某些 `HeaderError`([issue#1](https://github.com/YdrMaster/dtb-walker/issues/1)) + +--- + +- feature + - a new function with a filter closure, allows to ignore some `HeaderError` ([issue#1](https://github.com/YdrMaster/dtb-walker/issues/1)) + +## v0.1.1 + +- 修正 + - 导出 `HeaderError` + +- 示例 + - 演示判断 `HeaderError` 类型以接受某些不合规的首部([issue#1](https://github.com/YdrMaster/dtb-walker/issues/1)) + +--- + +- fix + - pub use `HeaderError` + +- examples + - shows the way to allow dtb implemeatations that do not conform to specification by matching the `HeaderError` ([issue#1](https://github.com/YdrMaster/dtb-walker/issues/1)) + +## v0.1.0 + +初次发布。 + +--- + +First release. diff --git a/Cargo.toml b/Cargo.toml index b2a7d6f..8a91c50 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "dtb-walker" description = "A simple package for DTB depth-first walking." -version = "0.1.1" +version = "0.1.2" edition = "2021" authors = ["YdrMaster "] repository = "https://github.com/YdrMaster/dtb-walker.git" diff --git a/README.md b/README.md index 08e8f42..17adbb0 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,8 @@ # 深度优先遍历设备树二进制对象 +- [An English README](docs/README_EN.md) +- [更新日志](CHANGELOG.md) + DTB 深度优先遍历的薄封装。 测试示例: @@ -14,7 +17,7 @@ cargo run --release --example qemu-virt - [x] 可选是否检查首部正确性; - [x] `no_std`; -- [x] without `alloc`; +- [x] 不需要 `alloc`; - [x] 提前终止遍历; - [x] 低开销跳过节点; - [ ] 内置标准属性解析; diff --git a/docs/README_EN.md b/docs/README_EN.md new file mode 100644 index 0000000..c56a831 --- /dev/null +++ b/docs/README_EN.md @@ -0,0 +1,36 @@ +# DTB depth-first walking + +- [中文自述文档](../README.md) +- [Change Log](../CHANGELOG.md) + +A simple package for DTB depth-first walking. + +Try an example: + +```cmd +cargo run --release --example qemu-virt +``` + +Following the [devicetree-specification-v0.4-rc1](https://github.com/devicetree-org/devicetree-specification/releases/tag/v0.4-rc1),DTB v17。 + +Features: + +- [x] optional header verifying; +- [x] `no_std`; +- [x] without `alloc`; +- [x] terminate walking at any time; +- [x] step over nodes with low overhead; +- [ ] built-in standard property parsing; + - [x] `compatible` + - [x] `model` + - [x] `phandle` + - [x] `status` + - [x] `#address-cells` + - [x] `#size-cells` + - [x] `reg` + - [x] `virtual-reg` + - [ ] `ranges` + - [ ] `dma-ranges` + - [x] `dma-coherent` + - [ ] `name (deprecated)` + - [ ] `device_type (deprecated)` diff --git a/examples/qemu-virt.rs b/examples/qemu-virt.rs index e1aa65b..bd33e8d 100644 --- a/examples/qemu-virt.rs +++ b/examples/qemu-virt.rs @@ -11,14 +11,15 @@ fn main() { .copy_from_nonoverlapping(DEVICE_TREE.as_ptr() as _, aligned.len()); } - let dtb = match unsafe { Dtb::from_raw_parts(aligned.as_ptr() as _) } { - Ok(ans) => ans, - Err(HeaderError::LastCompVersion) => { - // ignore! - unsafe { Dtb::from_raw_parts_unchecked(aligned.as_ptr() as _) } - } - Err(e) => panic!("Verify dtb header failed: {e:?}"), - }; + let dtb = unsafe { + Dtb::from_raw_parts_filtered(aligned.as_ptr() as _, |e| { + matches!( + e, + HeaderError::Misaligned(4) | HeaderError::LastCompVersion(16) + ) + }) + } + .unwrap(); dtb.walk(|path, obj| match obj { DtbObj::SubNode { name } => { println!("{}{path}/{}", indent(path.level(), INDENT_WIDTH), unsafe { diff --git a/src/header.rs b/src/header.rs index 114f1a4..3fb9324 100644 --- a/src/header.rs +++ b/src/header.rs @@ -1,4 +1,6 @@ -use crate::{is_aligned, U32BigEndian}; +use core::ops::Range; + +use crate::{is_aligned, U32BigEndian}; pub(crate) struct FdtHeader { magic: U32BigEndian, @@ -16,23 +18,23 @@ pub(crate) struct FdtHeader { #[derive(Debug)] pub enum HeaderError { - Misaligned, - Magic, - Version, - LastCompVersion, - TotalSize, - StructMisaligned, - StructOffset, - StructSize, + Misaligned(u32), + Magic(u32), + Version(u32), + LastCompVersion(u32), + TotalSize(u32), + StructMisaligned(u32), + StructOffset { value: u32, expected: Range }, + StructSize { value: u32, max: u32 }, StructContent, - MemRsvMisaligned, - MemRsvOffset, - StringsOffset, - StringsSize, + MemRsvMisaligned(u32), + MemRsvOffset { value: u32, expected: Range }, + StringsOffset { value: u32, expected: Range }, + StringsSize { value: u32, max: u32 }, } const FOUR: usize = 4; -const DTB_ALIGN_BITS: usize = FOUR; +const DTB_ALIGN_BITS: usize = 8; const MEMREV_ALIGN_BITS: usize = FOUR; const STRUCT_ALIGN_BITS: usize = FOUR; const STRUCT_SIZE_ALIGN_BITS: usize = FOUR; @@ -43,52 +45,72 @@ const LAST_COMP_VERSION: u32 = 16; const LEN_HEADER: u32 = core::mem::size_of::() as _; impl FdtHeader { - pub fn verify(&self) -> Result<(), HeaderError> { + pub fn verify(&self, filter: impl Fn(&HeaderError) -> bool) -> Result<(), HeaderError> { use HeaderError as E; // 检查整体对齐 - // 标准说设备树应该 8 对齐,至少是 usize 对齐,但实际上里面的内容确实都是 4 对齐的,很多实现也只提供 4 对齐 if !is_aligned(self as *const _ as _, DTB_ALIGN_BITS) { - return Err(E::Misaligned); + check(&filter, E::Misaligned(misaligned(self as *const _ as _)))?; } // 检查 magic 和版本 if self.magic != MAGIC { - return Err(E::Magic); + check(&filter, E::Magic(self.magic.into_u32()))?; } if self.version.into_u32() < VERSION { - return Err(E::Version); + check(&filter, E::Version(self.version.into_u32()))?; } if self.last_comp_version.into_u32() != LAST_COMP_VERSION { - return Err(E::LastCompVersion); + check( + &filter, + E::LastCompVersion(self.last_comp_version.into_u32()), + )?; } // 检查结构 let len_total = self.totalsize.into_u32(); if len_total < LEN_HEADER { - return Err(E::TotalSize); + check(&filter, E::TotalSize(len_total))?; } let mut range = LEN_HEADER..len_total; // 保留内存块 let off_memrev = self.off_mem_rsvmap.into_u32(); if !is_aligned(off_memrev as _, MEMREV_ALIGN_BITS) { - return Err(E::MemRsvMisaligned); + check(&filter, E::MemRsvMisaligned(misaligned(off_memrev)))?; } if !range.contains(&off_memrev) { - return Err(E::MemRsvOffset); + check( + &filter, + E::MemRsvOffset { + value: off_memrev, + expected: range.clone(), + }, + )?; } range = off_memrev..len_total; // 结构块 let off_struct = self.off_dt_struct.into_u32(); if !is_aligned(off_struct as _, STRUCT_ALIGN_BITS) { - return Err(E::StructMisaligned); + check(&filter, E::StructMisaligned(misaligned(off_struct)))?; } if !range.contains(&off_struct) { - return Err(E::StructOffset); + check( + &filter, + E::StructOffset { + value: off_struct, + expected: range.clone(), + }, + )?; } let len_struct = self.size_dt_struct.into_u32(); if !is_aligned(len_struct as _, STRUCT_SIZE_ALIGN_BITS) { - return Err(E::StructMisaligned); + check(&filter, E::StructMisaligned(misaligned(len_struct)))?; } if len_struct > range.len() as u32 { - return Err(E::StructSize); + check( + &filter, + E::StructSize { + value: len_struct, + max: range.len() as _, + }, + )?; } unsafe { use crate::StructureBlock as Blk; @@ -99,19 +121,41 @@ impl FdtHeader { len_struct as usize / Blk::LEN, ) { [Blk::NODE_BEGIN, Blk::EMPTY_STR, .., Blk::END] => {} - _ => return Err(E::StructContent), + _ => check(&filter, E::StructContent)?, } } range = off_struct + len_struct..len_total; // 字符串块 let off_strings = self.off_dt_strings.into_u32(); if !range.contains(&off_strings) { - return Err(E::StringsOffset); + check( + &filter, + E::StringsOffset { + value: off_strings, + expected: range.clone(), + }, + )?; } let len_strings = self.size_dt_strings.into_u32(); if len_strings > range.len() as u32 { - return Err(E::StringsSize); + check( + filter, + E::StringsSize { + value: len_strings, + max: range.len() as _, + }, + )?; } Ok(()) } } + +#[inline] +fn misaligned(addr: u32) -> u32 { + 1 << addr.trailing_zeros() +} + +#[inline] +fn check(filter: impl Fn(&HeaderError) -> bool, err: HeaderError) -> Result<(), HeaderError> { + filter(&err).then_some(()).ok_or(err) +} diff --git a/src/lib.rs b/src/lib.rs index abb984d..20de1a7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,8 +1,6 @@ #![no_std] #![feature(slice_internals)] -#![deny(warnings)] // cancel this during developing - -use core::{fmt, mem, slice}; +#![deny(warnings)] // cancel this line during developing mod header; mod indent; @@ -18,6 +16,7 @@ pub mod utils { } pub use header::HeaderError; +use core::{fmt, mem, slice}; use header::FdtHeader; use property::RegCfg; use structure_block::StructureBlock; @@ -34,7 +33,21 @@ impl Dtb<'static> { /// 如果指针指向一个有效的 DTB 首部,其中描述的整个二进制对象会被切片。 #[inline] pub unsafe fn from_raw_parts(ptr: *const u8) -> Result { - (*ptr.cast::()).verify()?; + (*ptr.cast::()).verify(|_| true)?; + Ok(Self::from_raw_parts_unchecked(ptr)) + } + + /// 构造设备树二进制对象。 + /// + /// # Safety + /// + /// 如果指针指向一个有效的 DTB 首部,其中描述的整个二进制对象会被切片。 + #[inline] + pub unsafe fn from_raw_parts_filtered( + ptr: *const u8, + f: impl Fn(&HeaderError) -> bool, + ) -> Result { + (*ptr.cast::()).verify(f)?; Ok(Self::from_raw_parts_unchecked(ptr)) } @@ -64,7 +77,7 @@ impl<'a> Dtb<'a> { return Err(ConvertError::Truncated); } let header = unsafe { &*slice.as_ptr().cast::() }; - match header.verify() { + match header.verify(|_| true) { Ok(()) => { let len = header.totalsize.into_u32() as usize; if len <= slice.len() {