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