xref: /rk3399_rockchip-uboot/tools/dtoc/fdt.py (revision 2a70d897ed68fd521411a10831ac05e1ffdd3d41)
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
187    def DeleteProp(self, prop_name):
188        """Delete a property of a node
189
190        This should be implemented by subclasses
191
192        Args:
193            prop_name: Name of the property to delete
194        """
195        raise NotImplementedError()
196
197class Fdt:
198    """Provides simple access to a flat device tree blob.
199
200    Properties:
201      fname: Filename of fdt
202      _root: Root of device tree (a Node object)
203    """
204    def __init__(self, fname):
205        self._fname = fname
206
207    def Scan(self, root='/'):
208        """Scan a device tree, building up a tree of Node objects
209
210        This fills in the self._root property
211
212        Args:
213            root: Ignored
214
215        TODO(sjg@chromium.org): Implement the 'root' parameter
216        """
217        self._root = self.Node(self, 0, '/', '/')
218        self._root.Scan()
219
220    def GetRoot(self):
221        """Get the root Node of the device tree
222
223        Returns:
224            The root Node object
225        """
226        return self._root
227
228    def GetNode(self, path):
229        """Look up a node from its path
230
231        Args:
232            path: Path to look up, e.g. '/microcode/update@0'
233        Returns:
234            Node object, or None if not found
235        """
236        node = self._root
237        for part in path.split('/')[1:]:
238            node = node._FindNode(part)
239            if not node:
240                return None
241        return node
242
243