blob: 35453fbed9af55c4027cb87da24709d2daf394a1 [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 Glass6bbfafb2019-05-17 22:00:33 -060014import tools
Simon Glass1f941b62016-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 Glassa9440932017-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 Glass059ae522017-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 Glass1f941b62016-07-25 18:59:04 -060023
Simon Glassb1a5e262016-07-25 18:59:05 -060024# A list of types we support
Simon Glassfc3ae9c2017-08-29 14:15:48 -060025(TYPE_BYTE, TYPE_INT, TYPE_STRING, TYPE_BOOL, TYPE_INT64) = range(5)
Simon Glassb1a5e262016-07-25 18:59:05 -060026
Simon Glass1f941b62016-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 Glassb73d4482019-05-17 22:00:34 -060032
33def BytesToValue(bytes):
34 """Converts a string of bytes into a type and value
35
36 Args:
37 A string containing bytes
38
39 Return:
40 A tuple:
41 Type of data
42 Data, either a single element or a list of elements. Each element
43 is one of:
44 TYPE_STRING: string value from the property
45 TYPE_INT: a byte-swapped integer stored as a 4-byte string
46 TYPE_BYTE: a byte stored as a single-byte string
47 """
48 bytes = str(bytes)
49 size = len(bytes)
50 strings = bytes.split('\0')
51 is_string = True
52 count = len(strings) - 1
53 if count > 0 and not strings[-1]:
54 for string in strings[:-1]:
55 if not string:
56 is_string = False
57 break
58 for ch in string:
59 if ch < ' ' or ch > '~':
60 is_string = False
61 break
62 else:
63 is_string = False
64 if is_string:
65 if count == 1:
66 return TYPE_STRING, strings[0]
67 else:
68 return TYPE_STRING, strings[:-1]
69 if size % 4:
70 if size == 1:
71 return TYPE_BYTE, bytes[0]
72 else:
73 return TYPE_BYTE, list(bytes)
74 val = []
75 for i in range(0, size, 4):
76 val.append(bytes[i:i + 4])
77 if size == 4:
78 return TYPE_INT, val[0]
79 else:
80 return TYPE_INT, val
81
82
Simon Glass059ae522017-05-27 07:38:28 -060083class Prop:
Simon Glass1f941b62016-07-25 18:59:04 -060084 """A device tree property
85
86 Properties:
87 name: Property name (as per the device tree)
88 value: Property value as a string of bytes, or a list of strings of
89 bytes
90 type: Value type
91 """
Simon Glass059ae522017-05-27 07:38:28 -060092 def __init__(self, node, offset, name, bytes):
Simon Glass1f941b62016-07-25 18:59:04 -060093 self._node = node
94 self._offset = offset
95 self.name = name
96 self.value = None
Simon Glass059ae522017-05-27 07:38:28 -060097 self.bytes = str(bytes)
Simon Glasseddd7292018-09-14 04:57:13 -060098 self.dirty = False
Simon Glass059ae522017-05-27 07:38:28 -060099 if not bytes:
100 self.type = TYPE_BOOL
101 self.value = True
102 return
Simon Glassb73d4482019-05-17 22:00:34 -0600103 self.type, self.value = BytesToValue(bytes)
Simon Glass1f941b62016-07-25 18:59:04 -0600104
Simon Glass4df8a0c2018-07-06 10:27:29 -0600105 def RefreshOffset(self, poffset):
106 self._offset = poffset
107
Simon Glass248ccd22016-07-25 18:59:06 -0600108 def Widen(self, newprop):
109 """Figure out which property type is more general
110
111 Given a current property and a new property, this function returns the
112 one that is less specific as to type. The less specific property will
113 be ble to represent the data in the more specific property. This is
114 used for things like:
115
116 node1 {
117 compatible = "fred";
118 value = <1>;
119 };
120 node1 {
121 compatible = "fred";
122 value = <1 2>;
123 };
124
125 He we want to use an int array for 'value'. The first property
126 suggests that a single int is enough, but the second one shows that
127 it is not. Calling this function with these two propertes would
128 update the current property to be like the second, since it is less
129 specific.
130 """
131 if newprop.type < self.type:
132 self.type = newprop.type
133
134 if type(newprop.value) == list and type(self.value) != list:
135 self.value = [self.value]
136
137 if type(self.value) == list and len(newprop.value) > len(self.value):
138 val = self.GetEmpty(self.type)
139 while len(self.value) < len(newprop.value):
140 self.value.append(val)
141
Simon Glass0ed50752018-07-06 10:27:24 -0600142 @classmethod
Simon Glassb1a5e262016-07-25 18:59:05 -0600143 def GetEmpty(self, type):
144 """Get an empty / zero value of the given type
145
146 Returns:
147 A single value of the given type
148 """
149 if type == TYPE_BYTE:
150 return chr(0)
151 elif type == TYPE_INT:
Simon Glassc330b142018-09-14 04:57:14 -0600152 return struct.pack('>I', 0);
Simon Glassb1a5e262016-07-25 18:59:05 -0600153 elif type == TYPE_STRING:
154 return ''
155 else:
156 return True
157
Simon Glass54cec6d2016-07-25 18:59:16 -0600158 def GetOffset(self):
159 """Get the offset of a property
160
Simon Glass54cec6d2016-07-25 18:59:16 -0600161 Returns:
Simon Glass059ae522017-05-27 07:38:28 -0600162 The offset of the property (struct fdt_property) within the file
Simon Glass54cec6d2016-07-25 18:59:16 -0600163 """
Simon Glass4df8a0c2018-07-06 10:27:29 -0600164 self._node._fdt.CheckCache()
Simon Glass059ae522017-05-27 07:38:28 -0600165 return self._node._fdt.GetStructOffset(self._offset)
Simon Glass54cec6d2016-07-25 18:59:16 -0600166
Simon Glasseddd7292018-09-14 04:57:13 -0600167 def SetInt(self, val):
168 """Set the integer value of the property
169
170 The device tree is marked dirty so that the value will be written to
171 the block on the next sync.
172
173 Args:
174 val: Integer value (32-bit, single cell)
175 """
176 self.bytes = struct.pack('>I', val);
Simon Glassa2b9acb2018-10-01 12:22:49 -0600177 self.value = self.bytes
Simon Glasseddd7292018-09-14 04:57:13 -0600178 self.type = TYPE_INT
179 self.dirty = True
180
Simon Glassccd25262018-09-14 04:57:16 -0600181 def SetData(self, bytes):
182 """Set the value of a property as bytes
183
184 Args:
185 bytes: New property value to set
186 """
187 self.bytes = str(bytes)
Simon Glassb73d4482019-05-17 22:00:34 -0600188 self.type, self.value = BytesToValue(bytes)
Simon Glassccd25262018-09-14 04:57:16 -0600189 self.dirty = True
190
Simon Glasseddd7292018-09-14 04:57:13 -0600191 def Sync(self, auto_resize=False):
192 """Sync property changes back to the device tree
193
194 This updates the device tree blob with any changes to this property
195 since the last sync.
196
197 Args:
198 auto_resize: Resize the device tree automatically if it does not
199 have enough space for the update
200
201 Raises:
202 FdtException if auto_resize is False and there is not enough space
203 """
204 if self._offset is None or self.dirty:
205 node = self._node
206 fdt_obj = node._fdt._fdt_obj
207 if auto_resize:
208 while fdt_obj.setprop(node.Offset(), self.name, self.bytes,
209 (libfdt.NOSPACE,)) == -libfdt.NOSPACE:
210 fdt_obj.resize(fdt_obj.totalsize() + 1024)
211 fdt_obj.setprop(node.Offset(), self.name, self.bytes)
212 else:
213 fdt_obj.setprop(node.Offset(), self.name, self.bytes)
214
215
Simon Glass059ae522017-05-27 07:38:28 -0600216class Node:
Simon Glass1f941b62016-07-25 18:59:04 -0600217 """A device tree node
218
219 Properties:
220 offset: Integer offset in the device tree
221 name: Device tree node tname
222 path: Full path to node, along with the node name itself
223 _fdt: Device tree object
224 subnodes: A list of subnodes for this node, each a Node object
225 props: A dict of properties for this node, each a Prop object.
226 Keyed by property name
227 """
Simon Glass80ef7052017-08-29 14:15:47 -0600228 def __init__(self, fdt, parent, offset, name, path):
Simon Glass1f941b62016-07-25 18:59:04 -0600229 self._fdt = fdt
Simon Glass80ef7052017-08-29 14:15:47 -0600230 self.parent = parent
Simon Glass1f941b62016-07-25 18:59:04 -0600231 self._offset = offset
232 self.name = name
233 self.path = path
234 self.subnodes = []
235 self.props = {}
236
Simon Glasse2d65282018-07-17 13:25:46 -0600237 def GetFdt(self):
238 """Get the Fdt object for this node
239
240 Returns:
241 Fdt object
242 """
243 return self._fdt
244
Simon Glassaa1a5d72018-07-17 13:25:41 -0600245 def FindNode(self, name):
Simon Glasscc346a72016-07-25 18:59:07 -0600246 """Find a node given its name
247
248 Args:
249 name: Node name to look for
250 Returns:
251 Node object if found, else None
252 """
253 for subnode in self.subnodes:
254 if subnode.name == name:
255 return subnode
256 return None
257
Simon Glass059ae522017-05-27 07:38:28 -0600258 def Offset(self):
259 """Returns the offset of a node, after checking the cache
260
261 This should be used instead of self._offset directly, to ensure that
262 the cache does not contain invalid offsets.
263 """
264 self._fdt.CheckCache()
265 return self._offset
266
Simon Glasscc346a72016-07-25 18:59:07 -0600267 def Scan(self):
Simon Glass059ae522017-05-27 07:38:28 -0600268 """Scan a node's properties and subnodes
Simon Glasscc346a72016-07-25 18:59:07 -0600269
Simon Glass059ae522017-05-27 07:38:28 -0600270 This fills in the props and subnodes properties, recursively
271 searching into subnodes so that the entire tree is built.
Simon Glasscc346a72016-07-25 18:59:07 -0600272 """
Simon Glass151aeed2018-07-06 10:27:26 -0600273 fdt_obj = self._fdt._fdt_obj
Simon Glass059ae522017-05-27 07:38:28 -0600274 self.props = self._fdt.GetProps(self)
Simon Glass151aeed2018-07-06 10:27:26 -0600275 phandle = fdt_obj.get_phandle(self.Offset())
Simon Glassa3f94442017-08-29 14:15:52 -0600276 if phandle:
Simon Glass151aeed2018-07-06 10:27:26 -0600277 self._fdt.phandle_to_node[phandle] = self
Simon Glass059ae522017-05-27 07:38:28 -0600278
Simon Glass151aeed2018-07-06 10:27:26 -0600279 offset = fdt_obj.first_subnode(self.Offset(), QUIET_NOTFOUND)
Simon Glass059ae522017-05-27 07:38:28 -0600280 while offset >= 0:
281 sep = '' if self.path[-1] == '/' else '/'
Simon Glass151aeed2018-07-06 10:27:26 -0600282 name = fdt_obj.get_name(offset)
Simon Glass059ae522017-05-27 07:38:28 -0600283 path = self.path + sep + name
Simon Glass80ef7052017-08-29 14:15:47 -0600284 node = Node(self._fdt, self, offset, name, path)
Simon Glass059ae522017-05-27 07:38:28 -0600285 self.subnodes.append(node)
286
287 node.Scan()
Simon Glass151aeed2018-07-06 10:27:26 -0600288 offset = fdt_obj.next_subnode(offset, QUIET_NOTFOUND)
Simon Glass059ae522017-05-27 07:38:28 -0600289
290 def Refresh(self, my_offset):
291 """Fix up the _offset for each node, recursively
292
293 Note: This does not take account of property offsets - these will not
294 be updated.
295 """
Simon Glass792d2392018-07-06 10:27:27 -0600296 fdt_obj = self._fdt._fdt_obj
Simon Glass059ae522017-05-27 07:38:28 -0600297 if self._offset != my_offset:
Simon Glass059ae522017-05-27 07:38:28 -0600298 self._offset = my_offset
Simon Glass792d2392018-07-06 10:27:27 -0600299 offset = fdt_obj.first_subnode(self._offset, QUIET_NOTFOUND)
Simon Glass059ae522017-05-27 07:38:28 -0600300 for subnode in self.subnodes:
Simon Glass4df8a0c2018-07-06 10:27:29 -0600301 if subnode.name != fdt_obj.get_name(offset):
302 raise ValueError('Internal error, node name mismatch %s != %s' %
303 (subnode.name, fdt_obj.get_name(offset)))
Simon Glass059ae522017-05-27 07:38:28 -0600304 subnode.Refresh(offset)
Simon Glass792d2392018-07-06 10:27:27 -0600305 offset = fdt_obj.next_subnode(offset, QUIET_NOTFOUND)
Simon Glass4df8a0c2018-07-06 10:27:29 -0600306 if offset != -libfdt.FDT_ERR_NOTFOUND:
307 raise ValueError('Internal error, offset == %d' % offset)
308
309 poffset = fdt_obj.first_property_offset(self._offset, QUIET_NOTFOUND)
310 while poffset >= 0:
311 p = fdt_obj.get_property_by_offset(poffset)
312 prop = self.props.get(p.name)
313 if not prop:
314 raise ValueError("Internal error, property '%s' missing, "
315 'offset %d' % (p.name, poffset))
316 prop.RefreshOffset(poffset)
317 poffset = fdt_obj.next_property_offset(poffset, QUIET_NOTFOUND)
Simon Glasscc346a72016-07-25 18:59:07 -0600318
Simon Glassc719e422016-07-25 18:59:14 -0600319 def DeleteProp(self, prop_name):
320 """Delete a property of a node
321
Simon Glass059ae522017-05-27 07:38:28 -0600322 The property is deleted and the offset cache is invalidated.
Simon Glassc719e422016-07-25 18:59:14 -0600323
324 Args:
325 prop_name: Name of the property to delete
Simon Glass059ae522017-05-27 07:38:28 -0600326 Raises:
327 ValueError if the property does not exist
Simon Glassc719e422016-07-25 18:59:14 -0600328 """
Simon Glass792d2392018-07-06 10:27:27 -0600329 CheckErr(self._fdt._fdt_obj.delprop(self.Offset(), prop_name),
Simon Glass059ae522017-05-27 07:38:28 -0600330 "Node '%s': delete property: '%s'" % (self.path, prop_name))
331 del self.props[prop_name]
332 self._fdt.Invalidate()
Simon Glassc719e422016-07-25 18:59:14 -0600333
Simon Glasse80c5562018-07-06 10:27:38 -0600334 def AddZeroProp(self, prop_name):
335 """Add a new property to the device tree with an integer value of 0.
336
337 Args:
338 prop_name: Name of property
339 """
Simon Glass6bbfafb2019-05-17 22:00:33 -0600340 self.props[prop_name] = Prop(self, None, prop_name,
341 tools.GetBytes(0, 4))
Simon Glasse80c5562018-07-06 10:27:38 -0600342
Simon Glassccd25262018-09-14 04:57:16 -0600343 def AddEmptyProp(self, prop_name, len):
344 """Add a property with a fixed data size, for filling in later
345
346 The device tree is marked dirty so that the value will be written to
347 the blob on the next sync.
348
349 Args:
350 prop_name: Name of property
351 len: Length of data in property
352 """
Simon Glass6bbfafb2019-05-17 22:00:33 -0600353 value = tools.GetBytes(0, len)
Simon Glassccd25262018-09-14 04:57:16 -0600354 self.props[prop_name] = Prop(self, None, prop_name, value)
355
Simon Glasse80c5562018-07-06 10:27:38 -0600356 def SetInt(self, prop_name, val):
357 """Update an integer property int the device tree.
358
359 This is not allowed to change the size of the FDT.
360
Simon Glassccd25262018-09-14 04:57:16 -0600361 The device tree is marked dirty so that the value will be written to
362 the blob on the next sync.
363
Simon Glasse80c5562018-07-06 10:27:38 -0600364 Args:
365 prop_name: Name of property
366 val: Value to set
367 """
Simon Glasseddd7292018-09-14 04:57:13 -0600368 self.props[prop_name].SetInt(val)
369
Simon Glassccd25262018-09-14 04:57:16 -0600370 def SetData(self, prop_name, val):
371 """Set the data value of a property
372
373 The device tree is marked dirty so that the value will be written to
374 the blob on the next sync.
375
376 Args:
377 prop_name: Name of property to set
378 val: Data value to set
379 """
380 self.props[prop_name].SetData(val)
381
382 def SetString(self, prop_name, val):
383 """Set the string value of a property
384
385 The device tree is marked dirty so that the value will be written to
386 the blob on the next sync.
387
388 Args:
389 prop_name: Name of property to set
390 val: String value to set (will be \0-terminated in DT)
391 """
392 self.props[prop_name].SetData(val + chr(0))
393
394 def AddString(self, prop_name, val):
395 """Add a new string property to a node
396
397 The device tree is marked dirty so that the value will be written to
398 the blob on the next sync.
399
400 Args:
401 prop_name: Name of property to add
402 val: String value of property
403 """
404 self.props[prop_name] = Prop(self, None, prop_name, val + chr(0))
405
Simon Glassf3a17962018-09-14 04:57:15 -0600406 def AddSubnode(self, name):
Simon Glassccd25262018-09-14 04:57:16 -0600407 """Add a new subnode to the node
408
409 Args:
410 name: name of node to add
411
412 Returns:
413 New subnode that was created
414 """
Simon Glassf3a17962018-09-14 04:57:15 -0600415 path = self.path + '/' + name
416 subnode = Node(self._fdt, self, None, name, path)
417 self.subnodes.append(subnode)
418 return subnode
419
Simon Glasseddd7292018-09-14 04:57:13 -0600420 def Sync(self, auto_resize=False):
421 """Sync node changes back to the device tree
422
423 This updates the device tree blob with any changes to this node and its
424 subnodes since the last sync.
425
426 Args:
427 auto_resize: Resize the device tree automatically if it does not
428 have enough space for the update
429
430 Raises:
431 FdtException if auto_resize is False and there is not enough space
432 """
Simon Glassf3a17962018-09-14 04:57:15 -0600433 if self._offset is None:
434 # The subnode doesn't exist yet, so add it
435 fdt_obj = self._fdt._fdt_obj
436 if auto_resize:
437 while True:
438 offset = fdt_obj.add_subnode(self.parent._offset, self.name,
439 (libfdt.NOSPACE,))
440 if offset != -libfdt.NOSPACE:
441 break
442 fdt_obj.resize(fdt_obj.totalsize() + 1024)
443 else:
444 offset = fdt_obj.add_subnode(self.parent._offset, self.name)
445 self._offset = offset
446
Simon Glasseddd7292018-09-14 04:57:13 -0600447 # Sync subnodes in reverse so that we don't disturb node offsets for
448 # nodes that are earlier in the DT. This avoids an O(n^2) rescan of
449 # node offsets.
450 for node in reversed(self.subnodes):
451 node.Sync(auto_resize)
Simon Glasse80c5562018-07-06 10:27:38 -0600452
Simon Glasseddd7292018-09-14 04:57:13 -0600453 # Sync properties now, whose offsets should not have been disturbed.
454 # We do this after subnodes, since this disturbs the offsets of these
455 # properties.
456 prop_list = sorted(self.props.values(), key=lambda prop: prop._offset,
457 reverse=True)
458 for prop in prop_list:
459 prop.Sync(auto_resize)
460
Simon Glasse80c5562018-07-06 10:27:38 -0600461
Simon Glass1f941b62016-07-25 18:59:04 -0600462class Fdt:
Simon Glass059ae522017-05-27 07:38:28 -0600463 """Provides simple access to a flat device tree blob using libfdts.
Simon Glass1f941b62016-07-25 18:59:04 -0600464
465 Properties:
466 fname: Filename of fdt
467 _root: Root of device tree (a Node object)
468 """
469 def __init__(self, fname):
470 self._fname = fname
Simon Glass059ae522017-05-27 07:38:28 -0600471 self._cached_offsets = False
Simon Glassa3f94442017-08-29 14:15:52 -0600472 self.phandle_to_node = {}
Simon Glass059ae522017-05-27 07:38:28 -0600473 if self._fname:
474 self._fname = fdt_util.EnsureCompiled(self._fname)
475
Simon Glass116236f2019-05-14 15:53:43 -0600476 with open(self._fname, 'rb') as fd:
Simon Glass792d2392018-07-06 10:27:27 -0600477 self._fdt_obj = libfdt.Fdt(fd.read())
Simon Glasscc346a72016-07-25 18:59:07 -0600478
Simon Glassb8a49292018-09-14 04:57:17 -0600479 @staticmethod
480 def FromData(data):
481 """Create a new Fdt object from the given data
482
483 Args:
484 data: Device-tree data blob
485
486 Returns:
487 Fdt object containing the data
488 """
489 fdt = Fdt(None)
490 fdt._fdt_obj = libfdt.Fdt(bytearray(data))
491 return fdt
492
Simon Glasse2d65282018-07-17 13:25:46 -0600493 def LookupPhandle(self, phandle):
494 """Look up a phandle
495
496 Args:
497 phandle: Phandle to look up (int)
498
499 Returns:
500 Node object the phandle points to
501 """
502 return self.phandle_to_node.get(phandle)
503
Simon Glasscc346a72016-07-25 18:59:07 -0600504 def Scan(self, root='/'):
505 """Scan a device tree, building up a tree of Node objects
506
507 This fills in the self._root property
508
509 Args:
510 root: Ignored
511
512 TODO(sjg@chromium.org): Implement the 'root' parameter
513 """
Simon Glass4df8a0c2018-07-06 10:27:29 -0600514 self._cached_offsets = True
Simon Glass80ef7052017-08-29 14:15:47 -0600515 self._root = self.Node(self, None, 0, '/', '/')
Simon Glasscc346a72016-07-25 18:59:07 -0600516 self._root.Scan()
517
518 def GetRoot(self):
519 """Get the root Node of the device tree
520
521 Returns:
522 The root Node object
523 """
524 return self._root
525
526 def GetNode(self, path):
527 """Look up a node from its path
528
529 Args:
530 path: Path to look up, e.g. '/microcode/update@0'
531 Returns:
532 Node object, or None if not found
533 """
534 node = self._root
Simon Glassc5eddc82018-07-06 10:27:30 -0600535 parts = path.split('/')
536 if len(parts) < 2:
537 return None
538 for part in parts[1:]:
Simon Glassaa1a5d72018-07-17 13:25:41 -0600539 node = node.FindNode(part)
Simon Glasscc346a72016-07-25 18:59:07 -0600540 if not node:
541 return None
542 return node
543
Simon Glass32d98272016-07-25 18:59:15 -0600544 def Flush(self):
545 """Flush device tree changes back to the file
546
547 If the device tree has changed in memory, write it back to the file.
Simon Glass32d98272016-07-25 18:59:15 -0600548 """
Simon Glass059ae522017-05-27 07:38:28 -0600549 with open(self._fname, 'wb') as fd:
Simon Glass792d2392018-07-06 10:27:27 -0600550 fd.write(self._fdt_obj.as_bytearray())
Simon Glass32d98272016-07-25 18:59:15 -0600551
Simon Glasseddd7292018-09-14 04:57:13 -0600552 def Sync(self, auto_resize=False):
553 """Make sure any DT changes are written to the blob
554
555 Args:
556 auto_resize: Resize the device tree automatically if it does not
557 have enough space for the update
558
559 Raises:
560 FdtException if auto_resize is False and there is not enough space
561 """
562 self._root.Sync(auto_resize)
563 self.Invalidate()
564
Simon Glass32d98272016-07-25 18:59:15 -0600565 def Pack(self):
566 """Pack the device tree down to its minimum size
567
568 When nodes and properties shrink or are deleted, wasted space can
Simon Glass059ae522017-05-27 07:38:28 -0600569 build up in the device tree binary.
570 """
Simon Glass151aeed2018-07-06 10:27:26 -0600571 CheckErr(self._fdt_obj.pack(), 'pack')
572 self.Invalidate()
Simon Glass059ae522017-05-27 07:38:28 -0600573
Simon Glass792d2392018-07-06 10:27:27 -0600574 def GetContents(self):
Simon Glass059ae522017-05-27 07:38:28 -0600575 """Get the contents of the FDT
576
577 Returns:
578 The FDT contents as a string of bytes
Simon Glass32d98272016-07-25 18:59:15 -0600579 """
Simon Glass792d2392018-07-06 10:27:27 -0600580 return self._fdt_obj.as_bytearray()
Simon Glass059ae522017-05-27 07:38:28 -0600581
Simon Glass0ed50752018-07-06 10:27:24 -0600582 def GetFdtObj(self):
583 """Get the contents of the FDT
584
585 Returns:
586 The FDT contents as a libfdt.Fdt object
587 """
588 return self._fdt_obj
589
Simon Glass059ae522017-05-27 07:38:28 -0600590 def GetProps(self, node):
591 """Get all properties from a node.
592
593 Args:
594 node: Full path to node name to look in.
595
596 Returns:
597 A dictionary containing all the properties, indexed by node name.
598 The entries are Prop objects.
599
600 Raises:
601 ValueError: if the node does not exist.
602 """
603 props_dict = {}
Simon Glass151aeed2018-07-06 10:27:26 -0600604 poffset = self._fdt_obj.first_property_offset(node._offset,
605 QUIET_NOTFOUND)
Simon Glass059ae522017-05-27 07:38:28 -0600606 while poffset >= 0:
607 p = self._fdt_obj.get_property_by_offset(poffset)
Simon Glass70cd0d72018-07-06 10:27:20 -0600608 prop = Prop(node, poffset, p.name, p)
Simon Glass059ae522017-05-27 07:38:28 -0600609 props_dict[prop.name] = prop
610
Simon Glass151aeed2018-07-06 10:27:26 -0600611 poffset = self._fdt_obj.next_property_offset(poffset,
612 QUIET_NOTFOUND)
Simon Glass059ae522017-05-27 07:38:28 -0600613 return props_dict
614
615 def Invalidate(self):
616 """Mark our offset cache as invalid"""
617 self._cached_offsets = False
618
619 def CheckCache(self):
620 """Refresh the offset cache if needed"""
621 if self._cached_offsets:
622 return
623 self.Refresh()
624 self._cached_offsets = True
625
626 def Refresh(self):
627 """Refresh the offset cache"""
628 self._root.Refresh(0)
629
630 def GetStructOffset(self, offset):
631 """Get the file offset of a given struct offset
632
633 Args:
634 offset: Offset within the 'struct' region of the device tree
635 Returns:
636 Position of @offset within the device tree binary
637 """
Simon Glass151aeed2018-07-06 10:27:26 -0600638 return self._fdt_obj.off_dt_struct() + offset
Simon Glass059ae522017-05-27 07:38:28 -0600639
640 @classmethod
Simon Glass80ef7052017-08-29 14:15:47 -0600641 def Node(self, fdt, parent, offset, name, path):
Simon Glass059ae522017-05-27 07:38:28 -0600642 """Create a new node
643
644 This is used by Fdt.Scan() to create a new node using the correct
645 class.
646
647 Args:
648 fdt: Fdt object
Simon Glass80ef7052017-08-29 14:15:47 -0600649 parent: Parent node, or None if this is the root node
Simon Glass059ae522017-05-27 07:38:28 -0600650 offset: Offset of node
651 name: Node name
652 path: Full path to node
653 """
Simon Glass80ef7052017-08-29 14:15:47 -0600654 node = Node(fdt, parent, offset, name, path)
Simon Glass059ae522017-05-27 07:38:28 -0600655 return node
Simon Glassa9440932017-05-27 07:38:30 -0600656
657def FdtScan(fname):
Simon Glassc38fee042018-07-06 10:27:32 -0600658 """Returns a new Fdt object"""
Simon Glassa9440932017-05-27 07:38:30 -0600659 dtb = Fdt(fname)
660 dtb.Scan()
661 return dtb