From c7d8035ec185c789f8182c7d3374db4dcdbd250b Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 8 Jul 2019 13:18:28 -0600 Subject: binman: Add a --toolpath option to set the tool search path Sometimes tools used by binman may not be in the normal PATH search path, such as when the tool is built by the U-Boot build itself (e.g. mkimage). Provide a way to specify an additional search path for tools. The flag can be used multiple times. Update the help to describe this option. Signed-off-by: Simon Glass --- tools/binman/control.py | 1 + 1 file changed, 1 insertion(+) (limited to 'tools/binman/control.py') diff --git a/tools/binman/control.py b/tools/binman/control.py index 20186ee1980..df78848e13d 100644 --- a/tools/binman/control.py +++ b/tools/binman/control.py @@ -112,6 +112,7 @@ def Binman(options, args): try: tools.SetInputDirs(options.indir) tools.PrepareOutputDir(options.outdir, options.preserve) + tools.SetToolPaths(options.toolpath) state.SetEntryArgs(options.entry_arg) # Get the device tree ready by compiling it and copying the compiled -- cgit v1.2.3 From ac62fba459ad94d0d6b457a47d90628cc7b76496 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 8 Jul 2019 13:18:53 -0600 Subject: binman: Add support for CBFS entries Add support for putting CBFSs (Coreboot Filesystems) in an image. This allows binman to produce firmware images used by coreboot to boot. Signed-off-by: Simon Glass --- tools/binman/control.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'tools/binman/control.py') diff --git a/tools/binman/control.py b/tools/binman/control.py index df78848e13d..4a94afc8640 100644 --- a/tools/binman/control.py +++ b/tools/binman/control.py @@ -12,6 +12,7 @@ import os import sys import tools +import cbfs_util import command import elf from image import Image @@ -108,6 +109,7 @@ def Binman(options, args): tout.Init(options.verbosity) elf.debug = options.debug + cbfs_util.VERBOSE = options.verbosity > 2 state.use_fake_dtb = options.fake_dtb try: tools.SetInputDirs(options.indir) -- cgit v1.2.3 From 53cd5d921dd76d4651f2c99681a3c050743b6ba1 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 8 Jul 2019 14:25:29 -0600 Subject: binman: Convert to use ArgumentParser This class is the new way to handle arguments in Python. Convert binman over to use it. At the same time, introduce commands so that we can separate out the different parts of binman functionality. Signed-off-by: Simon Glass --- tools/binman/control.py | 51 ++++++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 26 deletions(-) (limited to 'tools/binman/control.py') diff --git a/tools/binman/control.py b/tools/binman/control.py index 4a94afc8640..9022cf76e99 100644 --- a/tools/binman/control.py +++ b/tools/binman/control.py @@ -67,19 +67,18 @@ def WriteEntryDocs(modules, test_missing=None): from entry import Entry Entry.WriteDocs(modules, test_missing) -def Binman(options, args): +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: - options: Command line options object - args: Command line arguments (list of strings) + args: Command line arguments Namespace object """ global images - if options.full_help: + if args.full_help: pager = os.getenv('PAGER') if not pager: pager = 'more' @@ -89,17 +88,17 @@ def Binman(options, args): return 0 # Try to figure out which device tree contains our image description - if options.dt: - dtb_fname = options.dt + if args.dt: + dtb_fname = args.dt else: - board = options.board + board = args.board if not board: raise ValueError('Must provide a board to process (use -b )') - board_pathname = os.path.join(options.build_dir, board) + board_pathname = os.path.join(args.build_dir, board) dtb_fname = os.path.join(board_pathname, 'u-boot.dtb') - if not options.indir: - options.indir = ['.'] - options.indir.append(board_pathname) + if not args.indir: + args.indir = ['.'] + args.indir.append(board_pathname) try: # Import these here in case libfdt.py is not available, in which case @@ -107,15 +106,15 @@ def Binman(options, args): import fdt import fdt_util - tout.Init(options.verbosity) - elf.debug = options.debug - cbfs_util.VERBOSE = options.verbosity > 2 - state.use_fake_dtb = options.fake_dtb + tout.Init(args.verbosity) + elf.debug = args.debug + cbfs_util.VERBOSE = args.verbosity > 2 + state.use_fake_dtb = args.fake_dtb try: - tools.SetInputDirs(options.indir) - tools.PrepareOutputDir(options.outdir, options.preserve) - tools.SetToolPaths(options.toolpath) - state.SetEntryArgs(options.entry_arg) + tools.SetInputDirs(args.indir) + tools.PrepareOutputDir(args.outdir, args.preserve) + tools.SetToolPaths(args.toolpath) + state.SetEntryArgs(args.entry_arg) # 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 @@ -132,16 +131,16 @@ def Binman(options, args): images = _ReadImageDesc(node) - if options.image: + if args.image: skip = [] new_images = OrderedDict() for name, image in images.items(): - if name in options.image: + if name in args.image: new_images[name] = image else: skip.append(name) images = new_images - if skip and options.verbosity >= 2: + if skip and args.verbosity >= 2: print('Skipping images: %s' % ', '.join(skip)) state.Prepare(images, dtb) @@ -155,7 +154,7 @@ def Binman(options, args): # entry offsets remain the same. for image in images.values(): image.ExpandEntries() - if options.update_fdt: + if args.update_fdt: image.AddMissingProperties() image.ProcessFdt(dtb) @@ -176,19 +175,19 @@ def Binman(options, args): image.CheckSize() image.CheckEntries() except Exception as e: - if options.map: + if args.map: fname = image.WriteMap() print("Wrote map file '%s' to show errors" % fname) raise image.SetImagePos() - if options.update_fdt: + if args.update_fdt: image.SetCalculatedProperties() for dtb_item in state.GetFdts(): dtb_item.Sync() image.ProcessEntryContents() image.WriteSymbols() image.BuildImage() - if options.map: + if args.map: image.WriteMap() # Write the updated FDTs to our output files -- cgit v1.2.3 From c52c9e7da809e36001d125891e594c4740235055 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 8 Jul 2019 14:25:37 -0600 Subject: binman: Allow entries to expand after packing Add support for detecting entries that change size after they have already been packed, and re-running packing when it happens. This removes the limitation that entry size cannot change after PackEntries() is called. Signed-off-by: Simon Glass --- tools/binman/control.py | 51 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 36 insertions(+), 15 deletions(-) (limited to 'tools/binman/control.py') diff --git a/tools/binman/control.py b/tools/binman/control.py index 9022cf76e99..35faf115062 100644 --- a/tools/binman/control.py +++ b/tools/binman/control.py @@ -170,21 +170,42 @@ def Binman(args): # completed and written, but that does not seem important. image.GetEntryContents() image.GetEntryOffsets() - try: - image.PackEntries() - image.CheckSize() - image.CheckEntries() - except Exception as e: - if args.map: - fname = image.WriteMap() - print("Wrote map file '%s' to show errors" % fname) - raise - image.SetImagePos() - if args.update_fdt: - image.SetCalculatedProperties() - for dtb_item in state.GetFdts(): - dtb_item.Sync() - image.ProcessEntryContents() + + # 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 = 3 + for pack_pass in range(passes): + try: + image.PackEntries() + image.CheckSize() + image.CheckEntries() + except Exception as e: + if args.map: + fname = image.WriteMap() + print("Wrote map file '%s' to show errors" % fname) + raise + image.SetImagePos() + if args.update_fdt: + image.SetCalculatedProperties() + for dtb_item in state.GetFdts(): + dtb_item.Sync() + sizes_ok = image.ProcessEntryContents() + if sizes_ok: + break + image.ResetForPack() + if not sizes_ok: + image.Raise('Entries expanded after packing (tried %s passes)' % + passes) + image.WriteSymbols() image.BuildImage() if args.map: -- cgit v1.2.3 From 61f564d15f35e5f5600ed639201b257efa09d1f1 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 8 Jul 2019 14:25:48 -0600 Subject: binman: Support listing an image Add support for listing the entries in an image. This relies on the image having an FDT map. Signed-off-by: Simon Glass --- tools/binman/control.py | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) (limited to 'tools/binman/control.py') diff --git a/tools/binman/control.py b/tools/binman/control.py index 35faf115062..813c8b1bf9e 100644 --- a/tools/binman/control.py +++ b/tools/binman/control.py @@ -67,6 +67,37 @@ def WriteEntryDocs(modules, test_missing=None): from entry import Entry Entry.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 Binman(args): """The main control code for binman @@ -87,6 +118,10 @@ def Binman(args): command.Run(pager, fname) return 0 + if args.cmd == 'ls': + ListEntries(args.image, args.paths) + return 0 + # Try to figure out which device tree contains our image description if args.dt: dtb_fname = args.dt -- cgit v1.2.3 From f667e45b1c0a7f21d433ee8f3ec18858d87dd2e5 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 8 Jul 2019 14:25:50 -0600 Subject: binman: Allow reading an entry from an image It is useful to be able to extract entry contents from an image to see what is inside. Add a simple function to read the contents of an entry, decompressing it by default. Signed-off-by: Simon Glass --- tools/binman/control.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'tools/binman/control.py') diff --git a/tools/binman/control.py b/tools/binman/control.py index 813c8b1bf9e..b244e7a0cd0 100644 --- a/tools/binman/control.py +++ b/tools/binman/control.py @@ -98,6 +98,26 @@ def ListEntries(image_fname, entry_paths): 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 + """ + image = Image.FromFile(image_fname) + entry = image.FindEntryPath(entry_path) + return entry.ReadData(decomp) + + def Binman(args): """The main control code for binman -- cgit v1.2.3 From 71ce0ba284aeb388ddcefb4f6f0056c759a758cc Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 8 Jul 2019 14:25:52 -0600 Subject: binman: Add an 'extract' command It is useful to be able to extract all binaries from the image, or a subset of them. Add a new 'extract' command to handle this. Signed-off-by: Simon Glass --- tools/binman/control.py | 60 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) (limited to 'tools/binman/control.py') diff --git a/tools/binman/control.py b/tools/binman/control.py index b244e7a0cd0..dc898be6179 100644 --- a/tools/binman/control.py +++ b/tools/binman/control.py @@ -118,6 +118,57 @@ def ReadEntry(image_fname, entry_path, decomp=True): return entry.ReadData(decomp) +def ExtractEntries(image_fname, output_fname, outdir, entry_paths, + decomp=True): + """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 compress the entry data + + Returns: + List of EntryInfo records that were written + """ + image = Image.FromFile(image_fname) + + # 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 -o') + if len(entry_paths) != 1: + raise ValueError('Must specify exactly one entry path to write with -o') + entry = image.FindEntryPath(entry_paths[0]) + data = entry.ReadData(decomp) + tools.WriteFile(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) + 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 not os.path.exists(fname): + os.makedirs(fname) + fname = os.path.join(fname, 'root') + tout.Notice("Write entry '%s' to '%s'" % (entry.GetPath(), fname)) + tools.WriteFile(fname, data) + return einfos + + def Binman(args): """The main control code for binman @@ -142,6 +193,15 @@ def Binman(args): ListEntries(args.image, args.paths) return 0 + if args.cmd == 'extract': + try: + tools.PrepareOutputDir(None) + ExtractEntries(args.image, args.filename, args.outdir, args.paths, + not args.uncompressed) + finally: + tools.FinaliseOutputDir() + return 0 + # Try to figure out which device tree contains our image description if args.dt: dtb_fname = args.dt -- cgit v1.2.3