vm_fdt/
writer.rs

1// Copyright 2021 The Chromium OS Authors. All rights reserved.
2// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
3
4//! This module writes Flattened Devicetree blobs as defined here:
5//! <https://devicetree-specification.readthedocs.io/en/stable/flattened-format.html>
6
7use alloc::collections::BTreeMap;
8use alloc::ffi::CString;
9use alloc::string::String;
10use alloc::vec::Vec;
11use core::cmp::{Ord, Ordering};
12use core::convert::TryInto;
13use core::fmt;
14use core::mem::size_of_val;
15#[cfg(feature = "std")]
16use std::collections::HashSet;
17
18#[cfg(all(feature = "alloc", not(feature = "std")))]
19use hashbrown::HashSet;
20
21use crate::{
22    FDT_BEGIN_NODE, FDT_END, FDT_END_NODE, FDT_MAGIC, FDT_PROP, NODE_NAME_MAX_LEN,
23    PROPERTY_NAME_MAX_LEN,
24};
25
26#[derive(Debug, Eq, PartialEq)]
27/// Errors associated with creating the Flattened Device Tree.
28pub enum Error {
29    /// Properties may not be added before beginning a node.
30    PropertyBeforeBeginNode,
31    /// Properties may not be added after a node has been ended.
32    PropertyAfterEndNode,
33    /// Property value size must fit in 32 bits.
34    PropertyValueTooLarge,
35    /// Total size must fit in 32 bits.
36    TotalSizeTooLarge,
37    /// Strings cannot contain NUL.
38    InvalidString,
39    /// Attempted to end a node that was not the most recent.
40    OutOfOrderEndNode,
41    /// Attempted to call finish without ending all nodes.
42    UnclosedNode,
43    /// Memory reservation is invalid.
44    InvalidMemoryReservation,
45    /// Memory reservations are overlapping.
46    OverlappingMemoryReservations,
47    /// Invalid node name.
48    InvalidNodeName,
49    /// Invalid property name.
50    InvalidPropertyName,
51    /// Node depth exceeds FDT_MAX_NODE_DEPTH
52    NodeDepthTooLarge,
53    /// Duplicate phandle property
54    DuplicatePhandle,
55}
56
57impl fmt::Display for Error {
58    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
59        match self {
60            Error::PropertyBeforeBeginNode => {
61                write!(f, "Properties may not be added before beginning a node")
62            }
63            Error::PropertyAfterEndNode => {
64                write!(f, "Properties may not be added after a node has been ended")
65            }
66            Error::PropertyValueTooLarge => write!(f, "Property value size must fit in 32 bits"),
67            Error::TotalSizeTooLarge => write!(f, "Total size must fit in 32 bits"),
68            Error::InvalidString => write!(f, "Strings cannot contain NUL"),
69            Error::OutOfOrderEndNode => {
70                write!(f, "Attempted to end a node that was not the most recent")
71            }
72            Error::UnclosedNode => write!(f, "Attempted to call finish without ending all nodes"),
73            Error::InvalidMemoryReservation => write!(f, "Memory reservation is invalid"),
74            Error::OverlappingMemoryReservations => {
75                write!(f, "Memory reservations are overlapping")
76            }
77            Error::InvalidNodeName => write!(f, "Invalid node name"),
78            Error::InvalidPropertyName => write!(f, "Invalid property name"),
79            Error::NodeDepthTooLarge => write!(f, "Node depth exceeds FDT_MAX_NODE_DEPTH"),
80            Error::DuplicatePhandle => write!(f, "Duplicate phandle value"),
81        }
82    }
83}
84
85#[cfg(feature = "std")]
86impl std::error::Error for Error {}
87
88/// Result of a FDT writer operation.
89pub type Result<T> = core::result::Result<T, Error>;
90
91const FDT_HEADER_SIZE: usize = 40;
92const FDT_VERSION: u32 = 17;
93const FDT_LAST_COMP_VERSION: u32 = 16;
94/// The same max depth as in the Linux kernel.
95const FDT_MAX_NODE_DEPTH: usize = 64;
96
97/// Interface for writing a Flattened Devicetree (FDT) and emitting a Devicetree Blob (DTB).
98#[derive(Debug)]
99pub struct FdtWriter {
100    data: Vec<u8>,
101    off_mem_rsvmap: u32,
102    off_dt_struct: u32,
103    strings: Vec<u8>,
104    string_offsets: BTreeMap<CString, u32>,
105    node_depth: usize,
106    node_ended: bool,
107    boot_cpuid_phys: u32,
108    // The set is used to track the uniqueness of phandle values as required by the spec
109    // https://devicetree-specification.readthedocs.io/en/stable/devicetree-basics.html#phandle
110    phandles: HashSet<u32>,
111}
112
113/// Reserved physical memory region.
114///
115/// This represents an area of physical memory reserved by the firmware and unusable by the OS.
116/// For example, this could be used to preserve bootloader code or data used at runtime.
117#[derive(Clone, Debug, Eq, PartialEq)]
118pub struct FdtReserveEntry {
119    address: u64,
120    size: u64,
121}
122
123impl FdtReserveEntry {
124    /// Create a memory reservation for the FDT.
125    ///
126    /// # Arguments
127    ///
128    /// * address: Physical address of the beginning of the reserved region.
129    /// * size: Size of the reserved region in bytes.
130    pub fn new(address: u64, size: u64) -> Result<Self> {
131        if address.checked_add(size).is_none() || size == 0 {
132            return Err(Error::InvalidMemoryReservation);
133        }
134
135        Ok(FdtReserveEntry { address, size })
136    }
137}
138
139impl Ord for FdtReserveEntry {
140    fn cmp(&self, other: &Self) -> Ordering {
141        self.address.cmp(&other.address)
142    }
143}
144
145impl PartialOrd for FdtReserveEntry {
146    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
147        Some(self.cmp(other))
148    }
149}
150
151// Returns true if there are any overlapping memory reservations.
152fn check_overlapping(mem_reservations: &[FdtReserveEntry]) -> Result<()> {
153    let mut mem_rsvmap_copy = mem_reservations.to_vec();
154    mem_rsvmap_copy.sort();
155    let overlapping = mem_rsvmap_copy.windows(2).any(|w| {
156        // The following add cannot overflow because we can only have
157        // valid FdtReserveEntry (as per the constructor of the type).
158        w[0].address + w[0].size > w[1].address
159    });
160
161    if overlapping {
162        return Err(Error::OverlappingMemoryReservations);
163    }
164
165    Ok(())
166}
167
168// Check if `name` is a valid node name in the form "node-name@unit-address".
169// https://devicetree-specification.readthedocs.io/en/stable/devicetree-basics.html#node-name-requirements
170fn node_name_valid(name: &str) -> bool {
171    // Special case: allow empty node names.
172    // This is technically not allowed by the spec, but it seems to be accepted in practice.
173    if name.is_empty() {
174        return true;
175    }
176
177    let mut parts = name.split('@');
178
179    let node_name = parts.next().unwrap(); // split() always returns at least one part
180    let unit_address = parts.next();
181
182    if unit_address.is_some() && parts.next().is_some() {
183        // Node names should only contain one '@'.
184        return false;
185    }
186
187    if node_name.is_empty() || node_name.len() > NODE_NAME_MAX_LEN {
188        return false;
189    }
190
191    if !node_name.starts_with(node_name_valid_first_char) {
192        return false;
193    }
194
195    if node_name.contains(|c: char| !node_name_valid_char(c)) {
196        return false;
197    }
198
199    if let Some(unit_address) = unit_address {
200        if unit_address.contains(|c: char| !node_name_valid_char(c)) {
201            return false;
202        }
203    }
204
205    true
206}
207
208fn node_name_valid_char(c: char) -> bool {
209    c.is_ascii_alphanumeric() || matches!(c, ',' | '.' | '_' | '+' | '-')
210}
211
212fn node_name_valid_first_char(c: char) -> bool {
213    c.is_ascii_alphabetic()
214}
215
216// Check if `name` is a valid property name.
217// https://devicetree-specification.readthedocs.io/en/stable/devicetree-basics.html#property-names
218fn property_name_valid(name: &str) -> bool {
219    if name.is_empty() || name.len() > PROPERTY_NAME_MAX_LEN {
220        return false;
221    }
222
223    if name.contains(|c: char| !property_name_valid_char(c)) {
224        return false;
225    }
226
227    true
228}
229
230fn property_name_valid_char(c: char) -> bool {
231    matches!(c, '0'..='9' | 'a'..='z' | 'A'..='Z' | ',' | '.' | '_' | '+' | '?' | '#' | '-')
232}
233
234/// Handle to an open node created by `FdtWriter::begin_node`.
235///
236/// This must be passed back to `FdtWriter::end_node` to close the nodes.
237/// Nodes must be closed in reverse order as they were opened, matching the nesting structure
238/// of the devicetree.
239#[derive(Debug)]
240pub struct FdtWriterNode {
241    depth: usize,
242}
243
244impl FdtWriter {
245    /// Create a new Flattened Devicetree writer instance.
246    pub fn new() -> Result<Self> {
247        FdtWriter::new_with_mem_reserv(&[])
248    }
249
250    /// Create a new Flattened Devicetree writer instance.
251    ///
252    /// # Arguments
253    ///
254    /// `mem_reservations` - reserved physical memory regions to list in the FDT header.
255    pub fn new_with_mem_reserv(mem_reservations: &[FdtReserveEntry]) -> Result<Self> {
256        let data = vec![0u8; FDT_HEADER_SIZE]; // Reserve space for header.
257
258        let mut fdt = FdtWriter {
259            data,
260            off_mem_rsvmap: 0,
261            off_dt_struct: 0,
262            strings: Vec::new(),
263            string_offsets: BTreeMap::new(),
264            node_depth: 0,
265            node_ended: false,
266            boot_cpuid_phys: 0,
267            phandles: HashSet::new(),
268        };
269
270        fdt.align(8);
271        // This conversion cannot fail since the size of the header is fixed.
272        fdt.off_mem_rsvmap = fdt.data.len() as u32;
273
274        check_overlapping(mem_reservations)?;
275        fdt.write_mem_rsvmap(mem_reservations);
276
277        fdt.align(4);
278        fdt.off_dt_struct = fdt
279            .data
280            .len()
281            .try_into()
282            .map_err(|_| Error::TotalSizeTooLarge)?;
283
284        Ok(fdt)
285    }
286
287    fn write_mem_rsvmap(&mut self, mem_reservations: &[FdtReserveEntry]) {
288        for rsv in mem_reservations {
289            self.append_u64(rsv.address);
290            self.append_u64(rsv.size);
291        }
292
293        self.append_u64(0);
294        self.append_u64(0);
295    }
296
297    /// Set the `boot_cpuid_phys` field of the devicetree header.
298    ///
299    /// # Example
300    ///
301    /// ```rust
302    /// use vm_fdt::{Error, FdtWriter};
303    ///
304    /// fn create_fdt() -> Result<Vec<u8>, Error> {
305    ///     let mut fdt = FdtWriter::new()?;
306    ///     fdt.set_boot_cpuid_phys(0x12345678);
307    ///     // ... add other nodes & properties
308    ///     fdt.finish()
309    /// }
310    ///
311    /// # let dtb = create_fdt().unwrap();
312    /// ```
313    pub fn set_boot_cpuid_phys(&mut self, boot_cpuid_phys: u32) {
314        self.boot_cpuid_phys = boot_cpuid_phys;
315    }
316
317    // Append `num_bytes` padding bytes (0x00).
318    fn pad(&mut self, num_bytes: usize) {
319        self.data.extend(core::iter::repeat(0).take(num_bytes));
320    }
321
322    // Append padding bytes (0x00) until the length of data is a multiple of `alignment`.
323    fn align(&mut self, alignment: usize) {
324        let offset = self.data.len() % alignment;
325        if offset != 0 {
326            self.pad(alignment - offset);
327        }
328    }
329
330    // Rewrite the value of a big-endian u32 within data.
331    fn update_u32(&mut self, offset: usize, val: u32) {
332        // Safe to use `+ 4` since we are calling this function with small values, and it's a
333        // private function.
334        let data_slice = &mut self.data[offset..offset + 4];
335        data_slice.copy_from_slice(&val.to_be_bytes());
336    }
337
338    fn append_u32(&mut self, val: u32) {
339        self.data.extend_from_slice(&val.to_be_bytes());
340    }
341
342    fn append_u64(&mut self, val: u64) {
343        self.data.extend_from_slice(&val.to_be_bytes());
344    }
345
346    /// Open a new FDT node.
347    ///
348    /// The node must be closed using `end_node`.
349    ///
350    /// # Arguments
351    ///
352    /// `name` - name of the node; must not contain any NUL bytes.
353    pub fn begin_node(&mut self, name: &str) -> Result<FdtWriterNode> {
354        if self.node_depth >= FDT_MAX_NODE_DEPTH {
355            return Err(Error::NodeDepthTooLarge);
356        }
357
358        let name_cstr = CString::new(name).map_err(|_| Error::InvalidString)?;
359        // The unit adddress part of the node name, if present, is not fully validated
360        // since the exact requirements depend on the bus mapping.
361        // https://devicetree-specification.readthedocs.io/en/stable/devicetree-basics.html#node-name-requirements
362        if !node_name_valid(name) {
363            return Err(Error::InvalidNodeName);
364        }
365        self.append_u32(FDT_BEGIN_NODE);
366        self.data.extend(name_cstr.to_bytes_with_nul());
367        self.align(4);
368        // This can not overflow due to the `if` at the beginning of the function
369        // where the current depth is checked against FDT_MAX_NODE_DEPTH.
370        self.node_depth += 1;
371        self.node_ended = false;
372        Ok(FdtWriterNode {
373            depth: self.node_depth,
374        })
375    }
376
377    /// Close a node previously opened with `begin_node`.
378    pub fn end_node(&mut self, node: FdtWriterNode) -> Result<()> {
379        if node.depth != self.node_depth {
380            return Err(Error::OutOfOrderEndNode);
381        }
382
383        self.append_u32(FDT_END_NODE);
384        // This can not underflow. The above `if` makes sure there is at least one open node
385        // (node_depth >= 1).
386        self.node_depth -= 1;
387        self.node_ended = true;
388        Ok(())
389    }
390
391    // Find an existing instance of a string `s`, or add it to the strings block.
392    // Returns the offset into the strings block.
393    fn intern_string(&mut self, s: CString) -> Result<u32> {
394        if let Some(off) = self.string_offsets.get(&s) {
395            Ok(*off)
396        } else {
397            let off = self
398                .strings
399                .len()
400                .try_into()
401                .map_err(|_| Error::TotalSizeTooLarge)?;
402            self.strings.extend_from_slice(s.to_bytes_with_nul());
403            self.string_offsets.insert(s, off);
404            Ok(off)
405        }
406    }
407
408    /// Write a property.
409    ///
410    /// # Arguments
411    ///
412    /// `name` - name of the property; must not contain any NUL bytes.
413    /// `val` - value of the property (raw byte array).
414    pub fn property(&mut self, name: &str, val: &[u8]) -> Result<()> {
415        if self.node_ended {
416            return Err(Error::PropertyAfterEndNode);
417        }
418
419        if self.node_depth == 0 {
420            return Err(Error::PropertyBeforeBeginNode);
421        }
422
423        let name_cstr = CString::new(name).map_err(|_| Error::InvalidString)?;
424
425        if !property_name_valid(name) {
426            return Err(Error::InvalidPropertyName);
427        }
428
429        let len = val
430            .len()
431            .try_into()
432            .map_err(|_| Error::PropertyValueTooLarge)?;
433
434        let nameoff = self.intern_string(name_cstr)?;
435        self.append_u32(FDT_PROP);
436        self.append_u32(len);
437        self.append_u32(nameoff);
438        self.data.extend_from_slice(val);
439        self.align(4);
440        Ok(())
441    }
442
443    /// Write an empty property.
444    pub fn property_null(&mut self, name: &str) -> Result<()> {
445        self.property(name, &[])
446    }
447
448    /// Write a string property.
449    pub fn property_string(&mut self, name: &str, val: &str) -> Result<()> {
450        let cstr_value = CString::new(val).map_err(|_| Error::InvalidString)?;
451        self.property(name, cstr_value.to_bytes_with_nul())
452    }
453
454    /// Write a stringlist property.
455    pub fn property_string_list(&mut self, name: &str, values: Vec<String>) -> Result<()> {
456        let mut bytes = Vec::new();
457        for s in values {
458            let cstr = CString::new(s).map_err(|_| Error::InvalidString)?;
459            bytes.extend_from_slice(cstr.to_bytes_with_nul());
460        }
461        self.property(name, &bytes)
462    }
463
464    /// Write a 32-bit unsigned integer property.
465    pub fn property_u32(&mut self, name: &str, val: u32) -> Result<()> {
466        self.property(name, &val.to_be_bytes())
467    }
468
469    /// Write a 64-bit unsigned integer property.
470    pub fn property_u64(&mut self, name: &str, val: u64) -> Result<()> {
471        self.property(name, &val.to_be_bytes())
472    }
473
474    /// Write a property containing an array of 32-bit unsigned integers.
475    pub fn property_array_u32(&mut self, name: &str, cells: &[u32]) -> Result<()> {
476        let mut arr = Vec::with_capacity(size_of_val(cells));
477        for &c in cells {
478            arr.extend(c.to_be_bytes());
479        }
480        self.property(name, &arr)
481    }
482
483    /// Write a property containing an array of 64-bit unsigned integers.
484    pub fn property_array_u64(&mut self, name: &str, cells: &[u64]) -> Result<()> {
485        let mut arr = Vec::with_capacity(size_of_val(cells));
486        for &c in cells {
487            arr.extend(c.to_be_bytes());
488        }
489        self.property(name, &arr)
490    }
491
492    /// Write a [`phandle`](https://devicetree-specification.readthedocs.io/en/stable/devicetree-basics.html?#phandle)
493    /// property. The value is checked for uniqueness within the FDT. In the case of a duplicate
494    /// [`Error::DuplicatePhandle`] is returned.
495    pub fn property_phandle(&mut self, val: u32) -> Result<()> {
496        if !self.phandles.insert(val) {
497            return Err(Error::DuplicatePhandle);
498        }
499        self.property("phandle", &val.to_be_bytes())
500    }
501
502    /// Finish writing the Devicetree Blob (DTB).
503    ///
504    /// Returns the DTB as a vector of bytes, consuming the `FdtWriter`.
505    pub fn finish(mut self) -> Result<Vec<u8>> {
506        if self.node_depth > 0 {
507            return Err(Error::UnclosedNode);
508        }
509
510        self.append_u32(FDT_END);
511        let size_dt_plus_header: u32 = self
512            .data
513            .len()
514            .try_into()
515            .map_err(|_| Error::TotalSizeTooLarge)?;
516        // The following operation cannot fail because the total size of data
517        // also includes the offset, and we checked that `size_dt_plus_header`
518        // does not wrap around when converted to an u32.
519        let size_dt_struct = size_dt_plus_header - self.off_dt_struct;
520
521        let off_dt_strings = self
522            .data
523            .len()
524            .try_into()
525            .map_err(|_| Error::TotalSizeTooLarge)?;
526        let size_dt_strings = self
527            .strings
528            .len()
529            .try_into()
530            .map_err(|_| Error::TotalSizeTooLarge)?;
531
532        let totalsize = self
533            .data
534            .len()
535            .checked_add(self.strings.len())
536            .ok_or(Error::TotalSizeTooLarge)?;
537        let totalsize = totalsize.try_into().map_err(|_| Error::TotalSizeTooLarge)?;
538
539        // Finalize the header.
540        self.update_u32(0, FDT_MAGIC);
541        self.update_u32(4, totalsize);
542        self.update_u32(2 * 4, self.off_dt_struct);
543        self.update_u32(3 * 4, off_dt_strings);
544        self.update_u32(4 * 4, self.off_mem_rsvmap);
545        self.update_u32(5 * 4, FDT_VERSION);
546        self.update_u32(6 * 4, FDT_LAST_COMP_VERSION);
547        self.update_u32(7 * 4, self.boot_cpuid_phys);
548        self.update_u32(8 * 4, size_dt_strings);
549        self.update_u32(9 * 4, size_dt_struct);
550
551        // Add the strings block.
552        self.data.append(&mut self.strings);
553
554        Ok(self.data)
555    }
556}
557
558#[cfg(test)]
559mod tests {
560    use super::*;
561
562    #[test]
563    fn minimal() {
564        let mut fdt = FdtWriter::new().unwrap();
565        let root_node = fdt.begin_node("").unwrap();
566        fdt.end_node(root_node).unwrap();
567        let actual_fdt = fdt.finish().unwrap();
568        let expected_fdt = vec![
569            0xd0, 0x0d, 0xfe, 0xed, // 0000: magic (0xd00dfeed)
570            0x00, 0x00, 0x00, 0x48, // 0004: totalsize (0x48)
571            0x00, 0x00, 0x00, 0x38, // 0008: off_dt_struct (0x38)
572            0x00, 0x00, 0x00, 0x48, // 000C: off_dt_strings (0x48)
573            0x00, 0x00, 0x00, 0x28, // 0010: off_mem_rsvmap (0x28)
574            0x00, 0x00, 0x00, 0x11, // 0014: version (0x11 = 17)
575            0x00, 0x00, 0x00, 0x10, // 0018: last_comp_version (0x10 = 16)
576            0x00, 0x00, 0x00, 0x00, // 001C: boot_cpuid_phys (0)
577            0x00, 0x00, 0x00, 0x00, // 0020: size_dt_strings (0)
578            0x00, 0x00, 0x00, 0x10, // 0024: size_dt_struct (0x10)
579            0x00, 0x00, 0x00, 0x00, // 0028: rsvmap terminator (address = 0 high)
580            0x00, 0x00, 0x00, 0x00, // 002C: rsvmap terminator (address = 0 low)
581            0x00, 0x00, 0x00, 0x00, // 0030: rsvmap terminator (size = 0 high)
582            0x00, 0x00, 0x00, 0x00, // 0034: rsvmap terminator (size = 0 low)
583            0x00, 0x00, 0x00, 0x01, // 0038: FDT_BEGIN_NODE
584            0x00, 0x00, 0x00, 0x00, // 003C: node name ("") + padding
585            0x00, 0x00, 0x00, 0x02, // 0040: FDT_END_NODE
586            0x00, 0x00, 0x00, 0x09, // 0044: FDT_END
587        ];
588        assert_eq!(expected_fdt, actual_fdt);
589    }
590
591    #[test]
592    fn reservemap() {
593        let mut fdt = FdtWriter::new_with_mem_reserv(&[
594            FdtReserveEntry::new(0x12345678AABBCCDD, 0x1234).unwrap(),
595            FdtReserveEntry::new(0x1020304050607080, 0x5678).unwrap(),
596        ])
597        .unwrap();
598        let root_node = fdt.begin_node("").unwrap();
599        fdt.end_node(root_node).unwrap();
600        let actual_fdt = fdt.finish().unwrap();
601        let expected_fdt = vec![
602            0xd0, 0x0d, 0xfe, 0xed, // 0000: magic (0xd00dfeed)
603            0x00, 0x00, 0x00, 0x68, // 0004: totalsize (0x68)
604            0x00, 0x00, 0x00, 0x58, // 0008: off_dt_struct (0x58)
605            0x00, 0x00, 0x00, 0x68, // 000C: off_dt_strings (0x68)
606            0x00, 0x00, 0x00, 0x28, // 0010: off_mem_rsvmap (0x28)
607            0x00, 0x00, 0x00, 0x11, // 0014: version (0x11 = 17)
608            0x00, 0x00, 0x00, 0x10, // 0018: last_comp_version (0x10 = 16)
609            0x00, 0x00, 0x00, 0x00, // 001C: boot_cpuid_phys (0)
610            0x00, 0x00, 0x00, 0x00, // 0020: size_dt_strings (0)
611            0x00, 0x00, 0x00, 0x10, // 0024: size_dt_struct (0x10)
612            0x12, 0x34, 0x56, 0x78, // 0028: rsvmap entry 0 address high
613            0xAA, 0xBB, 0xCC, 0xDD, // 002C: rsvmap entry 0 address low
614            0x00, 0x00, 0x00, 0x00, // 0030: rsvmap entry 0 size high
615            0x00, 0x00, 0x12, 0x34, // 0034: rsvmap entry 0 size low
616            0x10, 0x20, 0x30, 0x40, // 0038: rsvmap entry 1 address high
617            0x50, 0x60, 0x70, 0x80, // 003C: rsvmap entry 1 address low
618            0x00, 0x00, 0x00, 0x00, // 0040: rsvmap entry 1 size high
619            0x00, 0x00, 0x56, 0x78, // 0044: rsvmap entry 1 size low
620            0x00, 0x00, 0x00, 0x00, // 0048: rsvmap terminator (address = 0 high)
621            0x00, 0x00, 0x00, 0x00, // 004C: rsvmap terminator (address = 0 low)
622            0x00, 0x00, 0x00, 0x00, // 0050: rsvmap terminator (size = 0 high)
623            0x00, 0x00, 0x00, 0x00, // 0054: rsvmap terminator (size = 0 low)
624            0x00, 0x00, 0x00, 0x01, // 0058: FDT_BEGIN_NODE
625            0x00, 0x00, 0x00, 0x00, // 005C: node name ("") + padding
626            0x00, 0x00, 0x00, 0x02, // 0060: FDT_END_NODE
627            0x00, 0x00, 0x00, 0x09, // 0064: FDT_END
628        ];
629        assert_eq!(expected_fdt, actual_fdt);
630    }
631
632    #[test]
633    fn prop_null() {
634        let mut fdt = FdtWriter::new().unwrap();
635        let root_node = fdt.begin_node("").unwrap();
636        fdt.property_null("null").unwrap();
637        fdt.end_node(root_node).unwrap();
638        let actual_fdt = fdt.finish().unwrap();
639        let expected_fdt = vec![
640            0xd0, 0x0d, 0xfe, 0xed, // 0000: magic (0xd00dfeed)
641            0x00, 0x00, 0x00, 0x59, // 0004: totalsize (0x59)
642            0x00, 0x00, 0x00, 0x38, // 0008: off_dt_struct (0x38)
643            0x00, 0x00, 0x00, 0x54, // 000C: off_dt_strings (0x54)
644            0x00, 0x00, 0x00, 0x28, // 0010: off_mem_rsvmap (0x28)
645            0x00, 0x00, 0x00, 0x11, // 0014: version (0x11 = 17)
646            0x00, 0x00, 0x00, 0x10, // 0018: last_comp_version (0x10 = 16)
647            0x00, 0x00, 0x00, 0x00, // 001C: boot_cpuid_phys (0)
648            0x00, 0x00, 0x00, 0x05, // 0020: size_dt_strings (0x05)
649            0x00, 0x00, 0x00, 0x1c, // 0024: size_dt_struct (0x1C)
650            0x00, 0x00, 0x00, 0x00, // 0028: rsvmap terminator (address = 0 high)
651            0x00, 0x00, 0x00, 0x00, // 002C: rsvmap terminator (address = 0 low)
652            0x00, 0x00, 0x00, 0x00, // 0030: rsvmap terminator (size = 0 high)
653            0x00, 0x00, 0x00, 0x00, // 0034: rsvmap terminator (size = 0 low)
654            0x00, 0x00, 0x00, 0x01, // 0038: FDT_BEGIN_NODE
655            0x00, 0x00, 0x00, 0x00, // 003C: node name ("") + padding
656            0x00, 0x00, 0x00, 0x03, // 0040: FDT_PROP
657            0x00, 0x00, 0x00, 0x00, // 0044: prop len (0)
658            0x00, 0x00, 0x00, 0x00, // 0048: prop nameoff (0)
659            0x00, 0x00, 0x00, 0x02, // 004C: FDT_END_NODE
660            0x00, 0x00, 0x00, 0x09, // 0050: FDT_END
661            b'n', b'u', b'l', b'l', 0x00, // 0054: strings block
662        ];
663        assert_eq!(expected_fdt, actual_fdt);
664    }
665
666    #[test]
667    fn prop_u32() {
668        let mut fdt = FdtWriter::new().unwrap();
669        let root_node = fdt.begin_node("").unwrap();
670        fdt.property_u32("u32", 0x12345678).unwrap();
671        fdt.end_node(root_node).unwrap();
672        let actual_fdt = fdt.finish().unwrap();
673        let expected_fdt = vec![
674            0xd0, 0x0d, 0xfe, 0xed, // 0000: magic (0xd00dfeed)
675            0x00, 0x00, 0x00, 0x5c, // 0004: totalsize (0x5C)
676            0x00, 0x00, 0x00, 0x38, // 0008: off_dt_struct (0x38)
677            0x00, 0x00, 0x00, 0x58, // 000C: off_dt_strings (0x58)
678            0x00, 0x00, 0x00, 0x28, // 0010: off_mem_rsvmap (0x28)
679            0x00, 0x00, 0x00, 0x11, // 0014: version (0x11 = 17)
680            0x00, 0x00, 0x00, 0x10, // 0018: last_comp_version (0x10 = 16)
681            0x00, 0x00, 0x00, 0x00, // 001C: boot_cpuid_phys (0)
682            0x00, 0x00, 0x00, 0x04, // 0020: size_dt_strings (0x04)
683            0x00, 0x00, 0x00, 0x20, // 0024: size_dt_struct (0x20)
684            0x00, 0x00, 0x00, 0x00, // 0028: rsvmap terminator (address = 0 high)
685            0x00, 0x00, 0x00, 0x00, // 002C: rsvmap terminator (address = 0 low)
686            0x00, 0x00, 0x00, 0x00, // 0030: rsvmap terminator (size = 0 high)
687            0x00, 0x00, 0x00, 0x00, // 0034: rsvmap terminator (size = 0 low)
688            0x00, 0x00, 0x00, 0x01, // 0038: FDT_BEGIN_NODE
689            0x00, 0x00, 0x00, 0x00, // 003C: node name ("") + padding
690            0x00, 0x00, 0x00, 0x03, // 0040: FDT_PROP
691            0x00, 0x00, 0x00, 0x04, // 0044: prop len (4)
692            0x00, 0x00, 0x00, 0x00, // 0048: prop nameoff (0)
693            0x12, 0x34, 0x56, 0x78, // 004C: prop u32 value (0x12345678)
694            0x00, 0x00, 0x00, 0x02, // 0050: FDT_END_NODE
695            0x00, 0x00, 0x00, 0x09, // 0054: FDT_END
696            b'u', b'3', b'2', 0x00, // 0058: strings block
697        ];
698        assert_eq!(expected_fdt, actual_fdt);
699    }
700
701    #[test]
702    fn all_props() {
703        let mut fdt = FdtWriter::new().unwrap();
704        let root_node = fdt.begin_node("").unwrap();
705        fdt.property_null("null").unwrap();
706        fdt.property_u32("u32", 0x12345678).unwrap();
707        fdt.property_u64("u64", 0x1234567887654321).unwrap();
708        fdt.property_string("str", "hello").unwrap();
709        fdt.property_string_list("strlst", vec!["hi".into(), "bye".into()])
710            .unwrap();
711        fdt.property_array_u32("arru32", &[0x12345678, 0xAABBCCDD])
712            .unwrap();
713        fdt.property_array_u64("arru64", &[0x1234567887654321])
714            .unwrap();
715        fdt.end_node(root_node).unwrap();
716        let actual_fdt = fdt.finish().unwrap();
717        let expected_fdt = vec![
718            0xd0, 0x0d, 0xfe, 0xed, // 0000: magic (0xd00dfeed)
719            0x00, 0x00, 0x00, 0xee, // 0004: totalsize (0xEE)
720            0x00, 0x00, 0x00, 0x38, // 0008: off_dt_struct (0x38)
721            0x00, 0x00, 0x00, 0xc8, // 000C: off_dt_strings (0xC8)
722            0x00, 0x00, 0x00, 0x28, // 0010: off_mem_rsvmap (0x28)
723            0x00, 0x00, 0x00, 0x11, // 0014: version (0x11 = 17)
724            0x00, 0x00, 0x00, 0x10, // 0018: last_comp_version (0x10 = 16)
725            0x00, 0x00, 0x00, 0x00, // 001C: boot_cpuid_phys (0)
726            0x00, 0x00, 0x00, 0x26, // 0020: size_dt_strings (0x26)
727            0x00, 0x00, 0x00, 0x90, // 0024: size_dt_struct (0x90)
728            0x00, 0x00, 0x00, 0x00, // 0028: rsvmap terminator (address = 0 high)
729            0x00, 0x00, 0x00, 0x00, // 002C: rsvmap terminator (address = 0 low)
730            0x00, 0x00, 0x00, 0x00, // 0030: rsvmap terminator (size = 0 high)
731            0x00, 0x00, 0x00, 0x00, // 0034: rsvmap terminator (size = 0 low)
732            0x00, 0x00, 0x00, 0x01, // 0038: FDT_BEGIN_NODE
733            0x00, 0x00, 0x00, 0x00, // 003C: node name ("") + padding
734            0x00, 0x00, 0x00, 0x03, // 0040: FDT_PROP (null)
735            0x00, 0x00, 0x00, 0x00, // 0044: prop len (0)
736            0x00, 0x00, 0x00, 0x00, // 0048: prop nameoff (0)
737            0x00, 0x00, 0x00, 0x03, // 004C: FDT_PROP (u32)
738            0x00, 0x00, 0x00, 0x04, // 0050: prop len (4)
739            0x00, 0x00, 0x00, 0x05, // 0054: prop nameoff (0x05)
740            0x12, 0x34, 0x56, 0x78, // 0058: prop u32 value (0x12345678)
741            0x00, 0x00, 0x00, 0x03, // 005C: FDT_PROP (u64)
742            0x00, 0x00, 0x00, 0x08, // 0060: prop len (8)
743            0x00, 0x00, 0x00, 0x09, // 0064: prop nameoff (0x09)
744            0x12, 0x34, 0x56, 0x78, // 0068: prop u64 value high (0x12345678)
745            0x87, 0x65, 0x43, 0x21, // 006C: prop u64 value low (0x87654321)
746            0x00, 0x00, 0x00, 0x03, // 0070: FDT_PROP (string)
747            0x00, 0x00, 0x00, 0x06, // 0074: prop len (6)
748            0x00, 0x00, 0x00, 0x0D, // 0078: prop nameoff (0x0D)
749            b'h', b'e', b'l', b'l', // 007C: prop str value ("hello") + padding
750            b'o', 0x00, 0x00, 0x00, // 0080: "o\0" + padding
751            0x00, 0x00, 0x00, 0x03, // 0084: FDT_PROP (string list)
752            0x00, 0x00, 0x00, 0x07, // 0088: prop len (7)
753            0x00, 0x00, 0x00, 0x11, // 008C: prop nameoff (0x11)
754            b'h', b'i', 0x00, b'b', // 0090: prop value ("hi", "bye")
755            b'y', b'e', 0x00, 0x00, // 0094: "ye\0" + padding
756            0x00, 0x00, 0x00, 0x03, // 0098: FDT_PROP (u32 array)
757            0x00, 0x00, 0x00, 0x08, // 009C: prop len (8)
758            0x00, 0x00, 0x00, 0x18, // 00A0: prop nameoff (0x18)
759            0x12, 0x34, 0x56, 0x78, // 00A4: prop value 0
760            0xAA, 0xBB, 0xCC, 0xDD, // 00A8: prop value 1
761            0x00, 0x00, 0x00, 0x03, // 00AC: FDT_PROP (u64 array)
762            0x00, 0x00, 0x00, 0x08, // 00B0: prop len (8)
763            0x00, 0x00, 0x00, 0x1f, // 00B4: prop nameoff (0x1F)
764            0x12, 0x34, 0x56, 0x78, // 00B8: prop u64 value 0 high
765            0x87, 0x65, 0x43, 0x21, // 00BC: prop u64 value 0 low
766            0x00, 0x00, 0x00, 0x02, // 00C0: FDT_END_NODE
767            0x00, 0x00, 0x00, 0x09, // 00C4: FDT_END
768            b'n', b'u', b'l', b'l', 0x00, // 00C8: strings + 0x00: "null""
769            b'u', b'3', b'2', 0x00, // 00CD: strings + 0x05: "u32"
770            b'u', b'6', b'4', 0x00, // 00D1: strings + 0x09: "u64"
771            b's', b't', b'r', 0x00, // 00D5: strings + 0x0D: "str"
772            b's', b't', b'r', b'l', b's', b't', 0x00, // 00D9: strings + 0x11: "strlst"
773            b'a', b'r', b'r', b'u', b'3', b'2', 0x00, // 00E0: strings + 0x18: "arru32"
774            b'a', b'r', b'r', b'u', b'6', b'4', 0x00, // 00E7: strings + 0x1F: "arru64"
775        ];
776        assert_eq!(expected_fdt, actual_fdt);
777    }
778
779    #[test]
780    fn property_before_begin_node() {
781        let mut fdt = FdtWriter::new().unwrap();
782        // Test that adding a property at the beginning of the FDT blob does not work.
783        assert_eq!(
784            fdt.property_string("invalid", "property").unwrap_err(),
785            Error::PropertyBeforeBeginNode
786        );
787
788        // Test that adding a property after the end node does not work.
789        let node = fdt.begin_node("root").unwrap();
790        fdt.end_node(node).unwrap();
791        assert_eq!(
792            fdt.property_string("invalid", "property").unwrap_err(),
793            Error::PropertyAfterEndNode
794        );
795    }
796
797    #[test]
798    fn nested_nodes() {
799        let mut fdt = FdtWriter::new().unwrap();
800        let root_node = fdt.begin_node("").unwrap();
801        fdt.property_u32("abc", 0x13579024).unwrap();
802        let nested_node = fdt.begin_node("nested").unwrap();
803        fdt.property_u32("def", 0x12121212).unwrap();
804        fdt.end_node(nested_node).unwrap();
805        fdt.end_node(root_node).unwrap();
806        let actual_fdt = fdt.finish().unwrap();
807        let expected_fdt = vec![
808            0xd0, 0x0d, 0xfe, 0xed, // 0000: magic (0xd00dfeed)
809            0x00, 0x00, 0x00, 0x80, // 0004: totalsize (0x80)
810            0x00, 0x00, 0x00, 0x38, // 0008: off_dt_struct (0x38)
811            0x00, 0x00, 0x00, 0x78, // 000C: off_dt_strings (0x78)
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, 0x08, // 0020: size_dt_strings (0x08)
817            0x00, 0x00, 0x00, 0x40, // 0024: size_dt_struct (0x40)
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, 0x03, // 0040: FDT_PROP
825            0x00, 0x00, 0x00, 0x04, // 0044: prop len (4)
826            0x00, 0x00, 0x00, 0x00, // 0048: prop nameoff (0x00)
827            0x13, 0x57, 0x90, 0x24, // 004C: prop u32 value (0x13579024)
828            0x00, 0x00, 0x00, 0x01, // 0050: FDT_BEGIN_NODE
829            b'n', b'e', b's', b't', // 0054: Node name ("nested")
830            b'e', b'd', 0x00, 0x00, // 0058: "ed\0" + pad
831            0x00, 0x00, 0x00, 0x03, // 005C: FDT_PROP
832            0x00, 0x00, 0x00, 0x04, // 0060: prop len (4)
833            0x00, 0x00, 0x00, 0x04, // 0064: prop nameoff (0x04)
834            0x12, 0x12, 0x12, 0x12, // 0068: prop u32 value (0x12121212)
835            0x00, 0x00, 0x00, 0x02, // 006C: FDT_END_NODE ("nested")
836            0x00, 0x00, 0x00, 0x02, // 0070: FDT_END_NODE ("")
837            0x00, 0x00, 0x00, 0x09, // 0074: FDT_END
838            b'a', b'b', b'c', 0x00, // 0078: strings + 0x00: "abc"
839            b'd', b'e', b'f', 0x00, // 007C: strings + 0x04: "def"
840        ];
841        assert_eq!(expected_fdt, actual_fdt);
842    }
843
844    #[test]
845    fn prop_name_string_reuse() {
846        let mut fdt = FdtWriter::new().unwrap();
847        let root_node = fdt.begin_node("").unwrap();
848        fdt.property_u32("abc", 0x13579024).unwrap();
849        let nested_node = fdt.begin_node("nested").unwrap();
850        fdt.property_u32("def", 0x12121212).unwrap();
851        fdt.property_u32("abc", 0x12121212).unwrap(); // This should reuse the "abc" string.
852        fdt.end_node(nested_node).unwrap();
853        fdt.end_node(root_node).unwrap();
854        let actual_fdt = fdt.finish().unwrap();
855        let expected_fdt = vec![
856            0xd0, 0x0d, 0xfe, 0xed, // 0000: magic (0xd00dfeed)
857            0x00, 0x00, 0x00, 0x90, // 0004: totalsize (0x90)
858            0x00, 0x00, 0x00, 0x38, // 0008: off_dt_struct (0x38)
859            0x00, 0x00, 0x00, 0x88, // 000C: off_dt_strings (0x88)
860            0x00, 0x00, 0x00, 0x28, // 0010: off_mem_rsvmap (0x28)
861            0x00, 0x00, 0x00, 0x11, // 0014: version (0x11 = 17)
862            0x00, 0x00, 0x00, 0x10, // 0018: last_comp_version (0x10 = 16)
863            0x00, 0x00, 0x00, 0x00, // 001C: boot_cpuid_phys (0)
864            0x00, 0x00, 0x00, 0x08, // 0020: size_dt_strings (0x08)
865            0x00, 0x00, 0x00, 0x50, // 0024: size_dt_struct (0x50)
866            0x00, 0x00, 0x00, 0x00, // 0028: rsvmap terminator (address = 0 high)
867            0x00, 0x00, 0x00, 0x00, // 002C: rsvmap terminator (address = 0 low)
868            0x00, 0x00, 0x00, 0x00, // 0030: rsvmap terminator (size = 0 high)
869            0x00, 0x00, 0x00, 0x00, // 0034: rsvmap terminator (size = 0 low)
870            0x00, 0x00, 0x00, 0x01, // 0038: FDT_BEGIN_NODE
871            0x00, 0x00, 0x00, 0x00, // 003C: node name ("") + padding
872            0x00, 0x00, 0x00, 0x03, // 0040: FDT_PROP
873            0x00, 0x00, 0x00, 0x04, // 0044: prop len (4)
874            0x00, 0x00, 0x00, 0x00, // 0048: prop nameoff (0x00)
875            0x13, 0x57, 0x90, 0x24, // 004C: prop u32 value (0x13579024)
876            0x00, 0x00, 0x00, 0x01, // 0050: FDT_BEGIN_NODE
877            b'n', b'e', b's', b't', // 0054: Node name ("nested")
878            b'e', b'd', 0x00, 0x00, // 0058: "ed\0" + pad
879            0x00, 0x00, 0x00, 0x03, // 005C: FDT_PROP
880            0x00, 0x00, 0x00, 0x04, // 0060: prop len (4)
881            0x00, 0x00, 0x00, 0x04, // 0064: prop nameoff (0x04)
882            0x12, 0x12, 0x12, 0x12, // 0068: prop u32 value (0x12121212)
883            0x00, 0x00, 0x00, 0x03, // 006C: FDT_PROP
884            0x00, 0x00, 0x00, 0x04, // 0070: prop len (4)
885            0x00, 0x00, 0x00, 0x00, // 0074: prop nameoff (0x00 - reuse)
886            0x12, 0x12, 0x12, 0x12, // 0078: prop u32 value (0x12121212)
887            0x00, 0x00, 0x00, 0x02, // 007C: FDT_END_NODE ("nested")
888            0x00, 0x00, 0x00, 0x02, // 0080: FDT_END_NODE ("")
889            0x00, 0x00, 0x00, 0x09, // 0084: FDT_END
890            b'a', b'b', b'c', 0x00, // 0088: strings + 0x00: "abc"
891            b'd', b'e', b'f', 0x00, // 008C: strings + 0x04: "def"
892        ];
893        assert_eq!(expected_fdt, actual_fdt);
894    }
895
896    #[test]
897    fn boot_cpuid() {
898        let mut fdt = FdtWriter::new().unwrap();
899        fdt.set_boot_cpuid_phys(0x12345678);
900        let root_node = fdt.begin_node("").unwrap();
901        fdt.end_node(root_node).unwrap();
902        let actual_fdt = fdt.finish().unwrap();
903        let expected_fdt = vec![
904            0xd0, 0x0d, 0xfe, 0xed, // 0000: magic (0xd00dfeed)
905            0x00, 0x00, 0x00, 0x48, // 0004: totalsize (0x48)
906            0x00, 0x00, 0x00, 0x38, // 0008: off_dt_struct (0x38)
907            0x00, 0x00, 0x00, 0x48, // 000C: off_dt_strings (0x48)
908            0x00, 0x00, 0x00, 0x28, // 0010: off_mem_rsvmap (0x28)
909            0x00, 0x00, 0x00, 0x11, // 0014: version (0x11 = 17)
910            0x00, 0x00, 0x00, 0x10, // 0018: last_comp_version (0x10 = 16)
911            0x12, 0x34, 0x56, 0x78, // 001C: boot_cpuid_phys (0x12345678)
912            0x00, 0x00, 0x00, 0x00, // 0020: size_dt_strings (0)
913            0x00, 0x00, 0x00, 0x10, // 0024: size_dt_struct (0x10)
914            0x00, 0x00, 0x00, 0x00, // 0028: rsvmap terminator (address = 0 high)
915            0x00, 0x00, 0x00, 0x00, // 002C: rsvmap terminator (address = 0 low)
916            0x00, 0x00, 0x00, 0x00, // 0030: rsvmap terminator (size = 0 high)
917            0x00, 0x00, 0x00, 0x00, // 0034: rsvmap terminator (size = 0 low)
918            0x00, 0x00, 0x00, 0x01, // 0038: FDT_BEGIN_NODE
919            0x00, 0x00, 0x00, 0x00, // 003C: node name ("") + padding
920            0x00, 0x00, 0x00, 0x02, // 0040: FDT_END_NODE
921            0x00, 0x00, 0x00, 0x09, // 0044: FDT_END
922        ];
923        assert_eq!(expected_fdt, actual_fdt);
924    }
925
926    #[test]
927    fn invalid_node_name_nul() {
928        let mut fdt = FdtWriter::new().unwrap();
929        fdt.begin_node("root").unwrap();
930        assert_eq!(
931            fdt.begin_node("abc\0def").unwrap_err(),
932            Error::InvalidString
933        );
934    }
935
936    #[test]
937    fn invalid_prop_name_nul() {
938        let mut fdt = FdtWriter::new().unwrap();
939        fdt.begin_node("root").unwrap();
940        assert_eq!(
941            fdt.property_u32("abc\0def", 0).unwrap_err(),
942            Error::InvalidString
943        );
944    }
945
946    #[test]
947    fn invalid_prop_string_value_nul() {
948        let mut fdt = FdtWriter::new().unwrap();
949        fdt.begin_node("root").unwrap();
950        assert_eq!(
951            fdt.property_string("mystr", "abc\0def").unwrap_err(),
952            Error::InvalidString
953        );
954    }
955
956    #[test]
957    fn invalid_prop_string_list_value_nul() {
958        let mut fdt = FdtWriter::new().unwrap();
959        let strs = vec!["test".into(), "abc\0def".into()];
960        assert_eq!(
961            fdt.property_string_list("mystr", strs).unwrap_err(),
962            Error::InvalidString
963        );
964    }
965
966    #[test]
967    fn invalid_prop_after_end_node() {
968        let mut fdt = FdtWriter::new().unwrap();
969        let _root_node = fdt.begin_node("").unwrap();
970        fdt.property_u32("ok_prop", 1234).unwrap();
971        let nested_node = fdt.begin_node("mynode").unwrap();
972        fdt.property_u32("ok_nested_prop", 5678).unwrap();
973        fdt.end_node(nested_node).unwrap();
974        assert_eq!(
975            fdt.property_u32("bad_prop_after_end_node", 1357)
976                .unwrap_err(),
977            Error::PropertyAfterEndNode
978        );
979    }
980
981    #[test]
982    fn invalid_end_node_out_of_order() {
983        let mut fdt = FdtWriter::new().unwrap();
984        let root_node = fdt.begin_node("").unwrap();
985        fdt.property_u32("ok_prop", 1234).unwrap();
986        let _nested_node = fdt.begin_node("mynode").unwrap();
987        assert_eq!(
988            fdt.end_node(root_node).unwrap_err(),
989            Error::OutOfOrderEndNode
990        );
991    }
992
993    #[test]
994    fn invalid_finish_while_node_open() {
995        let mut fdt = FdtWriter::new().unwrap();
996        let _root_node = fdt.begin_node("").unwrap();
997        fdt.property_u32("ok_prop", 1234).unwrap();
998        let _nested_node = fdt.begin_node("mynode").unwrap();
999        fdt.property_u32("ok_nested_prop", 5678).unwrap();
1000        assert_eq!(fdt.finish().unwrap_err(), Error::UnclosedNode);
1001    }
1002
1003    #[test]
1004    #[cfg(feature = "long_running_test")]
1005    fn test_overflow_subtract() {
1006        let overflow_size = u32::MAX / std::mem::size_of::<FdtReserveEntry>() as u32 - 3;
1007        let too_large_mem_reserve: Vec<FdtReserveEntry> = (0..overflow_size)
1008            .map(|i| FdtReserveEntry::new(u64::from(i) * 2, 1).unwrap())
1009            .collect();
1010        let mut fdt = FdtWriter::new_with_mem_reserv(&too_large_mem_reserve).unwrap();
1011        let root_node = fdt.begin_node("").unwrap();
1012        fdt.end_node(root_node).unwrap();
1013        assert_eq!(fdt.finish().unwrap_err(), Error::TotalSizeTooLarge);
1014    }
1015
1016    #[test]
1017    fn test_invalid_mem_reservations() {
1018        // Test that we cannot create an invalid FDT reserve entry where the
1019        // end address of the region would not fit in an u64.
1020        assert_eq!(
1021            FdtReserveEntry::new(0x1, u64::MAX).unwrap_err(),
1022            Error::InvalidMemoryReservation
1023        );
1024
1025        // Test that we cannot have a memory reservation with size 0.
1026        assert_eq!(
1027            FdtReserveEntry::new(0x1, 0).unwrap_err(),
1028            Error::InvalidMemoryReservation
1029        );
1030    }
1031
1032    #[test]
1033    fn test_cmp_mem_reservations() {
1034        // Test that just the address is taken into consideration when comparing to `FdtReserveEntry`.
1035        assert_eq!(
1036            FdtReserveEntry::new(0x1, 10)
1037                .unwrap()
1038                .cmp(&FdtReserveEntry::new(0x1, 11).unwrap()),
1039            Ordering::Equal
1040        );
1041        assert_eq!(
1042            FdtReserveEntry::new(0x1, 10)
1043                .unwrap()
1044                .cmp(&FdtReserveEntry::new(0x2, 10).unwrap()),
1045            Ordering::Less
1046        );
1047        assert_eq!(
1048            FdtReserveEntry::new(0x1, 10)
1049                .unwrap()
1050                .cmp(&FdtReserveEntry::new(0x0, 10).unwrap()),
1051            Ordering::Greater
1052        );
1053    }
1054
1055    #[test]
1056    fn test_overlapping_mem_reservations() {
1057        // Check that regions that overlap return an error on new.
1058        // Check overlapping by one.
1059        let overlapping = [
1060            FdtReserveEntry::new(0x3, 1).unwrap(), // this overlaps with
1061            FdtReserveEntry::new(0x0, 1).unwrap(),
1062            FdtReserveEntry::new(0x2, 2).unwrap(), // this one.
1063        ];
1064        let fdt = FdtWriter::new_with_mem_reserv(&overlapping);
1065        assert_eq!(fdt.unwrap_err(), Error::OverlappingMemoryReservations);
1066
1067        // Check a larger overlap.
1068        let overlapping = [
1069            FdtReserveEntry::new(0x100, 100).unwrap(),
1070            FdtReserveEntry::new(0x50, 300).unwrap(),
1071        ];
1072        let fdt = FdtWriter::new_with_mem_reserv(&overlapping);
1073        assert_eq!(fdt.unwrap_err(), Error::OverlappingMemoryReservations);
1074    }
1075
1076    #[test]
1077    fn test_off_by_one_mem_rsv() {
1078        // This test is for making sure we do not introduce off by one errors
1079        // in the memory reservations checks.
1080        let non_overlapping = [
1081            FdtReserveEntry::new(0x0, 1).unwrap(),
1082            FdtReserveEntry::new(0x1, 1).unwrap(),
1083            FdtReserveEntry::new(0x2, 2).unwrap(),
1084        ];
1085
1086        assert!(FdtWriter::new_with_mem_reserv(&non_overlapping).is_ok());
1087    }
1088
1089    #[test]
1090    fn test_node_name_valid() {
1091        assert!(node_name_valid("abcdef"));
1092        assert!(node_name_valid("abcdef@1000"));
1093        assert!(node_name_valid("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"));
1094        assert!(node_name_valid("azAZ09,._+-"));
1095        assert!(node_name_valid("Abcd"));
1096
1097        assert!(node_name_valid(""));
1098
1099        // Name missing.
1100        assert!(!node_name_valid("@1000"));
1101
1102        // Name too long.
1103        assert!(!node_name_valid("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"));
1104        assert!(!node_name_valid("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@1234"));
1105
1106        // Name contains invalid characters.
1107        assert!(!node_name_valid("abc#def"));
1108        assert!(!node_name_valid("abc/def"));
1109
1110        // Name begins with non-alphabetic character.
1111        assert!(!node_name_valid("1abc"));
1112
1113        // Unit address contains invalid characters.
1114        assert!(!node_name_valid("abcdef@1000#"));
1115
1116        // More than one '@'.
1117        assert!(!node_name_valid("abc@1000@def"));
1118    }
1119
1120    #[test]
1121    fn test_property_name_valid() {
1122        assert!(property_name_valid("abcdef"));
1123        assert!(property_name_valid("01234"));
1124        assert!(property_name_valid("azAZ09,._+?#-"));
1125        assert!(property_name_valid("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"));
1126
1127        // Name contains invalid characters.
1128        assert!(!property_name_valid("abc!def"));
1129        assert!(!property_name_valid("abc@1234"));
1130
1131        // Name too long.
1132        assert!(!property_name_valid("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"));
1133    }
1134
1135    #[test]
1136    fn depth_overflow() {
1137        let mut fdt = FdtWriter::new().unwrap();
1138        for _ in 1..=FDT_MAX_NODE_DEPTH {
1139            fdt.begin_node("test").unwrap();
1140        }
1141        assert_eq!(
1142            fdt.begin_node("test").unwrap_err(),
1143            Error::NodeDepthTooLarge
1144        );
1145    }
1146
1147    #[test]
1148    fn unique_phandles() {
1149        let mut fdt = FdtWriter::new().unwrap();
1150        let root_node = fdt.begin_node("root").unwrap();
1151
1152        let prim_node = fdt.begin_node("phandle-1").unwrap();
1153        fdt.property_phandle(1).unwrap();
1154        fdt.end_node(prim_node).unwrap();
1155
1156        let prim_node = fdt.begin_node("phandle-2").unwrap();
1157        fdt.property_phandle(2).unwrap();
1158        fdt.end_node(prim_node).unwrap();
1159
1160        fdt.end_node(root_node).unwrap();
1161        fdt.finish().unwrap();
1162    }
1163
1164    #[test]
1165    fn duplicate_phandles() {
1166        let mut fdt = FdtWriter::new().unwrap();
1167        let _root_node = fdt.begin_node("root").unwrap();
1168
1169        let prim_node = fdt.begin_node("phandle-1").unwrap();
1170        fdt.property_phandle(1).unwrap();
1171        fdt.end_node(prim_node).unwrap();
1172
1173        let _sec_node = fdt.begin_node("phandle-2").unwrap();
1174        assert_eq!(
1175            fdt.property_phandle(1).unwrap_err(),
1176            Error::DuplicatePhandle
1177        );
1178    }
1179}