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/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 diff --git a/ci/test-keys/ecdsa/qpsa_attestca0.cer b/ci/test-keys/ecdsa/qpsa_attestca0.cer new file mode 100644 index 000000000..81a289720 Binary files /dev/null and b/ci/test-keys/ecdsa/qpsa_attestca0.cer differ 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 000000000..9e3a85636 Binary files /dev/null and b/ci/test-keys/ecdsa/qpsa_rootca0.cer differ 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 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 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 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 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 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" 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" 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 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