diff options
Diffstat (limited to 'tools/binman/etype/cbfs.py')
-rw-r--r-- | tools/binman/etype/cbfs.py | 263 |
1 files changed, 263 insertions, 0 deletions
diff --git a/tools/binman/etype/cbfs.py b/tools/binman/etype/cbfs.py new file mode 100644 index 0000000000..edf2189fd2 --- /dev/null +++ b/tools/binman/etype/cbfs.py @@ -0,0 +1,263 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright 2019 Google LLC +# Written by Simon Glass <sjg@chromium.org> +# +# Entry-type module for a Coreboot Filesystem (CBFS) +# + +from collections import OrderedDict + +import cbfs_util +from cbfs_util import CbfsWriter +from entry import Entry +import fdt_util +import state + +class Entry_cbfs(Entry): + """Entry containing a Coreboot Filesystem (CBFS) + + A CBFS provides a way to group files into a group. It has a simple directory + structure and allows the position of individual files to be set, since it is + designed to support execute-in-place in an x86 SPI-flash device. Where XIP + is not used, it supports compression and storing ELF files. + + CBFS is used by coreboot as its way of orgnanising SPI-flash contents. + + The contents of the CBFS are defined by subnodes of the cbfs entry, e.g.: + + cbfs { + size = <0x100000>; + u-boot { + cbfs-type = "raw"; + }; + u-boot-dtb { + cbfs-type = "raw"; + }; + }; + + This creates a CBFS 1MB in size two files in it: u-boot.bin and u-boot.dtb. + Note that the size is required since binman does not support calculating it. + The contents of each entry is just what binman would normally provide if it + were not a CBFS node. A blob type can be used to import arbitrary files as + with the second subnode below: + + cbfs { + size = <0x100000>; + u-boot { + cbfs-name = "BOOT"; + cbfs-type = "raw"; + }; + + dtb { + type = "blob"; + filename = "u-boot.dtb"; + cbfs-type = "raw"; + cbfs-compress = "lz4"; + cbfs-offset = <0x100000>; + }; + }; + + This creates a CBFS 1MB in size with u-boot.bin (named "BOOT") and + u-boot.dtb (named "dtb") and compressed with the lz4 algorithm. + + + Properties supported in the top-level CBFS node: + + cbfs-arch: + Defaults to "x86", but you can specify the architecture if needed. + + + Properties supported in the CBFS entry subnodes: + + cbfs-name: + This is the name of the file created in CBFS. It defaults to the entry + name (which is the node name), but you can override it with this + property. + + cbfs-type: + This is the CBFS file type. The following are supported: + + raw: + This is a 'raw' file, although compression is supported. It can be + used to store any file in CBFS. + + stage: + This is an ELF file that has been loaded (i.e. mapped to memory), so + appears in the CBFS as a flat binary. The input file must be an ELF + image, for example this puts "u-boot" (the ELF image) into a 'stage' + entry: + + cbfs { + size = <0x100000>; + u-boot-elf { + cbfs-name = "BOOT"; + cbfs-type = "stage"; + }; + }; + + You can use your own ELF file with something like: + + cbfs { + size = <0x100000>; + something { + type = "blob"; + filename = "cbfs-stage.elf"; + cbfs-type = "stage"; + }; + }; + + As mentioned, the file is converted to a flat binary, so it is + equivalent to adding "u-boot.bin", for example, but with the load and + start addresses specified by the ELF. At present there is no option + to add a flat binary with a load/start address, similar to the + 'add-flat-binary' option in cbfstool. + + cbfs-offset: + This is the offset of the file's data within the CBFS. It is used to + specify where the file should be placed in cases where a fixed position + is needed. Typical uses are for code which is not relocatable and must + execute in-place from a particular address. This works because SPI flash + is generally mapped into memory on x86 devices. The file header is + placed before this offset so that the data start lines up exactly with + the chosen offset. If this property is not provided, then the file is + placed in the next available spot. + + The current implementation supports only a subset of CBFS features. It does + not support other file types (e.g. payload), adding multiple files (like the + 'files' entry with a pattern supported by binman), putting files at a + particular offset in the CBFS and a few other things. + + Of course binman can create images containing multiple CBFSs, simply by + defining these in the binman config: + + + binman { + size = <0x800000>; + cbfs { + offset = <0x100000>; + size = <0x100000>; + u-boot { + cbfs-type = "raw"; + }; + u-boot-dtb { + cbfs-type = "raw"; + }; + }; + + cbfs2 { + offset = <0x700000>; + size = <0x100000>; + u-boot { + cbfs-type = "raw"; + }; + u-boot-dtb { + cbfs-type = "raw"; + }; + image { + type = "blob"; + filename = "image.jpg"; + }; + }; + }; + + This creates an 8MB image with two CBFSs, one at offset 1MB, one at 7MB, + both of size 1MB. + """ + def __init__(self, section, etype, node): + Entry.__init__(self, section, etype, node) + self._cbfs_arg = fdt_util.GetString(node, 'cbfs-arch', 'x86') + self._cbfs_entries = OrderedDict() + self._ReadSubnodes() + + def ObtainContents(self): + arch = cbfs_util.find_arch(self._cbfs_arg) + if arch is None: + self.Raise("Invalid architecture '%s'" % self._cbfs_arg) + if self.size is None: + self.Raise("'cbfs' entry must have a size property") + cbfs = CbfsWriter(self.size, arch) + for entry in self._cbfs_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() + cfile = None + if entry._type == 'raw': + cfile = cbfs.add_file_raw(entry._cbfs_name, data, + entry._cbfs_offset, + entry._cbfs_compress) + elif entry._type == 'stage': + cfile = cbfs.add_file_stage(entry._cbfs_name, data, + entry._cbfs_offset) + else: + entry.Raise("Unknown cbfs-type '%s' (use 'raw', 'stage')" % + entry._type) + if cfile: + entry._cbfs_file = cfile + data = cbfs.get_data() + self.SetContents(data) + return True + + def _ReadSubnodes(self): + """Read the subnodes to find out what should go in this IFWI""" + for node in self._node.subnodes: + entry = Entry.Create(self.section, node) + entry._cbfs_name = fdt_util.GetString(node, 'cbfs-name', entry.name) + entry._type = fdt_util.GetString(node, 'cbfs-type') + compress = fdt_util.GetString(node, 'cbfs-compress', 'none') + entry._cbfs_offset = fdt_util.GetInt(node, 'cbfs-offset') + entry._cbfs_compress = cbfs_util.find_compress(compress) + if entry._cbfs_compress is None: + self.Raise("Invalid compression in '%s': '%s'" % + (node.name, compress)) + self._cbfs_entries[entry._cbfs_name] = entry + + def SetImagePos(self, image_pos): + """Override this function to set all the entry properties from CBFS + + We can only do this once image_pos is known + + Args: + image_pos: Position of this entry in the image + """ + Entry.SetImagePos(self, image_pos) + + # Now update the entries with info from the CBFS entries + for entry in self._cbfs_entries.values(): + cfile = entry._cbfs_file + entry.size = cfile.data_len + entry.offset = cfile.calced_cbfs_offset + entry.image_pos = self.image_pos + entry.offset + if entry._cbfs_compress: + entry.uncomp_size = cfile.memlen + + def AddMissingProperties(self): + Entry.AddMissingProperties(self) + for entry in self._cbfs_entries.values(): + entry.AddMissingProperties() + if entry._cbfs_compress: + state.AddZeroProp(entry._node, 'uncomp-size') + # Store the 'compress' property, since we don't look at + # 'cbfs-compress' in Entry.ReadData() + state.AddString(entry._node, 'compress', + cbfs_util.compress_name(entry._cbfs_compress)) + + def SetCalculatedProperties(self): + """Set the value of device-tree properties calculated by binman""" + Entry.SetCalculatedProperties(self) + for entry in self._cbfs_entries.values(): + state.SetInt(entry._node, 'offset', entry.offset) + state.SetInt(entry._node, 'size', entry.size) + state.SetInt(entry._node, 'image-pos', entry.image_pos) + if entry.uncomp_size is not None: + state.SetInt(entry._node, 'uncomp-size', entry.uncomp_size) + + def ListEntries(self, entries, indent): + """Override this method to list all files in the section""" + Entry.ListEntries(self, entries, indent) + for entry in self._cbfs_entries.values(): + entry.ListEntries(entries, indent + 1) + + def GetEntries(self): + return self._cbfs_entries |