Skip to content

Add UEFI FMP capsule generation-related recipes#1751

Merged
ricardosalveti merged 7 commits intoqualcomm-linux:masterfrom
igoropaniuk:feat/uefi-fmp-capsule-generation
Apr 16, 2026
Merged

Add UEFI FMP capsule generation-related recipes#1751
ricardosalveti merged 7 commits intoqualcomm-linux:masterfrom
igoropaniuk:feat/uefi-fmp-capsule-generation

Conversation

@igoropaniuk
Copy link
Copy Markdown
Contributor

@igoropaniuk igoropaniuk commented Mar 12, 2026

Introduces edk2-basetools-native to build the required EDK2 host tools (GenFfs, GenFv) and firmware-qcom-capsule which fetches capsule scripts from cbsp-boot-utilities, consumes boot binaries from virtual/bootbins, and produces a signed FMP capsule. The capsule is wired into the qcomflash image via a new QCOM_CAPSULE_FIRMWARE variable following the same pattern as QCOM_BOOT_FIRMWARE.

Test PKI keys are provided vi for development and CI; production builds should override CAPSULE_ROOT_CER, CAPSULE_CERT_PEM, CAPSULE_ROOT_PUB, and CAPSULE_SUB_PUB with keys from a secure source.

@igoropaniuk igoropaniuk changed the title [RFC] Add UEFI FMP capsule generation recipe for Qualcomm platforms (qcm6490, qcs9100, qcs8300, qcs615). [RFC] Add UEFI FMP capsule generation recipe Mar 12, 2026
@igoropaniuk igoropaniuk force-pushed the feat/uefi-fmp-capsule-generation branch from e3d1784 to 8910cf6 Compare March 12, 2026 18:01
Comment thread recipes-devtools/edk2-basetools/edk2-basetools-native_1.0.bb Outdated
Comment thread recipes-firmware/firmware/firmware-qcom-capsule_1.0.bb Outdated
Comment thread recipes-firmware/firmware/firmware-qcom-capsule_1.0.bb Outdated
Comment thread recipes-firmware/firmware/firmware-qcom-capsule_1.0.bb Outdated
Comment thread ci/test-keys/QcFMPCert.pem
Comment thread classes-recipe/image_types_qcom.bbclass
@ricardosalveti
Copy link
Copy Markdown
Contributor

We should also coordinate with the debian efforts here (@lool / @basak-qcom / @gagath), since the generation and usage should be similar.

Copy link
Copy Markdown
Contributor

@quaresmajose quaresmajose left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the use of some recipes from meta-arm we should move the news ones who depend on them to dynamic layers bucause meta-arm layer is optional.

Comment thread recipes-firmware/firmware/firmware-qcom-capsule_1.0.bb Outdated
@igoropaniuk igoropaniuk force-pushed the feat/uefi-fmp-capsule-generation branch from 8910cf6 to bf4d648 Compare March 16, 2026 13:02
@igoropaniuk igoropaniuk requested a review from mwasilew as a code owner March 16, 2026 13:02
@igoropaniuk igoropaniuk force-pushed the feat/uefi-fmp-capsule-generation branch 2 times, most recently from 19eff2f to 7250f77 Compare March 16, 2026 13:34
@igoropaniuk
Copy link
Copy Markdown
Contributor Author

I've addressed most of the comments; please take another look.
@ricardosalveti @quaresmajose

With the use of some recipes from meta-arm we should move the news ones who depend on them to dynamic layers bucause meta-arm layer is optional.

I've tried to address it in a bit different way (not sure if it might be consdered as correct approach, but CI pipelines don't fail anymore) in:

commit f531d46e58b5e1ca529fc07d1ad239c1b36e3f10
Author: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
Date:   Mon Mar 16 14:42:19 2026 +0100

    firmware-qcom-capsule: skip when meta-arm layer is absent
    
    edk2-basetools-native (GenFfs, GenFv, GenerateCapsule.py) is only
    provided by meta-arm.  Without it capsule assembly cannot complete.
    
    Move the edk2-basetools-native DEPENDS out of cbsp-boot-utilities-native
    into a dynamic-layers bbappend so it is only pulled in when meta-arm is
    active.  Add a python() guard to firmware-qcom-capsule that raises
    SkipRecipe when meta-arm is not in BBFILE_COLLECTIONS, giving a clear
    error instead of an unresolvable dependency chain.
    
    Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>

@igoropaniuk igoropaniuk changed the title [RFC] Add UEFI FMP capsule generation recipe Add UEFI FMP capsule generation recipe Mar 16, 2026
@igoropaniuk igoropaniuk changed the title Add UEFI FMP capsule generation recipe Add UEFI FMP capsule generation recipes Mar 16, 2026
@igoropaniuk igoropaniuk changed the title Add UEFI FMP capsule generation recipes Add UEFI FMP capsule generation-related recipes Mar 16, 2026
Comment thread classes-recipe/qcom-capsule.bbclass Outdated
Comment thread ci/capsule.yml
Comment thread conf/machine/include/qcom-common.inc Outdated
…toolkit

Add a new native recipe that fetches cbsp-boot-utilities [1] and installs the
uefi_capsule_generation/ Python scripts to the native sysroot under
${datadir}/cbsp-boot-utilities/.

[1] https://github.com/quic/cbsp-boot-utilities
Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
@igoropaniuk igoropaniuk force-pushed the feat/uefi-fmp-capsule-generation branch from bfcb106 to b1b261e Compare April 15, 2026 19:05
Comment thread ci/capsule.yml
@igoropaniuk igoropaniuk force-pushed the feat/uefi-fmp-capsule-generation branch from b1b261e to 9d578b9 Compare April 15, 2026 19:55
@igoropaniuk igoropaniuk force-pushed the feat/uefi-fmp-capsule-generation branch from 9d578b9 to 01e1bd9 Compare April 15, 2026 20:09
@ricardosalveti
Copy link
Copy Markdown
Contributor

Can you add one extra entry at https://github.com/qualcomm-linux/meta-qcom/blob/master/.github/workflows/build-yocto.yml#L278 so we can build-test this in our CI as well? Fine to build for just one machine for now (and nodistro is probably enough).

@igoropaniuk
Copy link
Copy Markdown
Contributor Author

@ricardosalveti done!

ricardosalveti
ricardosalveti previously approved these changes Apr 15, 2026
quaresmajose
quaresmajose previously approved these changes Apr 15, 2026
Copy link
Copy Markdown
Contributor

@quaresmajose quaresmajose left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know if my suggestions will work, but I think they simplified things and removed some inline Python that's always heavy for Bitbake to parse.

Comment thread conf/layer.conf Outdated
Comment thread classes-recipe/image_types_qcom.bbclass Outdated
Comment thread conf/machine/include/qcom-common.inc Outdated
@github-actions
Copy link
Copy Markdown

Test run workflow

Test jobs for commit 02fa764

Test dragonboard-820c qcs615-adp-air qcs6490 qcs8300 qcs9100 qcs9100-rb8 qrb2210-rb1
AudioRecord 🚫 pass pass pass pass 🚫 pass
BT_FW_KMD_Service 🚫 pass pass pass pass pass fail
BT_ON_OFF 🚫 pass pass pass pass pass pass
BT_SCAN 🚫 pass fail pass pass pass pass
CPUFreq_Validation 🚫 pass pass pass pass pass pass
DSP_AudioPD 🚫 pass pass pass pass pass pass
Ethernet 🚫 pass 🚫 pass pass pass ⚠️ skip
Interrupts 🚫 pass pass pass pass pass pass
KMSCube 🚫 ⚠️ skip ⚠️ skip ⚠️ skip ⚠️ skip pass pass
OpenCV 🚫 pass pass pass pass pass pass
WiFi_Firmware_Driver 🚫 pass pass pass pass pass pass
WiFi_OnOff 🚫 pass pass pass pass pass pass
adsp_remoteproc 🚫 pass pass pass pass pass pass
boot pass pass pass pass pass pass pass
cdsp_remoteproc 🚫 pass pass pass pass pass ⚠️ skip
core_auth 🚫 pass pass pass pass pass pass
fastrpc_test 🚫 pass pass pass pass pass 🚫
hotplug 🚫 pass pass pass pass pass pass
irq 🚫 pass pass pass pass pass pass
weston-simple-egl 🚫 ⚠️ skip ⚠️ skip ⚠️ skip ⚠️ skip pass pass

All jobs summary

Job ID Device State Health
179112 qrb2210-rb1 Finished Complete
179144 qcs6490 Finished Complete
179117 qcs6490 Finished Complete
179095 qrb2210-rb1 Finished Complete
179101 qcs9100-rb8 Finished Complete
179118 qcs9100-rb8 Finished Complete
179123 qcs9100-rb8 Finished Complete
179157 qcs8300 Finished Complete
179138 qcs615-adp-air Finished Complete
179105 qcs9100 Finished Complete
179108 dragonboard-820c Finished Complete
179141 qcs8300 Finished Complete
179096 qcs6490 Finished Complete
179152 qcs9100-rb8 Finished Complete
179099 qcs8300 Finished Complete
179143 qcs9100-rb8 Finished Complete
179134 qrb2210-rb1 Finished Complete
179137 qcs6490 Finished Complete
179146 qcs6490 Finished Complete
179131 qcs8300 Finished Complete
179114 qcs9100-rb8 Finished Complete
179147 qcs9100 Finished Complete
179103 dragonboard-820c Finished Complete
179153 qrb2210-rb1 Finished Complete
179127 qrb2210-rb1 Finished Complete
179097 qcs9100 Finished Complete
179156 qcs615-adp-air Finished Complete
179125 qcs9100-rb8 Finished Complete
179135 qcs9100-rb8 Finished Complete
179132 qcs9100-rb8 Finished Complete
179113 qrb2210-rb1 Finished Complete
179121 qcs6490 Finished Complete
179104 qcs615-adp-air Finished Complete
179124 qcs9100 Finished Complete
179150 qcs6490 Finished Complete
179122 qcs9100 Finished Complete
179130 qcs615-adp-air Finished Complete
179145 qrb2210-rb1 Finished Complete
179098 qcs8300 Finished Complete
179102 qcs6490 Finished Complete
179149 qcs615-adp-air Finished Incomplete
179120 qcs615-adp-air Finished Complete
179129 qcs615-adp-air Finished Complete
179133 qcs9100 Finished Complete
179140 qcs615-adp-air Finished Complete
179116 dragonboard-820c Finished Complete
179100 qcs8300 Finished Complete
179107 dragonboard-820c Finished Complete
179126 qcs9100 Finished Complete
179151 qrb2210-rb1 Finished Complete
179128 qcs8300 Finished Complete
179142 qrb2210-rb1 Finished Complete
179115 qcs8300 Finished Complete
179111 qcs615-adp-air Finished Complete
179106 qcs9100 Finished Complete
179110 qcs9100-rb8 Finished Complete
179139 qcs6490 Finished Complete
179148 qcs6490 Finished Complete
179155 qcs9100 Finished Complete
179109 qrb2210-rb1 Finished Complete
179158 qcs8300 Finished Complete
179136 qcs8300 Finished Complete
179119 qcs615-adp-air Finished Complete
179154 qcs9100 Finished Complete

Comment thread .github/workflows/build-yocto.yml
Add test keys to ci/test-keys/ so they are clearly scoped to CI/development
use and not accidentally treated as a default for production builds.

Add ci/capsule-test-keys.yml, a kas overlay that sets CAPSULE_ROOT_CER,
CAPSULE_CERT_PEM, CAPSULE_ROOT_PUB and CAPSULE_SUB_PUB to the test keys.
Keys are located via META_QCOM_DIR (added to conf/layer.conf), which
captures the layer root at parse time and is independent of the kas
project-directory layout.

The test keys under ci/test-keys/ were generated with the following
procedure:

export FMP_ROOT_CERT_SUBJECT="/CN=OEM Root CA/O=FMP/OU=OEM Key/L=San Diego/ST=California/C=US"
export FMP_CA_CERT_SUBJECT="/CN=OEM Intermediate CA/O=FMP/OU=OEM Key/L=San Diego/ST=California/C=US"
export FMP_USER_CERT_SUBJECT="/CN=OEM User/O=FMP/OU=OEM Key/L=San Diego/ST=California/C=US"
export FMP_KEY_PASSWORD="testpassword"

mkdir -p demoCA
mkdir -p demoCA/newcerts
touch demoCA/index.txt
echo 01 > demoCA/serial

cp ${REP_ROOT}/.github/opensslroot.cfg ./

dd if=/dev/urandom of=randfile bs=256 count=1 > /dev/null 2>&1

openssl genrsa -aes256 -passout "pass:${FMP_KEY_PASSWORD}" -out QcFMPRoot.key 2048
openssl req -new -x509 -config opensslroot.cfg -subj "${FMP_ROOT_CERT_SUBJECT}" -days 3650 \
  -passin "pass:${FMP_KEY_PASSWORD}" -key QcFMPRoot.key -out QcFMPRoot.crt
openssl x509 -in QcFMPRoot.crt -out QcFMPRoot.cer -outform DER
openssl x509 -inform DER -in QcFMPRoot.cer -outform PEM -out QcFMPRoot.pub.pem

openssl genrsa -aes256 -passout "pass:${FMP_KEY_PASSWORD}" -out QcFMPSub.key 2048
openssl req -new -config opensslroot.cfg -subj "${FMP_CA_CERT_SUBJECT}" \
    -passin "pass:${FMP_KEY_PASSWORD}" -key QcFMPSub.key -out QcFMPSub.csr
openssl ca -config opensslroot.cfg -extensions v3_ca -batch \
    -in QcFMPSub.csr -days 3650 -out QcFMPSub.crt -cert QcFMPRoot.crt \
    -passin "pass:${FMP_KEY_PASSWORD}" -keyfile QcFMPRoot.key
openssl x509 -in QcFMPSub.crt -out QcFMPSub.cer -outform DER
openssl x509 -inform DER -in QcFMPSub.cer -outform PEM -out QcFMPSub.pub.pem

openssl genrsa -aes256 -passout "pass:${FMP_KEY_PASSWORD}" -out QcFMPCert.key 2048
openssl req -new -config opensslroot.cfg -subj "${FMP_USER_CERT_SUBJECT}" \
    -passin "pass:${FMP_KEY_PASSWORD}" -key QcFMPCert.key -out QcFMPCert.csr
openssl ca -config opensslroot.cfg -batch -in QcFMPCert.csr -days 3650 \
    -out QcFMPCert.crt -cert QcFMPSub.crt -passin "pass:${FMP_KEY_PASSWORD}" -keyfile QcFMPSub.key
openssl x509 -in QcFMPCert.crt -out QcFMPCert.cer -outform DER
openssl x509 -inform DER -in QcFMPCert.cer -outform PEM -out QcFMPCert.pub.pem

openssl pkcs12 -export -passout "pass:${FMP_KEY_PASSWORD}" -out QcFMPCert.pfx \
    -passin "pass:${FMP_KEY_PASSWORD}" -inkey QcFMPCert.key -in QcFMPCert.crt
openssl pkcs12 -passin "pass:${FMP_KEY_PASSWORD}" -in QcFMPCert.pfx -nodes \
    -out QcFMPCert.pem

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
Add a qcom-capsule class and a firmware-qcom-capsule reference
recipe that automates UEFI FMP capsule creation for Qualcomm platforms,
replacing the manual workflow described in the cbsp-boot-utilities README.md.

The qcom-capsule class carries all build logic and configurable variables
so that customers can produce their own capsule recipe by simply
inheriting the class and setting the required variables.

All Python capsule-generation scripts are consumed from the native sysroot
via cbsp-boot-utilities-native (${STAGING_DATADIR_NATIVE}/cbsp-boot-utilities/).
EDK2 host tools (GenFfs, GenFv, GenerateCapsule.py) are consumed from
edk2-basetools-native. Neither the class nor the recipe has source of
its own.

The class:
- Consumes pre-built NHLOS firmware images from QCOM_BOOT_FIRMWARE
  via do_compile[depends] on do_deploy
- Auto-detects the post-DDR DTB for XBLConfig cert injection by parsing
  the xblconfig_parser.py dump output (XBLCONFIG_DTB overrides when set
  explicitly); the cert-patched binary is deployed as xbl_config-patched.elf
- Requires OEM PKI material (CAPSULE_ROOT_CER, CAPSULE_CERT_PEM,
  CAPSULE_ROOT_PUB, CAPSULE_SUB_PUB) to be set explicitly; the build
  fails fast with bbfatal if they are unset
- Deploys the resulting FMP capsule as <pn>.cap

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
Extend image_types_qcom.bbclass so that when QCOM_CAPSULE_FIRMWARE is
set, the resulting capsule is:
- depended on via do_image_qcomflash[depends] += '...:do_deploy'
- copied into the qcomflash tarball alongside the other firmware images
- pulled into the rootfs via IMAGE_INSTALL:append so the capsule is also
  available on-device (e.g. for fwupd / LVFS delivery)

When QCOM_CAPSULE_FIRMWARE is empty (the default), behaviour is
unchanged.

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
Use a runtime virtual-package indirection so customers can swap in
their own capsule recipe without touching qcom-common.inc.

Recommend the capsule package into the rootfs from qcom-common.inc
via MACHINE_EXTRA_RRECOMMENDS, gated on the VIRTUAL-RUNTIME provider
being set (no-op when capsule generation is disabled).

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
Add ci/capsule.yml, a kas overlay that opts-in to UEFI FMP capsule
generation by selecting firmware-qcom-capsule as the
virtual/qcom-capsule-firmware provider. Keeping this out of the machine
include files allows CI configurations to build capsules explicitly
rather than unconditionally on every machine.

Usage:
  kas build ci/base.yml:ci/<machine>.yml:ci/capsule.yml
  kas build ci/base.yml:ci/<machine>.yml:ci/capsule.yml:ci/capsule-test-keys.yml

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
Add a rb3gen2-core-kit matrix entry that builds with meta-arm,
ci/capsule.yml and ci/capsule-test-keys.yml so the capsule generation
path is exercised on every PR. nodistro is used since capsule build
does not depend on distro features.

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
@igoropaniuk igoropaniuk dismissed stale reviews from quaresmajose and ricardosalveti via 0799447 April 16, 2026 10:46
@igoropaniuk igoropaniuk force-pushed the feat/uefi-fmp-capsule-generation branch from 02fa764 to 0799447 Compare April 16, 2026 10:46
@github-actions
Copy link
Copy Markdown

Test run workflow

Test jobs for commit 0799447

Test dragonboard-820c qcs615-adp-air qcs6490 qcs8300 qcs9100 qcs9100-rb8 qrb2210-rb1
boot pass pass pass pass pass pass pass

All jobs summary

Job ID Device State Health
179732 qcs9100-rb8 Finished Complete
179737 qcs615-adp-air Finished Complete
179742 qcs6490 Finished Complete
179748 qcs615-adp-air Finished Complete
179735 dragonboard-820c Finished Complete
179731 dragonboard-820c Finished Complete
179747 qcs9100 Finished Complete
179726 dragonboard-820c Finished Complete
179729 qcs8300 Finished Complete
179744 qcs6490 Finished Complete
179749 qcs6490 Finished Complete
179725 dragonboard-820c Finished Complete
179733 qcs9100-rb8 Finished Complete
179738 qcs9100-rb8 Finished Complete
179728 qrb2210-rb1 Finished Complete
179734 qrb2210-rb1 Finished Complete
179736 qcs9100 Finished Complete
179730 qcs6490 Finished Complete
179739 qrb2210-rb1 Finished Complete
179743 qcs8300 Finished Complete
179722 qcs8300 Finished Complete
179723 qcs615-adp-air Finished Complete
179724 qcs8300 Finished Complete
179746 qcs9100 Finished Complete
179741 qrb2210-rb1 Finished Complete
179727 qcs9100-rb8 Finished Complete
179745 qcs9100 Finished Complete

@ricardosalveti ricardosalveti merged commit 0f597b4 into qualcomm-linux:master Apr 16, 2026
245 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants