Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
google
GitHub Repository: google/crosvm
Path: blob/main/cros_fdt/src/overlay.rs
5392 views
1
// Copyright 2023 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 applies binary flattened device tree overlays.
6
7
use std::collections::BTreeMap;
8
use std::collections::HashSet;
9
use std::collections::VecDeque;
10
11
use crate::fdt::Error;
12
use crate::fdt::Fdt;
13
use crate::fdt::FdtNode;
14
use crate::fdt::FdtReserveEntry;
15
use crate::fdt::Result;
16
use crate::path::parse_path_with_prop;
17
use crate::path::Path;
18
use crate::path::PhandlePin;
19
use crate::path::PATH_SEP;
20
21
const PHANDLE_PROP: &str = "phandle";
22
const LINUX_PHANDLE_PROP: &str = "linux,phandle";
23
const TARGET_PATH_PROP: &str = "target-path";
24
const TARGET_PROP: &str = "target";
25
const LOCAL_FIXUPS_NODE: &str = "__local_fixups__";
26
const OVERLAY_NODE: &str = "__overlay__";
27
const SYMBOLS_NODE: &str = "__symbols__";
28
const FIXUPS_NODE: &str = "__fixups__";
29
const ROOT_NODE: &str = "/";
30
31
// Ensure filtered symbols exist and contain a valid path. They will be the starting points
32
// for the filtering algorithm.
33
fn prepare_filtered_symbols<T: AsRef<str>>(
34
start_symbols: impl std::iter::IntoIterator<Item = T>,
35
fdt: &Fdt,
36
) -> Result<(HashSet<String>, Vec<Path>)> {
37
let symbols = HashSet::from_iter(start_symbols.into_iter().map(|s| s.as_ref().to_owned()));
38
let mut paths = vec![];
39
for symbol in &symbols {
40
paths.push(
41
fdt.symbol_to_path(symbol)
42
.map_err(|e| Error::FilterError(format!("{e}")))?,
43
);
44
}
45
Ok((symbols, paths))
46
}
47
48
// Look for references (phandle values) defined by `fixup_node` in properties of `tree_node`.
49
fn collect_phandle_refs_from_props(fixup_node: &FdtNode, tree_node: &FdtNode) -> Result<Vec<u32>> {
50
let mut phandles = vec![];
51
for propname in fixup_node.prop_names() {
52
for phandle_offset in fixup_node.get_prop::<Vec<u32>>(propname).unwrap() {
53
phandles.push(
54
tree_node
55
.phandle_at_offset(propname, phandle_offset as usize)
56
.ok_or(Error::PropertyValueInvalid)?,
57
);
58
}
59
}
60
Ok(phandles)
61
}
62
63
// Traverse all nodes along given node path, and collect phandle reference values from properties.
64
fn collect_all_references_by_path(
65
path: &Path,
66
root: &FdtNode,
67
local_fixups_node: &FdtNode,
68
) -> Result<HashSet<u32>> {
69
// Follow node names inside the local fixups node and in the tree root.
70
let mut tree_node = root;
71
let mut fixup_node = local_fixups_node;
72
let mut phandle_refs = HashSet::<u32>::new();
73
74
// Follow node names along path
75
for node_name in path.iter() {
76
tree_node = tree_node
77
.subnode(node_name)
78
.ok_or_else(|| Error::InvalidPath(format!("cannot find subnode {node_name}")))?;
79
if let Some(n) = fixup_node.subnode(node_name) {
80
fixup_node = n
81
} else {
82
return Ok(phandle_refs); // No references left to collect in this subtree.
83
}
84
85
// Look for references (phandle values) in properties along path; add them to set.
86
phandle_refs.extend(collect_phandle_refs_from_props(fixup_node, tree_node)?);
87
}
88
Ok(phandle_refs)
89
}
90
91
// Collect locations of all phandles in the FDT.
92
fn get_all_phandles(fdt: &Fdt) -> BTreeMap<u32, Path> {
93
let mut phandles = BTreeMap::new();
94
let mut nodes = VecDeque::<(&FdtNode, Path)>::new();
95
nodes.push_back((&fdt.root, ROOT_NODE.parse().unwrap()));
96
while let Some((node, path)) = nodes.pop_front() {
97
for subnode in node.iter_subnodes() {
98
nodes.push_back((subnode, path.push(&subnode.name).unwrap()));
99
}
100
if let Some(phandle) = get_node_phandle(node) {
101
phandles.insert(phandle, path);
102
}
103
}
104
phandles
105
}
106
107
// Minimize paths - if the vector contains two paths where one is the
108
// parent of the other, only include the parent path, and drop the child path.
109
fn minimize_paths(paths: &mut Vec<Path>) {
110
paths.sort();
111
paths.dedup_by(|a, b| a.is_child_of(b));
112
}
113
114
// Collect paths of all nodes that nodes in `start_paths` depend on. Path A depends on
115
// path B if any node along the path A references the node path B points to.
116
fn collect_all_filtered_paths(mut start_paths: Vec<Path>, fdt: &Fdt) -> Result<Vec<Path>> {
117
if start_paths.is_empty() {
118
return Ok(vec![]);
119
}
120
minimize_paths(&mut start_paths);
121
let Some(local_fixups_node) = fdt.root.subnode(LOCAL_FIXUPS_NODE) else {
122
return Ok(start_paths); // No fixups node -> no other references
123
};
124
125
let all_phandles = get_all_phandles(fdt); // All FDT phandles, mapped to their paths
126
let mut result_paths = HashSet::<Path>::with_capacity(start_paths.len());
127
let mut pending_paths: VecDeque<_> = start_paths.iter().collect(); // Paths to visit
128
129
while let Some(path) = pending_paths.pop_front() {
130
if result_paths.contains(path) {
131
continue; // Already seen this path
132
}
133
// Collect all phandles that this path references
134
let phandles = collect_all_references_by_path(path, &fdt.root, local_fixups_node)?;
135
// Map the phandles to other locations
136
for ph in phandles {
137
pending_paths.push_back(all_phandles.get(&ph).ok_or(Error::PropertyValueInvalid)?);
138
}
139
// This path should remain in the final overlay.
140
result_paths.insert(path.to_owned());
141
}
142
143
let mut result_paths = result_paths.into_iter().collect();
144
minimize_paths(&mut result_paths);
145
Ok(result_paths)
146
}
147
148
// Drop nodes which are not covered by the filtered paths.
149
fn do_overlay_filter(filtered_paths: Vec<Path>, overlay: &mut Fdt) {
150
if filtered_paths.is_empty() {
151
return;
152
}
153
let mut new_root = FdtNode::empty("").unwrap();
154
for path in filtered_paths {
155
let mut src_node = &overlay.root;
156
let mut tgt_node = &mut new_root;
157
for node_name in path.iter() {
158
src_node = src_node
159
.subnode(node_name)
160
.expect("filtered paths reference valid nodes");
161
tgt_node = tgt_node
162
.subnode_mut(node_name)
163
.expect("filtered paths reference valid nodes");
164
tgt_node.props.clone_from(&src_node.props);
165
}
166
}
167
overlay.root = new_root;
168
}
169
170
// Read 'phandle' or 'linux,phandle' property of a node.
171
fn get_node_phandle(node: &FdtNode) -> Option<u32> {
172
node.get_prop(PHANDLE_PROP)
173
.or_else(|| node.get_prop(LINUX_PHANDLE_PROP))
174
}
175
176
// Return the largest phandle value in a node tree.
177
fn get_max_phandle(root_node: &FdtNode) -> u32 {
178
let mut max_phandle = 0u32;
179
let mut nodes_to_visit = VecDeque::new();
180
nodes_to_visit.push_back(root_node);
181
while let Some(node) = nodes_to_visit.pop_front() {
182
max_phandle = max_phandle.max(get_node_phandle(node).unwrap_or(0u32));
183
nodes_to_visit.extend(node.iter_subnodes());
184
}
185
max_phandle
186
}
187
188
// Add the given delta to the phandle property of the node.
189
fn offset_phandle_prop(node: &mut FdtNode, propname: &str, delta: u32) -> Result<()> {
190
let mut val: u32 = node.get_prop(propname).ok_or_else(|| {
191
Error::ApplyOverlayError(format!(
192
"cannot offset {}:{} - invalid value",
193
node.name, propname
194
))
195
})?;
196
val = val
197
.checked_add(delta)
198
.ok_or_else(|| Error::ApplyOverlayError("cannot offset phandle - value overflow".into()))?;
199
node.set_prop(propname, val)
200
.expect("phandle property name is valid");
201
Ok(())
202
}
203
204
// Add the given delta to phandle properties of all nodes in the FDT.
205
fn offset_phandle_values(fdt: &mut Fdt, delta: u32) -> Result<()> {
206
let mut stack = VecDeque::new();
207
stack.push_back(&mut fdt.root);
208
while let Some(node) = stack.pop_front() {
209
if node.has_prop(PHANDLE_PROP) {
210
offset_phandle_prop(node, PHANDLE_PROP, delta)?;
211
}
212
if node.has_prop(LINUX_PHANDLE_PROP) {
213
offset_phandle_prop(node, LINUX_PHANDLE_PROP, delta)?;
214
}
215
stack.extend(node.iter_subnodes_mut());
216
}
217
Ok(())
218
}
219
220
// Returns a vector of paths which contain a local phandle value (reference)
221
fn collect_local_fixup_paths(fdt: &Fdt) -> Result<BTreeMap<Path, Vec<PhandlePin>>> {
222
let mut local_phandles = BTreeMap::<Path, Vec<PhandlePin>>::new();
223
let Some(local_fixups_node) = fdt.root.subnode(LOCAL_FIXUPS_NODE) else {
224
return Ok(local_phandles);
225
};
226
let mut stack = VecDeque::<(Path, &FdtNode)>::new();
227
stack.push_back((ROOT_NODE.parse().unwrap(), local_fixups_node));
228
229
// Collect local phandle properties to fixup from __local_fixups__
230
while let Some((path, node)) = stack.pop_front() {
231
// Every property in __local_fixups__ contains a vector of offsets (u32)
232
// where the phandles are located
233
for propname in node.prop_names() {
234
let offsets = node.get_prop::<Vec<u32>>(propname).ok_or_else(|| {
235
Error::ApplyOverlayError(format!(
236
"fixup node {} contains invalid offset array",
237
node.name
238
))
239
})?;
240
// Add phandle pins
241
if !local_phandles.contains_key(&path) {
242
local_phandles.insert(path.clone(), vec![]);
243
}
244
let pins = local_phandles.get_mut(&path).unwrap();
245
pins.extend(offsets.into_iter().map(|o| PhandlePin(propname.into(), o)));
246
}
247
// Traverse into this node's children
248
for child in node.iter_subnodes() {
249
stack.push_back((path.push(&child.name)?, child));
250
}
251
}
252
Ok(local_phandles)
253
}
254
255
fn update_local_phandle_propvals(
256
fdt: &mut Fdt,
257
paths: BTreeMap<Path, Vec<PhandlePin>>,
258
delta: u32,
259
) -> Result<()> {
260
// Update phandles in collected locations
261
for (path, pins) in paths {
262
let node = fdt
263
.get_node_mut(path)
264
.ok_or_else(|| Error::ApplyOverlayError("cannot find node for fixup".into()))?;
265
for pin in pins {
266
let phandle_val = node
267
.phandle_at_offset(&pin.0, pin.1 as usize)
268
.ok_or_else(|| Error::ApplyOverlayError(format!("missing property {}", &pin.0)))?;
269
node.update_phandle_at_offset(&pin.0, pin.1 as usize, phandle_val + delta)?;
270
}
271
}
272
Ok(())
273
}
274
275
fn update_local_refs(fdt: &mut Fdt, delta: u32) -> Result<()> {
276
let phandle_locations = collect_local_fixup_paths(fdt)?;
277
update_local_phandle_propvals(fdt, phandle_locations, delta)
278
}
279
280
// Given a DT symbol (label), find the path and phandle value of the node the symbol refers to.
281
fn get_symbol_path_and_phandle(symbol: &str, fdt: &Fdt) -> Option<(String, u32)> {
282
let symbols_node = fdt.root.subnode(SYMBOLS_NODE)?;
283
let symbol = symbols_node.get_prop::<String>(symbol)?;
284
let target_node = fdt.get_node(symbol.as_str())?;
285
Some((symbol, get_node_phandle(target_node)?))
286
}
287
288
// For each symbol defined in base and referenced in overlay, set its references in overlay to
289
// correct phandle values.
290
fn apply_external_fixups(base: &Fdt, overlay: &mut Fdt) -> Result<()> {
291
let Some(fixups_node) = overlay.root.subnode(FIXUPS_NODE) else {
292
return Ok(()); // No references to base nodes
293
};
294
295
// Collect locations in overlay where external nodes are referenced
296
let mut paths_to_update = BTreeMap::<(String, u32), Vec<String>>::new();
297
for fixup_symbol in fixups_node.prop_names() {
298
// Find phandle value and path of a labeled node in base DT
299
let path_and_phandle =
300
get_symbol_path_and_phandle(fixup_symbol, base).ok_or_else(|| {
301
Error::ApplyOverlayError(format!("cannot find symbol {fixup_symbol} in base fdt"))
302
})?;
303
// Get target paths of this symbol in overlay
304
let target_paths: Vec<String> = fixups_node.get_prop(fixup_symbol).ok_or_else(|| {
305
Error::ApplyOverlayError(format!(
306
"cannot parse target paths for fixup {fixup_symbol}"
307
))
308
})?;
309
paths_to_update.insert(path_and_phandle, target_paths);
310
}
311
312
// Update locations in overlay where external nodes are referenced
313
for ((base_path, phandle), paths) in paths_to_update {
314
for path in paths {
315
let (path, pin) = parse_path_with_prop(&path)?;
316
// Update phandle reference in target to new value
317
let target_node = overlay
318
.get_node_mut(path)
319
.ok_or_else(|| Error::ApplyOverlayError("invalid fixup target path".into()))?;
320
target_node.update_phandle_at_offset(&pin.0, pin.1 as usize, phandle)?;
321
322
// If the property that is being updated here is actually a `target` property of
323
// an overlay fragment, also add the `target-path` property to the fragment, containing
324
// the full path to the target node in base FDT.
325
// This covers the case where the target of an overlay fragment is a phandle reference
326
// (of a node in base overlay), instead of absolute path in base.
327
if pin.0 == TARGET_PROP && target_node.iter_subnodes().any(|n| n.name == OVERLAY_NODE) {
328
target_node.set_prop(TARGET_PATH_PROP, base_path.as_str())?;
329
}
330
}
331
}
332
Ok(())
333
}
334
335
// Copy properties from overlay node to base node, then add subnodes and overlay them as well.
336
fn overlay_node_pair(base_node: &mut FdtNode, overlay_node: &FdtNode) -> Result<()> {
337
base_node.props.extend(overlay_node.props.clone());
338
for overlay_subnode in overlay_node.iter_subnodes() {
339
overlay_node_pair(
340
base_node.subnode_mut(&overlay_subnode.name)?,
341
overlay_subnode,
342
)?;
343
}
344
Ok(())
345
}
346
347
// Verify and apply an overlay fragment node to the base FDT.
348
fn overlay_fragment(fragment_node: &FdtNode, base: &mut Fdt) -> Result<()> {
349
// Fragment must have an '__overlay__' subnode and `target-path` property.
350
let Some(overlay_node) = fragment_node.subnode(OVERLAY_NODE) else {
351
return Ok(()); // Skip invalid fragments.
352
};
353
let Some(target_path) = fragment_node.get_prop::<String>(TARGET_PATH_PROP) else {
354
return Ok(()); // Skip invalid fragments.
355
};
356
// Apply overlay fragment to target node in base FDT.
357
let target_node = base.get_node_mut(target_path.as_str()).ok_or_else(|| {
358
Error::ApplyOverlayError(format!(
359
"cannot find node in base FDT for target-path {target_path}",
360
))
361
})?;
362
overlay_node_pair(target_node, overlay_node)
363
}
364
365
// Parse the location of the symbol (property value), extract fragment name and the
366
// rest of the path after `__overlay__`, (expected structure:
367
// "/fragment@X/__overlay__/path/to/subnode").
368
fn extract_fragment_and_subpath(path: &Path) -> Result<(&str, String)> {
369
let mut path_iter = path.iter();
370
let fragment_name = path_iter
371
.next()
372
.ok_or_else(|| Error::ApplyOverlayError(format!("symbol path {path} too short")))?;
373
path_iter.next(); // Skip "__overlay__" node
374
let rest = path_iter.collect::<Vec<_>>();
375
if rest.is_empty() {
376
Err(Error::ApplyOverlayError(format!(
377
"symbol path {path} too short"
378
)))
379
} else {
380
Ok((fragment_name, rest.join(PATH_SEP)))
381
}
382
}
383
384
fn update_base_symbols(
385
base: &mut Fdt,
386
overlay: &Fdt,
387
filtered_symbols: HashSet<String>,
388
) -> Result<()> {
389
let Some(overlay_symbols_node) = overlay.root.subnode(SYMBOLS_NODE) else {
390
return Ok(()); // If there are no symbols in the overlay, just skip it.
391
};
392
let base_symbols_node = base.root.subnode_mut(SYMBOLS_NODE).unwrap();
393
for symbol in overlay_symbols_node.prop_names() {
394
if !filtered_symbols.is_empty() && !filtered_symbols.contains(symbol) {
395
continue; // Skip this symbol, it is not in the set of symbols we want.
396
}
397
398
let symbol_target: Path = overlay_symbols_node
399
.get_prop::<String>(symbol)
400
.unwrap()
401
.parse()?;
402
403
// Parse location
404
let (fragment_name, rest) = extract_fragment_and_subpath(&symbol_target)?;
405
406
// Find the overlay fragment
407
let fragment_node = overlay.root.subnode(fragment_name).ok_or_else(|| {
408
Error::ApplyOverlayError(format!("invalid symbol path {symbol_target}"))
409
})?;
410
411
// Construct the new symbol path from `target-path` property value and the remainder of
412
// the symbol location. Eg, for target-path = "/node", and overlay symbol path
413
// "/fragment@X/__overlay__/path/to/subnode", the result is "/node/path/to/subnode".
414
let new_path: String = fragment_node
415
.get_prop::<String>(TARGET_PATH_PROP)
416
.unwrap_or_default()
417
.parse::<Path>()?
418
.push(&rest)?
419
.into();
420
// Update base with new symbol path. `symbol` is a valid property name.
421
base_symbols_node.set_prop(symbol, new_path).unwrap();
422
}
423
Ok(())
424
}
425
426
// Merge new reserved memory entries from overlay into base.
427
fn merge_resvmem(base: &mut Vec<FdtReserveEntry>, new_entries: Vec<FdtReserveEntry>) {
428
base.extend(new_entries);
429
base.sort_by_key(|a| std::cmp::Reverse(a.address));
430
if let Some(mut entry) = base.pop() {
431
let mut result = Vec::new();
432
while let Some(next_entry) = base.pop() {
433
if next_entry.address <= entry.address + entry.size {
434
entry.size = (entry.address + entry.size).max(next_entry.address + next_entry.size)
435
- entry.address;
436
} else {
437
result.push(entry);
438
entry = next_entry;
439
}
440
}
441
result.push(entry);
442
base.extend(result);
443
}
444
}
445
446
/// Apply an overlay to the base FDT.
447
///
448
/// # Arguments
449
///
450
/// `base` - base FDT that will be updated with new nodes and properties.
451
/// `overlay` - overlay FDT that will be applied to the base. Must contain symbols and fixups nodes.
452
/// `filtered_symbols` - A slice of node labels (symbols) listing nodes which will be applied to the
453
/// base. Values must correspond to the properties of overlay `__symbols__` node. If empty, the
454
/// entire overlay is applied to base.
455
pub fn apply_overlay<T: AsRef<str>>(
456
base: &mut Fdt,
457
mut overlay: Fdt,
458
filter_symbols: impl std::iter::IntoIterator<Item = T>,
459
) -> Result<()> {
460
// Analyze filtered symbols and find paths they point to.
461
let (filter_symbols, filter_paths) = prepare_filtered_symbols(filter_symbols, &overlay)?;
462
463
// Analyze the overlay tree and extract paths that have to be applied to base.
464
let filtered_paths = collect_all_filtered_paths(filter_paths, &overlay)?;
465
466
// Offset phandle property values in overlay nodes
467
let max_phandle = get_max_phandle(&base.root);
468
offset_phandle_values(&mut overlay, max_phandle)?;
469
470
// Offset local phandle references in overlay properties
471
update_local_refs(&mut overlay, max_phandle)?;
472
473
// Apply phandle values for external references
474
apply_external_fixups(base, &mut overlay)?;
475
476
// Copy filtered overlay __symbols__ to base
477
update_base_symbols(base, &overlay, filter_symbols)?;
478
479
// Remove unneeded nodes
480
do_overlay_filter(filtered_paths, &mut overlay);
481
482
// Merge nodes from overlay into base
483
for fragment_node in overlay.root.iter_subnodes() {
484
overlay_fragment(fragment_node, base)?;
485
}
486
487
// Merge reserved regions
488
merge_resvmem(&mut base.reserved_memory, overlay.reserved_memory);
489
Ok(())
490
}
491
492
#[cfg(test)]
493
mod tests {
494
use super::*;
495
496
fn load_fdt(mut reader: impl std::io::Read) -> Result<Fdt> {
497
let mut buffer = Vec::new();
498
reader.read_to_end(&mut buffer).map_err(Error::FdtIoError)?;
499
Fdt::from_blob(&buffer[..])
500
}
501
502
#[test]
503
fn fdt_merge_resvmem() {
504
let mut base = vec![
505
FdtReserveEntry::new(1000, 100),
506
FdtReserveEntry::new(2000, 500),
507
FdtReserveEntry::new(3000, 1000),
508
];
509
let new_entries = vec![
510
FdtReserveEntry::new(1010, 20),
511
FdtReserveEntry::new(1050, 1000),
512
FdtReserveEntry::new(2700, 500),
513
];
514
merge_resvmem(&mut base, new_entries);
515
assert_eq!(
516
base,
517
vec![
518
FdtReserveEntry::new(1000, 1500),
519
FdtReserveEntry::new(2700, 1300),
520
]
521
);
522
}
523
524
#[test]
525
fn fdt_find_phandle_single() {
526
let mut root = FdtNode::empty("").unwrap();
527
root.set_prop("a", 1u32).unwrap();
528
root.set_prop("b", 2u32).unwrap();
529
root.set_prop("phandle", 3u32).unwrap();
530
assert_eq!(get_node_phandle(&root), Some(3));
531
}
532
533
#[test]
534
fn fdt_find_phandle_none() {
535
let mut root = FdtNode::empty("").unwrap();
536
root.set_prop("a", 1u32).unwrap();
537
root.set_prop("b", 2u32).unwrap();
538
assert_eq!(get_node_phandle(&root), None);
539
}
540
541
#[test]
542
fn fdt_find_phandle_deprecated() {
543
let mut root = FdtNode::empty("").unwrap();
544
root.set_prop("a", 1u32).unwrap();
545
root.set_prop("linux,phandle", 2u32).unwrap();
546
assert_eq!(get_node_phandle(&root), Some(2));
547
}
548
549
#[test]
550
fn fdt_find_max_phandle() {
551
let mut root = FdtNode::empty("").unwrap();
552
root.set_prop("phandle", 2u32).unwrap();
553
let node_a = root.subnode_mut("a").unwrap();
554
node_a.set_prop("linux,phandle", 4u32).unwrap();
555
let node_b = root.subnode_mut("b").unwrap();
556
node_b.set_prop("phandle", 0xAu32).unwrap();
557
node_b.set_prop("linux,phandle", 0xAAu32).unwrap();
558
559
let node_c = node_b.subnode_mut("c").unwrap();
560
node_c.set_prop("linux,phandle", 0x10u32).unwrap();
561
node_c.set_prop("not-phandle", 0x11u32).unwrap();
562
let node_d = node_b.subnode_mut("d").unwrap();
563
node_d.set_prop("not-phandle", 0x20u32).unwrap();
564
node_b.subnode_mut("").unwrap();
565
566
assert_eq!(get_max_phandle(&root), 0x10);
567
}
568
569
#[test]
570
fn fdt_offset_phandles() {
571
let mut fdt = Fdt::new(&[]);
572
fdt.root.set_prop("a", 1u32).unwrap();
573
fdt.root.set_prop("b", 2u32).unwrap();
574
fdt.root.set_prop("phandle", 3u32).unwrap();
575
let node_a = fdt.root.subnode_mut("a").unwrap();
576
node_a.set_prop("linux,phandle", 0x10u32).unwrap();
577
fdt.root.subnode_mut("b").unwrap();
578
579
offset_phandle_values(&mut fdt, 100).unwrap();
580
for (prop, exp_val) in fdt.root.prop_names().zip([1u32, 2, 103].into_iter()) {
581
assert_eq!(fdt.root.get_prop::<u32>(prop).unwrap(), exp_val);
582
}
583
let node = fdt.get_node("/a").unwrap();
584
assert_eq!(node.get_prop::<u32>(LINUX_PHANDLE_PROP).unwrap(), 116);
585
let node = fdt.get_node("/b").unwrap();
586
assert!(node.prop_names().next().is_none());
587
}
588
589
#[test]
590
fn fdt_collect_local_references() {
591
let mut fdt = Fdt::new(&[]);
592
let fixups_node = fdt.root.subnode_mut(LOCAL_FIXUPS_NODE).unwrap();
593
fixups_node.set_prop("p1", vec![0u32, 4u32]).unwrap();
594
let fixups_subnode = fixups_node.subnode_mut("subnode1").unwrap();
595
fixups_subnode.set_prop("p2", vec![8u32]).unwrap();
596
let fixups_subnode = fixups_node.subnode_mut("subnode2").unwrap();
597
fixups_subnode.set_prop("p1", vec![16u32, 24u32]).unwrap();
598
599
let paths = collect_local_fixup_paths(&fdt).unwrap();
600
assert_eq!(paths.len(), 3);
601
602
let expected_paths: BTreeMap<Path, Vec<PhandlePin>> = BTreeMap::from([
603
(
604
ROOT_NODE.parse().unwrap(),
605
vec![PhandlePin("p1".into(), 0), PhandlePin("p1".into(), 4)],
606
),
607
(
608
"/subnode1".parse().unwrap(),
609
vec![PhandlePin("p2".into(), 8)],
610
),
611
(
612
"/subnode2".parse().unwrap(),
613
vec![PhandlePin("p1".into(), 16), PhandlePin("p1".into(), 24)],
614
),
615
]);
616
617
for (key, value) in expected_paths {
618
assert!(value.eq(paths.get(&key).unwrap()));
619
}
620
}
621
622
fn make_fragment0() -> FdtNode {
623
let mut fragment_node = FdtNode::empty("fragment@0").unwrap();
624
fragment_node.set_prop("target-path", ROOT_NODE).unwrap();
625
626
let overlay_node = fragment_node.subnode_mut(OVERLAY_NODE).unwrap();
627
overlay_node.set_prop("root-prop1", 1u32).unwrap();
628
overlay_node
629
.set_prop("root-prop2", vec![1u32, 2u32, 3u32])
630
.unwrap();
631
let overlay_child_node = overlay_node.subnode_mut("child1").unwrap();
632
overlay_child_node.set_prop("prop1", 10u32).unwrap();
633
overlay_child_node
634
.set_prop("prop2", vec![10u32, 20u32, 30u32])
635
.unwrap();
636
fragment_node
637
}
638
639
fn make_fragment1() -> FdtNode {
640
let mut fragment_node = FdtNode::empty("fragment@1").unwrap();
641
fragment_node.set_prop("target-path", ROOT_NODE).unwrap();
642
643
let overlay_node = fragment_node.subnode_mut(OVERLAY_NODE).unwrap();
644
overlay_node.set_prop("root-prop1", "abc").unwrap();
645
overlay_node.set_prop("root-prop3", 100u64).unwrap();
646
let overlay_child_node = overlay_node.subnode_mut("child1").unwrap();
647
overlay_child_node.set_prop("prop1", 0u32).unwrap();
648
let _ = overlay_node.subnode_mut("child2").unwrap();
649
fragment_node
650
}
651
652
#[test]
653
fn fdt_test_overlay_nodes() {
654
let mut base = Fdt::new(&[]);
655
656
let fragment_node = make_fragment0();
657
overlay_fragment(&fragment_node, &mut base).unwrap();
658
659
assert_eq!(base.root.get_prop::<u32>("root-prop1").unwrap(), 1u32);
660
assert_eq!(
661
base.root.get_prop::<Vec<u32>>("root-prop2").unwrap(),
662
vec![1u32, 2u32, 3u32]
663
);
664
let child_node = base.get_node("/child1").unwrap();
665
assert_eq!(child_node.get_prop::<u32>("prop1").unwrap(), 10u32);
666
assert_eq!(
667
child_node.get_prop::<Vec<u32>>("prop2").unwrap(),
668
vec![10u32, 20u32, 30u32]
669
);
670
671
let fragment_node = make_fragment1();
672
overlay_fragment(&fragment_node, &mut base).unwrap();
673
assert_eq!(
674
base.root.get_prop::<Vec<u8>>("root-prop1").unwrap(),
675
vec![b'a', b'b', b'c', 0u8]
676
);
677
assert_eq!(base.root.get_prop::<u64>("root-prop3").unwrap(), 100u64);
678
679
let child_node = base.get_node("/child1").unwrap();
680
assert_eq!(child_node.get_prop::<u32>("prop1").unwrap(), 0u32);
681
682
let child_node = base.get_node("/child2").unwrap();
683
assert!(child_node.prop_names().next().is_none());
684
}
685
686
#[test]
687
fn fdt_overlay_symbols() {
688
let mut base = Fdt::new(&[]);
689
let symbols = base.root.subnode_mut(SYMBOLS_NODE).unwrap();
690
691
symbols.set_prop("n1", "/path/to/node1").unwrap();
692
symbols.set_prop("n2", "/path/to/node2").unwrap();
693
694
let mut overlay = Fdt::new(&[]);
695
let symbols = overlay.root.subnode_mut(SYMBOLS_NODE).unwrap();
696
symbols
697
.set_prop("n1", "/fragment@0/__overlay__/node1")
698
.unwrap();
699
symbols
700
.set_prop("n3", "/fragment@0/__overlay__/path/to/node3")
701
.unwrap();
702
let fragment = overlay.root.subnode_mut("fragment@0").unwrap();
703
fragment.set_prop("target-path", ROOT_NODE).unwrap();
704
705
update_base_symbols(&mut base, &overlay, [].into()).unwrap();
706
707
let symbols = base.root.subnode_mut(SYMBOLS_NODE).unwrap();
708
assert_eq!(symbols.get_prop::<String>("n1").unwrap(), "/node1");
709
assert_eq!(symbols.get_prop::<String>("n2").unwrap(), "/path/to/node2");
710
assert_eq!(symbols.get_prop::<String>("n3").unwrap(), "/path/to/node3");
711
}
712
713
#[test]
714
fn fdt_overlay_filtered_symbols() {
715
let mut base = Fdt::new(&[]);
716
717
let symbols = base.root.subnode_mut(SYMBOLS_NODE).unwrap();
718
symbols.set_prop("n1", "/path/to/node1").unwrap();
719
symbols.set_prop("n2", "/path/to/node2").unwrap();
720
721
let mut overlay = Fdt::new(&[]);
722
let symbols = overlay.root.subnode_mut(SYMBOLS_NODE).unwrap();
723
symbols
724
.set_prop("n1", "/fragment@0/__overlay__/node1")
725
.unwrap();
726
symbols
727
.set_prop("n3", "/fragment@0/__overlay__/path/to/node3")
728
.unwrap();
729
symbols
730
.set_prop("not-this", "/fragment@0/__overlay__/path/to/not-this")
731
.unwrap();
732
symbols
733
.set_prop(
734
"not-this-either",
735
"/fragment@0/__overlay__/path/to/not-this-either",
736
)
737
.unwrap();
738
let fragment = overlay.root.subnode_mut("fragment@0").unwrap();
739
fragment.set_prop("target-path", ROOT_NODE).unwrap();
740
741
update_base_symbols(
742
&mut base,
743
&overlay,
744
["n1".to_string(), "n3".to_string()].into(),
745
)
746
.unwrap();
747
let symbols = base.root.subnode(SYMBOLS_NODE).unwrap();
748
assert_eq!(symbols.get_prop::<String>("n1").unwrap(), "/node1");
749
assert_eq!(symbols.get_prop::<String>("n2").unwrap(), "/path/to/node2");
750
assert_eq!(symbols.get_prop::<String>("n3").unwrap(), "/path/to/node3");
751
assert!(symbols.get_prop::<String>("not-this").is_none());
752
assert!(symbols.get_prop::<String>("not-this-either").is_none());
753
754
update_base_symbols(&mut base, &overlay, [].into()).unwrap();
755
let symbols = base.root.subnode(SYMBOLS_NODE).unwrap();
756
assert_eq!(symbols.get_prop::<String>("n1").unwrap(), "/node1");
757
assert_eq!(symbols.get_prop::<String>("n2").unwrap(), "/path/to/node2");
758
assert_eq!(symbols.get_prop::<String>("n3").unwrap(), "/path/to/node3");
759
assert_eq!(
760
symbols.get_prop::<String>("not-this").unwrap(),
761
"/path/to/not-this"
762
);
763
assert_eq!(
764
symbols.get_prop::<String>("not-this-either").unwrap(),
765
"/path/to/not-this-either"
766
);
767
}
768
769
fn make_fdt_with_local_refs(references: &[(&str, u32)]) -> Result<Fdt> {
770
/* Returns this structure:
771
/
772
node1 (phandle=1)
773
node1-1 (phandle=2)
774
node1-1-1 (phandle=3)
775
node1-1-2 (phandle=4)
776
node1-2 (phandle=5)
777
node1-2-1 (phandle=6)
778
node2 (phandle=7)
779
node2-1 (phandle=8)
780
node2-2 (phandle=9)
781
node2-3 (phandle=10)
782
node2-3-1 (phandle=11)
783
node3 (phandle=12)
784
node3-1 (phandle=13)
785
__local_fixups__
786
<references>
787
__symbols__
788
<symbols>
789
*/
790
let mut fdt = Fdt::new(&[]);
791
let root = fdt.root_mut();
792
793
let node1 = root.subnode_mut("node1")?;
794
node1.set_prop(PHANDLE_PROP, 1u32)?;
795
let node11 = node1.subnode_mut("node1-1")?;
796
node11.set_prop(PHANDLE_PROP, 2u32)?;
797
let node111 = node11.subnode_mut("node1-1-1")?;
798
node111.set_prop(PHANDLE_PROP, 3u32)?;
799
let node112 = node11.subnode_mut("node1-1-2")?;
800
node112.set_prop(PHANDLE_PROP, 4u32)?;
801
let node12 = node1.subnode_mut("node1-2")?;
802
node12.set_prop(PHANDLE_PROP, 5u32)?;
803
let node121 = node12.subnode_mut("node1-2-1")?;
804
node121.set_prop(PHANDLE_PROP, 6u32)?;
805
let node2 = root.subnode_mut("node2")?;
806
node2.set_prop(PHANDLE_PROP, 7u32)?;
807
let node21 = node2.subnode_mut("node2-1")?;
808
node21.set_prop(PHANDLE_PROP, 8u32)?;
809
let node22 = node2.subnode_mut("node2-2")?;
810
node22.set_prop(PHANDLE_PROP, 9u32)?;
811
let node23 = node2.subnode_mut("node2-3")?;
812
node23.set_prop(PHANDLE_PROP, 10u32)?;
813
let node231 = node23.subnode_mut("node2-3-1")?;
814
node231.set_prop(PHANDLE_PROP, 11u32)?;
815
let node3 = root.subnode_mut("node3")?;
816
node3.set_prop(PHANDLE_PROP, 12u32)?;
817
let node31 = node3.subnode_mut("node3-1")?;
818
node31.set_prop(PHANDLE_PROP, 13u32)?;
819
820
let symbols = root.subnode_mut(SYMBOLS_NODE)?;
821
symbols.set_prop("node1", "/node1")?;
822
symbols.set_prop("node1-1", "/node1/node1-1")?;
823
symbols.set_prop("node1-1-2", "/node1/node1-1/node1-1-2")?;
824
symbols.set_prop("node2", "/node2")?;
825
symbols.set_prop("node2-3-1", "/node2/node2-3/node2-3-1")?;
826
827
for (loc, phandle_val) in references {
828
let (path, pin) = parse_path_with_prop(loc)?;
829
// Write reference value in the tree sutrcture
830
let mut node = fdt
831
.get_node_mut(path.clone())
832
.ok_or_else(|| Error::InvalidPath(path.to_string()))?;
833
node.set_prop(&pin.0, *phandle_val)?;
834
835
// Write reference path to local fixups node
836
node = fdt.root_mut().subnode_mut(LOCAL_FIXUPS_NODE)?;
837
for nname in path.iter() {
838
node = node.subnode_mut(nname)?;
839
}
840
node.set_prop(&pin.0, 0u32)?;
841
}
842
843
Ok(fdt)
844
}
845
846
#[test]
847
fn fdt_collect_filter_roots() {
848
let fdt = make_fdt_with_local_refs(&[]).unwrap();
849
let (symbols, paths) = prepare_filtered_symbols::<&str>([], &fdt).unwrap();
850
assert!(symbols.is_empty());
851
assert!(paths.is_empty());
852
853
let (symbols, paths) = prepare_filtered_symbols(["node1"], &fdt).unwrap();
854
assert_eq!(symbols.len(), 1);
855
assert_eq!(paths.len(), 1);
856
assert!(symbols.contains("node1"));
857
assert!(paths.contains(&"/node1".parse().unwrap()));
858
859
let (symbols, paths) =
860
prepare_filtered_symbols(["node1", "node1-1", "node1"], &fdt).unwrap();
861
assert_eq!(symbols.len(), 2);
862
assert!(symbols.contains("node1") && symbols.contains("node1-1"));
863
assert!(
864
paths.contains(&"/node1".parse().unwrap())
865
&& paths.contains(&"/node1/node1-1".parse().unwrap())
866
);
867
868
prepare_filtered_symbols(["node1", "node1-1", "node1", "nosuchnode"], &fdt)
869
.expect_err("no symbol");
870
prepare_filtered_symbols(["node1-1-1"], &fdt).expect_err("no symbol");
871
prepare_filtered_symbols(["node1"], &Fdt::new(&[])).expect_err("no symbols node");
872
}
873
874
#[test]
875
fn fdt_collect_filtered_paths() {
876
// /node1/node1-2/node1-2-1:prop:0 => /node2/node2-3/node2-3-1 (phandle=11)
877
// /node1:prop:0 => /node3 (phandle=12)
878
let fdt = make_fdt_with_local_refs(&[
879
("/node1/node1-2/node1-2-1:prop:0", 11),
880
("/node1:prop:0", 12),
881
])
882
.unwrap();
883
let (_, paths) = prepare_filtered_symbols(["node1"], &fdt).unwrap();
884
let filtered = collect_all_filtered_paths(paths, &fdt).unwrap();
885
886
// This is referenced by the symbol that was given.
887
assert!(filtered.contains(&"/node1".parse().unwrap()));
888
// This is referenced by the phandle value stored in the property.
889
assert!(filtered.contains(&"/node3".parse().unwrap()));
890
// References that appeart in the subtree of the filtered node are not included.
891
assert!(!filtered.contains(&"/node2/node2-3/node2-3-1".parse().unwrap()));
892
}
893
894
#[test]
895
fn fdt_collect_filtered_paths_circular() {
896
// /node1:prop:0 => /node2/node2-3/node2-3-1 (phandle=11)
897
// /node2/node2-3:prop:0 => /node1/node1-1 (phandle=2)
898
let fdt = make_fdt_with_local_refs(&[("/node1:prop:0", 11), ("/node2/node2-3:prop:0", 2)])
899
.unwrap();
900
let (_, paths) = prepare_filtered_symbols(["node1-1"], &fdt).unwrap();
901
let filtered = collect_all_filtered_paths(paths, &fdt).unwrap();
902
903
// This is referenced by the symbol that was given.
904
assert!(filtered.contains(&"/node1/node1-1".parse().unwrap()));
905
// This is referenced by a parent node of the given symbol.
906
assert!(filtered.contains(&"/node2/node2-3/node2-3-1".parse().unwrap()));
907
// Above two paths cover all references
908
assert_eq!(filtered.len(), 2);
909
}
910
911
#[test]
912
fn fdt_collect_filtered_paths_dangling() {
913
// /node1:prop:0 => /node2/node2-3/node2-3-1 (phandle=11)
914
// /node2/node2-3:prop:0 => dangling phandle=200
915
let fdt =
916
make_fdt_with_local_refs(&[("/node1:prop:0", 11), ("/node2/node2-3:prop:0", 200)])
917
.unwrap();
918
let (_, paths) = prepare_filtered_symbols(["node1"], &fdt).unwrap();
919
collect_all_filtered_paths(paths, &fdt).expect_err("dangling phandle");
920
}
921
922
#[test]
923
fn fdt_collect_filtered_paths_minimal() {
924
// /node1:prop:0 => /node3/node3-1 (phandle=13)
925
// /node1/node1-1:prop:0 => /node1/node1-1/node1-1-2 (phandle=4)
926
// /node1/node1-1/node1-1-2:prop:0 => /node1 (phandle=1)
927
// /node3/node3-1:prop:0 => /node3 (phandle=12)
928
let fdt = make_fdt_with_local_refs(&[
929
("/node1:prop:0", 13),
930
("/node1/node1-1:prop:0", 4),
931
("/node1/node1-1/node1-1-2:prop:0", 1),
932
("/node3/node3-1:prop:0", 12),
933
])
934
.unwrap();
935
let (_, paths) = prepare_filtered_symbols(["node1"], &fdt).unwrap();
936
let filtered = collect_all_filtered_paths(paths, &fdt).unwrap();
937
938
assert!(filtered.contains(&"/node1".parse().unwrap()));
939
assert!(filtered.contains(&"/node3".parse().unwrap()));
940
// Above two paths cover all references
941
assert_eq!(filtered.len(), 2);
942
}
943
944
fn count_nodes(root: &FdtNode) -> usize {
945
let mut count = 1;
946
for s in root.iter_subnodes() {
947
count += count_nodes(s);
948
}
949
count
950
}
951
952
#[test]
953
fn fdt_do_filter_simple() {
954
let l1 = "/node1";
955
let l2 = "/node2";
956
let l3 = "/node3";
957
let fdt = &mut make_fdt_with_local_refs(&[]).unwrap();
958
959
do_overlay_filter([].into(), fdt);
960
assert!(fdt.get_node(l1).is_some());
961
assert!(fdt.get_node(l2).is_some());
962
assert!(fdt.get_node(l3).is_some());
963
964
do_overlay_filter([l1.try_into().unwrap(), l2.try_into().unwrap()].into(), fdt);
965
assert!(fdt.get_node(l1).is_some());
966
assert!(fdt.get_node(l2).is_some());
967
assert!(fdt.get_node(l3).is_none());
968
}
969
970
#[test]
971
fn fdt_do_filter_subnodes() {
972
let l1: Path = "/node1/node1-1".parse().unwrap();
973
let fdt = &mut make_fdt_with_local_refs(&[]).unwrap();
974
975
do_overlay_filter([l1.clone()].into(), fdt);
976
assert!(fdt.get_node(l1).is_some());
977
assert_eq!(count_nodes(&fdt.root), 3);
978
}
979
980
#[test]
981
fn fdt_do_filter_deep() {
982
let l1: Path = "/node1/node1-1/node1-1-1".parse().unwrap();
983
let l2: Path = "/node2/node2-2".parse().unwrap();
984
let l3: Path = "/node2/node2-3/node2-3-1".parse().unwrap();
985
let fdt = &mut make_fdt_with_local_refs(&[]).unwrap();
986
987
do_overlay_filter([l1.clone(), l2.clone(), l3.clone()].into(), fdt);
988
assert!(fdt.get_node(l1).is_some());
989
assert!(fdt.get_node(l2).is_some());
990
assert!(fdt.get_node(l3).is_some());
991
assert_eq!(count_nodes(&fdt.root), 8);
992
}
993
994
#[test]
995
fn fdt_offset_local_references() {
996
let file = include_bytes!("../test-files/local_refs.dtb").as_slice();
997
let mut fdt = load_fdt(file).unwrap();
998
999
let node = fdt.get_node("/fragment@0/__overlay__/node1").unwrap();
1000
assert_eq!(node.get_prop::<u32>("p2").unwrap(), 0x01);
1001
assert_eq!(node.get_prop::<u32>("p3").unwrap(), 0xaa);
1002
let node = fdt.get_node("/fragment@0/__overlay__/node1/node2").unwrap();
1003
assert_eq!(node.get_prop::<u32>("p1").unwrap(), 0xaa);
1004
assert_eq!(node.get_prop::<u32>("p2").unwrap(), 0x02);
1005
assert_eq!(node.get_prop::<u32>("p3").unwrap(), 0x03);
1006
let node = fdt.get_node("/fragment@0/__overlay__/node1/node3").unwrap();
1007
assert_eq!(node.get_prop::<u32>("p1").unwrap(), 0x01);
1008
1009
update_local_refs(&mut fdt, 5).unwrap();
1010
let node = fdt.get_node("/fragment@0/__overlay__/node1").unwrap();
1011
assert_eq!(node.get_prop::<u32>("p2").unwrap(), 0x06);
1012
assert_eq!(node.get_prop::<u32>("p3").unwrap(), 0xaa);
1013
let node = fdt.get_node("/fragment@0/__overlay__/node1/node2").unwrap();
1014
assert_eq!(node.get_prop::<u32>("p1").unwrap(), 0xaa);
1015
assert_eq!(node.get_prop::<u32>("p2").unwrap(), 0x07);
1016
assert_eq!(node.get_prop::<u32>("p3").unwrap(), 0x08);
1017
let node = fdt.get_node("/fragment@0/__overlay__/node1/node3").unwrap();
1018
assert_eq!(node.get_prop::<u32>("p1").unwrap(), 0x06);
1019
}
1020
1021
#[test]
1022
fn fdt_collect_symbols() {
1023
let base =
1024
load_fdt(include_bytes!("../test-files/external_refs_base.dtb").as_slice()).unwrap();
1025
let mut overlay =
1026
load_fdt(include_bytes!("../test-files/external_refs_overlay.dtb").as_slice()).unwrap();
1027
let paths = [
1028
"/fragment@0/__overlay__/node1:p2:0",
1029
"/fragment@0/__overlay__/node1/node2:p3:4",
1030
"/fragment@0/__overlay__/node1/node3:p1:0",
1031
];
1032
for p in paths.iter() {
1033
let (path, pin) = parse_path_with_prop(p).unwrap();
1034
let node = overlay.get_node(path).unwrap();
1035
let ref_val = node.phandle_at_offset(&pin.0, pin.1 as usize).unwrap();
1036
assert_eq!(ref_val, 0xffffffff);
1037
}
1038
1039
apply_external_fixups(&base, &mut overlay).unwrap();
1040
for (p, exp_val) in paths.iter().zip([1u32, 2u32, 2u32].into_iter()) {
1041
let (path, pin) = parse_path_with_prop(p).unwrap();
1042
let node = overlay.get_node(path).unwrap();
1043
let ref_val = node.phandle_at_offset(&pin.0, pin.1 as usize).unwrap();
1044
assert_eq!(ref_val, exp_val);
1045
}
1046
}
1047
1048
#[test]
1049
fn fdt_apply_overlay_complete() {
1050
let mut base = load_fdt(include_bytes!("../test-files/base.dtb").as_slice()).unwrap();
1051
assert_eq!(count_nodes(&base.root), 7);
1052
1053
let overlay = load_fdt(include_bytes!("../test-files/overlay.dtb").as_slice()).unwrap();
1054
apply_overlay(&mut base, overlay, ["mydev"]).unwrap();
1055
assert!(base.get_node("/mydev@8000000").is_some());
1056
assert!(base.get_node("/mydev@8000000/devnode1").is_none());
1057
assert!(base.get_node("/mydev@8001000").is_none());
1058
assert_eq!(count_nodes(&base.root), 8);
1059
1060
let overlay = load_fdt(include_bytes!("../test-files/overlay.dtb").as_slice()).unwrap();
1061
apply_overlay(&mut base, overlay, ["mydev"]).unwrap();
1062
assert!(base.get_node("/mydev@8000000").is_some());
1063
assert!(base.get_node("/mydev@8001000").is_none());
1064
assert_eq!(count_nodes(&base.root), 8);
1065
1066
let overlay = load_fdt(include_bytes!("../test-files/overlay.dtb").as_slice()).unwrap();
1067
apply_overlay(&mut base, overlay, ["mydev2"]).unwrap();
1068
assert!(base.get_node("/mydev@8000000").is_some());
1069
assert!(base.get_node("/mydev@8001000").is_some());
1070
assert!(base.get_node("/mydev@8000000/devnode1").is_none());
1071
assert!(base.get_node("/mydev@8001000/devnode1").is_none());
1072
assert_eq!(count_nodes(&base.root), 9);
1073
}
1074
1075
#[test]
1076
fn fdt_overlay_filter_with_dependencies() {
1077
let mut base = Fdt::new(&[]);
1078
let overlay =
1079
load_fdt(include_bytes!("../test-files/overlay_deps.dtb").as_slice()).unwrap();
1080
apply_overlay(&mut base, overlay, ["dev2"]).unwrap();
1081
assert_eq!(count_nodes(&base.root), 6);
1082
1083
let n = base.get_node("/n0-1").unwrap();
1084
assert_eq!(n.get_prop::<u32>("prop1"), Some(1));
1085
1086
assert!(base.get_node("/no-1/n2").is_none());
1087
let n = base.get_node("/n0-1/n1").unwrap();
1088
assert_eq!(n.get_prop::<u32>("prop1"), Some(2));
1089
1090
let n = base.get_node("/n0-2").unwrap();
1091
assert_eq!(n.get_prop::<u32>("prop1"), Some(4));
1092
1093
assert!(base.get_node("/n0-2/n2").is_none());
1094
let n = base.get_node("/n0-2/n1").unwrap();
1095
assert_eq!(n.get_prop::<u32>("prop1"), Some(5));
1096
}
1097
1098
#[test]
1099
fn fdt_overlay_skips_children() {
1100
let mut base =
1101
load_fdt(include_bytes!("../test-files/external_refs_base.dtb").as_slice()).unwrap();
1102
let overlay =
1103
load_fdt(include_bytes!("../test-files/external_refs_overlay.dtb").as_slice()).unwrap();
1104
apply_overlay(&mut base, overlay, ["n1"]).unwrap();
1105
assert_eq!(count_nodes(&base.root), 6);
1106
assert!(base.get_node("/node1").is_some());
1107
assert!(base.get_node("/node1/node2").is_none());
1108
assert!(base.get_node("/node1/node3").is_none());
1109
}
1110
}
1111
1112