1 // Copyright 2023, 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 libc;
20 
21 use core::ffi::CStr;
22 use core::mem::size_of;
23 use core::slice::{from_raw_parts, from_raw_parts_mut};
24 
25 use libfdt_c_def::{
26     fdt_add_subnode_namelen, fdt_header, fdt_setprop, fdt_setprop_placeholder, fdt_strerror,
27     fdt_subnode_offset_namelen,
28 };
29 
30 use zerocopy::{AsBytes, FromBytes, FromZeroes, Ref};
31 
32 /// libfdt error type.
33 #[derive(Debug)]
34 pub enum FdtError {
35     CLibError(&'static str),
36     InvalidInput,
37     IntegerOverflow,
38 }
39 
40 /// libfdt result type,
41 pub type Result<T> = core::result::Result<T, FdtError>;
42 
43 /// Convert libfdt_c error code to Result
map_result(code: core::ffi::c_int) -> Result<core::ffi::c_int>44 fn map_result(code: core::ffi::c_int) -> Result<core::ffi::c_int> {
45     match code {
46         // SAFETY: Static null terminated string returned from libfdt_c API.
47         v if v < 0 => Err(FdtError::CLibError(unsafe {
48             core::ffi::CStr::from_ptr(fdt_strerror(v)).to_str().unwrap()
49         })),
50         v => Ok(v),
51     }
52 }
53 
54 /// Check header and verified that totalsize does not exceed buffer size.
fdt_check_header(fdt: &[u8]) -> Result<()>55 fn fdt_check_header(fdt: &[u8]) -> Result<()> {
56     map_result(unsafe { libfdt_c_def::fdt_check_header(fdt.as_ptr() as *const _) })?;
57     match FdtHeader::from_bytes_ref(fdt)?.totalsize() <= fdt.len() {
58         true => Ok(()),
59         _ => Err(FdtError::InvalidInput),
60     }
61 }
62 
63 /// Wrapper of fdt_add_subnode_namelen()
fdt_add_subnode( fdt: &mut [u8], parent: core::ffi::c_int, name: &str, ) -> Result<core::ffi::c_int>64 fn fdt_add_subnode(
65     fdt: &mut [u8],
66     parent: core::ffi::c_int,
67     name: &str,
68 ) -> Result<core::ffi::c_int> {
69     // SAFETY: API from libfdt_c.
70     map_result(unsafe {
71         fdt_add_subnode_namelen(
72             fdt.as_mut_ptr() as *mut _,
73             parent,
74             name.as_ptr() as *const _,
75             name.len().try_into().map_err(|_| FdtError::IntegerOverflow)?,
76         )
77     })
78 }
79 
80 /// Wrapper of fdt_subnode_offset_namelen()
fdt_subnode_offset( fdt: &[u8], parent: core::ffi::c_int, name: &str, ) -> Result<core::ffi::c_int>81 fn fdt_subnode_offset(
82     fdt: &[u8],
83     parent: core::ffi::c_int,
84     name: &str,
85 ) -> Result<core::ffi::c_int> {
86     // SAFETY: API from libfdt_c.
87     map_result(unsafe {
88         fdt_subnode_offset_namelen(
89             fdt.as_ptr() as *const _,
90             parent,
91             name.as_ptr() as *const _,
92             name.len().try_into().map_err(|_| FdtError::IntegerOverflow)?,
93         )
94     })
95 }
96 
97 #[repr(transparent)]
98 #[derive(Debug, Copy, Clone, AsBytes, FromBytes, FromZeroes, PartialEq)]
99 pub struct FdtHeader(fdt_header);
100 
101 impl FdtHeader {
102     /// Return the totalsize field.
totalsize(&self) -> usize103     pub fn totalsize(&self) -> usize {
104         u32::from_be(self.0.totalsize) as usize
105     }
106 
107     /// Return the minimal size of the FDT. Disregard trailing free space.
actual_size(&self) -> usize108     pub fn actual_size(&self) -> usize {
109         u32::from_be(self.0.off_dt_strings)
110             .checked_add(u32::from_be(self.0.size_dt_strings))
111             .unwrap() as usize
112     }
113 
114     /// Update the totalsize field.
set_totalsize(&mut self, value: u32)115     pub fn set_totalsize(&mut self, value: u32) {
116         self.0.totalsize = value.to_be();
117     }
118 
119     /// Cast a bytes into a reference of FDT header
from_bytes_ref(buffer: &[u8]) -> Result<&FdtHeader>120     pub fn from_bytes_ref(buffer: &[u8]) -> Result<&FdtHeader> {
121         Ok(Ref::<_, FdtHeader>::new_from_prefix(buffer)
122             .ok_or_else(|| FdtError::InvalidInput)?
123             .0
124             .into_ref())
125     }
126 
127     /// Cast a bytes into a mutable reference of FDT header.
from_bytes_mut(buffer: &mut [u8]) -> Result<&mut FdtHeader>128     pub fn from_bytes_mut(buffer: &mut [u8]) -> Result<&mut FdtHeader> {
129         Ok(Ref::<_, FdtHeader>::new_from_prefix(buffer)
130             .ok_or_else(|| FdtError::InvalidInput)?
131             .0
132             .into_mut())
133     }
134 
135     /// Get FDT header and raw bytes from a raw pointer.
136     ///
137     /// Caller should guarantee that
138     ///   1. `ptr` contains a valid FDT.
139     ///   2. The buffer remains valid as long as the returned references are in use.
from_raw(ptr: *const u8) -> Result<(&'static FdtHeader, &'static [u8])>140     pub unsafe fn from_raw(ptr: *const u8) -> Result<(&'static FdtHeader, &'static [u8])> {
141         // SAFETY: By safety requirement of this function, `ptr` points to a valid FDT and remains
142         // valid when in use.
143         unsafe {
144             map_result(libfdt_c_def::fdt_check_header(ptr as *const _))?;
145             let header_bytes = from_raw_parts(ptr, size_of::<FdtHeader>());
146             let header = Self::from_bytes_ref(header_bytes)?;
147             Ok((header, from_raw_parts(ptr, header.totalsize())))
148         }
149     }
150 }
151 
152 /// Object for managing an FDT.
153 pub struct Fdt<T>(T);
154 
155 /// Read only APIs.
156 impl<T: AsRef<[u8]>> Fdt<T> {
new(init: T) -> Result<Self>157     pub fn new(init: T) -> Result<Self> {
158         fdt_check_header(init.as_ref())?;
159         Ok(Fdt(init))
160     }
161 
header_ref(&self) -> Result<&FdtHeader>162     pub fn header_ref(&self) -> Result<&FdtHeader> {
163         FdtHeader::from_bytes_ref(self.0.as_ref())
164     }
165 
166     /// Returns the totalsize according to FDT header. Trailing free space is included.
size(&self) -> Result<usize>167     pub fn size(&self) -> Result<usize> {
168         Ok(self.header_ref()?.totalsize())
169     }
170 
171     /// Get a property from an existing node.
get_property<'a>(&'a self, path: &str, name: &CStr) -> Result<&'a [u8]>172     pub fn get_property<'a>(&'a self, path: &str, name: &CStr) -> Result<&'a [u8]> {
173         let node = self.find_node(path)?;
174         let mut len: core::ffi::c_int = 0;
175         // SAFETY: API from libfdt_c.
176         let ptr = unsafe {
177             libfdt_c_def::fdt_get_property(
178                 self.0.as_ref().as_ptr() as *const _,
179                 node,
180                 name.to_bytes_with_nul().as_ptr() as *const _,
181                 &mut len as *mut _,
182             )
183         };
184         // SAFETY: Buffer returned by API from libfdt_c.
185         match unsafe { ptr.as_ref() } {
186             // SAFETY: Buffer returned by API from libfdt_c.
187             Some(v) => Ok(unsafe {
188                 from_raw_parts(
189                     v.data.as_ptr() as *const u8,
190                     u32::from_be(v.len).try_into().map_err(|_| FdtError::IntegerOverflow)?,
191                 )
192             }),
193             _ => Err(map_result(len).unwrap_err()),
194         }
195     }
196 
197     /// Find the offset of a node by a given node path.
find_node(&self, path: &str) -> Result<core::ffi::c_int>198     fn find_node(&self, path: &str) -> Result<core::ffi::c_int> {
199         let mut curr: core::ffi::c_int = 0;
200         for name in path.split('/') {
201             if name.len() == 0 {
202                 continue;
203             }
204             curr = fdt_subnode_offset(self.0.as_ref(), curr, name)?;
205         }
206         Ok(curr)
207     }
208 }
209 
210 /// APIs when data can be modified.
211 impl<T: AsMut<[u8]> + AsRef<[u8]>> Fdt<T> {
new_from_init(mut fdt: T, init: &[u8]) -> Result<Self>212     pub fn new_from_init(mut fdt: T, init: &[u8]) -> Result<Self> {
213         fdt_check_header(init)?;
214         // SAFETY: API from libfdt_c.
215         map_result(unsafe {
216             libfdt_c_def::fdt_move(
217                 init.as_ptr() as *const _,
218                 fdt.as_mut().as_ptr() as *mut _,
219                 fdt.as_mut().len().try_into().map_err(|_| FdtError::IntegerOverflow)?,
220             )
221         })?;
222         let new_size: u32 = fdt.as_mut().len().try_into().map_err(|_| FdtError::IntegerOverflow)?;
223         let mut ret = Fdt::new(fdt)?;
224         ret.header_mut()?.set_totalsize(new_size);
225         Ok(ret)
226     }
227 
228     /// Parse and get the FDT header.
header_mut(&mut self) -> Result<&mut FdtHeader>229     fn header_mut(&mut self) -> Result<&mut FdtHeader> {
230         FdtHeader::from_bytes_mut(self.0.as_mut())
231     }
232 
233     /// Reduce the total size field in the header to minimum that will fit existing content.
234     /// No more data can be added to the FDT. This should be called after all modification is
235     /// done and before passing to the kernel. This is to prevent kernel hang when FDT size is too
236     /// big.
shrink_to_fit(&mut self) -> Result<()>237     pub fn shrink_to_fit(&mut self) -> Result<()> {
238         let actual = self.header_ref()?.actual_size();
239         self.header_mut()?.set_totalsize(actual.try_into().unwrap());
240         Ok(())
241     }
242 
243     /// 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<()>244     pub fn set_property(&mut self, path: &str, name: &CStr, val: &[u8]) -> Result<()> {
245         let node = self.find_or_add_node(path)?;
246         // SAFETY: API from libfdt_c.
247         map_result(unsafe {
248             fdt_setprop(
249                 self.0.as_mut().as_mut_ptr() as *mut _,
250                 node,
251                 name.to_bytes_with_nul().as_ptr() as *const _,
252                 val.as_ptr() as *const _,
253                 val.len().try_into().map_err(|_| FdtError::IntegerOverflow)?,
254             )
255         })?;
256         Ok(())
257     }
258 
259     /// Wrapper/equivalent of fdt_setprop_placeholder.
260     /// It creates/resizes a node's property to the given size and returns the buffer for caller
261     /// to modify content.
set_property_placeholder( &mut self, path: &str, name: &CStr, len: usize, ) -> Result<&mut [u8]>262     pub fn set_property_placeholder(
263         &mut self,
264         path: &str,
265         name: &CStr,
266         len: usize,
267     ) -> Result<&mut [u8]> {
268         let node = self.find_or_add_node(path)?;
269         let mut out_ptr: *mut u8 = core::ptr::null_mut();
270         // SAFETY: API from libfdt_c.
271         map_result(unsafe {
272             fdt_setprop_placeholder(
273                 self.0.as_mut().as_mut_ptr() as *mut _,
274                 node,
275                 name.to_bytes_with_nul().as_ptr() as *const _,
276                 len.try_into().map_err(|_| FdtError::IntegerOverflow)?,
277                 &mut out_ptr as *mut *mut u8 as *mut _,
278             )
279         })?;
280         assert!(!out_ptr.is_null());
281         // SAFETY: Buffer returned by API from libfdt_c.
282         Ok(unsafe { from_raw_parts_mut(out_ptr, len) })
283     }
284 
285     /// 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<core::ffi::c_int>286     fn find_or_add_node(&mut self, path: &str) -> Result<core::ffi::c_int> {
287         let mut curr: core::ffi::c_int = 0;
288         for name in path.split('/') {
289             if name.len() == 0 {
290                 continue;
291             }
292             curr = match fdt_subnode_offset(self.0.as_ref(), curr, name) {
293                 Ok(v) => v,
294                 _ => fdt_add_subnode(self.0.as_mut(), curr, name)?,
295             };
296         }
297         Ok(curr)
298     }
299 }
300 
301 #[cfg(test)]
302 mod test {
303     use super::*;
304     use std::ffi::CString;
305 
to_cstr(s: &str) -> CString306     fn to_cstr(s: &str) -> CString {
307         CString::new(s).unwrap()
308     }
309 
310     #[test]
test_new_from_invalid_fdt()311     fn test_new_from_invalid_fdt() {
312         let mut init = include_bytes!("../test/test.dtb").to_vec();
313         let mut fdt_buf = vec![0u8; init.len()];
314         // Invalid total size
315         assert!(Fdt::new_from_init(&mut fdt_buf[..], &init[..init.len() - 1]).is_err());
316         // Invalid FDT
317         init[..4].fill(0);
318         assert!(Fdt::new_from_init(&mut fdt_buf[..], &init[..]).is_err());
319     }
320 
321     #[test]
test_get_property()322     fn test_get_property() {
323         let init = include_bytes!("../test/test.dtb").to_vec();
324         let mut fdt_buf = vec![0u8; init.len()];
325         let fdt = Fdt::new_from_init(&mut fdt_buf[..], &init[..]).unwrap();
326 
327         assert_eq!(
328             CStr::from_bytes_with_nul(fdt.get_property("/", &to_cstr("info")).unwrap())
329                 .unwrap()
330                 .to_str()
331                 .unwrap(),
332             "test device tree"
333         );
334         assert_eq!(
335             CStr::from_bytes_with_nul(
336                 fdt.get_property("/dev-2/dev-2.2/dev-2.2.1", &to_cstr("property-1")).unwrap()
337             )
338             .unwrap()
339             .to_str()
340             .unwrap(),
341             "dev-2.2.1-property-1"
342         );
343 
344         // Non eixsts
345         assert!(fdt.get_property("/", &to_cstr("non-exist")).is_err());
346     }
347 
348     #[test]
test_set_property()349     fn test_set_property() {
350         let init = include_bytes!("../test/test.dtb").to_vec();
351         let mut fdt_buf = vec![0u8; init.len() + 512];
352         let mut fdt = Fdt::new_from_init(&mut fdt_buf[..], &init[..]).unwrap();
353         let data = vec![0x11u8, 0x22u8, 0x33u8];
354         fdt.set_property("/new-node", &to_cstr("custom"), &data).unwrap();
355         assert_eq!(fdt.get_property("/new-node", &to_cstr("custom")).unwrap().to_vec(), data);
356     }
357 
358     #[test]
test_set_property_placeholder()359     fn test_set_property_placeholder() {
360         let init = include_bytes!("../test/test.dtb").to_vec();
361         let mut fdt_buf = vec![0u8; init.len() + 512];
362         let mut fdt = Fdt::new_from_init(&mut fdt_buf[..], &init[..]).unwrap();
363         let data = vec![0x11u8, 0x22u8, 0x33u8, 0x44u8, 0x55u8];
364         let payload =
365             fdt.set_property_placeholder("/new-node", &to_cstr("custom"), data.len()).unwrap();
366         payload.clone_from_slice(&data[..]);
367         assert_eq!(fdt.get_property("/new-node", &to_cstr("custom")).unwrap().to_vec(), data);
368     }
369 
370     #[test]
test_header_from_raw()371     fn test_header_from_raw() {
372         let init = include_bytes!("../test/test.dtb").to_vec();
373         // Pointer points to `init`
374         let (header, bytes) = unsafe { FdtHeader::from_raw(init.as_ptr()).unwrap() };
375         assert_eq!(header.totalsize(), init.len());
376         assert_eq!(bytes.to_vec(), init);
377     }
378 
379     #[test]
test_header_from_raw_invalid()380     fn test_header_from_raw_invalid() {
381         let mut init = include_bytes!("../test/test.dtb").to_vec();
382         init[..4].fill(0);
383         // Pointer points to `init`
384         assert!(unsafe { FdtHeader::from_raw(init.as_ptr()).is_err() });
385     }
386 
387     #[test]
test_fdt_shrink_to_fit()388     fn test_fdt_shrink_to_fit() {
389         let init = include_bytes!("../test/test.dtb").to_vec();
390         let mut fdt_buf = vec![0u8; init.len() + 512];
391         let fdt_buf_len = fdt_buf.len();
392         let mut fdt = Fdt::new_from_init(&mut fdt_buf[..], &init[..]).unwrap();
393         assert_eq!(fdt.size().unwrap(), fdt_buf_len);
394         fdt.shrink_to_fit().unwrap();
395         assert_eq!(fdt.size().unwrap(), init.len());
396     }
397 }
398