blob: 72cd3e9952a23943e0b586e1e4b1fa222db81747 [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 Tosifbb5ee22023-12-21 13:49:59 +000021mod libfdt;
Pierre-Clément Tosi99a76902023-12-21 10:30:37 +000022mod result;
Andrew Walbran55ad01b2022-12-05 17:00:40 +000023
Jaewan Kimfe06c852023-10-05 23:40:06 +090024pub use iterators::{
Jaewan Kimc9e14112023-12-04 17:05:27 +090025 AddressRange, CellIterator, CompatibleIterator, DescendantsIterator, MemRegIterator,
26 PropertyIterator, RangesIterator, Reg, RegIterator, SubnodeIterator,
Jaewan Kimfe06c852023-10-05 23:40:06 +090027};
Pierre-Clément Tosi99a76902023-12-21 10:30:37 +000028pub use result::{FdtError, Result};
Andrew Walbran55ad01b2022-12-05 17:00:40 +000029
David Brazdil1baa9a92022-06-28 14:47:50 +010030use core::ffi::{c_int, c_void, CStr};
Alice Wang2422bdc2023-06-12 08:37:55 +000031use core::ops::Range;
Jaewan Kim5b057772023-10-19 01:02:17 +090032use core::ptr;
Pierre-Clément Tosi1bf532b2023-11-13 11:06:20 +000033use cstr::cstr;
Pierre-Clément Tosi99a76902023-12-21 10:30:37 +000034use result::{fdt_err, fdt_err_expect_zero, fdt_err_or_option};
Pierre-Clément Tosic27c4272023-05-19 15:46:26 +000035use zerocopy::AsBytes as _;
David Brazdil1baa9a92022-06-28 14:47:50 +010036
David Brazdil1baa9a92022-06-28 14:47:50 +010037/// Value of a #address-cells property.
Andrew Walbranb39e6922022-12-05 17:01:20 +000038#[derive(Copy, Clone, Debug, Eq, PartialEq)]
David Brazdil1baa9a92022-06-28 14:47:50 +010039enum AddrCells {
40 Single = 1,
41 Double = 2,
Andrew Walbranb39e6922022-12-05 17:01:20 +000042 Triple = 3,
David Brazdil1baa9a92022-06-28 14:47:50 +010043}
44
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +000045impl TryFrom<usize> for AddrCells {
David Brazdil1baa9a92022-06-28 14:47:50 +010046 type Error = FdtError;
47
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +000048 fn try_from(value: usize) -> Result<Self> {
49 match value {
50 x if x == Self::Single as _ => Ok(Self::Single),
51 x if x == Self::Double as _ => Ok(Self::Double),
52 x if x == Self::Triple as _ => Ok(Self::Triple),
David Brazdil1baa9a92022-06-28 14:47:50 +010053 _ => Err(FdtError::BadNCells),
54 }
55 }
56}
57
58/// Value of a #size-cells property.
Andrew Walbranb39e6922022-12-05 17:01:20 +000059#[derive(Copy, Clone, Debug, Eq, PartialEq)]
David Brazdil1baa9a92022-06-28 14:47:50 +010060enum SizeCells {
61 None = 0,
62 Single = 1,
63 Double = 2,
64}
65
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +000066impl TryFrom<usize> for SizeCells {
David Brazdil1baa9a92022-06-28 14:47:50 +010067 type Error = FdtError;
68
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +000069 fn try_from(value: usize) -> Result<Self> {
70 match value {
71 x if x == Self::None as _ => Ok(Self::None),
72 x if x == Self::Single as _ => Ok(Self::Single),
73 x if x == Self::Double as _ => Ok(Self::Double),
David Brazdil1baa9a92022-06-28 14:47:50 +010074 _ => Err(FdtError::BadNCells),
75 }
76 }
77}
78
Jaewan Kim72d10902023-10-12 21:59:26 +090079/// DT property wrapper to abstract endianess changes
80#[repr(transparent)]
81#[derive(Debug)]
82struct FdtPropertyStruct(libfdt_bindgen::fdt_property);
83
Pierre-Clément Tosidf3037f2024-01-22 15:41:43 +000084impl AsRef<FdtPropertyStruct> for libfdt_bindgen::fdt_property {
85 fn as_ref(&self) -> &FdtPropertyStruct {
86 let ptr = self as *const _ as *const _;
87 // SAFETY: Types have the same layout (transparent) so the valid reference remains valid.
88 unsafe { &*ptr }
89 }
90}
91
Jaewan Kim72d10902023-10-12 21:59:26 +090092impl FdtPropertyStruct {
93 fn from_offset(fdt: &Fdt, offset: c_int) -> Result<&Self> {
94 let mut len = 0;
95 let prop =
96 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
97 unsafe { libfdt_bindgen::fdt_get_property_by_offset(fdt.as_ptr(), offset, &mut len) };
98 if prop.is_null() {
99 fdt_err(len)?;
100 return Err(FdtError::Internal); // shouldn't happen.
101 }
102 // SAFETY: prop is only returned when it points to valid libfdt_bindgen.
Pierre-Clément Tosidf3037f2024-01-22 15:41:43 +0000103 let prop = unsafe { &*prop };
104 Ok(prop.as_ref())
Jaewan Kim72d10902023-10-12 21:59:26 +0900105 }
106
107 fn name_offset(&self) -> c_int {
108 u32::from_be(self.0.nameoff).try_into().unwrap()
109 }
110
111 fn data_len(&self) -> usize {
112 u32::from_be(self.0.len).try_into().unwrap()
113 }
114
115 fn data_ptr(&self) -> *const c_void {
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000116 self.0.data.as_ptr().cast()
Jaewan Kim72d10902023-10-12 21:59:26 +0900117 }
118}
119
120/// DT property.
121#[derive(Clone, Copy, Debug)]
122pub struct FdtProperty<'a> {
123 fdt: &'a Fdt,
124 offset: c_int,
125 property: &'a FdtPropertyStruct,
126}
127
128impl<'a> FdtProperty<'a> {
129 fn new(fdt: &'a Fdt, offset: c_int) -> Result<Self> {
130 let property = FdtPropertyStruct::from_offset(fdt, offset)?;
131 Ok(Self { fdt, offset, property })
132 }
133
134 /// Returns the property name
135 pub fn name(&self) -> Result<&'a CStr> {
136 self.fdt.string(self.property.name_offset())
137 }
138
139 /// Returns the property value
140 pub fn value(&self) -> Result<&'a [u8]> {
141 self.fdt.get_from_ptr(self.property.data_ptr(), self.property.data_len())
142 }
143
144 fn next_property(&self) -> Result<Option<Self>> {
145 let ret =
146 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
147 unsafe { libfdt_bindgen::fdt_next_property_offset(self.fdt.as_ptr(), self.offset) };
148
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000149 if let Some(offset) = fdt_err_or_option(ret)? {
150 Ok(Some(Self::new(self.fdt, offset)?))
151 } else {
152 Ok(None)
153 }
Jaewan Kim72d10902023-10-12 21:59:26 +0900154 }
155}
156
David Brazdil1baa9a92022-06-28 14:47:50 +0100157/// DT node.
Alice Wang9d4df702023-05-25 14:14:12 +0000158#[derive(Clone, Copy, Debug)]
David Brazdil1baa9a92022-06-28 14:47:50 +0100159pub struct FdtNode<'a> {
160 fdt: &'a Fdt,
161 offset: c_int,
162}
163
164impl<'a> FdtNode<'a> {
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900165 /// Returns parent node.
David Brazdil1baa9a92022-06-28 14:47:50 +0100166 pub fn parent(&self) -> Result<Self> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000167 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
David Brazdil1baa9a92022-06-28 14:47:50 +0100168 let ret = unsafe { libfdt_bindgen::fdt_parent_offset(self.fdt.as_ptr(), self.offset) };
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000169 let offset = fdt_err(ret)?;
David Brazdil1baa9a92022-06-28 14:47:50 +0100170
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000171 Ok(Self { fdt: self.fdt, offset })
David Brazdil1baa9a92022-06-28 14:47:50 +0100172 }
173
Jaewan Kim5b057772023-10-19 01:02:17 +0900174 /// Returns supernode with depth. Note that root is at depth 0.
175 pub fn supernode_at_depth(&self, depth: usize) -> Result<Self> {
176 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
177 let ret = unsafe {
178 libfdt_bindgen::fdt_supernode_atdepth_offset(
179 self.fdt.as_ptr(),
180 self.offset,
181 depth.try_into().unwrap(),
182 ptr::null_mut(),
183 )
184 };
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000185 let offset = fdt_err(ret)?;
Jaewan Kim5b057772023-10-19 01:02:17 +0900186
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000187 Ok(Self { fdt: self.fdt, offset })
Jaewan Kim5b057772023-10-19 01:02:17 +0900188 }
189
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900190 /// Returns the standard (deprecated) device_type <string> property.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000191 pub fn device_type(&self) -> Result<Option<&CStr>> {
Jaewan Kimb635bb02023-11-01 13:00:34 +0900192 self.getprop_str(cstr!("device_type"))
David Brazdil1baa9a92022-06-28 14:47:50 +0100193 }
194
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900195 /// Returns the standard reg <prop-encoded-array> property.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000196 pub fn reg(&self) -> Result<Option<RegIterator<'a>>> {
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000197 if let Some(cells) = self.getprop_cells(cstr!("reg"))? {
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000198 let parent = self.parent()?;
David Brazdil1baa9a92022-06-28 14:47:50 +0100199
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000200 let addr_cells = parent.address_cells()?;
201 let size_cells = parent.size_cells()?;
202
203 Ok(Some(RegIterator::new(cells, addr_cells, size_cells)))
204 } else {
205 Ok(None)
206 }
David Brazdil1baa9a92022-06-28 14:47:50 +0100207 }
208
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900209 /// Returns the standard ranges property.
Andrew Walbranb39e6922022-12-05 17:01:20 +0000210 pub fn ranges<A, P, S>(&self) -> Result<Option<RangesIterator<'a, A, P, S>>> {
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000211 if let Some(cells) = self.getprop_cells(cstr!("ranges"))? {
Andrew Walbranb39e6922022-12-05 17:01:20 +0000212 let parent = self.parent()?;
213 let addr_cells = self.address_cells()?;
214 let parent_addr_cells = parent.address_cells()?;
215 let size_cells = self.size_cells()?;
216 Ok(Some(RangesIterator::<A, P, S>::new(
217 cells,
218 addr_cells,
219 parent_addr_cells,
220 size_cells,
221 )))
222 } else {
223 Ok(None)
224 }
225 }
226
Jaewan Kimaa638702023-09-19 13:34:01 +0900227 /// Returns the node name.
228 pub fn name(&self) -> Result<&'a CStr> {
229 let mut len: c_int = 0;
230 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor). On success, the
231 // function returns valid null terminating string and otherwise returned values are dropped.
232 let name = unsafe { libfdt_bindgen::fdt_get_name(self.fdt.as_ptr(), self.offset, &mut len) }
233 as *const c_void;
234 let len = usize::try_from(fdt_err(len)?).unwrap();
235 let name = self.fdt.get_from_ptr(name, len + 1)?;
236 CStr::from_bytes_with_nul(name).map_err(|_| FdtError::Internal)
237 }
238
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900239 /// Returns the value of a given <string> property.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000240 pub fn getprop_str(&self, name: &CStr) -> Result<Option<&CStr>> {
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000241 if let Some(bytes) = self.getprop(name)? {
242 Ok(Some(CStr::from_bytes_with_nul(bytes).map_err(|_| FdtError::BadValue)?))
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000243 } else {
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000244 Ok(None)
245 }
David Brazdil1baa9a92022-06-28 14:47:50 +0100246 }
247
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900248 /// Returns the value of a given property as an array of cells.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000249 pub fn getprop_cells(&self, name: &CStr) -> Result<Option<CellIterator<'a>>> {
250 if let Some(cells) = self.getprop(name)? {
251 Ok(Some(CellIterator::new(cells)))
252 } else {
253 Ok(None)
254 }
David Brazdil1baa9a92022-06-28 14:47:50 +0100255 }
256
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900257 /// Returns the value of a given <u32> property.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000258 pub fn getprop_u32(&self, name: &CStr) -> Result<Option<u32>> {
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000259 if let Some(bytes) = self.getprop(name)? {
260 Ok(Some(u32::from_be_bytes(bytes.try_into().map_err(|_| FdtError::BadValue)?)))
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000261 } else {
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000262 Ok(None)
263 }
David Brazdil1baa9a92022-06-28 14:47:50 +0100264 }
265
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900266 /// Returns the value of a given <u64> property.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000267 pub fn getprop_u64(&self, name: &CStr) -> Result<Option<u64>> {
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000268 if let Some(bytes) = self.getprop(name)? {
269 Ok(Some(u64::from_be_bytes(bytes.try_into().map_err(|_| FdtError::BadValue)?)))
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000270 } else {
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000271 Ok(None)
272 }
David Brazdil1baa9a92022-06-28 14:47:50 +0100273 }
274
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900275 /// Returns the value of a given property.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000276 pub fn getprop(&self, name: &CStr) -> Result<Option<&'a [u8]>> {
Jiyong Park9c63cd12023-03-21 17:53:07 +0900277 if let Some((prop, len)) = Self::getprop_internal(self.fdt, self.offset, name)? {
Jaewan Kimaa638702023-09-19 13:34:01 +0900278 Ok(Some(self.fdt.get_from_ptr(prop, len)?))
Jiyong Park9c63cd12023-03-21 17:53:07 +0900279 } else {
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000280 Ok(None)
Jiyong Park9c63cd12023-03-21 17:53:07 +0900281 }
282 }
283
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900284 /// Returns the pointer and size of the property named `name`, in a node at offset `offset`, in
Jiyong Park9c63cd12023-03-21 17:53:07 +0900285 /// a device tree `fdt`. The pointer is guaranteed to be non-null, in which case error returns.
286 fn getprop_internal(
287 fdt: &'a Fdt,
288 offset: c_int,
289 name: &CStr,
290 ) -> Result<Option<(*const c_void, usize)>> {
David Brazdil1baa9a92022-06-28 14:47:50 +0100291 let mut len: i32 = 0;
Andrew Walbran84b9a232023-07-05 14:01:40 +0000292 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor) and the
David Brazdil1baa9a92022-06-28 14:47:50 +0100293 // function respects the passed number of characters.
294 let prop = unsafe {
295 libfdt_bindgen::fdt_getprop_namelen(
Jiyong Park9c63cd12023-03-21 17:53:07 +0900296 fdt.as_ptr(),
297 offset,
David Brazdil1baa9a92022-06-28 14:47:50 +0100298 name.as_ptr(),
299 // *_namelen functions don't include the trailing nul terminator in 'len'.
300 name.to_bytes().len().try_into().map_err(|_| FdtError::BadPath)?,
301 &mut len as *mut i32,
302 )
303 } as *const u8;
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000304
305 let Some(len) = fdt_err_or_option(len)? else {
306 return Ok(None); // Property was not found.
307 };
Jaewan Kimaa638702023-09-19 13:34:01 +0900308 let len = usize::try_from(len).unwrap();
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000309
David Brazdil1baa9a92022-06-28 14:47:50 +0100310 if prop.is_null() {
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000311 // We expected an error code in len but still received a valid value?!
312 return Err(FdtError::Internal);
David Brazdil1baa9a92022-06-28 14:47:50 +0100313 }
Jiyong Park9c63cd12023-03-21 17:53:07 +0900314 Ok(Some((prop.cast::<c_void>(), len)))
David Brazdil1baa9a92022-06-28 14:47:50 +0100315 }
316
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900317 /// Returns reference to the containing device tree.
David Brazdil1baa9a92022-06-28 14:47:50 +0100318 pub fn fdt(&self) -> &Fdt {
319 self.fdt
320 }
321
Alice Wang474c0ee2023-09-14 12:52:33 +0000322 /// Returns the compatible node of the given name that is next after this node.
323 pub fn next_compatible(self, compatible: &CStr) -> Result<Option<Self>> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000324 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
Pierre-Clément Tosi41c158e2022-11-21 19:16:25 +0000325 let ret = unsafe {
326 libfdt_bindgen::fdt_node_offset_by_compatible(
327 self.fdt.as_ptr(),
328 self.offset,
329 compatible.as_ptr(),
330 )
331 };
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000332 let offset = fdt_err_or_option(ret)?;
Pierre-Clément Tosi41c158e2022-11-21 19:16:25 +0000333
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000334 Ok(offset.map(|offset| Self { fdt: self.fdt, offset }))
Pierre-Clément Tosi41c158e2022-11-21 19:16:25 +0000335 }
336
Alice Wang474c0ee2023-09-14 12:52:33 +0000337 /// Returns the first range of `reg` in this node.
338 pub fn first_reg(&self) -> Result<Reg<u64>> {
339 self.reg()?.ok_or(FdtError::NotFound)?.next().ok_or(FdtError::NotFound)
340 }
341
David Brazdil1baa9a92022-06-28 14:47:50 +0100342 fn address_cells(&self) -> Result<AddrCells> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000343 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor).
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000344 let ret = unsafe { libfdt_bindgen::fdt_address_cells(self.fdt.as_ptr(), self.offset) };
345
346 usize::try_from(ret).map_err(|_| FdtError::Internal)?.try_into()
David Brazdil1baa9a92022-06-28 14:47:50 +0100347 }
348
349 fn size_cells(&self) -> Result<SizeCells> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000350 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor).
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000351 let ret = unsafe { libfdt_bindgen::fdt_size_cells(self.fdt.as_ptr(), self.offset) };
352
353 usize::try_from(ret).map_err(|_| FdtError::Internal)?.try_into()
David Brazdil1baa9a92022-06-28 14:47:50 +0100354 }
Jaewan Kimbc828d72023-09-19 15:52:08 +0900355
356 /// Returns an iterator of subnodes
Jaewan Kim4a34b0d2024-01-19 13:17:47 +0900357 pub fn subnodes(&self) -> Result<SubnodeIterator<'a>> {
Jaewan Kimbc828d72023-09-19 15:52:08 +0900358 SubnodeIterator::new(self)
359 }
360
361 fn first_subnode(&self) -> Result<Option<Self>> {
362 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
363 let ret = unsafe { libfdt_bindgen::fdt_first_subnode(self.fdt.as_ptr(), self.offset) };
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000364 let offset = fdt_err_or_option(ret)?;
Jaewan Kimbc828d72023-09-19 15:52:08 +0900365
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000366 Ok(offset.map(|offset| Self { fdt: self.fdt, offset }))
Jaewan Kimbc828d72023-09-19 15:52:08 +0900367 }
368
369 fn next_subnode(&self) -> Result<Option<Self>> {
370 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
371 let ret = unsafe { libfdt_bindgen::fdt_next_subnode(self.fdt.as_ptr(), self.offset) };
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000372 let offset = fdt_err_or_option(ret)?;
Jaewan Kimbc828d72023-09-19 15:52:08 +0900373
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000374 Ok(offset.map(|offset| Self { fdt: self.fdt, offset }))
Jaewan Kimbc828d72023-09-19 15:52:08 +0900375 }
Jaewan Kim72d10902023-10-12 21:59:26 +0900376
Jaewan Kimc9e14112023-12-04 17:05:27 +0900377 /// Returns an iterator of descendants
Jaewan Kim1eab7232024-01-04 09:46:16 +0900378 pub fn descendants(&self) -> DescendantsIterator<'a> {
Jaewan Kimc9e14112023-12-04 17:05:27 +0900379 DescendantsIterator::new(self)
380 }
381
382 fn next_node(&self, depth: usize) -> Result<Option<(Self, usize)>> {
383 let mut next_depth: c_int = depth.try_into().unwrap();
384 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
385 let ret = unsafe {
386 libfdt_bindgen::fdt_next_node(self.fdt.as_ptr(), self.offset, &mut next_depth)
387 };
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000388 let Ok(depth) = usize::try_from(next_depth) else {
Jaewan Kimc9e14112023-12-04 17:05:27 +0900389 return Ok(None);
390 };
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000391 if let Some(offset) = fdt_err_or_option(ret)? {
392 Ok(Some((Self { fdt: self.fdt, offset }, depth)))
393 } else {
394 Ok(None)
395 }
Jaewan Kimc9e14112023-12-04 17:05:27 +0900396 }
397
Jaewan Kim72d10902023-10-12 21:59:26 +0900398 /// Returns an iterator of properties
399 pub fn properties(&'a self) -> Result<PropertyIterator<'a>> {
400 PropertyIterator::new(self)
401 }
402
403 fn first_property(&self) -> Result<Option<FdtProperty<'a>>> {
404 let ret =
405 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
406 unsafe { libfdt_bindgen::fdt_first_property_offset(self.fdt.as_ptr(), self.offset) };
407
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000408 if let Some(offset) = fdt_err_or_option(ret)? {
409 Ok(Some(FdtProperty::new(self.fdt, offset)?))
410 } else {
411 Ok(None)
412 }
Jaewan Kim72d10902023-10-12 21:59:26 +0900413 }
Jaewan Kimf34f4b82023-11-03 19:38:38 +0900414
415 /// Returns the phandle
416 pub fn get_phandle(&self) -> Result<Option<Phandle>> {
417 // This rewrites the fdt_get_phandle() because it doesn't return error code.
418 if let Some(prop) = self.getprop_u32(cstr!("phandle"))? {
419 Ok(Some(prop.try_into()?))
420 } else if let Some(prop) = self.getprop_u32(cstr!("linux,phandle"))? {
421 Ok(Some(prop.try_into()?))
422 } else {
423 Ok(None)
424 }
425 }
Jaewan Kim52026012023-12-13 13:49:28 +0900426
427 /// Returns the subnode of the given name. The name doesn't need to be nul-terminated.
428 pub fn subnode(&self, name: &CStr) -> Result<Option<Self>> {
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000429 let name = name.to_bytes();
430 let offset = self.subnode_offset(name)?;
431
Jaewan Kim52026012023-12-13 13:49:28 +0900432 Ok(offset.map(|offset| Self { fdt: self.fdt, offset }))
433 }
434
435 /// Returns the subnode of the given name bytes
436 pub fn subnode_with_name_bytes(&self, name: &[u8]) -> Result<Option<Self>> {
437 let offset = self.subnode_offset(name)?;
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000438
Jaewan Kim52026012023-12-13 13:49:28 +0900439 Ok(offset.map(|offset| Self { fdt: self.fdt, offset }))
440 }
441
442 fn subnode_offset(&self, name: &[u8]) -> Result<Option<c_int>> {
443 let namelen = name.len().try_into().unwrap();
444 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor).
445 let ret = unsafe {
446 libfdt_bindgen::fdt_subnode_offset_namelen(
447 self.fdt.as_ptr(),
448 self.offset,
449 name.as_ptr().cast::<_>(),
450 namelen,
451 )
452 };
453 fdt_err_or_option(ret)
454 }
David Brazdil1baa9a92022-06-28 14:47:50 +0100455}
456
Pierre-Clément Tosi504b4302023-10-30 12:22:50 +0000457impl<'a> PartialEq for FdtNode<'a> {
458 fn eq(&self, other: &Self) -> bool {
459 self.fdt.as_ptr() == other.fdt.as_ptr() && self.offset == other.offset
460 }
461}
462
Jaewan Kim17ba7a32023-10-19 13:25:15 +0900463/// Phandle of a FDT node
464#[repr(transparent)]
Jaewan Kim55f438c2023-11-15 01:24:36 +0900465#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
Jaewan Kim17ba7a32023-10-19 13:25:15 +0900466pub struct Phandle(u32);
467
468impl Phandle {
Pierre-Clément Tosieba27792023-10-30 12:04:12 +0000469 /// Minimum valid value for device tree phandles.
470 pub const MIN: Self = Self(1);
471 /// Maximum valid value for device tree phandles.
472 pub const MAX: Self = Self(libfdt_bindgen::FDT_MAX_PHANDLE);
473
Jaewan Kim17ba7a32023-10-19 13:25:15 +0900474 /// Creates a new Phandle
Pierre-Clément Tosieba27792023-10-30 12:04:12 +0000475 pub const fn new(value: u32) -> Option<Self> {
476 if Self::MIN.0 <= value && value <= Self::MAX.0 {
477 Some(Self(value))
478 } else {
479 None
Jaewan Kim17ba7a32023-10-19 13:25:15 +0900480 }
Jaewan Kim17ba7a32023-10-19 13:25:15 +0900481 }
482}
483
484impl From<Phandle> for u32 {
485 fn from(phandle: Phandle) -> u32 {
486 phandle.0
487 }
488}
489
Pierre-Clément Tosieba27792023-10-30 12:04:12 +0000490impl TryFrom<u32> for Phandle {
491 type Error = FdtError;
492
493 fn try_from(value: u32) -> Result<Self> {
494 Self::new(value).ok_or(FdtError::BadPhandle)
495 }
496}
497
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000498/// Mutable FDT node.
Pierre-Clément Tosi504b4302023-10-30 12:22:50 +0000499#[derive(Debug)]
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000500pub struct FdtNodeMut<'a> {
501 fdt: &'a mut Fdt,
502 offset: c_int,
503}
504
505impl<'a> FdtNodeMut<'a> {
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900506 /// Appends a property name-value (possibly empty) pair to the given node.
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000507 pub fn appendprop<T: AsRef<[u8]>>(&mut self, name: &CStr, value: &T) -> Result<()> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000508 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor).
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000509 let ret = unsafe {
510 libfdt_bindgen::fdt_appendprop(
511 self.fdt.as_mut_ptr(),
512 self.offset,
513 name.as_ptr(),
514 value.as_ref().as_ptr().cast::<c_void>(),
515 value.as_ref().len().try_into().map_err(|_| FdtError::BadValue)?,
516 )
517 };
518
519 fdt_err_expect_zero(ret)
520 }
521
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900522 /// Appends a (address, size) pair property to the given node.
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000523 pub fn appendprop_addrrange(&mut self, name: &CStr, addr: u64, size: u64) -> Result<()> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000524 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor).
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000525 let ret = unsafe {
526 libfdt_bindgen::fdt_appendprop_addrrange(
527 self.fdt.as_mut_ptr(),
528 self.parent()?.offset,
529 self.offset,
530 name.as_ptr(),
531 addr,
532 size,
533 )
534 };
535
536 fdt_err_expect_zero(ret)
537 }
538
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900539 /// Sets a property name-value pair to the given node.
540 ///
541 /// This may create a new prop or replace existing value.
Jaewan Kimba8929b2023-01-13 11:13:29 +0900542 pub fn setprop(&mut self, name: &CStr, value: &[u8]) -> Result<()> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000543 // SAFETY: New value size is constrained to the DT totalsize
Jaewan Kimba8929b2023-01-13 11:13:29 +0900544 // (validated by underlying libfdt).
545 let ret = unsafe {
546 libfdt_bindgen::fdt_setprop(
547 self.fdt.as_mut_ptr(),
548 self.offset,
549 name.as_ptr(),
550 value.as_ptr().cast::<c_void>(),
551 value.len().try_into().map_err(|_| FdtError::BadValue)?,
552 )
553 };
554
555 fdt_err_expect_zero(ret)
556 }
557
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900558 /// Sets the value of the given property with the given value, and ensure that the given
559 /// value has the same length as the current value length.
560 ///
561 /// This can only be used to replace existing value.
Jiyong Park9c63cd12023-03-21 17:53:07 +0900562 pub fn setprop_inplace(&mut self, name: &CStr, value: &[u8]) -> Result<()> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000563 // SAFETY: fdt size is not altered
Jiyong Park9c63cd12023-03-21 17:53:07 +0900564 let ret = unsafe {
565 libfdt_bindgen::fdt_setprop_inplace(
566 self.fdt.as_mut_ptr(),
567 self.offset,
568 name.as_ptr(),
569 value.as_ptr().cast::<c_void>(),
570 value.len().try_into().map_err(|_| FdtError::BadValue)?,
571 )
572 };
573
574 fdt_err_expect_zero(ret)
575 }
576
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900577 /// Sets the value of the given (address, size) pair property with the given value, and
578 /// ensure that the given value has the same length as the current value length.
579 ///
580 /// This can only be used to replace existing value.
Pierre-Clément Tosic27c4272023-05-19 15:46:26 +0000581 pub fn setprop_addrrange_inplace(&mut self, name: &CStr, addr: u64, size: u64) -> Result<()> {
582 let pair = [addr.to_be(), size.to_be()];
583 self.setprop_inplace(name, pair.as_bytes())
584 }
585
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900586 /// Sets a flag-like empty property.
587 ///
588 /// This may create a new prop or replace existing value.
Pierre-Clément Tosi4ba79662023-02-13 11:22:41 +0000589 pub fn setprop_empty(&mut self, name: &CStr) -> Result<()> {
590 self.setprop(name, &[])
591 }
592
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900593 /// Deletes the given property.
Pierre-Clément Tosi4ba79662023-02-13 11:22:41 +0000594 pub fn delprop(&mut self, name: &CStr) -> Result<()> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000595 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor) when the
Pierre-Clément Tosi4ba79662023-02-13 11:22:41 +0000596 // library locates the node's property. Removing the property may shift the offsets of
597 // other nodes and properties but the borrow checker should prevent this function from
598 // being called when FdtNode instances are in use.
599 let ret = unsafe {
600 libfdt_bindgen::fdt_delprop(self.fdt.as_mut_ptr(), self.offset, name.as_ptr())
601 };
602
603 fdt_err_expect_zero(ret)
604 }
605
Jaewan Kim4ae0e712023-10-19 14:16:17 +0900606 /// Deletes the given property effectively from DT, by setting it with FDT_NOP.
Pierre-Clément Tosibe3a97b2023-05-19 14:56:23 +0000607 pub fn nop_property(&mut self, name: &CStr) -> Result<()> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000608 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor) when the
Pierre-Clément Tosibe3a97b2023-05-19 14:56:23 +0000609 // library locates the node's property.
610 let ret = unsafe {
611 libfdt_bindgen::fdt_nop_property(self.fdt.as_mut_ptr(), self.offset, name.as_ptr())
612 };
613
614 fdt_err_expect_zero(ret)
615 }
616
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900617 /// Trims the size of the given property to new_size.
Jiyong Park9c63cd12023-03-21 17:53:07 +0900618 pub fn trimprop(&mut self, name: &CStr, new_size: usize) -> Result<()> {
619 let (prop, len) =
620 FdtNode::getprop_internal(self.fdt, self.offset, name)?.ok_or(FdtError::NotFound)?;
621 if len == new_size {
622 return Ok(());
623 }
624 if new_size > len {
625 return Err(FdtError::NoSpace);
626 }
627
Andrew Walbran84b9a232023-07-05 14:01:40 +0000628 // SAFETY: new_size is smaller than the old size
Jiyong Park9c63cd12023-03-21 17:53:07 +0900629 let ret = unsafe {
630 libfdt_bindgen::fdt_setprop(
631 self.fdt.as_mut_ptr(),
632 self.offset,
633 name.as_ptr(),
634 prop.cast::<c_void>(),
635 new_size.try_into().map_err(|_| FdtError::BadValue)?,
636 )
637 };
638
639 fdt_err_expect_zero(ret)
640 }
641
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900642 /// Returns reference to the containing device tree.
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000643 pub fn fdt(&mut self) -> &mut Fdt {
644 self.fdt
645 }
646
Jaewan Kimf72f4f22023-11-03 19:21:34 +0900647 /// Returns immutable FdtNode of this node.
648 pub fn as_node(&self) -> FdtNode {
649 FdtNode { fdt: self.fdt, offset: self.offset }
650 }
651
Jaewan Kime6363422024-01-19 14:00:00 +0900652 /// Adds new subnodes to the given node.
653 pub fn add_subnodes(&mut self, names: &[&CStr]) -> Result<()> {
654 for name in names {
655 self.add_subnode_offset(name.to_bytes())?;
656 }
657 Ok(())
658 }
659
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900660 /// 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 +0000661 pub fn add_subnode(&'a mut self, name: &CStr) -> Result<Self> {
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000662 let name = name.to_bytes();
663 let offset = self.add_subnode_offset(name)?;
664
Jaewan Kim5ab13582023-10-20 20:56:27 +0900665 Ok(Self { fdt: self.fdt, offset })
666 }
667
668 /// Adds a new subnode to the given node with name and namelen, and returns it as a FdtNodeMut
669 /// on success.
670 pub fn add_subnode_with_namelen(&'a mut self, name: &CStr, namelen: usize) -> Result<Self> {
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000671 let name = &name.to_bytes()[..namelen];
672 let offset = self.add_subnode_offset(name)?;
673
Jaewan Kim5ab13582023-10-20 20:56:27 +0900674 Ok(Self { fdt: self.fdt, offset })
675 }
676
677 fn add_subnode_offset(&mut self, name: &[u8]) -> Result<c_int> {
678 let namelen = name.len().try_into().unwrap();
Andrew Walbran84b9a232023-07-05 14:01:40 +0000679 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor).
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000680 let ret = unsafe {
Jaewan Kim5ab13582023-10-20 20:56:27 +0900681 libfdt_bindgen::fdt_add_subnode_namelen(
682 self.fdt.as_mut_ptr(),
683 self.offset,
684 name.as_ptr().cast::<_>(),
685 namelen,
686 )
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000687 };
Jaewan Kim5ab13582023-10-20 20:56:27 +0900688 fdt_err(ret)
689 }
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000690
Jaewan Kim5f1a6032023-12-18 15:17:58 +0900691 /// Returns the first subnode of this
692 pub fn first_subnode(&'a mut self) -> Result<Option<Self>> {
693 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
694 let ret = unsafe { libfdt_bindgen::fdt_first_subnode(self.fdt.as_ptr(), self.offset) };
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000695 let offset = fdt_err_or_option(ret)?;
Jaewan Kim5f1a6032023-12-18 15:17:58 +0900696
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000697 Ok(offset.map(|offset| Self { fdt: self.fdt, offset }))
Jaewan Kim5f1a6032023-12-18 15:17:58 +0900698 }
699
700 /// Returns the next subnode that shares the same parent with this
701 pub fn next_subnode(self) -> Result<Option<Self>> {
702 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
703 let ret = unsafe { libfdt_bindgen::fdt_next_subnode(self.fdt.as_ptr(), self.offset) };
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000704 let offset = fdt_err_or_option(ret)?;
Jaewan Kim5f1a6032023-12-18 15:17:58 +0900705
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000706 Ok(offset.map(|offset| Self { fdt: self.fdt, offset }))
Jaewan Kim5f1a6032023-12-18 15:17:58 +0900707 }
708
709 /// Deletes the current node and returns the next subnode
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000710 pub fn delete_and_next_subnode(self) -> Result<Option<Self>> {
Jaewan Kim5f1a6032023-12-18 15:17:58 +0900711 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
712 let ret = unsafe { libfdt_bindgen::fdt_next_subnode(self.fdt.as_ptr(), self.offset) };
713
714 let next_offset = fdt_err_or_option(ret)?;
715
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000716 self.delete_and_next(next_offset)
Jaewan Kim5f1a6032023-12-18 15:17:58 +0900717 }
718
Jaewan Kim28a13ea2024-01-04 09:22:40 +0900719 fn next_node_offset(&self, depth: usize) -> Result<Option<(c_int, usize)>> {
720 let mut next_depth: c_int = depth.try_into().or(Err(FdtError::BadValue))?;
721 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
722 let ret = unsafe {
723 libfdt_bindgen::fdt_next_node(self.fdt.as_ptr(), self.offset, &mut next_depth)
724 };
725 let Ok(next_depth) = usize::try_from(next_depth) else {
726 return Ok(None);
727 };
728 Ok(fdt_err_or_option(ret)?.map(|offset| (offset, next_depth)))
729 }
730
731 /// Returns the next node
732 pub fn next_node(self, depth: usize) -> Result<Option<(Self, usize)>> {
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000733 let next = self.next_node_offset(depth)?;
734
735 Ok(next.map(|(offset, depth)| (Self { fdt: self.fdt, offset }, depth)))
Jaewan Kim28a13ea2024-01-04 09:22:40 +0900736 }
737
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000738 fn next_node_skip_subnodes(&mut self, depth: usize) -> Result<Option<(c_int, usize)>> {
Jaewan Kim28a13ea2024-01-04 09:22:40 +0900739 let mut iter = self.next_node_offset(depth)?;
740 while let Some((descendant_offset, descendant_depth)) = iter {
741 if descendant_depth <= depth {
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000742 return Ok(Some((descendant_offset, descendant_depth)));
Jaewan Kim28a13ea2024-01-04 09:22:40 +0900743 }
744 let descendant = FdtNodeMut { fdt: self.fdt, offset: descendant_offset };
745 iter = descendant.next_node_offset(descendant_depth)?;
746 }
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000747
748 Ok(None)
749 }
750
751 /// Deletes this and returns the next node
752 pub fn delete_and_next_node(mut self, depth: usize) -> Result<Option<(Self, usize)>> {
753 let next_node = self.next_node_skip_subnodes(depth)?;
754 if let Some((offset, depth)) = next_node {
755 let next_node = self.delete_and_next(Some(offset))?.unwrap();
756 Ok(Some((next_node, depth)))
757 } else {
758 Ok(None)
759 }
Jaewan Kim28a13ea2024-01-04 09:22:40 +0900760 }
761
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000762 fn parent(&'a self) -> Result<FdtNode<'a>> {
Pierre-Clément Tosidf3037f2024-01-22 15:41:43 +0000763 self.as_node().parent()
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000764 }
Jiyong Park9c63cd12023-03-21 17:53:07 +0900765
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900766 /// Returns the compatible node of the given name that is next after this node.
Jiyong Park9c63cd12023-03-21 17:53:07 +0900767 pub fn next_compatible(self, compatible: &CStr) -> Result<Option<Self>> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000768 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
Jiyong Park9c63cd12023-03-21 17:53:07 +0900769 let ret = unsafe {
770 libfdt_bindgen::fdt_node_offset_by_compatible(
771 self.fdt.as_ptr(),
772 self.offset,
773 compatible.as_ptr(),
774 )
775 };
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000776 let offset = fdt_err_or_option(ret)?;
Jiyong Park9c63cd12023-03-21 17:53:07 +0900777
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000778 Ok(offset.map(|offset| Self { fdt: self.fdt, offset }))
Jiyong Park9c63cd12023-03-21 17:53:07 +0900779 }
780
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900781 /// Deletes the node effectively by overwriting this node and its subtree with nop tags.
782 /// Returns the next compatible node of the given name.
Jiyong Park9c63cd12023-03-21 17:53:07 +0900783 // Side note: without this, filterint out excessive compatible nodes from the DT is impossible.
784 // The reason is that libfdt ensures that the node from where the search for the next
785 // compatible node is started is always a valid one -- except for the special case of offset =
786 // -1 which is to find the first compatible node. So, we can't delete a node and then find the
787 // next compatible node from it.
788 //
789 // We can't do in the opposite direction either. If we call next_compatible to find the next
790 // node, and delete the current node, the Rust borrow checker kicks in. The next node has a
791 // mutable reference to DT, so we can't use current node (which also has a mutable reference to
792 // DT).
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000793 pub fn delete_and_next_compatible(self, compatible: &CStr) -> Result<Option<Self>> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000794 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
Jiyong Park9c63cd12023-03-21 17:53:07 +0900795 let ret = unsafe {
796 libfdt_bindgen::fdt_node_offset_by_compatible(
797 self.fdt.as_ptr(),
798 self.offset,
799 compatible.as_ptr(),
800 )
801 };
802 let next_offset = fdt_err_or_option(ret)?;
803
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000804 self.delete_and_next(next_offset)
805 }
806
807 fn delete_and_next(mut self, next_offset: Option<c_int>) -> Result<Option<Self>> {
Jaewan Kim4ae0e712023-10-19 14:16:17 +0900808 if Some(self.offset) == next_offset {
809 return Err(FdtError::Internal);
810 }
811
812 // SAFETY: nop_self() only touches bytes of the self and its properties and subnodes, and
813 // doesn't alter any other blob in the tree. self.fdt and next_offset would remain valid.
814 unsafe { self.nop_self()? };
Jiyong Park9c63cd12023-03-21 17:53:07 +0900815
816 Ok(next_offset.map(|offset| Self { fdt: self.fdt, offset }))
817 }
Jaewan Kim4ae0e712023-10-19 14:16:17 +0900818
819 /// Deletes this node effectively from DT, by setting it with FDT_NOP
820 pub fn nop(mut self) -> Result<()> {
821 // SAFETY: This consumes self, so invalid node wouldn't be used any further
822 unsafe { self.nop_self() }
823 }
824
825 /// Deletes this node effectively from DT, by setting it with FDT_NOP.
826 /// This only changes bytes of the node and its properties and subnodes, and doesn't alter or
827 /// move any other part of the tree.
828 /// SAFETY: This node is no longer valid.
829 unsafe fn nop_self(&mut self) -> Result<()> {
830 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor).
831 let ret = unsafe { libfdt_bindgen::fdt_nop_node(self.fdt.as_mut_ptr(), self.offset) };
832
833 fdt_err_expect_zero(ret)
834 }
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000835}
836
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000837/// Wrapper around low-level libfdt functions.
Alice Wang9d4df702023-05-25 14:14:12 +0000838#[derive(Debug)]
David Brazdil1baa9a92022-06-28 14:47:50 +0100839#[repr(transparent)]
840pub struct Fdt {
Pierre-Clément Tosief2030e2022-11-28 11:21:20 +0000841 buffer: [u8],
David Brazdil1baa9a92022-06-28 14:47:50 +0100842}
843
844impl Fdt {
845 /// Wraps a slice containing a Flattened Device Tree.
846 ///
847 /// Fails if the FDT does not pass validation.
848 pub fn from_slice(fdt: &[u8]) -> Result<&Self> {
Pierre-Clément Tosifbb5ee22023-12-21 13:49:59 +0000849 libfdt::check_full(fdt)?;
850 // SAFETY: The FDT was validated.
David Brazdil1baa9a92022-06-28 14:47:50 +0100851 let fdt = unsafe { Self::unchecked_from_slice(fdt) };
Pierre-Clément Tosifbb5ee22023-12-21 13:49:59 +0000852
David Brazdil1baa9a92022-06-28 14:47:50 +0100853 Ok(fdt)
854 }
855
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000856 /// Wraps a mutable slice containing a Flattened Device Tree.
857 ///
858 /// Fails if the FDT does not pass validation.
859 pub fn from_mut_slice(fdt: &mut [u8]) -> Result<&mut Self> {
Pierre-Clément Tosifbb5ee22023-12-21 13:49:59 +0000860 libfdt::check_full(fdt)?;
861 // SAFETY: The FDT was validated.
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000862 let fdt = unsafe { Self::unchecked_from_mut_slice(fdt) };
Pierre-Clément Tosifbb5ee22023-12-21 13:49:59 +0000863
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000864 Ok(fdt)
865 }
866
Jaewan Kim4cf20aa2023-04-03 10:25:38 +0900867 /// Creates an empty Flattened Device Tree with a mutable slice.
868 pub fn create_empty_tree(fdt: &mut [u8]) -> Result<&mut Self> {
Pierre-Clément Tosifbb5ee22023-12-21 13:49:59 +0000869 libfdt::create_empty_tree(fdt)?;
Jaewan Kim4cf20aa2023-04-03 10:25:38 +0900870
Pierre-Clément Tosifbb5ee22023-12-21 13:49:59 +0000871 Self::from_mut_slice(fdt)
Jaewan Kim4cf20aa2023-04-03 10:25:38 +0900872 }
873
David Brazdil1baa9a92022-06-28 14:47:50 +0100874 /// Wraps a slice containing a Flattened Device Tree.
875 ///
876 /// # Safety
877 ///
Pierre-Clément Tosidf3037f2024-01-22 15:41:43 +0000878 /// 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 +0100879 pub unsafe fn unchecked_from_slice(fdt: &[u8]) -> &Self {
Pierre-Clément Tosidf3037f2024-01-22 15:41:43 +0000880 let self_ptr = fdt as *const _ as *const _;
881 // SAFETY: The pointer is non-null, dereferenceable, and points to allocated memory.
882 unsafe { &*self_ptr }
David Brazdil1baa9a92022-06-28 14:47:50 +0100883 }
884
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000885 /// Wraps a mutable slice containing a Flattened Device Tree.
886 ///
887 /// # Safety
888 ///
Pierre-Clément Tosidf3037f2024-01-22 15:41:43 +0000889 /// 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 +0000890 pub unsafe fn unchecked_from_mut_slice(fdt: &mut [u8]) -> &mut Self {
Pierre-Clément Tosidf3037f2024-01-22 15:41:43 +0000891 let self_mut_ptr = fdt as *mut _ as *mut _;
892 // SAFETY: The pointer is non-null, dereferenceable, and points to allocated memory.
893 unsafe { &mut *self_mut_ptr }
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000894 }
895
Pierre-Clément Tosice0b36d2024-01-26 10:50:05 +0000896 /// Updates this FDT from another FDT.
897 pub fn clone_from(&mut self, other: &Self) -> Result<()> {
898 let new_len = other.buffer.len();
899 if self.buffer.len() < new_len {
900 return Err(FdtError::NoSpace);
Jiyong Parke9d87e82023-03-21 19:28:40 +0900901 }
Pierre-Clément Tosice0b36d2024-01-26 10:50:05 +0000902
903 let zeroed_len = self.totalsize().checked_sub(new_len);
904 let (cloned, zeroed) = self.buffer.split_at_mut(new_len);
905
906 cloned.clone_from_slice(&other.buffer);
907 if let Some(len) = zeroed_len {
908 zeroed[..len].fill(0);
909 }
910
911 Ok(())
Jiyong Parke9d87e82023-03-21 19:28:40 +0900912 }
913
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900914 /// Unpacks the DT to cover the whole slice it is contained in.
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000915 pub fn unpack(&mut self) -> Result<()> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000916 // SAFETY: "Opens" the DT in-place (supported use-case) by updating its header and
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000917 // internal structures to make use of the whole self.fdt slice but performs no accesses
918 // outside of it and leaves the DT in a state that will be detected by other functions.
919 let ret = unsafe {
920 libfdt_bindgen::fdt_open_into(
921 self.as_ptr(),
922 self.as_mut_ptr(),
923 self.capacity().try_into().map_err(|_| FdtError::Internal)?,
924 )
925 };
926 fdt_err_expect_zero(ret)
927 }
928
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900929 /// Packs the DT to take a minimum amount of memory.
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000930 ///
931 /// Doesn't shrink the underlying memory slice.
932 pub fn pack(&mut self) -> Result<()> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000933 // SAFETY: "Closes" the DT in-place by updating its header and relocating its structs.
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000934 let ret = unsafe { libfdt_bindgen::fdt_pack(self.as_mut_ptr()) };
935 fdt_err_expect_zero(ret)
936 }
937
Pierre-Clément Tosi90e19352022-11-21 17:11:48 +0000938 /// Applies a DT overlay on the base DT.
939 ///
940 /// # Safety
941 ///
942 /// On failure, the library corrupts the DT and overlay so both must be discarded.
943 pub unsafe fn apply_overlay<'a>(&'a mut self, overlay: &'a mut Fdt) -> Result<&'a mut Self> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000944 let ret =
945 // SAFETY: Both pointers are valid because they come from references, and fdt_overlay_apply
946 // doesn't keep them after it returns. It may corrupt their contents if there is an error,
947 // but that's our caller's responsibility.
948 unsafe { libfdt_bindgen::fdt_overlay_apply(self.as_mut_ptr(), overlay.as_mut_ptr()) };
949 fdt_err_expect_zero(ret)?;
Pierre-Clément Tosi90e19352022-11-21 17:11:48 +0000950 Ok(self)
951 }
952
Alice Wang2422bdc2023-06-12 08:37:55 +0000953 /// Returns an iterator of memory banks specified the "/memory" node.
954 /// Throws an error when the "/memory" is not found in the device tree.
David Brazdil1baa9a92022-06-28 14:47:50 +0100955 ///
956 /// NOTE: This does not support individual "/memory@XXXX" banks.
Alice Wang2422bdc2023-06-12 08:37:55 +0000957 pub fn memory(&self) -> Result<MemRegIterator> {
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000958 let node = self.node(cstr!("/memory"))?.ok_or(FdtError::NotFound)?;
959 if node.device_type()? != Some(cstr!("memory")) {
Alice Wang2422bdc2023-06-12 08:37:55 +0000960 return Err(FdtError::BadValue);
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000961 }
Alice Wang2422bdc2023-06-12 08:37:55 +0000962 node.reg()?.ok_or(FdtError::BadValue).map(MemRegIterator::new)
963 }
964
965 /// Returns the first memory range in the `/memory` node.
966 pub fn first_memory_range(&self) -> Result<Range<usize>> {
967 self.memory()?.next().ok_or(FdtError::NotFound)
David Brazdil1baa9a92022-06-28 14:47:50 +0100968 }
969
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900970 /// Returns the standard /chosen node.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000971 pub fn chosen(&self) -> Result<Option<FdtNode>> {
Jaewan Kimb635bb02023-11-01 13:00:34 +0900972 self.node(cstr!("/chosen"))
David Brazdil1baa9a92022-06-28 14:47:50 +0100973 }
974
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900975 /// Returns the standard /chosen node as mutable.
Pierre-Clément Tosi4ba79662023-02-13 11:22:41 +0000976 pub fn chosen_mut(&mut self) -> Result<Option<FdtNodeMut>> {
Jaewan Kimb635bb02023-11-01 13:00:34 +0900977 self.node_mut(cstr!("/chosen"))
Pierre-Clément Tosi4ba79662023-02-13 11:22:41 +0000978 }
979
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900980 /// Returns the root node of the tree.
Pierre-Clément Tosi41c158e2022-11-21 19:16:25 +0000981 pub fn root(&self) -> Result<FdtNode> {
Jaewan Kimb635bb02023-11-01 13:00:34 +0900982 self.node(cstr!("/"))?.ok_or(FdtError::Internal)
Pierre-Clément Tosi41c158e2022-11-21 19:16:25 +0000983 }
984
Jaewan Kimf163d762023-11-01 13:12:50 +0900985 /// Returns the standard /__symbols__ node.
986 pub fn symbols(&self) -> Result<Option<FdtNode>> {
987 self.node(cstr!("/__symbols__"))
988 }
989
990 /// Returns the standard /__symbols__ node as mutable
991 pub fn symbols_mut(&mut self) -> Result<Option<FdtNodeMut>> {
992 self.node_mut(cstr!("/__symbols__"))
993 }
994
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900995 /// Returns a tree node by its full path.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000996 pub fn node(&self, path: &CStr) -> Result<Option<FdtNode>> {
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000997 let offset = self.path_offset(path.to_bytes())?;
998
999 Ok(offset.map(|offset| FdtNode { fdt: self, offset }))
David Brazdil1baa9a92022-06-28 14:47:50 +01001000 }
1001
Pierre-Clément Tosi41c158e2022-11-21 19:16:25 +00001002 /// Iterate over nodes with a given compatible string.
1003 pub fn compatible_nodes<'a>(&'a self, compatible: &'a CStr) -> Result<CompatibleIterator<'a>> {
1004 CompatibleIterator::new(self, compatible)
1005 }
1006
Jaewan Kim17ba7a32023-10-19 13:25:15 +09001007 /// Returns max phandle in the tree.
1008 pub fn max_phandle(&self) -> Result<Phandle> {
1009 let mut phandle: u32 = 0;
1010 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
1011 let ret = unsafe { libfdt_bindgen::fdt_find_max_phandle(self.as_ptr(), &mut phandle) };
1012
1013 fdt_err_expect_zero(ret)?;
Pierre-Clément Tosieba27792023-10-30 12:04:12 +00001014 phandle.try_into()
Jaewan Kim17ba7a32023-10-19 13:25:15 +09001015 }
1016
1017 /// Returns a node with the phandle
1018 pub fn node_with_phandle(&self, phandle: Phandle) -> Result<Option<FdtNode>> {
Jaewan Kimc63246d2023-11-09 15:41:01 +09001019 let offset = self.node_offset_with_phandle(phandle)?;
1020 Ok(offset.map(|offset| FdtNode { fdt: self, offset }))
1021 }
1022
1023 /// Returns a mutable node with the phandle
1024 pub fn node_mut_with_phandle(&mut self, phandle: Phandle) -> Result<Option<FdtNodeMut>> {
1025 let offset = self.node_offset_with_phandle(phandle)?;
1026 Ok(offset.map(|offset| FdtNodeMut { fdt: self, offset }))
1027 }
1028
1029 fn node_offset_with_phandle(&self, phandle: Phandle) -> Result<Option<c_int>> {
1030 // SAFETY: Accesses are constrained to the DT totalsize.
Jaewan Kim17ba7a32023-10-19 13:25:15 +09001031 let ret = unsafe { libfdt_bindgen::fdt_node_offset_by_phandle(self.as_ptr(), phandle.0) };
Jaewan Kimc63246d2023-11-09 15:41:01 +09001032 fdt_err_or_option(ret)
Jaewan Kim17ba7a32023-10-19 13:25:15 +09001033 }
1034
Jaewan Kimb3dcfc22023-09-20 10:20:52 +09001035 /// Returns the mutable root node of the tree.
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +00001036 pub fn root_mut(&mut self) -> Result<FdtNodeMut> {
Jaewan Kimb635bb02023-11-01 13:00:34 +09001037 self.node_mut(cstr!("/"))?.ok_or(FdtError::Internal)
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +00001038 }
1039
Jaewan Kimb3dcfc22023-09-20 10:20:52 +09001040 /// Returns a mutable tree node by its full path.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +00001041 pub fn node_mut(&mut self, path: &CStr) -> Result<Option<FdtNodeMut>> {
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +00001042 let offset = self.path_offset(path.to_bytes())?;
1043
1044 Ok(offset.map(|offset| FdtNodeMut { fdt: self, offset }))
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +00001045 }
1046
Jaewan Kimb3dcfc22023-09-20 10:20:52 +09001047 /// Returns the device tree as a slice (may be smaller than the containing buffer).
Pierre-Clément Tosidb74cb12022-12-08 13:56:25 +00001048 pub fn as_slice(&self) -> &[u8] {
1049 &self.buffer[..self.totalsize()]
1050 }
1051
Jaewan Kimbab42592023-10-13 15:47:19 +09001052 fn path_offset(&self, path: &[u8]) -> Result<Option<c_int>> {
1053 let len = path.len().try_into().map_err(|_| FdtError::BadPath)?;
Andrew Walbran84b9a232023-07-05 14:01:40 +00001054 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor) and the
David Brazdil1baa9a92022-06-28 14:47:50 +01001055 // function respects the passed number of characters.
1056 let ret = unsafe {
1057 // *_namelen functions don't include the trailing nul terminator in 'len'.
Jaewan Kimbab42592023-10-13 15:47:19 +09001058 libfdt_bindgen::fdt_path_offset_namelen(self.as_ptr(), path.as_ptr().cast::<_>(), len)
David Brazdil1baa9a92022-06-28 14:47:50 +01001059 };
1060
Pierre-Clément Tosib244d932022-11-24 16:45:53 +00001061 fdt_err_or_option(ret)
David Brazdil1baa9a92022-06-28 14:47:50 +01001062 }
1063
Jaewan Kimaa638702023-09-19 13:34:01 +09001064 fn get_from_ptr(&self, ptr: *const c_void, len: usize) -> Result<&[u8]> {
1065 let ptr = ptr as usize;
1066 let offset = ptr.checked_sub(self.as_ptr() as usize).ok_or(FdtError::Internal)?;
1067 self.buffer.get(offset..(offset + len)).ok_or(FdtError::Internal)
1068 }
1069
Jaewan Kim72d10902023-10-12 21:59:26 +09001070 fn string(&self, offset: c_int) -> Result<&CStr> {
1071 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
1072 let res = unsafe { libfdt_bindgen::fdt_string(self.as_ptr(), offset) };
1073 if res.is_null() {
1074 return Err(FdtError::Internal);
1075 }
1076
1077 // SAFETY: Non-null return from fdt_string() is valid null-terminating string within FDT.
1078 Ok(unsafe { CStr::from_ptr(res) })
1079 }
1080
Jaewan Kimb3dcfc22023-09-20 10:20:52 +09001081 /// Returns a shared pointer to the device tree.
Pierre-Clément Tosi8036b4f2023-02-17 10:31:31 +00001082 pub fn as_ptr(&self) -> *const c_void {
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +00001083 self.buffer.as_ptr().cast()
David Brazdil1baa9a92022-06-28 14:47:50 +01001084 }
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +00001085
1086 fn as_mut_ptr(&mut self) -> *mut c_void {
Pierre-Clément Tosi0dcc75e2023-05-02 13:43:55 +00001087 self.buffer.as_mut_ptr().cast::<_>()
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +00001088 }
1089
1090 fn capacity(&self) -> usize {
Pierre-Clément Tosief2030e2022-11-28 11:21:20 +00001091 self.buffer.len()
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +00001092 }
Pierre-Clément Tosidb74cb12022-12-08 13:56:25 +00001093
1094 fn header(&self) -> &libfdt_bindgen::fdt_header {
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +00001095 let p = self.as_ptr().cast();
Andrew Walbran84b9a232023-07-05 14:01:40 +00001096 // SAFETY: A valid FDT (verified by constructor) must contain a valid fdt_header.
Pierre-Clément Tosi0dcc75e2023-05-02 13:43:55 +00001097 unsafe { &*p }
Pierre-Clément Tosidb74cb12022-12-08 13:56:25 +00001098 }
1099
1100 fn totalsize(&self) -> usize {
1101 u32::from_be(self.header().totalsize) as usize
1102 }
David Brazdil1baa9a92022-06-28 14:47:50 +01001103}