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