“If the only tool you have is a hammer, it’s hard to eat spaghetti.”
—David Allen
Looking back at history, tools have been the impetus for the evolution of the human race. Starting from the Stone Age through the Iron Age and to the modern computing age, tools have eased the work that humans have to do. Having the right tools for developing a boot firmware product is absolutely necessary to ease the development effort as well, provide a flexible interface for configuration, and offer seamless upgradability. This chapter focuses on the details of the various types of tools that a system developer should be equipped with for creating their own boot firmware.
The system firmware development journey includes various tools from its creation until deployment, as shown in Figure 2-1. This book is committed to providing detailed knowledge about various tooling requirements for each phase to prepare developers to work with the prerequisite software development kits (SDKs).
Integrated development environment: An integrated development environment (IDE) is software that helps developers ease their development process. It typically is a source code editor that provides some basic functionality such as the ability to create new files, search files, replace a string in a file, and more. Based on developers’ needs, there are various types of IDEs available, from basic ones such as the vim editor to advanced IDEs such as Eclipse, for firmware development.
Infrastructure tools: Chapter 3 provides ample details about the usage of infrastructure tools in system firmware development. Hence, this chapter will refrain from discussing the topic.
Debugging tools: Refer to Chapter 4 for a detailed overview of the debug methodology and debug tools usage on cross-architecture platforms. That chapter highlights the different types of tools used in system firmware development.
Build tools (compiler and stitching tools): Build tools are the first step toward creating a firmware image for embedded systems. This process involves taking source code as input and generating binary files as output. The next step combines all the binaries and produces the final firmware image. This process is highly dependent on the chip architecture, firmware architecture, and technology being used to create the system firmware.
Configuration tools: Configurability is a fundamental right for system integrators and validation engineers working on an embedded system. Relying on the development engineers to alter the source code to allow configuration and re-generate firmware images is not a scaled solution. This approach is a bottleneck for innovation. For example, it limits the scope for performing boundary testing with a wide range of data inputs. Even not a sustained model when the goal is to enable more ODM/OEM platforms with reduced development cost. Configuration tools allow developers to generate firmware images based on SoC and board-level configuration changes without rebuilding the system firmware image.
Flashing tools: The most important step to bring an embedded system to life is programming the flash parts. The purpose of flashing tools is to ensure the firmware image is correctly burned into the embedded system boot devices. The flashing process and mechanisms vary a lot, based on the underlying hardware design, target operating system, firmware architecture, technology being used, etc. A flashing tool might have a different interface and mechanism to consider based on the product lifecycle. The tooling process is different on the development side, factory side, and end-user side. Another consideration is that some firmware updates are over the air (OTA), meaning accomplished via a network.
The purpose of this chapter is to limit the discussion to the three major tooling needs (build, configuration, and flashing tools) while creating the system firmware and focus on the tools architecture across different system firmwares.
Build Tools
The most effective way to understand a system firmware architecture is by understanding its building blocks. Different firmware systems have much in common: they mostly use C, with some assembly, for example. The differentiating factors among these different system firmware are the underlying build tools.
- Refer to the Open Source EDKII project GitHub repository:
- Refer to the open source coreboot project GitHub repository:
Source code files: Files get compiled to generate object files or binaries that need further processing.
Flash layout files: Files are processed to combine various binaries to create the final firmware image.
- 1.
Set up the environment.
- 2.
Build the package components.
- 3.
Package the components to create the final firmware image.
EDKII Build Tools and Process
Prior to discussing the build process and understanding how the underlying build tools are getting used, let’s first understand the prerequisites to initiate the build process.
Build Environment Setup
Operating Systems | Compiler Tool Chains |
---|---|
Microsoft Windows | Visual Studio C compiler and Windows Driver Kit (WDK) |
Linux | Native GCC Installation 4.4 onward |
Additional Compilers | Details |
---|---|
Nasm | Nasm is required for the EDKII build if the source package has Intel-style assembly code. |
iASL | This is needed to compile ACPI Source Language (ASL) and generate .aml files. |
Python | EDKII has adopted Python for several build-related tools; hence, developers are expected to install Python to run Python-based tools from source. Examples: Build, GenFds, Trim, etc. |
Environment Variables | Description |
---|---|
WORKSPACE | The first variable to be set is WORKSPACE. This variable points to the root directory of an EDKII directory tree. More than one development tree can exist, and this environment variable is used to identify the current working directory tree. |
PACKAGES_PATH | This variable points out all possible required repositories for building the target project. For example, for building MinPlatformPkg, the developer needs to point to these three repositories: Edk2 Edk2Platforms/Platform/Intel Edk2Platforms/Silicon/Intel |
EDK_TOOLS_PATH | This points to the BaseTools directory belonging to the EDKII package. It contains binaries as mentioned in the EDK_TOOLS_BIN environment variable. |
EDK_TOOLS_BIN | This is the path to point out build tool binaries. This depends on the operating system. |
CONF_PATH | This is the variable to point out tool metadata files. |
ACTIVE_PLATFORM | Path to the .dsc file that represents the target embedded system |
---|---|
TARGET | Characteristics of system firmware like DEBUG or RELEASE |
TARGET_ARCH | Underlying SoC or CPU architecture of target hardware |
TOOL_CHAIN_TAG | To specify the compiler name example: GCC5 or VS2015x86 |
BUILD_RULE_CONF | Path to the build rule file (build_rule.txt) that specifies the build process, usage of build tools based input and output file types and specifies the type of cross-architecture platform. |
At this stage, the EDKII prebuild is done with all prerequisites except the build tools, which is required to start the build process.
Build Binaries
Developers to build the BaseTools directory at least once to create build tool executables and update EDK_TOOLS_BIN before compiling the target platform package (.dsc) to generate the final Flash Descriptor (FD) file.
Depending on the host machine, perform the following command to generate executable Build Tools:
For Windows: nmake all
For Linux: make -C Edk2/BaseTools
All the required utilities will be copied into the path specified by the EDK_TOOLS_BIN environment variable. This is a manual process and expected to execute once, unless the developer cleans the entire workspace.
The build_rule.txt file is intended to provide detailed input and the expected outcome from each build tool taking part in the build process.
Utilities | Description |
---|---|
build.py | The main interface to complete the entire build process. This tool is written in Python. The build calls the AutoGen process; then it calls make process, and finally the ImageGen process to create the final FD binary. |
AutoGen.py | Another key tool in the build process; this gets called by the build command. This tool is written in Python to work as a parsing tool to parse metadata files (INF, DSC, DEC, FDF) to autogenerate C source code files, makefiles per module, and the master makefile for the project with the help of other sets of libraries such as the following: GenC Library: Used by AutoGen.py to create AutoGen.h and AutoGen.c after parsing the metadata files to resolve PCDs, GUIDs, etc. GenMake Library: Used to create a makefile for each module and the top-level makefiles that can be further processed by nmake or gmake or cmake |
GenFw.exe | Part of the C source code tool. Responsible for creating UEFI Firmware Image (.efi) files based on the module types listed in the INF files as part of each module. |
GenSec.exe | Used to generate a valid EFI_SECTION type based on INF files. For example, as part of the .efi binary, each module also has several sections such as name sections, GUIDed sections, version sections, etc., based on associated fields in the INF file. |
GenFfs.exe | Part of ImageGen process to create FFS files based on FDF to take part into firmware volume (FV). One can make use of the GenFds tool as well to create the FFS file. |
GenFds.py | To process the files generated as part of the build binary and associated binary files that are listed in the FDF and DSC files to be part of the final Flash Device (FD) image. |
In addition to the previous list, there are other useful utilities that are used for some specific purposes such as compression, generating an EFI option ROM image from the .efi file, creating a configuration file and/or patching a binary module, and creating an FD image.
Build Process
AutoGen process
Make process
ImageGen process
Every build process listed here involves certain build tools (described in earlier sections), driven by a fixed set of input files, that generate output files, which are used as input to the next build process. This process continues until a final firmware file (a so-called FD file) is generated.
AutoGen Process
This process was introduced in the EDKII build infrastructure. The major drawback that EDK had was each module needed to create its own makefile (similar to the C programming logic) to link the required libraries, GUID, etc., prior to compiling a module. It is difficult for a new developer who is transitioning into an EDK-based framework for firmware development.
EDKII has introduced this concept of AutoGen to parse the metadata files as part of the target package and/or individual module to generate some C source code files, the makefiles for each module, and even the master makefile for the target project.
AutoGen.py: This tool is used to parse the metadata files with the help of another two libraries, GenC and GenMake, to create AutoGen.c, AutoGen.h, and other makefiles.
File | Name | Description |
---|---|---|
.dsc | Platform Description | Each package has one DSC file to describe the build rules, libraries, and components (INF) being used. A platform build process starts with this file. |
.dec | Package Declaration | Declares the interfaces that are being used in that package. |
.inf | Module Definition | Each module has one INF file to define the interface, source code, libraries, usage of GUID, etc. |
.fdf | Flash Description File | File in each package that provides the flash layout and associated firmware volumes. Each component that is described in this file will take part in final binary creation. |
Top-level makefile: This is the makefile for the entire package that resolves all libraries, GUIDs, and any global definitions as part of the package.
Makefiles per module: A C-based module can’t really compile without having a dedicated makefile. The parsing tool will generate a makefile for each module based on the INF file, after resolving the required dependencies using other packages.
Autogenerated C source code: The source code as mentioned in the INF files is mostly written in C, where it relies on C-based data structures. With the metadata-based implementation in EDKII, it needs a parser to create an autogenerated AutoGen.h or .AutoGen.C file based on the need to provide macro definitions and resolve external symbols for the next build process (which is the make process).
Make Process
Build binary: This process involves standard C-compilers, assemblers, static linkers, and dynamic linkers to generate PE/PE32+/COFF images from component or module types listed in the INF files. Additionally, the GenFw tool is used to generate EFI firmware image files.
Input files: The make process takes two types of files as inputs. The source code belongs to the target and associated packages (C source, headers, ASM source and includes, ACPI and ASL files, etc.) and the autogenerated C source code headers and makefiles.
- Output files: The purpose of the make process is to generate EFI file images that can be further used during the ImageGen process. But based on the input files, it can also generate .acpi, .aml, and .bin binaries. Figure 2-4 has provided a detailed control flow for generating EFI files based on input C source code. This process involves several intermediate steps, as shown here:
- 1.
The standard C-based compiler has compiled the source code to generate .obj files.
- 2.
.obj files have been given as input to the static linker to generate static library files (.lib), which further process into dynamic library files (.dll) with the help of a dynamic linker.
- 3.
Finally, the GenFw tool as part of the $(make) phase converts the .dll into an EFI_SECTION type. The EFI file format is compatible with the PE32/PE32+/COFF format.
ImageGen Process
Build binary: Once the EFI section files have been created in the previous step, they need to be placed within an FFS file. GenFfs is the build tool that generates FFS files after combining those EFI_SECTION type binaries with the FFS header. GenFds is used to construct the final firmware binary.
In addition, there might be a few modules and components that are part of the DSC files that are not meant to be part of a final firmware binary like Option ROM (OpROM). The EfiRom tool builds an option ROM image from an EFI file.
Input files: At this stage, all components or modules that are part of the target platform package DSC file are now converted into build binaries. Out of those build binaries, module types with UEFI applications and OpROM are excluded from being part of the final Flash Descriptor (FD) creation process. This final build process depends on the FDF file to create the flash layout.
Output files: The FDF file section provides an overview of generating one or more firmware volumes (FVs). The FV section describes how to combine FFS files to create FV files. Multiple FV files are combined to create the final firmware image as a flash device (FD). FD tokens provide the BaseAddress, Size, ErasePolarity, BlockSize, and NumBlocks fields.
BaseAddress | $(FLASH_BASE) | gSiPkgTokenSpaceGuid.PcdBiosAreaBaseAddress |
---|---|
Size | $(FLASH_SIZE) | gSiPkgTokenSpaceGuid.PcdBiosSize |
ErasePolarity | 1 |
BlockSize | $(FLASH_BLOCK_SIZE) |
NumBlocks | $(FLASH_NUM_BLOCKS) |
At the end of this phase, the final firmware binary at a size of $(FLASH_SIZE) is ready to use the EDKII framework to flash on the target embedded system.
This section provided an overview of the build system for an EDKII-based system firmware development. The next section will focus on the build tool and its creation process using another system firmware architecture, known as coreboot.
coreboot Build Tools and Process
For a better understanding and to be able to compare the build process with EDKII, this section presents the coreboot build flow almost in the same manner as was presented in the previous section.
Build Environment Setup
This section provides the prerequisites to download the coreboot source code, which includes both the package source code and the source code for build tools. Additionally, it provides details about the compiler based on the host systems and environment variables that are expected to be set based on the target embedded system architecture.
The coreboot build process is based on GNU make; hence, developers need to have a Linux and UNIX-equivalent operating system on the host side with native GCC installed. GCC is the compiler for building coreboot (alternatively, one can also use LLVM/clang to build coreboot).
Additional Compilers | Details |
---|---|
iASL | This compiles ACPI Source Language (ASL) and generate .aml files. |
Flex | The GNU flex tool is used to parse the device tree field as it works as a lexical parser. |
Bison | The GNU bison tool works as a grammar parser for device tree files. |
The coreboot prebuild phase is to ensure that all associated coreboot project-related files are also getting synced from their respective repositories prior to building the target project or mainboard. These associated projects are maintained separately as part of Git submodules and checked out automatically after fresh coreboot code sync using git submodule update --init --checkout. The reason for maintaining these submodules separately is because they might be additional firmware projects by themselves like vboot or might be some SoC-specific closed source binary blobs like FSP, CSE, or PSP, etc., used to generate the final firmware binary.
The prebuild phase also sets up the required basic environment variables.
Environment Variables | Description |
---|---|
FIRMWARE_ARCH | This variable points to the target hardware architecture for which these files are getting compiled. Values can be used as x86, arm, etc. |
KCONFIG_CONFIG | This variable points to the input config file. Typically, this is referred to as $(DOTCONFIG). |
KCONFIG_STRICT | The value assigned to this variable is build/util/kconfig/conf --oldconfig src/Kconfig. Define to enable warning as errors. |
KCONFIG_AUTOHEADER | This variable points to the path and filename of the auto.conf file. |
The tool chain configuration file (toolchain.inc) is meant to provide paths for compiler, assembler, and linkers based on the target hardware architecture.
At this stage, the coreboot prebuild has been done with all prerequisites except the build tools, which are required to start the build process.
Build Binaries
To start the final firmware binary generation process based on the target hardware, coreboot needs to build the cross-compiler, such as x86, arm, risc v, etc. For coreboot, the entire build process is designed based on GNU make; hence, running a make help command would list the possible tool chain options.
Additionally, developers need to build other tools as per package source support or build flow needs.
Let’s understand a few widely used build tools/utilities in detail and their usage prior to discussing the build process.
Kconfig
config: Text mode configuration; asks developer about each configuration option
menuconfig: Menu-driven configuration tool (added from Linux 2.5.45) still in Text mode
Output: After parsing all the source files, the Kconfig tool generates a HEADER file with a list of values inside build/auto.conf, which will be further used by the source code and makefiles of the project.
Sconfig
Lex: The GNU flex tool is used as a lexical analysis tool that can parse the .cb file to identify specific text strings such as chip, register, device, pci, on, off, domain, cpu_cluster, irq, etc.
Yacc: This is a grammar parser. The GNU bison tool is used to provide this functionality. The output of the flex tool is used as input for this tool to understand what action to take when a token is being identified. Examples: for Interrupts INT[A-D], for decimals [0-9.]+, for hexadecimal 0x[0-9 a-f A-F.]+, etc.
- 1.
util/sconfig/lex.yy.c_shipped is converted to lex.yy.c as a source file.
- 2.
YACC takes lex.yy.c as input and creates the sconfig.tab.c and sconfig.tab.h files that contain the macros for the tokens.
- 3.
The Sconfig utility is generated from the combined output from lex and YACC.
- 4.
Finally, the Sconfig utility is used to generate static.c, static.h, and static_fw_config.h files.
cbfstool
eXecute-In-Place (XIP): cbfs components marked as --xip will execute from the address where they’ve been mapped in SPINOR. Examples: verstage, FSP-M binary. To support XIP components with a higher SPINOR (32MB), cbfs has introduced concepts called extended window and extended window size that are mapped anywhere outside (4GB to 16MB) the system memory range.
Position-independent modules: Modules can be relocated anywhere in physical memory after it’s being available. The raw binaries like data or configuration files also belong to this category.
There could be more than one cbfs inside the coreboot final binary based on project design. For example, on Chrome OS projects there are three CBFSs per image: COREBOOT, FW_MAIN_A, and FW_MAIN_B.
fmaptool: This is the Flashmap Descriptor language and compiler. It’s a tool that is able to parse the textual representation of an .fmd file and describe the layout of the flash chips that might contain more than one CBFS. This tool creates an intermediate file called fmap_config.h with the start and size of each component that would like to be part of fmap blobs. The final output of this tool is fmap.fmap.
rmodtool: This is another tool that is part of cbfstool, which is intended to parse and convert ELF type files to rmodules. For .elf files as part of the coreboot project, those that are supposed to get executed after the physical memory is initialized and not using SPI mapped memory, they are using rmodtool to generate position-independent code and data blocks.
Utility | Description |
---|---|
ifdtool | Intel Flash Descriptor (IFD), an open source tool that is used on the Intel platform to stitch boot-critical binaries (referred to as SI_ALL in .fmd) like Intel ME, GbE, EC, and Flash Descriptor along with coreboot BIOS region, referred as SI_BIOS in the .fmd file. Developers can use this tool to override the Flash Descriptor fields as well as part of the manufacturing flow. |
apcb | AMD Platform Security Processor (PSP) Control Block Tools. This tool is capable of patching an existing APCB binary into the PSP blob. A part of this tool (apcb_edit.py) is allowed to patch the AMD PSP Customization Block (APCB). A binary has been integrated into the PSP to provide the SPD information and perform the required GPIO programming to select and load the right SPD. |
amdfwtool | This tool is used to inject various images needed by the PSP to complete the reset flow. This tool takes image name, size, and intended location in the firmware structure. The output of this tool is amdfw.rom, which holds headers, pointers, and added firmware images. |
amdcompress | A utility to generate a compressed BIOS image for AMD Family 17h. This compressed image is added into PSP’s amdfw.rom binary. The modern AMD system has the PSP, which is able to bring up DRAM prior to x86 reset; hence, the PSP decompresses (the PSP has support for zlib engine) the BIOS image into the DRAM and starts execution. |
Build Process
AutoGen process
Make process
ImageGen process
Every build process listed here involves picking up the correct build tools (described in earlier sections) and having a fixed set of input files, and the target is to generate the output files that can be further worked as input for the next builder script. This process continues unless a final firmware file is generated.
AutoGen Process
Autogenerated C header that holds the value of lists of Kconfig being used on the project package (CONFIG_*). The auto.conf header file is the outcome of parsing the Kconfig file by the Kconfig build tool.
Static C source and header files are being generated as part of this process by the Sconfig tool. These files are getting used further to provide the hardware configuration snapshot to the system firmware during boot.
File | Name | Description |
---|---|---|
*.CB | Device Tree Files | Each package should have at least one devicetree.cb file, which allows for the board-level configuration along with providing the snapshot of the CPU and PCI bus endpoint devices. At runtime this snapshot is being used to enable/disable the hardware interface or initialize a device. |
Make Process
The top-level makefile for the project and all the package makefiles are used in the make process. The input for the make process includes autogenerated C source and headers from the previous stage; coreboot provides assembly and C source code per stage such as bootblock, romstage, postcar, ramstage, and SMM. The coreboot build process is flexible enough to make any boot stage optional. For example, if the system firmware design doesn’t need to have a dedicated ramstage as a stage for loading the payload, then coreboot can generate the final ROM image without compiling files for ramstage. These coreboot stages are independent enough in this build process to generate the ELF file after using the FIRMWARE_ARCH-specific compiler, assemblers, and linkers.
On the x86 platform where SPINOR is being used as a boot device and in absence of physical memory, it needs to ensure that the bootblock is able to patch at the reset-vector where coreboot is able to execute the bootblock instruction upon hitting CPU reset. In addition, there are a few stages (.ELF) that would like to load as a position-independent binary; hence, there is a need to use rmodtool to generate a special .rmod binary for stages that are typically getting executed after DRAM is initialized.
File | Name | Description |
---|---|---|
.fmd | Flashmap File | Each board package is equipped with one .fmd file to provide the description about flash layout. This .fmd file might consist of more than one region (SI_ME, SI_EC, etc.) in addition to the BIOS region, specified as SI_BIOS. Based on the project requirement, this FMD file might have more than one CBFS as well. |
ImageGen Process
This phase is responsible for creating the final firmware image (ROM) after adding all those build binaries from the previous stage into the CBFS.
Create CBFSs: The coreboot build process relies on the cbfstool build to add the .elf and .bin binaries into sections into cbfs as per the following example where different coreboot stages and raw binary is getting injected to create the final ROM image:
Add SoC/CPU vendor-specific binary: This process involves using vendor-specific binaries and tools to inject into coreboot.rom to call it the final firmware binary. For example, on the Intel platform, ifdtool is used to inject a descriptor, ME and EC, whereas on the AMD platform, an additional step has to be performed to compress the bootblock (using amdcompress) and create the build binary (amdfw.rom using amdfwtool) into PSP; it can then be added directly into the coreboot image.
At the end of this phase, the final firmware binary (ROM) at a size of CONFIG_ROM_SIZE is ready using the coreboot open source firmware model to flash on the target embedded system.
Configuration Tools
The configuration tools are intended to allow changes in the final firmware image without going through the entire build process. The scope of configuration can vary between static and dynamic based on the system where this tool is running. For example, a tool can be running as part of the host system. Taking a final firmware binary (FD or ROM) as input to modify its default configuration value is known as static configuration. Or, if during the runtime execution on the target hardware a native configuration interface allows modification of the configuration database, this is called a dynamic configuration tool.
In this section, we will discuss a few widely used tools in the scope of different system firmware architectures like coreboot, EDKII, and Slimboot that are allowed to modify the configuration database.
Human Interface Infrastructure
Legacy system firmware was lagging in terms of allowing a unified approach to configure the underlying hardware with different hardware vendors providing their own configuration tools and access mechanisms. This made it harder for system integrators to design a robust interface for end users and various other users of system firmware.
On the UEFI platform, the Human Interface Infrastructure (HII) is used to provide a flexible and standard way to configure the target hardware. HII allows the platform configuration to access the hardware interface and store the data using the form browser. The form browser is like a web page that uses display and input devices for configuration to take place. Figure 2-7 shows the high-level operational model of HII.
HID devices: Input devices are used for configuration in the form of localization. HII supports localization, a process that helps a product adapt to the local market. HII supports keyboard mapping to allow users to choose their own language as input.
Display devices: The output devices support the localization. HII supports Unicode characters, which allows it to support all possible languages to display as part of the form.
HII database: The HII database is created dynamically as the system boots. The UEFI driver is required to register a list of HII packages into the HI database. The package list provides different types of binary data. The data types could be font, string, image, keyboard layout, form, etc.
Driver: The UEFI driver provides the Config Routing Protocol as ExtractConfig, RouteConfig, and Callback to retrieve and save configuration information associated with HII forms.
NV Storage: The NV Storage is to store any data which remains persistent even after the system is resuming from the mechanical off state. The NVRAM is getting used as the NV storage. The HII form retrieves the configuration data from NV Storage and allows modification of these parameters from the available configuration list. The EFI variable services protocol is used to access the NV Storage.
- Forms Browser: This is a GUI to represent the HII and allow users to configure the options. HII has its own standard architecture and language as IFR and VFR to present the browser with the help of a Unicode string.
IFR : Internal Forms Representation is the architectural binary encoding used to present the user interface pages. The Vfr compiler takes VFR files as input and generates output IFR files.
VFR: Visual Forms Representation is the source code language that is used by developers to design a form page.
VFR | IFR |
---|---|
form formid = 1, title = STRING_TOKEN( STR_FORM1_TITLE ), | typedef struct _EFI_IFR_FORM { EFI_IFR_OP_HEADER Header; UINT16 FormId; EFI_STRING_ID FormTitle; } EFI_IFR_FORM; |
YAML-Based Configuration
YAML-based configuration is part of Slim Bootloader (SBL) configuration process to provide a simple and flexible method to modify the board-specific parameter to support new boards. SBL provides configuration parameters that are getting used for platform initialization and are typically categorized into memory; SoC hardware interfaces like USB, PCIE, and GPIO; and OS boot options.
SBL configuration parameters are packed in a configuration binary blob. This binary blob is stitched with BIOS regions to apply the configuration at runtime (during Stage 1B phase of SBL).
The idea of the configuration binary block is to support multiple board configurations using a single system firmware image; hence, this blob contains configuration parameters for multiple different boards. The configuration binary block starts with a configuration blob header and is followed by the configuration parameters that are organized in configuration blocks. Each configuration block contains a block header followed by the parameter structure. Each configuration block is identified by a unique tag as PLATFORM_CFG_DATA, MEMORY_CFG_DATA, etc.
Users can open the CfgDataDef.yami file using the ConfigEditor GUI tool to allow the default value to be overridden.
All configuration YAML files will be processed by configuration tools like GenCfgData, CfgDataTool, and CfgDataStitch to generate configuration header files and binary blobs.
Finally, use the CfgDataStitch tool to patch the new configuration data file into the final firmware binary.
Firmware Configuration Interface
Traditionally, coreboot has limited dynamic configuration capabilities compared to other system firmware used on the embedded system. Because coreboot was designed with the principle of instant boot and a small footprint, it doesn’t provide much scope for a preboot configuration environment like a UEFI browser. This eventually results in source-based modifications to enable/disable certain hardware interfaces as per system firmware users’ needs.
The firmware configuration interface in coreboot is designed to overcome such limitations and allow users to configure the possible hardware interface at runtime. This interface will also help to maintain a single firmware image that can work seamlessly on different motherboards where the base schematics is the same but the I/Os might be different.
The coreboot Kconfig option CONFIG_FW_CONFIG can be used to enable this feature where the platform has decided to provide a bunch of configuration options as part of the devicetree.cb source code for runtime configuration.
CBFS
A 64-bit raw value can be stored into CBFS with the name CONFIG_CBFS_PREFIX/fw_config. To enable this feature, coreboot needs to be built with the CONFIG_FW_CONFIG_CBFS option.
At runtime while the fw_config_probe() function is getting called, it will check the Kconfig option and load the CBFS filename to get this configuration value. cbfstool can be also used to override this binary blob at build time.
Embedded Controller
On the Chrome OS platform, the embedded controller interface will read and write the firmware configuration value using the CrOS Board Info (CBI) command. Mainboard users can select CONFIG_FW_CONFIG_CHROME_EC_CBI options to read the fw_config value from EC CBI.
The ectool command can also be used to configure this value using the ChromeOS environment.
Firmware Configuration Table
The firmware configuration table, which is part of the mainboard devicetree.cb file, starts with a special token as fw_config and terminates with end. The SCONFIG tool is used to parse this fw_config token to generate a static_fw_config.h file after understanding the grammar, where each field is defined by providing the field name, the start bit, and the end bit. Inside each field block, the option is used to provide the possible option name and associated value. This example configures the EMMC boot using fw_config:
Binary Configuration Tool (BCT)/Config Editor
Flashing Tools
In the system firmware development approach, the last but very significant tool is the flashing tool. Starting from the source code development all the way up to generating the final firmware image, the process will fulfill its purpose only if the embedded system is able to boot after flashing the final image on the targeted embedded system’s boot device. The flashing tools are highly specific to the target embedded system; they need to know the hardware interface that can be used to flash into the boot device from the local or remote host. At the high level, these flashing tools can be divided into two categories: hardware-based tools and software-based tools.
In this section, we will discuss a few popular flashing tools used on the target hardware for flashing the system firmware. The assumption being made here is that all these embedded systems are using SPINOR as the boot device.
Hardware-Based Tools
Hardware-based tools are used to flash the embedded systems in some specific scenarios like a device with an empty SPINOR as the very first boot during the manufacturing process, a corrupted SPINOR, or the device being unable to boot to the OS or pre-boot console to allow flashing using a software-based mechanism. This type of tool requires the host system to be connected to the target device where the host and the target device will connect using the hardware interface like USB or serial and where the host system is running the flashing software to write into the target device SPINOR.
SPINOR Programmer
Mostly in all the latest embedded platforms, the SPINOR is the default and soldered onto the board; hence, either the board design has an SF programming header where the SPINOR programmer can be attached to flash the firmware image or you need to make use of some custom clip solution to attach directly onto the SPINOR chip itself for flashing. Dediprog SF100 and SF600 are the widely used in system programing. Programmers can flash the firmware on the target system using a USB interface.
Servo
This ensures the recovery of the bricked system by flashing the EC and system firmware using Servo.
Software-Based Tools
The software-based tools don’t have any prerequisites for the underlying hardware interface or any dependency over the host system. These kinds of tools run on the target device to allow read, write, or erase on the SPINOR using the pre-OS or OS environment. The following are a few popular software-based flashing utilities being used on cross-firmware projects.
Flashrom
An open source flashing utility is used across various operating systems and motherboards for detecting, reading, writing, erasing, and verifying the flash chips. The Flashrom tool can be used to flash coreboot/EFI images on the supported mainboard. Flashrom has support for various AICs like network, graphics, and storage.
It also supports a wide range of SPI programmers using USB interfaces like FTDI FT2232/FT4232H. The flashing mechanism is different from flashrom while using hardware-based tools like Servo to run as a user space application on the DUT itself.
UEFI Tools and Utility
Different vendors have created their own platform flashing tools using UEFI pre-boot services or native OS-based drivers to access the underlying SPI Flash device with the highest privilege.
AMI Firmware Update (AFU) is one popular tool (AfuEfix64.efi) used on the Aptio platform either from the pre-boot environment or from the OS layer to update the SPINOR. Many OEMs are using the uefiflash.efi binary to update the final firmware image from the EFI Shell environment as part of the shell automated script.
Summary
This chapter has provided detailed analysis of various system firmware development tools such as build tools, configuration tools, and flashing tools that a developer has to be equipped with prior to starting their own system firmware development. This chapter might also be useful for firmware developers to understand the underlying tools architecture and build process for popular boot firmware like EDKII and coreboot. This knowledge can be directly applied while creating your own system firmware using a new SoC/CPU architecture that is not yet supported by existing system firmware (EDKII or coreboot).
The book System Firmware: An Essential Guide to Open Source and Embedded Solutions has a case study about adding support for a new RISC-based CPU using EDKII.
We also discussed possible configuration options across different leading system firmware solutions so that developers can make the right decision when choosing the correct boot firmware for their target embedded system. Understanding flashing tools and the underlying hardware interface is useful to ensure guaranteed recovery even from the bricking state of the target device.