Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
google
GitHub Repository: google/crosvm
Path: blob/main/cros_fdt/src/fdt.rs
5392 views
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::io;
11
12
use indexmap::map::Entry;
13
use indexmap::IndexMap;
14
use remain::sorted;
15
use thiserror::Error as ThisError;
16
17
use crate::path::Path;
18
use crate::propval::FromFdtPropval;
19
use crate::propval::ToFdtPropval;
20
21
pub(crate) const SIZE_U32: usize = std::mem::size_of::<u32>();
22
pub(crate) const SIZE_U64: usize = std::mem::size_of::<u64>();
23
24
#[sorted]
25
#[derive(ThisError, Debug)]
26
pub enum Error {
27
#[error("Error applying device tree overlay: {}", .0)]
28
ApplyOverlayError(String),
29
#[error("Binary size must fit in 32 bits")]
30
BinarySizeTooLarge,
31
#[error("Duplicate node {}", .0)]
32
DuplicateNode(String),
33
#[error("I/O error dumping FDT to file code={} path={}", .0, .1.display())]
34
FdtDumpIoError(io::Error, std::path::PathBuf),
35
#[error("Error writing FDT to guest memory")]
36
FdtGuestMemoryWriteError,
37
#[error("I/O error code={0}")]
38
FdtIoError(io::Error),
39
#[error("Parse error reading FDT parameters: {}", .0)]
40
FdtParseError(String),
41
#[error("Error applying FDT tree filter: {}", .0)]
42
FilterError(String),
43
#[error("Invalid name string: {}", .0)]
44
InvalidName(String),
45
#[error("Invalid path: {}", .0)]
46
InvalidPath(String),
47
#[error("Invalid string value {}", .0)]
48
InvalidString(String),
49
#[error("Expected phandle value for IOMMU of type: {}, id: {:?}", .0, .1)]
50
MissingIommuPhandle(String, Option<u32>),
51
#[error("Expected power domain: offset={}", .0)]
52
MissingPowerDomain(usize),
53
#[error("Property value is not valid")]
54
PropertyValueInvalid,
55
#[error("Property value size must fit in 32 bits")]
56
PropertyValueTooLarge,
57
#[error("Total size must fit in 32 bits")]
58
TotalSizeTooLarge,
59
}
60
61
impl From<io::Error> for Error {
62
fn from(value: io::Error) -> Self {
63
Self::FdtIoError(value)
64
}
65
}
66
67
pub type Result<T> = std::result::Result<T, Error>;
68
type Blob<'a> = &'a [u8];
69
70
const FDT_BEGIN_NODE: u32 = 0x00000001;
71
const FDT_END_NODE: u32 = 0x00000002;
72
const FDT_PROP: u32 = 0x00000003;
73
const FDT_NOP: u32 = 0x00000004;
74
const FDT_END: u32 = 0x00000009;
75
76
// Consume and return `n` bytes from the beginning of a slice.
77
fn consume<'a>(bytes: &mut &'a [u8], n: usize) -> Result<&'a [u8]> {
78
let mid = n;
79
if mid > bytes.len() {
80
Err(Error::PropertyValueInvalid)
81
} else {
82
let (data_bytes, rest) = bytes.split_at(n);
83
*(bytes) = rest;
84
Ok(data_bytes)
85
}
86
}
87
88
// Consume a u32 from a byte slice.
89
#[inline]
90
fn rdu32(data: &mut Blob) -> Result<u32> {
91
Ok(u32::from_be_bytes(
92
// Unwrap won't panic because the slice length is checked in consume().
93
consume(data, SIZE_U32)?.try_into().unwrap(),
94
))
95
}
96
97
// Consume a u64 from a byte slice.
98
#[inline]
99
fn rdu64(data: &mut Blob) -> Result<u64> {
100
Ok(u64::from_be_bytes(
101
// Unwrap won't panic because the slice length is checked in consume().
102
consume(data, SIZE_U64)?.try_into().unwrap(),
103
))
104
}
105
106
// Return the number of padding bytes required to align `size` to `alignment`.
107
#[inline]
108
fn align_pad_len(size: usize, alignment: usize) -> usize {
109
(alignment - size % alignment) % alignment
110
}
111
112
// Pad a byte vector to given alignment.
113
#[inline]
114
fn align_data(data: &mut Vec<u8>, alignment: usize) {
115
data.resize(align_pad_len(data.len(), alignment) + data.len(), 0u8);
116
}
117
118
// Construct a string from the start of a byte slice until the first null byte.
119
pub(crate) fn c_str_to_string(input: Blob) -> Option<String> {
120
let size = input.iter().position(|&v| v == 0u8)?;
121
String::from_utf8(input[..size].to_vec()).ok()
122
}
123
124
// Verify FDT property name.
125
fn is_valid_prop_name(name: &str) -> bool {
126
const ALLOWED_SPECIAL_CHARS: [u8; 7] = [b'.', b',', b'_', b'+', b'?', b'#', b'-'];
127
name.bytes()
128
.all(|c| c.is_ascii_alphanumeric() || ALLOWED_SPECIAL_CHARS.contains(&c))
129
}
130
131
// Verify FDT node name.
132
fn is_valid_node_name(name: &str) -> bool {
133
const ALLOWED_SPECIAL_CHARS: [u8; 6] = [b'.', b',', b'_', b'+', b'-', b'@'];
134
const ADDR_SEP: u8 = b'@';
135
// At most one `@` separating node-name and unit-address
136
if name.bytes().filter(|&c| c == ADDR_SEP).count() > 1 {
137
return false;
138
}
139
name.bytes()
140
.all(|c| c.is_ascii_alphanumeric() || ALLOWED_SPECIAL_CHARS.contains(&c))
141
}
142
143
// An implementation of FDT header.
144
#[derive(Default, Debug)]
145
struct FdtHeader {
146
magic: u32, // magic word
147
total_size: u32, // total size of DT block
148
off_dt_struct: u32, // offset to structure
149
off_dt_strings: u32, // offset to strings
150
off_mem_rsvmap: u32, // offset to memory reserve map
151
version: u32, // format version
152
last_comp_version: u32, // last compatible version
153
boot_cpuid_phys: u32, // Which physical CPU id we're booting on
154
size_dt_strings: u32, // size of the strings block
155
size_dt_struct: u32, // size of the structure block
156
}
157
158
impl FdtHeader {
159
const MAGIC: u32 = 0xd00dfeed;
160
const VERSION: u32 = 17;
161
const LAST_COMP_VERSION: u32 = 16;
162
const SIZE: usize = 10 * SIZE_U32;
163
164
// Create a new FdtHeader instance.
165
fn new(
166
total_size: u32,
167
off_dt_struct: u32,
168
off_dt_strings: u32,
169
off_mem_rsvmap: u32,
170
boot_cpuid_phys: u32,
171
size_dt_strings: u32,
172
size_dt_struct: u32,
173
) -> Self {
174
Self {
175
magic: Self::MAGIC,
176
total_size,
177
off_dt_struct,
178
off_dt_strings,
179
off_mem_rsvmap,
180
version: Self::VERSION,
181
last_comp_version: Self::LAST_COMP_VERSION,
182
boot_cpuid_phys,
183
size_dt_strings,
184
size_dt_struct,
185
}
186
}
187
188
// Dump FDT header to a byte vector.
189
fn write_blob(&self, buffer: &mut [u8]) -> Result<()> {
190
assert_eq!(buffer.len(), Self::SIZE);
191
for (chunk, val_u32) in buffer.chunks_exact_mut(SIZE_U32).zip(&[
192
self.magic,
193
self.total_size,
194
self.off_dt_struct,
195
self.off_dt_strings,
196
self.off_mem_rsvmap,
197
self.version,
198
self.last_comp_version,
199
self.boot_cpuid_phys,
200
self.size_dt_strings,
201
self.size_dt_struct,
202
]) {
203
chunk.copy_from_slice(&val_u32.to_be_bytes());
204
}
205
Ok(())
206
}
207
208
// Load FDT header from a byte slice.
209
fn from_blob(mut input: Blob) -> Result<Self> {
210
if input.len() < Self::SIZE {
211
return Err(Error::FdtParseError("invalid binary size".into()));
212
}
213
let input = &mut input;
214
let header = Self {
215
magic: rdu32(input)?,
216
total_size: rdu32(input)?,
217
off_dt_struct: rdu32(input)?,
218
off_dt_strings: rdu32(input)?,
219
off_mem_rsvmap: rdu32(input)?,
220
version: rdu32(input)?,
221
last_comp_version: rdu32(input)?,
222
boot_cpuid_phys: rdu32(input)?,
223
size_dt_strings: rdu32(input)?,
224
size_dt_struct: rdu32(input)?,
225
};
226
if header.magic != Self::MAGIC {
227
return Err(Error::FdtParseError("invalid header magic".into()));
228
}
229
if header.version < Self::VERSION {
230
return Err(Error::FdtParseError("unsupported FDT version".into()));
231
}
232
if header.off_mem_rsvmap >= header.off_dt_strings
233
|| header.off_mem_rsvmap < FdtHeader::SIZE as u32
234
{
235
return Err(Error::FdtParseError(
236
"invalid reserved memory offset".into(),
237
));
238
}
239
240
let off_dt_struct_end = header
241
.off_dt_struct
242
.checked_add(header.size_dt_struct)
243
.ok_or_else(|| Error::FdtParseError("struct end offset must fit in 32 bits".into()))?;
244
if off_dt_struct_end > header.off_dt_strings {
245
return Err(Error::FdtParseError("struct and strings overlap".into()));
246
}
247
248
let off_dt_strings_end = header
249
.off_dt_strings
250
.checked_add(header.size_dt_strings)
251
.ok_or_else(|| Error::FdtParseError("strings end offset must fit in 32 bits".into()))?;
252
if off_dt_strings_end > header.total_size {
253
return Err(Error::FdtParseError("strings data past total size".into()));
254
}
255
256
Ok(header)
257
}
258
}
259
260
// An implementation of FDT strings block (property names)
261
#[derive(Default)]
262
struct FdtStrings {
263
strings: Vec<u8>,
264
string_offsets: BTreeMap<String, u32>,
265
}
266
267
impl FdtStrings {
268
// Load the strings block from a byte slice.
269
fn from_blob(input: Blob) -> Result<Self> {
270
if input.last().is_some_and(|i| *i != 0) {
271
return Err(Error::FdtParseError(
272
"strings block missing null terminator".into(),
273
));
274
}
275
let mut string_offsets = BTreeMap::new();
276
let mut offset = 0u32;
277
for bytes in input.split(|&x| x == 0u8) {
278
if bytes.is_empty() {
279
break;
280
}
281
let string = String::from_utf8(bytes.to_vec())
282
.map_err(|_| Error::FdtParseError("invalid value in strings block".into()))?;
283
string_offsets.insert(string, offset);
284
offset += u32::try_from(bytes.len() + 1).map_err(|_| Error::BinarySizeTooLarge)?;
285
}
286
Ok(Self {
287
strings: input.to_vec(),
288
string_offsets,
289
})
290
}
291
292
// Find an existing instance of a string `s`, or add it to the strings block.
293
// Returns the offset into the strings block.
294
fn intern_string(&mut self, s: &str) -> u32 {
295
if let Some(off) = self.string_offsets.get(s) {
296
*off
297
} else {
298
let off = self.strings.len() as u32;
299
self.strings.extend_from_slice(s.as_bytes());
300
self.strings.push(0u8);
301
self.string_offsets.insert(s.to_owned(), off);
302
off
303
}
304
}
305
306
// Write the strings blob to a `Write` object.
307
fn write_blob(&self, mut writer: impl io::Write) -> Result<()> {
308
Ok(writer.write_all(&self.strings)?)
309
}
310
311
// Return the string at given offset or `None` if such a string doesn't exist.
312
fn at_offset(&self, off: u32) -> Option<String> {
313
self.strings
314
.get(off as usize..)
315
.and_then(c_str_to_string)
316
.filter(|s| !s.is_empty())
317
}
318
}
319
320
/// Flattened device tree node.
321
///
322
/// This represents a single node from the FDT structure block. Every node may contain properties
323
/// and other (child) nodes.
324
#[derive(Debug, Clone)]
325
pub struct FdtNode {
326
/// Node name
327
pub(crate) name: String,
328
pub(crate) props: IndexMap<String, Vec<u8>>,
329
pub(crate) subnodes: IndexMap<String, FdtNode>,
330
}
331
332
impl FdtNode {
333
// Create a new node with the given name, properties, and child nodes. Return an error if
334
// node or property names do not satisfy devicetree naming criteria.
335
pub(crate) fn new(
336
name: String,
337
props: IndexMap<String, Vec<u8>>,
338
subnodes: IndexMap<String, FdtNode>,
339
) -> Result<Self> {
340
if !is_valid_node_name(&name) {
341
return Err(Error::InvalidName(name));
342
}
343
for pname in props.keys() {
344
if !is_valid_prop_name(pname) {
345
return Err(Error::InvalidName(pname.into()));
346
}
347
}
348
Ok(Self {
349
name,
350
props,
351
subnodes,
352
})
353
}
354
355
// Create an empty node with the given name.
356
pub(crate) fn empty(name: impl Into<String>) -> Result<Self> {
357
FdtNode::new(name.into(), [].into(), [].into())
358
}
359
360
fn read_token(input: &mut Blob) -> Result<u32> {
361
loop {
362
let value = rdu32(input)?;
363
if value != FDT_NOP {
364
return Ok(value);
365
}
366
}
367
}
368
369
// Parse binary content of an FDT node.
370
fn parse_node(input: &mut Blob, strings: &FdtStrings) -> Result<Self> {
371
// Node name
372
let name = c_str_to_string(input)
373
.ok_or_else(|| Error::FdtParseError("could not parse node name".into()))?;
374
let name_nbytes = name.len() + 1;
375
consume(input, name_nbytes + align_pad_len(name_nbytes, SIZE_U32))?;
376
377
// Node properties and subnodes
378
let mut props = IndexMap::new();
379
let mut subnodes = IndexMap::new();
380
let mut encountered_subnode = false; // Properties must appear before subnodes
381
382
loop {
383
match Self::read_token(input)? {
384
FDT_BEGIN_NODE => {
385
encountered_subnode = true;
386
let subnode = Self::parse_node(input, strings)?;
387
match subnodes.entry(subnode.name.clone()) {
388
Entry::Vacant(e) => e.insert(subnode),
389
Entry::Occupied(_) => return Err(Error::DuplicateNode(subnode.name)),
390
};
391
}
392
FDT_END_NODE => break,
393
FDT_PROP => {
394
if encountered_subnode {
395
return Err(Error::FdtParseError(
396
"unexpected prop token after subnode".into(),
397
));
398
}
399
let prop_len = rdu32(input)? as usize;
400
let prop_name_offset = rdu32(input)?;
401
let prop_blob = consume(input, prop_len + align_pad_len(prop_len, SIZE_U32))?;
402
let prop_name = strings.at_offset(prop_name_offset).ok_or_else(|| {
403
Error::FdtParseError(format!(
404
"invalid property name at {prop_name_offset:#x}",
405
))
406
})?;
407
// Keep the original (non-aligned) size as property value
408
props.insert(prop_name, prop_blob[..prop_len].to_vec());
409
}
410
FDT_NOP => continue,
411
FDT_END => return Err(Error::FdtParseError("unexpected END token".into())),
412
t => return Err(Error::FdtParseError(format!("invalid FDT token {t}"))),
413
}
414
}
415
FdtNode::new(name, props, subnodes)
416
}
417
418
// Load an `FdtNode` instance from a slice of bytes.
419
fn from_blob(mut input: Blob, strings: &FdtStrings) -> Result<Self> {
420
let input = &mut input;
421
if Self::read_token(input)? != FDT_BEGIN_NODE {
422
return Err(Error::FdtParseError("expected begin node token".into()));
423
}
424
let root = Self::parse_node(input, strings)?;
425
if Self::read_token(input)? != FDT_END {
426
Err(Error::FdtParseError("expected end node token".into()))
427
} else {
428
Ok(root)
429
}
430
}
431
432
// Write binary contents of a node to a vector of bytes.
433
fn write_blob(&self, writer: &mut impl io::Write, strings: &mut FdtStrings) -> Result<()> {
434
// Token
435
writer.write_all(&FDT_BEGIN_NODE.to_be_bytes())?;
436
// Name
437
writer.write_all(self.name.as_bytes())?;
438
writer.write_all(&[0])?; // Node name terminator
439
let pad_len = align_pad_len(self.name.len() + 1, SIZE_U32);
440
writer.write_all(&vec![0; pad_len])?;
441
// Properties
442
for (propname, propblob) in self.props.iter() {
443
// Prop token
444
writer.write_all(&FDT_PROP.to_be_bytes())?;
445
// Prop size
446
writer.write_all(&(propblob.len() as u32).to_be_bytes())?;
447
// Prop name offset
448
writer.write_all(&strings.intern_string(propname).to_be_bytes())?;
449
// Prop value
450
writer.write_all(propblob)?;
451
let pad_len = align_pad_len(propblob.len(), SIZE_U32);
452
writer.write_all(&vec![0; pad_len])?;
453
}
454
// Subnodes
455
for subnode in self.subnodes.values() {
456
subnode.write_blob(writer, strings)?;
457
}
458
// Token
459
writer.write_all(&FDT_END_NODE.to_be_bytes())?;
460
Ok(())
461
}
462
463
// Iterate over property names defined for this node.
464
pub(crate) fn prop_names(&self) -> impl std::iter::Iterator<Item = &str> {
465
self.props.keys().map(|s| s.as_str())
466
}
467
468
// Return true if a property with the given name exists.
469
pub(crate) fn has_prop(&self, name: &str) -> bool {
470
self.props.contains_key(name)
471
}
472
473
/// Read property value if it exists.
474
///
475
/// # Arguments
476
///
477
/// `name` - name of the property.
478
pub fn get_prop<T>(&self, name: &str) -> Option<T>
479
where
480
T: FromFdtPropval,
481
{
482
T::from_propval(self.props.get(name)?.as_slice())
483
}
484
485
// Read a phandle value (a `u32`) at some offset within a property value.
486
// Returns `None` if a phandle value cannot be constructed.
487
pub(crate) fn phandle_at_offset(&self, name: &str, offset: usize) -> Option<u32> {
488
let data = self.props.get(name)?;
489
data.get(offset..offset + SIZE_U32)
490
.and_then(u32::from_propval)
491
}
492
493
// Overwrite a phandle value (a `u32`) at some offset within a property value.
494
// Returns `Err` if the property doesn't exist, or if the property value is too short to
495
// construct a `u32` at given offset. Does not change property value size.
496
pub(crate) fn update_phandle_at_offset(
497
&mut self,
498
name: &str,
499
offset: usize,
500
phandle: u32,
501
) -> Result<()> {
502
let propval = self
503
.props
504
.get_mut(name)
505
.ok_or_else(|| Error::InvalidName(format!("property {name} does not exist")))?;
506
if let Some(bytes) = propval.get_mut(offset..offset + SIZE_U32) {
507
bytes.copy_from_slice(phandle.to_propval()?.as_slice());
508
Ok(())
509
} else {
510
Err(Error::PropertyValueInvalid)
511
}
512
}
513
514
/// Write a property.
515
///
516
/// # Arguments
517
///
518
/// `name` - name of the property; must be a valid property name according to DT spec.
519
/// `val` - value of the property (raw byte array).
520
pub fn set_prop<T>(&mut self, name: &str, value: T) -> Result<()>
521
where
522
T: ToFdtPropval,
523
{
524
if !is_valid_prop_name(name) {
525
return Err(Error::InvalidName(name.into()));
526
}
527
let bytes = value.to_propval()?;
528
// FDT property byte size must fit into a u32.
529
u32::try_from(bytes.len()).map_err(|_| Error::PropertyValueTooLarge)?;
530
self.props.insert(name.into(), bytes);
531
Ok(())
532
}
533
534
/// Return a reference to an existing subnode with given name, or `None` if it doesn't exist.
535
///
536
/// # Arguments
537
///
538
/// `name` - name of the node.
539
pub fn subnode(&self, name: &str) -> Option<&FdtNode> {
540
self.subnodes.get(name)
541
}
542
543
/// Create a node if it doesn't already exist, and return a mutable reference to it. Return
544
/// an error if the node name is not valid.
545
///
546
/// # Arguments
547
///
548
/// `name` - name of the node; must be a valid node name according to DT specification.
549
pub fn subnode_mut(&mut self, name: &str) -> Result<&mut FdtNode> {
550
if !self.subnodes.contains_key(name) {
551
self.subnodes.insert(name.into(), FdtNode::empty(name)?);
552
}
553
Ok(self.subnodes.get_mut(name).unwrap())
554
}
555
556
// Iterate subnode references.
557
pub(crate) fn iter_subnodes(&self) -> impl std::iter::Iterator<Item = &FdtNode> {
558
self.subnodes.values()
559
}
560
561
// Iterate mutable subnode references.
562
pub(crate) fn iter_subnodes_mut(&mut self) -> impl std::iter::Iterator<Item = &mut FdtNode> {
563
self.subnodes.values_mut()
564
}
565
}
566
567
/// Interface for creating and manipulating a Flattened Devicetree (FDT) and emitting
568
/// a Devicetree Blob (DTB).
569
///
570
/// # Example
571
///
572
/// ```rust
573
/// use cros_fdt::Fdt;
574
///
575
/// # fn main() -> cros_fdt::Result<()> {
576
/// let mut fdt = Fdt::new(&[]);
577
/// let root_node = fdt.root_mut();
578
/// root_node.set_prop("compatible", "linux,dummy-virt")?;
579
/// root_node.set_prop("#address-cells", 0x2u32)?;
580
/// root_node.set_prop("#size-cells", 0x2u32)?;
581
/// let chosen_node = root_node.subnode_mut("chosen")?;
582
/// chosen_node.set_prop("linux,pci-probe-only", 1u32)?;
583
/// chosen_node.set_prop("bootargs", "panic=-1 console=hvc0 root=/dev/vda")?;
584
/// let dtb = fdt.finish().unwrap();
585
/// # Ok(())
586
/// # }
587
/// ```
588
pub struct Fdt {
589
pub(crate) reserved_memory: Vec<FdtReserveEntry>,
590
pub(crate) root: FdtNode,
591
strings: FdtStrings,
592
boot_cpuid_phys: u32,
593
}
594
595
/// Reserved physical memory region.
596
///
597
/// This represents an area of physical memory reserved by the firmware and unusable by the OS.
598
/// For example, this could be used to preserve bootloader code or data used at runtime.
599
#[derive(Clone, PartialEq, Debug)]
600
pub struct FdtReserveEntry {
601
/// Physical address of the beginning of the reserved region.
602
pub address: u64,
603
/// Size of the reserved region in bytes.
604
pub size: u64,
605
}
606
607
// Last entry in the reserved memory section
608
const RESVMEM_TERMINATOR: FdtReserveEntry = FdtReserveEntry::new(0, 0);
609
610
impl FdtReserveEntry {
611
/// Create a new FdtReserveEntry
612
///
613
/// # Arguments
614
///
615
/// `address` - start of reserved memory region.
616
/// `size` - size of reserved memory region.
617
pub const fn new(address: u64, size: u64) -> Self {
618
Self { address, size }
619
}
620
621
// Load a reserved memory entry from a byte slice.
622
fn from_blob(input: &mut Blob) -> Result<Self> {
623
Ok(Self {
624
address: rdu64(input)?,
625
size: rdu64(input)?,
626
})
627
}
628
629
// Dump the entry as a vector of bytes.
630
fn write_blob(&self, mut writer: impl io::Write) -> Result<()> {
631
writer.write_all(&self.address.to_be_bytes())?;
632
writer.write_all(&self.size.to_be_bytes())?;
633
Ok(())
634
}
635
}
636
637
impl Fdt {
638
/// Create a new flattened device tree instance with an initialized root node.
639
///
640
/// # Arguments
641
///
642
/// `mem_reservations` - reserved physical memory regions to list in the FDT header.
643
pub fn new(mem_reservations: &[FdtReserveEntry]) -> Self {
644
Self {
645
reserved_memory: mem_reservations.to_vec(),
646
root: FdtNode::empty("").unwrap(),
647
strings: FdtStrings::default(),
648
boot_cpuid_phys: 0u32,
649
}
650
}
651
652
/// Set the `boot_cpuid_phys` field of the devicetree header.
653
///
654
/// # Arguments
655
///
656
/// `boot_cpuid_phys` - CPU ID
657
pub fn set_boot_cpuid_phys(&mut self, boot_cpuid_phys: u32) {
658
self.boot_cpuid_phys = boot_cpuid_phys;
659
}
660
661
// Parse the reserved memory block from a binary blob.
662
fn parse_reserved_memory(mut input: Blob) -> Result<Vec<FdtReserveEntry>> {
663
let mut entries = vec![];
664
let input = &mut input;
665
loop {
666
let entry = FdtReserveEntry::from_blob(input)?;
667
if entry == RESVMEM_TERMINATOR {
668
break;
669
}
670
entries.push(entry);
671
}
672
Ok(entries)
673
}
674
675
// Write the reserved memory block to a buffer.
676
fn write_reserved_memory(&self, mut writer: impl io::Write) -> Result<()> {
677
for entry in &self.reserved_memory {
678
entry.write_blob(&mut writer)?;
679
}
680
RESVMEM_TERMINATOR.write_blob(writer)
681
}
682
683
/// Load a flattened device tree from a byte slice.
684
///
685
/// # Arguments
686
///
687
/// `input` - byte slice from which to load the FDT.
688
pub fn from_blob(input: Blob) -> Result<Self> {
689
let header = input
690
.get(..FdtHeader::SIZE)
691
.ok_or_else(|| Error::FdtParseError("cannot extract header, input too small".into()))?;
692
let header = FdtHeader::from_blob(header)?;
693
if header.total_size as usize > input.len() {
694
return Err(Error::FdtParseError("input size doesn't match".into()));
695
}
696
697
let reserved_mem_blob = &input[header.off_mem_rsvmap as usize..];
698
let nodes_blob = &input[header.off_dt_struct as usize
699
..(header.off_dt_struct + header.size_dt_struct) as usize];
700
let strings_blob = &input[header.off_dt_strings as usize
701
..(header.off_dt_strings + header.size_dt_strings) as usize];
702
703
let reserved_memory = Self::parse_reserved_memory(reserved_mem_blob)?;
704
let strings = FdtStrings::from_blob(strings_blob)?;
705
let root = FdtNode::from_blob(nodes_blob, &strings)?;
706
707
Ok(Self {
708
reserved_memory,
709
root,
710
strings,
711
boot_cpuid_phys: header.boot_cpuid_phys,
712
})
713
}
714
715
// Write the structure block of the FDT
716
fn write_struct(&mut self, mut writer: impl io::Write) -> Result<()> {
717
self.root.write_blob(&mut writer, &mut self.strings)?;
718
writer.write_all(&FDT_END.to_be_bytes())?;
719
Ok(())
720
}
721
722
/// Finish writing the Devicetree Blob (DTB).
723
///
724
/// Returns the DTB as a vector of bytes.
725
pub fn finish(&mut self) -> Result<Vec<u8>> {
726
let mut result = vec![0u8; FdtHeader::SIZE];
727
align_data(&mut result, SIZE_U64);
728
729
let off_mem_rsvmap = result.len();
730
self.write_reserved_memory(&mut result)?;
731
align_data(&mut result, SIZE_U64);
732
733
let off_dt_struct = result.len();
734
self.write_struct(&mut result)?;
735
align_data(&mut result, SIZE_U32);
736
737
let off_dt_strings = result.len();
738
self.strings.write_blob(&mut result)?;
739
let total_size = u32::try_from(result.len()).map_err(|_| Error::TotalSizeTooLarge)?;
740
741
let header = FdtHeader::new(
742
total_size,
743
off_dt_struct as u32,
744
off_dt_strings as u32,
745
off_mem_rsvmap as u32,
746
self.boot_cpuid_phys,
747
total_size - off_dt_strings as u32, // strings size
748
off_dt_strings as u32 - off_dt_struct as u32, // struct size
749
);
750
header.write_blob(&mut result[..FdtHeader::SIZE])?;
751
Ok(result)
752
}
753
754
/// Return a mutable reference to the root node of the FDT.
755
pub fn root_mut(&mut self) -> &mut FdtNode {
756
&mut self.root
757
}
758
759
/// Return a reference to the node the path points to, or `None` if it doesn't exist.
760
///
761
/// # Arguments
762
///
763
/// `path` - device tree path of the target node.
764
pub fn get_node<T: TryInto<Path>>(&self, path: T) -> Option<&FdtNode> {
765
let mut result_node = &self.root;
766
let path: Path = path.try_into().ok()?;
767
for node_name in path.iter() {
768
result_node = result_node.subnodes.get(node_name)?;
769
}
770
Some(result_node)
771
}
772
773
/// Return a mutable reference to the node the path points to, or `None` if it
774
/// doesn't exist.
775
///
776
/// # Arguments
777
///
778
/// `path` - device tree path of the target node.
779
pub fn get_node_mut<T: TryInto<Path>>(&mut self, path: T) -> Option<&mut FdtNode> {
780
let mut result_node = &mut self.root;
781
let path: Path = path.try_into().ok()?;
782
for node_name in path.iter() {
783
result_node = result_node.subnodes.get_mut(node_name)?;
784
}
785
Some(result_node)
786
}
787
788
/// Find a device tree path to the symbol exported by the FDT. The symbol must be a node label.
789
///
790
/// # Arguments
791
///
792
/// `symbol` - symbol to search for.
793
pub fn symbol_to_path(&self, symbol: &str) -> Result<Path> {
794
const SYMBOLS_NODE: &str = "__symbols__";
795
let Some(symbols_node) = self.root.subnode(SYMBOLS_NODE) else {
796
return Err(Error::InvalidPath("no symbols in fdt".into()));
797
};
798
symbols_node
799
.get_prop::<String>(symbol)
800
.ok_or_else(|| Error::InvalidName(format!("filter symbol {symbol} does not exist")))?
801
.parse()
802
}
803
}
804
805
#[cfg(test)]
806
mod tests {
807
use super::*;
808
809
const FDT_BLOB_HEADER_ONLY: [u8; 0x48] = [
810
0xd0, 0x0d, 0xfe, 0xed, // 0000: magic (0xd00dfeed)
811
0x00, 0x00, 0x00, 0x48, // 0004: totalsize (0x48)
812
0x00, 0x00, 0x00, 0x38, // 0008: off_dt_struct (0x38)
813
0x00, 0x00, 0x00, 0x48, // 000C: off_dt_strings (0x48)
814
0x00, 0x00, 0x00, 0x28, // 0010: off_mem_rsvmap (0x28)
815
0x00, 0x00, 0x00, 0x11, // 0014: version (0x11 = 17)
816
0x00, 0x00, 0x00, 0x10, // 0018: last_comp_version (0x10 = 16)
817
0x00, 0x00, 0x00, 0x00, // 001C: boot_cpuid_phys (0)
818
0x00, 0x00, 0x00, 0x00, // 0020: size_dt_strings (0)
819
0x00, 0x00, 0x00, 0x10, // 0024: size_dt_struct (0x10)
820
0x00, 0x00, 0x00, 0x00, // 0028: rsvmap terminator (address = 0 high)
821
0x00, 0x00, 0x00, 0x00, // 002C: rsvmap terminator (address = 0 low)
822
0x00, 0x00, 0x00, 0x00, // 0030: rsvmap terminator (size = 0 high)
823
0x00, 0x00, 0x00, 0x00, // 0034: rsvmap terminator (size = 0 low)
824
0x00, 0x00, 0x00, 0x01, // 0038: FDT_BEGIN_NODE
825
0x00, 0x00, 0x00, 0x00, // 003C: node name ("") + padding
826
0x00, 0x00, 0x00, 0x02, // 0040: FDT_END_NODE
827
0x00, 0x00, 0x00, 0x09, // 0044: FDT_END
828
];
829
830
const FDT_BLOB_RSVMAP: [u8; 0x68] = [
831
0xd0, 0x0d, 0xfe, 0xed, // 0000: magic (0xd00dfeed)
832
0x00, 0x00, 0x00, 0x68, // 0004: totalsize (0x68)
833
0x00, 0x00, 0x00, 0x58, // 0008: off_dt_struct (0x58)
834
0x00, 0x00, 0x00, 0x68, // 000C: off_dt_strings (0x68)
835
0x00, 0x00, 0x00, 0x28, // 0010: off_mem_rsvmap (0x28)
836
0x00, 0x00, 0x00, 0x11, // 0014: version (0x11 = 17)
837
0x00, 0x00, 0x00, 0x10, // 0018: last_comp_version (0x10 = 16)
838
0x00, 0x00, 0x00, 0x00, // 001C: boot_cpuid_phys (0)
839
0x00, 0x00, 0x00, 0x00, // 0020: size_dt_strings (0)
840
0x00, 0x00, 0x00, 0x10, // 0024: size_dt_struct (0x10)
841
0x12, 0x34, 0x56, 0x78, // 0028: rsvmap entry 0 address high
842
0xAA, 0xBB, 0xCC, 0xDD, // 002C: rsvmap entry 0 address low
843
0x00, 0x00, 0x00, 0x00, // 0030: rsvmap entry 0 size high
844
0x00, 0x00, 0x12, 0x34, // 0034: rsvmap entry 0 size low
845
0x10, 0x20, 0x30, 0x40, // 0038: rsvmap entry 1 address high
846
0x50, 0x60, 0x70, 0x80, // 003C: rsvmap entry 1 address low
847
0x00, 0x00, 0x00, 0x00, // 0040: rsvmap entry 1 size high
848
0x00, 0x00, 0x56, 0x78, // 0044: rsvmap entry 1 size low
849
0x00, 0x00, 0x00, 0x00, // 0048: rsvmap terminator (address = 0 high)
850
0x00, 0x00, 0x00, 0x00, // 004C: rsvmap terminator (address = 0 low)
851
0x00, 0x00, 0x00, 0x00, // 0050: rsvmap terminator (size = 0 high)
852
0x00, 0x00, 0x00, 0x00, // 0054: rsvmap terminator (size = 0 low)
853
0x00, 0x00, 0x00, 0x01, // 0058: FDT_BEGIN_NODE
854
0x00, 0x00, 0x00, 0x00, // 005C: node name ("") + padding
855
0x00, 0x00, 0x00, 0x02, // 0060: FDT_END_NODE
856
0x00, 0x00, 0x00, 0x09, // 0064: FDT_END
857
];
858
859
const FDT_BLOB_STRINGS: [u8; 0x26] = [
860
b'n', b'u', b'l', b'l', 0x00, b'u', b'3', b'2', 0x00, b'u', b'6', b'4', 0x00, b's', b't',
861
b'r', 0x00, b's', b't', b'r', b'l', b's', b't', 0x00, b'a', b'r', b'r', b'u', b'3', b'2',
862
0x00, b'a', b'r', b'r', b'u', b'6', b'4', 0x00,
863
];
864
865
const EXPECTED_STRINGS: [&str; 7] = ["null", "u32", "u64", "str", "strlst", "arru32", "arru64"];
866
867
const FDT_BLOB_NODES_ROOT_ONLY: [u8; 0x90] = [
868
0x00, 0x00, 0x00, 0x01, // FDT_BEGIN_NODE
869
0x00, 0x00, 0x00, 0x00, // node name ("") + padding
870
0x00, 0x00, 0x00, 0x03, // FDT_PROP (null)
871
0x00, 0x00, 0x00, 0x00, // prop len (0)
872
0x00, 0x00, 0x00, 0x00, // prop nameoff (0)
873
0x00, 0x00, 0x00, 0x03, // FDT_PROP (u32)
874
0x00, 0x00, 0x00, 0x04, // prop len (4)
875
0x00, 0x00, 0x00, 0x05, // prop nameoff (0x05)
876
0x12, 0x34, 0x56, 0x78, // prop u32 value (0x12345678)
877
0x00, 0x00, 0x00, 0x03, // FDT_PROP (u64)
878
0x00, 0x00, 0x00, 0x08, // prop len (8)
879
0x00, 0x00, 0x00, 0x09, // prop nameoff (0x09)
880
0x12, 0x34, 0x56, 0x78, // prop u64 value high (0x12345678)
881
0x87, 0x65, 0x43, 0x21, // prop u64 value low (0x87654321)
882
0x00, 0x00, 0x00, 0x03, // FDT_PROP (string)
883
0x00, 0x00, 0x00, 0x06, // prop len (6)
884
0x00, 0x00, 0x00, 0x0D, // prop nameoff (0x0D)
885
b'h', b'e', b'l', b'l', // prop str value ("hello") + padding
886
b'o', 0x00, 0x00, 0x00, // "o\0" + padding
887
0x00, 0x00, 0x00, 0x03, // FDT_PROP (string list)
888
0x00, 0x00, 0x00, 0x07, // prop len (7)
889
0x00, 0x00, 0x00, 0x11, // prop nameoff (0x11)
890
b'h', b'i', 0x00, b'b', // prop value ("hi", "bye")
891
b'y', b'e', 0x00, 0x00, // "ye\0" + padding
892
0x00, 0x00, 0x00, 0x03, // FDT_PROP (u32 array)
893
0x00, 0x00, 0x00, 0x08, // prop len (8)
894
0x00, 0x00, 0x00, 0x18, // prop nameoff (0x18)
895
0x12, 0x34, 0x56, 0x78, // prop value 0
896
0xAA, 0xBB, 0xCC, 0xDD, // prop value 1
897
0x00, 0x00, 0x00, 0x03, // FDT_PROP (u64 array)
898
0x00, 0x00, 0x00, 0x08, // prop len (8)
899
0x00, 0x00, 0x00, 0x1f, // prop nameoff (0x1F)
900
0x12, 0x34, 0x56, 0x78, // prop u64 value 0 high
901
0x87, 0x65, 0x43, 0x21, // prop u64 value 0 low
902
0x00, 0x00, 0x00, 0x02, // FDT_END_NODE
903
0x00, 0x00, 0x00, 0x09, // FDT_END
904
];
905
906
/*
907
Node structure:
908
/
909
|- nested
910
|- nested2
911
|- nested3
912
*/
913
const FDT_BLOB_NESTED_NODES: [u8; 0x80] = [
914
0x00, 0x00, 0x00, 0x01, // FDT_BEGIN_NODE
915
0x00, 0x00, 0x00, 0x00, // node name ("") + padding
916
0x00, 0x00, 0x00, 0x03, // FDT_PROP
917
0x00, 0x00, 0x00, 0x04, // prop len (4)
918
0x00, 0x00, 0x00, 0x00, // prop nameoff (0x00)
919
0x13, 0x57, 0x90, 0x24, // prop u32 value (0x13579024)
920
0x00, 0x00, 0x00, 0x01, // FDT_BEGIN_NODE
921
b'n', b'e', b's', b't', // Node name ("nested")
922
b'e', b'd', 0x00, 0x00, // "ed\0" + pad
923
0x00, 0x00, 0x00, 0x03, // FDT_PROP
924
0x00, 0x00, 0x00, 0x04, // prop len (4)
925
0x00, 0x00, 0x00, 0x05, // prop nameoff (0x05)
926
0x12, 0x12, 0x12, 0x12, // prop u32 value (0x12121212)
927
0x00, 0x00, 0x00, 0x03, // FDT_PROP
928
0x00, 0x00, 0x00, 0x04, // prop len (4)
929
0x00, 0x00, 0x00, 0x18, // prop nameoff (0x18)
930
0x13, 0x57, 0x90, 0x24, // prop u32 value (0x13579024)
931
0x00, 0x00, 0x00, 0x02, // FDT_END_NODE ("nested")
932
0x00, 0x00, 0x00, 0x01, // FDT_BEGIN_NODE
933
b'n', b'e', b's', b't', // Node name ("nested2")
934
b'e', b'd', b'2', 0x00, // "ed2\0"
935
0x00, 0x00, 0x00, 0x03, // FDT_PROP
936
0x00, 0x00, 0x00, 0x04, // prop len (0)
937
0x00, 0x00, 0x00, 0x05, // prop nameoff (0x05)
938
0x12, 0x12, 0x12, 0x12, // prop u32 value (0x12121212)
939
0x00, 0x00, 0x00, 0x01, // FDT_BEGIN_NODE
940
b'n', b'e', b's', b't', // Node name ("nested3")
941
b'e', b'd', b'3', 0x00, // "ed3\0"
942
0x00, 0x00, 0x00, 0x02, // FDT_END_NODE ("nested3")
943
0x00, 0x00, 0x00, 0x02, // FDT_END_NODE ("nested2")
944
0x00, 0x00, 0x00, 0x02, // FDT_END_NODE ("")
945
0x00, 0x00, 0x00, 0x09, // FDT_END
946
];
947
948
#[test]
949
fn fdt_load_header() {
950
let blob: &[u8] = &FDT_BLOB_HEADER_ONLY;
951
let header = FdtHeader::from_blob(blob).unwrap();
952
assert_eq!(header.magic, FdtHeader::MAGIC);
953
assert_eq!(header.total_size, 0x48);
954
assert_eq!(header.off_dt_struct, 0x38);
955
assert_eq!(header.off_dt_strings, 0x48);
956
assert_eq!(header.off_mem_rsvmap, 0x28);
957
assert_eq!(header.version, 17);
958
assert_eq!(header.last_comp_version, 16);
959
assert_eq!(header.boot_cpuid_phys, 0);
960
assert_eq!(header.size_dt_strings, 0);
961
assert_eq!(header.size_dt_struct, 0x10);
962
}
963
964
#[test]
965
fn fdt_load_invalid_header() {
966
// HEADER is valid
967
const HEADER: [u8; 40] = [
968
0xd0, 0x0d, 0xfe, 0xed, // 0000: magic (0xd00dfeed)
969
0x00, 0x00, 0x00, 0xda, // 0004: totalsize (0xda)
970
0x00, 0x00, 0x00, 0x58, // 0008: off_dt_struct (0x58)
971
0x00, 0x00, 0x00, 0xb2, // 000C: off_dt_strings (0xb2)
972
0x00, 0x00, 0x00, 0x28, // 0010: off_mem_rsvmap (0x28)
973
0x00, 0x00, 0x00, 0x11, // 0014: version (0x11 = 17)
974
0x00, 0x00, 0x00, 0x10, // 0018: last_comp_version (0x10 = 16)
975
0x00, 0x00, 0x00, 0x00, // 001C: boot_cpuid_phys (0)
976
0x00, 0x00, 0x00, 0x28, // 0020: size_dt_strings (0x28)
977
0x00, 0x00, 0x00, 0x5a, // 0024: size_dt_struct (0x5a)
978
];
979
980
FdtHeader::from_blob(&HEADER).unwrap();
981
982
// Header too small
983
assert!(FdtHeader::from_blob(&HEADER[..FdtHeader::SIZE - 4]).is_err());
984
assert!(FdtHeader::from_blob(&[]).is_err());
985
986
let mut invalid_header = HEADER;
987
invalid_header[0x00] = 0x00; // change magic to (0x000dfeed)
988
FdtHeader::from_blob(&invalid_header).expect_err("invalid magic");
989
990
let mut invalid_header = HEADER;
991
invalid_header[0x07] = 0x10; // make totalsize too small
992
FdtHeader::from_blob(&invalid_header).expect_err("invalid totalsize");
993
994
let mut invalid_header = HEADER;
995
invalid_header[0x0b] = 0x60; // increase off_dt_struct
996
FdtHeader::from_blob(&invalid_header).expect_err("dt struct overlaps with strings");
997
998
let mut invalid_header = HEADER;
999
invalid_header[0x27] = 0x5c; // increase size_dt_struct
1000
FdtHeader::from_blob(&invalid_header).expect_err("dt struct overlaps with strings");
1001
1002
let mut invalid_header = HEADER;
1003
invalid_header[0x13] = 0x20; // decrease off_mem_rsvmap
1004
FdtHeader::from_blob(&invalid_header).expect_err("reserved memory overlaps with header");
1005
1006
let mut invalid_header = HEADER;
1007
invalid_header[0x0f] = 0x50; // decrease off_dt_strings
1008
FdtHeader::from_blob(&invalid_header).expect_err("strings start before struct");
1009
1010
let mut invalid_header = HEADER;
1011
invalid_header[0x23] = 0x50; // increase size_dt_strings
1012
FdtHeader::from_blob(&invalid_header).expect_err("strings go past totalsize");
1013
}
1014
1015
#[test]
1016
fn fdt_load_resv_map() {
1017
let blob: &[u8] = &FDT_BLOB_RSVMAP;
1018
let fdt = Fdt::from_blob(blob).unwrap();
1019
assert_eq!(fdt.reserved_memory.len(), 2);
1020
assert!(
1021
fdt.reserved_memory[0].address == 0x12345678AABBCCDD
1022
&& fdt.reserved_memory[0].size == 0x1234
1023
);
1024
assert!(
1025
fdt.reserved_memory[1].address == 0x1020304050607080
1026
&& fdt.reserved_memory[1].size == 0x5678
1027
);
1028
}
1029
1030
#[test]
1031
fn fdt_test_node_props() {
1032
let mut node = FdtNode::empty("mynode").unwrap();
1033
node.set_prop("myprop", 1u32).unwrap();
1034
assert_eq!(node.get_prop::<u32>("myprop").unwrap(), 1u32);
1035
node.set_prop("myprop", 0xabcdef9876543210u64).unwrap();
1036
assert_eq!(
1037
node.get_prop::<u64>("myprop").unwrap(),
1038
0xabcdef9876543210u64
1039
);
1040
node.set_prop("myprop", ()).unwrap();
1041
assert_eq!(node.get_prop::<Vec<u8>>("myprop").unwrap(), []);
1042
node.set_prop("myprop", vec![1u8, 2u8, 3u8]).unwrap();
1043
assert_eq!(
1044
node.get_prop::<Vec<u8>>("myprop").unwrap(),
1045
vec![1u8, 2u8, 3u8]
1046
);
1047
node.set_prop("myprop", vec![1u32, 2u32, 3u32]).unwrap();
1048
assert_eq!(
1049
node.get_prop::<Vec<u32>>("myprop").unwrap(),
1050
vec![1u32, 2u32, 3u32]
1051
);
1052
node.set_prop("myprop", vec![1u64, 2u64, 3u64]).unwrap();
1053
assert_eq!(
1054
node.get_prop::<Vec<u64>>("myprop").unwrap(),
1055
vec![1u64, 2u64, 3u64]
1056
);
1057
node.set_prop("myprop", "myval".to_string()).unwrap();
1058
assert_eq!(
1059
node.get_prop::<String>("myprop").unwrap(),
1060
"myval".to_string()
1061
);
1062
node.set_prop(
1063
"myprop",
1064
vec![
1065
"myval1".to_string(),
1066
"myval2".to_string(),
1067
"myval3".to_string(),
1068
],
1069
)
1070
.unwrap();
1071
assert_eq!(
1072
node.get_prop::<Vec<String>>("myprop").unwrap(),
1073
vec![
1074
"myval1".to_string(),
1075
"myval2".to_string(),
1076
"myval3".to_string()
1077
]
1078
);
1079
}
1080
1081
#[test]
1082
fn fdt_simple_use() {
1083
let mut fdt = Fdt::new(&[]);
1084
let root_node = fdt.root_mut();
1085
root_node
1086
.set_prop("compatible", "linux,dummy-virt")
1087
.unwrap();
1088
root_node.set_prop("#address-cells", 0x2u32).unwrap();
1089
root_node.set_prop("#size-cells", 0x2u32).unwrap();
1090
let chosen_node = root_node.subnode_mut("chosen").unwrap();
1091
chosen_node.set_prop("linux,pci-probe-only", 1u32).unwrap();
1092
chosen_node
1093
.set_prop("bootargs", "panic=-1 console=hvc0 root=/dev/vda")
1094
.unwrap();
1095
fdt.finish().unwrap();
1096
}
1097
1098
#[test]
1099
fn fdt_load_strings() {
1100
let blob = &FDT_BLOB_STRINGS[..];
1101
let strings = FdtStrings::from_blob(blob).unwrap();
1102
let mut offset = 0u32;
1103
1104
for s in EXPECTED_STRINGS {
1105
assert_eq!(strings.at_offset(offset).unwrap(), s);
1106
offset += strings.at_offset(offset).unwrap().len() as u32 + 1;
1107
}
1108
}
1109
1110
#[test]
1111
fn fdt_load_strings_intern() {
1112
let strings_blob = &FDT_BLOB_STRINGS[..];
1113
let mut strings = FdtStrings::from_blob(strings_blob).unwrap();
1114
assert_eq!(strings.intern_string("null"), 0);
1115
assert_eq!(strings.intern_string("strlst"), 17);
1116
assert_eq!(strings.intern_string("arru64"), 31);
1117
assert_eq!(strings.intern_string("abc"), 38);
1118
assert_eq!(strings.intern_string("def"), 42);
1119
assert_eq!(strings.intern_string("strlst"), 17);
1120
}
1121
1122
#[test]
1123
fn fdt_load_props() {
1124
const PROP_SIZES: [(&str, usize); 7] = [
1125
("null", 0),
1126
("u32", 4),
1127
("u64", 8),
1128
("str", 6),
1129
("strlst", 7),
1130
("arru32", 8),
1131
("arru64", 8),
1132
];
1133
1134
let blob: &[u8] = &FDT_BLOB_STRINGS[..];
1135
let strings = FdtStrings::from_blob(blob).unwrap();
1136
let blob: &[u8] = &FDT_BLOB_NODES_ROOT_ONLY[..];
1137
let node = FdtNode::from_blob(blob, &strings).unwrap();
1138
1139
assert_eq!(node.name, "");
1140
assert_eq!(node.subnodes.len(), 0);
1141
assert_eq!(node.props.len(), PROP_SIZES.len());
1142
1143
for (pname, s) in PROP_SIZES.into_iter() {
1144
assert_eq!(node.get_prop::<Vec<u8>>(pname).unwrap().len(), s);
1145
}
1146
}
1147
1148
#[test]
1149
fn fdt_load_nodes_nested() {
1150
let strings_blob = &FDT_BLOB_STRINGS[..];
1151
let strings = FdtStrings::from_blob(strings_blob).unwrap();
1152
let blob: &[u8] = &FDT_BLOB_NESTED_NODES[..];
1153
let root_node = FdtNode::from_blob(blob, &strings).unwrap();
1154
1155
// Check root node
1156
assert_eq!(root_node.name, "");
1157
assert_eq!(root_node.subnodes.len(), 2);
1158
assert_eq!(root_node.props.len(), 1);
1159
1160
// Check first nested node
1161
let nested_node = root_node.subnodes.get("nested").unwrap();
1162
assert_eq!(nested_node.name, "nested");
1163
assert_eq!(nested_node.subnodes.len(), 0);
1164
assert_eq!(nested_node.props.len(), 2);
1165
1166
// Check second nested node
1167
let nested2_node = root_node.subnodes.get("nested2").unwrap();
1168
assert_eq!(nested2_node.name, "nested2");
1169
assert_eq!(nested2_node.subnodes.len(), 1);
1170
assert_eq!(nested2_node.props.len(), 1);
1171
1172
// Check third nested node
1173
let nested3_node = nested2_node.subnodes.get("nested3").unwrap();
1174
assert_eq!(nested3_node.name, "nested3");
1175
assert_eq!(nested3_node.subnodes.len(), 0);
1176
assert_eq!(nested3_node.props.len(), 0);
1177
}
1178
1179
#[test]
1180
fn fdt_get_node() {
1181
let fdt = Fdt::new(&[]);
1182
assert!(fdt.get_node("/").is_some());
1183
assert!(fdt.get_node("/a").is_none());
1184
}
1185
1186
#[test]
1187
fn fdt_find_nested_node() {
1188
let mut fdt = Fdt::new(&[]);
1189
let node1 = fdt.root.subnode_mut("N1").unwrap();
1190
node1.subnode_mut("N1-1").unwrap();
1191
node1.subnode_mut("N1-2").unwrap();
1192
let node2 = fdt.root.subnode_mut("N2").unwrap();
1193
let node2_1 = node2.subnode_mut("N2-1").unwrap();
1194
node2_1.subnode_mut("N2-1-1").unwrap();
1195
1196
assert!(fdt.get_node("/").is_some());
1197
assert!(fdt.get_node("/N1").is_some());
1198
assert!(fdt.get_node("/N2").is_some());
1199
assert!(fdt.get_node("/N1/N1-1").is_some());
1200
assert!(fdt.get_node("/N1/N1-2").is_some());
1201
assert!(fdt.get_node("/N2/N2-1").is_some());
1202
assert!(fdt.get_node("/N2/N2-1/N2-1-1").is_some());
1203
assert!(fdt.get_node("/N2/N2-1/A").is_none());
1204
}
1205
1206
#[test]
1207
fn minimal() {
1208
let mut fdt = Fdt::new(&[]);
1209
assert_eq!(
1210
fdt.finish().unwrap(),
1211
[
1212
0xd0, 0x0d, 0xfe, 0xed, // 0000: magic (0xd00dfeed)
1213
0x00, 0x00, 0x00, 0x48, // 0004: totalsize (0x48)
1214
0x00, 0x00, 0x00, 0x38, // 0008: off_dt_struct (0x38)
1215
0x00, 0x00, 0x00, 0x48, // 000C: off_dt_strings (0x48)
1216
0x00, 0x00, 0x00, 0x28, // 0010: off_mem_rsvmap (0x28)
1217
0x00, 0x00, 0x00, 0x11, // 0014: version (0x11 = 17)
1218
0x00, 0x00, 0x00, 0x10, // 0018: last_comp_version (0x10 = 16)
1219
0x00, 0x00, 0x00, 0x00, // 001C: boot_cpuid_phys (0)
1220
0x00, 0x00, 0x00, 0x00, // 0020: size_dt_strings (0)
1221
0x00, 0x00, 0x00, 0x10, // 0024: size_dt_struct (0x10)
1222
0x00, 0x00, 0x00, 0x00, // 0028: rsvmap terminator (address = 0 high)
1223
0x00, 0x00, 0x00, 0x00, // 002C: rsvmap terminator (address = 0 low)
1224
0x00, 0x00, 0x00, 0x00, // 0030: rsvmap terminator (size = 0 high)
1225
0x00, 0x00, 0x00, 0x00, // 0034: rsvmap terminator (size = 0 low)
1226
0x00, 0x00, 0x00, 0x01, // 0038: FDT_BEGIN_NODE
1227
0x00, 0x00, 0x00, 0x00, // 003C: node name ("") + padding
1228
0x00, 0x00, 0x00, 0x02, // 0040: FDT_END_NODE
1229
0x00, 0x00, 0x00, 0x09, // 0044: FDT_END
1230
]
1231
);
1232
}
1233
1234
#[test]
1235
fn reservemap() {
1236
let mut fdt = Fdt::new(&[
1237
FdtReserveEntry {
1238
address: 0x12345678AABBCCDD,
1239
size: 0x1234,
1240
},
1241
FdtReserveEntry {
1242
address: 0x1020304050607080,
1243
size: 0x5678,
1244
},
1245
]);
1246
assert_eq!(
1247
fdt.finish().unwrap(),
1248
[
1249
0xd0, 0x0d, 0xfe, 0xed, // 0000: magic (0xd00dfeed)
1250
0x00, 0x00, 0x00, 0x68, // 0004: totalsize (0x68)
1251
0x00, 0x00, 0x00, 0x58, // 0008: off_dt_struct (0x58)
1252
0x00, 0x00, 0x00, 0x68, // 000C: off_dt_strings (0x68)
1253
0x00, 0x00, 0x00, 0x28, // 0010: off_mem_rsvmap (0x28)
1254
0x00, 0x00, 0x00, 0x11, // 0014: version (0x11 = 17)
1255
0x00, 0x00, 0x00, 0x10, // 0018: last_comp_version (0x10 = 16)
1256
0x00, 0x00, 0x00, 0x00, // 001C: boot_cpuid_phys (0)
1257
0x00, 0x00, 0x00, 0x00, // 0020: size_dt_strings (0)
1258
0x00, 0x00, 0x00, 0x10, // 0024: size_dt_struct (0x10)
1259
0x12, 0x34, 0x56, 0x78, // 0028: rsvmap entry 0 address high
1260
0xAA, 0xBB, 0xCC, 0xDD, // 002C: rsvmap entry 0 address low
1261
0x00, 0x00, 0x00, 0x00, // 0030: rsvmap entry 0 size high
1262
0x00, 0x00, 0x12, 0x34, // 0034: rsvmap entry 0 size low
1263
0x10, 0x20, 0x30, 0x40, // 0038: rsvmap entry 1 address high
1264
0x50, 0x60, 0x70, 0x80, // 003C: rsvmap entry 1 address low
1265
0x00, 0x00, 0x00, 0x00, // 0040: rsvmap entry 1 size high
1266
0x00, 0x00, 0x56, 0x78, // 0044: rsvmap entry 1 size low
1267
0x00, 0x00, 0x00, 0x00, // 0048: rsvmap terminator (address = 0 high)
1268
0x00, 0x00, 0x00, 0x00, // 004C: rsvmap terminator (address = 0 low)
1269
0x00, 0x00, 0x00, 0x00, // 0050: rsvmap terminator (size = 0 high)
1270
0x00, 0x00, 0x00, 0x00, // 0054: rsvmap terminator (size = 0 low)
1271
0x00, 0x00, 0x00, 0x01, // 0058: FDT_BEGIN_NODE
1272
0x00, 0x00, 0x00, 0x00, // 005C: node name ("") + padding
1273
0x00, 0x00, 0x00, 0x02, // 0060: FDT_END_NODE
1274
0x00, 0x00, 0x00, 0x09, // 0064: FDT_END
1275
]
1276
);
1277
}
1278
1279
#[test]
1280
fn prop_null() {
1281
let mut fdt = Fdt::new(&[]);
1282
let root_node = fdt.root_mut();
1283
root_node.set_prop("null", ()).unwrap();
1284
assert_eq!(
1285
fdt.finish().unwrap(),
1286
[
1287
0xd0, 0x0d, 0xfe, 0xed, // 0000: magic (0xd00dfeed)
1288
0x00, 0x00, 0x00, 0x59, // 0004: totalsize (0x59)
1289
0x00, 0x00, 0x00, 0x38, // 0008: off_dt_struct (0x38)
1290
0x00, 0x00, 0x00, 0x54, // 000C: off_dt_strings (0x54)
1291
0x00, 0x00, 0x00, 0x28, // 0010: off_mem_rsvmap (0x28)
1292
0x00, 0x00, 0x00, 0x11, // 0014: version (0x11 = 17)
1293
0x00, 0x00, 0x00, 0x10, // 0018: last_comp_version (0x10 = 16)
1294
0x00, 0x00, 0x00, 0x00, // 001C: boot_cpuid_phys (0)
1295
0x00, 0x00, 0x00, 0x05, // 0020: size_dt_strings (0x05)
1296
0x00, 0x00, 0x00, 0x1c, // 0024: size_dt_struct (0x1C)
1297
0x00, 0x00, 0x00, 0x00, // 0028: rsvmap terminator (address = 0 high)
1298
0x00, 0x00, 0x00, 0x00, // 002C: rsvmap terminator (address = 0 low)
1299
0x00, 0x00, 0x00, 0x00, // 0030: rsvmap terminator (size = 0 high)
1300
0x00, 0x00, 0x00, 0x00, // 0034: rsvmap terminator (size = 0 low)
1301
0x00, 0x00, 0x00, 0x01, // 0038: FDT_BEGIN_NODE
1302
0x00, 0x00, 0x00, 0x00, // 003C: node name ("") + padding
1303
0x00, 0x00, 0x00, 0x03, // 0040: FDT_PROP
1304
0x00, 0x00, 0x00, 0x00, // 0044: prop len (0)
1305
0x00, 0x00, 0x00, 0x00, // 0048: prop nameoff (0)
1306
0x00, 0x00, 0x00, 0x02, // 004C: FDT_END_NODE
1307
0x00, 0x00, 0x00, 0x09, // 0050: FDT_END
1308
b'n', b'u', b'l', b'l', 0x00, // 0054: strings block
1309
]
1310
);
1311
}
1312
1313
#[test]
1314
fn prop_u32() {
1315
let mut fdt = Fdt::new(&[]);
1316
let root_node = fdt.root_mut();
1317
root_node.set_prop("u32", 0x12345678u32).unwrap();
1318
assert_eq!(
1319
fdt.finish().unwrap(),
1320
[
1321
0xd0, 0x0d, 0xfe, 0xed, // 0000: magic (0xd00dfeed)
1322
0x00, 0x00, 0x00, 0x5c, // 0004: totalsize (0x5C)
1323
0x00, 0x00, 0x00, 0x38, // 0008: off_dt_struct (0x38)
1324
0x00, 0x00, 0x00, 0x58, // 000C: off_dt_strings (0x58)
1325
0x00, 0x00, 0x00, 0x28, // 0010: off_mem_rsvmap (0x28)
1326
0x00, 0x00, 0x00, 0x11, // 0014: version (0x11 = 17)
1327
0x00, 0x00, 0x00, 0x10, // 0018: last_comp_version (0x10 = 16)
1328
0x00, 0x00, 0x00, 0x00, // 001C: boot_cpuid_phys (0)
1329
0x00, 0x00, 0x00, 0x04, // 0020: size_dt_strings (0x04)
1330
0x00, 0x00, 0x00, 0x20, // 0024: size_dt_struct (0x20)
1331
0x00, 0x00, 0x00, 0x00, // 0028: rsvmap terminator (address = 0 high)
1332
0x00, 0x00, 0x00, 0x00, // 002C: rsvmap terminator (address = 0 low)
1333
0x00, 0x00, 0x00, 0x00, // 0030: rsvmap terminator (size = 0 high)
1334
0x00, 0x00, 0x00, 0x00, // 0034: rsvmap terminator (size = 0 low)
1335
0x00, 0x00, 0x00, 0x01, // 0038: FDT_BEGIN_NODE
1336
0x00, 0x00, 0x00, 0x00, // 003C: node name ("") + padding
1337
0x00, 0x00, 0x00, 0x03, // 0040: FDT_PROP
1338
0x00, 0x00, 0x00, 0x04, // 0044: prop len (4)
1339
0x00, 0x00, 0x00, 0x00, // 0048: prop nameoff (0)
1340
0x12, 0x34, 0x56, 0x78, // 004C: prop u32 value (0x12345678)
1341
0x00, 0x00, 0x00, 0x02, // 0050: FDT_END_NODE
1342
0x00, 0x00, 0x00, 0x09, // 0054: FDT_END
1343
b'u', b'3', b'2', 0x00, // 0058: strings block
1344
]
1345
);
1346
}
1347
1348
#[test]
1349
fn all_props() {
1350
let mut fdt = Fdt::new(&[]);
1351
let root_node = fdt.root_mut();
1352
root_node
1353
.set_prop("arru32", &[0x12345678u32, 0xAABBCCDDu32])
1354
.unwrap();
1355
root_node
1356
.set_prop("arru64", &[0x1234567887654321u64])
1357
.unwrap();
1358
root_node.set_prop("null", ()).unwrap();
1359
root_node.set_prop("str", "hello").unwrap();
1360
root_node.set_prop("strlst", &["hi", "bye"]).unwrap();
1361
root_node.set_prop("u32", 0x12345678u32).unwrap();
1362
root_node.set_prop("u64", 0x1234567887654321u64).unwrap();
1363
assert_eq!(
1364
fdt.finish().unwrap(),
1365
[
1366
0xd0, 0x0d, 0xfe, 0xed, // 0000: magic (0xd00dfeed)
1367
0x00, 0x00, 0x00, 0xee, // 0004: totalsize (0xEE)
1368
0x00, 0x00, 0x00, 0x38, // 0008: off_dt_struct (0x38)
1369
0x00, 0x00, 0x00, 0xc8, // 000C: off_dt_strings (0xC8)
1370
0x00, 0x00, 0x00, 0x28, // 0010: off_mem_rsvmap (0x28)
1371
0x00, 0x00, 0x00, 0x11, // 0014: version (0x11 = 17)
1372
0x00, 0x00, 0x00, 0x10, // 0018: last_comp_version (0x10 = 16)
1373
0x00, 0x00, 0x00, 0x00, // 001C: boot_cpuid_phys (0)
1374
0x00, 0x00, 0x00, 0x26, // 0020: size_dt_strings (0x26)
1375
0x00, 0x00, 0x00, 0x90, // 0024: size_dt_struct (0x90)
1376
0x00, 0x00, 0x00, 0x00, // 0028: rsvmap terminator (address = 0 high)
1377
0x00, 0x00, 0x00, 0x00, // 002C: rsvmap terminator (address = 0 low)
1378
0x00, 0x00, 0x00, 0x00, // 0030: rsvmap terminator (size = 0 high)
1379
0x00, 0x00, 0x00, 0x00, // 0034: rsvmap terminator (size = 0 low)
1380
0x00, 0x00, 0x00, 0x01, // 0038: FDT_BEGIN_NODE
1381
0x00, 0x00, 0x00, 0x00, // 003C: node name ("") + padding
1382
0x00, 0x00, 0x00, 0x03, // 0040: FDT_PROP (u32 array)
1383
0x00, 0x00, 0x00, 0x08, // 0044: prop len (8)
1384
0x00, 0x00, 0x00, 0x00, // 0048: prop nameoff (0x00)
1385
0x12, 0x34, 0x56, 0x78, // 004C: prop value 0
1386
0xAA, 0xBB, 0xCC, 0xDD, // 0050: prop value 1
1387
0x00, 0x00, 0x00, 0x03, // 0054: FDT_PROP (u64 array)
1388
0x00, 0x00, 0x00, 0x08, // 0058: prop len (8)
1389
0x00, 0x00, 0x00, 0x07, // 005C: prop nameoff (0x07)
1390
0x12, 0x34, 0x56, 0x78, // 0060: prop u64 value 0 high
1391
0x87, 0x65, 0x43, 0x21, // 0064: prop u64 value 0 low
1392
0x00, 0x00, 0x00, 0x03, // 0068: FDT_PROP (null)
1393
0x00, 0x00, 0x00, 0x00, // 006C: prop len (0)
1394
0x00, 0x00, 0x00, 0x0E, // 0070: prop nameoff (0x0e)
1395
0x00, 0x00, 0x00, 0x03, // 0074: FDT_PROP (string)
1396
0x00, 0x00, 0x00, 0x06, // 0078: prop len (6)
1397
0x00, 0x00, 0x00, 0x13, // 007C: prop nameoff (0x13)
1398
b'h', b'e', b'l', b'l', // 0080: prop str value ("hello") + padding
1399
b'o', 0x00, 0x00, 0x00, // 0084: "o\0" + padding
1400
0x00, 0x00, 0x00, 0x03, // 0088: FDT_PROP (string list)
1401
0x00, 0x00, 0x00, 0x07, // 008C: prop len (7)
1402
0x00, 0x00, 0x00, 0x17, // 0090: prop nameoff (0x17)
1403
b'h', b'i', 0x00, b'b', // 0094: prop value ("hi", "bye")
1404
b'y', b'e', 0x00, 0x00, // 0098: "ye\0" + padding
1405
0x00, 0x00, 0x00, 0x03, // 009C: FDT_PROP (u32)
1406
0x00, 0x00, 0x00, 0x04, // 00A0: prop len (4)
1407
0x00, 0x00, 0x00, 0x1E, // 00A4: prop nameoff (0x1E)
1408
0x12, 0x34, 0x56, 0x78, // 00A8: prop u32 value (0x12345678)
1409
0x00, 0x00, 0x00, 0x03, // 00AC: FDT_PROP (u64)
1410
0x00, 0x00, 0x00, 0x08, // 00B0: prop len (8)
1411
0x00, 0x00, 0x00, 0x22, // 00B4: prop nameoff (0x22)
1412
0x12, 0x34, 0x56, 0x78, // 00B8: prop u64 value high (0x12345678)
1413
0x87, 0x65, 0x43, 0x21, // 00BC: prop u64 value low (0x87654321)
1414
0x00, 0x00, 0x00, 0x02, // 00C0: FDT_END_NODE
1415
0x00, 0x00, 0x00, 0x09, // 00C4: FDT_END
1416
b'a', b'r', b'r', b'u', b'3', b'2', 0x00, // 00C8: strings + 0x00: "arru32"
1417
b'a', b'r', b'r', b'u', b'6', b'4', 0x00, // 00CF: strings + 0x07: "arru64"
1418
b'n', b'u', b'l', b'l', 0x00, // 00D6: strings + 0x0E: "null"
1419
b's', b't', b'r', 0x00, // 00DB: strings + 0x13: "str"
1420
b's', b't', b'r', b'l', b's', b't', 0x00, // 00DF: strings + 0x17: "strlst"
1421
b'u', b'3', b'2', 0x00, // 00E6: strings + 0x1E: "u32"
1422
b'u', b'6', b'4', 0x00, // 00EA: strings + 0x22: "u64"
1423
]
1424
);
1425
}
1426
1427
#[test]
1428
fn node_order() {
1429
let expected: &[u8] = &[
1430
0xd0, 0x0d, 0xfe, 0xed, // 0000: magic (0xd00dfeed)
1431
0x00, 0x00, 0x00, 0x9C, // 0004: totalsize (0x9C)
1432
0x00, 0x00, 0x00, 0x38, // 0008: off_dt_struct (0x38)
1433
0x00, 0x00, 0x00, 0x9C, // 000C: off_dt_strings (0x9C)
1434
0x00, 0x00, 0x00, 0x28, // 0010: off_mem_rsvmap (0x28)
1435
0x00, 0x00, 0x00, 0x11, // 0014: version (0x11 = 17)
1436
0x00, 0x00, 0x00, 0x10, // 0018: last_comp_version (0x10 = 16)
1437
0x00, 0x00, 0x00, 0x00, // 001C: boot_cpuid_phys (0)
1438
0x00, 0x00, 0x00, 0x00, // 0020: size_dt_strings (0x00)
1439
0x00, 0x00, 0x00, 0x64, // 0024: size_dt_struct (0x64)
1440
0x00, 0x00, 0x00, 0x00, // 0028: rsvmap terminator (address = 0 high)
1441
0x00, 0x00, 0x00, 0x00, // 002C: rsvmap terminator (address = 0 low)
1442
0x00, 0x00, 0x00, 0x00, // 0030: rsvmap terminator (size = 0 high)
1443
0x00, 0x00, 0x00, 0x00, // 0034: rsvmap terminator (size = 0 low)
1444
0x00, 0x00, 0x00, 0x01, // 0038: FDT_BEGIN_NODE
1445
0x00, 0x00, 0x00, 0x00, // 003C: node name ("") + padding
1446
0x00, 0x00, 0x00, 0x01, // 0040: FDT_BEGIN_NODE
1447
b'B', 0x00, 0x00, 0x00, // 0044: node name ("B") + padding
1448
0x00, 0x00, 0x00, 0x02, // 0048: FDT_END_NODE
1449
0x00, 0x00, 0x00, 0x01, // 004C: FDT_BEGIN_NODE
1450
b'A', 0x00, 0x00, 0x00, // 0050: node name ("A") + padding
1451
0x00, 0x00, 0x00, 0x02, // 0054: FDT_END_NODE
1452
0x00, 0x00, 0x00, 0x01, // 0058: FDT_BEGIN_NODE
1453
b'C', 0x00, 0x00, 0x00, // 005C: node name ("C") + padding
1454
0x00, 0x00, 0x00, 0x01, // 0060: FDT_BEGIN_NODE
1455
b'D', 0x00, 0x00, 0x00, // 0064: node name ("D") + padding
1456
0x00, 0x00, 0x00, 0x02, // 0068: FDT_END_NODE
1457
0x00, 0x00, 0x00, 0x01, // 006C: FDT_BEGIN_NODE
1458
b'E', 0x00, 0x00, 0x00, // 0070: node name ("E") + padding
1459
0x00, 0x00, 0x00, 0x02, // 0074: FDT_END_NODE
1460
0x00, 0x00, 0x00, 0x01, // 0078: FDT_BEGIN_NODE
1461
b'B', 0x00, 0x00, 0x00, // 007C: node name ("B") + padding
1462
0x00, 0x00, 0x00, 0x02, // 0080: FDT_END_NODE
1463
0x00, 0x00, 0x00, 0x01, // 0084: FDT_BEGIN_NODE
1464
b'F', 0x00, 0x00, 0x00, // 0088: node name ("F") + padding
1465
0x00, 0x00, 0x00, 0x02, // 008C: FDT_END_NODE
1466
0x00, 0x00, 0x00, 0x02, // 0090: FDT_END_NODE
1467
0x00, 0x00, 0x00, 0x02, // 0094: FDT_END_NODE
1468
0x00, 0x00, 0x00, 0x09, // 0098: FDT_END
1469
];
1470
1471
let mut fdt = Fdt::new(&[]);
1472
let root = fdt.root_mut();
1473
let root_subnode_names = ["B", "A", "C"];
1474
let node_c_subnode_names = ["D", "E", "B", "F"];
1475
for n in root_subnode_names {
1476
root.subnode_mut(n).unwrap();
1477
}
1478
let node_c = root.subnode_mut("C").unwrap();
1479
for n in node_c_subnode_names {
1480
node_c.subnode_mut(n).unwrap();
1481
}
1482
1483
assert!(root
1484
.iter_subnodes()
1485
.zip(root_subnode_names)
1486
.all(|(sn, n)| sn.name == n));
1487
assert!(root
1488
.subnode("C")
1489
.unwrap()
1490
.iter_subnodes()
1491
.zip(node_c_subnode_names)
1492
.all(|(sn, n)| sn.name == n));
1493
assert_eq!(fdt.finish().unwrap(), expected);
1494
}
1495
1496
#[test]
1497
fn prop_order() {
1498
let expected: &[u8] = &[
1499
0xd0, 0x0d, 0xfe, 0xed, // 0000: magic (0xd00dfeed)
1500
0x00, 0x00, 0x00, 0x98, // 0004: totalsize (0x98)
1501
0x00, 0x00, 0x00, 0x38, // 0008: off_dt_struct (0x38)
1502
0x00, 0x00, 0x00, 0x88, // 000C: off_dt_strings (0x88)
1503
0x00, 0x00, 0x00, 0x28, // 0010: off_mem_rsvmap (0x28)
1504
0x00, 0x00, 0x00, 0x11, // 0014: version (0x11 = 17)
1505
0x00, 0x00, 0x00, 0x10, // 0018: last_comp_version (0x10 = 16)
1506
0x00, 0x00, 0x00, 0x00, // 001C: boot_cpuid_phys (0)
1507
0x00, 0x00, 0x00, 0x10, // 0020: size_dt_strings (0x10)
1508
0x00, 0x00, 0x00, 0x50, // 0024: size_dt_struct (0x50)
1509
0x00, 0x00, 0x00, 0x00, // 0028: rsvmap terminator (address = 0 high)
1510
0x00, 0x00, 0x00, 0x00, // 002C: rsvmap terminator (address = 0 low)
1511
0x00, 0x00, 0x00, 0x00, // 0030: rsvmap terminator (size = 0 high)
1512
0x00, 0x00, 0x00, 0x00, // 0034: rsvmap terminator (size = 0 low)
1513
0x00, 0x00, 0x00, 0x01, // 0038: FDT_BEGIN_NODE
1514
0x00, 0x00, 0x00, 0x00, // 003C: node name ("") + padding
1515
0x00, 0x00, 0x00, 0x03, // 0040: FDT_PROP (u32)
1516
0x00, 0x00, 0x00, 0x04, // 0044: prop len (4)
1517
0x00, 0x00, 0x00, 0x00, // 0048: prop nameoff (0x00)
1518
0x76, 0x61, 0x6c, 0x00, // 004C: prop string value ("val")
1519
0x00, 0x00, 0x00, 0x03, // 0050: FDT_PROP (u32)
1520
0x00, 0x00, 0x00, 0x04, // 0054: prop len (4)
1521
0x00, 0x00, 0x00, 0x04, // 0058: prop nameoff (0x04)
1522
0x00, 0x00, 0x00, 0x02, // 005C: prop u32 high (0x2)
1523
0x00, 0x00, 0x00, 0x03, // 0060: FDT_PROP (u32)
1524
0x00, 0x00, 0x00, 0x04, // 0064: prop len (4)
1525
0x00, 0x00, 0x00, 0x08, // 0068: prop nameoff (0x08)
1526
0x00, 0x00, 0x00, 0x01, // 006C: prop u32 value (0x1)
1527
0x00, 0x00, 0x00, 0x03, // 0070: FDT_PROP (u32)
1528
0x00, 0x00, 0x00, 0x04, // 0074: prop len (4)
1529
0x00, 0x00, 0x00, 0x0C, // 0078: prop nameoff (0x0B)
1530
0x00, 0x00, 0x00, 0x03, // 007C: prop u32 value (0x3)
1531
0x00, 0x00, 0x00, 0x02, // 0080: FDT_END_NODE
1532
0x00, 0x00, 0x00, 0x09, // 0084: FDT_END
1533
b'g', b'h', b'i', 0x00, // 0088: strings + 0x00: "ghi"
1534
b'd', b'e', b'f', 0x00, // 008C: strings + 0x04: "def"
1535
b'a', b'b', b'c', 0x00, // 0090: strings + 0x08: "abc"
1536
b'b', b'c', b'd', 0x00, // 0094: strings + 0x0C: "bcd"
1537
];
1538
1539
let mut fdt = Fdt::new(&[]);
1540
let root_node = fdt.root_mut();
1541
root_node.set_prop("ghi", "val").unwrap();
1542
root_node.set_prop("def", 2u32).unwrap();
1543
root_node.set_prop("abc", 1u32).unwrap();
1544
root_node.set_prop("bcd", 3u32).unwrap();
1545
1546
assert_eq!(
1547
root_node.prop_names().collect::<Vec<_>>(),
1548
["ghi", "def", "abc", "bcd"]
1549
);
1550
assert_eq!(fdt.finish().unwrap(), expected);
1551
}
1552
1553
#[test]
1554
fn nested_nodes() {
1555
let mut fdt = Fdt::new(&[]);
1556
let root_node = fdt.root_mut();
1557
root_node.set_prop("abc", 0x13579024u32).unwrap();
1558
let nested_node = root_node.subnode_mut("nested").unwrap();
1559
nested_node.set_prop("def", 0x12121212u32).unwrap();
1560
assert_eq!(
1561
fdt.finish().unwrap(),
1562
[
1563
0xd0, 0x0d, 0xfe, 0xed, // 0000: magic (0xd00dfeed)
1564
0x00, 0x00, 0x00, 0x80, // 0004: totalsize (0x80)
1565
0x00, 0x00, 0x00, 0x38, // 0008: off_dt_struct (0x38)
1566
0x00, 0x00, 0x00, 0x78, // 000C: off_dt_strings (0x78)
1567
0x00, 0x00, 0x00, 0x28, // 0010: off_mem_rsvmap (0x28)
1568
0x00, 0x00, 0x00, 0x11, // 0014: version (0x11 = 17)
1569
0x00, 0x00, 0x00, 0x10, // 0018: last_comp_version (0x10 = 16)
1570
0x00, 0x00, 0x00, 0x00, // 001C: boot_cpuid_phys (0)
1571
0x00, 0x00, 0x00, 0x08, // 0020: size_dt_strings (0x08)
1572
0x00, 0x00, 0x00, 0x40, // 0024: size_dt_struct (0x40)
1573
0x00, 0x00, 0x00, 0x00, // 0028: rsvmap terminator (address = 0 high)
1574
0x00, 0x00, 0x00, 0x00, // 002C: rsvmap terminator (address = 0 low)
1575
0x00, 0x00, 0x00, 0x00, // 0030: rsvmap terminator (size = 0 high)
1576
0x00, 0x00, 0x00, 0x00, // 0034: rsvmap terminator (size = 0 low)
1577
0x00, 0x00, 0x00, 0x01, // 0038: FDT_BEGIN_NODE
1578
0x00, 0x00, 0x00, 0x00, // 003C: node name ("") + padding
1579
0x00, 0x00, 0x00, 0x03, // 0040: FDT_PROP
1580
0x00, 0x00, 0x00, 0x04, // 0044: prop len (4)
1581
0x00, 0x00, 0x00, 0x00, // 0048: prop nameoff (0x00)
1582
0x13, 0x57, 0x90, 0x24, // 004C: prop u32 value (0x13579024)
1583
0x00, 0x00, 0x00, 0x01, // 0050: FDT_BEGIN_NODE
1584
b'n', b'e', b's', b't', // 0054: Node name ("nested")
1585
b'e', b'd', 0x00, 0x00, // 0058: "ed\0" + pad
1586
0x00, 0x00, 0x00, 0x03, // 005C: FDT_PROP
1587
0x00, 0x00, 0x00, 0x04, // 0060: prop len (4)
1588
0x00, 0x00, 0x00, 0x04, // 0064: prop nameoff (0x04)
1589
0x12, 0x12, 0x12, 0x12, // 0068: prop u32 value (0x12121212)
1590
0x00, 0x00, 0x00, 0x02, // 006C: FDT_END_NODE ("nested")
1591
0x00, 0x00, 0x00, 0x02, // 0070: FDT_END_NODE ("")
1592
0x00, 0x00, 0x00, 0x09, // 0074: FDT_END
1593
b'a', b'b', b'c', 0x00, // 0078: strings + 0x00: "abc"
1594
b'd', b'e', b'f', 0x00, // 007C: strings + 0x04: "def"
1595
]
1596
);
1597
}
1598
1599
#[test]
1600
fn prop_name_string_reuse() {
1601
let mut fdt = Fdt::new(&[]);
1602
let root_node = fdt.root_mut();
1603
root_node.set_prop("abc", 0x13579024u32).unwrap();
1604
let nested = root_node.subnode_mut("nested").unwrap();
1605
nested.set_prop("abc", 0x12121212u32).unwrap(); // This should reuse the "abc" string.
1606
nested.set_prop("def", 0x12121212u32).unwrap();
1607
assert_eq!(
1608
fdt.finish().unwrap(),
1609
[
1610
0xd0, 0x0d, 0xfe, 0xed, // 0000: magic (0xd00dfeed)
1611
0x00, 0x00, 0x00, 0x90, // 0004: totalsize (0x90)
1612
0x00, 0x00, 0x00, 0x38, // 0008: off_dt_struct (0x38)
1613
0x00, 0x00, 0x00, 0x88, // 000C: off_dt_strings (0x88)
1614
0x00, 0x00, 0x00, 0x28, // 0010: off_mem_rsvmap (0x28)
1615
0x00, 0x00, 0x00, 0x11, // 0014: version (0x11 = 17)
1616
0x00, 0x00, 0x00, 0x10, // 0018: last_comp_version (0x10 = 16)
1617
0x00, 0x00, 0x00, 0x00, // 001C: boot_cpuid_phys (0)
1618
0x00, 0x00, 0x00, 0x08, // 0020: size_dt_strings (0x08)
1619
0x00, 0x00, 0x00, 0x50, // 0024: size_dt_struct (0x50)
1620
0x00, 0x00, 0x00, 0x00, // 0028: rsvmap terminator (address = 0 high)
1621
0x00, 0x00, 0x00, 0x00, // 002C: rsvmap terminator (address = 0 low)
1622
0x00, 0x00, 0x00, 0x00, // 0030: rsvmap terminator (size = 0 high)
1623
0x00, 0x00, 0x00, 0x00, // 0034: rsvmap terminator (size = 0 low)
1624
0x00, 0x00, 0x00, 0x01, // 0038: FDT_BEGIN_NODE
1625
0x00, 0x00, 0x00, 0x00, // 003C: node name ("") + padding
1626
0x00, 0x00, 0x00, 0x03, // 0040: FDT_PROP
1627
0x00, 0x00, 0x00, 0x04, // 0044: prop len (4)
1628
0x00, 0x00, 0x00, 0x00, // 0048: prop nameoff (0x00)
1629
0x13, 0x57, 0x90, 0x24, // 004C: prop u32 value (0x13579024)
1630
0x00, 0x00, 0x00, 0x01, // 0050: FDT_BEGIN_NODE
1631
b'n', b'e', b's', b't', // 0054: Node name ("nested")
1632
b'e', b'd', 0x00, 0x00, // 0058: "ed\0" + pad
1633
0x00, 0x00, 0x00, 0x03, // 005C: FDT_PROP
1634
0x00, 0x00, 0x00, 0x04, // 0060: prop len (4)
1635
0x00, 0x00, 0x00, 0x00, // 0064: prop nameoff (0x00 - reuse)
1636
0x12, 0x12, 0x12, 0x12, // 0068: prop u32 value (0x12121212)
1637
0x00, 0x00, 0x00, 0x03, // 006C: FDT_PROP
1638
0x00, 0x00, 0x00, 0x04, // 0070: prop len (4)
1639
0x00, 0x00, 0x00, 0x04, // 0074: prop nameoff (0x04)
1640
0x12, 0x12, 0x12, 0x12, // 0078: prop u32 value (0x12121212)
1641
0x00, 0x00, 0x00, 0x02, // 007C: FDT_END_NODE ("nested")
1642
0x00, 0x00, 0x00, 0x02, // 0080: FDT_END_NODE ("")
1643
0x00, 0x00, 0x00, 0x09, // 0084: FDT_END
1644
b'a', b'b', b'c', 0x00, // 0088: strings + 0x00: "abc"
1645
b'd', b'e', b'f', 0x00, // 008C: strings + 0x04: "def"
1646
]
1647
);
1648
}
1649
1650
#[test]
1651
fn invalid_node_name_nul() {
1652
let mut fdt = Fdt::new(&[]);
1653
let root_node = fdt.root_mut();
1654
root_node
1655
.subnode_mut("abc\0def")
1656
.expect_err("node name with embedded NUL");
1657
}
1658
1659
#[test]
1660
fn invalid_prop_name_nul() {
1661
let mut fdt = Fdt::new(&[]);
1662
let root_node = fdt.root_mut();
1663
root_node
1664
.set_prop("abc\0def", 0u32)
1665
.expect_err("property name with embedded NUL");
1666
}
1667
1668
#[test]
1669
fn invalid_prop_string_value_nul() {
1670
let mut fdt = Fdt::new(&[]);
1671
let root_node = fdt.root_mut();
1672
root_node
1673
.set_prop("mystr", "abc\0def")
1674
.expect_err("string property value with embedded NUL");
1675
}
1676
1677
#[test]
1678
fn invalid_prop_string_list_value_nul() {
1679
let mut fdt = Fdt::new(&[]);
1680
let root_node = fdt.root_mut();
1681
let strs = ["test", "abc\0def"];
1682
root_node
1683
.set_prop("mystr", &strs)
1684
.expect_err("stringlist property value with embedded NUL");
1685
}
1686
}
1687
1688