]> git.sur5r.net Git - u-boot/blob - tools/dtoc/fdt.py
Merge git://git.denx.de/u-boot-fsl-qoriq
[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     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
158 class 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
208 class 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