CBFS

The wiki is being retired!

Documentation is now handled by the same processes we use for code: Add something to the Documentation/ directory in the coreboot repo, and it will be rendered to https://doc.coreboot.org/. Contributions welcome!

The coreboot CBFS Specification was originally announced here.

See cbfs.txt for details.

Introduction

This document describes the coreboot CBFS specification (from here referred to as CBFS). 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.


Architecture

The CBFS architecture looks like the following:

/---------------\ <-- Start of ROM
| /-----------\ | --|
| | Header    | |   |
| |-----------| |   |
| | Name      | |   |-- Component
| |-----------| |   |
| |Data       | |   |
| |..         | |   |
| \-----------/ | --|
|               |
| /-----------\ |
| | Header    | |
| |-----------| |
| | Name      | |
| |-----------| |
| |Data       | |
| |..         | |
| \-----------/ |
|               |
| ...           |
| /-----------\ |
| |           | |
| | Bootblock | |
| | --------- | |
| | Reset     | | <- 0xFFFFFFF0
| \-----------/ |
\---------------/


The CBFS architecture consists of a binary associated with a physical ROM disk referred hereafter as the ROM. A number of independent of components, each with a header prepended on to data are located within the ROM. The components are nominally arranged sequentially, though they are aligned along a pre-defined boundary.

The bootblock occupies the last 20k of the ROM. Within the bootblock is a master header containing information about the ROM including the size, alignment of the components, and the offset of the start of the first CBFS component within the ROM.

(Note that the master header is currently being removed as part of a significant redesign and modernization of the CBFS structures. See the #Redesign section for more details on the changes being made and where we are in the process.)

Master Header

The master header contains essential information about the ROM that is used by both the CBFS implementation within coreboot at runtime as well as host based utilities to create and manage the ROM. The master header will be located somewhere within the bootblock (last 20k of the ROM). A pointer to the location of the header will be located at offset -4 from the end of the ROM. This translates to address 0xFFFFFFFC on a normal x86 system. The pointer will be to physical memory somewhere between - 0xFFFFB000 and 0xFFFFFFF0. This makes it easier for coreboot to locate the header at run time. Build time utilities will need to read the pointer and do the appropriate math to locate the header.

The following is the structure of the master header:

struct cbfs_header {
        uint32_t magic;
        uint32_t version;
        uint32_t romsize;
        uint32_t bootblocksize;
        uint32_t align;
        uint32_t offset;
        uint32_t architecture;
        uint32_t pad[1];
} __attribute__((packed));
Offset 0 1 2 3 4 5 6 7 8 9 A B C D E F
0x00 magic = "ORBC" version romsize bootblocksize
0x10 align offset architecture padding

The meaning of each member is as follows:

(Note that the master header is currently being removed as part of a significant redesign and modernization of the CBFS structures. See the #Redesign section for more details on the changes being made and where we are in the process.)

Bootblock

The bootblock is a mandatory component in the ROM. It is located in the last 20k of the ROM space, and contains, among other things, the location of the master header and the entry point for the loader firmware. The bootblock does not have a component header attached to it.

(Note that the master header location is currently being removed as part of a significant redesign and modernization of the CBFS structures. See the #Redesign section for more details on the changes being made and where we are in the process.)

Components

CBFS components are placed in the ROM starting at 'offset' specified in the master header and ending at the bootblock. Thus the total size available for components in the ROM is (ROM size - 20k - 'offset'). Each CBFS component is to be aligned according to the 'align' value in the header. Thus, if a component of size 1052 is located at offset 0 with an 'align' value of 1024, the next component will be located at offset 2048.

Each CBFS component will be indexed with a unique ASCII string name of unlimited size.

Each CBFS component starts with a header:

struct cbfs_file {
         char magic[8];
         uint32_t len;
         uint32_t type;
         uint32_t checksum;
         uint32_t offset;
};
Offset 0 1 2 3 4 5 6 7 8 9 A B C D E F
0x00 magic = "LARCHIVE" len type
0x10 checksum offset

Immediately following the header will be the name of the component, which will null terminated and 16 byte aligned. The following picture shows the structure of the header:

/--------\  <- start
| Header |
|--------|  <- sizeof(struct cbfs_file)
| Name   |
|--------|  <- 'offset'
| Data   |
| ...    |
\--------/  <- start + 'offset' + 'len'

(Note that there are some changes to the header format in the pipeline as part of a significant redesign and modernization of the CBFS structures. See the #Redesign section for more details on the updates and where we are in the process.)

Searching Alogrithm

To locate a specific component in the ROM, one starts at the 'offset' specified in the CBFS master header. For this example, the offset will be 0.

From that offset, the code should search for the magic string on the component, jumping 'align' bytes each time. So, assuming that 'align' is 16, the code will search for the string 'LARCHIVE' at offset 0, 16, 32, etc. If the offset ever exceeds the allowable range for CBFS components, then no component was found.

Upon recognizing a component, the software then has to search for the specific name of the component. This is accomplished by comparing the desired name with the string on the component located at offset + sizeof(struct cbfs_file). If the string matches, then the component has been located, otherwise the software should add 'offset' + 'len' to the offset and resume the search for the magic value.

(Note that the first step is currently being changed such that the location of the first component is read from the image's global FMAP. See the #Redesign section for more details on the updates and where we are in the process.)

Data Types

The 'type' member of struct cbfs_file is used to identify the content of the component data, and is used by coreboot and other run-time entities to make decisions about how to handle the data.

There are three component types that are essential to coreboot, and so are defined here.

Stages

Stages are code loaded by coreboot during the boot process. They are essential to a successful boot. Stages are comprised of a single blob of binary data that is to be loaded into a particular location in memory and executed. The uncompressed header contains information about how large the data is, and where it should be placed, and what additional memory needs to be cleared.

Stages are assigned a component value of 0x10. When coreboot sees this component type, it knows that it should pass the data to a sub-function that will process the stage.

The following is the format of a stage component:

/--------\
| Header |
|--------|
| Binary |
| ..     |
\--------/

The header is defined as:

struct cbfs_stage {
         uint32_t compression;
         uint64_t entry;
         uint64_t load;
         uint32_t len;
         uint32_t memlen;
};
Offset 0 1 2 3 4 5 6 7 8 9 A B C D E F
0x00 compression entry load ...
0x10 ... load len memlen

The component data will start immediately following the header.

When coreboot loads a stage, it will first zero the memory from 'load' to 'memlen'. It will then decompress the component data according to the specified scheme and place it in memory starting at 'load'. Following that, it will jump execution to the address specified by 'entry'. Some components are designed to execute directly from the ROM - coreboot knows which components must do that and will act accordingly.

Payloads

Payloads are loaded by coreboot following the boot process.

For more details, also see SELF.

Payloads are assigned a component value of 0x20. When coreboot sees this component type, it knows that it should pass the data to a sub-function that will process the payload. Furthermore, other run time applications such as 'bayou' may easily index all available payloads on the system by searching for the payload type.


The following is the format of a stage component:

/-----------\
| Header    |
| Segment 1 |
| Segment 2 |
| ...       |
|-----------|
| Binary    |
| ..        |
\-----------/

The header is as follows:

struct cbfs_payload {
         struct cbfs_payload_segment segments;
}

The header contains a number of segments corresponding to the segments that need to be loaded for the payload.

The following is the structure of each segment header:

struct cbfs_payload_segment {
         uint32_t type;
         uint32_t compression;
         uint32_t offset;
         uint64_t load_addr;
         uint32_t len;
         uint32_t mem_len;
};
Offset 0 1 2 3 4 5 6 7 8 9 A B C D E F
0x00 type compression offset load_addr ...
0x10 ... load_addr len mem_len
PAYLOAD_SEGMENT_CODE   0x45444F43   The segment contains executable code
PAYLOAD_SEGMENT_DATA   0x41544144   The segment contains data
PAYLOAD_SEGMENT_BSS    0x20535342   The memory speicfied by the segment
                                     should be zeroed
PAYLOAD_SEGMENT_PARAMS 0x41524150   The segment contains information for
                                     the payload
PAYLOAD_SEGMENT_ENTRY  0x52544E45   The segment contains the entry point
                                     for the payload

The data will located immediately following the last segment.

Option ROMS

The third specified component type will be Option ROMs. Option ROMS will have component type '0x30'. They will have no additional header, the uncompressed binary data will be located in the data portion of the component.

NULL

There is a 4th component type ,defined as NULL (0xFFFFFFFF). This is the "don't care" component type. This can be used when the component type is not necessary (such as when the name of the component is unique. i.e. option_table). It is recommended that all components be assigned a unique type, but NULL can be used when the type does not matter.

Redesign

The CBFS system is currently being modernized with the vision of greater adaptability to a variety of use cases. This work is being pursued as part of the Flashmap integration work; as such, it's being committed to the Chromium OS coreboot fork first, then upstreamed to the main repository. There are several important aspects to the design changes:

FMAP

One of the big goals here is to seamlessly support multiple CBFSes per firmware image. This is something that would be very useful to users such as the Chromium OS project that have complex firmware stacks with numerous components and modular update schemes, and having it in upstream coreboot would make the project more scalable to large projects, and consequently more adoptable.

The flashmap format provides the flash chip analog of disks' partition tables, and is therefore well suited to describing where the individual CBFSes live in flash. The idea is that the FMAP will eventually be a mandatory component of a coreboot firmware image, and will be consulted to find the location of the CBFS(es) before reading their structure. The coreboot binaries themselves will have compiled-in knowledge of where the FMAP begins, eliminating the need for expensive runtime flash searches while ideally reducing the number of compiled-in offsets to the one.

Master Header

Most of the information contained in the CBFS master header becomes redundant once a flashmap is present:

For this reason, the master header is being removed as FMAPs are being added. Relevant CL: http://review.coreboot.org/#/c/10135 (upstreamed from https://chromium-review.googlesource.com/#/c/265863)

Version Coding and Hashing: File Entry Headers

Because the version information was previously stored in the master header, it needs a new place to live before the new type of image comes into use. As such, the following modifications to the per--file entry header format are being implemented:

These changes are being made as part of the Chromium OS project, and haven't yet been upstreamed. Relevant (Chromium OS) CL: https://chromium-review.googlesource.com/#/c/268408

Progress and Future Work

In short: cbfstool support for the new format is landing/on the way, but the build system, coreboot, and libpayload changes still need to be made.

cbfstool

Support for the redesigned image format discussed herein has been implemented in cbfstool with the following user-visible interface changes:

While support for reading from the new image format has not yet been added to coreboot itself or libpayload, this will be a much easier problem for two reasons: these components shouldn't need write support and there's no reason for them to still support reading legacy images once we make the switch. It should be noted that the changes made to cbfstool preserve its backwards compatibility with legacy images: because invoking it in the same way as before continues to create and manipulate legacy images, it is possible for the build system coreboot to continue using the legacy format exclusively until all the necessary pieces are in place to switch over.

Build System

The following build system changes are needed before we can complete a switch to the new image format:

coreboot/libpayload

The main coreboot code and libpayload need to be updated to be able to read from the new images at all:

Remaining Design Work

The major remaining shortcoming is support for taking a structural hash of an entire CBFS. If such a hash were hierarchical and hashed the headers (including hashes and filenames) of all contained files, it would provide a guarantee that that entire portion of the image had been read correctly and hadn't been tampered with. Here are some design considerations, all of which are still open questions: