• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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::ffi::CString;
11 use std::io;
12 use std::mem::size_of;
13 
14 use remain::sorted;
15 use thiserror::Error as ThisError;
16 
17 #[sorted]
18 #[derive(ThisError, Debug)]
19 pub enum Error {
20     #[error("I/O error dumping FDT to file code={} path={}", .0, .1.display())]
21     FdtDumpIoError(io::Error, std::path::PathBuf),
22     #[error("Parse error reading FDT parameters")]
23     FdtFileParseError,
24     #[error("Error writing FDT to guest memory")]
25     FdtGuestMemoryWriteError,
26     #[error("I/O error reading FDT parameters code={0}")]
27     FdtIoError(io::Error),
28     #[error("Strings cannot contain NUL")]
29     InvalidString,
30     #[error("Attempted to end a node that was not the most recent")]
31     OutOfOrderEndNode,
32     #[error("Properties may not be added after a node has been ended")]
33     PropertyAfterEndNode,
34     #[error("Property value size must fit in 32 bits")]
35     PropertyValueTooLarge,
36     #[error("Total size must fit in 32 bits")]
37     TotalSizeTooLarge,
38     #[error("Attempted to call finish without ending all nodes")]
39     UnclosedNode,
40 }
41 
42 pub type Result<T> = std::result::Result<T, Error>;
43 
44 const FDT_HEADER_SIZE: usize = 40;
45 const FDT_VERSION: u32 = 17;
46 const FDT_LAST_COMP_VERSION: u32 = 16;
47 
48 const FDT_MAGIC: u32 = 0xd00dfeed;
49 
50 const FDT_BEGIN_NODE: u32 = 0x00000001;
51 const FDT_END_NODE: u32 = 0x00000002;
52 const FDT_PROP: u32 = 0x00000003;
53 const FDT_END: u32 = 0x00000009;
54 
55 /// Interface for writing a Flattened Devicetree (FDT) and emitting a Devicetree Blob (DTB).
56 ///
57 /// # Example
58 ///
59 /// ```rust
60 /// use cros_fdt::FdtWriter;
61 ///
62 /// # fn main() -> cros_fdt::Result<()> {
63 /// let mut fdt = FdtWriter::new(&[]);
64 /// let root_node = fdt.begin_node("")?;
65 /// fdt.property_string("compatible", "linux,dummy-virt")?;
66 /// fdt.property_u32("#address-cells", 0x2)?;
67 /// fdt.property_u32("#size-cells", 0x2)?;
68 /// let chosen_node = fdt.begin_node("chosen")?;
69 /// fdt.property_u32("linux,pci-probe-only", 1)?;
70 /// fdt.property_string("bootargs", "panic=-1 console=hvc0 root=/dev/vda")?;
71 /// fdt.end_node(chosen_node)?;
72 /// fdt.end_node(root_node)?;
73 /// let dtb = fdt.finish()?;
74 /// # Ok(())
75 /// # }
76 /// ```
77 pub struct FdtWriter {
78     data: Vec<u8>,
79     off_mem_rsvmap: u32,
80     off_dt_struct: u32,
81     strings: Vec<u8>,
82     string_offsets: BTreeMap<CString, u32>,
83     node_depth: usize,
84     node_ended: bool,
85     boot_cpuid_phys: u32,
86 }
87 
88 /// Reserved physical memory region.
89 ///
90 /// This represents an area of physical memory reserved by the firmware and unusable by the OS.
91 /// For example, this could be used to preserve bootloader code or data used at runtime.
92 pub struct FdtReserveEntry {
93     /// Physical address of the beginning of the reserved region.
94     pub address: u64,
95     /// Size of the reserved region in bytes.
96     pub size: u64,
97 }
98 
99 /// Handle to an open node created by `FdtWriter::begin_node`.
100 ///
101 /// This must be passed back to `FdtWriter::end_node` to close the nodes.
102 /// Nodes must be closed in reverse order as they were opened, matching the nesting structure
103 /// of the devicetree.
104 #[derive(Debug)]
105 pub struct FdtWriterNode {
106     depth: usize,
107 }
108 
109 impl FdtWriter {
110     /// Create a new Flattened Devicetree writer instance.
111     ///
112     /// # Arguments
113     ///
114     /// `mem_reservations` - reserved physical memory regions to list in the FDT header.
new(mem_reservations: &[FdtReserveEntry]) -> Self115     pub fn new(mem_reservations: &[FdtReserveEntry]) -> Self {
116         let data = vec![0u8; FDT_HEADER_SIZE]; // Reserve space for header.
117 
118         let mut fdt = FdtWriter {
119             data,
120             off_mem_rsvmap: 0,
121             off_dt_struct: 0,
122             strings: Vec::new(),
123             string_offsets: BTreeMap::new(),
124             node_depth: 0,
125             node_ended: false,
126             boot_cpuid_phys: 0,
127         };
128 
129         fdt.align(8);
130         fdt.off_mem_rsvmap = fdt.data.len() as u32;
131         fdt.write_mem_rsvmap(mem_reservations);
132 
133         fdt.align(4);
134         fdt.off_dt_struct = fdt.data.len() as u32;
135 
136         fdt
137     }
138 
write_mem_rsvmap(&mut self, mem_reservations: &[FdtReserveEntry])139     fn write_mem_rsvmap(&mut self, mem_reservations: &[FdtReserveEntry]) {
140         for rsv in mem_reservations {
141             self.append_u64(rsv.address);
142             self.append_u64(rsv.size);
143         }
144 
145         self.append_u64(0);
146         self.append_u64(0);
147     }
148 
149     /// Set the `boot_cpuid_phys` field of the devicetree header.
set_boot_cpuid_phys(&mut self, boot_cpuid_phys: u32)150     pub fn set_boot_cpuid_phys(&mut self, boot_cpuid_phys: u32) {
151         self.boot_cpuid_phys = boot_cpuid_phys;
152     }
153 
154     // Append `num_bytes` padding bytes (0x00).
pad(&mut self, num_bytes: usize)155     fn pad(&mut self, num_bytes: usize) {
156         self.data.extend(std::iter::repeat(0).take(num_bytes));
157     }
158 
159     // Append padding bytes (0x00) until the length of data is a multiple of `alignment`.
align(&mut self, alignment: usize)160     fn align(&mut self, alignment: usize) {
161         let offset = self.data.len() % alignment;
162         if offset != 0 {
163             self.pad(alignment - offset);
164         }
165     }
166 
167     // Rewrite the value of a big-endian u32 within data.
update_u32(&mut self, offset: usize, val: u32)168     fn update_u32(&mut self, offset: usize, val: u32) {
169         let data_slice = &mut self.data[offset..offset + 4];
170         data_slice.copy_from_slice(&val.to_be_bytes());
171     }
172 
append_u32(&mut self, val: u32)173     fn append_u32(&mut self, val: u32) {
174         self.data.extend_from_slice(&val.to_be_bytes());
175     }
176 
append_u64(&mut self, val: u64)177     fn append_u64(&mut self, val: u64) {
178         self.data.extend_from_slice(&val.to_be_bytes());
179     }
180 
181     /// Open a new FDT node.
182     ///
183     /// The node must be closed using `end_node`.
184     ///
185     /// # Arguments
186     ///
187     /// `name` - name of the node; must not contain any NUL bytes.
begin_node(&mut self, name: &str) -> Result<FdtWriterNode>188     pub fn begin_node(&mut self, name: &str) -> Result<FdtWriterNode> {
189         let name_cstr = CString::new(name).map_err(|_| Error::InvalidString)?;
190         self.append_u32(FDT_BEGIN_NODE);
191         self.data.extend(name_cstr.to_bytes_with_nul());
192         self.align(4);
193         self.node_depth += 1;
194         self.node_ended = false;
195         Ok(FdtWriterNode {
196             depth: self.node_depth,
197         })
198     }
199 
200     /// Close a node previously opened with `begin_node`.
end_node(&mut self, node: FdtWriterNode) -> Result<()>201     pub fn end_node(&mut self, node: FdtWriterNode) -> Result<()> {
202         if node.depth != self.node_depth {
203             return Err(Error::OutOfOrderEndNode);
204         }
205 
206         self.append_u32(FDT_END_NODE);
207         self.node_depth -= 1;
208         self.node_ended = true;
209         Ok(())
210     }
211 
212     // Find an existing instance of a string `s`, or add it to the strings block.
213     // Returns the offset into the strings block.
intern_string(&mut self, s: CString) -> u32214     fn intern_string(&mut self, s: CString) -> u32 {
215         if let Some(off) = self.string_offsets.get(&s) {
216             *off
217         } else {
218             let off = self.strings.len() as u32;
219             self.strings.extend_from_slice(s.to_bytes_with_nul());
220             self.string_offsets.insert(s, off);
221             off
222         }
223     }
224 
225     /// Write a property.
226     ///
227     /// # Arguments
228     ///
229     /// `name` - name of the property; must not contain any NUL bytes.
230     /// `val` - value of the property (raw byte array).
property(&mut self, name: &str, val: &[u8]) -> Result<()>231     pub fn property(&mut self, name: &str, val: &[u8]) -> Result<()> {
232         if self.node_ended {
233             return Err(Error::PropertyAfterEndNode);
234         }
235 
236         let name_cstr = CString::new(name).map_err(|_| Error::InvalidString)?;
237 
238         let len = val
239             .len()
240             .try_into()
241             .map_err(|_| Error::PropertyValueTooLarge)?;
242 
243         let nameoff = self.intern_string(name_cstr);
244         self.append_u32(FDT_PROP);
245         self.append_u32(len);
246         self.append_u32(nameoff);
247         self.data.extend_from_slice(val);
248         self.align(4);
249         Ok(())
250     }
251 
252     /// Write an empty property.
property_null(&mut self, name: &str) -> Result<()>253     pub fn property_null(&mut self, name: &str) -> Result<()> {
254         self.property(name, &[])
255     }
256 
257     /// Write a string property.
property_string(&mut self, name: &str, val: &str) -> Result<()>258     pub fn property_string(&mut self, name: &str, val: &str) -> Result<()> {
259         let cstr_value = CString::new(val).map_err(|_| Error::InvalidString)?;
260         self.property(name, cstr_value.to_bytes_with_nul())
261     }
262 
263     /// Write a stringlist property.
property_string_list(&mut self, name: &str, values: &[&str]) -> Result<()>264     pub fn property_string_list(&mut self, name: &str, values: &[&str]) -> Result<()> {
265         let mut bytes = Vec::new();
266         for &s in values {
267             let cstr = CString::new(s).map_err(|_| Error::InvalidString)?;
268             bytes.extend_from_slice(cstr.to_bytes_with_nul());
269         }
270         self.property(name, &bytes)
271     }
272 
273     /// Write a 32-bit unsigned integer property.
property_u32(&mut self, name: &str, val: u32) -> Result<()>274     pub fn property_u32(&mut self, name: &str, val: u32) -> Result<()> {
275         self.property(name, &val.to_be_bytes())
276     }
277 
278     /// Write a 64-bit unsigned integer property.
property_u64(&mut self, name: &str, val: u64) -> Result<()>279     pub fn property_u64(&mut self, name: &str, val: u64) -> Result<()> {
280         self.property(name, &val.to_be_bytes())
281     }
282 
283     /// Write a property containing an array of 32-bit unsigned integers.
property_array_u32(&mut self, name: &str, cells: &[u32]) -> Result<()>284     pub fn property_array_u32(&mut self, name: &str, cells: &[u32]) -> Result<()> {
285         let mut arr = Vec::with_capacity(cells.len() * size_of::<u32>());
286         for &c in cells {
287             arr.extend(c.to_be_bytes());
288         }
289         self.property(name, &arr)
290     }
291 
292     /// Write a property containing an array of 64-bit unsigned integers.
property_array_u64(&mut self, name: &str, cells: &[u64]) -> Result<()>293     pub fn property_array_u64(&mut self, name: &str, cells: &[u64]) -> Result<()> {
294         let mut arr = Vec::with_capacity(cells.len() * size_of::<u64>());
295         for &c in cells {
296             arr.extend(c.to_be_bytes());
297         }
298         self.property(name, &arr)
299     }
300 
301     /// Finish writing the Devicetree Blob (DTB).
302     ///
303     /// Returns the DTB as a vector of bytes, consuming the `FdtWriter`.
304     /// The DTB is always padded up to `max_size` with zeroes, so the returned
305     /// value will either be exactly `max_size` bytes long, or an error will
306     /// be returned if the DTB does not fit in `max_size` bytes.
307     ///
308     /// # Arguments
309     ///
310     /// `max_size` - Maximum size of the finished DTB in bytes.
finish(mut self) -> Result<Vec<u8>>311     pub fn finish(mut self) -> Result<Vec<u8>> {
312         if self.node_depth > 0 {
313             return Err(Error::UnclosedNode);
314         }
315 
316         self.append_u32(FDT_END);
317         let size_dt_struct = self.data.len() as u32 - self.off_dt_struct;
318 
319         let totalsize = self.data.len() + self.strings.len();
320 
321         let totalsize = totalsize.try_into().map_err(|_| Error::TotalSizeTooLarge)?;
322         let off_dt_strings = self
323             .data
324             .len()
325             .try_into()
326             .map_err(|_| Error::TotalSizeTooLarge)?;
327         let size_dt_strings = self
328             .strings
329             .len()
330             .try_into()
331             .map_err(|_| Error::TotalSizeTooLarge)?;
332 
333         // Finalize the header.
334         self.update_u32(0, FDT_MAGIC);
335         self.update_u32(1 * 4, totalsize);
336         self.update_u32(2 * 4, self.off_dt_struct);
337         self.update_u32(3 * 4, off_dt_strings);
338         self.update_u32(4 * 4, self.off_mem_rsvmap);
339         self.update_u32(5 * 4, FDT_VERSION);
340         self.update_u32(6 * 4, FDT_LAST_COMP_VERSION);
341         self.update_u32(7 * 4, self.boot_cpuid_phys);
342         self.update_u32(8 * 4, size_dt_strings);
343         self.update_u32(9 * 4, size_dt_struct);
344 
345         // Add the strings block.
346         self.data.append(&mut self.strings);
347         Ok(self.data)
348     }
349 }
350 
351 #[cfg(test)]
352 mod tests {
353     use super::*;
354 
355     #[test]
minimal()356     fn minimal() {
357         let mut fdt = FdtWriter::new(&[]);
358         let root_node = fdt.begin_node("").unwrap();
359         fdt.end_node(root_node).unwrap();
360         assert_eq!(
361             fdt.finish().unwrap(),
362             [
363                 0xd0, 0x0d, 0xfe, 0xed, // 0000: magic (0xd00dfeed)
364                 0x00, 0x00, 0x00, 0x48, // 0004: totalsize (0x48)
365                 0x00, 0x00, 0x00, 0x38, // 0008: off_dt_struct (0x38)
366                 0x00, 0x00, 0x00, 0x48, // 000C: off_dt_strings (0x48)
367                 0x00, 0x00, 0x00, 0x28, // 0010: off_mem_rsvmap (0x28)
368                 0x00, 0x00, 0x00, 0x11, // 0014: version (0x11 = 17)
369                 0x00, 0x00, 0x00, 0x10, // 0018: last_comp_version (0x10 = 16)
370                 0x00, 0x00, 0x00, 0x00, // 001C: boot_cpuid_phys (0)
371                 0x00, 0x00, 0x00, 0x00, // 0020: size_dt_strings (0)
372                 0x00, 0x00, 0x00, 0x10, // 0024: size_dt_struct (0x10)
373                 0x00, 0x00, 0x00, 0x00, // 0028: rsvmap terminator (address = 0 high)
374                 0x00, 0x00, 0x00, 0x00, // 002C: rsvmap terminator (address = 0 low)
375                 0x00, 0x00, 0x00, 0x00, // 0030: rsvmap terminator (size = 0 high)
376                 0x00, 0x00, 0x00, 0x00, // 0034: rsvmap terminator (size = 0 low)
377                 0x00, 0x00, 0x00, 0x01, // 0038: FDT_BEGIN_NODE
378                 0x00, 0x00, 0x00, 0x00, // 003C: node name ("") + padding
379                 0x00, 0x00, 0x00, 0x02, // 0040: FDT_END_NODE
380                 0x00, 0x00, 0x00, 0x09, // 0044: FDT_END
381             ]
382         );
383     }
384 
385     #[test]
reservemap()386     fn reservemap() {
387         let mut fdt = FdtWriter::new(&[
388             FdtReserveEntry {
389                 address: 0x12345678AABBCCDD,
390                 size: 0x1234,
391             },
392             FdtReserveEntry {
393                 address: 0x1020304050607080,
394                 size: 0x5678,
395             },
396         ]);
397         let root_node = fdt.begin_node("").unwrap();
398         fdt.end_node(root_node).unwrap();
399         assert_eq!(
400             fdt.finish().unwrap(),
401             [
402                 0xd0, 0x0d, 0xfe, 0xed, // 0000: magic (0xd00dfeed)
403                 0x00, 0x00, 0x00, 0x68, // 0004: totalsize (0x68)
404                 0x00, 0x00, 0x00, 0x58, // 0008: off_dt_struct (0x58)
405                 0x00, 0x00, 0x00, 0x68, // 000C: off_dt_strings (0x68)
406                 0x00, 0x00, 0x00, 0x28, // 0010: off_mem_rsvmap (0x28)
407                 0x00, 0x00, 0x00, 0x11, // 0014: version (0x11 = 17)
408                 0x00, 0x00, 0x00, 0x10, // 0018: last_comp_version (0x10 = 16)
409                 0x00, 0x00, 0x00, 0x00, // 001C: boot_cpuid_phys (0)
410                 0x00, 0x00, 0x00, 0x00, // 0020: size_dt_strings (0)
411                 0x00, 0x00, 0x00, 0x10, // 0024: size_dt_struct (0x10)
412                 0x12, 0x34, 0x56, 0x78, // 0028: rsvmap entry 0 address high
413                 0xAA, 0xBB, 0xCC, 0xDD, // 002C: rsvmap entry 0 address low
414                 0x00, 0x00, 0x00, 0x00, // 0030: rsvmap entry 0 size high
415                 0x00, 0x00, 0x12, 0x34, // 0034: rsvmap entry 0 size low
416                 0x10, 0x20, 0x30, 0x40, // 0038: rsvmap entry 1 address high
417                 0x50, 0x60, 0x70, 0x80, // 003C: rsvmap entry 1 address low
418                 0x00, 0x00, 0x00, 0x00, // 0040: rsvmap entry 1 size high
419                 0x00, 0x00, 0x56, 0x78, // 0044: rsvmap entry 1 size low
420                 0x00, 0x00, 0x00, 0x00, // 0048: rsvmap terminator (address = 0 high)
421                 0x00, 0x00, 0x00, 0x00, // 004C: rsvmap terminator (address = 0 low)
422                 0x00, 0x00, 0x00, 0x00, // 0050: rsvmap terminator (size = 0 high)
423                 0x00, 0x00, 0x00, 0x00, // 0054: rsvmap terminator (size = 0 low)
424                 0x00, 0x00, 0x00, 0x01, // 0058: FDT_BEGIN_NODE
425                 0x00, 0x00, 0x00, 0x00, // 005C: node name ("") + padding
426                 0x00, 0x00, 0x00, 0x02, // 0060: FDT_END_NODE
427                 0x00, 0x00, 0x00, 0x09, // 0064: FDT_END
428             ]
429         );
430     }
431 
432     #[test]
prop_null()433     fn prop_null() {
434         let mut fdt = FdtWriter::new(&[]);
435         let root_node = fdt.begin_node("").unwrap();
436         fdt.property_null("null").unwrap();
437         fdt.end_node(root_node).unwrap();
438         assert_eq!(
439             fdt.finish().unwrap(),
440             [
441                 0xd0, 0x0d, 0xfe, 0xed, // 0000: magic (0xd00dfeed)
442                 0x00, 0x00, 0x00, 0x59, // 0004: totalsize (0x59)
443                 0x00, 0x00, 0x00, 0x38, // 0008: off_dt_struct (0x38)
444                 0x00, 0x00, 0x00, 0x54, // 000C: off_dt_strings (0x54)
445                 0x00, 0x00, 0x00, 0x28, // 0010: off_mem_rsvmap (0x28)
446                 0x00, 0x00, 0x00, 0x11, // 0014: version (0x11 = 17)
447                 0x00, 0x00, 0x00, 0x10, // 0018: last_comp_version (0x10 = 16)
448                 0x00, 0x00, 0x00, 0x00, // 001C: boot_cpuid_phys (0)
449                 0x00, 0x00, 0x00, 0x05, // 0020: size_dt_strings (0x05)
450                 0x00, 0x00, 0x00, 0x1c, // 0024: size_dt_struct (0x1C)
451                 0x00, 0x00, 0x00, 0x00, // 0028: rsvmap terminator (address = 0 high)
452                 0x00, 0x00, 0x00, 0x00, // 002C: rsvmap terminator (address = 0 low)
453                 0x00, 0x00, 0x00, 0x00, // 0030: rsvmap terminator (size = 0 high)
454                 0x00, 0x00, 0x00, 0x00, // 0034: rsvmap terminator (size = 0 low)
455                 0x00, 0x00, 0x00, 0x01, // 0038: FDT_BEGIN_NODE
456                 0x00, 0x00, 0x00, 0x00, // 003C: node name ("") + padding
457                 0x00, 0x00, 0x00, 0x03, // 0040: FDT_PROP
458                 0x00, 0x00, 0x00, 0x00, // 0044: prop len (0)
459                 0x00, 0x00, 0x00, 0x00, // 0048: prop nameoff (0)
460                 0x00, 0x00, 0x00, 0x02, // 004C: FDT_END_NODE
461                 0x00, 0x00, 0x00, 0x09, // 0050: FDT_END
462                 b'n', b'u', b'l', b'l', 0x00, // 0054: strings block
463             ]
464         );
465     }
466 
467     #[test]
prop_u32()468     fn prop_u32() {
469         let mut fdt = FdtWriter::new(&[]);
470         let root_node = fdt.begin_node("").unwrap();
471         fdt.property_u32("u32", 0x12345678).unwrap();
472         fdt.end_node(root_node).unwrap();
473         assert_eq!(
474             fdt.finish().unwrap(),
475             [
476                 0xd0, 0x0d, 0xfe, 0xed, // 0000: magic (0xd00dfeed)
477                 0x00, 0x00, 0x00, 0x5c, // 0004: totalsize (0x5C)
478                 0x00, 0x00, 0x00, 0x38, // 0008: off_dt_struct (0x38)
479                 0x00, 0x00, 0x00, 0x58, // 000C: off_dt_strings (0x58)
480                 0x00, 0x00, 0x00, 0x28, // 0010: off_mem_rsvmap (0x28)
481                 0x00, 0x00, 0x00, 0x11, // 0014: version (0x11 = 17)
482                 0x00, 0x00, 0x00, 0x10, // 0018: last_comp_version (0x10 = 16)
483                 0x00, 0x00, 0x00, 0x00, // 001C: boot_cpuid_phys (0)
484                 0x00, 0x00, 0x00, 0x04, // 0020: size_dt_strings (0x04)
485                 0x00, 0x00, 0x00, 0x20, // 0024: size_dt_struct (0x20)
486                 0x00, 0x00, 0x00, 0x00, // 0028: rsvmap terminator (address = 0 high)
487                 0x00, 0x00, 0x00, 0x00, // 002C: rsvmap terminator (address = 0 low)
488                 0x00, 0x00, 0x00, 0x00, // 0030: rsvmap terminator (size = 0 high)
489                 0x00, 0x00, 0x00, 0x00, // 0034: rsvmap terminator (size = 0 low)
490                 0x00, 0x00, 0x00, 0x01, // 0038: FDT_BEGIN_NODE
491                 0x00, 0x00, 0x00, 0x00, // 003C: node name ("") + padding
492                 0x00, 0x00, 0x00, 0x03, // 0040: FDT_PROP
493                 0x00, 0x00, 0x00, 0x04, // 0044: prop len (4)
494                 0x00, 0x00, 0x00, 0x00, // 0048: prop nameoff (0)
495                 0x12, 0x34, 0x56, 0x78, // 004C: prop u32 value (0x12345678)
496                 0x00, 0x00, 0x00, 0x02, // 0050: FDT_END_NODE
497                 0x00, 0x00, 0x00, 0x09, // 0054: FDT_END
498                 b'u', b'3', b'2', 0x00, // 0058: strings block
499             ]
500         );
501     }
502 
503     #[test]
all_props()504     fn all_props() {
505         let mut fdt = FdtWriter::new(&[]);
506         let root_node = fdt.begin_node("").unwrap();
507         fdt.property_null("null").unwrap();
508         fdt.property_u32("u32", 0x12345678).unwrap();
509         fdt.property_u64("u64", 0x1234567887654321).unwrap();
510         fdt.property_string("str", "hello").unwrap();
511         fdt.property_string_list("strlst", &["hi", "bye"]).unwrap();
512         fdt.property_array_u32("arru32", &[0x12345678, 0xAABBCCDD])
513             .unwrap();
514         fdt.property_array_u64("arru64", &[0x1234567887654321])
515             .unwrap();
516         fdt.end_node(root_node).unwrap();
517         assert_eq!(
518             fdt.finish().unwrap(),
519             [
520                 0xd0, 0x0d, 0xfe, 0xed, // 0000: magic (0xd00dfeed)
521                 0x00, 0x00, 0x00, 0xee, // 0004: totalsize (0xEE)
522                 0x00, 0x00, 0x00, 0x38, // 0008: off_dt_struct (0x38)
523                 0x00, 0x00, 0x00, 0xc8, // 000C: off_dt_strings (0xC8)
524                 0x00, 0x00, 0x00, 0x28, // 0010: off_mem_rsvmap (0x28)
525                 0x00, 0x00, 0x00, 0x11, // 0014: version (0x11 = 17)
526                 0x00, 0x00, 0x00, 0x10, // 0018: last_comp_version (0x10 = 16)
527                 0x00, 0x00, 0x00, 0x00, // 001C: boot_cpuid_phys (0)
528                 0x00, 0x00, 0x00, 0x26, // 0020: size_dt_strings (0x26)
529                 0x00, 0x00, 0x00, 0x90, // 0024: size_dt_struct (0x90)
530                 0x00, 0x00, 0x00, 0x00, // 0028: rsvmap terminator (address = 0 high)
531                 0x00, 0x00, 0x00, 0x00, // 002C: rsvmap terminator (address = 0 low)
532                 0x00, 0x00, 0x00, 0x00, // 0030: rsvmap terminator (size = 0 high)
533                 0x00, 0x00, 0x00, 0x00, // 0034: rsvmap terminator (size = 0 low)
534                 0x00, 0x00, 0x00, 0x01, // 0038: FDT_BEGIN_NODE
535                 0x00, 0x00, 0x00, 0x00, // 003C: node name ("") + padding
536                 0x00, 0x00, 0x00, 0x03, // 0040: FDT_PROP (null)
537                 0x00, 0x00, 0x00, 0x00, // 0044: prop len (0)
538                 0x00, 0x00, 0x00, 0x00, // 0048: prop nameoff (0)
539                 0x00, 0x00, 0x00, 0x03, // 004C: FDT_PROP (u32)
540                 0x00, 0x00, 0x00, 0x04, // 0050: prop len (4)
541                 0x00, 0x00, 0x00, 0x05, // 0054: prop nameoff (0x05)
542                 0x12, 0x34, 0x56, 0x78, // 0058: prop u32 value (0x12345678)
543                 0x00, 0x00, 0x00, 0x03, // 005C: FDT_PROP (u64)
544                 0x00, 0x00, 0x00, 0x08, // 0060: prop len (8)
545                 0x00, 0x00, 0x00, 0x09, // 0064: prop nameoff (0x09)
546                 0x12, 0x34, 0x56, 0x78, // 0068: prop u64 value high (0x12345678)
547                 0x87, 0x65, 0x43, 0x21, // 006C: prop u64 value low (0x87654321)
548                 0x00, 0x00, 0x00, 0x03, // 0070: FDT_PROP (string)
549                 0x00, 0x00, 0x00, 0x06, // 0074: prop len (6)
550                 0x00, 0x00, 0x00, 0x0D, // 0078: prop nameoff (0x0D)
551                 b'h', b'e', b'l', b'l', // 007C: prop str value ("hello") + padding
552                 b'o', 0x00, 0x00, 0x00, // 0080: "o\0" + padding
553                 0x00, 0x00, 0x00, 0x03, // 0084: FDT_PROP (string list)
554                 0x00, 0x00, 0x00, 0x07, // 0088: prop len (7)
555                 0x00, 0x00, 0x00, 0x11, // 008C: prop nameoff (0x11)
556                 b'h', b'i', 0x00, b'b', // 0090: prop value ("hi", "bye")
557                 b'y', b'e', 0x00, 0x00, // 0094: "ye\0" + padding
558                 0x00, 0x00, 0x00, 0x03, // 0098: FDT_PROP (u32 array)
559                 0x00, 0x00, 0x00, 0x08, // 009C: prop len (8)
560                 0x00, 0x00, 0x00, 0x18, // 00A0: prop nameoff (0x18)
561                 0x12, 0x34, 0x56, 0x78, // 00A4: prop value 0
562                 0xAA, 0xBB, 0xCC, 0xDD, // 00A8: prop value 1
563                 0x00, 0x00, 0x00, 0x03, // 00AC: FDT_PROP (u64 array)
564                 0x00, 0x00, 0x00, 0x08, // 00B0: prop len (8)
565                 0x00, 0x00, 0x00, 0x1f, // 00B4: prop nameoff (0x1F)
566                 0x12, 0x34, 0x56, 0x78, // 00B8: prop u64 value 0 high
567                 0x87, 0x65, 0x43, 0x21, // 00BC: prop u64 value 0 low
568                 0x00, 0x00, 0x00, 0x02, // 00C0: FDT_END_NODE
569                 0x00, 0x00, 0x00, 0x09, // 00C4: FDT_END
570                 b'n', b'u', b'l', b'l', 0x00, // 00C8: strings + 0x00: "null""
571                 b'u', b'3', b'2', 0x00, // 00CD: strings + 0x05: "u32"
572                 b'u', b'6', b'4', 0x00, // 00D1: strings + 0x09: "u64"
573                 b's', b't', b'r', 0x00, // 00D5: strings + 0x0D: "str"
574                 b's', b't', b'r', b'l', b's', b't', 0x00, // 00D9: strings + 0x11: "strlst"
575                 b'a', b'r', b'r', b'u', b'3', b'2', 0x00, // 00E0: strings + 0x18: "arru32"
576                 b'a', b'r', b'r', b'u', b'6', b'4', 0x00, // 00E7: strings + 0x1F: "arru64"
577             ]
578         );
579     }
580 
581     #[test]
nested_nodes()582     fn nested_nodes() {
583         let mut fdt = FdtWriter::new(&[]);
584         let root_node = fdt.begin_node("").unwrap();
585         fdt.property_u32("abc", 0x13579024).unwrap();
586         let nested_node = fdt.begin_node("nested").unwrap();
587         fdt.property_u32("def", 0x12121212).unwrap();
588         fdt.end_node(nested_node).unwrap();
589         fdt.end_node(root_node).unwrap();
590         assert_eq!(
591             fdt.finish().unwrap(),
592             [
593                 0xd0, 0x0d, 0xfe, 0xed, // 0000: magic (0xd00dfeed)
594                 0x00, 0x00, 0x00, 0x80, // 0004: totalsize (0x80)
595                 0x00, 0x00, 0x00, 0x38, // 0008: off_dt_struct (0x38)
596                 0x00, 0x00, 0x00, 0x78, // 000C: off_dt_strings (0x78)
597                 0x00, 0x00, 0x00, 0x28, // 0010: off_mem_rsvmap (0x28)
598                 0x00, 0x00, 0x00, 0x11, // 0014: version (0x11 = 17)
599                 0x00, 0x00, 0x00, 0x10, // 0018: last_comp_version (0x10 = 16)
600                 0x00, 0x00, 0x00, 0x00, // 001C: boot_cpuid_phys (0)
601                 0x00, 0x00, 0x00, 0x08, // 0020: size_dt_strings (0x08)
602                 0x00, 0x00, 0x00, 0x40, // 0024: size_dt_struct (0x40)
603                 0x00, 0x00, 0x00, 0x00, // 0028: rsvmap terminator (address = 0 high)
604                 0x00, 0x00, 0x00, 0x00, // 002C: rsvmap terminator (address = 0 low)
605                 0x00, 0x00, 0x00, 0x00, // 0030: rsvmap terminator (size = 0 high)
606                 0x00, 0x00, 0x00, 0x00, // 0034: rsvmap terminator (size = 0 low)
607                 0x00, 0x00, 0x00, 0x01, // 0038: FDT_BEGIN_NODE
608                 0x00, 0x00, 0x00, 0x00, // 003C: node name ("") + padding
609                 0x00, 0x00, 0x00, 0x03, // 0040: FDT_PROP
610                 0x00, 0x00, 0x00, 0x04, // 0044: prop len (4)
611                 0x00, 0x00, 0x00, 0x00, // 0048: prop nameoff (0x00)
612                 0x13, 0x57, 0x90, 0x24, // 004C: prop u32 value (0x13579024)
613                 0x00, 0x00, 0x00, 0x01, // 0050: FDT_BEGIN_NODE
614                 b'n', b'e', b's', b't', // 0054: Node name ("nested")
615                 b'e', b'd', 0x00, 0x00, // 0058: "ed\0" + pad
616                 0x00, 0x00, 0x00, 0x03, // 005C: FDT_PROP
617                 0x00, 0x00, 0x00, 0x04, // 0060: prop len (4)
618                 0x00, 0x00, 0x00, 0x04, // 0064: prop nameoff (0x04)
619                 0x12, 0x12, 0x12, 0x12, // 0068: prop u32 value (0x12121212)
620                 0x00, 0x00, 0x00, 0x02, // 006C: FDT_END_NODE ("nested")
621                 0x00, 0x00, 0x00, 0x02, // 0070: FDT_END_NODE ("")
622                 0x00, 0x00, 0x00, 0x09, // 0074: FDT_END
623                 b'a', b'b', b'c', 0x00, // 0078: strings + 0x00: "abc"
624                 b'd', b'e', b'f', 0x00, // 007C: strings + 0x04: "def"
625             ]
626         );
627     }
628 
629     #[test]
prop_name_string_reuse()630     fn prop_name_string_reuse() {
631         let mut fdt = FdtWriter::new(&[]);
632         let root_node = fdt.begin_node("").unwrap();
633         fdt.property_u32("abc", 0x13579024).unwrap();
634         let nested_node = fdt.begin_node("nested").unwrap();
635         fdt.property_u32("def", 0x12121212).unwrap();
636         fdt.property_u32("abc", 0x12121212).unwrap(); // This should reuse the "abc" string.
637         fdt.end_node(nested_node).unwrap();
638         fdt.end_node(root_node).unwrap();
639         assert_eq!(
640             fdt.finish().unwrap(),
641             [
642                 0xd0, 0x0d, 0xfe, 0xed, // 0000: magic (0xd00dfeed)
643                 0x00, 0x00, 0x00, 0x90, // 0004: totalsize (0x90)
644                 0x00, 0x00, 0x00, 0x38, // 0008: off_dt_struct (0x38)
645                 0x00, 0x00, 0x00, 0x88, // 000C: off_dt_strings (0x88)
646                 0x00, 0x00, 0x00, 0x28, // 0010: off_mem_rsvmap (0x28)
647                 0x00, 0x00, 0x00, 0x11, // 0014: version (0x11 = 17)
648                 0x00, 0x00, 0x00, 0x10, // 0018: last_comp_version (0x10 = 16)
649                 0x00, 0x00, 0x00, 0x00, // 001C: boot_cpuid_phys (0)
650                 0x00, 0x00, 0x00, 0x08, // 0020: size_dt_strings (0x08)
651                 0x00, 0x00, 0x00, 0x50, // 0024: size_dt_struct (0x50)
652                 0x00, 0x00, 0x00, 0x00, // 0028: rsvmap terminator (address = 0 high)
653                 0x00, 0x00, 0x00, 0x00, // 002C: rsvmap terminator (address = 0 low)
654                 0x00, 0x00, 0x00, 0x00, // 0030: rsvmap terminator (size = 0 high)
655                 0x00, 0x00, 0x00, 0x00, // 0034: rsvmap terminator (size = 0 low)
656                 0x00, 0x00, 0x00, 0x01, // 0038: FDT_BEGIN_NODE
657                 0x00, 0x00, 0x00, 0x00, // 003C: node name ("") + padding
658                 0x00, 0x00, 0x00, 0x03, // 0040: FDT_PROP
659                 0x00, 0x00, 0x00, 0x04, // 0044: prop len (4)
660                 0x00, 0x00, 0x00, 0x00, // 0048: prop nameoff (0x00)
661                 0x13, 0x57, 0x90, 0x24, // 004C: prop u32 value (0x13579024)
662                 0x00, 0x00, 0x00, 0x01, // 0050: FDT_BEGIN_NODE
663                 b'n', b'e', b's', b't', // 0054: Node name ("nested")
664                 b'e', b'd', 0x00, 0x00, // 0058: "ed\0" + pad
665                 0x00, 0x00, 0x00, 0x03, // 005C: FDT_PROP
666                 0x00, 0x00, 0x00, 0x04, // 0060: prop len (4)
667                 0x00, 0x00, 0x00, 0x04, // 0064: prop nameoff (0x04)
668                 0x12, 0x12, 0x12, 0x12, // 0068: prop u32 value (0x12121212)
669                 0x00, 0x00, 0x00, 0x03, // 006C: FDT_PROP
670                 0x00, 0x00, 0x00, 0x04, // 0070: prop len (4)
671                 0x00, 0x00, 0x00, 0x00, // 0074: prop nameoff (0x00 - reuse)
672                 0x12, 0x12, 0x12, 0x12, // 0078: prop u32 value (0x12121212)
673                 0x00, 0x00, 0x00, 0x02, // 007C: FDT_END_NODE ("nested")
674                 0x00, 0x00, 0x00, 0x02, // 0080: FDT_END_NODE ("")
675                 0x00, 0x00, 0x00, 0x09, // 0084: FDT_END
676                 b'a', b'b', b'c', 0x00, // 0088: strings + 0x00: "abc"
677                 b'd', b'e', b'f', 0x00, // 008C: strings + 0x04: "def"
678             ]
679         );
680     }
681 
682     #[test]
invalid_node_name_nul()683     fn invalid_node_name_nul() {
684         let mut fdt = FdtWriter::new(&[]);
685         fdt.begin_node("abc\0def")
686             .expect_err("node name with embedded NUL");
687     }
688 
689     #[test]
invalid_prop_name_nul()690     fn invalid_prop_name_nul() {
691         let mut fdt = FdtWriter::new(&[]);
692         fdt.property_u32("abc\0def", 0)
693             .expect_err("property name with embedded NUL");
694     }
695 
696     #[test]
invalid_prop_string_value_nul()697     fn invalid_prop_string_value_nul() {
698         let mut fdt = FdtWriter::new(&[]);
699         fdt.property_string("mystr", "abc\0def")
700             .expect_err("string property value with embedded NUL");
701     }
702 
703     #[test]
invalid_prop_string_list_value_nul()704     fn invalid_prop_string_list_value_nul() {
705         let mut fdt = FdtWriter::new(&[]);
706         let strs = ["test", "abc\0def"];
707         fdt.property_string_list("mystr", &strs)
708             .expect_err("stringlist property value with embedded NUL");
709     }
710 
711     #[test]
invalid_prop_after_end_node()712     fn invalid_prop_after_end_node() {
713         let mut fdt = FdtWriter::new(&[]);
714         let _root_node = fdt.begin_node("").unwrap();
715         fdt.property_u32("ok_prop", 1234).unwrap();
716         let nested_node = fdt.begin_node("mynode").unwrap();
717         fdt.property_u32("ok_nested_prop", 5678).unwrap();
718         fdt.end_node(nested_node).unwrap();
719         fdt.property_u32("bad_prop_after_end_node", 1357)
720             .expect_err("property after end_node");
721     }
722 
723     #[test]
invalid_end_node_out_of_order()724     fn invalid_end_node_out_of_order() {
725         let mut fdt = FdtWriter::new(&[]);
726         let root_node = fdt.begin_node("").unwrap();
727         fdt.property_u32("ok_prop", 1234).unwrap();
728         let _nested_node = fdt.begin_node("mynode").unwrap();
729         fdt.end_node(root_node)
730             .expect_err("end node while nested node is open");
731     }
732 
733     #[test]
invalid_finish_while_node_open()734     fn invalid_finish_while_node_open() {
735         let mut fdt = FdtWriter::new(&[]);
736         let _root_node = fdt.begin_node("").unwrap();
737         fdt.property_u32("ok_prop", 1234).unwrap();
738         let _nested_node = fdt.begin_node("mynode").unwrap();
739         fdt.property_u32("ok_nested_prop", 5678).unwrap();
740         fdt.finish().expect_err("finish without ending all nodes");
741     }
742 }
743