xref: /rk3399_rockchip-uboot/tools/dtoc/fdt.py (revision 5a30597af76537e0d23456af5cf53699741f73b5)
1a06a34b2SSimon Glass#!/usr/bin/python
2a06a34b2SSimon Glass#
3a06a34b2SSimon Glass# Copyright (C) 2016 Google, Inc
4a06a34b2SSimon Glass# Written by Simon Glass <sjg@chromium.org>
5a06a34b2SSimon Glass#
6a06a34b2SSimon Glass# SPDX-License-Identifier:      GPL-2.0+
7a06a34b2SSimon Glass#
8a06a34b2SSimon Glass
9a06a34b2SSimon Glassimport struct
10a06a34b2SSimon Glassimport sys
11a06a34b2SSimon Glass
12a06a34b2SSimon Glassimport fdt_util
137b75b448SSimon Glassimport libfdt
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
24ef2715f4SSimon 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)
467b75b448SSimon Glass        if not bytes:
477b75b448SSimon Glass            self.type = TYPE_BOOL
487b75b448SSimon Glass            self.value = True
497b75b448SSimon Glass            return
507b75b448SSimon Glass        self.type, self.value = self.BytesToValue(bytes)
51a06a34b2SSimon Glass
52c322a850SSimon Glass    def GetPhandle(self):
53c322a850SSimon Glass        """Get a (single) phandle value from a property
54c322a850SSimon Glass
55c322a850SSimon Glass        Gets the phandle valuie from a property and returns it as an integer
56c322a850SSimon Glass        """
57c322a850SSimon Glass        return fdt_util.fdt32_to_cpu(self.value[:4])
58c322a850SSimon Glass
59c322a850SSimon Glass    def Widen(self, newprop):
60c322a850SSimon Glass        """Figure out which property type is more general
61c322a850SSimon Glass
62c322a850SSimon Glass        Given a current property and a new property, this function returns the
63c322a850SSimon Glass        one that is less specific as to type. The less specific property will
64c322a850SSimon Glass        be ble to represent the data in the more specific property. This is
65c322a850SSimon Glass        used for things like:
66c322a850SSimon Glass
67c322a850SSimon Glass            node1 {
68c322a850SSimon Glass                compatible = "fred";
69c322a850SSimon Glass                value = <1>;
70c322a850SSimon Glass            };
71c322a850SSimon Glass            node1 {
72c322a850SSimon Glass                compatible = "fred";
73c322a850SSimon Glass                value = <1 2>;
74c322a850SSimon Glass            };
75c322a850SSimon Glass
76c322a850SSimon Glass        He we want to use an int array for 'value'. The first property
77c322a850SSimon Glass        suggests that a single int is enough, but the second one shows that
78c322a850SSimon Glass        it is not. Calling this function with these two propertes would
79c322a850SSimon Glass        update the current property to be like the second, since it is less
80c322a850SSimon Glass        specific.
81c322a850SSimon Glass        """
82c322a850SSimon Glass        if newprop.type < self.type:
83c322a850SSimon Glass            self.type = newprop.type
84c322a850SSimon Glass
85c322a850SSimon Glass        if type(newprop.value) == list and type(self.value) != list:
86c322a850SSimon Glass            self.value = [self.value]
87c322a850SSimon Glass
88c322a850SSimon Glass        if type(self.value) == list and len(newprop.value) > len(self.value):
89c322a850SSimon Glass            val = self.GetEmpty(self.type)
90c322a850SSimon Glass            while len(self.value) < len(newprop.value):
91c322a850SSimon Glass                self.value.append(val)
92c322a850SSimon Glass
93bc1dea36SSimon Glass    def BytesToValue(self, bytes):
94bc1dea36SSimon Glass        """Converts a string of bytes into a type and value
95bc1dea36SSimon Glass
96bc1dea36SSimon Glass        Args:
97bc1dea36SSimon Glass            A string containing bytes
98bc1dea36SSimon Glass
99bc1dea36SSimon Glass        Return:
100bc1dea36SSimon Glass            A tuple:
101bc1dea36SSimon Glass                Type of data
102bc1dea36SSimon Glass                Data, either a single element or a list of elements. Each element
103bc1dea36SSimon Glass                is one of:
104bc1dea36SSimon Glass                    TYPE_STRING: string value from the property
105bc1dea36SSimon Glass                    TYPE_INT: a byte-swapped integer stored as a 4-byte string
106bc1dea36SSimon Glass                    TYPE_BYTE: a byte stored as a single-byte string
107bc1dea36SSimon Glass        """
108b4360206SSimon Glass        bytes = str(bytes)
109bc1dea36SSimon Glass        size = len(bytes)
110bc1dea36SSimon Glass        strings = bytes.split('\0')
111bc1dea36SSimon Glass        is_string = True
112bc1dea36SSimon Glass        count = len(strings) - 1
113bc1dea36SSimon Glass        if count > 0 and not strings[-1]:
114bc1dea36SSimon Glass            for string in strings[:-1]:
115bc1dea36SSimon Glass                if not string:
116bc1dea36SSimon Glass                    is_string = False
117bc1dea36SSimon Glass                    break
118bc1dea36SSimon Glass                for ch in string:
119bc1dea36SSimon Glass                    if ch < ' ' or ch > '~':
120bc1dea36SSimon Glass                        is_string = False
121bc1dea36SSimon Glass                        break
122bc1dea36SSimon Glass        else:
123bc1dea36SSimon Glass            is_string = False
124bc1dea36SSimon Glass        if is_string:
125bc1dea36SSimon Glass            if count == 1:
126bc1dea36SSimon Glass                return TYPE_STRING, strings[0]
127bc1dea36SSimon Glass            else:
128bc1dea36SSimon Glass                return TYPE_STRING, strings[:-1]
129bc1dea36SSimon Glass        if size % 4:
130bc1dea36SSimon Glass            if size == 1:
131bc1dea36SSimon Glass                return TYPE_BYTE, bytes[0]
132bc1dea36SSimon Glass            else:
133bc1dea36SSimon Glass                return TYPE_BYTE, list(bytes)
134bc1dea36SSimon Glass        val = []
135bc1dea36SSimon Glass        for i in range(0, size, 4):
136bc1dea36SSimon Glass            val.append(bytes[i:i + 4])
137bc1dea36SSimon Glass        if size == 4:
138bc1dea36SSimon Glass            return TYPE_INT, val[0]
139bc1dea36SSimon Glass        else:
140bc1dea36SSimon Glass            return TYPE_INT, val
141bc1dea36SSimon Glass
142bc1dea36SSimon Glass    def GetEmpty(self, type):
143bc1dea36SSimon Glass        """Get an empty / zero value of the given type
144bc1dea36SSimon Glass
145bc1dea36SSimon Glass        Returns:
146bc1dea36SSimon Glass            A single value of the given type
147bc1dea36SSimon Glass        """
148bc1dea36SSimon Glass        if type == TYPE_BYTE:
149bc1dea36SSimon Glass            return chr(0)
150bc1dea36SSimon Glass        elif type == TYPE_INT:
151bc1dea36SSimon Glass            return struct.pack('<I', 0);
152bc1dea36SSimon Glass        elif type == TYPE_STRING:
153bc1dea36SSimon Glass            return ''
154bc1dea36SSimon Glass        else:
155bc1dea36SSimon Glass            return True
156bc1dea36SSimon Glass
157babdbde6SSimon Glass    def GetOffset(self):
158babdbde6SSimon Glass        """Get the offset of a property
159babdbde6SSimon Glass
160babdbde6SSimon Glass        Returns:
1617b75b448SSimon Glass            The offset of the property (struct fdt_property) within the file
162babdbde6SSimon Glass        """
1637b75b448SSimon Glass        return self._node._fdt.GetStructOffset(self._offset)
164babdbde6SSimon Glass
1657b75b448SSimon Glassclass Node:
166a06a34b2SSimon Glass    """A device tree node
167a06a34b2SSimon Glass
168a06a34b2SSimon Glass    Properties:
169a06a34b2SSimon Glass        offset: Integer offset in the device tree
170a06a34b2SSimon Glass        name: Device tree node tname
171a06a34b2SSimon Glass        path: Full path to node, along with the node name itself
172a06a34b2SSimon Glass        _fdt: Device tree object
173a06a34b2SSimon Glass        subnodes: A list of subnodes for this node, each a Node object
174a06a34b2SSimon Glass        props: A dict of properties for this node, each a Prop object.
175a06a34b2SSimon Glass            Keyed by property name
176a06a34b2SSimon Glass    """
177f08a0424SSimon Glass    def __init__(self, fdt, parent, offset, name, path):
178a06a34b2SSimon Glass        self._fdt = fdt
179f08a0424SSimon Glass        self.parent = parent
180a06a34b2SSimon Glass        self._offset = offset
181a06a34b2SSimon Glass        self.name = name
182a06a34b2SSimon Glass        self.path = path
183a06a34b2SSimon Glass        self.subnodes = []
184a06a34b2SSimon Glass        self.props = {}
185a06a34b2SSimon Glass
186f7a2aeeeSSimon Glass    def _FindNode(self, name):
187f7a2aeeeSSimon Glass        """Find a node given its name
188f7a2aeeeSSimon Glass
189f7a2aeeeSSimon Glass        Args:
190f7a2aeeeSSimon Glass            name: Node name to look for
191f7a2aeeeSSimon Glass        Returns:
192f7a2aeeeSSimon Glass            Node object if found, else None
193f7a2aeeeSSimon Glass        """
194f7a2aeeeSSimon Glass        for subnode in self.subnodes:
195f7a2aeeeSSimon Glass            if subnode.name == name:
196f7a2aeeeSSimon Glass                return subnode
197f7a2aeeeSSimon Glass        return None
198f7a2aeeeSSimon Glass
1997b75b448SSimon Glass    def Offset(self):
2007b75b448SSimon Glass        """Returns the offset of a node, after checking the cache
201f7a2aeeeSSimon Glass
2027b75b448SSimon Glass        This should be used instead of self._offset directly, to ensure that
2037b75b448SSimon Glass        the cache does not contain invalid offsets.
204f7a2aeeeSSimon Glass        """
2057b75b448SSimon Glass        self._fdt.CheckCache()
2067b75b448SSimon Glass        return self._offset
2077b75b448SSimon Glass
2087b75b448SSimon Glass    def Scan(self):
2097b75b448SSimon Glass        """Scan a node's properties and subnodes
2107b75b448SSimon Glass
2117b75b448SSimon Glass        This fills in the props and subnodes properties, recursively
2127b75b448SSimon Glass        searching into subnodes so that the entire tree is built.
2137b75b448SSimon Glass        """
2147b75b448SSimon Glass        self.props = self._fdt.GetProps(self)
215*5a30597aSSimon Glass        phandle = self.props.get('phandle')
216*5a30597aSSimon Glass        if phandle:
217*5a30597aSSimon Glass            val = fdt_util.fdt32_to_cpu(phandle.value)
218*5a30597aSSimon Glass            self._fdt.phandle_to_node[val] = self
2197b75b448SSimon Glass
2207b75b448SSimon Glass        offset = libfdt.fdt_first_subnode(self._fdt.GetFdt(), self.Offset())
2217b75b448SSimon Glass        while offset >= 0:
2227b75b448SSimon Glass            sep = '' if self.path[-1] == '/' else '/'
2237b75b448SSimon Glass            name = self._fdt._fdt_obj.get_name(offset)
2247b75b448SSimon Glass            path = self.path + sep + name
225f08a0424SSimon Glass            node = Node(self._fdt, self, offset, name, path)
2267b75b448SSimon Glass            self.subnodes.append(node)
2277b75b448SSimon Glass
2287b75b448SSimon Glass            node.Scan()
2297b75b448SSimon Glass            offset = libfdt.fdt_next_subnode(self._fdt.GetFdt(), offset)
2307b75b448SSimon Glass
2317b75b448SSimon Glass    def Refresh(self, my_offset):
2327b75b448SSimon Glass        """Fix up the _offset for each node, recursively
2337b75b448SSimon Glass
2347b75b448SSimon Glass        Note: This does not take account of property offsets - these will not
2357b75b448SSimon Glass        be updated.
2367b75b448SSimon Glass        """
2377b75b448SSimon Glass        if self._offset != my_offset:
2387b75b448SSimon Glass            #print '%s: %d -> %d\n' % (self.path, self._offset, my_offset)
2397b75b448SSimon Glass            self._offset = my_offset
2407b75b448SSimon Glass        offset = libfdt.fdt_first_subnode(self._fdt.GetFdt(), self._offset)
2417b75b448SSimon Glass        for subnode in self.subnodes:
2427b75b448SSimon Glass            subnode.Refresh(offset)
2437b75b448SSimon Glass            offset = libfdt.fdt_next_subnode(self._fdt.GetFdt(), offset)
244f7a2aeeeSSimon Glass
2452a70d897SSimon Glass    def DeleteProp(self, prop_name):
2462a70d897SSimon Glass        """Delete a property of a node
2472a70d897SSimon Glass
2487b75b448SSimon Glass        The property is deleted and the offset cache is invalidated.
2492a70d897SSimon Glass
2502a70d897SSimon Glass        Args:
2512a70d897SSimon Glass            prop_name: Name of the property to delete
2527b75b448SSimon Glass        Raises:
2537b75b448SSimon Glass            ValueError if the property does not exist
2542a70d897SSimon Glass        """
2557b75b448SSimon Glass        CheckErr(libfdt.fdt_delprop(self._fdt.GetFdt(), self.Offset(), prop_name),
2567b75b448SSimon Glass                 "Node '%s': delete property: '%s'" % (self.path, prop_name))
2577b75b448SSimon Glass        del self.props[prop_name]
2587b75b448SSimon Glass        self._fdt.Invalidate()
2592a70d897SSimon Glass
260a06a34b2SSimon Glassclass Fdt:
2617b75b448SSimon Glass    """Provides simple access to a flat device tree blob using libfdts.
262a06a34b2SSimon Glass
263a06a34b2SSimon Glass    Properties:
264a06a34b2SSimon Glass      fname: Filename of fdt
265a06a34b2SSimon Glass      _root: Root of device tree (a Node object)
266a06a34b2SSimon Glass    """
267a06a34b2SSimon Glass    def __init__(self, fname):
268a06a34b2SSimon Glass        self._fname = fname
2697b75b448SSimon Glass        self._cached_offsets = False
270*5a30597aSSimon Glass        self.phandle_to_node = {}
2717b75b448SSimon Glass        if self._fname:
2727b75b448SSimon Glass            self._fname = fdt_util.EnsureCompiled(self._fname)
2737b75b448SSimon Glass
2747b75b448SSimon Glass            with open(self._fname) as fd:
2757b75b448SSimon Glass                self._fdt = bytearray(fd.read())
2767b75b448SSimon Glass                self._fdt_obj = libfdt.Fdt(self._fdt)
277f7a2aeeeSSimon Glass
278f7a2aeeeSSimon Glass    def Scan(self, root='/'):
279f7a2aeeeSSimon Glass        """Scan a device tree, building up a tree of Node objects
280f7a2aeeeSSimon Glass
281f7a2aeeeSSimon Glass        This fills in the self._root property
282f7a2aeeeSSimon Glass
283f7a2aeeeSSimon Glass        Args:
284f7a2aeeeSSimon Glass            root: Ignored
285f7a2aeeeSSimon Glass
286f7a2aeeeSSimon Glass        TODO(sjg@chromium.org): Implement the 'root' parameter
287f7a2aeeeSSimon Glass        """
288f08a0424SSimon Glass        self._root = self.Node(self, None, 0, '/', '/')
289f7a2aeeeSSimon Glass        self._root.Scan()
290f7a2aeeeSSimon Glass
291f7a2aeeeSSimon Glass    def GetRoot(self):
292f7a2aeeeSSimon Glass        """Get the root Node of the device tree
293f7a2aeeeSSimon Glass
294f7a2aeeeSSimon Glass        Returns:
295f7a2aeeeSSimon Glass            The root Node object
296f7a2aeeeSSimon Glass        """
297f7a2aeeeSSimon Glass        return self._root
298f7a2aeeeSSimon Glass
299f7a2aeeeSSimon Glass    def GetNode(self, path):
300f7a2aeeeSSimon Glass        """Look up a node from its path
301f7a2aeeeSSimon Glass
302f7a2aeeeSSimon Glass        Args:
303f7a2aeeeSSimon Glass            path: Path to look up, e.g. '/microcode/update@0'
304f7a2aeeeSSimon Glass        Returns:
305f7a2aeeeSSimon Glass            Node object, or None if not found
306f7a2aeeeSSimon Glass        """
307f7a2aeeeSSimon Glass        node = self._root
308f7a2aeeeSSimon Glass        for part in path.split('/')[1:]:
309f7a2aeeeSSimon Glass            node = node._FindNode(part)
310f7a2aeeeSSimon Glass            if not node:
311f7a2aeeeSSimon Glass                return None
312f7a2aeeeSSimon Glass        return node
313f7a2aeeeSSimon Glass
314da5f7499SSimon Glass    def Flush(self):
315da5f7499SSimon Glass        """Flush device tree changes back to the file
316da5f7499SSimon Glass
317da5f7499SSimon Glass        If the device tree has changed in memory, write it back to the file.
318da5f7499SSimon Glass        """
3197b75b448SSimon Glass        with open(self._fname, 'wb') as fd:
3207b75b448SSimon Glass            fd.write(self._fdt)
321da5f7499SSimon Glass
322da5f7499SSimon Glass    def Pack(self):
323da5f7499SSimon Glass        """Pack the device tree down to its minimum size
324da5f7499SSimon Glass
325da5f7499SSimon Glass        When nodes and properties shrink or are deleted, wasted space can
3267b75b448SSimon Glass        build up in the device tree binary.
327da5f7499SSimon Glass        """
3287b75b448SSimon Glass        CheckErr(libfdt.fdt_pack(self._fdt), 'pack')
3297b75b448SSimon Glass        fdt_len = libfdt.fdt_totalsize(self._fdt)
3307b75b448SSimon Glass        del self._fdt[fdt_len:]
3317b75b448SSimon Glass
3327b75b448SSimon Glass    def GetFdt(self):
3337b75b448SSimon Glass        """Get the contents of the FDT
3347b75b448SSimon Glass
3357b75b448SSimon Glass        Returns:
3367b75b448SSimon Glass            The FDT contents as a string of bytes
3377b75b448SSimon Glass        """
3387b75b448SSimon Glass        return self._fdt
3397b75b448SSimon Glass
3407b75b448SSimon Glass    def CheckErr(errnum, msg):
3417b75b448SSimon Glass        if errnum:
3427b75b448SSimon Glass            raise ValueError('Error %d: %s: %s' %
3437b75b448SSimon Glass                (errnum, libfdt.fdt_strerror(errnum), msg))
3447b75b448SSimon Glass
3457b75b448SSimon Glass
3467b75b448SSimon Glass    def GetProps(self, node):
3477b75b448SSimon Glass        """Get all properties from a node.
3487b75b448SSimon Glass
3497b75b448SSimon Glass        Args:
3507b75b448SSimon Glass            node: Full path to node name to look in.
3517b75b448SSimon Glass
3527b75b448SSimon Glass        Returns:
3537b75b448SSimon Glass            A dictionary containing all the properties, indexed by node name.
3547b75b448SSimon Glass            The entries are Prop objects.
3557b75b448SSimon Glass
3567b75b448SSimon Glass        Raises:
3577b75b448SSimon Glass            ValueError: if the node does not exist.
3587b75b448SSimon Glass        """
3597b75b448SSimon Glass        props_dict = {}
3607b75b448SSimon Glass        poffset = libfdt.fdt_first_property_offset(self._fdt, node._offset)
3617b75b448SSimon Glass        while poffset >= 0:
3627b75b448SSimon Glass            p = self._fdt_obj.get_property_by_offset(poffset)
3637b75b448SSimon Glass            prop = Prop(node, poffset, p.name, p.value)
3647b75b448SSimon Glass            props_dict[prop.name] = prop
3657b75b448SSimon Glass
3667b75b448SSimon Glass            poffset = libfdt.fdt_next_property_offset(self._fdt, poffset)
3677b75b448SSimon Glass        return props_dict
3687b75b448SSimon Glass
3697b75b448SSimon Glass    def Invalidate(self):
3707b75b448SSimon Glass        """Mark our offset cache as invalid"""
3717b75b448SSimon Glass        self._cached_offsets = False
3727b75b448SSimon Glass
3737b75b448SSimon Glass    def CheckCache(self):
3747b75b448SSimon Glass        """Refresh the offset cache if needed"""
3757b75b448SSimon Glass        if self._cached_offsets:
3767b75b448SSimon Glass            return
3777b75b448SSimon Glass        self.Refresh()
3787b75b448SSimon Glass        self._cached_offsets = True
3797b75b448SSimon Glass
3807b75b448SSimon Glass    def Refresh(self):
3817b75b448SSimon Glass        """Refresh the offset cache"""
3827b75b448SSimon Glass        self._root.Refresh(0)
3837b75b448SSimon Glass
3847b75b448SSimon Glass    def GetStructOffset(self, offset):
3857b75b448SSimon Glass        """Get the file offset of a given struct offset
3867b75b448SSimon Glass
3877b75b448SSimon Glass        Args:
3887b75b448SSimon Glass            offset: Offset within the 'struct' region of the device tree
3897b75b448SSimon Glass        Returns:
3907b75b448SSimon Glass            Position of @offset within the device tree binary
3917b75b448SSimon Glass        """
3927b75b448SSimon Glass        return libfdt.fdt_off_dt_struct(self._fdt) + offset
3937b75b448SSimon Glass
3947b75b448SSimon Glass    @classmethod
395f08a0424SSimon Glass    def Node(self, fdt, parent, offset, name, path):
3967b75b448SSimon Glass        """Create a new node
3977b75b448SSimon Glass
3987b75b448SSimon Glass        This is used by Fdt.Scan() to create a new node using the correct
3997b75b448SSimon Glass        class.
4007b75b448SSimon Glass
4017b75b448SSimon Glass        Args:
4027b75b448SSimon Glass            fdt: Fdt object
403f08a0424SSimon Glass            parent: Parent node, or None if this is the root node
4047b75b448SSimon Glass            offset: Offset of node
4057b75b448SSimon Glass            name: Node name
4067b75b448SSimon Glass            path: Full path to node
4077b75b448SSimon Glass        """
408f08a0424SSimon Glass        node = Node(fdt, parent, offset, name, path)
4097b75b448SSimon Glass        return node
41099ed4a2eSSimon Glass
41199ed4a2eSSimon Glassdef FdtScan(fname):
41299ed4a2eSSimon Glass    """Returns a new Fdt object from the implementation we are using"""
41399ed4a2eSSimon Glass    dtb = Fdt(fname)
41499ed4a2eSSimon Glass    dtb.Scan()
41599ed4a2eSSimon Glass    return dtb
416