moa_fdt 0.1.4

FDT (Flattened Device Tree) 零拷贝解析器
Documentation
// Based on fdt 0.1.5 (MPL-2.0) by repnop, with modifications:
// - Single lifetime (no &Fdt back-reference in FdtNode)
// - FDT_END = 0x9 per DTSpec
// - MemoryRegion.address is usize, not *const u8
// - Non-recursive skip_current_node
// - Removed BigEndianU32/U64 newtypes
// - Removed from_ptr, pretty-printing

//! FDT (Flattened Device Tree) 零拷贝解析器
//!
//! 基于 fdt 0.1.5 (MPL-2.0) fork,针对内核场景优化。
#![no_std]

/// 节点与属性
pub mod node;
mod parsing;
/// 标准节点(memory / chosen / cpus)
pub mod standard_nodes;

use node::FdtNode;
use parsing::{CStr, FdtData};
use standard_nodes::{Chosen, Cpu, Memory, Root};

/// FDT 解析错误
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FdtError {
    /// magic 不匹配 `0xd00dfeed`
    BadMagic,
    /// 缓冲区小于头部声明的 `total_size`
    BufferTooSmall,
}

impl core::fmt::Display for FdtError {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        match self {
            Self::BadMagic => f.write_str("bad FDT magic"),
            Self::BufferTooSmall => f.write_str("buffer too small"),
        }
    }
}

#[derive(Debug, Clone, Copy)]
struct FdtHeader {
    totalsize: u32,
    off_dt_struct: u32,
    off_dt_strings: u32,
    off_mem_rsvmap: u32,
    #[allow(dead_code)]
    version: u32,
    #[allow(dead_code)]
    boot_cpuid_phys: u32,
    size_dt_strings: u32,
    size_dt_struct: u32,
}

impl FdtHeader {
    #[moa_sec_macros::init]
    fn from_bytes(bytes: &mut FdtData<'_>) -> Option<Self> {
        let magic = bytes.u32()?;
        if magic != 0xd00d_feed {
            return None;
        }
        Some(Self {
            totalsize: bytes.u32()?,
            off_dt_struct: bytes.u32()?,
            off_dt_strings: bytes.u32()?,
            off_mem_rsvmap: bytes.u32()?,
            version: bytes.u32()?,
            boot_cpuid_phys: {
                let _last_comp_version = bytes.u32()?;
                bytes.u32()?
            },
            size_dt_strings: bytes.u32()?,
            size_dt_struct: bytes.u32()?,
        })
    }

    #[inline(always)]
    fn struct_range(&self) -> core::ops::Range<usize> {
        let start = self.off_dt_struct as usize;
        start..start + self.size_dt_struct as usize
    }

    #[inline(always)]
    fn strings_range(&self) -> core::ops::Range<usize> {
        let start = self.off_dt_strings as usize;
        start..start + self.size_dt_strings as usize
    }
}

/// 解析后的 Flattened Device Tree
///
/// 零拷贝:所有引用指向构造时传入的 `&[u8]` 缓冲区。
#[derive(Clone, Copy)]
pub struct Fdt<'a> {
    data: &'a [u8],
    header: FdtHeader,
}

impl<'a> Fdt<'a> {
    /// 从字节缓冲区解析 FDT
    ///
    /// # Errors
    /// magic 不匹配或缓冲区过小时返回错误。
    #[moa_sec_macros::init]
    pub fn new(data: &'a [u8]) -> Result<Self, FdtError> {
        let mut stream = FdtData::new(data);
        let header = FdtHeader::from_bytes(&mut stream).ok_or(FdtError::BadMagic)?;

        if data.len() < header.totalsize as usize {
            return Err(FdtError::BufferTooSmall);
        }

        Ok(Self { data, header })
    }

    /// FDT 总大小(字节)
    #[inline(always)]
    pub fn total_size(&self) -> usize {
        self.header.totalsize as usize
    }

    /// 根节点 (`/`)
    #[inline(always)]
    pub fn root(&self) -> Root<'a> {
        Root { node: self.find_node("/").expect("/ is required") }
    }

    /// `/chosen` 节点
    #[inline(always)]
    pub fn chosen(&self) -> Chosen<'a> {
        Chosen {
            node: node::find_node(
                &mut FdtData::new(self.structs_block()),
                "/chosen",
                self.strings_block(),
                None,
            )
            .expect("/chosen is required"),
        }
    }

    /// CPU 迭代器
    #[inline(always)]
    pub fn cpus(&self) -> impl Iterator<Item = Cpu<'a>> {
        let parent = self.find_node("/cpus").expect("/cpus is required");
        parent
            .children()
            .filter(|c| c.name.split('@').next() == Some("cpu"))
            .map(move |cpu| Cpu { parent, node: cpu })
    }

    /// 所有 `/memory` 节点
    #[inline(always)]
    pub fn memory(&self) -> Memory<'a> {
        Memory { structs: self.structs_block(), strings: self.strings_block() }
    }

    /// 按绝对路径查找节点
    #[inline(always)]
    pub fn find_node(&self, path: &str) -> Option<FdtNode<'a>> {
        node::find_node(&mut FdtData::new(self.structs_block()), path, self.strings_block(), None)
    }

    /// 查找第一个 `compatible` 包含指定字符串的节点
    #[inline(always)]
    pub fn find_compatible(&self, with: &str) -> Option<FdtNode<'a>> {
        self.all_nodes().find(|n| n.compatible().is_some_and(|c| c.contains(with)))
    }

    /// 查找第一个 `compatible` 包含任一指定字符串的节点
    #[inline(always)]
    pub fn find_compatible_any(&self, with: &[&str]) -> Option<FdtNode<'a>> {
        self.all_nodes().find(|n| {
            n.compatible().is_some_and(|compats| compats.all().any(|c| with.contains(&c)))
        })
    }

    /// 深度优先遍历所有节点
    #[inline(always)]
    pub fn all_nodes(&self) -> impl Iterator<Item = FdtNode<'a>> {
        node::all_nodes(self.structs_block(), self.strings_block())
    }

    /// 遍历字符串块中的所有字符串
    #[inline(always)]
    pub fn strings(&self) -> impl Iterator<Item = &'a str> {
        let mut block = self.strings_block();
        core::iter::from_fn(move || {
            if block.is_empty() {
                return None;
            }
            let cstr = CStr::new(block)?;
            block = &block[cstr.len() + 1..];
            cstr.as_str()
        })
    }

    /// 原始 FDT 数据
    #[inline(always)]
    pub fn raw_data(&self) -> &'a [u8] {
        self.data
    }

    /// memreserve 表在 FDT 中的字节偏移
    #[inline(always)]
    pub fn memreserve_offset(&self) -> usize {
        self.header.off_mem_rsvmap as usize
    }

    /// 遍历 memreserve 表条目
    #[inline(always)]
    pub fn memreserve_entries(&self) -> MemreserveIter<'a> {
        let off = self.header.off_mem_rsvmap as usize;
        MemreserveIter { stream: FdtData::new(self.data.get(off..).unwrap_or(&[])) }
    }

    /// 解析 initrd 地址范围(从 `/chosen` 节点)
    ///
    /// 返回 `Some((phys_start, size))`
    #[inline(always)]
    pub fn initrd(&self) -> Option<(usize, usize)> {
        let chosen = self.find_node("/chosen")?;
        let start = chosen.property("linux,initrd-start")?.as_usize()?;
        let end = chosen.property("linux,initrd-end")?.as_usize()?;
        if end > start { Some((start, end - start)) } else { None }
    }

    /// 获取 `/reserved-memory` 节点(调用者遍历其 children 的 reg)
    #[inline(always)]
    pub fn reserved_memory_node(&self) -> Option<FdtNode<'a>> {
        self.find_node("/reserved-memory")
    }

    #[inline(always)]
    fn structs_block(&self) -> &'a [u8] {
        self.data.get(self.header.struct_range()).unwrap_or(&[])
    }

    #[inline(always)]
    fn strings_block(&self) -> &'a [u8] {
        self.data.get(self.header.strings_range()).unwrap_or(&[])
    }
}

/// memreserve 表迭代器
pub struct MemreserveIter<'a> {
    stream: FdtData<'a>,
}

impl Iterator for MemreserveIter<'_> {
    type Item = node::MemoryRegion;

    #[moa_sec_macros::init]
    #[allow(clippy::cast_possible_truncation)]
    fn next(&mut self) -> Option<Self::Item> {
        let addr = self.stream.u64()? as usize;
        let size = self.stream.u64()? as usize;
        if addr == 0 && size == 0 {
            return None;
        }
        Some(node::MemoryRegion { address: addr, size: Some(size) })
    }
}