From 6f7971c234a2ba16435ee4bb0f609920d32590d0 Mon Sep 17 00:00:00 2001 From: Wesley Norris Date: Tue, 11 Jun 2024 21:38:25 -0400 Subject: [PATCH 01/38] wip commit --- Cargo.toml | 2 +- src/lib.rs | 241 ++++++++++++-------- src/{node.rs => node_old.rs} | 142 ++++++------ src/nodes.rs | 284 +++++++++++++++++++++++ src/parsing.rs | 421 +++++++++++++++++++++++++++++++---- src/parsing/aligned.rs | 120 ++++++++++ src/parsing/unaligned.rs | 122 ++++++++++ src/properties.rs | 55 +++++ src/standard_nodes.rs | 32 +-- src/tests.rs | 90 +++++--- src/util.rs | 5 + 11 files changed, 1262 insertions(+), 252 deletions(-) rename src/{node.rs => node_old.rs} (81%) create mode 100644 src/nodes.rs create mode 100644 src/parsing/aligned.rs create mode 100644 src/parsing/unaligned.rs create mode 100644 src/properties.rs create mode 100644 src/util.rs diff --git a/Cargo.toml b/Cargo.toml index 4db41e4..1884e96 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ name = "fdt" version = "0.2.0-alpha1" authors = ["Wesley Norris "] -edition = "2018" +edition = "2021" repository = "https://github.com/repnop/fdt" diff --git a/src/lib.rs b/src/lib.rs index c650024..76ff7a3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -35,7 +35,7 @@ //! } //! //! if let Some(stdout) = chosen.stdout() { -//! println!("It would write stdout to: {}", stdout.node().name); +//! println!("It would write stdout to: {}", stdout.name()); //! } //! //! let soc = fdt.find_node("/soc"); @@ -54,27 +54,35 @@ #[cfg(test)] mod tests; -pub mod node; +mod nodes; mod parsing; -pub mod standard_nodes; - -#[cfg(feature = "pretty-printing")] mod pretty_print; +pub mod properties; +pub mod standard_nodes; +mod util; use node::MemoryReservation; -use parsing::{BigEndianU32, CStr, FdtData}; +use parsing::{ + aligned::AlignedParser, unaligned::UnalignedParser, BigEndianU32, FdtData, NoPanic, Panic, + PanicMode, ParseError, Parser, +}; use standard_nodes::{Aliases, Chosen, Cpu, Memory, MemoryRange, MemoryRegion, Root}; /// Possible errors when attempting to create an `Fdt` -#[derive(Debug, Clone, Copy, PartialEq)] +#[derive(Debug, Clone, Copy)] pub enum FdtError { /// The FDT had an invalid magic value BadMagic, /// The given pointer was null BadPtr, - /// The slice passed in was too small to fit the given total size of the FDT - /// structure - BufferTooSmall, + /// An error was encountered during parsing + ParseError(ParseError), +} + +impl From for FdtError { + fn from(value: ParseError) -> Self { + Self::ParseError(value) + } } impl core::fmt::Display for FdtError { @@ -82,9 +90,7 @@ impl core::fmt::Display for FdtError { match self { FdtError::BadMagic => write!(f, "bad FDT magic value"), FdtError::BadPtr => write!(f, "an invalid pointer was passed"), - FdtError::BufferTooSmall => { - write!(f, "the given buffer was too small to contain a FDT header") - } + FdtError::ParseError(e) => core::fmt::Display::fmt(e, f), } } } @@ -95,20 +101,23 @@ impl core::fmt::Display for FdtError { /// print any useful information, if you would like a best-effort tree print /// which looks similar to `dtc`'s output, enable the `pretty-printing` feature #[derive(Clone, Copy)] -pub struct Fdt<'a> { - data: &'a [u8], +pub struct Fdt<'a, P: Parser<'a>, Mode: PanicMode = Panic> { + parser: P, + strings: &'a [u8], + structs: &'a [P::Granularity], header: FdtHeader, + _mode: core::marker::PhantomData<*mut Mode>, } -impl core::fmt::Debug for Fdt<'_> { +impl<'a, P: Parser<'a>> core::fmt::Debug for Fdt<'a, P> { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - #[cfg(feature = "pretty-printing")] - pretty_print::print_node(f, self.root().node, 0)?; - - #[cfg(not(feature = "pretty-printing"))] - f.debug_struct("Fdt").finish_non_exhaustive()?; + f.debug_struct("Fdt").finish_non_exhaustive() + } +} - Ok(()) +impl<'a, P: Parser<'a>> core::fmt::Display for Fdt<'a, P> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + pretty_print::print_node(f, self.root().node, 0) } } @@ -116,113 +125,144 @@ impl core::fmt::Debug for Fdt<'_> { #[repr(C)] struct FdtHeader { /// FDT header magic - magic: BigEndianU32, + pub magic: u32, /// Total size in bytes of the FDT structure - totalsize: BigEndianU32, + pub total_size: u32, /// Offset in bytes from the start of the header to the structure block - off_dt_struct: BigEndianU32, + pub structs_offset: u32, /// Offset in bytes from the start of the header to the strings block - off_dt_strings: BigEndianU32, + pub strings_offset: u32, /// Offset in bytes from the start of the header to the memory reservation /// block - off_mem_rsvmap: BigEndianU32, + pub memory_reserve_map_offset: u32, /// FDT version - version: BigEndianU32, + pub version: u32, /// Last compatible FDT version - last_comp_version: BigEndianU32, + pub last_compatible_version: u32, /// System boot CPU ID - boot_cpuid_phys: BigEndianU32, + pub boot_cpuid: u32, /// Length in bytes of the strings block - size_dt_strings: BigEndianU32, + pub strings_size: u32, /// Length in bytes of the struct block - size_dt_struct: BigEndianU32, + pub structs_size: u32, } impl FdtHeader { fn valid_magic(&self) -> bool { - self.magic.get() == 0xd00dfeed + self.magic == 0xd00dfeed } +} - fn struct_range(&self) -> core::ops::Range { - let start = self.off_dt_struct.get() as usize; - let end = start + self.size_dt_struct.get() as usize; +impl<'a> Fdt<'a, UnalignedParser<'a>, Panic> { + /// Construct a new `Fdt` from a byte buffer + pub fn new_unaligned(data: &'a [u8]) -> Result { + let mut parser = UnalignedParser::new(data); + let header = parser.parse_header()?; + let strings = &data[header.strings_offset as usize..][..header.strings_size as usize]; + let structs = &data[header.structs_offset as usize..][..header.structs_size as usize]; - start..end + if !header.valid_magic() { + return Err(FdtError::BadMagic); + } else if data.len() < header.total_size as usize { + return Err(FdtError::ParseError(ParseError::UnexpectedEndOfData)); + } + + Ok(Self { header, parser, strings, structs, _mode: core::marker::PhantomData }) } - fn strings_range(&self) -> core::ops::Range { - let start = self.off_dt_strings.get() as usize; - let end = start + self.size_dt_strings.get() as usize; + /// # Safety + /// This function performs a read to verify the magic value. If the pointer + /// is invalid this can result in undefined behavior. + pub unsafe fn from_ptr_unaligned(ptr: *const u8) -> Result { + if ptr.is_null() { + return Err(FdtError::BadPtr); + } - start..end - } + let tmp_header = core::slice::from_raw_parts(ptr, core::mem::size_of::()); + let real_size = + usize::try_from(UnalignedParser::new(tmp_header).parse_header()?.total_size) + .map_err(|_| ParseError::NumericConversionError)?; - fn from_bytes(bytes: &mut FdtData<'_>) -> Option { - Some(Self { - magic: bytes.u32()?, - totalsize: bytes.u32()?, - off_dt_struct: bytes.u32()?, - off_dt_strings: bytes.u32()?, - off_mem_rsvmap: bytes.u32()?, - version: bytes.u32()?, - last_comp_version: bytes.u32()?, - boot_cpuid_phys: bytes.u32()?, - size_dt_strings: bytes.u32()?, - size_dt_struct: bytes.u32()?, - }) + Self::new_unaligned(core::slice::from_raw_parts(ptr, real_size)) } } -impl<'a> Fdt<'a> { - /// Construct a new `Fdt` from a byte buffer - /// - /// Note: this function does ***not*** require that the data be 4-byte - /// aligned - pub fn new(data: &'a [u8]) -> Result { - let mut stream = FdtData::new(data); - let header = FdtHeader::from_bytes(&mut stream).ok_or(FdtError::BufferTooSmall)?; +impl<'a> Fdt<'a, AlignedParser<'a>, Panic> { + /// Construct a new `Fdt` from a `u32`-aligned buffer + pub fn new(data: &'a [u32]) -> Result { + let mut parser = AlignedParser::new(data); + let header = parser.parse_header()?; + + let strings_start = header.strings_offset as usize; + let strings_end = strings_start + header.strings_size as usize; + let strings = util::cast_slice(data) + .get(strings_start..strings_end) + .ok_or(FdtError::ParseError(ParseError::UnexpectedEndOfData))?; + + let structs_start = header.structs_offset as usize / 4; + let structs_end = structs_start + (header.structs_size as usize / 4); + let structs = data + .get(structs_start..structs_end) + .ok_or(FdtError::ParseError(ParseError::UnexpectedEndOfData))?; if !header.valid_magic() { return Err(FdtError::BadMagic); - } else if data.len() < header.totalsize.get() as usize { - return Err(FdtError::BufferTooSmall); + } else if data.len() < header.total_size as usize { + return Err(FdtError::ParseError(ParseError::UnexpectedEndOfData)); } - Ok(Self { data, header }) + Ok(Self { header, parser, strings, structs, _mode: core::marker::PhantomData }) } /// # Safety /// This function performs a read to verify the magic value. If the pointer /// is invalid this can result in undefined behavior. - /// - /// Note: this function does ***not*** require that the data be 4-byte - /// aligned - pub unsafe fn from_ptr(ptr: *const u8) -> Result { + pub unsafe fn from_ptr(ptr: *const u32) -> Result { if ptr.is_null() { return Err(FdtError::BadPtr); } let tmp_header = core::slice::from_raw_parts(ptr, core::mem::size_of::()); - let real_size = - FdtHeader::from_bytes(&mut FdtData::new(tmp_header)).unwrap().totalsize.get() as usize; + let real_size = usize::try_from(AlignedParser::new(tmp_header).parse_header()?.total_size) + .map_err(|_| ParseError::NumericConversionError)?; Self::new(core::slice::from_raw_parts(ptr, real_size)) } +} - /// Return reference to raw data. This can be used to obtain the original pointer passed to - /// [Fdt::from_ptr]. - /// - /// # Example - /// ``` - /// # let fdt_ref: &[u8] = include_bytes!("../dtb/test.dtb"); - /// # let original_pointer = fdt_ref.as_ptr(); - /// let fdt = unsafe{fdt::Fdt::from_ptr(original_pointer)}.unwrap(); - /// assert_eq!(fdt.raw_data().as_ptr(), original_pointer); - /// ``` - pub fn raw_data(&self) -> &'a [u8] { - self.data +impl<'a> Fdt<'a, UnalignedParser<'a>, NoPanic> { + /// Construct a new `Fdt` from a byte buffer + pub fn new_unaligned_fallible(data: &'a [u8]) -> Result { + let Fdt { parser, strings, structs, header, .. } = Fdt::new_unaligned(data)?; + Ok(Self { parser, strings, structs, header, _mode: core::marker::PhantomData }) } + /// # Safety + /// This function performs a read to verify the magic value. If the pointer + /// is invalid this can result in undefined behavior. + pub unsafe fn from_ptr_unaligned_fallible(ptr: *const u8) -> Result { + let Fdt { parser, strings, structs, header, .. } = Fdt::from_ptr_unaligned(ptr)?; + Ok(Self { parser, strings, structs, header, _mode: core::marker::PhantomData }) + } +} + +impl<'a> Fdt<'a, AlignedParser<'a>, NoPanic> { + /// Construct a new `Fdt` from a `u32`-aligned buffer which won't panic on invalid data + pub fn new_fallible(data: &'a [u32]) -> Result { + let Fdt { parser, strings, structs, header, .. } = Fdt::new(data)?; + Ok(Self { parser, strings, structs, header, _mode: core::marker::PhantomData }) + } + + /// # Safety + /// This function performs a read to verify the magic value. If the pointer + /// is invalid this can result in undefined behavior. + pub unsafe fn from_ptr_fallible(ptr: *const u32) -> Result { + let Fdt { parser, strings, structs, header, .. } = Fdt::from_ptr(ptr)?; + Ok(Self { parser, strings, structs, header, _mode: core::marker::PhantomData }) + } +} + +impl<'a, P: Parser<'a>, Mode: PanicMode> Fdt<'a, P, Mode> { /// Return the `/aliases` node, if one exists pub fn aliases(&self) -> Option> { Some(Aliases { @@ -255,7 +295,7 @@ impl<'a> Fdt<'a> { /// Returns an iterator over the memory reservations pub fn memory_reservations(&self) -> impl Iterator + 'a { - let mut stream = FdtData::new(&self.data[self.header.off_mem_rsvmap.get() as usize..]); + let mut stream = FdtData::new(&self.data[self.header.off_mem_rsvmap.to_ne() as usize..]); let mut done = false; core::iter::from_fn(move || { @@ -274,6 +314,20 @@ impl<'a> Fdt<'a> { }) } + /// Return reference to raw data. This can be used to obtain the original pointer passed to + /// [Fdt::from_ptr]. + /// + /// # Example + /// ``` + /// # let fdt_ref: &[u8] = include_bytes!("../dtb/test.dtb"); + /// # let original_pointer = fdt_ref.as_ptr(); + /// let fdt = unsafe{fdt::Fdt::from_ptr(original_pointer)}.unwrap(); + /// assert_eq!(fdt.raw_data().as_ptr(), original_pointer); + /// ``` + pub fn raw_data(&self) -> &'a [P::Granularity] { + self.data + } + /// Return the root (`/`) node, which is always available pub fn root(&self) -> Root<'_, 'a> { Root { node: self.find_node("/").expect("/ is a required node") } @@ -308,7 +362,7 @@ impl<'a> Fdt<'a> { self.all_nodes().find(|n| { n.properties() .find(|p| p.name == "phandle") - .and_then(|p| Some(BigEndianU32::from_bytes(p.value)?.get() == phandle)) + .and_then(|p| Some(BigEndianU32::from_bytes(p.value)?.to_ne() == phandle)) .unwrap_or(false) }) } @@ -398,25 +452,26 @@ impl<'a> Fdt<'a> { return None; } - let cstr = CStr::new(block)?; + let cstr = core::ffi::CStr::from_bytes_until_nul(block).ok()?; - block = &block[cstr.len() + 1..]; + block = &block[cstr.to_bytes().len() + 1..]; - cstr.as_str() + cstr.to_str().ok() }) } /// Total size of the devicetree in bytes pub fn total_size(&self) -> usize { - self.header.totalsize.get() as usize + self.header.totalsize.to_ne() as usize } - fn cstr_at_offset(&self, offset: usize) -> CStr<'a> { - CStr::new(&self.strings_block()[offset..]).expect("no null terminating string on C str?") + fn cstr_at_offset(&self, offset: usize) -> &'a core::ffi::CStr { + core::ffi::CStr::from_bytes_until_nul(&self.strings_block()[offset..]) + .expect("no null terminating string on C str?") } fn str_at_offset(&self, offset: usize) -> &'a str { - self.cstr_at_offset(offset).as_str().expect("not utf-8 cstr") + self.cstr_at_offset(offset).to_str().expect("not utf-8 cstr") } fn strings_block(&self) -> &'a [u8] { diff --git a/src/node.rs b/src/node_old.rs similarity index 81% rename from src/node.rs rename to src/node_old.rs index 3e5e215..cf30dd4 100644 --- a/src/node.rs +++ b/src/node_old.rs @@ -3,7 +3,7 @@ // obtain one at https://mozilla.org/MPL/2.0/. use crate::{ - parsing::{BigEndianU32, BigEndianU64, CStr, FdtData}, + parsing::{BigEndianU32, BigEndianU64, FdtData}, standard_nodes::{Compatible, MemoryRange, MemoryRegion}, Fdt, }; @@ -34,12 +34,11 @@ impl FdtProperty { #[derive(Debug, Clone, Copy)] pub struct FdtNode<'b, 'a: 'b> { pub name: &'a str, - pub(crate) header: &'b Fdt<'a>, + pub(crate) header: &'b Fdt<'a, crate::UnalignedParser<'a>>, props: &'a [u8], parent_props: Option<&'a [u8]>, } -#[cfg(feature = "pretty-printing")] impl core::fmt::Display for FdtNode<'_, '_> { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { crate::pretty_print::print_node(f, *self, 0)?; @@ -50,7 +49,7 @@ impl core::fmt::Display for FdtNode<'_, '_> { impl<'b, 'a: 'b> FdtNode<'b, 'a> { fn new( name: &'a str, - header: &'b Fdt<'a>, + header: &'b Fdt<'a, crate::UnalignedParser<'a>>, props: &'a [u8], parent_props: Option<&'a [u8]>, ) -> Self { @@ -67,11 +66,11 @@ impl<'b, 'a: 'b> FdtNode<'b, 'a> { return None; } - while stream.peek_u32()?.get() == FDT_NOP { + while stream.peek_u32()?.to_ne() == FDT_NOP { stream.skip(4); } - if stream.peek_u32()?.get() == FDT_PROP { + if stream.peek_u32().unwrap().to_ne() == FDT_PROP { Some(NodeProperty::parse(&mut stream, self.header)) } else { done = true; @@ -89,11 +88,11 @@ impl<'b, 'a: 'b> FdtNode<'b, 'a> { pub fn children(self) -> impl Iterator> { let mut stream = FdtData::new(self.props); - while stream.peek_u32().unwrap().get() == FDT_NOP { + while stream.peek_u32().unwrap().to_ne() == FDT_NOP { stream.skip(4); } - while stream.peek_u32().unwrap().get() == FDT_PROP { + while stream.peek_u32().unwrap().to_ne() == FDT_PROP { NodeProperty::parse(&mut stream, self.header); } @@ -104,15 +103,18 @@ impl<'b, 'a: 'b> FdtNode<'b, 'a> { return None; } - while stream.peek_u32()?.get() == FDT_NOP { + while stream.peek_u32()?.to_ne() == FDT_NOP { stream.skip(4); } - if stream.peek_u32()?.get() == FDT_BEGIN_NODE { + if stream.peek_u32()?.to_ne() == FDT_BEGIN_NODE { let origin = stream.remaining(); let ret = { stream.skip(4); - let unit_name = CStr::new(stream.remaining()).expect("unit name").as_str()?; + let unit_name = core::ffi::CStr::from_bytes_until_nul(stream.remaining()) + .expect("unit name") + .to_str() + .ok()?; let full_name_len = unit_name.len() + 1; stream.skip(full_name_len); @@ -160,15 +162,15 @@ impl<'b, 'a: 'b> FdtNode<'b, 'a> { let mut stream = FdtData::new(prop.value); reg = Some(core::iter::from_fn(move || { let starting_address = match sizes.address_cells { - 1 => stream.u32()?.get() as usize, - 2 => stream.u64()?.get() as usize, + 1 => stream.u32()?.to_ne() as usize, + 2 => stream.u64()?.to_ne() as usize, _ => return None, } as *const u8; let size = match sizes.size_cells { 0 => None, - 1 => Some(stream.u32()?.get() as usize), - 2 => Some(stream.u64()?.get() as usize), + 1 => Some(stream.u32()?.to_ne() as usize), + 2 => Some(stream.u64()?.to_ne() as usize), _ => return None, }; @@ -195,21 +197,21 @@ impl<'b, 'a: 'b> FdtNode<'b, 'a> { let mut stream = FdtData::new(prop.value); ranges = Some(core::iter::from_fn(move || { let (child_bus_address_hi, child_bus_address) = match sizes.address_cells { - 1 => (0, stream.u32()?.get() as usize), - 2 => (0, stream.u64()?.get() as usize), - 3 => (stream.u32()?.get(), stream.u64()?.get() as usize), + 1 => (0, stream.u32()?.to_ne() as usize), + 2 => (0, stream.u64()?.to_ne() as usize), + 3 => (stream.u32()?.to_ne(), stream.u64()?.to_ne() as usize), _ => return None, }; let parent_bus_address = match parent_sizes.address_cells { - 1 => stream.u32()?.get() as usize, - 2 => stream.u64()?.get() as usize, + 1 => stream.u32()?.to_ne() as usize, + 2 => stream.u64()?.to_ne() as usize, _ => return None, }; let size = match sizes.size_cells { - 1 => stream.u32()?.get() as usize, - 2 => stream.u64()?.get() as usize, + 1 => stream.u32()?.to_ne() as usize, + 2 => stream.u64()?.to_ne() as usize, _ => return None, }; @@ -266,12 +268,12 @@ impl<'b, 'a: 'b> FdtNode<'b, 'a> { "#address-cells" => { cell_sizes.address_cells = BigEndianU32::from_bytes(property.value) .expect("not enough bytes for #address-cells value") - .get() as usize; + .to_ne() as usize; } "#size-cells" => { cell_sizes.size_cells = BigEndianU32::from_bytes(property.value) .expect("not enough bytes for #size-cells value") - .get() as usize; + .to_ne() as usize; } _ => {} } @@ -284,7 +286,7 @@ impl<'b, 'a: 'b> FdtNode<'b, 'a> { pub fn interrupt_parent(self) -> Option> { self.properties() .find(|p| p.name == "interrupt-parent") - .and_then(|p| self.header.find_phandle(BigEndianU32::from_bytes(p.value)?.get())) + .and_then(|p| self.header.find_phandle(BigEndianU32::from_bytes(p.value)?.to_ne())) } /// `#interrupt-cells` property @@ -292,7 +294,7 @@ impl<'b, 'a: 'b> FdtNode<'b, 'a> { let mut interrupt_cells = None; if let Some(prop) = self.property("#interrupt-cells") { - interrupt_cells = BigEndianU32::from_bytes(prop.value).map(|n| n.get() as usize) + interrupt_cells = BigEndianU32::from_bytes(prop.value).map(|n| n.to_ne() as usize) } interrupt_cells @@ -308,8 +310,8 @@ impl<'b, 'a: 'b> FdtNode<'b, 'a> { let mut stream = FdtData::new(prop.value); interrupt = Some(core::iter::from_fn(move || { let interrupt = match sizes { - 1 => stream.u32()?.get() as usize, - 2 => stream.u64()?.get() as usize, + 1 => stream.u32()?.to_ne() as usize, + 2 => stream.u64()?.to_ne() as usize, _ => return None, }; @@ -362,7 +364,7 @@ impl<'b, 'a: 'b> FdtNode<'b, 'a> { let mut interrupt_cells = None; let parent = self .property("interrupt-parent") - .and_then(|p| self.header.find_phandle(BigEndianU32::from_bytes(p.value)?.get())) + .and_then(|p| self.header.find_phandle(BigEndianU32::from_bytes(p.value)?.to_ne())) .or_else(|| { Some(FdtNode { name: "", @@ -409,7 +411,7 @@ pub struct RawReg<'a> { pub(crate) fn find_node<'b, 'a: 'b>( stream: &mut FdtData<'a>, name: &str, - header: &'b Fdt<'a>, + header: &'b Fdt<'a, crate::UnalignedParser<'a>>, parent_props: Option<&'a [u8]>, ) -> Option> { let mut parts = name.splitn(2, '/'); @@ -419,12 +421,15 @@ pub(crate) fn find_node<'b, 'a: 'b>( let curr_data = stream.remaining(); - match stream.u32()?.get() { + match stream.u32()?.to_ne() { FDT_BEGIN_NODE => {} _ => return None, } - let unit_name = CStr::new(stream.remaining()).expect("unit name C str").as_str()?; + let unit_name = core::ffi::CStr::from_bytes_until_nul(stream.remaining()) + .expect("unit name C str") + .to_str() + .ok()?; let full_name_len = unit_name.len() + 1; skip_4_aligned(stream, full_name_len); @@ -451,11 +456,11 @@ pub(crate) fn find_node<'b, 'a: 'b>( let parent_props = Some(stream.remaining()); - while stream.peek_u32()?.get() == FDT_PROP { + while stream.peek_u32()?.to_ne() == FDT_PROP { let _ = NodeProperty::parse(stream, header); } - while stream.peek_u32()?.get() == FDT_BEGIN_NODE { + while stream.peek_u32()?.to_ne() == FDT_BEGIN_NODE { if let Some(p) = find_node(stream, next_part, header, parent_props) { return Some(p); } @@ -463,7 +468,7 @@ pub(crate) fn find_node<'b, 'a: 'b>( stream.skip_nops(); - if stream.u32()?.get() != FDT_END_NODE { + if stream.u32()?.to_ne() != FDT_END_NODE { return None; } @@ -471,7 +476,9 @@ pub(crate) fn find_node<'b, 'a: 'b>( } // FIXME: this probably needs refactored -pub(crate) fn all_nodes<'b, 'a: 'b>(header: &'b Fdt<'a>) -> impl Iterator> { +pub(crate) fn all_nodes<'b, 'a: 'b>( + header: &'b Fdt<'a, crate::UnalignedParser<'a>>, +) -> impl Iterator> { let mut stream = FdtData::new(header.structs_block()); let mut done = false; let mut parents: [&[u8]; 64] = [&[]; 64]; @@ -482,26 +489,29 @@ pub(crate) fn all_nodes<'b, 'a: 'b>(header: &'b Fdt<'a>) -> impl Iterator {} _ => return None, } - let unit_name = CStr::new(stream.remaining()).expect("unit name C str").as_str().unwrap(); + let unit_name = core::ffi::CStr::from_bytes_until_nul(stream.remaining()) + .expect("unit name C str") + .to_str() + .unwrap(); let full_name_len = unit_name.len() + 1; skip_4_aligned(&mut stream, full_name_len); @@ -510,11 +520,11 @@ pub(crate) fn all_nodes<'b, 'a: 'b>(header: &'b Fdt<'a>) -> impl Iterator(header: &'b Fdt<'a>) -> impl Iterator(stream: &mut FdtData<'a>, header: &Fdt<'a>) { - assert_eq!(stream.u32().unwrap().get(), FDT_BEGIN_NODE, "bad node"); - - let unit_name = CStr::new(stream.remaining()).expect("unit_name C str").as_str().unwrap(); +pub(crate) fn skip_current_node<'a>( + stream: &mut FdtData<'a>, + header: &Fdt<'a, crate::UnalignedParser<'a>>, +) { + assert_eq!(stream.u32().unwrap().to_ne(), FDT_BEGIN_NODE, "bad node"); + + let unit_name = core::ffi::CStr::from_bytes_until_nul(stream.remaining()) + .expect("unit_name C str") + .to_str() + .unwrap(); let full_name_len = unit_name.len() + 1; skip_4_aligned(stream, full_name_len); - while stream.peek_u32().unwrap().get() == FDT_PROP { + while stream.peek_u32().unwrap().to_ne() == FDT_PROP { NodeProperty::parse(stream, header); } - while stream.peek_u32().unwrap().get() == FDT_BEGIN_NODE { + while stream.peek_u32().unwrap().to_ne() == FDT_BEGIN_NODE { skip_current_node(stream, header); } stream.skip_nops(); - assert_eq!(stream.u32().unwrap().get(), FDT_END_NODE, "bad node"); + assert_eq!(stream.u32().unwrap().to_ne(), FDT_END_NODE, "bad node"); } /// A node property @@ -563,8 +579,8 @@ impl<'a> NodeProperty<'a> { /// Attempt to parse the property value as a `usize` pub fn as_usize(self) -> Option { match self.value.len() { - 4 => BigEndianU32::from_bytes(self.value).map(|i| i.get() as usize), - 8 => BigEndianU64::from_bytes(self.value).map(|i| i.get() as usize), + 4 => BigEndianU32::from_bytes(self.value).map(|i| i.to_ne() as usize), + 8 => BigEndianU64::from_bytes(self.value).map(|i| i.to_ne() as usize), _ => None, } } @@ -574,30 +590,20 @@ impl<'a> NodeProperty<'a> { core::str::from_utf8(self.value).map(|s| s.trim_end_matches('\0')).ok() } - /// Attempts to parse the property value as a list of [`&str`]. - pub fn iter_str(self) -> impl Iterator + 'a { - let mut s = self.as_str().map(|s| s.split('\0')); - - core::iter::from_fn(move || match s.as_mut() { - Some(s) => s.next(), - None => None, - }) - } - - fn parse(stream: &mut FdtData<'a>, header: &Fdt<'a>) -> Self { - match stream.u32().unwrap().get() { + fn parse(stream: &mut FdtData<'a>, header: &Fdt<'a, crate::UnalignedParser<'a>>) -> Self { + match stream.u32().unwrap().to_ne() { FDT_PROP => {} other => panic!("bad prop, tag: {}", other), } let prop = FdtProperty::from_bytes(stream).expect("FDT property"); - let data_len = prop.len.get() as usize; + let data_len = prop.len.to_ne() as usize; let data = &stream.remaining()[..data_len]; skip_4_aligned(stream, data_len); - NodeProperty { name: header.str_at_offset(prop.name_offset.get() as usize), value: data } + NodeProperty { name: header.str_at_offset(prop.name_offset.to_ne() as usize), value: data } } } @@ -612,12 +618,12 @@ pub struct MemoryReservation { impl MemoryReservation { /// Pointer representing the memory reservation address pub fn address(&self) -> *const u8 { - self.address.get() as usize as *const u8 + self.address.to_ne() as usize as *const u8 } /// Size of the memory reservation pub fn size(&self) -> usize { - self.size.get() as usize + self.size.to_ne() as usize } pub(crate) fn from_bytes(bytes: &mut FdtData<'_>) -> Option { diff --git a/src/nodes.rs b/src/nodes.rs new file mode 100644 index 0000000..76f77c7 --- /dev/null +++ b/src/nodes.rs @@ -0,0 +1,284 @@ +use crate::{ + parsing::{BigEndianToken, Panic, PanicMode, ParseError, Parser, ParserForSize, StringsBlock}, + properties::Property, +}; + +macro_rules! tryblock { + ($($ts:tt)+) => {{ + (|| -> Result<_, ParseError> { + $($ts)+ + })() + }}; +} + +pub struct NodeName<'a> { + pub name: &'a str, + pub unit_address: Option<&'a str>, +} + +impl<'a> NodeName<'a> { + pub fn new(name: &'a str, unit_address: Option<&'a str>) -> Self { + Self { name, unit_address } + } +} + +pub struct Node<'a, Granularity: ParserForSize = u32, Mode: PanicMode = Panic> { + this: &'a RawNode, + parent: Option<&'a RawNode>, + strings: StringsBlock<'a>, + _mode: core::marker::PhantomData<*mut Mode>, +} + +impl<'a, Granularity: ParserForSize, Mode: PanicMode> Node<'a, Granularity, Mode> { + #[inline] + pub(crate) fn new( + this: &'a RawNode, + parent: Option<&'a RawNode>, + strings: StringsBlock<'a>, + ) -> Self { + Self { this, parent, strings, _mode: core::marker::PhantomData } + } + + #[inline] + pub fn name(&self) -> ::Output> { + ::to_output( + <::Parser<'a> as Parser<'a>>::new( + &self.this.0, + self.strings.0, + ) + .advance_cstr() + .and_then(|s| s.to_str().map_err(|_| ParseError::InvalidCStrValue)) + .map(|s| { + let (name, unit_address) = s.split_once('@').unzip(); + NodeName { name: name.unwrap_or(s), unit_address } + }), + ) + } + + #[inline] + pub fn properties(&self) -> ::Output> { + let mut parser = <::Parser<'a> as Parser<'a>>::new( + &self.this.0, + self.strings.0, + ); + let res = parser.advance_cstr(); + + ::to_output(res.map(|_| NodeProperties { + data: parser.data(), + strings: self.strings, + _mode: core::marker::PhantomData, + })) + } + + #[inline] + pub fn raw_property( + &self, + name: &str, + ) -> ::Output>> { + ::to_output(tryblock! { + Ok(::to_result(self.properties())?.find(name)) + }) + } + + pub fn property>(&self) -> ::Output> { + ::to_output(tryblock! { + ::to_result(self.raw_property(P::NAME))?.map(|prop| P::parse(*self, prop)).transpose() + }) + } + + #[inline] + pub fn children(&self) -> ::Output> { + ::to_output(tryblock! { + let mut parser = + <::Parser<'a> as Parser<'a>>::new(&self.this.0, self.strings.0); + parser.advance_cstr()?; + while let BigEndianToken::PROP = parser.peek_token()? { + parser.parse_raw_property()?; + } + + Ok(NodeChildren { + data: parser.data(), + parent: self.this, + strings: self.strings, + _mode: core::marker::PhantomData, + }) + }) + } + + #[inline] + pub fn parent(&self) -> Option { + self.parent.map(|parent| Self { + this: parent, + parent: None, + strings: self.strings, + _mode: core::marker::PhantomData, + }) + } +} + +#[repr(transparent)] +pub(crate) struct RawNode([Granularity]); + +impl RawNode { + pub(crate) fn new(data: &[Granularity]) -> &Self { + // SAFETY: the representation of `Self` and `data` are the same + unsafe { core::mem::transmute(data) } + } +} + +#[derive(Debug, Clone, Copy)] +pub struct NodeProperties<'a, Granularity: ParserForSize = u32, Mode: PanicMode = Panic> { + data: &'a [Granularity], + strings: StringsBlock<'a>, + _mode: core::marker::PhantomData<*mut Mode>, +} + +impl<'a, Granularity: ParserForSize, Mode: PanicMode> NodeProperties<'a, Granularity, Mode> { + pub fn iter(self) -> NodePropertiesIter<'a, Granularity, Mode> { + NodePropertiesIter { properties: self } + } + + pub fn advance(&mut self) -> ::Output>> { + let mut parser = <::Parser<'a> as Parser<'a>>::new( + self.data, + self.strings.0, + ); + + ::to_output(tryblock! { + match parser.parse_raw_property() { + Ok((name_offset, data)) => { + self.data = parser.data(); + + Ok(Some(NodeProperty::new(self.strings.offset_at(name_offset)?, data))) + } + Err(ParseError::UnexpectedEndOfData) => Ok(None), + Err(e) => return Err(e), + } + }) + } + + pub fn find(&self, name: &str) -> ::Output>> { + ::reverse_transpose(self.iter().find(|p| { + ::ok_as_ref(p).map(|p| p.name == name).unwrap_or_default() + })) + } +} + +impl<'a, Granularity: ParserForSize, Mode: PanicMode> IntoIterator + for NodeProperties<'a, Granularity, Mode> +{ + type IntoIter = NodePropertiesIter<'a, Granularity, Mode>; + type Item = ::Output>; + + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + +#[derive(Debug, Clone)] +pub struct NodePropertiesIter<'a, Granularity: ParserForSize = u32, Mode: PanicMode = Panic> { + properties: NodeProperties<'a, Granularity, Mode>, +} + +impl<'a, Granularity: ParserForSize, Mode: PanicMode> Iterator + for NodePropertiesIter<'a, Granularity, Mode> +{ + type Item = ::Output>; + + #[track_caller] + fn next(&mut self) -> Option { + ::transpose(self.properties.advance()) + } +} + +pub trait Value<'a>: Sized { + fn parse(value: &'a [u8]) -> Option; +} + +impl<'a> Value<'a> for u32 { + fn parse(value: &'a [u8]) -> Option { + unsafe { core::ptr::read_unaligned(value.as_ptr().cast()) } + } +} + +pub struct NodeProperty<'a> { + name: &'a str, + value: &'a [u8], +} + +impl<'a> NodeProperty<'a> { + pub fn new(name: &'a str, value: &'a [u8]) -> Self { + Self { name, value } + } + + pub fn name(&self) -> &'a str { + self.name + } + + pub fn value(&self) -> &'a [u8] { + self.value + } + + pub fn to>(&self) -> Option {} +} + +#[derive(Clone, Copy)] +pub struct NodeChildren<'a, Granularity: ParserForSize = u32, Mode: PanicMode = Panic> { + data: &'a [Granularity], + parent: &'a RawNode, + strings: StringsBlock<'a>, + _mode: core::marker::PhantomData<*mut Mode>, +} + +impl<'a, Granularity: ParserForSize, Mode: PanicMode> NodeChildren<'a, Granularity, Mode> { + pub fn iter(self) -> NodeChildrenIter<'a, Granularity, Mode> { + NodeChildrenIter { children: self } + } + + pub fn advance(&mut self) -> ::Output>> { + let mut parser = <::Parser<'a> as Parser<'a>>::new( + self.data, + self.strings.0, + ); + + ::to_output(tryblock! { + match parser.parse_node(Some(self.parent)) { + Ok(node) => { + self.data = parser.data(); + + Ok(Some(node)) + } + Err(ParseError::UnexpectedEndOfData) => Ok(None), + Err(e) => return Err(e), + } + }) + } + + pub fn find( + &self, + name: &str, + ) -> ::Output>> { + ::reverse_transpose(self.iter().find(|n| { + ::ok_as_ref(n) + .and_then(|n| ::ok_as_ref(&n.name())) + .map(|n| n.name == name) + .unwrap_or_default() + })) + } +} + +#[derive(Clone)] +pub struct NodeChildrenIter<'a, Granularity: ParserForSize = u32, Mode: PanicMode = Panic> { + children: NodeChildren<'a, Granularity, Mode>, +} + +impl<'a, Granularity: ParserForSize, Mode: PanicMode> Iterator + for NodeChildrenIter<'a, Granularity, Mode> +{ + type Item = ::Output>; + + #[track_caller] + fn next(&mut self) -> Option { + ::transpose(self.children.advance()) + } +} diff --git a/src/parsing.rs b/src/parsing.rs index 526886b..f1f7fc2 100644 --- a/src/parsing.rs +++ b/src/parsing.rs @@ -2,53 +2,14 @@ // v. 2.0. If a copy of the MPL was not distributed with this file, You can // obtain one at https://mozilla.org/MPL/2.0/. -use core::convert::TryInto; -use core::ffi::CStr as FfiCStr; +pub mod aligned; +pub mod unaligned; -pub struct CStr<'a>(&'a FfiCStr); - -impl<'a> CStr<'a> { - pub fn new(data: &'a [u8]) -> Option { - Some(Self(FfiCStr::from_bytes_until_nul(data).ok()?)) - } - - /// Does not include the null terminating byte - pub fn len(&self) -> usize { - self.0.to_bytes().len().saturating_sub(1) - } - - pub fn as_str(&self) -> Option<&'a str> { - self.0.to_str().ok() - } -} - -#[derive(Debug, Clone, Copy)] -#[repr(transparent)] -pub struct BigEndianU32(u32); - -impl BigEndianU32 { - pub fn get(self) -> u32 { - self.0 - } - - pub(crate) fn from_bytes(bytes: &[u8]) -> Option { - Some(BigEndianU32(u32::from_be_bytes(bytes.get(..4)?.try_into().unwrap()))) - } -} - -#[derive(Debug, Clone, Copy)] -#[repr(transparent)] -pub struct BigEndianU64(u64); - -impl BigEndianU64 { - pub fn get(&self) -> u64 { - self.0 - } - - pub(crate) fn from_bytes(bytes: &[u8]) -> Option { - Some(BigEndianU64(u64::from_be_bytes(bytes.get(..8)?.try_into().unwrap()))) - } -} +use self::{aligned::AlignedParser, unaligned::UnalignedParser}; +use crate::{ + nodes::{Node, RawNode}, + FdtHeader, +}; #[derive(Debug, Clone, Copy)] pub struct FdtData<'a> { @@ -91,7 +52,7 @@ impl<'a> FdtData<'a> { } pub fn skip_nops(&mut self) { - while let Some(crate::node::FDT_NOP) = self.peek_u32().map(|n| n.get()) { + while let Some(4) = self.peek_u32().map(|n| n.to_ne()) { let _ = self.u32(); } } @@ -107,3 +68,369 @@ impl<'a> FdtData<'a> { None } } + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +#[repr(transparent)] +pub struct BigEndianU32(u32); + +impl BigEndianU32 { + pub const fn from_ne(n: u32) -> Self { + Self(n.to_be()) + } + + pub const fn from_le(n: u32) -> Self { + Self(u32::from_le(n)) + } + + pub const fn from_be(n: u32) -> Self { + Self(n) + } + + pub const fn to_ne(self) -> u32 { + u32::from_be(self.0) + } + + pub const fn to_be(self) -> u32 { + self.0 + } + + pub(crate) fn from_bytes(bytes: &[u8]) -> Option { + Some(BigEndianU32(u32::from_ne_bytes(bytes.get(..4)?.try_into().unwrap()))) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +#[repr(transparent)] +pub struct BigEndianU64(u64); + +impl BigEndianU64 { + pub const fn from_ne(n: u64) -> Self { + Self(n.to_be()) + } + + pub const fn from_le(n: u64) -> Self { + Self(u64::from_le(n)) + } + + pub const fn from_be(n: u64) -> Self { + Self(n) + } + + pub const fn to_ne(self) -> u64 { + u64::from_be(self.0) + } + + pub const fn to_be(self) -> u64 { + self.0 + } + + pub(crate) fn from_bytes(bytes: &[u8]) -> Option { + Some(BigEndianU64(u64::from_ne_bytes(bytes.get(..8)?.try_into().unwrap()))) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +#[repr(transparent)] +pub struct BigEndianToken(BigEndianU32); + +impl BigEndianToken { + pub const BEGIN_NODE: Self = Self(BigEndianU32::from_ne(1)); + pub const END_NODE: Self = Self(BigEndianU32::from_ne(2)); + pub const PROP: Self = Self(BigEndianU32::from_ne(3)); + pub const NOP: Self = Self(BigEndianU32::from_ne(4)); + pub const END: Self = Self(BigEndianU32::from_ne(5)); +} + +pub(crate) struct Stream<'a, T: Copy>(&'a [T]); + +impl<'a, T: Copy> Stream<'a, T> { + #[inline(always)] + pub(crate) fn new(data: &'a [T]) -> Self { + Self(data) + } + + #[inline(always)] + pub(crate) fn advance(&mut self) -> Option { + let ret = self.0[0]; + self.0 = self.0.get(1..)?; + Some(ret) + } + + pub(crate) fn skip_many(&mut self, n: usize) { + self.0 = self.0.get(n..).unwrap_or_default(); + } +} + +impl<'a, T: Copy> Clone for Stream<'a, T> { + fn clone(&self) -> Self { + Self(self.0) + } +} + +mod sealed { + pub trait Sealed {} +} + +#[derive(Debug, Clone, Copy)] +pub enum ParseError { + NumericConversionError, + InvalidCStrValue, + InvalidPropertyValue, + InvalidTokenValue, + UnexpectedToken, + UnexpectedEndOfData, +} + +impl core::fmt::Display for ParseError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Self::InvalidCStrValue => write!(f, "cstr was either non-terminated or invalid ASCII"), + Self::InvalidTokenValue => { + write!(f, "encountered invalid FDT token value while parsing") + } + Self::NumericConversionError => write!( + f, + "u32 value too large for usize (this should only occur on 16-bit platforms)" + ), + Self::UnexpectedEndOfData => { + write!(f, "encountered end of data while parsing but expected more") + } + Self::UnexpectedToken => { + write!(f, "encountered an unexpected FDT token value while parsing") + } + } + } +} + +pub trait ParserForSize: sealed::Sealed { + type Parser<'a>: Parser<'a, Granularity = Self>; +} + +impl sealed::Sealed for u8 {} +impl ParserForSize for u8 { + type Parser<'a> = UnalignedParser<'a>; +} + +impl sealed::Sealed for u32 {} +impl ParserForSize for u32 { + type Parser<'a> = AlignedParser<'a>; +} + +pub trait PanicMode: sealed::Sealed { + type Output; + fn to_output(result: Result) -> Self::Output; + fn transpose(result: Self::Output>) -> Option>; + fn reverse_transpose(result: Option>) -> Self::Output>; + fn ok_as_ref(output: &Self::Output) -> Option<&T>; + fn ok(output: Self::Output) -> Option; + fn to_result(output: Self::Output) -> Result; +} + +pub struct NoPanic; + +impl sealed::Sealed for NoPanic {} +impl PanicMode for NoPanic { + type Output = Result; + + #[inline(always)] + fn to_output(result: Result) -> Self::Output { + result + } + + fn transpose(result: Self::Output>) -> Option> { + result.transpose() + } + + fn reverse_transpose(result: Option>) -> Self::Output> { + result.transpose() + } + + fn ok_as_ref(output: &Self::Output) -> Option<&T> { + output.ok().as_ref() + } + + fn ok(output: Self::Output) -> Option { + output.ok() + } + + fn to_result(output: Self::Output) -> Result { + output + } +} + +pub struct Panic; + +impl sealed::Sealed for Panic {} +impl PanicMode for Panic { + type Output = T; + + #[track_caller] + #[inline(always)] + fn to_output(result: Result) -> Self::Output { + result.unwrap() + } + + #[track_caller] + #[inline(always)] + fn transpose(result: Self::Output>) -> Option> { + result + } + + fn reverse_transpose(result: Option>) -> Self::Output> { + result + } + + fn ok_as_ref(output: &Self::Output) -> Option<&T> { + Some(output) + } + + fn ok(output: Self::Output) -> Option { + Some(output) + } + + fn to_result(output: Self::Output) -> Result { + Ok(output) + } +} + +pub trait Parser<'a>: sealed::Sealed + Clone { + type Granularity: Copy + ParserForSize; + + fn new(data: &'a [Self::Granularity], strings: &'a [u8]) -> Self; + fn data(&self) -> &'a [Self::Granularity]; + fn byte_data(&self) -> &'a [u8]; + fn strings(&self) -> StringsBlock<'a>; + + fn advance_token(&mut self) -> Result; + fn peek_token(&mut self) -> Result { + self.clone().advance_token() + } + + fn advance_u32(&mut self) -> Result; + fn advance_u64(&mut self) -> Result; + fn advance_cstr(&mut self) -> Result<&'a core::ffi::CStr, ParseError>; + fn advance_aligned(&mut self, n: usize); + + fn peek_u32(&self) -> Result { + self.clone().advance_u32() + } + + fn peek_u64(&self) -> Result { + self.clone().advance_u64() + } + + fn parse_header(&mut self) -> Result { + let magic = self.advance_u32()?.to_ne(); + let total_size = self.advance_u32()?.to_ne(); + let struct_offset = self.advance_u32()?.to_ne(); + let strings_offset = self.advance_u32()?.to_ne(); + let memory_reserve_map_offset = self.advance_u32()?.to_ne(); + let version = self.advance_u32()?.to_ne(); + let last_compatible_version = self.advance_u32()?.to_ne(); + let boot_cpuid = self.advance_u32()?.to_ne(); + let strings_size = self.advance_u32()?.to_ne(); + let structs_size = self.advance_u32()?.to_ne(); + + Ok(FdtHeader { + magic, + total_size, + structs_offset: struct_offset, + strings_offset, + memory_reserve_map_offset, + version, + last_compatible_version, + boot_cpuid, + strings_size, + structs_size, + }) + } + + fn parse_node( + &mut self, + parent: Option<&'a RawNode>, + ) -> Result, ParseError> { + let starting_len = self.data().len(); + let starting_data = self.data(); + + match self.advance_token()? { + BigEndianToken::BEGIN_NODE => {} + _ => return Err(ParseError::UnexpectedToken), + } + + self.advance_cstr()?; + + while self.peek_token()? == BigEndianToken::PROP { + self.parse_raw_property()?; + } + + let mut depth = 0; + loop { + let token = self.advance_token()?; + match token { + BigEndianToken::BEGIN_NODE => depth += 1, + BigEndianToken::END_NODE => match depth { + 0 => break, + _ => { + depth -= 1; + continue; + } + }, + } + + self.advance_cstr()?; + + while self.peek_token()? == BigEndianToken::PROP { + self.parse_raw_property()?; + } + } + + let ending_len = self.data().len(); + + match self.advance_token()? { + BigEndianToken::END_NODE => Ok(Node { + this: RawNode::new( + starting_data + .get(..starting_len - ending_len) + .ok_or(ParseError::UnexpectedEndOfData)?, + ), + parent, + strings: self.strings(), + _mode: core::marker::PhantomData, + }), + _ => return Err(ParseError::UnexpectedToken), + } + } + + fn parse_raw_property(&mut self) -> Result<(usize, &'a [u8]), ParseError> { + match self.advance_token()? { + BigEndianToken::PROP => { + // Properties are in the format: + let len = usize::try_from(self.advance_u32()?.to_ne()) + .map_err(|_| ParseError::NumericConversionError)?; + let name_offset = usize::try_from(self.advance_u32()?.to_ne()) + .map_err(|_| ParseError::NumericConversionError)?; + let data = self.byte_data().get(..len).ok_or(ParseError::UnexpectedEndOfData)?; + + self.advance_aligned(data.len()); + + Ok((name_offset, data)) + } + _ => Err(ParseError::UnexpectedToken), + } + } +} + +#[derive(Debug, Clone, Copy)] +#[repr(transparent)] +pub struct StringsBlock<'a>(pub(crate) &'a [u8]); + +impl<'a> StringsBlock<'a> { + pub fn offset_at(self, offset: usize) -> Result<&'a str, ParseError> { + core::ffi::CStr::from_bytes_until_nul( + self.0.get(offset..).ok_or(ParseError::UnexpectedEndOfData)?, + ) + .map_err(|_| ParseError::InvalidCStrValue)? + .to_str() + .map_err(|_| ParseError::InvalidCStrValue) + } +} diff --git a/src/parsing/aligned.rs b/src/parsing/aligned.rs new file mode 100644 index 0000000..9461b7a --- /dev/null +++ b/src/parsing/aligned.rs @@ -0,0 +1,120 @@ +// This Source Code Form is subject to the terms of the Mozilla Public License, +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at https://mozilla.org/MPL/2.0/. + +use super::{ + sealed, BigEndianToken, BigEndianU32, BigEndianU64, ParseError, Parser, Stream, StringsBlock, +}; + +pub struct AlignedParser<'a> { + stream: Stream<'a, u32>, + strings: StringsBlock<'a>, +} + +impl Clone for AlignedParser<'_> { + fn clone(&self) -> Self { + Self { stream: self.stream.clone(), strings: self.strings.clone() } + } +} + +impl sealed::Sealed for AlignedParser<'_> {} +impl<'a> Parser<'a> for AlignedParser<'a> { + type Granularity = u32; + + fn new(data: &'a [Self::Granularity], strings: &'a [u8]) -> Self { + Self { stream: Stream::new(data), strings: StringsBlock(strings) } + } + + fn data(&self) -> &'a [Self::Granularity] { + self.stream.0 + } + + fn byte_data(&self) -> &'a [u8] { + // SAFETY: it is always valid to cast a `u32` to 4 `u8`s + unsafe { + core::slice::from_raw_parts( + self.stream.0.as_ptr().cast::(), + core::mem::size_of_val(self.stream.0), + ) + } + } + + fn strings(&self) -> super::StringsBlock<'a> { + self.strings + } + + fn advance_token(&mut self) -> Result { + loop { + match BigEndianToken( + self.stream.advance().map(BigEndianU32).ok_or(ParseError::UnexpectedEndOfData)?, + ) { + BigEndianToken::NOP => continue, + token @ BigEndianToken::BEGIN_NODE + | token @ BigEndianToken::END_NODE + | token @ BigEndianToken::PROP + | token @ BigEndianToken::END => break Ok(token), + _ => break Err(ParseError::InvalidTokenValue), + } + } + } + + fn advance_u32(&mut self) -> Result { + self.stream.advance().map(BigEndianU32).ok_or(ParseError::UnexpectedEndOfData) + } + + fn advance_u64(&mut self) -> Result { + let (a, b) = self + .stream + .advance() + .map(BigEndianU32) + .zip(self.stream.advance().map(BigEndianU32)) + .ok_or(ParseError::UnexpectedEndOfData)?; + + #[cfg(target_endian = "little")] + return Ok(BigEndianU64::from_be((u64::from(b.to_be()) << 32) | u64::from(a.to_be()))); + + #[cfg(target_endian = "big")] + return Ok(BigEndianU64::from_be((u64::from(a.to_be()) << 32) | u64::from(b.to_be()))); + } + + fn advance_cstr(&mut self) -> Result<&'a core::ffi::CStr, ParseError> { + // SAFETY: It is safe to reinterpret the stream data to a smaller integer size + let bytes = unsafe { + core::slice::from_raw_parts( + self.stream.0.as_ptr().cast::(), + core::mem::size_of_val(self.stream.0), + ) + }; + let cstr = core::ffi::CStr::from_bytes_until_nul(bytes) + .map_err(|_| ParseError::InvalidCStrValue)?; + + // Round up to the next multiple of 4, if necessary + let skip = ((cstr.to_bytes().len() + 3) & !3) / 4; + self.stream.skip_many(skip); + + Ok(cstr) + } + + fn advance_aligned(&mut self, n: usize) { + // Round up to the next multiple of 4, if necessary + let skip = ((n + 3) & !3) / 4; + self.stream.skip_many(skip); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn advance_u64() { + let n = BigEndianU64::from_ne(0xF00DCAFEDEADFEED); + let mut parser = AlignedParser::new( + unsafe { core::slice::from_raw_parts(&n as *const BigEndianU64 as *const u32, 2) }, + &[], + ); + let m = parser.advance_u64().unwrap(); + + assert_eq!(n, m); + } +} diff --git a/src/parsing/unaligned.rs b/src/parsing/unaligned.rs new file mode 100644 index 0000000..f3d6a11 --- /dev/null +++ b/src/parsing/unaligned.rs @@ -0,0 +1,122 @@ +// This Source Code Form is subject to the terms of the Mozilla Public License, +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at https://mozilla.org/MPL/2.0/. + +use super::{ + sealed, BigEndianToken, BigEndianU32, BigEndianU64, ParseError, Parser, Stream, StringsBlock, +}; + +pub struct UnalignedParser<'a> { + stream: Stream<'a, u8>, + strings: StringsBlock<'a>, +} + +impl Clone for UnalignedParser<'_> { + fn clone(&self) -> Self { + Self { stream: self.stream.clone(), strings: self.strings.clone() } + } +} + +impl sealed::Sealed for UnalignedParser<'_> {} +impl<'a> Parser<'a> for UnalignedParser<'a> { + type Granularity = u8; + + fn new(data: &'a [Self::Granularity], strings: &'a [u8]) -> Self { + Self { stream: Stream::new(data), strings: StringsBlock(strings) } + } + + fn data(&self) -> &'a [Self::Granularity] { + self.stream.0 + } + + fn byte_data(&self) -> &'a [u8] { + self.stream.0 + } + + fn strings(&self) -> super::StringsBlock<'a> { + self.strings + } + + fn advance_token(&mut self) -> Result { + loop { + match BigEndianToken(self.advance_u32()?) { + BigEndianToken::NOP => continue, + token @ BigEndianToken::BEGIN_NODE + | token @ BigEndianToken::END_NODE + | token @ BigEndianToken::PROP + | token @ BigEndianToken::END => break Ok(token), + _ => break Err(ParseError::InvalidTokenValue), + } + } + } + + fn advance_u32(&mut self) -> Result { + if self.stream.0.len() < core::mem::size_of::() { + return Err(ParseError::UnexpectedEndOfData); + } + + let data = self.stream.0; + self.stream.skip_many(4); + + // SAFETY: The buffer has at least 4 bytes available to read + Ok(BigEndianU32::from_be(unsafe { core::ptr::read_unaligned(data.as_ptr().cast::()) })) + } + + fn advance_u64(&mut self) -> Result { + if self.stream.0.len() < core::mem::size_of::() { + return Err(ParseError::UnexpectedEndOfData); + } + + let data = self.stream.0; + self.stream.skip_many(4); + + // SAFETY: The buffer has at least 4 bytes available to read + Ok(BigEndianU64::from_be(unsafe { core::ptr::read_unaligned(data.as_ptr().cast::()) })) + } + + fn advance_cstr(&mut self) -> Result<&'a core::ffi::CStr, ParseError> { + let cstr = core::ffi::CStr::from_bytes_until_nul(self.stream.0) + .map_err(|_| ParseError::InvalidCStrValue)?; + + // Round up to the next multiple of 4, if necessary + let skip = (cstr.to_bytes().len() + 3) & !3; + self.stream.skip_many(skip); + + Ok(cstr) + } + + fn advance_aligned(&mut self, n: usize) { + // Round up to the next multiple of 4, if necessary + let skip = (n + 3) & !3; + self.stream.skip_many(skip); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn advance_u32() { + let n = BigEndianU32::from_ne(0xF00DCAFE); + let mut parser = UnalignedParser::new( + unsafe { core::slice::from_raw_parts(&n as *const BigEndianU32 as *const u8, 4) }, + &[], + ); + let m = parser.advance_u32().unwrap(); + + assert_eq!(n, m); + } + + #[test] + fn advance_u64() { + let n = BigEndianU64::from_ne(0xF00DCAFEDEADFEED); + let mut parser = UnalignedParser::new( + unsafe { core::slice::from_raw_parts(&n as *const BigEndianU64 as *const u8, 8) }, + &[], + ); + let m = parser.advance_u64().unwrap(); + + assert_eq!(n, m); + } +} diff --git a/src/properties.rs b/src/properties.rs new file mode 100644 index 0000000..0d982a2 --- /dev/null +++ b/src/properties.rs @@ -0,0 +1,55 @@ +use crate::{ + nodes::Node, + parsing::{unaligned::UnalignedParser, PanicMode, ParseError, Parser, ParserForSize}, +}; + +pub trait Property<'a>: Sized { + fn parse( + node: Node<'a, Granularity, Mode>, + ) -> Result, ParseError> + where + Granularity: ParserForSize, + Mode: PanicMode; +} + +pub struct CellSizes { + pub address_cells: usize, + pub size_cells: usize, +} + +impl<'a> Property<'a> for CellSizes { + #[track_caller] + fn parse( + node: Node<'a, Granularity, Mode>, + ) -> Result, ParseError> + where + Granularity: ParserForSize, + Mode: PanicMode, + { + let (mut address_cells, mut size_cells) = (None, None); + + for property in ::to_result(node.properties())? { + let mut parser = UnalignedParser::new(property.value(), &[]); + match property.name() { + "#address-cells" => address_cells = Some(parser.advance_u32()?.to_ne() as usize), + "#size-cells" => size_cells = Some(parser.advance_u32()?.to_ne() as usize), + _ => {} + } + } + + if let (None, Some(parent)) = (address_cells, node.parent()) { + address_cells = + ::to_result(parent.property::())?.map(|c| c.address_cells); + } + + if let (None, Some(parent)) = (size_cells, node.parent()) { + size_cells = + ::to_result(parent.property::())?.map(|c| c.size_cells); + } + + Ok(Some(CellSizes { + address_cells: address_cells.unwrap_or(2), + size_cells: size_cells.unwrap_or(2), + })) + } +} diff --git a/src/standard_nodes.rs b/src/standard_nodes.rs index d75416d..d63d4ba 100644 --- a/src/standard_nodes.rs +++ b/src/standard_nodes.rs @@ -4,7 +4,7 @@ use crate::{ node::{CellSizes, FdtNode, NodeProperty}, - parsing::{BigEndianU32, BigEndianU64, CStr, FdtData}, + parsing::{BigEndianU32, BigEndianU64, FdtData}, Fdt, }; @@ -74,6 +74,10 @@ impl<'b, 'a> StdInOutPath<'b, 'a> { Self { node, params } } + pub fn name(&self) -> &'a str { + self.node.name + } + pub fn node(&self) -> FdtNode<'b, 'a> { self.node } @@ -123,7 +127,7 @@ impl<'b, 'a: 'b> Root<'b, 'a> { /// Represents the `/aliases` node with specific helper methods #[derive(Debug, Clone, Copy)] pub struct Aliases<'b, 'a: 'b> { - pub(crate) header: &'b Fdt<'a>, + pub(crate) header: &'b Fdt<'a, crate::UnalignedParser<'a>>, pub(crate) node: FdtNode<'b, 'a>, } @@ -178,8 +182,8 @@ impl<'b, 'a: 'b> Cpu<'b, 'a> { .find(|p| p.name == "clock-frequency") .or_else(|| self.parent.property("clock-frequency")) .map(|p| match p.value.len() { - 4 => BigEndianU32::from_bytes(p.value).unwrap().get() as usize, - 8 => BigEndianU64::from_bytes(p.value).unwrap().get() as usize, + 4 => BigEndianU32::from_bytes(p.value).unwrap().to_ne() as usize, + 8 => BigEndianU64::from_bytes(p.value).unwrap().to_ne() as usize, _ => unreachable!(), }) .expect("clock-frequency is a required property of cpu nodes") @@ -192,8 +196,8 @@ impl<'b, 'a: 'b> Cpu<'b, 'a> { .find(|p| p.name == "timebase-frequency") .or_else(|| self.parent.property("timebase-frequency")) .map(|p| match p.value.len() { - 4 => BigEndianU32::from_bytes(p.value).unwrap().get() as usize, - 8 => BigEndianU64::from_bytes(p.value).unwrap().get() as usize, + 4 => BigEndianU32::from_bytes(p.value).unwrap().to_ne() as usize, + 8 => BigEndianU64::from_bytes(p.value).unwrap().to_ne() as usize, _ => unreachable!(), }) .expect("timebase-frequency is a required property of cpu nodes") @@ -222,8 +226,8 @@ impl<'a> CpuIds<'a> { /// The first listed CPU ID, which will always exist pub fn first(self) -> usize { match self.address_cells { - 1 => BigEndianU32::from_bytes(self.reg.value).unwrap().get() as usize, - 2 => BigEndianU64::from_bytes(self.reg.value).unwrap().get() as usize, + 1 => BigEndianU32::from_bytes(self.reg.value).unwrap().to_ne() as usize, + 2 => BigEndianU64::from_bytes(self.reg.value).unwrap().to_ne() as usize, n => panic!("address-cells of size {} is currently not supported", n), } } @@ -234,8 +238,8 @@ impl<'a> CpuIds<'a> { core::iter::from_fn(move || match vals.remaining() { [] => None, _ => Some(match self.address_cells { - 1 => vals.u32()?.get() as usize, - 2 => vals.u64()?.get() as usize, + 1 => vals.u32()?.to_ne() as usize, + 2 => vals.u64()?.to_ne() as usize, n => panic!("address-cells of size {} is currently not supported", n), }), }) @@ -251,7 +255,7 @@ pub struct Compatible<'a> { impl<'a> Compatible<'a> { /// First compatible string pub fn first(self) -> &'a str { - CStr::new(self.data).expect("expected C str").as_str().unwrap() + core::ffi::CStr::from_bytes_until_nul(self.data).expect("expected C str").to_str().unwrap() } /// Returns an iterator over all available compatible strings @@ -303,9 +307,9 @@ impl<'a> Memory<'_, 'a> { let size = stream.u32().expect("size"); mapped_area = Some(MappedArea { - effective_address: effective_address.get() as usize, - physical_address: physical_address.get() as usize, - size: size.get() as usize, + effective_address: effective_address.to_ne() as usize, + physical_address: physical_address.to_ne() as usize, + size: size.to_ne() as usize, }); } diff --git a/src/tests.rs b/src/tests.rs index 06eeddc..1fb32b2 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -6,24 +6,56 @@ extern crate std; use crate::{node::RawReg, *}; -static TEST: &[u8] = include_bytes!("../dtb/test.dtb"); -static ISSUE_3: &[u8] = include_bytes!("../dtb/issue-3.dtb"); -static SIFIVE: &[u8] = include_bytes!("../dtb/sifive.dtb"); +struct AlignArrayUp([u8; N]); + +impl AlignArrayUp { + const fn align_up(self) -> [u8; M] { + assert!(M > N); + assert!(M % 4 == 0); + + let mut copy: [u8; M] = [0u8; M]; + let mut i = 0; + + while i < N { + copy[i] = self.0[i]; + } + + return copy; + } +} + +#[repr(align(4))] +struct Align4([u8; N]); + +impl Align4 { + const fn new(a: [u8; N]) -> Self { + Self(a) + } + + fn as_slice(&self) -> &[u32] { + unsafe { core::slice::from_raw_parts(self.0.as_ptr().cast::(), self.0.len() / 4) } + } +} + +static TEST: Align4<3764> = + Align4::new(AlignArrayUp(*include_bytes!("../dtb/test.dtb")).align_up::<3764>()); +static ISSUE_3: Align4<4658> = Align4::new(*include_bytes!("../dtb/issue-3.dtb")); +static SIFIVE: Align4<3872> = Align4::new(*include_bytes!("../dtb/sifive.dtb")); #[test] fn returns_fdt() { - assert!(Fdt::new(TEST).is_ok()); + assert!(Fdt::new(TEST.as_slice()).is_ok()); } #[test] fn finds_root_node() { - let fdt = Fdt::new(TEST).unwrap(); + let fdt = Fdt::new(TEST.as_slice()).unwrap(); assert!(fdt.find_node("/").is_some(), "couldn't find root node"); } #[test] fn finds_root_node_properties() { - let fdt = Fdt::new(TEST).unwrap(); + let fdt = Fdt::new(TEST.as_slice()).unwrap(); let prop = fdt .find_node("/") .unwrap() @@ -35,13 +67,13 @@ fn finds_root_node_properties() { #[test] fn finds_child_of_root_node() { - let fdt = Fdt::new(TEST).unwrap(); + let fdt = Fdt::new(TEST.as_slice()).unwrap(); assert!(fdt.find_node("/cpus").is_some(), "couldn't find cpus node"); } #[test] fn correct_flash_regions() { - let fdt = Fdt::new(TEST).unwrap(); + let fdt = Fdt::new(TEST.as_slice()).unwrap(); let regions = fdt.find_node("/soc/flash").unwrap().reg().unwrap().collect::>(); assert_eq!( @@ -55,7 +87,7 @@ fn correct_flash_regions() { #[test] fn parses_populated_ranges() { - let fdt = Fdt::new(TEST).unwrap(); + let fdt = Fdt::new(TEST.as_slice()).unwrap(); let ranges = fdt.find_node("/soc/pci").unwrap().ranges().unwrap().collect::>(); assert_eq!( @@ -79,7 +111,7 @@ fn parses_populated_ranges() { #[test] fn parses_empty_ranges() { - let fdt = Fdt::new(TEST).unwrap(); + let fdt = Fdt::new(TEST.as_slice()).unwrap(); let ranges = fdt.find_node("/soc").unwrap().ranges().unwrap().collect::>(); assert_eq!(ranges, &[]); @@ -87,13 +119,13 @@ fn parses_empty_ranges() { #[test] fn finds_with_addr() { - let fdt = Fdt::new(TEST).unwrap(); + let fdt = Fdt::new(TEST.as_slice()).unwrap(); assert_eq!(fdt.find_node("/soc/virtio_mmio@10004000").unwrap().name, "virtio_mmio@10004000"); } #[test] fn compatibles() { - let fdt = Fdt::new(TEST).unwrap(); + let fdt = Fdt::new(TEST.as_slice()).unwrap(); let res = fdt .find_node("/soc/test") .unwrap() @@ -107,7 +139,7 @@ fn compatibles() { #[test] fn parent_cell_sizes() { - let fdt = Fdt::new(TEST).unwrap(); + let fdt = Fdt::new(TEST.as_slice()).unwrap(); let regions = fdt.find_node("/memory").unwrap().reg().unwrap().collect::>(); assert_eq!( @@ -118,14 +150,14 @@ fn parent_cell_sizes() { #[test] fn no_properties() { - let fdt = Fdt::new(TEST).unwrap(); + let fdt = Fdt::new(TEST.as_slice()).unwrap(); let regions = fdt.find_node("/emptyproptest").unwrap(); assert_eq!(regions.properties().count(), 0); } #[test] fn finds_all_nodes() { - let fdt = Fdt::new(TEST).unwrap(); + let fdt = Fdt::new(TEST.as_slice()).unwrap(); let mut all_nodes: std::vec::Vec<_> = fdt.all_nodes().map(|n| n.name).collect(); all_nodes.sort_unstable(); @@ -167,7 +199,7 @@ fn finds_all_nodes() { #[test] fn required_nodes() { - let fdt = Fdt::new(TEST).unwrap(); + let fdt = Fdt::new(TEST.as_slice()).unwrap(); fdt.cpus().next().unwrap(); fdt.memory(); fdt.chosen(); @@ -175,13 +207,13 @@ fn required_nodes() { #[test] fn doesnt_exist() { - let fdt = Fdt::new(TEST).unwrap(); + let fdt = Fdt::new(TEST.as_slice()).unwrap(); assert!(fdt.find_node("/this/doesnt/exist").is_none()); } #[test] fn raw_reg() { - let fdt = Fdt::new(TEST).unwrap(); + let fdt = Fdt::new(TEST.as_slice()).unwrap(); let regions = fdt.find_node("/soc/flash").unwrap().raw_reg().unwrap().collect::>(); @@ -196,19 +228,19 @@ fn raw_reg() { #[test] fn issue_3() { - let fdt = Fdt::new(ISSUE_3).unwrap(); + let fdt = Fdt::new(ISSUE_3.as_slice()).unwrap(); fdt.find_all_nodes("uart").for_each(|n| std::println!("{:?}", n)); } #[test] fn issue_4() { - let fdt = Fdt::new(ISSUE_3).unwrap(); + let fdt = Fdt::new(ISSUE_3.as_slice()).unwrap(); fdt.all_nodes().for_each(|n| std::println!("{:?}", n)); } #[test] fn cpus() { - let fdt = Fdt::new(TEST).unwrap(); + let fdt = Fdt::new(TEST.as_slice()).unwrap(); for cpu in fdt.cpus() { cpu.ids().all().for_each(|n| std::println!("{:?}", n)); } @@ -216,13 +248,13 @@ fn cpus() { #[test] fn invalid_node() { - let fdt = Fdt::new(TEST).unwrap(); + let fdt = Fdt::new(TEST.as_slice()).unwrap(); assert!(fdt.find_node("this/is/an invalid node///////////").is_none()); } #[test] fn aliases() { - let fdt = Fdt::new(SIFIVE).unwrap(); + let fdt = Fdt::new(SIFIVE.as_slice()).unwrap(); let aliases = fdt.aliases().unwrap(); for (_, node_path) in aliases.all() { assert!(fdt.find_node(node_path).is_some(), "path: {:?}", node_path); @@ -231,7 +263,7 @@ fn aliases() { #[test] fn stdout() { - let fdt = Fdt::new(TEST).unwrap(); + let fdt = Fdt::new(TEST.as_slice()).unwrap(); let stdout = fdt.chosen().stdout().unwrap(); assert!(stdout.node().name == "uart@10000000"); assert!(stdout.params() == Some("115200")); @@ -239,7 +271,7 @@ fn stdout() { #[test] fn stdin() { - let fdt = Fdt::new(TEST).unwrap(); + let fdt = Fdt::new(TEST.as_slice()).unwrap(); let stdin = fdt.chosen().stdin().unwrap(); assert!(stdin.node().name == "uart@10000000"); assert!(stdin.params().is_none()); @@ -247,26 +279,26 @@ fn stdin() { #[test] fn node_property_str_value() { - let fdt = Fdt::new(TEST).unwrap(); + let fdt = Fdt::new(TEST.as_slice()).unwrap(); let cpu0 = fdt.find_node("/cpus/cpu@0").unwrap(); assert_eq!(cpu0.property("riscv,isa").unwrap().as_str().unwrap(), "rv64imafdcsu"); } #[test] fn model_value() { - let fdt = Fdt::new(TEST).unwrap(); + let fdt = Fdt::new(TEST.as_slice()).unwrap(); assert_eq!(fdt.root().model(), "riscv-virtio,qemu"); } #[test] fn memory_node() { - let fdt = Fdt::new(TEST).unwrap(); + let fdt = Fdt::new(TEST.as_slice()).unwrap(); assert_eq!(fdt.memory().regions().count(), 1); } #[test] fn interrupt_cells() { - let fdt = Fdt::new(TEST).unwrap(); + let fdt = Fdt::new(TEST.as_slice()).unwrap(); let uart = fdt.find_node("/soc/uart").unwrap(); std::println!("{:?}", uart.parent_interrupt_cells()); assert_eq!(uart.interrupts().unwrap().collect::>(), std::vec![0xA]); diff --git a/src/util.rs b/src/util.rs new file mode 100644 index 0000000..97532fb --- /dev/null +++ b/src/util.rs @@ -0,0 +1,5 @@ +pub fn cast_slice(s: &[u32]) -> &[u8] { + // SAFETY: it is always valid to cast a `u32` slice to a slice of `u8`s as + // they have lower alignment requirements and there is no padding + unsafe { core::slice::from_raw_parts(s.as_ptr().cast(), core::mem::size_of_val(s)) } +} From b9049d869bbec0ec7213f05549465a90df34552b Mon Sep 17 00:00:00 2001 From: repnop Date: Thu, 20 Jun 2024 23:49:52 -0400 Subject: [PATCH 02/38] AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA --- .direnv/flake-profile | 1 + .direnv/flake-profile-3-link | 1 + .envrc | 1 + examples/basic_info.rs | 62 ++-- examples/tree_print.rs | 28 +- flake.lock | 85 +++++ flake.nix | 35 +++ src/lib.rs | 278 +++++++++-------- src/nodes.rs | 171 ++++++++-- src/parsing.rs | 87 ++++-- src/parsing/aligned.rs | 12 +- src/parsing/unaligned.rs | 10 +- src/pretty_print.rs | 173 ++++++----- src/properties.rs | 4 +- src/standard_nodes.rs | 586 +++++++++++++++++++++++------------ src/tests.rs | 518 ++++++++++++++++--------------- 16 files changed, 1276 insertions(+), 776 deletions(-) create mode 120000 .direnv/flake-profile create mode 120000 .direnv/flake-profile-3-link create mode 100644 .envrc create mode 100644 flake.lock create mode 100644 flake.nix diff --git a/.direnv/flake-profile b/.direnv/flake-profile new file mode 120000 index 0000000..519b17b --- /dev/null +++ b/.direnv/flake-profile @@ -0,0 +1 @@ +flake-profile-3-link \ No newline at end of file diff --git a/.direnv/flake-profile-3-link b/.direnv/flake-profile-3-link new file mode 120000 index 0000000..ce99e09 --- /dev/null +++ b/.direnv/flake-profile-3-link @@ -0,0 +1 @@ +/nix/store/ixwhpjrhm228172lh148h40pcz68k7lg-nix-shell-env \ No newline at end of file diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..8392d15 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake \ No newline at end of file diff --git a/examples/basic_info.rs b/examples/basic_info.rs index 2bfc5e4..0e742b4 100644 --- a/examples/basic_info.rs +++ b/examples/basic_info.rs @@ -1,35 +1,37 @@ -static MY_FDT: &[u8] = include_bytes!("../dtb/test.dtb"); +// static MY_FDT: &[u8] = include_bytes!("../dtb/test.dtb"); -fn main() { - let fdt = fdt::Fdt::new(MY_FDT).unwrap(); +// fn main() { +// let fdt = fdt::Fdt::new(MY_FDT).unwrap(); - println!("This is a devicetree representation of a {}", fdt.root().model()); - println!("...which is compatible with at least: {}", fdt.root().compatible().first()); - println!("...and has {} CPU(s)", fdt.cpus().count()); - println!( - "...and has at least one memory location at: {:#X}\n", - fdt.memory().regions().next().unwrap().starting_address as usize - ); +// println!("This is a devicetree representation of a {}", fdt.root().model()); +// println!("...which is compatible with at least: {}", fdt.root().compatible().first()); +// println!("...and has {} CPU(s)", fdt.cpus().count()); +// println!( +// "...and has at least one memory location at: {:#X}\n", +// fdt.memory().regions().next().unwrap().starting_address as usize +// ); - let chosen = fdt.chosen(); - if let Some(bootargs) = chosen.bootargs() { - println!("The bootargs are: {:?}", bootargs); - } +// let chosen = fdt.chosen(); +// if let Some(bootargs) = chosen.bootargs() { +// println!("The bootargs are: {:?}", bootargs); +// } - if let Some(stdout) = chosen.stdout() { - println!( - "It would write stdout to: {} with params: {:?}", - stdout.node().name, - stdout.params() - ); - } +// if let Some(stdout) = chosen.stdout() { +// println!( +// "It would write stdout to: {} with params: {:?}", +// stdout.node().name, +// stdout.params() +// ); +// } - let soc = fdt.find_node("/soc"); - println!("Does it have a `/soc` node? {}", if soc.is_some() { "yes" } else { "no" }); - if let Some(soc) = soc { - println!("...and it has the following children:"); - for child in soc.children() { - println!(" {}", child.name); - } - } -} +// let soc = fdt.find_node("/soc"); +// println!("Does it have a `/soc` node? {}", if soc.is_some() { "yes" } else { "no" }); +// if let Some(soc) = soc { +// println!("...and it has the following children:"); +// for child in soc.children() { +// println!(" {}", child.name); +// } +// } +// } + +fn main() {} diff --git a/examples/tree_print.rs b/examples/tree_print.rs index 194562c..14a4806 100644 --- a/examples/tree_print.rs +++ b/examples/tree_print.rs @@ -1,18 +1,20 @@ -use fdt::node::FdtNode; +// use fdt::node::FdtNode; -static MY_FDT: &[u8] = include_bytes!("../dtb/test.dtb"); +// static MY_FDT: &[u8] = include_bytes!("../dtb/test.dtb"); -fn main() { - let fdt = fdt::Fdt::new(MY_FDT).unwrap(); +// fn main() { +// let fdt = fdt::Fdt::new(MY_FDT).unwrap(); - print_node(fdt.find_node("/").unwrap(), 0); -} +// print_node(fdt.find_node("/").unwrap(), 0); +// } -fn print_node(node: FdtNode<'_, '_>, n_spaces: usize) { - (0..n_spaces).for_each(|_| print!(" ")); - println!("{}/", node.name); +// fn print_node(node: FdtNode<'_, '_>, n_spaces: usize) { +// (0..n_spaces).for_each(|_| print!(" ")); +// println!("{}/", node.name); - for child in node.children() { - print_node(child, n_spaces + 4); - } -} +// for child in node.children() { +// print_node(child, n_spaces + 4); +// } +// } + +fn main() {} diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..e0b0c71 --- /dev/null +++ b/flake.lock @@ -0,0 +1,85 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1710146030, + "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1718318537, + "narHash": "sha256-4Zu0RYRcAY/VWuu6awwq4opuiD//ahpc2aFHg2CWqFY=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "e9ee548d90ff586a6471b4ae80ae9cfcbceb3420", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs", + "rust-overlay": "rust-overlay" + } + }, + "rust-overlay": { + "inputs": { + "flake-utils": [ + "flake-utils" + ], + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1718331519, + "narHash": "sha256-6Ru37wS8uec626nHVIh6hSpCYB7eNc3RPFa2U//bhw4=", + "owner": "oxalica", + "repo": "rust-overlay", + "rev": "419e7fae2731f41dd9b3e34dfe8802be68558b92", + "type": "github" + }, + "original": { + "owner": "oxalica", + "repo": "rust-overlay", + "type": "github" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..a543443 --- /dev/null +++ b/flake.nix @@ -0,0 +1,35 @@ +{ + description = "fdt development flake"; + + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + flake-utils.url = "github:numtide/flake-utils"; + rust-overlay = { + url = "github:oxalica/rust-overlay"; + inputs = { + nixpkgs.follows = "nixpkgs"; + flake-utils.follows = "flake-utils"; + }; + }; + }; + + outputs = { nixpkgs, flake-utils, rust-overlay, ... }: + flake-utils.lib.eachDefaultSystem(system: + let + overlays = [(import rust-overlay)]; + pkgs = import nixpkgs { + inherit system overlays; + }; + rust-bin = pkgs.rust-bin.stable.latest.default.override { + extensions = ["rust-src"]; + }; + in { + devShells.default = pkgs.mkShell { + buildInputs = [ + pkgs.nil + rust-bin + ]; + }; + } + ); +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 76ff7a3..d2bcd3f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -51,6 +51,9 @@ #![no_std] +#[cfg(test)] +extern crate std; + #[cfg(test)] mod tests; @@ -61,12 +64,16 @@ pub mod properties; pub mod standard_nodes; mod util; -use node::MemoryReservation; use parsing::{ - aligned::AlignedParser, unaligned::UnalignedParser, BigEndianU32, FdtData, NoPanic, Panic, - PanicMode, ParseError, Parser, + aligned::AlignedParser, unaligned::UnalignedParser, NoPanic, Panic, PanicMode, ParseError, + Parser, StringsBlock, }; -use standard_nodes::{Aliases, Chosen, Cpu, Memory, MemoryRange, MemoryRegion, Root}; +use standard_nodes::Root; +// use standard_nodes::{Aliases, Chosen, Cpu, Memory, MemoryRange, MemoryRegion, Root}; + +mod sealed { + pub trait Sealed {} +} /// Possible errors when attempting to create an `Fdt` #[derive(Debug, Clone, Copy)] @@ -103,7 +110,7 @@ impl core::fmt::Display for FdtError { #[derive(Clone, Copy)] pub struct Fdt<'a, P: Parser<'a>, Mode: PanicMode = Panic> { parser: P, - strings: &'a [u8], + strings: StringsBlock<'a>, structs: &'a [P::Granularity], header: FdtHeader, _mode: core::marker::PhantomData<*mut Mode>, @@ -115,11 +122,11 @@ impl<'a, P: Parser<'a>> core::fmt::Debug for Fdt<'a, P> { } } -impl<'a, P: Parser<'a>> core::fmt::Display for Fdt<'a, P> { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - pretty_print::print_node(f, self.root().node, 0) - } -} +// impl<'a, P: Parser<'a>> core::fmt::Display for Fdt<'a, P> { +// fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { +// pretty_print::print_node(f, self.root().node, 0) +// } +// } #[derive(Debug, Clone, Copy)] #[repr(C)] @@ -156,9 +163,10 @@ impl FdtHeader { impl<'a> Fdt<'a, UnalignedParser<'a>, Panic> { /// Construct a new `Fdt` from a byte buffer pub fn new_unaligned(data: &'a [u8]) -> Result { - let mut parser = UnalignedParser::new(data); + let mut parser = UnalignedParser::new(data, &[]); let header = parser.parse_header()?; - let strings = &data[header.strings_offset as usize..][..header.strings_size as usize]; + let strings = + StringsBlock(&data[header.strings_offset as usize..][..header.strings_size as usize]); let structs = &data[header.structs_offset as usize..][..header.structs_size as usize]; if !header.valid_magic() { @@ -180,7 +188,7 @@ impl<'a> Fdt<'a, UnalignedParser<'a>, Panic> { let tmp_header = core::slice::from_raw_parts(ptr, core::mem::size_of::()); let real_size = - usize::try_from(UnalignedParser::new(tmp_header).parse_header()?.total_size) + usize::try_from(UnalignedParser::new(tmp_header, &[]).parse_header()?.total_size) .map_err(|_| ParseError::NumericConversionError)?; Self::new_unaligned(core::slice::from_raw_parts(ptr, real_size)) @@ -190,14 +198,16 @@ impl<'a> Fdt<'a, UnalignedParser<'a>, Panic> { impl<'a> Fdt<'a, AlignedParser<'a>, Panic> { /// Construct a new `Fdt` from a `u32`-aligned buffer pub fn new(data: &'a [u32]) -> Result { - let mut parser = AlignedParser::new(data); + let mut parser = AlignedParser::new(data, &[]); let header = parser.parse_header()?; let strings_start = header.strings_offset as usize; let strings_end = strings_start + header.strings_size as usize; - let strings = util::cast_slice(data) - .get(strings_start..strings_end) - .ok_or(FdtError::ParseError(ParseError::UnexpectedEndOfData))?; + let strings = StringsBlock( + util::cast_slice(data) + .get(strings_start..strings_end) + .ok_or(FdtError::ParseError(ParseError::UnexpectedEndOfData))?, + ); let structs_start = header.structs_offset as usize / 4; let structs_end = structs_start + (header.structs_size as usize / 4); @@ -207,11 +217,17 @@ impl<'a> Fdt<'a, AlignedParser<'a>, Panic> { if !header.valid_magic() { return Err(FdtError::BadMagic); - } else if data.len() < header.total_size as usize { + } else if data.len() < (header.total_size / 4) as usize { return Err(FdtError::ParseError(ParseError::UnexpectedEndOfData)); } - Ok(Self { header, parser, strings, structs, _mode: core::marker::PhantomData }) + Ok(Self { + header, + parser: AlignedParser::new(structs, strings.0), + strings, + structs, + _mode: core::marker::PhantomData, + }) } /// # Safety @@ -223,8 +239,9 @@ impl<'a> Fdt<'a, AlignedParser<'a>, Panic> { } let tmp_header = core::slice::from_raw_parts(ptr, core::mem::size_of::()); - let real_size = usize::try_from(AlignedParser::new(tmp_header).parse_header()?.total_size) - .map_err(|_| ParseError::NumericConversionError)?; + let real_size = + usize::try_from(AlignedParser::new(tmp_header, &[]).parse_header()?.total_size) + .map_err(|_| ParseError::NumericConversionError)?; Self::new(core::slice::from_raw_parts(ptr, real_size)) } @@ -264,55 +281,55 @@ impl<'a> Fdt<'a, AlignedParser<'a>, NoPanic> { impl<'a, P: Parser<'a>, Mode: PanicMode> Fdt<'a, P, Mode> { /// Return the `/aliases` node, if one exists - pub fn aliases(&self) -> Option> { - Some(Aliases { - node: node::find_node(&mut FdtData::new(self.structs_block()), "/aliases", self, None)?, - header: self, - }) - } + // pub fn aliases(&self) -> Option> { + // Some(Aliases { + // node: node::find_node(&mut FdtData::new(self.structs_block()), "/aliases", self, None)?, + // header: self, + // }) + // } /// Searches for the `/chosen` node, which is always available - pub fn chosen(&self) -> Chosen<'_, 'a> { - node::find_node(&mut FdtData::new(self.structs_block()), "/chosen", self, None) - .map(|node| Chosen { node }) - .expect("/chosen is required") - } + // pub fn chosen(&self) -> Chosen<'_, 'a> { + // node::find_node(&mut FdtData::new(self.structs_block()), "/chosen", self, None) + // .map(|node| Chosen { node }) + // .expect("/chosen is required") + // } /// Return the `/cpus` node, which is always available - pub fn cpus(&self) -> impl Iterator> { - let parent = self.find_node("/cpus").expect("/cpus is a required node"); + // pub fn cpus(&self) -> impl Iterator> { + // let parent = self.find_node("/cpus").expect("/cpus is a required node"); - parent - .children() - .filter(|c| c.name.split('@').next().unwrap() == "cpu") - .map(move |cpu| Cpu { parent, node: cpu }) - } + // parent + // .children() + // .filter(|c| c.name.split('@').next().unwrap() == "cpu") + // .map(move |cpu| Cpu { parent, node: cpu }) + // } /// Returns the memory node, which is always available - pub fn memory(&self) -> Memory<'_, 'a> { - Memory { node: self.find_node("/memory").expect("requires memory node") } - } + // pub fn memory(&self) -> Memory<'_, 'a> { + // Memory { node: self.find_node("/memory").expect("requires memory node") } + // } /// Returns an iterator over the memory reservations - pub fn memory_reservations(&self) -> impl Iterator + 'a { - let mut stream = FdtData::new(&self.data[self.header.off_mem_rsvmap.to_ne() as usize..]); - let mut done = false; + // pub fn memory_reservations(&self) -> impl Iterator + 'a { + // let mut stream = FdtData::new(&self.data[self.header.off_mem_rsvmap.to_ne() as usize..]); + // let mut done = false; - core::iter::from_fn(move || { - if stream.is_empty() || done { - return None; - } + // core::iter::from_fn(move || { + // if stream.is_empty() || done { + // return None; + // } - let res = MemoryReservation::from_bytes(&mut stream)?; + // let res = MemoryReservation::from_bytes(&mut stream)?; - if res.address() as usize == 0 && res.size() == 0 { - done = true; - return None; - } + // if res.address() as usize == 0 && res.size() == 0 { + // done = true; + // return None; + // } - Some(res) - }) - } + // Some(res) + // }) + // } /// Return reference to raw data. This can be used to obtain the original pointer passed to /// [Fdt::from_ptr]. @@ -324,13 +341,16 @@ impl<'a, P: Parser<'a>, Mode: PanicMode> Fdt<'a, P, Mode> { /// let fdt = unsafe{fdt::Fdt::from_ptr(original_pointer)}.unwrap(); /// assert_eq!(fdt.raw_data().as_ptr(), original_pointer); /// ``` - pub fn raw_data(&self) -> &'a [P::Granularity] { - self.data - } + // pub fn raw_data(&self) -> &'a [P::Granularity] { + // // self.structs + // } /// Return the root (`/`) node, which is always available - pub fn root(&self) -> Root<'_, 'a> { - Root { node: self.find_node("/").expect("/ is a required node") } + pub fn root( + &self, + ) -> ::Output>::Granularity, Mode>> { + let mut parser = self.parser.clone(); + ::to_output(parser.parse_root::().map(|node| Root { node })) } /// Returns the first node that matches the node path, if you want all that @@ -344,28 +364,28 @@ impl<'a, P: Parser<'a>, Mode: PanicMode> Fdt<'a, P, Mode> { /// Note: if the address of a node name is left out, the search will find /// the first node that has a matching name, ignoring the address portion if /// it exists. - pub fn find_node(&self, path: &str) -> Option> { - let node = node::find_node(&mut FdtData::new(self.structs_block()), path, self, None); - node.or_else(|| self.aliases()?.resolve_node(path)) - } + // pub fn find_node(&self, path: &str) -> Option> { + // let node = node::find_node(&mut FdtData::new(self.structs_block()), path, self, None); + // node.or_else(|| self.aliases()?.resolve_node(path)) + // } /// Searches for a node which contains a `compatible` property and contains /// one of the strings inside of `with` - pub fn find_compatible(&self, with: &[&str]) -> Option> { - self.all_nodes().find(|n| { - n.compatible().and_then(|compats| compats.all().find(|c| with.contains(c))).is_some() - }) - } + // pub fn find_compatible(&self, with: &[&str]) -> Option> { + // self.all_nodes().find(|n| { + // n.compatible().and_then(|compats| compats.all().find(|c| with.contains(c))).is_some() + // }) + // } /// Searches for the given `phandle` - pub fn find_phandle(&self, phandle: u32) -> Option> { - self.all_nodes().find(|n| { - n.properties() - .find(|p| p.name == "phandle") - .and_then(|p| Some(BigEndianU32::from_bytes(p.value)?.to_ne() == phandle)) - .unwrap_or(false) - }) - } + // pub fn find_phandle(&self, phandle: u32) -> Option> { + // self.all_nodes().find(|n| { + // n.properties() + // .find(|p| p.name == "phandle") + // .and_then(|p| Some(BigEndianU32::from_bytes(p.value)?.to_ne() == phandle)) + // .unwrap_or(false) + // }) + // } /// Returns an iterator over all of the available nodes with the given path. /// This does **not** attempt to find any node with the same name as the @@ -394,54 +414,54 @@ impl<'a, P: Parser<'a>, Mode: PanicMode> Fdt<'a, P, Mode> { /// virtio_mmio@10002000 /// virtio_mmio@10001000 /// ``` - pub fn find_all_nodes(&self, path: &'a str) -> impl Iterator> { - let mut done = false; - let only_root = path == "/"; - let valid_path = path.chars().fold(0, |acc, c| acc + if c == '/' { 1 } else { 0 }) >= 1; - - let mut path_split = path.rsplitn(2, '/'); - let child_name = path_split.next().unwrap(); - let parent = match path_split.next() { - Some("") => Some(self.root().node), - Some(s) => node::find_node(&mut FdtData::new(self.structs_block()), s, self, None), - None => None, - }; - - let (parent, bad_parent) = match parent { - Some(parent) => (parent, false), - None => (self.find_node("/").unwrap(), true), - }; - - let mut child_iter = parent.children(); - - core::iter::from_fn(move || { - if done || !valid_path || bad_parent { - return None; - } - - if only_root { - done = true; - return self.find_node("/"); - } - - let mut ret = None; - - #[allow(clippy::while_let_on_iterator)] - while let Some(child) = child_iter.next() { - if child.name.split('@').next()? == child_name { - ret = Some(child); - break; - } - } - - ret - }) - } + // pub fn find_all_nodes(&self, path: &'a str) -> impl Iterator> { + // let mut done = false; + // let only_root = path == "/"; + // let valid_path = path.chars().fold(0, |acc, c| acc + if c == '/' { 1 } else { 0 }) >= 1; + + // let mut path_split = path.rsplitn(2, '/'); + // let child_name = path_split.next().unwrap(); + // let parent = match path_split.next() { + // Some("") => Some(self.root().node), + // Some(s) => node::find_node(&mut FdtData::new(self.structs_block()), s, self, None), + // None => None, + // }; + + // let (parent, bad_parent) = match parent { + // Some(parent) => (parent, false), + // None => (self.find_node("/").unwrap(), true), + // }; + + // let mut child_iter = parent.children(); + + // core::iter::from_fn(move || { + // if done || !valid_path || bad_parent { + // return None; + // } + + // if only_root { + // done = true; + // return self.find_node("/"); + // } + + // let mut ret = None; + + // #[allow(clippy::while_let_on_iterator)] + // while let Some(child) = child_iter.next() { + // if child.name.split('@').next()? == child_name { + // ret = Some(child); + // break; + // } + // } + + // ret + // }) + // } /// Returns an iterator over all of the nodes in the devicetree, depth-first - pub fn all_nodes(&self) -> impl Iterator> { - node::all_nodes(self) - } + // pub fn all_nodes(&self) -> impl Iterator> { + // node::all_nodes(self) + // } /// Returns an iterator over all of the strings inside of the strings block pub fn strings(&self) -> impl Iterator { @@ -462,7 +482,7 @@ impl<'a, P: Parser<'a>, Mode: PanicMode> Fdt<'a, P, Mode> { /// Total size of the devicetree in bytes pub fn total_size(&self) -> usize { - self.header.totalsize.to_ne() as usize + self.header.total_size as usize } fn cstr_at_offset(&self, offset: usize) -> &'a core::ffi::CStr { @@ -475,10 +495,10 @@ impl<'a, P: Parser<'a>, Mode: PanicMode> Fdt<'a, P, Mode> { } fn strings_block(&self) -> &'a [u8] { - &self.data[self.header.strings_range()] + self.strings.0 } - fn structs_block(&self) -> &'a [u8] { - &self.data[self.header.struct_range()] + fn structs_block(&self) -> &'a [P::Granularity] { + self.structs } } diff --git a/src/nodes.rs b/src/nodes.rs index 76f77c7..2ba1e3f 100644 --- a/src/nodes.rs +++ b/src/nodes.rs @@ -1,16 +1,51 @@ use crate::{ - parsing::{BigEndianToken, Panic, PanicMode, ParseError, Parser, ParserForSize, StringsBlock}, + parsing::{ + self, BigEndianToken, Panic, PanicMode, ParseError, Parser, ParserForSize, StringsBlock, + }, properties::Property, }; +#[macro_export] +#[doc(hidden)] macro_rules! tryblock { ($($ts:tt)+) => {{ - (|| -> Result<_, ParseError> { + (|| -> Result<_, $crate::parsing::ParseError> { $($ts)+ })() }}; } +#[derive(Debug, Clone, Copy)] +pub enum SearchableNodeName<'a> { + Base(&'a str), + WithUnitAddress(NodeName<'a>), +} + +pub trait IntoSearchableNodeName<'a>: Sized + crate::sealed::Sealed { + fn into_searchable_node_name(self) -> SearchableNodeName<'a>; +} + +impl crate::sealed::Sealed for NodeName<'_> {} +impl<'a> IntoSearchableNodeName<'a> for NodeName<'a> { + fn into_searchable_node_name(self) -> SearchableNodeName<'a> { + SearchableNodeName::WithUnitAddress(self) + } +} + +impl crate::sealed::Sealed for &'_ str {} +impl<'a> IntoSearchableNodeName<'a> for &'a str { + fn into_searchable_node_name(self) -> SearchableNodeName<'a> { + match self.split_once('@') { + Some((base, unit_address)) => SearchableNodeName::WithUnitAddress(NodeName { + name: base, + unit_address: Some(unit_address), + }), + None => SearchableNodeName::Base(self), + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct NodeName<'a> { pub name: &'a str, pub unit_address: Option<&'a str>, @@ -22,11 +57,20 @@ impl<'a> NodeName<'a> { } } +impl core::fmt::Display for NodeName<'_> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self.unit_address { + Some(ua) => write!(f, "{}@{}", self.name, ua), + None => write!(f, "{}", self.name), + } + } +} + pub struct Node<'a, Granularity: ParserForSize = u32, Mode: PanicMode = Panic> { - this: &'a RawNode, - parent: Option<&'a RawNode>, - strings: StringsBlock<'a>, - _mode: core::marker::PhantomData<*mut Mode>, + pub(crate) this: &'a RawNode, + pub(crate) parent: Option<&'a RawNode>, + pub(crate) strings: StringsBlock<'a>, + pub(crate) _mode: core::marker::PhantomData<*mut Mode>, } impl<'a, Granularity: ParserForSize, Mode: PanicMode> Node<'a, Granularity, Mode> { @@ -49,6 +93,10 @@ impl<'a, Granularity: ParserForSize, Mode: PanicMode> Node<'a, Granularity, Mode .advance_cstr() .and_then(|s| s.to_str().map_err(|_| ParseError::InvalidCStrValue)) .map(|s| { + if s.is_empty() { + return NodeName { name: "/", unit_address: None }; + } + let (name, unit_address) = s.split_once('@').unzip(); NodeName { name: name.unwrap_or(s), unit_address } }), @@ -81,9 +129,7 @@ impl<'a, Granularity: ParserForSize, Mode: PanicMode> Node<'a, Granularity, Mode } pub fn property>(&self) -> ::Output> { - ::to_output(tryblock! { - ::to_result(self.raw_property(P::NAME))?.map(|prop| P::parse(*self, prop)).transpose() - }) + ::to_output(P::parse(*self)) } #[inline] @@ -116,6 +162,24 @@ impl<'a, Granularity: ParserForSize, Mode: PanicMode> Node<'a, Granularity, Mode } } +impl<'a, Granularity: ParserForSize, Mode: PanicMode> core::fmt::Debug + for Node<'a, Granularity, Mode> +where + ::Output>: core::fmt::Debug, +{ + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("Node").field("name", &self.name()).finish_non_exhaustive() + } +} + +impl<'a, Granularity: ParserForSize, Mode: PanicMode> Clone for Node<'a, Granularity, Mode> { + fn clone(&self) -> Self { + *self + } +} + +impl<'a, Granularity: ParserForSize, Mode: PanicMode> Copy for Node<'a, Granularity, Mode> {} + #[repr(transparent)] pub(crate) struct RawNode([Granularity]); @@ -124,9 +188,14 @@ impl RawNode { // SAFETY: the representation of `Self` and `data` are the same unsafe { core::mem::transmute(data) } } + + pub(crate) fn as_slice(&self) -> &[Granularity] { + // SAFETY: the representation of `Self` and `data` are the same + unsafe { core::mem::transmute(self) } + } } -#[derive(Debug, Clone, Copy)] +#[derive(Debug)] pub struct NodeProperties<'a, Granularity: ParserForSize = u32, Mode: PanicMode = Panic> { data: &'a [Granularity], strings: StringsBlock<'a>, @@ -144,6 +213,16 @@ impl<'a, Granularity: ParserForSize, Mode: PanicMode> NodeProperties<'a, Granula self.strings.0, ); + match parser.peek_token() { + Ok(BigEndianToken::PROP) => {} + Ok(BigEndianToken::BEGIN_NODE) => return ::to_output(Ok(None)), + Ok(_) => return ::to_output(Err(ParseError::UnexpectedToken)), + Err(ParseError::UnexpectedEndOfData) => { + return ::to_output(Ok(None)) + } + Err(e) => return ::to_output(Err(e)), + } + ::to_output(tryblock! { match parser.parse_raw_property() { Ok((name_offset, data)) => { @@ -164,6 +243,14 @@ impl<'a, Granularity: ParserForSize, Mode: PanicMode> NodeProperties<'a, Granula } } +impl Clone for NodeProperties<'_, Granularity, Mode> { + fn clone(&self) -> Self { + *self + } +} + +impl Copy for NodeProperties<'_, Granularity, Mode> {} + impl<'a, Granularity: ParserForSize, Mode: PanicMode> IntoIterator for NodeProperties<'a, Granularity, Mode> { @@ -197,7 +284,10 @@ pub trait Value<'a>: Sized { impl<'a> Value<'a> for u32 { fn parse(value: &'a [u8]) -> Option { - unsafe { core::ptr::read_unaligned(value.as_ptr().cast()) } + match value { + [a, b, c, d, ..] => Some(u32::from_be_bytes([*a, *b, *c, *d])), + _ => None, + } } } @@ -219,10 +309,11 @@ impl<'a> NodeProperty<'a> { self.value } - pub fn to>(&self) -> Option {} + pub fn to>(&self) -> Option { + V::parse(self.value) + } } -#[derive(Clone, Copy)] pub struct NodeChildren<'a, Granularity: ParserForSize = u32, Mode: PanicMode = Panic> { data: &'a [Granularity], parent: &'a RawNode, @@ -241,38 +332,64 @@ impl<'a, Granularity: ParserForSize, Mode: PanicMode> NodeChildren<'a, Granulari self.strings.0, ); - ::to_output(tryblock! { - match parser.parse_node(Some(self.parent)) { - Ok(node) => { - self.data = parser.data(); + match parser.peek_token() { + Ok(BigEndianToken::BEGIN_NODE) => {} + Ok(BigEndianToken::END_NODE) => return ::to_output(Ok(None)), + Ok(_) => return ::to_output(Err(ParseError::UnexpectedToken)), + Err(ParseError::UnexpectedEndOfData) => { + return ::to_output(Ok(None)) + } + Err(e) => return ::to_output(Err(e)), + } - Ok(Some(node)) - } - Err(ParseError::UnexpectedEndOfData) => Ok(None), - Err(e) => return Err(e), + ::to_output(match parser.parse_node(Some(self.parent)) { + Ok(node) => { + self.data = parser.data(); + + Ok(Some(node)) } + Err(ParseError::UnexpectedEndOfData) => Ok(None), + Err(e) => Err(e), }) } - pub fn find( + pub fn find<'n, N>( &self, - name: &str, - ) -> ::Output>> { + name: N, + ) -> ::Output>> + where + N: IntoSearchableNodeName<'n>, + Mode: 'a, + { + let name = name.into_searchable_node_name(); ::reverse_transpose(self.iter().find(|n| { ::ok_as_ref(n) - .and_then(|n| ::ok_as_ref(&n.name())) - .map(|n| n.name == name) + .and_then(|n| ::ok(n.name())) + .map(|n| match name { + SearchableNodeName::Base(base) => n.name == base, + SearchableNodeName::WithUnitAddress(nn) => n == nn, + }) .unwrap_or_default() })) } } +impl<'a, Granularity: ParserForSize, Mode: PanicMode> Clone + for NodeChildren<'a, Granularity, Mode> +{ + fn clone(&self) -> Self { + Self { _mode: self._mode, data: self.data, strings: self.strings, parent: self.parent } + } +} + +impl<'a, Granularity: ParserForSize, Mode: PanicMode> Copy for NodeChildren<'a, Granularity, Mode> {} + #[derive(Clone)] pub struct NodeChildrenIter<'a, Granularity: ParserForSize = u32, Mode: PanicMode = Panic> { children: NodeChildren<'a, Granularity, Mode>, } -impl<'a, Granularity: ParserForSize, Mode: PanicMode> Iterator +impl<'a, Granularity: ParserForSize, Mode: PanicMode + 'a> Iterator for NodeChildrenIter<'a, Granularity, Mode> { type Item = ::Output>; diff --git a/src/parsing.rs b/src/parsing.rs index f1f7fc2..b16e079 100644 --- a/src/parsing.rs +++ b/src/parsing.rs @@ -79,7 +79,7 @@ impl BigEndianU32 { } pub const fn from_le(n: u32) -> Self { - Self(u32::from_le(n)) + Self(u32::from_le(n).to_be()) } pub const fn from_be(n: u32) -> Self { @@ -109,7 +109,7 @@ impl BigEndianU64 { } pub const fn from_le(n: u64) -> Self { - Self(u64::from_le(n)) + Self(u64::from_le(n).to_be()) } pub const fn from_be(n: u64) -> Self { @@ -138,7 +138,7 @@ impl BigEndianToken { pub const END_NODE: Self = Self(BigEndianU32::from_ne(2)); pub const PROP: Self = Self(BigEndianU32::from_ne(3)); pub const NOP: Self = Self(BigEndianU32::from_ne(4)); - pub const END: Self = Self(BigEndianU32::from_ne(5)); + pub const END: Self = Self(BigEndianU32::from_ne(9)); } pub(crate) struct Stream<'a, T: Copy>(&'a [T]); @@ -167,10 +167,6 @@ impl<'a, T: Copy> Clone for Stream<'a, T> { } } -mod sealed { - pub trait Sealed {} -} - #[derive(Debug, Clone, Copy)] pub enum ParseError { NumericConversionError, @@ -185,6 +181,7 @@ impl core::fmt::Display for ParseError { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { Self::InvalidCStrValue => write!(f, "cstr was either non-terminated or invalid ASCII"), + Self::InvalidPropertyValue => write!(f, "invalid property value"), Self::InvalidTokenValue => { write!(f, "encountered invalid FDT token value while parsing") } @@ -202,21 +199,21 @@ impl core::fmt::Display for ParseError { } } -pub trait ParserForSize: sealed::Sealed { +pub trait ParserForSize: crate::sealed::Sealed { type Parser<'a>: Parser<'a, Granularity = Self>; } -impl sealed::Sealed for u8 {} +impl crate::sealed::Sealed for u8 {} impl ParserForSize for u8 { type Parser<'a> = UnalignedParser<'a>; } -impl sealed::Sealed for u32 {} +impl crate::sealed::Sealed for u32 {} impl ParserForSize for u32 { type Parser<'a> = AlignedParser<'a>; } -pub trait PanicMode: sealed::Sealed { +pub trait PanicMode: crate::sealed::Sealed + 'static { type Output; fn to_output(result: Result) -> Self::Output; fn transpose(result: Self::Output>) -> Option>; @@ -228,7 +225,7 @@ pub trait PanicMode: sealed::Sealed { pub struct NoPanic; -impl sealed::Sealed for NoPanic {} +impl crate::sealed::Sealed for NoPanic {} impl PanicMode for NoPanic { type Output = Result; @@ -246,7 +243,7 @@ impl PanicMode for NoPanic { } fn ok_as_ref(output: &Self::Output) -> Option<&T> { - output.ok().as_ref() + output.as_ref().ok() } fn ok(output: Self::Output) -> Option { @@ -260,7 +257,7 @@ impl PanicMode for NoPanic { pub struct Panic; -impl sealed::Sealed for Panic {} +impl crate::sealed::Sealed for Panic {} impl PanicMode for Panic { type Output = T; @@ -293,7 +290,7 @@ impl PanicMode for Panic { } } -pub trait Parser<'a>: sealed::Sealed + Clone { +pub trait Parser<'a>: crate::sealed::Sealed + Clone { type Granularity: Copy + ParserForSize; fn new(data: &'a [Self::Granularity], strings: &'a [u8]) -> Self; @@ -345,18 +342,71 @@ pub trait Parser<'a>: sealed::Sealed + Clone { }) } - fn parse_node( + fn parse_root( &mut self, - parent: Option<&'a RawNode>, ) -> Result, ParseError> { - let starting_len = self.data().len(); + match self.advance_token()? { + BigEndianToken::BEGIN_NODE => {} + _ => return Err(ParseError::UnexpectedToken), + } + let starting_data = self.data(); + match core::mem::size_of::() { + 1 => { + let byte_data = self.byte_data(); + match byte_data.get(byte_data.len() - 4..).map(<[u8; 4]>::try_from) { + Some(Ok(data @ [_, _, _, _])) => { + match BigEndianToken(BigEndianU32(u32::from_ne_bytes(data))) { + BigEndianToken::END => {} + _ => return Err(ParseError::UnexpectedToken), + } + } + _ => return Err(ParseError::UnexpectedEndOfData), + } + + Ok(Node { + this: RawNode::new(&starting_data[..starting_data.len() - 4]), + parent: None, + strings: self.strings(), + _mode: core::marker::PhantomData, + }) + } + 4 => { + let byte_data = self.byte_data(); + match byte_data.get(byte_data.len() - 4..).map(<[u8; 4]>::try_from) { + Some(Ok(data @ [_, _, _, _])) => { + match BigEndianToken(BigEndianU32(u32::from_ne_bytes(data))) { + BigEndianToken::END => {} + _ => return Err(ParseError::UnexpectedToken), + } + } + _ => return Err(ParseError::UnexpectedEndOfData), + } + + Ok(Node { + this: RawNode::new(&starting_data[..starting_data.len() - 1]), + parent: None, + strings: self.strings(), + _mode: core::marker::PhantomData, + }) + } + _ => unreachable!(), + } + } + + fn parse_node( + &mut self, + parent: Option<&'a RawNode>, + ) -> Result, ParseError> { match self.advance_token()? { BigEndianToken::BEGIN_NODE => {} _ => return Err(ParseError::UnexpectedToken), } + let starting_data = self.data(); + let starting_len = starting_data.len(); + self.advance_cstr()?; while self.peek_token()? == BigEndianToken::PROP { @@ -375,6 +425,7 @@ pub trait Parser<'a>: sealed::Sealed + Clone { continue; } }, + _ => return Err(ParseError::InvalidTokenValue), } self.advance_cstr()?; diff --git a/src/parsing/aligned.rs b/src/parsing/aligned.rs index 9461b7a..8e8de36 100644 --- a/src/parsing/aligned.rs +++ b/src/parsing/aligned.rs @@ -2,9 +2,7 @@ // v. 2.0. If a copy of the MPL was not distributed with this file, You can // obtain one at https://mozilla.org/MPL/2.0/. -use super::{ - sealed, BigEndianToken, BigEndianU32, BigEndianU64, ParseError, Parser, Stream, StringsBlock, -}; +use super::{BigEndianToken, BigEndianU32, BigEndianU64, ParseError, Parser, Stream, StringsBlock}; pub struct AlignedParser<'a> { stream: Stream<'a, u32>, @@ -13,11 +11,11 @@ pub struct AlignedParser<'a> { impl Clone for AlignedParser<'_> { fn clone(&self) -> Self { - Self { stream: self.stream.clone(), strings: self.strings.clone() } + Self { stream: self.stream.clone(), strings: self.strings } } } -impl sealed::Sealed for AlignedParser<'_> {} +impl crate::sealed::Sealed for AlignedParser<'_> {} impl<'a> Parser<'a> for AlignedParser<'a> { type Granularity = u32; @@ -82,14 +80,14 @@ impl<'a> Parser<'a> for AlignedParser<'a> { let bytes = unsafe { core::slice::from_raw_parts( self.stream.0.as_ptr().cast::(), - core::mem::size_of_val(self.stream.0), + self.stream.0.len() * 4, ) }; let cstr = core::ffi::CStr::from_bytes_until_nul(bytes) .map_err(|_| ParseError::InvalidCStrValue)?; // Round up to the next multiple of 4, if necessary - let skip = ((cstr.to_bytes().len() + 3) & !3) / 4; + let skip = ((cstr.to_bytes_with_nul().len() + 3) & !3) / 4; self.stream.skip_many(skip); Ok(cstr) diff --git a/src/parsing/unaligned.rs b/src/parsing/unaligned.rs index f3d6a11..191c3fc 100644 --- a/src/parsing/unaligned.rs +++ b/src/parsing/unaligned.rs @@ -2,9 +2,7 @@ // v. 2.0. If a copy of the MPL was not distributed with this file, You can // obtain one at https://mozilla.org/MPL/2.0/. -use super::{ - sealed, BigEndianToken, BigEndianU32, BigEndianU64, ParseError, Parser, Stream, StringsBlock, -}; +use super::{BigEndianToken, BigEndianU32, BigEndianU64, ParseError, Parser, Stream, StringsBlock}; pub struct UnalignedParser<'a> { stream: Stream<'a, u8>, @@ -13,11 +11,11 @@ pub struct UnalignedParser<'a> { impl Clone for UnalignedParser<'_> { fn clone(&self) -> Self { - Self { stream: self.stream.clone(), strings: self.strings.clone() } + Self { stream: self.stream.clone(), strings: self.strings } } } -impl sealed::Sealed for UnalignedParser<'_> {} +impl crate::sealed::Sealed for UnalignedParser<'_> {} impl<'a> Parser<'a> for UnalignedParser<'a> { type Granularity = u8; @@ -79,7 +77,7 @@ impl<'a> Parser<'a> for UnalignedParser<'a> { .map_err(|_| ParseError::InvalidCStrValue)?; // Round up to the next multiple of 4, if necessary - let skip = (cstr.to_bytes().len() + 3) & !3; + let skip = (cstr.to_bytes_with_nul().len() + 3) & !3; self.stream.skip_many(skip); Ok(cstr) diff --git a/src/pretty_print.rs b/src/pretty_print.rs index 08725d7..771002f 100644 --- a/src/pretty_print.rs +++ b/src/pretty_print.rs @@ -2,96 +2,99 @@ // v. 2.0. If a copy of the MPL was not distributed with this file, You can // obtain one at https://mozilla.org/MPL/2.0/. -pub fn print_node( - f: &mut core::fmt::Formatter<'_>, - node: crate::node::FdtNode<'_, '_>, - n_spaces: usize, -) -> core::fmt::Result { - write!(f, "{:width$}", ' ', width = n_spaces)?; - writeln!(f, "{} {{", if node.name.is_empty() { "/" } else { node.name })?; - let mut were_props = false; - for prop in node.properties() { - were_props = true; +// pub fn print_node( +// f: &mut core::fmt::Formatter<'_>, +// node: crate::node::FdtNode<'_, '_>, +// n_spaces: usize, +// ) -> core::fmt::Result { +// write!(f, "{:width$}", ' ', width = n_spaces)?; +// writeln!(f, "{} {{", if node.name.is_empty() { "/" } else { node.name })?; +// let mut were_props = false; +// for prop in node.properties() { +// were_props = true; - match prop.name { - "reg" => { - write!(f, "{:width$}reg = <", ' ', width = n_spaces + 4)?; - for (i, reg) in node.reg().unwrap().enumerate() { - if i > 0 { - write!(f, " ")?; - } +// match prop.name { +// "reg" => { +// write!(f, "{:width$}reg = <", ' ', width = n_spaces + 4)?; +// for (i, reg) in node.reg().unwrap().enumerate() { +// if i > 0 { +// write!(f, " ")?; +// } - match reg.size { - Some(size) => { - write!(f, "{:#x} {:#x}", reg.starting_address as usize, size)? - } - None => write!(f, "{:#x}", reg.starting_address as usize)?, - } - } - writeln!(f, ">")?; - } - "compatible" => writeln!( - f, - "{:width$}compatible = {:?}", - ' ', - prop.as_str().unwrap(), - width = n_spaces + 4 - )?, - name if name.contains("-cells") => { - writeln!( - f, - "{:width$}{} = <{:#x}>", - ' ', - name, - prop.as_usize().unwrap(), - width = n_spaces + 4 - )?; - } - _ => match prop.as_str() { - Some(value) if !value.is_empty() => { - writeln!(f, "{:width$}{} = {:?}", ' ', prop.name, value, width = n_spaces + 4)? - } - _ => match prop.value.len() { - 4 | 8 => writeln!( - f, - "{:width$}{} = <{:#x}>", - ' ', - prop.name, - prop.as_usize().unwrap(), - width = n_spaces + 4 - )?, - _ => writeln!( - f, - "{:width$}{} = {:?}", - ' ', - prop.name, - prop.value, - width = n_spaces + 4 - )?, - }, - }, - } - } +// match reg.size { +// Some(size) => { +// write!(f, "{:#x} {:#x}", reg.starting_address as usize, size)? +// } +// None => write!(f, "{:#x}", reg.starting_address as usize)?, +// } +// } +// writeln!(f, ">")?; +// } +// "compatible" => writeln!( +// f, +// "{:width$}compatible = {:?}", +// ' ', +// prop.as_str().unwrap(), +// width = n_spaces + 4 +// )?, +// name if name.contains("-cells") => { +// writeln!( +// f, +// "{:width$}{} = <{:#x}>", +// ' ', +// name, +// prop.as_usize().unwrap(), +// width = n_spaces + 4 +// )?; +// } +// _ => match prop.as_str() { +// Some(value) +// if (!value.is_empty() && value.chars().all(|c| c.is_ascii_graphic())) +// || prop.value == [0] => +// { +// writeln!(f, "{:width$}{} = {:?}", ' ', prop.name, value, width = n_spaces + 4)? +// } +// _ => match prop.value.len() { +// 4 | 8 => writeln!( +// f, +// "{:width$}{} = <{:#x}>", +// ' ', +// prop.name, +// prop.as_usize().unwrap(), +// width = n_spaces + 4 +// )?, +// _ => writeln!( +// f, +// "{:width$}{} = {:?}", +// ' ', +// prop.name, +// prop.value, +// width = n_spaces + 4 +// )?, +// }, +// }, +// } +// } - if node.children().next().is_some() && were_props { - writeln!(f)?; - } +// if node.children().next().is_some() && were_props { +// writeln!(f)?; +// } - let mut first = true; - for child in node.children() { - if !first { - writeln!(f)?; - } +// let mut first = true; +// for child in node.children() { +// if !first { +// writeln!(f)?; +// } - print_node(f, child, n_spaces + 4)?; - first = false; - } +// print_node(f, child, n_spaces + 4)?; +// first = false; +// } - if n_spaces > 0 { - write!(f, "{:width$}", ' ', width = n_spaces)?; - } +// if n_spaces > 0 { +// write!(f, "{:width$}", ' ', width = n_spaces)?; +// } - writeln!(f, "}};")?; +// writeln!(f, "}};")?; - Ok(()) -} +// Ok(()) +// } diff --git a/src/properties.rs b/src/properties.rs index 0d982a2..9a64758 100644 --- a/src/properties.rs +++ b/src/properties.rs @@ -4,7 +4,7 @@ use crate::{ }; pub trait Property<'a>: Sized { - fn parse( + fn parse<'b, Granularity, Mode>( node: Node<'a, Granularity, Mode>, ) -> Result, ParseError> where @@ -19,7 +19,7 @@ pub struct CellSizes { impl<'a> Property<'a> for CellSizes { #[track_caller] - fn parse( + fn parse<'b, Granularity, Mode>( node: Node<'a, Granularity, Mode>, ) -> Result, ParseError> where diff --git a/src/standard_nodes.rs b/src/standard_nodes.rs index d63d4ba..ee62a15 100644 --- a/src/standard_nodes.rs +++ b/src/standard_nodes.rs @@ -3,54 +3,75 @@ // obtain one at https://mozilla.org/MPL/2.0/. use crate::{ - node::{CellSizes, FdtNode, NodeProperty}, - parsing::{BigEndianU32, BigEndianU64, FdtData}, - Fdt, + nodes::{IntoSearchableNodeName, Node, NodeName, RawNode, SearchableNodeName}, + parsing::{ + BigEndianToken, BigEndianU32, BigEndianU64, FdtData, Panic, PanicMode, ParseError, Parser, + ParserForSize, + }, + properties::CellSizes, + tryblock, Fdt, }; /// Represents the `/chosen` node with specific helper methods -#[derive(Debug, Clone, Copy)] -pub struct Chosen<'b, 'a: 'b> { - pub(crate) node: FdtNode<'b, 'a>, +pub struct Chosen<'a, Granularity = u32, Mode = Panic> +where + Granularity: ParserForSize, + Mode: PanicMode, +{ + pub(crate) node: Node<'a, Granularity, Mode>, } -impl<'b, 'a: 'b> Chosen<'b, 'a> { +impl<'a, Granularity, Mode> Chosen<'a, Granularity, Mode> +where + Granularity: ParserForSize, + Mode: PanicMode, +{ /// Contains the bootargs, if they exist - pub fn bootargs(self) -> Option<&'a str> { - self.node - .properties() - .find(|n| n.name == "bootargs") - .and_then(|n| core::str::from_utf8(&n.value[..n.value.len() - 1]).ok()) + #[track_caller] + pub fn bootargs(self) -> Mode::Output> { + ::to_output(crate::tryblock! { + Ok( + ::to_result(self.node.properties())? + .into_iter() + .find(|n| n.name() == "bootargs") + .and_then(|n| { + core::str::from_utf8(&n.value()[..n.value().len() - 1]).ok() + }) + ) + }) } /// Searches for the node representing `stdout`, if the property exists, /// attempting to resolve aliases if the node name doesn't exist as-is - pub fn stdout(self) -> Option> { - self.node - .properties() - .find(|n| n.name == "stdout-path") - .and_then(|n| core::str::from_utf8(&n.value[..n.value.len() - 1]).ok()) - .map(Self::split_stdinout_property) - .and_then(|(name, params)| { - self.node.header.find_node(name).map(|node| StdInOutPath::new(node, params)) - }) - } + // pub fn stdout(self) -> Mode::Output>> { + // ::to_output(crate::tryblock! { + // Ok( + // ::to_result(self.node.properties())? + // .find(|n| n.name == "stdout-path") + // .and_then(|n| core::str::from_utf8(&n.value()[..n.value().len() - 1]).ok()) + // .map(Self::split_stdinout_property) + // .and_then(|(name, params)| { + // self.node..find_node(name).map(|node| StdInOutPath::new(node, params)) + // }) + // ) + // }) + // } /// Searches for the node representing `stdout`, if the property exists, /// attempting to resolve aliases if the node name doesn't exist as-is. If /// no `stdin` property exists, but `stdout` is present, it will return the /// node specified by the `stdout` property. - pub fn stdin(self) -> Option> { - self.node - .properties() - .find(|n| n.name == "stdin-path") - .and_then(|n| core::str::from_utf8(&n.value[..n.value.len() - 1]).ok()) - .map(Self::split_stdinout_property) - .and_then(|(name, params)| { - self.node.header.find_node(name).map(|node| StdInOutPath::new(node, params)) - }) - .or_else(|| self.stdout()) - } + // pub fn stdin(self) -> Option> { + // self.node + // .properties() + // .find(|n| n.name == "stdin-path") + // .and_then(|n| core::str::from_utf8(&n.value[..n.value.len() - 1]).ok()) + // .map(Self::split_stdinout_property) + // .and_then(|(name, params)| { + // self.node.header.find_node(name).map(|node| StdInOutPath::new(node, params)) + // }) + // .or_else(|| self.stdout()) + // } /// Splits a stdout-path or stdin-path property into its node path and optional parameters which are seperated by a colon ':'. /// see https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#chosen-node @@ -64,188 +85,345 @@ impl<'b, 'a: 'b> Chosen<'b, 'a> { } } -pub struct StdInOutPath<'b, 'a> { - pub(crate) node: FdtNode<'b, 'a>, +impl Clone for Chosen<'_, Granularity, Mode> { + fn clone(&self) -> Self { + Self { node: self.node } + } +} + +impl Copy for Chosen<'_, Granularity, Mode> {} + +pub struct StdInOutPath<'a, Granularity: ParserForSize = u32, Mode: PanicMode = Panic> { + pub(crate) node: Node<'a, Granularity, Mode>, pub(crate) params: Option<&'a str>, } -impl<'b, 'a> StdInOutPath<'b, 'a> { - fn new(node: FdtNode<'b, 'a>, params: Option<&'a str>) -> Self { - Self { node, params } - } +// impl<'b, 'a> StdInOutPath<'b, 'a> { +// fn new(node: FdtNode<'b, 'a>, params: Option<&'a str>) -> Self { +// Self { node, params } +// } - pub fn name(&self) -> &'a str { - self.node.name - } +// pub fn name(&self) -> &'a str { +// self.node.name +// } - pub fn node(&self) -> FdtNode<'b, 'a> { - self.node - } +// pub fn node(&self) -> FdtNode<'b, 'a> { +// self.node +// } - pub fn params(&self) -> Option<&'a str> { - self.params - } -} +// pub fn params(&self) -> Option<&'a str> { +// self.params +// } +// } /// Represents the root (`/`) node with specific helper methods -#[derive(Debug, Clone, Copy)] -pub struct Root<'b, 'a: 'b> { - pub(crate) node: FdtNode<'b, 'a>, +pub struct Root<'a, Granularity: ParserForSize = u32, Mode: PanicMode = Panic> { + pub(crate) node: Node<'a, Granularity, Mode>, } -impl<'b, 'a: 'b> Root<'b, 'a> { +impl<'a, Granularity: ParserForSize, Mode: PanicMode> Root<'a, Granularity, Mode> { /// Root node cell sizes - pub fn cell_sizes(self) -> CellSizes { - self.node.cell_sizes() + pub fn cell_sizes(self) -> ::Output { + ::transpose(self.node.property::()).unwrap() } /// `model` property - pub fn model(self) -> &'a str { - self.node - .properties() - .find(|p| p.name == "model") - .and_then(|p| core::str::from_utf8(p.value).map(|s| s.trim_end_matches('\0')).ok()) - .unwrap() - } + // pub fn model(self) -> &'a str { + // self.node + // .properties() + // .find(|p| p.name == "model") + // .and_then(|p| core::str::from_utf8(p.value).map(|s| s.trim_end_matches('\0')).ok()) + // .unwrap() + // } /// `compatible` property - pub fn compatible(self) -> Compatible<'a> { - self.node.compatible().unwrap() - } + // pub fn compatible(self) -> Compatible<'a> { + // self.node.compatible().unwrap() + // } /// Returns an iterator over all of the available properties - pub fn properties(self) -> impl Iterator> + 'b { - self.node.properties() - } + // pub fn properties(self) -> impl Iterator> + 'a { + // self.node.properties() + // } /// Attempts to find the a property by its name - pub fn property(self, name: &str) -> Option> { - self.node.properties().find(|p| p.name == name) - } -} + // pub fn property(self, name: &str) -> Option> { + // self.node.properties().find(|p| p.name == name) + // } + + pub fn find_node<'b, N: IntoSearchableNodeName<'b>>( + self, + name: N, + ) -> ::Output>> { + let name = name.into_searchable_node_name(); + + if let SearchableNodeName::Base("/") = name { + return ::to_output(Ok(Some(self.node))); + } -/// Represents the `/aliases` node with specific helper methods -#[derive(Debug, Clone, Copy)] -pub struct Aliases<'b, 'a: 'b> { - pub(crate) header: &'b Fdt<'a, crate::UnalignedParser<'a>>, - pub(crate) node: FdtNode<'b, 'a>, -} + let (name_str, unit_address) = match name { + SearchableNodeName::Base(s) => (s, None), + SearchableNodeName::WithUnitAddress(NodeName { name, unit_address }) => { + (name, unit_address) + } + }; -impl<'b, 'a: 'b> Aliases<'b, 'a> { - /// Attempt to resolve an alias to a node name - pub fn resolve(self, alias: &str) -> Option<&'a str> { - self.node - .properties() - .find(|p| p.name == alias) - .and_then(|p| core::str::from_utf8(p.value).map(|s| s.trim_end_matches('\0')).ok()) - } + let mut children_iter = match ::to_result(self.node.children()) { + Ok(iter) => iter, + Err(e) => return ::to_output(Err(e)), + }; - /// Attempt to find the node specified by the given alias - pub fn resolve_node(self, alias: &str) -> Option> { - self.resolve(alias).and_then(|name| self.header.find_node(name)) - } + let mut name_components = name_str.trim_start_matches('/').split('/').peekable(); + loop { + if name_components.peek().is_none() { + return ::to_output(Ok(None)); + } - /// Returns an iterator over all of the available aliases - pub fn all(self) -> impl Iterator + 'b { - self.node.properties().filter_map(|p| { - Some((p.name, core::str::from_utf8(p.value).map(|s| s.trim_end_matches('\0')).ok()?)) - }) + let component = name_components.next().unwrap(); + let is_last_component = name_components.peek().is_none(); + let look_for_name = match is_last_component { + true => NodeName { name: component, unit_address }, + false => NodeName { name: component, unit_address: None }, + }; + + match ::to_result(children_iter.find(look_for_name)) { + Ok(Some(node)) => match is_last_component { + true => return ::to_output(Ok(Some(node))), + false => { + children_iter = match ::to_result(node.children()) { + Ok(iter) => iter, + Err(e) => return ::to_output(Err(e)), + } + } + }, + Ok(None) => return ::to_output(Ok(None)), + Err(e) => return ::to_output(Err(e)), + } + } } -} -/// Represents a `/cpus/cpu*` node with specific helper methods -#[derive(Debug, Clone, Copy)] -pub struct Cpu<'b, 'a: 'b> { - pub(crate) parent: FdtNode<'b, 'a>, - pub(crate) node: FdtNode<'b, 'a>, -} + pub fn all_nodes(self) -> ::Output> { + let mut parser = <::Parser<'a> as Parser<'a>>::new( + self.node.this.as_slice(), + self.node.strings.0, + ); + let res = tryblock!({ + parser.advance_cstr().unwrap(); -impl<'b, 'a: 'b> Cpu<'b, 'a> { - /// Return the IDs for the given CPU - pub fn ids(self) -> CpuIds<'a> { - let address_cells = self.node.parent_cell_sizes().address_cells; - - CpuIds { - reg: self - .node - .properties() - .find(|p| p.name == "reg") - .expect("reg is a required property of cpu nodes"), - address_cells, - } - } + while parser.peek_token().unwrap() == BigEndianToken::PROP { + parser.parse_raw_property().unwrap(); + } - /// `clock-frequency` property - pub fn clock_frequency(self) -> usize { - self.node - .properties() - .find(|p| p.name == "clock-frequency") - .or_else(|| self.parent.property("clock-frequency")) - .map(|p| match p.value.len() { - 4 => BigEndianU32::from_bytes(p.value).unwrap().to_ne() as usize, - 8 => BigEndianU64::from_bytes(p.value).unwrap().to_ne() as usize, - _ => unreachable!(), - }) - .expect("clock-frequency is a required property of cpu nodes") - } + Ok(()) + }); - /// `timebase-frequency` property - pub fn timebase_frequency(self) -> usize { - self.node - .properties() - .find(|p| p.name == "timebase-frequency") - .or_else(|| self.parent.property("timebase-frequency")) - .map(|p| match p.value.len() { - 4 => BigEndianU32::from_bytes(p.value).unwrap().to_ne() as usize, - 8 => BigEndianU64::from_bytes(p.value).unwrap().to_ne() as usize, - _ => unreachable!(), - }) - .expect("timebase-frequency is a required property of cpu nodes") - } + if let Err(e) = res { + return ::to_output(Err(e)); + } - /// Returns an iterator over all of the properties for the CPU node - pub fn properties(self) -> impl Iterator> + 'b { - self.node.properties() + ::to_output(Ok(AllNodesIterator { + parser, + parents: [&[]; 16], + parent_index: 0, + _mode: core::marker::PhantomData, + })) } +} - /// Attempts to find the a property by its name - pub fn property(self, name: &str) -> Option> { - self.node.properties().find(|p| p.name == name) +impl<'a, Granularity: ParserForSize, Mode: PanicMode> core::fmt::Debug + for Root<'a, Granularity, Mode> +{ + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("Root").finish_non_exhaustive() } } -/// Represents the value of the `reg` property of a `/cpus/cpu*` node which may -/// contain more than one CPU or thread ID -#[derive(Debug, Clone, Copy)] -pub struct CpuIds<'a> { - pub(crate) reg: NodeProperty<'a>, - pub(crate) address_cells: usize, +pub struct AllNodesIterator<'a, Granularity: ParserForSize, Mode: PanicMode> { + parser: ::Parser<'a>, + parents: [&'a [Granularity]; 16], + parent_index: usize, + _mode: core::marker::PhantomData<*mut Mode>, } -impl<'a> CpuIds<'a> { - /// The first listed CPU ID, which will always exist - pub fn first(self) -> usize { - match self.address_cells { - 1 => BigEndianU32::from_bytes(self.reg.value).unwrap().to_ne() as usize, - 2 => BigEndianU64::from_bytes(self.reg.value).unwrap().to_ne() as usize, - n => panic!("address-cells of size {} is currently not supported", n), +impl<'a, Granularity: ParserForSize, Mode: PanicMode> Iterator + for AllNodesIterator<'a, Granularity, Mode> +{ + type Item = ::Output>; + + #[track_caller] + fn next(&mut self) -> Option { + while let Ok(BigEndianToken::END_NODE) = self.parser.peek_token() { + let _ = self.parser.advance_token(); } - } - /// Returns an iterator over all of the listed CPU IDs - pub fn all(self) -> impl Iterator + 'a { - let mut vals = FdtData::new(self.reg.value); - core::iter::from_fn(move || match vals.remaining() { - [] => None, - _ => Some(match self.address_cells { - 1 => vals.u32()?.to_ne() as usize, - 2 => vals.u64()?.to_ne() as usize, - n => panic!("address-cells of size {} is currently not supported", n), - }), - }) + match self.parser.advance_token() { + Ok(BigEndianToken::BEGIN_NODE) => {} + Ok(BigEndianToken::END_NODE) => {} + Ok(BigEndianToken::END) | Err(ParseError::UnexpectedEndOfData) => return None, + Ok(_) => return Some(::to_output(Err(ParseError::UnexpectedToken))), + Err(e) => return Some(::to_output(Err(e))), + } + + let starting_data = self.parser.data(); + + match self.parents.get_mut(self.parent_index) { + Some(idx) => *idx = starting_data, + // FIXME: what makes sense for this to return? + None => return None, + } + self.parent_index += 1; + + let node = Some(::to_output(Ok(Node { + this: RawNode::new(starting_data), + parent: self + .parent_index + .checked_sub(1) + .and_then(|idx| self.parents.get(idx)) + .map(|parent| RawNode::new(*parent)), + strings: self.parser.strings(), + _mode: core::marker::PhantomData, + }))); + + let res = tryblock!({ + self.parser.advance_cstr()?; + + while self.parser.peek_token()? == BigEndianToken::PROP { + self.parser.parse_raw_property()?; + } + + Ok(()) + }); + + if let Err(e) = res { + return Some(::to_output(Err(e))); + } + + node } } +// /// Represents the `/aliases` node with specific helper methods +// #[derive(Debug, Clone, Copy)] +// pub struct Aliases<'b, 'a: 'b> { +// pub(crate) header: &'b Fdt<'a, crate::UnalignedParser<'a>>, +// pub(crate) node: FdtNode<'b, 'a>, +// } + +// impl<'b, 'a: 'b> Aliases<'b, 'a> { +// /// Attempt to resolve an alias to a node name +// pub fn resolve(self, alias: &str) -> Option<&'a str> { +// self.node +// .properties() +// .find(|p| p.name == alias) +// .and_then(|p| core::str::from_utf8(p.value).map(|s| s.trim_end_matches('\0')).ok()) +// } + +// /// Attempt to find the node specified by the given alias +// pub fn resolve_node(self, alias: &str) -> Option> { +// self.resolve(alias).and_then(|name| self.header.find_node(name)) +// } + +// /// Returns an iterator over all of the available aliases +// pub fn all(self) -> impl Iterator + 'b { +// self.node.properties().filter_map(|p| { +// Some((p.name, core::str::from_utf8(p.value).map(|s| s.trim_end_matches('\0')).ok()?)) +// }) +// } +// } + +// /// Represents a `/cpus/cpu*` node with specific helper methods +// #[derive(Debug, Clone, Copy)] +// pub struct Cpu<'b, 'a: 'b> { +// pub(crate) parent: FdtNode<'b, 'a>, +// pub(crate) node: FdtNode<'b, 'a>, +// } + +// impl<'b, 'a: 'b> Cpu<'b, 'a> { +// /// Return the IDs for the given CPU +// pub fn ids(self) -> CpuIds<'a> { +// let address_cells = self.node.parent_cell_sizes().address_cells; + +// CpuIds { +// reg: self +// .node +// .properties() +// .find(|p| p.name == "reg") +// .expect("reg is a required property of cpu nodes"), +// address_cells, +// } +// } + +// /// `clock-frequency` property +// pub fn clock_frequency(self) -> usize { +// self.node +// .properties() +// .find(|p| p.name == "clock-frequency") +// .or_else(|| self.parent.property("clock-frequency")) +// .map(|p| match p.value.len() { +// 4 => BigEndianU32::from_bytes(p.value).unwrap().to_ne() as usize, +// 8 => BigEndianU64::from_bytes(p.value).unwrap().to_ne() as usize, +// _ => unreachable!(), +// }) +// .expect("clock-frequency is a required property of cpu nodes") +// } + +// /// `timebase-frequency` property +// pub fn timebase_frequency(self) -> usize { +// self.node +// .properties() +// .find(|p| p.name == "timebase-frequency") +// .or_else(|| self.parent.property("timebase-frequency")) +// .map(|p| match p.value.len() { +// 4 => BigEndianU32::from_bytes(p.value).unwrap().to_ne() as usize, +// 8 => BigEndianU64::from_bytes(p.value).unwrap().to_ne() as usize, +// _ => unreachable!(), +// }) +// .expect("timebase-frequency is a required property of cpu nodes") +// } + +// /// Returns an iterator over all of the properties for the CPU node +// pub fn properties(self) -> impl Iterator> + 'b { +// self.node.properties() +// } + +// /// Attempts to find the a property by its name +// pub fn property(self, name: &str) -> Option> { +// self.node.properties().find(|p| p.name == name) +// } +// } + +// /// Represents the value of the `reg` property of a `/cpus/cpu*` node which may +// /// contain more than one CPU or thread ID +// #[derive(Debug, Clone, Copy)] +// pub struct CpuIds<'a> { +// pub(crate) reg: NodeProperty<'a>, +// pub(crate) address_cells: usize, +// } + +// impl<'a> CpuIds<'a> { +// /// The first listed CPU ID, which will always exist +// pub fn first(self) -> usize { +// match self.address_cells { +// 1 => BigEndianU32::from_bytes(self.reg.value).unwrap().to_ne() as usize, +// 2 => BigEndianU64::from_bytes(self.reg.value).unwrap().to_ne() as usize, +// n => panic!("address-cells of size {} is currently not supported", n), +// } +// } + +// /// Returns an iterator over all of the listed CPU IDs +// pub fn all(self) -> impl Iterator + 'a { +// let mut vals = FdtData::new(self.reg.value); +// core::iter::from_fn(move || match vals.remaining() { +// [] => None, +// _ => Some(match self.address_cells { +// 1 => vals.u32()?.to_ne() as usize, +// 2 => vals.u64()?.to_ne() as usize, +// n => panic!("address-cells of size {} is currently not supported", n), +// }), +// }) +// } +// } + /// Represents the `compatible` property of a node #[derive(Clone, Copy)] pub struct Compatible<'a> { @@ -285,37 +463,37 @@ impl<'a> Compatible<'a> { } /// Represents the `/memory` node with specific helper methods -#[derive(Debug, Clone, Copy)] -pub struct Memory<'b, 'a: 'b> { - pub(crate) node: FdtNode<'b, 'a>, -} - -impl<'a> Memory<'_, 'a> { - /// Returns an iterator over all of the available memory regions - pub fn regions(&self) -> impl Iterator + 'a { - self.node.reg().unwrap() - } - - /// Returns the initial mapped area, if it exists - pub fn initial_mapped_area(&self) -> Option { - let mut mapped_area = None; - - if let Some(init_mapped_area) = self.node.property("initial_mapped_area") { - let mut stream = FdtData::new(init_mapped_area.value); - let effective_address = stream.u64().expect("effective address"); - let physical_address = stream.u64().expect("physical address"); - let size = stream.u32().expect("size"); - - mapped_area = Some(MappedArea { - effective_address: effective_address.to_ne() as usize, - physical_address: physical_address.to_ne() as usize, - size: size.to_ne() as usize, - }); - } - - mapped_area - } -} +// #[derive(Debug, Clone, Copy)] +// pub struct Memory<'b, 'a: 'b> { +// pub(crate) node: FdtNode<'b, 'a>, +// } + +// impl<'a> Memory<'_, 'a> { +// /// Returns an iterator over all of the available memory regions +// pub fn regions(&self) -> impl Iterator + 'a { +// self.node.reg().unwrap() +// } + +// /// Returns the initial mapped area, if it exists +// pub fn initial_mapped_area(&self) -> Option { +// let mut mapped_area = None; + +// if let Some(init_mapped_area) = self.node.property("initial_mapped_area") { +// let mut stream = FdtData::new(init_mapped_area.value); +// let effective_address = stream.u64().expect("effective address"); +// let physical_address = stream.u64().expect("physical address"); +// let size = stream.u32().expect("size"); + +// mapped_area = Some(MappedArea { +// effective_address: effective_address.to_ne() as usize, +// physical_address: physical_address.to_ne() as usize, +// size: size.to_ne() as usize, +// }); +// } + +// mapped_area +// } +// } /// An area described by the `initial-mapped-area` property of the `/memory` /// node diff --git a/src/tests.rs b/src/tests.rs index 1fb32b2..2d6183a 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -4,7 +4,8 @@ extern crate std; -use crate::{node::RawReg, *}; +// use crate::{node::RawReg, *}; +use crate::*; struct AlignArrayUp([u8; N]); @@ -18,6 +19,7 @@ impl AlignArrayUp { while i < N { copy[i] = self.0[i]; + i += 1; } return copy; @@ -48,272 +50,278 @@ fn returns_fdt() { } #[test] -fn finds_root_node() { +fn root() { let fdt = Fdt::new(TEST.as_slice()).unwrap(); - assert!(fdt.find_node("/").is_some(), "couldn't find root node"); + std::println!("{:?}", fdt.root()); } #[test] -fn finds_root_node_properties() { +fn all_nodes() { let fdt = Fdt::new(TEST.as_slice()).unwrap(); - let prop = fdt - .find_node("/") - .unwrap() - .properties() - .any(|p| p.name == "compatible" && p.value == b"riscv-virtio\0"); - - assert!(prop); -} - -#[test] -fn finds_child_of_root_node() { - let fdt = Fdt::new(TEST.as_slice()).unwrap(); - assert!(fdt.find_node("/cpus").is_some(), "couldn't find cpus node"); -} - -#[test] -fn correct_flash_regions() { - let fdt = Fdt::new(TEST.as_slice()).unwrap(); - let regions = fdt.find_node("/soc/flash").unwrap().reg().unwrap().collect::>(); - - assert_eq!( - regions, - &[ - MemoryRegion { starting_address: 0x20000000 as *const u8, size: Some(0x2000000) }, - MemoryRegion { starting_address: 0x22000000 as *const u8, size: Some(0x2000000) } - ] + panic!( + "{}", + fdt.root() + .all_nodes() + .map(|n| std::format!("{}", n.name())) + .collect::>() + .join("\n") ); } -#[test] -fn parses_populated_ranges() { - let fdt = Fdt::new(TEST.as_slice()).unwrap(); - let ranges = fdt.find_node("/soc/pci").unwrap().ranges().unwrap().collect::>(); - - assert_eq!( - ranges, - &[ - MemoryRange { - child_bus_address: 0x0000_0000_0000_0000, - child_bus_address_hi: 0x0100_0000, - parent_bus_address: 0x3000000, - size: 0x10000, - }, - MemoryRange { - child_bus_address: 0x40000000, - child_bus_address_hi: 0x2000000, - parent_bus_address: 0x4000_0000, - size: 0x4000_0000, - } - ] - ); -} - -#[test] -fn parses_empty_ranges() { - let fdt = Fdt::new(TEST.as_slice()).unwrap(); - let ranges = fdt.find_node("/soc").unwrap().ranges().unwrap().collect::>(); - - assert_eq!(ranges, &[]); -} +// #[test] +// fn finds_root_node() { +// let fdt = Fdt::new(TEST.as_slice()).unwrap(); +// assert!(fdt.find_node("/").is_some(), "couldn't find root node"); +// } -#[test] -fn finds_with_addr() { - let fdt = Fdt::new(TEST.as_slice()).unwrap(); - assert_eq!(fdt.find_node("/soc/virtio_mmio@10004000").unwrap().name, "virtio_mmio@10004000"); -} +// #[test] +// fn finds_root_node_properties() { +// let fdt = Fdt::new(TEST.as_slice()).unwrap(); +// let prop = fdt +// .find_node("/") +// .unwrap() +// .properties() +// .any(|p| p.name == "compatible" && p.value == b"riscv-virtio\0"); -#[test] -fn compatibles() { - let fdt = Fdt::new(TEST.as_slice()).unwrap(); - let res = fdt - .find_node("/soc/test") - .unwrap() - .compatible() - .unwrap() - .all() - .all(|s| ["sifive,test1", "sifive,test0", "syscon"].contains(&s)); - - assert!(res); -} +// assert!(prop); +// } #[test] -fn parent_cell_sizes() { - let fdt = Fdt::new(TEST.as_slice()).unwrap(); - let regions = fdt.find_node("/memory").unwrap().reg().unwrap().collect::>(); - - assert_eq!( - regions, - &[MemoryRegion { starting_address: 0x80000000 as *const u8, size: Some(0x20000000) }] - ); -} - -#[test] -fn no_properties() { - let fdt = Fdt::new(TEST.as_slice()).unwrap(); - let regions = fdt.find_node("/emptyproptest").unwrap(); - assert_eq!(regions.properties().count(), 0); -} - -#[test] -fn finds_all_nodes() { - let fdt = Fdt::new(TEST.as_slice()).unwrap(); - - let mut all_nodes: std::vec::Vec<_> = fdt.all_nodes().map(|n| n.name).collect(); - all_nodes.sort_unstable(); - - assert_eq!( - all_nodes, - &[ - "/", - "chosen", - "clint@2000000", - "cluster0", - "core0", - "cpu-map", - "cpu@0", - "cpus", - "emptyproptest", - "flash@20000000", - "interrupt-controller", - "memory@80000000", - "pci@30000000", - "plic@c000000", - "poweroff", - "reboot", - "rtc@101000", - "soc", - "test@100000", - "uart@10000000", - "virtio_mmio@10001000", - "virtio_mmio@10002000", - "virtio_mmio@10003000", - "virtio_mmio@10004000", - "virtio_mmio@10005000", - "virtio_mmio@10006000", - "virtio_mmio@10007000", - "virtio_mmio@10008000" - ] - ) -} - -#[test] -fn required_nodes() { - let fdt = Fdt::new(TEST.as_slice()).unwrap(); - fdt.cpus().next().unwrap(); - fdt.memory(); - fdt.chosen(); -} - -#[test] -fn doesnt_exist() { - let fdt = Fdt::new(TEST.as_slice()).unwrap(); - assert!(fdt.find_node("/this/doesnt/exist").is_none()); -} - -#[test] -fn raw_reg() { - let fdt = Fdt::new(TEST.as_slice()).unwrap(); - let regions = - fdt.find_node("/soc/flash").unwrap().raw_reg().unwrap().collect::>(); - - assert_eq!( - regions, - &[ - RawReg { address: &0x20000000u64.to_be_bytes(), size: &0x2000000u64.to_be_bytes() }, - RawReg { address: &0x22000000u64.to_be_bytes(), size: &0x2000000u64.to_be_bytes() } - ] - ); -} - -#[test] -fn issue_3() { - let fdt = Fdt::new(ISSUE_3.as_slice()).unwrap(); - fdt.find_all_nodes("uart").for_each(|n| std::println!("{:?}", n)); -} - -#[test] -fn issue_4() { - let fdt = Fdt::new(ISSUE_3.as_slice()).unwrap(); - fdt.all_nodes().for_each(|n| std::println!("{:?}", n)); -} - -#[test] -fn cpus() { - let fdt = Fdt::new(TEST.as_slice()).unwrap(); - for cpu in fdt.cpus() { - cpu.ids().all().for_each(|n| std::println!("{:?}", n)); - } -} - -#[test] -fn invalid_node() { - let fdt = Fdt::new(TEST.as_slice()).unwrap(); - assert!(fdt.find_node("this/is/an invalid node///////////").is_none()); -} - -#[test] -fn aliases() { - let fdt = Fdt::new(SIFIVE.as_slice()).unwrap(); - let aliases = fdt.aliases().unwrap(); - for (_, node_path) in aliases.all() { - assert!(fdt.find_node(node_path).is_some(), "path: {:?}", node_path); - } -} - -#[test] -fn stdout() { - let fdt = Fdt::new(TEST.as_slice()).unwrap(); - let stdout = fdt.chosen().stdout().unwrap(); - assert!(stdout.node().name == "uart@10000000"); - assert!(stdout.params() == Some("115200")); -} - -#[test] -fn stdin() { - let fdt = Fdt::new(TEST.as_slice()).unwrap(); - let stdin = fdt.chosen().stdin().unwrap(); - assert!(stdin.node().name == "uart@10000000"); - assert!(stdin.params().is_none()); -} - -#[test] -fn node_property_str_value() { - let fdt = Fdt::new(TEST.as_slice()).unwrap(); - let cpu0 = fdt.find_node("/cpus/cpu@0").unwrap(); - assert_eq!(cpu0.property("riscv,isa").unwrap().as_str().unwrap(), "rv64imafdcsu"); -} - -#[test] -fn model_value() { - let fdt = Fdt::new(TEST.as_slice()).unwrap(); - assert_eq!(fdt.root().model(), "riscv-virtio,qemu"); -} - -#[test] -fn memory_node() { - let fdt = Fdt::new(TEST.as_slice()).unwrap(); - assert_eq!(fdt.memory().regions().count(), 1); -} - -#[test] -fn interrupt_cells() { +fn finds_child_of_root_node() { let fdt = Fdt::new(TEST.as_slice()).unwrap(); - let uart = fdt.find_node("/soc/uart").unwrap(); - std::println!("{:?}", uart.parent_interrupt_cells()); - assert_eq!(uart.interrupts().unwrap().collect::>(), std::vec![0xA]); + let root = fdt.root(); + assert!(root.find_node("/cpus").is_some(), "couldn't find cpus node"); } -#[test] -fn property_str_list() { - let fdt = Fdt::new(TEST).unwrap(); - let test = fdt.find_node("/soc/test").unwrap(); - let expected = ["sifive,test1", "sifive,test0", "syscon"]; - let compat = test.property("compatible").unwrap(); - - assert_eq!(compat.iter_str().count(), expected.len()); - - test.property("compatible").unwrap().iter_str().zip(expected).for_each(|(prop, exp)| { - assert_eq!(prop, exp); - }); -} +// #[test] +// fn correct_flash_regions() { +// let fdt = Fdt::new(TEST.as_slice()).unwrap(); +// let regions = fdt.find_node("/soc/flash").unwrap().reg().unwrap().collect::>(); + +// assert_eq!( +// regions, +// &[ +// MemoryRegion { starting_address: 0x20000000 as *const u8, size: Some(0x2000000) }, +// MemoryRegion { starting_address: 0x22000000 as *const u8, size: Some(0x2000000) } +// ] +// ); +// } + +// #[test] +// fn parses_populated_ranges() { +// let fdt = Fdt::new(TEST.as_slice()).unwrap(); +// let ranges = fdt.find_node("/soc/pci").unwrap().ranges().unwrap().collect::>(); + +// assert_eq!( +// ranges, +// &[ +// MemoryRange { +// child_bus_address: 0x0000_0000_0000_0000, +// child_bus_address_hi: 0x0100_0000, +// parent_bus_address: 0x3000000, +// size: 0x10000, +// }, +// MemoryRange { +// child_bus_address: 0x40000000, +// child_bus_address_hi: 0x2000000, +// parent_bus_address: 0x4000_0000, +// size: 0x4000_0000, +// } +// ] +// ); +// } + +// #[test] +// fn parses_empty_ranges() { +// let fdt = Fdt::new(TEST.as_slice()).unwrap(); +// let ranges = fdt.find_node("/soc").unwrap().ranges().unwrap().collect::>(); + +// assert_eq!(ranges, &[]); +// } + +// #[test] +// fn finds_with_addr() { +// let fdt = Fdt::new(TEST.as_slice()).unwrap(); +// assert_eq!(fdt.find_node("/soc/virtio_mmio@10004000").unwrap().name, "virtio_mmio@10004000"); +// } + +// #[test] +// fn compatibles() { +// let fdt = Fdt::new(TEST.as_slice()).unwrap(); +// let res = fdt +// .find_node("/soc/test") +// .unwrap() +// .compatible() +// .unwrap() +// .all() +// .all(|s| ["sifive,test1", "sifive,test0", "syscon"].contains(&s)); + +// assert!(res); +// } + +// #[test] +// fn parent_cell_sizes() { +// let fdt = Fdt::new(TEST.as_slice()).unwrap(); +// let regions = fdt.find_node("/memory").unwrap().reg().unwrap().collect::>(); + +// assert_eq!( +// regions, +// &[MemoryRegion { starting_address: 0x80000000 as *const u8, size: Some(0x20000000) }] +// ); +// } + +// #[test] +// fn no_properties() { +// let fdt = Fdt::new(TEST.as_slice()).unwrap(); +// let regions = fdt.find_node("/emptyproptest").unwrap(); +// assert_eq!(regions.properties().count(), 0); +// } + +// #[test] +// fn finds_all_nodes() { +// let fdt = Fdt::new(TEST.as_slice()).unwrap(); + +// let mut all_nodes: std::vec::Vec<_> = fdt.all_nodes().map(|n| n.name).collect(); +// all_nodes.sort_unstable(); + +// assert_eq!( +// all_nodes, +// &[ +// "/", +// "chosen", +// "clint@2000000", +// "cluster0", +// "core0", +// "cpu-map", +// "cpu@0", +// "cpus", +// "emptyproptest", +// "flash@20000000", +// "interrupt-controller", +// "memory@80000000", +// "pci@30000000", +// "plic@c000000", +// "poweroff", +// "reboot", +// "rtc@101000", +// "soc", +// "test@100000", +// "uart@10000000", +// "virtio_mmio@10001000", +// "virtio_mmio@10002000", +// "virtio_mmio@10003000", +// "virtio_mmio@10004000", +// "virtio_mmio@10005000", +// "virtio_mmio@10006000", +// "virtio_mmio@10007000", +// "virtio_mmio@10008000" +// ] +// ) +// } + +// #[test] +// fn required_nodes() { +// let fdt = Fdt::new(TEST.as_slice()).unwrap(); +// fdt.cpus().next().unwrap(); +// fdt.memory(); +// fdt.chosen(); +// } + +// #[test] +// fn doesnt_exist() { +// let fdt = Fdt::new(TEST.as_slice()).unwrap(); +// assert!(fdt.find_node("/this/doesnt/exist").is_none()); +// } + +// #[test] +// fn raw_reg() { +// let fdt = Fdt::new(TEST.as_slice()).unwrap(); +// let regions = +// fdt.find_node("/soc/flash").unwrap().raw_reg().unwrap().collect::>(); + +// assert_eq!( +// regions, +// &[ +// RawReg { address: &0x20000000u64.to_be_bytes(), size: &0x2000000u64.to_be_bytes() }, +// RawReg { address: &0x22000000u64.to_be_bytes(), size: &0x2000000u64.to_be_bytes() } +// ] +// ); +// } + +// #[test] +// fn issue_3() { +// let fdt = Fdt::new(ISSUE_3.as_slice()).unwrap(); +// fdt.find_all_nodes("uart").for_each(|n| std::println!("{:?}", n)); +// } + +// #[test] +// fn issue_4() { +// let fdt = Fdt::new(ISSUE_3.as_slice()).unwrap(); +// fdt.all_nodes().for_each(|n| std::println!("{:?}", n)); +// } + +// #[test] +// fn cpus() { +// let fdt = Fdt::new(TEST.as_slice()).unwrap(); +// for cpu in fdt.cpus() { +// cpu.ids().all().for_each(|n| std::println!("{:?}", n)); +// } +// } + +// #[test] +// fn invalid_node() { +// let fdt = Fdt::new(TEST.as_slice()).unwrap(); +// assert!(fdt.find_node("this/is/an invalid node///////////").is_none()); +// } + +// #[test] +// fn aliases() { +// let fdt = Fdt::new(SIFIVE.as_slice()).unwrap(); +// let aliases = fdt.aliases().unwrap(); +// for (_, node_path) in aliases.all() { +// assert!(fdt.find_node(node_path).is_some(), "path: {:?}", node_path); +// } +// } + +// #[test] +// fn stdout() { +// let fdt = Fdt::new(TEST.as_slice()).unwrap(); +// let stdout = fdt.chosen().stdout().unwrap(); +// assert!(stdout.node().name == "uart@10000000"); +// assert!(stdout.params() == Some("115200")); +// } + +// #[test] +// fn stdin() { +// let fdt = Fdt::new(TEST.as_slice()).unwrap(); +// let stdin = fdt.chosen().stdin().unwrap(); +// assert!(stdin.node().name == "uart@10000000"); +// assert!(stdin.params().is_none()); +// } + +// #[test] +// fn node_property_str_value() { +// let fdt = Fdt::new(TEST.as_slice()).unwrap(); +// let cpu0 = fdt.find_node("/cpus/cpu@0").unwrap(); +// assert_eq!(cpu0.property("riscv,isa").unwrap().as_str().unwrap(), "rv64imafdcsu"); +// } + +// #[test] +// fn model_value() { +// let fdt = Fdt::new(TEST.as_slice()).unwrap(); +// assert_eq!(fdt.root().model(), "riscv-virtio,qemu"); +// } + +// #[test] +// fn memory_node() { +// let fdt = Fdt::new(TEST.as_slice()).unwrap(); +// assert_eq!(fdt.memory().regions().count(), 1); +// } + +// #[test] +// fn interrupt_cells() { +// let fdt = Fdt::new(TEST.as_slice()).unwrap(); +// let uart = fdt.find_node("/soc/uart").unwrap(); +// std::println!("{:?}", uart.parent_interrupt_cells()); +// assert_eq!(uart.interrupts().unwrap().collect::>(), std::vec![0xA]); +// } From f80208ad5de67f75e8cd0f2e58ca2eb448da2032 Mon Sep 17 00:00:00 2001 From: repnop Date: Mon, 24 Jun 2024 16:05:25 -0400 Subject: [PATCH 03/38] dafgdfgadfgdfgadfgdfgagag AAAAAAAAAAAAAAAA --- src/lib.rs | 71 ++++++++---- src/nodes.rs | 230 ++++++++++++++++++--------------------- src/parsing.rs | 185 ++++++++++++++++++++++--------- src/parsing/aligned.rs | 19 ++-- src/parsing/unaligned.rs | 16 +-- src/properties.rs | 28 ++--- src/standard_nodes.rs | 183 ++++++++++++++++--------------- src/tests.rs | 76 +++++++++---- 8 files changed, 468 insertions(+), 340 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index d2bcd3f..1e19f57 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -65,8 +65,8 @@ pub mod standard_nodes; mod util; use parsing::{ - aligned::AlignedParser, unaligned::UnalignedParser, NoPanic, Panic, PanicMode, ParseError, - Parser, StringsBlock, + aligned::AlignedParser, unaligned::UnalignedParser, NoPanic, Panic, ParseError, Parser, + ParserWithMode, StringsBlock, }; use standard_nodes::Root; // use standard_nodes::{Aliases, Chosen, Cpu, Memory, MemoryRange, MemoryRegion, Root}; @@ -84,6 +84,8 @@ pub enum FdtError { BadPtr, /// An error was encountered during parsing ParseError(ParseError), + MissingRequiredNode(&'static str), + MissingRequiredProperty(&'static str), } impl From for FdtError { @@ -98,6 +100,12 @@ impl core::fmt::Display for FdtError { FdtError::BadMagic => write!(f, "bad FDT magic value"), FdtError::BadPtr => write!(f, "an invalid pointer was passed"), FdtError::ParseError(e) => core::fmt::Display::fmt(e, f), + FdtError::MissingRequiredNode(name) => { + write!(f, "FDT is missing a required node `{}`", name) + } + FdtError::MissingRequiredProperty(name) => { + write!(f, "FDT node is missing a required property `{}`", name) + } } } } @@ -108,15 +116,14 @@ impl core::fmt::Display for FdtError { /// print any useful information, if you would like a best-effort tree print /// which looks similar to `dtc`'s output, enable the `pretty-printing` feature #[derive(Clone, Copy)] -pub struct Fdt<'a, P: Parser<'a>, Mode: PanicMode = Panic> { +pub struct Fdt<'a, P: ParserWithMode<'a>> { parser: P, strings: StringsBlock<'a>, structs: &'a [P::Granularity], header: FdtHeader, - _mode: core::marker::PhantomData<*mut Mode>, } -impl<'a, P: Parser<'a>> core::fmt::Debug for Fdt<'a, P> { +impl<'a, P: ParserWithMode<'a>> core::fmt::Debug for Fdt<'a, P> { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_struct("Fdt").finish_non_exhaustive() } @@ -160,7 +167,7 @@ impl FdtHeader { } } -impl<'a> Fdt<'a, UnalignedParser<'a>, Panic> { +impl<'a> Fdt<'a, (UnalignedParser<'a>, Panic)> { /// Construct a new `Fdt` from a byte buffer pub fn new_unaligned(data: &'a [u8]) -> Result { let mut parser = UnalignedParser::new(data, &[]); @@ -175,7 +182,12 @@ impl<'a> Fdt<'a, UnalignedParser<'a>, Panic> { return Err(FdtError::ParseError(ParseError::UnexpectedEndOfData)); } - Ok(Self { header, parser, strings, structs, _mode: core::marker::PhantomData }) + Ok(Self { + header, + parser: (UnalignedParser::new(data, strings.0), Panic), + strings, + structs, + }) } /// # Safety @@ -195,7 +207,7 @@ impl<'a> Fdt<'a, UnalignedParser<'a>, Panic> { } } -impl<'a> Fdt<'a, AlignedParser<'a>, Panic> { +impl<'a> Fdt<'a, (AlignedParser<'a>, Panic)> { /// Construct a new `Fdt` from a `u32`-aligned buffer pub fn new(data: &'a [u32]) -> Result { let mut parser = AlignedParser::new(data, &[]); @@ -223,10 +235,9 @@ impl<'a> Fdt<'a, AlignedParser<'a>, Panic> { Ok(Self { header, - parser: AlignedParser::new(structs, strings.0), + parser: (AlignedParser::new(structs, strings.0), Panic), strings, structs, - _mode: core::marker::PhantomData, }) } @@ -247,11 +258,16 @@ impl<'a> Fdt<'a, AlignedParser<'a>, Panic> { } } -impl<'a> Fdt<'a, UnalignedParser<'a>, NoPanic> { +impl<'a> Fdt<'a, (UnalignedParser<'a>, NoPanic)> { /// Construct a new `Fdt` from a byte buffer pub fn new_unaligned_fallible(data: &'a [u8]) -> Result { let Fdt { parser, strings, structs, header, .. } = Fdt::new_unaligned(data)?; - Ok(Self { parser, strings, structs, header, _mode: core::marker::PhantomData }) + Ok(Self { + parser: (UnalignedParser::new(parser.data(), strings.0), NoPanic), + strings, + structs, + header, + }) } /// # Safety @@ -259,15 +275,25 @@ impl<'a> Fdt<'a, UnalignedParser<'a>, NoPanic> { /// is invalid this can result in undefined behavior. pub unsafe fn from_ptr_unaligned_fallible(ptr: *const u8) -> Result { let Fdt { parser, strings, structs, header, .. } = Fdt::from_ptr_unaligned(ptr)?; - Ok(Self { parser, strings, structs, header, _mode: core::marker::PhantomData }) + Ok(Self { + parser: (UnalignedParser::new(parser.data(), strings.0), NoPanic), + strings, + structs, + header, + }) } } -impl<'a> Fdt<'a, AlignedParser<'a>, NoPanic> { +impl<'a> Fdt<'a, (AlignedParser<'a>, NoPanic)> { /// Construct a new `Fdt` from a `u32`-aligned buffer which won't panic on invalid data pub fn new_fallible(data: &'a [u32]) -> Result { let Fdt { parser, strings, structs, header, .. } = Fdt::new(data)?; - Ok(Self { parser, strings, structs, header, _mode: core::marker::PhantomData }) + Ok(Self { + parser: (AlignedParser::new(parser.data(), strings.0), NoPanic), + strings, + structs, + header, + }) } /// # Safety @@ -275,11 +301,16 @@ impl<'a> Fdt<'a, AlignedParser<'a>, NoPanic> { /// is invalid this can result in undefined behavior. pub unsafe fn from_ptr_fallible(ptr: *const u32) -> Result { let Fdt { parser, strings, structs, header, .. } = Fdt::from_ptr(ptr)?; - Ok(Self { parser, strings, structs, header, _mode: core::marker::PhantomData }) + Ok(Self { + parser: (AlignedParser::new(parser.data(), strings.0), NoPanic), + strings, + structs, + header, + }) } } -impl<'a, P: Parser<'a>, Mode: PanicMode> Fdt<'a, P, Mode> { +impl<'a, P: ParserWithMode<'a>> Fdt<'a, P> { /// Return the `/aliases` node, if one exists // pub fn aliases(&self) -> Option> { // Some(Aliases { @@ -346,11 +377,9 @@ impl<'a, P: Parser<'a>, Mode: PanicMode> Fdt<'a, P, Mode> { // } /// Return the root (`/`) node, which is always available - pub fn root( - &self, - ) -> ::Output>::Granularity, Mode>> { + pub fn root(&self) -> P::Output> { let mut parser = self.parser.clone(); - ::to_output(parser.parse_root::().map(|node| Root { node })) + P::to_output(parser.parse_root().map(|node| Root { node })) } /// Returns the first node that matches the node path, if you want all that diff --git a/src/nodes.rs b/src/nodes.rs index 2ba1e3f..f3b8c3a 100644 --- a/src/nodes.rs +++ b/src/nodes.rs @@ -1,15 +1,17 @@ use crate::{ parsing::{ - self, BigEndianToken, Panic, PanicMode, ParseError, Parser, ParserForSize, StringsBlock, + aligned::AlignedParser, BigEndianToken, NoPanic, Panic, PanicMode, ParseError, Parser, + ParserWithMode, StringsBlock, }, properties::Property, + FdtError, }; #[macro_export] #[doc(hidden)] macro_rules! tryblock { ($($ts:tt)+) => {{ - (|| -> Result<_, $crate::parsing::ParseError> { + (|| -> Result<_, $crate::FdtError> { $($ts)+ })() }}; @@ -66,52 +68,48 @@ impl core::fmt::Display for NodeName<'_> { } } -pub struct Node<'a, Granularity: ParserForSize = u32, Mode: PanicMode = Panic> { - pub(crate) this: &'a RawNode, - pub(crate) parent: Option<&'a RawNode>, +pub struct Node<'a, P: ParserWithMode<'a>> { + pub(crate) this: &'a RawNode<

>::Granularity>, + pub(crate) parent: Option<&'a RawNode<

>::Granularity>>, pub(crate) strings: StringsBlock<'a>, - pub(crate) _mode: core::marker::PhantomData<*mut Mode>, + pub(crate) _mode: core::marker::PhantomData<*mut P>, } -impl<'a, Granularity: ParserForSize, Mode: PanicMode> Node<'a, Granularity, Mode> { +impl<'a, P: ParserWithMode<'a>> Node<'a, P> { #[inline] pub(crate) fn new( - this: &'a RawNode, - parent: Option<&'a RawNode>, + this: &'a RawNode<

>::Granularity>, + parent: Option<&'a RawNode<

>::Granularity>>, strings: StringsBlock<'a>, ) -> Self { Self { this, parent, strings, _mode: core::marker::PhantomData } } #[inline] - pub fn name(&self) -> ::Output> { - ::to_output( - <::Parser<'a> as Parser<'a>>::new( - &self.this.0, - self.strings.0, - ) - .advance_cstr() - .and_then(|s| s.to_str().map_err(|_| ParseError::InvalidCStrValue)) - .map(|s| { - if s.is_empty() { - return NodeName { name: "/", unit_address: None }; - } - - let (name, unit_address) = s.split_once('@').unzip(); - NodeName { name: name.unwrap_or(s), unit_address } - }), + pub fn name(&self) ->

::Output> { + P::to_output( + P::new(&self.this.0, self.strings.0) + .advance_cstr() + .and_then(|s| { + s.to_str().map_err(|_| FdtError::ParseError(ParseError::InvalidCStrValue)) + }) + .map(|s| { + if s.is_empty() { + return NodeName { name: "/", unit_address: None }; + } + + let (name, unit_address) = s.split_once('@').unzip(); + NodeName { name: name.unwrap_or(s), unit_address } + }), ) } #[inline] - pub fn properties(&self) -> ::Output> { - let mut parser = <::Parser<'a> as Parser<'a>>::new( - &self.this.0, - self.strings.0, - ); + pub fn properties(&self) -> P::Output> { + let mut parser = P::new(&self.this.0, self.strings.0); let res = parser.advance_cstr(); - ::to_output(res.map(|_| NodeProperties { + P::to_output(res.map(|_| NodeProperties { data: parser.data(), strings: self.strings, _mode: core::marker::PhantomData, @@ -119,24 +117,25 @@ impl<'a, Granularity: ParserForSize, Mode: PanicMode> Node<'a, Granularity, Mode } #[inline] - pub fn raw_property( - &self, - name: &str, - ) -> ::Output>> { - ::to_output(tryblock! { - Ok(::to_result(self.properties())?.find(name)) + pub fn raw_property(&self, name: &str) -> P::Output>> { + P::to_output(tryblock! { + P::to_result(P::to_result(self.properties())?.find(name)) }) } - pub fn property>(&self) -> ::Output> { - ::to_output(P::parse(*self)) + pub fn property>(&self) -> P::Output> { + P::to_output(Prop::parse(Node { + this: RawNode::new(self.this.as_slice()), + strings: self.strings, + parent: self.parent.map(|r| RawNode::new(r.as_slice())), + _mode: core::marker::PhantomData::<*mut (P::Parser, NoPanic)>, + })) } #[inline] - pub fn children(&self) -> ::Output> { - ::to_output(tryblock! { - let mut parser = - <::Parser<'a> as Parser<'a>>::new(&self.this.0, self.strings.0); + pub fn children(&self) -> P::Output> { + P::to_output(tryblock! { + let mut parser = P::new(&self.this.0, self.strings.0); parser.advance_cstr()?; while let BigEndianToken::PROP = parser.peek_token()? { parser.parse_raw_property()?; @@ -162,28 +161,27 @@ impl<'a, Granularity: ParserForSize, Mode: PanicMode> Node<'a, Granularity, Mode } } -impl<'a, Granularity: ParserForSize, Mode: PanicMode> core::fmt::Debug - for Node<'a, Granularity, Mode> +impl<'a, P: ParserWithMode<'a>> core::fmt::Debug for Node<'a, P> where - ::Output>: core::fmt::Debug, + P::Output>: core::fmt::Debug, { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_struct("Node").field("name", &self.name()).finish_non_exhaustive() } } -impl<'a, Granularity: ParserForSize, Mode: PanicMode> Clone for Node<'a, Granularity, Mode> { +impl<'a, P: ParserWithMode<'a>> Clone for Node<'a, P> { fn clone(&self) -> Self { *self } } -impl<'a, Granularity: ParserForSize, Mode: PanicMode> Copy for Node<'a, Granularity, Mode> {} +impl<'a, P: ParserWithMode<'a>> Copy for Node<'a, P> {} #[repr(transparent)] -pub(crate) struct RawNode([Granularity]); +pub(crate) struct RawNode([Granularity]); -impl RawNode { +impl RawNode { pub(crate) fn new(data: &[Granularity]) -> &Self { // SAFETY: the representation of `Self` and `data` are the same unsafe { core::mem::transmute(data) } @@ -195,86 +193,78 @@ impl RawNode { } } -#[derive(Debug)] -pub struct NodeProperties<'a, Granularity: ParserForSize = u32, Mode: PanicMode = Panic> { - data: &'a [Granularity], +pub struct NodeProperties<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { + data: &'a [

>::Granularity], strings: StringsBlock<'a>, - _mode: core::marker::PhantomData<*mut Mode>, + _mode: core::marker::PhantomData<*mut P>, } -impl<'a, Granularity: ParserForSize, Mode: PanicMode> NodeProperties<'a, Granularity, Mode> { - pub fn iter(self) -> NodePropertiesIter<'a, Granularity, Mode> { +impl<'a, P: ParserWithMode<'a>> NodeProperties<'a, P> { + pub fn iter(self) -> NodePropertiesIter<'a, P> { NodePropertiesIter { properties: self } } - pub fn advance(&mut self) -> ::Output>> { - let mut parser = <::Parser<'a> as Parser<'a>>::new( - self.data, - self.strings.0, - ); + pub fn advance(&mut self) -> P::Output>> { + let mut parser = P::new(self.data, self.strings.0); match parser.peek_token() { Ok(BigEndianToken::PROP) => {} - Ok(BigEndianToken::BEGIN_NODE) => return ::to_output(Ok(None)), - Ok(_) => return ::to_output(Err(ParseError::UnexpectedToken)), - Err(ParseError::UnexpectedEndOfData) => { - return ::to_output(Ok(None)) + Ok(BigEndianToken::BEGIN_NODE) => return P::to_output(Ok(None)), + Ok(_) => return P::to_output(Err(ParseError::UnexpectedToken.into())), + Err(FdtError::ParseError(ParseError::UnexpectedEndOfData)) => { + return P::to_output(Ok(None)) } - Err(e) => return ::to_output(Err(e)), + Err(e) => return P::to_output(Err(e.into())), } - ::to_output(tryblock! { + P::to_output(tryblock! { match parser.parse_raw_property() { Ok((name_offset, data)) => { self.data = parser.data(); Ok(Some(NodeProperty::new(self.strings.offset_at(name_offset)?, data))) } - Err(ParseError::UnexpectedEndOfData) => Ok(None), + Err(FdtError::ParseError(ParseError::UnexpectedEndOfData)) => Ok(None), Err(e) => return Err(e), } }) } - pub fn find(&self, name: &str) -> ::Output>> { - ::reverse_transpose(self.iter().find(|p| { - ::ok_as_ref(p).map(|p| p.name == name).unwrap_or_default() - })) + pub fn find(&self, name: &str) -> P::Output>> { + P::reverse_transpose( + self.iter().find(|p| P::ok_as_ref(p).map(|p| p.name == name).unwrap_or_default()), + ) } } -impl Clone for NodeProperties<'_, Granularity, Mode> { +impl<'a, P: ParserWithMode<'a>> Clone for NodeProperties<'a, P> { fn clone(&self) -> Self { *self } } -impl Copy for NodeProperties<'_, Granularity, Mode> {} +impl<'a, P: ParserWithMode<'a>> Copy for NodeProperties<'a, P> {} -impl<'a, Granularity: ParserForSize, Mode: PanicMode> IntoIterator - for NodeProperties<'a, Granularity, Mode> -{ - type IntoIter = NodePropertiesIter<'a, Granularity, Mode>; - type Item = ::Output>; +impl<'a, P: ParserWithMode<'a>> IntoIterator for NodeProperties<'a, P> { + type IntoIter = NodePropertiesIter<'a, P>; + type Item = P::Output>; fn into_iter(self) -> Self::IntoIter { self.iter() } } -#[derive(Debug, Clone)] -pub struct NodePropertiesIter<'a, Granularity: ParserForSize = u32, Mode: PanicMode = Panic> { - properties: NodeProperties<'a, Granularity, Mode>, +#[derive(Clone)] +pub struct NodePropertiesIter<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { + properties: NodeProperties<'a, P>, } -impl<'a, Granularity: ParserForSize, Mode: PanicMode> Iterator - for NodePropertiesIter<'a, Granularity, Mode> -{ - type Item = ::Output>; +impl<'a, P: ParserWithMode<'a>> Iterator for NodePropertiesIter<'a, P> { + type Item = P::Output>; #[track_caller] fn next(&mut self) -> Option { - ::transpose(self.properties.advance()) + P::transpose(self.properties.advance()) } } @@ -314,57 +304,51 @@ impl<'a> NodeProperty<'a> { } } -pub struct NodeChildren<'a, Granularity: ParserForSize = u32, Mode: PanicMode = Panic> { - data: &'a [Granularity], - parent: &'a RawNode, +pub struct NodeChildren<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { + data: &'a [

>::Granularity], + parent: &'a RawNode<

>::Granularity>, strings: StringsBlock<'a>, - _mode: core::marker::PhantomData<*mut Mode>, + _mode: core::marker::PhantomData<*mut P>, } -impl<'a, Granularity: ParserForSize, Mode: PanicMode> NodeChildren<'a, Granularity, Mode> { - pub fn iter(self) -> NodeChildrenIter<'a, Granularity, Mode> { +impl<'a, P: ParserWithMode<'a>> NodeChildren<'a, P> { + pub fn iter(self) -> NodeChildrenIter<'a, P> { NodeChildrenIter { children: self } } - pub fn advance(&mut self) -> ::Output>> { - let mut parser = <::Parser<'a> as Parser<'a>>::new( - self.data, - self.strings.0, - ); + pub fn advance(&mut self) -> P::Output>> { + let mut parser = P::new(self.data, self.strings.0); match parser.peek_token() { Ok(BigEndianToken::BEGIN_NODE) => {} - Ok(BigEndianToken::END_NODE) => return ::to_output(Ok(None)), - Ok(_) => return ::to_output(Err(ParseError::UnexpectedToken)), - Err(ParseError::UnexpectedEndOfData) => { - return ::to_output(Ok(None)) + Ok(BigEndianToken::END_NODE) => return P::to_output(Ok(None)), + Ok(_) => return P::to_output(Err(ParseError::UnexpectedToken.into())), + Err(FdtError::ParseError(ParseError::UnexpectedEndOfData)) => { + return P::to_output(Ok(None)) } - Err(e) => return ::to_output(Err(e)), + Err(e) => return P::to_output(Err(e.into())), } - ::to_output(match parser.parse_node(Some(self.parent)) { + P::to_output(match parser.parse_node(Some(self.parent)) { Ok(node) => { self.data = parser.data(); Ok(Some(node)) } - Err(ParseError::UnexpectedEndOfData) => Ok(None), - Err(e) => Err(e), + Err(FdtError::ParseError(ParseError::UnexpectedEndOfData)) => Ok(None), + Err(e) => Err(e.into()), }) } - pub fn find<'n, N>( - &self, - name: N, - ) -> ::Output>> + pub fn find<'n, N>(&self, name: N) -> P::Output>> where N: IntoSearchableNodeName<'n>, - Mode: 'a, + P: 'a, { let name = name.into_searchable_node_name(); - ::reverse_transpose(self.iter().find(|n| { - ::ok_as_ref(n) - .and_then(|n| ::ok(n.name())) + P::reverse_transpose(self.iter().find(|n| { + P::ok_as_ref(n) + .and_then(|n| P::ok(n.name())) .map(|n| match name { SearchableNodeName::Base(base) => n.name == base, SearchableNodeName::WithUnitAddress(nn) => n == nn, @@ -374,28 +358,24 @@ impl<'a, Granularity: ParserForSize, Mode: PanicMode> NodeChildren<'a, Granulari } } -impl<'a, Granularity: ParserForSize, Mode: PanicMode> Clone - for NodeChildren<'a, Granularity, Mode> -{ +impl<'a, P: ParserWithMode<'a>> Clone for NodeChildren<'a, P> { fn clone(&self) -> Self { Self { _mode: self._mode, data: self.data, strings: self.strings, parent: self.parent } } } -impl<'a, Granularity: ParserForSize, Mode: PanicMode> Copy for NodeChildren<'a, Granularity, Mode> {} +impl<'a, P: ParserWithMode<'a>> Copy for NodeChildren<'a, P> {} #[derive(Clone)] -pub struct NodeChildrenIter<'a, Granularity: ParserForSize = u32, Mode: PanicMode = Panic> { - children: NodeChildren<'a, Granularity, Mode>, +pub struct NodeChildrenIter<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { + children: NodeChildren<'a, P>, } -impl<'a, Granularity: ParserForSize, Mode: PanicMode + 'a> Iterator - for NodeChildrenIter<'a, Granularity, Mode> -{ - type Item = ::Output>; +impl<'a, P: ParserWithMode<'a> + 'a> Iterator for NodeChildrenIter<'a, P> { + type Item = P::Output>; #[track_caller] fn next(&mut self) -> Option { - ::transpose(self.children.advance()) + P::transpose(self.children.advance()) } } diff --git a/src/parsing.rs b/src/parsing.rs index b16e079..2265d41 100644 --- a/src/parsing.rs +++ b/src/parsing.rs @@ -5,10 +5,9 @@ pub mod aligned; pub mod unaligned; -use self::{aligned::AlignedParser, unaligned::UnalignedParser}; use crate::{ nodes::{Node, RawNode}, - FdtHeader, + FdtError, FdtHeader, }; #[derive(Debug, Clone, Copy)] @@ -199,38 +198,25 @@ impl core::fmt::Display for ParseError { } } -pub trait ParserForSize: crate::sealed::Sealed { - type Parser<'a>: Parser<'a, Granularity = Self>; -} - -impl crate::sealed::Sealed for u8 {} -impl ParserForSize for u8 { - type Parser<'a> = UnalignedParser<'a>; -} - -impl crate::sealed::Sealed for u32 {} -impl ParserForSize for u32 { - type Parser<'a> = AlignedParser<'a>; -} - -pub trait PanicMode: crate::sealed::Sealed + 'static { +pub trait PanicMode: crate::sealed::Sealed { type Output; - fn to_output(result: Result) -> Self::Output; + fn to_output(result: Result) -> Self::Output; fn transpose(result: Self::Output>) -> Option>; fn reverse_transpose(result: Option>) -> Self::Output>; fn ok_as_ref(output: &Self::Output) -> Option<&T>; fn ok(output: Self::Output) -> Option; - fn to_result(output: Self::Output) -> Result; + fn to_result(output: Self::Output) -> Result; } +#[derive(Clone, Copy, Default)] pub struct NoPanic; impl crate::sealed::Sealed for NoPanic {} impl PanicMode for NoPanic { - type Output = Result; + type Output = Result; #[inline(always)] - fn to_output(result: Result) -> Self::Output { + fn to_output(result: Result) -> Self::Output { result } @@ -250,11 +236,12 @@ impl PanicMode for NoPanic { output.ok() } - fn to_result(output: Self::Output) -> Result { + fn to_result(output: Self::Output) -> Result { output } } +#[derive(Clone, Copy, Default)] pub struct Panic; impl crate::sealed::Sealed for Panic {} @@ -263,7 +250,7 @@ impl PanicMode for Panic { #[track_caller] #[inline(always)] - fn to_output(result: Result) -> Self::Output { + fn to_output(result: Result) -> Self::Output { result.unwrap() } @@ -285,38 +272,128 @@ impl PanicMode for Panic { Some(output) } - fn to_result(output: Self::Output) -> Result { + fn to_result(output: Self::Output) -> Result { Ok(output) } } +pub trait ParserWithMode<'a>: Parser<'a> + PanicMode + crate::sealed::Sealed { + type Parser: Parser<'a, Granularity = Self::Granularity>; + type Mode: PanicMode; + + fn into_parts( + self, + ) -> (>::Parser, >::Mode); +} + +impl<'a, T: Parser<'a>, U: PanicMode> crate::sealed::Sealed for (T, U) {} + +impl<'a, T: Parser<'a>, U: PanicMode + Clone + Default> Parser<'a> for (T, U) { + type Granularity = T::Granularity; + + fn new(data: &'a [Self::Granularity], strings: &'a [u8]) -> Self { + (T::new(data, strings), U::default()) + } + + fn data(&self) -> &'a [Self::Granularity] { + self.0.data() + } + + fn byte_data(&self) -> &'a [u8] { + self.0.byte_data() + } + + fn strings(&self) -> StringsBlock<'a> { + self.0.strings() + } + + fn advance_token(&mut self) -> Result { + self.0.advance_token() + } + + fn advance_u32(&mut self) -> Result { + self.0.advance_u32() + } + + fn advance_u64(&mut self) -> Result { + self.0.advance_u64() + } + + fn advance_cstr(&mut self) -> Result<&'a core::ffi::CStr, FdtError> { + self.0.advance_cstr() + } + + fn advance_aligned(&mut self, n: usize) { + self.0.advance_aligned(n) + } +} + +impl<'a, P: Parser<'a>, U: PanicMode> PanicMode for (P, U) { + type Output = U::Output; + + fn to_output(result: Result) -> Self::Output { + U::to_output(result) + } + + fn transpose(result: Self::Output>) -> Option> { + U::transpose(result) + } + + fn reverse_transpose(result: Option>) -> Self::Output> { + U::reverse_transpose(result) + } + + fn ok_as_ref(output: &Self::Output) -> Option<&T> { + U::ok_as_ref(output) + } + + fn ok(output: Self::Output) -> Option { + U::ok(output) + } + + fn to_result(output: Self::Output) -> Result { + U::to_result(output) + } +} + +impl<'a, T: Parser<'a>, U: PanicMode + Clone + Default + 'static> ParserWithMode<'a> for (T, U) { + type Mode = U; + type Parser = T; + + fn into_parts( + self, + ) -> (>::Parser, >::Mode) { + self + } +} + pub trait Parser<'a>: crate::sealed::Sealed + Clone { - type Granularity: Copy + ParserForSize; + type Granularity: Copy; fn new(data: &'a [Self::Granularity], strings: &'a [u8]) -> Self; fn data(&self) -> &'a [Self::Granularity]; fn byte_data(&self) -> &'a [u8]; fn strings(&self) -> StringsBlock<'a>; - fn advance_token(&mut self) -> Result; - fn peek_token(&mut self) -> Result { + fn advance_token(&mut self) -> Result; + fn peek_token(&mut self) -> Result { self.clone().advance_token() } - fn advance_u32(&mut self) -> Result; - fn advance_u64(&mut self) -> Result; - fn advance_cstr(&mut self) -> Result<&'a core::ffi::CStr, ParseError>; + fn advance_u32(&mut self) -> Result; + fn advance_u64(&mut self) -> Result; + fn advance_cstr(&mut self) -> Result<&'a core::ffi::CStr, FdtError>; fn advance_aligned(&mut self, n: usize); - fn peek_u32(&self) -> Result { + fn peek_u32(&self) -> Result { self.clone().advance_u32() } - fn peek_u64(&self) -> Result { + fn peek_u64(&self) -> Result { self.clone().advance_u64() } - fn parse_header(&mut self) -> Result { + fn parse_header(&mut self) -> Result { let magic = self.advance_u32()?.to_ne(); let total_size = self.advance_u32()?.to_ne(); let struct_offset = self.advance_u32()?.to_ne(); @@ -342,12 +419,13 @@ pub trait Parser<'a>: crate::sealed::Sealed + Clone { }) } - fn parse_root( - &mut self, - ) -> Result, ParseError> { + fn parse_root(&mut self) -> Result, FdtError> + where + Self: ParserWithMode<'a>, + { match self.advance_token()? { BigEndianToken::BEGIN_NODE => {} - _ => return Err(ParseError::UnexpectedToken), + _ => return Err(FdtError::ParseError(ParseError::UnexpectedToken)), } let starting_data = self.data(); @@ -359,10 +437,10 @@ pub trait Parser<'a>: crate::sealed::Sealed + Clone { Some(Ok(data @ [_, _, _, _])) => { match BigEndianToken(BigEndianU32(u32::from_ne_bytes(data))) { BigEndianToken::END => {} - _ => return Err(ParseError::UnexpectedToken), + _ => return Err(FdtError::ParseError(ParseError::UnexpectedToken)), } } - _ => return Err(ParseError::UnexpectedEndOfData), + _ => return Err(FdtError::ParseError(ParseError::UnexpectedEndOfData)), } Ok(Node { @@ -378,10 +456,10 @@ pub trait Parser<'a>: crate::sealed::Sealed + Clone { Some(Ok(data @ [_, _, _, _])) => { match BigEndianToken(BigEndianU32(u32::from_ne_bytes(data))) { BigEndianToken::END => {} - _ => return Err(ParseError::UnexpectedToken), + _ => return Err(FdtError::ParseError(ParseError::UnexpectedToken)), } } - _ => return Err(ParseError::UnexpectedEndOfData), + _ => return Err(FdtError::ParseError(ParseError::UnexpectedEndOfData)), } Ok(Node { @@ -395,13 +473,16 @@ pub trait Parser<'a>: crate::sealed::Sealed + Clone { } } - fn parse_node( + fn parse_node( &mut self, parent: Option<&'a RawNode>, - ) -> Result, ParseError> { + ) -> Result, FdtError> + where + Self: ParserWithMode<'a>, + { match self.advance_token()? { BigEndianToken::BEGIN_NODE => {} - _ => return Err(ParseError::UnexpectedToken), + _ => return Err(FdtError::ParseError(ParseError::UnexpectedToken)), } let starting_data = self.data(); @@ -415,7 +496,7 @@ pub trait Parser<'a>: crate::sealed::Sealed + Clone { let mut depth = 0; loop { - let token = self.advance_token()?; + let token = self.peek_token()?; match token { BigEndianToken::BEGIN_NODE => depth += 1, BigEndianToken::END_NODE => match depth { @@ -425,9 +506,11 @@ pub trait Parser<'a>: crate::sealed::Sealed + Clone { continue; } }, - _ => return Err(ParseError::InvalidTokenValue), + _ => return Err(FdtError::ParseError(ParseError::InvalidTokenValue)), } + let _ = self.advance_token(); + self.advance_cstr()?; while self.peek_token()? == BigEndianToken::PROP { @@ -448,11 +531,11 @@ pub trait Parser<'a>: crate::sealed::Sealed + Clone { strings: self.strings(), _mode: core::marker::PhantomData, }), - _ => return Err(ParseError::UnexpectedToken), + _ => return Err(FdtError::ParseError(ParseError::UnexpectedToken)), } } - fn parse_raw_property(&mut self) -> Result<(usize, &'a [u8]), ParseError> { + fn parse_raw_property(&mut self) -> Result<(usize, &'a [u8]), FdtError> { match self.advance_token()? { BigEndianToken::PROP => { // Properties are in the format: @@ -466,7 +549,7 @@ pub trait Parser<'a>: crate::sealed::Sealed + Clone { Ok((name_offset, data)) } - _ => Err(ParseError::UnexpectedToken), + _ => Err(FdtError::ParseError(ParseError::UnexpectedToken)), } } } @@ -476,12 +559,12 @@ pub trait Parser<'a>: crate::sealed::Sealed + Clone { pub struct StringsBlock<'a>(pub(crate) &'a [u8]); impl<'a> StringsBlock<'a> { - pub fn offset_at(self, offset: usize) -> Result<&'a str, ParseError> { + pub fn offset_at(self, offset: usize) -> Result<&'a str, FdtError> { core::ffi::CStr::from_bytes_until_nul( self.0.get(offset..).ok_or(ParseError::UnexpectedEndOfData)?, ) .map_err(|_| ParseError::InvalidCStrValue)? .to_str() - .map_err(|_| ParseError::InvalidCStrValue) + .map_err(|_| ParseError::InvalidCStrValue.into()) } } diff --git a/src/parsing/aligned.rs b/src/parsing/aligned.rs index 8e8de36..5e0cea8 100644 --- a/src/parsing/aligned.rs +++ b/src/parsing/aligned.rs @@ -2,6 +2,8 @@ // v. 2.0. If a copy of the MPL was not distributed with this file, You can // obtain one at https://mozilla.org/MPL/2.0/. +use crate::FdtError; + use super::{BigEndianToken, BigEndianU32, BigEndianU64, ParseError, Parser, Stream, StringsBlock}; pub struct AlignedParser<'a> { @@ -32,7 +34,7 @@ impl<'a> Parser<'a> for AlignedParser<'a> { unsafe { core::slice::from_raw_parts( self.stream.0.as_ptr().cast::(), - core::mem::size_of_val(self.stream.0), + self.stream.0.len() * 4, ) } } @@ -41,7 +43,7 @@ impl<'a> Parser<'a> for AlignedParser<'a> { self.strings } - fn advance_token(&mut self) -> Result { + fn advance_token(&mut self) -> Result { loop { match BigEndianToken( self.stream.advance().map(BigEndianU32).ok_or(ParseError::UnexpectedEndOfData)?, @@ -51,16 +53,19 @@ impl<'a> Parser<'a> for AlignedParser<'a> { | token @ BigEndianToken::END_NODE | token @ BigEndianToken::PROP | token @ BigEndianToken::END => break Ok(token), - _ => break Err(ParseError::InvalidTokenValue), + _ => break Err(FdtError::ParseError(ParseError::InvalidTokenValue)), } } } - fn advance_u32(&mut self) -> Result { - self.stream.advance().map(BigEndianU32).ok_or(ParseError::UnexpectedEndOfData) + fn advance_u32(&mut self) -> Result { + self.stream + .advance() + .map(BigEndianU32) + .ok_or(FdtError::ParseError(ParseError::UnexpectedEndOfData)) } - fn advance_u64(&mut self) -> Result { + fn advance_u64(&mut self) -> Result { let (a, b) = self .stream .advance() @@ -75,7 +80,7 @@ impl<'a> Parser<'a> for AlignedParser<'a> { return Ok(BigEndianU64::from_be((u64::from(a.to_be()) << 32) | u64::from(b.to_be()))); } - fn advance_cstr(&mut self) -> Result<&'a core::ffi::CStr, ParseError> { + fn advance_cstr(&mut self) -> Result<&'a core::ffi::CStr, FdtError> { // SAFETY: It is safe to reinterpret the stream data to a smaller integer size let bytes = unsafe { core::slice::from_raw_parts( diff --git a/src/parsing/unaligned.rs b/src/parsing/unaligned.rs index 191c3fc..13c39d5 100644 --- a/src/parsing/unaligned.rs +++ b/src/parsing/unaligned.rs @@ -2,6 +2,8 @@ // v. 2.0. If a copy of the MPL was not distributed with this file, You can // obtain one at https://mozilla.org/MPL/2.0/. +use crate::FdtError; + use super::{BigEndianToken, BigEndianU32, BigEndianU64, ParseError, Parser, Stream, StringsBlock}; pub struct UnalignedParser<'a> { @@ -35,7 +37,7 @@ impl<'a> Parser<'a> for UnalignedParser<'a> { self.strings } - fn advance_token(&mut self) -> Result { + fn advance_token(&mut self) -> Result { loop { match BigEndianToken(self.advance_u32()?) { BigEndianToken::NOP => continue, @@ -43,14 +45,14 @@ impl<'a> Parser<'a> for UnalignedParser<'a> { | token @ BigEndianToken::END_NODE | token @ BigEndianToken::PROP | token @ BigEndianToken::END => break Ok(token), - _ => break Err(ParseError::InvalidTokenValue), + _ => break Err(FdtError::ParseError(ParseError::InvalidTokenValue)), } } } - fn advance_u32(&mut self) -> Result { + fn advance_u32(&mut self) -> Result { if self.stream.0.len() < core::mem::size_of::() { - return Err(ParseError::UnexpectedEndOfData); + return Err(FdtError::ParseError(ParseError::UnexpectedEndOfData)); } let data = self.stream.0; @@ -60,9 +62,9 @@ impl<'a> Parser<'a> for UnalignedParser<'a> { Ok(BigEndianU32::from_be(unsafe { core::ptr::read_unaligned(data.as_ptr().cast::()) })) } - fn advance_u64(&mut self) -> Result { + fn advance_u64(&mut self) -> Result { if self.stream.0.len() < core::mem::size_of::() { - return Err(ParseError::UnexpectedEndOfData); + return Err(FdtError::ParseError(ParseError::UnexpectedEndOfData)); } let data = self.stream.0; @@ -72,7 +74,7 @@ impl<'a> Parser<'a> for UnalignedParser<'a> { Ok(BigEndianU64::from_be(unsafe { core::ptr::read_unaligned(data.as_ptr().cast::()) })) } - fn advance_cstr(&mut self) -> Result<&'a core::ffi::CStr, ParseError> { + fn advance_cstr(&mut self) -> Result<&'a core::ffi::CStr, FdtError> { let cstr = core::ffi::CStr::from_bytes_until_nul(self.stream.0) .map_err(|_| ParseError::InvalidCStrValue)?; diff --git a/src/properties.rs b/src/properties.rs index 9a64758..5d16288 100644 --- a/src/properties.rs +++ b/src/properties.rs @@ -1,15 +1,11 @@ use crate::{ nodes::Node, - parsing::{unaligned::UnalignedParser, PanicMode, ParseError, Parser, ParserForSize}, + parsing::{unaligned::UnalignedParser, NoPanic, Parser}, + FdtError, }; pub trait Property<'a>: Sized { - fn parse<'b, Granularity, Mode>( - node: Node<'a, Granularity, Mode>, - ) -> Result, ParseError> - where - Granularity: ParserForSize, - Mode: PanicMode; + fn parse>(node: Node<'a, (P, NoPanic)>) -> Result, FdtError>; } pub struct CellSizes { @@ -19,16 +15,12 @@ pub struct CellSizes { impl<'a> Property<'a> for CellSizes { #[track_caller] - fn parse<'b, Granularity, Mode>( - node: Node<'a, Granularity, Mode>, - ) -> Result, ParseError> - where - Granularity: ParserForSize, - Mode: PanicMode, - { + fn parse>(node: Node<'a, (P, NoPanic)>) -> Result, FdtError> { let (mut address_cells, mut size_cells) = (None, None); - for property in ::to_result(node.properties())? { + for property in node.properties()? { + let property = property?; + let mut parser = UnalignedParser::new(property.value(), &[]); match property.name() { "#address-cells" => address_cells = Some(parser.advance_u32()?.to_ne() as usize), @@ -38,13 +30,11 @@ impl<'a> Property<'a> for CellSizes { } if let (None, Some(parent)) = (address_cells, node.parent()) { - address_cells = - ::to_result(parent.property::())?.map(|c| c.address_cells); + address_cells = parent.property::()?.map(|c| c.address_cells); } if let (None, Some(parent)) = (size_cells, node.parent()) { - size_cells = - ::to_result(parent.property::())?.map(|c| c.size_cells); + size_cells = parent.property::()?.map(|c| c.size_cells); } Ok(Some(CellSizes { diff --git a/src/standard_nodes.rs b/src/standard_nodes.rs index ee62a15..30a5874 100644 --- a/src/standard_nodes.rs +++ b/src/standard_nodes.rs @@ -2,51 +2,49 @@ // v. 2.0. If a copy of the MPL was not distributed with this file, You can // obtain one at https://mozilla.org/MPL/2.0/. +use core::ops::ControlFlow; + use crate::{ nodes::{IntoSearchableNodeName, Node, NodeName, RawNode, SearchableNodeName}, parsing::{ - BigEndianToken, BigEndianU32, BigEndianU64, FdtData, Panic, PanicMode, ParseError, Parser, - ParserForSize, + aligned::AlignedParser, BigEndianToken, BigEndianU32, BigEndianU64, FdtData, Panic, + PanicMode, ParseError, Parser, ParserWithMode, }, properties::CellSizes, - tryblock, Fdt, + tryblock, Fdt, FdtError, }; /// Represents the `/chosen` node with specific helper methods -pub struct Chosen<'a, Granularity = u32, Mode = Panic> -where - Granularity: ParserForSize, - Mode: PanicMode, -{ - pub(crate) node: Node<'a, Granularity, Mode>, +pub struct Chosen<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { + pub(crate) node: Node<'a, P>, } -impl<'a, Granularity, Mode> Chosen<'a, Granularity, Mode> -where - Granularity: ParserForSize, - Mode: PanicMode, -{ +impl<'a, P: ParserWithMode<'a>> Chosen<'a, P> { /// Contains the bootargs, if they exist #[track_caller] - pub fn bootargs(self) -> Mode::Output> { - ::to_output(crate::tryblock! { - Ok( - ::to_result(self.node.properties())? - .into_iter() - .find(|n| n.name() == "bootargs") - .and_then(|n| { - core::str::from_utf8(&n.value()[..n.value().len() - 1]).ok() - }) - ) + pub fn bootargs(self) -> P::Output> { + P::to_output(crate::tryblock! { + for prop in P::to_result(self.node.properties())?.into_iter().map(|r| P::to_result(r)) { + if let Ok(prop) = prop { + if prop.name() == "bootargs" { + return Ok(Some( + core::str::from_utf8(&prop.value()[..prop.value().len() - 1]) + .map_err(|_| FdtError::ParseError(ParseError::InvalidCStrValue))?, + )); + } + } + } + + Ok(None) }) } /// Searches for the node representing `stdout`, if the property exists, /// attempting to resolve aliases if the node name doesn't exist as-is - // pub fn stdout(self) -> Mode::Output>> { - // ::to_output(crate::tryblock! { + // pub fn stdout(self) -> Mode::Output>> { + // P::to_output(crate::tryblock! { // Ok( - // ::to_result(self.node.properties())? + // P::to_result(self.node.properties())? // .find(|n| n.name == "stdout-path") // .and_then(|n| core::str::from_utf8(&n.value()[..n.value().len() - 1]).ok()) // .map(Self::split_stdinout_property) @@ -85,16 +83,16 @@ where } } -impl Clone for Chosen<'_, Granularity, Mode> { +impl<'a, P: ParserWithMode<'a>> Clone for Chosen<'a, P> { fn clone(&self) -> Self { Self { node: self.node } } } -impl Copy for Chosen<'_, Granularity, Mode> {} +impl<'a, P: ParserWithMode<'a>> Copy for Chosen<'a, P> {} -pub struct StdInOutPath<'a, Granularity: ParserForSize = u32, Mode: PanicMode = Panic> { - pub(crate) node: Node<'a, Granularity, Mode>, +pub struct StdInOutPath<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { + pub(crate) node: Node<'a, P>, pub(crate) params: Option<&'a str>, } @@ -117,14 +115,14 @@ pub struct StdInOutPath<'a, Granularity: ParserForSize = u32, Mode: PanicMode = // } /// Represents the root (`/`) node with specific helper methods -pub struct Root<'a, Granularity: ParserForSize = u32, Mode: PanicMode = Panic> { - pub(crate) node: Node<'a, Granularity, Mode>, +pub struct Root<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { + pub(crate) node: Node<'a, P>, } -impl<'a, Granularity: ParserForSize, Mode: PanicMode> Root<'a, Granularity, Mode> { +impl<'a, P: ParserWithMode<'a>> Root<'a, P> { /// Root node cell sizes - pub fn cell_sizes(self) -> ::Output { - ::transpose(self.node.property::()).unwrap() + pub fn cell_sizes(self) -> P::Output { + P::transpose(self.node.property::()).unwrap() } /// `model` property @@ -154,11 +152,14 @@ impl<'a, Granularity: ParserForSize, Mode: PanicMode> Root<'a, Granularity, Mode pub fn find_node<'b, N: IntoSearchableNodeName<'b>>( self, name: N, - ) -> ::Output>> { + ) -> P::Output>> + where + P: 'a, + { let name = name.into_searchable_node_name(); if let SearchableNodeName::Base("/") = name { - return ::to_output(Ok(Some(self.node))); + return P::to_output(Ok(Some(self.node))); } let (name_str, unit_address) = match name { @@ -168,15 +169,15 @@ impl<'a, Granularity: ParserForSize, Mode: PanicMode> Root<'a, Granularity, Mode } }; - let mut children_iter = match ::to_result(self.node.children()) { + let mut children_iter = match P::to_result(self.node.children()) { Ok(iter) => iter, - Err(e) => return ::to_output(Err(e)), + Err(e) => return P::to_output(Err(e)), }; let mut name_components = name_str.trim_start_matches('/').split('/').peekable(); loop { if name_components.peek().is_none() { - return ::to_output(Ok(None)); + return P::to_output(Ok(None)); } let component = name_components.next().unwrap(); @@ -186,82 +187,81 @@ impl<'a, Granularity: ParserForSize, Mode: PanicMode> Root<'a, Granularity, Mode false => NodeName { name: component, unit_address: None }, }; - match ::to_result(children_iter.find(look_for_name)) { + match P::to_result(children_iter.find(look_for_name)) { Ok(Some(node)) => match is_last_component { - true => return ::to_output(Ok(Some(node))), + true => return P::to_output(Ok(Some(node))), false => { - children_iter = match ::to_result(node.children()) { + children_iter = match P::to_result(node.children()) { Ok(iter) => iter, - Err(e) => return ::to_output(Err(e)), + Err(e) => return P::to_output(Err(e)), } } }, - Ok(None) => return ::to_output(Ok(None)), - Err(e) => return ::to_output(Err(e)), + Ok(None) => return P::to_output(Ok(None)), + Err(e) => return P::to_output(Err(e)), } } } - pub fn all_nodes(self) -> ::Output> { - let mut parser = <::Parser<'a> as Parser<'a>>::new( - self.node.this.as_slice(), - self.node.strings.0, - ); + pub fn all_nodes(self) -> P::Output> { + let mut parser = P::new(self.node.this.as_slice(), self.node.strings.0); let res = tryblock!({ - parser.advance_cstr().unwrap(); + parser.advance_cstr()?; - while parser.peek_token().unwrap() == BigEndianToken::PROP { - parser.parse_raw_property().unwrap(); + while parser.peek_token()? == BigEndianToken::PROP { + parser.parse_raw_property()?; } Ok(()) }); if let Err(e) = res { - return ::to_output(Err(e)); + return P::to_output(Err(e)); } - ::to_output(Ok(AllNodesIterator { - parser, - parents: [&[]; 16], - parent_index: 0, - _mode: core::marker::PhantomData, - })) + P::to_output(Ok(AllNodesIterator { parser, parents: [&[]; 16], parent_index: 0 })) } } -impl<'a, Granularity: ParserForSize, Mode: PanicMode> core::fmt::Debug - for Root<'a, Granularity, Mode> -{ +impl<'a, P: ParserWithMode<'a>> Copy for Root<'a, P> {} +impl<'a, P: ParserWithMode<'a>> Clone for Root<'a, P> { + fn clone(&self) -> Self { + *self + } +} + +impl<'a, P: ParserWithMode<'a>> core::fmt::Debug for Root<'a, P> { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_struct("Root").finish_non_exhaustive() } } -pub struct AllNodesIterator<'a, Granularity: ParserForSize, Mode: PanicMode> { - parser: ::Parser<'a>, - parents: [&'a [Granularity]; 16], +pub struct AllNodesIterator<'a, P: ParserWithMode<'a>> { + parser: P, + parents: [&'a [

>::Granularity]; 16], parent_index: usize, - _mode: core::marker::PhantomData<*mut Mode>, } -impl<'a, Granularity: ParserForSize, Mode: PanicMode> Iterator - for AllNodesIterator<'a, Granularity, Mode> -{ - type Item = ::Output>; +impl<'a, P: ParserWithMode<'a>> Iterator for AllNodesIterator<'a, P> { + type Item = P::Output<(usize, Node<'a, P>)>; #[track_caller] fn next(&mut self) -> Option { + // BUG HERE: needs to properly track the depth with `parent_index` while let Ok(BigEndianToken::END_NODE) = self.parser.peek_token() { let _ = self.parser.advance_token(); + self.parent_index = self.parent_index.saturating_sub(1); } match self.parser.advance_token() { - Ok(BigEndianToken::BEGIN_NODE) => {} + Ok(BigEndianToken::BEGIN_NODE) => self.parent_index += 1, Ok(BigEndianToken::END_NODE) => {} - Ok(BigEndianToken::END) | Err(ParseError::UnexpectedEndOfData) => return None, - Ok(_) => return Some(::to_output(Err(ParseError::UnexpectedToken))), - Err(e) => return Some(::to_output(Err(e))), + Ok(BigEndianToken::END) + | Err(FdtError::ParseError(ParseError::UnexpectedEndOfData)) => return None, + Ok(_) => { + return Some(P::to_output(Err(FdtError::ParseError(ParseError::UnexpectedToken)))) + } + Err(e) => return Some(P::to_output(Err(e))), } let starting_data = self.parser.data(); @@ -271,18 +271,21 @@ impl<'a, Granularity: ParserForSize, Mode: PanicMode> Iterator // FIXME: what makes sense for this to return? None => return None, } - self.parent_index += 1; - - let node = Some(::to_output(Ok(Node { - this: RawNode::new(starting_data), - parent: self - .parent_index - .checked_sub(1) - .and_then(|idx| self.parents.get(idx)) - .map(|parent| RawNode::new(*parent)), - strings: self.parser.strings(), - _mode: core::marker::PhantomData, - }))); + // self.parent_index += 1; + + let node = Some(P::to_output(Ok(( + self.parent_index, + Node { + this: RawNode::new(starting_data), + parent: self + .parent_index + .checked_sub(1) + .and_then(|idx| self.parents.get(idx)) + .map(|parent| RawNode::new(*parent)), + strings: self.parser.strings(), + _mode: core::marker::PhantomData, + }, + )))); let res = tryblock!({ self.parser.advance_cstr()?; @@ -295,7 +298,7 @@ impl<'a, Granularity: ParserForSize, Mode: PanicMode> Iterator }); if let Err(e) = res { - return Some(::to_output(Err(e))); + return Some(P::to_output(Err(e))); } node diff --git a/src/tests.rs b/src/tests.rs index 2d6183a..2ab5cca 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -58,33 +58,61 @@ fn root() { #[test] fn all_nodes() { let fdt = Fdt::new(TEST.as_slice()).unwrap(); - panic!( - "{}", + assert_eq!( fdt.root() .all_nodes() - .map(|n| std::format!("{}", n.name())) + .map(|(depth, n)| std::format!("{depth} {}", n.name())) .collect::>() - .join("\n") + .join("\n"), + "1 chosen +1 memory@80000000 +1 cpus +2 cpu@0 +3 interrupt-controller +2 cpu-map +3 cluster0 +4 core0 +1 emptyproptest +1 soc +2 flash@20000000 +2 rtc@101000 +2 uart@10000000 +2 poweroff +2 reboot +2 test@100000" ); } -// #[test] -// fn finds_root_node() { -// let fdt = Fdt::new(TEST.as_slice()).unwrap(); -// assert!(fdt.find_node("/").is_some(), "couldn't find root node"); -// } - -// #[test] -// fn finds_root_node_properties() { -// let fdt = Fdt::new(TEST.as_slice()).unwrap(); -// let prop = fdt -// .find_node("/") -// .unwrap() -// .properties() -// .any(|p| p.name == "compatible" && p.value == b"riscv-virtio\0"); +#[test] +fn finds_root_node() { + let fdt = Fdt::new(TEST.as_slice()).unwrap(); + assert!(fdt.root().find_node("/").is_some(), "couldn't find root node"); +} -// assert!(prop); -// } +#[test] +fn finds_root_node_properties() { + // infallible + let fdt = Fdt::new(TEST.as_slice()).unwrap(); + let prop = fdt.root().find_node("/").unwrap().properties().find("compatible").unwrap(); + + assert_eq!(prop.value(), b"riscv-virtio\0"); + + // fallible + let fdt = Fdt::new_fallible(TEST.as_slice()).unwrap(); + let prop = fdt + .root() + .unwrap() + .find_node("/") + .unwrap() + .unwrap() + .properties() + .unwrap() + .find("compatible") + .unwrap() + .unwrap(); + + assert_eq!(prop.value(), b"riscv-virtio\0"); +} #[test] fn finds_child_of_root_node() { @@ -93,6 +121,14 @@ fn finds_child_of_root_node() { assert!(root.find_node("/cpus").is_some(), "couldn't find cpus node"); } +#[test] +fn finds_child_with_unit_address() { + let fdt = Fdt::new(TEST.as_slice()).unwrap(); + let root = fdt.root(); + assert!(root.find_node("/memory@80000000").is_some(), "couldn't find cpus node"); + assert!(root.find_node("/memory@80000001").is_none(), "didn't use unit address to filter!"); +} + // #[test] // fn correct_flash_regions() { // let fdt = Fdt::new(TEST.as_slice()).unwrap(); From de784fa7f3f39e65b684c419971cd63cb0ffaceb Mon Sep 17 00:00:00 2001 From: repnop Date: Tue, 25 Jun 2024 00:11:01 -0400 Subject: [PATCH 04/38] AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA2 --- src/nodes.rs | 43 ++++++++++------- src/parsing.rs | 74 ++---------------------------- src/properties.rs | 1 + src/standard_nodes.rs | 104 +++++++++++++++++++++--------------------- src/tests.rs | 78 ++++++++++++++++++++++++++----- 5 files changed, 151 insertions(+), 149 deletions(-) diff --git a/src/nodes.rs b/src/nodes.rs index f3b8c3a..8a832e3 100644 --- a/src/nodes.rs +++ b/src/nodes.rs @@ -37,7 +37,7 @@ impl<'a> IntoSearchableNodeName<'a> for NodeName<'a> { impl crate::sealed::Sealed for &'_ str {} impl<'a> IntoSearchableNodeName<'a> for &'a str { fn into_searchable_node_name(self) -> SearchableNodeName<'a> { - match self.split_once('@') { + match self.rsplit_once('@') { Some((base, unit_address)) => SearchableNodeName::WithUnitAddress(NodeName { name: base, unit_address: Some(unit_address), @@ -76,13 +76,24 @@ pub struct Node<'a, P: ParserWithMode<'a>> { } impl<'a, P: ParserWithMode<'a>> Node<'a, P> { - #[inline] - pub(crate) fn new( - this: &'a RawNode<

>::Granularity>, - parent: Option<&'a RawNode<

>::Granularity>>, - strings: StringsBlock<'a>, - ) -> Self { - Self { this, parent, strings, _mode: core::marker::PhantomData } + #[inline(always)] + pub(crate) fn fallible(self) -> Node<'a, (P::Parser, NoPanic)> { + Node { + this: self.this, + parent: self.parent, + strings: self.strings, + _mode: core::marker::PhantomData, + } + } + + #[inline(always)] + pub(crate) fn alt>(self) -> Node<'a, P2> { + Node { + this: self.this, + parent: self.parent, + strings: self.strings, + _mode: core::marker::PhantomData, + } } #[inline] @@ -123,13 +134,9 @@ impl<'a, P: ParserWithMode<'a>> Node<'a, P> { }) } + #[track_caller] pub fn property>(&self) -> P::Output> { - P::to_output(Prop::parse(Node { - this: RawNode::new(self.this.as_slice()), - strings: self.strings, - parent: self.parent.map(|r| RawNode::new(r.as_slice())), - _mode: core::marker::PhantomData::<*mut (P::Parser, NoPanic)>, - })) + P::to_output(Prop::parse(self.fallible())) } #[inline] @@ -209,8 +216,12 @@ impl<'a, P: ParserWithMode<'a>> NodeProperties<'a, P> { match parser.peek_token() { Ok(BigEndianToken::PROP) => {} - Ok(BigEndianToken::BEGIN_NODE) => return P::to_output(Ok(None)), - Ok(_) => return P::to_output(Err(ParseError::UnexpectedToken.into())), + Ok(BigEndianToken::BEGIN_NODE) | Ok(BigEndianToken::END_NODE) => { + return P::to_output(Ok(None)) + } + Ok(_) => { + return P::to_output(Err(ParseError::UnexpectedToken.into())); + } Err(FdtError::ParseError(ParseError::UnexpectedEndOfData)) => { return P::to_output(Ok(None)) } diff --git a/src/parsing.rs b/src/parsing.rs index 2265d41..3dc1392 100644 --- a/src/parsing.rs +++ b/src/parsing.rs @@ -9,65 +9,6 @@ use crate::{ nodes::{Node, RawNode}, FdtError, FdtHeader, }; - -#[derive(Debug, Clone, Copy)] -pub struct FdtData<'a> { - bytes: &'a [u8], -} - -impl<'a> FdtData<'a> { - pub fn new(bytes: &'a [u8]) -> Self { - Self { bytes } - } - - pub fn u32(&mut self) -> Option { - let ret = BigEndianU32::from_bytes(self.bytes)?; - self.skip(4); - - Some(ret) - } - - pub fn u64(&mut self) -> Option { - let ret = BigEndianU64::from_bytes(self.bytes)?; - self.skip(8); - - Some(ret) - } - - pub fn skip(&mut self, n_bytes: usize) { - self.bytes = self.bytes.get(n_bytes..).unwrap_or_default() - } - - pub fn remaining(&self) -> &'a [u8] { - self.bytes - } - - pub fn peek_u32(&self) -> Option { - Self::new(self.remaining()).u32() - } - - pub fn is_empty(&self) -> bool { - self.remaining().is_empty() - } - - pub fn skip_nops(&mut self) { - while let Some(4) = self.peek_u32().map(|n| n.to_ne()) { - let _ = self.u32(); - } - } - - pub fn take(&mut self, bytes: usize) -> Option<&'a [u8]> { - if self.bytes.len() >= bytes { - let ret = &self.bytes[..bytes]; - self.skip(bytes); - - return Some(ret); - } - - None - } -} - #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] #[repr(transparent)] pub struct BigEndianU32(u32); @@ -92,10 +33,6 @@ impl BigEndianU32 { pub const fn to_be(self) -> u32 { self.0 } - - pub(crate) fn from_bytes(bytes: &[u8]) -> Option { - Some(BigEndianU32(u32::from_ne_bytes(bytes.get(..4)?.try_into().unwrap()))) - } } #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] @@ -122,15 +59,11 @@ impl BigEndianU64 { pub const fn to_be(self) -> u64 { self.0 } - - pub(crate) fn from_bytes(bytes: &[u8]) -> Option { - Some(BigEndianU64(u64::from_ne_bytes(bytes.get(..8)?.try_into().unwrap()))) - } } #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] #[repr(transparent)] -pub struct BigEndianToken(BigEndianU32); +pub struct BigEndianToken(pub(crate) BigEndianU32); impl BigEndianToken { pub const BEGIN_NODE: Self = Self(BigEndianU32::from_ne(1)); @@ -150,7 +83,7 @@ impl<'a, T: Copy> Stream<'a, T> { #[inline(always)] pub(crate) fn advance(&mut self) -> Option { - let ret = self.0[0]; + let ret = *self.0.get(0)?; self.0 = self.0.get(1..)?; Some(ret) } @@ -279,7 +212,7 @@ impl PanicMode for Panic { pub trait ParserWithMode<'a>: Parser<'a> + PanicMode + crate::sealed::Sealed { type Parser: Parser<'a, Granularity = Self::Granularity>; - type Mode: PanicMode; + type Mode: PanicMode + Clone + Default; fn into_parts( self, @@ -331,6 +264,7 @@ impl<'a, T: Parser<'a>, U: PanicMode + Clone + Default> Parser<'a> for (T, U) { impl<'a, P: Parser<'a>, U: PanicMode> PanicMode for (P, U) { type Output = U::Output; + #[track_caller] fn to_output(result: Result) -> Self::Output { U::to_output(result) } diff --git a/src/properties.rs b/src/properties.rs index 5d16288..08254a9 100644 --- a/src/properties.rs +++ b/src/properties.rs @@ -8,6 +8,7 @@ pub trait Property<'a>: Sized { fn parse>(node: Node<'a, (P, NoPanic)>) -> Result, FdtError>; } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct CellSizes { pub address_cells: usize, pub size_cells: usize, diff --git a/src/standard_nodes.rs b/src/standard_nodes.rs index 30a5874..c42ace5 100644 --- a/src/standard_nodes.rs +++ b/src/standard_nodes.rs @@ -2,12 +2,10 @@ // v. 2.0. If a copy of the MPL was not distributed with this file, You can // obtain one at https://mozilla.org/MPL/2.0/. -use core::ops::ControlFlow; - use crate::{ - nodes::{IntoSearchableNodeName, Node, NodeName, RawNode, SearchableNodeName}, + nodes::{IntoSearchableNodeName, Node, RawNode, SearchableNodeName}, parsing::{ - aligned::AlignedParser, BigEndianToken, BigEndianU32, BigEndianU64, FdtData, Panic, + aligned::AlignedParser, BigEndianToken, BigEndianU32, BigEndianU64, NoPanic, Panic, PanicMode, ParseError, Parser, ParserWithMode, }, properties::CellSizes, @@ -24,7 +22,8 @@ impl<'a, P: ParserWithMode<'a>> Chosen<'a, P> { #[track_caller] pub fn bootargs(self) -> P::Output> { P::to_output(crate::tryblock! { - for prop in P::to_result(self.node.properties())?.into_iter().map(|r| P::to_result(r)) { + let node = self.node.fallible(); + for prop in node.properties()?.into_iter() { if let Ok(prop) = prop { if prop.name() == "bootargs" { return Ok(Some( @@ -149,58 +148,57 @@ impl<'a, P: ParserWithMode<'a>> Root<'a, P> { // self.node.properties().find(|p| p.name == name) // } - pub fn find_node<'b, N: IntoSearchableNodeName<'b>>( - self, - name: N, - ) -> P::Output>> + #[track_caller] + pub fn find_node(self, path: &str) -> P::Output>> where P: 'a, { - let name = name.into_searchable_node_name(); - - if let SearchableNodeName::Base("/") = name { + if path == "/" { return P::to_output(Ok(Some(self.node))); } - let (name_str, unit_address) = match name { - SearchableNodeName::Base(s) => (s, None), - SearchableNodeName::WithUnitAddress(NodeName { name, unit_address }) => { - (name, unit_address) - } - }; + let fallible_self = Root { node: self.node.fallible() }; - let mut children_iter = match P::to_result(self.node.children()) { + let mut current_depth = 1; + let mut all_nodes = match fallible_self.all_nodes() { Ok(iter) => iter, Err(e) => return P::to_output(Err(e)), }; - let mut name_components = name_str.trim_start_matches('/').split('/').peekable(); - loop { - if name_components.peek().is_none() { - return P::to_output(Ok(None)); - } + let mut found_node = None; + 'outer: for component in path.trim_start_matches('/').split('/') { + let component_name = IntoSearchableNodeName::into_searchable_node_name(component); - let component = name_components.next().unwrap(); - let is_last_component = name_components.peek().is_none(); - let look_for_name = match is_last_component { - true => NodeName { name: component, unit_address }, - false => NodeName { name: component, unit_address: None }, - }; - - match P::to_result(children_iter.find(look_for_name)) { - Ok(Some(node)) => match is_last_component { - true => return P::to_output(Ok(Some(node))), - false => { - children_iter = match P::to_result(node.children()) { - Ok(iter) => iter, - Err(e) => return P::to_output(Err(e)), - } - } - }, - Ok(None) => return P::to_output(Ok(None)), - Err(e) => return P::to_output(Err(e)), + loop { + let (depth, next_node) = match all_nodes.next() { + Some(Ok(next)) => next, + Some(Err(e)) => return P::to_output(Err(e)), + None => return P::to_output(Ok(None)), + }; + + if depth < current_depth { + return P::to_output(Ok(None)); + } + + let name = match next_node.name() { + Ok(name) => name, + Err(e) => return P::to_output(Err(e)), + }; + + let name_eq = match component_name { + SearchableNodeName::Base(cname) => cname == name.name, + SearchableNodeName::WithUnitAddress(cname) => cname == name, + }; + + if name_eq { + found_node = Some(next_node); + current_depth = depth; + continue 'outer; + } } } + + P::to_output(Ok(found_node.map(|n| n.alt::

()))) } pub fn all_nodes(self) -> P::Output> { @@ -251,11 +249,16 @@ impl<'a, P: ParserWithMode<'a>> Iterator for AllNodesIterator<'a, P> { while let Ok(BigEndianToken::END_NODE) = self.parser.peek_token() { let _ = self.parser.advance_token(); self.parent_index = self.parent_index.saturating_sub(1); + #[cfg(test)] + std::println!("subtracting parent index: {}", self.parent_index); } match self.parser.advance_token() { - Ok(BigEndianToken::BEGIN_NODE) => self.parent_index += 1, - Ok(BigEndianToken::END_NODE) => {} + Ok(BigEndianToken::BEGIN_NODE) => { + self.parent_index += 1; + #[cfg(test)] + std::println!("incrementing parent index: {}", self.parent_index); + } Ok(BigEndianToken::END) | Err(FdtError::ParseError(ParseError::UnexpectedEndOfData)) => return None, Ok(_) => { @@ -266,7 +269,10 @@ impl<'a, P: ParserWithMode<'a>> Iterator for AllNodesIterator<'a, P> { let starting_data = self.parser.data(); - match self.parents.get_mut(self.parent_index) { + #[cfg(test)] + std::println!("for: {}", self.parser.clone().advance_cstr().unwrap().to_str().unwrap()); + + match self.parents.get_mut(self.parent_index.saturating_sub(1)) { Some(idx) => *idx = starting_data, // FIXME: what makes sense for this to return? None => return None, @@ -277,11 +283,7 @@ impl<'a, P: ParserWithMode<'a>> Iterator for AllNodesIterator<'a, P> { self.parent_index, Node { this: RawNode::new(starting_data), - parent: self - .parent_index - .checked_sub(1) - .and_then(|idx| self.parents.get(idx)) - .map(|parent| RawNode::new(*parent)), + parent: self.parents.get(self.parent_index).map(|parent| RawNode::new(*parent)), strings: self.parser.strings(), _mode: core::marker::PhantomData, }, diff --git a/src/tests.rs b/src/tests.rs index 2ab5cca..37213df 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -4,6 +4,9 @@ extern crate std; +use nodes::NodeName; +use properties::CellSizes; + // use crate::{node::RawReg, *}; use crate::*; @@ -79,7 +82,18 @@ fn all_nodes() { 2 uart@10000000 2 poweroff 2 reboot -2 test@100000" +2 test@100000 +2 pci@30000000 +2 virtio_mmio@10008000 +2 virtio_mmio@10007000 +2 virtio_mmio@10006000 +2 virtio_mmio@10005000 +2 virtio_mmio@10004000 +2 virtio_mmio@10003000 +2 virtio_mmio@10002000 +2 virtio_mmio@10001000 +2 plic@c000000 +2 clint@2000000" ); } @@ -118,17 +132,54 @@ fn finds_root_node_properties() { fn finds_child_of_root_node() { let fdt = Fdt::new(TEST.as_slice()).unwrap(); let root = fdt.root(); - assert!(root.find_node("/cpus").is_some(), "couldn't find cpus node"); + assert_eq!( + root.find_node("/cpus").unwrap().name(), + NodeName { name: "cpus", unit_address: None }, + "couldn't find cpus node" + ); + + assert_eq!( + root.find_node("/cpus/cpu@0/interrupt-controller").unwrap().name(), + NodeName { name: "interrupt-controller", unit_address: None }, + "couldn't find interrupt-controller node" + ); + + assert!( + root.find_node("/cpus/cpu@1/interrupt-controller").is_none(), + "couldn't find interrupt-controller node" + ); } #[test] fn finds_child_with_unit_address() { let fdt = Fdt::new(TEST.as_slice()).unwrap(); let root = fdt.root(); - assert!(root.find_node("/memory@80000000").is_some(), "couldn't find cpus node"); + assert_eq!( + root.find_node("/memory@80000000").unwrap().name(), + NodeName { name: "memory", unit_address: Some("80000000") }, + "couldn't find cpus node" + ); assert!(root.find_node("/memory@80000001").is_none(), "didn't use unit address to filter!"); } +#[test] +fn properties() { + let fdt = Fdt::new(TEST.as_slice()).unwrap(); + let test = fdt.root().find_node("/soc/test").unwrap(); + + let props = + test.properties().into_iter().map(|p| (p.name(), p.value())).collect::>(); + + assert_eq!( + props, + &[ + ("phandle", &[0, 0, 0, 4][..]), + ("reg", &[0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0]), + ("compatible", b"sifive,test1\0sifive,test0\0syscon\0"), + ] + ); +} + // #[test] // fn correct_flash_regions() { // let fdt = Fdt::new(TEST.as_slice()).unwrap(); @@ -195,16 +246,19 @@ fn finds_child_with_unit_address() { // assert!(res); // } -// #[test] -// fn parent_cell_sizes() { -// let fdt = Fdt::new(TEST.as_slice()).unwrap(); -// let regions = fdt.find_node("/memory").unwrap().reg().unwrap().collect::>(); +#[test] +fn cell_sizes() { + let fdt = Fdt::new(TEST.as_slice()).unwrap(); -// assert_eq!( -// regions, -// &[MemoryRegion { starting_address: 0x80000000 as *const u8, size: Some(0x20000000) }] -// ); -// } + let cpu_cs = fdt.root().find_node("/cpus").unwrap().property::().unwrap(); + assert_eq!(cpu_cs, CellSizes { address_cells: 1, size_cells: 0 }); + + let soc_sc = fdt.root().find_node("/soc").unwrap().property::().unwrap(); + let test_cs = fdt.root().find_node("/soc/test").unwrap().property::().unwrap(); + let pci_cs = fdt.root().find_node("/soc/pci").unwrap().property::().unwrap(); + assert_eq!(test_cs, soc_sc); + assert_ne!(pci_cs, soc_sc); +} // #[test] // fn no_properties() { From f1b512d02be064450da2c4cb0f18e215f6828d5c Mon Sep 17 00:00:00 2001 From: repnop Date: Tue, 25 Jun 2024 23:03:20 -0400 Subject: [PATCH 05/38] suffering --- src/lib.rs | 17 +-- src/nodes.rs | 103 +++++++++++---- src/parsing.rs | 6 +- src/properties.rs | 16 ++- src/standard_nodes.rs | 288 +++++++++++++++++++++++++----------------- src/tests.rs | 2 +- 6 files changed, 279 insertions(+), 153 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 1e19f57..cbaa9e1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -86,6 +86,7 @@ pub enum FdtError { ParseError(ParseError), MissingRequiredNode(&'static str), MissingRequiredProperty(&'static str), + InvalidPropertyValue, } impl From for FdtError { @@ -106,6 +107,7 @@ impl core::fmt::Display for FdtError { FdtError::MissingRequiredProperty(name) => { write!(f, "FDT node is missing a required property `{}`", name) } + FdtError::InvalidPropertyValue => write!(f, "FDT property value is invalid"), } } } @@ -137,7 +139,7 @@ impl<'a, P: ParserWithMode<'a>> core::fmt::Debug for Fdt<'a, P> { #[derive(Debug, Clone, Copy)] #[repr(C)] -struct FdtHeader { +pub struct FdtHeader { /// FDT header magic pub magic: u32, /// Total size in bytes of the FDT structure @@ -514,20 +516,15 @@ impl<'a, P: ParserWithMode<'a>> Fdt<'a, P> { self.header.total_size as usize } - fn cstr_at_offset(&self, offset: usize) -> &'a core::ffi::CStr { - core::ffi::CStr::from_bytes_until_nul(&self.strings_block()[offset..]) - .expect("no null terminating string on C str?") + pub fn header(&self) -> &FdtHeader { + &self.header } - fn str_at_offset(&self, offset: usize) -> &'a str { - self.cstr_at_offset(offset).to_str().expect("not utf-8 cstr") - } - - fn strings_block(&self) -> &'a [u8] { + pub fn strings_block(&self) -> &'a [u8] { self.strings.0 } - fn structs_block(&self) -> &'a [P::Granularity] { + pub fn structs_block(&self) -> &'a [P::Granularity] { self.structs } } diff --git a/src/nodes.rs b/src/nodes.rs index 8a832e3..4300217 100644 --- a/src/nodes.rs +++ b/src/nodes.rs @@ -1,7 +1,9 @@ +use core::ffi::CStr; + use crate::{ parsing::{ - aligned::AlignedParser, BigEndianToken, NoPanic, Panic, PanicMode, ParseError, Parser, - ParserWithMode, StringsBlock, + aligned::AlignedParser, BigEndianToken, BigEndianU32, NoPanic, Panic, PanicMode, + ParseError, Parser, ParserWithMode, StringsBlock, }, properties::Property, FdtError, @@ -186,7 +188,7 @@ impl<'a, P: ParserWithMode<'a>> Clone for Node<'a, P> { impl<'a, P: ParserWithMode<'a>> Copy for Node<'a, P> {} #[repr(transparent)] -pub(crate) struct RawNode([Granularity]); +pub struct RawNode([Granularity]); impl RawNode { pub(crate) fn new(data: &[Granularity]) -> &Self { @@ -225,7 +227,7 @@ impl<'a, P: ParserWithMode<'a>> NodeProperties<'a, P> { Err(FdtError::ParseError(ParseError::UnexpectedEndOfData)) => { return P::to_output(Ok(None)) } - Err(e) => return P::to_output(Err(e.into())), + Err(e) => return P::to_output(Err(e)), } P::to_output(tryblock! { @@ -242,8 +244,19 @@ impl<'a, P: ParserWithMode<'a>> NodeProperties<'a, P> { } pub fn find(&self, name: &str) -> P::Output>> { - P::reverse_transpose( - self.iter().find(|p| P::ok_as_ref(p).map(|p| p.name == name).unwrap_or_default()), + let this: NodeProperties<'a, (P::Parser, NoPanic)> = NodeProperties { + data: self.data, + strings: self.strings, + _mode: core::marker::PhantomData, + }; + + P::to_output( + this.iter() + .find_map(|p| match p { + Err(e) => Some(Err(e)), + Ok(p) => (p.name == name).then_some(Ok(p)), + }) + .transpose(), ) } } @@ -279,19 +292,50 @@ impl<'a, P: ParserWithMode<'a>> Iterator for NodePropertiesIter<'a, P> { } } +pub struct InvalidPropertyValue; + +impl From for FdtError { + fn from(_: InvalidPropertyValue) -> Self { + FdtError::InvalidPropertyValue + } +} + pub trait Value<'a>: Sized { - fn parse(value: &'a [u8]) -> Option; + fn parse(value: &'a [u8]) -> Result; } impl<'a> Value<'a> for u32 { - fn parse(value: &'a [u8]) -> Option { + fn parse(value: &'a [u8]) -> Result { + match value { + [a, b, c, d] => Ok(u32::from_be_bytes([*a, *b, *c, *d])), + _ => Err(InvalidPropertyValue), + } + } +} + +impl<'a> Value<'a> for BigEndianU32 { + fn parse(value: &'a [u8]) -> Result { match value { - [a, b, c, d, ..] => Some(u32::from_be_bytes([*a, *b, *c, *d])), - _ => None, + [a, b, c, d] => Ok(BigEndianU32::from_be(u32::from_ne_bytes([*a, *b, *c, *d]))), + _ => Err(InvalidPropertyValue), } } } +impl<'a> Value<'a> for &'a CStr { + fn parse(value: &'a [u8]) -> Result { + CStr::from_bytes_until_nul(value).map_err(|_| InvalidPropertyValue) + } +} + +impl<'a> Value<'a> for &'a str { + fn parse(value: &'a [u8]) -> Result { + core::str::from_utf8(value) + .map(|s| s.trim_end_matches('\0')) + .map_err(|_| InvalidPropertyValue) + } +} + pub struct NodeProperty<'a> { name: &'a str, value: &'a [u8], @@ -310,7 +354,7 @@ impl<'a> NodeProperty<'a> { self.value } - pub fn to>(&self) -> Option { + pub fn to>(&self) -> Result { V::parse(self.value) } } @@ -337,7 +381,7 @@ impl<'a, P: ParserWithMode<'a>> NodeChildren<'a, P> { Err(FdtError::ParseError(ParseError::UnexpectedEndOfData)) => { return P::to_output(Ok(None)) } - Err(e) => return P::to_output(Err(e.into())), + Err(e) => return P::to_output(Err(e)), } P::to_output(match parser.parse_node(Some(self.parent)) { @@ -347,7 +391,7 @@ impl<'a, P: ParserWithMode<'a>> NodeChildren<'a, P> { Ok(Some(node)) } Err(FdtError::ParseError(ParseError::UnexpectedEndOfData)) => Ok(None), - Err(e) => Err(e.into()), + Err(e) => Err(e), }) } @@ -356,22 +400,37 @@ impl<'a, P: ParserWithMode<'a>> NodeChildren<'a, P> { N: IntoSearchableNodeName<'n>, P: 'a, { + let this: NodeChildren<'a, (P::Parser, NoPanic)> = NodeChildren { + data: self.data, + parent: self.parent, + strings: self.strings, + _mode: core::marker::PhantomData, + }; + let name = name.into_searchable_node_name(); - P::reverse_transpose(self.iter().find(|n| { - P::ok_as_ref(n) - .and_then(|n| P::ok(n.name())) - .map(|n| match name { - SearchableNodeName::Base(base) => n.name == base, - SearchableNodeName::WithUnitAddress(nn) => n == nn, + P::to_output( + this.iter() + .find_map(|n| match n { + Err(e) => Some(Err(e)), + Ok(node) => match node.name() { + Err(e) => Some(Err(e)), + Ok(nn) => match name { + SearchableNodeName::Base(base) => (nn.name == base).then_some(Ok(node)), + SearchableNodeName::WithUnitAddress(snn) => { + (nn == snn).then_some(Ok(node)) + } + }, + }, }) - .unwrap_or_default() - })) + .map(|n| n.map(Node::alt)) + .transpose(), + ) } } impl<'a, P: ParserWithMode<'a>> Clone for NodeChildren<'a, P> { fn clone(&self) -> Self { - Self { _mode: self._mode, data: self.data, strings: self.strings, parent: self.parent } + *self } } diff --git a/src/parsing.rs b/src/parsing.rs index 3dc1392..ed48f62 100644 --- a/src/parsing.rs +++ b/src/parsing.rs @@ -9,7 +9,7 @@ use crate::{ nodes::{Node, RawNode}, FdtError, FdtHeader, }; -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(transparent)] pub struct BigEndianU32(u32); @@ -83,7 +83,7 @@ impl<'a, T: Copy> Stream<'a, T> { #[inline(always)] pub(crate) fn advance(&mut self) -> Option { - let ret = *self.0.get(0)?; + let ret = *self.0.first()?; self.0 = self.0.get(1..)?; Some(ret) } @@ -465,7 +465,7 @@ pub trait Parser<'a>: crate::sealed::Sealed + Clone { strings: self.strings(), _mode: core::marker::PhantomData, }), - _ => return Err(FdtError::ParseError(ParseError::UnexpectedToken)), + _ => Err(FdtError::ParseError(ParseError::UnexpectedToken)), } } diff --git a/src/properties.rs b/src/properties.rs index 08254a9..8ea1fec 100644 --- a/src/properties.rs +++ b/src/properties.rs @@ -1,6 +1,6 @@ use crate::{ nodes::Node, - parsing::{unaligned::UnalignedParser, NoPanic, Parser}, + parsing::{unaligned::UnalignedParser, BigEndianU32, NoPanic, Parser}, FdtError, }; @@ -44,3 +44,17 @@ impl<'a> Property<'a> for CellSizes { })) } } + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct PHandle(BigEndianU32); + +impl<'a> Property<'a> for PHandle { + #[track_caller] + fn parse>(node: Node<'a, (P, NoPanic)>) -> Result, FdtError> { + let Some(phandle) = node.properties()?.find("phandle")? else { + return Ok(None); + }; + + Ok(Some(PHandle(phandle.to()?))) + } +} diff --git a/src/standard_nodes.rs b/src/standard_nodes.rs index c42ace5..c62a6bb 100644 --- a/src/standard_nodes.rs +++ b/src/standard_nodes.rs @@ -4,12 +4,9 @@ use crate::{ nodes::{IntoSearchableNodeName, Node, RawNode, SearchableNodeName}, - parsing::{ - aligned::AlignedParser, BigEndianToken, BigEndianU32, BigEndianU64, NoPanic, Panic, - PanicMode, ParseError, Parser, ParserWithMode, - }, + parsing::{aligned::AlignedParser, BigEndianToken, Panic, ParseError, Parser, ParserWithMode}, properties::CellSizes, - tryblock, Fdt, FdtError, + tryblock, FdtError, }; /// Represents the `/chosen` node with specific helper methods @@ -17,20 +14,18 @@ pub struct Chosen<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { pub(crate) node: Node<'a, P>, } -impl<'a, P: ParserWithMode<'a>> Chosen<'a, P> { +impl<'a, P: ParserWithMode<'a> + 'a> Chosen<'a, P> { /// Contains the bootargs, if they exist #[track_caller] pub fn bootargs(self) -> P::Output> { P::to_output(crate::tryblock! { let node = self.node.fallible(); - for prop in node.properties()?.into_iter() { - if let Ok(prop) = prop { - if prop.name() == "bootargs" { - return Ok(Some( - core::str::from_utf8(&prop.value()[..prop.value().len() - 1]) - .map_err(|_| FdtError::ParseError(ParseError::InvalidCStrValue))?, - )); - } + for prop in node.properties()?.into_iter().flatten() { + if prop.name() == "bootargs" { + return Ok(Some( + core::str::from_utf8(&prop.value()[..prop.value().len() - 1]) + .map_err(|_| FdtError::ParseError(ParseError::InvalidCStrValue))?, + )); } } @@ -38,43 +33,60 @@ impl<'a, P: ParserWithMode<'a>> Chosen<'a, P> { }) } - /// Searches for the node representing `stdout`, if the property exists, - /// attempting to resolve aliases if the node name doesn't exist as-is - // pub fn stdout(self) -> Mode::Output>> { - // P::to_output(crate::tryblock! { - // Ok( - // P::to_result(self.node.properties())? - // .find(|n| n.name == "stdout-path") - // .and_then(|n| core::str::from_utf8(&n.value()[..n.value().len() - 1]).ok()) - // .map(Self::split_stdinout_property) - // .and_then(|(name, params)| { - // self.node..find_node(name).map(|node| StdInOutPath::new(node, params)) - // }) - // ) - // }) - // } + /// Looks up the `stdout-path` property and returns the [`StdInOutPath`] + /// representing the path. The path may be an alias and require being + /// resolved with [`Alias::resolve`] before being used in conjunction with + /// [`Root::find_node`]. For more information about the path parameters, see + /// [`StdInOutPath::params`]. + #[track_caller] + pub fn stdout(self) -> P::Output>> { + P::to_output(crate::tryblock! { + let node = self.node.fallible(); + node.properties()?.into_iter().find_map(|n| match n { + Err(e) => Some(Err(e)), + Ok(property) => match property.name() == "stdout-path" { + false => None, + true => Some( + property + .to::<&'a str>() + .map_err(Into::into) + .map(|s| { + let (path, params) = Self::split_stdinout_property(s); + StdInOutPath { path, params } + }) + ), + }, + }).transpose() + }) + } - /// Searches for the node representing `stdout`, if the property exists, - /// attempting to resolve aliases if the node name doesn't exist as-is. If - /// no `stdin` property exists, but `stdout` is present, it will return the - /// node specified by the `stdout` property. - // pub fn stdin(self) -> Option> { - // self.node - // .properties() - // .find(|n| n.name == "stdin-path") - // .and_then(|n| core::str::from_utf8(&n.value[..n.value.len() - 1]).ok()) - // .map(Self::split_stdinout_property) - // .and_then(|(name, params)| { - // self.node.header.find_node(name).map(|node| StdInOutPath::new(node, params)) - // }) - // .or_else(|| self.stdout()) - // } + /// Looks up the `stdin-path` property and returns the [`StdInOutPath`] + /// representing the path. The path may be an alias and require being + /// resolved with [`Alias::resolve`] before being used in conjunction with + /// [`Root::find_node`]. For more information about the path parameters, see + /// [`StdInOutPath::params`]. + #[track_caller] + pub fn stdin(self) -> P::Output>> { + P::to_output(crate::tryblock! { + let node = self.node.fallible(); + node.properties()?.into_iter().find_map(|n| match n { + Err(e) => Some(Err(e)), + Ok(property) => match property.name() == "stdin-path" { + false => None, + true => Some( + property + .to::<&'a str>() + .map_err(Into::into) + .map(|s| { + let (path, params) = Self::split_stdinout_property(s); + StdInOutPath { path, params } + }) + ), + }, + }).transpose() + }) + } - /// Splits a stdout-path or stdin-path property into its node path and optional parameters which are seperated by a colon ':'. - /// see https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#chosen-node - /// example "/soc/uart@10000000" => ("/soc/uart@10000000", None) - /// example "/soc/uart@10000000:115200" => ("/soc/uart@10000000", Some("115200")) - /// example "/soc/uart@10000000:115200n8r" => ("/soc/uart@10000000", Some("115200n8r")) fn split_stdinout_property(property: &str) -> (&str, Option<&str>) { property .split_once(':') @@ -84,34 +96,51 @@ impl<'a, P: ParserWithMode<'a>> Chosen<'a, P> { impl<'a, P: ParserWithMode<'a>> Clone for Chosen<'a, P> { fn clone(&self) -> Self { - Self { node: self.node } + *self } } impl<'a, P: ParserWithMode<'a>> Copy for Chosen<'a, P> {} -pub struct StdInOutPath<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { - pub(crate) node: Node<'a, P>, - pub(crate) params: Option<&'a str>, +pub struct StdInOutPath<'a> { + path: &'a str, + params: Option<&'a str>, } -// impl<'b, 'a> StdInOutPath<'b, 'a> { -// fn new(node: FdtNode<'b, 'a>, params: Option<&'a str>) -> Self { -// Self { node, params } -// } - -// pub fn name(&self) -> &'a str { -// self.node.name -// } - -// pub fn node(&self) -> FdtNode<'b, 'a> { -// self.node -// } +impl<'a> StdInOutPath<'a> { + /// Path to the node representing the stdin/stdout device. This node path + /// may be an alias, which can be resolved with [`Aliases::resolve`]. To be + /// used in conjunction with [`Root::find_node`]. + pub fn path(&self) -> &'a str { + self.path + } -// pub fn params(&self) -> Option<&'a str> { -// self.params -// } -// } + /// Optional parameters specified by the stdin/stdout property value. See + /// https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#chosen-node + /// + /// Example: + /// ``` + /// / { + /// chosen { + /// stdout-path = "/soc/uart@10000000:115200"; + /// stdin-path = "/soc/uart@10000000"; + /// } + /// } + /// ``` + /// + /// ```rust,norun + /// # let fdt = Fdt::new_unaligned(include_bytes!("./dtb/test.dtb")).unwrap(); + /// # let chosen = fdt.root().chosen(); + /// let stdout = chosen.stdout().unwrap(); + /// let stdin = chosen.stdin().unwrap(); + /// + /// assert_eq!((stdout.path(), stdout.params()), ("soc/uart@10000000", None)); + /// assert_eq!((stdin.path(), stdin.params()), ("soc/uart@10000000", Some("115200"))); + /// ``` + pub fn params(&self) -> Option<&'a str> { + self.params + } +} /// Represents the root (`/`) node with specific helper methods pub struct Root<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { @@ -120,23 +149,70 @@ pub struct Root<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { impl<'a, P: ParserWithMode<'a>> Root<'a, P> { /// Root node cell sizes + #[track_caller] pub fn cell_sizes(self) -> P::Output { P::transpose(self.node.property::()).unwrap() } - /// `model` property - // pub fn model(self) -> &'a str { - // self.node - // .properties() - // .find(|p| p.name == "model") - // .and_then(|p| core::str::from_utf8(p.value).map(|s| s.trim_end_matches('\0')).ok()) - // .unwrap() - // } + /// [Devicetree 3.2. Root + /// node](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#root-node) + /// + /// Specifies a string that uniquely identifies the model of the system + /// board. The recommended format is "manufacturer,model-number". + #[track_caller] + pub fn model(self) -> P::Output<&'a str> { + P::to_output(crate::tryblock! { + let node = self.node.fallible(); + node + .properties()? + .into_iter() + .find_map(|n| match n { + Err(e) => Some(Err(e)), + Ok(property) => match property.name() == "model" { + false => None, + true => Some( + property + .to::<&'a str>() + .map_err(Into::into) + ), + }, + }) + .transpose()? + .ok_or(FdtError::MissingRequiredProperty("model")) + }) + } - /// `compatible` property - // pub fn compatible(self) -> Compatible<'a> { - // self.node.compatible().unwrap() - // } + /// [Devicetree 3.2. Root + /// node](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#root-node) + /// + /// Specifies a list of platform architectures with which this platform is + /// compatible. This property can be used by operating systems in selecting + /// platform specific code. The recommended form of the property value is: + /// `"manufacturer,model"` + /// + /// For example: `compatible = "fsl,mpc8572ds"` + pub fn compatible(self) -> P::Output> { + P::to_output(crate::tryblock! { + let node = self.node.fallible(); + node + .properties()? + .into_iter() + .find_map(|n| match n { + Err(e) => Some(Err(e)), + Ok(property) => match property.name() == "compatible" { + false => None, + true => Some( + property + .to::<&'a str>() + .map(|string| Compatible { string }) + .map_err(Into::into) + ), + }, + }) + .transpose()? + .ok_or(FdtError::MissingRequiredProperty("compatible")) + }) + } /// Returns an iterator over all of the available properties // pub fn properties(self) -> impl Iterator> + 'a { @@ -245,20 +321,13 @@ impl<'a, P: ParserWithMode<'a>> Iterator for AllNodesIterator<'a, P> { #[track_caller] fn next(&mut self) -> Option { - // BUG HERE: needs to properly track the depth with `parent_index` while let Ok(BigEndianToken::END_NODE) = self.parser.peek_token() { let _ = self.parser.advance_token(); self.parent_index = self.parent_index.saturating_sub(1); - #[cfg(test)] - std::println!("subtracting parent index: {}", self.parent_index); } match self.parser.advance_token() { - Ok(BigEndianToken::BEGIN_NODE) => { - self.parent_index += 1; - #[cfg(test)] - std::println!("incrementing parent index: {}", self.parent_index); - } + Ok(BigEndianToken::BEGIN_NODE) => self.parent_index += 1, Ok(BigEndianToken::END) | Err(FdtError::ParseError(ParseError::UnexpectedEndOfData)) => return None, Ok(_) => { @@ -269,21 +338,17 @@ impl<'a, P: ParserWithMode<'a>> Iterator for AllNodesIterator<'a, P> { let starting_data = self.parser.data(); - #[cfg(test)] - std::println!("for: {}", self.parser.clone().advance_cstr().unwrap().to_str().unwrap()); - match self.parents.get_mut(self.parent_index.saturating_sub(1)) { Some(idx) => *idx = starting_data, // FIXME: what makes sense for this to return? None => return None, } - // self.parent_index += 1; let node = Some(P::to_output(Ok(( self.parent_index, Node { this: RawNode::new(starting_data), - parent: self.parents.get(self.parent_index).map(|parent| RawNode::new(*parent)), + parent: self.parents.get(self.parent_index).map(|parent| RawNode::new(parent)), strings: self.parser.strings(), _mode: core::marker::PhantomData, }, @@ -432,38 +497,29 @@ impl<'a, P: ParserWithMode<'a>> Iterator for AllNodesIterator<'a, P> { /// Represents the `compatible` property of a node #[derive(Clone, Copy)] pub struct Compatible<'a> { - pub(crate) data: &'a [u8], + pub(crate) string: &'a str, } impl<'a> Compatible<'a> { /// First compatible string pub fn first(self) -> &'a str { - core::ffi::CStr::from_bytes_until_nul(self.data).expect("expected C str").to_str().unwrap() + self.string.split('\0').next().unwrap_or(self.string) } /// Returns an iterator over all available compatible strings - pub fn all(self) -> impl Iterator { - let mut data = self.data; - core::iter::from_fn(move || { - if data.is_empty() { - return None; - } - - match data.iter().position(|b| *b == b'\0') { - Some(idx) => { - let ret = Some(core::str::from_utf8(&data[..idx]).ok()?); - data = &data[idx + 1..]; + pub fn all(self) -> CompatibleIter<'a> { + CompatibleIter { iter: self.string.split('\0') } + } +} - ret - } - None => { - let ret = Some(core::str::from_utf8(data).ok()?); - data = &[]; +pub struct CompatibleIter<'a> { + iter: core::str::Split<'a, char>, +} - ret - } - } - }) +impl<'a> Iterator for CompatibleIter<'a> { + type Item = &'a str; + fn next(&mut self) -> Option { + self.iter.next() } } diff --git a/src/tests.rs b/src/tests.rs index 37213df..4a4994e 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -25,7 +25,7 @@ impl AlignArrayUp { i += 1; } - return copy; + copy } } From be7bd604d51c07ec625f460ee7279f4c3d7ec389 Mon Sep 17 00:00:00 2001 From: repnop Date: Sun, 30 Jun 2024 21:29:39 -0400 Subject: [PATCH 06/38] pain and suffering --- src/lib.rs | 91 ++++---- src/nodes.rs | 74 ++---- src/parsing.rs | 26 ++- src/parsing/aligned.rs | 23 +- src/parsing/unaligned.rs | 26 ++- src/properties.rs | 470 +++++++++++++++++++++++++++++++++++++-- src/standard_nodes.rs | 74 ++---- src/tests.rs | 5 +- 8 files changed, 610 insertions(+), 179 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index cbaa9e1..bba9d4d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -66,7 +66,7 @@ mod util; use parsing::{ aligned::AlignedParser, unaligned::UnalignedParser, NoPanic, Panic, ParseError, Parser, - ParserWithMode, StringsBlock, + ParserWithMode, StringsBlock, StructsBlock, }; use standard_nodes::Root; // use standard_nodes::{Aliases, Chosen, Cpu, Memory, MemoryRange, MemoryRegion, Root}; @@ -120,9 +120,8 @@ impl core::fmt::Display for FdtError { #[derive(Clone, Copy)] pub struct Fdt<'a, P: ParserWithMode<'a>> { parser: P, - strings: StringsBlock<'a>, - structs: &'a [P::Granularity], header: FdtHeader, + _lifetime: core::marker::PhantomData<&'a [u8]>, } impl<'a, P: ParserWithMode<'a>> core::fmt::Debug for Fdt<'a, P> { @@ -172,11 +171,12 @@ impl FdtHeader { impl<'a> Fdt<'a, (UnalignedParser<'a>, Panic)> { /// Construct a new `Fdt` from a byte buffer pub fn new_unaligned(data: &'a [u8]) -> Result { - let mut parser = UnalignedParser::new(data, &[]); + let mut parser = UnalignedParser::new(data, StringsBlock(&[]), StructsBlock(&[])); let header = parser.parse_header()?; let strings = StringsBlock(&data[header.strings_offset as usize..][..header.strings_size as usize]); - let structs = &data[header.structs_offset as usize..][..header.structs_size as usize]; + let structs = + StructsBlock(&data[header.structs_offset as usize..][..header.structs_size as usize]); if !header.valid_magic() { return Err(FdtError::BadMagic); @@ -186,9 +186,8 @@ impl<'a> Fdt<'a, (UnalignedParser<'a>, Panic)> { Ok(Self { header, - parser: (UnalignedParser::new(data, strings.0), Panic), - strings, - structs, + parser: (UnalignedParser::new(structs.0, strings, structs), Panic), + _lifetime: core::marker::PhantomData, }) } @@ -201,9 +200,12 @@ impl<'a> Fdt<'a, (UnalignedParser<'a>, Panic)> { } let tmp_header = core::slice::from_raw_parts(ptr, core::mem::size_of::()); - let real_size = - usize::try_from(UnalignedParser::new(tmp_header, &[]).parse_header()?.total_size) - .map_err(|_| ParseError::NumericConversionError)?; + let real_size = usize::try_from( + UnalignedParser::new(tmp_header, StringsBlock(&[]), StructsBlock(&[])) + .parse_header()? + .total_size, + ) + .map_err(|_| ParseError::NumericConversionError)?; Self::new_unaligned(core::slice::from_raw_parts(ptr, real_size)) } @@ -212,7 +214,7 @@ impl<'a> Fdt<'a, (UnalignedParser<'a>, Panic)> { impl<'a> Fdt<'a, (AlignedParser<'a>, Panic)> { /// Construct a new `Fdt` from a `u32`-aligned buffer pub fn new(data: &'a [u32]) -> Result { - let mut parser = AlignedParser::new(data, &[]); + let mut parser = AlignedParser::new(data, StringsBlock(&[]), StructsBlock(&[])); let header = parser.parse_header()?; let strings_start = header.strings_offset as usize; @@ -225,9 +227,10 @@ impl<'a> Fdt<'a, (AlignedParser<'a>, Panic)> { let structs_start = header.structs_offset as usize / 4; let structs_end = structs_start + (header.structs_size as usize / 4); - let structs = data - .get(structs_start..structs_end) - .ok_or(FdtError::ParseError(ParseError::UnexpectedEndOfData))?; + let structs = StructsBlock( + data.get(structs_start..structs_end) + .ok_or(FdtError::ParseError(ParseError::UnexpectedEndOfData))?, + ); if !header.valid_magic() { return Err(FdtError::BadMagic); @@ -237,9 +240,8 @@ impl<'a> Fdt<'a, (AlignedParser<'a>, Panic)> { Ok(Self { header, - parser: (AlignedParser::new(structs, strings.0), Panic), - strings, - structs, + parser: (AlignedParser::new(structs.0, strings, structs), Panic), + _lifetime: core::marker::PhantomData, }) } @@ -252,9 +254,12 @@ impl<'a> Fdt<'a, (AlignedParser<'a>, Panic)> { } let tmp_header = core::slice::from_raw_parts(ptr, core::mem::size_of::()); - let real_size = - usize::try_from(AlignedParser::new(tmp_header, &[]).parse_header()?.total_size) - .map_err(|_| ParseError::NumericConversionError)?; + let real_size = usize::try_from( + AlignedParser::new(tmp_header, StringsBlock(&[]), StructsBlock(&[])) + .parse_header()? + .total_size, + ) + .map_err(|_| ParseError::NumericConversionError)?; Self::new(core::slice::from_raw_parts(ptr, real_size)) } @@ -263,12 +268,14 @@ impl<'a> Fdt<'a, (AlignedParser<'a>, Panic)> { impl<'a> Fdt<'a, (UnalignedParser<'a>, NoPanic)> { /// Construct a new `Fdt` from a byte buffer pub fn new_unaligned_fallible(data: &'a [u8]) -> Result { - let Fdt { parser, strings, structs, header, .. } = Fdt::new_unaligned(data)?; + let Fdt { parser, header, .. } = Fdt::new_unaligned(data)?; Ok(Self { - parser: (UnalignedParser::new(parser.data(), strings.0), NoPanic), - strings, - structs, + parser: ( + UnalignedParser::new(parser.data(), parser.strings(), parser.structs()), + NoPanic, + ), header, + _lifetime: core::marker::PhantomData, }) } @@ -276,12 +283,14 @@ impl<'a> Fdt<'a, (UnalignedParser<'a>, NoPanic)> { /// This function performs a read to verify the magic value. If the pointer /// is invalid this can result in undefined behavior. pub unsafe fn from_ptr_unaligned_fallible(ptr: *const u8) -> Result { - let Fdt { parser, strings, structs, header, .. } = Fdt::from_ptr_unaligned(ptr)?; + let Fdt { parser, header, .. } = Fdt::from_ptr_unaligned(ptr)?; Ok(Self { - parser: (UnalignedParser::new(parser.data(), strings.0), NoPanic), - strings, - structs, + parser: ( + UnalignedParser::new(parser.data(), parser.strings(), parser.structs()), + NoPanic, + ), header, + _lifetime: core::marker::PhantomData, }) } } @@ -289,12 +298,14 @@ impl<'a> Fdt<'a, (UnalignedParser<'a>, NoPanic)> { impl<'a> Fdt<'a, (AlignedParser<'a>, NoPanic)> { /// Construct a new `Fdt` from a `u32`-aligned buffer which won't panic on invalid data pub fn new_fallible(data: &'a [u32]) -> Result { - let Fdt { parser, strings, structs, header, .. } = Fdt::new(data)?; + let Fdt { parser, header, .. } = Fdt::new(data)?; Ok(Self { - parser: (AlignedParser::new(parser.data(), strings.0), NoPanic), - strings, - structs, + parser: ( + AlignedParser::new(parser.data(), parser.strings(), parser.structs()), + NoPanic, + ), header, + _lifetime: core::marker::PhantomData, }) } @@ -302,12 +313,14 @@ impl<'a> Fdt<'a, (AlignedParser<'a>, NoPanic)> { /// This function performs a read to verify the magic value. If the pointer /// is invalid this can result in undefined behavior. pub unsafe fn from_ptr_fallible(ptr: *const u32) -> Result { - let Fdt { parser, strings, structs, header, .. } = Fdt::from_ptr(ptr)?; + let Fdt { parser, header, .. } = Fdt::from_ptr(ptr)?; Ok(Self { - parser: (AlignedParser::new(parser.data(), strings.0), NoPanic), - strings, - structs, + parser: ( + AlignedParser::new(parser.data(), parser.strings(), parser.structs()), + NoPanic, + ), header, + _lifetime: core::marker::PhantomData, }) } } @@ -521,10 +534,10 @@ impl<'a, P: ParserWithMode<'a>> Fdt<'a, P> { } pub fn strings_block(&self) -> &'a [u8] { - self.strings.0 + self.parser.strings().0 } pub fn structs_block(&self) -> &'a [P::Granularity] { - self.structs + self.parser.structs().0 } } diff --git a/src/nodes.rs b/src/nodes.rs index 4300217..7c40fd6 100644 --- a/src/nodes.rs +++ b/src/nodes.rs @@ -1,11 +1,9 @@ -use core::ffi::CStr; - use crate::{ parsing::{ - aligned::AlignedParser, BigEndianToken, BigEndianU32, NoPanic, Panic, PanicMode, - ParseError, Parser, ParserWithMode, StringsBlock, + aligned::AlignedParser, BigEndianToken, NoPanic, Panic, PanicMode, ParseError, Parser, + ParserWithMode, StringsBlock, StructsBlock, }, - properties::Property, + properties::{InvalidPropertyValue, Property, PropertyValue}, FdtError, }; @@ -74,6 +72,7 @@ pub struct Node<'a, P: ParserWithMode<'a>> { pub(crate) this: &'a RawNode<

>::Granularity>, pub(crate) parent: Option<&'a RawNode<

>::Granularity>>, pub(crate) strings: StringsBlock<'a>, + pub(crate) structs: StructsBlock<'a,

>::Granularity>, pub(crate) _mode: core::marker::PhantomData<*mut P>, } @@ -84,6 +83,7 @@ impl<'a, P: ParserWithMode<'a>> Node<'a, P> { this: self.this, parent: self.parent, strings: self.strings, + structs: self.structs, _mode: core::marker::PhantomData, } } @@ -94,6 +94,7 @@ impl<'a, P: ParserWithMode<'a>> Node<'a, P> { this: self.this, parent: self.parent, strings: self.strings, + structs: self.structs, _mode: core::marker::PhantomData, } } @@ -101,7 +102,7 @@ impl<'a, P: ParserWithMode<'a>> Node<'a, P> { #[inline] pub fn name(&self) ->

::Output> { P::to_output( - P::new(&self.this.0, self.strings.0) + P::new(&self.this.0, self.strings, self.structs) .advance_cstr() .and_then(|s| { s.to_str().map_err(|_| FdtError::ParseError(ParseError::InvalidCStrValue)) @@ -119,12 +120,13 @@ impl<'a, P: ParserWithMode<'a>> Node<'a, P> { #[inline] pub fn properties(&self) -> P::Output> { - let mut parser = P::new(&self.this.0, self.strings.0); + let mut parser = P::new(&self.this.0, self.strings, self.structs); let res = parser.advance_cstr(); P::to_output(res.map(|_| NodeProperties { data: parser.data(), strings: self.strings, + structs: self.structs, _mode: core::marker::PhantomData, })) } @@ -144,7 +146,7 @@ impl<'a, P: ParserWithMode<'a>> Node<'a, P> { #[inline] pub fn children(&self) -> P::Output> { P::to_output(tryblock! { - let mut parser = P::new(&self.this.0, self.strings.0); + let mut parser = P::new(&self.this.0, self.strings, self.structs); parser.advance_cstr()?; while let BigEndianToken::PROP = parser.peek_token()? { parser.parse_raw_property()?; @@ -154,6 +156,7 @@ impl<'a, P: ParserWithMode<'a>> Node<'a, P> { data: parser.data(), parent: self.this, strings: self.strings, + structs: self.structs, _mode: core::marker::PhantomData, }) }) @@ -165,6 +168,7 @@ impl<'a, P: ParserWithMode<'a>> Node<'a, P> { this: parent, parent: None, strings: self.strings, + structs: self.structs, _mode: core::marker::PhantomData, }) } @@ -205,6 +209,7 @@ impl RawNode { pub struct NodeProperties<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { data: &'a [

>::Granularity], strings: StringsBlock<'a>, + structs: StructsBlock<'a,

>::Granularity>, _mode: core::marker::PhantomData<*mut P>, } @@ -214,7 +219,7 @@ impl<'a, P: ParserWithMode<'a>> NodeProperties<'a, P> { } pub fn advance(&mut self) -> P::Output>> { - let mut parser = P::new(self.data, self.strings.0); + let mut parser = P::new(self.data, self.strings, self.structs); match parser.peek_token() { Ok(BigEndianToken::PROP) => {} @@ -247,6 +252,7 @@ impl<'a, P: ParserWithMode<'a>> NodeProperties<'a, P> { let this: NodeProperties<'a, (P::Parser, NoPanic)> = NodeProperties { data: self.data, strings: self.strings, + structs: self.structs, _mode: core::marker::PhantomData, }; @@ -292,50 +298,6 @@ impl<'a, P: ParserWithMode<'a>> Iterator for NodePropertiesIter<'a, P> { } } -pub struct InvalidPropertyValue; - -impl From for FdtError { - fn from(_: InvalidPropertyValue) -> Self { - FdtError::InvalidPropertyValue - } -} - -pub trait Value<'a>: Sized { - fn parse(value: &'a [u8]) -> Result; -} - -impl<'a> Value<'a> for u32 { - fn parse(value: &'a [u8]) -> Result { - match value { - [a, b, c, d] => Ok(u32::from_be_bytes([*a, *b, *c, *d])), - _ => Err(InvalidPropertyValue), - } - } -} - -impl<'a> Value<'a> for BigEndianU32 { - fn parse(value: &'a [u8]) -> Result { - match value { - [a, b, c, d] => Ok(BigEndianU32::from_be(u32::from_ne_bytes([*a, *b, *c, *d]))), - _ => Err(InvalidPropertyValue), - } - } -} - -impl<'a> Value<'a> for &'a CStr { - fn parse(value: &'a [u8]) -> Result { - CStr::from_bytes_until_nul(value).map_err(|_| InvalidPropertyValue) - } -} - -impl<'a> Value<'a> for &'a str { - fn parse(value: &'a [u8]) -> Result { - core::str::from_utf8(value) - .map(|s| s.trim_end_matches('\0')) - .map_err(|_| InvalidPropertyValue) - } -} - pub struct NodeProperty<'a> { name: &'a str, value: &'a [u8], @@ -354,7 +316,7 @@ impl<'a> NodeProperty<'a> { self.value } - pub fn to>(&self) -> Result { + pub fn to>(&self) -> Result { V::parse(self.value) } } @@ -363,6 +325,7 @@ pub struct NodeChildren<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> data: &'a [

>::Granularity], parent: &'a RawNode<

>::Granularity>, strings: StringsBlock<'a>, + structs: StructsBlock<'a,

>::Granularity>, _mode: core::marker::PhantomData<*mut P>, } @@ -372,7 +335,7 @@ impl<'a, P: ParserWithMode<'a>> NodeChildren<'a, P> { } pub fn advance(&mut self) -> P::Output>> { - let mut parser = P::new(self.data, self.strings.0); + let mut parser = P::new(self.data, self.strings, self.structs); match parser.peek_token() { Ok(BigEndianToken::BEGIN_NODE) => {} @@ -404,6 +367,7 @@ impl<'a, P: ParserWithMode<'a>> NodeChildren<'a, P> { data: self.data, parent: self.parent, strings: self.strings, + structs: self.structs, _mode: core::marker::PhantomData, }; diff --git a/src/parsing.rs b/src/parsing.rs index ed48f62..c4107be 100644 --- a/src/parsing.rs +++ b/src/parsing.rs @@ -224,8 +224,12 @@ impl<'a, T: Parser<'a>, U: PanicMode> crate::sealed::Sealed for (T, U) {} impl<'a, T: Parser<'a>, U: PanicMode + Clone + Default> Parser<'a> for (T, U) { type Granularity = T::Granularity; - fn new(data: &'a [Self::Granularity], strings: &'a [u8]) -> Self { - (T::new(data, strings), U::default()) + fn new( + data: &'a [Self::Granularity], + strings: StringsBlock<'a>, + structs: StructsBlock<'a, Self::Granularity>, + ) -> Self { + (T::new(data, strings, structs), U::default()) } fn data(&self) -> &'a [Self::Granularity] { @@ -240,6 +244,10 @@ impl<'a, T: Parser<'a>, U: PanicMode + Clone + Default> Parser<'a> for (T, U) { self.0.strings() } + fn structs(&self) -> StructsBlock<'a, Self::Granularity> { + self.0.structs() + } + fn advance_token(&mut self) -> Result { self.0.advance_token() } @@ -304,10 +312,15 @@ impl<'a, T: Parser<'a>, U: PanicMode + Clone + Default + 'static> ParserWithMode pub trait Parser<'a>: crate::sealed::Sealed + Clone { type Granularity: Copy; - fn new(data: &'a [Self::Granularity], strings: &'a [u8]) -> Self; + fn new( + data: &'a [Self::Granularity], + strings: StringsBlock<'a>, + structs: StructsBlock<'a, Self::Granularity>, + ) -> Self; fn data(&self) -> &'a [Self::Granularity]; fn byte_data(&self) -> &'a [u8]; fn strings(&self) -> StringsBlock<'a>; + fn structs(&self) -> StructsBlock<'a, Self::Granularity>; fn advance_token(&mut self) -> Result; fn peek_token(&mut self) -> Result { @@ -381,6 +394,7 @@ pub trait Parser<'a>: crate::sealed::Sealed + Clone { this: RawNode::new(&starting_data[..starting_data.len() - 4]), parent: None, strings: self.strings(), + structs: self.structs(), _mode: core::marker::PhantomData, }) } @@ -400,6 +414,7 @@ pub trait Parser<'a>: crate::sealed::Sealed + Clone { this: RawNode::new(&starting_data[..starting_data.len() - 1]), parent: None, strings: self.strings(), + structs: self.structs(), _mode: core::marker::PhantomData, }) } @@ -463,6 +478,7 @@ pub trait Parser<'a>: crate::sealed::Sealed + Clone { ), parent, strings: self.strings(), + structs: self.structs(), _mode: core::marker::PhantomData, }), _ => Err(FdtError::ParseError(ParseError::UnexpectedToken)), @@ -502,3 +518,7 @@ impl<'a> StringsBlock<'a> { .map_err(|_| ParseError::InvalidCStrValue.into()) } } + +#[derive(Debug, Clone, Copy)] +#[repr(transparent)] +pub struct StructsBlock<'a, G>(pub(crate) &'a [G]); diff --git a/src/parsing/aligned.rs b/src/parsing/aligned.rs index 5e0cea8..e61a5e1 100644 --- a/src/parsing/aligned.rs +++ b/src/parsing/aligned.rs @@ -4,16 +4,20 @@ use crate::FdtError; -use super::{BigEndianToken, BigEndianU32, BigEndianU64, ParseError, Parser, Stream, StringsBlock}; +use super::{ + BigEndianToken, BigEndianU32, BigEndianU64, ParseError, Parser, Stream, StringsBlock, + StructsBlock, +}; pub struct AlignedParser<'a> { stream: Stream<'a, u32>, strings: StringsBlock<'a>, + structs: StructsBlock<'a, u32>, } impl Clone for AlignedParser<'_> { fn clone(&self) -> Self { - Self { stream: self.stream.clone(), strings: self.strings } + Self { stream: self.stream.clone(), strings: self.strings, structs: self.structs } } } @@ -21,8 +25,12 @@ impl crate::sealed::Sealed for AlignedParser<'_> {} impl<'a> Parser<'a> for AlignedParser<'a> { type Granularity = u32; - fn new(data: &'a [Self::Granularity], strings: &'a [u8]) -> Self { - Self { stream: Stream::new(data), strings: StringsBlock(strings) } + fn new( + data: &'a [Self::Granularity], + strings: StringsBlock<'a>, + structs: StructsBlock<'a, Self::Granularity>, + ) -> Self { + Self { stream: Stream::new(data), strings, structs } } fn data(&self) -> &'a [Self::Granularity] { @@ -43,6 +51,10 @@ impl<'a> Parser<'a> for AlignedParser<'a> { self.strings } + fn structs(&self) -> StructsBlock<'a, Self::Granularity> { + self.structs + } + fn advance_token(&mut self) -> Result { loop { match BigEndianToken( @@ -114,7 +126,8 @@ mod tests { let n = BigEndianU64::from_ne(0xF00DCAFEDEADFEED); let mut parser = AlignedParser::new( unsafe { core::slice::from_raw_parts(&n as *const BigEndianU64 as *const u32, 2) }, - &[], + StringsBlock(&[]), + StructsBlock(&[]), ); let m = parser.advance_u64().unwrap(); diff --git a/src/parsing/unaligned.rs b/src/parsing/unaligned.rs index 13c39d5..7d17e5d 100644 --- a/src/parsing/unaligned.rs +++ b/src/parsing/unaligned.rs @@ -4,16 +4,20 @@ use crate::FdtError; -use super::{BigEndianToken, BigEndianU32, BigEndianU64, ParseError, Parser, Stream, StringsBlock}; +use super::{ + BigEndianToken, BigEndianU32, BigEndianU64, ParseError, Parser, Stream, StringsBlock, + StructsBlock, +}; pub struct UnalignedParser<'a> { stream: Stream<'a, u8>, strings: StringsBlock<'a>, + structs: StructsBlock<'a, u8>, } impl Clone for UnalignedParser<'_> { fn clone(&self) -> Self { - Self { stream: self.stream.clone(), strings: self.strings } + Self { stream: self.stream.clone(), strings: self.strings, structs: self.structs } } } @@ -21,8 +25,12 @@ impl crate::sealed::Sealed for UnalignedParser<'_> {} impl<'a> Parser<'a> for UnalignedParser<'a> { type Granularity = u8; - fn new(data: &'a [Self::Granularity], strings: &'a [u8]) -> Self { - Self { stream: Stream::new(data), strings: StringsBlock(strings) } + fn new( + data: &'a [Self::Granularity], + strings: StringsBlock<'a>, + structs: StructsBlock<'a, Self::Granularity>, + ) -> Self { + Self { stream: Stream::new(data), strings, structs } } fn data(&self) -> &'a [Self::Granularity] { @@ -37,6 +45,10 @@ impl<'a> Parser<'a> for UnalignedParser<'a> { self.strings } + fn structs(&self) -> StructsBlock<'a, Self::Granularity> { + self.structs + } + fn advance_token(&mut self) -> Result { loop { match BigEndianToken(self.advance_u32()?) { @@ -101,7 +113,8 @@ mod tests { let n = BigEndianU32::from_ne(0xF00DCAFE); let mut parser = UnalignedParser::new( unsafe { core::slice::from_raw_parts(&n as *const BigEndianU32 as *const u8, 4) }, - &[], + StringsBlock(&[]), + StructsBlock(&[]), ); let m = parser.advance_u32().unwrap(); @@ -113,7 +126,8 @@ mod tests { let n = BigEndianU64::from_ne(0xF00DCAFEDEADFEED); let mut parser = UnalignedParser::new( unsafe { core::slice::from_raw_parts(&n as *const BigEndianU64 as *const u8, 8) }, - &[], + StringsBlock(&[]), + StructsBlock(&[]), ); let m = parser.advance_u64().unwrap(); diff --git a/src/properties.rs b/src/properties.rs index 8ea1fec..4f12221 100644 --- a/src/properties.rs +++ b/src/properties.rs @@ -1,6 +1,10 @@ +use core::ffi::CStr; + use crate::{ nodes::Node, - parsing::{unaligned::UnalignedParser, BigEndianU32, NoPanic, Parser}, + parsing::{ + unaligned::UnalignedParser, BigEndianU32, NoPanic, Parser, StringsBlock, StructsBlock, + }, FdtError, }; @@ -8,6 +12,287 @@ pub trait Property<'a>: Sized { fn parse>(node: Node<'a, (P, NoPanic)>) -> Result, FdtError>; } +/// [Devicetree 2.3.1. +/// `compatible`](https://devicetree-specification.readthedocs.io/en/latest/chapter2-devicetree-basics.html#compatible) +/// +/// The `compatible` property value consists of one or more strings that define +/// the specific programming model for the device. This list of strings should +/// be used by a client program for device driver selection. The property value +/// consists of a concatenated list of null terminated strings, from most +/// specific to most general. They allow a device to express its compatibility +/// with a family of similar devices, potentially allowing a single device +/// driver to match against several devices. +/// +/// The recommended format is `"manufacturer,model"`, where `manufacturer` is a +/// string describing the name of the manufacturer (such as a stock ticker +/// symbol), and model specifies the model number. +/// +/// The compatible string should consist only of lowercase letters, digits and +/// dashes, and should start with a letter. A single comma is typically only +/// used following a vendor prefix. Underscores should not be used. +/// +/// Example: `compatible = "fsl,mpc8641", "ns16550";` +/// +/// In this example, an operating system would first try to locate a device +/// driver that supported `fsl,mpc8641`. If a driver was not found, it would +/// then try to locate a driver that supported the more general `ns16550` device +/// type. +#[derive(Debug, Clone, Copy)] +pub struct Compatible<'a> { + string: &'a str, +} + +impl<'a> Property<'a> for Compatible<'a> { + fn parse>(node: Node<'a, (P, NoPanic)>) -> Result, FdtError> { + let property = node.properties()?.find("compatible")?; + + match property { + Some(prop) => Ok(Some(Self { string: prop.to()? })), + None => Ok(None), + } + } +} + +impl<'a> Compatible<'a> { + /// First compatible model + pub fn first(self) -> &'a str { + self.string.split('\0').next().unwrap_or(self.string) + } + + /// Returns an iterator over all compatible models + pub fn all(self) -> CompatibleIter<'a> { + CompatibleIter { iter: self.string.split('\0') } + } +} + +impl<'a> IntoIterator for Compatible<'a> { + type IntoIter = CompatibleIter<'a>; + type Item = &'a str; + + fn into_iter(self) -> Self::IntoIter { + self.all() + } +} + +pub struct CompatibleIter<'a> { + iter: core::str::Split<'a, char>, +} + +impl<'a> Iterator for CompatibleIter<'a> { + type Item = &'a str; + fn next(&mut self) -> Option { + self.iter.next() + } +} + +/// [Devicetree 2.3.2. +/// `model`](https://devicetree-specification.readthedocs.io/en/latest/chapter2-devicetree-basics.html#model) +/// +/// The model property value is a `` that specifies the manufacturer’s +/// model number of the device. +/// +/// The recommended format is: `"manufacturer,model"`, where `manufacturer` is a +/// string describing the name of the manufacturer (such as a stock ticker +/// symbol), and model specifies the model number. +/// +/// Example: `model = "fsl,MPC8349EMITX";` +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct Model<'a>(&'a str); + +impl<'a> Property<'a> for Model<'a> { + fn parse>(node: Node<'a, (P, NoPanic)>) -> Result, FdtError> { + match node.properties()?.find("model")? { + Some(model) => Ok(Some(Self(model.to()?))), + None => Ok(None), + } + } +} + +impl<'a> core::ops::Deref for Model<'a> { + type Target = str; + fn deref(&self) -> &Self::Target { + self.0 + } +} + +impl<'a> core::cmp::PartialEq for Model<'a> { + fn eq(&self, other: &str) -> bool { + self.0.eq(other) + } +} + +impl<'a> core::cmp::PartialEq> for str { + fn eq(&self, other: &Model<'a>) -> bool { + self.eq(other.0) + } +} + +/// [Devicetree 2.3.3. +/// `phandle`](https://devicetree-specification.readthedocs.io/en/latest/chapter2-devicetree-basics.html#phandle) +/// +/// The `phandle` property specifies a numerical identifier for a node that is +/// unique within the devicetree. The `phandle` property value is used by other +/// nodes that need to refer to the node associated with the property. +/// +/// Example: +/// +/// ```dts +/// pic@10000000 { +/// phandle = <1>; +/// interrupt-controller; +/// reg = <0x10000000 0x100>; +/// }; +/// ``` +/// +/// A `phandle` value of `1` is defined. Another device node could reference the pic node with a `phandle` value of `1`: +/// +/// ```dts +/// another-device-node { +/// interrupt-parent = <1>; +/// }; +/// ``` +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct PHandle(BigEndianU32); + +impl<'a> Property<'a> for PHandle { + fn parse>(node: Node<'a, (P, NoPanic)>) -> Result, FdtError> { + let Some(phandle) = node.properties()?.find("phandle")? else { + return Ok(None); + }; + + Ok(Some(PHandle(phandle.to()?))) + } +} + +/// [Devicetree 2.3.4. +/// `status`](https://devicetree-specification.readthedocs.io/en/latest/chapter2-devicetree-basics.html#status) +/// +/// The `status` property indicates the operational status of a device. The lack +/// of a `status` property should be treated as if the property existed with the +/// value of `"okay"`. Valid values for the `status` property are: +/// +/// | Value | Description | +/// |--------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +/// | `"okay"` | Indicates the device is operational. | +/// | `"disabled"` | Indicates that the device is not presently operational, but it might become operational in the future (for example, something is not plugged in, or switched off). Refer to the device binding for details on what disabled means for a given device. | +/// | `"reserved"` | Indicates that the device is operational, but should not be used. Typically this is used for devices that are controlled by another software component, such as platform firmware. | +/// | `"fail"` | Indicates that the device is not operational. A serious error was detected in the device, and it is unlikely to become operational without repair. | +/// | `"fail-sss"` | Indicates that the device is not operational. A serious error was detected in the device and it is unlikely to become operational without repair. The `sss` portion of the value is specific to the device and indicates the error condition detected. | +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct Status<'a>(&'a str); + +impl<'a> Status<'a> { + pub const OKAY: Self = Self("okay"); + pub const DISABLED: Self = Self("disabled"); + pub const RESERVED: Self = Self("reserved"); + pub const FAIL: Self = Self("fail"); + + /// Returns true if the status is `"okay"` + #[inline] + pub fn is_okay(self) -> bool { + self == Self::OKAY + } + + /// Returns true if the status is `"disabled"` + #[inline] + pub fn is_disabled(self) -> bool { + self == Self::DISABLED + } + + /// Returns true if the status is `"reserved"` + #[inline] + pub fn is_reserved(self) -> bool { + self == Self::RESERVED + } + + /// Returns true if the status is `"fail"` or begins with `"fail-"` + #[inline] + pub fn is_failed(self) -> bool { + self == Self::FAIL || self.0.starts_with("fail-") + } + + /// Returns the `sss` portion of the `fail-sss` status, if the status is + /// failed and contains a status condition + pub fn failed_status_code(self) -> Option<&'a str> { + match self.0.starts_with("fail-") { + true => Some(self.0.trim_start_matches("fail-")), + false => None, + } + } +} + +impl<'a> Property<'a> for Status<'a> { + fn parse>(node: Node<'a, (P, NoPanic)>) -> Result, FdtError> { + match node.properties()?.find("status")? { + Some(model) => Ok(Some(Self(model.to()?))), + None => Ok(None), + } + } +} + +impl<'a> core::ops::Deref for Status<'a> { + type Target = str; + fn deref(&self) -> &Self::Target { + self.0 + } +} + +impl<'a> core::cmp::PartialEq for Status<'a> { + fn eq(&self, other: &str) -> bool { + self.0.eq(other) + } +} + +impl<'a> core::cmp::PartialEq> for str { + fn eq(&self, other: &Status<'a>) -> bool { + self.eq(other.0) + } +} + +/// [Devicetree 2.3.5. `#address-cells` and +/// `#size-cells`](https://devicetree-specification.readthedocs.io/en/latest/chapter2-devicetree-basics.html#address-cells-and-size-cells) +/// +/// The `#address-cells` and `#size-cells` properties may be used in any device +/// node that has children in the devicetree hierarchy and describes how child +/// device nodes should be addressed. The `#address-cells` property defines the +/// number of `` cells used to encode the address field in a child node’s +/// reg property. The `#size-cells` property defines the number of `` cells +/// used to encode the size field in a child node’s reg property. +/// +/// The `#address-cells` and `#size-cells` properties are not inherited from +/// ancestors in the devicetree. They shall be explicitly defined. +/// +/// A DTSpec-compliant boot program shall supply `#address-cells` and +/// `#size-cells` on all nodes that have children. +/// +/// If missing, a client program should assume a default value of 2 for +/// `#address-cells`, and a value of 1 for `#size-cells`. +/// +/// Example: +/// +/// ```dts +/// soc { +/// #address-cells = <1>; +/// #size-cells = <1>; +/// +/// serial@4600 { +/// compatible = "ns16550"; +/// reg = <0x4600 0x100>; +/// clock-frequency = <0>; +/// interrupts = <0xA 0x8>; +/// interrupt-parent = <&ipic>; +/// }; +/// }; +/// ``` +/// +/// In this example, the `#address-cells` and `#size-cells` properties of the +/// soc node are both set to `1`. This setting specifies that one cell is +/// required to represent an address and one cell is required to represent the +/// size of nodes that are children of this node. +/// +/// The serial device reg property necessarily follows this specification set in +/// the parent (soc) node—the address is represented by a single cell +/// (`0x4600`), and the size is represented by a single cell (`0x100`). #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct CellSizes { pub address_cells: usize, @@ -15,14 +300,15 @@ pub struct CellSizes { } impl<'a> Property<'a> for CellSizes { - #[track_caller] + #[inline] fn parse>(node: Node<'a, (P, NoPanic)>) -> Result, FdtError> { let (mut address_cells, mut size_cells) = (None, None); for property in node.properties()? { let property = property?; - let mut parser = UnalignedParser::new(property.value(), &[]); + let mut parser = + UnalignedParser::new(property.value(), StringsBlock(&[]), StructsBlock(&[])); match property.name() { "#address-cells" => address_cells = Some(parser.advance_u32()?.to_ne() as usize), "#size-cells" => size_cells = Some(parser.advance_u32()?.to_ne() as usize), @@ -30,31 +316,177 @@ impl<'a> Property<'a> for CellSizes { } } - if let (None, Some(parent)) = (address_cells, node.parent()) { - address_cells = parent.property::()?.map(|c| c.address_cells); + Ok(address_cells + .zip(size_cells) + .map(|(address_cells, size_cells)| CellSizes { address_cells, size_cells })) + } +} + +impl Default for CellSizes { + fn default() -> Self { + CellSizes { address_cells: 2, size_cells: 1 } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Reg<'a> { + cell_sizes: CellSizes, + encoded_array: &'a [u8], +} + +impl<'a> Reg<'a> { + pub fn cell_sizes(self) -> CellSizes { + self.cell_sizes + } + + pub fn iter_raw(self) -> RegRawIter<'a> { + RegRawIter { cell_sizes: self.cell_sizes, encoded_array: self.encoded_array } + } +} + +impl<'a> Property<'a> for Reg<'a> { + fn parse>(node: Node<'a, (P, NoPanic)>) -> Result, FdtError> { + let Some(prop) = node.raw_property("reg")? else { + return Ok(None); + }; + + let cell_sizes = match node.parent() { + Some(parent) => parent.property::()?.unwrap_or_default(), + None => CellSizes::default(), + }; + + let encoded_array = prop.value(); + + if encoded_array.len() % (cell_sizes.address_cells * 4 + cell_sizes.size_cells * 4) != 0 { + return Err(FdtError::InvalidPropertyValue); } - if let (None, Some(parent)) = (size_cells, node.parent()) { - size_cells = parent.property::()?.map(|c| c.size_cells); + Ok(Some(Self { cell_sizes, encoded_array })) + } +} + +pub struct RegRawIter<'a> { + cell_sizes: CellSizes, + encoded_array: &'a [u8], +} + +impl<'a> Iterator for RegRawIter<'a> { + type Item = RawRegEntry<'a>; + fn next(&mut self) -> Option { + let address_bytes = self.cell_sizes.address_cells * 4; + let size_bytes = self.cell_sizes.size_cells * 4; + + let address = self.encoded_array.get(..address_bytes)?; + let len = self.encoded_array.get(address_bytes..address_bytes + size_bytes)?; + self.encoded_array = self.encoded_array.get((address_bytes + size_bytes)..)?; + Some(RawRegEntry { address, len }) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct RawRegEntry<'a> { + address: &'a [u8], + len: &'a [u8], +} + +impl<'a> RawRegEntry<'a> { + pub fn address(self) -> &'a [u8] { + self.address + } + + pub fn len(self) -> &'a [u8] { + self.len + } +} + +pub struct InvalidPropertyValue; + +impl From for FdtError { + fn from(_: InvalidPropertyValue) -> Self { + FdtError::InvalidPropertyValue + } +} + +pub trait PropertyValue<'a>: Sized { + fn parse(value: &'a [u8]) -> Result; +} + +impl<'a> PropertyValue<'a> for u32 { + #[inline] + fn parse(value: &'a [u8]) -> Result { + match value { + [a, b, c, d] => Ok(u32::from_be_bytes([*a, *b, *c, *d])), + _ => Err(InvalidPropertyValue), } + } +} - Ok(Some(CellSizes { - address_cells: address_cells.unwrap_or(2), - size_cells: size_cells.unwrap_or(2), - })) +impl<'a> PropertyValue<'a> for BigEndianU32 { + #[inline] + fn parse(value: &'a [u8]) -> Result { + match value { + [a, b, c, d] => Ok(BigEndianU32::from_be(u32::from_ne_bytes([*a, *b, *c, *d]))), + _ => Err(InvalidPropertyValue), + } } } -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct PHandle(BigEndianU32); +impl<'a> PropertyValue<'a> for &'a CStr { + #[inline] + fn parse(value: &'a [u8]) -> Result { + CStr::from_bytes_until_nul(value).map_err(|_| InvalidPropertyValue) + } +} -impl<'a> Property<'a> for PHandle { - #[track_caller] - fn parse>(node: Node<'a, (P, NoPanic)>) -> Result, FdtError> { - let Some(phandle) = node.properties()?.find("phandle")? else { - return Ok(None); +impl<'a> PropertyValue<'a> for &'a str { + #[inline] + fn parse(value: &'a [u8]) -> Result { + core::str::from_utf8(value) + .map(|s| s.trim_end_matches('\0')) + .map_err(|_| InvalidPropertyValue) + } +} + +#[derive(Debug, Clone)] +pub struct StringList<'a> { + strs: core::str::Split<'a, char>, +} + +impl<'a> PropertyValue<'a> for StringList<'a> { + #[inline] + fn parse(value: &'a [u8]) -> Result { + Ok(Self { strs: <&'a str as PropertyValue<'a>>::parse(value)?.split('\0') }) + } +} + +impl<'a> Iterator for StringList<'a> { + type Item = &'a str; + + #[inline(always)] + fn next(&mut self) -> Option { + self.strs.next() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn reg_raw_iter() { + let mut iter = RegRawIter { + cell_sizes: CellSizes { address_cells: 2, size_cells: 1 }, + encoded_array: &[ + 0x55, 0x44, 0x33, 0x22, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, + ], }; - Ok(Some(PHandle(phandle.to()?))) + assert_eq!( + iter.next().unwrap(), + RawRegEntry { + address: &[0x55, 0x44, 0x33, 0x22, 0x66, 0x77, 0x88, 0x99], + len: &[0xAA, 0xBB, 0xCC, 0xDD] + } + ); } } diff --git a/src/standard_nodes.rs b/src/standard_nodes.rs index c62a6bb..8231dd6 100644 --- a/src/standard_nodes.rs +++ b/src/standard_nodes.rs @@ -5,7 +5,7 @@ use crate::{ nodes::{IntoSearchableNodeName, Node, RawNode, SearchableNodeName}, parsing::{aligned::AlignedParser, BigEndianToken, Panic, ParseError, Parser, ParserWithMode}, - properties::CellSizes, + properties::{CellSizes, Compatible, PHandle, Property}, tryblock, FdtError, }; @@ -119,7 +119,8 @@ impl<'a> StdInOutPath<'a> { /// https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#chosen-node /// /// Example: - /// ``` + /// + /// ```dts /// / { /// chosen { /// stdout-path = "/soc/uart@10000000:115200"; @@ -185,6 +186,8 @@ impl<'a, P: ParserWithMode<'a>> Root<'a, P> { /// [Devicetree 3.2. Root /// node](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#root-node) /// + /// **Required** + /// /// Specifies a list of platform architectures with which this platform is /// compatible. This property can be used by operating systems in selecting /// platform specific code. The recommended form of the property value is: @@ -193,24 +196,8 @@ impl<'a, P: ParserWithMode<'a>> Root<'a, P> { /// For example: `compatible = "fsl,mpc8572ds"` pub fn compatible(self) -> P::Output> { P::to_output(crate::tryblock! { - let node = self.node.fallible(); - node - .properties()? - .into_iter() - .find_map(|n| match n { - Err(e) => Some(Err(e)), - Ok(property) => match property.name() == "compatible" { - false => None, - true => Some( - property - .to::<&'a str>() - .map(|string| Compatible { string }) - .map_err(Into::into) - ), - }, - }) - .transpose()? - .ok_or(FdtError::MissingRequiredProperty("compatible")) + Compatible::parse(self.node.fallible())? + .ok_or(FdtError::MissingRequiredProperty("compatible")) }) } @@ -224,6 +211,21 @@ impl<'a, P: ParserWithMode<'a>> Root<'a, P> { // self.node.properties().find(|p| p.name == name) // } + #[track_caller] + pub fn resolve_phandle(self, phandle: PHandle) -> P::Output>> { + P::to_output(crate::tryblock! { + let this = Root { node: self.node.fallible() }; + for node in this.all_nodes()? { + let (_, node) = node?; + if node.property::()? == Some(phandle) { + return Ok(Some(node.alt())); + } + } + + Ok(None) + }) + } + #[track_caller] pub fn find_node(self, path: &str) -> P::Output>> where @@ -278,7 +280,7 @@ impl<'a, P: ParserWithMode<'a>> Root<'a, P> { } pub fn all_nodes(self) -> P::Output> { - let mut parser = P::new(self.node.this.as_slice(), self.node.strings.0); + let mut parser = P::new(self.node.this.as_slice(), self.node.strings, self.node.structs); let res = tryblock!({ parser.advance_cstr()?; @@ -350,6 +352,7 @@ impl<'a, P: ParserWithMode<'a>> Iterator for AllNodesIterator<'a, P> { this: RawNode::new(starting_data), parent: self.parents.get(self.parent_index).map(|parent| RawNode::new(parent)), strings: self.parser.strings(), + structs: self.parser.structs(), _mode: core::marker::PhantomData, }, )))); @@ -494,35 +497,6 @@ impl<'a, P: ParserWithMode<'a>> Iterator for AllNodesIterator<'a, P> { // } // } -/// Represents the `compatible` property of a node -#[derive(Clone, Copy)] -pub struct Compatible<'a> { - pub(crate) string: &'a str, -} - -impl<'a> Compatible<'a> { - /// First compatible string - pub fn first(self) -> &'a str { - self.string.split('\0').next().unwrap_or(self.string) - } - - /// Returns an iterator over all available compatible strings - pub fn all(self) -> CompatibleIter<'a> { - CompatibleIter { iter: self.string.split('\0') } - } -} - -pub struct CompatibleIter<'a> { - iter: core::str::Split<'a, char>, -} - -impl<'a> Iterator for CompatibleIter<'a> { - type Item = &'a str; - fn next(&mut self) -> Option { - self.iter.next() - } -} - /// Represents the `/memory` node with specific helper methods // #[derive(Debug, Clone, Copy)] // pub struct Memory<'b, 'a: 'b> { diff --git a/src/tests.rs b/src/tests.rs index 4a4994e..28b708f 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -254,9 +254,10 @@ fn cell_sizes() { assert_eq!(cpu_cs, CellSizes { address_cells: 1, size_cells: 0 }); let soc_sc = fdt.root().find_node("/soc").unwrap().property::().unwrap(); - let test_cs = fdt.root().find_node("/soc/test").unwrap().property::().unwrap(); + let test_cs = fdt.root().find_node("/soc/test").unwrap().property::(); let pci_cs = fdt.root().find_node("/soc/pci").unwrap().property::().unwrap(); - assert_eq!(test_cs, soc_sc); + assert_eq!(soc_sc, CellSizes { address_cells: 2, size_cells: 2 }); + assert_eq!(test_cs, None); assert_ne!(pci_cs, soc_sc); } From c17164c1aa7e7a07e594b8cae7ecd5416a65974d Mon Sep 17 00:00:00 2001 From: repnop Date: Tue, 2 Jul 2024 22:51:32 -0400 Subject: [PATCH 07/38] suffering knows no bounds --- src/lib.rs | 5 + src/nodes.rs | 14 +- src/properties.rs | 547 ++++++++++++++++++++++++++++++++++++++++-- src/standard_nodes.rs | 6 +- 4 files changed, 552 insertions(+), 20 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index bba9d4d..60b5f4f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -84,6 +84,7 @@ pub enum FdtError { BadPtr, /// An error was encountered during parsing ParseError(ParseError), + PHandleNotFound(u32), MissingRequiredNode(&'static str), MissingRequiredProperty(&'static str), InvalidPropertyValue, @@ -101,6 +102,10 @@ impl core::fmt::Display for FdtError { FdtError::BadMagic => write!(f, "bad FDT magic value"), FdtError::BadPtr => write!(f, "an invalid pointer was passed"), FdtError::ParseError(e) => core::fmt::Display::fmt(e, f), + FdtError::PHandleNotFound(value) => write!( + f, + "a node containing the `phandle` property value of `{value}` was not found" + ), FdtError::MissingRequiredNode(name) => { write!(f, "FDT is missing a required node `{}`", name) } diff --git a/src/nodes.rs b/src/nodes.rs index 7c40fd6..b761ea4 100644 --- a/src/nodes.rs +++ b/src/nodes.rs @@ -4,6 +4,7 @@ use crate::{ ParserWithMode, StringsBlock, StructsBlock, }, properties::{InvalidPropertyValue, Property, PropertyValue}, + standard_nodes::Root, FdtError, }; @@ -99,6 +100,13 @@ impl<'a, P: ParserWithMode<'a>> Node<'a, P> { } } + pub(crate) fn make_root>( + self, + ) -> Result, FdtError> { + let mut parser = <(P2, NoPanic)>::new(self.structs.0, self.strings, self.structs); + parser.parse_root().map(|node| Root { node }) + } + #[inline] pub fn name(&self) ->

::Output> { P::to_output( @@ -139,8 +147,10 @@ impl<'a, P: ParserWithMode<'a>> Node<'a, P> { } #[track_caller] - pub fn property>(&self) -> P::Output> { - P::to_output(Prop::parse(self.fallible())) + pub fn property>(&self) -> P::Output> { + P::to_output(crate::tryblock! { + Prop::parse(self.alt(), self.make_root()?) + }) } #[inline] diff --git a/src/properties.rs b/src/properties.rs index 4f12221..4d38722 100644 --- a/src/properties.rs +++ b/src/properties.rs @@ -3,13 +3,18 @@ use core::ffi::CStr; use crate::{ nodes::Node, parsing::{ - unaligned::UnalignedParser, BigEndianU32, NoPanic, Parser, StringsBlock, StructsBlock, + aligned::AlignedParser, unaligned::UnalignedParser, BigEndianU32, NoPanic, Panic, + PanicMode, Parser, ParserWithMode, StringsBlock, StructsBlock, }, + standard_nodes::Root, FdtError, }; -pub trait Property<'a>: Sized { - fn parse>(node: Node<'a, (P, NoPanic)>) -> Result, FdtError>; +pub trait Property<'a, P: Parser<'a>>: Sized { + fn parse( + node: Node<'a, (P, NoPanic)>, + root: Root<'a, (P, NoPanic)>, + ) -> Result, FdtError>; } /// [Devicetree 2.3.1. @@ -42,8 +47,11 @@ pub struct Compatible<'a> { string: &'a str, } -impl<'a> Property<'a> for Compatible<'a> { - fn parse>(node: Node<'a, (P, NoPanic)>) -> Result, FdtError> { +impl<'a, P: Parser<'a>> Property<'a, P> for Compatible<'a> { + fn parse( + node: Node<'a, (P, NoPanic)>, + _: Root<'a, (P, NoPanic)>, + ) -> Result, FdtError> { let property = node.properties()?.find("compatible")?; match property { @@ -99,8 +107,11 @@ impl<'a> Iterator for CompatibleIter<'a> { #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Model<'a>(&'a str); -impl<'a> Property<'a> for Model<'a> { - fn parse>(node: Node<'a, (P, NoPanic)>) -> Result, FdtError> { +impl<'a, P: Parser<'a>> Property<'a, P> for Model<'a> { + fn parse( + node: Node<'a, (P, NoPanic)>, + _: Root<'a, (P, NoPanic)>, + ) -> Result, FdtError> { match node.properties()?.find("model")? { Some(model) => Ok(Some(Self(model.to()?))), None => Ok(None), @@ -154,8 +165,11 @@ impl<'a> core::cmp::PartialEq> for str { #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct PHandle(BigEndianU32); -impl<'a> Property<'a> for PHandle { - fn parse>(node: Node<'a, (P, NoPanic)>) -> Result, FdtError> { +impl<'a, P: Parser<'a>> Property<'a, P> for PHandle { + fn parse( + node: Node<'a, (P, NoPanic)>, + _: Root<'a, (P, NoPanic)>, + ) -> Result, FdtError> { let Some(phandle) = node.properties()?.find("phandle")? else { return Ok(None); }; @@ -221,8 +235,11 @@ impl<'a> Status<'a> { } } -impl<'a> Property<'a> for Status<'a> { - fn parse>(node: Node<'a, (P, NoPanic)>) -> Result, FdtError> { +impl<'a, P: Parser<'a>> Property<'a, P> for Status<'a> { + fn parse( + node: Node<'a, (P, NoPanic)>, + _: Root<'a, (P, NoPanic)>, + ) -> Result, FdtError> { match node.properties()?.find("status")? { Some(model) => Ok(Some(Self(model.to()?))), None => Ok(None), @@ -299,9 +316,12 @@ pub struct CellSizes { pub size_cells: usize, } -impl<'a> Property<'a> for CellSizes { +impl<'a, P: Parser<'a>> Property<'a, P> for CellSizes { #[inline] - fn parse>(node: Node<'a, (P, NoPanic)>) -> Result, FdtError> { + fn parse( + node: Node<'a, (P, NoPanic)>, + _: Root<'a, (P, NoPanic)>, + ) -> Result, FdtError> { let (mut address_cells, mut size_cells) = (None, None); for property in node.properties()? { @@ -342,10 +362,17 @@ impl<'a> Reg<'a> { pub fn iter_raw(self) -> RegRawIter<'a> { RegRawIter { cell_sizes: self.cell_sizes, encoded_array: self.encoded_array } } + + pub fn iter_u64(self) -> RegU64Iter<'a> { + RegU64Iter { cell_sizes: self.cell_sizes, encoded_array: self.encoded_array } + } } -impl<'a> Property<'a> for Reg<'a> { - fn parse>(node: Node<'a, (P, NoPanic)>) -> Result, FdtError> { +impl<'a, P: Parser<'a>> Property<'a, P> for Reg<'a> { + fn parse( + node: Node<'a, (P, NoPanic)>, + _: Root<'a, (P, NoPanic)>, + ) -> Result, FdtError> { let Some(prop) = node.raw_property("reg")? else { return Ok(None); }; @@ -365,6 +392,56 @@ impl<'a> Property<'a> for Reg<'a> { } } +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct RegEntry { + pub address: I, + pub len: I, +} + +pub struct RegU64Iter<'a> { + cell_sizes: CellSizes, + encoded_array: &'a [u8], +} + +impl<'a> Iterator for RegU64Iter<'a> { + type Item = RegEntry; + fn next(&mut self) -> Option { + let address_bytes = self.cell_sizes.address_cells * 4; + let size_bytes = self.cell_sizes.size_cells * 4; + + let encoded_address = self.encoded_array.get(..address_bytes)?; + let encoded_len = self.encoded_array.get(address_bytes..address_bytes + size_bytes)?; + + let mut address: u64 = 0; + let mut len: u64 = 0; + + for encoded_address in encoded_address.chunks_exact(4) { + address = address.wrapping_shl(32); + + // TODO: replace this stuff with `array_chunks` when its stabilized + // + // These unwraps can't panic because `chunks_exact` guarantees that + // we'll always get slices of 4 bytes + let addr = u64::from(u32::from_be_bytes(encoded_address.try_into().unwrap())); + address = address.wrapping_add(addr); + } + + for encoded_len in encoded_len.chunks_exact(4) { + len = len.wrapping_shl(32); + + // TODO: replace this stuff with `array_chunks` when its stabilized + // + // These unwraps can't panic because `chunks_exact` guarantees that + // we'll always get slices of 4 bytes + let l = u64::from(u32::from_be_bytes(encoded_len.try_into().unwrap())); + len = len.wrapping_add(l); + } + + self.encoded_array = self.encoded_array.get((address_bytes + size_bytes)..)?; + Some(RegEntry { address, len }) + } +} + pub struct RegRawIter<'a> { cell_sizes: CellSizes, encoded_array: &'a [u8], @@ -399,6 +476,434 @@ impl<'a> RawRegEntry<'a> { } } +/// [Devicetree 2.3.7. +/// `virtual-reg`](https://devicetree-specification.readthedocs.io/en/latest/chapter2-devicetree-basics.html#virtual-reg) +/// +/// The `virtual-reg` property specifies an effective address that maps to the +/// first physical address specified in the `reg` property of the device node. +/// This property enables boot programs to provide client programs with +/// virtual-to-physical mappings that have been set up. +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct VirtualReg(u32); + +impl VirtualReg { + pub fn into_u32(self) -> u32 { + self.0 + } +} + +impl<'a, P: Parser<'a>> Property<'a, P> for VirtualReg { + fn parse( + node: Node<'a, (P, NoPanic)>, + _: Root<'a, (P, NoPanic)>, + ) -> Result, FdtError> { + match node.properties()?.find("virtual-reg")? { + Some(vreg) => Ok(Some(Self(vreg.to()?))), + None => Ok(None), + } + } +} + +pub struct Ranges<'a> { + cell_sizes: CellSizes, + ranges: &'a [u8], +} + +/// [Devicetree 2.3.10. +/// `dma-coherent`](https://devicetree-specification.readthedocs.io/en/latest/chapter2-devicetree-basics.html#dma-coherent) +/// +/// For architectures which are by default non-coherent for I/O, the +/// `dma-coherent` property is used to indicate a device is capable of coherent +/// DMA operations. Some architectures have coherent DMA by default and this +/// property is not applicable. +pub struct DmaCoherent; + +impl<'a, P: Parser<'a>> Property<'a, P> for DmaCoherent { + fn parse( + node: Node<'a, (P, NoPanic)>, + _: Root<'a, (P, NoPanic)>, + ) -> Result, FdtError> { + match node.properties()?.find("dma-coherent")? { + Some(_) => Ok(Some(Self)), + None => Ok(None), + } + } +} + +/// Enum representing the two possibilities for interrupt descriptions on a +/// devicetree node. See the documentation for each type for more information. +/// [`ExtendedInterrupts`] will take precedence if both properties exist. +pub enum Interrupts<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { + Legacy(LegacyInterrupts<'a, P>), + Extended(ExtendedInterrupts<'a, P>), +} + +impl<'a, P: Parser<'a>, Mode: PanicMode + Clone + Default + 'static> Property<'a, P> + for Interrupts<'a, (P, Mode)> +{ + fn parse( + node: Node<'a, (P, NoPanic)>, + root: Root<'a, (P, NoPanic)>, + ) -> Result, FdtError> { + match ExtendedInterrupts::parse(node, root)? { + Some(extended) => Ok(Some(Self::Extended(extended))), + None => match LegacyInterrupts::parse(node, root)? { + Some(legacy) => Ok(Some(Self::Legacy(legacy))), + None => Ok(None), + }, + } + } +} + +/// [Devicetree 2.4.1.1. +/// `interrupts`](https://devicetree-specification.readthedocs.io/en/latest/chapter2-devicetree-basics.html#interrupts) +/// +/// The `interrupts` property of a device node defines the interrupt or +/// interrupts that are generated by the device. The value of the `interrupts` +/// property consists of an arbitrary number of interrupt specifiers. The format +/// of an interrupt specifier is defined by the binding of the interrupt domain +/// root. +/// +/// `interrupts` is overridden by the `interrupts-extended` property and +/// normally only one or the other should be used. +pub struct LegacyInterrupts<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { + interrupt_parent: InterruptParent<'a, P>, + interrupt_cells: InterruptCells, + encoded_array: &'a [u8], +} + +impl<'a, P: Parser<'a>, Mode: PanicMode + Clone + Default + 'static> Property<'a, P> + for LegacyInterrupts<'a, (P, Mode)> +{ + fn parse( + node: Node<'a, (P, NoPanic)>, + root: Root<'a, (P, NoPanic)>, + ) -> Result, FdtError> { + match node.properties()?.find("interrupts")? { + Some(interrupts) => { + let interrupt_parent = match InterruptParent::<'a, (P, NoPanic)>::parse(node, root)? + { + Some(p) => p, + None => return Err(FdtError::MissingRequiredProperty("interrupt-parent")), + }; + + let Some(interrupt_cells) = interrupt_parent.property::()? else { + return Err(FdtError::MissingRequiredProperty("interrupt-cells")); + }; + + if interrupts.value().len() % (interrupt_cells.0 * 4) as usize != 0 { + return Err(FdtError::InvalidPropertyValue); + } + + Ok(Some(Self { + interrupt_parent: InterruptParent(interrupt_parent.0.alt()), + interrupt_cells, + encoded_array: interrupts.value(), + })) + } + None => Ok(None), + } + } +} + +/// [Devicetree 2.4.1.3. +/// `interrupts-extended`](https://devicetree-specification.readthedocs.io/en/latest/chapter2-devicetree-basics.html#interrupts-extended) +/// +/// The `interrupts-extended` property lists the interrupt(s) generated by a +/// device. `interrupts-extended` should be used instead of interrupts when a +/// device is connected to multiple interrupt controllers as it encodes a parent +/// `phandle` with each interrupt specifier. +/// +/// Example: +/// +/// This example shows how a device with two interrupt outputs connected to two +/// separate interrupt controllers would describe the connection using an +/// `interrupts-extended` property. `pic` is an interrupt controller with an +/// `#interrupt-cells` specifier of 2, while `gic` is an interrupt controller +/// with an `#interrupts-cells` specifier of 1. +/// +/// `interrupts-extended = <&pic 0xA 8>, <&gic 0xda>;` +pub struct ExtendedInterrupts<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { + root: Root<'a, P>, + encoded_array: &'a [u8], +} + +impl<'a, P: ParserWithMode<'a>> ExtendedInterrupts<'a, P> { + pub fn iter(self) -> ExtendedInterruptsIter<'a, P> { + ExtendedInterruptsIter { root: self.root, encoded_array: self.encoded_array } + } +} + +impl<'a, P: Parser<'a>, Mode: PanicMode + Clone + Default + 'static> Property<'a, P> + for ExtendedInterrupts<'a, (P, Mode)> +{ + fn parse( + node: Node<'a, (P, NoPanic)>, + root: Root<'a, (P, NoPanic)>, + ) -> Result, FdtError> { + match node.properties()?.find("interrupts-extended")? { + Some(interrupts) => Ok(Some(Self { + encoded_array: interrupts.value(), + root: Root { node: root.node.alt() }, + })), + + None => Ok(None), + } + } +} + +pub struct ExtendedInterruptsIter<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { + root: Root<'a, P>, + encoded_array: &'a [u8], +} + +impl<'a, P: ParserWithMode<'a>> Iterator for ExtendedInterruptsIter<'a, P> { + type Item = P::Output>; + + #[track_caller] + fn next(&mut self) -> Option { + let phandle = self.encoded_array.get(..4).map(|bytes| { + PHandle(BigEndianU32::from_be(u32::from_ne_bytes(bytes.try_into().unwrap()))) + })?; + self.encoded_array = self.encoded_array.get(4..)?; + + let res = crate::tryblock! { + let root = Root { node: self.root.node.fallible() }; + let Some(interrupt_parent) = root.resolve_phandle(phandle)? else { + return Err(FdtError::PHandleNotFound(phandle.0.to_ne())); + }; + + let Some(interrupt_cells) = interrupt_parent.property::()? else { + return Err(FdtError::MissingRequiredProperty("#interrupt-cells")); + }; + + let cells_length = 4 * interrupt_cells.0 as usize; + let encoded_array = match self.encoded_array.get(..cells_length) { + Some(bytes) => bytes, + None => return Ok(None), + }; + + self.encoded_array = match self.encoded_array.get(cells_length..) { + Some(bytes) => bytes, + None => return Ok(None), + }; + + Ok(Some(ExtendedInterrupt { + interrupt_parent: InterruptParent(interrupt_parent.alt()), + interrupt_cells, + encoded_array, + })) + }; + + // This is a manual impl of `map` because we need the panic location to + // be the caller if `P::to_output` panics + #[allow(clippy::manual_map)] + match res.transpose() { + Some(output) => Some(P::to_output(output)), + None => None, + } + } +} + +/// A single entry in an `interrupts-extended` property +pub struct ExtendedInterrupt<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { + interrupt_parent: InterruptParent<'a, P>, + interrupt_cells: InterruptCells, + encoded_array: &'a [u8], +} + +impl<'a, P: ParserWithMode<'a>> ExtendedInterrupt<'a, P> { + pub fn interrupt_parent(self) -> InterruptParent<'a, P> { + self.interrupt_parent + } + + pub fn interrupt_cells(self) -> InterruptCells { + self.interrupt_cells + } + + pub fn interrupt_specifier(self) -> InterruptSpecifier<'a> { + InterruptSpecifier { + interrupt_cells: self.interrupt_cells, + encoded_array: self.encoded_array, + } + } +} + +pub struct InterruptSpecifier<'a> { + interrupt_cells: InterruptCells, + encoded_array: &'a [u8], +} + +impl<'a> InterruptSpecifier<'a> { + /// Iterate over the components that comprise this interrupt specifier + pub fn iter(self) -> InterruptSpecifierIter<'a> { + InterruptSpecifierIter { encoded_array: self.encoded_array } + } + + /// Extract the single component that comprises the interrupt specifier, if + /// the `#interrupt-cells` value is `1` + pub fn single(self) -> Option { + if self.interrupt_cells.0 != 1 { + return None; + } + + self.iter().next() + } + + /// Extract the two components that comprise the interrupt specifier, if the + /// `#interrupt-cells` value is `2` + pub fn pair(self) -> Option<(u32, u32)> { + if self.interrupt_cells.0 != 2 { + return None; + } + + let mut iter = self.into_iter(); + Some((iter.next()?, iter.next()?)) + } +} + +impl<'a> IntoIterator for InterruptSpecifier<'a> { + type IntoIter = InterruptSpecifierIter<'a>; + type Item = u32; + + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + +/// Iterator over individual components in an interrupt specifier +pub struct InterruptSpecifierIter<'a> { + encoded_array: &'a [u8], +} + +impl<'a> Iterator for InterruptSpecifierIter<'a> { + type Item = u32; + + fn next(&mut self) -> Option { + if self.encoded_array.is_empty() { + return None; + } + + let next = self.encoded_array.get(..4)?; + self.encoded_array = self.encoded_array.get(4..)?; + + // This panic can never fail since the slice length is guaranteed to be + // 4 bytes long + Some(u32::from_be_bytes(next.try_into().unwrap())) + } +} + +/// Iterator over pairs of `u32`s representing an interrupt specifier +pub struct InterruptSpecifierIterPairs<'a> { + encoded_array: &'a [u8], +} + +impl<'a> Iterator for InterruptSpecifierIterPairs<'a> { + type Item = (u32, u32); + + fn next(&mut self) -> Option { + if self.encoded_array.is_empty() { + return None; + } + + let next = self.encoded_array.get(..8)?; + self.encoded_array = self.encoded_array.get(8..)?; + + // This panic can never fail since the slice length is guaranteed to be + // 4 bytes long + Some(( + u32::from_be_bytes(next[..4].try_into().unwrap()), + u32::from_be_bytes(next[4..8].try_into().unwrap()), + )) + } +} + +/// [Devicetree 2.4.1.2. +/// `interrupt-parent`](https://devicetree-specification.readthedocs.io/en/latest/chapter2-devicetree-basics.html#interrupt-parent) +/// +/// Because the hierarchy of the nodes in the interrupt tree might not match the +/// devicetree, the `interrupt-parent` property is available to make the +/// definition of an interrupt parent explicit. The value is the `phandle` to +/// the interrupt parent. If this property is missing from a device, its +/// interrupt parent is assumed to be its devicetree parent. +pub struct InterruptParent<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)>(Node<'a, P>); + +impl<'a, P: ParserWithMode<'a>> core::ops::Deref for InterruptParent<'a, P> { + type Target = Node<'a, P>; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl<'a, P: ParserWithMode<'a>> core::ops::DerefMut for InterruptParent<'a, P> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl<'a, P: Parser<'a>, Mode: PanicMode + Clone + Default + 'static> Property<'a, P> + for InterruptParent<'a, (P, Mode)> +{ + fn parse( + node: Node<'a, (P, NoPanic)>, + root: Root<'a, (P, NoPanic)>, + ) -> Result, FdtError> { + match node.properties()?.find("interrupt-parent")? { + Some(phandle) => match root.resolve_phandle(PHandle(phandle.to()?))? { + Some(parent) => Ok(Some(Self(parent.alt()))), + None => Err(FdtError::PHandleNotFound(phandle.to()?)), + }, + None => Ok(node.parent().map(|n| Self(n.alt()))), + } + } +} + +/// [Devicetree 2.4.2.1. +/// `#interrupt-cells`](https://devicetree-specification.readthedocs.io/en/latest/chapter2-devicetree-basics.html#interrupt-cells) +/// +/// The `#interrupt-cells` property defines the number of cells required to +/// encode an interrupt specifier for an interrupt domain. +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct InterruptCells(u32); + +impl InterruptCells { + pub fn into_u32(self) -> u32 { + self.0 + } +} + +impl<'a, P: Parser<'a>> Property<'a, P> for InterruptCells { + fn parse( + node: Node<'a, (P, NoPanic)>, + _: Root<'a, (P, NoPanic)>, + ) -> Result, FdtError> { + match node.properties()?.find("#interrupt-cells")? { + Some(ic) => Ok(Some(Self(ic.to()?))), + None => Ok(None), + } + } +} + +/// [Devicetree 2.4.2.2. +/// `interrupt-controller`](https://devicetree-specification.readthedocs.io/en/latest/chapter2-devicetree-basics.html#interrupt-controller) +/// +/// The presence of an `interrupt-controller` property defines a node as an +/// interrupt controller node. +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct InterruptController; + +impl<'a, P: Parser<'a>> Property<'a, P> for InterruptController { + fn parse( + node: Node<'a, (P, NoPanic)>, + _: Root<'a, (P, NoPanic)>, + ) -> Result, FdtError> { + match node.properties()?.find("interrupt-controller")? { + Some(_) => Ok(Some(Self)), + None => Ok(None), + } + } +} + pub struct InvalidPropertyValue; impl From for FdtError { @@ -489,4 +994,16 @@ mod tests { } ); } + + #[test] + fn reg_u64_iter() { + let mut iter = RegU64Iter { + cell_sizes: CellSizes { address_cells: 2, size_cells: 1 }, + encoded_array: &[ + 0x55, 0x44, 0x33, 0x22, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, + ], + }; + + assert_eq!(iter.next().unwrap(), RegEntry { address: 0x5544332266778899, len: 0xAABBCCDD }); + } } diff --git a/src/standard_nodes.rs b/src/standard_nodes.rs index 8231dd6..4593c95 100644 --- a/src/standard_nodes.rs +++ b/src/standard_nodes.rs @@ -129,8 +129,8 @@ impl<'a> StdInOutPath<'a> { /// } /// ``` /// - /// ```rust,norun - /// # let fdt = Fdt::new_unaligned(include_bytes!("./dtb/test.dtb")).unwrap(); + /// ```rust + /// # let fdt = fdt::Fdt::new_unaligned(include_bytes!("../dtb/test.dtb")).unwrap(); /// # let chosen = fdt.root().chosen(); /// let stdout = chosen.stdout().unwrap(); /// let stdin = chosen.stdin().unwrap(); @@ -196,7 +196,7 @@ impl<'a, P: ParserWithMode<'a>> Root<'a, P> { /// For example: `compatible = "fsl,mpc8572ds"` pub fn compatible(self) -> P::Output> { P::to_output(crate::tryblock! { - Compatible::parse(self.node.fallible())? + Compatible::parse(self.node.fallible(), self.node.make_root()?)? .ok_or(FdtError::MissingRequiredProperty("compatible")) }) } From 6d06108d4bd35f4dbc39f768fbdde4bd752ba88d Mon Sep 17 00:00:00 2001 From: repnop Date: Mon, 8 Jul 2024 19:12:52 -0400 Subject: [PATCH 08/38] aaaaaa --- src/lib.rs | 1 + src/properties.rs | 281 +++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 267 insertions(+), 15 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 60b5f4f..f19b545 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -88,6 +88,7 @@ pub enum FdtError { MissingRequiredNode(&'static str), MissingRequiredProperty(&'static str), InvalidPropertyValue, + CollectCellsError, } impl From for FdtError { diff --git a/src/properties.rs b/src/properties.rs index 4d38722..cd471a4 100644 --- a/src/properties.rs +++ b/src/properties.rs @@ -1,4 +1,4 @@ -use core::ffi::CStr; +use core::{ffi::CStr, ops::Shl}; use crate::{ nodes::Node, @@ -10,6 +10,259 @@ use crate::{ FdtError, }; +#[derive(Debug, Clone, Copy)] +pub struct CollectCellsError; + +impl From for FdtError { + fn from(_: CollectCellsError) -> Self { + FdtError::CollectCellsError + } +} + +pub trait BuildCellCollector: Default { + type Output; + + fn push(&mut self, component: u32) -> Result<(), CollectCellsError>; + fn finish(self) -> Self::Output; +} + +pub trait CellCollector: Default + Sized { + type Output; + type Builder: BuildCellCollector; + + fn map(builder_out: ::Output) -> Self::Output; +} + +pub struct BuildIntCollector { + value: Int, +} + +impl Default for BuildIntCollector { + fn default() -> Self { + Self { value: Default::default() } + } +} + +impl< + Int: Copy + + Default + + core::cmp::PartialEq + + core::ops::Shl + + core::ops::Shr + + core::ops::BitOr + + From, + > BuildCellCollector for BuildIntCollector +{ + type Output = Int; + + #[inline(always)] + fn push(&mut self, component: u32) -> Result<(), CollectCellsError> { + let shr = const { + match core::mem::size_of::().checked_sub(4) { + Some(value) => value as u32, + None => panic!("integer type too small"), + } + }; + + if self.value >> shr != Int::from(0u32) { + return Err(CollectCellsError); + } + + self.value = self.value.shl(32).bitor(Int::from(component)); + + Ok(()) + } + + #[inline(always)] + fn finish(self) -> Self::Output { + self.value + } +} + +pub struct BuildWrappingIntCollector { + value: Int, +} + +impl Default for BuildWrappingIntCollector { + fn default() -> Self { + Self { value: Default::default() } + } +} + +impl< + Int: Copy + + Default + + core::ops::Shl + + core::ops::BitOr + + From, + > BuildCellCollector for BuildWrappingIntCollector +{ + type Output = Int; + + #[inline(always)] + fn push(&mut self, component: u32) -> Result<(), CollectCellsError> { + self.value = self.value.shl(32).bitor(Int::from(component)); + + Ok(()) + } + + #[inline(always)] + fn finish(self) -> Self::Output { + self.value + } +} + +impl CellCollector for u64 { + type Output = Self; + type Builder = BuildIntCollector; + + #[inline(always)] + fn map(builder_out: as BuildCellCollector>::Output) -> Self::Output { + builder_out + } +} + +impl< + Int: Copy + + Default + + core::ops::Shl + + core::ops::BitOr + + From, + > CellCollector for core::num::Wrapping +{ + type Output = Int; + type Builder = BuildWrappingIntCollector; + + #[inline(always)] + fn map(builder_out: ::Output) -> Self::Output { + builder_out + } +} + +/// [PCI Bus Binding to Open Firmware 2.2.1.1 Numerical Representation](https://www.openfirmware.info/data/docs/bus.pci.pdf) +/// +/// Numerical representation of a PCI address used within the `interrupt-map` property +#[derive(Debug, Clone, Copy, Default)] +pub struct PciAddress { + pub hi: PciAddressHighBits, + pub mid: u32, + pub lo: u32, +} + +impl CellCollector for PciAddress { + type Builder = PciAddressCollector; + type Output = Self; + + fn map(builder_out: ::Output) -> Self::Output { + builder_out + } +} + +/// `phys.hi cell: npt000ss bbbbbbbb dddddfff rrrrrrrr` +/// +/// where: +/// +/// `n` is 0 if the address is relocatable, 1 otherwise +/// +/// `p` is 1 if the addressable region is "prefetchable", 0 otherwise +/// +/// `t` is 1 if the address is aliased (for non-relocatable I/O), below 1 MB (for Memory), +/// +/// `or` below 64 KB (for relocatable I/O). +/// +/// `ss` is the space code, denoting the address space +/// +/// `bbbbbbbb` is the 8-bit Bus Number +/// +/// `ddddd` is the 5-bit Device Number +/// +/// `fff` is the 3-bit Function Number +/// +/// `rrrrrrrr` is the 8-bit Register Number +#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct PciAddressHighBits(u32); + +impl PciAddressHighBits { + #[inline(always)] + pub fn register(self) -> u8 { + self.0 as u8 + } + + #[inline(always)] + pub fn function(self) -> u8 { + ((self.0 >> 8) & 0b111) as u8 + } + + #[inline(always)] + pub fn device(self) -> u8 { + ((self.0 >> 12) & 0b11111) as u8 + } + + #[inline(always)] + pub fn bus(self) -> u8 { + (self.0 >> 16) as u8 + } + + #[inline(always)] + pub fn address_space(self) -> PciAddressSpace { + const CONFIGURATION: u8 = const { PciAddressSpace::Configuration as u8 }; + const IO: u8 = const { PciAddressSpace::Io as u8 }; + const MEMORY32: u8 = const { PciAddressSpace::Memory32 as u8 }; + const MEMORY64: u8 = const { PciAddressSpace::Memory64 as u8 }; + + match ((self.0 >> 24) & 0b11) as u8 { + CONFIGURATION => PciAddressSpace::Configuration, + IO => PciAddressSpace::Io, + MEMORY32 => PciAddressSpace::Memory32, + MEMORY64 => PciAddressSpace::Memory64, + _ => unreachable!(), + } + } + + #[inline(always)] + pub fn prefetchable(self) -> bool { + (self.0 >> 30) & 0b1 == 0b1 + } + + #[inline(always)] + pub fn relocatable(self) -> bool { + (self.0 >> 31) & 0b1 == 0b0 + } +} + +#[repr(u8)] +pub enum PciAddressSpace { + Configuration = 0b00, + Io = 0b01, + Memory32 = 0b10, + Memory64 = 0b11, +} + +#[derive(Default)] +pub struct PciAddressCollector { + address: PciAddress, + num_pushes: u32, +} + +impl BuildCellCollector for PciAddressCollector { + type Output = PciAddress; + + fn push(&mut self, component: u32) -> Result<(), CollectCellsError> { + match self.num_pushes { + 0 => self.address.hi = PciAddressHighBits(component), + 1 => self.address.mid = component, + 2 => self.address.lo = component, + _ => return Err(CollectCellsError), + } + + Ok(()) + } + + fn finish(self) -> Self::Output { + self.address + } +} + pub trait Property<'a, P: Parser<'a>>: Sized { fn parse( node: Node<'a, (P, NoPanic)>, @@ -363,8 +616,8 @@ impl<'a> Reg<'a> { RegRawIter { cell_sizes: self.cell_sizes, encoded_array: self.encoded_array } } - pub fn iter_u64(self) -> RegU64Iter<'a> { - RegU64Iter { cell_sizes: self.cell_sizes, encoded_array: self.encoded_array } + pub fn iter(self) -> RegIter<'a, C> { + RegUIter { cell_sizes: self.cell_sizes, encoded_array: self.encoded_array, collector: core::marker::PhantomData } } } @@ -398,13 +651,14 @@ pub struct RegEntry { pub len: I, } -pub struct RegU64Iter<'a> { +pub struct RegIter<'a, C: CellCollector> { cell_sizes: CellSizes, encoded_array: &'a [u8], + _collector: core::marker::PhantomData<*mut C>, } -impl<'a> Iterator for RegU64Iter<'a> { - type Item = RegEntry; +impl<'a, C: CellCollector> Iterator for RegIter<'a, C> { + type Item = RegEntry; fn next(&mut self) -> Option { let address_bytes = self.cell_sizes.address_cells * 4; let size_bytes = self.cell_sizes.size_cells * 4; @@ -412,29 +666,24 @@ impl<'a> Iterator for RegU64Iter<'a> { let encoded_address = self.encoded_array.get(..address_bytes)?; let encoded_len = self.encoded_array.get(address_bytes..address_bytes + size_bytes)?; - let mut address: u64 = 0; - let mut len: u64 = 0; - + let mut address_collector = ::Builder::default(); for encoded_address in encoded_address.chunks_exact(4) { - address = address.wrapping_shl(32); - // TODO: replace this stuff with `array_chunks` when its stabilized // // These unwraps can't panic because `chunks_exact` guarantees that // we'll always get slices of 4 bytes let addr = u64::from(u32::from_be_bytes(encoded_address.try_into().unwrap())); - address = address.wrapping_add(addr); + address_collector.push(addr); } + let mut len_collector = ::Builder::default(); for encoded_len in encoded_len.chunks_exact(4) { - len = len.wrapping_shl(32); - // TODO: replace this stuff with `array_chunks` when its stabilized // // These unwraps can't panic because `chunks_exact` guarantees that // we'll always get slices of 4 bytes let l = u64::from(u32::from_be_bytes(encoded_len.try_into().unwrap())); - len = len.wrapping_add(l); + len_collector.push(l) } self.encoded_array = self.encoded_array.get((address_bytes + size_bytes)..)?; @@ -904,6 +1153,8 @@ impl<'a, P: Parser<'a>> Property<'a, P> for InterruptController { } } +pub struct InterruptMap<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> {} + pub struct InvalidPropertyValue; impl From for FdtError { From 2b2952f587ab6e9ba507264ea7ec1fcaf2320a5a Mon Sep 17 00:00:00 2001 From: repnop Date: Tue, 9 Jul 2024 00:25:50 -0400 Subject: [PATCH 09/38] please save me from this torment --- src/lib.rs | 1 + src/nodes.rs | 54 ++++- src/parsing.rs | 67 ------ src/properties.rs | 470 ++++++++++++++++++++++++++++++++++++++++-- src/standard_nodes.rs | 4 +- src/tests.rs | 26 ++- 6 files changed, 526 insertions(+), 96 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index f19b545..df8f6b8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -114,6 +114,7 @@ impl core::fmt::Display for FdtError { write!(f, "FDT node is missing a required property `{}`", name) } FdtError::InvalidPropertyValue => write!(f, "FDT property value is invalid"), + FdtError::CollectCellsError => write!(f, "overflow occurred while collecting `#-cells` size values into the desired type") } } } diff --git a/src/nodes.rs b/src/nodes.rs index b761ea4..6ce0484 100644 --- a/src/nodes.rs +++ b/src/nodes.rs @@ -142,12 +142,13 @@ impl<'a, P: ParserWithMode<'a>> Node<'a, P> { #[inline] pub fn raw_property(&self, name: &str) -> P::Output>> { P::to_output(tryblock! { - P::to_result(P::to_result(self.properties())?.find(name)) + let this = self.fallible(); + this.properties()?.find(name) }) } #[track_caller] - pub fn property>(&self) -> P::Output> { + pub fn property>(&self) -> P::Output> { P::to_output(crate::tryblock! { Prop::parse(self.alt(), self.make_root()?) }) @@ -224,11 +225,22 @@ pub struct NodeProperties<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic) } impl<'a, P: ParserWithMode<'a>> NodeProperties<'a, P> { + pub(crate) fn alt>( + self, + ) -> NodeProperties<'a, P2> { + NodeProperties { + data: self.data, + strings: self.strings, + structs: self.structs, + _mode: core::marker::PhantomData, + } + } + pub fn iter(self) -> NodePropertiesIter<'a, P> { - NodePropertiesIter { properties: self } + NodePropertiesIter { properties: self.alt(), _mode: core::marker::PhantomData } } - pub fn advance(&mut self) -> P::Output>> { + pub(crate) fn advance(&mut self) -> P::Output>> { let mut parser = P::new(self.data, self.strings, self.structs); match parser.peek_token() { @@ -296,7 +308,8 @@ impl<'a, P: ParserWithMode<'a>> IntoIterator for NodeProperties<'a, P> { #[derive(Clone)] pub struct NodePropertiesIter<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { - properties: NodeProperties<'a, P>, + properties: NodeProperties<'a, (P::Parser, NoPanic)>, + _mode: core::marker::PhantomData<*mut P>, } impl<'a, P: ParserWithMode<'a>> Iterator for NodePropertiesIter<'a, P> { @@ -304,10 +317,17 @@ impl<'a, P: ParserWithMode<'a>> Iterator for NodePropertiesIter<'a, P> { #[track_caller] fn next(&mut self) -> Option { - P::transpose(self.properties.advance()) + // This is a manual impl of `map` because we need the panic location to + // be the caller if `P::to_output` panics + #[allow(clippy::manual_map)] + match self.properties.advance().transpose() { + Some(output) => Some(P::to_output(output)), + None => None, + } } } +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub struct NodeProperty<'a> { name: &'a str, value: &'a [u8], @@ -341,10 +361,18 @@ pub struct NodeChildren<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> impl<'a, P: ParserWithMode<'a>> NodeChildren<'a, P> { pub fn iter(self) -> NodeChildrenIter<'a, P> { - NodeChildrenIter { children: self } + NodeChildrenIter { + children: NodeChildren { + data: self.data, + parent: self.parent, + strings: self.strings, + structs: self.structs, + _mode: core::marker::PhantomData, + }, + } } - pub fn advance(&mut self) -> P::Output>> { + pub(crate) fn advance(&mut self) -> P::Output>> { let mut parser = P::new(self.data, self.strings, self.structs); match parser.peek_token() { @@ -412,7 +440,7 @@ impl<'a, P: ParserWithMode<'a>> Copy for NodeChildren<'a, P> {} #[derive(Clone)] pub struct NodeChildrenIter<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { - children: NodeChildren<'a, P>, + children: NodeChildren<'a, (P::Parser, NoPanic)>, } impl<'a, P: ParserWithMode<'a> + 'a> Iterator for NodeChildrenIter<'a, P> { @@ -420,6 +448,12 @@ impl<'a, P: ParserWithMode<'a> + 'a> Iterator for NodeChildrenIter<'a, P> { #[track_caller] fn next(&mut self) -> Option { - P::transpose(self.children.advance()) + // This is a manual impl of `map` because we need the panic location to + // be the caller if `P::to_output` panics + #[allow(clippy::manual_map)] + match self.children.advance().transpose() { + Some(output) => Some(P::to_output(output.map(Node::alt))), + None => None, + } } } diff --git a/src/parsing.rs b/src/parsing.rs index c4107be..143c183 100644 --- a/src/parsing.rs +++ b/src/parsing.rs @@ -134,11 +134,6 @@ impl core::fmt::Display for ParseError { pub trait PanicMode: crate::sealed::Sealed { type Output; fn to_output(result: Result) -> Self::Output; - fn transpose(result: Self::Output>) -> Option>; - fn reverse_transpose(result: Option>) -> Self::Output>; - fn ok_as_ref(output: &Self::Output) -> Option<&T>; - fn ok(output: Self::Output) -> Option; - fn to_result(output: Self::Output) -> Result; } #[derive(Clone, Copy, Default)] @@ -152,26 +147,6 @@ impl PanicMode for NoPanic { fn to_output(result: Result) -> Self::Output { result } - - fn transpose(result: Self::Output>) -> Option> { - result.transpose() - } - - fn reverse_transpose(result: Option>) -> Self::Output> { - result.transpose() - } - - fn ok_as_ref(output: &Self::Output) -> Option<&T> { - output.as_ref().ok() - } - - fn ok(output: Self::Output) -> Option { - output.ok() - } - - fn to_result(output: Self::Output) -> Result { - output - } } #[derive(Clone, Copy, Default)] @@ -186,28 +161,6 @@ impl PanicMode for Panic { fn to_output(result: Result) -> Self::Output { result.unwrap() } - - #[track_caller] - #[inline(always)] - fn transpose(result: Self::Output>) -> Option> { - result - } - - fn reverse_transpose(result: Option>) -> Self::Output> { - result - } - - fn ok_as_ref(output: &Self::Output) -> Option<&T> { - Some(output) - } - - fn ok(output: Self::Output) -> Option { - Some(output) - } - - fn to_result(output: Self::Output) -> Result { - Ok(output) - } } pub trait ParserWithMode<'a>: Parser<'a> + PanicMode + crate::sealed::Sealed { @@ -276,26 +229,6 @@ impl<'a, P: Parser<'a>, U: PanicMode> PanicMode for (P, U) { fn to_output(result: Result) -> Self::Output { U::to_output(result) } - - fn transpose(result: Self::Output>) -> Option> { - U::transpose(result) - } - - fn reverse_transpose(result: Option>) -> Self::Output> { - U::reverse_transpose(result) - } - - fn ok_as_ref(output: &Self::Output) -> Option<&T> { - U::ok_as_ref(output) - } - - fn ok(output: Self::Output) -> Option { - U::ok(output) - } - - fn to_result(output: Self::Output) -> Result { - U::to_result(output) - } } impl<'a, T: Parser<'a>, U: PanicMode + Clone + Default + 'static> ParserWithMode<'a> for (T, U) { diff --git a/src/properties.rs b/src/properties.rs index cd471a4..ce5b2bc 100644 --- a/src/properties.rs +++ b/src/properties.rs @@ -122,6 +122,46 @@ impl CellCollector for u64 { } } +impl CellCollector for Option { + type Builder = BuildOptionalCellCollector; + type Output = Option; + + fn map(builder_out: ::Output) -> Self::Output { + builder_out.map(T::map) + } +} + +pub struct BuildOptionalCellCollector { + builder: T::Builder, + used: bool, +} + +impl Default for BuildOptionalCellCollector { + fn default() -> Self { + Self { builder: Default::default(), used: false } + } +} + +impl BuildCellCollector for BuildOptionalCellCollector { + type Output = Option<::Output>; + + #[inline(always)] + fn push(&mut self, component: u32) -> Result<(), CollectCellsError> { + self.used = true; + self.builder.push(component)?; + + Ok(()) + } + + #[inline(always)] + fn finish(self) -> Self::Output { + match self.used { + true => Some(self.builder.finish()), + false => None, + } + } +} + impl< Int: Copy + Default @@ -230,6 +270,17 @@ impl PciAddressHighBits { } } +impl core::ops::BitAnd for PciAddress { + type Output = Self; + fn bitand(self, rhs: Self) -> Self::Output { + Self { + hi: PciAddressHighBits(self.hi.0 & rhs.hi.0), + mid: self.mid & rhs.mid, + lo: self.lo & rhs.lo, + } + } +} + #[repr(u8)] pub enum PciAddressSpace { Configuration = 0b00, @@ -255,6 +306,8 @@ impl BuildCellCollector for PciAddressCollector { _ => return Err(CollectCellsError), } + self.num_pushes += 1; + Ok(()) } @@ -601,6 +654,22 @@ impl Default for CellSizes { } } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct AddressCells(pub usize); + +impl<'a, P: Parser<'a>> Property<'a, P> for AddressCells { + #[inline] + fn parse( + node: Node<'a, (P, NoPanic)>, + _: Root<'a, (P, NoPanic)>, + ) -> Result, FdtError> { + match node.properties()?.find("#address-cells")? { + Some(value) => Ok(Some(Self(value.to()?))), + None => Ok(None), + } + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Reg<'a> { cell_sizes: CellSizes, @@ -617,7 +686,11 @@ impl<'a> Reg<'a> { } pub fn iter(self) -> RegIter<'a, C> { - RegUIter { cell_sizes: self.cell_sizes, encoded_array: self.encoded_array, collector: core::marker::PhantomData } + RegIter { + cell_sizes: self.cell_sizes, + encoded_array: self.encoded_array, + _collector: core::marker::PhantomData, + } } } @@ -658,7 +731,7 @@ pub struct RegIter<'a, C: CellCollector> { } impl<'a, C: CellCollector> Iterator for RegIter<'a, C> { - type Item = RegEntry; + type Item = Result, CollectCellsError>; fn next(&mut self) -> Option { let address_bytes = self.cell_sizes.address_cells * 4; let size_bytes = self.cell_sizes.size_cells * 4; @@ -672,8 +745,11 @@ impl<'a, C: CellCollector> Iterator for RegIter<'a, C> { // // These unwraps can't panic because `chunks_exact` guarantees that // we'll always get slices of 4 bytes - let addr = u64::from(u32::from_be_bytes(encoded_address.try_into().unwrap())); - address_collector.push(addr); + if let Err(e) = + address_collector.push(u32::from_be_bytes(encoded_address.try_into().unwrap())) + { + return Some(Err(e)); + } } let mut len_collector = ::Builder::default(); @@ -682,12 +758,17 @@ impl<'a, C: CellCollector> Iterator for RegIter<'a, C> { // // These unwraps can't panic because `chunks_exact` guarantees that // we'll always get slices of 4 bytes - let l = u64::from(u32::from_be_bytes(encoded_len.try_into().unwrap())); - len_collector.push(l) + if let Err(e) = len_collector.push(u32::from_be_bytes(encoded_len.try_into().unwrap())) + { + return Some(Err(e)); + } } self.encoded_array = self.encoded_array.get((address_bytes + size_bytes)..)?; - Some(RegEntry { address, len }) + Some(Ok(RegEntry { + address: C::map(address_collector.finish()), + len: C::map(len_collector.finish()), + })) } } @@ -1113,13 +1194,7 @@ impl<'a, P: Parser<'a>, Mode: PanicMode + Clone + Default + 'static> Property<'a /// The `#interrupt-cells` property defines the number of cells required to /// encode an interrupt specifier for an interrupt domain. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct InterruptCells(u32); - -impl InterruptCells { - pub fn into_u32(self) -> u32 { - self.0 - } -} +pub struct InterruptCells(pub usize); impl<'a, P: Parser<'a>> Property<'a, P> for InterruptCells { fn parse( @@ -1133,6 +1208,84 @@ impl<'a, P: Parser<'a>> Property<'a, P> for InterruptCells { } } +/// [Devicetree 2.4.3.2. +/// `interrupt-map-mask`](https://devicetree-specification.readthedocs.io/en/latest/chapter2-devicetree-basics.html#interrupt-map-mask) +/// +/// An `interrupt-map-mask` property is specified for a nexus node in the +/// interrupt tree. This property specifies a mask that is `AND`ed with the +/// incoming unit interrupt specifier being looked up in the table specified in +/// the `interrupt-map` property. +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct InterruptMapMask { + address_mask: ADDRMASK::Output, + interrupt_specifier_mask: INTMASK::Output, +} + +impl InterruptMapMask { + pub fn mask( + self, + address: ::Output, + interrupt_specifier: ::Output, + ) -> (::Output, ::Output) + where + ::Output: core::ops::BitAnd< + ::Output, + Output = ::Output, + >, + ::Output: core::ops::BitAnd< + ::Output, + Output = ::Output, + >, + { + (self.address_mask & address, self.interrupt_specifier_mask & interrupt_specifier) + } +} + +impl<'a, ADDRMASK: CellCollector, INTMASK: CellCollector, P: Parser<'a>> Property<'a, P> + for InterruptMapMask +{ + fn parse( + node: Node<'a, (P, NoPanic)>, + _: Root<'a, (P, NoPanic)>, + ) -> Result, FdtError> { + let address_cells = node + .property::()? + .ok_or(FdtError::MissingRequiredProperty("#address-cells"))?; + let interrupt_cells = node + .property::()? + .ok_or(FdtError::MissingRequiredProperty("#interrupt-cells"))?; + match node.properties()?.find("interrupt-map-mask")? { + Some(prop) => { + if prop.value().len() % 4 != 0 { + return Err(FdtError::InvalidPropertyValue); + } + + let mut address_collector = ADDRMASK::Builder::default(); + let mut specifier_collector = INTMASK::Builder::default(); + let mut cells = prop.value().chunks_exact(4); + + // TODO: replace this stuff with `array_chunks` when its stabilized + // + // These unwraps can't panic because `chunks_exact` guarantees that + // we'll always get slices of 4 bytes + for chunk in cells.by_ref().take(address_cells.0) { + address_collector.push(u32::from_be_bytes(chunk.try_into().unwrap()))?; + } + + for chunk in cells.take(interrupt_cells.0 as usize) { + specifier_collector.push(u32::from_be_bytes(chunk.try_into().unwrap()))?; + } + + Ok(Some(Self { + address_mask: ADDRMASK::map(address_collector.finish()), + interrupt_specifier_mask: INTMASK::map(specifier_collector.finish()), + })) + } + None => Ok(None), + } + } +} + /// [Devicetree 2.4.2.2. /// `interrupt-controller`](https://devicetree-specification.readthedocs.io/en/latest/chapter2-devicetree-basics.html#interrupt-controller) /// @@ -1153,7 +1306,266 @@ impl<'a, P: Parser<'a>> Property<'a, P> for InterruptController { } } -pub struct InterruptMap<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> {} +pub struct InterruptMap< + 'a, + CADDR: CellCollector, + CINT: CellCollector, + PADDR: CellCollector, + PINT: CellCollector, + P: ParserWithMode<'a> = (AlignedParser<'a>, Panic), +> { + address_cells: AddressCells, + interrupt_cells: InterruptCells, + node: Node<'a, (P::Parser, NoPanic)>, + encoded_map: &'a [u8], + _collectors: core::marker::PhantomData<*mut (CADDR, CINT, PADDR, PINT)>, +} + +impl< + 'a, + P: ParserWithMode<'a>, + CADDR: CellCollector, + CINT: CellCollector, + PADDR: CellCollector, + PINT: CellCollector, + > InterruptMap<'a, CADDR, CINT, PADDR, PINT, P> +{ + pub fn iter(self) -> InterruptMapIter<'a, CADDR, CINT, PADDR, PINT, P> { + InterruptMapIter { + address_cells: self.address_cells, + interrupt_cells: self.interrupt_cells, + node: self.node, + encoded_map: self.encoded_map, + _collectors: core::marker::PhantomData, + } + } + + pub fn find( + self, + address: CADDR::Output, + interrupt_specifier: CINT::Output, + ) -> P::Output>> { + todo!() + } +} + +impl< + 'a, + P: Parser<'a>, + Mode: PanicMode + Clone + Default + 'static, + CADDR: CellCollector, + CINT: CellCollector, + PADDR: CellCollector, + PINT: CellCollector, + > Property<'a, P> for InterruptMap<'a, CADDR, CINT, PADDR, PINT, (P, Mode)> +{ + fn parse( + node: Node<'a, (P, NoPanic)>, + _: Root<'a, (P, NoPanic)>, + ) -> Result, FdtError> { + let Some(encoded_map) = node.properties()?.find("interrupt-map")? else { return Ok(None) }; + + let address_cells = node + .property::()? + .ok_or(FdtError::MissingRequiredProperty("#address-cells"))?; + let interrupt_cells = node + .property::()? + .ok_or(FdtError::MissingRequiredProperty("#interrupt-cells"))?; + + Ok(Some(InterruptMap { + address_cells, + interrupt_cells, + node: node.alt(), + encoded_map: encoded_map.value(), + _collectors: core::marker::PhantomData, + })) + } +} + +pub struct InterruptMapIter< + 'a, + CADDR: CellCollector, + CINT: CellCollector, + PADDR: CellCollector, + PINT: CellCollector, + P: ParserWithMode<'a> = (AlignedParser<'a>, Panic), +> { + address_cells: AddressCells, + interrupt_cells: InterruptCells, + node: Node<'a, (P::Parser, NoPanic)>, + encoded_map: &'a [u8], + _collectors: core::marker::PhantomData<*mut (CADDR, CINT, PADDR, PINT)>, +} + +impl< + 'a, + CADDR: CellCollector, + CINT: CellCollector, + PADDR: CellCollector, + PINT: CellCollector, + P: ParserWithMode<'a>, + > Iterator for InterruptMapIter<'a, CADDR, CINT, PADDR, PINT, P> +{ + type Item = P::Output>; + + #[track_caller] + fn next(&mut self) -> Option { + let res = crate::tryblock! { + let child_addr_size = self.address_cells.0 * 4; + let child_intsp_size = self.interrupt_cells.0 * 4; + + let Some(child_address_iter) = self.encoded_map.get(..child_addr_size) else { return Ok(None) }; + let Some(child_specifier_iter) = self.encoded_map.get(child_addr_size..child_addr_size+child_intsp_size) else { return Ok(None) }; + let Some(interrupt_parent) = self.encoded_map.get(child_addr_size+child_intsp_size..child_addr_size+child_intsp_size+4) else { return Ok(None) }; + + let root = self.node.make_root::<(P::Parser, NoPanic)>()?; + let phandle = u32::from_ne_bytes(interrupt_parent.try_into().unwrap()); + let interrupt_parent = root.resolve_phandle(PHandle(BigEndianU32::from_be(phandle)))?.ok_or(FdtError::PHandleNotFound(phandle.swap_bytes()))?; + + let parent_address_cells = interrupt_parent.property::()?.ok_or(FdtError::MissingRequiredProperty("#address-cells/#size-cells"))?; + let parent_interrupt_cells = interrupt_parent.property::()?.ok_or(FdtError::MissingRequiredProperty("#interrupt-cells"))?; + + let parent_addr_size = parent_address_cells.0 * 4; + let parent_intsp_size = parent_interrupt_cells.0 * 4; + let Some(parent_address_iter) = self.encoded_map.get(child_addr_size+child_intsp_size+4..child_addr_size+child_intsp_size+4+parent_addr_size) else { return Ok(None)}; + + let Some(mut parent_specifier_iter) = self.encoded_map.get(child_addr_size+child_intsp_size+4+parent_addr_size..) else { return Ok(None)}; + self.encoded_map = match parent_specifier_iter.get(parent_intsp_size..) { + Some(s) => s, + None => return Ok(None), + }; + parent_specifier_iter = match parent_specifier_iter.get(..parent_intsp_size) { + Some(s) => s, + None => return Ok(None), + }; + + let mut child_address_collector = CADDR::Builder::default(); + for chunk in child_address_iter.chunks_exact(4) { + child_address_collector.push(u32::from_be_bytes(chunk.try_into().unwrap()))?; + } + + let mut child_specifier_collector = CINT::Builder::default(); + for chunk in child_specifier_iter.chunks_exact(4) { + child_specifier_collector.push(u32::from_be_bytes(chunk.try_into().unwrap()))?; + } + + let mut parent_address_collector = PADDR::Builder::default(); + for chunk in parent_address_iter.chunks_exact(4) { + parent_address_collector.push(u32::from_be_bytes(chunk.try_into().unwrap()))?; + } + + let mut parent_specifier_collector = PINT::Builder::default(); + for chunk in parent_specifier_iter.chunks_exact(4) { + parent_specifier_collector.push(u32::from_be_bytes(chunk.try_into().unwrap()))?; + } + + Ok(Some(InterruptMapEntry { + interrupt_parent: interrupt_parent.alt(), + child_address: CADDR::map(child_address_collector.finish()), + child_interrupt_specifier: CINT::map(child_specifier_collector.finish()), + parent_address: PADDR::map(parent_address_collector.finish()), + parent_interrupt_specifier: PINT::map(parent_specifier_collector.finish()), + })) + }; + + #[allow(clippy::manual_map)] + match res.transpose() { + Some(output) => Some(P::to_output(output)), + None => None, + } + } +} + +pub struct InterruptMapEntry< + 'a, + CADDR: CellCollector, + CINT: CellCollector, + PADDR: CellCollector, + PINT: CellCollector, + P: ParserWithMode<'a>, +> { + interrupt_parent: Node<'a, P>, + child_address: CADDR::Output, + child_interrupt_specifier: CINT::Output, + parent_address: PADDR::Output, + parent_interrupt_specifier: PINT::Output, +} + +impl< + 'a, + P: ParserWithMode<'a>, + CADDR: CellCollector, + CINT: CellCollector, + PADDR: CellCollector, + PINT: CellCollector, + > InterruptMapEntry<'a, CADDR, CINT, PADDR, PINT, P> +{ + #[inline(always)] + pub fn interrupt_parent(self) -> Node<'a, P> { + self.interrupt_parent + } + + #[inline(always)] + pub fn child_unit_address(self) -> CADDR::Output { + self.child_address + } + + #[inline(always)] + pub fn child_interrupt_specifier(self) -> CINT::Output { + self.child_interrupt_specifier + } + + #[inline(always)] + pub fn parent_unit_address(self) -> PADDR::Output { + self.parent_address + } + + #[inline(always)] + pub fn parent_interrupt_specifier(self) -> PINT::Output { + self.parent_interrupt_specifier + } +} + +impl< + 'a, + P: ParserWithMode<'a>, + CADDR: CellCollector, + CINT: CellCollector, + PADDR: CellCollector, + PINT: CellCollector, + > Clone for InterruptMapEntry<'a, CADDR, CINT, PADDR, PINT, P> +where + CADDR::Output: Clone, + CINT::Output: Clone, + PADDR::Output: Clone, + PINT::Output: Clone, +{ + fn clone(&self) -> Self { + Self { + child_address: self.child_address.clone(), + child_interrupt_specifier: self.child_interrupt_specifier.clone(), + interrupt_parent: self.interrupt_parent, + parent_address: self.parent_address.clone(), + parent_interrupt_specifier: self.parent_interrupt_specifier.clone(), + } + } +} + +impl< + 'a, + P: ParserWithMode<'a>, + CADDR: CellCollector, + CINT: CellCollector, + PADDR: CellCollector, + PINT: CellCollector, + > Copy for InterruptMapEntry<'a, CADDR, CINT, PADDR, PINT, P> +where + CADDR::Output: Copy, + CINT::Output: Copy, + PADDR::Output: Copy, + PINT::Output: Copy, +{ +} pub struct InvalidPropertyValue; @@ -1177,6 +1589,26 @@ impl<'a> PropertyValue<'a> for u32 { } } +impl<'a> PropertyValue<'a> for usize { + #[inline] + fn parse(value: &'a [u8]) -> Result { + #[cfg(target_pointer_width = "32")] + let ret = match value { + [a, b, c, d] => Ok(usize::from_be_bytes([*a, *b, *c, *d])), + _ => Err(InvalidPropertyValue), + }; + + #[cfg(target_pointer_width = "64")] + let ret = match value { + [a, b, c, d] => Ok(usize::from_be_bytes([0, 0, 0, 0, *a, *b, *c, *d])), + [a, b, c, d, e, f, g, h] => Ok(usize::from_be_bytes([*a, *b, *c, *d, *e, *f, *g, *h])), + _ => Err(InvalidPropertyValue), + }; + + ret + } +} + impl<'a> PropertyValue<'a> for BigEndianU32 { #[inline] fn parse(value: &'a [u8]) -> Result { @@ -1248,13 +1680,17 @@ mod tests { #[test] fn reg_u64_iter() { - let mut iter = RegU64Iter { + let mut iter = RegIter:: { cell_sizes: CellSizes { address_cells: 2, size_cells: 1 }, encoded_array: &[ 0x55, 0x44, 0x33, 0x22, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, ], + _collector: core::marker::PhantomData, }; - assert_eq!(iter.next().unwrap(), RegEntry { address: 0x5544332266778899, len: 0xAABBCCDD }); + assert_eq!( + iter.next().unwrap().unwrap(), + RegEntry { address: 0x5544332266778899, len: 0xAABBCCDD } + ); } } diff --git a/src/standard_nodes.rs b/src/standard_nodes.rs index 4593c95..33a63ab 100644 --- a/src/standard_nodes.rs +++ b/src/standard_nodes.rs @@ -152,7 +152,9 @@ impl<'a, P: ParserWithMode<'a>> Root<'a, P> { /// Root node cell sizes #[track_caller] pub fn cell_sizes(self) -> P::Output { - P::transpose(self.node.property::()).unwrap() + P::to_output(crate::tryblock! { + self.node.fallible().property::()?.ok_or(FdtError::MissingRequiredProperty("#address-cells/#size-cells")) + }) } /// [Devicetree 3.2. Root diff --git a/src/tests.rs b/src/tests.rs index 28b708f..3da53f9 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -5,7 +5,7 @@ extern crate std; use nodes::NodeName; -use properties::CellSizes; +use properties::{CellSizes, InterruptMap, PciAddress}; // use crate::{node::RawReg, *}; use crate::*; @@ -261,6 +261,30 @@ fn cell_sizes() { assert_ne!(pci_cs, soc_sc); } +#[test] +fn interrupt_map() { + let fdt = Fdt::new(TEST.as_slice()).unwrap(); + let root = fdt.root(); + + for entry in root + .find_node("/soc/pci") + .unwrap() + .property::, u64, (AlignedParser<'_>, Panic)>>() + .unwrap() + .iter() + { + std::println!( + "{:?} {} {:?} {}", + entry.child_unit_address(), + entry.child_interrupt_specifier(), + entry.parent_unit_address(), + entry.parent_interrupt_specifier() + ); + } + + panic!() +} + // #[test] // fn no_properties() { // let fdt = Fdt::new(TEST.as_slice()).unwrap(); From ea5b13035c1d8bc19b57008c1457f86bcbf93a38 Mon Sep 17 00:00:00 2001 From: repnop Date: Tue, 9 Jul 2024 00:33:05 -0400 Subject: [PATCH 10/38] despair --- src/properties.rs | 9 +++++++-- src/tests.rs | 37 ++++++++++++++++++++++++++----------- 2 files changed, 33 insertions(+), 13 deletions(-) diff --git a/src/properties.rs b/src/properties.rs index ce5b2bc..ada74a2 100644 --- a/src/properties.rs +++ b/src/properties.rs @@ -59,7 +59,7 @@ impl< fn push(&mut self, component: u32) -> Result<(), CollectCellsError> { let shr = const { match core::mem::size_of::().checked_sub(4) { - Some(value) => value as u32, + Some(value) => value as u32 * 8, None => panic!("integer type too small"), } }; @@ -182,7 +182,7 @@ impl< /// [PCI Bus Binding to Open Firmware 2.2.1.1 Numerical Representation](https://www.openfirmware.info/data/docs/bus.pci.pdf) /// /// Numerical representation of a PCI address used within the `interrupt-map` property -#[derive(Debug, Clone, Copy, Default)] +#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct PciAddress { pub hi: PciAddressHighBits, pub mid: u32, @@ -223,6 +223,11 @@ impl CellCollector for PciAddress { pub struct PciAddressHighBits(u32); impl PciAddressHighBits { + #[inline(always)] + pub fn new(raw: u32) -> Self { + Self(raw) + } + #[inline(always)] pub fn register(self) -> u8 { self.0 as u8 diff --git a/src/tests.rs b/src/tests.rs index 3da53f9..afc2750 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -5,7 +5,7 @@ extern crate std; use nodes::NodeName; -use properties::{CellSizes, InterruptMap, PciAddress}; +use properties::{CellSizes, InterruptMap, PciAddress, PciAddressHighBits}; // use crate::{node::RawReg, *}; use crate::*; @@ -266,23 +266,38 @@ fn interrupt_map() { let fdt = Fdt::new(TEST.as_slice()).unwrap(); let root = fdt.root(); - for entry in root + let entries = [ + (PciAddress { hi: PciAddressHighBits::new(0), mid: 0, lo: 0 }, 1, None, 32), + (PciAddress { hi: PciAddressHighBits::new(0), mid: 0, lo: 0 }, 2, None, 33), + (PciAddress { hi: PciAddressHighBits::new(0), mid: 0, lo: 0 }, 3, None, 34), + (PciAddress { hi: PciAddressHighBits::new(0), mid: 0, lo: 0 }, 4, None, 35), + (PciAddress { hi: PciAddressHighBits::new(2048), mid: 0, lo: 0 }, 1, None, 33), + (PciAddress { hi: PciAddressHighBits::new(2048), mid: 0, lo: 0 }, 2, None, 34), + (PciAddress { hi: PciAddressHighBits::new(2048), mid: 0, lo: 0 }, 3, None, 35), + (PciAddress { hi: PciAddressHighBits::new(2048), mid: 0, lo: 0 }, 4, None, 32), + (PciAddress { hi: PciAddressHighBits::new(4096), mid: 0, lo: 0 }, 1, None, 34), + (PciAddress { hi: PciAddressHighBits::new(4096), mid: 0, lo: 0 }, 2, None, 35), + (PciAddress { hi: PciAddressHighBits::new(4096), mid: 0, lo: 0 }, 3, None, 32), + (PciAddress { hi: PciAddressHighBits::new(4096), mid: 0, lo: 0 }, 4, None, 33), + (PciAddress { hi: PciAddressHighBits::new(6144), mid: 0, lo: 0 }, 1, None, 35), + (PciAddress { hi: PciAddressHighBits::new(6144), mid: 0, lo: 0 }, 2, None, 32), + (PciAddress { hi: PciAddressHighBits::new(6144), mid: 0, lo: 0 }, 3, None, 33), + (PciAddress { hi: PciAddressHighBits::new(6144), mid: 0, lo: 0 }, 4, None, 34), + ]; + + for (entry, expected) in root .find_node("/soc/pci") .unwrap() .property::, u64, (AlignedParser<'_>, Panic)>>() .unwrap() .iter() + .zip(entries) { - std::println!( - "{:?} {} {:?} {}", - entry.child_unit_address(), - entry.child_interrupt_specifier(), - entry.parent_unit_address(), - entry.parent_interrupt_specifier() - ); + assert_eq!(entry.child_unit_address(), expected.0); + assert_eq!(entry.child_interrupt_specifier(), expected.1); + assert_eq!(entry.parent_unit_address(), expected.2); + assert_eq!(entry.parent_interrupt_specifier(), expected.3); } - - panic!() } // #[test] From 144a1507f0d7ddab329aee592c992a4bdb30b02b Mon Sep 17 00:00:00 2001 From: repnop Date: Thu, 11 Jul 2024 20:41:34 -0400 Subject: [PATCH 11/38] hmm yes --- src/nodes.rs | 2 +- src/properties.rs | 167 ++++++++++++++++++++++-------------------- src/standard_nodes.rs | 2 +- src/tests.rs | 2 +- 4 files changed, 92 insertions(+), 81 deletions(-) diff --git a/src/nodes.rs b/src/nodes.rs index 6ce0484..05e7a2f 100644 --- a/src/nodes.rs +++ b/src/nodes.rs @@ -148,7 +148,7 @@ impl<'a, P: ParserWithMode<'a>> Node<'a, P> { } #[track_caller] - pub fn property>(&self) -> P::Output> { + pub fn property>(&self) -> P::Output> { P::to_output(crate::tryblock! { Prop::parse(self.alt(), self.make_root()?) }) diff --git a/src/properties.rs b/src/properties.rs index ada74a2..531605d 100644 --- a/src/properties.rs +++ b/src/properties.rs @@ -1,10 +1,10 @@ -use core::{ffi::CStr, ops::Shl}; +use core::ffi::CStr; use crate::{ nodes::Node, parsing::{ - aligned::AlignedParser, unaligned::UnalignedParser, BigEndianU32, NoPanic, Panic, - PanicMode, Parser, ParserWithMode, StringsBlock, StructsBlock, + aligned::AlignedParser, unaligned::UnalignedParser, BigEndianU32, NoPanic, Panic, Parser, + ParserWithMode, StringsBlock, StructsBlock, }, standard_nodes::Root, FdtError, @@ -112,6 +112,16 @@ impl< } } +impl CellCollector for u32 { + type Output = Self; + type Builder = BuildIntCollector; + + #[inline(always)] + fn map(builder_out: as BuildCellCollector>::Output) -> Self::Output { + builder_out + } +} + impl CellCollector for u64 { type Output = Self; type Builder = BuildIntCollector; @@ -122,6 +132,16 @@ impl CellCollector for u64 { } } +impl CellCollector for u128 { + type Output = Self; + type Builder = BuildIntCollector; + + #[inline(always)] + fn map(builder_out: as BuildCellCollector>::Output) -> Self::Output { + builder_out + } +} + impl CellCollector for Option { type Builder = BuildOptionalCellCollector; type Output = Option; @@ -321,10 +341,10 @@ impl BuildCellCollector for PciAddressCollector { } } -pub trait Property<'a, P: Parser<'a>>: Sized { +pub trait Property<'a, P: ParserWithMode<'a>>: Sized { fn parse( - node: Node<'a, (P, NoPanic)>, - root: Root<'a, (P, NoPanic)>, + node: Node<'a, (P::Parser, NoPanic)>, + root: Root<'a, (P::Parser, NoPanic)>, ) -> Result, FdtError>; } @@ -358,10 +378,10 @@ pub struct Compatible<'a> { string: &'a str, } -impl<'a, P: Parser<'a>> Property<'a, P> for Compatible<'a> { +impl<'a, P: ParserWithMode<'a>> Property<'a, P> for Compatible<'a> { fn parse( - node: Node<'a, (P, NoPanic)>, - _: Root<'a, (P, NoPanic)>, + node: Node<'a, (P::Parser, NoPanic)>, + _: Root<'a, (P::Parser, NoPanic)>, ) -> Result, FdtError> { let property = node.properties()?.find("compatible")?; @@ -418,10 +438,10 @@ impl<'a> Iterator for CompatibleIter<'a> { #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Model<'a>(&'a str); -impl<'a, P: Parser<'a>> Property<'a, P> for Model<'a> { +impl<'a, P: ParserWithMode<'a>> Property<'a, P> for Model<'a> { fn parse( - node: Node<'a, (P, NoPanic)>, - _: Root<'a, (P, NoPanic)>, + node: Node<'a, (P::Parser, NoPanic)>, + _: Root<'a, (P::Parser, NoPanic)>, ) -> Result, FdtError> { match node.properties()?.find("model")? { Some(model) => Ok(Some(Self(model.to()?))), @@ -476,10 +496,10 @@ impl<'a> core::cmp::PartialEq> for str { #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct PHandle(BigEndianU32); -impl<'a, P: Parser<'a>> Property<'a, P> for PHandle { +impl<'a, P: ParserWithMode<'a>> Property<'a, P> for PHandle { fn parse( - node: Node<'a, (P, NoPanic)>, - _: Root<'a, (P, NoPanic)>, + node: Node<'a, (P::Parser, NoPanic)>, + _: Root<'a, (P::Parser, NoPanic)>, ) -> Result, FdtError> { let Some(phandle) = node.properties()?.find("phandle")? else { return Ok(None); @@ -546,10 +566,10 @@ impl<'a> Status<'a> { } } -impl<'a, P: Parser<'a>> Property<'a, P> for Status<'a> { +impl<'a, P: ParserWithMode<'a>> Property<'a, P> for Status<'a> { fn parse( - node: Node<'a, (P, NoPanic)>, - _: Root<'a, (P, NoPanic)>, + node: Node<'a, (P::Parser, NoPanic)>, + _: Root<'a, (P::Parser, NoPanic)>, ) -> Result, FdtError> { match node.properties()?.find("status")? { Some(model) => Ok(Some(Self(model.to()?))), @@ -627,11 +647,11 @@ pub struct CellSizes { pub size_cells: usize, } -impl<'a, P: Parser<'a>> Property<'a, P> for CellSizes { +impl<'a, P: ParserWithMode<'a>> Property<'a, P> for CellSizes { #[inline] fn parse( - node: Node<'a, (P, NoPanic)>, - _: Root<'a, (P, NoPanic)>, + node: Node<'a, (P::Parser, NoPanic)>, + _: Root<'a, (P::Parser, NoPanic)>, ) -> Result, FdtError> { let (mut address_cells, mut size_cells) = (None, None); @@ -662,11 +682,11 @@ impl Default for CellSizes { #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct AddressCells(pub usize); -impl<'a, P: Parser<'a>> Property<'a, P> for AddressCells { +impl<'a, P: ParserWithMode<'a>> Property<'a, P> for AddressCells { #[inline] fn parse( - node: Node<'a, (P, NoPanic)>, - _: Root<'a, (P, NoPanic)>, + node: Node<'a, (P::Parser, NoPanic)>, + _: Root<'a, (P::Parser, NoPanic)>, ) -> Result, FdtError> { match node.properties()?.find("#address-cells")? { Some(value) => Ok(Some(Self(value.to()?))), @@ -699,10 +719,10 @@ impl<'a> Reg<'a> { } } -impl<'a, P: Parser<'a>> Property<'a, P> for Reg<'a> { +impl<'a, P: ParserWithMode<'a>> Property<'a, P> for Reg<'a> { fn parse( - node: Node<'a, (P, NoPanic)>, - _: Root<'a, (P, NoPanic)>, + node: Node<'a, (P::Parser, NoPanic)>, + _: Root<'a, (P::Parser, NoPanic)>, ) -> Result, FdtError> { let Some(prop) = node.raw_property("reg")? else { return Ok(None); @@ -827,10 +847,10 @@ impl VirtualReg { } } -impl<'a, P: Parser<'a>> Property<'a, P> for VirtualReg { +impl<'a, P: ParserWithMode<'a>> Property<'a, P> for VirtualReg { fn parse( - node: Node<'a, (P, NoPanic)>, - _: Root<'a, (P, NoPanic)>, + node: Node<'a, (P::Parser, NoPanic)>, + _: Root<'a, (P::Parser, NoPanic)>, ) -> Result, FdtError> { match node.properties()?.find("virtual-reg")? { Some(vreg) => Ok(Some(Self(vreg.to()?))), @@ -853,10 +873,10 @@ pub struct Ranges<'a> { /// property is not applicable. pub struct DmaCoherent; -impl<'a, P: Parser<'a>> Property<'a, P> for DmaCoherent { +impl<'a, P: ParserWithMode<'a>> Property<'a, P> for DmaCoherent { fn parse( - node: Node<'a, (P, NoPanic)>, - _: Root<'a, (P, NoPanic)>, + node: Node<'a, (P::Parser, NoPanic)>, + _: Root<'a, (P::Parser, NoPanic)>, ) -> Result, FdtError> { match node.properties()?.find("dma-coherent")? { Some(_) => Ok(Some(Self)), @@ -873,12 +893,10 @@ pub enum Interrupts<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { Extended(ExtendedInterrupts<'a, P>), } -impl<'a, P: Parser<'a>, Mode: PanicMode + Clone + Default + 'static> Property<'a, P> - for Interrupts<'a, (P, Mode)> -{ +impl<'a, P: ParserWithMode<'a>> Property<'a, P> for Interrupts<'a, P> { fn parse( - node: Node<'a, (P, NoPanic)>, - root: Root<'a, (P, NoPanic)>, + node: Node<'a, (P::Parser, NoPanic)>, + root: Root<'a, (P::Parser, NoPanic)>, ) -> Result, FdtError> { match ExtendedInterrupts::parse(node, root)? { Some(extended) => Ok(Some(Self::Extended(extended))), @@ -907,26 +925,24 @@ pub struct LegacyInterrupts<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Pani encoded_array: &'a [u8], } -impl<'a, P: Parser<'a>, Mode: PanicMode + Clone + Default + 'static> Property<'a, P> - for LegacyInterrupts<'a, (P, Mode)> -{ +impl<'a, P: ParserWithMode<'a>> Property<'a, P> for LegacyInterrupts<'a, P> { fn parse( - node: Node<'a, (P, NoPanic)>, - root: Root<'a, (P, NoPanic)>, + node: Node<'a, (P::Parser, NoPanic)>, + root: Root<'a, (P::Parser, NoPanic)>, ) -> Result, FdtError> { match node.properties()?.find("interrupts")? { Some(interrupts) => { - let interrupt_parent = match InterruptParent::<'a, (P, NoPanic)>::parse(node, root)? - { - Some(p) => p, - None => return Err(FdtError::MissingRequiredProperty("interrupt-parent")), - }; + let interrupt_parent = + match InterruptParent::<(P::Parser, NoPanic)>::parse(node, root)? { + Some(p) => p, + None => return Err(FdtError::MissingRequiredProperty("interrupt-parent")), + }; let Some(interrupt_cells) = interrupt_parent.property::()? else { return Err(FdtError::MissingRequiredProperty("interrupt-cells")); }; - if interrupts.value().len() % (interrupt_cells.0 * 4) as usize != 0 { + if interrupts.value().len() % (interrupt_cells.0 * 4) != 0 { return Err(FdtError::InvalidPropertyValue); } @@ -969,12 +985,10 @@ impl<'a, P: ParserWithMode<'a>> ExtendedInterrupts<'a, P> { } } -impl<'a, P: Parser<'a>, Mode: PanicMode + Clone + Default + 'static> Property<'a, P> - for ExtendedInterrupts<'a, (P, Mode)> -{ +impl<'a, P: ParserWithMode<'a>> Property<'a, P> for ExtendedInterrupts<'a, P> { fn parse( - node: Node<'a, (P, NoPanic)>, - root: Root<'a, (P, NoPanic)>, + node: Node<'a, (P::Parser, NoPanic)>, + root: Root<'a, (P::Parser, NoPanic)>, ) -> Result, FdtError> { match node.properties()?.find("interrupts-extended")? { Some(interrupts) => Ok(Some(Self { @@ -1012,7 +1026,7 @@ impl<'a, P: ParserWithMode<'a>> Iterator for ExtendedInterruptsIter<'a, P> { return Err(FdtError::MissingRequiredProperty("#interrupt-cells")); }; - let cells_length = 4 * interrupt_cells.0 as usize; + let cells_length = interrupt_cells.0 * 4; let encoded_array = match self.encoded_array.get(..cells_length) { Some(bytes) => bytes, None => return Ok(None), @@ -1176,12 +1190,10 @@ impl<'a, P: ParserWithMode<'a>> core::ops::DerefMut for InterruptParent<'a, P> { } } -impl<'a, P: Parser<'a>, Mode: PanicMode + Clone + Default + 'static> Property<'a, P> - for InterruptParent<'a, (P, Mode)> -{ +impl<'a, P: ParserWithMode<'a>> Property<'a, P> for InterruptParent<'a, P> { fn parse( - node: Node<'a, (P, NoPanic)>, - root: Root<'a, (P, NoPanic)>, + node: Node<'a, (P::Parser, NoPanic)>, + root: Root<'a, (P::Parser, NoPanic)>, ) -> Result, FdtError> { match node.properties()?.find("interrupt-parent")? { Some(phandle) => match root.resolve_phandle(PHandle(phandle.to()?))? { @@ -1201,10 +1213,10 @@ impl<'a, P: Parser<'a>, Mode: PanicMode + Clone + Default + 'static> Property<'a #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct InterruptCells(pub usize); -impl<'a, P: Parser<'a>> Property<'a, P> for InterruptCells { +impl<'a, P: ParserWithMode<'a>> Property<'a, P> for InterruptCells { fn parse( - node: Node<'a, (P, NoPanic)>, - _: Root<'a, (P, NoPanic)>, + node: Node<'a, (P::Parser, NoPanic)>, + _: Root<'a, (P::Parser, NoPanic)>, ) -> Result, FdtError> { match node.properties()?.find("#interrupt-cells")? { Some(ic) => Ok(Some(Self(ic.to()?))), @@ -1246,12 +1258,12 @@ impl InterruptMapMask> Property<'a, P> +impl<'a, ADDRMASK: CellCollector, INTMASK: CellCollector, P: ParserWithMode<'a>> Property<'a, P> for InterruptMapMask { fn parse( - node: Node<'a, (P, NoPanic)>, - _: Root<'a, (P, NoPanic)>, + node: Node<'a, (P::Parser, NoPanic)>, + _: Root<'a, (P::Parser, NoPanic)>, ) -> Result, FdtError> { let address_cells = node .property::()? @@ -1277,7 +1289,7 @@ impl<'a, ADDRMASK: CellCollector, INTMASK: CellCollector, P: Parser<'a>> Propert address_collector.push(u32::from_be_bytes(chunk.try_into().unwrap()))?; } - for chunk in cells.take(interrupt_cells.0 as usize) { + for chunk in cells.take(interrupt_cells.0) { specifier_collector.push(u32::from_be_bytes(chunk.try_into().unwrap()))?; } @@ -1299,10 +1311,10 @@ impl<'a, ADDRMASK: CellCollector, INTMASK: CellCollector, P: Parser<'a>> Propert #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct InterruptController; -impl<'a, P: Parser<'a>> Property<'a, P> for InterruptController { +impl<'a, P: ParserWithMode<'a>> Property<'a, P> for InterruptController { fn parse( - node: Node<'a, (P, NoPanic)>, - _: Root<'a, (P, NoPanic)>, + node: Node<'a, (P::Parser, NoPanic)>, + _: Root<'a, (P::Parser, NoPanic)>, ) -> Result, FdtError> { match node.properties()?.find("interrupt-controller")? { Some(_) => Ok(Some(Self)), @@ -1314,9 +1326,9 @@ impl<'a, P: Parser<'a>> Property<'a, P> for InterruptController { pub struct InterruptMap< 'a, CADDR: CellCollector, - CINT: CellCollector, - PADDR: CellCollector, - PINT: CellCollector, + CINT: CellCollector = u32, + PADDR: CellCollector = u64, + PINT: CellCollector = u32, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic), > { address_cells: AddressCells, @@ -1356,17 +1368,16 @@ impl< impl< 'a, - P: Parser<'a>, - Mode: PanicMode + Clone + Default + 'static, + P: ParserWithMode<'a>, CADDR: CellCollector, CINT: CellCollector, PADDR: CellCollector, PINT: CellCollector, - > Property<'a, P> for InterruptMap<'a, CADDR, CINT, PADDR, PINT, (P, Mode)> + > Property<'a, P> for InterruptMap<'a, CADDR, CINT, PADDR, PINT, P> { fn parse( - node: Node<'a, (P, NoPanic)>, - _: Root<'a, (P, NoPanic)>, + node: Node<'a, (P::Parser, NoPanic)>, + _: Root<'a, (P::Parser, NoPanic)>, ) -> Result, FdtError> { let Some(encoded_map) = node.properties()?.find("interrupt-map")? else { return Ok(None) }; diff --git a/src/standard_nodes.rs b/src/standard_nodes.rs index 33a63ab..fd11f8f 100644 --- a/src/standard_nodes.rs +++ b/src/standard_nodes.rs @@ -198,7 +198,7 @@ impl<'a, P: ParserWithMode<'a>> Root<'a, P> { /// For example: `compatible = "fsl,mpc8572ds"` pub fn compatible(self) -> P::Output> { P::to_output(crate::tryblock! { - Compatible::parse(self.node.fallible(), self.node.make_root()?)? + >::parse(self.node.fallible(), self.node.make_root()?)? .ok_or(FdtError::MissingRequiredProperty("compatible")) }) } diff --git a/src/tests.rs b/src/tests.rs index afc2750..8d531cf 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -288,7 +288,7 @@ fn interrupt_map() { for (entry, expected) in root .find_node("/soc/pci") .unwrap() - .property::, u64, (AlignedParser<'_>, Panic)>>() + .property::, u64>>() .unwrap() .iter() .zip(entries) From 1c0d92188c944f56283bf371d2ed2917802f2dc6 Mon Sep 17 00:00:00 2001 From: repnop Date: Fri, 12 Jul 2024 14:47:30 -0400 Subject: [PATCH 12/38] stuff and or things --- src/nodes.rs | 2 +- src/properties.rs | 71 +++++++++++++++++++++++++++++++------------ src/standard_nodes.rs | 6 ++-- 3 files changed, 55 insertions(+), 24 deletions(-) diff --git a/src/nodes.rs b/src/nodes.rs index 05e7a2f..defb87c 100644 --- a/src/nodes.rs +++ b/src/nodes.rs @@ -346,7 +346,7 @@ impl<'a> NodeProperty<'a> { self.value } - pub fn to>(&self) -> Result { + pub fn as_value>(&self) -> Result { V::parse(self.value) } } diff --git a/src/properties.rs b/src/properties.rs index 531605d..be9261b 100644 --- a/src/properties.rs +++ b/src/properties.rs @@ -386,7 +386,7 @@ impl<'a, P: ParserWithMode<'a>> Property<'a, P> for Compatible<'a> { let property = node.properties()?.find("compatible")?; match property { - Some(prop) => Ok(Some(Self { string: prop.to()? })), + Some(prop) => Ok(Some(Self { string: prop.as_value()? })), None => Ok(None), } } @@ -444,7 +444,7 @@ impl<'a, P: ParserWithMode<'a>> Property<'a, P> for Model<'a> { _: Root<'a, (P::Parser, NoPanic)>, ) -> Result, FdtError> { match node.properties()?.find("model")? { - Some(model) => Ok(Some(Self(model.to()?))), + Some(model) => Ok(Some(Self(model.as_value()?))), None => Ok(None), } } @@ -505,7 +505,7 @@ impl<'a, P: ParserWithMode<'a>> Property<'a, P> for PHandle { return Ok(None); }; - Ok(Some(PHandle(phandle.to()?))) + Ok(Some(PHandle(phandle.as_value()?))) } } @@ -572,7 +572,7 @@ impl<'a, P: ParserWithMode<'a>> Property<'a, P> for Status<'a> { _: Root<'a, (P::Parser, NoPanic)>, ) -> Result, FdtError> { match node.properties()?.find("status")? { - Some(model) => Ok(Some(Self(model.to()?))), + Some(model) => Ok(Some(Self(model.as_value()?))), None => Ok(None), } } @@ -689,7 +689,7 @@ impl<'a, P: ParserWithMode<'a>> Property<'a, P> for AddressCells { _: Root<'a, (P::Parser, NoPanic)>, ) -> Result, FdtError> { match node.properties()?.find("#address-cells")? { - Some(value) => Ok(Some(Self(value.to()?))), + Some(value) => Ok(Some(Self(value.as_value()?))), None => Ok(None), } } @@ -853,7 +853,7 @@ impl<'a, P: ParserWithMode<'a>> Property<'a, P> for VirtualReg { _: Root<'a, (P::Parser, NoPanic)>, ) -> Result, FdtError> { match node.properties()?.find("virtual-reg")? { - Some(vreg) => Ok(Some(Self(vreg.to()?))), + Some(vreg) => Ok(Some(Self(vreg.as_value()?))), None => Ok(None), } } @@ -1196,9 +1196,9 @@ impl<'a, P: ParserWithMode<'a>> Property<'a, P> for InterruptParent<'a, P> { root: Root<'a, (P::Parser, NoPanic)>, ) -> Result, FdtError> { match node.properties()?.find("interrupt-parent")? { - Some(phandle) => match root.resolve_phandle(PHandle(phandle.to()?))? { + Some(phandle) => match root.resolve_phandle(PHandle(phandle.as_value()?))? { Some(parent) => Ok(Some(Self(parent.alt()))), - None => Err(FdtError::PHandleNotFound(phandle.to()?)), + None => Err(FdtError::PHandleNotFound(phandle.as_value()?)), }, None => Ok(node.parent().map(|n| Self(n.alt()))), } @@ -1219,7 +1219,7 @@ impl<'a, P: ParserWithMode<'a>> Property<'a, P> for InterruptCells { _: Root<'a, (P::Parser, NoPanic)>, ) -> Result, FdtError> { match node.properties()?.find("#interrupt-cells")? { - Some(ic) => Ok(Some(Self(ic.to()?))), + Some(ic) => Ok(Some(Self(ic.as_value()?))), None => Ok(None), } } @@ -1361,8 +1361,39 @@ impl< self, address: CADDR::Output, interrupt_specifier: CINT::Output, - ) -> P::Output>> { - todo!() + ) -> P::Output>> + where + for<'b> &'b CADDR::Output: PartialEq, + for<'b> &'b CINT::Output: PartialEq, + { + let this: InterruptMap<_, _, _, _, (P::Parser, NoPanic)> = InterruptMap { + address_cells: self.address_cells, + interrupt_cells: self.interrupt_cells, + node: self.node, + encoded_map: self.encoded_map, + _collectors: self._collectors, + }; + + P::to_output( + this.iter() + .find(|e| match e { + Err(_) => true, + Ok(entry) => { + entry.child_unit_address() == &address + && entry.child_interrupt_specifier() == &interrupt_specifier + } + }) + .transpose() + .map(|e| { + e.map(|e| InterruptMapEntry::<_, _, _, _, P> { + child_address: e.child_address, + child_interrupt_specifier: e.child_interrupt_specifier, + interrupt_parent: e.interrupt_parent.alt(), + parent_address: e.parent_address, + parent_interrupt_specifier: e.parent_interrupt_specifier, + }) + }), + ) } } @@ -1517,28 +1548,28 @@ impl< > InterruptMapEntry<'a, CADDR, CINT, PADDR, PINT, P> { #[inline(always)] - pub fn interrupt_parent(self) -> Node<'a, P> { + pub fn interrupt_parent(&self) -> Node<'a, P> { self.interrupt_parent } #[inline(always)] - pub fn child_unit_address(self) -> CADDR::Output { - self.child_address + pub fn child_unit_address(&self) -> &CADDR::Output { + &self.child_address } #[inline(always)] - pub fn child_interrupt_specifier(self) -> CINT::Output { - self.child_interrupt_specifier + pub fn child_interrupt_specifier(&self) -> &CINT::Output { + &self.child_interrupt_specifier } #[inline(always)] - pub fn parent_unit_address(self) -> PADDR::Output { - self.parent_address + pub fn parent_unit_address(&self) -> &PADDR::Output { + &self.parent_address } #[inline(always)] - pub fn parent_interrupt_specifier(self) -> PINT::Output { - self.parent_interrupt_specifier + pub fn parent_interrupt_specifier(&self) -> &PINT::Output { + &self.parent_interrupt_specifier } } diff --git a/src/standard_nodes.rs b/src/standard_nodes.rs index fd11f8f..cfd4430 100644 --- a/src/standard_nodes.rs +++ b/src/standard_nodes.rs @@ -48,7 +48,7 @@ impl<'a, P: ParserWithMode<'a> + 'a> Chosen<'a, P> { false => None, true => Some( property - .to::<&'a str>() + .as_value::<&'a str>() .map_err(Into::into) .map(|s| { let (path, params) = Self::split_stdinout_property(s); @@ -75,7 +75,7 @@ impl<'a, P: ParserWithMode<'a> + 'a> Chosen<'a, P> { false => None, true => Some( property - .to::<&'a str>() + .as_value::<&'a str>() .map_err(Into::into) .map(|s| { let (path, params) = Self::split_stdinout_property(s); @@ -175,7 +175,7 @@ impl<'a, P: ParserWithMode<'a>> Root<'a, P> { false => None, true => Some( property - .to::<&'a str>() + .as_value::<&'a str>() .map_err(Into::into) ), }, From cbdc150f9aacc131aeacdfe13a5e8ec3a0afb201 Mon Sep 17 00:00:00 2001 From: repnop Date: Sat, 13 Jul 2024 17:03:41 -0400 Subject: [PATCH 13/38] :waa: --- src/lib.rs | 24 +- src/nodes.rs | 53 ++-- src/parsing.rs | 2 +- src/pretty_print.rs | 308 +++++++++++++++-------- src/properties.rs | 555 ++++++++++++++++++++++++++++++------------ src/standard_nodes.rs | 132 +++++----- src/tests.rs | 13 +- 7 files changed, 747 insertions(+), 340 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index df8f6b8..05c8018 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -137,11 +137,25 @@ impl<'a, P: ParserWithMode<'a>> core::fmt::Debug for Fdt<'a, P> { } } -// impl<'a, P: Parser<'a>> core::fmt::Display for Fdt<'a, P> { -// fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { -// pretty_print::print_node(f, self.root().node, 0) -// } -// } +impl<'a, P: ParserWithMode<'a> + 'a> core::fmt::Display for Fdt<'a, P> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let this: Fdt<'a, (P::Parser, NoPanic)> = Fdt { + _lifetime: core::marker::PhantomData, + header: self.header, + parser: <(P::Parser, NoPanic)>::new( + self.parser.data(), + self.parser.strings(), + self.parser.structs(), + ), + }; + + let Ok(root) = this.root() else { + return Err(core::fmt::Error); + }; + + pretty_print::print_fdt(f, root) + } +} #[derive(Debug, Clone, Copy)] #[repr(C)] diff --git a/src/nodes.rs b/src/nodes.rs index defb87c..35a0985 100644 --- a/src/nodes.rs +++ b/src/nodes.rs @@ -3,7 +3,7 @@ use crate::{ aligned::AlignedParser, BigEndianToken, NoPanic, Panic, PanicMode, ParseError, Parser, ParserWithMode, StringsBlock, StructsBlock, }, - properties::{InvalidPropertyValue, Property, PropertyValue}, + properties::{InvalidPropertyValue, Property, PropertyValue, Reg}, standard_nodes::Root, FdtError, }; @@ -11,10 +11,11 @@ use crate::{ #[macro_export] #[doc(hidden)] macro_rules! tryblock { - ($($ts:tt)+) => {{ - (|| -> Result<_, $crate::FdtError> { - $($ts)+ - })() + ($errty:ty, $block:block) => {{ + (|| -> Result<_, $errty> { $block })() + }}; + ($block:block) => {{ + (|| -> Result<_, $crate::FdtError> { $block })() }}; } @@ -78,19 +79,15 @@ pub struct Node<'a, P: ParserWithMode<'a>> { } impl<'a, P: ParserWithMode<'a>> Node<'a, P> { + /// Change the type of this node's [`PanicMode`] to [`NoPanic`] #[inline(always)] pub(crate) fn fallible(self) -> Node<'a, (P::Parser, NoPanic)> { - Node { - this: self.this, - parent: self.parent, - strings: self.strings, - structs: self.structs, - _mode: core::marker::PhantomData, - } + self.alt() } + /// Helper function for changing the [`PanicMode`] of this node #[inline(always)] - pub(crate) fn alt>(self) -> Node<'a, P2> { + pub fn alt>(self) -> Node<'a, P2> { Node { this: self.this, parent: self.parent, @@ -126,6 +123,10 @@ impl<'a, P: ParserWithMode<'a>> Node<'a, P> { ) } + pub fn reg(&self) -> P::Output>> { + self.property::>() + } + #[inline] pub fn properties(&self) -> P::Output> { let mut parser = P::new(&self.this.0, self.strings, self.structs); @@ -141,26 +142,30 @@ impl<'a, P: ParserWithMode<'a>> Node<'a, P> { #[inline] pub fn raw_property(&self, name: &str) -> P::Output>> { - P::to_output(tryblock! { + P::to_output(tryblock!({ let this = self.fallible(); this.properties()?.find(name) - }) + })) } #[track_caller] pub fn property>(&self) -> P::Output> { - P::to_output(crate::tryblock! { - Prop::parse(self.alt(), self.make_root()?) - }) + P::to_output(crate::tryblock!({ Prop::parse(self.alt(), self.make_root()?) })) } #[inline] pub fn children(&self) -> P::Output> { - P::to_output(tryblock! { + P::to_output(tryblock!({ let mut parser = P::new(&self.this.0, self.strings, self.structs); parser.advance_cstr()?; - while let BigEndianToken::PROP = parser.peek_token()? { - parser.parse_raw_property()?; + + loop { + match parser.peek_token() { + Ok(BigEndianToken::PROP) => parser.parse_raw_property()?, + Ok(BigEndianToken::BEGIN_NODE) => break, + Ok(_) | Err(FdtError::ParseError(ParseError::UnexpectedEndOfData)) => break, + Err(e) => return Err(e), + }; } Ok(NodeChildren { @@ -170,7 +175,7 @@ impl<'a, P: ParserWithMode<'a>> Node<'a, P> { structs: self.structs, _mode: core::marker::PhantomData, }) - }) + })) } #[inline] @@ -257,7 +262,7 @@ impl<'a, P: ParserWithMode<'a>> NodeProperties<'a, P> { Err(e) => return P::to_output(Err(e)), } - P::to_output(tryblock! { + P::to_output(tryblock!({ match parser.parse_raw_property() { Ok((name_offset, data)) => { self.data = parser.data(); @@ -267,7 +272,7 @@ impl<'a, P: ParserWithMode<'a>> NodeProperties<'a, P> { Err(FdtError::ParseError(ParseError::UnexpectedEndOfData)) => Ok(None), Err(e) => return Err(e), } - }) + })) } pub fn find(&self, name: &str) -> P::Output>> { diff --git a/src/parsing.rs b/src/parsing.rs index 143c183..3bbc81f 100644 --- a/src/parsing.rs +++ b/src/parsing.rs @@ -243,7 +243,7 @@ impl<'a, T: Parser<'a>, U: PanicMode + Clone + Default + 'static> ParserWithMode } pub trait Parser<'a>: crate::sealed::Sealed + Clone { - type Granularity: Copy; + type Granularity: Copy + core::fmt::Debug; fn new( data: &'a [Self::Granularity], diff --git a/src/pretty_print.rs b/src/pretty_print.rs index 771002f..a9a1524 100644 --- a/src/pretty_print.rs +++ b/src/pretty_print.rs @@ -2,99 +2,215 @@ // v. 2.0. If a copy of the MPL was not distributed with this file, You can // obtain one at https://mozilla.org/MPL/2.0/. -// pub fn print_node( -// f: &mut core::fmt::Formatter<'_>, -// node: crate::node::FdtNode<'_, '_>, -// n_spaces: usize, -// ) -> core::fmt::Result { -// write!(f, "{:width$}", ' ', width = n_spaces)?; -// writeln!(f, "{} {{", if node.name.is_empty() { "/" } else { node.name })?; -// let mut were_props = false; -// for prop in node.properties() { -// were_props = true; - -// match prop.name { -// "reg" => { -// write!(f, "{:width$}reg = <", ' ', width = n_spaces + 4)?; -// for (i, reg) in node.reg().unwrap().enumerate() { -// if i > 0 { -// write!(f, " ")?; -// } - -// match reg.size { -// Some(size) => { -// write!(f, "{:#x} {:#x}", reg.starting_address as usize, size)? -// } -// None => write!(f, "{:#x}", reg.starting_address as usize)?, -// } -// } -// writeln!(f, ">")?; -// } -// "compatible" => writeln!( -// f, -// "{:width$}compatible = {:?}", -// ' ', -// prop.as_str().unwrap(), -// width = n_spaces + 4 -// )?, -// name if name.contains("-cells") => { -// writeln!( -// f, -// "{:width$}{} = <{:#x}>", -// ' ', -// name, -// prop.as_usize().unwrap(), -// width = n_spaces + 4 -// )?; -// } -// _ => match prop.as_str() { -// Some(value) -// if (!value.is_empty() && value.chars().all(|c| c.is_ascii_graphic())) -// || prop.value == [0] => -// { -// writeln!(f, "{:width$}{} = {:?}", ' ', prop.name, value, width = n_spaces + 4)? -// } -// _ => match prop.value.len() { -// 4 | 8 => writeln!( -// f, -// "{:width$}{} = <{:#x}>", -// ' ', -// prop.name, -// prop.as_usize().unwrap(), -// width = n_spaces + 4 -// )?, -// _ => writeln!( -// f, -// "{:width$}{} = {:?}", -// ' ', -// prop.name, -// prop.value, -// width = n_spaces + 4 -// )?, -// }, -// }, -// } -// } - -// if node.children().next().is_some() && were_props { -// writeln!(f)?; -// } - -// let mut first = true; -// for child in node.children() { -// if !first { -// writeln!(f)?; -// } - -// print_node(f, child, n_spaces + 4)?; -// first = false; -// } - -// if n_spaces > 0 { -// write!(f, "{:width$}", ' ', width = n_spaces)?; -// } - -// writeln!(f, "}};")?; - -// Ok(()) -// } +use crate::{ + nodes::{Node, NodeName}, + parsing::{NoPanic, Parser}, + properties::{CollectCellsError, InvalidPropertyValue, U32List}, + standard_nodes::Root, + FdtError, +}; + +#[derive(Debug)] +pub struct Error; + +impl From for Error { + #[track_caller] + fn from(e: FdtError) -> Self { + #[cfg(test)] + std::println!("{e:?}, {}", core::panic::Location::caller()); + Error + } +} + +impl From for Error { + #[track_caller] + fn from(e: CollectCellsError) -> Self { + #[cfg(test)] + std::println!("{e:?}, {}", core::panic::Location::caller()); + Error + } +} + +impl From for Error { + #[track_caller] + fn from(e: InvalidPropertyValue) -> Self { + #[cfg(test)] + std::println!("{e:?}, {}", core::panic::Location::caller()); + Error + } +} + +impl From for Error { + fn from(_: core::fmt::Error) -> Self { + Error + } +} + +impl From for core::fmt::Error { + fn from(_: Error) -> Self { + core::fmt::Error + } +} + +pub fn print_fdt<'a, P: Parser<'a> + 'a>( + f: &mut core::fmt::Formatter<'_>, + root: Root<'a, (P, NoPanic)>, +) -> core::fmt::Result { + let res = crate::tryblock!(Error, { + let mut any_children = true; + let mut any_props; + let mut node_iter = root.all_nodes()?.peekable(); + let (mut n_braces, mut final_depth) = (0, 0); + writeln!(f, "/ {{")?; + any_props = print_properties(f, root.node, 0)?; + loop { + let Some((depth, node)) = node_iter.next().transpose()? else { break }; + let next_depth = match node_iter.peek().cloned().transpose()? { + Some((next_depth, _)) => next_depth, + None => 0, + }; + let next_is_child = next_depth > depth; + any_children = true; + + if n_braces > 0 { + for _ in (0..n_braces).rev() { + final_depth -= 1; + writeln!(f, "{:width$}}};", ' ', width = (final_depth) * 4)?; + } + + n_braces = 0; + } + + if any_props { + writeln!(f)?; + } + + writeln!( + f, + "{:width$}{} {{", + ' ', + if node.name()?.name.is_empty() { + NodeName { name: "/", unit_address: None } + } else { + node.name()? + }, + width = depth * 4, + )?; + + any_props = print_properties(f, node, depth)?; + + if !any_props && !next_is_child { + writeln!(f)?; + } + + if next_depth <= depth { + writeln!(f, "{:width$}}};", ' ', width = depth * 4)?; + + if !any_props && !next_is_child { + writeln!(f)?; + } + } + + if depth > next_depth { + n_braces += depth - next_depth; + final_depth = depth; + } else { + n_braces = 0; + final_depth = 0; + } + } + + if any_children { + writeln!(f, " }};")?; + } + + write!(f, "}};")?; + + Ok(()) + }); + + #[cfg(test)] + std::println!("{res:?}"); + + Ok(res?) +} + +fn print_properties<'a, P: Parser<'a>>( + f: &mut core::fmt::Formatter<'_>, + node: Node<'a, (P, NoPanic)>, + depth: usize, +) -> Result { + let mut any_props = false; + for prop in node.properties()? { + any_props = true; + let prop = prop?; + + match prop.name() { + "reg" => { + write!(f, "{:width$}reg = <", ' ', width = depth * 4 + 4)?; + for (i, reg) in node.reg()?.unwrap().iter::>().enumerate() { + let reg = reg?; + if i > 0 { + write!(f, " ")?; + } + + match reg.len { + Some(size) => write!(f, "{:#04x} {:#04x}", reg.address as usize, size)?, + None => write!(f, "{:#04x}", reg.address as usize)?, + } + } + writeln!(f, ">;")?; + } + "compatible" => writeln!( + f, + "{:width$}compatible = {:?};", + ' ', + prop.as_value::<&str>()?, + width = depth * 4 + 4 + )?, + name if name.contains("-cells") => { + writeln!( + f, + "{:width$}{} = <{:#04x}>;", + ' ', + name, + prop.as_value::()?, + width = depth * 4 + 4 + )?; + } + _ => match prop.as_value::<&str>() { + Ok(value) + if (!value.is_empty() && value.chars().all(|c| c.is_ascii_graphic())) + || prop.value() == [0] => + { + writeln!( + f, + "{:width$}{} = {:?};", + ' ', + prop.name(), + value, + width = depth * 4 + 4 + )? + } + _ => match prop.value().len() { + 0 => writeln!(f, "{:width$}{};", ' ', prop.name(), width = depth * 4 + 4)?, + _ => { + write!(f, "{:width$}{} = <", ' ', prop.name(), width = depth * 4 + 4)?; + + for (i, n) in prop.as_value::()?.iter().enumerate() { + if i != 0 { + write!(f, " ")?; + } + + write!(f, "{n:#04x}")?; + } + + writeln!(f, ">;")?; + } + }, + }, + } + } + + Ok(any_props) +} diff --git a/src/properties.rs b/src/properties.rs index be9261b..e66b657 100644 --- a/src/properties.rs +++ b/src/properties.rs @@ -1,3 +1,7 @@ +// This Source Code Form is subject to the terms of the Mozilla Public License, +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at https://mozilla.org/MPL/2.0/. + use core::ffi::CStr; use crate::{ @@ -142,6 +146,16 @@ impl CellCollector for u128 { } } +impl CellCollector for usize { + type Output = Self; + type Builder = UsizeCollector; + + #[inline(always)] + fn map(builder_out: ::Output) -> Self::Output { + builder_out + } +} + impl CellCollector for Option { type Builder = BuildOptionalCellCollector; type Output = Option; @@ -151,6 +165,40 @@ impl CellCollector for Option { } } +#[derive(Default)] +pub struct UsizeCollector { + value: usize, +} + +impl BuildCellCollector for UsizeCollector { + type Output = usize; + + #[inline(always)] + fn push(&mut self, component: u32) -> Result<(), CollectCellsError> { + use core::ops::{BitOr, Shl}; + + let shr = const { + match core::mem::size_of::().checked_sub(4) { + Some(value) => value as u32 * 8, + None => panic!("integer type too small"), + } + }; + + if self.value >> shr != 0 { + return Err(CollectCellsError); + } + + self.value = self.value.shl(32i32).bitor(component as usize); + + Ok(()) + } + + #[inline(always)] + fn finish(self) -> Self::Output { + self.value + } +} + pub struct BuildOptionalCellCollector { builder: T::Builder, used: bool, @@ -218,6 +266,18 @@ impl CellCollector for PciAddress { } } +impl PartialEq<&'_ PciAddress> for PciAddress { + fn eq(&self, other: &&'_ Self) -> bool { + self.eq(*other) + } +} + +impl PartialEq for &'_ PciAddress { + fn eq(&self, other: &PciAddress) -> bool { + (*self).eq(other) + } +} + /// `phys.hi cell: npt000ss bbbbbbbb dddddfff rrrrrrrr` /// /// where: @@ -710,7 +770,7 @@ impl<'a> Reg<'a> { RegRawIter { cell_sizes: self.cell_sizes, encoded_array: self.encoded_array } } - pub fn iter(self) -> RegIter<'a, C> { + pub fn iter(self) -> RegIter<'a, Addr, Len> { RegIter { cell_sizes: self.cell_sizes, encoded_array: self.encoded_array, @@ -744,19 +804,19 @@ impl<'a, P: ParserWithMode<'a>> Property<'a, P> for Reg<'a> { } #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct RegEntry { - pub address: I, - pub len: I, +pub struct RegEntry { + pub address: Addr, + pub len: Len, } -pub struct RegIter<'a, C: CellCollector> { +pub struct RegIter<'a, CAddr: CellCollector, Len: CellCollector> { cell_sizes: CellSizes, encoded_array: &'a [u8], - _collector: core::marker::PhantomData<*mut C>, + _collector: core::marker::PhantomData<*mut (CAddr, Len)>, } -impl<'a, C: CellCollector> Iterator for RegIter<'a, C> { - type Item = Result, CollectCellsError>; +impl<'a, CAddr: CellCollector, Len: CellCollector> Iterator for RegIter<'a, CAddr, Len> { + type Item = Result, CollectCellsError>; fn next(&mut self) -> Option { let address_bytes = self.cell_sizes.address_cells * 4; let size_bytes = self.cell_sizes.size_cells * 4; @@ -764,7 +824,7 @@ impl<'a, C: CellCollector> Iterator for RegIter<'a, C> { let encoded_address = self.encoded_array.get(..address_bytes)?; let encoded_len = self.encoded_array.get(address_bytes..address_bytes + size_bytes)?; - let mut address_collector = ::Builder::default(); + let mut address_collector = ::Builder::default(); for encoded_address in encoded_address.chunks_exact(4) { // TODO: replace this stuff with `array_chunks` when its stabilized // @@ -777,7 +837,7 @@ impl<'a, C: CellCollector> Iterator for RegIter<'a, C> { } } - let mut len_collector = ::Builder::default(); + let mut len_collector = ::Builder::default(); for encoded_len in encoded_len.chunks_exact(4) { // TODO: replace this stuff with `array_chunks` when its stabilized // @@ -791,8 +851,8 @@ impl<'a, C: CellCollector> Iterator for RegIter<'a, C> { self.encoded_array = self.encoded_array.get((address_bytes + size_bytes)..)?; Some(Ok(RegEntry { - address: C::map(address_collector.finish()), - len: C::map(len_collector.finish()), + address: CAddr::map(address_collector.finish()), + len: Len::map(len_collector.finish()), })) } } @@ -859,9 +919,109 @@ impl<'a, P: ParserWithMode<'a>> Property<'a, P> for VirtualReg { } } +#[derive(Debug, Clone, Copy)] pub struct Ranges<'a> { + parent_address_cells: AddressCells, + cell_sizes: CellSizes, + ranges: &'a [u8], +} + +impl<'a> Ranges<'a> { + pub fn iter(self) -> RangesIter<'a, CAddr, PAddr, Len> + where + CAddr: CellCollector, + PAddr: CellCollector, + Len: CellCollector, + { + RangesIter { + parent_address_cells: self.parent_address_cells, + cell_sizes: self.cell_sizes, + ranges: self.ranges, + _collectors: core::marker::PhantomData, + } + } +} + +pub struct RangesIter< + 'a, + CAddr: CellCollector = u64, + PAddr: CellCollector = u64, + Len: CellCollector = u64, +> { + parent_address_cells: AddressCells, cell_sizes: CellSizes, ranges: &'a [u8], + _collectors: core::marker::PhantomData<*mut (CAddr, PAddr, Len)>, +} + +impl<'a, CAddr: CellCollector, PAddr: CellCollector, Len: CellCollector> Iterator + for RangesIter<'a, CAddr, PAddr, Len> +{ + type Item = Result, CollectCellsError>; + fn next(&mut self) -> Option { + let child_address_bytes = self.cell_sizes.address_cells * 4; + let parent_address_bytes = self.parent_address_cells.0 * 4; + let len_bytes = self.cell_sizes.size_cells * 4; + + let child_encoded_address = self.ranges.get(..child_address_bytes)?; + let parent_encoded_address = + self.ranges.get(child_address_bytes..child_address_bytes + parent_address_bytes)?; + let encoded_len = self.ranges.get( + child_address_bytes + parent_address_bytes + ..child_address_bytes + parent_address_bytes + len_bytes, + )?; + + let mut child_address_collector = ::Builder::default(); + for encoded_address in child_encoded_address.chunks_exact(4) { + // TODO: replace this stuff with `array_chunks` when its stabilized + // + // These unwraps can't panic because `chunks_exact` guarantees that + // we'll always get slices of 4 bytes + if let Err(e) = child_address_collector + .push(u32::from_be_bytes(encoded_address.try_into().unwrap())) + { + return Some(Err(e)); + } + } + + let mut parent_address_collector = ::Builder::default(); + for encoded_address in parent_encoded_address.chunks_exact(4) { + // TODO: replace this stuff with `array_chunks` when its stabilized + // + // These unwraps can't panic because `chunks_exact` guarantees that + // we'll always get slices of 4 bytes + if let Err(e) = parent_address_collector + .push(u32::from_be_bytes(encoded_address.try_into().unwrap())) + { + return Some(Err(e)); + } + } + + let mut len_collector = ::Builder::default(); + for encoded_len in encoded_len.chunks_exact(4) { + // TODO: replace this stuff with `array_chunks` when its stabilized + // + // These unwraps can't panic because `chunks_exact` guarantees that + // we'll always get slices of 4 bytes + if let Err(e) = len_collector.push(u32::from_be_bytes(encoded_len.try_into().unwrap())) + { + return Some(Err(e)); + } + } + + self.ranges = self.ranges.get(child_address_bytes + parent_address_bytes + len_bytes..)?; + Some(Ok(Range { + child_bus_address: CAddr::map(child_address_collector.finish()), + parent_bus_address: PAddr::map(parent_address_collector.finish()), + len: Len::map(len_collector.finish()), + })) + } +} + +pub struct Range { + pub child_bus_address: CAddr, + pub parent_bus_address: PAddr, + pub len: Len, } /// [Devicetree 2.3.10. @@ -925,6 +1085,20 @@ pub struct LegacyInterrupts<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Pani encoded_array: &'a [u8], } +impl<'a, P: ParserWithMode<'a>> LegacyInterrupts<'a, P> { + pub fn interrupt_parent(self) -> InterruptParent<'a, P> { + self.interrupt_parent + } + + pub fn iter(self) -> LegacyInterruptsIter<'a, I> { + LegacyInterruptsIter { + interrupt_cells: self.interrupt_cells, + encoded_array: self.encoded_array, + _collector: core::marker::PhantomData, + } + } +} + impl<'a, P: ParserWithMode<'a>> Property<'a, P> for LegacyInterrupts<'a, P> { fn parse( node: Node<'a, (P::Parser, NoPanic)>, @@ -957,6 +1131,42 @@ impl<'a, P: ParserWithMode<'a>> Property<'a, P> for LegacyInterrupts<'a, P> { } } +impl<'a, P: ParserWithMode<'a>> Copy for LegacyInterrupts<'a, P> {} +impl<'a, P: ParserWithMode<'a>> Clone for LegacyInterrupts<'a, P> { + fn clone(&self) -> Self { + *self + } +} + +pub struct LegacyInterruptsIter<'a, I: CellCollector> { + interrupt_cells: InterruptCells, + encoded_array: &'a [u8], + _collector: core::marker::PhantomData<*mut I>, +} + +impl<'a, I: CellCollector> Iterator for LegacyInterruptsIter<'a, I> { + type Item = Result; + fn next(&mut self) -> Option { + let encoded_specifier = self.encoded_array.get(..self.interrupt_cells.0 * 4)?; + let mut specifier_collector = ::Builder::default(); + + for encoded_specifier in encoded_specifier.chunks_exact(4) { + // TODO: replace this stuff with `array_chunks` when its stabilized + // + // These unwraps can't panic because `chunks_exact` guarantees that + // we'll always get slices of 4 bytes + if let Err(e) = + specifier_collector.push(u32::from_be_bytes(encoded_specifier.try_into().unwrap())) + { + return Some(Err(e)); + } + } + + self.encoded_array = self.encoded_array.get(self.interrupt_cells.0 * 4..)?; + Some(Ok(I::map(specifier_collector.finish()))) + } +} + /// [Devicetree 2.4.1.3. /// `interrupts-extended`](https://devicetree-specification.readthedocs.io/en/latest/chapter2-devicetree-basics.html#interrupts-extended) /// @@ -1016,7 +1226,7 @@ impl<'a, P: ParserWithMode<'a>> Iterator for ExtendedInterruptsIter<'a, P> { })?; self.encoded_array = self.encoded_array.get(4..)?; - let res = crate::tryblock! { + let res = crate::tryblock!({ let root = Root { node: self.root.node.fallible() }; let Some(interrupt_parent) = root.resolve_phandle(phandle)? else { return Err(FdtError::PHandleNotFound(phandle.0.to_ne())); @@ -1042,7 +1252,7 @@ impl<'a, P: ParserWithMode<'a>> Iterator for ExtendedInterruptsIter<'a, P> { interrupt_cells, encoded_array, })) - }; + }); // This is a manual impl of `map` because we need the panic location to // be the caller if `P::to_output` panics @@ -1177,6 +1387,13 @@ impl<'a> Iterator for InterruptSpecifierIterPairs<'a> { /// interrupt parent is assumed to be its devicetree parent. pub struct InterruptParent<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)>(Node<'a, P>); +impl<'a, P: ParserWithMode<'a>> Copy for InterruptParent<'a, P> {} +impl<'a, P: ParserWithMode<'a>> Clone for InterruptParent<'a, P> { + fn clone(&self) -> Self { + *self + } +} + impl<'a, P: ParserWithMode<'a>> core::ops::Deref for InterruptParent<'a, P> { type Target = Node<'a, P>; fn deref(&self) -> &Self::Target { @@ -1233,33 +1450,33 @@ impl<'a, P: ParserWithMode<'a>> Property<'a, P> for InterruptCells { /// incoming unit interrupt specifier being looked up in the table specified in /// the `interrupt-map` property. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct InterruptMapMask { - address_mask: ADDRMASK::Output, - interrupt_specifier_mask: INTMASK::Output, +pub struct InterruptMapMask { + address_mask: AddrMask::Output, + interrupt_specifier_mask: IntMask::Output, } -impl InterruptMapMask { +impl InterruptMapMask { pub fn mask( self, - address: ::Output, - interrupt_specifier: ::Output, - ) -> (::Output, ::Output) + address: ::Output, + interrupt_specifier: ::Output, + ) -> (::Output, ::Output) where - ::Output: core::ops::BitAnd< - ::Output, - Output = ::Output, + ::Output: core::ops::BitAnd< + ::Output, + Output = ::Output, >, - ::Output: core::ops::BitAnd< - ::Output, - Output = ::Output, + ::Output: core::ops::BitAnd< + ::Output, + Output = ::Output, >, { (self.address_mask & address, self.interrupt_specifier_mask & interrupt_specifier) } } -impl<'a, ADDRMASK: CellCollector, INTMASK: CellCollector, P: ParserWithMode<'a>> Property<'a, P> - for InterruptMapMask +impl<'a, AddrMask: CellCollector, IntMask: CellCollector, P: ParserWithMode<'a>> Property<'a, P> + for InterruptMapMask { fn parse( node: Node<'a, (P::Parser, NoPanic)>, @@ -1277,8 +1494,8 @@ impl<'a, ADDRMASK: CellCollector, INTMASK: CellCollector, P: ParserWithMode<'a>> return Err(FdtError::InvalidPropertyValue); } - let mut address_collector = ADDRMASK::Builder::default(); - let mut specifier_collector = INTMASK::Builder::default(); + let mut address_collector = AddrMask::Builder::default(); + let mut specifier_collector = IntMask::Builder::default(); let mut cells = prop.value().chunks_exact(4); // TODO: replace this stuff with `array_chunks` when its stabilized @@ -1294,8 +1511,8 @@ impl<'a, ADDRMASK: CellCollector, INTMASK: CellCollector, P: ParserWithMode<'a>> } Ok(Some(Self { - address_mask: ADDRMASK::map(address_collector.finish()), - interrupt_specifier_mask: INTMASK::map(specifier_collector.finish()), + address_mask: AddrMask::map(address_collector.finish()), + interrupt_specifier_mask: IntMask::map(specifier_collector.finish()), })) } None => Ok(None), @@ -1325,29 +1542,29 @@ impl<'a, P: ParserWithMode<'a>> Property<'a, P> for InterruptController { pub struct InterruptMap< 'a, - CADDR: CellCollector, - CINT: CellCollector = u32, - PADDR: CellCollector = u64, - PINT: CellCollector = u32, + CAddr: CellCollector, + CInt: CellCollector = u32, + PAddr: CellCollector = u64, + PInt: CellCollector = u32, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic), > { address_cells: AddressCells, interrupt_cells: InterruptCells, node: Node<'a, (P::Parser, NoPanic)>, encoded_map: &'a [u8], - _collectors: core::marker::PhantomData<*mut (CADDR, CINT, PADDR, PINT)>, + _collectors: core::marker::PhantomData<*mut (CAddr, CInt, PAddr, PInt)>, } impl< 'a, P: ParserWithMode<'a>, - CADDR: CellCollector, - CINT: CellCollector, - PADDR: CellCollector, - PINT: CellCollector, - > InterruptMap<'a, CADDR, CINT, PADDR, PINT, P> + CAddr: CellCollector, + CInt: CellCollector, + PAddr: CellCollector, + PInt: CellCollector, + > InterruptMap<'a, CAddr, CInt, PAddr, PInt, P> { - pub fn iter(self) -> InterruptMapIter<'a, CADDR, CINT, PADDR, PINT, P> { + pub fn iter(self) -> InterruptMapIter<'a, CAddr, CInt, PAddr, PInt, P> { InterruptMapIter { address_cells: self.address_cells, interrupt_cells: self.interrupt_cells, @@ -1357,14 +1574,15 @@ impl< } } + #[allow(clippy::type_complexity)] pub fn find( self, - address: CADDR::Output, - interrupt_specifier: CINT::Output, - ) -> P::Output>> + address: CAddr::Output, + interrupt_specifier: CInt::Output, + ) -> P::Output>> where - for<'b> &'b CADDR::Output: PartialEq, - for<'b> &'b CINT::Output: PartialEq, + CAddr::Output: PartialEq, + CInt::Output: PartialEq, { let this: InterruptMap<_, _, _, _, (P::Parser, NoPanic)> = InterruptMap { address_cells: self.address_cells, @@ -1379,17 +1597,17 @@ impl< .find(|e| match e { Err(_) => true, Ok(entry) => { - entry.child_unit_address() == &address - && entry.child_interrupt_specifier() == &interrupt_specifier + entry.child_unit_address == address + && entry.child_interrupt_specifier == interrupt_specifier } }) .transpose() .map(|e| { e.map(|e| InterruptMapEntry::<_, _, _, _, P> { - child_address: e.child_address, + child_unit_address: e.child_unit_address, child_interrupt_specifier: e.child_interrupt_specifier, interrupt_parent: e.interrupt_parent.alt(), - parent_address: e.parent_address, + parent_unit_address: e.parent_unit_address, parent_interrupt_specifier: e.parent_interrupt_specifier, }) }), @@ -1400,11 +1618,11 @@ impl< impl< 'a, P: ParserWithMode<'a>, - CADDR: CellCollector, - CINT: CellCollector, - PADDR: CellCollector, - PINT: CellCollector, - > Property<'a, P> for InterruptMap<'a, CADDR, CINT, PADDR, PINT, P> + CAddr: CellCollector, + CInt: CellCollector, + PAddr: CellCollector, + PInt: CellCollector, + > Property<'a, P> for InterruptMap<'a, CAddr, CInt, PAddr, PInt, P> { fn parse( node: Node<'a, (P::Parser, NoPanic)>, @@ -1431,52 +1649,78 @@ impl< pub struct InterruptMapIter< 'a, - CADDR: CellCollector, - CINT: CellCollector, - PADDR: CellCollector, - PINT: CellCollector, + CAddr: CellCollector, + CInt: CellCollector, + PAddr: CellCollector, + PInt: CellCollector, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic), > { address_cells: AddressCells, interrupt_cells: InterruptCells, node: Node<'a, (P::Parser, NoPanic)>, encoded_map: &'a [u8], - _collectors: core::marker::PhantomData<*mut (CADDR, CINT, PADDR, PINT)>, + _collectors: core::marker::PhantomData<*mut (CAddr, CInt, PAddr, PInt)>, } impl< 'a, - CADDR: CellCollector, - CINT: CellCollector, - PADDR: CellCollector, - PINT: CellCollector, + CAddr: CellCollector, + CInt: CellCollector, + PAddr: CellCollector, + PInt: CellCollector, P: ParserWithMode<'a>, - > Iterator for InterruptMapIter<'a, CADDR, CINT, PADDR, PINT, P> + > Iterator for InterruptMapIter<'a, CAddr, CInt, PAddr, PInt, P> { - type Item = P::Output>; + type Item = P::Output>; #[track_caller] fn next(&mut self) -> Option { - let res = crate::tryblock! { + let res = crate::tryblock!({ let child_addr_size = self.address_cells.0 * 4; let child_intsp_size = self.interrupt_cells.0 * 4; - let Some(child_address_iter) = self.encoded_map.get(..child_addr_size) else { return Ok(None) }; - let Some(child_specifier_iter) = self.encoded_map.get(child_addr_size..child_addr_size+child_intsp_size) else { return Ok(None) }; - let Some(interrupt_parent) = self.encoded_map.get(child_addr_size+child_intsp_size..child_addr_size+child_intsp_size+4) else { return Ok(None) }; + let Some(child_address_iter) = self.encoded_map.get(..child_addr_size) else { + return Ok(None); + }; + let Some(child_specifier_iter) = + self.encoded_map.get(child_addr_size..child_addr_size + child_intsp_size) + else { + return Ok(None); + }; + let Some(interrupt_parent) = self + .encoded_map + .get(child_addr_size + child_intsp_size..child_addr_size + child_intsp_size + 4) + else { + return Ok(None); + }; let root = self.node.make_root::<(P::Parser, NoPanic)>()?; let phandle = u32::from_ne_bytes(interrupt_parent.try_into().unwrap()); - let interrupt_parent = root.resolve_phandle(PHandle(BigEndianU32::from_be(phandle)))?.ok_or(FdtError::PHandleNotFound(phandle.swap_bytes()))?; + let interrupt_parent = root + .resolve_phandle(PHandle(BigEndianU32::from_be(phandle)))? + .ok_or(FdtError::PHandleNotFound(phandle.swap_bytes()))?; - let parent_address_cells = interrupt_parent.property::()?.ok_or(FdtError::MissingRequiredProperty("#address-cells/#size-cells"))?; - let parent_interrupt_cells = interrupt_parent.property::()?.ok_or(FdtError::MissingRequiredProperty("#interrupt-cells"))?; + let parent_address_cells = interrupt_parent + .property::()? + .ok_or(FdtError::MissingRequiredProperty("#address-cells/#size-cells"))?; + let parent_interrupt_cells = interrupt_parent + .property::()? + .ok_or(FdtError::MissingRequiredProperty("#interrupt-cells"))?; let parent_addr_size = parent_address_cells.0 * 4; let parent_intsp_size = parent_interrupt_cells.0 * 4; - let Some(parent_address_iter) = self.encoded_map.get(child_addr_size+child_intsp_size+4..child_addr_size+child_intsp_size+4+parent_addr_size) else { return Ok(None)}; + let Some(parent_address_iter) = self.encoded_map.get( + child_addr_size + child_intsp_size + 4 + ..child_addr_size + child_intsp_size + 4 + parent_addr_size, + ) else { + return Ok(None); + }; - let Some(mut parent_specifier_iter) = self.encoded_map.get(child_addr_size+child_intsp_size+4+parent_addr_size..) else { return Ok(None)}; + let Some(mut parent_specifier_iter) = + self.encoded_map.get(child_addr_size + child_intsp_size + 4 + parent_addr_size..) + else { + return Ok(None); + }; self.encoded_map = match parent_specifier_iter.get(parent_intsp_size..) { Some(s) => s, None => return Ok(None), @@ -1486,34 +1730,34 @@ impl< None => return Ok(None), }; - let mut child_address_collector = CADDR::Builder::default(); + let mut child_address_collector = CAddr::Builder::default(); for chunk in child_address_iter.chunks_exact(4) { child_address_collector.push(u32::from_be_bytes(chunk.try_into().unwrap()))?; } - let mut child_specifier_collector = CINT::Builder::default(); + let mut child_specifier_collector = CInt::Builder::default(); for chunk in child_specifier_iter.chunks_exact(4) { child_specifier_collector.push(u32::from_be_bytes(chunk.try_into().unwrap()))?; } - let mut parent_address_collector = PADDR::Builder::default(); + let mut parent_address_collector = PAddr::Builder::default(); for chunk in parent_address_iter.chunks_exact(4) { parent_address_collector.push(u32::from_be_bytes(chunk.try_into().unwrap()))?; } - let mut parent_specifier_collector = PINT::Builder::default(); + let mut parent_specifier_collector = PInt::Builder::default(); for chunk in parent_specifier_iter.chunks_exact(4) { parent_specifier_collector.push(u32::from_be_bytes(chunk.try_into().unwrap()))?; } Ok(Some(InterruptMapEntry { interrupt_parent: interrupt_parent.alt(), - child_address: CADDR::map(child_address_collector.finish()), - child_interrupt_specifier: CINT::map(child_specifier_collector.finish()), - parent_address: PADDR::map(parent_address_collector.finish()), - parent_interrupt_specifier: PINT::map(parent_specifier_collector.finish()), + child_unit_address: CAddr::map(child_address_collector.finish()), + child_interrupt_specifier: CInt::map(child_specifier_collector.finish()), + parent_unit_address: PAddr::map(parent_address_collector.finish()), + parent_interrupt_specifier: PInt::map(parent_specifier_collector.finish()), })) - }; + }); #[allow(clippy::manual_map)] match res.transpose() { @@ -1525,74 +1769,39 @@ impl< pub struct InterruptMapEntry< 'a, - CADDR: CellCollector, - CINT: CellCollector, - PADDR: CellCollector, - PINT: CellCollector, + CAddr: CellCollector, + CInt: CellCollector, + PAddr: CellCollector, + PInt: CellCollector, P: ParserWithMode<'a>, > { - interrupt_parent: Node<'a, P>, - child_address: CADDR::Output, - child_interrupt_specifier: CINT::Output, - parent_address: PADDR::Output, - parent_interrupt_specifier: PINT::Output, -} - -impl< - 'a, - P: ParserWithMode<'a>, - CADDR: CellCollector, - CINT: CellCollector, - PADDR: CellCollector, - PINT: CellCollector, - > InterruptMapEntry<'a, CADDR, CINT, PADDR, PINT, P> -{ - #[inline(always)] - pub fn interrupt_parent(&self) -> Node<'a, P> { - self.interrupt_parent - } - - #[inline(always)] - pub fn child_unit_address(&self) -> &CADDR::Output { - &self.child_address - } - - #[inline(always)] - pub fn child_interrupt_specifier(&self) -> &CINT::Output { - &self.child_interrupt_specifier - } - - #[inline(always)] - pub fn parent_unit_address(&self) -> &PADDR::Output { - &self.parent_address - } - - #[inline(always)] - pub fn parent_interrupt_specifier(&self) -> &PINT::Output { - &self.parent_interrupt_specifier - } + pub interrupt_parent: Node<'a, P>, + pub child_unit_address: CAddr::Output, + pub child_interrupt_specifier: CInt::Output, + pub parent_unit_address: PAddr::Output, + pub parent_interrupt_specifier: PInt::Output, } impl< 'a, P: ParserWithMode<'a>, - CADDR: CellCollector, - CINT: CellCollector, - PADDR: CellCollector, - PINT: CellCollector, - > Clone for InterruptMapEntry<'a, CADDR, CINT, PADDR, PINT, P> + CAddr: CellCollector, + CInt: CellCollector, + PAddr: CellCollector, + PInt: CellCollector, + > Clone for InterruptMapEntry<'a, CAddr, CInt, PAddr, PInt, P> where - CADDR::Output: Clone, - CINT::Output: Clone, - PADDR::Output: Clone, - PINT::Output: Clone, + CAddr::Output: Clone, + CInt::Output: Clone, + PAddr::Output: Clone, + PInt::Output: Clone, { fn clone(&self) -> Self { Self { - child_address: self.child_address.clone(), + child_unit_address: self.child_unit_address.clone(), child_interrupt_specifier: self.child_interrupt_specifier.clone(), interrupt_parent: self.interrupt_parent, - parent_address: self.parent_address.clone(), + parent_unit_address: self.parent_unit_address.clone(), parent_interrupt_specifier: self.parent_interrupt_specifier.clone(), } } @@ -1601,19 +1810,20 @@ where impl< 'a, P: ParserWithMode<'a>, - CADDR: CellCollector, - CINT: CellCollector, - PADDR: CellCollector, - PINT: CellCollector, - > Copy for InterruptMapEntry<'a, CADDR, CINT, PADDR, PINT, P> + CAddr: CellCollector, + CInt: CellCollector, + PAddr: CellCollector, + PInt: CellCollector, + > Copy for InterruptMapEntry<'a, CAddr, CInt, PAddr, PInt, P> where - CADDR::Output: Copy, - CINT::Output: Copy, - PADDR::Output: Copy, - PINT::Output: Copy, + CAddr::Output: Copy, + CInt::Output: Copy, + PAddr::Output: Copy, + PInt::Output: Copy, { } +#[derive(Debug, Clone, Copy)] pub struct InvalidPropertyValue; impl From for FdtError { @@ -1636,6 +1846,17 @@ impl<'a> PropertyValue<'a> for u32 { } } +impl<'a> PropertyValue<'a> for u64 { + #[inline] + fn parse(value: &'a [u8]) -> Result { + match value { + [a, b, c, d] => Ok(u64::from_be_bytes([0, 0, 0, 0, *a, *b, *c, *d])), + [a, b, c, d, e, f, g, h] => Ok(u64::from_be_bytes([*a, *b, *c, *d, *e, *f, *g, *h])), + _ => Err(InvalidPropertyValue), + } + } +} + impl<'a> PropertyValue<'a> for usize { #[inline] fn parse(value: &'a [u8]) -> Result { @@ -1682,6 +1903,36 @@ impl<'a> PropertyValue<'a> for &'a str { } } +#[derive(Debug, Clone, Copy)] +pub struct U32List<'a>(&'a [u8]); + +impl<'a> U32List<'a> { + pub fn iter(self) -> U32ListIter<'a> { + U32ListIter(self.0) + } +} + +impl<'a> PropertyValue<'a> for U32List<'a> { + fn parse(value: &'a [u8]) -> Result { + if value.len() % 4 != 0 { + return Err(InvalidPropertyValue); + } + + Ok(Self(value)) + } +} + +pub struct U32ListIter<'a>(&'a [u8]); + +impl<'a> Iterator for U32ListIter<'a> { + type Item = u32; + fn next(&mut self) -> Option { + let val = u32::from_be_bytes(self.0.get(..4)?.try_into().unwrap()); + self.0 = self.0.get(4..)?; + Some(val) + } +} + #[derive(Debug, Clone)] pub struct StringList<'a> { strs: core::str::Split<'a, char>, @@ -1727,7 +1978,7 @@ mod tests { #[test] fn reg_u64_iter() { - let mut iter = RegIter:: { + let mut iter = RegIter:: { cell_sizes: CellSizes { address_cells: 2, size_cells: 1 }, encoded_array: &[ 0x55, 0x44, 0x33, 0x22, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, diff --git a/src/standard_nodes.rs b/src/standard_nodes.rs index cfd4430..f3180c4 100644 --- a/src/standard_nodes.rs +++ b/src/standard_nodes.rs @@ -18,7 +18,7 @@ impl<'a, P: ParserWithMode<'a> + 'a> Chosen<'a, P> { /// Contains the bootargs, if they exist #[track_caller] pub fn bootargs(self) -> P::Output> { - P::to_output(crate::tryblock! { + P::to_output(crate::tryblock!({ let node = self.node.fallible(); for prop in node.properties()?.into_iter().flatten() { if prop.name() == "bootargs" { @@ -30,7 +30,7 @@ impl<'a, P: ParserWithMode<'a> + 'a> Chosen<'a, P> { } Ok(None) - }) + })) } /// Looks up the `stdout-path` property and returns the [`StdInOutPath`] @@ -40,24 +40,22 @@ impl<'a, P: ParserWithMode<'a> + 'a> Chosen<'a, P> { /// [`StdInOutPath::params`]. #[track_caller] pub fn stdout(self) -> P::Output>> { - P::to_output(crate::tryblock! { + P::to_output(crate::tryblock!({ let node = self.node.fallible(); - node.properties()?.into_iter().find_map(|n| match n { - Err(e) => Some(Err(e)), - Ok(property) => match property.name() == "stdout-path" { - false => None, - true => Some( - property - .as_value::<&'a str>() - .map_err(Into::into) - .map(|s| { - let (path, params) = Self::split_stdinout_property(s); - StdInOutPath { path, params } - }) - ), - }, - }).transpose() - }) + node.properties()? + .into_iter() + .find_map(|n| match n { + Err(e) => Some(Err(e)), + Ok(property) => match property.name() == "stdout-path" { + false => None, + true => Some(property.as_value::<&'a str>().map_err(Into::into).map(|s| { + let (path, params) = Self::split_stdinout_property(s); + StdInOutPath { path, params } + })), + }, + }) + .transpose() + })) } /// Looks up the `stdin-path` property and returns the [`StdInOutPath`] @@ -67,24 +65,22 @@ impl<'a, P: ParserWithMode<'a> + 'a> Chosen<'a, P> { /// [`StdInOutPath::params`]. #[track_caller] pub fn stdin(self) -> P::Output>> { - P::to_output(crate::tryblock! { + P::to_output(crate::tryblock!({ let node = self.node.fallible(); - node.properties()?.into_iter().find_map(|n| match n { - Err(e) => Some(Err(e)), - Ok(property) => match property.name() == "stdin-path" { - false => None, - true => Some( - property - .as_value::<&'a str>() - .map_err(Into::into) - .map(|s| { - let (path, params) = Self::split_stdinout_property(s); - StdInOutPath { path, params } - }) - ), - }, - }).transpose() - }) + node.properties()? + .into_iter() + .find_map(|n| match n { + Err(e) => Some(Err(e)), + Ok(property) => match property.name() == "stdin-path" { + false => None, + true => Some(property.as_value::<&'a str>().map_err(Into::into).map(|s| { + let (path, params) = Self::split_stdinout_property(s); + StdInOutPath { path, params } + })), + }, + }) + .transpose() + })) } fn split_stdinout_property(property: &str) -> (&str, Option<&str>) { @@ -152,9 +148,12 @@ impl<'a, P: ParserWithMode<'a>> Root<'a, P> { /// Root node cell sizes #[track_caller] pub fn cell_sizes(self) -> P::Output { - P::to_output(crate::tryblock! { - self.node.fallible().property::()?.ok_or(FdtError::MissingRequiredProperty("#address-cells/#size-cells")) - }) + P::to_output(crate::tryblock!({ + self.node + .fallible() + .property::()? + .ok_or(FdtError::MissingRequiredProperty("#address-cells/#size-cells")) + })) } /// [Devicetree 3.2. Root @@ -164,25 +163,20 @@ impl<'a, P: ParserWithMode<'a>> Root<'a, P> { /// board. The recommended format is "manufacturer,model-number". #[track_caller] pub fn model(self) -> P::Output<&'a str> { - P::to_output(crate::tryblock! { + P::to_output(crate::tryblock!({ let node = self.node.fallible(); - node - .properties()? + node.properties()? .into_iter() .find_map(|n| match n { Err(e) => Some(Err(e)), Ok(property) => match property.name() == "model" { false => None, - true => Some( - property - .as_value::<&'a str>() - .map_err(Into::into) - ), + true => Some(property.as_value::<&'a str>().map_err(Into::into)), }, }) .transpose()? .ok_or(FdtError::MissingRequiredProperty("model")) - }) + })) } /// [Devicetree 3.2. Root @@ -197,10 +191,10 @@ impl<'a, P: ParserWithMode<'a>> Root<'a, P> { /// /// For example: `compatible = "fsl,mpc8572ds"` pub fn compatible(self) -> P::Output> { - P::to_output(crate::tryblock! { - >::parse(self.node.fallible(), self.node.make_root()?)? - .ok_or(FdtError::MissingRequiredProperty("compatible")) - }) + P::to_output(crate::tryblock!({ + >::parse(self.node.fallible(), self.node.make_root()?)? + .ok_or(FdtError::MissingRequiredProperty("compatible")) + })) } /// Returns an iterator over all of the available properties @@ -215,7 +209,7 @@ impl<'a, P: ParserWithMode<'a>> Root<'a, P> { #[track_caller] pub fn resolve_phandle(self, phandle: PHandle) -> P::Output>> { - P::to_output(crate::tryblock! { + P::to_output(crate::tryblock!({ let this = Root { node: self.node.fallible() }; for node in this.all_nodes()? { let (_, node) = node?; @@ -225,7 +219,7 @@ impl<'a, P: ParserWithMode<'a>> Root<'a, P> { } Ok(None) - }) + })) } #[track_caller] @@ -297,7 +291,28 @@ impl<'a, P: ParserWithMode<'a>> Root<'a, P> { return P::to_output(Err(e)); } - P::to_output(Ok(AllNodesIterator { parser, parents: [&[]; 16], parent_index: 0 })) + P::to_output(Ok(AllNodesIterator { + parser, + parents: [ + self.node.this.as_slice(), + &[], + &[], + &[], + &[], + &[], + &[], + &[], + &[], + &[], + &[], + &[], + &[], + &[], + &[], + &[], + ], + parent_index: 0, + })) } } @@ -342,7 +357,7 @@ impl<'a, P: ParserWithMode<'a>> Iterator for AllNodesIterator<'a, P> { let starting_data = self.parser.data(); - match self.parents.get_mut(self.parent_index.saturating_sub(1)) { + match self.parents.get_mut(self.parent_index) { Some(idx) => *idx = starting_data, // FIXME: what makes sense for this to return? None => return None, @@ -352,7 +367,10 @@ impl<'a, P: ParserWithMode<'a>> Iterator for AllNodesIterator<'a, P> { self.parent_index, Node { this: RawNode::new(starting_data), - parent: self.parents.get(self.parent_index).map(|parent| RawNode::new(parent)), + parent: self + .parents + .get(self.parent_index.saturating_sub(1)) + .map(|parent| RawNode::new(parent)), strings: self.parser.strings(), structs: self.parser.structs(), _mode: core::marker::PhantomData, diff --git a/src/tests.rs b/src/tests.rs index 8d531cf..5453ff7 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -264,9 +264,12 @@ fn cell_sizes() { #[test] fn interrupt_map() { let fdt = Fdt::new(TEST.as_slice()).unwrap(); + + std::panic!("{fdt}"); + let root = fdt.root(); - let entries = [ + let entries: [(PciAddress, u64, Option, u64); 16] = [ (PciAddress { hi: PciAddressHighBits::new(0), mid: 0, lo: 0 }, 1, None, 32), (PciAddress { hi: PciAddressHighBits::new(0), mid: 0, lo: 0 }, 2, None, 33), (PciAddress { hi: PciAddressHighBits::new(0), mid: 0, lo: 0 }, 3, None, 34), @@ -293,10 +296,10 @@ fn interrupt_map() { .iter() .zip(entries) { - assert_eq!(entry.child_unit_address(), expected.0); - assert_eq!(entry.child_interrupt_specifier(), expected.1); - assert_eq!(entry.parent_unit_address(), expected.2); - assert_eq!(entry.parent_interrupt_specifier(), expected.3); + assert_eq!(entry.child_unit_address, expected.0); + assert_eq!(entry.child_interrupt_specifier, expected.1); + assert_eq!(entry.parent_unit_address, expected.2); + assert_eq!(entry.parent_interrupt_specifier, expected.3); } } From 48b51a7edad6429677e40be20c09b56fa5197f80 Mon Sep 17 00:00:00 2001 From: repnop Date: Sat, 13 Jul 2024 17:14:27 -0400 Subject: [PATCH 14/38] whoopsie --- src/tests.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/tests.rs b/src/tests.rs index 5453ff7..fe514e0 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -264,9 +264,6 @@ fn cell_sizes() { #[test] fn interrupt_map() { let fdt = Fdt::new(TEST.as_slice()).unwrap(); - - std::panic!("{fdt}"); - let root = fdt.root(); let entries: [(PciAddress, u64, Option, u64); 16] = [ From 6c87794801aaf8243e6831039a3d4a0cf16938ec Mon Sep 17 00:00:00 2001 From: repnop Date: Sat, 13 Jul 2024 17:31:13 -0400 Subject: [PATCH 15/38] yes --- src/pretty_print.rs | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/src/pretty_print.rs b/src/pretty_print.rs index a9a1524..6967e3c 100644 --- a/src/pretty_print.rs +++ b/src/pretty_print.rs @@ -15,27 +15,21 @@ pub struct Error; impl From for Error { #[track_caller] - fn from(e: FdtError) -> Self { - #[cfg(test)] - std::println!("{e:?}, {}", core::panic::Location::caller()); + fn from(_: FdtError) -> Self { Error } } impl From for Error { #[track_caller] - fn from(e: CollectCellsError) -> Self { - #[cfg(test)] - std::println!("{e:?}, {}", core::panic::Location::caller()); + fn from(_: CollectCellsError) -> Self { Error } } impl From for Error { #[track_caller] - fn from(e: InvalidPropertyValue) -> Self { - #[cfg(test)] - std::println!("{e:?}, {}", core::panic::Location::caller()); + fn from(_: InvalidPropertyValue) -> Self { Error } } @@ -129,9 +123,6 @@ pub fn print_fdt<'a, P: Parser<'a> + 'a>( Ok(()) }); - #[cfg(test)] - std::println!("{res:?}"); - Ok(res?) } From 55ddb8067990e058c5c8be7afef0abb3b6281d1b Mon Sep 17 00:00:00 2001 From: repnop Date: Sun, 14 Jul 2024 22:09:39 -0400 Subject: [PATCH 16/38] yes --- src/parsing.rs | 35 ----------------------------------- src/parsing/aligned.rs | 38 +------------------------------------- src/parsing/unaligned.rs | 30 +----------------------------- 3 files changed, 2 insertions(+), 101 deletions(-) diff --git a/src/parsing.rs b/src/parsing.rs index 3bbc81f..69a3b3b 100644 --- a/src/parsing.rs +++ b/src/parsing.rs @@ -35,32 +35,6 @@ impl BigEndianU32 { } } -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -#[repr(transparent)] -pub struct BigEndianU64(u64); - -impl BigEndianU64 { - pub const fn from_ne(n: u64) -> Self { - Self(n.to_be()) - } - - pub const fn from_le(n: u64) -> Self { - Self(u64::from_le(n).to_be()) - } - - pub const fn from_be(n: u64) -> Self { - Self(n) - } - - pub const fn to_ne(self) -> u64 { - u64::from_be(self.0) - } - - pub const fn to_be(self) -> u64 { - self.0 - } -} - #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] #[repr(transparent)] pub struct BigEndianToken(pub(crate) BigEndianU32); @@ -209,10 +183,6 @@ impl<'a, T: Parser<'a>, U: PanicMode + Clone + Default> Parser<'a> for (T, U) { self.0.advance_u32() } - fn advance_u64(&mut self) -> Result { - self.0.advance_u64() - } - fn advance_cstr(&mut self) -> Result<&'a core::ffi::CStr, FdtError> { self.0.advance_cstr() } @@ -261,7 +231,6 @@ pub trait Parser<'a>: crate::sealed::Sealed + Clone { } fn advance_u32(&mut self) -> Result; - fn advance_u64(&mut self) -> Result; fn advance_cstr(&mut self) -> Result<&'a core::ffi::CStr, FdtError>; fn advance_aligned(&mut self, n: usize); @@ -269,10 +238,6 @@ pub trait Parser<'a>: crate::sealed::Sealed + Clone { self.clone().advance_u32() } - fn peek_u64(&self) -> Result { - self.clone().advance_u64() - } - fn parse_header(&mut self) -> Result { let magic = self.advance_u32()?.to_ne(); let total_size = self.advance_u32()?.to_ne(); diff --git a/src/parsing/aligned.rs b/src/parsing/aligned.rs index e61a5e1..3a7a217 100644 --- a/src/parsing/aligned.rs +++ b/src/parsing/aligned.rs @@ -4,10 +4,7 @@ use crate::FdtError; -use super::{ - BigEndianToken, BigEndianU32, BigEndianU64, ParseError, Parser, Stream, StringsBlock, - StructsBlock, -}; +use super::{BigEndianToken, BigEndianU32, ParseError, Parser, Stream, StringsBlock, StructsBlock}; pub struct AlignedParser<'a> { stream: Stream<'a, u32>, @@ -77,21 +74,6 @@ impl<'a> Parser<'a> for AlignedParser<'a> { .ok_or(FdtError::ParseError(ParseError::UnexpectedEndOfData)) } - fn advance_u64(&mut self) -> Result { - let (a, b) = self - .stream - .advance() - .map(BigEndianU32) - .zip(self.stream.advance().map(BigEndianU32)) - .ok_or(ParseError::UnexpectedEndOfData)?; - - #[cfg(target_endian = "little")] - return Ok(BigEndianU64::from_be((u64::from(b.to_be()) << 32) | u64::from(a.to_be()))); - - #[cfg(target_endian = "big")] - return Ok(BigEndianU64::from_be((u64::from(a.to_be()) << 32) | u64::from(b.to_be()))); - } - fn advance_cstr(&mut self) -> Result<&'a core::ffi::CStr, FdtError> { // SAFETY: It is safe to reinterpret the stream data to a smaller integer size let bytes = unsafe { @@ -116,21 +98,3 @@ impl<'a> Parser<'a> for AlignedParser<'a> { self.stream.skip_many(skip); } } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn advance_u64() { - let n = BigEndianU64::from_ne(0xF00DCAFEDEADFEED); - let mut parser = AlignedParser::new( - unsafe { core::slice::from_raw_parts(&n as *const BigEndianU64 as *const u32, 2) }, - StringsBlock(&[]), - StructsBlock(&[]), - ); - let m = parser.advance_u64().unwrap(); - - assert_eq!(n, m); - } -} diff --git a/src/parsing/unaligned.rs b/src/parsing/unaligned.rs index 7d17e5d..18e0b20 100644 --- a/src/parsing/unaligned.rs +++ b/src/parsing/unaligned.rs @@ -4,10 +4,7 @@ use crate::FdtError; -use super::{ - BigEndianToken, BigEndianU32, BigEndianU64, ParseError, Parser, Stream, StringsBlock, - StructsBlock, -}; +use super::{BigEndianToken, BigEndianU32, ParseError, Parser, Stream, StringsBlock, StructsBlock}; pub struct UnalignedParser<'a> { stream: Stream<'a, u8>, @@ -74,18 +71,6 @@ impl<'a> Parser<'a> for UnalignedParser<'a> { Ok(BigEndianU32::from_be(unsafe { core::ptr::read_unaligned(data.as_ptr().cast::()) })) } - fn advance_u64(&mut self) -> Result { - if self.stream.0.len() < core::mem::size_of::() { - return Err(FdtError::ParseError(ParseError::UnexpectedEndOfData)); - } - - let data = self.stream.0; - self.stream.skip_many(4); - - // SAFETY: The buffer has at least 4 bytes available to read - Ok(BigEndianU64::from_be(unsafe { core::ptr::read_unaligned(data.as_ptr().cast::()) })) - } - fn advance_cstr(&mut self) -> Result<&'a core::ffi::CStr, FdtError> { let cstr = core::ffi::CStr::from_bytes_until_nul(self.stream.0) .map_err(|_| ParseError::InvalidCStrValue)?; @@ -120,17 +105,4 @@ mod tests { assert_eq!(n, m); } - - #[test] - fn advance_u64() { - let n = BigEndianU64::from_ne(0xF00DCAFEDEADFEED); - let mut parser = UnalignedParser::new( - unsafe { core::slice::from_raw_parts(&n as *const BigEndianU64 as *const u8, 8) }, - StringsBlock(&[]), - StructsBlock(&[]), - ); - let m = parser.advance_u64().unwrap(); - - assert_eq!(n, m); - } } From a1d05a009ee3259e6becaa6b6a27a16422224810 Mon Sep 17 00:00:00 2001 From: repnop Date: Mon, 15 Jul 2024 21:05:38 -0400 Subject: [PATCH 17/38] wauw --- src/properties.rs | 4 ++ src/standard_nodes.rs | 116 ++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 115 insertions(+), 5 deletions(-) diff --git a/src/properties.rs b/src/properties.rs index e66b657..1301b00 100644 --- a/src/properties.rs +++ b/src/properties.rs @@ -462,6 +462,10 @@ impl<'a> Compatible<'a> { pub fn all(self) -> CompatibleIter<'a> { CompatibleIter { iter: self.string.split('\0') } } + + pub fn compatible_with(self, kind: &str) -> bool { + self.all().any(|c| c == kind) + } } impl<'a> IntoIterator for Compatible<'a> { diff --git a/src/standard_nodes.rs b/src/standard_nodes.rs index f3180c4..61986ca 100644 --- a/src/standard_nodes.rs +++ b/src/standard_nodes.rs @@ -4,7 +4,9 @@ use crate::{ nodes::{IntoSearchableNodeName, Node, RawNode, SearchableNodeName}, - parsing::{aligned::AlignedParser, BigEndianToken, Panic, ParseError, Parser, ParserWithMode}, + parsing::{ + aligned::AlignedParser, BigEndianToken, NoPanic, Panic, ParseError, Parser, ParserWithMode, + }, properties::{CellSizes, Compatible, PHandle, Property}, tryblock, FdtError, }; @@ -222,6 +224,30 @@ impl<'a, P: ParserWithMode<'a>> Root<'a, P> { })) } + /// Returns an iterator that yields every node with the name that matches + /// `name` in depth-first order + pub fn find_all_nodes_with_name<'b>( + self, + name: &'b str, + ) -> P::Output> { + P::to_output(crate::tryblock!({ + let this = Root { node: self.node.fallible() }; + Ok(AllNodesWithNameIter { iter: this.all_nodes()?, name }) + })) + } + + /// Attempt to find a node with the given name, returning the first node + /// with a name that matches `name` in depth-first order + pub fn find_node_by_name(self, name: &str) -> P::Output>> { + P::to_output(crate::tryblock!({ + let this = Root { node: self.node.fallible() }; + this.find_all_nodes_with_name(name)?.next().transpose().map(|n| n.map(|n| n.alt())) + })) + } + + /// Attempt to find a node with the given path (with an optional unit + /// address, defaulting to the first matching name if omitted). If you only + /// have the node name but not the path, use [`Root::find_node_by_name`] instead. #[track_caller] pub fn find_node(self, path: &str) -> P::Output>> where @@ -275,7 +301,31 @@ impl<'a, P: ParserWithMode<'a>> Root<'a, P> { P::to_output(Ok(found_node.map(|n| n.alt::

()))) } - pub fn all_nodes(self) -> P::Output> { + /// Returns an iterator over every node within the devicetree which is + /// compatible with at least one of the compatible strings contained within + /// `with` + #[track_caller] + pub fn all_compatible<'b>(self, with: &'b [&str]) -> P::Output> { + P::to_output(crate::tryblock!({ + let this = Root { node: self.node.fallible() }; + let f: fn(_) -> _ = + |node: Result<(usize, Node<'a, (P::Parser, NoPanic)>), FdtError>| match node + .and_then(|(_, n)| Ok((n, n.property::()?))) + { + Ok((n, compatible)) => Some(Ok((n, compatible?))), + Err(e) => Some(Err(e)), + }; + + let iter = this.all_nodes()?.filter_map(f); + + Ok(AllCompatibleIter { iter, with }) + })) + } + + /// Returns an iterator over each node in the tree, depth-first, along with + /// its depth in the tree + #[track_caller] + pub fn all_nodes(self) -> P::Output> { let mut parser = P::new(self.node.this.as_slice(), self.node.strings, self.node.structs); let res = tryblock!({ parser.advance_cstr()?; @@ -291,7 +341,7 @@ impl<'a, P: ParserWithMode<'a>> Root<'a, P> { return P::to_output(Err(e)); } - P::to_output(Ok(AllNodesIterator { + P::to_output(Ok(AllNodesIter { parser, parents: [ self.node.this.as_slice(), @@ -329,13 +379,69 @@ impl<'a, P: ParserWithMode<'a>> core::fmt::Debug for Root<'a, P> { } } -pub struct AllNodesIterator<'a, P: ParserWithMode<'a>> { +pub struct AllNodesWithNameIter<'a, 'b, P: ParserWithMode<'a>> { + iter: AllNodesIter<'a, (P::Parser, NoPanic)>, + name: &'b str, +} + +impl<'a, 'b, P: ParserWithMode<'a>> Iterator for AllNodesWithNameIter<'a, 'b, P> { + type Item = P::Output>; + + #[track_caller] + fn next(&mut self) -> Option { + while let Some(next) = self.iter.next() { + match next.and_then(|(_, n)| Ok((n, n.name()?))) { + Ok((node, name)) => match name.name == self.name { + true => return Some(P::to_output(Ok(node.alt()))), + false => continue, + }, + Err(e) => return Some(P::to_output(Err(e))), + } + } + + None + } +} + +/// Iterator created by [`Root::all_compatible`] +pub struct AllCompatibleIter<'a, 'b, P: ParserWithMode<'a>> { + iter: core::iter::FilterMap< + AllNodesIter<'a, (P::Parser, NoPanic)>, + fn( + Result<(usize, Node<'a, (P::Parser, NoPanic)>), FdtError>, + ) -> Option, Compatible<'a>), FdtError>>, + >, + with: &'b [&'b str], +} + +impl<'a, 'b, P: ParserWithMode<'a>> Iterator for AllCompatibleIter<'a, 'b, P> { + type Item = P::Output>; + + #[track_caller] + fn next(&mut self) -> Option { + while let Some(next) = self.iter.next() { + match next { + Ok((node, compatible)) => { + match self.with.iter().copied().any(|c| compatible.compatible_with(c)) { + true => return Some(P::to_output(Ok(node.alt()))), + false => continue, + } + } + Err(e) => return Some(P::to_output(Err(e))), + } + } + + None + } +} + +pub struct AllNodesIter<'a, P: ParserWithMode<'a>> { parser: P, parents: [&'a [

>::Granularity]; 16], parent_index: usize, } -impl<'a, P: ParserWithMode<'a>> Iterator for AllNodesIterator<'a, P> { +impl<'a, P: ParserWithMode<'a>> Iterator for AllNodesIter<'a, P> { type Item = P::Output<(usize, Node<'a, P>)>; #[track_caller] From bc9fd2ebd05d2eb2aa70024ab87957caa3cc82a1 Mon Sep 17 00:00:00 2001 From: repnop Date: Wed, 17 Jul 2024 23:03:17 -0400 Subject: [PATCH 18/38] buncha stuff --- src/lib.rs | 100 ++++++++++----------------- src/nodes.rs | 9 +-- src/pretty_print.rs | 2 +- src/standard_nodes.rs | 155 +++++++++++++++++++++++++----------------- 4 files changed, 135 insertions(+), 131 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 05c8018..f722a18 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -82,6 +82,8 @@ pub enum FdtError { BadMagic, /// The given pointer was null BadPtr, + /// The provided slice is smaller than the required size given by the header + SliceTooSmall, /// An error was encountered during parsing ParseError(ParseError), PHandleNotFound(u32), @@ -102,6 +104,7 @@ impl core::fmt::Display for FdtError { match self { FdtError::BadMagic => write!(f, "bad FDT magic value"), FdtError::BadPtr => write!(f, "an invalid pointer was passed"), + FdtError::SliceTooSmall => write!(f, "provided slice is too small"), FdtError::ParseError(e) => core::fmt::Display::fmt(e, f), FdtError::PHandleNotFound(value) => write!( f, @@ -126,9 +129,9 @@ impl core::fmt::Display for FdtError { /// which looks similar to `dtc`'s output, enable the `pretty-printing` feature #[derive(Clone, Copy)] pub struct Fdt<'a, P: ParserWithMode<'a>> { - parser: P, + structs: StructsBlock<'a, P::Granularity>, + strings: StringsBlock<'a>, header: FdtHeader, - _lifetime: core::marker::PhantomData<&'a [u8]>, } impl<'a, P: ParserWithMode<'a>> core::fmt::Debug for Fdt<'a, P> { @@ -137,23 +140,15 @@ impl<'a, P: ParserWithMode<'a>> core::fmt::Debug for Fdt<'a, P> { } } -impl<'a, P: ParserWithMode<'a> + 'a> core::fmt::Display for Fdt<'a, P> { +impl<'a, P: ParserWithMode<'a>> core::fmt::Display for Fdt<'a, P> { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - let this: Fdt<'a, (P::Parser, NoPanic)> = Fdt { - _lifetime: core::marker::PhantomData, - header: self.header, - parser: <(P::Parser, NoPanic)>::new( - self.parser.data(), - self.parser.strings(), - self.parser.structs(), - ), - }; + let mut parser: (P::Parser, NoPanic) = <_>::new(self.structs.0, self.strings, self.structs); - let Ok(root) = this.root() else { + let Ok(node) = parser.parse_root() else { return Err(core::fmt::Error); }; - pretty_print::print_fdt(f, root) + pretty_print::print_fdt(f, Root { node }) } } @@ -194,6 +189,13 @@ impl<'a> Fdt<'a, (UnalignedParser<'a>, Panic)> { pub fn new_unaligned(data: &'a [u8]) -> Result { let mut parser = UnalignedParser::new(data, StringsBlock(&[]), StructsBlock(&[])); let header = parser.parse_header()?; + + if data.len() < (header.strings_offset + header.strings_size) as usize { + return Err(FdtError::SliceTooSmall); + } else if data.len() < (header.structs_offset + header.structs_size) as usize { + return Err(FdtError::SliceTooSmall); + } + let strings = StringsBlock(&data[header.strings_offset as usize..][..header.strings_size as usize]); let structs = @@ -202,14 +204,10 @@ impl<'a> Fdt<'a, (UnalignedParser<'a>, Panic)> { if !header.valid_magic() { return Err(FdtError::BadMagic); } else if data.len() < header.total_size as usize { - return Err(FdtError::ParseError(ParseError::UnexpectedEndOfData)); + return Err(FdtError::SliceTooSmall); } - Ok(Self { - header, - parser: (UnalignedParser::new(structs.0, strings, structs), Panic), - _lifetime: core::marker::PhantomData, - }) + Ok(Self { header, structs, strings }) } /// # Safety @@ -238,6 +236,12 @@ impl<'a> Fdt<'a, (AlignedParser<'a>, Panic)> { let mut parser = AlignedParser::new(data, StringsBlock(&[]), StructsBlock(&[])); let header = parser.parse_header()?; + if data.len() < (header.strings_offset + header.strings_size) as usize / 4 { + return Err(FdtError::SliceTooSmall); + } else if data.len() < (header.structs_offset + header.structs_size) as usize / 4 { + return Err(FdtError::SliceTooSmall); + } + let strings_start = header.strings_offset as usize; let strings_end = strings_start + header.strings_size as usize; let strings = StringsBlock( @@ -259,11 +263,7 @@ impl<'a> Fdt<'a, (AlignedParser<'a>, Panic)> { return Err(FdtError::ParseError(ParseError::UnexpectedEndOfData)); } - Ok(Self { - header, - parser: (AlignedParser::new(structs.0, strings, structs), Panic), - _lifetime: core::marker::PhantomData, - }) + Ok(Self { header, strings, structs }) } /// # Safety @@ -289,60 +289,32 @@ impl<'a> Fdt<'a, (AlignedParser<'a>, Panic)> { impl<'a> Fdt<'a, (UnalignedParser<'a>, NoPanic)> { /// Construct a new `Fdt` from a byte buffer pub fn new_unaligned_fallible(data: &'a [u8]) -> Result { - let Fdt { parser, header, .. } = Fdt::new_unaligned(data)?; - Ok(Self { - parser: ( - UnalignedParser::new(parser.data(), parser.strings(), parser.structs()), - NoPanic, - ), - header, - _lifetime: core::marker::PhantomData, - }) + let Fdt { header, strings, structs } = Fdt::new_unaligned(data)?; + Ok(Self { header, strings, structs }) } /// # Safety /// This function performs a read to verify the magic value. If the pointer /// is invalid this can result in undefined behavior. pub unsafe fn from_ptr_unaligned_fallible(ptr: *const u8) -> Result { - let Fdt { parser, header, .. } = Fdt::from_ptr_unaligned(ptr)?; - Ok(Self { - parser: ( - UnalignedParser::new(parser.data(), parser.strings(), parser.structs()), - NoPanic, - ), - header, - _lifetime: core::marker::PhantomData, - }) + let Fdt { header, strings, structs } = Fdt::from_ptr_unaligned(ptr)?; + Ok(Self { header, strings, structs }) } } impl<'a> Fdt<'a, (AlignedParser<'a>, NoPanic)> { /// Construct a new `Fdt` from a `u32`-aligned buffer which won't panic on invalid data pub fn new_fallible(data: &'a [u32]) -> Result { - let Fdt { parser, header, .. } = Fdt::new(data)?; - Ok(Self { - parser: ( - AlignedParser::new(parser.data(), parser.strings(), parser.structs()), - NoPanic, - ), - header, - _lifetime: core::marker::PhantomData, - }) + let Fdt { header, strings, structs } = Fdt::new(data)?; + Ok(Self { header, strings, structs }) } /// # Safety /// This function performs a read to verify the magic value. If the pointer /// is invalid this can result in undefined behavior. pub unsafe fn from_ptr_fallible(ptr: *const u32) -> Result { - let Fdt { parser, header, .. } = Fdt::from_ptr(ptr)?; - Ok(Self { - parser: ( - AlignedParser::new(parser.data(), parser.strings(), parser.structs()), - NoPanic, - ), - header, - _lifetime: core::marker::PhantomData, - }) + let Fdt { header, strings, structs } = Fdt::from_ptr(ptr)?; + Ok(Self { header, strings, structs }) } } @@ -414,7 +386,7 @@ impl<'a, P: ParserWithMode<'a>> Fdt<'a, P> { /// Return the root (`/`) node, which is always available pub fn root(&self) -> P::Output> { - let mut parser = self.parser.clone(); + let mut parser = P::new(self.structs.0, self.strings, self.structs); P::to_output(parser.parse_root().map(|node| Root { node })) } @@ -555,10 +527,10 @@ impl<'a, P: ParserWithMode<'a>> Fdt<'a, P> { } pub fn strings_block(&self) -> &'a [u8] { - self.parser.strings().0 + self.strings.0 } pub fn structs_block(&self) -> &'a [P::Granularity] { - self.parser.structs().0 + self.structs.0 } } diff --git a/src/nodes.rs b/src/nodes.rs index 35a0985..daf7c9d 100644 --- a/src/nodes.rs +++ b/src/nodes.rs @@ -70,6 +70,8 @@ impl core::fmt::Display for NodeName<'_> { } } +pub type FallibleNode<'a, P: ParserWithMode<'a>> = Node<'a, (P::Parser, NoPanic)>; + pub struct Node<'a, P: ParserWithMode<'a>> { pub(crate) this: &'a RawNode<

>::Granularity>, pub(crate) parent: Option<&'a RawNode<

>::Granularity>>, @@ -365,7 +367,7 @@ pub struct NodeChildren<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> } impl<'a, P: ParserWithMode<'a>> NodeChildren<'a, P> { - pub fn iter(self) -> NodeChildrenIter<'a, P> { + pub fn iter(&self) -> NodeChildrenIter<'a, P> { NodeChildrenIter { children: NodeChildren { data: self.data, @@ -404,9 +406,8 @@ impl<'a, P: ParserWithMode<'a>> NodeChildren<'a, P> { pub fn find<'n, N>(&self, name: N) -> P::Output>> where N: IntoSearchableNodeName<'n>, - P: 'a, { - let this: NodeChildren<'a, (P::Parser, NoPanic)> = NodeChildren { + let this: NodeChildren<(P::Parser, NoPanic)> = NodeChildren { data: self.data, parent: self.parent, strings: self.strings, @@ -448,7 +449,7 @@ pub struct NodeChildrenIter<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Pani children: NodeChildren<'a, (P::Parser, NoPanic)>, } -impl<'a, P: ParserWithMode<'a> + 'a> Iterator for NodeChildrenIter<'a, P> { +impl<'a, P: ParserWithMode<'a>> Iterator for NodeChildrenIter<'a, P> { type Item = P::Output>; #[track_caller] diff --git a/src/pretty_print.rs b/src/pretty_print.rs index 6967e3c..ba5324b 100644 --- a/src/pretty_print.rs +++ b/src/pretty_print.rs @@ -46,7 +46,7 @@ impl From for core::fmt::Error { } } -pub fn print_fdt<'a, P: Parser<'a> + 'a>( +pub fn print_fdt<'a, P: Parser<'a>>( f: &mut core::fmt::Formatter<'_>, root: Root<'a, (P, NoPanic)>, ) -> core::fmt::Result { diff --git a/src/standard_nodes.rs b/src/standard_nodes.rs index 61986ca..a16fb08 100644 --- a/src/standard_nodes.rs +++ b/src/standard_nodes.rs @@ -3,7 +3,7 @@ // obtain one at https://mozilla.org/MPL/2.0/. use crate::{ - nodes::{IntoSearchableNodeName, Node, RawNode, SearchableNodeName}, + nodes::{FallibleNode, IntoSearchableNodeName, Node, RawNode, SearchableNodeName}, parsing::{ aligned::AlignedParser, BigEndianToken, NoPanic, Panic, ParseError, Parser, ParserWithMode, }, @@ -16,7 +16,7 @@ pub struct Chosen<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { pub(crate) node: Node<'a, P>, } -impl<'a, P: ParserWithMode<'a> + 'a> Chosen<'a, P> { +impl<'a, P: ParserWithMode<'a>> Chosen<'a, P> { /// Contains the bootargs, if they exist #[track_caller] pub fn bootargs(self) -> P::Output> { @@ -51,7 +51,9 @@ impl<'a, P: ParserWithMode<'a> + 'a> Chosen<'a, P> { Ok(property) => match property.name() == "stdout-path" { false => None, true => Some(property.as_value::<&'a str>().map_err(Into::into).map(|s| { - let (path, params) = Self::split_stdinout_property(s); + let (path, params) = s + .split_once(':') + .map_or_else(|| (s, None), |(name, params)| (name, Some(params))); StdInOutPath { path, params } })), }, @@ -75,8 +77,10 @@ impl<'a, P: ParserWithMode<'a> + 'a> Chosen<'a, P> { Err(e) => Some(Err(e)), Ok(property) => match property.name() == "stdin-path" { false => None, - true => Some(property.as_value::<&'a str>().map_err(Into::into).map(|s| { - let (path, params) = Self::split_stdinout_property(s); + true => Some(property.as_value::<&str>().map_err(Into::into).map(|s| { + let (path, params) = s + .split_once(':') + .map_or_else(|| (s, None), |(name, params)| (name, Some(params))); StdInOutPath { path, params } })), }, @@ -84,12 +88,6 @@ impl<'a, P: ParserWithMode<'a> + 'a> Chosen<'a, P> { .transpose() })) } - - fn split_stdinout_property(property: &str) -> (&str, Option<&str>) { - property - .split_once(':') - .map_or_else(|| (property, None), |(name, params)| (name, Some(params))) - } } impl<'a, P: ParserWithMode<'a>> Clone for Chosen<'a, P> { @@ -147,7 +145,13 @@ pub struct Root<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { } impl<'a, P: ParserWithMode<'a>> Root<'a, P> { - /// Root node cell sizes + /// [Devicetree 3.2. Root + /// node](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#root-node) + /// + /// **Required** + /// + /// Specifies the number of cells to represent the address and length + /// in the reg property in children of root. #[track_caller] pub fn cell_sizes(self) -> P::Output { P::to_output(crate::tryblock!({ @@ -161,23 +165,19 @@ impl<'a, P: ParserWithMode<'a>> Root<'a, P> { /// [Devicetree 3.2. Root /// node](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#root-node) /// + /// **Required** + /// /// Specifies a string that uniquely identifies the model of the system /// board. The recommended format is "manufacturer,model-number". #[track_caller] pub fn model(self) -> P::Output<&'a str> { P::to_output(crate::tryblock!({ let node = self.node.fallible(); - node.properties()? - .into_iter() - .find_map(|n| match n { - Err(e) => Some(Err(e)), - Ok(property) => match property.name() == "model" { - false => None, - true => Some(property.as_value::<&'a str>().map_err(Into::into)), - }, - }) - .transpose()? - .ok_or(FdtError::MissingRequiredProperty("model")) + node.properties()?.find("model").and_then(|p| { + p.ok_or(FdtError::MissingRequiredProperty("model"))? + .as_value::<&'a str>() + .map_err(Into::into) + }) })) } @@ -192,25 +192,57 @@ impl<'a, P: ParserWithMode<'a>> Root<'a, P> { /// `"manufacturer,model"` /// /// For example: `compatible = "fsl,mpc8572ds"` - pub fn compatible(self) -> P::Output> { + pub fn compatible(&self) -> P::Output> { P::to_output(crate::tryblock!({ >::parse(self.node.fallible(), self.node.make_root()?)? .ok_or(FdtError::MissingRequiredProperty("compatible")) })) } - /// Returns an iterator over all of the available properties - // pub fn properties(self) -> impl Iterator> + 'a { - // self.node.properties() - // } + /// [Devicetree 3.2. Root + /// node](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#root-node) + /// + /// **Optional** + /// + /// Specifies a string representing the device’s serial number. + pub fn serial_number(&self) -> P::Output> { + P::to_output(crate::tryblock!({ + match self.node.fallible().properties()?.find("serial-number")? { + Some(prop) => Ok(Some(prop.as_value()?)), + None => Ok(None), + } + })) + } - /// Attempts to find the a property by its name - // pub fn property(self, name: &str) -> Option> { - // self.node.properties().find(|p| p.name == name) - // } + /// [Devicetree 3.2. Root + /// node](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#root-node) + /// + /// **Optional, but Recommended** + /// + /// Specifies a string that identifies the form-factor of the system. The + /// property value can be one of: + /// + /// * "desktop" + /// * "laptop" + /// * "convertible" + /// * "server" + /// * "tablet" + /// * "handset" + /// * "watch" + /// * "embedded" + pub fn chassis_type(&self) -> P::Output> { + P::to_output(crate::tryblock!({ + match self.node.fallible().properties()?.find("serial-number")? { + Some(prop) => Ok(Some(prop.as_value()?)), + None => Ok(None), + } + })) + } + /// Attempt to resolve a [`PHandle`] to the node containing a `phandle` + /// property with the value #[track_caller] - pub fn resolve_phandle(self, phandle: PHandle) -> P::Output>> { + pub fn resolve_phandle(&self, phandle: PHandle) -> P::Output>> { P::to_output(crate::tryblock!({ let this = Root { node: self.node.fallible() }; for node in this.all_nodes()? { @@ -249,10 +281,7 @@ impl<'a, P: ParserWithMode<'a>> Root<'a, P> { /// address, defaulting to the first matching name if omitted). If you only /// have the node name but not the path, use [`Root::find_node_by_name`] instead. #[track_caller] - pub fn find_node(self, path: &str) -> P::Output>> - where - P: 'a, - { + pub fn find_node(self, path: &str) -> P::Output>> { if path == "/" { return P::to_output(Ok(Some(self.node))); } @@ -501,34 +530,36 @@ impl<'a, P: ParserWithMode<'a>> Iterator for AllNodesIter<'a, P> { } } -// /// Represents the `/aliases` node with specific helper methods -// #[derive(Debug, Clone, Copy)] -// pub struct Aliases<'b, 'a: 'b> { -// pub(crate) header: &'b Fdt<'a, crate::UnalignedParser<'a>>, -// pub(crate) node: FdtNode<'b, 'a>, -// } +/// Represents the `/aliases` node with specific helper methods +#[derive(Debug, Clone, Copy)] +pub struct Aliases<'a, P: ParserWithMode<'a>> { + pub(crate) node: FallibleNode<'a, P>, +} -// impl<'b, 'a: 'b> Aliases<'b, 'a> { -// /// Attempt to resolve an alias to a node name -// pub fn resolve(self, alias: &str) -> Option<&'a str> { -// self.node -// .properties() -// .find(|p| p.name == alias) -// .and_then(|p| core::str::from_utf8(p.value).map(|s| s.trim_end_matches('\0')).ok()) -// } +impl<'a, P: ParserWithMode<'a>> Aliases<'a, P> { + /// Attempt to resolve an alias to a node name + pub fn resolve_name(self, alias: &str) -> P::Output> { + P::to_output(crate::tryblock!({ + self.node + .properties()? + .find(alias)? + .map(|p| p.as_value().map_err(Into::into)) + .transpose() + })) + } -// /// Attempt to find the node specified by the given alias -// pub fn resolve_node(self, alias: &str) -> Option> { -// self.resolve(alias).and_then(|name| self.header.find_node(name)) -// } + /// Attempt to find the node specified by the given alias + pub fn resolve(self, alias: &str) -> P::Output>> { + P::to_output(crate::tryblock!({ + let Some(path) = Aliases::<(_, NoPanic)> { node: self.node }.resolve_name(alias)? + else { + return Ok(None); + }; -// /// Returns an iterator over all of the available aliases -// pub fn all(self) -> impl Iterator + 'b { -// self.node.properties().filter_map(|p| { -// Some((p.name, core::str::from_utf8(p.value).map(|s| s.trim_end_matches('\0')).ok()?)) -// }) -// } -// } + self.node.make_root::()?.find_node(path).map(|r| r.map(|n| n.alt())) + })) + } +} // /// Represents a `/cpus/cpu*` node with specific helper methods // #[derive(Debug, Clone, Copy)] From 2927b3c5716ef24f91621a9946076d61a7968ce1 Mon Sep 17 00:00:00 2001 From: repnop Date: Fri, 26 Jul 2024 18:23:40 -0400 Subject: [PATCH 19/38] fix --- src/standard_nodes.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/standard_nodes.rs b/src/standard_nodes.rs index a16fb08..b7bbe6a 100644 --- a/src/standard_nodes.rs +++ b/src/standard_nodes.rs @@ -131,8 +131,8 @@ impl<'a> StdInOutPath<'a> { /// let stdout = chosen.stdout().unwrap(); /// let stdin = chosen.stdin().unwrap(); /// - /// assert_eq!((stdout.path(), stdout.params()), ("soc/uart@10000000", None)); - /// assert_eq!((stdin.path(), stdin.params()), ("soc/uart@10000000", Some("115200"))); + /// assert_eq!((stdout.path(), stdout.params()), ("/soc/uart@10000000", None)); + /// assert_eq!((stdin.path(), stdin.params()), ("/soc/uart@10000000", Some("115200"))); /// ``` pub fn params(&self) -> Option<&'a str> { self.params From d332c95f80ab8f4d5978c1f9269e0360e016c950 Mon Sep 17 00:00:00 2001 From: repnop Date: Sun, 28 Jul 2024 18:33:08 -0400 Subject: [PATCH 20/38] some `cpu` stuff --- .direnv/flake-profile | 2 +- .direnv/flake-profile-3-link | 1 - .direnv/flake-profile-4-link | 1 + flake.lock | 15 +- src/standard_nodes.rs | 288 ++++++++++++++++++++++++----------- 5 files changed, 210 insertions(+), 97 deletions(-) delete mode 120000 .direnv/flake-profile-3-link create mode 120000 .direnv/flake-profile-4-link diff --git a/.direnv/flake-profile b/.direnv/flake-profile index 519b17b..e289079 120000 --- a/.direnv/flake-profile +++ b/.direnv/flake-profile @@ -1 +1 @@ -flake-profile-3-link \ No newline at end of file +flake-profile-4-link \ No newline at end of file diff --git a/.direnv/flake-profile-3-link b/.direnv/flake-profile-3-link deleted file mode 120000 index ce99e09..0000000 --- a/.direnv/flake-profile-3-link +++ /dev/null @@ -1 +0,0 @@ -/nix/store/ixwhpjrhm228172lh148h40pcz68k7lg-nix-shell-env \ No newline at end of file diff --git a/.direnv/flake-profile-4-link b/.direnv/flake-profile-4-link new file mode 120000 index 0000000..b03c926 --- /dev/null +++ b/.direnv/flake-profile-4-link @@ -0,0 +1 @@ +/nix/store/d302gd4kc0pxx0bjxnq370vbl4jirf5n-nix-shell-env \ No newline at end of file diff --git a/flake.lock b/flake.lock index e0b0c71..6dfe80a 100644 --- a/flake.lock +++ b/flake.lock @@ -20,11 +20,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1718318537, - "narHash": "sha256-4Zu0RYRcAY/VWuu6awwq4opuiD//ahpc2aFHg2CWqFY=", + "lastModified": 1722062969, + "narHash": "sha256-QOS0ykELUmPbrrUGmegAUlpmUFznDQeR4q7rFhl8eQg=", "owner": "nixos", "repo": "nixpkgs", - "rev": "e9ee548d90ff586a6471b4ae80ae9cfcbceb3420", + "rev": "b73c2221a46c13557b1b3be9c2070cc42cf01eb3", "type": "github" }, "original": { @@ -43,19 +43,16 @@ }, "rust-overlay": { "inputs": { - "flake-utils": [ - "flake-utils" - ], "nixpkgs": [ "nixpkgs" ] }, "locked": { - "lastModified": 1718331519, - "narHash": "sha256-6Ru37wS8uec626nHVIh6hSpCYB7eNc3RPFa2U//bhw4=", + "lastModified": 1722133294, + "narHash": "sha256-XKSVN+lmjVEFPjMa5Ui0VTay2Uvqa74h0MQT0HU1pqw=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "419e7fae2731f41dd9b3e34dfe8802be68558b92", + "rev": "9803f6e04ca37a2c072783e8297d2080f8d0e739", "type": "github" }, "original": { diff --git a/src/standard_nodes.rs b/src/standard_nodes.rs index b7bbe6a..f0ba486 100644 --- a/src/standard_nodes.rs +++ b/src/standard_nodes.rs @@ -7,7 +7,10 @@ use crate::{ parsing::{ aligned::AlignedParser, BigEndianToken, NoPanic, Panic, ParseError, Parser, ParserWithMode, }, - properties::{CellSizes, Compatible, PHandle, Property}, + properties::{ + AddressCells, BuildCellCollector, CellCollector, CellSizes, CollectCellsError, Compatible, + PHandle, Property, + }, tryblock, FdtError, }; @@ -448,7 +451,7 @@ impl<'a, 'b, P: ParserWithMode<'a>> Iterator for AllCompatibleIter<'a, 'b, P> { #[track_caller] fn next(&mut self) -> Option { - while let Some(next) = self.iter.next() { + for next in self.iter.by_ref() { match next { Ok((node, compatible)) => { match self.with.iter().copied().any(|c| compatible.compatible_with(c)) { @@ -561,98 +564,211 @@ impl<'a, P: ParserWithMode<'a>> Aliases<'a, P> { } } -// /// Represents a `/cpus/cpu*` node with specific helper methods -// #[derive(Debug, Clone, Copy)] -// pub struct Cpu<'b, 'a: 'b> { -// pub(crate) parent: FdtNode<'b, 'a>, -// pub(crate) node: FdtNode<'b, 'a>, -// } +/// Represents a `/cpus/cpu*` node with specific helper methods +#[derive(Debug, Clone, Copy)] +pub struct Cpu<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { + node: FallibleNode<'a, P>, +} -// impl<'b, 'a: 'b> Cpu<'b, 'a> { -// /// Return the IDs for the given CPU -// pub fn ids(self) -> CpuIds<'a> { -// let address_cells = self.node.parent_cell_sizes().address_cells; - -// CpuIds { -// reg: self -// .node -// .properties() -// .find(|p| p.name == "reg") -// .expect("reg is a required property of cpu nodes"), -// address_cells, -// } -// } +impl<'a, P: ParserWithMode<'a>> Cpu<'a, P> { + /// [Devicetree 3.8.1 + /// `reg`](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#general-properties-of-cpus-cpu-nodes) + /// + /// **Required** + /// + /// The value of `reg` is a `` that defines a unique + /// CPU/thread id for the CPU/threads represented by the CPU node. + /// + /// If a CPU supports more than one thread (i.e. multiple streams of + /// execution) the `reg` property is an array with 1 element per thread. The + /// `#address-cells` on the `/cpus` node specifies how many cells each + /// element of the array takes. Software can determine the number of threads + /// by dividing the size of `reg` by the parent node’s `#address-cells`. + /// + /// If a CPU/thread can be the target of an external interrupt the `reg` + /// property value must be a unique CPU/thread id that is addressable by the + /// interrupt controller. + /// + /// If a CPU/thread cannot be the target of an external interrupt, then + /// `reg` must be unique and out of bounds of the range addressed by the + /// interrupt controller + /// + /// If a CPU/thread’s PIR (pending interrupt register) is modifiable, a + /// client program should modify PIR to match the `reg` property value. If + /// PIR cannot be modified and the PIR value is distinct from the interrupt + /// controller number space, the CPUs binding may define a binding-specific + /// representation of PIR values if desired. + pub fn reg(self) -> P::Output> { + P::to_output(crate::tryblock!({ + let Some(reg) = self.node.properties()?.find("reg")? else { + return Err(FdtError::MissingRequiredProperty("reg")); + }; -// /// `clock-frequency` property -// pub fn clock_frequency(self) -> usize { -// self.node -// .properties() -// .find(|p| p.name == "clock-frequency") -// .or_else(|| self.parent.property("clock-frequency")) -// .map(|p| match p.value.len() { -// 4 => BigEndianU32::from_bytes(p.value).unwrap().to_ne() as usize, -// 8 => BigEndianU64::from_bytes(p.value).unwrap().to_ne() as usize, -// _ => unreachable!(), -// }) -// .expect("clock-frequency is a required property of cpu nodes") -// } + if reg.value().is_empty() { + return Err(FdtError::InvalidPropertyValue); + } -// /// `timebase-frequency` property -// pub fn timebase_frequency(self) -> usize { -// self.node -// .properties() -// .find(|p| p.name == "timebase-frequency") -// .or_else(|| self.parent.property("timebase-frequency")) -// .map(|p| match p.value.len() { -// 4 => BigEndianU32::from_bytes(p.value).unwrap().to_ne() as usize, -// 8 => BigEndianU64::from_bytes(p.value).unwrap().to_ne() as usize, -// _ => unreachable!(), -// }) -// .expect("timebase-frequency is a required property of cpu nodes") -// } + let Some(address_cells) = self.node.parent().unwrap().property::()? + else { + return Err(FdtError::MissingRequiredProperty("#address-cells")); + }; -// /// Returns an iterator over all of the properties for the CPU node -// pub fn properties(self) -> impl Iterator> + 'b { -// self.node.properties() -// } + Ok(CpuIds { + reg: reg.value(), + address_cells: address_cells.0, + _collector: core::marker::PhantomData, + }) + })) + } -// /// Attempts to find the a property by its name -// pub fn property(self, name: &str) -> Option> { -// self.node.properties().find(|p| p.name == name) -// } -// } + /// [Devicetree 3.8.1 + /// `clock-frequency`](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#general-properties-of-cpus-cpu-nodes) + /// + /// **Required** + /// + /// Specifies the current clock speed of the CPU in Hertz. The value is a + /// `` in one of two forms: + /// + /// * A 32-bit integer consisting of one `` specifying the frequency. + /// * A 64-bit integer represented as a `` specifying the frequency. + pub fn clock_frequency(self) -> P::Output { + P::to_output(crate::tryblock!({ + match self.node.properties()?.find("clock-frequency")? { + Some(prop) => match prop.value().len() { + 4 => Ok(u64::from(prop.as_value::()?)), + 8 => Ok(prop.as_value::()?), + _ => Err(FdtError::InvalidPropertyValue), + }, + None => { + let prop = self + .node + .parent() + .unwrap() + .properties()? + .find("clock-frequency")? + .ok_or(FdtError::MissingRequiredProperty("clock-frequency"))?; + + match prop.value().len() { + 4 => Ok(u64::from(prop.as_value::()?)), + 8 => Ok(prop.as_value::()?), + _ => Err(FdtError::InvalidPropertyValue), + } + } + } + })) + } -// /// Represents the value of the `reg` property of a `/cpus/cpu*` node which may -// /// contain more than one CPU or thread ID -// #[derive(Debug, Clone, Copy)] -// pub struct CpuIds<'a> { -// pub(crate) reg: NodeProperty<'a>, -// pub(crate) address_cells: usize, -// } + /// [Devicetree 3.8.1 + /// `timebase-frequency`](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#general-properties-of-cpus-cpu-nodes) + /// + /// **Required** + /// + /// Specifies the current frequency at which the timebase and decrementer + /// registers are updated (in Hertz). The value is a `` in + /// one of two forms: + /// + /// * A 32-bit integer consisting of one `` specifying the frequency. + /// * A 64-bit integer represented as a ``. + pub fn timebase_frequency(self) -> P::Output { + P::to_output(crate::tryblock!({ + match self.node.properties()?.find("timebase-frequency")? { + Some(prop) => match prop.value().len() { + 4 => Ok(u64::from(prop.as_value::()?)), + 8 => Ok(prop.as_value::()?), + _ => Err(FdtError::InvalidPropertyValue), + }, + None => { + let prop = self + .node + .parent() + .unwrap() + .properties()? + .find("timebase-frequency")? + .ok_or(FdtError::MissingRequiredProperty("timebase-frequency"))?; + + match prop.value().len() { + 4 => Ok(u64::from(prop.as_value::()?)), + 8 => Ok(prop.as_value::()?), + _ => Err(FdtError::InvalidPropertyValue), + } + } + } + })) + } +} -// impl<'a> CpuIds<'a> { -// /// The first listed CPU ID, which will always exist -// pub fn first(self) -> usize { -// match self.address_cells { -// 1 => BigEndianU32::from_bytes(self.reg.value).unwrap().to_ne() as usize, -// 2 => BigEndianU64::from_bytes(self.reg.value).unwrap().to_ne() as usize, -// n => panic!("address-cells of size {} is currently not supported", n), -// } -// } +/// See [`Cpu::reg`] +pub struct CpuIds<'a, C: CellCollector> { + reg: &'a [u8], + address_cells: usize, + _collector: core::marker::PhantomData<*mut C>, +} -// /// Returns an iterator over all of the listed CPU IDs -// pub fn all(self) -> impl Iterator + 'a { -// let mut vals = FdtData::new(self.reg.value); -// core::iter::from_fn(move || match vals.remaining() { -// [] => None, -// _ => Some(match self.address_cells { -// 1 => vals.u32()?.to_ne() as usize, -// 2 => vals.u64()?.to_ne() as usize, -// n => panic!("address-cells of size {} is currently not supported", n), -// }), -// }) -// } -// } +impl<'a, C: CellCollector> CpuIds<'a, C> { + /// The first listed CPU ID, which will always exist + pub fn first(&self) -> Result { + self.iter().next().unwrap() + } + + pub fn iter(&self) -> CpuIdsIter<'a, C> { + CpuIdsIter { + reg: self.reg, + address_cells: self.address_cells, + _collector: core::marker::PhantomData, + } + } +} + +impl Copy for CpuIds<'_, C> {} +impl Clone for CpuIds<'_, C> { + fn clone(&self) -> Self { + *self + } +} + +impl<'a, C: CellCollector> core::fmt::Debug for CpuIds<'a, C> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("CpuIds") + .field("reg", &self.reg) + .field("address_cells", &self.address_cells) + .finish_non_exhaustive() + } +} + +#[derive(Debug)] +pub struct CpuIdsIter<'a, C: CellCollector> { + reg: &'a [u8], + address_cells: usize, + _collector: core::marker::PhantomData<*mut C>, +} + +impl Clone for CpuIdsIter<'_, C> { + fn clone(&self) -> Self { + Self { + address_cells: self.address_cells, + reg: self.reg, + _collector: core::marker::PhantomData, + } + } +} + +impl<'a, C: CellCollector> Iterator for CpuIdsIter<'a, C> { + type Item = Result; + fn next(&mut self) -> Option { + let (this_cell, rest) = self.reg.split_at_checked(self.address_cells * 4)?; + self.reg = rest; + + let mut collector = ::Builder::default(); + + for cell in this_cell.chunks_exact(4) { + if let Err(e) = collector.push(u32::from_be_bytes(cell.try_into().unwrap())) { + return Some(Err(e)); + } + } + + Some(Ok(C::map(collector.finish()))) + } +} /// Represents the `/memory` node with specific helper methods // #[derive(Debug, Clone, Copy)] From 56d48a7a1fc3161d334f75b9e3d8b593939fced9 Mon Sep 17 00:00:00 2001 From: repnop Date: Mon, 29 Jul 2024 22:37:55 -0400 Subject: [PATCH 21/38] hmm yes --- rustfmt.toml | 1 + src/lib.rs | 32 +++---- src/nodes.rs | 35 +++---- src/parsing.rs | 62 +++++------- src/parsing/aligned.rs | 27 ++---- src/parsing/unaligned.rs | 3 +- src/pretty_print.rs | 37 ++------ src/properties.rs | 198 ++++++++++++--------------------------- src/standard_nodes.rs | 97 ++++++------------- src/tests.rs | 24 +---- 10 files changed, 156 insertions(+), 360 deletions(-) diff --git a/rustfmt.toml b/rustfmt.toml index 2a35f02..4da9651 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1 +1,2 @@ use_small_heuristics = "Max" +max_width = 120 diff --git a/src/lib.rs b/src/lib.rs index f722a18..2b9d51d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -65,8 +65,8 @@ pub mod standard_nodes; mod util; use parsing::{ - aligned::AlignedParser, unaligned::UnalignedParser, NoPanic, Panic, ParseError, Parser, - ParserWithMode, StringsBlock, StructsBlock, + aligned::AlignedParser, unaligned::UnalignedParser, NoPanic, Panic, ParseError, Parser, ParserWithMode, + StringsBlock, StructsBlock, }; use standard_nodes::Root; // use standard_nodes::{Aliases, Chosen, Cpu, Memory, MemoryRange, MemoryRegion, Root}; @@ -106,10 +106,9 @@ impl core::fmt::Display for FdtError { FdtError::BadPtr => write!(f, "an invalid pointer was passed"), FdtError::SliceTooSmall => write!(f, "provided slice is too small"), FdtError::ParseError(e) => core::fmt::Display::fmt(e, f), - FdtError::PHandleNotFound(value) => write!( - f, - "a node containing the `phandle` property value of `{value}` was not found" - ), + FdtError::PHandleNotFound(value) => { + write!(f, "a node containing the `phandle` property value of `{value}` was not found") + } FdtError::MissingRequiredNode(name) => { write!(f, "FDT is missing a required node `{}`", name) } @@ -117,7 +116,9 @@ impl core::fmt::Display for FdtError { write!(f, "FDT node is missing a required property `{}`", name) } FdtError::InvalidPropertyValue => write!(f, "FDT property value is invalid"), - FdtError::CollectCellsError => write!(f, "overflow occurred while collecting `#-cells` size values into the desired type") + FdtError::CollectCellsError => { + write!(f, "overflow occurred while collecting `#-cells` size values into the desired type") + } } } } @@ -196,10 +197,8 @@ impl<'a> Fdt<'a, (UnalignedParser<'a>, Panic)> { return Err(FdtError::SliceTooSmall); } - let strings = - StringsBlock(&data[header.strings_offset as usize..][..header.strings_size as usize]); - let structs = - StructsBlock(&data[header.structs_offset as usize..][..header.structs_size as usize]); + let strings = StringsBlock(&data[header.strings_offset as usize..][..header.strings_size as usize]); + let structs = StructsBlock(&data[header.structs_offset as usize..][..header.structs_size as usize]); if !header.valid_magic() { return Err(FdtError::BadMagic); @@ -220,9 +219,7 @@ impl<'a> Fdt<'a, (UnalignedParser<'a>, Panic)> { let tmp_header = core::slice::from_raw_parts(ptr, core::mem::size_of::()); let real_size = usize::try_from( - UnalignedParser::new(tmp_header, StringsBlock(&[]), StructsBlock(&[])) - .parse_header()? - .total_size, + UnalignedParser::new(tmp_header, StringsBlock(&[]), StructsBlock(&[])).parse_header()?.total_size, ) .map_err(|_| ParseError::NumericConversionError)?; @@ -253,8 +250,7 @@ impl<'a> Fdt<'a, (AlignedParser<'a>, Panic)> { let structs_start = header.structs_offset as usize / 4; let structs_end = structs_start + (header.structs_size as usize / 4); let structs = StructsBlock( - data.get(structs_start..structs_end) - .ok_or(FdtError::ParseError(ParseError::UnexpectedEndOfData))?, + data.get(structs_start..structs_end).ok_or(FdtError::ParseError(ParseError::UnexpectedEndOfData))?, ); if !header.valid_magic() { @@ -276,9 +272,7 @@ impl<'a> Fdt<'a, (AlignedParser<'a>, Panic)> { let tmp_header = core::slice::from_raw_parts(ptr, core::mem::size_of::()); let real_size = usize::try_from( - AlignedParser::new(tmp_header, StringsBlock(&[]), StructsBlock(&[])) - .parse_header()? - .total_size, + AlignedParser::new(tmp_header, StringsBlock(&[]), StructsBlock(&[])).parse_header()?.total_size, ) .map_err(|_| ParseError::NumericConversionError)?; diff --git a/src/nodes.rs b/src/nodes.rs index daf7c9d..e65dcf4 100644 --- a/src/nodes.rs +++ b/src/nodes.rs @@ -1,7 +1,7 @@ use crate::{ parsing::{ - aligned::AlignedParser, BigEndianToken, NoPanic, Panic, PanicMode, ParseError, Parser, - ParserWithMode, StringsBlock, StructsBlock, + aligned::AlignedParser, BigEndianToken, NoPanic, Panic, PanicMode, ParseError, Parser, ParserWithMode, + StringsBlock, StructsBlock, }, properties::{InvalidPropertyValue, Property, PropertyValue, Reg}, standard_nodes::Root, @@ -40,10 +40,9 @@ impl crate::sealed::Sealed for &'_ str {} impl<'a> IntoSearchableNodeName<'a> for &'a str { fn into_searchable_node_name(self) -> SearchableNodeName<'a> { match self.rsplit_once('@') { - Some((base, unit_address)) => SearchableNodeName::WithUnitAddress(NodeName { - name: base, - unit_address: Some(unit_address), - }), + Some((base, unit_address)) => { + SearchableNodeName::WithUnitAddress(NodeName { name: base, unit_address: Some(unit_address) }) + } None => SearchableNodeName::Base(self), } } @@ -111,9 +110,7 @@ impl<'a, P: ParserWithMode<'a>> Node<'a, P> { P::to_output( P::new(&self.this.0, self.strings, self.structs) .advance_cstr() - .and_then(|s| { - s.to_str().map_err(|_| FdtError::ParseError(ParseError::InvalidCStrValue)) - }) + .and_then(|s| s.to_str().map_err(|_| FdtError::ParseError(ParseError::InvalidCStrValue))) .map(|s| { if s.is_empty() { return NodeName { name: "/", unit_address: None }; @@ -232,9 +229,7 @@ pub struct NodeProperties<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic) } impl<'a, P: ParserWithMode<'a>> NodeProperties<'a, P> { - pub(crate) fn alt>( - self, - ) -> NodeProperties<'a, P2> { + pub(crate) fn alt>(self) -> NodeProperties<'a, P2> { NodeProperties { data: self.data, strings: self.strings, @@ -252,15 +247,11 @@ impl<'a, P: ParserWithMode<'a>> NodeProperties<'a, P> { match parser.peek_token() { Ok(BigEndianToken::PROP) => {} - Ok(BigEndianToken::BEGIN_NODE) | Ok(BigEndianToken::END_NODE) => { - return P::to_output(Ok(None)) - } + Ok(BigEndianToken::BEGIN_NODE) | Ok(BigEndianToken::END_NODE) => return P::to_output(Ok(None)), Ok(_) => { return P::to_output(Err(ParseError::UnexpectedToken.into())); } - Err(FdtError::ParseError(ParseError::UnexpectedEndOfData)) => { - return P::to_output(Ok(None)) - } + Err(FdtError::ParseError(ParseError::UnexpectedEndOfData)) => return P::to_output(Ok(None)), Err(e) => return P::to_output(Err(e)), } @@ -386,9 +377,7 @@ impl<'a, P: ParserWithMode<'a>> NodeChildren<'a, P> { Ok(BigEndianToken::BEGIN_NODE) => {} Ok(BigEndianToken::END_NODE) => return P::to_output(Ok(None)), Ok(_) => return P::to_output(Err(ParseError::UnexpectedToken.into())), - Err(FdtError::ParseError(ParseError::UnexpectedEndOfData)) => { - return P::to_output(Ok(None)) - } + Err(FdtError::ParseError(ParseError::UnexpectedEndOfData)) => return P::to_output(Ok(None)), Err(e) => return P::to_output(Err(e)), } @@ -424,9 +413,7 @@ impl<'a, P: ParserWithMode<'a>> NodeChildren<'a, P> { Err(e) => Some(Err(e)), Ok(nn) => match name { SearchableNodeName::Base(base) => (nn.name == base).then_some(Ok(node)), - SearchableNodeName::WithUnitAddress(snn) => { - (nn == snn).then_some(Ok(node)) - } + SearchableNodeName::WithUnitAddress(snn) => (nn == snn).then_some(Ok(node)), }, }, }) diff --git a/src/parsing.rs b/src/parsing.rs index 69a3b3b..d7e75dc 100644 --- a/src/parsing.rs +++ b/src/parsing.rs @@ -91,10 +91,9 @@ impl core::fmt::Display for ParseError { Self::InvalidTokenValue => { write!(f, "encountered invalid FDT token value while parsing") } - Self::NumericConversionError => write!( - f, - "u32 value too large for usize (this should only occur on 16-bit platforms)" - ), + Self::NumericConversionError => { + write!(f, "u32 value too large for usize (this should only occur on 16-bit platforms)") + } Self::UnexpectedEndOfData => { write!(f, "encountered end of data while parsing but expected more") } @@ -141,9 +140,7 @@ pub trait ParserWithMode<'a>: Parser<'a> + PanicMode + crate::sealed::Sealed { type Parser: Parser<'a, Granularity = Self::Granularity>; type Mode: PanicMode + Clone + Default; - fn into_parts( - self, - ) -> (>::Parser, >::Mode); + fn into_parts(self) -> (>::Parser, >::Mode); } impl<'a, T: Parser<'a>, U: PanicMode> crate::sealed::Sealed for (T, U) {} @@ -205,9 +202,7 @@ impl<'a, T: Parser<'a>, U: PanicMode + Clone + Default + 'static> ParserWithMode type Mode = U; type Parser = T; - fn into_parts( - self, - ) -> (>::Parser, >::Mode) { + fn into_parts(self) -> (>::Parser, >::Mode) { self } } @@ -279,12 +274,10 @@ pub trait Parser<'a>: crate::sealed::Sealed + Clone { 1 => { let byte_data = self.byte_data(); match byte_data.get(byte_data.len() - 4..).map(<[u8; 4]>::try_from) { - Some(Ok(data @ [_, _, _, _])) => { - match BigEndianToken(BigEndianU32(u32::from_ne_bytes(data))) { - BigEndianToken::END => {} - _ => return Err(FdtError::ParseError(ParseError::UnexpectedToken)), - } - } + Some(Ok(data @ [_, _, _, _])) => match BigEndianToken(BigEndianU32(u32::from_ne_bytes(data))) { + BigEndianToken::END => {} + _ => return Err(FdtError::ParseError(ParseError::UnexpectedToken)), + }, _ => return Err(FdtError::ParseError(ParseError::UnexpectedEndOfData)), } @@ -299,12 +292,10 @@ pub trait Parser<'a>: crate::sealed::Sealed + Clone { 4 => { let byte_data = self.byte_data(); match byte_data.get(byte_data.len() - 4..).map(<[u8; 4]>::try_from) { - Some(Ok(data @ [_, _, _, _])) => { - match BigEndianToken(BigEndianU32(u32::from_ne_bytes(data))) { - BigEndianToken::END => {} - _ => return Err(FdtError::ParseError(ParseError::UnexpectedToken)), - } - } + Some(Ok(data @ [_, _, _, _])) => match BigEndianToken(BigEndianU32(u32::from_ne_bytes(data))) { + BigEndianToken::END => {} + _ => return Err(FdtError::ParseError(ParseError::UnexpectedToken)), + }, _ => return Err(FdtError::ParseError(ParseError::UnexpectedEndOfData)), } @@ -320,10 +311,7 @@ pub trait Parser<'a>: crate::sealed::Sealed + Clone { } } - fn parse_node( - &mut self, - parent: Option<&'a RawNode>, - ) -> Result, FdtError> + fn parse_node(&mut self, parent: Option<&'a RawNode>) -> Result, FdtError> where Self: ParserWithMode<'a>, { @@ -370,9 +358,7 @@ pub trait Parser<'a>: crate::sealed::Sealed + Clone { match self.advance_token()? { BigEndianToken::END_NODE => Ok(Node { this: RawNode::new( - starting_data - .get(..starting_len - ending_len) - .ok_or(ParseError::UnexpectedEndOfData)?, + starting_data.get(..starting_len - ending_len).ok_or(ParseError::UnexpectedEndOfData)?, ), parent, strings: self.strings(), @@ -387,10 +373,10 @@ pub trait Parser<'a>: crate::sealed::Sealed + Clone { match self.advance_token()? { BigEndianToken::PROP => { // Properties are in the format: - let len = usize::try_from(self.advance_u32()?.to_ne()) - .map_err(|_| ParseError::NumericConversionError)?; - let name_offset = usize::try_from(self.advance_u32()?.to_ne()) - .map_err(|_| ParseError::NumericConversionError)?; + let len = + usize::try_from(self.advance_u32()?.to_ne()).map_err(|_| ParseError::NumericConversionError)?; + let name_offset = + usize::try_from(self.advance_u32()?.to_ne()).map_err(|_| ParseError::NumericConversionError)?; let data = self.byte_data().get(..len).ok_or(ParseError::UnexpectedEndOfData)?; self.advance_aligned(data.len()); @@ -408,12 +394,10 @@ pub struct StringsBlock<'a>(pub(crate) &'a [u8]); impl<'a> StringsBlock<'a> { pub fn offset_at(self, offset: usize) -> Result<&'a str, FdtError> { - core::ffi::CStr::from_bytes_until_nul( - self.0.get(offset..).ok_or(ParseError::UnexpectedEndOfData)?, - ) - .map_err(|_| ParseError::InvalidCStrValue)? - .to_str() - .map_err(|_| ParseError::InvalidCStrValue.into()) + core::ffi::CStr::from_bytes_until_nul(self.0.get(offset..).ok_or(ParseError::UnexpectedEndOfData)?) + .map_err(|_| ParseError::InvalidCStrValue)? + .to_str() + .map_err(|_| ParseError::InvalidCStrValue.into()) } } diff --git a/src/parsing/aligned.rs b/src/parsing/aligned.rs index 3a7a217..192daa4 100644 --- a/src/parsing/aligned.rs +++ b/src/parsing/aligned.rs @@ -36,12 +36,7 @@ impl<'a> Parser<'a> for AlignedParser<'a> { fn byte_data(&self) -> &'a [u8] { // SAFETY: it is always valid to cast a `u32` to 4 `u8`s - unsafe { - core::slice::from_raw_parts( - self.stream.0.as_ptr().cast::(), - self.stream.0.len() * 4, - ) - } + unsafe { core::slice::from_raw_parts(self.stream.0.as_ptr().cast::(), self.stream.0.len() * 4) } } fn strings(&self) -> super::StringsBlock<'a> { @@ -54,9 +49,7 @@ impl<'a> Parser<'a> for AlignedParser<'a> { fn advance_token(&mut self) -> Result { loop { - match BigEndianToken( - self.stream.advance().map(BigEndianU32).ok_or(ParseError::UnexpectedEndOfData)?, - ) { + match BigEndianToken(self.stream.advance().map(BigEndianU32).ok_or(ParseError::UnexpectedEndOfData)?) { BigEndianToken::NOP => continue, token @ BigEndianToken::BEGIN_NODE | token @ BigEndianToken::END_NODE @@ -68,22 +61,14 @@ impl<'a> Parser<'a> for AlignedParser<'a> { } fn advance_u32(&mut self) -> Result { - self.stream - .advance() - .map(BigEndianU32) - .ok_or(FdtError::ParseError(ParseError::UnexpectedEndOfData)) + self.stream.advance().map(BigEndianU32).ok_or(FdtError::ParseError(ParseError::UnexpectedEndOfData)) } fn advance_cstr(&mut self) -> Result<&'a core::ffi::CStr, FdtError> { // SAFETY: It is safe to reinterpret the stream data to a smaller integer size - let bytes = unsafe { - core::slice::from_raw_parts( - self.stream.0.as_ptr().cast::(), - self.stream.0.len() * 4, - ) - }; - let cstr = core::ffi::CStr::from_bytes_until_nul(bytes) - .map_err(|_| ParseError::InvalidCStrValue)?; + let bytes = + unsafe { core::slice::from_raw_parts(self.stream.0.as_ptr().cast::(), self.stream.0.len() * 4) }; + let cstr = core::ffi::CStr::from_bytes_until_nul(bytes).map_err(|_| ParseError::InvalidCStrValue)?; // Round up to the next multiple of 4, if necessary let skip = ((cstr.to_bytes_with_nul().len() + 3) & !3) / 4; diff --git a/src/parsing/unaligned.rs b/src/parsing/unaligned.rs index 18e0b20..837268b 100644 --- a/src/parsing/unaligned.rs +++ b/src/parsing/unaligned.rs @@ -72,8 +72,7 @@ impl<'a> Parser<'a> for UnalignedParser<'a> { } fn advance_cstr(&mut self) -> Result<&'a core::ffi::CStr, FdtError> { - let cstr = core::ffi::CStr::from_bytes_until_nul(self.stream.0) - .map_err(|_| ParseError::InvalidCStrValue)?; + let cstr = core::ffi::CStr::from_bytes_until_nul(self.stream.0).map_err(|_| ParseError::InvalidCStrValue)?; // Round up to the next multiple of 4, if necessary let skip = (cstr.to_bytes_with_nul().len() + 3) & !3; diff --git a/src/pretty_print.rs b/src/pretty_print.rs index ba5324b..8c961be 100644 --- a/src/pretty_print.rs +++ b/src/pretty_print.rs @@ -83,11 +83,7 @@ pub fn print_fdt<'a, P: Parser<'a>>( f, "{:width$}{} {{", ' ', - if node.name()?.name.is_empty() { - NodeName { name: "/", unit_address: None } - } else { - node.name()? - }, + if node.name()?.name.is_empty() { NodeName { name: "/", unit_address: None } } else { node.name()? }, width = depth * 4, )?; @@ -152,36 +148,17 @@ fn print_properties<'a, P: Parser<'a>>( } writeln!(f, ">;")?; } - "compatible" => writeln!( - f, - "{:width$}compatible = {:?};", - ' ', - prop.as_value::<&str>()?, - width = depth * 4 + 4 - )?, + "compatible" => { + writeln!(f, "{:width$}compatible = {:?};", ' ', prop.as_value::<&str>()?, width = depth * 4 + 4)? + } name if name.contains("-cells") => { - writeln!( - f, - "{:width$}{} = <{:#04x}>;", - ' ', - name, - prop.as_value::()?, - width = depth * 4 + 4 - )?; + writeln!(f, "{:width$}{} = <{:#04x}>;", ' ', name, prop.as_value::()?, width = depth * 4 + 4)?; } _ => match prop.as_value::<&str>() { Ok(value) - if (!value.is_empty() && value.chars().all(|c| c.is_ascii_graphic())) - || prop.value() == [0] => + if (!value.is_empty() && value.chars().all(|c| c.is_ascii_graphic())) || prop.value() == [0] => { - writeln!( - f, - "{:width$}{} = {:?};", - ' ', - prop.name(), - value, - width = depth * 4 + 4 - )? + writeln!(f, "{:width$}{} = {:?};", ' ', prop.name(), value, width = depth * 4 + 4)? } _ => match prop.value().len() { 0 => writeln!(f, "{:width$}{};", ' ', prop.name(), width = depth * 4 + 4)?, diff --git a/src/properties.rs b/src/properties.rs index 1301b00..38c203f 100644 --- a/src/properties.rs +++ b/src/properties.rs @@ -7,8 +7,8 @@ use core::ffi::CStr; use crate::{ nodes::Node, parsing::{ - aligned::AlignedParser, unaligned::UnalignedParser, BigEndianU32, NoPanic, Panic, Parser, - ParserWithMode, StringsBlock, StructsBlock, + aligned::AlignedParser, unaligned::UnalignedParser, BigEndianU32, NoPanic, Panic, Parser, ParserWithMode, + StringsBlock, StructsBlock, }, standard_nodes::Root, FdtError, @@ -93,13 +93,8 @@ impl Default for BuildWrappingIntCollector { } } -impl< - Int: Copy - + Default - + core::ops::Shl - + core::ops::BitOr - + From, - > BuildCellCollector for BuildWrappingIntCollector +impl + core::ops::BitOr + From> + BuildCellCollector for BuildWrappingIntCollector { type Output = Int; @@ -230,13 +225,8 @@ impl BuildCellCollector for BuildOptionalCellCollector { } } -impl< - Int: Copy - + Default - + core::ops::Shl - + core::ops::BitOr - + From, - > CellCollector for core::num::Wrapping +impl + core::ops::BitOr + From> + CellCollector for core::num::Wrapping { type Output = Int; type Builder = BuildWrappingIntCollector; @@ -358,11 +348,7 @@ impl PciAddressHighBits { impl core::ops::BitAnd for PciAddress { type Output = Self; fn bitand(self, rhs: Self) -> Self::Output { - Self { - hi: PciAddressHighBits(self.hi.0 & rhs.hi.0), - mid: self.mid & rhs.mid, - lo: self.lo & rhs.lo, - } + Self { hi: PciAddressHighBits(self.hi.0 & rhs.hi.0), mid: self.mid & rhs.mid, lo: self.lo & rhs.lo } } } @@ -722,8 +708,7 @@ impl<'a, P: ParserWithMode<'a>> Property<'a, P> for CellSizes { for property in node.properties()? { let property = property?; - let mut parser = - UnalignedParser::new(property.value(), StringsBlock(&[]), StructsBlock(&[])); + let mut parser = UnalignedParser::new(property.value(), StringsBlock(&[]), StructsBlock(&[])); match property.name() { "#address-cells" => address_cells = Some(parser.advance_u32()?.to_ne() as usize), "#size-cells" => size_cells = Some(parser.advance_u32()?.to_ne() as usize), @@ -731,9 +716,7 @@ impl<'a, P: ParserWithMode<'a>> Property<'a, P> for CellSizes { } } - Ok(address_cells - .zip(size_cells) - .map(|(address_cells, size_cells)| CellSizes { address_cells, size_cells })) + Ok(address_cells.zip(size_cells).map(|(address_cells, size_cells)| CellSizes { address_cells, size_cells })) } } @@ -834,9 +817,7 @@ impl<'a, CAddr: CellCollector, Len: CellCollector> Iterator for RegIter<'a, CAdd // // These unwraps can't panic because `chunks_exact` guarantees that // we'll always get slices of 4 bytes - if let Err(e) = - address_collector.push(u32::from_be_bytes(encoded_address.try_into().unwrap())) - { + if let Err(e) = address_collector.push(u32::from_be_bytes(encoded_address.try_into().unwrap())) { return Some(Err(e)); } } @@ -847,17 +828,13 @@ impl<'a, CAddr: CellCollector, Len: CellCollector> Iterator for RegIter<'a, CAdd // // These unwraps can't panic because `chunks_exact` guarantees that // we'll always get slices of 4 bytes - if let Err(e) = len_collector.push(u32::from_be_bytes(encoded_len.try_into().unwrap())) - { + if let Err(e) = len_collector.push(u32::from_be_bytes(encoded_len.try_into().unwrap())) { return Some(Err(e)); } } self.encoded_array = self.encoded_array.get((address_bytes + size_bytes)..)?; - Some(Ok(RegEntry { - address: CAddr::map(address_collector.finish()), - len: Len::map(len_collector.finish()), - })) + Some(Ok(RegEntry { address: CAddr::map(address_collector.finish()), len: Len::map(len_collector.finish()) })) } } @@ -946,12 +923,7 @@ impl<'a> Ranges<'a> { } } -pub struct RangesIter< - 'a, - CAddr: CellCollector = u64, - PAddr: CellCollector = u64, - Len: CellCollector = u64, -> { +pub struct RangesIter<'a, CAddr: CellCollector = u64, PAddr: CellCollector = u64, Len: CellCollector = u64> { parent_address_cells: AddressCells, cell_sizes: CellSizes, ranges: &'a [u8], @@ -970,10 +942,9 @@ impl<'a, CAddr: CellCollector, PAddr: CellCollector, Len: CellCollector> Iterato let child_encoded_address = self.ranges.get(..child_address_bytes)?; let parent_encoded_address = self.ranges.get(child_address_bytes..child_address_bytes + parent_address_bytes)?; - let encoded_len = self.ranges.get( - child_address_bytes + parent_address_bytes - ..child_address_bytes + parent_address_bytes + len_bytes, - )?; + let encoded_len = self + .ranges + .get(child_address_bytes + parent_address_bytes..child_address_bytes + parent_address_bytes + len_bytes)?; let mut child_address_collector = ::Builder::default(); for encoded_address in child_encoded_address.chunks_exact(4) { @@ -981,9 +952,7 @@ impl<'a, CAddr: CellCollector, PAddr: CellCollector, Len: CellCollector> Iterato // // These unwraps can't panic because `chunks_exact` guarantees that // we'll always get slices of 4 bytes - if let Err(e) = child_address_collector - .push(u32::from_be_bytes(encoded_address.try_into().unwrap())) - { + if let Err(e) = child_address_collector.push(u32::from_be_bytes(encoded_address.try_into().unwrap())) { return Some(Err(e)); } } @@ -994,9 +963,7 @@ impl<'a, CAddr: CellCollector, PAddr: CellCollector, Len: CellCollector> Iterato // // These unwraps can't panic because `chunks_exact` guarantees that // we'll always get slices of 4 bytes - if let Err(e) = parent_address_collector - .push(u32::from_be_bytes(encoded_address.try_into().unwrap())) - { + if let Err(e) = parent_address_collector.push(u32::from_be_bytes(encoded_address.try_into().unwrap())) { return Some(Err(e)); } } @@ -1007,8 +974,7 @@ impl<'a, CAddr: CellCollector, PAddr: CellCollector, Len: CellCollector> Iterato // // These unwraps can't panic because `chunks_exact` guarantees that // we'll always get slices of 4 bytes - if let Err(e) = len_collector.push(u32::from_be_bytes(encoded_len.try_into().unwrap())) - { + if let Err(e) = len_collector.push(u32::from_be_bytes(encoded_len.try_into().unwrap())) { return Some(Err(e)); } } @@ -1110,11 +1076,10 @@ impl<'a, P: ParserWithMode<'a>> Property<'a, P> for LegacyInterrupts<'a, P> { ) -> Result, FdtError> { match node.properties()?.find("interrupts")? { Some(interrupts) => { - let interrupt_parent = - match InterruptParent::<(P::Parser, NoPanic)>::parse(node, root)? { - Some(p) => p, - None => return Err(FdtError::MissingRequiredProperty("interrupt-parent")), - }; + let interrupt_parent = match InterruptParent::<(P::Parser, NoPanic)>::parse(node, root)? { + Some(p) => p, + None => return Err(FdtError::MissingRequiredProperty("interrupt-parent")), + }; let Some(interrupt_cells) = interrupt_parent.property::()? else { return Err(FdtError::MissingRequiredProperty("interrupt-cells")); @@ -1159,9 +1124,7 @@ impl<'a, I: CellCollector> Iterator for LegacyInterruptsIter<'a, I> { // // These unwraps can't panic because `chunks_exact` guarantees that // we'll always get slices of 4 bytes - if let Err(e) = - specifier_collector.push(u32::from_be_bytes(encoded_specifier.try_into().unwrap())) - { + if let Err(e) = specifier_collector.push(u32::from_be_bytes(encoded_specifier.try_into().unwrap())) { return Some(Err(e)); } } @@ -1205,10 +1168,9 @@ impl<'a, P: ParserWithMode<'a>> Property<'a, P> for ExtendedInterrupts<'a, P> { root: Root<'a, (P::Parser, NoPanic)>, ) -> Result, FdtError> { match node.properties()?.find("interrupts-extended")? { - Some(interrupts) => Ok(Some(Self { - encoded_array: interrupts.value(), - root: Root { node: root.node.alt() }, - })), + Some(interrupts) => { + Ok(Some(Self { encoded_array: interrupts.value(), root: Root { node: root.node.alt() } })) + } None => Ok(None), } @@ -1225,9 +1187,10 @@ impl<'a, P: ParserWithMode<'a>> Iterator for ExtendedInterruptsIter<'a, P> { #[track_caller] fn next(&mut self) -> Option { - let phandle = self.encoded_array.get(..4).map(|bytes| { - PHandle(BigEndianU32::from_be(u32::from_ne_bytes(bytes.try_into().unwrap()))) - })?; + let phandle = self + .encoded_array + .get(..4) + .map(|bytes| PHandle(BigEndianU32::from_be(u32::from_ne_bytes(bytes.try_into().unwrap()))))?; self.encoded_array = self.encoded_array.get(4..)?; let res = crate::tryblock!({ @@ -1285,10 +1248,7 @@ impl<'a, P: ParserWithMode<'a>> ExtendedInterrupt<'a, P> { } pub fn interrupt_specifier(self) -> InterruptSpecifier<'a> { - InterruptSpecifier { - interrupt_cells: self.interrupt_cells, - encoded_array: self.encoded_array, - } + InterruptSpecifier { interrupt_cells: self.interrupt_cells, encoded_array: self.encoded_array } } } @@ -1374,10 +1334,7 @@ impl<'a> Iterator for InterruptSpecifierIterPairs<'a> { // This panic can never fail since the slice length is guaranteed to be // 4 bytes long - Some(( - u32::from_be_bytes(next[..4].try_into().unwrap()), - u32::from_be_bytes(next[4..8].try_into().unwrap()), - )) + Some((u32::from_be_bytes(next[..4].try_into().unwrap()), u32::from_be_bytes(next[4..8].try_into().unwrap()))) } } @@ -1466,14 +1423,10 @@ impl InterruptMapMask::Output, ) -> (::Output, ::Output) where - ::Output: core::ops::BitAnd< - ::Output, - Output = ::Output, - >, - ::Output: core::ops::BitAnd< - ::Output, - Output = ::Output, - >, + ::Output: + core::ops::BitAnd<::Output, Output = ::Output>, + ::Output: + core::ops::BitAnd<::Output, Output = ::Output>, { (self.address_mask & address, self.interrupt_specifier_mask & interrupt_specifier) } @@ -1486,12 +1439,10 @@ impl<'a, AddrMask: CellCollector, IntMask: CellCollector, P: ParserWithMode<'a>> node: Node<'a, (P::Parser, NoPanic)>, _: Root<'a, (P::Parser, NoPanic)>, ) -> Result, FdtError> { - let address_cells = node - .property::()? - .ok_or(FdtError::MissingRequiredProperty("#address-cells"))?; - let interrupt_cells = node - .property::()? - .ok_or(FdtError::MissingRequiredProperty("#interrupt-cells"))?; + let address_cells = + node.property::()?.ok_or(FdtError::MissingRequiredProperty("#address-cells"))?; + let interrupt_cells = + node.property::()?.ok_or(FdtError::MissingRequiredProperty("#interrupt-cells"))?; match node.properties()?.find("interrupt-map-mask")? { Some(prop) => { if prop.value().len() % 4 != 0 { @@ -1601,8 +1552,7 @@ impl< .find(|e| match e { Err(_) => true, Ok(entry) => { - entry.child_unit_address == address - && entry.child_interrupt_specifier == interrupt_specifier + entry.child_unit_address == address && entry.child_interrupt_specifier == interrupt_specifier } }) .transpose() @@ -1634,12 +1584,10 @@ impl< ) -> Result, FdtError> { let Some(encoded_map) = node.properties()?.find("interrupt-map")? else { return Ok(None) }; - let address_cells = node - .property::()? - .ok_or(FdtError::MissingRequiredProperty("#address-cells"))?; - let interrupt_cells = node - .property::()? - .ok_or(FdtError::MissingRequiredProperty("#interrupt-cells"))?; + let address_cells = + node.property::()?.ok_or(FdtError::MissingRequiredProperty("#address-cells"))?; + let interrupt_cells = + node.property::()?.ok_or(FdtError::MissingRequiredProperty("#interrupt-cells"))?; Ok(Some(InterruptMap { address_cells, @@ -1683,18 +1631,13 @@ impl< let child_addr_size = self.address_cells.0 * 4; let child_intsp_size = self.interrupt_cells.0 * 4; - let Some(child_address_iter) = self.encoded_map.get(..child_addr_size) else { + let Some((child_address_iter, rest)) = self.encoded_map.split_at_checked(child_addr_size) else { return Ok(None); }; - let Some(child_specifier_iter) = - self.encoded_map.get(child_addr_size..child_addr_size + child_intsp_size) - else { + let Some((child_specifier_iter, rest)) = rest.split_at_checked(child_intsp_size) else { return Ok(None); }; - let Some(interrupt_parent) = self - .encoded_map - .get(child_addr_size + child_intsp_size..child_addr_size + child_intsp_size + 4) - else { + let Some((interrupt_parent, rest)) = rest.split_at_checked(4) else { return Ok(None); }; @@ -1706,33 +1649,22 @@ impl< let parent_address_cells = interrupt_parent .property::()? - .ok_or(FdtError::MissingRequiredProperty("#address-cells/#size-cells"))?; + .ok_or(FdtError::MissingRequiredProperty("#address-cells"))?; let parent_interrupt_cells = interrupt_parent .property::()? .ok_or(FdtError::MissingRequiredProperty("#interrupt-cells"))?; let parent_addr_size = parent_address_cells.0 * 4; let parent_intsp_size = parent_interrupt_cells.0 * 4; - let Some(parent_address_iter) = self.encoded_map.get( - child_addr_size + child_intsp_size + 4 - ..child_addr_size + child_intsp_size + 4 + parent_addr_size, - ) else { + + let Some((parent_address_iter, rest)) = rest.split_at_checked(parent_addr_size) else { return Ok(None); }; - let Some(mut parent_specifier_iter) = - self.encoded_map.get(child_addr_size + child_intsp_size + 4 + parent_addr_size..) - else { + let Some((parent_specifier_iter, rest)) = rest.split_at_checked(parent_intsp_size) else { return Ok(None); }; - self.encoded_map = match parent_specifier_iter.get(parent_intsp_size..) { - Some(s) => s, - None => return Ok(None), - }; - parent_specifier_iter = match parent_specifier_iter.get(..parent_intsp_size) { - Some(s) => s, - None => return Ok(None), - }; + self.encoded_map = rest; let mut child_address_collector = CAddr::Builder::default(); for chunk in child_address_iter.chunks_exact(4) { @@ -1901,9 +1833,7 @@ impl<'a> PropertyValue<'a> for &'a CStr { impl<'a> PropertyValue<'a> for &'a str { #[inline] fn parse(value: &'a [u8]) -> Result { - core::str::from_utf8(value) - .map(|s| s.trim_end_matches('\0')) - .map_err(|_| InvalidPropertyValue) + core::str::from_utf8(value).map(|s| s.trim_end_matches('\0')).map_err(|_| InvalidPropertyValue) } } @@ -1966,17 +1896,12 @@ mod tests { fn reg_raw_iter() { let mut iter = RegRawIter { cell_sizes: CellSizes { address_cells: 2, size_cells: 1 }, - encoded_array: &[ - 0x55, 0x44, 0x33, 0x22, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, - ], + encoded_array: &[0x55, 0x44, 0x33, 0x22, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD], }; assert_eq!( iter.next().unwrap(), - RawRegEntry { - address: &[0x55, 0x44, 0x33, 0x22, 0x66, 0x77, 0x88, 0x99], - len: &[0xAA, 0xBB, 0xCC, 0xDD] - } + RawRegEntry { address: &[0x55, 0x44, 0x33, 0x22, 0x66, 0x77, 0x88, 0x99], len: &[0xAA, 0xBB, 0xCC, 0xDD] } ); } @@ -1984,15 +1909,10 @@ mod tests { fn reg_u64_iter() { let mut iter = RegIter:: { cell_sizes: CellSizes { address_cells: 2, size_cells: 1 }, - encoded_array: &[ - 0x55, 0x44, 0x33, 0x22, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, - ], + encoded_array: &[0x55, 0x44, 0x33, 0x22, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD], _collector: core::marker::PhantomData, }; - assert_eq!( - iter.next().unwrap().unwrap(), - RegEntry { address: 0x5544332266778899, len: 0xAABBCCDD } - ); + assert_eq!(iter.next().unwrap().unwrap(), RegEntry { address: 0x5544332266778899, len: 0xAABBCCDD }); } } diff --git a/src/standard_nodes.rs b/src/standard_nodes.rs index f0ba486..5bcf273 100644 --- a/src/standard_nodes.rs +++ b/src/standard_nodes.rs @@ -4,12 +4,9 @@ use crate::{ nodes::{FallibleNode, IntoSearchableNodeName, Node, RawNode, SearchableNodeName}, - parsing::{ - aligned::AlignedParser, BigEndianToken, NoPanic, Panic, ParseError, Parser, ParserWithMode, - }, + parsing::{aligned::AlignedParser, BigEndianToken, NoPanic, Panic, ParseError, Parser, ParserWithMode}, properties::{ - AddressCells, BuildCellCollector, CellCollector, CellSizes, CollectCellsError, Compatible, - PHandle, Property, + AddressCells, BuildCellCollector, CellCollector, CellSizes, CollectCellsError, Compatible, PHandle, Property, }, tryblock, FdtError, }; @@ -54,9 +51,8 @@ impl<'a, P: ParserWithMode<'a>> Chosen<'a, P> { Ok(property) => match property.name() == "stdout-path" { false => None, true => Some(property.as_value::<&'a str>().map_err(Into::into).map(|s| { - let (path, params) = s - .split_once(':') - .map_or_else(|| (s, None), |(name, params)| (name, Some(params))); + let (path, params) = + s.split_once(':').map_or_else(|| (s, None), |(name, params)| (name, Some(params))); StdInOutPath { path, params } })), }, @@ -81,9 +77,8 @@ impl<'a, P: ParserWithMode<'a>> Chosen<'a, P> { Ok(property) => match property.name() == "stdin-path" { false => None, true => Some(property.as_value::<&str>().map_err(Into::into).map(|s| { - let (path, params) = s - .split_once(':') - .map_or_else(|| (s, None), |(name, params)| (name, Some(params))); + let (path, params) = + s.split_once(':').map_or_else(|| (s, None), |(name, params)| (name, Some(params))); StdInOutPath { path, params } })), }, @@ -177,9 +172,7 @@ impl<'a, P: ParserWithMode<'a>> Root<'a, P> { P::to_output(crate::tryblock!({ let node = self.node.fallible(); node.properties()?.find("model").and_then(|p| { - p.ok_or(FdtError::MissingRequiredProperty("model"))? - .as_value::<&'a str>() - .map_err(Into::into) + p.ok_or(FdtError::MissingRequiredProperty("model"))?.as_value::<&'a str>().map_err(Into::into) }) })) } @@ -261,10 +254,7 @@ impl<'a, P: ParserWithMode<'a>> Root<'a, P> { /// Returns an iterator that yields every node with the name that matches /// `name` in depth-first order - pub fn find_all_nodes_with_name<'b>( - self, - name: &'b str, - ) -> P::Output> { + pub fn find_all_nodes_with_name<'b>(self, name: &'b str) -> P::Output> { P::to_output(crate::tryblock!({ let this = Root { node: self.node.fallible() }; Ok(AllNodesWithNameIter { iter: this.all_nodes()?, name }) @@ -340,13 +330,12 @@ impl<'a, P: ParserWithMode<'a>> Root<'a, P> { pub fn all_compatible<'b>(self, with: &'b [&str]) -> P::Output> { P::to_output(crate::tryblock!({ let this = Root { node: self.node.fallible() }; - let f: fn(_) -> _ = - |node: Result<(usize, Node<'a, (P::Parser, NoPanic)>), FdtError>| match node - .and_then(|(_, n)| Ok((n, n.property::()?))) - { - Ok((n, compatible)) => Some(Ok((n, compatible?))), - Err(e) => Some(Err(e)), - }; + let f: fn(_) -> _ = |node: Result<(usize, FallibleNode<'a, P>), FdtError>| match node + .and_then(|(_, n)| Ok((n, n.property::()?))) + { + Ok((n, compatible)) => Some(Ok((n, compatible?))), + Err(e) => Some(Err(e)), + }; let iter = this.all_nodes()?.filter_map(f); @@ -421,7 +410,7 @@ impl<'a, 'b, P: ParserWithMode<'a>> Iterator for AllNodesWithNameIter<'a, 'b, P> #[track_caller] fn next(&mut self) -> Option { - while let Some(next) = self.iter.next() { + for next in self.iter.by_ref() { match next.and_then(|(_, n)| Ok((n, n.name()?))) { Ok((node, name)) => match name.name == self.name { true => return Some(P::to_output(Ok(node.alt()))), @@ -440,8 +429,8 @@ pub struct AllCompatibleIter<'a, 'b, P: ParserWithMode<'a>> { iter: core::iter::FilterMap< AllNodesIter<'a, (P::Parser, NoPanic)>, fn( - Result<(usize, Node<'a, (P::Parser, NoPanic)>), FdtError>, - ) -> Option, Compatible<'a>), FdtError>>, + Result<(usize, FallibleNode<'a, P>), FdtError>, + ) -> Option, Compatible<'a>), FdtError>>, >, with: &'b [&'b str], } @@ -453,12 +442,10 @@ impl<'a, 'b, P: ParserWithMode<'a>> Iterator for AllCompatibleIter<'a, 'b, P> { fn next(&mut self) -> Option { for next in self.iter.by_ref() { match next { - Ok((node, compatible)) => { - match self.with.iter().copied().any(|c| compatible.compatible_with(c)) { - true => return Some(P::to_output(Ok(node.alt()))), - false => continue, - } - } + Ok((node, compatible)) => match self.with.iter().copied().any(|c| compatible.compatible_with(c)) { + true => return Some(P::to_output(Ok(node.alt()))), + false => continue, + }, Err(e) => return Some(P::to_output(Err(e))), } } @@ -485,11 +472,8 @@ impl<'a, P: ParserWithMode<'a>> Iterator for AllNodesIter<'a, P> { match self.parser.advance_token() { Ok(BigEndianToken::BEGIN_NODE) => self.parent_index += 1, - Ok(BigEndianToken::END) - | Err(FdtError::ParseError(ParseError::UnexpectedEndOfData)) => return None, - Ok(_) => { - return Some(P::to_output(Err(FdtError::ParseError(ParseError::UnexpectedToken)))) - } + Ok(BigEndianToken::END) | Err(FdtError::ParseError(ParseError::UnexpectedEndOfData)) => return None, + Ok(_) => return Some(P::to_output(Err(FdtError::ParseError(ParseError::UnexpectedToken)))), Err(e) => return Some(P::to_output(Err(e))), } @@ -505,10 +489,7 @@ impl<'a, P: ParserWithMode<'a>> Iterator for AllNodesIter<'a, P> { self.parent_index, Node { this: RawNode::new(starting_data), - parent: self - .parents - .get(self.parent_index.saturating_sub(1)) - .map(|parent| RawNode::new(parent)), + parent: self.parents.get(self.parent_index.saturating_sub(1)).map(|parent| RawNode::new(parent)), strings: self.parser.strings(), structs: self.parser.structs(), _mode: core::marker::PhantomData, @@ -543,19 +524,14 @@ impl<'a, P: ParserWithMode<'a>> Aliases<'a, P> { /// Attempt to resolve an alias to a node name pub fn resolve_name(self, alias: &str) -> P::Output> { P::to_output(crate::tryblock!({ - self.node - .properties()? - .find(alias)? - .map(|p| p.as_value().map_err(Into::into)) - .transpose() + self.node.properties()?.find(alias)?.map(|p| p.as_value().map_err(Into::into)).transpose() })) } /// Attempt to find the node specified by the given alias pub fn resolve(self, alias: &str) -> P::Output>> { P::to_output(crate::tryblock!({ - let Some(path) = Aliases::<(_, NoPanic)> { node: self.node }.resolve_name(alias)? - else { + let Some(path) = Aliases::<(_, NoPanic)> { node: self.node }.resolve_name(alias)? else { return Ok(None); }; @@ -608,16 +584,11 @@ impl<'a, P: ParserWithMode<'a>> Cpu<'a, P> { return Err(FdtError::InvalidPropertyValue); } - let Some(address_cells) = self.node.parent().unwrap().property::()? - else { + let Some(address_cells) = self.node.parent().unwrap().property::()? else { return Err(FdtError::MissingRequiredProperty("#address-cells")); }; - Ok(CpuIds { - reg: reg.value(), - address_cells: address_cells.0, - _collector: core::marker::PhantomData, - }) + Ok(CpuIds { reg: reg.value(), address_cells: address_cells.0, _collector: core::marker::PhantomData }) })) } @@ -711,11 +682,7 @@ impl<'a, C: CellCollector> CpuIds<'a, C> { } pub fn iter(&self) -> CpuIdsIter<'a, C> { - CpuIdsIter { - reg: self.reg, - address_cells: self.address_cells, - _collector: core::marker::PhantomData, - } + CpuIdsIter { reg: self.reg, address_cells: self.address_cells, _collector: core::marker::PhantomData } } } @@ -744,11 +711,7 @@ pub struct CpuIdsIter<'a, C: CellCollector> { impl Clone for CpuIdsIter<'_, C> { fn clone(&self) -> Self { - Self { - address_cells: self.address_cells, - reg: self.reg, - _collector: core::marker::PhantomData, - } + Self { address_cells: self.address_cells, reg: self.reg, _collector: core::marker::PhantomData } } } diff --git a/src/tests.rs b/src/tests.rs index fe514e0..9fa23c7 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -42,8 +42,7 @@ impl Align4 { } } -static TEST: Align4<3764> = - Align4::new(AlignArrayUp(*include_bytes!("../dtb/test.dtb")).align_up::<3764>()); +static TEST: Align4<3764> = Align4::new(AlignArrayUp(*include_bytes!("../dtb/test.dtb")).align_up::<3764>()); static ISSUE_3: Align4<4658> = Align4::new(*include_bytes!("../dtb/issue-3.dtb")); static SIFIVE: Align4<3872> = Align4::new(*include_bytes!("../dtb/sifive.dtb")); @@ -113,17 +112,8 @@ fn finds_root_node_properties() { // fallible let fdt = Fdt::new_fallible(TEST.as_slice()).unwrap(); - let prop = fdt - .root() - .unwrap() - .find_node("/") - .unwrap() - .unwrap() - .properties() - .unwrap() - .find("compatible") - .unwrap() - .unwrap(); + let prop = + fdt.root().unwrap().find_node("/").unwrap().unwrap().properties().unwrap().find("compatible").unwrap().unwrap(); assert_eq!(prop.value(), b"riscv-virtio\0"); } @@ -144,10 +134,7 @@ fn finds_child_of_root_node() { "couldn't find interrupt-controller node" ); - assert!( - root.find_node("/cpus/cpu@1/interrupt-controller").is_none(), - "couldn't find interrupt-controller node" - ); + assert!(root.find_node("/cpus/cpu@1/interrupt-controller").is_none(), "couldn't find interrupt-controller node"); } #[test] @@ -167,8 +154,7 @@ fn properties() { let fdt = Fdt::new(TEST.as_slice()).unwrap(); let test = fdt.root().find_node("/soc/test").unwrap(); - let props = - test.properties().into_iter().map(|p| (p.name(), p.value())).collect::>(); + let props = test.properties().into_iter().map(|p| (p.name(), p.value())).collect::>(); assert_eq!( props, From 4258ebb3b42024d866eee1b7d1725afb4d1a4fc4 Mon Sep 17 00:00:00 2001 From: repnop Date: Tue, 30 Jul 2024 18:09:53 -0400 Subject: [PATCH 22/38] organize some things into modules --- src/cell_collector.rs | 224 ++++ src/lib.rs | 13 +- src/nodes.rs | 8 +- src/pretty_print.rs | 3 +- src/properties.rs | 1656 +----------------------------- src/properties/cells.rs | 97 ++ src/properties/interrupts.rs | 732 +++++++++++++ src/properties/interrupts/pci.rs | 151 +++ src/properties/ranges.rs | 97 ++ src/properties/reg.rs | 191 ++++ src/properties/values.rs | 131 +++ src/standard_nodes.rs | 6 +- src/tests.rs | 8 +- 13 files changed, 1667 insertions(+), 1650 deletions(-) create mode 100644 src/cell_collector.rs create mode 100644 src/properties/cells.rs create mode 100644 src/properties/interrupts.rs create mode 100644 src/properties/interrupts/pci.rs create mode 100644 src/properties/ranges.rs create mode 100644 src/properties/reg.rs create mode 100644 src/properties/values.rs diff --git a/src/cell_collector.rs b/src/cell_collector.rs new file mode 100644 index 0000000..5613a60 --- /dev/null +++ b/src/cell_collector.rs @@ -0,0 +1,224 @@ +use crate::FdtError; + +#[derive(Debug, Clone, Copy)] +pub struct CollectCellsError; + +impl From for FdtError { + fn from(_: CollectCellsError) -> Self { + FdtError::CollectCellsError + } +} + +pub trait BuildCellCollector: Default { + type Output; + + fn push(&mut self, component: u32) -> Result<(), CollectCellsError>; + fn finish(self) -> Self::Output; +} + +pub trait CellCollector: Default + Sized { + type Output; + type Builder: BuildCellCollector; + + fn map(builder_out: ::Output) -> Self::Output; +} + +pub struct BuildIntCollector { + value: Int, +} + +impl Default for BuildIntCollector { + fn default() -> Self { + Self { value: Default::default() } + } +} + +impl< + Int: Copy + + Default + + core::cmp::PartialEq + + core::ops::Shl + + core::ops::Shr + + core::ops::BitOr + + From, + > BuildCellCollector for BuildIntCollector +{ + type Output = Int; + + #[inline(always)] + fn push(&mut self, component: u32) -> Result<(), CollectCellsError> { + let shr = const { + match core::mem::size_of::().checked_sub(4) { + Some(value) => value as u32 * 8, + None => panic!("integer type too small"), + } + }; + + if self.value >> shr != Int::from(0u32) { + return Err(CollectCellsError); + } + + self.value = self.value.shl(32).bitor(Int::from(component)); + + Ok(()) + } + + #[inline(always)] + fn finish(self) -> Self::Output { + self.value + } +} + +pub struct BuildWrappingIntCollector { + value: Int, +} + +impl Default for BuildWrappingIntCollector { + fn default() -> Self { + Self { value: Default::default() } + } +} + +impl + core::ops::BitOr + From> + BuildCellCollector for BuildWrappingIntCollector +{ + type Output = Int; + + #[inline(always)] + fn push(&mut self, component: u32) -> Result<(), CollectCellsError> { + self.value = self.value.shl(32).bitor(Int::from(component)); + + Ok(()) + } + + #[inline(always)] + fn finish(self) -> Self::Output { + self.value + } +} + +impl CellCollector for u32 { + type Output = Self; + type Builder = BuildIntCollector; + + #[inline(always)] + fn map(builder_out: as BuildCellCollector>::Output) -> Self::Output { + builder_out + } +} + +impl CellCollector for u64 { + type Output = Self; + type Builder = BuildIntCollector; + + #[inline(always)] + fn map(builder_out: as BuildCellCollector>::Output) -> Self::Output { + builder_out + } +} + +impl CellCollector for u128 { + type Output = Self; + type Builder = BuildIntCollector; + + #[inline(always)] + fn map(builder_out: as BuildCellCollector>::Output) -> Self::Output { + builder_out + } +} + +impl CellCollector for usize { + type Output = Self; + type Builder = UsizeCollector; + + #[inline(always)] + fn map(builder_out: ::Output) -> Self::Output { + builder_out + } +} + +impl CellCollector for Option { + type Builder = BuildOptionalCellCollector; + type Output = Option; + + fn map(builder_out: ::Output) -> Self::Output { + builder_out.map(T::map) + } +} + +#[derive(Default)] +pub struct UsizeCollector { + value: usize, +} + +impl BuildCellCollector for UsizeCollector { + type Output = usize; + + #[inline(always)] + fn push(&mut self, component: u32) -> Result<(), CollectCellsError> { + use core::ops::{BitOr, Shl}; + + let shr = const { + match core::mem::size_of::().checked_sub(4) { + Some(value) => value as u32 * 8, + None => panic!("integer type too small"), + } + }; + + if self.value >> shr != 0 { + return Err(CollectCellsError); + } + + self.value = self.value.shl(32i32).bitor(component as usize); + + Ok(()) + } + + #[inline(always)] + fn finish(self) -> Self::Output { + self.value + } +} + +pub struct BuildOptionalCellCollector { + builder: T::Builder, + used: bool, +} + +impl Default for BuildOptionalCellCollector { + fn default() -> Self { + Self { builder: Default::default(), used: false } + } +} + +impl BuildCellCollector for BuildOptionalCellCollector { + type Output = Option<::Output>; + + #[inline(always)] + fn push(&mut self, component: u32) -> Result<(), CollectCellsError> { + self.used = true; + self.builder.push(component)?; + + Ok(()) + } + + #[inline(always)] + fn finish(self) -> Self::Output { + match self.used { + true => Some(self.builder.finish()), + false => None, + } + } +} + +impl + core::ops::BitOr + From> + CellCollector for core::num::Wrapping +{ + type Output = Int; + type Builder = BuildWrappingIntCollector; + + #[inline(always)] + fn map(builder_out: ::Output) -> Self::Output { + builder_out + } +} diff --git a/src/lib.rs b/src/lib.rs index 2b9d51d..e41cf09 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -57,6 +57,7 @@ extern crate std; #[cfg(test)] mod tests; +pub mod cell_collector; mod nodes; mod parsing; mod pretty_print; @@ -191,9 +192,9 @@ impl<'a> Fdt<'a, (UnalignedParser<'a>, Panic)> { let mut parser = UnalignedParser::new(data, StringsBlock(&[]), StructsBlock(&[])); let header = parser.parse_header()?; - if data.len() < (header.strings_offset + header.strings_size) as usize { - return Err(FdtError::SliceTooSmall); - } else if data.len() < (header.structs_offset + header.structs_size) as usize { + let strings_end = (header.strings_offset + header.strings_size) as usize; + let structs_end = (header.structs_offset + header.structs_size) as usize; + if data.len() < strings_end || data.len() < structs_end { return Err(FdtError::SliceTooSmall); } @@ -233,9 +234,9 @@ impl<'a> Fdt<'a, (AlignedParser<'a>, Panic)> { let mut parser = AlignedParser::new(data, StringsBlock(&[]), StructsBlock(&[])); let header = parser.parse_header()?; - if data.len() < (header.strings_offset + header.strings_size) as usize / 4 { - return Err(FdtError::SliceTooSmall); - } else if data.len() < (header.structs_offset + header.structs_size) as usize / 4 { + let strings_end = (header.strings_offset + header.strings_size) as usize / 4; + let structs_end = (header.structs_offset + header.structs_size) as usize / 4; + if data.len() < strings_end || data.len() < structs_end { return Err(FdtError::SliceTooSmall); } diff --git a/src/nodes.rs b/src/nodes.rs index e65dcf4..5dbe409 100644 --- a/src/nodes.rs +++ b/src/nodes.rs @@ -3,7 +3,11 @@ use crate::{ aligned::AlignedParser, BigEndianToken, NoPanic, Panic, PanicMode, ParseError, Parser, ParserWithMode, StringsBlock, StructsBlock, }, - properties::{InvalidPropertyValue, Property, PropertyValue, Reg}, + properties::{ + reg::Reg, + values::{InvalidPropertyValue, PropertyValue}, + Property, + }, standard_nodes::Root, FdtError, }; @@ -69,7 +73,7 @@ impl core::fmt::Display for NodeName<'_> { } } -pub type FallibleNode<'a, P: ParserWithMode<'a>> = Node<'a, (P::Parser, NoPanic)>; +pub type FallibleNode<'a, P> = Node<'a, (

>::Parser, NoPanic)>; pub struct Node<'a, P: ParserWithMode<'a>> { pub(crate) this: &'a RawNode<

>::Granularity>, diff --git a/src/pretty_print.rs b/src/pretty_print.rs index 8c961be..8867886 100644 --- a/src/pretty_print.rs +++ b/src/pretty_print.rs @@ -3,9 +3,10 @@ // obtain one at https://mozilla.org/MPL/2.0/. use crate::{ + cell_collector::CollectCellsError, nodes::{Node, NodeName}, parsing::{NoPanic, Parser}, - properties::{CollectCellsError, InvalidPropertyValue, U32List}, + properties::values::{InvalidPropertyValue, U32List}, standard_nodes::Root, FdtError, }; diff --git a/src/properties.rs b/src/properties.rs index 38c203f..8273ed0 100644 --- a/src/properties.rs +++ b/src/properties.rs @@ -2,10 +2,14 @@ // v. 2.0. If a copy of the MPL was not distributed with this file, You can // obtain one at https://mozilla.org/MPL/2.0/. -use core::ffi::CStr; +pub mod cells; +pub mod interrupts; +pub mod ranges; +pub mod reg; +pub mod values; use crate::{ - nodes::Node, + nodes::{FallibleNode, Node}, parsing::{ aligned::AlignedParser, unaligned::UnalignedParser, BigEndianU32, NoPanic, Panic, Parser, ParserWithMode, StringsBlock, StructsBlock, @@ -13,385 +17,10 @@ use crate::{ standard_nodes::Root, FdtError, }; - -#[derive(Debug, Clone, Copy)] -pub struct CollectCellsError; - -impl From for FdtError { - fn from(_: CollectCellsError) -> Self { - FdtError::CollectCellsError - } -} - -pub trait BuildCellCollector: Default { - type Output; - - fn push(&mut self, component: u32) -> Result<(), CollectCellsError>; - fn finish(self) -> Self::Output; -} - -pub trait CellCollector: Default + Sized { - type Output; - type Builder: BuildCellCollector; - - fn map(builder_out: ::Output) -> Self::Output; -} - -pub struct BuildIntCollector { - value: Int, -} - -impl Default for BuildIntCollector { - fn default() -> Self { - Self { value: Default::default() } - } -} - -impl< - Int: Copy - + Default - + core::cmp::PartialEq - + core::ops::Shl - + core::ops::Shr - + core::ops::BitOr - + From, - > BuildCellCollector for BuildIntCollector -{ - type Output = Int; - - #[inline(always)] - fn push(&mut self, component: u32) -> Result<(), CollectCellsError> { - let shr = const { - match core::mem::size_of::().checked_sub(4) { - Some(value) => value as u32 * 8, - None => panic!("integer type too small"), - } - }; - - if self.value >> shr != Int::from(0u32) { - return Err(CollectCellsError); - } - - self.value = self.value.shl(32).bitor(Int::from(component)); - - Ok(()) - } - - #[inline(always)] - fn finish(self) -> Self::Output { - self.value - } -} - -pub struct BuildWrappingIntCollector { - value: Int, -} - -impl Default for BuildWrappingIntCollector { - fn default() -> Self { - Self { value: Default::default() } - } -} - -impl + core::ops::BitOr + From> - BuildCellCollector for BuildWrappingIntCollector -{ - type Output = Int; - - #[inline(always)] - fn push(&mut self, component: u32) -> Result<(), CollectCellsError> { - self.value = self.value.shl(32).bitor(Int::from(component)); - - Ok(()) - } - - #[inline(always)] - fn finish(self) -> Self::Output { - self.value - } -} - -impl CellCollector for u32 { - type Output = Self; - type Builder = BuildIntCollector; - - #[inline(always)] - fn map(builder_out: as BuildCellCollector>::Output) -> Self::Output { - builder_out - } -} - -impl CellCollector for u64 { - type Output = Self; - type Builder = BuildIntCollector; - - #[inline(always)] - fn map(builder_out: as BuildCellCollector>::Output) -> Self::Output { - builder_out - } -} - -impl CellCollector for u128 { - type Output = Self; - type Builder = BuildIntCollector; - - #[inline(always)] - fn map(builder_out: as BuildCellCollector>::Output) -> Self::Output { - builder_out - } -} - -impl CellCollector for usize { - type Output = Self; - type Builder = UsizeCollector; - - #[inline(always)] - fn map(builder_out: ::Output) -> Self::Output { - builder_out - } -} - -impl CellCollector for Option { - type Builder = BuildOptionalCellCollector; - type Output = Option; - - fn map(builder_out: ::Output) -> Self::Output { - builder_out.map(T::map) - } -} - -#[derive(Default)] -pub struct UsizeCollector { - value: usize, -} - -impl BuildCellCollector for UsizeCollector { - type Output = usize; - - #[inline(always)] - fn push(&mut self, component: u32) -> Result<(), CollectCellsError> { - use core::ops::{BitOr, Shl}; - - let shr = const { - match core::mem::size_of::().checked_sub(4) { - Some(value) => value as u32 * 8, - None => panic!("integer type too small"), - } - }; - - if self.value >> shr != 0 { - return Err(CollectCellsError); - } - - self.value = self.value.shl(32i32).bitor(component as usize); - - Ok(()) - } - - #[inline(always)] - fn finish(self) -> Self::Output { - self.value - } -} - -pub struct BuildOptionalCellCollector { - builder: T::Builder, - used: bool, -} - -impl Default for BuildOptionalCellCollector { - fn default() -> Self { - Self { builder: Default::default(), used: false } - } -} - -impl BuildCellCollector for BuildOptionalCellCollector { - type Output = Option<::Output>; - - #[inline(always)] - fn push(&mut self, component: u32) -> Result<(), CollectCellsError> { - self.used = true; - self.builder.push(component)?; - - Ok(()) - } - - #[inline(always)] - fn finish(self) -> Self::Output { - match self.used { - true => Some(self.builder.finish()), - false => None, - } - } -} - -impl + core::ops::BitOr + From> - CellCollector for core::num::Wrapping -{ - type Output = Int; - type Builder = BuildWrappingIntCollector; - - #[inline(always)] - fn map(builder_out: ::Output) -> Self::Output { - builder_out - } -} - -/// [PCI Bus Binding to Open Firmware 2.2.1.1 Numerical Representation](https://www.openfirmware.info/data/docs/bus.pci.pdf) -/// -/// Numerical representation of a PCI address used within the `interrupt-map` property -#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct PciAddress { - pub hi: PciAddressHighBits, - pub mid: u32, - pub lo: u32, -} - -impl CellCollector for PciAddress { - type Builder = PciAddressCollector; - type Output = Self; - - fn map(builder_out: ::Output) -> Self::Output { - builder_out - } -} - -impl PartialEq<&'_ PciAddress> for PciAddress { - fn eq(&self, other: &&'_ Self) -> bool { - self.eq(*other) - } -} - -impl PartialEq for &'_ PciAddress { - fn eq(&self, other: &PciAddress) -> bool { - (*self).eq(other) - } -} - -/// `phys.hi cell: npt000ss bbbbbbbb dddddfff rrrrrrrr` -/// -/// where: -/// -/// `n` is 0 if the address is relocatable, 1 otherwise -/// -/// `p` is 1 if the addressable region is "prefetchable", 0 otherwise -/// -/// `t` is 1 if the address is aliased (for non-relocatable I/O), below 1 MB (for Memory), -/// -/// `or` below 64 KB (for relocatable I/O). -/// -/// `ss` is the space code, denoting the address space -/// -/// `bbbbbbbb` is the 8-bit Bus Number -/// -/// `ddddd` is the 5-bit Device Number -/// -/// `fff` is the 3-bit Function Number -/// -/// `rrrrrrrr` is the 8-bit Register Number -#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct PciAddressHighBits(u32); - -impl PciAddressHighBits { - #[inline(always)] - pub fn new(raw: u32) -> Self { - Self(raw) - } - - #[inline(always)] - pub fn register(self) -> u8 { - self.0 as u8 - } - - #[inline(always)] - pub fn function(self) -> u8 { - ((self.0 >> 8) & 0b111) as u8 - } - - #[inline(always)] - pub fn device(self) -> u8 { - ((self.0 >> 12) & 0b11111) as u8 - } - - #[inline(always)] - pub fn bus(self) -> u8 { - (self.0 >> 16) as u8 - } - - #[inline(always)] - pub fn address_space(self) -> PciAddressSpace { - const CONFIGURATION: u8 = const { PciAddressSpace::Configuration as u8 }; - const IO: u8 = const { PciAddressSpace::Io as u8 }; - const MEMORY32: u8 = const { PciAddressSpace::Memory32 as u8 }; - const MEMORY64: u8 = const { PciAddressSpace::Memory64 as u8 }; - - match ((self.0 >> 24) & 0b11) as u8 { - CONFIGURATION => PciAddressSpace::Configuration, - IO => PciAddressSpace::Io, - MEMORY32 => PciAddressSpace::Memory32, - MEMORY64 => PciAddressSpace::Memory64, - _ => unreachable!(), - } - } - - #[inline(always)] - pub fn prefetchable(self) -> bool { - (self.0 >> 30) & 0b1 == 0b1 - } - - #[inline(always)] - pub fn relocatable(self) -> bool { - (self.0 >> 31) & 0b1 == 0b0 - } -} - -impl core::ops::BitAnd for PciAddress { - type Output = Self; - fn bitand(self, rhs: Self) -> Self::Output { - Self { hi: PciAddressHighBits(self.hi.0 & rhs.hi.0), mid: self.mid & rhs.mid, lo: self.lo & rhs.lo } - } -} - -#[repr(u8)] -pub enum PciAddressSpace { - Configuration = 0b00, - Io = 0b01, - Memory32 = 0b10, - Memory64 = 0b11, -} - -#[derive(Default)] -pub struct PciAddressCollector { - address: PciAddress, - num_pushes: u32, -} - -impl BuildCellCollector for PciAddressCollector { - type Output = PciAddress; - - fn push(&mut self, component: u32) -> Result<(), CollectCellsError> { - match self.num_pushes { - 0 => self.address.hi = PciAddressHighBits(component), - 1 => self.address.mid = component, - 2 => self.address.lo = component, - _ => return Err(CollectCellsError), - } - - self.num_pushes += 1; - - Ok(()) - } - - fn finish(self) -> Self::Output { - self.address - } -} +use core::ffi::CStr; pub trait Property<'a, P: ParserWithMode<'a>>: Sized { - fn parse( - node: Node<'a, (P::Parser, NoPanic)>, - root: Root<'a, (P::Parser, NoPanic)>, - ) -> Result, FdtError>; + fn parse(node: FallibleNode<'a, P>, root: Root<'a, (P::Parser, NoPanic)>) -> Result, FdtError>; } /// [Devicetree 2.3.1. @@ -647,1272 +276,23 @@ impl<'a> core::cmp::PartialEq> for str { } } -/// [Devicetree 2.3.5. `#address-cells` and -/// `#size-cells`](https://devicetree-specification.readthedocs.io/en/latest/chapter2-devicetree-basics.html#address-cells-and-size-cells) -/// -/// The `#address-cells` and `#size-cells` properties may be used in any device -/// node that has children in the devicetree hierarchy and describes how child -/// device nodes should be addressed. The `#address-cells` property defines the -/// number of `` cells used to encode the address field in a child node’s -/// reg property. The `#size-cells` property defines the number of `` cells -/// used to encode the size field in a child node’s reg property. -/// -/// The `#address-cells` and `#size-cells` properties are not inherited from -/// ancestors in the devicetree. They shall be explicitly defined. -/// -/// A DTSpec-compliant boot program shall supply `#address-cells` and -/// `#size-cells` on all nodes that have children. -/// -/// If missing, a client program should assume a default value of 2 for -/// `#address-cells`, and a value of 1 for `#size-cells`. -/// -/// Example: -/// -/// ```dts -/// soc { -/// #address-cells = <1>; -/// #size-cells = <1>; -/// -/// serial@4600 { -/// compatible = "ns16550"; -/// reg = <0x4600 0x100>; -/// clock-frequency = <0>; -/// interrupts = <0xA 0x8>; -/// interrupt-parent = <&ipic>; -/// }; -/// }; -/// ``` -/// -/// In this example, the `#address-cells` and `#size-cells` properties of the -/// soc node are both set to `1`. This setting specifies that one cell is -/// required to represent an address and one cell is required to represent the -/// size of nodes that are children of this node. +/// [Devicetree 2.3.10. +/// `dma-coherent`](https://devicetree-specification.readthedocs.io/en/latest/chapter2-devicetree-basics.html#dma-coherent) /// -/// The serial device reg property necessarily follows this specification set in -/// the parent (soc) node—the address is represented by a single cell -/// (`0x4600`), and the size is represented by a single cell (`0x100`). -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct CellSizes { - pub address_cells: usize, - pub size_cells: usize, -} - -impl<'a, P: ParserWithMode<'a>> Property<'a, P> for CellSizes { - #[inline] - fn parse( - node: Node<'a, (P::Parser, NoPanic)>, - _: Root<'a, (P::Parser, NoPanic)>, - ) -> Result, FdtError> { - let (mut address_cells, mut size_cells) = (None, None); - - for property in node.properties()? { - let property = property?; - - let mut parser = UnalignedParser::new(property.value(), StringsBlock(&[]), StructsBlock(&[])); - match property.name() { - "#address-cells" => address_cells = Some(parser.advance_u32()?.to_ne() as usize), - "#size-cells" => size_cells = Some(parser.advance_u32()?.to_ne() as usize), - _ => {} - } - } - - Ok(address_cells.zip(size_cells).map(|(address_cells, size_cells)| CellSizes { address_cells, size_cells })) - } -} - -impl Default for CellSizes { - fn default() -> Self { - CellSizes { address_cells: 2, size_cells: 1 } - } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct AddressCells(pub usize); +/// For architectures which are by default non-coherent for I/O, the +/// `dma-coherent` property is used to indicate a device is capable of coherent +/// DMA operations. Some architectures have coherent DMA by default and this +/// property is not applicable. +pub struct DmaCoherent; -impl<'a, P: ParserWithMode<'a>> Property<'a, P> for AddressCells { - #[inline] +impl<'a, P: ParserWithMode<'a>> Property<'a, P> for DmaCoherent { fn parse( node: Node<'a, (P::Parser, NoPanic)>, _: Root<'a, (P::Parser, NoPanic)>, ) -> Result, FdtError> { - match node.properties()?.find("#address-cells")? { - Some(value) => Ok(Some(Self(value.as_value()?))), + match node.properties()?.find("dma-coherent")? { + Some(_) => Ok(Some(Self)), None => Ok(None), } } } - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct Reg<'a> { - cell_sizes: CellSizes, - encoded_array: &'a [u8], -} - -impl<'a> Reg<'a> { - pub fn cell_sizes(self) -> CellSizes { - self.cell_sizes - } - - pub fn iter_raw(self) -> RegRawIter<'a> { - RegRawIter { cell_sizes: self.cell_sizes, encoded_array: self.encoded_array } - } - - pub fn iter(self) -> RegIter<'a, Addr, Len> { - RegIter { - cell_sizes: self.cell_sizes, - encoded_array: self.encoded_array, - _collector: core::marker::PhantomData, - } - } -} - -impl<'a, P: ParserWithMode<'a>> Property<'a, P> for Reg<'a> { - fn parse( - node: Node<'a, (P::Parser, NoPanic)>, - _: Root<'a, (P::Parser, NoPanic)>, - ) -> Result, FdtError> { - let Some(prop) = node.raw_property("reg")? else { - return Ok(None); - }; - - let cell_sizes = match node.parent() { - Some(parent) => parent.property::()?.unwrap_or_default(), - None => CellSizes::default(), - }; - - let encoded_array = prop.value(); - - if encoded_array.len() % (cell_sizes.address_cells * 4 + cell_sizes.size_cells * 4) != 0 { - return Err(FdtError::InvalidPropertyValue); - } - - Ok(Some(Self { cell_sizes, encoded_array })) - } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct RegEntry { - pub address: Addr, - pub len: Len, -} - -pub struct RegIter<'a, CAddr: CellCollector, Len: CellCollector> { - cell_sizes: CellSizes, - encoded_array: &'a [u8], - _collector: core::marker::PhantomData<*mut (CAddr, Len)>, -} - -impl<'a, CAddr: CellCollector, Len: CellCollector> Iterator for RegIter<'a, CAddr, Len> { - type Item = Result, CollectCellsError>; - fn next(&mut self) -> Option { - let address_bytes = self.cell_sizes.address_cells * 4; - let size_bytes = self.cell_sizes.size_cells * 4; - - let encoded_address = self.encoded_array.get(..address_bytes)?; - let encoded_len = self.encoded_array.get(address_bytes..address_bytes + size_bytes)?; - - let mut address_collector = ::Builder::default(); - for encoded_address in encoded_address.chunks_exact(4) { - // TODO: replace this stuff with `array_chunks` when its stabilized - // - // These unwraps can't panic because `chunks_exact` guarantees that - // we'll always get slices of 4 bytes - if let Err(e) = address_collector.push(u32::from_be_bytes(encoded_address.try_into().unwrap())) { - return Some(Err(e)); - } - } - - let mut len_collector = ::Builder::default(); - for encoded_len in encoded_len.chunks_exact(4) { - // TODO: replace this stuff with `array_chunks` when its stabilized - // - // These unwraps can't panic because `chunks_exact` guarantees that - // we'll always get slices of 4 bytes - if let Err(e) = len_collector.push(u32::from_be_bytes(encoded_len.try_into().unwrap())) { - return Some(Err(e)); - } - } - - self.encoded_array = self.encoded_array.get((address_bytes + size_bytes)..)?; - Some(Ok(RegEntry { address: CAddr::map(address_collector.finish()), len: Len::map(len_collector.finish()) })) - } -} - -pub struct RegRawIter<'a> { - cell_sizes: CellSizes, - encoded_array: &'a [u8], -} - -impl<'a> Iterator for RegRawIter<'a> { - type Item = RawRegEntry<'a>; - fn next(&mut self) -> Option { - let address_bytes = self.cell_sizes.address_cells * 4; - let size_bytes = self.cell_sizes.size_cells * 4; - - let address = self.encoded_array.get(..address_bytes)?; - let len = self.encoded_array.get(address_bytes..address_bytes + size_bytes)?; - self.encoded_array = self.encoded_array.get((address_bytes + size_bytes)..)?; - Some(RawRegEntry { address, len }) - } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct RawRegEntry<'a> { - address: &'a [u8], - len: &'a [u8], -} - -impl<'a> RawRegEntry<'a> { - pub fn address(self) -> &'a [u8] { - self.address - } - - pub fn len(self) -> &'a [u8] { - self.len - } -} - -/// [Devicetree 2.3.7. -/// `virtual-reg`](https://devicetree-specification.readthedocs.io/en/latest/chapter2-devicetree-basics.html#virtual-reg) -/// -/// The `virtual-reg` property specifies an effective address that maps to the -/// first physical address specified in the `reg` property of the device node. -/// This property enables boot programs to provide client programs with -/// virtual-to-physical mappings that have been set up. -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct VirtualReg(u32); - -impl VirtualReg { - pub fn into_u32(self) -> u32 { - self.0 - } -} - -impl<'a, P: ParserWithMode<'a>> Property<'a, P> for VirtualReg { - fn parse( - node: Node<'a, (P::Parser, NoPanic)>, - _: Root<'a, (P::Parser, NoPanic)>, - ) -> Result, FdtError> { - match node.properties()?.find("virtual-reg")? { - Some(vreg) => Ok(Some(Self(vreg.as_value()?))), - None => Ok(None), - } - } -} - -#[derive(Debug, Clone, Copy)] -pub struct Ranges<'a> { - parent_address_cells: AddressCells, - cell_sizes: CellSizes, - ranges: &'a [u8], -} - -impl<'a> Ranges<'a> { - pub fn iter(self) -> RangesIter<'a, CAddr, PAddr, Len> - where - CAddr: CellCollector, - PAddr: CellCollector, - Len: CellCollector, - { - RangesIter { - parent_address_cells: self.parent_address_cells, - cell_sizes: self.cell_sizes, - ranges: self.ranges, - _collectors: core::marker::PhantomData, - } - } -} - -pub struct RangesIter<'a, CAddr: CellCollector = u64, PAddr: CellCollector = u64, Len: CellCollector = u64> { - parent_address_cells: AddressCells, - cell_sizes: CellSizes, - ranges: &'a [u8], - _collectors: core::marker::PhantomData<*mut (CAddr, PAddr, Len)>, -} - -impl<'a, CAddr: CellCollector, PAddr: CellCollector, Len: CellCollector> Iterator - for RangesIter<'a, CAddr, PAddr, Len> -{ - type Item = Result, CollectCellsError>; - fn next(&mut self) -> Option { - let child_address_bytes = self.cell_sizes.address_cells * 4; - let parent_address_bytes = self.parent_address_cells.0 * 4; - let len_bytes = self.cell_sizes.size_cells * 4; - - let child_encoded_address = self.ranges.get(..child_address_bytes)?; - let parent_encoded_address = - self.ranges.get(child_address_bytes..child_address_bytes + parent_address_bytes)?; - let encoded_len = self - .ranges - .get(child_address_bytes + parent_address_bytes..child_address_bytes + parent_address_bytes + len_bytes)?; - - let mut child_address_collector = ::Builder::default(); - for encoded_address in child_encoded_address.chunks_exact(4) { - // TODO: replace this stuff with `array_chunks` when its stabilized - // - // These unwraps can't panic because `chunks_exact` guarantees that - // we'll always get slices of 4 bytes - if let Err(e) = child_address_collector.push(u32::from_be_bytes(encoded_address.try_into().unwrap())) { - return Some(Err(e)); - } - } - - let mut parent_address_collector = ::Builder::default(); - for encoded_address in parent_encoded_address.chunks_exact(4) { - // TODO: replace this stuff with `array_chunks` when its stabilized - // - // These unwraps can't panic because `chunks_exact` guarantees that - // we'll always get slices of 4 bytes - if let Err(e) = parent_address_collector.push(u32::from_be_bytes(encoded_address.try_into().unwrap())) { - return Some(Err(e)); - } - } - - let mut len_collector = ::Builder::default(); - for encoded_len in encoded_len.chunks_exact(4) { - // TODO: replace this stuff with `array_chunks` when its stabilized - // - // These unwraps can't panic because `chunks_exact` guarantees that - // we'll always get slices of 4 bytes - if let Err(e) = len_collector.push(u32::from_be_bytes(encoded_len.try_into().unwrap())) { - return Some(Err(e)); - } - } - - self.ranges = self.ranges.get(child_address_bytes + parent_address_bytes + len_bytes..)?; - Some(Ok(Range { - child_bus_address: CAddr::map(child_address_collector.finish()), - parent_bus_address: PAddr::map(parent_address_collector.finish()), - len: Len::map(len_collector.finish()), - })) - } -} - -pub struct Range { - pub child_bus_address: CAddr, - pub parent_bus_address: PAddr, - pub len: Len, -} - -/// [Devicetree 2.3.10. -/// `dma-coherent`](https://devicetree-specification.readthedocs.io/en/latest/chapter2-devicetree-basics.html#dma-coherent) -/// -/// For architectures which are by default non-coherent for I/O, the -/// `dma-coherent` property is used to indicate a device is capable of coherent -/// DMA operations. Some architectures have coherent DMA by default and this -/// property is not applicable. -pub struct DmaCoherent; - -impl<'a, P: ParserWithMode<'a>> Property<'a, P> for DmaCoherent { - fn parse( - node: Node<'a, (P::Parser, NoPanic)>, - _: Root<'a, (P::Parser, NoPanic)>, - ) -> Result, FdtError> { - match node.properties()?.find("dma-coherent")? { - Some(_) => Ok(Some(Self)), - None => Ok(None), - } - } -} - -/// Enum representing the two possibilities for interrupt descriptions on a -/// devicetree node. See the documentation for each type for more information. -/// [`ExtendedInterrupts`] will take precedence if both properties exist. -pub enum Interrupts<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { - Legacy(LegacyInterrupts<'a, P>), - Extended(ExtendedInterrupts<'a, P>), -} - -impl<'a, P: ParserWithMode<'a>> Property<'a, P> for Interrupts<'a, P> { - fn parse( - node: Node<'a, (P::Parser, NoPanic)>, - root: Root<'a, (P::Parser, NoPanic)>, - ) -> Result, FdtError> { - match ExtendedInterrupts::parse(node, root)? { - Some(extended) => Ok(Some(Self::Extended(extended))), - None => match LegacyInterrupts::parse(node, root)? { - Some(legacy) => Ok(Some(Self::Legacy(legacy))), - None => Ok(None), - }, - } - } -} - -/// [Devicetree 2.4.1.1. -/// `interrupts`](https://devicetree-specification.readthedocs.io/en/latest/chapter2-devicetree-basics.html#interrupts) -/// -/// The `interrupts` property of a device node defines the interrupt or -/// interrupts that are generated by the device. The value of the `interrupts` -/// property consists of an arbitrary number of interrupt specifiers. The format -/// of an interrupt specifier is defined by the binding of the interrupt domain -/// root. -/// -/// `interrupts` is overridden by the `interrupts-extended` property and -/// normally only one or the other should be used. -pub struct LegacyInterrupts<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { - interrupt_parent: InterruptParent<'a, P>, - interrupt_cells: InterruptCells, - encoded_array: &'a [u8], -} - -impl<'a, P: ParserWithMode<'a>> LegacyInterrupts<'a, P> { - pub fn interrupt_parent(self) -> InterruptParent<'a, P> { - self.interrupt_parent - } - - pub fn iter(self) -> LegacyInterruptsIter<'a, I> { - LegacyInterruptsIter { - interrupt_cells: self.interrupt_cells, - encoded_array: self.encoded_array, - _collector: core::marker::PhantomData, - } - } -} - -impl<'a, P: ParserWithMode<'a>> Property<'a, P> for LegacyInterrupts<'a, P> { - fn parse( - node: Node<'a, (P::Parser, NoPanic)>, - root: Root<'a, (P::Parser, NoPanic)>, - ) -> Result, FdtError> { - match node.properties()?.find("interrupts")? { - Some(interrupts) => { - let interrupt_parent = match InterruptParent::<(P::Parser, NoPanic)>::parse(node, root)? { - Some(p) => p, - None => return Err(FdtError::MissingRequiredProperty("interrupt-parent")), - }; - - let Some(interrupt_cells) = interrupt_parent.property::()? else { - return Err(FdtError::MissingRequiredProperty("interrupt-cells")); - }; - - if interrupts.value().len() % (interrupt_cells.0 * 4) != 0 { - return Err(FdtError::InvalidPropertyValue); - } - - Ok(Some(Self { - interrupt_parent: InterruptParent(interrupt_parent.0.alt()), - interrupt_cells, - encoded_array: interrupts.value(), - })) - } - None => Ok(None), - } - } -} - -impl<'a, P: ParserWithMode<'a>> Copy for LegacyInterrupts<'a, P> {} -impl<'a, P: ParserWithMode<'a>> Clone for LegacyInterrupts<'a, P> { - fn clone(&self) -> Self { - *self - } -} - -pub struct LegacyInterruptsIter<'a, I: CellCollector> { - interrupt_cells: InterruptCells, - encoded_array: &'a [u8], - _collector: core::marker::PhantomData<*mut I>, -} - -impl<'a, I: CellCollector> Iterator for LegacyInterruptsIter<'a, I> { - type Item = Result; - fn next(&mut self) -> Option { - let encoded_specifier = self.encoded_array.get(..self.interrupt_cells.0 * 4)?; - let mut specifier_collector = ::Builder::default(); - - for encoded_specifier in encoded_specifier.chunks_exact(4) { - // TODO: replace this stuff with `array_chunks` when its stabilized - // - // These unwraps can't panic because `chunks_exact` guarantees that - // we'll always get slices of 4 bytes - if let Err(e) = specifier_collector.push(u32::from_be_bytes(encoded_specifier.try_into().unwrap())) { - return Some(Err(e)); - } - } - - self.encoded_array = self.encoded_array.get(self.interrupt_cells.0 * 4..)?; - Some(Ok(I::map(specifier_collector.finish()))) - } -} - -/// [Devicetree 2.4.1.3. -/// `interrupts-extended`](https://devicetree-specification.readthedocs.io/en/latest/chapter2-devicetree-basics.html#interrupts-extended) -/// -/// The `interrupts-extended` property lists the interrupt(s) generated by a -/// device. `interrupts-extended` should be used instead of interrupts when a -/// device is connected to multiple interrupt controllers as it encodes a parent -/// `phandle` with each interrupt specifier. -/// -/// Example: -/// -/// This example shows how a device with two interrupt outputs connected to two -/// separate interrupt controllers would describe the connection using an -/// `interrupts-extended` property. `pic` is an interrupt controller with an -/// `#interrupt-cells` specifier of 2, while `gic` is an interrupt controller -/// with an `#interrupts-cells` specifier of 1. -/// -/// `interrupts-extended = <&pic 0xA 8>, <&gic 0xda>;` -pub struct ExtendedInterrupts<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { - root: Root<'a, P>, - encoded_array: &'a [u8], -} - -impl<'a, P: ParserWithMode<'a>> ExtendedInterrupts<'a, P> { - pub fn iter(self) -> ExtendedInterruptsIter<'a, P> { - ExtendedInterruptsIter { root: self.root, encoded_array: self.encoded_array } - } -} - -impl<'a, P: ParserWithMode<'a>> Property<'a, P> for ExtendedInterrupts<'a, P> { - fn parse( - node: Node<'a, (P::Parser, NoPanic)>, - root: Root<'a, (P::Parser, NoPanic)>, - ) -> Result, FdtError> { - match node.properties()?.find("interrupts-extended")? { - Some(interrupts) => { - Ok(Some(Self { encoded_array: interrupts.value(), root: Root { node: root.node.alt() } })) - } - - None => Ok(None), - } - } -} - -pub struct ExtendedInterruptsIter<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { - root: Root<'a, P>, - encoded_array: &'a [u8], -} - -impl<'a, P: ParserWithMode<'a>> Iterator for ExtendedInterruptsIter<'a, P> { - type Item = P::Output>; - - #[track_caller] - fn next(&mut self) -> Option { - let phandle = self - .encoded_array - .get(..4) - .map(|bytes| PHandle(BigEndianU32::from_be(u32::from_ne_bytes(bytes.try_into().unwrap()))))?; - self.encoded_array = self.encoded_array.get(4..)?; - - let res = crate::tryblock!({ - let root = Root { node: self.root.node.fallible() }; - let Some(interrupt_parent) = root.resolve_phandle(phandle)? else { - return Err(FdtError::PHandleNotFound(phandle.0.to_ne())); - }; - - let Some(interrupt_cells) = interrupt_parent.property::()? else { - return Err(FdtError::MissingRequiredProperty("#interrupt-cells")); - }; - - let cells_length = interrupt_cells.0 * 4; - let encoded_array = match self.encoded_array.get(..cells_length) { - Some(bytes) => bytes, - None => return Ok(None), - }; - - self.encoded_array = match self.encoded_array.get(cells_length..) { - Some(bytes) => bytes, - None => return Ok(None), - }; - - Ok(Some(ExtendedInterrupt { - interrupt_parent: InterruptParent(interrupt_parent.alt()), - interrupt_cells, - encoded_array, - })) - }); - - // This is a manual impl of `map` because we need the panic location to - // be the caller if `P::to_output` panics - #[allow(clippy::manual_map)] - match res.transpose() { - Some(output) => Some(P::to_output(output)), - None => None, - } - } -} - -/// A single entry in an `interrupts-extended` property -pub struct ExtendedInterrupt<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { - interrupt_parent: InterruptParent<'a, P>, - interrupt_cells: InterruptCells, - encoded_array: &'a [u8], -} - -impl<'a, P: ParserWithMode<'a>> ExtendedInterrupt<'a, P> { - pub fn interrupt_parent(self) -> InterruptParent<'a, P> { - self.interrupt_parent - } - - pub fn interrupt_cells(self) -> InterruptCells { - self.interrupt_cells - } - - pub fn interrupt_specifier(self) -> InterruptSpecifier<'a> { - InterruptSpecifier { interrupt_cells: self.interrupt_cells, encoded_array: self.encoded_array } - } -} - -pub struct InterruptSpecifier<'a> { - interrupt_cells: InterruptCells, - encoded_array: &'a [u8], -} - -impl<'a> InterruptSpecifier<'a> { - /// Iterate over the components that comprise this interrupt specifier - pub fn iter(self) -> InterruptSpecifierIter<'a> { - InterruptSpecifierIter { encoded_array: self.encoded_array } - } - - /// Extract the single component that comprises the interrupt specifier, if - /// the `#interrupt-cells` value is `1` - pub fn single(self) -> Option { - if self.interrupt_cells.0 != 1 { - return None; - } - - self.iter().next() - } - - /// Extract the two components that comprise the interrupt specifier, if the - /// `#interrupt-cells` value is `2` - pub fn pair(self) -> Option<(u32, u32)> { - if self.interrupt_cells.0 != 2 { - return None; - } - - let mut iter = self.into_iter(); - Some((iter.next()?, iter.next()?)) - } -} - -impl<'a> IntoIterator for InterruptSpecifier<'a> { - type IntoIter = InterruptSpecifierIter<'a>; - type Item = u32; - - fn into_iter(self) -> Self::IntoIter { - self.iter() - } -} - -/// Iterator over individual components in an interrupt specifier -pub struct InterruptSpecifierIter<'a> { - encoded_array: &'a [u8], -} - -impl<'a> Iterator for InterruptSpecifierIter<'a> { - type Item = u32; - - fn next(&mut self) -> Option { - if self.encoded_array.is_empty() { - return None; - } - - let next = self.encoded_array.get(..4)?; - self.encoded_array = self.encoded_array.get(4..)?; - - // This panic can never fail since the slice length is guaranteed to be - // 4 bytes long - Some(u32::from_be_bytes(next.try_into().unwrap())) - } -} - -/// Iterator over pairs of `u32`s representing an interrupt specifier -pub struct InterruptSpecifierIterPairs<'a> { - encoded_array: &'a [u8], -} - -impl<'a> Iterator for InterruptSpecifierIterPairs<'a> { - type Item = (u32, u32); - - fn next(&mut self) -> Option { - if self.encoded_array.is_empty() { - return None; - } - - let next = self.encoded_array.get(..8)?; - self.encoded_array = self.encoded_array.get(8..)?; - - // This panic can never fail since the slice length is guaranteed to be - // 4 bytes long - Some((u32::from_be_bytes(next[..4].try_into().unwrap()), u32::from_be_bytes(next[4..8].try_into().unwrap()))) - } -} - -/// [Devicetree 2.4.1.2. -/// `interrupt-parent`](https://devicetree-specification.readthedocs.io/en/latest/chapter2-devicetree-basics.html#interrupt-parent) -/// -/// Because the hierarchy of the nodes in the interrupt tree might not match the -/// devicetree, the `interrupt-parent` property is available to make the -/// definition of an interrupt parent explicit. The value is the `phandle` to -/// the interrupt parent. If this property is missing from a device, its -/// interrupt parent is assumed to be its devicetree parent. -pub struct InterruptParent<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)>(Node<'a, P>); - -impl<'a, P: ParserWithMode<'a>> Copy for InterruptParent<'a, P> {} -impl<'a, P: ParserWithMode<'a>> Clone for InterruptParent<'a, P> { - fn clone(&self) -> Self { - *self - } -} - -impl<'a, P: ParserWithMode<'a>> core::ops::Deref for InterruptParent<'a, P> { - type Target = Node<'a, P>; - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl<'a, P: ParserWithMode<'a>> core::ops::DerefMut for InterruptParent<'a, P> { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - -impl<'a, P: ParserWithMode<'a>> Property<'a, P> for InterruptParent<'a, P> { - fn parse( - node: Node<'a, (P::Parser, NoPanic)>, - root: Root<'a, (P::Parser, NoPanic)>, - ) -> Result, FdtError> { - match node.properties()?.find("interrupt-parent")? { - Some(phandle) => match root.resolve_phandle(PHandle(phandle.as_value()?))? { - Some(parent) => Ok(Some(Self(parent.alt()))), - None => Err(FdtError::PHandleNotFound(phandle.as_value()?)), - }, - None => Ok(node.parent().map(|n| Self(n.alt()))), - } - } -} - -/// [Devicetree 2.4.2.1. -/// `#interrupt-cells`](https://devicetree-specification.readthedocs.io/en/latest/chapter2-devicetree-basics.html#interrupt-cells) -/// -/// The `#interrupt-cells` property defines the number of cells required to -/// encode an interrupt specifier for an interrupt domain. -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct InterruptCells(pub usize); - -impl<'a, P: ParserWithMode<'a>> Property<'a, P> for InterruptCells { - fn parse( - node: Node<'a, (P::Parser, NoPanic)>, - _: Root<'a, (P::Parser, NoPanic)>, - ) -> Result, FdtError> { - match node.properties()?.find("#interrupt-cells")? { - Some(ic) => Ok(Some(Self(ic.as_value()?))), - None => Ok(None), - } - } -} - -/// [Devicetree 2.4.3.2. -/// `interrupt-map-mask`](https://devicetree-specification.readthedocs.io/en/latest/chapter2-devicetree-basics.html#interrupt-map-mask) -/// -/// An `interrupt-map-mask` property is specified for a nexus node in the -/// interrupt tree. This property specifies a mask that is `AND`ed with the -/// incoming unit interrupt specifier being looked up in the table specified in -/// the `interrupt-map` property. -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct InterruptMapMask { - address_mask: AddrMask::Output, - interrupt_specifier_mask: IntMask::Output, -} - -impl InterruptMapMask { - pub fn mask( - self, - address: ::Output, - interrupt_specifier: ::Output, - ) -> (::Output, ::Output) - where - ::Output: - core::ops::BitAnd<::Output, Output = ::Output>, - ::Output: - core::ops::BitAnd<::Output, Output = ::Output>, - { - (self.address_mask & address, self.interrupt_specifier_mask & interrupt_specifier) - } -} - -impl<'a, AddrMask: CellCollector, IntMask: CellCollector, P: ParserWithMode<'a>> Property<'a, P> - for InterruptMapMask -{ - fn parse( - node: Node<'a, (P::Parser, NoPanic)>, - _: Root<'a, (P::Parser, NoPanic)>, - ) -> Result, FdtError> { - let address_cells = - node.property::()?.ok_or(FdtError::MissingRequiredProperty("#address-cells"))?; - let interrupt_cells = - node.property::()?.ok_or(FdtError::MissingRequiredProperty("#interrupt-cells"))?; - match node.properties()?.find("interrupt-map-mask")? { - Some(prop) => { - if prop.value().len() % 4 != 0 { - return Err(FdtError::InvalidPropertyValue); - } - - let mut address_collector = AddrMask::Builder::default(); - let mut specifier_collector = IntMask::Builder::default(); - let mut cells = prop.value().chunks_exact(4); - - // TODO: replace this stuff with `array_chunks` when its stabilized - // - // These unwraps can't panic because `chunks_exact` guarantees that - // we'll always get slices of 4 bytes - for chunk in cells.by_ref().take(address_cells.0) { - address_collector.push(u32::from_be_bytes(chunk.try_into().unwrap()))?; - } - - for chunk in cells.take(interrupt_cells.0) { - specifier_collector.push(u32::from_be_bytes(chunk.try_into().unwrap()))?; - } - - Ok(Some(Self { - address_mask: AddrMask::map(address_collector.finish()), - interrupt_specifier_mask: IntMask::map(specifier_collector.finish()), - })) - } - None => Ok(None), - } - } -} - -/// [Devicetree 2.4.2.2. -/// `interrupt-controller`](https://devicetree-specification.readthedocs.io/en/latest/chapter2-devicetree-basics.html#interrupt-controller) -/// -/// The presence of an `interrupt-controller` property defines a node as an -/// interrupt controller node. -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct InterruptController; - -impl<'a, P: ParserWithMode<'a>> Property<'a, P> for InterruptController { - fn parse( - node: Node<'a, (P::Parser, NoPanic)>, - _: Root<'a, (P::Parser, NoPanic)>, - ) -> Result, FdtError> { - match node.properties()?.find("interrupt-controller")? { - Some(_) => Ok(Some(Self)), - None => Ok(None), - } - } -} - -pub struct InterruptMap< - 'a, - CAddr: CellCollector, - CInt: CellCollector = u32, - PAddr: CellCollector = u64, - PInt: CellCollector = u32, - P: ParserWithMode<'a> = (AlignedParser<'a>, Panic), -> { - address_cells: AddressCells, - interrupt_cells: InterruptCells, - node: Node<'a, (P::Parser, NoPanic)>, - encoded_map: &'a [u8], - _collectors: core::marker::PhantomData<*mut (CAddr, CInt, PAddr, PInt)>, -} - -impl< - 'a, - P: ParserWithMode<'a>, - CAddr: CellCollector, - CInt: CellCollector, - PAddr: CellCollector, - PInt: CellCollector, - > InterruptMap<'a, CAddr, CInt, PAddr, PInt, P> -{ - pub fn iter(self) -> InterruptMapIter<'a, CAddr, CInt, PAddr, PInt, P> { - InterruptMapIter { - address_cells: self.address_cells, - interrupt_cells: self.interrupt_cells, - node: self.node, - encoded_map: self.encoded_map, - _collectors: core::marker::PhantomData, - } - } - - #[allow(clippy::type_complexity)] - pub fn find( - self, - address: CAddr::Output, - interrupt_specifier: CInt::Output, - ) -> P::Output>> - where - CAddr::Output: PartialEq, - CInt::Output: PartialEq, - { - let this: InterruptMap<_, _, _, _, (P::Parser, NoPanic)> = InterruptMap { - address_cells: self.address_cells, - interrupt_cells: self.interrupt_cells, - node: self.node, - encoded_map: self.encoded_map, - _collectors: self._collectors, - }; - - P::to_output( - this.iter() - .find(|e| match e { - Err(_) => true, - Ok(entry) => { - entry.child_unit_address == address && entry.child_interrupt_specifier == interrupt_specifier - } - }) - .transpose() - .map(|e| { - e.map(|e| InterruptMapEntry::<_, _, _, _, P> { - child_unit_address: e.child_unit_address, - child_interrupt_specifier: e.child_interrupt_specifier, - interrupt_parent: e.interrupt_parent.alt(), - parent_unit_address: e.parent_unit_address, - parent_interrupt_specifier: e.parent_interrupt_specifier, - }) - }), - ) - } -} - -impl< - 'a, - P: ParserWithMode<'a>, - CAddr: CellCollector, - CInt: CellCollector, - PAddr: CellCollector, - PInt: CellCollector, - > Property<'a, P> for InterruptMap<'a, CAddr, CInt, PAddr, PInt, P> -{ - fn parse( - node: Node<'a, (P::Parser, NoPanic)>, - _: Root<'a, (P::Parser, NoPanic)>, - ) -> Result, FdtError> { - let Some(encoded_map) = node.properties()?.find("interrupt-map")? else { return Ok(None) }; - - let address_cells = - node.property::()?.ok_or(FdtError::MissingRequiredProperty("#address-cells"))?; - let interrupt_cells = - node.property::()?.ok_or(FdtError::MissingRequiredProperty("#interrupt-cells"))?; - - Ok(Some(InterruptMap { - address_cells, - interrupt_cells, - node: node.alt(), - encoded_map: encoded_map.value(), - _collectors: core::marker::PhantomData, - })) - } -} - -pub struct InterruptMapIter< - 'a, - CAddr: CellCollector, - CInt: CellCollector, - PAddr: CellCollector, - PInt: CellCollector, - P: ParserWithMode<'a> = (AlignedParser<'a>, Panic), -> { - address_cells: AddressCells, - interrupt_cells: InterruptCells, - node: Node<'a, (P::Parser, NoPanic)>, - encoded_map: &'a [u8], - _collectors: core::marker::PhantomData<*mut (CAddr, CInt, PAddr, PInt)>, -} - -impl< - 'a, - CAddr: CellCollector, - CInt: CellCollector, - PAddr: CellCollector, - PInt: CellCollector, - P: ParserWithMode<'a>, - > Iterator for InterruptMapIter<'a, CAddr, CInt, PAddr, PInt, P> -{ - type Item = P::Output>; - - #[track_caller] - fn next(&mut self) -> Option { - let res = crate::tryblock!({ - let child_addr_size = self.address_cells.0 * 4; - let child_intsp_size = self.interrupt_cells.0 * 4; - - let Some((child_address_iter, rest)) = self.encoded_map.split_at_checked(child_addr_size) else { - return Ok(None); - }; - let Some((child_specifier_iter, rest)) = rest.split_at_checked(child_intsp_size) else { - return Ok(None); - }; - let Some((interrupt_parent, rest)) = rest.split_at_checked(4) else { - return Ok(None); - }; - - let root = self.node.make_root::<(P::Parser, NoPanic)>()?; - let phandle = u32::from_ne_bytes(interrupt_parent.try_into().unwrap()); - let interrupt_parent = root - .resolve_phandle(PHandle(BigEndianU32::from_be(phandle)))? - .ok_or(FdtError::PHandleNotFound(phandle.swap_bytes()))?; - - let parent_address_cells = interrupt_parent - .property::()? - .ok_or(FdtError::MissingRequiredProperty("#address-cells"))?; - let parent_interrupt_cells = interrupt_parent - .property::()? - .ok_or(FdtError::MissingRequiredProperty("#interrupt-cells"))?; - - let parent_addr_size = parent_address_cells.0 * 4; - let parent_intsp_size = parent_interrupt_cells.0 * 4; - - let Some((parent_address_iter, rest)) = rest.split_at_checked(parent_addr_size) else { - return Ok(None); - }; - - let Some((parent_specifier_iter, rest)) = rest.split_at_checked(parent_intsp_size) else { - return Ok(None); - }; - self.encoded_map = rest; - - let mut child_address_collector = CAddr::Builder::default(); - for chunk in child_address_iter.chunks_exact(4) { - child_address_collector.push(u32::from_be_bytes(chunk.try_into().unwrap()))?; - } - - let mut child_specifier_collector = CInt::Builder::default(); - for chunk in child_specifier_iter.chunks_exact(4) { - child_specifier_collector.push(u32::from_be_bytes(chunk.try_into().unwrap()))?; - } - - let mut parent_address_collector = PAddr::Builder::default(); - for chunk in parent_address_iter.chunks_exact(4) { - parent_address_collector.push(u32::from_be_bytes(chunk.try_into().unwrap()))?; - } - - let mut parent_specifier_collector = PInt::Builder::default(); - for chunk in parent_specifier_iter.chunks_exact(4) { - parent_specifier_collector.push(u32::from_be_bytes(chunk.try_into().unwrap()))?; - } - - Ok(Some(InterruptMapEntry { - interrupt_parent: interrupt_parent.alt(), - child_unit_address: CAddr::map(child_address_collector.finish()), - child_interrupt_specifier: CInt::map(child_specifier_collector.finish()), - parent_unit_address: PAddr::map(parent_address_collector.finish()), - parent_interrupt_specifier: PInt::map(parent_specifier_collector.finish()), - })) - }); - - #[allow(clippy::manual_map)] - match res.transpose() { - Some(output) => Some(P::to_output(output)), - None => None, - } - } -} - -pub struct InterruptMapEntry< - 'a, - CAddr: CellCollector, - CInt: CellCollector, - PAddr: CellCollector, - PInt: CellCollector, - P: ParserWithMode<'a>, -> { - pub interrupt_parent: Node<'a, P>, - pub child_unit_address: CAddr::Output, - pub child_interrupt_specifier: CInt::Output, - pub parent_unit_address: PAddr::Output, - pub parent_interrupt_specifier: PInt::Output, -} - -impl< - 'a, - P: ParserWithMode<'a>, - CAddr: CellCollector, - CInt: CellCollector, - PAddr: CellCollector, - PInt: CellCollector, - > Clone for InterruptMapEntry<'a, CAddr, CInt, PAddr, PInt, P> -where - CAddr::Output: Clone, - CInt::Output: Clone, - PAddr::Output: Clone, - PInt::Output: Clone, -{ - fn clone(&self) -> Self { - Self { - child_unit_address: self.child_unit_address.clone(), - child_interrupt_specifier: self.child_interrupt_specifier.clone(), - interrupt_parent: self.interrupt_parent, - parent_unit_address: self.parent_unit_address.clone(), - parent_interrupt_specifier: self.parent_interrupt_specifier.clone(), - } - } -} - -impl< - 'a, - P: ParserWithMode<'a>, - CAddr: CellCollector, - CInt: CellCollector, - PAddr: CellCollector, - PInt: CellCollector, - > Copy for InterruptMapEntry<'a, CAddr, CInt, PAddr, PInt, P> -where - CAddr::Output: Copy, - CInt::Output: Copy, - PAddr::Output: Copy, - PInt::Output: Copy, -{ -} - -#[derive(Debug, Clone, Copy)] -pub struct InvalidPropertyValue; - -impl From for FdtError { - fn from(_: InvalidPropertyValue) -> Self { - FdtError::InvalidPropertyValue - } -} - -pub trait PropertyValue<'a>: Sized { - fn parse(value: &'a [u8]) -> Result; -} - -impl<'a> PropertyValue<'a> for u32 { - #[inline] - fn parse(value: &'a [u8]) -> Result { - match value { - [a, b, c, d] => Ok(u32::from_be_bytes([*a, *b, *c, *d])), - _ => Err(InvalidPropertyValue), - } - } -} - -impl<'a> PropertyValue<'a> for u64 { - #[inline] - fn parse(value: &'a [u8]) -> Result { - match value { - [a, b, c, d] => Ok(u64::from_be_bytes([0, 0, 0, 0, *a, *b, *c, *d])), - [a, b, c, d, e, f, g, h] => Ok(u64::from_be_bytes([*a, *b, *c, *d, *e, *f, *g, *h])), - _ => Err(InvalidPropertyValue), - } - } -} - -impl<'a> PropertyValue<'a> for usize { - #[inline] - fn parse(value: &'a [u8]) -> Result { - #[cfg(target_pointer_width = "32")] - let ret = match value { - [a, b, c, d] => Ok(usize::from_be_bytes([*a, *b, *c, *d])), - _ => Err(InvalidPropertyValue), - }; - - #[cfg(target_pointer_width = "64")] - let ret = match value { - [a, b, c, d] => Ok(usize::from_be_bytes([0, 0, 0, 0, *a, *b, *c, *d])), - [a, b, c, d, e, f, g, h] => Ok(usize::from_be_bytes([*a, *b, *c, *d, *e, *f, *g, *h])), - _ => Err(InvalidPropertyValue), - }; - - ret - } -} - -impl<'a> PropertyValue<'a> for BigEndianU32 { - #[inline] - fn parse(value: &'a [u8]) -> Result { - match value { - [a, b, c, d] => Ok(BigEndianU32::from_be(u32::from_ne_bytes([*a, *b, *c, *d]))), - _ => Err(InvalidPropertyValue), - } - } -} - -impl<'a> PropertyValue<'a> for &'a CStr { - #[inline] - fn parse(value: &'a [u8]) -> Result { - CStr::from_bytes_until_nul(value).map_err(|_| InvalidPropertyValue) - } -} - -impl<'a> PropertyValue<'a> for &'a str { - #[inline] - fn parse(value: &'a [u8]) -> Result { - core::str::from_utf8(value).map(|s| s.trim_end_matches('\0')).map_err(|_| InvalidPropertyValue) - } -} - -#[derive(Debug, Clone, Copy)] -pub struct U32List<'a>(&'a [u8]); - -impl<'a> U32List<'a> { - pub fn iter(self) -> U32ListIter<'a> { - U32ListIter(self.0) - } -} - -impl<'a> PropertyValue<'a> for U32List<'a> { - fn parse(value: &'a [u8]) -> Result { - if value.len() % 4 != 0 { - return Err(InvalidPropertyValue); - } - - Ok(Self(value)) - } -} - -pub struct U32ListIter<'a>(&'a [u8]); - -impl<'a> Iterator for U32ListIter<'a> { - type Item = u32; - fn next(&mut self) -> Option { - let val = u32::from_be_bytes(self.0.get(..4)?.try_into().unwrap()); - self.0 = self.0.get(4..)?; - Some(val) - } -} - -#[derive(Debug, Clone)] -pub struct StringList<'a> { - strs: core::str::Split<'a, char>, -} - -impl<'a> PropertyValue<'a> for StringList<'a> { - #[inline] - fn parse(value: &'a [u8]) -> Result { - Ok(Self { strs: <&'a str as PropertyValue<'a>>::parse(value)?.split('\0') }) - } -} - -impl<'a> Iterator for StringList<'a> { - type Item = &'a str; - - #[inline(always)] - fn next(&mut self) -> Option { - self.strs.next() - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn reg_raw_iter() { - let mut iter = RegRawIter { - cell_sizes: CellSizes { address_cells: 2, size_cells: 1 }, - encoded_array: &[0x55, 0x44, 0x33, 0x22, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD], - }; - - assert_eq!( - iter.next().unwrap(), - RawRegEntry { address: &[0x55, 0x44, 0x33, 0x22, 0x66, 0x77, 0x88, 0x99], len: &[0xAA, 0xBB, 0xCC, 0xDD] } - ); - } - - #[test] - fn reg_u64_iter() { - let mut iter = RegIter:: { - cell_sizes: CellSizes { address_cells: 2, size_cells: 1 }, - encoded_array: &[0x55, 0x44, 0x33, 0x22, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD], - _collector: core::marker::PhantomData, - }; - - assert_eq!(iter.next().unwrap().unwrap(), RegEntry { address: 0x5544332266778899, len: 0xAABBCCDD }); - } -} diff --git a/src/properties/cells.rs b/src/properties/cells.rs new file mode 100644 index 0000000..697f7c7 --- /dev/null +++ b/src/properties/cells.rs @@ -0,0 +1,97 @@ +use crate::{ + nodes::FallibleNode, + parsing::{unaligned::UnalignedParser, NoPanic, Parser, ParserWithMode, StringsBlock, StructsBlock}, + standard_nodes::Root, + FdtError, +}; + +use super::Property; + +/// [Devicetree 2.3.5. `#address-cells` and +/// `#size-cells`](https://devicetree-specification.readthedocs.io/en/latest/chapter2-devicetree-basics.html#address-cells-and-size-cells) +/// +/// The `#address-cells` and `#size-cells` properties may be used in any device +/// node that has children in the devicetree hierarchy and describes how child +/// device nodes should be addressed. The `#address-cells` property defines the +/// number of `` cells used to encode the address field in a child node’s +/// reg property. The `#size-cells` property defines the number of `` cells +/// used to encode the size field in a child node’s reg property. +/// +/// The `#address-cells` and `#size-cells` properties are not inherited from +/// ancestors in the devicetree. They shall be explicitly defined. +/// +/// A DTSpec-compliant boot program shall supply `#address-cells` and +/// `#size-cells` on all nodes that have children. +/// +/// If missing, a client program should assume a default value of 2 for +/// `#address-cells`, and a value of 1 for `#size-cells`. +/// +/// Example: +/// +/// ```dts +/// soc { +/// #address-cells = <1>; +/// #size-cells = <1>; +/// +/// serial@4600 { +/// compatible = "ns16550"; +/// reg = <0x4600 0x100>; +/// clock-frequency = <0>; +/// interrupts = <0xA 0x8>; +/// interrupt-parent = <&ipic>; +/// }; +/// }; +/// ``` +/// +/// In this example, the `#address-cells` and `#size-cells` properties of the +/// soc node are both set to `1`. This setting specifies that one cell is +/// required to represent an address and one cell is required to represent the +/// size of nodes that are children of this node. +/// +/// The serial device reg property necessarily follows this specification set in +/// the parent (soc) node—the address is represented by a single cell +/// (`0x4600`), and the size is represented by a single cell (`0x100`). +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct CellSizes { + pub address_cells: usize, + pub size_cells: usize, +} + +impl<'a, P: ParserWithMode<'a>> Property<'a, P> for CellSizes { + #[inline] + fn parse(node: FallibleNode<'a, P>, _: Root<'a, (P::Parser, NoPanic)>) -> Result, FdtError> { + let (mut address_cells, mut size_cells) = (None, None); + + for property in node.properties()? { + let property = property?; + + let mut parser = UnalignedParser::new(property.value(), StringsBlock(&[]), StructsBlock(&[])); + match property.name() { + "#address-cells" => address_cells = Some(parser.advance_u32()?.to_ne() as usize), + "#size-cells" => size_cells = Some(parser.advance_u32()?.to_ne() as usize), + _ => {} + } + } + + Ok(address_cells.zip(size_cells).map(|(address_cells, size_cells)| CellSizes { address_cells, size_cells })) + } +} + +impl Default for CellSizes { + fn default() -> Self { + CellSizes { address_cells: 2, size_cells: 1 } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct AddressCells(pub usize); + +impl<'a, P: ParserWithMode<'a>> Property<'a, P> for AddressCells { + #[inline] + fn parse(node: FallibleNode<'a, P>, _: Root<'a, (P::Parser, NoPanic)>) -> Result, FdtError> { + match node.properties()?.find("#address-cells")? { + Some(value) => Ok(Some(Self(value.as_value()?))), + None => Ok(None), + } + } +} diff --git a/src/properties/interrupts.rs b/src/properties/interrupts.rs new file mode 100644 index 0000000..68f6fd7 --- /dev/null +++ b/src/properties/interrupts.rs @@ -0,0 +1,732 @@ +pub mod pci; + +use super::{cells::AddressCells, PHandle, Property}; +use crate::{ + cell_collector::{BuildCellCollector, CellCollector, CollectCellsError}, + nodes::{FallibleNode, Node}, + parsing::{aligned::AlignedParser, BigEndianU32, NoPanic, Panic, ParserWithMode}, + standard_nodes::Root, + FdtError, +}; + +/// Enum representing the two possibilities for interrupt descriptions on a +/// devicetree node. See the documentation for each type for more information. +/// [`ExtendedInterrupts`] will take precedence if both properties exist. +pub enum Interrupts<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { + Legacy(LegacyInterrupts<'a, P>), + Extended(ExtendedInterrupts<'a, P>), +} + +impl<'a, P: ParserWithMode<'a>> Property<'a, P> for Interrupts<'a, P> { + fn parse(node: FallibleNode<'a, P>, root: Root<'a, (P::Parser, NoPanic)>) -> Result, FdtError> { + match ExtendedInterrupts::parse(node, root)? { + Some(extended) => Ok(Some(Self::Extended(extended))), + None => match LegacyInterrupts::parse(node, root)? { + Some(legacy) => Ok(Some(Self::Legacy(legacy))), + None => Ok(None), + }, + } + } +} + +/// [Devicetree 2.4.1.1. +/// `interrupts`](https://devicetree-specification.readthedocs.io/en/latest/chapter2-devicetree-basics.html#interrupts) +/// +/// The `interrupts` property of a device node defines the interrupt or +/// interrupts that are generated by the device. The value of the `interrupts` +/// property consists of an arbitrary number of interrupt specifiers. The format +/// of an interrupt specifier is defined by the binding of the interrupt domain +/// root. +/// +/// `interrupts` is overridden by the `interrupts-extended` property and +/// normally only one or the other should be used. +pub struct LegacyInterrupts<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { + interrupt_parent: InterruptParent<'a, P>, + interrupt_cells: InterruptCells, + encoded_array: &'a [u8], +} + +impl<'a, P: ParserWithMode<'a>> LegacyInterrupts<'a, P> { + pub fn interrupt_parent(self) -> InterruptParent<'a, P> { + self.interrupt_parent + } + + pub fn iter(self) -> LegacyInterruptsIter<'a, I> { + LegacyInterruptsIter { + interrupt_cells: self.interrupt_cells, + encoded_array: self.encoded_array, + _collector: core::marker::PhantomData, + } + } +} + +impl<'a, P: ParserWithMode<'a>> Property<'a, P> for LegacyInterrupts<'a, P> { + fn parse(node: FallibleNode<'a, P>, root: Root<'a, (P::Parser, NoPanic)>) -> Result, FdtError> { + match node.properties()?.find("interrupts")? { + Some(interrupts) => { + let interrupt_parent = match InterruptParent::<(P::Parser, NoPanic)>::parse(node, root)? { + Some(p) => p, + None => return Err(FdtError::MissingRequiredProperty("interrupt-parent")), + }; + + let Some(interrupt_cells) = interrupt_parent.property::()? else { + return Err(FdtError::MissingRequiredProperty("interrupt-cells")); + }; + + if interrupts.value().len() % (interrupt_cells.0 * 4) != 0 { + return Err(FdtError::InvalidPropertyValue); + } + + Ok(Some(Self { + interrupt_parent: InterruptParent(interrupt_parent.0.alt()), + interrupt_cells, + encoded_array: interrupts.value(), + })) + } + None => Ok(None), + } + } +} + +impl<'a, P: ParserWithMode<'a>> Copy for LegacyInterrupts<'a, P> {} +impl<'a, P: ParserWithMode<'a>> Clone for LegacyInterrupts<'a, P> { + fn clone(&self) -> Self { + *self + } +} + +pub struct LegacyInterruptsIter<'a, I: CellCollector> { + interrupt_cells: InterruptCells, + encoded_array: &'a [u8], + _collector: core::marker::PhantomData<*mut I>, +} + +impl<'a, I: CellCollector> Iterator for LegacyInterruptsIter<'a, I> { + type Item = Result; + fn next(&mut self) -> Option { + let encoded_specifier = self.encoded_array.get(..self.interrupt_cells.0 * 4)?; + let mut specifier_collector = ::Builder::default(); + + for encoded_specifier in encoded_specifier.chunks_exact(4) { + // TODO: replace this stuff with `array_chunks` when its stabilized + // + // These unwraps can't panic because `chunks_exact` guarantees that + // we'll always get slices of 4 bytes + if let Err(e) = specifier_collector.push(u32::from_be_bytes(encoded_specifier.try_into().unwrap())) { + return Some(Err(e)); + } + } + + self.encoded_array = self.encoded_array.get(self.interrupt_cells.0 * 4..)?; + Some(Ok(I::map(specifier_collector.finish()))) + } +} + +/// [Devicetree 2.4.1.3. +/// `interrupts-extended`](https://devicetree-specification.readthedocs.io/en/latest/chapter2-devicetree-basics.html#interrupts-extended) +/// +/// The `interrupts-extended` property lists the interrupt(s) generated by a +/// device. `interrupts-extended` should be used instead of interrupts when a +/// device is connected to multiple interrupt controllers as it encodes a parent +/// `phandle` with each interrupt specifier. +/// +/// Example: +/// +/// This example shows how a device with two interrupt outputs connected to two +/// separate interrupt controllers would describe the connection using an +/// `interrupts-extended` property. `pic` is an interrupt controller with an +/// `#interrupt-cells` specifier of 2, while `gic` is an interrupt controller +/// with an `#interrupts-cells` specifier of 1. +/// +/// `interrupts-extended = <&pic 0xA 8>, <&gic 0xda>;` +pub struct ExtendedInterrupts<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { + root: Root<'a, P>, + encoded_array: &'a [u8], +} + +impl<'a, P: ParserWithMode<'a>> ExtendedInterrupts<'a, P> { + pub fn iter(self) -> ExtendedInterruptsIter<'a, P> { + ExtendedInterruptsIter { root: self.root, encoded_array: self.encoded_array } + } +} + +impl<'a, P: ParserWithMode<'a>> Property<'a, P> for ExtendedInterrupts<'a, P> { + fn parse(node: FallibleNode<'a, P>, root: Root<'a, (P::Parser, NoPanic)>) -> Result, FdtError> { + match node.properties()?.find("interrupts-extended")? { + Some(interrupts) => { + Ok(Some(Self { encoded_array: interrupts.value(), root: Root { node: root.node.alt() } })) + } + + None => Ok(None), + } + } +} + +pub struct ExtendedInterruptsIter<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { + root: Root<'a, P>, + encoded_array: &'a [u8], +} + +impl<'a, P: ParserWithMode<'a>> Iterator for ExtendedInterruptsIter<'a, P> { + type Item = P::Output>; + + #[track_caller] + fn next(&mut self) -> Option { + let phandle = self + .encoded_array + .get(..4) + .map(|bytes| PHandle(BigEndianU32::from_be(u32::from_ne_bytes(bytes.try_into().unwrap()))))?; + self.encoded_array = self.encoded_array.get(4..)?; + + let res = crate::tryblock!({ + let root = Root { node: self.root.node.fallible() }; + let Some(interrupt_parent) = root.resolve_phandle(phandle)? else { + return Err(FdtError::PHandleNotFound(phandle.0.to_ne())); + }; + + let Some(interrupt_cells) = interrupt_parent.property::()? else { + return Err(FdtError::MissingRequiredProperty("#interrupt-cells")); + }; + + let cells_length = interrupt_cells.0 * 4; + let encoded_array = match self.encoded_array.get(..cells_length) { + Some(bytes) => bytes, + None => return Ok(None), + }; + + self.encoded_array = match self.encoded_array.get(cells_length..) { + Some(bytes) => bytes, + None => return Ok(None), + }; + + Ok(Some(ExtendedInterrupt { + interrupt_parent: InterruptParent(interrupt_parent.alt()), + interrupt_cells, + encoded_array, + })) + }); + + // This is a manual impl of `map` because we need the panic location to + // be the caller if `P::to_output` panics + #[allow(clippy::manual_map)] + match res.transpose() { + Some(output) => Some(P::to_output(output)), + None => None, + } + } +} + +/// A single entry in an `interrupts-extended` property +pub struct ExtendedInterrupt<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { + interrupt_parent: InterruptParent<'a, P>, + interrupt_cells: InterruptCells, + encoded_array: &'a [u8], +} + +impl<'a, P: ParserWithMode<'a>> ExtendedInterrupt<'a, P> { + pub fn interrupt_parent(self) -> InterruptParent<'a, P> { + self.interrupt_parent + } + + pub fn interrupt_cells(self) -> InterruptCells { + self.interrupt_cells + } + + pub fn interrupt_specifier(self) -> InterruptSpecifier<'a> { + InterruptSpecifier { interrupt_cells: self.interrupt_cells, encoded_array: self.encoded_array } + } +} + +pub struct InterruptSpecifier<'a> { + interrupt_cells: InterruptCells, + encoded_array: &'a [u8], +} + +impl<'a> InterruptSpecifier<'a> { + /// Iterate over the components that comprise this interrupt specifier + pub fn iter(self) -> InterruptSpecifierIter<'a> { + InterruptSpecifierIter { encoded_array: self.encoded_array } + } + + /// Extract the single component that comprises the interrupt specifier, if + /// the `#interrupt-cells` value is `1` + pub fn single(self) -> Option { + if self.interrupt_cells.0 != 1 { + return None; + } + + self.iter().next() + } + + /// Extract the two components that comprise the interrupt specifier, if the + /// `#interrupt-cells` value is `2` + pub fn pair(self) -> Option<(u32, u32)> { + if self.interrupt_cells.0 != 2 { + return None; + } + + let mut iter = self.into_iter(); + Some((iter.next()?, iter.next()?)) + } +} + +impl<'a> IntoIterator for InterruptSpecifier<'a> { + type IntoIter = InterruptSpecifierIter<'a>; + type Item = u32; + + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + +/// Iterator over individual components in an interrupt specifier +pub struct InterruptSpecifierIter<'a> { + encoded_array: &'a [u8], +} + +impl<'a> Iterator for InterruptSpecifierIter<'a> { + type Item = u32; + + fn next(&mut self) -> Option { + if self.encoded_array.is_empty() { + return None; + } + + let next = self.encoded_array.get(..4)?; + self.encoded_array = self.encoded_array.get(4..)?; + + // This panic can never fail since the slice length is guaranteed to be + // 4 bytes long + Some(u32::from_be_bytes(next.try_into().unwrap())) + } +} + +/// Iterator over pairs of `u32`s representing an interrupt specifier +pub struct InterruptSpecifierIterPairs<'a> { + encoded_array: &'a [u8], +} + +impl<'a> Iterator for InterruptSpecifierIterPairs<'a> { + type Item = (u32, u32); + + fn next(&mut self) -> Option { + if self.encoded_array.is_empty() { + return None; + } + + let (next, rest) = self.encoded_array.split_at_checked(8)?; + self.encoded_array = rest; + + let (first, second) = next.split_at(4); + + // This panic can never fail since the slice length is guaranteed to be + // 4 bytes long + Some((u32::from_be_bytes(first.try_into().unwrap()), u32::from_be_bytes(second.try_into().unwrap()))) + } +} + +/// [Devicetree 2.4.1.2. +/// `interrupt-parent`](https://devicetree-specification.readthedocs.io/en/latest/chapter2-devicetree-basics.html#interrupt-parent) +/// +/// Because the hierarchy of the nodes in the interrupt tree might not match the +/// devicetree, the `interrupt-parent` property is available to make the +/// definition of an interrupt parent explicit. The value is the `phandle` to +/// the interrupt parent. If this property is missing from a device, its +/// interrupt parent is assumed to be its devicetree parent. +pub struct InterruptParent<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)>(Node<'a, P>); + +impl<'a, P: ParserWithMode<'a>> Copy for InterruptParent<'a, P> {} +impl<'a, P: ParserWithMode<'a>> Clone for InterruptParent<'a, P> { + fn clone(&self) -> Self { + *self + } +} + +impl<'a, P: ParserWithMode<'a>> core::ops::Deref for InterruptParent<'a, P> { + type Target = Node<'a, P>; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl<'a, P: ParserWithMode<'a>> core::ops::DerefMut for InterruptParent<'a, P> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl<'a, P: ParserWithMode<'a>> Property<'a, P> for InterruptParent<'a, P> { + fn parse(node: FallibleNode<'a, P>, root: Root<'a, (P::Parser, NoPanic)>) -> Result, FdtError> { + match node.properties()?.find("interrupt-parent")? { + Some(phandle) => match root.resolve_phandle(PHandle(phandle.as_value()?))? { + Some(parent) => Ok(Some(Self(parent.alt()))), + None => Err(FdtError::PHandleNotFound(phandle.as_value()?)), + }, + None => Ok(node.parent().map(|n| Self(n.alt()))), + } + } +} + +/// [Devicetree 2.4.2.1. +/// `#interrupt-cells`](https://devicetree-specification.readthedocs.io/en/latest/chapter2-devicetree-basics.html#interrupt-cells) +/// +/// The `#interrupt-cells` property defines the number of cells required to +/// encode an interrupt specifier for an interrupt domain. +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct InterruptCells(pub usize); + +impl<'a, P: ParserWithMode<'a>> Property<'a, P> for InterruptCells { + fn parse(node: FallibleNode<'a, P>, _: Root<'a, (P::Parser, NoPanic)>) -> Result, FdtError> { + match node.properties()?.find("#interrupt-cells")? { + Some(ic) => Ok(Some(Self(ic.as_value()?))), + None => Ok(None), + } + } +} + +/// [Devicetree 2.4.3.2. +/// `interrupt-map-mask`](https://devicetree-specification.readthedocs.io/en/latest/chapter2-devicetree-basics.html#interrupt-map-mask) +/// +/// An `interrupt-map-mask` property is specified for a nexus node in the +/// interrupt tree. This property specifies a mask that is `AND`ed with the +/// incoming unit interrupt specifier being looked up in the table specified in +/// the `interrupt-map` property. +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct InterruptMapMask { + address_mask: AddrMask::Output, + interrupt_specifier_mask: IntMask::Output, +} + +impl InterruptMapMask { + pub fn mask( + self, + address: ::Output, + interrupt_specifier: ::Output, + ) -> (::Output, ::Output) + where + ::Output: + core::ops::BitAnd<::Output, Output = ::Output>, + ::Output: + core::ops::BitAnd<::Output, Output = ::Output>, + { + (self.address_mask & address, self.interrupt_specifier_mask & interrupt_specifier) + } +} + +impl<'a, AddrMask: CellCollector, IntMask: CellCollector, P: ParserWithMode<'a>> Property<'a, P> + for InterruptMapMask +{ + fn parse(node: FallibleNode<'a, P>, _: Root<'a, (P::Parser, NoPanic)>) -> Result, FdtError> { + let address_cells = + node.property::()?.ok_or(FdtError::MissingRequiredProperty("#address-cells"))?; + let interrupt_cells = + node.property::()?.ok_or(FdtError::MissingRequiredProperty("#interrupt-cells"))?; + match node.properties()?.find("interrupt-map-mask")? { + Some(prop) => { + if prop.value().len() % 4 != 0 { + return Err(FdtError::InvalidPropertyValue); + } + + let mut address_collector = AddrMask::Builder::default(); + let mut specifier_collector = IntMask::Builder::default(); + let mut cells = prop.value().chunks_exact(4); + + // TODO: replace this stuff with `array_chunks` when its stabilized + // + // These unwraps can't panic because `chunks_exact` guarantees that + // we'll always get slices of 4 bytes + for chunk in cells.by_ref().take(address_cells.0) { + address_collector.push(u32::from_be_bytes(chunk.try_into().unwrap()))?; + } + + for chunk in cells.take(interrupt_cells.0) { + specifier_collector.push(u32::from_be_bytes(chunk.try_into().unwrap()))?; + } + + Ok(Some(Self { + address_mask: AddrMask::map(address_collector.finish()), + interrupt_specifier_mask: IntMask::map(specifier_collector.finish()), + })) + } + None => Ok(None), + } + } +} + +/// [Devicetree 2.4.2.2. +/// `interrupt-controller`](https://devicetree-specification.readthedocs.io/en/latest/chapter2-devicetree-basics.html#interrupt-controller) +/// +/// The presence of an `interrupt-controller` property defines a node as an +/// interrupt controller node. +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct InterruptController; + +impl<'a, P: ParserWithMode<'a>> Property<'a, P> for InterruptController { + fn parse(node: FallibleNode<'a, P>, _: Root<'a, (P::Parser, NoPanic)>) -> Result, FdtError> { + match node.properties()?.find("interrupt-controller")? { + Some(_) => Ok(Some(Self)), + None => Ok(None), + } + } +} + +pub struct InterruptMap< + 'a, + CAddr: CellCollector, + CInt: CellCollector = u32, + PAddr: CellCollector = u64, + PInt: CellCollector = u32, + P: ParserWithMode<'a> = (AlignedParser<'a>, Panic), +> { + address_cells: AddressCells, + interrupt_cells: InterruptCells, + node: FallibleNode<'a, P>, + encoded_map: &'a [u8], + _collectors: core::marker::PhantomData<*mut (CAddr, CInt, PAddr, PInt)>, +} + +impl< + 'a, + P: ParserWithMode<'a>, + CAddr: CellCollector, + CInt: CellCollector, + PAddr: CellCollector, + PInt: CellCollector, + > InterruptMap<'a, CAddr, CInt, PAddr, PInt, P> +{ + pub fn iter(self) -> InterruptMapIter<'a, CAddr, CInt, PAddr, PInt, P> { + InterruptMapIter { + address_cells: self.address_cells, + interrupt_cells: self.interrupt_cells, + node: self.node, + encoded_map: self.encoded_map, + _collectors: core::marker::PhantomData, + } + } + + #[allow(clippy::type_complexity)] + pub fn find( + self, + address: CAddr::Output, + interrupt_specifier: CInt::Output, + ) -> P::Output>> + where + CAddr::Output: PartialEq, + CInt::Output: PartialEq, + { + let this: InterruptMap<_, _, _, _, (P::Parser, NoPanic)> = InterruptMap { + address_cells: self.address_cells, + interrupt_cells: self.interrupt_cells, + node: self.node, + encoded_map: self.encoded_map, + _collectors: self._collectors, + }; + + P::to_output( + this.iter() + .find(|e| match e { + Err(_) => true, + Ok(entry) => { + entry.child_unit_address == address && entry.child_interrupt_specifier == interrupt_specifier + } + }) + .transpose() + .map(|e| { + e.map(|e| InterruptMapEntry::<_, _, _, _, P> { + child_unit_address: e.child_unit_address, + child_interrupt_specifier: e.child_interrupt_specifier, + interrupt_parent: e.interrupt_parent.alt(), + parent_unit_address: e.parent_unit_address, + parent_interrupt_specifier: e.parent_interrupt_specifier, + }) + }), + ) + } +} + +impl< + 'a, + P: ParserWithMode<'a>, + CAddr: CellCollector, + CInt: CellCollector, + PAddr: CellCollector, + PInt: CellCollector, + > Property<'a, P> for InterruptMap<'a, CAddr, CInt, PAddr, PInt, P> +{ + fn parse(node: FallibleNode<'a, P>, _: Root<'a, (P::Parser, NoPanic)>) -> Result, FdtError> { + let Some(encoded_map) = node.properties()?.find("interrupt-map")? else { return Ok(None) }; + + let address_cells = + node.property::()?.ok_or(FdtError::MissingRequiredProperty("#address-cells"))?; + let interrupt_cells = + node.property::()?.ok_or(FdtError::MissingRequiredProperty("#interrupt-cells"))?; + + Ok(Some(InterruptMap { + address_cells, + interrupt_cells, + node: node.alt(), + encoded_map: encoded_map.value(), + _collectors: core::marker::PhantomData, + })) + } +} + +pub struct InterruptMapIter< + 'a, + CAddr: CellCollector, + CInt: CellCollector, + PAddr: CellCollector, + PInt: CellCollector, + P: ParserWithMode<'a> = (AlignedParser<'a>, Panic), +> { + address_cells: AddressCells, + interrupt_cells: InterruptCells, + node: FallibleNode<'a, P>, + encoded_map: &'a [u8], + _collectors: core::marker::PhantomData<*mut (CAddr, CInt, PAddr, PInt)>, +} + +impl< + 'a, + CAddr: CellCollector, + CInt: CellCollector, + PAddr: CellCollector, + PInt: CellCollector, + P: ParserWithMode<'a>, + > Iterator for InterruptMapIter<'a, CAddr, CInt, PAddr, PInt, P> +{ + type Item = P::Output>; + + #[track_caller] + fn next(&mut self) -> Option { + let res = crate::tryblock!({ + let child_addr_size = self.address_cells.0 * 4; + let child_intsp_size = self.interrupt_cells.0 * 4; + + let Some((child_address_iter, rest)) = self.encoded_map.split_at_checked(child_addr_size) else { + return Ok(None); + }; + let Some((child_specifier_iter, rest)) = rest.split_at_checked(child_intsp_size) else { + return Ok(None); + }; + let Some((interrupt_parent, rest)) = rest.split_at_checked(4) else { + return Ok(None); + }; + + let root = self.node.make_root::<(P::Parser, NoPanic)>()?; + let phandle = u32::from_ne_bytes(interrupt_parent.try_into().unwrap()); + let interrupt_parent = root + .resolve_phandle(PHandle(BigEndianU32::from_be(phandle)))? + .ok_or(FdtError::PHandleNotFound(phandle.swap_bytes()))?; + + let parent_address_cells = interrupt_parent + .property::()? + .ok_or(FdtError::MissingRequiredProperty("#address-cells"))?; + let parent_interrupt_cells = interrupt_parent + .property::()? + .ok_or(FdtError::MissingRequiredProperty("#interrupt-cells"))?; + + let parent_addr_size = parent_address_cells.0 * 4; + let parent_intsp_size = parent_interrupt_cells.0 * 4; + + let Some((parent_address_iter, rest)) = rest.split_at_checked(parent_addr_size) else { + return Ok(None); + }; + + let Some((parent_specifier_iter, rest)) = rest.split_at_checked(parent_intsp_size) else { + return Ok(None); + }; + self.encoded_map = rest; + + let mut child_address_collector = CAddr::Builder::default(); + for chunk in child_address_iter.chunks_exact(4) { + child_address_collector.push(u32::from_be_bytes(chunk.try_into().unwrap()))?; + } + + let mut child_specifier_collector = CInt::Builder::default(); + for chunk in child_specifier_iter.chunks_exact(4) { + child_specifier_collector.push(u32::from_be_bytes(chunk.try_into().unwrap()))?; + } + + let mut parent_address_collector = PAddr::Builder::default(); + for chunk in parent_address_iter.chunks_exact(4) { + parent_address_collector.push(u32::from_be_bytes(chunk.try_into().unwrap()))?; + } + + let mut parent_specifier_collector = PInt::Builder::default(); + for chunk in parent_specifier_iter.chunks_exact(4) { + parent_specifier_collector.push(u32::from_be_bytes(chunk.try_into().unwrap()))?; + } + + Ok(Some(InterruptMapEntry { + interrupt_parent: interrupt_parent.alt(), + child_unit_address: CAddr::map(child_address_collector.finish()), + child_interrupt_specifier: CInt::map(child_specifier_collector.finish()), + parent_unit_address: PAddr::map(parent_address_collector.finish()), + parent_interrupt_specifier: PInt::map(parent_specifier_collector.finish()), + })) + }); + + #[allow(clippy::manual_map)] + match res.transpose() { + Some(output) => Some(P::to_output(output)), + None => None, + } + } +} + +pub struct InterruptMapEntry< + 'a, + CAddr: CellCollector, + CInt: CellCollector, + PAddr: CellCollector, + PInt: CellCollector, + P: ParserWithMode<'a>, +> { + pub interrupt_parent: Node<'a, P>, + pub child_unit_address: CAddr::Output, + pub child_interrupt_specifier: CInt::Output, + pub parent_unit_address: PAddr::Output, + pub parent_interrupt_specifier: PInt::Output, +} + +impl< + 'a, + P: ParserWithMode<'a>, + CAddr: CellCollector, + CInt: CellCollector, + PAddr: CellCollector, + PInt: CellCollector, + > Clone for InterruptMapEntry<'a, CAddr, CInt, PAddr, PInt, P> +where + CAddr::Output: Clone, + CInt::Output: Clone, + PAddr::Output: Clone, + PInt::Output: Clone, +{ + fn clone(&self) -> Self { + Self { + child_unit_address: self.child_unit_address.clone(), + child_interrupt_specifier: self.child_interrupt_specifier.clone(), + interrupt_parent: self.interrupt_parent, + parent_unit_address: self.parent_unit_address.clone(), + parent_interrupt_specifier: self.parent_interrupt_specifier.clone(), + } + } +} + +impl< + 'a, + P: ParserWithMode<'a>, + CAddr: CellCollector, + CInt: CellCollector, + PAddr: CellCollector, + PInt: CellCollector, + > Copy for InterruptMapEntry<'a, CAddr, CInt, PAddr, PInt, P> +where + CAddr::Output: Copy, + CInt::Output: Copy, + PAddr::Output: Copy, + PInt::Output: Copy, +{ +} diff --git a/src/properties/interrupts/pci.rs b/src/properties/interrupts/pci.rs new file mode 100644 index 0000000..f9b4fe1 --- /dev/null +++ b/src/properties/interrupts/pci.rs @@ -0,0 +1,151 @@ +use crate::cell_collector::{BuildCellCollector, CellCollector, CollectCellsError}; + +/// [PCI Bus Binding to Open Firmware 2.2.1.1 Numerical Representation](https://www.openfirmware.info/data/docs/bus.pci.pdf) +/// +/// Numerical representation of a PCI address used within the `interrupt-map` property +#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct PciAddress { + pub hi: PciAddressHighBits, + pub mid: u32, + pub lo: u32, +} + +impl CellCollector for PciAddress { + type Builder = PciAddressCollector; + type Output = Self; + + fn map(builder_out: ::Output) -> Self::Output { + builder_out + } +} + +impl PartialEq<&'_ PciAddress> for PciAddress { + fn eq(&self, other: &&'_ Self) -> bool { + self.eq(*other) + } +} + +impl PartialEq for &'_ PciAddress { + fn eq(&self, other: &PciAddress) -> bool { + (*self).eq(other) + } +} + +/// `phys.hi cell: npt000ss bbbbbbbb dddddfff rrrrrrrr` +/// +/// where: +/// +/// `n` is 0 if the address is relocatable, 1 otherwise +/// +/// `p` is 1 if the addressable region is "prefetchable", 0 otherwise +/// +/// `t` is 1 if the address is aliased (for non-relocatable I/O), below 1 MB (for Memory), +/// +/// `or` below 64 KB (for relocatable I/O). +/// +/// `ss` is the space code, denoting the address space +/// +/// `bbbbbbbb` is the 8-bit Bus Number +/// +/// `ddddd` is the 5-bit Device Number +/// +/// `fff` is the 3-bit Function Number +/// +/// `rrrrrrrr` is the 8-bit Register Number +#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct PciAddressHighBits(u32); + +impl PciAddressHighBits { + #[inline(always)] + pub fn new(raw: u32) -> Self { + Self(raw) + } + + #[inline(always)] + pub fn register(self) -> u8 { + self.0 as u8 + } + + #[inline(always)] + pub fn function(self) -> u8 { + ((self.0 >> 8) & 0b111) as u8 + } + + #[inline(always)] + pub fn device(self) -> u8 { + ((self.0 >> 12) & 0b11111) as u8 + } + + #[inline(always)] + pub fn bus(self) -> u8 { + (self.0 >> 16) as u8 + } + + #[inline(always)] + pub fn address_space(self) -> PciAddressSpace { + const CONFIGURATION: u8 = const { PciAddressSpace::Configuration as u8 }; + const IO: u8 = const { PciAddressSpace::Io as u8 }; + const MEMORY32: u8 = const { PciAddressSpace::Memory32 as u8 }; + const MEMORY64: u8 = const { PciAddressSpace::Memory64 as u8 }; + + match ((self.0 >> 24) & 0b11) as u8 { + CONFIGURATION => PciAddressSpace::Configuration, + IO => PciAddressSpace::Io, + MEMORY32 => PciAddressSpace::Memory32, + MEMORY64 => PciAddressSpace::Memory64, + _ => unreachable!(), + } + } + + #[inline(always)] + pub fn prefetchable(self) -> bool { + (self.0 >> 30) & 0b1 == 0b1 + } + + #[inline(always)] + pub fn relocatable(self) -> bool { + (self.0 >> 31) & 0b1 == 0b0 + } +} + +impl core::ops::BitAnd for PciAddress { + type Output = Self; + fn bitand(self, rhs: Self) -> Self::Output { + Self { hi: PciAddressHighBits(self.hi.0 & rhs.hi.0), mid: self.mid & rhs.mid, lo: self.lo & rhs.lo } + } +} + +#[repr(u8)] +pub enum PciAddressSpace { + Configuration = 0b00, + Io = 0b01, + Memory32 = 0b10, + Memory64 = 0b11, +} + +#[derive(Default)] +pub struct PciAddressCollector { + address: PciAddress, + num_pushes: u32, +} + +impl BuildCellCollector for PciAddressCollector { + type Output = PciAddress; + + fn push(&mut self, component: u32) -> Result<(), CollectCellsError> { + match self.num_pushes { + 0 => self.address.hi = PciAddressHighBits(component), + 1 => self.address.mid = component, + 2 => self.address.lo = component, + _ => return Err(CollectCellsError), + } + + self.num_pushes += 1; + + Ok(()) + } + + fn finish(self) -> Self::Output { + self.address + } +} diff --git a/src/properties/ranges.rs b/src/properties/ranges.rs new file mode 100644 index 0000000..b1354ef --- /dev/null +++ b/src/properties/ranges.rs @@ -0,0 +1,97 @@ +use crate::cell_collector::{BuildCellCollector, CellCollector, CollectCellsError}; + +use super::cells::{AddressCells, CellSizes}; + +#[derive(Debug, Clone, Copy)] +pub struct Ranges<'a> { + parent_address_cells: AddressCells, + cell_sizes: CellSizes, + ranges: &'a [u8], +} + +impl<'a> Ranges<'a> { + pub fn iter(self) -> RangesIter<'a, CAddr, PAddr, Len> + where + CAddr: CellCollector, + PAddr: CellCollector, + Len: CellCollector, + { + RangesIter { + parent_address_cells: self.parent_address_cells, + cell_sizes: self.cell_sizes, + ranges: self.ranges, + _collectors: core::marker::PhantomData, + } + } +} + +pub struct RangesIter<'a, CAddr: CellCollector = u64, PAddr: CellCollector = u64, Len: CellCollector = u64> { + parent_address_cells: AddressCells, + cell_sizes: CellSizes, + ranges: &'a [u8], + _collectors: core::marker::PhantomData<*mut (CAddr, PAddr, Len)>, +} + +impl<'a, CAddr: CellCollector, PAddr: CellCollector, Len: CellCollector> Iterator + for RangesIter<'a, CAddr, PAddr, Len> +{ + type Item = Result, CollectCellsError>; + fn next(&mut self) -> Option { + let child_address_bytes = self.cell_sizes.address_cells * 4; + let parent_address_bytes = self.parent_address_cells.0 * 4; + let len_bytes = self.cell_sizes.size_cells * 4; + + let child_encoded_address = self.ranges.get(..child_address_bytes)?; + let parent_encoded_address = + self.ranges.get(child_address_bytes..child_address_bytes + parent_address_bytes)?; + let encoded_len = self + .ranges + .get(child_address_bytes + parent_address_bytes..child_address_bytes + parent_address_bytes + len_bytes)?; + + let mut child_address_collector = ::Builder::default(); + for encoded_address in child_encoded_address.chunks_exact(4) { + // TODO: replace this stuff with `array_chunks` when its stabilized + // + // These unwraps can't panic because `chunks_exact` guarantees that + // we'll always get slices of 4 bytes + if let Err(e) = child_address_collector.push(u32::from_be_bytes(encoded_address.try_into().unwrap())) { + return Some(Err(e)); + } + } + + let mut parent_address_collector = ::Builder::default(); + for encoded_address in parent_encoded_address.chunks_exact(4) { + // TODO: replace this stuff with `array_chunks` when its stabilized + // + // These unwraps can't panic because `chunks_exact` guarantees that + // we'll always get slices of 4 bytes + if let Err(e) = parent_address_collector.push(u32::from_be_bytes(encoded_address.try_into().unwrap())) { + return Some(Err(e)); + } + } + + let mut len_collector = ::Builder::default(); + for encoded_len in encoded_len.chunks_exact(4) { + // TODO: replace this stuff with `array_chunks` when its stabilized + // + // These unwraps can't panic because `chunks_exact` guarantees that + // we'll always get slices of 4 bytes + if let Err(e) = len_collector.push(u32::from_be_bytes(encoded_len.try_into().unwrap())) { + return Some(Err(e)); + } + } + + self.ranges = self.ranges.get(child_address_bytes + parent_address_bytes + len_bytes..)?; + Some(Ok(Range { + child_bus_address: CAddr::map(child_address_collector.finish()), + parent_bus_address: PAddr::map(parent_address_collector.finish()), + len: Len::map(len_collector.finish()), + })) + } +} + +pub struct Range { + pub child_bus_address: CAddr, + pub parent_bus_address: PAddr, + pub len: Len, +} diff --git a/src/properties/reg.rs b/src/properties/reg.rs new file mode 100644 index 0000000..43401b9 --- /dev/null +++ b/src/properties/reg.rs @@ -0,0 +1,191 @@ +use crate::{ + cell_collector::{BuildCellCollector, CellCollector, CollectCellsError}, + nodes::FallibleNode, + parsing::{NoPanic, ParserWithMode}, + standard_nodes::Root, + FdtError, +}; + +use super::{cells::CellSizes, Property}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Reg<'a> { + cell_sizes: CellSizes, + encoded_array: &'a [u8], +} + +impl<'a> Reg<'a> { + pub fn cell_sizes(self) -> CellSizes { + self.cell_sizes + } + + pub fn iter_raw(self) -> RegRawIter<'a> { + RegRawIter { cell_sizes: self.cell_sizes, encoded_array: self.encoded_array } + } + + pub fn iter(self) -> RegIter<'a, Addr, Len> { + RegIter { + cell_sizes: self.cell_sizes, + encoded_array: self.encoded_array, + _collector: core::marker::PhantomData, + } + } +} + +impl<'a, P: ParserWithMode<'a>> Property<'a, P> for Reg<'a> { + fn parse(node: FallibleNode<'a, P>, _: Root<'a, (P::Parser, NoPanic)>) -> Result, FdtError> { + let Some(prop) = node.raw_property("reg")? else { + return Ok(None); + }; + + let cell_sizes = match node.parent() { + Some(parent) => parent.property::()?.unwrap_or_default(), + None => CellSizes::default(), + }; + + let encoded_array = prop.value(); + + if encoded_array.len() % (cell_sizes.address_cells * 4 + cell_sizes.size_cells * 4) != 0 { + return Err(FdtError::InvalidPropertyValue); + } + + Ok(Some(Self { cell_sizes, encoded_array })) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct RegEntry { + pub address: Addr, + pub len: Len, +} + +pub struct RegIter<'a, CAddr: CellCollector, Len: CellCollector> { + cell_sizes: CellSizes, + encoded_array: &'a [u8], + _collector: core::marker::PhantomData<*mut (CAddr, Len)>, +} + +impl<'a, CAddr: CellCollector, Len: CellCollector> Iterator for RegIter<'a, CAddr, Len> { + type Item = Result, CollectCellsError>; + fn next(&mut self) -> Option { + let address_bytes = self.cell_sizes.address_cells * 4; + let size_bytes = self.cell_sizes.size_cells * 4; + + let encoded_address = self.encoded_array.get(..address_bytes)?; + let encoded_len = self.encoded_array.get(address_bytes..address_bytes + size_bytes)?; + + let mut address_collector = ::Builder::default(); + for encoded_address in encoded_address.chunks_exact(4) { + // TODO: replace this stuff with `array_chunks` when its stabilized + // + // These unwraps can't panic because `chunks_exact` guarantees that + // we'll always get slices of 4 bytes + if let Err(e) = address_collector.push(u32::from_be_bytes(encoded_address.try_into().unwrap())) { + return Some(Err(e)); + } + } + + let mut len_collector = ::Builder::default(); + for encoded_len in encoded_len.chunks_exact(4) { + // TODO: replace this stuff with `array_chunks` when its stabilized + // + // These unwraps can't panic because `chunks_exact` guarantees that + // we'll always get slices of 4 bytes + if let Err(e) = len_collector.push(u32::from_be_bytes(encoded_len.try_into().unwrap())) { + return Some(Err(e)); + } + } + + self.encoded_array = self.encoded_array.get((address_bytes + size_bytes)..)?; + Some(Ok(RegEntry { address: CAddr::map(address_collector.finish()), len: Len::map(len_collector.finish()) })) + } +} + +pub struct RegRawIter<'a> { + cell_sizes: CellSizes, + encoded_array: &'a [u8], +} + +impl<'a> Iterator for RegRawIter<'a> { + type Item = RawRegEntry<'a>; + fn next(&mut self) -> Option { + let address_bytes = self.cell_sizes.address_cells * 4; + let size_bytes = self.cell_sizes.size_cells * 4; + + let (addr_len, rest) = self.encoded_array.split_at_checked(address_bytes + size_bytes)?; + let (address, len) = addr_len.split_at(address_bytes); + + self.encoded_array = rest; + Some(RawRegEntry { address, len }) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct RawRegEntry<'a> { + address: &'a [u8], + len: &'a [u8], +} + +impl<'a> RawRegEntry<'a> { + pub fn address(self) -> &'a [u8] { + self.address + } + + pub fn len(self) -> &'a [u8] { + self.len + } +} + +/// [Devicetree 2.3.7. +/// `virtual-reg`](https://devicetree-specification.readthedocs.io/en/latest/chapter2-devicetree-basics.html#virtual-reg) +/// +/// The `virtual-reg` property specifies an effective address that maps to the +/// first physical address specified in the `reg` property of the device node. +/// This property enables boot programs to provide client programs with +/// virtual-to-physical mappings that have been set up. +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct VirtualReg(u32); + +impl VirtualReg { + pub fn into_u32(self) -> u32 { + self.0 + } +} + +impl<'a, P: ParserWithMode<'a>> Property<'a, P> for VirtualReg { + fn parse(node: FallibleNode<'a, P>, _: Root<'a, (P::Parser, NoPanic)>) -> Result, FdtError> { + match node.properties()?.find("virtual-reg")? { + Some(vreg) => Ok(Some(Self(vreg.as_value()?))), + None => Ok(None), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn reg_raw_iter() { + let mut iter = RegRawIter { + cell_sizes: CellSizes { address_cells: 2, size_cells: 1 }, + encoded_array: &[0x55, 0x44, 0x33, 0x22, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD], + }; + + assert_eq!( + iter.next().unwrap(), + RawRegEntry { address: &[0x55, 0x44, 0x33, 0x22, 0x66, 0x77, 0x88, 0x99], len: &[0xAA, 0xBB, 0xCC, 0xDD] } + ); + } + + #[test] + fn reg_u64_iter() { + let mut iter = RegIter:: { + cell_sizes: CellSizes { address_cells: 2, size_cells: 1 }, + encoded_array: &[0x55, 0x44, 0x33, 0x22, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD], + _collector: core::marker::PhantomData, + }; + + assert_eq!(iter.next().unwrap().unwrap(), RegEntry { address: 0x5544332266778899, len: 0xAABBCCDD }); + } +} diff --git a/src/properties/values.rs b/src/properties/values.rs new file mode 100644 index 0000000..f5e7237 --- /dev/null +++ b/src/properties/values.rs @@ -0,0 +1,131 @@ +use crate::{parsing::BigEndianU32, FdtError}; +use core::ffi::CStr; + +#[derive(Debug, Clone, Copy)] +pub struct InvalidPropertyValue; + +impl From for FdtError { + fn from(_: InvalidPropertyValue) -> Self { + FdtError::InvalidPropertyValue + } +} + +pub trait PropertyValue<'a>: Sized { + fn parse(value: &'a [u8]) -> Result; +} + +impl<'a> PropertyValue<'a> for u32 { + #[inline] + fn parse(value: &'a [u8]) -> Result { + match value { + [a, b, c, d] => Ok(u32::from_be_bytes([*a, *b, *c, *d])), + _ => Err(InvalidPropertyValue), + } + } +} + +impl<'a> PropertyValue<'a> for u64 { + #[inline] + fn parse(value: &'a [u8]) -> Result { + match value { + [a, b, c, d] => Ok(u64::from_be_bytes([0, 0, 0, 0, *a, *b, *c, *d])), + [a, b, c, d, e, f, g, h] => Ok(u64::from_be_bytes([*a, *b, *c, *d, *e, *f, *g, *h])), + _ => Err(InvalidPropertyValue), + } + } +} + +impl<'a> PropertyValue<'a> for usize { + #[inline] + fn parse(value: &'a [u8]) -> Result { + #[cfg(target_pointer_width = "32")] + let ret = match value { + [a, b, c, d] => Ok(usize::from_be_bytes([*a, *b, *c, *d])), + _ => Err(InvalidPropertyValue), + }; + + #[cfg(target_pointer_width = "64")] + let ret = match value { + [a, b, c, d] => Ok(usize::from_be_bytes([0, 0, 0, 0, *a, *b, *c, *d])), + [a, b, c, d, e, f, g, h] => Ok(usize::from_be_bytes([*a, *b, *c, *d, *e, *f, *g, *h])), + _ => Err(InvalidPropertyValue), + }; + + ret + } +} + +impl<'a> PropertyValue<'a> for BigEndianU32 { + #[inline] + fn parse(value: &'a [u8]) -> Result { + match value { + [a, b, c, d] => Ok(BigEndianU32::from_be(u32::from_ne_bytes([*a, *b, *c, *d]))), + _ => Err(InvalidPropertyValue), + } + } +} + +impl<'a> PropertyValue<'a> for &'a CStr { + #[inline] + fn parse(value: &'a [u8]) -> Result { + CStr::from_bytes_until_nul(value).map_err(|_| InvalidPropertyValue) + } +} + +impl<'a> PropertyValue<'a> for &'a str { + #[inline] + fn parse(value: &'a [u8]) -> Result { + core::str::from_utf8(value).map(|s| s.trim_end_matches('\0')).map_err(|_| InvalidPropertyValue) + } +} + +#[derive(Debug, Clone, Copy)] +pub struct U32List<'a>(&'a [u8]); + +impl<'a> U32List<'a> { + pub fn iter(self) -> U32ListIter<'a> { + U32ListIter(self.0) + } +} + +impl<'a> PropertyValue<'a> for U32List<'a> { + fn parse(value: &'a [u8]) -> Result { + if value.len() % 4 != 0 { + return Err(InvalidPropertyValue); + } + + Ok(Self(value)) + } +} + +pub struct U32ListIter<'a>(&'a [u8]); + +impl<'a> Iterator for U32ListIter<'a> { + type Item = u32; + fn next(&mut self) -> Option { + let val = u32::from_be_bytes(self.0.get(..4)?.try_into().unwrap()); + self.0 = self.0.get(4..)?; + Some(val) + } +} + +#[derive(Debug, Clone)] +pub struct StringList<'a> { + strs: core::str::Split<'a, char>, +} + +impl<'a> PropertyValue<'a> for StringList<'a> { + #[inline] + fn parse(value: &'a [u8]) -> Result { + Ok(Self { strs: <&'a str as PropertyValue<'a>>::parse(value)?.split('\0') }) + } +} + +impl<'a> Iterator for StringList<'a> { + type Item = &'a str; + + #[inline(always)] + fn next(&mut self) -> Option { + self.strs.next() + } +} diff --git a/src/standard_nodes.rs b/src/standard_nodes.rs index 5bcf273..9cd0be4 100644 --- a/src/standard_nodes.rs +++ b/src/standard_nodes.rs @@ -3,10 +3,12 @@ // obtain one at https://mozilla.org/MPL/2.0/. use crate::{ + cell_collector::{BuildCellCollector, CellCollector, CollectCellsError}, nodes::{FallibleNode, IntoSearchableNodeName, Node, RawNode, SearchableNodeName}, parsing::{aligned::AlignedParser, BigEndianToken, NoPanic, Panic, ParseError, Parser, ParserWithMode}, properties::{ - AddressCells, BuildCellCollector, CellCollector, CellSizes, CollectCellsError, Compatible, PHandle, Property, + cells::{AddressCells, CellSizes}, + Compatible, PHandle, Property, }, tryblock, FdtError, }; @@ -424,7 +426,7 @@ impl<'a, 'b, P: ParserWithMode<'a>> Iterator for AllNodesWithNameIter<'a, 'b, P> } } -/// Iterator created by [`Root::all_compatible`] +/// See [`Root::all_compatible`] pub struct AllCompatibleIter<'a, 'b, P: ParserWithMode<'a>> { iter: core::iter::FilterMap< AllNodesIter<'a, (P::Parser, NoPanic)>, diff --git a/src/tests.rs b/src/tests.rs index 9fa23c7..3d98458 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -5,7 +5,13 @@ extern crate std; use nodes::NodeName; -use properties::{CellSizes, InterruptMap, PciAddress, PciAddressHighBits}; +use properties::{ + cells::CellSizes, + interrupts::{ + pci::{PciAddress, PciAddressHighBits}, + InterruptMap, + }, +}; // use crate::{node::RawReg, *}; use crate::*; From 89572a2fbf0959ad24e5394b21771aed54f79c84 Mon Sep 17 00:00:00 2001 From: repnop Date: Tue, 30 Jul 2024 21:59:05 -0400 Subject: [PATCH 23/38] hellmo --- src/properties/values.rs | 6 + src/standard_nodes.rs | 291 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 292 insertions(+), 5 deletions(-) diff --git a/src/properties/values.rs b/src/properties/values.rs index f5e7237..447ccc5 100644 --- a/src/properties/values.rs +++ b/src/properties/values.rs @@ -129,3 +129,9 @@ impl<'a> Iterator for StringList<'a> { self.strs.next() } } + +impl<'a> From<&'a str> for StringList<'a> { + fn from(value: &'a str) -> Self { + Self { strs: value.split('\0') } + } +} diff --git a/src/standard_nodes.rs b/src/standard_nodes.rs index 9cd0be4..4652123 100644 --- a/src/standard_nodes.rs +++ b/src/standard_nodes.rs @@ -8,6 +8,7 @@ use crate::{ parsing::{aligned::AlignedParser, BigEndianToken, NoPanic, Panic, ParseError, Parser, ParserWithMode}, properties::{ cells::{AddressCells, CellSizes}, + values::StringList, Compatible, PHandle, Property, }, tryblock, FdtError, @@ -516,21 +517,52 @@ impl<'a, P: ParserWithMode<'a>> Iterator for AllNodesIter<'a, P> { } } -/// Represents the `/aliases` node with specific helper methods +/// [Devicetree 3.3. `/aliases` +/// node](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#aliases-node) +/// +/// A devicetree may have an aliases node (`/aliases`) that defines one or more +/// alias properties. The alias node shall be at the root of the devicetree and +/// have the node name `/aliases`. +/// +/// Each property of the `/aliases` node defines an alias. The property name +/// specifies the alias name. The property value specifies the full path to a +/// node in the devicetree. For example, the property `serial0 = +/// "/simple-bus@fe000000/serial@llc500"` defines the alias `serial0`. +/// +/// An alias value is a device path and is encoded as a string. The value +/// represents the full path to a node, but the path does not need to refer to a +/// leaf node. +/// +/// A client program may use an alias property name to refer to a full device +/// path as all or part of its string value. A client program, when considering +/// a string as a device path, shall detect and use the alias. +/// +/// ### Example +/// +/// ```norust +/// aliases { +/// serial0 = "/simple-bus@fe000000/serial@llc500"; +/// ethernet0 = "/simple-bus@fe000000/ethernet@31c000"; +/// }; +/// ``` +/// +/// Given the alias `serial0`, a client program can look at the `/aliases` node +/// and determine the alias refers to the device path +/// `/simple-bus@fe000000/serial@llc500`. #[derive(Debug, Clone, Copy)] pub struct Aliases<'a, P: ParserWithMode<'a>> { pub(crate) node: FallibleNode<'a, P>, } impl<'a, P: ParserWithMode<'a>> Aliases<'a, P> { - /// Attempt to resolve an alias to a node name + /// Attempt to resolve an alias to a node name. pub fn resolve_name(self, alias: &str) -> P::Output> { P::to_output(crate::tryblock!({ self.node.properties()?.find(alias)?.map(|p| p.as_value().map_err(Into::into)).transpose() })) } - /// Attempt to find the node specified by the given alias + /// Attempt resolve an alias to the aliased-to node. pub fn resolve(self, alias: &str) -> P::Output>> { P::to_output(crate::tryblock!({ let Some(path) = Aliases::<(_, NoPanic)> { node: self.node }.resolve_name(alias)? else { @@ -542,10 +574,83 @@ impl<'a, P: ParserWithMode<'a>> Aliases<'a, P> { } } -/// Represents a `/cpus/cpu*` node with specific helper methods +/// [Devicetree 3.7. +/// `/cpus`](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#cpus-node-properties) +/// +/// A `/cpus` node is required for all devicetrees. It does not represent a real +/// device in the system, but acts as a container for child cpu nodes which +/// represent the systems CPUs. +pub struct Cpus<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { + pub(crate) node: FallibleNode<'a, P>, +} + +impl<'a, P: ParserWithMode<'a>> Cpus<'a, P> { + /// Retrieve the `#address-cells` and `#size-cells` values from this node + pub fn cell_sizes(&self) -> P::Output { + P::to_output( + self.node.property().and_then(|p| p.ok_or(FdtError::MissingRequiredProperty("#address-cells/#size-cells"))), + ) + } + + /// Attempt to find a common `timebase-frequency` property inside of this + /// node, which will only exist if there is a common value between the child + /// `cpu` nodes. See [`Cpu::timebase_frequency`] for documentation about the + /// `timebase-frequency` property. + pub fn common_timebase_frequency(&self) -> P::Output> { + P::to_output(crate::tryblock!({ + match self.node.properties()?.find("timebase-frequency")? { + Some(prop) => match prop.value().len() { + 4 => Ok(Some(u64::from(prop.as_value::()?))), + 8 => Ok(Some(prop.as_value::()?)), + _ => Err(FdtError::InvalidPropertyValue), + }, + None => Ok(None), + } + })) + } + + /// Attempt to find a common `clock-frequency` property inside of this + /// node, which will only exist if there is a common value between the child + /// `cpu` nodes. See [`Cpu::clock_frequency`] for documentation about the + /// `clock-frequency` property. + pub fn common_clock_frequency(&self) -> P::Output> { + P::to_output(crate::tryblock!({ + match self.node.properties()?.find("clock-frequency")? { + Some(prop) => match prop.value().len() { + 4 => Ok(Some(u64::from(prop.as_value::()?))), + 8 => Ok(Some(prop.as_value::()?)), + _ => Err(FdtError::InvalidPropertyValue), + }, + None => Ok(None), + } + })) + } +} + +/// [Devicetree 3.8. +/// `/cpus/cpu*`](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#cpus-cpu-node-properties) +/// +/// A `cpu` node represents a hardware execution block that is sufficiently +/// independent that it is capable of running an operating system without +/// interfering with other CPUs possibly running other operating systems. +/// +/// Hardware threads that share an MMU would generally be represented under one +/// `cpu` node. If other more complex CPU topographies are designed, the binding +/// for the CPU must describe the topography (e.g. threads that don’t share an +/// MMU). +/// +/// CPUs and threads are numbered through a unified number-space that should +/// match as closely as possible the interrupt controller’s numbering of +/// CPUs/threads. +/// +/// Properties that have identical values across `cpu` nodes may be placed in the +/// /cpus node instead. A client program must first examine a specific `cpu` node, +/// but if an expected property is not found then it should look at the parent +/// /cpus node. This results in a less verbose representation of properties +/// which are identical across all CPUs. #[derive(Debug, Clone, Copy)] pub struct Cpu<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { - node: FallibleNode<'a, P>, + pub(crate) node: FallibleNode<'a, P>, } impl<'a, P: ParserWithMode<'a>> Cpu<'a, P> { @@ -668,6 +773,182 @@ impl<'a, P: ParserWithMode<'a>> Cpu<'a, P> { } })) } + + /// [Devicetree 3.8.1 + /// `status`](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#general-properties-of-cpus-cpu-nodes) + /// + /// A standard property describing the state of a CPU. This property shall + /// be present for nodes representing CPUs in a symmetric multiprocessing + /// (SMP) configuration. For a CPU node the meaning of the `"okay"`, + /// `"disabled"` and `"fail"` values are as follows: + /// + /// `"okay"`: The CPU is running. + /// + /// `"disabled"`: The CPU is in a quiescent state. + /// + /// `"fail"`: The CPU is not operational or does not exist. + /// + /// A quiescent CPU is in a state where it cannot interfere with the normal + /// operation of other CPUs, nor can its state be affected by the normal + /// operation of other running CPUs, except by an explicit method for + /// enabling or re-enabling the quiescent CPU (see the enable-method + /// property). + /// + /// In particular, a running CPU shall be able to issue broadcast TLB + /// invalidates without affecting a quiescent CPU. + /// + /// Examples: A quiescent CPU could be in a spin loop, held in reset, and + /// electrically isolated from the system bus or in another implementation + /// dependent state. + /// + /// A CPU with `"fail"` status does not affect the system in any way. The + /// status is assigned to nodes for which no corresponding CPU exists. + pub fn status(&self) -> P::Output> { + P::to_output(crate::tryblock!({ + let Some(status) = self.node.properties()?.find("status")? else { + return Ok(None); + }; + + Ok(Some(CpuStatus(status.as_value()?))) + })) + } + + /// [Devicetree 3.8.1 + /// `enable-method`](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#general-properties-of-cpus-cpu-nodes) + /// + /// Describes the method by which a CPU in a disabled state is enabled. This + /// property is required for CPUs with a status property with a value of + /// `"disabled"`. The value consists of one or more strings that define the + /// method to release this CPU. If a client program recognizes any of the + /// methods, it may use it. The value shall be one of the following: + /// + /// `"spin-table"`: The CPU is enabled with the spin table method defined in + /// the DTSpec. + /// + /// `"[vendor],[method]"`: Implementation dependent string that describes + /// the method by which a CPU is released from a `"disabled"` state. The + /// required format is: `"[vendor],[method]"`, where vendor is a string + /// describing the name of the manufacturer and method is a string + /// describing the vendor specific mechanism. + /// + /// Example: `"fsl,MPC8572DS"` + pub fn enable_method(&self) -> P::Output> { + P::to_output(crate::tryblock!({ + let Some(status) = self.node.properties()?.find("enable-method")? else { + return Ok(None); + }; + + let s: &'a str = status.as_value()?; + + if s.is_empty() { + return Err(FdtError::InvalidPropertyValue); + } + + Ok(Some(CpuEnableMethods(s.into()))) + })) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[repr(transparent)] +pub struct CpuStatus<'a>(&'a str); + +impl<'a> CpuStatus<'a> { + /// The CPU is running. + pub const OKAY: Self = Self("okay"); + /// The CPU is in a quiescent state. + pub const DISABLED: Self = Self("disabled"); + /// The CPU is not operational or does not exist. + pub const FAIL: Self = Self("fail"); + + /// Create a new [`CpuStatus`] which may not be one of the associated + /// constant values. + pub fn new(status: &'a str) -> Self { + Self(status) + } + + /// Whether the status is `"okay"`. + pub fn is_okay(self) -> bool { + self == Self::OKAY + } + + /// Whether the status is `"disabled"`. + pub fn is_disabled(self) -> bool { + self == Self::DISABLED + } + + /// Whether the status is `"failed"` + pub fn is_failed(self) -> bool { + self == Self::FAIL + } +} + +impl<'a> PartialEq for CpuStatus<'a> { + fn eq(&self, other: &str) -> bool { + self.0 == other + } +} + +/// Type representing one or more CPU enable methods. See +/// [`Cpu::enable_method`]. +#[derive(Debug, Clone)] +pub struct CpuEnableMethods<'a>(StringList<'a>); + +impl<'a> CpuEnableMethods<'a> { + /// Create an iterator over the enable methods. + pub fn iter(&self) -> CpuEnableMethodsIter<'a> { + CpuEnableMethodsIter(self.0.clone()) + } + + /// Return the first enable method contained in the list of enable methods. + pub fn first(&self) -> CpuEnableMethod<'a> { + self.iter().next().unwrap() + } +} + +impl<'a> IntoIterator for CpuEnableMethods<'a> { + type IntoIter = CpuEnableMethodsIter<'a>; + type Item = CpuEnableMethod<'a>; + + fn into_iter(self) -> Self::IntoIter { + CpuEnableMethodsIter(self.0) + } +} + +/// Iterator over the enable methods described by the `enable-method` property +/// on a CPU node. See [`Cpu::enable_method`]. +pub struct CpuEnableMethodsIter<'a>(StringList<'a>); + +impl<'a> Iterator for CpuEnableMethodsIter<'a> { + type Item = CpuEnableMethod<'a>; + + fn next(&mut self) -> Option { + match self.0.next()? { + "spin-table" => Some(CpuEnableMethod::SpinTable), + other => { + let (vendor, method) = other.split_once(',').unwrap_or((other, "")); + Some(CpuEnableMethod::VendorMethod { vendor, method }) + } + } + } +} + +/// An enable method contained by the [`Cpu::enable_method`] +pub enum CpuEnableMethod<'a> { + /// The CPU is enabled with the spin table method defined in the DTSpec. + SpinTable, + /// Implementation dependent string that describes the method by which a CPU + /// is released from a `"disabled"` state. + VendorMethod { + /// The manufacturer. + vendor: &'a str, + /// The vendor specific mechanism. + /// + /// NOTE: If the string value of this enable method does not match the + /// `"[vendor],[method]"` format defined by the devicetree spec, this + /// will be an empty string. + method: &'a str, + }, } /// See [`Cpu::reg`] From fc00b245bb850af75e9329963994aca93c042cfa Mon Sep 17 00:00:00 2001 From: repnop Date: Sun, 4 Aug 2024 20:50:51 -0400 Subject: [PATCH 24/38] guh --- src/nodes.rs | 4 + src/nodes/aliases.rs | 59 +++++ src/nodes/cpus.rs | 461 ++++++++++++++++++++++++++++++++++ src/nodes/memory.rs | 134 ++++++++++ src/standard_nodes.rs | 567 ------------------------------------------ 5 files changed, 658 insertions(+), 567 deletions(-) create mode 100644 src/nodes/aliases.rs create mode 100644 src/nodes/cpus.rs create mode 100644 src/nodes/memory.rs diff --git a/src/nodes.rs b/src/nodes.rs index 5dbe409..7687f96 100644 --- a/src/nodes.rs +++ b/src/nodes.rs @@ -1,3 +1,7 @@ +pub mod aliases; +pub mod cpus; +pub mod memory; + use crate::{ parsing::{ aligned::AlignedParser, BigEndianToken, NoPanic, Panic, PanicMode, ParseError, Parser, ParserWithMode, diff --git a/src/nodes/aliases.rs b/src/nodes/aliases.rs new file mode 100644 index 0000000..2506454 --- /dev/null +++ b/src/nodes/aliases.rs @@ -0,0 +1,59 @@ +use super::{FallibleNode, Node}; +use crate::parsing::{NoPanic, ParserWithMode}; + +/// [Devicetree 3.3. `/aliases` +/// node](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#aliases-node) +/// +/// A devicetree may have an aliases node (`/aliases`) that defines one or more +/// alias properties. The alias node shall be at the root of the devicetree and +/// have the node name `/aliases`. +/// +/// Each property of the `/aliases` node defines an alias. The property name +/// specifies the alias name. The property value specifies the full path to a +/// node in the devicetree. For example, the property `serial0 = +/// "/simple-bus@fe000000/serial@llc500"` defines the alias `serial0`. +/// +/// An alias value is a device path and is encoded as a string. The value +/// represents the full path to a node, but the path does not need to refer to a +/// leaf node. +/// +/// A client program may use an alias property name to refer to a full device +/// path as all or part of its string value. A client program, when considering +/// a string as a device path, shall detect and use the alias. +/// +/// ### Example +/// +/// ```norust +/// aliases { +/// serial0 = "/simple-bus@fe000000/serial@llc500"; +/// ethernet0 = "/simple-bus@fe000000/ethernet@31c000"; +/// }; +/// ``` +/// +/// Given the alias `serial0`, a client program can look at the `/aliases` node +/// and determine the alias refers to the device path +/// `/simple-bus@fe000000/serial@llc500`. +#[derive(Debug, Clone, Copy)] +pub struct Aliases<'a, P: ParserWithMode<'a>> { + pub(crate) node: FallibleNode<'a, P>, +} + +impl<'a, P: ParserWithMode<'a>> Aliases<'a, P> { + /// Attempt to resolve an alias to a node name. + pub fn resolve_name(self, alias: &str) -> P::Output> { + P::to_output(crate::tryblock!({ + self.node.properties()?.find(alias)?.map(|p| p.as_value().map_err(Into::into)).transpose() + })) + } + + /// Attempt resolve an alias to the aliased-to node. + pub fn resolve(self, alias: &str) -> P::Output>> { + P::to_output(crate::tryblock!({ + let Some(path) = Aliases::<(_, NoPanic)> { node: self.node }.resolve_name(alias)? else { + return Ok(None); + }; + + self.node.make_root::()?.find_node(path).map(|r| r.map(|n| n.alt())) + })) + } +} diff --git a/src/nodes/cpus.rs b/src/nodes/cpus.rs new file mode 100644 index 0000000..78cee37 --- /dev/null +++ b/src/nodes/cpus.rs @@ -0,0 +1,461 @@ +use crate::{ + cell_collector::{BuildCellCollector, CellCollector, CollectCellsError}, + parsing::{aligned::AlignedParser, Panic, ParserWithMode}, + properties::{ + cells::{AddressCells, CellSizes}, + values::StringList, + }, + FdtError, +}; + +use super::FallibleNode; + +/// [Devicetree 3.7. +/// `/cpus`](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#cpus-node-properties) +/// +/// A `/cpus` node is required for all devicetrees. It does not represent a real +/// device in the system, but acts as a container for child cpu nodes which +/// represent the systems CPUs. +pub struct Cpus<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { + pub(crate) node: FallibleNode<'a, P>, +} + +impl<'a, P: ParserWithMode<'a>> Cpus<'a, P> { + /// Retrieve the `#address-cells` and `#size-cells` values from this node + pub fn cell_sizes(&self) -> P::Output { + P::to_output( + self.node.property().and_then(|p| p.ok_or(FdtError::MissingRequiredProperty("#address-cells/#size-cells"))), + ) + } + + /// Attempt to find a common `timebase-frequency` property inside of this + /// node, which will only exist if there is a common value between the child + /// `cpu` nodes. See [`Cpu::timebase_frequency`] for documentation about the + /// `timebase-frequency` property. + pub fn common_timebase_frequency(&self) -> P::Output> { + P::to_output(crate::tryblock!({ + match self.node.properties()?.find("timebase-frequency")? { + Some(prop) => match prop.value().len() { + 4 => Ok(Some(u64::from(prop.as_value::()?))), + 8 => Ok(Some(prop.as_value::()?)), + _ => Err(FdtError::InvalidPropertyValue), + }, + None => Ok(None), + } + })) + } + + /// Attempt to find a common `clock-frequency` property inside of this + /// node, which will only exist if there is a common value between the child + /// `cpu` nodes. See [`Cpu::clock_frequency`] for documentation about the + /// `clock-frequency` property. + pub fn common_clock_frequency(&self) -> P::Output> { + P::to_output(crate::tryblock!({ + match self.node.properties()?.find("clock-frequency")? { + Some(prop) => match prop.value().len() { + 4 => Ok(Some(u64::from(prop.as_value::()?))), + 8 => Ok(Some(prop.as_value::()?)), + _ => Err(FdtError::InvalidPropertyValue), + }, + None => Ok(None), + } + })) + } +} + +/// [Devicetree 3.8. +/// `/cpus/cpu*`](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#cpus-cpu-node-properties) +/// +/// A `cpu` node represents a hardware execution block that is sufficiently +/// independent that it is capable of running an operating system without +/// interfering with other CPUs possibly running other operating systems. +/// +/// Hardware threads that share an MMU would generally be represented under one +/// `cpu` node. If other more complex CPU topographies are designed, the binding +/// for the CPU must describe the topography (e.g. threads that don’t share an +/// MMU). +/// +/// CPUs and threads are numbered through a unified number-space that should +/// match as closely as possible the interrupt controller’s numbering of +/// CPUs/threads. +/// +/// Properties that have identical values across `cpu` nodes may be placed in the +/// /cpus node instead. A client program must first examine a specific `cpu` node, +/// but if an expected property is not found then it should look at the parent +/// /cpus node. This results in a less verbose representation of properties +/// which are identical across all CPUs. +#[derive(Debug, Clone, Copy)] +pub struct Cpu<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { + pub(crate) node: FallibleNode<'a, P>, +} + +impl<'a, P: ParserWithMode<'a>> Cpu<'a, P> { + /// [Devicetree 3.8.1 + /// `reg`](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#general-properties-of-cpus-cpu-nodes) + /// + /// **Required** + /// + /// The value of `reg` is a `` that defines a unique + /// CPU/thread id for the CPU/threads represented by the CPU node. + /// + /// If a CPU supports more than one thread (i.e. multiple streams of + /// execution) the `reg` property is an array with 1 element per thread. The + /// `#address-cells` on the `/cpus` node specifies how many cells each + /// element of the array takes. Software can determine the number of threads + /// by dividing the size of `reg` by the parent node’s `#address-cells`. + /// + /// If a CPU/thread can be the target of an external interrupt the `reg` + /// property value must be a unique CPU/thread id that is addressable by the + /// interrupt controller. + /// + /// If a CPU/thread cannot be the target of an external interrupt, then + /// `reg` must be unique and out of bounds of the range addressed by the + /// interrupt controller + /// + /// If a CPU/thread’s PIR (pending interrupt register) is modifiable, a + /// client program should modify PIR to match the `reg` property value. If + /// PIR cannot be modified and the PIR value is distinct from the interrupt + /// controller number space, the CPUs binding may define a binding-specific + /// representation of PIR values if desired. + pub fn reg(self) -> P::Output> { + P::to_output(crate::tryblock!({ + let Some(reg) = self.node.properties()?.find("reg")? else { + return Err(FdtError::MissingRequiredProperty("reg")); + }; + + if reg.value().is_empty() { + return Err(FdtError::InvalidPropertyValue); + } + + let Some(address_cells) = self.node.parent().unwrap().property::()? else { + return Err(FdtError::MissingRequiredProperty("#address-cells")); + }; + + Ok(CpuIds { reg: reg.value(), address_cells: address_cells.0, _collector: core::marker::PhantomData }) + })) + } + + /// [Devicetree 3.8.1 + /// `clock-frequency`](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#general-properties-of-cpus-cpu-nodes) + /// + /// **Required** + /// + /// Specifies the current clock speed of the CPU in Hertz. The value is a + /// `` in one of two forms: + /// + /// * A 32-bit integer consisting of one `` specifying the frequency. + /// * A 64-bit integer represented as a `` specifying the frequency. + pub fn clock_frequency(self) -> P::Output { + P::to_output(crate::tryblock!({ + match self.node.properties()?.find("clock-frequency")? { + Some(prop) => match prop.value().len() { + 4 => Ok(u64::from(prop.as_value::()?)), + 8 => Ok(prop.as_value::()?), + _ => Err(FdtError::InvalidPropertyValue), + }, + None => { + let prop = self + .node + .parent() + .unwrap() + .properties()? + .find("clock-frequency")? + .ok_or(FdtError::MissingRequiredProperty("clock-frequency"))?; + + match prop.value().len() { + 4 => Ok(u64::from(prop.as_value::()?)), + 8 => Ok(prop.as_value::()?), + _ => Err(FdtError::InvalidPropertyValue), + } + } + } + })) + } + + /// [Devicetree 3.8.1 + /// `timebase-frequency`](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#general-properties-of-cpus-cpu-nodes) + /// + /// **Required** + /// + /// Specifies the current frequency at which the timebase and decrementer + /// registers are updated (in Hertz). The value is a `` in + /// one of two forms: + /// + /// * A 32-bit integer consisting of one `` specifying the frequency. + /// * A 64-bit integer represented as a ``. + pub fn timebase_frequency(self) -> P::Output { + P::to_output(crate::tryblock!({ + match self.node.properties()?.find("timebase-frequency")? { + Some(prop) => match prop.value().len() { + 4 => Ok(u64::from(prop.as_value::()?)), + 8 => Ok(prop.as_value::()?), + _ => Err(FdtError::InvalidPropertyValue), + }, + None => { + let prop = self + .node + .parent() + .unwrap() + .properties()? + .find("timebase-frequency")? + .ok_or(FdtError::MissingRequiredProperty("timebase-frequency"))?; + + match prop.value().len() { + 4 => Ok(u64::from(prop.as_value::()?)), + 8 => Ok(prop.as_value::()?), + _ => Err(FdtError::InvalidPropertyValue), + } + } + } + })) + } + + /// [Devicetree 3.8.1 + /// `status`](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#general-properties-of-cpus-cpu-nodes) + /// + /// A standard property describing the state of a CPU. This property shall + /// be present for nodes representing CPUs in a symmetric multiprocessing + /// (SMP) configuration. For a CPU node the meaning of the `"okay"`, + /// `"disabled"` and `"fail"` values are as follows: + /// + /// `"okay"`: The CPU is running. + /// + /// `"disabled"`: The CPU is in a quiescent state. + /// + /// `"fail"`: The CPU is not operational or does not exist. + /// + /// A quiescent CPU is in a state where it cannot interfere with the normal + /// operation of other CPUs, nor can its state be affected by the normal + /// operation of other running CPUs, except by an explicit method for + /// enabling or re-enabling the quiescent CPU (see the enable-method + /// property). + /// + /// In particular, a running CPU shall be able to issue broadcast TLB + /// invalidates without affecting a quiescent CPU. + /// + /// Examples: A quiescent CPU could be in a spin loop, held in reset, and + /// electrically isolated from the system bus or in another implementation + /// dependent state. + /// + /// A CPU with `"fail"` status does not affect the system in any way. The + /// status is assigned to nodes for which no corresponding CPU exists. + pub fn status(&self) -> P::Output> { + P::to_output(crate::tryblock!({ + let Some(status) = self.node.properties()?.find("status")? else { + return Ok(None); + }; + + Ok(Some(CpuStatus(status.as_value()?))) + })) + } + + /// [Devicetree 3.8.1 + /// `enable-method`](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#general-properties-of-cpus-cpu-nodes) + /// + /// Describes the method by which a CPU in a disabled state is enabled. This + /// property is required for CPUs with a status property with a value of + /// `"disabled"`. The value consists of one or more strings that define the + /// method to release this CPU. If a client program recognizes any of the + /// methods, it may use it. The value shall be one of the following: + /// + /// `"spin-table"`: The CPU is enabled with the spin table method defined in + /// the DTSpec. + /// + /// `"[vendor],[method]"`: Implementation dependent string that describes + /// the method by which a CPU is released from a `"disabled"` state. The + /// required format is: `"[vendor],[method]"`, where vendor is a string + /// describing the name of the manufacturer and method is a string + /// describing the vendor specific mechanism. + /// + /// Example: `"fsl,MPC8572DS"` + pub fn enable_method(&self) -> P::Output> { + P::to_output(crate::tryblock!({ + let Some(status) = self.node.properties()?.find("enable-method")? else { + return Ok(None); + }; + + let s: &'a str = status.as_value()?; + + if s.is_empty() { + return Err(FdtError::InvalidPropertyValue); + } + + Ok(Some(CpuEnableMethods(s.into()))) + })) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[repr(transparent)] +pub struct CpuStatus<'a>(&'a str); + +impl<'a> CpuStatus<'a> { + /// The CPU is running. + pub const OKAY: Self = Self("okay"); + /// The CPU is in a quiescent state. + pub const DISABLED: Self = Self("disabled"); + /// The CPU is not operational or does not exist. + pub const FAIL: Self = Self("fail"); + + /// Create a new [`CpuStatus`] which may not be one of the associated + /// constant values. + pub fn new(status: &'a str) -> Self { + Self(status) + } + + /// Whether the status is `"okay"`. + pub fn is_okay(self) -> bool { + self == Self::OKAY + } + + /// Whether the status is `"disabled"`. + pub fn is_disabled(self) -> bool { + self == Self::DISABLED + } + + /// Whether the status is `"failed"` + pub fn is_failed(self) -> bool { + self == Self::FAIL + } +} + +impl<'a> PartialEq for CpuStatus<'a> { + fn eq(&self, other: &str) -> bool { + self.0 == other + } +} + +/// Type representing one or more CPU enable methods. See +/// [`Cpu::enable_method`]. +#[derive(Debug, Clone)] +pub struct CpuEnableMethods<'a>(StringList<'a>); + +impl<'a> CpuEnableMethods<'a> { + /// Create an iterator over the enable methods. + pub fn iter(&self) -> CpuEnableMethodsIter<'a> { + CpuEnableMethodsIter(self.0.clone()) + } + + /// Return the first enable method contained in the list of enable methods. + pub fn first(&self) -> CpuEnableMethod<'a> { + self.iter().next().unwrap() + } +} + +impl<'a> IntoIterator for CpuEnableMethods<'a> { + type IntoIter = CpuEnableMethodsIter<'a>; + type Item = CpuEnableMethod<'a>; + + fn into_iter(self) -> Self::IntoIter { + CpuEnableMethodsIter(self.0) + } +} + +/// Iterator over the enable methods described by the `enable-method` property +/// on a CPU node. See [`Cpu::enable_method`]. +pub struct CpuEnableMethodsIter<'a>(StringList<'a>); + +impl<'a> Iterator for CpuEnableMethodsIter<'a> { + type Item = CpuEnableMethod<'a>; + + fn next(&mut self) -> Option { + match self.0.next()? { + "spin-table" => Some(CpuEnableMethod::SpinTable), + other => { + let (vendor, method) = other.split_once(',').unwrap_or((other, "")); + Some(CpuEnableMethod::VendorMethod { vendor, method }) + } + } + } +} + +/// An enable method contained by the [`Cpu::enable_method`] +pub enum CpuEnableMethod<'a> { + /// The CPU is enabled with the spin table method defined in the DTSpec. + SpinTable, + /// Implementation dependent string that describes the method by which a CPU + /// is released from a `"disabled"` state. + VendorMethod { + /// The manufacturer. + vendor: &'a str, + /// The vendor specific mechanism. + /// + /// NOTE: If the string value of this enable method does not match the + /// `"[vendor],[method]"` format defined by the devicetree spec, this + /// will be an empty string. + method: &'a str, + }, +} + +/// See [`Cpu::reg`] +pub struct CpuIds<'a, C: CellCollector> { + reg: &'a [u8], + address_cells: usize, + _collector: core::marker::PhantomData<*mut C>, +} + +impl<'a, C: CellCollector> CpuIds<'a, C> { + /// The first listed CPU ID, which will always exist + pub fn first(&self) -> Result { + self.iter().next().unwrap() + } + + pub fn iter(&self) -> CpuIdsIter<'a, C> { + CpuIdsIter { reg: self.reg, address_cells: self.address_cells, _collector: core::marker::PhantomData } + } +} + +impl Copy for CpuIds<'_, C> {} +impl Clone for CpuIds<'_, C> { + fn clone(&self) -> Self { + *self + } +} + +impl<'a, C: CellCollector> core::fmt::Debug for CpuIds<'a, C> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("CpuIds") + .field("reg", &self.reg) + .field("address_cells", &self.address_cells) + .finish_non_exhaustive() + } +} + +pub struct CpuIdsIter<'a, C: CellCollector> { + reg: &'a [u8], + address_cells: usize, + _collector: core::marker::PhantomData<*mut C>, +} + +impl<'a, C: CellCollector> core::fmt::Debug for CpuIdsIter<'a, C> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("CpuIds") + .field("reg", &self.reg) + .field("address_cells", &self.address_cells) + .finish_non_exhaustive() + } +} + +impl Clone for CpuIdsIter<'_, C> { + fn clone(&self) -> Self { + Self { address_cells: self.address_cells, reg: self.reg, _collector: core::marker::PhantomData } + } +} + +impl<'a, C: CellCollector> Iterator for CpuIdsIter<'a, C> { + type Item = Result; + fn next(&mut self) -> Option { + let (this_cell, rest) = self.reg.split_at_checked(self.address_cells * 4)?; + self.reg = rest; + + let mut collector = ::Builder::default(); + + for cell in this_cell.chunks_exact(4) { + if let Err(e) = collector.push(u32::from_be_bytes(cell.try_into().unwrap())) { + return Some(Err(e)); + } + } + + Some(Ok(C::map(collector.finish()))) + } +} diff --git a/src/nodes/memory.rs b/src/nodes/memory.rs new file mode 100644 index 0000000..a1e6409 --- /dev/null +++ b/src/nodes/memory.rs @@ -0,0 +1,134 @@ +use crate::{ + parsing::{aligned::AlignedParser, Panic, ParserWithMode}, + properties::{cells::CellSizes, reg::Reg}, + FdtError, +}; + +use super::FallibleNode; + +/// Represents the `/memory` node with specific helper methods +#[derive(Debug, Clone, Copy)] +pub struct Memory<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { + pub(crate) node: FallibleNode<'a, P>, +} + +impl<'a, P: ParserWithMode<'a>> Memory<'a, P> { + /// [Devicetree 3.4. `/memory` + /// node](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#memory-node) + /// + /// **Required** + /// + /// Consists of an arbitrary number of address and size pairs that specify + /// the physical address and size of the memory ranges. + pub fn reg(&self) -> P::Output> { + P::to_output(self.node.reg().and_then(|m| m.ok_or(FdtError::MissingRequiredNode("reg")))) + } + + /// [Devicetree 3.4. `/memory` + /// node](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#memory-node) + /// + /// **Optional** + /// + /// Specifies the address and size of the Initial Mapped Area + /// + /// A `prop-encoded-array` consisting of a triplet of (effective address, + /// physical address, size). The effective and physical address shall each + /// be 64-bit (`` value), and the size shall be 32-bits (`` + /// value). + pub fn initial_mapped_area(&self) -> P::Output> { + P::to_output(crate::tryblock!({ + match self.node.properties()?.find("initial-mapped-area")? { + Some(prop) => { + let value = prop.value(); + if value.len() != (/* effective address */8 + /* physical address */ 8 + /* size */ 4) { + return Err(FdtError::InvalidPropertyValue); + } + + Ok(Some(MappedArea { + effective_address: u64::from_be_bytes(value[0..8].try_into().unwrap()), + physical_address: u64::from_be_bytes(value[8..16].try_into().unwrap()), + size: u32::from_be_bytes(value[16..20].try_into().unwrap()), + })) + } + None => Ok(None), + } + })) + } + + /// [Devicetree 3.4. `/memory` + /// node](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#memory-node) + /// + /// Specifies an explicit hint to the operating system that this memory may + /// potentially be removed later. + pub fn hotpluggable(&self) -> P::Output { + P::to_output(crate::tryblock!({ Ok(self.node.properties()?.find("hotpluggable")?.is_some()) })) + } +} + +/// Describes the initial mapped area of the `/memory` node. See +/// [`Memory::initial_mapped_area`]. +#[allow(missing_docs)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct MappedArea { + pub effective_address: u64, + pub physical_address: u64, + pub size: u32, +} + +/// [Devicetree 3.5. `/reserved-memory` +/// node](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#reserved-memory-node) +/// +/// Reserved memory is specified as a node under the `/reserved-memory` node. The +/// operating system shall exclude reserved memory from normal usage. One can +/// create child nodes describing particular reserved (excluded from normal use) +/// memory regions. Such memory regions are usually designed for the special +/// usage by various device drivers. +pub struct ReservedMemory<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { + pub(crate) node: FallibleNode<'a, P>, +} + +impl<'a, P: ParserWithMode<'a>> ReservedMemory<'a, P> { + pub fn cell_sizes(&self) -> P::Output { + P::to_output( + self.node + .property::() + .and_then(|c| c.ok_or(FdtError::MissingRequiredNode("#address-cells/#size-cells"))), + ) + } + + pub fn children(&self) -> ReservedMemoryChildren<'a, P> { + ReservedMemoryChildren { node: self.node } + } +} + +pub struct ReservedMemoryChildren<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { + node: FallibleNode<'a, P>, +} + +impl<'a, P: ParserWithMode<'a>> ReservedMemoryChildren<'a, P> { + pub fn all(&self) -> P::Output> {} +} + +pub struct ReservedMemoryChild<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { + node: FallibleNode<'a, P>, +} + +/// A memory region +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct MemoryRegion { + pub starting_address: u64, + pub size: Option, +} + +/// Range mapping child bus addresses to parent bus addresses +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct MemoryRange { + /// Starting address on child bus + pub child_bus_address: usize, + /// The high bits of the child bus' starting address, if present + pub child_bus_address_hi: u32, + /// Starting address on parent bus + pub parent_bus_address: usize, + /// Size of range + pub size: usize, +} diff --git a/src/standard_nodes.rs b/src/standard_nodes.rs index 4652123..0a38e22 100644 --- a/src/standard_nodes.rs +++ b/src/standard_nodes.rs @@ -516,570 +516,3 @@ impl<'a, P: ParserWithMode<'a>> Iterator for AllNodesIter<'a, P> { node } } - -/// [Devicetree 3.3. `/aliases` -/// node](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#aliases-node) -/// -/// A devicetree may have an aliases node (`/aliases`) that defines one or more -/// alias properties. The alias node shall be at the root of the devicetree and -/// have the node name `/aliases`. -/// -/// Each property of the `/aliases` node defines an alias. The property name -/// specifies the alias name. The property value specifies the full path to a -/// node in the devicetree. For example, the property `serial0 = -/// "/simple-bus@fe000000/serial@llc500"` defines the alias `serial0`. -/// -/// An alias value is a device path and is encoded as a string. The value -/// represents the full path to a node, but the path does not need to refer to a -/// leaf node. -/// -/// A client program may use an alias property name to refer to a full device -/// path as all or part of its string value. A client program, when considering -/// a string as a device path, shall detect and use the alias. -/// -/// ### Example -/// -/// ```norust -/// aliases { -/// serial0 = "/simple-bus@fe000000/serial@llc500"; -/// ethernet0 = "/simple-bus@fe000000/ethernet@31c000"; -/// }; -/// ``` -/// -/// Given the alias `serial0`, a client program can look at the `/aliases` node -/// and determine the alias refers to the device path -/// `/simple-bus@fe000000/serial@llc500`. -#[derive(Debug, Clone, Copy)] -pub struct Aliases<'a, P: ParserWithMode<'a>> { - pub(crate) node: FallibleNode<'a, P>, -} - -impl<'a, P: ParserWithMode<'a>> Aliases<'a, P> { - /// Attempt to resolve an alias to a node name. - pub fn resolve_name(self, alias: &str) -> P::Output> { - P::to_output(crate::tryblock!({ - self.node.properties()?.find(alias)?.map(|p| p.as_value().map_err(Into::into)).transpose() - })) - } - - /// Attempt resolve an alias to the aliased-to node. - pub fn resolve(self, alias: &str) -> P::Output>> { - P::to_output(crate::tryblock!({ - let Some(path) = Aliases::<(_, NoPanic)> { node: self.node }.resolve_name(alias)? else { - return Ok(None); - }; - - self.node.make_root::()?.find_node(path).map(|r| r.map(|n| n.alt())) - })) - } -} - -/// [Devicetree 3.7. -/// `/cpus`](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#cpus-node-properties) -/// -/// A `/cpus` node is required for all devicetrees. It does not represent a real -/// device in the system, but acts as a container for child cpu nodes which -/// represent the systems CPUs. -pub struct Cpus<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { - pub(crate) node: FallibleNode<'a, P>, -} - -impl<'a, P: ParserWithMode<'a>> Cpus<'a, P> { - /// Retrieve the `#address-cells` and `#size-cells` values from this node - pub fn cell_sizes(&self) -> P::Output { - P::to_output( - self.node.property().and_then(|p| p.ok_or(FdtError::MissingRequiredProperty("#address-cells/#size-cells"))), - ) - } - - /// Attempt to find a common `timebase-frequency` property inside of this - /// node, which will only exist if there is a common value between the child - /// `cpu` nodes. See [`Cpu::timebase_frequency`] for documentation about the - /// `timebase-frequency` property. - pub fn common_timebase_frequency(&self) -> P::Output> { - P::to_output(crate::tryblock!({ - match self.node.properties()?.find("timebase-frequency")? { - Some(prop) => match prop.value().len() { - 4 => Ok(Some(u64::from(prop.as_value::()?))), - 8 => Ok(Some(prop.as_value::()?)), - _ => Err(FdtError::InvalidPropertyValue), - }, - None => Ok(None), - } - })) - } - - /// Attempt to find a common `clock-frequency` property inside of this - /// node, which will only exist if there is a common value between the child - /// `cpu` nodes. See [`Cpu::clock_frequency`] for documentation about the - /// `clock-frequency` property. - pub fn common_clock_frequency(&self) -> P::Output> { - P::to_output(crate::tryblock!({ - match self.node.properties()?.find("clock-frequency")? { - Some(prop) => match prop.value().len() { - 4 => Ok(Some(u64::from(prop.as_value::()?))), - 8 => Ok(Some(prop.as_value::()?)), - _ => Err(FdtError::InvalidPropertyValue), - }, - None => Ok(None), - } - })) - } -} - -/// [Devicetree 3.8. -/// `/cpus/cpu*`](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#cpus-cpu-node-properties) -/// -/// A `cpu` node represents a hardware execution block that is sufficiently -/// independent that it is capable of running an operating system without -/// interfering with other CPUs possibly running other operating systems. -/// -/// Hardware threads that share an MMU would generally be represented under one -/// `cpu` node. If other more complex CPU topographies are designed, the binding -/// for the CPU must describe the topography (e.g. threads that don’t share an -/// MMU). -/// -/// CPUs and threads are numbered through a unified number-space that should -/// match as closely as possible the interrupt controller’s numbering of -/// CPUs/threads. -/// -/// Properties that have identical values across `cpu` nodes may be placed in the -/// /cpus node instead. A client program must first examine a specific `cpu` node, -/// but if an expected property is not found then it should look at the parent -/// /cpus node. This results in a less verbose representation of properties -/// which are identical across all CPUs. -#[derive(Debug, Clone, Copy)] -pub struct Cpu<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { - pub(crate) node: FallibleNode<'a, P>, -} - -impl<'a, P: ParserWithMode<'a>> Cpu<'a, P> { - /// [Devicetree 3.8.1 - /// `reg`](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#general-properties-of-cpus-cpu-nodes) - /// - /// **Required** - /// - /// The value of `reg` is a `` that defines a unique - /// CPU/thread id for the CPU/threads represented by the CPU node. - /// - /// If a CPU supports more than one thread (i.e. multiple streams of - /// execution) the `reg` property is an array with 1 element per thread. The - /// `#address-cells` on the `/cpus` node specifies how many cells each - /// element of the array takes. Software can determine the number of threads - /// by dividing the size of `reg` by the parent node’s `#address-cells`. - /// - /// If a CPU/thread can be the target of an external interrupt the `reg` - /// property value must be a unique CPU/thread id that is addressable by the - /// interrupt controller. - /// - /// If a CPU/thread cannot be the target of an external interrupt, then - /// `reg` must be unique and out of bounds of the range addressed by the - /// interrupt controller - /// - /// If a CPU/thread’s PIR (pending interrupt register) is modifiable, a - /// client program should modify PIR to match the `reg` property value. If - /// PIR cannot be modified and the PIR value is distinct from the interrupt - /// controller number space, the CPUs binding may define a binding-specific - /// representation of PIR values if desired. - pub fn reg(self) -> P::Output> { - P::to_output(crate::tryblock!({ - let Some(reg) = self.node.properties()?.find("reg")? else { - return Err(FdtError::MissingRequiredProperty("reg")); - }; - - if reg.value().is_empty() { - return Err(FdtError::InvalidPropertyValue); - } - - let Some(address_cells) = self.node.parent().unwrap().property::()? else { - return Err(FdtError::MissingRequiredProperty("#address-cells")); - }; - - Ok(CpuIds { reg: reg.value(), address_cells: address_cells.0, _collector: core::marker::PhantomData }) - })) - } - - /// [Devicetree 3.8.1 - /// `clock-frequency`](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#general-properties-of-cpus-cpu-nodes) - /// - /// **Required** - /// - /// Specifies the current clock speed of the CPU in Hertz. The value is a - /// `` in one of two forms: - /// - /// * A 32-bit integer consisting of one `` specifying the frequency. - /// * A 64-bit integer represented as a `` specifying the frequency. - pub fn clock_frequency(self) -> P::Output { - P::to_output(crate::tryblock!({ - match self.node.properties()?.find("clock-frequency")? { - Some(prop) => match prop.value().len() { - 4 => Ok(u64::from(prop.as_value::()?)), - 8 => Ok(prop.as_value::()?), - _ => Err(FdtError::InvalidPropertyValue), - }, - None => { - let prop = self - .node - .parent() - .unwrap() - .properties()? - .find("clock-frequency")? - .ok_or(FdtError::MissingRequiredProperty("clock-frequency"))?; - - match prop.value().len() { - 4 => Ok(u64::from(prop.as_value::()?)), - 8 => Ok(prop.as_value::()?), - _ => Err(FdtError::InvalidPropertyValue), - } - } - } - })) - } - - /// [Devicetree 3.8.1 - /// `timebase-frequency`](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#general-properties-of-cpus-cpu-nodes) - /// - /// **Required** - /// - /// Specifies the current frequency at which the timebase and decrementer - /// registers are updated (in Hertz). The value is a `` in - /// one of two forms: - /// - /// * A 32-bit integer consisting of one `` specifying the frequency. - /// * A 64-bit integer represented as a ``. - pub fn timebase_frequency(self) -> P::Output { - P::to_output(crate::tryblock!({ - match self.node.properties()?.find("timebase-frequency")? { - Some(prop) => match prop.value().len() { - 4 => Ok(u64::from(prop.as_value::()?)), - 8 => Ok(prop.as_value::()?), - _ => Err(FdtError::InvalidPropertyValue), - }, - None => { - let prop = self - .node - .parent() - .unwrap() - .properties()? - .find("timebase-frequency")? - .ok_or(FdtError::MissingRequiredProperty("timebase-frequency"))?; - - match prop.value().len() { - 4 => Ok(u64::from(prop.as_value::()?)), - 8 => Ok(prop.as_value::()?), - _ => Err(FdtError::InvalidPropertyValue), - } - } - } - })) - } - - /// [Devicetree 3.8.1 - /// `status`](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#general-properties-of-cpus-cpu-nodes) - /// - /// A standard property describing the state of a CPU. This property shall - /// be present for nodes representing CPUs in a symmetric multiprocessing - /// (SMP) configuration. For a CPU node the meaning of the `"okay"`, - /// `"disabled"` and `"fail"` values are as follows: - /// - /// `"okay"`: The CPU is running. - /// - /// `"disabled"`: The CPU is in a quiescent state. - /// - /// `"fail"`: The CPU is not operational or does not exist. - /// - /// A quiescent CPU is in a state where it cannot interfere with the normal - /// operation of other CPUs, nor can its state be affected by the normal - /// operation of other running CPUs, except by an explicit method for - /// enabling or re-enabling the quiescent CPU (see the enable-method - /// property). - /// - /// In particular, a running CPU shall be able to issue broadcast TLB - /// invalidates without affecting a quiescent CPU. - /// - /// Examples: A quiescent CPU could be in a spin loop, held in reset, and - /// electrically isolated from the system bus or in another implementation - /// dependent state. - /// - /// A CPU with `"fail"` status does not affect the system in any way. The - /// status is assigned to nodes for which no corresponding CPU exists. - pub fn status(&self) -> P::Output> { - P::to_output(crate::tryblock!({ - let Some(status) = self.node.properties()?.find("status")? else { - return Ok(None); - }; - - Ok(Some(CpuStatus(status.as_value()?))) - })) - } - - /// [Devicetree 3.8.1 - /// `enable-method`](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#general-properties-of-cpus-cpu-nodes) - /// - /// Describes the method by which a CPU in a disabled state is enabled. This - /// property is required for CPUs with a status property with a value of - /// `"disabled"`. The value consists of one or more strings that define the - /// method to release this CPU. If a client program recognizes any of the - /// methods, it may use it. The value shall be one of the following: - /// - /// `"spin-table"`: The CPU is enabled with the spin table method defined in - /// the DTSpec. - /// - /// `"[vendor],[method]"`: Implementation dependent string that describes - /// the method by which a CPU is released from a `"disabled"` state. The - /// required format is: `"[vendor],[method]"`, where vendor is a string - /// describing the name of the manufacturer and method is a string - /// describing the vendor specific mechanism. - /// - /// Example: `"fsl,MPC8572DS"` - pub fn enable_method(&self) -> P::Output> { - P::to_output(crate::tryblock!({ - let Some(status) = self.node.properties()?.find("enable-method")? else { - return Ok(None); - }; - - let s: &'a str = status.as_value()?; - - if s.is_empty() { - return Err(FdtError::InvalidPropertyValue); - } - - Ok(Some(CpuEnableMethods(s.into()))) - })) - } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[repr(transparent)] -pub struct CpuStatus<'a>(&'a str); - -impl<'a> CpuStatus<'a> { - /// The CPU is running. - pub const OKAY: Self = Self("okay"); - /// The CPU is in a quiescent state. - pub const DISABLED: Self = Self("disabled"); - /// The CPU is not operational or does not exist. - pub const FAIL: Self = Self("fail"); - - /// Create a new [`CpuStatus`] which may not be one of the associated - /// constant values. - pub fn new(status: &'a str) -> Self { - Self(status) - } - - /// Whether the status is `"okay"`. - pub fn is_okay(self) -> bool { - self == Self::OKAY - } - - /// Whether the status is `"disabled"`. - pub fn is_disabled(self) -> bool { - self == Self::DISABLED - } - - /// Whether the status is `"failed"` - pub fn is_failed(self) -> bool { - self == Self::FAIL - } -} - -impl<'a> PartialEq for CpuStatus<'a> { - fn eq(&self, other: &str) -> bool { - self.0 == other - } -} - -/// Type representing one or more CPU enable methods. See -/// [`Cpu::enable_method`]. -#[derive(Debug, Clone)] -pub struct CpuEnableMethods<'a>(StringList<'a>); - -impl<'a> CpuEnableMethods<'a> { - /// Create an iterator over the enable methods. - pub fn iter(&self) -> CpuEnableMethodsIter<'a> { - CpuEnableMethodsIter(self.0.clone()) - } - - /// Return the first enable method contained in the list of enable methods. - pub fn first(&self) -> CpuEnableMethod<'a> { - self.iter().next().unwrap() - } -} - -impl<'a> IntoIterator for CpuEnableMethods<'a> { - type IntoIter = CpuEnableMethodsIter<'a>; - type Item = CpuEnableMethod<'a>; - - fn into_iter(self) -> Self::IntoIter { - CpuEnableMethodsIter(self.0) - } -} - -/// Iterator over the enable methods described by the `enable-method` property -/// on a CPU node. See [`Cpu::enable_method`]. -pub struct CpuEnableMethodsIter<'a>(StringList<'a>); - -impl<'a> Iterator for CpuEnableMethodsIter<'a> { - type Item = CpuEnableMethod<'a>; - - fn next(&mut self) -> Option { - match self.0.next()? { - "spin-table" => Some(CpuEnableMethod::SpinTable), - other => { - let (vendor, method) = other.split_once(',').unwrap_or((other, "")); - Some(CpuEnableMethod::VendorMethod { vendor, method }) - } - } - } -} - -/// An enable method contained by the [`Cpu::enable_method`] -pub enum CpuEnableMethod<'a> { - /// The CPU is enabled with the spin table method defined in the DTSpec. - SpinTable, - /// Implementation dependent string that describes the method by which a CPU - /// is released from a `"disabled"` state. - VendorMethod { - /// The manufacturer. - vendor: &'a str, - /// The vendor specific mechanism. - /// - /// NOTE: If the string value of this enable method does not match the - /// `"[vendor],[method]"` format defined by the devicetree spec, this - /// will be an empty string. - method: &'a str, - }, -} - -/// See [`Cpu::reg`] -pub struct CpuIds<'a, C: CellCollector> { - reg: &'a [u8], - address_cells: usize, - _collector: core::marker::PhantomData<*mut C>, -} - -impl<'a, C: CellCollector> CpuIds<'a, C> { - /// The first listed CPU ID, which will always exist - pub fn first(&self) -> Result { - self.iter().next().unwrap() - } - - pub fn iter(&self) -> CpuIdsIter<'a, C> { - CpuIdsIter { reg: self.reg, address_cells: self.address_cells, _collector: core::marker::PhantomData } - } -} - -impl Copy for CpuIds<'_, C> {} -impl Clone for CpuIds<'_, C> { - fn clone(&self) -> Self { - *self - } -} - -impl<'a, C: CellCollector> core::fmt::Debug for CpuIds<'a, C> { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - f.debug_struct("CpuIds") - .field("reg", &self.reg) - .field("address_cells", &self.address_cells) - .finish_non_exhaustive() - } -} - -#[derive(Debug)] -pub struct CpuIdsIter<'a, C: CellCollector> { - reg: &'a [u8], - address_cells: usize, - _collector: core::marker::PhantomData<*mut C>, -} - -impl Clone for CpuIdsIter<'_, C> { - fn clone(&self) -> Self { - Self { address_cells: self.address_cells, reg: self.reg, _collector: core::marker::PhantomData } - } -} - -impl<'a, C: CellCollector> Iterator for CpuIdsIter<'a, C> { - type Item = Result; - fn next(&mut self) -> Option { - let (this_cell, rest) = self.reg.split_at_checked(self.address_cells * 4)?; - self.reg = rest; - - let mut collector = ::Builder::default(); - - for cell in this_cell.chunks_exact(4) { - if let Err(e) = collector.push(u32::from_be_bytes(cell.try_into().unwrap())) { - return Some(Err(e)); - } - } - - Some(Ok(C::map(collector.finish()))) - } -} - -/// Represents the `/memory` node with specific helper methods -// #[derive(Debug, Clone, Copy)] -// pub struct Memory<'b, 'a: 'b> { -// pub(crate) node: FdtNode<'b, 'a>, -// } - -// impl<'a> Memory<'_, 'a> { -// /// Returns an iterator over all of the available memory regions -// pub fn regions(&self) -> impl Iterator + 'a { -// self.node.reg().unwrap() -// } - -// /// Returns the initial mapped area, if it exists -// pub fn initial_mapped_area(&self) -> Option { -// let mut mapped_area = None; - -// if let Some(init_mapped_area) = self.node.property("initial_mapped_area") { -// let mut stream = FdtData::new(init_mapped_area.value); -// let effective_address = stream.u64().expect("effective address"); -// let physical_address = stream.u64().expect("physical address"); -// let size = stream.u32().expect("size"); - -// mapped_area = Some(MappedArea { -// effective_address: effective_address.to_ne() as usize, -// physical_address: physical_address.to_ne() as usize, -// size: size.to_ne() as usize, -// }); -// } - -// mapped_area -// } -// } - -/// An area described by the `initial-mapped-area` property of the `/memory` -/// node -#[derive(Debug, Clone, Copy, PartialEq)] -#[repr(C)] -pub struct MappedArea { - /// Effective address of the mapped area - pub effective_address: usize, - /// Physical address of the mapped area - pub physical_address: usize, - /// Size of the mapped area - pub size: usize, -} - -/// A memory region -#[derive(Debug, Clone, Copy, PartialEq)] -pub struct MemoryRegion { - /// Starting address represented as a pointer - pub starting_address: *const u8, - /// Size of the memory region - pub size: Option, -} - -/// Range mapping child bus addresses to parent bus addresses -#[derive(Debug, Clone, Copy, PartialEq)] -pub struct MemoryRange { - /// Starting address on child bus - pub child_bus_address: usize, - /// The high bits of the child bus' starting address, if present - pub child_bus_address_hi: u32, - /// Starting address on parent bus - pub parent_bus_address: usize, - /// Size of range - pub size: usize, -} From d1d6f7bee77ff2bc79bbd1215e3f222a4de76411 Mon Sep 17 00:00:00 2001 From: repnop Date: Tue, 6 Aug 2024 20:32:17 -0400 Subject: [PATCH 25/38] aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa --- Cargo.toml | 1 + src/lib.rs | 218 +------- src/node_old.rs | 639 ----------------------- src/nodes.rs | 25 +- src/nodes/aliases.rs | 8 +- src/nodes/chosen.rs | 140 +++++ src/nodes/cpus.rs | 197 ++++++- src/nodes/memory.rs | 247 ++++++++- src/{standard_nodes.rs => nodes/root.rs} | 272 +++++----- src/pretty_print.rs | 3 +- src/properties.rs | 36 +- src/properties/cells.rs | 34 +- src/properties/interrupts.rs | 37 +- src/properties/ranges.rs | 29 +- src/properties/reg.rs | 9 +- src/tests.rs | 11 +- 16 files changed, 821 insertions(+), 1085 deletions(-) delete mode 100644 src/node_old.rs create mode 100644 src/nodes/chosen.rs rename src/{standard_nodes.rs => nodes/root.rs} (68%) diff --git a/Cargo.toml b/Cargo.toml index 1884e96..f2acc9a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,5 +15,6 @@ readme = "README.md" [features] pretty-printing = [] +linux-dt-bindings = [] [dependencies] diff --git a/src/lib.rs b/src/lib.rs index e41cf09..fae1b47 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,46 +4,50 @@ //! # `fdt` //! -//! A pure-Rust `#![no_std]` crate for parsing Flattened Devicetrees, with the goal of having a -//! very ergonomic and idiomatic API. +//! A pure-Rust `#![no_std]` crate for parsing Flattened Devicetrees, with the +//! goal of having a very ergonomic and idiomatic API. //! -//! [![crates.io](https://img.shields.io/crates/v/fdt.svg)](https://crates.io/crates/fdt) [![Documentation](https://docs.rs/fdt/badge.svg)](https://docs.rs/fdt) ![Build](https://github.com/repnop/fdt/actions/workflows/test.yml/badge.svg?branch=master&event=push) +//! [![crates.io](https://img.shields.io/crates/v/fdt.svg)](https://crates.io/crates/fdt) +//! [![Documentation](https://docs.rs/fdt/badge.svg)](https://docs.rs/fdt) +//! ![Build](https://github.com/repnop/fdt/actions/workflows/test.yml/badge.svg?branch=master&event=push) //! //! ## License //! -//! This crate is licensed under the Mozilla Public License 2.0 (see the LICENSE file). +//! This crate is licensed under the Mozilla Public License 2.0 (see the LICENSE +//! file). //! //! ## Example //! -//! ```rust,no_run +//! ```rust //! static MY_FDT: &[u8] = include_bytes!("../dtb/test.dtb"); //! //! fn main() { -//! let fdt = fdt::Fdt::new(MY_FDT).unwrap(); +//! let fdt = fdt::Fdt::new_unaligned(MY_FDT).unwrap(); +//! let root = fdt.root(); //! -//! println!("This is a devicetree representation of a {}", fdt.root().model()); -//! println!("...which is compatible with at least: {}", fdt.root().compatible().first()); -//! println!("...and has {} CPU(s)", fdt.cpus().count()); +//! println!("This is a devicetree representation of a {}", root.model()); +//! println!("...which is compatible with at least: {}", root.compatible().first()); +//! println!("...and has {} CPU(s)", root.cpus().cpus().count()); //! println!( //! "...and has at least one memory location at: {:#X}\n", -//! fdt.memory().regions().next().unwrap().starting_address as usize +//! root.memory().reg().iter::().next().unwrap().unwrap().address //! ); //! -//! let chosen = fdt.chosen(); +//! let chosen = root.chosen(); //! if let Some(bootargs) = chosen.bootargs() { //! println!("The bootargs are: {:?}", bootargs); //! } //! //! if let Some(stdout) = chosen.stdout() { -//! println!("It would write stdout to: {}", stdout.name()); +//! println!("It would write stdout to: {}", stdout.path()); //! } //! -//! let soc = fdt.find_node("/soc"); +//! let soc = root.find_node("/soc"); //! println!("Does it have a `/soc` node? {}", if soc.is_some() { "yes" } else { "no" }); //! if let Some(soc) = soc { //! println!("...and it has the following children:"); -//! for child in soc.children() { -//! println!(" {}", child.name); +//! for child in soc.children().iter() { +//! println!(" {}", child.name()); //! } //! } //! } @@ -58,18 +62,17 @@ extern crate std; mod tests; pub mod cell_collector; -mod nodes; +pub mod nodes; mod parsing; mod pretty_print; pub mod properties; -pub mod standard_nodes; mod util; +use nodes::root::Root; use parsing::{ aligned::AlignedParser, unaligned::UnalignedParser, NoPanic, Panic, ParseError, Parser, ParserWithMode, StringsBlock, StructsBlock, }; -use standard_nodes::Root; // use standard_nodes::{Aliases, Chosen, Cpu, Memory, MemoryRange, MemoryRegion, Root}; mod sealed { @@ -88,6 +91,7 @@ pub enum FdtError { /// An error was encountered during parsing ParseError(ParseError), PHandleNotFound(u32), + MissingParent, MissingRequiredNode(&'static str), MissingRequiredProperty(&'static str), InvalidPropertyValue, @@ -110,6 +114,7 @@ impl core::fmt::Display for FdtError { FdtError::PHandleNotFound(value) => { write!(f, "a node containing the `phandle` property value of `{value}` was not found") } + FdtError::MissingParent => write!(f, "node parent is not present but needed to parse a property"), FdtError::MissingRequiredNode(name) => { write!(f, "FDT is missing a required node `{}`", name) } @@ -314,187 +319,12 @@ impl<'a> Fdt<'a, (AlignedParser<'a>, NoPanic)> { } impl<'a, P: ParserWithMode<'a>> Fdt<'a, P> { - /// Return the `/aliases` node, if one exists - // pub fn aliases(&self) -> Option> { - // Some(Aliases { - // node: node::find_node(&mut FdtData::new(self.structs_block()), "/aliases", self, None)?, - // header: self, - // }) - // } - - /// Searches for the `/chosen` node, which is always available - // pub fn chosen(&self) -> Chosen<'_, 'a> { - // node::find_node(&mut FdtData::new(self.structs_block()), "/chosen", self, None) - // .map(|node| Chosen { node }) - // .expect("/chosen is required") - // } - - /// Return the `/cpus` node, which is always available - // pub fn cpus(&self) -> impl Iterator> { - // let parent = self.find_node("/cpus").expect("/cpus is a required node"); - - // parent - // .children() - // .filter(|c| c.name.split('@').next().unwrap() == "cpu") - // .map(move |cpu| Cpu { parent, node: cpu }) - // } - - /// Returns the memory node, which is always available - // pub fn memory(&self) -> Memory<'_, 'a> { - // Memory { node: self.find_node("/memory").expect("requires memory node") } - // } - - /// Returns an iterator over the memory reservations - // pub fn memory_reservations(&self) -> impl Iterator + 'a { - // let mut stream = FdtData::new(&self.data[self.header.off_mem_rsvmap.to_ne() as usize..]); - // let mut done = false; - - // core::iter::from_fn(move || { - // if stream.is_empty() || done { - // return None; - // } - - // let res = MemoryReservation::from_bytes(&mut stream)?; - - // if res.address() as usize == 0 && res.size() == 0 { - // done = true; - // return None; - // } - - // Some(res) - // }) - // } - - /// Return reference to raw data. This can be used to obtain the original pointer passed to - /// [Fdt::from_ptr]. - /// - /// # Example - /// ``` - /// # let fdt_ref: &[u8] = include_bytes!("../dtb/test.dtb"); - /// # let original_pointer = fdt_ref.as_ptr(); - /// let fdt = unsafe{fdt::Fdt::from_ptr(original_pointer)}.unwrap(); - /// assert_eq!(fdt.raw_data().as_ptr(), original_pointer); - /// ``` - // pub fn raw_data(&self) -> &'a [P::Granularity] { - // // self.structs - // } - /// Return the root (`/`) node, which is always available pub fn root(&self) -> P::Output> { let mut parser = P::new(self.structs.0, self.strings, self.structs); - P::to_output(parser.parse_root().map(|node| Root { node })) + P::to_output(parser.parse_root().map(|node| Root { node: node.fallible() })) } - /// Returns the first node that matches the node path, if you want all that - /// match the path, use `find_all_nodes`. This will automatically attempt to - /// resolve aliases if `path` is not found. - /// - /// Node paths must begin with a leading `/` and are ASCII only. Passing in - /// an invalid node path or non-ASCII node name in the path will return - /// `None`, as they will not be found within the devicetree structure. - /// - /// Note: if the address of a node name is left out, the search will find - /// the first node that has a matching name, ignoring the address portion if - /// it exists. - // pub fn find_node(&self, path: &str) -> Option> { - // let node = node::find_node(&mut FdtData::new(self.structs_block()), path, self, None); - // node.or_else(|| self.aliases()?.resolve_node(path)) - // } - - /// Searches for a node which contains a `compatible` property and contains - /// one of the strings inside of `with` - // pub fn find_compatible(&self, with: &[&str]) -> Option> { - // self.all_nodes().find(|n| { - // n.compatible().and_then(|compats| compats.all().find(|c| with.contains(c))).is_some() - // }) - // } - - /// Searches for the given `phandle` - // pub fn find_phandle(&self, phandle: u32) -> Option> { - // self.all_nodes().find(|n| { - // n.properties() - // .find(|p| p.name == "phandle") - // .and_then(|p| Some(BigEndianU32::from_bytes(p.value)?.to_ne() == phandle)) - // .unwrap_or(false) - // }) - // } - - /// Returns an iterator over all of the available nodes with the given path. - /// This does **not** attempt to find any node with the same name as the - /// provided path, if you're looking to do that, [`Fdt::all_nodes`] will - /// allow you to iterate over each node's name and filter for the desired - /// node(s). - /// - /// For example: - /// ```rust - /// static MY_FDT: &[u8] = include_bytes!("../dtb/test.dtb"); - /// - /// let fdt = fdt::Fdt::new(MY_FDT).unwrap(); - /// - /// for node in fdt.find_all_nodes("/soc/virtio_mmio") { - /// println!("{}", node.name); - /// } - /// ``` - /// prints: - /// ```notrust - /// virtio_mmio@10008000 - /// virtio_mmio@10007000 - /// virtio_mmio@10006000 - /// virtio_mmio@10005000 - /// virtio_mmio@10004000 - /// virtio_mmio@10003000 - /// virtio_mmio@10002000 - /// virtio_mmio@10001000 - /// ``` - // pub fn find_all_nodes(&self, path: &'a str) -> impl Iterator> { - // let mut done = false; - // let only_root = path == "/"; - // let valid_path = path.chars().fold(0, |acc, c| acc + if c == '/' { 1 } else { 0 }) >= 1; - - // let mut path_split = path.rsplitn(2, '/'); - // let child_name = path_split.next().unwrap(); - // let parent = match path_split.next() { - // Some("") => Some(self.root().node), - // Some(s) => node::find_node(&mut FdtData::new(self.structs_block()), s, self, None), - // None => None, - // }; - - // let (parent, bad_parent) = match parent { - // Some(parent) => (parent, false), - // None => (self.find_node("/").unwrap(), true), - // }; - - // let mut child_iter = parent.children(); - - // core::iter::from_fn(move || { - // if done || !valid_path || bad_parent { - // return None; - // } - - // if only_root { - // done = true; - // return self.find_node("/"); - // } - - // let mut ret = None; - - // #[allow(clippy::while_let_on_iterator)] - // while let Some(child) = child_iter.next() { - // if child.name.split('@').next()? == child_name { - // ret = Some(child); - // break; - // } - // } - - // ret - // }) - // } - - /// Returns an iterator over all of the nodes in the devicetree, depth-first - // pub fn all_nodes(&self) -> impl Iterator> { - // node::all_nodes(self) - // } - /// Returns an iterator over all of the strings inside of the strings block pub fn strings(&self) -> impl Iterator { let mut block = self.strings_block(); diff --git a/src/node_old.rs b/src/node_old.rs deleted file mode 100644 index cf30dd4..0000000 --- a/src/node_old.rs +++ /dev/null @@ -1,639 +0,0 @@ -// This Source Code Form is subject to the terms of the Mozilla Public License, -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at https://mozilla.org/MPL/2.0/. - -use crate::{ - parsing::{BigEndianU32, BigEndianU64, FdtData}, - standard_nodes::{Compatible, MemoryRange, MemoryRegion}, - Fdt, -}; - -const FDT_BEGIN_NODE: u32 = 1; -const FDT_END_NODE: u32 = 2; -const FDT_PROP: u32 = 3; -pub(crate) const FDT_NOP: u32 = 4; -const FDT_END: u32 = 5; - -#[derive(Debug, Clone, Copy)] -#[repr(C)] -struct FdtProperty { - len: BigEndianU32, - name_offset: BigEndianU32, -} - -impl FdtProperty { - fn from_bytes(bytes: &mut FdtData<'_>) -> Option { - let len = bytes.u32()?; - let name_offset = bytes.u32()?; - - Some(Self { len, name_offset }) - } -} - -/// A devicetree node -#[derive(Debug, Clone, Copy)] -pub struct FdtNode<'b, 'a: 'b> { - pub name: &'a str, - pub(crate) header: &'b Fdt<'a, crate::UnalignedParser<'a>>, - props: &'a [u8], - parent_props: Option<&'a [u8]>, -} - -impl core::fmt::Display for FdtNode<'_, '_> { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - crate::pretty_print::print_node(f, *self, 0)?; - Ok(()) - } -} - -impl<'b, 'a: 'b> FdtNode<'b, 'a> { - fn new( - name: &'a str, - header: &'b Fdt<'a, crate::UnalignedParser<'a>>, - props: &'a [u8], - parent_props: Option<&'a [u8]>, - ) -> Self { - Self { name, header, props, parent_props } - } - - /// Returns an iterator over the available properties of the node - pub fn properties(self) -> impl Iterator> + 'b { - let mut stream = FdtData::new(self.props); - let mut done = false; - - core::iter::from_fn(move || { - if stream.is_empty() || done { - return None; - } - - while stream.peek_u32()?.to_ne() == FDT_NOP { - stream.skip(4); - } - - if stream.peek_u32().unwrap().to_ne() == FDT_PROP { - Some(NodeProperty::parse(&mut stream, self.header)) - } else { - done = true; - None - } - }) - } - - /// Attempts to find the a property by its name - pub fn property(self, name: &str) -> Option> { - self.properties().find(|p| p.name == name) - } - - /// Returns an iterator over the children of the current node - pub fn children(self) -> impl Iterator> { - let mut stream = FdtData::new(self.props); - - while stream.peek_u32().unwrap().to_ne() == FDT_NOP { - stream.skip(4); - } - - while stream.peek_u32().unwrap().to_ne() == FDT_PROP { - NodeProperty::parse(&mut stream, self.header); - } - - let mut done = false; - - core::iter::from_fn(move || { - if stream.is_empty() || done { - return None; - } - - while stream.peek_u32()?.to_ne() == FDT_NOP { - stream.skip(4); - } - - if stream.peek_u32()?.to_ne() == FDT_BEGIN_NODE { - let origin = stream.remaining(); - let ret = { - stream.skip(4); - let unit_name = core::ffi::CStr::from_bytes_until_nul(stream.remaining()) - .expect("unit name") - .to_str() - .ok()?; - let full_name_len = unit_name.len() + 1; - stream.skip(full_name_len); - - if full_name_len % 4 != 0 { - stream.skip(4 - (full_name_len % 4)); - } - - Some(Self::new(unit_name, self.header, stream.remaining(), Some(self.props))) - }; - - stream = FdtData::new(origin); - - skip_current_node(&mut stream, self.header); - - ret - } else { - done = true; - None - } - }) - } - - /// `reg` property - /// - /// Important: this method assumes that the value(s) inside the `reg` - /// property represent CPU-addressable addresses that are able to fit within - /// the platform's pointer size (e.g. `#address-cells` and `#size-cells` are - /// less than or equal to 2 for a 64-bit platform). If this is not the case - /// or you're unsure of whether this applies to the node, it is recommended - /// to use the [`FdtNode::property`] method to extract the raw value slice - /// or use the provided [`FdtNode::raw_reg`] helper method to give you an - /// iterator over the address and size slices. One example of where this - /// would return `None` for a node is a `pci` child node which contains the - /// PCI address information in the `reg` property, of which the address has - /// an `#address-cells` value of 3. - pub fn reg(self) -> Option + 'a> { - let sizes = self.parent_cell_sizes(); - if sizes.address_cells > 2 || sizes.size_cells > 2 { - return None; - } - - let mut reg = None; - for prop in self.properties() { - if prop.name == "reg" { - let mut stream = FdtData::new(prop.value); - reg = Some(core::iter::from_fn(move || { - let starting_address = match sizes.address_cells { - 1 => stream.u32()?.to_ne() as usize, - 2 => stream.u64()?.to_ne() as usize, - _ => return None, - } as *const u8; - - let size = match sizes.size_cells { - 0 => None, - 1 => Some(stream.u32()?.to_ne() as usize), - 2 => Some(stream.u64()?.to_ne() as usize), - _ => return None, - }; - - Some(MemoryRegion { starting_address, size }) - })); - break; - } - } - - reg - } - - pub fn ranges(self) -> Option + 'a> { - let sizes = self.cell_sizes(); - let parent_sizes = self.parent_cell_sizes(); - - if sizes.address_cells > 3 || sizes.size_cells > 2 || parent_sizes.size_cells > 2 { - return None; - } - - let mut ranges = None; - for prop in self.properties() { - if prop.name == "ranges" { - let mut stream = FdtData::new(prop.value); - ranges = Some(core::iter::from_fn(move || { - let (child_bus_address_hi, child_bus_address) = match sizes.address_cells { - 1 => (0, stream.u32()?.to_ne() as usize), - 2 => (0, stream.u64()?.to_ne() as usize), - 3 => (stream.u32()?.to_ne(), stream.u64()?.to_ne() as usize), - _ => return None, - }; - - let parent_bus_address = match parent_sizes.address_cells { - 1 => stream.u32()?.to_ne() as usize, - 2 => stream.u64()?.to_ne() as usize, - _ => return None, - }; - - let size = match sizes.size_cells { - 1 => stream.u32()?.to_ne() as usize, - 2 => stream.u64()?.to_ne() as usize, - _ => return None, - }; - - Some(MemoryRange { - child_bus_address, - child_bus_address_hi, - parent_bus_address, - size, - }) - })); - break; - } - } - - ranges - } - - /// Convenience method that provides an iterator over the raw bytes for the - /// address and size values inside of the `reg` property - pub fn raw_reg(self) -> Option> + 'a> { - let sizes = self.parent_cell_sizes(); - - if let Some(prop) = self.property("reg") { - let mut stream = FdtData::new(prop.value); - return Some(core::iter::from_fn(move || { - Some(RawReg { - address: stream.take(sizes.address_cells * 4)?, - size: stream.take(sizes.size_cells * 4)?, - }) - })); - } - - None - } - - /// `compatible` property - pub fn compatible(self) -> Option> { - let mut s = None; - for prop in self.properties() { - if prop.name == "compatible" { - s = Some(Compatible { data: prop.value }); - } - } - - s - } - - /// Cell sizes for child nodes - pub fn cell_sizes(self) -> CellSizes { - let mut cell_sizes = CellSizes::default(); - - for property in self.properties() { - match property.name { - "#address-cells" => { - cell_sizes.address_cells = BigEndianU32::from_bytes(property.value) - .expect("not enough bytes for #address-cells value") - .to_ne() as usize; - } - "#size-cells" => { - cell_sizes.size_cells = BigEndianU32::from_bytes(property.value) - .expect("not enough bytes for #size-cells value") - .to_ne() as usize; - } - _ => {} - } - } - - cell_sizes - } - - /// Searches for the interrupt parent, if the node contains one - pub fn interrupt_parent(self) -> Option> { - self.properties() - .find(|p| p.name == "interrupt-parent") - .and_then(|p| self.header.find_phandle(BigEndianU32::from_bytes(p.value)?.to_ne())) - } - - /// `#interrupt-cells` property - pub fn interrupt_cells(self) -> Option { - let mut interrupt_cells = None; - - if let Some(prop) = self.property("#interrupt-cells") { - interrupt_cells = BigEndianU32::from_bytes(prop.value).map(|n| n.to_ne() as usize) - } - - interrupt_cells - } - - /// `interrupts` property - pub fn interrupts(self) -> Option + 'a> { - let sizes = self.parent_interrupt_cells()?; - - let mut interrupt = None; - for prop in self.properties() { - if prop.name == "interrupts" { - let mut stream = FdtData::new(prop.value); - interrupt = Some(core::iter::from_fn(move || { - let interrupt = match sizes { - 1 => stream.u32()?.to_ne() as usize, - 2 => stream.u64()?.to_ne() as usize, - _ => return None, - }; - - Some(interrupt) - })); - break; - } - } - - interrupt - } - - /// `interrupts-extended` property - pub fn interrupts_extended(self) -> Option + 'a> { - let sizes = self.interrupt_cells()?; - - let mut interrupt = None; - for prop in self.properties() { - if prop.name == "interrupts-extended" { - let mut stream = FdtData::new(prop.value); - interrupt = Some(core::iter::from_fn(move || { - let interrupt = match sizes { - 1 => stream.u32()?.get() as usize, - 2 => stream.u64()?.get() as usize, - _ => return None, - }; - - Some(interrupt) - })); - break; - } - } - - interrupt - } - - pub(crate) fn parent_cell_sizes(self) -> CellSizes { - let mut cell_sizes = CellSizes::default(); - - if let Some(parent) = self.parent_props { - let parent = - FdtNode { name: "", props: parent, header: self.header, parent_props: None }; - cell_sizes = parent.cell_sizes(); - } - - cell_sizes - } - - pub(crate) fn parent_interrupt_cells(self) -> Option { - let mut interrupt_cells = None; - let parent = self - .property("interrupt-parent") - .and_then(|p| self.header.find_phandle(BigEndianU32::from_bytes(p.value)?.to_ne())) - .or_else(|| { - Some(FdtNode { - name: "", - props: self.parent_props?, - header: self.header, - parent_props: None, - }) - }); - - if let Some(size) = parent.and_then(|parent| parent.interrupt_cells()) { - interrupt_cells = Some(size); - } - - interrupt_cells - } -} - -/// The number of cells (big endian u32s) that addresses and sizes take -#[derive(Debug, Clone, Copy)] -pub struct CellSizes { - /// Size of values representing an address - pub address_cells: usize, - /// Size of values representing a size - pub size_cells: usize, -} - -impl Default for CellSizes { - fn default() -> Self { - CellSizes { address_cells: 2, size_cells: 1 } - } -} - -/// A raw `reg` property value set -#[derive(Debug, Clone, Copy, PartialEq)] -pub struct RawReg<'a> { - /// Big-endian encoded bytes making up the address portion of the property. - /// Length will always be a multiple of 4 bytes. - pub address: &'a [u8], - /// Big-endian encoded bytes making up the size portion of the property. - /// Length will always be a multiple of 4 bytes. - pub size: &'a [u8], -} - -pub(crate) fn find_node<'b, 'a: 'b>( - stream: &mut FdtData<'a>, - name: &str, - header: &'b Fdt<'a, crate::UnalignedParser<'a>>, - parent_props: Option<&'a [u8]>, -) -> Option> { - let mut parts = name.splitn(2, '/'); - let looking_for = parts.next()?; - - stream.skip_nops(); - - let curr_data = stream.remaining(); - - match stream.u32()?.to_ne() { - FDT_BEGIN_NODE => {} - _ => return None, - } - - let unit_name = core::ffi::CStr::from_bytes_until_nul(stream.remaining()) - .expect("unit name C str") - .to_str() - .ok()?; - - let full_name_len = unit_name.len() + 1; - skip_4_aligned(stream, full_name_len); - - let looking_contains_addr = looking_for.contains('@'); - let addr_name_same = unit_name == looking_for; - let base_name_same = unit_name.split('@').next()? == looking_for; - - if (looking_contains_addr && !addr_name_same) || (!looking_contains_addr && !base_name_same) { - *stream = FdtData::new(curr_data); - skip_current_node(stream, header); - - return None; - } - - let next_part = match parts.next() { - None | Some("") => { - return Some(FdtNode::new(unit_name, header, stream.remaining(), parent_props)) - } - Some(part) => part, - }; - - stream.skip_nops(); - - let parent_props = Some(stream.remaining()); - - while stream.peek_u32()?.to_ne() == FDT_PROP { - let _ = NodeProperty::parse(stream, header); - } - - while stream.peek_u32()?.to_ne() == FDT_BEGIN_NODE { - if let Some(p) = find_node(stream, next_part, header, parent_props) { - return Some(p); - } - } - - stream.skip_nops(); - - if stream.u32()?.to_ne() != FDT_END_NODE { - return None; - } - - None -} - -// FIXME: this probably needs refactored -pub(crate) fn all_nodes<'b, 'a: 'b>( - header: &'b Fdt<'a, crate::UnalignedParser<'a>>, -) -> impl Iterator> { - let mut stream = FdtData::new(header.structs_block()); - let mut done = false; - let mut parents: [&[u8]; 64] = [&[]; 64]; - let mut parent_index = 0; - - core::iter::from_fn(move || { - if stream.is_empty() || done { - return None; - } - - while stream.peek_u32()?.to_ne() == FDT_END_NODE { - parent_index -= 1; - stream.skip(4); - } - - if stream.peek_u32()?.to_ne() == FDT_END { - done = true; - return None; - } - - while stream.peek_u32()?.to_ne() == FDT_NOP { - stream.skip(4); - } - - match stream.u32()?.to_ne() { - FDT_BEGIN_NODE => {} - _ => return None, - } - - let unit_name = core::ffi::CStr::from_bytes_until_nul(stream.remaining()) - .expect("unit name C str") - .to_str() - .unwrap(); - let full_name_len = unit_name.len() + 1; - skip_4_aligned(&mut stream, full_name_len); - - let curr_node = stream.remaining(); - - parent_index += 1; - parents[parent_index] = curr_node; - - while stream.peek_u32()?.to_ne() == FDT_NOP { - stream.skip(4); - } - - while stream.peek_u32()?.to_ne() == FDT_PROP { - NodeProperty::parse(&mut stream, header); - } - - Some(FdtNode { - name: if unit_name.is_empty() { "/" } else { unit_name }, - header, - parent_props: match parent_index { - 1 => None, - _ => Some(parents[parent_index - 1]), - }, - props: curr_node, - }) - }) -} - -pub(crate) fn skip_current_node<'a>( - stream: &mut FdtData<'a>, - header: &Fdt<'a, crate::UnalignedParser<'a>>, -) { - assert_eq!(stream.u32().unwrap().to_ne(), FDT_BEGIN_NODE, "bad node"); - - let unit_name = core::ffi::CStr::from_bytes_until_nul(stream.remaining()) - .expect("unit_name C str") - .to_str() - .unwrap(); - let full_name_len = unit_name.len() + 1; - skip_4_aligned(stream, full_name_len); - - while stream.peek_u32().unwrap().to_ne() == FDT_PROP { - NodeProperty::parse(stream, header); - } - - while stream.peek_u32().unwrap().to_ne() == FDT_BEGIN_NODE { - skip_current_node(stream, header); - } - - stream.skip_nops(); - - assert_eq!(stream.u32().unwrap().to_ne(), FDT_END_NODE, "bad node"); -} - -/// A node property -#[derive(Debug, Clone, Copy)] -pub struct NodeProperty<'a> { - /// Property name - pub name: &'a str, - /// Property value - pub value: &'a [u8], -} - -impl<'a> NodeProperty<'a> { - /// Attempt to parse the property value as a `usize` - pub fn as_usize(self) -> Option { - match self.value.len() { - 4 => BigEndianU32::from_bytes(self.value).map(|i| i.to_ne() as usize), - 8 => BigEndianU64::from_bytes(self.value).map(|i| i.to_ne() as usize), - _ => None, - } - } - - /// Attempt to parse the property value as a `&str` - pub fn as_str(self) -> Option<&'a str> { - core::str::from_utf8(self.value).map(|s| s.trim_end_matches('\0')).ok() - } - - fn parse(stream: &mut FdtData<'a>, header: &Fdt<'a, crate::UnalignedParser<'a>>) -> Self { - match stream.u32().unwrap().to_ne() { - FDT_PROP => {} - other => panic!("bad prop, tag: {}", other), - } - - let prop = FdtProperty::from_bytes(stream).expect("FDT property"); - let data_len = prop.len.to_ne() as usize; - - let data = &stream.remaining()[..data_len]; - - skip_4_aligned(stream, data_len); - - NodeProperty { name: header.str_at_offset(prop.name_offset.to_ne() as usize), value: data } - } -} - -/// A memory reservation -#[derive(Debug)] -#[repr(C)] -pub struct MemoryReservation { - pub(crate) address: BigEndianU64, - pub(crate) size: BigEndianU64, -} - -impl MemoryReservation { - /// Pointer representing the memory reservation address - pub fn address(&self) -> *const u8 { - self.address.to_ne() as usize as *const u8 - } - - /// Size of the memory reservation - pub fn size(&self) -> usize { - self.size.to_ne() as usize - } - - pub(crate) fn from_bytes(bytes: &mut FdtData<'_>) -> Option { - let address = bytes.u64()?; - let size = bytes.u64()?; - - Some(Self { address, size }) - } -} - -fn skip_4_aligned(stream: &mut FdtData<'_>, len: usize) { - stream.skip((len + 3) & !0x3); -} diff --git a/src/nodes.rs b/src/nodes.rs index 7687f96..6df2169 100644 --- a/src/nodes.rs +++ b/src/nodes.rs @@ -1,6 +1,10 @@ pub mod aliases; +pub mod chosen; pub mod cpus; pub mod memory; +pub mod root; + +use root::Root; use crate::{ parsing::{ @@ -8,11 +12,11 @@ use crate::{ StringsBlock, StructsBlock, }, properties::{ + ranges::Ranges, reg::Reg, values::{InvalidPropertyValue, PropertyValue}, Property, }, - standard_nodes::Root, FdtError, }; @@ -27,6 +31,11 @@ macro_rules! tryblock { }}; } +/// Trait for extracting a [`Node`] from a wrapper type. +pub trait AsNode<'a, P: ParserWithMode<'a>> { + fn as_node(&self) -> Node<'a, P>; +} + #[derive(Debug, Clone, Copy)] pub enum SearchableNodeName<'a> { Base(&'a str), @@ -78,6 +87,7 @@ impl core::fmt::Display for NodeName<'_> { } pub type FallibleNode<'a, P> = Node<'a, (

>::Parser, NoPanic)>; +pub type FallibleRoot<'a, P> = Root<'a, (

>::Parser, NoPanic)>; pub struct Node<'a, P: ParserWithMode<'a>> { pub(crate) this: &'a RawNode<

>::Granularity>, @@ -90,7 +100,7 @@ pub struct Node<'a, P: ParserWithMode<'a>> { impl<'a, P: ParserWithMode<'a>> Node<'a, P> { /// Change the type of this node's [`PanicMode`] to [`NoPanic`] #[inline(always)] - pub(crate) fn fallible(self) -> Node<'a, (P::Parser, NoPanic)> { + pub(crate) fn fallible(self) -> FallibleNode<'a, P> { self.alt() } @@ -130,10 +140,15 @@ impl<'a, P: ParserWithMode<'a>> Node<'a, P> { ) } + #[inline(always)] pub fn reg(&self) -> P::Output>> { self.property::>() } + pub fn ranges(&self) -> P::Output>> { + self.property() + } + #[inline] pub fn properties(&self) -> P::Output> { let mut parser = P::new(&self.this.0, self.strings, self.structs); @@ -197,6 +212,12 @@ impl<'a, P: ParserWithMode<'a>> Node<'a, P> { } } +impl<'a, P: ParserWithMode<'a>> AsNode<'a, P> for Node<'a, P> { + fn as_node(&self) -> Node<'a, P> { + *self + } +} + impl<'a, P: ParserWithMode<'a>> core::fmt::Debug for Node<'a, P> where P::Output>: core::fmt::Debug, diff --git a/src/nodes/aliases.rs b/src/nodes/aliases.rs index 2506454..09fce04 100644 --- a/src/nodes/aliases.rs +++ b/src/nodes/aliases.rs @@ -1,4 +1,4 @@ -use super::{FallibleNode, Node}; +use super::{AsNode, FallibleNode, Node}; use crate::parsing::{NoPanic, ParserWithMode}; /// [Devicetree 3.3. `/aliases` @@ -57,3 +57,9 @@ impl<'a, P: ParserWithMode<'a>> Aliases<'a, P> { })) } } + +impl<'a, P: ParserWithMode<'a>> AsNode<'a, P> for Aliases<'a, P> { + fn as_node(&self) -> Node<'a, P> { + self.node.alt() + } +} diff --git a/src/nodes/chosen.rs b/src/nodes/chosen.rs new file mode 100644 index 0000000..8a33331 --- /dev/null +++ b/src/nodes/chosen.rs @@ -0,0 +1,140 @@ +// This Source Code Form is subject to the terms of the Mozilla Public License, +// v. 2.0. If a copy of the MPL was not distributed with this file, You can +// obtain one at https://mozilla.org/MPL/2.0/. + +use crate::{ + parsing::{aligned::AlignedParser, Panic, ParseError, ParserWithMode}, + FdtError, +}; + +use super::FallibleNode; + +/// [Devicetree 3.6. `/chosen` +/// Node](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#chosen-node) +/// +/// The `/chosen` node does not represent a real device in the system but +/// describes parameters chosen or specified by the system firmware at run time. +/// It shall be a child of the root node. +pub struct Chosen<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { + pub(crate) node: FallibleNode<'a, P>, +} + +impl<'a, P: ParserWithMode<'a>> Chosen<'a, P> { + /// Contains the bootargs, if they exist + #[track_caller] + pub fn bootargs(self) -> P::Output> { + P::to_output(crate::tryblock!({ + for prop in self.node.properties()?.into_iter().flatten() { + if prop.name() == "bootargs" { + return Ok(Some( + core::str::from_utf8(&prop.value()[..prop.value().len() - 1]) + .map_err(|_| FdtError::ParseError(ParseError::InvalidCStrValue))?, + )); + } + } + + Ok(None) + })) + } + + /// Looks up the `stdout-path` property and returns the [`StdInOutPath`] + /// representing the path. The path may be an alias and require being + /// resolved with [`Alias::resolve`] before being used in conjunction with + /// [`Root::find_node`]. For more information about the path parameters, see + /// [`StdInOutPath::params`]. + #[track_caller] + pub fn stdout(self) -> P::Output>> { + P::to_output(crate::tryblock!({ + self.node + .properties()? + .into_iter() + .find_map(|n| match n { + Err(e) => Some(Err(e)), + Ok(property) => match property.name() == "stdout-path" { + false => None, + true => Some(property.as_value::<&'a str>().map_err(Into::into).map(|s| { + let (path, params) = + s.split_once(':').map_or_else(|| (s, None), |(name, params)| (name, Some(params))); + StdInOutPath { path, params } + })), + }, + }) + .transpose() + })) + } + + /// Looks up the `stdin-path` property and returns the [`StdInOutPath`] + /// representing the path. The path may be an alias and require being + /// resolved with [`Alias::resolve`] before being used in conjunction with + /// [`Root::find_node`]. For more information about the path parameters, see + /// [`StdInOutPath::params`]. + #[track_caller] + pub fn stdin(self) -> P::Output>> { + P::to_output(crate::tryblock!({ + self.node + .properties()? + .into_iter() + .find_map(|n| match n { + Err(e) => Some(Err(e)), + Ok(property) => match property.name() == "stdin-path" { + false => None, + true => Some(property.as_value::<&str>().map_err(Into::into).map(|s| { + let (path, params) = + s.split_once(':').map_or_else(|| (s, None), |(name, params)| (name, Some(params))); + StdInOutPath { path, params } + })), + }, + }) + .transpose() + })) + } +} + +impl<'a, P: ParserWithMode<'a>> Clone for Chosen<'a, P> { + fn clone(&self) -> Self { + *self + } +} + +impl<'a, P: ParserWithMode<'a>> Copy for Chosen<'a, P> {} + +pub struct StdInOutPath<'a> { + path: &'a str, + params: Option<&'a str>, +} + +impl<'a> StdInOutPath<'a> { + /// Path to the node representing the stdin/stdout device. This node path + /// may be an alias, which can be resolved with [`Aliases::resolve`]. To be + /// used in conjunction with [`Root::find_node`]. + pub fn path(&self) -> &'a str { + self.path + } + + /// Optional parameters specified by the stdin/stdout property value. See + /// https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#chosen-node + /// + /// Example: + /// + /// ```dts + /// / { + /// chosen { + /// stdout-path = "/soc/uart@10000000:115200"; + /// stdin-path = "/soc/uart@10000000"; + /// } + /// } + /// ``` + /// + /// ```rust + /// # let fdt = fdt::Fdt::new_unaligned(include_bytes!("../../dtb/test.dtb")).unwrap(); + /// # let chosen = fdt.root().chosen(); + /// let stdout = chosen.stdout().unwrap(); + /// let stdin = chosen.stdin().unwrap(); + /// + /// assert_eq!((stdout.path(), stdout.params()), ("/soc/uart@10000000", Some("115200"))); + /// assert_eq!((stdin.path(), stdin.params()), ("/soc/uart@10000000", None)); + /// ``` + pub fn params(&self) -> Option<&'a str> { + self.params + } +} diff --git a/src/nodes/cpus.rs b/src/nodes/cpus.rs index 78cee37..7535efa 100644 --- a/src/nodes/cpus.rs +++ b/src/nodes/cpus.rs @@ -1,6 +1,6 @@ use crate::{ cell_collector::{BuildCellCollector, CellCollector, CollectCellsError}, - parsing::{aligned::AlignedParser, Panic, ParserWithMode}, + parsing::{aligned::AlignedParser, NoPanic, Panic, ParserWithMode}, properties::{ cells::{AddressCells, CellSizes}, values::StringList, @@ -8,7 +8,7 @@ use crate::{ FdtError, }; -use super::FallibleNode; +use super::{AsNode, FallibleNode, NodeChildrenIter}; /// [Devicetree 3.7. /// `/cpus`](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#cpus-node-properties) @@ -22,6 +22,7 @@ pub struct Cpus<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { impl<'a, P: ParserWithMode<'a>> Cpus<'a, P> { /// Retrieve the `#address-cells` and `#size-cells` values from this node + #[track_caller] pub fn cell_sizes(&self) -> P::Output { P::to_output( self.node.property().and_then(|p| p.ok_or(FdtError::MissingRequiredProperty("#address-cells/#size-cells"))), @@ -32,6 +33,7 @@ impl<'a, P: ParserWithMode<'a>> Cpus<'a, P> { /// node, which will only exist if there is a common value between the child /// `cpu` nodes. See [`Cpu::timebase_frequency`] for documentation about the /// `timebase-frequency` property. + #[track_caller] pub fn common_timebase_frequency(&self) -> P::Output> { P::to_output(crate::tryblock!({ match self.node.properties()?.find("timebase-frequency")? { @@ -49,6 +51,7 @@ impl<'a, P: ParserWithMode<'a>> Cpus<'a, P> { /// node, which will only exist if there is a common value between the child /// `cpu` nodes. See [`Cpu::clock_frequency`] for documentation about the /// `clock-frequency` property. + #[track_caller] pub fn common_clock_frequency(&self) -> P::Output> { P::to_output(crate::tryblock!({ match self.node.properties()?.find("clock-frequency")? { @@ -61,6 +64,32 @@ impl<'a, P: ParserWithMode<'a>> Cpus<'a, P> { } })) } + + pub fn cpus(&self) -> P::Output> { + P::to_output(crate::tryblock!({ Ok(CpusIter { children: self.node.children()?.iter() }) })) + } +} + +impl<'a, P: ParserWithMode<'a>> AsNode<'a, P> for Cpus<'a, P> { + fn as_node(&self) -> super::Node<'a, P> { + self.node.alt() + } +} + +pub struct CpusIter<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { + children: NodeChildrenIter<'a, (P::Parser, NoPanic)>, +} + +impl<'a, P: ParserWithMode<'a>> Iterator for CpusIter<'a, P> { + type Item = P::Output>; + + #[track_caller] + fn next(&mut self) -> Option { + match self.children.next()? { + Ok(node) => Some(P::to_output(Ok(Cpu { node }))), + Err(e) => Some(P::to_output(Err(e))), + } + } } /// [Devicetree 3.8. @@ -90,8 +119,8 @@ pub struct Cpu<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { } impl<'a, P: ParserWithMode<'a>> Cpu<'a, P> { - /// [Devicetree 3.8.1 - /// `reg`](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#general-properties-of-cpus-cpu-nodes) + /// [Devicetree 3.8.1 General Properties of `/cpus/cpu*` + /// nodes](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#general-properties-of-cpus-cpu-nodes) /// /// **Required** /// @@ -117,6 +146,8 @@ impl<'a, P: ParserWithMode<'a>> Cpu<'a, P> { /// PIR cannot be modified and the PIR value is distinct from the interrupt /// controller number space, the CPUs binding may define a binding-specific /// representation of PIR values if desired. + #[inline] + #[track_caller] pub fn reg(self) -> P::Output> { P::to_output(crate::tryblock!({ let Some(reg) = self.node.properties()?.find("reg")? else { @@ -135,8 +166,8 @@ impl<'a, P: ParserWithMode<'a>> Cpu<'a, P> { })) } - /// [Devicetree 3.8.1 - /// `clock-frequency`](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#general-properties-of-cpus-cpu-nodes) + /// [Devicetree 3.8.1 General Properties of `/cpus/cpu*` + /// nodes](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#general-properties-of-cpus-cpu-nodes) /// /// **Required** /// @@ -145,6 +176,8 @@ impl<'a, P: ParserWithMode<'a>> Cpu<'a, P> { /// /// * A 32-bit integer consisting of one `` specifying the frequency. /// * A 64-bit integer represented as a `` specifying the frequency. + #[inline] + #[track_caller] pub fn clock_frequency(self) -> P::Output { P::to_output(crate::tryblock!({ match self.node.properties()?.find("clock-frequency")? { @@ -172,17 +205,19 @@ impl<'a, P: ParserWithMode<'a>> Cpu<'a, P> { })) } - /// [Devicetree 3.8.1 - /// `timebase-frequency`](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#general-properties-of-cpus-cpu-nodes) + /// [Devicetree 3.8.1 General Properties of `/cpus/cpu*` + /// nodes](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#general-properties-of-cpus-cpu-nodes) /// /// **Required** /// /// Specifies the current frequency at which the timebase and decrementer - /// registers are updated (in Hertz). The value is a `` in - /// one of two forms: + /// registers are updated (in Hertz). The value is a `` + /// in one of two forms: /// /// * A 32-bit integer consisting of one `` specifying the frequency. /// * A 64-bit integer represented as a ``. + #[inline] + #[track_caller] pub fn timebase_frequency(self) -> P::Output { P::to_output(crate::tryblock!({ match self.node.properties()?.find("timebase-frequency")? { @@ -210,8 +245,8 @@ impl<'a, P: ParserWithMode<'a>> Cpu<'a, P> { })) } - /// [Devicetree 3.8.1 - /// `status`](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#general-properties-of-cpus-cpu-nodes) + /// [Devicetree 3.8.1 General Properties of `/cpus/cpu*` + /// nodes](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#general-properties-of-cpus-cpu-nodes) /// /// A standard property describing the state of a CPU. This property shall /// be present for nodes representing CPUs in a symmetric multiprocessing @@ -239,6 +274,8 @@ impl<'a, P: ParserWithMode<'a>> Cpu<'a, P> { /// /// A CPU with `"fail"` status does not affect the system in any way. The /// status is assigned to nodes for which no corresponding CPU exists. + #[inline] + #[track_caller] pub fn status(&self) -> P::Output> { P::to_output(crate::tryblock!({ let Some(status) = self.node.properties()?.find("status")? else { @@ -249,8 +286,8 @@ impl<'a, P: ParserWithMode<'a>> Cpu<'a, P> { })) } - /// [Devicetree 3.8.1 - /// `enable-method`](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#general-properties-of-cpus-cpu-nodes) + /// [Devicetree 3.8.1 General Properties of `/cpus/cpu*` + /// nodes](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#general-properties-of-cpus-cpu-nodes) /// /// Describes the method by which a CPU in a disabled state is enabled. This /// property is required for CPUs with a status property with a value of @@ -268,6 +305,8 @@ impl<'a, P: ParserWithMode<'a>> Cpu<'a, P> { /// describing the vendor specific mechanism. /// /// Example: `"fsl,MPC8572DS"` + #[inline] + #[track_caller] pub fn enable_method(&self) -> P::Output> { P::to_output(crate::tryblock!({ let Some(status) = self.node.properties()?.find("enable-method")? else { @@ -283,6 +322,136 @@ impl<'a, P: ParserWithMode<'a>> Cpu<'a, P> { Ok(Some(CpuEnableMethods(s.into()))) })) } + + /// [Devicetree 3.8.1 General Properties of `/cpus/cpu*` + /// nodes](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#table-10) + /// + /// Specifies the CPU’s MMU type. + #[inline] + #[track_caller] + pub fn mmu_type(&self) -> P::Output> { + P::to_output(self.node.properties().and_then(|p| { + p.find("mmu-type").and_then(|p| match p { + Some(p) => Ok(Some(p.as_value()?)), + None => Ok(None), + }) + })) + } + + /// [Devicetree 3.8.2. TLB + /// Properties](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#tlb-properties) + /// + /// If present specifies that the TLB has a split configuration, with + /// separate TLBs for instructions and data. If absent, specifies that the + /// TLB has a unified configuration. Required for a CPU with a TLB in a + /// split configuration. + #[inline] + #[track_caller] + pub fn tlb_split(&self) -> P::Output { + P::to_output(self.node.properties().and_then(|p| p.find("tlb-split").map(|p| p.is_some()))) + } + + /// [Devicetree 3.8.2. TLB + /// Properties](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#tlb-properties) + /// + /// Specifies the number of entries in the TLB. Required for a CPU with a + /// unified TLB for instruction and data addresses. + #[inline] + #[track_caller] + pub fn tlb_size(&self) -> P::Output> { + P::to_output(self.node.properties().and_then(|p| { + p.find("tlb-size").and_then(|p| match p { + Some(p) => Ok(Some(p.as_value()?)), + None => Ok(None), + }) + })) + } + + /// [Devicetree 3.8.2. TLB + /// Properties](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#tlb-properties) + /// + /// Specifies the number of associativity sets in the TLB. Required for a + /// CPU with a unified TLB for instruction and data addresses. + #[inline] + #[track_caller] + pub fn tlb_sets(&self) -> P::Output> { + P::to_output(self.node.properties().and_then(|p| { + p.find("tlb-sets").and_then(|p| match p { + Some(p) => Ok(Some(p.as_value()?)), + None => Ok(None), + }) + })) + } + + /// [Devicetree 3.8.2. TLB + /// Properties](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#tlb-properties) + /// + /// Specifies the number of entries in the data TLB. Required for a CPU with + /// a split TLB configuration. + #[inline] + #[track_caller] + pub fn d_tlb_size(&self) -> P::Output> { + P::to_output(self.node.properties().and_then(|p| { + p.find("d-tlb-size").and_then(|p| match p { + Some(p) => Ok(Some(p.as_value()?)), + None => Ok(None), + }) + })) + } + + /// [Devicetree 3.8.2. TLB + /// Properties](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#tlb-properties) + /// + /// Specifies the number of associativity sets in the data TLB. Required for + /// a CPU with a split TLB configuration. + #[inline] + #[track_caller] + pub fn d_tlb_sets(&self) -> P::Output> { + P::to_output(self.node.properties().and_then(|p| { + p.find("d-tlb-sets").and_then(|p| match p { + Some(p) => Ok(Some(p.as_value()?)), + None => Ok(None), + }) + })) + } + + /// [Devicetree 3.8.2. TLB + /// Properties](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#tlb-properties) + /// + /// Specifies the number of entries in the instruction TLB. Required for a + /// CPU with a split TLB configuration. + #[inline] + #[track_caller] + pub fn i_tlb_size(&self) -> P::Output> { + P::to_output(self.node.properties().and_then(|p| { + p.find("i-tlb-size").and_then(|p| match p { + Some(p) => Ok(Some(p.as_value()?)), + None => Ok(None), + }) + })) + } + + /// [Devicetree 3.8.2. TLB + /// Properties](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#tlb-properties) + /// + /// Specifies the number of associativity sets in the instruction TLB. + /// Required for a CPU with a split TLB configuration. + #[inline] + #[track_caller] + pub fn i_tlb_sets(&self) -> P::Output> { + P::to_output(self.node.properties().and_then(|p| { + p.find("i-tlb-sets").and_then(|p| match p { + Some(p) => Ok(Some(p.as_value()?)), + None => Ok(None), + }) + })) + } +} + +impl<'a, P: ParserWithMode<'a>> AsNode<'a, P> for Cpu<'a, P> { + fn as_node(&self) -> super::Node<'a, P> { + self.node.alt() + } } #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] diff --git a/src/nodes/memory.rs b/src/nodes/memory.rs index a1e6409..5514072 100644 --- a/src/nodes/memory.rs +++ b/src/nodes/memory.rs @@ -1,12 +1,46 @@ use crate::{ - parsing::{aligned::AlignedParser, Panic, ParserWithMode}, - properties::{cells::CellSizes, reg::Reg}, + cell_collector::{BuildCellCollector, CellCollector, CollectCellsError}, + parsing::{aligned::AlignedParser, NoPanic, Panic, ParserWithMode}, + properties::{ + cells::{CellSizes, SizeCells}, + reg::Reg, + Compatible, + }, FdtError, }; -use super::FallibleNode; +use super::{AsNode, FallibleNode, NodeChildrenIter, NodeName}; -/// Represents the `/memory` node with specific helper methods +/// [Devicetree 3.4. `/memory` +/// node](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#memory-node) +/// +/// A memory device node is required for all devicetrees and describes the +/// physical memory layout for the system. If a system has multiple ranges of +/// memory, multiple memory nodes can be created, or the ranges can be specified +/// in the `reg` property of a single memory node. +/// +/// The unit-name component of the node name (see [Section +/// 2.2.1](https://devicetree-specification.readthedocs.io/en/latest/chapter2-devicetree-basics.html#sect-node-names)) +/// shall be memory. +/// +/// The client program may access memory not covered by any memory reservations +/// (see [Section +/// 5.3](https://devicetree-specification.readthedocs.io/en/latest/chapter5-flattened-format.html#sect-fdt-memory-reservation-block)) +/// using any storage attributes it chooses. However, before changing the +/// storage attributes used to access a real page, the client program is +/// responsible for performing actions required by the architecture and +/// implementation, possibly including flushing the real page from the caches. +/// The boot program is responsible for ensuring that, without taking any action +/// associated with a change in storage attributes, the client program can +/// safely access all memory (including memory covered by memory reservations) +/// as `WIMG = 0b001x`. That is: +/// +/// * not Write Through Required +/// * not Caching Inhibited +/// * Memory Coherence +/// * Required either not Guarded or Guarded +/// +/// If the VLE storage attribute is supported, with `VLE=0`. #[derive(Debug, Clone, Copy)] pub struct Memory<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { pub(crate) node: FallibleNode<'a, P>, @@ -20,6 +54,7 @@ impl<'a, P: ParserWithMode<'a>> Memory<'a, P> { /// /// Consists of an arbitrary number of address and size pairs that specify /// the physical address and size of the memory ranges. + #[track_caller] pub fn reg(&self) -> P::Output> { P::to_output(self.node.reg().and_then(|m| m.ok_or(FdtError::MissingRequiredNode("reg")))) } @@ -35,6 +70,7 @@ impl<'a, P: ParserWithMode<'a>> Memory<'a, P> { /// physical address, size). The effective and physical address shall each /// be 64-bit (`` value), and the size shall be 32-bits (`` /// value). + #[track_caller] pub fn initial_mapped_area(&self) -> P::Output> { P::to_output(crate::tryblock!({ match self.node.properties()?.find("initial-mapped-area")? { @@ -60,11 +96,18 @@ impl<'a, P: ParserWithMode<'a>> Memory<'a, P> { /// /// Specifies an explicit hint to the operating system that this memory may /// potentially be removed later. + #[track_caller] pub fn hotpluggable(&self) -> P::Output { P::to_output(crate::tryblock!({ Ok(self.node.properties()?.find("hotpluggable")?.is_some()) })) } } +impl<'a, P: ParserWithMode<'a>> AsNode<'a, P> for Memory<'a, P> { + fn as_node(&self) -> super::Node<'a, P> { + self.node.alt() + } +} + /// Describes the initial mapped area of the `/memory` node. See /// [`Memory::initial_mapped_area`]. #[allow(missing_docs)] @@ -88,6 +131,8 @@ pub struct ReservedMemory<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic) } impl<'a, P: ParserWithMode<'a>> ReservedMemory<'a, P> { + #[inline] + #[track_caller] pub fn cell_sizes(&self) -> P::Output { P::to_output( self.node @@ -96,39 +141,195 @@ impl<'a, P: ParserWithMode<'a>> ReservedMemory<'a, P> { ) } - pub fn children(&self) -> ReservedMemoryChildren<'a, P> { - ReservedMemoryChildren { node: self.node } + #[inline] + #[track_caller] + pub fn children(&self) -> P::Output> { + P::to_output(crate::tryblock!({ Ok(ReservedMemoryChildrenIter { children: self.node.children()?.iter() }) })) } } -pub struct ReservedMemoryChildren<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { - node: FallibleNode<'a, P>, +impl<'a, P: ParserWithMode<'a>> AsNode<'a, P> for ReservedMemory<'a, P> { + fn as_node(&self) -> super::Node<'a, P> { + self.node.alt() + } } -impl<'a, P: ParserWithMode<'a>> ReservedMemoryChildren<'a, P> { - pub fn all(&self) -> P::Output> {} +pub struct ReservedMemoryChildrenIter<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { + children: NodeChildrenIter<'a, (P::Parser, NoPanic)>, +} + +impl<'a, P: ParserWithMode<'a>> Iterator for ReservedMemoryChildrenIter<'a, P> { + type Item = P::Output>; + + #[track_caller] + fn next(&mut self) -> Option { + match self.children.next()? { + Ok(node) => Some(P::to_output(Ok(ReservedMemoryChild { node }))), + Err(e) => Some(P::to_output(Err(e))), + } + } } pub struct ReservedMemoryChild<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { node: FallibleNode<'a, P>, } +impl<'a, P: ParserWithMode<'a>> ReservedMemoryChild<'a, P> { + pub fn name(&self) -> P::Output> { + P::to_output(self.node.name()) + } + + /// [Devicetree 3.5.2. `/reserved-memory` child + /// nodes](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#table-5) + /// + /// **Optional** + /// + /// Consists of an arbitrary number of address and size pairs that specify + /// the physical address and size of the memory ranges. + pub fn reg(&self) -> P::Output>> { + P::to_output(self.node.reg()) + } + + /// [Devicetree 3.5.2. `/reserved-memory` child + /// nodes](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#table-5) + /// + /// **Optional** + /// + /// Size in bytes of memory to reserve for dynamically allocated regions. + /// Size of this property is based on parent node’s `#size-cells` property. + pub fn size(&self) -> P::Output>> { + P::to_output(crate::tryblock!({ + let Some(size) = self.node.properties()?.find("size")? else { + return Ok(None); + }; + + // Unwrap: nodes will always have parents because they are created + // from the `NodeChildrenIter` struct + let size_cells = self.node.parent().unwrap().property::()?.unwrap_or(SizeCells(1)); + + if size.value().len() % size_cells.0 != 0 { + return Err(FdtError::InvalidPropertyValue); + } + + let mut builder = ::Builder::default(); + + for component in size.value().chunks_exact(4) { + if builder.push(u32::from_be_bytes(component.try_into().unwrap())).is_err() { + return Ok(Some(Err(CollectCellsError))); + } + } + + Ok(Some(Ok(C::map(builder.finish())))) + })) + } + + /// [Devicetree 3.5.2. `/reserved-memory` child + /// nodes](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#table-5) + /// + /// **Optional** + /// + /// Address boundary for alignment of allocation. Size of this property is + /// based on parent node’s `#size-cells` property. + pub fn alignment(&self) -> P::Output>> { + P::to_output(crate::tryblock!({ + let Some(alignment) = self.node.properties()?.find("alignment")? else { + return Ok(None); + }; + + // Unwrap: nodes will always have parents because they are created + // from the `NodeChildrenIter` struct + let size_cells = self.node.parent().unwrap().property::()?.unwrap_or(SizeCells(1)); + + if alignment.value().len() % size_cells.0 != 0 { + return Err(FdtError::InvalidPropertyValue); + } + + let mut builder = ::Builder::default(); + + for component in alignment.value().chunks_exact(4) { + if builder.push(u32::from_be_bytes(component.try_into().unwrap())).is_err() { + return Ok(Some(Err(CollectCellsError))); + } + } + + Ok(Some(Ok(C::map(builder.finish())))) + })) + } + + /// [Devicetree 3.5.2. `/reserved-memory` child + /// nodes](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#table-5) + /// + /// **Optional** + /// + /// May contain the following strings: + /// + /// * `shared-dma-pool`: This indicates a region of memory meant to be used + /// as a shared pool of DMA buffers for a set of devices. It can be used by + /// an operating system to instantiate the necessary pool management + /// subsystem if necessary. + /// + /// * vendor specific string in the form `,[-]` + pub fn compatible(&self) -> P::Output>> { + P::to_output(self.node.property::>()) + } + + /// [Devicetree 3.5.2. `/reserved-memory` child + /// nodes](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#table-5) + /// + /// **Optional** + /// + /// If present, indicates the operating system must not create a virtual + /// mapping of the region as part of its standard mapping of system memory, + /// nor permit speculative access to it under any circumstances other than + /// under the control of the device driver using the region. + pub fn no_map(&self) -> P::Output { + P::to_output(self.node.properties().and_then(|p| p.find("no-map").map(|p| p.is_some()))) + } + + /// [Devicetree 3.5.2. `/reserved-memory` child + /// nodes](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#table-5) + /// + /// **Optional** + /// + /// The operating system can use the memory in this region with the + /// limitation that the device driver(s) owning the region need to be able + /// to reclaim it back. Typically that means that the operating system can + /// use that region to store volatile or cached data that can be otherwise + /// regenerated or migrated elsewhere. + pub fn reusable(&self) -> P::Output { + P::to_output(self.node.properties().and_then(|p| p.find("no-map").map(|p| p.is_some()))) + } + + /// [Devicetree 3.5.2. `/reserved-memory` child + /// nodes](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#table-5) + /// + /// If a `linux,cma-default` property is present, then Linux will use the + /// region for the default pool of the contiguous memory allocator. + #[cfg(feature = "linux-dt-bindings")] + pub fn cma_default(&self) -> P::Output { + P::to_output(self.node.properties().and_then(|p| p.find("no-map").map(|p| p.is_some()))) + } + + /// [Devicetree 3.5.2. `/reserved-memory` child + /// nodes](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#table-5) + /// + /// If a `linux,dma-default` property is present, then Linux will use the + /// region for the default pool of the consistent DMA allocator. + #[cfg(feature = "linux-dt-bindings")] + pub fn dma_default(&self) -> P::Output { + P::to_output(self.node.properties().and_then(|p| p.find("no-map").map(|p| p.is_some()))) + } +} + +impl<'a, P: ParserWithMode<'a>> AsNode<'a, P> for ReservedMemoryChild<'a, P> { + fn as_node(&self) -> super::Node<'a, P> { + self.node.alt() + } +} + /// A memory region #[derive(Debug, Clone, Copy, PartialEq)] pub struct MemoryRegion { pub starting_address: u64, pub size: Option, } - -/// Range mapping child bus addresses to parent bus addresses -#[derive(Debug, Clone, Copy, PartialEq)] -pub struct MemoryRange { - /// Starting address on child bus - pub child_bus_address: usize, - /// The high bits of the child bus' starting address, if present - pub child_bus_address_hi: u32, - /// Starting address on parent bus - pub parent_bus_address: usize, - /// Size of range - pub size: usize, -} diff --git a/src/standard_nodes.rs b/src/nodes/root.rs similarity index 68% rename from src/standard_nodes.rs rename to src/nodes/root.rs index 0a38e22..6f73aab 100644 --- a/src/standard_nodes.rs +++ b/src/nodes/root.rs @@ -1,148 +1,24 @@ -// This Source Code Form is subject to the terms of the Mozilla Public License, -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at https://mozilla.org/MPL/2.0/. - use crate::{ - cell_collector::{BuildCellCollector, CellCollector, CollectCellsError}, - nodes::{FallibleNode, IntoSearchableNodeName, Node, RawNode, SearchableNodeName}, parsing::{aligned::AlignedParser, BigEndianToken, NoPanic, Panic, ParseError, Parser, ParserWithMode}, - properties::{ - cells::{AddressCells, CellSizes}, - values::StringList, - Compatible, PHandle, Property, - }, - tryblock, FdtError, + properties::{cells::CellSizes, Compatible, PHandle, Property}, + FdtError, }; -/// Represents the `/chosen` node with specific helper methods -pub struct Chosen<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { - pub(crate) node: Node<'a, P>, -} - -impl<'a, P: ParserWithMode<'a>> Chosen<'a, P> { - /// Contains the bootargs, if they exist - #[track_caller] - pub fn bootargs(self) -> P::Output> { - P::to_output(crate::tryblock!({ - let node = self.node.fallible(); - for prop in node.properties()?.into_iter().flatten() { - if prop.name() == "bootargs" { - return Ok(Some( - core::str::from_utf8(&prop.value()[..prop.value().len() - 1]) - .map_err(|_| FdtError::ParseError(ParseError::InvalidCStrValue))?, - )); - } - } - - Ok(None) - })) - } - - /// Looks up the `stdout-path` property and returns the [`StdInOutPath`] - /// representing the path. The path may be an alias and require being - /// resolved with [`Alias::resolve`] before being used in conjunction with - /// [`Root::find_node`]. For more information about the path parameters, see - /// [`StdInOutPath::params`]. - #[track_caller] - pub fn stdout(self) -> P::Output>> { - P::to_output(crate::tryblock!({ - let node = self.node.fallible(); - node.properties()? - .into_iter() - .find_map(|n| match n { - Err(e) => Some(Err(e)), - Ok(property) => match property.name() == "stdout-path" { - false => None, - true => Some(property.as_value::<&'a str>().map_err(Into::into).map(|s| { - let (path, params) = - s.split_once(':').map_or_else(|| (s, None), |(name, params)| (name, Some(params))); - StdInOutPath { path, params } - })), - }, - }) - .transpose() - })) - } - - /// Looks up the `stdin-path` property and returns the [`StdInOutPath`] - /// representing the path. The path may be an alias and require being - /// resolved with [`Alias::resolve`] before being used in conjunction with - /// [`Root::find_node`]. For more information about the path parameters, see - /// [`StdInOutPath::params`]. - #[track_caller] - pub fn stdin(self) -> P::Output>> { - P::to_output(crate::tryblock!({ - let node = self.node.fallible(); - node.properties()? - .into_iter() - .find_map(|n| match n { - Err(e) => Some(Err(e)), - Ok(property) => match property.name() == "stdin-path" { - false => None, - true => Some(property.as_value::<&str>().map_err(Into::into).map(|s| { - let (path, params) = - s.split_once(':').map_or_else(|| (s, None), |(name, params)| (name, Some(params))); - StdInOutPath { path, params } - })), - }, - }) - .transpose() - })) - } -} - -impl<'a, P: ParserWithMode<'a>> Clone for Chosen<'a, P> { - fn clone(&self) -> Self { - *self - } -} - -impl<'a, P: ParserWithMode<'a>> Copy for Chosen<'a, P> {} - -pub struct StdInOutPath<'a> { - path: &'a str, - params: Option<&'a str>, -} - -impl<'a> StdInOutPath<'a> { - /// Path to the node representing the stdin/stdout device. This node path - /// may be an alias, which can be resolved with [`Aliases::resolve`]. To be - /// used in conjunction with [`Root::find_node`]. - pub fn path(&self) -> &'a str { - self.path - } - - /// Optional parameters specified by the stdin/stdout property value. See - /// https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#chosen-node - /// - /// Example: - /// - /// ```dts - /// / { - /// chosen { - /// stdout-path = "/soc/uart@10000000:115200"; - /// stdin-path = "/soc/uart@10000000"; - /// } - /// } - /// ``` - /// - /// ```rust - /// # let fdt = fdt::Fdt::new_unaligned(include_bytes!("../dtb/test.dtb")).unwrap(); - /// # let chosen = fdt.root().chosen(); - /// let stdout = chosen.stdout().unwrap(); - /// let stdin = chosen.stdin().unwrap(); - /// - /// assert_eq!((stdout.path(), stdout.params()), ("/soc/uart@10000000", None)); - /// assert_eq!((stdin.path(), stdin.params()), ("/soc/uart@10000000", Some("115200"))); - /// ``` - pub fn params(&self) -> Option<&'a str> { - self.params - } -} +use super::{ + aliases::Aliases, + chosen::Chosen, + cpus::Cpus, + memory::{Memory, ReservedMemory}, + FallibleNode, FallibleRoot, IntoSearchableNodeName, Node, RawNode, SearchableNodeName, +}; -/// Represents the root (`/`) node with specific helper methods +/// [Devicetree 3.2. Root +/// node](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#root-node) +/// +/// The devicetree has a single root node of which all other device nodes are +/// descendants. The full path to the root node is `/`. pub struct Root<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { - pub(crate) node: Node<'a, P>, + pub(crate) node: FallibleNode<'a, P>, } impl<'a, P: ParserWithMode<'a>> Root<'a, P> { @@ -156,10 +32,7 @@ impl<'a, P: ParserWithMode<'a>> Root<'a, P> { #[track_caller] pub fn cell_sizes(self) -> P::Output { P::to_output(crate::tryblock!({ - self.node - .fallible() - .property::()? - .ok_or(FdtError::MissingRequiredProperty("#address-cells/#size-cells")) + self.node.property::()?.ok_or(FdtError::MissingRequiredProperty("#address-cells/#size-cells")) })) } @@ -238,12 +111,108 @@ impl<'a, P: ParserWithMode<'a>> Root<'a, P> { })) } + /// [Devicetree 3.3. `/aliases` + /// node](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#aliases-node) + /// + /// **Required**s + /// + /// A devicetree may have an aliases node (`/aliases`) that defines one or + /// more alias properties. The alias node shall be at the root of the + /// devicetree and have the node name `/aliases`. + /// + /// Each property of the `/aliases` node defines an alias. The property name + /// specifies the alias name. The property value specifies the full path to + /// a node in the devicetree. For example, the property `serial0 = + /// "/simple-bus@fe000000/serial@llc500"` defines the alias `serial0`. + pub fn aliases(&self) -> P::Output>> { + P::to_output(crate::tryblock!({ + let this: FallibleRoot<'a, P> = Root { node: self.node }; + match this.find_node("/aliases")? { + Some(node) => Ok(Some(Aliases { node })), + None => Ok(None), + } + })) + } + + /// [Devicetree 3.6. `/chosen` + /// Node](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#chosen-node) + /// + /// **Required** + /// + /// The `/chosen` node does not represent a real device in the system but + /// describes parameters chosen or specified by the system firmware at run + /// time. It shall be a child of the root node. + pub fn chosen(&self) -> P::Output> { + P::to_output(crate::tryblock!({ + let this: FallibleRoot<'a, P> = Root { node: self.node }; + match this.find_node("/chosen")? { + Some(node) => Ok(Chosen { node }), + None => Err(FdtError::MissingRequiredNode("/chosen")), + } + })) + } + + /// [Devicetree 3.7. + /// `/cpus`](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#cpus-node-properties) + /// + /// **Required** + /// + /// A `/cpus` node is required for all devicetrees. It does not represent a + /// real device in the system, but acts as a container for child cpu nodes + /// which represent the systems CPUs. + pub fn cpus(&self) -> P::Output> { + P::to_output(crate::tryblock!({ + let this: FallibleRoot<'a, P> = Root { node: self.node }; + match this.find_node("/cpus")? { + Some(node) => Ok(Cpus { node }), + None => Err(FdtError::MissingRequiredNode("/cpus")), + } + })) + } + + /// [Devicetree 3.4. `/memory` + /// node](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#memory-node) + /// + /// **Required** + /// + /// A memory device node is required for all devicetrees and describes the + /// physical memory layout for the system. If a system has multiple ranges + /// of memory, multiple memory nodes can be created, or the ranges can be + /// specified in the `reg` property of a single memory node. + pub fn memory(&self) -> P::Output> { + P::to_output(crate::tryblock!({ + let this: FallibleRoot<'a, P> = Root { node: self.node }; + match this.find_node("/memory")? { + Some(node) => Ok(Memory { node }), + None => Err(FdtError::MissingRequiredNode("/memory")), + } + })) + } + + /// [Devicetree 3.5. `/reserved-memory` + /// node](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#reserved-memory-node) + /// + /// Reserved memory is specified as a node under the `/reserved-memory` + /// node. The operating system shall exclude reserved memory from normal + /// usage. One can create child nodes describing particular reserved + /// (excluded from normal use) memory regions. Such memory regions are + /// usually designed for the special usage by various device drivers. + pub fn reserved_memory(&self) -> P::Output> { + P::to_output(crate::tryblock!({ + let this: FallibleRoot<'a, P> = Root { node: self.node }; + match this.find_node("/reserved-memory")? { + Some(node) => Ok(ReservedMemory { node }), + None => Err(FdtError::MissingRequiredNode("/reserved-memory")), + } + })) + } + /// Attempt to resolve a [`PHandle`] to the node containing a `phandle` /// property with the value #[track_caller] pub fn resolve_phandle(&self, phandle: PHandle) -> P::Output>> { P::to_output(crate::tryblock!({ - let this = Root { node: self.node.fallible() }; + let this: FallibleRoot<'a, P> = Root { node: self.node.fallible() }; for node in this.all_nodes()? { let (_, node) = node?; if node.property::()? == Some(phandle) { @@ -259,7 +228,7 @@ impl<'a, P: ParserWithMode<'a>> Root<'a, P> { /// `name` in depth-first order pub fn find_all_nodes_with_name<'b>(self, name: &'b str) -> P::Output> { P::to_output(crate::tryblock!({ - let this = Root { node: self.node.fallible() }; + let this: FallibleRoot<'a, P> = Root { node: self.node }; Ok(AllNodesWithNameIter { iter: this.all_nodes()?, name }) })) } @@ -268,7 +237,7 @@ impl<'a, P: ParserWithMode<'a>> Root<'a, P> { /// with a name that matches `name` in depth-first order pub fn find_node_by_name(self, name: &str) -> P::Output>> { P::to_output(crate::tryblock!({ - let this = Root { node: self.node.fallible() }; + let this: FallibleRoot<'a, P> = Root { node: self.node }; this.find_all_nodes_with_name(name)?.next().transpose().map(|n| n.map(|n| n.alt())) })) } @@ -279,10 +248,10 @@ impl<'a, P: ParserWithMode<'a>> Root<'a, P> { #[track_caller] pub fn find_node(self, path: &str) -> P::Output>> { if path == "/" { - return P::to_output(Ok(Some(self.node))); + return P::to_output(Ok(Some(self.node.alt()))); } - let fallible_self = Root { node: self.node.fallible() }; + let fallible_self: FallibleRoot<'a, P> = Root { node: self.node.fallible() }; let mut current_depth = 1; let mut all_nodes = match fallible_self.all_nodes() { @@ -332,7 +301,7 @@ impl<'a, P: ParserWithMode<'a>> Root<'a, P> { #[track_caller] pub fn all_compatible<'b>(self, with: &'b [&str]) -> P::Output> { P::to_output(crate::tryblock!({ - let this = Root { node: self.node.fallible() }; + let this: FallibleRoot<'a, P> = Root { node: self.node }; let f: fn(_) -> _ = |node: Result<(usize, FallibleNode<'a, P>), FdtError>| match node .and_then(|(_, n)| Ok((n, n.property::()?))) { @@ -351,7 +320,7 @@ impl<'a, P: ParserWithMode<'a>> Root<'a, P> { #[track_caller] pub fn all_nodes(self) -> P::Output> { let mut parser = P::new(self.node.this.as_slice(), self.node.strings, self.node.structs); - let res = tryblock!({ + let res = crate::tryblock!({ parser.advance_cstr()?; while parser.peek_token()? == BigEndianToken::PROP { @@ -429,6 +398,7 @@ impl<'a, 'b, P: ParserWithMode<'a>> Iterator for AllNodesWithNameIter<'a, 'b, P> /// See [`Root::all_compatible`] pub struct AllCompatibleIter<'a, 'b, P: ParserWithMode<'a>> { + #[allow(clippy::type_complexity)] iter: core::iter::FilterMap< AllNodesIter<'a, (P::Parser, NoPanic)>, fn( @@ -499,7 +469,7 @@ impl<'a, P: ParserWithMode<'a>> Iterator for AllNodesIter<'a, P> { }, )))); - let res = tryblock!({ + let res = crate::tryblock!({ self.parser.advance_cstr()?; while self.parser.peek_token()? == BigEndianToken::PROP { diff --git a/src/pretty_print.rs b/src/pretty_print.rs index 8867886..36062da 100644 --- a/src/pretty_print.rs +++ b/src/pretty_print.rs @@ -4,10 +4,9 @@ use crate::{ cell_collector::CollectCellsError, - nodes::{Node, NodeName}, + nodes::{root::Root, Node, NodeName}, parsing::{NoPanic, Parser}, properties::values::{InvalidPropertyValue, U32List}, - standard_nodes::Root, FdtError, }; diff --git a/src/properties.rs b/src/properties.rs index 8273ed0..1db467f 100644 --- a/src/properties.rs +++ b/src/properties.rs @@ -9,18 +9,13 @@ pub mod reg; pub mod values; use crate::{ - nodes::{FallibleNode, Node}, - parsing::{ - aligned::AlignedParser, unaligned::UnalignedParser, BigEndianU32, NoPanic, Panic, Parser, ParserWithMode, - StringsBlock, StructsBlock, - }, - standard_nodes::Root, + nodes::{FallibleNode, FallibleRoot}, + parsing::{BigEndianU32, ParserWithMode}, FdtError, }; -use core::ffi::CStr; pub trait Property<'a, P: ParserWithMode<'a>>: Sized { - fn parse(node: FallibleNode<'a, P>, root: Root<'a, (P::Parser, NoPanic)>) -> Result, FdtError>; + fn parse(node: FallibleNode<'a, P>, root: FallibleRoot<'a, P>) -> Result, FdtError>; } /// [Devicetree 2.3.1. @@ -54,10 +49,7 @@ pub struct Compatible<'a> { } impl<'a, P: ParserWithMode<'a>> Property<'a, P> for Compatible<'a> { - fn parse( - node: Node<'a, (P::Parser, NoPanic)>, - _: Root<'a, (P::Parser, NoPanic)>, - ) -> Result, FdtError> { + fn parse(node: FallibleNode<'a, P>, _: FallibleRoot<'a, P>) -> Result, FdtError> { let property = node.properties()?.find("compatible")?; match property { @@ -118,10 +110,7 @@ impl<'a> Iterator for CompatibleIter<'a> { pub struct Model<'a>(&'a str); impl<'a, P: ParserWithMode<'a>> Property<'a, P> for Model<'a> { - fn parse( - node: Node<'a, (P::Parser, NoPanic)>, - _: Root<'a, (P::Parser, NoPanic)>, - ) -> Result, FdtError> { + fn parse(node: FallibleNode<'a, P>, _: FallibleRoot<'a, P>) -> Result, FdtError> { match node.properties()?.find("model")? { Some(model) => Ok(Some(Self(model.as_value()?))), None => Ok(None), @@ -176,10 +165,7 @@ impl<'a> core::cmp::PartialEq> for str { pub struct PHandle(BigEndianU32); impl<'a, P: ParserWithMode<'a>> Property<'a, P> for PHandle { - fn parse( - node: Node<'a, (P::Parser, NoPanic)>, - _: Root<'a, (P::Parser, NoPanic)>, - ) -> Result, FdtError> { + fn parse(node: FallibleNode<'a, P>, _: FallibleRoot<'a, P>) -> Result, FdtError> { let Some(phandle) = node.properties()?.find("phandle")? else { return Ok(None); }; @@ -246,10 +232,7 @@ impl<'a> Status<'a> { } impl<'a, P: ParserWithMode<'a>> Property<'a, P> for Status<'a> { - fn parse( - node: Node<'a, (P::Parser, NoPanic)>, - _: Root<'a, (P::Parser, NoPanic)>, - ) -> Result, FdtError> { + fn parse(node: FallibleNode<'a, P>, _: FallibleRoot<'a, P>) -> Result, FdtError> { match node.properties()?.find("status")? { Some(model) => Ok(Some(Self(model.as_value()?))), None => Ok(None), @@ -286,10 +269,7 @@ impl<'a> core::cmp::PartialEq> for str { pub struct DmaCoherent; impl<'a, P: ParserWithMode<'a>> Property<'a, P> for DmaCoherent { - fn parse( - node: Node<'a, (P::Parser, NoPanic)>, - _: Root<'a, (P::Parser, NoPanic)>, - ) -> Result, FdtError> { + fn parse(node: FallibleNode<'a, P>, _: FallibleRoot<'a, P>) -> Result, FdtError> { match node.properties()?.find("dma-coherent")? { Some(_) => Ok(Some(Self)), None => Ok(None), diff --git a/src/properties/cells.rs b/src/properties/cells.rs index 697f7c7..fa4f588 100644 --- a/src/properties/cells.rs +++ b/src/properties/cells.rs @@ -1,7 +1,6 @@ use crate::{ - nodes::FallibleNode, - parsing::{unaligned::UnalignedParser, NoPanic, Parser, ParserWithMode, StringsBlock, StructsBlock}, - standard_nodes::Root, + nodes::{FallibleNode, FallibleRoot}, + parsing::{unaligned::UnalignedParser, Parser, ParserWithMode, StringsBlock, StructsBlock}, FdtError, }; @@ -59,7 +58,7 @@ pub struct CellSizes { impl<'a, P: ParserWithMode<'a>> Property<'a, P> for CellSizes { #[inline] - fn parse(node: FallibleNode<'a, P>, _: Root<'a, (P::Parser, NoPanic)>) -> Result, FdtError> { + fn parse(node: FallibleNode<'a, P>, _: FallibleRoot<'a, P>) -> Result, FdtError> { let (mut address_cells, mut size_cells) = (None, None); for property in node.properties()? { @@ -88,10 +87,35 @@ pub struct AddressCells(pub usize); impl<'a, P: ParserWithMode<'a>> Property<'a, P> for AddressCells { #[inline] - fn parse(node: FallibleNode<'a, P>, _: Root<'a, (P::Parser, NoPanic)>) -> Result, FdtError> { + fn parse(node: FallibleNode<'a, P>, _: FallibleRoot<'a, P>) -> Result, FdtError> { match node.properties()?.find("#address-cells")? { Some(value) => Ok(Some(Self(value.as_value()?))), None => Ok(None), } } } + +impl Default for AddressCells { + fn default() -> Self { + Self(2) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct SizeCells(pub usize); + +impl<'a, P: ParserWithMode<'a>> Property<'a, P> for SizeCells { + #[inline] + fn parse(node: FallibleNode<'a, P>, _: FallibleRoot<'a, P>) -> Result, FdtError> { + match node.properties()?.find("#size-cells")? { + Some(value) => Ok(Some(Self(value.as_value()?))), + None => Ok(None), + } + } +} + +impl Default for SizeCells { + fn default() -> Self { + Self(1) + } +} diff --git a/src/properties/interrupts.rs b/src/properties/interrupts.rs index 68f6fd7..5e72e47 100644 --- a/src/properties/interrupts.rs +++ b/src/properties/interrupts.rs @@ -3,9 +3,8 @@ pub mod pci; use super::{cells::AddressCells, PHandle, Property}; use crate::{ cell_collector::{BuildCellCollector, CellCollector, CollectCellsError}, - nodes::{FallibleNode, Node}, + nodes::{root::Root, FallibleNode, FallibleRoot, Node}, parsing::{aligned::AlignedParser, BigEndianU32, NoPanic, Panic, ParserWithMode}, - standard_nodes::Root, FdtError, }; @@ -18,7 +17,7 @@ pub enum Interrupts<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { } impl<'a, P: ParserWithMode<'a>> Property<'a, P> for Interrupts<'a, P> { - fn parse(node: FallibleNode<'a, P>, root: Root<'a, (P::Parser, NoPanic)>) -> Result, FdtError> { + fn parse(node: FallibleNode<'a, P>, root: FallibleRoot<'a, P>) -> Result, FdtError> { match ExtendedInterrupts::parse(node, root)? { Some(extended) => Ok(Some(Self::Extended(extended))), None => match LegacyInterrupts::parse(node, root)? { @@ -61,7 +60,7 @@ impl<'a, P: ParserWithMode<'a>> LegacyInterrupts<'a, P> { } impl<'a, P: ParserWithMode<'a>> Property<'a, P> for LegacyInterrupts<'a, P> { - fn parse(node: FallibleNode<'a, P>, root: Root<'a, (P::Parser, NoPanic)>) -> Result, FdtError> { + fn parse(node: FallibleNode<'a, P>, root: FallibleRoot<'a, P>) -> Result, FdtError> { match node.properties()?.find("interrupts")? { Some(interrupts) => { let interrupt_parent = match InterruptParent::<(P::Parser, NoPanic)>::parse(node, root)? { @@ -151,7 +150,7 @@ impl<'a, P: ParserWithMode<'a>> ExtendedInterrupts<'a, P> { } impl<'a, P: ParserWithMode<'a>> Property<'a, P> for ExtendedInterrupts<'a, P> { - fn parse(node: FallibleNode<'a, P>, root: Root<'a, (P::Parser, NoPanic)>) -> Result, FdtError> { + fn parse(node: FallibleNode<'a, P>, root: FallibleRoot<'a, P>) -> Result, FdtError> { match node.properties()?.find("interrupts-extended")? { Some(interrupts) => { Ok(Some(Self { encoded_array: interrupts.value(), root: Root { node: root.node.alt() } })) @@ -179,7 +178,7 @@ impl<'a, P: ParserWithMode<'a>> Iterator for ExtendedInterruptsIter<'a, P> { self.encoded_array = self.encoded_array.get(4..)?; let res = crate::tryblock!({ - let root = Root { node: self.root.node.fallible() }; + let root: FallibleRoot<'a, P> = Root { node: self.root.node.fallible() }; let Some(interrupt_parent) = root.resolve_phandle(phandle)? else { return Err(FdtError::PHandleNotFound(phandle.0.to_ne())); }; @@ -243,13 +242,23 @@ pub struct InterruptSpecifier<'a> { } impl<'a> InterruptSpecifier<'a> { - /// Iterate over the components that comprise this interrupt specifier + /// Iterator over the components that comprise this interrupt specifier. pub fn iter(self) -> InterruptSpecifierIter<'a> { InterruptSpecifierIter { encoded_array: self.encoded_array } } + /// Iterator over `(u32, u32)` interrupt specifier pairs, if + /// `#interrupt-cells` value is `2`. + pub fn iter_pairs(self) -> Option> { + if self.interrupt_cells.0 != 2 { + return None; + } + + Some(InterruptSpecifierIterPairs { encoded_array: self.encoded_array }) + } + /// Extract the single component that comprises the interrupt specifier, if - /// the `#interrupt-cells` value is `1` + /// the `#interrupt-cells` value is `1`. pub fn single(self) -> Option { if self.interrupt_cells.0 != 1 { return None; @@ -259,7 +268,7 @@ impl<'a> InterruptSpecifier<'a> { } /// Extract the two components that comprise the interrupt specifier, if the - /// `#interrupt-cells` value is `2` + /// `#interrupt-cells` value is `2`. pub fn pair(self) -> Option<(u32, u32)> { if self.interrupt_cells.0 != 2 { return None; @@ -356,7 +365,7 @@ impl<'a, P: ParserWithMode<'a>> core::ops::DerefMut for InterruptParent<'a, P> { } impl<'a, P: ParserWithMode<'a>> Property<'a, P> for InterruptParent<'a, P> { - fn parse(node: FallibleNode<'a, P>, root: Root<'a, (P::Parser, NoPanic)>) -> Result, FdtError> { + fn parse(node: FallibleNode<'a, P>, root: FallibleRoot<'a, P>) -> Result, FdtError> { match node.properties()?.find("interrupt-parent")? { Some(phandle) => match root.resolve_phandle(PHandle(phandle.as_value()?))? { Some(parent) => Ok(Some(Self(parent.alt()))), @@ -376,7 +385,7 @@ impl<'a, P: ParserWithMode<'a>> Property<'a, P> for InterruptParent<'a, P> { pub struct InterruptCells(pub usize); impl<'a, P: ParserWithMode<'a>> Property<'a, P> for InterruptCells { - fn parse(node: FallibleNode<'a, P>, _: Root<'a, (P::Parser, NoPanic)>) -> Result, FdtError> { + fn parse(node: FallibleNode<'a, P>, _: FallibleRoot<'a, P>) -> Result, FdtError> { match node.properties()?.find("#interrupt-cells")? { Some(ic) => Ok(Some(Self(ic.as_value()?))), None => Ok(None), @@ -416,7 +425,7 @@ impl InterruptMapMask> Property<'a, P> for InterruptMapMask { - fn parse(node: FallibleNode<'a, P>, _: Root<'a, (P::Parser, NoPanic)>) -> Result, FdtError> { + fn parse(node: FallibleNode<'a, P>, _: FallibleRoot<'a, P>) -> Result, FdtError> { let address_cells = node.property::()?.ok_or(FdtError::MissingRequiredProperty("#address-cells"))?; let interrupt_cells = @@ -462,7 +471,7 @@ impl<'a, AddrMask: CellCollector, IntMask: CellCollector, P: ParserWithMode<'a>> pub struct InterruptController; impl<'a, P: ParserWithMode<'a>> Property<'a, P> for InterruptController { - fn parse(node: FallibleNode<'a, P>, _: Root<'a, (P::Parser, NoPanic)>) -> Result, FdtError> { + fn parse(node: FallibleNode<'a, P>, _: FallibleRoot<'a, P>) -> Result, FdtError> { match node.properties()?.find("interrupt-controller")? { Some(_) => Ok(Some(Self)), None => Ok(None), @@ -553,7 +562,7 @@ impl< PInt: CellCollector, > Property<'a, P> for InterruptMap<'a, CAddr, CInt, PAddr, PInt, P> { - fn parse(node: FallibleNode<'a, P>, _: Root<'a, (P::Parser, NoPanic)>) -> Result, FdtError> { + fn parse(node: FallibleNode<'a, P>, _: FallibleRoot<'a, P>) -> Result, FdtError> { let Some(encoded_map) = node.properties()?.find("interrupt-map")? else { return Ok(None) }; let address_cells = diff --git a/src/properties/ranges.rs b/src/properties/ranges.rs index b1354ef..fea2bd2 100644 --- a/src/properties/ranges.rs +++ b/src/properties/ranges.rs @@ -1,6 +1,14 @@ -use crate::cell_collector::{BuildCellCollector, CellCollector, CollectCellsError}; +use crate::{ + cell_collector::{BuildCellCollector, CellCollector, CollectCellsError}, + nodes::{root::Root, FallibleNode}, + parsing::{NoPanic, ParserWithMode}, + FdtError, +}; -use super::cells::{AddressCells, CellSizes}; +use super::{ + cells::{AddressCells, CellSizes}, + Property, +}; #[derive(Debug, Clone, Copy)] pub struct Ranges<'a> { @@ -25,6 +33,23 @@ impl<'a> Ranges<'a> { } } +impl<'a, P: ParserWithMode<'a>> Property<'a, P> for Ranges<'a> { + fn parse( + node: FallibleNode<'a, P>, + _: Root<'a, (

>::Parser, NoPanic)>, + ) -> Result, FdtError> { + let Some(ranges) = node.properties()?.find("ranges")? else { + return Ok(None); + }; + + let parent_address_cells = + node.parent().ok_or(FdtError::MissingParent)?.property::()?.unwrap_or_default(); + let cell_sizes = node.property::()?.unwrap_or_default(); + + Ok(Some(Self { parent_address_cells, cell_sizes, ranges: ranges.value() })) + } +} + pub struct RangesIter<'a, CAddr: CellCollector = u64, PAddr: CellCollector = u64, Len: CellCollector = u64> { parent_address_cells: AddressCells, cell_sizes: CellSizes, diff --git a/src/properties/reg.rs b/src/properties/reg.rs index 43401b9..3c55fb0 100644 --- a/src/properties/reg.rs +++ b/src/properties/reg.rs @@ -1,8 +1,7 @@ use crate::{ cell_collector::{BuildCellCollector, CellCollector, CollectCellsError}, - nodes::FallibleNode, - parsing::{NoPanic, ParserWithMode}, - standard_nodes::Root, + nodes::{FallibleNode, FallibleRoot}, + parsing::ParserWithMode, FdtError, }; @@ -33,7 +32,7 @@ impl<'a> Reg<'a> { } impl<'a, P: ParserWithMode<'a>> Property<'a, P> for Reg<'a> { - fn parse(node: FallibleNode<'a, P>, _: Root<'a, (P::Parser, NoPanic)>) -> Result, FdtError> { + fn parse(node: FallibleNode<'a, P>, _: FallibleRoot<'a, P>) -> Result, FdtError> { let Some(prop) = node.raw_property("reg")? else { return Ok(None); }; @@ -153,7 +152,7 @@ impl VirtualReg { } impl<'a, P: ParserWithMode<'a>> Property<'a, P> for VirtualReg { - fn parse(node: FallibleNode<'a, P>, _: Root<'a, (P::Parser, NoPanic)>) -> Result, FdtError> { + fn parse(node: FallibleNode<'a, P>, _: FallibleRoot<'a, P>) -> Result, FdtError> { match node.properties()?.find("virtual-reg")? { Some(vreg) => Ok(Some(Self(vreg.as_value()?))), None => Ok(None), diff --git a/src/tests.rs b/src/tests.rs index 3d98458..be7afc7 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -434,11 +434,12 @@ fn interrupt_map() { // assert_eq!(fdt.root().model(), "riscv-virtio,qemu"); // } -// #[test] -// fn memory_node() { -// let fdt = Fdt::new(TEST.as_slice()).unwrap(); -// assert_eq!(fdt.memory().regions().count(), 1); -// } +#[test] +fn memory_node() { + let fdt = Fdt::new(TEST.as_slice()).unwrap(); + let root = fdt.root(); + assert_eq!(root.memory().reg().iter::().count(), 1); +} // #[test] // fn interrupt_cells() { From 5cbd06bd428c49ffaa25b5df758eac57624b2e55 Mon Sep 17 00:00:00 2001 From: sanana Date: Sat, 14 Sep 2024 15:51:14 +0000 Subject: [PATCH 26/38] Add .direnv to .gitignore (#38) This should not have been committed. --- .direnv/flake-profile | 1 - .direnv/flake-profile-4-link | 1 - .gitignore | 5 +++-- 3 files changed, 3 insertions(+), 4 deletions(-) delete mode 120000 .direnv/flake-profile delete mode 120000 .direnv/flake-profile-4-link diff --git a/.direnv/flake-profile b/.direnv/flake-profile deleted file mode 120000 index e289079..0000000 --- a/.direnv/flake-profile +++ /dev/null @@ -1 +0,0 @@ -flake-profile-4-link \ No newline at end of file diff --git a/.direnv/flake-profile-4-link b/.direnv/flake-profile-4-link deleted file mode 120000 index b03c926..0000000 --- a/.direnv/flake-profile-4-link +++ /dev/null @@ -1 +0,0 @@ -/nix/store/d302gd4kc0pxx0bjxnq370vbl4jirf5n-nix-shell-env \ No newline at end of file diff --git a/.gitignore b/.gitignore index b354aec..17adb98 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ -Cargo.lock -target/ \ No newline at end of file +/.direnv/ +/Cargo.lock +/target/ From 1f8367ece69ae0557c2d823c3203884b776da748 Mon Sep 17 00:00:00 2001 From: repnop Date: Tue, 15 Oct 2024 18:07:31 -0400 Subject: [PATCH 27/38] fix: apply a similar patch to #39 --- src/pretty_print.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/pretty_print.rs b/src/pretty_print.rs index 36062da..9843fc2 100644 --- a/src/pretty_print.rs +++ b/src/pretty_print.rs @@ -155,11 +155,8 @@ fn print_properties<'a, P: Parser<'a>>( writeln!(f, "{:width$}{} = <{:#04x}>;", ' ', name, prop.as_value::()?, width = depth * 4 + 4)?; } _ => match prop.as_value::<&str>() { - Ok(value) - if (!value.is_empty() && value.chars().all(|c| c.is_ascii_graphic())) || prop.value() == [0] => - { - writeln!(f, "{:width$}{} = {:?};", ' ', prop.name(), value, width = depth * 4 + 4)? - } + Ok("") => writeln!(f, "{:width$}{};", ' ', prop.name(), width = depth * 4 + 4)?, + Ok(value) => writeln!(f, "{:width$}{} = {:?};", ' ', prop.name(), value, width = depth * 4 + 4)?, _ => match prop.value().len() { 0 => writeln!(f, "{:width$}{};", ' ', prop.name(), width = depth * 4 + 4)?, _ => { From 3abf3b009c77499f12b7cafb22a5270dbfbb2111 Mon Sep 17 00:00:00 2001 From: repnop Date: Tue, 15 Oct 2024 20:49:16 -0400 Subject: [PATCH 28/38] add some more documentation and impl some helper functions --- src/nodes.rs | 164 +++++++++++++++++++++++++++++++++++++-- src/nodes/chosen.rs | 104 +++++++++++++++++++++---- src/nodes/cpus.rs | 3 +- src/nodes/root.rs | 11 ++- src/parsing.rs | 1 + src/properties/cells.rs | 3 +- src/properties/ranges.rs | 11 ++- src/properties/reg.rs | 3 +- 8 files changed, 263 insertions(+), 37 deletions(-) diff --git a/src/nodes.rs b/src/nodes.rs index 6df2169..67f5d74 100644 --- a/src/nodes.rs +++ b/src/nodes.rs @@ -4,8 +4,6 @@ pub mod cpus; pub mod memory; pub mod root; -use root::Root; - use crate::{ parsing::{ aligned::AlignedParser, BigEndianToken, NoPanic, Panic, PanicMode, ParseError, Parser, ParserWithMode, @@ -19,6 +17,7 @@ use crate::{ }, FdtError, }; +use root::Root; #[macro_export] #[doc(hidden)] @@ -42,6 +41,16 @@ pub enum SearchableNodeName<'a> { WithUnitAddress(NodeName<'a>), } +/// Convert from a type that can potentially represent a node name that is able +/// to be searched for during lookup operations. +/// +/// Currently, two type impls are defined: +/// 1. [`NodeName`]: corresponds directly to a +/// [`SearchableNodeName::WithUnitAddress`]. +/// 2. [`&str`]: attempts to parse the `str` as `name@unit-address`, +/// corresponding to [`SearchableNodeName::WithUnitAddress`], or as +/// just a base node name with no specified unit address, which will +/// resolve to the first node with that base name found. pub trait IntoSearchableNodeName<'a>: Sized + crate::sealed::Sealed { fn into_searchable_node_name(self) -> SearchableNodeName<'a>; } @@ -86,9 +95,11 @@ impl core::fmt::Display for NodeName<'_> { } } -pub type FallibleNode<'a, P> = Node<'a, (

>::Parser, NoPanic)>; -pub type FallibleRoot<'a, P> = Root<'a, (

>::Parser, NoPanic)>; +pub type FallibleParser<'a, P> = (

>::Parser, NoPanic); +pub type FallibleNode<'a, P> = Node<'a, FallibleParser<'a, P>>; +pub type FallibleRoot<'a, P> = Root<'a, FallibleParser<'a, P>>; +/// A generic devicetree node. pub struct Node<'a, P: ParserWithMode<'a>> { pub(crate) this: &'a RawNode<

>::Granularity>, pub(crate) parent: Option<&'a RawNode<

>::Granularity>>, @@ -98,13 +109,13 @@ pub struct Node<'a, P: ParserWithMode<'a>> { } impl<'a, P: ParserWithMode<'a>> Node<'a, P> { - /// Change the type of this node's [`PanicMode`] to [`NoPanic`] + /// Change the type of this node's [`PanicMode`] to [`NoPanic`]. #[inline(always)] pub(crate) fn fallible(self) -> FallibleNode<'a, P> { self.alt() } - /// Helper function for changing the [`PanicMode`] of this node + /// Helper function for changing the [`PanicMode`] of this node. #[inline(always)] pub fn alt>(self) -> Node<'a, P2> { Node { @@ -123,7 +134,9 @@ impl<'a, P: ParserWithMode<'a>> Node<'a, P> { parser.parse_root().map(|node| Root { node }) } + /// The name of this node along with the optional unit address. #[inline] + #[track_caller] pub fn name(&self) ->

::Output> { P::to_output( P::new(&self.this.0, self.strings, self.structs) @@ -140,16 +153,116 @@ impl<'a, P: ParserWithMode<'a>> Node<'a, P> { ) } + /// [Devicetree 3.8.1 General Properties of `/cpus/cpu*` + /// nodes](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#general-properties-of-cpus-cpu-nodes) + /// + /// **Required** + /// + /// The value of `reg` is a `` that defines a unique + /// CPU/thread id for the CPU/threads represented by the CPU node. + /// + /// If a CPU supports more than one thread (i.e. multiple streams of + /// execution) the `reg` property is an array with 1 element per thread. The + /// `#address-cells` on the `/cpus` node specifies how many cells each + /// element of the array takes. Software can determine the number of threads + /// by dividing the size of `reg` by the parent node’s `#address-cells`. + /// + /// If a CPU/thread can be the target of an external interrupt the `reg` + /// property value must be a unique CPU/thread id that is addressable by the + /// interrupt controller. + /// + /// If a CPU/thread cannot be the target of an external interrupt, then + /// `reg` must be unique and out of bounds of the range addressed by the + /// interrupt controller + /// + /// If a CPU/thread’s PIR (pending interrupt register) is modifiable, a + /// client program should modify PIR to match the `reg` property value. If + /// PIR cannot be modified and the PIR value is distinct from the interrupt + /// controller number space, the CPUs binding may define a binding-specific + /// representation of PIR values if desired. #[inline(always)] + #[track_caller] pub fn reg(&self) -> P::Output>> { self.property::>() } + /// [Devicetree 2.3.8 + /// `ranges`](https://devicetree-specification.readthedocs.io/en/latest/chapter2-devicetree-basics.html#sect-standard-properties-ranges) + /// + /// Value type: `` or `` encoded as an arbitrary + /// number of `(child-bus-address, parent-bus-address, length)` triplets. + /// + /// Description: + /// + /// The ranges property provides a means of defining a mapping or + /// translation between the address space of the bus (the child address + /// space) and the address space of the bus node’s parent (the parent + /// address space). + /// + /// The format of the value of the ranges property is an arbitrary number of + /// triplets of `(child-bus-address, parent-bus-address, length)` + /// + /// * The `child-bus-address` is a physical address within the child bus’ + /// address space. The number of cells to represent the address is bus + /// dependent and can be determined from the `#address-cells` of this node + /// (the node in which the ranges property appears). + /// * The `parent-bus-address` is a physical address within the parent bus’ + /// address space. The number of cells to represent the parent address is + /// bus dependent and can be determined from the `#address-cells` property + /// of the node that defines the parent’s address space. + /// * The `length` specifies the size of the range in the child’s address + /// space. The number of cells to represent the size can be determined + /// from the `#size-cells` of this node (the node in which the ranges + /// property appears). + /// + /// If the property is defined with an `` value, it specifies that + /// the parent and child address space is identical, and no address + /// translation is required. + /// + /// If the property is not present in a bus node, it is assumed that no + /// mapping exists between children of the node and the parent address + /// space. + /// + /// Address Translation Example: + /// + /// ```notrust + /// soc { + /// compatible = "simple-bus"; + /// #address-cells = <1>; + /// #size-cells = <1>; + /// ranges = <0x0 0xe0000000 0x00100000>; + /// + /// serial@4600 { + /// device_type = "serial"; + /// compatible = "ns16550"; + /// reg = <0x4600 0x100>; + /// clock-frequency = <0>; + /// interrupts = <0xA 0x8>; + /// interrupt-parent = <&ipic>; + /// }; + /// }; + /// ``` + /// + /// The soc node specifies a ranges property of + /// + /// ```notrust + /// <0x0 0xe0000000 0x00100000>; + /// ``` + /// + /// This property value specifies that for a 1024 KB range of address space, + /// a child node addressed at physical `0x0` maps to a parent address of + /// physical `0xe0000000`. With this mapping, the serial device node can be + /// addressed by a load or store at address `0xe0004600`, an offset of + /// `0x4600` (specified in `reg`) plus the `0xe0000000` mapping specified in + /// ranges. + #[inline(always)] + #[track_caller] pub fn ranges(&self) -> P::Output>> { self.property() } #[inline] + #[track_caller] pub fn properties(&self) -> P::Output> { let mut parser = P::new(&self.this.0, self.strings, self.structs); let res = parser.advance_cstr(); @@ -162,7 +275,10 @@ impl<'a, P: ParserWithMode<'a>> Node<'a, P> { })) } + /// Attempt to find the property with the given name and extract the raw + /// name and value. #[inline] + #[track_caller] pub fn raw_property(&self, name: &str) -> P::Output>> { P::to_output(tryblock!({ let this = self.fallible(); @@ -175,7 +291,21 @@ impl<'a, P: ParserWithMode<'a>> Node<'a, P> { P::to_output(crate::tryblock!({ Prop::parse(self.alt(), self.make_root()?) })) } + /// Attempt to find a child of the current [`Node`] with the given name. + /// + /// For more details on what constitutes a node name which can be + /// searchable, see [`IntoSearchableNodeName`]. #[inline] + #[track_caller] + pub fn child(&self, name: N) -> P::Output>> + where + N: IntoSearchableNodeName<'a>, + { + P::to_output(crate::tryblock!({ self.fallible().children()?.find(name).map(|o| o.map(|n| n.alt())) })) + } + + #[inline] + #[track_caller] pub fn children(&self) -> P::Output> { P::to_output(tryblock!({ let mut parser = P::new(&self.this.0, self.strings, self.structs); @@ -267,10 +397,12 @@ impl<'a, P: ParserWithMode<'a>> NodeProperties<'a, P> { } } + #[inline(always)] pub fn iter(self) -> NodePropertiesIter<'a, P> { NodePropertiesIter { properties: self.alt(), _mode: core::marker::PhantomData } } + #[track_caller] pub(crate) fn advance(&mut self) -> P::Output>> { let mut parser = P::new(self.data, self.strings, self.structs); @@ -297,6 +429,8 @@ impl<'a, P: ParserWithMode<'a>> NodeProperties<'a, P> { })) } + #[inline] + #[track_caller] pub fn find(&self, name: &str) -> P::Output>> { let this: NodeProperties<'a, (P::Parser, NoPanic)> = NodeProperties { data: self.data, @@ -328,6 +462,7 @@ impl<'a, P: ParserWithMode<'a>> IntoIterator for NodeProperties<'a, P> { type IntoIter = NodePropertiesIter<'a, P>; type Item = P::Output>; + #[inline(always)] fn into_iter(self) -> Self::IntoIter { self.iter() } @@ -361,18 +496,22 @@ pub struct NodeProperty<'a> { } impl<'a> NodeProperty<'a> { + #[inline(always)] pub fn new(name: &'a str, value: &'a [u8]) -> Self { Self { name, value } } + #[inline(always)] pub fn name(&self) -> &'a str { self.name } + #[inline(always)] pub fn value(&self) -> &'a [u8] { self.value } + #[inline(always)] pub fn as_value>(&self) -> Result { V::parse(self.value) } @@ -387,6 +526,7 @@ pub struct NodeChildren<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> } impl<'a, P: ParserWithMode<'a>> NodeChildren<'a, P> { + #[inline(always)] pub fn iter(&self) -> NodeChildrenIter<'a, P> { NodeChildrenIter { children: NodeChildren { @@ -399,6 +539,7 @@ impl<'a, P: ParserWithMode<'a>> NodeChildren<'a, P> { } } + #[inline] pub(crate) fn advance(&mut self) -> P::Output>> { let mut parser = P::new(self.data, self.strings, self.structs); @@ -421,6 +562,8 @@ impl<'a, P: ParserWithMode<'a>> NodeChildren<'a, P> { }) } + #[inline] + #[track_caller] pub fn find<'n, N>(&self, name: N) -> P::Output>> where N: IntoSearchableNodeName<'n>, @@ -460,6 +603,15 @@ impl<'a, P: ParserWithMode<'a>> Clone for NodeChildren<'a, P> { impl<'a, P: ParserWithMode<'a>> Copy for NodeChildren<'a, P> {} +impl<'a, P: ParserWithMode<'a>> IntoIterator for NodeChildren<'a, P> { + type IntoIter = NodeChildrenIter<'a, P>; + type Item = P::Output>; + + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + #[derive(Clone)] pub struct NodeChildrenIter<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { children: NodeChildren<'a, (P::Parser, NoPanic)>, diff --git a/src/nodes/chosen.rs b/src/nodes/chosen.rs index 8a33331..61420ff 100644 --- a/src/nodes/chosen.rs +++ b/src/nodes/chosen.rs @@ -2,13 +2,12 @@ // v. 2.0. If a copy of the MPL was not distributed with this file, You can // obtain one at https://mozilla.org/MPL/2.0/. +use super::{FallibleNode, FallibleParser, FallibleRoot, Node}; use crate::{ parsing::{aligned::AlignedParser, Panic, ParseError, ParserWithMode}, FdtError, }; -use super::FallibleNode; - /// [Devicetree 3.6. `/chosen` /// Node](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#chosen-node) /// @@ -20,7 +19,12 @@ pub struct Chosen<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { } impl<'a, P: ParserWithMode<'a>> Chosen<'a, P> { - /// Contains the bootargs, if they exist + /// [Devicetree 3.6. `/chosen` + /// Node](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#chosen-node) + /// + /// A string that specifies the boot arguments for the client program. The + /// value could potentially be a null string if no boot arguments are + /// required. #[track_caller] pub fn bootargs(self) -> P::Output> { P::to_output(crate::tryblock!({ @@ -37,13 +41,49 @@ impl<'a, P: ParserWithMode<'a>> Chosen<'a, P> { })) } - /// Looks up the `stdout-path` property and returns the [`StdInOutPath`] - /// representing the path. The path may be an alias and require being - /// resolved with [`Alias::resolve`] before being used in conjunction with - /// [`Root::find_node`]. For more information about the path parameters, see - /// [`StdInOutPath::params`]. + /// Like [`Chosen::stdout_path`] but also attempts to resolve the path (also + /// attempts to resolve the path to an alias if: the path does not look like + /// a devicetree path, or the path is not found), and returns the stdout + /// parameters along with the node, if it was successfully resolved. + /// + /// For more information on the `stdout-path` property, see + /// [`Chosen::stdout_path`]. + #[allow(clippy::type_complexity)] + #[track_caller] + pub fn stdout(self) -> P::Output, Option<&'a str>)>> { + P::to_output(crate::tryblock!({ + let this: Chosen<'a, FallibleParser<'a, P>> = Chosen { node: self.node }; + let Some(stdout) = this.stdout_path()? else { return Ok(None) }; + let root: FallibleRoot<'a, P> = this.node.make_root()?; + + let node = match stdout.path().contains('/') { + true => root.find_node(stdout.path())?, + false => None, + }; + + match node { + Some(node) => Ok(Some((node.alt(), stdout.params()))), + None => { + let Some(aliases) = root.aliases()? else { return Ok(None) }; + match aliases.resolve(stdout.path())? { + Some(node) => Ok(Some((node.alt(), stdout.params()))), + None => Ok(None), + } + } + } + })) + } + + /// [Devicetree 3.6. `/chosen` + /// Node](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#chosen-node) + /// + /// A string that specifies the full path to the node representing the + /// device to be used for boot console output. If the character ":" is + /// present in the value it terminates the path. The value may be an alias. + /// If the `stdin-path` property is not specified, `stdout-path` should be + /// assumed to define the input device. #[track_caller] - pub fn stdout(self) -> P::Output>> { + pub fn stdout_path(self) -> P::Output>> { P::to_output(crate::tryblock!({ self.node .properties()? @@ -63,13 +103,47 @@ impl<'a, P: ParserWithMode<'a>> Chosen<'a, P> { })) } - /// Looks up the `stdin-path` property and returns the [`StdInOutPath`] - /// representing the path. The path may be an alias and require being - /// resolved with [`Alias::resolve`] before being used in conjunction with - /// [`Root::find_node`]. For more information about the path parameters, see - /// [`StdInOutPath::params`]. + /// Like [`Chosen::stdin_path`] but also attempts to resolve the path (also + /// attempts to resolve the path to an alias if: the path does not look like + /// a devicetree path, or the path is not found), and returns the stdin + /// parameters along with the node, if it was successfully resolved. + /// + /// For more information on the `stdin-path` property, see + /// [`Chosen::stdin_path`]. + #[allow(clippy::type_complexity)] + #[track_caller] + pub fn stdin(self) -> P::Output, Option<&'a str>)>> { + P::to_output(crate::tryblock!({ + let this: Chosen<'a, FallibleParser<'a, P>> = Chosen { node: self.node }; + let Some(stdin) = this.stdin_path()? else { return Ok(None) }; + let root: FallibleRoot<'a, P> = this.node.make_root()?; + + let node = match stdin.path().contains('/') { + true => root.find_node(stdin.path())?, + false => None, + }; + + match node { + Some(node) => Ok(Some((node.alt(), stdin.params()))), + None => { + let Some(aliases) = root.aliases()? else { return Ok(None) }; + match aliases.resolve(stdin.path())? { + Some(node) => Ok(Some((node.alt(), stdin.params()))), + None => Ok(None), + } + } + } + })) + } + + /// [Devicetree 3.6. `/chosen` + /// Node](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#chosen-node) + /// + /// A string that specifies the full path to the node representing the + /// device to be used for boot console input. If the character ":" is + /// present in the value it terminates the path. The value may be an alias. #[track_caller] - pub fn stdin(self) -> P::Output>> { + pub fn stdin_path(self) -> P::Output>> { P::to_output(crate::tryblock!({ self.node .properties()? diff --git a/src/nodes/cpus.rs b/src/nodes/cpus.rs index 7535efa..c52f3e9 100644 --- a/src/nodes/cpus.rs +++ b/src/nodes/cpus.rs @@ -1,3 +1,4 @@ +use super::{AsNode, FallibleNode, NodeChildrenIter}; use crate::{ cell_collector::{BuildCellCollector, CellCollector, CollectCellsError}, parsing::{aligned::AlignedParser, NoPanic, Panic, ParserWithMode}, @@ -8,8 +9,6 @@ use crate::{ FdtError, }; -use super::{AsNode, FallibleNode, NodeChildrenIter}; - /// [Devicetree 3.7. /// `/cpus`](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#cpus-node-properties) /// diff --git a/src/nodes/root.rs b/src/nodes/root.rs index 6f73aab..713de88 100644 --- a/src/nodes/root.rs +++ b/src/nodes/root.rs @@ -1,9 +1,3 @@ -use crate::{ - parsing::{aligned::AlignedParser, BigEndianToken, NoPanic, Panic, ParseError, Parser, ParserWithMode}, - properties::{cells::CellSizes, Compatible, PHandle, Property}, - FdtError, -}; - use super::{ aliases::Aliases, chosen::Chosen, @@ -11,6 +5,11 @@ use super::{ memory::{Memory, ReservedMemory}, FallibleNode, FallibleRoot, IntoSearchableNodeName, Node, RawNode, SearchableNodeName, }; +use crate::{ + parsing::{aligned::AlignedParser, BigEndianToken, NoPanic, Panic, ParseError, Parser, ParserWithMode}, + properties::{cells::CellSizes, Compatible, PHandle, Property}, + FdtError, +}; /// [Devicetree 3.2. Root /// node](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#root-node) diff --git a/src/parsing.rs b/src/parsing.rs index d7e75dc..2775cd5 100644 --- a/src/parsing.rs +++ b/src/parsing.rs @@ -9,6 +9,7 @@ use crate::{ nodes::{Node, RawNode}, FdtError, FdtHeader, }; + #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(transparent)] pub struct BigEndianU32(u32); diff --git a/src/properties/cells.rs b/src/properties/cells.rs index fa4f588..d689b8f 100644 --- a/src/properties/cells.rs +++ b/src/properties/cells.rs @@ -1,11 +1,10 @@ +use super::Property; use crate::{ nodes::{FallibleNode, FallibleRoot}, parsing::{unaligned::UnalignedParser, Parser, ParserWithMode, StringsBlock, StructsBlock}, FdtError, }; -use super::Property; - /// [Devicetree 2.3.5. `#address-cells` and /// `#size-cells`](https://devicetree-specification.readthedocs.io/en/latest/chapter2-devicetree-basics.html#address-cells-and-size-cells) /// diff --git a/src/properties/ranges.rs b/src/properties/ranges.rs index fea2bd2..1092517 100644 --- a/src/properties/ranges.rs +++ b/src/properties/ranges.rs @@ -1,3 +1,7 @@ +use super::{ + cells::{AddressCells, CellSizes}, + Property, +}; use crate::{ cell_collector::{BuildCellCollector, CellCollector, CollectCellsError}, nodes::{root::Root, FallibleNode}, @@ -5,11 +9,10 @@ use crate::{ FdtError, }; -use super::{ - cells::{AddressCells, CellSizes}, - Property, -}; +#[cfg(doc)] +use crate::nodes::Node; +/// See [`Node::ranges`]. #[derive(Debug, Clone, Copy)] pub struct Ranges<'a> { parent_address_cells: AddressCells, diff --git a/src/properties/reg.rs b/src/properties/reg.rs index 3c55fb0..7551785 100644 --- a/src/properties/reg.rs +++ b/src/properties/reg.rs @@ -1,3 +1,4 @@ +use super::{cells::CellSizes, Property}; use crate::{ cell_collector::{BuildCellCollector, CellCollector, CollectCellsError}, nodes::{FallibleNode, FallibleRoot}, @@ -5,8 +6,6 @@ use crate::{ FdtError, }; -use super::{cells::CellSizes, Property}; - #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Reg<'a> { cell_sizes: CellSizes, From d0b2d9b3362585f00dbacff866bd1b4271cb730d Mon Sep 17 00:00:00 2001 From: repnop Date: Wed, 27 Nov 2024 17:00:48 -0500 Subject: [PATCH 29/38] woop, all tests pass --- src/cell_collector.rs | 12 +- src/lib.rs | 66 ++++++- src/nodes/aliases.rs | 26 ++- src/nodes/chosen.rs | 52 ++++- src/nodes/cpus.rs | 3 +- src/nodes/root.rs | 24 ++- src/properties/ranges.rs | 1 + src/properties/reg.rs | 14 +- src/tests.rs | 417 +++++++++++++++++++-------------------- 9 files changed, 366 insertions(+), 249 deletions(-) diff --git a/src/cell_collector.rs b/src/cell_collector.rs index 5613a60..3ee6d29 100644 --- a/src/cell_collector.rs +++ b/src/cell_collector.rs @@ -58,7 +58,17 @@ impl< return Err(CollectCellsError); } - self.value = self.value.shl(32).bitor(Int::from(component)); + // HACK: shifting a `u32` by `32` bits at all, regardless of the value, + // panics, so for `u32`s, don't shift at all since the next call will + // fail above. + let shl = const { + match core::mem::size_of::() { + 0..=4 => 0, + _ => 32, + } + }; + + self.value = self.value.shl(shl).bitor(Int::from(component)); Ok(()) } diff --git a/src/lib.rs b/src/lib.rs index fae1b47..599f709 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -27,7 +27,7 @@ //! //! println!("This is a devicetree representation of a {}", root.model()); //! println!("...which is compatible with at least: {}", root.compatible().first()); -//! println!("...and has {} CPU(s)", root.cpus().cpus().count()); +//! println!("...and has {} CPU(s)", root.cpus().iter().count()); //! println!( //! "...and has at least one memory location at: {:#X}\n", //! root.memory().reg().iter::().next().unwrap().unwrap().address @@ -38,7 +38,7 @@ //! println!("The bootargs are: {:?}", bootargs); //! } //! -//! if let Some(stdout) = chosen.stdout() { +//! if let Some(stdout) = chosen.stdout_path() { //! println!("It would write stdout to: {}", stdout.path()); //! } //! @@ -68,7 +68,10 @@ mod pretty_print; pub mod properties; mod util; -use nodes::root::Root; +use nodes::{ + root::{AllCompatibleIter, AllNodesIter, AllNodesWithNameIter, Root}, + FallibleParser, Node, +}; use parsing::{ aligned::AlignedParser, unaligned::UnalignedParser, NoPanic, Panic, ParseError, Parser, ParserWithMode, StringsBlock, StructsBlock, @@ -319,6 +322,12 @@ impl<'a> Fdt<'a, (AlignedParser<'a>, NoPanic)> { } impl<'a, P: ParserWithMode<'a>> Fdt<'a, P> { + #[inline(always)] + fn fallible_root(&self) -> Result>, FdtError> { + let mut parser = FallibleParser::<'a, P>::new(self.structs.0, self.strings, self.structs); + Ok(Root { node: parser.parse_root()? }) + } + /// Return the root (`/`) node, which is always available pub fn root(&self) -> P::Output> { let mut parser = P::new(self.structs.0, self.strings, self.structs); @@ -342,6 +351,57 @@ impl<'a, P: ParserWithMode<'a>> Fdt<'a, P> { }) } + /// Convenience wrapper around [`Root::find_all_nodes_with_name`]. Returns + /// an iterator that yields every node with the name that matches `name` in + /// depth-first order. + #[track_caller] + pub fn find_all_nodes_with_name<'b>(&self, name: &'b str) -> P::Output> { + P::to_output(self.fallible_root().and_then(|root| { + root.find_all_nodes_with_name(name).map(|i| AllNodesWithNameIter { iter: i.iter, name: i.name }) + })) + } + + /// Convenience wrapper around [`Root::find_node_by_name`]. Attempt to find + /// a node with the given name, returning the first node with a name that + /// matches `name` in depth-first order. + #[track_caller] + pub fn find_node_by_name(&self, name: &str) -> P::Output>> { + P::to_output(self.fallible_root().and_then(|root| Ok(root.find_node_by_name(name)?.map(|n| n.alt())))) + } + + /// Convenience wrapper around [`Root::find_node`]. Attempt to find a node + /// with the given path (with an optional unit address, defaulting to the + /// first matching name if omitted). If you only have the node name but not + /// the path, use [`Root::find_node_by_name`] instead. + #[track_caller] + pub fn find_node(&self, path: &str) -> P::Output>> { + P::to_output(self.fallible_root().and_then(|root| Ok(root.find_node(path)?.map(|n| n.alt())))) + } + + /// Convenience wrapper around [`Root::all_compatible`]. Returns an iterator over + /// every node within the devicetree which is compatible with at least one + /// of the compatible strings contained within `with`. + #[track_caller] + pub fn all_compatible<'b>(&self, with: &'b [&str]) -> P::Output> { + P::to_output( + self.fallible_root() + .and_then(|root| root.all_compatible(with).map(|i| AllCompatibleIter { iter: i.iter, with: i.with })), + ) + } + + /// Convenience wrapper around [`Root::all_nodes`]. Returns an iterator over + /// each node in the tree, depth-first, along with its depth in the tree. + #[track_caller] + pub fn all_nodes(&self) -> P::Output> { + P::to_output(self.fallible_root().and_then(|root| { + root.all_nodes().map(|i| AllNodesIter { + parser: P::new(i.parser.data(), i.parser.strings(), i.parser.structs()), + parent_index: i.parent_index, + parents: i.parents, + }) + })) + } + /// Total size of the devicetree in bytes pub fn total_size(&self) -> usize { self.header.total_size as usize diff --git a/src/nodes/aliases.rs b/src/nodes/aliases.rs index 09fce04..e4276f9 100644 --- a/src/nodes/aliases.rs +++ b/src/nodes/aliases.rs @@ -1,4 +1,4 @@ -use super::{AsNode, FallibleNode, Node}; +use super::{AsNode, FallibleNode, FallibleParser, Node, NodePropertiesIter}; use crate::parsing::{NoPanic, ParserWithMode}; /// [Devicetree 3.3. `/aliases` @@ -56,6 +56,11 @@ impl<'a, P: ParserWithMode<'a>> Aliases<'a, P> { self.node.make_root::()?.find_node(path).map(|r| r.map(|n| n.alt())) })) } + + /// Create an iterator over all of the available aliases + pub fn iter(self) -> P::Output> { + P::to_output(crate::tryblock!({ Ok(AllAliasesIter { properties: self.node.properties()?.iter() }) })) + } } impl<'a, P: ParserWithMode<'a>> AsNode<'a, P> for Aliases<'a, P> { @@ -63,3 +68,22 @@ impl<'a, P: ParserWithMode<'a>> AsNode<'a, P> for Aliases<'a, P> { self.node.alt() } } + +pub struct AllAliasesIter<'a, P: ParserWithMode<'a>> { + properties: NodePropertiesIter<'a, FallibleParser<'a, P>>, +} + +impl<'a, P> Iterator for AllAliasesIter<'a, P> +where + P: ParserWithMode<'a>, +{ + type Item = P::Output<(&'a str, &'a str)>; + #[track_caller] + fn next(&mut self) -> Option { + Some(P::to_output(match self.properties.next() { + Some(Ok(prop)) => crate::tryblock!({ Ok((prop.name(), prop.as_value::<&'a str>()?)) }), + Some(Err(e)) => Err(e), + None => return None, + })) + } +} diff --git a/src/nodes/chosen.rs b/src/nodes/chosen.rs index 61420ff..86d91f5 100644 --- a/src/nodes/chosen.rs +++ b/src/nodes/chosen.rs @@ -50,7 +50,7 @@ impl<'a, P: ParserWithMode<'a>> Chosen<'a, P> { /// [`Chosen::stdout_path`]. #[allow(clippy::type_complexity)] #[track_caller] - pub fn stdout(self) -> P::Output, Option<&'a str>)>> { + pub fn stdout(self) -> P::Output>> { P::to_output(crate::tryblock!({ let this: Chosen<'a, FallibleParser<'a, P>> = Chosen { node: self.node }; let Some(stdout) = this.stdout_path()? else { return Ok(None) }; @@ -62,11 +62,11 @@ impl<'a, P: ParserWithMode<'a>> Chosen<'a, P> { }; match node { - Some(node) => Ok(Some((node.alt(), stdout.params()))), + Some(node) => Ok(Some(Stdout { node: node.alt(), params: stdout.params() })), None => { let Some(aliases) = root.aliases()? else { return Ok(None) }; match aliases.resolve(stdout.path())? { - Some(node) => Ok(Some((node.alt(), stdout.params()))), + Some(node) => Ok(Some(Stdout { node: node.alt(), params: stdout.params() })), None => Ok(None), } } @@ -112,7 +112,7 @@ impl<'a, P: ParserWithMode<'a>> Chosen<'a, P> { /// [`Chosen::stdin_path`]. #[allow(clippy::type_complexity)] #[track_caller] - pub fn stdin(self) -> P::Output, Option<&'a str>)>> { + pub fn stdin(self) -> P::Output>> { P::to_output(crate::tryblock!({ let this: Chosen<'a, FallibleParser<'a, P>> = Chosen { node: self.node }; let Some(stdin) = this.stdin_path()? else { return Ok(None) }; @@ -124,11 +124,11 @@ impl<'a, P: ParserWithMode<'a>> Chosen<'a, P> { }; match node { - Some(node) => Ok(Some((node.alt(), stdin.params()))), + Some(node) => Ok(Some(Stdin { node: node.alt(), params: stdin.params() })), None => { let Some(aliases) = root.aliases()? else { return Ok(None) }; match aliases.resolve(stdin.path())? { - Some(node) => Ok(Some((node.alt(), stdin.params()))), + Some(node) => Ok(Some(Stdin { node: node.alt(), params: stdin.params() })), None => Ok(None), } } @@ -172,6 +172,42 @@ impl<'a, P: ParserWithMode<'a>> Clone for Chosen<'a, P> { impl<'a, P: ParserWithMode<'a>> Copy for Chosen<'a, P> {} +/// See [`Chosen::stdin`]. +pub struct Stdin<'a, P: ParserWithMode<'a>> { + pub node: Node<'a, P>, + pub params: Option<&'a str>, +} + +impl<'a, P: ParserWithMode<'a>> core::fmt::Debug for Stdin<'a, P> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let mut debug_struct = f.debug_struct("Stdin"); + let debug_struct = match self.node.fallible().name() { + Ok(name) => debug_struct.field("node", &name), + Err(e) => debug_struct.field("node", &Err::<(), _>(e)), + }; + + debug_struct.field("params", &self.params).finish() + } +} + +/// See [`Chosen::stdout`]. +pub struct Stdout<'a, P: ParserWithMode<'a>> { + pub node: Node<'a, P>, + pub params: Option<&'a str>, +} + +impl<'a, P: ParserWithMode<'a>> core::fmt::Debug for Stdout<'a, P> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let mut debug_struct = f.debug_struct("Stdin"); + let debug_struct = match self.node.fallible().name() { + Ok(name) => debug_struct.field("node", &name), + Err(e) => debug_struct.field("node", &Err::<(), _>(e)), + }; + + debug_struct.field("params", &self.params).finish() + } +} + pub struct StdInOutPath<'a> { path: &'a str, params: Option<&'a str>, @@ -202,8 +238,8 @@ impl<'a> StdInOutPath<'a> { /// ```rust /// # let fdt = fdt::Fdt::new_unaligned(include_bytes!("../../dtb/test.dtb")).unwrap(); /// # let chosen = fdt.root().chosen(); - /// let stdout = chosen.stdout().unwrap(); - /// let stdin = chosen.stdin().unwrap(); + /// let stdout = chosen.stdout_path().unwrap(); + /// let stdin = chosen.stdin_path().unwrap(); /// /// assert_eq!((stdout.path(), stdout.params()), ("/soc/uart@10000000", Some("115200"))); /// assert_eq!((stdin.path(), stdin.params()), ("/soc/uart@10000000", None)); diff --git a/src/nodes/cpus.rs b/src/nodes/cpus.rs index c52f3e9..b4e0d3f 100644 --- a/src/nodes/cpus.rs +++ b/src/nodes/cpus.rs @@ -64,7 +64,7 @@ impl<'a, P: ParserWithMode<'a>> Cpus<'a, P> { })) } - pub fn cpus(&self) -> P::Output> { + pub fn iter(&self) -> P::Output> { P::to_output(crate::tryblock!({ Ok(CpusIter { children: self.node.children()?.iter() }) })) } } @@ -147,6 +147,7 @@ impl<'a, P: ParserWithMode<'a>> Cpu<'a, P> { /// representation of PIR values if desired. #[inline] #[track_caller] + #[doc(alias = "ids")] pub fn reg(self) -> P::Output> { P::to_output(crate::tryblock!({ let Some(reg) = self.node.properties()?.find("reg")? else { diff --git a/src/nodes/root.rs b/src/nodes/root.rs index 713de88..a513d42 100644 --- a/src/nodes/root.rs +++ b/src/nodes/root.rs @@ -63,6 +63,7 @@ impl<'a, P: ParserWithMode<'a>> Root<'a, P> { /// `"manufacturer,model"` /// /// For example: `compatible = "fsl,mpc8572ds"` + #[track_caller] pub fn compatible(&self) -> P::Output> { P::to_output(crate::tryblock!({ >::parse(self.node.fallible(), self.node.make_root()?)? @@ -76,6 +77,7 @@ impl<'a, P: ParserWithMode<'a>> Root<'a, P> { /// **Optional** /// /// Specifies a string representing the device’s serial number. + #[track_caller] pub fn serial_number(&self) -> P::Output> { P::to_output(crate::tryblock!({ match self.node.fallible().properties()?.find("serial-number")? { @@ -101,6 +103,7 @@ impl<'a, P: ParserWithMode<'a>> Root<'a, P> { /// * "handset" /// * "watch" /// * "embedded" + #[track_caller] pub fn chassis_type(&self) -> P::Output> { P::to_output(crate::tryblock!({ match self.node.fallible().properties()?.find("serial-number")? { @@ -123,6 +126,7 @@ impl<'a, P: ParserWithMode<'a>> Root<'a, P> { /// specifies the alias name. The property value specifies the full path to /// a node in the devicetree. For example, the property `serial0 = /// "/simple-bus@fe000000/serial@llc500"` defines the alias `serial0`. + #[track_caller] pub fn aliases(&self) -> P::Output>> { P::to_output(crate::tryblock!({ let this: FallibleRoot<'a, P> = Root { node: self.node }; @@ -141,6 +145,7 @@ impl<'a, P: ParserWithMode<'a>> Root<'a, P> { /// The `/chosen` node does not represent a real device in the system but /// describes parameters chosen or specified by the system firmware at run /// time. It shall be a child of the root node. + #[track_caller] pub fn chosen(&self) -> P::Output> { P::to_output(crate::tryblock!({ let this: FallibleRoot<'a, P> = Root { node: self.node }; @@ -159,6 +164,7 @@ impl<'a, P: ParserWithMode<'a>> Root<'a, P> { /// A `/cpus` node is required for all devicetrees. It does not represent a /// real device in the system, but acts as a container for child cpu nodes /// which represent the systems CPUs. + #[track_caller] pub fn cpus(&self) -> P::Output> { P::to_output(crate::tryblock!({ let this: FallibleRoot<'a, P> = Root { node: self.node }; @@ -178,6 +184,7 @@ impl<'a, P: ParserWithMode<'a>> Root<'a, P> { /// physical memory layout for the system. If a system has multiple ranges /// of memory, multiple memory nodes can be created, or the ranges can be /// specified in the `reg` property of a single memory node. + #[track_caller] pub fn memory(&self) -> P::Output> { P::to_output(crate::tryblock!({ let this: FallibleRoot<'a, P> = Root { node: self.node }; @@ -196,6 +203,7 @@ impl<'a, P: ParserWithMode<'a>> Root<'a, P> { /// usage. One can create child nodes describing particular reserved /// (excluded from normal use) memory regions. Such memory regions are /// usually designed for the special usage by various device drivers. + #[track_caller] pub fn reserved_memory(&self) -> P::Output> { P::to_output(crate::tryblock!({ let this: FallibleRoot<'a, P> = Root { node: self.node }; @@ -225,6 +233,7 @@ impl<'a, P: ParserWithMode<'a>> Root<'a, P> { /// Returns an iterator that yields every node with the name that matches /// `name` in depth-first order + #[track_caller] pub fn find_all_nodes_with_name<'b>(self, name: &'b str) -> P::Output> { P::to_output(crate::tryblock!({ let this: FallibleRoot<'a, P> = Root { node: self.node }; @@ -234,6 +243,7 @@ impl<'a, P: ParserWithMode<'a>> Root<'a, P> { /// Attempt to find a node with the given name, returning the first node /// with a name that matches `name` in depth-first order + #[track_caller] pub fn find_node_by_name(self, name: &str) -> P::Output>> { P::to_output(crate::tryblock!({ let this: FallibleRoot<'a, P> = Root { node: self.node }; @@ -372,8 +382,8 @@ impl<'a, P: ParserWithMode<'a>> core::fmt::Debug for Root<'a, P> { } pub struct AllNodesWithNameIter<'a, 'b, P: ParserWithMode<'a>> { - iter: AllNodesIter<'a, (P::Parser, NoPanic)>, - name: &'b str, + pub(crate) iter: AllNodesIter<'a, (P::Parser, NoPanic)>, + pub(crate) name: &'b str, } impl<'a, 'b, P: ParserWithMode<'a>> Iterator for AllNodesWithNameIter<'a, 'b, P> { @@ -398,13 +408,13 @@ impl<'a, 'b, P: ParserWithMode<'a>> Iterator for AllNodesWithNameIter<'a, 'b, P> /// See [`Root::all_compatible`] pub struct AllCompatibleIter<'a, 'b, P: ParserWithMode<'a>> { #[allow(clippy::type_complexity)] - iter: core::iter::FilterMap< + pub(crate) iter: core::iter::FilterMap< AllNodesIter<'a, (P::Parser, NoPanic)>, fn( Result<(usize, FallibleNode<'a, P>), FdtError>, ) -> Option, Compatible<'a>), FdtError>>, >, - with: &'b [&'b str], + pub(crate) with: &'b [&'b str], } impl<'a, 'b, P: ParserWithMode<'a>> Iterator for AllCompatibleIter<'a, 'b, P> { @@ -427,9 +437,9 @@ impl<'a, 'b, P: ParserWithMode<'a>> Iterator for AllCompatibleIter<'a, 'b, P> { } pub struct AllNodesIter<'a, P: ParserWithMode<'a>> { - parser: P, - parents: [&'a [

>::Granularity]; 16], - parent_index: usize, + pub(crate) parser: P, + pub(crate) parents: [&'a [

>::Granularity]; 16], + pub(crate) parent_index: usize, } impl<'a, P: ParserWithMode<'a>> Iterator for AllNodesIter<'a, P> { diff --git a/src/properties/ranges.rs b/src/properties/ranges.rs index 1092517..71f9a0d 100644 --- a/src/properties/ranges.rs +++ b/src/properties/ranges.rs @@ -118,6 +118,7 @@ impl<'a, CAddr: CellCollector, PAddr: CellCollector, Len: CellCollector> Iterato } } +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Range { pub child_bus_address: CAddr, pub parent_bus_address: PAddr, diff --git a/src/properties/reg.rs b/src/properties/reg.rs index 7551785..48d64ee 100644 --- a/src/properties/reg.rs +++ b/src/properties/reg.rs @@ -120,18 +120,8 @@ impl<'a> Iterator for RegRawIter<'a> { #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct RawRegEntry<'a> { - address: &'a [u8], - len: &'a [u8], -} - -impl<'a> RawRegEntry<'a> { - pub fn address(self) -> &'a [u8] { - self.address - } - - pub fn len(self) -> &'a [u8] { - self.len - } + pub address: &'a [u8], + pub len: &'a [u8], } /// [Devicetree 2.3.7. diff --git a/src/tests.rs b/src/tests.rs index be7afc7..770c3ee 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -9,8 +9,11 @@ use properties::{ cells::CellSizes, interrupts::{ pci::{PciAddress, PciAddressHighBits}, - InterruptMap, + InterruptCells, InterruptMap, Interrupts, }, + ranges::Range, + reg::{RawRegEntry, RegEntry}, + Compatible, }; // use crate::{node::RawReg, *}; @@ -49,8 +52,8 @@ impl Align4 { } static TEST: Align4<3764> = Align4::new(AlignArrayUp(*include_bytes!("../dtb/test.dtb")).align_up::<3764>()); -static ISSUE_3: Align4<4658> = Align4::new(*include_bytes!("../dtb/issue-3.dtb")); -static SIFIVE: Align4<3872> = Align4::new(*include_bytes!("../dtb/sifive.dtb")); +static ISSUE_3: &[u8] = include_bytes!("../dtb/issue-3.dtb"); +static SIFIVE: &[u8] = include_bytes!("../dtb/sifive.dtb"); #[test] fn returns_fdt() { @@ -172,71 +175,90 @@ fn properties() { ); } -// #[test] -// fn correct_flash_regions() { -// let fdt = Fdt::new(TEST.as_slice()).unwrap(); -// let regions = fdt.find_node("/soc/flash").unwrap().reg().unwrap().collect::>(); - -// assert_eq!( -// regions, -// &[ -// MemoryRegion { starting_address: 0x20000000 as *const u8, size: Some(0x2000000) }, -// MemoryRegion { starting_address: 0x22000000 as *const u8, size: Some(0x2000000) } -// ] -// ); -// } - -// #[test] -// fn parses_populated_ranges() { -// let fdt = Fdt::new(TEST.as_slice()).unwrap(); -// let ranges = fdt.find_node("/soc/pci").unwrap().ranges().unwrap().collect::>(); - -// assert_eq!( -// ranges, -// &[ -// MemoryRange { -// child_bus_address: 0x0000_0000_0000_0000, -// child_bus_address_hi: 0x0100_0000, -// parent_bus_address: 0x3000000, -// size: 0x10000, -// }, -// MemoryRange { -// child_bus_address: 0x40000000, -// child_bus_address_hi: 0x2000000, -// parent_bus_address: 0x4000_0000, -// size: 0x4000_0000, -// } -// ] -// ); -// } - -// #[test] -// fn parses_empty_ranges() { -// let fdt = Fdt::new(TEST.as_slice()).unwrap(); -// let ranges = fdt.find_node("/soc").unwrap().ranges().unwrap().collect::>(); - -// assert_eq!(ranges, &[]); -// } - -// #[test] -// fn finds_with_addr() { -// let fdt = Fdt::new(TEST.as_slice()).unwrap(); -// assert_eq!(fdt.find_node("/soc/virtio_mmio@10004000").unwrap().name, "virtio_mmio@10004000"); -// } - -// #[test] -// fn compatibles() { -// let fdt = Fdt::new(TEST.as_slice()).unwrap(); -// let res = fdt -// .find_node("/soc/test") -// .unwrap() -// .compatible() -// .unwrap() -// .all() -// .all(|s| ["sifive,test1", "sifive,test0", "syscon"].contains(&s)); - -// assert!(res); -// } +#[test] +fn correct_flash_regions() { + let fdt = Fdt::new(TEST.as_slice()).unwrap(); + let regions = fdt + .find_node("/soc/flash") + .unwrap() + .reg() + .unwrap() + .iter::() + .collect::, _>>() + .unwrap(); + + assert_eq!( + regions, + &[RegEntry { address: 0x20000000, len: 0x2000000 }, RegEntry { address: 0x22000000, len: 0x2000000 }] + ); +} + +#[test] +fn parses_populated_ranges() { + let fdt = Fdt::new(TEST.as_slice()).unwrap(); + let ranges = fdt + .find_node("/soc/pci") + .unwrap() + .ranges() + .unwrap() + .iter::() + .collect::, _>>() + .unwrap(); + + assert_eq!( + ranges, + &[ + Range { + child_bus_address: PciAddress { hi: PciAddressHighBits::new(0x1000000), mid: 0, lo: 0 }, + parent_bus_address: 0x3000000, + len: 0x10000 + }, + Range { + child_bus_address: PciAddress { hi: PciAddressHighBits::new(0x2000000), mid: 0, lo: 0x40000000 }, + parent_bus_address: 0x4000_0000, + len: 0x4000_0000 + } + ] + ); +} + +#[test] +fn parses_empty_ranges() { + let fdt = Fdt::new(TEST.as_slice()).unwrap(); + let ranges = fdt + .find_node("/soc") + .unwrap() + .ranges() + .unwrap() + .iter::() + .collect::, _>>() + .unwrap(); + + assert_eq!(ranges, &[]); +} + +#[test] +fn finds_with_addr() { + let fdt = Fdt::new(TEST.as_slice()).unwrap(); + assert_eq!( + fdt.find_node("/soc/virtio_mmio@10004000").unwrap().name(), + NodeName { name: "virtio_mmio", unit_address: Some("10004000") } + ); +} + +#[test] +fn compatibles() { + let fdt = Fdt::new(TEST.as_slice()).unwrap(); + let res = fdt + .find_node("/soc/test") + .unwrap() + .property::() + .unwrap() + .into_iter() + .all(|s| ["sifive,test1", "sifive,test0", "syscon"].contains(&s)); + + assert!(res); +} #[test] fn cell_sizes() { @@ -292,147 +314,105 @@ fn interrupt_map() { } } -// #[test] -// fn no_properties() { -// let fdt = Fdt::new(TEST.as_slice()).unwrap(); -// let regions = fdt.find_node("/emptyproptest").unwrap(); -// assert_eq!(regions.properties().count(), 0); -// } - -// #[test] -// fn finds_all_nodes() { -// let fdt = Fdt::new(TEST.as_slice()).unwrap(); - -// let mut all_nodes: std::vec::Vec<_> = fdt.all_nodes().map(|n| n.name).collect(); -// all_nodes.sort_unstable(); - -// assert_eq!( -// all_nodes, -// &[ -// "/", -// "chosen", -// "clint@2000000", -// "cluster0", -// "core0", -// "cpu-map", -// "cpu@0", -// "cpus", -// "emptyproptest", -// "flash@20000000", -// "interrupt-controller", -// "memory@80000000", -// "pci@30000000", -// "plic@c000000", -// "poweroff", -// "reboot", -// "rtc@101000", -// "soc", -// "test@100000", -// "uart@10000000", -// "virtio_mmio@10001000", -// "virtio_mmio@10002000", -// "virtio_mmio@10003000", -// "virtio_mmio@10004000", -// "virtio_mmio@10005000", -// "virtio_mmio@10006000", -// "virtio_mmio@10007000", -// "virtio_mmio@10008000" -// ] -// ) -// } - -// #[test] -// fn required_nodes() { -// let fdt = Fdt::new(TEST.as_slice()).unwrap(); -// fdt.cpus().next().unwrap(); -// fdt.memory(); -// fdt.chosen(); -// } - -// #[test] -// fn doesnt_exist() { -// let fdt = Fdt::new(TEST.as_slice()).unwrap(); -// assert!(fdt.find_node("/this/doesnt/exist").is_none()); -// } - -// #[test] -// fn raw_reg() { -// let fdt = Fdt::new(TEST.as_slice()).unwrap(); -// let regions = -// fdt.find_node("/soc/flash").unwrap().raw_reg().unwrap().collect::>(); - -// assert_eq!( -// regions, -// &[ -// RawReg { address: &0x20000000u64.to_be_bytes(), size: &0x2000000u64.to_be_bytes() }, -// RawReg { address: &0x22000000u64.to_be_bytes(), size: &0x2000000u64.to_be_bytes() } -// ] -// ); -// } - -// #[test] -// fn issue_3() { -// let fdt = Fdt::new(ISSUE_3.as_slice()).unwrap(); -// fdt.find_all_nodes("uart").for_each(|n| std::println!("{:?}", n)); -// } - -// #[test] -// fn issue_4() { -// let fdt = Fdt::new(ISSUE_3.as_slice()).unwrap(); -// fdt.all_nodes().for_each(|n| std::println!("{:?}", n)); -// } - -// #[test] -// fn cpus() { -// let fdt = Fdt::new(TEST.as_slice()).unwrap(); -// for cpu in fdt.cpus() { -// cpu.ids().all().for_each(|n| std::println!("{:?}", n)); -// } -// } - -// #[test] -// fn invalid_node() { -// let fdt = Fdt::new(TEST.as_slice()).unwrap(); -// assert!(fdt.find_node("this/is/an invalid node///////////").is_none()); -// } - -// #[test] -// fn aliases() { -// let fdt = Fdt::new(SIFIVE.as_slice()).unwrap(); -// let aliases = fdt.aliases().unwrap(); -// for (_, node_path) in aliases.all() { -// assert!(fdt.find_node(node_path).is_some(), "path: {:?}", node_path); -// } -// } - -// #[test] -// fn stdout() { -// let fdt = Fdt::new(TEST.as_slice()).unwrap(); -// let stdout = fdt.chosen().stdout().unwrap(); -// assert!(stdout.node().name == "uart@10000000"); -// assert!(stdout.params() == Some("115200")); -// } - -// #[test] -// fn stdin() { -// let fdt = Fdt::new(TEST.as_slice()).unwrap(); -// let stdin = fdt.chosen().stdin().unwrap(); -// assert!(stdin.node().name == "uart@10000000"); -// assert!(stdin.params().is_none()); -// } - -// #[test] -// fn node_property_str_value() { -// let fdt = Fdt::new(TEST.as_slice()).unwrap(); -// let cpu0 = fdt.find_node("/cpus/cpu@0").unwrap(); -// assert_eq!(cpu0.property("riscv,isa").unwrap().as_str().unwrap(), "rv64imafdcsu"); -// } - -// #[test] -// fn model_value() { -// let fdt = Fdt::new(TEST.as_slice()).unwrap(); -// assert_eq!(fdt.root().model(), "riscv-virtio,qemu"); -// } +#[test] +fn no_properties() { + let fdt = Fdt::new(TEST.as_slice()).unwrap(); + let regions = fdt.find_node("/emptyproptest").unwrap(); + assert_eq!(regions.properties().into_iter().count(), 0); +} + +#[test] +fn required_nodes() { + let fdt = Fdt::new(TEST.as_slice()).unwrap(); + let root = fdt.root(); + root.cpus().iter().next().unwrap(); + root.memory(); + root.chosen(); +} + +#[test] +fn doesnt_exist() { + let fdt = Fdt::new(TEST.as_slice()).unwrap(); + assert!(fdt.find_node("/this/doesnt/exist").is_none()); +} + +#[test] +fn raw_reg() { + let fdt = Fdt::new(TEST.as_slice()).unwrap(); + let regions = fdt.find_node("/soc/flash").unwrap().reg().unwrap().iter_raw().collect::>(); + + assert_eq!( + regions, + &[ + RawRegEntry { address: &0x20000000u64.to_be_bytes(), len: &0x2000000u64.to_be_bytes() }, + RawRegEntry { address: &0x22000000u64.to_be_bytes(), len: &0x2000000u64.to_be_bytes() } + ] + ); +} + +#[test] +fn issue_3() { + let fdt = Fdt::new_unaligned(ISSUE_3).unwrap(); + fdt.find_all_nodes_with_name("uart").for_each(|n| std::println!("{:?}", n)); +} + +#[test] +fn issue_4() { + let fdt = Fdt::new_unaligned(ISSUE_3).unwrap(); + fdt.all_nodes().for_each(|n| std::println!("{:?}", n)); +} + +#[test] +fn cpus() { + let fdt = Fdt::new(TEST.as_slice()).unwrap(); + for cpu in fdt.root().cpus().iter() { + cpu.reg::().iter().for_each(|n| std::println!("{:?}", n)); + } +} + +#[test] +fn invalid_node() { + let fdt = Fdt::new(TEST.as_slice()).unwrap(); + assert!(fdt.find_node("this/is/an invalid node///////////").is_none()); +} + +#[test] +fn aliases() { + let fdt = Fdt::new_unaligned(SIFIVE).unwrap(); + let aliases = fdt.root().aliases().unwrap(); + for (_, node_path) in aliases.iter() { + assert!(fdt.find_node(node_path).is_some(), "path: {:?}", node_path); + } +} + +#[test] +fn stdout() { + let fdt = Fdt::new(TEST.as_slice()).unwrap(); + let stdout = fdt.root().chosen().stdout().unwrap(); + assert!(stdout.node.name() == NodeName { name: "uart", unit_address: Some("10000000") }); + assert!(stdout.params == Some("115200")); +} + +#[test] +fn stdin() { + let fdt = Fdt::new(TEST.as_slice()).unwrap(); + let stdin = fdt.root().chosen().stdin().unwrap(); + assert!(stdin.node.name() == NodeName { name: "uart", unit_address: Some("10000000") }); + assert!(stdin.params.is_none()); +} + +#[test] +fn node_property_str_value() { + let fdt = Fdt::new(TEST.as_slice()).unwrap(); + let cpu0 = fdt.find_node("/cpus/cpu@0").unwrap(); + assert_eq!(cpu0.properties().find("riscv,isa").unwrap().as_value::<&str>().unwrap(), "rv64imafdcsu"); +} + +#[test] +fn model_value() { + let fdt = Fdt::new(TEST.as_slice()).unwrap(); + assert_eq!(fdt.root().model(), "riscv-virtio,qemu"); +} #[test] fn memory_node() { @@ -441,10 +421,15 @@ fn memory_node() { assert_eq!(root.memory().reg().iter::().count(), 1); } -// #[test] -// fn interrupt_cells() { -// let fdt = Fdt::new(TEST.as_slice()).unwrap(); -// let uart = fdt.find_node("/soc/uart").unwrap(); -// std::println!("{:?}", uart.parent_interrupt_cells()); -// assert_eq!(uart.interrupts().unwrap().collect::>(), std::vec![0xA]); -// } +#[test] +fn interrupt_cells() { + let fdt = Fdt::new(TEST.as_slice()).unwrap(); + let uart = fdt.find_node("/soc/uart").unwrap(); + std::println!("{:?}", uart.parent().unwrap().property::()); + let interrupts = match uart.property::().unwrap() { + Interrupts::Legacy(legacy) => legacy, + _ => unreachable!(), + }; + + assert_eq!(interrupts.iter::().collect::, _>>().unwrap(), &[0xA]); +} From 2a8ef75f1e2a5764ca2a826afdbca05b1ac51a30 Mon Sep 17 00:00:00 2001 From: repnop Date: Sun, 5 Jan 2025 17:30:36 -0500 Subject: [PATCH 30/38] fix: missing token consumption in node parsing --- src/parsing.rs | 62 +++++++++++++++++++------------------------------- 1 file changed, 24 insertions(+), 38 deletions(-) diff --git a/src/parsing.rs b/src/parsing.rs index 2775cd5..de5e508 100644 --- a/src/parsing.rs +++ b/src/parsing.rs @@ -271,45 +271,30 @@ pub trait Parser<'a>: crate::sealed::Sealed + Clone { let starting_data = self.data(); - match core::mem::size_of::() { - 1 => { - let byte_data = self.byte_data(); - match byte_data.get(byte_data.len() - 4..).map(<[u8; 4]>::try_from) { - Some(Ok(data @ [_, _, _, _])) => match BigEndianToken(BigEndianU32(u32::from_ne_bytes(data))) { - BigEndianToken::END => {} - _ => return Err(FdtError::ParseError(ParseError::UnexpectedToken)), - }, - _ => return Err(FdtError::ParseError(ParseError::UnexpectedEndOfData)), - } - - Ok(Node { - this: RawNode::new(&starting_data[..starting_data.len() - 4]), - parent: None, - strings: self.strings(), - structs: self.structs(), - _mode: core::marker::PhantomData, - }) - } - 4 => { - let byte_data = self.byte_data(); - match byte_data.get(byte_data.len() - 4..).map(<[u8; 4]>::try_from) { - Some(Ok(data @ [_, _, _, _])) => match BigEndianToken(BigEndianU32(u32::from_ne_bytes(data))) { - BigEndianToken::END => {} - _ => return Err(FdtError::ParseError(ParseError::UnexpectedToken)), - }, - _ => return Err(FdtError::ParseError(ParseError::UnexpectedEndOfData)), - } - - Ok(Node { - this: RawNode::new(&starting_data[..starting_data.len() - 1]), - parent: None, - strings: self.strings(), - structs: self.structs(), - _mode: core::marker::PhantomData, - }) - } - _ => unreachable!(), + let byte_data = self.byte_data(); + match byte_data.get(byte_data.len() - 4..).map(<[u8; 4]>::try_from) { + Some(Ok(data @ [_, _, _, _])) => match BigEndianToken(BigEndianU32(u32::from_ne_bytes(data))) { + BigEndianToken::END => {} + _ => return Err(FdtError::ParseError(ParseError::UnexpectedToken)), + }, + _ => return Err(FdtError::ParseError(ParseError::UnexpectedEndOfData)), } + + let granularity_offset = const { + match core::mem::size_of::() { + 1 => 4, + 4 => 1, + _ => unreachable!(), + } + }; + + Ok(Node { + this: RawNode::new(&starting_data[..starting_data.len() - granularity_offset]), + parent: None, + strings: self.strings(), + structs: self.structs(), + _mode: core::marker::PhantomData, + }) } fn parse_node(&mut self, parent: Option<&'a RawNode>) -> Result, FdtError> @@ -339,6 +324,7 @@ pub trait Parser<'a>: crate::sealed::Sealed + Clone { 0 => break, _ => { depth -= 1; + let _ = self.advance_token(); continue; } }, From 3ed4acb943438d9111ab431a003455f07d6bffa8 Mon Sep 17 00:00:00 2001 From: repnop Date: Sun, 5 Jan 2025 18:28:23 -0500 Subject: [PATCH 31/38] fix: filter out non-`cpu` child nodes --- src/nodes/cpus.rs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/nodes/cpus.rs b/src/nodes/cpus.rs index b4e0d3f..a6e2bce 100644 --- a/src/nodes/cpus.rs +++ b/src/nodes/cpus.rs @@ -65,7 +65,9 @@ impl<'a, P: ParserWithMode<'a>> Cpus<'a, P> { } pub fn iter(&self) -> P::Output> { - P::to_output(crate::tryblock!({ Ok(CpusIter { children: self.node.children()?.iter() }) })) + P::to_output(crate::tryblock!({ + Ok(CpusIter { children: self.node.children()?.iter().filter(filter_cpus::

) }) + })) } } @@ -75,8 +77,20 @@ impl<'a, P: ParserWithMode<'a>> AsNode<'a, P> for Cpus<'a, P> { } } +fn filter_cpus<'a, P: ParserWithMode<'a>>(node: &Result, FdtError>) -> bool { + match node { + Ok(node) => match node.name().map(|n| n.name) { + Ok("cpu") => true, + _ => false, + }, + _ => true, + } +} pub struct CpusIter<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { - children: NodeChildrenIter<'a, (P::Parser, NoPanic)>, + children: core::iter::Filter< + NodeChildrenIter<'a, (P::Parser, NoPanic)>, + fn(&Result, FdtError>) -> bool, + >, } impl<'a, P: ParserWithMode<'a>> Iterator for CpusIter<'a, P> { From b6b56505c96f4601317ad6a7534984cea3fe94f1 Mon Sep 17 00:00:00 2001 From: repnop Date: Sun, 5 Jan 2025 20:33:43 -0500 Subject: [PATCH 32/38] wip: little refactors + add CPU topology nodes --- examples/tree_print.rs | 28 ++- src/helpers.rs | 14 ++ src/lib.rs | 12 +- src/nodes.rs | 5 +- src/nodes/aliases.rs | 7 +- src/nodes/chosen.rs | 3 +- src/nodes/cpus.rs | 320 +++++++++++++++++++++++++++++++++++ src/nodes/root.rs | 9 +- src/properties.rs | 16 +- src/properties/cells.rs | 2 +- src/properties/interrupts.rs | 9 +- src/properties/ranges.rs | 9 +- src/properties/reg.rs | 2 +- src/tests.rs | 41 ++++- 14 files changed, 434 insertions(+), 43 deletions(-) create mode 100644 src/helpers.rs diff --git a/examples/tree_print.rs b/examples/tree_print.rs index 14a4806..6e81cc3 100644 --- a/examples/tree_print.rs +++ b/examples/tree_print.rs @@ -1,20 +1,18 @@ -// use fdt::node::FdtNode; +use fdt::helpers::UnalignedInfallibleNode; -// static MY_FDT: &[u8] = include_bytes!("../dtb/test.dtb"); +static MY_FDT: &[u8] = include_bytes!("../dtb/test.dtb"); -// fn main() { -// let fdt = fdt::Fdt::new(MY_FDT).unwrap(); +fn main() { + let fdt = fdt::Fdt::new_unaligned(MY_FDT).unwrap(); -// print_node(fdt.find_node("/").unwrap(), 0); -// } + print_node(fdt.find_node("/").unwrap(), 0); +} -// fn print_node(node: FdtNode<'_, '_>, n_spaces: usize) { -// (0..n_spaces).for_each(|_| print!(" ")); -// println!("{}/", node.name); +fn print_node(node: UnalignedInfallibleNode<'_>, n_spaces: usize) { + (0..n_spaces).for_each(|_| print!(" ")); + println!("{}/", node.name()); -// for child in node.children() { -// print_node(child, n_spaces + 4); -// } -// } - -fn main() {} + for child in node.children() { + print_node(child, n_spaces + 4); + } +} diff --git a/src/helpers.rs b/src/helpers.rs new file mode 100644 index 0000000..b54577b --- /dev/null +++ b/src/helpers.rs @@ -0,0 +1,14 @@ +use crate::{ + nodes::{root::Root, Node}, + parsing::{aligned::AlignedParser, unaligned::UnalignedParser, NoPanic, Panic, ParserWithMode}, +}; + +pub type FallibleParser<'a, P> = (

>::Parser, NoPanic); +pub type FallibleNode<'a, P> = Node<'a, FallibleParser<'a, P>>; +pub type FallibleRoot<'a, P> = Root<'a, FallibleParser<'a, P>>; + +pub type AlignedFallibleNode<'a> = Node<'a, (AlignedParser<'a>, NoPanic)>; +pub type UnalignedFallibleNode<'a> = Node<'a, (UnalignedParser<'a>, NoPanic)>; + +pub type AlignedInfallibleNode<'a> = Node<'a, (AlignedParser<'a>, Panic)>; +pub type UnalignedInfallibleNode<'a> = Node<'a, (UnalignedParser<'a>, Panic)>; diff --git a/src/lib.rs b/src/lib.rs index 599f709..e42350a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -62,15 +62,17 @@ extern crate std; mod tests; pub mod cell_collector; +pub mod helpers; pub mod nodes; mod parsing; mod pretty_print; pub mod properties; mod util; +use helpers::FallibleParser; use nodes::{ root::{AllCompatibleIter, AllNodesIter, AllNodesWithNameIter, Root}, - FallibleParser, Node, + Node, }; use parsing::{ aligned::AlignedParser, unaligned::UnalignedParser, NoPanic, Panic, ParseError, Parser, ParserWithMode, @@ -93,11 +95,12 @@ pub enum FdtError { SliceTooSmall, /// An error was encountered during parsing ParseError(ParseError), - PHandleNotFound(u32), + MissingPHandleNode(u32), MissingParent, MissingRequiredNode(&'static str), MissingRequiredProperty(&'static str), InvalidPropertyValue, + InvalidNodeName, CollectCellsError, } @@ -114,7 +117,7 @@ impl core::fmt::Display for FdtError { FdtError::BadPtr => write!(f, "an invalid pointer was passed"), FdtError::SliceTooSmall => write!(f, "provided slice is too small"), FdtError::ParseError(e) => core::fmt::Display::fmt(e, f), - FdtError::PHandleNotFound(value) => { + FdtError::MissingPHandleNode(value) => { write!(f, "a node containing the `phandle` property value of `{value}` was not found") } FdtError::MissingParent => write!(f, "node parent is not present but needed to parse a property"), @@ -125,6 +128,9 @@ impl core::fmt::Display for FdtError { write!(f, "FDT node is missing a required property `{}`", name) } FdtError::InvalidPropertyValue => write!(f, "FDT property value is invalid"), + FdtError::InvalidNodeName => { + write!(f, "FDT node contained invalid characters or did not match the expected format") + } FdtError::CollectCellsError => { write!(f, "overflow occurred while collecting `#-cells` size values into the desired type") } diff --git a/src/nodes.rs b/src/nodes.rs index 67f5d74..de8f22e 100644 --- a/src/nodes.rs +++ b/src/nodes.rs @@ -5,6 +5,7 @@ pub mod memory; pub mod root; use crate::{ + helpers::FallibleNode, parsing::{ aligned::AlignedParser, BigEndianToken, NoPanic, Panic, PanicMode, ParseError, Parser, ParserWithMode, StringsBlock, StructsBlock, @@ -95,10 +96,6 @@ impl core::fmt::Display for NodeName<'_> { } } -pub type FallibleParser<'a, P> = (

>::Parser, NoPanic); -pub type FallibleNode<'a, P> = Node<'a, FallibleParser<'a, P>>; -pub type FallibleRoot<'a, P> = Root<'a, FallibleParser<'a, P>>; - /// A generic devicetree node. pub struct Node<'a, P: ParserWithMode<'a>> { pub(crate) this: &'a RawNode<

>::Granularity>, diff --git a/src/nodes/aliases.rs b/src/nodes/aliases.rs index e4276f9..b09b642 100644 --- a/src/nodes/aliases.rs +++ b/src/nodes/aliases.rs @@ -1,5 +1,8 @@ -use super::{AsNode, FallibleNode, FallibleParser, Node, NodePropertiesIter}; -use crate::parsing::{NoPanic, ParserWithMode}; +use super::{AsNode, Node, NodePropertiesIter}; +use crate::{ + helpers::{FallibleNode, FallibleParser}, + parsing::{NoPanic, ParserWithMode}, +}; /// [Devicetree 3.3. `/aliases` /// node](https://devicetree-specification.readthedocs.io/en/latest/chapter3-devicenodes.html#aliases-node) diff --git a/src/nodes/chosen.rs b/src/nodes/chosen.rs index 86d91f5..966a040 100644 --- a/src/nodes/chosen.rs +++ b/src/nodes/chosen.rs @@ -2,8 +2,9 @@ // v. 2.0. If a copy of the MPL was not distributed with this file, You can // obtain one at https://mozilla.org/MPL/2.0/. -use super::{FallibleNode, FallibleParser, FallibleRoot, Node}; +use super::{FallibleNode, Node}; use crate::{ + helpers::{FallibleParser, FallibleRoot}, parsing::{aligned::AlignedParser, Panic, ParseError, ParserWithMode}, FdtError, }; diff --git a/src/nodes/cpus.rs b/src/nodes/cpus.rs index a6e2bce..677b14e 100644 --- a/src/nodes/cpus.rs +++ b/src/nodes/cpus.rs @@ -5,6 +5,7 @@ use crate::{ properties::{ cells::{AddressCells, CellSizes}, values::StringList, + PHandle, }, FdtError, }; @@ -64,6 +65,17 @@ impl<'a, P: ParserWithMode<'a>> Cpus<'a, P> { })) } + /// Returns the (optional) `cpu-map` child node, which describes the system + /// socket and CPU topology. See [`CpuTopology`] for more details. + pub fn topology(&self) -> P::Output>> { + P::to_output(crate::tryblock!({ + match self.node.children()?.find("cpu-map")? { + Some(node) => Ok(Some(CpuTopology { node })), + None => Ok(None), + } + })) + } + pub fn iter(&self) -> P::Output> { P::to_output(crate::tryblock!({ Ok(CpusIter { children: self.node.children()?.iter().filter(filter_cpus::

) }) @@ -86,6 +98,7 @@ fn filter_cpus<'a, P: ParserWithMode<'a>>(node: &Result, Fdt _ => true, } } + pub struct CpusIter<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { children: core::iter::Filter< NodeChildrenIter<'a, (P::Parser, NoPanic)>, @@ -642,3 +655,310 @@ impl<'a, C: CellCollector> Iterator for CpuIdsIter<'a, C> { Some(Ok(C::map(collector.finish()))) } } + +/// [Linux Kernel Devicetree Bindings - CPU topology binding +/// description](https://www.kernel.org/doc/Documentation/devicetree/bindings/cpu/cpu-topology.txt) +/// +/// In a SMP system, the hierarchy of CPUs is defined through three entities +/// that are used to describe the layout of physical CPUs in the system: +/// +/// - socket +/// - cluster +/// - core +/// - thread +/// +/// The bottom hierarchy level sits at core or thread level depending on whether +/// symmetric multi-threading (SMT) is supported or not. +/// +/// For instance in a system where CPUs support SMT, "cpu" nodes represent all +/// threads existing in the system and map to the hierarchy level "thread" +/// above. In systems where SMT is not supported "cpu" nodes represent all cores +/// present in the system and map to the hierarchy level "core" above. +/// +/// CPU topology bindings allow one to associate cpu nodes with hierarchical +/// groups corresponding to the system hierarchy; syntactically they are defined +/// as device tree nodes. +/// +/// Currently, only ARM/RISC-V intend to use this cpu topology binding but it +/// may be used for any other architecture as well. +/// +/// The cpu nodes, as per bindings defined in [4][4], represent the devices that +/// correspond to physical CPUs and are to be mapped to the hierarchy levels. +/// +/// A topology description containing phandles to cpu nodes that are not +/// compliant with bindings standardized in [4][4] is therefore considered invalid. +/// +/// [4]: https://www.devicetree.org/specifications/ +pub struct CpuTopology<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { + node: FallibleNode<'a, P>, +} + +impl<'a, P: ParserWithMode<'a>> CpuTopology<'a, P> { + /// Returns an iterator over all top-level [`CpuSocket`] children. Sockets are + /// optional for single-socket systems, so this may not return any sockets. + /// If that is the case, iterate over the clusters instead. + pub fn sockets(&self) -> P::Output> { + P::to_output(crate::tryblock!({ + Ok(CpuSocketIter { children: self.node.children()?.iter().filter(filter_sockets::

) }) + })) + } + + /// Returns an iterator over all top-level [`CpuCluster`] children. Clusters may be + /// contained underneath socket nodes, so if the iterator is empty, iterate + /// over the sockets instead. + pub fn clusters(&self) -> P::Output> { + P::to_output(crate::tryblock!({ + Ok(CpuClusterIter { children: self.node.children()?.iter().filter(filter_clusters::

) }) + })) + } +} + +fn filter_sockets<'a, P: ParserWithMode<'a>>(node: &Result, FdtError>) -> bool { + match node { + Ok(node) => match node.name().map(|n| n.name) { + Ok(n) if n.starts_with("socket") => true, + _ => false, + }, + _ => true, + } +} + +pub struct CpuSocketIter<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { + children: core::iter::Filter< + NodeChildrenIter<'a, (P::Parser, NoPanic)>, + fn(&Result, FdtError>) -> bool, + >, +} + +impl<'a, P: ParserWithMode<'a>> Iterator for CpuSocketIter<'a, P> { + type Item = P::Output>; + + fn next(&mut self) -> Option { + match self.children.next()? { + Ok(node) => Some(P::to_output(Ok(CpuSocket { node }))), + Err(e) => Some(P::to_output(Err(e))), + } + } +} + +fn filter_clusters<'a, P: ParserWithMode<'a>>(node: &Result, FdtError>) -> bool { + match node { + Ok(node) => match node.name().map(|n| n.name) { + Ok(n) if n.starts_with("cluster") => true, + _ => false, + }, + _ => true, + } +} + +pub struct CpuClusterIter<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { + children: core::iter::Filter< + NodeChildrenIter<'a, (P::Parser, NoPanic)>, + fn(&Result, FdtError>) -> bool, + >, +} + +impl<'a, P: ParserWithMode<'a>> Iterator for CpuClusterIter<'a, P> { + type Item = P::Output>; + + #[track_caller] + fn next(&mut self) -> Option { + match self.children.next()? { + Ok(node) => Some(P::to_output(Ok(CpuCluster { node }))), + Err(e) => Some(P::to_output(Err(e))), + } + } +} + +/// A physical CPU socket. +pub struct CpuSocket<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { + node: FallibleNode<'a, P>, +} + +impl<'a, P: ParserWithMode<'a>> CpuSocket<'a, P> { + /// Returns the socket number for this particular socket, e.g. the `0` in + /// `socket0`. + pub fn id(&self) -> P::Output { + P::to_output(crate::tryblock!({ + match self.node.name()?.name.trim_start_matches("socket").parse() { + Ok(id) => Ok(id), + Err(_) => Err(FdtError::InvalidNodeName), + } + })) + } + + /// Returns an iterator over the [`CpuCluster`]s contained by this socket. + pub fn clusters(&self) -> P::Output> { + P::to_output(crate::tryblock!({ + Ok(CpuClusterIter { children: self.node.children()?.iter().filter(filter_clusters::

) }) + })) + } +} + +/// A CPU cluster that is made up of either one or more clusters, or one or more [`CpuCore`]s. +pub struct CpuCluster<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { + node: FallibleNode<'a, P>, +} + +impl<'a, P: ParserWithMode<'a>> CpuCluster<'a, P> { + /// Returns the cluster number for this particular cluster, e.g. the `0` in + /// `cluster0`. + pub fn id(&self) -> P::Output { + P::to_output(crate::tryblock!({ + match self.node.name()?.name.trim_start_matches("cluster").parse() { + Ok(id) => Ok(id), + Err(_) => Err(FdtError::InvalidNodeName), + } + })) + } + + /// Returns an iterator over the [`CpuCore`]s contained by this cluster. + pub fn cores(&self) -> P::Output> { + P::to_output(crate::tryblock!({ + Ok(CpuCoreIter { children: self.node.children()?.iter().filter(filter_cores::

) }) + })) + } +} + +fn filter_cores<'a, P: ParserWithMode<'a>>(node: &Result, FdtError>) -> bool { + match node { + Ok(node) => match node.name().map(|n| n.name) { + Ok(n) if n.starts_with("core") => true, + _ => false, + }, + _ => true, + } +} + +pub struct CpuCoreIter<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { + children: core::iter::Filter< + NodeChildrenIter<'a, (P::Parser, NoPanic)>, + fn(&Result, FdtError>) -> bool, + >, +} + +impl<'a, P: ParserWithMode<'a>> Iterator for CpuCoreIter<'a, P> { + type Item = P::Output>; + + #[track_caller] + fn next(&mut self) -> Option { + match self.children.next()? { + Ok(node) => Some(P::to_output(Ok(CpuCore { node }))), + Err(e) => Some(P::to_output(Err(e))), + } + } +} + +/// A physical CPU core, which may be described by a `cpu` node or a set of +/// threads if symmetric multithreading (SMT) is enabled. +pub struct CpuCore<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { + node: FallibleNode<'a, P>, +} + +impl<'a, P: ParserWithMode<'a>> CpuCore<'a, P> { + /// Returns the core number for this particular core, e.g. the `0` in + /// `core0`. + pub fn id(&self) -> P::Output { + P::to_output(crate::tryblock!({ + match self.node.name()?.name.trim_start_matches("core").parse() { + Ok(id) => Ok(id), + Err(_) => Err(FdtError::InvalidNodeName), + } + })) + } + + /// If this core is described by a single physical CPU core (that is, if SMT + /// is not enabled), return the `/cpus/cpu@N` node that represents this + /// code. See [`Cpu`] for more details. If this returns [`None`], the core is + /// represented by one or more [`CpuThread`]s. + pub fn cpu(&self) -> P::Output>> { + P::to_output(crate::tryblock!({ + let phandle = match self.node.properties()?.find("cpu")? { + Some(property) => PHandle::new(property.as_value::()?), + None => return Ok(None), + }; + + Ok(Some(Cpu { + node: self + .node + .make_root()? + .resolve_phandle(phandle)? + .ok_or(FdtError::MissingPHandleNode(phandle.as_u32()))?, + })) + })) + } + + /// Returns an iterator over all threads described by this CPU core. If this + /// iterator does not return any [`CpuThread`]s, SMT is not enabled and the + /// core is described by a single [`Cpu`] which can be retreived by + /// [`CpuCore::cpu`]. + pub fn threads(&self) -> P::Output> { + P::to_output(crate::tryblock!({ + Ok(CpuThreadIter { children: self.node.children()?.iter().filter(filter_threads::

) }) + })) + } +} + +fn filter_threads<'a, P: ParserWithMode<'a>>(node: &Result, FdtError>) -> bool { + match node { + Ok(node) => match node.name().map(|n| n.name) { + Ok(n) if n.starts_with("thread") => true, + _ => false, + }, + _ => true, + } +} + +pub struct CpuThreadIter<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { + children: core::iter::Filter< + NodeChildrenIter<'a, (P::Parser, NoPanic)>, + fn(&Result, FdtError>) -> bool, + >, +} + +impl<'a, P: ParserWithMode<'a>> Iterator for CpuThreadIter<'a, P> { + type Item = P::Output>; + + #[track_caller] + fn next(&mut self) -> Option { + match self.children.next()? { + Ok(node) => Some(P::to_output(Ok(CpuThread { node }))), + Err(e) => Some(P::to_output(Err(e))), + } + } +} + +/// A logical CPU thread of execution. A single [`CpuCore`] may contain multiple +/// threads if symmetric multithreading is enabled. +pub struct CpuThread<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { + node: FallibleNode<'a, P>, +} + +impl<'a, P: ParserWithMode<'a>> CpuThread<'a, P> { + /// Returns the thread number for this particular thread, e.g. the `0` in + /// `thread0`. + pub fn id(&self) -> P::Output { + P::to_output(crate::tryblock!({ + match self.node.name()?.name.trim_start_matches("socket").parse() { + Ok(id) => Ok(id), + Err(_) => Err(FdtError::InvalidNodeName), + } + })) + } + + /// Returns the [`Cpu`] that is represented by this thread. + pub fn cpu(&self) -> P::Output> { + P::to_output(crate::tryblock!({ + let phandle = match self.node.properties()?.find("cpu")? { + Some(property) => PHandle::new(property.as_value::()?), + None => return Err(FdtError::MissingRequiredProperty("cpu")), + }; + + self.node + .make_root()? + .resolve_phandle(phandle)? + .map(|node| Cpu { node }) + .ok_or(FdtError::MissingPHandleNode(phandle.as_u32())) + })) + } +} diff --git a/src/nodes/root.rs b/src/nodes/root.rs index a513d42..5942ea3 100644 --- a/src/nodes/root.rs +++ b/src/nodes/root.rs @@ -3,9 +3,10 @@ use super::{ chosen::Chosen, cpus::Cpus, memory::{Memory, ReservedMemory}, - FallibleNode, FallibleRoot, IntoSearchableNodeName, Node, RawNode, SearchableNodeName, + AsNode, IntoSearchableNodeName, Node, RawNode, SearchableNodeName, }; use crate::{ + helpers::{FallibleNode, FallibleRoot}, parsing::{aligned::AlignedParser, BigEndianToken, NoPanic, Panic, ParseError, Parser, ParserWithMode}, properties::{cells::CellSizes, Compatible, PHandle, Property}, FdtError, @@ -368,6 +369,12 @@ impl<'a, P: ParserWithMode<'a>> Root<'a, P> { } } +impl<'a, P: ParserWithMode<'a>> AsNode<'a, P> for Root<'a, P> { + fn as_node(&self) -> Node<'a, P> { + self.node.alt() + } +} + impl<'a, P: ParserWithMode<'a>> Copy for Root<'a, P> {} impl<'a, P: ParserWithMode<'a>> Clone for Root<'a, P> { fn clone(&self) -> Self { diff --git a/src/properties.rs b/src/properties.rs index 1db467f..d244d38 100644 --- a/src/properties.rs +++ b/src/properties.rs @@ -9,7 +9,7 @@ pub mod reg; pub mod values; use crate::{ - nodes::{FallibleNode, FallibleRoot}, + helpers::{FallibleNode, FallibleRoot}, parsing::{BigEndianU32, ParserWithMode}, FdtError, }; @@ -164,6 +164,20 @@ impl<'a> core::cmp::PartialEq> for str { #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct PHandle(BigEndianU32); +impl PHandle { + /// Create a new [`PHandle`] using an existing handle ID. + /// + /// Note: this function will convert the handle ID to big endian, so it + /// should be in the platform's native endianness. + pub fn new(handle: u32) -> Self { + Self(BigEndianU32::from_ne(handle)) + } + + pub fn as_u32(self) -> u32 { + self.0.to_ne() + } +} + impl<'a, P: ParserWithMode<'a>> Property<'a, P> for PHandle { fn parse(node: FallibleNode<'a, P>, _: FallibleRoot<'a, P>) -> Result, FdtError> { let Some(phandle) = node.properties()?.find("phandle")? else { diff --git a/src/properties/cells.rs b/src/properties/cells.rs index d689b8f..caec55e 100644 --- a/src/properties/cells.rs +++ b/src/properties/cells.rs @@ -1,6 +1,6 @@ use super::Property; use crate::{ - nodes::{FallibleNode, FallibleRoot}, + helpers::{FallibleNode, FallibleRoot}, parsing::{unaligned::UnalignedParser, Parser, ParserWithMode, StringsBlock, StructsBlock}, FdtError, }; diff --git a/src/properties/interrupts.rs b/src/properties/interrupts.rs index 5e72e47..d3f1de5 100644 --- a/src/properties/interrupts.rs +++ b/src/properties/interrupts.rs @@ -3,7 +3,8 @@ pub mod pci; use super::{cells::AddressCells, PHandle, Property}; use crate::{ cell_collector::{BuildCellCollector, CellCollector, CollectCellsError}, - nodes::{root::Root, FallibleNode, FallibleRoot, Node}, + helpers::{FallibleNode, FallibleRoot}, + nodes::{root::Root, Node}, parsing::{aligned::AlignedParser, BigEndianU32, NoPanic, Panic, ParserWithMode}, FdtError, }; @@ -180,7 +181,7 @@ impl<'a, P: ParserWithMode<'a>> Iterator for ExtendedInterruptsIter<'a, P> { let res = crate::tryblock!({ let root: FallibleRoot<'a, P> = Root { node: self.root.node.fallible() }; let Some(interrupt_parent) = root.resolve_phandle(phandle)? else { - return Err(FdtError::PHandleNotFound(phandle.0.to_ne())); + return Err(FdtError::MissingPHandleNode(phandle.0.to_ne())); }; let Some(interrupt_cells) = interrupt_parent.property::()? else { @@ -369,7 +370,7 @@ impl<'a, P: ParserWithMode<'a>> Property<'a, P> for InterruptParent<'a, P> { match node.properties()?.find("interrupt-parent")? { Some(phandle) => match root.resolve_phandle(PHandle(phandle.as_value()?))? { Some(parent) => Ok(Some(Self(parent.alt()))), - None => Err(FdtError::PHandleNotFound(phandle.as_value()?)), + None => Err(FdtError::MissingPHandleNode(phandle.as_value()?)), }, None => Ok(node.parent().map(|n| Self(n.alt()))), } @@ -626,7 +627,7 @@ impl< let phandle = u32::from_ne_bytes(interrupt_parent.try_into().unwrap()); let interrupt_parent = root .resolve_phandle(PHandle(BigEndianU32::from_be(phandle)))? - .ok_or(FdtError::PHandleNotFound(phandle.swap_bytes()))?; + .ok_or(FdtError::MissingPHandleNode(phandle.swap_bytes()))?; let parent_address_cells = interrupt_parent .property::()? diff --git a/src/properties/ranges.rs b/src/properties/ranges.rs index 71f9a0d..588ef11 100644 --- a/src/properties/ranges.rs +++ b/src/properties/ranges.rs @@ -4,8 +4,8 @@ use super::{ }; use crate::{ cell_collector::{BuildCellCollector, CellCollector, CollectCellsError}, - nodes::{root::Root, FallibleNode}, - parsing::{NoPanic, ParserWithMode}, + helpers::{FallibleNode, FallibleRoot}, + parsing::ParserWithMode, FdtError, }; @@ -37,10 +37,7 @@ impl<'a> Ranges<'a> { } impl<'a, P: ParserWithMode<'a>> Property<'a, P> for Ranges<'a> { - fn parse( - node: FallibleNode<'a, P>, - _: Root<'a, (

>::Parser, NoPanic)>, - ) -> Result, FdtError> { + fn parse(node: FallibleNode<'a, P>, _: FallibleRoot<'a, P>) -> Result, FdtError> { let Some(ranges) = node.properties()?.find("ranges")? else { return Ok(None); }; diff --git a/src/properties/reg.rs b/src/properties/reg.rs index 48d64ee..45f6244 100644 --- a/src/properties/reg.rs +++ b/src/properties/reg.rs @@ -1,7 +1,7 @@ use super::{cells::CellSizes, Property}; use crate::{ cell_collector::{BuildCellCollector, CellCollector, CollectCellsError}, - nodes::{FallibleNode, FallibleRoot}, + helpers::{FallibleNode, FallibleRoot}, parsing::ParserWithMode, FdtError, }; diff --git a/src/tests.rs b/src/tests.rs index 770c3ee..53d2dca 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -4,7 +4,8 @@ extern crate std; -use nodes::NodeName; +use crate::*; +use nodes::{AsNode, NodeName}; use properties::{ cells::CellSizes, interrupts::{ @@ -16,9 +17,6 @@ use properties::{ Compatible, }; -// use crate::{node::RawReg, *}; -use crate::*; - struct AlignArrayUp([u8; N]); impl AlignArrayUp { @@ -366,6 +364,14 @@ fn issue_4() { fn cpus() { let fdt = Fdt::new(TEST.as_slice()).unwrap(); for cpu in fdt.root().cpus().iter() { + std::println!( + "{:?}", + cpu.as_node() + .properties() + .iter() + .map(|p| std::format!("{}={:?}", p.name(), p.value())) + .collect::>() + ); cpu.reg::().iter().for_each(|n| std::println!("{:?}", n)); } } @@ -433,3 +439,30 @@ fn interrupt_cells() { assert_eq!(interrupts.iter::().collect::, _>>().unwrap(), &[0xA]); } + +#[test] +fn cpu_map() { + let fdt = Fdt::new(TEST.as_slice()).unwrap(); + let topology = fdt.root().cpus().topology().unwrap(); + + assert_eq!(topology.sockets().count(), 0); + + assert_eq!(topology.clusters().next().unwrap().id(), 0); + assert_eq!(topology.clusters().next().unwrap().cores().next().unwrap().id(), 0); + assert_eq!( + topology + .clusters() + .next() + .unwrap() + .cores() + .next() + .unwrap() + .cpu() + .unwrap() + .as_node() + .raw_property("riscv,isa") + .unwrap() + .value(), + b"rv64imafdcsu\0" + ); +} From 4174cb6334979c91c9fed44effc9ae2b6880485d Mon Sep 17 00:00:00 2001 From: repnop Date: Sun, 12 Jan 2025 22:40:25 -0500 Subject: [PATCH 33/38] wip: more docs + remove some methods in favor of direct field access --- src/nodes.rs | 49 +++++++++++++++++++++++++----------- src/nodes/aliases.rs | 2 +- src/nodes/chosen.rs | 8 +++--- src/nodes/cpus.rs | 16 ++++++------ src/nodes/memory.rs | 10 ++++---- src/pretty_print.rs | 12 ++++----- src/properties.rs | 21 +++++++++++++--- src/properties/cells.rs | 4 +-- src/properties/interrupts.rs | 12 ++++----- src/properties/ranges.rs | 2 +- src/properties/reg.rs | 2 +- src/tests.rs | 10 ++++---- 12 files changed, 91 insertions(+), 57 deletions(-) diff --git a/src/nodes.rs b/src/nodes.rs index de8f22e..3509d75 100644 --- a/src/nodes.rs +++ b/src/nodes.rs @@ -36,16 +36,20 @@ pub trait AsNode<'a, P: ParserWithMode<'a>> { fn as_node(&self) -> Node<'a, P>; } +/// A node name that can searched with. #[derive(Debug, Clone, Copy)] pub enum SearchableNodeName<'a> { + /// Node name without the unit address Base(&'a str), + /// Node name with the unit address WithUnitAddress(NodeName<'a>), } /// Convert from a type that can potentially represent a node name that is able /// to be searched for during lookup operations. /// -/// Currently, two type impls are defined: +/// Currently, two type impls are defined on types other than +/// [`SearchableNodeName`]: /// 1. [`NodeName`]: corresponds directly to a /// [`SearchableNodeName::WithUnitAddress`]. /// 2. [`&str`]: attempts to parse the `str` as `name@unit-address`, @@ -56,6 +60,13 @@ pub trait IntoSearchableNodeName<'a>: Sized + crate::sealed::Sealed { fn into_searchable_node_name(self) -> SearchableNodeName<'a>; } +impl crate::sealed::Sealed for SearchableNodeName<'_> {} +impl<'a> IntoSearchableNodeName<'a> for SearchableNodeName<'a> { + fn into_searchable_node_name(self) -> SearchableNodeName<'a> { + self + } +} + impl crate::sealed::Sealed for NodeName<'_> {} impl<'a> IntoSearchableNodeName<'a> for NodeName<'a> { fn into_searchable_node_name(self) -> SearchableNodeName<'a> { @@ -75,9 +86,12 @@ impl<'a> IntoSearchableNodeName<'a> for &'a str { } } +/// A node name, split into its component parts. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct NodeName<'a> { + /// Node name. pub name: &'a str, + /// Optional unit address specified after the `@`. pub unit_address: Option<&'a str>, } @@ -108,7 +122,7 @@ pub struct Node<'a, P: ParserWithMode<'a>> { impl<'a, P: ParserWithMode<'a>> Node<'a, P> { /// Change the type of this node's [`PanicMode`] to [`NoPanic`]. #[inline(always)] - pub(crate) fn fallible(self) -> FallibleNode<'a, P> { + pub fn fallible(self) -> FallibleNode<'a, P> { self.alt() } @@ -377,6 +391,8 @@ impl RawNode { } } +/// Allows for searching and iterating over all of the properties of a given +/// [`Node`]. pub struct NodeProperties<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { data: &'a [

>::Granularity], strings: StringsBlock<'a>, @@ -394,6 +410,7 @@ impl<'a, P: ParserWithMode<'a>> NodeProperties<'a, P> { } } + /// Create an iterator over the properties in the [`Node`]. #[inline(always)] pub fn iter(self) -> NodePropertiesIter<'a, P> { NodePropertiesIter { properties: self.alt(), _mode: core::marker::PhantomData } @@ -426,6 +443,7 @@ impl<'a, P: ParserWithMode<'a>> NodeProperties<'a, P> { })) } + /// Attempt to find a property with the provided name. #[inline] #[track_caller] pub fn find(&self, name: &str) -> P::Output>> { @@ -465,6 +483,7 @@ impl<'a, P: ParserWithMode<'a>> IntoIterator for NodeProperties<'a, P> { } } +/// See [`NodeProperties::iter`]. #[derive(Clone)] pub struct NodePropertiesIter<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { properties: NodeProperties<'a, (P::Parser, NoPanic)>, @@ -486,10 +505,13 @@ impl<'a, P: ParserWithMode<'a>> Iterator for NodePropertiesIter<'a, P> { } } +/// Generic node property. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub struct NodeProperty<'a> { - name: &'a str, - value: &'a [u8], + /// Property name. + pub name: &'a str, + /// Raw property value. + pub value: &'a [u8], } impl<'a> NodeProperty<'a> { @@ -498,22 +520,15 @@ impl<'a> NodeProperty<'a> { Self { name, value } } - #[inline(always)] - pub fn name(&self) -> &'a str { - self.name - } - - #[inline(always)] - pub fn value(&self) -> &'a [u8] { - self.value - } - + /// Attempt to convert this property's value to the specified + /// [`PropertyValue`] type. #[inline(always)] pub fn as_value>(&self) -> Result { V::parse(self.value) } } +/// Allows for searching and iterating over the children of a given [`Node`]. pub struct NodeChildren<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { data: &'a [

>::Granularity], parent: &'a RawNode<

>::Granularity>, @@ -523,6 +538,7 @@ pub struct NodeChildren<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> } impl<'a, P: ParserWithMode<'a>> NodeChildren<'a, P> { + /// Create an iterator over the [`Node`]'s children. #[inline(always)] pub fn iter(&self) -> NodeChildrenIter<'a, P> { NodeChildrenIter { @@ -559,6 +575,10 @@ impl<'a, P: ParserWithMode<'a>> NodeChildren<'a, P> { }) } + /// Attempt to find the first child matching the provided name, see + /// [`IntoSearchableNodeName`] for more details. If the name lacks a unit + /// address, unit addresses on the children will be ignored when checking if + /// the name matches. #[inline] #[track_caller] pub fn find<'n, N>(&self, name: N) -> P::Output>> @@ -609,6 +629,7 @@ impl<'a, P: ParserWithMode<'a>> IntoIterator for NodeChildren<'a, P> { } } +/// See [`NodeChildren::iter`]. #[derive(Clone)] pub struct NodeChildrenIter<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { children: NodeChildren<'a, (P::Parser, NoPanic)>, diff --git a/src/nodes/aliases.rs b/src/nodes/aliases.rs index b09b642..26d7e66 100644 --- a/src/nodes/aliases.rs +++ b/src/nodes/aliases.rs @@ -84,7 +84,7 @@ where #[track_caller] fn next(&mut self) -> Option { Some(P::to_output(match self.properties.next() { - Some(Ok(prop)) => crate::tryblock!({ Ok((prop.name(), prop.as_value::<&'a str>()?)) }), + Some(Ok(prop)) => crate::tryblock!({ Ok((prop.name, prop.as_value::<&'a str>()?)) }), Some(Err(e)) => Err(e), None => return None, })) diff --git a/src/nodes/chosen.rs b/src/nodes/chosen.rs index 966a040..69eb3b0 100644 --- a/src/nodes/chosen.rs +++ b/src/nodes/chosen.rs @@ -30,9 +30,9 @@ impl<'a, P: ParserWithMode<'a>> Chosen<'a, P> { pub fn bootargs(self) -> P::Output> { P::to_output(crate::tryblock!({ for prop in self.node.properties()?.into_iter().flatten() { - if prop.name() == "bootargs" { + if prop.name == "bootargs" { return Ok(Some( - core::str::from_utf8(&prop.value()[..prop.value().len() - 1]) + core::str::from_utf8(&prop.value[..prop.value.len() - 1]) .map_err(|_| FdtError::ParseError(ParseError::InvalidCStrValue))?, )); } @@ -91,7 +91,7 @@ impl<'a, P: ParserWithMode<'a>> Chosen<'a, P> { .into_iter() .find_map(|n| match n { Err(e) => Some(Err(e)), - Ok(property) => match property.name() == "stdout-path" { + Ok(property) => match property.name == "stdout-path" { false => None, true => Some(property.as_value::<&'a str>().map_err(Into::into).map(|s| { let (path, params) = @@ -151,7 +151,7 @@ impl<'a, P: ParserWithMode<'a>> Chosen<'a, P> { .into_iter() .find_map(|n| match n { Err(e) => Some(Err(e)), - Ok(property) => match property.name() == "stdin-path" { + Ok(property) => match property.name == "stdin-path" { false => None, true => Some(property.as_value::<&str>().map_err(Into::into).map(|s| { let (path, params) = diff --git a/src/nodes/cpus.rs b/src/nodes/cpus.rs index 677b14e..56d322e 100644 --- a/src/nodes/cpus.rs +++ b/src/nodes/cpus.rs @@ -37,7 +37,7 @@ impl<'a, P: ParserWithMode<'a>> Cpus<'a, P> { pub fn common_timebase_frequency(&self) -> P::Output> { P::to_output(crate::tryblock!({ match self.node.properties()?.find("timebase-frequency")? { - Some(prop) => match prop.value().len() { + Some(prop) => match prop.value.len() { 4 => Ok(Some(u64::from(prop.as_value::()?))), 8 => Ok(Some(prop.as_value::()?)), _ => Err(FdtError::InvalidPropertyValue), @@ -55,7 +55,7 @@ impl<'a, P: ParserWithMode<'a>> Cpus<'a, P> { pub fn common_clock_frequency(&self) -> P::Output> { P::to_output(crate::tryblock!({ match self.node.properties()?.find("clock-frequency")? { - Some(prop) => match prop.value().len() { + Some(prop) => match prop.value.len() { 4 => Ok(Some(u64::from(prop.as_value::()?))), 8 => Ok(Some(prop.as_value::()?)), _ => Err(FdtError::InvalidPropertyValue), @@ -181,7 +181,7 @@ impl<'a, P: ParserWithMode<'a>> Cpu<'a, P> { return Err(FdtError::MissingRequiredProperty("reg")); }; - if reg.value().is_empty() { + if reg.value.is_empty() { return Err(FdtError::InvalidPropertyValue); } @@ -189,7 +189,7 @@ impl<'a, P: ParserWithMode<'a>> Cpu<'a, P> { return Err(FdtError::MissingRequiredProperty("#address-cells")); }; - Ok(CpuIds { reg: reg.value(), address_cells: address_cells.0, _collector: core::marker::PhantomData }) + Ok(CpuIds { reg: reg.value, address_cells: address_cells.0, _collector: core::marker::PhantomData }) })) } @@ -208,7 +208,7 @@ impl<'a, P: ParserWithMode<'a>> Cpu<'a, P> { pub fn clock_frequency(self) -> P::Output { P::to_output(crate::tryblock!({ match self.node.properties()?.find("clock-frequency")? { - Some(prop) => match prop.value().len() { + Some(prop) => match prop.value.len() { 4 => Ok(u64::from(prop.as_value::()?)), 8 => Ok(prop.as_value::()?), _ => Err(FdtError::InvalidPropertyValue), @@ -222,7 +222,7 @@ impl<'a, P: ParserWithMode<'a>> Cpu<'a, P> { .find("clock-frequency")? .ok_or(FdtError::MissingRequiredProperty("clock-frequency"))?; - match prop.value().len() { + match prop.value.len() { 4 => Ok(u64::from(prop.as_value::()?)), 8 => Ok(prop.as_value::()?), _ => Err(FdtError::InvalidPropertyValue), @@ -248,7 +248,7 @@ impl<'a, P: ParserWithMode<'a>> Cpu<'a, P> { pub fn timebase_frequency(self) -> P::Output { P::to_output(crate::tryblock!({ match self.node.properties()?.find("timebase-frequency")? { - Some(prop) => match prop.value().len() { + Some(prop) => match prop.value.len() { 4 => Ok(u64::from(prop.as_value::()?)), 8 => Ok(prop.as_value::()?), _ => Err(FdtError::InvalidPropertyValue), @@ -262,7 +262,7 @@ impl<'a, P: ParserWithMode<'a>> Cpu<'a, P> { .find("timebase-frequency")? .ok_or(FdtError::MissingRequiredProperty("timebase-frequency"))?; - match prop.value().len() { + match prop.value.len() { 4 => Ok(u64::from(prop.as_value::()?)), 8 => Ok(prop.as_value::()?), _ => Err(FdtError::InvalidPropertyValue), diff --git a/src/nodes/memory.rs b/src/nodes/memory.rs index 5514072..4dda35b 100644 --- a/src/nodes/memory.rs +++ b/src/nodes/memory.rs @@ -75,7 +75,7 @@ impl<'a, P: ParserWithMode<'a>> Memory<'a, P> { P::to_output(crate::tryblock!({ match self.node.properties()?.find("initial-mapped-area")? { Some(prop) => { - let value = prop.value(); + let value = prop.value; if value.len() != (/* effective address */8 + /* physical address */ 8 + /* size */ 4) { return Err(FdtError::InvalidPropertyValue); } @@ -207,13 +207,13 @@ impl<'a, P: ParserWithMode<'a>> ReservedMemoryChild<'a, P> { // from the `NodeChildrenIter` struct let size_cells = self.node.parent().unwrap().property::()?.unwrap_or(SizeCells(1)); - if size.value().len() % size_cells.0 != 0 { + if size.value.len() % size_cells.0 != 0 { return Err(FdtError::InvalidPropertyValue); } let mut builder = ::Builder::default(); - for component in size.value().chunks_exact(4) { + for component in size.value.chunks_exact(4) { if builder.push(u32::from_be_bytes(component.try_into().unwrap())).is_err() { return Ok(Some(Err(CollectCellsError))); } @@ -240,13 +240,13 @@ impl<'a, P: ParserWithMode<'a>> ReservedMemoryChild<'a, P> { // from the `NodeChildrenIter` struct let size_cells = self.node.parent().unwrap().property::()?.unwrap_or(SizeCells(1)); - if alignment.value().len() % size_cells.0 != 0 { + if alignment.value.len() % size_cells.0 != 0 { return Err(FdtError::InvalidPropertyValue); } let mut builder = ::Builder::default(); - for component in alignment.value().chunks_exact(4) { + for component in alignment.value.chunks_exact(4) { if builder.push(u32::from_be_bytes(component.try_into().unwrap())).is_err() { return Ok(Some(Err(CollectCellsError))); } diff --git a/src/pretty_print.rs b/src/pretty_print.rs index 9843fc2..f44d8f8 100644 --- a/src/pretty_print.rs +++ b/src/pretty_print.rs @@ -132,7 +132,7 @@ fn print_properties<'a, P: Parser<'a>>( any_props = true; let prop = prop?; - match prop.name() { + match prop.name { "reg" => { write!(f, "{:width$}reg = <", ' ', width = depth * 4 + 4)?; for (i, reg) in node.reg()?.unwrap().iter::>().enumerate() { @@ -155,12 +155,12 @@ fn print_properties<'a, P: Parser<'a>>( writeln!(f, "{:width$}{} = <{:#04x}>;", ' ', name, prop.as_value::()?, width = depth * 4 + 4)?; } _ => match prop.as_value::<&str>() { - Ok("") => writeln!(f, "{:width$}{};", ' ', prop.name(), width = depth * 4 + 4)?, - Ok(value) => writeln!(f, "{:width$}{} = {:?};", ' ', prop.name(), value, width = depth * 4 + 4)?, - _ => match prop.value().len() { - 0 => writeln!(f, "{:width$}{};", ' ', prop.name(), width = depth * 4 + 4)?, + Ok("") => writeln!(f, "{:width$}{};", ' ', prop.name, width = depth * 4 + 4)?, + Ok(value) => writeln!(f, "{:width$}{} = {:?};", ' ', prop.name, value, width = depth * 4 + 4)?, + _ => match prop.value.len() { + 0 => writeln!(f, "{:width$}{};", ' ', prop.name, width = depth * 4 + 4)?, _ => { - write!(f, "{:width$}{} = <", ' ', prop.name(), width = depth * 4 + 4)?; + write!(f, "{:width$}{} = <", ' ', prop.name, width = depth * 4 + 4)?; for (i, n) in prop.as_value::()?.iter().enumerate() { if i != 0 { diff --git a/src/properties.rs b/src/properties.rs index d244d38..55f3091 100644 --- a/src/properties.rs +++ b/src/properties.rs @@ -14,6 +14,9 @@ use crate::{ FdtError, }; +/// A property (or potentially a group of related properties, see +/// [`cells::CellSizes`]) that can be parsed from a [`crate::nodes::Node`] which +/// may also need additional information from the devicetree. pub trait Property<'a, P: ParserWithMode<'a>>: Sized { fn parse(node: FallibleNode<'a, P>, root: FallibleRoot<'a, P>) -> Result, FdtError>; } @@ -60,18 +63,19 @@ impl<'a, P: ParserWithMode<'a>> Property<'a, P> for Compatible<'a> { } impl<'a> Compatible<'a> { - /// First compatible model + /// First compatible model. pub fn first(self) -> &'a str { self.string.split('\0').next().unwrap_or(self.string) } - /// Returns an iterator over all compatible models + /// Returns an iterator over all compatible models. pub fn all(self) -> CompatibleIter<'a> { CompatibleIter { iter: self.string.split('\0') } } - pub fn compatible_with(self, kind: &str) -> bool { - self.all().any(|c| c == kind) + /// Returns whether the node is compatible with the given string value. + pub fn compatible_with(self, value: &str) -> bool { + self.all().any(|c| c == value) } } @@ -84,6 +88,8 @@ impl<'a> IntoIterator for Compatible<'a> { } } +/// An iterator over all of the strings contained within a [`Compatible`] +/// property. pub struct CompatibleIter<'a> { iter: core::str::Split<'a, char>, } @@ -125,6 +131,12 @@ impl<'a> core::ops::Deref for Model<'a> { } } +impl<'a> AsRef for Model<'a> { + fn as_ref(&self) -> &str { + self.0 + } +} + impl<'a> core::cmp::PartialEq for Model<'a> { fn eq(&self, other: &str) -> bool { self.0.eq(other) @@ -173,6 +185,7 @@ impl PHandle { Self(BigEndianU32::from_ne(handle)) } + /// Return the [`PHandle`]'s value as a native-endianness [`u32`]. pub fn as_u32(self) -> u32 { self.0.to_ne() } diff --git a/src/properties/cells.rs b/src/properties/cells.rs index caec55e..50de09b 100644 --- a/src/properties/cells.rs +++ b/src/properties/cells.rs @@ -63,8 +63,8 @@ impl<'a, P: ParserWithMode<'a>> Property<'a, P> for CellSizes { for property in node.properties()? { let property = property?; - let mut parser = UnalignedParser::new(property.value(), StringsBlock(&[]), StructsBlock(&[])); - match property.name() { + let mut parser = UnalignedParser::new(property.value, StringsBlock(&[]), StructsBlock(&[])); + match property.name { "#address-cells" => address_cells = Some(parser.advance_u32()?.to_ne() as usize), "#size-cells" => size_cells = Some(parser.advance_u32()?.to_ne() as usize), _ => {} diff --git a/src/properties/interrupts.rs b/src/properties/interrupts.rs index d3f1de5..0105c9c 100644 --- a/src/properties/interrupts.rs +++ b/src/properties/interrupts.rs @@ -73,14 +73,14 @@ impl<'a, P: ParserWithMode<'a>> Property<'a, P> for LegacyInterrupts<'a, P> { return Err(FdtError::MissingRequiredProperty("interrupt-cells")); }; - if interrupts.value().len() % (interrupt_cells.0 * 4) != 0 { + if interrupts.value.len() % (interrupt_cells.0 * 4) != 0 { return Err(FdtError::InvalidPropertyValue); } Ok(Some(Self { interrupt_parent: InterruptParent(interrupt_parent.0.alt()), interrupt_cells, - encoded_array: interrupts.value(), + encoded_array: interrupts.value, })) } None => Ok(None), @@ -154,7 +154,7 @@ impl<'a, P: ParserWithMode<'a>> Property<'a, P> for ExtendedInterrupts<'a, P> { fn parse(node: FallibleNode<'a, P>, root: FallibleRoot<'a, P>) -> Result, FdtError> { match node.properties()?.find("interrupts-extended")? { Some(interrupts) => { - Ok(Some(Self { encoded_array: interrupts.value(), root: Root { node: root.node.alt() } })) + Ok(Some(Self { encoded_array: interrupts.value, root: Root { node: root.node.alt() } })) } None => Ok(None), @@ -433,13 +433,13 @@ impl<'a, AddrMask: CellCollector, IntMask: CellCollector, P: ParserWithMode<'a>> node.property::()?.ok_or(FdtError::MissingRequiredProperty("#interrupt-cells"))?; match node.properties()?.find("interrupt-map-mask")? { Some(prop) => { - if prop.value().len() % 4 != 0 { + if prop.value.len() % 4 != 0 { return Err(FdtError::InvalidPropertyValue); } let mut address_collector = AddrMask::Builder::default(); let mut specifier_collector = IntMask::Builder::default(); - let mut cells = prop.value().chunks_exact(4); + let mut cells = prop.value.chunks_exact(4); // TODO: replace this stuff with `array_chunks` when its stabilized // @@ -575,7 +575,7 @@ impl< address_cells, interrupt_cells, node: node.alt(), - encoded_map: encoded_map.value(), + encoded_map: encoded_map.value, _collectors: core::marker::PhantomData, })) } diff --git a/src/properties/ranges.rs b/src/properties/ranges.rs index 588ef11..d2bf720 100644 --- a/src/properties/ranges.rs +++ b/src/properties/ranges.rs @@ -46,7 +46,7 @@ impl<'a, P: ParserWithMode<'a>> Property<'a, P> for Ranges<'a> { node.parent().ok_or(FdtError::MissingParent)?.property::()?.unwrap_or_default(); let cell_sizes = node.property::()?.unwrap_or_default(); - Ok(Some(Self { parent_address_cells, cell_sizes, ranges: ranges.value() })) + Ok(Some(Self { parent_address_cells, cell_sizes, ranges: ranges.value })) } } diff --git a/src/properties/reg.rs b/src/properties/reg.rs index 45f6244..82396e0 100644 --- a/src/properties/reg.rs +++ b/src/properties/reg.rs @@ -41,7 +41,7 @@ impl<'a, P: ParserWithMode<'a>> Property<'a, P> for Reg<'a> { None => CellSizes::default(), }; - let encoded_array = prop.value(); + let encoded_array = prop.value; if encoded_array.len() % (cell_sizes.address_cells * 4 + cell_sizes.size_cells * 4) != 0 { return Err(FdtError::InvalidPropertyValue); diff --git a/src/tests.rs b/src/tests.rs index 53d2dca..20194cc 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -115,14 +115,14 @@ fn finds_root_node_properties() { let fdt = Fdt::new(TEST.as_slice()).unwrap(); let prop = fdt.root().find_node("/").unwrap().properties().find("compatible").unwrap(); - assert_eq!(prop.value(), b"riscv-virtio\0"); + assert_eq!(prop.value, b"riscv-virtio\0"); // fallible let fdt = Fdt::new_fallible(TEST.as_slice()).unwrap(); let prop = fdt.root().unwrap().find_node("/").unwrap().unwrap().properties().unwrap().find("compatible").unwrap().unwrap(); - assert_eq!(prop.value(), b"riscv-virtio\0"); + assert_eq!(prop.value, b"riscv-virtio\0"); } #[test] @@ -161,7 +161,7 @@ fn properties() { let fdt = Fdt::new(TEST.as_slice()).unwrap(); let test = fdt.root().find_node("/soc/test").unwrap(); - let props = test.properties().into_iter().map(|p| (p.name(), p.value())).collect::>(); + let props = test.properties().into_iter().map(|p| (p.name, p.value)).collect::>(); assert_eq!( props, @@ -369,7 +369,7 @@ fn cpus() { cpu.as_node() .properties() .iter() - .map(|p| std::format!("{}={:?}", p.name(), p.value())) + .map(|p| std::format!("{}={:?}", p.name, p.value)) .collect::>() ); cpu.reg::().iter().for_each(|n| std::println!("{:?}", n)); @@ -462,7 +462,7 @@ fn cpu_map() { .as_node() .raw_property("riscv,isa") .unwrap() - .value(), + .value, b"rv64imafdcsu\0" ); } From 5f14f9375051150c908813e56c0ddd9e144ec065 Mon Sep 17 00:00:00 2001 From: repnop Date: Sat, 6 Dec 2025 19:00:23 -0500 Subject: [PATCH 34/38] fix: missing lifetimes --- src/nodes/cpus.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/nodes/cpus.rs b/src/nodes/cpus.rs index 56d322e..0948d82 100644 --- a/src/nodes/cpus.rs +++ b/src/nodes/cpus.rs @@ -303,7 +303,7 @@ impl<'a, P: ParserWithMode<'a>> Cpu<'a, P> { /// status is assigned to nodes for which no corresponding CPU exists. #[inline] #[track_caller] - pub fn status(&self) -> P::Output> { + pub fn status(&self) -> P::Output>> { P::to_output(crate::tryblock!({ let Some(status) = self.node.properties()?.find("status")? else { return Ok(None); @@ -334,7 +334,7 @@ impl<'a, P: ParserWithMode<'a>> Cpu<'a, P> { /// Example: `"fsl,MPC8572DS"` #[inline] #[track_caller] - pub fn enable_method(&self) -> P::Output> { + pub fn enable_method(&self) -> P::Output>> { P::to_output(crate::tryblock!({ let Some(status) = self.node.properties()?.find("enable-method")? else { return Ok(None); From 11792fb2314211935a166bad64c57c2a2e9fe568 Mon Sep 17 00:00:00 2001 From: repnop Date: Sat, 6 Dec 2025 21:13:34 -0500 Subject: [PATCH 35/38] fix: enable docs warnings for cleanup --- src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib.rs b/src/lib.rs index e42350a..9593af3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -54,6 +54,7 @@ //! ``` #![no_std] +#![warn(missing_docs)] #[cfg(test)] extern crate std; From ef008d66a81d8ce8bf2d3a2add85c264bab57d3a Mon Sep 17 00:00:00 2001 From: repnop Date: Sun, 7 Dec 2025 21:20:16 -0500 Subject: [PATCH 36/38] fix: buncha documentation and some fixes/adjustments for some types. --- src/cell_collector.rs | 37 ++++- src/helpers.rs | 11 ++ src/nodes.rs | 11 ++ src/properties.rs | 6 + src/properties/cells.rs | 4 + src/properties/interrupts.rs | 264 ++++++++++++++++++++++++++++--- src/properties/interrupts/pci.rs | 3 + src/properties/ranges.rs | 20 ++- src/properties/reg.rs | 21 +++ src/properties/values.rs | 9 ++ 10 files changed, 352 insertions(+), 34 deletions(-) diff --git a/src/cell_collector.rs b/src/cell_collector.rs index 3ee6d29..78886e8 100644 --- a/src/cell_collector.rs +++ b/src/cell_collector.rs @@ -1,5 +1,7 @@ use crate::FdtError; +/// Error type indicating that the cell value that was attempted to be collected +/// was too large for the desired type. #[derive(Debug, Clone, Copy)] pub struct CollectCellsError; @@ -9,20 +11,45 @@ impl From for FdtError { } } +/// A type which performs the underlying collection of cell-sized values into +/// the desired underlying type. pub trait BuildCellCollector: Default { + /// Output of the builder. Usually the same as the type implementing + /// [`CellCollector`]. type Output; + /// Push a new [`u32`] component of a cell-sized value. This can error + /// whenever the value would overflow or otherwise be undesirable. fn push(&mut self, component: u32) -> Result<(), CollectCellsError>; + /// Finish collecting components and return the collected value. fn finish(self) -> Self::Output; } +/// A type which can be "collected" into from devicetree cell-sized values. The +/// most common types to use for this purpose are [`u32`] and [`u64`] but other +/// types may implement this trait when it's useful, such as [PciAddress]. +/// +/// In the case that a cell value may not exist (such as the parent unit address +/// in a PCI `interrupt-map`), [`Option`] implements [`CellCollector`] for +/// any type `C: CellCollector`. +/// +/// For those who want the collection of these values to always succeed, +/// [`core::num::Wrapping`] implements [`CellCollector`] for numeric types +/// which fit the bounds. ([`u32`] and above, as it requires `From`) +/// +/// [PciAddress]: crate::properties::interrupts::pci::PciAddress pub trait CellCollector: Default + Sized { + /// Underlying output type, this is usually the same as `Self`. type Output; + /// Builder type used to collect the individual cell values into the desired type. type Builder: BuildCellCollector; + /// Maps the builder output to the desired underlying type. This is usually + /// a no-op, but may not always be, see the [`core::num::Wrapping`] impl. fn map(builder_out: ::Output) -> Self::Output; } +/// Generic integer type collector. pub struct BuildIntCollector { value: Int, } @@ -79,6 +106,7 @@ impl< } } +/// Wrapping collector, used for [`core::num::Wrapping`]. pub struct BuildWrappingIntCollector { value: Int, } @@ -156,6 +184,7 @@ impl CellCollector for Option { } } +#[allow(missing_docs)] #[derive(Default)] pub struct UsizeCollector { value: usize, @@ -168,12 +197,7 @@ impl BuildCellCollector for UsizeCollector { fn push(&mut self, component: u32) -> Result<(), CollectCellsError> { use core::ops::{BitOr, Shl}; - let shr = const { - match core::mem::size_of::().checked_sub(4) { - Some(value) => value as u32 * 8, - None => panic!("integer type too small"), - } - }; + let shr = const { (core::mem::size_of::() - 4) * 8 }; if self.value >> shr != 0 { return Err(CollectCellsError); @@ -190,6 +214,7 @@ impl BuildCellCollector for UsizeCollector { } } +/// [`BuildCellCollector`] for [`Option`]. pub struct BuildOptionalCellCollector { builder: T::Builder, used: bool, diff --git a/src/helpers.rs b/src/helpers.rs index b54577b..1ae343b 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -3,12 +3,23 @@ use crate::{ parsing::{aligned::AlignedParser, unaligned::UnalignedParser, NoPanic, Panic, ParserWithMode}, }; +/// Parser mode tuple which indicates the parser will not panic and return [`Result`]s instead. pub type FallibleParser<'a, P> = (

>::Parser, NoPanic); +/// A node using a fallible parser. pub type FallibleNode<'a, P> = Node<'a, FallibleParser<'a, P>>; +/// Devicetree root which uses a fallible parser. pub type FallibleRoot<'a, P> = Root<'a, FallibleParser<'a, P>>; +/// Indicates the underlying data is aligned to 4 bytes and the parser will +/// produce [`Result`]s instead of panicking. pub type AlignedFallibleNode<'a> = Node<'a, (AlignedParser<'a>, NoPanic)>; +/// Indicates the underlying data is byte aligned and the parser will +/// produce [`Result`]s instead of panicking. pub type UnalignedFallibleNode<'a> = Node<'a, (UnalignedParser<'a>, NoPanic)>; +/// Indicates the underlying data is aligned to 4 bytes and the parser will +/// panic if invalid devicetree data is encountered. pub type AlignedInfallibleNode<'a> = Node<'a, (AlignedParser<'a>, Panic)>; +/// Indicates the underlying data is byte aligned and the parser will +/// panic if invalid devicetree data is encountered. pub type UnalignedInfallibleNode<'a> = Node<'a, (UnalignedParser<'a>, Panic)>; diff --git a/src/nodes.rs b/src/nodes.rs index 3509d75..00ec4df 100644 --- a/src/nodes.rs +++ b/src/nodes.rs @@ -33,6 +33,7 @@ macro_rules! tryblock { /// Trait for extracting a [`Node`] from a wrapper type. pub trait AsNode<'a, P: ParserWithMode<'a>> { + #[allow(missing_docs)] fn as_node(&self) -> Node<'a, P>; } @@ -57,6 +58,7 @@ pub enum SearchableNodeName<'a> { /// just a base node name with no specified unit address, which will /// resolve to the first node with that base name found. pub trait IntoSearchableNodeName<'a>: Sized + crate::sealed::Sealed { + #[allow(missing_docs)] fn into_searchable_node_name(self) -> SearchableNodeName<'a>; } @@ -96,6 +98,7 @@ pub struct NodeName<'a> { } impl<'a> NodeName<'a> { + /// Create a new [`NodeName`] from its raw parts. pub fn new(name: &'a str, unit_address: Option<&'a str>) -> Self { Self { name, unit_address } } @@ -272,6 +275,8 @@ impl<'a, P: ParserWithMode<'a>> Node<'a, P> { self.property() } + /// Returns [`NodeProperties`] which allows searching and iterating over + /// this node's properties. #[inline] #[track_caller] pub fn properties(&self) -> P::Output> { @@ -297,6 +302,8 @@ impl<'a, P: ParserWithMode<'a>> Node<'a, P> { })) } + /// Attempt to find and extract the specified property represented by + /// `Prop`. #[track_caller] pub fn property>(&self) -> P::Output> { P::to_output(crate::tryblock!({ Prop::parse(self.alt(), self.make_root()?) })) @@ -315,6 +322,8 @@ impl<'a, P: ParserWithMode<'a>> Node<'a, P> { P::to_output(crate::tryblock!({ self.fallible().children()?.find(name).map(|o| o.map(|n| n.alt())) })) } + /// Returns [`NodeChildren`] which allows searching and iterating over this + /// node's children. #[inline] #[track_caller] pub fn children(&self) -> P::Output> { @@ -341,6 +350,7 @@ impl<'a, P: ParserWithMode<'a>> Node<'a, P> { })) } + /// Attempt to retrieve the parent for this node. Note that this #[inline] pub fn parent(&self) -> Option { self.parent.map(|parent| Self { @@ -376,6 +386,7 @@ impl<'a, P: ParserWithMode<'a>> Clone for Node<'a, P> { impl<'a, P: ParserWithMode<'a>> Copy for Node<'a, P> {} +/// Newtype around a slice of raw node data. #[repr(transparent)] pub struct RawNode([Granularity]); diff --git a/src/properties.rs b/src/properties.rs index 55f3091..e630bf3 100644 --- a/src/properties.rs +++ b/src/properties.rs @@ -18,6 +18,8 @@ use crate::{ /// [`cells::CellSizes`]) that can be parsed from a [`crate::nodes::Node`] which /// may also need additional information from the devicetree. pub trait Property<'a, P: ParserWithMode<'a>>: Sized { + /// Attempt to parse out the property from the given node and a reference to + /// the root node of the devicetree. fn parse(node: FallibleNode<'a, P>, root: FallibleRoot<'a, P>) -> Result, FdtError>; } @@ -219,9 +221,13 @@ impl<'a, P: ParserWithMode<'a>> Property<'a, P> for PHandle { pub struct Status<'a>(&'a str); impl<'a> Status<'a> { + #[allow(missing_docs)] pub const OKAY: Self = Self("okay"); + #[allow(missing_docs)] pub const DISABLED: Self = Self("disabled"); + #[allow(missing_docs)] pub const RESERVED: Self = Self("reserved"); + #[allow(missing_docs)] pub const FAIL: Self = Self("fail"); /// Returns true if the status is `"okay"` diff --git a/src/properties/cells.rs b/src/properties/cells.rs index 50de09b..cb009bc 100644 --- a/src/properties/cells.rs +++ b/src/properties/cells.rs @@ -51,7 +51,9 @@ use crate::{ /// (`0x4600`), and the size is represented by a single cell (`0x100`). #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct CellSizes { + #[allow(missing_docs)] pub address_cells: usize, + #[allow(missing_docs)] pub size_cells: usize, } @@ -81,6 +83,7 @@ impl Default for CellSizes { } } +/// Newtype representing the number of [`u32`]s that make up an address value. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct AddressCells(pub usize); @@ -100,6 +103,7 @@ impl Default for AddressCells { } } +/// Newtype representing the number of [`u32`]s that make up a size value. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct SizeCells(pub usize); diff --git a/src/properties/interrupts.rs b/src/properties/interrupts.rs index 0105c9c..80b65d7 100644 --- a/src/properties/interrupts.rs +++ b/src/properties/interrupts.rs @@ -1,3 +1,4 @@ +/// Types and helpers for the devicetree PCI bindings. pub mod pci; use super::{cells::AddressCells, PHandle, Property}; @@ -13,7 +14,9 @@ use crate::{ /// devicetree node. See the documentation for each type for more information. /// [`ExtendedInterrupts`] will take precedence if both properties exist. pub enum Interrupts<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { + /// The `interrupts` property. Legacy(LegacyInterrupts<'a, P>), + /// The `interrupts-extended` property. Extended(ExtendedInterrupts<'a, P>), } @@ -47,10 +50,12 @@ pub struct LegacyInterrupts<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Pani } impl<'a, P: ParserWithMode<'a>> LegacyInterrupts<'a, P> { + #[allow(missing_docs)] pub fn interrupt_parent(self) -> InterruptParent<'a, P> { self.interrupt_parent } + #[allow(missing_docs)] pub fn iter(self) -> LegacyInterruptsIter<'a, I> { LegacyInterruptsIter { interrupt_cells: self.interrupt_cells, @@ -73,7 +78,7 @@ impl<'a, P: ParserWithMode<'a>> Property<'a, P> for LegacyInterrupts<'a, P> { return Err(FdtError::MissingRequiredProperty("interrupt-cells")); }; - if interrupts.value.len() % (interrupt_cells.0 * 4) != 0 { + if interrupts.value.len() % (interrupt_cells.as_byte_count()) != 0 { return Err(FdtError::InvalidPropertyValue); } @@ -95,6 +100,7 @@ impl<'a, P: ParserWithMode<'a>> Clone for LegacyInterrupts<'a, P> { } } +#[allow(missing_docs)] pub struct LegacyInterruptsIter<'a, I: CellCollector> { interrupt_cells: InterruptCells, encoded_array: &'a [u8], @@ -104,7 +110,7 @@ pub struct LegacyInterruptsIter<'a, I: CellCollector> { impl<'a, I: CellCollector> Iterator for LegacyInterruptsIter<'a, I> { type Item = Result; fn next(&mut self) -> Option { - let encoded_specifier = self.encoded_array.get(..self.interrupt_cells.0 * 4)?; + let encoded_specifier = self.encoded_array.get(..self.interrupt_cells.as_byte_count())?; let mut specifier_collector = ::Builder::default(); for encoded_specifier in encoded_specifier.chunks_exact(4) { @@ -117,7 +123,7 @@ impl<'a, I: CellCollector> Iterator for LegacyInterruptsIter<'a, I> { } } - self.encoded_array = self.encoded_array.get(self.interrupt_cells.0 * 4..)?; + self.encoded_array = self.encoded_array.get(self.interrupt_cells.as_byte_count()..)?; Some(Ok(I::map(specifier_collector.finish()))) } } @@ -145,6 +151,7 @@ pub struct ExtendedInterrupts<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Pa } impl<'a, P: ParserWithMode<'a>> ExtendedInterrupts<'a, P> { + #[allow(missing_docs)] pub fn iter(self) -> ExtendedInterruptsIter<'a, P> { ExtendedInterruptsIter { root: self.root, encoded_array: self.encoded_array } } @@ -162,6 +169,7 @@ impl<'a, P: ParserWithMode<'a>> Property<'a, P> for ExtendedInterrupts<'a, P> { } } +#[allow(missing_docs)] pub struct ExtendedInterruptsIter<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { root: Root<'a, P>, encoded_array: &'a [u8], @@ -188,7 +196,7 @@ impl<'a, P: ParserWithMode<'a>> Iterator for ExtendedInterruptsIter<'a, P> { return Err(FdtError::MissingRequiredProperty("#interrupt-cells")); }; - let cells_length = interrupt_cells.0 * 4; + let cells_length = interrupt_cells.as_byte_count(); let encoded_array = match self.encoded_array.get(..cells_length) { Some(bytes) => bytes, None => return Ok(None), @@ -216,13 +224,14 @@ impl<'a, P: ParserWithMode<'a>> Iterator for ExtendedInterruptsIter<'a, P> { } } -/// A single entry in an `interrupts-extended` property +/// A single entry in an `interrupts-extended` property. pub struct ExtendedInterrupt<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { interrupt_parent: InterruptParent<'a, P>, interrupt_cells: InterruptCells, encoded_array: &'a [u8], } +#[allow(missing_docs)] impl<'a, P: ParserWithMode<'a>> ExtendedInterrupt<'a, P> { pub fn interrupt_parent(self) -> InterruptParent<'a, P> { self.interrupt_parent @@ -237,13 +246,26 @@ impl<'a, P: ParserWithMode<'a>> ExtendedInterrupt<'a, P> { } } +/// An individual interrupt specifier from an [`ExtendedInterrupt`] value. pub struct InterruptSpecifier<'a> { interrupt_cells: InterruptCells, encoded_array: &'a [u8], } impl<'a> InterruptSpecifier<'a> { - /// Iterator over the components that comprise this interrupt specifier. + /// Attempt to collect the specifier bytes into a specific type. + pub fn collect_to(self) -> Result<::Output, CollectCellsError> { + let mut collector = ::Builder::default(); + for chunk in self.encoded_array.chunks_exact(4) { + // UNWRAP: this unwrap cannot panic because `chunk` is guaranteed to + // be 4 bytes. + collector.push(u32::from_be_bytes(chunk.try_into().unwrap()))?; + } + + Ok(C::map(collector.finish())) + } + + /// Iterator over the raw [`u32`] components that comprise this interrupt specifier. pub fn iter(self) -> InterruptSpecifierIter<'a> { InterruptSpecifierIter { encoded_array: self.encoded_array } } @@ -385,6 +407,13 @@ impl<'a, P: ParserWithMode<'a>> Property<'a, P> for InterruptParent<'a, P> { #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct InterruptCells(pub usize); +impl InterruptCells { + /// The number of interrupt cells times `size_of::()`. + pub fn as_byte_count(self) -> usize { + self.0 * core::mem::size_of::() + } +} + impl<'a, P: ParserWithMode<'a>> Property<'a, P> for InterruptCells { fn parse(node: FallibleNode<'a, P>, _: FallibleRoot<'a, P>) -> Result, FdtError> { match node.properties()?.find("#interrupt-cells")? { @@ -408,18 +437,20 @@ pub struct InterruptMapMask { } impl InterruptMapMask { + /// Mask the provided unit address and interrupt specifier with the value of + /// the mask. pub fn mask( - self, + &self, address: ::Output, interrupt_specifier: ::Output, ) -> (::Output, ::Output) where - ::Output: - core::ops::BitAnd<::Output, Output = ::Output>, + ::Output: Clone + + core::ops::BitAnd<::Output, Output = ::Output>, ::Output: - core::ops::BitAnd<::Output, Output = ::Output>, + Clone + core::ops::BitAnd<::Output, Output = ::Output>, { - (self.address_mask & address, self.interrupt_specifier_mask & interrupt_specifier) + (self.address_mask.clone() & address, self.interrupt_specifier_mask.clone() & interrupt_specifier) } } @@ -480,19 +511,53 @@ impl<'a, P: ParserWithMode<'a>> Property<'a, P> for InterruptController { } } +/// [Devicetree 2.4.3.1. +/// `interrupt-map`](https://devicetree-specification.readthedocs.io/en/latest/chapter2-devicetree-basics.html#interrupt-map) +/// +/// The `interrupt-map` property allows for mapping interrupt specifiers between +/// one interrupt domain to a parent domain. The description of each generic +/// parameter is as follows: +/// +/// `ChildUnitAddres`: +/// > The unit address of the child node being mapped. The number of 32-bit +/// > cells required to specify this is described by the `#address-cells` +/// > property of the bus node on which the child is located. +/// +/// `ChildInterruptSpecifier`: +/// > The interrupt specifier of the child node being mapped. The number of +/// > 32-bit cells required to specify this component is described by the +/// > `#interrupt-cells` property of this node—the nexus node containing the +/// > `interrupt-map` property. +/// +/// `ParentUnitAddress`: +/// > The unit address in the domain of the interrupt parent. The number of +/// > 32-bit cells required to specify this address is described by the +/// > `#address-cells` property of the node pointed to by the +/// > `interrupt-parent` field. +/// +/// `ParentInterruptSpecifier`: +/// > The interrupt specifier in the parent domain. The number of 32-bit +/// > cells required to specify this component is described by the +/// > `#interrupt-cells` property of the node pointed to by the +/// > `interrupt-parent` field. pub struct InterruptMap< 'a, - CAddr: CellCollector, - CInt: CellCollector = u32, - PAddr: CellCollector = u64, - PInt: CellCollector = u32, + ChildUnitAddress: CellCollector, + ChildInterruptSpecifier: CellCollector = u32, + ParentUnitAddress: CellCollector = u64, + ParentInterruptSpecifier: CellCollector = u32, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic), > { address_cells: AddressCells, interrupt_cells: InterruptCells, node: FallibleNode<'a, P>, encoded_map: &'a [u8], - _collectors: core::marker::PhantomData<*mut (CAddr, CInt, PAddr, PInt)>, + _collectors: core::marker::PhantomData<*mut ( + ChildUnitAddress, + ChildInterruptSpecifier, + ParentUnitAddress, + ParentInterruptSpecifier, + )>, } impl< @@ -504,6 +569,7 @@ impl< PInt: CellCollector, > InterruptMap<'a, CAddr, CInt, PAddr, PInt, P> { + /// Create an iterator over each individual [`InterruptMapEntry`]. pub fn iter(self) -> InterruptMapIter<'a, CAddr, CInt, PAddr, PInt, P> { InterruptMapIter { address_cells: self.address_cells, @@ -514,11 +580,34 @@ impl< } } + /// Create an iterator over each individual [`InterruptMapEntry`] which uses the provided mask to modify the . + pub fn iter_masked( + self, + mask: InterruptMapMask, + ) -> MaskedInterruptMapIter<'a, CAddr, CInt, PAddr, PInt, P> + where + ::Output: + Clone + core::ops::BitAnd<::Output, Output = ::Output>, + ::Output: + Clone + core::ops::BitAnd<::Output, Output = ::Output>, + { + MaskedInterruptMapIter { + address_cells: self.address_cells, + interrupt_cells: self.interrupt_cells, + node: self.node, + encoded_map: self.encoded_map, + mask, + _collectors: core::marker::PhantomData, + } + } + + /// Attempt to find a specific child unit address with a specific child + /// interrupt specifier. #[allow(clippy::type_complexity)] pub fn find( self, - address: CAddr::Output, - interrupt_specifier: CInt::Output, + client_unit_address: CAddr::Output, + child_interrupt_specifier: CInt::Output, ) -> P::Output>> where CAddr::Output: PartialEq, @@ -537,7 +626,8 @@ impl< .find(|e| match e { Err(_) => true, Ok(entry) => { - entry.child_unit_address == address && entry.child_interrupt_specifier == interrupt_specifier + entry.child_unit_address == client_unit_address + && entry.child_interrupt_specifier == child_interrupt_specifier } }) .transpose() @@ -581,6 +671,7 @@ impl< } } +#[allow(missing_docs)] pub struct InterruptMapIter< 'a, CAddr: CellCollector, @@ -611,7 +702,7 @@ impl< fn next(&mut self) -> Option { let res = crate::tryblock!({ let child_addr_size = self.address_cells.0 * 4; - let child_intsp_size = self.interrupt_cells.0 * 4; + let child_intsp_size = self.interrupt_cells.as_byte_count(); let Some((child_address_iter, rest)) = self.encoded_map.split_at_checked(child_addr_size) else { return Ok(None); @@ -637,7 +728,7 @@ impl< .ok_or(FdtError::MissingRequiredProperty("#interrupt-cells"))?; let parent_addr_size = parent_address_cells.0 * 4; - let parent_intsp_size = parent_interrupt_cells.0 * 4; + let parent_intsp_size = parent_interrupt_cells.as_byte_count(); let Some((parent_address_iter, rest)) = rest.split_at_checked(parent_addr_size) else { return Ok(None); @@ -685,6 +776,132 @@ impl< } } +#[allow(missing_docs)] +pub struct MaskedInterruptMapIter< + 'a, + CAddr: CellCollector, + CInt: CellCollector, + PAddr: CellCollector, + PInt: CellCollector, + P: ParserWithMode<'a> = (AlignedParser<'a>, Panic), +> where + ::Output: + Clone + core::ops::BitAnd<::Output, Output = ::Output>, + ::Output: + Clone + core::ops::BitAnd<::Output, Output = ::Output>, +{ + address_cells: AddressCells, + interrupt_cells: InterruptCells, + node: FallibleNode<'a, P>, + encoded_map: &'a [u8], + mask: InterruptMapMask, + _collectors: core::marker::PhantomData<*mut (CAddr, CInt, PAddr, PInt)>, +} + +impl< + 'a, + CAddr: CellCollector, + CInt: CellCollector, + PAddr: CellCollector, + PInt: CellCollector, + P: ParserWithMode<'a>, + > Iterator for MaskedInterruptMapIter<'a, CAddr, CInt, PAddr, PInt, P> +where + ::Output: + Clone + core::ops::BitAnd<::Output, Output = ::Output>, + ::Output: + Clone + core::ops::BitAnd<::Output, Output = ::Output>, +{ + type Item = P::Output>; + + #[track_caller] + fn next(&mut self) -> Option { + let res = crate::tryblock!({ + let child_addr_size = self.address_cells.0 * 4; + let child_intsp_size = self.interrupt_cells.as_byte_count(); + + let Some((child_address_iter, rest)) = self.encoded_map.split_at_checked(child_addr_size) else { + return Ok(None); + }; + let Some((child_specifier_iter, rest)) = rest.split_at_checked(child_intsp_size) else { + return Ok(None); + }; + let Some((interrupt_parent, rest)) = rest.split_at_checked(4) else { + return Ok(None); + }; + + let root = self.node.make_root::<(P::Parser, NoPanic)>()?; + let phandle = u32::from_ne_bytes(interrupt_parent.try_into().unwrap()); + let interrupt_parent = root + .resolve_phandle(PHandle(BigEndianU32::from_be(phandle)))? + .ok_or(FdtError::MissingPHandleNode(phandle.swap_bytes()))?; + + let parent_address_cells = interrupt_parent + .property::()? + .ok_or(FdtError::MissingRequiredProperty("#address-cells"))?; + let parent_interrupt_cells = interrupt_parent + .property::()? + .ok_or(FdtError::MissingRequiredProperty("#interrupt-cells"))?; + + let parent_addr_size = parent_address_cells.0 * 4; + let parent_intsp_size = parent_interrupt_cells.as_byte_count(); + + let Some((parent_address_iter, rest)) = rest.split_at_checked(parent_addr_size) else { + return Ok(None); + }; + + let Some((parent_specifier_iter, rest)) = rest.split_at_checked(parent_intsp_size) else { + return Ok(None); + }; + self.encoded_map = rest; + + let mut child_address_collector = CAddr::Builder::default(); + for chunk in child_address_iter.chunks_exact(4) { + child_address_collector.push(u32::from_be_bytes(chunk.try_into().unwrap()))?; + } + + let mut child_specifier_collector = CInt::Builder::default(); + for chunk in child_specifier_iter.chunks_exact(4) { + child_specifier_collector.push(u32::from_be_bytes(chunk.try_into().unwrap()))?; + } + + let mut parent_address_collector = PAddr::Builder::default(); + for chunk in parent_address_iter.chunks_exact(4) { + parent_address_collector.push(u32::from_be_bytes(chunk.try_into().unwrap()))?; + } + + let mut parent_specifier_collector = PInt::Builder::default(); + for chunk in parent_specifier_iter.chunks_exact(4) { + parent_specifier_collector.push(u32::from_be_bytes(chunk.try_into().unwrap()))?; + } + + let child_unit_address = CAddr::map(child_address_collector.finish()); + let child_interrupt_specifier = CInt::map(child_specifier_collector.finish()); + let (child_unit_address, child_interrupt_specifier) = + self.mask.mask(child_unit_address, child_interrupt_specifier); + + Ok(Some(InterruptMapEntry { + interrupt_parent: interrupt_parent.alt(), + child_unit_address, + child_interrupt_specifier, + parent_unit_address: PAddr::map(parent_address_collector.finish()), + parent_interrupt_specifier: PInt::map(parent_specifier_collector.finish()), + })) + }); + + #[allow(clippy::manual_map)] + match res.transpose() { + Some(output) => Some(P::to_output(output)), + None => None, + } + } +} + +/// [Devicetree 2.4.3.1. +/// `interrupt-map`](https://devicetree-specification.readthedocs.io/en/latest/chapter2-devicetree-basics.html#interrupt-map) +/// +/// An individual `interrupt-map` entry, including the resolved interrupt parent +/// [`PHandle`]. pub struct InterruptMapEntry< 'a, CAddr: CellCollector, @@ -693,10 +910,15 @@ pub struct InterruptMapEntry< PInt: CellCollector, P: ParserWithMode<'a>, > { + /// Interrupt parent of this entry. pub interrupt_parent: Node<'a, P>, + /// See [`InterruptMap`] for more information. pub child_unit_address: CAddr::Output, + /// See [`InterruptMap`] for more information. pub child_interrupt_specifier: CInt::Output, + /// See [`InterruptMap`] for more information. pub parent_unit_address: PAddr::Output, + /// See [`InterruptMap`] for more information. pub parent_interrupt_specifier: PInt::Output, } diff --git a/src/properties/interrupts/pci.rs b/src/properties/interrupts/pci.rs index f9b4fe1..048e89a 100644 --- a/src/properties/interrupts/pci.rs +++ b/src/properties/interrupts/pci.rs @@ -5,8 +5,11 @@ use crate::cell_collector::{BuildCellCollector, CellCollector, CollectCellsError /// Numerical representation of a PCI address used within the `interrupt-map` property #[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct PciAddress { + #[allow(missing_docs)] pub hi: PciAddressHighBits, + #[allow(missing_docs)] pub mid: u32, + #[allow(missing_docs)] pub lo: u32, } diff --git a/src/properties/ranges.rs b/src/properties/ranges.rs index d2bf720..ae825e9 100644 --- a/src/properties/ranges.rs +++ b/src/properties/ranges.rs @@ -1,18 +1,17 @@ -use super::{ - cells::{AddressCells, CellSizes}, - Property, -}; use crate::{ cell_collector::{BuildCellCollector, CellCollector, CollectCellsError}, helpers::{FallibleNode, FallibleRoot}, parsing::ParserWithMode, + properties::{ + cells::{AddressCells, CellSizes}, + Property, + }, FdtError, }; -#[cfg(doc)] -use crate::nodes::Node; - /// See [`Node::ranges`]. +/// +/// [`Node::ranges`]: crate::nodes::Node::ranges #[derive(Debug, Clone, Copy)] pub struct Ranges<'a> { parent_address_cells: AddressCells, @@ -21,6 +20,8 @@ pub struct Ranges<'a> { } impl<'a> Ranges<'a> { + /// Create an iterator over the entries in the property value and attempt to + /// collect the constituent parts into the specified [`CellCollector`]s. pub fn iter(self) -> RangesIter<'a, CAddr, PAddr, Len> where CAddr: CellCollector, @@ -50,6 +51,7 @@ impl<'a, P: ParserWithMode<'a>> Property<'a, P> for Ranges<'a> { } } +#[allow(missing_docs)] pub struct RangesIter<'a, CAddr: CellCollector = u64, PAddr: CellCollector = u64, Len: CellCollector = u64> { parent_address_cells: AddressCells, cell_sizes: CellSizes, @@ -115,9 +117,13 @@ impl<'a, CAddr: CellCollector, PAddr: CellCollector, Len: CellCollector> Iterato } } +/// A single range entry contained by a [`Ranges`] property. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Range { + #[allow(missing_docs)] pub child_bus_address: CAddr, + #[allow(missing_docs)] pub parent_bus_address: PAddr, + #[allow(missing_docs)] pub len: Len, } diff --git a/src/properties/reg.rs b/src/properties/reg.rs index 82396e0..bf1edfd 100644 --- a/src/properties/reg.rs +++ b/src/properties/reg.rs @@ -6,6 +6,7 @@ use crate::{ FdtError, }; +/// A `reg` property value. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Reg<'a> { cell_sizes: CellSizes, @@ -13,14 +14,18 @@ pub struct Reg<'a> { } impl<'a> Reg<'a> { + /// Standard cell sizes for this [`Reg`]. pub fn cell_sizes(self) -> CellSizes { self.cell_sizes } + /// Returns an iterator over the raw property data for custom behavior. pub fn iter_raw(self) -> RegRawIter<'a> { RegRawIter { cell_sizes: self.cell_sizes, encoded_array: self.encoded_array } } + /// Iterate over the entries represented by this [`Reg`] and attempt to + /// collect them into the specified address and length types. pub fn iter(self) -> RegIter<'a, Addr, Len> { RegIter { cell_sizes: self.cell_sizes, @@ -51,12 +56,16 @@ impl<'a, P: ParserWithMode<'a>> Property<'a, P> for Reg<'a> { } } +/// An individual entry in a [`Reg`] property. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct RegEntry { + /// Starting address. pub address: Addr, + /// Length of the region. pub len: Len, } +#[allow(missing_docs)] pub struct RegIter<'a, CAddr: CellCollector, Len: CellCollector> { cell_sizes: CellSizes, encoded_array: &'a [u8], @@ -99,6 +108,7 @@ impl<'a, CAddr: CellCollector, Len: CellCollector> Iterator for RegIter<'a, CAdd } } +#[allow(missing_docs)] pub struct RegRawIter<'a> { cell_sizes: CellSizes, encoded_array: &'a [u8], @@ -118,9 +128,13 @@ impl<'a> Iterator for RegRawIter<'a> { } } +/// [`RegEntry`] except the address and length are represented by their raw +/// underlying bytes. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct RawRegEntry<'a> { + #[allow(missing_docs)] pub address: &'a [u8], + #[allow(missing_docs)] pub len: &'a [u8], } @@ -135,11 +149,18 @@ pub struct RawRegEntry<'a> { pub struct VirtualReg(u32); impl VirtualReg { + #[allow(missing_docs)] pub fn into_u32(self) -> u32 { self.0 } } +impl From for u32 { + fn from(value: VirtualReg) -> Self { + value.0 + } +} + impl<'a, P: ParserWithMode<'a>> Property<'a, P> for VirtualReg { fn parse(node: FallibleNode<'a, P>, _: FallibleRoot<'a, P>) -> Result, FdtError> { match node.properties()?.find("virtual-reg")? { diff --git a/src/properties/values.rs b/src/properties/values.rs index 447ccc5..47bb339 100644 --- a/src/properties/values.rs +++ b/src/properties/values.rs @@ -1,6 +1,7 @@ use crate::{parsing::BigEndianU32, FdtError}; use core::ffi::CStr; +/// Error type indicating an invalid property value was encountered. #[derive(Debug, Clone, Copy)] pub struct InvalidPropertyValue; @@ -10,7 +11,10 @@ impl From for FdtError { } } +/// Helper trait for parsing property values. pub trait PropertyValue<'a>: Sized { + /// Attempt to parse the raw property value bytes that represent the type + /// implementing this trait. fn parse(value: &'a [u8]) -> Result; } @@ -79,10 +83,13 @@ impl<'a> PropertyValue<'a> for &'a str { } } +/// Property value represented by a list of [`u32`] values. #[derive(Debug, Clone, Copy)] pub struct U32List<'a>(&'a [u8]); impl<'a> U32List<'a> { + /// Returns an iterator over the individual [`u32`] components of the + /// property value. pub fn iter(self) -> U32ListIter<'a> { U32ListIter(self.0) } @@ -98,6 +105,7 @@ impl<'a> PropertyValue<'a> for U32List<'a> { } } +#[allow(missing_docs)] pub struct U32ListIter<'a>(&'a [u8]); impl<'a> Iterator for U32ListIter<'a> { @@ -109,6 +117,7 @@ impl<'a> Iterator for U32ListIter<'a> { } } +/// Property value represented by a list of null-terminated string values. #[derive(Debug, Clone)] pub struct StringList<'a> { strs: core::str::Split<'a, char>, From 11dea5ed395ecddc50fe810818f8074e91c925f8 Mon Sep 17 00:00:00 2001 From: repnop Date: Sat, 10 Jan 2026 22:17:24 -0500 Subject: [PATCH 37/38] chore: docs done --- src/lib.rs | 17 +++++++++++++++++ src/nodes.rs | 6 ++++++ src/nodes/aliases.rs | 1 + src/nodes/chosen.rs | 6 ++++++ src/nodes/cpus.rs | 10 ++++++++++ src/nodes/memory.rs | 9 ++++++++- src/nodes/root.rs | 2 ++ src/properties.rs | 5 +++++ src/properties/interrupts/pci.rs | 4 ++++ 9 files changed, 59 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 9593af3..048ffb9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -62,11 +62,15 @@ extern crate std; #[cfg(test)] mod tests; +/// Trait and types for working with `*-cells` values. pub mod cell_collector; +/// Helper type aliases. pub mod helpers; +/// Devicetree node abstractions. pub mod nodes; mod parsing; mod pretty_print; +/// Devicetree property abstractions. pub mod properties; mod util; @@ -96,12 +100,21 @@ pub enum FdtError { SliceTooSmall, /// An error was encountered during parsing ParseError(ParseError), + /// Attempted to resolve the `phandle` value for a node, but was unable to + /// locate it. MissingPHandleNode(u32), + /// A parent node is required. MissingParent, + /// A required node with the given name wasn't found. MissingRequiredNode(&'static str), + /// A required property with the given name wasn't found. MissingRequiredProperty(&'static str), + /// Property name contained invalid characters. InvalidPropertyValue, + /// Node name contained invalid characters. InvalidNodeName, + /// A `-cells` property value was unable to be collected into the specified + /// type. CollectCellsError, } @@ -169,6 +182,7 @@ impl<'a, P: ParserWithMode<'a>> core::fmt::Display for Fdt<'a, P> { } } +/// FDT header. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct FdtHeader { @@ -414,14 +428,17 @@ impl<'a, P: ParserWithMode<'a>> Fdt<'a, P> { self.header.total_size as usize } + /// Header describing this devicetree. pub fn header(&self) -> &FdtHeader { &self.header } + /// Slice pointing to the raw strings block. pub fn strings_block(&self) -> &'a [u8] { self.strings.0 } + /// Slice pointing to the raw structs block. pub fn structs_block(&self) -> &'a [P::Granularity] { self.structs.0 } diff --git a/src/nodes.rs b/src/nodes.rs index 00ec4df..39c4476 100644 --- a/src/nodes.rs +++ b/src/nodes.rs @@ -1,7 +1,12 @@ +/// `/aliases` node. pub mod aliases; +/// Parameters chosen or specified by the system firmware at run time. pub mod chosen; +/// Description of the CPUs available on the system. pub mod cpus; +/// Memory region nodes and properties. pub mod memory; +/// Root devicetree node type and helpers. pub mod root; use crate::{ @@ -526,6 +531,7 @@ pub struct NodeProperty<'a> { } impl<'a> NodeProperty<'a> { + /// Create a new [`NodeProperty`] from its name and raw value. #[inline(always)] pub fn new(name: &'a str, value: &'a [u8]) -> Self { Self { name, value } diff --git a/src/nodes/aliases.rs b/src/nodes/aliases.rs index 26d7e66..ee98507 100644 --- a/src/nodes/aliases.rs +++ b/src/nodes/aliases.rs @@ -72,6 +72,7 @@ impl<'a, P: ParserWithMode<'a>> AsNode<'a, P> for Aliases<'a, P> { } } +#[allow(missing_docs)] pub struct AllAliasesIter<'a, P: ParserWithMode<'a>> { properties: NodePropertiesIter<'a, FallibleParser<'a, P>>, } diff --git a/src/nodes/chosen.rs b/src/nodes/chosen.rs index 69eb3b0..ab92845 100644 --- a/src/nodes/chosen.rs +++ b/src/nodes/chosen.rs @@ -175,7 +175,9 @@ impl<'a, P: ParserWithMode<'a>> Copy for Chosen<'a, P> {} /// See [`Chosen::stdin`]. pub struct Stdin<'a, P: ParserWithMode<'a>> { + /// Node representing an stdin device. pub node: Node<'a, P>, + /// Optional parameters following the node path. pub params: Option<&'a str>, } @@ -193,7 +195,9 @@ impl<'a, P: ParserWithMode<'a>> core::fmt::Debug for Stdin<'a, P> { /// See [`Chosen::stdout`]. pub struct Stdout<'a, P: ParserWithMode<'a>> { + /// Node representing an stdout device. pub node: Node<'a, P>, + /// Optional parameters following the node path. pub params: Option<&'a str>, } @@ -209,6 +213,8 @@ impl<'a, P: ParserWithMode<'a>> core::fmt::Debug for Stdout<'a, P> { } } +/// Like [`Stdout`] and [`Stdin`] but does not contain the resolved node, only +/// its path and the optional parameters that may follow. pub struct StdInOutPath<'a> { path: &'a str, params: Option<&'a str>, diff --git a/src/nodes/cpus.rs b/src/nodes/cpus.rs index 0948d82..c102235 100644 --- a/src/nodes/cpus.rs +++ b/src/nodes/cpus.rs @@ -76,6 +76,8 @@ impl<'a, P: ParserWithMode<'a>> Cpus<'a, P> { })) } + /// Create an iterator over the children of this node, primarily composed of + /// [`Cpu`] nodes. pub fn iter(&self) -> P::Output> { P::to_output(crate::tryblock!({ Ok(CpusIter { children: self.node.children()?.iter().filter(filter_cpus::

) }) @@ -99,6 +101,7 @@ fn filter_cpus<'a, P: ParserWithMode<'a>>(node: &Result, Fdt } } +#[allow(missing_docs)] pub struct CpusIter<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { children: core::iter::Filter< NodeChildrenIter<'a, (P::Parser, NoPanic)>, @@ -481,6 +484,7 @@ impl<'a, P: ParserWithMode<'a>> AsNode<'a, P> for Cpu<'a, P> { } } +/// CPU status value. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(transparent)] pub struct CpuStatus<'a>(&'a str); @@ -596,6 +600,7 @@ impl<'a, C: CellCollector> CpuIds<'a, C> { self.iter().next().unwrap() } + /// Create an iterator over the CPU IDs described by a [`Cpu`] node. pub fn iter(&self) -> CpuIdsIter<'a, C> { CpuIdsIter { reg: self.reg, address_cells: self.address_cells, _collector: core::marker::PhantomData } } @@ -617,6 +622,7 @@ impl<'a, C: CellCollector> core::fmt::Debug for CpuIds<'a, C> { } } +#[allow(missing_docs)] pub struct CpuIdsIter<'a, C: CellCollector> { reg: &'a [u8], address_cells: usize, @@ -723,6 +729,7 @@ fn filter_sockets<'a, P: ParserWithMode<'a>>(node: &Result, } } +#[allow(missing_docs)] pub struct CpuSocketIter<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { children: core::iter::Filter< NodeChildrenIter<'a, (P::Parser, NoPanic)>, @@ -751,6 +758,7 @@ fn filter_clusters<'a, P: ParserWithMode<'a>>(node: &Result, } } +#[allow(missing_docs)] pub struct CpuClusterIter<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { children: core::iter::Filter< NodeChildrenIter<'a, (P::Parser, NoPanic)>, @@ -830,6 +838,7 @@ fn filter_cores<'a, P: ParserWithMode<'a>>(node: &Result, Fd } } +#[allow(missing_docs)] pub struct CpuCoreIter<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { children: core::iter::Filter< NodeChildrenIter<'a, (P::Parser, NoPanic)>, @@ -909,6 +918,7 @@ fn filter_threads<'a, P: ParserWithMode<'a>>(node: &Result, } } +#[allow(missing_docs)] pub struct CpuThreadIter<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { children: core::iter::Filter< NodeChildrenIter<'a, (P::Parser, NoPanic)>, diff --git a/src/nodes/memory.rs b/src/nodes/memory.rs index 4dda35b..c36ced7 100644 --- a/src/nodes/memory.rs +++ b/src/nodes/memory.rs @@ -133,6 +133,7 @@ pub struct ReservedMemory<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic) impl<'a, P: ParserWithMode<'a>> ReservedMemory<'a, P> { #[inline] #[track_caller] + #[allow(missing_docs)] pub fn cell_sizes(&self) -> P::Output { P::to_output( self.node @@ -143,6 +144,7 @@ impl<'a, P: ParserWithMode<'a>> ReservedMemory<'a, P> { #[inline] #[track_caller] + #[allow(missing_docs)] pub fn children(&self) -> P::Output> { P::to_output(crate::tryblock!({ Ok(ReservedMemoryChildrenIter { children: self.node.children()?.iter() }) })) } @@ -154,6 +156,7 @@ impl<'a, P: ParserWithMode<'a>> AsNode<'a, P> for ReservedMemory<'a, P> { } } +#[allow(missing_docs)] pub struct ReservedMemoryChildrenIter<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { children: NodeChildrenIter<'a, (P::Parser, NoPanic)>, } @@ -170,11 +173,13 @@ impl<'a, P: ParserWithMode<'a>> Iterator for ReservedMemoryChildrenIter<'a, P> { } } +#[allow(missing_docs)] pub struct ReservedMemoryChild<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { node: FallibleNode<'a, P>, } impl<'a, P: ParserWithMode<'a>> ReservedMemoryChild<'a, P> { + #[allow(missing_docs)] pub fn name(&self) -> P::Output> { P::to_output(self.node.name()) } @@ -327,9 +332,11 @@ impl<'a, P: ParserWithMode<'a>> AsNode<'a, P> for ReservedMemoryChild<'a, P> { } } -/// A memory region +/// A memory region. #[derive(Debug, Clone, Copy, PartialEq)] pub struct MemoryRegion { + #[allow(missing_docs)] pub starting_address: u64, + #[allow(missing_docs)] pub size: Option, } diff --git a/src/nodes/root.rs b/src/nodes/root.rs index 5942ea3..20ea7f9 100644 --- a/src/nodes/root.rs +++ b/src/nodes/root.rs @@ -388,6 +388,7 @@ impl<'a, P: ParserWithMode<'a>> core::fmt::Debug for Root<'a, P> { } } +#[allow(missing_docs)] pub struct AllNodesWithNameIter<'a, 'b, P: ParserWithMode<'a>> { pub(crate) iter: AllNodesIter<'a, (P::Parser, NoPanic)>, pub(crate) name: &'b str, @@ -443,6 +444,7 @@ impl<'a, 'b, P: ParserWithMode<'a>> Iterator for AllCompatibleIter<'a, 'b, P> { } } +#[allow(missing_docs)] pub struct AllNodesIter<'a, P: ParserWithMode<'a>> { pub(crate) parser: P, pub(crate) parents: [&'a [

>::Granularity]; 16], diff --git a/src/properties.rs b/src/properties.rs index e630bf3..4f7a199 100644 --- a/src/properties.rs +++ b/src/properties.rs @@ -2,10 +2,15 @@ // v. 2.0. If a copy of the MPL was not distributed with this file, You can // obtain one at https://mozilla.org/MPL/2.0/. +/// Types for various `*-cells` properties. pub mod cells; +/// Types for working with interrupt properties. pub mod interrupts; +/// Types for working with the `ranges` property. pub mod ranges; +/// Type for working with the `reg` property. pub mod reg; +/// Abstractions for various devicetree value types. pub mod values; use crate::{ diff --git a/src/properties/interrupts/pci.rs b/src/properties/interrupts/pci.rs index 048e89a..cb37a52 100644 --- a/src/properties/interrupts/pci.rs +++ b/src/properties/interrupts/pci.rs @@ -58,6 +58,7 @@ impl PartialEq for &'_ PciAddress { #[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct PciAddressHighBits(u32); +#[allow(missing_docs)] impl PciAddressHighBits { #[inline(always)] pub fn new(raw: u32) -> Self { @@ -118,6 +119,8 @@ impl core::ops::BitAnd for PciAddress { } } +/// Type of PCI address space. +#[allow(missing_docs)] #[repr(u8)] pub enum PciAddressSpace { Configuration = 0b00, @@ -126,6 +129,7 @@ pub enum PciAddressSpace { Memory64 = 0b11, } +#[allow(missing_docs)] #[derive(Default)] pub struct PciAddressCollector { address: PciAddress, From df362b7f25cbd4b32aa9137ff33e8fd46a06083d Mon Sep 17 00:00:00 2001 From: repnop Date: Sat, 10 Jan 2026 22:22:57 -0500 Subject: [PATCH 38/38] fix: clippy lints --- src/nodes.rs | 8 ++++---- src/nodes/cpus.rs | 30 ++++++++++-------------------- src/pretty_print.rs | 3 +-- src/properties/values.rs | 2 +- src/tests.rs | 2 +- 5 files changed, 17 insertions(+), 28 deletions(-) diff --git a/src/nodes.rs b/src/nodes.rs index 39c4476..1d21fbf 100644 --- a/src/nodes.rs +++ b/src/nodes.rs @@ -57,11 +57,11 @@ pub enum SearchableNodeName<'a> { /// Currently, two type impls are defined on types other than /// [`SearchableNodeName`]: /// 1. [`NodeName`]: corresponds directly to a -/// [`SearchableNodeName::WithUnitAddress`]. +/// [`SearchableNodeName::WithUnitAddress`]. /// 2. [`&str`]: attempts to parse the `str` as `name@unit-address`, -/// corresponding to [`SearchableNodeName::WithUnitAddress`], or as -/// just a base node name with no specified unit address, which will -/// resolve to the first node with that base name found. +/// corresponding to [`SearchableNodeName::WithUnitAddress`], or as just a +/// base node name with no specified unit address, which will resolve +/// to the first node with that base name found. pub trait IntoSearchableNodeName<'a>: Sized + crate::sealed::Sealed { #[allow(missing_docs)] fn into_searchable_node_name(self) -> SearchableNodeName<'a>; diff --git a/src/nodes/cpus.rs b/src/nodes/cpus.rs index c102235..47b5fc6 100644 --- a/src/nodes/cpus.rs +++ b/src/nodes/cpus.rs @@ -93,16 +93,14 @@ impl<'a, P: ParserWithMode<'a>> AsNode<'a, P> for Cpus<'a, P> { fn filter_cpus<'a, P: ParserWithMode<'a>>(node: &Result, FdtError>) -> bool { match node { - Ok(node) => match node.name().map(|n| n.name) { - Ok("cpu") => true, - _ => false, - }, + Ok(node) => matches!(node.name().map(|n| n.name), Ok("cpu")), _ => true, } } #[allow(missing_docs)] pub struct CpusIter<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { + #[allow(clippy::type_complexity)] children: core::iter::Filter< NodeChildrenIter<'a, (P::Parser, NoPanic)>, fn(&Result, FdtError>) -> bool, @@ -721,16 +719,14 @@ impl<'a, P: ParserWithMode<'a>> CpuTopology<'a, P> { fn filter_sockets<'a, P: ParserWithMode<'a>>(node: &Result, FdtError>) -> bool { match node { - Ok(node) => match node.name().map(|n| n.name) { - Ok(n) if n.starts_with("socket") => true, - _ => false, - }, + Ok(node) => matches!(node.name().map(|n| n.name), Ok(n) if n.starts_with("socket")), _ => true, } } #[allow(missing_docs)] pub struct CpuSocketIter<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { + #[allow(clippy::type_complexity)] children: core::iter::Filter< NodeChildrenIter<'a, (P::Parser, NoPanic)>, fn(&Result, FdtError>) -> bool, @@ -750,16 +746,14 @@ impl<'a, P: ParserWithMode<'a>> Iterator for CpuSocketIter<'a, P> { fn filter_clusters<'a, P: ParserWithMode<'a>>(node: &Result, FdtError>) -> bool { match node { - Ok(node) => match node.name().map(|n| n.name) { - Ok(n) if n.starts_with("cluster") => true, - _ => false, - }, + Ok(node) => matches!(node.name().map(|n| n.name), Ok(n) if n.starts_with("cluster")), _ => true, } } #[allow(missing_docs)] pub struct CpuClusterIter<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { + #[allow(clippy::type_complexity)] children: core::iter::Filter< NodeChildrenIter<'a, (P::Parser, NoPanic)>, fn(&Result, FdtError>) -> bool, @@ -830,16 +824,14 @@ impl<'a, P: ParserWithMode<'a>> CpuCluster<'a, P> { fn filter_cores<'a, P: ParserWithMode<'a>>(node: &Result, FdtError>) -> bool { match node { - Ok(node) => match node.name().map(|n| n.name) { - Ok(n) if n.starts_with("core") => true, - _ => false, - }, + Ok(node) => matches!(node.name().map(|n| n.name), Ok(n) if n.starts_with("core")), _ => true, } } #[allow(missing_docs)] pub struct CpuCoreIter<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { + #[allow(clippy::type_complexity)] children: core::iter::Filter< NodeChildrenIter<'a, (P::Parser, NoPanic)>, fn(&Result, FdtError>) -> bool, @@ -910,16 +902,14 @@ impl<'a, P: ParserWithMode<'a>> CpuCore<'a, P> { fn filter_threads<'a, P: ParserWithMode<'a>>(node: &Result, FdtError>) -> bool { match node { - Ok(node) => match node.name().map(|n| n.name) { - Ok(n) if n.starts_with("thread") => true, - _ => false, - }, + Ok(node) => matches!(node.name().map(|n| n.name), Ok(n) if n.starts_with("thread")), _ => true, } } #[allow(missing_docs)] pub struct CpuThreadIter<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> { + #[allow(clippy::type_complexity)] children: core::iter::Filter< NodeChildrenIter<'a, (P::Parser, NoPanic)>, fn(&Result, FdtError>) -> bool, diff --git a/src/pretty_print.rs b/src/pretty_print.rs index f44d8f8..8ac7662 100644 --- a/src/pretty_print.rs +++ b/src/pretty_print.rs @@ -57,8 +57,7 @@ pub fn print_fdt<'a, P: Parser<'a>>( let (mut n_braces, mut final_depth) = (0, 0); writeln!(f, "/ {{")?; any_props = print_properties(f, root.node, 0)?; - loop { - let Some((depth, node)) = node_iter.next().transpose()? else { break }; + while let Some((depth, node)) = node_iter.next().transpose()? { let next_depth = match node_iter.peek().cloned().transpose()? { Some((next_depth, _)) => next_depth, None => 0, diff --git a/src/properties/values.rs b/src/properties/values.rs index 47bb339..a068a82 100644 --- a/src/properties/values.rs +++ b/src/properties/values.rs @@ -97,7 +97,7 @@ impl<'a> U32List<'a> { impl<'a> PropertyValue<'a> for U32List<'a> { fn parse(value: &'a [u8]) -> Result { - if value.len() % 4 != 0 { + if !value.len().is_multiple_of(4) { return Err(InvalidPropertyValue); } diff --git a/src/tests.rs b/src/tests.rs index 20194cc..6fdaaa0 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -22,7 +22,7 @@ struct AlignArrayUp([u8; N]); impl AlignArrayUp { const fn align_up(self) -> [u8; M] { assert!(M > N); - assert!(M % 4 == 0); + assert!(M.is_multiple_of(4)); let mut copy: [u8; M] = [0u8; M]; let mut i = 0;