1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
//! Package of [`device_tree`].

use crate::{DeviceError, DeviceResult, PhysAddr, VirtAddr};
use alloc::vec::Vec;
use core::ops::Range;
use device_tree::{DeviceTree as DeviceTreeInner, PropError};

pub use device_tree::{util::StringList, Node};

/// A unified representation of the `interrupts` and `interrupts_extended`
/// properties for any interrupt generating device.
pub type InterruptsProp = Vec<u32>;

/// A wrapper structure of `device_tree::DeviceTree`.
pub struct Devicetree(DeviceTreeInner);

/// Some properties inherited from ancestor nodes.
///
/// About the notion: cell, see <https://elinux.org/Device_Tree_Usage#How_Addressing_Works>.
#[derive(Clone, Copy, Debug, Default)]
pub struct InheritProps {
    /// The `#address-cells` property of its parent node.
    pub parent_address_cells: u32,
    /// The `#size-cells` property of its parent node.
    pub parent_size_cells: u32,
    /// The `interrupt-parent` property of the node. If don't have, inherit from
    /// its parent node.
    pub interrupt_parent: u32,
}

impl Devicetree {
    /// Load the device tree blob from the given virtual address.
    pub fn from(dtb_base_vaddr: VirtAddr) -> DeviceResult<Self> {
        info!("Loading device tree blob from {:#x}", dtb_base_vaddr);
        match unsafe { DeviceTreeInner::load_from_raw_pointer(dtb_base_vaddr as *const _) } {
            Ok(dt) => Ok(Self(dt)),
            Err(err) => {
                warn!(
                    "device-tree: failed to load DTB @ {:#x}: {:?}",
                    dtb_base_vaddr, err
                );
                Err(DeviceError::InvalidParam)
            }
        }
    }

    fn walk_inner<F>(&self, node: &Node, props: InheritProps, device_node_op: &mut F)
    where
        F: FnMut(&Node, &StringList, &InheritProps),
    {
        let mut props = props;
        if let Ok(num) = node.prop_u32("interrupt-parent") {
            props.interrupt_parent = num;
        }
        if let Ok(comp) = node.prop_str_list("compatible") {
            device_node_op(node, &comp, &props);
        }

        props.parent_address_cells = node.prop_u32("#address-cells").unwrap_or(0);
        props.parent_size_cells = node.prop_u32("#size-cells").unwrap_or(0);

        // DFS
        for child in node.children.iter() {
            self.walk_inner(child, props, device_node_op);
        }
    }

    /// Traverse the tree from root by DFS, collect necessary properties, and
    /// apply the `device_node_op` to each node.
    pub fn walk<F>(&self, device_node_op: &mut F)
    where
        F: FnMut(&Node, &StringList, &InheritProps),
    {
        self.walk_inner(&self.0.root, InheritProps::default(), device_node_op)
    }

    /// Returns the `bootargs` property in the `/chosen` node, as the kernel
    /// command line.
    pub fn bootargs(&self) -> Option<&str> {
        self.0.find("/chosen")?.prop_str("bootargs").ok()
    }

    /// Returns the `timebase-frequency` property in the `/cpus` node, as timer
    pub fn timebase_frequency(&self) -> Option<u32> {
        self.0.find("/cpus")?.prop_u32("timebase-frequency").ok()
    }

    /// Returns the `linux,initrd-start` and `linux,initrd-end` properties in
    /// the `/chosen` node, as the init RAM disk address region.
    pub fn initrd_region(&self) -> Option<Range<PhysAddr>> {
        let chosen = self.0.find("/chosen")?;
        let start = chosen.prop_u32("linux,initrd-start").ok()? as _;
        let end = chosen.prop_u32("linux,initrd-end").ok()? as _;
        Some(start..end)
    }

    /// Returns the physical memory regions specified in the `/memory` nodes.
    pub fn memory_regions(&self) -> DeviceResult<Vec<Range<PhysAddr>>> {
        let props = InheritProps {
            parent_address_cells: self.0.root.prop_u32("#address-cells").unwrap_or(0),
            parent_size_cells: self.0.root.prop_u32("#size-cells").unwrap_or(0),
            ..Default::default()
        };

        let mut regions = Vec::new();
        for node in &self.0.root.children {
            if node.name.starts_with("memory@")
                || node.prop_str("device_type").unwrap_or_default() == "memory"
            {
                let (addr, size) = parse_reg(node, &props)?;
                regions.push(addr as usize..addr as usize + size as usize)
            }
        }
        Ok(regions)
    }
}

/// Combine `cell_num` of 32-bit integers from `cells` into a 64-bit integer.
fn from_cells(cells: &[u32], cell_num: u32) -> DeviceResult<u64> {
    if cell_num as usize > cells.len() {
        return Err(DeviceError::InvalidParam);
    }
    let mut value = 0;
    for &c in &cells[..cell_num as usize] {
        value = value << 32 | c as u64;
    }
    Ok(value)
}

/// Parse the `reg` property, about `reg`: <https://elinux.org/Device_Tree_Usage#How_Addressing_Works>.
pub fn parse_reg(node: &Node, props: &InheritProps) -> DeviceResult<(u64, u64)> {
    let cells = node.prop_cells("reg")?;
    let addr = from_cells(&cells, props.parent_address_cells)?;
    let size = from_cells(
        &cells[props.parent_address_cells as usize..],
        props.parent_size_cells,
    )?;
    Ok((addr, size))
}

/// Returns a `Vec<u32>` according to the `interrupts` or `interrupts-extended`
/// property, the first element is the interrupt parent.
pub fn parse_interrupts(node: &Node, props: &InheritProps) -> DeviceResult<InterruptsProp> {
    if node.has_prop("interrupts-extended") {
        Ok(node.prop_cells("interrupts-extended")?)
    } else if node.has_prop("interrupts") && props.interrupt_parent > 0 {
        let mut ret = node.prop_cells("interrupts")?;
        ret.insert(0, props.interrupt_parent);
        Ok(ret)
    } else {
        Ok(Vec::new())
    }
}

impl From<PropError> for DeviceError {
    fn from(_err: PropError) -> Self {
        Self::InvalidParam
    }
}