binman: Move entry-data collection into a Entry method

Collecting the data from a list of entries and putting it in a file is
a useful operation that will be needed by other entry types. Put this into
a method in the Entry class.

Add some documentation about how to collect data for an entry type.

Signed-off-by: Simon Glass <sjg@chromium.org>
This commit is contained in:
Simon Glass 2022-02-08 11:50:00 -07:00
parent 56385c585f
commit 81b71c3200
3 changed files with 118 additions and 10 deletions

View file

@ -1358,6 +1358,92 @@ development, since dealing with exceptions and problems in threads is more
difficult. This avoids any use of ThreadPoolExecutor.
Collecting data for an entry type
---------------------------------
Some entry types deal with data obtained from others. For example,
`Entry_mkimage` calls the `mkimage` tool with data from its subnodes::
mkimage {
args = "-n test -T script";
u-boot-spl {
};
u-boot {
};
};
This shows mkimage being passed a file consisting of SPL and U-Boot proper. It
is create by calling `Entry.collect_contents_to_file()`. Note that in this case,
the data is passed to mkimage for processing but does not appear separately in
the image. It may not appear at all, depending on what mkimage does. The
contents of the `mkimage` entry are entirely dependent on the processing done
by the entry, with the provided subnodes (`u-boot-spl` and `u-boot`) simply
providing the input data for that processing.
Note that `Entry.collect_contents_to_file()` simply concatenates the data from
the different entries together, with no control over alignment, etc. Another
approach is to subclass `Entry_section` so that those features become available,
such as `size` and `pad-byte`. Then the contents of the entry can be obtained by
calling `BuildSectionData()`.
There are other ways to obtain data also, depending on the situation. If the
entry type is simply signing data which exists elsewhere in the image, then
you can use `Entry_collection` as a base class. It lets you use a property
called `content` which lists the entries containing data to be processed. This
is used by `Entry_vblock`, for example::
u_boot: u-boot {
};
vblock {
content = <&u_boot &dtb>;
keyblock = "firmware.keyblock";
signprivate = "firmware_data_key.vbprivk";
version = <1>;
kernelkey = "kernel_subkey.vbpubk";
preamble-flags = <1>;
};
dtb: u-boot-dtb {
};
which shows an image containing `u-boot` and `u-boot-dtb`, with the `vblock`
image collecting their contents to produce input for its signing process,
without affecting those entries, which still appear in the final image
untouched.
Another example is where an entry type needs several independent pieces of input
to function. For example, `Entry_fip` allows a number of different binary blobs
to be placed in their own individual places in a custom data structure in the
output image. To make that work you can add subnodes for each of them and call
`Entry.Create()` on each subnode, as `Entry_fip` does. Then the data for each
blob can come from any suitable place, such as an `Entry_u_boot` or an
`Entry_blob` or anything else::
atf-fip {
fip-hdr-flags = /bits/ 64 <0x123>;
soc-fw {
fip-flags = /bits/ 64 <0x123456789abcdef>;
filename = "bl31.bin";
};
u-boot {
fip-uuid = [fc 65 13 92 4a 5b 11 ec
94 35 ff 2d 1c fc 79 9c];
};
};
The `soc-fw` node is a `blob-ext` (i.e. it reads in a named binary file) whereas
`u-boot` is a normal entry type. This works because `Entry_fip` selects the
`blob-ext` entry type if the node name (here `soc-fw`) is recognised as being
a known blob type.
When adding new entry types you are encouraged to use subnodes to provide the
data for processing, unless the `content` approach is more suitable. Ad-hoc
properties and other methods of obtaining data are discouraged, since it adds to
confusion for users.
History / Credits
-----------------

View file

@ -1123,3 +1123,31 @@ features to produce new behaviours.
update_hash: True if hash should be updated, False if not
"""
self.update_hash = update_hash
def collect_contents_to_file(self, entries, prefix):
"""Put the contents of a list of entries into a file
Args:
entries (list of Entry): Entries to collect
prefix (str): Filename prefix of file to write to
If any entry does not have contents yet, this function returns False
for the data.
Returns:
Tuple:
bytes: Concatenated data from all the entries (or False)
str: Filename of file written (or False if no data)
str: Unique portion of filename (or False if no data)
"""
data = b''
for entry in entries:
# First get the input data and put it in a file. If not available,
# try later.
if not entry.ObtainContents():
return False, False, False
data += entry.GetData()
uniq = self.GetUniqueName()
fname = tools.get_output_filename(f'{prefix}.{uniq}')
tools.write_file(fname, data)
return data, fname, uniq

View file

@ -51,16 +51,10 @@ class Entry_mkimage(Entry):
self.ReadEntries()
def ObtainContents(self):
data = b''
for entry in self._mkimage_entries.values():
# First get the input data and put it in a file. If not available,
# try later.
if not entry.ObtainContents():
return False
data += entry.GetData()
uniq = self.GetUniqueName()
input_fname = tools.get_output_filename('mkimage.%s' % uniq)
tools.write_file(input_fname, data)
data, input_fname, uniq = self.collect_contents_to_file(
self._mkimage_entries.values(), 'mkimage')
if data is False:
return False
output_fname = tools.get_output_filename('mkimage-out.%s' % uniq)
if self.mkimage.run_cmd('-d', input_fname, *self._args,
output_fname) is not None: