cros_fdt/
fdt.rs

1// Copyright 2018 The ChromiumOS Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! This module writes Flattened Devicetree blobs as defined here:
6//! <https://devicetree-specification.readthedocs.io/en/stable/flattened-format.html>
7
8use std::collections::BTreeMap;
9use std::convert::TryInto;
10use std::io;
11
12use indexmap::map::Entry;
13use indexmap::IndexMap;
14use remain::sorted;
15use thiserror::Error as ThisError;
16
17use crate::path::Path;
18use crate::propval::FromFdtPropval;
19use crate::propval::ToFdtPropval;
20
21pub(crate) const SIZE_U32: usize = std::mem::size_of::<u32>();
22pub(crate) const SIZE_U64: usize = std::mem::size_of::<u64>();
23
24#[sorted]
25#[derive(ThisError, Debug)]
26pub enum Error {
27    #[error("Error applying device tree overlay: {}", .0)]
28    ApplyOverlayError(String),
29    #[error("Binary size must fit in 32 bits")]
30    BinarySizeTooLarge,
31    #[error("Duplicate node {}", .0)]
32    DuplicateNode(String),
33    #[error("I/O error dumping FDT to file code={} path={}", .0, .1.display())]
34    FdtDumpIoError(io::Error, std::path::PathBuf),
35    #[error("Error writing FDT to guest memory")]
36    FdtGuestMemoryWriteError,
37    #[error("I/O error code={0}")]
38    FdtIoError(io::Error),
39    #[error("Parse error reading FDT parameters: {}", .0)]
40    FdtParseError(String),
41    #[error("Error applying FDT tree filter: {}", .0)]
42    FilterError(String),
43    #[error("Invalid name string: {}", .0)]
44    InvalidName(String),
45    #[error("Invalid path: {}", .0)]
46    InvalidPath(String),
47    #[error("Invalid string value {}", .0)]
48    InvalidString(String),
49    #[error("Expected phandle value for IOMMU of type: {}, id: {:?}", .0, .1)]
50    MissingIommuPhandle(String, Option<u32>),
51    #[error("Property value is not valid")]
52    PropertyValueInvalid,
53    #[error("Property value size must fit in 32 bits")]
54    PropertyValueTooLarge,
55    #[error("Total size must fit in 32 bits")]
56    TotalSizeTooLarge,
57}
58
59impl From<io::Error> for Error {
60    fn from(value: io::Error) -> Self {
61        Self::FdtIoError(value)
62    }
63}
64
65pub type Result<T> = std::result::Result<T, Error>;
66type Blob<'a> = &'a [u8];
67
68const FDT_BEGIN_NODE: u32 = 0x00000001;
69const FDT_END_NODE: u32 = 0x00000002;
70const FDT_PROP: u32 = 0x00000003;
71const FDT_NOP: u32 = 0x00000004;
72const FDT_END: u32 = 0x00000009;
73
74// Consume and return `n` bytes from the beginning of a slice.
75fn consume<'a>(bytes: &mut &'a [u8], n: usize) -> Result<&'a [u8]> {
76    let mid = n;
77    if mid > bytes.len() {
78        Err(Error::PropertyValueInvalid)
79    } else {
80        let (data_bytes, rest) = bytes.split_at(n);
81        *(bytes) = rest;
82        Ok(data_bytes)
83    }
84}
85
86// Consume a u32 from a byte slice.
87#[inline]
88fn rdu32(data: &mut Blob) -> Result<u32> {
89    Ok(u32::from_be_bytes(
90        // Unwrap won't panic because the slice length is checked in consume().
91        consume(data, SIZE_U32)?.try_into().unwrap(),
92    ))
93}
94
95// Consume a u64 from a byte slice.
96#[inline]
97fn rdu64(data: &mut Blob) -> Result<u64> {
98    Ok(u64::from_be_bytes(
99        // Unwrap won't panic because the slice length is checked in consume().
100        consume(data, SIZE_U64)?.try_into().unwrap(),
101    ))
102}
103
104// Return the number of padding bytes required to align `size` to `alignment`.
105#[inline]
106fn align_pad_len(size: usize, alignment: usize) -> usize {
107    (alignment - size % alignment) % alignment
108}
109
110// Pad a byte vector to given alignment.
111#[inline]
112fn align_data(data: &mut Vec<u8>, alignment: usize) {
113    data.resize(align_pad_len(data.len(), alignment) + data.len(), 0u8);
114}
115
116// Construct a string from the start of a byte slice until the first null byte.
117pub(crate) fn c_str_to_string(input: Blob) -> Option<String> {
118    let size = input.iter().position(|&v| v == 0u8)?;
119    String::from_utf8(input[..size].to_vec()).ok()
120}
121
122// Verify FDT property name.
123fn is_valid_prop_name(name: &str) -> bool {
124    const ALLOWED_SPECIAL_CHARS: [u8; 7] = [b'.', b',', b'_', b'+', b'?', b'#', b'-'];
125    name.bytes()
126        .all(|c| c.is_ascii_alphanumeric() || ALLOWED_SPECIAL_CHARS.contains(&c))
127}
128
129// Verify FDT node name.
130fn is_valid_node_name(name: &str) -> bool {
131    const ALLOWED_SPECIAL_CHARS: [u8; 6] = [b'.', b',', b'_', b'+', b'-', b'@'];
132    const ADDR_SEP: u8 = b'@';
133    // At most one `@` separating node-name and unit-address
134    if name.bytes().filter(|&c| c == ADDR_SEP).count() > 1 {
135        return false;
136    }
137    name.bytes()
138        .all(|c| c.is_ascii_alphanumeric() || ALLOWED_SPECIAL_CHARS.contains(&c))
139}
140
141// An implementation of FDT header.
142#[derive(Default, Debug)]
143struct FdtHeader {
144    magic: u32,             // magic word
145    total_size: u32,        // total size of DT block
146    off_dt_struct: u32,     // offset to structure
147    off_dt_strings: u32,    // offset to strings
148    off_mem_rsvmap: u32,    // offset to memory reserve map
149    version: u32,           // format version
150    last_comp_version: u32, // last compatible version
151    boot_cpuid_phys: u32,   // Which physical CPU id we're booting on
152    size_dt_strings: u32,   // size of the strings block
153    size_dt_struct: u32,    // size of the structure block
154}
155
156impl FdtHeader {
157    const MAGIC: u32 = 0xd00dfeed;
158    const VERSION: u32 = 17;
159    const LAST_COMP_VERSION: u32 = 16;
160    const SIZE: usize = 10 * SIZE_U32;
161
162    // Create a new FdtHeader instance.
163    fn new(
164        total_size: u32,
165        off_dt_struct: u32,
166        off_dt_strings: u32,
167        off_mem_rsvmap: u32,
168        boot_cpuid_phys: u32,
169        size_dt_strings: u32,
170        size_dt_struct: u32,
171    ) -> Self {
172        Self {
173            magic: Self::MAGIC,
174            total_size,
175            off_dt_struct,
176            off_dt_strings,
177            off_mem_rsvmap,
178            version: Self::VERSION,
179            last_comp_version: Self::LAST_COMP_VERSION,
180            boot_cpuid_phys,
181            size_dt_strings,
182            size_dt_struct,
183        }
184    }
185
186    // Dump FDT header to a byte vector.
187    fn write_blob(&self, buffer: &mut [u8]) -> Result<()> {
188        assert_eq!(buffer.len(), Self::SIZE);
189        for (chunk, val_u32) in buffer.chunks_exact_mut(SIZE_U32).zip(&[
190            self.magic,
191            self.total_size,
192            self.off_dt_struct,
193            self.off_dt_strings,
194            self.off_mem_rsvmap,
195            self.version,
196            self.last_comp_version,
197            self.boot_cpuid_phys,
198            self.size_dt_strings,
199            self.size_dt_struct,
200        ]) {
201            chunk.copy_from_slice(&val_u32.to_be_bytes());
202        }
203        Ok(())
204    }
205
206    // Load FDT header from a byte slice.
207    fn from_blob(mut input: Blob) -> Result<Self> {
208        if input.len() < Self::SIZE {
209            return Err(Error::FdtParseError("invalid binary size".into()));
210        }
211        let input = &mut input;
212        let header = Self {
213            magic: rdu32(input)?,
214            total_size: rdu32(input)?,
215            off_dt_struct: rdu32(input)?,
216            off_dt_strings: rdu32(input)?,
217            off_mem_rsvmap: rdu32(input)?,
218            version: rdu32(input)?,
219            last_comp_version: rdu32(input)?,
220            boot_cpuid_phys: rdu32(input)?,
221            size_dt_strings: rdu32(input)?,
222            size_dt_struct: rdu32(input)?,
223        };
224        if header.magic != Self::MAGIC {
225            return Err(Error::FdtParseError("invalid header magic".into()));
226        }
227        if header.version < Self::VERSION {
228            return Err(Error::FdtParseError("unsupported FDT version".into()));
229        }
230        if header.off_mem_rsvmap >= header.off_dt_strings
231            || header.off_mem_rsvmap < FdtHeader::SIZE as u32
232        {
233            return Err(Error::FdtParseError(
234                "invalid reserved memory offset".into(),
235            ));
236        }
237
238        let off_dt_struct_end = header
239            .off_dt_struct
240            .checked_add(header.size_dt_struct)
241            .ok_or_else(|| Error::FdtParseError("struct end offset must fit in 32 bits".into()))?;
242        if off_dt_struct_end > header.off_dt_strings {
243            return Err(Error::FdtParseError("struct and strings overlap".into()));
244        }
245
246        let off_dt_strings_end = header
247            .off_dt_strings
248            .checked_add(header.size_dt_strings)
249            .ok_or_else(|| Error::FdtParseError("strings end offset must fit in 32 bits".into()))?;
250        if off_dt_strings_end > header.total_size {
251            return Err(Error::FdtParseError("strings data past total size".into()));
252        }
253
254        Ok(header)
255    }
256}
257
258// An implementation of FDT strings block (property names)
259#[derive(Default)]
260struct FdtStrings {
261    strings: Vec<u8>,
262    string_offsets: BTreeMap<String, u32>,
263}
264
265impl FdtStrings {
266    // Load the strings block from a byte slice.
267    fn from_blob(input: Blob) -> Result<Self> {
268        if input.last().is_some_and(|i| *i != 0) {
269            return Err(Error::FdtParseError(
270                "strings block missing null terminator".into(),
271            ));
272        }
273        let mut string_offsets = BTreeMap::new();
274        let mut offset = 0u32;
275        for bytes in input.split(|&x| x == 0u8) {
276            if bytes.is_empty() {
277                break;
278            }
279            let string = String::from_utf8(bytes.to_vec())
280                .map_err(|_| Error::FdtParseError("invalid value in strings block".into()))?;
281            string_offsets.insert(string, offset);
282            offset += u32::try_from(bytes.len() + 1).map_err(|_| Error::BinarySizeTooLarge)?;
283        }
284        Ok(Self {
285            strings: input.to_vec(),
286            string_offsets,
287        })
288    }
289
290    // Find an existing instance of a string `s`, or add it to the strings block.
291    // Returns the offset into the strings block.
292    fn intern_string(&mut self, s: &str) -> u32 {
293        if let Some(off) = self.string_offsets.get(s) {
294            *off
295        } else {
296            let off = self.strings.len() as u32;
297            self.strings.extend_from_slice(s.as_bytes());
298            self.strings.push(0u8);
299            self.string_offsets.insert(s.to_owned(), off);
300            off
301        }
302    }
303
304    // Write the strings blob to a `Write` object.
305    fn write_blob(&self, mut writer: impl io::Write) -> Result<()> {
306        Ok(writer.write_all(&self.strings)?)
307    }
308
309    // Return the string at given offset or `None` if such a string doesn't exist.
310    fn at_offset(&self, off: u32) -> Option<String> {
311        self.strings
312            .get(off as usize..)
313            .and_then(c_str_to_string)
314            .filter(|s| !s.is_empty())
315    }
316}
317
318/// Flattened device tree node.
319///
320/// This represents a single node from the FDT structure block. Every node may contain properties
321/// and other (child) nodes.
322#[derive(Debug, Clone)]
323pub struct FdtNode {
324    /// Node name
325    pub(crate) name: String,
326    pub(crate) props: IndexMap<String, Vec<u8>>,
327    pub(crate) subnodes: IndexMap<String, FdtNode>,
328}
329
330impl FdtNode {
331    // Create a new node with the given name, properties, and child nodes. Return an error if
332    // node or property names do not satisfy devicetree naming criteria.
333    pub(crate) fn new(
334        name: String,
335        props: IndexMap<String, Vec<u8>>,
336        subnodes: IndexMap<String, FdtNode>,
337    ) -> Result<Self> {
338        if !is_valid_node_name(&name) {
339            return Err(Error::InvalidName(name));
340        }
341        for pname in props.keys() {
342            if !is_valid_prop_name(pname) {
343                return Err(Error::InvalidName(pname.into()));
344            }
345        }
346        Ok(Self {
347            name,
348            props,
349            subnodes,
350        })
351    }
352
353    // Create an empty node with the given name.
354    pub(crate) fn empty(name: impl Into<String>) -> Result<Self> {
355        FdtNode::new(name.into(), [].into(), [].into())
356    }
357
358    fn read_token(input: &mut Blob) -> Result<u32> {
359        loop {
360            let value = rdu32(input)?;
361            if value != FDT_NOP {
362                return Ok(value);
363            }
364        }
365    }
366
367    // Parse binary content of an FDT node.
368    fn parse_node(input: &mut Blob, strings: &FdtStrings) -> Result<Self> {
369        // Node name
370        let name = c_str_to_string(input)
371            .ok_or_else(|| Error::FdtParseError("could not parse node name".into()))?;
372        let name_nbytes = name.len() + 1;
373        consume(input, name_nbytes + align_pad_len(name_nbytes, SIZE_U32))?;
374
375        // Node properties and subnodes
376        let mut props = IndexMap::new();
377        let mut subnodes = IndexMap::new();
378        let mut encountered_subnode = false; // Properties must appear before subnodes
379
380        loop {
381            match Self::read_token(input)? {
382                FDT_BEGIN_NODE => {
383                    encountered_subnode = true;
384                    let subnode = Self::parse_node(input, strings)?;
385                    match subnodes.entry(subnode.name.clone()) {
386                        Entry::Vacant(e) => e.insert(subnode),
387                        Entry::Occupied(_) => return Err(Error::DuplicateNode(subnode.name)),
388                    };
389                }
390                FDT_END_NODE => break,
391                FDT_PROP => {
392                    if encountered_subnode {
393                        return Err(Error::FdtParseError(
394                            "unexpected prop token after subnode".into(),
395                        ));
396                    }
397                    let prop_len = rdu32(input)? as usize;
398                    let prop_name_offset = rdu32(input)?;
399                    let prop_blob = consume(input, prop_len + align_pad_len(prop_len, SIZE_U32))?;
400                    let prop_name = strings.at_offset(prop_name_offset).ok_or_else(|| {
401                        Error::FdtParseError(format!(
402                            "invalid property name at {prop_name_offset:#x}",
403                        ))
404                    })?;
405                    // Keep the original (non-aligned) size as property value
406                    props.insert(prop_name, prop_blob[..prop_len].to_vec());
407                }
408                FDT_NOP => continue,
409                FDT_END => return Err(Error::FdtParseError("unexpected END token".into())),
410                t => return Err(Error::FdtParseError(format!("invalid FDT token {t}"))),
411            }
412        }
413        FdtNode::new(name, props, subnodes)
414    }
415
416    // Load an `FdtNode` instance from a slice of bytes.
417    fn from_blob(mut input: Blob, strings: &FdtStrings) -> Result<Self> {
418        let input = &mut input;
419        if Self::read_token(input)? != FDT_BEGIN_NODE {
420            return Err(Error::FdtParseError("expected begin node token".into()));
421        }
422        let root = Self::parse_node(input, strings)?;
423        if Self::read_token(input)? != FDT_END {
424            Err(Error::FdtParseError("expected end node token".into()))
425        } else {
426            Ok(root)
427        }
428    }
429
430    // Write binary contents of a node to a vector of bytes.
431    fn write_blob(&self, writer: &mut impl io::Write, strings: &mut FdtStrings) -> Result<()> {
432        // Token
433        writer.write_all(&FDT_BEGIN_NODE.to_be_bytes())?;
434        // Name
435        writer.write_all(self.name.as_bytes())?;
436        writer.write_all(&[0])?; // Node name terminator
437        let pad_len = align_pad_len(self.name.len() + 1, SIZE_U32);
438        writer.write_all(&vec![0; pad_len])?;
439        // Properties
440        for (propname, propblob) in self.props.iter() {
441            // Prop token
442            writer.write_all(&FDT_PROP.to_be_bytes())?;
443            // Prop size
444            writer.write_all(&(propblob.len() as u32).to_be_bytes())?;
445            // Prop name offset
446            writer.write_all(&strings.intern_string(propname).to_be_bytes())?;
447            // Prop value
448            writer.write_all(propblob)?;
449            let pad_len = align_pad_len(propblob.len(), SIZE_U32);
450            writer.write_all(&vec![0; pad_len])?;
451        }
452        // Subnodes
453        for subnode in self.subnodes.values() {
454            subnode.write_blob(writer, strings)?;
455        }
456        // Token
457        writer.write_all(&FDT_END_NODE.to_be_bytes())?;
458        Ok(())
459    }
460
461    // Iterate over property names defined for this node.
462    pub(crate) fn prop_names(&self) -> impl std::iter::Iterator<Item = &str> {
463        self.props.keys().map(|s| s.as_str())
464    }
465
466    // Return true if a property with the given name exists.
467    pub(crate) fn has_prop(&self, name: &str) -> bool {
468        self.props.contains_key(name)
469    }
470
471    /// Read property value if it exists.
472    ///
473    /// # Arguments
474    ///
475    /// `name` - name of the property.
476    pub fn get_prop<T>(&self, name: &str) -> Option<T>
477    where
478        T: FromFdtPropval,
479    {
480        T::from_propval(self.props.get(name)?.as_slice())
481    }
482
483    // Read a phandle value (a `u32`) at some offset within a property value.
484    // Returns `None` if a phandle value cannot be constructed.
485    pub(crate) fn phandle_at_offset(&self, name: &str, offset: usize) -> Option<u32> {
486        let data = self.props.get(name)?;
487        data.get(offset..offset + SIZE_U32)
488            .and_then(u32::from_propval)
489    }
490
491    // Overwrite a phandle value (a `u32`) at some offset within a property value.
492    // Returns `Err` if the property doesn't exist, or if the property value is too short to
493    // construct a `u32` at given offset. Does not change property value size.
494    pub(crate) fn update_phandle_at_offset(
495        &mut self,
496        name: &str,
497        offset: usize,
498        phandle: u32,
499    ) -> Result<()> {
500        let propval = self
501            .props
502            .get_mut(name)
503            .ok_or_else(|| Error::InvalidName(format!("property {name} does not exist")))?;
504        if let Some(bytes) = propval.get_mut(offset..offset + SIZE_U32) {
505            bytes.copy_from_slice(phandle.to_propval()?.as_slice());
506            Ok(())
507        } else {
508            Err(Error::PropertyValueInvalid)
509        }
510    }
511
512    /// Write a property.
513    ///
514    /// # Arguments
515    ///
516    /// `name` - name of the property; must be a valid property name according to DT spec.
517    /// `val` - value of the property (raw byte array).
518    pub fn set_prop<T>(&mut self, name: &str, value: T) -> Result<()>
519    where
520        T: ToFdtPropval,
521    {
522        if !is_valid_prop_name(name) {
523            return Err(Error::InvalidName(name.into()));
524        }
525        let bytes = value.to_propval()?;
526        // FDT property byte size must fit into a u32.
527        u32::try_from(bytes.len()).map_err(|_| Error::PropertyValueTooLarge)?;
528        self.props.insert(name.into(), bytes);
529        Ok(())
530    }
531
532    /// Return a reference to an existing subnode with given name, or `None` if it doesn't exist.
533    ///
534    /// # Arguments
535    ///
536    /// `name` - name of the node.
537    pub fn subnode(&self, name: &str) -> Option<&FdtNode> {
538        self.subnodes.get(name)
539    }
540
541    /// Create a node if it doesn't already exist, and return a mutable reference to it. Return
542    /// an error if the node name is not valid.
543    ///
544    /// # Arguments
545    ///
546    /// `name` - name of the node; must be a valid node name according to DT specification.
547    pub fn subnode_mut(&mut self, name: &str) -> Result<&mut FdtNode> {
548        if !self.subnodes.contains_key(name) {
549            self.subnodes.insert(name.into(), FdtNode::empty(name)?);
550        }
551        Ok(self.subnodes.get_mut(name).unwrap())
552    }
553
554    // Iterate subnode references.
555    pub(crate) fn iter_subnodes(&self) -> impl std::iter::Iterator<Item = &FdtNode> {
556        self.subnodes.values()
557    }
558
559    // Iterate mutable subnode references.
560    pub(crate) fn iter_subnodes_mut(&mut self) -> impl std::iter::Iterator<Item = &mut FdtNode> {
561        self.subnodes.values_mut()
562    }
563}
564
565/// Interface for creating and manipulating a Flattened Devicetree (FDT) and emitting
566/// a Devicetree Blob (DTB).
567///
568/// # Example
569///
570/// ```rust
571/// use cros_fdt::Fdt;
572///
573/// # fn main() -> cros_fdt::Result<()> {
574/// let mut fdt = Fdt::new(&[]);
575/// let root_node = fdt.root_mut();
576/// root_node.set_prop("compatible", "linux,dummy-virt")?;
577/// root_node.set_prop("#address-cells", 0x2u32)?;
578/// root_node.set_prop("#size-cells", 0x2u32)?;
579/// let chosen_node = root_node.subnode_mut("chosen")?;
580/// chosen_node.set_prop("linux,pci-probe-only", 1u32)?;
581/// chosen_node.set_prop("bootargs", "panic=-1 console=hvc0 root=/dev/vda")?;
582/// let dtb = fdt.finish().unwrap();
583/// # Ok(())
584/// # }
585/// ```
586pub struct Fdt {
587    pub(crate) reserved_memory: Vec<FdtReserveEntry>,
588    pub(crate) root: FdtNode,
589    strings: FdtStrings,
590    boot_cpuid_phys: u32,
591}
592
593/// Reserved physical memory region.
594///
595/// This represents an area of physical memory reserved by the firmware and unusable by the OS.
596/// For example, this could be used to preserve bootloader code or data used at runtime.
597#[derive(Clone, PartialEq, Debug)]
598pub struct FdtReserveEntry {
599    /// Physical address of the beginning of the reserved region.
600    pub address: u64,
601    /// Size of the reserved region in bytes.
602    pub size: u64,
603}
604
605// Last entry in the reserved memory section
606const RESVMEM_TERMINATOR: FdtReserveEntry = FdtReserveEntry::new(0, 0);
607
608impl FdtReserveEntry {
609    /// Create a new FdtReserveEntry
610    ///
611    /// # Arguments
612    ///
613    /// `address` - start of reserved memory region.
614    /// `size` - size of reserved memory region.
615    pub const fn new(address: u64, size: u64) -> Self {
616        Self { address, size }
617    }
618
619    // Load a reserved memory entry from a byte slice.
620    fn from_blob(input: &mut Blob) -> Result<Self> {
621        Ok(Self {
622            address: rdu64(input)?,
623            size: rdu64(input)?,
624        })
625    }
626
627    // Dump the entry as a vector of bytes.
628    fn write_blob(&self, mut writer: impl io::Write) -> Result<()> {
629        writer.write_all(&self.address.to_be_bytes())?;
630        writer.write_all(&self.size.to_be_bytes())?;
631        Ok(())
632    }
633}
634
635impl Fdt {
636    /// Create a new flattened device tree instance with an initialized root node.
637    ///
638    /// # Arguments
639    ///
640    /// `mem_reservations` - reserved physical memory regions to list in the FDT header.
641    pub fn new(mem_reservations: &[FdtReserveEntry]) -> Self {
642        Self {
643            reserved_memory: mem_reservations.to_vec(),
644            root: FdtNode::empty("").unwrap(),
645            strings: FdtStrings::default(),
646            boot_cpuid_phys: 0u32,
647        }
648    }
649
650    /// Set the `boot_cpuid_phys` field of the devicetree header.
651    ///
652    /// # Arguments
653    ///
654    /// `boot_cpuid_phys` - CPU ID
655    pub fn set_boot_cpuid_phys(&mut self, boot_cpuid_phys: u32) {
656        self.boot_cpuid_phys = boot_cpuid_phys;
657    }
658
659    // Parse the reserved memory block from a binary blob.
660    fn parse_reserved_memory(mut input: Blob) -> Result<Vec<FdtReserveEntry>> {
661        let mut entries = vec![];
662        let input = &mut input;
663        loop {
664            let entry = FdtReserveEntry::from_blob(input)?;
665            if entry == RESVMEM_TERMINATOR {
666                break;
667            }
668            entries.push(entry);
669        }
670        Ok(entries)
671    }
672
673    // Write the reserved memory block to a buffer.
674    fn write_reserved_memory(&self, mut writer: impl io::Write) -> Result<()> {
675        for entry in &self.reserved_memory {
676            entry.write_blob(&mut writer)?;
677        }
678        RESVMEM_TERMINATOR.write_blob(writer)
679    }
680
681    /// Load a flattened device tree from a byte slice.
682    ///
683    /// # Arguments
684    ///
685    /// `input` - byte slice from which to load the FDT.
686    pub fn from_blob(input: Blob) -> Result<Self> {
687        let header = input
688            .get(..FdtHeader::SIZE)
689            .ok_or_else(|| Error::FdtParseError("cannot extract header, input too small".into()))?;
690        let header = FdtHeader::from_blob(header)?;
691        if header.total_size as usize != input.len() {
692            return Err(Error::FdtParseError("input size doesn't match".into()));
693        }
694
695        let reserved_mem_blob = &input[header.off_mem_rsvmap as usize..];
696        let nodes_blob = &input[header.off_dt_struct as usize
697            ..(header.off_dt_struct + header.size_dt_struct) as usize];
698        let strings_blob = &input[header.off_dt_strings as usize
699            ..(header.off_dt_strings + header.size_dt_strings) as usize];
700
701        let reserved_memory = Self::parse_reserved_memory(reserved_mem_blob)?;
702        let strings = FdtStrings::from_blob(strings_blob)?;
703        let root = FdtNode::from_blob(nodes_blob, &strings)?;
704
705        Ok(Self {
706            reserved_memory,
707            root,
708            strings,
709            boot_cpuid_phys: header.boot_cpuid_phys,
710        })
711    }
712
713    // Write the structure block of the FDT
714    fn write_struct(&mut self, mut writer: impl io::Write) -> Result<()> {
715        self.root.write_blob(&mut writer, &mut self.strings)?;
716        writer.write_all(&FDT_END.to_be_bytes())?;
717        Ok(())
718    }
719
720    /// Finish writing the Devicetree Blob (DTB).
721    ///
722    /// Returns the DTB as a vector of bytes.
723    pub fn finish(&mut self) -> Result<Vec<u8>> {
724        let mut result = vec![0u8; FdtHeader::SIZE];
725        align_data(&mut result, SIZE_U64);
726
727        let off_mem_rsvmap = result.len();
728        self.write_reserved_memory(&mut result)?;
729        align_data(&mut result, SIZE_U64);
730
731        let off_dt_struct = result.len();
732        self.write_struct(&mut result)?;
733        align_data(&mut result, SIZE_U32);
734
735        let off_dt_strings = result.len();
736        self.strings.write_blob(&mut result)?;
737        let total_size = u32::try_from(result.len()).map_err(|_| Error::TotalSizeTooLarge)?;
738
739        let header = FdtHeader::new(
740            total_size,
741            off_dt_struct as u32,
742            off_dt_strings as u32,
743            off_mem_rsvmap as u32,
744            self.boot_cpuid_phys,
745            total_size - off_dt_strings as u32, // strings size
746            off_dt_strings as u32 - off_dt_struct as u32, // struct size
747        );
748        header.write_blob(&mut result[..FdtHeader::SIZE])?;
749        Ok(result)
750    }
751
752    /// Return a mutable reference to the root node of the FDT.
753    pub fn root_mut(&mut self) -> &mut FdtNode {
754        &mut self.root
755    }
756
757    /// Return a reference to the node the path points to, or `None` if it doesn't exist.
758    ///
759    /// # Arguments
760    ///
761    /// `path` - device tree path of the target node.
762    pub fn get_node<T: TryInto<Path>>(&self, path: T) -> Option<&FdtNode> {
763        let mut result_node = &self.root;
764        let path: Path = path.try_into().ok()?;
765        for node_name in path.iter() {
766            result_node = result_node.subnodes.get(node_name)?;
767        }
768        Some(result_node)
769    }
770
771    /// Return a mutable reference to the node the path points to, or `None` if it
772    /// doesn't exist.
773    ///
774    /// # Arguments
775    ///
776    /// `path` - device tree path of the target node.
777    pub fn get_node_mut<T: TryInto<Path>>(&mut self, path: T) -> Option<&mut FdtNode> {
778        let mut result_node = &mut self.root;
779        let path: Path = path.try_into().ok()?;
780        for node_name in path.iter() {
781            result_node = result_node.subnodes.get_mut(node_name)?;
782        }
783        Some(result_node)
784    }
785
786    /// Find a device tree path to the symbol exported by the FDT. The symbol must be a node label.
787    ///
788    /// # Arguments
789    ///
790    /// `symbol` - symbol to search for.
791    pub fn symbol_to_path(&self, symbol: &str) -> Result<Path> {
792        const SYMBOLS_NODE: &str = "__symbols__";
793        let Some(symbols_node) = self.root.subnode(SYMBOLS_NODE) else {
794            return Err(Error::InvalidPath("no symbols in fdt".into()));
795        };
796        symbols_node
797            .get_prop::<String>(symbol)
798            .ok_or_else(|| Error::InvalidName(format!("filter symbol {symbol} does not exist")))?
799            .parse()
800    }
801}
802
803#[cfg(test)]
804mod tests {
805    use super::*;
806
807    const FDT_BLOB_HEADER_ONLY: [u8; 0x48] = [
808        0xd0, 0x0d, 0xfe, 0xed, // 0000: magic (0xd00dfeed)
809        0x00, 0x00, 0x00, 0x48, // 0004: totalsize (0x48)
810        0x00, 0x00, 0x00, 0x38, // 0008: off_dt_struct (0x38)
811        0x00, 0x00, 0x00, 0x48, // 000C: off_dt_strings (0x48)
812        0x00, 0x00, 0x00, 0x28, // 0010: off_mem_rsvmap (0x28)
813        0x00, 0x00, 0x00, 0x11, // 0014: version (0x11 = 17)
814        0x00, 0x00, 0x00, 0x10, // 0018: last_comp_version (0x10 = 16)
815        0x00, 0x00, 0x00, 0x00, // 001C: boot_cpuid_phys (0)
816        0x00, 0x00, 0x00, 0x00, // 0020: size_dt_strings (0)
817        0x00, 0x00, 0x00, 0x10, // 0024: size_dt_struct (0x10)
818        0x00, 0x00, 0x00, 0x00, // 0028: rsvmap terminator (address = 0 high)
819        0x00, 0x00, 0x00, 0x00, // 002C: rsvmap terminator (address = 0 low)
820        0x00, 0x00, 0x00, 0x00, // 0030: rsvmap terminator (size = 0 high)
821        0x00, 0x00, 0x00, 0x00, // 0034: rsvmap terminator (size = 0 low)
822        0x00, 0x00, 0x00, 0x01, // 0038: FDT_BEGIN_NODE
823        0x00, 0x00, 0x00, 0x00, // 003C: node name ("") + padding
824        0x00, 0x00, 0x00, 0x02, // 0040: FDT_END_NODE
825        0x00, 0x00, 0x00, 0x09, // 0044: FDT_END
826    ];
827
828    const FDT_BLOB_RSVMAP: [u8; 0x68] = [
829        0xd0, 0x0d, 0xfe, 0xed, // 0000: magic (0xd00dfeed)
830        0x00, 0x00, 0x00, 0x68, // 0004: totalsize (0x68)
831        0x00, 0x00, 0x00, 0x58, // 0008: off_dt_struct (0x58)
832        0x00, 0x00, 0x00, 0x68, // 000C: off_dt_strings (0x68)
833        0x00, 0x00, 0x00, 0x28, // 0010: off_mem_rsvmap (0x28)
834        0x00, 0x00, 0x00, 0x11, // 0014: version (0x11 = 17)
835        0x00, 0x00, 0x00, 0x10, // 0018: last_comp_version (0x10 = 16)
836        0x00, 0x00, 0x00, 0x00, // 001C: boot_cpuid_phys (0)
837        0x00, 0x00, 0x00, 0x00, // 0020: size_dt_strings (0)
838        0x00, 0x00, 0x00, 0x10, // 0024: size_dt_struct (0x10)
839        0x12, 0x34, 0x56, 0x78, // 0028: rsvmap entry 0 address high
840        0xAA, 0xBB, 0xCC, 0xDD, // 002C: rsvmap entry 0 address low
841        0x00, 0x00, 0x00, 0x00, // 0030: rsvmap entry 0 size high
842        0x00, 0x00, 0x12, 0x34, // 0034: rsvmap entry 0 size low
843        0x10, 0x20, 0x30, 0x40, // 0038: rsvmap entry 1 address high
844        0x50, 0x60, 0x70, 0x80, // 003C: rsvmap entry 1 address low
845        0x00, 0x00, 0x00, 0x00, // 0040: rsvmap entry 1 size high
846        0x00, 0x00, 0x56, 0x78, // 0044: rsvmap entry 1 size low
847        0x00, 0x00, 0x00, 0x00, // 0048: rsvmap terminator (address = 0 high)
848        0x00, 0x00, 0x00, 0x00, // 004C: rsvmap terminator (address = 0 low)
849        0x00, 0x00, 0x00, 0x00, // 0050: rsvmap terminator (size = 0 high)
850        0x00, 0x00, 0x00, 0x00, // 0054: rsvmap terminator (size = 0 low)
851        0x00, 0x00, 0x00, 0x01, // 0058: FDT_BEGIN_NODE
852        0x00, 0x00, 0x00, 0x00, // 005C: node name ("") + padding
853        0x00, 0x00, 0x00, 0x02, // 0060: FDT_END_NODE
854        0x00, 0x00, 0x00, 0x09, // 0064: FDT_END
855    ];
856
857    const FDT_BLOB_STRINGS: [u8; 0x26] = [
858        b'n', b'u', b'l', b'l', 0x00, b'u', b'3', b'2', 0x00, b'u', b'6', b'4', 0x00, b's', b't',
859        b'r', 0x00, b's', b't', b'r', b'l', b's', b't', 0x00, b'a', b'r', b'r', b'u', b'3', b'2',
860        0x00, b'a', b'r', b'r', b'u', b'6', b'4', 0x00,
861    ];
862
863    const EXPECTED_STRINGS: [&str; 7] = ["null", "u32", "u64", "str", "strlst", "arru32", "arru64"];
864
865    const FDT_BLOB_NODES_ROOT_ONLY: [u8; 0x90] = [
866        0x00, 0x00, 0x00, 0x01, // FDT_BEGIN_NODE
867        0x00, 0x00, 0x00, 0x00, // node name ("") + padding
868        0x00, 0x00, 0x00, 0x03, // FDT_PROP (null)
869        0x00, 0x00, 0x00, 0x00, // prop len (0)
870        0x00, 0x00, 0x00, 0x00, // prop nameoff (0)
871        0x00, 0x00, 0x00, 0x03, // FDT_PROP (u32)
872        0x00, 0x00, 0x00, 0x04, // prop len (4)
873        0x00, 0x00, 0x00, 0x05, // prop nameoff (0x05)
874        0x12, 0x34, 0x56, 0x78, // prop u32 value (0x12345678)
875        0x00, 0x00, 0x00, 0x03, // FDT_PROP (u64)
876        0x00, 0x00, 0x00, 0x08, // prop len (8)
877        0x00, 0x00, 0x00, 0x09, // prop nameoff (0x09)
878        0x12, 0x34, 0x56, 0x78, // prop u64 value high (0x12345678)
879        0x87, 0x65, 0x43, 0x21, // prop u64 value low (0x87654321)
880        0x00, 0x00, 0x00, 0x03, // FDT_PROP (string)
881        0x00, 0x00, 0x00, 0x06, // prop len (6)
882        0x00, 0x00, 0x00, 0x0D, // prop nameoff (0x0D)
883        b'h', b'e', b'l', b'l', // prop str value ("hello") + padding
884        b'o', 0x00, 0x00, 0x00, // "o\0" + padding
885        0x00, 0x00, 0x00, 0x03, // FDT_PROP (string list)
886        0x00, 0x00, 0x00, 0x07, // prop len (7)
887        0x00, 0x00, 0x00, 0x11, // prop nameoff (0x11)
888        b'h', b'i', 0x00, b'b', // prop value ("hi", "bye")
889        b'y', b'e', 0x00, 0x00, // "ye\0" + padding
890        0x00, 0x00, 0x00, 0x03, // FDT_PROP (u32 array)
891        0x00, 0x00, 0x00, 0x08, // prop len (8)
892        0x00, 0x00, 0x00, 0x18, // prop nameoff (0x18)
893        0x12, 0x34, 0x56, 0x78, // prop value 0
894        0xAA, 0xBB, 0xCC, 0xDD, // prop value 1
895        0x00, 0x00, 0x00, 0x03, // FDT_PROP (u64 array)
896        0x00, 0x00, 0x00, 0x08, // prop len (8)
897        0x00, 0x00, 0x00, 0x1f, // prop nameoff (0x1F)
898        0x12, 0x34, 0x56, 0x78, // prop u64 value 0 high
899        0x87, 0x65, 0x43, 0x21, // prop u64 value 0 low
900        0x00, 0x00, 0x00, 0x02, // FDT_END_NODE
901        0x00, 0x00, 0x00, 0x09, // FDT_END
902    ];
903
904    /*
905    Node structure:
906    /
907    |- nested
908    |- nested2
909       |- nested3
910     */
911    const FDT_BLOB_NESTED_NODES: [u8; 0x80] = [
912        0x00, 0x00, 0x00, 0x01, // FDT_BEGIN_NODE
913        0x00, 0x00, 0x00, 0x00, // node name ("") + padding
914        0x00, 0x00, 0x00, 0x03, // FDT_PROP
915        0x00, 0x00, 0x00, 0x04, // prop len (4)
916        0x00, 0x00, 0x00, 0x00, // prop nameoff (0x00)
917        0x13, 0x57, 0x90, 0x24, // prop u32 value (0x13579024)
918        0x00, 0x00, 0x00, 0x01, // FDT_BEGIN_NODE
919        b'n', b'e', b's', b't', // Node name ("nested")
920        b'e', b'd', 0x00, 0x00, // "ed\0" + pad
921        0x00, 0x00, 0x00, 0x03, // FDT_PROP
922        0x00, 0x00, 0x00, 0x04, // prop len (4)
923        0x00, 0x00, 0x00, 0x05, // prop nameoff (0x05)
924        0x12, 0x12, 0x12, 0x12, // prop u32 value (0x12121212)
925        0x00, 0x00, 0x00, 0x03, // FDT_PROP
926        0x00, 0x00, 0x00, 0x04, // prop len (4)
927        0x00, 0x00, 0x00, 0x18, // prop nameoff (0x18)
928        0x13, 0x57, 0x90, 0x24, // prop u32 value (0x13579024)
929        0x00, 0x00, 0x00, 0x02, // FDT_END_NODE ("nested")
930        0x00, 0x00, 0x00, 0x01, // FDT_BEGIN_NODE
931        b'n', b'e', b's', b't', // Node name ("nested2")
932        b'e', b'd', b'2', 0x00, // "ed2\0"
933        0x00, 0x00, 0x00, 0x03, // FDT_PROP
934        0x00, 0x00, 0x00, 0x04, // prop len (0)
935        0x00, 0x00, 0x00, 0x05, // prop nameoff (0x05)
936        0x12, 0x12, 0x12, 0x12, // prop u32 value (0x12121212)
937        0x00, 0x00, 0x00, 0x01, // FDT_BEGIN_NODE
938        b'n', b'e', b's', b't', // Node name ("nested3")
939        b'e', b'd', b'3', 0x00, // "ed3\0"
940        0x00, 0x00, 0x00, 0x02, // FDT_END_NODE ("nested3")
941        0x00, 0x00, 0x00, 0x02, // FDT_END_NODE ("nested2")
942        0x00, 0x00, 0x00, 0x02, // FDT_END_NODE ("")
943        0x00, 0x00, 0x00, 0x09, // FDT_END
944    ];
945
946    #[test]
947    fn fdt_load_header() {
948        let blob: &[u8] = &FDT_BLOB_HEADER_ONLY;
949        let header = FdtHeader::from_blob(blob).unwrap();
950        assert_eq!(header.magic, FdtHeader::MAGIC);
951        assert_eq!(header.total_size, 0x48);
952        assert_eq!(header.off_dt_struct, 0x38);
953        assert_eq!(header.off_dt_strings, 0x48);
954        assert_eq!(header.off_mem_rsvmap, 0x28);
955        assert_eq!(header.version, 17);
956        assert_eq!(header.last_comp_version, 16);
957        assert_eq!(header.boot_cpuid_phys, 0);
958        assert_eq!(header.size_dt_strings, 0);
959        assert_eq!(header.size_dt_struct, 0x10);
960    }
961
962    #[test]
963    fn fdt_load_invalid_header() {
964        // HEADER is valid
965        const HEADER: [u8; 40] = [
966            0xd0, 0x0d, 0xfe, 0xed, // 0000: magic (0xd00dfeed)
967            0x00, 0x00, 0x00, 0xda, // 0004: totalsize (0xda)
968            0x00, 0x00, 0x00, 0x58, // 0008: off_dt_struct (0x58)
969            0x00, 0x00, 0x00, 0xb2, // 000C: off_dt_strings (0xb2)
970            0x00, 0x00, 0x00, 0x28, // 0010: off_mem_rsvmap (0x28)
971            0x00, 0x00, 0x00, 0x11, // 0014: version (0x11 = 17)
972            0x00, 0x00, 0x00, 0x10, // 0018: last_comp_version (0x10 = 16)
973            0x00, 0x00, 0x00, 0x00, // 001C: boot_cpuid_phys (0)
974            0x00, 0x00, 0x00, 0x28, // 0020: size_dt_strings (0x28)
975            0x00, 0x00, 0x00, 0x5a, // 0024: size_dt_struct (0x5a)
976        ];
977
978        FdtHeader::from_blob(&HEADER).unwrap();
979
980        // Header too small
981        assert!(FdtHeader::from_blob(&HEADER[..FdtHeader::SIZE - 4]).is_err());
982        assert!(FdtHeader::from_blob(&[]).is_err());
983
984        let mut invalid_header = HEADER;
985        invalid_header[0x00] = 0x00; // change magic to (0x000dfeed)
986        FdtHeader::from_blob(&invalid_header).expect_err("invalid magic");
987
988        let mut invalid_header = HEADER;
989        invalid_header[0x07] = 0x10; // make totalsize too small
990        FdtHeader::from_blob(&invalid_header).expect_err("invalid totalsize");
991
992        let mut invalid_header = HEADER;
993        invalid_header[0x0b] = 0x60; // increase off_dt_struct
994        FdtHeader::from_blob(&invalid_header).expect_err("dt struct overlaps with strings");
995
996        let mut invalid_header = HEADER;
997        invalid_header[0x27] = 0x5c; // increase size_dt_struct
998        FdtHeader::from_blob(&invalid_header).expect_err("dt struct overlaps with strings");
999
1000        let mut invalid_header = HEADER;
1001        invalid_header[0x13] = 0x20; // decrease off_mem_rsvmap
1002        FdtHeader::from_blob(&invalid_header).expect_err("reserved memory overlaps with header");
1003
1004        let mut invalid_header = HEADER;
1005        invalid_header[0x0f] = 0x50; // decrease off_dt_strings
1006        FdtHeader::from_blob(&invalid_header).expect_err("strings start before struct");
1007
1008        let mut invalid_header = HEADER;
1009        invalid_header[0x23] = 0x50; // increase size_dt_strings
1010        FdtHeader::from_blob(&invalid_header).expect_err("strings go past totalsize");
1011    }
1012
1013    #[test]
1014    fn fdt_load_resv_map() {
1015        let blob: &[u8] = &FDT_BLOB_RSVMAP;
1016        let fdt = Fdt::from_blob(blob).unwrap();
1017        assert_eq!(fdt.reserved_memory.len(), 2);
1018        assert!(
1019            fdt.reserved_memory[0].address == 0x12345678AABBCCDD
1020                && fdt.reserved_memory[0].size == 0x1234
1021        );
1022        assert!(
1023            fdt.reserved_memory[1].address == 0x1020304050607080
1024                && fdt.reserved_memory[1].size == 0x5678
1025        );
1026    }
1027
1028    #[test]
1029    fn fdt_test_node_props() {
1030        let mut node = FdtNode::empty("mynode").unwrap();
1031        node.set_prop("myprop", 1u32).unwrap();
1032        assert_eq!(node.get_prop::<u32>("myprop").unwrap(), 1u32);
1033        node.set_prop("myprop", 0xabcdef9876543210u64).unwrap();
1034        assert_eq!(
1035            node.get_prop::<u64>("myprop").unwrap(),
1036            0xabcdef9876543210u64
1037        );
1038        node.set_prop("myprop", ()).unwrap();
1039        assert_eq!(node.get_prop::<Vec<u8>>("myprop").unwrap(), []);
1040        node.set_prop("myprop", vec![1u8, 2u8, 3u8]).unwrap();
1041        assert_eq!(
1042            node.get_prop::<Vec<u8>>("myprop").unwrap(),
1043            vec![1u8, 2u8, 3u8]
1044        );
1045        node.set_prop("myprop", vec![1u32, 2u32, 3u32]).unwrap();
1046        assert_eq!(
1047            node.get_prop::<Vec<u32>>("myprop").unwrap(),
1048            vec![1u32, 2u32, 3u32]
1049        );
1050        node.set_prop("myprop", vec![1u64, 2u64, 3u64]).unwrap();
1051        assert_eq!(
1052            node.get_prop::<Vec<u64>>("myprop").unwrap(),
1053            vec![1u64, 2u64, 3u64]
1054        );
1055        node.set_prop("myprop", "myval".to_string()).unwrap();
1056        assert_eq!(
1057            node.get_prop::<String>("myprop").unwrap(),
1058            "myval".to_string()
1059        );
1060        node.set_prop(
1061            "myprop",
1062            vec![
1063                "myval1".to_string(),
1064                "myval2".to_string(),
1065                "myval3".to_string(),
1066            ],
1067        )
1068        .unwrap();
1069        assert_eq!(
1070            node.get_prop::<Vec<String>>("myprop").unwrap(),
1071            vec![
1072                "myval1".to_string(),
1073                "myval2".to_string(),
1074                "myval3".to_string()
1075            ]
1076        );
1077    }
1078
1079    #[test]
1080    fn fdt_simple_use() {
1081        let mut fdt = Fdt::new(&[]);
1082        let root_node = fdt.root_mut();
1083        root_node
1084            .set_prop("compatible", "linux,dummy-virt")
1085            .unwrap();
1086        root_node.set_prop("#address-cells", 0x2u32).unwrap();
1087        root_node.set_prop("#size-cells", 0x2u32).unwrap();
1088        let chosen_node = root_node.subnode_mut("chosen").unwrap();
1089        chosen_node.set_prop("linux,pci-probe-only", 1u32).unwrap();
1090        chosen_node
1091            .set_prop("bootargs", "panic=-1 console=hvc0 root=/dev/vda")
1092            .unwrap();
1093        fdt.finish().unwrap();
1094    }
1095
1096    #[test]
1097    fn fdt_load_strings() {
1098        let blob = &FDT_BLOB_STRINGS[..];
1099        let strings = FdtStrings::from_blob(blob).unwrap();
1100        let mut offset = 0u32;
1101
1102        for s in EXPECTED_STRINGS {
1103            assert_eq!(strings.at_offset(offset).unwrap(), s);
1104            offset += strings.at_offset(offset).unwrap().len() as u32 + 1;
1105        }
1106    }
1107
1108    #[test]
1109    fn fdt_load_strings_intern() {
1110        let strings_blob = &FDT_BLOB_STRINGS[..];
1111        let mut strings = FdtStrings::from_blob(strings_blob).unwrap();
1112        assert_eq!(strings.intern_string("null"), 0);
1113        assert_eq!(strings.intern_string("strlst"), 17);
1114        assert_eq!(strings.intern_string("arru64"), 31);
1115        assert_eq!(strings.intern_string("abc"), 38);
1116        assert_eq!(strings.intern_string("def"), 42);
1117        assert_eq!(strings.intern_string("strlst"), 17);
1118    }
1119
1120    #[test]
1121    fn fdt_load_props() {
1122        const PROP_SIZES: [(&str, usize); 7] = [
1123            ("null", 0),
1124            ("u32", 4),
1125            ("u64", 8),
1126            ("str", 6),
1127            ("strlst", 7),
1128            ("arru32", 8),
1129            ("arru64", 8),
1130        ];
1131
1132        let blob: &[u8] = &FDT_BLOB_STRINGS[..];
1133        let strings = FdtStrings::from_blob(blob).unwrap();
1134        let blob: &[u8] = &FDT_BLOB_NODES_ROOT_ONLY[..];
1135        let node = FdtNode::from_blob(blob, &strings).unwrap();
1136
1137        assert_eq!(node.name, "");
1138        assert_eq!(node.subnodes.len(), 0);
1139        assert_eq!(node.props.len(), PROP_SIZES.len());
1140
1141        for (pname, s) in PROP_SIZES.into_iter() {
1142            assert_eq!(node.get_prop::<Vec<u8>>(pname).unwrap().len(), s);
1143        }
1144    }
1145
1146    #[test]
1147    fn fdt_load_nodes_nested() {
1148        let strings_blob = &FDT_BLOB_STRINGS[..];
1149        let strings = FdtStrings::from_blob(strings_blob).unwrap();
1150        let blob: &[u8] = &FDT_BLOB_NESTED_NODES[..];
1151        let root_node = FdtNode::from_blob(blob, &strings).unwrap();
1152
1153        // Check root node
1154        assert_eq!(root_node.name, "");
1155        assert_eq!(root_node.subnodes.len(), 2);
1156        assert_eq!(root_node.props.len(), 1);
1157
1158        // Check first nested node
1159        let nested_node = root_node.subnodes.get("nested").unwrap();
1160        assert_eq!(nested_node.name, "nested");
1161        assert_eq!(nested_node.subnodes.len(), 0);
1162        assert_eq!(nested_node.props.len(), 2);
1163
1164        // Check second nested node
1165        let nested2_node = root_node.subnodes.get("nested2").unwrap();
1166        assert_eq!(nested2_node.name, "nested2");
1167        assert_eq!(nested2_node.subnodes.len(), 1);
1168        assert_eq!(nested2_node.props.len(), 1);
1169
1170        // Check third nested node
1171        let nested3_node = nested2_node.subnodes.get("nested3").unwrap();
1172        assert_eq!(nested3_node.name, "nested3");
1173        assert_eq!(nested3_node.subnodes.len(), 0);
1174        assert_eq!(nested3_node.props.len(), 0);
1175    }
1176
1177    #[test]
1178    fn fdt_get_node() {
1179        let fdt = Fdt::new(&[]);
1180        assert!(fdt.get_node("/").is_some());
1181        assert!(fdt.get_node("/a").is_none());
1182    }
1183
1184    #[test]
1185    fn fdt_find_nested_node() {
1186        let mut fdt = Fdt::new(&[]);
1187        let node1 = fdt.root.subnode_mut("N1").unwrap();
1188        node1.subnode_mut("N1-1").unwrap();
1189        node1.subnode_mut("N1-2").unwrap();
1190        let node2 = fdt.root.subnode_mut("N2").unwrap();
1191        let node2_1 = node2.subnode_mut("N2-1").unwrap();
1192        node2_1.subnode_mut("N2-1-1").unwrap();
1193
1194        assert!(fdt.get_node("/").is_some());
1195        assert!(fdt.get_node("/N1").is_some());
1196        assert!(fdt.get_node("/N2").is_some());
1197        assert!(fdt.get_node("/N1/N1-1").is_some());
1198        assert!(fdt.get_node("/N1/N1-2").is_some());
1199        assert!(fdt.get_node("/N2/N2-1").is_some());
1200        assert!(fdt.get_node("/N2/N2-1/N2-1-1").is_some());
1201        assert!(fdt.get_node("/N2/N2-1/A").is_none());
1202    }
1203
1204    #[test]
1205    fn minimal() {
1206        let mut fdt = Fdt::new(&[]);
1207        assert_eq!(
1208            fdt.finish().unwrap(),
1209            [
1210                0xd0, 0x0d, 0xfe, 0xed, // 0000: magic (0xd00dfeed)
1211                0x00, 0x00, 0x00, 0x48, // 0004: totalsize (0x48)
1212                0x00, 0x00, 0x00, 0x38, // 0008: off_dt_struct (0x38)
1213                0x00, 0x00, 0x00, 0x48, // 000C: off_dt_strings (0x48)
1214                0x00, 0x00, 0x00, 0x28, // 0010: off_mem_rsvmap (0x28)
1215                0x00, 0x00, 0x00, 0x11, // 0014: version (0x11 = 17)
1216                0x00, 0x00, 0x00, 0x10, // 0018: last_comp_version (0x10 = 16)
1217                0x00, 0x00, 0x00, 0x00, // 001C: boot_cpuid_phys (0)
1218                0x00, 0x00, 0x00, 0x00, // 0020: size_dt_strings (0)
1219                0x00, 0x00, 0x00, 0x10, // 0024: size_dt_struct (0x10)
1220                0x00, 0x00, 0x00, 0x00, // 0028: rsvmap terminator (address = 0 high)
1221                0x00, 0x00, 0x00, 0x00, // 002C: rsvmap terminator (address = 0 low)
1222                0x00, 0x00, 0x00, 0x00, // 0030: rsvmap terminator (size = 0 high)
1223                0x00, 0x00, 0x00, 0x00, // 0034: rsvmap terminator (size = 0 low)
1224                0x00, 0x00, 0x00, 0x01, // 0038: FDT_BEGIN_NODE
1225                0x00, 0x00, 0x00, 0x00, // 003C: node name ("") + padding
1226                0x00, 0x00, 0x00, 0x02, // 0040: FDT_END_NODE
1227                0x00, 0x00, 0x00, 0x09, // 0044: FDT_END
1228            ]
1229        );
1230    }
1231
1232    #[test]
1233    fn reservemap() {
1234        let mut fdt = Fdt::new(&[
1235            FdtReserveEntry {
1236                address: 0x12345678AABBCCDD,
1237                size: 0x1234,
1238            },
1239            FdtReserveEntry {
1240                address: 0x1020304050607080,
1241                size: 0x5678,
1242            },
1243        ]);
1244        assert_eq!(
1245            fdt.finish().unwrap(),
1246            [
1247                0xd0, 0x0d, 0xfe, 0xed, // 0000: magic (0xd00dfeed)
1248                0x00, 0x00, 0x00, 0x68, // 0004: totalsize (0x68)
1249                0x00, 0x00, 0x00, 0x58, // 0008: off_dt_struct (0x58)
1250                0x00, 0x00, 0x00, 0x68, // 000C: off_dt_strings (0x68)
1251                0x00, 0x00, 0x00, 0x28, // 0010: off_mem_rsvmap (0x28)
1252                0x00, 0x00, 0x00, 0x11, // 0014: version (0x11 = 17)
1253                0x00, 0x00, 0x00, 0x10, // 0018: last_comp_version (0x10 = 16)
1254                0x00, 0x00, 0x00, 0x00, // 001C: boot_cpuid_phys (0)
1255                0x00, 0x00, 0x00, 0x00, // 0020: size_dt_strings (0)
1256                0x00, 0x00, 0x00, 0x10, // 0024: size_dt_struct (0x10)
1257                0x12, 0x34, 0x56, 0x78, // 0028: rsvmap entry 0 address high
1258                0xAA, 0xBB, 0xCC, 0xDD, // 002C: rsvmap entry 0 address low
1259                0x00, 0x00, 0x00, 0x00, // 0030: rsvmap entry 0 size high
1260                0x00, 0x00, 0x12, 0x34, // 0034: rsvmap entry 0 size low
1261                0x10, 0x20, 0x30, 0x40, // 0038: rsvmap entry 1 address high
1262                0x50, 0x60, 0x70, 0x80, // 003C: rsvmap entry 1 address low
1263                0x00, 0x00, 0x00, 0x00, // 0040: rsvmap entry 1 size high
1264                0x00, 0x00, 0x56, 0x78, // 0044: rsvmap entry 1 size low
1265                0x00, 0x00, 0x00, 0x00, // 0048: rsvmap terminator (address = 0 high)
1266                0x00, 0x00, 0x00, 0x00, // 004C: rsvmap terminator (address = 0 low)
1267                0x00, 0x00, 0x00, 0x00, // 0050: rsvmap terminator (size = 0 high)
1268                0x00, 0x00, 0x00, 0x00, // 0054: rsvmap terminator (size = 0 low)
1269                0x00, 0x00, 0x00, 0x01, // 0058: FDT_BEGIN_NODE
1270                0x00, 0x00, 0x00, 0x00, // 005C: node name ("") + padding
1271                0x00, 0x00, 0x00, 0x02, // 0060: FDT_END_NODE
1272                0x00, 0x00, 0x00, 0x09, // 0064: FDT_END
1273            ]
1274        );
1275    }
1276
1277    #[test]
1278    fn prop_null() {
1279        let mut fdt = Fdt::new(&[]);
1280        let root_node = fdt.root_mut();
1281        root_node.set_prop("null", ()).unwrap();
1282        assert_eq!(
1283            fdt.finish().unwrap(),
1284            [
1285                0xd0, 0x0d, 0xfe, 0xed, // 0000: magic (0xd00dfeed)
1286                0x00, 0x00, 0x00, 0x59, // 0004: totalsize (0x59)
1287                0x00, 0x00, 0x00, 0x38, // 0008: off_dt_struct (0x38)
1288                0x00, 0x00, 0x00, 0x54, // 000C: off_dt_strings (0x54)
1289                0x00, 0x00, 0x00, 0x28, // 0010: off_mem_rsvmap (0x28)
1290                0x00, 0x00, 0x00, 0x11, // 0014: version (0x11 = 17)
1291                0x00, 0x00, 0x00, 0x10, // 0018: last_comp_version (0x10 = 16)
1292                0x00, 0x00, 0x00, 0x00, // 001C: boot_cpuid_phys (0)
1293                0x00, 0x00, 0x00, 0x05, // 0020: size_dt_strings (0x05)
1294                0x00, 0x00, 0x00, 0x1c, // 0024: size_dt_struct (0x1C)
1295                0x00, 0x00, 0x00, 0x00, // 0028: rsvmap terminator (address = 0 high)
1296                0x00, 0x00, 0x00, 0x00, // 002C: rsvmap terminator (address = 0 low)
1297                0x00, 0x00, 0x00, 0x00, // 0030: rsvmap terminator (size = 0 high)
1298                0x00, 0x00, 0x00, 0x00, // 0034: rsvmap terminator (size = 0 low)
1299                0x00, 0x00, 0x00, 0x01, // 0038: FDT_BEGIN_NODE
1300                0x00, 0x00, 0x00, 0x00, // 003C: node name ("") + padding
1301                0x00, 0x00, 0x00, 0x03, // 0040: FDT_PROP
1302                0x00, 0x00, 0x00, 0x00, // 0044: prop len (0)
1303                0x00, 0x00, 0x00, 0x00, // 0048: prop nameoff (0)
1304                0x00, 0x00, 0x00, 0x02, // 004C: FDT_END_NODE
1305                0x00, 0x00, 0x00, 0x09, // 0050: FDT_END
1306                b'n', b'u', b'l', b'l', 0x00, // 0054: strings block
1307            ]
1308        );
1309    }
1310
1311    #[test]
1312    fn prop_u32() {
1313        let mut fdt = Fdt::new(&[]);
1314        let root_node = fdt.root_mut();
1315        root_node.set_prop("u32", 0x12345678u32).unwrap();
1316        assert_eq!(
1317            fdt.finish().unwrap(),
1318            [
1319                0xd0, 0x0d, 0xfe, 0xed, // 0000: magic (0xd00dfeed)
1320                0x00, 0x00, 0x00, 0x5c, // 0004: totalsize (0x5C)
1321                0x00, 0x00, 0x00, 0x38, // 0008: off_dt_struct (0x38)
1322                0x00, 0x00, 0x00, 0x58, // 000C: off_dt_strings (0x58)
1323                0x00, 0x00, 0x00, 0x28, // 0010: off_mem_rsvmap (0x28)
1324                0x00, 0x00, 0x00, 0x11, // 0014: version (0x11 = 17)
1325                0x00, 0x00, 0x00, 0x10, // 0018: last_comp_version (0x10 = 16)
1326                0x00, 0x00, 0x00, 0x00, // 001C: boot_cpuid_phys (0)
1327                0x00, 0x00, 0x00, 0x04, // 0020: size_dt_strings (0x04)
1328                0x00, 0x00, 0x00, 0x20, // 0024: size_dt_struct (0x20)
1329                0x00, 0x00, 0x00, 0x00, // 0028: rsvmap terminator (address = 0 high)
1330                0x00, 0x00, 0x00, 0x00, // 002C: rsvmap terminator (address = 0 low)
1331                0x00, 0x00, 0x00, 0x00, // 0030: rsvmap terminator (size = 0 high)
1332                0x00, 0x00, 0x00, 0x00, // 0034: rsvmap terminator (size = 0 low)
1333                0x00, 0x00, 0x00, 0x01, // 0038: FDT_BEGIN_NODE
1334                0x00, 0x00, 0x00, 0x00, // 003C: node name ("") + padding
1335                0x00, 0x00, 0x00, 0x03, // 0040: FDT_PROP
1336                0x00, 0x00, 0x00, 0x04, // 0044: prop len (4)
1337                0x00, 0x00, 0x00, 0x00, // 0048: prop nameoff (0)
1338                0x12, 0x34, 0x56, 0x78, // 004C: prop u32 value (0x12345678)
1339                0x00, 0x00, 0x00, 0x02, // 0050: FDT_END_NODE
1340                0x00, 0x00, 0x00, 0x09, // 0054: FDT_END
1341                b'u', b'3', b'2', 0x00, // 0058: strings block
1342            ]
1343        );
1344    }
1345
1346    #[test]
1347    fn all_props() {
1348        let mut fdt = Fdt::new(&[]);
1349        let root_node = fdt.root_mut();
1350        root_node
1351            .set_prop("arru32", &[0x12345678u32, 0xAABBCCDDu32])
1352            .unwrap();
1353        root_node
1354            .set_prop("arru64", &[0x1234567887654321u64])
1355            .unwrap();
1356        root_node.set_prop("null", ()).unwrap();
1357        root_node.set_prop("str", "hello").unwrap();
1358        root_node.set_prop("strlst", &["hi", "bye"]).unwrap();
1359        root_node.set_prop("u32", 0x12345678u32).unwrap();
1360        root_node.set_prop("u64", 0x1234567887654321u64).unwrap();
1361        assert_eq!(
1362            fdt.finish().unwrap(),
1363            [
1364                0xd0, 0x0d, 0xfe, 0xed, // 0000: magic (0xd00dfeed)
1365                0x00, 0x00, 0x00, 0xee, // 0004: totalsize (0xEE)
1366                0x00, 0x00, 0x00, 0x38, // 0008: off_dt_struct (0x38)
1367                0x00, 0x00, 0x00, 0xc8, // 000C: off_dt_strings (0xC8)
1368                0x00, 0x00, 0x00, 0x28, // 0010: off_mem_rsvmap (0x28)
1369                0x00, 0x00, 0x00, 0x11, // 0014: version (0x11 = 17)
1370                0x00, 0x00, 0x00, 0x10, // 0018: last_comp_version (0x10 = 16)
1371                0x00, 0x00, 0x00, 0x00, // 001C: boot_cpuid_phys (0)
1372                0x00, 0x00, 0x00, 0x26, // 0020: size_dt_strings (0x26)
1373                0x00, 0x00, 0x00, 0x90, // 0024: size_dt_struct (0x90)
1374                0x00, 0x00, 0x00, 0x00, // 0028: rsvmap terminator (address = 0 high)
1375                0x00, 0x00, 0x00, 0x00, // 002C: rsvmap terminator (address = 0 low)
1376                0x00, 0x00, 0x00, 0x00, // 0030: rsvmap terminator (size = 0 high)
1377                0x00, 0x00, 0x00, 0x00, // 0034: rsvmap terminator (size = 0 low)
1378                0x00, 0x00, 0x00, 0x01, // 0038: FDT_BEGIN_NODE
1379                0x00, 0x00, 0x00, 0x00, // 003C: node name ("") + padding
1380                0x00, 0x00, 0x00, 0x03, // 0040: FDT_PROP (u32 array)
1381                0x00, 0x00, 0x00, 0x08, // 0044: prop len (8)
1382                0x00, 0x00, 0x00, 0x00, // 0048: prop nameoff (0x00)
1383                0x12, 0x34, 0x56, 0x78, // 004C: prop value 0
1384                0xAA, 0xBB, 0xCC, 0xDD, // 0050: prop value 1
1385                0x00, 0x00, 0x00, 0x03, // 0054: FDT_PROP (u64 array)
1386                0x00, 0x00, 0x00, 0x08, // 0058: prop len (8)
1387                0x00, 0x00, 0x00, 0x07, // 005C: prop nameoff (0x07)
1388                0x12, 0x34, 0x56, 0x78, // 0060: prop u64 value 0 high
1389                0x87, 0x65, 0x43, 0x21, // 0064: prop u64 value 0 low
1390                0x00, 0x00, 0x00, 0x03, // 0068: FDT_PROP (null)
1391                0x00, 0x00, 0x00, 0x00, // 006C: prop len (0)
1392                0x00, 0x00, 0x00, 0x0E, // 0070: prop nameoff (0x0e)
1393                0x00, 0x00, 0x00, 0x03, // 0074: FDT_PROP (string)
1394                0x00, 0x00, 0x00, 0x06, // 0078: prop len (6)
1395                0x00, 0x00, 0x00, 0x13, // 007C: prop nameoff (0x13)
1396                b'h', b'e', b'l', b'l', // 0080: prop str value ("hello") + padding
1397                b'o', 0x00, 0x00, 0x00, // 0084: "o\0" + padding
1398                0x00, 0x00, 0x00, 0x03, // 0088: FDT_PROP (string list)
1399                0x00, 0x00, 0x00, 0x07, // 008C: prop len (7)
1400                0x00, 0x00, 0x00, 0x17, // 0090: prop nameoff (0x17)
1401                b'h', b'i', 0x00, b'b', // 0094: prop value ("hi", "bye")
1402                b'y', b'e', 0x00, 0x00, // 0098: "ye\0" + padding
1403                0x00, 0x00, 0x00, 0x03, // 009C: FDT_PROP (u32)
1404                0x00, 0x00, 0x00, 0x04, // 00A0: prop len (4)
1405                0x00, 0x00, 0x00, 0x1E, // 00A4: prop nameoff (0x1E)
1406                0x12, 0x34, 0x56, 0x78, // 00A8: prop u32 value (0x12345678)
1407                0x00, 0x00, 0x00, 0x03, // 00AC: FDT_PROP (u64)
1408                0x00, 0x00, 0x00, 0x08, // 00B0: prop len (8)
1409                0x00, 0x00, 0x00, 0x22, // 00B4: prop nameoff (0x22)
1410                0x12, 0x34, 0x56, 0x78, // 00B8: prop u64 value high (0x12345678)
1411                0x87, 0x65, 0x43, 0x21, // 00BC: prop u64 value low (0x87654321)
1412                0x00, 0x00, 0x00, 0x02, // 00C0: FDT_END_NODE
1413                0x00, 0x00, 0x00, 0x09, // 00C4: FDT_END
1414                b'a', b'r', b'r', b'u', b'3', b'2', 0x00, // 00C8: strings + 0x00: "arru32"
1415                b'a', b'r', b'r', b'u', b'6', b'4', 0x00, // 00CF: strings + 0x07: "arru64"
1416                b'n', b'u', b'l', b'l', 0x00, // 00D6: strings + 0x0E: "null"
1417                b's', b't', b'r', 0x00, // 00DB: strings + 0x13: "str"
1418                b's', b't', b'r', b'l', b's', b't', 0x00, // 00DF: strings + 0x17: "strlst"
1419                b'u', b'3', b'2', 0x00, // 00E6: strings + 0x1E: "u32"
1420                b'u', b'6', b'4', 0x00, // 00EA: strings + 0x22: "u64"
1421            ]
1422        );
1423    }
1424
1425    #[test]
1426    fn node_order() {
1427        let expected: &[u8] = &[
1428            0xd0, 0x0d, 0xfe, 0xed, // 0000: magic (0xd00dfeed)
1429            0x00, 0x00, 0x00, 0x9C, // 0004: totalsize (0x9C)
1430            0x00, 0x00, 0x00, 0x38, // 0008: off_dt_struct (0x38)
1431            0x00, 0x00, 0x00, 0x9C, // 000C: off_dt_strings (0x9C)
1432            0x00, 0x00, 0x00, 0x28, // 0010: off_mem_rsvmap (0x28)
1433            0x00, 0x00, 0x00, 0x11, // 0014: version (0x11 = 17)
1434            0x00, 0x00, 0x00, 0x10, // 0018: last_comp_version (0x10 = 16)
1435            0x00, 0x00, 0x00, 0x00, // 001C: boot_cpuid_phys (0)
1436            0x00, 0x00, 0x00, 0x00, // 0020: size_dt_strings (0x00)
1437            0x00, 0x00, 0x00, 0x64, // 0024: size_dt_struct (0x64)
1438            0x00, 0x00, 0x00, 0x00, // 0028: rsvmap terminator (address = 0 high)
1439            0x00, 0x00, 0x00, 0x00, // 002C: rsvmap terminator (address = 0 low)
1440            0x00, 0x00, 0x00, 0x00, // 0030: rsvmap terminator (size = 0 high)
1441            0x00, 0x00, 0x00, 0x00, // 0034: rsvmap terminator (size = 0 low)
1442            0x00, 0x00, 0x00, 0x01, // 0038: FDT_BEGIN_NODE
1443            0x00, 0x00, 0x00, 0x00, // 003C: node name ("") + padding
1444            0x00, 0x00, 0x00, 0x01, // 0040: FDT_BEGIN_NODE
1445            b'B', 0x00, 0x00, 0x00, // 0044: node name ("B") + padding
1446            0x00, 0x00, 0x00, 0x02, // 0048: FDT_END_NODE
1447            0x00, 0x00, 0x00, 0x01, // 004C: FDT_BEGIN_NODE
1448            b'A', 0x00, 0x00, 0x00, // 0050: node name ("A") + padding
1449            0x00, 0x00, 0x00, 0x02, // 0054: FDT_END_NODE
1450            0x00, 0x00, 0x00, 0x01, // 0058: FDT_BEGIN_NODE
1451            b'C', 0x00, 0x00, 0x00, // 005C: node name ("C") + padding
1452            0x00, 0x00, 0x00, 0x01, // 0060: FDT_BEGIN_NODE
1453            b'D', 0x00, 0x00, 0x00, // 0064: node name ("D") + padding
1454            0x00, 0x00, 0x00, 0x02, // 0068: FDT_END_NODE
1455            0x00, 0x00, 0x00, 0x01, // 006C: FDT_BEGIN_NODE
1456            b'E', 0x00, 0x00, 0x00, // 0070: node name ("E") + padding
1457            0x00, 0x00, 0x00, 0x02, // 0074: FDT_END_NODE
1458            0x00, 0x00, 0x00, 0x01, // 0078: FDT_BEGIN_NODE
1459            b'B', 0x00, 0x00, 0x00, // 007C: node name ("B") + padding
1460            0x00, 0x00, 0x00, 0x02, // 0080: FDT_END_NODE
1461            0x00, 0x00, 0x00, 0x01, // 0084: FDT_BEGIN_NODE
1462            b'F', 0x00, 0x00, 0x00, // 0088: node name ("F") + padding
1463            0x00, 0x00, 0x00, 0x02, // 008C: FDT_END_NODE
1464            0x00, 0x00, 0x00, 0x02, // 0090: FDT_END_NODE
1465            0x00, 0x00, 0x00, 0x02, // 0094: FDT_END_NODE
1466            0x00, 0x00, 0x00, 0x09, // 0098: FDT_END
1467        ];
1468
1469        let mut fdt = Fdt::new(&[]);
1470        let root = fdt.root_mut();
1471        let root_subnode_names = ["B", "A", "C"];
1472        let node_c_subnode_names = ["D", "E", "B", "F"];
1473        for n in root_subnode_names {
1474            root.subnode_mut(n).unwrap();
1475        }
1476        let node_c = root.subnode_mut("C").unwrap();
1477        for n in node_c_subnode_names {
1478            node_c.subnode_mut(n).unwrap();
1479        }
1480
1481        assert!(root
1482            .iter_subnodes()
1483            .zip(root_subnode_names)
1484            .all(|(sn, n)| sn.name == n));
1485        assert!(root
1486            .subnode("C")
1487            .unwrap()
1488            .iter_subnodes()
1489            .zip(node_c_subnode_names)
1490            .all(|(sn, n)| sn.name == n));
1491        assert_eq!(fdt.finish().unwrap(), expected);
1492    }
1493
1494    #[test]
1495    fn prop_order() {
1496        let expected: &[u8] = &[
1497            0xd0, 0x0d, 0xfe, 0xed, // 0000: magic (0xd00dfeed)
1498            0x00, 0x00, 0x00, 0x98, // 0004: totalsize (0x98)
1499            0x00, 0x00, 0x00, 0x38, // 0008: off_dt_struct (0x38)
1500            0x00, 0x00, 0x00, 0x88, // 000C: off_dt_strings (0x88)
1501            0x00, 0x00, 0x00, 0x28, // 0010: off_mem_rsvmap (0x28)
1502            0x00, 0x00, 0x00, 0x11, // 0014: version (0x11 = 17)
1503            0x00, 0x00, 0x00, 0x10, // 0018: last_comp_version (0x10 = 16)
1504            0x00, 0x00, 0x00, 0x00, // 001C: boot_cpuid_phys (0)
1505            0x00, 0x00, 0x00, 0x10, // 0020: size_dt_strings (0x10)
1506            0x00, 0x00, 0x00, 0x50, // 0024: size_dt_struct (0x50)
1507            0x00, 0x00, 0x00, 0x00, // 0028: rsvmap terminator (address = 0 high)
1508            0x00, 0x00, 0x00, 0x00, // 002C: rsvmap terminator (address = 0 low)
1509            0x00, 0x00, 0x00, 0x00, // 0030: rsvmap terminator (size = 0 high)
1510            0x00, 0x00, 0x00, 0x00, // 0034: rsvmap terminator (size = 0 low)
1511            0x00, 0x00, 0x00, 0x01, // 0038: FDT_BEGIN_NODE
1512            0x00, 0x00, 0x00, 0x00, // 003C: node name ("") + padding
1513            0x00, 0x00, 0x00, 0x03, // 0040: FDT_PROP (u32)
1514            0x00, 0x00, 0x00, 0x04, // 0044: prop len (4)
1515            0x00, 0x00, 0x00, 0x00, // 0048: prop nameoff (0x00)
1516            0x76, 0x61, 0x6c, 0x00, // 004C: prop string value ("val")
1517            0x00, 0x00, 0x00, 0x03, // 0050: FDT_PROP (u32)
1518            0x00, 0x00, 0x00, 0x04, // 0054: prop len (4)
1519            0x00, 0x00, 0x00, 0x04, // 0058: prop nameoff (0x04)
1520            0x00, 0x00, 0x00, 0x02, // 005C: prop u32 high (0x2)
1521            0x00, 0x00, 0x00, 0x03, // 0060: FDT_PROP (u32)
1522            0x00, 0x00, 0x00, 0x04, // 0064: prop len (4)
1523            0x00, 0x00, 0x00, 0x08, // 0068: prop nameoff (0x08)
1524            0x00, 0x00, 0x00, 0x01, // 006C: prop u32 value (0x1)
1525            0x00, 0x00, 0x00, 0x03, // 0070: FDT_PROP (u32)
1526            0x00, 0x00, 0x00, 0x04, // 0074: prop len (4)
1527            0x00, 0x00, 0x00, 0x0C, // 0078: prop nameoff (0x0B)
1528            0x00, 0x00, 0x00, 0x03, // 007C: prop u32 value (0x3)
1529            0x00, 0x00, 0x00, 0x02, // 0080: FDT_END_NODE
1530            0x00, 0x00, 0x00, 0x09, // 0084: FDT_END
1531            b'g', b'h', b'i', 0x00, // 0088: strings + 0x00: "ghi"
1532            b'd', b'e', b'f', 0x00, // 008C: strings + 0x04: "def"
1533            b'a', b'b', b'c', 0x00, // 0090: strings + 0x08: "abc"
1534            b'b', b'c', b'd', 0x00, // 0094: strings + 0x0C: "bcd"
1535        ];
1536
1537        let mut fdt = Fdt::new(&[]);
1538        let root_node = fdt.root_mut();
1539        root_node.set_prop("ghi", "val").unwrap();
1540        root_node.set_prop("def", 2u32).unwrap();
1541        root_node.set_prop("abc", 1u32).unwrap();
1542        root_node.set_prop("bcd", 3u32).unwrap();
1543
1544        assert_eq!(
1545            root_node.prop_names().collect::<Vec<_>>(),
1546            ["ghi", "def", "abc", "bcd"]
1547        );
1548        assert_eq!(fdt.finish().unwrap(), expected);
1549    }
1550
1551    #[test]
1552    fn nested_nodes() {
1553        let mut fdt = Fdt::new(&[]);
1554        let root_node = fdt.root_mut();
1555        root_node.set_prop("abc", 0x13579024u32).unwrap();
1556        let nested_node = root_node.subnode_mut("nested").unwrap();
1557        nested_node.set_prop("def", 0x12121212u32).unwrap();
1558        assert_eq!(
1559            fdt.finish().unwrap(),
1560            [
1561                0xd0, 0x0d, 0xfe, 0xed, // 0000: magic (0xd00dfeed)
1562                0x00, 0x00, 0x00, 0x80, // 0004: totalsize (0x80)
1563                0x00, 0x00, 0x00, 0x38, // 0008: off_dt_struct (0x38)
1564                0x00, 0x00, 0x00, 0x78, // 000C: off_dt_strings (0x78)
1565                0x00, 0x00, 0x00, 0x28, // 0010: off_mem_rsvmap (0x28)
1566                0x00, 0x00, 0x00, 0x11, // 0014: version (0x11 = 17)
1567                0x00, 0x00, 0x00, 0x10, // 0018: last_comp_version (0x10 = 16)
1568                0x00, 0x00, 0x00, 0x00, // 001C: boot_cpuid_phys (0)
1569                0x00, 0x00, 0x00, 0x08, // 0020: size_dt_strings (0x08)
1570                0x00, 0x00, 0x00, 0x40, // 0024: size_dt_struct (0x40)
1571                0x00, 0x00, 0x00, 0x00, // 0028: rsvmap terminator (address = 0 high)
1572                0x00, 0x00, 0x00, 0x00, // 002C: rsvmap terminator (address = 0 low)
1573                0x00, 0x00, 0x00, 0x00, // 0030: rsvmap terminator (size = 0 high)
1574                0x00, 0x00, 0x00, 0x00, // 0034: rsvmap terminator (size = 0 low)
1575                0x00, 0x00, 0x00, 0x01, // 0038: FDT_BEGIN_NODE
1576                0x00, 0x00, 0x00, 0x00, // 003C: node name ("") + padding
1577                0x00, 0x00, 0x00, 0x03, // 0040: FDT_PROP
1578                0x00, 0x00, 0x00, 0x04, // 0044: prop len (4)
1579                0x00, 0x00, 0x00, 0x00, // 0048: prop nameoff (0x00)
1580                0x13, 0x57, 0x90, 0x24, // 004C: prop u32 value (0x13579024)
1581                0x00, 0x00, 0x00, 0x01, // 0050: FDT_BEGIN_NODE
1582                b'n', b'e', b's', b't', // 0054: Node name ("nested")
1583                b'e', b'd', 0x00, 0x00, // 0058: "ed\0" + pad
1584                0x00, 0x00, 0x00, 0x03, // 005C: FDT_PROP
1585                0x00, 0x00, 0x00, 0x04, // 0060: prop len (4)
1586                0x00, 0x00, 0x00, 0x04, // 0064: prop nameoff (0x04)
1587                0x12, 0x12, 0x12, 0x12, // 0068: prop u32 value (0x12121212)
1588                0x00, 0x00, 0x00, 0x02, // 006C: FDT_END_NODE ("nested")
1589                0x00, 0x00, 0x00, 0x02, // 0070: FDT_END_NODE ("")
1590                0x00, 0x00, 0x00, 0x09, // 0074: FDT_END
1591                b'a', b'b', b'c', 0x00, // 0078: strings + 0x00: "abc"
1592                b'd', b'e', b'f', 0x00, // 007C: strings + 0x04: "def"
1593            ]
1594        );
1595    }
1596
1597    #[test]
1598    fn prop_name_string_reuse() {
1599        let mut fdt = Fdt::new(&[]);
1600        let root_node = fdt.root_mut();
1601        root_node.set_prop("abc", 0x13579024u32).unwrap();
1602        let nested = root_node.subnode_mut("nested").unwrap();
1603        nested.set_prop("abc", 0x12121212u32).unwrap(); // This should reuse the "abc" string.
1604        nested.set_prop("def", 0x12121212u32).unwrap();
1605        assert_eq!(
1606            fdt.finish().unwrap(),
1607            [
1608                0xd0, 0x0d, 0xfe, 0xed, // 0000: magic (0xd00dfeed)
1609                0x00, 0x00, 0x00, 0x90, // 0004: totalsize (0x90)
1610                0x00, 0x00, 0x00, 0x38, // 0008: off_dt_struct (0x38)
1611                0x00, 0x00, 0x00, 0x88, // 000C: off_dt_strings (0x88)
1612                0x00, 0x00, 0x00, 0x28, // 0010: off_mem_rsvmap (0x28)
1613                0x00, 0x00, 0x00, 0x11, // 0014: version (0x11 = 17)
1614                0x00, 0x00, 0x00, 0x10, // 0018: last_comp_version (0x10 = 16)
1615                0x00, 0x00, 0x00, 0x00, // 001C: boot_cpuid_phys (0)
1616                0x00, 0x00, 0x00, 0x08, // 0020: size_dt_strings (0x08)
1617                0x00, 0x00, 0x00, 0x50, // 0024: size_dt_struct (0x50)
1618                0x00, 0x00, 0x00, 0x00, // 0028: rsvmap terminator (address = 0 high)
1619                0x00, 0x00, 0x00, 0x00, // 002C: rsvmap terminator (address = 0 low)
1620                0x00, 0x00, 0x00, 0x00, // 0030: rsvmap terminator (size = 0 high)
1621                0x00, 0x00, 0x00, 0x00, // 0034: rsvmap terminator (size = 0 low)
1622                0x00, 0x00, 0x00, 0x01, // 0038: FDT_BEGIN_NODE
1623                0x00, 0x00, 0x00, 0x00, // 003C: node name ("") + padding
1624                0x00, 0x00, 0x00, 0x03, // 0040: FDT_PROP
1625                0x00, 0x00, 0x00, 0x04, // 0044: prop len (4)
1626                0x00, 0x00, 0x00, 0x00, // 0048: prop nameoff (0x00)
1627                0x13, 0x57, 0x90, 0x24, // 004C: prop u32 value (0x13579024)
1628                0x00, 0x00, 0x00, 0x01, // 0050: FDT_BEGIN_NODE
1629                b'n', b'e', b's', b't', // 0054: Node name ("nested")
1630                b'e', b'd', 0x00, 0x00, // 0058: "ed\0" + pad
1631                0x00, 0x00, 0x00, 0x03, // 005C: FDT_PROP
1632                0x00, 0x00, 0x00, 0x04, // 0060: prop len (4)
1633                0x00, 0x00, 0x00, 0x00, // 0064: prop nameoff (0x00 - reuse)
1634                0x12, 0x12, 0x12, 0x12, // 0068: prop u32 value (0x12121212)
1635                0x00, 0x00, 0x00, 0x03, // 006C: FDT_PROP
1636                0x00, 0x00, 0x00, 0x04, // 0070: prop len (4)
1637                0x00, 0x00, 0x00, 0x04, // 0074: prop nameoff (0x04)
1638                0x12, 0x12, 0x12, 0x12, // 0078: prop u32 value (0x12121212)
1639                0x00, 0x00, 0x00, 0x02, // 007C: FDT_END_NODE ("nested")
1640                0x00, 0x00, 0x00, 0x02, // 0080: FDT_END_NODE ("")
1641                0x00, 0x00, 0x00, 0x09, // 0084: FDT_END
1642                b'a', b'b', b'c', 0x00, // 0088: strings + 0x00: "abc"
1643                b'd', b'e', b'f', 0x00, // 008C: strings + 0x04: "def"
1644            ]
1645        );
1646    }
1647
1648    #[test]
1649    fn invalid_node_name_nul() {
1650        let mut fdt = Fdt::new(&[]);
1651        let root_node = fdt.root_mut();
1652        root_node
1653            .subnode_mut("abc\0def")
1654            .expect_err("node name with embedded NUL");
1655    }
1656
1657    #[test]
1658    fn invalid_prop_name_nul() {
1659        let mut fdt = Fdt::new(&[]);
1660        let root_node = fdt.root_mut();
1661        root_node
1662            .set_prop("abc\0def", 0u32)
1663            .expect_err("property name with embedded NUL");
1664    }
1665
1666    #[test]
1667    fn invalid_prop_string_value_nul() {
1668        let mut fdt = Fdt::new(&[]);
1669        let root_node = fdt.root_mut();
1670        root_node
1671            .set_prop("mystr", "abc\0def")
1672            .expect_err("string property value with embedded NUL");
1673    }
1674
1675    #[test]
1676    fn invalid_prop_string_list_value_nul() {
1677        let mut fdt = Fdt::new(&[]);
1678        let root_node = fdt.root_mut();
1679        let strs = ["test", "abc\0def"];
1680        root_node
1681            .set_prop("mystr", &strs)
1682            .expect_err("stringlist property value with embedded NUL");
1683    }
1684}