Skip to content

Commit

Permalink
feat: new function to ignore some HeaderError
Browse files Browse the repository at this point in the history
Signed-off-by: YdrMaster <ydrml@hotmail.com>
  • Loading branch information
YdrMaster committed Jun 30, 2022
1 parent 3d70e37 commit 5b80c8d
Show file tree
Hide file tree
Showing 7 changed files with 177 additions and 45 deletions.
35 changes: 35 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -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 <ydrml@hotmail.com>"]
repository = "https://github.com/YdrMaster/dtb-walker.git"
Expand Down
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# 深度优先遍历设备树二进制对象

- [An English README](docs/README_EN.md)
- [更新日志](CHANGELOG.md)

DTB 深度优先遍历的薄封装。

测试示例:
Expand All @@ -14,7 +17,7 @@ cargo run --release --example qemu-virt

- [x] 可选是否检查首部正确性;
- [x] `no_std`
- [x] without `alloc`
- [x] 不需要 `alloc`
- [x] 提前终止遍历;
- [x] 低开销跳过节点;
- [ ] 内置标准属性解析;
Expand Down
36 changes: 36 additions & 0 deletions docs/README_EN.md
Original file line number Diff line number Diff line change
@@ -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)`
17 changes: 9 additions & 8 deletions examples/qemu-virt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
104 changes: 74 additions & 30 deletions src/header.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use crate::{is_aligned, U32BigEndian};
use core::ops::Range;

use crate::{is_aligned, U32BigEndian};

pub(crate) struct FdtHeader {
magic: U32BigEndian,
Expand All @@ -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<u32> },
StructSize { value: u32, max: u32 },
StructContent,
MemRsvMisaligned,
MemRsvOffset,
StringsOffset,
StringsSize,
MemRsvMisaligned(u32),
MemRsvOffset { value: u32, expected: Range<u32> },
StringsOffset { value: u32, expected: Range<u32> },
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;
Expand All @@ -43,52 +45,72 @@ const LAST_COMP_VERSION: u32 = 16;
const LEN_HEADER: u32 = core::mem::size_of::<FdtHeader>() 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;
Expand All @@ -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)
}
23 changes: 18 additions & 5 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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;
Expand All @@ -34,7 +33,21 @@ impl Dtb<'static> {
/// 如果指针指向一个有效的 DTB 首部,其中描述的整个二进制对象会被切片。
#[inline]
pub unsafe fn from_raw_parts(ptr: *const u8) -> Result<Self, HeaderError> {
(*ptr.cast::<FdtHeader>()).verify()?;
(*ptr.cast::<FdtHeader>()).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<Self, HeaderError> {
(*ptr.cast::<FdtHeader>()).verify(f)?;
Ok(Self::from_raw_parts_unchecked(ptr))
}

Expand Down Expand Up @@ -64,7 +77,7 @@ impl<'a> Dtb<'a> {
return Err(ConvertError::Truncated);
}
let header = unsafe { &*slice.as_ptr().cast::<FdtHeader>() };
match header.verify() {
match header.verify(|_| true) {
Ok(()) => {
let len = header.totalsize.into_u32() as usize;
if len <= slice.len() {
Expand Down

0 comments on commit 5b80c8d

Please sign in to comment.