diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6489056..87d44fe 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -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 @@ -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: @@ -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 @@ -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 }} diff --git a/dtb-file/src/lib.rs b/dtb-file/src/lib.rs index b520dc4..dce9fae 100644 --- a/dtb-file/src/lib.rs +++ b/dtb-file/src/lib.rs @@ -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"); @@ -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) diff --git a/fdt-edit/src/node/mod.rs b/fdt-edit/src/node/mod.rs index 8f45f68..e93c949 100644 --- a/fdt-edit/src/node/mod.rs +++ b/fdt-edit/src/node/mod.rs @@ -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 { self.get_property("interrupt-parent") .and_then(|prop| prop.get_u32()) diff --git a/fdt-edit/src/node/view/generic.rs b/fdt-edit/src/node/view/generic.rs index f2feb6b..8cdd913 100644 --- a/fdt-edit/src/node/view/generic.rs +++ b/fdt-edit/src/node/view/generic.rs @@ -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}; @@ -28,6 +28,11 @@ impl<'a> NodeGeneric<'a> { pub fn regs(&self) -> Vec { self.inner.regs() } + + /// Returns the effective `interrupt-parent`, inheriting from ancestors. + pub fn interrupt_parent(&self) -> Option { + self.inner.interrupt_parent() + } } impl<'a> ViewOp<'a> for NodeGeneric<'a> { diff --git a/fdt-edit/src/node/view/mod.rs b/fdt-edit/src/node/view/mod.rs index e8dc169..b1e85c8 100644 --- a/fdt-edit/src/node/view/mod.rs +++ b/fdt-edit/src/node/view/mod.rs @@ -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}; @@ -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 { + 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. @@ -379,6 +395,11 @@ impl<'a> NodeType<'a> { pub fn regs(&self) -> Vec { self.as_view().regs() } + + /// Returns the effective `interrupt-parent`, inheriting from ancestors. + pub fn interrupt_parent(&self) -> Option { + self.as_view().interrupt_parent() + } } impl core::fmt::Display for NodeType<'_> { diff --git a/fdt-edit/tests/fdt.rs b/fdt-edit/tests/fdt.rs index 2c9658e..5db0a5c 100644 --- a/fdt-edit/tests/fdt.rs +++ b/fdt-edit/tests/fdt.rs @@ -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(); diff --git a/fdt-raw/src/iter.rs b/fdt-raw/src/iter.rs index 0a1f344..c385c14 100644 --- a/fdt-raw/src/iter.rs +++ b/fdt-raw/src/iter.rs @@ -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()), }; let _ = self.context_stack.push(child_context); diff --git a/fdt-raw/src/node/mod.rs b/fdt-raw/src/node/mod.rs index 75db6ae..395aa29 100644 --- a/fdt-raw/src/node/mod.rs +++ b/fdt-raw/src/node/mod.rs @@ -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}, }; @@ -40,6 +40,8 @@ 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, } impl Default for NodeContext { @@ -47,6 +49,7 @@ impl Default for NodeContext { NodeContext { address_cells: 2, size_cells: 1, + interrupt_parent: None, } } } @@ -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 { + 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 @@ -246,6 +256,7 @@ impl fmt::Debug for Node<'_> { pub(crate) struct ParsedProps { pub address_cells: Option, pub size_cells: Option, + pub interrupt_parent: Option, } /// State of a single node iteration. @@ -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)); + } _ => {} } } diff --git a/fdt-raw/tests/node.rs b/fdt-raw/tests/node.rs index 6319aaa..e83ce8c 100644 --- a/fdt-raw/tests/node.rs +++ b/fdt-raw/tests/node.rs @@ -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();