1.. SPDX-License-Identifier: GPL-2.0+ 2.. Copyright (c) 2018 Heinrich Schuchardt 3 4UEFI on U-Boot 5============== 6 7The Unified Extensible Firmware Interface Specification (UEFI) [1] has become 8the default for booting on AArch64 and x86 systems. It provides a stable API for 9the interaction of drivers and applications with the firmware. The API comprises 10access to block storage, network, and console to name a few. The Linux kernel 11and boot loaders like GRUB or the FreeBSD loader can be executed. 12 13Development target 14------------------ 15 16The implementation of UEFI in U-Boot strives to reach the requirements described 17in the "Embedded Base Boot Requirements (EBBR) Specification - Release v1.0" 18[2]. The "Server Base Boot Requirements System Software on ARM Platforms" [3] 19describes a superset of the EBBR specification and may be used as further 20reference. 21 22A full blown UEFI implementation would contradict the U-Boot design principle 23"keep it small". 24 25Building U-Boot for UEFI 26------------------------ 27 28The UEFI standard supports only little-endian systems. The UEFI support can be 29activated for ARM and x86 by specifying:: 30 31 CONFIG_CMD_BOOTEFI=y 32 CONFIG_EFI_LOADER=y 33 34in the .config file. 35 36Support for attaching virtual block devices, e.g. iSCSI drives connected by the 37loaded UEFI application [4], requires:: 38 39 CONFIG_BLK=y 40 CONFIG_PARTITIONS=y 41 42Executing a UEFI binary 43~~~~~~~~~~~~~~~~~~~~~~~ 44 45The bootefi command is used to start UEFI applications or to install UEFI 46drivers. It takes two parameters:: 47 48 bootefi <image address> [fdt address] 49 50* image address - the memory address of the UEFI binary 51* fdt address - the memory address of the flattened device tree 52 53Below you find the output of an example session starting GRUB:: 54 55 => load mmc 0:2 ${fdt_addr_r} boot/dtb 56 29830 bytes read in 14 ms (2 MiB/s) 57 => load mmc 0:1 ${kernel_addr_r} efi/debian/grubaa64.efi 58 reading efi/debian/grubaa64.efi 59 120832 bytes read in 7 ms (16.5 MiB/s) 60 => bootefi ${kernel_addr_r} ${fdt_addr_r} 61 62When booting from a memory location it is unknown from which file it was loaded. 63Therefore the bootefi command uses the device path of the block device partition 64or the network adapter and the file name of the most recently loaded PE-COFF 65file when setting up the loaded image protocol. 66 67Launching a UEFI binary from a FIT image 68~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 69 70A signed FIT image can be used to securely boot a UEFI image via the 71bootm command. This feature is available if U-Boot is configured with:: 72 73 CONFIG_BOOTM_EFI=y 74 75A sample configuration is provided as file doc/uImage.FIT/uefi.its. 76 77Below you find the output of an example session starting GRUB:: 78 79 => load mmc 0:1 ${kernel_addr_r} image.fit 80 4620426 bytes read in 83 ms (53.1 MiB/s) 81 => bootm ${kernel_addr_r}#config-grub-nofdt 82 ## Loading kernel from FIT Image at 40400000 ... 83 Using 'config-grub-nofdt' configuration 84 Verifying Hash Integrity ... sha256,rsa2048:dev+ OK 85 Trying 'efi-grub' kernel subimage 86 Description: GRUB EFI Firmware 87 Created: 2019-11-20 8:18:16 UTC 88 Type: Kernel Image (no loading done) 89 Compression: uncompressed 90 Data Start: 0x404000d0 91 Data Size: 450560 Bytes = 440 KiB 92 Hash algo: sha256 93 Hash value: 4dbee00021112df618f58b3f7cf5e1595533d543094064b9ce991e8b054a9eec 94 Verifying Hash Integrity ... sha256+ OK 95 XIP Kernel Image (no loading done) 96 ## Transferring control to EFI (at address 404000d0) ... 97 Welcome to GRUB! 98 99See doc/uImage.FIT/howto.txt for an introduction to FIT images. 100 101Configuring UEFI secure boot 102~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 103 104The UEFI specification[1] defines a secure way of executing UEFI images 105by verifying a signature (or message digest) of image with certificates. 106This feature on U-Boot is enabled with:: 107 108 CONFIG_UEFI_SECURE_BOOT=y 109 110To make the boot sequence safe, you need to establish a chain of trust; 111In UEFI secure boot the chain trust is defined by the following UEFI variables 112 113* PK - Platform Key 114* KEK - Key Exchange Keys 115* db - white list database 116* dbx - black list database 117 118An in depth description of UEFI secure boot is beyond the scope of this 119document. Please, refer to the UEFI specification and available online 120documentation. Here is a simple example that you can follow for your initial 121attempt (Please note that the actual steps will depend on your system and 122environment.): 123 124Install the required tools on your host 125 126* openssl 127* efitools 128* sbsigntool 129 130Create signing keys and the key database on your host: 131 132The platform key 133 134.. code-block:: bash 135 136 openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_PK/ \ 137 -keyout PK.key -out PK.crt -nodes -days 365 138 cert-to-efi-sig-list -g 11111111-2222-3333-4444-123456789abc \ 139 PK.crt PK.esl; 140 sign-efi-sig-list -c PK.crt -k PK.key PK PK.esl PK.auth 141 142The key exchange keys 143 144.. code-block:: bash 145 146 openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_KEK/ \ 147 -keyout KEK.key -out KEK.crt -nodes -days 365 148 cert-to-efi-sig-list -g 11111111-2222-3333-4444-123456789abc \ 149 KEK.crt KEK.esl 150 sign-efi-sig-list -c PK.crt -k PK.key KEK KEK.esl KEK.auth 151 152The whitelist database 153 154.. code-block:: bash 155 156 openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_db/ \ 157 -keyout db.key -out db.crt -nodes -days 365 158 cert-to-efi-sig-list -g 11111111-2222-3333-4444-123456789abc \ 159 db.crt db.esl 160 sign-efi-sig-list -c KEK.crt -k KEK.key db db.esl db.auth 161 162Copy the \*.auth files to media, say mmc, that is accessible from U-Boot. 163 164Sign an image with one of the keys in "db" on your host 165 166.. code-block:: bash 167 168 sbsign --key db.key --cert db.crt helloworld.efi 169 170Now in U-Boot install the keys on your board:: 171 172 fatload mmc 0:1 <tmpaddr> PK.auth 173 setenv -e -nv -bs -rt -at -i <tmpaddr>:$filesize PK 174 fatload mmc 0:1 <tmpaddr> KEK.auth 175 setenv -e -nv -bs -rt -at -i <tmpaddr>:$filesize KEK 176 fatload mmc 0:1 <tmpaddr> db.auth 177 setenv -e -nv -bs -rt -at -i <tmpaddr>:$filesize db 178 179Set up boot parameters on your board:: 180 181 efidebug boot add -b 1 HELLO mmc 0:1 /helloworld.efi.signed "" 182 183Since kernel 5.7 there's an alternative way of loading an initrd using 184LoadFile2 protocol if CONFIG_EFI_LOAD_FILE2_INITRD is enabled. 185The initrd path can be specified with:: 186 187 efidebug boot add -b ABE0 'kernel' mmc 0:1 Image -i mmc 0:1 initrd 188 189Now your board can run the signed image via the boot manager (see below). 190You can also try this sequence by running Pytest, test_efi_secboot, 191on the sandbox 192 193.. code-block:: bash 194 195 cd <U-Boot source directory> 196 pytest.py test/py/tests/test_efi_secboot/test_signed.py --bd sandbox 197 198UEFI binaries may be signed by Microsoft using the following certificates: 199 200* KEK: Microsoft Corporation KEK CA 2011 201 http://go.microsoft.com/fwlink/?LinkId=321185. 202* db: Microsoft Windows Production PCA 2011 203 http://go.microsoft.com/fwlink/p/?linkid=321192. 204* db: Microsoft Corporation UEFI CA 2011 205 http://go.microsoft.com/fwlink/p/?linkid=321194. 206 207Using OP-TEE for EFI variables 208~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 209 210Instead of implementing UEFI variable services inside U-Boot they can 211also be provided in the secure world by a module for OP-TEE[1]. The 212interface between U-Boot and OP-TEE for variable services is enabled by 213CONFIG_EFI_MM_COMM_TEE=y. 214 215Tianocore EDK II's standalone management mode driver for variables can 216be linked to OP-TEE for this purpose. This module uses the Replay 217Protected Memory Block (RPMB) of an eMMC device for persisting 218non-volatile variables. When calling the variable services via the 219OP-TEE API U-Boot's OP-TEE supplicant relays calls to the RPMB driver 220which has to be enabled via CONFIG_SUPPORT_EMMC_RPMB=y. 221 222EDK2 Build instructions 223*********************** 224 225.. code-block:: bash 226 227 $ git clone https://github.com/tianocore/edk2.git 228 $ git clone https://github.com/tianocore/edk2-platforms.git 229 $ cd edk2 230 $ git submodule init && git submodule update --init --recursive 231 $ cd .. 232 $ export WORKSPACE=$(pwd) 233 $ export PACKAGES_PATH=$WORKSPACE/edk2:$WORKSPACE/edk2-platforms 234 $ export ACTIVE_PLATFORM="Platform/StandaloneMm/PlatformStandaloneMmPkg/PlatformStandaloneMmRpmb.dsc" 235 $ export GCC5_AARCH64_PREFIX=aarch64-linux-gnu- 236 $ source edk2/edksetup.sh 237 $ make -C edk2/BaseTools 238 $ build -p $ACTIVE_PLATFORM -b RELEASE -a AARCH64 -t GCC5 -n `nproc` 239 240OP-TEE Build instructions 241************************* 242 243.. code-block:: bash 244 245 $ git clone https://github.com/OP-TEE/optee_os.git 246 $ cd optee_os 247 $ ln -s ../Build/MmStandaloneRpmb/RELEASE_GCC5/FV/BL32_AP_MM.fd 248 $ export ARCH=arm 249 $ CROSS_COMPILE32=arm-linux-gnueabihf- make -j32 CFG_ARM64_core=y \ 250 PLATFORM=<myboard> CFG_STMM_PATH=BL32_AP_MM.fd CFG_RPMB_FS=y \ 251 CFG_RPMB_FS_DEV_ID=0 CFG_CORE_HEAP_SIZE=524288 CFG_RPMB_WRITE_KEY=1 \ 252 CFG_CORE_HEAP_SIZE=524288 CFG_CORE_DYN_SHM=y CFG_RPMB_TESTKEY=y \ 253 CFG_REE_FS=n CFG_CORE_ARM64_PA_BITS=48 CFG_TEE_CORE_LOG_LEVEL=1 \ 254 CFG_TEE_TA_LOG_LEVEL=1 CFG_SCTLR_ALIGNMENT_CHECK=n 255 256U-Boot Build instructions 257************************* 258 259Although the StandAloneMM binary comes from EDK2, using and storing the 260variables is currently available in U-Boot only. 261 262.. code-block:: bash 263 264 $ git clone https://github.com/u-boot/u-boot.git 265 $ cd u-boot 266 $ export CROSS_COMPILE=aarch64-linux-gnu- 267 $ export ARCH=<arch> 268 $ make <myboard>_defconfig 269 $ make menuconfig 270 271Enable ``CONFIG_OPTEE``, ``CONFIG_CMD_OPTEE_RPMB`` and ``CONFIG_EFI_MM_COMM_TEE`` 272 273.. warning:: 274 275 - Your OP-TEE platform port must support Dynamic shared memory, since that's 276 the only kind of memory U-Boot supports for now. 277 278[1] https://optee.readthedocs.io/en/latest/building/efi_vars/stmm.html 279 280Executing the boot manager 281~~~~~~~~~~~~~~~~~~~~~~~~~~ 282 283The UEFI specification foresees to define boot entries and boot sequence via 284UEFI variables. Booting according to these variables is possible via:: 285 286 bootefi bootmgr [fdt address] 287 288As of U-Boot v2020.10 UEFI variables cannot be set at runtime. The U-Boot 289command 'efidebug' can be used to set the variables. 290 291Executing the built in hello world application 292~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 293 294A hello world UEFI application can be built with:: 295 296 CONFIG_CMD_BOOTEFI_HELLO_COMPILE=y 297 298It can be embedded into the U-Boot binary with:: 299 300 CONFIG_CMD_BOOTEFI_HELLO=y 301 302The bootefi command is used to start the embedded hello world application:: 303 304 bootefi hello [fdt address] 305 306Below you find the output of an example session:: 307 308 => bootefi hello ${fdtcontroladdr} 309 ## Starting EFI application at 01000000 ... 310 WARNING: using memory device/image path, this may confuse some payloads! 311 Hello, world! 312 Running on UEFI 2.7 313 Have SMBIOS table 314 Have device tree 315 Load options: root=/dev/sdb3 init=/sbin/init rootwait ro 316 ## Application terminated, r = 0 317 318The environment variable fdtcontroladdr points to U-Boot's internal device tree 319(if available). 320 321Executing the built-in self-test 322~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 323 324An UEFI self-test suite can be embedded in U-Boot by building with:: 325 326 CONFIG_CMD_BOOTEFI_SELFTEST=y 327 328For testing the UEFI implementation the bootefi command can be used to start the 329self-test:: 330 331 bootefi selftest [fdt address] 332 333The environment variable 'efi_selftest' can be used to select a single test. If 334it is not provided all tests are executed except those marked as 'on request'. 335If the environment variable is set to 'list' a list of all tests is shown. 336 337Below you can find the output of an example session:: 338 339 => setenv efi_selftest simple network protocol 340 => bootefi selftest 341 Testing EFI API implementation 342 Selected test: 'simple network protocol' 343 Setting up 'simple network protocol' 344 Setting up 'simple network protocol' succeeded 345 Executing 'simple network protocol' 346 DHCP Discover 347 DHCP reply received from 192.168.76.2 (52:55:c0:a8:4c:02) 348 as broadcast message. 349 Executing 'simple network protocol' succeeded 350 Tearing down 'simple network protocol' 351 Tearing down 'simple network protocol' succeeded 352 Boot services terminated 353 Summary: 0 failures 354 Preparing for reset. Press any key. 355 356The UEFI life cycle 357------------------- 358 359After the U-Boot platform has been initialized the UEFI API provides two kinds 360of services: 361 362* boot services 363* runtime services 364 365The API can be extended by loading UEFI drivers which come in two variants: 366 367* boot drivers 368* runtime drivers 369 370UEFI drivers are installed with U-Boot's bootefi command. With the same command 371UEFI applications can be executed. 372 373Loaded images of UEFI drivers stay in memory after returning to U-Boot while 374loaded images of applications are removed from memory. 375 376An UEFI application (e.g. an operating system) that wants to take full control 377of the system calls ExitBootServices. After a UEFI application calls 378ExitBootServices 379 380* boot services are not available anymore 381* timer events are stopped 382* the memory used by U-Boot except for runtime services is released 383* the memory used by boot time drivers is released 384 385So this is a point of no return. Afterwards the UEFI application can only return 386to U-Boot by rebooting. 387 388The UEFI object model 389--------------------- 390 391UEFI offers a flexible and expandable object model. The objects in the UEFI API 392are devices, drivers, and loaded images. These objects are referenced by 393handles. 394 395The interfaces implemented by the objects are referred to as protocols. These 396are identified by GUIDs. They can be installed and uninstalled by calling the 397appropriate boot services. 398 399Handles are created by the InstallProtocolInterface or the 400InstallMultipleProtocolinterfaces service if NULL is passed as handle. 401 402Handles are deleted when the last protocol has been removed with the 403UninstallProtocolInterface or the UninstallMultipleProtocolInterfaces service. 404 405Devices offer the EFI_DEVICE_PATH_PROTOCOL. A device path is the concatenation 406of device nodes. By their device paths all devices of a system are arranged in a 407tree. 408 409Drivers offer the EFI_DRIVER_BINDING_PROTOCOL. This protocol is used to connect 410a driver to devices (which are referenced as controllers in this context). 411 412Loaded images offer the EFI_LOADED_IMAGE_PROTOCOL. This protocol provides meta 413information about the image and a pointer to the unload callback function. 414 415The UEFI events 416--------------- 417 418In the UEFI terminology an event is a data object referencing a notification 419function which is queued for calling when the event is signaled. The following 420types of events exist: 421 422* periodic and single shot timer events 423* exit boot services events, triggered by calling the ExitBootServices() service 424* virtual address change events 425* memory map change events 426* read to boot events 427* reset system events 428* system table events 429* events that are only triggered programmatically 430 431Events can be created with the CreateEvent service and deleted with CloseEvent 432service. 433 434Events can be assigned to an event group. If any of the events in a group is 435signaled, all other events in the group are also set to the signaled state. 436 437The UEFI driver model 438--------------------- 439 440A driver is specific for a single protocol installed on a device. To install a 441driver on a device the ConnectController service is called. In this context 442controller refers to the device for which the driver is installed. 443 444The relevant drivers are identified using the EFI_DRIVER_BINDING_PROTOCOL. This 445protocol has has three functions: 446 447* supported - determines if the driver is compatible with the device 448* start - installs the driver by opening the relevant protocol with 449 attribute EFI_OPEN_PROTOCOL_BY_DRIVER 450* stop - uninstalls the driver 451 452The driver may create child controllers (child devices). E.g. a driver for block 453IO devices will create the device handles for the partitions. The child 454controllers will open the supported protocol with the attribute 455EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER. 456 457A driver can be detached from a device using the DisconnectController service. 458 459U-Boot devices mapped as UEFI devices 460------------------------------------- 461 462Some of the U-Boot devices are mapped as UEFI devices 463 464* block IO devices 465* console 466* graphical output 467* network adapter 468 469As of U-Boot 2018.03 the logic for doing this is hard coded. 470 471The development target is to integrate the setup of these UEFI devices with the 472U-Boot driver model [5]. So when a U-Boot device is discovered a handle should 473be created and the device path protocol and the relevant IO protocol should be 474installed. The UEFI driver then would be attached by calling ConnectController. 475When a U-Boot device is removed DisconnectController should be called. 476 477UEFI devices mapped as U-Boot devices 478------------------------------------- 479 480UEFI drivers binaries and applications may create new (virtual) devices, install 481a protocol and call the ConnectController service. Now the matching UEFI driver 482is determined by iterating over the implementations of the 483EFI_DRIVER_BINDING_PROTOCOL. 484 485It is the task of the UEFI driver to create a corresponding U-Boot device and to 486proxy calls for this U-Boot device to the controller. 487 488In U-Boot 2018.03 this has only been implemented for block IO devices. 489 490UEFI uclass 491~~~~~~~~~~~ 492 493An UEFI uclass driver (lib/efi_driver/efi_uclass.c) has been created that 494takes care of initializing the UEFI drivers and providing the 495EFI_DRIVER_BINDING_PROTOCOL implementation for the UEFI drivers. 496 497A linker created list is used to keep track of the UEFI drivers. To create an 498entry in the list the UEFI driver uses the U_BOOT_DRIVER macro specifying 499UCLASS_EFI as the ID of its uclass, e.g:: 500 501 /* Identify as UEFI driver */ 502 U_BOOT_DRIVER(efi_block) = { 503 .name = "EFI block driver", 504 .id = UCLASS_EFI, 505 .ops = &driver_ops, 506 }; 507 508The available operations are defined via the structure struct efi_driver_ops:: 509 510 struct efi_driver_ops { 511 const efi_guid_t *protocol; 512 const efi_guid_t *child_protocol; 513 int (*bind)(efi_handle_t handle, void *interface); 514 }; 515 516When the supported() function of the EFI_DRIVER_BINDING_PROTOCOL is called the 517uclass checks if the protocol GUID matches the protocol GUID of the UEFI driver. 518In the start() function the bind() function of the UEFI driver is called after 519checking the GUID. 520The stop() function of the EFI_DRIVER_BINDING_PROTOCOL disconnects the child 521controllers created by the UEFI driver and the UEFI driver. (In U-Boot v2013.03 522this is not yet completely implemented.) 523 524UEFI block IO driver 525~~~~~~~~~~~~~~~~~~~~ 526 527The UEFI block IO driver supports devices exposing the EFI_BLOCK_IO_PROTOCOL. 528 529When connected it creates a new U-Boot block IO device with interface type 530IF_TYPE_EFI, adds child controllers mapping the partitions, and installs the 531EFI_SIMPLE_FILE_SYSTEM_PROTOCOL on these. This can be used together with the 532software iPXE to boot from iSCSI network drives [4]. 533 534This driver is only available if U-Boot is configured with:: 535 536 CONFIG_BLK=y 537 CONFIG_PARTITIONS=y 538 539Miscellaneous 540------------- 541 542Load file 2 protocol 543~~~~~~~~~~~~~~~~~~~~ 544 545The load file 2 protocol can be used by the Linux kernel to load the initial 546RAM disk. U-Boot can be configured to provide an implementation with:: 547 548 EFI_LOAD_FILE2_INITRD=y 549 550When the option is enabled the user can add the initrd path with the efidebug 551command. 552 553Load options Boot#### have a FilePathList[] member. The first element of 554the array (FilePathList[0]) is the EFI binary to execute. When an initrd 555is specified the Device Path for the initrd is denoted by a VenMedia node 556with the EFI_INITRD_MEDIA_GUID. Each entry of the array is terminated by the 557'end of entire device path' subtype (0xff). If a user wants to define multiple 558initrds, those must by separated by the 'end of this instance' identifier of 559the end node (0x01). 560 561So our final format of the FilePathList[] is:: 562 563 Loaded image - end node (0xff) - VenMedia - initrd_1 - [end node (0x01) - initrd_n ...] - end node (0xff) 564 565Links 566----- 567 568* [1] http://uefi.org/specifications - UEFI specifications 569* [2] https://github.com/ARM-software/ebbr/releases/download/v1.0/ebbr-v1.0.pdf - 570 Embedded Base Boot Requirements (EBBR) Specification - Release v1.0 571* [3] https://developer.arm.com/docs/den0044/latest/server-base-boot-requirements-system-software-on-arm-platforms-version-11 - 572 Server Base Boot Requirements System Software on ARM Platforms - Version 1.1 573* [4] :doc:`iscsi` 574* [5] :doc:`../driver-model/index` 575