.. _barebox_on_uefi: 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 :ref:`bootloader_spec` 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 (:term:`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. Building barebox for EFI ------------------------ Use the following to build barebox for EFI: .. code-block:: sh export ARCH=x86 make efi_defconfig make The resulting EFI image is ``barebox.efi`` (or the barebox-flash-image link). 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 :term:`ESP`. This can be a barebox binary as well. To use the :ref:`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: .. code-block:: sh 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 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: .. code-block:: sh 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. 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. 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 :ref:`command_reset` 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 :ref:`command_bootm` command. Only the bootm command passes the Kernel commandline to the Kernel. 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: .. code-block:: sh barebox:/ /boot/network-drivers/0001-SnpDxe.efi Should the drivers instanciate new devices these are automatically registered after the driver has been loaded. 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 :ref:`efi_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``: .. code-block:: sh #!/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: .. code-block:: sh /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. EFI File IO Interface --------------------- EFI itself has filesystem support. At least the :term:`ESP` will be mounted by the EFI core already. The :term:`ESP` is mounted to ``/boot`` under barebox, other devices are mounted to ``/efi`` in no particular order. 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.`` under barebox and can be accessed like any other device: .. code-block:: sh 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 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: .. code-block:: sh barebox:/ echo ${handle-00000000d0012198.devpath} pci_root(0)/Pci(0x1d,0x0)/Usb(0x1,0x0)/Usb(0x2,0x0) 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: .. code-block:: sh mkdir efivarfs mount -tefivarfs none /efivarfs In efivarfs each variable is represented by a file named -. Access to EFI variables is currently readonly. Since the variables have binary content using :ref:`command_md` is often more suitable than :ref:`command_cat`. 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: .. code-block:: sh 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: .. code-block:: c #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. .. _efi_building_edk2: 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. .. code-block:: sh git clone git://github.com/tianocore/edk2.git cd edk2 make -C BaseTools . edksetup.sh At least the following lines in ``Conf/target.txt`` should be edited:: ACTIVE_PLATFORM = MdeModulePkg/MdeModulePkg.dsc TARGET_ARCH = X64 TOOL_CHAIN_TAG = GCC48 MAX_CONCURRENT_THREAD_NUMBER = 4 The actual build is started with invoking ``build``. After building ``Build/MdeModule/DEBUG_GCC48/X64/SnpDxe.efi`` should exist. **NOTE** As of this writing (July 2014) the following patch was needed to compile EDK2. .. code-block:: diff 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 (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: .. epigraph:: 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 a 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.