diff --git a/cros_fdt/src/fdt.rs b/cros_fdt/src/fdt.rs index 0eb2024e4e..5228e13175 100644 --- a/cros_fdt/src/fdt.rs +++ b/cros_fdt/src/fdt.rs @@ -13,6 +13,7 @@ use std::io; use remain::sorted; use thiserror::Error as ThisError; +use crate::propval::FromFdtPropval; use crate::propval::ToFdtPropval; pub(crate) const SIZE_U32: usize = std::mem::size_of::(); @@ -434,6 +435,49 @@ impl FdtNode { Ok(()) } + /// Read property value if it exists. + /// + /// # Arguments + /// + /// `name` - name of the property. + pub fn get_prop(&self, name: &str) -> Option + where + T: FromFdtPropval, + { + T::from_propval(self.props.get(name)?.as_slice()) + } + + // Read a phandle value (a `u32`) at some offset within a property value. + // Returns `None` if a phandle value cannot be constructed. + #[allow(unused)] + pub(crate) fn phandle_at_offset(&self, name: &str, offset: usize) -> Option { + let data = self.props.get(name)?; + data.get(offset..offset + SIZE_U32) + .and_then(u32::from_propval) + } + + // Overwrite a phandle value (a `u32`) at some offset within a property value. + // Returns `Err` if the property doesn't exist, or if the property value is too short to + // construct a `u32` at given offset. Does not change property value size. + #[allow(unused)] + pub(crate) fn update_phandle_at_offset( + &mut self, + name: &str, + offset: usize, + phandle: u32, + ) -> Result<()> { + let propval = self + .props + .get_mut(name) + .ok_or_else(|| Error::InvalidName(format!("property {name} does not exist")))?; + if let Some(bytes) = propval.get_mut(offset..offset + SIZE_U32) { + bytes.copy_from_slice(phandle.to_propval()?.as_slice()); + Ok(()) + } else { + Err(Error::PropertyValueInvalid) + } + } + /// Write a property. /// /// # Arguments @@ -720,6 +764,45 @@ mod tests { const EXPECTED_STRINGS: [&str; 7] = ["null", "u32", "u64", "str", "strlst", "arru32", "arru64"]; + const FDT_BLOB_NODES_ROOT_ONLY: [u8; 0x90] = [ + 0x00, 0x00, 0x00, 0x01, // FDT_BEGIN_NODE + 0x00, 0x00, 0x00, 0x00, // node name ("") + padding + 0x00, 0x00, 0x00, 0x03, // FDT_PROP (null) + 0x00, 0x00, 0x00, 0x00, // prop len (0) + 0x00, 0x00, 0x00, 0x00, // prop nameoff (0) + 0x00, 0x00, 0x00, 0x03, // FDT_PROP (u32) + 0x00, 0x00, 0x00, 0x04, // prop len (4) + 0x00, 0x00, 0x00, 0x05, // prop nameoff (0x05) + 0x12, 0x34, 0x56, 0x78, // prop u32 value (0x12345678) + 0x00, 0x00, 0x00, 0x03, // FDT_PROP (u64) + 0x00, 0x00, 0x00, 0x08, // prop len (8) + 0x00, 0x00, 0x00, 0x09, // prop nameoff (0x09) + 0x12, 0x34, 0x56, 0x78, // prop u64 value high (0x12345678) + 0x87, 0x65, 0x43, 0x21, // prop u64 value low (0x87654321) + 0x00, 0x00, 0x00, 0x03, // FDT_PROP (string) + 0x00, 0x00, 0x00, 0x06, // prop len (6) + 0x00, 0x00, 0x00, 0x0D, // prop nameoff (0x0D) + b'h', b'e', b'l', b'l', // prop str value ("hello") + padding + b'o', 0x00, 0x00, 0x00, // "o\0" + padding + 0x00, 0x00, 0x00, 0x03, // FDT_PROP (string list) + 0x00, 0x00, 0x00, 0x07, // prop len (7) + 0x00, 0x00, 0x00, 0x11, // prop nameoff (0x11) + b'h', b'i', 0x00, b'b', // prop value ("hi", "bye") + b'y', b'e', 0x00, 0x00, // "ye\0" + padding + 0x00, 0x00, 0x00, 0x03, // FDT_PROP (u32 array) + 0x00, 0x00, 0x00, 0x08, // prop len (8) + 0x00, 0x00, 0x00, 0x18, // prop nameoff (0x18) + 0x12, 0x34, 0x56, 0x78, // prop value 0 + 0xAA, 0xBB, 0xCC, 0xDD, // prop value 1 + 0x00, 0x00, 0x00, 0x03, // FDT_PROP (u64 array) + 0x00, 0x00, 0x00, 0x08, // prop len (8) + 0x00, 0x00, 0x00, 0x1f, // prop nameoff (0x1F) + 0x12, 0x34, 0x56, 0x78, // prop u64 value 0 high + 0x87, 0x65, 0x43, 0x21, // prop u64 value 0 low + 0x00, 0x00, 0x00, 0x02, // FDT_END_NODE + 0x00, 0x00, 0x00, 0x09, // FDT_END + ]; + /* Node structure: / @@ -793,6 +876,57 @@ mod tests { ); } + #[test] + fn fdt_test_node_props() { + let mut node = FdtNode::empty("mynode").unwrap(); + node.set_prop("myprop", 1u32).unwrap(); + assert_eq!(node.get_prop::("myprop").unwrap(), 1u32); + node.set_prop("myprop", 0xabcdef9876543210u64).unwrap(); + assert_eq!( + node.get_prop::("myprop").unwrap(), + 0xabcdef9876543210u64 + ); + node.set_prop("myprop", ()).unwrap(); + assert_eq!(node.get_prop::>("myprop").unwrap(), []); + node.set_prop("myprop", vec![1u8, 2u8, 3u8]).unwrap(); + assert_eq!( + node.get_prop::>("myprop").unwrap(), + vec![1u8, 2u8, 3u8] + ); + node.set_prop("myprop", vec![1u32, 2u32, 3u32]).unwrap(); + assert_eq!( + node.get_prop::>("myprop").unwrap(), + vec![1u32, 2u32, 3u32] + ); + node.set_prop("myprop", vec![1u64, 2u64, 3u64]).unwrap(); + assert_eq!( + node.get_prop::>("myprop").unwrap(), + vec![1u64, 2u64, 3u64] + ); + node.set_prop("myprop", "myval".to_string()).unwrap(); + assert_eq!( + node.get_prop::("myprop").unwrap(), + "myval".to_string() + ); + node.set_prop( + "myprop", + vec![ + "myval1".to_string(), + "myval2".to_string(), + "myval3".to_string(), + ], + ) + .unwrap(); + assert_eq!( + node.get_prop::>("myprop").unwrap(), + vec![ + "myval1".to_string(), + "myval2".to_string(), + "myval3".to_string() + ] + ); + } + #[test] fn fdt_simple_use() { let mut fdt = Fdt::new(&[]); @@ -834,6 +968,32 @@ mod tests { assert_eq!(strings.intern_string("strlst"), 17); } + #[test] + fn fdt_load_props() { + const PROP_SIZES: [(&str, usize); 7] = [ + ("null", 0), + ("u32", 4), + ("u64", 8), + ("str", 6), + ("strlst", 7), + ("arru32", 8), + ("arru64", 8), + ]; + + let blob: &[u8] = &FDT_BLOB_STRINGS[..]; + let strings = FdtStrings::from_blob(blob).unwrap(); + let blob: &[u8] = &FDT_BLOB_NODES_ROOT_ONLY[..]; + let node = FdtNode::from_blob(blob, &strings).unwrap(); + + assert_eq!(node.name, ""); + assert_eq!(node.subnodes.len(), 0); + assert_eq!(node.props.len(), PROP_SIZES.len()); + + for (pname, s) in PROP_SIZES.into_iter() { + assert_eq!(node.get_prop::>(pname).unwrap().len(), s); + } + } + #[test] fn fdt_load_nodes_nested() { let strings_blob = &FDT_BLOB_STRINGS[..]; diff --git a/cros_fdt/src/propval.rs b/cros_fdt/src/propval.rs index 6637ff26d5..1090c25d12 100644 --- a/cros_fdt/src/propval.rs +++ b/cros_fdt/src/propval.rs @@ -6,8 +6,11 @@ use std::mem::size_of_val; +use crate::fdt::c_str_to_string; use crate::fdt::Error; use crate::fdt::Result; +use crate::fdt::SIZE_U32; +use crate::fdt::SIZE_U64; /// Conversion into an FDT property value. /// @@ -166,6 +169,101 @@ impl ToFdtPropval for Vec { } } +/// Conversion from an FDT property value. +/// +/// Implementing `FromFdtPropval` for a type defines its construction from a raw +/// FDT property value (a byte slice). +pub trait FromFdtPropval { + // Try to convert FDT property bytes to `Self`, return `None` if impossible. + fn from_propval(propval: &[u8]) -> Option + where + Self: Sized; +} + +impl FromFdtPropval for () { + fn from_propval(propval: &[u8]) -> Option { + propval.is_empty().then_some(()) + } +} + +impl FromFdtPropval for Vec { + fn from_propval(propval: &[u8]) -> Option { + Some(propval.into()) + } +} + +impl FromFdtPropval for u32 { + fn from_propval(propval: &[u8]) -> Option { + if propval.len() == SIZE_U32 { + Some(u32::from_be_bytes(propval.try_into().unwrap())) + } else { + None + } + } +} + +impl FromFdtPropval for Vec { + fn from_propval(propval: &[u8]) -> Option { + if propval.len() % SIZE_U32 != 0 { + None + } else { + Some( + propval + .chunks(SIZE_U32) + .map(|v| u32::from_be_bytes(v.try_into().unwrap())) + .collect(), + ) + } + } +} + +impl FromFdtPropval for u64 { + fn from_propval(propval: &[u8]) -> Option { + if propval.len() == SIZE_U64 { + Some(u64::from_be_bytes(propval.try_into().unwrap())) + } else { + None + } + } +} + +impl FromFdtPropval for Vec { + fn from_propval(propval: &[u8]) -> Option { + if propval.len() % SIZE_U64 != 0 { + None + } else { + Some( + propval + .chunks(SIZE_U64) + .map(|v| u64::from_be_bytes(v.try_into().unwrap())) + .collect(), + ) + } + } +} + +impl FromFdtPropval for String { + fn from_propval(propval: &[u8]) -> Option { + c_str_to_string(propval) + } +} + +impl FromFdtPropval for Vec { + fn from_propval(propval: &[u8]) -> Option { + if Some(&0) == propval.last() { + Some( + propval + .split(|&b| b == 0u8) + .take_while(|s| !s.is_empty()) + .filter_map(|b| String::from_utf8(b.into()).ok()) + .collect(), + ) + } else { + None + } + } +} + #[cfg(test)] mod tests { use super::*; @@ -207,4 +305,50 @@ mod tests { ); "abc\0def".to_propval().expect_err("invalid string"); } + + #[test] + fn fdt_from_propval() { + assert_eq!(Vec::::from_propval(&[]).unwrap(), []); + assert_eq!(u32::from_propval(&[0, 0, 0, 1]).unwrap(), 1u32); + assert_eq!( + u32::from_propval(&[0x12u8, 0x34, 0x56, 0x78]).unwrap(), + 0x12345678u32 + ); + assert_eq!( + u64::from_propval(&[0x00u8, 0x00, 0x12, 0x34, 0x56, 0x78, 0xAB, 0xCD]).unwrap(), + 0x12345678ABCDu64 + ); + assert_eq!( + Vec::::from_propval(&[0x00u8, 0x00, 0x00, 0x01, 0x00, 0x00, 0xAB, 0xCD]).unwrap(), + [0x1u32, 0xABCDu32] + ); + assert_eq!( + Vec::::from_propval(&[ + 0x00u8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0xAB, 0xCD, 0x00, + 0x00, 0x00, 0x00 + ]) + .unwrap(), + [0x1u64, 0xABCD00000000u64] + ); + assert_eq!( + String::from_propval(&[0x61u8, 0x62, 0x63, 0x20, 0x64, 0x65, 0x66, 0x00]).unwrap(), + "abc def" + ); + assert_eq!( + Vec::::from_propval(&[ + 0x61u8, 0x62, 0x63, 0x20, 0x64, 0x65, 0x66, 0x00, 0x67u8, 0x68, 0x69, 0x20, 0x6A, + 0x6B, 0x6C, 0x00, 0x6Du8, 0x6E, 0x6F, 0x20, 0x70, 0x71, 0x72, 0x00, + ]) + .unwrap(), + ["abc def", "ghi jkl", "mno pqr"], + ); + + assert!(Vec::::from_propval(&[ + 0x61u8, 0x62, 0x63, 0x20, 0x64, 0x65, 0x66, 0x00, 0x67u8, 0x68, + ]) + .is_none()); + assert!(String::from_propval(&[0x61u8, 0x62, 0x63]).is_none()); + assert!(u32::from_propval(&[0x61u8, 0x62]).is_none()); + assert!(u64::from_propval(&[0x61u8, 0x62, 0x61u8, 0x62, 0x61u8, 0x62]).is_none()); + } }