ATF/ TEE / REE 与ARM 异常级别EL 安全体系完整篇:TrustZone硬件到 SMC 进OP-TEE的全链路

板子上开了 TrustZone,dmesg 却找不到 /dev/tee0;CA 调 TEEC_InvokeCommand 返回 0xFFFF0006;SMC 在 EL3 直接 panic------根因往往不在应用层,而在 NS/S 世界划分、TZASC 内存隔离、TF-A BL31 调度、GIC Group 路由 没对齐。本文从 SoC 硬件安全组件讲起,经 TF-A 启动链、EL0--EL3 特权模型,追到 Linux drivers/tee/ → SMC → BL31 → OP-TEE → TA 的完整路径,并覆盖中断分流、设备树 binding 与现场排障。与《嵌入式安全完整篇》侧重 Secure Boot/IMA 互补:本篇专注 TrustZone + ATF + TEE 运行时架构

源码锚点

路径 作用
arm-trusted-firmware/bl1/ 首级 Bootloader(部分平台 ROM 直跳 BL2)
arm-trusted-firmware/bl2/ 加载 BL31/BL32/BL33 镜像、证书验签
arm-trusted-firmware/bl31/bl31_main.c EL3 Monitor 入口、runtime service 注册
arm-trusted-firmware/bl31/aarch64/runtime_svc_handlers.S SMC 陷入 EL3 的汇编入口
arm-trusted-firmware/services/spd/opteed/ OP-TEE Secure Payload Dispatcher
arm-trusted-firmware/services/std_svc/psci/ PSCI(CPU ON/OFF/SUSPEND)
arm-trusted-firmware/plat/<vendor>/<soc>/ 平台 TZC/TZPC/GIC/DRAM 布局
arm-trusted-firmware/docs/design/firmware-design.rst BL 阶段官方设计说明
optee_os/core/arch/arm/kernel/thread_a64.S Secure world 线程切换
optee_os/core/arch/arm/sm/sm.c OP-TEE 侧 SMC 处理入口
optee_os/core/tee/entry_std.c 标准 SMC 命令分发
optee_client/libteec/src/tee_client_api.c GlobalPlatform TEE Client API
linux/drivers/tee/tee_core.c TEE 子系统核心、/dev/tee*
linux/drivers/tee/optee/call.c OP-TEE 驱动 SMC 封装
linux/drivers/tee/optee/optee_smc.h OP-TEE SMC function ID 定义
linux/Documentation/devicetree/bindings/tee/linaro,optee-tz.yaml 传统 OP-TEE 设备树 binding
linux/Documentation/devicetree/bindings/firmware/arm,ffa.yaml FF-A 固件接口 binding
linux/arch/arm64/include/asm/exception.h EL 切换、异常向量相关
linux/arch/arm64/kernel/smccc-call.S 内核发起 SMC/HVC 的封装
linux/include/linux/arm-smccc.h SMCCC 调用约定宏

常用观测命令:

bash 复制代码
# REE 侧
ls -l /dev/tee* /dev/teepriv*
dmesg | grep -iE 'optee|tee|smc|ffa'
cat /sys/firmware/optee/uefi/mode 2>/dev/null
modinfo optee

# 设备树(运行态或反编译)
fdtdump /sys/firmware/fdt 2>/dev/null | grep -A5 optee
dtc -I fs -O dts /sys/firmware/fdt 2>/dev/null | grep -E 'optee|ffa|reserved-memory'

# OP-TEE 测试(需 tee-supplicant)
tee-supplicant &
xtest -l 15

GlobalPlatform Client API 最小调用骨架:

c 复制代码
TEEC_Context ctx;
TEEC_Session sess;
TEEC_Operation op;
TEEC_Result res;

res = TEEC_InitializeContext(NULL, &ctx);
res = TEEC_OpenSession(&ctx, &sess, &uuid,
                       TEEC_LOGIN_PUBLIC, NULL, NULL, &origin);
memset(&op, 0, sizeof(op));
op.paramTypes = TEEC_PARAM_TYPES(TEEC_MEMREF_TEMP_INPUT,
                                 TEEC_NONE, TEEC_NONE, TEEC_NONE);
res = TEEC_InvokeCommand(&sess, CMD_TEST, &op, &origin);
TEEC_CloseSession(&sess);
TEEC_FinalizeContext(&ctx);

调用链

TF-A 冷启动到 Linux + OP-TEE 驻留

#mermaid-svg-cfJ49acxAJKN2Jff{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-cfJ49acxAJKN2Jff .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-cfJ49acxAJKN2Jff .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-cfJ49acxAJKN2Jff .error-icon{fill:#552222;}#mermaid-svg-cfJ49acxAJKN2Jff .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-cfJ49acxAJKN2Jff .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-cfJ49acxAJKN2Jff .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-cfJ49acxAJKN2Jff .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-cfJ49acxAJKN2Jff .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-cfJ49acxAJKN2Jff .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-cfJ49acxAJKN2Jff .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-cfJ49acxAJKN2Jff .marker{fill:#333333;stroke:#333333;}#mermaid-svg-cfJ49acxAJKN2Jff .marker.cross{stroke:#333333;}#mermaid-svg-cfJ49acxAJKN2Jff svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-cfJ49acxAJKN2Jff p{margin:0;}#mermaid-svg-cfJ49acxAJKN2Jff .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-cfJ49acxAJKN2Jff .cluster-label text{fill:#333;}#mermaid-svg-cfJ49acxAJKN2Jff .cluster-label span{color:#333;}#mermaid-svg-cfJ49acxAJKN2Jff .cluster-label span p{background-color:transparent;}#mermaid-svg-cfJ49acxAJKN2Jff .label text,#mermaid-svg-cfJ49acxAJKN2Jff span{fill:#333;color:#333;}#mermaid-svg-cfJ49acxAJKN2Jff .node rect,#mermaid-svg-cfJ49acxAJKN2Jff .node circle,#mermaid-svg-cfJ49acxAJKN2Jff .node ellipse,#mermaid-svg-cfJ49acxAJKN2Jff .node polygon,#mermaid-svg-cfJ49acxAJKN2Jff .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-cfJ49acxAJKN2Jff .rough-node .label text,#mermaid-svg-cfJ49acxAJKN2Jff .node .label text,#mermaid-svg-cfJ49acxAJKN2Jff .image-shape .label,#mermaid-svg-cfJ49acxAJKN2Jff .icon-shape .label{text-anchor:middle;}#mermaid-svg-cfJ49acxAJKN2Jff .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-cfJ49acxAJKN2Jff .rough-node .label,#mermaid-svg-cfJ49acxAJKN2Jff .node .label,#mermaid-svg-cfJ49acxAJKN2Jff .image-shape .label,#mermaid-svg-cfJ49acxAJKN2Jff .icon-shape .label{text-align:center;}#mermaid-svg-cfJ49acxAJKN2Jff .node.clickable{cursor:pointer;}#mermaid-svg-cfJ49acxAJKN2Jff .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-cfJ49acxAJKN2Jff .arrowheadPath{fill:#333333;}#mermaid-svg-cfJ49acxAJKN2Jff .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-cfJ49acxAJKN2Jff .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-cfJ49acxAJKN2Jff .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-cfJ49acxAJKN2Jff .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-cfJ49acxAJKN2Jff .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-cfJ49acxAJKN2Jff .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-cfJ49acxAJKN2Jff .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-cfJ49acxAJKN2Jff .cluster text{fill:#333;}#mermaid-svg-cfJ49acxAJKN2Jff .cluster span{color:#333;}#mermaid-svg-cfJ49acxAJKN2Jff div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-cfJ49acxAJKN2Jff .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-cfJ49acxAJKN2Jff rect.text{fill:none;stroke-width:0;}#mermaid-svg-cfJ49acxAJKN2Jff .icon-shape,#mermaid-svg-cfJ49acxAJKN2Jff .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-cfJ49acxAJKN2Jff .icon-shape p,#mermaid-svg-cfJ49acxAJKN2Jff .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-cfJ49acxAJKN2Jff .icon-shape .label rect,#mermaid-svg-cfJ49acxAJKN2Jff .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-cfJ49acxAJKN2Jff .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-cfJ49acxAJKN2Jff .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-cfJ49acxAJKN2Jff :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 常驻 EL3
S-EL1
SMC
SPD opteed
Boot ROM / HW fuse
BL1 可选
BL2: 验签 + 加载镜像
BL31 @ EL3 Monitor
BL32 @ S-EL1: OP-TEE
BL33 @ NS-EL2/EL1: U-Boot/UEFI
Runtime Services: PSCI / SiP / SPD
OP-TEE OS + TA
Linux @ NS-EL1
drivers/tee/optee

REE 进 TEE 与 EL / 世界切换

#mermaid-svg-hGuWYu8jre0U5i25{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-hGuWYu8jre0U5i25 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-hGuWYu8jre0U5i25 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-hGuWYu8jre0U5i25 .error-icon{fill:#552222;}#mermaid-svg-hGuWYu8jre0U5i25 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-hGuWYu8jre0U5i25 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-hGuWYu8jre0U5i25 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-hGuWYu8jre0U5i25 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-hGuWYu8jre0U5i25 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-hGuWYu8jre0U5i25 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-hGuWYu8jre0U5i25 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-hGuWYu8jre0U5i25 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-hGuWYu8jre0U5i25 .marker.cross{stroke:#333333;}#mermaid-svg-hGuWYu8jre0U5i25 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-hGuWYu8jre0U5i25 p{margin:0;}#mermaid-svg-hGuWYu8jre0U5i25 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-hGuWYu8jre0U5i25 .cluster-label text{fill:#333;}#mermaid-svg-hGuWYu8jre0U5i25 .cluster-label span{color:#333;}#mermaid-svg-hGuWYu8jre0U5i25 .cluster-label span p{background-color:transparent;}#mermaid-svg-hGuWYu8jre0U5i25 .label text,#mermaid-svg-hGuWYu8jre0U5i25 span{fill:#333;color:#333;}#mermaid-svg-hGuWYu8jre0U5i25 .node rect,#mermaid-svg-hGuWYu8jre0U5i25 .node circle,#mermaid-svg-hGuWYu8jre0U5i25 .node ellipse,#mermaid-svg-hGuWYu8jre0U5i25 .node polygon,#mermaid-svg-hGuWYu8jre0U5i25 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-hGuWYu8jre0U5i25 .rough-node .label text,#mermaid-svg-hGuWYu8jre0U5i25 .node .label text,#mermaid-svg-hGuWYu8jre0U5i25 .image-shape .label,#mermaid-svg-hGuWYu8jre0U5i25 .icon-shape .label{text-anchor:middle;}#mermaid-svg-hGuWYu8jre0U5i25 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-hGuWYu8jre0U5i25 .rough-node .label,#mermaid-svg-hGuWYu8jre0U5i25 .node .label,#mermaid-svg-hGuWYu8jre0U5i25 .image-shape .label,#mermaid-svg-hGuWYu8jre0U5i25 .icon-shape .label{text-align:center;}#mermaid-svg-hGuWYu8jre0U5i25 .node.clickable{cursor:pointer;}#mermaid-svg-hGuWYu8jre0U5i25 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-hGuWYu8jre0U5i25 .arrowheadPath{fill:#333333;}#mermaid-svg-hGuWYu8jre0U5i25 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-hGuWYu8jre0U5i25 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-hGuWYu8jre0U5i25 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-hGuWYu8jre0U5i25 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-hGuWYu8jre0U5i25 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-hGuWYu8jre0U5i25 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-hGuWYu8jre0U5i25 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-hGuWYu8jre0U5i25 .cluster text{fill:#333;}#mermaid-svg-hGuWYu8jre0U5i25 .cluster span{color:#333;}#mermaid-svg-hGuWYu8jre0U5i25 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-hGuWYu8jre0U5i25 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-hGuWYu8jre0U5i25 rect.text{fill:none;stroke-width:0;}#mermaid-svg-hGuWYu8jre0U5i25 .icon-shape,#mermaid-svg-hGuWYu8jre0U5i25 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-hGuWYu8jre0U5i25 .icon-shape p,#mermaid-svg-hGuWYu8jre0U5i25 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-hGuWYu8jre0U5i25 .icon-shape .label rect,#mermaid-svg-hGuWYu8jre0U5i25 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-hGuWYu8jre0U5i25 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-hGuWYu8jre0U5i25 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-hGuWYu8jre0U5i25 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Secure World S-EL1
EL3 Secure Monitor
Non-Secure World
ioctl
SMC trap
SMC return
eret NS
用户 CA / libteec
/dev/tee0
optee driver call.c
arm_smccc_smc
runtime_svc_handlers.S
opteed_smc_handler
OP-TEE sm.c
Trusted Application

Linux 发起 SMC 的内核时序

OP-TEE S-EL1 BL31 EL3 smccc-call.S optee/call.c tee_core.c tee_client REE OP-TEE S-EL1 BL31 EL3 smccc-call.S optee/call.c tee_core.c tee_client REE #mermaid-svg-fd1FjGJVsfedrm2z{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-fd1FjGJVsfedrm2z .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-fd1FjGJVsfedrm2z .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-fd1FjGJVsfedrm2z .error-icon{fill:#552222;}#mermaid-svg-fd1FjGJVsfedrm2z .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-fd1FjGJVsfedrm2z .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-fd1FjGJVsfedrm2z .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-fd1FjGJVsfedrm2z .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-fd1FjGJVsfedrm2z .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-fd1FjGJVsfedrm2z .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-fd1FjGJVsfedrm2z .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-fd1FjGJVsfedrm2z .marker{fill:#333333;stroke:#333333;}#mermaid-svg-fd1FjGJVsfedrm2z .marker.cross{stroke:#333333;}#mermaid-svg-fd1FjGJVsfedrm2z svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-fd1FjGJVsfedrm2z p{margin:0;}#mermaid-svg-fd1FjGJVsfedrm2z .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-fd1FjGJVsfedrm2z text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-fd1FjGJVsfedrm2z .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-fd1FjGJVsfedrm2z .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-fd1FjGJVsfedrm2z .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-fd1FjGJVsfedrm2z .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-fd1FjGJVsfedrm2z #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-fd1FjGJVsfedrm2z .sequenceNumber{fill:white;}#mermaid-svg-fd1FjGJVsfedrm2z #sequencenumber{fill:#333;}#mermaid-svg-fd1FjGJVsfedrm2z #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-fd1FjGJVsfedrm2z .messageText{fill:#333;stroke:none;}#mermaid-svg-fd1FjGJVsfedrm2z .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-fd1FjGJVsfedrm2z .labelText,#mermaid-svg-fd1FjGJVsfedrm2z .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-fd1FjGJVsfedrm2z .loopText,#mermaid-svg-fd1FjGJVsfedrm2z .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-fd1FjGJVsfedrm2z .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-fd1FjGJVsfedrm2z .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-fd1FjGJVsfedrm2z .noteText,#mermaid-svg-fd1FjGJVsfedrm2z .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-fd1FjGJVsfedrm2z .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-fd1FjGJVsfedrm2z .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-fd1FjGJVsfedrm2z .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-fd1FjGJVsfedrm2z .actorPopupMenu{position:absolute;}#mermaid-svg-fd1FjGJVsfedrm2z .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-fd1FjGJVsfedrm2z .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-fd1FjGJVsfedrm2z .actor-man circle,#mermaid-svg-fd1FjGJVsfedrm2z line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-fd1FjGJVsfedrm2z :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} TEE_IOC_OPEN_SESSIONoptee_open_session()arm_smccc_smc(OPTEE_SMC_*)SMC 陷入 EL3opteed 转发加载/调度 TA返回值 x0-x3eret 到 NS-EL1结果寄存器tee_ioctl 完成返回用户态

重点知识

TrustZone 硬件:Secure / Non-Secure 与世界边界

嵌入式 Linux 上跑支付、DRM 密钥、设备身份、OTA 验签私钥时,REE(Rich OS,通常是 Linux) 攻击面太大:任意内核漏洞、恶意驱动、物理 DMA 都可能读到明文密钥。ARM TrustZone 在硬件上切分出 Secure WorldNon-Secure World,让敏感计算与密钥材料只在 Secure 侧可见;REE 只能通过受控网关(SMC)请求服务,不能直接 mmap Secure DRAM。

TrustZone 不是加密加速器,而是 隔离与访问控制 机制:CPU、总线、部分外设、DRAM 区域都可带 NS 属性。理解后续 TZASC/TZPC、EL3 Monitor、OP-TEE,都建立在这个边界上。

概念 含义
Non-Secure (NS) 普通 Rich OS、应用、大部分驱动;内存访问带 NS=1
Secure (S) TEE OS(OP-TEE)、TA、部分 EL3 固件关联逻辑;NS=0
世界切换 通过 SMC 进入 EL3 Monitor,Monitor 改 SCR_EL3.NS 等状态后 eret 到目标世界
同核复用 同一物理 CPU 在某一时刻只属于一个世界;切换时保存/恢复上下文

软件上常把 REE = NS 世界的 Rich Execution Environment (Linux + user space),TEE = Secure 世界的 Trusted Execution Environment (OP-TEE + TA)。BL31 运行在 EL3,是 Monitor,负责在两个世界之间仲裁,不完全等同于 Secure World 里的应用。

ARMv8-A 中,处理器、MMU/TLB、缓存、总线主端口都会携带 Non-secure bit 。当 CPU 处于 NS 状态访问被标记为 Secure 的内存或寄存器时,硬件应返回 abort 而非静默泄露------这是 TZASC 与 MMU 阶段配合的基础。在 EL3,SCR_EL3.NS 决定 eret 返回后 处理器进入 NS 还是 S 视图;BL31 在处理完 OP-TEE 的 SMC 后,根据调用来源与返回目标设置该位,再执行 eret 回到 Linux(NS-EL1)。

Cortex-M 的 IDAU/SAU 是 TrustZone-M 另一套地址划分,与 A 核 TZASC 概念类似但寄存器完全不同。STM32MP1 A7 + M4 异构时,M4 侧 TrustZone-M 与 A 核 OP-TEE 通过 resource table / mailbox 协作,不要混用寄存器说明。

现场判断 TrustZone 是否生效:

bash 复制代码
grep -i optee /proc/devices
dmesg | grep -iE 'smccc|psci|optee'

若 ROM 未 fuse TrustZone、或 BL2 未加载 BL32,硬件能力存在但 TEE 运行时缺席------这与「TZ 未开」是不同层面的问题。

TZASC、TZPC 与内存 / 外设隔离

DRAM 控制器与 CPU 之间往往还有 NIC-400 / NIC-301 等互连,TZASC 挂在物理地址解码路径上:当 NS master(Linux DMA、CPU NS 访问)试图读写 Secure-only region 时,总线层直接 SLVERR/DECERR ,不会落到 OP-TEE MMU 才拒绝------这是硬件第一道防线。OP-TEE 自身 MMU 是 Secure 侧第二道:即使 TZASC 配错允许 NS 读 Secure PA,OP-TEE 仍可用 S-EL1 页表 限制 TA 可见范围,但不应依赖软件补救硬件配错。

平台初始化典型调用链(以 TZC-400 为例,名称因 plat 而异):

复制代码
BL2 plat_arch_setup()
  └── tzc400_init()
        ├── tzc400_disable_filters()
        ├── tzc400_configure_region(0, ... )  /* 通常 region0 覆盖全空间默认拒绝 */
        ├── tzc400_configure_region(n, base, top, SECURE_ONLY)
        └── tzc400_enable_filters()

OP-TEE core/arch/arm/plat-*/main.c 可能在 init_primary_helper 里再次确认 TZC 与 CFG_TEE_RAM_START 一致。若 BL2 把 OP-TEE 加载地址划在 NS 可访问区,启动早期就会 data abort。

TZPC 作用在外设端口:例如某 UART 或 TRNG 的 APB 从端口带 NSACCESS 信号,TZPC 寄存器决定该端口对 NS 是否可见。与 TZASC 正交:TZASC 管 PA 段,TZPC 管设备端口。MPC/PPC 是 AMBA-5 体系下更细粒度的命名,思路上仍属于「内存侧 / 外设侧」两类隔离。

i.MX8 系列常见 plat_tzc380.c ;Rockchip rk3399plat/rk3399/drivers/soc/soc.c 附近配置 trustzone;STM32MP1 有 stm32mp1_syscfg.c 配合 ETZPC。读代码时搜 tzc/tzpc/etzpc 关键字,不要假设所有 SoC 都用 TZC-400 IP。

TZASC(TrustZone Address Space Controller)DRAM 物理地址空间 上划窗口,规定某段 PA 仅 Secure 可访问、或 Secure/NS 均可访问但属性不同。典型场景:OP-TEE 核心与 TA 堆必须在 Secure-only 区域;Linux 普通 DMA 缓冲区在 NS 区域。

要点 说明
Region 配置 region 配置起止地址与权限;region 数量与对齐粒度 因 SoC 而异
初始化时机 通常在 BL2/BL31 平台代码 或 OP-TEE 早期初始化完成,Linux 不应随意改 TZASC
配错表现 Linux heap 放到 Secure-only 区 → data abort 或 hang;OP-TEE 区被 NS 踩 → TEE 随机崩溃

不要编造寄存器细节 :各厂商 TZASC 寄存器名、region 索引不统一。读 plat/<vendor>/plat_tzc400.cplat_tzc380.c 等平台文件,不要凭记忆写固定 MMIO 地址。

组件 典型作用
TZASC / TZC-400 DRAM 区域 S/NS 访问控制
TZPC 外设或 APB 从设备 S/NS 划分;配合 decprotnset 类信号
MPC AMBA 系统上类似 TZASC 的内存保护单元
PPC 外设端口保护

Boot 链早期会把 UART、SDHCI、Crypto 引擎 等划给 NS 或 S。Linux 驱动只能访问 NS 外设;若某 crypto 引擎被划为 Secure-only,REE 里不应再出现对应 platform driver,而应通过 TEE TA 间接调用。

STM32MP、i.MX、Rockchip、Amlogic 等平台在 TF-A plat/ 目录下都有 trustzone 初始化 文件,命名可能是 stm32mp1_tz.crk3399_pm_ops.c 等------以具体 SoC 为准。

排障 TZASC 相关 abort:

bash 复制代码
# 对比 TF-A 与 OP-TEE 内存布局文档
grep -r 'TZC\|TZASC\|DRAM' arm-trusted-firmware/plat/<soc>/
grep -r 'CFG_TEE_RAM\|CFG_TA_RAM' optee_os/core/arch/arm/plat-<soc>/

U-Boot fdtreserved-memory 必须与 TZASC region 一致;NS Linux 不能占用 Secure-only PA。

REE 与 TEE:职责、边界与 tee-supplicant

边界原则可以概括成一句话:REE 编排业务与 I/O,TEE 保管密钥与敏感原语 。CA(Client Application)跑在 REE user space,通过 GlobalPlatform Client API 发请求;TA 跑在 S-EL1,通过 Internal API 访问 crypto、storage、OTP。REE 内核 never 直接读 Secure 物理页;它只能通过 /dev/tee0 ioctl 触发 world switch,让 OP-TEE 在 Secure 侧代劳。

能力 REE 典型做法 TEE 典型做法
AES 加解密 调 TA,密钥不出 Secure TA 内 TEE_AllocateTransientObject
设备唯一密钥 只拿公钥/证书 HUK 派生,PTA 导出受限
文件加密 文件在 ext4,密钥在 TEE Secure Storage object
OTA 验签在 REE(dm-verity) 可选:TA 验签 bootloader 参数

tee-supplicant 与内核 optee/rpc.c 配合:OP-TEE 返回 OPTEE_SMC_RETURN_RPC 时,驱动唤醒 supplicant 处理 OPTEE_RPC_CMD_LOAD_TAFSSHM 等命令。内核路径 optee_rpc_cmd → 用户态 tee_supplicant → 读 /lib/optee_armtz/*.ta 或 socket。systemd 单元通常 After=multi-user.target ,若 CA 在 supplicant 前启动会 TEEC_ERROR_BAD_STATE (0xFFFF0006)

REE(Rich Execution Environment) 指 NS 世界里的「完整 Rich OS」栈:

层次 典型软件
User 银行 App、CA(libteec)、tee-supplicant
Kernel Linux、optee 驱动、普通设备驱动
Boot U-Boot / UEFI(BL33)
可选 KVM Hypervisor @ NS-EL2

REE 负责 UI、网络、文件系统、通用驱动;不应持有长期明文 DRM key、设备唯一私钥。REE 可以发起 TEE 会话,让 TA 在 Secure 内存里完成加解密签名,只返回结果或句柄。

TEE(Trusted Execution Environment) 是 Secure 世界里的执行环境,常见实现为 OP-TEE OS(BL32 @ S-EL1)

组件 说明
OP-TEE core 调度、MMU、IPC、crypto 框架
TA(Trusted Application) 签名 ELF,UUID 标识,类似 Secure 侧「进程」
PTA Pseudo-TA,内核态 TA,提供设备密钥等
Secure driver 访问 Secure 外设或 on-chip OTP

TEE 提供 隔离、证明(attestation)、安全存储 ;GlobalPlatform TEE Internal Core API 定义 TA 编程模型。TA 与 CA 之间通过 共享内存 + 命令 ID 传参,参数类型由 TEEC_ParamTypes 编码。

tee-supplicant 是 REE 用户态守护进程:OP-TEE 需要 fopen、RPC 到 REE 加载 TA 文件、socket 等 Rich OS 服务 时,通过 supplicant 完成。没有 supplicant,TEEC_OpenSession 可能挂起或失败。

bash 复制代码
# 确认 supplicant 运行
pgrep -a tee-supplicant
systemctl status tee-supplicant.service

# TA 安装路径
ls /lib/optee_armtz/

与 TPM / SE 的关系:TPM /dev/tpm0 在 REE;TEE sealed storage 可绑定 PCR。ATECC608 等 SE 经 I2C + PTA 发 APDU,私钥 never export。典型产品:DRM (Widevine TA)、IoT 身份 (HUK → PTA → 设备 cert)、支付(PIN 在 TA 校验)。

TF-A BL 启动链、FIP 与各阶段职责

BL 命名来自 ARM Boot Loader stage 约定,并非所有平台都实现 BL1。许多 SoC Boot ROM → BL2 直链:ROM 验签 BL2 后把 CPU 留在 EL3 或 S-EL1,由 BL2 完成 DDR training、TrustZone 初始化、镜像加载。BL2 结束时通过 bl2_run_next_image() 依次跳转 BL31、注册 BL32 entry、再跳 BL33。

FIP 内部是 UUID tagged TOC(Table of Contents) 条目,常见 UUID(见 tools/fiptool/fiptool.c):

UUID 名 内容
TB_FW_CONFIG Trusted Boot 固件配置
BL31 EL3 Monitor 镜像
BL32 Secure Payload(OP-TEE)
BL33 Non-secure Bootloader
SCP_BL2 部分平台 SCP 固件

打包示例:

bash 复制代码
make PLAT=fvp SPD=opteed BL32=tee-header_v2.bin BL32_EXTRA1=tee-pager_v2.bin \
     BL32_EXTRA2=tee-pageable_v2.bin BL33=bl33.bin all fip

OP-TEE 在 FIP 里常拆成 header + pager + pageable 三段,BL2 按 OP-TEE 头部 struct optee_header 解析 load address 与 size。U-Boot 打包 u-boot.itb 与 FIP 是两层:FIP 由 BL2 消费,FIT 由 BL33 消费。

ARM Trusted Firmware-A(TF-A) 是 ARMv8-A 开源参考固件,实现 EL3 Secure Monitor 、PSCI、SiP 服务、Secure Payload 调度。商业板 BSP 里的 bl31.binatf.bin 通常即 TF-A 构建产物。

阶段 典型 EL 职责
BL1 EL3/ROM 部分平台存在:ROM 验签后跳 BL2;许多 SoC ROM 直载 BL2
BL2 S-EL1 初始化 DDR、加载 BL31/BL32/BL33、验证证书链(Trusted Board Boot)
BL31 EL3 常驻 Monitor:PSCI、SMC 分发、world switch
BL32 S-EL1 Secure Payload:OP-TEE 或 TEE OS
BL33 NS-EL1/EL2 U-Boot / UEFI / GRUB,再链式加载 Linux

FIP(Firmware Image Package) 常把 BL31/BL32/BL33 打在一个包里,BL2 解析:

bash 复制代码
fiptool info fip.bin
fiptool unpack fip.bin
# 确认 BL32(OP-TEE)条目非空

U-Boot 命令 booti 启动的 kernel 已是 NS 世界;OP-TEE 在 BL2 阶段已驻留 Secure RAM,不会随 Linux 再加载一次(除非 REE 动态 reload 场景,较少见)。

TF-A 与 OP-TEE、Linux 关系:

复制代码
TF-A BL31  → EL3 Monitor,不替代 TEE
TF-A BL32  → 常部署 OP-TEE(也可其他 SPD)
OP-TEE     → TEE OS,处理 GP SMC
Linux      → REE,通过 optee 驱动 SMC 进 TEE

构建示例(以 RK3399 为例,具体平台名替换):

bash 复制代码
make PLAT=rk3399 SPD=opteed BL32=optee BL33=u-boot.bin all fip

bl31/bl31_main.c 冷启动顺序:bl31_setupbl31_lib_initruntime_svc_registerbl31_prepare_next_image_entry → 进入 BL33。对照 docs/design/firmware-design.rst 「Cold boot」章节。

Secure Boot 与 ATF 的交界

Secure Boot 解决「镜像是否被篡改」;TrustZone 解决「运行期隔离」。二者在 BL2 交汇:BL2 用 ROTPK 验签 BL31/BL32/BL33 证书。验签失败则不应跳转到恶意 BL31。

TF-A 中 auth/plat/common/tbb/ 等目录实现 Trusted Board Boot。U-Boot 侧 FIT 签名验证是 BL33 之后 的下一环,与 BL2 证书链不同层。排障「Linux 起不来」与「TEE 不存在」要分开:前者查 BL33/kernel;后者查 BL32 是否加载、DT 是否声明 optee。

环节 验证对象 失败表现
BL2 TBB BL31/BL32/BL33 证书 卡在 BL2,无串口 BL31 日志
U-Boot FIT kernel/dtb/initrd U-Boot 拒绝 booti
OP-TEE TA 签名 .ta 加载 OpenSession 失败,origin=TEE OS

Secure Boot 不自动保证 TEE 可用:即使 BL33 验签通过,若 FIP 未含 BL32 或 DT 无 optee 节点,Linux 仍无 /dev/tee0。产线流程常见:fuse ROTPK → 写 HUK → 灌 .ta → xtest

与本文边界:Secure Boot 链式验签、IMA/EVM、dm-verity 等 REE 完整性机制另文详述;本篇只强调 BL2 验签通过后 BL32 必须正确驻留 ,且 OP-TEE 版本与 kernel 驱动匹配

BL2 验签链在 TF-A 里由 auth_mod 驱动:auth_img_verify() 逐级验证书与哈希;TBBR_* 宏启用 Trusted Board Boot。ROT 公钥 hash 可 fuse 到 OTP ,与 U-Boot CONFIG_FIT_SIGNATURE 使用的 key 不是同一层------后者保护 kernel 镜像,前者保护 BL31/BL32/BL33。现场若更换 OP-TEE 但未重签 FIP,BL2 阶段即失败,Linux 根本起不来,更谈不上 TEE 驱动 probe。

EL0--EL3 特权级与 NS-EL1 / S-EL1 正交

ARMv8-A Exception Level 数值越高特权越大:

EL 典型软件 能力概要
EL0 用户态 App、CA 无特权指令;通过 SVC 进内核
EL1 Linux kernel、OP-TEE OS MMU、大部分系统寄存器;NS/S 各有一套「EL1」
EL2 Hypervisor(KVM、Xen) 虚拟化扩展;管理 Guest EL1/EL0
EL3 Secure Monitor(BL31) 最高特权;SMC 目标;控制 SCR_EL3

关键 :EL 与 World 是正交概念。NS-EL1 (Linux)与 S-EL1(OP-TEE)特权级相同,但 NS bit 不同,可见内存与外设不同。

场景 Linux 所在 说明
嵌入式常见 NS-EL1 无 KVM;BL33 直接 U-Boot → Linux
虚拟化 Guest NS-EL1,Host NS-EL2 SMC 可能 trap 到 Host 再转发(复杂)
OP-TEE S-EL1 与 Linux 同级不同世界

启用 KVM 时,Guest 里访问 TEE 需 代理驱动;多数嵌入式方案让 Linux 跑 Host EL2 或干脆不开 EL2。

读当前 EL(调试,需未锁 debug):

bash 复制代码
# 内核态
grep -i el /proc/cpuinfo   # 仅 REE 视图
# 汇编
mrs x0, CurrentEL          # EL[3:2] = CurrentEL >> 2

PSCI 也走 SMC,由 BL31 psci 服务处理:CPU_ONCPU_OFFSYSTEM_SUSPEND。Secondary CPU 启动:Linux cpu_up() → PSCI SMC → BL31 让核从 secure 或 NS entry 启动,需与 OP-TEE CFG_BOOT_SECONDARY_REQUEST 等平台选项一致。若 CPU1 不起,查 dmesg psci 与 TF-A PSCI 日志,不要先怀疑 TA。

S-EL1 与 NS-EL1 的 异常向量表独立 :Linux 在 entry.SVBAR_EL1 ;OP-TEE 在 core/arch/arm/kernel/entry_a64.S 设 Secure VBAR_EL1 。从 NS 发 SMC 进 EL3 再进 S-EL1 时,不会经过 Linux 的 el0_svc 路径------这是两条完全独立的 trap 路径。

Hypervisor(EL2) 存在时,HCR_EL2.TSC 可 trap SMC 到 EL2 由 KVM 模拟或转发;嵌入式无 EL2 时 SMC 直达 EL3。读 /sys/hypervisor/type 可知是否在 VM 内------Guest 内通常 没有真 OP-TEE,除非 Host 做 passthrough。

AArch32 兼容层(CONFIG_COMPAT )下,32 位 CA 仍通过 /dev/tee0 ioctl,内核 compat_tee_ioctl 做结构体 padding 转换;OP-TEE 侧仍 AArch64 SMC,与 CA 位宽无关。

寄存器 bank、SCR_EL3 与世界切换控制

同一 EL 在不同世界访问 同名系统寄存器 时,硬件可能提供 banked copy因 NS 属性而 trap

寄存器 说明
VBAR_EL1 Linux 与 OP-TEE 各有独立向量基址
TTBR*_EL1 各自页表,Secure/Non-secure 转换表分离
SCR_EL3 仅 EL3 可写,控制 NS、IRQ/FIQ 路由到哪个世界
SPSR_EL3 / ELR_EL3 保存 SMC 陷入前 NS-EL1 的 PSTATE 与返回 PC

SCR_EL3 关键位(概念,以 ARM DDI 0406 为准):

作用
NS 1=Non-secure,0=Secure;eret 后世界
IRQ/FIQ 控制中断在 Monitor 与 lower EL 间路由
EA 外部 abort 路由策略

SMC 陷入 EL3 时,runtime_svc_handlers.S 保存 NS 上下文到 cpu_context 结构;opteed_enter_sp 切换进 S-EL1 前再保存一份。返回时必须:

  1. 恢复 NS-EL1 的 elr_el3spsr_el3
  2. 设置 SCR_EL3.NS = 1
  3. eret 回到 Linux 被中断的指令流

OP-TEE 若 secure panic,BL31 可能无法干净返回,表现为 Linux hard lockup 或 RCU stall。调试时开 OP-TEE CFG_TEE_CORE_LOG_LEVEL 与 TF-A LOG_LEVEL

/proc/cpuinfo 只能看到 REE 视图;OP-TEE 内部状态需 OP-TEE core 日志 或 JTAG(若 secure debug 未 fuse)。

SVC、HVC、SMC 与 SMCCC 调用约定

指令 典型用途 陷入 EL
SVC 用户态系统调用 → Linux kernel EL1(NS)
HVC 虚拟化 hypercall EL2
SMC Secure Monitor 调用 EL3

CA 不会 直接发 SMC;libteecioctl(/dev/tee0)optee 驱动arm_smccc_smc()。这是故意设计:内核可审计、可映射共享内存、可限制并发。

ARM SMCCC 规定 SMC 参数:

  • x0:Function ID(含 bit31:30 实体类型:fast/standard/32-bit 等)
  • x1--x3:参数
  • x4--x7:额外参数或返回
  • 返回:x0 为主返回值,OP-TEE 还用 x1--x3 传会话信息

Function ID 分区(概念):

范围 所有者
Standard ARM 服务 PSCI 等
SiP Service 硅厂自定义(DDR training、特殊时钟)
OEM 板厂
Trusted OS/TEE OP-TEE fast/standard calls

Linux include/linux/arm-smccc.h

c 复制代码
#define arm_smccc_smc(...)     arm_smccc_1_2(SMCCC_SMC_INST, __VA_ARGS__)

展开为 smc #0 + clobber list。驱动里应使用宏而非裸写 magic number。

linux/arch/arm64/kernel/smccc-call.S 提供 __arm_smccc_smc 入口,保存 callee-saved 寄存器,执行 SMC,把 x0--x3 写回结果结构体。

SMC 类型 特点 示例
Fast 不切换完整 OS 线程上下文 UID、capabilities
Standard 可调度 OP-TEE thread OpenSession、Invoke

Linux driver 对 session 操作一律走 standard;滥用 fast call 可能导致 OP-TEE 状态不一致。

Linux 进 TEE 全链路:libteec 到 Trusted Application

tee_client_api.cTEEC_InitializeContext 最终 open("/dev/tee0")TEEC_OpenSession 组装 struct tee_ioctl_open_session_arg ,UUID 占 16 字节。内核 tee_ioctl_open_sessiontee_core.c 里分配 struct tee_shm 存放 optee_msg_arg ,物理地址经 SMC 传给 OP-TEE------这是 非安全共享缓冲 ,OP-TEE 用 MOBILE_MEM/NONSECURE 属性映射只读/读写。

call.coptee_do_call_with_arg 核心逻辑:

c 复制代码
/* 概念流程,见 linux/drivers/tee/optee/call.c */
param.a0 = OPTEE_SMC_CALL_WITH_ARG;
param.a1 = virt_to_phys(arg);
param.a2 = optee->sec_cap;
arm_smccc_smc(a0, a1, a2, a3, a4, a5, a6, a7, &res);

smccc-call.Ssmc #0 触发 sync exception to EL3 ;硬件自动:PSTATE → SPSR_EL3LR → ELR_EL3SPSR.M4 NS=1 的返回态被保存。BL31 handler 查 SMC_FUNCTION_ID 高位判定 standard/fast,OP-TEE 范围进 opteed_smc_handler

OP-TEE entry_std.centry_open_session 根据 UUID 找已加载 TA 或触发 ldelf 加载;entry_invoke_commandoptee_msg_param 转成 TEE_Param 调 TA。origin 回填路径:驱动 → libteec → CA,便于区分 COMMS / TEE OS / TA 哪层失败。

完整调用链(与 mermaid 对应):

步骤 1:用户 CA

TEEC_InitializeContext() 打开 /dev/tee0

路径:optee_client/libteec/src/tee_client_api.c

步骤 2:ioctl 进入内核

TEEC_OpenSession()TEE_IOC_OPEN_SESSION,携带 UUID。

路径:linux/drivers/tee/tee_core.ctee_ioctl()

步骤 3:optee 驱动组 SMC 参数

分配 optee_msg_arg (共享内存),填 cmduuid、operation。

路径:linux/drivers/tee/optee/call.coptee_do_call_with_arg()

步骤 4:arm_smccc_smc

汇编 smccc-call.S 执行 smc #0,CPU 进 EL3。

路径:linux/arch/arm64/kernel/smccc-call.S

步骤 5:BL31 分发

runtime_svc_handlers.Sopteed_smc_handler()

路径:arm-trusted-firmware/services/spd/opteed/opteed_main.c

步骤 6:OP-TEE 入口

thread_handle_std_smc() / tee_entry_std() 解析 arg,加载 TA。

路径:optee_os/core/arch/arm/sm/sm.ccore/tee/entry_std.c

步骤 7:TA 执行

TA_InvokeCommandEntryPoint() 处理 CMD,读写 memref 参数。

optee_smc.h 与 OP-TEE 侧 optee_os/core/arch/arm/include/sm/optee_smc.h 应对齐:

c 复制代码
#define OPTEE_SMC_CALLS_COUNT       /* UID / 能力查询 */
#define OPTEE_SMC_CALL_WITH_ARG     /* open session / invoke 等 */

Standard call 用于带 optee_msg_arg 的复杂操作;fast call 用于轻量查询。具体数值以当前 LTS 内核与 OP-TEE 版本配对为准,升级 BL32 或 kernel 时需 成对升级

opteed_main.c 读码顺序:opteed_initopteed_smc_handler(switch function ID)→ opteed_enter_spopteed_cpu_on(secondary)。

任一步失败都会在 CA 看到 TEEC_ERROR_* 或 origin 指出来自 TEE OS / COMMS / Trusted App

共享内存、RPC 与从 Secure 世界返回 NS

Linux tee_shm_pool 两种来源:DT reserved-memoryno-map,物理固定)或 dynamic dma_alloc 。固定池优点是多核、suspend/resume 后 PA 不变;动态池部署简单但需 OP-TEE 支持 REGISTER_SHM SMC。optee_msg_argoptee_msg_param 布局在内核 optee_smc.h 与 OP-TEE core/include/optee_msg.h 必须一致------这是版本锁的重点之一。

RPC 循环典型时序:

复制代码
NS: InvokeCommand ioctl
  → SMC → OP-TEE: 需要读 /lib/optee_armtz/xxx.ta
  → SMC return RPC LOAD_TA
  → optee_rpc_load_ta → wake supplicant
  → supplicant 读文件写入 shm
  → SMC 再进 OP-TEE 继续加载
  → 最终 SMC return OK 回到 NS

一次 open session 若 TA 未缓存,可能 多次 world switch 。性能调优:预装 TA 、增大 CFG_SHMEM_SIZE 、避免在 invoke 里频繁 TEEC_MEMREF_TEMP 拷贝大 buffer------改用 TEEC_RegisterSharedMemory

从 Secure 返回时 EL3 恢复 ctx->cpu_context 里 NS x0-x30sp_el0elr_el3 。若 OP-TEE 在 standard call 里开了中断且 secure IRQ 抢占,opteed 必须保证 SCR_EL3.NS 在 re-enter NS 前为 1,否则 Linux 会在 S 视图跑 NS 代码------极难调试的 silent corruption。

OP-TEE 与 Linux 之间大量交互通过 非安全共享内存(NS buffer 注册给 Secure 侧):

  1. 驱动分配 dma_alloc_coherent 或与 OP-TEE 预留区 carveout
  2. 物理地址通过 SMC 告诉 OP-TEE
  3. TA 通过 TEE_ParamType memref 访问(OP-TEE 做 NS 映射与安全检查)

tee-supplicant RPC :当 TA 要读 REE 文件系统上的 TA 二进制,OP-TEE 发 RPC 到 NS,supplicant 读文件后写回共享缓冲。缺 supplicant 时常见 timeout0xFFFF000C(comm error)。

OP-TEE thread_std_smc 处理完标准 SMC 后,若需 REE 服务,走 thread_return_from_rpc 先回到 NS,supplicant 完成后再 SMC 进 Secure 继续。

返回路径是进入的逆序,但 EL3 必须保证 NS 上下文完整恢复。一次 InvokeCommand 典型 数百微秒到毫秒 (world switch + cache 维护)。高频 crypto 应在 TA 内 batch,避免 per-byte SMC。

NS shared buffer 应映射为 Normal memory ;Device 映射做 shm 会 fault 或极慢。never/dev/mem mmap secure PA------CPU abort 或读到 garbage。

Docker 访问 TEE 需 --device /dev/tee0 且 host tee-supplicant 运行;SELinux Enforcing 需允许 tee 设备 ioctl。

最小 TA 验证整条 SMC 链:

c 复制代码
/* optee_os/ta/hello_world/ta.c */
TEE_Result TA_InvokeCommandEntryPoint(..., uint32_t cmd) {
    IMSG("Hello from Secure world cmd=%u", cmd);
    return TEE_SUCCESS;
}

编译安装 .ta/lib/optee_armtz/tee-supplicant &xtest 或自定义 CA 发 CMD。

BL31 runtime services 与 opteed Secure Payload Dispatcher

bl31/bl31_main.c 完成:

  1. 平台早期初始化(GIC、控制台、TZC)
  2. 注册 runtime service:PSCI、Standard SIP、OP-TEE SPD 等
  3. 将 EL 降到 BL33 入口(NS)或等待 secondary CPU 通过 PSCI 上线

SMC 从 NS 进入时,runtime_svc_handlers.S 根据 function ID 查表跳转到 psci_smc_handleropteed_smc_handler。未注册 ID 返回 SMCCC UNKNOWN

Secure Payload Dispatcher(SPD) 是 BL31 里专管某一 Secure OS 的模块。OP-TEE 对应 services/spd/opteed/

  • 处理 OP-TEE 标准 SMC(fast/standard call)
  • 维护 OP-TEE 与 NS 世界切换时的 CPU 上下文
  • 与 OP-TEE 约定 entry point、共享内存、CPU offline 通知

若 TF-A 编译时未启用 SPD=opteed 或 OP-TEE 头信息不匹配,Linux 侧 SMC 可能直接失败。

opteed_enter_sp 逻辑概要:保存 NS elr/spsr/spoptee_context ,设 S-EL1 entryeret 进 OP-TEE std_smc_entry

PSCI 与 OP-TEE SMC 使用 不同 function ID 空间,由 BL31 统一分发,互不覆盖。

反汇编 BL31 SMC 表:

bash 复制代码
aarch64-none-elf-objdump -d bl31.elf | less
# 搜 runtime_svc 或 smc_handler
grep -r 'opteed_smc' build/<plat>/release/bl31/bl31.map

建议源码走读顺序:bl31/bl31_main.copteed_main.coptee_os/core/arch/arm/sm/sm.clinux/drivers/tee/optee/call.coptee_client/libteec/src/tee_client_api.c

OP-TEE 内核架构与 SMC 处理路径

OP-TEE OS 目录 optee_os/core/arch/arm/ 是 ARM 移植核心:

路径 作用
kernel/thread_a64.S 线程上下文切换、异常返回
sm/sm.c SMC 入口分发
mm/core_mmu.c Secure 侧页表
plat-*/main.c 平台早期 init、UART

sm.c 收到 standard SMC 后调用 thread_handle_std_smc ,最终进入 tee_entry_std()core/tee/entry_std.c),根据 optee_msg_arg->cmd 执行 OPEN_SESSION、INVOKE_COMMAND、CLOSE_SESSION 等。

TA 生命周期:

  1. 加载 :从 REE 文件系统经 RPC 读 .ta,验签后映射到 Secure RAM
  2. 会话:UUID 匹配,创建 session context
  3. 调用TA_InvokeCommandEntryPoint 处理业务
  4. 卸载:session 关闭后回收

PTA (Pseudo-TA)在内核态提供 device.key、RPMB 等,CA 通过相同 GP API 访问但 UUID 固定。

配置项(optee_os/conf.mk 或平台 defconfig):

配置 含义
CFG_TEE_CORE_LOG_LEVEL 日志级别
CFG_TEE_RAM_VA_SIZE TEE 核心 RAM
CFG_TA_RAM_SIZE TA 堆
CFG_SHMEM_SIZE 与 NS 共享内存
CFG_BOOT_SECONDARY_REQUEST 从核启动配合 PSCI

OP-TEE 若 secure panic,串口 secure 日志(若启用)会打印 core/data-abortpanic ,与 Linux dmesg 分离------联合调试常用 两路 UART(NS Linux + secure log)。

ldelf (Load ELF)在 optee_os/ldelf/ ,负责 TA 重定位与 PT_LOAD 段映射;TA 签名由 scripts/sign_encrypt.py 与平台 key 决定。开发 TA 时 export TA_DEV_KIT_DIR=$(optee_os/out/arm/export-ta_arm64) ,Makefile 包含 mk/ta_dev_kit.mk

GlobalPlatform Internal API 与 Client API 分层:CA 只应链接 libteec ;TA 链接 libutee + libutils。混用头文件会导致参数类型在 REE/Secure 边界语义错误。

GIC Group0 / Group1 中断分流与安全路由

GICv2/v3 把中断分为 Group 0 / Group 1(v3 还有 Group 2 扩展概念):

Group 典型路由
Group 0 Secure 或需 Monitor 处理;可配置为 FIQ 进 EL3
Group 1 Non-secure,Linux handle_arch_irq 正常路径

安全外设中断 (Secure watchdog、部分 crypto)应在 Group 0,由 OP-TEE 或 BL31 处理;普通设备在 Group 1 给 Linux。

TF-A plat_gic.c 初始化 GIC 分组;OP-TEE drivers/gic 处理 secure interrupt。若 TZ 配错,可能出现 Linux 收不到中断secure 中断 storm 导致 NS 饿死

Historically FIQ 可路由到 Monitor / Secure OS,IRQ 给 NS Linux。具体路由由 SCR_EL3.AWK/FIQ 与 GIC 配置决定。不同 TF-A 版本与平台默认策略不同,以 plat/<soc>/platform.mk 与 GIC init 为准。

Linux 侧只管理 NS Group;读 GICD_IGROUPR 等 secure 寄存器需 EL3 或 debug 接口,不要在 NS 驱动里随意写 GIC secure 寄存器

排障中断异常:

bash 复制代码
# Linux 侧可见的中断统计
cat /proc/interrupts
# 若某设备 probe 成功但无中断,查 DT interrupt-parent 与 GIC group

多媒体 protected buffer 场景需 secure heapTZASC 专用 region;VPU firmware 有时跑 S-EL2 或 TA 内------与 GIC 路由、TZPC 外设划分一并规划。

Linux drivers/tee/ 子系统架构

除 OP-TEE 外,同一框架还支持 drivers/tee/amdtee/ 等其它 provider;接口统一为 struct tee_driver_opsopen/close/invoke/get_version 。字符设备 tee_core 注册 major 249(常见), /dev/tee0 world-shared,/dev/teepriv0 仅 supplicant 用于 RPC。

ioctl 命令(linux/include/uapi/linux/tee.h):

ioctl 用途
TEE_IOC_VERSION 查询 TEE 实现版本
TEE_IOC_OPEN_SESSION 按 UUID 开 session
TEE_IOC_INVOKE 调 TA command
TEE_IOC_CLOSE_SESSION 关 session
TEE_IOC_SHM_ALLOC 分配共享内存
TEE_IOC_SUPPL_RECV supplicant 收 RPC

optee/core.c optee_probe :SMC 模式下先 optee_smc_get_uid ,UID 应为 OP-TEE 固定 UUID 字节序列;再 optee_smc_exchange_capabilities 协商 sec_cap (是否 dynamic shm、RPMB 等)。probe 失败常见 log:optee: probe of firmware:optee failed with error -ENODEV → 多半 BL32 未起或 SMC UID 不对。

模块参数与 debugfs(视版本):

bash 复制代码
ls /sys/module/optee/parameters/ 2>/dev/null
cat /sys/kernel/debug/optee/* 2>/dev/null   # 需 CONFIG_DEBUG_FS

linux/drivers/tee/ 是 TEE 通用框架,OP-TEE 是其中一种 provider

文件 作用
tee_core.c 注册 tee device、ioctl 分发、/dev/tee0
tee_shm.c 共享内存池管理
optee/core.c OP-TEE probe、版本探测
optee/call.c SMC 调用封装
optee/rpc.c supplicant RPC 处理
optee/ffa.c FF-A 模式 probe(新平台)

内核配置:

复制代码
CONFIG_TEE=y
CONFIG_OPTEE=y

tee_ioctl() 处理 TEE_IOC_OPEN_SESSIONTEE_IOC_INVOKE 等;拷贝 struct tee_ioctl_invoke_arg 与用户 tee_param ,校验 param_types ,调用 teedev->ops->invokeoptee_invoke_func

probe 流程(SMC 传统模式):

  1. DT 匹配 linaro,optee-tz
  2. optee_smc_get_uid() 确认 OP-TEE 存在
  3. 注册 /dev/tee0/dev/teepriv0
  4. 初始化 shm pool(DT memory-region 或 dynamic alloc)

Linux 5.x → 6.x 变更:关注 FF-Aoptee_ffa probe 顺序、tee_shm_pool dma ops。升级 LTS 读 drivers/tee/optee/Kconfig changelog。

用户态链接:

makefile 复制代码
LDLIBS += -lteec
CFLAGS += -I$(OPTEE_CLIENT_EXPORT)/include

Yocto meta-optee 、Buildroot BR2_PACKAGE_OPTEE_CLIENT 提供打包路径。

设备树:linaro,optee-tz 与 arm,ffa

传统 OP-TEE 使用 linaro,optee-tz binding(linux/Documentation/devicetree/bindings/tee/linaro,optee-tz.yaml):

dts 复制代码
firmware {
    optee {
        compatible = "linaro,optee-tz";
        method = "smc";
    };
};

reserved-memory {
    #address-cells = <2>;
    #size-cells = <2>;
    optee_shm: optee-shm@82000000 {
        compatible = "shared-dma-pool";
        reg = <0x0 0x82000000 0x0 0x02000000>;
        no-map;
    };
};

/ {
    optee {
        compatible = "linaro,optee-tz";
        method = "smc";
        memory-region = <&optee_shm>;
    };
};

method = "smc" 表示走 SMC 而非 FF-A。memory-region 绑定 NS 侧与 OP-TEE 共享的 carveout,必须 不在 Linux memory 节点内,且与 TZASC region 一致。

新平台逐步迁移 FF-A(Firmware Framework for Arm) ,binding 为 arm,ffalinux/Documentation/devicetree/bindings/firmware/arm,ffa.yaml):

dts 复制代码
firmware {
    optee {
        compatible = "linaro,optee-tz";
        method = "ffa";
    };
};

内核 optee/ffa.c 注册 arm_ffa_device ,与 arm,ffa DT 节点匹配;失败时 fallback 到 SMC 视平台而定。FF-A 迁移动机:多 Secure Partition 、标准化 SPMC ↔ SP 通信、降低单一 OP-TEE SPD 耦合。迁移读 optee_os/documentation/ffa/linux/Documentation/arch/arm64/ffa.rst

KASLR 不影响 固定 reserved-memory ;但 CMA 与之争用会导致 probe 后 alloc 失败------确保 DT optee_shm 独立保留。

initramfs 阶段无 /dev/tee0 正常;早期 unlock 需 builtin optee 或延后到 rootfs。

三大源码树导览:TF-A、OP-TEE、Linux

arm-trusted-firmware 关键目录:

复制代码
trusted-firmware-a/
├── bl1/ bl2/ bl31/          # Boot Loader 阶段
├── services/
│   ├── spd/opteed/          # OP-TEE SPD
│   └── std_svc/psci/        # PSCI
├── plat/<vendor>/<soc>/     # 平台 TZC/GIC/DDR
├── auth/                    # Trusted Board Boot
└── tools/fiptool/           # FIP 打包解包

optee_os 关键目录:

复制代码
optee_os/
├── core/arch/arm/           # ARM 核心、SMC、MMU
├── core/tee/                # GP 内部 API 实现
├── ta/                      # 示例 TA
├── ldelf/                   # TA 动态加载
└── lib/libutee/             # TA 用户库

linux 关键路径见本文源码锚点表。三角版本应锁定,建议在 BSP manifest 记录:

text 复制代码
tf-a:         v2.10
optee_os:     3.21.0
linux:        6.1.55
optee_client: 3.21.0

上游仓库:

text 复制代码
https://git.trustedfirmware.org/TF-A/trusted-firmware-a.git
https://github.com/OP-TEE/optee_os
https://github.com/OP-TEE/optee_client

交叉编译:

bash 复制代码
export CROSS_COMPILE=aarch64-none-linux-gnu-
export ARCH=arm64
# TF-A / OP-TEE core 用 aarch64-none-elf-
# TA 用 ta_dev_kit 指定 cross compiler

QEMU 学习路径:virt,secure=on 启用 EL3 与 TZ 仿真;TF-A plat/fvp 与 OP-TEE PLATFORM=fvp 文档齐全。

现场排障:无 tee0、SMC hang、错误码与版本锁

快速分层表

层次 症状 首查
硬件 TZ 无 secure RAM TZASC map、BL2 mem layout
FW BL32 无 UID SMC 串口 BL32 entry、fiptool
DT probe defer compatible、method、memory-region
内核 无 device CONFIG_OPTEE、模块加载
用户态 open EIO supplicant、权限

strace -e ioctl 跟踪 CA:

bash 复制代码
strace -e ioctl ./my_ca 2>&1 | grep -E 'tee0|TEE_IOC'

若 ioctl 返回 EIO 且 dmesg 有 arm_smccc: SMC failed ,往 EL3/OP-TEE 查。若 ENODEV,设备节点不存在。

SMC hang 时用另一终端看 watchdogRCU stall ;若有 JTAG,在 EL3 brk #0 或 TF-A LOG_LEVEL=40 打印 Unhandled SMCUnhandled SMC 说明 function ID 未注册------常见原因:内核 optee 版本新、TF-A 太旧 ,或 SiP/OEM ID 冲突

RPMB / secure storage 失败常表现为 TEEC_ERROR_STORAGE_NOT_AVAILABLE ,与 eMMC mmcblk0rpmb 驱动、OP-TEE CFG_RPMB_FS 相关,不是 SMC 本身 hang。

版本矩阵示例(仅说明需成对,非推荐组合):

linux optee_os 备注
5.15 LTS 3.16.x SMC 传统为主
6.1 LTS 3.21.x FF-A 可选
6.6+ 4.0.x msg layout 变更需对齐

/dev/tee0 排查顺序:

  1. zcat /proc/config.gz | grep OPTEE 或检查 CONFIG_OPTEE
  2. DT 是否有 linaro,optee-tz / method
  3. dmesg | grep optee --- probe 失败原因
  4. TF-A 日志 BL32 是否 entry(串口 early log)
  5. fiptool info 确认 FIP 含 BL32

SMC hang / hard lockup

  • OP-TEE secure panic 未返回 NS
  • function ID 与 BL31 注册表不匹配(无限 loop 或 watchdog)
  • secondary CPU PSCI 与 OP-TEE 不一致
  • COUNTER_FREQUENCYarch_timer 与 OP-TEE 定时器 RPC

TEEC 错误码

含义
0x00000000 OK 成功
0xFFFF0000 GENERIC 通用错误
0xFFFF0006 BAD_STATE 状态/supplicant
0xFFFF000C COMMUNICATION SMC/驱动
0xFFFF000F OUT_OF_MEMORY TEE RAM

Origin 0x3 = TEE OS,0x4 = TA。

版本 skew :仅升级 linux 6.6 未升级 optee_os 3.16→4.0,OPTEE_SMC_CALL_WITH_ARG 参数布局变化 → xtest 4001 FAIL 。成对升级 tf-a + optee_os + linux + optee_client

有设备 open fail → supplicant → shm reserved → SMC UID。

open OK invoke fail → TA 路径/签名 → shared mem 大小 → TA 日志。

偶发 hang → 多核 PSCI → secure interrupt storm → WDT。

OP-TEE CFG_TEE_RAM_VA_SIZE=0x02000000 + CFG_TA_RAM_SIZE + shm 0x02000000 → DT reserved-memory 至少 64MiB 连续。对比 U-Boot fdt_fixup 与运行态 dtc -I fs -O dts /sys/firmware/fdt

Android 用 Trusty 或 OP-TEE + HIDL gatekeeper;/dev/tee0 权限 system group------SMC 原理仍与本文 Linux 路径一致。

fiptool、xtest 与端到端验证

fiptool 是 TF-A 自带工具,用于检查与解包 FIP:

bash 复制代码
fiptool info fip.bin
fiptool unpack fip.bin
ls -la image.bin  # 解包后的 BL31/BL32/BL33

确认 TOB-1 UUID 对应 BL32 非空。若 BL32 缺失,Linux 永远 probe 不到 OP-TEE。

xtest 是 OP-TEE 官方回归套件:

bash 复制代码
tee-supplicant &
xtest -l 15          # 完整测试
xtest 1001           # 单个用例
xtest -t regression  # 回归子集

典型失败映射:

xtest 现象 可能原因
0001 FAIL SMC UID 不通,BL31 SPD 未启
4001 FAIL shm/参数布局版本不匹配
5xxx FAIL crypto/RPMB 平台未配

产线回归建议:ls /dev/tee0xtest -l 15systemctl suspend 后重测 session → 多核 stress-ng 并行 invoke。

文档与规范索引:

文档 内容
ARM DEN 0028A SMCCC
ARM DDI 0406 ARMv8-A 架构
GlobalPlatform TEE Client API TEEC_*
TF-A docs/ BL 设计
OP-TEE documentation/ 构建与 porting

EL3 Monitor 当成交通警察、OP-TEE 当成 Secure 侧操作系统、Linux 当成 NS Rich OS、SMC 当成唯一合法跨界 syscall------读源码与 DT 对照,调用链就不会散。

典型 SoC 移植差异速览:

平台 要点
RK3399 PLAT=rk3399 SPD=opteed;DDR sip 在 BL31
i.MX8 CAAM NS/S 划分;TZASC 在 plat_tzc380.c
STM32MP1 stm32mp1 平台;M4 COPRO 与 A7 资源隔离
Amlogic G12 U-Boot BL32=tee_xxx.bin;DT firmware/optee
Allwinner 社区 sunxi 碎片化,tf-a/optee/linux 版本锁死

长期维护:跟 LTS linux + optee_os + tf-a 三角;订阅 OP-TEE mail list CVE;每季度 xtest regressionsuspend/resume 后重测 session。OTA 更新 tee.bin/fip.bin 必须走 signed FIP,与 BL2 TBB 证书链一致,否则 BL2 拒绝加载新 BL32。

QEMU virt,secure=on 启动示例(学习 SMC,非生产):

bash 复制代码
qemu-system-aarch64 -machine virt,secure=on -cpu cortex-a57 \
  -bios bl1.bin -serial stdio -nographic

TrustZone 选型与 Trusty(Android)QSEE(高通)Kinibi 等厂商 TEE 互斥占 BL32------选型在 BSP 阶段决定,Linux 驱动与 SMC 协议不同,不可混用。

相关推荐
寥落半伤感1 小时前
在Pycharm中一键迅速安装包及查看虚拟环境中包名的方法
ide·python·pycharm
呆呆在发呆.1 小时前
Agnet狼人杀
人工智能·python·microsoft
hhzz1 小时前
【OpenCV 入门到精通 04】视频入门与绘图交互:从摄像头到鼠标画笔
人工智能·python·opencv·计算机视觉·音视频·交互
Thomas.Sir1 小时前
第40课:TensorFlow|模型推理部署前置知识【离线推理、接口调用基础】
人工智能·python·tensorflow
2601_962387821 小时前
多源数据查询指南:MySQL 联邦、Python 聚合与 Presto 引擎全对比
python·mysql·数据集成·presto·联邦查询
IT小白杨1 小时前
TikTok多账号运营体系安全运营攻略:移动端优先架构下的环境隔离技术解析
大数据·经验分享·tcp/ip·安全·架构·指纹浏览器
weixin199701080161 小时前
《Mercari OTA 直连接入:日本二手电商出海的API通道与600次/分钟限流实战》(附Python源码)
开发语言·数据库·python
爱签AI电子合同2 小时前
电子合同安全怎么测?加密与权限审计维度专项测评
安全
山东科恩光电2 小时前
如何有效安装安全地毯以保障操作人员安全?
安全