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.