SMC 调用遵循 ARM SMC Calling Convention (SMCCC) 规范(文档编号:ARM DEN 0028)。

这是一套标准化的“握手协议”,规定了非安全世界(EL1/EL2)如何向安全监控器(EL3)传递请求,以及 EL3 如何返回结果。


一、寄存器使用约定

SMC 调用本质上是函数调用,但通过寄存器传参。

1. 输入参数

寄存器名称说明
x0Function ID核心标识。EL3 根据这个值决定执行哪个服务
x1Arg1第 1 个参数
x2Arg2第 2 个参数
x3Arg3第 3 个参数
x7Arg7第 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 位无符号整数,其位域结构如下:

31
(1 bit)

30
(1 bit)

29:24
(6 bits)

23:16
(8 bits)

15:0
(16 bits)

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]:服务类型

这是区分不同服务提供者的关键字段:

类型说明
0x00Arch架构服务(如 PSCI)
0x01SiP硅片厂商服务
0x02OEMOEM 厂商服务
0x03Std标准安全服务
0x04PSCI电源状态协调接口(实际常用)

4. 位 [23:16]:OEN

OEM 或 SiP 的具体编号,用于进一步细分服务来源。

5. 位 [15:0]:功能编号

具体的功能序号,由服务提供者自定义。


三、常见的 Function ID 示例

以 TF-A 中最常用的 PSCI 服务为例:

Function ID (Hex)二进制关键位含义
0x8400000010 (Fast SMC)PSCI 版本查询
0x8400000110 (Fast SMC)CPU 关机
0x8400000210 (Fast SMC)CPU 开机
0x8400000310 (Fast SMC)CPU 挂起
0xC400000011 (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)名称说明
0x00000000SUCCESS执行成功
0xFFFFFFFENOT_SUPPORTED功能不支持
0xFFFFFFFDINVALID_PARAMS参数无效
0xFFFFFFFCDENIED权限拒绝
0xFFFFFFFBNOT_PRESENT服务不存在
0xFFFFFFF1ON_PENDINGCPU 开机操作挂起中

五、代码示例

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);
}

六、总结图示

EL3 (BL31)EL1/EL2 (调用者)EL3 (BL31)EL1/EL2 (调用者)准备参数 x0=FuncID, x1-x7=Args1. 读取 x02. 解析服务类型3. 查表找到 Handler设置返回 x0=RetCode, x1-x4=Out检查 x0 判断结果SMC执行服务逻辑ERET

核心规则:

  1. x0 是身份证:EL3 靠它识别你要干什么。
  2. x1-x7 是快递包:里面装着具体的参数。
  3. x0-x4 是回执单:EL3 告诉你结果如何。
  4. 遵守 SMCCC:这是 ARM 生态(Linux、TF-A、OP-TEE)互通的基础。

更多推荐