.. SPDX-License-Identifier: GPL-2.0+
.. Copyright (C) 2020, Linaro Limited

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.

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
    CONFIG_EFI_CAPSULE_FMP_HEADER=y

In addition, the following config needs to be disabled(QEMU ARM specific)::

    CONFIG_TFABOOT

The capsule file can be generated by using the GenerateCapsule.py
script in EDKII::

    $ ./BaseTools/BinWrappers/PosixLike/GenerateCapsule -e -o \
    <capsule_file_name> --fw-version <val> --lsv <val> --guid \
    e2bb9c06-70e9-4b14-97a3-5a7913176e3f --verbose --update-image-index \
    <val> --verbose <u-boot.bin>

The above is a wrapper script(GenerateCapsule) which eventually calls
the actual GenerateCapsule.py script.

As per the UEFI specification, the capsule file needs to be placed on
the EFI System Partition, under the \EFI\UpdateCapsule directory. The
EFI System Partition can be a virtio-blk-device.

Before initiating the firmware update, the efi variables BootNext,
BootXXXX and OsIndications need to be set. The BootXXXX variable needs
to be pointing to the EFI System Partition which contains the capsule
file. The BootNext, BootXXXX and OsIndications variables can be set
using the following commands::

    => efidebug boot add 0 Boot0000 virtio 0:1 <capsule_file_name>
    => efidebug boot next 0
    => setenv -e -nv -bs -rt -v OsIndications =0x04
    => saveenv

Finally, the capsule update can be initiated with the following
command::

    => efidebug capsule disk-update

The updated U-Boot image will be booted on subsequent boot.

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 the platform's device tree blob using the mkeficapsule
utility.

On the QEMU virt platforms, the device-tree is generated on the fly
based on the devices configured. This device tree is then passed on to
the various software components booting on the platform, including
U-Boot. Therefore, on the QEMU virt platform, the signatute is
embedded on an overlay. This overlay is then applied at runtime to the
base platform device-tree. Steps needed for embedding the esl file in
the overlay are highlighted below.

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

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

For embedding the public key certificate, the following steps need to
be followed::

    1. Generate a skeleton overlay dts file, with a single fragment
       node and an empty __overlay__ node

       A typical skeleton overlay file will look like this

       /dts-v1/;
       /plugin/;

       / {
               fragment@0 {
                       target-path = "/";
                       __overlay__ {
                       };
               };
       };


    2. Convert the dts to a corresponding dtb with the following
       command
        ./scripts/dtc/dtc -@ -I dts -O dtb -o <ov_dtb_file_name> \
        <dts_file>

    3. Run the dtb file generated above through the mkeficapsule tool
       in U-Boot
        ./tools/mkeficapsule -O <pub_key.esl> -D <ov_dtb>

Running the above command results in the creation of a 'signature'
node in the dtb, under which the public key is stored as a
'capsule-key' property. The '-O' option is to be used since the
public key certificate(esl) file is being embedded in an overlay.

The dtb file embedded with the certificate is now to be placed on an
EFI System Partition. This would then be loaded and "merged" with the
base platform flattened device-tree(dtb) at runtime.

Build U-Boot with the following steps(QEMU ARM64)::

    $ make qemu_arm64_defconfig
    $ make menuconfig
        Disable CONFIG_TFABOOT
        Enable CONFIG_EFI_CAPSULE_AUTHENTICATE
        Enable all configs needed for capsule update(listed above)
    $ make all

Boot the platform and perform the following steps on the U-Boot
command line::

    1. Enable capsule authentication by setting the following env
       variable

        => setenv capsule_authentication_enabled 1
        => saveenv

    2. Load the overlay dtb to memory and merge it with the base fdt

        => fatload virtio 0:1 <$fdtovaddr> EFI/<ov_dtb_file>
        => fdt addr $fdtcontroladdr
        => fdt resize <size_of_ov_dtb_file>
        => fdt apply <$fdtovaddr>

    3. Set the following environment and UEFI boot variables

        => setenv -e -nv -bs -rt -v OsIndications =0x04
        => efidebug boot add 0 Boot0000 virtio 0:1 <capsule_file_name>
        => efidebug boot next 0
        => saveenv

    4. Finally, the capsule update can be initiated with the following
       command

        => efidebug capsule disk-update

On subsequent reboot, the platform should boot the updated U-Boot binary.
