1# SPDX-License-Identifier: GPL-2.0+
2# Copyright 2022 Google LLC
3#
4"""Bintool implementation for cbfstool
5
6cfstool provides a number of features useful with Coreboot Filesystem binaries.
7
8Documentation is at https://www.coreboot.org/CBFS
9
10Source code is at https://github.com/coreboot/coreboot/blob/master/util/cbfstool/cbfstool.c
11
12Here is the help:
13
14cbfstool: Management utility for CBFS formatted ROM images
15
16USAGE:
17 cbfstool [-h]
18 cbfstool FILE COMMAND [-v] [PARAMETERS]...
19
20OPTIONs:
21  -H header_offset Do not search for header; use this offset*
22  -T               Output top-aligned memory address
23  -u               Accept short data; fill upward/from bottom
24  -d               Accept short data; fill downward/from top
25  -F               Force action
26  -g               Generate position and alignment arguments
27  -U               Unprocessed; don't decompress or make ELF
28  -v               Provide verbose output
29  -h               Display this help message
30
31COMMANDs:
32 add [-r image,regions] -f FILE -n NAME -t TYPE [-A hash] \
33        [-c compression] [-b base-address | -a alignment] \
34        [-p padding size] [-y|--xip if TYPE is FSP]       \
35        [-j topswap-size] (Intel CPUs only) [--ibb]
36        Add a component
37        -j valid size: 0x10000 0x20000 0x40000 0x80000 0x100000
38 add-payload [-r image,regions] -f FILE -n NAME [-A hash] \
39        [-c compression] [-b base-address] \
40        (linux specific: [-C cmdline] [-I initrd])
41        Add a payload to the ROM
42 add-stage [-r image,regions] -f FILE -n NAME [-A hash] \
43        [-c compression] [-b base] [-S section-to-ignore] \
44        [-a alignment] [-y|--xip] [-P page-size] [--ibb]
45        Add a stage to the ROM
46 add-flat-binary [-r image,regions] -f FILE -n NAME \
47        [-A hash] -l load-address -e entry-point \
48        [-c compression] [-b base]
49        Add a 32bit flat mode binary
50 add-int [-r image,regions] -i INTEGER -n NAME [-b base]
51 Add a raw 64-bit integer value
52 add-master-header [-r image,regions] \
53        [-j topswap-size] (Intel CPUs only)
54        Add a legacy CBFS master header
55 remove [-r image,regions] -n NAME
56 Remove a component
57 compact -r image,regions
58 Defragment CBFS image.
59 copy -r image,regions -R source-region
60 Create a copy (duplicate) cbfs instance in fmap
61 create -m ARCH -s size [-b bootblock offset] \
62        [-o CBFS offset] [-H header offset] [-B bootblock]
63        Create a legacy ROM file with CBFS master header*
64 create -M flashmap [-r list,of,regions,containing,cbfses]
65 Create a new-style partitioned firmware image
66 locate [-r image,regions] -f FILE -n NAME [-P page-size] \
67        [-a align] [-T]
68        Find a place for a file of that size
69 layout [-w]
70 List mutable (or, with -w, readable) image regions
71 print [-r image,regions]
72 Show the contents of the ROM
73 extract [-r image,regions] [-m ARCH] -n NAME -f FILE [-U]
74 Extracts a file from ROM
75 write [-F] -r image,regions -f file [-u | -d] [-i int]
76 Write file into same-size [or larger] raw region
77 read [-r fmap-region] -f file
78 Extract raw region contents into binary file
79 truncate [-r fmap-region]
80 Truncate CBFS and print new size on stdout
81 expand [-r fmap-region]
82 Expand CBFS to span entire region
83OFFSETs:
84  Numbers accompanying -b, -H, and -o switches* may be provided
85  in two possible formats: if their value is greater than
86  0x80000000, they are interpreted as a top-aligned x86 memory
87  address; otherwise, they are treated as an offset into flash.
88ARCHes:
89  arm64, arm, mips, ppc64, power8, riscv, x86, unknown
90TYPEs:
91 bootblock, cbfs header, stage, simple elf, fit, optionrom, bootsplash, raw,
92 vsa, mbi, microcode, fsp, mrc, cmos_default, cmos_layout, spd,
93 mrc_cache, mma, efi, struct, deleted, null
94
95* Note that these actions and switches are only valid when
96  working with legacy images whose structure is described
97  primarily by a CBFS master header. New-style images, in
98  contrast, exclusively make use of an FMAP to describe their
99  layout: this must minimally contain an 'FMAP' section
100  specifying the location of this FMAP itself and a 'COREBOOT'
101  section describing the primary CBFS. It should also be noted
102  that, when working with such images, the -F and -r switches
103  default to 'COREBOOT' for convenience, and both the -b switch to
104  CBFS operations and the output of the locate action become
105  relative to the selected CBFS region's lowest address.
106  The one exception to this rule is the top-aligned address,
107  which is always relative to the end of the entire image
108  rather than relative to the local region; this is true for
109  for both input (sufficiently large) and output (-T) data.
110
111
112Since binman has a native implementation of CBFS (see cbfs_util.py), we don't
113actually need this tool, except for sanity checks in the tests.
114"""
115
116from binman import bintool
117
118class Bintoolcbfstool(bintool.Bintool):
119    """Coreboot filesystem (CBFS) tool
120
121    This bintool supports creating new CBFS images and adding files to an
122    existing image, i.e. the features needed by binman.
123
124    It also supports fetching a binary cbfstool, since building it from source
125    is fairly slow.
126
127    Documentation about CBFS is at https://www.coreboot.org/CBFS
128    """
129    def __init__(self, name):
130        super().__init__(name, 'Manipulate CBFS files')
131
132    def create_new(self, cbfs_fname, size, arch='x86'):
133        """Create a new CBFS
134
135        Args:
136            cbfs_fname (str): Filename of CBFS to create
137            size (int): Size of CBFS in bytes
138            arch (str): Architecture for which this CBFS is intended
139
140        Returns:
141            str: Tool output
142        """
143        args = [cbfs_fname, 'create', '-s', f'{size:#x}', '-m', arch]
144        return self.run_cmd(*args)
145
146    # pylint: disable=R0913
147    def add_raw(self, cbfs_fname, name, fname, compress=None, base=None):
148        """Add a raw file to the CBFS
149
150        Args:
151            cbfs_fname (str): Filename of CBFS to create
152            name (str): Name to use inside the CBFS
153            fname (str): Filename of file to add
154            compress (str): Compression to use (cbfs_util.COMPRESS_NAMES) or
155                None for None
156            base (int): Address to place the file, or None for anywhere
157
158        Returns:
159            str: Tool output
160        """
161        args = [cbfs_fname,
162                'add',
163                '-n', name,
164                '-t', 'raw',
165                '-f', fname,
166                '-c', compress or 'none']
167        if base:
168            args += ['-b', f'{base:#x}']
169        return self.run_cmd(*args)
170
171    def add_stage(self, cbfs_fname, name, fname):
172        """Add a stage file to the CBFS
173
174        Args:
175            cbfs_fname (str): Filename of CBFS to create
176            name (str): Name to use inside the CBFS
177            fname (str): Filename of file to add
178
179        Returns:
180            str: Tool output
181        """
182        args = [cbfs_fname,
183                'add-stage',
184                '-n', name,
185                '-f', fname
186            ]
187        return self.run_cmd(*args)
188
189    def fail(self):
190        """Run cbfstool with invalid arguments to check it reports failure
191
192        This is really just a sanity check
193
194        Returns:
195            CommandResult: Result from running the bad command
196        """
197        args = ['missing-file', 'bad-command']
198        return self.run_cmd_result(*args)
199
200    def fetch(self, method):
201        """Fetch handler for cbfstool
202
203        This installs cbfstool by downloading from Google Drive.
204
205        Args:
206            method (FETCH_...): Method to use
207
208        Returns:
209            True if the file was fetched and now installed, None if a method
210            other than FETCH_BIN was requested
211
212        Raises:
213            Valuerror: Fetching could not be completed
214        """
215        if method != bintool.FETCH_BIN:
216            return None
217        fname, tmpdir = self.fetch_from_drive(
218            '1IOnE0Qvy97d-0WOCwF64xBGpKSY2sMtJ')
219        return fname, tmpdir
220