blob: 965106a3b28a294eca849443273bea8290c9c90a [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 Glass479dd302020-11-08 20:36:20 -070090 return Type.BYTE, chr(data[0])
Simon Glass7e6952d2019-05-17 22:00:34 -060091 else:
Simon Glass479dd302020-11-08 20:36:20 -070092 return Type.BYTE, [chr(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:
Simon Glass479dd302020-11-08 20:36:20 -0700155 new_value += [chr(by) for by in val]
Simon Glasse144caf2020-10-03 11:31:27 -0600156 else:
Simon Glass479dd302020-11-08 20:36:20 -0700157 new_value = [chr(by) for by in self.value]
Simon Glasse144caf2020-10-03 11:31:27 -0600158 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 Glass9fc6ebd2021-01-06 21:35:10 -0700463 val = bytes(val, 'utf-8')
Simon Glassc0639172020-07-09 18:39:44 -0600464 self.AddData(prop_name, val + b'\0')
Simon Glass64349612018-09-14 04:57:16 -0600465
Simon Glasse21c27a2018-09-14 04:57:15 -0600466 def AddSubnode(self, name):
Simon Glass64349612018-09-14 04:57:16 -0600467 """Add a new subnode to the node
468
469 Args:
470 name: name of node to add
471
472 Returns:
473 New subnode that was created
474 """
Simon Glasse21c27a2018-09-14 04:57:15 -0600475 path = self.path + '/' + name
476 subnode = Node(self._fdt, self, None, name, path)
477 self.subnodes.append(subnode)
478 return subnode
479
Simon Glassfa80c252018-09-14 04:57:13 -0600480 def Sync(self, auto_resize=False):
481 """Sync node changes back to the device tree
482
483 This updates the device tree blob with any changes to this node and its
484 subnodes since the last sync.
485
486 Args:
487 auto_resize: Resize the device tree automatically if it does not
488 have enough space for the update
489
490 Raises:
491 FdtException if auto_resize is False and there is not enough space
492 """
Simon Glasse21c27a2018-09-14 04:57:15 -0600493 if self._offset is None:
494 # The subnode doesn't exist yet, so add it
495 fdt_obj = self._fdt._fdt_obj
496 if auto_resize:
497 while True:
498 offset = fdt_obj.add_subnode(self.parent._offset, self.name,
499 (libfdt.NOSPACE,))
500 if offset != -libfdt.NOSPACE:
501 break
502 fdt_obj.resize(fdt_obj.totalsize() + 1024)
503 else:
504 offset = fdt_obj.add_subnode(self.parent._offset, self.name)
505 self._offset = offset
506
Simon Glassfa80c252018-09-14 04:57:13 -0600507 # Sync subnodes in reverse so that we don't disturb node offsets for
508 # nodes that are earlier in the DT. This avoids an O(n^2) rescan of
509 # node offsets.
510 for node in reversed(self.subnodes):
511 node.Sync(auto_resize)
512
513 # Sync properties now, whose offsets should not have been disturbed.
514 # We do this after subnodes, since this disturbs the offsets of these
Simon Glass63518052019-05-17 22:00:38 -0600515 # properties. Note that new properties will have an offset of None here,
516 # which Python 3 cannot sort against int. So use a large value instead
517 # to ensure that the new properties are added first.
518 prop_list = sorted(self.props.values(),
519 key=lambda prop: prop._offset or 1 << 31,
Simon Glassfa80c252018-09-14 04:57:13 -0600520 reverse=True)
521 for prop in prop_list:
522 prop.Sync(auto_resize)
Simon Glass116adec2018-07-06 10:27:38 -0600523
524
Simon Glassa06a34b2016-07-25 18:59:04 -0600525class Fdt:
Simon Glass7b75b442017-05-27 07:38:28 -0600526 """Provides simple access to a flat device tree blob using libfdts.
Simon Glassa06a34b2016-07-25 18:59:04 -0600527
528 Properties:
529 fname: Filename of fdt
530 _root: Root of device tree (a Node object)
Simon Glass880e9ee2019-07-20 12:23:38 -0600531 name: Helpful name for this Fdt for the user (useful when creating the
532 DT from data rather than a file)
Simon Glassa06a34b2016-07-25 18:59:04 -0600533 """
534 def __init__(self, fname):
535 self._fname = fname
Simon Glass7b75b442017-05-27 07:38:28 -0600536 self._cached_offsets = False
Simon Glass09264e02017-08-29 14:15:52 -0600537 self.phandle_to_node = {}
Simon Glass880e9ee2019-07-20 12:23:38 -0600538 self.name = ''
Simon Glass7b75b442017-05-27 07:38:28 -0600539 if self._fname:
Simon Glass880e9ee2019-07-20 12:23:38 -0600540 self.name = self._fname
Simon Glass7b75b442017-05-27 07:38:28 -0600541 self._fname = fdt_util.EnsureCompiled(self._fname)
542
Simon Glass3e4b51e2019-05-14 15:53:43 -0600543 with open(self._fname, 'rb') as fd:
Simon Glass96066242018-07-06 10:27:27 -0600544 self._fdt_obj = libfdt.Fdt(fd.read())
Simon Glassf7a2aee2016-07-25 18:59:07 -0600545
Simon Glass746aee32018-09-14 04:57:17 -0600546 @staticmethod
Simon Glass880e9ee2019-07-20 12:23:38 -0600547 def FromData(data, name=''):
Simon Glass746aee32018-09-14 04:57:17 -0600548 """Create a new Fdt object from the given data
549
550 Args:
551 data: Device-tree data blob
Simon Glass880e9ee2019-07-20 12:23:38 -0600552 name: Helpful name for this Fdt for the user
Simon Glass746aee32018-09-14 04:57:17 -0600553
554 Returns:
555 Fdt object containing the data
556 """
557 fdt = Fdt(None)
Simon Glassf6b64812019-05-17 22:00:36 -0600558 fdt._fdt_obj = libfdt.Fdt(bytes(data))
Simon Glass880e9ee2019-07-20 12:23:38 -0600559 fdt.name = name
Simon Glass746aee32018-09-14 04:57:17 -0600560 return fdt
561
Simon Glass94a7c602018-07-17 13:25:46 -0600562 def LookupPhandle(self, phandle):
563 """Look up a phandle
564
565 Args:
566 phandle: Phandle to look up (int)
567
568 Returns:
569 Node object the phandle points to
570 """
571 return self.phandle_to_node.get(phandle)
572
Simon Glassf7a2aee2016-07-25 18:59:07 -0600573 def Scan(self, root='/'):
574 """Scan a device tree, building up a tree of Node objects
575
576 This fills in the self._root property
577
578 Args:
579 root: Ignored
580
581 TODO(sjg@chromium.org): Implement the 'root' parameter
582 """
Simon Glassf9b88b32018-07-06 10:27:29 -0600583 self._cached_offsets = True
Simon Glass979ab022017-08-29 14:15:47 -0600584 self._root = self.Node(self, None, 0, '/', '/')
Simon Glassf7a2aee2016-07-25 18:59:07 -0600585 self._root.Scan()
586
587 def GetRoot(self):
588 """Get the root Node of the device tree
589
590 Returns:
591 The root Node object
592 """
593 return self._root
594
595 def GetNode(self, path):
596 """Look up a node from its path
597
598 Args:
599 path: Path to look up, e.g. '/microcode/update@0'
600 Returns:
601 Node object, or None if not found
602 """
603 node = self._root
Simon Glassb9066ff2018-07-06 10:27:30 -0600604 parts = path.split('/')
605 if len(parts) < 2:
606 return None
Simon Glasse44bc832019-07-20 12:23:39 -0600607 if len(parts) == 2 and parts[1] == '':
608 return node
Simon Glassb9066ff2018-07-06 10:27:30 -0600609 for part in parts[1:]:
Simon Glass1d858882018-07-17 13:25:41 -0600610 node = node.FindNode(part)
Simon Glassf7a2aee2016-07-25 18:59:07 -0600611 if not node:
612 return None
613 return node
614
Simon Glassda5f7492016-07-25 18:59:15 -0600615 def Flush(self):
616 """Flush device tree changes back to the file
617
618 If the device tree has changed in memory, write it back to the file.
Simon Glassda5f7492016-07-25 18:59:15 -0600619 """
Simon Glass7b75b442017-05-27 07:38:28 -0600620 with open(self._fname, 'wb') as fd:
Simon Glass96066242018-07-06 10:27:27 -0600621 fd.write(self._fdt_obj.as_bytearray())
Simon Glassda5f7492016-07-25 18:59:15 -0600622
Simon Glassfa80c252018-09-14 04:57:13 -0600623 def Sync(self, auto_resize=False):
624 """Make sure any DT changes are written to the blob
625
626 Args:
627 auto_resize: Resize the device tree automatically if it does not
628 have enough space for the update
629
630 Raises:
631 FdtException if auto_resize is False and there is not enough space
632 """
633 self._root.Sync(auto_resize)
634 self.Invalidate()
635
Simon Glassda5f7492016-07-25 18:59:15 -0600636 def Pack(self):
637 """Pack the device tree down to its minimum size
638
639 When nodes and properties shrink or are deleted, wasted space can
Simon Glass7b75b442017-05-27 07:38:28 -0600640 build up in the device tree binary.
Simon Glassda5f7492016-07-25 18:59:15 -0600641 """
Simon Glass117f57b2018-07-06 10:27:26 -0600642 CheckErr(self._fdt_obj.pack(), 'pack')
643 self.Invalidate()
Simon Glass7b75b442017-05-27 07:38:28 -0600644
Simon Glass96066242018-07-06 10:27:27 -0600645 def GetContents(self):
Simon Glass7b75b442017-05-27 07:38:28 -0600646 """Get the contents of the FDT
647
648 Returns:
649 The FDT contents as a string of bytes
650 """
Simon Glassf6b64812019-05-17 22:00:36 -0600651 return bytes(self._fdt_obj.as_bytearray())
Simon Glass7b75b442017-05-27 07:38:28 -0600652
Simon Glass2ba98752018-07-06 10:27:24 -0600653 def GetFdtObj(self):
654 """Get the contents of the FDT
655
656 Returns:
657 The FDT contents as a libfdt.Fdt object
658 """
659 return self._fdt_obj
660
Simon Glass7b75b442017-05-27 07:38:28 -0600661 def GetProps(self, node):
662 """Get all properties from a node.
663
664 Args:
665 node: Full path to node name to look in.
666
667 Returns:
668 A dictionary containing all the properties, indexed by node name.
669 The entries are Prop objects.
670
671 Raises:
672 ValueError: if the node does not exist.
673 """
674 props_dict = {}
Simon Glass117f57b2018-07-06 10:27:26 -0600675 poffset = self._fdt_obj.first_property_offset(node._offset,
676 QUIET_NOTFOUND)
Simon Glass7b75b442017-05-27 07:38:28 -0600677 while poffset >= 0:
678 p = self._fdt_obj.get_property_by_offset(poffset)
Simon Glass3def0cf2018-07-06 10:27:20 -0600679 prop = Prop(node, poffset, p.name, p)
Simon Glass7b75b442017-05-27 07:38:28 -0600680 props_dict[prop.name] = prop
681
Simon Glass117f57b2018-07-06 10:27:26 -0600682 poffset = self._fdt_obj.next_property_offset(poffset,
683 QUIET_NOTFOUND)
Simon Glass7b75b442017-05-27 07:38:28 -0600684 return props_dict
685
686 def Invalidate(self):
687 """Mark our offset cache as invalid"""
688 self._cached_offsets = False
689
690 def CheckCache(self):
691 """Refresh the offset cache if needed"""
692 if self._cached_offsets:
693 return
694 self.Refresh()
695 self._cached_offsets = True
696
697 def Refresh(self):
698 """Refresh the offset cache"""
699 self._root.Refresh(0)
700
701 def GetStructOffset(self, offset):
702 """Get the file offset of a given struct offset
703
704 Args:
705 offset: Offset within the 'struct' region of the device tree
706 Returns:
707 Position of @offset within the device tree binary
708 """
Simon Glass117f57b2018-07-06 10:27:26 -0600709 return self._fdt_obj.off_dt_struct() + offset
Simon Glass7b75b442017-05-27 07:38:28 -0600710
711 @classmethod
Simon Glass979ab022017-08-29 14:15:47 -0600712 def Node(self, fdt, parent, offset, name, path):
Simon Glass7b75b442017-05-27 07:38:28 -0600713 """Create a new node
714
715 This is used by Fdt.Scan() to create a new node using the correct
716 class.
717
718 Args:
719 fdt: Fdt object
Simon Glass979ab022017-08-29 14:15:47 -0600720 parent: Parent node, or None if this is the root node
Simon Glass7b75b442017-05-27 07:38:28 -0600721 offset: Offset of node
722 name: Node name
723 path: Full path to node
724 """
Simon Glass979ab022017-08-29 14:15:47 -0600725 node = Node(fdt, parent, offset, name, path)
Simon Glass7b75b442017-05-27 07:38:28 -0600726 return node
Simon Glass99ed4a22017-05-27 07:38:30 -0600727
Simon Glassf6e02492019-07-20 12:24:08 -0600728 def GetFilename(self):
729 """Get the filename of the device tree
730
731 Returns:
732 String filename
733 """
734 return self._fname
735
Simon Glass99ed4a22017-05-27 07:38:30 -0600736def FdtScan(fname):
Simon Glassdfe5f5b2018-07-06 10:27:32 -0600737 """Returns a new Fdt object"""
Simon Glass99ed4a22017-05-27 07:38:30 -0600738 dtb = Fdt(fname)
739 dtb.Scan()
740 return dtb