Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
142 changes: 138 additions & 4 deletions src/model/overlay.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@ use alloc::vec::Vec;

use crate::error::OverlayError;
use crate::fdt::Fdt;
use crate::model::{DeviceTree, DeviceTreeNode};
use crate::model::{DeviceTree, DeviceTreeNode, DeviceTreeProperty};
use crate::overlay::{
Fragment, FragmentTarget, NODE_LOCAL_FIXUPS, NODE_SYMBOLS, Overlay, PHANDLE_PROPS, get_phandle,
FixupLocation, Fragment, FragmentTarget, NODE_FIXUPS, NODE_LOCAL_FIXUPS, NODE_SYMBOLS, Overlay,
PHANDLE_PROPS, get_phandle,
};
use crate::{Node, Property};

Expand All @@ -27,7 +28,6 @@ use crate::{Node, Property};
/// It caches phandle mappings to avoid repeatedly traversing the entire base
/// tree.
#[derive(Debug)]
#[allow(unused)]
pub struct OverlayApplier<'a> {
base: &'a mut DeviceTree,
phandles: BTreeMap<u32, String>,
Expand Down Expand Up @@ -95,6 +95,12 @@ impl<'a> OverlayApplier<'a> {
/// overlay data.
pub fn apply_overlay_tree(&mut self, mut overlay: DeviceTree) -> Result<(), OverlayError> {
relocate_local_phandles(&mut overlay, self.max_phandle)?;
resolve_external_fixups(
self.base,
&mut overlay,
&mut self.max_phandle,
&mut self.phandles,
)?;

let overlay_view = Overlay {
node: &overlay.root,
Expand All @@ -107,13 +113,73 @@ impl<'a> OverlayApplier<'a> {
fragment_targets.push((frag_name, target_path));
}

if let Some(symbols_node) = overlay.root.remove_child(NODE_SYMBOLS) {
let base_symbols = if let Some(base_symbols) = self.base.root.child_mut(NODE_SYMBOLS) {
base_symbols
} else {
self.base
.root
.add_child_mut(DeviceTreeNode::new(NODE_SYMBOLS)?)
};

for sym_prop in symbols_node.properties() {
Self::rewrite_symbol(&fragment_targets, base_symbols, sym_prop)?;
}
}

for (frag_name, target_path) in &fragment_targets {
self.merge_fragment(&mut overlay, frag_name, target_path)?;
}

Ok(())
}

/// Rewrites a symbol defined in the overlay to the base DT.
fn rewrite_symbol(
fragment_targets: &[(String, String)],
base_symbols: &mut DeviceTreeNode,
sym_prop: &DeviceTreeProperty,
) -> Result<(), OverlayError> {
let sym_val = sym_prop.value();
let mut new_val_str: Vec<u8> = Vec::new();

for (frag_name, target_path) in fragment_targets {
// symbols referring to /fragment@.../__overlay__/<node> should be rewritten
// to just /<node>
if let Some(subpath) = sym_val
.strip_prefix(b"/")
.and_then(|s| s.strip_prefix(frag_name.as_bytes()))
.and_then(|s| s.strip_prefix(b"/__overlay__"))
{
if target_path == "/" {
if subpath.is_empty() {
new_val_str.push(b'/');
} else {
new_val_str.extend_from_slice(subpath);
}
} else {
new_val_str.extend_from_slice(target_path.as_bytes());
new_val_str.extend_from_slice(subpath);
}
break;
}
}

if new_val_str.is_empty() {
// use the original symbol path if the overlay symbol didn't point to
// a fragment inside the overlay
new_val_str.extend_from_slice(sym_val);
}

if !new_val_str.ends_with(b"\0") {
new_val_str.push(b'\0');
}

base_symbols.add_property(DeviceTreeProperty::new(sym_prop.name(), new_val_str)?);

Ok(())
}

fn resolve_fragment_target_path(
&self,
fragment: Fragment<&DeviceTreeNode>,
Expand Down Expand Up @@ -250,7 +316,6 @@ fn relocate_local_phandles(

/// Relocates all local phandles defined in `overlay` and updates internal
/// references specified by `/__local_fixups__` by adding `phandle_offset`.
#[allow(unused)]
fn offset_node_phandles(node: &mut DeviceTreeNode, offset: u32) -> Result<(), OverlayError> {
for prop_name in PHANDLE_PROPS {
if let Some(prop) = node.property_mut(prop_name)
Expand Down Expand Up @@ -337,6 +402,75 @@ fn update_local_references(
Ok(())
}

/// Resolves external symbol fixups (`/__fixups__`) in `overlay` against `base`
/// tree symbols, allocating new phandles in `base` if needed starting from
/// `max_phandle`.
fn resolve_external_fixups(
base: &mut DeviceTree,
overlay: &mut DeviceTree,
max_phandle: &mut u32,
phandles_cache: &mut BTreeMap<u32, String>,
) -> Result<(), OverlayError> {
let Some(fixup_node) = overlay.root.remove_child(NODE_FIXUPS) else {
return Ok(());
};

for fixup_prop in fixup_node.properties() {
let symbol_name = fixup_prop.name();

let target_path = base
.root
.child(NODE_SYMBOLS)
.and_then(|sym| sym.property(symbol_name))
.ok_or_else(|| OverlayError::UnresolvedSymbol(symbol_name.to_string()))?
.as_str()
.map_err(|_| OverlayError::UnresolvedSymbol(symbol_name.to_string()))?
.to_string();

let target_node = base
.find_node_mut(&target_path)
.ok_or_else(|| OverlayError::TargetNotFound(target_path.clone()))?;

let phandle = if let Some(p) = get_phandle(target_node) {
p
} else {
*max_phandle = max_phandle
.checked_add(1)
.filter(|&v| v != u32::MAX)
.ok_or(OverlayError::PhandleOverflow)?;
let new_p = *max_phandle;
target_node.add_property(DeviceTreeProperty::new(
"phandle",
new_p.to_be_bytes().to_vec(),
)?);
phandles_cache.insert(new_p, target_path.clone());
new_p
};

for loc_str in fixup_prop.as_str_list() {
let loc = FixupLocation::parse(loc_str)?;
let overlay_node = overlay
.find_node_mut(loc.node_path)
.ok_or(OverlayError::InvalidFixupLocation)?;
let target_prop = overlay_node
.property_mut(loc.property_name)
.ok_or(OverlayError::InvalidFixupLocation)?;
let end_offset = loc
.offset
.checked_add(4)
.ok_or(OverlayError::InvalidFixupLocation)?;
if end_offset > (&*target_prop).value().len() {
return Err(OverlayError::InvalidFixupLocation);
}
let mut new_value = (&*target_prop).value().to_owned();
new_value[loc.offset..end_offset].copy_from_slice(&phandle.to_be_bytes());
target_prop.set_value(new_value);
}
}

Ok(())
}

#[cfg(test)]
mod tests {
use alloc::vec;
Expand Down
2 changes: 0 additions & 2 deletions src/overlay.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@
//! overlay inspection works uniformly across both read-only (`FdtNode`) and
//! in-memory (`DeviceTreeNode`) representations.

#![allow(unused)]

use core::ops::Deref;

use crate::error::OverlayError;
Expand Down
17 changes: 17 additions & 0 deletions tests/overlay.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,23 @@ fn apply_overlay_with_local_fixups() {
assert_eq!(dev.property("clocks").unwrap().value(), &[0, 0, 0, 2]);
}

#[test]
fn apply_overlay_with_external_fixups_and_symbols() {
let mut base = apply_overlay!(
"overlay_external_symbols_base.dtb",
"overlay_external_symbols_overlay.dtb"
);

let uart = base.find_node_mut("/soc/uart@1000").unwrap();
assert_eq!(uart.property("status").unwrap().as_str(), Ok("okay"));

let base_sym = base.root.child("__symbols__").unwrap();
assert_eq!(
base_sym.property("uart0").unwrap().as_str(),
Ok("/soc/uart@1000")
);
}

#[test]
fn apply_overlay_dependent_fragments() {
// Just verify it applies without error
Expand Down