blob: 36993c29ca4545cc8d47d0ba79db27b02ab8309d [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:
Simon Glass37ba9842021-03-21 18:24:35 +1300106 node: Node containing this property
107 offset: Offset of the property (None if still to be synced)
Simon Glassa06a34b2016-07-25 18:59:04 -0600108 name: Property name (as per the device tree)
109 value: Property value as a string of bytes, or a list of strings of
110 bytes
111 type: Value type
112 """
Simon Glass928527f2019-05-17 22:00:37 -0600113 def __init__(self, node, offset, name, data):
Simon Glassa06a34b2016-07-25 18:59:04 -0600114 self._node = node
115 self._offset = offset
116 self.name = name
117 self.value = None
Simon Glass928527f2019-05-17 22:00:37 -0600118 self.bytes = bytes(data)
Simon Glass37ba9842021-03-21 18:24:35 +1300119 self.dirty = offset is None
Simon Glass928527f2019-05-17 22:00:37 -0600120 if not data:
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700121 self.type = Type.BOOL
Simon Glass7b75b442017-05-27 07:38:28 -0600122 self.value = True
123 return
Simon Glass928527f2019-05-17 22:00:37 -0600124 self.type, self.value = BytesToValue(bytes(data))
Simon Glassa06a34b2016-07-25 18:59:04 -0600125
Simon Glassf9b88b32018-07-06 10:27:29 -0600126 def RefreshOffset(self, poffset):
127 self._offset = poffset
128
Simon Glassc322a852016-07-25 18:59:06 -0600129 def Widen(self, newprop):
130 """Figure out which property type is more general
131
132 Given a current property and a new property, this function returns the
133 one that is less specific as to type. The less specific property will
134 be ble to represent the data in the more specific property. This is
135 used for things like:
136
137 node1 {
138 compatible = "fred";
139 value = <1>;
140 };
141 node1 {
142 compatible = "fred";
143 value = <1 2>;
144 };
145
146 He we want to use an int array for 'value'. The first property
147 suggests that a single int is enough, but the second one shows that
148 it is not. Calling this function with these two propertes would
149 update the current property to be like the second, since it is less
150 specific.
151 """
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700152 if self.type.is_wider_than(newprop.type):
153 if self.type == Type.INT and newprop.type == Type.BYTE:
Simon Glasse144caf2020-10-03 11:31:27 -0600154 if type(self.value) == list:
155 new_value = []
156 for val in self.value:
Simon Glass479dd302020-11-08 20:36:20 -0700157 new_value += [chr(by) for by in val]
Simon Glasse144caf2020-10-03 11:31:27 -0600158 else:
Simon Glass479dd302020-11-08 20:36:20 -0700159 new_value = [chr(by) for by in self.value]
Simon Glasse144caf2020-10-03 11:31:27 -0600160 self.value = new_value
Simon Glassc322a852016-07-25 18:59:06 -0600161 self.type = newprop.type
162
163 if type(newprop.value) == list and type(self.value) != list:
164 self.value = [self.value]
165
166 if type(self.value) == list and len(newprop.value) > len(self.value):
167 val = self.GetEmpty(self.type)
168 while len(self.value) < len(newprop.value):
169 self.value.append(val)
170
Simon Glass2ba98752018-07-06 10:27:24 -0600171 @classmethod
Simon Glassbc1dea32016-07-25 18:59:05 -0600172 def GetEmpty(self, type):
173 """Get an empty / zero value of the given type
174
175 Returns:
176 A single value of the given type
177 """
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700178 if type == Type.BYTE:
Simon Glassbc1dea32016-07-25 18:59:05 -0600179 return chr(0)
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700180 elif type == Type.INT:
Simon Glassaf53f5a2018-09-14 04:57:14 -0600181 return struct.pack('>I', 0);
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700182 elif type == Type.STRING:
Simon Glassbc1dea32016-07-25 18:59:05 -0600183 return ''
184 else:
185 return True
186
Simon Glassbabdbde2016-07-25 18:59:16 -0600187 def GetOffset(self):
188 """Get the offset of a property
189
Simon Glassbabdbde2016-07-25 18:59:16 -0600190 Returns:
Simon Glass7b75b442017-05-27 07:38:28 -0600191 The offset of the property (struct fdt_property) within the file
Simon Glassbabdbde2016-07-25 18:59:16 -0600192 """
Simon Glassf9b88b32018-07-06 10:27:29 -0600193 self._node._fdt.CheckCache()
Simon Glass7b75b442017-05-27 07:38:28 -0600194 return self._node._fdt.GetStructOffset(self._offset)
Simon Glassbabdbde2016-07-25 18:59:16 -0600195
Simon Glassfa80c252018-09-14 04:57:13 -0600196 def SetInt(self, val):
197 """Set the integer value of the property
198
199 The device tree is marked dirty so that the value will be written to
200 the block on the next sync.
201
202 Args:
203 val: Integer value (32-bit, single cell)
204 """
205 self.bytes = struct.pack('>I', val);
Simon Glass41b781d2018-10-01 12:22:49 -0600206 self.value = self.bytes
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700207 self.type = Type.INT
Simon Glassfa80c252018-09-14 04:57:13 -0600208 self.dirty = True
209
Simon Glass64349612018-09-14 04:57:16 -0600210 def SetData(self, bytes):
211 """Set the value of a property as bytes
212
213 Args:
214 bytes: New property value to set
215 """
Simon Glassf6b64812019-05-17 22:00:36 -0600216 self.bytes = bytes
Simon Glass7e6952d2019-05-17 22:00:34 -0600217 self.type, self.value = BytesToValue(bytes)
Simon Glass64349612018-09-14 04:57:16 -0600218 self.dirty = True
219
Simon Glassfa80c252018-09-14 04:57:13 -0600220 def Sync(self, auto_resize=False):
221 """Sync property changes back to the device tree
222
223 This updates the device tree blob with any changes to this property
224 since the last sync.
225
226 Args:
227 auto_resize: Resize the device tree automatically if it does not
228 have enough space for the update
229
230 Raises:
231 FdtException if auto_resize is False and there is not enough space
232 """
Simon Glass37ba9842021-03-21 18:24:35 +1300233 if self.dirty:
Simon Glassfa80c252018-09-14 04:57:13 -0600234 node = self._node
235 fdt_obj = node._fdt._fdt_obj
236 if auto_resize:
237 while fdt_obj.setprop(node.Offset(), self.name, self.bytes,
238 (libfdt.NOSPACE,)) == -libfdt.NOSPACE:
Simon Glassc0639172020-07-09 18:39:44 -0600239 fdt_obj.resize(fdt_obj.totalsize() + 1024 +
240 len(self.bytes))
Simon Glassfa80c252018-09-14 04:57:13 -0600241 fdt_obj.setprop(node.Offset(), self.name, self.bytes)
242 else:
243 fdt_obj.setprop(node.Offset(), self.name, self.bytes)
Simon Glass37ba9842021-03-21 18:24:35 +1300244 self.dirty = False
Simon Glassfa80c252018-09-14 04:57:13 -0600245
246
Simon Glass7b75b442017-05-27 07:38:28 -0600247class Node:
Simon Glassa06a34b2016-07-25 18:59:04 -0600248 """A device tree node
249
250 Properties:
Simon Glass37ba9842021-03-21 18:24:35 +1300251 parent: Parent Node
252 offset: Integer offset in the device tree (None if to be synced)
Simon Glassa06a34b2016-07-25 18:59:04 -0600253 name: Device tree node tname
254 path: Full path to node, along with the node name itself
255 _fdt: Device tree object
256 subnodes: A list of subnodes for this node, each a Node object
257 props: A dict of properties for this node, each a Prop object.
258 Keyed by property name
259 """
Simon Glass979ab022017-08-29 14:15:47 -0600260 def __init__(self, fdt, parent, offset, name, path):
Simon Glassa06a34b2016-07-25 18:59:04 -0600261 self._fdt = fdt
Simon Glass979ab022017-08-29 14:15:47 -0600262 self.parent = parent
Simon Glassa06a34b2016-07-25 18:59:04 -0600263 self._offset = offset
264 self.name = name
265 self.path = path
266 self.subnodes = []
267 self.props = {}
268
Simon Glass94a7c602018-07-17 13:25:46 -0600269 def GetFdt(self):
270 """Get the Fdt object for this node
271
272 Returns:
273 Fdt object
274 """
275 return self._fdt
276
Simon Glass1d858882018-07-17 13:25:41 -0600277 def FindNode(self, name):
Simon Glassf7a2aee2016-07-25 18:59:07 -0600278 """Find a node given its name
279
280 Args:
281 name: Node name to look for
282 Returns:
283 Node object if found, else None
284 """
285 for subnode in self.subnodes:
286 if subnode.name == name:
287 return subnode
288 return None
289
Simon Glass7b75b442017-05-27 07:38:28 -0600290 def Offset(self):
291 """Returns the offset of a node, after checking the cache
Simon Glassf7a2aee2016-07-25 18:59:07 -0600292
Simon Glass7b75b442017-05-27 07:38:28 -0600293 This should be used instead of self._offset directly, to ensure that
294 the cache does not contain invalid offsets.
Simon Glassf7a2aee2016-07-25 18:59:07 -0600295 """
Simon Glass7b75b442017-05-27 07:38:28 -0600296 self._fdt.CheckCache()
297 return self._offset
298
299 def Scan(self):
300 """Scan a node's properties and subnodes
301
302 This fills in the props and subnodes properties, recursively
303 searching into subnodes so that the entire tree is built.
304 """
Simon Glass117f57b2018-07-06 10:27:26 -0600305 fdt_obj = self._fdt._fdt_obj
Simon Glass7b75b442017-05-27 07:38:28 -0600306 self.props = self._fdt.GetProps(self)
Simon Glass117f57b2018-07-06 10:27:26 -0600307 phandle = fdt_obj.get_phandle(self.Offset())
Simon Glass09264e02017-08-29 14:15:52 -0600308 if phandle:
Simon Glass117f57b2018-07-06 10:27:26 -0600309 self._fdt.phandle_to_node[phandle] = self
Simon Glass7b75b442017-05-27 07:38:28 -0600310
Simon Glass117f57b2018-07-06 10:27:26 -0600311 offset = fdt_obj.first_subnode(self.Offset(), QUIET_NOTFOUND)
Simon Glass7b75b442017-05-27 07:38:28 -0600312 while offset >= 0:
313 sep = '' if self.path[-1] == '/' else '/'
Simon Glass117f57b2018-07-06 10:27:26 -0600314 name = fdt_obj.get_name(offset)
Simon Glass7b75b442017-05-27 07:38:28 -0600315 path = self.path + sep + name
Simon Glass979ab022017-08-29 14:15:47 -0600316 node = Node(self._fdt, self, offset, name, path)
Simon Glass7b75b442017-05-27 07:38:28 -0600317 self.subnodes.append(node)
318
319 node.Scan()
Simon Glass117f57b2018-07-06 10:27:26 -0600320 offset = fdt_obj.next_subnode(offset, QUIET_NOTFOUND)
Simon Glass7b75b442017-05-27 07:38:28 -0600321
322 def Refresh(self, my_offset):
323 """Fix up the _offset for each node, recursively
324
325 Note: This does not take account of property offsets - these will not
326 be updated.
327 """
Simon Glass96066242018-07-06 10:27:27 -0600328 fdt_obj = self._fdt._fdt_obj
Simon Glass7b75b442017-05-27 07:38:28 -0600329 if self._offset != my_offset:
Simon Glass7b75b442017-05-27 07:38:28 -0600330 self._offset = my_offset
Simon Glass96066242018-07-06 10:27:27 -0600331 offset = fdt_obj.first_subnode(self._offset, QUIET_NOTFOUND)
Simon Glass7b75b442017-05-27 07:38:28 -0600332 for subnode in self.subnodes:
Simon Glassf9b88b32018-07-06 10:27:29 -0600333 if subnode.name != fdt_obj.get_name(offset):
334 raise ValueError('Internal error, node name mismatch %s != %s' %
335 (subnode.name, fdt_obj.get_name(offset)))
Simon Glass7b75b442017-05-27 07:38:28 -0600336 subnode.Refresh(offset)
Simon Glass96066242018-07-06 10:27:27 -0600337 offset = fdt_obj.next_subnode(offset, QUIET_NOTFOUND)
Simon Glassf9b88b32018-07-06 10:27:29 -0600338 if offset != -libfdt.FDT_ERR_NOTFOUND:
339 raise ValueError('Internal error, offset == %d' % offset)
340
341 poffset = fdt_obj.first_property_offset(self._offset, QUIET_NOTFOUND)
342 while poffset >= 0:
343 p = fdt_obj.get_property_by_offset(poffset)
344 prop = self.props.get(p.name)
345 if not prop:
Simon Glassacd98612021-03-21 18:24:34 +1300346 raise ValueError("Internal error, node '%s' property '%s' missing, "
347 'offset %d' % (self.path, p.name, poffset))
Simon Glassf9b88b32018-07-06 10:27:29 -0600348 prop.RefreshOffset(poffset)
349 poffset = fdt_obj.next_property_offset(poffset, QUIET_NOTFOUND)
Simon Glassf7a2aee2016-07-25 18:59:07 -0600350
Simon Glass2a70d892016-07-25 18:59:14 -0600351 def DeleteProp(self, prop_name):
352 """Delete a property of a node
353
Simon Glass7b75b442017-05-27 07:38:28 -0600354 The property is deleted and the offset cache is invalidated.
Simon Glass2a70d892016-07-25 18:59:14 -0600355
356 Args:
357 prop_name: Name of the property to delete
Simon Glass7b75b442017-05-27 07:38:28 -0600358 Raises:
359 ValueError if the property does not exist
Simon Glass2a70d892016-07-25 18:59:14 -0600360 """
Simon Glass96066242018-07-06 10:27:27 -0600361 CheckErr(self._fdt._fdt_obj.delprop(self.Offset(), prop_name),
Simon Glass7b75b442017-05-27 07:38:28 -0600362 "Node '%s': delete property: '%s'" % (self.path, prop_name))
363 del self.props[prop_name]
364 self._fdt.Invalidate()
Simon Glass2a70d892016-07-25 18:59:14 -0600365
Simon Glass116adec2018-07-06 10:27:38 -0600366 def AddZeroProp(self, prop_name):
367 """Add a new property to the device tree with an integer value of 0.
368
369 Args:
370 prop_name: Name of property
371 """
Simon Glass194b8d52019-05-17 22:00:33 -0600372 self.props[prop_name] = Prop(self, None, prop_name,
373 tools.GetBytes(0, 4))
Simon Glass116adec2018-07-06 10:27:38 -0600374
Simon Glass64349612018-09-14 04:57:16 -0600375 def AddEmptyProp(self, prop_name, len):
376 """Add a property with a fixed data size, for filling in later
377
378 The device tree is marked dirty so that the value will be written to
379 the blob on the next sync.
380
381 Args:
382 prop_name: Name of property
383 len: Length of data in property
384 """
Simon Glass194b8d52019-05-17 22:00:33 -0600385 value = tools.GetBytes(0, len)
Simon Glass64349612018-09-14 04:57:16 -0600386 self.props[prop_name] = Prop(self, None, prop_name, value)
387
Simon Glassd9dad102019-07-20 12:23:37 -0600388 def _CheckProp(self, prop_name):
389 """Check if a property is present
390
391 Args:
392 prop_name: Name of property
393
394 Returns:
395 self
396
397 Raises:
398 ValueError if the property is missing
399 """
400 if prop_name not in self.props:
401 raise ValueError("Fdt '%s', node '%s': Missing property '%s'" %
402 (self._fdt._fname, self.path, prop_name))
403 return self
404
Simon Glass116adec2018-07-06 10:27:38 -0600405 def SetInt(self, prop_name, val):
406 """Update an integer property int the device tree.
407
408 This is not allowed to change the size of the FDT.
409
Simon Glass64349612018-09-14 04:57:16 -0600410 The device tree is marked dirty so that the value will be written to
411 the blob on the next sync.
412
Simon Glass116adec2018-07-06 10:27:38 -0600413 Args:
414 prop_name: Name of property
415 val: Value to set
416 """
Simon Glassd9dad102019-07-20 12:23:37 -0600417 self._CheckProp(prop_name).props[prop_name].SetInt(val)
Simon Glassfa80c252018-09-14 04:57:13 -0600418
Simon Glass64349612018-09-14 04:57:16 -0600419 def SetData(self, prop_name, val):
420 """Set the data value of a property
421
422 The device tree is marked dirty so that the value will be written to
423 the blob on the next sync.
424
425 Args:
426 prop_name: Name of property to set
427 val: Data value to set
428 """
Simon Glassd9dad102019-07-20 12:23:37 -0600429 self._CheckProp(prop_name).props[prop_name].SetData(val)
Simon Glass64349612018-09-14 04:57:16 -0600430
431 def SetString(self, prop_name, val):
432 """Set the string value of a property
433
434 The device tree is marked dirty so that the value will be written to
435 the blob on the next sync.
436
437 Args:
438 prop_name: Name of property to set
439 val: String value to set (will be \0-terminated in DT)
440 """
Simon Glassa90df2b2019-10-31 07:43:04 -0600441 if type(val) == str:
442 val = val.encode('utf-8')
Simon Glassd9dad102019-07-20 12:23:37 -0600443 self._CheckProp(prop_name).props[prop_name].SetData(val + b'\0')
Simon Glass64349612018-09-14 04:57:16 -0600444
Simon Glassc0639172020-07-09 18:39:44 -0600445 def AddData(self, prop_name, val):
446 """Add a new property to a node
447
448 The device tree is marked dirty so that the value will be written to
449 the blob on the next sync.
450
451 Args:
452 prop_name: Name of property to add
453 val: Bytes value of property
454 """
455 self.props[prop_name] = Prop(self, None, prop_name, val)
456
Simon Glass64349612018-09-14 04:57:16 -0600457 def AddString(self, prop_name, val):
458 """Add a new string property to a node
459
460 The device tree is marked dirty so that the value will be written to
461 the blob on the next sync.
462
463 Args:
464 prop_name: Name of property to add
465 val: String value of property
466 """
Simon Glass9fc6ebd2021-01-06 21:35:10 -0700467 val = bytes(val, 'utf-8')
Simon Glassc0639172020-07-09 18:39:44 -0600468 self.AddData(prop_name, val + b'\0')
Simon Glass64349612018-09-14 04:57:16 -0600469
Simon Glass6eb99322021-01-06 21:35:18 -0700470 def AddInt(self, prop_name, val):
471 """Add a new integer property to a node
472
473 The device tree is marked dirty so that the value will be written to
474 the blob on the next sync.
475
476 Args:
477 prop_name: Name of property to add
478 val: Integer value of property
479 """
480 self.AddData(prop_name, struct.pack('>I', val))
481
Simon Glasse21c27a2018-09-14 04:57:15 -0600482 def AddSubnode(self, name):
Simon Glass64349612018-09-14 04:57:16 -0600483 """Add a new subnode to the node
484
485 Args:
486 name: name of node to add
487
488 Returns:
489 New subnode that was created
490 """
Simon Glasse21c27a2018-09-14 04:57:15 -0600491 path = self.path + '/' + name
492 subnode = Node(self._fdt, self, None, name, path)
493 self.subnodes.append(subnode)
494 return subnode
495
Simon Glassfa80c252018-09-14 04:57:13 -0600496 def Sync(self, auto_resize=False):
497 """Sync node changes back to the device tree
498
499 This updates the device tree blob with any changes to this node and its
500 subnodes since the last sync.
501
502 Args:
503 auto_resize: Resize the device tree automatically if it does not
504 have enough space for the update
505
506 Raises:
507 FdtException if auto_resize is False and there is not enough space
508 """
Simon Glasse21c27a2018-09-14 04:57:15 -0600509 if self._offset is None:
510 # The subnode doesn't exist yet, so add it
511 fdt_obj = self._fdt._fdt_obj
512 if auto_resize:
513 while True:
514 offset = fdt_obj.add_subnode(self.parent._offset, self.name,
515 (libfdt.NOSPACE,))
516 if offset != -libfdt.NOSPACE:
517 break
518 fdt_obj.resize(fdt_obj.totalsize() + 1024)
519 else:
520 offset = fdt_obj.add_subnode(self.parent._offset, self.name)
521 self._offset = offset
522
Simon Glassfa80c252018-09-14 04:57:13 -0600523 # Sync subnodes in reverse so that we don't disturb node offsets for
524 # nodes that are earlier in the DT. This avoids an O(n^2) rescan of
525 # node offsets.
526 for node in reversed(self.subnodes):
527 node.Sync(auto_resize)
528
529 # Sync properties now, whose offsets should not have been disturbed.
530 # We do this after subnodes, since this disturbs the offsets of these
Simon Glass63518052019-05-17 22:00:38 -0600531 # properties. Note that new properties will have an offset of None here,
532 # which Python 3 cannot sort against int. So use a large value instead
533 # to ensure that the new properties are added first.
534 prop_list = sorted(self.props.values(),
535 key=lambda prop: prop._offset or 1 << 31,
Simon Glassfa80c252018-09-14 04:57:13 -0600536 reverse=True)
537 for prop in prop_list:
538 prop.Sync(auto_resize)
Simon Glass116adec2018-07-06 10:27:38 -0600539
540
Simon Glassa06a34b2016-07-25 18:59:04 -0600541class Fdt:
Simon Glass7b75b442017-05-27 07:38:28 -0600542 """Provides simple access to a flat device tree blob using libfdts.
Simon Glassa06a34b2016-07-25 18:59:04 -0600543
544 Properties:
545 fname: Filename of fdt
546 _root: Root of device tree (a Node object)
Simon Glass880e9ee2019-07-20 12:23:38 -0600547 name: Helpful name for this Fdt for the user (useful when creating the
548 DT from data rather than a file)
Simon Glassa06a34b2016-07-25 18:59:04 -0600549 """
550 def __init__(self, fname):
551 self._fname = fname
Simon Glass7b75b442017-05-27 07:38:28 -0600552 self._cached_offsets = False
Simon Glass09264e02017-08-29 14:15:52 -0600553 self.phandle_to_node = {}
Simon Glass880e9ee2019-07-20 12:23:38 -0600554 self.name = ''
Simon Glass7b75b442017-05-27 07:38:28 -0600555 if self._fname:
Simon Glass880e9ee2019-07-20 12:23:38 -0600556 self.name = self._fname
Simon Glass7b75b442017-05-27 07:38:28 -0600557 self._fname = fdt_util.EnsureCompiled(self._fname)
558
Simon Glass3e4b51e2019-05-14 15:53:43 -0600559 with open(self._fname, 'rb') as fd:
Simon Glass96066242018-07-06 10:27:27 -0600560 self._fdt_obj = libfdt.Fdt(fd.read())
Simon Glassf7a2aee2016-07-25 18:59:07 -0600561
Simon Glass746aee32018-09-14 04:57:17 -0600562 @staticmethod
Simon Glass880e9ee2019-07-20 12:23:38 -0600563 def FromData(data, name=''):
Simon Glass746aee32018-09-14 04:57:17 -0600564 """Create a new Fdt object from the given data
565
566 Args:
567 data: Device-tree data blob
Simon Glass880e9ee2019-07-20 12:23:38 -0600568 name: Helpful name for this Fdt for the user
Simon Glass746aee32018-09-14 04:57:17 -0600569
570 Returns:
571 Fdt object containing the data
572 """
573 fdt = Fdt(None)
Simon Glassf6b64812019-05-17 22:00:36 -0600574 fdt._fdt_obj = libfdt.Fdt(bytes(data))
Simon Glass880e9ee2019-07-20 12:23:38 -0600575 fdt.name = name
Simon Glass746aee32018-09-14 04:57:17 -0600576 return fdt
577
Simon Glass94a7c602018-07-17 13:25:46 -0600578 def LookupPhandle(self, phandle):
579 """Look up a phandle
580
581 Args:
582 phandle: Phandle to look up (int)
583
584 Returns:
585 Node object the phandle points to
586 """
587 return self.phandle_to_node.get(phandle)
588
Simon Glassf7a2aee2016-07-25 18:59:07 -0600589 def Scan(self, root='/'):
590 """Scan a device tree, building up a tree of Node objects
591
592 This fills in the self._root property
593
594 Args:
595 root: Ignored
596
597 TODO(sjg@chromium.org): Implement the 'root' parameter
598 """
Simon Glassf9b88b32018-07-06 10:27:29 -0600599 self._cached_offsets = True
Simon Glass979ab022017-08-29 14:15:47 -0600600 self._root = self.Node(self, None, 0, '/', '/')
Simon Glassf7a2aee2016-07-25 18:59:07 -0600601 self._root.Scan()
602
603 def GetRoot(self):
604 """Get the root Node of the device tree
605
606 Returns:
607 The root Node object
608 """
609 return self._root
610
611 def GetNode(self, path):
612 """Look up a node from its path
613
614 Args:
615 path: Path to look up, e.g. '/microcode/update@0'
616 Returns:
617 Node object, or None if not found
618 """
619 node = self._root
Simon Glassb9066ff2018-07-06 10:27:30 -0600620 parts = path.split('/')
621 if len(parts) < 2:
622 return None
Simon Glasse44bc832019-07-20 12:23:39 -0600623 if len(parts) == 2 and parts[1] == '':
624 return node
Simon Glassb9066ff2018-07-06 10:27:30 -0600625 for part in parts[1:]:
Simon Glass1d858882018-07-17 13:25:41 -0600626 node = node.FindNode(part)
Simon Glassf7a2aee2016-07-25 18:59:07 -0600627 if not node:
628 return None
629 return node
630
Simon Glassda5f7492016-07-25 18:59:15 -0600631 def Flush(self):
632 """Flush device tree changes back to the file
633
634 If the device tree has changed in memory, write it back to the file.
Simon Glassda5f7492016-07-25 18:59:15 -0600635 """
Simon Glass7b75b442017-05-27 07:38:28 -0600636 with open(self._fname, 'wb') as fd:
Simon Glass96066242018-07-06 10:27:27 -0600637 fd.write(self._fdt_obj.as_bytearray())
Simon Glassda5f7492016-07-25 18:59:15 -0600638
Simon Glassfa80c252018-09-14 04:57:13 -0600639 def Sync(self, auto_resize=False):
640 """Make sure any DT changes are written to the blob
641
642 Args:
643 auto_resize: Resize the device tree automatically if it does not
644 have enough space for the update
645
646 Raises:
647 FdtException if auto_resize is False and there is not enough space
648 """
649 self._root.Sync(auto_resize)
650 self.Invalidate()
651
Simon Glassda5f7492016-07-25 18:59:15 -0600652 def Pack(self):
653 """Pack the device tree down to its minimum size
654
655 When nodes and properties shrink or are deleted, wasted space can
Simon Glass7b75b442017-05-27 07:38:28 -0600656 build up in the device tree binary.
Simon Glassda5f7492016-07-25 18:59:15 -0600657 """
Simon Glass117f57b2018-07-06 10:27:26 -0600658 CheckErr(self._fdt_obj.pack(), 'pack')
659 self.Invalidate()
Simon Glass7b75b442017-05-27 07:38:28 -0600660
Simon Glass96066242018-07-06 10:27:27 -0600661 def GetContents(self):
Simon Glass7b75b442017-05-27 07:38:28 -0600662 """Get the contents of the FDT
663
664 Returns:
665 The FDT contents as a string of bytes
666 """
Simon Glassf6b64812019-05-17 22:00:36 -0600667 return bytes(self._fdt_obj.as_bytearray())
Simon Glass7b75b442017-05-27 07:38:28 -0600668
Simon Glass2ba98752018-07-06 10:27:24 -0600669 def GetFdtObj(self):
670 """Get the contents of the FDT
671
672 Returns:
673 The FDT contents as a libfdt.Fdt object
674 """
675 return self._fdt_obj
676
Simon Glass7b75b442017-05-27 07:38:28 -0600677 def GetProps(self, node):
678 """Get all properties from a node.
679
680 Args:
681 node: Full path to node name to look in.
682
683 Returns:
684 A dictionary containing all the properties, indexed by node name.
685 The entries are Prop objects.
686
687 Raises:
688 ValueError: if the node does not exist.
689 """
690 props_dict = {}
Simon Glass117f57b2018-07-06 10:27:26 -0600691 poffset = self._fdt_obj.first_property_offset(node._offset,
692 QUIET_NOTFOUND)
Simon Glass7b75b442017-05-27 07:38:28 -0600693 while poffset >= 0:
694 p = self._fdt_obj.get_property_by_offset(poffset)
Simon Glass3def0cf2018-07-06 10:27:20 -0600695 prop = Prop(node, poffset, p.name, p)
Simon Glass7b75b442017-05-27 07:38:28 -0600696 props_dict[prop.name] = prop
697
Simon Glass117f57b2018-07-06 10:27:26 -0600698 poffset = self._fdt_obj.next_property_offset(poffset,
699 QUIET_NOTFOUND)
Simon Glass7b75b442017-05-27 07:38:28 -0600700 return props_dict
701
702 def Invalidate(self):
703 """Mark our offset cache as invalid"""
704 self._cached_offsets = False
705
706 def CheckCache(self):
707 """Refresh the offset cache if needed"""
708 if self._cached_offsets:
709 return
710 self.Refresh()
711 self._cached_offsets = True
712
713 def Refresh(self):
714 """Refresh the offset cache"""
715 self._root.Refresh(0)
716
717 def GetStructOffset(self, offset):
718 """Get the file offset of a given struct offset
719
720 Args:
721 offset: Offset within the 'struct' region of the device tree
722 Returns:
723 Position of @offset within the device tree binary
724 """
Simon Glass117f57b2018-07-06 10:27:26 -0600725 return self._fdt_obj.off_dt_struct() + offset
Simon Glass7b75b442017-05-27 07:38:28 -0600726
727 @classmethod
Simon Glass979ab022017-08-29 14:15:47 -0600728 def Node(self, fdt, parent, offset, name, path):
Simon Glass7b75b442017-05-27 07:38:28 -0600729 """Create a new node
730
731 This is used by Fdt.Scan() to create a new node using the correct
732 class.
733
734 Args:
735 fdt: Fdt object
Simon Glass979ab022017-08-29 14:15:47 -0600736 parent: Parent node, or None if this is the root node
Simon Glass7b75b442017-05-27 07:38:28 -0600737 offset: Offset of node
738 name: Node name
739 path: Full path to node
740 """
Simon Glass979ab022017-08-29 14:15:47 -0600741 node = Node(fdt, parent, offset, name, path)
Simon Glass7b75b442017-05-27 07:38:28 -0600742 return node
Simon Glass99ed4a22017-05-27 07:38:30 -0600743
Simon Glassf6e02492019-07-20 12:24:08 -0600744 def GetFilename(self):
745 """Get the filename of the device tree
746
747 Returns:
748 String filename
749 """
750 return self._fname
751
Simon Glass99ed4a22017-05-27 07:38:30 -0600752def FdtScan(fname):
Simon Glassdfe5f5b2018-07-06 10:27:32 -0600753 """Returns a new Fdt object"""
Simon Glass99ed4a22017-05-27 07:38:30 -0600754 dtb = Fdt(fname)
755 dtb.Scan()
756 return dtb