xref: /rk3399_rockchip-uboot/tools/dtoc/fdt.py (revision 515d3f08017d2f67fd2e400babadb2c192c5c055)
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 FdtNormal subclass. See fdt_select.py for how to
18# 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        bytes = str(bytes)
100        size = len(bytes)
101        strings = bytes.split('\0')
102        is_string = True
103        count = len(strings) - 1
104        if count > 0 and not strings[-1]:
105            for string in strings[:-1]:
106                if not string:
107                    is_string = False
108                    break
109                for ch in string:
110                    if ch < ' ' or ch > '~':
111                        is_string = False
112                        break
113        else:
114            is_string = False
115        if is_string:
116            if count == 1:
117                return TYPE_STRING, strings[0]
118            else:
119                return TYPE_STRING, strings[:-1]
120        if size % 4:
121            if size == 1:
122                return TYPE_BYTE, bytes[0]
123            else:
124                return TYPE_BYTE, list(bytes)
125        val = []
126        for i in range(0, size, 4):
127            val.append(bytes[i:i + 4])
128        if size == 4:
129            return TYPE_INT, val[0]
130        else:
131            return TYPE_INT, val
132
133    def GetEmpty(self, type):
134        """Get an empty / zero value of the given type
135
136        Returns:
137            A single value of the given type
138        """
139        if type == TYPE_BYTE:
140            return chr(0)
141        elif type == TYPE_INT:
142            return struct.pack('<I', 0);
143        elif type == TYPE_STRING:
144            return ''
145        else:
146            return True
147
148    def GetOffset(self):
149        """Get the offset of a property
150
151        This can be implemented by subclasses.
152
153        Returns:
154            The offset of the property (struct fdt_property) within the
155            file, or None if not known.
156        """
157        return None
158
159class NodeBase:
160    """A device tree node
161
162    Properties:
163        offset: Integer offset in the device tree
164        name: Device tree node tname
165        path: Full path to node, along with the node name itself
166        _fdt: Device tree object
167        subnodes: A list of subnodes for this node, each a Node object
168        props: A dict of properties for this node, each a Prop object.
169            Keyed by property name
170    """
171    def __init__(self, fdt, offset, name, path):
172        self._fdt = fdt
173        self._offset = offset
174        self.name = name
175        self.path = path
176        self.subnodes = []
177        self.props = {}
178
179    def _FindNode(self, name):
180        """Find a node given its name
181
182        Args:
183            name: Node name to look for
184        Returns:
185            Node object if found, else None
186        """
187        for subnode in self.subnodes:
188            if subnode.name == name:
189                return subnode
190        return None
191
192    def Scan(self):
193        """Scan the subnodes of a node
194
195        This should be implemented by subclasses
196        """
197        raise NotImplementedError()
198
199    def DeleteProp(self, prop_name):
200        """Delete a property of a node
201
202        This should be implemented by subclasses
203
204        Args:
205            prop_name: Name of the property to delete
206        """
207        raise NotImplementedError()
208
209class Fdt:
210    """Provides simple access to a flat device tree blob.
211
212    Properties:
213      fname: Filename of fdt
214      _root: Root of device tree (a Node object)
215    """
216    def __init__(self, fname):
217        self._fname = fname
218
219    def Scan(self, root='/'):
220        """Scan a device tree, building up a tree of Node objects
221
222        This fills in the self._root property
223
224        Args:
225            root: Ignored
226
227        TODO(sjg@chromium.org): Implement the 'root' parameter
228        """
229        self._root = self.Node(self, 0, '/', '/')
230        self._root.Scan()
231
232    def GetRoot(self):
233        """Get the root Node of the device tree
234
235        Returns:
236            The root Node object
237        """
238        return self._root
239
240    def GetNode(self, path):
241        """Look up a node from its path
242
243        Args:
244            path: Path to look up, e.g. '/microcode/update@0'
245        Returns:
246            Node object, or None if not found
247        """
248        node = self._root
249        for part in path.split('/')[1:]:
250            node = node._FindNode(part)
251            if not node:
252                return None
253        return node
254
255    def Flush(self):
256        """Flush device tree changes back to the file
257
258        If the device tree has changed in memory, write it back to the file.
259        Subclasses can implement this if needed.
260        """
261        pass
262
263    def Pack(self):
264        """Pack the device tree down to its minimum size
265
266        When nodes and properties shrink or are deleted, wasted space can
267        build up in the device tree binary. Subclasses can implement this
268        to remove that spare space.
269        """
270        pass
271