blob: 6770be79fbe3a28fc6d6b8462769c06929e14de0 [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
8import struct
9import sys
10
11import fdt_util
Simon Glass059ae522017-05-27 07:38:28 -060012import libfdt
Simon Glass151aeed2018-07-06 10:27:26 -060013from libfdt import QUIET_NOTFOUND
Simon Glass6bbfafb2019-05-17 22:00:33 -060014import tools
Simon Glass1f941b62016-07-25 18:59:04 -060015
16# This deals with a device tree, presenting it as an assortment of Node and
17# Prop objects, representing nodes and properties, respectively. This file
Simon Glassa9440932017-05-27 07:38:30 -060018# contains the base classes and defines the high-level API. You can use
19# FdtScan() as a convenience function to create and scan an Fdt.
Simon Glass059ae522017-05-27 07:38:28 -060020
21# This implementation uses a libfdt Python library to access the device tree,
22# so it is fairly efficient.
Simon Glass1f941b62016-07-25 18:59:04 -060023
Simon Glassb1a5e262016-07-25 18:59:05 -060024# A list of types we support
Simon Glassfc3ae9c2017-08-29 14:15:48 -060025(TYPE_BYTE, TYPE_INT, TYPE_STRING, TYPE_BOOL, TYPE_INT64) = range(5)
Simon Glassb1a5e262016-07-25 18:59:05 -060026
Simon Glass1f941b62016-07-25 18:59:04 -060027def CheckErr(errnum, msg):
28 if errnum:
29 raise ValueError('Error %d: %s: %s' %
30 (errnum, libfdt.fdt_strerror(errnum), msg))
31
Simon Glassb73d4482019-05-17 22:00:34 -060032
Simon Glassd8f593f2019-05-17 22:00:35 -060033def BytesToValue(data):
Simon Glassb73d4482019-05-17 22:00:34 -060034 """Converts a string of bytes into a type and value
35
36 Args:
Simon Glassd8f593f2019-05-17 22:00:35 -060037 A bytes value (which on Python 2 is an alias for str)
Simon Glassb73d4482019-05-17 22:00:34 -060038
39 Return:
40 A tuple:
41 Type of data
42 Data, either a single element or a list of elements. Each element
43 is one of:
Simon Glassd8f593f2019-05-17 22:00:35 -060044 TYPE_STRING: str/bytes value from the property
45 TYPE_INT: a byte-swapped integer stored as a 4-byte str/bytes
46 TYPE_BYTE: a byte stored as a single-byte str/bytes
Simon Glassb73d4482019-05-17 22:00:34 -060047 """
Simon Glassd8f593f2019-05-17 22:00:35 -060048 data = bytes(data)
49 size = len(data)
50 strings = data.split(b'\0')
Simon Glassb73d4482019-05-17 22:00:34 -060051 is_string = True
52 count = len(strings) - 1
Simon Glassd8f593f2019-05-17 22:00:35 -060053 if count > 0 and not len(strings[-1]):
Simon Glassb73d4482019-05-17 22:00:34 -060054 for string in strings[:-1]:
55 if not string:
56 is_string = False
57 break
58 for ch in string:
Simon Glassd8f593f2019-05-17 22:00:35 -060059 # Handle Python 2 treating bytes as str
60 if type(ch) == str:
61 ch = ord(ch)
62 if ch < 32 or ch > 127:
Simon Glassb73d4482019-05-17 22:00:34 -060063 is_string = False
64 break
65 else:
66 is_string = False
67 if is_string:
Simon Glassd8f593f2019-05-17 22:00:35 -060068 if count == 1:
69 if sys.version_info[0] >= 3: # pragma: no cover
70 return TYPE_STRING, strings[0].decode()
71 else:
72 return TYPE_STRING, strings[0]
Simon Glassb73d4482019-05-17 22:00:34 -060073 else:
Simon Glassd8f593f2019-05-17 22:00:35 -060074 if sys.version_info[0] >= 3: # pragma: no cover
75 return TYPE_STRING, [s.decode() for s in strings[:-1]]
76 else:
77 return TYPE_STRING, strings[:-1]
Simon Glassb73d4482019-05-17 22:00:34 -060078 if size % 4:
79 if size == 1:
Simon Glassd8f593f2019-05-17 22:00:35 -060080 return TYPE_BYTE, tools.ToChar(data[0])
Simon Glassb73d4482019-05-17 22:00:34 -060081 else:
Simon Glassd8f593f2019-05-17 22:00:35 -060082 return TYPE_BYTE, [tools.ToChar(ch) for ch in list(data)]
Simon Glassb73d4482019-05-17 22:00:34 -060083 val = []
84 for i in range(0, size, 4):
Simon Glassd8f593f2019-05-17 22:00:35 -060085 val.append(data[i:i + 4])
Simon Glassb73d4482019-05-17 22:00:34 -060086 if size == 4:
87 return TYPE_INT, val[0]
88 else:
89 return TYPE_INT, val
90
91
Simon Glass059ae522017-05-27 07:38:28 -060092class Prop:
Simon Glass1f941b62016-07-25 18:59:04 -060093 """A device tree property
94
95 Properties:
96 name: Property name (as per the device tree)
97 value: Property value as a string of bytes, or a list of strings of
98 bytes
99 type: Value type
100 """
Simon Glass74b472e2019-05-17 22:00:37 -0600101 def __init__(self, node, offset, name, data):
Simon Glass1f941b62016-07-25 18:59:04 -0600102 self._node = node
103 self._offset = offset
104 self.name = name
105 self.value = None
Simon Glass74b472e2019-05-17 22:00:37 -0600106 self.bytes = bytes(data)
Simon Glasseddd7292018-09-14 04:57:13 -0600107 self.dirty = False
Simon Glass74b472e2019-05-17 22:00:37 -0600108 if not data:
Simon Glass059ae522017-05-27 07:38:28 -0600109 self.type = TYPE_BOOL
110 self.value = True
111 return
Simon Glass74b472e2019-05-17 22:00:37 -0600112 self.type, self.value = BytesToValue(bytes(data))
Simon Glass1f941b62016-07-25 18:59:04 -0600113
Simon Glass4df8a0c2018-07-06 10:27:29 -0600114 def RefreshOffset(self, poffset):
115 self._offset = poffset
116
Simon Glass248ccd22016-07-25 18:59:06 -0600117 def Widen(self, newprop):
118 """Figure out which property type is more general
119
120 Given a current property and a new property, this function returns the
121 one that is less specific as to type. The less specific property will
122 be ble to represent the data in the more specific property. This is
123 used for things like:
124
125 node1 {
126 compatible = "fred";
127 value = <1>;
128 };
129 node1 {
130 compatible = "fred";
131 value = <1 2>;
132 };
133
134 He we want to use an int array for 'value'. The first property
135 suggests that a single int is enough, but the second one shows that
136 it is not. Calling this function with these two propertes would
137 update the current property to be like the second, since it is less
138 specific.
139 """
140 if newprop.type < self.type:
141 self.type = newprop.type
142
143 if type(newprop.value) == list and type(self.value) != list:
144 self.value = [self.value]
145
146 if type(self.value) == list and len(newprop.value) > len(self.value):
147 val = self.GetEmpty(self.type)
148 while len(self.value) < len(newprop.value):
149 self.value.append(val)
150
Simon Glass0ed50752018-07-06 10:27:24 -0600151 @classmethod
Simon Glassb1a5e262016-07-25 18:59:05 -0600152 def GetEmpty(self, type):
153 """Get an empty / zero value of the given type
154
155 Returns:
156 A single value of the given type
157 """
158 if type == TYPE_BYTE:
159 return chr(0)
160 elif type == TYPE_INT:
Simon Glassc330b142018-09-14 04:57:14 -0600161 return struct.pack('>I', 0);
Simon Glassb1a5e262016-07-25 18:59:05 -0600162 elif type == TYPE_STRING:
163 return ''
164 else:
165 return True
166
Simon Glass54cec6d2016-07-25 18:59:16 -0600167 def GetOffset(self):
168 """Get the offset of a property
169
Simon Glass54cec6d2016-07-25 18:59:16 -0600170 Returns:
Simon Glass059ae522017-05-27 07:38:28 -0600171 The offset of the property (struct fdt_property) within the file
Simon Glass54cec6d2016-07-25 18:59:16 -0600172 """
Simon Glass4df8a0c2018-07-06 10:27:29 -0600173 self._node._fdt.CheckCache()
Simon Glass059ae522017-05-27 07:38:28 -0600174 return self._node._fdt.GetStructOffset(self._offset)
Simon Glass54cec6d2016-07-25 18:59:16 -0600175
Simon Glasseddd7292018-09-14 04:57:13 -0600176 def SetInt(self, val):
177 """Set the integer value of the property
178
179 The device tree is marked dirty so that the value will be written to
180 the block on the next sync.
181
182 Args:
183 val: Integer value (32-bit, single cell)
184 """
185 self.bytes = struct.pack('>I', val);
Simon Glassa2b9acb2018-10-01 12:22:49 -0600186 self.value = self.bytes
Simon Glasseddd7292018-09-14 04:57:13 -0600187 self.type = TYPE_INT
188 self.dirty = True
189
Simon Glassccd25262018-09-14 04:57:16 -0600190 def SetData(self, bytes):
191 """Set the value of a property as bytes
192
193 Args:
194 bytes: New property value to set
195 """
Simon Glass1cd40082019-05-17 22:00:36 -0600196 self.bytes = bytes
Simon Glassb73d4482019-05-17 22:00:34 -0600197 self.type, self.value = BytesToValue(bytes)
Simon Glassccd25262018-09-14 04:57:16 -0600198 self.dirty = True
199
Simon Glasseddd7292018-09-14 04:57:13 -0600200 def Sync(self, auto_resize=False):
201 """Sync property changes back to the device tree
202
203 This updates the device tree blob with any changes to this property
204 since the last sync.
205
206 Args:
207 auto_resize: Resize the device tree automatically if it does not
208 have enough space for the update
209
210 Raises:
211 FdtException if auto_resize is False and there is not enough space
212 """
213 if self._offset is None or self.dirty:
214 node = self._node
215 fdt_obj = node._fdt._fdt_obj
216 if auto_resize:
217 while fdt_obj.setprop(node.Offset(), self.name, self.bytes,
218 (libfdt.NOSPACE,)) == -libfdt.NOSPACE:
219 fdt_obj.resize(fdt_obj.totalsize() + 1024)
220 fdt_obj.setprop(node.Offset(), self.name, self.bytes)
221 else:
222 fdt_obj.setprop(node.Offset(), self.name, self.bytes)
223
224
Simon Glass059ae522017-05-27 07:38:28 -0600225class Node:
Simon Glass1f941b62016-07-25 18:59:04 -0600226 """A device tree node
227
228 Properties:
229 offset: Integer offset in the device tree
230 name: Device tree node tname
231 path: Full path to node, along with the node name itself
232 _fdt: Device tree object
233 subnodes: A list of subnodes for this node, each a Node object
234 props: A dict of properties for this node, each a Prop object.
235 Keyed by property name
236 """
Simon Glass80ef7052017-08-29 14:15:47 -0600237 def __init__(self, fdt, parent, offset, name, path):
Simon Glass1f941b62016-07-25 18:59:04 -0600238 self._fdt = fdt
Simon Glass80ef7052017-08-29 14:15:47 -0600239 self.parent = parent
Simon Glass1f941b62016-07-25 18:59:04 -0600240 self._offset = offset
241 self.name = name
242 self.path = path
243 self.subnodes = []
244 self.props = {}
245
Simon Glasse2d65282018-07-17 13:25:46 -0600246 def GetFdt(self):
247 """Get the Fdt object for this node
248
249 Returns:
250 Fdt object
251 """
252 return self._fdt
253
Simon Glassaa1a5d72018-07-17 13:25:41 -0600254 def FindNode(self, name):
Simon Glasscc346a72016-07-25 18:59:07 -0600255 """Find a node given its name
256
257 Args:
258 name: Node name to look for
259 Returns:
260 Node object if found, else None
261 """
262 for subnode in self.subnodes:
263 if subnode.name == name:
264 return subnode
265 return None
266
Simon Glass059ae522017-05-27 07:38:28 -0600267 def Offset(self):
268 """Returns the offset of a node, after checking the cache
269
270 This should be used instead of self._offset directly, to ensure that
271 the cache does not contain invalid offsets.
272 """
273 self._fdt.CheckCache()
274 return self._offset
275
Simon Glasscc346a72016-07-25 18:59:07 -0600276 def Scan(self):
Simon Glass059ae522017-05-27 07:38:28 -0600277 """Scan a node's properties and subnodes
Simon Glasscc346a72016-07-25 18:59:07 -0600278
Simon Glass059ae522017-05-27 07:38:28 -0600279 This fills in the props and subnodes properties, recursively
280 searching into subnodes so that the entire tree is built.
Simon Glasscc346a72016-07-25 18:59:07 -0600281 """
Simon Glass151aeed2018-07-06 10:27:26 -0600282 fdt_obj = self._fdt._fdt_obj
Simon Glass059ae522017-05-27 07:38:28 -0600283 self.props = self._fdt.GetProps(self)
Simon Glass151aeed2018-07-06 10:27:26 -0600284 phandle = fdt_obj.get_phandle(self.Offset())
Simon Glassa3f94442017-08-29 14:15:52 -0600285 if phandle:
Simon Glass151aeed2018-07-06 10:27:26 -0600286 self._fdt.phandle_to_node[phandle] = self
Simon Glass059ae522017-05-27 07:38:28 -0600287
Simon Glass151aeed2018-07-06 10:27:26 -0600288 offset = fdt_obj.first_subnode(self.Offset(), QUIET_NOTFOUND)
Simon Glass059ae522017-05-27 07:38:28 -0600289 while offset >= 0:
290 sep = '' if self.path[-1] == '/' else '/'
Simon Glass151aeed2018-07-06 10:27:26 -0600291 name = fdt_obj.get_name(offset)
Simon Glass059ae522017-05-27 07:38:28 -0600292 path = self.path + sep + name
Simon Glass80ef7052017-08-29 14:15:47 -0600293 node = Node(self._fdt, self, offset, name, path)
Simon Glass059ae522017-05-27 07:38:28 -0600294 self.subnodes.append(node)
295
296 node.Scan()
Simon Glass151aeed2018-07-06 10:27:26 -0600297 offset = fdt_obj.next_subnode(offset, QUIET_NOTFOUND)
Simon Glass059ae522017-05-27 07:38:28 -0600298
299 def Refresh(self, my_offset):
300 """Fix up the _offset for each node, recursively
301
302 Note: This does not take account of property offsets - these will not
303 be updated.
304 """
Simon Glass792d2392018-07-06 10:27:27 -0600305 fdt_obj = self._fdt._fdt_obj
Simon Glass059ae522017-05-27 07:38:28 -0600306 if self._offset != my_offset:
Simon Glass059ae522017-05-27 07:38:28 -0600307 self._offset = my_offset
Simon Glass792d2392018-07-06 10:27:27 -0600308 offset = fdt_obj.first_subnode(self._offset, QUIET_NOTFOUND)
Simon Glass059ae522017-05-27 07:38:28 -0600309 for subnode in self.subnodes:
Simon Glass4df8a0c2018-07-06 10:27:29 -0600310 if subnode.name != fdt_obj.get_name(offset):
311 raise ValueError('Internal error, node name mismatch %s != %s' %
312 (subnode.name, fdt_obj.get_name(offset)))
Simon Glass059ae522017-05-27 07:38:28 -0600313 subnode.Refresh(offset)
Simon Glass792d2392018-07-06 10:27:27 -0600314 offset = fdt_obj.next_subnode(offset, QUIET_NOTFOUND)
Simon Glass4df8a0c2018-07-06 10:27:29 -0600315 if offset != -libfdt.FDT_ERR_NOTFOUND:
316 raise ValueError('Internal error, offset == %d' % offset)
317
318 poffset = fdt_obj.first_property_offset(self._offset, QUIET_NOTFOUND)
319 while poffset >= 0:
320 p = fdt_obj.get_property_by_offset(poffset)
321 prop = self.props.get(p.name)
322 if not prop:
323 raise ValueError("Internal error, property '%s' missing, "
324 'offset %d' % (p.name, poffset))
325 prop.RefreshOffset(poffset)
326 poffset = fdt_obj.next_property_offset(poffset, QUIET_NOTFOUND)
Simon Glasscc346a72016-07-25 18:59:07 -0600327
Simon Glassc719e422016-07-25 18:59:14 -0600328 def DeleteProp(self, prop_name):
329 """Delete a property of a node
330
Simon Glass059ae522017-05-27 07:38:28 -0600331 The property is deleted and the offset cache is invalidated.
Simon Glassc719e422016-07-25 18:59:14 -0600332
333 Args:
334 prop_name: Name of the property to delete
Simon Glass059ae522017-05-27 07:38:28 -0600335 Raises:
336 ValueError if the property does not exist
Simon Glassc719e422016-07-25 18:59:14 -0600337 """
Simon Glass792d2392018-07-06 10:27:27 -0600338 CheckErr(self._fdt._fdt_obj.delprop(self.Offset(), prop_name),
Simon Glass059ae522017-05-27 07:38:28 -0600339 "Node '%s': delete property: '%s'" % (self.path, prop_name))
340 del self.props[prop_name]
341 self._fdt.Invalidate()
Simon Glassc719e422016-07-25 18:59:14 -0600342
Simon Glasse80c5562018-07-06 10:27:38 -0600343 def AddZeroProp(self, prop_name):
344 """Add a new property to the device tree with an integer value of 0.
345
346 Args:
347 prop_name: Name of property
348 """
Simon Glass6bbfafb2019-05-17 22:00:33 -0600349 self.props[prop_name] = Prop(self, None, prop_name,
350 tools.GetBytes(0, 4))
Simon Glasse80c5562018-07-06 10:27:38 -0600351
Simon Glassccd25262018-09-14 04:57:16 -0600352 def AddEmptyProp(self, prop_name, len):
353 """Add a property with a fixed data size, for filling in later
354
355 The device tree is marked dirty so that the value will be written to
356 the blob on the next sync.
357
358 Args:
359 prop_name: Name of property
360 len: Length of data in property
361 """
Simon Glass6bbfafb2019-05-17 22:00:33 -0600362 value = tools.GetBytes(0, len)
Simon Glassccd25262018-09-14 04:57:16 -0600363 self.props[prop_name] = Prop(self, None, prop_name, value)
364
Simon Glassa683a5f2019-07-20 12:23:37 -0600365 def _CheckProp(self, prop_name):
366 """Check if a property is present
367
368 Args:
369 prop_name: Name of property
370
371 Returns:
372 self
373
374 Raises:
375 ValueError if the property is missing
376 """
377 if prop_name not in self.props:
378 raise ValueError("Fdt '%s', node '%s': Missing property '%s'" %
379 (self._fdt._fname, self.path, prop_name))
380 return self
381
Simon Glasse80c5562018-07-06 10:27:38 -0600382 def SetInt(self, prop_name, val):
383 """Update an integer property int the device tree.
384
385 This is not allowed to change the size of the FDT.
386
Simon Glassccd25262018-09-14 04:57:16 -0600387 The device tree is marked dirty so that the value will be written to
388 the blob on the next sync.
389
Simon Glasse80c5562018-07-06 10:27:38 -0600390 Args:
391 prop_name: Name of property
392 val: Value to set
393 """
Simon Glassa683a5f2019-07-20 12:23:37 -0600394 self._CheckProp(prop_name).props[prop_name].SetInt(val)
Simon Glasseddd7292018-09-14 04:57:13 -0600395
Simon Glassccd25262018-09-14 04:57:16 -0600396 def SetData(self, prop_name, val):
397 """Set the data value of a property
398
399 The device tree is marked dirty so that the value will be written to
400 the blob on the next sync.
401
402 Args:
403 prop_name: Name of property to set
404 val: Data value to set
405 """
Simon Glassa683a5f2019-07-20 12:23:37 -0600406 self._CheckProp(prop_name).props[prop_name].SetData(val)
Simon Glassccd25262018-09-14 04:57:16 -0600407
408 def SetString(self, prop_name, val):
409 """Set the string value of a property
410
411 The device tree is marked dirty so that the value will be written to
412 the blob on the next sync.
413
414 Args:
415 prop_name: Name of property to set
416 val: String value to set (will be \0-terminated in DT)
417 """
Simon Glass1cd40082019-05-17 22:00:36 -0600418 if sys.version_info[0] >= 3: # pragma: no cover
419 val = bytes(val, 'utf-8')
Simon Glassa683a5f2019-07-20 12:23:37 -0600420 self._CheckProp(prop_name).props[prop_name].SetData(val + b'\0')
Simon Glassccd25262018-09-14 04:57:16 -0600421
422 def AddString(self, prop_name, val):
423 """Add a new string property to a node
424
425 The device tree is marked dirty so that the value will be written to
426 the blob on the next sync.
427
428 Args:
429 prop_name: Name of property to add
430 val: String value of property
431 """
Simon Glass1cd40082019-05-17 22:00:36 -0600432 if sys.version_info[0] >= 3: # pragma: no cover
433 val = bytes(val, 'utf-8')
434 self.props[prop_name] = Prop(self, None, prop_name, val + b'\0')
Simon Glassccd25262018-09-14 04:57:16 -0600435
Simon Glassf3a17962018-09-14 04:57:15 -0600436 def AddSubnode(self, name):
Simon Glassccd25262018-09-14 04:57:16 -0600437 """Add a new subnode to the node
438
439 Args:
440 name: name of node to add
441
442 Returns:
443 New subnode that was created
444 """
Simon Glassf3a17962018-09-14 04:57:15 -0600445 path = self.path + '/' + name
446 subnode = Node(self._fdt, self, None, name, path)
447 self.subnodes.append(subnode)
448 return subnode
449
Simon Glasseddd7292018-09-14 04:57:13 -0600450 def Sync(self, auto_resize=False):
451 """Sync node changes back to the device tree
452
453 This updates the device tree blob with any changes to this node and its
454 subnodes since the last sync.
455
456 Args:
457 auto_resize: Resize the device tree automatically if it does not
458 have enough space for the update
459
460 Raises:
461 FdtException if auto_resize is False and there is not enough space
462 """
Simon Glassf3a17962018-09-14 04:57:15 -0600463 if self._offset is None:
464 # The subnode doesn't exist yet, so add it
465 fdt_obj = self._fdt._fdt_obj
466 if auto_resize:
467 while True:
468 offset = fdt_obj.add_subnode(self.parent._offset, self.name,
469 (libfdt.NOSPACE,))
470 if offset != -libfdt.NOSPACE:
471 break
472 fdt_obj.resize(fdt_obj.totalsize() + 1024)
473 else:
474 offset = fdt_obj.add_subnode(self.parent._offset, self.name)
475 self._offset = offset
476
Simon Glasseddd7292018-09-14 04:57:13 -0600477 # Sync subnodes in reverse so that we don't disturb node offsets for
478 # nodes that are earlier in the DT. This avoids an O(n^2) rescan of
479 # node offsets.
480 for node in reversed(self.subnodes):
481 node.Sync(auto_resize)
Simon Glasse80c5562018-07-06 10:27:38 -0600482
Simon Glasseddd7292018-09-14 04:57:13 -0600483 # Sync properties now, whose offsets should not have been disturbed.
484 # We do this after subnodes, since this disturbs the offsets of these
Simon Glassa57cfee2019-05-17 22:00:38 -0600485 # properties. Note that new properties will have an offset of None here,
486 # which Python 3 cannot sort against int. So use a large value instead
487 # to ensure that the new properties are added first.
488 prop_list = sorted(self.props.values(),
489 key=lambda prop: prop._offset or 1 << 31,
Simon Glasseddd7292018-09-14 04:57:13 -0600490 reverse=True)
491 for prop in prop_list:
492 prop.Sync(auto_resize)
493
Simon Glasse80c5562018-07-06 10:27:38 -0600494
Simon Glass1f941b62016-07-25 18:59:04 -0600495class Fdt:
Simon Glass059ae522017-05-27 07:38:28 -0600496 """Provides simple access to a flat device tree blob using libfdts.
Simon Glass1f941b62016-07-25 18:59:04 -0600497
498 Properties:
499 fname: Filename of fdt
500 _root: Root of device tree (a Node object)
Simon Glass50cfc6e2019-07-20 12:23:38 -0600501 name: Helpful name for this Fdt for the user (useful when creating the
502 DT from data rather than a file)
Simon Glass1f941b62016-07-25 18:59:04 -0600503 """
504 def __init__(self, fname):
505 self._fname = fname
Simon Glass059ae522017-05-27 07:38:28 -0600506 self._cached_offsets = False
Simon Glassa3f94442017-08-29 14:15:52 -0600507 self.phandle_to_node = {}
Simon Glass50cfc6e2019-07-20 12:23:38 -0600508 self.name = ''
Simon Glass059ae522017-05-27 07:38:28 -0600509 if self._fname:
Simon Glass50cfc6e2019-07-20 12:23:38 -0600510 self.name = self._fname
Simon Glass059ae522017-05-27 07:38:28 -0600511 self._fname = fdt_util.EnsureCompiled(self._fname)
512
Simon Glass116236f2019-05-14 15:53:43 -0600513 with open(self._fname, 'rb') as fd:
Simon Glass792d2392018-07-06 10:27:27 -0600514 self._fdt_obj = libfdt.Fdt(fd.read())
Simon Glasscc346a72016-07-25 18:59:07 -0600515
Simon Glassb8a49292018-09-14 04:57:17 -0600516 @staticmethod
Simon Glass50cfc6e2019-07-20 12:23:38 -0600517 def FromData(data, name=''):
Simon Glassb8a49292018-09-14 04:57:17 -0600518 """Create a new Fdt object from the given data
519
520 Args:
521 data: Device-tree data blob
Simon Glass50cfc6e2019-07-20 12:23:38 -0600522 name: Helpful name for this Fdt for the user
Simon Glassb8a49292018-09-14 04:57:17 -0600523
524 Returns:
525 Fdt object containing the data
526 """
527 fdt = Fdt(None)
Simon Glass1cd40082019-05-17 22:00:36 -0600528 fdt._fdt_obj = libfdt.Fdt(bytes(data))
Simon Glass50cfc6e2019-07-20 12:23:38 -0600529 fdt.name = name
Simon Glassb8a49292018-09-14 04:57:17 -0600530 return fdt
531
Simon Glasse2d65282018-07-17 13:25:46 -0600532 def LookupPhandle(self, phandle):
533 """Look up a phandle
534
535 Args:
536 phandle: Phandle to look up (int)
537
538 Returns:
539 Node object the phandle points to
540 """
541 return self.phandle_to_node.get(phandle)
542
Simon Glasscc346a72016-07-25 18:59:07 -0600543 def Scan(self, root='/'):
544 """Scan a device tree, building up a tree of Node objects
545
546 This fills in the self._root property
547
548 Args:
549 root: Ignored
550
551 TODO(sjg@chromium.org): Implement the 'root' parameter
552 """
Simon Glass4df8a0c2018-07-06 10:27:29 -0600553 self._cached_offsets = True
Simon Glass80ef7052017-08-29 14:15:47 -0600554 self._root = self.Node(self, None, 0, '/', '/')
Simon Glasscc346a72016-07-25 18:59:07 -0600555 self._root.Scan()
556
557 def GetRoot(self):
558 """Get the root Node of the device tree
559
560 Returns:
561 The root Node object
562 """
563 return self._root
564
565 def GetNode(self, path):
566 """Look up a node from its path
567
568 Args:
569 path: Path to look up, e.g. '/microcode/update@0'
570 Returns:
571 Node object, or None if not found
572 """
573 node = self._root
Simon Glassc5eddc82018-07-06 10:27:30 -0600574 parts = path.split('/')
575 if len(parts) < 2:
576 return None
Simon Glass3b9a8292019-07-20 12:23:39 -0600577 if len(parts) == 2 and parts[1] == '':
578 return node
Simon Glassc5eddc82018-07-06 10:27:30 -0600579 for part in parts[1:]:
Simon Glassaa1a5d72018-07-17 13:25:41 -0600580 node = node.FindNode(part)
Simon Glasscc346a72016-07-25 18:59:07 -0600581 if not node:
582 return None
583 return node
584
Simon Glass32d98272016-07-25 18:59:15 -0600585 def Flush(self):
586 """Flush device tree changes back to the file
587
588 If the device tree has changed in memory, write it back to the file.
Simon Glass32d98272016-07-25 18:59:15 -0600589 """
Simon Glass059ae522017-05-27 07:38:28 -0600590 with open(self._fname, 'wb') as fd:
Simon Glass792d2392018-07-06 10:27:27 -0600591 fd.write(self._fdt_obj.as_bytearray())
Simon Glass32d98272016-07-25 18:59:15 -0600592
Simon Glasseddd7292018-09-14 04:57:13 -0600593 def Sync(self, auto_resize=False):
594 """Make sure any DT changes are written to the blob
595
596 Args:
597 auto_resize: Resize the device tree automatically if it does not
598 have enough space for the update
599
600 Raises:
601 FdtException if auto_resize is False and there is not enough space
602 """
603 self._root.Sync(auto_resize)
604 self.Invalidate()
605
Simon Glass32d98272016-07-25 18:59:15 -0600606 def Pack(self):
607 """Pack the device tree down to its minimum size
608
609 When nodes and properties shrink or are deleted, wasted space can
Simon Glass059ae522017-05-27 07:38:28 -0600610 build up in the device tree binary.
611 """
Simon Glass151aeed2018-07-06 10:27:26 -0600612 CheckErr(self._fdt_obj.pack(), 'pack')
613 self.Invalidate()
Simon Glass059ae522017-05-27 07:38:28 -0600614
Simon Glass792d2392018-07-06 10:27:27 -0600615 def GetContents(self):
Simon Glass059ae522017-05-27 07:38:28 -0600616 """Get the contents of the FDT
617
618 Returns:
619 The FDT contents as a string of bytes
Simon Glass32d98272016-07-25 18:59:15 -0600620 """
Simon Glass1cd40082019-05-17 22:00:36 -0600621 return bytes(self._fdt_obj.as_bytearray())
Simon Glass059ae522017-05-27 07:38:28 -0600622
Simon Glass0ed50752018-07-06 10:27:24 -0600623 def GetFdtObj(self):
624 """Get the contents of the FDT
625
626 Returns:
627 The FDT contents as a libfdt.Fdt object
628 """
629 return self._fdt_obj
630
Simon Glass059ae522017-05-27 07:38:28 -0600631 def GetProps(self, node):
632 """Get all properties from a node.
633
634 Args:
635 node: Full path to node name to look in.
636
637 Returns:
638 A dictionary containing all the properties, indexed by node name.
639 The entries are Prop objects.
640
641 Raises:
642 ValueError: if the node does not exist.
643 """
644 props_dict = {}
Simon Glass151aeed2018-07-06 10:27:26 -0600645 poffset = self._fdt_obj.first_property_offset(node._offset,
646 QUIET_NOTFOUND)
Simon Glass059ae522017-05-27 07:38:28 -0600647 while poffset >= 0:
648 p = self._fdt_obj.get_property_by_offset(poffset)
Simon Glass70cd0d72018-07-06 10:27:20 -0600649 prop = Prop(node, poffset, p.name, p)
Simon Glass059ae522017-05-27 07:38:28 -0600650 props_dict[prop.name] = prop
651
Simon Glass151aeed2018-07-06 10:27:26 -0600652 poffset = self._fdt_obj.next_property_offset(poffset,
653 QUIET_NOTFOUND)
Simon Glass059ae522017-05-27 07:38:28 -0600654 return props_dict
655
656 def Invalidate(self):
657 """Mark our offset cache as invalid"""
658 self._cached_offsets = False
659
660 def CheckCache(self):
661 """Refresh the offset cache if needed"""
662 if self._cached_offsets:
663 return
664 self.Refresh()
665 self._cached_offsets = True
666
667 def Refresh(self):
668 """Refresh the offset cache"""
669 self._root.Refresh(0)
670
671 def GetStructOffset(self, offset):
672 """Get the file offset of a given struct offset
673
674 Args:
675 offset: Offset within the 'struct' region of the device tree
676 Returns:
677 Position of @offset within the device tree binary
678 """
Simon Glass151aeed2018-07-06 10:27:26 -0600679 return self._fdt_obj.off_dt_struct() + offset
Simon Glass059ae522017-05-27 07:38:28 -0600680
681 @classmethod
Simon Glass80ef7052017-08-29 14:15:47 -0600682 def Node(self, fdt, parent, offset, name, path):
Simon Glass059ae522017-05-27 07:38:28 -0600683 """Create a new node
684
685 This is used by Fdt.Scan() to create a new node using the correct
686 class.
687
688 Args:
689 fdt: Fdt object
Simon Glass80ef7052017-08-29 14:15:47 -0600690 parent: Parent node, or None if this is the root node
Simon Glass059ae522017-05-27 07:38:28 -0600691 offset: Offset of node
692 name: Node name
693 path: Full path to node
694 """
Simon Glass80ef7052017-08-29 14:15:47 -0600695 node = Node(fdt, parent, offset, name, path)
Simon Glass059ae522017-05-27 07:38:28 -0600696 return node
Simon Glassa9440932017-05-27 07:38:30 -0600697
Simon Glass74f5feb2019-07-20 12:24:08 -0600698 def GetFilename(self):
699 """Get the filename of the device tree
700
701 Returns:
702 String filename
703 """
704 return self._fname
705
Simon Glassa9440932017-05-27 07:38:30 -0600706def FdtScan(fname):
Simon Glassc38fee042018-07-06 10:27:32 -0600707 """Returns a new Fdt object"""
Simon Glassa9440932017-05-27 07:38:30 -0600708 dtb = Fdt(fname)
709 dtb.Scan()
710 return dtb