| // Copyright 2023-2024, The Android Open Source Project |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| //! This library provides a few wrapper APIs for libfdt_c |
| |
| #![cfg_attr(not(test), no_std)] |
| |
| extern crate alloc; |
| extern crate libc; |
| |
| use arrayvec::ArrayVec; |
| use core::ffi::{c_int, CStr}; |
| use core::mem::size_of; |
| use core::slice::{from_raw_parts, from_raw_parts_mut}; |
| use liberror::{Error, Result}; |
| use libfdt_bindgen::{ |
| fdt_add_subnode_namelen, fdt_del_node, fdt_get_property, fdt_header, fdt_move, fdt_setprop, |
| fdt_setprop_placeholder, fdt_strerror, fdt_subnode_offset_namelen, |
| }; |
| use libufdt_bindgen::ufdt_apply_multioverlay; |
| use zerocopy::{AsBytes, FromBytes, FromZeroes, Ref}; |
| |
| const MAXIMUM_OVERLAYS_TO_APPLY: usize = 16; |
| const MAXIMUM_OVERLAYS_ERROR_MSG: &str = "At most 16 overlays are supported to apply at a time"; |
| |
| /// Convert libfdt_c error code to Result |
| fn map_result(code: c_int) -> Result<c_int> { |
| match code { |
| // SAFETY: Static null terminated string returned from libfdt_c API. |
| v if v < 0 => { |
| Err(Error::Other(Some(unsafe { CStr::from_ptr(fdt_strerror(v)).to_str().unwrap() }))) |
| } |
| v => Ok(v), |
| } |
| } |
| |
| /// Convert libufdt_c error code to Result |
| fn map_result_libufdt(code: c_int) -> Result<c_int> { |
| match code { |
| v if v < 0 => Err(Error::Other(Some("Failed to execute libufdt call"))), |
| v => Ok(v), |
| } |
| } |
| |
| /// Check header and verified that totalsize does not exceed buffer size. |
| fn fdt_check_header(fdt: &[u8]) -> Result<()> { |
| // SAFETY: |
| // `fdt_check_header` is only access the memory pointed to by `fdt` during this call and |
| // not store the pointer for later use. `fdt` remains valid for the duration of this call. |
| map_result(unsafe { libfdt_bindgen::fdt_check_header(fdt.as_ptr() as *const _) })?; |
| match FdtHeader::from_bytes_ref(fdt)?.totalsize() <= fdt.len() { |
| true => Ok(()), |
| _ => Err(Error::InvalidInput), |
| } |
| } |
| |
| /// Wrapper of fdt_add_subnode_namelen() |
| fn fdt_add_subnode(fdt: &mut [u8], parent: c_int, name: &str) -> Result<c_int> { |
| // SAFETY: API from libfdt_c. |
| map_result(unsafe { |
| fdt_add_subnode_namelen( |
| fdt.as_mut_ptr() as *mut _, |
| parent, |
| name.as_ptr() as *const _, |
| name.len().try_into()?, |
| ) |
| }) |
| } |
| |
| /// Wrapper of fdt_subnode_offset_namelen() |
| fn fdt_subnode_offset(fdt: &[u8], parent: c_int, name: &str) -> Result<c_int> { |
| // SAFETY: API from libfdt_c. |
| map_result(unsafe { |
| fdt_subnode_offset_namelen( |
| fdt.as_ptr() as *const _, |
| parent, |
| name.as_ptr() as *const _, |
| name.len().try_into()?, |
| ) |
| }) |
| } |
| |
| /// Rust wrapper for the FDT header data. |
| #[repr(transparent)] |
| #[derive(Debug, Copy, Clone, AsBytes, FromBytes, FromZeroes, PartialEq)] |
| pub struct FdtHeader(fdt_header); |
| |
| impl FdtHeader { |
| /// Return the totalsize field. |
| pub fn totalsize(&self) -> usize { |
| u32::from_be(self.0.totalsize) as usize |
| } |
| |
| /// Return the minimal size of the FDT. Disregard trailing free space. |
| pub fn actual_size(&self) -> usize { |
| u32::from_be(self.0.off_dt_strings) |
| .checked_add(u32::from_be(self.0.size_dt_strings)) |
| .unwrap() as usize |
| } |
| |
| /// Update the totalsize field. |
| pub fn set_totalsize(&mut self, value: u32) { |
| self.0.totalsize = value.to_be(); |
| } |
| |
| /// Cast a bytes into a reference of FDT header |
| pub fn from_bytes_ref(buffer: &[u8]) -> Result<&FdtHeader> { |
| Ok(Ref::<_, FdtHeader>::new_from_prefix(buffer) |
| .ok_or(Error::BufferTooSmall(Some(size_of::<FdtHeader>())))? |
| .0 |
| .into_ref()) |
| } |
| |
| /// Cast a bytes into a mutable reference of FDT header. |
| pub fn from_bytes_mut(buffer: &mut [u8]) -> Result<&mut FdtHeader> { |
| Ok(Ref::<_, FdtHeader>::new_from_prefix(buffer) |
| .ok_or(Error::BufferTooSmall(Some(size_of::<FdtHeader>())))? |
| .0 |
| .into_mut()) |
| } |
| |
| /// Get FDT header and raw bytes from a raw pointer. |
| /// |
| /// Caller should guarantee that |
| /// 1. `ptr` contains a valid FDT. |
| /// 2. The buffer remains valid as long as the returned references are in use. |
| pub unsafe fn from_raw(ptr: *const u8) -> Result<(&'static FdtHeader, &'static [u8])> { |
| // SAFETY: By safety requirement of this function, `ptr` points to a valid FDT and remains |
| // valid when in use. |
| unsafe { |
| map_result(libfdt_bindgen::fdt_check_header(ptr as *const _))?; |
| let header_bytes = from_raw_parts(ptr, size_of::<FdtHeader>()); |
| let header = Self::from_bytes_ref(header_bytes)?; |
| Ok((header, from_raw_parts(ptr, header.totalsize()))) |
| } |
| } |
| } |
| |
| /// Object for managing an FDT. |
| pub struct Fdt<T>(T); |
| |
| /// Read only APIs. |
| impl<'a, T: AsRef<[u8]> + 'a> Fdt<T> { |
| /// Creates a new [Fdt] wrapping the contents of `init`. |
| pub fn new(init: T) -> Result<Self> { |
| fdt_check_header(init.as_ref())?; |
| Ok(Fdt(init)) |
| } |
| |
| /// Returns the [FdtHeader], or an error if the underlying buffer was invalid. |
| pub fn header_ref(&self) -> Result<&FdtHeader> { |
| FdtHeader::from_bytes_ref(self.0.as_ref()) |
| } |
| |
| /// Returns the totalsize according to FDT header. Trailing free space is included. |
| pub fn size(&self) -> Result<usize> { |
| Ok(self.header_ref()?.totalsize()) |
| } |
| |
| /// Get a property from an existing node. |
| pub fn get_property(&self, path: &str, name: &CStr) -> Result<&'a [u8]> { |
| let node = self.find_node(path)?; |
| let mut len: c_int = 0; |
| // SAFETY: API from libfdt_c. |
| let ptr = unsafe { |
| fdt_get_property( |
| self.0.as_ref().as_ptr() as *const _, |
| node, |
| name.to_bytes_with_nul().as_ptr() as *const _, |
| &mut len as *mut _, |
| ) |
| }; |
| // SAFETY: Buffer returned by API from libfdt_c. |
| match unsafe { ptr.as_ref() } { |
| // SAFETY: Buffer returned by API from libfdt_c. |
| Some(v) => Ok(unsafe { |
| from_raw_parts( |
| v.data.as_ptr() as *const u8, |
| u32::from_be(v.len).try_into().or(Err(Error::Other(None)))?, |
| ) |
| }), |
| _ => Err(map_result(len).unwrap_err()), |
| } |
| } |
| |
| /// Find the offset of a node by a given node path. |
| fn find_node(&self, path: &str) -> Result<c_int> { |
| let mut curr: c_int = 0; |
| for name in path.split('/') { |
| if name.len() == 0 { |
| continue; |
| } |
| curr = fdt_subnode_offset(self.0.as_ref(), curr, name)?; |
| } |
| Ok(curr) |
| } |
| } |
| |
| /// APIs when data can be modified. |
| impl<T: AsMut<[u8]> + AsRef<[u8]>> Fdt<T> { |
| /// Creates a new mut [Fdt] wrapping the contents of `init`. |
| pub fn new_mut(init: T) -> Result<Self> { |
| let mut fdt = Fdt::new(init)?; |
| let new_size: u32 = fdt.as_mut().len().try_into().or(Err(Error::Other(None)))?; |
| fdt.header_mut()?.set_totalsize(new_size); |
| Ok(fdt) |
| } |
| |
| /// Creates a mutable [Fdt] copied from `init`. |
| pub fn new_from_init(mut fdt: T, init: &[u8]) -> Result<Self> { |
| fdt_check_header(init)?; |
| // SAFETY: API from libfdt_c. |
| map_result(unsafe { |
| fdt_move( |
| init.as_ptr() as *const _, |
| fdt.as_mut().as_ptr() as *mut _, |
| fdt.as_mut().len().try_into().or(Err(Error::Other(None)))?, |
| ) |
| })?; |
| let new_size: u32 = fdt.as_mut().len().try_into().or(Err(Error::Other(None)))?; |
| let mut ret = Fdt::new(fdt)?; |
| ret.header_mut()?.set_totalsize(new_size); |
| Ok(ret) |
| } |
| |
| /// Parse and get the FDT header. |
| fn header_mut(&mut self) -> Result<&mut FdtHeader> { |
| FdtHeader::from_bytes_mut(self.0.as_mut()) |
| } |
| |
| /// Reduce the total size field in the header to minimum that will fit existing content. |
| /// No more data can be added to the FDT. This should be called after all modification is |
| /// done and before passing to the kernel. This is to prevent kernel hang when FDT size is too |
| /// big. |
| pub fn shrink_to_fit(&mut self) -> Result<()> { |
| let actual = self.header_ref()?.actual_size(); |
| self.header_mut()?.set_totalsize(actual.try_into().unwrap()); |
| Ok(()) |
| } |
| |
| /// Delete node by `path``. Fail if node doesn't exist. |
| pub fn delete_node(&mut self, path: &str) -> Result<()> { |
| let node = self.find_node(path)?; |
| // SAFETY: |
| // * `self.0` is guaranteed to be a proper fdt header reference |
| // * `node` is offset of the node to delete within `self.0` fdt buffer |
| map_result(unsafe { fdt_del_node(self.0.as_mut().as_mut_ptr() as *mut _, node) })?; |
| Ok(()) |
| } |
| |
| /// Set the value of a node's property. Create the node and property if it doesn't exist. |
| pub fn set_property(&mut self, path: &str, name: &CStr, val: &[u8]) -> Result<()> { |
| let node = self.find_or_add_node(path)?; |
| // SAFETY: API from libfdt_c. |
| map_result(unsafe { |
| fdt_setprop( |
| self.0.as_mut().as_mut_ptr() as *mut _, |
| node, |
| name.to_bytes_with_nul().as_ptr() as *const _, |
| val.as_ptr() as *const _, |
| val.len().try_into().or(Err(Error::Other(None)))?, |
| ) |
| })?; |
| Ok(()) |
| } |
| |
| /// Wrapper/equivalent of fdt_setprop_placeholder. |
| /// It creates/resizes a node's property to the given size and returns the buffer for caller |
| /// to modify content. |
| pub fn set_property_placeholder( |
| &mut self, |
| path: &str, |
| name: &CStr, |
| len: usize, |
| ) -> Result<&mut [u8]> { |
| let node = self.find_or_add_node(path)?; |
| let mut out_ptr: *mut u8 = core::ptr::null_mut(); |
| // SAFETY: API from libfdt_c. |
| map_result(unsafe { |
| fdt_setprop_placeholder( |
| self.0.as_mut().as_mut_ptr() as *mut _, |
| node, |
| name.to_bytes_with_nul().as_ptr() as *const _, |
| len.try_into().or(Err(Error::Other(None)))?, |
| &mut out_ptr as *mut *mut u8 as *mut _, |
| ) |
| })?; |
| assert!(!out_ptr.is_null()); |
| // SAFETY: Buffer returned by API from libfdt_c. |
| Ok(unsafe { from_raw_parts_mut(out_ptr, len) }) |
| } |
| |
| /// Wrapper/equivalent of ufdt_apply_multioverlay. |
| /// It extend current FDT buffer by applying passed overlays. |
| pub fn multioverlay_apply(&mut self, overlays: &[&[u8]]) -> Result<()> { |
| // Avoid shrinking device tree or doing any other actions in case nothing to apply. |
| if overlays.is_empty() { |
| return Ok(()); |
| } |
| if overlays.len() > MAXIMUM_OVERLAYS_TO_APPLY { |
| return Err(Error::Other(Some(MAXIMUM_OVERLAYS_ERROR_MSG))); |
| } |
| |
| self.shrink_to_fit()?; |
| |
| // Convert input fat references into the raw pointers. |
| let pointers: ArrayVec<_, MAXIMUM_OVERLAYS_TO_APPLY> = |
| overlays.iter().map(|&slice| slice.as_ptr()).collect(); |
| |
| // SAFETY: The `ufdt_apply_multioverlay` function guarantees that `self.0` is accessed |
| // within the specified length boundaries. The `pointers` are non-null and are accessed |
| // by indexes only within the provided length. |
| map_result_libufdt(unsafe { |
| ufdt_apply_multioverlay( |
| self.0.as_mut().as_mut_ptr() as *mut _, |
| self.0.as_ref().len(), |
| pointers.as_ptr().cast(), |
| overlays.len(), |
| ) |
| })?; |
| |
| Ok(()) |
| } |
| |
| /// Find the offset of a node by a given node path. Add if node does not exist. |
| fn find_or_add_node(&mut self, path: &str) -> Result<c_int> { |
| let mut curr: c_int = 0; |
| for name in path.split('/') { |
| if name.len() == 0 { |
| continue; |
| } |
| curr = match fdt_subnode_offset(self.0.as_ref(), curr, name) { |
| Ok(v) => v, |
| _ => fdt_add_subnode(self.0.as_mut(), curr, name)?, |
| }; |
| } |
| Ok(curr) |
| } |
| } |
| |
| impl<T: AsMut<[u8]>> AsMut<[u8]> for Fdt<T> { |
| fn as_mut(&mut self) -> &mut [u8] { |
| self.0.as_mut() |
| } |
| } |
| |
| impl<T: AsRef<[u8]>> AsRef<[u8]> for Fdt<T> { |
| fn as_ref(&self) -> &[u8] { |
| self.0.as_ref() |
| } |
| } |
| |
| #[cfg(test)] |
| mod test { |
| use super::*; |
| |
| /// Checks to verify `overlay_*_by_path`/`overlay_*_by_reference` are successfully applied |
| fn check_overlays_are_applied(fdt: &[u8]) { |
| let fdt = Fdt::new(fdt).unwrap(); |
| |
| assert_eq!( |
| CStr::from_bytes_with_nul( |
| fdt.get_property("/dev-2/dev-2.2/dev-2.2.1", c"property-1").unwrap() |
| ) |
| .unwrap() |
| .to_str() |
| .unwrap(), |
| "overlay1-property-1-value", |
| "overlay_modify: failed to modify \"property-1\" in \"/dev-2/dev-2.2/dev-2.2.1\"" |
| ); |
| assert_eq!( |
| CStr::from_bytes_with_nul( |
| fdt.get_property("/dev-1/overlay1-new-node", c"overlay1-new-node-property") |
| .unwrap() |
| ) |
| .unwrap() |
| .to_str() |
| .unwrap(), |
| "overlay1-new-node-property-value", |
| "overlay_modify: failed to add \"overlay1-new-node\" to \"/dev-1\"" |
| ); |
| assert_eq!( |
| CStr::from_bytes_with_nul( |
| fdt.get_property("/dev-4", c"overlay1-root-node-property").unwrap() |
| ) |
| .unwrap() |
| .to_str() |
| .unwrap(), |
| "overlay1-root-node-property-value", |
| "overlay_modify: failed to add \"/dev-4/overlay1-root-node-property\"" |
| ); |
| assert_eq!( |
| CStr::from_bytes_with_nul( |
| fdt.get_property("/dev-2/dev-2.2/dev-2.2.1", c"overlay1-new-property").unwrap() |
| ) |
| .unwrap() |
| .to_str() |
| .unwrap(), |
| "overlay2-new-property-value", |
| "overlay_modify2: failed to modify \"overlay1-new-property\" in \"/dev-2/dev-2.2/dev-2.2.1\"" |
| ); |
| assert_eq!( |
| CStr::from_bytes_with_nul( |
| fdt.get_property("/dev-4", c"overlay2-root-node-property").unwrap() |
| ) |
| .unwrap() |
| .to_str() |
| .unwrap(), |
| "overlay2-root-node-property-value", |
| "overlay_modify2: failed to add \"overlay2-root-node-property\" to \"/dev-4\"" |
| ); |
| } |
| |
| #[test] |
| fn test_new_from_invalid_fdt() { |
| let mut init = include_bytes!("../test/data/base.dtb").to_vec(); |
| let mut fdt_buf = vec![0u8; init.len()]; |
| // Invalid total size |
| assert!(Fdt::new_from_init(&mut fdt_buf[..], &init[..init.len() - 1]).is_err()); |
| // Invalid FDT |
| init[..4].fill(0); |
| assert!(Fdt::new_from_init(&mut fdt_buf[..], &init[..]).is_err()); |
| } |
| |
| #[test] |
| fn test_get_property() { |
| let init = include_bytes!("../test/data/base.dtb").to_vec(); |
| let mut fdt_buf = vec![0u8; init.len()]; |
| let fdt = Fdt::new_from_init(&mut fdt_buf[..], &init[..]).unwrap(); |
| |
| assert_eq!( |
| CStr::from_bytes_with_nul(fdt.get_property("/", c"info").unwrap()) |
| .unwrap() |
| .to_str() |
| .unwrap(), |
| "test device tree" |
| ); |
| assert_eq!( |
| CStr::from_bytes_with_nul( |
| fdt.get_property("/dev-2/dev-2.2/dev-2.2.1", c"property-1").unwrap() |
| ) |
| .unwrap() |
| .to_str() |
| .unwrap(), |
| "dev-2.2.1-property-1" |
| ); |
| |
| // Non eixsts |
| assert!(fdt.get_property("/", c"non-existent").is_err()); |
| } |
| |
| #[test] |
| fn test_set_property() { |
| let init = include_bytes!("../test/data/base.dtb").to_vec(); |
| let mut fdt_buf = vec![0u8; init.len() + 512]; |
| let mut fdt = Fdt::new_from_init(&mut fdt_buf[..], &init[..]).unwrap(); |
| let data = vec![0x11u8, 0x22u8, 0x33u8]; |
| fdt.set_property("/new-node", c"custom", &data).unwrap(); |
| assert_eq!(fdt.get_property("/new-node", c"custom").unwrap().to_vec(), data); |
| } |
| |
| #[test] |
| fn test_delete_node() { |
| let init = include_bytes!("../test/data/base.dtb").to_vec(); |
| let mut fdt_buf = vec![0u8; init.len()]; |
| let mut fdt = Fdt::new_from_init(&mut fdt_buf[..], &init[..]).unwrap(); |
| |
| assert_eq!( |
| CStr::from_bytes_with_nul( |
| fdt.get_property("/dev-2/dev-2.2/dev-2.2.1", c"property-1").unwrap() |
| ) |
| .unwrap() |
| .to_str() |
| .unwrap(), |
| "dev-2.2.1-property-1" |
| ); |
| |
| fdt.delete_node("dev-2").unwrap(); |
| |
| assert!( |
| fdt.get_property("/dev-2/dev-2.2/dev-2.2.1", c"property-1").is_err(), |
| "dev-2.2.1-property-1 expected to be deleted" |
| ); |
| } |
| |
| #[test] |
| fn test_delete_nost_existed_node_is_failed() { |
| let init = include_bytes!("../test/data/base.dtb").to_vec(); |
| let mut fdt_buf = vec![0u8; init.len()]; |
| let mut fdt = Fdt::new_from_init(&mut fdt_buf[..], &init[..]).unwrap(); |
| |
| assert!( |
| fdt.delete_node("/non-existent").is_err(), |
| "expected failed to delete non existent node" |
| ); |
| } |
| |
| #[test] |
| fn test_set_property_placeholder() { |
| let init = include_bytes!("../test/data/base.dtb").to_vec(); |
| let mut fdt_buf = vec![0u8; init.len() + 512]; |
| let mut fdt = Fdt::new_from_init(&mut fdt_buf[..], &init[..]).unwrap(); |
| let data = vec![0x11u8, 0x22u8, 0x33u8, 0x44u8, 0x55u8]; |
| let payload = fdt.set_property_placeholder("/new-node", c"custom", data.len()).unwrap(); |
| payload.clone_from_slice(&data[..]); |
| assert_eq!(fdt.get_property("/new-node", c"custom").unwrap().to_vec(), data); |
| } |
| |
| #[test] |
| fn test_header_from_raw() { |
| let init = include_bytes!("../test/data/base.dtb").to_vec(); |
| // Pointer points to `init` |
| let (header, bytes) = unsafe { FdtHeader::from_raw(init.as_ptr()).unwrap() }; |
| assert_eq!(header.totalsize(), init.len()); |
| assert_eq!(bytes.to_vec(), init); |
| } |
| |
| #[test] |
| fn test_header_from_raw_invalid() { |
| let mut init = include_bytes!("../test/data/base.dtb").to_vec(); |
| init[..4].fill(0); |
| // Pointer points to `init` |
| assert!(unsafe { FdtHeader::from_raw(init.as_ptr()).is_err() }); |
| } |
| |
| #[test] |
| fn test_fdt_shrink_to_fit() { |
| let init = include_bytes!("../test/data/base.dtb").to_vec(); |
| let mut fdt_buf = vec![0u8; init.len() + 512]; |
| let fdt_buf_len = fdt_buf.len(); |
| let mut fdt = Fdt::new_from_init(&mut fdt_buf[..], &init[..]).unwrap(); |
| assert_eq!(fdt.size().unwrap(), fdt_buf_len); |
| fdt.shrink_to_fit().unwrap(); |
| assert_eq!(fdt.size().unwrap(), init.len()); |
| } |
| |
| #[test] |
| fn test_fdt_multioverlay_apply_by_path() { |
| let base = include_bytes!("../test/data/base.dtb").to_vec(); |
| let overlay_modify = include_bytes!("../test/data/overlay_by_path.dtbo").to_vec(); |
| let overlay_modify2 = include_bytes!("../test/data/overlay_2_by_path.dtbo").to_vec(); |
| |
| let mut fdt_buf = vec![0u8; base.len() + overlay_modify.len() + overlay_modify2.len()]; |
| let mut fdt = Fdt::new_from_init(&mut fdt_buf[..], &base[..]).unwrap(); |
| |
| fdt.multioverlay_apply(&[&overlay_modify[..] as _, &overlay_modify2[..] as _]).unwrap(); |
| fdt.shrink_to_fit().unwrap(); |
| |
| check_overlays_are_applied(fdt.0); |
| } |
| |
| #[test] |
| fn test_fdt_multioverlay_apply_by_path_separately() { |
| let base = include_bytes!("../test/data/base.dtb").to_vec(); |
| let overlay_modify = include_bytes!("../test/data/overlay_by_path.dtbo").to_vec(); |
| let overlay_modify2 = include_bytes!("../test/data/overlay_2_by_path.dtbo").to_vec(); |
| |
| let mut fdt_buf = vec![0u8; base.len() + overlay_modify.len() + overlay_modify2.len()]; |
| let mut fdt = Fdt::new_from_init(&mut fdt_buf[..], &base[..]).unwrap(); |
| |
| fdt.multioverlay_apply(&[&overlay_modify[..] as _]).unwrap(); |
| fdt.multioverlay_apply(&[&overlay_modify2[..] as _]).unwrap(); |
| fdt.shrink_to_fit().unwrap(); |
| |
| check_overlays_are_applied(fdt.0); |
| } |
| |
| // TODO(b/362486327): symbols from overlay are not added to the result tree |
| // so cannot refer to them. |
| #[ignore] |
| #[test] |
| fn test_fdt_multioverlay_apply_by_reference() { |
| let base = include_bytes!("../test/data/base.dtb").to_vec(); |
| let overlay_modify = include_bytes!("../test/data/overlay_by_reference.dtbo").to_vec(); |
| let overlay_modify2 = include_bytes!("../test/data/overlay_2_by_reference.dtbo").to_vec(); |
| |
| let mut fdt_buf = vec![0u8; base.len() + overlay_modify.len() + overlay_modify2.len()]; |
| let mut fdt = Fdt::new_from_init(&mut fdt_buf[..], &base[..]).unwrap(); |
| |
| fdt.multioverlay_apply(&[&overlay_modify[..] as _, &overlay_modify2[..] as _]).unwrap(); |
| fdt.shrink_to_fit().unwrap(); |
| |
| check_overlays_are_applied(fdt.0); |
| } |
| |
| // TODO(b/362486327): symbols from overlay are not added to the result tree |
| // so cannot refer to them. |
| #[ignore] |
| #[test] |
| fn test_fdt_multioverlay_apply_by_reference_separately() { |
| let base = include_bytes!("../test/data/base.dtb").to_vec(); |
| let overlay_modify = include_bytes!("../test/data/overlay_by_reference.dtbo").to_vec(); |
| let overlay_modify2 = include_bytes!("../test/data/overlay_2_by_reference.dtbo").to_vec(); |
| |
| let mut fdt_buf = vec![0u8; base.len() + overlay_modify.len() + overlay_modify2.len()]; |
| let mut fdt = Fdt::new_from_init(&mut fdt_buf[..], &base[..]).unwrap(); |
| |
| fdt.multioverlay_apply(&[&overlay_modify[..] as _]).unwrap(); |
| fdt.multioverlay_apply(&[&overlay_modify2[..] as _]).unwrap(); |
| fdt.shrink_to_fit().unwrap(); |
| |
| check_overlays_are_applied(fdt.0); |
| } |
| |
| #[test] |
| fn test_fdt_multioverlay_apply_not_enough_space() { |
| let init = include_bytes!("../test/data/base.dtb").to_vec(); |
| let overlay_basic = include_bytes!("../test/data/overlay_by_path.dtbo").to_vec(); |
| |
| let mut fdt_buf = vec![0u8; init.len()]; |
| let mut fdt = Fdt::new_from_init(&mut fdt_buf[..], &init[..]).unwrap(); |
| |
| assert!( |
| fdt.multioverlay_apply(&[&overlay_basic[..]]).is_err(), |
| "expected the problem is catched when not enough space in the main fdt buffer" |
| ); |
| } |
| |
| #[test] |
| fn test_fdt_multioverlay_apply_corrupted() { |
| let init = include_bytes!("../test/data/base.dtb").to_vec(); |
| let overlay_corrupted: Vec<u8> = include_bytes!("../test/data/overlay_by_path.dtbo") |
| .to_vec() |
| .iter() |
| .copied() |
| .rev() |
| .collect(); |
| |
| let mut fdt_buf = vec![0u8; init.len() + overlay_corrupted.len()]; |
| let mut fdt = Fdt::new_from_init(&mut fdt_buf[..], &init[..]).unwrap(); |
| |
| assert!( |
| fdt.multioverlay_apply(&[&overlay_corrupted[..]]).is_err(), |
| "expected the problem is catched when applying corrupted overlay" |
| ); |
| } |
| |
| #[test] |
| fn test_fdt_multioverlay_apply_with_wrong_target_path() { |
| let init = include_bytes!("../test/data/base.dtb").to_vec(); |
| let overlay_wrong_path = include_bytes!("../test/data/overlay_wrong_path.dtbo").to_vec(); |
| |
| let mut fdt_buf = vec![0u8; init.len()]; |
| let mut fdt = Fdt::new_from_init(&mut fdt_buf[..], &init[..]).unwrap(); |
| |
| assert!( |
| fdt.multioverlay_apply(&[&overlay_wrong_path[..]]).is_err(), |
| "expected the problem is catched when applying overlay with wrong target path" |
| ); |
| } |
| |
| #[test] |
| fn test_fdt_multioverlay_apply_maximum_amount_of_overlays_handled() { |
| let init = include_bytes!("../test/data/base.dtb").to_vec(); |
| let too_many_overlays = &[&[] as &[u8]; MAXIMUM_OVERLAYS_TO_APPLY + 1]; |
| |
| let mut fdt_buf = vec![0u8; init.len()]; |
| let mut fdt = Fdt::new_from_init(&mut fdt_buf[..], &init[..]).unwrap(); |
| |
| assert_eq!( |
| fdt.multioverlay_apply(too_many_overlays), |
| Err(Error::Other(Some(MAXIMUM_OVERLAYS_ERROR_MSG))), |
| "too many overlays isn't handled" |
| ); |
| } |
| } |