栏目分类:
子分类:
返回
文库吧用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
文库吧 > IT > 前沿技术 > 物联网 > 嵌入式开发

ARM系列之ARM Trustzone 技术浅析(三)——— Exceptions & Interrupts Handling 的安全扩展

ARM系列之ARM Trustzone 技术浅析(三)——— Exceptions & Interrupts Handling 的安全扩展

TEE系列之ARM Trustzone 技术浅析(三)
  • 1、异常和中断的概念
  • 2、异常产生的方法:
  • 3、同步异常和异步异常的处理
  • 4、异常处理
  • 5、异常向量表
  • 6、异常返回
  • 7、PSTATE
  • 8、中断的处理流程
  • 9、安全扩展

1、异常和中断的概念

(AArch64 Exception and Interrupt Handling)
异常是指需要特权软件(an exception handler))采取某些操作,以确保系统的平稳运行。中断有时用作异常的同义词。但是对于ARM的术语来说,中断是异步异常,只是异常的一种;异常是一个事件(而不是分支或跳转指令)导致指令的正常顺序执行被修改。

一般来说,会打断 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 执行流程的改变。这样的软件中断指令在 arm指令集中有三个: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。

综上所述,ARM异常其实可以分为两组,同步和异步的。
同步异常类型可以有很多原因,但是它们是以类似的方式处理。
异步异常类型被细分为三个中断:IRQ, FIQ和SError(系统错误)。

2、异常产生的方法:
  • 1、Abort

指令获取失败时生成中止(指令中止),失败的数据访问(数据中止),它们可以来自外部记忆系统在内存访问上给出错误响应(指示可能指定的地址与系统中的实际内存不对应)。或者,核心的内存管理单元(MMU)生成中止。在AArch64中,同步中止会导致同步异常。异步中止会导致SError中断异常。

  • 2、Reset

重置是一种特殊情况,因为它总是有自己的向量以最高的实现异常级别为目标。地址可以从重置向量基地址寄存器中读取RVBAR_ELn,其中n是实现的最高异常级别的数目。所有的核心都有一个重置输入,并在它们被重置后接受重置异常重置。它是最高优先级的异常,不能被掩盖。这个异常是用来在内核上执行代码来初始化它,在系统通电后向上

  • 3、同步异常

• The Supervisor Call (SVC) instruction enables User mode programs
to request an OS service.
• The Hypervisor Call (HVC) instruction enables the guest OS to
request hypervisor services.
• The Secure monitor Call (SMC) instruction enables the Normal

  • 4、异步异常(中断)

中断有三种类型,IRQ, FIQ和SError。IRQ和FIQ是与SError相比,一般用途是与外部异步数据中止。所以通常,术语“中断”仅指到IRQ和FIQ。

3、同步异常和异步异常的处理
  • 1、处理一个同步异常

Exception Syndrome Register (ESR_ELn)
Fault Address Register (FAR_ELn)
The Exception Link Register (ELR_ELn)

异常综合征寄存器(ESR_ELn)和错误地址寄存器(FAR_ELn)是提供用于向异常处理程序提供有关同步异常原因的信息。ESR_ELn提供异常原因的信息,而FAR_ELn保存所有同步指令和数据中止和对齐错误的错误虚拟地址。

对于实现了EL2 (Hypervisor)或EL3(安全内核)的系统,同步异常通常在当前或更高的异常级别中获取。异步异常可以(如果)),将被路由到更高的异常级别,由Hypervisor或安全程序处理
内核。SCR_EL3寄存器指定哪些异常被路由到EL3,类似地,HCR_EL2指定将哪些异常路由到EL2。有一些单独的位允许独立控制IRQ, FIQ和SError的路由。

  • 2、ESR_ELn - 异常综合征寄存器

包含允许异常的信息。处理程序来确定异常的原因。它只针对同步异常进行更新和SError。它不为IRQ或FIQ更新,因为这些中断处理程序通常获得状态来自通用中断控制器(GIC)寄存器的信息

  • (3)、Unallocated instructions

Unallocated instructions cause a Synchronous Abort in AArch64. 该异常产生的原因:
• An instruction opcode that is not allocated.
• An instruction that requires a higher level of privilege than the current Exception level. //比如你在EL1中操作了SCR_EL3寄存器
• An instruction that has been disabled.
• Any instruction when the PSTATE.IL field is set.

  • (4)、System calls
4、异常处理

ARMv8-A体系结构有四个异常级别:EL0、EL1、EL2和EL3。处理器执行
只能通过获取异常或从异常返回来在异常级别之间移动。

需要注意的是:

When the processor moves from a higher to a lower Exception level, the Execution state can
stay the same, or it can switch from AArch64 to AArch32.
When moving from a lower to a higher Exception level, the Execution state can stay the same
or switch from AArch32 to AArch64
也就是说,如果高级别的level为aarch32,那么低级别的level一定得为aarch32
如果高级别的level为aarch64,那么低级别的level一可以为aarch32或aarch64

当一个异常发生时, the processor自动执行了如下动作:

The SPSR_ELn is updated (where n is the Exception level where the exception is taken), to
store the PSTATE information that is required to correctly return at the end of the exception.
PSTATE is updated to reflect the new processor status (and this can mean that the
Exception level is raised, or it can stay the same).
The address to return to at the end of the exception is stored in ELR_ELn.
The _ELn suffix on register names denotes that there are multiple copies of these registers
existing at different Exception levels. This means, for example, that SPSR_EL1 is a different
physical register to SPSR_EL2.


而我们在异常处理中,又会调用下一级的函数来干活,图形也就如下所示:

当异常处理完成后,processor由高级别返回低级别时,使用ERET指令返回。

5、异常向量表

每个异常级别都有自己的一套向量表,这些表的基地址分别写在VBAR_EL3, VBAR_EL2 and VBAR_EL1系统寄存器中。
向量表中的每个条目有16 instructions long(0x80字节)(在ARMv7-A和AArch32中,每个条目只有4个字节)。这意味着在AArch64中顶层处理程序可以直接在向量中,而不是跳转到其它地址处执行。

VBAR_ELn执行的每个table中,定义了16个entries,具体走哪一个entry是由下面几个因素决定的:
• The type of exception (SError, FIQ, IRQ, or Synchronous)
• If the exception is being taken at the same Exception level, the stack pointer to be used (SP0 or SPn).
• If the exception is being taken at a lower Exception level, the Execution state of the next lower level (AArch64 or AArch32).

一张经典的向量表,如下所示:

举一个超级简单的例子:
kernel code执行在EL1,这时来了一个IRQ,该中断没有配置到hypervisor和secure environment中,所以它的处理仅仅是在kernel中完成,程序跳转到VBAR_EL1+0x280,栈使用sp_el1(将SPSel设置程sp_el1)。

6、异常返回

软件必须告诉处理器何时从异常中返回。这是通过代码完成的使用ERET指令。这将从SPSR_ELn中恢复异常前的PSTATE并返回通过从ELR_ELn恢复PC,将程序执行返回到原始位置。
在A64指令集中,使用寄存器X30(与RET指令一起)返回子例程

ELR_ELn寄存器用于存储来自异常的返回地址。它的价值寄存器是自动写入的入口异常,并被写入到PC作为其中之一执行用于从异常中返回的ERET指令的效果。

除了SPSR和ELR寄存器之外,每个异常级别都有自己专用的堆栈指针登记。它们是SP_EL0、SP_EL1、SP_EL2和SP_EL3。这些寄存器用来指向专用的栈。堆栈可以,例如,用来存储寄存器被损坏异常处理程序,使它们可以在返回之前恢复到原来的值原始代码

处理程序代码可以从使用SP_ELn切换到使用SP_EL0。例如,SP_EL1可能指向存储小堆栈的内存块,内核可以始终保证该小堆栈是有效的。SP_EL0可能指向较大的内核任务堆栈. 但不能保证不发生溢出。这切换通过写入SPSel寄存器来控制。

7、PSTATE


当前processor的状态保存在PSTATE,当异常到来时,PSTATE的值会自动保存在SPSR中。 aarch64中由三个SPSR : SPSR_EL3, SPSR_EL2, and SPSR_EL1
例如,如果发生了一个异常target到EL1,那么当前处理器的状态会自动保存到SPSR_EL1中。

8、中断的处理流程

9、安全扩展

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

.L__vectors_start:
    W(b)    vector_rst    
    W(b)    vector_und    
    W(ldr)  pc, .L__vectors_start + 0x1000    
    W(b)    vector_pabt    
    W(b)    vector_dabt    
    W(b)    vector_addrexcptn  
    W(b)    vector_irq    
    W(b)    vector_fiq    

配置 Vector table 除了需要指定各个 exceptions handler 的入口函数之外,最重要的就是指定 vector table 的地址,这样 cpu 才计算出各个入口函数的地址。

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

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(ARM V7):

monitor_vector_table:
    nop             
    b   .           
    b   .Lsmcall_func       
    b   .           
    b   .           
    nop             
    b   .           
    b   .           

arm-trusted-firmware-masterbl31aarch64runtime_exceptions.S (ARM V8)

vector_base runtime_exceptions

	
vector_entry sync_exception_sp_el0
	
	no_ret	report_unhandled_exception
	check_vector_size sync_exception_sp_el0

vector_entry irq_sp_el0
	
	no_ret	report_unhandled_interrupt
	check_vector_size irq_sp_el0


vector_entry fiq_sp_el0
	no_ret	report_unhandled_interrupt
	check_vector_size fiq_sp_el0


vector_entry serror_sp_el0
	no_ret	report_unhandled_exception
	check_vector_size serror_sp_el0

	
vector_entry sync_exception_sp_elx
	
	no_ret	report_unhandled_exception
	check_vector_size sync_exception_sp_elx

vector_entry irq_sp_elx
	no_ret	report_unhandled_interrupt
	check_vector_size irq_sp_elx

vector_entry fiq_sp_elx
	no_ret	report_unhandled_interrupt
	check_vector_size fiq_sp_elx

vector_entry serror_sp_elx
	no_ret	report_unhandled_exception
	check_vector_size serror_sp_elx

	
vector_entry sync_exception_aarch64
	
	handle_sync_exception
	check_vector_size sync_exception_aarch64

vector_entry irq_aarch64
	handle_interrupt_exception irq_aarch64
	check_vector_size irq_aarch64

vector_entry fiq_aarch64
	handle_interrupt_exception fiq_aarch64
	check_vector_size fiq_aarch64

vector_entry serror_aarch64
	
	no_ret	report_unhandled_exception
	check_vector_size serror_aarch64

	
vector_entry sync_exception_aarch32
	
	handle_sync_exception
	check_vector_size sync_exception_aarch32

vector_entry irq_aarch32
	handle_interrupt_exception irq_aarch32
	check_vector_size irq_aarch32

vector_entry fiq_aarch32
	handle_interrupt_exception fiq_aarch32
	check_vector_size fiq_aarch32

vector_entry serror_aarch32
	
	no_ret	report_unhandled_exception
	check_vector_size serror_aarch32

也就是一旦系统中发生 smc 指令,cpu 会立即挂起当前程序,直接跳到 smcall_func 处理 smc 引发的异常。而 smcall_func 的入口地址究竟在哪里则由 MVBAR 计算得出,跳转到上述的地址执行。
在前一个 topic: Processor modes & registers 中提过,除了 smc 软件中断指令可以触发 cpu 进入 monitor mode,外部中断 IRQ,FIQ 也可以配置成触发 cpu 进入 monitor mode,由寄存器 SCR(secure configuration register)的 IRQ bit 和 FIQ bit 决定,再回顾一下SCR 的寄存器表格:

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


也就是说一旦在 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 上下文的安全性都是需要考虑的重中之重!

转载请注明:文章转载自 www.wk8.com.cn
本文地址:https://www.wk8.com.cn/it/852667.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 wk8.com.cn

ICP备案号:晋ICP备2021003244-6号