Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
2 changes: 1 addition & 1 deletion fdt-edit/src/node/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ impl Node {
.map(Phandle::from)
}

/// Returns the `interrupt-parent` property value.
/// Returns the local `interrupt-parent` property value.
pub fn interrupt_parent(&self) -> Option<Phandle> {
self.get_property("interrupt-parent")
.and_then(|prop| prop.get_u32())
Expand Down
7 changes: 6 additions & 1 deletion fdt-edit/src/node/view/generic.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Generic node view specialization.

use alloc::{string::String, vec::Vec};
use fdt_raw::RegInfo;
use fdt_raw::{Phandle, RegInfo};

use super::NodeView;
use crate::{Node, NodeId, RegFixed, ViewMutOp, ViewOp};
Expand All @@ -28,6 +28,11 @@ impl<'a> NodeGeneric<'a> {
pub fn regs(&self) -> Vec<RegFixed> {
self.inner.regs()
}

/// Returns the effective `interrupt-parent`, inheriting from ancestors.
pub fn interrupt_parent(&self) -> Option<Phandle> {
self.inner.interrupt_parent()
}
}

impl<'a> ViewOp<'a> for NodeGeneric<'a> {
Expand Down
21 changes: 21 additions & 0 deletions fdt-edit/src/node/view/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use core::fmt::Display;

use alloc::{string::String, vec::Vec};
use enum_dispatch::enum_dispatch;
use fdt_raw::Phandle;

use crate::{Fdt, Node, NodeId, Property, RangesEntry};

Expand Down Expand Up @@ -121,6 +122,21 @@ impl<'a> NodeView<'a> {
self.as_node().size_cells()
}

/// Returns the effective `interrupt-parent`, inheriting from ancestors.
pub fn interrupt_parent(&self) -> Option<Phandle> {
let mut current = Some(self.id);

while let Some(node_id) = current {
let node = self.fdt().node(node_id)?;
if let Some(phandle) = node.interrupt_parent() {
return Some(phandle);
}
current = self.fdt().parent_of(node_id);
}

None
}

/// Parses the `reg` property and returns corrected register entries.
///
/// Uses parent node's `ranges` property to translate bus addresses to CPU addresses.
Expand Down Expand Up @@ -379,6 +395,11 @@ impl<'a> NodeType<'a> {
pub fn regs(&self) -> Vec<RegFixed> {
self.as_view().regs()
}

/// Returns the effective `interrupt-parent`, inheriting from ancestors.
pub fn interrupt_parent(&self) -> Option<Phandle> {
self.as_view().interrupt_parent()
}
}

impl core::fmt::Display for NodeType<'_> {
Expand Down
36 changes: 36 additions & 0 deletions fdt-edit/tests/fdt.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,42 @@
use dtb_file::*;
use fdt_edit::*;

#[test]
fn test_interrupt_parent_inheritance() {
let mut fdt = Fdt::new();
let root_id = fdt.root_id();

fdt.node_mut(root_id).unwrap().set_property(Property::new(
"interrupt-parent",
0x10_u32.to_be_bytes().to_vec(),
));

let soc_id = fdt.add_node(root_id, Node::new("soc"));
fdt.add_node(soc_id, Node::new("uart@1000"));

let mut timer = Node::new("timer@2000");
timer.set_property(Property::new(
"interrupt-parent",
0x20_u32.to_be_bytes().to_vec(),
));
fdt.add_node(soc_id, timer);

let soc = fdt.get_by_path("/soc").unwrap();
assert_eq!(soc.as_node().interrupt_parent(), None);
assert_eq!(soc.interrupt_parent(), Some(Phandle::from(0x10)));

let uart = fdt.get_by_path("/soc/uart@1000").unwrap();
assert_eq!(uart.as_node().interrupt_parent(), None);
assert_eq!(uart.interrupt_parent(), Some(Phandle::from(0x10)));

let timer = fdt.get_by_path("/soc/timer@2000").unwrap();
assert_eq!(
timer.as_node().interrupt_parent(),
Some(Phandle::from(0x20))
);
assert_eq!(timer.interrupt_parent(), Some(Phandle::from(0x20)));
}

#[test]
fn test_iter_nodes() {
let raw_data = fdt_phytium();
Expand Down
3 changes: 3 additions & 0 deletions fdt-raw/src/iter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,9 @@ impl<'a> Iterator for FdtIter<'a> {
let child_context = NodeContext {
address_cells: node.address_cells,
size_cells: node.size_cells,
interrupt_parent: props
.interrupt_parent
.or(node.interrupt_parent()),

Copilot AI Mar 9, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In child_context, props.interrupt_parent.or(node.interrupt_parent()) evaluates node.interrupt_parent() eagerly, which re-scans the node’s properties even when props.interrupt_parent is already Some. This can add significant overhead while iterating large trees. Use a lazy alternative (e.g. or_else(|| node.interrupt_parent())) or compute from the already-available inherited context (e.g. props.interrupt_parent.or(self.current_context().interrupt_parent)) to avoid the extra scan.

Suggested change
.or(node.interrupt_parent()),
.or_else(|| node.interrupt_parent()),

Copilot uses AI. Check for mistakes.
};
let _ = self.context_stack.push(child_context);

Expand Down
17 changes: 16 additions & 1 deletion fdt-raw/src/node/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use core::{ffi::CStr, fmt::Debug};
use crate::Fdt;
use crate::fmt_utils;
use crate::{
FdtError, Token,
FdtError, Phandle, Token,
data::{Bytes, Reader, U32_SIZE},
};

Expand Down Expand Up @@ -40,13 +40,16 @@ pub(crate) struct NodeContext {
pub address_cells: u8,
/// Parent node's #size-cells (used for parsing current node's reg)
pub size_cells: u8,
/// Effective interrupt-parent inherited from ancestors
pub interrupt_parent: Option<Phandle>,
}

impl Default for NodeContext {
fn default() -> Self {
NodeContext {
address_cells: 2,
size_cells: 1,
interrupt_parent: None,
}
}
}
Expand Down Expand Up @@ -150,6 +153,13 @@ impl<'a> NodeBase<'a> {
.flat_map(|p| p.as_str_iter())
}

/// Returns the effective `interrupt-parent`, inheriting from ancestors.
pub fn interrupt_parent(&self) -> Option<Phandle> {
self.find_property("interrupt-parent")
.and_then(|prop| prop.as_interrupt_parent())
.or(self.context.interrupt_parent)
}

/// Returns the full path of this node as a string.
///
/// For the root node, returns "/". For other nodes, returns the
Expand Down Expand Up @@ -246,6 +256,7 @@ impl fmt::Debug for Node<'_> {
pub(crate) struct ParsedProps {
pub address_cells: Option<u8>,
pub size_cells: Option<u8>,
pub interrupt_parent: Option<Phandle>,
}

/// State of a single node iteration.
Expand Down Expand Up @@ -453,6 +464,10 @@ impl<'a> OneNodeIter<'a> {
self.parsed_props.size_cells =
Some(Self::read_u32_be(&prop_data, 0) as u8);
}
"interrupt-parent" if len == 4 => {
self.parsed_props.interrupt_parent =
Some(Phandle::from(Self::read_u32_be(&prop_data, 0) as u32));
}
_ => {}
}
}
Expand Down
14 changes: 14 additions & 0 deletions fdt-raw/tests/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,20 @@ fn test_node_properties() {
info!("All property types validated successfully!");
}

#[test]
fn test_interrupt_parent_inheritance() {
init_logging();
let raw = fdt_qemu();
let fdt = Fdt::from_bytes(&raw).unwrap();

let root = fdt.find_by_path("/").unwrap();
assert_eq!(root.interrupt_parent(), Some(Phandle::from(0x8002)));

let chosen = fdt.find_by_path("/chosen").unwrap();
assert!(chosen.find_property("interrupt-parent").is_none());
assert_eq!(chosen.interrupt_parent(), Some(Phandle::from(0x8002)));
}

#[test]
fn test_reg_parsing() {
init_logging();
Expand Down