cros_fdt/
overlay.rs

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
7use std::collections::BTreeMap;
8use std::collections::HashSet;
9use std::collections::VecDeque;
10
11use crate::fdt::Error;
12use crate::fdt::Fdt;
13use crate::fdt::FdtNode;
14use crate::fdt::FdtReserveEntry;
15use crate::fdt::Result;
16use crate::path::parse_path_with_prop;
17use crate::path::Path;
18use crate::path::PhandlePin;
19use crate::path::PATH_SEP;
20
21const PHANDLE_PROP: &str = "phandle";
22const LINUX_PHANDLE_PROP: &str = "linux,phandle";
23const TARGET_PATH_PROP: &str = "target-path";
24const TARGET_PROP: &str = "target";
25const LOCAL_FIXUPS_NODE: &str = "__local_fixups__";
26const OVERLAY_NODE: &str = "__overlay__";
27const SYMBOLS_NODE: &str = "__symbols__";
28const FIXUPS_NODE: &str = "__fixups__";
29const 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.
33fn 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`.
49fn 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.
64fn 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.
92fn 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.
109fn 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.
116fn 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.
149fn 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.
171fn 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.
177fn 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.
189fn 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.
205fn 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)
221fn 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
255fn 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
275fn 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.
281fn 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.
290fn 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.
336fn 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.
348fn 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").
368fn 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
384fn 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.
427fn 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.
455pub 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)]
493mod 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}