fdt_rs/base/
parse.rs

1//! Low level flattened device tree parsing functions.
2//!
3
4use core::mem::size_of;
5use core::ptr;
6
7use num_traits::FromPrimitive;
8
9use crate::base::DevTree;
10use crate::error::{DevTreeError, Result};
11use crate::priv_util::SliceRead;
12use crate::spec::{fdt_prop_header, FdtTok, MAX_NODE_NAME_LEN};
13
14use fallible_iterator::FallibleIterator;
15
16/// This function implements the logic to tokenize the device tree's main structure block.
17///
18/// This function will return the next [`ParsedTok`] if one exists. If it succeeds in parsing
19/// a token, `off` will be incremented to the start of the next token within `buf`.
20///
21/// # Safety
22///
23/// 1. The provided buffer must contain a device tree structure block.
24///
25/// 2. The given offset into the buffer, `off`, must be u32 aligned.
26///
27///    If this function returns `Ok(Some(_))`, the offset is guaranteed to be u32 aligned.  This
28///    means that as long as this function is initially called with an aligned offset, this
29///    function may be iteratively called without checking the offset's alignment again.
30///
31pub unsafe fn next_devtree_token<'a>(
32    buf: &'a [u8],
33    off: &mut usize,
34) -> Result<Option<ParsedTok<'a>>> {
35    // These are guaranteed.
36    // We only produce associated offsets that are aligned to 32 bits and within the buffer.
37    debug_assert!(buf.as_ptr().add(*off) as usize % size_of::<u32>() == 0);
38    debug_assert!(buf.len() > (*off + size_of::<u32>()));
39
40    let fdt_tok_val = buf.unsafe_read_be_u32(*off)?;
41    *off += size_of::<u32>();
42
43    match FromPrimitive::from_u32(fdt_tok_val) {
44        Some(FdtTok::BeginNode) => {
45            // Read the name (or return an error if the device tree is incorrectly formatted).
46            let name = buf.nread_bstring0(*off, MAX_NODE_NAME_LEN - 1)?;
47
48            // Move to the end of name (adding null byte).
49            *off += name.len() + 1;
50            // Per spec - align back to u32.
51            *off += buf.as_ptr().add(*off).align_offset(size_of::<u32>());
52
53            Ok(Some(ParsedTok::BeginNode(ParsedBeginNode { name })))
54        }
55        Some(FdtTok::Prop) => {
56            // Get the memory we'll use as the header
57            let header_slice = buf
58                .get(*off..*off + size_of::<fdt_prop_header>())
59                .ok_or(DevTreeError::ParseError)?;
60            // Re-interpret the data as a fdt_header.
61            //
62            // We already checked length.
63            // We statically verify alignment by ensuring pointer alignment matches known u32 alignment.
64            assert_eq_align!(fdt_prop_header, u32);
65            #[allow(clippy::cast_ptr_alignment)]
66            let header = &*(header_slice.as_ptr() as *const fdt_prop_header);
67            let prop_len = u32::from(header.len) as usize;
68
69            // Move offset past prop header
70            *off += size_of::<fdt_prop_header>();
71            // Create a slice using the offset
72            let prop_buf = buf
73                .get(*off..*off + prop_len)
74                .ok_or(DevTreeError::ParseError)?;
75
76            // Move the offset past the prop data.
77            *off += prop_buf.len();
78            // Align back to u32.
79            *off += buf.as_ptr().add(*off).align_offset(size_of::<u32>());
80
81            let name_offset = u32::from(header.nameoff) as usize;
82            if name_offset > buf.len() {
83                return Err(DevTreeError::ParseError);
84            }
85            let name_offset = name_offset;
86
87            Ok(Some(ParsedTok::Prop(ParsedProp {
88                prop_buf,
89                name_offset,
90            })))
91        }
92        Some(FdtTok::EndNode) => Ok(Some(ParsedTok::EndNode)),
93        Some(FdtTok::Nop) => Ok(Some(ParsedTok::Nop)),
94        Some(FdtTok::End) => Ok(None),
95        None => {
96            // Invalid token
97            Err(DevTreeError::ParseError)
98        }
99    }
100}
101
102#[derive(Clone, Debug)]
103pub struct ParsedBeginNode<'a> {
104    pub name: &'a [u8],
105}
106
107impl<'a> PartialEq for ParsedBeginNode<'a> {
108    fn eq(&self, other: &Self) -> bool {
109        ptr::eq(self.name, other.name)
110    }
111}
112
113#[derive(Clone, Debug)]
114pub struct ParsedProp<'a> {
115    pub prop_buf: &'a [u8],
116    pub name_offset: usize,
117}
118
119impl<'a> PartialEq for ParsedProp<'a> {
120    fn eq(&self, other: &Self) -> bool {
121        ptr::eq(self.prop_buf, other.prop_buf) && self.name_offset == other.name_offset
122    }
123}
124
125/// Enumeration of all tokens within a device tree's structure block.
126#[derive(Clone, Debug, PartialEq)]
127pub enum ParsedTok<'a> {
128    BeginNode(ParsedBeginNode<'a>),
129    EndNode,
130    Prop(ParsedProp<'a>),
131    Nop,
132}
133
134#[derive(Debug, PartialEq)]
135pub struct DevTreeParseIter<'r, 'dt: 'r> {
136    pub offset: usize,
137    pub fdt: &'r DevTree<'dt>,
138}
139
140impl<'r, 'dt: 'r> DevTreeParseIter<'r, 'dt> {
141    pub fn new(fdt: &'r DevTree<'dt>) -> Self {
142        Self {
143            offset: fdt.off_dt_struct(),
144            fdt,
145        }
146    }
147}
148
149impl<'dt, 'a: 'dt> FallibleIterator for DevTreeParseIter<'dt, 'a> {
150    type Error = DevTreeError;
151    type Item = ParsedTok<'a>;
152
153    fn next(&mut self) -> Result<Option<Self::Item>> {
154        // Safe because we're passing an unmodified (by us) offset.
155        // next_devtree_token guaruntees alignment and out-of-bounds won't occur.
156        unsafe { next_devtree_token(self.fdt.buf(), &mut self.offset) }
157    }
158}