Skip to content

Boot a real secure world: upstream OP-TEE as BL32 in the boot FIT - #13

Merged
MrMati merged 4 commits into
mainfrom
optee
Aug 13, 2026
Merged

Boot a real secure world: upstream OP-TEE as BL32 in the boot FIT#13
MrMati merged 4 commits into
mainfrom
optee

Conversation

@MrMati

@MrMati MrMati commented Aug 5, 2026

Copy link
Copy Markdown
Owner

Brings up OP-TEE on the Pico Max, recreating the working bring-up from luckfox-pico-yocto on this repo's mainline boot chain. Upstream OP-TEE gained an RV1106 port in plat-rockchip; the new make optee stage pins that exact commit and builds it from source (no rv1106_tee_ta blob from rkbin). Since this repo already runs mainline U-Boot, none of the vendor fit_nodes.sh surgery the Yocto reference needed applies here: the stock CONFIG_SPL_OPTEE_IMAGE flow (the RK3229/RK3288 path) has binman pack tee-raw.bin into u-boot.itb, which now ships as u-boot.img. The SPL enters OP-TEE, and OP-TEE returns to U-Boot in the normal world.

Two mismatches between mainline conventions and the upstream RV1106 port had to be bridged. The FIT template hardcodes the vendor blob's load address, so the board -u-boot.dtsi re-points it to where upstream OP-TEE links:

&fit_template {
	images {
		op-tee {
			load = <0x03d00000>;	/* CFG_TZDRAM_START, not the blob's 0x8400000 */
			entry = <0x03d00000>;
		};
	};
};

And the platform forces CFG_DT_ADDR=0x08000000 because the vendor SPL passes garbage in r2; mainline SPL passes the real control-DTB address there, so build-optee.sh builds with CFG_DT_ADDR=n and OP-TEE believes r2. The non-secure entry needs no override: the SPL hands over lr = CONFIG_TEXT_BASE = 0x00200000, which is exactly the platform's CFG_NS_ENTRY_ADDR default.

Kernel side: the devicetree reserves the TZDRAM+SHM window [0x03d00000, 0x04e00000) no-map and enables the existing &optee node; a new config fragment adds TEE/OPTEE/ARM_PSCI/HW_RANDOM_OPTEE (the DT's psci { method = "smc" } calls finally have a monitor to land in). Notably, pico-max.env's fdt/ramdisk/script/pxefile staging addresses sat inside that window and moved above it; make check now holds all four statements of the window (OP-TEE link address, FIT load, kernel reservation, staging addresses) to one another. The standard rootfs profile picks up Debian's tee-supplicant + libteec2 so userspace is ready when a TA needs it.

Validated by building all of it: tee.elf links with entry 0x3d00000, the produced FIT carries firmware = "op-tee" at 0x03d00000 with U-Boot as loadable at 0x00200000, u-boot.img comes out at 848 KiB (inside the 1 MiB NAND slot, now enforced by build-uboot.sh), the kernel fragment survives olddefconfig symbol-for-symbol, and the DTB compiles with the optee node okay and the no-map reservation. One flashing caveat, called out in the README: a kernel from this tree expects to run in the normal world, so u-boot.img and the system must be reflashed together.

Update: the secure world is now exercisable end to end from userspace, and the core traces at debug level. build-optee.sh pins the debug configuration explicitly (CFG_TEE_CORE_DEBUG=y, upstream's default, forced so it cannot silently change) and raises core and TA trace levels from 1 to 3 (error+info+debug; level 4 flow-traces every SMC and would drown the 115200 console), which costs the core 8.5 KiB. It also exports the TA dev kit, and a new stage builds six upstream examples against it:

# scripts/build-optee-examples.sh, consumed by the standard rootfs profile
want="hello_world random aes acipher hotp secure_storage"
make -C "$examples/$ex/host" CROSS_COMPILE="$CROSS_COMPILE" TEEC_EXPORT="$texp"
make -C "$examples/$ex/ta"   CROSS_COMPILE="$CROSS_COMPILE" TA_DEV_KIT_DIR="$devkit"

Host apps land in /usr/bin/optee_example_*, signed TAs in /lib/optee_armtz/ where Debian's tee-supplicant loads them from (upstream default path, unchanged by Debian). optee_client and optee_examples are pinned at 4.9.0; the host apps link a source-built libteec at build time but resolve Debian's libteec2 on the board, and the build asserts the libteec.so.2 soname to catch a divergence early. The examples ride the standard profile only, since minimal ships no libteec2. Running optee_example_hello_world on the board is the smoke test: it opens a session, invokes the increment command, and the secure console narrates the TA load as it happens; optee_example_secure_storage additionally round-trips tee-supplicant's REE FS RPC through /var/lib/tee.


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith with what you need. Autofix is disabled.

MrMati and others added 4 commits August 5, 2026 20:44
Upstream OP-TEE gained an RV1106 port in plat-rockchip; pin that commit
and build it from source (no rv1106_tee_ta blob). Mainline U-Boot's stock
CONFIG_SPL_OPTEE_IMAGE flow packs tee-raw.bin into binman's u-boot.itb,
which now ships as u-boot.img: the SPL enters OP-TEE, and OP-TEE returns
to U-Boot in the normal world.

Board-side adjustments, each with its reason next to it:

 - the FIT's op-tee node is re-pointed from the vendor blob address
   (SDRAM + 0x8400000) to 0x03d00000, where upstream OP-TEE links
 - OP-TEE builds with CFG_DT_ADDR unset, so it takes the control DTB
   address the mainline SPL passes in r2
 - the kernel devicetree reserves [0x03d00000, 0x04e00000) no-map and
   enables the &optee node; the config fragment adds TEE/OPTEE/ARM_PSCI
 - pico-max.env's fdt/ramdisk/script/pxefile staging addresses move out
   of that window (they sat right inside it)
 - the standard rootfs profile ships Debian's tee-supplicant + libteec1

make check now holds the four files that state the TEE window to one
another, and build-uboot.sh refuses a u-boot.img that outgrows its 1 MiB
NAND slot (the FIT is 848 KiB).

Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>
Trixie's optee-client 4.7.0 bumped the client library soname, so the
libteec1 package no longer has an installation candidate and mmdebstrap
aborts the standard profile. Install its replacement, libteec2.

Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>
… at debug level

The core build turns explicit about being a debug build: CFG_TEE_CORE_DEBUG=y
(upstream's default, forced so it cannot silently change) and core/TA trace
levels raised from 1 to 3, so the secure console narrates session setup and
TA loading. Level 4 (flow) would drown the 115200 console. The debug core
costs 8.5 KiB over the level-1 build, nowhere near the 1 MiB NAND slot.

A new make optee-examples stage builds six of the upstream optee_examples
(pinned at 4.9.0, like optee_client) against the TA dev kit that make optee
now exports: host apps to /usr/bin, signed TAs to /lib/optee_armtz where
Debian's tee-supplicant loads them from. The host apps link a libteec built
from the pinned optee_client but run against Debian's libteec2 -- same
libteec.so.2 soname, checked at build time. The examples ride the standard
rootfs profile only; minimal has no libteec2 for them to resolve.

Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>
The u-boot job installed only gcc-arm-linux-gnueabihf, which was enough
for as long as everything it built was freestanding: U-Boot, the SPL and
the OP-TEE core carry their own headers. The new make optee-examples
step is different in kind, not just in size: libteec and the example
host apps are hosted userspace programs that include <errno.h> and
friends. Without libc6-dev-armhf-cross the cross gcc has no
/usr/arm-linux-gnueabihf/include and falls back to the host's
/usr/include, where features-time64.h asks for <bits/wordsize.h> that
only lives in the x86_64 multiarch directory the cross compiler never
searches. Hence the fatal error the job died with.

The package is a Recommends of the cross gcc, so --no-install-recommends
drops it; the check and npu jobs (and scripts/install-deps.sh) already
list it explicitly for the same reason.

Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>
@MrMati
MrMati merged commit 91a0b10 into main Aug 13, 2026
7 checks passed
@MrMati
MrMati deleted the optee branch August 13, 2026 11:26
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.

1 participant