xref: /openbmc/u-boot/tools/dtoc/fdt.py (revision fc0b5948)
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
147    def GetOffset(self):
148        """Get the offset of a property
149
150        This can be implemented by subclasses.
151
152        Returns:
153            The offset of the property (struct fdt_property) within the
154            file, or None if not known.
155        """
156        return None
157
158class NodeBase:
159    """A device tree node
160
161    Properties:
162        offset: Integer offset in the device tree
163        name: Device tree node tname
164        path: Full path to node, along with the node name itself
165        _fdt: Device tree object
166        subnodes: A list of subnodes for this node, each a Node object
167        props: A dict of properties for this node, each a Prop object.
168            Keyed by property name
169    """
170    def __init__(self, fdt, offset, name, path):
171        self._fdt = fdt
172        self._offset = offset
173        self.name = name
174        self.path = path
175        self.subnodes = []
176        self.props = {}
177
178    def _FindNode(self, name):
179        """Find a node given its name
180
181        Args:
182            name: Node name to look for
183        Returns:
184            Node object if found, else None
185        """
186        for subnode in self.subnodes:
187            if subnode.name == name:
188                return subnode
189        return None
190
191    def Scan(self):
192        """Scan the subnodes of a node
193
194        This should be implemented by subclasses
195        """
196        raise NotImplementedError()
197
198    def DeleteProp(self, prop_name):
199        """Delete a property of a node
200
201        This should be implemented by subclasses
202
203        Args:
204            prop_name: Name of the property to delete
205        """
206        raise NotImplementedError()
207
208class Fdt:
209    """Provides simple access to a flat device tree blob.
210
211    Properties:
212      fname: Filename of fdt
213      _root: Root of device tree (a Node object)
214    """
215    def __init__(self, fname):
216        self._fname = fname
217
218    def Scan(self, root='/'):
219        """Scan a device tree, building up a tree of Node objects
220
221        This fills in the self._root property
222
223        Args:
224            root: Ignored
225
226        TODO(sjg@chromium.org): Implement the 'root' parameter
227        """
228        self._root = self.Node(self, 0, '/', '/')
229        self._root.Scan()
230
231    def GetRoot(self):
232        """Get the root Node of the device tree
233
234        Returns:
235            The root Node object
236        """
237        return self._root
238
239    def GetNode(self, path):
240        """Look up a node from its path
241
242        Args:
243            path: Path to look up, e.g. '/microcode/update@0'
244        Returns:
245            Node object, or None if not found
246        """
247        node = self._root
248        for part in path.split('/')[1:]:
249            node = node._FindNode(part)
250            if not node:
251                return None
252        return node
253
254    def Flush(self):
255        """Flush device tree changes back to the file
256
257        If the device tree has changed in memory, write it back to the file.
258        Subclasses can implement this if needed.
259        """
260        pass
261
262    def Pack(self):
263        """Pack the device tree down to its minimum size
264
265        When nodes and properties shrink or are deleted, wasted space can
266        build up in the device tree binary. Subclasses can implement this
267        to remove that spare space.
268        """
269        pass
270