blob: 5963925146a5df3fadbf29e33f6d5e995b5ff300 [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
Simon Glass5ea9dcc2020-11-08 20:36:17 -07008from enum import IntEnum
Simon Glassa06a34b2016-07-25 18:59:04 -06009import struct
10import sys
11
Simon Glassbf776672020-04-17 18:09:04 -060012from dtoc import fdt_util
Simon Glass7b75b442017-05-27 07:38:28 -060013import libfdt
Simon Glass117f57b2018-07-06 10:27:26 -060014from libfdt import QUIET_NOTFOUND
Simon Glass4583c002023-02-23 18:18:04 -070015from u_boot_pylib import tools
Simon Glass4df457b2023-07-18 07:24:02 -060016from u_boot_pylib import tout
Simon Glassa06a34b2016-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 Glass99ed4a22017-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 Glass7b75b442017-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 Glassa06a34b2016-07-25 18:59:04 -060025
Simon Glassbc1dea32016-07-25 18:59:05 -060026# A list of types we support
Simon Glass5ea9dcc2020-11-08 20:36:17 -070027class Type(IntEnum):
Simon Glassdf82de82021-07-28 19:23:09 -060028 # Types in order from widest to narrowest
Simon Glass5ea9dcc2020-11-08 20:36:17 -070029 (BYTE, INT, STRING, BOOL, INT64) = range(5)
30
Simon Glassdf82de82021-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 Glass5ea9dcc2020-11-08 20:36:17 -070033
Simon Glassdf82de82021-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 Glass5ea9dcc2020-11-08 20:36:17 -070038
39 This uses a simple arithmetic comparison, since type values are in order
Simon Glassdf82de82021-07-28 19:23:09 -060040 from widest (BYTE) to narrowest (INT64).
Simon Glass5ea9dcc2020-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 Glassbc1dea32016-07-25 18:59:05 -060049
Simon Glassa06a34b2016-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 Glass7e6952d2019-05-17 22:00:34 -060055
Simon Glass2b6ed5e2019-05-17 22:00:35 -060056def BytesToValue(data):
Simon Glass7e6952d2019-05-17 22:00:34 -060057 """Converts a string of bytes into a type and value
58
59 Args:
Simon Glass2b6ed5e2019-05-17 22:00:35 -060060 A bytes value (which on Python 2 is an alias for str)
Simon Glass7e6952d2019-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 Glass5ea9dcc2020-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 Glass7e6952d2019-05-17 22:00:34 -060070 """
Simon Glass2b6ed5e2019-05-17 22:00:35 -060071 data = bytes(data)
72 size = len(data)
73 strings = data.split(b'\0')
Simon Glass7e6952d2019-05-17 22:00:34 -060074 is_string = True
75 count = len(strings) - 1
Simon Glass2b6ed5e2019-05-17 22:00:35 -060076 if count > 0 and not len(strings[-1]):
Simon Glass7e6952d2019-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 Glass2b6ed5e2019-05-17 22:00:35 -060082 if ch < 32 or ch > 127:
Simon Glass7e6952d2019-05-17 22:00:34 -060083 is_string = False
84 break
85 else:
86 is_string = False
87 if is_string:
Simon Glass2b6ed5e2019-05-17 22:00:35 -060088 if count == 1:
Simon Glass5ea9dcc2020-11-08 20:36:17 -070089 return Type.STRING, strings[0].decode()
Simon Glass7e6952d2019-05-17 22:00:34 -060090 else:
Simon Glass5ea9dcc2020-11-08 20:36:17 -070091 return Type.STRING, [s.decode() for s in strings[:-1]]
Simon Glass7e6952d2019-05-17 22:00:34 -060092 if size % 4:
93 if size == 1:
Simon Glass479dd302020-11-08 20:36:20 -070094 return Type.BYTE, chr(data[0])
Simon Glass7e6952d2019-05-17 22:00:34 -060095 else:
Simon Glass479dd302020-11-08 20:36:20 -070096 return Type.BYTE, [chr(ch) for ch in list(data)]
Simon Glass7e6952d2019-05-17 22:00:34 -060097 val = []
98 for i in range(0, size, 4):
Simon Glass2b6ed5e2019-05-17 22:00:35 -060099 val.append(data[i:i + 4])
Simon Glass7e6952d2019-05-17 22:00:34 -0600100 if size == 4:
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700101 return Type.INT, val[0]
Simon Glass7e6952d2019-05-17 22:00:34 -0600102 else:
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700103 return Type.INT, val
Simon Glass7e6952d2019-05-17 22:00:34 -0600104
105
Simon Glass7b75b442017-05-27 07:38:28 -0600106class Prop:
Simon Glassa06a34b2016-07-25 18:59:04 -0600107 """A device tree property
108
109 Properties:
Simon Glass37ba9842021-03-21 18:24:35 +1300110 node: Node containing this property
111 offset: Offset of the property (None if still to be synced)
Simon Glassa06a34b2016-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 Glass928527f2019-05-17 22:00:37 -0600117 def __init__(self, node, offset, name, data):
Simon Glassa06a34b2016-07-25 18:59:04 -0600118 self._node = node
119 self._offset = offset
120 self.name = name
121 self.value = None
Simon Glass928527f2019-05-17 22:00:37 -0600122 self.bytes = bytes(data)
Simon Glass37ba9842021-03-21 18:24:35 +1300123 self.dirty = offset is None
Simon Glass928527f2019-05-17 22:00:37 -0600124 if not data:
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700125 self.type = Type.BOOL
Simon Glass7b75b442017-05-27 07:38:28 -0600126 self.value = True
127 return
Simon Glass928527f2019-05-17 22:00:37 -0600128 self.type, self.value = BytesToValue(bytes(data))
Simon Glassa06a34b2016-07-25 18:59:04 -0600129
Simon Glassf9b88b32018-07-06 10:27:29 -0600130 def RefreshOffset(self, poffset):
131 self._offset = poffset
132
Simon Glassc322a852016-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 Glassdf82de82021-07-28 19:23:09 -0600156 if self.type.needs_widening(newprop.type):
Simon Glasseec44c72021-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 Glass5ea9dcc2020-11-08 20:36:17 -0700169 if self.type == Type.INT and newprop.type == Type.BYTE:
Simon Glasse144caf2020-10-03 11:31:27 -0600170 if type(self.value) == list:
171 new_value = []
172 for val in self.value:
Simon Glass479dd302020-11-08 20:36:20 -0700173 new_value += [chr(by) for by in val]
Simon Glasse144caf2020-10-03 11:31:27 -0600174 else:
Simon Glass479dd302020-11-08 20:36:20 -0700175 new_value = [chr(by) for by in self.value]
Simon Glasse144caf2020-10-03 11:31:27 -0600176 self.value = new_value
Simon Glassc322a852016-07-25 18:59:06 -0600177 self.type = newprop.type
178
Simon Glassca044942021-07-28 19:23:10 -0600179 if type(newprop.value) == list:
180 if type(self.value) != list:
181 self.value = [self.value]
Simon Glassc322a852016-07-25 18:59:06 -0600182
Simon Glassca044942021-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 Glassc322a852016-07-25 18:59:06 -0600187
Simon Glass2ba98752018-07-06 10:27:24 -0600188 @classmethod
Simon Glassbc1dea32016-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 Glass5ea9dcc2020-11-08 20:36:17 -0700195 if type == Type.BYTE:
Simon Glassbc1dea32016-07-25 18:59:05 -0600196 return chr(0)
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700197 elif type == Type.INT:
Simon Glassaf53f5a2018-09-14 04:57:14 -0600198 return struct.pack('>I', 0);
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700199 elif type == Type.STRING:
Simon Glassbc1dea32016-07-25 18:59:05 -0600200 return ''
201 else:
202 return True
203
Simon Glassbabdbde2016-07-25 18:59:16 -0600204 def GetOffset(self):
205 """Get the offset of a property
206
Simon Glassbabdbde2016-07-25 18:59:16 -0600207 Returns:
Simon Glass7b75b442017-05-27 07:38:28 -0600208 The offset of the property (struct fdt_property) within the file
Simon Glassbabdbde2016-07-25 18:59:16 -0600209 """
Simon Glassf9b88b32018-07-06 10:27:29 -0600210 self._node._fdt.CheckCache()
Simon Glass7b75b442017-05-27 07:38:28 -0600211 return self._node._fdt.GetStructOffset(self._offset)
Simon Glassbabdbde2016-07-25 18:59:16 -0600212
Simon Glassfa80c252018-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 Glass41b781d2018-10-01 12:22:49 -0600223 self.value = self.bytes
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700224 self.type = Type.INT
Simon Glassfa80c252018-09-14 04:57:13 -0600225 self.dirty = True
226
Simon Glass64349612018-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 Glassf6b64812019-05-17 22:00:36 -0600233 self.bytes = bytes
Simon Glass7e6952d2019-05-17 22:00:34 -0600234 self.type, self.value = BytesToValue(bytes)
Simon Glass64349612018-09-14 04:57:16 -0600235 self.dirty = True
236
Simon Glassfa80c252018-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 Glass37ba9842021-03-21 18:24:35 +1300250 if self.dirty:
Simon Glassfa80c252018-09-14 04:57:13 -0600251 node = self._node
Simon Glass8df8b6d2023-07-22 21:43:54 -0600252 tout.debug(f'sync {node.path}: {self.name}')
Simon Glassfa80c252018-09-14 04:57:13 -0600253 fdt_obj = node._fdt._fdt_obj
Simon Glass5d1bec32021-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 Glassfa80c252018-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 Glassc0639172020-07-09 18:39:44 -0600262 fdt_obj.resize(fdt_obj.totalsize() + 1024 +
263 len(self.bytes))
Simon Glassfa80c252018-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 Glass37ba9842021-03-21 18:24:35 +1300267 self.dirty = False
Simon Glassfa80c252018-09-14 04:57:13 -0600268
Simon Glass4df457b2023-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 Glass71556462023-07-22 21:43:53 -0600276 self.dirty = True
Simon Glassfa80c252018-09-14 04:57:13 -0600277
Simon Glass7b75b442017-05-27 07:38:28 -0600278class Node:
Simon Glassa06a34b2016-07-25 18:59:04 -0600279 """A device tree node
280
281 Properties:
Simon Glass37ba9842021-03-21 18:24:35 +1300282 parent: Parent Node
283 offset: Integer offset in the device tree (None if to be synced)
Simon Glassa06a34b2016-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 Glass979ab022017-08-29 14:15:47 -0600291 def __init__(self, fdt, parent, offset, name, path):
Simon Glassa06a34b2016-07-25 18:59:04 -0600292 self._fdt = fdt
Simon Glass979ab022017-08-29 14:15:47 -0600293 self.parent = parent
Simon Glassa06a34b2016-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 Glass94a7c602018-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 Glass1d858882018-07-17 13:25:41 -0600308 def FindNode(self, name):
Simon Glassf7a2aee2016-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 Glass7b75b442017-05-27 07:38:28 -0600321 def Offset(self):
322 """Returns the offset of a node, after checking the cache
Simon Glassf7a2aee2016-07-25 18:59:07 -0600323
Simon Glass7b75b442017-05-27 07:38:28 -0600324 This should be used instead of self._offset directly, to ensure that
325 the cache does not contain invalid offsets.
Simon Glassf7a2aee2016-07-25 18:59:07 -0600326 """
Simon Glass7b75b442017-05-27 07:38:28 -0600327 self._fdt.CheckCache()
328 return self._offset
329
330 def Scan(self):
331 """Scan a node's properties and subnodes
332
333 This fills in the props and subnodes properties, recursively
334 searching into subnodes so that the entire tree is built.
335 """
Simon Glass117f57b2018-07-06 10:27:26 -0600336 fdt_obj = self._fdt._fdt_obj
Simon Glass7b75b442017-05-27 07:38:28 -0600337 self.props = self._fdt.GetProps(self)
Simon Glass117f57b2018-07-06 10:27:26 -0600338 phandle = fdt_obj.get_phandle(self.Offset())
Simon Glass09264e02017-08-29 14:15:52 -0600339 if phandle:
Simon Glass589c2d92023-07-22 21:43:55 -0600340 dup = self._fdt.phandle_to_node.get(phandle)
341 if dup:
Simon Glassbbbf04c2023-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 Glass7b75b442017-05-27 07:38:28 -0600346
Simon Glass117f57b2018-07-06 10:27:26 -0600347 offset = fdt_obj.first_subnode(self.Offset(), QUIET_NOTFOUND)
Simon Glass7b75b442017-05-27 07:38:28 -0600348 while offset >= 0:
349 sep = '' if self.path[-1] == '/' else '/'
Simon Glass117f57b2018-07-06 10:27:26 -0600350 name = fdt_obj.get_name(offset)
Simon Glass7b75b442017-05-27 07:38:28 -0600351 path = self.path + sep + name
Simon Glass979ab022017-08-29 14:15:47 -0600352 node = Node(self._fdt, self, offset, name, path)
Simon Glass7b75b442017-05-27 07:38:28 -0600353 self.subnodes.append(node)
354
355 node.Scan()
Simon Glass117f57b2018-07-06 10:27:26 -0600356 offset = fdt_obj.next_subnode(offset, QUIET_NOTFOUND)
Simon Glass7b75b442017-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 Glass96066242018-07-06 10:27:27 -0600364 fdt_obj = self._fdt._fdt_obj
Simon Glass7b75b442017-05-27 07:38:28 -0600365 if self._offset != my_offset:
Simon Glass7b75b442017-05-27 07:38:28 -0600366 self._offset = my_offset
Simon Glass5d1bec32021-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 Glass96066242018-07-06 10:27:27 -0600372 offset = fdt_obj.first_subnode(self._offset, QUIET_NOTFOUND)
Simon Glass7b75b442017-05-27 07:38:28 -0600373 for subnode in self.subnodes:
Simon Glassdd857ee2022-02-08 11:49:52 -0700374 if subnode._offset is None:
375 continue
Simon Glassf9b88b32018-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 Glass7b75b442017-05-27 07:38:28 -0600379 subnode.Refresh(offset)
Simon Glass96066242018-07-06 10:27:27 -0600380 offset = fdt_obj.next_subnode(offset, QUIET_NOTFOUND)
Simon Glassf9b88b32018-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 Glassacd98612021-03-21 18:24:34 +1300389 raise ValueError("Internal error, node '%s' property '%s' missing, "
390 'offset %d' % (self.path, p.name, poffset))
Simon Glassf9b88b32018-07-06 10:27:29 -0600391 prop.RefreshOffset(poffset)
392 poffset = fdt_obj.next_property_offset(poffset, QUIET_NOTFOUND)
Simon Glassf7a2aee2016-07-25 18:59:07 -0600393
Simon Glass2a70d892016-07-25 18:59:14 -0600394 def DeleteProp(self, prop_name):
395 """Delete a property of a node
396
Simon Glass7b75b442017-05-27 07:38:28 -0600397 The property is deleted and the offset cache is invalidated.
Simon Glass2a70d892016-07-25 18:59:14 -0600398
399 Args:
400 prop_name: Name of the property to delete
Simon Glass7b75b442017-05-27 07:38:28 -0600401 Raises:
402 ValueError if the property does not exist
Simon Glass2a70d892016-07-25 18:59:14 -0600403 """
Simon Glass96066242018-07-06 10:27:27 -0600404 CheckErr(self._fdt._fdt_obj.delprop(self.Offset(), prop_name),
Simon Glass7b75b442017-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 Glass2a70d892016-07-25 18:59:14 -0600408
Simon Glass116adec2018-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 Glass194b8d52019-05-17 22:00:33 -0600415 self.props[prop_name] = Prop(self, None, prop_name,
Simon Glassc1aa66e2022-01-29 14:14:04 -0700416 tools.get_bytes(0, 4))
Simon Glass116adec2018-07-06 10:27:38 -0600417
Simon Glass64349612018-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 Glassc1aa66e2022-01-29 14:14:04 -0700428 value = tools.get_bytes(0, len)
Simon Glass64349612018-09-14 04:57:16 -0600429 self.props[prop_name] = Prop(self, None, prop_name, value)
430
Simon Glassd9dad102019-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 Glass116adec2018-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 Glass64349612018-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 Glass116adec2018-07-06 10:27:38 -0600456 Args:
457 prop_name: Name of property
458 val: Value to set
459 """
Simon Glassd9dad102019-07-20 12:23:37 -0600460 self._CheckProp(prop_name).props[prop_name].SetInt(val)
Simon Glassfa80c252018-09-14 04:57:13 -0600461
Simon Glass64349612018-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 Glassd9dad102019-07-20 12:23:37 -0600472 self._CheckProp(prop_name).props[prop_name].SetData(val)
Simon Glass64349612018-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 Glassa90df2b2019-10-31 07:43:04 -0600484 if type(val) == str:
485 val = val.encode('utf-8')
Simon Glassd9dad102019-07-20 12:23:37 -0600486 self._CheckProp(prop_name).props[prop_name].SetData(val + b'\0')
Simon Glass64349612018-09-14 04:57:16 -0600487
Simon Glassc0639172020-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 Glass5d1bec32021-03-21 18:24:39 +1300497
498 Returns:
499 Prop added
Simon Glassc0639172020-07-09 18:39:44 -0600500 """
Simon Glass5d1bec32021-03-21 18:24:39 +1300501 prop = Prop(self, None, prop_name, val)
502 self.props[prop_name] = prop
503 return prop
Simon Glassc0639172020-07-09 18:39:44 -0600504
Simon Glass64349612018-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 Glass5d1bec32021-03-21 18:24:39 +1300514
515 Returns:
516 Prop added
Simon Glass64349612018-09-14 04:57:16 -0600517 """
Simon Glass9fc6ebd2021-01-06 21:35:10 -0700518 val = bytes(val, 'utf-8')
Simon Glass5d1bec32021-03-21 18:24:39 +1300519 return self.AddData(prop_name, val + b'\0')
Simon Glass64349612018-09-14 04:57:16 -0600520
Simon Glassbc116022022-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 Glass0ded4d42022-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 Glassbc116022022-02-08 11:49:50 -0700535 return self.AddData(prop_name, out)
536
Simon Glass6eb99322021-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 Glass5d1bec32021-03-21 18:24:39 +1300546
547 Returns:
548 Prop added
Simon Glass6eb99322021-01-06 21:35:18 -0700549 """
Simon Glass5d1bec32021-03-21 18:24:39 +1300550 return self.AddData(prop_name, struct.pack('>I', val))
Simon Glass6eb99322021-01-06 21:35:18 -0700551
Simon Glass4df457b2023-07-18 07:24:02 -0600552 def Subnode(self, name):
553 """Create new subnode for the node
Simon Glass64349612018-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 Glasse21c27a2018-09-14 04:57:15 -0600561 path = self.path + '/' + name
Simon Glass4df457b2023-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 Glasse21c27a2018-09-14 04:57:15 -0600574 self.subnodes.append(subnode)
575 return subnode
576
Simon Glass4df457b2023-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 Glassa30c39f2022-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 Glassfa80c252018-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 Glassf6176652021-03-21 18:24:38 +1300655 Returns:
656 True if the node had to be added, False if it already existed
657
Simon Glassfa80c252018-09-14 04:57:13 -0600658 Raises:
659 FdtException if auto_resize is False and there is not enough space
660 """
Simon Glassf6176652021-03-21 18:24:38 +1300661 added = False
Simon Glasse21c27a2018-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 Glassf6176652021-03-21 18:24:38 +1300675 added = True
Simon Glasse21c27a2018-09-14 04:57:15 -0600676
Simon Glassf6176652021-03-21 18:24:38 +1300677 # 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.
Simon Glassfa80c252018-09-14 04:57:13 -0600680 for node in reversed(self.subnodes):
Simon Glassf6176652021-03-21 18:24:38 +1300681 if node._offset is not None:
682 node.Sync(auto_resize)
Simon Glassfa80c252018-09-14 04:57:13 -0600683
Simon Glassf6176652021-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
688 for node in reversed(self.subnodes):
689 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
697
698 # 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 Glass63518052019-05-17 22:00:38 -0600708 prop_list = sorted(self.props.values(),
709 key=lambda prop: prop._offset or 1 << 31,
Simon Glassfa80c252018-09-14 04:57:13 -0600710 reverse=True)
711 for prop in prop_list:
712 prop.Sync(auto_resize)
Simon Glassf6176652021-03-21 18:24:38 +1300713 return added
Simon Glass116adec2018-07-06 10:27:38 -0600714
Simon Glass589c2d92023-07-22 21:43:55 -0600715 def merge_props(self, src, copy_phandles):
Simon Glass4df457b2023-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 Glass589c2d92023-07-22 21:43:55 -0600720 copy_phandles (bool): True to copy phandle properties in nodes
Simon Glass4df457b2023-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 Glass8df8b6d2023-07-22 21:43:54 -0600726 tout.debug(f'copy to {self.path}: {src.path}')
Simon Glass4df457b2023-07-18 07:24:02 -0600727 for name, src_prop in src.props.items():
Simon Glass8df8b6d2023-07-22 21:43:54 -0600728 done = False
Simon Glass589c2d92023-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 Glass8df8b6d2023-07-22 21:43:54 -0600733 tout.debug(f" {name}{'' if done else ' - ignored'}")
Simon Glass4df457b2023-07-18 07:24:02 -0600734
Simon Glass589c2d92023-07-22 21:43:55 -0600735 def copy_node(self, src, copy_phandles=False):
Simon Glass4df457b2023-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 Glass589c2d92023-07-22 21:43:55 -0600740 copy_phandles (bool): True to copy phandle properties in nodes
Simon Glass4df457b2023-07-18 07:24:02 -0600741
742 Returns:
743 Node: Resulting destination node
744
Simon Glass589c2d92023-07-22 21:43:55 -0600745 This works recursively, with copy_phandles being set to True for the
746 recursive calls
Simon Glass4df457b2023-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 Glass589c2d92023-07-22 21:43:55 -0600758 dst.merge_props(src, copy_phandles)
Simon Glass4df457b2023-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 Glass589c2d92023-07-22 21:43:55 -0600763 dst.copy_node(node, True)
Simon Glass4df457b2023-07-18 07:24:02 -0600764 return dst
765
Simon Glass55e12782023-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 Glass589c2d92023-07-22 21:43:55 -0600786 self.merge_props(parent, False)
Simon Glass55e12782023-07-18 07:24:03 -0600787
Simon Glass116adec2018-07-06 10:27:38 -0600788
Simon Glassa06a34b2016-07-25 18:59:04 -0600789class Fdt:
Simon Glass7b75b442017-05-27 07:38:28 -0600790 """Provides simple access to a flat device tree blob using libfdts.
Simon Glassa06a34b2016-07-25 18:59:04 -0600791
792 Properties:
793 fname: Filename of fdt
794 _root: Root of device tree (a Node object)
Simon Glass880e9ee2019-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 Glassa06a34b2016-07-25 18:59:04 -0600797 """
798 def __init__(self, fname):
799 self._fname = fname
Simon Glass7b75b442017-05-27 07:38:28 -0600800 self._cached_offsets = False
Simon Glass09264e02017-08-29 14:15:52 -0600801 self.phandle_to_node = {}
Simon Glass880e9ee2019-07-20 12:23:38 -0600802 self.name = ''
Simon Glass7b75b442017-05-27 07:38:28 -0600803 if self._fname:
Simon Glass880e9ee2019-07-20 12:23:38 -0600804 self.name = self._fname
Simon Glass7b75b442017-05-27 07:38:28 -0600805 self._fname = fdt_util.EnsureCompiled(self._fname)
806
Simon Glass3e4b51e2019-05-14 15:53:43 -0600807 with open(self._fname, 'rb') as fd:
Simon Glass96066242018-07-06 10:27:27 -0600808 self._fdt_obj = libfdt.Fdt(fd.read())
Simon Glassf7a2aee2016-07-25 18:59:07 -0600809
Simon Glass746aee32018-09-14 04:57:17 -0600810 @staticmethod
Simon Glass880e9ee2019-07-20 12:23:38 -0600811 def FromData(data, name=''):
Simon Glass746aee32018-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 Glass880e9ee2019-07-20 12:23:38 -0600816 name: Helpful name for this Fdt for the user
Simon Glass746aee32018-09-14 04:57:17 -0600817
818 Returns:
819 Fdt object containing the data
820 """
821 fdt = Fdt(None)
Simon Glassf6b64812019-05-17 22:00:36 -0600822 fdt._fdt_obj = libfdt.Fdt(bytes(data))
Simon Glass880e9ee2019-07-20 12:23:38 -0600823 fdt.name = name
Simon Glass746aee32018-09-14 04:57:17 -0600824 return fdt
825
Simon Glass94a7c602018-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 Glassf7a2aee2016-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 Glass589c2d92023-07-22 21:43:55 -0600847 self.phandle_to_node = {}
Simon Glassf9b88b32018-07-06 10:27:29 -0600848 self._cached_offsets = True
Simon Glass979ab022017-08-29 14:15:47 -0600849 self._root = self.Node(self, None, 0, '/', '/')
Simon Glassf7a2aee2016-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 Glassb9066ff2018-07-06 10:27:30 -0600869 parts = path.split('/')
870 if len(parts) < 2:
871 return None
Simon Glasse44bc832019-07-20 12:23:39 -0600872 if len(parts) == 2 and parts[1] == '':
873 return node
Simon Glassb9066ff2018-07-06 10:27:30 -0600874 for part in parts[1:]:
Simon Glass1d858882018-07-17 13:25:41 -0600875 node = node.FindNode(part)
Simon Glassf7a2aee2016-07-25 18:59:07 -0600876 if not node:
877 return None
878 return node
879
Simon Glassda5f7492016-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 Glassda5f7492016-07-25 18:59:15 -0600884 """
Simon Glass7b75b442017-05-27 07:38:28 -0600885 with open(self._fname, 'wb') as fd:
Simon Glass96066242018-07-06 10:27:27 -0600886 fd.write(self._fdt_obj.as_bytearray())
Simon Glassda5f7492016-07-25 18:59:15 -0600887
Simon Glassfa80c252018-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 Glass71719e12021-03-21 18:24:36 +1300898 self.CheckCache()
Simon Glassfa80c252018-09-14 04:57:13 -0600899 self._root.Sync(auto_resize)
Simon Glass71719e12021-03-21 18:24:36 +1300900 self.Refresh()
Simon Glassfa80c252018-09-14 04:57:13 -0600901
Simon Glassda5f7492016-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 Glass7b75b442017-05-27 07:38:28 -0600906 build up in the device tree binary.
Simon Glassda5f7492016-07-25 18:59:15 -0600907 """
Simon Glass117f57b2018-07-06 10:27:26 -0600908 CheckErr(self._fdt_obj.pack(), 'pack')
Simon Glass71719e12021-03-21 18:24:36 +1300909 self.Refresh()
Simon Glass7b75b442017-05-27 07:38:28 -0600910
Simon Glass96066242018-07-06 10:27:27 -0600911 def GetContents(self):
Simon Glass7b75b442017-05-27 07:38:28 -0600912 """Get the contents of the FDT
913
914 Returns:
915 The FDT contents as a string of bytes
916 """
Simon Glassf6b64812019-05-17 22:00:36 -0600917 return bytes(self._fdt_obj.as_bytearray())
Simon Glass7b75b442017-05-27 07:38:28 -0600918
Simon Glass2ba98752018-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 Glass7b75b442017-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 Glass117f57b2018-07-06 10:27:26 -0600941 poffset = self._fdt_obj.first_property_offset(node._offset,
942 QUIET_NOTFOUND)
Simon Glass7b75b442017-05-27 07:38:28 -0600943 while poffset >= 0:
944 p = self._fdt_obj.get_property_by_offset(poffset)
Simon Glass3def0cf2018-07-06 10:27:20 -0600945 prop = Prop(node, poffset, p.name, p)
Simon Glass7b75b442017-05-27 07:38:28 -0600946 props_dict[prop.name] = prop
947
Simon Glass117f57b2018-07-06 10:27:26 -0600948 poffset = self._fdt_obj.next_property_offset(poffset,
949 QUIET_NOTFOUND)
Simon Glass7b75b442017-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 Glass7b75b442017-05-27 07:38:28 -0600961
962 def Refresh(self):
963 """Refresh the offset cache"""
964 self._root.Refresh(0)
Simon Glass71719e12021-03-21 18:24:36 +1300965 self._cached_offsets = True
Simon Glass7b75b442017-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 Glass117f57b2018-07-06 10:27:26 -0600975 return self._fdt_obj.off_dt_struct() + offset
Simon Glass7b75b442017-05-27 07:38:28 -0600976
977 @classmethod
Simon Glass979ab022017-08-29 14:15:47 -0600978 def Node(self, fdt, parent, offset, name, path):
Simon Glass7b75b442017-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 Glass979ab022017-08-29 14:15:47 -0600986 parent: Parent node, or None if this is the root node
Simon Glass7b75b442017-05-27 07:38:28 -0600987 offset: Offset of node
988 name: Node name
989 path: Full path to node
990 """
Simon Glass979ab022017-08-29 14:15:47 -0600991 node = Node(fdt, parent, offset, name, path)
Simon Glass7b75b442017-05-27 07:38:28 -0600992 return node
Simon Glass99ed4a22017-05-27 07:38:30 -0600993
Simon Glassf6e02492019-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 Glass99ed4a22017-05-27 07:38:30 -06001002def FdtScan(fname):
Simon Glassdfe5f5b2018-07-06 10:27:32 -06001003 """Returns a new Fdt object"""
Simon Glass99ed4a22017-05-27 07:38:30 -06001004 dtb = Fdt(fname)
1005 dtb.Scan()
1006 return dtb