]> git.sur5r.net Git - u-boot/blob - tools/dtoc/fdt.py
dtoc: Support packing the device tree
[u-boot] / tools / dtoc / fdt.py
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
9 import struct
10 import sys
11
12 import 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
23 def CheckErr(errnum, msg):
24     if errnum:
25         raise ValueError('Error %d: %s: %s' %
26             (errnum, libfdt.fdt_strerror(errnum), msg))
27
28 class 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 class 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
197 class 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     def Flush(self):
244         """Flush device tree changes back to the file
245
246         If the device tree has changed in memory, write it back to the file.
247         Subclasses can implement this if needed.
248         """
249         pass
250
251     def Pack(self):
252         """Pack the device tree down to its minimum size
253
254         When nodes and properties shrink or are deleted, wasted space can
255         build up in the device tree binary. Subclasses can implement this
256         to remove that spare space.
257         """
258         pass