xref: /aosp_15_r20/external/crosvm/cros_fdt/src/overlay.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
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.
prepare_filtered_symbols<T: AsRef<str>>( start_symbols: impl std::iter::IntoIterator<Item = T>, fdt: &Fdt, ) -> Result<(HashSet<String>, Vec<Path>)>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`.
collect_phandle_refs_from_props(fixup_node: &FdtNode, tree_node: &FdtNode) -> Result<Vec<u32>>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.
collect_all_references_by_path( path: &Path, root: &FdtNode, local_fixups_node: &FdtNode, ) -> Result<HashSet<u32>>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.
get_all_phandles(fdt: &Fdt) -> BTreeMap<u32, Path>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.
minimize_paths(paths: &mut Vec<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.
collect_all_filtered_paths(mut start_paths: Vec<Path>, fdt: &Fdt) -> Result<Vec<Path>>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.
do_overlay_filter(filtered_paths: Vec<Path>, overlay: &mut Fdt)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.
get_node_phandle(node: &FdtNode) -> Option<u32>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.
get_max_phandle(root_node: &FdtNode) -> u32177 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.
offset_phandle_prop(node: &mut FdtNode, propname: &str, delta: u32) -> Result<()>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.
offset_phandle_values(fdt: &mut Fdt, delta: u32) -> Result<()>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)
collect_local_fixup_paths(fdt: &Fdt) -> Result<BTreeMap<Path, Vec<PhandlePin>>>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 
update_local_phandle_propvals( fdt: &mut Fdt, paths: BTreeMap<Path, Vec<PhandlePin>>, delta: u32, ) -> Result<()>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 
update_local_refs(fdt: &mut Fdt, delta: u32) -> Result<()>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.
get_symbol_path_and_phandle(symbol: &str, fdt: &Fdt) -> Option<(String, u32)>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.
apply_external_fixups(base: &Fdt, overlay: &mut Fdt) -> Result<()>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.
overlay_node_pair(base_node: &mut FdtNode, overlay_node: &FdtNode) -> Result<()>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.
overlay_fragment(fragment_node: &FdtNode, base: &mut Fdt) -> Result<()>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").
extract_fragment_and_subpath(path: &Path) -> Result<(&str, String)>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 
update_base_symbols( base: &mut Fdt, overlay: &Fdt, filtered_symbols: HashSet<String>, ) -> Result<()>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.
merge_resvmem(base: &mut Vec<FdtReserveEntry>, new_entries: Vec<FdtReserveEntry>)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.
apply_overlay<T: AsRef<str>>( base: &mut Fdt, mut overlay: Fdt, filter_symbols: impl std::iter::IntoIterator<Item = T>, ) -> Result<()>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 
load_fdt(mut reader: impl std::io::Read) -> Result<Fdt>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]
fdt_merge_resvmem()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]
fdt_find_phandle_single()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]
fdt_find_phandle_none()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]
fdt_find_phandle_deprecated()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]
fdt_find_max_phandle()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]
fdt_offset_phandles()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]
fdt_collect_local_references()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 
make_fragment0() -> FdtNode622     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 
make_fragment1() -> FdtNode639     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]
fdt_test_overlay_nodes()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]
fdt_overlay_symbols()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]
fdt_overlay_filtered_symbols()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 
make_fdt_with_local_refs(references: &[(&str, u32)]) -> Result<Fdt>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]
fdt_collect_filter_roots()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]
fdt_collect_filtered_paths()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]
fdt_collect_filtered_paths_circular()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]
fdt_collect_filtered_paths_dangling()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]
fdt_collect_filtered_paths_minimal()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 
count_nodes(root: &FdtNode) -> usize944     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]
fdt_do_filter_simple()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]
fdt_do_filter_subnodes()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]
fdt_do_filter_deep()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]
fdt_offset_local_references()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]
fdt_collect_symbols()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]
fdt_apply_overlay_complete()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]
fdt_overlay_filter_with_dependencies()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]
fdt_overlay_skips_children()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