# Design proposal: UEFI Capsule support

**URL:** <https://hub.mender.io/t/design-proposal-uefi-capsule-support/5278>\
**Category:** General Discussions\
**Tags:** yocto, uefi, capsule\
**Created:** [September 27, 2022, 9:01am UTC](https://hub.mender.io/t/design-proposal-uefi-capsule-support/5278 "2022-09-27T09:01:20Z")\
**Posts on this page:** 1\
**Showing post:** 15

<div class="post-metadata">

**Author:** ![jainvikas8](https://yyz2.discourse-cdn.com/flex036/user_avatar/hub.mender.io/jainvikas8/32/1558_2.png) [@jainvikas8](https://hub.mender.io/u/jainvikas8)\
**Post date:** [October 26, 2022, 4:36pm UTC](https://hub.mender.io/t/design-proposal-uefi-capsule-support/5278/15 "2022-10-26T16:36:32Z")

</div>

# **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](https://www.uefi.org/sites/default/files/resources/UEFI%20Fall%202018%20Intel%20UEFI%20Capsules.pdf#page=3 "System Firmware and Device Firmware Updates Using Unified Extensible Firmware Interface (UEFI) Capsules, Page 3"), [4](https://www.uefi.org/sites/default/files/resources/UEFI%20Fall%202018%20Intel%20UEFI%20Capsules.pdf#page=4 "System Firmware and Device Firmware Updates Using Unified Extensible Firmware Interface (UEFI) Capsules, Page 4"), and [5](https://www.uefi.org/sites/default/files/resources/UEFI%20Fall%202018%20Intel%20UEFI%20Capsules.pdf#page=5 "System Firmware and Device Firmware Updates Using Unified Extensible Firmware Interface (UEFI) Capsules, Page 5").  
The information on various other topics related to UEFI can be found here:

1. [UEFI platform initialization (PI)](https://uefi.org/sites/default/files/resources/PI_Spec_1_6.pdf "UEFI Platform initialization (PI) specification") firmware process.
2. [UEFI 2.9 specification](https://uefi.org/sites/default/files/resources/UEFI_Spec_2_9_2021_03_18.pdf "UEFI specification 2.9 (Mar 2021)").
3. Additional [presentations and videos](https://uefi.org/learning_center/presentationsandvideos/ "UEFI presentation 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:**

```js
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](https://uefi.org/sites/default/files/resources/UEFI_Spec_2_9_2021_03_18.pdf#page=1084 "UEFI specification 2.9 (Mar 2021), Section 23.4") 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** :

```shell
./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:

```js
<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](https://github.com/mendersoftware/meta-mender "Meta-mender 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](https://hub.mender.io/t/apply-preexisting-provides-to-compiled-mender-uefiimg-image/4870 "Query 4890").

This [feature](https://tracker.mender.io/browse/MEN-2582 "MEN-2582") 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**](https://docs.mender.io/client-installation/inventory "Documentation - inventory")

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

```bash
#!/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](https://docs.mender.io/artifact-creation/create-a-custom-update-module#the-state-machine-workflow "state-machine-workflow") . The current custom update modules are under this [repository](https://github.com/mendersoftware/mender-update-modules "mender-ipdate-module 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](https://docs.mender.io/3.2/artifact-creation/create-a-custom-update-module#basic-example-file-copy-update-module "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)](https://uefi.org/sites/default/files/resources/UEFI_Spec_2_9_2021_03_18.pdf#page=1084 "UEFI specification 2.9 (Mar 2021), 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**

---

_[View the full topic](https://hub.mender.io/t/design-proposal-uefi-capsule-support/5278)._
