binman: Drop a special case related to fdt_fallback
[oweals/u-boot.git] / tools / dtoc / fdt_normal.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
13 from fdt import Fdt, NodeBase, PropBase
14 import fdt_util
15 import libfdt
16
17
18 # This deals with a device tree, presenting it as a list of Node and Prop
19 # objects, representing nodes and properties, respectively.
20 #
21 # This implementation uses a libfdt Python library to access the device tree,
22 # so it is fairly efficient.
23
24 def CheckErr(errnum, msg):
25     if errnum:
26         raise ValueError('Error %d: %s: %s' %
27             (errnum, libfdt.fdt_strerror(errnum), msg))
28
29 class Prop(PropBase):
30     """A device tree property
31
32     Properties:
33         name: Property name (as per the device tree)
34         value: Property value as a string of bytes, or a list of strings of
35             bytes
36         type: Value type
37     """
38     def __init__(self, node, offset, name, bytes):
39         PropBase.__init__(self, node, offset, name)
40         self.bytes = str(bytes)
41         if not bytes:
42             self.type = fdt.TYPE_BOOL
43             self.value = True
44             return
45         self.type, self.value = self.BytesToValue(bytes)
46
47     def GetOffset(self):
48         """Get the offset of a property
49
50         Returns:
51             The offset of the property (struct fdt_property) within the file
52         """
53         return self._node._fdt.GetStructOffset(self._offset)
54
55 class Node(NodeBase):
56     """A device tree node
57
58     Properties:
59         offset: Integer offset in the device tree
60         name: Device tree node tname
61         path: Full path to node, along with the node name itself
62         _fdt: Device tree object
63         subnodes: A list of subnodes for this node, each a Node object
64         props: A dict of properties for this node, each a Prop object.
65             Keyed by property name
66     """
67     def __init__(self, fdt, offset, name, path):
68         NodeBase.__init__(self, fdt, offset, name, path)
69
70     def Offset(self):
71         """Returns the offset of a node, after checking the cache
72
73         This should be used instead of self._offset directly, to ensure that
74         the cache does not contain invalid offsets.
75         """
76         self._fdt.CheckCache()
77         return self._offset
78
79     def Scan(self):
80         """Scan a node's properties and subnodes
81
82         This fills in the props and subnodes properties, recursively
83         searching into subnodes so that the entire tree is built.
84         """
85         self.props = self._fdt.GetProps(self)
86
87         offset = libfdt.fdt_first_subnode(self._fdt.GetFdt(), self.Offset())
88         while offset >= 0:
89             sep = '' if self.path[-1] == '/' else '/'
90             name = self._fdt._fdt_obj.get_name(offset)
91             path = self.path + sep + name
92             node = Node(self._fdt, offset, name, path)
93             self.subnodes.append(node)
94
95             node.Scan()
96             offset = libfdt.fdt_next_subnode(self._fdt.GetFdt(), offset)
97
98     def Refresh(self, my_offset):
99         """Fix up the _offset for each node, recursively
100
101         Note: This does not take account of property offsets - these will not
102         be updated.
103         """
104         if self._offset != my_offset:
105             #print '%s: %d -> %d\n' % (self.path, self._offset, my_offset)
106             self._offset = my_offset
107         offset = libfdt.fdt_first_subnode(self._fdt.GetFdt(), self._offset)
108         for subnode in self.subnodes:
109             subnode.Refresh(offset)
110             offset = libfdt.fdt_next_subnode(self._fdt.GetFdt(), offset)
111
112     def DeleteProp(self, prop_name):
113         """Delete a property of a node
114
115         The property is deleted and the offset cache is invalidated.
116
117         Args:
118             prop_name: Name of the property to delete
119         Raises:
120             ValueError if the property does not exist
121         """
122         CheckErr(libfdt.fdt_delprop(self._fdt.GetFdt(), self.Offset(), prop_name),
123                  "Node '%s': delete property: '%s'" % (self.path, prop_name))
124         del self.props[prop_name]
125         self._fdt.Invalidate()
126
127 class FdtNormal(Fdt):
128     """Provides simple access to a flat device tree blob using libfdt.
129
130     Properties:
131         _fdt: Device tree contents (bytearray)
132         _cached_offsets: True if all the nodes have a valid _offset property,
133             False if something has changed to invalidate the offsets
134     """
135     def __init__(self, fname):
136         Fdt.__init__(self, fname)
137         self._cached_offsets = False
138         if self._fname:
139             self._fname = fdt_util.EnsureCompiled(self._fname)
140
141             with open(self._fname) as fd:
142                 self._fdt = bytearray(fd.read())
143                 self._fdt_obj = libfdt.Fdt(self._fdt)
144
145     def GetFdt(self):
146         """Get the contents of the FDT
147
148         Returns:
149             The FDT contents as a string of bytes
150         """
151         return self._fdt
152
153     def Flush(self):
154         """Flush device tree changes back to the file"""
155         with open(self._fname, 'wb') as fd:
156             fd.write(self._fdt)
157
158     def Pack(self):
159         """Pack the device tree down to its minimum size"""
160         CheckErr(libfdt.fdt_pack(self._fdt), 'pack')
161         fdt_len = libfdt.fdt_totalsize(self._fdt)
162         del self._fdt[fdt_len:]
163
164     def GetProps(self, node):
165         """Get all properties from a node.
166
167         Args:
168             node: Full path to node name to look in.
169
170         Returns:
171             A dictionary containing all the properties, indexed by node name.
172             The entries are Prop objects.
173
174         Raises:
175             ValueError: if the node does not exist.
176         """
177         props_dict = {}
178         poffset = libfdt.fdt_first_property_offset(self._fdt, node._offset)
179         while poffset >= 0:
180             p = self._fdt_obj.get_property_by_offset(poffset)
181             prop = Prop(node, poffset, p.name, p.value)
182             props_dict[prop.name] = prop
183
184             poffset = libfdt.fdt_next_property_offset(self._fdt, poffset)
185         return props_dict
186
187     def Invalidate(self):
188         """Mark our offset cache as invalid"""
189         self._cached_offsets = False
190
191     def CheckCache(self):
192         """Refresh the offset cache if needed"""
193         if self._cached_offsets:
194             return
195         self.Refresh()
196         self._cached_offsets = True
197
198     def Refresh(self):
199         """Refresh the offset cache"""
200         self._root.Refresh(0)
201
202     def GetStructOffset(self, offset):
203         """Get the file offset of a given struct offset
204
205         Args:
206             offset: Offset within the 'struct' region of the device tree
207         Returns:
208             Position of @offset within the device tree binary
209         """
210         return libfdt.fdt_off_dt_struct(self._fdt) + offset
211
212     @classmethod
213     def Node(self, fdt, offset, name, path):
214         """Create a new node
215
216         This is used by Fdt.Scan() to create a new node using the correct
217         class.
218
219         Args:
220             fdt: Fdt object
221             offset: Offset of node
222             name: Node name
223             path: Full path to node
224         """
225         node = Node(fdt, offset, name, path)
226         return node