blob: 0249d0d76968fe06121cb9558513185dac9d59db [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;
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
Pierre-Clément Tosid83741d2024-02-02 10:44:55 +000036use crate::libfdt::{Libfdt, LibfdtMut};
37
David Brazdil1baa9a92022-06-28 14:47:50 +010038/// Value of a #address-cells property.
Andrew Walbranb39e6922022-12-05 17:01:20 +000039#[derive(Copy, Clone, Debug, Eq, PartialEq)]
David Brazdil1baa9a92022-06-28 14:47:50 +010040enum AddrCells {
41 Single = 1,
42 Double = 2,
Andrew Walbranb39e6922022-12-05 17:01:20 +000043 Triple = 3,
David Brazdil1baa9a92022-06-28 14:47:50 +010044}
45
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +000046impl TryFrom<usize> for AddrCells {
David Brazdil1baa9a92022-06-28 14:47:50 +010047 type Error = FdtError;
48
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +000049 fn try_from(value: usize) -> Result<Self> {
50 match value {
51 x if x == Self::Single as _ => Ok(Self::Single),
52 x if x == Self::Double as _ => Ok(Self::Double),
53 x if x == Self::Triple as _ => Ok(Self::Triple),
David Brazdil1baa9a92022-06-28 14:47:50 +010054 _ => Err(FdtError::BadNCells),
55 }
56 }
57}
58
59/// Value of a #size-cells property.
Andrew Walbranb39e6922022-12-05 17:01:20 +000060#[derive(Copy, Clone, Debug, Eq, PartialEq)]
David Brazdil1baa9a92022-06-28 14:47:50 +010061enum SizeCells {
62 None = 0,
63 Single = 1,
64 Double = 2,
65}
66
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +000067impl TryFrom<usize> for SizeCells {
David Brazdil1baa9a92022-06-28 14:47:50 +010068 type Error = FdtError;
69
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +000070 fn try_from(value: usize) -> Result<Self> {
71 match value {
72 x if x == Self::None as _ => Ok(Self::None),
73 x if x == Self::Single as _ => Ok(Self::Single),
74 x if x == Self::Double as _ => Ok(Self::Double),
David Brazdil1baa9a92022-06-28 14:47:50 +010075 _ => Err(FdtError::BadNCells),
76 }
77 }
78}
79
Jaewan Kim72d10902023-10-12 21:59:26 +090080/// DT property wrapper to abstract endianess changes
81#[repr(transparent)]
82#[derive(Debug)]
83struct FdtPropertyStruct(libfdt_bindgen::fdt_property);
84
Pierre-Clément Tosidf3037f2024-01-22 15:41:43 +000085impl AsRef<FdtPropertyStruct> for libfdt_bindgen::fdt_property {
86 fn as_ref(&self) -> &FdtPropertyStruct {
87 let ptr = self as *const _ as *const _;
88 // SAFETY: Types have the same layout (transparent) so the valid reference remains valid.
89 unsafe { &*ptr }
90 }
91}
92
Jaewan Kim72d10902023-10-12 21:59:26 +090093impl FdtPropertyStruct {
94 fn from_offset(fdt: &Fdt, offset: c_int) -> Result<&Self> {
95 let mut len = 0;
96 let prop =
97 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
98 unsafe { libfdt_bindgen::fdt_get_property_by_offset(fdt.as_ptr(), offset, &mut len) };
99 if prop.is_null() {
100 fdt_err(len)?;
101 return Err(FdtError::Internal); // shouldn't happen.
102 }
103 // SAFETY: prop is only returned when it points to valid libfdt_bindgen.
Pierre-Clément Tosidf3037f2024-01-22 15:41:43 +0000104 let prop = unsafe { &*prop };
105 Ok(prop.as_ref())
Jaewan Kim72d10902023-10-12 21:59:26 +0900106 }
107
108 fn name_offset(&self) -> c_int {
109 u32::from_be(self.0.nameoff).try_into().unwrap()
110 }
111
112 fn data_len(&self) -> usize {
113 u32::from_be(self.0.len).try_into().unwrap()
114 }
115
116 fn data_ptr(&self) -> *const c_void {
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000117 self.0.data.as_ptr().cast()
Jaewan Kim72d10902023-10-12 21:59:26 +0900118 }
119}
120
121/// DT property.
122#[derive(Clone, Copy, Debug)]
123pub struct FdtProperty<'a> {
124 fdt: &'a Fdt,
125 offset: c_int,
126 property: &'a FdtPropertyStruct,
127}
128
129impl<'a> FdtProperty<'a> {
130 fn new(fdt: &'a Fdt, offset: c_int) -> Result<Self> {
131 let property = FdtPropertyStruct::from_offset(fdt, offset)?;
132 Ok(Self { fdt, offset, property })
133 }
134
135 /// Returns the property name
136 pub fn name(&self) -> Result<&'a CStr> {
137 self.fdt.string(self.property.name_offset())
138 }
139
140 /// Returns the property value
141 pub fn value(&self) -> Result<&'a [u8]> {
142 self.fdt.get_from_ptr(self.property.data_ptr(), self.property.data_len())
143 }
144
145 fn next_property(&self) -> Result<Option<Self>> {
146 let ret =
147 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
148 unsafe { libfdt_bindgen::fdt_next_property_offset(self.fdt.as_ptr(), self.offset) };
149
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000150 if let Some(offset) = fdt_err_or_option(ret)? {
151 Ok(Some(Self::new(self.fdt, offset)?))
152 } else {
153 Ok(None)
154 }
Jaewan Kim72d10902023-10-12 21:59:26 +0900155 }
156}
157
David Brazdil1baa9a92022-06-28 14:47:50 +0100158/// DT node.
Alice Wang9d4df702023-05-25 14:14:12 +0000159#[derive(Clone, Copy, Debug)]
David Brazdil1baa9a92022-06-28 14:47:50 +0100160pub struct FdtNode<'a> {
161 fdt: &'a Fdt,
162 offset: c_int,
163}
164
165impl<'a> FdtNode<'a> {
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900166 /// Returns parent node.
David Brazdil1baa9a92022-06-28 14:47:50 +0100167 pub fn parent(&self) -> Result<Self> {
Pierre-Clément Tosid83741d2024-02-02 10:44:55 +0000168 let offset = self.fdt.parent_offset(self.offset)?;
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> {
Pierre-Clément Tosid83741d2024-02-02 10:44:55 +0000175 let offset = self.fdt.supernode_atdepth_offset(self.offset, depth)?;
Jaewan Kim5b057772023-10-19 01:02:17 +0900176
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000177 Ok(Self { fdt: self.fdt, offset })
Jaewan Kim5b057772023-10-19 01:02:17 +0900178 }
179
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900180 /// Returns the standard (deprecated) device_type <string> property.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000181 pub fn device_type(&self) -> Result<Option<&CStr>> {
Jaewan Kimb635bb02023-11-01 13:00:34 +0900182 self.getprop_str(cstr!("device_type"))
David Brazdil1baa9a92022-06-28 14:47:50 +0100183 }
184
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900185 /// Returns the standard reg <prop-encoded-array> property.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000186 pub fn reg(&self) -> Result<Option<RegIterator<'a>>> {
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000187 if let Some(cells) = self.getprop_cells(cstr!("reg"))? {
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000188 let parent = self.parent()?;
David Brazdil1baa9a92022-06-28 14:47:50 +0100189
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000190 let addr_cells = parent.address_cells()?;
191 let size_cells = parent.size_cells()?;
192
193 Ok(Some(RegIterator::new(cells, addr_cells, size_cells)))
194 } else {
195 Ok(None)
196 }
David Brazdil1baa9a92022-06-28 14:47:50 +0100197 }
198
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900199 /// Returns the standard ranges property.
Andrew Walbranb39e6922022-12-05 17:01:20 +0000200 pub fn ranges<A, P, S>(&self) -> Result<Option<RangesIterator<'a, A, P, S>>> {
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000201 if let Some(cells) = self.getprop_cells(cstr!("ranges"))? {
Andrew Walbranb39e6922022-12-05 17:01:20 +0000202 let parent = self.parent()?;
203 let addr_cells = self.address_cells()?;
204 let parent_addr_cells = parent.address_cells()?;
205 let size_cells = self.size_cells()?;
206 Ok(Some(RangesIterator::<A, P, S>::new(
207 cells,
208 addr_cells,
209 parent_addr_cells,
210 size_cells,
211 )))
212 } else {
213 Ok(None)
214 }
215 }
216
Jaewan Kimaa638702023-09-19 13:34:01 +0900217 /// Returns the node name.
218 pub fn name(&self) -> Result<&'a CStr> {
219 let mut len: c_int = 0;
220 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor). On success, the
221 // function returns valid null terminating string and otherwise returned values are dropped.
222 let name = unsafe { libfdt_bindgen::fdt_get_name(self.fdt.as_ptr(), self.offset, &mut len) }
223 as *const c_void;
224 let len = usize::try_from(fdt_err(len)?).unwrap();
225 let name = self.fdt.get_from_ptr(name, len + 1)?;
226 CStr::from_bytes_with_nul(name).map_err(|_| FdtError::Internal)
227 }
228
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900229 /// Returns the value of a given <string> property.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000230 pub fn getprop_str(&self, name: &CStr) -> Result<Option<&CStr>> {
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000231 if let Some(bytes) = self.getprop(name)? {
232 Ok(Some(CStr::from_bytes_with_nul(bytes).map_err(|_| FdtError::BadValue)?))
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000233 } else {
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000234 Ok(None)
235 }
David Brazdil1baa9a92022-06-28 14:47:50 +0100236 }
237
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900238 /// Returns the value of a given property as an array of cells.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000239 pub fn getprop_cells(&self, name: &CStr) -> Result<Option<CellIterator<'a>>> {
240 if let Some(cells) = self.getprop(name)? {
241 Ok(Some(CellIterator::new(cells)))
242 } else {
243 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 <u32> property.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000248 pub fn getprop_u32(&self, name: &CStr) -> Result<Option<u32>> {
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000249 if let Some(bytes) = self.getprop(name)? {
250 Ok(Some(u32::from_be_bytes(bytes.try_into().map_err(|_| FdtError::BadValue)?)))
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000251 } else {
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000252 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 <u64> property.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000257 pub fn getprop_u64(&self, name: &CStr) -> Result<Option<u64>> {
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000258 if let Some(bytes) = self.getprop(name)? {
259 Ok(Some(u64::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 property.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000266 pub fn getprop(&self, name: &CStr) -> Result<Option<&'a [u8]>> {
Jiyong Park9c63cd12023-03-21 17:53:07 +0900267 if let Some((prop, len)) = Self::getprop_internal(self.fdt, self.offset, name)? {
Jaewan Kimaa638702023-09-19 13:34:01 +0900268 Ok(Some(self.fdt.get_from_ptr(prop, len)?))
Jiyong Park9c63cd12023-03-21 17:53:07 +0900269 } else {
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000270 Ok(None)
Jiyong Park9c63cd12023-03-21 17:53:07 +0900271 }
272 }
273
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900274 /// Returns the pointer and size of the property named `name`, in a node at offset `offset`, in
Jiyong Park9c63cd12023-03-21 17:53:07 +0900275 /// a device tree `fdt`. The pointer is guaranteed to be non-null, in which case error returns.
276 fn getprop_internal(
277 fdt: &'a Fdt,
278 offset: c_int,
279 name: &CStr,
280 ) -> Result<Option<(*const c_void, usize)>> {
David Brazdil1baa9a92022-06-28 14:47:50 +0100281 let mut len: i32 = 0;
Andrew Walbran84b9a232023-07-05 14:01:40 +0000282 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor) and the
David Brazdil1baa9a92022-06-28 14:47:50 +0100283 // function respects the passed number of characters.
284 let prop = unsafe {
285 libfdt_bindgen::fdt_getprop_namelen(
Jiyong Park9c63cd12023-03-21 17:53:07 +0900286 fdt.as_ptr(),
287 offset,
David Brazdil1baa9a92022-06-28 14:47:50 +0100288 name.as_ptr(),
289 // *_namelen functions don't include the trailing nul terminator in 'len'.
290 name.to_bytes().len().try_into().map_err(|_| FdtError::BadPath)?,
291 &mut len as *mut i32,
292 )
293 } as *const u8;
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000294
295 let Some(len) = fdt_err_or_option(len)? else {
296 return Ok(None); // Property was not found.
297 };
Jaewan Kimaa638702023-09-19 13:34:01 +0900298 let len = usize::try_from(len).unwrap();
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000299
David Brazdil1baa9a92022-06-28 14:47:50 +0100300 if prop.is_null() {
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000301 // We expected an error code in len but still received a valid value?!
302 return Err(FdtError::Internal);
David Brazdil1baa9a92022-06-28 14:47:50 +0100303 }
Jiyong Park9c63cd12023-03-21 17:53:07 +0900304 Ok(Some((prop.cast::<c_void>(), len)))
David Brazdil1baa9a92022-06-28 14:47:50 +0100305 }
306
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900307 /// Returns reference to the containing device tree.
David Brazdil1baa9a92022-06-28 14:47:50 +0100308 pub fn fdt(&self) -> &Fdt {
309 self.fdt
310 }
311
Alice Wang474c0ee2023-09-14 12:52:33 +0000312 /// Returns the compatible node of the given name that is next after this node.
313 pub fn next_compatible(self, compatible: &CStr) -> Result<Option<Self>> {
Pierre-Clément Tosid83741d2024-02-02 10:44:55 +0000314 let offset = self.fdt.node_offset_by_compatible(self.offset, compatible)?;
Pierre-Clément Tosi41c158e2022-11-21 19:16:25 +0000315
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000316 Ok(offset.map(|offset| Self { fdt: self.fdt, offset }))
Pierre-Clément Tosi41c158e2022-11-21 19:16:25 +0000317 }
318
Alice Wang474c0ee2023-09-14 12:52:33 +0000319 /// Returns the first range of `reg` in this node.
320 pub fn first_reg(&self) -> Result<Reg<u64>> {
321 self.reg()?.ok_or(FdtError::NotFound)?.next().ok_or(FdtError::NotFound)
322 }
323
David Brazdil1baa9a92022-06-28 14:47:50 +0100324 fn address_cells(&self) -> Result<AddrCells> {
Pierre-Clément Tosid83741d2024-02-02 10:44:55 +0000325 self.fdt.address_cells(self.offset)?.try_into()
David Brazdil1baa9a92022-06-28 14:47:50 +0100326 }
327
328 fn size_cells(&self) -> Result<SizeCells> {
Pierre-Clément Tosid83741d2024-02-02 10:44:55 +0000329 self.fdt.size_cells(self.offset)?.try_into()
David Brazdil1baa9a92022-06-28 14:47:50 +0100330 }
Jaewan Kimbc828d72023-09-19 15:52:08 +0900331
332 /// Returns an iterator of subnodes
Jaewan Kim4a34b0d2024-01-19 13:17:47 +0900333 pub fn subnodes(&self) -> Result<SubnodeIterator<'a>> {
Jaewan Kimbc828d72023-09-19 15:52:08 +0900334 SubnodeIterator::new(self)
335 }
336
337 fn first_subnode(&self) -> Result<Option<Self>> {
Pierre-Clément Tosid83741d2024-02-02 10:44:55 +0000338 let offset = self.fdt.first_subnode(self.offset)?;
Jaewan Kimbc828d72023-09-19 15:52:08 +0900339
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000340 Ok(offset.map(|offset| Self { fdt: self.fdt, offset }))
Jaewan Kimbc828d72023-09-19 15:52:08 +0900341 }
342
343 fn next_subnode(&self) -> Result<Option<Self>> {
Pierre-Clément Tosid83741d2024-02-02 10:44:55 +0000344 let offset = self.fdt.next_subnode(self.offset)?;
Jaewan Kimbc828d72023-09-19 15:52:08 +0900345
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000346 Ok(offset.map(|offset| Self { fdt: self.fdt, offset }))
Jaewan Kimbc828d72023-09-19 15:52:08 +0900347 }
Jaewan Kim72d10902023-10-12 21:59:26 +0900348
Jaewan Kimc9e14112023-12-04 17:05:27 +0900349 /// Returns an iterator of descendants
Jaewan Kim1eab7232024-01-04 09:46:16 +0900350 pub fn descendants(&self) -> DescendantsIterator<'a> {
Jaewan Kimc9e14112023-12-04 17:05:27 +0900351 DescendantsIterator::new(self)
352 }
353
354 fn next_node(&self, depth: usize) -> Result<Option<(Self, usize)>> {
Pierre-Clément Tosid83741d2024-02-02 10:44:55 +0000355 if let Some((offset, depth)) = self.fdt.next_node(self.offset, depth)? {
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000356 Ok(Some((Self { fdt: self.fdt, offset }, depth)))
357 } else {
358 Ok(None)
359 }
Jaewan Kimc9e14112023-12-04 17:05:27 +0900360 }
361
Jaewan Kim72d10902023-10-12 21:59:26 +0900362 /// Returns an iterator of properties
363 pub fn properties(&'a self) -> Result<PropertyIterator<'a>> {
364 PropertyIterator::new(self)
365 }
366
367 fn first_property(&self) -> Result<Option<FdtProperty<'a>>> {
368 let ret =
369 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
370 unsafe { libfdt_bindgen::fdt_first_property_offset(self.fdt.as_ptr(), self.offset) };
371
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000372 if let Some(offset) = fdt_err_or_option(ret)? {
373 Ok(Some(FdtProperty::new(self.fdt, offset)?))
374 } else {
375 Ok(None)
376 }
Jaewan Kim72d10902023-10-12 21:59:26 +0900377 }
Jaewan Kimf34f4b82023-11-03 19:38:38 +0900378
379 /// Returns the phandle
380 pub fn get_phandle(&self) -> Result<Option<Phandle>> {
381 // This rewrites the fdt_get_phandle() because it doesn't return error code.
382 if let Some(prop) = self.getprop_u32(cstr!("phandle"))? {
383 Ok(Some(prop.try_into()?))
384 } else if let Some(prop) = self.getprop_u32(cstr!("linux,phandle"))? {
385 Ok(Some(prop.try_into()?))
386 } else {
387 Ok(None)
388 }
389 }
Jaewan Kim52026012023-12-13 13:49:28 +0900390
391 /// Returns the subnode of the given name. The name doesn't need to be nul-terminated.
392 pub fn subnode(&self, name: &CStr) -> Result<Option<Self>> {
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000393 let name = name.to_bytes();
Pierre-Clément Tosid83741d2024-02-02 10:44:55 +0000394 let offset = self.fdt.subnode_offset_namelen(self.offset, name)?;
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000395
Jaewan Kim52026012023-12-13 13:49:28 +0900396 Ok(offset.map(|offset| Self { fdt: self.fdt, offset }))
397 }
398
399 /// Returns the subnode of the given name bytes
400 pub fn subnode_with_name_bytes(&self, name: &[u8]) -> Result<Option<Self>> {
Pierre-Clément Tosid83741d2024-02-02 10:44:55 +0000401 let offset = self.fdt.subnode_offset_namelen(self.offset, name)?;
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000402
Jaewan Kim52026012023-12-13 13:49:28 +0900403 Ok(offset.map(|offset| Self { fdt: self.fdt, offset }))
404 }
David Brazdil1baa9a92022-06-28 14:47:50 +0100405}
406
Pierre-Clément Tosi504b4302023-10-30 12:22:50 +0000407impl<'a> PartialEq for FdtNode<'a> {
408 fn eq(&self, other: &Self) -> bool {
409 self.fdt.as_ptr() == other.fdt.as_ptr() && self.offset == other.offset
410 }
411}
412
Jaewan Kim17ba7a32023-10-19 13:25:15 +0900413/// Phandle of a FDT node
414#[repr(transparent)]
Jaewan Kim55f438c2023-11-15 01:24:36 +0900415#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
Jaewan Kim17ba7a32023-10-19 13:25:15 +0900416pub struct Phandle(u32);
417
418impl Phandle {
Pierre-Clément Tosieba27792023-10-30 12:04:12 +0000419 /// Minimum valid value for device tree phandles.
420 pub const MIN: Self = Self(1);
421 /// Maximum valid value for device tree phandles.
422 pub const MAX: Self = Self(libfdt_bindgen::FDT_MAX_PHANDLE);
423
Jaewan Kim17ba7a32023-10-19 13:25:15 +0900424 /// Creates a new Phandle
Pierre-Clément Tosieba27792023-10-30 12:04:12 +0000425 pub const fn new(value: u32) -> Option<Self> {
426 if Self::MIN.0 <= value && value <= Self::MAX.0 {
427 Some(Self(value))
428 } else {
429 None
Jaewan Kim17ba7a32023-10-19 13:25:15 +0900430 }
Jaewan Kim17ba7a32023-10-19 13:25:15 +0900431 }
432}
433
434impl From<Phandle> for u32 {
435 fn from(phandle: Phandle) -> u32 {
436 phandle.0
437 }
438}
439
Pierre-Clément Tosieba27792023-10-30 12:04:12 +0000440impl TryFrom<u32> for Phandle {
441 type Error = FdtError;
442
443 fn try_from(value: u32) -> Result<Self> {
444 Self::new(value).ok_or(FdtError::BadPhandle)
445 }
446}
447
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000448/// Mutable FDT node.
Pierre-Clément Tosi504b4302023-10-30 12:22:50 +0000449#[derive(Debug)]
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000450pub struct FdtNodeMut<'a> {
451 fdt: &'a mut Fdt,
452 offset: c_int,
453}
454
455impl<'a> FdtNodeMut<'a> {
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900456 /// Appends a property name-value (possibly empty) pair to the given node.
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000457 pub fn appendprop<T: AsRef<[u8]>>(&mut self, name: &CStr, value: &T) -> Result<()> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000458 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor).
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000459 let ret = unsafe {
460 libfdt_bindgen::fdt_appendprop(
461 self.fdt.as_mut_ptr(),
462 self.offset,
463 name.as_ptr(),
464 value.as_ref().as_ptr().cast::<c_void>(),
465 value.as_ref().len().try_into().map_err(|_| FdtError::BadValue)?,
466 )
467 };
468
469 fdt_err_expect_zero(ret)
470 }
471
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900472 /// Appends a (address, size) pair property to the given node.
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000473 pub fn appendprop_addrrange(&mut self, name: &CStr, addr: u64, size: u64) -> Result<()> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000474 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor).
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000475 let ret = unsafe {
476 libfdt_bindgen::fdt_appendprop_addrrange(
477 self.fdt.as_mut_ptr(),
478 self.parent()?.offset,
479 self.offset,
480 name.as_ptr(),
481 addr,
482 size,
483 )
484 };
485
486 fdt_err_expect_zero(ret)
487 }
488
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900489 /// Sets a property name-value pair to the given node.
490 ///
491 /// This may create a new prop or replace existing value.
Jaewan Kimba8929b2023-01-13 11:13:29 +0900492 pub fn setprop(&mut self, name: &CStr, value: &[u8]) -> Result<()> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000493 // SAFETY: New value size is constrained to the DT totalsize
Jaewan Kimba8929b2023-01-13 11:13:29 +0900494 // (validated by underlying libfdt).
495 let ret = unsafe {
496 libfdt_bindgen::fdt_setprop(
497 self.fdt.as_mut_ptr(),
498 self.offset,
499 name.as_ptr(),
500 value.as_ptr().cast::<c_void>(),
501 value.len().try_into().map_err(|_| FdtError::BadValue)?,
502 )
503 };
504
505 fdt_err_expect_zero(ret)
506 }
507
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900508 /// Sets the value of the given property with the given value, and ensure that the given
509 /// value has the same length as the current value length.
510 ///
511 /// This can only be used to replace existing value.
Jiyong Park9c63cd12023-03-21 17:53:07 +0900512 pub fn setprop_inplace(&mut self, name: &CStr, value: &[u8]) -> Result<()> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000513 // SAFETY: fdt size is not altered
Jiyong Park9c63cd12023-03-21 17:53:07 +0900514 let ret = unsafe {
515 libfdt_bindgen::fdt_setprop_inplace(
516 self.fdt.as_mut_ptr(),
517 self.offset,
518 name.as_ptr(),
519 value.as_ptr().cast::<c_void>(),
520 value.len().try_into().map_err(|_| FdtError::BadValue)?,
521 )
522 };
523
524 fdt_err_expect_zero(ret)
525 }
526
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900527 /// Sets the value of the given (address, size) pair property with the given value, and
528 /// ensure that the given value has the same length as the current value length.
529 ///
530 /// This can only be used to replace existing value.
Pierre-Clément Tosic27c4272023-05-19 15:46:26 +0000531 pub fn setprop_addrrange_inplace(&mut self, name: &CStr, addr: u64, size: u64) -> Result<()> {
532 let pair = [addr.to_be(), size.to_be()];
533 self.setprop_inplace(name, pair.as_bytes())
534 }
535
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900536 /// Sets a flag-like empty property.
537 ///
538 /// This may create a new prop or replace existing value.
Pierre-Clément Tosi4ba79662023-02-13 11:22:41 +0000539 pub fn setprop_empty(&mut self, name: &CStr) -> Result<()> {
540 self.setprop(name, &[])
541 }
542
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900543 /// Deletes the given property.
Pierre-Clément Tosi4ba79662023-02-13 11:22:41 +0000544 pub fn delprop(&mut self, name: &CStr) -> Result<()> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000545 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor) when the
Pierre-Clément Tosi4ba79662023-02-13 11:22:41 +0000546 // library locates the node's property. Removing the property may shift the offsets of
547 // other nodes and properties but the borrow checker should prevent this function from
548 // being called when FdtNode instances are in use.
549 let ret = unsafe {
550 libfdt_bindgen::fdt_delprop(self.fdt.as_mut_ptr(), self.offset, name.as_ptr())
551 };
552
553 fdt_err_expect_zero(ret)
554 }
555
Jaewan Kim4ae0e712023-10-19 14:16:17 +0900556 /// Deletes the given property effectively from DT, by setting it with FDT_NOP.
Pierre-Clément Tosibe3a97b2023-05-19 14:56:23 +0000557 pub fn nop_property(&mut self, name: &CStr) -> Result<()> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000558 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor) when the
Pierre-Clément Tosibe3a97b2023-05-19 14:56:23 +0000559 // library locates the node's property.
560 let ret = unsafe {
561 libfdt_bindgen::fdt_nop_property(self.fdt.as_mut_ptr(), self.offset, name.as_ptr())
562 };
563
564 fdt_err_expect_zero(ret)
565 }
566
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900567 /// Trims the size of the given property to new_size.
Jiyong Park9c63cd12023-03-21 17:53:07 +0900568 pub fn trimprop(&mut self, name: &CStr, new_size: usize) -> Result<()> {
569 let (prop, len) =
570 FdtNode::getprop_internal(self.fdt, self.offset, name)?.ok_or(FdtError::NotFound)?;
571 if len == new_size {
572 return Ok(());
573 }
574 if new_size > len {
575 return Err(FdtError::NoSpace);
576 }
577
Andrew Walbran84b9a232023-07-05 14:01:40 +0000578 // SAFETY: new_size is smaller than the old size
Jiyong Park9c63cd12023-03-21 17:53:07 +0900579 let ret = unsafe {
580 libfdt_bindgen::fdt_setprop(
581 self.fdt.as_mut_ptr(),
582 self.offset,
583 name.as_ptr(),
584 prop.cast::<c_void>(),
585 new_size.try_into().map_err(|_| FdtError::BadValue)?,
586 )
587 };
588
589 fdt_err_expect_zero(ret)
590 }
591
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900592 /// Returns reference to the containing device tree.
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000593 pub fn fdt(&mut self) -> &mut Fdt {
594 self.fdt
595 }
596
Jaewan Kimf72f4f22023-11-03 19:21:34 +0900597 /// Returns immutable FdtNode of this node.
598 pub fn as_node(&self) -> FdtNode {
599 FdtNode { fdt: self.fdt, offset: self.offset }
600 }
601
Jaewan Kime6363422024-01-19 14:00:00 +0900602 /// Adds new subnodes to the given node.
603 pub fn add_subnodes(&mut self, names: &[&CStr]) -> Result<()> {
604 for name in names {
Pierre-Clément Tosid83741d2024-02-02 10:44:55 +0000605 self.fdt.add_subnode_namelen(self.offset, name.to_bytes())?;
Jaewan Kime6363422024-01-19 14:00:00 +0900606 }
607 Ok(())
608 }
609
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900610 /// 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 +0000611 pub fn add_subnode(&'a mut self, name: &CStr) -> Result<Self> {
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000612 let name = name.to_bytes();
Pierre-Clément Tosid83741d2024-02-02 10:44:55 +0000613 let offset = self.fdt.add_subnode_namelen(self.offset, name)?;
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000614
Jaewan Kim5ab13582023-10-20 20:56:27 +0900615 Ok(Self { fdt: self.fdt, offset })
616 }
617
618 /// Adds a new subnode to the given node with name and namelen, and returns it as a FdtNodeMut
619 /// on success.
620 pub fn add_subnode_with_namelen(&'a mut self, name: &CStr, namelen: usize) -> Result<Self> {
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000621 let name = &name.to_bytes()[..namelen];
Pierre-Clément Tosid83741d2024-02-02 10:44:55 +0000622 let offset = self.fdt.add_subnode_namelen(self.offset, name)?;
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000623
Jaewan Kim5ab13582023-10-20 20:56:27 +0900624 Ok(Self { fdt: self.fdt, offset })
625 }
626
Jaewan Kim5f1a6032023-12-18 15:17:58 +0900627 /// Returns the first subnode of this
628 pub fn first_subnode(&'a mut self) -> Result<Option<Self>> {
Pierre-Clément Tosid83741d2024-02-02 10:44:55 +0000629 let offset = self.fdt.first_subnode(self.offset)?;
Jaewan Kim5f1a6032023-12-18 15:17:58 +0900630
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000631 Ok(offset.map(|offset| Self { fdt: self.fdt, offset }))
Jaewan Kim5f1a6032023-12-18 15:17:58 +0900632 }
633
634 /// Returns the next subnode that shares the same parent with this
635 pub fn next_subnode(self) -> Result<Option<Self>> {
Pierre-Clément Tosid83741d2024-02-02 10:44:55 +0000636 let offset = self.fdt.next_subnode(self.offset)?;
Jaewan Kim5f1a6032023-12-18 15:17:58 +0900637
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000638 Ok(offset.map(|offset| Self { fdt: self.fdt, offset }))
Jaewan Kim5f1a6032023-12-18 15:17:58 +0900639 }
640
641 /// Deletes the current node and returns the next subnode
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000642 pub fn delete_and_next_subnode(self) -> Result<Option<Self>> {
Pierre-Clément Tosid83741d2024-02-02 10:44:55 +0000643 let next_offset = self.fdt.next_subnode(self.offset)?;
Jaewan Kim5f1a6032023-12-18 15:17:58 +0900644
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000645 self.delete_and_next(next_offset)
Jaewan Kim5f1a6032023-12-18 15:17:58 +0900646 }
647
Jaewan Kim28a13ea2024-01-04 09:22:40 +0900648 /// Returns the next node
649 pub fn next_node(self, depth: usize) -> Result<Option<(Self, usize)>> {
Pierre-Clément Tosid83741d2024-02-02 10:44:55 +0000650 let next = self.fdt.next_node(self.offset, depth)?;
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000651
652 Ok(next.map(|(offset, depth)| (Self { fdt: self.fdt, offset }, depth)))
Jaewan Kim28a13ea2024-01-04 09:22:40 +0900653 }
654
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000655 /// Deletes this and returns the next node
Pierre-Clément Tosi81c5bc72024-01-29 13:39:07 +0000656 pub fn delete_and_next_node(self, depth: usize) -> Result<Option<(Self, usize)>> {
657 let next_node = self.fdt.next_node_skip_subnodes(self.offset, depth)?;
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000658 if let Some((offset, depth)) = next_node {
659 let next_node = self.delete_and_next(Some(offset))?.unwrap();
660 Ok(Some((next_node, depth)))
661 } else {
662 Ok(None)
663 }
Jaewan Kim28a13ea2024-01-04 09:22:40 +0900664 }
665
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000666 fn parent(&'a self) -> Result<FdtNode<'a>> {
Pierre-Clément Tosidf3037f2024-01-22 15:41:43 +0000667 self.as_node().parent()
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000668 }
Jiyong Park9c63cd12023-03-21 17:53:07 +0900669
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900670 /// Returns the compatible node of the given name that is next after this node.
Jiyong Park9c63cd12023-03-21 17:53:07 +0900671 pub fn next_compatible(self, compatible: &CStr) -> Result<Option<Self>> {
Pierre-Clément Tosid83741d2024-02-02 10:44:55 +0000672 let offset = self.fdt.node_offset_by_compatible(self.offset, compatible)?;
Jiyong Park9c63cd12023-03-21 17:53:07 +0900673
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000674 Ok(offset.map(|offset| Self { fdt: self.fdt, offset }))
Jiyong Park9c63cd12023-03-21 17:53:07 +0900675 }
676
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900677 /// Deletes the node effectively by overwriting this node and its subtree with nop tags.
678 /// Returns the next compatible node of the given name.
Jiyong Park9c63cd12023-03-21 17:53:07 +0900679 // Side note: without this, filterint out excessive compatible nodes from the DT is impossible.
680 // The reason is that libfdt ensures that the node from where the search for the next
681 // compatible node is started is always a valid one -- except for the special case of offset =
682 // -1 which is to find the first compatible node. So, we can't delete a node and then find the
683 // next compatible node from it.
684 //
685 // We can't do in the opposite direction either. If we call next_compatible to find the next
686 // node, and delete the current node, the Rust borrow checker kicks in. The next node has a
687 // mutable reference to DT, so we can't use current node (which also has a mutable reference to
688 // DT).
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000689 pub fn delete_and_next_compatible(self, compatible: &CStr) -> Result<Option<Self>> {
Pierre-Clément Tosid83741d2024-02-02 10:44:55 +0000690 let next_offset = self.fdt.node_offset_by_compatible(self.offset, compatible)?;
Jiyong Park9c63cd12023-03-21 17:53:07 +0900691
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000692 self.delete_and_next(next_offset)
693 }
694
Pierre-Clément Tosid83741d2024-02-02 10:44:55 +0000695 fn delete_and_next(self, next_offset: Option<c_int>) -> Result<Option<Self>> {
Jaewan Kim4ae0e712023-10-19 14:16:17 +0900696 if Some(self.offset) == next_offset {
697 return Err(FdtError::Internal);
698 }
699
Pierre-Clément Tosid83741d2024-02-02 10:44:55 +0000700 self.fdt.nop_node(self.offset)?;
Jiyong Park9c63cd12023-03-21 17:53:07 +0900701
702 Ok(next_offset.map(|offset| Self { fdt: self.fdt, offset }))
703 }
Jaewan Kim4ae0e712023-10-19 14:16:17 +0900704
705 /// Deletes this node effectively from DT, by setting it with FDT_NOP
Pierre-Clément Tosid83741d2024-02-02 10:44:55 +0000706 pub fn nop(self) -> Result<()> {
707 self.fdt.nop_node(self.offset)
Jaewan Kim4ae0e712023-10-19 14:16:17 +0900708 }
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000709}
710
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000711/// Wrapper around low-level libfdt functions.
Alice Wang9d4df702023-05-25 14:14:12 +0000712#[derive(Debug)]
David Brazdil1baa9a92022-06-28 14:47:50 +0100713#[repr(transparent)]
714pub struct Fdt {
Pierre-Clément Tosief2030e2022-11-28 11:21:20 +0000715 buffer: [u8],
David Brazdil1baa9a92022-06-28 14:47:50 +0100716}
717
Pierre-Clément Tosid83741d2024-02-02 10:44:55 +0000718// SAFETY: Fdt calls check_full() before safely returning a &Self, making it impossible for trait
719// methods to be called on invalid device trees.
720unsafe impl Libfdt for Fdt {
721 fn as_fdt_slice(&self) -> &[u8] {
722 &self.buffer[..self.totalsize()]
723 }
724}
725
726// SAFETY: Fdt calls check_full() before safely returning a &Self, making it impossible for trait
727// methods to be called on invalid device trees.
728unsafe impl LibfdtMut for Fdt {
729 fn as_fdt_slice_mut(&mut self) -> &mut [u8] {
730 &mut self.buffer
731 }
732}
733
David Brazdil1baa9a92022-06-28 14:47:50 +0100734impl Fdt {
735 /// Wraps a slice containing a Flattened Device Tree.
736 ///
737 /// Fails if the FDT does not pass validation.
738 pub fn from_slice(fdt: &[u8]) -> Result<&Self> {
Pierre-Clément Tosifbb5ee22023-12-21 13:49:59 +0000739 libfdt::check_full(fdt)?;
740 // SAFETY: The FDT was validated.
David Brazdil1baa9a92022-06-28 14:47:50 +0100741 let fdt = unsafe { Self::unchecked_from_slice(fdt) };
Pierre-Clément Tosifbb5ee22023-12-21 13:49:59 +0000742
David Brazdil1baa9a92022-06-28 14:47:50 +0100743 Ok(fdt)
744 }
745
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000746 /// Wraps a mutable slice containing a Flattened Device Tree.
747 ///
748 /// Fails if the FDT does not pass validation.
749 pub fn from_mut_slice(fdt: &mut [u8]) -> Result<&mut Self> {
Pierre-Clément Tosifbb5ee22023-12-21 13:49:59 +0000750 libfdt::check_full(fdt)?;
751 // SAFETY: The FDT was validated.
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000752 let fdt = unsafe { Self::unchecked_from_mut_slice(fdt) };
Pierre-Clément Tosifbb5ee22023-12-21 13:49:59 +0000753
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000754 Ok(fdt)
755 }
756
Jaewan Kim4cf20aa2023-04-03 10:25:38 +0900757 /// Creates an empty Flattened Device Tree with a mutable slice.
758 pub fn create_empty_tree(fdt: &mut [u8]) -> Result<&mut Self> {
Pierre-Clément Tosifbb5ee22023-12-21 13:49:59 +0000759 libfdt::create_empty_tree(fdt)?;
Jaewan Kim4cf20aa2023-04-03 10:25:38 +0900760
Pierre-Clément Tosifbb5ee22023-12-21 13:49:59 +0000761 Self::from_mut_slice(fdt)
Jaewan Kim4cf20aa2023-04-03 10:25:38 +0900762 }
763
David Brazdil1baa9a92022-06-28 14:47:50 +0100764 /// Wraps a slice containing a Flattened Device Tree.
765 ///
766 /// # Safety
767 ///
Pierre-Clément Tosidf3037f2024-01-22 15:41:43 +0000768 /// 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 +0100769 pub unsafe fn unchecked_from_slice(fdt: &[u8]) -> &Self {
Pierre-Clément Tosidf3037f2024-01-22 15:41:43 +0000770 let self_ptr = fdt as *const _ as *const _;
771 // SAFETY: The pointer is non-null, dereferenceable, and points to allocated memory.
772 unsafe { &*self_ptr }
David Brazdil1baa9a92022-06-28 14:47:50 +0100773 }
774
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000775 /// Wraps a mutable slice containing a Flattened Device Tree.
776 ///
777 /// # Safety
778 ///
Pierre-Clément Tosidf3037f2024-01-22 15:41:43 +0000779 /// 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 +0000780 pub unsafe fn unchecked_from_mut_slice(fdt: &mut [u8]) -> &mut Self {
Pierre-Clément Tosidf3037f2024-01-22 15:41:43 +0000781 let self_mut_ptr = fdt as *mut _ as *mut _;
782 // SAFETY: The pointer is non-null, dereferenceable, and points to allocated memory.
783 unsafe { &mut *self_mut_ptr }
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000784 }
785
Pierre-Clément Tosice0b36d2024-01-26 10:50:05 +0000786 /// Updates this FDT from another FDT.
787 pub fn clone_from(&mut self, other: &Self) -> Result<()> {
788 let new_len = other.buffer.len();
789 if self.buffer.len() < new_len {
790 return Err(FdtError::NoSpace);
Jiyong Parke9d87e82023-03-21 19:28:40 +0900791 }
Pierre-Clément Tosice0b36d2024-01-26 10:50:05 +0000792
793 let zeroed_len = self.totalsize().checked_sub(new_len);
794 let (cloned, zeroed) = self.buffer.split_at_mut(new_len);
795
796 cloned.clone_from_slice(&other.buffer);
797 if let Some(len) = zeroed_len {
798 zeroed[..len].fill(0);
799 }
800
801 Ok(())
Jiyong Parke9d87e82023-03-21 19:28:40 +0900802 }
803
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900804 /// Unpacks the DT to cover the whole slice it is contained in.
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000805 pub fn unpack(&mut self) -> Result<()> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000806 // SAFETY: "Opens" the DT in-place (supported use-case) by updating its header and
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000807 // internal structures to make use of the whole self.fdt slice but performs no accesses
808 // outside of it and leaves the DT in a state that will be detected by other functions.
809 let ret = unsafe {
810 libfdt_bindgen::fdt_open_into(
811 self.as_ptr(),
812 self.as_mut_ptr(),
813 self.capacity().try_into().map_err(|_| FdtError::Internal)?,
814 )
815 };
816 fdt_err_expect_zero(ret)
817 }
818
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900819 /// Packs the DT to take a minimum amount of memory.
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000820 ///
821 /// Doesn't shrink the underlying memory slice.
822 pub fn pack(&mut self) -> Result<()> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000823 // SAFETY: "Closes" the DT in-place by updating its header and relocating its structs.
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000824 let ret = unsafe { libfdt_bindgen::fdt_pack(self.as_mut_ptr()) };
825 fdt_err_expect_zero(ret)
826 }
827
Pierre-Clément Tosi90e19352022-11-21 17:11:48 +0000828 /// Applies a DT overlay on the base DT.
829 ///
830 /// # Safety
831 ///
832 /// On failure, the library corrupts the DT and overlay so both must be discarded.
833 pub unsafe fn apply_overlay<'a>(&'a mut self, overlay: &'a mut Fdt) -> Result<&'a mut Self> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000834 let ret =
835 // SAFETY: Both pointers are valid because they come from references, and fdt_overlay_apply
836 // doesn't keep them after it returns. It may corrupt their contents if there is an error,
837 // but that's our caller's responsibility.
838 unsafe { libfdt_bindgen::fdt_overlay_apply(self.as_mut_ptr(), overlay.as_mut_ptr()) };
839 fdt_err_expect_zero(ret)?;
Pierre-Clément Tosi90e19352022-11-21 17:11:48 +0000840 Ok(self)
841 }
842
Alice Wang2422bdc2023-06-12 08:37:55 +0000843 /// Returns an iterator of memory banks specified the "/memory" node.
844 /// Throws an error when the "/memory" is not found in the device tree.
David Brazdil1baa9a92022-06-28 14:47:50 +0100845 ///
846 /// NOTE: This does not support individual "/memory@XXXX" banks.
Alice Wang2422bdc2023-06-12 08:37:55 +0000847 pub fn memory(&self) -> Result<MemRegIterator> {
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000848 let node = self.node(cstr!("/memory"))?.ok_or(FdtError::NotFound)?;
849 if node.device_type()? != Some(cstr!("memory")) {
Alice Wang2422bdc2023-06-12 08:37:55 +0000850 return Err(FdtError::BadValue);
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000851 }
Alice Wang2422bdc2023-06-12 08:37:55 +0000852 node.reg()?.ok_or(FdtError::BadValue).map(MemRegIterator::new)
853 }
854
855 /// Returns the first memory range in the `/memory` node.
856 pub fn first_memory_range(&self) -> Result<Range<usize>> {
857 self.memory()?.next().ok_or(FdtError::NotFound)
David Brazdil1baa9a92022-06-28 14:47:50 +0100858 }
859
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900860 /// Returns the standard /chosen node.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000861 pub fn chosen(&self) -> Result<Option<FdtNode>> {
Jaewan Kimb635bb02023-11-01 13:00:34 +0900862 self.node(cstr!("/chosen"))
David Brazdil1baa9a92022-06-28 14:47:50 +0100863 }
864
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900865 /// Returns the standard /chosen node as mutable.
Pierre-Clément Tosi4ba79662023-02-13 11:22:41 +0000866 pub fn chosen_mut(&mut self) -> Result<Option<FdtNodeMut>> {
Jaewan Kimb635bb02023-11-01 13:00:34 +0900867 self.node_mut(cstr!("/chosen"))
Pierre-Clément Tosi4ba79662023-02-13 11:22:41 +0000868 }
869
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900870 /// Returns the root node of the tree.
Pierre-Clément Tosi41c158e2022-11-21 19:16:25 +0000871 pub fn root(&self) -> Result<FdtNode> {
Jaewan Kimb635bb02023-11-01 13:00:34 +0900872 self.node(cstr!("/"))?.ok_or(FdtError::Internal)
Pierre-Clément Tosi41c158e2022-11-21 19:16:25 +0000873 }
874
Jaewan Kimf163d762023-11-01 13:12:50 +0900875 /// Returns the standard /__symbols__ node.
876 pub fn symbols(&self) -> Result<Option<FdtNode>> {
877 self.node(cstr!("/__symbols__"))
878 }
879
880 /// Returns the standard /__symbols__ node as mutable
881 pub fn symbols_mut(&mut self) -> Result<Option<FdtNodeMut>> {
882 self.node_mut(cstr!("/__symbols__"))
883 }
884
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900885 /// Returns a tree node by its full path.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000886 pub fn node(&self, path: &CStr) -> Result<Option<FdtNode>> {
Pierre-Clément Tosid83741d2024-02-02 10:44:55 +0000887 let offset = self.path_offset_namelen(path.to_bytes())?;
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000888
889 Ok(offset.map(|offset| FdtNode { fdt: self, offset }))
David Brazdil1baa9a92022-06-28 14:47:50 +0100890 }
891
Pierre-Clément Tosi41c158e2022-11-21 19:16:25 +0000892 /// Iterate over nodes with a given compatible string.
893 pub fn compatible_nodes<'a>(&'a self, compatible: &'a CStr) -> Result<CompatibleIterator<'a>> {
894 CompatibleIterator::new(self, compatible)
895 }
896
Jaewan Kim17ba7a32023-10-19 13:25:15 +0900897 /// Returns max phandle in the tree.
898 pub fn max_phandle(&self) -> Result<Phandle> {
899 let mut phandle: u32 = 0;
900 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
901 let ret = unsafe { libfdt_bindgen::fdt_find_max_phandle(self.as_ptr(), &mut phandle) };
902
903 fdt_err_expect_zero(ret)?;
Pierre-Clément Tosieba27792023-10-30 12:04:12 +0000904 phandle.try_into()
Jaewan Kim17ba7a32023-10-19 13:25:15 +0900905 }
906
907 /// Returns a node with the phandle
908 pub fn node_with_phandle(&self, phandle: Phandle) -> Result<Option<FdtNode>> {
Jaewan Kimc63246d2023-11-09 15:41:01 +0900909 let offset = self.node_offset_with_phandle(phandle)?;
910 Ok(offset.map(|offset| FdtNode { fdt: self, offset }))
911 }
912
913 /// Returns a mutable node with the phandle
914 pub fn node_mut_with_phandle(&mut self, phandle: Phandle) -> Result<Option<FdtNodeMut>> {
915 let offset = self.node_offset_with_phandle(phandle)?;
916 Ok(offset.map(|offset| FdtNodeMut { fdt: self, offset }))
917 }
918
919 fn node_offset_with_phandle(&self, phandle: Phandle) -> Result<Option<c_int>> {
920 // SAFETY: Accesses are constrained to the DT totalsize.
Jaewan Kim17ba7a32023-10-19 13:25:15 +0900921 let ret = unsafe { libfdt_bindgen::fdt_node_offset_by_phandle(self.as_ptr(), phandle.0) };
Jaewan Kimc63246d2023-11-09 15:41:01 +0900922 fdt_err_or_option(ret)
Jaewan Kim17ba7a32023-10-19 13:25:15 +0900923 }
924
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900925 /// Returns the mutable root node of the tree.
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000926 pub fn root_mut(&mut self) -> Result<FdtNodeMut> {
Jaewan Kimb635bb02023-11-01 13:00:34 +0900927 self.node_mut(cstr!("/"))?.ok_or(FdtError::Internal)
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000928 }
929
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900930 /// Returns a mutable tree node by its full path.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000931 pub fn node_mut(&mut self, path: &CStr) -> Result<Option<FdtNodeMut>> {
Pierre-Clément Tosid83741d2024-02-02 10:44:55 +0000932 let offset = self.path_offset_namelen(path.to_bytes())?;
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000933
934 Ok(offset.map(|offset| FdtNodeMut { fdt: self, offset }))
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000935 }
936
Pierre-Clément Tosi81c5bc72024-01-29 13:39:07 +0000937 fn next_node_skip_subnodes(&self, node: c_int, depth: usize) -> Result<Option<(c_int, usize)>> {
938 let mut iter = self.next_node(node, depth)?;
939 while let Some((offset, next_depth)) = iter {
940 if next_depth <= depth {
941 return Ok(Some((offset, next_depth)));
942 }
943 iter = self.next_node(offset, next_depth)?;
944 }
945
946 Ok(None)
947 }
948
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900949 /// Returns the device tree as a slice (may be smaller than the containing buffer).
Pierre-Clément Tosidb74cb12022-12-08 13:56:25 +0000950 pub fn as_slice(&self) -> &[u8] {
Pierre-Clément Tosid83741d2024-02-02 10:44:55 +0000951 self.as_fdt_slice()
David Brazdil1baa9a92022-06-28 14:47:50 +0100952 }
953
Jaewan Kimaa638702023-09-19 13:34:01 +0900954 fn get_from_ptr(&self, ptr: *const c_void, len: usize) -> Result<&[u8]> {
955 let ptr = ptr as usize;
956 let offset = ptr.checked_sub(self.as_ptr() as usize).ok_or(FdtError::Internal)?;
957 self.buffer.get(offset..(offset + len)).ok_or(FdtError::Internal)
958 }
959
Jaewan Kim72d10902023-10-12 21:59:26 +0900960 fn string(&self, offset: c_int) -> Result<&CStr> {
961 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
962 let res = unsafe { libfdt_bindgen::fdt_string(self.as_ptr(), offset) };
963 if res.is_null() {
964 return Err(FdtError::Internal);
965 }
966
967 // SAFETY: Non-null return from fdt_string() is valid null-terminating string within FDT.
968 Ok(unsafe { CStr::from_ptr(res) })
969 }
970
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900971 /// Returns a shared pointer to the device tree.
Pierre-Clément Tosi8036b4f2023-02-17 10:31:31 +0000972 pub fn as_ptr(&self) -> *const c_void {
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000973 self.buffer.as_ptr().cast()
David Brazdil1baa9a92022-06-28 14:47:50 +0100974 }
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000975
976 fn as_mut_ptr(&mut self) -> *mut c_void {
Pierre-Clément Tosi0dcc75e2023-05-02 13:43:55 +0000977 self.buffer.as_mut_ptr().cast::<_>()
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000978 }
979
980 fn capacity(&self) -> usize {
Pierre-Clément Tosief2030e2022-11-28 11:21:20 +0000981 self.buffer.len()
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000982 }
Pierre-Clément Tosidb74cb12022-12-08 13:56:25 +0000983
984 fn header(&self) -> &libfdt_bindgen::fdt_header {
Pierre-Clément Tosicb92b512024-01-22 15:55:25 +0000985 let p = self.as_ptr().cast();
Andrew Walbran84b9a232023-07-05 14:01:40 +0000986 // SAFETY: A valid FDT (verified by constructor) must contain a valid fdt_header.
Pierre-Clément Tosi0dcc75e2023-05-02 13:43:55 +0000987 unsafe { &*p }
Pierre-Clément Tosidb74cb12022-12-08 13:56:25 +0000988 }
989
990 fn totalsize(&self) -> usize {
991 u32::from_be(self.header().totalsize) as usize
992 }
David Brazdil1baa9a92022-06-28 14:47:50 +0100993}