1# SPDX-License-Identifier: GPL-2.0+ 2# Copyright (c) 2016 Google, Inc 3# Written by Simon Glass <sjg@chromium.org> 4# 5# Entry-type module for producing an image using mkimage 6# 7 8from collections import OrderedDict 9 10from binman.entry import Entry 11from binman.etype.section import Entry_section 12from dtoc import fdt_util 13from u_boot_pylib import tools 14 15class Entry_mkimage(Entry_section): 16 """Binary produced by mkimage 17 18 Properties / Entry arguments: 19 - args: Arguments to pass 20 - data-to-imagename: Indicates that the -d data should be passed in as 21 the image name also (-n) 22 - multiple-data-files: boolean to tell binman to pass all files as 23 datafiles to mkimage instead of creating a temporary file the result 24 of datafiles concatenation 25 - filename: filename of output binary generated by mkimage 26 27 The data passed to mkimage via the -d flag is collected from subnodes of the 28 mkimage node, e.g.:: 29 30 mkimage { 31 filename = "imximage.bin"; 32 args = "-n test -T imximage"; 33 34 u-boot-spl { 35 }; 36 }; 37 38 This calls mkimage to create an imximage with `u-boot-spl.bin` as the data 39 file, with mkimage being called like this:: 40 41 mkimage -d <data_file> -n test -T imximage <output_file> 42 43 The output from mkimage then becomes part of the image produced by 44 binman but also is written into `imximage.bin` file. If you need to put 45 multiple things in the data file, you can use a section, or just multiple 46 subnodes like this:: 47 48 mkimage { 49 args = "-n test -T imximage"; 50 51 u-boot-spl { 52 }; 53 54 u-boot-tpl { 55 }; 56 }; 57 58 Note that binman places the contents (here SPL and TPL) into a single file 59 and passes that to mkimage using the -d option. 60 61 To pass all datafiles untouched to mkimage:: 62 63 mkimage { 64 args = "-n rk3399 -T rkspi"; 65 multiple-data-files; 66 67 u-boot-tpl { 68 }; 69 70 u-boot-spl { 71 }; 72 }; 73 74 This calls mkimage to create a Rockchip RK3399-specific first stage 75 bootloader, made of TPL+SPL. Since this first stage bootloader requires to 76 align the TPL and SPL but also some weird hacks that is handled by mkimage 77 directly, binman is told to not perform the concatenation of datafiles prior 78 to passing the data to mkimage. 79 80 To use CONFIG options in the arguments, use a string list instead, as in 81 this example which also produces four arguments:: 82 83 mkimage { 84 args = "-n", CONFIG_SYS_SOC, "-T imximage"; 85 86 u-boot-spl { 87 }; 88 }; 89 90 If you need to pass the input data in with the -n argument as well, then use 91 the 'data-to-imagename' property:: 92 93 mkimage { 94 args = "-T imximage"; 95 data-to-imagename; 96 97 u-boot-spl { 98 }; 99 }; 100 101 That will pass the data to mkimage both as the data file (with -d) and as 102 the image name (with -n). In both cases, a filename is passed as the 103 argument, with the actual data being in that file. 104 105 If need to pass different data in with -n, then use an `imagename` subnode:: 106 107 mkimage { 108 args = "-T imximage"; 109 110 imagename { 111 blob { 112 filename = "spl/u-boot-spl.cfgout" 113 }; 114 }; 115 116 u-boot-spl { 117 }; 118 }; 119 120 This will pass in u-boot-spl as the input data and the .cfgout file as the 121 -n data. 122 """ 123 def __init__(self, section, etype, node): 124 super().__init__(section, etype, node) 125 self._imagename = None 126 self._multiple_data_files = False 127 128 def ReadNode(self): 129 super().ReadNode() 130 self._multiple_data_files = fdt_util.GetBool(self._node, 131 'multiple-data-files') 132 self._args = fdt_util.GetArgs(self._node, 'args') 133 self._data_to_imagename = fdt_util.GetBool(self._node, 134 'data-to-imagename') 135 if self._data_to_imagename and self._node.FindNode('imagename'): 136 self.Raise('Cannot use both imagename node and data-to-imagename') 137 138 def ReadEntries(self): 139 """Read the subnodes to find out what should go in this image""" 140 for node in self._node.subnodes: 141 if self.IsSpecialSubnode(node): 142 continue 143 entry = Entry.Create(self, node, 144 expanded=self.GetImage().use_expanded, 145 missing_etype=self.GetImage().missing_etype) 146 entry.ReadNode() 147 entry.SetPrefix(self._name_prefix) 148 if entry.name == 'imagename': 149 self._imagename = entry 150 else: 151 self._entries[entry.name] = entry 152 153 def BuildSectionData(self, required): 154 """Build mkimage entry contents 155 156 Runs mkimage to build the entry contents 157 158 Args: 159 required (bool): True if the data must be present, False if it is OK 160 to return None 161 162 Returns: 163 bytes: Contents of the section 164 """ 165 # Use a non-zero size for any fake files to keep mkimage happy 166 # Note that testMkimageImagename() relies on this 'mkimage' parameter 167 fake_size = 1024 168 if self._multiple_data_files: 169 fnames = [] 170 uniq = self.GetUniqueName() 171 for entry in self._entries.values(): 172 # Put the contents in a temporary file 173 ename = f'mkimage-in-{uniq}-{entry.name}' 174 fname = tools.get_output_filename(ename) 175 data = entry.GetData(required) 176 tools.write_file(fname, data) 177 fnames.append(fname) 178 input_fname = ":".join(fnames) 179 data = b'' 180 else: 181 data, input_fname, uniq = self.collect_contents_to_file( 182 self._entries.values(), 'mkimage', fake_size) 183 if self._imagename: 184 image_data, imagename_fname, _ = self.collect_contents_to_file( 185 [self._imagename], 'mkimage-n', 1024) 186 outfile = self._filename if self._filename else 'mkimage-out.%s' % uniq 187 output_fname = tools.get_output_filename(outfile) 188 189 missing_list = [] 190 self.CheckMissing(missing_list) 191 self.missing = bool(missing_list) 192 if self.missing: 193 return b'' 194 195 args = ['-d', input_fname] 196 if self._data_to_imagename: 197 args += ['-n', input_fname] 198 elif self._imagename: 199 args += ['-n', imagename_fname] 200 args += self._args + [output_fname] 201 if self.mkimage.run_cmd(*args) is not None: 202 return tools.read_file(output_fname) 203 else: 204 # Bintool is missing; just use the input data as the output 205 self.record_missing_bintool(self.mkimage) 206 return data 207 208 def GetEntries(self): 209 # Make a copy so we don't change the original 210 entries = OrderedDict(self._entries) 211 if self._imagename: 212 entries['imagename'] = self._imagename 213 return entries 214 215 def AddBintools(self, btools): 216 super().AddBintools(btools) 217 self.mkimage = self.AddBintool(btools, 'mkimage') 218 219 def CheckEntries(self): 220 pass 221 222 def ProcessContents(self): 223 # The blob may have changed due to WriteSymbols() 224 ok = super().ProcessContents() 225 data = self.BuildSectionData(True) 226 ok2 = self.ProcessContentsUpdate(data) 227 return ok and ok2 228 229 def SetImagePos(self, image_pos): 230 """Set the position in the image 231 232 This sets each subentry's offsets, sizes and positions-in-image 233 according to where they ended up in the packed mkimage file. 234 235 NOTE: This assumes a legacy mkimage and assumes that the images are 236 written to the output in order. SoC-specific mkimage handling may not 237 conform to this, in which case these values may be wrong. 238 239 Args: 240 image_pos (int): Position of this entry in the image 241 """ 242 # The mkimage header consists of 0x40 bytes, following by a table of 243 # offsets for each file 244 upto = 0x40 245 246 # Skip the 0-terminated list of offsets (assume a single image) 247 upto += 4 + 4 248 for entry in self.GetEntries().values(): 249 entry.SetOffsetSize(upto, None) 250 251 # Give up if any entries lack a size 252 if entry.size is None: 253 return 254 upto += entry.size 255 256 super().SetImagePos(image_pos) 257