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