xref: /rk3399_rockchip-uboot/tools/dtoc/fdt.py (revision f7a2aeeeb8d4bbbb9bfd788903942062c8535efb)
1#!/usr/bin/python
2#
3# Copyright (C) 2016 Google, Inc
4# Written by Simon Glass <sjg@chromium.org>
5#
6# SPDX-License-Identifier:      GPL-2.0+
7#
8
9import struct
10import sys
11
12import fdt_util
13
14# This deals with a device tree, presenting it as an assortment of Node and
15# Prop objects, representing nodes and properties, respectively. This file
16# contains the base classes and defines the high-level API. Most of the
17# implementation is in the FdtFallback and FdtNormal subclasses. See
18# fdt_select.py for how to create an Fdt object.
19
20# A list of types we support
21(TYPE_BYTE, TYPE_INT, TYPE_STRING, TYPE_BOOL) = range(4)
22
23def CheckErr(errnum, msg):
24    if errnum:
25        raise ValueError('Error %d: %s: %s' %
26            (errnum, libfdt.fdt_strerror(errnum), msg))
27
28class PropBase:
29    """A device tree property
30
31    Properties:
32        name: Property name (as per the device tree)
33        value: Property value as a string of bytes, or a list of strings of
34            bytes
35        type: Value type
36    """
37    def __init__(self, node, offset, name):
38        self._node = node
39        self._offset = offset
40        self.name = name
41        self.value = None
42
43    def GetPhandle(self):
44        """Get a (single) phandle value from a property
45
46        Gets the phandle valuie from a property and returns it as an integer
47        """
48        return fdt_util.fdt32_to_cpu(self.value[:4])
49
50    def Widen(self, newprop):
51        """Figure out which property type is more general
52
53        Given a current property and a new property, this function returns the
54        one that is less specific as to type. The less specific property will
55        be ble to represent the data in the more specific property. This is
56        used for things like:
57
58            node1 {
59                compatible = "fred";
60                value = <1>;
61            };
62            node1 {
63                compatible = "fred";
64                value = <1 2>;
65            };
66
67        He we want to use an int array for 'value'. The first property
68        suggests that a single int is enough, but the second one shows that
69        it is not. Calling this function with these two propertes would
70        update the current property to be like the second, since it is less
71        specific.
72        """
73        if newprop.type < self.type:
74            self.type = newprop.type
75
76        if type(newprop.value) == list and type(self.value) != list:
77            self.value = [self.value]
78
79        if type(self.value) == list and len(newprop.value) > len(self.value):
80            val = self.GetEmpty(self.type)
81            while len(self.value) < len(newprop.value):
82                self.value.append(val)
83
84    def BytesToValue(self, bytes):
85        """Converts a string of bytes into a type and value
86
87        Args:
88            A string containing bytes
89
90        Return:
91            A tuple:
92                Type of data
93                Data, either a single element or a list of elements. Each element
94                is one of:
95                    TYPE_STRING: string value from the property
96                    TYPE_INT: a byte-swapped integer stored as a 4-byte string
97                    TYPE_BYTE: a byte stored as a single-byte string
98        """
99        size = len(bytes)
100        strings = bytes.split('\0')
101        is_string = True
102        count = len(strings) - 1
103        if count > 0 and not strings[-1]:
104            for string in strings[:-1]:
105                if not string:
106                    is_string = False
107                    break
108                for ch in string:
109                    if ch < ' ' or ch > '~':
110                        is_string = False
111                        break
112        else:
113            is_string = False
114        if is_string:
115            if count == 1:
116                return TYPE_STRING, strings[0]
117            else:
118                return TYPE_STRING, strings[:-1]
119        if size % 4:
120            if size == 1:
121                return TYPE_BYTE, bytes[0]
122            else:
123                return TYPE_BYTE, list(bytes)
124        val = []
125        for i in range(0, size, 4):
126            val.append(bytes[i:i + 4])
127        if size == 4:
128            return TYPE_INT, val[0]
129        else:
130            return TYPE_INT, val
131
132    def GetEmpty(self, type):
133        """Get an empty / zero value of the given type
134
135        Returns:
136            A single value of the given type
137        """
138        if type == TYPE_BYTE:
139            return chr(0)
140        elif type == TYPE_INT:
141            return struct.pack('<I', 0);
142        elif type == TYPE_STRING:
143            return ''
144        else:
145            return True
146
147class NodeBase:
148    """A device tree node
149
150    Properties:
151        offset: Integer offset in the device tree
152        name: Device tree node tname
153        path: Full path to node, along with the node name itself
154        _fdt: Device tree object
155        subnodes: A list of subnodes for this node, each a Node object
156        props: A dict of properties for this node, each a Prop object.
157            Keyed by property name
158    """
159    def __init__(self, fdt, offset, name, path):
160        self._fdt = fdt
161        self._offset = offset
162        self.name = name
163        self.path = path
164        self.subnodes = []
165        self.props = {}
166
167    def _FindNode(self, name):
168        """Find a node given its name
169
170        Args:
171            name: Node name to look for
172        Returns:
173            Node object if found, else None
174        """
175        for subnode in self.subnodes:
176            if subnode.name == name:
177                return subnode
178        return None
179
180    def Scan(self):
181        """Scan the subnodes of a node
182
183        This should be implemented by subclasses
184        """
185        raise NotImplementedError()
186
187class Fdt:
188    """Provides simple access to a flat device tree blob.
189
190    Properties:
191      fname: Filename of fdt
192      _root: Root of device tree (a Node object)
193    """
194    def __init__(self, fname):
195        self._fname = fname
196
197    def Scan(self, root='/'):
198        """Scan a device tree, building up a tree of Node objects
199
200        This fills in the self._root property
201
202        Args:
203            root: Ignored
204
205        TODO(sjg@chromium.org): Implement the 'root' parameter
206        """
207        self._root = self.Node(self, 0, '/', '/')
208        self._root.Scan()
209
210    def GetRoot(self):
211        """Get the root Node of the device tree
212
213        Returns:
214            The root Node object
215        """
216        return self._root
217
218    def GetNode(self, path):
219        """Look up a node from its path
220
221        Args:
222            path: Path to look up, e.g. '/microcode/update@0'
223        Returns:
224            Node object, or None if not found
225        """
226        node = self._root
227        for part in path.split('/')[1:]:
228            node = node._FindNode(part)
229            if not node:
230                return None
231        return node
232
233