diff options
Diffstat (limited to 'roms/u-boot/doc/develop/uefi/uefi.rst')
-rw-r--r-- | roms/u-boot/doc/develop/uefi/uefi.rst | 574 |
1 files changed, 574 insertions, 0 deletions
diff --git a/roms/u-boot/doc/develop/uefi/uefi.rst b/roms/u-boot/doc/develop/uefi/uefi.rst new file mode 100644 index 000000000..4f2b8b036 --- /dev/null +++ b/roms/u-boot/doc/develop/uefi/uefi.rst @@ -0,0 +1,574 @@ +.. SPDX-License-Identifier: GPL-2.0+ +.. Copyright (c) 2018 Heinrich Schuchardt + +UEFI on U-Boot +============== + +The Unified Extensible Firmware Interface Specification (UEFI) [1] has become +the default for booting on AArch64 and x86 systems. It provides a stable API for +the interaction of drivers and applications with the firmware. The API comprises +access to block storage, network, and console to name a few. The Linux kernel +and boot loaders like GRUB or the FreeBSD loader can be executed. + +Development target +------------------ + +The implementation of UEFI in U-Boot strives to reach the requirements described +in the "Embedded Base Boot Requirements (EBBR) Specification - Release v1.0" +[2]. The "Server Base Boot Requirements System Software on ARM Platforms" [3] +describes a superset of the EBBR specification and may be used as further +reference. + +A full blown UEFI implementation would contradict the U-Boot design principle +"keep it small". + +Building U-Boot for UEFI +------------------------ + +The UEFI standard supports only little-endian systems. The UEFI support can be +activated for ARM and x86 by specifying:: + + CONFIG_CMD_BOOTEFI=y + CONFIG_EFI_LOADER=y + +in the .config file. + +Support for attaching virtual block devices, e.g. iSCSI drives connected by the +loaded UEFI application [4], requires:: + + CONFIG_BLK=y + CONFIG_PARTITIONS=y + +Executing a UEFI binary +~~~~~~~~~~~~~~~~~~~~~~~ + +The bootefi command is used to start UEFI applications or to install UEFI +drivers. It takes two parameters:: + + bootefi <image address> [fdt address] + +* image address - the memory address of the UEFI binary +* fdt address - the memory address of the flattened device tree + +Below you find the output of an example session starting GRUB:: + + => load mmc 0:2 ${fdt_addr_r} boot/dtb + 29830 bytes read in 14 ms (2 MiB/s) + => load mmc 0:1 ${kernel_addr_r} efi/debian/grubaa64.efi + reading efi/debian/grubaa64.efi + 120832 bytes read in 7 ms (16.5 MiB/s) + => bootefi ${kernel_addr_r} ${fdt_addr_r} + +When booting from a memory location it is unknown from which file it was loaded. +Therefore the bootefi command uses the device path of the block device partition +or the network adapter and the file name of the most recently loaded PE-COFF +file when setting up the loaded image protocol. + +Launching a UEFI binary from a FIT image +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +A signed FIT image can be used to securely boot a UEFI image via the +bootm command. This feature is available if U-Boot is configured with:: + + CONFIG_BOOTM_EFI=y + +A sample configuration is provided as file doc/uImage.FIT/uefi.its. + +Below you find the output of an example session starting GRUB:: + + => load mmc 0:1 ${kernel_addr_r} image.fit + 4620426 bytes read in 83 ms (53.1 MiB/s) + => bootm ${kernel_addr_r}#config-grub-nofdt + ## Loading kernel from FIT Image at 40400000 ... + Using 'config-grub-nofdt' configuration + Verifying Hash Integrity ... sha256,rsa2048:dev+ OK + Trying 'efi-grub' kernel subimage + Description: GRUB EFI Firmware + Created: 2019-11-20 8:18:16 UTC + Type: Kernel Image (no loading done) + Compression: uncompressed + Data Start: 0x404000d0 + Data Size: 450560 Bytes = 440 KiB + Hash algo: sha256 + Hash value: 4dbee00021112df618f58b3f7cf5e1595533d543094064b9ce991e8b054a9eec + Verifying Hash Integrity ... sha256+ OK + XIP Kernel Image (no loading done) + ## Transferring control to EFI (at address 404000d0) ... + Welcome to GRUB! + +See doc/uImage.FIT/howto.txt for an introduction to FIT images. + +Configuring UEFI secure boot +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The UEFI specification[1] defines a secure way of executing UEFI images +by verifying a signature (or message digest) of image with certificates. +This feature on U-Boot is enabled with:: + + CONFIG_UEFI_SECURE_BOOT=y + +To make the boot sequence safe, you need to establish a chain of trust; +In UEFI secure boot the chain trust is defined by the following UEFI variables + +* PK - Platform Key +* KEK - Key Exchange Keys +* db - white list database +* dbx - black list database + +An in depth description of UEFI secure boot is beyond the scope of this +document. Please, refer to the UEFI specification and available online +documentation. Here is a simple example that you can follow for your initial +attempt (Please note that the actual steps will depend on your system and +environment.): + +Install the required tools on your host + +* openssl +* efitools +* sbsigntool + +Create signing keys and the key database on your host: + +The platform key + +.. code-block:: bash + + openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_PK/ \ + -keyout PK.key -out PK.crt -nodes -days 365 + cert-to-efi-sig-list -g 11111111-2222-3333-4444-123456789abc \ + PK.crt PK.esl; + sign-efi-sig-list -c PK.crt -k PK.key PK PK.esl PK.auth + +The key exchange keys + +.. code-block:: bash + + openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_KEK/ \ + -keyout KEK.key -out KEK.crt -nodes -days 365 + cert-to-efi-sig-list -g 11111111-2222-3333-4444-123456789abc \ + KEK.crt KEK.esl + sign-efi-sig-list -c PK.crt -k PK.key KEK KEK.esl KEK.auth + +The whitelist database + +.. code-block:: bash + + openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_db/ \ + -keyout db.key -out db.crt -nodes -days 365 + cert-to-efi-sig-list -g 11111111-2222-3333-4444-123456789abc \ + db.crt db.esl + sign-efi-sig-list -c KEK.crt -k KEK.key db db.esl db.auth + +Copy the \*.auth files to media, say mmc, that is accessible from U-Boot. + +Sign an image with one of the keys in "db" on your host + +.. code-block:: bash + + sbsign --key db.key --cert db.crt helloworld.efi + +Now in U-Boot install the keys on your board:: + + fatload mmc 0:1 <tmpaddr> PK.auth + setenv -e -nv -bs -rt -at -i <tmpaddr>:$filesize PK + fatload mmc 0:1 <tmpaddr> KEK.auth + setenv -e -nv -bs -rt -at -i <tmpaddr>:$filesize KEK + fatload mmc 0:1 <tmpaddr> db.auth + setenv -e -nv -bs -rt -at -i <tmpaddr>:$filesize db + +Set up boot parameters on your board:: + + efidebug boot add -b 1 HELLO mmc 0:1 /helloworld.efi.signed "" + +Since kernel 5.7 there's an alternative way of loading an initrd using +LoadFile2 protocol if CONFIG_EFI_LOAD_FILE2_INITRD is enabled. +The initrd path can be specified with:: + + efidebug boot add -b ABE0 'kernel' mmc 0:1 Image -i mmc 0:1 initrd + +Now your board can run the signed image via the boot manager (see below). +You can also try this sequence by running Pytest, test_efi_secboot, +on the sandbox + +.. code-block:: bash + + cd <U-Boot source directory> + pytest.py test/py/tests/test_efi_secboot/test_signed.py --bd sandbox + +UEFI binaries may be signed by Microsoft using the following certificates: + +* KEK: Microsoft Corporation KEK CA 2011 + http://go.microsoft.com/fwlink/?LinkId=321185. +* db: Microsoft Windows Production PCA 2011 + http://go.microsoft.com/fwlink/p/?linkid=321192. +* db: Microsoft Corporation UEFI CA 2011 + http://go.microsoft.com/fwlink/p/?linkid=321194. + +Using OP-TEE for EFI variables +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Instead of implementing UEFI variable services inside U-Boot they can +also be provided in the secure world by a module for OP-TEE[1]. The +interface between U-Boot and OP-TEE for variable services is enabled by +CONFIG_EFI_MM_COMM_TEE=y. + +Tianocore EDK II's standalone management mode driver for variables can +be linked to OP-TEE for this purpose. This module uses the Replay +Protected Memory Block (RPMB) of an eMMC device for persisting +non-volatile variables. When calling the variable services via the +OP-TEE API U-Boot's OP-TEE supplicant relays calls to the RPMB driver +which has to be enabled via CONFIG_SUPPORT_EMMC_RPMB=y. + +EDK2 Build instructions +*********************** + +.. code-block:: bash + + $ git clone https://github.com/tianocore/edk2.git + $ git clone https://github.com/tianocore/edk2-platforms.git + $ cd edk2 + $ git submodule init && git submodule update --init --recursive + $ cd .. + $ export WORKSPACE=$(pwd) + $ export PACKAGES_PATH=$WORKSPACE/edk2:$WORKSPACE/edk2-platforms + $ export ACTIVE_PLATFORM="Platform/StandaloneMm/PlatformStandaloneMmPkg/PlatformStandaloneMmRpmb.dsc" + $ export GCC5_AARCH64_PREFIX=aarch64-linux-gnu- + $ source edk2/edksetup.sh + $ make -C edk2/BaseTools + $ build -p $ACTIVE_PLATFORM -b RELEASE -a AARCH64 -t GCC5 -n `nproc` + +OP-TEE Build instructions +************************* + +.. code-block:: bash + + $ git clone https://github.com/OP-TEE/optee_os.git + $ cd optee_os + $ ln -s ../Build/MmStandaloneRpmb/RELEASE_GCC5/FV/BL32_AP_MM.fd + $ export ARCH=arm + $ CROSS_COMPILE32=arm-linux-gnueabihf- make -j32 CFG_ARM64_core=y \ + PLATFORM=<myboard> CFG_STMM_PATH=BL32_AP_MM.fd CFG_RPMB_FS=y \ + CFG_RPMB_FS_DEV_ID=0 CFG_CORE_HEAP_SIZE=524288 CFG_RPMB_WRITE_KEY=1 \ + CFG_CORE_HEAP_SIZE=524288 CFG_CORE_DYN_SHM=y CFG_RPMB_TESTKEY=y \ + CFG_REE_FS=n CFG_CORE_ARM64_PA_BITS=48 CFG_TEE_CORE_LOG_LEVEL=1 \ + CFG_TEE_TA_LOG_LEVEL=1 CFG_SCTLR_ALIGNMENT_CHECK=n + +U-Boot Build instructions +************************* + +Although the StandAloneMM binary comes from EDK2, using and storing the +variables is currently available in U-Boot only. + +.. code-block:: bash + + $ git clone https://github.com/u-boot/u-boot.git + $ cd u-boot + $ export CROSS_COMPILE=aarch64-linux-gnu- + $ export ARCH=<arch> + $ make <myboard>_defconfig + $ make menuconfig + +Enable ``CONFIG_OPTEE``, ``CONFIG_CMD_OPTEE_RPMB`` and ``CONFIG_EFI_MM_COMM_TEE`` + +.. warning:: + + - Your OP-TEE platform port must support Dynamic shared memory, since that's + the only kind of memory U-Boot supports for now. + +[1] https://optee.readthedocs.io/en/latest/building/efi_vars/stmm.html + +Executing the boot manager +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The UEFI specification foresees to define boot entries and boot sequence via +UEFI variables. Booting according to these variables is possible via:: + + bootefi bootmgr [fdt address] + +As of U-Boot v2020.10 UEFI variables cannot be set at runtime. The U-Boot +command 'efidebug' can be used to set the variables. + +Executing the built in hello world application +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +A hello world UEFI application can be built with:: + + CONFIG_CMD_BOOTEFI_HELLO_COMPILE=y + +It can be embedded into the U-Boot binary with:: + + CONFIG_CMD_BOOTEFI_HELLO=y + +The bootefi command is used to start the embedded hello world application:: + + bootefi hello [fdt address] + +Below you find the output of an example session:: + + => bootefi hello ${fdtcontroladdr} + ## Starting EFI application at 01000000 ... + WARNING: using memory device/image path, this may confuse some payloads! + Hello, world! + Running on UEFI 2.7 + Have SMBIOS table + Have device tree + Load options: root=/dev/sdb3 init=/sbin/init rootwait ro + ## Application terminated, r = 0 + +The environment variable fdtcontroladdr points to U-Boot's internal device tree +(if available). + +Executing the built-in self-test +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +An UEFI self-test suite can be embedded in U-Boot by building with:: + + CONFIG_CMD_BOOTEFI_SELFTEST=y + +For testing the UEFI implementation the bootefi command can be used to start the +self-test:: + + bootefi selftest [fdt address] + +The environment variable 'efi_selftest' can be used to select a single test. If +it is not provided all tests are executed except those marked as 'on request'. +If the environment variable is set to 'list' a list of all tests is shown. + +Below you can find the output of an example session:: + + => setenv efi_selftest simple network protocol + => bootefi selftest + Testing EFI API implementation + Selected test: 'simple network protocol' + Setting up 'simple network protocol' + Setting up 'simple network protocol' succeeded + Executing 'simple network protocol' + DHCP Discover + DHCP reply received from 192.168.76.2 (52:55:c0:a8:4c:02) + as broadcast message. + Executing 'simple network protocol' succeeded + Tearing down 'simple network protocol' + Tearing down 'simple network protocol' succeeded + Boot services terminated + Summary: 0 failures + Preparing for reset. Press any key. + +The UEFI life cycle +------------------- + +After the U-Boot platform has been initialized the UEFI API provides two kinds +of services: + +* boot services +* runtime services + +The API can be extended by loading UEFI drivers which come in two variants: + +* boot drivers +* runtime drivers + +UEFI drivers are installed with U-Boot's bootefi command. With the same command +UEFI applications can be executed. + +Loaded images of UEFI drivers stay in memory after returning to U-Boot while +loaded images of applications are removed from memory. + +An UEFI application (e.g. an operating system) that wants to take full control +of the system calls ExitBootServices. After a UEFI application calls +ExitBootServices + +* boot services are not available anymore +* timer events are stopped +* the memory used by U-Boot except for runtime services is released +* the memory used by boot time drivers is released + +So this is a point of no return. Afterwards the UEFI application can only return +to U-Boot by rebooting. + +The UEFI object model +--------------------- + +UEFI offers a flexible and expandable object model. The objects in the UEFI API +are devices, drivers, and loaded images. These objects are referenced by +handles. + +The interfaces implemented by the objects are referred to as protocols. These +are identified by GUIDs. They can be installed and uninstalled by calling the +appropriate boot services. + +Handles are created by the InstallProtocolInterface or the +InstallMultipleProtocolinterfaces service if NULL is passed as handle. + +Handles are deleted when the last protocol has been removed with the +UninstallProtocolInterface or the UninstallMultipleProtocolInterfaces service. + +Devices offer the EFI_DEVICE_PATH_PROTOCOL. A device path is the concatenation +of device nodes. By their device paths all devices of a system are arranged in a +tree. + +Drivers offer the EFI_DRIVER_BINDING_PROTOCOL. This protocol is used to connect +a driver to devices (which are referenced as controllers in this context). + +Loaded images offer the EFI_LOADED_IMAGE_PROTOCOL. This protocol provides meta +information about the image and a pointer to the unload callback function. + +The UEFI events +--------------- + +In the UEFI terminology an event is a data object referencing a notification +function which is queued for calling when the event is signaled. The following +types of events exist: + +* periodic and single shot timer events +* exit boot services events, triggered by calling the ExitBootServices() service +* virtual address change events +* memory map change events +* read to boot events +* reset system events +* system table events +* events that are only triggered programmatically + +Events can be created with the CreateEvent service and deleted with CloseEvent +service. + +Events can be assigned to an event group. If any of the events in a group is +signaled, all other events in the group are also set to the signaled state. + +The UEFI driver model +--------------------- + +A driver is specific for a single protocol installed on a device. To install a +driver on a device the ConnectController service is called. In this context +controller refers to the device for which the driver is installed. + +The relevant drivers are identified using the EFI_DRIVER_BINDING_PROTOCOL. This +protocol has has three functions: + +* supported - determines if the driver is compatible with the device +* start - installs the driver by opening the relevant protocol with + attribute EFI_OPEN_PROTOCOL_BY_DRIVER +* stop - uninstalls the driver + +The driver may create child controllers (child devices). E.g. a driver for block +IO devices will create the device handles for the partitions. The child +controllers will open the supported protocol with the attribute +EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER. + +A driver can be detached from a device using the DisconnectController service. + +U-Boot devices mapped as UEFI devices +------------------------------------- + +Some of the U-Boot devices are mapped as UEFI devices + +* block IO devices +* console +* graphical output +* network adapter + +As of U-Boot 2018.03 the logic for doing this is hard coded. + +The development target is to integrate the setup of these UEFI devices with the +U-Boot driver model [5]. So when a U-Boot device is discovered a handle should +be created and the device path protocol and the relevant IO protocol should be +installed. The UEFI driver then would be attached by calling ConnectController. +When a U-Boot device is removed DisconnectController should be called. + +UEFI devices mapped as U-Boot devices +------------------------------------- + +UEFI drivers binaries and applications may create new (virtual) devices, install +a protocol and call the ConnectController service. Now the matching UEFI driver +is determined by iterating over the implementations of the +EFI_DRIVER_BINDING_PROTOCOL. + +It is the task of the UEFI driver to create a corresponding U-Boot device and to +proxy calls for this U-Boot device to the controller. + +In U-Boot 2018.03 this has only been implemented for block IO devices. + +UEFI uclass +~~~~~~~~~~~ + +An UEFI uclass driver (lib/efi_driver/efi_uclass.c) has been created that +takes care of initializing the UEFI drivers and providing the +EFI_DRIVER_BINDING_PROTOCOL implementation for the UEFI drivers. + +A linker created list is used to keep track of the UEFI drivers. To create an +entry in the list the UEFI driver uses the U_BOOT_DRIVER macro specifying +UCLASS_EFI as the ID of its uclass, e.g:: + + /* Identify as UEFI driver */ + U_BOOT_DRIVER(efi_block) = { + .name = "EFI block driver", + .id = UCLASS_EFI, + .ops = &driver_ops, + }; + +The available operations are defined via the structure struct efi_driver_ops:: + + struct efi_driver_ops { + const efi_guid_t *protocol; + const efi_guid_t *child_protocol; + int (*bind)(efi_handle_t handle, void *interface); + }; + +When the supported() function of the EFI_DRIVER_BINDING_PROTOCOL is called the +uclass checks if the protocol GUID matches the protocol GUID of the UEFI driver. +In the start() function the bind() function of the UEFI driver is called after +checking the GUID. +The stop() function of the EFI_DRIVER_BINDING_PROTOCOL disconnects the child +controllers created by the UEFI driver and the UEFI driver. (In U-Boot v2013.03 +this is not yet completely implemented.) + +UEFI block IO driver +~~~~~~~~~~~~~~~~~~~~ + +The UEFI block IO driver supports devices exposing the EFI_BLOCK_IO_PROTOCOL. + +When connected it creates a new U-Boot block IO device with interface type +IF_TYPE_EFI, adds child controllers mapping the partitions, and installs the +EFI_SIMPLE_FILE_SYSTEM_PROTOCOL on these. This can be used together with the +software iPXE to boot from iSCSI network drives [4]. + +This driver is only available if U-Boot is configured with:: + + CONFIG_BLK=y + CONFIG_PARTITIONS=y + +Miscellaneous +------------- + +Load file 2 protocol +~~~~~~~~~~~~~~~~~~~~ + +The load file 2 protocol can be used by the Linux kernel to load the initial +RAM disk. U-Boot can be configured to provide an implementation with:: + + EFI_LOAD_FILE2_INITRD=y + +When the option is enabled the user can add the initrd path with the efidebug +command. + +Load options Boot#### have a FilePathList[] member. The first element of +the array (FilePathList[0]) is the EFI binary to execute. When an initrd +is specified the Device Path for the initrd is denoted by a VenMedia node +with the EFI_INITRD_MEDIA_GUID. Each entry of the array is terminated by the +'end of entire device path' subtype (0xff). If a user wants to define multiple +initrds, those must by separated by the 'end of this instance' identifier of +the end node (0x01). + +So our final format of the FilePathList[] is:: + + Loaded image - end node (0xff) - VenMedia - initrd_1 - [end node (0x01) - initrd_n ...] - end node (0xff) + +Links +----- + +* [1] http://uefi.org/specifications - UEFI specifications +* [2] https://github.com/ARM-software/ebbr/releases/download/v1.0/ebbr-v1.0.pdf - + Embedded Base Boot Requirements (EBBR) Specification - Release v1.0 +* [3] https://developer.arm.com/docs/den0044/latest/server-base-boot-requirements-system-software-on-arm-platforms-version-11 - + Server Base Boot Requirements System Software on ARM Platforms - Version 1.1 +* [4] :doc:`iscsi` +* [5] :doc:`../driver-model/index` |