blob: 429e95f9a96cd492274acd3adeddda4f1e2ab5fa [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 Glassa997ea52020-04-17 18:09:04 -060015from patman import tools
Simon Glass1f941b62016-07-25 18:59:04 -060016
17# This deals with a device tree, presenting it as an assortment of Node and
18# Prop objects, representing nodes and properties, respectively. This file
Simon Glassa9440932017-05-27 07:38:30 -060019# contains the base classes and defines the high-level API. You can use
20# FdtScan() as a convenience function to create and scan an Fdt.
Simon Glass059ae522017-05-27 07:38:28 -060021
22# This implementation uses a libfdt Python library to access the device tree,
23# so it is fairly efficient.
Simon Glass1f941b62016-07-25 18:59:04 -060024
Simon Glassb1a5e262016-07-25 18:59:05 -060025# A list of types we support
Simon Glassc9a032c2020-11-08 20:36:17 -070026class Type(IntEnum):
Simon Glassfd4078e2021-07-28 19:23:09 -060027 # Types in order from widest to narrowest
Simon Glassc9a032c2020-11-08 20:36:17 -070028 (BYTE, INT, STRING, BOOL, INT64) = range(5)
29
Simon Glassfd4078e2021-07-28 19:23:09 -060030 def needs_widening(self, other):
31 """Check if this type needs widening to hold a value from another type
Simon Glassc9a032c2020-11-08 20:36:17 -070032
Simon Glassfd4078e2021-07-28 19:23:09 -060033 A wider type is one that can hold a wider array of information than
34 another one, or is less restrictive, so it can hold the information of
35 another type as well as its own. This is similar to the concept of
36 type-widening in C.
Simon Glassc9a032c2020-11-08 20:36:17 -070037
38 This uses a simple arithmetic comparison, since type values are in order
Simon Glassfd4078e2021-07-28 19:23:09 -060039 from widest (BYTE) to narrowest (INT64).
Simon Glassc9a032c2020-11-08 20:36:17 -070040
41 Args:
42 other: Other type to compare against
43
44 Return:
45 True if the other type is wider
46 """
47 return self.value > other.value
Simon Glassb1a5e262016-07-25 18:59:05 -060048
Simon Glass1f941b62016-07-25 18:59:04 -060049def CheckErr(errnum, msg):
50 if errnum:
51 raise ValueError('Error %d: %s: %s' %
52 (errnum, libfdt.fdt_strerror(errnum), msg))
53
Simon Glassb73d4482019-05-17 22:00:34 -060054
Simon Glassd8f593f2019-05-17 22:00:35 -060055def BytesToValue(data):
Simon Glassb73d4482019-05-17 22:00:34 -060056 """Converts a string of bytes into a type and value
57
58 Args:
Simon Glassd8f593f2019-05-17 22:00:35 -060059 A bytes value (which on Python 2 is an alias for str)
Simon Glassb73d4482019-05-17 22:00:34 -060060
61 Return:
62 A tuple:
63 Type of data
64 Data, either a single element or a list of elements. Each element
65 is one of:
Simon Glassc9a032c2020-11-08 20:36:17 -070066 Type.STRING: str/bytes value from the property
67 Type.INT: a byte-swapped integer stored as a 4-byte str/bytes
68 Type.BYTE: a byte stored as a single-byte str/bytes
Simon Glassb73d4482019-05-17 22:00:34 -060069 """
Simon Glassd8f593f2019-05-17 22:00:35 -060070 data = bytes(data)
71 size = len(data)
72 strings = data.split(b'\0')
Simon Glassb73d4482019-05-17 22:00:34 -060073 is_string = True
74 count = len(strings) - 1
Simon Glassd8f593f2019-05-17 22:00:35 -060075 if count > 0 and not len(strings[-1]):
Simon Glassb73d4482019-05-17 22:00:34 -060076 for string in strings[:-1]:
77 if not string:
78 is_string = False
79 break
80 for ch in string:
Simon Glassd8f593f2019-05-17 22:00:35 -060081 if ch < 32 or ch > 127:
Simon Glassb73d4482019-05-17 22:00:34 -060082 is_string = False
83 break
84 else:
85 is_string = False
86 if is_string:
Simon Glassd8f593f2019-05-17 22:00:35 -060087 if count == 1:
Simon Glassc9a032c2020-11-08 20:36:17 -070088 return Type.STRING, strings[0].decode()
Simon Glassb73d4482019-05-17 22:00:34 -060089 else:
Simon Glassc9a032c2020-11-08 20:36:17 -070090 return Type.STRING, [s.decode() for s in strings[:-1]]
Simon Glassb73d4482019-05-17 22:00:34 -060091 if size % 4:
92 if size == 1:
Simon Glass632b84c2020-11-08 20:36:20 -070093 return Type.BYTE, chr(data[0])
Simon Glassb73d4482019-05-17 22:00:34 -060094 else:
Simon Glass632b84c2020-11-08 20:36:20 -070095 return Type.BYTE, [chr(ch) for ch in list(data)]
Simon Glassb73d4482019-05-17 22:00:34 -060096 val = []
97 for i in range(0, size, 4):
Simon Glassd8f593f2019-05-17 22:00:35 -060098 val.append(data[i:i + 4])
Simon Glassb73d4482019-05-17 22:00:34 -060099 if size == 4:
Simon Glassc9a032c2020-11-08 20:36:17 -0700100 return Type.INT, val[0]
Simon Glassb73d4482019-05-17 22:00:34 -0600101 else:
Simon Glassc9a032c2020-11-08 20:36:17 -0700102 return Type.INT, val
Simon Glassb73d4482019-05-17 22:00:34 -0600103
104
Simon Glass059ae522017-05-27 07:38:28 -0600105class Prop:
Simon Glass1f941b62016-07-25 18:59:04 -0600106 """A device tree property
107
108 Properties:
Simon Glass459df472021-03-21 18:24:35 +1300109 node: Node containing this property
110 offset: Offset of the property (None if still to be synced)
Simon Glass1f941b62016-07-25 18:59:04 -0600111 name: Property name (as per the device tree)
112 value: Property value as a string of bytes, or a list of strings of
113 bytes
114 type: Value type
115 """
Simon Glass74b472e2019-05-17 22:00:37 -0600116 def __init__(self, node, offset, name, data):
Simon Glass1f941b62016-07-25 18:59:04 -0600117 self._node = node
118 self._offset = offset
119 self.name = name
120 self.value = None
Simon Glass74b472e2019-05-17 22:00:37 -0600121 self.bytes = bytes(data)
Simon Glass459df472021-03-21 18:24:35 +1300122 self.dirty = offset is None
Simon Glass74b472e2019-05-17 22:00:37 -0600123 if not data:
Simon Glassc9a032c2020-11-08 20:36:17 -0700124 self.type = Type.BOOL
Simon Glass059ae522017-05-27 07:38:28 -0600125 self.value = True
126 return
Simon Glass74b472e2019-05-17 22:00:37 -0600127 self.type, self.value = BytesToValue(bytes(data))
Simon Glass1f941b62016-07-25 18:59:04 -0600128
Simon Glass4df8a0c2018-07-06 10:27:29 -0600129 def RefreshOffset(self, poffset):
130 self._offset = poffset
131
Simon Glass248ccd22016-07-25 18:59:06 -0600132 def Widen(self, newprop):
133 """Figure out which property type is more general
134
135 Given a current property and a new property, this function returns the
136 one that is less specific as to type. The less specific property will
137 be ble to represent the data in the more specific property. This is
138 used for things like:
139
140 node1 {
141 compatible = "fred";
142 value = <1>;
143 };
144 node1 {
145 compatible = "fred";
146 value = <1 2>;
147 };
148
149 He we want to use an int array for 'value'. The first property
150 suggests that a single int is enough, but the second one shows that
151 it is not. Calling this function with these two propertes would
152 update the current property to be like the second, since it is less
153 specific.
154 """
Simon Glassfd4078e2021-07-28 19:23:09 -0600155 if self.type.needs_widening(newprop.type):
Simon Glassc9a032c2020-11-08 20:36:17 -0700156 if self.type == Type.INT and newprop.type == Type.BYTE:
Simon Glass8034e4d2020-10-03 11:31:27 -0600157 if type(self.value) == list:
158 new_value = []
159 for val in self.value:
Simon Glass632b84c2020-11-08 20:36:20 -0700160 new_value += [chr(by) for by in val]
Simon Glass8034e4d2020-10-03 11:31:27 -0600161 else:
Simon Glass632b84c2020-11-08 20:36:20 -0700162 new_value = [chr(by) for by in self.value]
Simon Glass8034e4d2020-10-03 11:31:27 -0600163 self.value = new_value
Simon Glass248ccd22016-07-25 18:59:06 -0600164 self.type = newprop.type
165
Simon Glassa7d66982021-07-28 19:23:10 -0600166 if type(newprop.value) == list:
167 if type(self.value) != list:
168 self.value = [self.value]
Simon Glass248ccd22016-07-25 18:59:06 -0600169
Simon Glassa7d66982021-07-28 19:23:10 -0600170 if len(newprop.value) > len(self.value):
171 val = self.GetEmpty(self.type)
172 while len(self.value) < len(newprop.value):
173 self.value.append(val)
Simon Glass248ccd22016-07-25 18:59:06 -0600174
Simon Glass0ed50752018-07-06 10:27:24 -0600175 @classmethod
Simon Glassb1a5e262016-07-25 18:59:05 -0600176 def GetEmpty(self, type):
177 """Get an empty / zero value of the given type
178
179 Returns:
180 A single value of the given type
181 """
Simon Glassc9a032c2020-11-08 20:36:17 -0700182 if type == Type.BYTE:
Simon Glassb1a5e262016-07-25 18:59:05 -0600183 return chr(0)
Simon Glassc9a032c2020-11-08 20:36:17 -0700184 elif type == Type.INT:
Simon Glassc330b142018-09-14 04:57:14 -0600185 return struct.pack('>I', 0);
Simon Glassc9a032c2020-11-08 20:36:17 -0700186 elif type == Type.STRING:
Simon Glassb1a5e262016-07-25 18:59:05 -0600187 return ''
188 else:
189 return True
190
Simon Glass54cec6d2016-07-25 18:59:16 -0600191 def GetOffset(self):
192 """Get the offset of a property
193
Simon Glass54cec6d2016-07-25 18:59:16 -0600194 Returns:
Simon Glass059ae522017-05-27 07:38:28 -0600195 The offset of the property (struct fdt_property) within the file
Simon Glass54cec6d2016-07-25 18:59:16 -0600196 """
Simon Glass4df8a0c2018-07-06 10:27:29 -0600197 self._node._fdt.CheckCache()
Simon Glass059ae522017-05-27 07:38:28 -0600198 return self._node._fdt.GetStructOffset(self._offset)
Simon Glass54cec6d2016-07-25 18:59:16 -0600199
Simon Glasseddd7292018-09-14 04:57:13 -0600200 def SetInt(self, val):
201 """Set the integer value of the property
202
203 The device tree is marked dirty so that the value will be written to
204 the block on the next sync.
205
206 Args:
207 val: Integer value (32-bit, single cell)
208 """
209 self.bytes = struct.pack('>I', val);
Simon Glassa2b9acb2018-10-01 12:22:49 -0600210 self.value = self.bytes
Simon Glassc9a032c2020-11-08 20:36:17 -0700211 self.type = Type.INT
Simon Glasseddd7292018-09-14 04:57:13 -0600212 self.dirty = True
213
Simon Glassccd25262018-09-14 04:57:16 -0600214 def SetData(self, bytes):
215 """Set the value of a property as bytes
216
217 Args:
218 bytes: New property value to set
219 """
Simon Glass1cd40082019-05-17 22:00:36 -0600220 self.bytes = bytes
Simon Glassb73d4482019-05-17 22:00:34 -0600221 self.type, self.value = BytesToValue(bytes)
Simon Glassccd25262018-09-14 04:57:16 -0600222 self.dirty = True
223
Simon Glasseddd7292018-09-14 04:57:13 -0600224 def Sync(self, auto_resize=False):
225 """Sync property changes back to the device tree
226
227 This updates the device tree blob with any changes to this property
228 since the last sync.
229
230 Args:
231 auto_resize: Resize the device tree automatically if it does not
232 have enough space for the update
233
234 Raises:
235 FdtException if auto_resize is False and there is not enough space
236 """
Simon Glass459df472021-03-21 18:24:35 +1300237 if self.dirty:
Simon Glasseddd7292018-09-14 04:57:13 -0600238 node = self._node
239 fdt_obj = node._fdt._fdt_obj
Simon Glassd8bee462021-03-21 18:24:39 +1300240 node_name = fdt_obj.get_name(node._offset)
241 if node_name and node_name != node.name:
242 raise ValueError("Internal error, node '%s' name mismatch '%s'" %
243 (node.path, node_name))
244
Simon Glasseddd7292018-09-14 04:57:13 -0600245 if auto_resize:
246 while fdt_obj.setprop(node.Offset(), self.name, self.bytes,
247 (libfdt.NOSPACE,)) == -libfdt.NOSPACE:
Simon Glassf67c99c2020-07-09 18:39:44 -0600248 fdt_obj.resize(fdt_obj.totalsize() + 1024 +
249 len(self.bytes))
Simon Glasseddd7292018-09-14 04:57:13 -0600250 fdt_obj.setprop(node.Offset(), self.name, self.bytes)
251 else:
252 fdt_obj.setprop(node.Offset(), self.name, self.bytes)
Simon Glass459df472021-03-21 18:24:35 +1300253 self.dirty = False
Simon Glasseddd7292018-09-14 04:57:13 -0600254
255
Simon Glass059ae522017-05-27 07:38:28 -0600256class Node:
Simon Glass1f941b62016-07-25 18:59:04 -0600257 """A device tree node
258
259 Properties:
Simon Glass459df472021-03-21 18:24:35 +1300260 parent: Parent Node
261 offset: Integer offset in the device tree (None if to be synced)
Simon Glass1f941b62016-07-25 18:59:04 -0600262 name: Device tree node tname
263 path: Full path to node, along with the node name itself
264 _fdt: Device tree object
265 subnodes: A list of subnodes for this node, each a Node object
266 props: A dict of properties for this node, each a Prop object.
267 Keyed by property name
268 """
Simon Glass80ef7052017-08-29 14:15:47 -0600269 def __init__(self, fdt, parent, offset, name, path):
Simon Glass1f941b62016-07-25 18:59:04 -0600270 self._fdt = fdt
Simon Glass80ef7052017-08-29 14:15:47 -0600271 self.parent = parent
Simon Glass1f941b62016-07-25 18:59:04 -0600272 self._offset = offset
273 self.name = name
274 self.path = path
275 self.subnodes = []
276 self.props = {}
277
Simon Glasse2d65282018-07-17 13:25:46 -0600278 def GetFdt(self):
279 """Get the Fdt object for this node
280
281 Returns:
282 Fdt object
283 """
284 return self._fdt
285
Simon Glassaa1a5d72018-07-17 13:25:41 -0600286 def FindNode(self, name):
Simon Glasscc346a72016-07-25 18:59:07 -0600287 """Find a node given its name
288
289 Args:
290 name: Node name to look for
291 Returns:
292 Node object if found, else None
293 """
294 for subnode in self.subnodes:
295 if subnode.name == name:
296 return subnode
297 return None
298
Simon Glass059ae522017-05-27 07:38:28 -0600299 def Offset(self):
300 """Returns the offset of a node, after checking the cache
301
302 This should be used instead of self._offset directly, to ensure that
303 the cache does not contain invalid offsets.
304 """
305 self._fdt.CheckCache()
306 return self._offset
307
Simon Glasscc346a72016-07-25 18:59:07 -0600308 def Scan(self):
Simon Glass059ae522017-05-27 07:38:28 -0600309 """Scan a node's properties and subnodes
Simon Glasscc346a72016-07-25 18:59:07 -0600310
Simon Glass059ae522017-05-27 07:38:28 -0600311 This fills in the props and subnodes properties, recursively
312 searching into subnodes so that the entire tree is built.
Simon Glasscc346a72016-07-25 18:59:07 -0600313 """
Simon Glass151aeed2018-07-06 10:27:26 -0600314 fdt_obj = self._fdt._fdt_obj
Simon Glass059ae522017-05-27 07:38:28 -0600315 self.props = self._fdt.GetProps(self)
Simon Glass151aeed2018-07-06 10:27:26 -0600316 phandle = fdt_obj.get_phandle(self.Offset())
Simon Glassa3f94442017-08-29 14:15:52 -0600317 if phandle:
Simon Glass151aeed2018-07-06 10:27:26 -0600318 self._fdt.phandle_to_node[phandle] = self
Simon Glass059ae522017-05-27 07:38:28 -0600319
Simon Glass151aeed2018-07-06 10:27:26 -0600320 offset = fdt_obj.first_subnode(self.Offset(), QUIET_NOTFOUND)
Simon Glass059ae522017-05-27 07:38:28 -0600321 while offset >= 0:
322 sep = '' if self.path[-1] == '/' else '/'
Simon Glass151aeed2018-07-06 10:27:26 -0600323 name = fdt_obj.get_name(offset)
Simon Glass059ae522017-05-27 07:38:28 -0600324 path = self.path + sep + name
Simon Glass80ef7052017-08-29 14:15:47 -0600325 node = Node(self._fdt, self, offset, name, path)
Simon Glass059ae522017-05-27 07:38:28 -0600326 self.subnodes.append(node)
327
328 node.Scan()
Simon Glass151aeed2018-07-06 10:27:26 -0600329 offset = fdt_obj.next_subnode(offset, QUIET_NOTFOUND)
Simon Glass059ae522017-05-27 07:38:28 -0600330
331 def Refresh(self, my_offset):
332 """Fix up the _offset for each node, recursively
333
334 Note: This does not take account of property offsets - these will not
335 be updated.
336 """
Simon Glass792d2392018-07-06 10:27:27 -0600337 fdt_obj = self._fdt._fdt_obj
Simon Glass059ae522017-05-27 07:38:28 -0600338 if self._offset != my_offset:
Simon Glass059ae522017-05-27 07:38:28 -0600339 self._offset = my_offset
Simon Glassd8bee462021-03-21 18:24:39 +1300340 name = fdt_obj.get_name(self._offset)
341 if name and self.name != name:
342 raise ValueError("Internal error, node '%s' name mismatch '%s'" %
343 (self.path, name))
344
Simon Glass792d2392018-07-06 10:27:27 -0600345 offset = fdt_obj.first_subnode(self._offset, QUIET_NOTFOUND)
Simon Glass059ae522017-05-27 07:38:28 -0600346 for subnode in self.subnodes:
Simon Glass4df8a0c2018-07-06 10:27:29 -0600347 if subnode.name != fdt_obj.get_name(offset):
348 raise ValueError('Internal error, node name mismatch %s != %s' %
349 (subnode.name, fdt_obj.get_name(offset)))
Simon Glass059ae522017-05-27 07:38:28 -0600350 subnode.Refresh(offset)
Simon Glass792d2392018-07-06 10:27:27 -0600351 offset = fdt_obj.next_subnode(offset, QUIET_NOTFOUND)
Simon Glass4df8a0c2018-07-06 10:27:29 -0600352 if offset != -libfdt.FDT_ERR_NOTFOUND:
353 raise ValueError('Internal error, offset == %d' % offset)
354
355 poffset = fdt_obj.first_property_offset(self._offset, QUIET_NOTFOUND)
356 while poffset >= 0:
357 p = fdt_obj.get_property_by_offset(poffset)
358 prop = self.props.get(p.name)
359 if not prop:
Simon Glass93f18a12021-03-21 18:24:34 +1300360 raise ValueError("Internal error, node '%s' property '%s' missing, "
361 'offset %d' % (self.path, p.name, poffset))
Simon Glass4df8a0c2018-07-06 10:27:29 -0600362 prop.RefreshOffset(poffset)
363 poffset = fdt_obj.next_property_offset(poffset, QUIET_NOTFOUND)
Simon Glasscc346a72016-07-25 18:59:07 -0600364
Simon Glassc719e422016-07-25 18:59:14 -0600365 def DeleteProp(self, prop_name):
366 """Delete a property of a node
367
Simon Glass059ae522017-05-27 07:38:28 -0600368 The property is deleted and the offset cache is invalidated.
Simon Glassc719e422016-07-25 18:59:14 -0600369
370 Args:
371 prop_name: Name of the property to delete
Simon Glass059ae522017-05-27 07:38:28 -0600372 Raises:
373 ValueError if the property does not exist
Simon Glassc719e422016-07-25 18:59:14 -0600374 """
Simon Glass792d2392018-07-06 10:27:27 -0600375 CheckErr(self._fdt._fdt_obj.delprop(self.Offset(), prop_name),
Simon Glass059ae522017-05-27 07:38:28 -0600376 "Node '%s': delete property: '%s'" % (self.path, prop_name))
377 del self.props[prop_name]
378 self._fdt.Invalidate()
Simon Glassc719e422016-07-25 18:59:14 -0600379
Simon Glasse80c5562018-07-06 10:27:38 -0600380 def AddZeroProp(self, prop_name):
381 """Add a new property to the device tree with an integer value of 0.
382
383 Args:
384 prop_name: Name of property
385 """
Simon Glass6bbfafb2019-05-17 22:00:33 -0600386 self.props[prop_name] = Prop(self, None, prop_name,
387 tools.GetBytes(0, 4))
Simon Glasse80c5562018-07-06 10:27:38 -0600388
Simon Glassccd25262018-09-14 04:57:16 -0600389 def AddEmptyProp(self, prop_name, len):
390 """Add a property with a fixed data size, for filling in later
391
392 The device tree is marked dirty so that the value will be written to
393 the blob on the next sync.
394
395 Args:
396 prop_name: Name of property
397 len: Length of data in property
398 """
Simon Glass6bbfafb2019-05-17 22:00:33 -0600399 value = tools.GetBytes(0, len)
Simon Glassccd25262018-09-14 04:57:16 -0600400 self.props[prop_name] = Prop(self, None, prop_name, value)
401
Simon Glassa683a5f2019-07-20 12:23:37 -0600402 def _CheckProp(self, prop_name):
403 """Check if a property is present
404
405 Args:
406 prop_name: Name of property
407
408 Returns:
409 self
410
411 Raises:
412 ValueError if the property is missing
413 """
414 if prop_name not in self.props:
415 raise ValueError("Fdt '%s', node '%s': Missing property '%s'" %
416 (self._fdt._fname, self.path, prop_name))
417 return self
418
Simon Glasse80c5562018-07-06 10:27:38 -0600419 def SetInt(self, prop_name, val):
420 """Update an integer property int the device tree.
421
422 This is not allowed to change the size of the FDT.
423
Simon Glassccd25262018-09-14 04:57:16 -0600424 The device tree is marked dirty so that the value will be written to
425 the blob on the next sync.
426
Simon Glasse80c5562018-07-06 10:27:38 -0600427 Args:
428 prop_name: Name of property
429 val: Value to set
430 """
Simon Glassa683a5f2019-07-20 12:23:37 -0600431 self._CheckProp(prop_name).props[prop_name].SetInt(val)
Simon Glasseddd7292018-09-14 04:57:13 -0600432
Simon Glassccd25262018-09-14 04:57:16 -0600433 def SetData(self, prop_name, val):
434 """Set the data value of a property
435
436 The device tree is marked dirty so that the value will be written to
437 the blob on the next sync.
438
439 Args:
440 prop_name: Name of property to set
441 val: Data value to set
442 """
Simon Glassa683a5f2019-07-20 12:23:37 -0600443 self._CheckProp(prop_name).props[prop_name].SetData(val)
Simon Glassccd25262018-09-14 04:57:16 -0600444
445 def SetString(self, prop_name, val):
446 """Set the string value of a property
447
448 The device tree is marked dirty so that the value will be written to
449 the blob on the next sync.
450
451 Args:
452 prop_name: Name of property to set
453 val: String value to set (will be \0-terminated in DT)
454 """
Simon Glass39e22332019-10-31 07:43:04 -0600455 if type(val) == str:
456 val = val.encode('utf-8')
Simon Glassa683a5f2019-07-20 12:23:37 -0600457 self._CheckProp(prop_name).props[prop_name].SetData(val + b'\0')
Simon Glassccd25262018-09-14 04:57:16 -0600458
Simon Glassf67c99c2020-07-09 18:39:44 -0600459 def AddData(self, prop_name, val):
460 """Add a new property to a node
461
462 The device tree is marked dirty so that the value will be written to
463 the blob on the next sync.
464
465 Args:
466 prop_name: Name of property to add
467 val: Bytes value of property
Simon Glassd8bee462021-03-21 18:24:39 +1300468
469 Returns:
470 Prop added
Simon Glassf67c99c2020-07-09 18:39:44 -0600471 """
Simon Glassd8bee462021-03-21 18:24:39 +1300472 prop = Prop(self, None, prop_name, val)
473 self.props[prop_name] = prop
474 return prop
Simon Glassf67c99c2020-07-09 18:39:44 -0600475
Simon Glassccd25262018-09-14 04:57:16 -0600476 def AddString(self, prop_name, val):
477 """Add a new string property to a node
478
479 The device tree is marked dirty so that the value will be written to
480 the blob on the next sync.
481
482 Args:
483 prop_name: Name of property to add
484 val: String value of property
Simon Glassd8bee462021-03-21 18:24:39 +1300485
486 Returns:
487 Prop added
Simon Glassccd25262018-09-14 04:57:16 -0600488 """
Simon Glassd35a8522021-01-06 21:35:10 -0700489 val = bytes(val, 'utf-8')
Simon Glassd8bee462021-03-21 18:24:39 +1300490 return self.AddData(prop_name, val + b'\0')
Simon Glassccd25262018-09-14 04:57:16 -0600491
Simon Glassa2af7302021-01-06 21:35:18 -0700492 def AddInt(self, prop_name, val):
493 """Add a new integer 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: Integer value of property
Simon Glassd8bee462021-03-21 18:24:39 +1300501
502 Returns:
503 Prop added
Simon Glassa2af7302021-01-06 21:35:18 -0700504 """
Simon Glassd8bee462021-03-21 18:24:39 +1300505 return self.AddData(prop_name, struct.pack('>I', val))
Simon Glassa2af7302021-01-06 21:35:18 -0700506
Simon Glassf3a17962018-09-14 04:57:15 -0600507 def AddSubnode(self, name):
Simon Glassccd25262018-09-14 04:57:16 -0600508 """Add a new subnode to the node
509
510 Args:
511 name: name of node to add
512
513 Returns:
514 New subnode that was created
515 """
Simon Glassf3a17962018-09-14 04:57:15 -0600516 path = self.path + '/' + name
517 subnode = Node(self._fdt, self, None, name, path)
518 self.subnodes.append(subnode)
519 return subnode
520
Simon Glasseddd7292018-09-14 04:57:13 -0600521 def Sync(self, auto_resize=False):
522 """Sync node changes back to the device tree
523
524 This updates the device tree blob with any changes to this node and its
525 subnodes since the last sync.
526
527 Args:
528 auto_resize: Resize the device tree automatically if it does not
529 have enough space for the update
530
Simon Glass3be798a2021-03-21 18:24:38 +1300531 Returns:
532 True if the node had to be added, False if it already existed
533
Simon Glasseddd7292018-09-14 04:57:13 -0600534 Raises:
535 FdtException if auto_resize is False and there is not enough space
536 """
Simon Glass3be798a2021-03-21 18:24:38 +1300537 added = False
Simon Glassf3a17962018-09-14 04:57:15 -0600538 if self._offset is None:
539 # The subnode doesn't exist yet, so add it
540 fdt_obj = self._fdt._fdt_obj
541 if auto_resize:
542 while True:
543 offset = fdt_obj.add_subnode(self.parent._offset, self.name,
544 (libfdt.NOSPACE,))
545 if offset != -libfdt.NOSPACE:
546 break
547 fdt_obj.resize(fdt_obj.totalsize() + 1024)
548 else:
549 offset = fdt_obj.add_subnode(self.parent._offset, self.name)
550 self._offset = offset
Simon Glass3be798a2021-03-21 18:24:38 +1300551 added = True
552
553 # Sync the existing subnodes first, so that we can rely on the offsets
554 # being correct. As soon as we add new subnodes, it pushes all the
555 # existing subnodes up.
556 for node in reversed(self.subnodes):
557 if node._offset is not None:
558 node.Sync(auto_resize)
Simon Glassf3a17962018-09-14 04:57:15 -0600559
Simon Glass3be798a2021-03-21 18:24:38 +1300560 # Sync subnodes in reverse so that we get the expected order. Each
561 # new node goes at the start of the subnode list. This avoids an O(n^2)
562 # rescan of node offsets.
563 num_added = 0
Simon Glasseddd7292018-09-14 04:57:13 -0600564 for node in reversed(self.subnodes):
Simon Glass3be798a2021-03-21 18:24:38 +1300565 if node.Sync(auto_resize):
566 num_added += 1
567 if num_added:
568 # Reorder our list of nodes to put the new ones first, since that's
569 # what libfdt does
570 old_count = len(self.subnodes) - num_added
571 subnodes = self.subnodes[old_count:] + self.subnodes[:old_count]
572 self.subnodes = subnodes
Simon Glasse80c5562018-07-06 10:27:38 -0600573
Simon Glass3be798a2021-03-21 18:24:38 +1300574 # Sync properties now, whose offsets should not have been disturbed,
575 # since properties come before subnodes. This is done after all the
576 # subnode processing above, since updating properties can disturb the
577 # offsets of those subnodes.
578 # Properties are synced in reverse order, with new properties added
579 # before existing properties are synced. This ensures that the offsets
580 # of earlier properties are not disturbed.
581 # Note that new properties will have an offset of None here, which
582 # Python cannot sort against int. So use a large value instead so that
583 # new properties are added first.
Simon Glassa57cfee2019-05-17 22:00:38 -0600584 prop_list = sorted(self.props.values(),
585 key=lambda prop: prop._offset or 1 << 31,
Simon Glasseddd7292018-09-14 04:57:13 -0600586 reverse=True)
587 for prop in prop_list:
588 prop.Sync(auto_resize)
Simon Glass3be798a2021-03-21 18:24:38 +1300589 return added
Simon Glasseddd7292018-09-14 04:57:13 -0600590
Simon Glasse80c5562018-07-06 10:27:38 -0600591
Simon Glass1f941b62016-07-25 18:59:04 -0600592class Fdt:
Simon Glass059ae522017-05-27 07:38:28 -0600593 """Provides simple access to a flat device tree blob using libfdts.
Simon Glass1f941b62016-07-25 18:59:04 -0600594
595 Properties:
596 fname: Filename of fdt
597 _root: Root of device tree (a Node object)
Simon Glass50cfc6e2019-07-20 12:23:38 -0600598 name: Helpful name for this Fdt for the user (useful when creating the
599 DT from data rather than a file)
Simon Glass1f941b62016-07-25 18:59:04 -0600600 """
601 def __init__(self, fname):
602 self._fname = fname
Simon Glass059ae522017-05-27 07:38:28 -0600603 self._cached_offsets = False
Simon Glassa3f94442017-08-29 14:15:52 -0600604 self.phandle_to_node = {}
Simon Glass50cfc6e2019-07-20 12:23:38 -0600605 self.name = ''
Simon Glass059ae522017-05-27 07:38:28 -0600606 if self._fname:
Simon Glass50cfc6e2019-07-20 12:23:38 -0600607 self.name = self._fname
Simon Glass059ae522017-05-27 07:38:28 -0600608 self._fname = fdt_util.EnsureCompiled(self._fname)
609
Simon Glass116236f2019-05-14 15:53:43 -0600610 with open(self._fname, 'rb') as fd:
Simon Glass792d2392018-07-06 10:27:27 -0600611 self._fdt_obj = libfdt.Fdt(fd.read())
Simon Glasscc346a72016-07-25 18:59:07 -0600612
Simon Glassb8a49292018-09-14 04:57:17 -0600613 @staticmethod
Simon Glass50cfc6e2019-07-20 12:23:38 -0600614 def FromData(data, name=''):
Simon Glassb8a49292018-09-14 04:57:17 -0600615 """Create a new Fdt object from the given data
616
617 Args:
618 data: Device-tree data blob
Simon Glass50cfc6e2019-07-20 12:23:38 -0600619 name: Helpful name for this Fdt for the user
Simon Glassb8a49292018-09-14 04:57:17 -0600620
621 Returns:
622 Fdt object containing the data
623 """
624 fdt = Fdt(None)
Simon Glass1cd40082019-05-17 22:00:36 -0600625 fdt._fdt_obj = libfdt.Fdt(bytes(data))
Simon Glass50cfc6e2019-07-20 12:23:38 -0600626 fdt.name = name
Simon Glassb8a49292018-09-14 04:57:17 -0600627 return fdt
628
Simon Glasse2d65282018-07-17 13:25:46 -0600629 def LookupPhandle(self, phandle):
630 """Look up a phandle
631
632 Args:
633 phandle: Phandle to look up (int)
634
635 Returns:
636 Node object the phandle points to
637 """
638 return self.phandle_to_node.get(phandle)
639
Simon Glasscc346a72016-07-25 18:59:07 -0600640 def Scan(self, root='/'):
641 """Scan a device tree, building up a tree of Node objects
642
643 This fills in the self._root property
644
645 Args:
646 root: Ignored
647
648 TODO(sjg@chromium.org): Implement the 'root' parameter
649 """
Simon Glass4df8a0c2018-07-06 10:27:29 -0600650 self._cached_offsets = True
Simon Glass80ef7052017-08-29 14:15:47 -0600651 self._root = self.Node(self, None, 0, '/', '/')
Simon Glasscc346a72016-07-25 18:59:07 -0600652 self._root.Scan()
653
654 def GetRoot(self):
655 """Get the root Node of the device tree
656
657 Returns:
658 The root Node object
659 """
660 return self._root
661
662 def GetNode(self, path):
663 """Look up a node from its path
664
665 Args:
666 path: Path to look up, e.g. '/microcode/update@0'
667 Returns:
668 Node object, or None if not found
669 """
670 node = self._root
Simon Glassc5eddc82018-07-06 10:27:30 -0600671 parts = path.split('/')
672 if len(parts) < 2:
673 return None
Simon Glass3b9a8292019-07-20 12:23:39 -0600674 if len(parts) == 2 and parts[1] == '':
675 return node
Simon Glassc5eddc82018-07-06 10:27:30 -0600676 for part in parts[1:]:
Simon Glassaa1a5d72018-07-17 13:25:41 -0600677 node = node.FindNode(part)
Simon Glasscc346a72016-07-25 18:59:07 -0600678 if not node:
679 return None
680 return node
681
Simon Glass32d98272016-07-25 18:59:15 -0600682 def Flush(self):
683 """Flush device tree changes back to the file
684
685 If the device tree has changed in memory, write it back to the file.
Simon Glass32d98272016-07-25 18:59:15 -0600686 """
Simon Glass059ae522017-05-27 07:38:28 -0600687 with open(self._fname, 'wb') as fd:
Simon Glass792d2392018-07-06 10:27:27 -0600688 fd.write(self._fdt_obj.as_bytearray())
Simon Glass32d98272016-07-25 18:59:15 -0600689
Simon Glasseddd7292018-09-14 04:57:13 -0600690 def Sync(self, auto_resize=False):
691 """Make sure any DT changes are written to the blob
692
693 Args:
694 auto_resize: Resize the device tree automatically if it does not
695 have enough space for the update
696
697 Raises:
698 FdtException if auto_resize is False and there is not enough space
699 """
Simon Glassb2e8ac42021-03-21 18:24:36 +1300700 self.CheckCache()
Simon Glasseddd7292018-09-14 04:57:13 -0600701 self._root.Sync(auto_resize)
Simon Glassb2e8ac42021-03-21 18:24:36 +1300702 self.Refresh()
Simon Glasseddd7292018-09-14 04:57:13 -0600703
Simon Glass32d98272016-07-25 18:59:15 -0600704 def Pack(self):
705 """Pack the device tree down to its minimum size
706
707 When nodes and properties shrink or are deleted, wasted space can
Simon Glass059ae522017-05-27 07:38:28 -0600708 build up in the device tree binary.
709 """
Simon Glass151aeed2018-07-06 10:27:26 -0600710 CheckErr(self._fdt_obj.pack(), 'pack')
Simon Glassb2e8ac42021-03-21 18:24:36 +1300711 self.Refresh()
Simon Glass059ae522017-05-27 07:38:28 -0600712
Simon Glass792d2392018-07-06 10:27:27 -0600713 def GetContents(self):
Simon Glass059ae522017-05-27 07:38:28 -0600714 """Get the contents of the FDT
715
716 Returns:
717 The FDT contents as a string of bytes
Simon Glass32d98272016-07-25 18:59:15 -0600718 """
Simon Glass1cd40082019-05-17 22:00:36 -0600719 return bytes(self._fdt_obj.as_bytearray())
Simon Glass059ae522017-05-27 07:38:28 -0600720
Simon Glass0ed50752018-07-06 10:27:24 -0600721 def GetFdtObj(self):
722 """Get the contents of the FDT
723
724 Returns:
725 The FDT contents as a libfdt.Fdt object
726 """
727 return self._fdt_obj
728
Simon Glass059ae522017-05-27 07:38:28 -0600729 def GetProps(self, node):
730 """Get all properties from a node.
731
732 Args:
733 node: Full path to node name to look in.
734
735 Returns:
736 A dictionary containing all the properties, indexed by node name.
737 The entries are Prop objects.
738
739 Raises:
740 ValueError: if the node does not exist.
741 """
742 props_dict = {}
Simon Glass151aeed2018-07-06 10:27:26 -0600743 poffset = self._fdt_obj.first_property_offset(node._offset,
744 QUIET_NOTFOUND)
Simon Glass059ae522017-05-27 07:38:28 -0600745 while poffset >= 0:
746 p = self._fdt_obj.get_property_by_offset(poffset)
Simon Glass70cd0d72018-07-06 10:27:20 -0600747 prop = Prop(node, poffset, p.name, p)
Simon Glass059ae522017-05-27 07:38:28 -0600748 props_dict[prop.name] = prop
749
Simon Glass151aeed2018-07-06 10:27:26 -0600750 poffset = self._fdt_obj.next_property_offset(poffset,
751 QUIET_NOTFOUND)
Simon Glass059ae522017-05-27 07:38:28 -0600752 return props_dict
753
754 def Invalidate(self):
755 """Mark our offset cache as invalid"""
756 self._cached_offsets = False
757
758 def CheckCache(self):
759 """Refresh the offset cache if needed"""
760 if self._cached_offsets:
761 return
762 self.Refresh()
Simon Glass059ae522017-05-27 07:38:28 -0600763
764 def Refresh(self):
765 """Refresh the offset cache"""
766 self._root.Refresh(0)
Simon Glassb2e8ac42021-03-21 18:24:36 +1300767 self._cached_offsets = True
Simon Glass059ae522017-05-27 07:38:28 -0600768
769 def GetStructOffset(self, offset):
770 """Get the file offset of a given struct offset
771
772 Args:
773 offset: Offset within the 'struct' region of the device tree
774 Returns:
775 Position of @offset within the device tree binary
776 """
Simon Glass151aeed2018-07-06 10:27:26 -0600777 return self._fdt_obj.off_dt_struct() + offset
Simon Glass059ae522017-05-27 07:38:28 -0600778
779 @classmethod
Simon Glass80ef7052017-08-29 14:15:47 -0600780 def Node(self, fdt, parent, offset, name, path):
Simon Glass059ae522017-05-27 07:38:28 -0600781 """Create a new node
782
783 This is used by Fdt.Scan() to create a new node using the correct
784 class.
785
786 Args:
787 fdt: Fdt object
Simon Glass80ef7052017-08-29 14:15:47 -0600788 parent: Parent node, or None if this is the root node
Simon Glass059ae522017-05-27 07:38:28 -0600789 offset: Offset of node
790 name: Node name
791 path: Full path to node
792 """
Simon Glass80ef7052017-08-29 14:15:47 -0600793 node = Node(fdt, parent, offset, name, path)
Simon Glass059ae522017-05-27 07:38:28 -0600794 return node
Simon Glassa9440932017-05-27 07:38:30 -0600795
Simon Glass74f5feb2019-07-20 12:24:08 -0600796 def GetFilename(self):
797 """Get the filename of the device tree
798
799 Returns:
800 String filename
801 """
802 return self._fname
803
Simon Glassa9440932017-05-27 07:38:30 -0600804def FdtScan(fname):
Simon Glassc38fee042018-07-06 10:27:32 -0600805 """Returns a new Fdt object"""
Simon Glassa9440932017-05-27 07:38:30 -0600806 dtb = Fdt(fname)
807 dtb.Scan()
808 return dtb