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