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