blob: 9518a287a264af2644b687bbf99f674eb940b71b [file] [log] [blame]
Simon Glassa06a34b2016-07-25 18:59:04 -06001#!/usr/bin/python
Tom Rini83d290c2018-05-06 17:58:06 -04002# SPDX-License-Identifier: GPL-2.0+
Simon Glassa06a34b2016-07-25 18:59:04 -06003#
4# Copyright (C) 2016 Google, Inc
5# Written by Simon Glass <sjg@chromium.org>
6#
Simon Glassa06a34b2016-07-25 18:59:04 -06007
8import struct
9import sys
10
11import fdt_util
Simon Glass7b75b442017-05-27 07:38:28 -060012import libfdt
Simon Glass117f57b2018-07-06 10:27:26 -060013from libfdt import QUIET_NOTFOUND
Simon Glass194b8d52019-05-17 22:00:33 -060014import tools
Simon Glassa06a34b2016-07-25 18:59:04 -060015
16# This deals with a device tree, presenting it as an assortment of Node and
17# Prop objects, representing nodes and properties, respectively. This file
Simon Glass99ed4a22017-05-27 07:38:30 -060018# contains the base classes and defines the high-level API. You can use
19# FdtScan() as a convenience function to create and scan an Fdt.
Simon Glass7b75b442017-05-27 07:38:28 -060020
21# This implementation uses a libfdt Python library to access the device tree,
22# so it is fairly efficient.
Simon Glassa06a34b2016-07-25 18:59:04 -060023
Simon Glassbc1dea32016-07-25 18:59:05 -060024# A list of types we support
Simon Glassfbdfd222017-08-29 14:15:48 -060025(TYPE_BYTE, TYPE_INT, TYPE_STRING, TYPE_BOOL, TYPE_INT64) = range(5)
Simon Glassbc1dea32016-07-25 18:59:05 -060026
Simon Glassa06a34b2016-07-25 18:59:04 -060027def CheckErr(errnum, msg):
28 if errnum:
29 raise ValueError('Error %d: %s: %s' %
30 (errnum, libfdt.fdt_strerror(errnum), msg))
31
Simon Glass7b75b442017-05-27 07:38:28 -060032class Prop:
Simon Glassa06a34b2016-07-25 18:59:04 -060033 """A device tree property
34
35 Properties:
36 name: Property name (as per the device tree)
37 value: Property value as a string of bytes, or a list of strings of
38 bytes
39 type: Value type
40 """
Simon Glass7b75b442017-05-27 07:38:28 -060041 def __init__(self, node, offset, name, bytes):
Simon Glassa06a34b2016-07-25 18:59:04 -060042 self._node = node
43 self._offset = offset
44 self.name = name
45 self.value = None
Simon Glass7b75b442017-05-27 07:38:28 -060046 self.bytes = str(bytes)
Simon Glassfa80c252018-09-14 04:57:13 -060047 self.dirty = False
Simon Glass7b75b442017-05-27 07:38:28 -060048 if not bytes:
49 self.type = TYPE_BOOL
50 self.value = True
51 return
52 self.type, self.value = self.BytesToValue(bytes)
Simon Glassa06a34b2016-07-25 18:59:04 -060053
Simon Glassf9b88b32018-07-06 10:27:29 -060054 def RefreshOffset(self, poffset):
55 self._offset = poffset
56
Simon Glassc322a852016-07-25 18:59:06 -060057 def Widen(self, newprop):
58 """Figure out which property type is more general
59
60 Given a current property and a new property, this function returns the
61 one that is less specific as to type. The less specific property will
62 be ble to represent the data in the more specific property. This is
63 used for things like:
64
65 node1 {
66 compatible = "fred";
67 value = <1>;
68 };
69 node1 {
70 compatible = "fred";
71 value = <1 2>;
72 };
73
74 He we want to use an int array for 'value'. The first property
75 suggests that a single int is enough, but the second one shows that
76 it is not. Calling this function with these two propertes would
77 update the current property to be like the second, since it is less
78 specific.
79 """
80 if newprop.type < self.type:
81 self.type = newprop.type
82
83 if type(newprop.value) == list and type(self.value) != list:
84 self.value = [self.value]
85
86 if type(self.value) == list and len(newprop.value) > len(self.value):
87 val = self.GetEmpty(self.type)
88 while len(self.value) < len(newprop.value):
89 self.value.append(val)
90
Simon Glassbc1dea32016-07-25 18:59:05 -060091 def BytesToValue(self, bytes):
92 """Converts a string of bytes into a type and value
93
94 Args:
95 A string containing bytes
96
97 Return:
98 A tuple:
99 Type of data
100 Data, either a single element or a list of elements. Each element
101 is one of:
102 TYPE_STRING: string value from the property
103 TYPE_INT: a byte-swapped integer stored as a 4-byte string
104 TYPE_BYTE: a byte stored as a single-byte string
105 """
Simon Glassb4360202017-05-27 07:38:22 -0600106 bytes = str(bytes)
Simon Glassbc1dea32016-07-25 18:59:05 -0600107 size = len(bytes)
108 strings = bytes.split('\0')
109 is_string = True
110 count = len(strings) - 1
111 if count > 0 and not strings[-1]:
112 for string in strings[:-1]:
113 if not string:
114 is_string = False
115 break
116 for ch in string:
117 if ch < ' ' or ch > '~':
118 is_string = False
119 break
120 else:
121 is_string = False
122 if is_string:
123 if count == 1:
124 return TYPE_STRING, strings[0]
125 else:
126 return TYPE_STRING, strings[:-1]
127 if size % 4:
128 if size == 1:
129 return TYPE_BYTE, bytes[0]
130 else:
131 return TYPE_BYTE, list(bytes)
132 val = []
133 for i in range(0, size, 4):
134 val.append(bytes[i:i + 4])
135 if size == 4:
136 return TYPE_INT, val[0]
137 else:
138 return TYPE_INT, val
139
Simon Glass2ba98752018-07-06 10:27:24 -0600140 @classmethod
Simon Glassbc1dea32016-07-25 18:59:05 -0600141 def GetEmpty(self, type):
142 """Get an empty / zero value of the given type
143
144 Returns:
145 A single value of the given type
146 """
147 if type == TYPE_BYTE:
148 return chr(0)
149 elif type == TYPE_INT:
Simon Glassaf53f5a2018-09-14 04:57:14 -0600150 return struct.pack('>I', 0);
Simon Glassbc1dea32016-07-25 18:59:05 -0600151 elif type == TYPE_STRING:
152 return ''
153 else:
154 return True
155
Simon Glassbabdbde2016-07-25 18:59:16 -0600156 def GetOffset(self):
157 """Get the offset of a property
158
Simon Glassbabdbde2016-07-25 18:59:16 -0600159 Returns:
Simon Glass7b75b442017-05-27 07:38:28 -0600160 The offset of the property (struct fdt_property) within the file
Simon Glassbabdbde2016-07-25 18:59:16 -0600161 """
Simon Glassf9b88b32018-07-06 10:27:29 -0600162 self._node._fdt.CheckCache()
Simon Glass7b75b442017-05-27 07:38:28 -0600163 return self._node._fdt.GetStructOffset(self._offset)
Simon Glassbabdbde2016-07-25 18:59:16 -0600164
Simon Glassfa80c252018-09-14 04:57:13 -0600165 def SetInt(self, val):
166 """Set the integer value of the property
167
168 The device tree is marked dirty so that the value will be written to
169 the block on the next sync.
170
171 Args:
172 val: Integer value (32-bit, single cell)
173 """
174 self.bytes = struct.pack('>I', val);
Simon Glass41b781d2018-10-01 12:22:49 -0600175 self.value = self.bytes
Simon Glassfa80c252018-09-14 04:57:13 -0600176 self.type = TYPE_INT
177 self.dirty = True
178
Simon Glass64349612018-09-14 04:57:16 -0600179 def SetData(self, bytes):
180 """Set the value of a property as bytes
181
182 Args:
183 bytes: New property value to set
184 """
185 self.bytes = str(bytes)
186 self.type, self.value = self.BytesToValue(bytes)
187 self.dirty = True
188
Simon Glassfa80c252018-09-14 04:57:13 -0600189 def Sync(self, auto_resize=False):
190 """Sync property changes back to the device tree
191
192 This updates the device tree blob with any changes to this property
193 since the last sync.
194
195 Args:
196 auto_resize: Resize the device tree automatically if it does not
197 have enough space for the update
198
199 Raises:
200 FdtException if auto_resize is False and there is not enough space
201 """
202 if self._offset is None or self.dirty:
203 node = self._node
204 fdt_obj = node._fdt._fdt_obj
205 if auto_resize:
206 while fdt_obj.setprop(node.Offset(), self.name, self.bytes,
207 (libfdt.NOSPACE,)) == -libfdt.NOSPACE:
208 fdt_obj.resize(fdt_obj.totalsize() + 1024)
209 fdt_obj.setprop(node.Offset(), self.name, self.bytes)
210 else:
211 fdt_obj.setprop(node.Offset(), self.name, self.bytes)
212
213
Simon Glass7b75b442017-05-27 07:38:28 -0600214class Node:
Simon Glassa06a34b2016-07-25 18:59:04 -0600215 """A device tree node
216
217 Properties:
218 offset: Integer offset in the device tree
219 name: Device tree node tname
220 path: Full path to node, along with the node name itself
221 _fdt: Device tree object
222 subnodes: A list of subnodes for this node, each a Node object
223 props: A dict of properties for this node, each a Prop object.
224 Keyed by property name
225 """
Simon Glass979ab022017-08-29 14:15:47 -0600226 def __init__(self, fdt, parent, offset, name, path):
Simon Glassa06a34b2016-07-25 18:59:04 -0600227 self._fdt = fdt
Simon Glass979ab022017-08-29 14:15:47 -0600228 self.parent = parent
Simon Glassa06a34b2016-07-25 18:59:04 -0600229 self._offset = offset
230 self.name = name
231 self.path = path
232 self.subnodes = []
233 self.props = {}
234
Simon Glass94a7c602018-07-17 13:25:46 -0600235 def GetFdt(self):
236 """Get the Fdt object for this node
237
238 Returns:
239 Fdt object
240 """
241 return self._fdt
242
Simon Glass1d858882018-07-17 13:25:41 -0600243 def FindNode(self, name):
Simon Glassf7a2aee2016-07-25 18:59:07 -0600244 """Find a node given its name
245
246 Args:
247 name: Node name to look for
248 Returns:
249 Node object if found, else None
250 """
251 for subnode in self.subnodes:
252 if subnode.name == name:
253 return subnode
254 return None
255
Simon Glass7b75b442017-05-27 07:38:28 -0600256 def Offset(self):
257 """Returns the offset of a node, after checking the cache
Simon Glassf7a2aee2016-07-25 18:59:07 -0600258
Simon Glass7b75b442017-05-27 07:38:28 -0600259 This should be used instead of self._offset directly, to ensure that
260 the cache does not contain invalid offsets.
Simon Glassf7a2aee2016-07-25 18:59:07 -0600261 """
Simon Glass7b75b442017-05-27 07:38:28 -0600262 self._fdt.CheckCache()
263 return self._offset
264
265 def Scan(self):
266 """Scan a node's properties and subnodes
267
268 This fills in the props and subnodes properties, recursively
269 searching into subnodes so that the entire tree is built.
270 """
Simon Glass117f57b2018-07-06 10:27:26 -0600271 fdt_obj = self._fdt._fdt_obj
Simon Glass7b75b442017-05-27 07:38:28 -0600272 self.props = self._fdt.GetProps(self)
Simon Glass117f57b2018-07-06 10:27:26 -0600273 phandle = fdt_obj.get_phandle(self.Offset())
Simon Glass09264e02017-08-29 14:15:52 -0600274 if phandle:
Simon Glass117f57b2018-07-06 10:27:26 -0600275 self._fdt.phandle_to_node[phandle] = self
Simon Glass7b75b442017-05-27 07:38:28 -0600276
Simon Glass117f57b2018-07-06 10:27:26 -0600277 offset = fdt_obj.first_subnode(self.Offset(), QUIET_NOTFOUND)
Simon Glass7b75b442017-05-27 07:38:28 -0600278 while offset >= 0:
279 sep = '' if self.path[-1] == '/' else '/'
Simon Glass117f57b2018-07-06 10:27:26 -0600280 name = fdt_obj.get_name(offset)
Simon Glass7b75b442017-05-27 07:38:28 -0600281 path = self.path + sep + name
Simon Glass979ab022017-08-29 14:15:47 -0600282 node = Node(self._fdt, self, offset, name, path)
Simon Glass7b75b442017-05-27 07:38:28 -0600283 self.subnodes.append(node)
284
285 node.Scan()
Simon Glass117f57b2018-07-06 10:27:26 -0600286 offset = fdt_obj.next_subnode(offset, QUIET_NOTFOUND)
Simon Glass7b75b442017-05-27 07:38:28 -0600287
288 def Refresh(self, my_offset):
289 """Fix up the _offset for each node, recursively
290
291 Note: This does not take account of property offsets - these will not
292 be updated.
293 """
Simon Glass96066242018-07-06 10:27:27 -0600294 fdt_obj = self._fdt._fdt_obj
Simon Glass7b75b442017-05-27 07:38:28 -0600295 if self._offset != my_offset:
Simon Glass7b75b442017-05-27 07:38:28 -0600296 self._offset = my_offset
Simon Glass96066242018-07-06 10:27:27 -0600297 offset = fdt_obj.first_subnode(self._offset, QUIET_NOTFOUND)
Simon Glass7b75b442017-05-27 07:38:28 -0600298 for subnode in self.subnodes:
Simon Glassf9b88b32018-07-06 10:27:29 -0600299 if subnode.name != fdt_obj.get_name(offset):
300 raise ValueError('Internal error, node name mismatch %s != %s' %
301 (subnode.name, fdt_obj.get_name(offset)))
Simon Glass7b75b442017-05-27 07:38:28 -0600302 subnode.Refresh(offset)
Simon Glass96066242018-07-06 10:27:27 -0600303 offset = fdt_obj.next_subnode(offset, QUIET_NOTFOUND)
Simon Glassf9b88b32018-07-06 10:27:29 -0600304 if offset != -libfdt.FDT_ERR_NOTFOUND:
305 raise ValueError('Internal error, offset == %d' % offset)
306
307 poffset = fdt_obj.first_property_offset(self._offset, QUIET_NOTFOUND)
308 while poffset >= 0:
309 p = fdt_obj.get_property_by_offset(poffset)
310 prop = self.props.get(p.name)
311 if not prop:
312 raise ValueError("Internal error, property '%s' missing, "
313 'offset %d' % (p.name, poffset))
314 prop.RefreshOffset(poffset)
315 poffset = fdt_obj.next_property_offset(poffset, QUIET_NOTFOUND)
Simon Glassf7a2aee2016-07-25 18:59:07 -0600316
Simon Glass2a70d892016-07-25 18:59:14 -0600317 def DeleteProp(self, prop_name):
318 """Delete a property of a node
319
Simon Glass7b75b442017-05-27 07:38:28 -0600320 The property is deleted and the offset cache is invalidated.
Simon Glass2a70d892016-07-25 18:59:14 -0600321
322 Args:
323 prop_name: Name of the property to delete
Simon Glass7b75b442017-05-27 07:38:28 -0600324 Raises:
325 ValueError if the property does not exist
Simon Glass2a70d892016-07-25 18:59:14 -0600326 """
Simon Glass96066242018-07-06 10:27:27 -0600327 CheckErr(self._fdt._fdt_obj.delprop(self.Offset(), prop_name),
Simon Glass7b75b442017-05-27 07:38:28 -0600328 "Node '%s': delete property: '%s'" % (self.path, prop_name))
329 del self.props[prop_name]
330 self._fdt.Invalidate()
Simon Glass2a70d892016-07-25 18:59:14 -0600331
Simon Glass116adec2018-07-06 10:27:38 -0600332 def AddZeroProp(self, prop_name):
333 """Add a new property to the device tree with an integer value of 0.
334
335 Args:
336 prop_name: Name of property
337 """
Simon Glass194b8d52019-05-17 22:00:33 -0600338 self.props[prop_name] = Prop(self, None, prop_name,
339 tools.GetBytes(0, 4))
Simon Glass116adec2018-07-06 10:27:38 -0600340
Simon Glass64349612018-09-14 04:57:16 -0600341 def AddEmptyProp(self, prop_name, len):
342 """Add a property with a fixed data size, for filling in later
343
344 The device tree is marked dirty so that the value will be written to
345 the blob on the next sync.
346
347 Args:
348 prop_name: Name of property
349 len: Length of data in property
350 """
Simon Glass194b8d52019-05-17 22:00:33 -0600351 value = tools.GetBytes(0, len)
Simon Glass64349612018-09-14 04:57:16 -0600352 self.props[prop_name] = Prop(self, None, prop_name, value)
353
Simon Glass116adec2018-07-06 10:27:38 -0600354 def SetInt(self, prop_name, val):
355 """Update an integer property int the device tree.
356
357 This is not allowed to change the size of the FDT.
358
Simon Glass64349612018-09-14 04:57:16 -0600359 The device tree is marked dirty so that the value will be written to
360 the blob on the next sync.
361
Simon Glass116adec2018-07-06 10:27:38 -0600362 Args:
363 prop_name: Name of property
364 val: Value to set
365 """
Simon Glassfa80c252018-09-14 04:57:13 -0600366 self.props[prop_name].SetInt(val)
367
Simon Glass64349612018-09-14 04:57:16 -0600368 def SetData(self, prop_name, val):
369 """Set the data value of a property
370
371 The device tree is marked dirty so that the value will be written to
372 the blob on the next sync.
373
374 Args:
375 prop_name: Name of property to set
376 val: Data value to set
377 """
378 self.props[prop_name].SetData(val)
379
380 def SetString(self, prop_name, val):
381 """Set the string value of a property
382
383 The device tree is marked dirty so that the value will be written to
384 the blob on the next sync.
385
386 Args:
387 prop_name: Name of property to set
388 val: String value to set (will be \0-terminated in DT)
389 """
390 self.props[prop_name].SetData(val + chr(0))
391
392 def AddString(self, prop_name, val):
393 """Add a new string property to a node
394
395 The device tree is marked dirty so that the value will be written to
396 the blob on the next sync.
397
398 Args:
399 prop_name: Name of property to add
400 val: String value of property
401 """
402 self.props[prop_name] = Prop(self, None, prop_name, val + chr(0))
403
Simon Glasse21c27a2018-09-14 04:57:15 -0600404 def AddSubnode(self, name):
Simon Glass64349612018-09-14 04:57:16 -0600405 """Add a new subnode to the node
406
407 Args:
408 name: name of node to add
409
410 Returns:
411 New subnode that was created
412 """
Simon Glasse21c27a2018-09-14 04:57:15 -0600413 path = self.path + '/' + name
414 subnode = Node(self._fdt, self, None, name, path)
415 self.subnodes.append(subnode)
416 return subnode
417
Simon Glassfa80c252018-09-14 04:57:13 -0600418 def Sync(self, auto_resize=False):
419 """Sync node changes back to the device tree
420
421 This updates the device tree blob with any changes to this node and its
422 subnodes since the last sync.
423
424 Args:
425 auto_resize: Resize the device tree automatically if it does not
426 have enough space for the update
427
428 Raises:
429 FdtException if auto_resize is False and there is not enough space
430 """
Simon Glasse21c27a2018-09-14 04:57:15 -0600431 if self._offset is None:
432 # The subnode doesn't exist yet, so add it
433 fdt_obj = self._fdt._fdt_obj
434 if auto_resize:
435 while True:
436 offset = fdt_obj.add_subnode(self.parent._offset, self.name,
437 (libfdt.NOSPACE,))
438 if offset != -libfdt.NOSPACE:
439 break
440 fdt_obj.resize(fdt_obj.totalsize() + 1024)
441 else:
442 offset = fdt_obj.add_subnode(self.parent._offset, self.name)
443 self._offset = offset
444
Simon Glassfa80c252018-09-14 04:57:13 -0600445 # Sync subnodes in reverse so that we don't disturb node offsets for
446 # nodes that are earlier in the DT. This avoids an O(n^2) rescan of
447 # node offsets.
448 for node in reversed(self.subnodes):
449 node.Sync(auto_resize)
450
451 # Sync properties now, whose offsets should not have been disturbed.
452 # We do this after subnodes, since this disturbs the offsets of these
453 # properties.
454 prop_list = sorted(self.props.values(), key=lambda prop: prop._offset,
455 reverse=True)
456 for prop in prop_list:
457 prop.Sync(auto_resize)
Simon Glass116adec2018-07-06 10:27:38 -0600458
459
Simon Glassa06a34b2016-07-25 18:59:04 -0600460class Fdt:
Simon Glass7b75b442017-05-27 07:38:28 -0600461 """Provides simple access to a flat device tree blob using libfdts.
Simon Glassa06a34b2016-07-25 18:59:04 -0600462
463 Properties:
464 fname: Filename of fdt
465 _root: Root of device tree (a Node object)
466 """
467 def __init__(self, fname):
468 self._fname = fname
Simon Glass7b75b442017-05-27 07:38:28 -0600469 self._cached_offsets = False
Simon Glass09264e02017-08-29 14:15:52 -0600470 self.phandle_to_node = {}
Simon Glass7b75b442017-05-27 07:38:28 -0600471 if self._fname:
472 self._fname = fdt_util.EnsureCompiled(self._fname)
473
Simon Glass3e4b51e2019-05-14 15:53:43 -0600474 with open(self._fname, 'rb') as fd:
Simon Glass96066242018-07-06 10:27:27 -0600475 self._fdt_obj = libfdt.Fdt(fd.read())
Simon Glassf7a2aee2016-07-25 18:59:07 -0600476
Simon Glass746aee32018-09-14 04:57:17 -0600477 @staticmethod
478 def FromData(data):
479 """Create a new Fdt object from the given data
480
481 Args:
482 data: Device-tree data blob
483
484 Returns:
485 Fdt object containing the data
486 """
487 fdt = Fdt(None)
488 fdt._fdt_obj = libfdt.Fdt(bytearray(data))
489 return fdt
490
Simon Glass94a7c602018-07-17 13:25:46 -0600491 def LookupPhandle(self, phandle):
492 """Look up a phandle
493
494 Args:
495 phandle: Phandle to look up (int)
496
497 Returns:
498 Node object the phandle points to
499 """
500 return self.phandle_to_node.get(phandle)
501
Simon Glassf7a2aee2016-07-25 18:59:07 -0600502 def Scan(self, root='/'):
503 """Scan a device tree, building up a tree of Node objects
504
505 This fills in the self._root property
506
507 Args:
508 root: Ignored
509
510 TODO(sjg@chromium.org): Implement the 'root' parameter
511 """
Simon Glassf9b88b32018-07-06 10:27:29 -0600512 self._cached_offsets = True
Simon Glass979ab022017-08-29 14:15:47 -0600513 self._root = self.Node(self, None, 0, '/', '/')
Simon Glassf7a2aee2016-07-25 18:59:07 -0600514 self._root.Scan()
515
516 def GetRoot(self):
517 """Get the root Node of the device tree
518
519 Returns:
520 The root Node object
521 """
522 return self._root
523
524 def GetNode(self, path):
525 """Look up a node from its path
526
527 Args:
528 path: Path to look up, e.g. '/microcode/update@0'
529 Returns:
530 Node object, or None if not found
531 """
532 node = self._root
Simon Glassb9066ff2018-07-06 10:27:30 -0600533 parts = path.split('/')
534 if len(parts) < 2:
535 return None
536 for part in parts[1:]:
Simon Glass1d858882018-07-17 13:25:41 -0600537 node = node.FindNode(part)
Simon Glassf7a2aee2016-07-25 18:59:07 -0600538 if not node:
539 return None
540 return node
541
Simon Glassda5f7492016-07-25 18:59:15 -0600542 def Flush(self):
543 """Flush device tree changes back to the file
544
545 If the device tree has changed in memory, write it back to the file.
Simon Glassda5f7492016-07-25 18:59:15 -0600546 """
Simon Glass7b75b442017-05-27 07:38:28 -0600547 with open(self._fname, 'wb') as fd:
Simon Glass96066242018-07-06 10:27:27 -0600548 fd.write(self._fdt_obj.as_bytearray())
Simon Glassda5f7492016-07-25 18:59:15 -0600549
Simon Glassfa80c252018-09-14 04:57:13 -0600550 def Sync(self, auto_resize=False):
551 """Make sure any DT changes are written to the blob
552
553 Args:
554 auto_resize: Resize the device tree automatically if it does not
555 have enough space for the update
556
557 Raises:
558 FdtException if auto_resize is False and there is not enough space
559 """
560 self._root.Sync(auto_resize)
561 self.Invalidate()
562
Simon Glassda5f7492016-07-25 18:59:15 -0600563 def Pack(self):
564 """Pack the device tree down to its minimum size
565
566 When nodes and properties shrink or are deleted, wasted space can
Simon Glass7b75b442017-05-27 07:38:28 -0600567 build up in the device tree binary.
Simon Glassda5f7492016-07-25 18:59:15 -0600568 """
Simon Glass117f57b2018-07-06 10:27:26 -0600569 CheckErr(self._fdt_obj.pack(), 'pack')
570 self.Invalidate()
Simon Glass7b75b442017-05-27 07:38:28 -0600571
Simon Glass96066242018-07-06 10:27:27 -0600572 def GetContents(self):
Simon Glass7b75b442017-05-27 07:38:28 -0600573 """Get the contents of the FDT
574
575 Returns:
576 The FDT contents as a string of bytes
577 """
Simon Glass96066242018-07-06 10:27:27 -0600578 return self._fdt_obj.as_bytearray()
Simon Glass7b75b442017-05-27 07:38:28 -0600579
Simon Glass2ba98752018-07-06 10:27:24 -0600580 def GetFdtObj(self):
581 """Get the contents of the FDT
582
583 Returns:
584 The FDT contents as a libfdt.Fdt object
585 """
586 return self._fdt_obj
587
Simon Glass7b75b442017-05-27 07:38:28 -0600588 def GetProps(self, node):
589 """Get all properties from a node.
590
591 Args:
592 node: Full path to node name to look in.
593
594 Returns:
595 A dictionary containing all the properties, indexed by node name.
596 The entries are Prop objects.
597
598 Raises:
599 ValueError: if the node does not exist.
600 """
601 props_dict = {}
Simon Glass117f57b2018-07-06 10:27:26 -0600602 poffset = self._fdt_obj.first_property_offset(node._offset,
603 QUIET_NOTFOUND)
Simon Glass7b75b442017-05-27 07:38:28 -0600604 while poffset >= 0:
605 p = self._fdt_obj.get_property_by_offset(poffset)
Simon Glass3def0cf2018-07-06 10:27:20 -0600606 prop = Prop(node, poffset, p.name, p)
Simon Glass7b75b442017-05-27 07:38:28 -0600607 props_dict[prop.name] = prop
608
Simon Glass117f57b2018-07-06 10:27:26 -0600609 poffset = self._fdt_obj.next_property_offset(poffset,
610 QUIET_NOTFOUND)
Simon Glass7b75b442017-05-27 07:38:28 -0600611 return props_dict
612
613 def Invalidate(self):
614 """Mark our offset cache as invalid"""
615 self._cached_offsets = False
616
617 def CheckCache(self):
618 """Refresh the offset cache if needed"""
619 if self._cached_offsets:
620 return
621 self.Refresh()
622 self._cached_offsets = True
623
624 def Refresh(self):
625 """Refresh the offset cache"""
626 self._root.Refresh(0)
627
628 def GetStructOffset(self, offset):
629 """Get the file offset of a given struct offset
630
631 Args:
632 offset: Offset within the 'struct' region of the device tree
633 Returns:
634 Position of @offset within the device tree binary
635 """
Simon Glass117f57b2018-07-06 10:27:26 -0600636 return self._fdt_obj.off_dt_struct() + offset
Simon Glass7b75b442017-05-27 07:38:28 -0600637
638 @classmethod
Simon Glass979ab022017-08-29 14:15:47 -0600639 def Node(self, fdt, parent, offset, name, path):
Simon Glass7b75b442017-05-27 07:38:28 -0600640 """Create a new node
641
642 This is used by Fdt.Scan() to create a new node using the correct
643 class.
644
645 Args:
646 fdt: Fdt object
Simon Glass979ab022017-08-29 14:15:47 -0600647 parent: Parent node, or None if this is the root node
Simon Glass7b75b442017-05-27 07:38:28 -0600648 offset: Offset of node
649 name: Node name
650 path: Full path to node
651 """
Simon Glass979ab022017-08-29 14:15:47 -0600652 node = Node(fdt, parent, offset, name, path)
Simon Glass7b75b442017-05-27 07:38:28 -0600653 return node
Simon Glass99ed4a22017-05-27 07:38:30 -0600654
655def FdtScan(fname):
Simon Glassdfe5f5b2018-07-06 10:27:32 -0600656 """Returns a new Fdt object"""
Simon Glass99ed4a22017-05-27 07:38:30 -0600657 dtb = Fdt(fname)
658 dtb.Scan()
659 return dtb