【DeepSeek】SMC 调用约定
·
SMC 调用遵循 ARM SMC Calling Convention (SMCCC) 规范(文档编号:ARM DEN 0028)。
这是一套标准化的“握手协议”,规定了非安全世界(EL1/EL2)如何向安全监控器(EL3)传递请求,以及 EL3 如何返回结果。
一、寄存器使用约定
SMC 调用本质上是函数调用,但通过寄存器传参。
1. 输入参数
| 寄存器 | 名称 | 说明 |
|---|---|---|
| x0 | Function ID | 核心标识。EL3 根据这个值决定执行哪个服务 |
| x1 | Arg1 | 第 1 个参数 |
| x2 | Arg2 | 第 2 个参数 |
| x3 | Arg3 | 第 3 个参数 |
| … | … | … |
| x7 | Arg7 | 第 7 个参数 |
2. 输出结果
| 寄存器 | 说明 |
|---|---|
| x0 | 返回码,表示执行状态(成功、失败、不支持等) |
| x1 | 返回值 1 |
| x2 | 返回值 2 |
| x3 | 返回值 3 |
| x4 | 返回值 4 |
3. 重要限制
- 最多传递 7 个参数(x1-x7)。
- 最多返回 4 个输出值(x1-x4)。
- x5 - x17:在 SMC 调用过程中被视为临时寄存器,调用完成后其值未定义,调用者必须自行保存/恢复。
二、Function ID (x0) 的结构解析
x0 是 SMC 调用的灵魂,EL3 的 runtime_svc 层会解析它来定位服务。
x0 是一个 32 位无符号整数,其位域结构如下:
1. 位 [31]:调用类型
0:标准调用。通常用于 64 位调用者,参数通过 x0-x7 传递。1:快速调用。通常用于 32 位调用者,参数通过 w0-w7 传递。
2. 位 [30]:调用指令类型
0:快速调用,由SMC指令触发。1:标准调用,由HVC指令触发(用于 Hypervisor 场景)。
注:在 TF-A 实现中,通常通过
is_smc_fid()宏来区分。常见的 SMC FID 顶层十六进制为0x8...或0xC...。
3. 位 [29:24]:服务类型
这是区分不同服务提供者的关键字段:
| 值 | 类型 | 说明 |
|---|---|---|
0x00 |
Arch | 架构服务(如 PSCI) |
0x01 |
SiP | 硅片厂商服务 |
0x02 |
OEM | OEM 厂商服务 |
0x03 |
Std | 标准安全服务 |
0x04 |
PSCI | 电源状态协调接口(实际常用) |
4. 位 [23:16]:OEN
OEM 或 SiP 的具体编号,用于进一步细分服务来源。
5. 位 [15:0]:功能编号
具体的功能序号,由服务提供者自定义。
三、常见的 Function ID 示例
以 TF-A 中最常用的 PSCI 服务为例:
| Function ID (Hex) | 二进制关键位 | 含义 |
|---|---|---|
0x84000000 |
10 (Fast SMC) |
PSCI 版本查询 |
0x84000001 |
10 (Fast SMC) |
CPU 关机 |
0x84000002 |
10 (Fast SMC) |
CPU 开机 |
0x84000003 |
10 (Fast SMC) |
CPU 挂起 |
0xC4000000 |
11 (Std SMC) |
PSCI 版本查询 (64-bit) |
解析 0x84000001 (PSCI_CPU_OFF):
0x84=1000 0100(二进制)- Bit 31 =
1(Fast Call) - Bit 30 =
0(SMC) - Bit 29:24 =
000100=4(PSCI 类型) - Bit 15:0 =
1(功能号 1,即 CPU_OFF)
四、标准返回码
SMC 调用返回后,x0 保存执行状态。SMCCC 定义了以下标准返回码:
| 返回码 (Hex) | 名称 | 说明 |
|---|---|---|
0x00000000 |
SUCCESS | 执行成功 |
0xFFFFFFFE |
NOT_SUPPORTED | 功能不支持 |
0xFFFFFFFD |
INVALID_PARAMS | 参数无效 |
0xFFFFFFFC |
DENIED | 权限拒绝 |
0xFFFFFFFB |
NOT_PRESENT | 服务不存在 |
0xFFFFFFF1 |
ON_PENDING | CPU 开机操作挂起中 |
五、代码示例
1. 汇编层面发起 SMC
// 示例:调用 PSCI CPU_OFF (0x84000001)
// 输入:无参数
// 输出:x0 = 返回码
smc_cpu_off:
mov x0, #0x84000001 // 设置 Function ID
smc #0 // 触发 SMC 异常
ret // 返回调用者 (x0 包含结果)
2. C 语言层面发起 SMC
在 Linux 内核或 U-Boot 中,通常使用 arm_smccc_smc 宏/函数:
#include <linux/arm-smccc.h>
// 定义输入参数结构体
struct arm_smccc_res res;
// 发起 SMC 调用
// 参数1: Function ID
// 参数2-7: 具体参数
arm_smccc_smc(0x84000001, 0, 0, 0, 0, 0, 0, 0, &res);
// 检查结果
if (res.a0 == 0) {
pr_info("CPU off success\n");
} else {
pr_err("CPU off failed: %lx\n", res.a0);
}
六、总结图示
核心规则:
- x0 是身份证:EL3 靠它识别你要干什么。
- x1-x7 是快递包:里面装着具体的参数。
- x0-x4 是回执单:EL3 告诉你结果如何。
- 遵守 SMCCC:这是 ARM 生态(Linux、TF-A、OP-TEE)互通的基础。
更多推荐


所有评论(0)