]> git.ipfire.org Git - people/ms/u-boot.git/blame - tools/dtoc/fdt.py
bootcount: Migrate CONFIG_SYS_BOOTCOUNT_ADDR
[people/ms/u-boot.git] / tools / dtoc / fdt.py
CommitLineData
a06a34b2
SG
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
9import struct
10import sys
11
12import fdt_util
7b75b448 13import libfdt
a06a34b2
SG
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
99ed4a2e
SG
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.
7b75b448
SG
19
20# This implementation uses a libfdt Python library to access the device tree,
21# so it is fairly efficient.
a06a34b2 22
bc1dea36 23# A list of types we support
fbdfd228 24(TYPE_BYTE, TYPE_INT, TYPE_STRING, TYPE_BOOL, TYPE_INT64) = range(5)
bc1dea36 25
a06a34b2
SG
26def CheckErr(errnum, msg):
27 if errnum:
28 raise ValueError('Error %d: %s: %s' %
29 (errnum, libfdt.fdt_strerror(errnum), msg))
30
7b75b448 31class Prop:
a06a34b2
SG
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 """
7b75b448 40 def __init__(self, node, offset, name, bytes):
a06a34b2
SG
41 self._node = node
42 self._offset = offset
43 self.name = name
44 self.value = None
7b75b448
SG
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)
a06a34b2 51
c322a850
SG
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
bc1dea36
SG
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 """
b4360206 108 bytes = str(bytes)
bc1dea36
SG
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
babdbde6
SG
157 def GetOffset(self):
158 """Get the offset of a property
159
babdbde6 160 Returns:
7b75b448 161 The offset of the property (struct fdt_property) within the file
babdbde6 162 """
7b75b448 163 return self._node._fdt.GetStructOffset(self._offset)
babdbde6 164
7b75b448 165class Node:
a06a34b2
SG
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 """
979ab024 177 def __init__(self, fdt, parent, offset, name, path):
a06a34b2 178 self._fdt = fdt
979ab024 179 self.parent = parent
a06a34b2
SG
180 self._offset = offset
181 self.name = name
182 self.path = path
183 self.subnodes = []
184 self.props = {}
185
f7a2aeee
SG
186 def _FindNode(self, name):
187 """Find a node given its name
188
189 Args:
190 name: Node name to look for
191 Returns:
192 Node object if found, else None
193 """
194 for subnode in self.subnodes:
195 if subnode.name == name:
196 return subnode
197 return None
198
7b75b448
SG
199 def Offset(self):
200 """Returns the offset of a node, after checking the cache
201
202 This should be used instead of self._offset directly, to ensure that
203 the cache does not contain invalid offsets.
204 """
205 self._fdt.CheckCache()
206 return self._offset
207
f7a2aeee 208 def Scan(self):
7b75b448
SG
209 """Scan a node's properties and subnodes
210
211 This fills in the props and subnodes properties, recursively
212 searching into subnodes so that the entire tree is built.
213 """
214 self.props = self._fdt.GetProps(self)
09264e04
SG
215 phandle = self.props.get('phandle')
216 if phandle:
217 val = fdt_util.fdt32_to_cpu(phandle.value)
218 self._fdt.phandle_to_node[val] = self
7b75b448
SG
219
220 offset = libfdt.fdt_first_subnode(self._fdt.GetFdt(), self.Offset())
221 while offset >= 0:
222 sep = '' if self.path[-1] == '/' else '/'
223 name = self._fdt._fdt_obj.get_name(offset)
224 path = self.path + sep + name
979ab024 225 node = Node(self._fdt, self, offset, name, path)
7b75b448 226 self.subnodes.append(node)
f7a2aeee 227
7b75b448
SG
228 node.Scan()
229 offset = libfdt.fdt_next_subnode(self._fdt.GetFdt(), offset)
230
231 def Refresh(self, my_offset):
232 """Fix up the _offset for each node, recursively
233
234 Note: This does not take account of property offsets - these will not
235 be updated.
f7a2aeee 236 """
7b75b448
SG
237 if self._offset != my_offset:
238 #print '%s: %d -> %d\n' % (self.path, self._offset, my_offset)
239 self._offset = my_offset
240 offset = libfdt.fdt_first_subnode(self._fdt.GetFdt(), self._offset)
241 for subnode in self.subnodes:
242 subnode.Refresh(offset)
243 offset = libfdt.fdt_next_subnode(self._fdt.GetFdt(), offset)
f7a2aeee 244
2a70d897
SG
245 def DeleteProp(self, prop_name):
246 """Delete a property of a node
247
7b75b448 248 The property is deleted and the offset cache is invalidated.
2a70d897
SG
249
250 Args:
251 prop_name: Name of the property to delete
7b75b448
SG
252 Raises:
253 ValueError if the property does not exist
2a70d897 254 """
7b75b448
SG
255 CheckErr(libfdt.fdt_delprop(self._fdt.GetFdt(), self.Offset(), prop_name),
256 "Node '%s': delete property: '%s'" % (self.path, prop_name))
257 del self.props[prop_name]
258 self._fdt.Invalidate()
2a70d897 259
a06a34b2 260class Fdt:
7b75b448 261 """Provides simple access to a flat device tree blob using libfdts.
a06a34b2
SG
262
263 Properties:
264 fname: Filename of fdt
265 _root: Root of device tree (a Node object)
266 """
267 def __init__(self, fname):
268 self._fname = fname
7b75b448 269 self._cached_offsets = False
09264e04 270 self.phandle_to_node = {}
7b75b448
SG
271 if self._fname:
272 self._fname = fdt_util.EnsureCompiled(self._fname)
273
274 with open(self._fname) as fd:
275 self._fdt = bytearray(fd.read())
276 self._fdt_obj = libfdt.Fdt(self._fdt)
f7a2aeee
SG
277
278 def Scan(self, root='/'):
279 """Scan a device tree, building up a tree of Node objects
280
281 This fills in the self._root property
282
283 Args:
284 root: Ignored
285
286 TODO(sjg@chromium.org): Implement the 'root' parameter
287 """
979ab024 288 self._root = self.Node(self, None, 0, '/', '/')
f7a2aeee
SG
289 self._root.Scan()
290
291 def GetRoot(self):
292 """Get the root Node of the device tree
293
294 Returns:
295 The root Node object
296 """
297 return self._root
298
299 def GetNode(self, path):
300 """Look up a node from its path
301
302 Args:
303 path: Path to look up, e.g. '/microcode/update@0'
304 Returns:
305 Node object, or None if not found
306 """
307 node = self._root
308 for part in path.split('/')[1:]:
309 node = node._FindNode(part)
310 if not node:
311 return None
312 return node
313
da5f7499
SG
314 def Flush(self):
315 """Flush device tree changes back to the file
316
317 If the device tree has changed in memory, write it back to the file.
da5f7499 318 """
7b75b448
SG
319 with open(self._fname, 'wb') as fd:
320 fd.write(self._fdt)
da5f7499
SG
321
322 def Pack(self):
323 """Pack the device tree down to its minimum size
324
325 When nodes and properties shrink or are deleted, wasted space can
7b75b448
SG
326 build up in the device tree binary.
327 """
328 CheckErr(libfdt.fdt_pack(self._fdt), 'pack')
329 fdt_len = libfdt.fdt_totalsize(self._fdt)
330 del self._fdt[fdt_len:]
331
332 def GetFdt(self):
333 """Get the contents of the FDT
334
335 Returns:
336 The FDT contents as a string of bytes
337 """
338 return self._fdt
339
340 def CheckErr(errnum, msg):
341 if errnum:
342 raise ValueError('Error %d: %s: %s' %
343 (errnum, libfdt.fdt_strerror(errnum), msg))
344
345
346 def GetProps(self, node):
347 """Get all properties from a node.
348
349 Args:
350 node: Full path to node name to look in.
351
352 Returns:
353 A dictionary containing all the properties, indexed by node name.
354 The entries are Prop objects.
355
356 Raises:
357 ValueError: if the node does not exist.
358 """
359 props_dict = {}
360 poffset = libfdt.fdt_first_property_offset(self._fdt, node._offset)
361 while poffset >= 0:
362 p = self._fdt_obj.get_property_by_offset(poffset)
363 prop = Prop(node, poffset, p.name, p.value)
364 props_dict[prop.name] = prop
365
366 poffset = libfdt.fdt_next_property_offset(self._fdt, poffset)
367 return props_dict
368
369 def Invalidate(self):
370 """Mark our offset cache as invalid"""
371 self._cached_offsets = False
372
373 def CheckCache(self):
374 """Refresh the offset cache if needed"""
375 if self._cached_offsets:
376 return
377 self.Refresh()
378 self._cached_offsets = True
379
380 def Refresh(self):
381 """Refresh the offset cache"""
382 self._root.Refresh(0)
383
384 def GetStructOffset(self, offset):
385 """Get the file offset of a given struct offset
386
387 Args:
388 offset: Offset within the 'struct' region of the device tree
389 Returns:
390 Position of @offset within the device tree binary
da5f7499 391 """
7b75b448
SG
392 return libfdt.fdt_off_dt_struct(self._fdt) + offset
393
394 @classmethod
979ab024 395 def Node(self, fdt, parent, offset, name, path):
7b75b448
SG
396 """Create a new node
397
398 This is used by Fdt.Scan() to create a new node using the correct
399 class.
400
401 Args:
402 fdt: Fdt object
979ab024 403 parent: Parent node, or None if this is the root node
7b75b448
SG
404 offset: Offset of node
405 name: Node name
406 path: Full path to node
407 """
979ab024 408 node = Node(fdt, parent, offset, name, path)
7b75b448 409 return node
99ed4a2e
SG
410
411def FdtScan(fname):
412 """Returns a new Fdt object from the implementation we are using"""
413 dtb = Fdt(fname)
414 dtb.Scan()
415 return dtb