267 lines
11 KiB
Markdown
267 lines
11 KiB
Markdown
|
Rebuilding coreboot image generation
|
|||
|
====================================
|
|||
|
|
|||
|
Current situation
|
|||
|
-----------------
|
|||
|
Chrome OS (CrOS) probably has the most complex image bundling process in the
|
|||
|
coreboot ecosystem. To make CrOS features more accessible to the wider
|
|||
|
coreboot community, we want to move these capabilities into upstream
|
|||
|
coreboot’s build system.
|
|||
|
|
|||
|
Right now, the CrOS build system creates coreboot images, and various
|
|||
|
instances of the payload (with different configuration options), plus some
|
|||
|
more files (eg. EC firmware), then passes them to a CrOS-specific utility
|
|||
|
(`bundle_firmware.py`) to build the final image from that.
|
|||
|
|
|||
|
`bundle_firmware` adds a flashmap (fmap) to the final image and creates
|
|||
|
additional CBFS filesystems in fmap regions. It then extracts some files from
|
|||
|
the original CBFS region (that was put in place carefully to later match to
|
|||
|
the default fmap region) and copies some of them into the others, as well as
|
|||
|
putting more data (eg. the bitmap data, keys) as raw data into other fmap
|
|||
|
regions.
|
|||
|
|
|||
|
With the recent addition of more files to CBFS, both on the coreboot side
|
|||
|
(dsdt, FSP, and so on) and with Chrome OS specifics (eg. more files describing
|
|||
|
boot screens) we either need to expand the scope of bundle\_firmware or move
|
|||
|
the capability to build complex images to upstream coreboot’s build system.
|
|||
|
This document proposes to do the latter and outlines how this could be
|
|||
|
achieved.
|
|||
|
|
|||
|
Problems with the current build system parts
|
|||
|
--------------------------------------------
|
|||
|
One common sentiment is that it should be possible to reuse some of the
|
|||
|
existing mechanisms that are supposed to be supplanted by this.
|
|||
|
The main concern during this design that precluded their use was that none of
|
|||
|
them provides a comprehensive solution to building complex coreboot based
|
|||
|
images:
|
|||
|
* fmap.dts and fmd provide a flash layout, but no assignment of files of regions
|
|||
|
* cbfs-files-y ends up as an internal make variable using
|
|||
|
`weird|formatting|to|deal|with|make’s|limitations`
|
|||
|
* make isn’t powerful enough to deal with ordering these entries in said
|
|||
|
variable to guarantee success if there’s enough room for the files. While that
|
|||
|
could be added, that becomes more make macro work indistinguishable from magic
|
|||
|
that people fail to understand, break and with good reason complain about
|
|||
|
to work around such issues, Chrome OS firmware uses a custom tool with even
|
|||
|
more special cases to finally build the image it needs. If coreboot upstream
|
|||
|
is to support vboot, it should also be powerful enough not to need magic tools
|
|||
|
that only live within downstream projects.
|
|||
|
|
|||
|
Requirements
|
|||
|
------------
|
|||
|
A complete Chrome OS coreboot image consists of (depending on the device)
|
|||
|
* platform specific data in raw fmap regions (eg IFD, ME firmware),
|
|||
|
* the bootblock (coming from the bootblock),
|
|||
|
* three copies of coreboot, consisting of the stages (verstage, romstage,
|
|||
|
ramstage) plus data,
|
|||
|
* depthcharge plus data (with each of the coreboot copies),
|
|||
|
* EC firmware files (with each of the coreboot copies),
|
|||
|
* signatures over several parts of the image and
|
|||
|
* some final checksumming over parts of the image to satisfy boot ROM
|
|||
|
tests on ARM
|
|||
|
|
|||
|
A complete upstream coreboot image (with fallback/normal switch configuration,
|
|||
|
using a yet to be implemented switching scheme based on fmaps) consists of
|
|||
|
* platform specific data in raw fmap regions (eg IFD, ME firmware),
|
|||
|
* two copies of coreboot, consisting of
|
|||
|
* the bootblock and
|
|||
|
* the stages (romstage, ramstage) plus data,
|
|||
|
* payload plus data (with each of the coreboot copies),
|
|||
|
|
|||
|
Since a single platform is potentially built with different payload
|
|||
|
configurations (eg. modding a Chromebook to not use the verified Chrome OS
|
|||
|
boot scheme), some concerns need to be kept separate:
|
|||
|
* Platform requirements that have nothing to do with the payload or boot schemes
|
|||
|
* IFD, ME, … need to copied to the right place
|
|||
|
* boot ROM requirements such as checksums must be honored
|
|||
|
* Payload/boot scheme requirements
|
|||
|
* Having one to three regions with certain files copied into them
|
|||
|
|
|||
|
Proposal
|
|||
|
--------
|
|||
|
The proposal is based on manifest files that describe certain aspects of the
|
|||
|
final image.
|
|||
|
The number of manifest files may change over time, but this seems to be a
|
|||
|
reasonable approach for now. As long as coreboot uses fmap and cbfs, there
|
|||
|
should be few need to change the language, since composition is done through
|
|||
|
files.
|
|||
|
|
|||
|
The final image is generated by a utility that is handed a number of manifests
|
|||
|
and the size of the flash (derived from `CONFIG_ROM_SIZE`). These manifest files
|
|||
|
deal with different concerns, with the following an example that should match
|
|||
|
current use cases:
|
|||
|
|
|||
|
Chipset manifest
|
|||
|
----------------
|
|||
|
The chipset details if there are any non-coreboot regions, and assigns them
|
|||
|
names, locations, sizes and file contents and prepares a region for what is
|
|||
|
“platform visible” (eg. IFD’s BIOS region) that may be of flexible size
|
|||
|
(depending on the flash chip’s size). For the purpose of this document, that
|
|||
|
region is called “BIOS”.
|
|||
|
It can also specify if there’s a post processing requirement on the final
|
|||
|
image.
|
|||
|
|
|||
|
coreboot manifest
|
|||
|
-----------------
|
|||
|
coreboot provides lists of the files it generates for each category it’s
|
|||
|
building (eg. bootblock, verstage, romstage, ramstage). They not only contain
|
|||
|
the stages themselves, but also additional files (eg. dsdt belongs to ramstage
|
|||
|
since that’s where it is used)
|
|||
|
|
|||
|
Boot method manifest
|
|||
|
--------------------
|
|||
|
The boot method manifest can subdivide the BIOS region, eg. using it directly
|
|||
|
(for coreboot’s “simple” bootblock), splitting it in two (for coreboot’s
|
|||
|
fallback/normal) or in many parts (for Chrome OS, which requires two CBFS
|
|||
|
regions, one for GBB, several for VPD, …).
|
|||
|
It also specifies which of the file lists specified earlier belong in which
|
|||
|
region (eg. with verstage verifying romstage, verstage needs to be only in
|
|||
|
Chrome OS’ RO region, while romstage belongs in RO and both RW regions).
|
|||
|
It can also specify a post processing step that is executed before the
|
|||
|
chipset’s.
|
|||
|
|
|||
|
Payload and additional manifests
|
|||
|
--------------------------------
|
|||
|
External components should also provide manifests to add files to categories.
|
|||
|
This way the payload and other components (eg. EC firmware) can be developed
|
|||
|
without needing to touch the central boot method manifest (that likely resides
|
|||
|
in the coreboot tree, given that coreboot needs to deal with choosing fmap
|
|||
|
regions already).
|
|||
|
|
|||
|
coreboot build system
|
|||
|
---------------------
|
|||
|
The coreboot build system will be split more distinctly in two phases: The
|
|||
|
first is about building the files (with results like romstage.elf), while the
|
|||
|
second phase covers the assembly of the final image.
|
|||
|
|
|||
|
By having a global picture of the final image’s requirements, we can also
|
|||
|
avoid issues where files added earlier may prevent later additions that have
|
|||
|
stricter constraints - without resorting to hacks like
|
|||
|
https://chromium-review.googlesource.com/289491 that reorder the file addition
|
|||
|
manually.
|
|||
|
|
|||
|
Example
|
|||
|
-------
|
|||
|
As an example, we’ll define an Intel-based board with a postprocessing tool
|
|||
|
(something that doesn’t exist, but isn’t hard to imagine):
|
|||
|
|
|||
|
It specifies an IFD region, an ME, and the BIOS region. After the image is
|
|||
|
built, the entire image needs to be processed (although the tool likely works
|
|||
|
only on a small part of it)
|
|||
|
|
|||
|
It’s built in a Chrome OS-like configuration (simplified at places to avoid
|
|||
|
distracting from the important parts), so it has three CBFS regions, and
|
|||
|
several data regions for its own purpose (similar to GBB, FWID, VPD, …). After
|
|||
|
the regions are filled, one data region must be post-processed to contain
|
|||
|
signatures to enable verifying other regions.
|
|||
|
|
|||
|
Chipset manifest
|
|||
|
================
|
|||
|
# A region called IFD, starting at 0, ending at 4K
|
|||
|
region IFD: 0 4K
|
|||
|
# Add the specified file “raw” into the region.
|
|||
|
# If the file is smaller than the region, put it at the bottom and fill up
|
|||
|
# with 0xff
|
|||
|
raw IFD: build/ifd.bin align=bottom empty=0xff
|
|||
|
# Call the postprocessor on the data that ends up in IFD (in this example it
|
|||
|
# might lock the IFD)
|
|||
|
postprocess IFD: util/ifdprocess -l
|
|||
|
|
|||
|
# a region called ME, starting at 4K, ending at 2M
|
|||
|
region ME: 4K 2M
|
|||
|
raw ME: 3rdparty/blobs/soc/intel/xanadu/me.bin align=bottom empty=0x00
|
|||
|
|
|||
|
# a region called BIOS, starting at 2M, filling up the free space
|
|||
|
# filling up fails (build error) if two regions are requested to fill up
|
|||
|
# against each other
|
|||
|
region BIOS: 2M *
|
|||
|
|
|||
|
# This would define a region that covers the last 4K of flash.
|
|||
|
# The BIOS region specified above will end right before it instead of
|
|||
|
# expanding to end of flash
|
|||
|
# region AUX: -4K -0
|
|||
|
|
|||
|
# specify the tool that post-processes the entire image.
|
|||
|
postprocess image: util/intelchksum/intelchksum.sh
|
|||
|
|
|||
|
coreboot manifest
|
|||
|
=================
|
|||
|
# declare that build/verstage.elf belongs into the group ‘verstage’
|
|||
|
# these groups are later referred to by the “cbfs” command.
|
|||
|
group verstage: build/verstage.elf stage xip name=fallback/verstage
|
|||
|
group romstage: build/romstage.elf stage xip name=fallback/romstage
|
|||
|
group ramstage: build/ramstage.elf stage name=fallback/ramstage
|
|||
|
compression=lzma
|
|||
|
group ramstage: build/dsdt.aml compression=lzma
|
|||
|
|
|||
|
boot method manifest
|
|||
|
====================
|
|||
|
# Define RO as region inside BIOS, covering the upper half of the image.
|
|||
|
# It’s a build error if the result crosses outside BIOS.
|
|||
|
# math expressions are wrapped with ( ),
|
|||
|
# and mentions of regions therein always refer to their size
|
|||
|
subregion BIOS RO: ( image / 2 ) -0
|
|||
|
|
|||
|
# Define RW to cover the rest of BIOS.
|
|||
|
# The order of RW and RO doesn’t matter except to keep comments clearer.
|
|||
|
# Dynamic items like RW (“*”) will be sized to fill unused space after
|
|||
|
# everything else is placed.
|
|||
|
subregion BIOS RW: 0 *
|
|||
|
|
|||
|
# It may be necessary to separate the RO/RW definition into another manifest
|
|||
|
# file
|
|||
|
# that defines the RO configuration of the flash
|
|||
|
|
|||
|
# Some more subregions, with dynamically calculated sizes
|
|||
|
subregion RW RW_A: 0 ( RW / 2 )
|
|||
|
subregion RW RW_B: * -0
|
|||
|
subregion RW_A FW_MAIN_A: RW_A * -0
|
|||
|
subregion RW_A VBLOCK_A: 0 64K
|
|||
|
# foo +bar specifies start + size, not (start, end)
|
|||
|
# also, start is given as “the end of VBLOCK_A”
|
|||
|
# (while using a region in the “end” field means “start of region”)
|
|||
|
subregion RW_A FWID_A: VBLOCK_A +64
|
|||
|
|
|||
|
# To make the example not too verbose, RO only has the CBFS region
|
|||
|
subregion RO BOOTSTUB: 0 *
|
|||
|
|
|||
|
# Postprocess the data that ends up in VBLOCK_A,
|
|||
|
# passing the listed regions as additional arguments.
|
|||
|
# Circular dependencies are build errors.
|
|||
|
postprocess VBLOCK_A(FW_MAIN_A): signtool
|
|||
|
|
|||
|
# binding files to regions indirectly through groups
|
|||
|
cbfs BOOTSTUB: verstage, romstage, ramstage, payload
|
|||
|
cbfs FW_MAIN_A: romstage, ramstage, payload
|
|||
|
|
|||
|
# defining defaults: unless overridden, in all regions that use CBFS (“*”),
|
|||
|
# we want all files to come with SHA256 hashes.
|
|||
|
# Wildcard defaults have lower priority than specific defaults.
|
|||
|
# Other conflicts lead to a build error.
|
|||
|
cbfsdefaults *: hash=sha3
|
|||
|
|
|||
|
payload manifest
|
|||
|
================
|
|||
|
group payload: payload.elf payload
|
|||
|
group payload: bootscreen.jpg name=splashscreen.jpg type=splashscreen
|
|||
|
|
|||
|
EC firmware manifest
|
|||
|
====================
|
|||
|
# overrides the cbfsdefault above
|
|||
|
group payload: ecrw.bin name=ecrw hash=sha256
|
|||
|
group payload: pdrw.bin name=pdrw hash=sha256
|
|||
|
|
|||
|
manifest parsing
|
|||
|
================
|
|||
|
The exact BNF is work in progress.
|
|||
|
|
|||
|
Some parser rules are
|
|||
|
* one line per statement
|
|||
|
* # introduces a command until the end of line
|
|||
|
|
|||
|
Some processing rules
|
|||
|
* When there’s a conflict (eg. two statements on what to do to a region,
|
|||
|
overlap, anything that can’t be determined), that is a build error.
|
|||
|
* the order of statements doesn’t matter, enabling simple addition of more
|
|||
|
manifests where the need arises.
|
|||
|
|