1# Copyright (c) 2016,2019 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, **kwargs): 91 """Instantiate values of this state. The state can only be initialized 92 once.""" 93 94 self.addr_cells = kwargs.pop('addr_cells', 0) 95 self.size_cells = kwargs.pop('size_cells', 0) 96 self.cpu_cells = kwargs.pop('cpu_cells', 0) 97 self.interrupt_cells = kwargs.pop('interrupt_cells', 0) 98 99 def phandle(self, obj): 100 """Return a unique phandle number for a key. The key can be a SimObject 101 or any value that is castable to a string. If the phandle doesn't exist 102 a new one is created, otherwise the existing one is returned.""" 103 104 if isinstance(obj, SimObject): 105 key = str(id(obj)) 106 else: 107 try: 108 key = str(obj) 109 except ValueError: 110 raise ValueError('Phandle keys must be castable to str') 111 112 if not key in FdtState.phandles: 113 FdtState.phandle_counter += 1 114 115 return FdtState.phandles.setdefault(key, FdtState.phandle_counter) 116 117 def resetPhandles(self): 118 FdtState.phandle_counter = 0 119 FdtState.phandles = dict() 120 121 def int_to_cells(self, value, cells): 122 """Helper function for: generates a list of 32 bit cells from an int, 123 used to split up addresses in appropriate 32 bit chunks.""" 124 value = long(value) 125 126 if (value >> (32 * cells)) != 0: 127 fatal("Value %d doesn't fit in %d cells" % (value, cells)) 128 129 return [(value >> 32*(x-1)) & 0xFFFFFFFF for x in range(cells, 0, -1)] 130 131 def addrCells(self, addr): 132 """Format an integer type according to the address_cells value of this 133 state.""" 134 return self.int_to_cells(addr, self.addr_cells) 135 136 def CPUAddrCells(self, addr): 137 """Format an integer type according to the cpu_cells value of this 138 state.""" 139 return self.int_to_cells(addr, self.cpu_cells) 140 141 def sizeCells(self, size): 142 """Format an integer type according to the size_cells value of this 143 state.""" 144 return self.int_to_cells(size, self.size_cells) 145 146 def interruptCells(self, interrupt): 147 """Format an integer type according to the interrupt_cells value 148 of this state.""" 149 return self.int_to_cells(interrupt, self.interrupt_cells) 150 151 def addrCellsProperty(self): 152 """Return an #address-cells property with the value of this state.""" 153 return FdtPropertyWords("#address-cells", self.addr_cells) 154 155 def sizeCellsProperty(self): 156 """Return an #size-cells property with the value of this state.""" 157 return FdtPropertyWords("#size-cells", self.size_cells) 158 159 def CPUCellsProperty(self): 160 """Return an #address-cells property for cpu nodes with the value 161 of this state.""" 162 return FdtPropertyWords("#address-cells", self.cpu_cells) 163 164 def interruptCellsProperty(self): 165 """Return an #interrupt-cells property for cpu nodes with the value 166 of this state.""" 167 return FdtPropertyWords("#interrupt-cells", self.interrupt_cells) 168 169 170class FdtNop(pyfdt.FdtNop): 171 """Create an empty node.""" 172 pass 173 174class FdtNode(pyfdt.FdtNode): 175 def __init__(self, name, obj=None): 176 """Create a new node and immediately set the phandle property, if obj 177 is supplied""" 178 super(FdtNode, self).__init__(name) 179 if obj != None: 180 self.appendPhandle(obj) 181 182 def append(self, subnodes): 183 """Change the behavior of the normal append to override if a node with 184 the same name already exists or merge if the name exists and is a node 185 type. Can also take a list of subnodes, that each get appended.""" 186 if not hasattr(subnodes, '__iter__'): 187 subnodes = [subnodes] 188 189 for subnode in subnodes: 190 try: 191 if not issubclass(type(subnode), pyfdt.FdtNop): 192 index = self.index(subnode.name) 193 item = self.pop(index) 194 else: 195 item = None 196 except ValueError: 197 item = None 198 199 if isinstance(item, pyfdt.FdtNode) and \ 200 isinstance(subnode, pyfdt.FdtNode): 201 item.merge(subnode) 202 subnode = item 203 204 super(FdtNode, self).append(subnode) 205 206 def appendList(self, subnode_list): 207 """Append all properties/nodes in the iterable.""" 208 for subnode in subnode_list: 209 self.append(subnode) 210 211 def appendCompatible(self, compatible): 212 """Append a compatible property with the supplied compatibility 213 strings.""" 214 if isinstance(compatible, str): 215 compatible = [compatible] 216 self.append(FdtPropertyStrings('compatible', compatible)) 217 218 def appendPhandle(self, obj): 219 """Append a phandle property to this node with the phandle of the 220 supplied object.""" 221 # Create a bogus state because we only need the Phandle dictionary 222 state = FdtState(addr_cells=1, size_cells=1, cpu_cells=1) 223 224 phandle = state.phandle(obj) 225 self.append(FdtPropertyWords("phandle", [phandle])) 226 227class Fdt(pyfdt.Fdt): 228 def sortNodes(self, node): 229 """Move all properties to the beginning and subnodes to the end 230 while maintaining the order of the subnodes. DTB files require the 231 properties to go before the nodes, but the PyFdt doesn't account for 232 defining nodes and properties in a random order.""" 233 properties = FdtNode(node.name) 234 subnodes = FdtNode(node.name) 235 236 while len(node): 237 subnode = node.pop(0) 238 if issubclass(type(subnode), pyfdt.FdtNode): 239 subnode = self.sortNodes(subnode) 240 subnodes.append(subnode) 241 else: 242 properties.append(subnode) 243 244 properties.merge(subnodes) 245 246 return properties 247 248 def add_rootnode(self, rootnode, prenops=None, postnops=None): 249 """First sort the device tree, so that properties are before nodes.""" 250 rootnode = self.sortNodes(rootnode) 251 super(Fdt, self).add_rootnode(rootnode, prenops, postnops) 252 253 def writeDtbFile(self, filename): 254 """Convert the device tree to DTB and write to a file.""" 255 filename = os.path.realpath(filename) 256 try: 257 with open(filename, 'wb') as f: 258 f.write(self.to_dtb()) 259 return filename 260 except IOError: 261 raise RuntimeError("Failed to open DTB output file") 262 263 def writeDtsFile(self, filename): 264 """Convert the device tree to DTS and write to a file.""" 265 filename = os.path.realpath(filename) 266 try: 267 with open(filename, 'w') as f: 268 f.write(self.to_dts()) 269 return filename 270 except IOError: 271 raise RuntimeError("Failed to open DTS output file") 272