blob: f3544cbfbdf6128e0f1ed1b90e4d0d26e27131f9 [file] [log] [blame]
David Brazdil1baa9a92022-06-28 14:47:50 +01001// Copyright 2022, 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//! Wrapper around libfdt library. Provides parsing/generating functionality
16//! to a bare-metal environment.
17
18#![no_std]
19
Andrew Walbran55ad01b2022-12-05 17:00:40 +000020mod iterators;
Pierre-Clément Tosi99a76902023-12-21 10:30:37 +000021mod result;
Andrew Walbran55ad01b2022-12-05 17:00:40 +000022
Jaewan Kimfe06c852023-10-05 23:40:06 +090023pub use iterators::{
Jaewan Kimc9e14112023-12-04 17:05:27 +090024 AddressRange, CellIterator, CompatibleIterator, DescendantsIterator, MemRegIterator,
25 PropertyIterator, RangesIterator, Reg, RegIterator, SubnodeIterator,
Jaewan Kimfe06c852023-10-05 23:40:06 +090026};
Pierre-Clément Tosi99a76902023-12-21 10:30:37 +000027pub use result::{FdtError, Result};
Andrew Walbran55ad01b2022-12-05 17:00:40 +000028
David Brazdil1baa9a92022-06-28 14:47:50 +010029use core::ffi::{c_int, c_void, CStr};
Alice Wang2422bdc2023-06-12 08:37:55 +000030use core::ops::Range;
Jaewan Kim5b057772023-10-19 01:02:17 +090031use core::ptr;
Pierre-Clément Tosi1bf532b2023-11-13 11:06:20 +000032use cstr::cstr;
Pierre-Clément Tosi99a76902023-12-21 10:30:37 +000033use result::{fdt_err, fdt_err_expect_zero, fdt_err_or_option};
Pierre-Clément Tosic27c4272023-05-19 15:46:26 +000034use zerocopy::AsBytes as _;
David Brazdil1baa9a92022-06-28 14:47:50 +010035
David Brazdil1baa9a92022-06-28 14:47:50 +010036/// Value of a #address-cells property.
Andrew Walbranb39e6922022-12-05 17:01:20 +000037#[derive(Copy, Clone, Debug, Eq, PartialEq)]
David Brazdil1baa9a92022-06-28 14:47:50 +010038enum AddrCells {
39 Single = 1,
40 Double = 2,
Andrew Walbranb39e6922022-12-05 17:01:20 +000041 Triple = 3,
David Brazdil1baa9a92022-06-28 14:47:50 +010042}
43
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +000044impl TryFrom<usize> for AddrCells {
David Brazdil1baa9a92022-06-28 14:47:50 +010045 type Error = FdtError;
46
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +000047 fn try_from(value: usize) -> Result<Self> {
48 match value {
49 x if x == Self::Single as _ => Ok(Self::Single),
50 x if x == Self::Double as _ => Ok(Self::Double),
51 x if x == Self::Triple as _ => Ok(Self::Triple),
David Brazdil1baa9a92022-06-28 14:47:50 +010052 _ => Err(FdtError::BadNCells),
53 }
54 }
55}
56
57/// Value of a #size-cells property.
Andrew Walbranb39e6922022-12-05 17:01:20 +000058#[derive(Copy, Clone, Debug, Eq, PartialEq)]
David Brazdil1baa9a92022-06-28 14:47:50 +010059enum SizeCells {
60 None = 0,
61 Single = 1,
62 Double = 2,
63}
64
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +000065impl TryFrom<usize> for SizeCells {
David Brazdil1baa9a92022-06-28 14:47:50 +010066 type Error = FdtError;
67
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +000068 fn try_from(value: usize) -> Result<Self> {
69 match value {
70 x if x == Self::None as _ => Ok(Self::None),
71 x if x == Self::Single as _ => Ok(Self::Single),
72 x if x == Self::Double as _ => Ok(Self::Double),
David Brazdil1baa9a92022-06-28 14:47:50 +010073 _ => Err(FdtError::BadNCells),
74 }
75 }
76}
77
Jaewan Kim72d10902023-10-12 21:59:26 +090078/// DT property wrapper to abstract endianess changes
79#[repr(transparent)]
80#[derive(Debug)]
81struct FdtPropertyStruct(libfdt_bindgen::fdt_property);
82
Pierre-Clément Tosidf3037f2024-01-22 15:41:43 +000083impl AsRef<FdtPropertyStruct> for libfdt_bindgen::fdt_property {
84 fn as_ref(&self) -> &FdtPropertyStruct {
85 let ptr = self as *const _ as *const _;
86 // SAFETY: Types have the same layout (transparent) so the valid reference remains valid.
87 unsafe { &*ptr }
88 }
89}
90
Jaewan Kim72d10902023-10-12 21:59:26 +090091impl FdtPropertyStruct {
92 fn from_offset(fdt: &Fdt, offset: c_int) -> Result<&Self> {
93 let mut len = 0;
94 let prop =
95 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
96 unsafe { libfdt_bindgen::fdt_get_property_by_offset(fdt.as_ptr(), offset, &mut len) };
97 if prop.is_null() {
98 fdt_err(len)?;
99 return Err(FdtError::Internal); // shouldn't happen.
100 }
101 // SAFETY: prop is only returned when it points to valid libfdt_bindgen.
Pierre-Clément Tosidf3037f2024-01-22 15:41:43 +0000102 let prop = unsafe { &*prop };
103 Ok(prop.as_ref())
Jaewan Kim72d10902023-10-12 21:59:26 +0900104 }
105
106 fn name_offset(&self) -> c_int {
107 u32::from_be(self.0.nameoff).try_into().unwrap()
108 }
109
110 fn data_len(&self) -> usize {
111 u32::from_be(self.0.len).try_into().unwrap()
112 }
113
114 fn data_ptr(&self) -> *const c_void {
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000115 self.0.data.as_ptr().cast()
Jaewan Kim72d10902023-10-12 21:59:26 +0900116 }
117}
118
119/// DT property.
120#[derive(Clone, Copy, Debug)]
121pub struct FdtProperty<'a> {
122 fdt: &'a Fdt,
123 offset: c_int,
124 property: &'a FdtPropertyStruct,
125}
126
127impl<'a> FdtProperty<'a> {
128 fn new(fdt: &'a Fdt, offset: c_int) -> Result<Self> {
129 let property = FdtPropertyStruct::from_offset(fdt, offset)?;
130 Ok(Self { fdt, offset, property })
131 }
132
133 /// Returns the property name
134 pub fn name(&self) -> Result<&'a CStr> {
135 self.fdt.string(self.property.name_offset())
136 }
137
138 /// Returns the property value
139 pub fn value(&self) -> Result<&'a [u8]> {
140 self.fdt.get_from_ptr(self.property.data_ptr(), self.property.data_len())
141 }
142
143 fn next_property(&self) -> Result<Option<Self>> {
144 let ret =
145 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
146 unsafe { libfdt_bindgen::fdt_next_property_offset(self.fdt.as_ptr(), self.offset) };
147
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000148 if let Some(offset) = fdt_err_or_option(ret)? {
149 Ok(Some(Self::new(self.fdt, offset)?))
150 } else {
151 Ok(None)
152 }
Jaewan Kim72d10902023-10-12 21:59:26 +0900153 }
154}
155
David Brazdil1baa9a92022-06-28 14:47:50 +0100156/// DT node.
Alice Wang9d4df702023-05-25 14:14:12 +0000157#[derive(Clone, Copy, Debug)]
David Brazdil1baa9a92022-06-28 14:47:50 +0100158pub struct FdtNode<'a> {
159 fdt: &'a Fdt,
160 offset: c_int,
161}
162
163impl<'a> FdtNode<'a> {
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900164 /// Returns parent node.
David Brazdil1baa9a92022-06-28 14:47:50 +0100165 pub fn parent(&self) -> Result<Self> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000166 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
David Brazdil1baa9a92022-06-28 14:47:50 +0100167 let ret = unsafe { libfdt_bindgen::fdt_parent_offset(self.fdt.as_ptr(), self.offset) };
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000168 let offset = fdt_err(ret)?;
David Brazdil1baa9a92022-06-28 14:47:50 +0100169
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000170 Ok(Self { fdt: self.fdt, offset })
David Brazdil1baa9a92022-06-28 14:47:50 +0100171 }
172
Jaewan Kim5b057772023-10-19 01:02:17 +0900173 /// Returns supernode with depth. Note that root is at depth 0.
174 pub fn supernode_at_depth(&self, depth: usize) -> Result<Self> {
175 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
176 let ret = unsafe {
177 libfdt_bindgen::fdt_supernode_atdepth_offset(
178 self.fdt.as_ptr(),
179 self.offset,
180 depth.try_into().unwrap(),
181 ptr::null_mut(),
182 )
183 };
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000184 let offset = fdt_err(ret)?;
Jaewan Kim5b057772023-10-19 01:02:17 +0900185
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000186 Ok(Self { fdt: self.fdt, offset })
Jaewan Kim5b057772023-10-19 01:02:17 +0900187 }
188
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900189 /// Returns the standard (deprecated) device_type <string> property.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000190 pub fn device_type(&self) -> Result<Option<&CStr>> {
Jaewan Kimb635bb02023-11-01 13:00:34 +0900191 self.getprop_str(cstr!("device_type"))
David Brazdil1baa9a92022-06-28 14:47:50 +0100192 }
193
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900194 /// Returns the standard reg <prop-encoded-array> property.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000195 pub fn reg(&self) -> Result<Option<RegIterator<'a>>> {
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000196 if let Some(cells) = self.getprop_cells(cstr!("reg"))? {
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000197 let parent = self.parent()?;
David Brazdil1baa9a92022-06-28 14:47:50 +0100198
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000199 let addr_cells = parent.address_cells()?;
200 let size_cells = parent.size_cells()?;
201
202 Ok(Some(RegIterator::new(cells, addr_cells, size_cells)))
203 } else {
204 Ok(None)
205 }
David Brazdil1baa9a92022-06-28 14:47:50 +0100206 }
207
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900208 /// Returns the standard ranges property.
Andrew Walbranb39e6922022-12-05 17:01:20 +0000209 pub fn ranges<A, P, S>(&self) -> Result<Option<RangesIterator<'a, A, P, S>>> {
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000210 if let Some(cells) = self.getprop_cells(cstr!("ranges"))? {
Andrew Walbranb39e6922022-12-05 17:01:20 +0000211 let parent = self.parent()?;
212 let addr_cells = self.address_cells()?;
213 let parent_addr_cells = parent.address_cells()?;
214 let size_cells = self.size_cells()?;
215 Ok(Some(RangesIterator::<A, P, S>::new(
216 cells,
217 addr_cells,
218 parent_addr_cells,
219 size_cells,
220 )))
221 } else {
222 Ok(None)
223 }
224 }
225
Jaewan Kimaa638702023-09-19 13:34:01 +0900226 /// Returns the node name.
227 pub fn name(&self) -> Result<&'a CStr> {
228 let mut len: c_int = 0;
229 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor). On success, the
230 // function returns valid null terminating string and otherwise returned values are dropped.
231 let name = unsafe { libfdt_bindgen::fdt_get_name(self.fdt.as_ptr(), self.offset, &mut len) }
232 as *const c_void;
233 let len = usize::try_from(fdt_err(len)?).unwrap();
234 let name = self.fdt.get_from_ptr(name, len + 1)?;
235 CStr::from_bytes_with_nul(name).map_err(|_| FdtError::Internal)
236 }
237
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900238 /// Returns the value of a given <string> property.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000239 pub fn getprop_str(&self, name: &CStr) -> Result<Option<&CStr>> {
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000240 if let Some(bytes) = self.getprop(name)? {
241 Ok(Some(CStr::from_bytes_with_nul(bytes).map_err(|_| FdtError::BadValue)?))
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000242 } else {
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000243 Ok(None)
244 }
David Brazdil1baa9a92022-06-28 14:47:50 +0100245 }
246
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900247 /// Returns the value of a given property as an array of cells.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000248 pub fn getprop_cells(&self, name: &CStr) -> Result<Option<CellIterator<'a>>> {
249 if let Some(cells) = self.getprop(name)? {
250 Ok(Some(CellIterator::new(cells)))
251 } else {
252 Ok(None)
253 }
David Brazdil1baa9a92022-06-28 14:47:50 +0100254 }
255
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900256 /// Returns the value of a given <u32> property.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000257 pub fn getprop_u32(&self, name: &CStr) -> Result<Option<u32>> {
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000258 if let Some(bytes) = self.getprop(name)? {
259 Ok(Some(u32::from_be_bytes(bytes.try_into().map_err(|_| FdtError::BadValue)?)))
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000260 } else {
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000261 Ok(None)
262 }
David Brazdil1baa9a92022-06-28 14:47:50 +0100263 }
264
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900265 /// Returns the value of a given <u64> property.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000266 pub fn getprop_u64(&self, name: &CStr) -> Result<Option<u64>> {
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000267 if let Some(bytes) = self.getprop(name)? {
268 Ok(Some(u64::from_be_bytes(bytes.try_into().map_err(|_| FdtError::BadValue)?)))
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000269 } else {
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000270 Ok(None)
271 }
David Brazdil1baa9a92022-06-28 14:47:50 +0100272 }
273
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900274 /// Returns the value of a given property.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000275 pub fn getprop(&self, name: &CStr) -> Result<Option<&'a [u8]>> {
Jiyong Park9c63cd12023-03-21 17:53:07 +0900276 if let Some((prop, len)) = Self::getprop_internal(self.fdt, self.offset, name)? {
Jaewan Kimaa638702023-09-19 13:34:01 +0900277 Ok(Some(self.fdt.get_from_ptr(prop, len)?))
Jiyong Park9c63cd12023-03-21 17:53:07 +0900278 } else {
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000279 Ok(None)
Jiyong Park9c63cd12023-03-21 17:53:07 +0900280 }
281 }
282
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900283 /// Returns the pointer and size of the property named `name`, in a node at offset `offset`, in
Jiyong Park9c63cd12023-03-21 17:53:07 +0900284 /// a device tree `fdt`. The pointer is guaranteed to be non-null, in which case error returns.
285 fn getprop_internal(
286 fdt: &'a Fdt,
287 offset: c_int,
288 name: &CStr,
289 ) -> Result<Option<(*const c_void, usize)>> {
David Brazdil1baa9a92022-06-28 14:47:50 +0100290 let mut len: i32 = 0;
Andrew Walbran84b9a232023-07-05 14:01:40 +0000291 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor) and the
David Brazdil1baa9a92022-06-28 14:47:50 +0100292 // function respects the passed number of characters.
293 let prop = unsafe {
294 libfdt_bindgen::fdt_getprop_namelen(
Jiyong Park9c63cd12023-03-21 17:53:07 +0900295 fdt.as_ptr(),
296 offset,
David Brazdil1baa9a92022-06-28 14:47:50 +0100297 name.as_ptr(),
298 // *_namelen functions don't include the trailing nul terminator in 'len'.
299 name.to_bytes().len().try_into().map_err(|_| FdtError::BadPath)?,
300 &mut len as *mut i32,
301 )
302 } as *const u8;
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000303
304 let Some(len) = fdt_err_or_option(len)? else {
305 return Ok(None); // Property was not found.
306 };
Jaewan Kimaa638702023-09-19 13:34:01 +0900307 let len = usize::try_from(len).unwrap();
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000308
David Brazdil1baa9a92022-06-28 14:47:50 +0100309 if prop.is_null() {
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000310 // We expected an error code in len but still received a valid value?!
311 return Err(FdtError::Internal);
David Brazdil1baa9a92022-06-28 14:47:50 +0100312 }
Jiyong Park9c63cd12023-03-21 17:53:07 +0900313 Ok(Some((prop.cast::<c_void>(), len)))
David Brazdil1baa9a92022-06-28 14:47:50 +0100314 }
315
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900316 /// Returns reference to the containing device tree.
David Brazdil1baa9a92022-06-28 14:47:50 +0100317 pub fn fdt(&self) -> &Fdt {
318 self.fdt
319 }
320
Alice Wang474c0ee2023-09-14 12:52:33 +0000321 /// Returns the compatible node of the given name that is next after this node.
322 pub fn next_compatible(self, compatible: &CStr) -> Result<Option<Self>> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000323 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
Pierre-Clément Tosi41c158e2022-11-21 19:16:25 +0000324 let ret = unsafe {
325 libfdt_bindgen::fdt_node_offset_by_compatible(
326 self.fdt.as_ptr(),
327 self.offset,
328 compatible.as_ptr(),
329 )
330 };
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000331 let offset = fdt_err_or_option(ret)?;
Pierre-Clément Tosi41c158e2022-11-21 19:16:25 +0000332
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000333 Ok(offset.map(|offset| Self { fdt: self.fdt, offset }))
Pierre-Clément Tosi41c158e2022-11-21 19:16:25 +0000334 }
335
Alice Wang474c0ee2023-09-14 12:52:33 +0000336 /// Returns the first range of `reg` in this node.
337 pub fn first_reg(&self) -> Result<Reg<u64>> {
338 self.reg()?.ok_or(FdtError::NotFound)?.next().ok_or(FdtError::NotFound)
339 }
340
David Brazdil1baa9a92022-06-28 14:47:50 +0100341 fn address_cells(&self) -> Result<AddrCells> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000342 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor).
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000343 let ret = unsafe { libfdt_bindgen::fdt_address_cells(self.fdt.as_ptr(), self.offset) };
344
345 usize::try_from(ret).map_err(|_| FdtError::Internal)?.try_into()
David Brazdil1baa9a92022-06-28 14:47:50 +0100346 }
347
348 fn size_cells(&self) -> Result<SizeCells> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000349 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor).
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000350 let ret = unsafe { libfdt_bindgen::fdt_size_cells(self.fdt.as_ptr(), self.offset) };
351
352 usize::try_from(ret).map_err(|_| FdtError::Internal)?.try_into()
David Brazdil1baa9a92022-06-28 14:47:50 +0100353 }
Jaewan Kimbc828d72023-09-19 15:52:08 +0900354
355 /// Returns an iterator of subnodes
Jaewan Kim4a34b0d2024-01-19 13:17:47 +0900356 pub fn subnodes(&self) -> Result<SubnodeIterator<'a>> {
Jaewan Kimbc828d72023-09-19 15:52:08 +0900357 SubnodeIterator::new(self)
358 }
359
360 fn first_subnode(&self) -> Result<Option<Self>> {
361 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
362 let ret = unsafe { libfdt_bindgen::fdt_first_subnode(self.fdt.as_ptr(), self.offset) };
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000363 let offset = fdt_err_or_option(ret)?;
Jaewan Kimbc828d72023-09-19 15:52:08 +0900364
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000365 Ok(offset.map(|offset| Self { fdt: self.fdt, offset }))
Jaewan Kimbc828d72023-09-19 15:52:08 +0900366 }
367
368 fn next_subnode(&self) -> Result<Option<Self>> {
369 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
370 let ret = unsafe { libfdt_bindgen::fdt_next_subnode(self.fdt.as_ptr(), self.offset) };
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000371 let offset = fdt_err_or_option(ret)?;
Jaewan Kimbc828d72023-09-19 15:52:08 +0900372
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000373 Ok(offset.map(|offset| Self { fdt: self.fdt, offset }))
Jaewan Kimbc828d72023-09-19 15:52:08 +0900374 }
Jaewan Kim72d10902023-10-12 21:59:26 +0900375
Jaewan Kimc9e14112023-12-04 17:05:27 +0900376 /// Returns an iterator of descendants
Jaewan Kim1eab7232024-01-04 09:46:16 +0900377 pub fn descendants(&self) -> DescendantsIterator<'a> {
Jaewan Kimc9e14112023-12-04 17:05:27 +0900378 DescendantsIterator::new(self)
379 }
380
381 fn next_node(&self, depth: usize) -> Result<Option<(Self, usize)>> {
382 let mut next_depth: c_int = depth.try_into().unwrap();
383 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
384 let ret = unsafe {
385 libfdt_bindgen::fdt_next_node(self.fdt.as_ptr(), self.offset, &mut next_depth)
386 };
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000387 let Ok(depth) = usize::try_from(next_depth) else {
Jaewan Kimc9e14112023-12-04 17:05:27 +0900388 return Ok(None);
389 };
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000390 if let Some(offset) = fdt_err_or_option(ret)? {
391 Ok(Some((Self { fdt: self.fdt, offset }, depth)))
392 } else {
393 Ok(None)
394 }
Jaewan Kimc9e14112023-12-04 17:05:27 +0900395 }
396
Jaewan Kim72d10902023-10-12 21:59:26 +0900397 /// Returns an iterator of properties
398 pub fn properties(&'a self) -> Result<PropertyIterator<'a>> {
399 PropertyIterator::new(self)
400 }
401
402 fn first_property(&self) -> Result<Option<FdtProperty<'a>>> {
403 let ret =
404 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
405 unsafe { libfdt_bindgen::fdt_first_property_offset(self.fdt.as_ptr(), self.offset) };
406
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000407 if let Some(offset) = fdt_err_or_option(ret)? {
408 Ok(Some(FdtProperty::new(self.fdt, offset)?))
409 } else {
410 Ok(None)
411 }
Jaewan Kim72d10902023-10-12 21:59:26 +0900412 }
Jaewan Kimf34f4b82023-11-03 19:38:38 +0900413
414 /// Returns the phandle
415 pub fn get_phandle(&self) -> Result<Option<Phandle>> {
416 // This rewrites the fdt_get_phandle() because it doesn't return error code.
417 if let Some(prop) = self.getprop_u32(cstr!("phandle"))? {
418 Ok(Some(prop.try_into()?))
419 } else if let Some(prop) = self.getprop_u32(cstr!("linux,phandle"))? {
420 Ok(Some(prop.try_into()?))
421 } else {
422 Ok(None)
423 }
424 }
Jaewan Kim52026012023-12-13 13:49:28 +0900425
426 /// Returns the subnode of the given name. The name doesn't need to be nul-terminated.
427 pub fn subnode(&self, name: &CStr) -> Result<Option<Self>> {
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000428 let name = name.to_bytes();
429 let offset = self.subnode_offset(name)?;
430
Jaewan Kim52026012023-12-13 13:49:28 +0900431 Ok(offset.map(|offset| Self { fdt: self.fdt, offset }))
432 }
433
434 /// Returns the subnode of the given name bytes
435 pub fn subnode_with_name_bytes(&self, name: &[u8]) -> Result<Option<Self>> {
436 let offset = self.subnode_offset(name)?;
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000437
Jaewan Kim52026012023-12-13 13:49:28 +0900438 Ok(offset.map(|offset| Self { fdt: self.fdt, offset }))
439 }
440
441 fn subnode_offset(&self, name: &[u8]) -> Result<Option<c_int>> {
442 let namelen = name.len().try_into().unwrap();
443 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor).
444 let ret = unsafe {
445 libfdt_bindgen::fdt_subnode_offset_namelen(
446 self.fdt.as_ptr(),
447 self.offset,
448 name.as_ptr().cast::<_>(),
449 namelen,
450 )
451 };
452 fdt_err_or_option(ret)
453 }
David Brazdil1baa9a92022-06-28 14:47:50 +0100454}
455
Pierre-Clément Tosi504b4302023-10-30 12:22:50 +0000456impl<'a> PartialEq for FdtNode<'a> {
457 fn eq(&self, other: &Self) -> bool {
458 self.fdt.as_ptr() == other.fdt.as_ptr() && self.offset == other.offset
459 }
460}
461
Jaewan Kim17ba7a32023-10-19 13:25:15 +0900462/// Phandle of a FDT node
463#[repr(transparent)]
Jaewan Kim55f438c2023-11-15 01:24:36 +0900464#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
Jaewan Kim17ba7a32023-10-19 13:25:15 +0900465pub struct Phandle(u32);
466
467impl Phandle {
Pierre-Clément Tosieba27792023-10-30 12:04:12 +0000468 /// Minimum valid value for device tree phandles.
469 pub const MIN: Self = Self(1);
470 /// Maximum valid value for device tree phandles.
471 pub const MAX: Self = Self(libfdt_bindgen::FDT_MAX_PHANDLE);
472
Jaewan Kim17ba7a32023-10-19 13:25:15 +0900473 /// Creates a new Phandle
Pierre-Clément Tosieba27792023-10-30 12:04:12 +0000474 pub const fn new(value: u32) -> Option<Self> {
475 if Self::MIN.0 <= value && value <= Self::MAX.0 {
476 Some(Self(value))
477 } else {
478 None
Jaewan Kim17ba7a32023-10-19 13:25:15 +0900479 }
Jaewan Kim17ba7a32023-10-19 13:25:15 +0900480 }
481}
482
483impl From<Phandle> for u32 {
484 fn from(phandle: Phandle) -> u32 {
485 phandle.0
486 }
487}
488
Pierre-Clément Tosieba27792023-10-30 12:04:12 +0000489impl TryFrom<u32> for Phandle {
490 type Error = FdtError;
491
492 fn try_from(value: u32) -> Result<Self> {
493 Self::new(value).ok_or(FdtError::BadPhandle)
494 }
495}
496
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000497/// Mutable FDT node.
Pierre-Clément Tosi504b4302023-10-30 12:22:50 +0000498#[derive(Debug)]
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000499pub struct FdtNodeMut<'a> {
500 fdt: &'a mut Fdt,
501 offset: c_int,
502}
503
504impl<'a> FdtNodeMut<'a> {
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900505 /// Appends a property name-value (possibly empty) pair to the given node.
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000506 pub fn appendprop<T: AsRef<[u8]>>(&mut self, name: &CStr, value: &T) -> Result<()> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000507 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor).
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000508 let ret = unsafe {
509 libfdt_bindgen::fdt_appendprop(
510 self.fdt.as_mut_ptr(),
511 self.offset,
512 name.as_ptr(),
513 value.as_ref().as_ptr().cast::<c_void>(),
514 value.as_ref().len().try_into().map_err(|_| FdtError::BadValue)?,
515 )
516 };
517
518 fdt_err_expect_zero(ret)
519 }
520
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900521 /// Appends a (address, size) pair property to the given node.
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000522 pub fn appendprop_addrrange(&mut self, name: &CStr, addr: u64, size: u64) -> Result<()> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000523 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor).
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000524 let ret = unsafe {
525 libfdt_bindgen::fdt_appendprop_addrrange(
526 self.fdt.as_mut_ptr(),
527 self.parent()?.offset,
528 self.offset,
529 name.as_ptr(),
530 addr,
531 size,
532 )
533 };
534
535 fdt_err_expect_zero(ret)
536 }
537
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900538 /// Sets a property name-value pair to the given node.
539 ///
540 /// This may create a new prop or replace existing value.
Jaewan Kimba8929b2023-01-13 11:13:29 +0900541 pub fn setprop(&mut self, name: &CStr, value: &[u8]) -> Result<()> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000542 // SAFETY: New value size is constrained to the DT totalsize
Jaewan Kimba8929b2023-01-13 11:13:29 +0900543 // (validated by underlying libfdt).
544 let ret = unsafe {
545 libfdt_bindgen::fdt_setprop(
546 self.fdt.as_mut_ptr(),
547 self.offset,
548 name.as_ptr(),
549 value.as_ptr().cast::<c_void>(),
550 value.len().try_into().map_err(|_| FdtError::BadValue)?,
551 )
552 };
553
554 fdt_err_expect_zero(ret)
555 }
556
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900557 /// Sets the value of the given property with the given value, and ensure that the given
558 /// value has the same length as the current value length.
559 ///
560 /// This can only be used to replace existing value.
Jiyong Park9c63cd12023-03-21 17:53:07 +0900561 pub fn setprop_inplace(&mut self, name: &CStr, value: &[u8]) -> Result<()> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000562 // SAFETY: fdt size is not altered
Jiyong Park9c63cd12023-03-21 17:53:07 +0900563 let ret = unsafe {
564 libfdt_bindgen::fdt_setprop_inplace(
565 self.fdt.as_mut_ptr(),
566 self.offset,
567 name.as_ptr(),
568 value.as_ptr().cast::<c_void>(),
569 value.len().try_into().map_err(|_| FdtError::BadValue)?,
570 )
571 };
572
573 fdt_err_expect_zero(ret)
574 }
575
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900576 /// Sets the value of the given (address, size) pair property with the given value, and
577 /// ensure that the given value has the same length as the current value length.
578 ///
579 /// This can only be used to replace existing value.
Pierre-Clément Tosic27c4272023-05-19 15:46:26 +0000580 pub fn setprop_addrrange_inplace(&mut self, name: &CStr, addr: u64, size: u64) -> Result<()> {
581 let pair = [addr.to_be(), size.to_be()];
582 self.setprop_inplace(name, pair.as_bytes())
583 }
584
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900585 /// Sets a flag-like empty property.
586 ///
587 /// This may create a new prop or replace existing value.
Pierre-Clément Tosi4ba79662023-02-13 11:22:41 +0000588 pub fn setprop_empty(&mut self, name: &CStr) -> Result<()> {
589 self.setprop(name, &[])
590 }
591
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900592 /// Deletes the given property.
Pierre-Clément Tosi4ba79662023-02-13 11:22:41 +0000593 pub fn delprop(&mut self, name: &CStr) -> Result<()> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000594 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor) when the
Pierre-Clément Tosi4ba79662023-02-13 11:22:41 +0000595 // library locates the node's property. Removing the property may shift the offsets of
596 // other nodes and properties but the borrow checker should prevent this function from
597 // being called when FdtNode instances are in use.
598 let ret = unsafe {
599 libfdt_bindgen::fdt_delprop(self.fdt.as_mut_ptr(), self.offset, name.as_ptr())
600 };
601
602 fdt_err_expect_zero(ret)
603 }
604
Jaewan Kim4ae0e712023-10-19 14:16:17 +0900605 /// Deletes the given property effectively from DT, by setting it with FDT_NOP.
Pierre-Clément Tosibe3a97b2023-05-19 14:56:23 +0000606 pub fn nop_property(&mut self, name: &CStr) -> Result<()> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000607 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor) when the
Pierre-Clément Tosibe3a97b2023-05-19 14:56:23 +0000608 // library locates the node's property.
609 let ret = unsafe {
610 libfdt_bindgen::fdt_nop_property(self.fdt.as_mut_ptr(), self.offset, name.as_ptr())
611 };
612
613 fdt_err_expect_zero(ret)
614 }
615
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900616 /// Trims the size of the given property to new_size.
Jiyong Park9c63cd12023-03-21 17:53:07 +0900617 pub fn trimprop(&mut self, name: &CStr, new_size: usize) -> Result<()> {
618 let (prop, len) =
619 FdtNode::getprop_internal(self.fdt, self.offset, name)?.ok_or(FdtError::NotFound)?;
620 if len == new_size {
621 return Ok(());
622 }
623 if new_size > len {
624 return Err(FdtError::NoSpace);
625 }
626
Andrew Walbran84b9a232023-07-05 14:01:40 +0000627 // SAFETY: new_size is smaller than the old size
Jiyong Park9c63cd12023-03-21 17:53:07 +0900628 let ret = unsafe {
629 libfdt_bindgen::fdt_setprop(
630 self.fdt.as_mut_ptr(),
631 self.offset,
632 name.as_ptr(),
633 prop.cast::<c_void>(),
634 new_size.try_into().map_err(|_| FdtError::BadValue)?,
635 )
636 };
637
638 fdt_err_expect_zero(ret)
639 }
640
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900641 /// Returns reference to the containing device tree.
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000642 pub fn fdt(&mut self) -> &mut Fdt {
643 self.fdt
644 }
645
Jaewan Kimf72f4f22023-11-03 19:21:34 +0900646 /// Returns immutable FdtNode of this node.
647 pub fn as_node(&self) -> FdtNode {
648 FdtNode { fdt: self.fdt, offset: self.offset }
649 }
650
Jaewan Kime6363422024-01-19 14:00:00 +0900651 /// Adds new subnodes to the given node.
652 pub fn add_subnodes(&mut self, names: &[&CStr]) -> Result<()> {
653 for name in names {
654 self.add_subnode_offset(name.to_bytes())?;
655 }
656 Ok(())
657 }
658
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900659 /// Adds a new subnode to the given node and return it as a FdtNodeMut on success.
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000660 pub fn add_subnode(&'a mut self, name: &CStr) -> Result<Self> {
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000661 let name = name.to_bytes();
662 let offset = self.add_subnode_offset(name)?;
663
Jaewan Kim5ab13582023-10-20 20:56:27 +0900664 Ok(Self { fdt: self.fdt, offset })
665 }
666
667 /// Adds a new subnode to the given node with name and namelen, and returns it as a FdtNodeMut
668 /// on success.
669 pub fn add_subnode_with_namelen(&'a mut self, name: &CStr, namelen: usize) -> Result<Self> {
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000670 let name = &name.to_bytes()[..namelen];
671 let offset = self.add_subnode_offset(name)?;
672
Jaewan Kim5ab13582023-10-20 20:56:27 +0900673 Ok(Self { fdt: self.fdt, offset })
674 }
675
676 fn add_subnode_offset(&mut self, name: &[u8]) -> Result<c_int> {
677 let namelen = name.len().try_into().unwrap();
Andrew Walbran84b9a232023-07-05 14:01:40 +0000678 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor).
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000679 let ret = unsafe {
Jaewan Kim5ab13582023-10-20 20:56:27 +0900680 libfdt_bindgen::fdt_add_subnode_namelen(
681 self.fdt.as_mut_ptr(),
682 self.offset,
683 name.as_ptr().cast::<_>(),
684 namelen,
685 )
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000686 };
Jaewan Kim5ab13582023-10-20 20:56:27 +0900687 fdt_err(ret)
688 }
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000689
Jaewan Kim5f1a6032023-12-18 15:17:58 +0900690 /// Returns the first subnode of this
691 pub fn first_subnode(&'a mut self) -> Result<Option<Self>> {
692 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
693 let ret = unsafe { libfdt_bindgen::fdt_first_subnode(self.fdt.as_ptr(), self.offset) };
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000694 let offset = fdt_err_or_option(ret)?;
Jaewan Kim5f1a6032023-12-18 15:17:58 +0900695
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000696 Ok(offset.map(|offset| Self { fdt: self.fdt, offset }))
Jaewan Kim5f1a6032023-12-18 15:17:58 +0900697 }
698
699 /// Returns the next subnode that shares the same parent with this
700 pub fn next_subnode(self) -> Result<Option<Self>> {
701 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
702 let ret = unsafe { libfdt_bindgen::fdt_next_subnode(self.fdt.as_ptr(), self.offset) };
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000703 let offset = fdt_err_or_option(ret)?;
Jaewan Kim5f1a6032023-12-18 15:17:58 +0900704
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000705 Ok(offset.map(|offset| Self { fdt: self.fdt, offset }))
Jaewan Kim5f1a6032023-12-18 15:17:58 +0900706 }
707
708 /// Deletes the current node and returns the next subnode
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000709 pub fn delete_and_next_subnode(self) -> Result<Option<Self>> {
Jaewan Kim5f1a6032023-12-18 15:17:58 +0900710 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
711 let ret = unsafe { libfdt_bindgen::fdt_next_subnode(self.fdt.as_ptr(), self.offset) };
712
713 let next_offset = fdt_err_or_option(ret)?;
714
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000715 self.delete_and_next(next_offset)
Jaewan Kim5f1a6032023-12-18 15:17:58 +0900716 }
717
Jaewan Kim28a13ea2024-01-04 09:22:40 +0900718 fn next_node_offset(&self, depth: usize) -> Result<Option<(c_int, usize)>> {
719 let mut next_depth: c_int = depth.try_into().or(Err(FdtError::BadValue))?;
720 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
721 let ret = unsafe {
722 libfdt_bindgen::fdt_next_node(self.fdt.as_ptr(), self.offset, &mut next_depth)
723 };
724 let Ok(next_depth) = usize::try_from(next_depth) else {
725 return Ok(None);
726 };
727 Ok(fdt_err_or_option(ret)?.map(|offset| (offset, next_depth)))
728 }
729
730 /// Returns the next node
731 pub fn next_node(self, depth: usize) -> Result<Option<(Self, usize)>> {
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000732 let next = self.next_node_offset(depth)?;
733
734 Ok(next.map(|(offset, depth)| (Self { fdt: self.fdt, offset }, depth)))
Jaewan Kim28a13ea2024-01-04 09:22:40 +0900735 }
736
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000737 fn next_node_skip_subnodes(&mut self, depth: usize) -> Result<Option<(c_int, usize)>> {
Jaewan Kim28a13ea2024-01-04 09:22:40 +0900738 let mut iter = self.next_node_offset(depth)?;
739 while let Some((descendant_offset, descendant_depth)) = iter {
740 if descendant_depth <= depth {
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000741 return Ok(Some((descendant_offset, descendant_depth)));
Jaewan Kim28a13ea2024-01-04 09:22:40 +0900742 }
743 let descendant = FdtNodeMut { fdt: self.fdt, offset: descendant_offset };
744 iter = descendant.next_node_offset(descendant_depth)?;
745 }
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000746
747 Ok(None)
748 }
749
750 /// Deletes this and returns the next node
751 pub fn delete_and_next_node(mut self, depth: usize) -> Result<Option<(Self, usize)>> {
752 let next_node = self.next_node_skip_subnodes(depth)?;
753 if let Some((offset, depth)) = next_node {
754 let next_node = self.delete_and_next(Some(offset))?.unwrap();
755 Ok(Some((next_node, depth)))
756 } else {
757 Ok(None)
758 }
Jaewan Kim28a13ea2024-01-04 09:22:40 +0900759 }
760
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000761 fn parent(&'a self) -> Result<FdtNode<'a>> {
Pierre-Clément Tosidf3037f2024-01-22 15:41:43 +0000762 self.as_node().parent()
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000763 }
Jiyong Park9c63cd12023-03-21 17:53:07 +0900764
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900765 /// Returns the compatible node of the given name that is next after this node.
Jiyong Park9c63cd12023-03-21 17:53:07 +0900766 pub fn next_compatible(self, compatible: &CStr) -> Result<Option<Self>> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000767 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
Jiyong Park9c63cd12023-03-21 17:53:07 +0900768 let ret = unsafe {
769 libfdt_bindgen::fdt_node_offset_by_compatible(
770 self.fdt.as_ptr(),
771 self.offset,
772 compatible.as_ptr(),
773 )
774 };
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000775 let offset = fdt_err_or_option(ret)?;
Jiyong Park9c63cd12023-03-21 17:53:07 +0900776
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000777 Ok(offset.map(|offset| Self { fdt: self.fdt, offset }))
Jiyong Park9c63cd12023-03-21 17:53:07 +0900778 }
779
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900780 /// Deletes the node effectively by overwriting this node and its subtree with nop tags.
781 /// Returns the next compatible node of the given name.
Jiyong Park9c63cd12023-03-21 17:53:07 +0900782 // Side note: without this, filterint out excessive compatible nodes from the DT is impossible.
783 // The reason is that libfdt ensures that the node from where the search for the next
784 // compatible node is started is always a valid one -- except for the special case of offset =
785 // -1 which is to find the first compatible node. So, we can't delete a node and then find the
786 // next compatible node from it.
787 //
788 // We can't do in the opposite direction either. If we call next_compatible to find the next
789 // node, and delete the current node, the Rust borrow checker kicks in. The next node has a
790 // mutable reference to DT, so we can't use current node (which also has a mutable reference to
791 // DT).
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000792 pub fn delete_and_next_compatible(self, compatible: &CStr) -> Result<Option<Self>> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000793 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
Jiyong Park9c63cd12023-03-21 17:53:07 +0900794 let ret = unsafe {
795 libfdt_bindgen::fdt_node_offset_by_compatible(
796 self.fdt.as_ptr(),
797 self.offset,
798 compatible.as_ptr(),
799 )
800 };
801 let next_offset = fdt_err_or_option(ret)?;
802
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000803 self.delete_and_next(next_offset)
804 }
805
806 fn delete_and_next(mut self, next_offset: Option<c_int>) -> Result<Option<Self>> {
Jaewan Kim4ae0e712023-10-19 14:16:17 +0900807 if Some(self.offset) == next_offset {
808 return Err(FdtError::Internal);
809 }
810
811 // SAFETY: nop_self() only touches bytes of the self and its properties and subnodes, and
812 // doesn't alter any other blob in the tree. self.fdt and next_offset would remain valid.
813 unsafe { self.nop_self()? };
Jiyong Park9c63cd12023-03-21 17:53:07 +0900814
815 Ok(next_offset.map(|offset| Self { fdt: self.fdt, offset }))
816 }
Jaewan Kim4ae0e712023-10-19 14:16:17 +0900817
818 /// Deletes this node effectively from DT, by setting it with FDT_NOP
819 pub fn nop(mut self) -> Result<()> {
820 // SAFETY: This consumes self, so invalid node wouldn't be used any further
821 unsafe { self.nop_self() }
822 }
823
824 /// Deletes this node effectively from DT, by setting it with FDT_NOP.
825 /// This only changes bytes of the node and its properties and subnodes, and doesn't alter or
826 /// move any other part of the tree.
827 /// SAFETY: This node is no longer valid.
828 unsafe fn nop_self(&mut self) -> Result<()> {
829 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor).
830 let ret = unsafe { libfdt_bindgen::fdt_nop_node(self.fdt.as_mut_ptr(), self.offset) };
831
832 fdt_err_expect_zero(ret)
833 }
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000834}
835
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000836/// Wrapper around low-level libfdt functions.
Alice Wang9d4df702023-05-25 14:14:12 +0000837#[derive(Debug)]
David Brazdil1baa9a92022-06-28 14:47:50 +0100838#[repr(transparent)]
839pub struct Fdt {
Pierre-Clément Tosief2030e2022-11-28 11:21:20 +0000840 buffer: [u8],
David Brazdil1baa9a92022-06-28 14:47:50 +0100841}
842
843impl Fdt {
844 /// Wraps a slice containing a Flattened Device Tree.
845 ///
846 /// Fails if the FDT does not pass validation.
847 pub fn from_slice(fdt: &[u8]) -> Result<&Self> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000848 // SAFETY: The FDT will be validated before it is returned.
David Brazdil1baa9a92022-06-28 14:47:50 +0100849 let fdt = unsafe { Self::unchecked_from_slice(fdt) };
850 fdt.check_full()?;
851 Ok(fdt)
852 }
853
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000854 /// Wraps a mutable slice containing a Flattened Device Tree.
855 ///
856 /// Fails if the FDT does not pass validation.
857 pub fn from_mut_slice(fdt: &mut [u8]) -> Result<&mut Self> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000858 // SAFETY: The FDT will be validated before it is returned.
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000859 let fdt = unsafe { Self::unchecked_from_mut_slice(fdt) };
860 fdt.check_full()?;
861 Ok(fdt)
862 }
863
Jaewan Kim4cf20aa2023-04-03 10:25:38 +0900864 /// Creates an empty Flattened Device Tree with a mutable slice.
865 pub fn create_empty_tree(fdt: &mut [u8]) -> Result<&mut Self> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000866 // SAFETY: fdt_create_empty_tree() only write within the specified length,
Jaewan Kim4cf20aa2023-04-03 10:25:38 +0900867 // and returns error if buffer was insufficient.
868 // There will be no memory write outside of the given fdt.
869 let ret = unsafe {
870 libfdt_bindgen::fdt_create_empty_tree(
871 fdt.as_mut_ptr().cast::<c_void>(),
872 fdt.len() as i32,
873 )
874 };
875 fdt_err_expect_zero(ret)?;
876
Andrew Walbran84b9a232023-07-05 14:01:40 +0000877 // SAFETY: The FDT will be validated before it is returned.
Jaewan Kim4cf20aa2023-04-03 10:25:38 +0900878 let fdt = unsafe { Self::unchecked_from_mut_slice(fdt) };
879 fdt.check_full()?;
880
881 Ok(fdt)
882 }
883
David Brazdil1baa9a92022-06-28 14:47:50 +0100884 /// Wraps a slice containing a Flattened Device Tree.
885 ///
886 /// # Safety
887 ///
Pierre-Clément Tosidf3037f2024-01-22 15:41:43 +0000888 /// It is undefined to call this function on a slice that does not contain a valid device tree.
David Brazdil1baa9a92022-06-28 14:47:50 +0100889 pub unsafe fn unchecked_from_slice(fdt: &[u8]) -> &Self {
Pierre-Clément Tosidf3037f2024-01-22 15:41:43 +0000890 let self_ptr = fdt as *const _ as *const _;
891 // SAFETY: The pointer is non-null, dereferenceable, and points to allocated memory.
892 unsafe { &*self_ptr }
David Brazdil1baa9a92022-06-28 14:47:50 +0100893 }
894
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000895 /// Wraps a mutable slice containing a Flattened Device Tree.
896 ///
897 /// # Safety
898 ///
Pierre-Clément Tosidf3037f2024-01-22 15:41:43 +0000899 /// It is undefined to call this function on a slice that does not contain a valid device tree.
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000900 pub unsafe fn unchecked_from_mut_slice(fdt: &mut [u8]) -> &mut Self {
Pierre-Clément Tosidf3037f2024-01-22 15:41:43 +0000901 let self_mut_ptr = fdt as *mut _ as *mut _;
902 // SAFETY: The pointer is non-null, dereferenceable, and points to allocated memory.
903 unsafe { &mut *self_mut_ptr }
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000904 }
905
Pierre-Clément Tosice0b36d2024-01-26 10:50:05 +0000906 /// Updates this FDT from another FDT.
907 pub fn clone_from(&mut self, other: &Self) -> Result<()> {
908 let new_len = other.buffer.len();
909 if self.buffer.len() < new_len {
910 return Err(FdtError::NoSpace);
Jiyong Parke9d87e82023-03-21 19:28:40 +0900911 }
Pierre-Clément Tosice0b36d2024-01-26 10:50:05 +0000912
913 let zeroed_len = self.totalsize().checked_sub(new_len);
914 let (cloned, zeroed) = self.buffer.split_at_mut(new_len);
915
916 cloned.clone_from_slice(&other.buffer);
917 if let Some(len) = zeroed_len {
918 zeroed[..len].fill(0);
919 }
920
921 Ok(())
Jiyong Parke9d87e82023-03-21 19:28:40 +0900922 }
923
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900924 /// Unpacks the DT to cover the whole slice it is contained in.
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000925 pub fn unpack(&mut self) -> Result<()> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000926 // SAFETY: "Opens" the DT in-place (supported use-case) by updating its header and
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000927 // internal structures to make use of the whole self.fdt slice but performs no accesses
928 // outside of it and leaves the DT in a state that will be detected by other functions.
929 let ret = unsafe {
930 libfdt_bindgen::fdt_open_into(
931 self.as_ptr(),
932 self.as_mut_ptr(),
933 self.capacity().try_into().map_err(|_| FdtError::Internal)?,
934 )
935 };
936 fdt_err_expect_zero(ret)
937 }
938
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900939 /// Packs the DT to take a minimum amount of memory.
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000940 ///
941 /// Doesn't shrink the underlying memory slice.
942 pub fn pack(&mut self) -> Result<()> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000943 // SAFETY: "Closes" the DT in-place by updating its header and relocating its structs.
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000944 let ret = unsafe { libfdt_bindgen::fdt_pack(self.as_mut_ptr()) };
945 fdt_err_expect_zero(ret)
946 }
947
Pierre-Clément Tosi90e19352022-11-21 17:11:48 +0000948 /// Applies a DT overlay on the base DT.
949 ///
950 /// # Safety
951 ///
952 /// On failure, the library corrupts the DT and overlay so both must be discarded.
953 pub unsafe fn apply_overlay<'a>(&'a mut self, overlay: &'a mut Fdt) -> Result<&'a mut Self> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000954 let ret =
955 // SAFETY: Both pointers are valid because they come from references, and fdt_overlay_apply
956 // doesn't keep them after it returns. It may corrupt their contents if there is an error,
957 // but that's our caller's responsibility.
958 unsafe { libfdt_bindgen::fdt_overlay_apply(self.as_mut_ptr(), overlay.as_mut_ptr()) };
959 fdt_err_expect_zero(ret)?;
Pierre-Clément Tosi90e19352022-11-21 17:11:48 +0000960 Ok(self)
961 }
962
Alice Wang2422bdc2023-06-12 08:37:55 +0000963 /// Returns an iterator of memory banks specified the "/memory" node.
964 /// Throws an error when the "/memory" is not found in the device tree.
David Brazdil1baa9a92022-06-28 14:47:50 +0100965 ///
966 /// NOTE: This does not support individual "/memory@XXXX" banks.
Alice Wang2422bdc2023-06-12 08:37:55 +0000967 pub fn memory(&self) -> Result<MemRegIterator> {
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000968 let node = self.node(cstr!("/memory"))?.ok_or(FdtError::NotFound)?;
969 if node.device_type()? != Some(cstr!("memory")) {
Alice Wang2422bdc2023-06-12 08:37:55 +0000970 return Err(FdtError::BadValue);
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000971 }
Alice Wang2422bdc2023-06-12 08:37:55 +0000972 node.reg()?.ok_or(FdtError::BadValue).map(MemRegIterator::new)
973 }
974
975 /// Returns the first memory range in the `/memory` node.
976 pub fn first_memory_range(&self) -> Result<Range<usize>> {
977 self.memory()?.next().ok_or(FdtError::NotFound)
David Brazdil1baa9a92022-06-28 14:47:50 +0100978 }
979
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900980 /// Returns the standard /chosen node.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000981 pub fn chosen(&self) -> Result<Option<FdtNode>> {
Jaewan Kimb635bb02023-11-01 13:00:34 +0900982 self.node(cstr!("/chosen"))
David Brazdil1baa9a92022-06-28 14:47:50 +0100983 }
984
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900985 /// Returns the standard /chosen node as mutable.
Pierre-Clément Tosi4ba79662023-02-13 11:22:41 +0000986 pub fn chosen_mut(&mut self) -> Result<Option<FdtNodeMut>> {
Jaewan Kimb635bb02023-11-01 13:00:34 +0900987 self.node_mut(cstr!("/chosen"))
Pierre-Clément Tosi4ba79662023-02-13 11:22:41 +0000988 }
989
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900990 /// Returns the root node of the tree.
Pierre-Clément Tosi41c158e2022-11-21 19:16:25 +0000991 pub fn root(&self) -> Result<FdtNode> {
Jaewan Kimb635bb02023-11-01 13:00:34 +0900992 self.node(cstr!("/"))?.ok_or(FdtError::Internal)
Pierre-Clément Tosi41c158e2022-11-21 19:16:25 +0000993 }
994
Jaewan Kimf163d762023-11-01 13:12:50 +0900995 /// Returns the standard /__symbols__ node.
996 pub fn symbols(&self) -> Result<Option<FdtNode>> {
997 self.node(cstr!("/__symbols__"))
998 }
999
1000 /// Returns the standard /__symbols__ node as mutable
1001 pub fn symbols_mut(&mut self) -> Result<Option<FdtNodeMut>> {
1002 self.node_mut(cstr!("/__symbols__"))
1003 }
1004
Jaewan Kimb3dcfc22023-09-20 10:20:52 +09001005 /// Returns a tree node by its full path.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +00001006 pub fn node(&self, path: &CStr) -> Result<Option<FdtNode>> {
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +00001007 let offset = self.path_offset(path.to_bytes())?;
1008
1009 Ok(offset.map(|offset| FdtNode { fdt: self, offset }))
David Brazdil1baa9a92022-06-28 14:47:50 +01001010 }
1011
Pierre-Clément Tosi41c158e2022-11-21 19:16:25 +00001012 /// Iterate over nodes with a given compatible string.
1013 pub fn compatible_nodes<'a>(&'a self, compatible: &'a CStr) -> Result<CompatibleIterator<'a>> {
1014 CompatibleIterator::new(self, compatible)
1015 }
1016
Jaewan Kim17ba7a32023-10-19 13:25:15 +09001017 /// Returns max phandle in the tree.
1018 pub fn max_phandle(&self) -> Result<Phandle> {
1019 let mut phandle: u32 = 0;
1020 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
1021 let ret = unsafe { libfdt_bindgen::fdt_find_max_phandle(self.as_ptr(), &mut phandle) };
1022
1023 fdt_err_expect_zero(ret)?;
Pierre-Clément Tosieba27792023-10-30 12:04:12 +00001024 phandle.try_into()
Jaewan Kim17ba7a32023-10-19 13:25:15 +09001025 }
1026
1027 /// Returns a node with the phandle
1028 pub fn node_with_phandle(&self, phandle: Phandle) -> Result<Option<FdtNode>> {
Jaewan Kimc63246d2023-11-09 15:41:01 +09001029 let offset = self.node_offset_with_phandle(phandle)?;
1030 Ok(offset.map(|offset| FdtNode { fdt: self, offset }))
1031 }
1032
1033 /// Returns a mutable node with the phandle
1034 pub fn node_mut_with_phandle(&mut self, phandle: Phandle) -> Result<Option<FdtNodeMut>> {
1035 let offset = self.node_offset_with_phandle(phandle)?;
1036 Ok(offset.map(|offset| FdtNodeMut { fdt: self, offset }))
1037 }
1038
1039 fn node_offset_with_phandle(&self, phandle: Phandle) -> Result<Option<c_int>> {
1040 // SAFETY: Accesses are constrained to the DT totalsize.
Jaewan Kim17ba7a32023-10-19 13:25:15 +09001041 let ret = unsafe { libfdt_bindgen::fdt_node_offset_by_phandle(self.as_ptr(), phandle.0) };
Jaewan Kimc63246d2023-11-09 15:41:01 +09001042 fdt_err_or_option(ret)
Jaewan Kim17ba7a32023-10-19 13:25:15 +09001043 }
1044
Jaewan Kimb3dcfc22023-09-20 10:20:52 +09001045 /// Returns the mutable root node of the tree.
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +00001046 pub fn root_mut(&mut self) -> Result<FdtNodeMut> {
Jaewan Kimb635bb02023-11-01 13:00:34 +09001047 self.node_mut(cstr!("/"))?.ok_or(FdtError::Internal)
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +00001048 }
1049
Jaewan Kimb3dcfc22023-09-20 10:20:52 +09001050 /// Returns a mutable tree node by its full path.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +00001051 pub fn node_mut(&mut self, path: &CStr) -> Result<Option<FdtNodeMut>> {
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +00001052 let offset = self.path_offset(path.to_bytes())?;
1053
1054 Ok(offset.map(|offset| FdtNodeMut { fdt: self, offset }))
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +00001055 }
1056
Jaewan Kimb3dcfc22023-09-20 10:20:52 +09001057 /// Returns the device tree as a slice (may be smaller than the containing buffer).
Pierre-Clément Tosidb74cb12022-12-08 13:56:25 +00001058 pub fn as_slice(&self) -> &[u8] {
1059 &self.buffer[..self.totalsize()]
1060 }
1061
Jaewan Kimbab42592023-10-13 15:47:19 +09001062 fn path_offset(&self, path: &[u8]) -> Result<Option<c_int>> {
1063 let len = path.len().try_into().map_err(|_| FdtError::BadPath)?;
Andrew Walbran84b9a232023-07-05 14:01:40 +00001064 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor) and the
David Brazdil1baa9a92022-06-28 14:47:50 +01001065 // function respects the passed number of characters.
1066 let ret = unsafe {
1067 // *_namelen functions don't include the trailing nul terminator in 'len'.
Jaewan Kimbab42592023-10-13 15:47:19 +09001068 libfdt_bindgen::fdt_path_offset_namelen(self.as_ptr(), path.as_ptr().cast::<_>(), len)
David Brazdil1baa9a92022-06-28 14:47:50 +01001069 };
1070
Pierre-Clément Tosib244d932022-11-24 16:45:53 +00001071 fdt_err_or_option(ret)
David Brazdil1baa9a92022-06-28 14:47:50 +01001072 }
1073
1074 fn check_full(&self) -> Result<()> {
Andrew Walbran84b9a232023-07-05 14:01:40 +00001075 // SAFETY: Only performs read accesses within the limits of the slice. If successful, this
David Brazdil1baa9a92022-06-28 14:47:50 +01001076 // call guarantees to other unsafe calls that the header contains a valid totalsize (w.r.t.
1077 // 'len' i.e. the self.fdt slice) that those C functions can use to perform bounds
1078 // checking. The library doesn't maintain an internal state (such as pointers) between
1079 // calls as it expects the client code to keep track of the objects (DT, nodes, ...).
Pierre-Clément Tosi02017da2023-09-26 17:57:04 +01001080 let ret = unsafe { libfdt_bindgen::fdt_check_full(self.as_ptr(), self.capacity()) };
David Brazdil1baa9a92022-06-28 14:47:50 +01001081 fdt_err_expect_zero(ret)
1082 }
1083
Jaewan Kimaa638702023-09-19 13:34:01 +09001084 fn get_from_ptr(&self, ptr: *const c_void, len: usize) -> Result<&[u8]> {
1085 let ptr = ptr as usize;
1086 let offset = ptr.checked_sub(self.as_ptr() as usize).ok_or(FdtError::Internal)?;
1087 self.buffer.get(offset..(offset + len)).ok_or(FdtError::Internal)
1088 }
1089
Jaewan Kim72d10902023-10-12 21:59:26 +09001090 fn string(&self, offset: c_int) -> Result<&CStr> {
1091 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
1092 let res = unsafe { libfdt_bindgen::fdt_string(self.as_ptr(), offset) };
1093 if res.is_null() {
1094 return Err(FdtError::Internal);
1095 }
1096
1097 // SAFETY: Non-null return from fdt_string() is valid null-terminating string within FDT.
1098 Ok(unsafe { CStr::from_ptr(res) })
1099 }
1100
Jaewan Kimb3dcfc22023-09-20 10:20:52 +09001101 /// Returns a shared pointer to the device tree.
Pierre-Clément Tosi8036b4f2023-02-17 10:31:31 +00001102 pub fn as_ptr(&self) -> *const c_void {
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +00001103 self.buffer.as_ptr().cast()
David Brazdil1baa9a92022-06-28 14:47:50 +01001104 }
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +00001105
1106 fn as_mut_ptr(&mut self) -> *mut c_void {
Pierre-Clément Tosi0dcc75e2023-05-02 13:43:55 +00001107 self.buffer.as_mut_ptr().cast::<_>()
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +00001108 }
1109
1110 fn capacity(&self) -> usize {
Pierre-Clément Tosief2030e2022-11-28 11:21:20 +00001111 self.buffer.len()
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +00001112 }
Pierre-Clément Tosidb74cb12022-12-08 13:56:25 +00001113
1114 fn header(&self) -> &libfdt_bindgen::fdt_header {
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +00001115 let p = self.as_ptr().cast();
Andrew Walbran84b9a232023-07-05 14:01:40 +00001116 // SAFETY: A valid FDT (verified by constructor) must contain a valid fdt_header.
Pierre-Clément Tosi0dcc75e2023-05-02 13:43:55 +00001117 unsafe { &*p }
Pierre-Clément Tosidb74cb12022-12-08 13:56:25 +00001118 }
1119
1120 fn totalsize(&self) -> usize {
1121 u32::from_be(self.header().totalsize) as usize
1122 }
David Brazdil1baa9a92022-06-28 14:47:50 +01001123}