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