--- /dev/null
+# Copyright (c) 2016 ARM Limited
+# All rights reserved.
+#
+# The license below extends only to copyright in the software and shall
+# not be construed as granting a license to any other intellectual
+# property including but not limited to intellectual property relating
+# to a hardware implementation of the functionality of the software
+# licensed hereunder. You may use the software subject to the license
+# terms below provided that you ensure that this notice is replicated
+# unmodified and in its entirety in all distributions of the software,
+# modified or unmodified, in source code or in binary form.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met: redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer;
+# redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution;
+# neither the name of the copyright holders nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# Author: Glenn Bergmans
+
+from m5.ext.pyfdt import pyfdt
+import re
+import os
+from m5.SimObject import SimObject
+
+class FdtProperty(pyfdt.FdtProperty):
+ """Create a property without values."""
+ pass
+
+class FdtPropertyWords(pyfdt.FdtPropertyWords):
+ """Create a property with word (32-bit unsigned) values."""
+ def __init__(self, name, words):
+ if type(words) != list:
+ words = [words]
+ # Make sure all values are ints (use automatic base detection if the
+ # type is str)
+ words = [long(w, base=0) if type(w) == str else long(w) for w in words]
+ super(FdtPropertyWords, self).__init__(name, words)
+
+class FdtPropertyStrings(pyfdt.FdtPropertyStrings):
+ """Create a property with string values."""
+
+ def __init__(self, name, strings):
+ if type(strings) == str:
+ strings = [strings]
+ strings = [str(string) for string in strings] # Make all values strings
+ super(FdtPropertyStrings, self).__init__(name, strings)
+
+class FdtPropertyBytes(pyfdt.FdtPropertyBytes):
+ """Create a property with integer (8-bit signed) values."""
+
+ def __init__(self, name, values):
+ if type(values) != list:
+ values = [values]
+ # Make sure all values are ints (use automatic base detection if the
+ # type is str)
+ values = [int(v, base=0)
+ if isinstance(v, str) else int(v) for v in values]
+ super(FdtPropertyBytes, self).__init__(name, values)
+
+class FdtState(object):
+ """Class for maintaining state while recursively generating a flattened
+ device tree. The state tracks address, size and CPU address cell sizes, and
+ maintains a dictionary of allocated phandles."""
+
+ phandle_counter = 0
+ phandles = dict()
+
+ def __init__(self, addr_cells, size_cells, cpu_cells):
+ """Instantiate values of this state. The state can only be initialized
+ once."""
+
+ self.addr_cells = addr_cells
+ self.size_cells = size_cells
+ self.cpu_cells = cpu_cells
+
+ def phandle(self, obj):
+ """Return a unique phandle number for a key. The key can be a SimObject
+ or any value that is castable to a string. If the phandle doesn't exist
+ a new one is created, otherwise the existing one is returned."""
+
+ if isinstance(obj, SimObject):
+ key = str(id(obj))
+ else:
+ try:
+ key = str(obj)
+ except ValueError:
+ raise ValueError('Phandle keys must be castable to str')
+
+ if not key in FdtState.phandles:
+ FdtState.phandle_counter += 1
+
+ return FdtState.phandles.setdefault(key, FdtState.phandle_counter)
+
+ def resetPhandles(self):
+ FdtState.phandle_counter = 0
+ FdtState.phandles = dict()
+
+ def int_to_cells(self, value, cells):
+ """Helper function for: generates a list of 32 bit cells from an int,
+ used to split up addresses in appropriate 32 bit chunks."""
+ value = long(value)
+
+ if (value >> (32 * cells)) != 0:
+ fatal("Value %d doesn't fit in %d cells" % (value, cells))
+
+ return [(value >> 32*(x-1)) & 0xFFFFFFFF for x in range(cells, 0, -1)]
+
+ def addrCells(self, addr):
+ """Format an integer type according to the address_cells value of this
+ state."""
+ return self.int_to_cells(addr, self.addr_cells)
+
+ def CPUAddrCells(self, addr):
+ """Format an integer type according to the cpu_cells value of this
+ state."""
+ return self.int_to_cells(addr, self.cpu_cells)
+
+ def sizeCells(self, size):
+ """Format an integer type according to the size_cells value of this
+ state."""
+ return self.int_to_cells(size, self.size_cells)
+
+ def addrCellsProperty(self):
+ """Return an #address-cells property with the value of this state."""
+ return FdtPropertyWords("#address-cells", self.addr_cells)
+
+ def sizeCellsProperty(self):
+ """Return an #size-cells property with the value of this state."""
+ return FdtPropertyWords("#size-cells", self.size_cells)
+
+ def CPUCellsProperty(self):
+ """Return an #address-cells property for cpu nodes with the value
+ of this state."""
+ return FdtPropertyWords("#address-cells", self.cpu_cells)
+
+class FdtNop(pyfdt.FdtNop):
+ """Create an empty node."""
+ pass
+
+class FdtNode(pyfdt.FdtNode):
+ def __init__(self, name, obj=None):
+ """Create a new node and immediately set the phandle property, if obj
+ is supplied"""
+ super(FdtNode, self).__init__(name)
+ if obj != None:
+ self.appendPhandle(obj)
+
+ def append(self, subnodes):
+ """Change the behavior of the normal append to override if a node with
+ the same name already exists or merge if the name exists and is a node
+ type. Can also take a list of subnodes, that each get appended."""
+ if not hasattr(subnodes, '__iter__'):
+ subnodes = [subnodes]
+
+ for subnode in subnodes:
+ try:
+ if not issubclass(type(subnode), pyfdt.FdtNop):
+ index = self.index(subnode.name)
+ item = self.pop(index)
+ else:
+ item = None
+ except ValueError:
+ item = None
+
+ if isinstance(item, pyfdt.FdtNode) and \
+ isinstance(subnode, pyfdt.FdtNode):
+ item.merge(subnode)
+ subnode = item
+
+ super(FdtNode, self).append(subnode)
+
+ def appendList(self, subnode_list):
+ """Append all properties/nodes in the iterable."""
+ for subnode in subnode_list:
+ self.append(subnode)
+
+ def appendCompatible(self, compatible):
+ """Append a compatible property with the supplied compatibility
+ strings."""
+ if isinstance(compatible, str):
+ compatible = [compatible]
+ self.append(FdtPropertyStrings('compatible', compatible))
+
+ def appendPhandle(self, obj):
+ """Append a phandle property to this node with the phandle of the
+ supplied object."""
+ # Create a bogus state because we only need the Phandle dictionary
+ state = FdtState(addr_cells=1, size_cells=1, cpu_cells=1)
+
+ phandle = state.phandle(obj)
+ self.append(FdtPropertyWords("phandle", [phandle]))
+
+class Fdt(pyfdt.Fdt):
+ def sortNodes(self, node):
+ """Move all properties to the beginning and subnodes to the end
+ while maintaining the order of the subnodes. DTB files require the
+ properties to go before the nodes, but the PyFdt doesn't account for
+ defining nodes and properties in a random order."""
+ properties = FdtNode(node.name)
+ subnodes = FdtNode(node.name)
+
+ while len(node):
+ subnode = node.pop(0)
+ if issubclass(type(subnode), pyfdt.FdtNode):
+ subnode = self.sortNodes(subnode)
+ subnodes.append(subnode)
+ else:
+ properties.append(subnode)
+
+ properties.merge(subnodes)
+
+ return properties
+
+ def add_rootnode(self, rootnode, prenops=None, postnops=None):
+ """First sort the device tree, so that properties are before nodes."""
+ rootnode = self.sortNodes(rootnode)
+ super(Fdt, self).add_rootnode(rootnode, prenops, postnops)
+
+ def writeDtbFile(self, filename):
+ """Convert the device tree to DTB and write to a file."""
+ filename = os.path.realpath(filename)
+ try:
+ with open(filename, 'wb') as f:
+ f.write(self.to_dtb())
+ return filename
+ except IOError:
+ raise RuntimeError("Failed to open DTB output file")
+
+ def writeDtsFile(self, filename):
+ """Convert the device tree to DTS and write to a file."""
+ filename = os.path.realpath(filename)
+ try:
+ with open(filename, 'w') as f:
+ f.write(self.to_dts())
+ return filename
+ except IOError:
+ raise RuntimeError("Failed to open DTS output file")