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