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