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