summaryrefslogtreecommitdiff
path: root/tools/binman/control.py
diff options
context:
space:
mode:
Diffstat (limited to 'tools/binman/control.py')
-rw-r--r--tools/binman/control.py408
1 files changed, 303 insertions, 105 deletions
diff --git a/tools/binman/control.py b/tools/binman/control.py
index dc898be617..9e7587864c 100644
--- a/tools/binman/control.py
+++ b/tools/binman/control.py
@@ -128,7 +128,7 @@ def ExtractEntries(image_fname, output_fname, outdir, entry_paths,
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
+ decomp: True to decompress the entry data
Returns:
List of EntryInfo records that were written
@@ -138,9 +138,9 @@ def ExtractEntries(image_fname, output_fname, outdir, entry_paths,
# 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')
+ 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 -o')
+ raise ValueError('Must specify exactly one entry path to write with -f')
entry = image.FindEntryPath(entry_paths[0])
data = entry.ReadData(decomp)
tools.WriteFile(output_fname, data)
@@ -169,6 +169,287 @@ def ExtractEntries(image_fname, output_fname, outdir, entry_paths,
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.LoadData()
+
+ # If repacking, drop the old offset/size values except for the original
+ # ones, so we are only left with the constraints.
+ if 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)
+ 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 ilename 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 extract
+ 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 = Image.FromFile(image_fname)
+
+ # 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.ReadFile(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.ReadFile(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 PrepareImagesAndDtbs(dtb_fname, select_images, update_fdt):
+ """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.
+ """
+ # Import these here in case libfdt.py is not available, in which case
+ # the above help option still works.
+ import fdt
+ 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.
+ dtb_fname = fdt_util.EnsureCompiled(dtb_fname)
+ fname = tools.GetOutputFilename('u-boot.dtb.out')
+ tools.WriteFile(fname, tools.ReadFile(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)
+
+ images = _ReadImageDesc(node)
+
+ 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.ExpandEntries()
+ if update_fdt:
+ image.AddMissingProperties()
+ image.ProcessFdt(dtb)
+
+ for dtb_item in state.GetAllFdts():
+ dtb_item.Sync(auto_resize=True)
+ dtb_item.Pack()
+ dtb_item.Flush()
+ return images
+
+
+def ProcessImage(image, update_fdt, write_map, get_contents=True,
+ allow_resize=True):
+ """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
+ """
+ if get_contents:
+ 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()
+ image.CheckSize()
+ image.CheckEntries()
+ 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()
+ sizes_ok = image.ProcessEntryContents()
+ if sizes_ok:
+ break
+ image.ResetForPack()
+ if not sizes_ok:
+ image.Raise('Entries changed size after packing (tried %s passes)' %
+ passes)
+
+ image.WriteSymbols()
+ image.BuildImage()
+ if write_map:
+ image.WriteMap()
+
+
def Binman(args):
"""The main control code for binman
@@ -178,8 +459,6 @@ def Binman(args):
Args:
args: Command line arguments Namespace object
"""
- global images
-
if args.full_help:
pager = os.getenv('PAGER')
if not pager:
@@ -190,7 +469,11 @@ def Binman(args):
return 0
if args.cmd == 'ls':
- ListEntries(args.image, args.paths)
+ try:
+ tools.PrepareOutputDir(None)
+ ListEntries(args.image, args.paths)
+ finally:
+ tools.FinaliseOutputDir()
return 0
if args.cmd == 'extract':
@@ -202,6 +485,16 @@ def Binman(args):
tools.FinaliseOutputDir()
return 0
+ if args.cmd == 'replace':
+ try:
+ tools.PrepareOutputDir(None)
+ ReplaceEntries(args.image, args.filename, args.indir, args.paths,
+ do_compress=not args.compressed,
+ allow_resize=not args.fix_size, write_map=args.map)
+ finally:
+ tools.FinaliseOutputDir()
+ return 0
+
# Try to figure out which device tree contains our image description
if args.dt:
dtb_fname = args.dt
@@ -216,11 +509,6 @@ def Binman(args):
args.indir.append(board_pathname)
try:
- # Import these here in case libfdt.py is not available, in which case
- # the above help option still works.
- import fdt
- import fdt_util
-
tout.Init(args.verbosity)
elf.debug = args.debug
cbfs_util.VERBOSE = args.verbosity > 2
@@ -231,103 +519,13 @@ def Binman(args):
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
- # in binman.
- dtb_fname = fdt_util.EnsureCompiled(dtb_fname)
- fname = tools.GetOutputFilename('u-boot.dtb.out')
- tools.WriteFile(fname, tools.ReadFile(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)
-
- images = _ReadImageDesc(node)
-
- if args.image:
- skip = []
- new_images = OrderedDict()
- for name, image in images.items():
- if name in args.image:
- new_images[name] = image
- else:
- skip.append(name)
- images = new_images
- if skip and args.verbosity >= 2:
- print('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.ExpandEntries()
- if args.update_fdt:
- image.AddMissingProperties()
- image.ProcessFdt(dtb)
-
- for dtb_item in state.GetFdts():
- dtb_item.Sync(auto_resize=True)
- dtb_item.Pack()
- dtb_item.Flush()
-
+ images = PrepareImagesAndDtbs(dtb_fname, args.image,
+ args.update_fdt)
for image in images.values():
- # 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.
- 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 = 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:
- image.WriteMap()
+ ProcessImage(image, args.update_fdt, args.map)
# Write the updated FDTs to our output files
- for dtb_item in state.GetFdts():
+ for dtb_item in state.GetAllFdts():
tools.WriteFile(dtb_item._fname, dtb_item.GetContents())
finally: