Design proposal: UEFI Capsule support

UEFI Capsule update proposal

1. Why do we need this?

Today Mender has no ability to update System Firmware. Whole firmware single image updates will be a step forwards from today’s firmware update capability, but the end goal should be to support partial firmware updates where only one part of the installed firmware is updated. Therefore, it would be ideal to use a UEFI capsule delivery mechanism to only update only specific firmware(s) on a device when needed by adding some intelligence on the device which has access to the information of ESRT table (GUID’s with their versions).

Benefits:

  • Specific devices are sent the update if they are out of date.
  • Rather than updating the whole image (bank), they only update specific GUID → firmware(s)

The capsule update sequence is described here on pages 3, 4, and 5.
The information on various other topics related to UEFI can be found here:

  1. UEFI platform initialization (PI) firmware process.
  2. UEFI 2.9 specification.
  3. Additional presentations and videos.

2. Mender Server – The deployment microservice

The deployment microservice is the one that is responsible for deciding if an artifact must be pushed to a device or not. If an update is required, the microservice monitors the deployment for each device until it is completed. Please note when the device is provisioned, pre-existing provides needs to be present for it to deploy the update, because the board might have single or multiple firmware image(s) containing their own version information.

There are no changes required for deployment service, but we must specify the provides and depends field to give the top layer control over what version of firmware(s) are pushed on to the device when generating the mender artifact.

For example:

provides: {
<custom update module>.<GUID_1>.version:10
<custom update module>.<GUID_2>.version:12
}

depends: {
<custom update module>.<GUID_1>.version:9
<custom update module>.<GUID_2>.version:9
}

Note: The firmware versions are read from ESRT table which is of integer format.

2.1 Artifact format for UEFI capsule update

UEFI capsule update works through custom update module scripts so we need to create an artifact of type module-image. The firmware versioning information is encoded in the provides and depends field of the artifact which is used by the deployment microservice to decide if the artifact needs to be pushed to the device or not.

For example:

./mender-artifact write module-image -t <platform> -o capsule.mender -T uefi-capsule -n <sha or md5> -f <platform-capsule> --software-filesystem uefi-firmware --no-default-clears-provides
--clears-provides uefi-firmware.<GUID_1>.* --depends uefi-firmware.<GUID_1>.version:15 --provides uefi-firmware.<GUID_1>.version:16 --provides uefi-firmware.<GUID_1>.name:<firmware_name>
--clears-provides uefi-firmware.<GUID_2>.* --depends uefi-firmware.<GUID_2>.version:10 --provides uefi-firmware.<GUID_2>.version:12 --provides uefi-firmware.<GUID_2>.name:<firmware_name>
--clears-provides uefi-firmware.<GUID_3>.* --depends uefi-firmware.<GUID_3>.version:10 --provides uefi-firmware.<GUID_3>.version:13 --provides uefi-firmware.<GUID_3>.name:<firmware_name>
-t  The compatible device type of this Mender Artifact.
-o  The path where to place the output Mender Artifact. This should always have a `.mender` suffix.
-T  The payload type. It should be the same as the Update Module name and corresponds to the filename of the script which is present inside the  /usr/share/mender/modules/v3 directory.
-n  The name of the Mender Artifact. (This could sha or md5 of uefi image)
-f  The path to the file(s) to send to the device(s) in the update.
--no-default-clear-provides     Do not add any default clears_artifact_provides fields to Artifact payload
--clears-provides VALUE         Add a clears_artifact_provides field to Artifact payload. Can be given multiple times
--provides KEY:VALUE            Generic KEY:VALUE which is added to the type-info -> artifact_provides section. Can be given multiple times
--depends KEY:VALUE             Generic KEY:VALUE which is added to the type-info -> artifact_depends section. Can be given multiple times
--software-filesystem VALUE     If specified, it is used instead of rootfs-image

Depending on various update scenarios mentioned below we need to modify the above command.

2.2 Update scenarios

Note: In the mentioned scenarios below, these are the following assumptions:

  1. The GUID is a unique ID/name, which would be translated to --provides <custom_update_module>.tfa.version:16 or --provides <custom_update_module>.78dg-349765nf-45nf-vioj4.version:16.
  2. .version has a special meaning in the mender server UI.
  3. We can have multiple KEY:VALUE pairs for uefi-firmware.<GUID_1>.*, where GUID_1 is unique.
    For example:
    <custom_update_module>.<GUID_1>.version:<version>
    <custom_update_module>.<GUID_1>.name:<firmware_name>
    
Scenario Mender artifact Pre-existing provides on the device Update scenario
1
provides depends
uefi-firmware.tfa.version : 17 uefi-firmware.tfa.version: 16
uefi-firmware.edk2.version : 20 uefi-firmware.edk2.version : 10
uefi-firmware.optee.version : 15 uefi-firmware.optee.version : 12
uefi-firmware.tfm.version : 30 uefi-firmware.tfm.version : 27
provides
uefi-firmware.tfa.version : 16
uefi-firmware.edk2.version : 10
uefi-firmware.optee.version : 12
uefi-firmware.tfm.version : 27
This update can be used when all the firmware images must be updated on the device. As seen in the diagram, the mender artifact has the latest firmware versions in the provides compared to the artifact depends. This artifact will be installed on all devices which have provides matching the artifact’s depends. If any depends does not match a provides on the device, the server is not going to push the artifact to the device.
2
provides depends
uefi-firmware.tfa.version : 17
uefi-firmware.edk2.version : 20
uefi-firmware.optee.version : 15
uefi-firmware.tfm.version : 30
provides
uefi-firmware.tfa.version : 16
uefi-firmware.edk2.version : 10
uefi-firmware.optee.version : 12
uefi-firmware.tfm.version : 27
In this case, the artifact will be pushed to all the available devices as there are no depends in the artifact. Since the pushed artifact is containing all the new artifact versions, the mender client will install all the firmware capsules and update the device provides field. If any firmware image installation fails, the device will reject all the firmware images in the capsule and report the update as unsuccessful to the server.
3
provides depends
uefi-firmware.tfa.version : 17 uefi-firmware.tfa.version: 16
provides
uefi-firmware.tfa.version : 16
uefi-firmware.edk2.version : 10
uefi-firmware.optee.version : 12
uefi-firmware.tfm.version : 27
In this case, the artifact contains only a single firmware image in the capsule and also has a depends field. Only those devices with provides containing tfa 16 will receive the artifact. Upon successful installation, only the tfa provides information on the device will be updated. If the installation fails, then the mender client should report to the server.
4
provides depends
uefi-firmware.tfa.version : 17
provides
uefi-firmware.tfa.version : 16
uefi-firmware.edk2.version : 10
uefi-firmware.optee.version : 12
uefi-firmware.tfm.version : 27
In this case, the mender artifact contains only a single firmware image with provides tfa 17 which will be pushed to all the devices as there is no artifact depends - device provides matching. If the device already has a tfa version greater than 17 then the artifact should be rejected. Since the device in this case has tfa 16, the installation will be successful.
5
provides depends
uefi-firmware.tfa.version : 17 uefi-firmware.tfa.version: 16
uefi-firmware.edk2.version : 10
uefi-firmware.optee.version : 12
uefi-firmware.tfm.version : 27
provides
uefi-firmware.tfa.version : 16
uefi-firmware.edk2.version : 10
uefi-firmware.optee.version : 12
uefi-firmware.tfm.version : 27
In this case, the mender artifact contains only a single firmware image capsule and has depends for multiple firmware images. This update can be used when a firmware image has dependencies on other firmware image versions. In the current scenario, the update will be successful as provides, depends match and tfa version is higher than the currently installed version.
6
provides depends
uefi-firmware.tfa.version : 17 uefi-firmware.tfa.version: 16
uefi-firmware.edk2.version : 20 uefi-firmware.edk2.version : 10
uefi-firmware.optee.version : 15 uefi-firmware.optee.version : 12
uefi-firmware.tfm.version : 30 uefi-firmware.tfm.version : 27
provides
uefi-firmware.tfa.version : 13
uefi-firmware.edk2.version : 10
uefi-firmware.optee.version : 12
uefi-firmware.tfm.version : 27
This is a negative case, where the update fails. Since one of the depends on the artifact does not match any provides on the device, the installation aborts.
7
provides depends
uefi-firmware.tfa.version : 17 uefi-firmware.tfa.version: 16
provides
uefi-firmware.tfa.version : 12
uefi-firmware.edk2.version : 10
uefi-firmware.optee.version : 12
uefi-firmware.tfm.version : 27
This is again a negative update scenario, where the single depends field does not match any provides on the device on the artifact. The artifact in this case can only be pushed to device with tfa 16 version.
8 !
provides depends
uefi-firmware.tfa.version : 17
uefi-firmware.edk2.version : 20
uefi-firmware.optee.version : 15
uefi-firmware.tfm.version : 17
provides
uefi-firmware.tfa.version : 16
uefi-firmware.edk2.version : 10
uefi-firmware.optee.version : 12
uefi-firmware.tfm.version : 27
This is a negative update case. The artifacts firstly do not contain any depends field. So all device receive the artifact. But in the current scenario, the tfm version in the artifact is older than the one in the artifact. So all the firmware images in the artifact should be rejected and the client reports the failure status to the server.
9
provides depends
uefi-firmware.tfa.version : 11
provides
uefi-firmware.tfa.version : 16
uefi-firmware.edk2.version : 10
uefi-firmware.optee.version : 12
uefi-firmware.tfm.version : 27
This is again a similar negative update scenario. In this case, the tfa version is older than the one installed on the device. So even though the artifact gets pushed the client should not install the firmware image.

2.3 Pre-existing provides on the device

For the update scenarios explained above to work as expected, we need to have the device provisioned with provides. This feature is currently missing on the meta-mender yocto repository. There is a MENDER_ARTIFACT_PROVIDES yocto variable available but this only adds the provides information to the mender artifact but not the compiled image. This query was posted in the mender community and the discussion can be found here.

This feature has been planned for implementation within the mender community. The approach is to have an bootstrap.mender artifact with the initial provides information included in the compiled image. This artifact will be installed by the mender client upon first boot to populate the provides information.

Therefore, we need to implement a yocto class or script to use the tool to populate necessary details after the UEFI image has been created for a target device which would be wrapped as a mender artifact.

3. Mender Client

To support a UEFI capsule update, we need to develop a custom script which would be part of mender client to collect all the information related to firmware from the ESRT table and export as a json file before the update process begins. This is to maintain the information of previous firmware version(s) of the mender-client which would be used for performing a check after the update process. This script can also publish the details to the mender server if needed.

Example of custom inventory script

Example code snippet to read ESRT table and write a json file:

#!/bin/bash

WORK_DIR="/sys/firmware/efi/esrt"
OUTPUT_FILE="esrt_table.json"

write_info() {
NUM_ENTRIES=$(cat $WORK_DIR/fw_resource_count)
if [ -z "$NUM_ENTRIES" ]; then
    echo "Error: No entries found in ESRT table. Can not continue..."
    exit 1
fi

echo "$(cat <<EOF
{
    "fw_resource_count": $NUM_ENTRIES,
    "fw_resource_count_max": $(cat $WORK_DIR/fw_resource_count_max),
    "fw_resource_version": $(cat $WORK_DIR/fw_resource_version),
    "entries": {
EOF
)" > $OUTPUT_FILE

index=0
while [ $index -lt $NUM_ENTRIES ]
do
    ENTRY_PATH="$WORK_DIR/entries/entry$index"
    echo "$(cat <<EOF
    "entry$index": {
        "capsule_flags": "$(cat $ENTRY_PATH/capsule_flags)",
        "fw_class": "$(cat $ENTRY_PATH/fw_class)",
        "fw_type": $(cat $ENTRY_PATH/fw_type),
        "fw_version": $(cat $ENTRY_PATH/fw_version),
        "last_attempt_status": $(cat $ENTRY_PATH/last_attempt_status),
        "last_attempt_version": $(cat $ENTRY_PATH/last_attempt_version),
        "lowest_supported_fw_version": $(cat $ENTRY_PATH/lowest_supported_fw_version)
}
EOF
)" >> $OUTPUT_FILE

if [ $index -lt `expr $NUM_ENTRIES - 1` ]; then
truncate -s-1 $OUTPUT_FILE
echo "," >> $OUTPUT_FILE
else
echo "$(cat <<EOF
    }
}
EOF
)" >> $OUTPUT_FILE
fi

index=`expr $index + 1`
done

}

if [ -d "$WORK_DIR" ]; then
    echo "Reading the ESRT table from ${WORK_DIR}..."
    if [ -f "$OUTPUT_FILE" ]; then
        chmod a+rw $OUTPUT_FILE
        rm -rf $OUTPUT_FILE
    fi
    write_info
    chmod a+rw $OUTPUT_FILE
    echo "Output file - $(cd "$(dirname "$OUTPUT_FILE")"; pwd -P)/$(basename "$OUTPUT_FILE")"
else
    echo "Error: ${WORK_DIR} not found. Can not continue."
    exit 1
fi

4. Creating a UEFI Update module

The Update module follows Mender state machine workflow as described in the state-machine-workflow . The current custom update modules are under this repository, which also hosts an artifact generation script for the custom update module. We are relying on mender-artifact tool to populate the necessary provides and depends fields.

An example of Custom Update module is already provided by mender with rollback feature. With rollback scenario, the initial investigation suggests that LastAttemptVersion and LastAttemptStatus from the EFI_SYSTEM_RESOURCE_ENTRY (section 23.4) should ensure that we are not running on trial state firmware after the firmware update.

Since we would have a json file with ESRT information, the update module needs to verify ESRT Table with various GUID’s of the firmware versions to check if the update was successful and then only perform Artifact commit.

Implementation stages

Stage I – All required firmware(s) or complete software image in UEFI Capsule

  • Develop a script to read ESRT table.
  • Develop yocto class or script which will package UEFI image into mender artifact.
  • Develop UEFI update module which includes mender artifact generation script.
  • Test the update module.
  • Refactor - UEFI Update module with rollback scenario
  • Test the update module with rollback scenario

Stage II – Single or multiple firmware(s) in UEFI Capsule (Phased firmware update)

  • Refactor - UEFI Update module
  • Test the update module.
  • Refactor - UEFI Update module with rollback scenario
  • Test the update module with rollback scenario
1 Like