]> git.sur5r.net Git - u-boot/blob - tools/dtoc/fdt.py
Merge git://git.denx.de/u-boot-usb
[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 import libfdt
14
15 # This deals with a device tree, presenting it as an assortment of Node and
16 # Prop objects, representing nodes and properties, respectively. This file
17 # contains the base classes and defines the high-level API. You can use
18 # FdtScan() as a convenience function to create and scan an Fdt.
19
20 # This implementation uses a libfdt Python library to access the device tree,
21 # so it is fairly efficient.
22
23 # A list of types we support
24 (TYPE_BYTE, TYPE_INT, TYPE_STRING, TYPE_BOOL) = range(4)
25
26 def CheckErr(errnum, msg):
27     if errnum:
28         raise ValueError('Error %d: %s: %s' %
29             (errnum, libfdt.fdt_strerror(errnum), msg))
30
31 class Prop:
32     """A device tree property
33
34     Properties:
35         name: Property name (as per the device tree)
36         value: Property value as a string of bytes, or a list of strings of
37             bytes
38         type: Value type
39     """
40     def __init__(self, node, offset, name, bytes):
41         self._node = node
42         self._offset = offset
43         self.name = name
44         self.value = None
45         self.bytes = str(bytes)
46         if not bytes:
47             self.type = TYPE_BOOL
48             self.value = True
49             return
50         self.type, self.value = self.BytesToValue(bytes)
51
52     def GetPhandle(self):
53         """Get a (single) phandle value from a property
54
55         Gets the phandle valuie from a property and returns it as an integer
56         """
57         return fdt_util.fdt32_to_cpu(self.value[:4])
58
59     def Widen(self, newprop):
60         """Figure out which property type is more general
61
62         Given a current property and a new property, this function returns the
63         one that is less specific as to type. The less specific property will
64         be ble to represent the data in the more specific property. This is
65         used for things like:
66
67             node1 {
68                 compatible = "fred";
69                 value = <1>;
70             };
71             node1 {
72                 compatible = "fred";
73                 value = <1 2>;
74             };
75
76         He we want to use an int array for 'value'. The first property
77         suggests that a single int is enough, but the second one shows that
78         it is not. Calling this function with these two propertes would
79         update the current property to be like the second, since it is less
80         specific.
81         """
82         if newprop.type < self.type:
83             self.type = newprop.type
84
85         if type(newprop.value) == list and type(self.value) != list:
86             self.value = [self.value]
87
88         if type(self.value) == list and len(newprop.value) > len(self.value):
89             val = self.GetEmpty(self.type)
90             while len(self.value) < len(newprop.value):
91                 self.value.append(val)
92
93     def BytesToValue(self, bytes):
94         """Converts a string of bytes into a type and value
95
96         Args:
97             A string containing bytes
98
99         Return:
100             A tuple:
101                 Type of data
102                 Data, either a single element or a list of elements. Each element
103                 is one of:
104                     TYPE_STRING: string value from the property
105                     TYPE_INT: a byte-swapped integer stored as a 4-byte string
106                     TYPE_BYTE: a byte stored as a single-byte string
107         """
108         bytes = str(bytes)
109         size = len(bytes)
110         strings = bytes.split('\0')
111         is_string = True
112         count = len(strings) - 1
113         if count > 0 and not strings[-1]:
114             for string in strings[:-1]:
115                 if not string:
116                     is_string = False
117                     break
118                 for ch in string:
119                     if ch < ' ' or ch > '~':
120                         is_string = False
121                         break
122         else:
123             is_string = False
124         if is_string:
125             if count == 1:
126                 return TYPE_STRING, strings[0]
127             else:
128                 return TYPE_STRING, strings[:-1]
129         if size % 4:
130             if size == 1:
131                 return TYPE_BYTE, bytes[0]
132             else:
133                 return TYPE_BYTE, list(bytes)
134         val = []
135         for i in range(0, size, 4):
136             val.append(bytes[i:i + 4])
137         if size == 4:
138             return TYPE_INT, val[0]
139         else:
140             return TYPE_INT, val
141
142     def GetEmpty(self, type):
143         """Get an empty / zero value of the given type
144
145         Returns:
146             A single value of the given type
147         """
148         if type == TYPE_BYTE:
149             return chr(0)
150         elif type == TYPE_INT:
151             return struct.pack('<I', 0);
152         elif type == TYPE_STRING:
153             return ''
154         else:
155             return True
156
157     def GetOffset(self):
158         """Get the offset of a property
159
160         Returns:
161             The offset of the property (struct fdt_property) within the file
162         """
163         return self._node._fdt.GetStructOffset(self._offset)
164
165 class Node:
166     """A device tree node
167
168     Properties:
169         offset: Integer offset in the device tree
170         name: Device tree node tname
171         path: Full path to node, along with the node name itself
172         _fdt: Device tree object
173         subnodes: A list of subnodes for this node, each a Node object
174         props: A dict of properties for this node, each a Prop object.
175             Keyed by property name
176     """
177     def __init__(self, fdt, offset, name, path):
178         self._fdt = fdt
179         self._offset = offset
180         self.name = name
181         self.path = path
182         self.subnodes = []
183         self.props = {}
184
185     def _FindNode(self, name):
186         """Find a node given its name
187
188         Args:
189             name: Node name to look for
190         Returns:
191             Node object if found, else None
192         """
193         for subnode in self.subnodes:
194             if subnode.name == name:
195                 return subnode
196         return None
197
198     def Offset(self):
199         """Returns the offset of a node, after checking the cache
200
201         This should be used instead of self._offset directly, to ensure that
202         the cache does not contain invalid offsets.
203         """
204         self._fdt.CheckCache()
205         return self._offset
206
207     def Scan(self):
208         """Scan a node's properties and subnodes
209
210         This fills in the props and subnodes properties, recursively
211         searching into subnodes so that the entire tree is built.
212         """
213         self.props = self._fdt.GetProps(self)
214
215         offset = libfdt.fdt_first_subnode(self._fdt.GetFdt(), self.Offset())
216         while offset >= 0:
217             sep = '' if self.path[-1] == '/' else '/'
218             name = self._fdt._fdt_obj.get_name(offset)
219             path = self.path + sep + name
220             node = Node(self._fdt, offset, name, path)
221             self.subnodes.append(node)
222
223             node.Scan()
224             offset = libfdt.fdt_next_subnode(self._fdt.GetFdt(), offset)
225
226     def Refresh(self, my_offset):
227         """Fix up the _offset for each node, recursively
228
229         Note: This does not take account of property offsets - these will not
230         be updated.
231         """
232         if self._offset != my_offset:
233             #print '%s: %d -> %d\n' % (self.path, self._offset, my_offset)
234             self._offset = my_offset
235         offset = libfdt.fdt_first_subnode(self._fdt.GetFdt(), self._offset)
236         for subnode in self.subnodes:
237             subnode.Refresh(offset)
238             offset = libfdt.fdt_next_subnode(self._fdt.GetFdt(), offset)
239
240     def DeleteProp(self, prop_name):
241         """Delete a property of a node
242
243         The property is deleted and the offset cache is invalidated.
244
245         Args:
246             prop_name: Name of the property to delete
247         Raises:
248             ValueError if the property does not exist
249         """
250         CheckErr(libfdt.fdt_delprop(self._fdt.GetFdt(), self.Offset(), prop_name),
251                  "Node '%s': delete property: '%s'" % (self.path, prop_name))
252         del self.props[prop_name]
253         self._fdt.Invalidate()
254
255 class Fdt:
256     """Provides simple access to a flat device tree blob using libfdts.
257
258     Properties:
259       fname: Filename of fdt
260       _root: Root of device tree (a Node object)
261     """
262     def __init__(self, fname):
263         self._fname = fname
264         self._cached_offsets = False
265         if self._fname:
266             self._fname = fdt_util.EnsureCompiled(self._fname)
267
268             with open(self._fname) as fd:
269                 self._fdt = bytearray(fd.read())
270                 self._fdt_obj = libfdt.Fdt(self._fdt)
271
272     def Scan(self, root='/'):
273         """Scan a device tree, building up a tree of Node objects
274
275         This fills in the self._root property
276
277         Args:
278             root: Ignored
279
280         TODO(sjg@chromium.org): Implement the 'root' parameter
281         """
282         self._root = self.Node(self, 0, '/', '/')
283         self._root.Scan()
284
285     def GetRoot(self):
286         """Get the root Node of the device tree
287
288         Returns:
289             The root Node object
290         """
291         return self._root
292
293     def GetNode(self, path):
294         """Look up a node from its path
295
296         Args:
297             path: Path to look up, e.g. '/microcode/update@0'
298         Returns:
299             Node object, or None if not found
300         """
301         node = self._root
302         for part in path.split('/')[1:]:
303             node = node._FindNode(part)
304             if not node:
305                 return None
306         return node
307
308     def Flush(self):
309         """Flush device tree changes back to the file
310
311         If the device tree has changed in memory, write it back to the file.
312         """
313         with open(self._fname, 'wb') as fd:
314             fd.write(self._fdt)
315
316     def Pack(self):
317         """Pack the device tree down to its minimum size
318
319         When nodes and properties shrink or are deleted, wasted space can
320         build up in the device tree binary.
321         """
322         CheckErr(libfdt.fdt_pack(self._fdt), 'pack')
323         fdt_len = libfdt.fdt_totalsize(self._fdt)
324         del self._fdt[fdt_len:]
325
326     def GetFdt(self):
327         """Get the contents of the FDT
328
329         Returns:
330             The FDT contents as a string of bytes
331         """
332         return self._fdt
333
334     def CheckErr(errnum, msg):
335         if errnum:
336             raise ValueError('Error %d: %s: %s' %
337                 (errnum, libfdt.fdt_strerror(errnum), msg))
338
339
340     def GetProps(self, node):
341         """Get all properties from a node.
342
343         Args:
344             node: Full path to node name to look in.
345
346         Returns:
347             A dictionary containing all the properties, indexed by node name.
348             The entries are Prop objects.
349
350         Raises:
351             ValueError: if the node does not exist.
352         """
353         props_dict = {}
354         poffset = libfdt.fdt_first_property_offset(self._fdt, node._offset)
355         while poffset >= 0:
356             p = self._fdt_obj.get_property_by_offset(poffset)
357             prop = Prop(node, poffset, p.name, p.value)
358             props_dict[prop.name] = prop
359
360             poffset = libfdt.fdt_next_property_offset(self._fdt, poffset)
361         return props_dict
362
363     def Invalidate(self):
364         """Mark our offset cache as invalid"""
365         self._cached_offsets = False
366
367     def CheckCache(self):
368         """Refresh the offset cache if needed"""
369         if self._cached_offsets:
370             return
371         self.Refresh()
372         self._cached_offsets = True
373
374     def Refresh(self):
375         """Refresh the offset cache"""
376         self._root.Refresh(0)
377
378     def GetStructOffset(self, offset):
379         """Get the file offset of a given struct offset
380
381         Args:
382             offset: Offset within the 'struct' region of the device tree
383         Returns:
384             Position of @offset within the device tree binary
385         """
386         return libfdt.fdt_off_dt_struct(self._fdt) + offset
387
388     @classmethod
389     def Node(self, fdt, offset, name, path):
390         """Create a new node
391
392         This is used by Fdt.Scan() to create a new node using the correct
393         class.
394
395         Args:
396             fdt: Fdt object
397             offset: Offset of node
398             name: Node name
399             path: Full path to node
400         """
401         node = Node(fdt, offset, name, path)
402         return node
403
404 def FdtScan(fname):
405     """Returns a new Fdt object from the implementation we are using"""
406     dtb = Fdt(fname)
407     dtb.Scan()
408     return dtb