cros_fdt: Implement FdtNode properties API

Implement additional property APIs on FdtNodes. Beside the generic
property getter method, implement special getter and setter for phandle
values with an offset parameter. These will be used for updating phandle
references stored inside larger property values. Also, each FDT property type
now implements a new `AsFdtPropval` trait for parsing a binary property
value into a supported type.

Bug: b/296796644
Test: cd cros_fdt && cargo test
Change-Id: I2d9e2259e49f8a88e99d67b25672b1add7c0b6b5
Reviewed-on: https://chromium-review.googlesource.com/c/crosvm/crosvm/+/4855961
Reviewed-by: Daniel Verkamp <dverkamp@chromium.org>
Commit-Queue: Jakob Vukalović <jakobvukalovic@google.com>
This commit is contained in:
Jakob Vukalovic 2023-10-09 11:52:48 +01:00 committed by crosvm LUCI
parent 134acc4516
commit 6ba5fab07a
2 changed files with 304 additions and 0 deletions

View file

@ -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::<u32>();
@ -434,6 +435,49 @@ impl FdtNode {
Ok(())
}
/// Read property value if it exists.
///
/// # Arguments
///
/// `name` - name of the property.
pub fn get_prop<T>(&self, name: &str) -> Option<T>
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<u32> {
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::<u32>("myprop").unwrap(), 1u32);
node.set_prop("myprop", 0xabcdef9876543210u64).unwrap();
assert_eq!(
node.get_prop::<u64>("myprop").unwrap(),
0xabcdef9876543210u64
);
node.set_prop("myprop", ()).unwrap();
assert_eq!(node.get_prop::<Vec<u8>>("myprop").unwrap(), []);
node.set_prop("myprop", vec![1u8, 2u8, 3u8]).unwrap();
assert_eq!(
node.get_prop::<Vec<u8>>("myprop").unwrap(),
vec![1u8, 2u8, 3u8]
);
node.set_prop("myprop", vec![1u32, 2u32, 3u32]).unwrap();
assert_eq!(
node.get_prop::<Vec<u32>>("myprop").unwrap(),
vec![1u32, 2u32, 3u32]
);
node.set_prop("myprop", vec![1u64, 2u64, 3u64]).unwrap();
assert_eq!(
node.get_prop::<Vec<u64>>("myprop").unwrap(),
vec![1u64, 2u64, 3u64]
);
node.set_prop("myprop", "myval".to_string()).unwrap();
assert_eq!(
node.get_prop::<String>("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::<Vec<String>>("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::<Vec<u8>>(pname).unwrap().len(), s);
}
}
#[test]
fn fdt_load_nodes_nested() {
let strings_blob = &FDT_BLOB_STRINGS[..];

View file

@ -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<String> {
}
}
/// 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<Self>
where
Self: Sized;
}
impl FromFdtPropval for () {
fn from_propval(propval: &[u8]) -> Option<Self> {
propval.is_empty().then_some(())
}
}
impl FromFdtPropval for Vec<u8> {
fn from_propval(propval: &[u8]) -> Option<Self> {
Some(propval.into())
}
}
impl FromFdtPropval for u32 {
fn from_propval(propval: &[u8]) -> Option<Self> {
if propval.len() == SIZE_U32 {
Some(u32::from_be_bytes(propval.try_into().unwrap()))
} else {
None
}
}
}
impl FromFdtPropval for Vec<u32> {
fn from_propval(propval: &[u8]) -> Option<Self> {
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<Self> {
if propval.len() == SIZE_U64 {
Some(u64::from_be_bytes(propval.try_into().unwrap()))
} else {
None
}
}
}
impl FromFdtPropval for Vec<u64> {
fn from_propval(propval: &[u8]) -> Option<Self> {
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<Self> {
c_str_to_string(propval)
}
}
impl FromFdtPropval for Vec<String> {
fn from_propval(propval: &[u8]) -> Option<Self> {
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::<u8>::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::<u32>::from_propval(&[0x00u8, 0x00, 0x00, 0x01, 0x00, 0x00, 0xAB, 0xCD]).unwrap(),
[0x1u32, 0xABCDu32]
);
assert_eq!(
Vec::<u64>::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::<String>::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::<String>::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());
}
}