ONELab Firmware Packaging & Upload

There are two scenarios wherein a user can upload firmware for compliance testing in ONELab. The first is the creation of a new Release Stream, and the second is when a user wants to append a new firmware version to an existing Release Stream. Packaging the firmware for upload uses the same process for both as defined in the remainder of this section.

Firmware Upload Requirements

SystemReady DT compliance requires the firmware to adhere to UEFI specification. Thus, it’s required that Device/Product Manufacturer users of ONELab package their firmware to be compliant with the UEFI Specification in order to pass the SystemReady DT ACS tests. To support meeting this requirement, Linaro ONELab requires that the firmware from customers be built and packaged as defined in this section. Furthermore, taking into consideration the need for customers to support firmware updates from LVFS, ONELab requires the firmware to be packaged in an LVFS-compatible format. This section will describe these packaging requirements for ONELab to successfully receive and test customer developed firmware targeted for SystemReady DT compliance.

Rationale and Overview

The primary input into ONELab is of course the customer UEFI firmware to be verified as SystemReady DT Compliant. Just as important, ONELab has a goal of being able to validate the firmware that’s packaged to be leveraged by LVFS and the fwupd utility . This section provides an overview of LVFS and the high-level compatibility requirements ONELab will impose upon a user when uploading their firmware to be validated for SystemReady DT compliance. After this section, additional sections will outline the creation of the firmware images that will be compatible with both ONELab and LVFS.

LVFS is a platform that enables hardware vendors to securely and easily distribute firmware updates to Linux devices through the fwupd framework, ensuring that updates reach a wide array of users and systems. For vendors in the embedded space, ensuring compatibility with LVFS is critical because it simplifies the deployment of secure firmware updates across different Linux distributions, helping meet compliance standards like SystemReady DT, which is essential for ensuring embedded systems are interoperable, up-to-date, and secure.

To ensure that ONELab is validating firmware that is compatible with LVFS, ONELab requires firmware submitted for testing to be provided in the LVFS-compatible .cab format. Both LVFS required and optional content must be included when building the .cab file to meet ONELab’s requirements to validate against SystemReady DT test suites.

After reading this section, you will be able to successfully package their firmware in UEFI capsules that support SystemReady DT 2.1 requirements. This will also include providing firmware updates to existing ONELab Release Streams for verification.

Building the ONELab Firmware File for Upload

Warning

All examples in this document assume a Linux development environment, specifically a Debian-based Linux distribution that uses the APT package manager.

General Flow

As noted above, the primary input into ONELab by the customers is the firmware to be validated for SystemReady 2.1 compliance. The customer must build and package this firmware in a compliant format in order for ONELab to process and test it. There must also be enough information included for the user to be able to identify the firmware, the platform/DUT that it’s targeted for, and which Firmware Release Stream the firmware is associated with. The customer must package the firmware in a way that passes SystemReady testing when it’s provided to ONELab. To do this, ONELab requires that the customer firmware must be packaged into a single .cap (UEFI Capsule Update) file. UEFI is mandated by SystemReady DT. This .cap file includes all the required images(OP-TEE, TF-A, U-Boot, etc.) to install the entire firmware stack. This .cap is then wrapped into an LVFS .cab file. The .cab is what a customer will submit to ONELab for testing. The following sections define the mandatory fields that must be included in both the .cab and the .cap files when submitting to ONELab as well as the recommended build procedure in creating the UEFI capsule and the LVFS-compatible cabinet file.

../_images/ONELabFirmwarePackagingFlow.jpg

Referring to the above diagram, note that the customer will be required to provide one input into ONELab, the .cab file.

The following subsections will provide guidance on how to build the SystemReady IR / LVFS / ONELab compliant .cab file.

UEFI Capsule Update .cap File Format and Creation

Capsule Creation

The tool used to create signed capsules comes from EDK2 and is called ‘GenerateCapsule’. Although you can use command line arguments to specify various input parameters – e.g the keys used to sign the capsule, the GUIDs used, etc it’s preferable to create a .json file. The following JSON sample is provided.

Replace FwVersion, LowerSupportedVersion, Guid, and Payload with the correct values that are supported in your bootloader. The complete list of descriptors that can be used are in the EDK2 GenerateCapule.py file itself. The below represent the mandatory fields to populate the EFI_FIRMWARE_IMAGE_DESCRIPTOR definition in §23.1.2 of the UEFI Specification.

The file below has two entries as an example. This is hardware specific, but most boards nowadays contain the firmware in a single file format.

sample.json
{
      "Payloads": [
      {
              "FwVersion": "1",
              "Guid": "GUID-0",
              "HardwareInstance": "0",
              "LowestSupportedVersion": "x",
              "MonotonicCount": "0",
              "Payload": "/path/to/Payload-0",
              "UpdateImageIndex": "1",
              "OpenSslSignerPrivateCertFile": "CRT.pem",
              "OpenSslTrustedPublicCertFile": "CRT.pub.pem",
              "OpenSslOtherPublicCertFile":   "CRT.pub.pem"
      },
     {
              "FwVersion": "2.2",
              "Guid": "GUID-N",
              "HardwareInstance": "0",
              "LowestSupportedVersion": "x",
              "MonotonicCount": "0",
              "Payload": "/path/to/Payload-N",
              "UpdateImageIndex": "2",
              "OpenSslSignerPrivateCertFile": "CRT.pem",
              "OpenSslTrustedPublicCertFile": "CRT.pub.pem",
              "OpenSslOtherPublicCertFile":   "CRT.pub.pem"
     }
   ]
}

In the above, for the use cases where a firmware is composed of multiple images, each one will have a unique Guid(ImageTypeID in its EFI_FIRMWARE_IMAGE_DESCRIPTOR). This Guid shall never change for that payload “image” in order to support proper capsule update. Examples of images/components may be OP-TEE, TF-A, FIP etc. If a user has all firmware components packaged in one image, then the entire firmware is updated, but in the cases where a vendor chooses to represent firmware components multiple images, then it’s possible for a vendor to update just one of the images(TF-A for example) and not update any of the other firmware image components.

Note: All Guids are expected to be created in compliance with this chapter of the UEFI specification. The customer must generate their Guids prior to filling in the json file above. A capsule may contain an image not installed on the DUT. In that case, the image is skipped. If the version in the .cap file is identified to be the same one already installed, it is skipped as well. The flow below shows the way ONELab will parse the file once received.

        graph TD
 A[Input .cab File] --> C[Perform Checksum on Files]
 C --> D{For Each Payload Image in the .cap}
 D --> E["Compare Image GUID (UpdateImageTypeId) with Installed Image GUID"]
 E --> F{Does GUID Match AND a newer version}
 F -- Yes --> H[Update Installed Image]
 F -- No --> G[Skip Update]
    

Building the .cap Capsule Update File

Use the GenerateCapsule command to create the capsule as shown below.

git clone --depth 1 https://github.com/tianocore/edk2.git && cd edk2
./BaseTools/BinWrappers/PosixLike/GenerateCapsule -j sample.json -e -o <name>.capsule

Note

U-Boot also provides ‘mkeficapsule’ but at the moment of writing they are not compatible. Since we need the edk2 tool to extract firmware from the capsules as well, users are advised to use the EDK2 tools for now.

.Cab File Format and Creation Instructions

With the UEFI Capsule(s) built per the previous section, the user can now build the Cabinet file.

Building the .cab archive for ONELab

Warning

It was found that Ubuntu 22.04 did not support updating a required package to the latest version. It was required to upgrade to Ubuntu 24.04 to get the fwupd to version 1.9.x or higher.

In order to minimise the amount of effort, ONELab tries to reuse available open-source projects. fwupd is an open-source daemon that aims to make updating firmware on Linux automatic, safe, and reliable. It works seamlessly with LVFS and provides tools to package and distribute firmware images. Those tools already define a set of metadata that ONELab can reuse. More information on the supported metadata can be found here. The tool will create an .xml file and inject it to the .cab archive. Additional information on firmware_packager.py can be found here.

Below is an example of building the .cab file using firmware_packager.py highlighting the parameters required by ONELab.

firmware_packager.py \
     --firmware-name <ONELab Device Type> \
     --release-version 0.x \
     --bin <board name>.capsule \
     --out <name>_capsule.cab \
     --device-guid <device specific GUID> \
     --developer-name <company name> \
     --firmware-summary <ONELab Release Stream> \
     --firmware-description <optional description> \
     --version-format number|double|triplet|quad etc \
     --update-protocol org.uefi.capsule

Several of the parameters above are required by fwupd. Others are optional, and are thus leveraged by ONELab to provide required inputs. A description of the arguments and their ONELab usage is below:

  • firmware-name: Overloaded Field - Contains a valid/registered ONELab Device Type. Required by fwupd.org and by ONELab

  • release-version: represents the release version found in the LVFS metainfo.xml file(referenced above). ONELab will use this release-version as the single version reference in the ONELab Dashboard. Required

  • bin: path to the capsule generated in <insert link to the chapter above> Required

  • out: output name of the .cab archive Required

  • device-guid: GUID of the device this firmware will run on, this must match the output of one of the GUIDs in fwupdmgr get-devices. If a capsule has been created with multiple images, note that each image has its own GUID. This field shall represent the GUID on the first entry of these, and any remaining ones will automatically be resolved by fwupdmgr for the purposes of capsule update. Required

  • developer-name: Company name. Required

  • firmware-summary: This must include the targeted ONELab Release Stream Name. If unique, a new Release Stream will be created in ONELab, if pre-existing, the release-version will be added as a next increment to the existing Release-Stream. Required by ONELab

  • firmware-description: This is a longer description string of the image. It will be displayed in the appropriate ONELab Dashboards. Optional by ONELab

  • version-format: number e.g 1,2,3 etc or double 0.1, 0.2 etc or triple 0.1.1, 0.1.2 etc

  • update-protocol: must be set to org.uefi.capsule. Required

Additional Tooling

This section provides some additional tools that a ONELab user may find helpful when building .cap and .cab files for ONELab. These allow the user to check what has been built.

Reverse a .cab archive for ONELab

Extract files from the .cab archive

$~> cabextract test.cab
Extracting cabinet: test.cab
extracting firmware.bin
extracting firmware.metainfo.xml
All done, no errors.

Reverse the .cap file for ONELab

Dump capsule information

$~ >./BaseTools/BinWrappers/PosixLike/GenerateCapsule firmware.bin --dump-info

========
EFI_CAPSULE_HEADER.CapsuleGuid      = 6DCBD5ED-E82D-4C44-BDA1-7194199AD92A
EFI_CAPSULE_HEADER.HeaderSize       = 00000020
EFI_CAPSULE_HEADER.Flags            = 00000000
 OEM Flags                         = 0000
EFI_CAPSULE_HEADER.CapsuleImageSize = 00289E5D
sizeof (Payload)                    = 00289E3D
--------
EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER.Version             = 00000001
EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER.EmbeddedDriverCount = 00000000
EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER.PayloadItemCount    = 00000002
EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER.ItemOffsetList      =
 0000000000000018
 0000000000030F8B
EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER.Version                = 00000003
EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER.UpdateImageTypeId      = 02F4D760-CFD5-43BD-8E2D-A42ACB33C660
EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER.UpdateImageIndex       = 00000001
EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER.UpdateImageSize        = 00030F43
EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER.UpdateVendorCodeSize   = 00000000
EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER.UpdateHardwareInstance = 0000000000000000
EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER.ImageCapsuleSupport    = 0000000000000001
sizeof (Payload)                                                    = 00030F43
sizeof (VendorCodeBytes)                                            = 00000000
EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER.Version                = 00000003
EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER.UpdateImageTypeId      = 4CE292DA-1DD8-428D-A1C2-77743EF8B96E
EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER.UpdateImageIndex       = 00000002
EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER.UpdateImageSize        = 00258E82
EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER.UpdateVendorCodeSize   = 00000000
EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER.UpdateHardwareInstance = 0000000000000000
EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER.ImageCapsuleSupport    = 0000000000000001
sizeof (Payload)                                                    = 00258E82
sizeof (VendorCodeBytes)                                            = 00000000
--------
EFI_FIRMWARE_IMAGE_AUTHENTICATION.MonotonicCount                = 0000000000000000
EFI_FIRMWARE_IMAGE_AUTHENTICATION.AuthInfo.Hdr.dwLength         = 00000892
EFI_FIRMWARE_IMAGE_AUTHENTICATION.AuthInfo.Hdr.wRevision        = 0200
EFI_FIRMWARE_IMAGE_AUTHENTICATION.AuthInfo.Hdr.wCertificateType = 0EF1
EFI_FIRMWARE_IMAGE_AUTHENTICATION.AuthInfo.CertType             = 4AAFD29D-68DF-49EE-8AA9-347D375665A7
sizeof (EFI_FIRMWARE_IMAGE_AUTHENTICATION.AuthInfo.CertData)    = 0000087A
sizeof (Payload)                                                = 000306A9
--------
No EFI_FIRMWARE_IMAGE_DEP
--------
FMP_PAYLOAD_HEADER.Signature              = 3153534D (MSS1)
FMP_PAYLOAD_HEADER.HeaderSize             = 00000010
FMP_PAYLOAD_HEADER.FwVersion              = 00000001
FMP_PAYLOAD_HEADER.LowestSupportedVersion = 00000001
sizeof (Payload)                          = 00030699
========
--------
EFI_FIRMWARE_IMAGE_AUTHENTICATION.MonotonicCount                = 0000000000000000
EFI_FIRMWARE_IMAGE_AUTHENTICATION.AuthInfo.Hdr.dwLength         = 00000892
EFI_FIRMWARE_IMAGE_AUTHENTICATION.AuthInfo.Hdr.wRevision        = 0200
EFI_FIRMWARE_IMAGE_AUTHENTICATION.AuthInfo.Hdr.wCertificateType = 0EF1
EFI_FIRMWARE_IMAGE_AUTHENTICATION.AuthInfo.CertType             = 4AAFD29D-68DF-49EE-8AA9-347D375665A7
sizeof (EFI_FIRMWARE_IMAGE_AUTHENTICATION.AuthInfo.CertData)    = 0000087A
sizeof (Payload)                                                = 002585E8
--------
No EFI_FIRMWARE_IMAGE_DEP
--------
FMP_PAYLOAD_HEADER.Signature              = 3153534D (MSS1)
FMP_PAYLOAD_HEADER.HeaderSize             = 00000010
FMP_PAYLOAD_HEADER.FwVersion              = 00000001
FMP_PAYLOAD_HEADER.LowestSupportedVersion = 00000001
sizeof (Payload)                          = 002585D8
========

Extract capsule images

$~ >./BaseTools/BinWrappers/PosixLike/GenerateCapsule -d test.capsule -o test

GenerateCapsule: decode warning: can not verify singed payload without cert or pfx file. Index = 1
Error loading file None
80C6972E6D7F0000:error:80000002:system library:file_ctrl:No such file or directory:../crypto/bio/bss_file.c:297:calling fopen(None, r)
80C6972E6D7F0000:error:10080002:BIO routines:file_ctrl:system lib:../crypto/bio/bss_file.c:300:
80C6972E6D7F0000:error:05880020:x509 certificate routines:X509_load_cert_file_ex:BIO lib:../crypto/x509/by_file.c:97:

GenerateCapsule: warning: payload verification failed Index = 1
GenerateCapsule: error: openssl failed.
GenerateCapsule: decode warning: can not verify singed payload without cert or pfx file. Index = 2
Error loading file None
80B6A034B17F0000:error:80000002:system library:file_ctrl:No such file or directory:../crypto/bio/bss_file.c:297:calling fopen(None, r)
80B6A034B17F0000:error:10080002:BIO routines:file_ctrl:system lib:../crypto/bio/bss_file.c:300:
80B6A034B17F0000:error:05880020:x509 certificate routines:X509_load_cert_file_ex:BIO lib:../crypto/x509/by_file.c:97:

GenerateCapsule: warning: payload verification failed Index = 2
GenerateCapsule: error: openssl failed.

Since the capsule must be signed and the files won’t have the private certificate (at the moment of writing the EDK2 tools need both the private and public keypair to authenticate a capsule, although the private part is not needed), some SSL errors will appear. You can safely ignore these. You can also run the tool with -v (verbose) for a more verbose output.

The capsule we tried to extract has 2 payloads (as seen in the example above). After the command you should find 2 files in the folder test.Payload1.bin and test.Payload2.bin

Example capsule and cabinet Build

This section walks through a working example of building a capsule and a cabinet file using the tooling described in the earlier sections.

The steps below are meant to be sequential and represent a psuedo-workflow for the process to build a .cab containing target firmware. Sample scripts and json files will be referenced in the instructions. They are located in a script directory of the repository found here

Note

This example assumes Ubuntu 24.04. If using a different environment, detailed instructions will differ.

Also note that the intent of this example is for a user to be able to successfully set up and build a valid cabinet file. Once confirming this through the examples of this section, you can then take steps to apply to your valid firmwware.

Warning

It was found that Ubuntu 22.04 did not support updating a required package to the latest version. It was required to upgrade to Ubuntu 24.04 to get the fwupd to version 1.9.x or higher.

$ fwupdtool --version
Loading…                 [************************************** ]
compile   org.freedesktop.fwupd         1.9.24
compile   com.hughsie.libxmlb           0.3.18
compile   com.hughsie.libjcat           0.2.0
runtime   org.freedesktop.fwupd-efi     1.4
compile   org.freedesktop.gusb          0.4.8
runtime   com.hughsie.libxmlb           0.3.x
runtime   com.hughsie.libjcat           0.2.0
runtime   org.freedesktop.gusb          0.4.8
runtime   org.freedesktop.fwupd         1.9.24
runtime   org.kernel                    6.8.0-45-generic

Prerequisites:

To prepare for building the capsule and cabinet files, there are a number of packages that need to be set up. The code block below should catch most of them.

sudo apt -y install build-essential uuid-dev iasl git nasm
sudo apt -y install cabextract
sudo apt -y install  gcab
sudo apt -y install p7zip-full p7zip-rar
sudo apt -y install fwupdate
sudo apt -y install uuid-runtime

This example also assumes a python3 environment that may require additional packages to be set up as well.

There are several videos along the way to help you understand the build steps.

First, we’ll start with prerequisites:


Followed by an overview of the steps we’ll be going through:



Step 1: Guid Generation

Each firmware image included in the capsule requires a unique GUID. Once assigned to a firmware image, each GUID remains permanently associated with this image throughout the lifetime of the Release Stream.

Released firmware GUIDs are of course permanently assigned by the project (U-Boot / EDK2). The user will already have these in their UEFI firmware, and these, along with their associated images, can be used as you walk through the process in this example.

Note

For the purpose of completing this example, actual GUIDs purposed for a valid firmware are not used. Instead termporary, but valid GUIDs are created to complete the demo. This as well as the payloads used in this walkthru WILL NOT lead to a working firmware. The command used to create these sample GUIDs was the following.

$ uuidgen
4d46c940-ebc7-4b18-a78a-8af35a52659f

Simply run the above command multiple times to generate as many valid GUIDs as needed. Note that when the user runs the GenerateCapsule script against a customized JSON (sample.json), it checks for valid GUIDs

// Inserted GUID format error into sample.json

$ ./edk2/BaseTools/BinWrappers/PosixLike/GenerateCapsule -j sample.json -e -o Oct16-v3.capsule
GenerateCapsule: error: Guid in payload descriptor has invalid syntax.

Note

For more details regarding the creation of compliant UEFI see the UEFI Specification.

If GUIDs are already assigned to the images that will be part of the capsule, use these.

In either case, make sure the GUIDs you plan to use are avaiblable for Step 3 below.

Step 2: Key Creation

When building the capsule, you will need to include the private certificate (OpenSslSignerPrivateCertFile) and the public certificate (OpenSslTrustedPublicCertFile) used to authenticate the signed capsule during installation. For ONELab, the OpenSslTrustedPublicCertFile is also required to be entered when uploading a new Release Stream.

You can use either actual production keys or a public/private keypair created only for testing on ONELab. A script has been made available that creates the keys needed the sample.json.

Note

Note that UEFI firmware includes the public key. This key must match the key being requested for upload. ONELab uses this key to validate the capsules prior to accepting the cabinet file. If verification fails, an error will be presented to the user. When actual testing begins, the (same) public key baked into the firmware is what is used by the firmware for on-platform verification.

To create test keys, a script(create_pems.py) has been made available here In this example, if the script is run from the working directory, it will place the keys in a subfolder that is already pointed to by the sample.json (./onelab_keys).

Note

Note that when running the create_pems.py script, it will request a password several times. Simply press “enter” each time and don’t enter a password

Make sure to have the keys available to use in step 3

Step 3: Update capsule configuration JSON

Now that the GUID(S) and Keys are available, you can create the sample.json required for building the capsule file.

If desires, a sample.json file is available in the scripts directory for reference. It can be customized for building your specific capsule file.

Starting with the sample.json, fill in the GUID and Keys created in the previous two steps. Also fill in the other requested information such as the path to the payload image and the Image Version.

Step 4: Build Capsule file

With the sample.json built, you’re now ready to build the capsule! For this example, we assumes we’re using the sample.json found in the script directory and shown below.

{
      "Payloads": [
      {
           "FwVersion": "1",
           "Guid": "a38238e4-6737-48e2-ad1a-dd81864bbd69",
           "HardwareInstance": "1",
           "LowestSupportedVersion": "1",
           "MonotonicCount": "0",
           "Payload": "$HOME/dev/onelab/sample_bin/ImageA.bin",
           "UpdateImageIndex": "1",
           "OpenSslSignerPrivateCertFile": "./onelab_keys/CRT.pem",
           "OpenSslTrustedPublicCertFile": "./onelab_keys/CRT.pub.pem",
           "OpenSslOtherPublicCertFile":   "./onelab_keys/CRT.pub.pem"
      },
     {
           "FwVersion": "2",
           "Guid": "22f34803-98c6-4ee6-a912-bd7edb7ee897",
           "HardwareInstance": "0",
           "LowestSupportedVersion": "2",
           "MonotonicCount": "0",
           "Payload": "$HOME/dev/onelab/sample_bin/ImageB.bin",
           "UpdateImageIndex": "2",
           "OpenSslSignerPrivateCertFile": "./onelab_keys/CRT.pem",
           "OpenSslTrustedPublicCertFile": "./onelab_keys/CRT.pub.pem",
           "OpenSslOtherPublicCertFile":   "./onelab_keys/CRT.pub.pem"
     }
   ]
}

The following builds the capsule defined in sample.json and names it ONELab_sample.capsule.

$ cd ~/dev/onelab
~/dev/onelab$ create_pems.py   // creates needed keys and puts them in ./onelab_keys
~/dev/onelab$ git clone --depth 1 https://github.com/tianocore/edk2.git
~/dev/onelab$ ./edk2/BaseTools/BinWrappers/PosixLike/GenerateCapsule -j sample.json -e -o ONELab_sample.capsule

Note

sample.json also assumes a subdirectory for the ImageA.bin and ImageB.bin sample UEFI firmware images are located in the subdirectory named ./sample_bin. The content of these .bins are irrelevant for this example build as they aren’t planned to ever be executed upon. In a fully functional setup, a ONELab user would be expected to replace these examples with the fileset that lead to a valid UEFI SystemReady DT compliant (or close to) firmware upate capsule.

Step 5: Build Cabinet File

Warning

It was found that Ubuntu 22.04 did not support updating a required package to the latest version. It was required to upgrade to Ubuntu 24.04 to get the fwupd to version 1.9.x or higher.

Run the firmware_packager.py script

~/dev/onelab$ git clone https://github.com/fwupd/fwupd.git
~/dev/onelab$ ./fwupd/contrib/firmware_packager/firmware_packager.py \
--firmware-name VALID_DEVICE_TYPE \
--device-guid a38238e4-6737-48e2-ad1a-dd81864bbd69 \
--developer-name Linaro \
--release-version 0.4.1 \
--version-format triplet \
--bin $HOME/dev/onelab/ONELab_sample.capsule \
--out ONELab-sampleV1.cab \
--update-protocol org.uefi.capsule \
--firmware-summary "Dons_test_Release_Stream"  \
--firmware-description "Optional expanded description of this firmware build" \
--release-description "Optional additional description"

Using temp directory /tmp/tmpk7etu1s4
Locating firmware bin
Creating metainfo
Creating cabinet file
Done

~/dev/onelab$

Step 6: Validate cab / capsule files (OPTIONAL)

Using the steps in the Additional Tooling section, check the cab and capsule content for valid content.

~/dev/onelab$ mkdir temp_extract
~/dev/onelab$ cp ONELab-sampleV1.cab temp_extract
~/dev/onelab$ cd temp_extract

~/dev/onelab/temp_extract$ cabextract ONELab-sampleV1.cab
Extracting cabinet: ONELab-sampleV1.cab
extracting firmware.bin
extracting firmware.metainfo.xml

All done, no errors.

~/dev/onelab/temp_extract$ ls
firmware.bin  firmware.metainfo.xml  ONELab-sampleV1.cab

~/dev/onelab/temp_extract$  ../edk2/BaseTools/BinWrappers/PosixLike/GenerateCapsule firmware.bin --dump-info
========
EFI_CAPSULE_HEADER.CapsuleGuid      = 6DCBD5ED-E82D-4C44-BDA1-7194199AD92A
EFI_CAPSULE_HEADER.HeaderSize       = 00000020
EFI_CAPSULE_HEADER.Flags            = 00000000
  OEM Flags                         = 0000
EFI_CAPSULE_HEADER.CapsuleImageSize = 009AA424
sizeof (Payload)                    = 009AA404
--------
EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER.Version             = 00000001
EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER.EmbeddedDriverCount = 00000000
EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER.PayloadItemCount    = 00000002
EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER.ItemOffsetList      =
  0000000000000018
  00000000004D520E
EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER.Version                = 00000003
EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER.UpdateImageTypeId      = A38238E4-6737-48E2-AD1A-DD81864BBD69
EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER.UpdateImageIndex       = 00000001
EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER.UpdateImageSize        = 004D51C6
EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER.UpdateVendorCodeSize   = 00000000
EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER.UpdateHardwareInstance = 0000000000000001
EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER.ImageCapsuleSupport    = 0000000000000001
sizeof (Payload)                                                    = 004D51C6
sizeof (VendorCodeBytes)                                            = 00000000
EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER.Version                = 00000003
EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER.UpdateImageTypeId      = 22F34803-98C6-4EE6-A912-BD7EDB7EE897
EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER.UpdateImageIndex       = 00000002
EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER.UpdateImageSize        = 004D51C6
EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER.UpdateVendorCodeSize   = 00000000
EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER.UpdateHardwareInstance = 0000000000000000
EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER.ImageCapsuleSupport    = 0000000000000001
sizeof (Payload)                                                    = 004D51C6
sizeof (VendorCodeBytes)                                            = 00000000
--------
EFI_FIRMWARE_IMAGE_AUTHENTICATION.MonotonicCount                = 0000000000000000
EFI_FIRMWARE_IMAGE_AUTHENTICATION.AuthInfo.Hdr.dwLength         = 00000942
EFI_FIRMWARE_IMAGE_AUTHENTICATION.AuthInfo.Hdr.wRevision        = 0200
EFI_FIRMWARE_IMAGE_AUTHENTICATION.AuthInfo.Hdr.wCertificateType = 0EF1
EFI_FIRMWARE_IMAGE_AUTHENTICATION.AuthInfo.CertType             = 4AAFD29D-68DF-49EE-8AA9-347D375665A7
sizeof (EFI_FIRMWARE_IMAGE_AUTHENTICATION.AuthInfo.CertData)    = 0000092A
sizeof (Payload)                                                = 004D487C
--------
No EFI_FIRMWARE_IMAGE_DEP
--------
FMP_PAYLOAD_HEADER.Signature              = 3153534D (MSS1)
FMP_PAYLOAD_HEADER.HeaderSize             = 00000010
FMP_PAYLOAD_HEADER.FwVersion              = 00000001
FMP_PAYLOAD_HEADER.LowestSupportedVersion = 00000001
sizeof (Payload)                          = 004D486C
========
--------
EFI_FIRMWARE_IMAGE_AUTHENTICATION.MonotonicCount                = 0000000000000000
EFI_FIRMWARE_IMAGE_AUTHENTICATION.AuthInfo.Hdr.dwLength         = 00000942
EFI_FIRMWARE_IMAGE_AUTHENTICATION.AuthInfo.Hdr.wRevision        = 0200
EFI_FIRMWARE_IMAGE_AUTHENTICATION.AuthInfo.Hdr.wCertificateType = 0EF1
EFI_FIRMWARE_IMAGE_AUTHENTICATION.AuthInfo.CertType             = 4AAFD29D-68DF-49EE-8AA9-347D375665A7
sizeof (EFI_FIRMWARE_IMAGE_AUTHENTICATION.AuthInfo.CertData)    = 0000092A
sizeof (Payload)                                                = 004D487C
--------
No EFI_FIRMWARE_IMAGE_DEP
--------
FMP_PAYLOAD_HEADER.Signature              = 3153534D (MSS1)
FMP_PAYLOAD_HEADER.HeaderSize             = 00000010
FMP_PAYLOAD_HEADER.FwVersion              = 00000002
FMP_PAYLOAD_HEADER.LowestSupportedVersion = 00000002
sizeof (Payload)                          = 004D486C
========

/** CONFIRM in the above that the UpdateImageTypeID contains the GUIDs for each image in the sample.json

~/dev/onelab/temp_extract$ ls
firmware.bin  firmware.metainfo.xml  ONELab-testv2.cab

~/dev/onelab/temp_extract$ cat firmware.metainfo.xml
<?xml version="1.0" encoding="UTF-8"?>
<component type="firmware">
  <id>org.Linaro.guida38238e4</id>
  <name>VALID_DEVICE_TYPE</name>
  <summary>Dons_test_Release_Stream</summary>
  <description>
    Optional expanded description of this firmware build
  </description>
  <provides>
    <firmware type="flashed">a38238e4-6737-48e2-ad1a-dd81864bbd69</firmware>
  </provides>
  <url type="homepage">None</url>
  <metadata_license>CC0-1.0</metadata_license>
  <project_license>proprietary</project_license>
  <updatecontact>None</updatecontact>
  <developer_name>Linaro</developer_name>
  <releases>
    <release version="0.4.1" timestamp="1729212497.3263595">
      <description>
        Optional additional description
      </description>

Using ONELab Cabinet Builder

While the previous sections are helpful to understand the individual steps to build you ONELab cabinet file, there may be users that would prefer to simply use a prebuilt virtual environment. ONELab team created a Docker image with all aforementioned dependencies pre-installed along with a single command in that environment to create you .cab. This section describes how to do so.

Before diving into the Docker command below, there are a few required files that need to exist:

  • payloads.json: This follows the exact same format as described above.

  • my-firmware.bin and my-firmware2.bin: These are simple testing files to show off creating cabinet for firmware including more than one payload.

  • CRT.pem and CRT.pub.pem: These are the certificate files.

Make sure all necessary files are within your current directory tree.

# Create cabinet file in a single command
$ docker \
      run \
      -v <current absolute directory path>:/workspace \
      -it artifacts.codelinaro.org/onelab-tools/cabinet-builder \
      create-firmware-package \
          --developer-name "Charles Oliveira" \
          --target-device MyDevice \
          --target-device-guid 11111111-2222-3333-4444-555555555555 \
          --firmware-name "My Firmware in a Cabinet" \
          --release-version "0.0.1" \
          --release-description "First release" \
          --payloads-json payloads.json
[INFO] Working on temp dir: /tmp/tmpt_tk02gx
[INFO] Identified 2 images in payloads.json: my-firmware.bin, my-firmware2.bin
[INFO] Running ['/tools/edk2/BaseTools/BinWrappers/PosixLike/GenerateCapsule', '-j', '/tmp/tmpt_tk02gx/payloads.json', '-e', '-o', '/tmp/tmpt_tk02gx/firmware.capsule', '--verbose']
[INFO] Running ['python3', '/tools/firmware_packager.py', '--developer-name', 'Charles Oliveira', '--firmware-name', 'MyDevice', '--firmware-summary', 'My Firmware in a Cabinet', '--release-version', '0.0.1', '--bin', '/tmp/tmpt_tk02gx/firmware.capsule', '--out', '/workspace/my-firmware-in-a-cabinet.0.0.1.cab', '--device-guid', '11111111-2222-3333-4444-555555555555', '--version-format', 'triplet', '--update-protocol', 'org.uefi.capsule', '--release-description', 'First release']
[INFO] Cabinet file generated: /workspace/my-firmware-in-a-cabinet.0.0.1.cab

The final result is my-firmware-in-a-cabinet.0.0.1.cab file which can be uploaded to ONELab.

The same Docker image can generate GUIDs and certificate files.

# Generate a GUID
$ docker run artifacts.codelinaro.org/onelab-tools/cabinet-builder generate-guid
b9c8e0e8-b164-4ff7-8191-35a18eb63dfd

# Generate certificate files
$ docker run -v <current absolute directory path>:/workspace -it artifacts.codelinaro.org/onelab-tools/cabinet-builder generate-pems
Enter Export Password:
Verifying - Enter Export Password:
Enter Import Password:

Files generated:
CRT.pem     (private)
CRT.pub.pem (public)

Note that CRT.pem and CRT.pub.pem are generated as mentioned above, via the create_pems.py script.