mirror of
				https://xff.cz/git/u-boot/
				synced 2025-10-31 18:35:42 +01:00 
			
		
		
		
	This reverts commita7e4f905d2. The description originally written by Sughosh is still valid even after the commit47a25e81d3("Revert "efi_capsule: Move signature from DTB to .rodata"") was applied. Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
		
			
				
	
	
		
			699 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			ReStructuredText
		
	
	
	
	
	
			
		
		
	
	
			699 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			ReStructuredText
		
	
	
	
	
	
| .. 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
 | |
| 
 | |
| Enabling UEFI Capsule Update feature
 | |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | |
| 
 | |
| Support has been added for the UEFI capsule update feature which
 | |
| enables updating the U-Boot image using the UEFI firmware management
 | |
| protocol (FMP). The capsules are not passed to the firmware through
 | |
| the UpdateCapsule runtime service. Instead, capsule-on-disk
 | |
| functionality is used for fetching the capsule from the EFI System
 | |
| Partition (ESP) by placing the capsule file under the
 | |
| \EFI\UpdateCapsule directory.
 | |
| 
 | |
| The directory \EFI\UpdateCapsule is checked for capsules only within the
 | |
| EFI system partition on the device specified in the active boot option
 | |
| determined by reference to BootNext variable or BootOrder variable processing.
 | |
| The active Boot Variable is the variable with highest priority BootNext or
 | |
| within BootOrder that refers to a device found to be present. Boot variables
 | |
| in BootOrder but referring to devices not present are ignored when determining
 | |
| active boot variable.
 | |
| Before starting a capsule update make sure your capsules are installed in the
 | |
| correct ESP partition or set BootNext.
 | |
| 
 | |
| Performing the update
 | |
| *********************
 | |
| 
 | |
| Since U-boot doesn't currently support SetVariable at runtime there's a Kconfig
 | |
| option (CONFIG_EFI_IGNORE_OSINDICATIONS) to disable the OsIndications variable
 | |
| check. If that option is enabled just copy your capsule to \EFI\UpdateCapsule.
 | |
| 
 | |
| If that option is disabled, you'll need to set the OsIndications variable with::
 | |
| 
 | |
|     => setenv -e -nv -bs -rt -v OsIndications =0x04
 | |
| 
 | |
| Finally, the capsule update can be initiated either by rebooting the board,
 | |
| which is the preferred method, or by issuing the following command::
 | |
| 
 | |
|     => efidebug capsule disk-update
 | |
| 
 | |
| **The efidebug command is should only be used during debugging/development.**
 | |
| 
 | |
| Enabling Capsule Authentication
 | |
| *******************************
 | |
| 
 | |
| The UEFI specification defines a way of authenticating the capsule to
 | |
| be updated by verifying the capsule signature. The capsule signature
 | |
| is computed and prepended to the capsule payload at the time of
 | |
| capsule generation. This signature is then verified by using the
 | |
| public key stored as part of the X509 certificate. This certificate is
 | |
| in the form of an efi signature list (esl) file, which is embedded as
 | |
| part of U-Boot.
 | |
| 
 | |
| The capsule authentication feature can be enabled through the
 | |
| following config, in addition to the configs listed above for capsule
 | |
| update::
 | |
| 
 | |
|     CONFIG_EFI_CAPSULE_AUTHENTICATE=y
 | |
|     CONFIG_EFI_CAPSULE_KEY_PATH=<path to .esl cert>
 | |
| 
 | |
| The public and private keys used for the signing process are generated
 | |
| and used by the steps highlighted below::
 | |
| 
 | |
|     1. Install utility commands on your host
 | |
|        * OPENSSL
 | |
|        * efitools
 | |
| 
 | |
|     2. Create signing keys and certificate files on your host
 | |
| 
 | |
|         $ openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=CRT/ \
 | |
|             -keyout CRT.key -out CRT.crt -nodes -days 365
 | |
|         $ cert-to-efi-sig-list CRT.crt CRT.esl
 | |
| 
 | |
|         $ openssl x509 -in CRT.crt -out CRT.cer -outform DER
 | |
|         $ openssl x509 -inform DER -in CRT.cer -outform PEM -out CRT.pub.pem
 | |
| 
 | |
|         $ openssl pkcs12 -export -out CRT.pfx -inkey CRT.key -in CRT.crt
 | |
|         $ openssl pkcs12 -in CRT.pfx -nodes -out CRT.pem
 | |
| 
 | |
| The capsule file can be generated by using the GenerateCapsule.py
 | |
| script in EDKII::
 | |
| 
 | |
|     $ ./BaseTools/BinWrappers/PosixLike/GenerateCapsule -e -o \
 | |
|       <capsule_file_name> --monotonic-count <val> --fw-version \
 | |
|       <val> --lsv <val> --guid \
 | |
|       e2bb9c06-70e9-4b14-97a3-5a7913176e3f --verbose \
 | |
|       --update-image-index <val> --signer-private-cert \
 | |
|       /path/to/CRT.pem --trusted-public-cert \
 | |
|       /path/to/CRT.pub.pem --other-public-cert /path/to/CRT.pub.pem \
 | |
|       <u-boot.bin>
 | |
| 
 | |
| Place the capsule generated in the above step on the EFI System
 | |
| Partition under the EFI/UpdateCapsule directory
 | |
| 
 | |
| Testing on QEMU
 | |
| ***************
 | |
| 
 | |
| Currently, support has been added on the QEMU ARM64 virt platform for
 | |
| updating the U-Boot binary as a raw image when the platform is booted
 | |
| in non-secure mode, i.e. with CONFIG_TFABOOT disabled. For this
 | |
| configuration, the QEMU platform needs to be booted with
 | |
| 'secure=off'. The U-Boot binary placed on the first bank of the NOR
 | |
| flash at offset 0x0. The U-Boot environment is placed on the second
 | |
| NOR flash bank at offset 0x4000000.
 | |
| 
 | |
| The capsule update feature is enabled with the following configuration
 | |
| settings::
 | |
| 
 | |
|     CONFIG_MTD=y
 | |
|     CONFIG_FLASH_CFI_MTD=y
 | |
|     CONFIG_CMD_MTDPARTS=y
 | |
|     CONFIG_CMD_DFU=y
 | |
|     CONFIG_DFU_MTD=y
 | |
|     CONFIG_PCI_INIT_R=y
 | |
|     CONFIG_EFI_CAPSULE_ON_DISK=y
 | |
|     CONFIG_EFI_CAPSULE_FIRMWARE_MANAGEMENT=y
 | |
|     CONFIG_EFI_CAPSULE_FIRMWARE=y
 | |
|     CONFIG_EFI_CAPSULE_FIRMWARE_RAW=y
 | |
| 
 | |
| In addition, the following config needs to be disabled(QEMU ARM specific)::
 | |
| 
 | |
|     CONFIG_TFABOOT
 | |
| 
 | |
| The capsule file can be generated by using the tools/mkeficapsule::
 | |
| 
 | |
|     $ mkeficapsule --raw <u-boot.bin> --index 1 <capsule_file_name>
 | |
| 
 | |
| 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`
 |