blob: d9471c43819bfca8cc1e1435fc740a42a8c61735 [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 # Handle Python 2 treating bytes as str
60 if type(ch) == str:
61 ch = ord(ch)
62 if ch < 32 or ch > 127:
Simon Glass7e6952d2019-05-17 22:00:34 -060063 is_string = False
64 break
65 else:
66 is_string = False
67 if is_string:
Simon Glass2b6ed5e2019-05-17 22:00:35 -060068 if count == 1:
69 if sys.version_info[0] >= 3: # pragma: no cover
70 return TYPE_STRING, strings[0].decode()
71 else:
72 return TYPE_STRING, strings[0]
Simon Glass7e6952d2019-05-17 22:00:34 -060073 else:
Simon Glass2b6ed5e2019-05-17 22:00:35 -060074 if sys.version_info[0] >= 3: # pragma: no cover
75 return TYPE_STRING, [s.decode() for s in strings[:-1]]
76 else:
77 return TYPE_STRING, strings[:-1]
Simon Glass7e6952d2019-05-17 22:00:34 -060078 if size % 4:
79 if size == 1:
Simon Glass2b6ed5e2019-05-17 22:00:35 -060080 return TYPE_BYTE, tools.ToChar(data[0])
Simon Glass7e6952d2019-05-17 22:00:34 -060081 else:
Simon Glass2b6ed5e2019-05-17 22:00:35 -060082 return TYPE_BYTE, [tools.ToChar(ch) for ch in list(data)]
Simon Glass7e6952d2019-05-17 22:00:34 -060083 val = []
84 for i in range(0, size, 4):
Simon Glass2b6ed5e2019-05-17 22:00:35 -060085 val.append(data[i:i + 4])
Simon Glass7e6952d2019-05-17 22:00:34 -060086 if size == 4:
87 return TYPE_INT, val[0]
88 else:
89 return TYPE_INT, val
90
91
Simon Glass7b75b442017-05-27 07:38:28 -060092class Prop:
Simon Glassa06a34b2016-07-25 18:59:04 -060093 """A device tree property
94
95 Properties:
96 name: Property name (as per the device tree)
97 value: Property value as a string of bytes, or a list of strings of
98 bytes
99 type: Value type
100 """
Simon Glass928527f2019-05-17 22:00:37 -0600101 def __init__(self, node, offset, name, data):
Simon Glassa06a34b2016-07-25 18:59:04 -0600102 self._node = node
103 self._offset = offset
104 self.name = name
105 self.value = None
Simon Glass928527f2019-05-17 22:00:37 -0600106 self.bytes = bytes(data)
Simon Glassfa80c252018-09-14 04:57:13 -0600107 self.dirty = False
Simon Glass928527f2019-05-17 22:00:37 -0600108 if not data:
Simon Glass7b75b442017-05-27 07:38:28 -0600109 self.type = TYPE_BOOL
110 self.value = True
111 return
Simon Glass928527f2019-05-17 22:00:37 -0600112 self.type, self.value = BytesToValue(bytes(data))
Simon Glassa06a34b2016-07-25 18:59:04 -0600113
Simon Glassf9b88b32018-07-06 10:27:29 -0600114 def RefreshOffset(self, poffset):
115 self._offset = poffset
116
Simon Glassc322a852016-07-25 18:59:06 -0600117 def Widen(self, newprop):
118 """Figure out which property type is more general
119
120 Given a current property and a new property, this function returns the
121 one that is less specific as to type. The less specific property will
122 be ble to represent the data in the more specific property. This is
123 used for things like:
124
125 node1 {
126 compatible = "fred";
127 value = <1>;
128 };
129 node1 {
130 compatible = "fred";
131 value = <1 2>;
132 };
133
134 He we want to use an int array for 'value'. The first property
135 suggests that a single int is enough, but the second one shows that
136 it is not. Calling this function with these two propertes would
137 update the current property to be like the second, since it is less
138 specific.
139 """
140 if newprop.type < self.type:
141 self.type = newprop.type
142
143 if type(newprop.value) == list and type(self.value) != list:
144 self.value = [self.value]
145
146 if type(self.value) == list and len(newprop.value) > len(self.value):
147 val = self.GetEmpty(self.type)
148 while len(self.value) < len(newprop.value):
149 self.value.append(val)
150
Simon Glass2ba98752018-07-06 10:27:24 -0600151 @classmethod
Simon Glassbc1dea32016-07-25 18:59:05 -0600152 def GetEmpty(self, type):
153 """Get an empty / zero value of the given type
154
155 Returns:
156 A single value of the given type
157 """
158 if type == TYPE_BYTE:
159 return chr(0)
160 elif type == TYPE_INT:
Simon Glassaf53f5a2018-09-14 04:57:14 -0600161 return struct.pack('>I', 0);
Simon Glassbc1dea32016-07-25 18:59:05 -0600162 elif type == TYPE_STRING:
163 return ''
164 else:
165 return True
166
Simon Glassbabdbde2016-07-25 18:59:16 -0600167 def GetOffset(self):
168 """Get the offset of a property
169
Simon Glassbabdbde2016-07-25 18:59:16 -0600170 Returns:
Simon Glass7b75b442017-05-27 07:38:28 -0600171 The offset of the property (struct fdt_property) within the file
Simon Glassbabdbde2016-07-25 18:59:16 -0600172 """
Simon Glassf9b88b32018-07-06 10:27:29 -0600173 self._node._fdt.CheckCache()
Simon Glass7b75b442017-05-27 07:38:28 -0600174 return self._node._fdt.GetStructOffset(self._offset)
Simon Glassbabdbde2016-07-25 18:59:16 -0600175
Simon Glassfa80c252018-09-14 04:57:13 -0600176 def SetInt(self, val):
177 """Set the integer value of the property
178
179 The device tree is marked dirty so that the value will be written to
180 the block on the next sync.
181
182 Args:
183 val: Integer value (32-bit, single cell)
184 """
185 self.bytes = struct.pack('>I', val);
Simon Glass41b781d2018-10-01 12:22:49 -0600186 self.value = self.bytes
Simon Glassfa80c252018-09-14 04:57:13 -0600187 self.type = TYPE_INT
188 self.dirty = True
189
Simon Glass64349612018-09-14 04:57:16 -0600190 def SetData(self, bytes):
191 """Set the value of a property as bytes
192
193 Args:
194 bytes: New property value to set
195 """
Simon Glassf6b64812019-05-17 22:00:36 -0600196 self.bytes = bytes
Simon Glass7e6952d2019-05-17 22:00:34 -0600197 self.type, self.value = BytesToValue(bytes)
Simon Glass64349612018-09-14 04:57:16 -0600198 self.dirty = True
199
Simon Glassfa80c252018-09-14 04:57:13 -0600200 def Sync(self, auto_resize=False):
201 """Sync property changes back to the device tree
202
203 This updates the device tree blob with any changes to this property
204 since the last sync.
205
206 Args:
207 auto_resize: Resize the device tree automatically if it does not
208 have enough space for the update
209
210 Raises:
211 FdtException if auto_resize is False and there is not enough space
212 """
213 if self._offset is None or self.dirty:
214 node = self._node
215 fdt_obj = node._fdt._fdt_obj
216 if auto_resize:
217 while fdt_obj.setprop(node.Offset(), self.name, self.bytes,
218 (libfdt.NOSPACE,)) == -libfdt.NOSPACE:
219 fdt_obj.resize(fdt_obj.totalsize() + 1024)
220 fdt_obj.setprop(node.Offset(), self.name, self.bytes)
221 else:
222 fdt_obj.setprop(node.Offset(), self.name, self.bytes)
223
224
Simon Glass7b75b442017-05-27 07:38:28 -0600225class Node:
Simon Glassa06a34b2016-07-25 18:59:04 -0600226 """A device tree node
227
228 Properties:
229 offset: Integer offset in the device tree
230 name: Device tree node tname
231 path: Full path to node, along with the node name itself
232 _fdt: Device tree object
233 subnodes: A list of subnodes for this node, each a Node object
234 props: A dict of properties for this node, each a Prop object.
235 Keyed by property name
236 """
Simon Glass979ab022017-08-29 14:15:47 -0600237 def __init__(self, fdt, parent, offset, name, path):
Simon Glassa06a34b2016-07-25 18:59:04 -0600238 self._fdt = fdt
Simon Glass979ab022017-08-29 14:15:47 -0600239 self.parent = parent
Simon Glassa06a34b2016-07-25 18:59:04 -0600240 self._offset = offset
241 self.name = name
242 self.path = path
243 self.subnodes = []
244 self.props = {}
245
Simon Glass94a7c602018-07-17 13:25:46 -0600246 def GetFdt(self):
247 """Get the Fdt object for this node
248
249 Returns:
250 Fdt object
251 """
252 return self._fdt
253
Simon Glass1d858882018-07-17 13:25:41 -0600254 def FindNode(self, name):
Simon Glassf7a2aee2016-07-25 18:59:07 -0600255 """Find a node given its name
256
257 Args:
258 name: Node name to look for
259 Returns:
260 Node object if found, else None
261 """
262 for subnode in self.subnodes:
263 if subnode.name == name:
264 return subnode
265 return None
266
Simon Glass7b75b442017-05-27 07:38:28 -0600267 def Offset(self):
268 """Returns the offset of a node, after checking the cache
Simon Glassf7a2aee2016-07-25 18:59:07 -0600269
Simon Glass7b75b442017-05-27 07:38:28 -0600270 This should be used instead of self._offset directly, to ensure that
271 the cache does not contain invalid offsets.
Simon Glassf7a2aee2016-07-25 18:59:07 -0600272 """
Simon Glass7b75b442017-05-27 07:38:28 -0600273 self._fdt.CheckCache()
274 return self._offset
275
276 def Scan(self):
277 """Scan a node's properties and subnodes
278
279 This fills in the props and subnodes properties, recursively
280 searching into subnodes so that the entire tree is built.
281 """
Simon Glass117f57b2018-07-06 10:27:26 -0600282 fdt_obj = self._fdt._fdt_obj
Simon Glass7b75b442017-05-27 07:38:28 -0600283 self.props = self._fdt.GetProps(self)
Simon Glass117f57b2018-07-06 10:27:26 -0600284 phandle = fdt_obj.get_phandle(self.Offset())
Simon Glass09264e02017-08-29 14:15:52 -0600285 if phandle:
Simon Glass117f57b2018-07-06 10:27:26 -0600286 self._fdt.phandle_to_node[phandle] = self
Simon Glass7b75b442017-05-27 07:38:28 -0600287
Simon Glass117f57b2018-07-06 10:27:26 -0600288 offset = fdt_obj.first_subnode(self.Offset(), QUIET_NOTFOUND)
Simon Glass7b75b442017-05-27 07:38:28 -0600289 while offset >= 0:
290 sep = '' if self.path[-1] == '/' else '/'
Simon Glass117f57b2018-07-06 10:27:26 -0600291 name = fdt_obj.get_name(offset)
Simon Glass7b75b442017-05-27 07:38:28 -0600292 path = self.path + sep + name
Simon Glass979ab022017-08-29 14:15:47 -0600293 node = Node(self._fdt, self, offset, name, path)
Simon Glass7b75b442017-05-27 07:38:28 -0600294 self.subnodes.append(node)
295
296 node.Scan()
Simon Glass117f57b2018-07-06 10:27:26 -0600297 offset = fdt_obj.next_subnode(offset, QUIET_NOTFOUND)
Simon Glass7b75b442017-05-27 07:38:28 -0600298
299 def Refresh(self, my_offset):
300 """Fix up the _offset for each node, recursively
301
302 Note: This does not take account of property offsets - these will not
303 be updated.
304 """
Simon Glass96066242018-07-06 10:27:27 -0600305 fdt_obj = self._fdt._fdt_obj
Simon Glass7b75b442017-05-27 07:38:28 -0600306 if self._offset != my_offset:
Simon Glass7b75b442017-05-27 07:38:28 -0600307 self._offset = my_offset
Simon Glass96066242018-07-06 10:27:27 -0600308 offset = fdt_obj.first_subnode(self._offset, QUIET_NOTFOUND)
Simon Glass7b75b442017-05-27 07:38:28 -0600309 for subnode in self.subnodes:
Simon Glassf9b88b32018-07-06 10:27:29 -0600310 if subnode.name != fdt_obj.get_name(offset):
311 raise ValueError('Internal error, node name mismatch %s != %s' %
312 (subnode.name, fdt_obj.get_name(offset)))
Simon Glass7b75b442017-05-27 07:38:28 -0600313 subnode.Refresh(offset)
Simon Glass96066242018-07-06 10:27:27 -0600314 offset = fdt_obj.next_subnode(offset, QUIET_NOTFOUND)
Simon Glassf9b88b32018-07-06 10:27:29 -0600315 if offset != -libfdt.FDT_ERR_NOTFOUND:
316 raise ValueError('Internal error, offset == %d' % offset)
317
318 poffset = fdt_obj.first_property_offset(self._offset, QUIET_NOTFOUND)
319 while poffset >= 0:
320 p = fdt_obj.get_property_by_offset(poffset)
321 prop = self.props.get(p.name)
322 if not prop:
323 raise ValueError("Internal error, property '%s' missing, "
324 'offset %d' % (p.name, poffset))
325 prop.RefreshOffset(poffset)
326 poffset = fdt_obj.next_property_offset(poffset, QUIET_NOTFOUND)
Simon Glassf7a2aee2016-07-25 18:59:07 -0600327
Simon Glass2a70d892016-07-25 18:59:14 -0600328 def DeleteProp(self, prop_name):
329 """Delete a property of a node
330
Simon Glass7b75b442017-05-27 07:38:28 -0600331 The property is deleted and the offset cache is invalidated.
Simon Glass2a70d892016-07-25 18:59:14 -0600332
333 Args:
334 prop_name: Name of the property to delete
Simon Glass7b75b442017-05-27 07:38:28 -0600335 Raises:
336 ValueError if the property does not exist
Simon Glass2a70d892016-07-25 18:59:14 -0600337 """
Simon Glass96066242018-07-06 10:27:27 -0600338 CheckErr(self._fdt._fdt_obj.delprop(self.Offset(), prop_name),
Simon Glass7b75b442017-05-27 07:38:28 -0600339 "Node '%s': delete property: '%s'" % (self.path, prop_name))
340 del self.props[prop_name]
341 self._fdt.Invalidate()
Simon Glass2a70d892016-07-25 18:59:14 -0600342
Simon Glass116adec2018-07-06 10:27:38 -0600343 def AddZeroProp(self, prop_name):
344 """Add a new property to the device tree with an integer value of 0.
345
346 Args:
347 prop_name: Name of property
348 """
Simon Glass194b8d52019-05-17 22:00:33 -0600349 self.props[prop_name] = Prop(self, None, prop_name,
350 tools.GetBytes(0, 4))
Simon Glass116adec2018-07-06 10:27:38 -0600351
Simon Glass64349612018-09-14 04:57:16 -0600352 def AddEmptyProp(self, prop_name, len):
353 """Add a property with a fixed data size, for filling in later
354
355 The device tree is marked dirty so that the value will be written to
356 the blob on the next sync.
357
358 Args:
359 prop_name: Name of property
360 len: Length of data in property
361 """
Simon Glass194b8d52019-05-17 22:00:33 -0600362 value = tools.GetBytes(0, len)
Simon Glass64349612018-09-14 04:57:16 -0600363 self.props[prop_name] = Prop(self, None, prop_name, value)
364
Simon Glass116adec2018-07-06 10:27:38 -0600365 def SetInt(self, prop_name, val):
366 """Update an integer property int the device tree.
367
368 This is not allowed to change the size of the FDT.
369
Simon Glass64349612018-09-14 04:57:16 -0600370 The device tree is marked dirty so that the value will be written to
371 the blob on the next sync.
372
Simon Glass116adec2018-07-06 10:27:38 -0600373 Args:
374 prop_name: Name of property
375 val: Value to set
376 """
Simon Glassfa80c252018-09-14 04:57:13 -0600377 self.props[prop_name].SetInt(val)
378
Simon Glass64349612018-09-14 04:57:16 -0600379 def SetData(self, prop_name, val):
380 """Set the data value of a property
381
382 The device tree is marked dirty so that the value will be written to
383 the blob on the next sync.
384
385 Args:
386 prop_name: Name of property to set
387 val: Data value to set
388 """
389 self.props[prop_name].SetData(val)
390
391 def SetString(self, prop_name, val):
392 """Set the string value of a property
393
394 The device tree is marked dirty so that the value will be written to
395 the blob on the next sync.
396
397 Args:
398 prop_name: Name of property to set
399 val: String value to set (will be \0-terminated in DT)
400 """
Simon Glassf6b64812019-05-17 22:00:36 -0600401 if sys.version_info[0] >= 3: # pragma: no cover
402 val = bytes(val, 'utf-8')
403 self.props[prop_name].SetData(val + b'\0')
Simon Glass64349612018-09-14 04:57:16 -0600404
405 def AddString(self, prop_name, val):
406 """Add a new string property to a node
407
408 The device tree is marked dirty so that the value will be written to
409 the blob on the next sync.
410
411 Args:
412 prop_name: Name of property to add
413 val: String value of property
414 """
Simon Glassf6b64812019-05-17 22:00:36 -0600415 if sys.version_info[0] >= 3: # pragma: no cover
416 val = bytes(val, 'utf-8')
417 self.props[prop_name] = Prop(self, None, prop_name, val + b'\0')
Simon Glass64349612018-09-14 04:57:16 -0600418
Simon Glasse21c27a2018-09-14 04:57:15 -0600419 def AddSubnode(self, name):
Simon Glass64349612018-09-14 04:57:16 -0600420 """Add a new subnode to the node
421
422 Args:
423 name: name of node to add
424
425 Returns:
426 New subnode that was created
427 """
Simon Glasse21c27a2018-09-14 04:57:15 -0600428 path = self.path + '/' + name
429 subnode = Node(self._fdt, self, None, name, path)
430 self.subnodes.append(subnode)
431 return subnode
432
Simon Glassfa80c252018-09-14 04:57:13 -0600433 def Sync(self, auto_resize=False):
434 """Sync node changes back to the device tree
435
436 This updates the device tree blob with any changes to this node and its
437 subnodes since the last sync.
438
439 Args:
440 auto_resize: Resize the device tree automatically if it does not
441 have enough space for the update
442
443 Raises:
444 FdtException if auto_resize is False and there is not enough space
445 """
Simon Glasse21c27a2018-09-14 04:57:15 -0600446 if self._offset is None:
447 # The subnode doesn't exist yet, so add it
448 fdt_obj = self._fdt._fdt_obj
449 if auto_resize:
450 while True:
451 offset = fdt_obj.add_subnode(self.parent._offset, self.name,
452 (libfdt.NOSPACE,))
453 if offset != -libfdt.NOSPACE:
454 break
455 fdt_obj.resize(fdt_obj.totalsize() + 1024)
456 else:
457 offset = fdt_obj.add_subnode(self.parent._offset, self.name)
458 self._offset = offset
459
Simon Glassfa80c252018-09-14 04:57:13 -0600460 # Sync subnodes in reverse so that we don't disturb node offsets for
461 # nodes that are earlier in the DT. This avoids an O(n^2) rescan of
462 # node offsets.
463 for node in reversed(self.subnodes):
464 node.Sync(auto_resize)
465
466 # Sync properties now, whose offsets should not have been disturbed.
467 # We do this after subnodes, since this disturbs the offsets of these
Simon Glass63518052019-05-17 22:00:38 -0600468 # properties. Note that new properties will have an offset of None here,
469 # which Python 3 cannot sort against int. So use a large value instead
470 # to ensure that the new properties are added first.
471 prop_list = sorted(self.props.values(),
472 key=lambda prop: prop._offset or 1 << 31,
Simon Glassfa80c252018-09-14 04:57:13 -0600473 reverse=True)
474 for prop in prop_list:
475 prop.Sync(auto_resize)
Simon Glass116adec2018-07-06 10:27:38 -0600476
477
Simon Glassa06a34b2016-07-25 18:59:04 -0600478class Fdt:
Simon Glass7b75b442017-05-27 07:38:28 -0600479 """Provides simple access to a flat device tree blob using libfdts.
Simon Glassa06a34b2016-07-25 18:59:04 -0600480
481 Properties:
482 fname: Filename of fdt
483 _root: Root of device tree (a Node object)
484 """
485 def __init__(self, fname):
486 self._fname = fname
Simon Glass7b75b442017-05-27 07:38:28 -0600487 self._cached_offsets = False
Simon Glass09264e02017-08-29 14:15:52 -0600488 self.phandle_to_node = {}
Simon Glass7b75b442017-05-27 07:38:28 -0600489 if self._fname:
490 self._fname = fdt_util.EnsureCompiled(self._fname)
491
Simon Glass3e4b51e2019-05-14 15:53:43 -0600492 with open(self._fname, 'rb') as fd:
Simon Glass96066242018-07-06 10:27:27 -0600493 self._fdt_obj = libfdt.Fdt(fd.read())
Simon Glassf7a2aee2016-07-25 18:59:07 -0600494
Simon Glass746aee32018-09-14 04:57:17 -0600495 @staticmethod
496 def FromData(data):
497 """Create a new Fdt object from the given data
498
499 Args:
500 data: Device-tree data blob
501
502 Returns:
503 Fdt object containing the data
504 """
505 fdt = Fdt(None)
Simon Glassf6b64812019-05-17 22:00:36 -0600506 fdt._fdt_obj = libfdt.Fdt(bytes(data))
Simon Glass746aee32018-09-14 04:57:17 -0600507 return fdt
508
Simon Glass94a7c602018-07-17 13:25:46 -0600509 def LookupPhandle(self, phandle):
510 """Look up a phandle
511
512 Args:
513 phandle: Phandle to look up (int)
514
515 Returns:
516 Node object the phandle points to
517 """
518 return self.phandle_to_node.get(phandle)
519
Simon Glassf7a2aee2016-07-25 18:59:07 -0600520 def Scan(self, root='/'):
521 """Scan a device tree, building up a tree of Node objects
522
523 This fills in the self._root property
524
525 Args:
526 root: Ignored
527
528 TODO(sjg@chromium.org): Implement the 'root' parameter
529 """
Simon Glassf9b88b32018-07-06 10:27:29 -0600530 self._cached_offsets = True
Simon Glass979ab022017-08-29 14:15:47 -0600531 self._root = self.Node(self, None, 0, '/', '/')
Simon Glassf7a2aee2016-07-25 18:59:07 -0600532 self._root.Scan()
533
534 def GetRoot(self):
535 """Get the root Node of the device tree
536
537 Returns:
538 The root Node object
539 """
540 return self._root
541
542 def GetNode(self, path):
543 """Look up a node from its path
544
545 Args:
546 path: Path to look up, e.g. '/microcode/update@0'
547 Returns:
548 Node object, or None if not found
549 """
550 node = self._root
Simon Glassb9066ff2018-07-06 10:27:30 -0600551 parts = path.split('/')
552 if len(parts) < 2:
553 return None
554 for part in parts[1:]:
Simon Glass1d858882018-07-17 13:25:41 -0600555 node = node.FindNode(part)
Simon Glassf7a2aee2016-07-25 18:59:07 -0600556 if not node:
557 return None
558 return node
559
Simon Glassda5f7492016-07-25 18:59:15 -0600560 def Flush(self):
561 """Flush device tree changes back to the file
562
563 If the device tree has changed in memory, write it back to the file.
Simon Glassda5f7492016-07-25 18:59:15 -0600564 """
Simon Glass7b75b442017-05-27 07:38:28 -0600565 with open(self._fname, 'wb') as fd:
Simon Glass96066242018-07-06 10:27:27 -0600566 fd.write(self._fdt_obj.as_bytearray())
Simon Glassda5f7492016-07-25 18:59:15 -0600567
Simon Glassfa80c252018-09-14 04:57:13 -0600568 def Sync(self, auto_resize=False):
569 """Make sure any DT changes are written to the blob
570
571 Args:
572 auto_resize: Resize the device tree automatically if it does not
573 have enough space for the update
574
575 Raises:
576 FdtException if auto_resize is False and there is not enough space
577 """
578 self._root.Sync(auto_resize)
579 self.Invalidate()
580
Simon Glassda5f7492016-07-25 18:59:15 -0600581 def Pack(self):
582 """Pack the device tree down to its minimum size
583
584 When nodes and properties shrink or are deleted, wasted space can
Simon Glass7b75b442017-05-27 07:38:28 -0600585 build up in the device tree binary.
Simon Glassda5f7492016-07-25 18:59:15 -0600586 """
Simon Glass117f57b2018-07-06 10:27:26 -0600587 CheckErr(self._fdt_obj.pack(), 'pack')
588 self.Invalidate()
Simon Glass7b75b442017-05-27 07:38:28 -0600589
Simon Glass96066242018-07-06 10:27:27 -0600590 def GetContents(self):
Simon Glass7b75b442017-05-27 07:38:28 -0600591 """Get the contents of the FDT
592
593 Returns:
594 The FDT contents as a string of bytes
595 """
Simon Glassf6b64812019-05-17 22:00:36 -0600596 return bytes(self._fdt_obj.as_bytearray())
Simon Glass7b75b442017-05-27 07:38:28 -0600597
Simon Glass2ba98752018-07-06 10:27:24 -0600598 def GetFdtObj(self):
599 """Get the contents of the FDT
600
601 Returns:
602 The FDT contents as a libfdt.Fdt object
603 """
604 return self._fdt_obj
605
Simon Glass7b75b442017-05-27 07:38:28 -0600606 def GetProps(self, node):
607 """Get all properties from a node.
608
609 Args:
610 node: Full path to node name to look in.
611
612 Returns:
613 A dictionary containing all the properties, indexed by node name.
614 The entries are Prop objects.
615
616 Raises:
617 ValueError: if the node does not exist.
618 """
619 props_dict = {}
Simon Glass117f57b2018-07-06 10:27:26 -0600620 poffset = self._fdt_obj.first_property_offset(node._offset,
621 QUIET_NOTFOUND)
Simon Glass7b75b442017-05-27 07:38:28 -0600622 while poffset >= 0:
623 p = self._fdt_obj.get_property_by_offset(poffset)
Simon Glass3def0cf2018-07-06 10:27:20 -0600624 prop = Prop(node, poffset, p.name, p)
Simon Glass7b75b442017-05-27 07:38:28 -0600625 props_dict[prop.name] = prop
626
Simon Glass117f57b2018-07-06 10:27:26 -0600627 poffset = self._fdt_obj.next_property_offset(poffset,
628 QUIET_NOTFOUND)
Simon Glass7b75b442017-05-27 07:38:28 -0600629 return props_dict
630
631 def Invalidate(self):
632 """Mark our offset cache as invalid"""
633 self._cached_offsets = False
634
635 def CheckCache(self):
636 """Refresh the offset cache if needed"""
637 if self._cached_offsets:
638 return
639 self.Refresh()
640 self._cached_offsets = True
641
642 def Refresh(self):
643 """Refresh the offset cache"""
644 self._root.Refresh(0)
645
646 def GetStructOffset(self, offset):
647 """Get the file offset of a given struct offset
648
649 Args:
650 offset: Offset within the 'struct' region of the device tree
651 Returns:
652 Position of @offset within the device tree binary
653 """
Simon Glass117f57b2018-07-06 10:27:26 -0600654 return self._fdt_obj.off_dt_struct() + offset
Simon Glass7b75b442017-05-27 07:38:28 -0600655
656 @classmethod
Simon Glass979ab022017-08-29 14:15:47 -0600657 def Node(self, fdt, parent, offset, name, path):
Simon Glass7b75b442017-05-27 07:38:28 -0600658 """Create a new node
659
660 This is used by Fdt.Scan() to create a new node using the correct
661 class.
662
663 Args:
664 fdt: Fdt object
Simon Glass979ab022017-08-29 14:15:47 -0600665 parent: Parent node, or None if this is the root node
Simon Glass7b75b442017-05-27 07:38:28 -0600666 offset: Offset of node
667 name: Node name
668 path: Full path to node
669 """
Simon Glass979ab022017-08-29 14:15:47 -0600670 node = Node(fdt, parent, offset, name, path)
Simon Glass7b75b442017-05-27 07:38:28 -0600671 return node
Simon Glass99ed4a22017-05-27 07:38:30 -0600672
673def FdtScan(fname):
Simon Glassdfe5f5b2018-07-06 10:27:32 -0600674 """Returns a new Fdt object"""
Simon Glass99ed4a22017-05-27 07:38:30 -0600675 dtb = Fdt(fname)
676 dtb.Scan()
677 return dtb