Agenda

Platform Overview

SoC · board · software stack

Build System Architecture

Top-level Makefile · component layout

Make Targets

Build targets and their outputs

U-Boot SPL

SWIV · qtestsign (local, no CASS)

OP-TEE TEE

BL32 — Secure World Kernel

OP-TEE client

libteec.so · tee-supplicant daemon

OP-TEE xtests

OP-TEE test suite on the board

U-Boot

BL33 — UEFI bootloader

Linux Kernel

EL2 device tree · EFI_ZBOOT

Buildroot

Root filesystem → initramfs · br-ext

UKI Assembly

Unified Kernel Image → efi.bin

Flashing

QDL / Firehose · flash-loader vs flash-kernel

Boot Chain

PBL → XBL → XBL-SEC → U-Boot SPL → TF-A BL31 → OP-TEE → U-Boot → Linux

Boot Load Map & DTBs

Stage load addresses + how the DTBs are used

Runtime Exception Levels

EL3 / S-EL1 / EL2 at runtime

Yocto Integration

Optional BSP for firmware blobs

Blobs: Direct Download

Skip Yocto — fetch blobs directly

Extending Buildroot

Adding a package via br-ext

Tests

On-board validation · FastRPC · video · stress-ng

Summary

Key facts and commands

Qualcomm SA8775P · IQ-9075-EVK

Lemans

Build · Flash · Boot

How the firmware stack is assembled from source,
programmed onto the board, and brought to life.

Target hardware: Qualcomm IQ-9075 Evaluation Kit (EVK).

Part of the OP-TEE build.git project, extended for Qualcomm TZ open firmware development — work is being upstreamed to github.com/OP-TEE.

U-Boot SPL OP-TEE U-Boot Linux + UKI Buildroot QDL Flash

Platform Overview

SoC

  • Qualcomm SA8775P (QCS9075)
  • 8 × Kryo CPU cores (AArch64)
  • Adreno GPU
  • Hexagon DSP
  • Automotive-grade security domains

Board

  • IQ-9075-EVK (Lemans EVK)
  • Engineering validation kit
  • UFS storage
  • Serial: ttyMSM0
  • USB EDL / QDL flash

Software Stack

  • U-Boot SPL (chosen over TF-A BL2 — both open-source)
  • OP-TEE OS (BL32, loaded by SPL)
  • U-Boot proper (BL33 / UEFI)
  • Linux + Buildroot initramfs
  • Packaged as Unified Kernel Image

Notable specifics: SPL signed locally via qtestsign (open-source — no QTI CASS access required); U-Boot proper loaded at 0xaf000000.

Build System Architecture

Entry Point

  • lemans.mk — top-level GNU Make
  • Calls into common.mk for shared targets
  • Toolchain: aarch64-none-linux-gnu-

Key Directories

  • lemans/input/ — pre-built blobs & flash support
  • lemans/output/ — generated artifacts
  • lemans/security/ — SWIV annotation tool (local qtestsign signing)
  • lemans/ukify/ — EFI stub for UKI
  • br-ext/ — external Buildroot package tree
make bootimage Bootloader pipeline make flash-loader
U-Boot SPL
u-boot-spl.elf
make spl
SWIV + qtestsign
→ tz.mbn
make flash-loader
QDL / Firehose
BL31 (TF-A)
bl31.bin
+
OP-TEE
tee-raw.bin
+
U-Boot proper
u-boot.bin
make uefi
SPL_ATF FIT
→ uefi.elf
make flash-loader
QDL / Firehose
make efi Kernel + Root pipeline make flash-kernel
Linux
Image · el2.dtb
+
Buildroot
rootfs.cpio.gz
make efi
uki.efi → efi.bin
make flash-kernel
QDL / Firehose

Make Targets

Target Output Notes
Main Targets
all Builds every component + produces efi.bin Start here for a clean build
bootimage → flash-loader tz.mbn — qtestsign-signed U-Boot SPL
uefi.elf — SPL_ATF FIT: BL31 + OP-TEE + U-Boot proper
Runs optee-os → tfa → u-boot → u-boot-proper → spl → uefi. Signing is local (no CASS needed).
efi → flash-kernel efi.bin — FAT32 image containing uki.efi UKI bundles kernel + initramfs + DTB + cmdline into one EFI binary.
flash-loader Programs tz.mbn + uefi.elf + Qualcomm blobs via QDL/Firehose (LUNs 1-5) After any OP-TEE / TF-A / U-Boot change; needs bootimage
flash-kernel Programs efi.bin (EFI partition only, LUN 0) via QDL Fast iteration — skips firmware; needs efi
Component Builds (invoked by the main targets)
optee-os tee-raw.bin / tee.elf — OP-TEE OS (BL32) @ 0x1c300000 Embedded into the uefi FIT as the BL32 loadable
tfa bl31.bin — TF-A BL31 Secure Monitor @ 0x1c200000 Firmware image in the uefi FIT; no BL2 (we use SPL instead)
u-boot spl/u-boot-spl.elf — U-Boot SPL (qcom_lemans_spl_defconfig) Input to spl → tz.mbn
u-boot-proper u-boot.bin — U-Boot proper (BL33) @ 0xaf000000 (qcom_lemans_defconfig) Input to uefi; carries the control DTB (OP-TEE node)
spl tz.mbn — SWIV-annotated, qtestsign-signed SPL Local signing; auto-fetches qtestsign
uefi uefi.elf — SPL_ATF FIT (mkimage): BL31 + OP-TEE + U-Boot proper + FDT The image the SPL loads from the uefi partition
linux Image + vmlinuz.efi + lemans-evk-el2.dtb Input to efi
buildroot out-br/images/rootfs.cpio.gz — compressed initramfs Input to efi. Depends on linux-firmware.
linux-firmware Stages upstream linux-firmware (qcom/sa8775p: *.mbn/*.jsn/*.elf, incl. qupv3fw.elf) into the rootfs overlay at /lib/firmware/ Prerequisite of buildroot (hence efi). Standalone — independent of fetch-blobs.
Source Overlays (local u-boot / linux patches, not committed upstream)
u-boot-overlays
linux-overlays
Apply lemans/patches/{u-boot,linux}/*.patch to the working tree (idempotent) Auto-run by the builds; enable the OP-TEE DT node + CONFIG_TEE/OPTEE. Revert with *-overlays-revert.
Other Flash / Support
efi-kernel-only efi.bin with a local-kernel UKI (root=PARTLABEL=rootfs) Boots the on-disk Yocto rootfs; needs flash-yocto. Flash with flash-kernel.
fetch-blobs Downloads Qualcomm firmware blobs into lemans/blobs/ No Yocto build needed; required before first flash
flash-yocto Programs the complete unmodified Yocto release (all 6 LUNs) via QDL Verify default hardware against the stock BSP (needs make yocto)

U-Boot SPL SWIV annotation · qtestsign signing (local)

Inputs

  • SPL ELF: u-boot/.output/spl/u-boot-spl.elf
  • TF-A BL31: arm-trusted-firmware/build/lemans_evk/release/bl31/bl31.elf
  • OP-TEE: tee.elf (embedded in SPL FIT image at build time)
  • SWIV tool: lemans/security/swiv_build_utility.py
  • Signing: msm8916-mainline/qtestsign

Outputs

  • u-boot-spl.elf → SWIV → u-boot-spl-swiv.elf
  • u-boot-spl-swiv.elf → qtestsign → tz.mbn
  • uefi.elf assembled separately by make uefi (mkimage FIT: BL31 + OP-TEE + U-Boot proper)

SPL Signing — Two-Stage Pipeline (make spl)

Stage 1 — SWIV Annotation
python3 lemans/security/swiv_build_utility.py \
  u-boot/.output/spl/u-boot-spl-swiv.elf \
  u-boot/.output/spl/u-boot-spl.elf \
  lemans

# Input:  u-boot-spl.elf  (raw U-Boot SPL output)
# Output: u-boot-spl-swiv.elf  (SPL + SWIV segment)
Stage 2 — qtestsign (local, open-source)
qtestsign -v6 tz \
  -o u-boot/.output/spl/u-boot-spl.mbn \
  u-boot/.output/spl/u-boot-spl-swiv.elf

# Cloned from msm8916-mainline/qtestsign
# No CASS / remote signing server needed

qtestsign is auto-fetched by make spl if not already present at $(ROOT)/qtestsign.

OP-TEE TEE BL32 — Secure World Kernel

What it is

OP-TEE is an open-source Trusted Execution Environment that runs in the Arm TrustZone Secure World (EL1-S). It hosts Trusted Applications (TAs) and handles Secure Monitor Calls (SMCs) from the Normal World.

Configuration

  • Platform: qcom-lemans
  • Output: optee_os/out/arm/core/tee.elf
  • Passed to U-Boot as: TEE= (embedded in SPL FIT image)
Role in the Boot Chain

U-Boot SPL loads TF-A BL31, OP-TEE (BL32), and U-Boot proper (BL33) from the SPL FIT image. TF-A BL31 runs at EL3 as Secure Monitor.

The Linux kernel communicates with OP-TEE via the tee-supplicant daemon and the CONFIG_OPTEE driver, both enabled in the kernel defconfig.

OP-TEE client Normal World Runtime

Library — libteec.so

The TEE Client API library. User-space applications call it to open sessions with Trusted Applications and invoke commands across the Normal/Secure World boundary.

  • TEEC_InitializeContext / TEEC_FinalizeContext
  • TEEC_OpenSession — open a channel to a TA
  • TEEC_InvokeCommand — call a TA entry point
  • TEEC_CloseSession

Buildroot Package

  • Package: optee_client_ext (br-ext)
  • Init script: /etc/init.d/S30optee (auto-start)
  • User tee — runs supplicant, owns /dev/teepriv0
  • Group teeclnt — client apps; includes user test
Daemon — tee-supplicant

Runs in Normal World as a privileged daemon. Acts as the bridge between the OP-TEE Secure World and REE filesystem / hardware resources.

  • TA loading — reads /lib/optee_armtz/*.ta and passes TA binaries to the Secure World on demand
  • Secure storage — mediates RPMB and REE-filesystem secure storage RPCs from OP-TEE OS
  • Socket / time RPCs — handles other Secure World RPC calls that need REE access (entropy, sockets, time)

Without tee-supplicant running, TA loading and secure storage fail silently — always verify it is active before running xtest.

OP-TEE xtests OP-TEE Test Suite

What it covers

  • Core TEE OS functionality (panics, memory, threads)
  • GlobalPlatform TEE Internal Core API
  • GlobalPlatform TEE Client API
  • Cryptographic operations — AES, RSA, ECC, hash, ECDH
  • Secure Storage — RPMB and REE-filesystem backends
  • Regression suite and benchmarks

Buildroot Package

  • Package: optee_test_ext (br-ext, on by default)
  • Depends on: optee_client_ext, openssl
  • TAs installed to: /lib/optee_armtz/
  • Init: S30test-arm-ffa-user for ARM FF-A user tests
  • Source: OP-TEE/optee_test.git (via manifest)
Running xtest on the board

Connect via serial (ttyMSM0 115200) or SSH. Verify tee-supplicant is running before starting.

# Full test suite
xtest

# Regression tests only
xtest -t regression

# Single test case by number
xtest 1001

# List all test identifiers
xtest -l

# Help
xtest -h

Test results show OK / FAILED per case. A clean run ends with "RESULT: regression tests PASSED".

U-Boot SPL + proper — First open-source boot stage

Build — two defconfigs

  • SPL: qcom_lemans_spl_defconfig.output/spl/u-boot-spl.elf (→ tz.mbn)
  • Proper (BL33): qcom_lemans_defconfig.output-proper/u-boot.bin @ 0xaf000000
  • Cross-compiler: aarch64-none-linux-gnu-
  • SPL is built with SPL_LOAD_FIT + SPL_ATF: it reads the uefi partition as a FIT and hands off to BL31 → OP-TEE → U-Boot proper
  • OP-TEE enabled in proper via source overlay: /firmware/optee node + CONFIG_TEE/OPTEE

Why U-Boot?

U-Boot is configured in UEFI mode so the system presents a standard UEFI firmware interface. This lets the EFI partition be read using standard UEFI boot discovery — no bespoke boot config needed.

Boot behaviour
  • Reads the EFI system partition (FAT32 efi.bin)
  • Falls back to EFI/BOOT/bootaa64.efi as the UEFI fallback path
  • Discovers and launches EFI/Linux/uki.efi (the Unified Kernel Image)
  • Hands kernel execution over at EL2 once the UKI is loaded

Linux Kernel + EL2 Device Tree

Build Config

  • Defconfig: defconfig (override via LINUX_DEFCONFIG)
  • Auto-enabled: CONFIG_TEE, CONFIG_OPTEE
  • Auto-enabled: CONFIG_EFI_BOOT, CONFIG_EFI_ZBOOT
  • Auto-enabled: CONFIG_DEVTMPFS, CONFIG_QCOM_PAS_TEE
  • Output: arch/arm64/boot/Image
  • EFI_ZBOOT: arch/arm64/boot/vmlinuz.efi

Console

console=ttyMSM0,115200
qcom_scm.download_mode=1
arm-smmu.disable_bypass=0
clk_ignore_unused
pd_ignore_unused
EL2 Device Tree

lemans-evk-el2.dtb is a special Device Tree variant required when running Linux at EL2 without the Gunyah hypervisor.

  • Enables the Qualcomm watchdog at EL2
  • Disables the GPU Zap shader (not needed at EL2)
  • Remaps IOMMU streams for ADSP, CDSP, WPSS remoteprocs
  • Adjusts power domain topology for EL2 operation

The DTB is bundled into the UKI alongside the kernel Image and the Buildroot initramfs — no separate flash step required.

Buildroot Root Filesystem → Initramfs

Target Format

Buildroot produces a compressed CPIO archive (out-br/images/rootfs.cpio.gz). It is embedded directly into the UKI as an initramfs, loaded entirely into RAM at boot.

Firmware Overlay

The kernel is built CONFIG_MODULES=n (all drivers are built-in, so the PAS remoteprocs can probe without waiting on late-loaded modules) — there are no .ko files. Buildroot's only root overlay is the DSP/remoteproc firmware staged into lemans/overlay/, so it lands in /lib/firmware inside the initramfs.

Included Packages & br-ext
  • Busybox (core utilities, watchdog support)
  • OpenSSH server + key utilities
  • OpenSSL CLI
  • DHCPCD (networking), Ethtool, xinetd
  • GStreamer 1.x + v4l-utils — V4L2 video decode/encode pipeline
  • optee_client_ext — libteec.so + tee-supplicant
  • optee_test_ext — xtest binary + TAs (on by default)

Login banner: "OP-TEE embedded distrib for lemans"

br-ext — External Package Tree

All OP-TEE packages use br-ext/, which points to local source checkouts. Stock BR2_PACKAGE_OPTEE_* packages are explicitly disabled — always use the _ext variants.

UKI Assembly Unified Kernel Image → efi.bin

Step 1 — ukify build → uki.efi

Bundles four inputs into a single PE/COFF EFI binary — no grub.cfg, no extlinux.conf. UEFI loads and verifies it in one shot.

EFI stub lemans/ukify/linuxaa64.efi.stub
Kernel linux/arch/arm64/boot/Image
DTB lemans-evk-el2.dtb
Initramfs out-br/images/rootfs.cpio.gz
Cmdline console=ttyMSM0,115200 …
inputs
×4
ukify
systemd-ukify
uki.efi
PE/COFF binary
Step 2 — mkfs.fat + mcopy → efi.bin

A blank 512 MB file is formatted as FAT32, then populated with two files using mtools (no loop-mount needed):

truncate -s 512M lemans/output/efi.bin
mkfs.fat -F 32 -S 4096 lemans/output/efi.bin
mmd -i efi.bin ::/EFI/BOOT ::/EFI/Linux
mcopy -i efi.bin bootaa64.efi ::/EFI/BOOT/  # UEFI fallback
mcopy -i efi.bin uki.efi     ::/EFI/Linux/ # UKI
efi.bin (FAT32, 512 MB)
├── EFI/BOOT/bootaa64.efi   ← UEFI fallback entry
└── EFI/Linux/uki.efi       ← Unified Kernel Image
uki.efi
step 1 output
mkfs.fat
+ mcopy
efi.bin
→ flash-kernel

Flashing QDL — Qualcomm Download Protocol

The board is held in EDL (Emergency Download Mode) over USB. The host sends a firehose programmer ELF, then uses XML manifests to write partition images directly via the Sahara/Firehose protocol.
For the full Linux procedure — installing qdl, entering EDL mode, and provisioning the UFS — see Qualcomm's official Flash over QLI guide.

make flash-loader

Programs the full bootloader stack: firmware blobs, XBL, GPT tables, U-Boot SPL (tz.mbn), and the SPL_ATF boot FIT (uefi.elf, carrying TF-A BL31 + OP-TEE + U-Boot proper). Programs LUNs 1–5 (rawprogram1–5, with rawprogram4 patched to fill the qupfw slots).

Use after a full rebuild, first-time provisioning, or any U-Boot SPL / OP-TEE change. Follow with make flash-kernel to also write the kernel.

make flash-kernel

Programs only the EFI partition (efi.bin). Generates a filtered rawprogram0-only-kernel.xml from the full rawprogram0.xml.

Use for fast kernel / initramfs / TA iteration — avoids re-flashing firmware.

make efi-kernel-only

Builds a UKI from the locally-built kernel (no initramfs) with root=PARTLABEL=rootfs rw rootwait and injects it into a copy of the Yocto efi.bin (replacing its stock UKI). Build only — flash the result with make flash-kernel.

Boots the on-disk Yocto rootfs with your own kernel — unlike make efi, which boots the Buildroot initramfs. The matching Yocto rootfs.img must already be flashed (make flash-yocto).

Flash Artifacts — Source

flash-loader / flash-kernel stage every stock firmware file strictly from the fetched blob set — no Yocto or hand-placed copy can shadow them. Built images come from the local output:

stock FW
lemans/blobs/
(make fetch-blobs)
+
built images
lemans/output/
tz.mbn · uefi.elf · efi.bin
missing blob
hard error

Flash Procedure — Step by Step

  • 1
    Enter EDL mode Hold the EDL button while connecting USB, or issue adb reboot edl. The host sees a QDL USB device.
  • 2
    Load the firehose programmer QDL uploads prog_firehose_ddr.elf into SRAM via the Sahara protocol. The programmer initialises DDR and the UFS controller.
  • 3
    Write GPT partition tables gpt_main*.bin / gpt_backup*.bin are written first to lay out the partition map across UFS LUNs 1–5.
  • 4
    Flash partition images via rawprogram XML The XML manifests map each image file to a partition. flash-loader uses rawprogram1–5 (LUNs 1–5); flash-kernel uses only the filtered rawprogram0-only-kernel.xml.
  • 5
    Apply patches (patch*.xml) GPT corrections and CRC patches are applied. Board resets and boots from the freshly written images.

Boot Chain

PBL — Primary Boot Loader Hardware ROM in SoC. Authenticates and loads XBL from UFS. Cannot be modified.
XBL — eXtensible Boot Loader Qualcomm proprietary. Initialises DDR, clocks, power rails. Loads XBL-SEC.
XBL-SEC — Security Authenticator Verifies the qtestsign signature on tz.mbn using the hash chain embedded in the MBN header before passing control to U-Boot SPL.
U-Boot SPL (tz.mbn, qtestsign-signed) Runs at EL3. Initialises DRAM. Loads TF-A BL31, OP-TEE (BL32), and U-Boot proper (BL33) from the embedded SPL FIT image. Hands off to TF-A BL31.
TF-A BL31 — Secure Monitor Runs at EL3. Sets up the Secure Monitor. Loads OP-TEE into Secure EL1 and stages U-Boot proper for Normal World handoff.
SPL FIT → BL31 + BL32 + BL33 SPL unpacks the FIT image: BL31 stays at EL3 as Secure Monitor, OP-TEE loads into Secure EL1, U-Boot proper staged for Normal World at 0xaf000000.
BL32 — OP-TEE OS Initialises in Secure EL1. Sets up Trusted Application infrastructure. Returns control to TF-A BL31 Secure Monitor.
BL33 — U-Boot (UEFI) Runs at Normal World EL2. Presents UEFI runtime. Reads EFI system partition. Discovers bootaa64.efi fallback, then uki.efi.
UEFI Boot — uki.efi U-Boot loads and executes the Unified Kernel Image. The UKI PE/COFF stub sets up the execution environment.
Linux Kernel (EL2, lemans-evk-el2.dtb) Kernel boots with the EL2-specific device tree. Mounts the embedded initramfs. Loads OP-TEE kernel driver.
Buildroot Initramfs Busybox init, OpenSSH, networking. OP-TEE supplicant bridges TAs between user space and Secure World.

Boot Loader Map Load Addresses & Device Trees

Stage Image / partition Load / entry EL Where it comes from
U-Boot SPL tz.mbn (tz) 0x1c100000 EL3 CONFIG_SPL_TEXT_BASE; loads uefi FIT to 0xB0800000
TF-A BL31 bl31.bin (FIT: atf) 0x1c200000 EL3 Secure Monitor — stays resident after boot
OP-TEE OS (BL32) tee-raw.bin (FIT: optee) 0x1c300000 S-EL1 Headerless pager binary linked at this address
U-Boot proper (BL33) u-boot.bin (FIT: uboot) 0xaf000000 EL2 CONFIG_TEXT_BASE; UEFI environment for the UKI
Linux Image in uki.efi relocatable EL2 Placed by U-Boot's EFI loader from the ESP
SPL
0x1c100000 · EL3
BL31
0x1c200000 · EL3
OP-TEE (BL32)
0x1c300000 · S-EL1 · resident
U-Boot (BL33)
0xaf000000 · EL2
Linux
UKI · EL2

BL31 boots OP-TEE (BL32) and U-Boot proper (BL33); OP-TEE stays resident at S-EL1 while U-Boot loads Linux.

Control DTB — U-Boot (fdt-1 in the uefi FIT)

  • The SPL reads it so it records the FIT loadables into /fit-images, letting spl_invoke_atf() discover the OP-TEE (os="tee") and U-Boot (os="u-boot") entry points.
  • u-boot.bin ships as nodtb + appended control DTB; U-Boot proper uses its own embedded DTB via board_fdt_blob_setup().
  • BL31 hands off with x0 = MPIDR (not an fdt pointer), so the appended DTB is required.

Linux DTB — lemans-evk-el2.dtb

  • Bundled into the UKI next to the kernel Image and initramfs — no separate flash step.
  • EL2-specific variant needed to run Linux at EL2 without the Gunyah hypervisor.
  • Enables the EL2 watchdog, disables the GPU Zap shader, and remaps IOMMU streams for the remoteprocs.

The three FIT payloads (BL31 · OP-TEE · U-Boot) live on the uefi partition; the signed SPL (tz.mbn) on tz. Loadable order matters — uboot must precede optee so fdt_addr is set before OP-TEE is recorded.

Arm Exception Levels at Runtime

Unlike the transient boot stages (PBL, XBL, U-Boot SPL), these are the persistent images that keep executing on a running system — each resident at its own exception level after the board has booted.

EL3 — Secure Monitor (TF-A BL31)

Highest privilege. Handles SMCs between Normal and Secure worlds. TF-A BL31 is loaded by U-Boot SPL and remains resident as the Secure Monitor after boot.

Secure EL1 — OP-TEE OS

Runs in TrustZone Secure World. Hosts Trusted Applications. Communicates with Linux via the TEE subsystem SMC interface.

Normal EL2 — Linux Kernel

Linux runs at EL2 (hypervisor level) in the Normal World. The EL2 DTB overlay enables this — it remaps IOMMU streams that behave differently without Gunyah.

Normal EL0 — User Space

Buildroot initramfs processes (Busybox, OpenSSH, tee-supplicant). OP-TEE client calls travel up through EL1 → EL3 → Secure EL1.

Yocto Integration Optional reference BSP for regression testing

Why Yocto?

Yocto builds Qualcomm's official BSP — a complete, known-good working system you can flash and run as a reference. It lets you confirm the board and firmware are healthy on a trusted baseline, and compare the open firmware stack against it when hunting regressions.

It is not required to build or flash the open stack — for that always use make fetch-blobs, which downloads just the firmware blobs in minutes and keeps the open boot firmware stack functional.

make yocto — full BSP (hours)

  • Clones qualcomm-linux/meta-qcom from GitHub
  • Builds via kas using iq-9075-evk.yml
  • Produces yocto/build/tmp/deploy/images/iq-9075-evk/
  • Consumed only by make flash-yoctoflash-loader/flash-kernel never read from here

Use make fetch-blobs instead to skip the build entirely.

Note: flashing the Yocto BSP image programs the traditional Qualcomm boot stack (closed XBL / ABL / UEFI), not the open firmware stack this repo builds (U-Boot SPL → TF-A → OP-TEE → U-Boot). Its purpose here is a working reference stack to evaluate regressions against — for the open firmware stack always use make fetch-blobs to keep it functional.

Stock firmware (fetch-blobs) vs this repo
From fetch-blobsFrom this repo
XBL + XBL configU-Boot SPL (tz.mbn)
AOP firmwareBoot FIT (uefi.elf)
ADSP / CDSP FWLinux kernel
WPSS firmwareBuildroot initramfs
Prog firehose ELFUKI (uki.efi)
GPT tablesefi.bin (FAT32)
rawprogram XMLs
devcfg.mbn, cpucp.elf, …

Yocto Blobs Skipping the build — direct download

Both upstream URLs are publicly accessible — no Qualcomm account required. make fetch-blobs automates all three steps below and populates lemans/blobs/, which flash-loader/flash-kernel stage from exclusively.

① Boot Binaries — 18 MB

XBL, XBL config, AOP, ADSP/CDSP/WPSS FW, prog_firehose.elf, devcfg.mbn, sec.dat, rawprogram XMLs

curl -LO https://softwarecenter.qualcomm.com/nexus/generic/\
product/chip/tech-package/QCS9100_bootbinaries.1.0/\
qcs9100_bootbinaries.1.0-test-device-public/\
00132/QCS9100_bootbinaries_00132.zip

sha256: be297c43…f12f9db

② CDT File — 380 KB

Customer Device Tree — board-specific hardware config blob needed by XBL

curl -LO https://artifacts.codelinaro.org/artifactory/\
codelinaro-le/Qualcomm_Linux/QCS9100/cdt/\
rb8_core_kit.zip

sha256: a252244f…9f15bfa

③ GPT Tables & rawprogram XMLs

The github.com/qualcomm-linux/qcom-ptool repo ships no pre-generated tables — only a partitions.conf under platforms/iq-9075-evk/ufs/. make fetch-blobs shallow-clones the repo, installs ptool into a venv, then runs gen_partition (conf → XML) followed by ptool (XML → GPT bins + rawprogram XMLs). sha256-verified checksums are applied to the two downloaded zips before extraction.

Extending Buildroot Adding a package via br-ext

Package structure

Directory layout

br-ext/package/mypkg/
├── Config.in        ← Kconfig menu entry
└── mypkg.mk         ← build rules

Mirror the pattern from br-ext/package/optee_client_ext/. Register with:
source "$BR2_EXTERNAL_BR_EXT_PATH/package/mypkg/Config.in" in br-ext/package/Config.in.

Enable in lemans.mk

# In lemans.mk — package selection block
BR2_PACKAGE_MYPKG      ?= y
BR2_PACKAGE_MYPKG_SITE ?= $(ROOT)/mypkg

The ?= operator allows command-line overrides without editing the file.

mypkg.mk (CMake example)
MYPKG_VERSION       = 1.0
MYPKG_SITE          = $(BR2_PACKAGE_MYPKG_SITE)
MYPKG_SITE_METHOD   = local
MYPKG_INSTALL_STAGING = YES

MYPKG_CONF_OPTS += -DENABLE_TESTS=OFF

define MYPKG_INSTALL_TAS
  @$(foreach f,$(wildcard $(@D)/ta/out/*.ta), \
    $(INSTALL) -m 444 $$f \
      $(TARGET_DIR)/lib/optee_armtz/ &&) true
endef
MYPKG_POST_INSTALL_TARGET_HOOKS += MYPKG_INSTALL_TAS

$(eval $(cmake-package))

TAs installed to /lib/optee_armtz/ are loaded dynamically by tee-supplicant on demand.

Build & flash

# Full rootfs rebuild:
make buildroot efi flash-kernel

# Rebuild just one package:
make -C ../buildroot O=$(ROOT)/out-br \
    mypkg-rebuild
make efi flash-kernel

Tests On-board validation from the Normal World

Once the board boots, validate the stack from the Normal World serial console (ttyMSM0 115200) or over SSH. These programs ship in the Buildroot rootfs — the stock Yocto rootfs does not include them.

qcom-tests — bundled bring-up runner

# Interactive runner — pick step(s); PASS/FAIL per step
qcom-tests
#   1) ADSP FastRPC     2) CDSP FastRPC
#   3) VIDEO round-trip 4) CPU stress (stress-ng)
Choice> 1 3      # blank = all

# multi-hour CPU soak via stress-ng — logs load +
# thermal-zone temps each minute (watch for throttle)
STRESS_SECS=14400 qcom-tests   # 4h, then pick 4

We also use stress-ng for performance metrics — its per-stressor throughput report (bogo-ops/s) gives a repeatable CPU benchmark.

xtest — OP-TEE test suite

# full OP-TEE regression suite
xtest

# a single test, or list the options
xtest 1004      # run one test
xtest -h        # selection / levels / loops

fastrpc_test — CPU↔DSP / OP-TEE PAS

# CDSP brought up via remoteproc → OP-TEE PAS, then:
fastrpc_test -d 3 -U 0

# -d 3 = CDSP domain. A passing run means OP-TEE ran
# the PAS reset sequence correctly.

Video codec — OP-TEE PAS + GStreamer (optional)

# iris firmware loads lazily on first open — trigger it:
exec 3<> /dev/video0 ; exec 3>&-

# optional H.264 encode → decode (hardware v4l2)
gst-launch-1.0 videotestsrc num-buffers=100 ! \
  videoconvert ! v4l2h264enc ! h264parse ! \
  v4l2h264dec ! fakesink -v

Summary

Build

  • 5 components built from source: OP-TEE, TF-A BL31, U-Boot (SPL + proper), Linux, Buildroot
  • SPL SWIV-annotated then signed locally via qtestsign (no CASS required)
  • All packed into a single UKI EFI binary inside a FAT32 image
  • make all builds everything; make bootimage rebuilds the boot chain

Flash

  • make flash-loader — full bootloader provisioning (rawprogram 1–5)
  • make flash-kernel — EFI partition only (fast iteration)
  • Uses QDL / Firehose protocol over USB in EDL mode
  • Stock firmware: staged strictly from make fetch-blobs (lemans/blobs/)

Boot

  • PBL → XBL → XBL-SEC → SPL (EL3) → BL31 (EL3); BL31 boots OP-TEE (S-EL1) + U-Boot (EL2) → UKI → Linux (EL2)
  • XBL-SEC verifies qtestsign signature on tz.mbn before SPL runs
  • EL2 DTB overlay required for Linux without Gunyah
  • Initramfs is embedded — no block storage mount needed

Key files

  • lemans.mk — top-level build orchestration
  • lemans/output/tz.mbn — qtestsign-signed U-Boot SPL
  • lemans/output/uefi.elf — SPL_ATF boot FIT (BL31 + OP-TEE + U-Boot proper)
  • lemans/output/efi.bin — FAT32 EFI partition image