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 process has the sequence of events as shown in this figure page 3, 4 and 5. The detailed UEFI platform initialization (PI) firmware process can be found here.

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>.<GUID1>-:1.0
<custom update module>.<GUID2>-:1.2
}

depends: {
<custom update module>.<GUID1>:0.9
<custom update module>.<GUID2>:1.1
}

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 --no-default-clears-provides  --depends KEY:VALUE --provides KEY:VALUE
-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-software-version   Disable the software version field for compatibility with old clients
--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

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

2.2 Update scenarios

Scenario Mender artifact Pre-existing provides on the device Update scenario
1
provides depends
uefi-firmware.uefi-capsule.tfa : 1.7 uefi-firmware.uefi-capsule.tfa: 1.6
uefi-firmware.uefi-capsule.edk2 : 2.0 uefi-firmware.uefi-capsule.edk2 : 1.0
uefi-firmware.uefi-capsule.optee : 1.5 uefi-firmware.uefi-capsule.optee : 1.2
uefi-firmware.uefi-capsule.tfm : 3.0 uefi-firmware.uefi-capsule.tfm : 2.7
provides
uefi-firmware.uefi-capsule.tfa : 1.6
uefi-firmware.uefi-capsule.edk2 : 1.0
uefi-firmware.uefi-capsule.optee : 1.2
uefi-firmware.uefi-capsule.tfm : 2.7
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.uefi-capsule.tfa : 1.7
uefi-firmware.uefi-capsule.edk2 : 2.0
uefi-firmware.uefi-capsule.optee : 1.5
uefi-firmware.uefi-capsule.tfm : 3.0
provides
uefi-firmware.uefi-capsule.tfa : 1.6
uefi-firmware.uefi-capsule.edk2 : 1.0
uefi-firmware.uefi-capsule.optee : 1.2
uefi-firmware.uefi-capsule.tfm : 2.7
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.uefi-capsule.tfa : 1.7 uefi-firmware.uefi-capsule.tfa: 1.6
provides
uefi-firmware.uefi-capsule.tfa : 1.6
uefi-firmware.uefi-capsule.edk2 : 1.0
uefi-firmware.uefi-capsule.optee : 1.2
uefi-firmware.uefi-capsule.tfm : 2.7
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 1.6 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.uefi-capsule.tfa : 1.7
provides
uefi-firmware.uefi-capsule.tfa : 1.6
uefi-firmware.uefi-capsule.edk2 : 1.0
uefi-firmware.uefi-capsule.optee : 1.2
uefi-firmware.uefi-capsule.tfm : 2.7
In this case, the mender artifact contains only a single firmware image with provides tfa 1.7 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 1.7 then the artifact should be rejected. Since the device in this case has tfa 1.6, the installation will be successful.
5
provides depends
uefi-firmware.uefi-capsule.tfa : 1.7 uefi-firmware.uefi-capsule.tfa: 1.6
uefi-firmware.uefi-capsule.edk2 : 1.0
uefi-firmware.uefi-capsule.optee : 1.2
uefi-firmware.uefi-capsule.tfm : 2.7
provides
uefi-firmware.uefi-capsule.tfa : 1.6
uefi-firmware.uefi-capsule.edk2 : 1.0
uefi-firmware.uefi-capsule.optee : 1.2
uefi-firmware.uefi-capsule.tfm : 2.7
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.uefi-capsule.tfa : 1.7 uefi-firmware.uefi-capsule.tfa: 1.6
uefi-firmware.uefi-capsule.edk2 : 2.0 uefi-firmware.uefi-capsule.edk2 : 1.0
uefi-firmware.uefi-capsule.optee : 1.5 uefi-firmware.uefi-capsule.optee : 1.2
uefi-firmware.uefi-capsule.tfm : 3.0 uefi-firmware.uefi-capsule.tfm : 2.7
provides
uefi-firmware.uefi-capsule.tfa : 1.3
uefi-firmware.uefi-capsule.edk2 : 1.0
uefi-firmware.uefi-capsule.optee : 1.2
uefi-firmware.uefi-capsule.tfm : 2.7
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 depends
provides
uefi-firmware.uefi-capsule.tfa : 1.7 uefi-firmware.uefi-capsule.tfa: 1.6
provides
uefi-firmware.uefi-capsule.tfa : 1.2
uefi-firmware.uefi-capsule.edk2 : 1.0
uefi-firmware.uefi-capsule.optee : 1.2
uefi-firmware.uefi-capsule.tfm : 2.7
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 1.6 version.
8 !
provides depends
uefi-firmware.uefi-capsule.tfa : 1.7
uefi-firmware.uefi-capsule.edk2 : 2.0
uefi-firmware.uefi-capsule.optee : 1.5
uefi-firmware.uefi-capsule.tfm : 1.7
provides
uefi-firmware.uefi-capsule.tfa : 1.6
uefi-firmware.uefi-capsule.edk2 : 1.0
uefi-firmware.uefi-capsule.optee : 1.2
uefi-firmware.uefi-capsule.tfm : 2.7
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.uefi-capsule.tfa : 1.1
provides
uefi-firmware.uefi-capsule.tfa : 1.6
uefi-firmware.uefi-capsule.edk2 : 1.0
uefi-firmware.uefi-capsule.optee : 1.2
uefi-firmware.uefi-capsule.tfm : 2.7
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.

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
2 Likes

@kacf Please could you share your thoughts on this and tag the necessary members to have a look at this? Thanks.

Sorry for the delay, this was quite a lot to take in.

I think the proposal is very good, very similar to what I would have suggested myself. You are using provides and depends exactly as intended, I believe!

Probably I’m the main guy to look at this, but I’ll tag @eystein as well, just so he’s aware. I’m not sure if he will have any comments, but I know he’s looking a bit at UEFI support, though at a bit higher and more architectural level.

One comment about naming

You describe the --no-default-software-version flag, but in your command you have --no-default-clears-provides. I would recommend that you remove both, and instead use --software-filesystem uefi-firmware --software-name edk2. This aligns it better with your later examples, and sets up both the “provides” and the “clears provides” to support this use case.

Your naming would then change slightly: Instead of uefi-firmware.uefi-capsule.edk2: 1.0, you would have uefi-firmware.edk2.name : 1.0. Although strictly speaking your naming is not wrong, my suggestion has the advantage that it is more in line with what Mender expects, it will look better in the UI, and most likely you can stick to only the --software-* options, and avoid the --provides options altogether.

With this approach it is also possible to put other pieces of information under edk2, such as for example: --provides uefi-firmware.edk2.checksum: abcdef.

1 Like

Tagging: @GowthamSK

Thanks for reviewing and tagging the necessary members.

Considering your suggestion: I think the idea to use this kind of format uefi-firmware.<firmware name>.<GUID of the firmware>:<version> would be beneficial?. As firmware’s have a unique identification mentioned below:
FwClass - The firmware class field contains a GUID that identifies a firmware component that can be updated via UpdateCapsule(). This GUID must be unique within all entries of the ESRT.
It would be easy to identify GUID to their name in the provides?

./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.<*> --depends KEY:VALUE --provides KEY:VALUE
-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 is used instead of rootfs-image

Please note: --clears-provides uefi-firmware.<*> should be similar to provides, so that it clears any existing provides.

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.tfa.<GUID> --depends uefi-firmware.tfa.<GUID>:15 --provides uefi-firmware.tfa.<GUID>:16
--clears-provides uefi-firmware.edk2.<GUID> --depends uefi-firmware.edk2.<GUID>:10 --provides uefi-firmware.edk2.<GUID>:12
--clears-provides uefi-firmware.optee.<GUID> --depends uefi-firmware.optee.<GUID>:11 --provides uefi-firmware.optee.<GUID>:12

We are not sure whether we need to append uefi-firmware to the provides and depends fields in the above command, as we have specified it in --software-filesystem?

Unfortunately, I can’t update the proposal above as it has been disabled to take in more changes. So when we have finalised this design, I’ll post the complete this again.

Just a clarification first, I made a mistake above: When I said uefi-firmware.edk2.name, I meant uefi-firmware.edk2.version. Sorry about that.

Aha, so you want both a name and a GUID. Is there a 1-to-1 relationship between the GUID and the name? Or can there be multiple GUIDs within the same name? If it’s the former, I might recommend something like this instead:

uefi-firmware.<firmware name>.version: <version>
uefi-firmware.<firmware name>.guid: <GUID of the firmware>

But if you want it the way you described, then I would recommend you use this:

uefi-firmware.<firmware name>.<GUID of the firmware>.version: <version>

Note the verbatim version in there. This is important for clear-provides to work correctly. But I think it will not render correctly in the UI, because it does not yet support multi-level software versions (this is coming though).

Your existing proposal will work just fine for dependency resolution though. I’m mainly concerned with how it will look in the UI.

Thanks for the reply.
< firmware name > is just an alias and can be the same (very low probability), as people can decide what they want to call it.
On the other hand <GUID> is unique, in that way 1:1 relationship can be established.

This should be good, but .version anyways is implied? so we could ignore that, therefore avoiding the complexity of having multiple fields.

So could go with the below?
uefi-firmware.<firmware name>.<GUID of the firmware>: <version>

or

uefi-firmware.<GUID of the firmware>.version: <version>
uefi-firmware.<GUID of the firmware>.name: <firmware name>

I understand about the UI, considering GUID could be for example: a2cb66ff-df5c-4a5c-aded-bf3dda35eb1a, so it will not look good.

The way to differentiate the firmware GUID is required.

.version has a special meaning in the UI. That’s why I keep suggesting it. :slightly_smiling_face:

This one is perfect. It both captures the necessary information, and will look good in the UI.

Could you please link the issue? Will update the design proposal and resend it as a reply.

It’s this one: [MEN-5875] UI: Implement support for more than three levels of software provides - Northern.tech AS.

It will work in your proposed variant though, because it has three levels, which is already supported.

Hi @kacf.

Thanks for your input. I tried both the naming conventions discussed above and found the following,
(Please note we are using Mender Server Version: 3.1.0)

  1. Separate name and version

uefi-firmware.GUID.version: v1
uefi-firmware.GUID.name: edk2

Sample Mender server view:
image

Sample Client view:

The .name parameter does not appear in the UI.

  1. Single field with name and version

uefi-firmware.GUID.edk2.version: v1

Sample Mender server view:
image

Sample Client view:


Only the GUID appears not the name in this case.

Is this expected? :slight_smile:

Oh my apologies, I thought this would render correctly, but it appears that there is special handling in the UI for rootfs-image, but not for other software components.

However, I spoke to our UI developer and he confirmed that this will be fixed in https://tracker.mender.io/browse/MEN-5875, which he is working on as we speak, so it should not be long before this is live on Hosted Mender.

@GowthamSK take a look at this pull request which has screenshots. We used your use case as inspiration! It’s being worked on right now, so maybe you can comment directly in there if you see something which doesn’t align with what you expect.

Thanks for the update @kacf.
image

This representation of the provides and depends fields works for us.

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

@kacf Updated the design proposal as per our discussion. Thanks.

I am sorry for the long delay here @jainvikas8 - to me the proposal looks really nice.

@kacf is really busy for a few days more, but the will get back to you soon :slight_smile:

1 Like

Looks good to me! I have updated Apply preexisting provides to compiled mender uefiimg image - #11 by kacf.

1 Like