xref: /openbmc/u-boot/tools/dtoc/fdt.py (revision cf033e04)
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