device_tree/
lib.rs

1//! Parse flattened linux device trees
2//!
3//! Device trees are used to describe a lot of hardware, especially in the ARM
4//! embedded world and are also used to boot Linux on these device. A device
5//! tree describes addresses and other attributes for many parts on these
6//! boards
7//!
8//! This library allows parsing the so-called flattened device trees, which
9//! are the compiled binary forms of these trees.
10//!
11//! To read more about device trees, check out
12//! [the kernel docs](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/plain/Documentation/devicetree/booting-without-of.txt?id=HEAD).
13//! Some example device trees
14//! to try out are [the Raspberry Pi ones]
15//! (https://github.com/raspberrypi/firmware/tree/master/boot).
16//!
17//! The library does not use `std`, just `core`.
18//!
19//! # Examples
20//!
21//! ```ignore
22//! fn main() {
23//!     // read file into memory
24//!     let mut input = fs::File::open("sample.dtb").unwrap();
25//!     let mut buf = Vec::new();
26//!     input.read_to_end(&mut buf).unwrap();
27//!
28//!     let dt = device_tree::DeviceTree::load(buf.as_slice ()).unwrap();
29//!     println!("{:?}", dt);
30//! }
31//! ```
32
33extern crate core;
34
35pub mod util;
36
37use core::str;
38use util::{align, SliceRead, SliceReadError};
39
40const MAGIC_NUMBER     : u32 = 0xd00dfeed;
41const SUPPORTED_VERSION: u32 = 17;
42const OF_DT_BEGIN_NODE : u32 = 0x00000001;
43const OF_DT_END_NODE   : u32 = 0x00000002;
44const OF_DT_PROP       : u32 = 0x00000003;
45
46
47/// An error describe parsing problems when creating device trees.
48#[derive(Debug)]
49pub enum DeviceTreeError {
50    /// The magic number `MAGIC_NUMBER` was not found at the start of the
51    /// structure.
52    InvalidMagicNumber,
53
54    /// An offset or size found inside the device tree is outside of what was
55    /// supplied to `load()`.
56    SizeMismatch,
57
58    /// Failed to read data from slice.
59    SliceReadError(SliceReadError),
60
61    /// The data format was not as expected at the given position
62    ParseError(usize),
63
64    /// While trying to convert a string that was supposed to be ASCII, invalid
65    /// utf8 sequences were encounted
66    Utf8Error,
67
68    /// The device tree version is not supported by this library.
69    VersionNotSupported,
70}
71
72/// Device tree structure.
73#[derive(Debug)]
74pub struct DeviceTree {
75    /// Version, as indicated by version header
76    pub version: u32,
77
78    /// The number of the CPU the system boots from
79    pub boot_cpuid_phys: u32,
80
81    /// A list of tuples of `(offset, length)`, indicating reserved memory
82    // regions.
83    pub reserved: Vec<(u64, u64)>,
84
85    /// The root node.
86    pub root: Node,
87}
88
89/// A single node in the device tree.
90#[derive(Debug)]
91pub struct Node {
92    /// The name of the node, as it appears in the node path.
93    pub name: String,
94
95    /// A list of node properties, `(key, value)`.
96    pub props: Vec<(String, Vec<u8>)>,
97
98    /// Child nodes of this node.
99    pub children: Vec<Node>,
100}
101
102#[derive(Debug)]
103pub enum PropError {
104    NotFound,
105    Utf8Error,
106    Missing0,
107    SliceReadError(SliceReadError),
108}
109
110impl From<SliceReadError> for DeviceTreeError {
111    fn from(e: SliceReadError) -> DeviceTreeError {
112        DeviceTreeError::SliceReadError(e)
113    }
114}
115
116impl From<str::Utf8Error> for DeviceTreeError {
117    fn from(_: str::Utf8Error) -> DeviceTreeError {
118        DeviceTreeError::Utf8Error
119    }
120}
121
122impl DeviceTree {
123    //! Load a device tree from a memory buffer.
124    pub fn load(buffer: &[u8]) -> Result<DeviceTree, DeviceTreeError> {
125        //  0  magic_number: u32,
126
127        //  4  totalsize: u32,
128        //  8  off_dt_struct: u32,
129        // 12  off_dt_strings: u32,
130        // 16  off_mem_rsvmap: u32,
131        // 20  version: u32,
132        // 24  last_comp_version: u32,
133
134        // // version 2 fields
135        // 28  boot_cpuid_phys: u32,
136
137        // // version 3 fields
138        // 32  size_dt_strings: u32,
139
140        // // version 17 fields
141        // 36  size_dt_struct: u32,
142
143        if try!(buffer.read_be_u32(0)) != MAGIC_NUMBER {
144            return Err(DeviceTreeError::InvalidMagicNumber)
145        }
146
147        // check total size
148        if try!(buffer.read_be_u32(4)) as usize != buffer.len() {
149            return Err(DeviceTreeError::SizeMismatch);
150        }
151
152        // check version
153        let version = try!(buffer.read_be_u32(20));
154        if version != SUPPORTED_VERSION {
155            return Err(DeviceTreeError::VersionNotSupported);
156        }
157
158        let off_dt_struct = try!(buffer.read_be_u32(8)) as usize;
159        let off_dt_strings = try!(buffer.read_be_u32(12)) as usize;
160        let off_mem_rsvmap = try!(buffer.read_be_u32(16)) as usize;
161        let boot_cpuid_phys = try!(buffer.read_be_u32(28));
162
163        // load reserved memory list
164        let mut reserved = Vec::new();
165        let mut pos = off_mem_rsvmap;
166
167        loop {
168            let offset = try!(buffer.read_be_u64(pos));
169            pos += 8;
170            let size = try!(buffer.read_be_u64(pos));
171            pos += 8;
172
173            reserved.push((offset, size));
174
175            if size == 0 {
176                break;
177            }
178        }
179
180        let (_, root) = try!(Node::load(buffer, off_dt_struct, off_dt_strings));
181
182        Ok(DeviceTree{
183            version: version,
184            boot_cpuid_phys: boot_cpuid_phys,
185            reserved: reserved,
186            root: root,
187        })
188    }
189
190    pub fn find<'a>(&'a self, path: &str) -> Option<&'a Node> {
191        // we only find root nodes on the device tree
192        if ! path.starts_with('/') {
193            return None
194        }
195
196        self.root.find(&path[1..])
197    }
198}
199
200
201impl Node {
202    fn load(buffer: &[u8], start: usize, off_dt_strings: usize)
203    -> Result<(usize, Node), DeviceTreeError> {
204        // check for DT_BEGIN_NODE
205        if try!(buffer.read_be_u32(start)) != OF_DT_BEGIN_NODE {
206            return Err(DeviceTreeError::ParseError(start))
207        }
208
209        let raw_name = try!(buffer.read_bstring0(start+4));
210
211        // read all the props
212        let mut pos = align(start + 4 + raw_name.len() + 1, 4);
213
214        let mut props = Vec::new();
215
216        while try!(buffer.read_be_u32(pos)) == OF_DT_PROP {
217            let val_size = try!(buffer.read_be_u32(pos+4)) as usize;
218            let name_offset = try!(buffer.read_be_u32(pos+8)) as usize;
219
220            // get value slice
221            let val_start = pos + 12;
222            let val_end = val_start + val_size;
223            let val = try!(buffer.subslice(val_start, val_end));
224
225            // lookup name in strings table
226            let prop_name = try!(
227                buffer.read_bstring0(off_dt_strings + name_offset)
228            );
229
230            props.push((
231                try!(str::from_utf8(prop_name)).to_owned(),
232                val.to_owned(),
233            ));
234
235            pos = align(val_end, 4);
236        }
237
238        // finally, parse children
239        let mut children = Vec::new();
240
241        while try!(buffer.read_be_u32(pos)) == OF_DT_BEGIN_NODE {
242            let (new_pos, child_node) = try!(Node::load(buffer, pos,
243                off_dt_strings));
244            pos = new_pos;
245
246            children.push(child_node);
247        }
248
249        if try!(buffer.read_be_u32(pos)) != OF_DT_END_NODE {
250            return Err(DeviceTreeError::ParseError(pos))
251        }
252
253        pos += 4;
254
255        Ok((pos, Node{
256            name: try!(str::from_utf8(raw_name)).to_owned(),
257            props: props,
258            children: children,
259        }))
260    }
261
262    pub fn find<'a>(&'a self, path: &str) -> Option<&'a Node> {
263        if path == "" {
264            return Some(self)
265        }
266
267        match path.find('/') {
268            Some(idx) => {
269                // find should return the proper index, so we're safe to
270                // use indexing here
271                let (l, r) = path.split_at(idx);
272
273                // we know that the first char of slashed is a '/'
274                let subpath = &r[1..];
275
276                for child in self.children.iter() {
277                    if child.name == l {
278                        return child.find(subpath);
279                    }
280                }
281
282                // no matching child found
283                None
284            },
285            None => self.children.iter().find(|n| n.name == path)
286        }
287    }
288
289    pub fn has_prop(&self, name: &str) -> bool {
290        if let Some(_) = self.prop_raw(name) {
291            true
292        } else {
293            false
294        }
295    }
296
297    pub fn prop_str<'a>(&'a self, name: &str) -> Result<&'a str, PropError> {
298        let raw = try!(self.prop_raw(name).ok_or(PropError::NotFound));
299
300        let l = raw.len();
301        if l < 1 || raw[l-1] != 0 {
302            return Err(PropError::Missing0)
303        }
304
305        Ok(try!(str::from_utf8(&raw[..(l-1)])))
306    }
307
308    pub fn prop_raw<'a>(&'a self, name: &str) -> Option<&'a Vec<u8>> {
309        for &(ref key, ref val) in self.props.iter() {
310            if key == name {
311                return Some(val)
312            }
313        }
314        None
315    }
316
317    pub fn prop_u64(&self, name: &str) -> Result<u64, PropError> {
318        let raw = try!(self.prop_raw(name).ok_or(PropError::NotFound));
319
320        Ok(try!(raw.as_slice().read_be_u64(0)))
321    }
322
323    pub fn prop_u32(&self, name: &str) -> Result<u32, PropError> {
324        let raw = try!(self.prop_raw(name).ok_or(PropError::NotFound));
325
326        Ok(try!(raw.as_slice().read_be_u32(0)))
327    }
328}
329
330impl From<str::Utf8Error> for PropError {
331    fn from(_: str::Utf8Error) -> PropError {
332        PropError::Utf8Error
333    }
334}
335
336impl From<SliceReadError> for PropError {
337    fn from(e: SliceReadError) -> PropError {
338        PropError::SliceReadError(e)
339    }
340}