RMM,即 Realm 管理监视器。管理 Realm 机密虚拟机,同时做为了机密虚拟机和虚拟机管理者之间沟通的桥梁。
RMM 代码获取方法:
mkdir cca-v4 cd cca-v4 repo init -u https://git.codelinaro.org/linaro/dcap/op-tee-4.2.0/manifest.git -b cca/v4 -m sbsa_cca.xml repo sync -j8 --no-clone-bundle
RMM 代码框架
在 cca-v4 的根目录下,可以看到 rmm 目录,为 RMM 项目的主目录。目录结构如下:
|- cmake
|- configs
|- docs
|- drivers
|- ext
|- lib
|- plat
|- runtime
|- toolchain
|- tools
lib 文件夹中有相关的库功能实现,例如 GIC 的实现。runtime 文件夹中就是 RMM 的运行时代码。我们再看看 runtime 文件目录:
|- core
|- include
|- rmi
|- rsi
|- tests
core:一些……include:相关头文件rmi:rmi接口相关代码的实现rsi:rsi接口相关代码的实现tests:一些测试文件
我们重点关注 core / rmi / rsi 文件夹中的代码。
REC_ENTER
从非安全环境 (NS) 中进入 Realm,需要调用 RMI_REC_ENTER。搜索 REC_ENTER 关键字,得到了以下内容:

第一条搜索内容就很有意义。我们在 core/handler.c 代码中找到了 smc_handler 结构体。该结构体定义了绝大多数 RMI 接口功能的处理函数。REC_ENTER 的处理函数是 smc_rec_enter。
在 rmi/run.c 文件中,我们找到了 smc_rec_enter 的原型。该函数体现出了 REC 进入和退出的一个完整周期:
- 清空
RecExit结构体。 - 获取
RecEnter中的信息。 - 检查 Realm 的状态。
- 检查 GIC 的状态,并将 GIC 的状态拷贝到
RecEnter中。 - 检查之前的处理异常时模拟的结果。
- 调用
rec_run_loop函数。 - 此时 REC 已经退出,将 GIC 信息拷贝到
RecExit中。
rec_run_loop 是一个关键点。在此之前,是在检查进入 REC 前的相关条件是否符合要求;在此之后,就是保存 GIC 的相关信息。我们再 core/run.c 文件中找到了 rec_run_loop 的原型。该函数涉及到 REC 上下文相关的处理:
-
保存 NS 的上下文。
-
恢复 Realm 的上下文。
-
进行一些 attest 相关操作。
-
进入一个循环体。
- 检查时钟状态。如果计时器时间达到,就直接退出循环体。
- 激活 REC 相关事件。
- 保存 RMM 的
cptr_el2寄存器,恢复 Realm 的cptr_el2寄存器。 - 恢复 Realm 的密钥。
- 调用
run_realm,参数是 REC 的通用寄存器堆。 - 保存 Realm 的密钥。
- 恢复 RMM 的密钥。
- 保存 Realm 的
cptr_el2寄存器,恢复 RMM 的cptr_el2寄存器。 - 检查 Realm 退出的原因,决定是否继续执行循环体。
-
反馈 Realm 的时钟状态。
-
保存 Realm 的上下文。
-
恢复 NS 的上下文。
几个比较重要的点,时钟的检查,Realm 的上下文的保存与恢复,run_realm 函数以及检查 Realm 退出原因。
Realm 的上下文
RMM 分别调用 save_realm_state 以及 restore_realm_state 函数保存和恢复 Realm 的上下文。两个函数均在 core/run.c 文件中。我们以 save_realm_state 为例子,会涉及到以下过程:
- 调用
save_sysreg_state保存系统寄存器至 REC 中。 - 将
elr_el2的值保存到 REC 的 PC 中。 - 将
spsr_el2的值保存到 REC 的 pstate 中。 - 保存 GIC 信息到 REC 中。
- 对 PMU 一些内容进行处理。
此处比较关键的内容就是 save_sysreg_state 函数。同样也在 core/run.c 文件中,找到了这个函数。函数内容基本都是赋值语句,将读取到的 sysreg 的值存储到相应的 REC 寄存器中。这些寄存器就有我们比较关注的寄存器,例如 ttbr,vbar 等。这个函数可以证明,整个 REC 就一套 sysreg。
对于时钟和 Realm 退出检查的内容,会在后面的博客讲述。