blob: cb365ca094a59c7d6dd3d48d95b68c36445f400e [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
Simon Glass5ea9dcc2020-11-08 20:36:17 -07008from enum import IntEnum
Simon Glassa06a34b2016-07-25 18:59:04 -06009import struct
10import sys
11
Simon Glassbf776672020-04-17 18:09:04 -060012from dtoc import fdt_util
Simon Glass7b75b442017-05-27 07:38:28 -060013import libfdt
Simon Glass117f57b2018-07-06 10:27:26 -060014from libfdt import QUIET_NOTFOUND
Simon Glassbf776672020-04-17 18:09:04 -060015from patman import tools
Simon Glassa06a34b2016-07-25 18:59:04 -060016
17# This deals with a device tree, presenting it as an assortment of Node and
18# Prop objects, representing nodes and properties, respectively. This file
Simon Glass99ed4a22017-05-27 07:38:30 -060019# contains the base classes and defines the high-level API. You can use
20# FdtScan() as a convenience function to create and scan an Fdt.
Simon Glass7b75b442017-05-27 07:38:28 -060021
22# This implementation uses a libfdt Python library to access the device tree,
23# so it is fairly efficient.
Simon Glassa06a34b2016-07-25 18:59:04 -060024
Simon Glassbc1dea32016-07-25 18:59:05 -060025# A list of types we support
Simon Glass5ea9dcc2020-11-08 20:36:17 -070026class Type(IntEnum):
27 (BYTE, INT, STRING, BOOL, INT64) = range(5)
28
29 def is_wider_than(self, other):
30 """Check if another type is 'wider' than this one
31
32 A wider type is one that holds more information than an earlier one,
33 similar to the concept of type-widening in C.
34
35 This uses a simple arithmetic comparison, since type values are in order
36 from narrowest (BYTE) to widest (INT64).
37
38 Args:
39 other: Other type to compare against
40
41 Return:
42 True if the other type is wider
43 """
44 return self.value > other.value
Simon Glassbc1dea32016-07-25 18:59:05 -060045
Simon Glassa06a34b2016-07-25 18:59:04 -060046def CheckErr(errnum, msg):
47 if errnum:
48 raise ValueError('Error %d: %s: %s' %
49 (errnum, libfdt.fdt_strerror(errnum), msg))
50
Simon Glass7e6952d2019-05-17 22:00:34 -060051
Simon Glass2b6ed5e2019-05-17 22:00:35 -060052def BytesToValue(data):
Simon Glass7e6952d2019-05-17 22:00:34 -060053 """Converts a string of bytes into a type and value
54
55 Args:
Simon Glass2b6ed5e2019-05-17 22:00:35 -060056 A bytes value (which on Python 2 is an alias for str)
Simon Glass7e6952d2019-05-17 22:00:34 -060057
58 Return:
59 A tuple:
60 Type of data
61 Data, either a single element or a list of elements. Each element
62 is one of:
Simon Glass5ea9dcc2020-11-08 20:36:17 -070063 Type.STRING: str/bytes value from the property
64 Type.INT: a byte-swapped integer stored as a 4-byte str/bytes
65 Type.BYTE: a byte stored as a single-byte str/bytes
Simon Glass7e6952d2019-05-17 22:00:34 -060066 """
Simon Glass2b6ed5e2019-05-17 22:00:35 -060067 data = bytes(data)
68 size = len(data)
69 strings = data.split(b'\0')
Simon Glass7e6952d2019-05-17 22:00:34 -060070 is_string = True
71 count = len(strings) - 1
Simon Glass2b6ed5e2019-05-17 22:00:35 -060072 if count > 0 and not len(strings[-1]):
Simon Glass7e6952d2019-05-17 22:00:34 -060073 for string in strings[:-1]:
74 if not string:
75 is_string = False
76 break
77 for ch in string:
Simon Glass2b6ed5e2019-05-17 22:00:35 -060078 if ch < 32 or ch > 127:
Simon Glass7e6952d2019-05-17 22:00:34 -060079 is_string = False
80 break
81 else:
82 is_string = False
83 if is_string:
Simon Glass2b6ed5e2019-05-17 22:00:35 -060084 if count == 1:
Simon Glass5ea9dcc2020-11-08 20:36:17 -070085 return Type.STRING, strings[0].decode()
Simon Glass7e6952d2019-05-17 22:00:34 -060086 else:
Simon Glass5ea9dcc2020-11-08 20:36:17 -070087 return Type.STRING, [s.decode() for s in strings[:-1]]
Simon Glass7e6952d2019-05-17 22:00:34 -060088 if size % 4:
89 if size == 1:
Simon Glass5ea9dcc2020-11-08 20:36:17 -070090 return Type.BYTE, tools.ToChar(data[0])
Simon Glass7e6952d2019-05-17 22:00:34 -060091 else:
Simon Glass5ea9dcc2020-11-08 20:36:17 -070092 return Type.BYTE, [tools.ToChar(ch) for ch in list(data)]
Simon Glass7e6952d2019-05-17 22:00:34 -060093 val = []
94 for i in range(0, size, 4):
Simon Glass2b6ed5e2019-05-17 22:00:35 -060095 val.append(data[i:i + 4])
Simon Glass7e6952d2019-05-17 22:00:34 -060096 if size == 4:
Simon Glass5ea9dcc2020-11-08 20:36:17 -070097 return Type.INT, val[0]
Simon Glass7e6952d2019-05-17 22:00:34 -060098 else:
Simon Glass5ea9dcc2020-11-08 20:36:17 -070099 return Type.INT, val
Simon Glass7e6952d2019-05-17 22:00:34 -0600100
101
Simon Glass7b75b442017-05-27 07:38:28 -0600102class Prop:
Simon Glassa06a34b2016-07-25 18:59:04 -0600103 """A device tree property
104
105 Properties:
106 name: Property name (as per the device tree)
107 value: Property value as a string of bytes, or a list of strings of
108 bytes
109 type: Value type
110 """
Simon Glass928527f2019-05-17 22:00:37 -0600111 def __init__(self, node, offset, name, data):
Simon Glassa06a34b2016-07-25 18:59:04 -0600112 self._node = node
113 self._offset = offset
114 self.name = name
115 self.value = None
Simon Glass928527f2019-05-17 22:00:37 -0600116 self.bytes = bytes(data)
Simon Glassfa80c252018-09-14 04:57:13 -0600117 self.dirty = False
Simon Glass928527f2019-05-17 22:00:37 -0600118 if not data:
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700119 self.type = Type.BOOL
Simon Glass7b75b442017-05-27 07:38:28 -0600120 self.value = True
121 return
Simon Glass928527f2019-05-17 22:00:37 -0600122 self.type, self.value = BytesToValue(bytes(data))
Simon Glassa06a34b2016-07-25 18:59:04 -0600123
Simon Glassf9b88b32018-07-06 10:27:29 -0600124 def RefreshOffset(self, poffset):
125 self._offset = poffset
126
Simon Glassc322a852016-07-25 18:59:06 -0600127 def Widen(self, newprop):
128 """Figure out which property type is more general
129
130 Given a current property and a new property, this function returns the
131 one that is less specific as to type. The less specific property will
132 be ble to represent the data in the more specific property. This is
133 used for things like:
134
135 node1 {
136 compatible = "fred";
137 value = <1>;
138 };
139 node1 {
140 compatible = "fred";
141 value = <1 2>;
142 };
143
144 He we want to use an int array for 'value'. The first property
145 suggests that a single int is enough, but the second one shows that
146 it is not. Calling this function with these two propertes would
147 update the current property to be like the second, since it is less
148 specific.
149 """
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700150 if self.type.is_wider_than(newprop.type):
151 if self.type == Type.INT and newprop.type == Type.BYTE:
Simon Glasse144caf2020-10-03 11:31:27 -0600152 if type(self.value) == list:
153 new_value = []
154 for val in self.value:
155 new_value += [tools.ToChar(by) for by in val]
156 else:
157 new_value = [tools.ToChar(by) for by in self.value]
158 self.value = new_value
Simon Glassc322a852016-07-25 18:59:06 -0600159 self.type = newprop.type
160
161 if type(newprop.value) == list and type(self.value) != list:
162 self.value = [self.value]
163
164 if type(self.value) == list and len(newprop.value) > len(self.value):
165 val = self.GetEmpty(self.type)
166 while len(self.value) < len(newprop.value):
167 self.value.append(val)
168
Simon Glass2ba98752018-07-06 10:27:24 -0600169 @classmethod
Simon Glassbc1dea32016-07-25 18:59:05 -0600170 def GetEmpty(self, type):
171 """Get an empty / zero value of the given type
172
173 Returns:
174 A single value of the given type
175 """
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700176 if type == Type.BYTE:
Simon Glassbc1dea32016-07-25 18:59:05 -0600177 return chr(0)
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700178 elif type == Type.INT:
Simon Glassaf53f5a2018-09-14 04:57:14 -0600179 return struct.pack('>I', 0);
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700180 elif type == Type.STRING:
Simon Glassbc1dea32016-07-25 18:59:05 -0600181 return ''
182 else:
183 return True
184
Simon Glassbabdbde2016-07-25 18:59:16 -0600185 def GetOffset(self):
186 """Get the offset of a property
187
Simon Glassbabdbde2016-07-25 18:59:16 -0600188 Returns:
Simon Glass7b75b442017-05-27 07:38:28 -0600189 The offset of the property (struct fdt_property) within the file
Simon Glassbabdbde2016-07-25 18:59:16 -0600190 """
Simon Glassf9b88b32018-07-06 10:27:29 -0600191 self._node._fdt.CheckCache()
Simon Glass7b75b442017-05-27 07:38:28 -0600192 return self._node._fdt.GetStructOffset(self._offset)
Simon Glassbabdbde2016-07-25 18:59:16 -0600193
Simon Glassfa80c252018-09-14 04:57:13 -0600194 def SetInt(self, val):
195 """Set the integer value of the property
196
197 The device tree is marked dirty so that the value will be written to
198 the block on the next sync.
199
200 Args:
201 val: Integer value (32-bit, single cell)
202 """
203 self.bytes = struct.pack('>I', val);
Simon Glass41b781d2018-10-01 12:22:49 -0600204 self.value = self.bytes
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700205 self.type = Type.INT
Simon Glassfa80c252018-09-14 04:57:13 -0600206 self.dirty = True
207
Simon Glass64349612018-09-14 04:57:16 -0600208 def SetData(self, bytes):
209 """Set the value of a property as bytes
210
211 Args:
212 bytes: New property value to set
213 """
Simon Glassf6b64812019-05-17 22:00:36 -0600214 self.bytes = bytes
Simon Glass7e6952d2019-05-17 22:00:34 -0600215 self.type, self.value = BytesToValue(bytes)
Simon Glass64349612018-09-14 04:57:16 -0600216 self.dirty = True
217
Simon Glassfa80c252018-09-14 04:57:13 -0600218 def Sync(self, auto_resize=False):
219 """Sync property changes back to the device tree
220
221 This updates the device tree blob with any changes to this property
222 since the last sync.
223
224 Args:
225 auto_resize: Resize the device tree automatically if it does not
226 have enough space for the update
227
228 Raises:
229 FdtException if auto_resize is False and there is not enough space
230 """
231 if self._offset is None or self.dirty:
232 node = self._node
233 fdt_obj = node._fdt._fdt_obj
234 if auto_resize:
235 while fdt_obj.setprop(node.Offset(), self.name, self.bytes,
236 (libfdt.NOSPACE,)) == -libfdt.NOSPACE:
Simon Glassc0639172020-07-09 18:39:44 -0600237 fdt_obj.resize(fdt_obj.totalsize() + 1024 +
238 len(self.bytes))
Simon Glassfa80c252018-09-14 04:57:13 -0600239 fdt_obj.setprop(node.Offset(), self.name, self.bytes)
240 else:
241 fdt_obj.setprop(node.Offset(), self.name, self.bytes)
242
243
Simon Glass7b75b442017-05-27 07:38:28 -0600244class Node:
Simon Glassa06a34b2016-07-25 18:59:04 -0600245 """A device tree node
246
247 Properties:
248 offset: Integer offset in the device tree
249 name: Device tree node tname
250 path: Full path to node, along with the node name itself
251 _fdt: Device tree object
252 subnodes: A list of subnodes for this node, each a Node object
253 props: A dict of properties for this node, each a Prop object.
254 Keyed by property name
255 """
Simon Glass979ab022017-08-29 14:15:47 -0600256 def __init__(self, fdt, parent, offset, name, path):
Simon Glassa06a34b2016-07-25 18:59:04 -0600257 self._fdt = fdt
Simon Glass979ab022017-08-29 14:15:47 -0600258 self.parent = parent
Simon Glassa06a34b2016-07-25 18:59:04 -0600259 self._offset = offset
260 self.name = name
261 self.path = path
262 self.subnodes = []
263 self.props = {}
264
Simon Glass94a7c602018-07-17 13:25:46 -0600265 def GetFdt(self):
266 """Get the Fdt object for this node
267
268 Returns:
269 Fdt object
270 """
271 return self._fdt
272
Simon Glass1d858882018-07-17 13:25:41 -0600273 def FindNode(self, name):
Simon Glassf7a2aee2016-07-25 18:59:07 -0600274 """Find a node given its name
275
276 Args:
277 name: Node name to look for
278 Returns:
279 Node object if found, else None
280 """
281 for subnode in self.subnodes:
282 if subnode.name == name:
283 return subnode
284 return None
285
Simon Glass7b75b442017-05-27 07:38:28 -0600286 def Offset(self):
287 """Returns the offset of a node, after checking the cache
Simon Glassf7a2aee2016-07-25 18:59:07 -0600288
Simon Glass7b75b442017-05-27 07:38:28 -0600289 This should be used instead of self._offset directly, to ensure that
290 the cache does not contain invalid offsets.
Simon Glassf7a2aee2016-07-25 18:59:07 -0600291 """
Simon Glass7b75b442017-05-27 07:38:28 -0600292 self._fdt.CheckCache()
293 return self._offset
294
295 def Scan(self):
296 """Scan a node's properties and subnodes
297
298 This fills in the props and subnodes properties, recursively
299 searching into subnodes so that the entire tree is built.
300 """
Simon Glass117f57b2018-07-06 10:27:26 -0600301 fdt_obj = self._fdt._fdt_obj
Simon Glass7b75b442017-05-27 07:38:28 -0600302 self.props = self._fdt.GetProps(self)
Simon Glass117f57b2018-07-06 10:27:26 -0600303 phandle = fdt_obj.get_phandle(self.Offset())
Simon Glass09264e02017-08-29 14:15:52 -0600304 if phandle:
Simon Glass117f57b2018-07-06 10:27:26 -0600305 self._fdt.phandle_to_node[phandle] = self
Simon Glass7b75b442017-05-27 07:38:28 -0600306
Simon Glass117f57b2018-07-06 10:27:26 -0600307 offset = fdt_obj.first_subnode(self.Offset(), QUIET_NOTFOUND)
Simon Glass7b75b442017-05-27 07:38:28 -0600308 while offset >= 0:
309 sep = '' if self.path[-1] == '/' else '/'
Simon Glass117f57b2018-07-06 10:27:26 -0600310 name = fdt_obj.get_name(offset)
Simon Glass7b75b442017-05-27 07:38:28 -0600311 path = self.path + sep + name
Simon Glass979ab022017-08-29 14:15:47 -0600312 node = Node(self._fdt, self, offset, name, path)
Simon Glass7b75b442017-05-27 07:38:28 -0600313 self.subnodes.append(node)
314
315 node.Scan()
Simon Glass117f57b2018-07-06 10:27:26 -0600316 offset = fdt_obj.next_subnode(offset, QUIET_NOTFOUND)
Simon Glass7b75b442017-05-27 07:38:28 -0600317
318 def Refresh(self, my_offset):
319 """Fix up the _offset for each node, recursively
320
321 Note: This does not take account of property offsets - these will not
322 be updated.
323 """
Simon Glass96066242018-07-06 10:27:27 -0600324 fdt_obj = self._fdt._fdt_obj
Simon Glass7b75b442017-05-27 07:38:28 -0600325 if self._offset != my_offset:
Simon Glass7b75b442017-05-27 07:38:28 -0600326 self._offset = my_offset
Simon Glass96066242018-07-06 10:27:27 -0600327 offset = fdt_obj.first_subnode(self._offset, QUIET_NOTFOUND)
Simon Glass7b75b442017-05-27 07:38:28 -0600328 for subnode in self.subnodes:
Simon Glassf9b88b32018-07-06 10:27:29 -0600329 if subnode.name != fdt_obj.get_name(offset):
330 raise ValueError('Internal error, node name mismatch %s != %s' %
331 (subnode.name, fdt_obj.get_name(offset)))
Simon Glass7b75b442017-05-27 07:38:28 -0600332 subnode.Refresh(offset)
Simon Glass96066242018-07-06 10:27:27 -0600333 offset = fdt_obj.next_subnode(offset, QUIET_NOTFOUND)
Simon Glassf9b88b32018-07-06 10:27:29 -0600334 if offset != -libfdt.FDT_ERR_NOTFOUND:
335 raise ValueError('Internal error, offset == %d' % offset)
336
337 poffset = fdt_obj.first_property_offset(self._offset, QUIET_NOTFOUND)
338 while poffset >= 0:
339 p = fdt_obj.get_property_by_offset(poffset)
340 prop = self.props.get(p.name)
341 if not prop:
342 raise ValueError("Internal error, property '%s' missing, "
343 'offset %d' % (p.name, poffset))
344 prop.RefreshOffset(poffset)
345 poffset = fdt_obj.next_property_offset(poffset, QUIET_NOTFOUND)
Simon Glassf7a2aee2016-07-25 18:59:07 -0600346
Simon Glass2a70d892016-07-25 18:59:14 -0600347 def DeleteProp(self, prop_name):
348 """Delete a property of a node
349
Simon Glass7b75b442017-05-27 07:38:28 -0600350 The property is deleted and the offset cache is invalidated.
Simon Glass2a70d892016-07-25 18:59:14 -0600351
352 Args:
353 prop_name: Name of the property to delete
Simon Glass7b75b442017-05-27 07:38:28 -0600354 Raises:
355 ValueError if the property does not exist
Simon Glass2a70d892016-07-25 18:59:14 -0600356 """
Simon Glass96066242018-07-06 10:27:27 -0600357 CheckErr(self._fdt._fdt_obj.delprop(self.Offset(), prop_name),
Simon Glass7b75b442017-05-27 07:38:28 -0600358 "Node '%s': delete property: '%s'" % (self.path, prop_name))
359 del self.props[prop_name]
360 self._fdt.Invalidate()
Simon Glass2a70d892016-07-25 18:59:14 -0600361
Simon Glass116adec2018-07-06 10:27:38 -0600362 def AddZeroProp(self, prop_name):
363 """Add a new property to the device tree with an integer value of 0.
364
365 Args:
366 prop_name: Name of property
367 """
Simon Glass194b8d52019-05-17 22:00:33 -0600368 self.props[prop_name] = Prop(self, None, prop_name,
369 tools.GetBytes(0, 4))
Simon Glass116adec2018-07-06 10:27:38 -0600370
Simon Glass64349612018-09-14 04:57:16 -0600371 def AddEmptyProp(self, prop_name, len):
372 """Add a property with a fixed data size, for filling in later
373
374 The device tree is marked dirty so that the value will be written to
375 the blob on the next sync.
376
377 Args:
378 prop_name: Name of property
379 len: Length of data in property
380 """
Simon Glass194b8d52019-05-17 22:00:33 -0600381 value = tools.GetBytes(0, len)
Simon Glass64349612018-09-14 04:57:16 -0600382 self.props[prop_name] = Prop(self, None, prop_name, value)
383
Simon Glassd9dad102019-07-20 12:23:37 -0600384 def _CheckProp(self, prop_name):
385 """Check if a property is present
386
387 Args:
388 prop_name: Name of property
389
390 Returns:
391 self
392
393 Raises:
394 ValueError if the property is missing
395 """
396 if prop_name not in self.props:
397 raise ValueError("Fdt '%s', node '%s': Missing property '%s'" %
398 (self._fdt._fname, self.path, prop_name))
399 return self
400
Simon Glass116adec2018-07-06 10:27:38 -0600401 def SetInt(self, prop_name, val):
402 """Update an integer property int the device tree.
403
404 This is not allowed to change the size of the FDT.
405
Simon Glass64349612018-09-14 04:57:16 -0600406 The device tree is marked dirty so that the value will be written to
407 the blob on the next sync.
408
Simon Glass116adec2018-07-06 10:27:38 -0600409 Args:
410 prop_name: Name of property
411 val: Value to set
412 """
Simon Glassd9dad102019-07-20 12:23:37 -0600413 self._CheckProp(prop_name).props[prop_name].SetInt(val)
Simon Glassfa80c252018-09-14 04:57:13 -0600414
Simon Glass64349612018-09-14 04:57:16 -0600415 def SetData(self, prop_name, val):
416 """Set the data value of a property
417
418 The device tree is marked dirty so that the value will be written to
419 the blob on the next sync.
420
421 Args:
422 prop_name: Name of property to set
423 val: Data value to set
424 """
Simon Glassd9dad102019-07-20 12:23:37 -0600425 self._CheckProp(prop_name).props[prop_name].SetData(val)
Simon Glass64349612018-09-14 04:57:16 -0600426
427 def SetString(self, prop_name, val):
428 """Set the string value of a property
429
430 The device tree is marked dirty so that the value will be written to
431 the blob on the next sync.
432
433 Args:
434 prop_name: Name of property to set
435 val: String value to set (will be \0-terminated in DT)
436 """
Simon Glassa90df2b2019-10-31 07:43:04 -0600437 if type(val) == str:
438 val = val.encode('utf-8')
Simon Glassd9dad102019-07-20 12:23:37 -0600439 self._CheckProp(prop_name).props[prop_name].SetData(val + b'\0')
Simon Glass64349612018-09-14 04:57:16 -0600440
Simon Glassc0639172020-07-09 18:39:44 -0600441 def AddData(self, prop_name, val):
442 """Add a new property to a node
443
444 The device tree is marked dirty so that the value will be written to
445 the blob on the next sync.
446
447 Args:
448 prop_name: Name of property to add
449 val: Bytes value of property
450 """
451 self.props[prop_name] = Prop(self, None, prop_name, val)
452
Simon Glass64349612018-09-14 04:57:16 -0600453 def AddString(self, prop_name, val):
454 """Add a new string property to a node
455
456 The device tree is marked dirty so that the value will be written to
457 the blob on the next sync.
458
459 Args:
460 prop_name: Name of property to add
461 val: String value of property
462 """
Simon Glassf6b64812019-05-17 22:00:36 -0600463 if sys.version_info[0] >= 3: # pragma: no cover
464 val = bytes(val, 'utf-8')
Simon Glassc0639172020-07-09 18:39:44 -0600465 self.AddData(prop_name, val + b'\0')
Simon Glass64349612018-09-14 04:57:16 -0600466
Simon Glasse21c27a2018-09-14 04:57:15 -0600467 def AddSubnode(self, name):
Simon Glass64349612018-09-14 04:57:16 -0600468 """Add a new subnode to the node
469
470 Args:
471 name: name of node to add
472
473 Returns:
474 New subnode that was created
475 """
Simon Glasse21c27a2018-09-14 04:57:15 -0600476 path = self.path + '/' + name
477 subnode = Node(self._fdt, self, None, name, path)
478 self.subnodes.append(subnode)
479 return subnode
480
Simon Glassfa80c252018-09-14 04:57:13 -0600481 def Sync(self, auto_resize=False):
482 """Sync node changes back to the device tree
483
484 This updates the device tree blob with any changes to this node and its
485 subnodes since the last sync.
486
487 Args:
488 auto_resize: Resize the device tree automatically if it does not
489 have enough space for the update
490
491 Raises:
492 FdtException if auto_resize is False and there is not enough space
493 """
Simon Glasse21c27a2018-09-14 04:57:15 -0600494 if self._offset is None:
495 # The subnode doesn't exist yet, so add it
496 fdt_obj = self._fdt._fdt_obj
497 if auto_resize:
498 while True:
499 offset = fdt_obj.add_subnode(self.parent._offset, self.name,
500 (libfdt.NOSPACE,))
501 if offset != -libfdt.NOSPACE:
502 break
503 fdt_obj.resize(fdt_obj.totalsize() + 1024)
504 else:
505 offset = fdt_obj.add_subnode(self.parent._offset, self.name)
506 self._offset = offset
507
Simon Glassfa80c252018-09-14 04:57:13 -0600508 # Sync subnodes in reverse so that we don't disturb node offsets for
509 # nodes that are earlier in the DT. This avoids an O(n^2) rescan of
510 # node offsets.
511 for node in reversed(self.subnodes):
512 node.Sync(auto_resize)
513
514 # Sync properties now, whose offsets should not have been disturbed.
515 # We do this after subnodes, since this disturbs the offsets of these
Simon Glass63518052019-05-17 22:00:38 -0600516 # properties. Note that new properties will have an offset of None here,
517 # which Python 3 cannot sort against int. So use a large value instead
518 # to ensure that the new properties are added first.
519 prop_list = sorted(self.props.values(),
520 key=lambda prop: prop._offset or 1 << 31,
Simon Glassfa80c252018-09-14 04:57:13 -0600521 reverse=True)
522 for prop in prop_list:
523 prop.Sync(auto_resize)
Simon Glass116adec2018-07-06 10:27:38 -0600524
525
Simon Glassa06a34b2016-07-25 18:59:04 -0600526class Fdt:
Simon Glass7b75b442017-05-27 07:38:28 -0600527 """Provides simple access to a flat device tree blob using libfdts.
Simon Glassa06a34b2016-07-25 18:59:04 -0600528
529 Properties:
530 fname: Filename of fdt
531 _root: Root of device tree (a Node object)
Simon Glass880e9ee2019-07-20 12:23:38 -0600532 name: Helpful name for this Fdt for the user (useful when creating the
533 DT from data rather than a file)
Simon Glassa06a34b2016-07-25 18:59:04 -0600534 """
535 def __init__(self, fname):
536 self._fname = fname
Simon Glass7b75b442017-05-27 07:38:28 -0600537 self._cached_offsets = False
Simon Glass09264e02017-08-29 14:15:52 -0600538 self.phandle_to_node = {}
Simon Glass880e9ee2019-07-20 12:23:38 -0600539 self.name = ''
Simon Glass7b75b442017-05-27 07:38:28 -0600540 if self._fname:
Simon Glass880e9ee2019-07-20 12:23:38 -0600541 self.name = self._fname
Simon Glass7b75b442017-05-27 07:38:28 -0600542 self._fname = fdt_util.EnsureCompiled(self._fname)
543
Simon Glass3e4b51e2019-05-14 15:53:43 -0600544 with open(self._fname, 'rb') as fd:
Simon Glass96066242018-07-06 10:27:27 -0600545 self._fdt_obj = libfdt.Fdt(fd.read())
Simon Glassf7a2aee2016-07-25 18:59:07 -0600546
Simon Glass746aee32018-09-14 04:57:17 -0600547 @staticmethod
Simon Glass880e9ee2019-07-20 12:23:38 -0600548 def FromData(data, name=''):
Simon Glass746aee32018-09-14 04:57:17 -0600549 """Create a new Fdt object from the given data
550
551 Args:
552 data: Device-tree data blob
Simon Glass880e9ee2019-07-20 12:23:38 -0600553 name: Helpful name for this Fdt for the user
Simon Glass746aee32018-09-14 04:57:17 -0600554
555 Returns:
556 Fdt object containing the data
557 """
558 fdt = Fdt(None)
Simon Glassf6b64812019-05-17 22:00:36 -0600559 fdt._fdt_obj = libfdt.Fdt(bytes(data))
Simon Glass880e9ee2019-07-20 12:23:38 -0600560 fdt.name = name
Simon Glass746aee32018-09-14 04:57:17 -0600561 return fdt
562
Simon Glass94a7c602018-07-17 13:25:46 -0600563 def LookupPhandle(self, phandle):
564 """Look up a phandle
565
566 Args:
567 phandle: Phandle to look up (int)
568
569 Returns:
570 Node object the phandle points to
571 """
572 return self.phandle_to_node.get(phandle)
573
Simon Glassf7a2aee2016-07-25 18:59:07 -0600574 def Scan(self, root='/'):
575 """Scan a device tree, building up a tree of Node objects
576
577 This fills in the self._root property
578
579 Args:
580 root: Ignored
581
582 TODO(sjg@chromium.org): Implement the 'root' parameter
583 """
Simon Glassf9b88b32018-07-06 10:27:29 -0600584 self._cached_offsets = True
Simon Glass979ab022017-08-29 14:15:47 -0600585 self._root = self.Node(self, None, 0, '/', '/')
Simon Glassf7a2aee2016-07-25 18:59:07 -0600586 self._root.Scan()
587
588 def GetRoot(self):
589 """Get the root Node of the device tree
590
591 Returns:
592 The root Node object
593 """
594 return self._root
595
596 def GetNode(self, path):
597 """Look up a node from its path
598
599 Args:
600 path: Path to look up, e.g. '/microcode/update@0'
601 Returns:
602 Node object, or None if not found
603 """
604 node = self._root
Simon Glassb9066ff2018-07-06 10:27:30 -0600605 parts = path.split('/')
606 if len(parts) < 2:
607 return None
Simon Glasse44bc832019-07-20 12:23:39 -0600608 if len(parts) == 2 and parts[1] == '':
609 return node
Simon Glassb9066ff2018-07-06 10:27:30 -0600610 for part in parts[1:]:
Simon Glass1d858882018-07-17 13:25:41 -0600611 node = node.FindNode(part)
Simon Glassf7a2aee2016-07-25 18:59:07 -0600612 if not node:
613 return None
614 return node
615
Simon Glassda5f7492016-07-25 18:59:15 -0600616 def Flush(self):
617 """Flush device tree changes back to the file
618
619 If the device tree has changed in memory, write it back to the file.
Simon Glassda5f7492016-07-25 18:59:15 -0600620 """
Simon Glass7b75b442017-05-27 07:38:28 -0600621 with open(self._fname, 'wb') as fd:
Simon Glass96066242018-07-06 10:27:27 -0600622 fd.write(self._fdt_obj.as_bytearray())
Simon Glassda5f7492016-07-25 18:59:15 -0600623
Simon Glassfa80c252018-09-14 04:57:13 -0600624 def Sync(self, auto_resize=False):
625 """Make sure any DT changes are written to the blob
626
627 Args:
628 auto_resize: Resize the device tree automatically if it does not
629 have enough space for the update
630
631 Raises:
632 FdtException if auto_resize is False and there is not enough space
633 """
634 self._root.Sync(auto_resize)
635 self.Invalidate()
636
Simon Glassda5f7492016-07-25 18:59:15 -0600637 def Pack(self):
638 """Pack the device tree down to its minimum size
639
640 When nodes and properties shrink or are deleted, wasted space can
Simon Glass7b75b442017-05-27 07:38:28 -0600641 build up in the device tree binary.
Simon Glassda5f7492016-07-25 18:59:15 -0600642 """
Simon Glass117f57b2018-07-06 10:27:26 -0600643 CheckErr(self._fdt_obj.pack(), 'pack')
644 self.Invalidate()
Simon Glass7b75b442017-05-27 07:38:28 -0600645
Simon Glass96066242018-07-06 10:27:27 -0600646 def GetContents(self):
Simon Glass7b75b442017-05-27 07:38:28 -0600647 """Get the contents of the FDT
648
649 Returns:
650 The FDT contents as a string of bytes
651 """
Simon Glassf6b64812019-05-17 22:00:36 -0600652 return bytes(self._fdt_obj.as_bytearray())
Simon Glass7b75b442017-05-27 07:38:28 -0600653
Simon Glass2ba98752018-07-06 10:27:24 -0600654 def GetFdtObj(self):
655 """Get the contents of the FDT
656
657 Returns:
658 The FDT contents as a libfdt.Fdt object
659 """
660 return self._fdt_obj
661
Simon Glass7b75b442017-05-27 07:38:28 -0600662 def GetProps(self, node):
663 """Get all properties from a node.
664
665 Args:
666 node: Full path to node name to look in.
667
668 Returns:
669 A dictionary containing all the properties, indexed by node name.
670 The entries are Prop objects.
671
672 Raises:
673 ValueError: if the node does not exist.
674 """
675 props_dict = {}
Simon Glass117f57b2018-07-06 10:27:26 -0600676 poffset = self._fdt_obj.first_property_offset(node._offset,
677 QUIET_NOTFOUND)
Simon Glass7b75b442017-05-27 07:38:28 -0600678 while poffset >= 0:
679 p = self._fdt_obj.get_property_by_offset(poffset)
Simon Glass3def0cf2018-07-06 10:27:20 -0600680 prop = Prop(node, poffset, p.name, p)
Simon Glass7b75b442017-05-27 07:38:28 -0600681 props_dict[prop.name] = prop
682
Simon Glass117f57b2018-07-06 10:27:26 -0600683 poffset = self._fdt_obj.next_property_offset(poffset,
684 QUIET_NOTFOUND)
Simon Glass7b75b442017-05-27 07:38:28 -0600685 return props_dict
686
687 def Invalidate(self):
688 """Mark our offset cache as invalid"""
689 self._cached_offsets = False
690
691 def CheckCache(self):
692 """Refresh the offset cache if needed"""
693 if self._cached_offsets:
694 return
695 self.Refresh()
696 self._cached_offsets = True
697
698 def Refresh(self):
699 """Refresh the offset cache"""
700 self._root.Refresh(0)
701
702 def GetStructOffset(self, offset):
703 """Get the file offset of a given struct offset
704
705 Args:
706 offset: Offset within the 'struct' region of the device tree
707 Returns:
708 Position of @offset within the device tree binary
709 """
Simon Glass117f57b2018-07-06 10:27:26 -0600710 return self._fdt_obj.off_dt_struct() + offset
Simon Glass7b75b442017-05-27 07:38:28 -0600711
712 @classmethod
Simon Glass979ab022017-08-29 14:15:47 -0600713 def Node(self, fdt, parent, offset, name, path):
Simon Glass7b75b442017-05-27 07:38:28 -0600714 """Create a new node
715
716 This is used by Fdt.Scan() to create a new node using the correct
717 class.
718
719 Args:
720 fdt: Fdt object
Simon Glass979ab022017-08-29 14:15:47 -0600721 parent: Parent node, or None if this is the root node
Simon Glass7b75b442017-05-27 07:38:28 -0600722 offset: Offset of node
723 name: Node name
724 path: Full path to node
725 """
Simon Glass979ab022017-08-29 14:15:47 -0600726 node = Node(fdt, parent, offset, name, path)
Simon Glass7b75b442017-05-27 07:38:28 -0600727 return node
Simon Glass99ed4a22017-05-27 07:38:30 -0600728
Simon Glassf6e02492019-07-20 12:24:08 -0600729 def GetFilename(self):
730 """Get the filename of the device tree
731
732 Returns:
733 String filename
734 """
735 return self._fname
736
Simon Glass99ed4a22017-05-27 07:38:30 -0600737def FdtScan(fname):
Simon Glassdfe5f5b2018-07-06 10:27:32 -0600738 """Returns a new Fdt object"""
Simon Glass99ed4a22017-05-27 07:38:30 -0600739 dtb = Fdt(fname)
740 dtb.Scan()
741 return dtb