【笔记】Armv8-A Exception model 异常模型

0.目录

文章目录

  • 0.目录
  • 1.简介
  • 2. 特权与异常等级
    • 2.1 特权的类型
    • 2.2 存储特权
    • 2.3 寄存器访问
  • 3.执行状态和安全状态
    • 3.1 执行状态
    • 3.2 安全状态
    • 3.3 改变执行状态
    • 3.4 改变安全状态
    • 3.5 异常等级和执行状态
  • 4.异常类型
    • 4.1 同步异常
    • 4.2 异步异常
    • 4.3 IRQ和FIQ
    • 4.4 SError
  • 5.处理异常
    • 5.1 异常术语
  • 5.2 处理异常
    • 5.3 异步异常路由
    • 5.4 确定异常将被带往的执行状态
    • 5.5 从异常状态返回
    • 5.6 异常栈
  • 6.向量表
  • 参考

1.简介

本文介绍:Armv8-A的异常和特权模型。包括:ARM架构中的不同的异常,以及当处理器接收到异常之后的行为

本文将有助于:

  • 底层编程开发,如boot或驱动
  • 设置与管理异常

你将学会:

  • 列出异常等级
  • 阐述如何切换执行的异常等级
  • 命名与描述执行状态
  • 建立简单的AArch64异常向量与异常handler

2. 特权与异常等级

在我们解释Armv8-A异常模型的细节之前,让我们首先介绍特权的概念。现代软件希望被分成不同的模块,每个模块对系统和处理器资源有不同的访问级别。这方面的一个例子是操作系统内核和用户应用程序之间的分离,操作系统内核具有对系统资源的高级访问权限,而用户应用程序配置系统的能力则更有限。

Armv8-A通过实现不同级别的特权来实现这种分割。只有在在处理器接到异常或从异常返回时才能更改特权等级。因此,这些特权级别在Armv8-A体系结构中被称为异常级别。每个例外级别都有编号,级别越高的特权有越高的编号。

如下图所示,异常级别称为EL, x为0 ~ 3之间的数字。例如,最低级别的特权被称为EL0。

【笔记】Armv8-A Exception model 异常模型_第1张图片

大多数应用代码运行在EL0,操作系统运行在EL1,hypervisor运行在EL2。EL3为底层固件和安全代码所保留。

注意:架构不强制要求这个软件模型,但是标准软件采用这种模式。

2.1 特权的类型

有两种与本主题相关的类型,第一种是存储系统中的特权,第二种是从访问处理器资源的视角出发的特权。这两种特权都受当前的异常等级影响。

2.2 存储特权

Armv8-A实现了虚拟存储系统。在这个系统中,MMU允许软件指定内存区域的属性。这些属性包括读写权限,可以用两个维度来进行配置。这种使得允许特权访问和非特权访问的权限得以分离。

发生在EL0的内存访问将对照非特权访问权限进行检查。相对的,EL1,EL2和EL3的访问将对照特权访问权限进行检查。

由于这种存储权限配置将用到MMU的转换表,所以在编程这些表格的时候需要考虑到这种特权。MMU的配置存储在系统寄存器中,访问这些寄存器的能力也由当前异常级别控制。

2.3 寄存器访问

一组系统寄存器存储了Armv8-A处理器的配置设置。系统寄存器的设置组合定义了当前处理器的上下文。对系统寄存器的访问由当前的异常等级决定。

系统寄存器的名称表示可以访问该寄存器的最低异常级别。例如,TTBR0_EL1是保存EL0和EL1使用的转换表的基址的寄存器。这个寄存器不能从EL0访问,如果硬要访问将导致一个异常。

Armv8-A架构有很多组功能相似的寄存器,其命名仅有后缀的异常等级不同。这些寄存器相互之间独立,在指令集中的编码不同,在硬件上单独实现。比如下面的寄存器,针对不同的转换阶段配置MMU。虽然名字相似,但是各自有各自的访问语义:

  • SCTLR_EL1 – Top level system control for EL0 and EL1
  • SCTLR_EL2 – Top level system control for EL2
  • SCTLR_EL3 – Top level system control for EL3

注意:EL1和EL0共享MMU的配置,且只能由运行在EL1的代码进行控制。因此,没有SCTLR_EL0.

高级别可以访问低级别的寄存器。例如,切换上下文需要保存寄存器状态的时候

3.执行状态和安全状态

Armv8-A处理器的当前状态取决于异常等级和另外两个重要状态。

当前执行状态定义了通用寄存器的标准宽度和可用的指令集。

执行状态不会更改内存模型和异常的管理。

当前的安全状态控制着哪些异常等级当前有效,哪块内存区域当前可访问,以及这些访问如何在系统内存总线上表示。

下图显示了不同执行状态下使用的异常等级和安全等级:

【笔记】Armv8-A Exception model 异常模型_第2张图片

3.1 执行状态

Armv8-A的两个执行状态:

  • AArch32: 32位执行状态。该状态下的操作与Armv7-A兼容。有两种可用的指令集:T32和A32。标准寄存器的宽度是32位。
  • AArch64: 64位执行状态。有一个可用的指令集:A64。标准寄存器的宽度是64位

3.2 安全状态

Armv8-A体系结构允许实现两种安全状态。这允许对软件进行进一步的分区,以隔离和划分受信任的软件:

  • 安全状态:在这种状态下,处理单元(PE)可以访问安全的和不安全的物理地址空间。在这种状态下,PE可以访问安全寄存器和非安全寄存器。在这种状态下运行的软件只能接收安全中断。
  • 非安全状态:此时PE只能访问不安全的物理地址空间。PE也只能访问允许非安全访问的系统寄存器。在这种状态下运行的软件只能确认不安全的中断。

安全状态的使用请参考:rustZone指南

3.3 改变执行状态

PE只能在复位或异常等级发生变化时改变执行状态。

复位时的执行状态由IMPLEMENTATION DEFINED的机制决定。一些实现固定了复位时的执行状态。例如,Cortex-A32总是会复位为AArch32状态。在大多数Armv8-A的实现中,复位后的执行状态由复位时采样的信号控制。这允许在片上系统(Soc)级别上控制重置重围状态。

当PE在异常级别之间改变时,也可以改变执行状态。在AArch32和AArch64之间的转换只允许遵循一定的规则。

  • 当从较低的异常级别移动到较高的异常级别时,执行状态可以保持不变或更改为AArch64。

  • 当从较高的异常级别移动到较低的异常级别时,执行状态可以保持不变或更改为AArch32。

将这两个规则放在一起意味着64位层可以承载32位层,但32位层不能承载64位层。例如,64位操作系统内核可以同时承载64位和32位应用程序,而32位操作系统内核只能承载32位应用程序。如下图所示:

【笔记】Armv8-A Exception model 异常模型_第3张图片

3.4 改变安全状态

EL3总是被认为在安全状态下执行。使用SCR_EL3, EL3代码可以改变所有较低异常级别的安全状态。如果软件使用SCR_EL3改变低异常级别的安全状态,PE只有在改变到低异常级别时才会改变安全状态。

3.5 异常等级和执行状态

Armv8-A架构允许厂家选择是否实现所有异常级别,以及为每个实现的异常级别选择允许哪些执行状态

EL0和EL1必须实现,EL2和EL3可选。选择不实现EL3或EL2具有重要的含义。

EL3是唯一可以改变安全状态的级别。如果一个实现选择不实现EL3,那么该PE将无法访问单个安全状态。

类似地,EL2包含很多虚拟化功能。目前该体系结构的所有Arm厂家都实现了所有的异常级别,如果没有所有的异常级别,就不可能使用大多数标准软件。

厂家还可以选择每个异常级别有效的执行状态。如果在异常级别允许AArch32,那么所有比它等级低的异常级别都可以允许AArch32。

许多厂家都允许所有的执行状态和所有的异常等级。但是现存的厂家又一定限制。举例,Cortex-A32所有异常级别只允许AArch32。

Cortex-A55只允许EL0上AArch32。

4.异常类型

异常:异常将导致当前执行的程序阻塞,当前的状态将会发生改变以执行处理异常的程序。

其它处理器架构可能将其描述为中断

在Armv8-A架构中,中断是一种外部生成的异常

Armv8-A体系结构将异常分为两大类:同步异常异步异常

4.1 同步异常

同步异常:是由当前指令引起的或与之相关的异常。这意味着同步异常与执行流是同步的。

**同步异常的产生:**试图执行无效的指令,或当前异常等级不允许或已禁止执行的指令。

同步异常也可能由内存访问引起,这可能是地址非对齐的结果,也可能是因为MMU权限检查失败。

因为这种错误是同步的,所以可以在试图访问内存之前接收到异常。内存访问还可以生成异步异常,后面将讨论。

Armv8-A体系结构有一系列异常生成指令**:SVC**、HVCSMC。这些指令不同于简单的无效指令,因为它们针对不同的异常级别,并且在对异常进行优先级排序时处理不同。这些指令用于实现系统调用接口,以允许特权较低的代码向特权较高的代码请求服务。

调试异常也是同步的。调试异常在Debug overview guide中进行了讨论。

4.2 异步异常

某些类型的异常是在外部生成的,因此与当前指令流不同步。这意味着不可能准确地保证何时接收异步异常。

Armv8-A架构只要求它在有限的时间内发生。异步异常也可以暂时屏蔽。这意味着异步异常可以在异常被接受之前保持挂起状态。

这些异步异常类型是:

物理中断:

  • SError (System Error)
  • IRQ
  • FIQ

虚拟中断:

  • vSError (Virtual System Error)
  • vIRQ (Virtual IRQ)
  • vFIQ (Virtual FIQ)

物理中断是响应PE外部产生的信号而产生的。虚拟中断可以由外部生成,也可以由在EL2上执行的软件生成。

4.3 IRQ和FIQ

Armv8-A体系结构有两种异常类型,IRQ和FIQ,它们被用于生成外围中断。在Arm架构的其他版本中,FIQ被用作更高优先级的快速中断。这与Armv8-A不同,在Armv8-A中FIQ与IRQ具有相同的优先级。

IRQ和FIQ有独立的路由控制,经常用于实现安全中断和不安全中断,在Generic Interrupt Controller guide中有讨论。

4.4 SError

SError是一种异常类型,由内存系统在响应错误的内存访问时产生。

SError的典型用法是以前所说的外部、异步中止,例如,内存访问通过了所有的MMU检查,但是在内存总线上发生了错误。这个错误可能是异步报告的,因为发出的指令已经结束了。

SError中断也可能由一些ram上的奇偶校验或错误纠正码(ECC)检查引起,例如内置缓存中的校验码。

5.处理异常

当发生异常时,当前程序流被中断。处理单元(PE)将更新当前状态,并跳转到向量表中的某个地址。通常这个地址包含相应的代码,能够将当前程序的状态保存到堆栈上,然后分支到进一步的代码。如下图:

【笔记】Armv8-A Exception model 异常模型_第4张图片

5.1 异常术语

发现异常时的处理器状态称为异常发生时的状态。PE在异常之后的状态就是异常被带入的状态

Armv8-A架构有触发异常返回的指令。在这种情况下,执行该指令时PE所处的状态就是异常返回前的状态。异常返回指令执行后的状态就是异常返回到的状态

每一个异常类型对应着一个异常级别。异步异常可以被路由到不同的异常级别。

5.2 处理异常

当处理异常时,当前的状态必须保存,以便返回。PE将自动地保存异常返回地址和当前的PSTATE。

通用寄存器的数据必须由软件保存。PE将跟新当前的PSTATE为体系结构中相应的异常类型所定义的PSTATE,然后从向量表跳转到相应的异常handler。

异常对应的PSTATE存储在系统寄存器SPSR_ELx,其中是异常被带入的异常级别的编号。异常返回地址储存在ELR_ELx。其中是异常被带入的异常级别。

5.3 异步异常路由

这三种物理中断类型可以被路由到特权异常级别:EL1、EL2或EL3。下图以IRQs为例:

【笔记】Armv8-A Exception model 异常模型_第5张图片

路由使用SCR_EL3和HCR_EL2配置。使用SCR_EL3进行的路由配置将覆盖使用HCR_EL2进行的路由配置。这些控制允许不同的中断类型被路由到不同的软件。

路由到比正在执行的级别更低的异常级别的异常被隐式屏蔽。该异常将被挂起,直到PE的异常级别等于或低于路由到的异常级别。

5.4 确定异常将被带往的执行状态

异常级别的执行状态由更高级别的异常决定。假设所有异常级别都实现了,下表显示了如何确定执行状态:

去往的异常等级: 异常等级取决于:
非安全状态的EL1 HCR_EL2.RW
安全状态EL1 SCR_EL3或者HCR_EL2(如果使能安全EL2)
EL2 SCR_EL3.RW
EL3 EL3的复位状态

5.5 从异常状态返回

软件可以通过执行AArch64中的ERET指令来启动异常返回。

这将会导致异常等级根据SPSR_ELx的值进行返回。

SPSR_ELx包含返回的目标等级和返回的执行状态。

注意,SPSR_ELx中指定的执行状态必须与SCR_EL3.RWHCR_EL2.RW中的任何一个配置匹配,否则将非法。

执行ERET指令时,状态将从SPSR_ELx恢复,程序计数器将更新为ELR_ELx中的值。这两个更新将以原子和不可分割的方式执行,这样PE就不会处于未定义的状态。

5.6 异常栈

当在AArch64中执行时,架构允许选择两个堆栈指针寄存器;SP_EL0或SP_ELx,其中是当前异常级别。例如,在EL1,可以选择SP_EL0或SP_EL1。

在常规执行期间,所有代码都应该使用SP_EL0。在处理异常时,首先选择SP_ELx。这将维护一个单独的栈来处理异常的前若干步。

这对于在处理由堆栈溢出引起的异常时很有用。

6.向量表

在Armv8-A中,向量表是包含指令的普通内存区域。处理器元素(PE)保存着系统寄存器中表的基址,并且每种异常类型都定义了与该基址的偏移量。

每一个特权异常等级都有着各自的向量表,由VBAR_ELx(Vector Base Address Register)定义基址。

复位后VBAR寄存器的值是未定义的,在启动中断前寄存器必须配置好。

向量表格式如下:

【笔记】Armv8-A Exception model 异常模型_第6张图片

每种异常类型都可能导致 跳转到四个位置中的一个,这个位置基于异常所在级别的状态。

参考

https://developer.arm.com/architectures/learn-the-architecture/exception-model

你可能感兴趣的:(arm,操作系统)