• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2023-2024, The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 //! This library provides a few wrapper APIs for libfdt_c
16 
17 #![cfg_attr(not(test), no_std)]
18 
19 extern crate alloc;
20 extern crate libc;
21 
22 use arrayvec::ArrayVec;
23 use core::ffi::{c_int, CStr};
24 use core::mem::size_of;
25 use core::slice::{from_raw_parts, from_raw_parts_mut};
26 use liberror::{Error, Result};
27 use libfdt_bindgen::{
28     fdt_add_subnode_namelen, fdt_del_node, fdt_get_property, fdt_header, fdt_move, fdt_setprop,
29     fdt_setprop_placeholder, fdt_strerror, fdt_subnode_offset_namelen,
30 };
31 use libufdt_bindgen::ufdt_apply_multioverlay;
32 use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout, Ref};
33 
34 /// Fdt header structure size.
35 pub const FDT_HEADER_SIZE: usize = size_of::<FdtHeader>();
36 const MAXIMUM_OVERLAYS_TO_APPLY: usize = 16;
37 const MAXIMUM_OVERLAYS_ERROR_MSG: &str = "At most 16 overlays are supported to apply at a time";
38 
39 /// Convert libfdt_c error code to Result
map_result(code: c_int) -> Result<c_int>40 fn map_result(code: c_int) -> Result<c_int> {
41     match code {
42         // SAFETY: Static null terminated string returned from libfdt_c API.
43         v if v < 0 => {
44             Err(Error::Other(Some(unsafe { CStr::from_ptr(fdt_strerror(v)).to_str().unwrap() })))
45         }
46         v => Ok(v),
47     }
48 }
49 
50 /// Convert libufdt_c error code to Result
map_result_libufdt(code: c_int) -> Result<c_int>51 fn map_result_libufdt(code: c_int) -> Result<c_int> {
52     match code {
53         v if v < 0 => Err(Error::Other(Some("Failed to execute libufdt call"))),
54         v => Ok(v),
55     }
56 }
57 
58 /// Check header.
fdt_check_header(header: &[u8]) -> Result<()>59 fn fdt_check_header(header: &[u8]) -> Result<()> {
60     // SAFETY:
61     // `fdt_check_header` is only access the memory pointed to by `header` during this call and
62     // not store the pointer for later use. `header` remains valid for the duration of this call.
63     map_result(unsafe { libfdt_bindgen::fdt_check_header(header.as_ptr() as *const _) })?;
64     Ok(())
65 }
66 
67 /// Check header and verified that totalsize does not exceed buffer size.
fdt_check_buffer(fdt: &[u8]) -> Result<()>68 fn fdt_check_buffer(fdt: &[u8]) -> Result<()> {
69     match FdtHeader::from_bytes_ref(fdt)?.totalsize() <= fdt.len() {
70         true => Ok(()),
71         _ => Err(Error::InvalidInput),
72     }
73 }
74 
75 /// Wrapper of fdt_add_subnode_namelen()
fdt_add_subnode(fdt: &mut [u8], parent: c_int, name: &str) -> Result<c_int>76 fn fdt_add_subnode(fdt: &mut [u8], parent: c_int, name: &str) -> Result<c_int> {
77     // SAFETY: API from libfdt_c.
78     map_result(unsafe {
79         fdt_add_subnode_namelen(
80             fdt.as_mut_ptr() as *mut _,
81             parent,
82             name.as_ptr() as *const _,
83             name.len().try_into()?,
84         )
85     })
86 }
87 
88 /// Wrapper of fdt_subnode_offset_namelen()
fdt_subnode_offset(fdt: &[u8], parent: c_int, name: &str) -> Result<c_int>89 fn fdt_subnode_offset(fdt: &[u8], parent: c_int, name: &str) -> Result<c_int> {
90     // SAFETY: API from libfdt_c.
91     map_result(unsafe {
92         fdt_subnode_offset_namelen(
93             fdt.as_ptr() as *const _,
94             parent,
95             name.as_ptr() as *const _,
96             name.len().try_into()?,
97         )
98     })
99 }
100 
101 /// Rust wrapper for the FDT header data.
102 #[repr(transparent)]
103 #[derive(Debug, Copy, Clone, Immutable, IntoBytes, KnownLayout, FromBytes, PartialEq)]
104 pub struct FdtHeader(fdt_header);
105 
106 impl FdtHeader {
107     /// Return the totalsize field.
totalsize(&self) -> usize108     pub fn totalsize(&self) -> usize {
109         u32::from_be(self.0.totalsize) as usize
110     }
111 
112     /// Return the minimal size of the FDT. Disregard trailing free space.
actual_size(&self) -> usize113     pub fn actual_size(&self) -> usize {
114         u32::from_be(self.0.off_dt_strings)
115             .checked_add(u32::from_be(self.0.size_dt_strings))
116             .unwrap() as usize
117     }
118 
119     /// Update the totalsize field.
set_totalsize(&mut self, value: u32)120     pub fn set_totalsize(&mut self, value: u32) {
121         self.0.totalsize = value.to_be();
122     }
123 
124     /// Cast a bytes into a reference of FDT header
from_bytes_ref(buffer: &[u8]) -> Result<&FdtHeader>125     pub fn from_bytes_ref(buffer: &[u8]) -> Result<&FdtHeader> {
126         fdt_check_header(buffer)?;
127 
128         Ok(Ref::into_ref(
129             Ref::<_, FdtHeader>::new_from_prefix(buffer)
130                 .ok_or(Error::BufferTooSmall(Some(FDT_HEADER_SIZE)))?
131                 .0,
132         ))
133     }
134 
135     /// Cast a bytes into a mutable reference of FDT header.
from_bytes_mut(buffer: &mut [u8]) -> Result<&mut FdtHeader>136     pub fn from_bytes_mut(buffer: &mut [u8]) -> Result<&mut FdtHeader> {
137         fdt_check_header(buffer)?;
138 
139         Ok(Ref::into_mut(
140             Ref::<_, FdtHeader>::new_from_prefix(buffer)
141                 .ok_or(Error::BufferTooSmall(Some(FDT_HEADER_SIZE)))?
142                 .0,
143         ))
144     }
145 
146     /// Get FDT header and raw bytes from a raw pointer.
147     ///
148     /// Caller should guarantee that
149     ///   1. `ptr` contains a valid FDT.
150     ///   2. The buffer remains valid as long as the returned references are in use.
from_raw(ptr: *const u8) -> Result<(&'static FdtHeader, &'static [u8])>151     pub unsafe fn from_raw(ptr: *const u8) -> Result<(&'static FdtHeader, &'static [u8])> {
152         // SAFETY: By safety requirement of this function, `ptr` points to a valid FDT and remains
153         // valid when in use.
154         unsafe {
155             let header_bytes = from_raw_parts(ptr, FDT_HEADER_SIZE);
156             let header = Self::from_bytes_ref(header_bytes)?;
157             Ok((header, from_raw_parts(ptr, header.totalsize())))
158         }
159     }
160 }
161 
162 /// Object for managing an FDT.
163 pub struct Fdt<T>(T);
164 
165 /// Read only APIs.
166 impl<'a, T: AsRef<[u8]> + 'a> Fdt<T> {
167     /// Creates a new [Fdt] wrapping the contents of `init`.
new(init: T) -> Result<Self>168     pub fn new(init: T) -> Result<Self> {
169         fdt_check_buffer(init.as_ref())?;
170         Ok(Fdt(init))
171     }
172 
173     /// Returns the [FdtHeader], or an error if the underlying buffer was invalid.
header_ref(&self) -> Result<&FdtHeader>174     pub fn header_ref(&self) -> Result<&FdtHeader> {
175         FdtHeader::from_bytes_ref(self.0.as_ref())
176     }
177 
178     /// Returns the totalsize according to FDT header. Trailing free space is included.
size(&self) -> Result<usize>179     pub fn size(&self) -> Result<usize> {
180         Ok(self.header_ref()?.totalsize())
181     }
182 
183     /// Get a property from an existing node.
get_property(&self, path: &str, name: &CStr) -> Result<&'a [u8]>184     pub fn get_property(&self, path: &str, name: &CStr) -> Result<&'a [u8]> {
185         let node = self.find_node(path)?;
186         let mut len: c_int = 0;
187         // SAFETY: API from libfdt_c.
188         let ptr = unsafe {
189             fdt_get_property(
190                 self.0.as_ref().as_ptr() as *const _,
191                 node,
192                 name.to_bytes_with_nul().as_ptr() as *const _,
193                 &mut len as *mut _,
194             )
195         };
196         // SAFETY: Buffer returned by API from libfdt_c.
197         match unsafe { ptr.as_ref() } {
198             // SAFETY: Buffer returned by API from libfdt_c.
199             Some(v) => Ok(unsafe {
200                 from_raw_parts(
201                     v.data.as_ptr() as *const u8,
202                     u32::from_be(v.len).try_into().or(Err(Error::Other(None)))?,
203                 )
204             }),
205             _ => Err(map_result(len).unwrap_err()),
206         }
207     }
208 
209     /// Find the offset of a node by a given node path.
find_node(&self, path: &str) -> Result<c_int>210     fn find_node(&self, path: &str) -> Result<c_int> {
211         let mut curr: c_int = 0;
212         for name in path.split('/') {
213             if name.len() == 0 {
214                 continue;
215             }
216             curr = fdt_subnode_offset(self.0.as_ref(), curr, name)?;
217         }
218         Ok(curr)
219     }
220 }
221 
222 /// APIs when data can be modified.
223 impl<T: AsMut<[u8]> + AsRef<[u8]>> Fdt<T> {
224     /// Creates a new mut [Fdt] wrapping the contents of `init`.
new_mut(init: T) -> Result<Self>225     pub fn new_mut(init: T) -> Result<Self> {
226         let mut fdt = Fdt::new(init)?;
227         fdt.expand_to_buffer()?;
228         Ok(fdt)
229     }
230 
231     /// Creates a mutable [Fdt] copied from `init`.
new_from_init(mut fdt: T, init: &[u8]) -> Result<Self>232     pub fn new_from_init(mut fdt: T, init: &[u8]) -> Result<Self> {
233         fdt_check_buffer(init)?;
234         // SAFETY: API from libfdt_c.
235         map_result(unsafe {
236             fdt_move(
237                 init.as_ptr() as *const _,
238                 fdt.as_mut().as_ptr() as *mut _,
239                 fdt.as_mut().len().try_into().or(Err(Error::Other(None)))?,
240             )
241         })?;
242         let mut ret = Fdt::new(fdt)?;
243         ret.expand_to_buffer()?;
244         Ok(ret)
245     }
246 
247     /// Parse and get the FDT header.
header_mut(&mut self) -> Result<&mut FdtHeader>248     fn header_mut(&mut self) -> Result<&mut FdtHeader> {
249         FdtHeader::from_bytes_mut(self.0.as_mut())
250     }
251 
252     /// Reduce the total size field in the header to minimum that will fit existing content.
253     /// No more data can be added to the FDT. This should be called after all modification is
254     /// done and before passing to the kernel. This is to prevent kernel hang when FDT size is too
255     /// big.
shrink_to_fit(&mut self) -> Result<()>256     pub fn shrink_to_fit(&mut self) -> Result<()> {
257         let actual = self.header_ref()?.actual_size();
258         self.header_mut()?.set_totalsize(actual.try_into().unwrap());
259         Ok(())
260     }
261 
262     /// Expand the total size field in the header to match the full buffer size.
263     /// This allows the FDT to be modified further by ensuring sufficient space is available.
264     /// Typically used before making modifications to an existing FDT, especially if it was
265     /// previously shrunk. After modifications are complete, consider calling `shrink_to_fit`
266     /// to reduce the size before passing to the kernel.
expand_to_buffer(&mut self) -> Result<()>267     pub fn expand_to_buffer(&mut self) -> Result<()> {
268         let buffer_size = self.0.as_ref().len().try_into().unwrap();
269         self.header_mut()?.set_totalsize(buffer_size);
270         Ok(())
271     }
272 
273     /// Delete node by `path``. Fail if node doesn't exist.
delete_node(&mut self, path: &str) -> Result<()>274     pub fn delete_node(&mut self, path: &str) -> Result<()> {
275         let node = self.find_node(path)?;
276         // SAFETY:
277         // * `self.0` is guaranteed to be a proper fdt header reference
278         // * `node` is offset of the node to delete within `self.0` fdt buffer
279         map_result(unsafe { fdt_del_node(self.0.as_mut().as_mut_ptr() as *mut _, node) })?;
280         Ok(())
281     }
282 
283     /// Set the value of a node's property. Create the node and property if it doesn't exist.
set_property(&mut self, path: &str, name: &CStr, val: &[u8]) -> Result<()>284     pub fn set_property(&mut self, path: &str, name: &CStr, val: &[u8]) -> Result<()> {
285         let node = self.find_or_add_node(path)?;
286         // SAFETY: API from libfdt_c.
287         map_result(unsafe {
288             fdt_setprop(
289                 self.0.as_mut().as_mut_ptr() as *mut _,
290                 node,
291                 name.to_bytes_with_nul().as_ptr() as *const _,
292                 val.as_ptr() as *const _,
293                 val.len().try_into().or(Err(Error::Other(None)))?,
294             )
295         })?;
296         Ok(())
297     }
298 
299     /// Wrapper/equivalent of fdt_setprop_placeholder.
300     /// It creates/resizes a node's property to the given size and returns the buffer for caller
301     /// to modify content.
set_property_placeholder( &mut self, path: &str, name: &CStr, len: usize, ) -> Result<&mut [u8]>302     pub fn set_property_placeholder(
303         &mut self,
304         path: &str,
305         name: &CStr,
306         len: usize,
307     ) -> Result<&mut [u8]> {
308         let node = self.find_or_add_node(path)?;
309         let mut out_ptr: *mut u8 = core::ptr::null_mut();
310         // SAFETY: API from libfdt_c.
311         map_result(unsafe {
312             fdt_setprop_placeholder(
313                 self.0.as_mut().as_mut_ptr() as *mut _,
314                 node,
315                 name.to_bytes_with_nul().as_ptr() as *const _,
316                 len.try_into().or(Err(Error::Other(None)))?,
317                 &mut out_ptr as *mut *mut u8 as *mut _,
318             )
319         })?;
320         assert!(!out_ptr.is_null());
321         // SAFETY: Buffer returned by API from libfdt_c.
322         Ok(unsafe { from_raw_parts_mut(out_ptr, len) })
323     }
324 
325     /// Wrapper/equivalent of ufdt_apply_multioverlay.
326     /// It extend current FDT buffer by applying passed overlays.
multioverlay_apply(&mut self, overlays: &[&[u8]]) -> Result<()>327     pub fn multioverlay_apply(&mut self, overlays: &[&[u8]]) -> Result<()> {
328         // Avoid shrinking device tree or doing any other actions in case nothing to apply.
329         if overlays.is_empty() {
330             return Ok(());
331         }
332         if overlays.len() > MAXIMUM_OVERLAYS_TO_APPLY {
333             return Err(Error::Other(Some(MAXIMUM_OVERLAYS_ERROR_MSG)));
334         }
335 
336         self.shrink_to_fit()?;
337 
338         // Convert input fat references into the raw pointers.
339         let pointers: ArrayVec<_, MAXIMUM_OVERLAYS_TO_APPLY> =
340             overlays.iter().map(|&slice| slice.as_ptr()).collect();
341 
342         // SAFETY: The `ufdt_apply_multioverlay` function guarantees that `self.0` is accessed
343         // within the specified length boundaries. The `pointers` are non-null and are accessed
344         // by indexes only within the provided length.
345         map_result_libufdt(unsafe {
346             ufdt_apply_multioverlay(
347                 self.0.as_mut().as_mut_ptr() as *mut _,
348                 self.0.as_ref().len(),
349                 pointers.as_ptr().cast(),
350                 overlays.len(),
351             )
352         })?;
353 
354         self.expand_to_buffer()?;
355 
356         Ok(())
357     }
358 
359     /// Find the offset of a node by a given node path. Add if node does not exist.
find_or_add_node(&mut self, path: &str) -> Result<c_int>360     fn find_or_add_node(&mut self, path: &str) -> Result<c_int> {
361         let mut curr: c_int = 0;
362         for name in path.split('/') {
363             if name.len() == 0 {
364                 continue;
365             }
366             curr = match fdt_subnode_offset(self.0.as_ref(), curr, name) {
367                 Ok(v) => v,
368                 _ => fdt_add_subnode(self.0.as_mut(), curr, name)?,
369             };
370         }
371         Ok(curr)
372     }
373 }
374 
375 impl<T: AsMut<[u8]>> AsMut<[u8]> for Fdt<T> {
as_mut(&mut self) -> &mut [u8]376     fn as_mut(&mut self) -> &mut [u8] {
377         self.0.as_mut()
378     }
379 }
380 
381 impl<T: AsRef<[u8]>> AsRef<[u8]> for Fdt<T> {
as_ref(&self) -> &[u8]382     fn as_ref(&self) -> &[u8] {
383         self.0.as_ref()
384     }
385 }
386 
387 #[cfg(test)]
388 mod test {
389     extern crate libc_deps_posix;
390 
391     use super::*;
392 
393     // Fdt is required to be 8 bytes aligned. Buffer to test alignment-related logic.
394     #[repr(align(8))]
395     struct AlignedBytes<const N: usize>([u8; N]);
396 
397     /// Checks to verify `overlay_*_by_path`/`overlay_*_by_reference` are successfully applied
check_overlays_are_applied(fdt: &[u8])398     fn check_overlays_are_applied(fdt: &[u8]) {
399         let fdt = Fdt::new(fdt).unwrap();
400 
401         assert_eq!(fdt.header_ref().unwrap().totalsize(), fdt.as_ref().len());
402         assert_eq!(
403             CStr::from_bytes_with_nul(
404                 fdt.get_property("/dev-2/dev-2.2/dev-2.2.1", c"property-1").unwrap()
405             )
406             .unwrap()
407             .to_str()
408             .unwrap(),
409             "overlay1-property-1-value",
410             "overlay_modify: failed to modify \"property-1\" in \"/dev-2/dev-2.2/dev-2.2.1\""
411         );
412         assert_eq!(
413             CStr::from_bytes_with_nul(
414                 fdt.get_property("/dev-1/overlay1-new-node", c"overlay1-new-node-property")
415                     .unwrap()
416             )
417             .unwrap()
418             .to_str()
419             .unwrap(),
420             "overlay1-new-node-property-value",
421             "overlay_modify: failed to add \"overlay1-new-node\" to \"/dev-1\""
422         );
423         assert_eq!(
424             CStr::from_bytes_with_nul(
425                 fdt.get_property("/dev-4", c"overlay1-root-node-property").unwrap()
426             )
427             .unwrap()
428             .to_str()
429             .unwrap(),
430             "overlay1-root-node-property-value",
431             "overlay_modify: failed to add \"/dev-4/overlay1-root-node-property\""
432         );
433         assert_eq!(
434             CStr::from_bytes_with_nul(
435                 fdt.get_property("/dev-2/dev-2.2/dev-2.2.1", c"overlay1-new-property").unwrap()
436             )
437             .unwrap()
438             .to_str()
439             .unwrap(),
440             "overlay2-new-property-value",
441             "overlay_modify2: failed to modify \"overlay1-new-property\" in \"/dev-2/dev-2.2/dev-2.2.1\""
442         );
443         assert_eq!(
444             CStr::from_bytes_with_nul(
445                 fdt.get_property("/dev-4", c"overlay2-root-node-property").unwrap()
446             )
447             .unwrap()
448             .to_str()
449             .unwrap(),
450             "overlay2-root-node-property-value",
451             "overlay_modify2: failed to add \"overlay2-root-node-property\" to \"/dev-4\""
452         );
453     }
454 
455     #[test]
test_new_from_invalid_fdt()456     fn test_new_from_invalid_fdt() {
457         let mut init = include_bytes!("../test/data/base.dtb").to_vec();
458         let mut fdt_buf = vec![0u8; init.len()];
459         // Invalid total size
460         assert!(Fdt::new_from_init(&mut fdt_buf[..], &init[..init.len() - 1]).is_err());
461         // Invalid FDT
462         init[..4].fill(0);
463         assert!(Fdt::new_from_init(&mut fdt_buf[..], &init[..]).is_err());
464     }
465 
466     #[test]
test_get_property()467     fn test_get_property() {
468         let init = include_bytes!("../test/data/base.dtb").to_vec();
469         let mut fdt_buf = vec![0u8; init.len()];
470         let fdt = Fdt::new_from_init(&mut fdt_buf[..], &init[..]).unwrap();
471 
472         assert_eq!(
473             CStr::from_bytes_with_nul(fdt.get_property("/", c"info").unwrap())
474                 .unwrap()
475                 .to_str()
476                 .unwrap(),
477             "test device tree"
478         );
479         assert_eq!(
480             CStr::from_bytes_with_nul(
481                 fdt.get_property("/dev-2/dev-2.2/dev-2.2.1", c"property-1").unwrap()
482             )
483             .unwrap()
484             .to_str()
485             .unwrap(),
486             "dev-2.2.1-property-1"
487         );
488 
489         // Non eixsts
490         assert!(fdt.get_property("/", c"non-existent").is_err());
491     }
492 
493     #[test]
test_set_property()494     fn test_set_property() {
495         let init = include_bytes!("../test/data/base.dtb").to_vec();
496         let mut fdt_buf = vec![0u8; init.len() + 512];
497         let mut fdt = Fdt::new_from_init(&mut fdt_buf[..], &init[..]).unwrap();
498         let data = vec![0x11u8, 0x22u8, 0x33u8];
499         fdt.set_property("/new-node", c"custom", &data).unwrap();
500         assert_eq!(fdt.get_property("/new-node", c"custom").unwrap().to_vec(), data);
501     }
502 
503     #[test]
test_delete_node()504     fn test_delete_node() {
505         let init = include_bytes!("../test/data/base.dtb").to_vec();
506         let mut fdt_buf = vec![0u8; init.len()];
507         let mut fdt = Fdt::new_from_init(&mut fdt_buf[..], &init[..]).unwrap();
508 
509         assert_eq!(
510             CStr::from_bytes_with_nul(
511                 fdt.get_property("/dev-2/dev-2.2/dev-2.2.1", c"property-1").unwrap()
512             )
513             .unwrap()
514             .to_str()
515             .unwrap(),
516             "dev-2.2.1-property-1"
517         );
518 
519         fdt.delete_node("dev-2").unwrap();
520 
521         assert!(
522             fdt.get_property("/dev-2/dev-2.2/dev-2.2.1", c"property-1").is_err(),
523             "dev-2.2.1-property-1 expected to be deleted"
524         );
525     }
526 
527     #[test]
test_delete_nost_existed_node_is_failed()528     fn test_delete_nost_existed_node_is_failed() {
529         let init = include_bytes!("../test/data/base.dtb").to_vec();
530         let mut fdt_buf = vec![0u8; init.len()];
531         let mut fdt = Fdt::new_from_init(&mut fdt_buf[..], &init[..]).unwrap();
532 
533         assert!(
534             fdt.delete_node("/non-existent").is_err(),
535             "expected failed to delete non existent node"
536         );
537     }
538 
539     #[test]
test_set_property_placeholder()540     fn test_set_property_placeholder() {
541         let init = include_bytes!("../test/data/base.dtb").to_vec();
542         let mut fdt_buf = vec![0u8; init.len() + 512];
543         let mut fdt = Fdt::new_from_init(&mut fdt_buf[..], &init[..]).unwrap();
544         let data = vec![0x11u8, 0x22u8, 0x33u8, 0x44u8, 0x55u8];
545         let payload = fdt.set_property_placeholder("/new-node", c"custom", data.len()).unwrap();
546         payload.clone_from_slice(&data[..]);
547         assert_eq!(fdt.get_property("/new-node", c"custom").unwrap().to_vec(), data);
548     }
549 
550     #[test]
test_header_from_bytes()551     fn test_header_from_bytes() {
552         let init = include_bytes!("../test/data/base.dtb").to_vec();
553         let header = FdtHeader::from_bytes_ref(&init[..]).unwrap();
554 
555         assert_eq!(header.totalsize(), init.len());
556     }
557 
558     #[test]
test_header_from_bytes_wrong_alignment()559     fn test_header_from_bytes_wrong_alignment() {
560         let init = include_bytes!("../test/data/base.dtb").to_vec();
561 
562         const HEADER_SIZE: usize = size_of::<FdtHeader>();
563         let mut bytes = AlignedBytes([0u8; HEADER_SIZE + 1]);
564 
565         // Guaranteed not to be 8 bytes aligned.
566         let (_, unaligned) = bytes.0.split_at_mut(1);
567         unaligned.copy_from_slice(&init[..HEADER_SIZE]);
568 
569         assert!(FdtHeader::from_bytes_ref(unaligned).is_err());
570     }
571 
572     #[test]
test_header_from_raw()573     fn test_header_from_raw() {
574         let init = include_bytes!("../test/data/base.dtb").to_vec();
575         // Pointer points to `init`
576         let (header, bytes) = unsafe { FdtHeader::from_raw(init.as_ptr()).unwrap() };
577         assert_eq!(header.totalsize(), init.len());
578         assert_eq!(bytes.to_vec(), init);
579     }
580 
581     #[test]
test_header_from_raw_invalid()582     fn test_header_from_raw_invalid() {
583         let mut init = include_bytes!("../test/data/base.dtb").to_vec();
584         init[..4].fill(0);
585         // Pointer points to `init`
586         assert!(unsafe { FdtHeader::from_raw(init.as_ptr()).is_err() });
587     }
588 
589     #[test]
test_fdt_shrink_to_fit()590     fn test_fdt_shrink_to_fit() {
591         let init = include_bytes!("../test/data/base.dtb").to_vec();
592         let mut fdt_buf = vec![0u8; init.len() + 512];
593         let fdt_buf_len = fdt_buf.len();
594         let mut fdt = Fdt::new_from_init(&mut fdt_buf[..], &init[..]).unwrap();
595         assert_eq!(fdt.size().unwrap(), fdt_buf_len);
596         fdt.shrink_to_fit().unwrap();
597         assert_eq!(fdt.size().unwrap(), init.len());
598     }
599 
600     #[test]
test_fdt_multioverlay_apply_by_path()601     fn test_fdt_multioverlay_apply_by_path() {
602         let base = include_bytes!("../test/data/base.dtb").to_vec();
603         let overlay_modify = include_bytes!("../test/data/overlay_by_path.dtbo").to_vec();
604         let overlay_modify2 = include_bytes!("../test/data/overlay_2_by_path.dtbo").to_vec();
605 
606         let mut fdt_buf = vec![0u8; base.len() + overlay_modify.len() + overlay_modify2.len()];
607         let mut fdt = Fdt::new_from_init(&mut fdt_buf[..], &base[..]).unwrap();
608 
609         fdt.multioverlay_apply(&[&overlay_modify[..] as _, &overlay_modify2[..] as _]).unwrap();
610 
611         check_overlays_are_applied(fdt.0);
612     }
613 
614     #[test]
test_fdt_multioverlay_apply_by_path_separately()615     fn test_fdt_multioverlay_apply_by_path_separately() {
616         let base = include_bytes!("../test/data/base.dtb").to_vec();
617         let overlay_modify = include_bytes!("../test/data/overlay_by_path.dtbo").to_vec();
618         let overlay_modify2 = include_bytes!("../test/data/overlay_2_by_path.dtbo").to_vec();
619 
620         let mut fdt_buf = vec![0u8; base.len() + overlay_modify.len() + overlay_modify2.len()];
621         let mut fdt = Fdt::new_from_init(&mut fdt_buf[..], &base[..]).unwrap();
622 
623         fdt.multioverlay_apply(&[&overlay_modify[..] as _]).unwrap();
624         fdt.multioverlay_apply(&[&overlay_modify2[..] as _]).unwrap();
625 
626         check_overlays_are_applied(fdt.0);
627     }
628 
629     // TODO(b/362486327): symbols from overlay are not added to the result tree
630     // so cannot refer to them.
631     #[ignore]
632     #[test]
test_fdt_multioverlay_apply_by_reference()633     fn test_fdt_multioverlay_apply_by_reference() {
634         let base = include_bytes!("../test/data/base.dtb").to_vec();
635         let overlay_modify = include_bytes!("../test/data/overlay_by_reference.dtbo").to_vec();
636         let overlay_modify2 = include_bytes!("../test/data/overlay_2_by_reference.dtbo").to_vec();
637 
638         let mut fdt_buf = vec![0u8; base.len() + overlay_modify.len() + overlay_modify2.len()];
639         let mut fdt = Fdt::new_from_init(&mut fdt_buf[..], &base[..]).unwrap();
640 
641         fdt.multioverlay_apply(&[&overlay_modify[..] as _, &overlay_modify2[..] as _]).unwrap();
642 
643         check_overlays_are_applied(fdt.0);
644     }
645 
646     // TODO(b/362486327): symbols from overlay are not added to the result tree
647     // so cannot refer to them.
648     #[ignore]
649     #[test]
test_fdt_multioverlay_apply_by_reference_separately()650     fn test_fdt_multioverlay_apply_by_reference_separately() {
651         let base = include_bytes!("../test/data/base.dtb").to_vec();
652         let overlay_modify = include_bytes!("../test/data/overlay_by_reference.dtbo").to_vec();
653         let overlay_modify2 = include_bytes!("../test/data/overlay_2_by_reference.dtbo").to_vec();
654 
655         let mut fdt_buf = vec![0u8; base.len() + overlay_modify.len() + overlay_modify2.len()];
656         let mut fdt = Fdt::new_from_init(&mut fdt_buf[..], &base[..]).unwrap();
657 
658         fdt.multioverlay_apply(&[&overlay_modify[..] as _]).unwrap();
659         fdt.multioverlay_apply(&[&overlay_modify2[..] as _]).unwrap();
660 
661         check_overlays_are_applied(fdt.0);
662     }
663 
664     #[test]
test_fdt_multioverlay_apply_not_enough_space()665     fn test_fdt_multioverlay_apply_not_enough_space() {
666         let init = include_bytes!("../test/data/base.dtb").to_vec();
667         let overlay_basic = include_bytes!("../test/data/overlay_by_path.dtbo").to_vec();
668 
669         let mut fdt_buf = vec![0u8; init.len()];
670         let mut fdt = Fdt::new_from_init(&mut fdt_buf[..], &init[..]).unwrap();
671 
672         assert!(
673             fdt.multioverlay_apply(&[&overlay_basic[..]]).is_err(),
674             "expected the problem is catched when not enough space in the main fdt buffer"
675         );
676     }
677 
678     #[test]
test_fdt_multioverlay_apply_corrupted()679     fn test_fdt_multioverlay_apply_corrupted() {
680         let init = include_bytes!("../test/data/base.dtb").to_vec();
681         let overlay_corrupted: Vec<u8> = include_bytes!("../test/data/overlay_by_path.dtbo")
682             .to_vec()
683             .iter()
684             .copied()
685             .rev()
686             .collect();
687 
688         let mut fdt_buf = vec![0u8; init.len() + overlay_corrupted.len()];
689         let mut fdt = Fdt::new_from_init(&mut fdt_buf[..], &init[..]).unwrap();
690 
691         assert!(
692             fdt.multioverlay_apply(&[&overlay_corrupted[..]]).is_err(),
693             "expected the problem is catched when applying corrupted overlay"
694         );
695     }
696 
697     #[test]
test_fdt_multioverlay_apply_with_wrong_target_path()698     fn test_fdt_multioverlay_apply_with_wrong_target_path() {
699         let init = include_bytes!("../test/data/base.dtb").to_vec();
700         let overlay_wrong_path = include_bytes!("../test/data/overlay_wrong_path.dtbo").to_vec();
701 
702         let mut fdt_buf = vec![0u8; init.len()];
703         let mut fdt = Fdt::new_from_init(&mut fdt_buf[..], &init[..]).unwrap();
704 
705         assert!(
706             fdt.multioverlay_apply(&[&overlay_wrong_path[..]]).is_err(),
707             "expected the problem is catched when applying overlay with wrong target path"
708         );
709     }
710 
711     #[test]
test_fdt_multioverlay_apply_maximum_amount_of_overlays_handled()712     fn test_fdt_multioverlay_apply_maximum_amount_of_overlays_handled() {
713         let init = include_bytes!("../test/data/base.dtb").to_vec();
714         let too_many_overlays = &[&[] as &[u8]; MAXIMUM_OVERLAYS_TO_APPLY + 1];
715 
716         let mut fdt_buf = vec![0u8; init.len()];
717         let mut fdt = Fdt::new_from_init(&mut fdt_buf[..], &init[..]).unwrap();
718 
719         assert_eq!(
720             fdt.multioverlay_apply(too_many_overlays),
721             Err(Error::Other(Some(MAXIMUM_OVERLAYS_ERROR_MSG))),
722             "too many overlays isn't handled"
723         );
724     }
725 }
726