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}