hermit_dtb/lib.rs
1// Copyright (c) 2018 Colin Finck, RWTH Aachen University
2//
3// MIT License
4//
5// Permission is hereby granted, free of charge, to any person obtaining
6// a copy of this software and associated documentation files (the
7// "Software"), to deal in the Software without restriction, including
8// without limitation the rights to use, copy, modify, merge, publish,
9// distribute, sublicense, and/or sell copies of the Software, and to
10// permit persons to whom the Software is furnished to do so, subject to
11// the following conditions:
12//
13// The above copyright notice and this permission notice shall be
14// included in all copies or substantial portions of the Software.
15//
16// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
24#![allow(clippy::missing_safety_doc)]
25#![allow(dead_code)]
26#![no_std]
27
28// MACROS
29macro_rules! align_down {
30 ($value:expr, $alignment:expr) => {
31 $value & !($alignment - 1)
32 };
33}
34
35macro_rules! align_up {
36 ($value:expr, $alignment:expr) => {
37 align_down!($value + ($alignment - 1), $alignment)
38 };
39}
40
41// IMPORTS
42use core::{cmp, mem, slice, str};
43
44// FUNCTIONS
45/// Get the length of a C-style null-terminated byte string, which is part of a larger slice.
46/// The latter requirement makes this function safe to use.
47fn c_strlen_on_slice(slice: &[u8]) -> usize {
48 let mut end = slice;
49 while !end.is_empty() && *end.first().unwrap_or(&0) != 0 {
50 end = &end[1..];
51 }
52
53 end.as_ptr() as usize - slice.as_ptr() as usize
54}
55
56/// Get the token and advance the struct_slice to the next token.
57fn parse_token(struct_slice: &mut &[u8]) -> u32 {
58 let (token_slice, remaining_slice) = struct_slice.split_at(mem::size_of::<u32>());
59 *struct_slice = remaining_slice;
60
61 u32::from_be_bytes(token_slice.try_into().unwrap())
62}
63
64/// Get the node name of a FDT_BEGIN_NODE token and advance the struct_slice to the next token.
65fn parse_begin_node<'a>(struct_slice: &mut &'a [u8]) -> &'a str {
66 let node_name_length = c_strlen_on_slice(struct_slice);
67 let node_name = str::from_utf8(&struct_slice[..node_name_length]).unwrap();
68 let aligned_length = align_up!(node_name_length + 1, mem::size_of::<u32>());
69 *struct_slice = &struct_slice[aligned_length..];
70
71 node_name
72}
73
74/// Get the property data length of a FDT_PROP token and advance the struct_slice to the property name offset.
75fn parse_prop_data_length(struct_slice: &mut &[u8]) -> usize {
76 let (property_length_slice, remaining_slice) = struct_slice.split_at(mem::size_of::<u32>());
77 *struct_slice = remaining_slice;
78
79 u32::from_be_bytes(property_length_slice.try_into().unwrap()) as usize
80}
81
82/// Get the property name of a FDT_PROP token and advance the struct_slice to the next token.
83fn parse_prop_name<'a>(struct_slice: &mut &[u8], strings_slice: &'a [u8]) -> &'a str {
84 // Get the offset of the property name string inside strings_slice.
85 let (property_name_offset_slice, remaining_slice) =
86 struct_slice.split_at(mem::size_of::<u32>());
87 *struct_slice = remaining_slice;
88 let property_name_offset =
89 u32::from_be_bytes(property_name_offset_slice.try_into().unwrap()) as usize;
90
91 // Determine the length of that null-terminated string and return it.
92 let property_name_slice = &strings_slice[property_name_offset..];
93 let property_name_length = c_strlen_on_slice(property_name_slice);
94 let property_name = str::from_utf8(&property_name_slice[..property_name_length]).unwrap();
95
96 property_name
97}
98
99// CONSTANTS
100const DTB_MAGIC: u32 = 0xD00DFEED;
101const DTB_VERSION: u32 = 17;
102
103const FDT_BEGIN_NODE: u32 = 0x00000001;
104const FDT_END_NODE: u32 = 0x00000002;
105const FDT_PROP: u32 = 0x00000003;
106const FDT_NOP: u32 = 0x00000004;
107const FDT_END: u32 = 0x00000009;
108
109// STRUCTURES
110#[repr(C)]
111struct DtbHeader {
112 magic: u32,
113 totalsize: u32,
114 off_dt_struct: u32,
115 off_dt_strings: u32,
116 off_mem_rsvmap: u32,
117 version: u32,
118 last_comp_version: u32,
119 boot_cpuid_phys: u32,
120 size_dt_strings: u32,
121 size_dt_struct: u32,
122}
123
124pub struct Dtb<'a> {
125 header: &'a DtbHeader,
126 struct_slice: &'a [u8],
127 strings_slice: &'a [u8],
128}
129
130impl<'a> Dtb<'a> {
131 fn check_header(header: &DtbHeader) -> bool {
132 u32::from_be(header.magic) == DTB_MAGIC && u32::from_be(header.version) == DTB_VERSION
133 }
134
135 pub unsafe fn from_raw(address: *const u8) -> Option<Self> {
136 let header = &*(address as *const DtbHeader);
137 if !Self::check_header(header) {
138 return None;
139 }
140
141 let address = header as *const _ as usize + u32::from_be(header.off_dt_struct) as usize;
142 let length = u32::from_be(header.size_dt_struct) as usize;
143 let struct_slice = slice::from_raw_parts(address as *const u8, length);
144
145 let address = header as *const _ as usize + u32::from_be(header.off_dt_strings) as usize;
146 let length = u32::from_be(header.size_dt_strings) as usize;
147 let strings_slice = slice::from_raw_parts(address as *const u8, length);
148
149 Some(Self {
150 header,
151 struct_slice,
152 strings_slice,
153 })
154 }
155
156 pub fn enum_subnodes<'b>(&self, path: &'b str) -> EnumSubnodesIter<'a, 'b> {
157 assert!(!path.is_empty());
158
159 EnumSubnodesIter {
160 struct_slice: self.struct_slice,
161 path,
162 nesting_level: 0,
163 looking_on_level: 1,
164 }
165 }
166
167 pub fn enum_properties<'b>(&self, path: &'b str) -> EnumPropertiesIter<'a, 'b> {
168 assert!(!path.is_empty());
169
170 EnumPropertiesIter {
171 struct_slice: self.struct_slice,
172 strings_slice: self.strings_slice,
173 path,
174 nesting_level: 0,
175 looking_on_level: 1,
176 }
177 }
178
179 pub fn get_property(&self, path: &str, property: &str) -> Option<&'a [u8]> {
180 let mut struct_slice = self.struct_slice;
181 let mut path = path;
182 let mut nesting_level = 0;
183 let mut looking_on_level = 1;
184
185 while !struct_slice.is_empty() {
186 let token = parse_token(&mut struct_slice);
187 match token {
188 FDT_BEGIN_NODE => {
189 if path.is_empty() {
190 // This is a subnode of the node we have been looking for.
191 // The Flattened Device Tree Specification states that properties always precede subnodes, so we can stop.
192 struct_slice = &[];
193 } else {
194 // The beginning of a node starts a new nesting level.
195 nesting_level += 1;
196
197 // Get the node information and advance the cursor to the next token.
198 let node_name = parse_begin_node(&mut struct_slice);
199
200 // We're only interested in this node if it is on the nesting level we are looking for.
201 if looking_on_level == nesting_level {
202 // path is advanced with every path component that matches, so we can compare it against
203 // node_name using starts_with().
204 // But path can either contain a full node name (like "uart@fe001000") or leave out the
205 // unit address (like "uart@") to find the first UART device.
206 // Therefore, get the minimum of both lengths and only call starts_with() on that length.
207 let length_to_check = cmp::min(path.len(), node_name.len());
208 let name_to_check = &node_name[..length_to_check];
209
210 if node_name.is_empty() || path.starts_with(name_to_check) {
211 // The current node is either the root node (node_name.is_empty()) or a matching path
212 // component.
213 // Advance path and the nesting level we are looking for.
214 path = &path[length_to_check..];
215 if path.starts_with('/') {
216 // Skip the slash.
217 path = &path[1..];
218 }
219
220 looking_on_level += 1;
221 }
222 }
223 }
224 }
225
226 FDT_END_NODE => {
227 // Finish this nesting level.
228 nesting_level -= 1;
229
230 if path.is_empty() {
231 // If path is empty and we encounter the end of a nesting level, we have iterated over
232 // all properties of the node we were looking for and can stop.
233 struct_slice = &[];
234 }
235 }
236
237 FDT_PROP => {
238 // Get the property data length.
239 let property_length = parse_prop_data_length(&mut struct_slice);
240 let aligned_length = align_up!(property_length, mem::size_of::<u32>());
241
242 if path.is_empty() {
243 // We have reached the node we are looking for.
244 // Now get the property_name to also check if this is the property we are looking for.
245 let property_name = parse_prop_name(&mut struct_slice, self.strings_slice);
246
247 if property_name == property {
248 // It is, so get the data and return it.
249 let property_data = &struct_slice[..property_length];
250 return Some(property_data);
251 } else {
252 // It is not, so just advance the cursor.
253 struct_slice = &struct_slice[aligned_length..];
254 }
255 } else {
256 // Skip over the property name offset and data.
257 struct_slice = &struct_slice[mem::size_of::<u32>()..];
258 struct_slice = &struct_slice[aligned_length..];
259 }
260 }
261
262 FDT_NOP => {
263 // Nothing to be done for NOPs.
264 }
265
266 FDT_END => {
267 // This marks the end of the device tree.
268 struct_slice = &[];
269 }
270
271 _ => {
272 panic!("get_property encountered an invalid token {:#010X} {} bytes before the end", token, struct_slice.len());
273 }
274 }
275 }
276
277 None
278 }
279}
280
281pub struct EnumSubnodesIter<'a, 'b> {
282 struct_slice: &'a [u8],
283 path: &'b str,
284 nesting_level: usize,
285 looking_on_level: usize,
286}
287
288impl<'a, 'b> Iterator for EnumSubnodesIter<'a, 'b> {
289 type Item = &'a str;
290
291 fn next(&mut self) -> Option<&'a str> {
292 while !self.struct_slice.is_empty() {
293 let token = parse_token(&mut self.struct_slice);
294 match token {
295 FDT_BEGIN_NODE => {
296 // The beginning of a node starts a new nesting level.
297 self.nesting_level += 1;
298
299 // Get the node information and advance the cursor to the next token.
300 let node_name = parse_begin_node(&mut self.struct_slice);
301
302 // We're only interested in this node if it is on the nesting level we are looking for.
303 if self.looking_on_level == self.nesting_level {
304 if self.path.is_empty() {
305 // self.path is empty and we are on the right nesting level, so this is a subnode
306 // we are looking for.
307 return Some(node_name);
308 } else {
309 // self.path is advanced with every path component that matches, so we can compare it against
310 // node_name using starts_with().
311 // But self.path can either contain a full node name (like "uart@fe001000") or leave out the
312 // unit address (like "uart@") to find the first UART device.
313 // Therefore, get the minimum of both lengths and only call starts_with() on that length.
314 let length_to_check = cmp::min(self.path.len(), node_name.len());
315 let name_to_check = &node_name[..length_to_check];
316
317 if node_name.is_empty() || self.path.starts_with(name_to_check) {
318 // The current node is either the root node (node_name.is_empty()) or a matching path
319 // component.
320 // Advance self.path and the nesting level we are looking for.
321 self.path = &self.path[length_to_check..];
322 if self.path.starts_with('/') {
323 // Skip the slash.
324 self.path = &self.path[1..];
325 }
326
327 self.looking_on_level += 1;
328 }
329 }
330 }
331 }
332
333 FDT_END_NODE => {
334 // Finish this nesting level.
335 self.nesting_level -= 1;
336
337 if self.nesting_level < self.looking_on_level - 1 {
338 // If the current nesting level is two levels below the level we are looking for,
339 // we have finished enumerating the parent node and can stop.
340 self.struct_slice = &[];
341 }
342 }
343
344 FDT_PROP => {
345 // EnumSubnodesIter is not interested in property information.
346 // Get the property data length.
347 let property_length = parse_prop_data_length(&mut self.struct_slice);
348 let aligned_length = align_up!(property_length, mem::size_of::<u32>());
349
350 // Skip over the property name offset and data.
351 self.struct_slice = &self.struct_slice[mem::size_of::<u32>()..];
352 self.struct_slice = &self.struct_slice[aligned_length..];
353 }
354
355 FDT_NOP => {
356 // Nothing to be done for NOPs.
357 }
358
359 FDT_END => {
360 // This marks the end of the device tree.
361 self.struct_slice = &[];
362 }
363
364 _ => {
365 panic!("EnumSubnodesIter encountered an invalid token {:#010X} {} bytes before the end", token, self.struct_slice.len());
366 }
367 }
368 }
369
370 None
371 }
372}
373
374pub struct EnumPropertiesIter<'a, 'b> {
375 struct_slice: &'a [u8],
376 strings_slice: &'a [u8],
377 path: &'b str,
378 nesting_level: usize,
379 looking_on_level: usize,
380}
381
382impl<'a, 'b> Iterator for EnumPropertiesIter<'a, 'b> {
383 type Item = &'a str;
384
385 fn next(&mut self) -> Option<&'a str> {
386 while !self.struct_slice.is_empty() {
387 let token = parse_token(&mut self.struct_slice);
388 match token {
389 FDT_BEGIN_NODE => {
390 if self.path.is_empty() {
391 // This is a subnode of the node we have been looking for.
392 // The Flattened Device Tree Specification states that properties always precede subnodes, so we can stop.
393 self.struct_slice = &[];
394 } else {
395 // The beginning of a node starts a new nesting level.
396 self.nesting_level += 1;
397
398 // Get the node information and advance the cursor to the next token.
399 let node_name = parse_begin_node(&mut self.struct_slice);
400
401 // We're only interested in this node if it is on the nesting level we are looking for.
402 if self.looking_on_level == self.nesting_level {
403 // self.path is advanced with every path component that matches, so we can compare it against
404 // node_name using starts_with().
405 // But self.path can either contain a full node name (like "uart@fe001000") or leave out the
406 // unit address (like "uart@") to find the first UART device.
407 // Therefore, get the minimum of both lengths and only call starts_with() on that length.
408 let length_to_check = cmp::min(self.path.len(), node_name.len());
409 let name_to_check = &node_name[..length_to_check];
410
411 if node_name.is_empty() || self.path.starts_with(name_to_check) {
412 // The current node is either the root node (node_name.is_empty()) or a matching path
413 // component.
414 // Advance self.path and the nesting level we are looking for.
415 self.path = &self.path[length_to_check..];
416 if self.path.starts_with('/') {
417 // Skip the slash.
418 self.path = &self.path[1..];
419 }
420
421 self.looking_on_level += 1;
422 }
423 }
424 }
425 }
426
427 FDT_END_NODE => {
428 // Finish this nesting level.
429 self.nesting_level -= 1;
430
431 if self.path.is_empty() {
432 // If self.path is empty and we encounter the end of a nesting level, we have iterated over
433 // all properties of the node we were looking for and can stop.
434 self.struct_slice = &[];
435 }
436 }
437
438 FDT_PROP => {
439 // Get the property data length.
440 let property_length = parse_prop_data_length(&mut self.struct_slice);
441 let aligned_length = align_up!(property_length, mem::size_of::<u32>());
442
443 if self.path.is_empty() {
444 // We have reached the node we are looking for and this is a property to enumerate.
445 // So get the property name, skip over the data, and return the name.
446 let property_name =
447 parse_prop_name(&mut self.struct_slice, self.strings_slice);
448 self.struct_slice = &self.struct_slice[aligned_length..];
449 return Some(property_name);
450 } else {
451 // Skip over the property name offset and data.
452 self.struct_slice = &self.struct_slice[mem::size_of::<u32>()..];
453 self.struct_slice = &self.struct_slice[aligned_length..];
454 }
455 }
456
457 FDT_NOP => {
458 // Nothing to be done for NOPs.
459 }
460
461 FDT_END => {
462 // This marks the end of the device tree.
463 self.struct_slice = &[];
464 }
465
466 _ => {
467 panic!("EnumPropertiesIter encountered an invalid token {:#010X} {} bytes before the end", token, self.struct_slice.len());
468 }
469 }
470 }
471
472 None
473 }
474}
475
476#[repr(C)]
477struct DtbReserveEntry {
478 address: u64,
479 size: u64,
480}