fdthelper.py revision 13719
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
38import six
39if six.PY3:
40    long = int
41
42from m5.ext.pyfdt import pyfdt
43import re
44import os
45from m5.SimObject import SimObject
46
47class FdtProperty(pyfdt.FdtProperty):
48    """Create a property without values."""
49    pass
50
51class FdtPropertyWords(pyfdt.FdtPropertyWords):
52    """Create a property with word (32-bit unsigned) values."""
53    def __init__(self, name, words):
54        if type(words) != list:
55            words = [words]
56        # Make sure all values are ints (use automatic base detection if the
57        # type is str)
58        words = [long(w, base=0) if type(w) == str else long(w) for w in words]
59        super(FdtPropertyWords, self).__init__(name, words)
60
61class FdtPropertyStrings(pyfdt.FdtPropertyStrings):
62    """Create a property with string values."""
63
64    def __init__(self, name, strings):
65        if type(strings) == str:
66            strings = [strings]
67        strings = [str(string) for string in strings] # Make all values strings
68        super(FdtPropertyStrings, self).__init__(name, strings)
69
70class FdtPropertyBytes(pyfdt.FdtPropertyBytes):
71    """Create a property with integer (8-bit signed) values."""
72
73    def __init__(self, name, values):
74        if type(values) != list:
75            values = [values]
76        # Make sure all values are ints (use automatic base detection if the
77        # type is str)
78        values = [int(v, base=0)
79                   if isinstance(v, str) else int(v) for v in values]
80        super(FdtPropertyBytes, self).__init__(name, values)
81
82class FdtState(object):
83    """Class for maintaining state while recursively generating a flattened
84    device tree. The state tracks address, size and CPU address cell sizes, and
85    maintains a dictionary of allocated phandles."""
86
87    phandle_counter = 0
88    phandles = dict()
89
90    def __init__(self, addr_cells, size_cells, cpu_cells):
91        """Instantiate values of this state. The state can only be initialized
92        once."""
93
94        self.addr_cells = addr_cells
95        self.size_cells = size_cells
96        self.cpu_cells = cpu_cells
97
98    def phandle(self, obj):
99        """Return a unique phandle number for a key. The key can be a SimObject
100        or any value that is castable to a string. If the phandle doesn't exist
101        a new one is created, otherwise the existing one is returned."""
102
103        if isinstance(obj, SimObject):
104            key = str(id(obj))
105        else:
106            try:
107                key = str(obj)
108            except ValueError:
109                raise ValueError('Phandle keys must be castable to str')
110
111        if not key in FdtState.phandles:
112            FdtState.phandle_counter += 1
113
114        return FdtState.phandles.setdefault(key, FdtState.phandle_counter)
115
116    def resetPhandles(self):
117        FdtState.phandle_counter = 0
118        FdtState.phandles = dict()
119
120    def int_to_cells(self, value, cells):
121        """Helper function for: generates a list of 32 bit cells from an int,
122        used to split up addresses in appropriate 32 bit chunks."""
123        value = long(value)
124
125        if (value >> (32 * cells)) != 0:
126            fatal("Value %d doesn't fit in %d cells" % (value, cells))
127
128        return [(value >> 32*(x-1)) & 0xFFFFFFFF for x in range(cells, 0, -1)]
129
130    def addrCells(self, addr):
131        """Format an integer type according to the address_cells value of this
132        state."""
133        return self.int_to_cells(addr, self.addr_cells)
134
135    def CPUAddrCells(self, addr):
136        """Format an integer type according to the cpu_cells value of this
137        state."""
138        return self.int_to_cells(addr, self.cpu_cells)
139
140    def sizeCells(self, size):
141        """Format an integer type according to the size_cells value of this
142        state."""
143        return self.int_to_cells(size, self.size_cells)
144
145    def addrCellsProperty(self):
146        """Return an #address-cells property with the value of this state."""
147        return FdtPropertyWords("#address-cells", self.addr_cells)
148
149    def sizeCellsProperty(self):
150        """Return an #size-cells property with the value of this state."""
151        return FdtPropertyWords("#size-cells", self.size_cells)
152
153    def CPUCellsProperty(self):
154        """Return an #address-cells property for cpu nodes with the value
155        of this state."""
156        return FdtPropertyWords("#address-cells", self.cpu_cells)
157
158class FdtNop(pyfdt.FdtNop):
159    """Create an empty node."""
160    pass
161
162class FdtNode(pyfdt.FdtNode):
163    def __init__(self, name, obj=None):
164        """Create a new node and immediately set the phandle property, if obj
165        is supplied"""
166        super(FdtNode, self).__init__(name)
167        if obj != None:
168            self.appendPhandle(obj)
169
170    def append(self, subnodes):
171        """Change the behavior of the normal append to override if a node with
172        the same name already exists or merge if the name exists and is a node
173        type. Can also take a list of subnodes, that each get appended."""
174        if not hasattr(subnodes, '__iter__'):
175            subnodes = [subnodes]
176
177        for subnode in subnodes:
178            try:
179                if not issubclass(type(subnode), pyfdt.FdtNop):
180                    index = self.index(subnode.name)
181                    item = self.pop(index)
182                else:
183                    item = None
184            except ValueError:
185                item = None
186
187            if isinstance(item,  pyfdt.FdtNode) and \
188               isinstance(subnode,  pyfdt.FdtNode):
189                item.merge(subnode)
190                subnode = item
191
192            super(FdtNode, self).append(subnode)
193
194    def appendList(self, subnode_list):
195        """Append all properties/nodes in the iterable."""
196        for subnode in subnode_list:
197            self.append(subnode)
198
199    def appendCompatible(self, compatible):
200        """Append a compatible property with the supplied compatibility
201        strings."""
202        if isinstance(compatible, str):
203            compatible = [compatible]
204        self.append(FdtPropertyStrings('compatible', compatible))
205
206    def appendPhandle(self, obj):
207        """Append a phandle property to this node with the phandle of the
208        supplied object."""
209        # Create a bogus state because we only need the Phandle dictionary
210        state = FdtState(addr_cells=1, size_cells=1, cpu_cells=1)
211
212        phandle = state.phandle(obj)
213        self.append(FdtPropertyWords("phandle", [phandle]))
214
215class Fdt(pyfdt.Fdt):
216    def sortNodes(self, node):
217        """Move all properties to the beginning and subnodes to the end
218        while maintaining the order of the subnodes. DTB files require the
219        properties to go before the nodes, but the PyFdt doesn't account for
220        defining nodes and properties in a random order."""
221        properties = FdtNode(node.name)
222        subnodes = FdtNode(node.name)
223
224        while len(node):
225            subnode = node.pop(0)
226            if issubclass(type(subnode), pyfdt.FdtNode):
227                subnode = self.sortNodes(subnode)
228                subnodes.append(subnode)
229            else:
230                properties.append(subnode)
231
232        properties.merge(subnodes)
233
234        return properties
235
236    def add_rootnode(self, rootnode, prenops=None, postnops=None):
237        """First sort the device tree, so that properties are before nodes."""
238        rootnode = self.sortNodes(rootnode)
239        super(Fdt, self).add_rootnode(rootnode, prenops, postnops)
240
241    def writeDtbFile(self, filename):
242        """Convert the device tree to DTB and write to a file."""
243        filename = os.path.realpath(filename)
244        try:
245            with open(filename, 'wb') as f:
246                f.write(self.to_dtb())
247            return filename
248        except IOError:
249            raise RuntimeError("Failed to open DTB output file")
250
251    def writeDtsFile(self, filename):
252        """Convert the device tree to DTS and write to a file."""
253        filename = os.path.realpath(filename)
254        try:
255            with open(filename, 'w') as f:
256                f.write(self.to_dts())
257            return filename
258        except IOError:
259            raise RuntimeError("Failed to open DTS output file")
260