The manifest example has to be marked as code. The Manifest parsing section header should be L2 and finally # is a special character in markdown. Change-Id: I38cb1a508ec9ccb39cb39048de3742a5cb595f7b Signed-off-by: Arthur Heymans <arthur@aheymans.xyz> Reviewed-on: https://review.coreboot.org/c/coreboot/+/36199 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Patrick Georgi <pgeorgi@google.com>
		
			
				
	
	
		
			269 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			269 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.
 | 
						||
 |