blob: 03b86773d5f9b2337438ed377d3341118fb9c5f3 [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:
Simon Glasse144caf2020-10-03 11:31:27 -0600132 # Special handling to convert an int into bytes
133 if self.type == TYPE_INT and newprop.type == TYPE_BYTE:
134 if type(self.value) == list:
135 new_value = []
136 for val in self.value:
137 new_value += [tools.ToChar(by) for by in val]
138 else:
139 new_value = [tools.ToChar(by) for by in self.value]
140 self.value = new_value
Simon Glassc322a852016-07-25 18:59:06 -0600141 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:
Simon Glassc0639172020-07-09 18:39:44 -0600219 fdt_obj.resize(fdt_obj.totalsize() + 1024 +
220 len(self.bytes))
Simon Glassfa80c252018-09-14 04:57:13 -0600221 fdt_obj.setprop(node.Offset(), self.name, self.bytes)
222 else:
223 fdt_obj.setprop(node.Offset(), self.name, self.bytes)
224
225
Simon Glass7b75b442017-05-27 07:38:28 -0600226class Node:
Simon Glassa06a34b2016-07-25 18:59:04 -0600227 """A device tree node
228
229 Properties:
230 offset: Integer offset in the device tree
231 name: Device tree node tname
232 path: Full path to node, along with the node name itself
233 _fdt: Device tree object
234 subnodes: A list of subnodes for this node, each a Node object
235 props: A dict of properties for this node, each a Prop object.
236 Keyed by property name
237 """
Simon Glass979ab022017-08-29 14:15:47 -0600238 def __init__(self, fdt, parent, offset, name, path):
Simon Glassa06a34b2016-07-25 18:59:04 -0600239 self._fdt = fdt
Simon Glass979ab022017-08-29 14:15:47 -0600240 self.parent = parent
Simon Glassa06a34b2016-07-25 18:59:04 -0600241 self._offset = offset
242 self.name = name
243 self.path = path
244 self.subnodes = []
245 self.props = {}
246
Simon Glass94a7c602018-07-17 13:25:46 -0600247 def GetFdt(self):
248 """Get the Fdt object for this node
249
250 Returns:
251 Fdt object
252 """
253 return self._fdt
254
Simon Glass1d858882018-07-17 13:25:41 -0600255 def FindNode(self, name):
Simon Glassf7a2aee2016-07-25 18:59:07 -0600256 """Find a node given its name
257
258 Args:
259 name: Node name to look for
260 Returns:
261 Node object if found, else None
262 """
263 for subnode in self.subnodes:
264 if subnode.name == name:
265 return subnode
266 return None
267
Simon Glass7b75b442017-05-27 07:38:28 -0600268 def Offset(self):
269 """Returns the offset of a node, after checking the cache
Simon Glassf7a2aee2016-07-25 18:59:07 -0600270
Simon Glass7b75b442017-05-27 07:38:28 -0600271 This should be used instead of self._offset directly, to ensure that
272 the cache does not contain invalid offsets.
Simon Glassf7a2aee2016-07-25 18:59:07 -0600273 """
Simon Glass7b75b442017-05-27 07:38:28 -0600274 self._fdt.CheckCache()
275 return self._offset
276
277 def Scan(self):
278 """Scan a node's properties and subnodes
279
280 This fills in the props and subnodes properties, recursively
281 searching into subnodes so that the entire tree is built.
282 """
Simon Glass117f57b2018-07-06 10:27:26 -0600283 fdt_obj = self._fdt._fdt_obj
Simon Glass7b75b442017-05-27 07:38:28 -0600284 self.props = self._fdt.GetProps(self)
Simon Glass117f57b2018-07-06 10:27:26 -0600285 phandle = fdt_obj.get_phandle(self.Offset())
Simon Glass09264e02017-08-29 14:15:52 -0600286 if phandle:
Simon Glass117f57b2018-07-06 10:27:26 -0600287 self._fdt.phandle_to_node[phandle] = self
Simon Glass7b75b442017-05-27 07:38:28 -0600288
Simon Glass117f57b2018-07-06 10:27:26 -0600289 offset = fdt_obj.first_subnode(self.Offset(), QUIET_NOTFOUND)
Simon Glass7b75b442017-05-27 07:38:28 -0600290 while offset >= 0:
291 sep = '' if self.path[-1] == '/' else '/'
Simon Glass117f57b2018-07-06 10:27:26 -0600292 name = fdt_obj.get_name(offset)
Simon Glass7b75b442017-05-27 07:38:28 -0600293 path = self.path + sep + name
Simon Glass979ab022017-08-29 14:15:47 -0600294 node = Node(self._fdt, self, offset, name, path)
Simon Glass7b75b442017-05-27 07:38:28 -0600295 self.subnodes.append(node)
296
297 node.Scan()
Simon Glass117f57b2018-07-06 10:27:26 -0600298 offset = fdt_obj.next_subnode(offset, QUIET_NOTFOUND)
Simon Glass7b75b442017-05-27 07:38:28 -0600299
300 def Refresh(self, my_offset):
301 """Fix up the _offset for each node, recursively
302
303 Note: This does not take account of property offsets - these will not
304 be updated.
305 """
Simon Glass96066242018-07-06 10:27:27 -0600306 fdt_obj = self._fdt._fdt_obj
Simon Glass7b75b442017-05-27 07:38:28 -0600307 if self._offset != my_offset:
Simon Glass7b75b442017-05-27 07:38:28 -0600308 self._offset = my_offset
Simon Glass96066242018-07-06 10:27:27 -0600309 offset = fdt_obj.first_subnode(self._offset, QUIET_NOTFOUND)
Simon Glass7b75b442017-05-27 07:38:28 -0600310 for subnode in self.subnodes:
Simon Glassf9b88b32018-07-06 10:27:29 -0600311 if subnode.name != fdt_obj.get_name(offset):
312 raise ValueError('Internal error, node name mismatch %s != %s' %
313 (subnode.name, fdt_obj.get_name(offset)))
Simon Glass7b75b442017-05-27 07:38:28 -0600314 subnode.Refresh(offset)
Simon Glass96066242018-07-06 10:27:27 -0600315 offset = fdt_obj.next_subnode(offset, QUIET_NOTFOUND)
Simon Glassf9b88b32018-07-06 10:27:29 -0600316 if offset != -libfdt.FDT_ERR_NOTFOUND:
317 raise ValueError('Internal error, offset == %d' % offset)
318
319 poffset = fdt_obj.first_property_offset(self._offset, QUIET_NOTFOUND)
320 while poffset >= 0:
321 p = fdt_obj.get_property_by_offset(poffset)
322 prop = self.props.get(p.name)
323 if not prop:
324 raise ValueError("Internal error, property '%s' missing, "
325 'offset %d' % (p.name, poffset))
326 prop.RefreshOffset(poffset)
327 poffset = fdt_obj.next_property_offset(poffset, QUIET_NOTFOUND)
Simon Glassf7a2aee2016-07-25 18:59:07 -0600328
Simon Glass2a70d892016-07-25 18:59:14 -0600329 def DeleteProp(self, prop_name):
330 """Delete a property of a node
331
Simon Glass7b75b442017-05-27 07:38:28 -0600332 The property is deleted and the offset cache is invalidated.
Simon Glass2a70d892016-07-25 18:59:14 -0600333
334 Args:
335 prop_name: Name of the property to delete
Simon Glass7b75b442017-05-27 07:38:28 -0600336 Raises:
337 ValueError if the property does not exist
Simon Glass2a70d892016-07-25 18:59:14 -0600338 """
Simon Glass96066242018-07-06 10:27:27 -0600339 CheckErr(self._fdt._fdt_obj.delprop(self.Offset(), prop_name),
Simon Glass7b75b442017-05-27 07:38:28 -0600340 "Node '%s': delete property: '%s'" % (self.path, prop_name))
341 del self.props[prop_name]
342 self._fdt.Invalidate()
Simon Glass2a70d892016-07-25 18:59:14 -0600343
Simon Glass116adec2018-07-06 10:27:38 -0600344 def AddZeroProp(self, prop_name):
345 """Add a new property to the device tree with an integer value of 0.
346
347 Args:
348 prop_name: Name of property
349 """
Simon Glass194b8d52019-05-17 22:00:33 -0600350 self.props[prop_name] = Prop(self, None, prop_name,
351 tools.GetBytes(0, 4))
Simon Glass116adec2018-07-06 10:27:38 -0600352
Simon Glass64349612018-09-14 04:57:16 -0600353 def AddEmptyProp(self, prop_name, len):
354 """Add a property with a fixed data size, for filling in later
355
356 The device tree is marked dirty so that the value will be written to
357 the blob on the next sync.
358
359 Args:
360 prop_name: Name of property
361 len: Length of data in property
362 """
Simon Glass194b8d52019-05-17 22:00:33 -0600363 value = tools.GetBytes(0, len)
Simon Glass64349612018-09-14 04:57:16 -0600364 self.props[prop_name] = Prop(self, None, prop_name, value)
365
Simon Glassd9dad102019-07-20 12:23:37 -0600366 def _CheckProp(self, prop_name):
367 """Check if a property is present
368
369 Args:
370 prop_name: Name of property
371
372 Returns:
373 self
374
375 Raises:
376 ValueError if the property is missing
377 """
378 if prop_name not in self.props:
379 raise ValueError("Fdt '%s', node '%s': Missing property '%s'" %
380 (self._fdt._fname, self.path, prop_name))
381 return self
382
Simon Glass116adec2018-07-06 10:27:38 -0600383 def SetInt(self, prop_name, val):
384 """Update an integer property int the device tree.
385
386 This is not allowed to change the size of the FDT.
387
Simon Glass64349612018-09-14 04:57:16 -0600388 The device tree is marked dirty so that the value will be written to
389 the blob on the next sync.
390
Simon Glass116adec2018-07-06 10:27:38 -0600391 Args:
392 prop_name: Name of property
393 val: Value to set
394 """
Simon Glassd9dad102019-07-20 12:23:37 -0600395 self._CheckProp(prop_name).props[prop_name].SetInt(val)
Simon Glassfa80c252018-09-14 04:57:13 -0600396
Simon Glass64349612018-09-14 04:57:16 -0600397 def SetData(self, prop_name, val):
398 """Set the data value of a property
399
400 The device tree is marked dirty so that the value will be written to
401 the blob on the next sync.
402
403 Args:
404 prop_name: Name of property to set
405 val: Data value to set
406 """
Simon Glassd9dad102019-07-20 12:23:37 -0600407 self._CheckProp(prop_name).props[prop_name].SetData(val)
Simon Glass64349612018-09-14 04:57:16 -0600408
409 def SetString(self, prop_name, val):
410 """Set the string value of a property
411
412 The device tree is marked dirty so that the value will be written to
413 the blob on the next sync.
414
415 Args:
416 prop_name: Name of property to set
417 val: String value to set (will be \0-terminated in DT)
418 """
Simon Glassa90df2b2019-10-31 07:43:04 -0600419 if type(val) == str:
420 val = val.encode('utf-8')
Simon Glassd9dad102019-07-20 12:23:37 -0600421 self._CheckProp(prop_name).props[prop_name].SetData(val + b'\0')
Simon Glass64349612018-09-14 04:57:16 -0600422
Simon Glassc0639172020-07-09 18:39:44 -0600423 def AddData(self, prop_name, val):
424 """Add a new property to a node
425
426 The device tree is marked dirty so that the value will be written to
427 the blob on the next sync.
428
429 Args:
430 prop_name: Name of property to add
431 val: Bytes value of property
432 """
433 self.props[prop_name] = Prop(self, None, prop_name, val)
434
Simon Glass64349612018-09-14 04:57:16 -0600435 def AddString(self, prop_name, val):
436 """Add a new string property to a node
437
438 The device tree is marked dirty so that the value will be written to
439 the blob on the next sync.
440
441 Args:
442 prop_name: Name of property to add
443 val: String value of property
444 """
Simon Glassf6b64812019-05-17 22:00:36 -0600445 if sys.version_info[0] >= 3: # pragma: no cover
446 val = bytes(val, 'utf-8')
Simon Glassc0639172020-07-09 18:39:44 -0600447 self.AddData(prop_name, val + b'\0')
Simon Glass64349612018-09-14 04:57:16 -0600448
Simon Glasse21c27a2018-09-14 04:57:15 -0600449 def AddSubnode(self, name):
Simon Glass64349612018-09-14 04:57:16 -0600450 """Add a new subnode to the node
451
452 Args:
453 name: name of node to add
454
455 Returns:
456 New subnode that was created
457 """
Simon Glasse21c27a2018-09-14 04:57:15 -0600458 path = self.path + '/' + name
459 subnode = Node(self._fdt, self, None, name, path)
460 self.subnodes.append(subnode)
461 return subnode
462
Simon Glassfa80c252018-09-14 04:57:13 -0600463 def Sync(self, auto_resize=False):
464 """Sync node changes back to the device tree
465
466 This updates the device tree blob with any changes to this node and its
467 subnodes since the last sync.
468
469 Args:
470 auto_resize: Resize the device tree automatically if it does not
471 have enough space for the update
472
473 Raises:
474 FdtException if auto_resize is False and there is not enough space
475 """
Simon Glasse21c27a2018-09-14 04:57:15 -0600476 if self._offset is None:
477 # The subnode doesn't exist yet, so add it
478 fdt_obj = self._fdt._fdt_obj
479 if auto_resize:
480 while True:
481 offset = fdt_obj.add_subnode(self.parent._offset, self.name,
482 (libfdt.NOSPACE,))
483 if offset != -libfdt.NOSPACE:
484 break
485 fdt_obj.resize(fdt_obj.totalsize() + 1024)
486 else:
487 offset = fdt_obj.add_subnode(self.parent._offset, self.name)
488 self._offset = offset
489
Simon Glassfa80c252018-09-14 04:57:13 -0600490 # Sync subnodes in reverse so that we don't disturb node offsets for
491 # nodes that are earlier in the DT. This avoids an O(n^2) rescan of
492 # node offsets.
493 for node in reversed(self.subnodes):
494 node.Sync(auto_resize)
495
496 # Sync properties now, whose offsets should not have been disturbed.
497 # We do this after subnodes, since this disturbs the offsets of these
Simon Glass63518052019-05-17 22:00:38 -0600498 # properties. Note that new properties will have an offset of None here,
499 # which Python 3 cannot sort against int. So use a large value instead
500 # to ensure that the new properties are added first.
501 prop_list = sorted(self.props.values(),
502 key=lambda prop: prop._offset or 1 << 31,
Simon Glassfa80c252018-09-14 04:57:13 -0600503 reverse=True)
504 for prop in prop_list:
505 prop.Sync(auto_resize)
Simon Glass116adec2018-07-06 10:27:38 -0600506
507
Simon Glassa06a34b2016-07-25 18:59:04 -0600508class Fdt:
Simon Glass7b75b442017-05-27 07:38:28 -0600509 """Provides simple access to a flat device tree blob using libfdts.
Simon Glassa06a34b2016-07-25 18:59:04 -0600510
511 Properties:
512 fname: Filename of fdt
513 _root: Root of device tree (a Node object)
Simon Glass880e9ee2019-07-20 12:23:38 -0600514 name: Helpful name for this Fdt for the user (useful when creating the
515 DT from data rather than a file)
Simon Glassa06a34b2016-07-25 18:59:04 -0600516 """
517 def __init__(self, fname):
518 self._fname = fname
Simon Glass7b75b442017-05-27 07:38:28 -0600519 self._cached_offsets = False
Simon Glass09264e02017-08-29 14:15:52 -0600520 self.phandle_to_node = {}
Simon Glass880e9ee2019-07-20 12:23:38 -0600521 self.name = ''
Simon Glass7b75b442017-05-27 07:38:28 -0600522 if self._fname:
Simon Glass880e9ee2019-07-20 12:23:38 -0600523 self.name = self._fname
Simon Glass7b75b442017-05-27 07:38:28 -0600524 self._fname = fdt_util.EnsureCompiled(self._fname)
525
Simon Glass3e4b51e2019-05-14 15:53:43 -0600526 with open(self._fname, 'rb') as fd:
Simon Glass96066242018-07-06 10:27:27 -0600527 self._fdt_obj = libfdt.Fdt(fd.read())
Simon Glassf7a2aee2016-07-25 18:59:07 -0600528
Simon Glass746aee32018-09-14 04:57:17 -0600529 @staticmethod
Simon Glass880e9ee2019-07-20 12:23:38 -0600530 def FromData(data, name=''):
Simon Glass746aee32018-09-14 04:57:17 -0600531 """Create a new Fdt object from the given data
532
533 Args:
534 data: Device-tree data blob
Simon Glass880e9ee2019-07-20 12:23:38 -0600535 name: Helpful name for this Fdt for the user
Simon Glass746aee32018-09-14 04:57:17 -0600536
537 Returns:
538 Fdt object containing the data
539 """
540 fdt = Fdt(None)
Simon Glassf6b64812019-05-17 22:00:36 -0600541 fdt._fdt_obj = libfdt.Fdt(bytes(data))
Simon Glass880e9ee2019-07-20 12:23:38 -0600542 fdt.name = name
Simon Glass746aee32018-09-14 04:57:17 -0600543 return fdt
544
Simon Glass94a7c602018-07-17 13:25:46 -0600545 def LookupPhandle(self, phandle):
546 """Look up a phandle
547
548 Args:
549 phandle: Phandle to look up (int)
550
551 Returns:
552 Node object the phandle points to
553 """
554 return self.phandle_to_node.get(phandle)
555
Simon Glassf7a2aee2016-07-25 18:59:07 -0600556 def Scan(self, root='/'):
557 """Scan a device tree, building up a tree of Node objects
558
559 This fills in the self._root property
560
561 Args:
562 root: Ignored
563
564 TODO(sjg@chromium.org): Implement the 'root' parameter
565 """
Simon Glassf9b88b32018-07-06 10:27:29 -0600566 self._cached_offsets = True
Simon Glass979ab022017-08-29 14:15:47 -0600567 self._root = self.Node(self, None, 0, '/', '/')
Simon Glassf7a2aee2016-07-25 18:59:07 -0600568 self._root.Scan()
569
570 def GetRoot(self):
571 """Get the root Node of the device tree
572
573 Returns:
574 The root Node object
575 """
576 return self._root
577
578 def GetNode(self, path):
579 """Look up a node from its path
580
581 Args:
582 path: Path to look up, e.g. '/microcode/update@0'
583 Returns:
584 Node object, or None if not found
585 """
586 node = self._root
Simon Glassb9066ff2018-07-06 10:27:30 -0600587 parts = path.split('/')
588 if len(parts) < 2:
589 return None
Simon Glasse44bc832019-07-20 12:23:39 -0600590 if len(parts) == 2 and parts[1] == '':
591 return node
Simon Glassb9066ff2018-07-06 10:27:30 -0600592 for part in parts[1:]:
Simon Glass1d858882018-07-17 13:25:41 -0600593 node = node.FindNode(part)
Simon Glassf7a2aee2016-07-25 18:59:07 -0600594 if not node:
595 return None
596 return node
597
Simon Glassda5f7492016-07-25 18:59:15 -0600598 def Flush(self):
599 """Flush device tree changes back to the file
600
601 If the device tree has changed in memory, write it back to the file.
Simon Glassda5f7492016-07-25 18:59:15 -0600602 """
Simon Glass7b75b442017-05-27 07:38:28 -0600603 with open(self._fname, 'wb') as fd:
Simon Glass96066242018-07-06 10:27:27 -0600604 fd.write(self._fdt_obj.as_bytearray())
Simon Glassda5f7492016-07-25 18:59:15 -0600605
Simon Glassfa80c252018-09-14 04:57:13 -0600606 def Sync(self, auto_resize=False):
607 """Make sure any DT changes are written to the blob
608
609 Args:
610 auto_resize: Resize the device tree automatically if it does not
611 have enough space for the update
612
613 Raises:
614 FdtException if auto_resize is False and there is not enough space
615 """
616 self._root.Sync(auto_resize)
617 self.Invalidate()
618
Simon Glassda5f7492016-07-25 18:59:15 -0600619 def Pack(self):
620 """Pack the device tree down to its minimum size
621
622 When nodes and properties shrink or are deleted, wasted space can
Simon Glass7b75b442017-05-27 07:38:28 -0600623 build up in the device tree binary.
Simon Glassda5f7492016-07-25 18:59:15 -0600624 """
Simon Glass117f57b2018-07-06 10:27:26 -0600625 CheckErr(self._fdt_obj.pack(), 'pack')
626 self.Invalidate()
Simon Glass7b75b442017-05-27 07:38:28 -0600627
Simon Glass96066242018-07-06 10:27:27 -0600628 def GetContents(self):
Simon Glass7b75b442017-05-27 07:38:28 -0600629 """Get the contents of the FDT
630
631 Returns:
632 The FDT contents as a string of bytes
633 """
Simon Glassf6b64812019-05-17 22:00:36 -0600634 return bytes(self._fdt_obj.as_bytearray())
Simon Glass7b75b442017-05-27 07:38:28 -0600635
Simon Glass2ba98752018-07-06 10:27:24 -0600636 def GetFdtObj(self):
637 """Get the contents of the FDT
638
639 Returns:
640 The FDT contents as a libfdt.Fdt object
641 """
642 return self._fdt_obj
643
Simon Glass7b75b442017-05-27 07:38:28 -0600644 def GetProps(self, node):
645 """Get all properties from a node.
646
647 Args:
648 node: Full path to node name to look in.
649
650 Returns:
651 A dictionary containing all the properties, indexed by node name.
652 The entries are Prop objects.
653
654 Raises:
655 ValueError: if the node does not exist.
656 """
657 props_dict = {}
Simon Glass117f57b2018-07-06 10:27:26 -0600658 poffset = self._fdt_obj.first_property_offset(node._offset,
659 QUIET_NOTFOUND)
Simon Glass7b75b442017-05-27 07:38:28 -0600660 while poffset >= 0:
661 p = self._fdt_obj.get_property_by_offset(poffset)
Simon Glass3def0cf2018-07-06 10:27:20 -0600662 prop = Prop(node, poffset, p.name, p)
Simon Glass7b75b442017-05-27 07:38:28 -0600663 props_dict[prop.name] = prop
664
Simon Glass117f57b2018-07-06 10:27:26 -0600665 poffset = self._fdt_obj.next_property_offset(poffset,
666 QUIET_NOTFOUND)
Simon Glass7b75b442017-05-27 07:38:28 -0600667 return props_dict
668
669 def Invalidate(self):
670 """Mark our offset cache as invalid"""
671 self._cached_offsets = False
672
673 def CheckCache(self):
674 """Refresh the offset cache if needed"""
675 if self._cached_offsets:
676 return
677 self.Refresh()
678 self._cached_offsets = True
679
680 def Refresh(self):
681 """Refresh the offset cache"""
682 self._root.Refresh(0)
683
684 def GetStructOffset(self, offset):
685 """Get the file offset of a given struct offset
686
687 Args:
688 offset: Offset within the 'struct' region of the device tree
689 Returns:
690 Position of @offset within the device tree binary
691 """
Simon Glass117f57b2018-07-06 10:27:26 -0600692 return self._fdt_obj.off_dt_struct() + offset
Simon Glass7b75b442017-05-27 07:38:28 -0600693
694 @classmethod
Simon Glass979ab022017-08-29 14:15:47 -0600695 def Node(self, fdt, parent, offset, name, path):
Simon Glass7b75b442017-05-27 07:38:28 -0600696 """Create a new node
697
698 This is used by Fdt.Scan() to create a new node using the correct
699 class.
700
701 Args:
702 fdt: Fdt object
Simon Glass979ab022017-08-29 14:15:47 -0600703 parent: Parent node, or None if this is the root node
Simon Glass7b75b442017-05-27 07:38:28 -0600704 offset: Offset of node
705 name: Node name
706 path: Full path to node
707 """
Simon Glass979ab022017-08-29 14:15:47 -0600708 node = Node(fdt, parent, offset, name, path)
Simon Glass7b75b442017-05-27 07:38:28 -0600709 return node
Simon Glass99ed4a22017-05-27 07:38:30 -0600710
Simon Glassf6e02492019-07-20 12:24:08 -0600711 def GetFilename(self):
712 """Get the filename of the device tree
713
714 Returns:
715 String filename
716 """
717 return self._fname
718
Simon Glass99ed4a22017-05-27 07:38:30 -0600719def FdtScan(fname):
Simon Glassdfe5f5b2018-07-06 10:27:32 -0600720 """Returns a new Fdt object"""
Simon Glass99ed4a22017-05-27 07:38:30 -0600721 dtb = Fdt(fname)
722 dtb.Scan()
723 return dtb