blob: d058c59e92781df75ede38f6fba7ace4b8f88ffd [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
8import struct
9import sys
10
Simon Glassbf776672020-04-17 18:09:04 -060011from dtoc import fdt_util
Simon Glass7b75b442017-05-27 07:38:28 -060012import libfdt
Simon Glass117f57b2018-07-06 10:27:26 -060013from libfdt import QUIET_NOTFOUND
Simon Glassbf776672020-04-17 18:09:04 -060014from patman import tools
Simon Glassa06a34b2016-07-25 18:59:04 -060015
16# This deals with a device tree, presenting it as an assortment of Node and
17# Prop objects, representing nodes and properties, respectively. This file
Simon Glass99ed4a22017-05-27 07:38:30 -060018# contains the base classes and defines the high-level API. You can use
19# FdtScan() as a convenience function to create and scan an Fdt.
Simon Glass7b75b442017-05-27 07:38:28 -060020
21# This implementation uses a libfdt Python library to access the device tree,
22# so it is fairly efficient.
Simon Glassa06a34b2016-07-25 18:59:04 -060023
Simon Glassbc1dea32016-07-25 18:59:05 -060024# A list of types we support
Simon Glassfbdfd222017-08-29 14:15:48 -060025(TYPE_BYTE, TYPE_INT, TYPE_STRING, TYPE_BOOL, TYPE_INT64) = range(5)
Simon Glassbc1dea32016-07-25 18:59:05 -060026
Simon Glassa06a34b2016-07-25 18:59:04 -060027def CheckErr(errnum, msg):
28 if errnum:
29 raise ValueError('Error %d: %s: %s' %
30 (errnum, libfdt.fdt_strerror(errnum), msg))
31
Simon Glass7e6952d2019-05-17 22:00:34 -060032
Simon Glass2b6ed5e2019-05-17 22:00:35 -060033def BytesToValue(data):
Simon Glass7e6952d2019-05-17 22:00:34 -060034 """Converts a string of bytes into a type and value
35
36 Args:
Simon Glass2b6ed5e2019-05-17 22:00:35 -060037 A bytes value (which on Python 2 is an alias for str)
Simon Glass7e6952d2019-05-17 22:00:34 -060038
39 Return:
40 A tuple:
41 Type of data
42 Data, either a single element or a list of elements. Each element
43 is one of:
Simon Glass2b6ed5e2019-05-17 22:00:35 -060044 TYPE_STRING: str/bytes value from the property
45 TYPE_INT: a byte-swapped integer stored as a 4-byte str/bytes
46 TYPE_BYTE: a byte stored as a single-byte str/bytes
Simon Glass7e6952d2019-05-17 22:00:34 -060047 """
Simon Glass2b6ed5e2019-05-17 22:00:35 -060048 data = bytes(data)
49 size = len(data)
50 strings = data.split(b'\0')
Simon Glass7e6952d2019-05-17 22:00:34 -060051 is_string = True
52 count = len(strings) - 1
Simon Glass2b6ed5e2019-05-17 22:00:35 -060053 if count > 0 and not len(strings[-1]):
Simon Glass7e6952d2019-05-17 22:00:34 -060054 for string in strings[:-1]:
55 if not string:
56 is_string = False
57 break
58 for ch in string:
Simon Glass2b6ed5e2019-05-17 22:00:35 -060059 if ch < 32 or ch > 127:
Simon Glass7e6952d2019-05-17 22:00:34 -060060 is_string = False
61 break
62 else:
63 is_string = False
64 if is_string:
Simon Glass2b6ed5e2019-05-17 22:00:35 -060065 if count == 1:
Simon Glassa90df2b2019-10-31 07:43:04 -060066 return TYPE_STRING, strings[0].decode()
Simon Glass7e6952d2019-05-17 22:00:34 -060067 else:
Simon Glassa90df2b2019-10-31 07:43:04 -060068 return TYPE_STRING, [s.decode() for s in strings[:-1]]
Simon Glass7e6952d2019-05-17 22:00:34 -060069 if size % 4:
70 if size == 1:
Simon Glass2b6ed5e2019-05-17 22:00:35 -060071 return TYPE_BYTE, tools.ToChar(data[0])
Simon Glass7e6952d2019-05-17 22:00:34 -060072 else:
Simon Glass2b6ed5e2019-05-17 22:00:35 -060073 return TYPE_BYTE, [tools.ToChar(ch) for ch in list(data)]
Simon Glass7e6952d2019-05-17 22:00:34 -060074 val = []
75 for i in range(0, size, 4):
Simon Glass2b6ed5e2019-05-17 22:00:35 -060076 val.append(data[i:i + 4])
Simon Glass7e6952d2019-05-17 22:00:34 -060077 if size == 4:
78 return TYPE_INT, val[0]
79 else:
80 return TYPE_INT, val
81
82
Simon Glass7b75b442017-05-27 07:38:28 -060083class Prop:
Simon Glassa06a34b2016-07-25 18:59:04 -060084 """A device tree property
85
86 Properties:
87 name: Property name (as per the device tree)
88 value: Property value as a string of bytes, or a list of strings of
89 bytes
90 type: Value type
91 """
Simon Glass928527f2019-05-17 22:00:37 -060092 def __init__(self, node, offset, name, data):
Simon Glassa06a34b2016-07-25 18:59:04 -060093 self._node = node
94 self._offset = offset
95 self.name = name
96 self.value = None
Simon Glass928527f2019-05-17 22:00:37 -060097 self.bytes = bytes(data)
Simon Glassfa80c252018-09-14 04:57:13 -060098 self.dirty = False
Simon Glass928527f2019-05-17 22:00:37 -060099 if not data:
Simon Glass7b75b442017-05-27 07:38:28 -0600100 self.type = TYPE_BOOL
101 self.value = True
102 return
Simon Glass928527f2019-05-17 22:00:37 -0600103 self.type, self.value = BytesToValue(bytes(data))
Simon Glassa06a34b2016-07-25 18:59:04 -0600104
Simon Glassf9b88b32018-07-06 10:27:29 -0600105 def RefreshOffset(self, poffset):
106 self._offset = poffset
107
Simon Glassc322a852016-07-25 18:59:06 -0600108 def Widen(self, newprop):
109 """Figure out which property type is more general
110
111 Given a current property and a new property, this function returns the
112 one that is less specific as to type. The less specific property will
113 be ble to represent the data in the more specific property. This is
114 used for things like:
115
116 node1 {
117 compatible = "fred";
118 value = <1>;
119 };
120 node1 {
121 compatible = "fred";
122 value = <1 2>;
123 };
124
125 He we want to use an int array for 'value'. The first property
126 suggests that a single int is enough, but the second one shows that
127 it is not. Calling this function with these two propertes would
128 update the current property to be like the second, since it is less
129 specific.
130 """
131 if newprop.type < self.type:
132 self.type = newprop.type
133
134 if type(newprop.value) == list and type(self.value) != list:
135 self.value = [self.value]
136
137 if type(self.value) == list and len(newprop.value) > len(self.value):
138 val = self.GetEmpty(self.type)
139 while len(self.value) < len(newprop.value):
140 self.value.append(val)
141
Simon Glass2ba98752018-07-06 10:27:24 -0600142 @classmethod
Simon Glassbc1dea32016-07-25 18:59:05 -0600143 def GetEmpty(self, type):
144 """Get an empty / zero value of the given type
145
146 Returns:
147 A single value of the given type
148 """
149 if type == TYPE_BYTE:
150 return chr(0)
151 elif type == TYPE_INT:
Simon Glassaf53f5a2018-09-14 04:57:14 -0600152 return struct.pack('>I', 0);
Simon Glassbc1dea32016-07-25 18:59:05 -0600153 elif type == TYPE_STRING:
154 return ''
155 else:
156 return True
157
Simon Glassbabdbde2016-07-25 18:59:16 -0600158 def GetOffset(self):
159 """Get the offset of a property
160
Simon Glassbabdbde2016-07-25 18:59:16 -0600161 Returns:
Simon Glass7b75b442017-05-27 07:38:28 -0600162 The offset of the property (struct fdt_property) within the file
Simon Glassbabdbde2016-07-25 18:59:16 -0600163 """
Simon Glassf9b88b32018-07-06 10:27:29 -0600164 self._node._fdt.CheckCache()
Simon Glass7b75b442017-05-27 07:38:28 -0600165 return self._node._fdt.GetStructOffset(self._offset)
Simon Glassbabdbde2016-07-25 18:59:16 -0600166
Simon Glassfa80c252018-09-14 04:57:13 -0600167 def SetInt(self, val):
168 """Set the integer value of the property
169
170 The device tree is marked dirty so that the value will be written to
171 the block on the next sync.
172
173 Args:
174 val: Integer value (32-bit, single cell)
175 """
176 self.bytes = struct.pack('>I', val);
Simon Glass41b781d2018-10-01 12:22:49 -0600177 self.value = self.bytes
Simon Glassfa80c252018-09-14 04:57:13 -0600178 self.type = TYPE_INT
179 self.dirty = True
180
Simon Glass64349612018-09-14 04:57:16 -0600181 def SetData(self, bytes):
182 """Set the value of a property as bytes
183
184 Args:
185 bytes: New property value to set
186 """
Simon Glassf6b64812019-05-17 22:00:36 -0600187 self.bytes = bytes
Simon Glass7e6952d2019-05-17 22:00:34 -0600188 self.type, self.value = BytesToValue(bytes)
Simon Glass64349612018-09-14 04:57:16 -0600189 self.dirty = True
190
Simon Glassfa80c252018-09-14 04:57:13 -0600191 def Sync(self, auto_resize=False):
192 """Sync property changes back to the device tree
193
194 This updates the device tree blob with any changes to this property
195 since the last sync.
196
197 Args:
198 auto_resize: Resize the device tree automatically if it does not
199 have enough space for the update
200
201 Raises:
202 FdtException if auto_resize is False and there is not enough space
203 """
204 if self._offset is None or self.dirty:
205 node = self._node
206 fdt_obj = node._fdt._fdt_obj
207 if auto_resize:
208 while fdt_obj.setprop(node.Offset(), self.name, self.bytes,
209 (libfdt.NOSPACE,)) == -libfdt.NOSPACE:
Simon Glassc0639172020-07-09 18:39:44 -0600210 fdt_obj.resize(fdt_obj.totalsize() + 1024 +
211 len(self.bytes))
Simon Glassfa80c252018-09-14 04:57:13 -0600212 fdt_obj.setprop(node.Offset(), self.name, self.bytes)
213 else:
214 fdt_obj.setprop(node.Offset(), self.name, self.bytes)
215
216
Simon Glass7b75b442017-05-27 07:38:28 -0600217class Node:
Simon Glassa06a34b2016-07-25 18:59:04 -0600218 """A device tree node
219
220 Properties:
221 offset: Integer offset in the device tree
222 name: Device tree node tname
223 path: Full path to node, along with the node name itself
224 _fdt: Device tree object
225 subnodes: A list of subnodes for this node, each a Node object
226 props: A dict of properties for this node, each a Prop object.
227 Keyed by property name
228 """
Simon Glass979ab022017-08-29 14:15:47 -0600229 def __init__(self, fdt, parent, offset, name, path):
Simon Glassa06a34b2016-07-25 18:59:04 -0600230 self._fdt = fdt
Simon Glass979ab022017-08-29 14:15:47 -0600231 self.parent = parent
Simon Glassa06a34b2016-07-25 18:59:04 -0600232 self._offset = offset
233 self.name = name
234 self.path = path
235 self.subnodes = []
236 self.props = {}
237
Simon Glass94a7c602018-07-17 13:25:46 -0600238 def GetFdt(self):
239 """Get the Fdt object for this node
240
241 Returns:
242 Fdt object
243 """
244 return self._fdt
245
Simon Glass1d858882018-07-17 13:25:41 -0600246 def FindNode(self, name):
Simon Glassf7a2aee2016-07-25 18:59:07 -0600247 """Find a node given its name
248
249 Args:
250 name: Node name to look for
251 Returns:
252 Node object if found, else None
253 """
254 for subnode in self.subnodes:
255 if subnode.name == name:
256 return subnode
257 return None
258
Simon Glass7b75b442017-05-27 07:38:28 -0600259 def Offset(self):
260 """Returns the offset of a node, after checking the cache
Simon Glassf7a2aee2016-07-25 18:59:07 -0600261
Simon Glass7b75b442017-05-27 07:38:28 -0600262 This should be used instead of self._offset directly, to ensure that
263 the cache does not contain invalid offsets.
Simon Glassf7a2aee2016-07-25 18:59:07 -0600264 """
Simon Glass7b75b442017-05-27 07:38:28 -0600265 self._fdt.CheckCache()
266 return self._offset
267
268 def Scan(self):
269 """Scan a node's properties and subnodes
270
271 This fills in the props and subnodes properties, recursively
272 searching into subnodes so that the entire tree is built.
273 """
Simon Glass117f57b2018-07-06 10:27:26 -0600274 fdt_obj = self._fdt._fdt_obj
Simon Glass7b75b442017-05-27 07:38:28 -0600275 self.props = self._fdt.GetProps(self)
Simon Glass117f57b2018-07-06 10:27:26 -0600276 phandle = fdt_obj.get_phandle(self.Offset())
Simon Glass09264e02017-08-29 14:15:52 -0600277 if phandle:
Simon Glass117f57b2018-07-06 10:27:26 -0600278 self._fdt.phandle_to_node[phandle] = self
Simon Glass7b75b442017-05-27 07:38:28 -0600279
Simon Glass117f57b2018-07-06 10:27:26 -0600280 offset = fdt_obj.first_subnode(self.Offset(), QUIET_NOTFOUND)
Simon Glass7b75b442017-05-27 07:38:28 -0600281 while offset >= 0:
282 sep = '' if self.path[-1] == '/' else '/'
Simon Glass117f57b2018-07-06 10:27:26 -0600283 name = fdt_obj.get_name(offset)
Simon Glass7b75b442017-05-27 07:38:28 -0600284 path = self.path + sep + name
Simon Glass979ab022017-08-29 14:15:47 -0600285 node = Node(self._fdt, self, offset, name, path)
Simon Glass7b75b442017-05-27 07:38:28 -0600286 self.subnodes.append(node)
287
288 node.Scan()
Simon Glass117f57b2018-07-06 10:27:26 -0600289 offset = fdt_obj.next_subnode(offset, QUIET_NOTFOUND)
Simon Glass7b75b442017-05-27 07:38:28 -0600290
291 def Refresh(self, my_offset):
292 """Fix up the _offset for each node, recursively
293
294 Note: This does not take account of property offsets - these will not
295 be updated.
296 """
Simon Glass96066242018-07-06 10:27:27 -0600297 fdt_obj = self._fdt._fdt_obj
Simon Glass7b75b442017-05-27 07:38:28 -0600298 if self._offset != my_offset:
Simon Glass7b75b442017-05-27 07:38:28 -0600299 self._offset = my_offset
Simon Glass96066242018-07-06 10:27:27 -0600300 offset = fdt_obj.first_subnode(self._offset, QUIET_NOTFOUND)
Simon Glass7b75b442017-05-27 07:38:28 -0600301 for subnode in self.subnodes:
Simon Glassf9b88b32018-07-06 10:27:29 -0600302 if subnode.name != fdt_obj.get_name(offset):
303 raise ValueError('Internal error, node name mismatch %s != %s' %
304 (subnode.name, fdt_obj.get_name(offset)))
Simon Glass7b75b442017-05-27 07:38:28 -0600305 subnode.Refresh(offset)
Simon Glass96066242018-07-06 10:27:27 -0600306 offset = fdt_obj.next_subnode(offset, QUIET_NOTFOUND)
Simon Glassf9b88b32018-07-06 10:27:29 -0600307 if offset != -libfdt.FDT_ERR_NOTFOUND:
308 raise ValueError('Internal error, offset == %d' % offset)
309
310 poffset = fdt_obj.first_property_offset(self._offset, QUIET_NOTFOUND)
311 while poffset >= 0:
312 p = fdt_obj.get_property_by_offset(poffset)
313 prop = self.props.get(p.name)
314 if not prop:
315 raise ValueError("Internal error, property '%s' missing, "
316 'offset %d' % (p.name, poffset))
317 prop.RefreshOffset(poffset)
318 poffset = fdt_obj.next_property_offset(poffset, QUIET_NOTFOUND)
Simon Glassf7a2aee2016-07-25 18:59:07 -0600319
Simon Glass2a70d892016-07-25 18:59:14 -0600320 def DeleteProp(self, prop_name):
321 """Delete a property of a node
322
Simon Glass7b75b442017-05-27 07:38:28 -0600323 The property is deleted and the offset cache is invalidated.
Simon Glass2a70d892016-07-25 18:59:14 -0600324
325 Args:
326 prop_name: Name of the property to delete
Simon Glass7b75b442017-05-27 07:38:28 -0600327 Raises:
328 ValueError if the property does not exist
Simon Glass2a70d892016-07-25 18:59:14 -0600329 """
Simon Glass96066242018-07-06 10:27:27 -0600330 CheckErr(self._fdt._fdt_obj.delprop(self.Offset(), prop_name),
Simon Glass7b75b442017-05-27 07:38:28 -0600331 "Node '%s': delete property: '%s'" % (self.path, prop_name))
332 del self.props[prop_name]
333 self._fdt.Invalidate()
Simon Glass2a70d892016-07-25 18:59:14 -0600334
Simon Glass116adec2018-07-06 10:27:38 -0600335 def AddZeroProp(self, prop_name):
336 """Add a new property to the device tree with an integer value of 0.
337
338 Args:
339 prop_name: Name of property
340 """
Simon Glass194b8d52019-05-17 22:00:33 -0600341 self.props[prop_name] = Prop(self, None, prop_name,
342 tools.GetBytes(0, 4))
Simon Glass116adec2018-07-06 10:27:38 -0600343
Simon Glass64349612018-09-14 04:57:16 -0600344 def AddEmptyProp(self, prop_name, len):
345 """Add a property with a fixed data size, for filling in later
346
347 The device tree is marked dirty so that the value will be written to
348 the blob on the next sync.
349
350 Args:
351 prop_name: Name of property
352 len: Length of data in property
353 """
Simon Glass194b8d52019-05-17 22:00:33 -0600354 value = tools.GetBytes(0, len)
Simon Glass64349612018-09-14 04:57:16 -0600355 self.props[prop_name] = Prop(self, None, prop_name, value)
356
Simon Glassd9dad102019-07-20 12:23:37 -0600357 def _CheckProp(self, prop_name):
358 """Check if a property is present
359
360 Args:
361 prop_name: Name of property
362
363 Returns:
364 self
365
366 Raises:
367 ValueError if the property is missing
368 """
369 if prop_name not in self.props:
370 raise ValueError("Fdt '%s', node '%s': Missing property '%s'" %
371 (self._fdt._fname, self.path, prop_name))
372 return self
373
Simon Glass116adec2018-07-06 10:27:38 -0600374 def SetInt(self, prop_name, val):
375 """Update an integer property int the device tree.
376
377 This is not allowed to change the size of the FDT.
378
Simon Glass64349612018-09-14 04:57:16 -0600379 The device tree is marked dirty so that the value will be written to
380 the blob on the next sync.
381
Simon Glass116adec2018-07-06 10:27:38 -0600382 Args:
383 prop_name: Name of property
384 val: Value to set
385 """
Simon Glassd9dad102019-07-20 12:23:37 -0600386 self._CheckProp(prop_name).props[prop_name].SetInt(val)
Simon Glassfa80c252018-09-14 04:57:13 -0600387
Simon Glass64349612018-09-14 04:57:16 -0600388 def SetData(self, prop_name, val):
389 """Set the data value of a property
390
391 The device tree is marked dirty so that the value will be written to
392 the blob on the next sync.
393
394 Args:
395 prop_name: Name of property to set
396 val: Data value to set
397 """
Simon Glassd9dad102019-07-20 12:23:37 -0600398 self._CheckProp(prop_name).props[prop_name].SetData(val)
Simon Glass64349612018-09-14 04:57:16 -0600399
400 def SetString(self, prop_name, val):
401 """Set the string value of a property
402
403 The device tree is marked dirty so that the value will be written to
404 the blob on the next sync.
405
406 Args:
407 prop_name: Name of property to set
408 val: String value to set (will be \0-terminated in DT)
409 """
Simon Glassa90df2b2019-10-31 07:43:04 -0600410 if type(val) == str:
411 val = val.encode('utf-8')
Simon Glassd9dad102019-07-20 12:23:37 -0600412 self._CheckProp(prop_name).props[prop_name].SetData(val + b'\0')
Simon Glass64349612018-09-14 04:57:16 -0600413
Simon Glassc0639172020-07-09 18:39:44 -0600414 def AddData(self, prop_name, val):
415 """Add a new property to a node
416
417 The device tree is marked dirty so that the value will be written to
418 the blob on the next sync.
419
420 Args:
421 prop_name: Name of property to add
422 val: Bytes value of property
423 """
424 self.props[prop_name] = Prop(self, None, prop_name, val)
425
Simon Glass64349612018-09-14 04:57:16 -0600426 def AddString(self, prop_name, val):
427 """Add a new string property to a node
428
429 The device tree is marked dirty so that the value will be written to
430 the blob on the next sync.
431
432 Args:
433 prop_name: Name of property to add
434 val: String value of property
435 """
Simon Glassf6b64812019-05-17 22:00:36 -0600436 if sys.version_info[0] >= 3: # pragma: no cover
437 val = bytes(val, 'utf-8')
Simon Glassc0639172020-07-09 18:39:44 -0600438 self.AddData(prop_name, val + b'\0')
Simon Glass64349612018-09-14 04:57:16 -0600439
Simon Glasse21c27a2018-09-14 04:57:15 -0600440 def AddSubnode(self, name):
Simon Glass64349612018-09-14 04:57:16 -0600441 """Add a new subnode to the node
442
443 Args:
444 name: name of node to add
445
446 Returns:
447 New subnode that was created
448 """
Simon Glasse21c27a2018-09-14 04:57:15 -0600449 path = self.path + '/' + name
450 subnode = Node(self._fdt, self, None, name, path)
451 self.subnodes.append(subnode)
452 return subnode
453
Simon Glassfa80c252018-09-14 04:57:13 -0600454 def Sync(self, auto_resize=False):
455 """Sync node changes back to the device tree
456
457 This updates the device tree blob with any changes to this node and its
458 subnodes since the last sync.
459
460 Args:
461 auto_resize: Resize the device tree automatically if it does not
462 have enough space for the update
463
464 Raises:
465 FdtException if auto_resize is False and there is not enough space
466 """
Simon Glasse21c27a2018-09-14 04:57:15 -0600467 if self._offset is None:
468 # The subnode doesn't exist yet, so add it
469 fdt_obj = self._fdt._fdt_obj
470 if auto_resize:
471 while True:
472 offset = fdt_obj.add_subnode(self.parent._offset, self.name,
473 (libfdt.NOSPACE,))
474 if offset != -libfdt.NOSPACE:
475 break
476 fdt_obj.resize(fdt_obj.totalsize() + 1024)
477 else:
478 offset = fdt_obj.add_subnode(self.parent._offset, self.name)
479 self._offset = offset
480
Simon Glassfa80c252018-09-14 04:57:13 -0600481 # Sync subnodes in reverse so that we don't disturb node offsets for
482 # nodes that are earlier in the DT. This avoids an O(n^2) rescan of
483 # node offsets.
484 for node in reversed(self.subnodes):
485 node.Sync(auto_resize)
486
487 # Sync properties now, whose offsets should not have been disturbed.
488 # We do this after subnodes, since this disturbs the offsets of these
Simon Glass63518052019-05-17 22:00:38 -0600489 # properties. Note that new properties will have an offset of None here,
490 # which Python 3 cannot sort against int. So use a large value instead
491 # to ensure that the new properties are added first.
492 prop_list = sorted(self.props.values(),
493 key=lambda prop: prop._offset or 1 << 31,
Simon Glassfa80c252018-09-14 04:57:13 -0600494 reverse=True)
495 for prop in prop_list:
496 prop.Sync(auto_resize)
Simon Glass116adec2018-07-06 10:27:38 -0600497
498
Simon Glassa06a34b2016-07-25 18:59:04 -0600499class Fdt:
Simon Glass7b75b442017-05-27 07:38:28 -0600500 """Provides simple access to a flat device tree blob using libfdts.
Simon Glassa06a34b2016-07-25 18:59:04 -0600501
502 Properties:
503 fname: Filename of fdt
504 _root: Root of device tree (a Node object)
Simon Glass880e9ee2019-07-20 12:23:38 -0600505 name: Helpful name for this Fdt for the user (useful when creating the
506 DT from data rather than a file)
Simon Glassa06a34b2016-07-25 18:59:04 -0600507 """
508 def __init__(self, fname):
509 self._fname = fname
Simon Glass7b75b442017-05-27 07:38:28 -0600510 self._cached_offsets = False
Simon Glass09264e02017-08-29 14:15:52 -0600511 self.phandle_to_node = {}
Simon Glass880e9ee2019-07-20 12:23:38 -0600512 self.name = ''
Simon Glass7b75b442017-05-27 07:38:28 -0600513 if self._fname:
Simon Glass880e9ee2019-07-20 12:23:38 -0600514 self.name = self._fname
Simon Glass7b75b442017-05-27 07:38:28 -0600515 self._fname = fdt_util.EnsureCompiled(self._fname)
516
Simon Glass3e4b51e2019-05-14 15:53:43 -0600517 with open(self._fname, 'rb') as fd:
Simon Glass96066242018-07-06 10:27:27 -0600518 self._fdt_obj = libfdt.Fdt(fd.read())
Simon Glassf7a2aee2016-07-25 18:59:07 -0600519
Simon Glass746aee32018-09-14 04:57:17 -0600520 @staticmethod
Simon Glass880e9ee2019-07-20 12:23:38 -0600521 def FromData(data, name=''):
Simon Glass746aee32018-09-14 04:57:17 -0600522 """Create a new Fdt object from the given data
523
524 Args:
525 data: Device-tree data blob
Simon Glass880e9ee2019-07-20 12:23:38 -0600526 name: Helpful name for this Fdt for the user
Simon Glass746aee32018-09-14 04:57:17 -0600527
528 Returns:
529 Fdt object containing the data
530 """
531 fdt = Fdt(None)
Simon Glassf6b64812019-05-17 22:00:36 -0600532 fdt._fdt_obj = libfdt.Fdt(bytes(data))
Simon Glass880e9ee2019-07-20 12:23:38 -0600533 fdt.name = name
Simon Glass746aee32018-09-14 04:57:17 -0600534 return fdt
535
Simon Glass94a7c602018-07-17 13:25:46 -0600536 def LookupPhandle(self, phandle):
537 """Look up a phandle
538
539 Args:
540 phandle: Phandle to look up (int)
541
542 Returns:
543 Node object the phandle points to
544 """
545 return self.phandle_to_node.get(phandle)
546
Simon Glassf7a2aee2016-07-25 18:59:07 -0600547 def Scan(self, root='/'):
548 """Scan a device tree, building up a tree of Node objects
549
550 This fills in the self._root property
551
552 Args:
553 root: Ignored
554
555 TODO(sjg@chromium.org): Implement the 'root' parameter
556 """
Simon Glassf9b88b32018-07-06 10:27:29 -0600557 self._cached_offsets = True
Simon Glass979ab022017-08-29 14:15:47 -0600558 self._root = self.Node(self, None, 0, '/', '/')
Simon Glassf7a2aee2016-07-25 18:59:07 -0600559 self._root.Scan()
560
561 def GetRoot(self):
562 """Get the root Node of the device tree
563
564 Returns:
565 The root Node object
566 """
567 return self._root
568
569 def GetNode(self, path):
570 """Look up a node from its path
571
572 Args:
573 path: Path to look up, e.g. '/microcode/update@0'
574 Returns:
575 Node object, or None if not found
576 """
577 node = self._root
Simon Glassb9066ff2018-07-06 10:27:30 -0600578 parts = path.split('/')
579 if len(parts) < 2:
580 return None
Simon Glasse44bc832019-07-20 12:23:39 -0600581 if len(parts) == 2 and parts[1] == '':
582 return node
Simon Glassb9066ff2018-07-06 10:27:30 -0600583 for part in parts[1:]:
Simon Glass1d858882018-07-17 13:25:41 -0600584 node = node.FindNode(part)
Simon Glassf7a2aee2016-07-25 18:59:07 -0600585 if not node:
586 return None
587 return node
588
Simon Glassda5f7492016-07-25 18:59:15 -0600589 def Flush(self):
590 """Flush device tree changes back to the file
591
592 If the device tree has changed in memory, write it back to the file.
Simon Glassda5f7492016-07-25 18:59:15 -0600593 """
Simon Glass7b75b442017-05-27 07:38:28 -0600594 with open(self._fname, 'wb') as fd:
Simon Glass96066242018-07-06 10:27:27 -0600595 fd.write(self._fdt_obj.as_bytearray())
Simon Glassda5f7492016-07-25 18:59:15 -0600596
Simon Glassfa80c252018-09-14 04:57:13 -0600597 def Sync(self, auto_resize=False):
598 """Make sure any DT changes are written to the blob
599
600 Args:
601 auto_resize: Resize the device tree automatically if it does not
602 have enough space for the update
603
604 Raises:
605 FdtException if auto_resize is False and there is not enough space
606 """
607 self._root.Sync(auto_resize)
608 self.Invalidate()
609
Simon Glassda5f7492016-07-25 18:59:15 -0600610 def Pack(self):
611 """Pack the device tree down to its minimum size
612
613 When nodes and properties shrink or are deleted, wasted space can
Simon Glass7b75b442017-05-27 07:38:28 -0600614 build up in the device tree binary.
Simon Glassda5f7492016-07-25 18:59:15 -0600615 """
Simon Glass117f57b2018-07-06 10:27:26 -0600616 CheckErr(self._fdt_obj.pack(), 'pack')
617 self.Invalidate()
Simon Glass7b75b442017-05-27 07:38:28 -0600618
Simon Glass96066242018-07-06 10:27:27 -0600619 def GetContents(self):
Simon Glass7b75b442017-05-27 07:38:28 -0600620 """Get the contents of the FDT
621
622 Returns:
623 The FDT contents as a string of bytes
624 """
Simon Glassf6b64812019-05-17 22:00:36 -0600625 return bytes(self._fdt_obj.as_bytearray())
Simon Glass7b75b442017-05-27 07:38:28 -0600626
Simon Glass2ba98752018-07-06 10:27:24 -0600627 def GetFdtObj(self):
628 """Get the contents of the FDT
629
630 Returns:
631 The FDT contents as a libfdt.Fdt object
632 """
633 return self._fdt_obj
634
Simon Glass7b75b442017-05-27 07:38:28 -0600635 def GetProps(self, node):
636 """Get all properties from a node.
637
638 Args:
639 node: Full path to node name to look in.
640
641 Returns:
642 A dictionary containing all the properties, indexed by node name.
643 The entries are Prop objects.
644
645 Raises:
646 ValueError: if the node does not exist.
647 """
648 props_dict = {}
Simon Glass117f57b2018-07-06 10:27:26 -0600649 poffset = self._fdt_obj.first_property_offset(node._offset,
650 QUIET_NOTFOUND)
Simon Glass7b75b442017-05-27 07:38:28 -0600651 while poffset >= 0:
652 p = self._fdt_obj.get_property_by_offset(poffset)
Simon Glass3def0cf2018-07-06 10:27:20 -0600653 prop = Prop(node, poffset, p.name, p)
Simon Glass7b75b442017-05-27 07:38:28 -0600654 props_dict[prop.name] = prop
655
Simon Glass117f57b2018-07-06 10:27:26 -0600656 poffset = self._fdt_obj.next_property_offset(poffset,
657 QUIET_NOTFOUND)
Simon Glass7b75b442017-05-27 07:38:28 -0600658 return props_dict
659
660 def Invalidate(self):
661 """Mark our offset cache as invalid"""
662 self._cached_offsets = False
663
664 def CheckCache(self):
665 """Refresh the offset cache if needed"""
666 if self._cached_offsets:
667 return
668 self.Refresh()
669 self._cached_offsets = True
670
671 def Refresh(self):
672 """Refresh the offset cache"""
673 self._root.Refresh(0)
674
675 def GetStructOffset(self, offset):
676 """Get the file offset of a given struct offset
677
678 Args:
679 offset: Offset within the 'struct' region of the device tree
680 Returns:
681 Position of @offset within the device tree binary
682 """
Simon Glass117f57b2018-07-06 10:27:26 -0600683 return self._fdt_obj.off_dt_struct() + offset
Simon Glass7b75b442017-05-27 07:38:28 -0600684
685 @classmethod
Simon Glass979ab022017-08-29 14:15:47 -0600686 def Node(self, fdt, parent, offset, name, path):
Simon Glass7b75b442017-05-27 07:38:28 -0600687 """Create a new node
688
689 This is used by Fdt.Scan() to create a new node using the correct
690 class.
691
692 Args:
693 fdt: Fdt object
Simon Glass979ab022017-08-29 14:15:47 -0600694 parent: Parent node, or None if this is the root node
Simon Glass7b75b442017-05-27 07:38:28 -0600695 offset: Offset of node
696 name: Node name
697 path: Full path to node
698 """
Simon Glass979ab022017-08-29 14:15:47 -0600699 node = Node(fdt, parent, offset, name, path)
Simon Glass7b75b442017-05-27 07:38:28 -0600700 return node
Simon Glass99ed4a22017-05-27 07:38:30 -0600701
Simon Glassf6e02492019-07-20 12:24:08 -0600702 def GetFilename(self):
703 """Get the filename of the device tree
704
705 Returns:
706 String filename
707 """
708 return self._fname
709
Simon Glass99ed4a22017-05-27 07:38:30 -0600710def FdtScan(fname):
Simon Glassdfe5f5b2018-07-06 10:27:32 -0600711 """Returns a new Fdt object"""
Simon Glass99ed4a22017-05-27 07:38:30 -0600712 dtb = Fdt(fname)
713 dtb.Scan()
714 return dtb