114151Sgiacomo.travaglini@arm.com# Copyright (c) 2016,2019 ARM Limited
212469Sglenn.bergmans@arm.com# All rights reserved.
312469Sglenn.bergmans@arm.com#
412469Sglenn.bergmans@arm.com# The license below extends only to copyright in the software and shall
512469Sglenn.bergmans@arm.com# not be construed as granting a license to any other intellectual
612469Sglenn.bergmans@arm.com# property including but not limited to intellectual property relating
712469Sglenn.bergmans@arm.com# to a hardware implementation of the functionality of the software
812469Sglenn.bergmans@arm.com# licensed hereunder.  You may use the software subject to the license
912469Sglenn.bergmans@arm.com# terms below provided that you ensure that this notice is replicated
1012469Sglenn.bergmans@arm.com# unmodified and in its entirety in all distributions of the software,
1112469Sglenn.bergmans@arm.com# modified or unmodified, in source code or in binary form.
1212469Sglenn.bergmans@arm.com#
1312469Sglenn.bergmans@arm.com# Redistribution and use in source and binary forms, with or without
1412469Sglenn.bergmans@arm.com# modification, are permitted provided that the following conditions are
1512469Sglenn.bergmans@arm.com# met: redistributions of source code must retain the above copyright
1612469Sglenn.bergmans@arm.com# notice, this list of conditions and the following disclaimer;
1712469Sglenn.bergmans@arm.com# redistributions in binary form must reproduce the above copyright
1812469Sglenn.bergmans@arm.com# notice, this list of conditions and the following disclaimer in the
1912469Sglenn.bergmans@arm.com# documentation and/or other materials provided with the distribution;
2012469Sglenn.bergmans@arm.com# neither the name of the copyright holders nor the names of its
2112469Sglenn.bergmans@arm.com# contributors may be used to endorse or promote products derived from
2212469Sglenn.bergmans@arm.com# this software without specific prior written permission.
2312469Sglenn.bergmans@arm.com#
2412469Sglenn.bergmans@arm.com# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
2512469Sglenn.bergmans@arm.com# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
2612469Sglenn.bergmans@arm.com# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
2712469Sglenn.bergmans@arm.com# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
2812469Sglenn.bergmans@arm.com# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
2912469Sglenn.bergmans@arm.com# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
3012469Sglenn.bergmans@arm.com# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
3112469Sglenn.bergmans@arm.com# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
3212469Sglenn.bergmans@arm.com# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3312469Sglenn.bergmans@arm.com# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
3412469Sglenn.bergmans@arm.com# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3512469Sglenn.bergmans@arm.com#
3612469Sglenn.bergmans@arm.com# Author: Glenn Bergmans
3712469Sglenn.bergmans@arm.com
3813719Sandreas.sandberg@arm.comimport six
3913719Sandreas.sandberg@arm.comif six.PY3:
4013719Sandreas.sandberg@arm.com    long = int
4113719Sandreas.sandberg@arm.com
4212469Sglenn.bergmans@arm.comfrom m5.ext.pyfdt import pyfdt
4312469Sglenn.bergmans@arm.comimport re
4412469Sglenn.bergmans@arm.comimport os
4512469Sglenn.bergmans@arm.comfrom m5.SimObject import SimObject
4612469Sglenn.bergmans@arm.com
4712469Sglenn.bergmans@arm.comclass FdtProperty(pyfdt.FdtProperty):
4812469Sglenn.bergmans@arm.com    """Create a property without values."""
4912469Sglenn.bergmans@arm.com    pass
5012469Sglenn.bergmans@arm.com
5112469Sglenn.bergmans@arm.comclass FdtPropertyWords(pyfdt.FdtPropertyWords):
5212469Sglenn.bergmans@arm.com    """Create a property with word (32-bit unsigned) values."""
5312469Sglenn.bergmans@arm.com    def __init__(self, name, words):
5412469Sglenn.bergmans@arm.com        if type(words) != list:
5512469Sglenn.bergmans@arm.com            words = [words]
5612469Sglenn.bergmans@arm.com        # Make sure all values are ints (use automatic base detection if the
5712469Sglenn.bergmans@arm.com        # type is str)
5812469Sglenn.bergmans@arm.com        words = [long(w, base=0) if type(w) == str else long(w) for w in words]
5912469Sglenn.bergmans@arm.com        super(FdtPropertyWords, self).__init__(name, words)
6012469Sglenn.bergmans@arm.com
6112469Sglenn.bergmans@arm.comclass FdtPropertyStrings(pyfdt.FdtPropertyStrings):
6212469Sglenn.bergmans@arm.com    """Create a property with string values."""
6312469Sglenn.bergmans@arm.com
6412469Sglenn.bergmans@arm.com    def __init__(self, name, strings):
6512469Sglenn.bergmans@arm.com        if type(strings) == str:
6612469Sglenn.bergmans@arm.com            strings = [strings]
6712469Sglenn.bergmans@arm.com        strings = [str(string) for string in strings] # Make all values strings
6812469Sglenn.bergmans@arm.com        super(FdtPropertyStrings, self).__init__(name, strings)
6912469Sglenn.bergmans@arm.com
7012469Sglenn.bergmans@arm.comclass FdtPropertyBytes(pyfdt.FdtPropertyBytes):
7112469Sglenn.bergmans@arm.com    """Create a property with integer (8-bit signed) values."""
7212469Sglenn.bergmans@arm.com
7312469Sglenn.bergmans@arm.com    def __init__(self, name, values):
7412469Sglenn.bergmans@arm.com        if type(values) != list:
7512469Sglenn.bergmans@arm.com            values = [values]
7612469Sglenn.bergmans@arm.com        # Make sure all values are ints (use automatic base detection if the
7712469Sglenn.bergmans@arm.com        # type is str)
7812469Sglenn.bergmans@arm.com        values = [int(v, base=0)
7912469Sglenn.bergmans@arm.com                   if isinstance(v, str) else int(v) for v in values]
8012469Sglenn.bergmans@arm.com        super(FdtPropertyBytes, self).__init__(name, values)
8112469Sglenn.bergmans@arm.com
8212469Sglenn.bergmans@arm.comclass FdtState(object):
8312469Sglenn.bergmans@arm.com    """Class for maintaining state while recursively generating a flattened
8412469Sglenn.bergmans@arm.com    device tree. The state tracks address, size and CPU address cell sizes, and
8512469Sglenn.bergmans@arm.com    maintains a dictionary of allocated phandles."""
8612469Sglenn.bergmans@arm.com
8712469Sglenn.bergmans@arm.com    phandle_counter = 0
8812469Sglenn.bergmans@arm.com    phandles = dict()
8912469Sglenn.bergmans@arm.com
9014151Sgiacomo.travaglini@arm.com    def __init__(self, **kwargs):
9112469Sglenn.bergmans@arm.com        """Instantiate values of this state. The state can only be initialized
9212469Sglenn.bergmans@arm.com        once."""
9312469Sglenn.bergmans@arm.com
9414151Sgiacomo.travaglini@arm.com        self.addr_cells = kwargs.pop('addr_cells', 0)
9514151Sgiacomo.travaglini@arm.com        self.size_cells = kwargs.pop('size_cells', 0)
9614151Sgiacomo.travaglini@arm.com        self.cpu_cells = kwargs.pop('cpu_cells', 0)
9714151Sgiacomo.travaglini@arm.com        self.interrupt_cells = kwargs.pop('interrupt_cells', 0)
9812469Sglenn.bergmans@arm.com
9912469Sglenn.bergmans@arm.com    def phandle(self, obj):
10012469Sglenn.bergmans@arm.com        """Return a unique phandle number for a key. The key can be a SimObject
10112469Sglenn.bergmans@arm.com        or any value that is castable to a string. If the phandle doesn't exist
10212469Sglenn.bergmans@arm.com        a new one is created, otherwise the existing one is returned."""
10312469Sglenn.bergmans@arm.com
10412469Sglenn.bergmans@arm.com        if isinstance(obj, SimObject):
10512469Sglenn.bergmans@arm.com            key = str(id(obj))
10612469Sglenn.bergmans@arm.com        else:
10712469Sglenn.bergmans@arm.com            try:
10812469Sglenn.bergmans@arm.com                key = str(obj)
10912469Sglenn.bergmans@arm.com            except ValueError:
11012469Sglenn.bergmans@arm.com                raise ValueError('Phandle keys must be castable to str')
11112469Sglenn.bergmans@arm.com
11212469Sglenn.bergmans@arm.com        if not key in FdtState.phandles:
11312469Sglenn.bergmans@arm.com            FdtState.phandle_counter += 1
11412469Sglenn.bergmans@arm.com
11512469Sglenn.bergmans@arm.com        return FdtState.phandles.setdefault(key, FdtState.phandle_counter)
11612469Sglenn.bergmans@arm.com
11712469Sglenn.bergmans@arm.com    def resetPhandles(self):
11812469Sglenn.bergmans@arm.com        FdtState.phandle_counter = 0
11912469Sglenn.bergmans@arm.com        FdtState.phandles = dict()
12012469Sglenn.bergmans@arm.com
12112469Sglenn.bergmans@arm.com    def int_to_cells(self, value, cells):
12212469Sglenn.bergmans@arm.com        """Helper function for: generates a list of 32 bit cells from an int,
12312469Sglenn.bergmans@arm.com        used to split up addresses in appropriate 32 bit chunks."""
12412469Sglenn.bergmans@arm.com        value = long(value)
12512469Sglenn.bergmans@arm.com
12612469Sglenn.bergmans@arm.com        if (value >> (32 * cells)) != 0:
12712469Sglenn.bergmans@arm.com            fatal("Value %d doesn't fit in %d cells" % (value, cells))
12812469Sglenn.bergmans@arm.com
12912469Sglenn.bergmans@arm.com        return [(value >> 32*(x-1)) & 0xFFFFFFFF for x in range(cells, 0, -1)]
13012469Sglenn.bergmans@arm.com
13112469Sglenn.bergmans@arm.com    def addrCells(self, addr):
13212469Sglenn.bergmans@arm.com        """Format an integer type according to the address_cells value of this
13312469Sglenn.bergmans@arm.com        state."""
13412469Sglenn.bergmans@arm.com        return self.int_to_cells(addr, self.addr_cells)
13512469Sglenn.bergmans@arm.com
13612469Sglenn.bergmans@arm.com    def CPUAddrCells(self, addr):
13712469Sglenn.bergmans@arm.com        """Format an integer type according to the cpu_cells value of this
13812469Sglenn.bergmans@arm.com        state."""
13912469Sglenn.bergmans@arm.com        return self.int_to_cells(addr, self.cpu_cells)
14012469Sglenn.bergmans@arm.com
14112469Sglenn.bergmans@arm.com    def sizeCells(self, size):
14212469Sglenn.bergmans@arm.com        """Format an integer type according to the size_cells value of this
14312469Sglenn.bergmans@arm.com        state."""
14412469Sglenn.bergmans@arm.com        return self.int_to_cells(size, self.size_cells)
14512469Sglenn.bergmans@arm.com
14614151Sgiacomo.travaglini@arm.com    def interruptCells(self, interrupt):
14714151Sgiacomo.travaglini@arm.com        """Format an integer type according to the interrupt_cells value
14814151Sgiacomo.travaglini@arm.com        of this state."""
14914151Sgiacomo.travaglini@arm.com        return self.int_to_cells(interrupt, self.interrupt_cells)
15014151Sgiacomo.travaglini@arm.com
15112469Sglenn.bergmans@arm.com    def addrCellsProperty(self):
15212469Sglenn.bergmans@arm.com        """Return an #address-cells property with the value of this state."""
15312469Sglenn.bergmans@arm.com        return FdtPropertyWords("#address-cells", self.addr_cells)
15412469Sglenn.bergmans@arm.com
15512469Sglenn.bergmans@arm.com    def sizeCellsProperty(self):
15612469Sglenn.bergmans@arm.com        """Return an #size-cells property with the value of this state."""
15712469Sglenn.bergmans@arm.com        return FdtPropertyWords("#size-cells", self.size_cells)
15812469Sglenn.bergmans@arm.com
15912469Sglenn.bergmans@arm.com    def CPUCellsProperty(self):
16012469Sglenn.bergmans@arm.com        """Return an #address-cells property for cpu nodes with the value
16112469Sglenn.bergmans@arm.com        of this state."""
16212469Sglenn.bergmans@arm.com        return FdtPropertyWords("#address-cells", self.cpu_cells)
16312469Sglenn.bergmans@arm.com
16414151Sgiacomo.travaglini@arm.com    def interruptCellsProperty(self):
16514151Sgiacomo.travaglini@arm.com        """Return an #interrupt-cells property for cpu nodes with the value
16614151Sgiacomo.travaglini@arm.com        of this state."""
16714151Sgiacomo.travaglini@arm.com        return FdtPropertyWords("#interrupt-cells", self.interrupt_cells)
16814151Sgiacomo.travaglini@arm.com
16914151Sgiacomo.travaglini@arm.com
17012469Sglenn.bergmans@arm.comclass FdtNop(pyfdt.FdtNop):
17112469Sglenn.bergmans@arm.com    """Create an empty node."""
17212469Sglenn.bergmans@arm.com    pass
17312469Sglenn.bergmans@arm.com
17412469Sglenn.bergmans@arm.comclass FdtNode(pyfdt.FdtNode):
17512469Sglenn.bergmans@arm.com    def __init__(self, name, obj=None):
17612469Sglenn.bergmans@arm.com        """Create a new node and immediately set the phandle property, if obj
17712469Sglenn.bergmans@arm.com        is supplied"""
17812469Sglenn.bergmans@arm.com        super(FdtNode, self).__init__(name)
17912469Sglenn.bergmans@arm.com        if obj != None:
18012469Sglenn.bergmans@arm.com            self.appendPhandle(obj)
18112469Sglenn.bergmans@arm.com
18212469Sglenn.bergmans@arm.com    def append(self, subnodes):
18312469Sglenn.bergmans@arm.com        """Change the behavior of the normal append to override if a node with
18412469Sglenn.bergmans@arm.com        the same name already exists or merge if the name exists and is a node
18512469Sglenn.bergmans@arm.com        type. Can also take a list of subnodes, that each get appended."""
18612469Sglenn.bergmans@arm.com        if not hasattr(subnodes, '__iter__'):
18712469Sglenn.bergmans@arm.com            subnodes = [subnodes]
18812469Sglenn.bergmans@arm.com
18912469Sglenn.bergmans@arm.com        for subnode in subnodes:
19012469Sglenn.bergmans@arm.com            try:
19112469Sglenn.bergmans@arm.com                if not issubclass(type(subnode), pyfdt.FdtNop):
19212469Sglenn.bergmans@arm.com                    index = self.index(subnode.name)
19312469Sglenn.bergmans@arm.com                    item = self.pop(index)
19412469Sglenn.bergmans@arm.com                else:
19512469Sglenn.bergmans@arm.com                    item = None
19612469Sglenn.bergmans@arm.com            except ValueError:
19712469Sglenn.bergmans@arm.com                item = None
19812469Sglenn.bergmans@arm.com
19912469Sglenn.bergmans@arm.com            if isinstance(item,  pyfdt.FdtNode) and \
20012469Sglenn.bergmans@arm.com               isinstance(subnode,  pyfdt.FdtNode):
20112469Sglenn.bergmans@arm.com                item.merge(subnode)
20212469Sglenn.bergmans@arm.com                subnode = item
20312469Sglenn.bergmans@arm.com
20412469Sglenn.bergmans@arm.com            super(FdtNode, self).append(subnode)
20512469Sglenn.bergmans@arm.com
20612469Sglenn.bergmans@arm.com    def appendList(self, subnode_list):
20712469Sglenn.bergmans@arm.com        """Append all properties/nodes in the iterable."""
20812469Sglenn.bergmans@arm.com        for subnode in subnode_list:
20912469Sglenn.bergmans@arm.com            self.append(subnode)
21012469Sglenn.bergmans@arm.com
21112469Sglenn.bergmans@arm.com    def appendCompatible(self, compatible):
21212469Sglenn.bergmans@arm.com        """Append a compatible property with the supplied compatibility
21312469Sglenn.bergmans@arm.com        strings."""
21412469Sglenn.bergmans@arm.com        if isinstance(compatible, str):
21512469Sglenn.bergmans@arm.com            compatible = [compatible]
21612469Sglenn.bergmans@arm.com        self.append(FdtPropertyStrings('compatible', compatible))
21712469Sglenn.bergmans@arm.com
21812469Sglenn.bergmans@arm.com    def appendPhandle(self, obj):
21912469Sglenn.bergmans@arm.com        """Append a phandle property to this node with the phandle of the
22012469Sglenn.bergmans@arm.com        supplied object."""
22112469Sglenn.bergmans@arm.com        # Create a bogus state because we only need the Phandle dictionary
22212469Sglenn.bergmans@arm.com        state = FdtState(addr_cells=1, size_cells=1, cpu_cells=1)
22312469Sglenn.bergmans@arm.com
22412469Sglenn.bergmans@arm.com        phandle = state.phandle(obj)
22512469Sglenn.bergmans@arm.com        self.append(FdtPropertyWords("phandle", [phandle]))
22612469Sglenn.bergmans@arm.com
22712469Sglenn.bergmans@arm.comclass Fdt(pyfdt.Fdt):
22812469Sglenn.bergmans@arm.com    def sortNodes(self, node):
22912469Sglenn.bergmans@arm.com        """Move all properties to the beginning and subnodes to the end
23012469Sglenn.bergmans@arm.com        while maintaining the order of the subnodes. DTB files require the
23112469Sglenn.bergmans@arm.com        properties to go before the nodes, but the PyFdt doesn't account for
23212469Sglenn.bergmans@arm.com        defining nodes and properties in a random order."""
23312469Sglenn.bergmans@arm.com        properties = FdtNode(node.name)
23412469Sglenn.bergmans@arm.com        subnodes = FdtNode(node.name)
23512469Sglenn.bergmans@arm.com
23612469Sglenn.bergmans@arm.com        while len(node):
23712469Sglenn.bergmans@arm.com            subnode = node.pop(0)
23812469Sglenn.bergmans@arm.com            if issubclass(type(subnode), pyfdt.FdtNode):
23912469Sglenn.bergmans@arm.com                subnode = self.sortNodes(subnode)
24012469Sglenn.bergmans@arm.com                subnodes.append(subnode)
24112469Sglenn.bergmans@arm.com            else:
24212469Sglenn.bergmans@arm.com                properties.append(subnode)
24312469Sglenn.bergmans@arm.com
24412469Sglenn.bergmans@arm.com        properties.merge(subnodes)
24512469Sglenn.bergmans@arm.com
24612469Sglenn.bergmans@arm.com        return properties
24712469Sglenn.bergmans@arm.com
24812469Sglenn.bergmans@arm.com    def add_rootnode(self, rootnode, prenops=None, postnops=None):
24912469Sglenn.bergmans@arm.com        """First sort the device tree, so that properties are before nodes."""
25012469Sglenn.bergmans@arm.com        rootnode = self.sortNodes(rootnode)
25112469Sglenn.bergmans@arm.com        super(Fdt, self).add_rootnode(rootnode, prenops, postnops)
25212469Sglenn.bergmans@arm.com
25312469Sglenn.bergmans@arm.com    def writeDtbFile(self, filename):
25412469Sglenn.bergmans@arm.com        """Convert the device tree to DTB and write to a file."""
25512469Sglenn.bergmans@arm.com        filename = os.path.realpath(filename)
25612469Sglenn.bergmans@arm.com        try:
25712469Sglenn.bergmans@arm.com            with open(filename, 'wb') as f:
25812469Sglenn.bergmans@arm.com                f.write(self.to_dtb())
25912469Sglenn.bergmans@arm.com            return filename
26012469Sglenn.bergmans@arm.com        except IOError:
26112469Sglenn.bergmans@arm.com            raise RuntimeError("Failed to open DTB output file")
26212469Sglenn.bergmans@arm.com
26312469Sglenn.bergmans@arm.com    def writeDtsFile(self, filename):
26412469Sglenn.bergmans@arm.com        """Convert the device tree to DTS and write to a file."""
26512469Sglenn.bergmans@arm.com        filename = os.path.realpath(filename)
26612469Sglenn.bergmans@arm.com        try:
26712469Sglenn.bergmans@arm.com            with open(filename, 'w') as f:
26812469Sglenn.bergmans@arm.com                f.write(self.to_dts())
26912469Sglenn.bergmans@arm.com            return filename
27012469Sglenn.bergmans@arm.com        except IOError:
27112469Sglenn.bergmans@arm.com            raise RuntimeError("Failed to open DTS output file")
272