1a06a34b2SSimon Glass#!/usr/bin/python 283d290c5STom Rini# SPDX-License-Identifier: GPL-2.0+ 3a06a34b2SSimon Glass# 4a06a34b2SSimon Glass# Copyright (C) 2016 Google, Inc 5a06a34b2SSimon Glass# Written by Simon Glass <sjg@chromium.org> 6a06a34b2SSimon Glass# 7a06a34b2SSimon Glass 8a06a34b2SSimon Glassimport struct 9a06a34b2SSimon Glassimport sys 10a06a34b2SSimon Glass 11a06a34b2SSimon Glassimport fdt_util 127b75b448SSimon Glassimport libfdt 13117f57b7SSimon Glassfrom libfdt import QUIET_NOTFOUND 14a06a34b2SSimon Glass 15a06a34b2SSimon Glass# This deals with a device tree, presenting it as an assortment of Node and 16a06a34b2SSimon Glass# Prop objects, representing nodes and properties, respectively. This file 1799ed4a2eSSimon Glass# contains the base classes and defines the high-level API. You can use 1899ed4a2eSSimon Glass# FdtScan() as a convenience function to create and scan an Fdt. 197b75b448SSimon Glass 207b75b448SSimon Glass# This implementation uses a libfdt Python library to access the device tree, 217b75b448SSimon Glass# so it is fairly efficient. 22a06a34b2SSimon Glass 23bc1dea36SSimon Glass# A list of types we support 24fbdfd228SSimon Glass(TYPE_BYTE, TYPE_INT, TYPE_STRING, TYPE_BOOL, TYPE_INT64) = range(5) 25bc1dea36SSimon Glass 26a06a34b2SSimon Glassdef CheckErr(errnum, msg): 27a06a34b2SSimon Glass if errnum: 28a06a34b2SSimon Glass raise ValueError('Error %d: %s: %s' % 29a06a34b2SSimon Glass (errnum, libfdt.fdt_strerror(errnum), msg)) 30a06a34b2SSimon Glass 317b75b448SSimon Glassclass Prop: 32a06a34b2SSimon Glass """A device tree property 33a06a34b2SSimon Glass 34a06a34b2SSimon Glass Properties: 35a06a34b2SSimon Glass name: Property name (as per the device tree) 36a06a34b2SSimon Glass value: Property value as a string of bytes, or a list of strings of 37a06a34b2SSimon Glass bytes 38a06a34b2SSimon Glass type: Value type 39a06a34b2SSimon Glass """ 407b75b448SSimon Glass def __init__(self, node, offset, name, bytes): 41a06a34b2SSimon Glass self._node = node 42a06a34b2SSimon Glass self._offset = offset 43a06a34b2SSimon Glass self.name = name 44a06a34b2SSimon Glass self.value = None 457b75b448SSimon Glass self.bytes = str(bytes) 46fa80c25cSSimon Glass self.dirty = False 477b75b448SSimon Glass if not bytes: 487b75b448SSimon Glass self.type = TYPE_BOOL 497b75b448SSimon Glass self.value = True 507b75b448SSimon Glass return 517b75b448SSimon Glass self.type, self.value = self.BytesToValue(bytes) 52a06a34b2SSimon Glass 53f9b88b3aSSimon Glass def RefreshOffset(self, poffset): 54f9b88b3aSSimon Glass self._offset = poffset 55f9b88b3aSSimon Glass 56c322a850SSimon Glass def Widen(self, newprop): 57c322a850SSimon Glass """Figure out which property type is more general 58c322a850SSimon Glass 59c322a850SSimon Glass Given a current property and a new property, this function returns the 60c322a850SSimon Glass one that is less specific as to type. The less specific property will 61c322a850SSimon Glass be ble to represent the data in the more specific property. This is 62c322a850SSimon Glass used for things like: 63c322a850SSimon Glass 64c322a850SSimon Glass node1 { 65c322a850SSimon Glass compatible = "fred"; 66c322a850SSimon Glass value = <1>; 67c322a850SSimon Glass }; 68c322a850SSimon Glass node1 { 69c322a850SSimon Glass compatible = "fred"; 70c322a850SSimon Glass value = <1 2>; 71c322a850SSimon Glass }; 72c322a850SSimon Glass 73c322a850SSimon Glass He we want to use an int array for 'value'. The first property 74c322a850SSimon Glass suggests that a single int is enough, but the second one shows that 75c322a850SSimon Glass it is not. Calling this function with these two propertes would 76c322a850SSimon Glass update the current property to be like the second, since it is less 77c322a850SSimon Glass specific. 78c322a850SSimon Glass """ 79c322a850SSimon Glass if newprop.type < self.type: 80c322a850SSimon Glass self.type = newprop.type 81c322a850SSimon Glass 82c322a850SSimon Glass if type(newprop.value) == list and type(self.value) != list: 83c322a850SSimon Glass self.value = [self.value] 84c322a850SSimon Glass 85c322a850SSimon Glass if type(self.value) == list and len(newprop.value) > len(self.value): 86c322a850SSimon Glass val = self.GetEmpty(self.type) 87c322a850SSimon Glass while len(self.value) < len(newprop.value): 88c322a850SSimon Glass self.value.append(val) 89c322a850SSimon Glass 90bc1dea36SSimon Glass def BytesToValue(self, bytes): 91bc1dea36SSimon Glass """Converts a string of bytes into a type and value 92bc1dea36SSimon Glass 93bc1dea36SSimon Glass Args: 94bc1dea36SSimon Glass A string containing bytes 95bc1dea36SSimon Glass 96bc1dea36SSimon Glass Return: 97bc1dea36SSimon Glass A tuple: 98bc1dea36SSimon Glass Type of data 99bc1dea36SSimon Glass Data, either a single element or a list of elements. Each element 100bc1dea36SSimon Glass is one of: 101bc1dea36SSimon Glass TYPE_STRING: string value from the property 102bc1dea36SSimon Glass TYPE_INT: a byte-swapped integer stored as a 4-byte string 103bc1dea36SSimon Glass TYPE_BYTE: a byte stored as a single-byte string 104bc1dea36SSimon Glass """ 105b4360206SSimon Glass bytes = str(bytes) 106bc1dea36SSimon Glass size = len(bytes) 107bc1dea36SSimon Glass strings = bytes.split('\0') 108bc1dea36SSimon Glass is_string = True 109bc1dea36SSimon Glass count = len(strings) - 1 110bc1dea36SSimon Glass if count > 0 and not strings[-1]: 111bc1dea36SSimon Glass for string in strings[:-1]: 112bc1dea36SSimon Glass if not string: 113bc1dea36SSimon Glass is_string = False 114bc1dea36SSimon Glass break 115bc1dea36SSimon Glass for ch in string: 116bc1dea36SSimon Glass if ch < ' ' or ch > '~': 117bc1dea36SSimon Glass is_string = False 118bc1dea36SSimon Glass break 119bc1dea36SSimon Glass else: 120bc1dea36SSimon Glass is_string = False 121bc1dea36SSimon Glass if is_string: 122bc1dea36SSimon Glass if count == 1: 123bc1dea36SSimon Glass return TYPE_STRING, strings[0] 124bc1dea36SSimon Glass else: 125bc1dea36SSimon Glass return TYPE_STRING, strings[:-1] 126bc1dea36SSimon Glass if size % 4: 127bc1dea36SSimon Glass if size == 1: 128bc1dea36SSimon Glass return TYPE_BYTE, bytes[0] 129bc1dea36SSimon Glass else: 130bc1dea36SSimon Glass return TYPE_BYTE, list(bytes) 131bc1dea36SSimon Glass val = [] 132bc1dea36SSimon Glass for i in range(0, size, 4): 133bc1dea36SSimon Glass val.append(bytes[i:i + 4]) 134bc1dea36SSimon Glass if size == 4: 135bc1dea36SSimon Glass return TYPE_INT, val[0] 136bc1dea36SSimon Glass else: 137bc1dea36SSimon Glass return TYPE_INT, val 138bc1dea36SSimon Glass 1392ba98753SSimon Glass @classmethod 140bc1dea36SSimon Glass def GetEmpty(self, type): 141bc1dea36SSimon Glass """Get an empty / zero value of the given type 142bc1dea36SSimon Glass 143bc1dea36SSimon Glass Returns: 144bc1dea36SSimon Glass A single value of the given type 145bc1dea36SSimon Glass """ 146bc1dea36SSimon Glass if type == TYPE_BYTE: 147bc1dea36SSimon Glass return chr(0) 148bc1dea36SSimon Glass elif type == TYPE_INT: 149af53f5aaSSimon Glass return struct.pack('>I', 0); 150bc1dea36SSimon Glass elif type == TYPE_STRING: 151bc1dea36SSimon Glass return '' 152bc1dea36SSimon Glass else: 153bc1dea36SSimon Glass return True 154bc1dea36SSimon Glass 155babdbde6SSimon Glass def GetOffset(self): 156babdbde6SSimon Glass """Get the offset of a property 157babdbde6SSimon Glass 158babdbde6SSimon Glass Returns: 1597b75b448SSimon Glass The offset of the property (struct fdt_property) within the file 160babdbde6SSimon Glass """ 161f9b88b3aSSimon Glass self._node._fdt.CheckCache() 1627b75b448SSimon Glass return self._node._fdt.GetStructOffset(self._offset) 163babdbde6SSimon Glass 164fa80c25cSSimon Glass def SetInt(self, val): 165fa80c25cSSimon Glass """Set the integer value of the property 166fa80c25cSSimon Glass 167fa80c25cSSimon Glass The device tree is marked dirty so that the value will be written to 168fa80c25cSSimon Glass the block on the next sync. 169fa80c25cSSimon Glass 170fa80c25cSSimon Glass Args: 171fa80c25cSSimon Glass val: Integer value (32-bit, single cell) 172fa80c25cSSimon Glass """ 173fa80c25cSSimon Glass self.bytes = struct.pack('>I', val); 174*41b781ddSSimon Glass self.value = self.bytes 175fa80c25cSSimon Glass self.type = TYPE_INT 176fa80c25cSSimon Glass self.dirty = True 177fa80c25cSSimon Glass 1786434961bSSimon Glass def SetData(self, bytes): 1796434961bSSimon Glass """Set the value of a property as bytes 1806434961bSSimon Glass 1816434961bSSimon Glass Args: 1826434961bSSimon Glass bytes: New property value to set 1836434961bSSimon Glass """ 1846434961bSSimon Glass self.bytes = str(bytes) 1856434961bSSimon Glass self.type, self.value = self.BytesToValue(bytes) 1866434961bSSimon Glass self.dirty = True 1876434961bSSimon Glass 188fa80c25cSSimon Glass def Sync(self, auto_resize=False): 189fa80c25cSSimon Glass """Sync property changes back to the device tree 190fa80c25cSSimon Glass 191fa80c25cSSimon Glass This updates the device tree blob with any changes to this property 192fa80c25cSSimon Glass since the last sync. 193fa80c25cSSimon Glass 194fa80c25cSSimon Glass Args: 195fa80c25cSSimon Glass auto_resize: Resize the device tree automatically if it does not 196fa80c25cSSimon Glass have enough space for the update 197fa80c25cSSimon Glass 198fa80c25cSSimon Glass Raises: 199fa80c25cSSimon Glass FdtException if auto_resize is False and there is not enough space 200fa80c25cSSimon Glass """ 201fa80c25cSSimon Glass if self._offset is None or self.dirty: 202fa80c25cSSimon Glass node = self._node 203fa80c25cSSimon Glass fdt_obj = node._fdt._fdt_obj 204fa80c25cSSimon Glass if auto_resize: 205fa80c25cSSimon Glass while fdt_obj.setprop(node.Offset(), self.name, self.bytes, 206fa80c25cSSimon Glass (libfdt.NOSPACE,)) == -libfdt.NOSPACE: 207fa80c25cSSimon Glass fdt_obj.resize(fdt_obj.totalsize() + 1024) 208fa80c25cSSimon Glass fdt_obj.setprop(node.Offset(), self.name, self.bytes) 209fa80c25cSSimon Glass else: 210fa80c25cSSimon Glass fdt_obj.setprop(node.Offset(), self.name, self.bytes) 211fa80c25cSSimon Glass 212fa80c25cSSimon Glass 2137b75b448SSimon Glassclass Node: 214a06a34b2SSimon Glass """A device tree node 215a06a34b2SSimon Glass 216a06a34b2SSimon Glass Properties: 217a06a34b2SSimon Glass offset: Integer offset in the device tree 218a06a34b2SSimon Glass name: Device tree node tname 219a06a34b2SSimon Glass path: Full path to node, along with the node name itself 220a06a34b2SSimon Glass _fdt: Device tree object 221a06a34b2SSimon Glass subnodes: A list of subnodes for this node, each a Node object 222a06a34b2SSimon Glass props: A dict of properties for this node, each a Prop object. 223a06a34b2SSimon Glass Keyed by property name 224a06a34b2SSimon Glass """ 225979ab024SSimon Glass def __init__(self, fdt, parent, offset, name, path): 226a06a34b2SSimon Glass self._fdt = fdt 227979ab024SSimon Glass self.parent = parent 228a06a34b2SSimon Glass self._offset = offset 229a06a34b2SSimon Glass self.name = name 230a06a34b2SSimon Glass self.path = path 231a06a34b2SSimon Glass self.subnodes = [] 232a06a34b2SSimon Glass self.props = {} 233a06a34b2SSimon Glass 23494a7c603SSimon Glass def GetFdt(self): 23594a7c603SSimon Glass """Get the Fdt object for this node 23694a7c603SSimon Glass 23794a7c603SSimon Glass Returns: 23894a7c603SSimon Glass Fdt object 23994a7c603SSimon Glass """ 24094a7c603SSimon Glass return self._fdt 24194a7c603SSimon Glass 2421d85888cSSimon Glass def FindNode(self, name): 243f7a2aeeeSSimon Glass """Find a node given its name 244f7a2aeeeSSimon Glass 245f7a2aeeeSSimon Glass Args: 246f7a2aeeeSSimon Glass name: Node name to look for 247f7a2aeeeSSimon Glass Returns: 248f7a2aeeeSSimon Glass Node object if found, else None 249f7a2aeeeSSimon Glass """ 250f7a2aeeeSSimon Glass for subnode in self.subnodes: 251f7a2aeeeSSimon Glass if subnode.name == name: 252f7a2aeeeSSimon Glass return subnode 253f7a2aeeeSSimon Glass return None 254f7a2aeeeSSimon Glass 2557b75b448SSimon Glass def Offset(self): 2567b75b448SSimon Glass """Returns the offset of a node, after checking the cache 257f7a2aeeeSSimon Glass 2587b75b448SSimon Glass This should be used instead of self._offset directly, to ensure that 2597b75b448SSimon Glass the cache does not contain invalid offsets. 260f7a2aeeeSSimon Glass """ 2617b75b448SSimon Glass self._fdt.CheckCache() 2627b75b448SSimon Glass return self._offset 2637b75b448SSimon Glass 2647b75b448SSimon Glass def Scan(self): 2657b75b448SSimon Glass """Scan a node's properties and subnodes 2667b75b448SSimon Glass 2677b75b448SSimon Glass This fills in the props and subnodes properties, recursively 2687b75b448SSimon Glass searching into subnodes so that the entire tree is built. 2697b75b448SSimon Glass """ 270117f57b7SSimon Glass fdt_obj = self._fdt._fdt_obj 2717b75b448SSimon Glass self.props = self._fdt.GetProps(self) 272117f57b7SSimon Glass phandle = fdt_obj.get_phandle(self.Offset()) 27309264e04SSimon Glass if phandle: 274117f57b7SSimon Glass self._fdt.phandle_to_node[phandle] = self 2757b75b448SSimon Glass 276117f57b7SSimon Glass offset = fdt_obj.first_subnode(self.Offset(), QUIET_NOTFOUND) 2777b75b448SSimon Glass while offset >= 0: 2787b75b448SSimon Glass sep = '' if self.path[-1] == '/' else '/' 279117f57b7SSimon Glass name = fdt_obj.get_name(offset) 2807b75b448SSimon Glass path = self.path + sep + name 281979ab024SSimon Glass node = Node(self._fdt, self, offset, name, path) 2827b75b448SSimon Glass self.subnodes.append(node) 2837b75b448SSimon Glass 2847b75b448SSimon Glass node.Scan() 285117f57b7SSimon Glass offset = fdt_obj.next_subnode(offset, QUIET_NOTFOUND) 2867b75b448SSimon Glass 2877b75b448SSimon Glass def Refresh(self, my_offset): 2887b75b448SSimon Glass """Fix up the _offset for each node, recursively 2897b75b448SSimon Glass 2907b75b448SSimon Glass Note: This does not take account of property offsets - these will not 2917b75b448SSimon Glass be updated. 2927b75b448SSimon Glass """ 29396066240SSimon Glass fdt_obj = self._fdt._fdt_obj 2947b75b448SSimon Glass if self._offset != my_offset: 2957b75b448SSimon Glass self._offset = my_offset 29696066240SSimon Glass offset = fdt_obj.first_subnode(self._offset, QUIET_NOTFOUND) 2977b75b448SSimon Glass for subnode in self.subnodes: 298f9b88b3aSSimon Glass if subnode.name != fdt_obj.get_name(offset): 299f9b88b3aSSimon Glass raise ValueError('Internal error, node name mismatch %s != %s' % 300f9b88b3aSSimon Glass (subnode.name, fdt_obj.get_name(offset))) 3017b75b448SSimon Glass subnode.Refresh(offset) 30296066240SSimon Glass offset = fdt_obj.next_subnode(offset, QUIET_NOTFOUND) 303f9b88b3aSSimon Glass if offset != -libfdt.FDT_ERR_NOTFOUND: 304f9b88b3aSSimon Glass raise ValueError('Internal error, offset == %d' % offset) 305f9b88b3aSSimon Glass 306f9b88b3aSSimon Glass poffset = fdt_obj.first_property_offset(self._offset, QUIET_NOTFOUND) 307f9b88b3aSSimon Glass while poffset >= 0: 308f9b88b3aSSimon Glass p = fdt_obj.get_property_by_offset(poffset) 309f9b88b3aSSimon Glass prop = self.props.get(p.name) 310f9b88b3aSSimon Glass if not prop: 311f9b88b3aSSimon Glass raise ValueError("Internal error, property '%s' missing, " 312f9b88b3aSSimon Glass 'offset %d' % (p.name, poffset)) 313f9b88b3aSSimon Glass prop.RefreshOffset(poffset) 314f9b88b3aSSimon Glass poffset = fdt_obj.next_property_offset(poffset, QUIET_NOTFOUND) 315f7a2aeeeSSimon Glass 3162a70d897SSimon Glass def DeleteProp(self, prop_name): 3172a70d897SSimon Glass """Delete a property of a node 3182a70d897SSimon Glass 3197b75b448SSimon Glass The property is deleted and the offset cache is invalidated. 3202a70d897SSimon Glass 3212a70d897SSimon Glass Args: 3222a70d897SSimon Glass prop_name: Name of the property to delete 3237b75b448SSimon Glass Raises: 3247b75b448SSimon Glass ValueError if the property does not exist 3252a70d897SSimon Glass """ 32696066240SSimon Glass CheckErr(self._fdt._fdt_obj.delprop(self.Offset(), prop_name), 3277b75b448SSimon Glass "Node '%s': delete property: '%s'" % (self.path, prop_name)) 3287b75b448SSimon Glass del self.props[prop_name] 3297b75b448SSimon Glass self._fdt.Invalidate() 3302a70d897SSimon Glass 331116adecbSSimon Glass def AddZeroProp(self, prop_name): 332116adecbSSimon Glass """Add a new property to the device tree with an integer value of 0. 333116adecbSSimon Glass 334116adecbSSimon Glass Args: 335116adecbSSimon Glass prop_name: Name of property 336116adecbSSimon Glass """ 337fa80c25cSSimon Glass self.props[prop_name] = Prop(self, None, prop_name, '\0' * 4) 338116adecbSSimon Glass 3396434961bSSimon Glass def AddEmptyProp(self, prop_name, len): 3406434961bSSimon Glass """Add a property with a fixed data size, for filling in later 3416434961bSSimon Glass 3426434961bSSimon Glass The device tree is marked dirty so that the value will be written to 3436434961bSSimon Glass the blob on the next sync. 3446434961bSSimon Glass 3456434961bSSimon Glass Args: 3466434961bSSimon Glass prop_name: Name of property 3476434961bSSimon Glass len: Length of data in property 3486434961bSSimon Glass """ 3496434961bSSimon Glass value = chr(0) * len 3506434961bSSimon Glass self.props[prop_name] = Prop(self, None, prop_name, value) 3516434961bSSimon Glass 352116adecbSSimon Glass def SetInt(self, prop_name, val): 353116adecbSSimon Glass """Update an integer property int the device tree. 354116adecbSSimon Glass 355116adecbSSimon Glass This is not allowed to change the size of the FDT. 356116adecbSSimon Glass 3576434961bSSimon Glass The device tree is marked dirty so that the value will be written to 3586434961bSSimon Glass the blob on the next sync. 3596434961bSSimon Glass 360116adecbSSimon Glass Args: 361116adecbSSimon Glass prop_name: Name of property 362116adecbSSimon Glass val: Value to set 363116adecbSSimon Glass """ 364fa80c25cSSimon Glass self.props[prop_name].SetInt(val) 365fa80c25cSSimon Glass 3666434961bSSimon Glass def SetData(self, prop_name, val): 3676434961bSSimon Glass """Set the data value of a property 3686434961bSSimon Glass 3696434961bSSimon Glass The device tree is marked dirty so that the value will be written to 3706434961bSSimon Glass the blob on the next sync. 3716434961bSSimon Glass 3726434961bSSimon Glass Args: 3736434961bSSimon Glass prop_name: Name of property to set 3746434961bSSimon Glass val: Data value to set 3756434961bSSimon Glass """ 3766434961bSSimon Glass self.props[prop_name].SetData(val) 3776434961bSSimon Glass 3786434961bSSimon Glass def SetString(self, prop_name, val): 3796434961bSSimon Glass """Set the string value of a property 3806434961bSSimon Glass 3816434961bSSimon Glass The device tree is marked dirty so that the value will be written to 3826434961bSSimon Glass the blob on the next sync. 3836434961bSSimon Glass 3846434961bSSimon Glass Args: 3856434961bSSimon Glass prop_name: Name of property to set 3866434961bSSimon Glass val: String value to set (will be \0-terminated in DT) 3876434961bSSimon Glass """ 3886434961bSSimon Glass self.props[prop_name].SetData(val + chr(0)) 3896434961bSSimon Glass 3906434961bSSimon Glass def AddString(self, prop_name, val): 3916434961bSSimon Glass """Add a new string property to a node 3926434961bSSimon Glass 3936434961bSSimon Glass The device tree is marked dirty so that the value will be written to 3946434961bSSimon Glass the blob on the next sync. 3956434961bSSimon Glass 3966434961bSSimon Glass Args: 3976434961bSSimon Glass prop_name: Name of property to add 3986434961bSSimon Glass val: String value of property 3996434961bSSimon Glass """ 4006434961bSSimon Glass self.props[prop_name] = Prop(self, None, prop_name, val + chr(0)) 4016434961bSSimon Glass 402e21c27afSSimon Glass def AddSubnode(self, name): 4036434961bSSimon Glass """Add a new subnode to the node 4046434961bSSimon Glass 4056434961bSSimon Glass Args: 4066434961bSSimon Glass name: name of node to add 4076434961bSSimon Glass 4086434961bSSimon Glass Returns: 4096434961bSSimon Glass New subnode that was created 4106434961bSSimon Glass """ 411e21c27afSSimon Glass path = self.path + '/' + name 412e21c27afSSimon Glass subnode = Node(self._fdt, self, None, name, path) 413e21c27afSSimon Glass self.subnodes.append(subnode) 414e21c27afSSimon Glass return subnode 415e21c27afSSimon Glass 416fa80c25cSSimon Glass def Sync(self, auto_resize=False): 417fa80c25cSSimon Glass """Sync node changes back to the device tree 418fa80c25cSSimon Glass 419fa80c25cSSimon Glass This updates the device tree blob with any changes to this node and its 420fa80c25cSSimon Glass subnodes since the last sync. 421fa80c25cSSimon Glass 422fa80c25cSSimon Glass Args: 423fa80c25cSSimon Glass auto_resize: Resize the device tree automatically if it does not 424fa80c25cSSimon Glass have enough space for the update 425fa80c25cSSimon Glass 426fa80c25cSSimon Glass Raises: 427fa80c25cSSimon Glass FdtException if auto_resize is False and there is not enough space 428fa80c25cSSimon Glass """ 429e21c27afSSimon Glass if self._offset is None: 430e21c27afSSimon Glass # The subnode doesn't exist yet, so add it 431e21c27afSSimon Glass fdt_obj = self._fdt._fdt_obj 432e21c27afSSimon Glass if auto_resize: 433e21c27afSSimon Glass while True: 434e21c27afSSimon Glass offset = fdt_obj.add_subnode(self.parent._offset, self.name, 435e21c27afSSimon Glass (libfdt.NOSPACE,)) 436e21c27afSSimon Glass if offset != -libfdt.NOSPACE: 437e21c27afSSimon Glass break 438e21c27afSSimon Glass fdt_obj.resize(fdt_obj.totalsize() + 1024) 439e21c27afSSimon Glass else: 440e21c27afSSimon Glass offset = fdt_obj.add_subnode(self.parent._offset, self.name) 441e21c27afSSimon Glass self._offset = offset 442e21c27afSSimon Glass 443fa80c25cSSimon Glass # Sync subnodes in reverse so that we don't disturb node offsets for 444fa80c25cSSimon Glass # nodes that are earlier in the DT. This avoids an O(n^2) rescan of 445fa80c25cSSimon Glass # node offsets. 446fa80c25cSSimon Glass for node in reversed(self.subnodes): 447fa80c25cSSimon Glass node.Sync(auto_resize) 448fa80c25cSSimon Glass 449fa80c25cSSimon Glass # Sync properties now, whose offsets should not have been disturbed. 450fa80c25cSSimon Glass # We do this after subnodes, since this disturbs the offsets of these 451fa80c25cSSimon Glass # properties. 452fa80c25cSSimon Glass prop_list = sorted(self.props.values(), key=lambda prop: prop._offset, 453fa80c25cSSimon Glass reverse=True) 454fa80c25cSSimon Glass for prop in prop_list: 455fa80c25cSSimon Glass prop.Sync(auto_resize) 456116adecbSSimon Glass 457116adecbSSimon Glass 458a06a34b2SSimon Glassclass Fdt: 4597b75b448SSimon Glass """Provides simple access to a flat device tree blob using libfdts. 460a06a34b2SSimon Glass 461a06a34b2SSimon Glass Properties: 462a06a34b2SSimon Glass fname: Filename of fdt 463a06a34b2SSimon Glass _root: Root of device tree (a Node object) 464a06a34b2SSimon Glass """ 465a06a34b2SSimon Glass def __init__(self, fname): 466a06a34b2SSimon Glass self._fname = fname 4677b75b448SSimon Glass self._cached_offsets = False 46809264e04SSimon Glass self.phandle_to_node = {} 4697b75b448SSimon Glass if self._fname: 4707b75b448SSimon Glass self._fname = fdt_util.EnsureCompiled(self._fname) 4717b75b448SSimon Glass 4727b75b448SSimon Glass with open(self._fname) as fd: 47396066240SSimon Glass self._fdt_obj = libfdt.Fdt(fd.read()) 474f7a2aeeeSSimon Glass 475746aee3fSSimon Glass @staticmethod 476746aee3fSSimon Glass def FromData(data): 477746aee3fSSimon Glass """Create a new Fdt object from the given data 478746aee3fSSimon Glass 479746aee3fSSimon Glass Args: 480746aee3fSSimon Glass data: Device-tree data blob 481746aee3fSSimon Glass 482746aee3fSSimon Glass Returns: 483746aee3fSSimon Glass Fdt object containing the data 484746aee3fSSimon Glass """ 485746aee3fSSimon Glass fdt = Fdt(None) 486746aee3fSSimon Glass fdt._fdt_obj = libfdt.Fdt(bytearray(data)) 487746aee3fSSimon Glass return fdt 488746aee3fSSimon Glass 48994a7c603SSimon Glass def LookupPhandle(self, phandle): 49094a7c603SSimon Glass """Look up a phandle 49194a7c603SSimon Glass 49294a7c603SSimon Glass Args: 49394a7c603SSimon Glass phandle: Phandle to look up (int) 49494a7c603SSimon Glass 49594a7c603SSimon Glass Returns: 49694a7c603SSimon Glass Node object the phandle points to 49794a7c603SSimon Glass """ 49894a7c603SSimon Glass return self.phandle_to_node.get(phandle) 49994a7c603SSimon Glass 500f7a2aeeeSSimon Glass def Scan(self, root='/'): 501f7a2aeeeSSimon Glass """Scan a device tree, building up a tree of Node objects 502f7a2aeeeSSimon Glass 503f7a2aeeeSSimon Glass This fills in the self._root property 504f7a2aeeeSSimon Glass 505f7a2aeeeSSimon Glass Args: 506f7a2aeeeSSimon Glass root: Ignored 507f7a2aeeeSSimon Glass 508f7a2aeeeSSimon Glass TODO(sjg@chromium.org): Implement the 'root' parameter 509f7a2aeeeSSimon Glass """ 510f9b88b3aSSimon Glass self._cached_offsets = True 511979ab024SSimon Glass self._root = self.Node(self, None, 0, '/', '/') 512f7a2aeeeSSimon Glass self._root.Scan() 513f7a2aeeeSSimon Glass 514f7a2aeeeSSimon Glass def GetRoot(self): 515f7a2aeeeSSimon Glass """Get the root Node of the device tree 516f7a2aeeeSSimon Glass 517f7a2aeeeSSimon Glass Returns: 518f7a2aeeeSSimon Glass The root Node object 519f7a2aeeeSSimon Glass """ 520f7a2aeeeSSimon Glass return self._root 521f7a2aeeeSSimon Glass 522f7a2aeeeSSimon Glass def GetNode(self, path): 523f7a2aeeeSSimon Glass """Look up a node from its path 524f7a2aeeeSSimon Glass 525f7a2aeeeSSimon Glass Args: 526f7a2aeeeSSimon Glass path: Path to look up, e.g. '/microcode/update@0' 527f7a2aeeeSSimon Glass Returns: 528f7a2aeeeSSimon Glass Node object, or None if not found 529f7a2aeeeSSimon Glass """ 530f7a2aeeeSSimon Glass node = self._root 531b9066ffcSSimon Glass parts = path.split('/') 532b9066ffcSSimon Glass if len(parts) < 2: 533b9066ffcSSimon Glass return None 534b9066ffcSSimon Glass for part in parts[1:]: 5351d85888cSSimon Glass node = node.FindNode(part) 536f7a2aeeeSSimon Glass if not node: 537f7a2aeeeSSimon Glass return None 538f7a2aeeeSSimon Glass return node 539f7a2aeeeSSimon Glass 540da5f7499SSimon Glass def Flush(self): 541da5f7499SSimon Glass """Flush device tree changes back to the file 542da5f7499SSimon Glass 543da5f7499SSimon Glass If the device tree has changed in memory, write it back to the file. 544da5f7499SSimon Glass """ 5457b75b448SSimon Glass with open(self._fname, 'wb') as fd: 54696066240SSimon Glass fd.write(self._fdt_obj.as_bytearray()) 547da5f7499SSimon Glass 548fa80c25cSSimon Glass def Sync(self, auto_resize=False): 549fa80c25cSSimon Glass """Make sure any DT changes are written to the blob 550fa80c25cSSimon Glass 551fa80c25cSSimon Glass Args: 552fa80c25cSSimon Glass auto_resize: Resize the device tree automatically if it does not 553fa80c25cSSimon Glass have enough space for the update 554fa80c25cSSimon Glass 555fa80c25cSSimon Glass Raises: 556fa80c25cSSimon Glass FdtException if auto_resize is False and there is not enough space 557fa80c25cSSimon Glass """ 558fa80c25cSSimon Glass self._root.Sync(auto_resize) 559fa80c25cSSimon Glass self.Invalidate() 560fa80c25cSSimon Glass 561da5f7499SSimon Glass def Pack(self): 562da5f7499SSimon Glass """Pack the device tree down to its minimum size 563da5f7499SSimon Glass 564da5f7499SSimon Glass When nodes and properties shrink or are deleted, wasted space can 5657b75b448SSimon Glass build up in the device tree binary. 566da5f7499SSimon Glass """ 567117f57b7SSimon Glass CheckErr(self._fdt_obj.pack(), 'pack') 568117f57b7SSimon Glass self.Invalidate() 5697b75b448SSimon Glass 57096066240SSimon Glass def GetContents(self): 5717b75b448SSimon Glass """Get the contents of the FDT 5727b75b448SSimon Glass 5737b75b448SSimon Glass Returns: 5747b75b448SSimon Glass The FDT contents as a string of bytes 5757b75b448SSimon Glass """ 57696066240SSimon Glass return self._fdt_obj.as_bytearray() 5777b75b448SSimon Glass 5782ba98753SSimon Glass def GetFdtObj(self): 5792ba98753SSimon Glass """Get the contents of the FDT 5802ba98753SSimon Glass 5812ba98753SSimon Glass Returns: 5822ba98753SSimon Glass The FDT contents as a libfdt.Fdt object 5832ba98753SSimon Glass """ 5842ba98753SSimon Glass return self._fdt_obj 5852ba98753SSimon Glass 5867b75b448SSimon Glass def GetProps(self, node): 5877b75b448SSimon Glass """Get all properties from a node. 5887b75b448SSimon Glass 5897b75b448SSimon Glass Args: 5907b75b448SSimon Glass node: Full path to node name to look in. 5917b75b448SSimon Glass 5927b75b448SSimon Glass Returns: 5937b75b448SSimon Glass A dictionary containing all the properties, indexed by node name. 5947b75b448SSimon Glass The entries are Prop objects. 5957b75b448SSimon Glass 5967b75b448SSimon Glass Raises: 5977b75b448SSimon Glass ValueError: if the node does not exist. 5987b75b448SSimon Glass """ 5997b75b448SSimon Glass props_dict = {} 600117f57b7SSimon Glass poffset = self._fdt_obj.first_property_offset(node._offset, 601117f57b7SSimon Glass QUIET_NOTFOUND) 6027b75b448SSimon Glass while poffset >= 0: 6037b75b448SSimon Glass p = self._fdt_obj.get_property_by_offset(poffset) 6043def0cf2SSimon Glass prop = Prop(node, poffset, p.name, p) 6057b75b448SSimon Glass props_dict[prop.name] = prop 6067b75b448SSimon Glass 607117f57b7SSimon Glass poffset = self._fdt_obj.next_property_offset(poffset, 608117f57b7SSimon Glass QUIET_NOTFOUND) 6097b75b448SSimon Glass return props_dict 6107b75b448SSimon Glass 6117b75b448SSimon Glass def Invalidate(self): 6127b75b448SSimon Glass """Mark our offset cache as invalid""" 6137b75b448SSimon Glass self._cached_offsets = False 6147b75b448SSimon Glass 6157b75b448SSimon Glass def CheckCache(self): 6167b75b448SSimon Glass """Refresh the offset cache if needed""" 6177b75b448SSimon Glass if self._cached_offsets: 6187b75b448SSimon Glass return 6197b75b448SSimon Glass self.Refresh() 6207b75b448SSimon Glass self._cached_offsets = True 6217b75b448SSimon Glass 6227b75b448SSimon Glass def Refresh(self): 6237b75b448SSimon Glass """Refresh the offset cache""" 6247b75b448SSimon Glass self._root.Refresh(0) 6257b75b448SSimon Glass 6267b75b448SSimon Glass def GetStructOffset(self, offset): 6277b75b448SSimon Glass """Get the file offset of a given struct offset 6287b75b448SSimon Glass 6297b75b448SSimon Glass Args: 6307b75b448SSimon Glass offset: Offset within the 'struct' region of the device tree 6317b75b448SSimon Glass Returns: 6327b75b448SSimon Glass Position of @offset within the device tree binary 6337b75b448SSimon Glass """ 634117f57b7SSimon Glass return self._fdt_obj.off_dt_struct() + offset 6357b75b448SSimon Glass 6367b75b448SSimon Glass @classmethod 637979ab024SSimon Glass def Node(self, fdt, parent, offset, name, path): 6387b75b448SSimon Glass """Create a new node 6397b75b448SSimon Glass 6407b75b448SSimon Glass This is used by Fdt.Scan() to create a new node using the correct 6417b75b448SSimon Glass class. 6427b75b448SSimon Glass 6437b75b448SSimon Glass Args: 6447b75b448SSimon Glass fdt: Fdt object 645979ab024SSimon Glass parent: Parent node, or None if this is the root node 6467b75b448SSimon Glass offset: Offset of node 6477b75b448SSimon Glass name: Node name 6487b75b448SSimon Glass path: Full path to node 6497b75b448SSimon Glass """ 650979ab024SSimon Glass node = Node(fdt, parent, offset, name, path) 6517b75b448SSimon Glass return node 65299ed4a2eSSimon Glass 65399ed4a2eSSimon Glassdef FdtScan(fname): 654dfe5f5b9SSimon Glass """Returns a new Fdt object""" 65599ed4a2eSSimon Glass dtb = Fdt(fname) 65699ed4a2eSSimon Glass dtb.Scan() 65799ed4a2eSSimon Glass return dtb 658