blob: 0b20d52f3136c5019e1ca646435b85b663036ae0 [file] [log] [blame]
Simon Glass1f941b62016-07-25 18:59:04 -06001#!/usr/bin/python
Tom Rini10e47792018-05-06 17:58:06 -04002# SPDX-License-Identifier: GPL-2.0+
Simon Glass1f941b62016-07-25 18:59:04 -06003#
4# Copyright (C) 2016 Google, Inc
5# Written by Simon Glass <sjg@chromium.org>
6#
Simon Glass1f941b62016-07-25 18:59:04 -06007
Simon Glassc9a032c2020-11-08 20:36:17 -07008from enum import IntEnum
Simon Glass1f941b62016-07-25 18:59:04 -06009import struct
10import sys
11
Simon Glassa997ea52020-04-17 18:09:04 -060012from dtoc import fdt_util
Simon Glass059ae522017-05-27 07:38:28 -060013import libfdt
Simon Glass151aeed2018-07-06 10:27:26 -060014from libfdt import QUIET_NOTFOUND
Simon Glass131444f2023-02-23 18:18:04 -070015from u_boot_pylib import tools
Simon Glassadf8e052023-07-18 07:24:02 -060016from u_boot_pylib import tout
Simon Glass1f941b62016-07-25 18:59:04 -060017
Simon Glassfc5569f2023-08-02 09:23:13 -060018# Temporary hack
19IGNORE_DUP_PHANDLES = False
20
Simon Glass1f941b62016-07-25 18:59:04 -060021# This deals with a device tree, presenting it as an assortment of Node and
22# Prop objects, representing nodes and properties, respectively. This file
Simon Glassa9440932017-05-27 07:38:30 -060023# contains the base classes and defines the high-level API. You can use
24# FdtScan() as a convenience function to create and scan an Fdt.
Simon Glass059ae522017-05-27 07:38:28 -060025
26# This implementation uses a libfdt Python library to access the device tree,
27# so it is fairly efficient.
Simon Glass1f941b62016-07-25 18:59:04 -060028
Simon Glassb1a5e262016-07-25 18:59:05 -060029# A list of types we support
Simon Glassc9a032c2020-11-08 20:36:17 -070030class Type(IntEnum):
Simon Glassfd4078e2021-07-28 19:23:09 -060031 # Types in order from widest to narrowest
Simon Glassc9a032c2020-11-08 20:36:17 -070032 (BYTE, INT, STRING, BOOL, INT64) = range(5)
33
Simon Glassfd4078e2021-07-28 19:23:09 -060034 def needs_widening(self, other):
35 """Check if this type needs widening to hold a value from another type
Simon Glassc9a032c2020-11-08 20:36:17 -070036
Simon Glassfd4078e2021-07-28 19:23:09 -060037 A wider type is one that can hold a wider array of information than
38 another one, or is less restrictive, so it can hold the information of
39 another type as well as its own. This is similar to the concept of
40 type-widening in C.
Simon Glassc9a032c2020-11-08 20:36:17 -070041
42 This uses a simple arithmetic comparison, since type values are in order
Simon Glassfd4078e2021-07-28 19:23:09 -060043 from widest (BYTE) to narrowest (INT64).
Simon Glassc9a032c2020-11-08 20:36:17 -070044
45 Args:
46 other: Other type to compare against
47
48 Return:
49 True if the other type is wider
50 """
51 return self.value > other.value
Simon Glassb1a5e262016-07-25 18:59:05 -060052
Simon Glass1f941b62016-07-25 18:59:04 -060053def CheckErr(errnum, msg):
54 if errnum:
55 raise ValueError('Error %d: %s: %s' %
56 (errnum, libfdt.fdt_strerror(errnum), msg))
57
Simon Glassb73d4482019-05-17 22:00:34 -060058
Simon Glassd8f593f2019-05-17 22:00:35 -060059def BytesToValue(data):
Simon Glassb73d4482019-05-17 22:00:34 -060060 """Converts a string of bytes into a type and value
61
62 Args:
Simon Glassd8f593f2019-05-17 22:00:35 -060063 A bytes value (which on Python 2 is an alias for str)
Simon Glassb73d4482019-05-17 22:00:34 -060064
65 Return:
66 A tuple:
67 Type of data
68 Data, either a single element or a list of elements. Each element
69 is one of:
Simon Glassc9a032c2020-11-08 20:36:17 -070070 Type.STRING: str/bytes value from the property
71 Type.INT: a byte-swapped integer stored as a 4-byte str/bytes
72 Type.BYTE: a byte stored as a single-byte str/bytes
Simon Glassb73d4482019-05-17 22:00:34 -060073 """
Simon Glassd8f593f2019-05-17 22:00:35 -060074 data = bytes(data)
75 size = len(data)
76 strings = data.split(b'\0')
Simon Glassb73d4482019-05-17 22:00:34 -060077 is_string = True
78 count = len(strings) - 1
Simon Glassd8f593f2019-05-17 22:00:35 -060079 if count > 0 and not len(strings[-1]):
Simon Glassb73d4482019-05-17 22:00:34 -060080 for string in strings[:-1]:
81 if not string:
82 is_string = False
83 break
84 for ch in string:
Simon Glassd8f593f2019-05-17 22:00:35 -060085 if ch < 32 or ch > 127:
Simon Glassb73d4482019-05-17 22:00:34 -060086 is_string = False
87 break
88 else:
89 is_string = False
90 if is_string:
Simon Glassd8f593f2019-05-17 22:00:35 -060091 if count == 1:
Simon Glassc9a032c2020-11-08 20:36:17 -070092 return Type.STRING, strings[0].decode()
Simon Glassb73d4482019-05-17 22:00:34 -060093 else:
Simon Glassc9a032c2020-11-08 20:36:17 -070094 return Type.STRING, [s.decode() for s in strings[:-1]]
Simon Glassb73d4482019-05-17 22:00:34 -060095 if size % 4:
96 if size == 1:
Simon Glass632b84c2020-11-08 20:36:20 -070097 return Type.BYTE, chr(data[0])
Simon Glassb73d4482019-05-17 22:00:34 -060098 else:
Simon Glass632b84c2020-11-08 20:36:20 -070099 return Type.BYTE, [chr(ch) for ch in list(data)]
Simon Glassb73d4482019-05-17 22:00:34 -0600100 val = []
101 for i in range(0, size, 4):
Simon Glassd8f593f2019-05-17 22:00:35 -0600102 val.append(data[i:i + 4])
Simon Glassb73d4482019-05-17 22:00:34 -0600103 if size == 4:
Simon Glassc9a032c2020-11-08 20:36:17 -0700104 return Type.INT, val[0]
Simon Glassb73d4482019-05-17 22:00:34 -0600105 else:
Simon Glassc9a032c2020-11-08 20:36:17 -0700106 return Type.INT, val
Simon Glassb73d4482019-05-17 22:00:34 -0600107
108
Simon Glass059ae522017-05-27 07:38:28 -0600109class Prop:
Simon Glass1f941b62016-07-25 18:59:04 -0600110 """A device tree property
111
112 Properties:
Simon Glass459df472021-03-21 18:24:35 +1300113 node: Node containing this property
114 offset: Offset of the property (None if still to be synced)
Simon Glass1f941b62016-07-25 18:59:04 -0600115 name: Property name (as per the device tree)
116 value: Property value as a string of bytes, or a list of strings of
117 bytes
118 type: Value type
119 """
Simon Glass74b472e2019-05-17 22:00:37 -0600120 def __init__(self, node, offset, name, data):
Simon Glass1f941b62016-07-25 18:59:04 -0600121 self._node = node
122 self._offset = offset
123 self.name = name
124 self.value = None
Simon Glass74b472e2019-05-17 22:00:37 -0600125 self.bytes = bytes(data)
Simon Glass459df472021-03-21 18:24:35 +1300126 self.dirty = offset is None
Simon Glass74b472e2019-05-17 22:00:37 -0600127 if not data:
Simon Glassc9a032c2020-11-08 20:36:17 -0700128 self.type = Type.BOOL
Simon Glass059ae522017-05-27 07:38:28 -0600129 self.value = True
130 return
Simon Glass74b472e2019-05-17 22:00:37 -0600131 self.type, self.value = BytesToValue(bytes(data))
Simon Glass1f941b62016-07-25 18:59:04 -0600132
Simon Glass4df8a0c2018-07-06 10:27:29 -0600133 def RefreshOffset(self, poffset):
134 self._offset = poffset
135
Simon Glass248ccd22016-07-25 18:59:06 -0600136 def Widen(self, newprop):
137 """Figure out which property type is more general
138
139 Given a current property and a new property, this function returns the
140 one that is less specific as to type. The less specific property will
141 be ble to represent the data in the more specific property. This is
142 used for things like:
143
144 node1 {
145 compatible = "fred";
146 value = <1>;
147 };
148 node1 {
149 compatible = "fred";
150 value = <1 2>;
151 };
152
153 He we want to use an int array for 'value'. The first property
154 suggests that a single int is enough, but the second one shows that
155 it is not. Calling this function with these two propertes would
156 update the current property to be like the second, since it is less
157 specific.
158 """
Simon Glassfd4078e2021-07-28 19:23:09 -0600159 if self.type.needs_widening(newprop.type):
Simon Glass43118322021-07-28 19:23:11 -0600160
161 # A boolean has an empty value: if it exists it is True and if not
162 # it is False. So when widening we always start with an empty list
163 # since the only valid integer property would be an empty list of
164 # integers.
165 # e.g. this is a boolean:
166 # some-prop;
167 # and it would be widened to int list by:
168 # some-prop = <1 2>;
169 if self.type == Type.BOOL:
170 self.type = Type.INT
171 self.value = [self.GetEmpty(self.type)]
Simon Glassc9a032c2020-11-08 20:36:17 -0700172 if self.type == Type.INT and newprop.type == Type.BYTE:
Simon Glass8034e4d2020-10-03 11:31:27 -0600173 if type(self.value) == list:
174 new_value = []
175 for val in self.value:
Simon Glass632b84c2020-11-08 20:36:20 -0700176 new_value += [chr(by) for by in val]
Simon Glass8034e4d2020-10-03 11:31:27 -0600177 else:
Simon Glass632b84c2020-11-08 20:36:20 -0700178 new_value = [chr(by) for by in self.value]
Simon Glass8034e4d2020-10-03 11:31:27 -0600179 self.value = new_value
Simon Glass248ccd22016-07-25 18:59:06 -0600180 self.type = newprop.type
181
Simon Glassa7d66982021-07-28 19:23:10 -0600182 if type(newprop.value) == list:
183 if type(self.value) != list:
184 self.value = [self.value]
Simon Glass248ccd22016-07-25 18:59:06 -0600185
Simon Glassa7d66982021-07-28 19:23:10 -0600186 if len(newprop.value) > len(self.value):
187 val = self.GetEmpty(self.type)
188 while len(self.value) < len(newprop.value):
189 self.value.append(val)
Simon Glass248ccd22016-07-25 18:59:06 -0600190
Simon Glass0ed50752018-07-06 10:27:24 -0600191 @classmethod
Simon Glassb1a5e262016-07-25 18:59:05 -0600192 def GetEmpty(self, type):
193 """Get an empty / zero value of the given type
194
195 Returns:
196 A single value of the given type
197 """
Simon Glassc9a032c2020-11-08 20:36:17 -0700198 if type == Type.BYTE:
Simon Glassb1a5e262016-07-25 18:59:05 -0600199 return chr(0)
Simon Glassc9a032c2020-11-08 20:36:17 -0700200 elif type == Type.INT:
Simon Glassc330b142018-09-14 04:57:14 -0600201 return struct.pack('>I', 0);
Simon Glassc9a032c2020-11-08 20:36:17 -0700202 elif type == Type.STRING:
Simon Glassb1a5e262016-07-25 18:59:05 -0600203 return ''
204 else:
205 return True
206
Simon Glass54cec6d2016-07-25 18:59:16 -0600207 def GetOffset(self):
208 """Get the offset of a property
209
Simon Glass54cec6d2016-07-25 18:59:16 -0600210 Returns:
Simon Glass059ae522017-05-27 07:38:28 -0600211 The offset of the property (struct fdt_property) within the file
Simon Glass54cec6d2016-07-25 18:59:16 -0600212 """
Simon Glass4df8a0c2018-07-06 10:27:29 -0600213 self._node._fdt.CheckCache()
Simon Glass059ae522017-05-27 07:38:28 -0600214 return self._node._fdt.GetStructOffset(self._offset)
Simon Glass54cec6d2016-07-25 18:59:16 -0600215
Simon Glasseddd7292018-09-14 04:57:13 -0600216 def SetInt(self, val):
217 """Set the integer value of the property
218
219 The device tree is marked dirty so that the value will be written to
220 the block on the next sync.
221
222 Args:
223 val: Integer value (32-bit, single cell)
224 """
225 self.bytes = struct.pack('>I', val);
Simon Glassa2b9acb2018-10-01 12:22:49 -0600226 self.value = self.bytes
Simon Glassc9a032c2020-11-08 20:36:17 -0700227 self.type = Type.INT
Simon Glasseddd7292018-09-14 04:57:13 -0600228 self.dirty = True
229
Simon Glassccd25262018-09-14 04:57:16 -0600230 def SetData(self, bytes):
231 """Set the value of a property as bytes
232
233 Args:
234 bytes: New property value to set
235 """
Simon Glass1cd40082019-05-17 22:00:36 -0600236 self.bytes = bytes
Simon Glassb73d4482019-05-17 22:00:34 -0600237 self.type, self.value = BytesToValue(bytes)
Simon Glassccd25262018-09-14 04:57:16 -0600238 self.dirty = True
239
Simon Glasseddd7292018-09-14 04:57:13 -0600240 def Sync(self, auto_resize=False):
241 """Sync property changes back to the device tree
242
243 This updates the device tree blob with any changes to this property
244 since the last sync.
245
246 Args:
247 auto_resize: Resize the device tree automatically if it does not
248 have enough space for the update
249
250 Raises:
251 FdtException if auto_resize is False and there is not enough space
252 """
Simon Glass459df472021-03-21 18:24:35 +1300253 if self.dirty:
Simon Glasseddd7292018-09-14 04:57:13 -0600254 node = self._node
Simon Glass638567d2023-07-22 21:43:54 -0600255 tout.debug(f'sync {node.path}: {self.name}')
Simon Glasseddd7292018-09-14 04:57:13 -0600256 fdt_obj = node._fdt._fdt_obj
Simon Glassd8bee462021-03-21 18:24:39 +1300257 node_name = fdt_obj.get_name(node._offset)
258 if node_name and node_name != node.name:
259 raise ValueError("Internal error, node '%s' name mismatch '%s'" %
260 (node.path, node_name))
261
Simon Glasseddd7292018-09-14 04:57:13 -0600262 if auto_resize:
263 while fdt_obj.setprop(node.Offset(), self.name, self.bytes,
264 (libfdt.NOSPACE,)) == -libfdt.NOSPACE:
Simon Glassf67c99c2020-07-09 18:39:44 -0600265 fdt_obj.resize(fdt_obj.totalsize() + 1024 +
266 len(self.bytes))
Simon Glasseddd7292018-09-14 04:57:13 -0600267 fdt_obj.setprop(node.Offset(), self.name, self.bytes)
268 else:
269 fdt_obj.setprop(node.Offset(), self.name, self.bytes)
Simon Glass459df472021-03-21 18:24:35 +1300270 self.dirty = False
Simon Glasseddd7292018-09-14 04:57:13 -0600271
Simon Glassadf8e052023-07-18 07:24:02 -0600272 def purge(self):
273 """Set a property offset to None
274
275 The property remains in the tree structure and will be recreated when
276 the FDT is synced
277 """
278 self._offset = None
Simon Glass02e505e2023-07-22 21:43:53 -0600279 self.dirty = True
Simon Glasseddd7292018-09-14 04:57:13 -0600280
Simon Glass059ae522017-05-27 07:38:28 -0600281class Node:
Simon Glass1f941b62016-07-25 18:59:04 -0600282 """A device tree node
283
284 Properties:
Simon Glass459df472021-03-21 18:24:35 +1300285 parent: Parent Node
286 offset: Integer offset in the device tree (None if to be synced)
Simon Glass1f941b62016-07-25 18:59:04 -0600287 name: Device tree node tname
288 path: Full path to node, along with the node name itself
289 _fdt: Device tree object
290 subnodes: A list of subnodes for this node, each a Node object
291 props: A dict of properties for this node, each a Prop object.
292 Keyed by property name
293 """
Simon Glass80ef7052017-08-29 14:15:47 -0600294 def __init__(self, fdt, parent, offset, name, path):
Simon Glass1f941b62016-07-25 18:59:04 -0600295 self._fdt = fdt
Simon Glass80ef7052017-08-29 14:15:47 -0600296 self.parent = parent
Simon Glass1f941b62016-07-25 18:59:04 -0600297 self._offset = offset
298 self.name = name
299 self.path = path
300 self.subnodes = []
301 self.props = {}
302
Simon Glasse2d65282018-07-17 13:25:46 -0600303 def GetFdt(self):
304 """Get the Fdt object for this node
305
306 Returns:
307 Fdt object
308 """
309 return self._fdt
310
Simon Glassaa1a5d72018-07-17 13:25:41 -0600311 def FindNode(self, name):
Simon Glasscc346a72016-07-25 18:59:07 -0600312 """Find a node given its name
313
314 Args:
315 name: Node name to look for
316 Returns:
317 Node object if found, else None
318 """
319 for subnode in self.subnodes:
320 if subnode.name == name:
321 return subnode
322 return None
323
Simon Glass059ae522017-05-27 07:38:28 -0600324 def Offset(self):
325 """Returns the offset of a node, after checking the cache
326
327 This should be used instead of self._offset directly, to ensure that
328 the cache does not contain invalid offsets.
329 """
330 self._fdt.CheckCache()
331 return self._offset
332
Simon Glasscc346a72016-07-25 18:59:07 -0600333 def Scan(self):
Simon Glass059ae522017-05-27 07:38:28 -0600334 """Scan a node's properties and subnodes
Simon Glasscc346a72016-07-25 18:59:07 -0600335
Simon Glass059ae522017-05-27 07:38:28 -0600336 This fills in the props and subnodes properties, recursively
337 searching into subnodes so that the entire tree is built.
Simon Glasscc346a72016-07-25 18:59:07 -0600338 """
Simon Glass151aeed2018-07-06 10:27:26 -0600339 fdt_obj = self._fdt._fdt_obj
Simon Glass059ae522017-05-27 07:38:28 -0600340 self.props = self._fdt.GetProps(self)
Simon Glass151aeed2018-07-06 10:27:26 -0600341 phandle = fdt_obj.get_phandle(self.Offset())
Simon Glassa3f94442017-08-29 14:15:52 -0600342 if phandle:
Simon Glass48ec1ff2023-07-22 21:43:55 -0600343 dup = self._fdt.phandle_to_node.get(phandle)
344 if dup:
Simon Glassfc5569f2023-08-02 09:23:13 -0600345 if not IGNORE_DUP_PHANDLES:
346 raise ValueError(
347 f'Duplicate phandle {phandle} in nodes {dup.path} and {self.path}')
348 else:
349 self._fdt.phandle_to_node[phandle] = self
Simon Glass059ae522017-05-27 07:38:28 -0600350
Simon Glass151aeed2018-07-06 10:27:26 -0600351 offset = fdt_obj.first_subnode(self.Offset(), QUIET_NOTFOUND)
Simon Glass059ae522017-05-27 07:38:28 -0600352 while offset >= 0:
353 sep = '' if self.path[-1] == '/' else '/'
Simon Glass151aeed2018-07-06 10:27:26 -0600354 name = fdt_obj.get_name(offset)
Simon Glass059ae522017-05-27 07:38:28 -0600355 path = self.path + sep + name
Simon Glass80ef7052017-08-29 14:15:47 -0600356 node = Node(self._fdt, self, offset, name, path)
Simon Glass059ae522017-05-27 07:38:28 -0600357 self.subnodes.append(node)
358
359 node.Scan()
Simon Glass151aeed2018-07-06 10:27:26 -0600360 offset = fdt_obj.next_subnode(offset, QUIET_NOTFOUND)
Simon Glass059ae522017-05-27 07:38:28 -0600361
362 def Refresh(self, my_offset):
363 """Fix up the _offset for each node, recursively
364
365 Note: This does not take account of property offsets - these will not
366 be updated.
367 """
Simon Glass792d2392018-07-06 10:27:27 -0600368 fdt_obj = self._fdt._fdt_obj
Simon Glass059ae522017-05-27 07:38:28 -0600369 if self._offset != my_offset:
Simon Glass059ae522017-05-27 07:38:28 -0600370 self._offset = my_offset
Simon Glassd8bee462021-03-21 18:24:39 +1300371 name = fdt_obj.get_name(self._offset)
372 if name and self.name != name:
373 raise ValueError("Internal error, node '%s' name mismatch '%s'" %
374 (self.path, name))
375
Simon Glass792d2392018-07-06 10:27:27 -0600376 offset = fdt_obj.first_subnode(self._offset, QUIET_NOTFOUND)
Simon Glass059ae522017-05-27 07:38:28 -0600377 for subnode in self.subnodes:
Simon Glass00238612022-02-08 11:49:52 -0700378 if subnode._offset is None:
379 continue
Simon Glass4df8a0c2018-07-06 10:27:29 -0600380 if subnode.name != fdt_obj.get_name(offset):
381 raise ValueError('Internal error, node name mismatch %s != %s' %
382 (subnode.name, fdt_obj.get_name(offset)))
Simon Glass059ae522017-05-27 07:38:28 -0600383 subnode.Refresh(offset)
Simon Glass792d2392018-07-06 10:27:27 -0600384 offset = fdt_obj.next_subnode(offset, QUIET_NOTFOUND)
Simon Glass4df8a0c2018-07-06 10:27:29 -0600385 if offset != -libfdt.FDT_ERR_NOTFOUND:
386 raise ValueError('Internal error, offset == %d' % offset)
387
388 poffset = fdt_obj.first_property_offset(self._offset, QUIET_NOTFOUND)
389 while poffset >= 0:
390 p = fdt_obj.get_property_by_offset(poffset)
391 prop = self.props.get(p.name)
392 if not prop:
Simon Glass93f18a12021-03-21 18:24:34 +1300393 raise ValueError("Internal error, node '%s' property '%s' missing, "
394 'offset %d' % (self.path, p.name, poffset))
Simon Glass4df8a0c2018-07-06 10:27:29 -0600395 prop.RefreshOffset(poffset)
396 poffset = fdt_obj.next_property_offset(poffset, QUIET_NOTFOUND)
Simon Glasscc346a72016-07-25 18:59:07 -0600397
Simon Glassc719e422016-07-25 18:59:14 -0600398 def DeleteProp(self, prop_name):
399 """Delete a property of a node
400
Simon Glass059ae522017-05-27 07:38:28 -0600401 The property is deleted and the offset cache is invalidated.
Simon Glassc719e422016-07-25 18:59:14 -0600402
403 Args:
404 prop_name: Name of the property to delete
Simon Glass059ae522017-05-27 07:38:28 -0600405 Raises:
406 ValueError if the property does not exist
Simon Glassc719e422016-07-25 18:59:14 -0600407 """
Simon Glass792d2392018-07-06 10:27:27 -0600408 CheckErr(self._fdt._fdt_obj.delprop(self.Offset(), prop_name),
Simon Glass059ae522017-05-27 07:38:28 -0600409 "Node '%s': delete property: '%s'" % (self.path, prop_name))
410 del self.props[prop_name]
411 self._fdt.Invalidate()
Simon Glassc719e422016-07-25 18:59:14 -0600412
Simon Glasse80c5562018-07-06 10:27:38 -0600413 def AddZeroProp(self, prop_name):
414 """Add a new property to the device tree with an integer value of 0.
415
416 Args:
417 prop_name: Name of property
418 """
Simon Glass6bbfafb2019-05-17 22:00:33 -0600419 self.props[prop_name] = Prop(self, None, prop_name,
Simon Glass80025522022-01-29 14:14:04 -0700420 tools.get_bytes(0, 4))
Simon Glasse80c5562018-07-06 10:27:38 -0600421
Simon Glassccd25262018-09-14 04:57:16 -0600422 def AddEmptyProp(self, prop_name, len):
423 """Add a property with a fixed data size, for filling in later
424
425 The device tree is marked dirty so that the value will be written to
426 the blob on the next sync.
427
428 Args:
429 prop_name: Name of property
430 len: Length of data in property
431 """
Simon Glass80025522022-01-29 14:14:04 -0700432 value = tools.get_bytes(0, len)
Simon Glassccd25262018-09-14 04:57:16 -0600433 self.props[prop_name] = Prop(self, None, prop_name, value)
434
Simon Glassa683a5f2019-07-20 12:23:37 -0600435 def _CheckProp(self, prop_name):
436 """Check if a property is present
437
438 Args:
439 prop_name: Name of property
440
441 Returns:
442 self
443
444 Raises:
445 ValueError if the property is missing
446 """
447 if prop_name not in self.props:
448 raise ValueError("Fdt '%s', node '%s': Missing property '%s'" %
449 (self._fdt._fname, self.path, prop_name))
450 return self
451
Simon Glasse80c5562018-07-06 10:27:38 -0600452 def SetInt(self, prop_name, val):
453 """Update an integer property int the device tree.
454
455 This is not allowed to change the size of the FDT.
456
Simon Glassccd25262018-09-14 04:57:16 -0600457 The device tree is marked dirty so that the value will be written to
458 the blob on the next sync.
459
Simon Glasse80c5562018-07-06 10:27:38 -0600460 Args:
461 prop_name: Name of property
462 val: Value to set
463 """
Simon Glassa683a5f2019-07-20 12:23:37 -0600464 self._CheckProp(prop_name).props[prop_name].SetInt(val)
Simon Glasseddd7292018-09-14 04:57:13 -0600465
Simon Glassccd25262018-09-14 04:57:16 -0600466 def SetData(self, prop_name, val):
467 """Set the data value of a property
468
469 The device tree is marked dirty so that the value will be written to
470 the blob on the next sync.
471
472 Args:
473 prop_name: Name of property to set
474 val: Data value to set
475 """
Simon Glassa683a5f2019-07-20 12:23:37 -0600476 self._CheckProp(prop_name).props[prop_name].SetData(val)
Simon Glassccd25262018-09-14 04:57:16 -0600477
478 def SetString(self, prop_name, val):
479 """Set the string value of a property
480
481 The device tree is marked dirty so that the value will be written to
482 the blob on the next sync.
483
484 Args:
485 prop_name: Name of property to set
486 val: String value to set (will be \0-terminated in DT)
487 """
Simon Glass39e22332019-10-31 07:43:04 -0600488 if type(val) == str:
489 val = val.encode('utf-8')
Simon Glassa683a5f2019-07-20 12:23:37 -0600490 self._CheckProp(prop_name).props[prop_name].SetData(val + b'\0')
Simon Glassccd25262018-09-14 04:57:16 -0600491
Simon Glassf67c99c2020-07-09 18:39:44 -0600492 def AddData(self, prop_name, val):
493 """Add a new property to a node
494
495 The device tree is marked dirty so that the value will be written to
496 the blob on the next sync.
497
498 Args:
499 prop_name: Name of property to add
500 val: Bytes value of property
Simon Glassd8bee462021-03-21 18:24:39 +1300501
502 Returns:
503 Prop added
Simon Glassf67c99c2020-07-09 18:39:44 -0600504 """
Simon Glassd8bee462021-03-21 18:24:39 +1300505 prop = Prop(self, None, prop_name, val)
506 self.props[prop_name] = prop
507 return prop
Simon Glassf67c99c2020-07-09 18:39:44 -0600508
Simon Glassccd25262018-09-14 04:57:16 -0600509 def AddString(self, prop_name, val):
510 """Add a new string property to a node
511
512 The device tree is marked dirty so that the value will be written to
513 the blob on the next sync.
514
515 Args:
516 prop_name: Name of property to add
517 val: String value of property
Simon Glassd8bee462021-03-21 18:24:39 +1300518
519 Returns:
520 Prop added
Simon Glassccd25262018-09-14 04:57:16 -0600521 """
Simon Glassd35a8522021-01-06 21:35:10 -0700522 val = bytes(val, 'utf-8')
Simon Glassd8bee462021-03-21 18:24:39 +1300523 return self.AddData(prop_name, val + b'\0')
Simon Glassccd25262018-09-14 04:57:16 -0600524
Simon Glass452be422022-02-08 11:49:50 -0700525 def AddStringList(self, prop_name, val):
526 """Add a new string-list property to a node
527
528 The device tree is marked dirty so that the value will be written to
529 the blob on the next sync.
530
531 Args:
532 prop_name: Name of property to add
533 val (list of str): List of strings to add
534
535 Returns:
536 Prop added
537 """
Simon Glass120fa002022-03-05 20:18:56 -0700538 out = b'\0'.join(bytes(s, 'utf-8') for s in val) + b'\0' if val else b''
Simon Glass452be422022-02-08 11:49:50 -0700539 return self.AddData(prop_name, out)
540
Simon Glassa2af7302021-01-06 21:35:18 -0700541 def AddInt(self, prop_name, val):
542 """Add a new integer property to a node
543
544 The device tree is marked dirty so that the value will be written to
545 the blob on the next sync.
546
547 Args:
548 prop_name: Name of property to add
549 val: Integer value of property
Simon Glassd8bee462021-03-21 18:24:39 +1300550
551 Returns:
552 Prop added
Simon Glassa2af7302021-01-06 21:35:18 -0700553 """
Simon Glassd8bee462021-03-21 18:24:39 +1300554 return self.AddData(prop_name, struct.pack('>I', val))
Simon Glassa2af7302021-01-06 21:35:18 -0700555
Simon Glassadf8e052023-07-18 07:24:02 -0600556 def Subnode(self, name):
557 """Create new subnode for the node
Simon Glassccd25262018-09-14 04:57:16 -0600558
559 Args:
560 name: name of node to add
561
562 Returns:
563 New subnode that was created
564 """
Simon Glassf3a17962018-09-14 04:57:15 -0600565 path = self.path + '/' + name
Simon Glassadf8e052023-07-18 07:24:02 -0600566 return Node(self._fdt, self, None, name, path)
567
568 def AddSubnode(self, name):
569 """Add a new subnode to the node, after all other subnodes
570
571 Args:
572 name: name of node to add
573
574 Returns:
575 New subnode that was created
576 """
577 subnode = self.Subnode(name)
Simon Glassf3a17962018-09-14 04:57:15 -0600578 self.subnodes.append(subnode)
579 return subnode
580
Simon Glassadf8e052023-07-18 07:24:02 -0600581 def insert_subnode(self, name):
582 """Add a new subnode to the node, before all other subnodes
583
584 This deletes other subnodes and sets their offset to None, so that they
585 will be recreated after this one.
586
587 Args:
588 name: name of node to add
589
590 Returns:
591 New subnode that was created
592 """
593 # Deleting a node invalidates the offsets of all following nodes, so
594 # process in reverse order so that the offset of each node remains valid
595 # until deletion.
596 for subnode in reversed(self.subnodes):
597 subnode.purge(True)
598 subnode = self.Subnode(name)
599 self.subnodes.insert(0, subnode)
600 return subnode
601
602 def purge(self, delete_it=False):
603 """Purge this node, setting offset to None and deleting from FDT"""
604 if self._offset is not None:
605 if delete_it:
606 CheckErr(self._fdt._fdt_obj.del_node(self.Offset()),
607 "Node '%s': delete" % self.path)
608 self._offset = None
609 self._fdt.Invalidate()
610
611 for prop in self.props.values():
612 prop.purge()
613
614 for subnode in self.subnodes:
615 subnode.purge(False)
616
617 def move_to_first(self):
618 """Move the current node to first in its parent's node list"""
619 parent = self.parent
620 if parent.subnodes and parent.subnodes[0] == self:
621 return
622 for subnode in reversed(parent.subnodes):
623 subnode.purge(True)
624
625 new_subnodes = [self]
626 for subnode in parent.subnodes:
627 #subnode.purge(False)
628 if subnode != self:
629 new_subnodes.append(subnode)
630 parent.subnodes = new_subnodes
631
Simon Glassb9b5cb32022-02-08 11:49:51 -0700632 def Delete(self):
633 """Delete a node
634
635 The node is deleted and the offset cache is invalidated.
636
637 Args:
638 node (Node): Node to delete
639
640 Raises:
641 ValueError if the node does not exist
642 """
643 CheckErr(self._fdt._fdt_obj.del_node(self.Offset()),
644 "Node '%s': delete" % self.path)
645 parent = self.parent
646 self._fdt.Invalidate()
647 parent.subnodes.remove(self)
648
Simon Glasseddd7292018-09-14 04:57:13 -0600649 def Sync(self, auto_resize=False):
650 """Sync node changes back to the device tree
651
652 This updates the device tree blob with any changes to this node and its
653 subnodes since the last sync.
654
655 Args:
656 auto_resize: Resize the device tree automatically if it does not
657 have enough space for the update
658
Simon Glass3be798a2021-03-21 18:24:38 +1300659 Returns:
660 True if the node had to be added, False if it already existed
661
Simon Glasseddd7292018-09-14 04:57:13 -0600662 Raises:
663 FdtException if auto_resize is False and there is not enough space
664 """
Simon Glass3be798a2021-03-21 18:24:38 +1300665 added = False
Simon Glassf3a17962018-09-14 04:57:15 -0600666 if self._offset is None:
667 # The subnode doesn't exist yet, so add it
668 fdt_obj = self._fdt._fdt_obj
669 if auto_resize:
670 while True:
671 offset = fdt_obj.add_subnode(self.parent._offset, self.name,
672 (libfdt.NOSPACE,))
673 if offset != -libfdt.NOSPACE:
674 break
675 fdt_obj.resize(fdt_obj.totalsize() + 1024)
676 else:
677 offset = fdt_obj.add_subnode(self.parent._offset, self.name)
678 self._offset = offset
Simon Glass3be798a2021-03-21 18:24:38 +1300679 added = True
680
681 # Sync the existing subnodes first, so that we can rely on the offsets
682 # being correct. As soon as we add new subnodes, it pushes all the
683 # existing subnodes up.
684 for node in reversed(self.subnodes):
685 if node._offset is not None:
686 node.Sync(auto_resize)
Simon Glassf3a17962018-09-14 04:57:15 -0600687
Simon Glass3be798a2021-03-21 18:24:38 +1300688 # Sync subnodes in reverse so that we get the expected order. Each
689 # new node goes at the start of the subnode list. This avoids an O(n^2)
690 # rescan of node offsets.
691 num_added = 0
Simon Glasseddd7292018-09-14 04:57:13 -0600692 for node in reversed(self.subnodes):
Simon Glass3be798a2021-03-21 18:24:38 +1300693 if node.Sync(auto_resize):
694 num_added += 1
695 if num_added:
696 # Reorder our list of nodes to put the new ones first, since that's
697 # what libfdt does
698 old_count = len(self.subnodes) - num_added
699 subnodes = self.subnodes[old_count:] + self.subnodes[:old_count]
700 self.subnodes = subnodes
Simon Glasse80c5562018-07-06 10:27:38 -0600701
Simon Glass3be798a2021-03-21 18:24:38 +1300702 # Sync properties now, whose offsets should not have been disturbed,
703 # since properties come before subnodes. This is done after all the
704 # subnode processing above, since updating properties can disturb the
705 # offsets of those subnodes.
706 # Properties are synced in reverse order, with new properties added
707 # before existing properties are synced. This ensures that the offsets
708 # of earlier properties are not disturbed.
709 # Note that new properties will have an offset of None here, which
710 # Python cannot sort against int. So use a large value instead so that
711 # new properties are added first.
Simon Glassa57cfee2019-05-17 22:00:38 -0600712 prop_list = sorted(self.props.values(),
713 key=lambda prop: prop._offset or 1 << 31,
Simon Glasseddd7292018-09-14 04:57:13 -0600714 reverse=True)
715 for prop in prop_list:
716 prop.Sync(auto_resize)
Simon Glass3be798a2021-03-21 18:24:38 +1300717 return added
Simon Glasseddd7292018-09-14 04:57:13 -0600718
Simon Glass48ec1ff2023-07-22 21:43:55 -0600719 def merge_props(self, src, copy_phandles):
Simon Glassadf8e052023-07-18 07:24:02 -0600720 """Copy missing properties (except 'phandle') from another node
721
722 Args:
723 src (Node): Node containing properties to copy
Simon Glass48ec1ff2023-07-22 21:43:55 -0600724 copy_phandles (bool): True to copy phandle properties in nodes
Simon Glassadf8e052023-07-18 07:24:02 -0600725
726 Adds properties which are present in src but not in this node. Any
727 'phandle' property is not copied since this might result in two nodes
728 with the same phandle, thus making phandle references ambiguous.
729 """
Simon Glass638567d2023-07-22 21:43:54 -0600730 tout.debug(f'copy to {self.path}: {src.path}')
Simon Glassadf8e052023-07-18 07:24:02 -0600731 for name, src_prop in src.props.items():
Simon Glass638567d2023-07-22 21:43:54 -0600732 done = False
Simon Glass48ec1ff2023-07-22 21:43:55 -0600733 if name not in self.props:
734 if copy_phandles or name != 'phandle':
735 self.props[name] = Prop(self, None, name, src_prop.bytes)
736 done = True
Simon Glass638567d2023-07-22 21:43:54 -0600737 tout.debug(f" {name}{'' if done else ' - ignored'}")
Simon Glassadf8e052023-07-18 07:24:02 -0600738
Simon Glass48ec1ff2023-07-22 21:43:55 -0600739 def copy_node(self, src, copy_phandles=False):
Simon Glassadf8e052023-07-18 07:24:02 -0600740 """Copy a node and all its subnodes into this node
741
742 Args:
743 src (Node): Node to copy
Simon Glass48ec1ff2023-07-22 21:43:55 -0600744 copy_phandles (bool): True to copy phandle properties in nodes
Simon Glassadf8e052023-07-18 07:24:02 -0600745
746 Returns:
747 Node: Resulting destination node
748
Simon Glass48ec1ff2023-07-22 21:43:55 -0600749 This works recursively, with copy_phandles being set to True for the
750 recursive calls
Simon Glassadf8e052023-07-18 07:24:02 -0600751
752 The new node is put before all other nodes. If the node already
753 exists, just its subnodes and properties are copied, placing them before
754 any existing subnodes. Properties which exist in the destination node
755 already are not copied.
756 """
757 dst = self.FindNode(src.name)
758 if dst:
759 dst.move_to_first()
760 else:
761 dst = self.insert_subnode(src.name)
Simon Glass48ec1ff2023-07-22 21:43:55 -0600762 dst.merge_props(src, copy_phandles)
Simon Glassadf8e052023-07-18 07:24:02 -0600763
764 # Process in reverse order so that they appear correctly in the result,
765 # since copy_node() puts the node first in the list
766 for node in reversed(src.subnodes):
Simon Glass48ec1ff2023-07-22 21:43:55 -0600767 dst.copy_node(node, True)
Simon Glassadf8e052023-07-18 07:24:02 -0600768 return dst
769
Simon Glasscd971192023-07-18 07:24:03 -0600770 def copy_subnodes_from_phandles(self, phandle_list):
771 """Copy subnodes of a list of nodes into another node
772
773 Args:
774 phandle_list (list of int): List of phandles of nodes to copy
775
776 For each node in the phandle list, its subnodes and their properties are
777 copied recursively. Note that it does not copy the node itself, nor its
778 properties.
779 """
780 # Process in reverse order, since new nodes are inserted at the start of
781 # the destination's node list. We want them to appear in order of the
782 # phandle list
783 for phandle in phandle_list.__reversed__():
784 parent = self.GetFdt().LookupPhandle(phandle)
785 tout.debug(f'adding template {parent.path} to node {self.path}')
786 for node in parent.subnodes.__reversed__():
787 dst = self.copy_node(node)
788
789 tout.debug(f'merge props from {parent.path} to {dst.path}')
Simon Glass48ec1ff2023-07-22 21:43:55 -0600790 self.merge_props(parent, False)
Simon Glasscd971192023-07-18 07:24:03 -0600791
Simon Glasse80c5562018-07-06 10:27:38 -0600792
Simon Glass1f941b62016-07-25 18:59:04 -0600793class Fdt:
Simon Glass059ae522017-05-27 07:38:28 -0600794 """Provides simple access to a flat device tree blob using libfdts.
Simon Glass1f941b62016-07-25 18:59:04 -0600795
796 Properties:
797 fname: Filename of fdt
798 _root: Root of device tree (a Node object)
Simon Glass50cfc6e2019-07-20 12:23:38 -0600799 name: Helpful name for this Fdt for the user (useful when creating the
800 DT from data rather than a file)
Simon Glass1f941b62016-07-25 18:59:04 -0600801 """
802 def __init__(self, fname):
803 self._fname = fname
Simon Glass059ae522017-05-27 07:38:28 -0600804 self._cached_offsets = False
Simon Glassa3f94442017-08-29 14:15:52 -0600805 self.phandle_to_node = {}
Simon Glass50cfc6e2019-07-20 12:23:38 -0600806 self.name = ''
Simon Glass059ae522017-05-27 07:38:28 -0600807 if self._fname:
Simon Glass50cfc6e2019-07-20 12:23:38 -0600808 self.name = self._fname
Simon Glass059ae522017-05-27 07:38:28 -0600809 self._fname = fdt_util.EnsureCompiled(self._fname)
810
Simon Glass116236f2019-05-14 15:53:43 -0600811 with open(self._fname, 'rb') as fd:
Simon Glass792d2392018-07-06 10:27:27 -0600812 self._fdt_obj = libfdt.Fdt(fd.read())
Simon Glasscc346a72016-07-25 18:59:07 -0600813
Simon Glassb8a49292018-09-14 04:57:17 -0600814 @staticmethod
Simon Glass50cfc6e2019-07-20 12:23:38 -0600815 def FromData(data, name=''):
Simon Glassb8a49292018-09-14 04:57:17 -0600816 """Create a new Fdt object from the given data
817
818 Args:
819 data: Device-tree data blob
Simon Glass50cfc6e2019-07-20 12:23:38 -0600820 name: Helpful name for this Fdt for the user
Simon Glassb8a49292018-09-14 04:57:17 -0600821
822 Returns:
823 Fdt object containing the data
824 """
825 fdt = Fdt(None)
Simon Glass1cd40082019-05-17 22:00:36 -0600826 fdt._fdt_obj = libfdt.Fdt(bytes(data))
Simon Glass50cfc6e2019-07-20 12:23:38 -0600827 fdt.name = name
Simon Glassb8a49292018-09-14 04:57:17 -0600828 return fdt
829
Simon Glasse2d65282018-07-17 13:25:46 -0600830 def LookupPhandle(self, phandle):
831 """Look up a phandle
832
833 Args:
834 phandle: Phandle to look up (int)
835
836 Returns:
837 Node object the phandle points to
838 """
839 return self.phandle_to_node.get(phandle)
840
Simon Glasscc346a72016-07-25 18:59:07 -0600841 def Scan(self, root='/'):
842 """Scan a device tree, building up a tree of Node objects
843
844 This fills in the self._root property
845
846 Args:
847 root: Ignored
848
849 TODO(sjg@chromium.org): Implement the 'root' parameter
850 """
Simon Glass48ec1ff2023-07-22 21:43:55 -0600851 self.phandle_to_node = {}
Simon Glass4df8a0c2018-07-06 10:27:29 -0600852 self._cached_offsets = True
Simon Glass80ef7052017-08-29 14:15:47 -0600853 self._root = self.Node(self, None, 0, '/', '/')
Simon Glasscc346a72016-07-25 18:59:07 -0600854 self._root.Scan()
855
856 def GetRoot(self):
857 """Get the root Node of the device tree
858
859 Returns:
860 The root Node object
861 """
862 return self._root
863
864 def GetNode(self, path):
865 """Look up a node from its path
866
867 Args:
868 path: Path to look up, e.g. '/microcode/update@0'
869 Returns:
870 Node object, or None if not found
871 """
872 node = self._root
Simon Glassc5eddc82018-07-06 10:27:30 -0600873 parts = path.split('/')
874 if len(parts) < 2:
875 return None
Simon Glass3b9a8292019-07-20 12:23:39 -0600876 if len(parts) == 2 and parts[1] == '':
877 return node
Simon Glassc5eddc82018-07-06 10:27:30 -0600878 for part in parts[1:]:
Simon Glassaa1a5d72018-07-17 13:25:41 -0600879 node = node.FindNode(part)
Simon Glasscc346a72016-07-25 18:59:07 -0600880 if not node:
881 return None
882 return node
883
Simon Glass32d98272016-07-25 18:59:15 -0600884 def Flush(self):
885 """Flush device tree changes back to the file
886
887 If the device tree has changed in memory, write it back to the file.
Simon Glass32d98272016-07-25 18:59:15 -0600888 """
Simon Glass059ae522017-05-27 07:38:28 -0600889 with open(self._fname, 'wb') as fd:
Simon Glass792d2392018-07-06 10:27:27 -0600890 fd.write(self._fdt_obj.as_bytearray())
Simon Glass32d98272016-07-25 18:59:15 -0600891
Simon Glasseddd7292018-09-14 04:57:13 -0600892 def Sync(self, auto_resize=False):
893 """Make sure any DT changes are written to the blob
894
895 Args:
896 auto_resize: Resize the device tree automatically if it does not
897 have enough space for the update
898
899 Raises:
900 FdtException if auto_resize is False and there is not enough space
901 """
Simon Glassb2e8ac42021-03-21 18:24:36 +1300902 self.CheckCache()
Simon Glasseddd7292018-09-14 04:57:13 -0600903 self._root.Sync(auto_resize)
Simon Glassb2e8ac42021-03-21 18:24:36 +1300904 self.Refresh()
Simon Glasseddd7292018-09-14 04:57:13 -0600905
Simon Glass32d98272016-07-25 18:59:15 -0600906 def Pack(self):
907 """Pack the device tree down to its minimum size
908
909 When nodes and properties shrink or are deleted, wasted space can
Simon Glass059ae522017-05-27 07:38:28 -0600910 build up in the device tree binary.
911 """
Simon Glass151aeed2018-07-06 10:27:26 -0600912 CheckErr(self._fdt_obj.pack(), 'pack')
Simon Glassb2e8ac42021-03-21 18:24:36 +1300913 self.Refresh()
Simon Glass059ae522017-05-27 07:38:28 -0600914
Simon Glass792d2392018-07-06 10:27:27 -0600915 def GetContents(self):
Simon Glass059ae522017-05-27 07:38:28 -0600916 """Get the contents of the FDT
917
918 Returns:
919 The FDT contents as a string of bytes
Simon Glass32d98272016-07-25 18:59:15 -0600920 """
Simon Glass1cd40082019-05-17 22:00:36 -0600921 return bytes(self._fdt_obj.as_bytearray())
Simon Glass059ae522017-05-27 07:38:28 -0600922
Simon Glass0ed50752018-07-06 10:27:24 -0600923 def GetFdtObj(self):
924 """Get the contents of the FDT
925
926 Returns:
927 The FDT contents as a libfdt.Fdt object
928 """
929 return self._fdt_obj
930
Simon Glass059ae522017-05-27 07:38:28 -0600931 def GetProps(self, node):
932 """Get all properties from a node.
933
934 Args:
935 node: Full path to node name to look in.
936
937 Returns:
938 A dictionary containing all the properties, indexed by node name.
939 The entries are Prop objects.
940
941 Raises:
942 ValueError: if the node does not exist.
943 """
944 props_dict = {}
Simon Glass151aeed2018-07-06 10:27:26 -0600945 poffset = self._fdt_obj.first_property_offset(node._offset,
946 QUIET_NOTFOUND)
Simon Glass059ae522017-05-27 07:38:28 -0600947 while poffset >= 0:
948 p = self._fdt_obj.get_property_by_offset(poffset)
Simon Glass70cd0d72018-07-06 10:27:20 -0600949 prop = Prop(node, poffset, p.name, p)
Simon Glass059ae522017-05-27 07:38:28 -0600950 props_dict[prop.name] = prop
951
Simon Glass151aeed2018-07-06 10:27:26 -0600952 poffset = self._fdt_obj.next_property_offset(poffset,
953 QUIET_NOTFOUND)
Simon Glass059ae522017-05-27 07:38:28 -0600954 return props_dict
955
956 def Invalidate(self):
957 """Mark our offset cache as invalid"""
958 self._cached_offsets = False
959
960 def CheckCache(self):
961 """Refresh the offset cache if needed"""
962 if self._cached_offsets:
963 return
964 self.Refresh()
Simon Glass059ae522017-05-27 07:38:28 -0600965
966 def Refresh(self):
967 """Refresh the offset cache"""
968 self._root.Refresh(0)
Simon Glassb2e8ac42021-03-21 18:24:36 +1300969 self._cached_offsets = True
Simon Glass059ae522017-05-27 07:38:28 -0600970
971 def GetStructOffset(self, offset):
972 """Get the file offset of a given struct offset
973
974 Args:
975 offset: Offset within the 'struct' region of the device tree
976 Returns:
977 Position of @offset within the device tree binary
978 """
Simon Glass151aeed2018-07-06 10:27:26 -0600979 return self._fdt_obj.off_dt_struct() + offset
Simon Glass059ae522017-05-27 07:38:28 -0600980
981 @classmethod
Simon Glass80ef7052017-08-29 14:15:47 -0600982 def Node(self, fdt, parent, offset, name, path):
Simon Glass059ae522017-05-27 07:38:28 -0600983 """Create a new node
984
985 This is used by Fdt.Scan() to create a new node using the correct
986 class.
987
988 Args:
989 fdt: Fdt object
Simon Glass80ef7052017-08-29 14:15:47 -0600990 parent: Parent node, or None if this is the root node
Simon Glass059ae522017-05-27 07:38:28 -0600991 offset: Offset of node
992 name: Node name
993 path: Full path to node
994 """
Simon Glass80ef7052017-08-29 14:15:47 -0600995 node = Node(fdt, parent, offset, name, path)
Simon Glass059ae522017-05-27 07:38:28 -0600996 return node
Simon Glassa9440932017-05-27 07:38:30 -0600997
Simon Glass74f5feb2019-07-20 12:24:08 -0600998 def GetFilename(self):
999 """Get the filename of the device tree
1000
1001 Returns:
1002 String filename
1003 """
1004 return self._fname
1005
Simon Glassa9440932017-05-27 07:38:30 -06001006def FdtScan(fname):
Simon Glassc38fee042018-07-06 10:27:32 -06001007 """Returns a new Fdt object"""
Simon Glassa9440932017-05-27 07:38:30 -06001008 dtb = Fdt(fname)
1009 dtb.Scan()
1010 return dtb