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