diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7b8c8b2..405a135 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,7 +22,7 @@ jobs: - name: Check code format run: cargo fmt --all -- --check - name: Clippy - run: cargo clippy --manifest-path ./fdt-parser/Cargo.toml --target ${{ matrix.targets }} --all-features -- -A clippy::new_without_default + run: cargo clippy --manifest-path ./fdt-parser/Cargo.toml --target ${{ matrix.targets }} --all-features -- -A clippy::new_without_default - name: Build run: cargo build -p fdt-parser --target ${{ matrix.targets }} --all-features - name: Install dtc @@ -30,4 +30,31 @@ jobs: run: sudo apt-get install device-tree-compiler - name: Unit test if: ${{ matrix.targets == 'x86_64-unknown-linux-gnu' }} - run: cargo test --target ${{ matrix.targets }} -- --nocapture \ No newline at end of file + run: cargo test --target ${{ matrix.targets }} -- --nocapture + + doc: + runs-on: ubuntu-latest + strategy: + fail-fast: false + permissions: + contents: write + env: + default-branch: ${{ format('refs/heads/{0}', github.event.repository.default_branch) }} + RUSTDOCFLAGS: -D rustdoc::broken_intra_doc_links -D missing-docs + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@nightly + with: + toolchain: nightly-2025-12-12 + - name: Build docs + continue-on-error: ${{ github.ref != env.default-branch && github.event_name != 'pull_request' }} + run: | + cargo doc --no-deps --all-features + printf '' $(cargo tree | head -1 | cut -d' ' -f1) > target/doc/index.html + - name: Deploy to Github Pages + if: ${{ github.ref == env.default-branch }} + uses: JamesIves/github-pages-deploy-action@v4 + with: + single-commit: true + branch: gh-pages + folder: target/doc \ No newline at end of file diff --git a/dtb-file/Cargo.toml b/dtb-file/Cargo.toml index b26d444..2a0daef 100644 --- a/dtb-file/Cargo.toml +++ b/dtb-file/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "dtb-file" -version = "0.1.0" +version = "0.1.1" edition = "2024" publish = false diff --git a/dtb-file/src/lib.rs b/dtb-file/src/lib.rs index 1ee2d51..b520dc4 100644 --- a/dtb-file/src/lib.rs +++ b/dtb-file/src/lib.rs @@ -1,3 +1,9 @@ +//! Test data and sample Device Tree Blob (DTB) files for the FDT parser. +//! +//! This crate provides embedded DTB files from various hardware platforms +//! for testing purposes, along with a helper struct to ensure 4-byte alignment +//! required by the FDT specification. + use core::ops::Deref; const TEST_RPI_4_FDT: &[u8] = include_bytes!("dtb/bcm2711-rpi-4-b.dtb"); @@ -6,32 +12,43 @@ const TEST_QEMU_FDT: &[u8] = include_bytes!("dtb/qemu_pci.dtb"); const TEST_3568_FDT: &[u8] = include_bytes!("dtb/rk3568-firefly-roc-pc-se.dtb"); const TEST_RESERVE_FDT: &[u8] = include_bytes!("dtb/test_reserve.dtb"); +/// Returns the FDT data for Raspberry Pi 4 Model B. pub fn fdt_rpi_4b() -> Align4Vec { Align4Vec::new(TEST_RPI_4_FDT) } +/// Returns the FDT data for Phytium platform. pub fn fdt_phytium() -> Align4Vec { Align4Vec::new(TEST_PHYTIUM_FDT) } +/// Returns the FDT data for QEMU with PCI support. pub fn fdt_qemu() -> Align4Vec { Align4Vec::new(TEST_QEMU_FDT) } +/// Returns the FDT data for RK3568 Firefly ROC PC SE. pub fn fdt_3568() -> Align4Vec { Align4Vec::new(TEST_3568_FDT) } +/// Returns the FDT data with reserved memory entries for testing. pub fn fdt_reserve() -> Align4Vec { Align4Vec::new(TEST_RESERVE_FDT) } +/// A 4-byte aligned buffer for FDT data. +/// +/// The Device Tree Blob specification requires that the FDT structure +/// be 4-byte aligned in memory. This wrapper allocates aligned memory +/// and provides raw pointer access for FDT parsing. pub struct Align4Vec { ptr: *mut u8, size: usize, } impl Align4Vec { + /// Creates a new 4-byte aligned buffer from the provided data. pub fn new(data: &[u8]) -> Self { let size = data.len(); let layout = core::alloc::Layout::from_size_align(size, 4).unwrap(); @@ -40,12 +57,14 @@ impl Align4Vec { Align4Vec { ptr, size } } + /// Returns a raw pointer to the aligned buffer. pub fn ptr(&self) -> *mut u8 { self.ptr } } impl Drop for Align4Vec { + /// Deallocates the aligned buffer when the `Align4Vec` is dropped. fn drop(&mut self) { let layout = core::alloc::Layout::from_size_align(self.size, 4).unwrap(); unsafe { std::alloc::dealloc(self.ptr, layout) }; @@ -53,6 +72,7 @@ impl Drop for Align4Vec { } impl Deref for Align4Vec { + /// Allows treating `Align4Vec` as a byte slice for convenient data access. type Target = [u8]; fn deref(&self) -> &Self::Target { diff --git a/dtb-tool/Cargo.toml b/dtb-tool/Cargo.toml index 361fe8a..aaf6a83 100644 --- a/dtb-tool/Cargo.toml +++ b/dtb-tool/Cargo.toml @@ -1,9 +1,9 @@ [package] name = "dtb-tool" -version = "0.1.0" +version = "0.1.1" authors = ["Zhourui "] edition = "2021" -repository = "https://github.com/qclic/fdt-parser" +repository = "https://github.com/drivercraft/fdt-parser" documentation = "https://docs.rs/fdt-parser" license = "MPL-2.0" description = "A pure-Rust `#![no_std]` crate for parsing FDT" diff --git a/dtb-tool/src/main.rs b/dtb-tool/src/main.rs index 5c8af93..dba6f9f 100644 --- a/dtb-tool/src/main.rs +++ b/dtb-tool/src/main.rs @@ -1,16 +1,22 @@ +//! Command-line tool for inspecting and converting Device Tree Blob (DTB) files. +//! +//! This tool reads a DTB file, parses it using the `fdt_parser` library, +//! and outputs a human-readable text representation showing the device tree +//! structure including nodes, compatible strings, and memory reservations. + use clap::Parser; use fdt_parser::Fdt; use std::io::Write; -/// Simple DTB parser +/// Command-line arguments for the DTB parser tool. #[derive(Parser, Debug)] #[command(version, about, long_about = None)] struct Args { - /// dtb file path + /// Path to the input DTB file #[arg(short, long)] input: String, - /// output file path + /// Path to the output text file #[arg(short, long)] output: String, } diff --git a/example_all_nodes.rs b/example_all_nodes.rs index c07112d..5c89aa8 100644 --- a/example_all_nodes.rs +++ b/example_all_nodes.rs @@ -1,14 +1,14 @@ -// 示例:使用 all_nodes 函数 +// Example: Using the all_nodes function extern crate alloc; use alloc::{string::String, vec::Vec}; use fdt_edit::{Fdt, Node, NodeRef}; fn main() { - // 创建一个示例 FDT + // Create an example FDT let mut fdt = Fdt::new(); - // 添加一些示例节点 + // Add some example nodes { let root = &mut fdt.root; let mut soc = Node::new_raw("soc"); @@ -16,44 +16,44 @@ fn main() { let mut gpio = Node::new_raw("gpio@5000"); let mut led = Node::new_raw("led"); - // 设置属性 + // Set properties uart.add_property(fdt_edit::Property::new_str("compatible", "vendor,uart")); gpio.add_property(fdt_edit::Property::new_str("compatible", "vendor,gpio")); led.add_property(fdt_edit::Property::new_str("compatible", "vendor,led")); - // 构建树结构 + // Build the tree structure gpio.add_child(led); soc.add_child(uart); soc.add_child(gpio); root.add_child(soc); } - // 使用 all_nodes 获取所有节点 + // Use all_nodes to get all nodes let all_nodes: Vec = fdt.all_nodes().collect(); - println!("FDT 中所有节点 (深度优先遍历):"); + println!("All nodes in FDT (depth-first traversal):"); for (i, node_ref) in all_nodes.iter().enumerate() { println!( - "{}: 节点 '{}', 路径: '{}', 深度: {}", + "{}: Node '{}', Path: '{}', Depth: {}", i + 1, node_ref.node.name(), node_ref.context.current_path, node_ref.context.depth ); - // 显示节点的 compatible 属性 + // Display the node's compatible property let compatibles: Vec<&str> = node_ref.compatibles(); if !compatibles.is_empty() { println!(" Compatible: {:?}", compatibles); } } - // 使用 find_compatible 查找特定节点 + // Use find_compatible to find specific nodes let uart_nodes = fdt.find_compatible(&["vendor,uart"]); - println!("\n找到 UART 节点:"); + println!("\nFound UART nodes:"); for node_ref in uart_nodes { println!( - " 节点: {}, 完整路径: '{}'", + " Node: {}, Full path: '{}'", node_ref.node.name(), node_ref.context.current_path ); diff --git a/fdt-edit/Cargo.toml b/fdt-edit/Cargo.toml index 308a3eb..ab57a7a 100644 --- a/fdt-edit/Cargo.toml +++ b/fdt-edit/Cargo.toml @@ -11,7 +11,7 @@ license = "MIT OR Apache-2.0" name = "fdt-edit" readme = "README.md" repository = "https://github.com/drivercraft/fdt-parser" -version = "0.1.6" +version = "0.1.7" [dependencies] enum_dispatch = "0.3.13" diff --git a/fdt-edit/examples/fdt_debug_demo.rs b/fdt-edit/examples/fdt_debug_demo.rs index 7bcb469..179049b 100644 --- a/fdt-edit/examples/fdt_debug_demo.rs +++ b/fdt-edit/examples/fdt_debug_demo.rs @@ -1,6 +1,7 @@ -//! FDT 深度调试演示 +//! FDT deep debug demonstration //! -//! 演示如何使用新的深度调试功能来遍历和打印设备树的所有节点 +//! Demonstrates how to use the new deep debug functionality to traverse +//! and print all nodes in the device tree. use dtb_file::fdt_rpi_4b; use fdt_edit::Fdt; @@ -8,17 +9,17 @@ use fdt_edit::Fdt; fn main() -> Result<(), Box> { env_logger::init(); - // 从 RPI 4B DTB 数据创建 FDT + // Create FDT from RPI 4B DTB data let raw_data = fdt_rpi_4b(); let fdt = Fdt::from_bytes(&raw_data)?; - println!("=== FDT 基本调试信息 ==="); - // 基本调试格式(紧凑) + println!("=== FDT Basic Debug Information ==="); + // Basic debug format (compact) println!("{:?}", fdt); println!(); - println!("=== FDT 深度调试信息 ==="); - // 深度调试格式(遍历所有节点) + println!("=== FDT Deep Debug Information ==="); + // Deep debug format (traverses all nodes) println!("{:#?}", fdt); Ok(()) diff --git a/fdt-edit/src/ctx.rs b/fdt-edit/src/ctx.rs index 56c7859..f2c2771 100644 --- a/fdt-edit/src/ctx.rs +++ b/fdt-edit/src/ctx.rs @@ -1,3 +1,9 @@ +//! Context for FDT traversal and node lookup. +//! +//! This module provides the `Context` type which maintains state during +//! FDT parsing and traversal, including parent references, phandle mappings, +//! and inherited properties like address-cells and size-cells. + use alloc::{collections::BTreeMap, string::String, vec::Vec}; use fdt_raw::{Phandle, Status}; @@ -5,27 +11,32 @@ use fdt_raw::{Phandle, Status}; use crate::{Node, RangesEntry}; // ============================================================================ -// FDT 上下文 +// FDT Context // ============================================================================ -/// 遍历上下文,存储从根到当前节点的父节点引用栈 +/// Traversal context storing parent node reference stack. +/// +/// The context maintains state during FDT parsing and tree traversal, +/// including the stack of parent nodes from root to the current position +/// and mappings for efficient node lookups by phandle. #[derive(Clone, Default)] pub struct Context<'a> { - /// 父节点引用栈(从根节点到当前节点的父节点) - /// 栈底是根节点,栈顶是当前节点的直接父节点 + /// Parent node reference stack (from root to current node's parent) + /// The stack bottom is the root node, the stack top is the direct parent pub parents: Vec<&'a Node>, - /// phandle 到节点引用的映射 - /// 用于通过 phandle 快速查找节点(如中断父节点) + /// Phandle to node reference mapping + /// Used for fast node lookup by phandle (e.g., interrupt parent) pub phandle_map: BTreeMap, } impl<'a> Context<'a> { - /// 创建新的上下文 + /// Creates a new empty context. pub fn new() -> Self { Self::default() } + /// Returns the current path as a string. pub fn current_path(&self) -> String { self.parents .iter() @@ -34,35 +45,39 @@ impl<'a> Context<'a> { .join("/") } - /// 创建用于根节点的上下文 + /// Creates a context for the root node. pub fn for_root() -> Self { Self::default() } - /// 获取当前深度(父节点数量 + 1) + /// Returns the current depth (parent count + 1). pub fn depth(&self) -> usize { self.parents.len() + 1 } - /// 获取直接父节点 + /// Returns the direct parent node. pub fn parent(&self) -> Option<&'a Node> { self.parents.last().copied() } - /// 获取父节点的 #address-cells - /// 优先从直接父节点获取,否则返回默认值 2 + /// Returns the parent's #address-cells value. + /// + /// Gets the value from the direct parent node, or returns 2 as default. pub fn parent_address_cells(&self) -> u32 { self.parent().and_then(|p| p.address_cells()).unwrap_or(2) } - /// 获取父节点的 #size-cells - /// 优先从直接父节点获取,否则返回默认值 1 + /// Returns the parent's #size-cells value. + /// + /// Gets the value from the direct parent node, or returns 1 as default. pub fn parent_size_cells(&self) -> u32 { self.parent().and_then(|p| p.size_cells()).unwrap_or(1) } - /// 查找中断父节点 phandle - /// 从当前父节点向上查找,返回最近的 interrupt-parent + /// Finds the interrupt parent phandle. + /// + /// Searches upward through the parent stack to find the nearest + /// interrupt-parent property. pub fn interrupt_parent(&self) -> Option { for parent in self.parents.iter().rev() { if let Some(phandle) = parent.interrupt_parent() { @@ -72,8 +87,9 @@ impl<'a> Context<'a> { None } - /// 检查节点是否被禁用 - /// 检查父节点栈中是否有任何节点被禁用 + /// Checks if the node is disabled. + /// + /// Returns true if any parent in the stack has status = "disabled". pub fn is_disabled(&self) -> bool { for parent in &self.parents { if matches!(parent.status(), Some(Status::Disabled)) { @@ -83,53 +99,56 @@ impl<'a> Context<'a> { false } - /// 解析当前路径上所有父节点的 ranges,用于地址转换 - /// 返回从根到父节点的 ranges 栈 + /// Collects ranges from all parent nodes for address translation. + /// + /// Returns a stack of ranges from root to parent, used for translating + /// device addresses to CPU physical addresses. pub fn collect_ranges(&self) -> Vec> { let mut ranges_stack = Vec::new(); - let mut prev_address_cells = 2; // 根节点默认 + let mut prev_address_cells = 2; // Root node default for parent in &self.parents { if let Some(ranges) = parent.ranges(prev_address_cells) { ranges_stack.push(ranges); } - // 更新 address cells 为当前节点的值,供下一级使用 + // Update address cells to current node's value for next level prev_address_cells = parent.address_cells().unwrap_or(2); } ranges_stack } - /// 获取最近一层的 ranges(用于当前节点的地址转换) + /// Returns the most recent ranges layer (for current node's address translation). pub fn current_ranges(&self) -> Option> { - // 需要父节点来获取 ranges + // Need parent node to get ranges if self.parents.is_empty() { return None; } let parent = self.parents.last()?; - // 获取父节点的父节点的 address_cells + // Get parent node's parent's address_cells let grandparent_address_cells = if self.parents.len() >= 2 { self.parents[self.parents.len() - 2] .address_cells() .unwrap_or(2) } else { - 2 // 根节点默认 + 2 // Root node default }; parent.ranges(grandparent_address_cells) } + /// Pushes a node onto the parent stack. pub fn push(&mut self, node: &'a Node) { self.parents.push(node); } - /// 通过 phandle 查找节点 + /// Finds a node by its phandle value. pub fn find_by_phandle(&self, phandle: Phandle) -> Option<&'a Node> { self.phandle_map.get(&phandle).copied() } - /// 从 Fdt 构建 phandle 映射 + /// Builds a phandle mapping from a node tree. pub fn build_phandle_map_from_node(node: &'a Node, map: &mut BTreeMap) { if let Some(phandle) = node.phandle() { map.insert(phandle, node); diff --git a/fdt-edit/src/encode.rs b/fdt-edit/src/encode.rs index ad43b81..02a4f4d 100644 --- a/fdt-edit/src/encode.rs +++ b/fdt-edit/src/encode.rs @@ -1,6 +1,7 @@ -//! FDT 编码模块 +//! FDT encoding module. //! -//! 将 Fdt 结构序列化为 DTB 二进制格式 +//! This module handles serialization of the `Fdt` structure into the +//! DTB (Device Tree Blob) binary format. use alloc::{string::String, vec::Vec}; use core::ops::Deref; @@ -8,17 +9,19 @@ use fdt_raw::{FDT_MAGIC, Token}; use crate::{Fdt, Node}; -/// FDT 二进制数据 +/// FDT binary data container. +/// +/// Wraps the encoded DTB data and provides access to the underlying bytes. #[derive(Clone, Debug)] pub struct FdtData(Vec); impl FdtData { - /// 获取数据长度(字节) + /// Returns the data length in bytes. pub fn len(&self) -> usize { self.0.len() * 4 } - /// 数据是否为空 + /// Returns true if the data is empty. pub fn is_empty(&self) -> bool { self.0.is_empty() } @@ -43,7 +46,10 @@ impl AsRef<[u8]> for FdtData { } } -/// FDT 编码器 +/// FDT encoder for serializing to DTB format. +/// +/// This encoder walks the node tree and generates the binary DTB format +/// according to the Device Tree Specification. pub struct FdtEncoder<'a> { fdt: &'a Fdt, struct_data: Vec, @@ -52,7 +58,7 @@ pub struct FdtEncoder<'a> { } impl<'a> FdtEncoder<'a> { - /// 创建新的编码器 + /// Creates a new encoder for the given FDT. pub fn new(fdt: &'a Fdt) -> Self { Self { fdt, @@ -62,7 +68,7 @@ impl<'a> FdtEncoder<'a> { } } - /// 获取或添加字符串,返回偏移量 + /// Gets or adds a string to the strings block, returning its offset. fn get_or_add_string(&mut self, s: &str) -> u32 { for (existing, offset) in &self.string_offsets { if existing == s { @@ -77,7 +83,7 @@ impl<'a> FdtEncoder<'a> { offset } - /// 写入 BEGIN_NODE token 和节点名 + /// Writes a BEGIN_NODE token and node name. fn write_begin_node(&mut self, name: &str) { let begin_token: u32 = Token::BeginNode.into(); self.struct_data.push(begin_token.to_be()); @@ -95,13 +101,13 @@ impl<'a> FdtEncoder<'a> { } } - /// 写入 END_NODE token + /// Writes an END_NODE token. fn write_end_node(&mut self) { let end_token: u32 = Token::EndNode.into(); self.struct_data.push(end_token.to_be()); } - /// 写入属性 + /// Writes a property to the structure block. fn write_property(&mut self, name: &str, data: &[u8]) { let prop_token: u32 = Token::Prop.into(); self.struct_data.push(prop_token.to_be()); @@ -123,38 +129,38 @@ impl<'a> FdtEncoder<'a> { } } - /// 执行编码 + /// Performs the encoding and returns the binary DTB data. pub fn encode(mut self) -> FdtData { - // 递归编码节点树 + // Recursively encode node tree self.encode_node(&self.fdt.root.clone()); - // 添加 END token + // Add END token let token: u32 = Token::End.into(); self.struct_data.push(token.to_be()); self.finalize() } - /// 递归编码节点及其子节点 + /// Recursively encodes a node and its children. fn encode_node(&mut self, node: &Node) { - // 写入 BEGIN_NODE 和节点名 + // Write BEGIN_NODE and node name self.write_begin_node(node.name()); - // 写入所有属性(直接使用原始数据) + // Write all properties (using raw data directly) for prop in node.properties() { self.write_property(prop.name(), &prop.data); } - // 递归编码子节点 + // Recursively encode child nodes for child in node.children() { self.encode_node(child); } - // 写入 END_NODE + // Write END_NODE self.write_end_node(); } - /// 生成最终 FDT 数据 + /// Generates the final FDT binary data. fn finalize(self) -> FdtData { let memory_reservations = &self.fdt.memory_reservations; let boot_cpuid_phys = self.fdt.boot_cpuid_phys; diff --git a/fdt-edit/src/fdt.rs b/fdt-edit/src/fdt.rs index cdb4528..5107372 100644 --- a/fdt-edit/src/fdt.rs +++ b/fdt-edit/src/fdt.rs @@ -1,3 +1,9 @@ +//! Editable Flattened Device Tree (FDT) structure. +//! +//! This module provides the main `Fdt` type for creating, modifying, and +//! encoding device tree blobs. It supports loading from existing DTB files, +//! building new trees programmatically, and applying device tree overlays. + use alloc::{ collections::BTreeMap, format, @@ -13,16 +19,21 @@ use crate::{ encode::{FdtData, FdtEncoder}, }; -/// 可编辑的 FDT +/// An editable Flattened Device Tree (FDT). +/// +/// This structure represents a mutable device tree that can be created from +/// scratch, loaded from an existing DTB file, modified, and encoded back to +/// the binary DTB format. It maintains a phandle cache for efficient node +/// lookups by phandle value. #[derive(Clone)] pub struct Fdt { - /// 引导 CPU ID + /// Boot CPU ID pub boot_cpuid_phys: u32, - /// 内存保留块 + /// Memory reservation block entries pub memory_reservations: Vec, - /// 根节点 + /// Root node of the device tree pub root: Node, - /// phandle 到节点完整路径的缓存 + /// Cache mapping phandles to full node paths phandle_cache: BTreeMap, } @@ -33,7 +44,7 @@ impl Default for Fdt { } impl Fdt { - /// 创建新的空 FDT + /// Creates a new empty FDT. pub fn new() -> Self { Self { boot_cpuid_phys: 0, @@ -43,22 +54,24 @@ impl Fdt { } } - /// 从原始 FDT 数据解析 + /// Parses an FDT from raw byte data. pub fn from_bytes(data: &[u8]) -> Result { let raw_fdt = fdt_raw::Fdt::from_bytes(data)?; Self::from_raw(&raw_fdt) } - /// 从原始指针解析 + /// Parses an FDT from a raw pointer. /// /// # Safety - /// 调用者必须确保指针有效且指向有效的 FDT 数据 + /// + /// The caller must ensure that the pointer is valid and points to a + /// valid FDT data structure. pub unsafe fn from_ptr(ptr: *mut u8) -> Result { let raw_fdt = unsafe { fdt_raw::Fdt::from_ptr(ptr)? }; Self::from_raw(&raw_fdt) } - /// 从 fdt_raw::Fdt 转换 + /// Converts from a raw FDT parser instance. fn from_raw(raw_fdt: &fdt_raw::Fdt) -> Result { let header = raw_fdt.header(); @@ -69,23 +82,20 @@ impl Fdt { phandle_cache: BTreeMap::new(), }; - // 构建节点树 - // 使用栈来跟踪父节点,栈底是一个虚拟父节点 + // Build node tree using a stack to track parent nodes let mut node_stack: Vec = Vec::new(); for raw_node in raw_fdt.all_nodes() { let level = raw_node.level(); let node = Node::from(&raw_node); - // 弹出栈直到达到正确的父级别 - // level 0 = 根节点,应该直接放入空栈 - // level 1 = 根节点的子节点,栈中应该只有根节点 + // Pop stack until we reach the correct parent level while node_stack.len() > level { let child = node_stack.pop().unwrap(); if let Some(parent) = node_stack.last_mut() { parent.add_child(child); } else { - // 这是根节点 + // This is the root node fdt.root = child; } } @@ -93,37 +103,37 @@ impl Fdt { node_stack.push(node); } - // 弹出所有剩余节点 + // Pop all remaining nodes while let Some(child) = node_stack.pop() { if let Some(parent) = node_stack.last_mut() { parent.add_child(child); } else { - // 这是根节点 + // This is the root node fdt.root = child; } } - // 构建 phandle 缓存 + // Build phandle cache fdt.rebuild_phandle_cache(); Ok(fdt) } - /// 重建 phandle 缓存 + /// Rebuilds the phandle cache by scanning all nodes. pub fn rebuild_phandle_cache(&mut self) { self.phandle_cache.clear(); let root_clone = self.root.clone(); self.build_phandle_cache_recursive(&root_clone, "/"); } - /// 递归构建 phandle 缓存 + /// Recursively builds the phandle cache starting from a node. fn build_phandle_cache_recursive(&mut self, node: &Node, current_path: &str) { - // 检查节点是否有 phandle 属性 + // Check if node has a phandle property if let Some(phandle) = node.phandle() { self.phandle_cache.insert(phandle, current_path.to_string()); } - // 递归处理子节点 + // Recursively process child nodes for child in node.children() { let child_name = child.name(); let child_path = if current_path == "/" { @@ -135,28 +145,27 @@ impl Fdt { } } - /// 规范化路径:如果是别名则解析为完整路径,否则确保以 / 开头 + /// Normalizes a path: resolves aliases or ensures leading '/'. fn normalize_path(&self, path: &str) -> Option { if path.starts_with('/') { Some(path.to_string()) } else { - // 尝试解析别名 + // Try to resolve as an alias self.resolve_alias(path).map(|s| s.to_string()) } } - /// 解析别名,返回对应的完整路径 + /// Resolves an alias to its full path. /// - /// 从 /aliases 节点查找别名对应的路径 + /// Looks up the alias in the /aliases node and returns the + /// corresponding path string. pub fn resolve_alias(&self, alias: &str) -> Option<&str> { let aliases_node = self.get_by_path("/aliases")?; let prop = aliases_node.find_property(alias)?; prop.as_str() } - /// 获取所有别名 - /// - /// 返回 (别名, 路径) 的列表 + /// Returns all aliases as (name, path) pairs. pub fn aliases(&self) -> Vec<(String, String)> { let mut result = Vec::new(); if let Some(aliases_node) = self.get_by_path("/aliases") { @@ -169,39 +178,38 @@ impl Fdt { result } - /// 根据 phandle 查找节点 - /// 返回 (节点引用, 完整路径) + /// Finds a node by its phandle value. pub fn find_by_phandle(&self, phandle: Phandle) -> Option> { let path = self.phandle_cache.get(&phandle)?.clone(); self.get_by_path(&path) } - /// 根据 phandle 查找节点(可变) - /// 返回 (节点可变引用, 完整路径) + /// Finds a node by phandle (mutable reference). pub fn find_by_phandle_mut(&mut self, phandle: Phandle) -> Option> { let path = self.phandle_cache.get(&phandle)?.clone(); self.get_by_path_mut(&path) } - /// 获取根节点 + /// Returns the root node. pub fn root<'a>(&'a self) -> NodeRef<'a> { self.get_by_path("/").unwrap() } - /// 获取根节点(可变) + /// Returns the root node (mutable reference). pub fn root_mut<'a>(&'a mut self) -> NodeMut<'a> { self.get_by_path_mut("/").unwrap() } - /// 应用设备树覆盖 (Device Tree Overlay) + /// Applies a device tree overlay to this FDT. /// - /// 支持两种 overlay 格式: - /// 1. fragment 格式:包含 fragment@N 节点,每个 fragment 有 target/target-path 和 __overlay__ - /// 2. 简单格式:直接包含 __overlay__ 节点 + /// Supports two overlay formats: + /// 1. Fragment format: contains fragment@N nodes with target/target-path and __overlay__ + /// 2. Simple format: directly contains __overlay__ node + /// + /// # Example /// - /// # 示例 /// ```ignore - /// // fragment 格式 + /// // Fragment format /// fragment@0 { /// target-path = "/soc"; /// __overlay__ { @@ -210,62 +218,61 @@ impl Fdt { /// }; /// ``` pub fn apply_overlay(&mut self, overlay: &Fdt) -> Result<(), FdtError> { - // 遍历 overlay 根节点的所有子节点 + // Iterate through all children of overlay root node for child in overlay.root.children() { if child.name().starts_with("fragment@") || child.name() == "fragment" { - // fragment 格式 + // Fragment format self.apply_fragment(child)?; } else if child.name() == "__overlay__" { - // 简单格式:直接应用到根节点 + // Simple format: apply directly to root self.merge_overlay_to_root(child)?; } else if child.name() == "__symbols__" || child.name() == "__fixups__" || child.name() == "__local_fixups__" { - // 跳过这些特殊节点 + // Skip these special nodes continue; } } - // 重建 phandle 缓存 + // Rebuild phandle cache self.rebuild_phandle_cache(); Ok(()) } - /// 应用单个 fragment + /// Applies a single fragment from an overlay. fn apply_fragment(&mut self, fragment: &Node) -> Result<(), FdtError> { - // 获取目标路径 + // Get target path let target_path = self.resolve_fragment_target(fragment)?; - // 找到 __overlay__ 子节点 + // Find __overlay__ child node let overlay_node = fragment .get_child("__overlay__") .ok_or(FdtError::NotFound)?; - // 找到目标节点并应用覆盖 - // 需要克隆路径因为后面要修改 self + // Find target node and apply overlay let target_path_owned = target_path.to_string(); - // 应用覆盖到目标节点 + // Apply overlay to target node self.apply_overlay_to_target(&target_path_owned, overlay_node)?; Ok(()) } - /// 解析 fragment 的目标路径 + /// Resolves the target path of a fragment. fn resolve_fragment_target(&self, fragment: &Node) -> Result { - // 优先使用 target-path(字符串路径) + // Prefer target-path (string path) if let Some(prop) = fragment.get_property("target-path") { return Ok(prop.as_str().ok_or(FdtError::Utf8Parse)?.to_string()); } - // 使用 target(phandle 引用) + // Use target (phandle reference) if let Some(prop) = fragment.get_property("target") { let ph = prop.get_u32().ok_or(FdtError::InvalidInput)?; let ph = Phandle::from(ph); - // 通过 phandle 找到节点,然后构建路径 + // Find node by phandle and build path if let Some(node) = self.find_by_phandle(ph) { return Ok(node.path()); } @@ -274,26 +281,26 @@ impl Fdt { Err(FdtError::NotFound) } - /// 将 overlay 应用到目标节点 + /// Applies an overlay to a target node. fn apply_overlay_to_target( &mut self, target_path: &str, overlay_node: &Node, ) -> Result<(), FdtError> { - // 找到目标节点 + // Find target node let mut target = self .get_by_path_mut(target_path) .ok_or(FdtError::NotFound)?; - // 合并 overlay 的属性和子节点 + // Merge overlay properties and child nodes Self::merge_nodes(target.node, overlay_node); Ok(()) } - /// 合并 overlay 节点到根节点 + /// Merges an overlay node to the root node. fn merge_overlay_to_root(&mut self, overlay: &Node) -> Result<(), FdtError> { - // 合并属性和子节点到根节点 + // Merge properties and child nodes to root for prop in overlay.properties() { self.root.set_property(prop.clone()); } @@ -301,10 +308,10 @@ impl Fdt { for child in overlay.children() { let child_name = child.name(); if let Some(existing) = self.root.get_child_mut(child_name) { - // 合并到现有子节点 + // Merge into existing child node Self::merge_nodes(existing, child); } else { - // 添加新子节点 + // Add new child node self.root.add_child(child.clone()); } } @@ -312,29 +319,30 @@ impl Fdt { Ok(()) } - /// 递归合并两个节点 + /// Recursively merges two nodes. fn merge_nodes(target: &mut Node, source: &Node) { - // 合并属性(source 覆盖 target) + // Merge properties (source overrides target) for prop in source.properties() { target.set_property(prop.clone()); } - // 合并子节点 + // Merge child nodes for source_child in source.children() { let child_name = &source_child.name(); if let Some(target_child) = target.get_child_mut(child_name) { - // 递归合并 + // Recursive merge Self::merge_nodes(target_child, source_child); } else { - // 添加新子节点 + // Add new child node target.add_child(source_child.clone()); } } } - /// 删除节点(通过设置 status = "disabled" 或直接删除) + /// Applies an overlay with optional deletion of disabled nodes. /// - /// 如果 overlay 中的节点有 status = "disabled",则禁用目标节点 + /// If a node in the overlay has status = "disabled", the corresponding + /// target node will be disabled or deleted. pub fn apply_overlay_with_delete( &mut self, overlay: &Fdt, @@ -343,7 +351,7 @@ impl Fdt { self.apply_overlay(overlay)?; if delete_disabled { - // 移除所有 status = "disabled" 的节点 + // Remove all nodes with status = "disabled" Self::remove_disabled_nodes(&mut self.root); self.rebuild_phandle_cache(); } @@ -351,9 +359,9 @@ impl Fdt { Ok(()) } - /// 递归移除 disabled 的节点 + /// Recursively removes disabled nodes. fn remove_disabled_nodes(node: &mut Node) { - // 移除 disabled 的子节点 + // Remove disabled child nodes let mut to_remove = Vec::new(); for child in node.children() { if matches!(child.status(), Some(Status::Disabled)) { @@ -365,49 +373,49 @@ impl Fdt { node.remove_child(&child_name); } - // 递归处理剩余子节点 + // Recursively process remaining child nodes for child in node.children_mut() { Self::remove_disabled_nodes(child); } } - /// 通过精确路径删除节点及其子树 - /// 只支持精确路径匹配,不支持模糊匹配 - /// 支持通过别名删除节点,并自动删除对应的别名条目 + /// Removes a node by exact path. + /// + /// Supports exact path matching only. Aliases are automatically resolved. /// - /// # 参数 - /// - `path`: 删除路径,格式如 "soc/gpio@1000" 或 "/soc/gpio@1000" 或别名 + /// # Arguments /// - /// # 返回值 - /// `Ok(Option)`: 如果找到并删除了节点,返回被删除的节点;如果路径不存在,返回 None - /// `Err(FdtError)`: 如果路径格式无效 + /// * `path` - Node path (e.g., "soc/gpio@1000", "/soc/gpio@1000", or an alias) + /// + /// # Returns + /// + /// * `Ok(Some(node))` - The removed node + /// * `Ok(None)` - Path not found + /// * `Err(FdtError)` - Invalid path format + /// + /// # Example /// - /// # 示例 /// ```rust /// # use fdt_edit::{Fdt, Node}; /// let mut fdt = Fdt::new(); /// - /// // 先添加节点再删除 + /// // Add node then remove it /// let mut soc = Node::new("soc"); /// soc.add_child(Node::new("gpio@1000")); /// fdt.root.add_child(soc); /// - /// // 精确删除节点(使用完整路径) + /// // Remove node with exact path /// let removed = fdt.remove_node("/soc/gpio@1000")?; /// assert!(removed.is_some()); - /// - /// // 尝试删除不存在的节点会返回错误 - /// let not_found = fdt.remove_node("/soc/nonexistent"); - /// assert!(not_found.is_err()); /// # Ok::<(), fdt_raw::FdtError>(()) /// ``` pub fn remove_node(&mut self, path: &str) -> Result, FdtError> { let normalized_path = self.normalize_path(path).ok_or(FdtError::InvalidInput)?; - // 直接使用精确路径删除 + // Use exact path for removal let result = self.root.remove_by_path(&normalized_path)?; - // 如果删除成功且结果是 None,说明路径不存在 + // If removal succeeded but result is None, path doesn't exist if result.is_none() { return Err(FdtError::NotFound); } @@ -415,17 +423,17 @@ impl Fdt { Ok(result) } - /// 获取所有节点的深度优先迭代器 - /// - /// 返回包含根节点及其所有子节点的迭代器,按照深度优先遍历顺序 + /// Returns a depth-first iterator over all nodes. pub fn all_nodes(&self) -> impl Iterator> + '_ { NodeIter::new(&self.root) } + /// Returns a mutable depth-first iterator over all nodes. pub fn all_nodes_mut(&mut self) -> impl Iterator> + '_ { NodeIterMut::new(&mut self.root) } + /// Finds nodes by path (supports fuzzy matching). pub fn find_by_path<'a>(&'a self, path: &str) -> impl Iterator> { let path = self .normalize_path(path) @@ -440,6 +448,7 @@ impl Fdt { }) } + /// Gets a node by exact path. pub fn get_by_path<'a>(&'a self, path: &str) -> Option> { let path = self.normalize_path(path)?; NodeIter::new(&self.root).find_map(move |node_ref| { @@ -451,6 +460,7 @@ impl Fdt { }) } + /// Gets a node by exact path (mutable reference). pub fn get_by_path_mut<'a>(&'a mut self, path: &str) -> Option> { let path = self.normalize_path(path)?; NodeIterMut::new(&mut self.root).find_map(move |node_mut| { @@ -462,6 +472,7 @@ impl Fdt { }) } + /// Finds nodes with matching compatible strings. pub fn find_compatible(&self, compatible: &[&str]) -> Vec> { let mut results = Vec::new(); for node_ref in self.all_nodes() { @@ -479,7 +490,7 @@ impl Fdt { results } - /// 序列化为 FDT 二进制数据 + /// Serializes the FDT to binary DTB format. pub fn encode(&self) -> FdtData { FdtEncoder::new(self).encode() } @@ -487,10 +498,10 @@ impl Fdt { impl core::fmt::Display for Fdt { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - // 输出 DTS 头部信息 + // Output DTS header writeln!(f, "/dts-v1/;")?; - // 输出内存保留块 + // Output memory reservation block for reservation in &self.memory_reservations { writeln!( f, @@ -499,7 +510,7 @@ impl core::fmt::Display for Fdt { )?; } - // 输出根节点 + // Output root node writeln!(f, "{}", self.root) } } @@ -510,7 +521,7 @@ impl core::fmt::Debug for Fdt { // Deep debug format with node traversal self.fmt_debug_deep(f) } else { - // Simple debug format (current behavior) + // Simple debug format f.debug_struct("Fdt") .field("boot_cpuid_phys", &self.boot_cpuid_phys) .field("memory_reservations_count", &self.memory_reservations.len()) @@ -523,6 +534,7 @@ impl core::fmt::Debug for Fdt { } impl Fdt { + /// Formats the FDT with detailed debug information. fn fmt_debug_deep(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { writeln!(f, "Fdt {{")?; writeln!(f, " boot_cpuid_phys: 0x{:x},", self.boot_cpuid_phys)?; @@ -534,7 +546,7 @@ impl Fdt { writeln!(f, " phandle_cache_size: {},", self.phandle_cache.len())?; writeln!(f, " nodes:")?; - // 遍历所有节点并打印带缩进的调试信息 + // Iterate through all nodes and print debug info with indentation for (i, node) in self.all_nodes().enumerate() { self.fmt_node_debug(f, &node, 2, i)?; } @@ -542,6 +554,7 @@ impl Fdt { writeln!(f, "}}") } + /// Formats a single node for debug output. fn fmt_node_debug( &self, f: &mut core::fmt::Formatter<'_>, @@ -549,15 +562,15 @@ impl Fdt { indent: usize, index: usize, ) -> core::fmt::Result { - // 打印缩进 + // Print indentation for _ in 0..indent { write!(f, " ")?; } - // 打印节点索引和基本信息 + // Print node index and basic info write!(f, "[{:03}] {}: ", index, node.name())?; - // 根据节点类型打印特定信息 + // Print type-specific information match node.as_ref() { NodeKind::Clock(clock) => { write!(f, "Clock")?; @@ -607,12 +620,12 @@ impl Fdt { } } - // 打印 phandle 信息 + // Print phandle information if let Some(phandle) = node.phandle() { write!(f, ", phandle={}", phandle)?; } - // 打印地址和大小 cells 信息 + // Print address and size cells information if let Some(address_cells) = node.address_cells() { write!(f, ", #address-cells={}", address_cells)?; } diff --git a/fdt-edit/src/lib.rs b/fdt-edit/src/lib.rs index e2abea5..72478ac 100644 --- a/fdt-edit/src/lib.rs +++ b/fdt-edit/src/lib.rs @@ -1,4 +1,48 @@ +//! Device Tree Blob (DTB) editing and manipulation library. +//! +//! This crate provides functionality for creating, modifying, and encoding +//! Flattened Device Tree (FDT) structures. Unlike the parser crates which +//! focus on reading existing device trees, this crate allows you to build +//! and modify device trees programmatically. +//! +//! # Features +//! +//! - `#![no_std]` compatible +//! - Build device trees from scratch +//! - Modify existing device trees +//! - Add/remove nodes and properties +//! - Encode to standard DTB format +//! - Support for overlays +//! +//! # Example +//! +//! ```ignore +//! use fdt_edit::{Fdt, Context, Property, NodeKind}; +//! +//! // Create a new FDT with a context +//! let mut fdt = Fdt::new(&Context::default()); +//! +//! // Add a root node +//! let root = fdt.root_mut(); +//! +//! // Add a memory node +//! let memory = fdt.add_node( +//! root, +//! "memory", +//! NodeKind::Memory +//! ); +//! +//! // Add properties to the memory node +//! fdt.add_property(memory, "reg", Property::Reg(&[ +//! RegInfo { address: 0x80000000, size: 0x10000000 }, +//! ])); +//! +//! // Encode to DTB format +//! let dtb_data = fdt.encode()?; +//! ``` + #![no_std] +#![deny(warnings, missing_docs)] #[macro_use] extern crate alloc; diff --git a/fdt-edit/src/node/clock.rs b/fdt-edit/src/node/clock.rs index 11724b5..826345f 100644 --- a/fdt-edit/src/node/clock.rs +++ b/fdt-edit/src/node/clock.rs @@ -8,47 +8,50 @@ use fdt_raw::Phandle; use crate::node::gerneric::NodeRefGen; -/// 时钟提供者类型 +/// Clock provider type #[derive(Clone, Debug, PartialEq)] pub enum ClockType { - /// 固定时钟 + /// Fixed clock Fixed(FixedClock), - /// 普通时钟提供者 + /// Normal clock provider Normal, } -/// 固定时钟 +/// Fixed clock provider. +/// +/// Represents a fixed-rate clock that always operates at a constant frequency. #[derive(Clone, Debug, PartialEq)] pub struct FixedClock { + /// Optional name for the clock pub name: Option, - /// 时钟频率 (Hz) + /// Clock frequency in Hz pub frequency: u32, - /// 时钟精度 + /// Clock accuracy in ppb (parts per billion) pub accuracy: Option, } -/// 时钟引用,用于解析 clocks 属性 +/// Clock reference, used to parse clocks property /// -/// 根据设备树规范,clocks 属性格式为: +/// According to the device tree specification, the clocks property format is: /// `clocks = <&clock_provider specifier [specifier ...]> [<&clock_provider2 ...>]` /// -/// 每个时钟引用由一个 phandle 和若干个 specifier cells 组成, -/// specifier 的数量由目标 clock provider 的 `#clock-cells` 属性决定。 +/// Each clock reference consists of a phandle and several specifier cells, +/// the number of specifiers is determined by the target clock provider's `#clock-cells` property. #[derive(Clone, Debug)] pub struct ClockRef { - /// 时钟的名称,来自 clock-names 属性 + /// Clock name, from clock-names property pub name: Option, - /// 时钟提供者的 phandle + /// Phandle of the clock provider pub phandle: Phandle, - /// provider 的 #clock-cells 值 + /// #clock-cells value of the provider pub cells: u32, - /// 时钟选择器(specifier),通常第一个值用于选择时钟输出 - /// 长度由 provider 的 #clock-cells 决定 + /// Clock selector (specifier), usually the first value is used to select clock output + /// Length is determined by provider's #clock-cells pub specifier: Vec, } impl ClockRef { - /// 创建一个新的时钟引用 + /// Create a new clock reference pub fn new(phandle: Phandle, cells: u32, specifier: Vec) -> Self { Self { name: None, @@ -58,7 +61,7 @@ impl ClockRef { } } - /// 创建一个带名称的时钟引用 + /// Create a named clock reference pub fn with_name( name: Option, phandle: Phandle, @@ -73,10 +76,10 @@ impl ClockRef { } } - /// 获取选择器的第一个值(通常用于选择时钟输出) + /// Get the first value of the selector (usually used to select clock output) /// - /// 只有当 `cells > 0` 时才返回选择器值, - /// 因为 `#clock-cells = 0` 的 provider 不需要选择器。 + /// Only returns a selector value when `cells > 0`, + /// because providers with `#clock-cells = 0` don't need a selector. pub fn select(&self) -> Option { if self.cells > 0 { self.specifier.first().copied() @@ -86,23 +89,32 @@ impl ClockRef { } } -/// 时钟提供者节点引用 +/// Clock provider node reference. +/// +/// Provides specialized access to clock provider nodes and their properties. #[derive(Clone)] pub struct NodeRefClock<'a> { + /// The underlying generic node reference pub node: NodeRefGen<'a>, + /// Names of clock outputs from this provider pub clock_output_names: Vec, + /// Value of the `#clock-cells` property pub clock_cells: u32, + /// The type of clock provider pub kind: ClockType, } impl<'a> NodeRefClock<'a> { + /// Attempts to create a clock provider reference from a generic node. + /// + /// Returns `Err` with the original node if it doesn't have a `#clock-cells` property. pub fn try_from(node: NodeRefGen<'a>) -> Result> { - // 检查是否有时钟提供者属性 + // Check if it has clock provider properties if node.find_property("#clock-cells").is_none() { return Err(node); } - // 获取 clock-output-names 属性 + // Get clock-output-names property let clock_output_names = if let Some(prop) = node.find_property("clock-output-names") { let iter = prop.as_str_iter(); iter.map(|s| s.to_string()).collect() @@ -110,13 +122,13 @@ impl<'a> NodeRefClock<'a> { Vec::new() }; - // 获取 #clock-cells + // Get #clock-cells let clock_cells = node .find_property("#clock-cells") .and_then(|prop| prop.get_u32()) .unwrap_or(0); - // 判断时钟类型 + // Determine clock type let kind = if node.compatibles().any(|c| c == "fixed-clock") { let frequency = node .find_property("clock-frequency") @@ -144,15 +156,15 @@ impl<'a> NodeRefClock<'a> { }) } - /// 获取时钟输出名称(用于 provider) + /// Get clock output name (for provider) pub fn output_name(&self, index: usize) -> Option<&str> { self.clock_output_names.get(index).map(|s| s.as_str()) } - /// 解析 clocks 属性,返回时钟引用列表 + /// Parse clocks property, return list of clock references /// - /// 通过查找每个 phandle 对应的 clock provider 的 #clock-cells, - /// 正确解析 specifier 的长度。 + /// By looking up each phandle's corresponding clock provider's #clock-cells, + /// correctly parse the specifier length. pub fn clocks(&self) -> Vec { let Some(prop) = self.find_property("clocks") else { return Vec::new(); @@ -162,7 +174,7 @@ impl<'a> NodeRefClock<'a> { let mut data = prop.as_reader(); let mut index = 0; - // 获取 clock-names 用于命名 + // Get clock-names for naming let clock_names = if let Some(prop) = self.find_property("clock-names") { let iter = prop.as_str_iter(); iter.map(|s| s.to_string()).collect() @@ -173,35 +185,35 @@ impl<'a> NodeRefClock<'a> { while let Some(phandle_raw) = data.read_u32() { let phandle = Phandle::from(phandle_raw); - // 通过 phandle 查找 provider 节点,获取其 #clock-cells + // Look up provider node by phandle, get its #clock-cells let clock_cells = if let Some(provider) = self.ctx.find_by_phandle(phandle) { provider .get_property("#clock-cells") .and_then(|p| p.get_u32()) - .unwrap_or(1) // 默认 1 cell + .unwrap_or(1) // Default 1 cell } else { - 1 // 默认 1 cell + 1 // Default 1 cell }; - // 读取 specifier(根据 provider 的 #clock-cells) + // Read specifier (based on provider's #clock-cells) let mut specifier = Vec::with_capacity(clock_cells as usize); let mut complete = true; for _ in 0..clock_cells { if let Some(val) = data.read_u32() { specifier.push(val); } else { - // 数据不足,停止解析 + // Insufficient data, stop parsing complete = false; break; } } - // 只有完整的 clock reference 才添加 + // Only add complete clock reference if !complete { break; } - // 从 clock-names 获取对应的名称 + // Get corresponding name from clock-names let name = clock_names.get(index).cloned(); clocks.push(ClockRef::with_name(name, phandle, clock_cells, specifier)); diff --git a/fdt-edit/src/node/display.rs b/fdt-edit/src/node/display.rs index 4d6050c..47845e6 100644 --- a/fdt-edit/src/node/display.rs +++ b/fdt-edit/src/node/display.rs @@ -7,7 +7,7 @@ use crate::{ NodeRefMemory, Property, }; -/// Node 的 DTS 显示格式化器 +/// Formatter for displaying nodes in DTS (device tree source) format. pub struct NodeDisplay<'a> { node: &'a Node, indent: usize, @@ -16,6 +16,7 @@ pub struct NodeDisplay<'a> { } impl<'a> NodeDisplay<'a> { + /// Creates a new display formatter for the given node. pub fn new(node: &'a Node) -> Self { Self { node, @@ -25,16 +26,19 @@ impl<'a> NodeDisplay<'a> { } } + /// Sets the indentation level for nested nodes. pub fn indent(mut self, indent: usize) -> Self { self.indent = indent; self } + /// Sets whether to show address values in properties. pub fn show_address(mut self, show: bool) -> Self { self.show_address = show; self } + /// Sets whether to show size values in properties. pub fn show_size(mut self, show: bool) -> Self { self.show_size = show; self @@ -87,10 +91,10 @@ impl<'a> NodeDisplay<'a> { let mut first = true; write!(f, "<")?; - // 获取 parent 的 address-cells 和 size-cells - // 这里需要从上下文获取,暂时使用默认值 - let address_cells = 2; // 默认值 - let size_cells = 1; // 默认值 + // Get parent's address-cells and size-cells + // Need to get from context, using default values for now + let address_cells = 2; // Default value + let size_cells = 1; // Default value while let (Some(addr), Some(size)) = ( reader.read_cells(address_cells), @@ -137,7 +141,7 @@ impl<'a> NodeDisplay<'a> { } else if let Some(u64_val) = prop.get_u64() { write!(f, "<0x{:x}>;", u64_val) } else { - // 尝试格式化为字节数组 + // Try to format as byte array let mut reader = prop.as_reader(); let mut first = true; write!(f, "<")?; @@ -158,13 +162,13 @@ impl<'a> fmt::Display for NodeDisplay<'a> { self.format_indent(f)?; if self.node.name.is_empty() { - // 根节点 + // Root node writeln!(f, "/ {{")?; } else { - // 普通节点 + // Regular node write!(f, "{}", self.node.name)?; - // 检查是否有地址和大小属性需要显示 + // Check if there are address and size properties to display let mut props = Vec::new(); for prop in self.node.properties() { if prop.name() != "reg" { @@ -180,14 +184,14 @@ impl<'a> fmt::Display for NodeDisplay<'a> { } } - // 输出属性 + // Output properties for prop in self.node.properties() { if prop.name() != "reg" || self.show_address || self.show_size { self.format_property(prop, f)?; } } - // 输出子节点 + // Output child nodes for child in self.node.children() { let child_display = NodeDisplay::new(child) .indent(self.indent + 1) @@ -196,7 +200,7 @@ impl<'a> fmt::Display for NodeDisplay<'a> { write!(f, "{}", child_display)?; } - // 关闭节点 + // Close node self.format_indent(f)?; writeln!(f, "}};")?; @@ -224,7 +228,9 @@ impl fmt::Debug for Node { } } -/// NodeRef 的显示格式化器 +/// Display formatter for node references. +/// +/// Formats specialized node references with type-specific information. pub struct NodeRefDisplay<'a> { node_ref: &'a NodeRef<'a>, indent: usize, @@ -232,6 +238,7 @@ pub struct NodeRefDisplay<'a> { } impl<'a> NodeRefDisplay<'a> { + /// Creates a new display formatter for the given node reference. pub fn new(node_ref: &'a NodeRef<'a>) -> Self { Self { node_ref, @@ -240,11 +247,13 @@ impl<'a> NodeRefDisplay<'a> { } } + /// Sets the indentation level for nested nodes. pub fn indent(mut self, indent: usize) -> Self { self.indent = indent; self } + /// Sets whether to show detailed type information. pub fn show_details(mut self, show: bool) -> Self { self.show_details = show; self @@ -323,7 +332,7 @@ impl<'a> fmt::Display for NodeRefDisplay<'a> { self.format_type_info(f)?; writeln!(f)?; - // 添加缩进并显示 DTS + // Add indentation and display DTS let dts_display = NodeDisplay::new(self.node_ref).indent(self.indent + 1); write!(f, "{}", dts_display)?; } else { diff --git a/fdt-edit/src/node/gerneric.rs b/fdt-edit/src/node/gerneric.rs index a92961e..9d96810 100644 --- a/fdt-edit/src/node/gerneric.rs +++ b/fdt-edit/src/node/gerneric.rs @@ -7,9 +7,15 @@ use fdt_raw::RegInfo; use crate::{Context, Node, NodeMut, Property}; +/// Generic node reference with context. +/// +/// Provides basic node access operations with context-aware functionality +/// for traversing and manipulating device tree nodes. #[derive(Clone)] pub struct NodeRefGen<'a> { + /// The underlying node reference pub node: &'a Node, + /// The parsing context containing parent information and path pub ctx: Context<'a>, } @@ -54,8 +60,14 @@ impl Deref for NodeRefGen<'_> { } } +/// Generic mutable node reference with context. +/// +/// Provides mutable node operations with context-aware functionality +/// for modifying device tree nodes and their properties. pub struct NodeMutGen<'a> { + /// The underlying mutable node reference pub node: &'a mut Node, + /// The parsing context containing parent information and path pub ctx: Context<'a>, } @@ -83,12 +95,10 @@ impl<'a> NodeMutGen<'a> { self.op().regs() } - /// 设置 reg 属性 + /// Sets the reg property with automatic address translation. /// - /// # 参数 - /// - `regs`: RegInfo 列表,其中 `address` 是 CPU 地址(物理地址) - /// - /// 该方法会根据父节点的 ranges 将 CPU 地址转换为 bus 地址后存储 + /// This method converts CPU physical addresses to bus addresses using the + /// parent node's ranges mapping before storing them in the reg property. pub fn set_regs(&mut self, regs: &[RegInfo]) { let address_cells = self.ctx.parent_address_cells() as usize; let size_cells = self.ctx.parent_size_cells() as usize; @@ -97,22 +107,22 @@ impl<'a> NodeMutGen<'a> { let mut data = Vec::new(); for reg in regs { - // 将 CPU 地址转换为 bus 地址 + // Convert CPU address to bus address let mut bus_address = reg.address; if let Some(ref ranges) = ranges { for r in ranges { - // 检查 CPU 地址是否在 ranges 映射范围内 + // Check if CPU address is within ranges mapping range if reg.address >= r.parent_bus_address && reg.address < r.parent_bus_address + r.length { - // 反向转换:cpu_address -> bus_address + // Reverse conversion: cpu_address -> bus_address bus_address = reg.address - r.parent_bus_address + r.child_bus_address; break; } } } - // 写入 bus address (big-endian) + // Write bus address (big-endian) if address_cells == 1 { data.extend_from_slice(&(bus_address as u32).to_be_bytes()); } else if address_cells == 2 { @@ -120,7 +130,7 @@ impl<'a> NodeMutGen<'a> { data.extend_from_slice(&((bus_address & 0xFFFF_FFFF) as u32).to_be_bytes()); } - // 写入 size (big-endian) + // Write size (big-endian) if size_cells == 1 { let size = reg.size.unwrap_or(0); data.extend_from_slice(&(size as u32).to_be_bytes()); @@ -135,6 +145,11 @@ impl<'a> NodeMutGen<'a> { self.node.set_property(prop); } + /// Adds a child node to this node. + /// + /// This method attaches a child node to the current node, updating the + /// context to include the parent-child relationship, and returns a + /// mutable reference to the newly added child. pub fn add_child(&mut self, child: Node) -> NodeMut<'a> { let name = child.name().to_string(); let mut ctx = self.ctx.clone(); @@ -165,20 +180,37 @@ impl Debug for NodeMutGen<'_> { } } +/// Internal helper struct for node operations with context. +/// +/// This struct provides common operations that are shared between +/// `NodeRefGen` and `NodeMutGen`, avoiding code duplication. struct RefOp<'a> { + /// Reference to the parsing context ctx: &'a Context<'a>, + /// Reference to the node being operated on node: &'a Node, } impl<'a> RefOp<'a> { + /// Constructs the full path of the node. + /// + /// Combines the current context path with the node name to create + /// the full device tree path. fn path(&self) -> String { self.ctx.current_path() + "/" + self.node.name() } + /// Checks if the node's path exactly matches the given path. fn ref_path_eq(&self, path: &str) -> bool { self.path() == path } + /// Checks if the node's path matches the given path using fuzzy matching. + /// + /// Fuzzy matching allows comparing paths without requiring the exact + /// address portion (the `@address` suffix) to match unless explicitly + /// specified. This is useful for matching nodes by name when the + /// specific address is not important. fn ref_path_eq_fuzzy(&self, path: &str) -> bool { let mut want = path.trim_matches('/').split("/"); let got_path = self.path(); @@ -221,13 +253,18 @@ impl<'a> RefOp<'a> { true } + /// Parses the reg property and returns a list of register regions. + /// + /// This method reads the reg property and performs address translation + /// from child bus addresses to CPU physical addresses using the parent's + /// ranges mapping. fn regs(&self) -> Option> { let prop = self.node.get_property("reg")?; let mut iter = prop.as_reader(); let address_cells = self.ctx.parent_address_cells() as usize; let size_cells = self.ctx.parent_size_cells() as usize; - // 从上下文获取当前 ranges + // Get current ranges from context let ranges = self.ctx.current_ranges(); let mut out = vec![]; let mut size; @@ -263,9 +300,16 @@ impl<'a> RefOp<'a> { } } +/// Fixed register region with address translation information. +/// +/// Represents a single register region from the reg property with both +/// the bus address (stored in the DTB) and the translated CPU physical address. #[derive(Clone, Copy, Debug)] pub struct RegFixed { + /// CPU physical address after translation pub address: u64, + /// Child bus address as stored in the reg property pub child_bus_address: u64, + /// Size of the register region (None if size-cells is 0) pub size: Option, } diff --git a/fdt-edit/src/node/interrupt_controller.rs b/fdt-edit/src/node/interrupt_controller.rs index cee6f71..24f86ae 100644 --- a/fdt-edit/src/node/interrupt_controller.rs +++ b/fdt-edit/src/node/interrupt_controller.rs @@ -4,13 +4,19 @@ use alloc::vec::Vec; use crate::node::gerneric::NodeRefGen; -/// 中断控制器节点引用 +/// Interrupt controller node reference. +/// +/// Provides specialized access to interrupt controller nodes and their properties. #[derive(Clone)] pub struct NodeRefInterruptController<'a> { + /// The underlying generic node reference pub node: NodeRefGen<'a>, } impl<'a> NodeRefInterruptController<'a> { + /// Attempts to create an interrupt controller reference from a generic node. + /// + /// Returns `Err` with the original node if it's not an interrupt controller. pub fn try_from(node: NodeRefGen<'a>) -> Result> { if !is_interrupt_controller_node(&node) { return Err(node); @@ -18,27 +24,28 @@ impl<'a> NodeRefInterruptController<'a> { Ok(Self { node }) } - /// 获取 #interrupt-cells 值 + /// Get #interrupt-cells value /// - /// 这决定了引用此控制器的中断需要多少个 cell 来描述 + /// This determines how many cells are needed to describe interrupts + /// referencing this controller pub fn interrupt_cells(&self) -> Option { self.find_property("#interrupt-cells") .and_then(|prop| prop.get_u32()) } - /// 获取 #address-cells 值(用于 interrupt-map) + /// Get #address-cells value (used for interrupt-map) pub fn interrupt_address_cells(&self) -> Option { self.find_property("#address-cells") .and_then(|prop| prop.get_u32()) } - /// 检查是否是中断控制器 + /// Check if this is an interrupt controller pub fn is_interrupt_controller(&self) -> bool { - // 检查 interrupt-controller 属性(空属性标记) + // Check for interrupt-controller property (empty property marker) self.find_property("interrupt-controller").is_some() } - /// 获取 compatible 列表 + /// Get compatible list pub fn compatibles(&self) -> Vec<&str> { self.node.compatibles().collect() } @@ -52,13 +59,13 @@ impl<'a> Deref for NodeRefInterruptController<'a> { } } -/// 检查节点是否是中断控制器 +/// Check if node is an interrupt controller fn is_interrupt_controller_node(node: &NodeRefGen) -> bool { - // 名称以 interrupt-controller 开头 + // Name starts with interrupt-controller if node.name().starts_with("interrupt-controller") { return true; } - // 或者有 interrupt-controller 属性 + // Or has interrupt-controller property node.find_property("interrupt-controller").is_some() } diff --git a/fdt-edit/src/node/iter.rs b/fdt-edit/src/node/iter.rs index e178977..bf230b4 100644 --- a/fdt-edit/src/node/iter.rs +++ b/fdt-edit/src/node/iter.rs @@ -11,38 +11,51 @@ use crate::{ node::gerneric::{NodeMutGen, NodeRefGen}, }; +/// Enum representing a reference to a specialized node type. +/// +/// This enum provides automatic type detection and dispatch for different +/// node types based on their properties and compatible strings. #[derive(Clone)] pub enum NodeRef<'a> { + /// Generic node without specific type Gerneric(NodeRefGen<'a>), + /// PCI bridge node Pci(NodeRefPci<'a>), + /// Clock provider node Clock(NodeRefClock<'a>), + /// Interrupt controller node InterruptController(NodeRefInterruptController<'a>), + /// Memory reservation node Memory(NodeRefMemory<'a>), } impl<'a> NodeRef<'a> { + /// Creates a new node reference with automatic type detection. + /// + /// Attempts to create specialized references (PCI, Clock, etc.) based on + /// the node's properties and compatible strings. pub fn new(node: &'a Node, ctx: Context<'a>) -> Self { let mut g = NodeRefGen { node, ctx }; - // 先尝试 PCI + // Try PCI first g = match NodeRefPci::try_from(g) { Ok(pci) => return Self::Pci(pci), Err(v) => v, }; - // 再尝试 Clock + // Then try Clock g = match NodeRefClock::try_from(g) { Ok(clock) => return Self::Clock(clock), Err(v) => v, }; - // 然后尝试 InterruptController + // Then try InterruptController g = match NodeRefInterruptController::try_from(g) { Ok(ic) => return Self::InterruptController(ic), Err(v) => v, }; - // 最后尝试 Memory + // Finally try Memory g = match NodeRefMemory::try_from(g) { Ok(mem) => return Self::Memory(mem), Err(v) => v, @@ -51,7 +64,7 @@ impl<'a> NodeRef<'a> { Self::Gerneric(g) } - /// 获取节点的具体类型用于模式匹配 + /// Get concrete node type for pattern matching pub fn as_ref(&self) -> NodeKind<'a> { match self { NodeRef::Clock(clock) => NodeKind::Clock(clock.clone()), @@ -77,11 +90,16 @@ impl<'a> Deref for NodeRef<'a> { } } +/// Enum representing a mutable reference to a node. +/// +/// Currently only generic mutable nodes are supported. pub enum NodeMut<'a> { + /// Generic mutable node reference Gerneric(NodeMutGen<'a>), } impl<'a> NodeMut<'a> { + /// Creates a new mutable node reference. pub fn new(node: &'a mut Node, ctx: Context<'a>) -> Self { Self::Gerneric(NodeMutGen { node, ctx }) } @@ -105,6 +123,9 @@ impl<'a> DerefMut for NodeMut<'a> { } } +/// Iterator over nodes in a device tree. +/// +/// Provides depth-first traversal with automatic type detection for each node. pub struct NodeIter<'a> { ctx: Context<'a>, node: Option<&'a Node>, @@ -112,10 +133,11 @@ pub struct NodeIter<'a> { } impl<'a> NodeIter<'a> { + /// Creates a new node iterator starting from the root node. pub fn new(root: &'a Node) -> Self { let mut ctx = Context::new(); - // 预先构建整棵树的 phandle_map - // 这样在遍历任何节点时都能通过 phandle 找到其他节点 + // Build phandle_map for entire tree upfront + // This allows finding any node by phandle during traversal Context::build_phandle_map_from_node(root, &mut ctx.phandle_map); Self { @@ -131,7 +153,7 @@ impl<'a> Iterator for NodeIter<'a> { fn next(&mut self) -> Option { if let Some(n) = self.node.take() { - // 返回当前节点,并将其子节点压入栈中 + // Return current node and push its children onto stack let ctx = self.ctx.clone(); self.ctx.push(n); self.stack.push(n.children.iter()); @@ -141,20 +163,23 @@ impl<'a> Iterator for NodeIter<'a> { let iter = self.stack.last_mut()?; if let Some(child) = iter.next() { - // 返回子节点,并将其子节点压入栈中 + // Return child node and push its children onto stack let ctx = self.ctx.clone(); self.ctx.push(child); self.stack.push(child.children.iter()); return Some(NodeRef::new(child, ctx)); } - // 当前迭代器耗尽,弹出栈顶 + // Current iterator exhausted, pop from stack self.stack.pop(); self.ctx.parents.pop(); self.next() } } +/// Mutable iterator over nodes in a device tree. +/// +/// Provides depth-first traversal with mutable access to nodes. pub struct NodeIterMut<'a> { ctx: Context<'a>, node: Option>, @@ -162,7 +187,9 @@ pub struct NodeIterMut<'a> { _marker: core::marker::PhantomData<&'a mut Node>, } -/// 原始指针子节点迭代器 +/// Raw pointer-based child node iterator. +/// +/// Used internally by `NodeIterMut` to avoid borrow conflicts. struct RawChildIter { ptr: *mut Node, end: *mut Node, @@ -187,13 +214,14 @@ impl RawChildIter { } impl<'a> NodeIterMut<'a> { + /// Creates a new mutable node iterator starting from the root node. pub fn new(root: &'a mut Node) -> Self { let mut ctx = Context::new(); - // 预先构建整棵树的 phandle_map - // 使用原始指针来避免借用冲突 + // Build phandle_map for entire tree upfront + // Use raw pointers to avoid borrow conflicts let root_ptr = root as *mut Node; unsafe { - // 用不可变引用构建 phandle_map + // Build phandle_map using immutable reference Context::build_phandle_map_from_node(&*root_ptr, &mut ctx.phandle_map); } @@ -211,7 +239,7 @@ impl<'a> Iterator for NodeIterMut<'a> { fn next(&mut self) -> Option { if let Some(node_ptr) = self.node.take() { - // 返回当前节点,并将其子节点压入栈中 + // Return current node and push its children onto stack let ctx = self.ctx.clone(); unsafe { let node_ref = node_ptr.as_ref(); @@ -225,7 +253,7 @@ impl<'a> Iterator for NodeIterMut<'a> { let iter = self.stack.last_mut()?; if let Some(child_ptr) = iter.next() { - // 返回子节点,并将其子节点压入栈中 + // Return child node and push its children onto stack let ctx = self.ctx.clone(); unsafe { let child_ref = child_ptr.as_ref(); @@ -236,7 +264,7 @@ impl<'a> Iterator for NodeIterMut<'a> { } } - // 当前迭代器耗尽,弹出栈顶 + // Current iterator exhausted, pop from stack self.stack.pop(); self.ctx.parents.pop(); self.next() diff --git a/fdt-edit/src/node/memory.rs b/fdt-edit/src/node/memory.rs index 2d17aaf..f329cdf 100644 --- a/fdt-edit/src/node/memory.rs +++ b/fdt-edit/src/node/memory.rs @@ -8,45 +8,53 @@ use fdt_raw::MemoryRegion; use crate::node::gerneric::NodeRefGen; -/// Memory 节点,描述物理内存布局 +/// Memory node describing physical memory layout. #[derive(Clone, Debug)] pub struct NodeMemory { + /// Node name pub name: String, } impl NodeMemory { + /// Creates a new memory node with the given name. pub fn new(name: &str) -> Self { Self { name: name.to_string(), } } - /// 获取节点名称 + /// Get node name pub fn name(&self) -> &str { &self.name } - /// 获取内存区域列表 - /// 注意:这是一个简单的实现,实际使用时需要从实际的 FDT 节点中解析 + /// Get memory region list + /// Note: This is a simple implementation, in actual use needs to parse from real FDT nodes pub fn regions(&self) -> Vec { - // 这个方法在测试中主要用来检查是否为空 + // This method is mainly used in tests to check if empty Vec::new() } - /// 获取 device_type 属性 - /// 注意:这是一个简单的实现,返回 "memory" + /// Get device_type property + /// Note: This is a simple implementation, returns "memory" pub fn device_type(&self) -> Option<&str> { Some("memory") } } -/// Memory 节点引用 +/// Memory node reference. +/// +/// Provides specialized access to memory nodes and their regions. #[derive(Clone)] pub struct NodeRefMemory<'a> { + /// The underlying generic node reference pub node: NodeRefGen<'a>, } impl<'a> NodeRefMemory<'a> { + /// Attempts to create a memory node reference from a generic node. + /// + /// Returns `Err` with the original node if it's not a memory node. pub fn try_from(node: NodeRefGen<'a>) -> Result> { if !is_memory_node(&node) { return Err(node); @@ -54,13 +62,13 @@ impl<'a> NodeRefMemory<'a> { Ok(Self { node }) } - /// 获取内存区域列表 + /// Get memory region list pub fn regions(&self) -> Vec { let mut regions = Vec::new(); if let Some(reg_prop) = self.find_property("reg") { let mut reader = reg_prop.as_reader(); - // 获取 parent 的 address-cells 和 size-cells + // Get parent's address-cells and size-cells let address_cells = self.ctx.parent_address_cells() as usize; let size_cells = self.ctx.parent_size_cells() as usize; @@ -74,7 +82,7 @@ impl<'a> NodeRefMemory<'a> { regions } - /// 获取 device_type 属性 + /// Get device_type property pub fn device_type(&self) -> Option<&str> { self.find_property("device_type") .and_then(|prop| prop.as_str()) @@ -89,15 +97,15 @@ impl<'a> Deref for NodeRefMemory<'a> { } } -/// 检查节点是否是 memory 节点 +/// Check if node is a memory node fn is_memory_node(node: &NodeRefGen) -> bool { - // 检查 device_type 属性是否为 "memory" + // Check if device_type property is "memory" if let Some(device_type) = node.device_type() && device_type == "memory" { return true; } - // 或者节点名以 "memory" 开头 + // Or node name starts with "memory" node.name().starts_with("memory") } diff --git a/fdt-edit/src/node/mod.rs b/fdt-edit/src/node/mod.rs index 3bdc9e7..48f6b79 100644 --- a/fdt-edit/src/node/mod.rs +++ b/fdt-edit/src/node/mod.rs @@ -1,3 +1,8 @@ +//! Device tree node representation and manipulation. +//! +//! This module provides the `Node` type which represents a mutable device tree node +//! with properties, child nodes, and methods for traversal and modification. + use core::fmt::Debug; use alloc::{ @@ -24,28 +29,45 @@ pub use iter::*; pub use memory::*; pub use pci::*; -/// 节点类型枚举,用于模式匹配 +/// Node type enum for pattern matching. +/// +/// Represents different specialized node types that can be identified +/// by their compatible strings and properties. #[derive(Clone, Debug)] pub enum NodeKind<'a> { + /// Clock provider node Clock(NodeRefClock<'a>), + /// PCI bridge node Pci(NodeRefPci<'a>), + /// Interrupt controller node InterruptController(NodeRefInterruptController<'a>), + /// Memory reservation node Memory(NodeRefMemory<'a>), + /// Generic node (no specialized type) Generic(NodeRefGen<'a>), } +/// A mutable device tree node. +/// +/// Represents a node in the device tree with a name, properties, and child nodes. +/// Provides efficient property and child lookup through cached indices while +/// maintaining insertion order. #[derive(Clone)] pub struct Node { + /// Node name (without path) pub name: String, - /// 属性列表(保持原始顺序) + /// Property list (maintains original order) pub(crate) properties: Vec, - /// 属性名到索引的映射(用于快速查找) + /// Property name to index mapping (for fast lookup) pub(crate) prop_cache: BTreeMap, + /// Child nodes children: Vec, + /// Child name to index mapping (for fast lookup) name_cache: BTreeMap, } impl Node { + /// Creates a new node with the given name. pub fn new(name: &str) -> Self { Self { name: name.to_string(), @@ -56,28 +78,38 @@ impl Node { } } + /// Returns the node's name. pub fn name(&self) -> &str { &self.name } + /// Returns an iterator over the node's properties. pub fn properties(&self) -> impl Iterator { self.properties.iter() } + /// Returns a slice of the node's children. pub fn children(&self) -> &[Node] { &self.children } + /// Returns a mutable iterator over the node's children. pub fn children_mut(&mut self) -> impl Iterator { self.children.iter_mut() } + /// Adds a child node to this node. + /// + /// Updates the name cache for fast lookups. pub fn add_child(&mut self, child: Node) { let index = self.children.len(); self.name_cache.insert(child.name.clone(), index); self.children.push(child); } + /// Adds a property to this node. + /// + /// Updates the property cache for fast lookups. pub fn add_property(&mut self, prop: Property) { let name = prop.name.clone(); let index = self.properties.len(); @@ -85,6 +117,9 @@ impl Node { self.properties.push(prop); } + /// Gets a child node by name. + /// + /// Uses the cache for fast lookup, with a fallback to linear search. pub fn get_child(&self, name: &str) -> Option<&Node> { if let Some(&index) = self.name_cache.get(name) && let Some(child) = self.children.get(index) @@ -96,6 +131,9 @@ impl Node { self.children.iter().find(|c| c.name == name) } + /// Gets a mutable reference to a child node by name. + /// + /// Rebuilds the cache on mismatch to keep indices synchronized. pub fn get_child_mut(&mut self, name: &str) -> Option<&mut Node> { if let Some(&index) = self.name_cache.get(name) && index < self.children.len() @@ -110,6 +148,9 @@ impl Node { self.children.get_mut(pos) } + /// Removes a child node by name. + /// + /// Rebuilds the name cache after removal. pub fn remove_child(&mut self, name: &str) -> Option { let index = self .name_cache @@ -125,33 +166,39 @@ impl Node { Some(removed) } + /// Sets a property, adding it if it doesn't exist or updating if it does. pub fn set_property(&mut self, prop: Property) { let name = prop.name.clone(); if let Some(&idx) = self.prop_cache.get(&name) { - // 更新已存在的属性 + // Update existing property self.properties[idx] = prop; } else { - // 添加新属性 + // Add new property let idx = self.properties.len(); self.prop_cache.insert(name, idx); self.properties.push(prop); } } + /// Gets a property by name. pub fn get_property(&self, name: &str) -> Option<&Property> { self.prop_cache.get(name).map(|&idx| &self.properties[idx]) } + /// Gets a mutable reference to a property by name. pub fn get_property_mut(&mut self, name: &str) -> Option<&mut Property> { self.prop_cache .get(name) .map(|&idx| &mut self.properties[idx]) } + /// Removes a property by name. + /// + /// Updates indices after removal to keep the cache consistent. pub fn remove_property(&mut self, name: &str) -> Option { if let Some(&idx) = self.prop_cache.get(name) { self.prop_cache.remove(name); - // 重建索引(移除元素后需要更新后续索引) + // Rebuild indices (need to update subsequent indices after removal) let prop = self.properties.remove(idx); for (_, v) in self.prop_cache.iter_mut() { if *v > idx { @@ -164,28 +211,33 @@ impl Node { } } + /// Returns the `#address-cells` property value. pub fn address_cells(&self) -> Option { self.get_property("#address-cells") .and_then(|prop| prop.get_u32()) } + /// Returns the `#size-cells` property value. pub fn size_cells(&self) -> Option { self.get_property("#size-cells") .and_then(|prop| prop.get_u32()) } + /// Returns the `phandle` property value. pub fn phandle(&self) -> Option { self.get_property("phandle") .and_then(|prop| prop.get_u32()) .map(Phandle::from) } + /// Returns the `interrupt-parent` property value. pub fn interrupt_parent(&self) -> Option { self.get_property("interrupt-parent") .and_then(|prop| prop.get_u32()) .map(Phandle::from) } + /// Returns the `status` property value. pub fn status(&self) -> Option { let prop = self.get_property("status")?; let s = prop.as_str()?; @@ -196,16 +248,19 @@ impl Node { } } + /// Parses the `ranges` property for address translation. + /// + /// Returns a vector of range entries mapping child bus addresses to parent bus addresses. pub fn ranges(&self, parent_address_cells: u32) -> Option> { let prop = self.get_property("ranges")?; let mut entries = Vec::new(); let mut reader = prop.as_reader(); - // 当前节点的 #address-cells 用于子节点地址 + // Current node's #address-cells for child node addresses let child_address_cells = self.address_cells().unwrap_or(2) as usize; - // 父节点的 #address-cells 用于父总线地址 + // Parent node's #address-cells for parent bus addresses let parent_addr_cells = parent_address_cells as usize; - // 当前节点的 #size-cells + // Current node's #size-cells let size_cells = self.size_cells().unwrap_or(1) as usize; while let (Some(child_addr), Some(parent_addr), Some(size)) = ( @@ -223,6 +278,7 @@ impl Node { Some(entries) } + /// Rebuilds the name cache from the current children list. fn rebuild_name_cache(&mut self) { self.name_cache.clear(); for (idx, child) in self.children.iter().enumerate() { @@ -230,11 +286,13 @@ impl Node { } } + /// Returns the `compatible` property as a string iterator. pub fn compatible(&self) -> Option> { let prop = self.get_property("compatible")?; Some(prop.as_str_iter()) } + /// Returns an iterator over all compatible strings. pub fn compatibles(&self) -> impl Iterator { self.get_property("compatible") .map(|prop| prop.as_str_iter()) @@ -242,31 +300,36 @@ impl Node { .flatten() } + /// Returns the `device_type` property value. pub fn device_type(&self) -> Option<&str> { let prop = self.get_property("device_type")?; prop.as_str() } - /// 通过精确路径删除子节点及其子树 - /// 只支持精确路径匹配,不支持模糊匹配 + /// Removes a child node and its subtree by exact path. + /// + /// Only supports exact path matching, not wildcard matching. + /// + /// # Arguments /// - /// # 参数 - /// - `path`: 删除路径,格式如 "soc/gpio@1000" 或 "/soc/gpio@1000" + /// * `path` - The removal path, format: "soc/gpio@1000" or "/soc/gpio@1000" /// - /// # 返回值 - /// `Ok(Option)`: 如果找到并删除了节点,返回被删除的节点;如果路径不存在,返回 None - /// `Err(FdtError)`: 如果路径格式无效 + /// # Returns + /// + /// * `Ok(Option)` - The removed node if found, None if path doesn't exist + /// * `Err(FdtError)` - If the path format is invalid + /// + /// # Example /// - /// # 示例 /// ```rust /// # use fdt_edit::Node; /// let mut root = Node::new(""); - /// // 添加测试节点 + /// // Add test nodes /// let mut soc = Node::new("soc"); /// soc.add_child(Node::new("gpio@1000")); /// root.add_child(soc); /// - /// // 精确删除节点 + /// // Remove node by exact path /// let removed = root.remove_by_path("soc/gpio@1000")?; /// assert!(removed.is_some()); /// # Ok::<(), fdt_raw::FdtError>(()) @@ -282,35 +345,37 @@ impl Node { return Err(fdt_raw::FdtError::InvalidInput); } if parts.len() == 1 { - // 删除直接子节点(精确匹配) + // Remove direct child (exact match) let child_name = parts[0]; Ok(self.remove_child(child_name)) } else { - // 需要递归到父节点进行删除 + // Need to recurse to parent node for removal self.remove_child_recursive(&parts, 0) } } - /// 递归删除子节点的实现 - /// 找到要删除节点的父节点,然后从父节点中删除目标子节点 + /// Recursive implementation for removing child nodes. + /// + /// Finds the parent of the node to remove, then removes the target child + /// from that parent node. fn remove_child_recursive( &mut self, parts: &[&str], index: usize, ) -> Result, fdt_raw::FdtError> { if index >= parts.len() - 1 { - // 已经到达要删除节点的父级 + // Already at the parent level of the node to remove let child_name_to_remove = parts[index]; Ok(self.remove_child(child_name_to_remove)) } else { - // 继续向下递归 + // Continue recursing down let current_part = parts[index]; - // 中间级别只支持精确匹配(使用缓存) + // Intermediate levels only support exact matching (using cache) if let Some(&child_index) = self.name_cache.get(current_part) { self.children[child_index].remove_child_recursive(parts, index + 1) } else { - // 路径不存在 + // Path doesn't exist Ok(None) } } @@ -320,7 +385,7 @@ impl Node { impl From<&fdt_raw::Node<'_>> for Node { fn from(raw: &fdt_raw::Node<'_>) -> Self { let mut new_node = Node::new(raw.name()); - // 复制属性 + // Copy properties for raw_prop in raw.properties() { let prop = Property::from(&raw_prop); new_node.set_property(prop); diff --git a/fdt-edit/src/node/pci.rs b/fdt-edit/src/node/pci.rs index f895896..2223d2f 100644 --- a/fdt-edit/src/node/pci.rs +++ b/fdt-edit/src/node/pci.rs @@ -6,42 +6,71 @@ use log::debug; use crate::node::gerneric::NodeRefGen; +/// PCI address space types. #[derive(Clone, Debug, PartialEq)] pub enum PciSpace { + /// I/O space IO, + /// 32-bit memory space Memory32, + /// 64-bit memory space Memory64, } +/// PCI address range entry. +/// +/// Represents a range of addresses in PCI address space with mapping to CPU address space. #[derive(Clone, Debug, PartialEq)] pub struct PciRange { + /// The PCI address space type pub space: PciSpace, + /// Address on the PCI bus pub bus_address: u64, + /// Address in CPU physical address space pub cpu_address: u64, + /// Size of the range in bytes pub size: u64, + /// Whether the memory region is prefetchable pub prefetchable: bool, } +/// PCI interrupt mapping entry. +/// +/// Represents a mapping from PCI device interrupts to parent interrupt controller inputs. #[derive(Clone, Debug)] pub struct PciInterruptMap { + /// Child device address (masked) pub child_address: Vec, + /// Child device IRQ (masked) pub child_irq: Vec, + /// Phandle of the interrupt parent controller pub interrupt_parent: Phandle, + /// Parent controller IRQ inputs pub parent_irq: Vec, } +/// PCI interrupt information. +/// +/// Contains the resolved interrupt information for a PCI device. #[derive(Clone, Debug, PartialEq)] pub struct PciInterruptInfo { + /// List of IRQ numbers pub irqs: Vec, } +/// PCI node reference. +/// +/// Provides specialized access to PCI bridge nodes and their properties. #[derive(Clone, Debug)] pub struct NodeRefPci<'a> { + /// The underlying generic node reference pub node: NodeRefGen<'a>, } impl<'a> NodeRefPci<'a> { - // 在这里添加 PCI 相关的方法 + /// Attempts to create a PCI node reference from a generic node. + /// + /// Returns `Err` with the original node if it's not a PCI node. pub fn try_from(node: NodeRefGen<'a>) -> Result> { if node.device_type() == Some("pci") { Ok(Self { node }) @@ -50,6 +79,9 @@ impl<'a> NodeRefPci<'a> { } } + /// Returns the `#interrupt-cells` property value. + /// + /// Defaults to 1 for PCI devices if not specified. pub fn interrupt_cells(&self) -> u32 { self.find_property("#interrupt-cells") .and_then(|prop| prop.get_u32()) @@ -131,8 +163,8 @@ impl<'a> NodeRefPci<'a> { (space, prefetchable) } - /// 获取 PCI 设备的中断信息 - /// 参数: bus, device, function, pin (1=INTA, 2=INTB, 3=INTC, 4=INTD) + /// Get interrupt information for a PCI device + /// Parameters: bus, device, function, pin (1=INTA, 2=INTB, 3=INTC, 4=INTD) pub fn child_interrupts( &self, bus: u8, @@ -140,17 +172,17 @@ impl<'a> NodeRefPci<'a> { function: u8, interrupt_pin: u8, ) -> Result { - // 获取 interrupt-map 和 mask + // Get interrupt-map and mask let interrupt_map = self.interrupt_map()?; - // 将 mask 转换为 Vec 以便索引访问 + // Convert mask to Vec for indexed access let mask: Vec = self .interrupt_map_mask() .ok_or(FdtError::NotFound)? .collect(); - // 构造 PCI 设备的子地址 - // 格式: [bus_num, device_num, func_num] 在适当的位 + // Construct child address for PCI device + // Format: [bus_num, device_num, func_num] at appropriate bit positions let child_addr_high = ((bus as u32 & 0xff) << 16) | ((device as u32 & 0x1f) << 11) | ((function as u32 & 0x7) << 8); @@ -163,20 +195,20 @@ impl<'a> NodeRefPci<'a> { let encoded_address = [child_addr_high, child_addr_mid, child_addr_low]; let mut masked_child_address = Vec::with_capacity(child_addr_cells); - // 应用 mask 到子地址 + // Apply mask to child address for (idx, value) in encoded_address.iter().take(child_addr_cells).enumerate() { let mask_value = mask.get(idx).copied().unwrap_or(0xffff_ffff); masked_child_address.push(value & mask_value); } - // 如果 encoded_address 比 child_addr_cells 短,填充 0 + // If encoded_address is shorter than child_addr_cells, pad with 0 let remaining = child_addr_cells.saturating_sub(encoded_address.len()); masked_child_address.extend(core::iter::repeat_n(0, remaining)); let encoded_irq = [interrupt_pin as u32]; let mut masked_child_irq = Vec::with_capacity(child_irq_cells); - // 应用 mask 到子 IRQ + // Apply mask to child IRQ for (idx, value) in encoded_irq.iter().take(child_irq_cells).enumerate() { let mask_value = mask .get(child_addr_cells + idx) @@ -185,11 +217,11 @@ impl<'a> NodeRefPci<'a> { masked_child_irq.push(value & mask_value); } - // 如果 encoded_irq 比 child_irq_cells 短,填充 0 + // If encoded_irq is shorter than child_irq_cells, pad with 0 let remaining_irq = child_irq_cells.saturating_sub(encoded_irq.len()); masked_child_irq.extend(core::iter::repeat_n(0, remaining_irq)); - // 在 interrupt-map 中查找匹配的条目 + // Search for matching entry in interrupt-map for mapping in &interrupt_map { if mapping.child_address == masked_child_address && mapping.child_irq == masked_child_irq @@ -200,20 +232,20 @@ impl<'a> NodeRefPci<'a> { } } - // 回退到简单的 IRQ 计算 + // Fall back to simple IRQ calculation let simple_irq = (device as u32 * 4 + interrupt_pin as u32) % 32; Ok(PciInterruptInfo { irqs: vec![simple_irq], }) } - /// 解析 interrupt-map 属性 + /// Parse interrupt-map property pub fn interrupt_map(&self) -> Result, FdtError> { let prop = self .find_property("interrupt-map") .ok_or(FdtError::NotFound)?; - // 将 mask 和 data 转换为 Vec 以便索引访问 + // Convert mask and data to Vec for indexed access let mask: Vec = self .interrupt_map_mask() .ok_or(FdtError::NotFound)? @@ -222,22 +254,22 @@ impl<'a> NodeRefPci<'a> { let mut data = prop.as_reader(); let mut mappings = Vec::new(); - // 计算每个条目的大小 - // 格式: + // Calculate size of each entry + // Format: let child_addr_cells = self.address_cells().unwrap_or(3) as usize; let child_irq_cells = self.interrupt_cells() as usize; loop { - // 解析子地址 + // Parse child address let mut child_address = Vec::with_capacity(child_addr_cells); for _ in 0..child_addr_cells { match data.read_u32() { Some(v) => child_address.push(v), - None => return Ok(mappings), // 数据结束 + None => return Ok(mappings), // End of data } } - // 解析子 IRQ + // Parse child IRQ let mut child_irq = Vec::with_capacity(child_irq_cells); for _ in 0..child_irq_cells { match data.read_u32() { @@ -246,7 +278,7 @@ impl<'a> NodeRefPci<'a> { } } - // 解析中断父 phandle + // Parse interrupt parent phandle let interrupt_parent_raw = match data.read_u32() { Some(v) => v, None => return Ok(mappings), @@ -267,13 +299,13 @@ impl<'a> NodeRefPci<'a> { .collect::>() ); - // 通过 phandle 查找中断父节点以获取其 #address-cells 和 #interrupt-cells - // 根据 devicetree 规范,interrupt-map 中的 parent unit address 使用中断父节点的 #address-cells + // Look up interrupt parent node by phandle to get its #address-cells and #interrupt-cells + // According to devicetree spec, parent unit address in interrupt-map uses interrupt parent's #address-cells let (parent_addr_cells, parent_irq_cells) = if let Some(irq_parent) = self.ctx.find_by_phandle(interrupt_parent) { debug!("Found interrupt parent: {:?}", irq_parent.name); - // 直接使用中断父节点的 #address-cells + // Use interrupt parent node's #address-cells directly let addr_cells = irq_parent.address_cells().unwrap_or(0) as usize; let irq_cells = irq_parent @@ -290,18 +322,18 @@ impl<'a> NodeRefPci<'a> { "Interrupt parent phandle 0x{:x} NOT FOUND in context!", interrupt_parent.raw() ); - // 默认值:address_cells=0, interrupt_cells=3 (GIC 格式) + // Default values: address_cells=0, interrupt_cells=3 (GIC format) (0, 3) }; - // 跳过父地址 cells + // Skip parent address cells for _ in 0..parent_addr_cells { if data.read_u32().is_none() { return Ok(mappings); } } - // 解析父 IRQ + // Parse parent IRQ let mut parent_irq = Vec::with_capacity(parent_irq_cells); for _ in 0..parent_irq_cells { match data.read_u32() { @@ -310,7 +342,7 @@ impl<'a> NodeRefPci<'a> { } } - // 应用 mask 到子地址和 IRQ + // Apply mask to child address and IRQ let masked_address: Vec = child_address .iter() .enumerate() diff --git a/fdt-edit/src/prop/mod.rs b/fdt-edit/src/prop/mod.rs index b361c5b..3b9a0fb 100644 --- a/fdt-edit/src/prop/mod.rs +++ b/fdt-edit/src/prop/mod.rs @@ -1,3 +1,9 @@ +//! Device tree property representation and manipulation. +//! +//! This module provides the `Property` type which represents a mutable device tree +//! property with a name and data, along with methods for accessing and modifying +//! various property data formats. + use core::ffi::CStr; use alloc::{ @@ -9,13 +15,20 @@ use fdt_raw::data::{Bytes, Reader, StrIter, U32Iter}; // Re-export from fdt_raw pub use fdt_raw::{Phandle, RegInfo, Status}; +/// A mutable device tree property. +/// +/// Represents a property with a name and raw data. Provides methods for +/// accessing and modifying the data in various formats (u32, u64, strings, etc.). #[derive(Clone)] pub struct Property { + /// Property name pub name: String, + /// Raw property data pub data: Vec, } impl Property { + /// Creates a new property with the given name and data. pub fn new(name: &str, data: Vec) -> Self { Self { name: name.to_string(), @@ -23,10 +36,14 @@ impl Property { } } + /// Returns the property name. pub fn name(&self) -> &str { &self.name } + /// Returns the property data as a big-endian u32. + /// + /// Returns None if the data is not exactly 4 bytes. pub fn get_u32(&self) -> Option { if self.data.len() != 4 { return None; @@ -39,6 +56,7 @@ impl Property { ])) } + /// Sets the property data from a list of u32 values (as big-endian). pub fn set_u32_ls(&mut self, values: &[u32]) { self.data.clear(); for &value in values { @@ -46,10 +64,14 @@ impl Property { } } + /// Returns an iterator over u32 values in the property data. pub fn get_u32_iter(&self) -> U32Iter<'_> { Bytes::new(&self.data).as_u32_iter() } + /// Returns the property data as a big-endian u64. + /// + /// Returns None if the data is not exactly 8 bytes. pub fn get_u64(&self) -> Option { if self.data.len() != 8 { return None; @@ -66,26 +88,37 @@ impl Property { ])) } + /// Sets the property data from a u64 value (as big-endian). pub fn set_u64(&mut self, value: u64) { self.data = value.to_be_bytes().to_vec(); } + /// Returns the property data as a null-terminated string. + /// + /// Returns None if the data is not a valid null-terminated UTF-8 string. pub fn as_str(&self) -> Option<&str> { CStr::from_bytes_with_nul(&self.data) .ok() .and_then(|cstr| cstr.to_str().ok()) } + /// Sets the property data from a string value. + /// + /// The string will be null-terminated. pub fn set_string(&mut self, value: &str) { let mut bytes = value.as_bytes().to_vec(); bytes.push(0); // Null-terminate self.data = bytes; } + /// Returns an iterator over null-terminated strings in the property data. pub fn as_str_iter(&self) -> StrIter<'_> { Bytes::new(&self.data).as_str_iter() } + /// Sets the property data from a list of string values. + /// + /// Each string will be null-terminated. pub fn set_string_ls(&mut self, values: &[&str]) { self.data.clear(); for &value in values { @@ -94,6 +127,7 @@ impl Property { } } + /// Returns a reader for accessing the property data. pub fn as_reader(&self) -> Reader<'_> { Bytes::new(&self.data).reader() } @@ -108,13 +142,16 @@ impl From<&fdt_raw::Property<'_>> for Property { } } -/// Ranges 条目信息 +/// Ranges entry information for address translation. +/// +/// Represents a single entry in a `ranges` property, mapping a child bus +/// address range to a parent bus address range. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct RangesEntry { - /// 子总线地址 + /// Child bus address pub child_bus_address: u64, - /// 父总线地址 + /// Parent bus address pub parent_bus_address: u64, - /// 区域长度 + /// Length of the region pub length: u64, } diff --git a/fdt-edit/tests/clock.rs b/fdt-edit/tests/clock.rs index e0c5bfa..e967cab 100644 --- a/fdt-edit/tests/clock.rs +++ b/fdt-edit/tests/clock.rs @@ -10,11 +10,11 @@ mod tests { #[test] fn test_clock_node_detection() { - // 使用 RPI 4B DTB 测试 clock 节点检测 + // Test clock node detection using RPI 4B DTB let raw_data = fdt_rpi_4b(); let fdt = Fdt::from_bytes(&raw_data).unwrap(); - // 遍历查找 clock 节点(有 #clock-cells 属性的节点) + // Traverse to find clock nodes (nodes with #clock-cells property) let mut clock_count = 0; for node in fdt.all_nodes() { if let NodeKind::Clock(clock) = node.as_ref() { @@ -36,11 +36,11 @@ mod tests { for node in fdt.all_nodes() { if let NodeKind::Clock(clock) = node.as_ref() { - // 获取 #clock-cells + // Get #clock-cells let cells = clock.clock_cells; println!("Clock: {} cells={}", clock.name(), cells); - // 获取输出名称 + // Get output names if !clock.clock_output_names.is_empty() { println!(" output-names: {:?}", clock.clock_output_names); } @@ -65,26 +65,26 @@ mod tests { let raw_data = fdt_rpi_4b(); let fdt = Fdt::from_bytes(&raw_data).unwrap(); - // 查找固定时钟 + // Find fixed clocks let mut found_with_freq = false; for node in fdt.all_nodes() { - if let NodeKind::Clock(clock) = node.as_ref() { - if let ClockType::Fixed(fixed) = &clock.kind { - // 打印固定时钟信息 - println!( - "Fixed clock found: {} freq={}Hz accuracy={:?}", - clock.name(), - fixed.frequency, - fixed.accuracy - ); - // 有些固定时钟(如 cam1_clk, cam0_clk)没有 clock-frequency 属性 - if fixed.frequency > 0 { - found_with_freq = true; - } + if let NodeKind::Clock(clock) = node.as_ref() + && let ClockType::Fixed(fixed) = &clock.kind + { + // Print fixed clock information + println!( + "Fixed clock found: {} freq={}Hz accuracy={:?}", + clock.name(), + fixed.frequency, + fixed.accuracy + ); + // Some fixed clocks (e.g., cam1_clk, cam0_clk) don't have clock-frequency property + if fixed.frequency > 0 { + found_with_freq = true; } } } - // 至少应该有一个固定时钟有频率 + // At least one fixed clock should have a frequency assert!( found_with_freq, "Should find at least one fixed clock with frequency" @@ -100,11 +100,11 @@ mod tests { if let NodeKind::Clock(clock) = node.as_ref() { let names = &clock.clock_output_names; if !names.is_empty() { - // 测试 output_name 方法 + // Test output_name method let first = clock.output_name(0); assert_eq!(first, Some(names[0].as_str())); - // 如果有多个输出,测试索引访问 + // If there are multiple outputs, test indexed access if names.len() > 1 && clock.clock_cells > 0 { let second = clock.output_name(1); assert_eq!(second, Some(names[1].as_str())); @@ -123,7 +123,7 @@ mod tests { if let NodeKind::Clock(clock) = node.as_ref() { match &clock.kind { ClockType::Fixed(fixed) => { - // 打印固定时钟信息 + // Print fixed clock information println!( "Fixed clock: {} freq={} accuracy={:?}", clock.name(), @@ -132,7 +132,7 @@ mod tests { ); } ClockType::Normal => { - // 测试 Normal 类型 + // Test Normal type println!("Clock {} is a provider", clock.name()); } } @@ -163,7 +163,7 @@ mod tests { " [{}] phandle={:?} cells={} specifier={:?} name={:?}", i, clk.phandle, clk.cells, clk.specifier, clk.name ); - // 验证 specifier 长度与 cells 一致 + // Verify specifier length matches cells assert_eq!( clk.specifier.len(), clk.cells as usize, @@ -182,11 +182,11 @@ mod tests { let fdt = Fdt::from_bytes(&raw_data).unwrap(); for node in fdt.all_nodes() { - // 使用 as_clock_ref 获取带上下文的 clock 引用 + // Use as_clock_ref to get clock reference with context if let NodeKind::Clock(clock) = node.as_ref() { let clocks = clock.clocks(); for clk in clocks { - // 测试 select() 方法 + // Test select() method if clk.cells > 0 { assert!( clk.select().is_some(), diff --git a/fdt-edit/tests/display_debug.rs b/fdt-edit/tests/display_debug.rs index b6ed916..344b57f 100644 --- a/fdt-edit/tests/display_debug.rs +++ b/fdt-edit/tests/display_debug.rs @@ -9,17 +9,17 @@ mod tests { #[test] fn test_fdt_display() { - // 使用 RPI 4B DTB 测试 Display 功能 + // Test Display functionality using RPI 4B DTB let raw_data = fdt_rpi_4b(); let fdt = Fdt::from_bytes(&raw_data).unwrap(); - // 测试 Display 输出 + // Test Display output let dts_output = format!("{}", fdt); - // 验证输出包含 DTS 头部 + // Verify output contains DTS header assert!(dts_output.contains("/dts-v1/;")); - // 验证输出包含根节点 + // Verify output contains root node assert!(dts_output.contains("/ {")); println!("FDT Display output:\n{}", dts_output); @@ -30,10 +30,10 @@ mod tests { let raw_data = fdt_rpi_4b(); let fdt = Fdt::from_bytes(&raw_data).unwrap(); - // 测试 Debug 输出 + // Test Debug output let debug_output = format!("{:?}", fdt); - // 验证 Debug 输出包含结构体信息 + // Verify Debug output contains struct information assert!(debug_output.contains("Fdt")); assert!(debug_output.contains("boot_cpuid_phys")); @@ -45,12 +45,12 @@ mod tests { let raw_data = fdt_rpi_4b(); let fdt = Fdt::from_bytes(&raw_data).unwrap(); - // 找到一个节点进行测试 + // Find a node to test for node in fdt.all_nodes() { if node.name().contains("gpio") { let dts_output = format!("{}", node); - // 验证输出包含节点名称 + // Verify output contains node name assert!(dts_output.contains("gpio")); println!("Node Display output:\n{}", dts_output); @@ -68,7 +68,7 @@ mod tests { if node.name().contains("gpio") { let debug_output = format!("{:?}", node); - // 验证 Debug 输出包含 Node 结构体信息 + // Verify Debug output contains Node struct information assert!(debug_output.contains("NodeRef")); assert!(debug_output.contains("name")); @@ -91,10 +91,10 @@ mod tests { println!("Clock Node Display:\n{}", display_output); println!("Clock Node Debug:\n{}", debug_output); - // 验证输出包含时钟相关信息 + // Verify output contains clock-related information assert!(display_output.contains("Clock Node")); - // 验证 Debug 包含详细信息 + // Verify Debug contains detailed information assert!(debug_output.contains("NodeRefClock")); assert!(debug_output.contains("clock_cells")); @@ -116,10 +116,10 @@ mod tests { println!("Interrupt Controller Display:\n{}", display_output); println!("Interrupt Controller Debug:\n{}", debug_output); - // 验证输出包含中断控制器相关信息 + // Verify output contains interrupt controller-related information assert!(display_output.contains("Interrupt Controller")); - // 验证 Debug 包含详细信息 + // Verify Debug contains detailed information assert!(debug_output.contains("NodeRefInterruptController")); assert!(debug_output.contains("interrupt_cells")); @@ -141,10 +141,10 @@ mod tests { println!("Memory Node Display:\n{}", display_output); println!("Memory Node Debug:\n{}", debug_output); - // 验证输出包含内存相关信息 + // Verify output contains memory-related information assert!(display_output.contains("Memory Node")); - // 验证 Debug 包含详细信息 + // Verify Debug contains detailed information assert!(debug_output.contains("NodeRefMemory")); assert!(debug_output.contains("regions_count")); @@ -164,7 +164,7 @@ mod tests { println!("NodeRef Display with details:\n{}", display_output); - // 验证输出包含类型信息 + // Verify output contains type information assert!(display_output.contains("Clock Node")); break; @@ -174,29 +174,29 @@ mod tests { #[test] fn test_create_simple_fdt() { - let mut fdt = Fdt::new(); + let fdt = Fdt::new(); - // 测试基本 Display 功能 + // Test basic Display functionality let dts_output = format!("{}", fdt); println!("Created FDT Display:\n{}", dts_output); - // 验证输出包含基本头部 + // Verify output contains basic header assert!(dts_output.contains("/dts-v1/;")); assert!(dts_output.contains("/ {")); } #[test] fn test_manual_node_display() { - let mut node = Node::new("test-node"); + let node = Node::new("test-node"); - // 测试基本 Display 功能 + // Test basic Display functionality let display_output = format!("{}", node); println!("Manual Node Display:\n{}", display_output); - // 验证输出包含节点名称 + // Verify output contains node name assert!(display_output.contains("test-node")); - // 测试 Debug + // Test Debug let debug_output = format!("{:?}", node); println!("Manual Node Debug:\n{}", debug_output); @@ -209,24 +209,24 @@ mod tests { let raw_data = fdt_rpi_4b(); let fdt = Fdt::from_bytes(&raw_data).unwrap(); - // 测试基本 Debug 格式 + // Test basic Debug format let simple_debug = format!("{:?}", fdt); println!("FDT Simple Debug:\n{}", simple_debug); - // 验证基本格式包含基本信息 + // Verify basic format contains basic information assert!(simple_debug.contains("Fdt")); assert!(simple_debug.contains("boot_cpuid_phys")); - // 测试深度 Debug 格式 + // Test deep Debug format let deep_debug = format!("{:#?}", fdt); println!("FDT Deep Debug:\n{}", deep_debug); - // 验证深度格式包含节点信息 + // Verify deep format contains node information assert!(deep_debug.contains("Fdt {")); assert!(deep_debug.contains("nodes:")); assert!(deep_debug.contains("[000]")); - // 验证包含特定节点类型 + // Verify it contains specific node types assert!( deep_debug.contains("Clock") || deep_debug.contains("InterruptController") @@ -239,7 +239,7 @@ mod tests { fn test_fdt_deep_debug_with_simple_tree() { let mut fdt = Fdt::new(); - // 创建一个简单的树结构进行测试 + // Create a simple tree structure for testing let mut soc = Node::new("soc"); soc.set_property(Property::new("#address-cells", vec![0x1, 0x0, 0x0, 0x0])); soc.set_property(Property::new("#size-cells", vec![0x1, 0x0, 0x0, 0x0])); @@ -257,11 +257,11 @@ mod tests { soc.add_child(uart); fdt.root.add_child(soc); - // 测试深度调试输出 + // Test deep debug output let deep_debug = format!("{:#?}", fdt); println!("Simple Tree Deep Debug:\n{}", deep_debug); - // 验证输出包含预期的节点信息 + // Verify output contains expected node information assert!(deep_debug.contains("[000] : Generic")); assert!(deep_debug.contains("[001] soc: Generic")); assert!(deep_debug.contains("[002] uart@9000000: Generic")); diff --git a/fdt-edit/tests/edit.rs b/fdt-edit/tests/edit.rs index 0a61430..159dc50 100644 --- a/fdt-edit/tests/edit.rs +++ b/fdt-edit/tests/edit.rs @@ -7,19 +7,19 @@ use std::process::Command; #[test] fn test_parse_and_rebuild() { - // 解析原始 DTB + // Parse original DTB let raw_data = fdt_qemu(); let fdt = Fdt::from_bytes(&raw_data).unwrap(); let fdt_data = fdt.encode(); - // 创建临时文件 + // Create temporary files let temp_dir = std::env::temp_dir(); let original_dtb_path = temp_dir.join("original.dtb"); let rebuilt_dtb_path = temp_dir.join("rebuilt.dtb"); let original_dts_path = temp_dir.join("original.dts"); let rebuilt_dts_path = temp_dir.join("rebuilt.dts"); - // 清理函数 + // Cleanup function let cleanup = || { let _ = fs::remove_file(&original_dtb_path); let _ = fs::remove_file(&rebuilt_dtb_path); @@ -27,22 +27,22 @@ fn test_parse_and_rebuild() { let _ = fs::remove_file(&rebuilt_dts_path); }; - // 确保清理临时文件 + // Ensure cleanup of temporary files cleanup(); - // 保存原始数据和重建数据到临时文件 - fs::write(&original_dtb_path, &*raw_data).expect("无法写入原始DTB文件"); - fs::write(&rebuilt_dtb_path, &fdt_data).expect("无法写入重建DTB文件"); + // Save original and rebuilt data to temporary files + fs::write(&original_dtb_path, &*raw_data).expect("Failed to write original DTB file"); + fs::write(&rebuilt_dtb_path, &fdt_data).expect("Failed to write rebuilt DTB file"); - // 检查dtc命令是否可用 + // Check if dtc command is available let dtc_check = Command::new("dtc").arg("--version").output(); if dtc_check.is_err() { cleanup(); - panic!("dtc命令不可用,请安装device-tree-compiler"); + panic!("dtc command not available, please install device-tree-compiler"); } - // 使用dtc将DTB文件转换为DTS文件 + // Use dtc to convert DTB files to DTS files let original_output = Command::new("dtc") .args([ "-I", @@ -54,12 +54,12 @@ fn test_parse_and_rebuild() { ]) .arg(original_dtb_path.to_str().unwrap()) .output() - .expect("执行dtc命令失败(原始文件)"); + .expect("Failed to execute dtc command (original file)"); if !original_output.status.success() { cleanup(); panic!( - "dtc转换原始DTB失败: {}", + "dtc conversion of original DTB failed: {}", String::from_utf8_lossy(&original_output.stderr) ); } @@ -75,26 +75,28 @@ fn test_parse_and_rebuild() { ]) .arg(rebuilt_dtb_path.to_str().unwrap()) .output() - .expect("执行dtc命令失败(重建文件)"); + .expect("Failed to execute dtc command (rebuilt file)"); if !rebuilt_output.status.success() { cleanup(); panic!( - "dtc转换重建DTB失败: {}", + "dtc conversion of rebuilt DTB failed: {}", String::from_utf8_lossy(&rebuilt_output.stderr) ); } - // 读取生成的DTS文件并进行逐字对比 - let original_dts = fs::read_to_string(&original_dts_path).expect("无法读取原始DTS文件"); - let rebuilt_dts = fs::read_to_string(&rebuilt_dts_path).expect("无法读取重建DTS文件"); + // Read generated DTS files and perform byte-by-byte comparison + let original_dts = + fs::read_to_string(&original_dts_path).expect("Failed to read original DTS file"); + let rebuilt_dts = + fs::read_to_string(&rebuilt_dts_path).expect("Failed to read rebuilt DTS file"); - // 进行逐字对比 + // Perform byte-by-byte comparison if original_dts != rebuilt_dts { - println!("原始DTS文件内容:\n{}", original_dts); - println!("\n重建DTS文件内容:\n{}", rebuilt_dts); + println!("Original DTS file content:\n{}", original_dts); + println!("\nRebuilt DTS file content:\n{}", rebuilt_dts); - // 找到第一个不同的位置 + // Find first differing position let original_chars: Vec = original_dts.chars().collect(); let rebuilt_chars: Vec = rebuilt_dts.chars().collect(); @@ -113,14 +115,14 @@ fn test_parse_and_rebuild() { let context_start = pos.saturating_sub(50); let context_end = (pos + 50).min(min_len); - println!("\n发现差异,位置: {}", pos); + println!("\nDifference found at position: {}", pos); println!( - "原始文件片段: {}>>>DIFF<<<{}", + "Original file segment: {}>>>DIFF<<<{}", &original_dts[context_start..pos], &original_dts[pos..context_end] ); println!( - "重建文件片段: {}>>>DIFF<<<{}", + "Rebuilt file segment: {}>>>DIFF<<<{}", &rebuilt_dts[context_start..pos], &rebuilt_dts[pos..context_end] ); @@ -128,7 +130,7 @@ fn test_parse_and_rebuild() { None => { if original_chars.len() != rebuilt_chars.len() { println!( - "文件长度不同: 原始={}, 重建={}", + "File length differs: original={}, rebuilt={}", original_chars.len(), rebuilt_chars.len() ); @@ -137,33 +139,33 @@ fn test_parse_and_rebuild() { } cleanup(); - panic!("原始DTS和重建DTS不完全匹配"); + panic!("Original DTS and rebuilt DTS do not match exactly"); } - // 清理临时文件 + // Cleanup temporary files cleanup(); - println!("✅ 测试通过:原始DTB和重建DTB的DTS表示完全一致"); + println!("✅ Test passed: Original DTB and rebuilt DTB DTS representations match exactly"); } -// TODO: 需要为 Fdt 实现 Display trait +// TODO: Need to implement Display trait for Fdt // #[test] // fn test_display_dts() { -// // 解析 DTB +// // Parse DTB // let raw_data = fdt_qemu(); // let fdt = Fdt::from_bytes(&raw_data).unwrap(); -// // 使用 Display 输出 DTS +// // Use Display to output DTS // let dts = format!("{}", fdt); -// // 验证输出格式 -// assert!(dts.starts_with("/dts-v1/;"), "DTS 应该以 /dts-v1/; 开头"); -// assert!(dts.contains("/ {"), "DTS 应该包含根节点"); -// assert!(dts.contains("};"), "DTS 应该包含节点闭合"); +// // Verify output format +// assert!(dts.starts_with("/dts-v1/;"), "DTS should start with /dts-v1/;"); +// assert!(dts.contains("/ {"), "DTS should contain root node"); +// assert!(dts.contains("};"), "DTS should contain node closing"); -// // 验证包含一些常见节点 -// assert!(dts.contains("compatible"), "DTS 应该包含 compatible 属性"); +// // Verify it contains some common nodes +// assert!(dts.contains("compatible"), "DTS should contain compatible property"); -// println!("✅ Display 测试通过"); -// println!("DTS 输出前 500 字符:\n{}", &dts[..dts.len().min(500)]); +// println!("✅ Display test passed"); +// println!("DTS output first 500 characters:\n{}", &dts[..dts.len().min(500)]); // } diff --git a/fdt-edit/tests/find2.rs b/fdt-edit/tests/find2.rs index 8545472..99933ad 100644 --- a/fdt-edit/tests/find2.rs +++ b/fdt-edit/tests/find2.rs @@ -5,7 +5,7 @@ mod tests { #[test] fn test_get_method() { - // 解析原始 DTB + // Parse the original DTB let raw_data = fdt_qemu(); let fdt = Fdt::from_bytes(&raw_data).unwrap(); @@ -16,7 +16,7 @@ mod tests { #[test] fn test_find_method() { - // 解析原始 DTB + // Parse the original DTB let raw_data = fdt_qemu(); let fdt = Fdt::from_bytes(&raw_data).unwrap(); @@ -33,7 +33,7 @@ mod tests { #[test] fn test_all() { - // 解析原始 DTB + // Parse the original DTB let raw_data = fdt_qemu(); let fdt = Fdt::from_bytes(&raw_data).unwrap(); @@ -50,7 +50,7 @@ mod tests { #[test] fn test_all_mut() { - // 解析原始 DTB + // Parse the original DTB let raw_data = fdt_qemu(); let mut fdt = Fdt::from_bytes(&raw_data).unwrap(); diff --git a/fdt-edit/tests/irq.rs b/fdt-edit/tests/irq.rs index bc4bd65..321dc1c 100644 --- a/fdt-edit/tests/irq.rs +++ b/fdt-edit/tests/irq.rs @@ -10,11 +10,11 @@ mod tests { #[test] fn test_interrupt_controller_detection() { - // 使用 RPI 4B DTB 测试中断控制器节点检测 + // Test interrupt controller node detection using RPI 4B DTB let raw_data = fdt_rpi_4b(); let fdt = Fdt::from_bytes(&raw_data).unwrap(); - // 遍历查找中断控制器节点 + // Traverse to find interrupt controller nodes let mut irq_count = 0; for node in fdt.all_nodes() { if let NodeKind::InterruptController(ic) = node.as_ref() { @@ -40,23 +40,23 @@ mod tests { for node in fdt.all_nodes() { if let NodeKind::InterruptController(ic) = node.as_ref() { - // 获取 #interrupt-cells + // Get #interrupt-cells let cells = ic.interrupt_cells(); println!("IRQ Controller: {} cells={:?}", ic.name(), cells); - // 获取 #address-cells (如果有) + // Get #address-cells (if present) let addr_cells = ic.interrupt_address_cells(); if addr_cells.is_some() { println!(" #address-cells: {:?}", addr_cells); } - // 验证 is_interrupt_controller + // Verify is_interrupt_controller assert!( ic.is_interrupt_controller(), "Should be marked as interrupt controller" ); - // 获取 compatible 列表 + // Get compatible list let compat = ic.compatibles(); if !compat.is_empty() { println!(" compatible: {:?}", compat); @@ -70,7 +70,7 @@ mod tests { let raw_data = fdt_rpi_4b(); let fdt = Fdt::from_bytes(&raw_data).unwrap(); - // 查找 GIC (ARM Generic Interrupt Controller) + // Find GIC (ARM Generic Interrupt Controller) let mut found_gic = false; for node in fdt.all_nodes() { if let NodeKind::InterruptController(ic) = node.as_ref() { @@ -79,13 +79,13 @@ mod tests { found_gic = true; println!("Found GIC: {}", ic.name()); - // GIC 通常有 3 个 interrupt-cells + // GIC typically has 3 interrupt-cells let cells = ic.interrupt_cells(); println!(" #interrupt-cells: {:?}", cells); } } } - // 注意:并非所有 DTB 都有 GIC,这里只是示例 + // Note: Not all DTBs have GIC, this is just an example if found_gic { println!("GIC found in this DTB"); } @@ -93,7 +93,7 @@ mod tests { #[test] fn test_interrupt_controller_with_phytium() { - // Phytium DTB 应该有中断控制器 + // Phytium DTB should have interrupt controllers let raw_data = fdt_phytium(); let fdt = Fdt::from_bytes(&raw_data).unwrap(); @@ -124,7 +124,7 @@ mod tests { #[test] fn test_interrupt_controller_detection_logic() { - // 测试节点是否正确被识别为中断控制器 + // Test whether nodes are correctly identified as interrupt controllers let raw_data = fdt_qemu(); let fdt = Fdt::from_bytes(&raw_data).unwrap(); @@ -132,7 +132,7 @@ mod tests { let name = node.name(); let is_ic = matches!(node.as_ref(), NodeKind::InterruptController(_)); - // 如果节点名以 interrupt-controller 开头,应该被识别 + // If node name starts with interrupt-controller, it should be detected if name.starts_with("interrupt-controller") && !is_ic { println!( "Warning: {} might be an interrupt controller but not detected", @@ -140,7 +140,7 @@ mod tests { ); } - // 如果有 interrupt-controller 属性,应该被识别 + // If node has interrupt-controller property, it should be detected if node.find_property("interrupt-controller").is_some() && !is_ic { println!( "Warning: {} has interrupt-controller property but not detected", @@ -156,16 +156,16 @@ mod tests { let fdt = Fdt::from_bytes(&raw_data).unwrap(); for node in fdt.all_nodes() { - if let NodeKind::InterruptController(ic) = node.as_ref() { - if let Some(cells) = ic.interrupt_cells() { - // 常见的 interrupt-cells 值:1, 2, 3 - assert!( - cells >= 1 && cells <= 4, - "Unusual #interrupt-cells value: {} for {}", - cells, - ic.name() - ); - } + if let NodeKind::InterruptController(ic) = node.as_ref() + && let Some(cells) = ic.interrupt_cells() + { + // Common interrupt-cells values: 1, 2, 3 + assert!( + (1..=4).contains(&cells), + "Unusual #interrupt-cells value: {} for {}", + cells, + ic.name() + ); } } } diff --git a/fdt-edit/tests/memory.rs b/fdt-edit/tests/memory.rs index 77c3239..ad0623c 100644 --- a/fdt-edit/tests/memory.rs +++ b/fdt-edit/tests/memory.rs @@ -10,11 +10,11 @@ mod tests { #[test] fn test_memory_node_detection() { - // 使用 phytium DTB 测试 memory 节点检测 + // Test memory node detection using phytium DTB let raw_data = fdt_phytium(); let fdt = Fdt::from_bytes(&raw_data).unwrap(); - // 遍历查找 memory 节点 + // Traverse to find memory nodes let mut found_memory = false; for node in fdt.all_nodes() { if let NodeKind::Memory(mem) = node.as_ref() { @@ -30,11 +30,11 @@ mod tests { let raw_data = fdt_phytium(); let fdt = Fdt::from_bytes(&raw_data).unwrap(); - // 查找 memory 节点并获取区域信息 + // Find memory nodes and get region information for node in fdt.all_nodes() { if let NodeKind::Memory(mem) = node.as_ref() { let regions = mem.regions(); - // memory 节点应该有至少一个区域 + // Memory node should have at least one region if !regions.is_empty() { for region in regions { println!( @@ -54,13 +54,13 @@ mod tests { for node in fdt.all_nodes() { if let NodeKind::Memory(mem) = node.as_ref() { - // memory 节点应该有 device_type 属性 + // Memory node should have device_type property let dt = mem.device_type(); if let Some(device_type) = dt { assert_eq!(device_type, "memory", "device_type should be 'memory'"); } - // 获取节点名称 + // Get node name let name = mem.name(); assert!( name.starts_with("memory"), @@ -72,11 +72,11 @@ mod tests { #[test] fn test_create_memory_node() { - // 手动创建一个 memory 节点 + // Manually create a memory node let mem = NodeMemory::new("memory@80000000"); assert_eq!(mem.name(), "memory@80000000"); - // 验证初始状态 + // Verify initial state assert!( mem.regions().is_empty(), "New memory node should have no regions" diff --git a/fdt-edit/tests/range.rs b/fdt-edit/tests/range.rs index 4207cc2..25b17f8 100644 --- a/fdt-edit/tests/range.rs +++ b/fdt-edit/tests/range.rs @@ -61,28 +61,28 @@ mod tests { let original_reg = original_regs[0]; info!("Original reg: {:#x?}", original_reg); - // 使用 CPU 地址设置 reg (0xfe215040 是 CPU 地址) - // set_regs 应该将其转换为 bus 地址 (0x7e215040) 后存储 - let new_cpu_address = 0xfe215080u64; // 新的 CPU 地址 + // Set regs using CPU address (0xfe215040 is CPU address) + // set_regs should convert it to bus address (0x7e215040) when storing + let new_cpu_address = 0xfe215080u64; // New CPU address let new_size = 0x80u64; node.set_regs(&[RegInfo { address: new_cpu_address, size: Some(new_size), }]); - // 重新读取验证 + // Re-read to verify let updated_regs = node.regs().unwrap(); let updated_reg = updated_regs[0]; info!("Updated reg: {:#x?}", updated_reg); - // 验证:读取回来的 CPU 地址应该是我们设置的值 + // Verify: CPU address read back should be what we set assert_eq!( updated_reg.address, new_cpu_address, "CPU address should be {:#x}, got {:#x}", new_cpu_address, updated_reg.address ); - // 验证:bus 地址应该是转换后的值 + // Verify: bus address should be the converted value // 0xfe215080 - 0xfe000000 + 0x7e000000 = 0x7e215080 let expected_bus_address = 0x7e215080u64; assert_eq!( @@ -106,23 +106,23 @@ mod tests { let raw = fdt_rpi_4b(); let mut fdt = Fdt::from_bytes(&raw).unwrap(); - // 获取原始 reg 信息 + // Get original reg information let original_reg = { let node = fdt.get_by_path("/soc/serial@7e215040").unwrap(); node.regs().unwrap()[0] }; info!("Original reg: {:#x?}", original_reg); - // 使用相同的 CPU 地址重新设置 reg + // Set regs again using the same CPU address { let mut node = fdt.get_by_path_mut("/soc/serial@7e215040").unwrap(); node.set_regs(&[RegInfo { - address: original_reg.address, // 使用 CPU 地址 + address: original_reg.address, // Use CPU address size: original_reg.size, }]); } - // 验证 roundtrip:读取回来应该和原来一样 + // Verify roundtrip: reading back should be same as original let roundtrip_reg = { let node = fdt.get_by_path("/soc/serial@7e215040").unwrap(); node.regs().unwrap()[0] diff --git a/fdt-edit/tests/remove_node.rs b/fdt-edit/tests/remove_node.rs index af74508..5835ca2 100644 --- a/fdt-edit/tests/remove_node.rs +++ b/fdt-edit/tests/remove_node.rs @@ -18,28 +18,28 @@ mod tests { #[test] fn test_remove_node_exact_path() { init_logging(); - // 解析原始 DTB + // Parse original DTB let raw_data = fdt_qemu(); let mut fdt = Fdt::from_bytes(&raw_data).unwrap(); - // 找到一个存在的节点路径进行删除 + // Find an existing node path to remove let node = fdt.get_by_path("/psci"); - assert!(node.is_some(), "psci 节点应该存在"); + assert!(node.is_some(), "psci node should exist"); - // 删除节点 + // Remove node let removed = fdt.remove_node("/psci"); - assert!(removed.is_ok(), "删除应该成功"); - assert!(removed.unwrap().is_some(), "应该返回被删除的节点"); + assert!(removed.is_ok(), "Removal should succeed"); + assert!(removed.unwrap().is_some(), "Should return the removed node"); - // 验证节点已被删除 + // Verify node has been removed let node_after = fdt.get_by_path("/psci"); - assert!(node_after.is_none(), "psci 节点应该已被删除"); + assert!(node_after.is_none(), "psci node should have been removed"); } #[test] fn test_remove_node_exact_path_parts() { init_logging(); - // 解析原始 DTB + // Parse original DTB let raw_data = fdt_qemu(); let mut fdt = Fdt::from_bytes(&raw_data).unwrap(); @@ -51,30 +51,30 @@ mod tests { println!("Removing node at path: {}", path); // drop(node); - // 删除节点 + // Remove node let removed = fdt.remove_node(&path); - assert!(removed.is_ok(), "删除应该成功"); - assert!(removed.unwrap().is_some(), "应该返回被删除的节点"); + assert!(removed.is_ok(), "Removal should succeed"); + assert!(removed.unwrap().is_some(), "Should return the removed node"); - // 验证节点已被删除 + // Verify node has been removed let node_after = fdt.get_by_path("/cpus/cpu@0"); - assert!(node_after.is_none(), "cpu 节点应该已被删除"); + assert!(node_after.is_none(), "cpu node should have been removed"); let raw = fdt.encode(); let fdt2 = Fdt::from_bytes(&raw).unwrap(); let node_after_reload = fdt2.get_by_path("/cpus/cpu@0"); assert!( node_after_reload.is_none(), - "重新加载后 cpu 节点应该已被删除" + "cpu node should have been removed after reload" ); } #[test] fn test_remove_nested_node() { - // 使用手动创建的树测试嵌套删除 + // Use manually created tree to test nested removal let mut fdt = Fdt::new(); - // 创建嵌套节点: /soc/i2c@0/eeprom@50 + // Create nested nodes: /soc/i2c@0/eeprom@50 let mut soc = Node::new("soc"); let mut i2c = Node::new("i2c@0"); let eeprom = Node::new("eeprom@50"); @@ -82,18 +82,18 @@ mod tests { soc.add_child(i2c); fdt.root.add_child(soc); - // 验证节点存在 + // Verify node exists assert!(fdt.get_by_path("/soc/i2c@0/eeprom@50").is_some()); - // 删除嵌套节点 + // Remove nested node let removed = fdt.remove_node("/soc/i2c@0/eeprom@50"); assert!(removed.is_ok()); assert!(removed.unwrap().is_some()); - // 验证节点已删除 + // Verify node has been removed assert!(fdt.get_by_path("/soc/i2c@0/eeprom@50").is_none()); - // 父节点应该仍然存在 + // Parent nodes should still exist assert!(fdt.get_by_path("/soc/i2c@0").is_some()); assert!(fdt.get_by_path("/soc").is_some()); } @@ -102,7 +102,7 @@ mod tests { fn test_remove_nonexistent_node() { let mut fdt = Fdt::new(); - // 删除不存在的节点应该返回 NotFound + // Removing non-existent node should return NotFound let result = fdt.remove_node("/nonexistent"); assert!(result.is_err()); } @@ -111,18 +111,18 @@ mod tests { fn test_remove_direct_child() { let mut fdt = Fdt::new(); - // 添加直接子节点 + // Add direct child node fdt.root.add_child(Node::new("memory@0")); - // 验证存在 + // Verify it exists assert!(fdt.get_by_path("/memory@0").is_some()); - // 删除直接子节点 + // Remove direct child node let removed = fdt.remove_node("/memory@0"); assert!(removed.is_ok()); assert!(removed.unwrap().is_some()); - // 验证已删除 + // Verify it has been removed assert!(fdt.get_by_path("/memory@0").is_none()); } @@ -130,7 +130,7 @@ mod tests { fn test_remove_empty_path() { let mut fdt = Fdt::new(); - // 空路径应该返回错误 + // Empty path should return error let result = fdt.remove_node(""); assert!(result.is_err()); @@ -140,10 +140,10 @@ mod tests { #[test] fn test_node_remove_by_path() { - // 直接测试 Node 的 remove_by_path 方法 + // Test Node's remove_by_path method directly let mut root = Node::new(""); - // 创建结构: /a/b/c + // Create structure: /a/b/c let mut a = Node::new("a"); let mut b = Node::new("b"); let c = Node::new("c"); @@ -151,25 +151,25 @@ mod tests { a.add_child(b); root.add_child(a); - // 验证 c 存在 + // Verify c exists assert!(root.get_child("a").is_some()); - // 删除 c + // Remove c let removed = root.remove_by_path("a/b/c"); assert!(removed.is_ok()); assert!(removed.unwrap().is_some()); - // 删除 b + // Remove b let removed = root.remove_by_path("a/b"); assert!(removed.is_ok()); assert!(removed.unwrap().is_some()); - // 删除 a + // Remove a let removed = root.remove_by_path("a"); assert!(removed.is_ok()); assert!(removed.unwrap().is_some()); - // 所有节点都已删除 + // All nodes have been removed assert!(root.get_child("a").is_none()); } @@ -180,7 +180,7 @@ mod tests { assert_eq!(&node.path(), "/test"); println!("Node:\n {:?}", node); - // 带有和不带斜杠的路径都应该工作 + // Both paths with and without leading slash should work let result = fdt.remove_node("/test"); assert!(result.is_ok()); @@ -191,16 +191,16 @@ mod tests { fn test_remove_node_preserves_siblings() { let mut fdt = Fdt::new(); - // 添加多个兄弟节点 + // Add multiple sibling nodes fdt.root.add_child(Node::new("node1")); fdt.root.add_child(Node::new("node2")); fdt.root.add_child(Node::new("node3")); - // 删除中间节点 + // Remove middle node let removed = fdt.remove_node("/node2"); assert!(removed.is_ok()); - // 验证其他节点仍然存在 + // Verify other nodes still exist assert!(fdt.get_by_path("/node1").is_some()); assert!(fdt.get_by_path("/node2").is_none()); assert!(fdt.get_by_path("/node3").is_some()); diff --git a/fdt-parser/Cargo.toml b/fdt-parser/Cargo.toml index 096b827..c25a879 100644 --- a/fdt-parser/Cargo.toml +++ b/fdt-parser/Cargo.toml @@ -8,8 +8,7 @@ keywords = ["devicetree", "fdt", "dt", "dtb"] license = "MPL-2.0" name = "fdt-parser" repository = "https://github.com/drivercraft/fdt-parser" -version = "0.5.1" - +version = "0.5.2" [lib] doctest = false diff --git a/fdt-parser/src/base/fdt.rs b/fdt-parser/src/base/fdt.rs index 49abf74..b55e0b3 100644 --- a/fdt-parser/src/base/fdt.rs +++ b/fdt-parser/src/base/fdt.rs @@ -1,3 +1,9 @@ +//! Core FDT parser with direct structure walking. +//! +//! This module provides the main `Fdt` type for parsing Device Tree Blobs. +//! The parser walks the structure directly, providing zero-copy access to +//! the device tree data. + use core::iter; use super::node::*; @@ -6,6 +12,22 @@ use crate::{ FdtError, FdtRangeSilce, Header, MemoryRegion, Phandle, Property, Token, }; +/// Type alias for the result of scanning node properties. +/// +/// This type is returned by `scan_node_properties` and contains the +/// #address-cells, #size-cells, interrupt-parent, and ranges property values. +type ScannedProperties<'a> = ( + Option, + Option, + Option, + Option>, +); + +/// A Flattened Device Tree (FDT) parser. +/// +/// `Fdt` provides direct access to device tree data by walking the structure +/// without building an in-memory index. This is memory-efficient but may be +/// slower for repeated lookups compared to the cached parser. #[derive(Clone)] pub struct Fdt<'a> { header: Header, @@ -43,6 +65,7 @@ impl<'a> Fdt<'a> { Ok(Fdt { header, raw }) } + /// Returns a slice of the underlying FDT data. pub fn as_slice(&self) -> &'a [u8] { self.raw.value() } @@ -52,12 +75,14 @@ impl<'a> Fdt<'a> { &self.header } + /// Returns the total size of the FDT in bytes. pub fn total_size(&self) -> usize { self.header.totalsize as usize } - /// This field shall contain the physical ID of the system's boot CPU. It shall be identical to the physical ID given in the - /// reg property of that CPU node within the devicetree. + /// This field shall contain the physical ID of the system's boot CPU. + /// It shall be identical to the physical ID given in the reg property of + /// that CPU node within the devicetree. pub fn boot_cpuid_phys(&self) -> u32 { self.header.boot_cpuid_phys } @@ -72,6 +97,7 @@ impl<'a> Fdt<'a> { self.header.version } + /// Returns an iterator over memory reservation blocks. pub fn memory_reservation_blocks(&self) -> impl Iterator + 'a { let mut buffer = self .raw @@ -99,11 +125,14 @@ impl<'a> Fdt<'a> { buffer.take_str() } + /// Returns an iterator over all nodes in the device tree. pub fn all_nodes(&self) -> NodeIter<'a, 16> { NodeIter::new(self.clone()) } - /// if path start with '/' then search by path, else search by aliases + /// Find nodes by path or alias. + /// + /// If path starts with '/' then search by path, else search by aliases. pub fn find_nodes( &self, path: &'a str, @@ -117,6 +146,7 @@ impl<'a> Fdt<'a> { IterFindNode::new(self.all_nodes(), path) } + /// Find an alias by name and return its path. pub fn find_aliase(&self, name: &str) -> Result<&'a str, FdtError> { let aliases = self .find_nodes("/aliases") @@ -131,6 +161,7 @@ impl<'a> Fdt<'a> { Err(FdtError::NoAlias) } + /// Find nodes with compatible strings matching the given list. pub fn find_compatible<'b, 'c: 'b>( &'b self, with: &'c [&'c str], @@ -168,6 +199,7 @@ impl<'a> Fdt<'a> { }) } + /// Get the /chosen node. pub fn chosen(&self) -> Result, FdtError> { let node = self .find_nodes("/chosen") @@ -180,6 +212,7 @@ impl<'a> Fdt<'a> { Ok(node) } + /// Find a node by its phandle. pub fn get_node_by_phandle(&self, phandle: Phandle) -> Result, FdtError> { for node in self.all_nodes() { let node = node?; @@ -193,6 +226,7 @@ impl<'a> Fdt<'a> { Err(FdtError::NotFound) } + /// Find a node by its name. pub fn get_node_by_name(&'a self, name: &str) -> Result, FdtError> { for node in self.all_nodes() { let node = node?; @@ -203,6 +237,7 @@ impl<'a> Fdt<'a> { Err(FdtError::NotFound) } + /// Get memory nodes from the /memory node. pub fn memory(&'a self) -> impl Iterator, FdtError>> + 'a { self.find_nodes("/memory").map(|o| { o.map(|o| match o { @@ -221,7 +256,7 @@ impl<'a> Fdt<'a> { node } - /// Get all reserved-memory child nodes (memory regions) + /// Get all reserved-memory child nodes (memory regions). pub fn reserved_memory_regions(&self) -> Result, FdtError> { match self.reserved_memory_node() { Ok(reserved_memory_node) => Ok(ReservedMemoryRegionsIter::new(reserved_memory_node)), @@ -231,25 +266,25 @@ impl<'a> Fdt<'a> { } } -/// Iterator for reserved memory regions (child nodes of reserved-memory) +/// Iterator for reserved memory regions (child nodes of reserved-memory). pub struct ReservedMemoryRegionsIter<'a> { child_iter: Option>, } impl<'a> ReservedMemoryRegionsIter<'a> { - /// Create a new iterator for reserved memory regions + /// Create a new iterator for reserved memory regions. fn new(reserved_memory_node: Node<'a>) -> Self { ReservedMemoryRegionsIter { child_iter: Some(reserved_memory_node.children()), } } - /// Create an empty iterator + /// Create an empty iterator. fn empty() -> Self { ReservedMemoryRegionsIter { child_iter: None } } - /// Find a reserved memory region by name + /// Find a reserved memory region by name. pub fn find_by_name(self, name: &str) -> Result, FdtError> { for region_result in self { let region = region_result?; @@ -260,7 +295,7 @@ impl<'a> ReservedMemoryRegionsIter<'a> { Err(FdtError::NotFound) } - /// Find reserved memory regions by compatible string + /// Find reserved memory regions by compatible string. pub fn find_by_compatible( self, compatible: &str, @@ -295,7 +330,7 @@ impl<'a> Iterator for ReservedMemoryRegionsIter<'a> { } } -/// Stack frame for tracking node context during iteration +/// Stack frame for tracking node context during iteration. #[derive(Clone)] struct NodeStackFrame<'a> { level: usize, @@ -306,6 +341,10 @@ struct NodeStackFrame<'a> { interrupt_parent: Option, } +/// Iterator over all nodes in the device tree. +/// +/// The iterator maintains a stack to track the node hierarchy and +/// provide context for address translation and interrupt routing. pub struct NodeIter<'a, const MAX_DEPTH: usize = 16> { buffer: Buffer<'a>, fdt: Fdt<'a>, @@ -316,7 +355,7 @@ pub struct NodeIter<'a, const MAX_DEPTH: usize = 16> { } impl<'a, const MAX_DEPTH: usize> NodeIter<'a, MAX_DEPTH> { - /// Create a new NodeIter with the given FDT + /// Create a new NodeIter with the given FDT. pub fn new(fdt: Fdt<'a>) -> Self { NodeIter { buffer: fdt.raw.begin_at(fdt.header.off_dt_struct as usize).buffer(), @@ -327,12 +366,12 @@ impl<'a, const MAX_DEPTH: usize> NodeIter<'a, MAX_DEPTH> { } } - /// Get the current node from stack (parent of the node being created) + /// Get the current node from stack (parent of the node being created). fn current_parent(&self) -> Option<&NodeBase<'a>> { self.node_stack.last().map(|frame| &frame.node) } - /// Get the current effective interrupt parent phandle from the stack + /// Get the current effective interrupt parent phandle from the stack. fn current_interrupt_parent(&self) -> Option { // Search from the top of the stack downward for the first interrupt parent for frame in self.node_stack.iter().rev() { @@ -343,7 +382,7 @@ impl<'a, const MAX_DEPTH: usize> NodeIter<'a, MAX_DEPTH> { None } - /// Get address_cells and size_cells from parent frame + /// Get address_cells and size_cells from parent frame. fn current_cells(&self) -> (u8, u8) { self.node_stack .last() @@ -351,7 +390,7 @@ impl<'a, const MAX_DEPTH: usize> NodeIter<'a, MAX_DEPTH> { .unwrap_or((2, 1)) } - /// Push a new node onto the stack + /// Push a new node onto the stack. fn push_node(&mut self, frame: NodeStackFrame<'a>) -> Result<(), FdtError> { self.node_stack .push(frame) @@ -360,7 +399,7 @@ impl<'a, const MAX_DEPTH: usize> NodeIter<'a, MAX_DEPTH> { }) } - /// Pop nodes from stack when exiting to a certain level + /// Pop nodes from stack when exiting to a certain level. fn pop_to_level(&mut self, target_level: isize) { while let Some(frame) = self.node_stack.last() { if frame.level as isize > target_level { @@ -371,18 +410,8 @@ impl<'a, const MAX_DEPTH: usize> NodeIter<'a, MAX_DEPTH> { } } - /// Scan ahead to find node properties (#address-cells, #size-cells, interrupt-parent, ranges) - fn scan_node_properties( - &self, - ) -> Result< - ( - Option, - Option, - Option, - Option>, - ), - FdtError, - > { + /// Scan ahead to find node properties (#address-cells, #size-cells, interrupt-parent, ranges). + fn scan_node_properties(&self) -> Result, FdtError> { let mut address_cells = None; let mut size_cells = None; let mut interrupt_parent = self.current_interrupt_parent(); @@ -428,7 +457,7 @@ impl<'a, const MAX_DEPTH: usize> NodeIter<'a, MAX_DEPTH> { Ok((address_cells, size_cells, interrupt_parent, ranges)) } - /// Handle BeginNode token and create a new node + /// Handle BeginNode token and create a new node. fn handle_begin_node(&mut self) -> Result>, FdtError> { self.level += 1; @@ -476,16 +505,19 @@ impl<'a, const MAX_DEPTH: usize> NodeIter<'a, MAX_DEPTH> { }; // Create the new node with parent info from stack + use crate::base::node::ParentInfoBuilder; let node = NodeBase::new_with_parent_info( name, self.fdt.clone(), self.buffer.remain(), self.level as _, parent, - parent_address_cells, - parent_size_cells, - parent_ranges, - interrupt_parent, + ParentInfoBuilder { + parent_address_cells, + parent_size_cells, + parent_ranges, + interrupt_parent, + }, ); // Push this node onto the stack for its children let frame = NodeStackFrame { @@ -502,7 +534,7 @@ impl<'a, const MAX_DEPTH: usize> NodeIter<'a, MAX_DEPTH> { Ok(Some(node)) } - /// Handle EndNode token - just pop from stack + /// Handle EndNode token - just pop from stack. fn handle_end_node(&mut self) -> Option> { self.level -= 1; @@ -513,7 +545,7 @@ impl<'a, const MAX_DEPTH: usize> NodeIter<'a, MAX_DEPTH> { None } - /// Handle Prop token + /// Handle Prop token. fn handle_prop(&mut self) -> Result<(), FdtError> { let _prop = self.buffer.take_prop(&self.fdt)?; // Property handling is now done in BeginNode scanning diff --git a/fdt-parser/src/base/mod.rs b/fdt-parser/src/base/mod.rs index 05efcfd..9e20f3b 100644 --- a/fdt-parser/src/base/mod.rs +++ b/fdt-parser/src/base/mod.rs @@ -1,3 +1,9 @@ +//! Direct parsing module for FDT structures. +//! +//! This module provides a zero-copy parser that walks the FDT structure +//! directly without building an in-memory index. It is suitable for +//! one-pass operations where memory efficiency is important. + mod fdt; mod node; diff --git a/fdt-parser/src/base/node/chosen.rs b/fdt-parser/src/base/node/chosen.rs index 80978c2..ba2a81f 100644 --- a/fdt-parser/src/base/node/chosen.rs +++ b/fdt-parser/src/base/node/chosen.rs @@ -1,19 +1,32 @@ +//! Chosen node type for boot parameters. +//! +//! This module provides the `Chosen` type for the /chosen node which contains +//! system configuration parameters passed by the bootloader. + use core::{fmt::Debug, ops::Deref}; use crate::{base::NodeBase, FdtError}; +/// Result of debug console lookup. #[derive(Clone, Debug)] pub enum DebugCon<'a> { - /// 找到了对应的设备树节点 + /// Found the corresponding device tree node Node(NodeBase<'a>), - /// 仅在bootargs中找到earlycon参数,包含解析出的信息 + /// Found earlycon parameter only in bootargs, with parsed information EarlyConInfo { + /// The name of the early console device (e.g., "uart8250") name: &'a str, + /// The MMIO address of the device mmio: u64, + /// Additional parameters for the early console params: Option<&'a str>, }, } +/// The /chosen node containing boot parameters. +/// +/// The chosen node doesn't represent any actual hardware device but serves +/// as a place to pass parameters to the operating system or bootloader. #[derive(Clone)] pub struct Chosen<'a> { node: NodeBase<'a>, @@ -24,14 +37,16 @@ impl<'a> Chosen<'a> { Chosen { node } } - /// Contains the bootargs, if they exist + /// Get the bootargs from the bootargs property, if it exists. pub fn bootargs(&self) -> Result<&'a str, FdtError> { let prop = self.node.find_property("bootargs")?; prop.str() } - /// Searches for the node representing `stdout`, if the property exists, - /// attempting to resolve aliases if the node name doesn't exist as-is + /// Get the stdout node specified by the stdout-path property. + /// + /// Searches for the node representing stdout, attempting to resolve + /// aliases if the node name doesn't exist as-is. pub fn stdout(&self) -> Result, FdtError> { let prop = self.node.find_property("stdout-path")?; @@ -55,6 +70,10 @@ impl<'a> Chosen<'a> { }) } + /// Get the debug console information. + /// + /// First tries to find the stdout node. If that fails, parses the + /// bootargs for earlycon configuration. pub fn debugcon(&self) -> Result, FdtError> { match self.stdout() { Ok(stdout) => Ok(DebugCon::Node(stdout.node.clone())), @@ -76,7 +95,7 @@ impl<'a> Chosen<'a> { let _ = none_ok!(tmp.next(), FdtError::NotFound); let values = none_ok!(tmp.next(), FdtError::NotFound); - // 解析所有参数 + // Parse all parameters let mut params_iter = values.split(','); let name = none_ok!(params_iter.next(), FdtError::NotFound); @@ -95,7 +114,7 @@ impl<'a> Chosen<'a> { let mmio = u64::from_str_radix(addr_str.trim_start_matches("0x"), 16) .map_err(|_| FdtError::Utf8Parse)?; - // 先尝试在设备树中查找对应节点 + // Try to find the corresponding node in the device tree first for node_result in self.node.fdt.all_nodes() { let node = node_result?; match node.reg() { @@ -111,13 +130,13 @@ impl<'a> Chosen<'a> { } } - // 如果找不到对应节点,返回解析出的earlycon信息 - // 重新分割字符串以获取剩余参数 + // If no matching node is found, return the parsed earlycon information + // Re-split the string to get remaining parameters let mut parts = values.split(','); - let _name = parts.next(); // 跳过name - let _addr_part = parts.next(); // 跳过地址部分 + let _name = parts.next(); // skip name + let _addr_part = parts.next(); // skip address part let params = if let Some(param) = parts.next() { - // 获取第一个剩余参数的位置,然后取剩余所有内容 + // Get the position of the first remaining parameter, then take all remaining content let param_start = values.find(param).unwrap_or(0); if param_start > 0 { Some(&values[param_start..]) @@ -149,9 +168,15 @@ impl<'a> Deref for Chosen<'a> { } } +/// The stdout device specified by the chosen node. +/// +/// Contains the node reference and optional parameters (typically specifying +/// the baud rate or other console configuration). #[derive(Clone)] pub struct Stdout<'a> { + /// Optional parameters for the stdout device (e.g., baud rate) pub params: Option<&'a str>, + /// The device tree node for the stdout device pub node: NodeBase<'a>, } diff --git a/fdt-parser/src/base/node/interrupt_controller.rs b/fdt-parser/src/base/node/interrupt_controller.rs index 3cbcdf1..31911bb 100644 --- a/fdt-parser/src/base/node/interrupt_controller.rs +++ b/fdt-parser/src/base/node/interrupt_controller.rs @@ -1,8 +1,18 @@ +//! Interrupt controller node type. +//! +//! This module provides the `InterruptController` type for nodes that +//! manage interrupt routing and handling in the system. + use core::ops::Deref; use super::NodeBase; use crate::FdtError; +/// An interrupt controller device node. +/// +/// Interrupt controllers manage interrupt routing and handling. This type +/// provides access to interrupt controller specific properties like the +/// `#interrupt-cells` property. #[derive(Clone)] pub struct InterruptController<'a> { node: NodeBase<'a>, @@ -13,10 +23,15 @@ impl<'a> InterruptController<'a> { InterruptController { node } } + /// Get the name of this interrupt controller. pub fn name(&self) -> &'a str { self.node.name() } + /// Get the value of the `#interrupt-cells` property. + /// + /// This property specifies the number of cells used to encode an + /// interrupt specifier for this interrupt controller. pub fn interrupt_cells(&self) -> Result { let prop = self.node.find_property("#interrupt-cells")?; let val = prop.u32()?; diff --git a/fdt-parser/src/base/node/memory.rs b/fdt-parser/src/base/node/memory.rs index 7286d2a..2ab2ec5 100644 --- a/fdt-parser/src/base/node/memory.rs +++ b/fdt-parser/src/base/node/memory.rs @@ -1,7 +1,17 @@ +//! Memory node type. +//! +//! This module provides the `Memory` type for memory device nodes that +//! describe the physical memory layout of the system. + use core::{iter, ops::Deref}; use crate::{base::NodeBase, FdtError, MemoryRegion}; +/// A memory device node. +/// +/// Memory device nodes describe the physical memory layout for the system. +/// A system can have multiple memory nodes, or multiple memory ranges +/// specified in the `reg` property of a single memory node. #[derive(Clone)] pub struct Memory<'a> { node: NodeBase<'a>, @@ -12,9 +22,12 @@ impl<'a> Memory<'a> { Memory { node } } - /// A memory device node is required for all devicetrees and describes the physical memory layout for the system. If a system - /// has multiple ranges of memory, multiple memory nodes can be created, or the ranges can be specified in the reg property - /// of a single memory node. + /// Returns an iterator over the memory regions described by this node. + /// + /// A memory device node is required for all devicetrees and describes the + /// physical memory layout for the system. If a system has multiple ranges + /// of memory, multiple memory nodes can be created, or the ranges can be + /// specified in the reg property of a single memory node. pub fn regions(&self) -> impl Iterator> + 'a { let mut reg = self.node.reg(); let mut has_error = false; @@ -38,6 +51,7 @@ impl<'a> Memory<'a> { }) } + /// Get the name of this memory node. pub fn name(&self) -> &'a str { self.node.name() } diff --git a/fdt-parser/src/base/node/mod.rs b/fdt-parser/src/base/node/mod.rs index d3ffbaa..ba13ec3 100644 --- a/fdt-parser/src/base/node/mod.rs +++ b/fdt-parser/src/base/node/mod.rs @@ -1,3 +1,9 @@ +//! Device tree node types and accessors. +//! +//! This module provides the `Node` enum and related types for accessing +//! device tree nodes. Nodes are automatically classified into specialized +//! types (Chosen, Memory, InterruptController, etc.) based on their properties. + use core::ops::Deref; use super::Fdt; @@ -16,40 +22,55 @@ pub use chosen::*; pub use interrupt_controller::*; pub use memory::*; +/// Base node type representing any device tree node. +/// +/// `NodeBase` provides common functionality available on all nodes, +/// including property access, child iteration, and parent references. #[derive(Clone)] pub struct NodeBase<'a> { name: &'a str, pub(crate) fdt: Fdt<'a>, + /// The depth/level of this node in the device tree (0 for root) pub level: usize, pub(crate) raw: Raw<'a>, pub(crate) parent: Option>, interrupt_parent: Option, } +/// Information about a node's parent, used for address translation. #[derive(Clone)] pub(crate) struct ParentInfo<'a> { pub name: &'a str, pub level: usize, pub raw: Raw<'a>, - // Parent's #address-cells and #size-cells (for parsing reg) + /// Parent's #address-cells and #size-cells (for parsing reg) pub address_cells: Option, pub size_cells: Option, - // Parent's ranges for address translation + /// Parent's ranges for address translation pub ranges: Option>, } +/// Builder for creating NodeBase with parent information. +/// +/// This struct reduces the number of parameters needed for `NodeBase::new_with_parent_info` +/// by grouping related parameters together. +pub(crate) struct ParentInfoBuilder<'a> { + pub parent_address_cells: Option, + pub parent_size_cells: Option, + pub parent_ranges: Option>, + pub interrupt_parent: Option, +} + impl<'a> NodeBase<'a> { - /// Create a new NodeBase with pre-calculated parent information from the stack + /// Create a new NodeBase with pre-calculated parent information from the stack. + #[allow(clippy::too_many_arguments)] pub(crate) fn new_with_parent_info( name: &'a str, fdt: Fdt<'a>, raw: Raw<'a>, level: usize, parent: Option<&NodeBase<'a>>, - parent_address_cells: Option, - parent_size_cells: Option, - parent_ranges: Option>, - interrupt_parent: Option, + parent_info: ParentInfoBuilder<'a>, ) -> Self { let name = if name.is_empty() { "/" } else { name }; NodeBase { @@ -60,19 +81,21 @@ impl<'a> NodeBase<'a> { name: p.name(), level: p.level(), raw: p.raw(), - address_cells: parent_address_cells, - size_cells: parent_size_cells, - ranges: parent_ranges, + address_cells: parent_info.parent_address_cells, + size_cells: parent_info.parent_size_cells, + ranges: parent_info.parent_ranges, }), - interrupt_parent, + interrupt_parent: parent_info.interrupt_parent, raw, } } + /// Returns the name of this node's parent. pub fn parent_name(&self) -> Option<&'a str> { self.parent_fast().map(|p| p.name()) } + /// Returns the parent node as a `Node`. pub fn parent(&self) -> Option> { let parent_info = self.parent.as_ref()?; self.fdt @@ -92,30 +115,39 @@ impl<'a> NodeBase<'a> { }) } + /// Returns the raw data for this node. pub fn raw(&self) -> Raw<'a> { self.raw } - /// Get the name of this node + /// Get the name of this node. pub fn name(&self) -> &'a str { self.name } - /// Get the level/depth of this node in the device tree + /// Get the level/depth of this node in the device tree. pub fn level(&self) -> usize { self.level } - /// Get compatible strings for this node (placeholder implementation) + /// Get compatible strings for this node (placeholder implementation). pub fn compatibles(&self) -> Result + 'a, FdtError> { let prop = self.find_property("compatible")?; Ok(prop.str_list()) } + /// Returns a flattened iterator over compatible strings. + /// + /// This is an alias for [`compatibles`](Self::compatibles) that + /// returns the same iterator for chaining with other iterator operations. pub fn compatibles_flatten(&self) -> Result + 'a, FdtError> { self.compatibles() } + /// Returns an iterator over this node's register entries. + /// + /// The addresses are automatically translated from child bus addresses + /// to parent bus addresses using the parent's ranges property. pub fn reg(&self) -> Result, FdtError> { let prop = self.find_property("reg")?; @@ -144,12 +176,12 @@ impl<'a> NodeBase<'a> { self.find_property("#interrupt-controller").is_ok() } - /// 检查这个节点是否是根节点 + /// Check if this node is the root node. pub fn is_root(&self) -> bool { self.level == 0 } - /// 获取节点的完整路径信息(仅限调试用途) + /// Get debug information about the node (for debugging purposes only). pub fn debug_info(&self) -> NodeDebugInfo<'a> { NodeDebugInfo { name: self.name(), @@ -158,11 +190,13 @@ impl<'a> NodeBase<'a> { } } + /// Returns an iterator over this node's properties. pub fn properties(&self) -> impl Iterator, FdtError>> + '_ { let reader = self.raw.buffer(); PropIter::new(self.fdt.clone(), reader) } + /// Find a property by name. pub fn find_property(&self, name: &str) -> Result, FdtError> { for prop in self.properties() { let prop = prop?; @@ -173,12 +207,13 @@ impl<'a> NodeBase<'a> { Err(FdtError::NotFound) } + /// Get this node's phandle. pub fn phandle(&self) -> Result { let prop = self.find_property("phandle")?; Ok(prop.u32()?.into()) } - /// Find [InterruptController] from current node or its parent + /// Find [InterruptController] from current node or its parent. pub fn interrupt_parent(&self) -> Result, FdtError> { // First try to get the interrupt parent phandle from the node itself let phandle = self.interrupt_parent.ok_or(FdtError::NotFound)?; @@ -191,11 +226,14 @@ impl<'a> NodeBase<'a> { } } - /// Get the interrupt parent phandle for this node + /// Get the interrupt parent phandle for this node. pub fn get_interrupt_parent_phandle(&self) -> Option { self.interrupt_parent } + /// Returns an iterator over this node's interrupts. + /// + /// Each interrupt is represented as an iterator of u32 cells. pub fn interrupts( &self, ) -> Result + 'a> + 'a, FdtError> { @@ -207,11 +245,13 @@ impl<'a> NodeBase<'a> { Ok(iter) } + /// Get the clock-frequency property value. pub fn clock_frequency(&self) -> Result { let prop = self.find_property("clock-frequency")?; prop.u32() } + /// Returns an iterator over this node's children. pub fn children(&self) -> NodeChildIter<'a> { NodeChildIter { fdt: self.fdt.clone(), @@ -222,6 +262,7 @@ impl<'a> NodeBase<'a> { } } + /// Get the status property value. pub fn status(&self) -> Result { let prop = self.find_property("status")?; let s = prop.str()?; @@ -238,11 +279,14 @@ impl<'a> NodeBase<'a> { } } -/// 节点调试信息 +/// Node debug information. #[derive(Debug)] pub struct NodeDebugInfo<'a> { + /// The name of the node pub name: &'a str, + /// The depth/level of the node in the device tree pub level: usize, + /// The position of the node in the raw data pub pos: usize, } @@ -252,6 +296,7 @@ impl core::fmt::Debug for NodeBase<'_> { } } +/// Iterator over register entries. pub struct RegIter<'a> { pub(crate) size_cell: u8, pub(crate) address_cell: u8, @@ -294,15 +339,24 @@ impl Iterator for RegIter<'_> { } } +/// Typed node enum for specialized node access. +/// +/// Nodes are automatically classified based on their name and properties. +/// Use pattern matching to access node-specific functionality. #[derive(Debug, Clone)] pub enum Node<'a> { + /// A general-purpose node without special handling General(NodeBase<'a>), + /// The /chosen node containing boot parameters Chosen(Chosen<'a>), + /// A memory node (e.g., /memory@0) Memory(Memory<'a>), + /// An interrupt controller node InterruptController(InterruptController<'a>), } impl<'a> Node<'a> { + /// Returns a reference to the underlying `NodeBase`. pub fn node(&self) -> &NodeBase<'a> { self.deref() } @@ -335,6 +389,7 @@ impl<'a> Deref for Node<'a> { } } +/// Iterator over a node's children. pub struct NodeChildIter<'a> { fdt: Fdt<'a>, parent: NodeBase<'a>, @@ -347,21 +402,21 @@ impl<'a> Iterator for NodeChildIter<'a> { type Item = Result, FdtError>; fn next(&mut self) -> Option { - // 懒初始化节点迭代器 + // Lazily initialize the node iterator if self.all_nodes.is_none() { self.all_nodes = Some(self.fdt.all_nodes()); } let all_nodes = self.all_nodes.as_mut()?; - // 寻找子节点 + // Search for child nodes loop { let node = match all_nodes.next()? { Ok(node) => node, Err(e) => return Some(Err(e)), }; - // 首先找到父节点 + // First, find the parent node if !self.found_parent { if node.name() == self.parent.name() && node.level() == self.parent.level() { self.found_parent = true; @@ -370,16 +425,17 @@ impl<'a> Iterator for NodeChildIter<'a> { continue; } - // 已经找到父节点,现在查找子节点 + // Parent node found, now look for child nodes let current_level = node.level(); - // 如果当前节点的级别等于目标级别,并且在树结构中紧跟在父节点之后, - // 那么它就是父节点的直接子节点 + // If current node's level equals target level and follows parent in tree structure, + // then it's a direct child of the parent node if current_level == self.target_level { return Some(Ok(node)); } - // 如果当前节点的级别小于或等于父节点级别,说明我们已经离开了父节点的子树 + // If current node's level is less than or equal to parent's level, + // we've left the parent's subtree if current_level <= self.parent.level() { break; } @@ -390,7 +446,7 @@ impl<'a> Iterator for NodeChildIter<'a> { } impl<'a> NodeChildIter<'a> { - /// 创建一个新的子节点迭代器 + /// Create a new child node iterator. pub fn new(fdt: Fdt<'a>, parent: NodeBase<'a>) -> Self { NodeChildIter { fdt, @@ -401,17 +457,17 @@ impl<'a> NodeChildIter<'a> { } } - /// 获取父节点的引用 + /// Get a reference to the parent node. pub fn parent(&self) -> &NodeBase<'a> { &self.parent } - /// 收集所有子节点到一个 Vec 中 + /// Collect all child nodes into a Vec. pub fn collect_children(self) -> Result>, FdtError> { self.collect() } - /// 查找具有特定名称的子节点 + /// Find a child node by name. pub fn find_child_by_name(self, name: &str) -> Result, FdtError> { for child_result in self { let child = child_result?; @@ -422,7 +478,7 @@ impl<'a> NodeChildIter<'a> { Err(FdtError::NotFound) } - /// 查找具有特定兼容性字符串的子节点 + /// Find a child node by compatible string. pub fn find_child_by_compatible(self, compatible: &str) -> Result, FdtError> { for child_result in self { let child = child_result?; @@ -449,25 +505,35 @@ mod tests { let dtb_data = include_bytes!("../../../../dtb-file/src/dtb/bcm2711-rpi-4-b.dtb"); let fdt = Fdt::from_bytes(dtb_data).unwrap(); - // 查找根节点 + // Find the root node let root_node = fdt.find_nodes("/").next().unwrap().unwrap(); - // 测试子节点迭代器 + // Test child node iterator let children: Result, _> = root_node.children().collect(); let children = children.unwrap(); - // 根节点应该有子节点 - assert!(!children.is_empty(), "根节点应该有子节点"); + // Root node should have children + assert!(!children.is_empty(), "Root node should have children"); - // 所有子节点的 level 应该是 1 + // All children should be at level 1 for child in &children { - assert_eq!(child.level(), 1, "根节点的直接子节点应该在 level 1"); + assert_eq!( + child.level(), + 1, + "Root node's direct children should be at level 1" + ); } - // 检查是否包含一些预期的子节点 + // Check that expected children are present let child_names: alloc::vec::Vec<_> = children.iter().map(|c| c.name()).collect(); - assert!(child_names.contains(&"chosen"), "应该包含 chosen 节点"); - assert!(child_names.contains(&"memory@0"), "应该包含 memory@0 节点"); + assert!( + child_names.contains(&"chosen"), + "Should contain chosen node" + ); + assert!( + child_names.contains(&"memory@0"), + "Should contain memory@0 node" + ); } #[test] @@ -475,15 +541,15 @@ mod tests { let dtb_data = include_bytes!("../../../../dtb-file/src/dtb/bcm2711-rpi-4-b.dtb"); let fdt = Fdt::from_bytes(dtb_data).unwrap(); - // 查找根节点 + // Find the root node let root_node = fdt.find_nodes("/").next().unwrap().unwrap(); - // 测试通过名称查找子节点 + // Test finding child by name let memory_node = root_node.children().find_child_by_name("memory@0").unwrap(); assert_eq!(memory_node.name(), "memory@0"); - // 测试查找不存在的节点 + // Test finding non-existent node let nonexistent_err = root_node .children() .find_child_by_name("nonexistent") @@ -496,14 +562,14 @@ mod tests { let dtb_data = include_bytes!("../../../../dtb-file/src/dtb/bcm2711-rpi-4-b.dtb"); let fdt = Fdt::from_bytes(dtb_data).unwrap(); - // 查找一个叶子节点(没有子节点的节点) + // Find a leaf node (a node with no children) let leaf_node = fdt.find_nodes("/chosen").next().unwrap().unwrap(); - // 测试叶子节点的子节点迭代器 + // Test leaf node's child iterator let children: Result, _> = leaf_node.children().collect(); let children = children.unwrap(); - assert!(children.is_empty(), "叶子节点不应该有子节点"); + assert!(children.is_empty(), "Leaf node should not have children"); } #[test] @@ -511,23 +577,23 @@ mod tests { let dtb_data = include_bytes!("../../../../dtb-file/src/dtb/bcm2711-rpi-4-b.dtb"); let fdt = Fdt::from_bytes(dtb_data).unwrap(); - // 查找 reserved-memory 节点,它应该有子节点 + // Find reserved-memory node, which should have children let reserved_memory = fdt .all_nodes() .find(|node| node.as_ref().is_ok_and(|n| n.name() == "reserved-memory")) .unwrap() .unwrap(); - // 测试子节点迭代器 + // Test child node iterator let children: Result, _> = reserved_memory.children().collect(); let children = children.unwrap(); - // 确保子节点的 level 正确 + // Ensure children's level is correct for child in &children { assert_eq!( child.level(), reserved_memory.level() + 1, - "子节点的 level 应该比父节点高 1" + "Child's level should be 1 higher than parent's level" ); } } diff --git a/fdt-parser/src/cache/fdt.rs b/fdt-parser/src/cache/fdt.rs index ec5d5c6..b94cda3 100644 --- a/fdt-parser/src/cache/fdt.rs +++ b/fdt-parser/src/cache/fdt.rs @@ -1,3 +1,8 @@ +//! Cached FDT parser with indexed lookups. +//! +//! This module provides the `Fdt` type for the cached parser, which builds +//! internal indices for fast path-based and phandle-based node lookups. + use alloc::{ collections::{btree_map::BTreeMap, btree_set::BTreeSet}, string::{String, ToString}, @@ -8,6 +13,11 @@ use alloc::{ use super::{Align4Vec, Node}; use crate::{base, cache::NodeMeta, data::Raw, FdtError, Header, Phandle}; +/// Cached Flattened Device Tree parser. +/// +/// This parser builds internal indices (path cache, phandle cache, compatible cache) +/// during construction, providing O(1) lookup time for subsequent queries. +/// It uses more memory than the base parser but is much faster for repeated lookups. #[derive(Clone)] pub struct Fdt { pub(super) inner: Arc, @@ -15,6 +25,8 @@ pub struct Fdt { impl Fdt { /// Create a new `Fdt` from byte slice. + /// + /// This will parse the entire device tree and build internal indices. pub fn from_bytes(data: &[u8]) -> Result { let inner = Inner::new(data)?; Ok(Self { @@ -22,6 +34,7 @@ impl Fdt { }) } + /// Returns a slice of the underlying FDT data. pub fn as_slice(&self) -> &[u8] { &self.inner.raw } @@ -42,14 +55,17 @@ impl Fdt { base::Fdt::from_bytes(&self.inner.raw).unwrap() } + /// Get the FDT version. pub fn version(&self) -> u32 { self.fdt_base().version() } + /// Get the FDT header. pub fn header(&self) -> Header { self.fdt_base().header().clone() } + /// Get all nodes in the device tree. pub fn all_nodes(&self) -> Vec { self.inner .all_nodes @@ -58,7 +74,9 @@ impl Fdt { .collect() } - /// if path start with '/' then search by path, else search by aliases + /// Find nodes by path or alias. + /// + /// If path starts with '/' then search by path, else search by aliases. pub fn find_nodes(&self, path: impl AsRef) -> Vec { let path = path.as_ref(); let path = if path.starts_with("/") { @@ -79,17 +97,20 @@ impl Fdt { out } + /// Find an alias by name. pub fn find_aliase(&self, name: impl AsRef) -> Option { let fdt = self.fdt_base(); let s = fdt.find_aliase(name.as_ref()).ok()?; Some(s.into()) } + /// Get a node by its phandle (O(1) lookup). pub fn get_node_by_phandle(&self, phandle: Phandle) -> Option { let meta = self.inner.get_node_by_phandle(phandle)?; Some(Node::new(self, &meta)) } + /// Find nodes with compatible strings matching the given list. pub fn find_compatible(&self, with: &[&str]) -> Vec { let mut ids = BTreeSet::new(); for &c in with { @@ -109,21 +130,24 @@ impl Fdt { out } + /// Get all memory reservation blocks. pub fn memory_reservation_blocks(&self) -> Vec { let fdt = self.fdt_base(); fdt.memory_reservation_blocks().collect() } + /// Get raw access to the FDT data. pub fn raw<'a>(&'a self) -> Raw<'a> { Raw::new(&self.inner.raw) } - /// Get a node by its path in the device tree + /// Get a node by its path in the device tree (O(1) lookup). pub fn get_node_by_path(&self, path: &str) -> Option { let meta = self.inner.get_node_by_path(path)?; Some(Node::new(self, &meta)) } + /// Get all memory nodes. pub fn memory(&self) -> Result, FdtError> { let nodes = self.find_nodes("/memory"); let mut out = Vec::new(); @@ -137,6 +161,9 @@ impl Fdt { } } +/// Internal cached representation of the FDT. +/// +/// Contains the raw FDT data plus various indices for fast lookups. pub(super) struct Inner { raw: Align4Vec, phandle_cache: BTreeMap, @@ -151,6 +178,7 @@ unsafe impl Send for Inner {} unsafe impl Sync for Inner {} impl Inner { + /// Build the cached representation from raw FDT data. fn new(data: &[u8]) -> Result { let b = base::Fdt::from_bytes(data)?; let mut inner = Inner { diff --git a/fdt-parser/src/cache/mod.rs b/fdt-parser/src/cache/mod.rs index c42ac2d..5c98032 100644 --- a/fdt-parser/src/cache/mod.rs +++ b/fdt-parser/src/cache/mod.rs @@ -1,3 +1,9 @@ +//! Cached FDT parser with indexed nodes for efficient lookups. +//! +//! This module provides a cached representation of the device tree that +//! builds an index for fast repeated lookups. It uses more memory than the +//! direct parser but provides O(1) node access by path or phandle. + mod fdt; mod node; @@ -6,6 +12,10 @@ use core::ops::Deref; pub use fdt::*; pub use node::*; +/// A 4-byte aligned buffer for storing FDT data. +/// +/// The Device Tree Blob specification requires 4-byte alignment, +/// and this wrapper ensures the allocated memory meets that requirement. struct Align4Vec { ptr: *mut u8, size: usize, @@ -16,6 +26,7 @@ unsafe impl Send for Align4Vec {} impl Align4Vec { const ALIGN: usize = 4; + /// Creates a new 4-byte aligned buffer containing the provided data. pub fn new(data: &[u8]) -> Self { let size = data.len(); let layout = core::alloc::Layout::from_size_align(size, Self::ALIGN).unwrap(); @@ -26,6 +37,7 @@ impl Align4Vec { } impl Drop for Align4Vec { + /// Deallocates the aligned buffer when dropped. fn drop(&mut self) { let layout = core::alloc::Layout::from_size_align(self.size, Self::ALIGN).unwrap(); unsafe { alloc::alloc::dealloc(self.ptr, layout) }; diff --git a/fdt-parser/src/cache/node/chosen.rs b/fdt-parser/src/cache/node/chosen.rs index 1eb1743..4814be5 100644 --- a/fdt-parser/src/cache/node/chosen.rs +++ b/fdt-parser/src/cache/node/chosen.rs @@ -3,6 +3,10 @@ use core::{fmt::Debug, ops::Deref}; use crate::cache::node::NodeBase; use alloc::{string::String, string::ToString}; +/// The /chosen node containing boot parameters (cached version). +/// +/// The chosen node doesn't represent any actual hardware device but serves +/// as a place to pass parameters to the operating system or bootloader. #[derive(Clone)] pub struct Chosen { node: NodeBase, @@ -31,13 +35,17 @@ impl Chosen { let name = sp.next()?; let params = sp.next(); - // 尝试在cache中找到节点 + // Try to find the node in the cache self.node.fdt.get_node_by_path(name).map(|node| Stdout { params: params.map(|s| s.to_string()), node, }) } + /// Get the debug console information. + /// + /// First tries to find the stdout node. If that fails, parses the + /// bootargs for earlycon configuration. pub fn debugcon(&self) -> Option { if let Some(stdout) = self.stdout() { Some(DebugConCache::Node(stdout.node)) @@ -49,16 +57,16 @@ impl Chosen { fn fdt_bootargs_find_debugcon_info(&self) -> Option { let bootargs = self.bootargs()?; - // 查找 earlycon 参数 + // Look for earlycon parameter let earlycon = bootargs .split_ascii_whitespace() .find(|arg| arg.contains("earlycon"))?; let mut tmp = earlycon.split('='); - let _ = tmp.next()?; // 跳过 "earlycon" + let _ = tmp.next()?; // skip "earlycon" let values = tmp.next()?; - // 解析所有参数 + // Parse all parameters let mut params_iter = values.split(','); let name = params_iter.next()?; @@ -76,7 +84,7 @@ impl Chosen { let mmio = u64::from_str_radix(addr_str.trim_start_matches("0x"), 16).ok()?; - // 先尝试在cache中查找对应节点 + // Try to find the corresponding node in the cache first let all_nodes = self.node.fdt.all_nodes(); for node in all_nodes { let Ok(reg) = node.reg() else { @@ -90,13 +98,13 @@ impl Chosen { } } - // 如果找不到对应节点,返回解析出的earlycon信息 - // 重新分割字符串以获取剩余参数 + // If no matching node is found, return the parsed earlycon information + // Re-split the string to get remaining parameters let mut parts = values.split(','); - let _name = parts.next(); // 跳过name - let _addr_part = parts.next(); // 跳过地址部分 + let _name = parts.next(); // skip name + let _addr_part = parts.next(); // skip address part let params = if let Some(param) = parts.next() { - // 获取第一个剩余参数的位置,然后取剩余所有内容 + // Get the position of the first remaining parameter, then take all remaining content let param_start = values.find(param).unwrap_or(0); if param_start > 0 { Some(values[param_start..].to_string()) @@ -132,21 +140,31 @@ impl Deref for Chosen { } } +/// Result of debug console lookup for the cached parser. #[derive(Clone, Debug)] pub enum DebugConCache { - /// 找到了对应的设备树节点 + /// Found the corresponding device tree node Node(super::super::Node), - /// 仅在bootargs中找到earlycon参数,包含解析出的信息 + /// Found earlycon parameter only in bootargs, with parsed information EarlyConInfo { + /// The name of the early console device (e.g., "uart8250") name: String, + /// The MMIO address of the device mmio: u64, + /// Additional parameters for the early console params: Option, }, } +/// The stdout device specified by the chosen node (cached version). +/// +/// Contains the node reference and optional parameters (typically specifying +/// the baud rate or other console configuration). #[derive(Clone)] pub struct Stdout { + /// Optional parameters for the stdout device (e.g., baud rate) pub params: Option, + /// The device tree node for the stdout device pub node: super::super::Node, } diff --git a/fdt-parser/src/cache/node/clock.rs b/fdt-parser/src/cache/node/clock.rs index 1317825..13a5f68 100644 --- a/fdt-parser/src/cache/node/clock.rs +++ b/fdt-parser/src/cache/node/clock.rs @@ -3,6 +3,7 @@ use core::ops::Deref; use crate::{cache::node::NodeBase, Phandle}; use alloc::{string::String, string::ToString, vec::Vec}; +/// Information about a clock connection between a consumer and provider. #[derive(Clone, Debug)] pub struct ClockInfo { /// Name supplied by the consumer through `clock-names` @@ -10,7 +11,9 @@ pub struct ClockInfo { /// Name exposed by the provider via `clock-output-names` that matches the specifier pub provider_output_name: Option, + /// The phandle of the clock provider pub phandle: Phandle, + /// The clock specifier/index value pub select: u64, /// Provider details pub provider: ClockType, @@ -28,9 +31,12 @@ impl ClockInfo { } } +/// The type of clock provider. #[derive(Clone, Debug)] pub enum ClockType { + /// A fixed clock with a constant frequency Fixed(FixedClock), + /// A general clock provider Provider(Clock), } @@ -53,6 +59,7 @@ impl ClockType { } } + /// Get the number of clock cells for this clock type. pub fn clock_cells(&self) -> u32 { match self { ClockType::Fixed(fixed) => fixed.clock.clock_cells, @@ -60,6 +67,7 @@ impl ClockType { } } + /// Get the output name for the given clock selector. pub fn output_name(&self, select: u64) -> Option { match self { ClockType::Fixed(fixed) => fixed.clock.output_name(select), @@ -79,17 +87,25 @@ impl Deref for ClockType { } } +/// A fixed clock with a constant frequency. #[derive(Clone, Debug)] pub struct FixedClock { + /// The clock provider node pub clock: Clock, + /// The fixed frequency in Hz pub frequency: Option, + /// The clock accuracy in ppb (parts per billion) pub accuracy: Option, } +/// A clock provider node. #[derive(Clone, Debug)] pub struct Clock { + /// The device tree node for this clock pub node: NodeBase, + /// The value of #clock-cells property pub clock_cells: u32, + /// The names of the clock outputs pub output_names: Vec, } @@ -111,6 +127,7 @@ impl Clock { } } + /// Get the output name for the given clock selector. pub fn output_name(&self, select: u64) -> Option { if self.output_names.is_empty() { return None; diff --git a/fdt-parser/src/cache/node/interrupt_controller.rs b/fdt-parser/src/cache/node/interrupt_controller.rs index 606b31e..5434f3d 100644 --- a/fdt-parser/src/cache/node/interrupt_controller.rs +++ b/fdt-parser/src/cache/node/interrupt_controller.rs @@ -2,6 +2,7 @@ use core::{fmt::Debug, ops::Deref}; use crate::{cache::node::NodeBase, FdtError}; +/// An interrupt controller node (cached version). #[derive(Clone)] pub struct InterruptController { node: NodeBase, diff --git a/fdt-parser/src/cache/node/memory.rs b/fdt-parser/src/cache/node/memory.rs index 0313e68..65331d3 100644 --- a/fdt-parser/src/cache/node/memory.rs +++ b/fdt-parser/src/cache/node/memory.rs @@ -3,6 +3,7 @@ use core::{fmt::Debug, ops::Deref}; use crate::{cache::node::NodeBase, FdtError, MemoryRegion}; use alloc::vec::Vec; +/// A memory node (cached version). #[derive(Clone)] pub struct Memory { node: NodeBase, diff --git a/fdt-parser/src/cache/node/mod.rs b/fdt-parser/src/cache/node/mod.rs index fc118cf..a246582 100644 --- a/fdt-parser/src/cache/node/mod.rs +++ b/fdt-parser/src/cache/node/mod.rs @@ -1,3 +1,8 @@ +//! Cached node types with specialized accessors. +//! +//! This module provides the `Node` enum and related types for the cached parser. +//! Nodes are automatically classified into specialized types based on their properties. + use core::{fmt::Debug, ops::Deref}; use super::Fdt; @@ -25,12 +30,21 @@ pub use interrupt_controller::*; pub use memory::*; pub use pci::*; +/// Typed node enum for specialized node access. +/// +/// Nodes are automatically classified based on their name and properties. +/// Use pattern matching to access node-specific functionality. #[derive(Debug, Clone)] pub enum Node { + /// A general-purpose node without special handling General(NodeBase), + /// The /chosen node containing boot parameters Chosen(Chosen), + /// A memory node (e.g., /memory@0) Memory(Memory), + /// An interrupt controller node InterruptController(InterruptController), + /// A PCI host bridge node Pci(Pci), } @@ -41,12 +55,12 @@ impl Node { meta: meta.clone(), }; - // 根据节点类型创建具体类型 + // Create specific type based on node type match meta.name.as_str() { "chosen" => Self::Chosen(Chosen::new(base)), name if name.starts_with("memory@") => Self::Memory(Memory::new(base)), _ => { - // 检查是否是PCI节点 + // Check if this is a PCI node let pci = Pci::new(base.clone()); if pci.is_pci_host_bridge() { Self::Pci(pci) @@ -74,6 +88,10 @@ impl Deref for Node { } } +/// Base node type for cached parser nodes. +/// +/// `NodeBase` provides common functionality available on all nodes, +/// with fast lookups using the cached indices. #[derive(Clone)] pub struct NodeBase { fdt: Fdt, @@ -85,24 +103,29 @@ impl NodeBase { self.fdt.raw().begin_at(self.meta.pos) } + /// Get the level/depth of this node in the device tree. pub fn level(&self) -> usize { self.meta.level } + /// Get the name of this node. pub fn name(&self) -> &str { &self.meta.name } + /// Get the full path of this node. pub fn full_path(&self) -> &str { &self.meta.full_path } + /// Get the parent node. pub fn parent(&self) -> Option { let parent_path = self.meta.parent.as_ref()?.path.as_str(); let parent_meta = self.fdt.inner.get_node_by_path(parent_path)?; Some(Node::new(&self.fdt, &parent_meta)) } + /// Get all properties of this node. pub fn properties<'a>(&'a self) -> Vec> { let reader = self.raw().buffer(); PropIter::new(self.fdt.fdt_base(), reader) @@ -110,13 +133,14 @@ impl NodeBase { .collect() } + /// Find a property by name. pub fn find_property<'a>(&'a self, name: impl AsRef) -> Option> { self.properties() .into_iter() .find(|prop| prop.name == name.as_ref()) } - /// Get compatible strings for this node (placeholder implementation) + /// Get compatible strings for this node (placeholder implementation). pub fn compatibles(&self) -> Vec { self.find_property("compatible") .map(|p| { @@ -128,7 +152,7 @@ impl NodeBase { .unwrap_or_default() } - /// Get the status of this node + /// Get the status of this node. pub fn status(&self) -> Option { self.find_property("status") .and_then(|prop| prop.str().ok()) @@ -143,6 +167,7 @@ impl NodeBase { }) } + /// Get the #address-cells value for this node. pub fn address_cells(&self) -> u8 { self.find_property("#address-cells") .and_then(|p| p.u32().ok()) @@ -162,7 +187,10 @@ impl NodeBase { || self.find_property("#interrupt-controller").is_some() } - /// Get register information for this node + /// Get register information for this node. + /// + /// Returns a vector of register entries with addresses translated + /// to the parent bus address space. pub fn reg(&self) -> Result, FdtError> { let prop = self.find_property("reg").ok_or(FdtError::NotFound)?; @@ -189,6 +217,7 @@ impl NodeBase { Ok(iter.collect()) } + /// Get the ranges property for address translation. pub fn ranges(&self) -> Option> { let p = self.find_property("ranges")?; let parent_info = self.meta.parent.as_ref(); @@ -217,10 +246,12 @@ impl NodeBase { )) } + /// Get the interrupt parent phandle for this node. pub fn interrupt_parent_phandle(&self) -> Option { self.meta.interrupt_parent } + /// Get the interrupt parent node. pub fn interrupt_parent(&self) -> Option { let phandle = self.interrupt_parent_phandle()?; let irq = self.fdt.get_node_by_phandle(phandle)?; @@ -230,6 +261,7 @@ impl NodeBase { Some(i) } + /// Get the interrupts for this node. pub fn interrupts(&self) -> Result>, FdtError> { let res = self .find_property("interrupts") @@ -246,7 +278,7 @@ impl NodeBase { Ok(out) } - /// Get the clocks used by this node following the Devicetree clock binding + /// Get the clocks used by this node following the Devicetree clock binding. pub fn clocks(&self) -> Result, FdtError> { let mut clocks = Vec::new(); let Some(prop) = self.find_property("clocks") else { @@ -308,6 +340,9 @@ impl Debug for NodeBase { } } +/// Metadata for a cached node. +/// +/// Contains precomputed information about the node for fast access. #[derive(Clone)] pub(super) struct NodeMeta { name: String, @@ -319,6 +354,7 @@ pub(super) struct NodeMeta { } impl NodeMeta { + /// Create node metadata from a base parser node. pub fn new(node: &base::Node<'_>, full_path: String, parent: Option<&NodeMeta>) -> Self { NodeMeta { full_path, @@ -335,6 +371,7 @@ impl NodeMeta { } } +/// Information about a node's parent. #[derive(Clone)] struct ParentInfo { path: String, diff --git a/fdt-parser/src/cache/node/pci.rs b/fdt-parser/src/cache/node/pci.rs index a2ac889..b85631d 100644 --- a/fdt-parser/src/cache/node/pci.rs +++ b/fdt-parser/src/cache/node/pci.rs @@ -6,35 +6,53 @@ use core::{ use crate::{cache::node::NodeBase, FdtError, Phandle}; use alloc::{vec, vec::Vec}; +/// PCI address space type. #[derive(Clone, Debug, PartialEq)] pub enum PciSpace { + /// I/O space IO, + /// 32-bit memory space Memory32, + /// 64-bit memory space Memory64, } +/// A PCI address range for address translation. #[derive(Clone, Debug, PartialEq)] pub struct PciRange { + /// The address space type pub space: PciSpace, + /// The address on the PCI bus pub bus_address: u64, + /// The address in CPU physical memory pub cpu_address: u64, + /// The size of the range pub size: u64, + /// Whether the memory is prefetchable pub prefetchable: bool, } +/// A PCI interrupt mapping entry from the interrupt-map property. #[derive(Clone, Debug)] pub struct PciInterruptMap { + /// The child device address (masked) pub child_address: Vec, + /// The child interrupt pin (masked) pub child_irq: Vec, + /// The phandle of the interrupt parent controller pub interrupt_parent: Phandle, + /// The interrupt specifier for the parent controller pub parent_irq: Vec, } +/// Interrupt information for a PCI device. #[derive(Clone, Debug, PartialEq)] pub struct PciInterruptInfo { + /// The interrupt lines/numbers for this device pub irqs: Vec, } +/// A PCI device tree node. #[derive(Clone)] pub struct Pci { node: NodeBase, @@ -45,6 +63,7 @@ impl Pci { Pci { node } } + /// Get the number of interrupt cells for PCI devices. pub fn interrupt_cells(&self) -> u32 { self.find_property("#interrupt-cells") .and_then(|prop| prop.u32().ok()) diff --git a/fdt-parser/src/data.rs b/fdt-parser/src/data.rs index 26c3ccc..cb728c0 100644 --- a/fdt-parser/src/data.rs +++ b/fdt-parser/src/data.rs @@ -1,3 +1,9 @@ +//! Low-level data access utilities for FDT parsing. +//! +//! This module provides raw data access primitives for reading and parsing +//! Device Tree Blob data structures. It handles byte-aligned access, buffer +//! management, and iterators for common data formats used in device trees. + use core::{ ffi::CStr, ops::{Deref, Range}, @@ -5,6 +11,10 @@ use core::{ use crate::{base::Fdt, FdtError, Property, Token}; +/// A raw byte slice view with position tracking for FDT data. +/// +/// `Raw` provides a window into the FDT data with the ability to track +/// the current position and create sub-ranges. #[derive(Clone, Copy)] pub struct Raw<'a> { value: &'a [u8], @@ -12,10 +22,12 @@ pub struct Raw<'a> { } impl<'a> Raw<'a> { + /// Creates a new `Raw` view from a byte slice. pub(crate) fn new(value: &'a [u8]) -> Self { Raw { value, pos: 0 } } + /// Creates a new `Buffer` for sequential reading from this raw data. pub fn buffer(&self) -> Buffer<'a> { Buffer { raw: *self, @@ -23,10 +35,12 @@ impl<'a> Raw<'a> { } } + /// Returns the underlying byte slice. pub fn value(&self) -> &'a [u8] { self.value } + /// Creates a new `Raw` starting at the specified offset from the current position. pub fn begin_at(&self, offset: usize) -> Raw<'a> { let pos = self.pos + offset; Raw { @@ -35,6 +49,11 @@ impl<'a> Raw<'a> { } } + /// Returns a sub-range of the data as a new `Raw`. + /// + /// # Errors + /// + /// Returns `FdtError::BufferTooSmall` if the range extends beyond the data. pub fn get_range(&self, range: Range) -> Result, FdtError> { let pos = self.pos + range.start; let end = pos + range.len(); @@ -48,10 +67,12 @@ impl<'a> Raw<'a> { } } + /// Returns the current position in the original data stream. pub fn pos(&self) -> usize { self.pos } + /// Returns the underlying byte slice as a reference. pub fn as_ref(&self) -> &'a [u8] { self.value } @@ -65,6 +86,11 @@ impl<'a> Deref for Raw<'a> { } } +/// A sequential buffer reader for parsing FDT data structures. +/// +/// `Buffer` provides sequential read access with automatic position tracking, +/// supporting various data types and alignment operations required by the +/// Device Tree specification. #[derive(Clone)] pub struct Buffer<'a> { raw: Raw<'a>, @@ -72,6 +98,11 @@ pub struct Buffer<'a> { } impl<'a> Buffer<'a> { + /// Takes the specified number of bytes from the buffer. + /// + /// # Errors + /// + /// Returns `FdtError::BufferTooSmall` if insufficient bytes remain. pub fn take(&mut self, size: usize) -> Result, FdtError> { let start = self.iter; let end = start + size; @@ -92,6 +123,7 @@ impl<'a> Buffer<'a> { self.raw.pos + self.iter } + /// Returns the remaining unread data as a `Raw`. pub fn remain(&self) -> Raw<'a> { Raw { value: &self.raw.value[self.iter..], @@ -99,11 +131,13 @@ impl<'a> Buffer<'a> { } } + /// Reads a big-endian u32 value. pub fn take_u32(&mut self) -> Result { let bytes = self.take(4)?; Ok(u32::from_be_bytes(bytes.as_ref().try_into().unwrap())) } + /// Reads a big-endian u64 value. pub fn take_u64(&mut self) -> Result { let bytes = self.take(8)?; Ok(u64::from_be_bytes(bytes.as_ref().try_into().unwrap())) @@ -114,6 +148,7 @@ impl<'a> Buffer<'a> { Ok(Token::from(u)) } + /// Reads a null-terminated string. pub fn take_str(&mut self) -> Result<&'a str, FdtError> { let remain = self.remain(); if remain.is_empty() { @@ -126,23 +161,24 @@ impl<'a> Buffer<'a> { let s = cs.to_str()?; let str_len = cs.to_bytes_with_nul().len(); - // Align to 4-byte boundary for FDT format - // let aligned_len = (str_len + 3) & !3; self.iter += str_len; Ok(s) } + /// Skips bytes aligned to 4-byte boundary (FDT format requirement). pub fn skip_4_aligned(&mut self, len: usize) -> Result<(), FdtError> { self.take((len + 3) & !0x3)?; Ok(()) } + /// Takes bytes aligned to 4-byte boundary. pub fn take_aligned(&mut self, len: usize) -> Result, FdtError> { let bytes = (len + 3) & !0x3; self.take(bytes) } + /// Advances the position to the next 4-byte boundary. pub fn take_to_aligned(&mut self) { let remain = self.iter % 4; if remain != 0 { @@ -155,6 +191,11 @@ impl<'a> Buffer<'a> { } } + /// Takes a value based on the cell size (1 = 4 bytes, 2 = 8 bytes). + /// + /// # Panics + /// + /// Panics if cell_size is not 1 or 2. pub fn take_by_cell_size(&mut self, cell_size: u8) -> Option { match cell_size { 1 => self.take_u32().map(|s| s as _).ok(), @@ -163,6 +204,7 @@ impl<'a> Buffer<'a> { } } + /// Takes a property value from the buffer. pub fn take_prop(&mut self, fdt: &Fdt<'a>) -> Result, FdtError> { let len = self.take_u32()?; let nameoff = self.take_u32()?; @@ -174,17 +216,20 @@ impl<'a> Buffer<'a> { } } +/// Iterator over u32 values in raw data. pub struct U32Iter<'a> { buffer: Buffer<'a>, } impl<'a> U32Iter<'a> { + /// Creates a new u32 iterator from raw data. pub fn new(raw: Raw<'a>) -> Self { Self { buffer: raw.buffer(), } } + /// Reads two u32 values as a u64 (big-endian combination). pub fn as_u64(&mut self) -> u64 { let h = self.buffer.take_u32().unwrap(); if let Ok(l) = self.buffer.take_u32() { @@ -203,12 +248,14 @@ impl<'a> Iterator for U32Iter<'a> { } } +/// Iterator over 2D arrays of u32 values. pub struct U32Iter2D<'a> { reader: Buffer<'a>, row_len: u8, } impl<'a> U32Iter2D<'a> { + /// Creates a new 2D iterator with the specified row length (in u32 cells). pub fn new(bytes: &Raw<'a>, row_len: u8) -> Self { Self { reader: bytes.buffer(), diff --git a/fdt-parser/src/define.rs b/fdt-parser/src/define.rs index d8b1360..a89ea90 100644 --- a/fdt-parser/src/define.rs +++ b/fdt-parser/src/define.rs @@ -1,16 +1,35 @@ +//! Common type definitions and constants for FDT parsing. +//! +//! This module defines the core data types, constants, and enumerations +//! used throughout the FDT parser, including the magic number, tokens, +//! status values, and device tree-specific structures. + use core::fmt::{Debug, Display}; use crate::data::{Buffer, Raw, U32Iter}; +/// The Device Tree Blob magic number (0xd00dfeed). +/// +/// This value must be present at the start of any valid Device Tree Blob. pub const FDT_MAGIC: u32 = 0xd00dfeed; +/// Token type for parsing FDT structure blocks. +/// +/// Tokens are 32-bit values that identify different elements in the +/// device tree structure block. #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub(crate) enum Token { + /// Begin node token (0x1) BeginNode, + /// End node token (0x2) EndNode, + /// Property token (0x3) Prop, + /// No-op token (0x4) Nop, + /// End token (0x9) - marks the end of the structure block End, + /// Any other data (not a valid token) Data, } @@ -27,15 +46,24 @@ impl From for Token { } } +/// Device node status indicating whether the node is enabled or disabled. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub enum Status { + /// Node is enabled and operational ("okay") Okay, + /// Node is disabled ("disabled") Disabled, } +/// A memory reservation entry in the FDT. +/// +/// Memory reservations specify physical memory regions that must +/// not be overwritten by the device tree or bootloader. #[derive(Clone, Copy)] pub struct MemoryRegion { + /// Physical address of the reserved region pub address: *mut u8, + /// Size of the reserved region in bytes pub size: usize, } @@ -48,6 +76,10 @@ impl Debug for MemoryRegion { } } +/// A phandle (pointer handle) for referencing nodes in the device tree. +/// +/// Phandles are unique integer identifiers assigned to nodes that need +/// to be referenced from other nodes. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(transparent)] pub struct Phandle(u32); @@ -58,6 +90,7 @@ impl From for Phandle { } } impl Phandle { + /// Returns the phandle value as a usize. pub fn as_usize(&self) -> usize { self.0 as usize } @@ -69,12 +102,17 @@ impl Display for Phandle { } } +/// A register entry describing a memory-mapped region. +/// +/// The `reg` property contains one or more of these entries, each +/// describing a address range for a device's registers. #[derive(Clone, Copy)] pub struct FdtReg { - /// parent bus address + /// Parent bus address pub address: u64, - /// child bus address + /// Child bus address pub child_bus_address: u64, + /// Size of the region (None if not specified) pub size: Option, } @@ -93,7 +131,10 @@ impl Debug for FdtReg { } } -/// Range mapping child bus addresses to parent bus addresses +/// Range mapping child bus addresses to parent bus addresses. +/// +/// The `ranges` property uses these entries to describe how addresses +/// on one bus are translated to another bus. #[derive(Clone)] pub struct FdtRange<'a> { data_child: Raw<'a>, @@ -103,10 +144,12 @@ pub struct FdtRange<'a> { } impl<'a> FdtRange<'a> { + /// Returns an iterator over the child bus address cells. pub fn child_bus_address(&self) -> U32Iter<'a> { U32Iter::new(self.data_child) } + /// Returns an iterator over the parent bus address cells. pub fn parent_bus_address(&self) -> U32Iter<'a> { U32Iter::new(self.data_parent) } @@ -126,6 +169,7 @@ impl core::fmt::Debug for FdtRange<'_> { } } +/// A slice of range entries with associated cell size information. #[derive(Clone)] pub struct FdtRangeSilce<'a> { address_cell: u8, @@ -149,10 +193,13 @@ impl<'a> FdtRangeSilce<'a> { } } + /// Returns an iterator over the range entries. pub fn iter(&self) -> FdtRangeIter<'a> { FdtRangeIter { s: self.clone() } } } + +/// Iterator over range entries. #[derive(Clone)] pub struct FdtRangeIter<'a> { s: FdtRangeSilce<'a>, diff --git a/fdt-parser/src/header.rs b/fdt-parser/src/header.rs index c2c0eee..d339203 100644 --- a/fdt-parser/src/header.rs +++ b/fdt-parser/src/header.rs @@ -1,3 +1,8 @@ +//! FDT header structure parsing. +//! +//! This module handles parsing the Device Tree Blob header, which contains +//! metadata about the structure and layout of the device tree data. + use core::ptr::NonNull; use crate::FdtError; @@ -5,9 +10,14 @@ use crate::FdtError; #[repr(align(4))] struct AlignedHeader([u8; size_of::
()]); +/// The FDT header structure. +/// +/// The header is located at the start of any Device Tree Blob and contains +/// information about the layout and version of the device tree. All multi-byte +/// fields are stored in big-endian byte order. #[derive(Debug, Clone)] pub struct Header { - /// FDT header magic + /// FDT header magic number (0xd00dfeed) pub magic: u32, /// Total size in bytes of the FDT structure pub totalsize: u32, @@ -15,8 +25,7 @@ pub struct Header { pub off_dt_struct: u32, /// Offset in bytes from the start of the header to the strings block pub off_dt_strings: u32, - /// Offset in bytes from the start of the header to the memory reservation - /// block + /// Offset in bytes from the start of the header to the memory reservation block pub off_mem_rsvmap: u32, /// FDT version pub version: u32, @@ -33,6 +42,11 @@ pub struct Header { impl Header { /// Read a header from a byte slice and return an owned `Header` whose /// fields are converted from big-endian (on-disk) to host order. + /// + /// # Errors + /// + /// Returns `FdtError::BufferTooSmall` if the slice is too small. + /// Returns `FdtError::InvalidMagic` if the magic number is incorrect. pub fn from_bytes(data: &[u8]) -> Result { if data.len() < core::mem::size_of::
() { return Err(FdtError::BufferTooSmall { diff --git a/fdt-parser/src/lib.rs b/fdt-parser/src/lib.rs index 1e4d245..baa318d 100644 --- a/fdt-parser/src/lib.rs +++ b/fdt-parser/src/lib.rs @@ -1,7 +1,53 @@ +//! A `#![no_std]` Flattened Device Tree (FDT) parser for Rust. +//! +//! This crate provides a pure-Rust parser for Device Tree Blob (DTB) files +//! based on the devicetree-specification-v0.4. It supports both direct parsing +//! and a cached representation for efficient repeated lookups. +//! +//! # Features +//! +//! - `#![no_std]` compatible - suitable for bare-metal and embedded systems +//! - Two parsing modes: +//! - [`base`] - Direct parsing that walks the FDT structure +//! - [`cache`] - Cached representation with indexed nodes for faster lookups +//! - Zero-copy parsing where possible +//! - Comprehensive error handling +//! +//! # Example +//! +//! ```no_run +//! use fdt_parser::Fdt; +//! +//! # fn main() -> Result<(), Box> { +//! // Read DTB data from file or memory +//! let data = std::fs::read("path/to/device.dtb")?; +//! +//! // Parse the FDT +//! let fdt = Fdt::from_bytes(&data)?; +//! +//! // Get the root node +//! let root = fdt.get_node_by_path("/").unwrap(); +//! println!("Root node: {}", root.name()); +//! +//! // Iterate over all nodes +//! for node in fdt.all_nodes() { +//! println!("Node: {}", node.name()); +//! } +//! # Ok(()) +//! # } +//! ``` + #![no_std] +#![deny(warnings, missing_docs)] extern crate alloc; +/// Macro to unwrap `Option` values, returning `FdtError::NotFound` if `None`. +/// +/// # Variants +/// +/// - `none_ok!(expr)` - Returns `FdtError::NotFound` if `expr` is `None` +/// - `none_ok!(expr, err)` - Returns the specified error if `expr` is `None` macro_rules! none_ok { ($e:expr) => {{ let Some(v) = $e else { @@ -32,36 +78,52 @@ pub use define::*; pub use header::Header; pub use property::Property; +/// Errors that can occur during FDT parsing and traversal. #[derive(thiserror::Error, Debug, Clone)] pub enum FdtError { + /// A requested item (node, property, etc.) was not found #[error("not found")] NotFound, + /// The buffer is too small to contain the expected data at the given position #[error("buffer too small at position {pos}")] - BufferTooSmall { pos: usize }, + BufferTooSmall { + /// The position at which the buffer was found to be too small + pos: usize, + }, + /// The FDT magic number does not match the expected value #[error("invalid magic number {0:#x} != {FDT_MAGIC:#x}")] InvalidMagic(u32), + /// An invalid pointer was encountered during parsing #[error("invalid pointer")] InvalidPtr, + /// String data does not contain a null terminator #[error("data provided does not contain a nul")] FromBytesUntilNull, + /// Failed to parse data as UTF-8 #[error("failed to parse UTF-8 string")] Utf8Parse, + /// No alias was found for the requested path #[error("no aliase found")] NoAlias, + /// Memory allocation failed #[error("system out of memory")] NoMemory, + /// The specified node was not found #[error("node `{0}` not found")] NodeNotFound(&'static str), + /// The specified property was not found #[error("property `{0}` not found")] PropertyNotFound(&'static str), } impl From for FdtError { + /// Converts a UTF-8 parsing error into `FdtError::Utf8Parse`. fn from(_: core::str::Utf8Error) -> Self { FdtError::Utf8Parse } } impl From for FdtError { + /// Converts a C-string parsing error into `FdtError::FromBytesUntilNull`. fn from(_: FromBytesUntilNulError) -> Self { FdtError::FromBytesUntilNull } diff --git a/fdt-parser/src/property.rs b/fdt-parser/src/property.rs index c7a423d..b10938c 100644 --- a/fdt-parser/src/property.rs +++ b/fdt-parser/src/property.rs @@ -1,3 +1,8 @@ +//! Device tree property parsing and access. +//! +//! This module provides the `Property` type for accessing device tree +//! property values, with methods for interpreting the data in various formats. + use core::{ffi::CStr, iter}; use crate::{ @@ -6,40 +11,53 @@ use crate::{ FdtError, Token, }; +/// A device tree property. +/// +/// Properties are key-value pairs associated with device tree nodes. +/// Each property has a name and a value, where the value can be interpreted +/// in various ways depending on the property type. #[derive(Clone)] pub struct Property<'a> { + /// The property name pub name: &'a str, pub(crate) data: Raw<'a>, } impl<'a> Property<'a> { + /// Returns the raw property value as a byte slice. pub fn raw_value(&self) -> &'a [u8] { self.data.value() } + /// Interprets the property value as a big-endian u32. pub fn u32(&self) -> Result { self.data.buffer().take_u32() } + /// Interprets the property value as a big-endian u64. pub fn u64(&self) -> Result { self.data.buffer().take_u64() } + /// Interprets the property value as a null-terminated string. pub fn str(&self) -> Result<&'a str, FdtError> { let res = CStr::from_bytes_until_nul(self.data.value())?.to_str()?; Ok(res) } + /// Interprets the property value as a list of null-terminated strings. pub fn str_list(&self) -> impl Iterator + 'a { let mut value = self.data.buffer(); iter::from_fn(move || value.take_str().ok()) } + /// Interprets the property value as a list of big-endian u32 values. pub fn u32_list(&self) -> impl Iterator + 'a { let mut value = self.data.buffer(); iter::from_fn(move || value.take_u32().ok()) } + /// Interprets the property value as a list of big-endian u64 values. pub fn u64_list(&self) -> impl Iterator + 'a { let mut value = self.data.buffer(); iter::from_fn(move || value.take_u64().ok()) @@ -57,6 +75,7 @@ impl core::fmt::Debug for Property<'_> { } } +/// Iterator over properties in a device tree node. pub(crate) struct PropIter<'a> { fdt: Fdt<'a>, reader: Buffer<'a>, diff --git a/fdt-parser/tests/node.rs b/fdt-parser/tests/node.rs index b3e0f3e..0493866 100644 --- a/fdt-parser/tests/node.rs +++ b/fdt-parser/tests/node.rs @@ -112,7 +112,7 @@ mod test { } } - fn test_node<'a>() -> Option { + fn test_node() -> Option { let raw = fdt_rpi_4b(); let fdt = unsafe { Fdt::from_ptr(raw.ptr()).unwrap() }; fdt.all_nodes().into_iter().next() @@ -231,7 +231,7 @@ mod test { let node = fdt.find_nodes("/soc/serial@7e215040")[0].clone(); - let reg = node.reg().unwrap()[0].clone(); + let reg = node.reg().unwrap()[0]; let parent = node.parent().unwrap(); if let Some(addr_cells_prop) = parent.find_property("#address-cells") { @@ -554,15 +554,15 @@ mod test { let raw = fdt_reserve(); let fdt = unsafe { fdt_parser::Fdt::from_ptr(raw.ptr()).unwrap() }; - // 收集所有节点到Vec中以便查找 + // Collect all nodes into Vec for easier lookup let nodes = fdt.all_nodes(); - // 测试根节点没有父节点 + // Test that root node has no parent let root = nodes.iter().find(|n| n.full_path() == "/").unwrap(); assert!(root.parent().is_none(), "Root node should have no parent"); assert_eq!(root.level(), 0); - // 测试一级节点的父节点是根节点 + // Test that first level nodes have root as parent let chosen = nodes.iter().find(|n| n.full_path() == "/chosen").unwrap(); assert_eq!(chosen.parent().unwrap().full_path(), "/"); assert_eq!(chosen.level(), 1); @@ -586,7 +586,7 @@ mod test { assert_eq!(serial.parent().unwrap().full_path(), "/"); assert_eq!(serial.level(), 1); - // 测试二级节点的父节点正确 + // Test that second level nodes have correct parent let cpu0 = nodes .iter() .find(|n| n.full_path() == "/cpus/cpu@0") @@ -607,15 +607,15 @@ mod test { let raw = fdt_reserve(); let fdt = unsafe { fdt_parser::Fdt::from_ptr(raw.ptr()).unwrap() }; - // 收集所有节点到Vec中以便查找 + // Collect all nodes into Vec for easier lookup let nodes = fdt.all_nodes(); - // 测试根节点没有父节点 + // Test that root node has no parent let root = nodes.iter().find(|n| n.full_path() == "/").unwrap(); assert!(root.parent().is_none(), "Root node should have no parent"); assert_eq!(root.level(), 0); - // 测试一级节点的父节点是根节点 + // Test that first level nodes have root as parent let chosen = nodes.iter().find(|n| n.full_path() == "/chosen").unwrap(); assert_eq!(chosen.parent().unwrap().full_path(), "/"); assert_eq!(chosen.level(), 1); @@ -639,7 +639,7 @@ mod test { assert_eq!(serial.parent().unwrap().full_path(), "/"); assert_eq!(serial.level(), 1); - // 测试二级节点的父节点正确 + // Test that second level nodes have correct parent let cpu0 = nodes .iter() .find(|n| n.full_path() == "/cpus/cpu@0") @@ -657,13 +657,13 @@ mod test { #[test] fn test_parent_with_different_dtb() { - // 只使用一个较小的DTB文件测试parent关系以避免性能问题 + // Use only a smaller DTB file to test parent relationships to avoid performance issues let test_cases = [("Test Reserve", fdt_reserve())]; for (name, raw) in test_cases { let fdt = unsafe { fdt_parser::Fdt::from_ptr(raw.ptr()).unwrap() }; - // 找到根节点 + // Find root node let nodes = fdt.all_nodes(); let root_node = nodes.iter().find(|node| node.full_path() == "/").unwrap(); @@ -679,7 +679,7 @@ mod test { name ); - // 找一个一级节点 + // Find a first level node let first_level_node = nodes .iter() .find(|node| node.level() == 1 && node.full_path() != "/") @@ -705,12 +705,12 @@ mod test { let raw = fdt_reserve(); let fdt = unsafe { fdt_parser::Fdt::from_ptr(raw.ptr()).unwrap() }; - // 测试节点的父节点一致性 + // Test parent node consistency let nodes = fdt.all_nodes(); for node in &nodes { if let Some(parent) = node.parent() { - // 父节点的level应该比当前节点少1 + // Parent's level should be one less than current node assert_eq!( parent.level(), node.level().saturating_sub(1), @@ -718,14 +718,14 @@ mod test { node.full_path() ); - // 如果不是根节点,父节点不应该为None + // If not root node, parent should not be None if node.level() > 0 { assert!(parent.parent().is_some() || parent.level() == 0, "Parent of non-root node should either have a parent or be root for node {}", node.full_path()); } } else { - // 没有父节点的应该只有根节点 + // Only root node should have no parent assert_eq!( node.level(), 0, diff --git a/fdt-raw/Cargo.toml b/fdt-raw/Cargo.toml index ce6335c..7666ead 100644 --- a/fdt-raw/Cargo.toml +++ b/fdt-raw/Cargo.toml @@ -11,7 +11,7 @@ name = "fdt-raw" readme = "README.md" homepage = "https://github.com/drivercraft/fdt-parser" repository = "https://github.com/drivercraft/fdt-parser" -version = "0.1.5" +version = "0.1.6" [dependencies] heapless = "0.9" diff --git a/fdt-raw/README.md b/fdt-raw/README.md index 098463e..dde9ad9 100644 --- a/fdt-raw/README.md +++ b/fdt-raw/README.md @@ -1,58 +1,58 @@ # fdt-raw -用于解析设备树二进制文件(DTB)的低级 Rust 库。 +A low-level Rust library for parsing Device Tree Blob (DTB) files. -## 概述 +## Overview -`fdt-raw` 是一个基于 [Device Tree Specification v0.4](https://www.devicetree.org/specifications/) 实现的纯 Rust、`#![no_std]` 兼容的设备树解析库。该库提供了对扁平设备树(FDT)结构的底层访问接口,适用于嵌入式系统和裸机开发环境。 +`fdt-raw` is a pure Rust, `#![no_std]` compatible device tree parsing library based on the [Device Tree Specification v0.4](https://www.devicetree.org/specifications/). This library provides low-level access interfaces to the Flattened Device Tree (FDT) structure, suitable for embedded systems and bare-metal development environments. -## 特性 +## Features -- **纯 Rust 实现**:无需 C 语言依赖 -- **`no_std` 兼容**:适用于裸机和嵌入式环境 -- **基于规范**:严格遵循 Device Tree Specification v0.4 -- **零拷贝解析**:直接在原始数据上操作,避免不必要的内存分配 -- **类型安全**:提供强类型的 API 接口 -- **内存高效**:使用 `heapless` 进行无分配器集合操作 +- **Pure Rust Implementation**: No C language dependencies +- **`no_std` Compatible**: Suitable for bare-metal and embedded environments +- **Specification Based**: Strictly follows Device Tree Specification v0.4 +- **Zero-Copy Parsing**: Operates directly on raw data, avoiding unnecessary memory allocations +- **Type Safe**: Provides strongly typed API interfaces +- **Memory Efficient**: Uses `heapless` for allocator-free collections -## 核心组件 +## Core Components -### Fdt 结构 -主要的 FDT 解析器,提供对设备树结构的访问: -- 头部信息解析 -- 内存保留块遍历 -- 节点树遍历 -- 属性访问 +### Fdt Structure +The main FDT parser providing access to the device tree structure: +- Header information parsing +- Memory reservation block traversal +- Node tree traversal +- Property access -### 支持的节点类型 -- **内存节点**:解析内存区域信息 -- **chosen 节点**:访问启动参数 -- **通用节点**:处理其他所有节点类型 +### Supported Node Types +- **Memory Nodes**: Parse memory region information +- **Chosen Nodes**: Access boot parameters +- **General Nodes**: Handle all other node types -### 属性解析 -- **reg 属性**:地址范围解析,支持 `#address-cells` 和 `#size-cells` -- **属性迭代器**:高效的属性遍历 -- **属性值访问**:提供各种数据类型的访问方法 +### Property Parsing +- **reg Property**: Address range parsing with `#address-cells` and `#size-cells` support +- **Property Iterators**: Efficient property traversal +- **Property Value Access**: Provides various data type access methods -## 快速开始 +## Quick Start ```rust use fdt_raw::Fdt; -// 从字节数据解析 FDT +// Parse FDT from byte data let fdt = Fdt::from_bytes(&dtb_data)?; -// 遍历根节点的子节点 +// Iterate through root node's children for node in fdt.root().children() { println!("Node name: {}", node.name()?); - // 遍历节点属性 + // Iterate through node properties for prop in node.properties() { println!(" Property: {}", prop.name()?); } } -// 访问内存保留块 +// Access memory reservation block for reservation in fdt.memory_reservations() { println!("Reserved: 0x{:x} - 0x{:x}", reservation.address, @@ -60,32 +60,32 @@ for reservation in fdt.memory_reservations() { } ``` -## 依赖 +## Dependencies -- `heapless = "0.9"` - 无分配器集合 -- `log = "0.4"` - 日志记录 -- `thiserror = {version = "2", default-features = false}` - 错误处理 +- `heapless = "0.9"` - Allocator-free collections +- `log = "0.4"` - Logging +- `thiserror = {version = "2", default-features = false}` - Error handling -## 开发依赖 +## Dev Dependencies -- `dtb-file` - 测试数据 -- `env_logger = "0.11"` - 日志实现 +- `dtb-file` - Test data +- `env_logger = "0.11"` - Logging implementation -## 许可证 +## License -本项目采用开源许可证,具体许可证类型请查看项目根目录的 LICENSE 文件。 +This project is open source. Please see the LICENSE file in the project root directory for specific license details. -## 贡献 +## Contributing -欢迎提交 Issue 和 Pull Request。请确保: +Issues and Pull Requests are welcome. Please ensure: -1. 代码遵循项目的格式规范(`cargo fmt`) -2. 通过所有测试(`cargo test`) -3. 通过 Clippy 检查(`cargo clippy`) +1. Code follows project formatting standards (`cargo fmt`) +2. All tests pass (`cargo test`) +3. Clippy checks pass (`cargo clippy`) -## 相关项目 +## Related Projects -- [fdt-parser](../fdt-parser/) - 更高级的缓存式 FDT 解析器 -- [fdt-edit](../fdt-edit/) - FDT 编辑和操作库 -- [dtb-tool](../dtb-tool/) - DTB 文件检查工具 -- [dtb-file](../dtb-file/) - 测试数据包 \ No newline at end of file +- [fdt-parser](../fdt-parser/) - Higher-level cached FDT parser +- [fdt-edit](../fdt-edit/) - FDT editing and manipulation library +- [dtb-tool](../dtb-tool/) - DTB file inspection tool +- [dtb-file](../dtb-file/) - Test data package diff --git a/fdt-raw/src/data.rs b/fdt-raw/src/data.rs index 6791455..25d9807 100644 --- a/fdt-raw/src/data.rs +++ b/fdt-raw/src/data.rs @@ -1,3 +1,8 @@ +//! Low-level data access primitives for FDT parsing. +//! +//! This module provides raw data access types for reading FDT binary format, +//! including bytes slices, readers, and various iterators. + use core::{ ffi::CStr, ops::{Deref, Range}, @@ -5,6 +10,10 @@ use core::{ use crate::define::{FdtError, Token}; +/// A view into a byte slice with a specific range. +/// +/// `Bytes` provides a window into FDT data with range tracking and +/// convenience methods for creating readers and iterators. #[derive(Clone)] pub struct Bytes<'a> { pub(crate) all: &'a [u8], @@ -20,12 +29,15 @@ impl Deref for Bytes<'_> { } impl<'a> Bytes<'a> { + /// Creates a new `Bytes` from the entire byte slice. pub fn new(all: &'a [u8]) -> Self { Self { all, range: 0..all.len(), } } + + /// Creates a new `Bytes` from a subrange of the current data. pub fn slice(&self, range: Range) -> Self { assert!(range.end <= self.len()); Self { @@ -34,14 +46,17 @@ impl<'a> Bytes<'a> { } } + /// Returns the underlying byte slice as reference. pub fn as_slice(&self) -> &'a [u8] { &self.all[self.range.clone()] } + /// Returns the length of the byte slice. pub fn len(&self) -> usize { self.range.end - self.range.start } + /// Creates a reader for sequential reading from this position. pub fn reader(&self) -> Reader<'a> { Reader { bytes: self.slice(0..self.len()), @@ -49,6 +64,7 @@ impl<'a> Bytes<'a> { } } + /// Creates a reader starting at a specific position. pub fn reader_at(&self, position: usize) -> Reader<'a> { assert!(position < self.len()); Reader { @@ -57,23 +73,30 @@ impl<'a> Bytes<'a> { } } + /// Creates a u32 iterator over this data. pub fn as_u32_iter(&self) -> U32Iter<'a> { U32Iter { reader: self.reader(), } } + /// Creates a string iterator over this data. pub fn as_str_iter(&self) -> StrIter<'a> { StrIter { reader: self.reader(), } } + /// Checks if the byte slice is empty. pub fn is_empty(&self) -> bool { self.len() == 0 } } +/// Sequential reader for parsing FDT data structures. +/// +/// `Reader` provides sequential read access with position tracking for +/// parsing FDT binary format. #[derive(Clone)] pub struct Reader<'a> { pub(crate) bytes: Bytes<'a>, @@ -81,14 +104,19 @@ pub struct Reader<'a> { } impl<'a> Reader<'a> { + /// Returns the current read position in the original data. pub fn position(&self) -> usize { self.bytes.range.start + self.iter } + /// Returns the remaining unread data as a `Bytes`. pub fn remain(&self) -> Bytes<'a> { self.bytes.slice(self.iter..self.bytes.len()) } + /// Reads the specified number of bytes, advancing the position. + /// + /// Returns `None` if insufficient bytes remain. pub fn read_bytes(&mut self, size: usize) -> Option> { if self.iter + size > self.bytes.len() { return None; @@ -98,17 +126,22 @@ impl<'a> Reader<'a> { Some(self.bytes.slice(start..start + size)) } + /// Reads a big-endian u32 value. pub fn read_u32(&mut self) -> Option { let bytes = self.read_bytes(4)?; Some(u32::from_be_bytes(bytes.as_slice().try_into().unwrap())) } + /// Reads a big-endian u64 value (composed of two u32 values). pub fn read_u64(&mut self) -> Option { let high = self.read_u32()? as u64; let low = self.read_u32()? as u64; Some((high << 32) | low) } + /// Reads a value composed of the specified number of cells. + /// + /// Each cell is 4 bytes (a u32). The cells are combined into a u64 value. pub fn read_cells(&mut self, cell_count: usize) -> Option { let mut value: u64 = 0; for _ in 0..cell_count { @@ -118,6 +151,7 @@ impl<'a> Reader<'a> { Some(value) } + /// Reads a token from the FDT structure block. pub fn read_token(&mut self) -> Result { let bytes = self.read_bytes(4).ok_or(FdtError::BufferTooSmall { pos: self.position(), @@ -125,14 +159,17 @@ impl<'a> Reader<'a> { Ok(u32::from_be_bytes(bytes.as_slice().try_into().unwrap()).into()) } + /// Moves the read position back by the specified size. pub fn backtrack(&mut self, size: usize) { assert!(size <= self.iter); self.iter -= size; } } +/// Iterator over u32 values in FDT data. #[derive(Clone)] pub struct U32Iter<'a> { + /// The underlying reader for accessing FDT data pub reader: Reader<'a>, } @@ -145,8 +182,10 @@ impl Iterator for U32Iter<'_> { } } +/// Iterator over null-terminated strings in FDT data. #[derive(Clone)] pub struct StrIter<'a> { + /// The underlying reader for accessing FDT data pub reader: Reader<'a>, } @@ -162,8 +201,8 @@ impl<'a> Iterator for StrIter<'a> { .ok()? .to_str() .ok()?; - let str_len = s.len() + 1; // 包括 null 终止符 - self.reader.read_bytes(str_len)?; // 移动读取位置 + let str_len = s.len() + 1; // including null terminator + self.reader.read_bytes(str_len)?; // advance read position Some(s) } } diff --git a/fdt-raw/src/define.rs b/fdt-raw/src/define.rs index c1f41be..4912185 100644 --- a/fdt-raw/src/define.rs +++ b/fdt-raw/src/define.rs @@ -1,25 +1,51 @@ +//! Core type definitions and constants for FDT parsing. +//! +//! This module provides fundamental types used throughout the FDT parser, +//! including the magic number constant, tokens for parsing the structure +//! block, error types, and common enums. + use core::{ ffi::FromBytesUntilNulError, fmt::{Debug, Display}, ops::Deref, }; +/// The magic number that identifies a valid Flattened Device Tree blob. +/// +/// This value (0xd00dfeed) must be present at the beginning of any +/// valid device tree blob. It is used for validation when parsing. pub const FDT_MAGIC: u32 = 0xd00dfeed; -/// Memory reservation block entry +/// Entry in the memory reservation block. +/// +/// The memory reservation block contains a list of physical memory regions +/// that must be preserved (not used by the OS) during boot. Each entry +/// specifies the starting address and size of a reserved region. #[derive(Clone, Debug, Default)] pub struct MemoryReservation { + /// Physical address of the reserved region pub address: u64, + /// Size of the reserved region in bytes pub size: u64, } +/// Token type for parsing the FDT structure block. +/// +/// The device tree structure block is composed of a sequence of 32-bit +/// tokens followed by data. This enum represents the possible token values. #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum Token { + /// Marks the beginning of a node (FDT_BEGIN_NODE, 0x00000001) BeginNode, + /// Marks the end of a node (FDT_END_NODE, 0x00000002) EndNode, + /// Marks a property (FDT_PROP, 0x00000003) Prop, + /// No-op token, should be ignored (FDT_NOP, 0x00000004) Nop, + /// Marks the end of the structure block (FDT_END, 0x00000009) End, + /// Any other 32-bit value (invalid or unknown token) Data(u32), } @@ -49,9 +75,16 @@ impl From for u32 { } } +/// Device tree node status property value. +/// +/// The `status` property in a device tree indicates whether a node is +/// enabled or disabled. A disabled node should generally be ignored by +/// the OS, though the node may still be probed if explicitly requested. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub enum Status { + /// Node is operational and should be used ("okay") Okay, + /// Node is disabled and should not be used ("disabled") Disabled, } @@ -72,6 +105,11 @@ impl core::fmt::Display for Status { } } +/// A phandle (pointer handle) for referencing device tree nodes. +/// +/// Phandles provide a way for nodes to reference other nodes in the device tree. +/// A node that may be referenced defines a `phandle` property with a unique value, +/// and other nodes reference it using that value in properties like `interrupt-parent`. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(transparent)] pub struct Phandle(u32); @@ -81,11 +119,14 @@ impl From for Phandle { Self(value) } } + impl Phandle { + /// Returns the phandle value as a `usize`. pub fn as_usize(&self) -> usize { self.0 as usize } + /// Returns the raw u32 value of this phandle. pub fn raw(&self) -> u32 { self.0 } @@ -97,28 +138,46 @@ impl Display for Phandle { } } +/// Errors that can occur during FDT parsing. +/// +/// This enum represents all possible error conditions that may be encountered +/// when parsing a device tree blob or accessing its contents. #[derive(thiserror::Error, Debug, Clone)] pub enum FdtError { + /// A requested item (node, property, etc.) was not found #[error("not found")] NotFound, + /// The buffer is too small to read the requested data at the given position #[error("buffer too small at position {pos}")] - BufferTooSmall { pos: usize }, + BufferTooSmall { + /// The position at which the buffer was too small + pos: usize, + }, + /// The FDT magic number doesn't match the expected value #[error("invalid magic number {0:#x} != {FDT_MAGIC:#x}")] InvalidMagic(u32), + /// An invalid pointer was provided #[error("invalid pointer")] InvalidPtr, + /// The input data is invalid or malformed #[error("invalid input")] InvalidInput, + /// A null-terminated string was expected but not found #[error("data provided does not contain a nul")] FromBytesUntilNull, + /// Failed to parse data as a UTF-8 string #[error("failed to parse UTF-8 string")] Utf8Parse, + /// The specified alias was not found in the /aliases node #[error("no aliase `{0}` found")] NoAlias(&'static str), + /// Memory allocation failed #[error("system out of memory")] NoMemory, + /// The specified node was not found #[error("node `{0}` not found")] NodeNotFound(&'static str), + /// The specified property was not found #[error("property `{0}` not found")] PropertyNotFound(&'static str), } diff --git a/fdt-raw/src/fdt.rs b/fdt-raw/src/fdt.rs index d57582d..48d8f3e 100644 --- a/fdt-raw/src/fdt.rs +++ b/fdt-raw/src/fdt.rs @@ -1,10 +1,21 @@ +//! Main Flattened Device Tree (FDT) parser. +//! +//! This module provides the primary `Fdt` type that represents a parsed +//! device tree blob. It offers methods for traversing nodes, resolving +//! paths, translating addresses, and accessing special nodes like +//! /chosen and /memory. + use core::fmt; use crate::{ Chosen, FdtError, Memory, MemoryReservation, Node, data::Bytes, header::Header, iter::FdtIter, }; -/// Memory reservation block iterator +/// Iterator over memory reservation entries. +/// +/// The memory reservation block contains a list of physical memory regions +/// that must be preserved during boot. This iterator yields each reservation +/// entry until it reaches the terminating entry (address=0, size=0). pub struct MemoryReservationIter<'a> { data: &'a [u8], offset: usize, @@ -14,22 +25,22 @@ impl<'a> Iterator for MemoryReservationIter<'a> { type Item = MemoryReservation; fn next(&mut self) -> Option { - // 确保我们有足够的数据来读取地址和大小(各8字节) + // Ensure we have enough data to read address and size (8 bytes each) if self.offset + 16 > self.data.len() { return None; } - // 读取地址(8字节,大端序) + // Read address (8 bytes, big-endian) let address_bytes = &self.data[self.offset..self.offset + 8]; let address = u64::from_be_bytes(address_bytes.try_into().unwrap()); self.offset += 8; - // 读取大小(8字节,大端序) + // Read size (8 bytes, big-endian) let size_bytes = &self.data[self.offset..self.offset + 8]; let size = u64::from_be_bytes(size_bytes.try_into().unwrap()); self.offset += 8; - // 检查是否到达终止符(地址和大小都为0) + // Check for terminator (both address and size are zero) if address == 0 && size == 0 { return None; } @@ -38,7 +49,7 @@ impl<'a> Iterator for MemoryReservationIter<'a> { } } -/// 写入缩进(使用空格) +/// Helper function for writing indentation during formatting. fn write_indent(f: &mut fmt::Formatter<'_>, count: usize, ch: &str) -> fmt::Result { for _ in 0..count { write!(f, "{}", ch)?; @@ -46,6 +57,14 @@ fn write_indent(f: &mut fmt::Formatter<'_>, count: usize, ch: &str) -> fmt::Resu Ok(()) } +/// A parsed Flattened Device Tree (FDT). +/// +/// This is the main type for working with device tree blobs. It provides +/// methods for traversing the tree, finding nodes by path, translating +/// addresses, and accessing special nodes like /chosen and /memory. +/// +/// The `Fdt` holds a reference to the underlying device tree data and +/// performs zero-copy parsing where possible. #[derive(Clone)] pub struct Fdt<'a> { header: Header, @@ -53,7 +72,15 @@ pub struct Fdt<'a> { } impl<'a> Fdt<'a> { - /// Create a new `Fdt` from byte slice. + /// Create a new `Fdt` from a byte slice. + /// + /// Parses the FDT header and validates the magic number. The slice + /// must contain a complete, valid device tree blob. + /// + /// # Errors + /// + /// Returns `FdtError` if the header is invalid, the magic number + /// doesn't match, or the buffer is too small. pub fn from_bytes(data: &'a [u8]) -> Result, FdtError> { let header = Header::from_bytes(data)?; if data.len() < header.totalsize as usize { @@ -69,13 +96,21 @@ impl<'a> Fdt<'a> { }) } - /// Create a new `Fdt` from a raw pointer and size in bytes. + /// Create a new `Fdt` from a raw pointer. + /// + /// Parses an FDT from the memory location pointed to by `ptr`. + /// This is useful when working with device trees loaded by bootloaders. /// /// # Safety /// /// The caller must ensure that the pointer is valid and points to a - /// memory region of at least `size` bytes that contains a valid device tree - /// blob. + /// memory region of at least `totalsize` bytes that contains a valid + /// device tree blob. The memory must remain valid for the lifetime `'a`. + /// + /// # Errors + /// + /// Returns `FdtError` if the header is invalid or the magic number + /// doesn't match. pub unsafe fn from_ptr(ptr: *mut u8) -> Result, FdtError> { let header = unsafe { Header::from_ptr(ptr)? }; @@ -85,18 +120,32 @@ impl<'a> Fdt<'a> { Ok(Fdt { header, data }) } + /// Returns a reference to the FDT header. pub fn header(&self) -> &Header { &self.header } + /// Returns the underlying byte slice. pub fn as_slice(&self) -> &'a [u8] { self.data.as_slice() } + /// Returns an iterator over all nodes in the device tree. pub fn all_nodes(&self) -> FdtIter<'a> { FdtIter::new(self.clone()) } + /// Find a node by its absolute path or alias. + /// + /// The path can be an absolute path starting with '/', or an alias + /// defined in the /aliases node. Returns `None` if the node is not found. + /// + /// # Example + /// + /// ```ignore + /// let node = fdt.find_by_path("/soc@30000000/serial@10000"); + /// let uart = fdt.find_by_path("serial0"); // Using alias + /// ``` pub fn find_by_path(&self, path: &str) -> Option> { let path = self.normalize_path(path)?; let split = path.trim_matches('/').split('/'); @@ -122,11 +171,19 @@ impl<'a> Fdt<'a> { found_node } + /// Resolve an alias to its full path. + /// + /// Looks up the alias in the /aliases node and returns the corresponding + /// path string. fn resolve_alias(&self, alias: &str) -> Option<&'a str> { let aliases_node = self.find_by_path("/aliases")?; aliases_node.find_property_str(alias) } + /// Normalize a path to an absolute path. + /// + /// If the path starts with '/', it's returned as-is. Otherwise, + /// it's treated as an alias and resolved. fn normalize_path(&self, path: &'a str) -> Option<&'a str> { if path.starts_with('/') { Some(path) @@ -135,26 +192,33 @@ impl<'a> Fdt<'a> { } } - /// Translate device address to CPU physical address. + /// Translate a device address to a CPU physical address. /// - /// This function implements address translation similar to Linux's of_translate_address. - /// It walks up the device tree hierarchy, applying each parent's ranges property to - /// translate the child address space to parent address space, ultimately obtaining - /// the CPU physical address. + /// This function implements address translation similar to Linux's + /// `of_translate_address`. It walks up the device tree hierarchy, + /// applying each parent's `ranges` property to translate the child + /// address space to the parent address space. + /// + /// The translation starts from the node at `path` and walks up through + /// each parent, applying the `ranges` property until reaching the root. /// /// # Arguments + /// /// * `path` - Node path (absolute path starting with '/' or alias name) - /// * `address` - Device address from the node's reg property + /// * `address` - Device address from the node's `reg` property /// /// # Returns - /// The translated CPU physical address. If translation fails, returns the original address. + /// + /// The translated CPU physical address. If translation fails at any + /// point (e.g., a parent node has no `ranges` property), the original + /// address is returned. pub fn translate_address(&self, path: &'a str, address: u64) -> u64 { let path = match self.normalize_path(path) { Some(p) => p, None => return address, }; - // 分割路径为各级节点名称 + // Split path into component parts let path_parts: heapless::Vec<&str, 16> = path .trim_matches('/') .split('/') @@ -167,17 +231,17 @@ impl<'a> Fdt<'a> { let mut current_address = address; - // 从最深层的节点向上遍历,对每一层应用 ranges 转换 - // 注意:我们需要从倒数第二层开始(因为最后一层是目标节点本身) + // Walk up from the deepest node, applying ranges at each level + // Note: We start from the second-to-last level (the target node itself is skipped) for depth in (0..path_parts.len()).rev() { - // 构建到当前层的路径 + // Build the path to the current parent level let parent_parts = &path_parts[..depth]; if parent_parts.is_empty() { - // 已经到达根节点,不需要继续转换 + // Reached root node, no more translation needed break; } - // 查找父节点 + // Find the parent node let mut parent_path = heapless::String::<256>::new(); parent_path.push('/').ok(); for (i, part) in parent_parts.iter().enumerate() { @@ -192,25 +256,25 @@ impl<'a> Fdt<'a> { None => continue, }; - // 获取父节点的 ranges 属性 + // Get the parent's ranges property let ranges = match parent_node.ranges() { Some(r) => r, None => { - // 没有 ranges 属性,停止转换 + // No ranges property, stop translation break; } }; - // 在 ranges 中查找匹配的转换规则 + // Look for a matching translation rule in ranges let mut found = false; for range in ranges.iter() { - // 检查地址是否在当前 range 的范围内 + // Check if the address falls within this range if current_address >= range.child_address && current_address < range.child_address + range.length { - // 计算在 child address space 中的偏移 + // Calculate offset in child address space let offset = current_address - range.child_address; - // 转换到 parent address space + // Translate to parent address space current_address = range.parent_address + offset; found = true; break; @@ -218,15 +282,15 @@ impl<'a> Fdt<'a> { } if !found { - // 如果在 ranges 中没有找到匹配项,保持当前地址不变 - // 这通常意味着地址转换失败,但我们继续尝试上层 + // No matching range found, keep current address and continue + // This typically means translation failed, but we try upper levels } } current_address } - /// Get an iterator over memory reservation entries + /// Returns an iterator over memory reservation entries. pub fn memory_reservations(&self) -> MemoryReservationIter<'a> { MemoryReservationIter { data: self.data.as_slice(), @@ -234,6 +298,7 @@ impl<'a> Fdt<'a> { } } + /// Returns the /chosen node if it exists. pub fn chosen(&self) -> Option> { for node in self.all_nodes() { if let Node::Chosen(c) = node { @@ -243,6 +308,7 @@ impl<'a> Fdt<'a> { None } + /// Returns an iterator over all memory nodes. pub fn memory(&self) -> impl Iterator> + 'a { self.all_nodes().filter_map(|node| { if let Node::Memory(mem) = node { @@ -253,6 +319,7 @@ impl<'a> Fdt<'a> { }) } + /// Returns an iterator over nodes in the /reserved-memory region. pub fn reserved_memory(&self) -> impl Iterator> + 'a { ReservedMemoryIter { node_iter: self.all_nodes(), @@ -262,6 +329,10 @@ impl<'a> Fdt<'a> { } } +/// Iterator over nodes in the /reserved-memory region. +/// +/// Yields all child nodes of the /reserved-memory node, which describe +/// memory regions that are reserved for specific purposes. struct ReservedMemoryIter<'a> { node_iter: FdtIter<'a>, in_reserved_memory: bool, @@ -272,7 +343,7 @@ impl<'a> Iterator for ReservedMemoryIter<'a> { type Item = Node<'a>; fn next(&mut self) -> Option { - while let Some(node) = self.node_iter.next() { + for node in self.node_iter.by_ref() { if node.name() == "reserved-memory" { self.in_reserved_memory = true; self.reserved_level = node.level(); @@ -281,7 +352,7 @@ impl<'a> Iterator for ReservedMemoryIter<'a> { if self.in_reserved_memory { if node.level() <= self.reserved_level { - // 已经离开 reserved-memory 节点 + // Left the reserved-memory node self.in_reserved_memory = false; return None; } else { @@ -303,7 +374,7 @@ impl fmt::Display for Fdt<'_> { for node in self.all_nodes() { let level = node.level(); - // 关闭前一层级的节点 + // Close nodes from the previous level while prev_level > level { prev_level -= 1; write_indent(f, prev_level, " ")?; @@ -317,10 +388,10 @@ impl fmt::Display for Fdt<'_> { node.name() }; - // 打印节点头部 + // Print node header writeln!(f, "{} {{", name)?; - // 打印属性 + // Print properties for prop in node.properties() { write_indent(f, level + 1, " ")?; writeln!(f, "{};", prop)?; @@ -329,7 +400,7 @@ impl fmt::Display for Fdt<'_> { prev_level = level + 1; } - // 关闭剩余的节点 + // Close remaining nodes while prev_level > 0 { prev_level -= 1; write_indent(f, prev_level, " ")?; @@ -348,7 +419,7 @@ impl fmt::Debug for Fdt<'_> { for node in self.all_nodes() { let level = node.level(); - // 基础缩进 2 个 tab,每层再加 1 个 tab + // Base indentation is 2 tabs, plus 1 tab per level write_indent(f, level + 2, "\t")?; let name = if node.name().is_empty() { @@ -357,14 +428,14 @@ impl fmt::Debug for Fdt<'_> { node.name() }; - // 打印节点名称和基本信息 + // Print node name and basic info writeln!( f, "[{}] address_cells={}, size_cells={}", name, node.address_cells, node.size_cells )?; - // 打印属性 + // Print properties for prop in node.properties() { write_indent(f, level + 3, "\t")?; if let Some(v) = prop.as_address_cells() { @@ -378,7 +449,7 @@ impl fmt::Debug for Fdt<'_> { } else if let Some(p) = prop.as_phandle() { writeln!(f, "phandle: {}", p)?; } else { - // 默认处理未知属性 + // Default handling for unknown properties if prop.is_empty() { writeln!(f, "{}", prop.name())?; } else if let Some(s) = prop.as_str() { @@ -404,13 +475,13 @@ mod tests { #[test] fn test_memory_reservation_iterator() { - // 创建一个简单的测试数据:一个内存保留条目 + 终止符 + // Create simple test data: one memory reservation entry + terminator let mut test_data = [0u8; 32]; - // 地址: 0x80000000, 大小: 0x10000000 (256MB) + // Address: 0x80000000, Size: 0x10000000 (256MB) test_data[0..8].copy_from_slice(&0x80000000u64.to_be_bytes()); test_data[8..16].copy_from_slice(&0x10000000u64.to_be_bytes()); - // 终止符: address=0, size=0 + // Terminator: address=0, size=0 test_data[16..24].copy_from_slice(&0u64.to_be_bytes()); test_data[24..32].copy_from_slice(&0u64.to_be_bytes()); @@ -427,7 +498,7 @@ mod tests { #[test] fn test_empty_memory_reservation_iterator() { - // 只有终止符 + // Only terminator let mut test_data = [0u8; 16]; test_data[0..8].copy_from_slice(&0u64.to_be_bytes()); test_data[8..16].copy_from_slice(&0u64.to_be_bytes()); diff --git a/fdt-raw/src/header.rs b/fdt-raw/src/header.rs index c2c0eee..54dc2db 100644 --- a/fdt-raw/src/header.rs +++ b/fdt-raw/src/header.rs @@ -1,13 +1,29 @@ +//! FDT header parsing. +//! +//! This module handles parsing of the Flattened Device Tree header structure, +//! which appears at the beginning of every device tree blob and contains +//! metadata about the layout and version of the FDT. + use core::ptr::NonNull; use crate::FdtError; +/// A 4-byte aligned buffer for header data. +/// +/// The Device Tree Blob specification requires 4-byte alignment, and this +/// wrapper ensures that we have properly aligned memory when reading from +/// potentially unaligned pointers. #[repr(align(4))] struct AlignedHeader([u8; size_of::
()]); +/// The FDT header structure. +/// +/// Every device tree blob begins with this header, which contains metadata +/// about the layout and version of the FDT. All fields are stored in big-endian +/// byte order on-disk and are converted to host byte order when parsed. #[derive(Debug, Clone)] pub struct Header { - /// FDT header magic + /// FDT header magic number (must be 0xd00dfeed) pub magic: u32, /// Total size in bytes of the FDT structure pub totalsize: u32, @@ -15,24 +31,31 @@ pub struct Header { pub off_dt_struct: u32, /// Offset in bytes from the start of the header to the strings block pub off_dt_strings: u32, - /// Offset in bytes from the start of the header to the memory reservation - /// block + /// Offset in bytes from the start of the header to the memory reservation block pub off_mem_rsvmap: u32, - /// FDT version + /// FDT version number pub version: u32, /// Last compatible FDT version pub last_comp_version: u32, - /// System boot CPU ID + /// Physical ID of the boot CPU pub boot_cpuid_phys: u32, /// Length in bytes of the strings block pub size_dt_strings: u32, - /// Length in bytes of the struct block + /// Length in bytes of the structure block pub size_dt_struct: u32, } impl Header { - /// Read a header from a byte slice and return an owned `Header` whose - /// fields are converted from big-endian (on-disk) to host order. + /// Read a header from a byte slice. + /// + /// Parses an FDT header from the beginning of a byte slice, validating + /// the magic number and converting all fields from big-endian to host order. + /// + /// # Errors + /// + /// Returns `FdtError::BufferTooSmall` if the slice is too small to contain + /// a complete header, or `FdtError::InvalidMagic` if the magic number doesn't + /// match the expected value. pub fn from_bytes(data: &[u8]) -> Result { if data.len() < core::mem::size_of::
() { return Err(FdtError::BufferTooSmall { @@ -43,14 +66,22 @@ impl Header { unsafe { Self::from_ptr(ptr.as_ptr()) } } - /// Read a header from a raw pointer and return an owned `Header` whose - /// fields are converted from big-endian (on-disk) to host order. + /// Read a header from a raw pointer. + /// + /// Parses an FDT header from the memory location pointed to by `ptr`, + /// validating the magic number and converting all fields from big-endian + /// to host order. Handles unaligned pointers by copying to an aligned buffer. /// /// # Safety /// /// The caller must ensure that the pointer is valid and points to a /// memory region of at least `size_of::
()` bytes that contains a - /// valid device tree blob. + /// valid device tree blob header. + /// + /// # Errors + /// + /// Returns `FdtError::InvalidPtr` if the pointer is null, or + /// `FdtError::InvalidMagic` if the magic number doesn't match. pub unsafe fn from_ptr(ptr: *mut u8) -> Result { if !(ptr as usize).is_multiple_of(core::mem::align_of::
()) { // Pointer is not aligned, so we need to copy the data to an aligned @@ -70,6 +101,15 @@ impl Header { } } + /// Read a header from an aligned pointer. + /// + /// Internal helper that assumes the pointer is already 4-byte aligned. + /// Reads the raw header bytes and converts each field from big-endian. + /// + /// # Safety + /// + /// Caller must ensure the pointer is valid, aligned, and points to + /// sufficient memory containing a valid FDT header. fn from_aligned_ptr(ptr: *mut u8) -> Result { let ptr = NonNull::new(ptr).ok_or(FdtError::InvalidPtr)?; diff --git a/fdt-raw/src/iter.rs b/fdt-raw/src/iter.rs index 2cca901..2090ff1 100644 --- a/fdt-raw/src/iter.rs +++ b/fdt-raw/src/iter.rs @@ -1,3 +1,8 @@ +//! Iterator for traversing all nodes in a Flattened Device Tree. +//! +//! This module provides `FdtIter`, which walks through the structure block +//! of an FDT and yields each node in a depth-first traversal order. + use log::error; use crate::{ @@ -6,21 +11,32 @@ use crate::{ node::{OneNodeIter, OneNodeState}, }; +/// Iterator over all nodes in a Flattened Device Tree. +/// +/// This iterator performs a depth-first traversal of the device tree, +/// yielding each node as it's encountered. It maintains a context stack +/// to track the `#address-cells` and `#size-cells` values inherited from +/// parent nodes. pub struct FdtIter<'a> { fdt: Fdt<'a>, reader: Reader<'a>, strings: Bytes<'a>, - /// 当前正在处理的节点迭代器 + /// The node iterator currently being processed node_iter: Option>, - /// 是否已终止(出错或结束) + /// Whether iteration has terminated (due to error or end) finished: bool, - /// 当前层级深度 + /// Current depth level in the tree level: usize, - /// 上下文栈,栈顶为当前上下文 + /// Context stack, with the top being the current context context_stack: heapless::Vec, } impl<'a> FdtIter<'a> { + /// Creates a new FDT iterator from an FDT instance. + /// + /// Initializes the reader at the start of the structure block and the + /// strings slice at the strings block. Also initializes the context + /// stack with default values. pub fn new(fdt: Fdt<'a>) -> Self { let header = fdt.header(); let struct_offset = header.off_dt_struct as usize; @@ -32,7 +48,7 @@ impl<'a> FdtIter<'a> { .data .slice(strings_offset..strings_offset + strings_size); - // 初始化上下文栈,压入默认上下文 + // Initialize context stack with default context let mut context_stack = heapless::Vec::new(); let _ = context_stack.push(NodeContext::default()); @@ -47,14 +63,14 @@ impl<'a> FdtIter<'a> { } } - /// 获取当前上下文(栈顶) + /// Returns the current context (top of the stack). #[inline] fn current_context(&self) -> &NodeContext { - // 栈永远不为空,因为初始化时压入了默认上下文 + // The stack is never empty because we push a default context on initialization self.context_stack.last().unwrap() } - /// 处理错误:输出错误日志并终止迭代 + /// Handles an error by logging it and terminating iteration. fn handle_error(&mut self, err: FdtError) { error!("FDT parse error: {}", err); self.finished = true; @@ -70,28 +86,28 @@ impl<'a> Iterator for FdtIter<'a> { } loop { - // 如果有正在处理的节点,继续处理它 + // If there's a node being processed, continue processing it if let Some(ref mut node_iter) = self.node_iter { match node_iter.process() { Ok(OneNodeState::ChildBegin) => { - // 遇到子节点,更新 reader 位置并清空当前节点迭代器 + // Child node encountered, update reader position and clear current node iterator self.reader = node_iter.reader().clone(); self.node_iter = None; - // 继续循环,下一次会读取 BeginNode token + // Continue loop, next iteration will read BeginNode token } Ok(OneNodeState::End) => { - // 当前节点结束,更新 reader 并降低层级 + // Current node ended, update reader and decrease level self.reader = node_iter.reader().clone(); self.node_iter = None; if self.level > 0 { self.level -= 1; - // 弹出栈顶,恢复父节点上下文 + // Pop stack to restore parent node context self.context_stack.pop(); } - // 继续循环处理下一个 token + // Continue loop to process next token } Ok(OneNodeState::Processing) => { - // 不应该到达这里 + // Should not reach here continue; } Err(e) => { @@ -102,10 +118,10 @@ impl<'a> Iterator for FdtIter<'a> { continue; } - // 读取下一个 token + // Read next token match self.reader.read_token() { Ok(Token::BeginNode) => { - // 创建新的节点迭代器来处理这个节点 + // Create new node iterator to handle this node let mut node_iter = OneNodeIter::new( self.reader.clone(), self.strings.clone(), @@ -114,41 +130,41 @@ impl<'a> Iterator for FdtIter<'a> { self.fdt.clone(), ); - // 读取节点名称 + // Read node name match node_iter.read_node_name() { Ok(mut node) => { - // 先处理节点属性以获取 address-cells, size-cells + // Process node properties to get address-cells, size-cells match node_iter.process() { Ok(state) => { let props = node_iter.parsed_props(); - // 更新节点的 cells + // Update node's cells node.address_cells = props.address_cells.unwrap_or(2); node.size_cells = props.size_cells.unwrap_or(1); - // 根据状态决定下一步动作 + // Decide next action based on state match state { OneNodeState::ChildBegin => { - // 有子节点,压入子节点上下文 + // Has child nodes, push child context let child_context = NodeContext { address_cells: node.address_cells, size_cells: node.size_cells, }; let _ = self.context_stack.push(child_context); - // 有子节点,更新 reader 位置 + // Has child nodes, update reader position self.reader = node_iter.reader().clone(); - // 增加层级(节点有子节点) + // Increase level (node has children) self.level += 1; } OneNodeState::End => { - // 节点已结束(没有子节点),更新 reader + // Node ended (no children), update reader self.reader = node_iter.reader().clone(); - // 不压栈,不更新上下文,因为节点没有子节点 - // 不增加层级,因为节点已经关闭 + // Don't push or update context since node has no children + // Don't increase level since node is already closed } OneNodeState::Processing => { - // 不应该到达这里,因为 process() 应该总是返回 ChildBegin 或 End + // Should not reach here, process() should always return ChildBegin or End self.node_iter = Some(node_iter); self.level += 1; } @@ -169,25 +185,25 @@ impl<'a> Iterator for FdtIter<'a> { } } Ok(Token::EndNode) => { - // 顶层 EndNode,降低层级 + // Top-level EndNode, decrease level if self.level > 0 { self.level -= 1; - // 弹出栈顶,恢复父节点上下文 + // Pop stack to restore parent node context self.context_stack.pop(); } continue; } Ok(Token::End) => { - // 结构块结束 + // Structure block ended self.finished = true; return None; } Ok(Token::Nop) => { - // 忽略 NOP + // Ignore NOP tokens continue; } Ok(Token::Prop) | Ok(Token::Data(_)) => { - // 在顶层遇到属性或未知数据是错误的 + // Property or unknown data at top level is an error self.handle_error(FdtError::BufferTooSmall { pos: self.reader.position(), }); diff --git a/fdt-raw/src/lib.rs b/fdt-raw/src/lib.rs index 5376c6e..b54c7a1 100644 --- a/fdt-raw/src/lib.rs +++ b/fdt-raw/src/lib.rs @@ -1,4 +1,44 @@ +//! Raw FDT parser without high-level abstractions. +//! +//! This crate provides a very low-level parser for Flattened Device Tree (FDT) files. +//! It is designed to be a minimal dependency that only handles the binary format +//! of device tree blobs without providing any node or property abstractions. +//! +//! # Features +//! +//! - `#![no_std]` compatible +//! - Zero-copy parsing where possible +//! - Direct access to the FDT structure blocks +//! - Minimal dependencies +//! +//! # Example +//! +//! ```no_run +//! use fdt_raw::{Fdt, Header}; +//! +//! # fn main() -> Result<(), Box> { +//! // Read FDT data from file or memory +//! let data = std::fs::read("path/to/device.dtb")?; +//! +//! // Parse the header +//! let header = Header::from_bytes(&data)?; +//! +//! println!("FDT version: {}", header.version); +//! println!("Total size: {} bytes", header.totalsize); +//! +//! // Create the FDT parser +//! let fdt = Fdt::from_bytes(&data)?; +//! +//! // Iterate over memory reservation entries +//! for rsv in fdt.memory_reservations() { +//! println!("Reserved: {:?} - {:?} bytes", rsv.address, rsv.size); +//! } +//! # Ok(()) +//! # } +//! ``` + #![no_std] +#![deny(warnings, missing_docs)] pub mod data; mod define; diff --git a/fdt-raw/src/node/chosen.rs b/fdt-raw/src/node/chosen.rs index c1be43c..4036729 100644 --- a/fdt-raw/src/node/chosen.rs +++ b/fdt-raw/src/node/chosen.rs @@ -1,29 +1,48 @@ +//! Chosen node type for boot parameters. +//! +//! This module provides the `Chosen` type which represents the /chosen node +//! in the device tree, containing boot parameters and system configuration. + use core::ops::Deref; use super::NodeBase; -/// Chosen 节点,包含启动参数等信息 +/// The /chosen node containing boot parameters. +/// +/// This node contains system configuration parameters chosen by the firmware +/// or bootloader, such as boot arguments, console paths, and other boot-time +/// settings. #[derive(Clone)] pub struct Chosen<'a> { node: NodeBase<'a>, } impl<'a> Chosen<'a> { + /// Creates a new Chosen wrapper from a NodeBase. pub(crate) fn new(node: NodeBase<'a>) -> Self { Self { node } } - /// 获取 bootargs 属性 + /// Returns the bootargs property value. + /// + /// This property contains command-line arguments to be passed to the + /// operating system kernel. pub fn bootargs(&self) -> Option<&'a str> { self.node.find_property_str("bootargs") } - /// 获取 stdout-path 属性 + /// Returns the stdout-path property value. + /// + /// This property specifies the path to the device to be used for + /// standard output (console). pub fn stdout_path(&self) -> Option<&'a str> { self.node.find_property_str("stdout-path") } - /// 获取 stdin-path 属性 + /// Returns the stdin-path property value. + /// + /// This property specifies the path to the device to be used for + /// standard input. pub fn stdin_path(&self) -> Option<&'a str> { self.node.find_property_str("stdin-path") } diff --git a/fdt-raw/src/node/memory.rs b/fdt-raw/src/node/memory.rs index fdcc3b7..e9987fb 100644 --- a/fdt-raw/src/node/memory.rs +++ b/fdt-raw/src/node/memory.rs @@ -1,30 +1,44 @@ +//! Memory node type for physical memory layout. +//! +//! This module provides the `Memory` type which represents memory nodes +//! in the device tree, describing the physical memory layout of the system. + use core::ops::Deref; use super::NodeBase; -/// 内存区域信息 +/// Memory region information. +/// +/// Represents a contiguous region of physical memory with its base address +/// and size. #[derive(Debug, Clone, Copy)] pub struct MemoryRegion { - /// 起始地址 + /// Base address of the memory region pub address: u64, - /// 区域大小 + /// Size of the memory region in bytes pub size: u64, } -/// Memory 节点,描述物理内存布局 +/// Memory node describing physical memory layout. +/// +/// This node type represents memory nodes in the device tree, which describe +/// the physical memory layout available to the system. The `reg` property +/// contains one or more memory regions. #[derive(Clone)] pub struct Memory<'a> { node: NodeBase<'a>, } impl<'a> Memory<'a> { + /// Creates a new Memory wrapper from a NodeBase. pub(crate) fn new(node: NodeBase<'a>) -> Self { Self { node } } - /// 获取内存区域迭代器 + /// Returns an iterator over memory regions. /// - /// Memory 节点的 reg 属性描述了物理内存的布局 + /// The `reg` property of a memory node describes the physical memory + /// layout, with each entry specifying a base address and size. pub fn regions(&self) -> impl Iterator + 'a { self.node.reg().into_iter().flat_map(|reg| { reg.map(|info| MemoryRegion { @@ -34,7 +48,10 @@ impl<'a> Memory<'a> { }) } - /// 获取所有内存区域(使用固定大小数组) + /// Returns all memory regions as a fixed-size array. + /// + /// This is useful for no_std environments where heap allocation is not + /// available. Returns a `heapless::Vec` with at most N entries. pub fn regions_array(&self) -> heapless::Vec { let mut result = heapless::Vec::new(); for region in self.regions() { @@ -45,7 +62,7 @@ impl<'a> Memory<'a> { result } - /// 计算总内存大小 + /// Returns the total memory size across all regions. pub fn total_size(&self) -> u64 { self.regions().map(|r| r.size).sum() } diff --git a/fdt-raw/src/node/mod.rs b/fdt-raw/src/node/mod.rs index 5d83e79..9137044 100644 --- a/fdt-raw/src/node/mod.rs +++ b/fdt-raw/src/node/mod.rs @@ -1,3 +1,10 @@ +//! Device tree node types and parsing. +//! +//! This module provides types for representing device tree nodes, +//! including the base node type and specialized variants like Chosen +//! and Memory nodes. It also contains the iterator logic for parsing +//! nodes from the FDT structure block. + use core::fmt; use core::ops::Deref; use core::{ffi::CStr, fmt::Debug}; @@ -16,12 +23,15 @@ pub use chosen::Chosen; pub use memory::{Memory, MemoryRegion}; pub use prop::{PropIter, Property, RangeInfo, RegInfo, RegIter, VecRange}; -/// 节点上下文,保存从父节点继承的信息 +/// Context inherited from a node's parent. +/// +/// Contains the `#address-cells` and `#size-cells` values that should +/// be used when parsing properties of the current node. #[derive(Clone)] pub(crate) struct NodeContext { - /// 父节点的 #address-cells (用于解析当前节点的 reg) + /// Parent node's #address-cells (used for parsing current node's reg) pub address_cells: u8, - /// 父节点的 #size-cells (用于解析当前节点的 reg) + /// Parent node's #size-cells (used for parsing current node's reg) pub size_cells: u8, } @@ -34,7 +44,10 @@ impl Default for NodeContext { } } -/// 基础节点结构 +/// Base device tree node structure. +/// +/// Contains the common data and methods available on all nodes, +/// including name, level, properties, and cell values. #[derive(Clone)] pub struct NodeBase<'a> { name: &'a str, @@ -42,42 +55,42 @@ pub struct NodeBase<'a> { strings: Bytes<'a>, level: usize, _fdt: Fdt<'a>, - /// 当前节点的 #address-cells(用于子节点) + /// Current node's #address-cells (used for child nodes) pub address_cells: u8, - /// 当前节点的 #size-cells(用于子节点) + /// Current node's #size-cells (used for child nodes) pub size_cells: u8, - /// 继承的上下文(包含父节点的 cells 和累积的 ranges) + /// Inherited context (contains parent's cells) context: NodeContext, } impl<'a> NodeBase<'a> { + /// Returns the node's name. pub fn name(&self) -> &'a str { self.name } + /// Returns the depth/level of this node in the tree. pub fn level(&self) -> usize { self.level } - /// 获取节点属性迭代器 + /// Returns an iterator over this node's properties. pub fn properties(&self) -> PropIter<'a> { PropIter::new(self.data.reader(), self.strings.clone()) } - /// 查找指定名称的属性 + /// Finds a property by name. pub fn find_property(&self, name: &str) -> Option> { self.properties().find(|p| p.name() == name) } - /// 查找指定名称的字符串属性 + /// Finds a string property by name. pub fn find_property_str(&self, name: &str) -> Option<&'a str> { let prop = self.find_property(name)?; - - // 否则作为普通字符串处理 prop.as_str() } - /// 查找并解析 reg 属性,返回 Reg 迭代器 + /// Finds and parses the `reg` property, returning a Reg iterator. pub fn reg(&self) -> Option> { let prop = self.find_property("reg")?; Some(RegIter::new( @@ -87,29 +100,30 @@ impl<'a> NodeBase<'a> { )) } - /// 查找并解析 reg 属性,返回所有 RegInfo 条目 + /// Finds and parses the `reg` property, returning all RegInfo entries. pub fn reg_array(&self) -> heapless::Vec { let mut result = heapless::Vec::new(); if let Some(reg) = self.reg() { for info in reg { if result.push(info).is_err() { - break; // 数组已满 + break; // Array is full } } } result } - /// 检查是否是 chosen 节点 + /// Checks if this is the chosen node. fn is_chosen(&self) -> bool { self.name == "chosen" } - /// 检查是否是 memory 节点 + /// Checks if this is a memory node. fn is_memory(&self) -> bool { self.name.starts_with("memory") } + /// Returns the `ranges` property if present. pub fn ranges(&self) -> Option> { let prop = self.find_property("ranges")?; Some(VecRange::new( @@ -120,6 +134,7 @@ impl<'a> NodeBase<'a> { )) } + /// Returns an iterator over compatible strings. pub fn compatibles(&self) -> impl Iterator { self.find_property("compatible") .into_iter() @@ -127,7 +142,7 @@ impl<'a> NodeBase<'a> { } } -/// 写入缩进 +/// Helper function for writing indentation during formatting. fn write_indent(f: &mut fmt::Formatter<'_>, count: usize, ch: &str) -> fmt::Result { for _ in 0..count { write!(f, "{}", ch)?; @@ -151,17 +166,20 @@ impl fmt::Display for NodeBase<'_> { } // ============================================================================ -// Node 枚举:支持特化节点类型 +// Node enum: supports specialized node types // ============================================================================ -/// 节点枚举,支持 General、Chosen、Memory 等特化类型 +/// Device tree node enum supporting specialized node types. +/// +/// Nodes are automatically classified into General, Chosen, or Memory +/// variants based on their name and properties. #[derive(Clone)] pub enum Node<'a> { - /// 通用节点 + /// A general-purpose node without special handling General(NodeBase<'a>), - /// Chosen 节点,包含启动参数 + /// The /chosen node containing boot parameters Chosen(Chosen<'a>), - /// Memory 节点,描述物理内存布局 + /// A memory node describing physical memory layout Memory(Memory<'a>), } @@ -205,26 +223,29 @@ impl fmt::Debug for Node<'_> { } } -/// 解析属性时提取的关键信息 +/// Key information extracted when parsing properties. #[derive(Debug, Clone, Default)] pub(crate) struct ParsedProps { pub address_cells: Option, pub size_cells: Option, } -/// 单节点迭代状态 +/// State of a single node iteration. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub(crate) enum OneNodeState { - /// 正在处理当前节点 + /// Currently processing the node Processing, - /// 遇到子节点的 BeginNode,需要回溯 + /// Encountered a child's BeginNode, needs to backtrack ChildBegin, - /// 遇到 EndNode,当前节点处理完成 + /// Encountered EndNode, current node processing complete End, } /// An iterator over a single node's content. -/// When encountering a child's BeginNode, it backtracks and signals FdtIter to handle it. +/// +/// When encountering a child's BeginNode token, it backtracks and signals +/// FdtIter to handle the child node. This allows FdtIter to maintain +/// proper tree traversal state. pub(crate) struct OneNodeIter<'a> { reader: Reader<'a>, strings: Bytes<'a>, @@ -236,6 +257,7 @@ pub(crate) struct OneNodeIter<'a> { } impl<'a> OneNodeIter<'a> { + /// Creates a new single node iterator. pub fn new( reader: Reader<'a>, strings: Bytes<'a>, @@ -254,20 +276,22 @@ impl<'a> OneNodeIter<'a> { } } + /// Returns a reference to the reader. pub fn reader(&self) -> &Reader<'a> { &self.reader } + /// Returns the parsed properties. pub fn parsed_props(&self) -> &ParsedProps { &self.parsed_props } - /// 读取节点名称(在 BeginNode token 之后调用) + /// Reads the node name (called after BeginNode token). pub fn read_node_name(&mut self) -> Result, FdtError> { - // 读取以 null 结尾的名称字符串 + // Read null-terminated name string let name = self.read_cstr()?; - // 对齐到 4 字节边界 + // Align to 4-byte boundary self.align4(); let data = self.reader.remain(); @@ -277,7 +301,7 @@ impl<'a> OneNodeIter<'a> { data, strings: self.strings.clone(), level: self.level, - // 默认值,会在 process() 中更新 + // Default values, will be updated in process() address_cells: 2, size_cells: 1, context: self.context.clone(), @@ -285,15 +309,17 @@ impl<'a> OneNodeIter<'a> { }) } + /// Reads a null-terminated string. fn read_cstr(&mut self) -> Result<&'a str, FdtError> { let bytes = self.reader.remain(); let cstr = CStr::from_bytes_until_nul(bytes.as_slice())?; let s = cstr.to_str()?; - // 跳过字符串内容 + null 终止符 + // Skip string content + null terminator let _ = self.reader.read_bytes(s.len() + 1); Ok(s) } + /// Aligns the reader to a 4-byte boundary. fn align4(&mut self) { let pos = self.reader.position(); let aligned = (pos + 3) & !3; @@ -303,25 +329,25 @@ impl<'a> OneNodeIter<'a> { } } - /// 从 strings block 读取属性名 + /// Reads a property name from the strings block. fn read_prop_name(&self, nameoff: u32) -> Result<&'a str, FdtError> { let bytes = self.strings.slice(nameoff as usize..self.strings.len()); let cstr = CStr::from_bytes_until_nul(bytes.as_slice())?; Ok(cstr.to_str()?) } - /// 读取 u32 从大端字节 + /// Reads a u32 from big-endian bytes. fn read_u32_be(data: &[u8], offset: usize) -> u64 { u32::from_be_bytes(data[offset..offset + 4].try_into().unwrap()) as u64 } - /// 处理节点内容,解析关键属性,遇到子节点或结束时返回 + /// Processes node content, parsing key properties until child node or end. pub fn process(&mut self) -> Result { loop { let token = self.reader.read_token()?; match token { Token::BeginNode => { - // 遇到子节点,回溯 token 并返回 + // Child node encountered, backtrack token and return self.reader.backtrack(4); self.state = OneNodeState::ChildBegin; return Ok(OneNodeState::ChildBegin); @@ -331,7 +357,7 @@ impl<'a> OneNodeIter<'a> { return Ok(OneNodeState::End); } Token::Prop => { - // 读取属性:len 和 nameoff + // Read property: len and nameoff let len = self.reader.read_u32().ok_or(FdtError::BufferTooSmall { pos: self.reader.position(), })? as usize; @@ -340,7 +366,7 @@ impl<'a> OneNodeIter<'a> { pos: self.reader.position(), })?; - // 读取属性数据 + // Read property data let prop_data = if len > 0 { self.reader .read_bytes(len) @@ -351,7 +377,7 @@ impl<'a> OneNodeIter<'a> { Bytes::new(&[]) }; - // 解析关键属性 + // Parse key properties if let Ok(prop_name) = self.read_prop_name(nameoff) { match prop_name { "#address-cells" if len == 4 => { @@ -366,19 +392,19 @@ impl<'a> OneNodeIter<'a> { } } - // 对齐到 4 字节边界 + // Align to 4-byte boundary self.align4(); } Token::Nop => { - // 忽略 NOP + // Ignore NOP tokens } Token::End => { - // 结构块结束 + // Structure block ended self.state = OneNodeState::End; return Ok(OneNodeState::End); } Token::Data(_) => { - // 非法 token + // Invalid token return Err(FdtError::BufferTooSmall { pos: self.reader.position(), }); diff --git a/fdt-raw/src/node/prop/mod.rs b/fdt-raw/src/node/prop/mod.rs index 5c77391..49736b0 100644 --- a/fdt-raw/src/node/prop/mod.rs +++ b/fdt-raw/src/node/prop/mod.rs @@ -1,4 +1,8 @@ -//! 属性相关类型和迭代器 +//! Device tree property types and iterators. +//! +//! This module provides types for representing and iterating over device tree +//! properties, including the generic `Property` type and specialized parsers +//! for common property formats like `reg` and `ranges`. mod ranges; mod reg; @@ -16,7 +20,11 @@ use crate::{ data::{Bytes, Reader, StrIter, U32Iter}, }; -/// 通用属性,包含名称和原始数据 +/// A generic device tree property containing name and raw data. +/// +/// Represents a property with a name and associated data. Provides methods +/// for accessing and interpreting the data in various formats (u32, u64, +/// strings, etc.). #[derive(Clone)] pub struct Property<'a> { name: &'a str, @@ -24,42 +32,51 @@ pub struct Property<'a> { } impl<'a> Property<'a> { + /// Creates a new property from a name and data bytes. pub fn new(name: &'a str, data: Bytes<'a>) -> Self { Self { name, data } } + /// Returns the property name. pub fn name(&self) -> &'a str { self.name } + /// Returns the property data. pub fn data(&self) -> Bytes<'a> { self.data.clone() } + /// Returns true if the property has no data. pub fn is_empty(&self) -> bool { self.data.is_empty() } + /// Returns the length of the property data in bytes. pub fn len(&self) -> usize { self.data.len() } - /// 作为 u32 迭代器 + /// Returns an iterator over u32 values in the property data. pub fn as_u32_iter(&self) -> U32Iter<'a> { self.data.as_u32_iter() } - /// 作为字符串迭代器(用于 compatible 等属性) + /// Returns an iterator over null-terminated strings in the property data. + /// + /// Used for properties like `compatible` that contain multiple strings. pub fn as_str_iter(&self) -> StrIter<'a> { self.data.as_str_iter() } - /// 获取数据作为字节切片 + /// Returns the property data as a byte slice. pub fn as_slice(&self) -> &[u8] { self.data.as_slice() } - /// 作为单个 u64 值 + /// Returns the data as a single u64 value. + /// + /// Returns None if the data is not exactly 8 bytes. pub fn as_u64(&self) -> Option { let mut iter = self.as_u32_iter(); let high = iter.next()? as u64; @@ -70,7 +87,9 @@ impl<'a> Property<'a> { Some((high << 32) | low) } - /// 作为单个 u32 值 + /// Returns the data as a single u32 value. + /// + /// Returns None if the data is not exactly 4 bytes. pub fn as_u32(&self) -> Option { let mut iter = self.as_u32_iter(); let value = iter.next()?; @@ -80,14 +99,16 @@ impl<'a> Property<'a> { Some(value) } - /// 作为字符串 + /// Returns the data as a null-terminated string. pub fn as_str(&self) -> Option<&'a str> { let bytes = self.data.as_slice(); let cstr = CStr::from_bytes_until_nul(bytes).ok()?; cstr.to_str().ok() } - /// 获取为 #address-cells 值 + /// Returns the property value as #address-cells. + /// + /// Only returns a value if the property name is "#address-cells". pub fn as_address_cells(&self) -> Option { if self.name == "#address-cells" { self.as_u32().map(|v| v as u8) @@ -96,7 +117,9 @@ impl<'a> Property<'a> { } } - /// 获取为 #size-cells 值 + /// Returns the property value as #size-cells. + /// + /// Only returns a value if the property name is "#size-cells". pub fn as_size_cells(&self) -> Option { if self.name == "#size-cells" { self.as_u32().map(|v| v as u8) @@ -105,7 +128,9 @@ impl<'a> Property<'a> { } } - /// 获取为 #interrupt-cells 值 + /// Returns the property value as #interrupt-cells. + /// + /// Only returns a value if the property name is "#interrupt-cells". pub fn as_interrupt_cells(&self) -> Option { if self.name == "#interrupt-cells" { self.as_u32().map(|v| v as u8) @@ -114,7 +139,9 @@ impl<'a> Property<'a> { } } - /// 获取为 status 枚举 + /// Returns the property value as a Status enum. + /// + /// Only returns a value if the property name is "status". pub fn as_status(&self) -> Option { let v = self.as_str()?; if self.name == "status" { @@ -128,7 +155,9 @@ impl<'a> Property<'a> { } } - /// 获取为 phandle + /// Returns the property value as a phandle. + /// + /// Only returns a value if the property name is "phandle". pub fn as_phandle(&self) -> Option { if self.name == "phandle" { self.as_u32().map(Phandle::from) @@ -137,7 +166,9 @@ impl<'a> Property<'a> { } } - /// 获取为 device_type 字符串 + /// Returns the property value as device_type string. + /// + /// Only returns a value if the property name is "device_type". pub fn as_device_type(&self) -> Option<&'a str> { if self.name == "device_type" { self.as_str() @@ -146,7 +177,9 @@ impl<'a> Property<'a> { } } - /// 获取为 interrupt-parent + /// Returns the property value as interrupt-parent phandle. + /// + /// Only returns a value if the property name is "interrupt-parent". pub fn as_interrupt_parent(&self) -> Option { if self.name == "interrupt-parent" { self.as_u32().map(Phandle::from) @@ -155,7 +188,9 @@ impl<'a> Property<'a> { } } - /// 获取为 clock-names 字符串列表 + /// Returns the property value as clock-names string list. + /// + /// Only returns a value if the property name is "clock-names". pub fn as_clock_names(&self) -> Option> { if self.name == "clock-names" { Some(self.as_str_iter()) @@ -164,7 +199,9 @@ impl<'a> Property<'a> { } } - /// 获取为 compatible 字符串列表 + /// Returns the property value as compatible string list. + /// + /// Only returns a value if the property name is "compatible". pub fn as_compatible(&self) -> Option> { if self.name == "compatible" { Some(self.as_str_iter()) @@ -173,7 +210,9 @@ impl<'a> Property<'a> { } } - /// 是否为 dma-coherent 属性 + /// Returns true if this is a dma-coherent property. + /// + /// A dma-coherent property has no data and indicates DMA coherence. pub fn is_dma_coherent(&self) -> bool { self.name == "dma-coherent" && self.data.is_empty() } @@ -190,8 +229,8 @@ impl fmt::Display for Property<'_> { } else if let Some(v) = self.as_interrupt_cells() { write!(f, "#interrupt-cells = <{:#x}>", v) } else if self.name() == "reg" { - // reg 属性需要特殊处理,但我们没有 context 信息 - // 直接显示原始数据 + // reg property needs special handling, but we lack context info + // Display raw data write!(f, "reg = ")?; format_bytes(f, &self.data()) } else if let Some(s) = self.as_status() { @@ -227,7 +266,7 @@ impl fmt::Display for Property<'_> { } else if self.is_dma_coherent() { write!(f, "dma-coherent") } else if let Some(s) = self.as_str() { - // 检查是否有多个字符串 + // Check if there are multiple strings if self.data().iter().filter(|&&b| b == 0).count() > 1 { write!(f, "{} = ", self.name())?; let mut first = true; @@ -243,21 +282,21 @@ impl fmt::Display for Property<'_> { write!(f, "{} = \"{}\"", self.name(), s) } } else if self.len() == 4 { - // 单个 u32 + // Single u32 let v = u32::from_be_bytes(self.data().as_slice().try_into().unwrap()); write!(f, "{} = <{:#x}>", self.name(), v) } else { - // 原始字节 + // Raw bytes write!(f, "{} = ", self.name())?; format_bytes(f, &self.data()) } } } -/// 格式化字节数组为 DTS 格式 +/// Formats a byte array as DTS format. fn format_bytes(f: &mut fmt::Formatter<'_>, data: &[u8]) -> fmt::Result { if data.len().is_multiple_of(4) { - // 按 u32 格式化 + // Format as u32 values write!(f, "<")?; let mut first = true; for chunk in data.chunks(4) { @@ -270,7 +309,7 @@ fn format_bytes(f: &mut fmt::Formatter<'_>, data: &[u8]) -> fmt::Result { } write!(f, ">") } else { - // 按字节格式化 + // Format as bytes write!(f, "[")?; for (i, b) in data.iter().enumerate() { if i > 0 { @@ -282,7 +321,10 @@ fn format_bytes(f: &mut fmt::Formatter<'_>, data: &[u8]) -> fmt::Result { } } -/// 属性迭代器 +/// Property iterator. +/// +/// Iterates over properties within a node, parsing each property from the +/// device tree structure block. pub struct PropIter<'a> { reader: Reader<'a>, strings: Bytes<'a>, @@ -290,6 +332,7 @@ pub struct PropIter<'a> { } impl<'a> PropIter<'a> { + /// Creates a new property iterator. pub(crate) fn new(reader: Reader<'a>, strings: Bytes<'a>) -> Self { Self { reader, @@ -299,13 +342,13 @@ impl<'a> PropIter<'a> { } } - /// 处理错误:输出错误日志并终止迭代 + /// Handles errors: logs error and terminates iteration. fn handle_error(&mut self, err: FdtError) { error!("Property parse error: {}", err); self.finished = true; } - /// 从 strings block 读取属性名 + /// Reads a property name from the strings block. fn read_prop_name(&self, nameoff: u32) -> Result<&'a str, FdtError> { if nameoff as usize >= self.strings.len() { return Err(FdtError::BufferTooSmall { @@ -317,6 +360,7 @@ impl<'a> PropIter<'a> { Ok(cstr.to_str()?) } + /// Aligns the reader to a 4-byte boundary. fn align4(&mut self) { let pos = self.reader.position(); let aligned = (pos + 3) & !3; @@ -346,7 +390,7 @@ impl<'a> Iterator for PropIter<'a> { match token { Token::Prop => { - // 读取属性长度 + // Read property length let len = match self.reader.read_u32() { Some(b) => b, None => { @@ -357,7 +401,7 @@ impl<'a> Iterator for PropIter<'a> { } }; - // 读取属性名偏移 + // Read property name offset let nameoff = match self.reader.read_u32() { Some(b) => b, None => { @@ -368,7 +412,7 @@ impl<'a> Iterator for PropIter<'a> { } }; - // 读取属性数据 + // Read property data let prop_data = if len > 0 { match self.reader.read_bytes(len as _) { Some(b) => b, @@ -383,7 +427,7 @@ impl<'a> Iterator for PropIter<'a> { Bytes::new(&[]) }; - // 读取属性名 + // Read property name let name = match self.read_prop_name(nameoff) { Ok(n) => n, Err(e) => { @@ -392,23 +436,23 @@ impl<'a> Iterator for PropIter<'a> { } }; - // 对齐到 4 字节边界 + // Align to 4-byte boundary self.align4(); return Some(Property::new(name, prop_data)); } Token::BeginNode | Token::EndNode | Token::End => { - // 遇到节点边界,回溯并终止属性迭代 + // Encountered node boundary, backtrack and terminate property iteration self.reader.backtrack(4); self.finished = true; return None; } Token::Nop => { - // 忽略 NOP,继续 + // Ignore NOP and continue continue; } Token::Data(_) => { - // 非法 token + // Invalid token self.handle_error(FdtError::BufferTooSmall { pos: self.reader.position(), }); diff --git a/fdt-raw/src/node/prop/ranges.rs b/fdt-raw/src/node/prop/ranges.rs index 36cf01a..4407bde 100644 --- a/fdt-raw/src/node/prop/ranges.rs +++ b/fdt-raw/src/node/prop/ranges.rs @@ -1,5 +1,14 @@ +//! Ranges property parser for address translation. +//! +//! This module provides types for parsing the `ranges` property, which maps +//! child bus addresses to parent bus addresses for address translation. + use crate::data::{Bytes, Reader}; +/// Ranges property wrapper for parsing address translation entries. +/// +/// The `ranges` property maps child bus address ranges to parent bus address +/// ranges, enabling translation between address spaces. #[derive(Clone)] pub struct VecRange<'a> { address_cells: usize, @@ -9,6 +18,7 @@ pub struct VecRange<'a> { } impl<'a> VecRange<'a> { + /// Creates a new VecRange parser. pub(crate) fn new( address_cells: usize, parent_address_cells: usize, @@ -23,6 +33,7 @@ impl<'a> VecRange<'a> { } } + /// Returns an iterator over range entries. pub fn iter(&self) -> VecRangeIter<'a> { VecRangeIter { address_cells: self.address_cells, @@ -33,13 +44,21 @@ impl<'a> VecRange<'a> { } } +/// Range entry information. +/// +/// Represents a single entry in a `ranges` property, mapping a child bus +/// address range to a parent bus address range. #[derive(Debug, Clone)] pub struct RangeInfo { + /// Child bus address pub child_address: u64, + /// Parent bus address pub parent_address: u64, + /// Length of the region pub length: u64, } +/// Iterator over range entries. pub struct VecRangeIter<'a> { address_cells: usize, parent_address_cells: usize, diff --git a/fdt-raw/src/node/prop/reg.rs b/fdt-raw/src/node/prop/reg.rs index ac7b08e..66284e8 100644 --- a/fdt-raw/src/node/prop/reg.rs +++ b/fdt-raw/src/node/prop/reg.rs @@ -1,24 +1,33 @@ -//! Reg 属性相关类型 +//! Reg property parser for device register addresses. +//! +//! This module provides types for parsing the `reg` property, which describes +//! memory-mapped registers and address ranges for devices. use crate::data::Reader; -/// Reg 条目信息 +/// Reg entry information. +/// +/// Represents a single entry in a `reg` property, describing an address +/// range for a device's registers or memory. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct RegInfo { - /// 地址 + /// Base address pub address: u64, - /// 区域大小 + /// Region size (optional, as size can be 0) pub size: Option, } impl RegInfo { - /// 创建新的 RegInfo + /// Creates a new RegInfo. pub fn new(address: u64, size: Option) -> Self { Self { address, size } } } -/// Reg 迭代器 +/// Reg property iterator. +/// +/// Iterates over entries in a `reg` property, parsing address and size +/// values based on the parent node's #address-cells and #size-cells values. #[derive(Clone)] pub struct RegIter<'a> { reader: Reader<'a>, @@ -27,6 +36,7 @@ pub struct RegIter<'a> { } impl<'a> RegIter<'a> { + /// Creates a new Reg iterator. pub(crate) fn new(reader: Reader<'a>, address_cells: u8, size_cells: u8) -> RegIter<'a> { RegIter { reader, @@ -42,6 +52,8 @@ impl Iterator for RegIter<'_> { fn next(&mut self) -> Option { let address; let size; + + // Read address based on address_cells if self.address_cells == 1 { address = self.reader.read_u32().map(|addr| addr as u64)?; } else if self.address_cells == 2 { @@ -49,6 +61,8 @@ impl Iterator for RegIter<'_> { } else { return None; } + + // Read size based on size_cells if self.size_cells == 0 { size = None; } else if self.size_cells == 1 { @@ -56,7 +70,7 @@ impl Iterator for RegIter<'_> { } else if self.size_cells == 2 { size = self.reader.read_u64(); } else { - // 不支持的 size_cells + // Unsupported size_cells value return None; } diff --git a/fdt-raw/tests/node.rs b/fdt-raw/tests/node.rs index b067b8e..f8b7a5d 100644 --- a/fdt-raw/tests/node.rs +++ b/fdt-raw/tests/node.rs @@ -31,7 +31,7 @@ fn test_fdt_display() { let output = format!("{}", fdt); info!("FDT Display:\n{}", output); - // 验证基本 DTS 结构 + // Verify basic DTS structure let basic_checks = [ ("/dts-v1/;", "DTS version header"), ("/ {", "root node opening"), @@ -41,7 +41,7 @@ fn test_fdt_display() { assert!(output.contains(pattern), "Output should contain {desc}"); } - // 验证根节点属性 + // Verify root node properties let root_props = [ ("interrupt-parent = <0x8002>", "interrupt-parent property"), ("model = \"linux,dummy-virt\"", "model property"), @@ -53,7 +53,7 @@ fn test_fdt_display() { assert!(output.contains(pattern), "Should contain {desc}"); } - // 验证重要节点存在 + // Verify important nodes exist let important_nodes = [ ("psci {", "psci node opening"), ("memory@40000000 {", "memory node"), @@ -71,7 +71,7 @@ fn test_fdt_display() { assert!(output.contains(pattern), "Should contain {desc}"); } - // 验证重要属性 + // Verify important properties let important_props = [ ("device_type = \"memory\"", "memory device_type"), ("dma-coherent", "dma-coherent property"), @@ -82,7 +82,7 @@ fn test_fdt_display() { assert!(output.contains(pattern), "Should contain {desc}"); } - // 验证格式规范 + // Verify format specifications let format_checks = [ ("= <", "use '< >' for cell values"), ("= \"", "use '\" \"' for string values"), @@ -104,7 +104,7 @@ fn test_fdt_debug() { let output = format!("{:?}", fdt); info!("FDT Debug:\n{}", output); - // 验证基本 Debug 结构 + // Verify basic Debug structure let struct_checks = [ ("Fdt {", "Fdt struct opening"), ("header: Header", "header field"), @@ -117,7 +117,7 @@ fn test_fdt_debug() { ); } - // 验证 header 字段 + // Verify header fields let header_fields = [ ("magic:", "magic field"), ("totalsize:", "totalsize field"), @@ -134,7 +134,7 @@ fn test_fdt_debug() { assert!(output.contains(pattern), "Should contain header {desc}"); } - // 验证根节点信息 + // Verify root node information let root_node_checks = [ ("[/]", "root node"), ("address_cells=", "address_cells field"), @@ -149,7 +149,7 @@ fn test_fdt_debug() { assert!(output.contains(pattern), "Should contain {desc}"); } - // 验证数据格式 + // Verify data format let format_checks = [ ("0x", "hexadecimal numbers"), ("\"", "quoted strings"), @@ -160,7 +160,7 @@ fn test_fdt_debug() { assert!(output.contains(pattern), "Should contain {desc}"); } - // 验证特定节点 + // Verify specific nodes let specific_checks = [ ("memory@", "memory node"), ("soc", "soc node"), @@ -257,7 +257,7 @@ fn test_node_properties() { ); } else if let Some(s) = prop.as_status() { info!(" status = {:?}", s); - // 验证状态值的有效性 + // Verify status value validity match s { Status::Okay | Status::Disabled => {} } @@ -283,10 +283,10 @@ fn test_node_properties() { found_dma_coherent = true; info!(" dma-coherent (empty)"); } else { - // 处理未知属性 + // Handle unknown properties if let Some(s) = prop.as_str() { info!(" {} = \"{}\"", prop.name(), s); - // 验证字符串长度合理 + // Verify string length is reasonable assert!( s.len() <= 256, "String property too long: {} bytes", @@ -299,7 +299,7 @@ fn test_node_properties() { info!(" {} (empty)", prop.name()); } else { info!(" {} ({} bytes)", prop.name(), prop.len()); - // 验证属性长度合理 + // Verify property length is reasonable assert!( prop.len() <= 1024, "Property too large: {} bytes", @@ -307,7 +307,7 @@ fn test_node_properties() { ); } - // 验证属性名称 + // Verify property name assert!(!prop.name().is_empty(), "Property name should not be empty"); assert!( prop.name().len() <= 31, @@ -318,14 +318,14 @@ fn test_node_properties() { } } - // 验证找到了基本属性 + // Verify found basic properties assert!(found_address_cells, "Should find #address-cells property"); assert!(found_size_cells, "Should find #size-cells property"); assert!(found_compatible, "Should find compatible property"); assert!(found_device_type, "Should find device_type property"); assert!(found_reg, "Should find reg property"); - // 验证找到了其他重要属性 + // Verify found other important properties assert!(found_phandle, "Should find phandle property"); assert!( found_interrupt_parent, @@ -360,7 +360,7 @@ fn test_reg_parsing() { let reg_infos: Vec<_> = reg.collect(); - // 验证特定节点的 reg 属性 + // Verify reg property for specific nodes if node.name().starts_with("memory@") { found_memory_reg = true; @@ -370,7 +370,7 @@ fn test_reg_parsing() { ); let reg_info = ®_infos[0]; - // QEMU 内存地址验证 + // QEMU memory address verification assert_eq!( reg_info.address, 0x40000000, "Memory base address should be 0x40000000" @@ -400,14 +400,14 @@ fn test_reg_parsing() { reg_info.size ); - // 验证地址在预期范围内 (0xa000000 到 0xa003e00) + // Verify address is within expected range (0xa000000 to 0xa003e00) assert!( reg_info.address <= 0xa003e00, "Virtio MMIO address should be <= 0xa003e00, got {:#x}", reg_info.address ); - // 验证地址是 0x200 对齐的(每个设备占用 0x200 空间) + // Verify address is 0x200 aligned (each device occupies 0x200 space) assert_eq!( reg_info.address % 0x200, 0x0, @@ -454,7 +454,7 @@ fn test_reg_parsing() { } } - // 验证找到了所有预期的 reg 节点 + // Verify found all expected reg nodes assert!( found_memory_reg, "Should find memory node with reg property" @@ -477,12 +477,12 @@ fn test_reg_parsing() { fn test_memory_node() { init_logging(); - // 测试 RPi 4B DTB + // Test RPi 4B DTB info!("=== Testing RPi 4B DTB ==="); let raw = fdt_rpi_4b(); test_memory_in_fdt(&raw, "RPi 4B"); - // 测试 QEMU DTB + // Test QEMU DTB info!("\n=== Testing QEMU DTB ==="); let raw = fdt_qemu(); test_memory_in_fdt(&raw, "QEMU"); @@ -507,7 +507,7 @@ fn test_memory_in_fdt(raw: &[u8], name: &str) { node.level() ); - // 验证节点级别 - 内存节点应该在级别 1 + // Verify node level - memory node should be at level 1 assert_eq!( node.level(), 1, @@ -515,7 +515,7 @@ fn test_memory_in_fdt(raw: &[u8], name: &str) { node.level() ); - // 验证并解析 reg 属性 + // Verify and parse reg property let mut found_device_type = false; for prop in node.properties() { @@ -537,7 +537,7 @@ fn test_memory_in_fdt(raw: &[u8], name: &str) { } } - // 验证必要的属性 + // Verify required properties assert!( found_device_type, "Memory node should have device_type property" @@ -551,7 +551,7 @@ fn test_memory_in_fdt(raw: &[u8], name: &str) { name, i, reg_info.address, reg_info.size ); - // 基本验证:地址应该是有效的 + // Basic verification: address should be valid if reg_info.size.is_some() && reg_info.size.unwrap() > 0 { assert!( reg_info.size.unwrap() > 0, @@ -561,7 +561,7 @@ fn test_memory_in_fdt(raw: &[u8], name: &str) { } } - // 平台特定验证 + // Platform-specific verification if name == "QEMU" && !reg_infos.is_empty() { assert_eq!( reg_infos.len(), @@ -601,7 +601,6 @@ fn test_memory_in_fdt(raw: &[u8], name: &str) { info!("[{}] Found {} memory node(s)", name, memory_nodes_found); } - #[test] fn test_compatibles() { init_logging(); @@ -611,4 +610,4 @@ fn test_compatibles() { for compatible in node.compatibles() { info!("compatible: {}", compatible); } -} \ No newline at end of file +}