Skip to main content

fdt_raw/node/prop/
mod.rs

1//! Device tree property types and iterators.
2//!
3//! This module provides types for representing and iterating over device tree
4//! properties, including the generic `Property` type and specialized parsers
5//! for common property formats like `reg` and `ranges`.
6
7mod ranges;
8mod reg;
9
10use core::ffi::CStr;
11use core::fmt;
12
13use log::error;
14
15pub use ranges::*;
16pub use reg::{RegInfo, RegIter};
17
18use crate::{
19    FdtError, Phandle, Status, Token,
20    data::{Bytes, Reader, StrIter, U32_SIZE, U32Iter},
21};
22
23/// A generic device tree property containing name and raw data.
24///
25/// Represents a property with a name and associated data. Provides methods
26/// for accessing and interpreting the data in various formats (u32, u64,
27/// strings, etc.).
28#[derive(Clone)]
29pub struct Property<'a> {
30    name: &'a str,
31    data: Bytes<'a>,
32}
33
34impl<'a> Property<'a> {
35    /// Creates a new property from a name and data bytes.
36    pub fn new(name: &'a str, data: Bytes<'a>) -> Self {
37        Self { name, data }
38    }
39
40    /// Returns the property name.
41    pub fn name(&self) -> &'a str {
42        self.name
43    }
44
45    /// Returns the property data.
46    pub fn data(&self) -> Bytes<'a> {
47        self.data.clone()
48    }
49
50    /// Returns true if the property has no data.
51    pub fn is_empty(&self) -> bool {
52        self.data.is_empty()
53    }
54
55    /// Returns the length of the property data in bytes.
56    pub fn len(&self) -> usize {
57        self.data.len()
58    }
59
60    /// Returns an iterator over u32 values in the property data.
61    pub fn as_u32_iter(&self) -> U32Iter<'a> {
62        self.data.as_u32_iter()
63    }
64
65    /// Returns an iterator over null-terminated strings in the property data.
66    ///
67    /// Used for properties like `compatible` that contain multiple strings.
68    pub fn as_str_iter(&self) -> StrIter<'a> {
69        self.data.as_str_iter()
70    }
71
72    /// Returns the property data as a byte slice.
73    pub fn as_slice(&self) -> &[u8] {
74        self.data.as_slice()
75    }
76
77    /// Returns the data as a single u64 value.
78    ///
79    /// Returns None if the data is not exactly 8 bytes.
80    pub fn as_u64(&self) -> Option<u64> {
81        let mut iter = self.as_u32_iter();
82        let high = iter.next()? as u64;
83        let low = iter.next()? as u64;
84        if iter.next().is_some() {
85            return None;
86        }
87        Some((high << 32) | low)
88    }
89
90    /// Returns the data as a single u32 value.
91    ///
92    /// Returns None if the data is not exactly 4 bytes.
93    pub fn as_u32(&self) -> Option<u32> {
94        let mut iter = self.as_u32_iter();
95        let value = iter.next()?;
96        if iter.next().is_some() {
97            return None;
98        }
99        Some(value)
100    }
101
102    /// Returns the data as a null-terminated string.
103    pub fn as_str(&self) -> Option<&'a str> {
104        let bytes = self.data.as_slice();
105        let cstr = CStr::from_bytes_until_nul(bytes).ok()?;
106        cstr.to_str().ok()
107    }
108
109    /// Returns the property value as #address-cells.
110    ///
111    /// Only returns a value if the property name is "#address-cells".
112    pub fn as_address_cells(&self) -> Option<u8> {
113        if self.name == "#address-cells" {
114            self.as_u32().map(|v| v as u8)
115        } else {
116            None
117        }
118    }
119
120    /// Returns the property value as #size-cells.
121    ///
122    /// Only returns a value if the property name is "#size-cells".
123    pub fn as_size_cells(&self) -> Option<u8> {
124        if self.name == "#size-cells" {
125            self.as_u32().map(|v| v as u8)
126        } else {
127            None
128        }
129    }
130
131    /// Returns the property value as #interrupt-cells.
132    ///
133    /// Only returns a value if the property name is "#interrupt-cells".
134    pub fn as_interrupt_cells(&self) -> Option<u8> {
135        if self.name == "#interrupt-cells" {
136            self.as_u32().map(|v| v as u8)
137        } else {
138            None
139        }
140    }
141
142    /// Returns the property value as a Status enum.
143    ///
144    /// Only returns a value if the property name is "status".
145    pub fn as_status(&self) -> Option<Status> {
146        let v = self.as_str()?;
147        if self.name == "status" {
148            match v {
149                "okay" | "ok" => Some(Status::Okay),
150                "disabled" => Some(Status::Disabled),
151                _ => None,
152            }
153        } else {
154            None
155        }
156    }
157
158    /// Returns the property value as a phandle.
159    ///
160    /// Only returns a value if the property name is "phandle".
161    pub fn as_phandle(&self) -> Option<Phandle> {
162        if self.name == "phandle" {
163            self.as_u32().map(Phandle::from)
164        } else {
165            None
166        }
167    }
168
169    /// Returns the property value as device_type string.
170    ///
171    /// Only returns a value if the property name is "device_type".
172    pub fn as_device_type(&self) -> Option<&'a str> {
173        if self.name == "device_type" {
174            self.as_str()
175        } else {
176            None
177        }
178    }
179
180    /// Returns the property value as interrupt-parent phandle.
181    ///
182    /// Only returns a value if the property name is "interrupt-parent".
183    pub fn as_interrupt_parent(&self) -> Option<Phandle> {
184        if self.name == "interrupt-parent" {
185            self.as_u32().map(Phandle::from)
186        } else {
187            None
188        }
189    }
190
191    /// Returns the property value as clock-names string list.
192    ///
193    /// Only returns a value if the property name is "clock-names".
194    pub fn as_clock_names(&self) -> Option<StrIter<'a>> {
195        if self.name == "clock-names" {
196            Some(self.as_str_iter())
197        } else {
198            None
199        }
200    }
201
202    /// Returns the property value as compatible string list.
203    ///
204    /// Only returns a value if the property name is "compatible".
205    pub fn as_compatible(&self) -> Option<StrIter<'a>> {
206        if self.name == "compatible" {
207            Some(self.as_str_iter())
208        } else {
209            None
210        }
211    }
212
213    /// Returns true if this is a dma-coherent property.
214    ///
215    /// A dma-coherent property has no data and indicates DMA coherence.
216    pub fn is_dma_coherent(&self) -> bool {
217        self.name == "dma-coherent" && self.data.is_empty()
218    }
219}
220
221impl fmt::Display for Property<'_> {
222    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
223        if self.is_empty() {
224            return write!(f, "{}", self.name());
225        }
226
227        // Try typed formatters first
228        if let Some(result) = self.try_format_typed(f) {
229            return result;
230        }
231
232        // Named properties with special handling
233        match self.name() {
234            "reg" => {
235                write!(f, "reg = ")?;
236                format_bytes(f, &self.data())
237            }
238            _ => self.format_generic(f),
239        }
240    }
241}
242
243impl Property<'_> {
244    /// Attempts to format the property using its specific type formatter.
245    /// Returns `Some(result)` if a specific formatter was used, `None` otherwise.
246    fn try_format_typed(&self, f: &mut fmt::Formatter<'_>) -> Option<fmt::Result> {
247        if let Some(v) = self.as_address_cells() {
248            return Some(write!(f, "#address-cells = <{:#x}>", v));
249        }
250        if let Some(v) = self.as_size_cells() {
251            return Some(write!(f, "#size-cells = <{:#x}>", v));
252        }
253        if let Some(v) = self.as_interrupt_cells() {
254            return Some(write!(f, "#interrupt-cells = <{:#x}>", v));
255        }
256        if let Some(s) = self.as_status() {
257            return Some(write!(f, "status = \"{:?}\"", s));
258        }
259        if let Some(p) = self.as_phandle() {
260            return Some(write!(f, "phandle = {}", p));
261        }
262        if let Some(p) = self.as_interrupt_parent() {
263            return Some(write!(f, "interrupt-parent = {}", p));
264        }
265        if let Some(s) = self.as_device_type() {
266            return Some(write!(f, "device_type = \"{}\"", s));
267        }
268        if let Some(iter) = self.as_compatible() {
269            return Some(format_string_list(f, "compatible", iter));
270        }
271        if let Some(iter) = self.as_clock_names() {
272            return Some(format_string_list(f, "clock-names", iter));
273        }
274        if self.is_dma_coherent() {
275            return Some(write!(f, "dma-coherent"));
276        }
277        None
278    }
279
280    /// Formats the property as a generic value (string, number, or bytes).
281    fn format_generic(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
282        // Check for multiple strings
283        if self.has_multiple_strings() {
284            return format_string_list(f, self.name(), self.as_str_iter());
285        }
286
287        // Try as single string
288        if let Some(s) = self.as_str() {
289            return write!(f, "{} = \"{}\"", self.name(), s);
290        }
291
292        // Try as single u32
293        if self.len() == 4 {
294            let v = u32::from_be_bytes(self.data().as_slice().try_into().unwrap());
295            return write!(f, "{} = <{:#x}>", self.name(), v);
296        }
297
298        // Default to raw bytes
299        write!(f, "{} = ", self.name())?;
300        format_bytes(f, &self.data())
301    }
302
303    /// Checks if the property data contains multiple null-terminated strings.
304    fn has_multiple_strings(&self) -> bool {
305        self.data().iter().filter(|&&b| b == 0).count() > 1
306    }
307}
308
309/// Formats a list of strings as "name = "s1", "s2"".
310fn format_string_list<'a>(
311    f: &mut fmt::Formatter<'_>,
312    name: &str,
313    iter: impl Iterator<Item = &'a str>,
314) -> fmt::Result {
315    write!(f, "{} = ", name)?;
316    let mut first = true;
317    for s in iter {
318        if !first {
319            write!(f, ", ")?;
320        }
321        write!(f, "\"{}\"", s)?;
322        first = false;
323    }
324    Ok(())
325}
326
327/// Formats a byte array as DTS format.
328fn format_bytes(f: &mut fmt::Formatter<'_>, data: &[u8]) -> fmt::Result {
329    if data.len().is_multiple_of(4) {
330        // Format as u32 values
331        write!(f, "<")?;
332        let mut first = true;
333        for chunk in data.chunks(4) {
334            if !first {
335                write!(f, " ")?;
336            }
337            let v = u32::from_be_bytes(chunk.try_into().unwrap());
338            write!(f, "{:#x}", v)?;
339            first = false;
340        }
341        write!(f, ">")
342    } else {
343        // Format as bytes
344        write!(f, "[")?;
345        for (i, b) in data.iter().enumerate() {
346            if i > 0 {
347                write!(f, " ")?;
348            }
349            write!(f, "{:02x}", b)?;
350        }
351        write!(f, "]")
352    }
353}
354
355/// Property iterator.
356///
357/// Iterates over properties within a node, parsing each property from the
358/// device tree structure block. Properties are read sequentially until
359/// a node boundary (BeginNode, EndNode, or End token) is encountered.
360///
361/// # Examples
362///
363/// ```ignore
364/// for prop in node.properties() {
365///     println!("{}: {}", prop.name(), prop.len());
366/// }
367/// ```
368pub struct PropIter<'a> {
369    /// Reader for the property data
370    reader: Reader<'a>,
371    /// Strings block for resolving property names
372    strings: Bytes<'a>,
373    /// Whether iteration has terminated (due to error or boundary)
374    finished: bool,
375}
376
377impl<'a> PropIter<'a> {
378    /// Creates a new property iterator.
379    pub(crate) fn new(reader: Reader<'a>, strings: Bytes<'a>) -> Self {
380        Self {
381            reader,
382            strings,
383
384            finished: false,
385        }
386    }
387
388    /// Handles errors: logs error and terminates iteration.
389    fn handle_error(&mut self, err: FdtError) {
390        error!("Property parse error: {}", err);
391        self.finished = true;
392    }
393
394    /// Reads a property name from the strings block.
395    fn read_prop_name(&self, nameoff: u32) -> Result<&'a str, FdtError> {
396        if nameoff as usize >= self.strings.len() {
397            return Err(FdtError::BufferTooSmall {
398                pos: nameoff as usize,
399            });
400        }
401        let bytes = self.strings.slice(nameoff as usize..self.strings.len());
402        let cstr = CStr::from_bytes_until_nul(bytes.as_slice())?;
403        Ok(cstr.to_str()?)
404    }
405
406    /// Aligns the reader to a 4-byte boundary.
407    fn align4(&mut self) {
408        let pos = self.reader.position();
409        let aligned = (pos + U32_SIZE - 1) & !(U32_SIZE - 1);
410        let skip = aligned - pos;
411        if skip > 0 {
412            let _ = self.reader.read_bytes(skip);
413        }
414    }
415}
416
417impl<'a> Iterator for PropIter<'a> {
418    type Item = Property<'a>;
419
420    fn next(&mut self) -> Option<Self::Item> {
421        if self.finished {
422            return None;
423        }
424
425        loop {
426            let token = match self.reader.read_token() {
427                Ok(t) => t,
428                Err(e) => {
429                    self.handle_error(e);
430                    return None;
431                }
432            };
433
434            match token {
435                Token::Prop => {
436                    // Read property length
437                    let len = match self.reader.read_u32() {
438                        Some(b) => b,
439                        None => {
440                            self.handle_error(FdtError::BufferTooSmall {
441                                pos: self.reader.position(),
442                            });
443                            return None;
444                        }
445                    };
446
447                    // Read property name offset
448                    let nameoff = match self.reader.read_u32() {
449                        Some(b) => b,
450                        None => {
451                            self.handle_error(FdtError::BufferTooSmall {
452                                pos: self.reader.position(),
453                            });
454                            return None;
455                        }
456                    };
457
458                    // Read property data
459                    let prop_data = if len > 0 {
460                        match self.reader.read_bytes(len as _) {
461                            Some(b) => b,
462                            None => {
463                                self.handle_error(FdtError::BufferTooSmall {
464                                    pos: self.reader.position(),
465                                });
466                                return None;
467                            }
468                        }
469                    } else {
470                        Bytes::new(&[])
471                    };
472
473                    // Read property name
474                    let name = match self.read_prop_name(nameoff) {
475                        Ok(n) => n,
476                        Err(e) => {
477                            self.handle_error(e);
478                            return None;
479                        }
480                    };
481
482                    // Align to 4-byte boundary
483                    self.align4();
484
485                    return Some(Property::new(name, prop_data));
486                }
487                Token::BeginNode | Token::EndNode | Token::End => {
488                    // Encountered node boundary, backtrack and terminate property iteration
489                    self.reader.backtrack(U32_SIZE);
490                    self.finished = true;
491                    return None;
492                }
493                Token::Nop => {
494                    // Ignore NOP and continue
495                    continue;
496                }
497                Token::Data(_) => {
498                    // Invalid token
499                    self.handle_error(FdtError::BufferTooSmall {
500                        pos: self.reader.position(),
501                    });
502                    return None;
503                }
504            }
505        }
506    }
507}