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