fdthelper.py revision 12469
1# Copyright (c) 2016 ARM Limited
2# All rights reserved.
3#
4# The license below extends only to copyright in the software and shall
5# not be construed as granting a license to any other intellectual
6# property including but not limited to intellectual property relating
7# to a hardware implementation of the functionality of the software
8# licensed hereunder.  You may use the software subject to the license
9# terms below provided that you ensure that this notice is replicated
10# unmodified and in its entirety in all distributions of the software,
11# modified or unmodified, in source code or in binary form.
12#
13# Redistribution and use in source and binary forms, with or without
14# modification, are permitted provided that the following conditions are
15# met: redistributions of source code must retain the above copyright
16# notice, this list of conditions and the following disclaimer;
17# redistributions in binary form must reproduce the above copyright
18# notice, this list of conditions and the following disclaimer in the
19# documentation and/or other materials provided with the distribution;
20# neither the name of the copyright holders nor the names of its
21# contributors may be used to endorse or promote products derived from
22# this software without specific prior written permission.
23#
24# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
27# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
28# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
30# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
34# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35#
36# Author: Glenn Bergmans
37
38from m5.ext.pyfdt import pyfdt
39import re
40import os
41from m5.SimObject import SimObject
42
43class FdtProperty(pyfdt.FdtProperty):
44    """Create a property without values."""
45    pass
46
47class FdtPropertyWords(pyfdt.FdtPropertyWords):
48    """Create a property with word (32-bit unsigned) values."""
49    def __init__(self, name, words):
50        if type(words) != list:
51            words = [words]
52        # Make sure all values are ints (use automatic base detection if the
53        # type is str)
54        words = [long(w, base=0) if type(w) == str else long(w) for w in words]
55        super(FdtPropertyWords, self).__init__(name, words)
56
57class FdtPropertyStrings(pyfdt.FdtPropertyStrings):
58    """Create a property with string values."""
59
60    def __init__(self, name, strings):
61        if type(strings) == str:
62            strings = [strings]
63        strings = [str(string) for string in strings] # Make all values strings
64        super(FdtPropertyStrings, self).__init__(name, strings)
65
66class FdtPropertyBytes(pyfdt.FdtPropertyBytes):
67    """Create a property with integer (8-bit signed) values."""
68
69    def __init__(self, name, values):
70        if type(values) != list:
71            values = [values]
72        # Make sure all values are ints (use automatic base detection if the
73        # type is str)
74        values = [int(v, base=0)
75                   if isinstance(v, str) else int(v) for v in values]
76        super(FdtPropertyBytes, self).__init__(name, values)
77
78class FdtState(object):
79    """Class for maintaining state while recursively generating a flattened
80    device tree. The state tracks address, size and CPU address cell sizes, and
81    maintains a dictionary of allocated phandles."""
82
83    phandle_counter = 0
84    phandles = dict()
85
86    def __init__(self, addr_cells, size_cells, cpu_cells):
87        """Instantiate values of this state. The state can only be initialized
88        once."""
89
90        self.addr_cells = addr_cells
91        self.size_cells = size_cells
92        self.cpu_cells = cpu_cells
93
94    def phandle(self, obj):
95        """Return a unique phandle number for a key. The key can be a SimObject
96        or any value that is castable to a string. If the phandle doesn't exist
97        a new one is created, otherwise the existing one is returned."""
98
99        if isinstance(obj, SimObject):
100            key = str(id(obj))
101        else:
102            try:
103                key = str(obj)
104            except ValueError:
105                raise ValueError('Phandle keys must be castable to str')
106
107        if not key in FdtState.phandles:
108            FdtState.phandle_counter += 1
109
110        return FdtState.phandles.setdefault(key, FdtState.phandle_counter)
111
112    def resetPhandles(self):
113        FdtState.phandle_counter = 0
114        FdtState.phandles = dict()
115
116    def int_to_cells(self, value, cells):
117        """Helper function for: generates a list of 32 bit cells from an int,
118        used to split up addresses in appropriate 32 bit chunks."""
119        value = long(value)
120
121        if (value >> (32 * cells)) != 0:
122            fatal("Value %d doesn't fit in %d cells" % (value, cells))
123
124        return [(value >> 32*(x-1)) & 0xFFFFFFFF for x in range(cells, 0, -1)]
125
126    def addrCells(self, addr):
127        """Format an integer type according to the address_cells value of this
128        state."""
129        return self.int_to_cells(addr, self.addr_cells)
130
131    def CPUAddrCells(self, addr):
132        """Format an integer type according to the cpu_cells value of this
133        state."""
134        return self.int_to_cells(addr, self.cpu_cells)
135
136    def sizeCells(self, size):
137        """Format an integer type according to the size_cells value of this
138        state."""
139        return self.int_to_cells(size, self.size_cells)
140
141    def addrCellsProperty(self):
142        """Return an #address-cells property with the value of this state."""
143        return FdtPropertyWords("#address-cells", self.addr_cells)
144
145    def sizeCellsProperty(self):
146        """Return an #size-cells property with the value of this state."""
147        return FdtPropertyWords("#size-cells", self.size_cells)
148
149    def CPUCellsProperty(self):
150        """Return an #address-cells property for cpu nodes with the value
151        of this state."""
152        return FdtPropertyWords("#address-cells", self.cpu_cells)
153
154class FdtNop(pyfdt.FdtNop):
155    """Create an empty node."""
156    pass
157
158class FdtNode(pyfdt.FdtNode):
159    def __init__(self, name, obj=None):
160        """Create a new node and immediately set the phandle property, if obj
161        is supplied"""
162        super(FdtNode, self).__init__(name)
163        if obj != None:
164            self.appendPhandle(obj)
165
166    def append(self, subnodes):
167        """Change the behavior of the normal append to override if a node with
168        the same name already exists or merge if the name exists and is a node
169        type. Can also take a list of subnodes, that each get appended."""
170        if not hasattr(subnodes, '__iter__'):
171            subnodes = [subnodes]
172
173        for subnode in subnodes:
174            try:
175                if not issubclass(type(subnode), pyfdt.FdtNop):
176                    index = self.index(subnode.name)
177                    item = self.pop(index)
178                else:
179                    item = None
180            except ValueError:
181                item = None
182
183            if isinstance(item,  pyfdt.FdtNode) and \
184               isinstance(subnode,  pyfdt.FdtNode):
185                item.merge(subnode)
186                subnode = item
187
188            super(FdtNode, self).append(subnode)
189
190    def appendList(self, subnode_list):
191        """Append all properties/nodes in the iterable."""
192        for subnode in subnode_list:
193            self.append(subnode)
194
195    def appendCompatible(self, compatible):
196        """Append a compatible property with the supplied compatibility
197        strings."""
198        if isinstance(compatible, str):
199            compatible = [compatible]
200        self.append(FdtPropertyStrings('compatible', compatible))
201
202    def appendPhandle(self, obj):
203        """Append a phandle property to this node with the phandle of the
204        supplied object."""
205        # Create a bogus state because we only need the Phandle dictionary
206        state = FdtState(addr_cells=1, size_cells=1, cpu_cells=1)
207
208        phandle = state.phandle(obj)
209        self.append(FdtPropertyWords("phandle", [phandle]))
210
211class Fdt(pyfdt.Fdt):
212    def sortNodes(self, node):
213        """Move all properties to the beginning and subnodes to the end
214        while maintaining the order of the subnodes. DTB files require the
215        properties to go before the nodes, but the PyFdt doesn't account for
216        defining nodes and properties in a random order."""
217        properties = FdtNode(node.name)
218        subnodes = FdtNode(node.name)
219
220        while len(node):
221            subnode = node.pop(0)
222            if issubclass(type(subnode), pyfdt.FdtNode):
223                subnode = self.sortNodes(subnode)
224                subnodes.append(subnode)
225            else:
226                properties.append(subnode)
227
228        properties.merge(subnodes)
229
230        return properties
231
232    def add_rootnode(self, rootnode, prenops=None, postnops=None):
233        """First sort the device tree, so that properties are before nodes."""
234        rootnode = self.sortNodes(rootnode)
235        super(Fdt, self).add_rootnode(rootnode, prenops, postnops)
236
237    def writeDtbFile(self, filename):
238        """Convert the device tree to DTB and write to a file."""
239        filename = os.path.realpath(filename)
240        try:
241            with open(filename, 'wb') as f:
242                f.write(self.to_dtb())
243            return filename
244        except IOError:
245            raise RuntimeError("Failed to open DTB output file")
246
247    def writeDtsFile(self, filename):
248        """Convert the device tree to DTS and write to a file."""
249        filename = os.path.realpath(filename)
250        try:
251            with open(filename, 'w') as f:
252                f.write(self.to_dts())
253            return filename
254        except IOError:
255            raise RuntimeError("Failed to open DTS output file")
256