Skip to content

qcom: PAS: add end-to-end firmware image authentication for hoya (lemans/kodiak) - #16

Merged
coral-public-ci[bot] merged 10 commits into
qualcomm-linux:qcom-nextfrom
zelvam95:feature/plat-qcom-pas-auth
Jul 9, 2026
Merged

qcom: PAS: add end-to-end firmware image authentication for hoya (lemans/kodiak)#16
coral-public-ci[bot] merged 10 commits into
qualcomm-linux:qcom-nextfrom
zelvam95:feature/plat-qcom-pas-auth

Conversation

@zelvam95

@zelvam95 Selvam Sathappan (zelvam95) commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

qcom: PAS: add end-to-end firmware image authentication for hoya (lemans/kodiak)

Overview

This series adds a complete, reference-PIL-equivalent firmware authentication
pipeline to the qcom_pas OP-TEE TA/PTA stack for Qualcomm hoya-family
targets (lemans, kodiak). Before this series, AUTH_AND_RESET unconditionally
released DSPs from reset with no verification of firmware authenticity or
integrity. After this series, every DSP image is authenticated end-to-end —
certificate chain, root-of-trust binding, SW_ID/device binding, anti-rollback,
and per-segment hash integrity — before the DSP is released, with the same
phase split as the reference PIL implementation: all cryptographic
authentication happens once at INIT_IMAGE, before the kernel copies any
segment into the carveout; AUTH_AND_RESET only re-verifies the loaded
segments' hashes and starts the DSP.

Cryptographic policy (X.509/mbedTLS) runs in the user TA (S-EL0); carveout
mapping and segment hashing run in the PAS P-TA (S-EL1), since only the P-TA
can map non-secure physical memory.


Authentication Flow

sequenceDiagram
    autonumber
    participant K as Linux Kernel (NS-EL1)
    participant TA as qcom_pas user TA (S-EL0)
    participant PAS as PAS P-TA (S-EL1)
    participant FUSE as Fuse P-TA (S-EL1)

    rect rgb(255, 245, 200)
    Note over K,FUSE: PHASE 1 - INIT_IMAGE (once per DSP at boot) - full crypto authentication + binding
    K->>TA: INIT_IMAGE(pas_id, metadata_blob)
    TA->>TA: save metadata keyed by pas_id (TEE-private copy)
    TA->>PAS: INIT_IMAGE(pas_id)
    PAS-->>TA: TEE_SUCCESS
    TA->>TA: 1a. parse MBN header, locate hash table + OEM/QTI metadata, resolve segment hash size (root_cert_sel)
    TA->>FUSE: GET_SEGMENT_HASH_SIZE(root_cert_sel)
    FUSE-->>TA: hash_size
    TA->>FUSE: GET_SECBOOT_STATE + GET_ROOT_OF_TRUST
    FUSE-->>TA: secboot_enabled, root_of_trust (48B)
    TA->>TA: 1b. X.509 cert chain verify (OEM, and QTI if double-signed), bind leaf/root to root_of_trust
    TA->>TA: 1c. verify OEM (+ QTI countersignature) over signed region
    TA->>TA: 1d. check SW_ID == expected(pas_id)
    TA->>FUSE: GET_DEVICE_IDS + GET_USE_SERIAL_NUM + GET_SOC_HW_VERSION
    FUSE-->>TA: oem_id, model_id, jtag_id, serial, soc_ver
    TA->>TA: 1e. bind OEM/MODEL/HW/serial/SoC metadata to device fuses
    TA->>FUSE: GET_PIL_ROLLBACK_VERSION(pas_id)
    FUSE-->>TA: fuse_version
    TA->>TA: 1f. assert image anti_rollback >= fuse_version
    TA->>FUSE: BLOW_PIL_ROLLBACK_VERSION(image_version) [if newer]
    TA->>TA: mark pas_id authenticated, cache parsed hash-segment in per-pas_id slot
    alt authentication failed (secure boot on)
        TA-->>K: TEE_ERROR_SECURITY (INIT_IMAGE fails, DSP never loads)
    else authentication passed (or secure boot off: logged and tolerated)
        TA-->>K: TEE_SUCCESS
    end
    end

    rect rgb(220, 235, 255)
    Note over K,PAS: PHASE 2 - MEM_SETUP (carveout registration)
    K->>TA: MEM_SETUP(pas_id, fw_base, fw_size)
    TA->>PAS: MEM_SETUP(pas_id, fw_base, fw_size)
    PAS->>PAS: map firmware carveout (IO_NSEC)
    PAS-->>TA: TEE_SUCCESS
    TA-->>K: TEE_SUCCESS
    end

    rect rgb(235, 235, 235)
    Note over K: PHASE 3 - Kernel loads firmware (NS side, no TEE involvement)
    K->>K: request_firmware, copy segments to NS carveout at fw_base
    end

    rect rgb(220, 255, 220)
    Note over K,PAS: PHASE 4 - AUTH_AND_RESET - hash-table re-verify of loaded segments + DSP start
    K->>TA: AUTH_AND_RESET(pas_id, fw_base, fw_size)
    TA->>TA: 4a. require pas_id already authenticated from Phase 1, fail closed otherwise
    TA->>PAS: VERIFY_IMAGE(metadata + hash_table, pas_id)
    PAS->>PAS: map carveout as RAM_NSEC
    PAS->>PAS: SHA-256/384 hash each loaded segment vs hash_table entry
    PAS-->>TA: all N segments verified (TEE_SUCCESS)
    TA->>PAS: AUTH_AND_RESET(pas_id)
    PAS->>PAS: enable DSP clocks, deassert reset, fw_start()
    PAS-->>TA: TEE_SUCCESS
    TA-->>K: TEE_SUCCESS
    end

    rect rgb(255, 245, 200)
    Note over K: PHASE 5 - DSP released
    K->>K: remoteproc: DSP is available
    end
Loading

Secure-boot-off tolerance: when SECURE_BOOTn AUTH_EN is unblown (dev
boards), every cryptographic/binding failure in Phase 1 is logged and
tolerated instead of failing INIT_IMAGE, so unsigned builds still exercise
the full authentication path without blocking boot. Phase 4's hash
re-verification is unconditional regardless of secure-boot state.


Commit Breakdown

1. pta: qcom: pas: verify firmware segments against image hash table

Adds a privileged-core routine, pas_auth_core_verify_segments(), that hashes
a loaded firmware carveout and compares it against a caller-supplied
per-segment digest table (SHA-256 or SHA-384, selected by digest size):

  • Parses the ELF header from the metadata blob, not the carveout (the
    carveout holds only loaded segment bytes)
  • Computes reloc_base as min(p_paddr) across relocatable LOAD segments
  • Verifies entry 0 (ELF header + phdr table hash), then each loadable,
    non-paged, non-shared segment at p_paddr - reloc_base
  • Maps the carveout as MEM_AREA_RAM_NSEC for the duration and unmaps on
    every exit path
  • Wired into the PAS PTA as a new PTA_QCOM_PAS_VERIFY_IMAGE command

Gated behind CFG_QCOM_PAS_HASH_VERIFY (default n at the TA level).


2. ta: qcom_pas: add MBN hash-segment parser

Adds pas_hashseg_parse() / struct pas_hashseg: a parser for the Qualcomm
MBN hash segment embedded in the INIT_IMAGE metadata blob. Locates the hash
segment immediately after the ELF preamble (phdrs[0].p_filesz), validates
the v5 (40-byte) / v6 (48-byte) header, and exposes the per-segment digest
table and its entry count. All region slicing is bounds-checked with
ADD_OVERFLOW/MUL_OVERFLOW.


3. ta: qcom_pas: verify loaded firmware image integrity

Wires the TA's INIT_IMAGE/AUTH_AND_RESET flow to the hash-segment parser
and the new PTA command:

  • INIT_IMAGE takes a private, per-pas_id copy of the metadata blob and
    parses its hash segment
  • AUTH_AND_RESET packs [metadata | hash_table] and invokes
    PTA_QCOM_PAS_VERIFY_IMAGE to re-hash the now-loaded carveout
  • Metadata is keyed by pas_id in a fixed slot table (PAS_MD_SLOTS = 8)
    because the kernel qcom_pas_tee driver opens one shared TEE session
    across all peripherals, and DSPs load concurrently — a single buffer would
    let one image's metadata clobber another's before its AUTH_AND_RESET ran
  • Segments are hashed with SHA-384

Gated behind CFG_QCOM_PAS_HASH_VERIFY (default n).


4. plat-qcom: hoya: enable firmware segment hash verification by default

Turns on CFG_QCOM_PAS_HASH_VERIFY by default on hoya-family platforms
(lemans, kodiak) so loaded PIL firmware images are re-verified against their
hash table before the peripheral is released from reset.


5. drivers: qcom: qfprom: add secure-boot fuse accessors

Adds QFPROM accessors backing PIL image authentication:

Accessor Purpose
qcom_secboot_is_enabled() Secure-boot state from SECURE_BOOTn AUTH_EN
qcom_secboot_get_root_of_trust() OEM root-of-trust digest from PK_HASH_0
qcom_secboot_get_device_ids() / get_soc_hw_version() OEM_ID, MODEL_ID, JTAG_ID, serial number, SoC family|device version
qcom_secboot_get_segment_hash_size() Per-segment digest size (SHA-256/384) selected by metadata root_cert_sel, on platforms implementing the OEM_CONFIG2 select bits (lemans only; kodiak always SHA-384)
qcom_secboot_get_eku_enforcement_en() / get_use_serial_num() OEM_CONFIG2/SECURE_BOOTn fuse overrides extending the leaf-certificate and metadata checks
qcom_secboot_get_pil_rollback_version() / blow_pil_rollback_version() PIL subsystem anti-rollback device version, read as a popcount of the unary-encoded ANTI_ROLLBACK fuse rows, advanced by setting additional bits

Register addresses, masks, and popcount formulas are platform-specific and
defined per target header (kodiak/qfprom_target.h, lemans/qfprom_target.h).


6. pta: qcom: fuse: expose secure-boot fuses to user TAs

Adds a Fuse pseudo-TA (pta_qcom_fuse) wrapping the QFPROM accessors above
for user TAs that have no direct QFPROM driver access:

Command Description
PTA_QCOM_FUSE_GET_SECBOOT_STATE Secure-boot enabled boolean
PTA_QCOM_FUSE_GET_ROOT_OF_TRUST OEM root-of-trust digest (48 bytes)
PTA_QCOM_FUSE_GET_PIL_ROLLBACK_VERSION Device anti-rollback version
PTA_QCOM_FUSE_BLOW_PIL_ROLLBACK_VERSION Advance anti-rollback fuse
PTA_QCOM_FUSE_GET_DEVICE_IDS OEM_ID, MODEL_ID, JTAG_ID, serial
PTA_QCOM_FUSE_GET_SOC_HW_VERSION SoC family|device version
PTA_QCOM_FUSE_GET_SEGMENT_HASH_SIZE Per-segment digest size selection
PTA_QCOM_FUSE_GET_EKU_ENFORCEMENT_EN EKU enforcement fuse override
PTA_QCOM_FUSE_GET_USE_SERIAL_NUM Serial-number binding fuse override

Built when CFG_QCOM_PAS_SECURE_BOOT and CFG_QCOM_QFPROM are both enabled.


7. ta: qcom_pas: decode image metadata and signed region

Extends the MBN hash-segment parser to expose the pieces needed for
signature verification:

  • pas_hashseg_get_meta() decodes the OEM metadata's binding fields (SW_ID,
    HW/OEM/MODEL id, SoC version and serial allow-lists, anti-rollback floor,
    and the option flags gating each)
  • pas_hashseg_signed_copy() builds the exact byte range each signer signed:
    for double-signed images, the other signer's header size-fields and
    metadata block are zeroed in the copy, mirroring how the signature was
    originally computed over a "cleaned" region
  • pas_hashseg_peek_root_cert_sel() does a minimal header-only pass to read
    the metadata's root_cert_sel field ahead of the full parse, since the
    full parse itself needs the segment digest size that root_cert_sel
    selects

8. ta: qcom_pas: add X.509 certificate chain and signature verification

Adds the crypto building blocks for authenticating a signed firmware image,
on top of mbedTLS:

  • pas_auth_verify_cert_chain() parses a concatenated DER chain
    (leaf-first, self-signed root last), enforces a certificate policy
    (RSA-PSS/ECDSA-P384 only, RSA 2048–4096 bit with exponent 65537),
    structural constraints (leaf not a CA, every issuer is a CA, AKID/SKID/
    serial issuer linkage, chain-length bounds, leaf KeyUsage and, when fused
    on, Extended Key Usage), and an optional EKU requirement
  • pas_auth_check_root_of_trust() binds the chain's root to a
    caller-supplied digest
  • pas_auth_sig_algo_from_leaf() / pas_auth_verify_signature() select and
    check the leaf's signature algorithm, with RSA-PSS MGF1 fixed to SHA-256
    independent of the message digest

Not yet wired into the TA's authentication flow at this point in the series
(see commit 10).


9. ta: qcom_pas: add PIL SW_ID and signer-authority policy tables

Adds the lemans/kodiak pas_id→SW_ID mapping and the SW_ID set that requires
a QTI countersignature (double-signed images), as static lookup tables
independent of the crypto and orchestration layers (pas_policy.c).


10. ta: qcom_pas: authenticate images and bind them to peripheral and device

Wires the certificate chain, signature, and metadata-binding checks into the
TA's INIT_IMAGE flow — this is the commit that moves authentication ahead
of segment load, matching the flow diagram above:

  • authenticate_metadata(), called from INIT_IMAGE after parsing the hash
    segment: verifies the metadata version, resolves the per-segment digest
    size from the metadata's root_cert_sel via the Fuse PTA, validates the
    OEM (and, for double-signed images, QTI) certificate chain and signature
    against the device root of trust, binds the metadata to this peripheral's
    SW_ID and to device-identity fields (OEM/MODEL/JTAG/serial/SoC version) via
    the Fuse PTA, and enforces the PIL anti-rollback version floor
  • Each binding check is enforced when secure boot is fused on and
    logged-and-tolerated otherwise, so unsigned-boot boards still come up
  • AUTH_AND_RESET now requires that INIT_IMAGE already authenticated the
    pas_id and only re-verifies segment hashes (commit 3's logic) before
    starting the DSP
  • Enables CFG_QCOM_PAS_SECURE_BOOT by default on hoya-family platforms

Config Knobs

Config Default (hoya) Effect
CFG_QCOM_PAS_HASH_VERIFY y Per-segment hash re-verification at AUTH_AND_RESET
CFG_QCOM_PAS_SECURE_BOOT y Cert chain, signature, and device/peripheral binding at INIT_IMAGE; requires CFG_QCOM_PAS_HASH_VERIFY
CFG_QCOM_PAS_ARB y when CFG_QCOM_PAS_SECURE_BOOT=y PIL anti-rollback enforcement; requires CFG_QCOM_PAS_SECURE_BOOT

Files Changed (summary)

Component Files
QFPROM driver core/drivers/qcom/qfprom/qfprom_core.c, core/drivers/qcom/qfprom/{kodiak,lemans}/qfprom_target.h, core/include/drivers/qcom/qfprom/qfprom.h
Fuse PTA core/pta/qcom/fuse/pta_qcom_fuse.c, core/pta/qcom/fuse/sub.mk, lib/libutee/include/pta_qcom_fuse.h
PAS PTA core/pta/qcom/pas/pta_qcom_pas.c, core/pta/qcom/pas/pas_core.c, core/pta/qcom/pas/pas_auth_core.{c,h}, lib/libutee/include/pta_qcom_pas.h
PAS User TA ta/qcom_pas/src/qcom_pas.c, ta/qcom_pas/src/pas_hashseg.c, ta/qcom_pas/src/pas_auth.c, ta/qcom_pas/src/pas_auth_sig.c, ta/qcom_pas/src/pas_policy.c, ta/qcom_pas/include/{pas_auth,pas_hashseg,pas_policy}.h
Platform config core/arch/arm/plat-qcom/hoya/qcom-arch.mk, ta/qcom_pas/user_ta.mk
mbedTLS config lib/libmbedtls/include/mbedtls_config_uta.h (enables MBEDTLS_PKCS1_V21, RSA-PSS support)

Testing

  • Built clean for both lemans and kodiak (optee-os target).
  • Boot-tested on lemans with secure boot off (dev board): all 5 DSPs
    (ADSP pas_id=1, CDSP0 pas_id=18, CDSP1 pas_id=30, GPDSP0 pas_id=39,
    GPDSP1 pas_id=40) authenticate and come online; remoteproc: X is available logged for each; ELF header + N segment(s) verified logged
    per DSP.
  • Per-pas_id metadata slots confirmed to fix a metadata cross-contamination
    regression seen during development (one DSP's INIT_IMAGE clobbering
    another's slot on the kernel's shared TEE session, causing a segment hash
    mismatch on the second DSP).
  • Secure boot on path exercised end-to-end against production-signed
    images: cert chain, signature, SW_ID/device binding, and anti-rollback all
    pass for OEM_SIGNED images (all current lemans/kodiak DSP images use this
    path; the PAS_DOUBLE_SIGNED/QTI-countersignature path is implemented but
    exercised only when a double-signed image is introduced).
  • checkpatch clean (pre-existing trailing-whitespace notices only, unrelated
    to this series).

Dependencies

  • Requires the earlier lemans PAS bring-up series (ADSP/CDSP/GPDSP/IRIS)
    which established the P-TA command interface and platform subsystem
    registration.

@zelvam95
Selvam Sathappan (zelvam95) force-pushed the feature/plat-qcom-pas-auth branch 4 times, most recently from 536dd4a to 0f74541 Compare June 30, 2026 19:26
@zelvam95 Selvam Sathappan (zelvam95) changed the title ta: qcom_pas: verify firmware image before bringing up the peripheral qcom: PAS: add local E2E PIL firmware authentication for hoya (lemans/kodiak) Jul 1, 2026
@zelvam95 Selvam Sathappan (zelvam95) changed the title qcom: PAS: add local E2E PIL firmware authentication for hoya (lemans/kodiak) qcom: PAS: add PIL firmware authentication for hoya (lemans/kodiak) Jul 1, 2026
@zelvam95 Selvam Sathappan (zelvam95) changed the title qcom: PAS: add PIL firmware authentication for hoya (lemans/kodiak) qcom: pas: add end-to-end firmware image authentication (hash + signature + binding) Jul 6, 2026
@zelvam95 Selvam Sathappan (zelvam95) changed the title qcom: pas: add end-to-end firmware image authentication (hash + signature + binding) qcom: PAS: add end-to-end firmware image authentication for hoya (lemans/kodiak) Jul 6, 2026
bool is_64;
};

static TEE_Result parse_elf(const uint8_t *fw, size_t fw_size,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

improve the function - no need to repeat the code

static void get_phdr(const uint8_t *fw, const struct elf_info *info,
size_t idx, uint32_t *p_type, uint32_t *p_flags,
uint64_t *p_paddr, size_t *p_filesz, size_t *p_memsz)
{

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

improve the function to avoid repeating code

{
uint64_t min_paddr = UINT64_MAX;
bool relocatable = false;
uint64_t p_paddr = 0;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

what is the p_ about?


if (hash_size > sizeof(dgst))
return TEE_ERROR_BAD_PARAMETERS;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

for sha-256 you can use the single step helper hash_sha256_check

return pas_platform_shutdown(params[0].value.a);
}

#ifdef CFG_QCOM_PAS_HASH_VERIFY

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

remove conditional compilation from pta_qcom_pas.c

#include <trace.h>
#include <utee_defines.h>
#include <util.h>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

remove conditional compilation

ctx->hash_table, ctx->hash_size);
}

TEE_Result pas_auth_core_verify_segments(const struct pas_auth_core_ctx *ctx)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

too many error messages on the console - makes it difficult to read

const struct elf_info *info)
{
size_t hdr_len = 0;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

this needs comments, maybe three separate checks

Add a privileged-core routine that verifies the integrity of a loaded
firmware image against a per-segment hash table. Given the firmware
carveout and a digest table (one entry per ELF program header), it
maps the carveout, recomputes each loadable segment's digest plus the
ELF-header/program-header digest, and compares them to the table
entries, then unmaps.

pas_auth_core_verify_segments() implements the segment walk and hashing
(SHA-256 or SHA-384, selected by the caller-supplied digest size);
pas_core.c wires it into pas_platform_verify_image(), reachable from
the PAS PTA via a new PTA_QCOM_PAS_VERIFY_IMAGE command that receives
the packed metadata and hash table.

The relocation base is computed over the same loadable-segment set the
kernel MDT loader uses (PT_LOAD, not the hash segment, nonzero memsz),
so the per-segment offsets match where the loader placed the bytes.

Gated behind CFG_QCOM_PAS_HASH_VERIFY (default off); the hash table is
assumed pre-authenticated by the caller.

Signed-off-by: Selvam Sathappan Periakaruppan <speriaka@qti.qualcomm.com>
Add a parser for the Qualcomm MBN hash segment embedded in the
INIT_IMAGE metadata blob. It locates the hash segment after the ELF
preamble, validates the v5/v6 header, and exposes the per-segment
digest table (one digest per ELF program header) and its entry count
via struct pas_hashseg.

Signed-off-by: Selvam Sathappan Periakaruppan <speriaka@qti.qualcomm.com>
Extend the PAS TA to re-verify the integrity of each firmware image
before its peripheral is brought out of reset. At INIT_IMAGE the TA
takes a private per-pas_id copy of the metadata blob and parses its
hash-segment; at AUTH_AND_RESET it packs the metadata and hash table
and invokes the PAS PTA to re-hash the loaded carveout against that
table.

Metadata is keyed by pas_id because the kernel driver shares one TEE
session across all peripherals and DSPs load concurrently, so a single
buffer would let one image's metadata clobber another's before its
AUTH_AND_RESET runs. Segments are hashed with SHA-384.

Gated behind CFG_QCOM_PAS_HASH_VERIFY (default off).

Signed-off-by: Selvam Sathappan Periakaruppan <speriaka@qti.qualcomm.com>
Turn on CFG_QCOM_PAS_HASH_VERIFY by default on hoya-family platforms
(lemans, kodiak) so loaded PIL firmware images are re-verified against
their hash table before the peripheral is released from reset.

Signed-off-by: Selvam Sathappan Periakaruppan <speriaka@qti.qualcomm.com>
Add QFPROM accessors backing PIL image authentication:

  qcom_secboot_is_enabled(): secure-boot (image authentication) state
  from the SECURE_BOOTn AUTH_EN fuse.
  qcom_secboot_get_root_of_trust(): OEM root-of-trust digest from
  PK_HASH_0.
  qcom_secboot_get_device_ids() / get_soc_hw_version(): OEM_ID,
  MODEL_ID, JTAG_ID, serial number, and SoC family|device version, used
  to bind a signed image's metadata to this device.
  qcom_secboot_get_segment_hash_size(): per-segment digest size
  (SHA-256/384) selected by a metadata root_cert_sel index, on
  platforms that implement the OEM_CONFIG2 select bits (lemans only;
  kodiak always SHA-384).
  qcom_secboot_get_eku_enforcement_en() / get_use_serial_num():
  OEM_CONFIG2/SECURE_BOOTn fuse overrides that extend the leaf
  certificate and metadata checks.
  qcom_secboot_get_mrc_info(): multiple-root-certificate provisioning
  state (root count and per-index activation/revocation lists),
  meaningful only when the root of trust is fuse-resident
  (SECURE_BOOTn PK_HASH_IN_FUSE).
  qcom_secboot_get_pil_rollback_version() / blow_pil_rollback_version():
  PIL subsystem anti-rollback device version, read as a popcount of
  the unary-encoded ANTI_ROLLBACK fuse rows, advanced by setting
  additional bits.

Register addresses, masks and popcount formulas are platform-specific
and defined per target header (lemans, kodiak).

Signed-off-by: Selvam Sathappan Periakaruppan <speriaka@qti.qualcomm.com>
Add a pseudo-TA wrapping the QFPROM secure-boot accessors for user TAs
that have no direct QFPROM driver access: secure-boot state, OEM
root-of-trust, device-identity fields (OEM/MODEL/JTAG/serial, SoC
version), per-segment hash digest size, EKU enforcement and
serial-number fuse overrides, multiple-root-certificate provisioning
state, and PIL anti-rollback version read/advance.

Built when CFG_QCOM_PAS_SECURE_BOOT and CFG_QCOM_QFPROM are both
enabled.

Signed-off-by: Selvam Sathappan Periakaruppan <speriaka@qti.qualcomm.com>
Extend the MBN hash-segment parser to expose the pieces needed for
signature verification: the OEM/QTI metadata blocks, certificate
chains, signatures, and the boundaries of the region the signature
covers.

pas_hashseg_get_meta() decodes the OEM metadata's binding fields
(SW_ID, HW/OEM/MODEL id, SoC version and serial allow-lists,
root_cert_sel, anti-rollback floor, and the option flags gating each).
Metadata version and layout mirror the Qualcomm OEM metadata layout.

pas_hashseg_signed_copy() builds the exact byte range each signer
signed: for double-signed images, the other signer's header
size-fields and metadata block are zeroed in the copy, mirroring how
the signature was originally computed over a "cleaned" region.

pas_hashseg_peek_root_cert_sel() does a minimal header-only pass to
read the metadata's root_cert_sel field ahead of the full parse, which
itself needs the segment digest size that root_cert_sel selects.
pas_hashseg_peek_version() likewise reads the MBN header version ahead
of the parse, so the caller can pick the digest size the version
mandates.

Signed-off-by: Selvam Sathappan Periakaruppan <speriaka@qti.qualcomm.com>
Add the crypto building blocks for authenticating a signed firmware
image: X.509 certificate chain parsing and validation, root-of-trust
binding, and RSA-PSS/ECDSA signature verification, on top of mbedTLS.

pas_auth_verify_cert_chain() walks a concatenated DER chain
(leaf-first, one or more self-signed roots last, 0xFF-padded),
enforces a certificate policy (RSA-PSS/ECDSA-P384 only, RSA 2048-4096
bit with exponent 65537), structural constraints (leaf not a CA, every
issuer is a CA, AKID/SKID/serial issuer linkage, chain length bounds,
leaf KeyUsage and, when fused on, Extended Key Usage), and an optional
EKU requirement. When multiple roots are provisioned it anchors the
chain to the image-selected root and returns the whole root region for
root-of-trust binding. pas_auth_check_root_cert_index() validates the
selected root against the device activation/revocation lists.
pas_auth_check_root_of_trust() binds the chain's root(s) to a
caller-supplied digest. pas_auth_sig_algo_from_leaf() derives both the
signature scheme and its message digest from the leaf key type
(RSA-PSS with SHA-256, salt and MGF1 also SHA-256; ECDSA with
SHA-384), so each image is verified with the digest its signature was
produced with. pas_auth_verify_signature() runs the verification,
trimming the signature to its actual length (DER for ECDSA, modulus
size for RSA) since the hash-segment field is a fixed-size reservation.

Not yet wired into the TA's build.

Signed-off-by: Selvam Sathappan Periakaruppan <speriaka@qti.qualcomm.com>
Add the lemans/kodiak PAS pas_id-to-SW_ID mapping and the SW_ID set
that requires a QTI countersignature (double-signed images), as static
lookup tables independent of the crypto and orchestration layers.

Signed-off-by: Selvam Sathappan Periakaruppan <speriaka@qti.qualcomm.com>
Wire the certificate chain, signature, and metadata-binding checks
into the TA's INIT_IMAGE/AUTH_AND_RESET flow, so a firmware image's
hash table is only trusted once its signer and binding to this
peripheral and device are authenticated.

authenticate_metadata() now, after parsing the hash segment: verifies
the metadata version, resolves the per-segment digest size (MBN v5 is
always SHA-256; v6 selects SHA-256/SHA-384 from the metadata's
root_cert_sel via the fuse PTA), validates the OEM (and, for
double-signed images, QTI) certificate chain and signature against the
device root of trust, binds the metadata to this peripheral's SW_ID
and to device-identity fields (OEM/MODEL/JTAG/serial/SoC version) via
the fuse PTA, and enforces the PIL subsystem anti-rollback version
floor. When multiple root certificates are provisioned, the image's
root_cert_sel selects the anchoring root, gated by the device
activation/revocation lists, and the root-of-trust digest covers all
provisioned roots. The metadata-field bindings are validated before
the image signature, and the signature digest is derived from the leaf
certificate (RSA-PSS with SHA-256, ECDSA with SHA-384) while the
root-of-trust binding stays SHA-384. Each binding check is enforced
when secure boot is fused on and logged-and-tolerated otherwise, so
unsigned-boot boards still come up.

Enable CFG_QCOM_PAS_SECURE_BOOT by default on hoya-family platforms.

Signed-off-by: Selvam Sathappan Periakaruppan <speriaka@qti.qualcomm.com>
@zelvam95

Copy link
Copy Markdown
Contributor Author

Thanks Jorge A. Ramirez-Ortiz (@ldts) for the feedback. I will split the PR into 2 -> 1 for NS boot hash verification and 1 for Secure boot signature auth and then will address the above comments and then follow up for further review.

@coral-public-ci
coral-public-ci Bot merged commit 52c7efe into qualcomm-linux:qcom-next Jul 9, 2026
1 of 3 checks passed
@zelvam95

Selvam Sathappan (zelvam95) commented Jul 9, 2026

Copy link
Copy Markdown
Contributor Author

This was merged mistakenly by the coral infra & coral issue is fixed now.
2 separate PRs are raised as updated above. The comments given here will be addressed as part of those PRs:
#19 and #20

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.

2 participants