ARM Trustzone 技术 (三)ARMv7-A Exceptions & Interrupts Handling 的安全扩展

了解 Processor modes 和 registers 相关的 security extensions 之后,很自然就到了 exceptions (异常)和 interrupts (中断) 的处理。

首先简单介绍一下异常和中断。一般来说,会打断 CPU 当前程序执行的事件,都可以称之为一个异常,异常的来源可以是 cpu 内部,也可以是其他外部硬件。例如 cpu 在程序执行过程中,数据和指令发生了错误,导致程序无法正常执行,这就是一种异常;外部硬件向 cpu 发送中断请求,cpu 挂起当前执行程序,优先处理中断请求,这是外部中断,同时也是一种异常。异常的发生就会导致 processor modes 的切换,一个 data abort 的发生,cpu 就会进入 Abort mode,一个 undefined instruction 的发生,cpu 就会进入 Undefined mode,同样 IRQ,FIQ 的触发就会导致 cpu 进入 IRQ 和 FIQ mode。但一般由于操作系统的存在,cpu 进入 IRQ 和 FIQ mode 之后,会迅速进入 SVC mode,交由操作系统中相应的 interrupt handler 处理。还有一种异常并不是 abort,也并不是真实的硬件中断,而是通过特定指令触发的软件中断,引起 cpu 执行流程的改变。这样的软件中断指令在 armv7 中有三个:SVC,SMC 和 HVC。其中 SVC指令(supervisor call instruction)通常在用户进程切换到内核进程时使用,也就是我们常说的 syscall,会让 cpu 进入 SVC mode;HVC(Hypervisor call instruction)为 ARM 虚拟化技术的扩展指令,触发 cpu 进入 HYP mode,最后就是 Trustzone 的扩展指令 SMC(secure monitor call instruction),会触发 cpu 进入 MON mode。

了解 processor modes 和异常的关系之后,就明白为什么操作系统在初始化时首先需要做的事情是配置 cpu 的异常向量表:Vector Table。配置异常向量表就是告诉 cpu 不同异常发生之后,入口函数在哪里。以 Linux kernel 的 vector table 为例:

.L__vectors_start:
    W(b)    vector_rst    /* reset mode */
    W(b)    vector_und    /* undefined mode */
    W(ldr)  pc, .L__vectors_start + 0x1000    /* svc mode */
    W(b)    vector_pabt    /* abort mode (prefetch abort) */
    W(b)    vector_dabt    /* abort mode (data abort) */
    W(b)    vector_addrexcptn  /* unused */
    W(b)    vector_irq    /* irq mode */
    W(b)    vector_fiq    /* fiq mode */

配置 Vector table 除了需要指定各个 exceptions handler 的入口函数之外,最重要的就是指定 vector table 的地址,这样 cpu 才计算出各个入口函数的地址。32位 ARM架构中 vector table 基地址有两种方式配置,一种称为 high vectors,固定放在地址 0xFFFF0000;还一种是 normal vectors,放置地址由 cp15 寄存器 VBAR(Vector Base Address Register) 的值决定。而究竟使用 high vectors 还是 normal vectors 是由 cp15 寄存器 SCTLR 的 V bit 决定。具体可以参考 ARMv7-a 的手册。

那么异常处理和 Trustzone 又有什么关系呢。前文说过,引入 Trustzone 之后,cpu 在不同的 processor modes 时都要 secure 和 non-secure 两种状态,如下图所示:


ARM Trustzone 技术 (三)ARMv7-A Exceptions & Interrupts Handling 的安全扩展_第1张图片
secure-and-non-secure-worlds.png

secure 和 non-secure state 的切换必须经过 monitor mode,而 cpu mode 的切换必须又异常触发,那么触发进入 monitor mode 的异常处理函数应该在哪里配置呢?处于安全考虑,显然不能和 kernel OS 共用一套 vector table,因此一个新的寄存器: MVBAR (Monitor Vector Base Address Register)由此引入。先看一段monitor vector table 配置的 sample code:

monitor_vector_table:
    nop             /* RESET    */
    b   .           /* UNDEF    */
    b   .Lsmcall_func       /* SMC      */
    b   .           /* IABORT   */
    b   .           /* DABORT   */
    nop             /* reserved */
    b   .           /* IRQ      */
    b   .           /* FIQ      */

也就是一旦系统中发生 smc 指令,cpu 会立即挂起当前程序,直接跳到 smcall_func 处理 smc 引发的异常。而 smcall_func 的入口地址究竟在哪里则由 MVBAR 计算得出。MVBAR 为 cp15 c12 寄存器,需要用 mrc/mcr 指令读写,所以配置MVBAR的 sample code 如下:

/* setup monitor vector */
ldr     r0, =monitor_vector_table
mcr     p15, 0, r0, c12, c0, 1


在前一个 topic: Processor modes & registers 中提过,除了 smc 软件中断指令可以触发 cpu 进入 monitor mode,外部中断 IRQ,FIQ 也可以配置成触发 cpu 进入 monitor mode,由寄存器 SCR(secure configuration register)的 IRQ bit 和 FIQ bit 决定,再回顾一下SCR 的寄存器表格:

  • secure configuration register
    Bits Name Function
    [0] NS bit 0: secure;
    1:non-secure
    [1] IRQ 0: IRQ 异常触发 CPU 进入 IRQ mode;
    1: IRQ 异常触发 CPU 进入 MON mode
    [2] FIQ 0: FIQ 异常触发 CPU 进入 FIQ mode;
    1: FIQ 异常触发 CPU 进入 MON mode
    [3] EA 0: external abort 异常触发 CPU 进入 ABT mode;
    1: external abort 异常触发 CPU 进入 MON mode
    [4] FW 0: non-secure mode 下 CPSR 的 F bit 不可写;
    1: non-secure mode 下 CPSR 的 F bit 可写
    [5] AW 0: non-secure mode 下 CPSR 的 A bit 不可写;
    1: non-secure mode 下 CPSR 的 A bit 可写
    [6:31] reserved -

一般来说,推荐的做法是将 FIQ bit 置为1, 配置为 secure interrupt,也就是说一旦 FIQ 发生,cpu 就会进入 monitor mode,相应的 monitor_vector_table 修改如下:

monitor_vector_table:
    nop             /* RESET    */
    b   .           /* UNDEF    */
    b   .Lsmcall_func       /* SMC      */
    b   .           /* IABORT   */
    b   .           /* DABORT   */
    nop             /* reserved */
    b   .           /* IRQ      */
    b   .Lfiq_func          /* FIQ      */

这样 fiq 为 secure interrupt,而 irq 仍然是 normal interrupt。那么问题来了,当 cpu 在 secure state 时,收到 irq 中断会如何处理呢?irq interrupt routing 示意图如下:


ARM Trustzone 技术 (三)ARMv7-A Exceptions & Interrupts Handling 的安全扩展_第2张图片
irq-routing.png

也就是说一旦在 secure state 发生 irq 中断,cpu 应该先 route 到 monitor mode,切换到 non-secure state 之后,再进到 normal OS 的 irq 处理函数中处理 irq。每一次的切换都需要小心的保存和恢复现场,才能保证 cpu 的正常运行。而为了防止中断嵌套,一般 cpu 在进入 monitor mode 之后都会屏蔽所有中断。

最后说一句,整个系统会有 normal OS,secure OS,monitor 三个 exception vector table,通过寄存器和代码控制,系统中异常和中断的处理组合可以多种多样,官方推荐只是其中一种做法而已。只是不论采用官方推荐做法,还是自己发明创造的异常中断处理方式,如何保护和恢复上下文,以及保证secure world 上下文的安全性都是需要考虑的重中之重!

附上系列链接:
ARM Trustzone 技术(一) 综述
ARM Trustzone 技术(二) ARMv7-A Processor modes & registers 的安全扩展
ARM Trustzone 技术 (三)ARMv7-A Exceptions & Interrupts Handling 的安全扩展

你可能感兴趣的:(ARM Trustzone 技术 (三)ARMv7-A Exceptions & Interrupts Handling 的安全扩展)