libfdt: Extend Rust wrapper with write functions
Add a subset of the libfdt "read-write" function to the Rust wrapper.
Bug: 255521657
Test: atest vmbase_example.integration_test
Change-Id: Id73e823d7f522431c8f9dd23799841317a9ce38a
diff --git a/libs/libfdt/src/lib.rs b/libs/libfdt/src/lib.rs
index 01f7b36..c38381b 100644
--- a/libs/libfdt/src/lib.rs
+++ b/libs/libfdt/src/lib.rs
@@ -384,6 +384,69 @@
}
}
+/// Mutable FDT node.
+pub struct FdtNodeMut<'a> {
+ fdt: &'a mut Fdt,
+ offset: c_int,
+}
+
+impl<'a> FdtNodeMut<'a> {
+ /// Append a property name-value (possibly empty) pair to the given node.
+ pub fn appendprop<T: AsRef<[u8]>>(&mut self, name: &CStr, value: &T) -> Result<()> {
+ // SAFETY - Accesses are constrained to the DT totalsize (validated by ctor).
+ let ret = unsafe {
+ libfdt_bindgen::fdt_appendprop(
+ self.fdt.as_mut_ptr(),
+ self.offset,
+ name.as_ptr(),
+ value.as_ref().as_ptr().cast::<c_void>(),
+ value.as_ref().len().try_into().map_err(|_| FdtError::BadValue)?,
+ )
+ };
+
+ fdt_err_expect_zero(ret)
+ }
+
+ /// Append a (address, size) pair property to the given node.
+ pub fn appendprop_addrrange(&mut self, name: &CStr, addr: u64, size: u64) -> Result<()> {
+ // SAFETY - Accesses are constrained to the DT totalsize (validated by ctor).
+ let ret = unsafe {
+ libfdt_bindgen::fdt_appendprop_addrrange(
+ self.fdt.as_mut_ptr(),
+ self.parent()?.offset,
+ self.offset,
+ name.as_ptr(),
+ addr,
+ size,
+ )
+ };
+
+ fdt_err_expect_zero(ret)
+ }
+
+ /// Get reference to the containing device tree.
+ pub fn fdt(&mut self) -> &mut Fdt {
+ self.fdt
+ }
+
+ /// Add a new subnode to the given node and return it as a FdtNodeMut on success.
+ pub fn add_subnode(&'a mut self, name: &CStr) -> Result<Self> {
+ // SAFETY - Accesses are constrained to the DT totalsize (validated by ctor).
+ let ret = unsafe {
+ libfdt_bindgen::fdt_add_subnode(self.fdt.as_mut_ptr(), self.offset, name.as_ptr())
+ };
+
+ Ok(Self { fdt: self.fdt, offset: fdt_err(ret)? })
+ }
+
+ fn parent(&'a self) -> Result<FdtNode<'a>> {
+ // SAFETY - Accesses (read-only) are constrained to the DT totalsize.
+ let ret = unsafe { libfdt_bindgen::fdt_parent_offset(self.fdt.as_ptr(), self.offset) };
+
+ Ok(FdtNode { fdt: &*self.fdt, offset: fdt_err(ret)? })
+ }
+}
+
/// Iterator over nodes sharing a same compatible string.
pub struct CompatibleIterator<'a> {
node: FdtNode<'a>,
@@ -411,7 +474,7 @@
}
}
-/// Wrapper around low-level read-only libfdt functions.
+/// Wrapper around low-level libfdt functions.
#[repr(transparent)]
pub struct Fdt {
bytes: [u8],
@@ -428,6 +491,16 @@
Ok(fdt)
}
+ /// Wraps a mutable slice containing a Flattened Device Tree.
+ ///
+ /// Fails if the FDT does not pass validation.
+ pub fn from_mut_slice(fdt: &mut [u8]) -> Result<&mut Self> {
+ // SAFETY - The FDT will be validated before it is returned.
+ let fdt = unsafe { Self::unchecked_from_mut_slice(fdt) };
+ fdt.check_full()?;
+ Ok(fdt)
+ }
+
/// Wraps a slice containing a Flattened Device Tree.
///
/// # Safety
@@ -437,6 +510,39 @@
mem::transmute::<&[u8], &Self>(fdt)
}
+ /// Wraps a mutable slice containing a Flattened Device Tree.
+ ///
+ /// # Safety
+ ///
+ /// The returned FDT might be invalid, only use on slices containing a valid DT.
+ pub unsafe fn unchecked_from_mut_slice(fdt: &mut [u8]) -> &mut Self {
+ mem::transmute::<&mut [u8], &mut Self>(fdt)
+ }
+
+ /// Make the whole slice containing the DT available to libfdt.
+ pub fn unpack(&mut self) -> Result<()> {
+ // SAFETY - "Opens" the DT in-place (supported use-case) by updating its header and
+ // internal structures to make use of the whole self.fdt slice but performs no accesses
+ // outside of it and leaves the DT in a state that will be detected by other functions.
+ let ret = unsafe {
+ libfdt_bindgen::fdt_open_into(
+ self.as_ptr(),
+ self.as_mut_ptr(),
+ self.capacity().try_into().map_err(|_| FdtError::Internal)?,
+ )
+ };
+ fdt_err_expect_zero(ret)
+ }
+
+ /// Pack the DT to take a minimum amount of memory.
+ ///
+ /// Doesn't shrink the underlying memory slice.
+ pub fn pack(&mut self) -> Result<()> {
+ // SAFETY - "Closes" the DT in-place by updating its header and relocating its structs.
+ let ret = unsafe { libfdt_bindgen::fdt_pack(self.as_mut_ptr()) };
+ fdt_err_expect_zero(ret)
+ }
+
/// Return an iterator of memory banks specified the "/memory" node.
///
/// NOTE: This does not support individual "/memory@XXXX" banks.
@@ -473,6 +579,17 @@
CompatibleIterator::new(self, compatible)
}
+ /// Get the mutable root node of the tree.
+ pub fn root_mut(&mut self) -> Result<FdtNodeMut> {
+ self.node_mut(CStr::from_bytes_with_nul(b"/\0").unwrap())?.ok_or(FdtError::Internal)
+ }
+
+ /// Find a mutable tree node by its full path.
+ pub fn node_mut(&mut self, path: &CStr) -> Result<FdtNodeMut> {
+ let offset = self.path_offset(path)?;
+ Ok(FdtNodeMut { fdt: self, offset })
+ }
+
fn path_offset(&self, path: &CStr) -> Result<c_int> {
let len = path.to_bytes().len().try_into().map_err(|_| FdtError::BadPath)?;
// SAFETY - Accesses are constrained to the DT totalsize (validated by ctor) and the
@@ -499,4 +616,12 @@
fn as_ptr(&self) -> *const c_void {
self as *const _ as *const c_void
}
+
+ fn as_mut_ptr(&mut self) -> *mut c_void {
+ self as *mut _ as *mut c_void
+ }
+
+ fn capacity(&self) -> usize {
+ self.bytes.len()
+ }
}
diff --git a/vmbase/example/idmap.S b/vmbase/example/idmap.S
index 7fc5d5e..71a6ade 100644
--- a/vmbase/example/idmap.S
+++ b/vmbase/example/idmap.S
@@ -44,7 +44,7 @@
.fill 509, 8, 0x0 // 509 GiB of remaining VA space
/* level 2 */
-0: .quad .L_BLOCK_RO | 0x80000000 // DT provided by VMM
+0: .quad .L_BLOCK_MEM | 0x80000000 // DT provided by VMM
.quad .L_BLOCK_MEM_XIP | 0x80200000 // 2 MiB of DRAM containing image
.quad .L_BLOCK_MEM | 0x80400000 // 2 MiB of writable DRAM
.fill 509, 8, 0x0
diff --git a/vmbase/example/src/main.rs b/vmbase/example/src/main.rs
index dcff6e1..b305559 100644
--- a/vmbase/example/src/main.rs
+++ b/vmbase/example/src/main.rs
@@ -162,6 +162,30 @@
info!("node compatible with '{}' at {reg:?}", compatible.to_str().unwrap());
}
+ let writer = Fdt::from_mut_slice(fdt).unwrap();
+ writer.unpack().unwrap();
+ info!("FDT successfully unpacked.");
+
+ let path = CStr::from_bytes_with_nul(b"/memory\0").unwrap();
+ let mut node = writer.node_mut(path).unwrap();
+ let name = CStr::from_bytes_with_nul(b"child\0").unwrap();
+ let mut child = node.add_subnode(name).unwrap();
+ info!("Created subnode '{}/{}'.", path.to_str().unwrap(), name.to_str().unwrap());
+
+ let name = CStr::from_bytes_with_nul(b"str-property\0").unwrap();
+ child.appendprop(name, b"property-value\0").unwrap();
+ info!("Appended property '{}'.", name.to_str().unwrap());
+
+ let name = CStr::from_bytes_with_nul(b"pair-property\0").unwrap();
+ let addr = 0x0123_4567u64;
+ let size = 0x89ab_cdefu64;
+ child.appendprop_addrrange(name, addr, size).unwrap();
+ info!("Appended property '{}'.", name.to_str().unwrap());
+
+ let writer = child.fdt();
+ writer.pack().unwrap();
+ info!("FDT successfully packed.");
+
info!("FDT checks done.");
}