coreboot Filesystem (CBFS)
The coreboot filesystem (CBFS) is a scheme for managing independent chunks of data in a system ROM. Though not a true filesystem, the style and concepts are similar. It is a flat, append-oriented archive designed to be walked at any point in the boot process.
This document is an overview of the on-flash format, how it is consumed
at runtime, and of how cbfstool manipulates it.
The authoritative description of the binary format is
src/commonlib/bsd/include/commonlib/bsd/cbfs_serialized.h.
Image layout
Way back at the beginning of coreboot, ROM images were only defined by the CBFS.
As images started to include additional binaries, the FMAP was adopted. FMAP
holds information about the entire flash and CBFS holds the coreboot binaries.
CBFS lives inside a FMAP region. A minimal image contains an FMAP region
and a COREBOOT region holding the primary CBFS.
See Flashmap and Flashmap Descriptor for the region layout and
the descriptor format.
/------------------------\ <- start of flash
| ... |
|------------------------|
| FMAP | describes the region layout
|------------------------|
| COREBOOT | --\
| /-------------------\ | |
| | fallback/payload | | |
| |-------------------| | |
| | fallback/ramstage | | |
| |-------------------| | |-- a CBFS instance
| | fallback/romstage | | |
| |-------------------| | |
| | ... | | |
| |-------------------| | |
| | bootblock | | |
| \-------------------/ | |
| | --/
\------------------------/ <- end of flash (0xFFFFFFFF on x86)
The bootblock is a mandatory part of every image, but where it lives depends on
the architecture. On x86 it is a CBFS file of type bootblock, placed at the
highest flash address so that it covers the reset vector at 0xfffffff0, which
is why the diagram above draws it at the bottom. On other architectures it
normally occupies its own BOOTBLOCK FMAP region and is not part of any CBFS.
BOOTBLOCK_IN_CBFS selects between the two and defaults to the CBFS form on
x86.
The above example shows a single COREBOOT CBFS instance, but a flash may hold
several CBFS regions to support A/B or RO/RW update schemes.
For example, Intel Top Swap redundancy pairs COREBOOT
with a COREBOOT_B update slot. The instance the platform boots from is
decided by a platform-specific mechanism, not by CBFS.
File format
Every file starts with a metadata block, followed by the file data:
/------------\ <- start of file, 64 byte aligned
| header |
|------------| <- 'attributes_offset'
| attributes |
|------------| <- 'offset'
| data |
| ... |
\------------/ <- start + 'offset' + 'len'
The total size of the metadata block, that is header plus attributes,
is capped by CBFS_METADATA_MAX_SIZE (256 bytes).
All CBFS metadata is stored big-endian on flash, regardless of the host or target architecture. Fields must be byte-swapped as they are read. Only the metadata is byte-swapped, file data is opaque.
The header is:
struct cbfs_file {
char magic[8];
uint32_t len;
uint32_t type;
uint32_t attributes_offset;
uint32_t offset;
char filename[];
} __packed;
magic identifies the header. It is always the string LARCHIVE.
len is the length of the file data, excluding all metadata.
type describes the content of the data, see File types.
attributes_offset is the offset of the attributes from the start of the
header, or 0 if the file has no attributes.
offset is the offset of the file data from the start of the header
(also the total size of the metadata).
filename is an inline NUL-terminated string.
Files are aligned to CBFS_ALIGNMENT, which is fixed at 64 bytes.
File attributes
CBFS header fields are the same for every file. Anything a particular file needs beyond them is carried as an attribute, an optional struct in the metadata. Compression, hashes, placement constraints and a stage’s load address are all attributes.
Attributes are packed back to back between attributes_offset (the end of the
CBFS header) and offset (the data). Each begins with a common tag and length:
struct cbfs_file_attribute {
uint32_t tag;
/* len covers the whole structure, incl. tag and len */
uint32_t len;
uint8_t data[];
} __packed;
Every attribute size must be a multiple of CBFS_ATTRIBUTE_ALIGN (4). A parser
that does not recognize a tag skips len bytes and continues, so new attributes
can be added without breaking existing readers.
Tag |
Structure |
Purpose |
|---|---|---|
|
|
Algorithm and decompressed size of the file data |
|
|
|
|
|
Load address, entry point and memory size of a stage |
|
|
Pins the file to a fixed offset |
|
|
Forces a stronger alignment than 64 bytes |
|
– |
Marks the file as part of the Initial Boot Block |
|
– |
Reserves empty space, used to satisfy the above constraints |
Compression is a property of the file, not of a particular file type, and covers the whole data area. The supported algorithms are none (0), LZMA (1), LZ4 (2) and Zstandard (3). Which of them a given stage can use depends on what the decompressor in the preceding coreboot stage has available.
Lookup
CBFS has no index. Finding a file means walking the region from the start, as
implemented by cbfs_walk() in src/commonlib/bsd/cbfs_private.c:
Start at offset 0 of the CBFS region.
Read 8 bytes and compare against
LARCHIVE. If they do not match, advance byCBFS_ALIGNMENT(64) and retry. Running past the end of the region means the file is not present.On a match, read the rest of the metadata and range-check
len,attributes_offsetandoffsetagainst the region size. A header that fails validation is skipped.Compare
filenameagainst the name being looked up. On a match the data begins atoffsetbytes into the file.Otherwise continue the search at
ALIGN_UP(file_start + offset + len, 64).
Deleted files are not removed. Their type is set to CBFS_TYPE_DELETED (0) or
CBFS_TYPE_NULL (0xffffffff) and the space they occupy stays in the walk as a
hole. cbfstool compact merges those holes back together.
Because rescanning flash in every stage is expensive, coreboot builds a metadata
cache (src/commonlib/bsd/cbfs_mcache.c) during the first walk and passes it to
later stages through CBMEM.
File types
type identifies what the data is, so that coreboot and host tools know how to
handle it. The full list is enum cbfs_type in cbfs_serialized.h. The types
seen in a typical image are:
Name |
Value |
Content |
|---|---|---|
|
0x01 |
The bootblock |
|
0x02 |
Legacy master header |
|
0x11 |
A coreboot stage |
|
0x20 |
A SELF payload |
|
0x21 |
A FIT payload |
|
0x30 |
A PCI option ROM |
|
0x50 |
Uninterpreted data |
|
0x53 |
CPU microcode |
|
0x54 |
Intel Firmware Interface Table |
|
0x60 |
Intel Firmware Support Package |
|
0x80 |
AMD firmware container |
|
0xaa |
Default CMOS contents |
|
0x1aa |
CMOS layout description |
Note that FIT_PAYLOAD (a flattened image tree) and INTEL_FIT (a table of
pointers consumed by the Intel CPU microcode) are unrelated despite the shared
acronym.
Stages
Stages are the pieces of coreboot itself: romstage, ramstage, and on some platforms a separate verstage. They are loaded and executed by the preceding stage.
A stage is a flat binary plus a STAGEHEADER attribute:
struct cbfs_file_attr_stageheader {
uint32_t tag;
uint32_t len;
uint64_t loadaddr; /* Memory address to load the code to. */
uint32_t entry_offset; /* Offset of entry point from loadaddr. */
uint32_t memlen; /* Total length (including BSS) in memory. */
} __packed;
entry_offset is relative to loadaddr, not an absolute address. memlen
covers BSS, so it is generally larger than the decompressed data.
To load a stage, coreboot decompresses the data to loadaddr according to the
COMPRESSION attribute, zeroes the remainder up to memlen, and jumps to
loadaddr + entry_offset. An uncompressed stage that is already at loadaddr
on memory-mapped flash is executed in place, without being copied.
Type 0x10 is CBFS_TYPE_LEGACY_STAGE, an older format that carried this
information in a header prepended to the data instead of in an attribute.
Payloads
A payload is the program coreboot hands control to at the end of the boot
process. The native format is SimpleELF (SELF). Statically linked ELF binaries
are converted to SELF by cbfstool at build time, which splits the payload into
segments, each with its own loading address and compression (LZMA, LZ4, ZSTD or
none).
A SELF payload is a sequence of segment headers followed by the segment data:
struct cbfs_payload_segment {
uint32_t type;
uint32_t compression;
uint32_t offset;
uint64_t load_addr;
uint32_t len;
uint32_t mem_len;
} __packed;
type is one of:
Name |
Value |
Meaning |
|---|---|---|
|
0x434F4445 |
Executable code |
|
0x44415441 |
Data |
|
0x42535320 |
Memory to be zeroed |
|
0x454E5452 |
Entry point of the payload |
compression applies to this segment only. Unlike other file types, SELF
payloads are compressed per segment rather than through the file-level
COMPRESSION attribute.
offset locates the segment data relative to the start of the file data,
load_addr is where it goes in memory, len is its size in the file and
mem_len its size in memory.
A FIT payload (type 0x21) is supported on some architectures. It does not support whole-file compression, individual images inside the FIT carry their own.
The SELF loader implementation is in src/lib/selfboot.c. It decompresses
each segment and places it in memory. Every segment is checked against the
bootmem map before anything is loaded, so it must target usable RAM; a
payload overlapping the loading stage (RAMSTAGE) or any reserved region
aborts the boot. SELF payloads are never relocatable and are always
placed at the address they specify; if that address is unavailable, the
system won’t boot.
The SELF loader is not limited to payloads. It is also used to load arm64 BL31, RISC-V OpenSBI and vendor firmware blobs.
Calling conventions
The SELF payload is called with a pointer to the coreboot tables as first argument.
Note: One exception is made on RISC-V, which prepends the HARTID and a pointer to the FDT, so the coreboot tables arrive as third argument.
Raw files and everything else
Most other types have no additional header. The data is exactly what was passed
to cbfstool add, after optional compression. RAW is the catch-all for data a
driver or payload reads by name, for example an SPD blob or a splash image.
Option ROMs (0x30) similarly carry no extra header, just the ROM image.
Verification
When CBFS_VERIFICATION is enabled, every file carries a HASH attribute
covering its data, and coreboot checks it before using the file. The file
metadata itself, including those hashes, is covered by a single metadata hash
computed over the whole CBFS walk.
That metadata hash is stored in a struct metadata_hash_anchor embedded in the
uncompressed bootblock (src/commonlib/bsd/include/commonlib/bsd/metadata_hash.h),
which also holds a hash of the FMAP. The bootblock is therefore the root of
trust. Verifying it covers the FMAP and the whole read-only CBFS.
Updatable CBFS instances cannot be covered by an anchor baked into the read-only bootblock, so their metadata hash is supplied by whatever verified the instance. See verified boot for how that works in practice.
Legacy master header
Pre-FMAP x86 images described their CBFS with a master header rather than a
flash map. It records the image size, the offset of the first file and the file
alignment, and is stored as a CBFS file of type CBFSHEADER. The last four
bytes of the image hold its offset relative to the end of the image as a signed
32-bit integer, so early code can locate it without parsing anything.
New designs should not use this. It cannot describe more than one CBFS, and
several cbfstool operations including expand and truncate refuse to work
on such images. See struct cbfs_header in cbfs_serialized.h for the layout.
cbfstool
cbfstool is the host utility for building and inspecting images. It is built
as part of coreboot into build/cbfstool, and the build system uses it to
assemble coreboot.rom.
Unless -r/--fmap-regions says otherwise, commands operate on the COREBOOT
region.
Command |
Purpose |
|---|---|
|
Create a new image from an FMAP descriptor |
|
Add a file, with |
|
Convert an ELF to SELF and add it |
|
Convert an ELF to a stage and add it |
|
Add a flat binary with an explicit load address and entry point |
|
Add a raw 64-bit integer |
|
Mark a file deleted |
|
List the contents of a region |
|
Write a file out, |
|
List the FMAP regions of the image |
|
Copy a whole region out of or into the image |
|
Duplicate a CBFS instance into another region |
|
Merge the holes left by |
|
Grow a CBFS to fill its region, or shrink it to its contents |
A typical inspection looks like:
build/cbfstool build/coreboot.rom layout
build/cbfstool build/coreboot.rom print -r COREBOOT
Run cbfstool -h for the full option list, including the supported
architectures and file type names.
Adding files (at build time)
Calling cbfstool add by hand only modifies an image that has already been
built. To have a file included in every build, add it to the cbfs-files-y
make class, which the build system turns into the corresponding
cbfstool invocation. The class and its file, type, compression,
position and align options are described in
the coreboot build system.