The aim of LinuxBoot is to reduce complexity and obscure firmware by moving that functionality into kernel and userspace.
This chapter describes the procedures from a LinuxBoot workshop where an Atomic Pi board with UEFI firmware was converted to run LinuxBoot. The build materials associated with this are found at digitalloggers/atomicpi.
Read the below and consult the Makefile for the details of how it was implemented.
UEFI has three sections:
DXE process is very complex; some systems have 750 DXEs.
LinuxBoot replaces most of the UEFI software with Linux. LinuxBoot has an initramfs provided by u-root.
The above are stored inside a flash filesystem (FFS) inside a region of flash on your motherboard (the BIOS region). Another important region of flash is the ME region.
The Management Engine (ME) is an x86 CPU embedded in the Intel Platform
Controller Hub (PCH). It runs the Minix operating system which boots first and
enables hardware such as clocks and GPIOs. ME checks the contents of flash
memory and is used to implement “BootGuard”. If you reflash and the ME is in
“BootGuard” mode, your machine will be unusable. You need to run a tool called
me_cleaner
on the image to disable BootGuard.
Start with a board running standard UEFI and proceed from “zero changes to FLASH” to “max changes” in 4 steps:
One of the challenges in the above is in finding (or reclaiming) enough space in flash to shoehorn your kernel and initrd into.
There are two tools you use when you modify the UEFI flash image: utk
and
me_cleaner
.
The ME Cleaner tool:
/usr/bin/python2 me_cleaner.py -s
imagefile.bin
me_cleaner
sets the high assurance platform (HAP) bit. HAP provides a way to
disable a feature on Intel chips that does not allow us to modify the UEFI
image and install LinuxBoot. Setting the bit with me_cleaner
disables the
“feature”. Note that this does not always work; check with the LinuxBoot
community.
When you run me_cleaner
:
~/projects/linuxboot/me_cleaner/me_cleaner.py -s /tmp/rom.bin
you should see output similar to the following:
Full image detected |
Found FPT header at 0x1010 |
Found 20 partition(s) |
Found FTPR header: FTPR partition spans from 0x6f000 to 0xe700 |
ME/TXE firmware version 2.0.5.3112 (generation 2) |
Public key match: Intel TXE, firmware versions 2.x.x.x |
The AltMeDisable bit is SET |
Setting the AltMeDisable bit in PCHSTRP10 to disable Intel ME… |
Checking the FTPR RSA signature... VALID |
Done! Good luck! |
By applying me_cleaner
, it has been observed that almost 4M of flash ram can
be reclaimed for use. That 4M is enough to store a reasonably full featured
compressed initrd image.
The utk
tool can:
If the system only has a net interface, you use Dynamic Host Configuration Protocol (DHCP), using broadcast DISCOVER, and Trivial File Transfer Protocol (TFTP) to get the boot information you need.
Configuration information is provided by REPLY to a DHCP request. The REPLY returns an IP, server, and a configuration file name that provides:
Data is provided by TFTP. HTTP downloading takes a fraction of a second even for 16M kernels. With TFTP it’s very slow and TFTP won’t work with initramfs much large than 32MiB. Most LinuxBoot shops use or are transitioning to HTTP.
Note: Boot images require a kernel(bzImage) + an initramfs + a command line. They can be loaded as three pieces or compiled and loaded as one piece, as described in this section.
There are two main ways to read and write the flash - hardware and software.
Hardware: It is worth buying a Pomona 5250 SOIC Clip adapter to read directly by hardware to have something to roll back to if anything goes wrong. Avoid cheap SOIC clip adapters that don’t allow you to use standard jumper leads. For a good example of using a Raspberry Pi 3/4 to read/write, see Sakaki’s EFI Install Guide/Disabling the Intel Management Engine
Software: With a working boot image, use flashrom to read an image of your flash. To write you may need to disable flash protections (look for “ME Manufacturing mode” jumpers on your motherboard). Figure on generally using software methods for reading & writing flash, but with hardware to drop back to.
Open your flash image with UEFITool, and locate the filesystem containing the
DXE’s (it will have the Shell or Shell_Full
in it ). Check how much volume free
space is in that filesystem - this will be an initial limit when you come to
place your kernel and initramfs in it in step 5.
Start small and work your way up.
utk
command replaces the Shell code section with a Linux
kernel:
utk firmware.bin replace_pe32 Shell bzImage save
new.binutk
, you can reflashutk
automates removing DXEs: this is the DXE cleaner
utk
removes a DXE, reflashes, checks if it boots, repeat
This part should be easy: DXE can have a dependency section. In practice,
it’s hard: because dependency sections are full of errors and omissions. A lot
of UEFI code does not check for failed DXE loads.me_cleaner
and then utk tighten on the source image, then inspect the
image using UEFITool. If successful, there will now be padding at the
beginning of the BIOS region of a substantial size.utk
to remove the
proprietary one and replace it with one built from source. You can get DXE
source from the tianocore/EDK2 source repo at github.com. The GitHub repo has a
limited number of DXEs in source form; i.e., you can’t build a full
working image using it.flashrom -p internal -w _filename.bin_
where
filename.bin is a filename of your choosing.flashrom
with an external device such as an sf100. There may be a
header on the board, or you might have to use a clip.
flashrom -p dediprog:voltage=1.8 -w _filename.bin_
The voltage option is required for the Atomic Pi.