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