1use std::collections::BTreeMap;
9use std::convert::TryInto;
10use std::io;
11
12use indexmap::map::Entry;
13use indexmap::IndexMap;
14use remain::sorted;
15use thiserror::Error as ThisError;
16
17use crate::path::Path;
18use crate::propval::FromFdtPropval;
19use crate::propval::ToFdtPropval;
20
21pub(crate) const SIZE_U32: usize = std::mem::size_of::<u32>();
22pub(crate) const SIZE_U64: usize = std::mem::size_of::<u64>();
23
24#[sorted]
25#[derive(ThisError, Debug)]
26pub enum Error {
27 #[error("Error applying device tree overlay: {}", .0)]
28 ApplyOverlayError(String),
29 #[error("Binary size must fit in 32 bits")]
30 BinarySizeTooLarge,
31 #[error("Duplicate node {}", .0)]
32 DuplicateNode(String),
33 #[error("I/O error dumping FDT to file code={} path={}", .0, .1.display())]
34 FdtDumpIoError(io::Error, std::path::PathBuf),
35 #[error("Error writing FDT to guest memory")]
36 FdtGuestMemoryWriteError,
37 #[error("I/O error code={0}")]
38 FdtIoError(io::Error),
39 #[error("Parse error reading FDT parameters: {}", .0)]
40 FdtParseError(String),
41 #[error("Error applying FDT tree filter: {}", .0)]
42 FilterError(String),
43 #[error("Invalid name string: {}", .0)]
44 InvalidName(String),
45 #[error("Invalid path: {}", .0)]
46 InvalidPath(String),
47 #[error("Invalid string value {}", .0)]
48 InvalidString(String),
49 #[error("Expected phandle value for IOMMU of type: {}, id: {:?}", .0, .1)]
50 MissingIommuPhandle(String, Option<u32>),
51 #[error("Expected power domain: offset={}", .0)]
52 MissingPowerDomain(usize),
53 #[error("Property value is not valid")]
54 PropertyValueInvalid,
55 #[error("Property value size must fit in 32 bits")]
56 PropertyValueTooLarge,
57 #[error("Total size must fit in 32 bits")]
58 TotalSizeTooLarge,
59}
60
61impl From<io::Error> for Error {
62 fn from(value: io::Error) -> Self {
63 Self::FdtIoError(value)
64 }
65}
66
67pub type Result<T> = std::result::Result<T, Error>;
68type Blob<'a> = &'a [u8];
69
70const FDT_BEGIN_NODE: u32 = 0x00000001;
71const FDT_END_NODE: u32 = 0x00000002;
72const FDT_PROP: u32 = 0x00000003;
73const FDT_NOP: u32 = 0x00000004;
74const FDT_END: u32 = 0x00000009;
75
76fn consume<'a>(bytes: &mut &'a [u8], n: usize) -> Result<&'a [u8]> {
78 let mid = n;
79 if mid > bytes.len() {
80 Err(Error::PropertyValueInvalid)
81 } else {
82 let (data_bytes, rest) = bytes.split_at(n);
83 *(bytes) = rest;
84 Ok(data_bytes)
85 }
86}
87
88#[inline]
90fn rdu32(data: &mut Blob) -> Result<u32> {
91 Ok(u32::from_be_bytes(
92 consume(data, SIZE_U32)?.try_into().unwrap(),
94 ))
95}
96
97#[inline]
99fn rdu64(data: &mut Blob) -> Result<u64> {
100 Ok(u64::from_be_bytes(
101 consume(data, SIZE_U64)?.try_into().unwrap(),
103 ))
104}
105
106#[inline]
108fn align_pad_len(size: usize, alignment: usize) -> usize {
109 (alignment - size % alignment) % alignment
110}
111
112#[inline]
114fn align_data(data: &mut Vec<u8>, alignment: usize) {
115 data.resize(align_pad_len(data.len(), alignment) + data.len(), 0u8);
116}
117
118pub(crate) fn c_str_to_string(input: Blob) -> Option<String> {
120 let size = input.iter().position(|&v| v == 0u8)?;
121 String::from_utf8(input[..size].to_vec()).ok()
122}
123
124fn is_valid_prop_name(name: &str) -> bool {
126 const ALLOWED_SPECIAL_CHARS: [u8; 7] = [b'.', b',', b'_', b'+', b'?', b'#', b'-'];
127 name.bytes()
128 .all(|c| c.is_ascii_alphanumeric() || ALLOWED_SPECIAL_CHARS.contains(&c))
129}
130
131fn is_valid_node_name(name: &str) -> bool {
133 const ALLOWED_SPECIAL_CHARS: [u8; 6] = [b'.', b',', b'_', b'+', b'-', b'@'];
134 const ADDR_SEP: u8 = b'@';
135 if name.bytes().filter(|&c| c == ADDR_SEP).count() > 1 {
137 return false;
138 }
139 name.bytes()
140 .all(|c| c.is_ascii_alphanumeric() || ALLOWED_SPECIAL_CHARS.contains(&c))
141}
142
143#[derive(Default, Debug)]
145struct FdtHeader {
146 magic: u32, total_size: u32, off_dt_struct: u32, off_dt_strings: u32, off_mem_rsvmap: u32, version: u32, last_comp_version: u32, boot_cpuid_phys: u32, size_dt_strings: u32, size_dt_struct: u32, }
157
158impl FdtHeader {
159 const MAGIC: u32 = 0xd00dfeed;
160 const VERSION: u32 = 17;
161 const LAST_COMP_VERSION: u32 = 16;
162 const SIZE: usize = 10 * SIZE_U32;
163
164 fn new(
166 total_size: u32,
167 off_dt_struct: u32,
168 off_dt_strings: u32,
169 off_mem_rsvmap: u32,
170 boot_cpuid_phys: u32,
171 size_dt_strings: u32,
172 size_dt_struct: u32,
173 ) -> Self {
174 Self {
175 magic: Self::MAGIC,
176 total_size,
177 off_dt_struct,
178 off_dt_strings,
179 off_mem_rsvmap,
180 version: Self::VERSION,
181 last_comp_version: Self::LAST_COMP_VERSION,
182 boot_cpuid_phys,
183 size_dt_strings,
184 size_dt_struct,
185 }
186 }
187
188 fn write_blob(&self, buffer: &mut [u8]) -> Result<()> {
190 assert_eq!(buffer.len(), Self::SIZE);
191 for (chunk, val_u32) in buffer.chunks_exact_mut(SIZE_U32).zip(&[
192 self.magic,
193 self.total_size,
194 self.off_dt_struct,
195 self.off_dt_strings,
196 self.off_mem_rsvmap,
197 self.version,
198 self.last_comp_version,
199 self.boot_cpuid_phys,
200 self.size_dt_strings,
201 self.size_dt_struct,
202 ]) {
203 chunk.copy_from_slice(&val_u32.to_be_bytes());
204 }
205 Ok(())
206 }
207
208 fn from_blob(mut input: Blob) -> Result<Self> {
210 if input.len() < Self::SIZE {
211 return Err(Error::FdtParseError("invalid binary size".into()));
212 }
213 let input = &mut input;
214 let header = Self {
215 magic: rdu32(input)?,
216 total_size: rdu32(input)?,
217 off_dt_struct: rdu32(input)?,
218 off_dt_strings: rdu32(input)?,
219 off_mem_rsvmap: rdu32(input)?,
220 version: rdu32(input)?,
221 last_comp_version: rdu32(input)?,
222 boot_cpuid_phys: rdu32(input)?,
223 size_dt_strings: rdu32(input)?,
224 size_dt_struct: rdu32(input)?,
225 };
226 if header.magic != Self::MAGIC {
227 return Err(Error::FdtParseError("invalid header magic".into()));
228 }
229 if header.version < Self::VERSION {
230 return Err(Error::FdtParseError("unsupported FDT version".into()));
231 }
232 if header.off_mem_rsvmap >= header.off_dt_strings
233 || header.off_mem_rsvmap < FdtHeader::SIZE as u32
234 {
235 return Err(Error::FdtParseError(
236 "invalid reserved memory offset".into(),
237 ));
238 }
239
240 let off_dt_struct_end = header
241 .off_dt_struct
242 .checked_add(header.size_dt_struct)
243 .ok_or_else(|| Error::FdtParseError("struct end offset must fit in 32 bits".into()))?;
244 if off_dt_struct_end > header.off_dt_strings {
245 return Err(Error::FdtParseError("struct and strings overlap".into()));
246 }
247
248 let off_dt_strings_end = header
249 .off_dt_strings
250 .checked_add(header.size_dt_strings)
251 .ok_or_else(|| Error::FdtParseError("strings end offset must fit in 32 bits".into()))?;
252 if off_dt_strings_end > header.total_size {
253 return Err(Error::FdtParseError("strings data past total size".into()));
254 }
255
256 Ok(header)
257 }
258}
259
260#[derive(Default)]
262struct FdtStrings {
263 strings: Vec<u8>,
264 string_offsets: BTreeMap<String, u32>,
265}
266
267impl FdtStrings {
268 fn from_blob(input: Blob) -> Result<Self> {
270 if input.last().is_some_and(|i| *i != 0) {
271 return Err(Error::FdtParseError(
272 "strings block missing null terminator".into(),
273 ));
274 }
275 let mut string_offsets = BTreeMap::new();
276 let mut offset = 0u32;
277 for bytes in input.split(|&x| x == 0u8) {
278 if bytes.is_empty() {
279 break;
280 }
281 let string = String::from_utf8(bytes.to_vec())
282 .map_err(|_| Error::FdtParseError("invalid value in strings block".into()))?;
283 string_offsets.insert(string, offset);
284 offset += u32::try_from(bytes.len() + 1).map_err(|_| Error::BinarySizeTooLarge)?;
285 }
286 Ok(Self {
287 strings: input.to_vec(),
288 string_offsets,
289 })
290 }
291
292 fn intern_string(&mut self, s: &str) -> u32 {
295 if let Some(off) = self.string_offsets.get(s) {
296 *off
297 } else {
298 let off = self.strings.len() as u32;
299 self.strings.extend_from_slice(s.as_bytes());
300 self.strings.push(0u8);
301 self.string_offsets.insert(s.to_owned(), off);
302 off
303 }
304 }
305
306 fn write_blob(&self, mut writer: impl io::Write) -> Result<()> {
308 Ok(writer.write_all(&self.strings)?)
309 }
310
311 fn at_offset(&self, off: u32) -> Option<String> {
313 self.strings
314 .get(off as usize..)
315 .and_then(c_str_to_string)
316 .filter(|s| !s.is_empty())
317 }
318}
319
320#[derive(Debug, Clone)]
325pub struct FdtNode {
326 pub(crate) name: String,
328 pub(crate) props: IndexMap<String, Vec<u8>>,
329 pub(crate) subnodes: IndexMap<String, FdtNode>,
330}
331
332impl FdtNode {
333 pub(crate) fn new(
336 name: String,
337 props: IndexMap<String, Vec<u8>>,
338 subnodes: IndexMap<String, FdtNode>,
339 ) -> Result<Self> {
340 if !is_valid_node_name(&name) {
341 return Err(Error::InvalidName(name));
342 }
343 for pname in props.keys() {
344 if !is_valid_prop_name(pname) {
345 return Err(Error::InvalidName(pname.into()));
346 }
347 }
348 Ok(Self {
349 name,
350 props,
351 subnodes,
352 })
353 }
354
355 pub(crate) fn empty(name: impl Into<String>) -> Result<Self> {
357 FdtNode::new(name.into(), [].into(), [].into())
358 }
359
360 fn read_token(input: &mut Blob) -> Result<u32> {
361 loop {
362 let value = rdu32(input)?;
363 if value != FDT_NOP {
364 return Ok(value);
365 }
366 }
367 }
368
369 fn parse_node(input: &mut Blob, strings: &FdtStrings) -> Result<Self> {
371 let name = c_str_to_string(input)
373 .ok_or_else(|| Error::FdtParseError("could not parse node name".into()))?;
374 let name_nbytes = name.len() + 1;
375 consume(input, name_nbytes + align_pad_len(name_nbytes, SIZE_U32))?;
376
377 let mut props = IndexMap::new();
379 let mut subnodes = IndexMap::new();
380 let mut encountered_subnode = false; loop {
383 match Self::read_token(input)? {
384 FDT_BEGIN_NODE => {
385 encountered_subnode = true;
386 let subnode = Self::parse_node(input, strings)?;
387 match subnodes.entry(subnode.name.clone()) {
388 Entry::Vacant(e) => e.insert(subnode),
389 Entry::Occupied(_) => return Err(Error::DuplicateNode(subnode.name)),
390 };
391 }
392 FDT_END_NODE => break,
393 FDT_PROP => {
394 if encountered_subnode {
395 return Err(Error::FdtParseError(
396 "unexpected prop token after subnode".into(),
397 ));
398 }
399 let prop_len = rdu32(input)? as usize;
400 let prop_name_offset = rdu32(input)?;
401 let prop_blob = consume(input, prop_len + align_pad_len(prop_len, SIZE_U32))?;
402 let prop_name = strings.at_offset(prop_name_offset).ok_or_else(|| {
403 Error::FdtParseError(format!(
404 "invalid property name at {prop_name_offset:#x}",
405 ))
406 })?;
407 props.insert(prop_name, prop_blob[..prop_len].to_vec());
409 }
410 FDT_NOP => continue,
411 FDT_END => return Err(Error::FdtParseError("unexpected END token".into())),
412 t => return Err(Error::FdtParseError(format!("invalid FDT token {t}"))),
413 }
414 }
415 FdtNode::new(name, props, subnodes)
416 }
417
418 fn from_blob(mut input: Blob, strings: &FdtStrings) -> Result<Self> {
420 let input = &mut input;
421 if Self::read_token(input)? != FDT_BEGIN_NODE {
422 return Err(Error::FdtParseError("expected begin node token".into()));
423 }
424 let root = Self::parse_node(input, strings)?;
425 if Self::read_token(input)? != FDT_END {
426 Err(Error::FdtParseError("expected end node token".into()))
427 } else {
428 Ok(root)
429 }
430 }
431
432 fn write_blob(&self, writer: &mut impl io::Write, strings: &mut FdtStrings) -> Result<()> {
434 writer.write_all(&FDT_BEGIN_NODE.to_be_bytes())?;
436 writer.write_all(self.name.as_bytes())?;
438 writer.write_all(&[0])?; let pad_len = align_pad_len(self.name.len() + 1, SIZE_U32);
440 writer.write_all(&vec![0; pad_len])?;
441 for (propname, propblob) in self.props.iter() {
443 writer.write_all(&FDT_PROP.to_be_bytes())?;
445 writer.write_all(&(propblob.len() as u32).to_be_bytes())?;
447 writer.write_all(&strings.intern_string(propname).to_be_bytes())?;
449 writer.write_all(propblob)?;
451 let pad_len = align_pad_len(propblob.len(), SIZE_U32);
452 writer.write_all(&vec![0; pad_len])?;
453 }
454 for subnode in self.subnodes.values() {
456 subnode.write_blob(writer, strings)?;
457 }
458 writer.write_all(&FDT_END_NODE.to_be_bytes())?;
460 Ok(())
461 }
462
463 pub(crate) fn prop_names(&self) -> impl std::iter::Iterator<Item = &str> {
465 self.props.keys().map(|s| s.as_str())
466 }
467
468 pub(crate) fn has_prop(&self, name: &str) -> bool {
470 self.props.contains_key(name)
471 }
472
473 pub fn get_prop<T>(&self, name: &str) -> Option<T>
479 where
480 T: FromFdtPropval,
481 {
482 T::from_propval(self.props.get(name)?.as_slice())
483 }
484
485 pub(crate) fn phandle_at_offset(&self, name: &str, offset: usize) -> Option<u32> {
488 let data = self.props.get(name)?;
489 data.get(offset..offset + SIZE_U32)
490 .and_then(u32::from_propval)
491 }
492
493 pub(crate) fn update_phandle_at_offset(
497 &mut self,
498 name: &str,
499 offset: usize,
500 phandle: u32,
501 ) -> Result<()> {
502 let propval = self
503 .props
504 .get_mut(name)
505 .ok_or_else(|| Error::InvalidName(format!("property {name} does not exist")))?;
506 if let Some(bytes) = propval.get_mut(offset..offset + SIZE_U32) {
507 bytes.copy_from_slice(phandle.to_propval()?.as_slice());
508 Ok(())
509 } else {
510 Err(Error::PropertyValueInvalid)
511 }
512 }
513
514 pub fn set_prop<T>(&mut self, name: &str, value: T) -> Result<()>
521 where
522 T: ToFdtPropval,
523 {
524 if !is_valid_prop_name(name) {
525 return Err(Error::InvalidName(name.into()));
526 }
527 let bytes = value.to_propval()?;
528 u32::try_from(bytes.len()).map_err(|_| Error::PropertyValueTooLarge)?;
530 self.props.insert(name.into(), bytes);
531 Ok(())
532 }
533
534 pub fn subnode(&self, name: &str) -> Option<&FdtNode> {
540 self.subnodes.get(name)
541 }
542
543 pub fn subnode_mut(&mut self, name: &str) -> Result<&mut FdtNode> {
550 if !self.subnodes.contains_key(name) {
551 self.subnodes.insert(name.into(), FdtNode::empty(name)?);
552 }
553 Ok(self.subnodes.get_mut(name).unwrap())
554 }
555
556 pub(crate) fn iter_subnodes(&self) -> impl std::iter::Iterator<Item = &FdtNode> {
558 self.subnodes.values()
559 }
560
561 pub(crate) fn iter_subnodes_mut(&mut self) -> impl std::iter::Iterator<Item = &mut FdtNode> {
563 self.subnodes.values_mut()
564 }
565}
566
567pub struct Fdt {
589 pub(crate) reserved_memory: Vec<FdtReserveEntry>,
590 pub(crate) root: FdtNode,
591 strings: FdtStrings,
592 boot_cpuid_phys: u32,
593}
594
595#[derive(Clone, PartialEq, Debug)]
600pub struct FdtReserveEntry {
601 pub address: u64,
603 pub size: u64,
605}
606
607const RESVMEM_TERMINATOR: FdtReserveEntry = FdtReserveEntry::new(0, 0);
609
610impl FdtReserveEntry {
611 pub const fn new(address: u64, size: u64) -> Self {
618 Self { address, size }
619 }
620
621 fn from_blob(input: &mut Blob) -> Result<Self> {
623 Ok(Self {
624 address: rdu64(input)?,
625 size: rdu64(input)?,
626 })
627 }
628
629 fn write_blob(&self, mut writer: impl io::Write) -> Result<()> {
631 writer.write_all(&self.address.to_be_bytes())?;
632 writer.write_all(&self.size.to_be_bytes())?;
633 Ok(())
634 }
635}
636
637impl Fdt {
638 pub fn new(mem_reservations: &[FdtReserveEntry]) -> Self {
644 Self {
645 reserved_memory: mem_reservations.to_vec(),
646 root: FdtNode::empty("").unwrap(),
647 strings: FdtStrings::default(),
648 boot_cpuid_phys: 0u32,
649 }
650 }
651
652 pub fn set_boot_cpuid_phys(&mut self, boot_cpuid_phys: u32) {
658 self.boot_cpuid_phys = boot_cpuid_phys;
659 }
660
661 fn parse_reserved_memory(mut input: Blob) -> Result<Vec<FdtReserveEntry>> {
663 let mut entries = vec![];
664 let input = &mut input;
665 loop {
666 let entry = FdtReserveEntry::from_blob(input)?;
667 if entry == RESVMEM_TERMINATOR {
668 break;
669 }
670 entries.push(entry);
671 }
672 Ok(entries)
673 }
674
675 fn write_reserved_memory(&self, mut writer: impl io::Write) -> Result<()> {
677 for entry in &self.reserved_memory {
678 entry.write_blob(&mut writer)?;
679 }
680 RESVMEM_TERMINATOR.write_blob(writer)
681 }
682
683 pub fn from_blob(input: Blob) -> Result<Self> {
689 let header = input
690 .get(..FdtHeader::SIZE)
691 .ok_or_else(|| Error::FdtParseError("cannot extract header, input too small".into()))?;
692 let header = FdtHeader::from_blob(header)?;
693 if header.total_size as usize > input.len() {
694 return Err(Error::FdtParseError("input size doesn't match".into()));
695 }
696
697 let reserved_mem_blob = &input[header.off_mem_rsvmap as usize..];
698 let nodes_blob = &input[header.off_dt_struct as usize
699 ..(header.off_dt_struct + header.size_dt_struct) as usize];
700 let strings_blob = &input[header.off_dt_strings as usize
701 ..(header.off_dt_strings + header.size_dt_strings) as usize];
702
703 let reserved_memory = Self::parse_reserved_memory(reserved_mem_blob)?;
704 let strings = FdtStrings::from_blob(strings_blob)?;
705 let root = FdtNode::from_blob(nodes_blob, &strings)?;
706
707 Ok(Self {
708 reserved_memory,
709 root,
710 strings,
711 boot_cpuid_phys: header.boot_cpuid_phys,
712 })
713 }
714
715 fn write_struct(&mut self, mut writer: impl io::Write) -> Result<()> {
717 self.root.write_blob(&mut writer, &mut self.strings)?;
718 writer.write_all(&FDT_END.to_be_bytes())?;
719 Ok(())
720 }
721
722 pub fn finish(&mut self) -> Result<Vec<u8>> {
726 let mut result = vec![0u8; FdtHeader::SIZE];
727 align_data(&mut result, SIZE_U64);
728
729 let off_mem_rsvmap = result.len();
730 self.write_reserved_memory(&mut result)?;
731 align_data(&mut result, SIZE_U64);
732
733 let off_dt_struct = result.len();
734 self.write_struct(&mut result)?;
735 align_data(&mut result, SIZE_U32);
736
737 let off_dt_strings = result.len();
738 self.strings.write_blob(&mut result)?;
739 let total_size = u32::try_from(result.len()).map_err(|_| Error::TotalSizeTooLarge)?;
740
741 let header = FdtHeader::new(
742 total_size,
743 off_dt_struct as u32,
744 off_dt_strings as u32,
745 off_mem_rsvmap as u32,
746 self.boot_cpuid_phys,
747 total_size - off_dt_strings as u32, off_dt_strings as u32 - off_dt_struct as u32, );
750 header.write_blob(&mut result[..FdtHeader::SIZE])?;
751 Ok(result)
752 }
753
754 pub fn root_mut(&mut self) -> &mut FdtNode {
756 &mut self.root
757 }
758
759 pub fn get_node<T: TryInto<Path>>(&self, path: T) -> Option<&FdtNode> {
765 let mut result_node = &self.root;
766 let path: Path = path.try_into().ok()?;
767 for node_name in path.iter() {
768 result_node = result_node.subnodes.get(node_name)?;
769 }
770 Some(result_node)
771 }
772
773 pub fn get_node_mut<T: TryInto<Path>>(&mut self, path: T) -> Option<&mut FdtNode> {
780 let mut result_node = &mut self.root;
781 let path: Path = path.try_into().ok()?;
782 for node_name in path.iter() {
783 result_node = result_node.subnodes.get_mut(node_name)?;
784 }
785 Some(result_node)
786 }
787
788 pub fn symbol_to_path(&self, symbol: &str) -> Result<Path> {
794 const SYMBOLS_NODE: &str = "__symbols__";
795 let Some(symbols_node) = self.root.subnode(SYMBOLS_NODE) else {
796 return Err(Error::InvalidPath("no symbols in fdt".into()));
797 };
798 symbols_node
799 .get_prop::<String>(symbol)
800 .ok_or_else(|| Error::InvalidName(format!("filter symbol {symbol} does not exist")))?
801 .parse()
802 }
803}
804
805#[cfg(test)]
806mod tests {
807 use super::*;
808
809 const FDT_BLOB_HEADER_ONLY: [u8; 0x48] = [
810 0xd0, 0x0d, 0xfe, 0xed, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x09, ];
829
830 const FDT_BLOB_RSVMAP: [u8; 0x68] = [
831 0xd0, 0x0d, 0xfe, 0xed, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x12, 0x34, 0x56, 0x78, 0xAA, 0xBB, 0xCC, 0xDD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x34, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x56, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x09, ];
858
859 const FDT_BLOB_STRINGS: [u8; 0x26] = [
860 b'n', b'u', b'l', b'l', 0x00, b'u', b'3', b'2', 0x00, b'u', b'6', b'4', 0x00, b's', b't',
861 b'r', 0x00, b's', b't', b'r', b'l', b's', b't', 0x00, b'a', b'r', b'r', b'u', b'3', b'2',
862 0x00, b'a', b'r', b'r', b'u', b'6', b'4', 0x00,
863 ];
864
865 const EXPECTED_STRINGS: [&str; 7] = ["null", "u32", "u64", "str", "strlst", "arru32", "arru64"];
866
867 const FDT_BLOB_NODES_ROOT_ONLY: [u8; 0x90] = [
868 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x12, 0x34, 0x56, 0x78, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x09, 0x12, 0x34, 0x56, 0x78, 0x87, 0x65, 0x43, 0x21, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0D, b'h', b'e', b'l', b'l', b'o', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x11, b'h', b'i', 0x00, b'b', b'y', b'e', 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x18, 0x12, 0x34, 0x56, 0x78, 0xAA, 0xBB, 0xCC, 0xDD, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x1f, 0x12, 0x34, 0x56, 0x78, 0x87, 0x65, 0x43, 0x21, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x09, ];
905
906 const FDT_BLOB_NESTED_NODES: [u8; 0x80] = [
914 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x57, 0x90, 0x24, 0x00, 0x00, 0x00, 0x01, b'n', b'e', b's', b't', b'e', b'd', 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x12, 0x12, 0x12, 0x12, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x18, 0x13, 0x57, 0x90, 0x24, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, b'n', b'e', b's', b't', b'e', b'd', b'2', 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x12, 0x12, 0x12, 0x12, 0x00, 0x00, 0x00, 0x01, b'n', b'e', b's', b't', b'e', b'd', b'3', 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x09, ];
947
948 #[test]
949 fn fdt_load_header() {
950 let blob: &[u8] = &FDT_BLOB_HEADER_ONLY;
951 let header = FdtHeader::from_blob(blob).unwrap();
952 assert_eq!(header.magic, FdtHeader::MAGIC);
953 assert_eq!(header.total_size, 0x48);
954 assert_eq!(header.off_dt_struct, 0x38);
955 assert_eq!(header.off_dt_strings, 0x48);
956 assert_eq!(header.off_mem_rsvmap, 0x28);
957 assert_eq!(header.version, 17);
958 assert_eq!(header.last_comp_version, 16);
959 assert_eq!(header.boot_cpuid_phys, 0);
960 assert_eq!(header.size_dt_strings, 0);
961 assert_eq!(header.size_dt_struct, 0x10);
962 }
963
964 #[test]
965 fn fdt_load_invalid_header() {
966 const HEADER: [u8; 40] = [
968 0xd0, 0x0d, 0xfe, 0xed, 0x00, 0x00, 0x00, 0xda, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0xb2, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x5a, ];
979
980 FdtHeader::from_blob(&HEADER).unwrap();
981
982 assert!(FdtHeader::from_blob(&HEADER[..FdtHeader::SIZE - 4]).is_err());
984 assert!(FdtHeader::from_blob(&[]).is_err());
985
986 let mut invalid_header = HEADER;
987 invalid_header[0x00] = 0x00; FdtHeader::from_blob(&invalid_header).expect_err("invalid magic");
989
990 let mut invalid_header = HEADER;
991 invalid_header[0x07] = 0x10; FdtHeader::from_blob(&invalid_header).expect_err("invalid totalsize");
993
994 let mut invalid_header = HEADER;
995 invalid_header[0x0b] = 0x60; FdtHeader::from_blob(&invalid_header).expect_err("dt struct overlaps with strings");
997
998 let mut invalid_header = HEADER;
999 invalid_header[0x27] = 0x5c; FdtHeader::from_blob(&invalid_header).expect_err("dt struct overlaps with strings");
1001
1002 let mut invalid_header = HEADER;
1003 invalid_header[0x13] = 0x20; FdtHeader::from_blob(&invalid_header).expect_err("reserved memory overlaps with header");
1005
1006 let mut invalid_header = HEADER;
1007 invalid_header[0x0f] = 0x50; FdtHeader::from_blob(&invalid_header).expect_err("strings start before struct");
1009
1010 let mut invalid_header = HEADER;
1011 invalid_header[0x23] = 0x50; FdtHeader::from_blob(&invalid_header).expect_err("strings go past totalsize");
1013 }
1014
1015 #[test]
1016 fn fdt_load_resv_map() {
1017 let blob: &[u8] = &FDT_BLOB_RSVMAP;
1018 let fdt = Fdt::from_blob(blob).unwrap();
1019 assert_eq!(fdt.reserved_memory.len(), 2);
1020 assert!(
1021 fdt.reserved_memory[0].address == 0x12345678AABBCCDD
1022 && fdt.reserved_memory[0].size == 0x1234
1023 );
1024 assert!(
1025 fdt.reserved_memory[1].address == 0x1020304050607080
1026 && fdt.reserved_memory[1].size == 0x5678
1027 );
1028 }
1029
1030 #[test]
1031 fn fdt_test_node_props() {
1032 let mut node = FdtNode::empty("mynode").unwrap();
1033 node.set_prop("myprop", 1u32).unwrap();
1034 assert_eq!(node.get_prop::<u32>("myprop").unwrap(), 1u32);
1035 node.set_prop("myprop", 0xabcdef9876543210u64).unwrap();
1036 assert_eq!(
1037 node.get_prop::<u64>("myprop").unwrap(),
1038 0xabcdef9876543210u64
1039 );
1040 node.set_prop("myprop", ()).unwrap();
1041 assert_eq!(node.get_prop::<Vec<u8>>("myprop").unwrap(), []);
1042 node.set_prop("myprop", vec![1u8, 2u8, 3u8]).unwrap();
1043 assert_eq!(
1044 node.get_prop::<Vec<u8>>("myprop").unwrap(),
1045 vec![1u8, 2u8, 3u8]
1046 );
1047 node.set_prop("myprop", vec![1u32, 2u32, 3u32]).unwrap();
1048 assert_eq!(
1049 node.get_prop::<Vec<u32>>("myprop").unwrap(),
1050 vec![1u32, 2u32, 3u32]
1051 );
1052 node.set_prop("myprop", vec![1u64, 2u64, 3u64]).unwrap();
1053 assert_eq!(
1054 node.get_prop::<Vec<u64>>("myprop").unwrap(),
1055 vec![1u64, 2u64, 3u64]
1056 );
1057 node.set_prop("myprop", "myval".to_string()).unwrap();
1058 assert_eq!(
1059 node.get_prop::<String>("myprop").unwrap(),
1060 "myval".to_string()
1061 );
1062 node.set_prop(
1063 "myprop",
1064 vec![
1065 "myval1".to_string(),
1066 "myval2".to_string(),
1067 "myval3".to_string(),
1068 ],
1069 )
1070 .unwrap();
1071 assert_eq!(
1072 node.get_prop::<Vec<String>>("myprop").unwrap(),
1073 vec![
1074 "myval1".to_string(),
1075 "myval2".to_string(),
1076 "myval3".to_string()
1077 ]
1078 );
1079 }
1080
1081 #[test]
1082 fn fdt_simple_use() {
1083 let mut fdt = Fdt::new(&[]);
1084 let root_node = fdt.root_mut();
1085 root_node
1086 .set_prop("compatible", "linux,dummy-virt")
1087 .unwrap();
1088 root_node.set_prop("#address-cells", 0x2u32).unwrap();
1089 root_node.set_prop("#size-cells", 0x2u32).unwrap();
1090 let chosen_node = root_node.subnode_mut("chosen").unwrap();
1091 chosen_node.set_prop("linux,pci-probe-only", 1u32).unwrap();
1092 chosen_node
1093 .set_prop("bootargs", "panic=-1 console=hvc0 root=/dev/vda")
1094 .unwrap();
1095 fdt.finish().unwrap();
1096 }
1097
1098 #[test]
1099 fn fdt_load_strings() {
1100 let blob = &FDT_BLOB_STRINGS[..];
1101 let strings = FdtStrings::from_blob(blob).unwrap();
1102 let mut offset = 0u32;
1103
1104 for s in EXPECTED_STRINGS {
1105 assert_eq!(strings.at_offset(offset).unwrap(), s);
1106 offset += strings.at_offset(offset).unwrap().len() as u32 + 1;
1107 }
1108 }
1109
1110 #[test]
1111 fn fdt_load_strings_intern() {
1112 let strings_blob = &FDT_BLOB_STRINGS[..];
1113 let mut strings = FdtStrings::from_blob(strings_blob).unwrap();
1114 assert_eq!(strings.intern_string("null"), 0);
1115 assert_eq!(strings.intern_string("strlst"), 17);
1116 assert_eq!(strings.intern_string("arru64"), 31);
1117 assert_eq!(strings.intern_string("abc"), 38);
1118 assert_eq!(strings.intern_string("def"), 42);
1119 assert_eq!(strings.intern_string("strlst"), 17);
1120 }
1121
1122 #[test]
1123 fn fdt_load_props() {
1124 const PROP_SIZES: [(&str, usize); 7] = [
1125 ("null", 0),
1126 ("u32", 4),
1127 ("u64", 8),
1128 ("str", 6),
1129 ("strlst", 7),
1130 ("arru32", 8),
1131 ("arru64", 8),
1132 ];
1133
1134 let blob: &[u8] = &FDT_BLOB_STRINGS[..];
1135 let strings = FdtStrings::from_blob(blob).unwrap();
1136 let blob: &[u8] = &FDT_BLOB_NODES_ROOT_ONLY[..];
1137 let node = FdtNode::from_blob(blob, &strings).unwrap();
1138
1139 assert_eq!(node.name, "");
1140 assert_eq!(node.subnodes.len(), 0);
1141 assert_eq!(node.props.len(), PROP_SIZES.len());
1142
1143 for (pname, s) in PROP_SIZES.into_iter() {
1144 assert_eq!(node.get_prop::<Vec<u8>>(pname).unwrap().len(), s);
1145 }
1146 }
1147
1148 #[test]
1149 fn fdt_load_nodes_nested() {
1150 let strings_blob = &FDT_BLOB_STRINGS[..];
1151 let strings = FdtStrings::from_blob(strings_blob).unwrap();
1152 let blob: &[u8] = &FDT_BLOB_NESTED_NODES[..];
1153 let root_node = FdtNode::from_blob(blob, &strings).unwrap();
1154
1155 assert_eq!(root_node.name, "");
1157 assert_eq!(root_node.subnodes.len(), 2);
1158 assert_eq!(root_node.props.len(), 1);
1159
1160 let nested_node = root_node.subnodes.get("nested").unwrap();
1162 assert_eq!(nested_node.name, "nested");
1163 assert_eq!(nested_node.subnodes.len(), 0);
1164 assert_eq!(nested_node.props.len(), 2);
1165
1166 let nested2_node = root_node.subnodes.get("nested2").unwrap();
1168 assert_eq!(nested2_node.name, "nested2");
1169 assert_eq!(nested2_node.subnodes.len(), 1);
1170 assert_eq!(nested2_node.props.len(), 1);
1171
1172 let nested3_node = nested2_node.subnodes.get("nested3").unwrap();
1174 assert_eq!(nested3_node.name, "nested3");
1175 assert_eq!(nested3_node.subnodes.len(), 0);
1176 assert_eq!(nested3_node.props.len(), 0);
1177 }
1178
1179 #[test]
1180 fn fdt_get_node() {
1181 let fdt = Fdt::new(&[]);
1182 assert!(fdt.get_node("/").is_some());
1183 assert!(fdt.get_node("/a").is_none());
1184 }
1185
1186 #[test]
1187 fn fdt_find_nested_node() {
1188 let mut fdt = Fdt::new(&[]);
1189 let node1 = fdt.root.subnode_mut("N1").unwrap();
1190 node1.subnode_mut("N1-1").unwrap();
1191 node1.subnode_mut("N1-2").unwrap();
1192 let node2 = fdt.root.subnode_mut("N2").unwrap();
1193 let node2_1 = node2.subnode_mut("N2-1").unwrap();
1194 node2_1.subnode_mut("N2-1-1").unwrap();
1195
1196 assert!(fdt.get_node("/").is_some());
1197 assert!(fdt.get_node("/N1").is_some());
1198 assert!(fdt.get_node("/N2").is_some());
1199 assert!(fdt.get_node("/N1/N1-1").is_some());
1200 assert!(fdt.get_node("/N1/N1-2").is_some());
1201 assert!(fdt.get_node("/N2/N2-1").is_some());
1202 assert!(fdt.get_node("/N2/N2-1/N2-1-1").is_some());
1203 assert!(fdt.get_node("/N2/N2-1/A").is_none());
1204 }
1205
1206 #[test]
1207 fn minimal() {
1208 let mut fdt = Fdt::new(&[]);
1209 assert_eq!(
1210 fdt.finish().unwrap(),
1211 [
1212 0xd0, 0x0d, 0xfe, 0xed, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x09, ]
1231 );
1232 }
1233
1234 #[test]
1235 fn reservemap() {
1236 let mut fdt = Fdt::new(&[
1237 FdtReserveEntry {
1238 address: 0x12345678AABBCCDD,
1239 size: 0x1234,
1240 },
1241 FdtReserveEntry {
1242 address: 0x1020304050607080,
1243 size: 0x5678,
1244 },
1245 ]);
1246 assert_eq!(
1247 fdt.finish().unwrap(),
1248 [
1249 0xd0, 0x0d, 0xfe, 0xed, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x12, 0x34, 0x56, 0x78, 0xAA, 0xBB, 0xCC, 0xDD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x34, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x56, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x09, ]
1276 );
1277 }
1278
1279 #[test]
1280 fn prop_null() {
1281 let mut fdt = Fdt::new(&[]);
1282 let root_node = fdt.root_mut();
1283 root_node.set_prop("null", ()).unwrap();
1284 assert_eq!(
1285 fdt.finish().unwrap(),
1286 [
1287 0xd0, 0x0d, 0xfe, 0xed, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x09, b'n', b'u', b'l', b'l', 0x00, ]
1310 );
1311 }
1312
1313 #[test]
1314 fn prop_u32() {
1315 let mut fdt = Fdt::new(&[]);
1316 let root_node = fdt.root_mut();
1317 root_node.set_prop("u32", 0x12345678u32).unwrap();
1318 assert_eq!(
1319 fdt.finish().unwrap(),
1320 [
1321 0xd0, 0x0d, 0xfe, 0xed, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x34, 0x56, 0x78, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x09, b'u', b'3', b'2', 0x00, ]
1345 );
1346 }
1347
1348 #[test]
1349 fn all_props() {
1350 let mut fdt = Fdt::new(&[]);
1351 let root_node = fdt.root_mut();
1352 root_node
1353 .set_prop("arru32", &[0x12345678u32, 0xAABBCCDDu32])
1354 .unwrap();
1355 root_node
1356 .set_prop("arru64", &[0x1234567887654321u64])
1357 .unwrap();
1358 root_node.set_prop("null", ()).unwrap();
1359 root_node.set_prop("str", "hello").unwrap();
1360 root_node.set_prop("strlst", &["hi", "bye"]).unwrap();
1361 root_node.set_prop("u32", 0x12345678u32).unwrap();
1362 root_node.set_prop("u64", 0x1234567887654321u64).unwrap();
1363 assert_eq!(
1364 fdt.finish().unwrap(),
1365 [
1366 0xd0, 0x0d, 0xfe, 0xed, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0xc8, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x12, 0x34, 0x56, 0x78, 0xAA, 0xBB, 0xCC, 0xDD, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x07, 0x12, 0x34, 0x56, 0x78, 0x87, 0x65, 0x43, 0x21, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x13, b'h', b'e', b'l', b'l', b'o', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x17, b'h', b'i', 0x00, b'b', b'y', b'e', 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x1E, 0x12, 0x34, 0x56, 0x78, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x22, 0x12, 0x34, 0x56, 0x78, 0x87, 0x65, 0x43, 0x21, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x09, b'a', b'r', b'r', b'u', b'3', b'2', 0x00, b'a', b'r', b'r', b'u', b'6', b'4', 0x00, b'n', b'u', b'l', b'l', 0x00, b's', b't', b'r', 0x00, b's', b't', b'r', b'l', b's', b't', 0x00, b'u', b'3', b'2', 0x00, b'u', b'6', b'4', 0x00, ]
1424 );
1425 }
1426
1427 #[test]
1428 fn node_order() {
1429 let expected: &[u8] = &[
1430 0xd0, 0x0d, 0xfe, 0xed, 0x00, 0x00, 0x00, 0x9C, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x9C, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, b'B', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, b'A', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, b'C', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, b'D', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, b'E', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, b'B', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, b'F', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x09, ];
1470
1471 let mut fdt = Fdt::new(&[]);
1472 let root = fdt.root_mut();
1473 let root_subnode_names = ["B", "A", "C"];
1474 let node_c_subnode_names = ["D", "E", "B", "F"];
1475 for n in root_subnode_names {
1476 root.subnode_mut(n).unwrap();
1477 }
1478 let node_c = root.subnode_mut("C").unwrap();
1479 for n in node_c_subnode_names {
1480 node_c.subnode_mut(n).unwrap();
1481 }
1482
1483 assert!(root
1484 .iter_subnodes()
1485 .zip(root_subnode_names)
1486 .all(|(sn, n)| sn.name == n));
1487 assert!(root
1488 .subnode("C")
1489 .unwrap()
1490 .iter_subnodes()
1491 .zip(node_c_subnode_names)
1492 .all(|(sn, n)| sn.name == n));
1493 assert_eq!(fdt.finish().unwrap(), expected);
1494 }
1495
1496 #[test]
1497 fn prop_order() {
1498 let expected: &[u8] = &[
1499 0xd0, 0x0d, 0xfe, 0xed, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x76, 0x61, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x09, b'g', b'h', b'i', 0x00, b'd', b'e', b'f', 0x00, b'a', b'b', b'c', 0x00, b'b', b'c', b'd', 0x00, ];
1538
1539 let mut fdt = Fdt::new(&[]);
1540 let root_node = fdt.root_mut();
1541 root_node.set_prop("ghi", "val").unwrap();
1542 root_node.set_prop("def", 2u32).unwrap();
1543 root_node.set_prop("abc", 1u32).unwrap();
1544 root_node.set_prop("bcd", 3u32).unwrap();
1545
1546 assert_eq!(
1547 root_node.prop_names().collect::<Vec<_>>(),
1548 ["ghi", "def", "abc", "bcd"]
1549 );
1550 assert_eq!(fdt.finish().unwrap(), expected);
1551 }
1552
1553 #[test]
1554 fn nested_nodes() {
1555 let mut fdt = Fdt::new(&[]);
1556 let root_node = fdt.root_mut();
1557 root_node.set_prop("abc", 0x13579024u32).unwrap();
1558 let nested_node = root_node.subnode_mut("nested").unwrap();
1559 nested_node.set_prop("def", 0x12121212u32).unwrap();
1560 assert_eq!(
1561 fdt.finish().unwrap(),
1562 [
1563 0xd0, 0x0d, 0xfe, 0xed, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x57, 0x90, 0x24, 0x00, 0x00, 0x00, 0x01, b'n', b'e', b's', b't', b'e', b'd', 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x12, 0x12, 0x12, 0x12, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x09, b'a', b'b', b'c', 0x00, b'd', b'e', b'f', 0x00, ]
1596 );
1597 }
1598
1599 #[test]
1600 fn prop_name_string_reuse() {
1601 let mut fdt = Fdt::new(&[]);
1602 let root_node = fdt.root_mut();
1603 root_node.set_prop("abc", 0x13579024u32).unwrap();
1604 let nested = root_node.subnode_mut("nested").unwrap();
1605 nested.set_prop("abc", 0x12121212u32).unwrap(); nested.set_prop("def", 0x12121212u32).unwrap();
1607 assert_eq!(
1608 fdt.finish().unwrap(),
1609 [
1610 0xd0, 0x0d, 0xfe, 0xed, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x57, 0x90, 0x24, 0x00, 0x00, 0x00, 0x01, b'n', b'e', b's', b't', b'e', b'd', 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x12, 0x12, 0x12, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x12, 0x12, 0x12, 0x12, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x09, b'a', b'b', b'c', 0x00, b'd', b'e', b'f', 0x00, ]
1647 );
1648 }
1649
1650 #[test]
1651 fn invalid_node_name_nul() {
1652 let mut fdt = Fdt::new(&[]);
1653 let root_node = fdt.root_mut();
1654 root_node
1655 .subnode_mut("abc\0def")
1656 .expect_err("node name with embedded NUL");
1657 }
1658
1659 #[test]
1660 fn invalid_prop_name_nul() {
1661 let mut fdt = Fdt::new(&[]);
1662 let root_node = fdt.root_mut();
1663 root_node
1664 .set_prop("abc\0def", 0u32)
1665 .expect_err("property name with embedded NUL");
1666 }
1667
1668 #[test]
1669 fn invalid_prop_string_value_nul() {
1670 let mut fdt = Fdt::new(&[]);
1671 let root_node = fdt.root_mut();
1672 root_node
1673 .set_prop("mystr", "abc\0def")
1674 .expect_err("string property value with embedded NUL");
1675 }
1676
1677 #[test]
1678 fn invalid_prop_string_list_value_nul() {
1679 let mut fdt = Fdt::new(&[]);
1680 let root_node = fdt.root_mut();
1681 let strs = ["test", "abc\0def"];
1682 root_node
1683 .set_prop("mystr", &strs)
1684 .expect_err("stringlist property value with embedded NUL");
1685 }
1686}