From 50868242f0c1d8bff722a0ab5e008434947e31d1 Mon Sep 17 00:00:00 2001 From: Igor Opaniuk Date: Tue, 23 Jun 2026 20:20:58 +0200 Subject: [PATCH 01/10] security-profiles-native: package Qualcomm Security Profile XMLs Add a -native recipe that fetches the per-chipset Security Profile XML files from https://github.com/qualcomm/security-profiles and stages them under ${datadir}/qcom-security-profiles/ so they can be resolved by classes-recipe/qcom-firmware-sign.bbclass via the bare filename in QCOM_FIRMWARE_SIGN_SECPROFILE. The upstream repository is currently private; fetching it requires a GitHub credential that has SAML-SSO authorised the qualcomm organisation (gh auth login + browser flow). This restriction is expected to lift soon, at which point this note becomes obsolete. License is BSD-3-Clause-Clear, anchored to the upstream LICENSE.txt. Signed-off-by: Igor Opaniuk --- .../security-profiles-native_git.bb | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 recipes-devtools/security-profiles/security-profiles-native_git.bb diff --git a/recipes-devtools/security-profiles/security-profiles-native_git.bb b/recipes-devtools/security-profiles/security-profiles-native_git.bb new file mode 100644 index 000000000..1d256f9cf --- /dev/null +++ b/recipes-devtools/security-profiles/security-profiles-native_git.bb @@ -0,0 +1,22 @@ +SUMMARY = "Qualcomm Security Profiles" +DESCRIPTION = "Per-chipset Security Profile XML files consumed by Sectools" +HOMEPAGE = "https://github.com/qualcomm/security-profiles" +LICENSE = "BSD-3-Clause-Clear" +LIC_FILES_CHKSUM = "file://LICENSE.txt;md5=2998c54c288b081076c9af987bdf4838" + +SRC_URI = "git://github.com/qualcomm/security-profiles.git;protocol=https;branch=main" +SRCREV = "122f1917af2b428880a7607ed705bb46bec66f5b" + +inherit native + +do_configure[noexec] = "1" +do_compile[noexec] = "1" + +# Install the XML profiles into a well-known native datadir location +# so qcom-firmware-sign.bbclass can resolve them by filename via +# QCOM_FIRMWARE_SIGN_SECPROFILE. +do_install() { + install -d "${D}${datadir}/qcom-security-profiles" + install -m 0644 "${S}"/*_security_profile.xml \ + "${D}${datadir}/qcom-security-profiles/" +} \ No newline at end of file From f0325560b90e3acfc4e59052ef43648bfa1a087a Mon Sep 17 00:00:00 2001 From: Igor Opaniuk Date: Tue, 23 Jun 2026 20:21:09 +0200 Subject: [PATCH 02/10] sectools-native: package Qualcomm Security Tools v2 binary Add a -native recipe that fetches Qualcomm Sectools v2 from the Software Center (1.48.0) and stages the per-platform `sectools` binary under ${datadir}/sectools/, with a symlink in ${bindir} so consumers can invoke `sectools` from PATH irrespective of build-host architecture. The build-host architecture override selects the matching tree from the upstream zip (Linux x86_64 by default; Linux_aarch64 on aarch64 hosts). Other platforms shipped in the zip (macOS, Windows) are intentionally not installed. The Software Center download is anonymously accessible (verified by plain wget GET against the published 1.48.0 URL); BitBake's HTTP fetcher works without any pre-staging step. The SHA256 is pinned to the released 1.48.0 archive. LIC_FILES_CHKSUM is anchored to the in-archive CHANGES.txt because the zip ships no LICENSE file; LICENSE.qcom-2 is provided via the layer-local licenses directory through LICENSE_PATH (see conf/layer.conf). Signed-off-by: Igor Opaniuk --- .../sectools/sectools-native_1.48.0.bb | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 recipes-devtools/sectools/sectools-native_1.48.0.bb diff --git a/recipes-devtools/sectools/sectools-native_1.48.0.bb b/recipes-devtools/sectools/sectools-native_1.48.0.bb new file mode 100644 index 000000000..6d8a3d9d4 --- /dev/null +++ b/recipes-devtools/sectools/sectools-native_1.48.0.bb @@ -0,0 +1,47 @@ +SUMMARY = "Qualcomm Sectools v2 -- image signing / verification tool" +DESCRIPTION = "Qualcomm Security Tools v2: the host \ +binary used to sign, verify and inspect Qualcomm firmware images (XBL, \ +TZ, modem, etc.) according to a per-chipset Security Profile XML" +HOMEPAGE = "https://softwarecenter.qualcomm.com/catalog/item/Qualcomm_Security_Tools" +LICENSE = "LICENSE.qcom-2" + +LIC_FILES_CHKSUM = "file://${UNPACKDIR}/${ZIP_TOPDIR}/CHANGES.txt;md5=d2a0bb01dcd8befe660b832fbbe05900" +ZIP_TOPDIR = "1.48" + +SRC_URI = "https://softwarecenter.qualcomm.com/api/download/software/tools/Qualcomm_Security_Tools/All/${PV}/${ZIP_TOPDIR}.zip;name=sectools-zip;downloadfilename=qcom-sectools-${PV}.zip" +SRC_URI[sectools-zip.sha256sum] = "d89773bbfcc9c80c871b628bd2e766460a876277661ed6634d57590b7fd80fba" + +S = "${UNPACKDIR}/${ZIP_TOPDIR}" + +INHIBIT_DEFAULT_DEPS = "1" + +inherit native + +do_configure[noexec] = "1" +do_compile[noexec] = "1" + +# Pick the per-platform sectools binary that matches the build host. +SECTOOLS_PLATFORM_DIR = "${@'Linux_aarch64' if d.getVar('BUILD_ARCH') == 'aarch64' else 'Linux'}" + +# Stage the per-platform sectools binary under ${datadir}/sectools/ and +# drop a thin symlink into ${bindir} so consumers can invoke `sectools` +# from PATH regardless of which subdir was selected. +do_install() { + install -d "${D}${datadir}/sectools/${SECTOOLS_PLATFORM_DIR}" + install -m 0755 "${S}/${SECTOOLS_PLATFORM_DIR}/sectools" \ + "${D}${datadir}/sectools/${SECTOOLS_PLATFORM_DIR}/sectools" + + install -d "${D}${bindir}" + ln -sf "../share/sectools/${SECTOOLS_PLATFORM_DIR}/sectools" \ + "${D}${bindir}/sectools" + + install -m 0644 "${S}/CHANGES.txt" "${D}${datadir}/sectools/CHANGES.txt" +} + +do_unpack[postfuncs] += "sectools_chmod_unpacked" +sectools_chmod_unpacked() { + chmod -R u+w "${UNPACKDIR}" +} + +FILES:${PN} += "${datadir}/sectools" +INSANE_SKIP:${PN} += "already-stripped" \ No newline at end of file From 446e1ec496573b9ad3e11f6d7d19eed08908ad90 Mon Sep 17 00:00:00 2001 From: Igor Opaniuk Date: Tue, 23 Jun 2026 20:21:27 +0200 Subject: [PATCH 03/10] qcom-firmware-sign: bbclass for sectools-driven firmware signing Add a build-time signing class that wraps sectools-native and security-profiles-native, modelled on the meta-partner approach in https://github.com/foundriesio/meta-partner/pull/12. The class adds a do_qcom_firmware_sign task between do_install and do_deploy. When QCOM_FIRMWARE_SIGN_ENABLE is "0" (the default) the task is a no-op and no extra DEPENDS are pulled in -- consumers can inherit unconditionally without affecting non-signing builds. When enabled, do_qcom_firmware_sign: * walks ${B}/firmware-to-sign for *.mbn / *.elf files (or whatever the recipe overrides the default with); * looks each filename up in QCOM_FIRMWARE_SIGN_IMAGE_ID_MAP to obtain the sectools image-id label; * invokes `sectools secure-image --sign ...` with the OEM root / attestation CA cert + key from QCOM_FIRMWARE_SIGN_KEY_DIR and the per-SoC profile resolved from QCOM_FIRMWARE_SIGN_SECPROFILE; * verifies the resulting blob's root hash against the OEM hash file. Bare filenames in QCOM_FIRMWARE_SIGN_SECPROFILE resolve against the native datadir staged by security-profiles-native; absolute paths are passed through unchanged so OEM-specific profiles can override the in-tree set. Fuse-binding identifiers (QCOM_FUSE_OEM_HW_ID, QCOM_FUSE_OEM_PRODUCT_ID, QCOM_FUSE_SEC_KEY_DERIVATION_KEY) are declared here so they participate in task signature hashing; the actual fuse-blower invocation lives in downstream consumers (and the qcom-sec-tools shell pipeline). Image-id lookup uses a space-separated `filename:imageid` map (also overridable from a recipe) to keep the SoC-specific knowledge in data rather than code. Unknown filenames trigger a bbfatal so new firmware blobs are noticed at build time rather than silently shipped unsigned. Signed-off-by: Igor Opaniuk --- classes-recipe/qcom-firmware-sign.bbclass | 257 ++++++++++++++++++++++ 1 file changed, 257 insertions(+) create mode 100644 classes-recipe/qcom-firmware-sign.bbclass diff --git a/classes-recipe/qcom-firmware-sign.bbclass b/classes-recipe/qcom-firmware-sign.bbclass new file mode 100644 index 000000000..3b7dda082 --- /dev/null +++ b/classes-recipe/qcom-firmware-sign.bbclass @@ -0,0 +1,257 @@ +# +# Copyright (c) 2026 Qualcomm Innovation Center, Inc. All rights reserved. +# SPDX-License-Identifier: BSD-3-Clause-Clear +# + +QCOM_FIRMWARE_SIGN_ENABLE ??= "0" + +# Sectools binary -- defaults to the one staged by sectools-native. +QCOM_FIRMWARE_SIGN_SECTOOLS ?= "${STAGING_BINDIR_NATIVE}/sectools" + +QCOM_FIRMWARE_SIGN_SECPROFILE ?= "" +QCOM_FIRMWARE_SIGN_SECPROFILE_DIR ?= "${STAGING_DATADIR_NATIVE}/qcom-security-profiles" + +# Directory containing the OEM signing material: +# qpsa_rootca0.cer -- root certificate +# qpsa_attestca0.cer -- attestation CA certificate +# qpsa_attestca0.key -- attestation CA private key +# sha384_roots_hash.txt -- root hash for verification +# Tests use ci/test-keys/ecdsa via ci/ecdsa-secure-boot-test-keys.yml. +QCOM_FIRMWARE_SIGN_KEY_DIR ?= "" + +# Fuse identifiers, passed through to consumers / signing wrappers. +QCOM_FUSE_OEM_HW_ID ?= "" +QCOM_FUSE_OEM_PRODUCT_ID ?= "" +QCOM_FUSE_SEC_KEY_DERIVATION_KEY ?= "" + +# Anti-rollback version embedded in the signed images. +QCOM_FIRMWARE_SIGN_ANTI_ROLLBACK ?= "0x0" + +# Pull in the build-host signing helpers only when signing is enabled. +DEPENDS += "${@bb.utils.contains('QCOM_FIRMWARE_SIGN_ENABLE', '1', \ + 'sectools-native security-profiles-native', '', d)}" + +# Mapping from common firmware filenames to sectools image-id labels. +QCOM_FIRMWARE_SIGN_IMAGE_ID_MAP ??= "\ + a660_zap.mbn:GPU-MICRO-CODE \ + adsp.mbn:ADSP \ + aop.mbn:AOP \ + cdsp.mbn:CDSP \ + cpucp.elf:CPUCP \ + devcfg.mbn:TZ-DEVCFG \ + hypvm.mbn:QHEE \ + imagefv.elf:UEFIFV \ + ipa_fws.mbn:IPA-FW \ + loadalgota64.mbn:TZ-APP-OEM \ + msbtfw11.mbn:SKIP \ + multi_image.mbn:OEM-MISC \ + prog_firehose_ddr.elf:DEVICE-PROGRAMMER \ + prog_firehose_lite.elf:DEVICE-PROGRAMMER \ + qupv3fw.elf:QUPV3 \ + sec.elf:SEC-ELF \ + shrm.elf:SHRM \ + tz.mbn:TZ \ + uefi.elf:UEFI \ + uefi_sec.mbn:TZ-APP-OEM \ + vpu20_1v.mbn:VENUS-FW \ + vpu30_4v.mbn:VENUS-FW \ + vpu30_4v_16mb.mbn:VENUS-FW \ + wpss.mbn:WPSS \ + xbl_config.elf:XBL-CONFIG \ + xbl_config_gunyah.elf:XBL-CONFIG \ + xbl_config_kvm.elf:XBL-CONFIG \ + xbl.elf:XBL \ + XblRamdump.elf:XBL-RAM-DUMP \ + DigestsToSign.bin.mbn:VIP \ + FD02C9DA-306C-48C7-A49C-BBD827AE86EE.mbn:TZ-APP-OEM \ +" + +# Internal helper: print the absolute path of the security profile XML +# (resolving QCOM_FIRMWARE_SIGN_SECPROFILE bare filenames against +# QCOM_FIRMWARE_SIGN_SECPROFILE_DIR), or fail with bbfatal. +qcom_sign_resolve_secprofile() { + profile="${QCOM_FIRMWARE_SIGN_SECPROFILE}" + if [ -z "${profile}" ]; then + bbfatal "QCOM_FIRMWARE_SIGN_SECPROFILE is empty -- set it to a profile filename (e.g. kodiak_security_profile.xml) or to an absolute path." + fi + case "${profile}" in + /*) printf '%s\n' "${profile}" ;; + *) printf '%s/%s\n' "${QCOM_FIRMWARE_SIGN_SECPROFILE_DIR}" "${profile}" ;; + esac +} + +# Verifies that everything the signing step needs exists before the +# task actually runs -- avoids cryptic sectools errors deep in the +# signing pipeline. +qcom_check_signing_enabled() { + if [ "${QCOM_FIRMWARE_SIGN_ENABLE}" != "1" ]; then + return 1 + fi + if [ ! -x "${QCOM_FIRMWARE_SIGN_SECTOOLS}" ]; then + bbfatal "QCOM_FIRMWARE_SIGN_SECTOOLS ('${QCOM_FIRMWARE_SIGN_SECTOOLS}') is missing or not executable." + fi + secprofile="$(qcom_sign_resolve_secprofile)" + if [ ! -f "${secprofile}" ]; then + bbfatal "Security profile not found: ${secprofile}" + fi + if [ ! -d "${QCOM_FIRMWARE_SIGN_KEY_DIR}" ]; then + bbfatal "QCOM_FIRMWARE_SIGN_KEY_DIR ('${QCOM_FIRMWARE_SIGN_KEY_DIR}') is not a directory." + fi + for f in qpsa_rootca0.cer qpsa_attestca0.cer qpsa_attestca0.key sha384_roots_hash.txt; do + if [ ! -f "${QCOM_FIRMWARE_SIGN_KEY_DIR}/${f}" ]; then + bbfatal "Required key file missing: ${QCOM_FIRMWARE_SIGN_KEY_DIR}/${f}" + fi + done + return 0 +} + +# Look up the sectools image-id for a given firmware filename in +# QCOM_FIRMWARE_SIGN_IMAGE_ID_MAP. Prints the id (or "SKIP" / "") +# to stdout; never fails. +qcom_sign_lookup_image_id() { + basename="$1" + for entry in ${QCOM_FIRMWARE_SIGN_IMAGE_ID_MAP}; do + case "${entry}" in + "${basename}:"*) printf '%s\n' "${entry#*:}"; return 0 ;; + esac + done + printf '\n' +} + +# Sign a single file in place. $1 is the absolute path; any non-zero +# sectools exit triggers a bbfatal in the caller. The primary signal +# for "is this file signable" is the filename->image-id map -- the +# upstream Qualcomm reference binaries (qcm6490_bootbinaries.1.0- +# test-device-public et al.) arrive WITHOUT a pre-existing signature, +# so checking `--inspect` for a Software ID skips everything. +qcom_sign_only_file() { + file_path="$1" + file_name="$(basename "${file_path}")" + secprofile="$(qcom_sign_resolve_secprofile)" + + image_id="$(qcom_sign_lookup_image_id "${file_name}")" + if [ -z "${image_id}" ]; then + bbdebug 1 "${file_name}: not in image-id map, skipping" + return 0 + fi + case "${image_id}" in + SKIP|SKIP:*) + bbnote "${file_name}: skipping (mapped to ${image_id})" + return 0 + ;; + esac + + # Validate the image-id against the active security profile. Each + # profile only declares a subset of the global image-id namespace, + # so skip-with-warn rather than fail for out-of-scope ids. + # + # sectools v1.48 prints the list as a numbered enumeration, e.g. + # Available Image IDs: + # 1. ABL + # 2. ACPI + # ... + # 44. XBL + # 45. XBL-CONFIG + if ! "${QCOM_FIRMWARE_SIGN_SECTOOLS}" secure-image --available-image-ids \ + --security-profile "${secprofile}" 2>/dev/null \ + | sed -nE 's/^[[:space:]]*[0-9]+\.[[:space:]]*([A-Za-z][A-Za-z0-9_-]*)[[:space:]]*$/\1/p' \ + | grep -Fqx "${image_id}"; then + bbwarn "${file_name}: image-id '${image_id}' is not valid for the active security profile, skipping" + return 0 + fi + + bbnote "sectools sign ${file_name} as ${image_id}" + pre_sha="$(sha256sum "${file_path}" | cut -d' ' -f1)" + "${QCOM_FIRMWARE_SIGN_SECTOOLS}" secure-image \ + --sign "${file_path}" \ + --image-id="${image_id}" \ + --security-profile "${secprofile}" \ + --anti-rollback-version="${QCOM_FIRMWARE_SIGN_ANTI_ROLLBACK}" \ + --signing-mode LOCAL \ + --root-certificate-index 0 \ + --root-certificate="${QCOM_FIRMWARE_SIGN_KEY_DIR}/qpsa_rootca0.cer" \ + --ca-certificate="${QCOM_FIRMWARE_SIGN_KEY_DIR}/qpsa_attestca0.cer" \ + --ca-key="${QCOM_FIRMWARE_SIGN_KEY_DIR}/qpsa_attestca0.key" \ + --outfile "${file_path}" \ + || bbfatal "Signing ${file_name} failed" + + # sectools' --sign returns 0 for non-fatal "I don't know how to sign + # this" cases (e.g. plain ELF passed in for an image-id whose profile + # entry expects MBN-V6) and produces a byte-identical output instead + # of erroring out. Detect that explicitly so silent no-ops surface as + # warnings instead of being caught downstream by a confusing + # verify-root failure. + post_sha="$(sha256sum "${file_path}" | cut -d' ' -f1)" + if [ "${pre_sha}" = "${post_sha}" ]; then + bbwarn "${file_name}: sectools --sign left the file byte-identical when signing as '${image_id}' -- the input does not appear to be in the format that image-id expects. Mark this entry as 'SKIP:' in QCOM_FIRMWARE_SIGN_IMAGE_ID_MAP if this is intentional." + fi +} + +# Sign + verify-root in one step. Use this for files where the active +# security profile permits OEM-only signatures (i.e. does NOT have +# on the image entry). For files where the +# profile requires hybrid OEM+QTI vouching (e.g. VIP under the upstream +# github security-profiles repo), call qcom_sign_only_file() instead -- +# sectools' verify-root rejects OEM-only signatures against those +# profile entries even though the boot ROM accepts them. +qcom_sign_verify_file() { + file_path="$1" + file_name="$(basename "${file_path}")" + + pre_sha="$(sha256sum "${file_path}" | cut -d' ' -f1)" + qcom_sign_only_file "$1" || return $? + post_sha="$(sha256sum "${file_path}" | cut -d' ' -f1)" + + # If sign was a no-op (file not in map, mapped to SKIP, image-id + # rejected by the profile, or sectools silently refused -- the warn + # case in qcom_sign_only_file) there is nothing to verify. Skipping + # the verify-root is correct: running it against unchanged bytes + # would fail with a confusing "infile is not signed by OEM" error. + if [ "${pre_sha}" = "${post_sha}" ]; then + bbdebug 1 "${file_name}: unchanged after sign attempt, skipping verify-root" + return 0 + fi + + root_hash="0x$(cut -d' ' -f2 "${QCOM_FIRMWARE_SIGN_KEY_DIR}/sha384_roots_hash.txt")" + "${QCOM_FIRMWARE_SIGN_SECTOOLS}" secure-image --verify-root "${root_hash}" "${file_path}" \ + || bbfatal "Root hash verification of ${file_name} failed" +} + +# Default do_qcom_firmware_sign: walks ${B}/firmware-to-sign and signs +# every *.mbn / *.elf in it. Consumers normally override this to point +# at the actual directory holding their images (e.g. ${D}/ for recipes +# using firmware_install, or ${S}/ for archive-unpacked recipes). +do_qcom_firmware_sign() { + if ! qcom_check_signing_enabled ; then + return 0 + fi + + sign_root="${B}/firmware-to-sign" + if [ ! -d "${sign_root}" ]; then + bbnote "qcom-firmware-sign: ${sign_root} does not exist, nothing to do." + return 0 + fi + + bbnote "qcom-firmware-sign: signing MBN/ELF under ${sign_root}" + find "${sign_root}" -type f \( -iname '*.mbn' -o -iname '*.elf' \) | while read -r f; do + qcom_sign_verify_file "${f}" || bbfatal "Failed to sign: ${f}" + done +} + +do_qcom_firmware_sign[vardeps] += "\ + QCOM_FIRMWARE_SIGN_ENABLE \ + QCOM_FIRMWARE_SIGN_KEY_DIR \ + QCOM_FIRMWARE_SIGN_SECPROFILE \ + QCOM_FIRMWARE_SIGN_SECPROFILE_DIR \ + QCOM_FIRMWARE_SIGN_ANTI_ROLLBACK \ + QCOM_FIRMWARE_SIGN_IMAGE_ID_MAP \ + QCOM_FUSE_OEM_HW_ID \ + QCOM_FUSE_OEM_PRODUCT_ID \ + QCOM_FUSE_SEC_KEY_DERIVATION_KEY \ +" + +# Run before do_deploy (for recipes that ship signed blobs via DEPLOYDIR, +# e.g. firmware-qcom-boot-common.inc) and before do_package (for recipes +# that ship via package_install into /lib/firmware, e.g. +# firmware-qcom.inc / firmware-qcom-hlosfw style consumers) +addtask qcom_firmware_sign before do_deploy before do_package after do_install \ No newline at end of file From a998372df2757cadafd8ac48300aeffe1949f6e3 Mon Sep 17 00:00:00 2001 From: Igor Opaniuk Date: Wed, 24 Jun 2026 08:17:20 +0200 Subject: [PATCH 04/10] ci: add ECDSA test-keys kas overlay and test fixtures Add the development ECDSA PKI material and a kas overlay that wires qcom-firmware-sign.bbclass at it, so a fresh checkout can exercise the firmware-sign path end-to-end without sourcing OEM signing material. * ci/test-keys/ecdsa/ -- the four fixtures consumed by qcom-firmware-sign.bbclass: qpsa_rootca0.cer (root certificate) qpsa_attestca0.cer (attestation CA certificate) qpsa_attestca0.key (attestation CA key) sha384_roots_hash.txt (root hash for sectools --verify-root) These are DEVELOPMENT KEYS ONLY and must never appear in production builds. * ci/ecdsa-secure-boot-test-keys.yml -- mirrors the existing ci/capsule-test-keys.yml pattern; points QCOM_FIRMWARE_SIGN_KEY_DIR at the in-tree test fixtures so the next commit's secure-boot.yml can be combined with it: kas build ci/base.yml:ci/.yml:\ ci/secure-boot.yml:\ ci/ecdsa-secure-boot-test-keys.yml Signed-off-by: Igor Opaniuk --- ci/ecdsa-secure-boot-test-keys.yml | 14 ++++++++++++++ ci/test-keys/ecdsa/qpsa_attestca0.cer | Bin 0 -> 708 bytes ci/test-keys/ecdsa/qpsa_attestca0.key | 9 +++++++++ ci/test-keys/ecdsa/qpsa_rootca0.cer | Bin 0 -> 753 bytes ci/test-keys/ecdsa/sha384_roots_hash.txt | 1 + 5 files changed, 24 insertions(+) create mode 100644 ci/ecdsa-secure-boot-test-keys.yml create mode 100644 ci/test-keys/ecdsa/qpsa_attestca0.cer create mode 100644 ci/test-keys/ecdsa/qpsa_attestca0.key create mode 100644 ci/test-keys/ecdsa/qpsa_rootca0.cer create mode 100644 ci/test-keys/ecdsa/sha384_roots_hash.txt diff --git a/ci/ecdsa-secure-boot-test-keys.yml b/ci/ecdsa-secure-boot-test-keys.yml new file mode 100644 index 000000000..8e4073d0d --- /dev/null +++ b/ci/ecdsa-secure-boot-test-keys.yml @@ -0,0 +1,14 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/siemens/kas/master/kas/schema-kas.json +# +# Include this kas file to enable test ECDSA PKI keys for Qualcomm +# secure-boot image signing. These keys are for CI / development use +# only and must NOT be used in production images. +# +# Usage: kas build ci/base.yml:ci/.yml:ci/secure-boot.yml:ci/ecdsa-secure-boot-test-keys.yml + +header: + version: 14 + +local_conf_header: + ecdsa-secure-boot-test-keys: | + QCOM_FIRMWARE_SIGN_KEY_DIR = "${LAYERDIR_qcom}/ci/test-keys/ecdsa" \ No newline at end of file diff --git a/ci/test-keys/ecdsa/qpsa_attestca0.cer b/ci/test-keys/ecdsa/qpsa_attestca0.cer new file mode 100644 index 0000000000000000000000000000000000000000..81a289720da64160182d036ea06ba3134aab2ed4 GIT binary patch literal 708 zcmXqLVme^Z#N@hwnTe5!iILHOi;Y98&EuRc3p2Ap;~_(C15P&PP!={}rqEzRIRhCG zhgn$EJvA@2D6u3pMZw?IS0N}rzeK^=(NM}j0wm8ZEa>dw>!=Wtnw*iBpOc@SnObaU zZD0wJGlJ;NQ3x$g1?%=styIte8B?5*Uz(GmP+E`zG+7}rCr2SIGc_lrSkq9*Kmeqf zU6?aCF;BrIGc`TmP|$!MB*rDo5uBPFlAoVbY$$9X2omBD=5kKV$xO>H%F9eNkQ3)M zGBq$UurxF@H8L=b66ZAmaZR9Hg921PVl%@~)j%0!Fte}>s>dBmN>YnU5=%1k^FaQ` z;v_6CHz))8h8-NpOpI)-+Kns>O3X@Xwi(ldV$0p4`lKTG6#?Gtm(z}kE`JGr?^II>f`EQa)dcND;Wxu7?Ivks! z-D>y0ZVE%heb+o~^{a-mN+v0aPGO4^4B`xAf&P%?V-aH!X*XqDDAqjhUP`yd^8@UK z6XNQ8o*T%6q?K7D48$5lJk3@fRsP8&AM+;3(f@_{iznN*xq!o5R+xpufQ^yyKNBN^ z0XIkoKMM;p6B7$^;$hBYFi2xEWVk54t6^u+n|l^)g^JP7e&=p!*sP>>W3szZ)wDhP zB{i2c_)fUE)%Mul_u%Bk3+t|?9Ccs2cZE`m Zgnf~;_mbYFb}6nFccvFr#c}w!0RSy-)i3}6 literal 0 HcmV?d00001 diff --git a/ci/test-keys/ecdsa/qpsa_attestca0.key b/ci/test-keys/ecdsa/qpsa_attestca0.key new file mode 100644 index 000000000..4f92d363a --- /dev/null +++ b/ci/test-keys/ecdsa/qpsa_attestca0.key @@ -0,0 +1,9 @@ +-----BEGIN EC PARAMETERS----- +BgUrgQQAIg== +-----END EC PARAMETERS----- +-----BEGIN EC PRIVATE KEY----- +MIGkAgEBBDD0kj7dDRzPghNxNjknJC68swgJH/XGdBVinbVPoWjlEV95Ipr3y6E4 +RNF7IU3lT2KgBwYFK4EEACKhZANiAASnFtGeU9cEUA3+kBdiUyP7wDL4HUTgzZoo +KBS+aGWZ0F04+lcPxpKfxGXv3LGJ1Ucbusc2TsijfPsuWoP9YhRnb0bdpvsarUDG +mCuFPv9+lABY30VuKyfVMR0iNGQhQlY= +-----END EC PRIVATE KEY----- diff --git a/ci/test-keys/ecdsa/qpsa_rootca0.cer b/ci/test-keys/ecdsa/qpsa_rootca0.cer new file mode 100644 index 0000000000000000000000000000000000000000..9e3a856364c66ec2cd650db8bc3af7595db80977 GIT binary patch literal 753 zcmXqLVtQ-P#8k3?nTe5!iILHOi;Y98&EuRc3p2Ap;~_(C15P&PP!={}rqEzRIRhCG zhgn$EJvA@2D6u3pMZw?IS0N}rzeK^=(NM}j0wm8ZEa>dw>!=Wtnw*iBpOc@SnObaU zZD0wJGlJ;NQ3x$g1?%=styIte8B?5*Uz(GmP+E`zG+7}rCr2SIGc_lrSkq9*Kmeqf zU6?aCF;BrIGc`TmP|$!MB*rDo5uBPFlAoVbY$$9X2omBD=5kKV$xO>H%F9eNkQ3)M zGBq$UurxF@H8L=b66ZAmaZRAy;o-?LU;wd$WW`~bb|IX3ug_NgW9p{zS&K_Jtt>N##+7~i){?73}k^}F3ZOv z#vf}ws}kbv6k9t zOciD_@C|n0x%Q03F50ek{p&SS3%U)rPvmjgd75F_=DsZ3KPft0wV!#VlOlS{Gi*NQ HzrF+jRpI8n literal 0 HcmV?d00001 diff --git a/ci/test-keys/ecdsa/sha384_roots_hash.txt b/ci/test-keys/ecdsa/sha384_roots_hash.txt new file mode 100644 index 000000000..34d588973 --- /dev/null +++ b/ci/test-keys/ecdsa/sha384_roots_hash.txt @@ -0,0 +1 @@ +SHA2-384(qpsa_rootca0.cer)= 9bc289bd255ec63e5752d15c15fa64d2f1ec0dd78e0438e5ffea498ae16d0aada49491eadaefe782fe3f91d147a540d3 From 8c7a2de20787599e5ac8a64862b840b83db087a5 Mon Sep 17 00:00:00 2001 From: Igor Opaniuk Date: Wed, 24 Jun 2026 08:17:44 +0200 Subject: [PATCH 05/10] ci: add secure-boot kas overlay to enable firmware signing Add ci/secure-boot.yml, the main kas overlay that turns the firmware-sign pipeline on: * Flips QCOM_FIRMWARE_SIGN_ENABLE to "1", so the no-op default in qcom-firmware-sign.bbclass is replaced with a real sectools invocation. * Defaults QCOM_FIRMWARE_SIGN_SECPROFILE to the Kodiak Security Profile, resolved against the directory staged by security-profiles-native. * Provides placeholder values for the fuse-binding identifiers (QCOM_FUSE_OEM_HW_ID, QCOM_FUSE_OEM_PRODUCT_ID, QCOM_FUSE_SEC_KEY_DERIVATION_KEY) and the anti-rollback floor (QCOM_FIRMWARE_SIGN_ANTI_ROLLBACK). Production builds override these per OEM. * Pulls meta-oe from meta-openembedded for libzip-native, the qdl build-host dep used by the qcomflash-vip image type. To bind the build to actual key material the user combines this overlay with one of: * ci/ecdsa-secure-boot-test-keys.yml -- in-tree development fixtures (added in the previous commit). * An OEM-specific overlay pointing QCOM_FIRMWARE_SIGN_KEY_DIR at production keys sourced from a secrets manager. Typical CI / dev invocation: kas build ci/base.yml:ci/.yml:\ ci/secure-boot.yml:\ ci/ecdsa-secure-boot-test-keys.yml Signed-off-by: Igor Opaniuk --- ci/secure-boot.yml | 57 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 ci/secure-boot.yml diff --git a/ci/secure-boot.yml b/ci/secure-boot.yml new file mode 100644 index 000000000..81482a457 --- /dev/null +++ b/ci/secure-boot.yml @@ -0,0 +1,57 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/siemens/kas/master/kas/schema-kas.json +# +# Include this kas file to enable Qualcomm secure-boot signing of +# pre-built firmware images via classes-recipe/qcom-firmware-sign.bbclass. +# Wires the build through to sectools-native and security-profiles-native +# and supplies the fuse-binding parameters used by sectools at sign time. +# +# Usage: +# kas build ci/base.yml:ci/.yml:ci/secure-boot.yml:ci/ecdsa-secure-boot-test-keys.yml +# +# For production builds, replace ecdsa-secure-boot-test-keys.yml with +# a kas overlay that sets QCOM_FIRMWARE_SIGN_KEY_DIR to an OEM key +# directory (with qpsa_rootca0.cer / qpsa_attestca0.cer / +# qpsa_attestca0.key / sha384_roots_hash.txt) sourced from a secrets +# manager rather than the in-tree test fixtures. + +header: + version: 14 + +# meta-oe provides libzip, which qdl-native links against (besides +# libusb-1.0 and libxml-2.0 from oe-core). meta-python is not pulled +# in; we have no Python-only dependency. +repos: + meta-openembedded: + url: https://github.com/openembedded/meta-openembedded + layers: + meta-oe: + +local_conf_header: + secure-boot: | + QCOM_FIRMWARE_SIGN_ENABLE = "1" + + # Per-SoC Security Profile XML. Bare filenames are resolved + # against the directory staged by security-profiles-native; an + # absolute path can be used to point at a custom profile. + QCOM_SECURITY_PROFILE ?= "kodiak_security_profile.xml" + QCOM_FIRMWARE_SIGN_SECPROFILE = "${QCOM_SECURITY_PROFILE}" + + # Fuse-binding identifiers. These are device-specific; the values + # below are placeholders for development. Override per OEM / + # production build. + QCOM_FUSE_OEM_HW_ID = "0x0001" + QCOM_FUSE_OEM_PRODUCT_ID = "0xabcd" + QCOM_FUSE_SEC_KEY_DERIVATION_KEY = "" + + # Anti-rollback floor stamped into signed images. Bump only when + # rolling forward security-critical firmware revisions. + QCOM_FIRMWARE_SIGN_ANTI_ROLLBACK = "0x0" + + # Enable the qcomflash-vip image type. Produces an additional + # ${IMAGE_NAME}.qcomflash-vip.tar.gz next to the qcomflash tarball + # containing the signed VIP (Validated Image Programming) digest + # table -- intended to be flashed alongside qcomflash via + # `qdl --vip-table-path`. See classes-recipe/image_types_qcom_vip.bbclass + # and recipes-devtools/qdl/qdl-native_2.7.bb. + IMAGE_CLASSES += "image_types_qcom_vip" + IMAGE_FSTYPES += "qcomflash-vip" \ No newline at end of file From 626556032157d2550e45841bf0e6fbaf1065fbe9 Mon Sep 17 00:00:00 2001 From: Igor Opaniuk Date: Wed, 24 Jun 2026 08:09:07 +0200 Subject: [PATCH 06/10] firmware-qcom-boot-common: wire in qcom-firmware-sign Add `inherit qcom-firmware-sign` plus a recipe-appropriate do_qcom_firmware_sign override so the build-time signing class actually runs against the SoC boot binaries every firmware-qcom-boot-*.bb recipe ships. Signed-off-by: Igor Opaniuk --- .../firmware-boot/firmware-qcom-boot-common.inc | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/recipes-bsp/firmware-boot/firmware-qcom-boot-common.inc b/recipes-bsp/firmware-boot/firmware-qcom-boot-common.inc index 180da3b55..f1f83ef22 100644 --- a/recipes-bsp/firmware-boot/firmware-qcom-boot-common.inc +++ b/recipes-bsp/firmware-boot/firmware-qcom-boot-common.inc @@ -27,4 +27,21 @@ do_deploy() { } addtask deploy before do_build after do_install +# Optional secure-boot signing of pre-built MBN/ELF/MELF images. +# When QCOM_FIRMWARE_SIGN_ENABLE is "0" (default) this is a no-op +inherit qcom-firmware-sign + +do_qcom_firmware_sign() { + if ! qcom_check_signing_enabled ; then + return 0 + fi + + bbnote "qcom-firmware-sign: walking ${S} for MBN/ELF/MELF blobs" + find "${S}" -maxdepth 1 -type f \ + \( -iname '*.mbn' -o -iname '*.elf' -o -iname '*.melf' \) \ + | while read -r f; do + qcom_sign_verify_file "${f}" || bbfatal "Failed to sign: ${f}" + done +} + inherit allarch From bb6079a5253efad2d849bde73f587cb64c0eafff Mon Sep 17 00:00:00 2001 From: Igor Opaniuk Date: Wed, 24 Jun 2026 08:09:13 +0200 Subject: [PATCH 07/10] firmware-qcom: wire in qcom-firmware-sign Add `inherit qcom-firmware-sign` plus a recipe-appropriate do_qcom_firmware_sign override so HLOS firmware shipped through the linux-firmware-qcom-* split packages gets signed alongside the boot binaries. Signed-off-by: Igor Opaniuk --- recipes-bsp/firmware/firmware-qcom.inc | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/recipes-bsp/firmware/firmware-qcom.inc b/recipes-bsp/firmware/firmware-qcom.inc index 34808bddf..319e76830 100644 --- a/recipes-bsp/firmware/firmware-qcom.inc +++ b/recipes-bsp/firmware/firmware-qcom.inc @@ -68,6 +68,23 @@ do_install:append() { done } +# Optional secure-boot signing of pre-built MBN/ELF blobs. When +# QCOM_FIRMWARE_SIGN_ENABLE is "0" (the default) this is a no-op +inherit qcom-firmware-sign + +do_qcom_firmware_sign() { + if ! qcom_check_signing_enabled ; then + return 0 + fi + + bbnote "qcom-firmware-sign: walking ${D}${FW_QCOM_BASE_PATH} for MBN/ELF blobs" + find "${D}${FW_QCOM_BASE_PATH}" -type f \ + \( -iname '*.mbn' -o -iname '*.elf' \) \ + | while read -r f; do + qcom_sign_verify_file "${f}" || bbfatal "Failed to sign: ${f}" + done +} + INHIBIT_PACKAGE_DEBUG_SPLIT = "1" INHIBIT_PACKAGE_STRIP = "1" INHIBIT_DEFAULT_DEPS = "1" From 52c0458a34e511960502da2de7b2bdb3e45d7ac1 Mon Sep 17 00:00:00 2001 From: Igor Opaniuk Date: Wed, 24 Jun 2026 09:07:13 +0200 Subject: [PATCH 08/10] qdl: drop stale v2.1 recipe Remove recipes-devtools/qdl/qdl_git.bb. The recipe has not been functionally maintained, is pinned to a SRCREV from before qdl switched its build system from Makefile to meson, and still uses `oe_runmake install`. Signed-off-by: Igor Opaniuk --- recipes-devtools/qdl/qdl_git.bb | 23 ----------------------- 1 file changed, 23 deletions(-) delete mode 100644 recipes-devtools/qdl/qdl_git.bb diff --git a/recipes-devtools/qdl/qdl_git.bb b/recipes-devtools/qdl/qdl_git.bb deleted file mode 100644 index 288a0d601..000000000 --- a/recipes-devtools/qdl/qdl_git.bb +++ /dev/null @@ -1,23 +0,0 @@ -SUMMARY = "Qualcomm DownLoader flashing tool" -DESCRIPTION = "Communicate with Qualcomm SoCs to upload new software or \ -dump memory" -HOMEPAGE = "https://github.com/linux-msm/qdl.git" -SECTION = "devel" - -LICENSE = "BSD-3-Clause" -LIC_FILES_CHKSUM = "file://LICENSE;md5=da6bfde9cb5bc5120a51775381f6edf1" - -DEPENDS = "libxml2 libusb1" - -inherit pkgconfig - -SRCREV = "5db7794e9fdb73ed0c45384026cd8a62b5fff786" -SRC_URI = "git://github.com/linux-msm/${BPN}.git;branch=master;protocol=https" - -PV = "2.1+${SRCREV}" - -do_install () { - oe_runmake install DESTDIR=${D} prefix=${prefix} -} - -BBCLASSEXTEND = "native nativesdk" From efca6ccaa5d47ccc4f76dbc068f276f265561adc Mon Sep 17 00:00:00 2001 From: Igor Opaniuk Date: Tue, 23 Jun 2026 21:13:51 +0200 Subject: [PATCH 09/10] qdl-native: package linux-msm/qdl v2.7 build-host tool Add a -native recipe that builds Qualcomm's Download Tool (qdl) from the linux-msm/qdl upstream at v2.7. The recipe is mainly used for VIP (Validated Image Programming) digest-table generation. In --dry-run + --create-digests mode, qdl walks the rawprogram / patch XMLs the same way a real flash would but, instead of transferring data, computes a digest over each programmed segment and writes a DigestsToSign.bin file. classes-recipe/ image_types_qcom_vip.bbclass consumes this to build the signed qcomflash-vip image type. Build system is meson + ninja with libusb-1.0, libxml-2.0 and libzip runtime deps, all of which are packaged as -native by oe-core, so the recipe collapses to `inherit native meson pkgconfig` plus the three DEPENDS. Signed-off-by: Igor Opaniuk --- .../recipes-devtools/qdl/qdl-native_2.7.bb | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 dynamic-layers/openembedded-layer/recipes-devtools/qdl/qdl-native_2.7.bb diff --git a/dynamic-layers/openembedded-layer/recipes-devtools/qdl/qdl-native_2.7.bb b/dynamic-layers/openembedded-layer/recipes-devtools/qdl/qdl-native_2.7.bb new file mode 100644 index 000000000..9a657be02 --- /dev/null +++ b/dynamic-layers/openembedded-layer/recipes-devtools/qdl/qdl-native_2.7.bb @@ -0,0 +1,17 @@ +SUMMARY = "qdl -- Qualcomm Download Tool" +DESCRIPTION = "Userspace tool to flash images to Qualcomm SoCs over the \ +EDL (Emergency Download) USB protocol" + +HOMEPAGE = "https://github.com/linux-msm/qdl" +LICENSE = "BSD-3-Clause" +LIC_FILES_CHKSUM = "file://LICENSE;md5=da6bfde9cb5bc5120a51775381f6edf1" + +SRC_URI = "git://github.com/linux-msm/qdl.git;protocol=https;branch=master;tag=v${PV}" +SRCREV = "f540e59dbeb462242239778ae0993549304f3abb" + +# qdl links against libusb-1.0, libxml-2.0 and libzip; all three are +# packaged as -native in OE-core. meson/ninja and pkgconfig provided +# by the meson + pkgconfig classes. +DEPENDS = "libusb1-native libxml2-native libzip-native" + +inherit native meson pkgconfig \ No newline at end of file From a1ee7980397d28189b9f0aa2acd2daa8fac956e1 Mon Sep 17 00:00:00 2001 From: Igor Opaniuk Date: Tue, 23 Jun 2026 21:14:00 +0200 Subject: [PATCH 10/10] image_types_qcom_vip: add qcomflash-vip image type Add a new image type that bolts onto the existing qcomflash image type to produce a signed VIP (Validated Image Programming) digest table. Modelled on the "make sign-build" pipeline in qcom-sec-tools/secure.sh; the three on-target steps boil down to: 1. qdl --dry-run --create-digests -- walks the rawprogram/patch XMLs the same way a real flash would, but computes per-segment digests instead of transferring data. Produces DigestsToSign.bin under ${IMGDEPLOYDIR}/${IMAGE_NAME}.qcomflash-vip/. 2. sectools mbn-tool generate --mbn-version 6 -- wraps the digest binary in a Qualcomm firmware MBN container so the on-target verifier recognises it. 3. qcom_sign_verify_file -- signs the MBN as image-id VIP using the same keying material as the rest of the firmware blobs. Output: ${IMAGE_NAME}.qcomflash-vip.tar.gz, intended to be flashed alongside the qcomflash tarball via qdl --vip-table-path. The class inherits image_types_qcom (so qcomflash is defined) and qcom-firmware-sign (so signing helpers / key material are wired in) and adds itself to IMAGE_TYPES with an IMAGE_TYPEDEP on qcomflash so the dependency graph keeps the qcomflash build as a prerequisite of the VIP step. Activate via: IMAGE_FSTYPES += "qcomflash-vip" When QCOM_FIRMWARE_SIGN_ENABLE is not "1" the VIP step is a no-op because there is nothing OEM-specific to bind the digests to. Signed-off-by: Igor Opaniuk --- classes-recipe/image_types_qcom_vip.bbclass | 119 ++++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 classes-recipe/image_types_qcom_vip.bbclass diff --git a/classes-recipe/image_types_qcom_vip.bbclass b/classes-recipe/image_types_qcom_vip.bbclass new file mode 100644 index 000000000..453113a87 --- /dev/null +++ b/classes-recipe/image_types_qcom_vip.bbclass @@ -0,0 +1,119 @@ +# +# Copyright (c) 2026 Qualcomm Innovation Center, Inc. All rights reserved. +# SPDX-License-Identifier: BSD-3-Clause-Clear +# +# qcomflash-vip image type: produce a VIP (Validated Image Programming) +# digest table for a signed qcomflash build, wrap it as an MBN and sign +# it with the OEM secure-boot keys. The resulting `.qcomflash-vip.tar` +# bundle deploys the signed VIP table next to the qcomflash artifacts +# and is intended to be flashed alongside it via qdl --vip-table-path +# (see qcom-sec-tools/secure.sh "make sign-build" for the reference +# pipeline this class is derived from). +# +# Usage: +# IMAGE_FSTYPES += "qcomflash-vip" +# +# Requires the qcom-firmware-sign signing machinery to be active +# (QCOM_FIRMWARE_SIGN_ENABLE = "1"); without it the VIP step is a +# no-op because there is nothing to bind the digests to. + +inherit image_types_qcom qcom-firmware-sign + +IMAGE_TYPES += "qcomflash-vip" +IMAGE_TYPEDEP:qcomflash-vip += "qcomflash" +IMAGE_CMD:qcomflash-vip = "create_qcomflash_vip_pkg" + +# Tooling staged into the native sysroot by the secure-boot recipes. +QCOM_VIP_QDL ?= "${STAGING_BINDIR_NATIVE}/qdl" +QCOM_VIP_SECTOOLS ?= "${QCOM_FIRMWARE_SIGN_SECTOOLS}" + +QCOM_VIP_FIREHOSE ?= "prog_firehose_ddr.elf" + +# MBN container version used to wrap the digest table +QCOM_VIP_MBN_VERSION ?= "6" + +QCOMFLASH_VIP_DIR = "${IMGDEPLOYDIR}/${IMAGE_NAME}.qcomflash-vip" + +do_image_qcomflash_vip[dirs] = "${QCOMFLASH_VIP_DIR}" +do_image_qcomflash_vip[cleandirs] = "${QCOMFLASH_VIP_DIR}" +do_image_qcomflash_vip[depends] += "qdl-native:do_populate_sysroot \ + sectools-native:do_populate_sysroot \ + security-profiles-native:do_populate_sysroot" + +create_qcomflash_vip_pkg() { + if ! qcom_check_signing_enabled ; then + bbwarn "qcomflash-vip requested but QCOM_FIRMWARE_SIGN_ENABLE is not '1'; skipping VIP generation" + return 0 + fi + + if [ ! -d "${QCOMFLASH_DIR}" ]; then + bbfatal "qcomflash-vip: expected qcomflash output at ${QCOMFLASH_DIR}, but it does not exist" + fi + + if [ ! -f "${QCOMFLASH_DIR}/${QCOM_VIP_FIREHOSE}" ]; then + bbfatal "qcomflash-vip: firehose programmer ${QCOM_VIP_FIREHOSE} not found in ${QCOMFLASH_DIR}" + fi + + # 1. Mirror the qcomflash content into our output directory. The + # resulting tarball is a superset of the regular qcomflash tarball + bbnote "qcomflash-vip: mirroring qcomflash artefacts into ${QCOMFLASH_VIP_DIR}" + cp -al "${QCOMFLASH_DIR}"/. "${QCOMFLASH_VIP_DIR}/" + + vipdir="${QCOMFLASH_VIP_DIR}/vip-tables" + install -d "${vipdir}" + + # 2. Generate the digest table. qdl in --dry-run + --create-digests + # mode walks the rawprogram/patch XMLs the same way it would for a + # real flash, but instead of transferring data computes a digest + # over each programmed segment and writes them to + # DigestsToSign.bin under the requested directory. + ( + cd "${QCOMFLASH_VIP_DIR}" + "${QCOM_VIP_QDL}" --allow-fusing --dry-run \ + --create-digests="${vipdir}" \ + "${QCOM_VIP_FIREHOSE}" \ + rawprogram*.xml patch*.xml + ) || bbfatal "qcomflash-vip: qdl digest generation failed" + + if [ ! -f "${vipdir}/DigestsToSign.bin" ]; then + bbfatal "qcomflash-vip: qdl did not produce DigestsToSign.bin in ${vipdir}" + fi + + # 3. Wrap the digest table as an MBN so it has a proper Qualcomm + # firmware header, otherwise the on-target VIP verifier rejects + # it. mbn-tool generate writes the MBN beside the source bin. + "${QCOM_VIP_SECTOOLS}" mbn-tool generate \ + --data "${vipdir}/DigestsToSign.bin" \ + --mbn-version "${QCOM_VIP_MBN_VERSION}" \ + --outfile "${vipdir}/DigestsToSign.bin.mbn" \ + || bbfatal "qcomflash-vip: sectools mbn-tool generate failed" + + # 4. Sign the MBN as image-id VIP using the same keying material as + # the rest of the firmware. qcom_sign_only_file looks up VIP in + # QCOM_FIRMWARE_SIGN_IMAGE_ID_MAP, validates it against the active + # security profile and runs sectools secure-image --sign in-place. + # + # We deliberately skip the post-sign verify-root step (use + # qcom_sign_only_file, not qcom_sign_verify_file): the upstream + # github security-profiles repo marks VIP with + # , which makes sectools' verify-root + # reject OEM-only signatures even though the boot ROM accepts + qcom_sign_only_file "${vipdir}/DigestsToSign.bin.mbn" + + # 5. Bundle the mirrored qcomflash directory plus the signed VIP + # table into a single tarball. The tar layout mirrors the + # qcomflash convention: everything lives under -/ + # so consumers that drive flashing from the extracted root can + # use exactly the same paths as they do for the plain qcomflash + # artefact, with the additional vip-tables/ subdir. + ${IMAGE_CMD_TAR} --sparse --numeric-owner \ + --transform="s,^\./,${IMAGE_BASENAME}-${MACHINE}/," \ + -cf- -C "${QCOMFLASH_VIP_DIR}" . \ + | pigz -p ${BB_NUMBER_THREADS} -9 -n --rsyncable \ + > "${IMGDEPLOYDIR}/${IMAGE_NAME}.qcomflash-vip.tar.gz" + + ln -sf "${IMAGE_NAME}.qcomflash-vip.tar.gz" \ + "${IMGDEPLOYDIR}/${IMAGE_LINK_NAME}.qcomflash-vip.tar.gz" +} + +create_qcomflash_vip_pkg[vardepsexclude] += "BB_NUMBER_THREADS DATETIME" \ No newline at end of file