4.9. barebox on (U)EFI¶
barebox can be built as an EFI application for X86 PCs. This makes barebox a bootloader running on PC type hardware. In EFI jargon barebox would be a EFI shell. Due to the barebox Boot Loader Specification support it can act as a replacement for gummiboot.
For accessing hardware the EFI drivers and abstractions are used. barebox
has several drivers which merely map to the underlying EFI layers. A plain
barebox binary provides access to the screen and keyboard. The EFI System
partition (ESP) is available under /boot
, additional partitions may
be available as /efi*
. Networking may be available if the BIOS provides
the necessary drivers, but most likely you’ll have to download/compile
network drivers yourself, see below.
Depending on the CONFIG_64BIT
option either a ia32 binary or a x86_64
binary is built. Due to the lack of 32bit UEFI testing hardware only the
x86_64 binary currently is tested.
4.9.1. Building barebox for EFI¶
Use the following to build barebox for EFI:
export ARCH=x86
make efi_defconfig
make
The resulting EFI image is barebox.efi
(or the barebox-flash-image link).
4.9.2. Running barebox on EFI systems¶
The simplest way to run barebox on a USB memory stick. (U)EFI only supports
FAT filesystems, so make sure you either have a FAT16 or FAT32 filesystem on
the memory stick. Put barebox.efi
into the EFI/BOOT/
directory and
name it BOOTx64.EFI
on 64bit architectures and BOOTIA32.EFI
on 32bit
architectures. Switching to USB boot in the BIOS should then be enough to
start barebox via USB. Some BIOSes allow to specify a path to a binary to
be executed, others have a “start UEFI shell” entry which executes
EFI/Shellx64.efi on the ESP. This can be a barebox binary as well.
To use the Barebox State Framework, the describing devicetree file state.dtb
has to be put into the EFI/barebox/
directory.
Supported backends for EFI are raw partitions that can be discovered via a
partition UUID.
With this sample script you can create bootable image and transfer it to the flash driver:
truncate --size 128M barebox-boot.img
echo 'start=2048, type=ef' | sfdisk barebox-boot.img
LOOPDEV=$(losetup --find --show barebox-boot.img)
partprobe ${LOOPDEV}
# Create filesystems
mkfs.fat -F32 ${LOOPDEV}p1
MOUNTDIR=$(mktemp -d -t demoXXXXXX)
mount ${LOOPDEV}p1 $MOUNTDIR
mkdir -p ${MOUNTDIR}/EFI/BOOT/
cp barebox.efi ${MOUNTDIR}/EFI/BOOT/BOOTx64.EFI
if [ -d network-drivers ]; then
cp -r network-drivers ${MOUNTDIR}/
fi
umount ${MOUNTDIR}
losetup -d ${LOOPDEV}
dd if=barebox-boot.img of=/dev/sdX
4.9.2.1. Running EFI barebox on qemu¶
barebox can be started in qemu with OVMF http://www.linux-kvm.org/page/OVMF.
OVMF is part of several distributions and can be installed with the package management system. qemu needs the OVMF.fd from the OVMF package file as argument to the -pflash option. As qemu needs write access to that file it’s necessary to make a copy first.
To start it create a USB memory stick like above and execute:
qemu-system-x86_64 -pflash OVMF.fd -nographic /dev/sdx
A plain VFAT image will work aswell, but in this case the UEFI BIOS won’t
recognize it as ESP and /boot
won’t be mounted.
4.9.3. Loading EFI applications¶
EFI supports loading applications aswell as drivers. barebox does not differentiate between both. Both types can be simply executed by typing the path on the command line. When an application/driver returns barebox iterates over the handle database and will initialize all new devices.
4.9.3.1. Applications¶
barebox itself and also the Linux Kernel are EFI applications. This means both can be directly executed. On other architectures when barebox is executed from another barebox it means the barebox binary will be replaced. EFI behaves differently, here different barebox instances will be nested, so exiting barebox means passing control to the calling instance. Note that currently the reset - perform RESET of the CPU command will pass the control to the calling instance rather than resetting the CPU. This may change in the future.
Although the Linux Kernel can be directly executed one should use the bootm - boot an application image command. Only the bootm command passes the Kernel commandline to the Kernel.
4.9.3.2. Drivers¶
EFI is modular and drivers can be loaded during runtime. Many drivers are included in the BIOS already, but some have to be loaded during runtime, for example it’s common that network drivers are not included in the BIOS.
Drivers can be loaded under barebox simply by executing them:
barebox:/ /boot/network-drivers/0001-SnpDxe.efi
Should the drivers instanciate new devices these are automatically registered after the driver has been loaded.
4.9.4. Simple Network Protocol (SNP)¶
The Simple Network Protocol provides a raw packet interface to the EFI network drivers. Each device which supports SNP shows up as a regular network device under barebox. To use SNP the BIOS must have the SNP protocol and the network driver installed. For getting the SNP protocol follow the instruction in Building EDK2. Network drivers for the common Intel Network devices can be found here:
https://downloadcenter.intel.com/Detail_Desc.aspx?agr=Y&DwnldID=19186
Once instantiated the EFI drivers take some time to bring up the link, so
it’s best to only load the network drivers when needed. This can be
archieved with the following script to put under /env/network/eth0-discover
:
#!/bin/sh
for i in /boot/network-drivers/*; do
$i;
done
This script will load the drivers in /boot/network-drivers/
in alphabetical
order.
NOTE Loading the network drivers only works when loaded in the correct order. First the SNP driver must be loaded and then the network device driver. Otherwise the drivers will load without errors, but no devices will be instantiated. For making the order sure the driver names can be prepended with a number:
/boot/network-drivers/0001-SnpDxe.efi
/boot/network-drivers/0002-E6208X3.EFI
It is currently not known whether this is a limitation in EFI or a bug in barebox.
4.9.5. EFI File IO Interface¶
EFI itself has filesystem support. At least the ESP will be mounted by the
EFI core already. The ESP is mounted to /boot
under barebox, other devices
are mounted to /efi<no>
in no particular order.
4.9.6. Block IO Protocol¶
EFI provides access to block devices with the Block IO Protocol. This can
be used to access raw block devices under barebox and also to access filesystems
not supported by EFI. The block devices will show up as /dev/disk<diskno>.<partno>
under barebox and can be accessed like any other device:
mount /dev/disk0.1 -text4 /mnt
Care must be taken that a partition is only accessed either via the Block IO Protocol or the File IO Interface. Doing both at the same time will most likely result in data corruption on the partition
4.9.7. EFI device paths¶
In EFI each device can be pointed to using a device path. Device paths have multiple components. The toplevel component on X86 systems will be the PCI root complex, on other systems this can be the physical memory space. Each component will now describe how to find the child component on the parent bus. Additional device path nodes can describe network addresses or filenames on partitions. Device paths have a binary representation and a clearly defined string representation. These characteristics make device paths suitable for describing boot entries. barebox could use device paths to store the reference to kernels on boot media. Also device paths could be used to pass a root filesystem to the Kernel.
Currently device paths are only integrated into barebox in a way that each EFI device
has a device parameter devpath
which contains its device path:
barebox:/ echo ${handle-00000000d0012198.devpath}
pci_root(0)/Pci(0x1d,0x0)/Usb(0x1,0x0)/Usb(0x2,0x0)
4.9.8. EFI variables¶
EFI has support for variables which are exported via the EFI Variable Services. EFI variables are identified by a 64bit GUID and a name. EFI variables can have arbitrary binary values, so they are not compatible with barebox shell variables which can only have printable content. Support for these variables is not yet complete in barebox. barebox contains the efivarfs which has the same format as the Linux Kernels efivarfs. It can be mounted with:
mkdir efivarfs
mount -tefivarfs none /efivarfs
In efivarfs each variable is represented by a file named <varname>-<guid>. Access to EFI variables is currently readonly. Since the variables have binary content using md - memory display is often more suitable than cat - concatenate file(s) to stdout.
4.9.9. EFI driver model and barebox¶
The EFI driver model is based around handles and protocols. A handle is an opaque
cookie that represents a hardware device or a software object. Each handle can have
multiple protocols attached to it. A protocol is a callable interface and is defined
by a C struct containing function pointers. A protocol is identified by a 64bit GUID.
Common examples for protocols are DEVICE_PATH, DEVICE_IO, BLOCK_IO, DISK_IO,
FILE_SYSTEM, SIMPLE_INPUT or SIMPLE_TEXT_OUTPUT. Every handle that implements the
DEVICE_PATH protocol is registered as device in barebox. The structure can be best
seen in the devinfo
output of such a device:
barebox:/ devinfo handle-00000000cfaed198
Driver: efi-snp
Bus: efi
Protocols:
0: a19832b9-ac25-11d3-9a2d-0090273fc14d
1: 330d4706-f2a0-4e4f-a369-b66fa8d54385
2: e5dd1403-d622-c24e-8488-c71b17f5e802
3: 34d59603-1428-4429-a414-e6b3b5fd7dc1
4: 0e1ad94a-dcf4-11db-9705-00e08161165f
5: 1aced566-76ed-4218-bc81-767f1f977a89
6: e3161450-ad0f-11d9-9669-0800200c9a66
7: 09576e91-6d3f-11d2-8e39-00a0c969723b
8: 51dd8b21-ad8d-48e9-bc3f-24f46722c748
Parameters:
devpath: pci_root(0)/Pci(0x1c,0x3)/Pci(0x0,0x0)/Mac(e03f4914f157)
The protocols section in the output shows the different protocols this handle implements. One of this Protocols (here the first) is the Simple Network Protocol GUID:
#define EFI_SIMPLE_NETWORK_PROTOCOL_GUID \
EFI_GUID( 0xA19832B9, 0xAC25, 0x11D3, 0x9A, 0x2D, 0x00, 0x90, 0x27, 0x3F, 0xC1, 0x4D )
Matching between EFI devices and drivers is done based on the Protocol GUIDs, so whenever a driver GUID matches one of the GUIDs a device implements the drivers probe function is called.
4.9.10. Building EDK2¶
Additional drivers may be needed from the EDK2 package. For example to use Networking in barebox not only the network device drivers are needed, but also the Simple Network Protocol driver, SnpDxe.efi. This is often not included in the BIOS, but can be compiled from the EDK2 package.
Here is only a quick walkthrough for building edk2, there are more elaborated HOWTOs in the net, for example on http://tianocore.sourceforge.net/wiki/Using_EDK_II_with_Native_GCC.
git clone git://github.com/tianocore/edk2.git
cd edk2
git submodule update --init
make -C BaseTools
. edksetup.sh
At least the following lines in Conf/target.txt
should be edited:
ACTIVE_PLATFORM = NetworkPkg/NetworkPkg.dsc
TARGET_ARCH = X64
TOOL_CHAIN_TAG = GCC48
MAX_CONCURRENT_THREAD_NUMBER = 4
The actual build is started with invoking build
. After building
Build/NetworkPkg/DEBUG_GCC48/X64/SnpDxe.efi
should exist.
NOTE As of this writing (July 2014) the following patch was needed to compile EDK2.
diff --git a/MdeModulePkg/Universal/DebugSupportDxe/X64/AsmFuncs.S b/MdeModulePkg/Universal/DebugSupportDxe/X64/AsmFuncs.S
index 9783ec6..13fc06c 100644
--- a/MdeModulePkg/Universal/DebugSupportDxe/X64/AsmFuncs.S
+++ b/MdeModulePkg/Universal/DebugSupportDxe/X64/AsmFuncs.S
@@ -280,7 +280,7 @@ ExtraPushDone:
mov %ds, %rax
pushq %rax
- movw %es, %rax
+ mov %es, %rax^M
pushq %rax
mov %fs, %rax
pushq %rax
4.9.11. (U)EFI Watchdog¶
(U)EFI provides basic watchdog support. Depending on the system implementation it can be a software or hardware watchdog. Within the (U)EFI specification it is described as follows:
If the watchdog timer expires, the event is logged by the firmware. The system may then either reset with the Runtime Service ResetSystem(), or perform a platform specific action that must eventually cause the platform to be reset. The watchdog timer is armed before the firmware’s boot manager invokes an EFI boot option. The watchdog must be set to a period of 5 minutes. The EFI Image may reset or disable the watchdog timer as needed. If control is returned to the firmware’s boot manager, the watchdog timer must be disabled. The watchdog timer is only used during boot services. On successful completion of ExitBootServices() the watchdog timer is disabled.
See page 186: https://uefi.org/sites/default/files/resources/UEFI_Spec_2_1_D.pdf
Current linux kernel (v5.0) will execute ExitBootServices() during the early boot stage and thus will automatically disable the (U)EFI watchdog. Since it is the proper behavior according to the (U)EFI specification, it is impossible to protect full boot chain by using this watchdog only. It is recommended to use an alternative hardware watchdog, preferably started before the bootloader. If (U)EFI firmware lacks this feature, the bootloader should be able to start an alternative hardware watchdog on its own. Before implementing this kind of workaround please make sure (U)EFI watchdog is not using the same hardware as the alternative watchdog.
Nevertheless, barebox provides access to the (U)EFI SetWatchdogTimer() interface over its internal watchdog framework.