Skip to content
Merged
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
6 changes: 2 additions & 4 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ jobs:
uses: actions/checkout@v5
with:
fetch-depth: 0
token: ${{ secrets.RELEASE_PLZ_TOKEN }}
persist-credentials: false
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
Expand All @@ -32,7 +31,7 @@ jobs:
with:
command: release
env:
GITHUB_TOKEN: ${{ secrets.RELEASE_PLZ_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}

release-plz-pr:
Expand All @@ -53,7 +52,6 @@ jobs:
uses: actions/checkout@v5
with:
fetch-depth: 0
token: ${{ secrets.RELEASE_PLZ_TOKEN }}
persist-credentials: false
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
Expand All @@ -62,5 +60,5 @@ jobs:
with:
command: release-pr
env:
GITHUB_TOKEN: ${{ secrets.RELEASE_PLZ_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
6 changes: 6 additions & 0 deletions dtb-file/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use core::ops::Deref;

const TEST_RPI_4_FDT: &[u8] = include_bytes!("dtb/bcm2711-rpi-4-b.dtb");
const TEST_ORANGEPI_5_PLUS_FDT: &[u8] = include_bytes!("dtb/orangepi5plus.dtb");
const TEST_PHYTIUM_FDT: &[u8] = include_bytes!("dtb/phytium.dtb");
const TEST_QEMU_FDT: &[u8] = include_bytes!("dtb/qemu_pci.dtb");
const TEST_3568_FDT: &[u8] = include_bytes!("dtb/rk3568-firefly-roc-pc-se.dtb");
Expand All @@ -17,6 +18,11 @@ pub fn fdt_rpi_4b() -> Align4Vec {
Align4Vec::new(TEST_RPI_4_FDT)
}

/// Returns the FDT data for Orange Pi 5 Plus.
pub fn fdt_orangepi_5plus() -> Align4Vec {
Align4Vec::new(TEST_ORANGEPI_5_PLUS_FDT)
}

/// Returns the FDT data for Phytium platform.
pub fn fdt_phytium() -> Align4Vec {
Align4Vec::new(TEST_PHYTIUM_FDT)
Expand Down
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
53 changes: 53 additions & 0 deletions fdt-edit/tests/fdt.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,59 @@
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_interrupt_parent_inheritance_orangepi5plus() {
let raw_data = fdt_orangepi_5plus();
let fdt = Fdt::from_bytes(&raw_data).unwrap();

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

let mmc = fdt.get_by_path("/mmc@fe2e0000").unwrap();
assert_eq!(mmc.as_node().interrupt_parent(), None);
assert_eq!(mmc.interrupt_parent(), Some(Phandle::from(0x01)));

let irq_parent = fdt.get_by_phandle(Phandle::from(0x01)).unwrap();
assert_eq!(irq_parent.path(), "/interrupt-controller@fe600000");
}

#[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()),
Copy link

Copilot AI Mar 9, 2026

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