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