1# Copyright (c) 2016 Google, Inc 2# 3# SPDX-License-Identifier: GPL-2.0+ 4# 5# Base class for all entries 6# 7 8# importlib was introduced in Python 2.7 but there was a report of it not 9# working in 2.7.12, so we work around this: 10# http://lists.denx.de/pipermail/u-boot/2016-October/269729.html 11try: 12 import importlib 13 have_importlib = True 14except: 15 have_importlib = False 16 17import fdt_util 18import tools 19 20modules = {} 21 22class Entry(object): 23 """An Entry in the image 24 25 An entry corresponds to a single node in the device-tree description 26 of the image. Each entry ends up being a part of the final image. 27 Entries can be placed either right next to each other, or with padding 28 between them. The type of the entry determines the data that is in it. 29 30 This class is not used by itself. All entry objects are subclasses of 31 Entry. 32 33 Attributes: 34 image: The image containing this entry 35 node: The node that created this entry 36 pos: Absolute position of entry within the image, None if not known 37 size: Entry size in bytes, None if not known 38 contents_size: Size of contents in bytes, 0 by default 39 align: Entry start position alignment, or None 40 align_size: Entry size alignment, or None 41 align_end: Entry end position alignment, or None 42 pad_before: Number of pad bytes before the contents, 0 if none 43 pad_after: Number of pad bytes after the contents, 0 if none 44 data: Contents of entry (string of bytes) 45 """ 46 def __init__(self, image, etype, node, read_node=True): 47 self.image = image 48 self.etype = etype 49 self._node = node 50 self.pos = None 51 self.size = None 52 self.contents_size = 0 53 self.align = None 54 self.align_size = None 55 self.align_end = None 56 self.pad_before = 0 57 self.pad_after = 0 58 self.pos_unset = False 59 if read_node: 60 self.ReadNode() 61 62 @staticmethod 63 def Create(image, node, etype=None): 64 """Create a new entry for a node. 65 66 Args: 67 image: Image object containing this node 68 node: Node object containing information about the entry to create 69 etype: Entry type to use, or None to work it out (used for tests) 70 71 Returns: 72 A new Entry object of the correct type (a subclass of Entry) 73 """ 74 if not etype: 75 etype = fdt_util.GetString(node, 'type', node.name) 76 module_name = etype.replace('-', '_') 77 module = modules.get(module_name) 78 79 # Import the module if we have not already done so. 80 if not module: 81 try: 82 if have_importlib: 83 module = importlib.import_module(module_name) 84 else: 85 module = __import__(module_name) 86 except ImportError: 87 raise ValueError("Unknown entry type '%s' in node '%s'" % 88 (etype, node.path)) 89 modules[module_name] = module 90 91 # Call its constructor to get the object we want. 92 obj = getattr(module, 'Entry_%s' % module_name) 93 return obj(image, etype, node) 94 95 def ReadNode(self): 96 """Read entry information from the node 97 98 This reads all the fields we recognise from the node, ready for use. 99 """ 100 self.pos = fdt_util.GetInt(self._node, 'pos') 101 self.size = fdt_util.GetInt(self._node, 'size') 102 self.align = fdt_util.GetInt(self._node, 'align') 103 if tools.NotPowerOfTwo(self.align): 104 raise ValueError("Node '%s': Alignment %s must be a power of two" % 105 (self._node.path, self.align)) 106 self.pad_before = fdt_util.GetInt(self._node, 'pad-before', 0) 107 self.pad_after = fdt_util.GetInt(self._node, 'pad-after', 0) 108 self.align_size = fdt_util.GetInt(self._node, 'align-size') 109 if tools.NotPowerOfTwo(self.align_size): 110 raise ValueError("Node '%s': Alignment size %s must be a power " 111 "of two" % (self._node.path, self.align_size)) 112 self.align_end = fdt_util.GetInt(self._node, 'align-end') 113 self.pos_unset = fdt_util.GetBool(self._node, 'pos-unset') 114 115 def ObtainContents(self): 116 """Figure out the contents of an entry. 117 118 Returns: 119 True if the contents were found, False if another call is needed 120 after the other entries are processed. 121 """ 122 # No contents by default: subclasses can implement this 123 return True 124 125 def Pack(self, pos): 126 """Figure out how to pack the entry into the image 127 128 Most of the time the entries are not fully specified. There may be 129 an alignment but no size. In that case we take the size from the 130 contents of the entry. 131 132 If an entry has no hard-coded position, it will be placed at @pos. 133 134 Once this function is complete, both the position and size of the 135 entry will be know. 136 137 Args: 138 Current image position pointer 139 140 Returns: 141 New image position pointer (after this entry) 142 """ 143 if self.pos is None: 144 if self.pos_unset: 145 self.Raise('No position set with pos-unset: should another ' 146 'entry provide this correct position?') 147 self.pos = tools.Align(pos, self.align) 148 needed = self.pad_before + self.contents_size + self.pad_after 149 needed = tools.Align(needed, self.align_size) 150 size = self.size 151 if not size: 152 size = needed 153 new_pos = self.pos + size 154 aligned_pos = tools.Align(new_pos, self.align_end) 155 if aligned_pos != new_pos: 156 size = aligned_pos - self.pos 157 new_pos = aligned_pos 158 159 if not self.size: 160 self.size = size 161 162 if self.size < needed: 163 self.Raise("Entry contents size is %#x (%d) but entry size is " 164 "%#x (%d)" % (needed, needed, self.size, self.size)) 165 # Check that the alignment is correct. It could be wrong if the 166 # and pos or size values were provided (i.e. not calculated), but 167 # conflict with the provided alignment values 168 if self.size != tools.Align(self.size, self.align_size): 169 self.Raise("Size %#x (%d) does not match align-size %#x (%d)" % 170 (self.size, self.size, self.align_size, self.align_size)) 171 if self.pos != tools.Align(self.pos, self.align): 172 self.Raise("Position %#x (%d) does not match align %#x (%d)" % 173 (self.pos, self.pos, self.align, self.align)) 174 175 return new_pos 176 177 def Raise(self, msg): 178 """Convenience function to raise an error referencing a node""" 179 raise ValueError("Node '%s': %s" % (self._node.path, msg)) 180 181 def GetPath(self): 182 """Get the path of a node 183 184 Returns: 185 Full path of the node for this entry 186 """ 187 return self._node.path 188 189 def GetData(self): 190 return self.data 191 192 def GetPositions(self): 193 return {} 194 195 def SetPositionSize(self, pos, size): 196 self.pos = pos 197 self.size = size 198 199 def ProcessContents(self): 200 pass 201