blob: 5963925146a5df3fadbf29e33f6d5e995b5ff300 [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
18# This deals with a device tree, presenting it as an assortment of Node and
19# Prop objects, representing nodes and properties, respectively. This file
Simon Glassa9440932017-05-27 07:38:30 -060020# contains the base classes and defines the high-level API. You can use
21# FdtScan() as a convenience function to create and scan an Fdt.
Simon Glass059ae522017-05-27 07:38:28 -060022
23# This implementation uses a libfdt Python library to access the device tree,
24# so it is fairly efficient.
Simon Glass1f941b62016-07-25 18:59:04 -060025
Simon Glassb1a5e262016-07-25 18:59:05 -060026# A list of types we support
Simon Glassc9a032c2020-11-08 20:36:17 -070027class Type(IntEnum):
Simon Glassfd4078e2021-07-28 19:23:09 -060028 # Types in order from widest to narrowest
Simon Glassc9a032c2020-11-08 20:36:17 -070029 (BYTE, INT, STRING, BOOL, INT64) = range(5)
30
Simon Glassfd4078e2021-07-28 19:23:09 -060031 def needs_widening(self, other):
32 """Check if this type needs widening to hold a value from another type
Simon Glassc9a032c2020-11-08 20:36:17 -070033
Simon Glassfd4078e2021-07-28 19:23:09 -060034 A wider type is one that can hold a wider array of information than
35 another one, or is less restrictive, so it can hold the information of
36 another type as well as its own. This is similar to the concept of
37 type-widening in C.
Simon Glassc9a032c2020-11-08 20:36:17 -070038
39 This uses a simple arithmetic comparison, since type values are in order
Simon Glassfd4078e2021-07-28 19:23:09 -060040 from widest (BYTE) to narrowest (INT64).
Simon Glassc9a032c2020-11-08 20:36:17 -070041
42 Args:
43 other: Other type to compare against
44
45 Return:
46 True if the other type is wider
47 """
48 return self.value > other.value
Simon Glassb1a5e262016-07-25 18:59:05 -060049
Simon Glass1f941b62016-07-25 18:59:04 -060050def CheckErr(errnum, msg):
51 if errnum:
52 raise ValueError('Error %d: %s: %s' %
53 (errnum, libfdt.fdt_strerror(errnum), msg))
54
Simon Glassb73d4482019-05-17 22:00:34 -060055
Simon Glassd8f593f2019-05-17 22:00:35 -060056def BytesToValue(data):
Simon Glassb73d4482019-05-17 22:00:34 -060057 """Converts a string of bytes into a type and value
58
59 Args:
Simon Glassd8f593f2019-05-17 22:00:35 -060060 A bytes value (which on Python 2 is an alias for str)
Simon Glassb73d4482019-05-17 22:00:34 -060061
62 Return:
63 A tuple:
64 Type of data
65 Data, either a single element or a list of elements. Each element
66 is one of:
Simon Glassc9a032c2020-11-08 20:36:17 -070067 Type.STRING: str/bytes value from the property
68 Type.INT: a byte-swapped integer stored as a 4-byte str/bytes
69 Type.BYTE: a byte stored as a single-byte str/bytes
Simon Glassb73d4482019-05-17 22:00:34 -060070 """
Simon Glassd8f593f2019-05-17 22:00:35 -060071 data = bytes(data)
72 size = len(data)
73 strings = data.split(b'\0')
Simon Glassb73d4482019-05-17 22:00:34 -060074 is_string = True
75 count = len(strings) - 1
Simon Glassd8f593f2019-05-17 22:00:35 -060076 if count > 0 and not len(strings[-1]):
Simon Glassb73d4482019-05-17 22:00:34 -060077 for string in strings[:-1]:
78 if not string:
79 is_string = False
80 break
81 for ch in string:
Simon Glassd8f593f2019-05-17 22:00:35 -060082 if ch < 32 or ch > 127:
Simon Glassb73d4482019-05-17 22:00:34 -060083 is_string = False
84 break
85 else:
86 is_string = False
87 if is_string:
Simon Glassd8f593f2019-05-17 22:00:35 -060088 if count == 1:
Simon Glassc9a032c2020-11-08 20:36:17 -070089 return Type.STRING, strings[0].decode()
Simon Glassb73d4482019-05-17 22:00:34 -060090 else:
Simon Glassc9a032c2020-11-08 20:36:17 -070091 return Type.STRING, [s.decode() for s in strings[:-1]]
Simon Glassb73d4482019-05-17 22:00:34 -060092 if size % 4:
93 if size == 1:
Simon Glass632b84c2020-11-08 20:36:20 -070094 return Type.BYTE, chr(data[0])
Simon Glassb73d4482019-05-17 22:00:34 -060095 else:
Simon Glass632b84c2020-11-08 20:36:20 -070096 return Type.BYTE, [chr(ch) for ch in list(data)]
Simon Glassb73d4482019-05-17 22:00:34 -060097 val = []
98 for i in range(0, size, 4):
Simon Glassd8f593f2019-05-17 22:00:35 -060099 val.append(data[i:i + 4])
Simon Glassb73d4482019-05-17 22:00:34 -0600100 if size == 4:
Simon Glassc9a032c2020-11-08 20:36:17 -0700101 return Type.INT, val[0]
Simon Glassb73d4482019-05-17 22:00:34 -0600102 else:
Simon Glassc9a032c2020-11-08 20:36:17 -0700103 return Type.INT, val
Simon Glassb73d4482019-05-17 22:00:34 -0600104
105
Simon Glass059ae522017-05-27 07:38:28 -0600106class Prop:
Simon Glass1f941b62016-07-25 18:59:04 -0600107 """A device tree property
108
109 Properties:
Simon Glass459df472021-03-21 18:24:35 +1300110 node: Node containing this property
111 offset: Offset of the property (None if still to be synced)
Simon Glass1f941b62016-07-25 18:59:04 -0600112 name: Property name (as per the device tree)
113 value: Property value as a string of bytes, or a list of strings of
114 bytes
115 type: Value type
116 """
Simon Glass74b472e2019-05-17 22:00:37 -0600117 def __init__(self, node, offset, name, data):
Simon Glass1f941b62016-07-25 18:59:04 -0600118 self._node = node
119 self._offset = offset
120 self.name = name
121 self.value = None
Simon Glass74b472e2019-05-17 22:00:37 -0600122 self.bytes = bytes(data)
Simon Glass459df472021-03-21 18:24:35 +1300123 self.dirty = offset is None
Simon Glass74b472e2019-05-17 22:00:37 -0600124 if not data:
Simon Glassc9a032c2020-11-08 20:36:17 -0700125 self.type = Type.BOOL
Simon Glass059ae522017-05-27 07:38:28 -0600126 self.value = True
127 return
Simon Glass74b472e2019-05-17 22:00:37 -0600128 self.type, self.value = BytesToValue(bytes(data))
Simon Glass1f941b62016-07-25 18:59:04 -0600129
Simon Glass4df8a0c2018-07-06 10:27:29 -0600130 def RefreshOffset(self, poffset):
131 self._offset = poffset
132
Simon Glass248ccd22016-07-25 18:59:06 -0600133 def Widen(self, newprop):
134 """Figure out which property type is more general
135
136 Given a current property and a new property, this function returns the
137 one that is less specific as to type. The less specific property will
138 be ble to represent the data in the more specific property. This is
139 used for things like:
140
141 node1 {
142 compatible = "fred";
143 value = <1>;
144 };
145 node1 {
146 compatible = "fred";
147 value = <1 2>;
148 };
149
150 He we want to use an int array for 'value'. The first property
151 suggests that a single int is enough, but the second one shows that
152 it is not. Calling this function with these two propertes would
153 update the current property to be like the second, since it is less
154 specific.
155 """
Simon Glassfd4078e2021-07-28 19:23:09 -0600156 if self.type.needs_widening(newprop.type):
Simon Glass43118322021-07-28 19:23:11 -0600157
158 # A boolean has an empty value: if it exists it is True and if not
159 # it is False. So when widening we always start with an empty list
160 # since the only valid integer property would be an empty list of
161 # integers.
162 # e.g. this is a boolean:
163 # some-prop;
164 # and it would be widened to int list by:
165 # some-prop = <1 2>;
166 if self.type == Type.BOOL:
167 self.type = Type.INT
168 self.value = [self.GetEmpty(self.type)]
Simon Glassc9a032c2020-11-08 20:36:17 -0700169 if self.type == Type.INT and newprop.type == Type.BYTE:
Simon Glass8034e4d2020-10-03 11:31:27 -0600170 if type(self.value) == list:
171 new_value = []
172 for val in self.value:
Simon Glass632b84c2020-11-08 20:36:20 -0700173 new_value += [chr(by) for by in val]
Simon Glass8034e4d2020-10-03 11:31:27 -0600174 else:
Simon Glass632b84c2020-11-08 20:36:20 -0700175 new_value = [chr(by) for by in self.value]
Simon Glass8034e4d2020-10-03 11:31:27 -0600176 self.value = new_value
Simon Glass248ccd22016-07-25 18:59:06 -0600177 self.type = newprop.type
178
Simon Glassa7d66982021-07-28 19:23:10 -0600179 if type(newprop.value) == list:
180 if type(self.value) != list:
181 self.value = [self.value]
Simon Glass248ccd22016-07-25 18:59:06 -0600182
Simon Glassa7d66982021-07-28 19:23:10 -0600183 if len(newprop.value) > len(self.value):
184 val = self.GetEmpty(self.type)
185 while len(self.value) < len(newprop.value):
186 self.value.append(val)
Simon Glass248ccd22016-07-25 18:59:06 -0600187
Simon Glass0ed50752018-07-06 10:27:24 -0600188 @classmethod
Simon Glassb1a5e262016-07-25 18:59:05 -0600189 def GetEmpty(self, type):
190 """Get an empty / zero value of the given type
191
192 Returns:
193 A single value of the given type
194 """
Simon Glassc9a032c2020-11-08 20:36:17 -0700195 if type == Type.BYTE:
Simon Glassb1a5e262016-07-25 18:59:05 -0600196 return chr(0)
Simon Glassc9a032c2020-11-08 20:36:17 -0700197 elif type == Type.INT:
Simon Glassc330b142018-09-14 04:57:14 -0600198 return struct.pack('>I', 0);
Simon Glassc9a032c2020-11-08 20:36:17 -0700199 elif type == Type.STRING:
Simon Glassb1a5e262016-07-25 18:59:05 -0600200 return ''
201 else:
202 return True
203
Simon Glass54cec6d2016-07-25 18:59:16 -0600204 def GetOffset(self):
205 """Get the offset of a property
206
Simon Glass54cec6d2016-07-25 18:59:16 -0600207 Returns:
Simon Glass059ae522017-05-27 07:38:28 -0600208 The offset of the property (struct fdt_property) within the file
Simon Glass54cec6d2016-07-25 18:59:16 -0600209 """
Simon Glass4df8a0c2018-07-06 10:27:29 -0600210 self._node._fdt.CheckCache()
Simon Glass059ae522017-05-27 07:38:28 -0600211 return self._node._fdt.GetStructOffset(self._offset)
Simon Glass54cec6d2016-07-25 18:59:16 -0600212
Simon Glasseddd7292018-09-14 04:57:13 -0600213 def SetInt(self, val):
214 """Set the integer value of the property
215
216 The device tree is marked dirty so that the value will be written to
217 the block on the next sync.
218
219 Args:
220 val: Integer value (32-bit, single cell)
221 """
222 self.bytes = struct.pack('>I', val);
Simon Glassa2b9acb2018-10-01 12:22:49 -0600223 self.value = self.bytes
Simon Glassc9a032c2020-11-08 20:36:17 -0700224 self.type = Type.INT
Simon Glasseddd7292018-09-14 04:57:13 -0600225 self.dirty = True
226
Simon Glassccd25262018-09-14 04:57:16 -0600227 def SetData(self, bytes):
228 """Set the value of a property as bytes
229
230 Args:
231 bytes: New property value to set
232 """
Simon Glass1cd40082019-05-17 22:00:36 -0600233 self.bytes = bytes
Simon Glassb73d4482019-05-17 22:00:34 -0600234 self.type, self.value = BytesToValue(bytes)
Simon Glassccd25262018-09-14 04:57:16 -0600235 self.dirty = True
236
Simon Glasseddd7292018-09-14 04:57:13 -0600237 def Sync(self, auto_resize=False):
238 """Sync property changes back to the device tree
239
240 This updates the device tree blob with any changes to this property
241 since the last sync.
242
243 Args:
244 auto_resize: Resize the device tree automatically if it does not
245 have enough space for the update
246
247 Raises:
248 FdtException if auto_resize is False and there is not enough space
249 """
Simon Glass459df472021-03-21 18:24:35 +1300250 if self.dirty:
Simon Glasseddd7292018-09-14 04:57:13 -0600251 node = self._node
Simon Glass638567d2023-07-22 21:43:54 -0600252 tout.debug(f'sync {node.path}: {self.name}')
Simon Glasseddd7292018-09-14 04:57:13 -0600253 fdt_obj = node._fdt._fdt_obj
Simon Glassd8bee462021-03-21 18:24:39 +1300254 node_name = fdt_obj.get_name(node._offset)
255 if node_name and node_name != node.name:
256 raise ValueError("Internal error, node '%s' name mismatch '%s'" %
257 (node.path, node_name))
258
Simon Glasseddd7292018-09-14 04:57:13 -0600259 if auto_resize:
260 while fdt_obj.setprop(node.Offset(), self.name, self.bytes,
261 (libfdt.NOSPACE,)) == -libfdt.NOSPACE:
Simon Glassf67c99c2020-07-09 18:39:44 -0600262 fdt_obj.resize(fdt_obj.totalsize() + 1024 +
263 len(self.bytes))
Simon Glasseddd7292018-09-14 04:57:13 -0600264 fdt_obj.setprop(node.Offset(), self.name, self.bytes)
265 else:
266 fdt_obj.setprop(node.Offset(), self.name, self.bytes)
Simon Glass459df472021-03-21 18:24:35 +1300267 self.dirty = False
Simon Glasseddd7292018-09-14 04:57:13 -0600268
Simon Glassadf8e052023-07-18 07:24:02 -0600269 def purge(self):
270 """Set a property offset to None
271
272 The property remains in the tree structure and will be recreated when
273 the FDT is synced
274 """
275 self._offset = None
Simon Glass02e505e2023-07-22 21:43:53 -0600276 self.dirty = True
Simon Glasseddd7292018-09-14 04:57:13 -0600277
Simon Glass059ae522017-05-27 07:38:28 -0600278class Node:
Simon Glass1f941b62016-07-25 18:59:04 -0600279 """A device tree node
280
281 Properties:
Simon Glass459df472021-03-21 18:24:35 +1300282 parent: Parent Node
283 offset: Integer offset in the device tree (None if to be synced)
Simon Glass1f941b62016-07-25 18:59:04 -0600284 name: Device tree node tname
285 path: Full path to node, along with the node name itself
286 _fdt: Device tree object
287 subnodes: A list of subnodes for this node, each a Node object
288 props: A dict of properties for this node, each a Prop object.
289 Keyed by property name
290 """
Simon Glass80ef7052017-08-29 14:15:47 -0600291 def __init__(self, fdt, parent, offset, name, path):
Simon Glass1f941b62016-07-25 18:59:04 -0600292 self._fdt = fdt
Simon Glass80ef7052017-08-29 14:15:47 -0600293 self.parent = parent
Simon Glass1f941b62016-07-25 18:59:04 -0600294 self._offset = offset
295 self.name = name
296 self.path = path
297 self.subnodes = []
298 self.props = {}
299
Simon Glasse2d65282018-07-17 13:25:46 -0600300 def GetFdt(self):
301 """Get the Fdt object for this node
302
303 Returns:
304 Fdt object
305 """
306 return self._fdt
307
Simon Glassaa1a5d72018-07-17 13:25:41 -0600308 def FindNode(self, name):
Simon Glasscc346a72016-07-25 18:59:07 -0600309 """Find a node given its name
310
311 Args:
312 name: Node name to look for
313 Returns:
314 Node object if found, else None
315 """
316 for subnode in self.subnodes:
317 if subnode.name == name:
318 return subnode
319 return None
320
Simon Glass059ae522017-05-27 07:38:28 -0600321 def Offset(self):
322 """Returns the offset of a node, after checking the cache
323
324 This should be used instead of self._offset directly, to ensure that
325 the cache does not contain invalid offsets.
326 """
327 self._fdt.CheckCache()
328 return self._offset
329
Simon Glasscc346a72016-07-25 18:59:07 -0600330 def Scan(self):
Simon Glass059ae522017-05-27 07:38:28 -0600331 """Scan a node's properties and subnodes
Simon Glasscc346a72016-07-25 18:59:07 -0600332
Simon Glass059ae522017-05-27 07:38:28 -0600333 This fills in the props and subnodes properties, recursively
334 searching into subnodes so that the entire tree is built.
Simon Glasscc346a72016-07-25 18:59:07 -0600335 """
Simon Glass151aeed2018-07-06 10:27:26 -0600336 fdt_obj = self._fdt._fdt_obj
Simon Glass059ae522017-05-27 07:38:28 -0600337 self.props = self._fdt.GetProps(self)
Simon Glass151aeed2018-07-06 10:27:26 -0600338 phandle = fdt_obj.get_phandle(self.Offset())
Simon Glassa3f94442017-08-29 14:15:52 -0600339 if phandle:
Simon Glass48ec1ff2023-07-22 21:43:55 -0600340 dup = self._fdt.phandle_to_node.get(phandle)
341 if dup:
Simon Glassc3296d72023-08-23 19:18:02 -0600342 raise ValueError(
343 f'Duplicate phandle {phandle} in nodes {dup.path} and {self.path}')
344
345 self._fdt.phandle_to_node[phandle] = self
Simon Glass059ae522017-05-27 07:38:28 -0600346
Simon Glass151aeed2018-07-06 10:27:26 -0600347 offset = fdt_obj.first_subnode(self.Offset(), QUIET_NOTFOUND)
Simon Glass059ae522017-05-27 07:38:28 -0600348 while offset >= 0:
349 sep = '' if self.path[-1] == '/' else '/'
Simon Glass151aeed2018-07-06 10:27:26 -0600350 name = fdt_obj.get_name(offset)
Simon Glass059ae522017-05-27 07:38:28 -0600351 path = self.path + sep + name
Simon Glass80ef7052017-08-29 14:15:47 -0600352 node = Node(self._fdt, self, offset, name, path)
Simon Glass059ae522017-05-27 07:38:28 -0600353 self.subnodes.append(node)
354
355 node.Scan()
Simon Glass151aeed2018-07-06 10:27:26 -0600356 offset = fdt_obj.next_subnode(offset, QUIET_NOTFOUND)
Simon Glass059ae522017-05-27 07:38:28 -0600357
358 def Refresh(self, my_offset):
359 """Fix up the _offset for each node, recursively
360
361 Note: This does not take account of property offsets - these will not
362 be updated.
363 """
Simon Glass792d2392018-07-06 10:27:27 -0600364 fdt_obj = self._fdt._fdt_obj
Simon Glass059ae522017-05-27 07:38:28 -0600365 if self._offset != my_offset:
Simon Glass059ae522017-05-27 07:38:28 -0600366 self._offset = my_offset
Simon Glassd8bee462021-03-21 18:24:39 +1300367 name = fdt_obj.get_name(self._offset)
368 if name and self.name != name:
369 raise ValueError("Internal error, node '%s' name mismatch '%s'" %
370 (self.path, name))
371
Simon Glass792d2392018-07-06 10:27:27 -0600372 offset = fdt_obj.first_subnode(self._offset, QUIET_NOTFOUND)
Simon Glass059ae522017-05-27 07:38:28 -0600373 for subnode in self.subnodes:
Simon Glass00238612022-02-08 11:49:52 -0700374 if subnode._offset is None:
375 continue
Simon Glass4df8a0c2018-07-06 10:27:29 -0600376 if subnode.name != fdt_obj.get_name(offset):
377 raise ValueError('Internal error, node name mismatch %s != %s' %
378 (subnode.name, fdt_obj.get_name(offset)))
Simon Glass059ae522017-05-27 07:38:28 -0600379 subnode.Refresh(offset)
Simon Glass792d2392018-07-06 10:27:27 -0600380 offset = fdt_obj.next_subnode(offset, QUIET_NOTFOUND)
Simon Glass4df8a0c2018-07-06 10:27:29 -0600381 if offset != -libfdt.FDT_ERR_NOTFOUND:
382 raise ValueError('Internal error, offset == %d' % offset)
383
384 poffset = fdt_obj.first_property_offset(self._offset, QUIET_NOTFOUND)
385 while poffset >= 0:
386 p = fdt_obj.get_property_by_offset(poffset)
387 prop = self.props.get(p.name)
388 if not prop:
Simon Glass93f18a12021-03-21 18:24:34 +1300389 raise ValueError("Internal error, node '%s' property '%s' missing, "
390 'offset %d' % (self.path, p.name, poffset))
Simon Glass4df8a0c2018-07-06 10:27:29 -0600391 prop.RefreshOffset(poffset)
392 poffset = fdt_obj.next_property_offset(poffset, QUIET_NOTFOUND)
Simon Glasscc346a72016-07-25 18:59:07 -0600393
Simon Glassc719e422016-07-25 18:59:14 -0600394 def DeleteProp(self, prop_name):
395 """Delete a property of a node
396
Simon Glass059ae522017-05-27 07:38:28 -0600397 The property is deleted and the offset cache is invalidated.
Simon Glassc719e422016-07-25 18:59:14 -0600398
399 Args:
400 prop_name: Name of the property to delete
Simon Glass059ae522017-05-27 07:38:28 -0600401 Raises:
402 ValueError if the property does not exist
Simon Glassc719e422016-07-25 18:59:14 -0600403 """
Simon Glass792d2392018-07-06 10:27:27 -0600404 CheckErr(self._fdt._fdt_obj.delprop(self.Offset(), prop_name),
Simon Glass059ae522017-05-27 07:38:28 -0600405 "Node '%s': delete property: '%s'" % (self.path, prop_name))
406 del self.props[prop_name]
407 self._fdt.Invalidate()
Simon Glassc719e422016-07-25 18:59:14 -0600408
Simon Glasse80c5562018-07-06 10:27:38 -0600409 def AddZeroProp(self, prop_name):
410 """Add a new property to the device tree with an integer value of 0.
411
412 Args:
413 prop_name: Name of property
414 """
Simon Glass6bbfafb2019-05-17 22:00:33 -0600415 self.props[prop_name] = Prop(self, None, prop_name,
Simon Glass80025522022-01-29 14:14:04 -0700416 tools.get_bytes(0, 4))
Simon Glasse80c5562018-07-06 10:27:38 -0600417
Simon Glassccd25262018-09-14 04:57:16 -0600418 def AddEmptyProp(self, prop_name, len):
419 """Add a property with a fixed data size, for filling in later
420
421 The device tree is marked dirty so that the value will be written to
422 the blob on the next sync.
423
424 Args:
425 prop_name: Name of property
426 len: Length of data in property
427 """
Simon Glass80025522022-01-29 14:14:04 -0700428 value = tools.get_bytes(0, len)
Simon Glassccd25262018-09-14 04:57:16 -0600429 self.props[prop_name] = Prop(self, None, prop_name, value)
430
Simon Glassa683a5f2019-07-20 12:23:37 -0600431 def _CheckProp(self, prop_name):
432 """Check if a property is present
433
434 Args:
435 prop_name: Name of property
436
437 Returns:
438 self
439
440 Raises:
441 ValueError if the property is missing
442 """
443 if prop_name not in self.props:
444 raise ValueError("Fdt '%s', node '%s': Missing property '%s'" %
445 (self._fdt._fname, self.path, prop_name))
446 return self
447
Simon Glasse80c5562018-07-06 10:27:38 -0600448 def SetInt(self, prop_name, val):
449 """Update an integer property int the device tree.
450
451 This is not allowed to change the size of the FDT.
452
Simon Glassccd25262018-09-14 04:57:16 -0600453 The device tree is marked dirty so that the value will be written to
454 the blob on the next sync.
455
Simon Glasse80c5562018-07-06 10:27:38 -0600456 Args:
457 prop_name: Name of property
458 val: Value to set
459 """
Simon Glassa683a5f2019-07-20 12:23:37 -0600460 self._CheckProp(prop_name).props[prop_name].SetInt(val)
Simon Glasseddd7292018-09-14 04:57:13 -0600461
Simon Glassccd25262018-09-14 04:57:16 -0600462 def SetData(self, prop_name, val):
463 """Set the data value of a property
464
465 The device tree is marked dirty so that the value will be written to
466 the blob on the next sync.
467
468 Args:
469 prop_name: Name of property to set
470 val: Data value to set
471 """
Simon Glassa683a5f2019-07-20 12:23:37 -0600472 self._CheckProp(prop_name).props[prop_name].SetData(val)
Simon Glassccd25262018-09-14 04:57:16 -0600473
474 def SetString(self, prop_name, val):
475 """Set the string value of a property
476
477 The device tree is marked dirty so that the value will be written to
478 the blob on the next sync.
479
480 Args:
481 prop_name: Name of property to set
482 val: String value to set (will be \0-terminated in DT)
483 """
Simon Glass39e22332019-10-31 07:43:04 -0600484 if type(val) == str:
485 val = val.encode('utf-8')
Simon Glassa683a5f2019-07-20 12:23:37 -0600486 self._CheckProp(prop_name).props[prop_name].SetData(val + b'\0')
Simon Glassccd25262018-09-14 04:57:16 -0600487
Simon Glassf67c99c2020-07-09 18:39:44 -0600488 def AddData(self, prop_name, val):
489 """Add a new property to a node
490
491 The device tree is marked dirty so that the value will be written to
492 the blob on the next sync.
493
494 Args:
495 prop_name: Name of property to add
496 val: Bytes value of property
Simon Glassd8bee462021-03-21 18:24:39 +1300497
498 Returns:
499 Prop added
Simon Glassf67c99c2020-07-09 18:39:44 -0600500 """
Simon Glassd8bee462021-03-21 18:24:39 +1300501 prop = Prop(self, None, prop_name, val)
502 self.props[prop_name] = prop
503 return prop
Simon Glassf67c99c2020-07-09 18:39:44 -0600504
Simon Glassccd25262018-09-14 04:57:16 -0600505 def AddString(self, prop_name, val):
506 """Add a new string property to a node
507
508 The device tree is marked dirty so that the value will be written to
509 the blob on the next sync.
510
511 Args:
512 prop_name: Name of property to add
513 val: String value of property
Simon Glassd8bee462021-03-21 18:24:39 +1300514
515 Returns:
516 Prop added
Simon Glassccd25262018-09-14 04:57:16 -0600517 """
Simon Glassd35a8522021-01-06 21:35:10 -0700518 val = bytes(val, 'utf-8')
Simon Glassd8bee462021-03-21 18:24:39 +1300519 return self.AddData(prop_name, val + b'\0')
Simon Glassccd25262018-09-14 04:57:16 -0600520
Simon Glass452be422022-02-08 11:49:50 -0700521 def AddStringList(self, prop_name, val):
522 """Add a new string-list property to a node
523
524 The device tree is marked dirty so that the value will be written to
525 the blob on the next sync.
526
527 Args:
528 prop_name: Name of property to add
529 val (list of str): List of strings to add
530
531 Returns:
532 Prop added
533 """
Simon Glass120fa002022-03-05 20:18:56 -0700534 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 -0700535 return self.AddData(prop_name, out)
536
Simon Glassa2af7302021-01-06 21:35:18 -0700537 def AddInt(self, prop_name, val):
538 """Add a new integer property to a node
539
540 The device tree is marked dirty so that the value will be written to
541 the blob on the next sync.
542
543 Args:
544 prop_name: Name of property to add
545 val: Integer value of property
Simon Glassd8bee462021-03-21 18:24:39 +1300546
547 Returns:
548 Prop added
Simon Glassa2af7302021-01-06 21:35:18 -0700549 """
Simon Glassd8bee462021-03-21 18:24:39 +1300550 return self.AddData(prop_name, struct.pack('>I', val))
Simon Glassa2af7302021-01-06 21:35:18 -0700551
Simon Glassadf8e052023-07-18 07:24:02 -0600552 def Subnode(self, name):
553 """Create new subnode for the node
Simon Glassccd25262018-09-14 04:57:16 -0600554
555 Args:
556 name: name of node to add
557
558 Returns:
559 New subnode that was created
560 """
Simon Glassf3a17962018-09-14 04:57:15 -0600561 path = self.path + '/' + name
Simon Glassadf8e052023-07-18 07:24:02 -0600562 return Node(self._fdt, self, None, name, path)
563
564 def AddSubnode(self, name):
565 """Add a new subnode to the node, after all other subnodes
566
567 Args:
568 name: name of node to add
569
570 Returns:
571 New subnode that was created
572 """
573 subnode = self.Subnode(name)
Simon Glassf3a17962018-09-14 04:57:15 -0600574 self.subnodes.append(subnode)
575 return subnode
576
Simon Glassadf8e052023-07-18 07:24:02 -0600577 def insert_subnode(self, name):
578 """Add a new subnode to the node, before all other subnodes
579
580 This deletes other subnodes and sets their offset to None, so that they
581 will be recreated after this one.
582
583 Args:
584 name: name of node to add
585
586 Returns:
587 New subnode that was created
588 """
589 # Deleting a node invalidates the offsets of all following nodes, so
590 # process in reverse order so that the offset of each node remains valid
591 # until deletion.
592 for subnode in reversed(self.subnodes):
593 subnode.purge(True)
594 subnode = self.Subnode(name)
595 self.subnodes.insert(0, subnode)
596 return subnode
597
598 def purge(self, delete_it=False):
599 """Purge this node, setting offset to None and deleting from FDT"""
600 if self._offset is not None:
601 if delete_it:
602 CheckErr(self._fdt._fdt_obj.del_node(self.Offset()),
603 "Node '%s': delete" % self.path)
604 self._offset = None
605 self._fdt.Invalidate()
606
607 for prop in self.props.values():
608 prop.purge()
609
610 for subnode in self.subnodes:
611 subnode.purge(False)
612
613 def move_to_first(self):
614 """Move the current node to first in its parent's node list"""
615 parent = self.parent
616 if parent.subnodes and parent.subnodes[0] == self:
617 return
618 for subnode in reversed(parent.subnodes):
619 subnode.purge(True)
620
621 new_subnodes = [self]
622 for subnode in parent.subnodes:
623 #subnode.purge(False)
624 if subnode != self:
625 new_subnodes.append(subnode)
626 parent.subnodes = new_subnodes
627
Simon Glassb9b5cb32022-02-08 11:49:51 -0700628 def Delete(self):
629 """Delete a node
630
631 The node is deleted and the offset cache is invalidated.
632
633 Args:
634 node (Node): Node to delete
635
636 Raises:
637 ValueError if the node does not exist
638 """
639 CheckErr(self._fdt._fdt_obj.del_node(self.Offset()),
640 "Node '%s': delete" % self.path)
641 parent = self.parent
642 self._fdt.Invalidate()
643 parent.subnodes.remove(self)
644
Simon Glasseddd7292018-09-14 04:57:13 -0600645 def Sync(self, auto_resize=False):
646 """Sync node changes back to the device tree
647
648 This updates the device tree blob with any changes to this node and its
649 subnodes since the last sync.
650
651 Args:
652 auto_resize: Resize the device tree automatically if it does not
653 have enough space for the update
654
Simon Glass3be798a2021-03-21 18:24:38 +1300655 Returns:
656 True if the node had to be added, False if it already existed
657
Simon Glasseddd7292018-09-14 04:57:13 -0600658 Raises:
659 FdtException if auto_resize is False and there is not enough space
660 """
Simon Glass3be798a2021-03-21 18:24:38 +1300661 added = False
Simon Glassf3a17962018-09-14 04:57:15 -0600662 if self._offset is None:
663 # The subnode doesn't exist yet, so add it
664 fdt_obj = self._fdt._fdt_obj
665 if auto_resize:
666 while True:
667 offset = fdt_obj.add_subnode(self.parent._offset, self.name,
668 (libfdt.NOSPACE,))
669 if offset != -libfdt.NOSPACE:
670 break
671 fdt_obj.resize(fdt_obj.totalsize() + 1024)
672 else:
673 offset = fdt_obj.add_subnode(self.parent._offset, self.name)
674 self._offset = offset
Simon Glass3be798a2021-03-21 18:24:38 +1300675 added = True
676
677 # Sync the existing subnodes first, so that we can rely on the offsets
678 # being correct. As soon as we add new subnodes, it pushes all the
679 # existing subnodes up.
680 for node in reversed(self.subnodes):
681 if node._offset is not None:
682 node.Sync(auto_resize)
Simon Glassf3a17962018-09-14 04:57:15 -0600683
Simon Glass3be798a2021-03-21 18:24:38 +1300684 # Sync subnodes in reverse so that we get the expected order. Each
685 # new node goes at the start of the subnode list. This avoids an O(n^2)
686 # rescan of node offsets.
687 num_added = 0
Simon Glasseddd7292018-09-14 04:57:13 -0600688 for node in reversed(self.subnodes):
Simon Glass3be798a2021-03-21 18:24:38 +1300689 if node.Sync(auto_resize):
690 num_added += 1
691 if num_added:
692 # Reorder our list of nodes to put the new ones first, since that's
693 # what libfdt does
694 old_count = len(self.subnodes) - num_added
695 subnodes = self.subnodes[old_count:] + self.subnodes[:old_count]
696 self.subnodes = subnodes
Simon Glasse80c5562018-07-06 10:27:38 -0600697
Simon Glass3be798a2021-03-21 18:24:38 +1300698 # Sync properties now, whose offsets should not have been disturbed,
699 # since properties come before subnodes. This is done after all the
700 # subnode processing above, since updating properties can disturb the
701 # offsets of those subnodes.
702 # Properties are synced in reverse order, with new properties added
703 # before existing properties are synced. This ensures that the offsets
704 # of earlier properties are not disturbed.
705 # Note that new properties will have an offset of None here, which
706 # Python cannot sort against int. So use a large value instead so that
707 # new properties are added first.
Simon Glassa57cfee2019-05-17 22:00:38 -0600708 prop_list = sorted(self.props.values(),
709 key=lambda prop: prop._offset or 1 << 31,
Simon Glasseddd7292018-09-14 04:57:13 -0600710 reverse=True)
711 for prop in prop_list:
712 prop.Sync(auto_resize)
Simon Glass3be798a2021-03-21 18:24:38 +1300713 return added
Simon Glasseddd7292018-09-14 04:57:13 -0600714
Simon Glass48ec1ff2023-07-22 21:43:55 -0600715 def merge_props(self, src, copy_phandles):
Simon Glassadf8e052023-07-18 07:24:02 -0600716 """Copy missing properties (except 'phandle') from another node
717
718 Args:
719 src (Node): Node containing properties to copy
Simon Glass48ec1ff2023-07-22 21:43:55 -0600720 copy_phandles (bool): True to copy phandle properties in nodes
Simon Glassadf8e052023-07-18 07:24:02 -0600721
722 Adds properties which are present in src but not in this node. Any
723 'phandle' property is not copied since this might result in two nodes
724 with the same phandle, thus making phandle references ambiguous.
725 """
Simon Glass638567d2023-07-22 21:43:54 -0600726 tout.debug(f'copy to {self.path}: {src.path}')
Simon Glassadf8e052023-07-18 07:24:02 -0600727 for name, src_prop in src.props.items():
Simon Glass638567d2023-07-22 21:43:54 -0600728 done = False
Simon Glass48ec1ff2023-07-22 21:43:55 -0600729 if name not in self.props:
730 if copy_phandles or name != 'phandle':
731 self.props[name] = Prop(self, None, name, src_prop.bytes)
732 done = True
Simon Glass638567d2023-07-22 21:43:54 -0600733 tout.debug(f" {name}{'' if done else ' - ignored'}")
Simon Glassadf8e052023-07-18 07:24:02 -0600734
Simon Glass48ec1ff2023-07-22 21:43:55 -0600735 def copy_node(self, src, copy_phandles=False):
Simon Glassadf8e052023-07-18 07:24:02 -0600736 """Copy a node and all its subnodes into this node
737
738 Args:
739 src (Node): Node to copy
Simon Glass48ec1ff2023-07-22 21:43:55 -0600740 copy_phandles (bool): True to copy phandle properties in nodes
Simon Glassadf8e052023-07-18 07:24:02 -0600741
742 Returns:
743 Node: Resulting destination node
744
Simon Glass48ec1ff2023-07-22 21:43:55 -0600745 This works recursively, with copy_phandles being set to True for the
746 recursive calls
Simon Glassadf8e052023-07-18 07:24:02 -0600747
748 The new node is put before all other nodes. If the node already
749 exists, just its subnodes and properties are copied, placing them before
750 any existing subnodes. Properties which exist in the destination node
751 already are not copied.
752 """
753 dst = self.FindNode(src.name)
754 if dst:
755 dst.move_to_first()
756 else:
757 dst = self.insert_subnode(src.name)
Simon Glass48ec1ff2023-07-22 21:43:55 -0600758 dst.merge_props(src, copy_phandles)
Simon Glassadf8e052023-07-18 07:24:02 -0600759
760 # Process in reverse order so that they appear correctly in the result,
761 # since copy_node() puts the node first in the list
762 for node in reversed(src.subnodes):
Simon Glass48ec1ff2023-07-22 21:43:55 -0600763 dst.copy_node(node, True)
Simon Glassadf8e052023-07-18 07:24:02 -0600764 return dst
765
Simon Glasscd971192023-07-18 07:24:03 -0600766 def copy_subnodes_from_phandles(self, phandle_list):
767 """Copy subnodes of a list of nodes into another node
768
769 Args:
770 phandle_list (list of int): List of phandles of nodes to copy
771
772 For each node in the phandle list, its subnodes and their properties are
773 copied recursively. Note that it does not copy the node itself, nor its
774 properties.
775 """
776 # Process in reverse order, since new nodes are inserted at the start of
777 # the destination's node list. We want them to appear in order of the
778 # phandle list
779 for phandle in phandle_list.__reversed__():
780 parent = self.GetFdt().LookupPhandle(phandle)
781 tout.debug(f'adding template {parent.path} to node {self.path}')
782 for node in parent.subnodes.__reversed__():
783 dst = self.copy_node(node)
784
785 tout.debug(f'merge props from {parent.path} to {dst.path}')
Simon Glass48ec1ff2023-07-22 21:43:55 -0600786 self.merge_props(parent, False)
Simon Glasscd971192023-07-18 07:24:03 -0600787
Simon Glasse80c5562018-07-06 10:27:38 -0600788
Simon Glass1f941b62016-07-25 18:59:04 -0600789class Fdt:
Simon Glass059ae522017-05-27 07:38:28 -0600790 """Provides simple access to a flat device tree blob using libfdts.
Simon Glass1f941b62016-07-25 18:59:04 -0600791
792 Properties:
793 fname: Filename of fdt
794 _root: Root of device tree (a Node object)
Simon Glass50cfc6e2019-07-20 12:23:38 -0600795 name: Helpful name for this Fdt for the user (useful when creating the
796 DT from data rather than a file)
Simon Glass1f941b62016-07-25 18:59:04 -0600797 """
798 def __init__(self, fname):
799 self._fname = fname
Simon Glass059ae522017-05-27 07:38:28 -0600800 self._cached_offsets = False
Simon Glassa3f94442017-08-29 14:15:52 -0600801 self.phandle_to_node = {}
Simon Glass50cfc6e2019-07-20 12:23:38 -0600802 self.name = ''
Simon Glass059ae522017-05-27 07:38:28 -0600803 if self._fname:
Simon Glass50cfc6e2019-07-20 12:23:38 -0600804 self.name = self._fname
Simon Glass059ae522017-05-27 07:38:28 -0600805 self._fname = fdt_util.EnsureCompiled(self._fname)
806
Simon Glass116236f2019-05-14 15:53:43 -0600807 with open(self._fname, 'rb') as fd:
Simon Glass792d2392018-07-06 10:27:27 -0600808 self._fdt_obj = libfdt.Fdt(fd.read())
Simon Glasscc346a72016-07-25 18:59:07 -0600809
Simon Glassb8a49292018-09-14 04:57:17 -0600810 @staticmethod
Simon Glass50cfc6e2019-07-20 12:23:38 -0600811 def FromData(data, name=''):
Simon Glassb8a49292018-09-14 04:57:17 -0600812 """Create a new Fdt object from the given data
813
814 Args:
815 data: Device-tree data blob
Simon Glass50cfc6e2019-07-20 12:23:38 -0600816 name: Helpful name for this Fdt for the user
Simon Glassb8a49292018-09-14 04:57:17 -0600817
818 Returns:
819 Fdt object containing the data
820 """
821 fdt = Fdt(None)
Simon Glass1cd40082019-05-17 22:00:36 -0600822 fdt._fdt_obj = libfdt.Fdt(bytes(data))
Simon Glass50cfc6e2019-07-20 12:23:38 -0600823 fdt.name = name
Simon Glassb8a49292018-09-14 04:57:17 -0600824 return fdt
825
Simon Glasse2d65282018-07-17 13:25:46 -0600826 def LookupPhandle(self, phandle):
827 """Look up a phandle
828
829 Args:
830 phandle: Phandle to look up (int)
831
832 Returns:
833 Node object the phandle points to
834 """
835 return self.phandle_to_node.get(phandle)
836
Simon Glasscc346a72016-07-25 18:59:07 -0600837 def Scan(self, root='/'):
838 """Scan a device tree, building up a tree of Node objects
839
840 This fills in the self._root property
841
842 Args:
843 root: Ignored
844
845 TODO(sjg@chromium.org): Implement the 'root' parameter
846 """
Simon Glass48ec1ff2023-07-22 21:43:55 -0600847 self.phandle_to_node = {}
Simon Glass4df8a0c2018-07-06 10:27:29 -0600848 self._cached_offsets = True
Simon Glass80ef7052017-08-29 14:15:47 -0600849 self._root = self.Node(self, None, 0, '/', '/')
Simon Glasscc346a72016-07-25 18:59:07 -0600850 self._root.Scan()
851
852 def GetRoot(self):
853 """Get the root Node of the device tree
854
855 Returns:
856 The root Node object
857 """
858 return self._root
859
860 def GetNode(self, path):
861 """Look up a node from its path
862
863 Args:
864 path: Path to look up, e.g. '/microcode/update@0'
865 Returns:
866 Node object, or None if not found
867 """
868 node = self._root
Simon Glassc5eddc82018-07-06 10:27:30 -0600869 parts = path.split('/')
870 if len(parts) < 2:
871 return None
Simon Glass3b9a8292019-07-20 12:23:39 -0600872 if len(parts) == 2 and parts[1] == '':
873 return node
Simon Glassc5eddc82018-07-06 10:27:30 -0600874 for part in parts[1:]:
Simon Glassaa1a5d72018-07-17 13:25:41 -0600875 node = node.FindNode(part)
Simon Glasscc346a72016-07-25 18:59:07 -0600876 if not node:
877 return None
878 return node
879
Simon Glass32d98272016-07-25 18:59:15 -0600880 def Flush(self):
881 """Flush device tree changes back to the file
882
883 If the device tree has changed in memory, write it back to the file.
Simon Glass32d98272016-07-25 18:59:15 -0600884 """
Simon Glass059ae522017-05-27 07:38:28 -0600885 with open(self._fname, 'wb') as fd:
Simon Glass792d2392018-07-06 10:27:27 -0600886 fd.write(self._fdt_obj.as_bytearray())
Simon Glass32d98272016-07-25 18:59:15 -0600887
Simon Glasseddd7292018-09-14 04:57:13 -0600888 def Sync(self, auto_resize=False):
889 """Make sure any DT changes are written to the blob
890
891 Args:
892 auto_resize: Resize the device tree automatically if it does not
893 have enough space for the update
894
895 Raises:
896 FdtException if auto_resize is False and there is not enough space
897 """
Simon Glassb2e8ac42021-03-21 18:24:36 +1300898 self.CheckCache()
Simon Glasseddd7292018-09-14 04:57:13 -0600899 self._root.Sync(auto_resize)
Simon Glassb2e8ac42021-03-21 18:24:36 +1300900 self.Refresh()
Simon Glasseddd7292018-09-14 04:57:13 -0600901
Simon Glass32d98272016-07-25 18:59:15 -0600902 def Pack(self):
903 """Pack the device tree down to its minimum size
904
905 When nodes and properties shrink or are deleted, wasted space can
Simon Glass059ae522017-05-27 07:38:28 -0600906 build up in the device tree binary.
907 """
Simon Glass151aeed2018-07-06 10:27:26 -0600908 CheckErr(self._fdt_obj.pack(), 'pack')
Simon Glassb2e8ac42021-03-21 18:24:36 +1300909 self.Refresh()
Simon Glass059ae522017-05-27 07:38:28 -0600910
Simon Glass792d2392018-07-06 10:27:27 -0600911 def GetContents(self):
Simon Glass059ae522017-05-27 07:38:28 -0600912 """Get the contents of the FDT
913
914 Returns:
915 The FDT contents as a string of bytes
Simon Glass32d98272016-07-25 18:59:15 -0600916 """
Simon Glass1cd40082019-05-17 22:00:36 -0600917 return bytes(self._fdt_obj.as_bytearray())
Simon Glass059ae522017-05-27 07:38:28 -0600918
Simon Glass0ed50752018-07-06 10:27:24 -0600919 def GetFdtObj(self):
920 """Get the contents of the FDT
921
922 Returns:
923 The FDT contents as a libfdt.Fdt object
924 """
925 return self._fdt_obj
926
Simon Glass059ae522017-05-27 07:38:28 -0600927 def GetProps(self, node):
928 """Get all properties from a node.
929
930 Args:
931 node: Full path to node name to look in.
932
933 Returns:
934 A dictionary containing all the properties, indexed by node name.
935 The entries are Prop objects.
936
937 Raises:
938 ValueError: if the node does not exist.
939 """
940 props_dict = {}
Simon Glass151aeed2018-07-06 10:27:26 -0600941 poffset = self._fdt_obj.first_property_offset(node._offset,
942 QUIET_NOTFOUND)
Simon Glass059ae522017-05-27 07:38:28 -0600943 while poffset >= 0:
944 p = self._fdt_obj.get_property_by_offset(poffset)
Simon Glass70cd0d72018-07-06 10:27:20 -0600945 prop = Prop(node, poffset, p.name, p)
Simon Glass059ae522017-05-27 07:38:28 -0600946 props_dict[prop.name] = prop
947
Simon Glass151aeed2018-07-06 10:27:26 -0600948 poffset = self._fdt_obj.next_property_offset(poffset,
949 QUIET_NOTFOUND)
Simon Glass059ae522017-05-27 07:38:28 -0600950 return props_dict
951
952 def Invalidate(self):
953 """Mark our offset cache as invalid"""
954 self._cached_offsets = False
955
956 def CheckCache(self):
957 """Refresh the offset cache if needed"""
958 if self._cached_offsets:
959 return
960 self.Refresh()
Simon Glass059ae522017-05-27 07:38:28 -0600961
962 def Refresh(self):
963 """Refresh the offset cache"""
964 self._root.Refresh(0)
Simon Glassb2e8ac42021-03-21 18:24:36 +1300965 self._cached_offsets = True
Simon Glass059ae522017-05-27 07:38:28 -0600966
967 def GetStructOffset(self, offset):
968 """Get the file offset of a given struct offset
969
970 Args:
971 offset: Offset within the 'struct' region of the device tree
972 Returns:
973 Position of @offset within the device tree binary
974 """
Simon Glass151aeed2018-07-06 10:27:26 -0600975 return self._fdt_obj.off_dt_struct() + offset
Simon Glass059ae522017-05-27 07:38:28 -0600976
977 @classmethod
Simon Glass80ef7052017-08-29 14:15:47 -0600978 def Node(self, fdt, parent, offset, name, path):
Simon Glass059ae522017-05-27 07:38:28 -0600979 """Create a new node
980
981 This is used by Fdt.Scan() to create a new node using the correct
982 class.
983
984 Args:
985 fdt: Fdt object
Simon Glass80ef7052017-08-29 14:15:47 -0600986 parent: Parent node, or None if this is the root node
Simon Glass059ae522017-05-27 07:38:28 -0600987 offset: Offset of node
988 name: Node name
989 path: Full path to node
990 """
Simon Glass80ef7052017-08-29 14:15:47 -0600991 node = Node(fdt, parent, offset, name, path)
Simon Glass059ae522017-05-27 07:38:28 -0600992 return node
Simon Glassa9440932017-05-27 07:38:30 -0600993
Simon Glass74f5feb2019-07-20 12:24:08 -0600994 def GetFilename(self):
995 """Get the filename of the device tree
996
997 Returns:
998 String filename
999 """
1000 return self._fname
1001
Simon Glassa9440932017-05-27 07:38:30 -06001002def FdtScan(fname):
Simon Glassc38fee042018-07-06 10:27:32 -06001003 """Returns a new Fdt object"""
Simon Glassa9440932017-05-27 07:38:30 -06001004 dtb = Fdt(fname)
1005 dtb.Scan()
1006 return dtb