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>
This commit is contained in:
@@ -13,6 +13,7 @@ Implementation
|
||||
global_data
|
||||
logging
|
||||
menus
|
||||
uefi/index
|
||||
version
|
||||
|
||||
Debugging
|
||||
|
15
doc/develop/uefi/index.rst
Normal file
15
doc/develop/uefi/index.rst
Normal file
@@ -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
|
184
doc/develop/uefi/iscsi.rst
Normal file
184
doc/develop/uefi/iscsi.rst
Normal file
@@ -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
|
235
doc/develop/uefi/u-boot_on_efi.rst
Normal file
235
doc/develop/uefi/u-boot_on_efi.rst
Normal file
@@ -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/
|
498
doc/develop/uefi/uefi.rst
Normal file
498
doc/develop/uefi/uefi.rst
Normal file
@@ -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`
|
Reference in New Issue
Block a user