]> git.sur5r.net Git - u-boot/blob - tools/dtoc/fdt.py
fdt: Drop fdt_fallback library
[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 FdtNormal subclass. See fdt_select.py for how to
18 # 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         bytes = str(bytes)
100         size = len(bytes)
101         strings = bytes.split('\0')
102         is_string = True
103         count = len(strings) - 1
104         if count > 0 and not strings[-1]:
105             for string in strings[:-1]:
106                 if not string:
107                     is_string = False
108                     break
109                 for ch in string:
110                     if ch < ' ' or ch > '~':
111                         is_string = False
112                         break
113         else:
114             is_string = False
115         if is_string:
116             if count == 1:
117                 return TYPE_STRING, strings[0]
118             else:
119                 return TYPE_STRING, strings[:-1]
120         if size % 4:
121             if size == 1:
122                 return TYPE_BYTE, bytes[0]
123             else:
124                 return TYPE_BYTE, list(bytes)
125         val = []
126         for i in range(0, size, 4):
127             val.append(bytes[i:i + 4])
128         if size == 4:
129             return TYPE_INT, val[0]
130         else:
131             return TYPE_INT, val
132
133     def GetEmpty(self, type):
134         """Get an empty / zero value of the given type
135
136         Returns:
137             A single value of the given type
138         """
139         if type == TYPE_BYTE:
140             return chr(0)
141         elif type == TYPE_INT:
142             return struct.pack('<I', 0);
143         elif type == TYPE_STRING:
144             return ''
145         else:
146             return True
147
148     def GetOffset(self):
149         """Get the offset of a property
150
151         This can be implemented by subclasses.
152
153         Returns:
154             The offset of the property (struct fdt_property) within the
155             file, or None if not known.
156         """
157         return None
158
159 class NodeBase:
160     """A device tree node
161
162     Properties:
163         offset: Integer offset in the device tree
164         name: Device tree node tname
165         path: Full path to node, along with the node name itself
166         _fdt: Device tree object
167         subnodes: A list of subnodes for this node, each a Node object
168         props: A dict of properties for this node, each a Prop object.
169             Keyed by property name
170     """
171     def __init__(self, fdt, offset, name, path):
172         self._fdt = fdt
173         self._offset = offset
174         self.name = name
175         self.path = path
176         self.subnodes = []
177         self.props = {}
178
179     def _FindNode(self, name):
180         """Find a node given its name
181
182         Args:
183             name: Node name to look for
184         Returns:
185             Node object if found, else None
186         """
187         for subnode in self.subnodes:
188             if subnode.name == name:
189                 return subnode
190         return None
191
192     def Scan(self):
193         """Scan the subnodes of a node
194
195         This should be implemented by subclasses
196         """
197         raise NotImplementedError()
198
199     def DeleteProp(self, prop_name):
200         """Delete a property of a node
201
202         This should be implemented by subclasses
203
204         Args:
205             prop_name: Name of the property to delete
206         """
207         raise NotImplementedError()
208
209 class Fdt:
210     """Provides simple access to a flat device tree blob.
211
212     Properties:
213       fname: Filename of fdt
214       _root: Root of device tree (a Node object)
215     """
216     def __init__(self, fname):
217         self._fname = fname
218
219     def Scan(self, root='/'):
220         """Scan a device tree, building up a tree of Node objects
221
222         This fills in the self._root property
223
224         Args:
225             root: Ignored
226
227         TODO(sjg@chromium.org): Implement the 'root' parameter
228         """
229         self._root = self.Node(self, 0, '/', '/')
230         self._root.Scan()
231
232     def GetRoot(self):
233         """Get the root Node of the device tree
234
235         Returns:
236             The root Node object
237         """
238         return self._root
239
240     def GetNode(self, path):
241         """Look up a node from its path
242
243         Args:
244             path: Path to look up, e.g. '/microcode/update@0'
245         Returns:
246             Node object, or None if not found
247         """
248         node = self._root
249         for part in path.split('/')[1:]:
250             node = node._FindNode(part)
251             if not node:
252                 return None
253         return node
254
255     def Flush(self):
256         """Flush device tree changes back to the file
257
258         If the device tree has changed in memory, write it back to the file.
259         Subclasses can implement this if needed.
260         """
261         pass
262
263     def Pack(self):
264         """Pack the device tree down to its minimum size
265
266         When nodes and properties shrink or are deleted, wasted space can
267         build up in the device tree binary. Subclasses can implement this
268         to remove that spare space.
269         """
270         pass