diff options
| author | Simon Glass <sjg@chromium.org> | 2021-03-18 20:25:11 +1300 | 
|---|---|---|
| committer | Simon Glass <sjg@chromium.org> | 2021-03-26 17:03:10 +1300 | 
| commit | d1ceeeff6c2ee1e55b7140654c8d6de44b60dab6 (patch) | |
| tree | 7abee7a0ca0e6c1b7e8d12ff9b20340b8888baf7 /doc/develop/uefi | |
| parent | cad7b6b2519a275d79085bbdff0227492cd8ee48 (diff) | |
doc: Move UEFI under develop/
Much of the content here is useful only for development. Move it under
that section.
Signed-off-by: Simon Glass <sjg@chromium.org>
Reviewed-by: Heinrich Schuchardt <xypron.glpk@gmx.de>
Diffstat (limited to 'doc/develop/uefi')
| -rw-r--r-- | doc/develop/uefi/index.rst | 15 | ||||
| -rw-r--r-- | doc/develop/uefi/iscsi.rst | 184 | ||||
| -rw-r--r-- | doc/develop/uefi/u-boot_on_efi.rst | 235 | ||||
| -rw-r--r-- | doc/develop/uefi/uefi.rst | 498 | 
4 files changed, 932 insertions, 0 deletions
| diff --git a/doc/develop/uefi/index.rst b/doc/develop/uefi/index.rst new file mode 100644 index 00000000000..7e65dbc5d5e --- /dev/null +++ b/doc/develop/uefi/index.rst @@ -0,0 +1,15 @@ +.. SPDX-License-Identifier: GPL-2.0+ + +Unified Extensible Firmware (UEFI) +================================== + +U-Boot provides an implementation of the UEFI API allowing to run UEFI +compliant software like Linux, GRUB, and iPXE. Furthermore U-Boot itself +can be run an UEFI payload. + +.. toctree:: +   :maxdepth: 2 + +   uefi.rst +   u-boot_on_efi.rst +   iscsi.rst diff --git a/doc/develop/uefi/iscsi.rst b/doc/develop/uefi/iscsi.rst new file mode 100644 index 00000000000..51d38cde243 --- /dev/null +++ b/doc/develop/uefi/iscsi.rst @@ -0,0 +1,184 @@ +.. SPDX-License-Identifier: GPL-2.0+ +.. Copyright (c) 2018 Heinrich Schuchardt + +iSCSI booting with U-Boot and iPXE +================================== + +Motivation +---------- + +U-Boot has only a reduced set of supported network protocols. The focus for +network booting has been on UDP based protocols. A TCP stack and HTTP support +are expected to be integrated in 2018 together with a wget command. + +For booting a diskless computer this leaves us with BOOTP or DHCP to get the +address of a boot script. TFTP or NFS can be used to load the boot script, the +operating system kernel and the initial file system (initrd). + +These protocols are insecure. The client cannot validate the authenticity +of the contacted servers. And the server cannot verify the identity of the +client. + +Furthermore the services providing the operating system loader or kernel are +not the ones that the operating system typically will use. Especially in a SAN +environment this makes updating the operating system a hassle. After installing +a new kernel version the boot files have to be copied to the TFTP server +directory. + +The HTTPS protocol provides certificate based validation of servers. Sensitive +data like passwords can be securely transmitted. + +The iSCSI protocol is used for connecting storage attached networks. It +provides mutual authentication using the CHAP protocol. It typically runs on +a TCP transport. + +Thus a better solution than DHCP/TFTP/NFS boot would be to load a boot script +via HTTPS and to download any other files needed for booting via iSCSI from the +same target where the operating system is installed. + +An alternative to implementing these protocols in U-Boot is to use an existing +software that can run on top of U-Boot. iPXE[1] is the "swiss army knife" of +network booting. It supports both HTTPS and iSCSI. It has a scripting engine for +fine grained control of the boot process and can provide a command shell. + +iPXE can be built as an EFI application (named snp.efi) which can be loaded and +run by U-Boot. + +Boot sequence +------------- + +U-Boot loads the EFI application iPXE snp.efi using the bootefi command. This +application has network access via the simple network protocol offered by +U-Boot. + +iPXE executes its internal script. This script may optionally chain load a +secondary boot script via HTTPS or open a shell. + +For the further boot process iPXE connects to the iSCSI server. This includes +the mutual authentication using the CHAP protocol. After the authentication iPXE +has access to the iSCSI targets. + +For a selected iSCSI target iPXE sets up a handle with the block IO protocol. It +uses the ConnectController boot service of U-Boot to request U-Boot to connect a +file system driver. U-Boot reads from the iSCSI drive via the block IO protocol +offered by iPXE. It creates the partition handles and installs the simple file +protocol. Now iPXE can call the simple file protocol to load GRUB[2]. U-Boot +uses the block IO protocol offered by iPXE to fulfill the request. + +Once GRUB is started it uses the same block IO protocol to load Linux. Via +the EFI stub Linux is called as an EFI application:: + +                  +--------+         +--------+ +                  |        | Runs    |        | +                  | U-Boot |========>| iPXE   | +                  | EFI    |         | snp.efi| +    +--------+    |        | DHCP    |        | +    |        |<===|********|<========|        | +    | DHCP   |    |        | Get IP  |        | +    | Server |    |        | Address |        | +    |        |===>|********|========>|        | +    +--------+    |        | Response|        | +                  |        |         |        | +                  |        |         |        | +    +--------+    |        | HTTPS   |        | +    |        |<===|********|<========|        | +    | HTTPS  |    |        | Load    |        | +    | Server |    |        | Script  |        | +    |        |===>|********|========>|        | +    +--------+    |        |         |        | +                  |        |         |        | +                  |        |         |        | +    +--------+    |        | iSCSI   |        | +    |        |<===|********|<========|        | +    | iSCSI  |    |        | Auth    |        | +    | Server |===>|********|========>|        | +    |        |    |        |         |        | +    |        |    |        | Loads   |        | +    |        |<===|********|<========|        |       +--------+ +    |        |    |        | GRUB    |        | Runs  |        | +    |        |===>|********|========>|        |======>| GRUB   | +    |        |    |        |         |        |       |        | +    |        |    |        |         |        |       |        | +    |        |    |        |         |        | Loads |        | +    |        |<===|********|<========|********|<======|        |      +--------+ +    |        |    |        |         |        | Linux |        | Runs |        | +    |        |===>|********|========>|********|======>|        |=====>| Linux  | +    |        |    |        |         |        |       |        |      |        | +    +--------+    +--------+         +--------+       +--------+      |        | +                                                                      |        | +                                                                      |        | +                                                                      | ~ ~ ~ ~| + +Security +-------- + +The iSCSI protocol is not encrypted. The traffic could be secured using IPsec +but neither U-Boot nor iPXE does support this. So we should at least separate +the iSCSI traffic from all other network traffic. This can be achieved using a +virtual local area network (VLAN). + +Configuration +------------- + +iPXE +~~~~ + +For running iPXE on arm64 the bin-arm64-efi/snp.efi build target is needed:: + +    git clone http://git.ipxe.org/ipxe.git +    cd ipxe/src +    make bin-arm64-efi/snp.efi -j6 EMBED=myscript.ipxe + +The available commands for the boot script are documented at: + +http://ipxe.org/cmd + +Credentials are managed as environment variables. These are described here: + +http://ipxe.org/cfg + +iPXE by default will put the CPU to rest when waiting for input. U-Boot does +not wake it up due to missing interrupt support. To avoid this behavior create +file src/config/local/nap.h: + +.. code-block:: c + +    /* nap.h */ +    #undef NAP_EFIX86 +    #undef NAP_EFIARM +    #define NAP_NULL + +The supported commands in iPXE are controlled by an include, too. Putting the +following into src/config/local/general.h is sufficient for most use cases: + +.. code-block:: c + +    /* general.h */ +    #define NSLOOKUP_CMD            /* Name resolution command */ +    #define PING_CMD                /* Ping command */ +    #define NTP_CMD                 /* NTP commands */ +    #define VLAN_CMD                /* VLAN commands */ +    #define IMAGE_EFI               /* EFI image support */ +    #define DOWNLOAD_PROTO_HTTPS    /* Secure Hypertext Transfer Protocol */ +    #define DOWNLOAD_PROTO_FTP      /* File Transfer Protocol */ +    #define DOWNLOAD_PROTO_NFS      /* Network File System Protocol */ +    #define DOWNLOAD_PROTO_FILE     /* Local file system access */ + +Open-iSCSI +~~~~~~~~~~ + +When the root file system is on an iSCSI drive you should disable pings and set +the replacement timer to a high value in the configuration file [3]:: + +    node.conn[0].timeo.noop_out_interval = 0 +    node.conn[0].timeo.noop_out_timeout = 0 +    node.session.timeo.replacement_timeout = 86400 + +Links +----- + +* [1] https://ipxe.org - iPXE open source boot firmware +* [2] https://www.gnu.org/software/grub/ - +  GNU GRUB (Grand Unified Bootloader) +* [3] https://github.com/open-iscsi/open-iscsi/blob/master/README - +  Open-iSCSI README diff --git a/doc/develop/uefi/u-boot_on_efi.rst b/doc/develop/uefi/u-boot_on_efi.rst new file mode 100644 index 00000000000..c9a41bc919f --- /dev/null +++ b/doc/develop/uefi/u-boot_on_efi.rst @@ -0,0 +1,235 @@ +.. SPDX-License-Identifier: GPL-2.0+ +.. Copyright (C) 2015 Google, Inc + +U-Boot on EFI +============= +This document provides information about U-Boot running on top of EFI, either +as an application or just as a means of getting U-Boot onto a new platform. + + +Motivation +---------- +Running U-Boot on EFI is useful in several situations: + +- You have EFI running on a board but U-Boot does not natively support it +  fully yet. You can boot into U-Boot from EFI and use that until U-Boot is +  fully ported + +- You need to use an EFI implementation (e.g. UEFI) because your vendor +  requires it in order to provide support + +- You plan to use coreboot to boot into U-Boot but coreboot support does +  not currently exist for your platform. In the meantime you can use U-Boot +  on EFI and then move to U-Boot on coreboot when ready + +- You use EFI but want to experiment with a simpler alternative like U-Boot + + +Status +------ +Only x86 is supported at present. If you are using EFI on another architecture +you may want to reconsider. However, much of the code is generic so could be +ported. + +U-Boot supports running as an EFI application for 32-bit EFI only. This is +not very useful since only a serial port is provided. You can look around at +memory and type 'help' but that is about it. + +More usefully, U-Boot supports building itself as a payload for either 32-bit +or 64-bit EFI. U-Boot is packaged up and loaded in its entirety by EFI. Once +started, U-Boot changes to 32-bit mode (currently) and takes over the +machine. You can use devices, boot a kernel, etc. + + +Build Instructions +------------------ +First choose a board that has EFI support and obtain an EFI implementation +for that board. It will be either 32-bit or 64-bit. Alternatively, you can +opt for using QEMU [1] and the OVMF [2], as detailed below. + +To build U-Boot as an EFI application (32-bit EFI required), enable CONFIG_EFI +and CONFIG_EFI_APP. The efi-x86_app config (efi-x86_app_defconfig) is set up +for this. Just build U-Boot as normal, e.g.:: + +   make efi-x86_app_defconfig +   make + +To build U-Boot as an EFI payload (32-bit or 64-bit EFI can be used), enable +CONFIG_EFI, CONFIG_EFI_STUB, and select either CONFIG_EFI_STUB_32BIT or +CONFIG_EFI_STUB_64BIT. The efi-x86_payload configs (efi-x86_payload32_defconfig +and efi-x86_payload32_defconfig) are set up for this. Then build U-Boot as +normal, e.g.:: + +   make efi-x86_payload32_defconfig (or efi-x86_payload64_defconfig) +   make + +You will end up with one of these files depending on what you build for: + +* u-boot-app.efi - U-Boot EFI application +* u-boot-payload.efi  - U-Boot EFI payload application + + +Trying it out +------------- +QEMU is an emulator and it can emulate an x86 machine. Please make sure your +QEMU version is 2.3.0 or above to test this. You can run the payload with +something like this:: + +   mkdir /tmp/efi +   cp /path/to/u-boot*.efi /tmp/efi +   qemu-system-x86_64 -bios bios.bin -hda fat:/tmp/efi/ + +Add -nographic if you want to use the terminal for output. Once it starts +type 'fs0:u-boot-payload.efi' to run the payload or 'fs0:u-boot-app.efi' to +run the application. 'bios.bin' is the EFI 'BIOS'. Check [2] to obtain a +prebuilt EFI BIOS for QEMU or you can build one from source as well. + +To try it on real hardware, put u-boot-app.efi on a suitable boot medium, +such as a USB stick. Then you can type something like this to start it:: + +   fs0:u-boot-payload.efi + +(or fs0:u-boot-app.efi for the application) + +This will start the payload, copy U-Boot into RAM and start U-Boot. Note +that EFI does not support booting a 64-bit application from a 32-bit +EFI (or vice versa). Also it will often fail to print an error message if +you get this wrong. + + +Inner workings +-------------- +Here follow a few implementation notes for those who want to fiddle with +this and perhaps contribute patches. + +The application and payload approaches sound similar but are in fact +implemented completely differently. + +EFI Application +~~~~~~~~~~~~~~~ +For the application the whole of U-Boot is built as a shared library. The +efi_main() function is in lib/efi/efi_app.c. It sets up some basic EFI +functions with efi_init(), sets up U-Boot global_data, allocates memory for +U-Boot's malloc(), etc. and enters the normal init sequence (board_init_f() +and board_init_r()). + +Since U-Boot limits its memory access to the allocated regions very little +special code is needed. The CONFIG_EFI_APP option controls a few things +that need to change so 'git grep CONFIG_EFI_APP' may be instructive. +The CONFIG_EFI option controls more general EFI adjustments. + +The only available driver is the serial driver. This calls back into EFI +'boot services' to send and receive characters. Although it is implemented +as a serial driver the console device is not necessarilly serial. If you +boot EFI with video output then the 'serial' device will operate on your +target devices's display instead and the device's USB keyboard will also +work if connected. If you have both serial and video output, then both +consoles will be active. Even though U-Boot does the same thing normally, +These are features of EFI, not U-Boot. + +Very little code is involved in implementing the EFI application feature. +U-Boot is highly portable. Most of the difficulty is in modifying the +Makefile settings to pass the right build flags. In particular there is very +little x86-specific code involved - you can find most of it in +arch/x86/cpu. Porting to ARM (which can also use EFI if you are brave +enough) should be straightforward. + +Use the 'reset' command to get back to EFI. + +EFI Payload +~~~~~~~~~~~ +The payload approach is a different kettle of fish. It works by building +U-Boot exactly as normal for your target board, then adding the entire +image (including device tree) into a small EFI stub application responsible +for booting it. The stub application is built as a normal EFI application +except that it has a lot of data attached to it. + +The stub application is implemented in lib/efi/efi_stub.c. The efi_main() +function is called by EFI. It is responsible for copying U-Boot from its +original location into memory, disabling EFI boot services and starting +U-Boot. U-Boot then starts as normal, relocates, starts all drivers, etc. + +The stub application is architecture-dependent. At present it has some +x86-specific code and a comment at the top of efi_stub.c describes this. + +While the stub application does allocate some memory from EFI this is not +used by U-Boot (the payload). In fact when U-Boot starts it has all of the +memory available to it and can operate as it pleases (but see the next +section). + +Tables +~~~~~~ +The payload can pass information to U-Boot in the form of EFI tables. At +present this feature is used to pass the EFI memory map, an inordinately +large list of memory regions. You can use the 'efi mem all' command to +display this list. U-Boot uses the list to work out where to relocate +itself. + +Although U-Boot can use any memory it likes, EFI marks some memory as used +by 'run-time services', code that hangs around while U-Boot is running and +is even present when Linux is running. This is common on x86 and provides +a way for Linux to call back into the firmware to control things like CPU +fan speed. U-Boot uses only 'conventional' memory, in EFI terminology. It +will relocate itself to the top of the largest block of memory it can find +below 4GB. + +Interrupts +~~~~~~~~~~ +U-Boot drivers typically don't use interrupts. Since EFI enables interrupts +it is possible that an interrupt will fire that U-Boot cannot handle. This +seems to cause problems. For this reason the U-Boot payload runs with +interrupts disabled at present. + +32/64-bit +~~~~~~~~~ +While the EFI application can in principle be built as either 32- or 64-bit, +only 32-bit is currently supported. This means that the application can only +be used with 32-bit EFI. + +The payload stub can be build as either 32- or 64-bits. Only a small amount +of code is built this way (see the extra- line in lib/efi/Makefile). +Everything else is built as a normal U-Boot, so is always 32-bit on x86 at +present. + +Future work +----------- +This work could be extended in a number of ways: + +- Add ARM support + +- Add 64-bit application support + +- Figure out how to solve the interrupt problem + +- Add more drivers to the application side (e.g. video, block devices, USB, +  environment access). This would mostly be an academic exercise as a strong +  use case is not readily apparent, but it might be fun. + +- Avoid turning off boot services in the stub. Instead allow U-Boot to make +  use of boot services in case it wants to. It is unclear what it might want +  though. + +Where is the code? +------------------ +lib/efi +	payload stub, application, support code. Mostly arch-neutral + +arch/x86/cpu/efi +	x86 support code for running as an EFI application and payload + +board/efi/efi-x86_app/efi.c +	x86 board code for running as an EFI application + +board/efi/efi-x86_payload +	generic x86 EFI payload board support code + +common/cmd_efi.c +	the 'efi' command + +-- +Ben Stoltz, Simon Glass +Google, Inc +July 2015 + +* [1] http://www.qemu.org +* [2] http://www.tianocore.org/ovmf/ diff --git a/doc/develop/uefi/uefi.rst b/doc/develop/uefi/uefi.rst new file mode 100644 index 00000000000..5a67737c157 --- /dev/null +++ b/doc/develop/uefi/uefi.rst @@ -0,0 +1,498 @@ +.. 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 1 HELLO mmc 0:1 /helloworld.efi.signed "" + +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. + +[1] https://optee.readthedocs.io/ - OP-TEE documentation + +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 +    EFI_INITRD_FILESPEC=interface dev:part path_to_initrd + +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` | 
