Realm 的异常处理
本文最后更新于 118 天前,其中的信息可能已经有所发展或是发生改变。

据上篇 Linaro RMM 代码初步阅读,我们已经了解到,当 Realm 发生异常时,会直接回退到 RMM 的上下文。RMM 将 Realm 的上下文保存到 REC 结构体中,调用 handle_realm_exit() 函数对 Realm 的异常进行处理。

1 Realm 的异常判断和分发

handle_realm_exit() 函数有三个参数,分别是 REC 结构体地址,RecExit 结构体地址和异常值。对于异常值,是从 Realm 退出后,由 el2_vectors 代码设置的。

el2_vectors 代码在 rmm/runtime/core/aarch64/vectors.S 中实现,设置了所有中断处理函数的入口地址,这些地址应该在初始化的时候就被设置到了中断向量表中。当中断发生时,直接进入相应函数。

ENTRY(el2_vectors):
    ventry_unused    exc_sync_sp0
    ventry_unused    exc_irq_sp0
    ventry_unused    exc_fiq_sp0
    ventry_unused    exc_serror_sp0

    ventry        el2_sync_cel
    ventry_unused    exc_irq_spx
    ventry_unused    exc_fiq_spx
    ventry_unused    exc_serror_spx

    ventry        el2_sync_lel
    ventry        el2_irq_lel
    ventry        el2_fiq_lel
    ventry        el2_serror_lel

    ventry_unused    exc_sync_lel_32
    ventry_unused    exc_irq_lel_32
    ventry_unused    exc_fiq_lel_32
    ventry_unused    exc_serror_lel_32
ENDPROC(el2_vectors)

X0 寄存器是 aarch64 架构函数返回值所用的寄存器。

每个中断处理函数将异常信息码存储到 X0 寄存器中,并跳转 realm_exit() 函数。realm_exit() 函数在 rmm/runtime/core/aarch64/run-asm.S 中实现。该函数保存 Realm 通用寄存器和恢复一些寄存器值后直接返回到 rec_run_loop() 函数中。

handle_realm_exit() 函数在 rmm/runtime/core/exit.c 中实现。RMM 将 Realm 的异常分成了以下几类:

  • ARM_EXCEPTION_SYNC_LEL: 将 RecExit 退出原因设置成 RMI_EXIT_SYNC 后,交给 handle_exception_sync() 函数处理,由该函数决定是否需要 REC_EXIT 的操作。如果需要 REC_EXIT,则将一些 EL2 特权寄存器的值,如 ESR, FAR, HPFAR,保存在 REC 结构体的 last_run_info 域中。

  • ARM_EXCEPTION_IRQ_LEL: 交给 handle_exception_irq_lel() 函数处理,由该函数决定是否需要 REC_EXIT 的操作。

  • ARM_EXCEPTION_FIQ_LEL: 将 RecExit 退出原因设置成 RMI_EXIT_FIQ,并退出 switch 选择语句。

  • ARM_EXCEPTION_SERROR_LEL: 将 RecExit 退出原因设置成 RMI_EXIT_SERROR,然后交给 hanle_exception_serror_lel() 函数处理,并由该函数决定是否需要 REC_EXIT 操作。

  • 其他情况,默认需要 REC_EXIT 操作。

2 同步异常的处理

handle_exception_sync() 函数在 rmm/runtime/core/exit.c 中实现。函数先读取 EL2 的 ESR 寄存器,并根据读取的值,判断是那种异常。函数能处理的异常有以下几类:

  • ESR_EL2_EC_WFX: WF* 类指令,设置 RecExit 的 ESR 值为读取的 EL2 的 ESR 值,PC 更新到下一条指令后,需要 REC_EXIT。

  • ESR_EL2_EC_HVC: Realm 执行了 HVC 指令,调用 realm_inject_undef_abort() 函数,注入异常并返回到 Realm 中。

  • ESR_EL2_EC_SMC: Realm 有 RSI 请求,调用 handle_realm_rsi() 函数进行处理,并由该函数决定是否需要 REC_EXIT。

  • ESR_EL2_EC_SYSREG: Realm 在尝试读取寄存器,调用 handle_sysreg_access_trap() 处理,并由该函数决定是否需要 REC_EXIT。返回时需要将 PC 值设置为下一条指令的 PC 的地址。

  • ESR_EL2_EC_INST_ABORT / ESR_EL2_EC_DATA_ABORT: 指令和数据的访问异常,分别调用 handle_inst_abort()handle_data_abort() 函数进行处理,并由这两个函数决定是否需要 REC_EXIT。

  • ESR_EL2_EC_FPU / ESR_EL2_EC_SVE / ESR_EL2_EC_SME: SIMD 相关异常,交给 handle_simd_exception() 函数处理并决定是否需要 REC_EXIT。该异常我们应该暂时不需要了解。

  • 其他异常:未被定义的异常,需要 REC_EXIT。

2.1 异常注入

rmm/runtime/core/inject-exp.c 中,实现了几个异常注入的函数:

  • inject_sync_idabort()

  • inject_sync_idabort_rec()

  • realm_inject_undef_abort()

前两个主要区别在于,是否指定了注入异常的对象 REC。我们这里更关注最后一个,因为在 中提到了它。但这三者又非常的相似,涉及的主要操作如下:

  1. 读取 EL2 的 ESR / ELR 寄存器值和读取 EL12 的 SPSR / VBAR 寄存器的值。

  2. 根据 ESR 和 SPSR 的值,从 VBAR 中得到相应异常处理函数的地址,这里定义为

  3. 计算 PSTATE 的值。

  4. 将 ELR / ESR / SPSR 的值写入到 EL12 相应的寄存器中。

  5. 写入到 EL2 的 ELR 寄存器中。

  6. 将 PSTATE 值写入到 EL2 的 SPSR 寄存器中。

2.2 RSI 相关请求处理

handle_realm_rsi() 函数在 rmm/runtime/core/exit.c 中实现。函数先从 REC 中的 X0 寄存器读取 RSI 功能编号,并根据该功能编号执行相应 RSI 的服务函数。

2.3 寄存器读写处理

handle_sysreg_access_trap() 函数在 rmm/runtime/core/sysregs.c 中实现。函数先从 EL2 的 ESR 寄存器中读去要访问的 sysreg 的编号 和掩码 。根据 跳转相应的处理函数进行处理(主要是访问虚拟中断相关寄存器,如 ICC),并决定是否需要 REC_EXIT。其他情况,视为 RAZ 和 WI,返回到 Realm 中。

2.4 指令和数据异常

handle_inst_abort()handle_data_abort 函数均在 rmm/runtime/core/exit.c 中实现。两者都先通过 handle_sync_external_abort() 函数判断是不是 SEA。如果是,就直接 REC_EXIT。

对于指令异常,则判断是否存在内存越界、访问未保护的 IPA 亦或者是 RIPAS 为 EMPTY 的情况。如果是,则通过 inject_sync_idabort() 函数注入异常,交给 Realm 进行处理。

对于数据异常,则判断是否存在内存越界或者是 RIPAS 为 EMPTY 的情况。如果是,则通过 inject_sync_idabort() 函数注入异常,交给 Realm 进行处理。

对于其他情况,视为未定义的异常。将相关的访问信息写入到 RecExit 结构体中,通过 REC_EXIT,交给普通环境中的 Host 处理。

3 IRQ 和 FIQ 的处理

虽然 IRQ 提供了 handle_exception_irq_lel() 函数,但和 FIQ 没有任何区别。分别把 RecExit 中的退出原因设置成 RMI_EXIT_IRQ 和 RMI_EXIT_FIQ 后 REC_EXIT,由普通环境中的 Host 注入虚拟中断。至于虚拟中断如何反馈给 Realm,在上一篇文章中有介绍。

4 SERROR 的处理

handle_exception_serror_lel() 函数在 rmm/runtime/core/exit.c 中实现。函数先读取 EL2 的 ESR 寄存器,判断 SERROR 的类型:

  • 如果是未实现或者是未定义的 SERROR,直接让系统崩溃,停止运行。

  • 如果是 ESR_EL2_SERROR_AET_UEU (Unrecoverable RAS Error) 和 ESR_EL2_SERROR_UER (Recoverable RAS Error),则调用 inject_serror() 函数向 Realm 注入异常,这样可以让 Realm 中运行的系统关机。将 ESR 寄存器的值写入到 RecExit 中,告诉普通环境中的 Host 有关该异常的信息。

  • 如果是 ESR_EL2_SERROR_AET_CE (Corrected RAS Error) 和 ESR_EL2_SERROR_UEO (Restartable RAS Error),则将 ESR 寄存器的值到 RecExit 中,告诉普通环境中的 Host 有关该异常的信息。

  • 以上两者都需要 REC_EXIT。对于其他 SERROR,直接让系统崩溃。

inject_serror() 函数在 rmm/runtime/core/run.c 中实现,将 ESR 信息设置到 REC 的 serror_info 域中。


以上就是 Linaro RMM 的异常处理部分。源代码中也有很多的 TODO,相当一部分的功能还没有实现。

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇