| # SPDX-License-Identifier: GPL-2.0+ | |
| # Copyright (c) 2016 Google, Inc | |
| # Written by Simon Glass <sjg@chromium.org> | |
| # | |
| # Entry-type module for producing an image using mkimage | |
| # | |
| from collections import OrderedDict | |
| from binman.entry import Entry | |
| from binman.etype.section import Entry_section | |
| from dtoc import fdt_util | |
| from u_boot_pylib import tools | |
| class Entry_mkimage(Entry_section): | |
| """Binary produced by mkimage | |
| Properties / Entry arguments: | |
| - args: Arguments to pass | |
| - data-to-imagename: Indicates that the -d data should be passed in as | |
| the image name also (-n) | |
| - multiple-data-files: boolean to tell binman to pass all files as | |
| datafiles to mkimage instead of creating a temporary file the result | |
| of datafiles concatenation | |
| - filename: filename of output binary generated by mkimage | |
| The data passed to mkimage via the -d flag is collected from subnodes of the | |
| mkimage node, e.g.:: | |
| mkimage { | |
| filename = "imximage.bin"; | |
| args = "-n test -T imximage"; | |
| u-boot-spl { | |
| }; | |
| }; | |
| This calls mkimage to create an imximage with `u-boot-spl.bin` as the data | |
| file, with mkimage being called like this:: | |
| mkimage -d <data_file> -n test -T imximage <output_file> | |
| The output from mkimage then becomes part of the image produced by | |
| binman but also is written into `imximage.bin` file. If you need to put | |
| multiple things in the data file, you can use a section, or just multiple | |
| subnodes like this:: | |
| mkimage { | |
| args = "-n test -T imximage"; | |
| u-boot-spl { | |
| }; | |
| u-boot-tpl { | |
| }; | |
| }; | |
| Note that binman places the contents (here SPL and TPL) into a single file | |
| and passes that to mkimage using the -d option. | |
| To pass all datafiles untouched to mkimage:: | |
| mkimage { | |
| args = "-n rk3399 -T rkspi"; | |
| multiple-data-files; | |
| u-boot-tpl { | |
| }; | |
| u-boot-spl { | |
| }; | |
| }; | |
| This calls mkimage to create a Rockchip RK3399-specific first stage | |
| bootloader, made of TPL+SPL. Since this first stage bootloader requires to | |
| align the TPL and SPL but also some weird hacks that is handled by mkimage | |
| directly, binman is told to not perform the concatenation of datafiles prior | |
| to passing the data to mkimage. | |
| To use CONFIG options in the arguments, use a string list instead, as in | |
| this example which also produces four arguments:: | |
| mkimage { | |
| args = "-n", CONFIG_SYS_SOC, "-T imximage"; | |
| u-boot-spl { | |
| }; | |
| }; | |
| If you need to pass the input data in with the -n argument as well, then use | |
| the 'data-to-imagename' property:: | |
| mkimage { | |
| args = "-T imximage"; | |
| data-to-imagename; | |
| u-boot-spl { | |
| }; | |
| }; | |
| That will pass the data to mkimage both as the data file (with -d) and as | |
| the image name (with -n). In both cases, a filename is passed as the | |
| argument, with the actual data being in that file. | |
| If need to pass different data in with -n, then use an `imagename` subnode:: | |
| mkimage { | |
| args = "-T imximage"; | |
| imagename { | |
| blob { | |
| filename = "spl/u-boot-spl.cfgout" | |
| }; | |
| }; | |
| u-boot-spl { | |
| }; | |
| }; | |
| This will pass in u-boot-spl as the input data and the .cfgout file as the | |
| -n data. | |
| """ | |
| def __init__(self, section, etype, node): | |
| super().__init__(section, etype, node) | |
| self._imagename = None | |
| self._multiple_data_files = False | |
| def ReadNode(self): | |
| super().ReadNode() | |
| self._multiple_data_files = fdt_util.GetBool(self._node, | |
| 'multiple-data-files') | |
| self._args = fdt_util.GetArgs(self._node, 'args') | |
| self._data_to_imagename = fdt_util.GetBool(self._node, | |
| 'data-to-imagename') | |
| if self._data_to_imagename and self._node.FindNode('imagename'): | |
| self.Raise('Cannot use both imagename node and data-to-imagename') | |
| def ReadEntries(self): | |
| """Read the subnodes to find out what should go in this image""" | |
| for node in self._node.subnodes: | |
| if self.IsSpecialSubnode(node): | |
| continue | |
| entry = Entry.Create(self, node, | |
| expanded=self.GetImage().use_expanded, | |
| missing_etype=self.GetImage().missing_etype) | |
| entry.ReadNode() | |
| entry.SetPrefix(self._name_prefix) | |
| if entry.name == 'imagename': | |
| self._imagename = entry | |
| else: | |
| self._entries[entry.name] = entry | |
| def BuildSectionData(self, required): | |
| """Build mkimage entry contents | |
| Runs mkimage to build the entry contents | |
| Args: | |
| required (bool): True if the data must be present, False if it is OK | |
| to return None | |
| Returns: | |
| bytes: Contents of the section | |
| """ | |
| # Use a non-zero size for any fake files to keep mkimage happy | |
| # Note that testMkimageImagename() relies on this 'mkimage' parameter | |
| fake_size = 1024 | |
| if self._multiple_data_files: | |
| fnames = [] | |
| uniq = self.GetUniqueName() | |
| for entry in self._entries.values(): | |
| # Put the contents in a temporary file | |
| ename = f'mkimage-in-{uniq}-{entry.name}' | |
| fname = tools.get_output_filename(ename) | |
| data = entry.GetData(required) | |
| tools.write_file(fname, data) | |
| fnames.append(fname) | |
| input_fname = ":".join(fnames) | |
| data = b'' | |
| else: | |
| data, input_fname, uniq = self.collect_contents_to_file( | |
| self._entries.values(), 'mkimage', fake_size) | |
| if self._imagename: | |
| image_data, imagename_fname, _ = self.collect_contents_to_file( | |
| [self._imagename], 'mkimage-n', 1024) | |
| outfile = self._filename if self._filename else 'mkimage-out.%s' % uniq | |
| output_fname = tools.get_output_filename(outfile) | |
| missing_list = [] | |
| self.CheckMissing(missing_list) | |
| self.missing = bool(missing_list) | |
| if self.missing: | |
| return b'' | |
| args = ['-d', input_fname] | |
| if self._data_to_imagename: | |
| args += ['-n', input_fname] | |
| elif self._imagename: | |
| args += ['-n', imagename_fname] | |
| args += self._args + [output_fname] | |
| if self.mkimage.run_cmd(*args) is not None: | |
| return tools.read_file(output_fname) | |
| else: | |
| # Bintool is missing; just use the input data as the output | |
| self.record_missing_bintool(self.mkimage) | |
| return data | |
| def GetEntries(self): | |
| # Make a copy so we don't change the original | |
| entries = OrderedDict(self._entries) | |
| if self._imagename: | |
| entries['imagename'] = self._imagename | |
| return entries | |
| def AddBintools(self, btools): | |
| super().AddBintools(btools) | |
| self.mkimage = self.AddBintool(btools, 'mkimage') | |
| def CheckEntries(self): | |
| pass | |
| def ProcessContents(self): | |
| # The blob may have changed due to WriteSymbols() | |
| ok = super().ProcessContents() | |
| data = self.BuildSectionData(True) | |
| ok2 = self.ProcessContentsUpdate(data) | |
| return ok and ok2 | |
| def SetImagePos(self, image_pos): | |
| """Set the position in the image | |
| This sets each subentry's offsets, sizes and positions-in-image | |
| according to where they ended up in the packed mkimage file. | |
| NOTE: This assumes a legacy mkimage and assumes that the images are | |
| written to the output in order. SoC-specific mkimage handling may not | |
| conform to this, in which case these values may be wrong. | |
| Args: | |
| image_pos (int): Position of this entry in the image | |
| """ | |
| # The mkimage header consists of 0x40 bytes, following by a table of | |
| # offsets for each file | |
| upto = 0x40 | |
| # Skip the 0-terminated list of offsets (assume a single image) | |
| upto += 4 + 4 | |
| for entry in self.GetEntries().values(): | |
| entry.SetOffsetSize(upto, None) | |
| # Give up if any entries lack a size | |
| if entry.size is None: | |
| return | |
| upto += entry.size | |
| super().SetImagePos(image_pos) |