blob: c5054e8cc915e2ce1135764485d081c473f6e9fe [file] [log] [blame]
Simon Glass1f941b62016-07-25 18:59:04 -06001#!/usr/bin/python
Tom Rini10e47792018-05-06 17:58:06 -04002# SPDX-License-Identifier: GPL-2.0+
Simon Glass1f941b62016-07-25 18:59:04 -06003#
4# Copyright (C) 2016 Google, Inc
5# Written by Simon Glass <sjg@chromium.org>
6#
Simon Glass1f941b62016-07-25 18:59:04 -06007
8import struct
9import sys
10
11import fdt_util
Simon Glass059ae522017-05-27 07:38:28 -060012import libfdt
Simon Glass151aeed2018-07-06 10:27:26 -060013from libfdt import QUIET_NOTFOUND
Simon Glass1f941b62016-07-25 18:59:04 -060014
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
Simon Glassa9440932017-05-27 07:38:30 -060017# 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.
Simon Glass059ae522017-05-27 07:38:28 -060019
20# This implementation uses a libfdt Python library to access the device tree,
21# so it is fairly efficient.
Simon Glass1f941b62016-07-25 18:59:04 -060022
Simon Glassb1a5e262016-07-25 18:59:05 -060023# A list of types we support
Simon Glassfc3ae9c2017-08-29 14:15:48 -060024(TYPE_BYTE, TYPE_INT, TYPE_STRING, TYPE_BOOL, TYPE_INT64) = range(5)
Simon Glassb1a5e262016-07-25 18:59:05 -060025
Simon Glass1f941b62016-07-25 18:59:04 -060026def CheckErr(errnum, msg):
27 if errnum:
28 raise ValueError('Error %d: %s: %s' %
29 (errnum, libfdt.fdt_strerror(errnum), msg))
30
Simon Glass059ae522017-05-27 07:38:28 -060031class Prop:
Simon Glass1f941b62016-07-25 18:59:04 -060032 """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 """
Simon Glass059ae522017-05-27 07:38:28 -060040 def __init__(self, node, offset, name, bytes):
Simon Glass1f941b62016-07-25 18:59:04 -060041 self._node = node
42 self._offset = offset
43 self.name = name
44 self.value = None
Simon Glass059ae522017-05-27 07:38:28 -060045 self.bytes = str(bytes)
Simon Glasseddd7292018-09-14 04:57:13 -060046 self.dirty = False
Simon Glass059ae522017-05-27 07:38:28 -060047 if not bytes:
48 self.type = TYPE_BOOL
49 self.value = True
50 return
51 self.type, self.value = self.BytesToValue(bytes)
Simon Glass1f941b62016-07-25 18:59:04 -060052
Simon Glass4df8a0c2018-07-06 10:27:29 -060053 def RefreshOffset(self, poffset):
54 self._offset = poffset
55
Simon Glass248ccd22016-07-25 18:59:06 -060056 def Widen(self, newprop):
57 """Figure out which property type is more general
58
59 Given a current property and a new property, this function returns the
60 one that is less specific as to type. The less specific property will
61 be ble to represent the data in the more specific property. This is
62 used for things like:
63
64 node1 {
65 compatible = "fred";
66 value = <1>;
67 };
68 node1 {
69 compatible = "fred";
70 value = <1 2>;
71 };
72
73 He we want to use an int array for 'value'. The first property
74 suggests that a single int is enough, but the second one shows that
75 it is not. Calling this function with these two propertes would
76 update the current property to be like the second, since it is less
77 specific.
78 """
79 if newprop.type < self.type:
80 self.type = newprop.type
81
82 if type(newprop.value) == list and type(self.value) != list:
83 self.value = [self.value]
84
85 if type(self.value) == list and len(newprop.value) > len(self.value):
86 val = self.GetEmpty(self.type)
87 while len(self.value) < len(newprop.value):
88 self.value.append(val)
89
Simon Glassb1a5e262016-07-25 18:59:05 -060090 def BytesToValue(self, bytes):
91 """Converts a string of bytes into a type and value
92
93 Args:
94 A string containing bytes
95
96 Return:
97 A tuple:
98 Type of data
99 Data, either a single element or a list of elements. Each element
100 is one of:
101 TYPE_STRING: string value from the property
102 TYPE_INT: a byte-swapped integer stored as a 4-byte string
103 TYPE_BYTE: a byte stored as a single-byte string
104 """
Simon Glass55901ff2017-05-27 07:38:22 -0600105 bytes = str(bytes)
Simon Glassb1a5e262016-07-25 18:59:05 -0600106 size = len(bytes)
107 strings = bytes.split('\0')
108 is_string = True
109 count = len(strings) - 1
110 if count > 0 and not strings[-1]:
111 for string in strings[:-1]:
112 if not string:
113 is_string = False
114 break
115 for ch in string:
116 if ch < ' ' or ch > '~':
117 is_string = False
118 break
119 else:
120 is_string = False
121 if is_string:
122 if count == 1:
123 return TYPE_STRING, strings[0]
124 else:
125 return TYPE_STRING, strings[:-1]
126 if size % 4:
127 if size == 1:
128 return TYPE_BYTE, bytes[0]
129 else:
130 return TYPE_BYTE, list(bytes)
131 val = []
132 for i in range(0, size, 4):
133 val.append(bytes[i:i + 4])
134 if size == 4:
135 return TYPE_INT, val[0]
136 else:
137 return TYPE_INT, val
138
Simon Glass0ed50752018-07-06 10:27:24 -0600139 @classmethod
Simon Glassb1a5e262016-07-25 18:59:05 -0600140 def GetEmpty(self, type):
141 """Get an empty / zero value of the given type
142
143 Returns:
144 A single value of the given type
145 """
146 if type == TYPE_BYTE:
147 return chr(0)
148 elif type == TYPE_INT:
Simon Glassc330b142018-09-14 04:57:14 -0600149 return struct.pack('>I', 0);
Simon Glassb1a5e262016-07-25 18:59:05 -0600150 elif type == TYPE_STRING:
151 return ''
152 else:
153 return True
154
Simon Glass54cec6d2016-07-25 18:59:16 -0600155 def GetOffset(self):
156 """Get the offset of a property
157
Simon Glass54cec6d2016-07-25 18:59:16 -0600158 Returns:
Simon Glass059ae522017-05-27 07:38:28 -0600159 The offset of the property (struct fdt_property) within the file
Simon Glass54cec6d2016-07-25 18:59:16 -0600160 """
Simon Glass4df8a0c2018-07-06 10:27:29 -0600161 self._node._fdt.CheckCache()
Simon Glass059ae522017-05-27 07:38:28 -0600162 return self._node._fdt.GetStructOffset(self._offset)
Simon Glass54cec6d2016-07-25 18:59:16 -0600163
Simon Glasseddd7292018-09-14 04:57:13 -0600164 def SetInt(self, val):
165 """Set the integer value of the property
166
167 The device tree is marked dirty so that the value will be written to
168 the block on the next sync.
169
170 Args:
171 val: Integer value (32-bit, single cell)
172 """
173 self.bytes = struct.pack('>I', val);
174 self.value = val
175 self.type = TYPE_INT
176 self.dirty = True
177
Simon Glassccd25262018-09-14 04:57:16 -0600178 def SetData(self, bytes):
179 """Set the value of a property as bytes
180
181 Args:
182 bytes: New property value to set
183 """
184 self.bytes = str(bytes)
185 self.type, self.value = self.BytesToValue(bytes)
186 self.dirty = True
187
Simon Glasseddd7292018-09-14 04:57:13 -0600188 def Sync(self, auto_resize=False):
189 """Sync property changes back to the device tree
190
191 This updates the device tree blob with any changes to this property
192 since the last sync.
193
194 Args:
195 auto_resize: Resize the device tree automatically if it does not
196 have enough space for the update
197
198 Raises:
199 FdtException if auto_resize is False and there is not enough space
200 """
201 if self._offset is None or self.dirty:
202 node = self._node
203 fdt_obj = node._fdt._fdt_obj
204 if auto_resize:
205 while fdt_obj.setprop(node.Offset(), self.name, self.bytes,
206 (libfdt.NOSPACE,)) == -libfdt.NOSPACE:
207 fdt_obj.resize(fdt_obj.totalsize() + 1024)
208 fdt_obj.setprop(node.Offset(), self.name, self.bytes)
209 else:
210 fdt_obj.setprop(node.Offset(), self.name, self.bytes)
211
212
Simon Glass059ae522017-05-27 07:38:28 -0600213class Node:
Simon Glass1f941b62016-07-25 18:59:04 -0600214 """A device tree node
215
216 Properties:
217 offset: Integer offset in the device tree
218 name: Device tree node tname
219 path: Full path to node, along with the node name itself
220 _fdt: Device tree object
221 subnodes: A list of subnodes for this node, each a Node object
222 props: A dict of properties for this node, each a Prop object.
223 Keyed by property name
224 """
Simon Glass80ef7052017-08-29 14:15:47 -0600225 def __init__(self, fdt, parent, offset, name, path):
Simon Glass1f941b62016-07-25 18:59:04 -0600226 self._fdt = fdt
Simon Glass80ef7052017-08-29 14:15:47 -0600227 self.parent = parent
Simon Glass1f941b62016-07-25 18:59:04 -0600228 self._offset = offset
229 self.name = name
230 self.path = path
231 self.subnodes = []
232 self.props = {}
233
Simon Glasse2d65282018-07-17 13:25:46 -0600234 def GetFdt(self):
235 """Get the Fdt object for this node
236
237 Returns:
238 Fdt object
239 """
240 return self._fdt
241
Simon Glassaa1a5d72018-07-17 13:25:41 -0600242 def FindNode(self, name):
Simon Glasscc346a72016-07-25 18:59:07 -0600243 """Find a node given its name
244
245 Args:
246 name: Node name to look for
247 Returns:
248 Node object if found, else None
249 """
250 for subnode in self.subnodes:
251 if subnode.name == name:
252 return subnode
253 return None
254
Simon Glass059ae522017-05-27 07:38:28 -0600255 def Offset(self):
256 """Returns the offset of a node, after checking the cache
257
258 This should be used instead of self._offset directly, to ensure that
259 the cache does not contain invalid offsets.
260 """
261 self._fdt.CheckCache()
262 return self._offset
263
Simon Glasscc346a72016-07-25 18:59:07 -0600264 def Scan(self):
Simon Glass059ae522017-05-27 07:38:28 -0600265 """Scan a node's properties and subnodes
Simon Glasscc346a72016-07-25 18:59:07 -0600266
Simon Glass059ae522017-05-27 07:38:28 -0600267 This fills in the props and subnodes properties, recursively
268 searching into subnodes so that the entire tree is built.
Simon Glasscc346a72016-07-25 18:59:07 -0600269 """
Simon Glass151aeed2018-07-06 10:27:26 -0600270 fdt_obj = self._fdt._fdt_obj
Simon Glass059ae522017-05-27 07:38:28 -0600271 self.props = self._fdt.GetProps(self)
Simon Glass151aeed2018-07-06 10:27:26 -0600272 phandle = fdt_obj.get_phandle(self.Offset())
Simon Glassa3f94442017-08-29 14:15:52 -0600273 if phandle:
Simon Glass151aeed2018-07-06 10:27:26 -0600274 self._fdt.phandle_to_node[phandle] = self
Simon Glass059ae522017-05-27 07:38:28 -0600275
Simon Glass151aeed2018-07-06 10:27:26 -0600276 offset = fdt_obj.first_subnode(self.Offset(), QUIET_NOTFOUND)
Simon Glass059ae522017-05-27 07:38:28 -0600277 while offset >= 0:
278 sep = '' if self.path[-1] == '/' else '/'
Simon Glass151aeed2018-07-06 10:27:26 -0600279 name = fdt_obj.get_name(offset)
Simon Glass059ae522017-05-27 07:38:28 -0600280 path = self.path + sep + name
Simon Glass80ef7052017-08-29 14:15:47 -0600281 node = Node(self._fdt, self, offset, name, path)
Simon Glass059ae522017-05-27 07:38:28 -0600282 self.subnodes.append(node)
283
284 node.Scan()
Simon Glass151aeed2018-07-06 10:27:26 -0600285 offset = fdt_obj.next_subnode(offset, QUIET_NOTFOUND)
Simon Glass059ae522017-05-27 07:38:28 -0600286
287 def Refresh(self, my_offset):
288 """Fix up the _offset for each node, recursively
289
290 Note: This does not take account of property offsets - these will not
291 be updated.
292 """
Simon Glass792d2392018-07-06 10:27:27 -0600293 fdt_obj = self._fdt._fdt_obj
Simon Glass059ae522017-05-27 07:38:28 -0600294 if self._offset != my_offset:
Simon Glass059ae522017-05-27 07:38:28 -0600295 self._offset = my_offset
Simon Glass792d2392018-07-06 10:27:27 -0600296 offset = fdt_obj.first_subnode(self._offset, QUIET_NOTFOUND)
Simon Glass059ae522017-05-27 07:38:28 -0600297 for subnode in self.subnodes:
Simon Glass4df8a0c2018-07-06 10:27:29 -0600298 if subnode.name != fdt_obj.get_name(offset):
299 raise ValueError('Internal error, node name mismatch %s != %s' %
300 (subnode.name, fdt_obj.get_name(offset)))
Simon Glass059ae522017-05-27 07:38:28 -0600301 subnode.Refresh(offset)
Simon Glass792d2392018-07-06 10:27:27 -0600302 offset = fdt_obj.next_subnode(offset, QUIET_NOTFOUND)
Simon Glass4df8a0c2018-07-06 10:27:29 -0600303 if offset != -libfdt.FDT_ERR_NOTFOUND:
304 raise ValueError('Internal error, offset == %d' % offset)
305
306 poffset = fdt_obj.first_property_offset(self._offset, QUIET_NOTFOUND)
307 while poffset >= 0:
308 p = fdt_obj.get_property_by_offset(poffset)
309 prop = self.props.get(p.name)
310 if not prop:
311 raise ValueError("Internal error, property '%s' missing, "
312 'offset %d' % (p.name, poffset))
313 prop.RefreshOffset(poffset)
314 poffset = fdt_obj.next_property_offset(poffset, QUIET_NOTFOUND)
Simon Glasscc346a72016-07-25 18:59:07 -0600315
Simon Glassc719e422016-07-25 18:59:14 -0600316 def DeleteProp(self, prop_name):
317 """Delete a property of a node
318
Simon Glass059ae522017-05-27 07:38:28 -0600319 The property is deleted and the offset cache is invalidated.
Simon Glassc719e422016-07-25 18:59:14 -0600320
321 Args:
322 prop_name: Name of the property to delete
Simon Glass059ae522017-05-27 07:38:28 -0600323 Raises:
324 ValueError if the property does not exist
Simon Glassc719e422016-07-25 18:59:14 -0600325 """
Simon Glass792d2392018-07-06 10:27:27 -0600326 CheckErr(self._fdt._fdt_obj.delprop(self.Offset(), prop_name),
Simon Glass059ae522017-05-27 07:38:28 -0600327 "Node '%s': delete property: '%s'" % (self.path, prop_name))
328 del self.props[prop_name]
329 self._fdt.Invalidate()
Simon Glassc719e422016-07-25 18:59:14 -0600330
Simon Glasse80c5562018-07-06 10:27:38 -0600331 def AddZeroProp(self, prop_name):
332 """Add a new property to the device tree with an integer value of 0.
333
334 Args:
335 prop_name: Name of property
336 """
Simon Glasseddd7292018-09-14 04:57:13 -0600337 self.props[prop_name] = Prop(self, None, prop_name, '\0' * 4)
Simon Glasse80c5562018-07-06 10:27:38 -0600338
Simon Glassccd25262018-09-14 04:57:16 -0600339 def AddEmptyProp(self, prop_name, len):
340 """Add a property with a fixed data size, for filling in later
341
342 The device tree is marked dirty so that the value will be written to
343 the blob on the next sync.
344
345 Args:
346 prop_name: Name of property
347 len: Length of data in property
348 """
349 value = chr(0) * len
350 self.props[prop_name] = Prop(self, None, prop_name, value)
351
Simon Glasse80c5562018-07-06 10:27:38 -0600352 def SetInt(self, prop_name, val):
353 """Update an integer property int the device tree.
354
355 This is not allowed to change the size of the FDT.
356
Simon Glassccd25262018-09-14 04:57:16 -0600357 The device tree is marked dirty so that the value will be written to
358 the blob on the next sync.
359
Simon Glasse80c5562018-07-06 10:27:38 -0600360 Args:
361 prop_name: Name of property
362 val: Value to set
363 """
Simon Glasseddd7292018-09-14 04:57:13 -0600364 self.props[prop_name].SetInt(val)
365
Simon Glassccd25262018-09-14 04:57:16 -0600366 def SetData(self, prop_name, val):
367 """Set the data value of a property
368
369 The device tree is marked dirty so that the value will be written to
370 the blob on the next sync.
371
372 Args:
373 prop_name: Name of property to set
374 val: Data value to set
375 """
376 self.props[prop_name].SetData(val)
377
378 def SetString(self, prop_name, val):
379 """Set the string value of a property
380
381 The device tree is marked dirty so that the value will be written to
382 the blob on the next sync.
383
384 Args:
385 prop_name: Name of property to set
386 val: String value to set (will be \0-terminated in DT)
387 """
388 self.props[prop_name].SetData(val + chr(0))
389
390 def AddString(self, prop_name, val):
391 """Add a new string property to a node
392
393 The device tree is marked dirty so that the value will be written to
394 the blob on the next sync.
395
396 Args:
397 prop_name: Name of property to add
398 val: String value of property
399 """
400 self.props[prop_name] = Prop(self, None, prop_name, val + chr(0))
401
Simon Glassf3a17962018-09-14 04:57:15 -0600402 def AddSubnode(self, name):
Simon Glassccd25262018-09-14 04:57:16 -0600403 """Add a new subnode to the node
404
405 Args:
406 name: name of node to add
407
408 Returns:
409 New subnode that was created
410 """
Simon Glassf3a17962018-09-14 04:57:15 -0600411 path = self.path + '/' + name
412 subnode = Node(self._fdt, self, None, name, path)
413 self.subnodes.append(subnode)
414 return subnode
415
Simon Glasseddd7292018-09-14 04:57:13 -0600416 def Sync(self, auto_resize=False):
417 """Sync node changes back to the device tree
418
419 This updates the device tree blob with any changes to this node and its
420 subnodes since the last sync.
421
422 Args:
423 auto_resize: Resize the device tree automatically if it does not
424 have enough space for the update
425
426 Raises:
427 FdtException if auto_resize is False and there is not enough space
428 """
Simon Glassf3a17962018-09-14 04:57:15 -0600429 if self._offset is None:
430 # The subnode doesn't exist yet, so add it
431 fdt_obj = self._fdt._fdt_obj
432 if auto_resize:
433 while True:
434 offset = fdt_obj.add_subnode(self.parent._offset, self.name,
435 (libfdt.NOSPACE,))
436 if offset != -libfdt.NOSPACE:
437 break
438 fdt_obj.resize(fdt_obj.totalsize() + 1024)
439 else:
440 offset = fdt_obj.add_subnode(self.parent._offset, self.name)
441 self._offset = offset
442
Simon Glasseddd7292018-09-14 04:57:13 -0600443 # Sync subnodes in reverse so that we don't disturb node offsets for
444 # nodes that are earlier in the DT. This avoids an O(n^2) rescan of
445 # node offsets.
446 for node in reversed(self.subnodes):
447 node.Sync(auto_resize)
Simon Glasse80c5562018-07-06 10:27:38 -0600448
Simon Glasseddd7292018-09-14 04:57:13 -0600449 # Sync properties now, whose offsets should not have been disturbed.
450 # We do this after subnodes, since this disturbs the offsets of these
451 # properties.
452 prop_list = sorted(self.props.values(), key=lambda prop: prop._offset,
453 reverse=True)
454 for prop in prop_list:
455 prop.Sync(auto_resize)
456
Simon Glasse80c5562018-07-06 10:27:38 -0600457
Simon Glass1f941b62016-07-25 18:59:04 -0600458class Fdt:
Simon Glass059ae522017-05-27 07:38:28 -0600459 """Provides simple access to a flat device tree blob using libfdts.
Simon Glass1f941b62016-07-25 18:59:04 -0600460
461 Properties:
462 fname: Filename of fdt
463 _root: Root of device tree (a Node object)
464 """
465 def __init__(self, fname):
466 self._fname = fname
Simon Glass059ae522017-05-27 07:38:28 -0600467 self._cached_offsets = False
Simon Glassa3f94442017-08-29 14:15:52 -0600468 self.phandle_to_node = {}
Simon Glass059ae522017-05-27 07:38:28 -0600469 if self._fname:
470 self._fname = fdt_util.EnsureCompiled(self._fname)
471
472 with open(self._fname) as fd:
Simon Glass792d2392018-07-06 10:27:27 -0600473 self._fdt_obj = libfdt.Fdt(fd.read())
Simon Glasscc346a72016-07-25 18:59:07 -0600474
Simon Glasse2d65282018-07-17 13:25:46 -0600475 def LookupPhandle(self, phandle):
476 """Look up a phandle
477
478 Args:
479 phandle: Phandle to look up (int)
480
481 Returns:
482 Node object the phandle points to
483 """
484 return self.phandle_to_node.get(phandle)
485
Simon Glasscc346a72016-07-25 18:59:07 -0600486 def Scan(self, root='/'):
487 """Scan a device tree, building up a tree of Node objects
488
489 This fills in the self._root property
490
491 Args:
492 root: Ignored
493
494 TODO(sjg@chromium.org): Implement the 'root' parameter
495 """
Simon Glass4df8a0c2018-07-06 10:27:29 -0600496 self._cached_offsets = True
Simon Glass80ef7052017-08-29 14:15:47 -0600497 self._root = self.Node(self, None, 0, '/', '/')
Simon Glasscc346a72016-07-25 18:59:07 -0600498 self._root.Scan()
499
500 def GetRoot(self):
501 """Get the root Node of the device tree
502
503 Returns:
504 The root Node object
505 """
506 return self._root
507
508 def GetNode(self, path):
509 """Look up a node from its path
510
511 Args:
512 path: Path to look up, e.g. '/microcode/update@0'
513 Returns:
514 Node object, or None if not found
515 """
516 node = self._root
Simon Glassc5eddc82018-07-06 10:27:30 -0600517 parts = path.split('/')
518 if len(parts) < 2:
519 return None
520 for part in parts[1:]:
Simon Glassaa1a5d72018-07-17 13:25:41 -0600521 node = node.FindNode(part)
Simon Glasscc346a72016-07-25 18:59:07 -0600522 if not node:
523 return None
524 return node
525
Simon Glass32d98272016-07-25 18:59:15 -0600526 def Flush(self):
527 """Flush device tree changes back to the file
528
529 If the device tree has changed in memory, write it back to the file.
Simon Glass32d98272016-07-25 18:59:15 -0600530 """
Simon Glass059ae522017-05-27 07:38:28 -0600531 with open(self._fname, 'wb') as fd:
Simon Glass792d2392018-07-06 10:27:27 -0600532 fd.write(self._fdt_obj.as_bytearray())
Simon Glass32d98272016-07-25 18:59:15 -0600533
Simon Glasseddd7292018-09-14 04:57:13 -0600534 def Sync(self, auto_resize=False):
535 """Make sure any DT changes are written to the blob
536
537 Args:
538 auto_resize: Resize the device tree automatically if it does not
539 have enough space for the update
540
541 Raises:
542 FdtException if auto_resize is False and there is not enough space
543 """
544 self._root.Sync(auto_resize)
545 self.Invalidate()
546
Simon Glass32d98272016-07-25 18:59:15 -0600547 def Pack(self):
548 """Pack the device tree down to its minimum size
549
550 When nodes and properties shrink or are deleted, wasted space can
Simon Glass059ae522017-05-27 07:38:28 -0600551 build up in the device tree binary.
552 """
Simon Glass151aeed2018-07-06 10:27:26 -0600553 CheckErr(self._fdt_obj.pack(), 'pack')
554 self.Invalidate()
Simon Glass059ae522017-05-27 07:38:28 -0600555
Simon Glass792d2392018-07-06 10:27:27 -0600556 def GetContents(self):
Simon Glass059ae522017-05-27 07:38:28 -0600557 """Get the contents of the FDT
558
559 Returns:
560 The FDT contents as a string of bytes
Simon Glass32d98272016-07-25 18:59:15 -0600561 """
Simon Glass792d2392018-07-06 10:27:27 -0600562 return self._fdt_obj.as_bytearray()
Simon Glass059ae522017-05-27 07:38:28 -0600563
Simon Glass0ed50752018-07-06 10:27:24 -0600564 def GetFdtObj(self):
565 """Get the contents of the FDT
566
567 Returns:
568 The FDT contents as a libfdt.Fdt object
569 """
570 return self._fdt_obj
571
Simon Glass059ae522017-05-27 07:38:28 -0600572 def GetProps(self, node):
573 """Get all properties from a node.
574
575 Args:
576 node: Full path to node name to look in.
577
578 Returns:
579 A dictionary containing all the properties, indexed by node name.
580 The entries are Prop objects.
581
582 Raises:
583 ValueError: if the node does not exist.
584 """
585 props_dict = {}
Simon Glass151aeed2018-07-06 10:27:26 -0600586 poffset = self._fdt_obj.first_property_offset(node._offset,
587 QUIET_NOTFOUND)
Simon Glass059ae522017-05-27 07:38:28 -0600588 while poffset >= 0:
589 p = self._fdt_obj.get_property_by_offset(poffset)
Simon Glass70cd0d72018-07-06 10:27:20 -0600590 prop = Prop(node, poffset, p.name, p)
Simon Glass059ae522017-05-27 07:38:28 -0600591 props_dict[prop.name] = prop
592
Simon Glass151aeed2018-07-06 10:27:26 -0600593 poffset = self._fdt_obj.next_property_offset(poffset,
594 QUIET_NOTFOUND)
Simon Glass059ae522017-05-27 07:38:28 -0600595 return props_dict
596
597 def Invalidate(self):
598 """Mark our offset cache as invalid"""
599 self._cached_offsets = False
600
601 def CheckCache(self):
602 """Refresh the offset cache if needed"""
603 if self._cached_offsets:
604 return
605 self.Refresh()
606 self._cached_offsets = True
607
608 def Refresh(self):
609 """Refresh the offset cache"""
610 self._root.Refresh(0)
611
612 def GetStructOffset(self, offset):
613 """Get the file offset of a given struct offset
614
615 Args:
616 offset: Offset within the 'struct' region of the device tree
617 Returns:
618 Position of @offset within the device tree binary
619 """
Simon Glass151aeed2018-07-06 10:27:26 -0600620 return self._fdt_obj.off_dt_struct() + offset
Simon Glass059ae522017-05-27 07:38:28 -0600621
622 @classmethod
Simon Glass80ef7052017-08-29 14:15:47 -0600623 def Node(self, fdt, parent, offset, name, path):
Simon Glass059ae522017-05-27 07:38:28 -0600624 """Create a new node
625
626 This is used by Fdt.Scan() to create a new node using the correct
627 class.
628
629 Args:
630 fdt: Fdt object
Simon Glass80ef7052017-08-29 14:15:47 -0600631 parent: Parent node, or None if this is the root node
Simon Glass059ae522017-05-27 07:38:28 -0600632 offset: Offset of node
633 name: Node name
634 path: Full path to node
635 """
Simon Glass80ef7052017-08-29 14:15:47 -0600636 node = Node(fdt, parent, offset, name, path)
Simon Glass059ae522017-05-27 07:38:28 -0600637 return node
Simon Glassa9440932017-05-27 07:38:30 -0600638
639def FdtScan(fname):
Simon Glassc38fee042018-07-06 10:27:32 -0600640 """Returns a new Fdt object"""
Simon Glassa9440932017-05-27 07:38:30 -0600641 dtb = Fdt(fname)
642 dtb.Scan()
643 return dtb