4. 运行时服务核心概念
在 TF-A(Trusted Firmware-A)的运行时服务体系中,安全监控调用(SMC,Secure Monitor Call)是普通世界与安全世界之间唯一的标准化入口。无论是 Linux 内核发起 PSCI CPU 热插拔,还是 OP-TEE 请求随机数服务,最终都会转化为一条 SMC 指令,由 EL3 的异常向量捕获并分发。理解 SMC 处理、服务注册、服务 ID 分配以及同步/异步调用机制,是掌握 TF-A 运行时服务框架的关键。
一、SMC 处理:从异常入口到服务分发
SMC 指令由非安全世界(或安全世界)执行后触发同步异常,处理器跳转到 EL3 的异常向量表。TF-A 在 runtime_exceptions.S 中为 AArch64 的 SMC 异常设置了入口,保存现场后进入 C 语言处理函数 handle_smc()。整个处理链路可以概括为:异常向量 → 上下文保存 → SMC 参数解析 → 服务查找 → 服务处理 → 返回值写回 → 上下文恢复 → ERET 返回。
SMC 调用约定遵循 ARM SMC Calling Convention(SMCCC)。调用者通过 x0 传递 Function ID,x1~x6 传递参数,返回值放在 x0~x3。Function ID 的高 32 位编码了调用类型、服务拥有者、是否快速调用等关键信息,低 16 位则是具体功能号。TF-A 正是依据 Function ID 的位域结构完成服务路由。
二、服务注册:rt_svc_descs 与链接器段
TF-A 并不在运行时动态创建服务,而是通过链接器段 .rt_svc_descs 在编译期收集所有服务描述符。每个服务实现一个 rt_svc_desc_t 结构体,并通过 DECLARE_RT_SVC() 宏将其放入该段。启动阶段 runtime_svc_init() 遍历该段,按 start_oen 排序,并调用每个服务的 init() 回调完成初始化。
/* 服务描述符定义示例 */
typedef struct rt_svc_desc {
uint8_t start_oen; /* 服务 ID 起始值 */
uint8_t end_oen; /* 服务 ID 结束值 */
uint8_t call_type; /* FAST / YIELD */
const char *name;
rt_svc_init_fn_t init;
rt_svc_handle_fn_t handle;
} rt_svc_desc_t;
/* 注册 PSCI 服务 */
DECLARE_RT_SVC(
psci_svc,
OEN_STD_START, /* 0x84 */
OEN_STD_END, /* 0x84 */
SMC_TYPE_FAST,
psci_setup,
psci_smc_handler
);
注册表按 start_oen 升序排列后,查找服务时可以使用二分查找,将 O(n) 降为 O(log n)。这是 TF-A 在 SMC 高频调用场景下的重要优化。
三、服务 ID 分配:OEN 与 Function ID 位域
SMCCC 将 32 位 Function ID 划分为多个位域,其中 OEN(Owning Entity Number)标识服务拥有者,是服务路由的核心依据。TF-A 预定义了若干标准 OEN 区间,厂商和第三方也可申请自有区间。下表列出常见 OEN 及其用途:
| OEN 值 | 服务名称 | 典型 Function ID | 用途说明 |
|---|---|---|---|
| 0x84 | PSCI | 0x84000000 | CPU 热插拔、系统复位、电源管理 |
| 0x82 | SiP | 0x82000000 | 芯片厂商自定义服务 |
| 0x80 | OEM / OP-TEE | 0x80000000 | 可信操作系统调度 |
| 0x86 | SPD | 0x8600FF00 | 安全分区调度器 |
| 0xC4 | FF-A | 0xC4000000 | 固件框架分区通信 |
call_type 一致,否则会被框架拒绝。
四、同步与异步调用:FAST 与 YIELD 的取舍
TF-A 支持两种 SMC 调用类型,其行为差异直接影响系统实时性与吞吐量:
- FAST 调用:处理函数必须立即返回,不允许阻塞或睡眠。适用于读取寄存器、查询状态等短操作。典型如 PSCI 的
PSCI_VERSION。 - YIELD 调用:处理函数可以阻塞,允许在等待期间让出 CPU。适用于需要与安全世界长时间交互的操作,如 OP-TEE 的可信应用执行。
在 handle_smc() 中,框架会检查 Function ID 的 bit30 与注册服务的 call_type 是否匹配。若调用者以 FAST 方式请求一个 YIELD 服务,框架将返回 SMC_UNKNOWN 错误,避免非法阻塞导致系统挂死。
五、案例复盘:一次 SMC 返回异常的排查
某项目在移植自定义 SiP 服务后,Linux 侧调用 sip_smc_handler 始终返回 0xFFFFFFFF。排查过程如下:
- 确认 Function ID 为
0x82000001,OEN=0x82,属于 SiP 区间。 - 检查
DECLARE_RT_SVC宏,发现start_oen与end_oen均写为OEN_SIP_START,但call_type误写为SMC_TYPE_YIELD。 - Linux 侧以 FAST 方式发起调用,框架校验类型不匹配,直接返回错误。
- 将
call_type改为SMC_TYPE_FAST后问题解决。
该案例说明:服务 ID 分配、注册声明与调用方式三者必须严格一致。任何一处不匹配,SMC 都会在框架层被拦截,而不会进入服务处理函数,给调试带来迷惑性。