blob: 1b7b730359ae80c84260b58b85a3284a0c4f88a4 [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
11import fdt_util
Simon Glass7b75b442017-05-27 07:38:28 -060012import libfdt
Simon Glass117f57b2018-07-06 10:27:26 -060013from libfdt import QUIET_NOTFOUND
Simon Glass194b8d52019-05-17 22:00:33 -060014import 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:
210 fdt_obj.resize(fdt_obj.totalsize() + 1024)
211 fdt_obj.setprop(node.Offset(), self.name, self.bytes)
212 else:
213 fdt_obj.setprop(node.Offset(), self.name, self.bytes)
214
215
Simon Glass7b75b442017-05-27 07:38:28 -0600216class Node:
Simon Glassa06a34b2016-07-25 18:59:04 -0600217 """A device tree node
218
219 Properties:
220 offset: Integer offset in the device tree
221 name: Device tree node tname
222 path: Full path to node, along with the node name itself
223 _fdt: Device tree object
224 subnodes: A list of subnodes for this node, each a Node object
225 props: A dict of properties for this node, each a Prop object.
226 Keyed by property name
227 """
Simon Glass979ab022017-08-29 14:15:47 -0600228 def __init__(self, fdt, parent, offset, name, path):
Simon Glassa06a34b2016-07-25 18:59:04 -0600229 self._fdt = fdt
Simon Glass979ab022017-08-29 14:15:47 -0600230 self.parent = parent
Simon Glassa06a34b2016-07-25 18:59:04 -0600231 self._offset = offset
232 self.name = name
233 self.path = path
234 self.subnodes = []
235 self.props = {}
236
Simon Glass94a7c602018-07-17 13:25:46 -0600237 def GetFdt(self):
238 """Get the Fdt object for this node
239
240 Returns:
241 Fdt object
242 """
243 return self._fdt
244
Simon Glass1d858882018-07-17 13:25:41 -0600245 def FindNode(self, name):
Simon Glassf7a2aee2016-07-25 18:59:07 -0600246 """Find a node given its name
247
248 Args:
249 name: Node name to look for
250 Returns:
251 Node object if found, else None
252 """
253 for subnode in self.subnodes:
254 if subnode.name == name:
255 return subnode
256 return None
257
Simon Glass7b75b442017-05-27 07:38:28 -0600258 def Offset(self):
259 """Returns the offset of a node, after checking the cache
Simon Glassf7a2aee2016-07-25 18:59:07 -0600260
Simon Glass7b75b442017-05-27 07:38:28 -0600261 This should be used instead of self._offset directly, to ensure that
262 the cache does not contain invalid offsets.
Simon Glassf7a2aee2016-07-25 18:59:07 -0600263 """
Simon Glass7b75b442017-05-27 07:38:28 -0600264 self._fdt.CheckCache()
265 return self._offset
266
267 def Scan(self):
268 """Scan a node's properties and subnodes
269
270 This fills in the props and subnodes properties, recursively
271 searching into subnodes so that the entire tree is built.
272 """
Simon Glass117f57b2018-07-06 10:27:26 -0600273 fdt_obj = self._fdt._fdt_obj
Simon Glass7b75b442017-05-27 07:38:28 -0600274 self.props = self._fdt.GetProps(self)
Simon Glass117f57b2018-07-06 10:27:26 -0600275 phandle = fdt_obj.get_phandle(self.Offset())
Simon Glass09264e02017-08-29 14:15:52 -0600276 if phandle:
Simon Glass117f57b2018-07-06 10:27:26 -0600277 self._fdt.phandle_to_node[phandle] = self
Simon Glass7b75b442017-05-27 07:38:28 -0600278
Simon Glass117f57b2018-07-06 10:27:26 -0600279 offset = fdt_obj.first_subnode(self.Offset(), QUIET_NOTFOUND)
Simon Glass7b75b442017-05-27 07:38:28 -0600280 while offset >= 0:
281 sep = '' if self.path[-1] == '/' else '/'
Simon Glass117f57b2018-07-06 10:27:26 -0600282 name = fdt_obj.get_name(offset)
Simon Glass7b75b442017-05-27 07:38:28 -0600283 path = self.path + sep + name
Simon Glass979ab022017-08-29 14:15:47 -0600284 node = Node(self._fdt, self, offset, name, path)
Simon Glass7b75b442017-05-27 07:38:28 -0600285 self.subnodes.append(node)
286
287 node.Scan()
Simon Glass117f57b2018-07-06 10:27:26 -0600288 offset = fdt_obj.next_subnode(offset, QUIET_NOTFOUND)
Simon Glass7b75b442017-05-27 07:38:28 -0600289
290 def Refresh(self, my_offset):
291 """Fix up the _offset for each node, recursively
292
293 Note: This does not take account of property offsets - these will not
294 be updated.
295 """
Simon Glass96066242018-07-06 10:27:27 -0600296 fdt_obj = self._fdt._fdt_obj
Simon Glass7b75b442017-05-27 07:38:28 -0600297 if self._offset != my_offset:
Simon Glass7b75b442017-05-27 07:38:28 -0600298 self._offset = my_offset
Simon Glass96066242018-07-06 10:27:27 -0600299 offset = fdt_obj.first_subnode(self._offset, QUIET_NOTFOUND)
Simon Glass7b75b442017-05-27 07:38:28 -0600300 for subnode in self.subnodes:
Simon Glassf9b88b32018-07-06 10:27:29 -0600301 if subnode.name != fdt_obj.get_name(offset):
302 raise ValueError('Internal error, node name mismatch %s != %s' %
303 (subnode.name, fdt_obj.get_name(offset)))
Simon Glass7b75b442017-05-27 07:38:28 -0600304 subnode.Refresh(offset)
Simon Glass96066242018-07-06 10:27:27 -0600305 offset = fdt_obj.next_subnode(offset, QUIET_NOTFOUND)
Simon Glassf9b88b32018-07-06 10:27:29 -0600306 if offset != -libfdt.FDT_ERR_NOTFOUND:
307 raise ValueError('Internal error, offset == %d' % offset)
308
309 poffset = fdt_obj.first_property_offset(self._offset, QUIET_NOTFOUND)
310 while poffset >= 0:
311 p = fdt_obj.get_property_by_offset(poffset)
312 prop = self.props.get(p.name)
313 if not prop:
314 raise ValueError("Internal error, property '%s' missing, "
315 'offset %d' % (p.name, poffset))
316 prop.RefreshOffset(poffset)
317 poffset = fdt_obj.next_property_offset(poffset, QUIET_NOTFOUND)
Simon Glassf7a2aee2016-07-25 18:59:07 -0600318
Simon Glass2a70d892016-07-25 18:59:14 -0600319 def DeleteProp(self, prop_name):
320 """Delete a property of a node
321
Simon Glass7b75b442017-05-27 07:38:28 -0600322 The property is deleted and the offset cache is invalidated.
Simon Glass2a70d892016-07-25 18:59:14 -0600323
324 Args:
325 prop_name: Name of the property to delete
Simon Glass7b75b442017-05-27 07:38:28 -0600326 Raises:
327 ValueError if the property does not exist
Simon Glass2a70d892016-07-25 18:59:14 -0600328 """
Simon Glass96066242018-07-06 10:27:27 -0600329 CheckErr(self._fdt._fdt_obj.delprop(self.Offset(), prop_name),
Simon Glass7b75b442017-05-27 07:38:28 -0600330 "Node '%s': delete property: '%s'" % (self.path, prop_name))
331 del self.props[prop_name]
332 self._fdt.Invalidate()
Simon Glass2a70d892016-07-25 18:59:14 -0600333
Simon Glass116adec2018-07-06 10:27:38 -0600334 def AddZeroProp(self, prop_name):
335 """Add a new property to the device tree with an integer value of 0.
336
337 Args:
338 prop_name: Name of property
339 """
Simon Glass194b8d52019-05-17 22:00:33 -0600340 self.props[prop_name] = Prop(self, None, prop_name,
341 tools.GetBytes(0, 4))
Simon Glass116adec2018-07-06 10:27:38 -0600342
Simon Glass64349612018-09-14 04:57:16 -0600343 def AddEmptyProp(self, prop_name, len):
344 """Add a property with a fixed data size, for filling in later
345
346 The device tree is marked dirty so that the value will be written to
347 the blob on the next sync.
348
349 Args:
350 prop_name: Name of property
351 len: Length of data in property
352 """
Simon Glass194b8d52019-05-17 22:00:33 -0600353 value = tools.GetBytes(0, len)
Simon Glass64349612018-09-14 04:57:16 -0600354 self.props[prop_name] = Prop(self, None, prop_name, value)
355
Simon Glassd9dad102019-07-20 12:23:37 -0600356 def _CheckProp(self, prop_name):
357 """Check if a property is present
358
359 Args:
360 prop_name: Name of property
361
362 Returns:
363 self
364
365 Raises:
366 ValueError if the property is missing
367 """
368 if prop_name not in self.props:
369 raise ValueError("Fdt '%s', node '%s': Missing property '%s'" %
370 (self._fdt._fname, self.path, prop_name))
371 return self
372
Simon Glass116adec2018-07-06 10:27:38 -0600373 def SetInt(self, prop_name, val):
374 """Update an integer property int the device tree.
375
376 This is not allowed to change the size of the FDT.
377
Simon Glass64349612018-09-14 04:57:16 -0600378 The device tree is marked dirty so that the value will be written to
379 the blob on the next sync.
380
Simon Glass116adec2018-07-06 10:27:38 -0600381 Args:
382 prop_name: Name of property
383 val: Value to set
384 """
Simon Glassd9dad102019-07-20 12:23:37 -0600385 self._CheckProp(prop_name).props[prop_name].SetInt(val)
Simon Glassfa80c252018-09-14 04:57:13 -0600386
Simon Glass64349612018-09-14 04:57:16 -0600387 def SetData(self, prop_name, val):
388 """Set the data value of a property
389
390 The device tree is marked dirty so that the value will be written to
391 the blob on the next sync.
392
393 Args:
394 prop_name: Name of property to set
395 val: Data value to set
396 """
Simon Glassd9dad102019-07-20 12:23:37 -0600397 self._CheckProp(prop_name).props[prop_name].SetData(val)
Simon Glass64349612018-09-14 04:57:16 -0600398
399 def SetString(self, prop_name, val):
400 """Set the string value of a property
401
402 The device tree is marked dirty so that the value will be written to
403 the blob on the next sync.
404
405 Args:
406 prop_name: Name of property to set
407 val: String value to set (will be \0-terminated in DT)
408 """
Simon Glassa90df2b2019-10-31 07:43:04 -0600409 if type(val) == str:
410 val = val.encode('utf-8')
Simon Glassd9dad102019-07-20 12:23:37 -0600411 self._CheckProp(prop_name).props[prop_name].SetData(val + b'\0')
Simon Glass64349612018-09-14 04:57:16 -0600412
413 def AddString(self, prop_name, val):
414 """Add a new string property to a node
415
416 The device tree is marked dirty so that the value will be written to
417 the blob on the next sync.
418
419 Args:
420 prop_name: Name of property to add
421 val: String value of property
422 """
Simon Glassf6b64812019-05-17 22:00:36 -0600423 if sys.version_info[0] >= 3: # pragma: no cover
424 val = bytes(val, 'utf-8')
425 self.props[prop_name] = Prop(self, None, prop_name, val + b'\0')
Simon Glass64349612018-09-14 04:57:16 -0600426
Simon Glasse21c27a2018-09-14 04:57:15 -0600427 def AddSubnode(self, name):
Simon Glass64349612018-09-14 04:57:16 -0600428 """Add a new subnode to the node
429
430 Args:
431 name: name of node to add
432
433 Returns:
434 New subnode that was created
435 """
Simon Glasse21c27a2018-09-14 04:57:15 -0600436 path = self.path + '/' + name
437 subnode = Node(self._fdt, self, None, name, path)
438 self.subnodes.append(subnode)
439 return subnode
440
Simon Glassfa80c252018-09-14 04:57:13 -0600441 def Sync(self, auto_resize=False):
442 """Sync node changes back to the device tree
443
444 This updates the device tree blob with any changes to this node and its
445 subnodes since the last sync.
446
447 Args:
448 auto_resize: Resize the device tree automatically if it does not
449 have enough space for the update
450
451 Raises:
452 FdtException if auto_resize is False and there is not enough space
453 """
Simon Glasse21c27a2018-09-14 04:57:15 -0600454 if self._offset is None:
455 # The subnode doesn't exist yet, so add it
456 fdt_obj = self._fdt._fdt_obj
457 if auto_resize:
458 while True:
459 offset = fdt_obj.add_subnode(self.parent._offset, self.name,
460 (libfdt.NOSPACE,))
461 if offset != -libfdt.NOSPACE:
462 break
463 fdt_obj.resize(fdt_obj.totalsize() + 1024)
464 else:
465 offset = fdt_obj.add_subnode(self.parent._offset, self.name)
466 self._offset = offset
467
Simon Glassfa80c252018-09-14 04:57:13 -0600468 # Sync subnodes in reverse so that we don't disturb node offsets for
469 # nodes that are earlier in the DT. This avoids an O(n^2) rescan of
470 # node offsets.
471 for node in reversed(self.subnodes):
472 node.Sync(auto_resize)
473
474 # Sync properties now, whose offsets should not have been disturbed.
475 # We do this after subnodes, since this disturbs the offsets of these
Simon Glass63518052019-05-17 22:00:38 -0600476 # properties. Note that new properties will have an offset of None here,
477 # which Python 3 cannot sort against int. So use a large value instead
478 # to ensure that the new properties are added first.
479 prop_list = sorted(self.props.values(),
480 key=lambda prop: prop._offset or 1 << 31,
Simon Glassfa80c252018-09-14 04:57:13 -0600481 reverse=True)
482 for prop in prop_list:
483 prop.Sync(auto_resize)
Simon Glass116adec2018-07-06 10:27:38 -0600484
485
Simon Glassa06a34b2016-07-25 18:59:04 -0600486class Fdt:
Simon Glass7b75b442017-05-27 07:38:28 -0600487 """Provides simple access to a flat device tree blob using libfdts.
Simon Glassa06a34b2016-07-25 18:59:04 -0600488
489 Properties:
490 fname: Filename of fdt
491 _root: Root of device tree (a Node object)
Simon Glass880e9ee2019-07-20 12:23:38 -0600492 name: Helpful name for this Fdt for the user (useful when creating the
493 DT from data rather than a file)
Simon Glassa06a34b2016-07-25 18:59:04 -0600494 """
495 def __init__(self, fname):
496 self._fname = fname
Simon Glass7b75b442017-05-27 07:38:28 -0600497 self._cached_offsets = False
Simon Glass09264e02017-08-29 14:15:52 -0600498 self.phandle_to_node = {}
Simon Glass880e9ee2019-07-20 12:23:38 -0600499 self.name = ''
Simon Glass7b75b442017-05-27 07:38:28 -0600500 if self._fname:
Simon Glass880e9ee2019-07-20 12:23:38 -0600501 self.name = self._fname
Simon Glass7b75b442017-05-27 07:38:28 -0600502 self._fname = fdt_util.EnsureCompiled(self._fname)
503
Simon Glass3e4b51e2019-05-14 15:53:43 -0600504 with open(self._fname, 'rb') as fd:
Simon Glass96066242018-07-06 10:27:27 -0600505 self._fdt_obj = libfdt.Fdt(fd.read())
Simon Glassf7a2aee2016-07-25 18:59:07 -0600506
Simon Glass746aee32018-09-14 04:57:17 -0600507 @staticmethod
Simon Glass880e9ee2019-07-20 12:23:38 -0600508 def FromData(data, name=''):
Simon Glass746aee32018-09-14 04:57:17 -0600509 """Create a new Fdt object from the given data
510
511 Args:
512 data: Device-tree data blob
Simon Glass880e9ee2019-07-20 12:23:38 -0600513 name: Helpful name for this Fdt for the user
Simon Glass746aee32018-09-14 04:57:17 -0600514
515 Returns:
516 Fdt object containing the data
517 """
518 fdt = Fdt(None)
Simon Glassf6b64812019-05-17 22:00:36 -0600519 fdt._fdt_obj = libfdt.Fdt(bytes(data))
Simon Glass880e9ee2019-07-20 12:23:38 -0600520 fdt.name = name
Simon Glass746aee32018-09-14 04:57:17 -0600521 return fdt
522
Simon Glass94a7c602018-07-17 13:25:46 -0600523 def LookupPhandle(self, phandle):
524 """Look up a phandle
525
526 Args:
527 phandle: Phandle to look up (int)
528
529 Returns:
530 Node object the phandle points to
531 """
532 return self.phandle_to_node.get(phandle)
533
Simon Glassf7a2aee2016-07-25 18:59:07 -0600534 def Scan(self, root='/'):
535 """Scan a device tree, building up a tree of Node objects
536
537 This fills in the self._root property
538
539 Args:
540 root: Ignored
541
542 TODO(sjg@chromium.org): Implement the 'root' parameter
543 """
Simon Glassf9b88b32018-07-06 10:27:29 -0600544 self._cached_offsets = True
Simon Glass979ab022017-08-29 14:15:47 -0600545 self._root = self.Node(self, None, 0, '/', '/')
Simon Glassf7a2aee2016-07-25 18:59:07 -0600546 self._root.Scan()
547
548 def GetRoot(self):
549 """Get the root Node of the device tree
550
551 Returns:
552 The root Node object
553 """
554 return self._root
555
556 def GetNode(self, path):
557 """Look up a node from its path
558
559 Args:
560 path: Path to look up, e.g. '/microcode/update@0'
561 Returns:
562 Node object, or None if not found
563 """
564 node = self._root
Simon Glassb9066ff2018-07-06 10:27:30 -0600565 parts = path.split('/')
566 if len(parts) < 2:
567 return None
Simon Glasse44bc832019-07-20 12:23:39 -0600568 if len(parts) == 2 and parts[1] == '':
569 return node
Simon Glassb9066ff2018-07-06 10:27:30 -0600570 for part in parts[1:]:
Simon Glass1d858882018-07-17 13:25:41 -0600571 node = node.FindNode(part)
Simon Glassf7a2aee2016-07-25 18:59:07 -0600572 if not node:
573 return None
574 return node
575
Simon Glassda5f7492016-07-25 18:59:15 -0600576 def Flush(self):
577 """Flush device tree changes back to the file
578
579 If the device tree has changed in memory, write it back to the file.
Simon Glassda5f7492016-07-25 18:59:15 -0600580 """
Simon Glass7b75b442017-05-27 07:38:28 -0600581 with open(self._fname, 'wb') as fd:
Simon Glass96066242018-07-06 10:27:27 -0600582 fd.write(self._fdt_obj.as_bytearray())
Simon Glassda5f7492016-07-25 18:59:15 -0600583
Simon Glassfa80c252018-09-14 04:57:13 -0600584 def Sync(self, auto_resize=False):
585 """Make sure any DT changes are written to the blob
586
587 Args:
588 auto_resize: Resize the device tree automatically if it does not
589 have enough space for the update
590
591 Raises:
592 FdtException if auto_resize is False and there is not enough space
593 """
594 self._root.Sync(auto_resize)
595 self.Invalidate()
596
Simon Glassda5f7492016-07-25 18:59:15 -0600597 def Pack(self):
598 """Pack the device tree down to its minimum size
599
600 When nodes and properties shrink or are deleted, wasted space can
Simon Glass7b75b442017-05-27 07:38:28 -0600601 build up in the device tree binary.
Simon Glassda5f7492016-07-25 18:59:15 -0600602 """
Simon Glass117f57b2018-07-06 10:27:26 -0600603 CheckErr(self._fdt_obj.pack(), 'pack')
604 self.Invalidate()
Simon Glass7b75b442017-05-27 07:38:28 -0600605
Simon Glass96066242018-07-06 10:27:27 -0600606 def GetContents(self):
Simon Glass7b75b442017-05-27 07:38:28 -0600607 """Get the contents of the FDT
608
609 Returns:
610 The FDT contents as a string of bytes
611 """
Simon Glassf6b64812019-05-17 22:00:36 -0600612 return bytes(self._fdt_obj.as_bytearray())
Simon Glass7b75b442017-05-27 07:38:28 -0600613
Simon Glass2ba98752018-07-06 10:27:24 -0600614 def GetFdtObj(self):
615 """Get the contents of the FDT
616
617 Returns:
618 The FDT contents as a libfdt.Fdt object
619 """
620 return self._fdt_obj
621
Simon Glass7b75b442017-05-27 07:38:28 -0600622 def GetProps(self, node):
623 """Get all properties from a node.
624
625 Args:
626 node: Full path to node name to look in.
627
628 Returns:
629 A dictionary containing all the properties, indexed by node name.
630 The entries are Prop objects.
631
632 Raises:
633 ValueError: if the node does not exist.
634 """
635 props_dict = {}
Simon Glass117f57b2018-07-06 10:27:26 -0600636 poffset = self._fdt_obj.first_property_offset(node._offset,
637 QUIET_NOTFOUND)
Simon Glass7b75b442017-05-27 07:38:28 -0600638 while poffset >= 0:
639 p = self._fdt_obj.get_property_by_offset(poffset)
Simon Glass3def0cf2018-07-06 10:27:20 -0600640 prop = Prop(node, poffset, p.name, p)
Simon Glass7b75b442017-05-27 07:38:28 -0600641 props_dict[prop.name] = prop
642
Simon Glass117f57b2018-07-06 10:27:26 -0600643 poffset = self._fdt_obj.next_property_offset(poffset,
644 QUIET_NOTFOUND)
Simon Glass7b75b442017-05-27 07:38:28 -0600645 return props_dict
646
647 def Invalidate(self):
648 """Mark our offset cache as invalid"""
649 self._cached_offsets = False
650
651 def CheckCache(self):
652 """Refresh the offset cache if needed"""
653 if self._cached_offsets:
654 return
655 self.Refresh()
656 self._cached_offsets = True
657
658 def Refresh(self):
659 """Refresh the offset cache"""
660 self._root.Refresh(0)
661
662 def GetStructOffset(self, offset):
663 """Get the file offset of a given struct offset
664
665 Args:
666 offset: Offset within the 'struct' region of the device tree
667 Returns:
668 Position of @offset within the device tree binary
669 """
Simon Glass117f57b2018-07-06 10:27:26 -0600670 return self._fdt_obj.off_dt_struct() + offset
Simon Glass7b75b442017-05-27 07:38:28 -0600671
672 @classmethod
Simon Glass979ab022017-08-29 14:15:47 -0600673 def Node(self, fdt, parent, offset, name, path):
Simon Glass7b75b442017-05-27 07:38:28 -0600674 """Create a new node
675
676 This is used by Fdt.Scan() to create a new node using the correct
677 class.
678
679 Args:
680 fdt: Fdt object
Simon Glass979ab022017-08-29 14:15:47 -0600681 parent: Parent node, or None if this is the root node
Simon Glass7b75b442017-05-27 07:38:28 -0600682 offset: Offset of node
683 name: Node name
684 path: Full path to node
685 """
Simon Glass979ab022017-08-29 14:15:47 -0600686 node = Node(fdt, parent, offset, name, path)
Simon Glass7b75b442017-05-27 07:38:28 -0600687 return node
Simon Glass99ed4a22017-05-27 07:38:30 -0600688
Simon Glassf6e02492019-07-20 12:24:08 -0600689 def GetFilename(self):
690 """Get the filename of the device tree
691
692 Returns:
693 String filename
694 """
695 return self._fname
696
Simon Glass99ed4a22017-05-27 07:38:30 -0600697def FdtScan(fname):
Simon Glassdfe5f5b2018-07-06 10:27:32 -0600698 """Returns a new Fdt object"""
Simon Glass99ed4a22017-05-27 07:38:30 -0600699 dtb = Fdt(fname)
700 dtb.Scan()
701 return dtb