diff options
Diffstat (limited to 'tools/binman/control.py')
-rw-r--r-- | tools/binman/control.py | 976 |
1 files changed, 976 insertions, 0 deletions
diff --git a/tools/binman/control.py b/tools/binman/control.py new file mode 100644 index 00000000000..1307222591d --- /dev/null +++ b/tools/binman/control.py @@ -0,0 +1,976 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2016 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# +# Creates binary images from input files controlled by a description +# + +from collections import OrderedDict +import glob +try: + import importlib.resources as importlib_resources +except ImportError: # pragma: no cover + # for Python 3.6 + import importlib_resources +import os +import re + +import sys + +from binman import bintool +from binman import cbfs_util +from binman import elf +from binman import entry +from dtoc import fdt_util +from u_boot_pylib import command +from u_boot_pylib import tools +from u_boot_pylib import tout + +# These are imported if needed since they import libfdt +state = None +Image = None + +# List of images we plan to create +# Make this global so that it can be referenced from tests +images = OrderedDict() + +# Help text for each type of missing blob, dict: +# key: Value of the entry's 'missing-msg' or entry name +# value: Text for the help +missing_blob_help = {} + +def _ReadImageDesc(binman_node, use_expanded): + """Read the image descriptions from the /binman node + + This normally produces a single Image object called 'image'. But if + multiple images are present, they will all be returned. + + Args: + binman_node: Node object of the /binman node + use_expanded: True if the FDT will be updated with the entry information + Returns: + OrderedDict of Image objects, each of which describes an image + """ + # For Image() + # pylint: disable=E1102 + images = OrderedDict() + if 'multiple-images' in binman_node.props: + for node in binman_node.subnodes: + if not node.name.startswith('template'): + images[node.name] = Image(node.name, node, + use_expanded=use_expanded) + else: + images['image'] = Image('image', binman_node, use_expanded=use_expanded) + return images + +def _FindBinmanNode(dtb): + """Find the 'binman' node in the device tree + + Args: + dtb: Fdt object to scan + Returns: + Node object of /binman node, or None if not found + """ + for node in dtb.GetRoot().subnodes: + if node.name == 'binman': + return node + return None + +def _ReadMissingBlobHelp(): + """Read the missing-blob-help file + + This file containins help messages explaining what to do when external blobs + are missing. + + Returns: + Dict: + key: Message tag (str) + value: Message text (str) + """ + + def _FinishTag(tag, msg, result): + if tag: + result[tag] = msg.rstrip() + tag = None + msg = '' + return tag, msg + + my_data = importlib_resources.files(__package__).joinpath('missing-blob-help').read_bytes() + re_tag = re.compile(r"^([-\.a-z0-9]+):$") + result = {} + tag = None + msg = '' + for line in my_data.decode('utf-8').splitlines(): + if not line.startswith('#'): + m_tag = re_tag.match(line) + if m_tag: + _, msg = _FinishTag(tag, msg, result) + tag = m_tag.group(1) + elif tag: + msg += line + '\n' + _FinishTag(tag, msg, result) + return result + +def _ShowBlobHelp(level, path, text, fname): + tout.do_output(level, '%s (%s):' % (path, fname)) + for line in text.splitlines(): + tout.do_output(level, ' %s' % line) + tout.do_output(level, '') + +def _ShowHelpForMissingBlobs(level, missing_list): + """Show help for each missing blob to help the user take action + + Args: + missing_list: List of Entry objects to show help for + """ + global missing_blob_help + + if not missing_blob_help: + missing_blob_help = _ReadMissingBlobHelp() + + for entry in missing_list: + tags = entry.GetHelpTags() + + # Show the first match help message + shown_help = False + for tag in tags: + if tag in missing_blob_help: + _ShowBlobHelp(level, entry._node.path, missing_blob_help[tag], + entry.GetDefaultFilename()) + shown_help = True + break + # Or a generic help message + if not shown_help: + _ShowBlobHelp(level, entry._node.path, "Missing blob", + entry.GetDefaultFilename()) + +def GetEntryModules(include_testing=True): + """Get a set of entry class implementations + + Returns: + Set of paths to entry class filenames + """ + entries = importlib_resources.files(__package__).joinpath('etype') + glob_list = [entry.name for entry in entries.iterdir() + if entry.name.endswith('.py') and entry.is_file()] + return set([os.path.splitext(os.path.basename(item))[0] + for item in glob_list + if include_testing or '_testing' not in item]) + +def WriteEntryDocs(modules, test_missing=None): + """Write out documentation for all entries + + Args: + modules: List of Module objects to get docs for + test_missing: Used for testing only, to force an entry's documentation + to show as missing even if it is present. Should be set to None in + normal use. + """ + from binman.entry import Entry + Entry.WriteDocs(modules, test_missing) + + +def write_bintool_docs(modules, test_missing=None): + """Write out documentation for all bintools + + Args: + modules: List of Module objects to get docs for + test_missing: Used for testing only, to force an entry's documentation + to show as missing even if it is present. Should be set to None in + normal use. + """ + bintool.Bintool.WriteDocs(modules, test_missing) + + +def ListEntries(image_fname, entry_paths): + """List the entries in an image + + This decodes the supplied image and displays a table of entries from that + image, preceded by a header. + + Args: + image_fname: Image filename to process + entry_paths: List of wildcarded paths (e.g. ['*dtb*', 'u-boot*', + 'section/u-boot']) + """ + image = Image.FromFile(image_fname) + + entries, lines, widths = image.GetListEntries(entry_paths) + + num_columns = len(widths) + for linenum, line in enumerate(lines): + if linenum == 1: + # Print header line + print('-' * (sum(widths) + num_columns * 2)) + out = '' + for i, item in enumerate(line): + width = -widths[i] + if item.startswith('>'): + width = -width + item = item[1:] + txt = '%*s ' % (width, item) + out += txt + print(out.rstrip()) + + +def ReadEntry(image_fname, entry_path, decomp=True): + """Extract an entry from an image + + This extracts the data from a particular entry in an image + + Args: + image_fname: Image filename to process + entry_path: Path to entry to extract + decomp: True to return uncompressed data, if the data is compress + False to return the raw data + + Returns: + data extracted from the entry + """ + global Image + from binman.image import Image + + image = Image.FromFile(image_fname) + image.CollectBintools() + entry = image.FindEntryPath(entry_path) + return entry.ReadData(decomp) + + +def ShowAltFormats(image): + """Show alternative formats available for entries in the image + + This shows a list of formats available. + + Args: + image (Image): Image to check + """ + alt_formats = {} + image.CheckAltFormats(alt_formats) + print('%-10s %-20s %s' % ('Flag (-F)', 'Entry type', 'Description')) + for name, val in alt_formats.items(): + entry, helptext = val + print('%-10s %-20s %s' % (name, entry.etype, helptext)) + + +def ExtractEntries(image_fname, output_fname, outdir, entry_paths, + decomp=True, alt_format=None): + """Extract the data from one or more entries and write it to files + + Args: + image_fname: Image filename to process + output_fname: Single output filename to use if extracting one file, None + otherwise + outdir: Output directory to use (for any number of files), else None + entry_paths: List of entry paths to extract + decomp: True to decompress the entry data + + Returns: + List of EntryInfo records that were written + """ + image = Image.FromFile(image_fname) + image.CollectBintools() + + if alt_format == 'list': + ShowAltFormats(image) + return + + # Output an entry to a single file, as a special case + if output_fname: + if not entry_paths: + raise ValueError('Must specify an entry path to write with -f') + if len(entry_paths) != 1: + raise ValueError('Must specify exactly one entry path to write with -f') + entry = image.FindEntryPath(entry_paths[0]) + data = entry.ReadData(decomp, alt_format) + tools.write_file(output_fname, data) + tout.notice("Wrote %#x bytes to file '%s'" % (len(data), output_fname)) + return + + # Otherwise we will output to a path given by the entry path of each entry. + # This means that entries will appear in subdirectories if they are part of + # a sub-section. + einfos = image.GetListEntries(entry_paths)[0] + tout.notice('%d entries match and will be written' % len(einfos)) + for einfo in einfos: + entry = einfo.entry + data = entry.ReadData(decomp, alt_format) + path = entry.GetPath()[1:] + fname = os.path.join(outdir, path) + + # If this entry has children, create a directory for it and put its + # data in a file called 'root' in that directory + if entry.GetEntries(): + if fname and not os.path.exists(fname): + os.makedirs(fname) + fname = os.path.join(fname, 'root') + tout.notice("Write entry '%s' size %x to '%s'" % + (entry.GetPath(), len(data), fname)) + tools.write_file(fname, data) + return einfos + + +def BeforeReplace(image, allow_resize): + """Handle getting an image ready for replacing entries in it + + Args: + image: Image to prepare + """ + state.PrepareFromLoadedData(image) + image.CollectBintools() + image.LoadData(decomp=False) + + # If repacking, drop the old offset/size values except for the original + # ones, so we are only left with the constraints. + if image.allow_repack and allow_resize: + image.ResetForPack() + + +def ReplaceOneEntry(image, entry, data, do_compress, allow_resize): + """Handle replacing a single entry an an image + + Args: + image: Image to update + entry: Entry to write + data: Data to replace with + do_compress: True to compress the data if needed, False if data is + already compressed so should be used as is + allow_resize: True to allow entries to change size (this does a re-pack + of the entries), False to raise an exception + """ + if not entry.WriteData(data, do_compress): + if not image.allow_repack: + entry.Raise('Entry data size does not match, but allow-repack is not present for this image') + if not allow_resize: + entry.Raise('Entry data size does not match, but resize is disabled') + + +def AfterReplace(image, allow_resize, write_map): + """Handle write out an image after replacing entries in it + + Args: + image: Image to write + allow_resize: True to allow entries to change size (this does a re-pack + of the entries), False to raise an exception + write_map: True to write a map file + """ + tout.info('Processing image') + ProcessImage(image, update_fdt=True, write_map=write_map, + get_contents=False, allow_resize=allow_resize) + + +def WriteEntryToImage(image, entry, data, do_compress=True, allow_resize=True, + write_map=False): + BeforeReplace(image, allow_resize) + tout.info('Writing data to %s' % entry.GetPath()) + ReplaceOneEntry(image, entry, data, do_compress, allow_resize) + AfterReplace(image, allow_resize=allow_resize, write_map=write_map) + + +def WriteEntry(image_fname, entry_path, data, do_compress=True, + allow_resize=True, write_map=False): + """Replace an entry in an image + + This replaces the data in a particular entry in an image. This size of the + new data must match the size of the old data unless allow_resize is True. + + Args: + image_fname: Image filename to process + entry_path: Path to entry to extract + data: Data to replace with + do_compress: True to compress the data if needed, False if data is + already compressed so should be used as is + allow_resize: True to allow entries to change size (this does a re-pack + of the entries), False to raise an exception + write_map: True to write a map file + + Returns: + Image object that was updated + """ + tout.info("Write entry '%s', file '%s'" % (entry_path, image_fname)) + image = Image.FromFile(image_fname) + image.CollectBintools() + entry = image.FindEntryPath(entry_path) + WriteEntryToImage(image, entry, data, do_compress=do_compress, + allow_resize=allow_resize, write_map=write_map) + + return image + + +def ReplaceEntries(image_fname, input_fname, indir, entry_paths, + do_compress=True, allow_resize=True, write_map=False): + """Replace the data from one or more entries from input files + + Args: + image_fname: Image filename to process + input_fname: Single input filename to use if replacing one file, None + otherwise + indir: Input directory to use (for any number of files), else None + entry_paths: List of entry paths to replace + do_compress: True if the input data is uncompressed and may need to be + compressed if the entry requires it, False if the data is already + compressed. + write_map: True to write a map file + + Returns: + List of EntryInfo records that were written + """ + image_fname = os.path.abspath(image_fname) + image = Image.FromFile(image_fname) + + image.mark_build_done() + + # Replace an entry from a single file, as a special case + if input_fname: + if not entry_paths: + raise ValueError('Must specify an entry path to read with -f') + if len(entry_paths) != 1: + raise ValueError('Must specify exactly one entry path to write with -f') + entry = image.FindEntryPath(entry_paths[0]) + data = tools.read_file(input_fname) + tout.notice("Read %#x bytes from file '%s'" % (len(data), input_fname)) + WriteEntryToImage(image, entry, data, do_compress=do_compress, + allow_resize=allow_resize, write_map=write_map) + return + + # Otherwise we will input from a path given by the entry path of each entry. + # This means that files must appear in subdirectories if they are part of + # a sub-section. + einfos = image.GetListEntries(entry_paths)[0] + tout.notice("Replacing %d matching entries in image '%s'" % + (len(einfos), image_fname)) + + BeforeReplace(image, allow_resize) + + for einfo in einfos: + entry = einfo.entry + if entry.GetEntries(): + tout.info("Skipping section entry '%s'" % entry.GetPath()) + continue + + path = entry.GetPath()[1:] + fname = os.path.join(indir, path) + + if os.path.exists(fname): + tout.notice("Write entry '%s' from file '%s'" % + (entry.GetPath(), fname)) + data = tools.read_file(fname) + ReplaceOneEntry(image, entry, data, do_compress, allow_resize) + else: + tout.warning("Skipping entry '%s' from missing file '%s'" % + (entry.GetPath(), fname)) + + AfterReplace(image, allow_resize=allow_resize, write_map=write_map) + return image + +def SignEntries(image_fname, input_fname, privatekey_fname, algo, entry_paths, + write_map=False): + """Sign and replace the data from one or more entries from input files + + Args: + image_fname: Image filename to process + input_fname: Single input filename to use if replacing one file, None + otherwise + algo: Hashing algorithm + entry_paths: List of entry paths to sign + privatekey_fname: Private key filename + write_map (bool): True to write the map file + """ + image_fname = os.path.abspath(image_fname) + image = Image.FromFile(image_fname) + + image.mark_build_done() + + BeforeReplace(image, allow_resize=True) + + for entry_path in entry_paths: + entry = image.FindEntryPath(entry_path) + entry.UpdateSignatures(privatekey_fname, algo, input_fname) + + AfterReplace(image, allow_resize=True, write_map=write_map) + +def _ProcessTemplates(parent): + """Handle any templates in the binman description + + Args: + parent: Binman node to process (typically /binman) + + Returns: + bool: True if any templates were processed + + Search though each target node looking for those with an 'insert-template' + property. Use that as a list of references to template nodes to use to + adjust the target node. + + Processing involves copying each subnode of the template node into the + target node. + + This is done recursively, so templates can be at any level of the binman + image, e.g. inside a section. + + See 'Templates' in the Binman documnentation for details. + """ + found = False + for node in parent.subnodes: + tmpl = fdt_util.GetPhandleList(node, 'insert-template') + if tmpl: + node.copy_subnodes_from_phandles(tmpl) + found = True + + found |= _ProcessTemplates(node) + return found + +def _RemoveTemplates(parent): + """Remove any templates in the binman description + """ + del_nodes = [] + for node in parent.subnodes: + if node.name.startswith('template'): + del_nodes.append(node) + + for node in del_nodes: + node.Delete() + +def propagate_prop(node, prop): + """Propagate the provided property to all the parent nodes up the hierarchy + + Args: + node (fdt.Node): Node and all its parent nodes up to the root to + propagate the property. + prop (str): Boolean property to propagate + + Return: + True if any change was made, else False + """ + changed = False + while node: + if prop not in node.props: + node.AddEmptyProp(prop, 0) + changed = True + node = node.parent + return changed + +def scan_and_prop_bootph(node): + """Propagate bootph properties from children to parents + + The bootph schema indicates that bootph properties in children should be + implied in their parents, all the way up the hierarchy. This is expensive + to implement in U-Boot before relocation at runtime, so this function + explicitly propagates these bootph properties upwards during build time. + + This is used to set the bootph-all, bootph-some-ram property in the parent + node if the respective property is found in any of the parent's subnodes. + The other bootph-* properties are associated with the SPL stage and hence + handled by fdtgrep.c. + + Args: + node (fdt.Node): Node to scan for bootph-all and bootph-some-ram + property + + Return: + True if any change was made, else False + + """ + bootph_prop = {'bootph-all', 'bootph-some-ram'} + + changed = False + for prop in bootph_prop: + if prop in node.props: + changed |= propagate_prop(node.parent, prop) + + for subnode in node.subnodes: + changed |= scan_and_prop_bootph(subnode) + return changed + +def PrepareImagesAndDtbs(dtb_fname, select_images, update_fdt, use_expanded, indir): + """Prepare the images to be processed and select the device tree + + This function: + - reads in the device tree + - finds and scans the binman node to create all entries + - selects which images to build + - Updates the device tress with placeholder properties for offset, + image-pos, etc. + + Args: + dtb_fname: Filename of the device tree file to use (.dts or .dtb) + selected_images: List of images to output, or None for all + update_fdt: True to update the FDT wth entry offsets, etc. + use_expanded: True to use expanded versions of entries, if available. + So if 'u-boot' is called for, we use 'u-boot-expanded' instead. This + is needed if update_fdt is True (although tests may disable it) + indir: List of directories where input files can be found + + Returns: + OrderedDict of images: + key: Image name (str) + value: Image object + """ + # Import these here in case libfdt.py is not available, in which case + # the above help option still works. + from dtoc import fdt + from dtoc import fdt_util + global images + + # Get the device tree ready by compiling it and copying the compiled + # output into a file in our output directly. Then scan it for use + # in binman. + if indir is None: + indir = [] + dtb_fname = fdt_util.EnsureCompiled(dtb_fname, indir=indir) + fname = tools.get_output_filename('u-boot.dtb.out') + tools.write_file(fname, tools.read_file(dtb_fname)) + dtb = fdt.FdtScan(fname) + + node = _FindBinmanNode(dtb) + if not node: + raise ValueError("Device tree '%s' does not have a 'binman' " + "node" % dtb_fname) + + if _ProcessTemplates(node): + dtb.Sync(True) + fname = tools.get_output_filename('u-boot.dtb.tmpl1') + tools.write_file(fname, dtb.GetContents()) + + _RemoveTemplates(node) + dtb.Sync(True) + + # Rescan the dtb to pick up the new phandles + dtb.Scan() + node = _FindBinmanNode(dtb) + fname = tools.get_output_filename('u-boot.dtb.tmpl2') + tools.write_file(fname, dtb.GetContents()) + + if scan_and_prop_bootph(dtb.GetRoot()): + dtb.Sync(True) + + images = _ReadImageDesc(node, use_expanded) + + if select_images: + skip = [] + new_images = OrderedDict() + for name, image in images.items(): + if name in select_images: + new_images[name] = image + else: + skip.append(name) + images = new_images + tout.notice('Skipping images: %s' % ', '.join(skip)) + + state.Prepare(images, dtb) + + # Prepare the device tree by making sure that any missing + # properties are added (e.g. 'pos' and 'size'). The values of these + # may not be correct yet, but we add placeholders so that the + # size of the device tree is correct. Later, in + # SetCalculatedProperties() we will insert the correct values + # without changing the device-tree size, thus ensuring that our + # entry offsets remain the same. + for image in images.values(): + image.gen_entries() + image.CollectBintools() + if update_fdt: + image.AddMissingProperties(True) + image.ProcessFdt(dtb) + + for dtb_item in state.GetAllFdts(): + dtb_item.Sync(auto_resize=True) + dtb_item.Pack() + dtb_item.Flush() + return images + +def CheckForProblems(image): + """Check for problems with image generation + + Shows warning about missing, faked or optional external blobs, as well as + missing bintools. + + Args: + image (Image): Image to process + + Returns: + bool: True if there are any problems which result in a non-functional + image + """ + missing_list = [] + image.CheckMissing(missing_list) + if missing_list: + tout.error("Image '%s' is missing external blobs and is non-functional: %s\n" % + (image.name, ' '.join([e.name for e in missing_list]))) + _ShowHelpForMissingBlobs(tout.ERROR, missing_list) + + faked_list = [] + faked_optional_list = [] + faked_required_list = [] + image.CheckFakedBlobs(faked_list) + for e in faked_list: + if e.optional: + faked_optional_list.append(e) + else: + faked_required_list.append(e) + if faked_required_list: + tout.warning( + "Image '%s' has faked external blobs and is non-functional: %s\n" % + (image.name, ' '.join([os.path.basename(e.GetDefaultFilename()) + for e in faked_required_list]))) + + optional_list = [] + # For optional blobs, we should inform the user when the blob is not present. This will come as + # a warning since it may not be immediately apparent that something is missing otherwise. + # E.g. user thinks they supplied a blob, but there is no info of the contrary if they made an + # error. + # Faked optional blobs are not relevant for final images (as they are dropped anyway) so we + # will omit the message with default verbosity. + image.CheckOptional(optional_list) + if optional_list: + tout.warning( + "Image '%s' is missing optional external blobs but is still functional: %s\n" % + (image.name, ' '.join([e.name for e in optional_list]))) + _ShowHelpForMissingBlobs(tout.WARNING, optional_list) + + if faked_optional_list: + tout.info( + "Image '%s' has faked optional external blobs but is still functional: %s\n" % + (image.name, ' '.join([os.path.basename(e.GetDefaultFilename()) + for e in faked_optional_list]))) + + missing_bintool_list = [] + image.check_missing_bintools(missing_bintool_list) + if missing_bintool_list: + tout.warning( + "Image '%s' has missing bintools and is non-functional: %s\n" % + (image.name, ' '.join([os.path.basename(bintool.name) + for bintool in missing_bintool_list]))) + return any([missing_list, faked_required_list, missing_bintool_list]) + +def ProcessImage(image, update_fdt, write_map, get_contents=True, + allow_resize=True, allow_missing=False, + allow_fake_blobs=False): + """Perform all steps for this image, including checking and # writing it. + + This means that errors found with a later image will be reported after + earlier images are already completed and written, but that does not seem + important. + + Args: + image: Image to process + update_fdt: True to update the FDT wth entry offsets, etc. + write_map: True to write a map file + get_contents: True to get the image contents from files, etc., False if + the contents is already present + allow_resize: True to allow entries to change size (this does a re-pack + of the entries), False to raise an exception + allow_missing: Allow blob_ext objects to be missing + allow_fake_blobs: Allow blob_ext objects to be faked with dummy files + + Returns: + True if one or more external blobs are missing or faked, + False if all are present + """ + if get_contents: + image.SetAllowMissing(allow_missing) + image.SetAllowFakeBlob(allow_fake_blobs) + image.GetEntryContents() + image.GetEntryOffsets() + + # We need to pack the entries to figure out where everything + # should be placed. This sets the offset/size of each entry. + # However, after packing we call ProcessEntryContents() which + # may result in an entry changing size. In that case we need to + # do another pass. Since the device tree often contains the + # final offset/size information we try to make space for this in + # AddMissingProperties() above. However, if the device is + # compressed we cannot know this compressed size in advance, + # since changing an offset from 0x100 to 0x104 (for example) can + # alter the compressed size of the device tree. So we need a + # third pass for this. + passes = 5 + for pack_pass in range(passes): + try: + image.PackEntries() + except Exception as e: + if write_map: + fname = image.WriteMap() + print("Wrote map file '%s' to show errors" % fname) + raise + image.SetImagePos() + if update_fdt: + image.SetCalculatedProperties() + for dtb_item in state.GetAllFdts(): + dtb_item.Sync() + dtb_item.Flush() + image.WriteSymbols() + sizes_ok = image.ProcessEntryContents() + if sizes_ok: + break + image.ResetForPack() + tout.info('Pack completed after %d pass(es)' % (pack_pass + 1)) + if not sizes_ok: + image.Raise('Entries changed size after packing (tried %s passes)' % + passes) + + has_problems = CheckForProblems(image) + + image.BuildImage() + if write_map: + image.WriteMap() + + image.WriteAlternates() + + return has_problems + +def Binman(args): + """The main control code for binman + + This assumes that help and test options have already been dealt with. It + deals with the core task of building images. + + Args: + args: Command line arguments Namespace object + """ + global Image + global state + + if args.full_help: + with importlib_resources.path('binman', 'README.rst') as readme: + tools.print_full_help(str(readme)) + return 0 + + # Put these here so that we can import this module without libfdt + from binman.image import Image + from binman import state + + tool_paths = [] + if args.toolpath: + tool_paths += args.toolpath + if args.tooldir: + tool_paths.append(args.tooldir) + tools.set_tool_paths(tool_paths or None) + bintool.Bintool.set_tool_dir(args.tooldir) + + if args.cmd in ['ls', 'extract', 'replace', 'tool', 'sign']: + try: + tout.init(args.verbosity + 1) + if args.cmd == 'replace': + tools.prepare_output_dir(args.outdir, args.preserve) + else: + tools.prepare_output_dir(None) + if args.cmd == 'ls': + ListEntries(args.image, args.paths) + + if args.cmd == 'extract': + ExtractEntries(args.image, args.filename, args.outdir, args.paths, + not args.uncompressed, args.format) + + if args.cmd == 'replace': + ReplaceEntries(args.image, args.filename, args.indir, args.paths, + do_compress=not args.compressed, + allow_resize=not args.fix_size, write_map=args.map) + + if args.cmd == 'sign': + SignEntries(args.image, args.file, args.key, args.algo, args.paths) + + if args.cmd == 'tool': + if args.list: + bintool.Bintool.list_all() + elif args.fetch: + if not args.bintools: + raise ValueError( + "Please specify bintools to fetch or 'all' or 'missing'") + bintool.Bintool.fetch_tools(bintool.FETCH_ANY, + args.bintools) + else: + raise ValueError("Invalid arguments to 'tool' subcommand") + except: + raise + finally: + tools.finalise_output_dir() + return 0 + + elf_params = None + if args.update_fdt_in_elf: + elf_params = args.update_fdt_in_elf.split(',') + if len(elf_params) != 4: + raise ValueError('Invalid args %s to --update-fdt-in-elf: expected infile,outfile,begin_sym,end_sym' % + elf_params) + + # Try to figure out which device tree contains our image description + if args.dt: + dtb_fname = args.dt + else: + board = args.board + if not board: + raise ValueError('Must provide a board to process (use -b <board>)') + board_pathname = os.path.join(args.build_dir, board) + dtb_fname = os.path.join(board_pathname, 'u-boot.dtb') + if not args.indir: + args.indir = ['.'] + args.indir.append(board_pathname) + + try: + tout.init(args.verbosity + 1) + elf.debug = args.debug + cbfs_util.VERBOSE = args.verbosity > tout.NOTICE + state.use_fake_dtb = args.fake_dtb + + # Normally we replace the 'u-boot' etype with 'u-boot-expanded', etc. + # When running tests this can be disabled using this flag. When not + # updating the FDT in image, it is not needed by binman, but we use it + # for consistency, so that the images look the same to U-Boot at + # runtime. + use_expanded = not args.no_expanded + try: + tools.set_input_dirs(args.indir) + tools.prepare_output_dir(args.outdir, args.preserve) + state.SetEntryArgs(args.entry_arg) + state.SetThreads(args.threads) + + images = PrepareImagesAndDtbs(dtb_fname, args.image, + args.update_fdt, use_expanded, args.indir) + + if args.test_section_timeout: + # Set the first image to timeout, used in testThreadTimeout() + images[list(images.keys())[0]].test_section_timeout = True + invalid = False + bintool.Bintool.set_missing_list( + args.force_missing_bintools.split(',') if + args.force_missing_bintools else None) + + # Create the directory here instead of Entry.check_fake_fname() + # since that is called from a threaded context so different threads + # may race to create the directory + if args.fake_ext_blobs: + entry.Entry.create_fake_dir() + + for image in images.values(): + invalid |= ProcessImage(image, args.update_fdt, args.map, + allow_missing=args.allow_missing, + allow_fake_blobs=args.fake_ext_blobs) + + # Write the updated FDTs to our output files + for dtb_item in state.GetAllFdts(): + tools.write_file(dtb_item._fname, dtb_item.GetContents()) + + if elf_params: + data = state.GetFdtForEtype('u-boot-dtb').GetContents() + elf.UpdateFile(*elf_params, data) + + bintool.Bintool.set_missing_list(None) + + # This can only be True if -M is provided, since otherwise binman + # would have raised an error already + if invalid: + msg = 'Some images are invalid' + if args.ignore_missing: + tout.warning(msg) + else: + tout.error(msg) + return 103 + + # Use this to debug the time take to pack the image + #state.TimingShow() + finally: + tools.finalise_output_dir() + finally: + tout.uninit() + + return 0 |