Armv8/Armv9的异常中断模型

文摘   科技   2024-10-10 07:28   上海  
点击左上方蓝色“Arm精选”,选择“设为星标


1 简介

本文介绍了 Armv8-A 中的异常和特权模型。本文涵盖了 Arm 架构中不同类型的异常,以及处理器在收到异常时的行为。本文适用于底层代码的开发人员,例如引导代码或驱动程序。它与编写代码来设置或管理异常的任何人都特别相关。

2 特权和异常模型

在讲解Armv8-A异常模型的细节之前,我们先来介绍一下特权的概念。现代软件期望被分成不同的模块,每个模块对系统和处理器资源的访问级别不同。这方面的一个例子是操作系统内核(具有对系统资源的高级访问权限)和用户应用程序访问呢的系统资源是有限的。

Armv8-A 通过实现不同级别的权限来实现这种拆分。当前权限级别只能在处理器接受或从异常中返回时更改。因此,这些特权级别在 Armv8-A 架构中被称为异常级别。每个异常级别都有编号,特权级别越高,编号越大。

如下图所示,异常级别称为 EL,其中 x 为 0 到 3 之间的数字。例如,最低权限级别称为 EL0。一个常见的使用模型是应用程序代码在 EL0 上运行,操作系统在 EL1 上运行。EL2 由hypervisror使用,EL3 由TrustedFirmware使用。

注意:体系结构不强制执行此软件模型,但标准软件采用此模型。出于这个原因,本文的其余部分假设了这种使用模型

2.1. 特权的类型

有两种与此相关的特权。第一个是内存系统中的特权,第二个是从访问处理器资源的角度来看的特权。两者都受当前异常级别的影响。

2.2. 内存特权

Armv8-A 实现了一个虚拟内存系统,其中内存管理单元 (MMU) 允许软件为内存区域分配属性。这些属性包括读/写权限,可以配置两个自由度。此配置允许特权和非特权访问的单独访问权限。

当处理器在 EL0 中执行时启动的内存访问将根据非特权访问权限进行检查。来自 EL1、EL2 和 EL3 的内存访问将根据特权访问权限进行检查。

由于此内存配置是由软件使用 MMU 的转换表编程的,因此您应该考虑对这些表进行编程所需的特权。MMU 配置存储在系统寄存器中,访问这些寄存器的能力也受当前异常级别控制。

2.3. 寄存器的访问

Armv8-A 处理器的配置设置保存在一系列称为系统寄存器的寄存器中。系统寄存器中的设置组合定义了当前的处理器上下文。对系统寄存器的访问由当前异常级别控制

系统寄存器的名称表示可以访问该寄存器的最低异常级别。例如,TTBR0_EL1 是保存 EL0 和 EL1 使用的转换表的基地址的寄存器。无法从 EL0 访问该寄存器,任何尝试这样做都会导致生成异常。

该体系结构有许多具有概念上相似功能的寄存器,它们的名称仅在异常级别后缀上有所不同。这些是独立的、单独的寄存器,在指令集中有自己的编码,并将在硬件中单独实现。例如,以下寄存器都为不同的转换机制执行 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,所有控制都来自 EL1 可访问寄存器。其他控制寄存器通常遵循此模型。

较高的异常级别有权访问控制较低级别的寄存器。例如,EL2 有权在必要时访问 SCTLR_EL1。在系统的一般操作中,特权异常级别通常会控制自己的配置。然而,更高的特权级别有时会访问与较低异常级别相关联的寄存器,例如,实现虚拟化功能或在上下文切换或电源管理操作期间作为保存和恢复操作的一部分读取和写入寄存器集。

3 执行状态和安全状态

Armv8-A 处理器的当前状态由异常级别和其他两个重要状态决定。当前执行状态定义了通用寄存器的标准宽度和可用指令集。执行状态也会影响内存模型的各个方面以及如何管理异常。

当前安全状态控制当前有效的异常级别、当前可以访问的内存区域以及这些访问在系统内存总线上的表示方式。

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

3.1. 执行状态

Armv8-A提供两种执行状态:

  • AArch32: The 32-bit Execution state. Operation in this state is compatible with Armv7-A. There are two available instruction sets: T32 and A32. The standard register width is 32 bits.

  • AArch64: The 64-bit Execution state. There is one available instruction set: A64. The standard register width is 64 bits

3.2. 安全状态

Armv8-A 架构允许实现两种安全状态。这允许进一步划分软件以隔离和划分受信任的软件。

这两种安全状态是:

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

  • Non-secure state 在这种状态下,PE 只能访问非安全物理地址空间。PE 也只能访问允许非安全访问的系统寄存器。在此状态下运行的软件只能确认非安全中断

3.3. 执行状态的改变

PE 只能在reset或异常级别更改时更改执行状态.

复位时的执行状态由 IMPLEMENTATION DEFINED 机制确定。一些实现修复了重置时的执行状态。例如,Cortex-A32 将始终重置为 AArch32 状态。在 Armv8-A 的大多数实现中,复位后的执行状态由在复位时采样的信号控制。这允许在片上系统级别控制复位执行状态。

当 PE 在 Exception 级别之间发生变化时,也可以更改 Execution 状态。AArch32 和 AArch64 之间的转换只允许遵守某些规则

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

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

将这两个规则放在一起意味着 64 位之上可以允许 32 位,但反过来不行。例如,64 位操作系统内核可以同时托管 64 位和 32 位应用程序,而 32 位操作系统内核只能托管 32 位应用程序。在此示例中,我们使用了操作系统和应用程序,但相同的规则适用于所有异常级别。例如,EL2 的 32 位虚拟机管理程序只能托管 EL1 的 32 位虚拟机

3.4. 安全状态的改变

EL3 始终被视为在安全状态下执行。使用 SCREL3,EL3 代码可以更改所有较低异常级别的安全状态。如果软件使用 SCREL3 更改较低异常级别的安全状态,则 PE 将不会更改安全状态,直到它更改为较低的异常级别。

3.5. 实现定义的异常级别和执行状态

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

EL0 和 EL1 是唯一必须实现的异常级别。EL2 和 EL3 是可选的。选择不实施 EL3 或 EL2 具有重要意义

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

同样,EL2 包含许多虚拟化功能。没有 EL2 的实现可以访问这些功能。该架构的所有当前 Arm 实现都实现了所有异常级别,如果没有所有异常级别,就不可能使用大多数标准软件

实现还可以选择对每个异常级别有效的执行状态。如果在异常级别允许使用 AArch32,则必须允许所有较低的异常级别。例如,如果 EL3 允许 AArch32,那么它必须在所有较低的异常级别上都允许

许多实现允许所有执行状态和所有异常级别,但存在有限制的现有实现。例如,Cortex-A32 只允许任何异常级别的 AArch32

一些现代实现,例如 Cortex-A55,实现了所有异常级别,但只允许 AArch32 在 EL0。(例如可参考这篇文章:Cortex-A76仅EL0支持aarch32) 其他异常级别 EL1、EL2 和 EL3 必须是 AArch64.

4 异常类型

异常是可以导致当前正在执行的程序被挂起并导致状态发生变化以执行代码来处理该异常的任何事件。其他处理器架构可能将此描述为中断。在 Armv8-A 架构中,中断是一种外部产生的异常。Armv8-A 架构将异常分为两大类:同步异常和异步异常。

4.1. 同步异常

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

尝试执行无效指令可能会导致同步异常,无论是当前异常级别不允许的指令还是已禁用的指令。

由于地址未对齐或 MMU 权限检查之一失败,内存访问也可能导致同步异常。由于这些错误是同步的,因此可以在尝试访问内存之前发生异常。内存访问也可以产生异步异常,这将在本节中讨论。内存管理指南中更详细地讨论了内存访问错误。

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

Debug exceptions也是同步异常

4.2. 异步异常

某些类型的异常是在外部生成的,因此与当前指令流不同步。这意味着无法准确保证何时会发生异步异常。Armv8-A 架构只要求它在有限的时间内发生。异步异常也可以暂时屏蔽。这意味着在发生异常之前,异步异常可以处于挂起状态。

异步异常的种类有:Physical interrupts• SError (System Error) • IRQ • FIQVirtual Interrupts• vSError (Virtual System Error) • vIRQ (Virtual IRQ) • vFIQ (Virtual FIQ)

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

4.3. IRQ and FIQ

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

IRQ 和 FIQ 具有独立的路由控制,通常用于实现安全和非安全中断,如通用中断控制器指南中所述

4.4. SError

SError 是一种异常类型,旨在由内存系统生成以响应错误的内存访问。SError 的典型用途是以前称为外部的异步中止,例如已通过所有 MMU 检查但在内存总线上遇到错误的内存访问。这可能会被异步报告,因为该指令可能已经退出。SError 中断也可能由某些 RAM 上的奇偶校验或纠错码 (ECC) 检查引起,例如内置缓存中的那些。

5 Handling exceptions

当发生异常时,当前程序流程被中断。处理元素 (PE) 将更新当前状态并分支到向量表中的某个位置。通常这个位置将包含通用代码,用于将当前程序的状态推送到堆栈上,然后分支到进一步的代码。

5.1. Exception terminology

识别出异常时处理器所处的状态称为taken from 。异常发生后 PE 立即所处的状态是taken to。例如,可以从 AArch32 EL0 到 AArch64 EL1 进行异常处理。

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

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

5.2. Taking an exception

当发生异常时,必须保留当前状态以便可以返回。PE 会自动保存异常返回地址和当前 PSTATE

存储在通用寄存器中的状态必须由软件保存。PE 然后将当前 PSTATE 更新为架构中为该异常类型定义的 PSTATE,并跳转到向量表中的异常处理程序。

发生异常的taken from的PSTATE 存储在系统寄存器 SPSRELx 中,其中是taken to的编号。异常返回地址存储在 ELRELx 中,其中是taken to的编号。

5.3. Routing asynchronous exceptions

三种物理中断类型可以独立路由到特权异常级别之一,EL1、EL2 或 EL3。下图以 IRQ 为例:此路由是使用 SCREL3 和 HCREL2 配置的。使用 SCREL3 进行的路由配置将覆盖使用 HCREL2 进行的路由配置。这些控件允许将不同的中断类型路由到不同的软件。

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

5.4. Determining which Execution state an exception is taken to

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

5.5. Returning from an exception

软件可以通过执行来自 AArch64 的 ERET 指令来启动从异常返回。这将导致根据 SPSRELx 的值配置返回的异常级别,其中是从中返回的级别。SPSRELx 包含要返回的目标级别和目标执行状态。

注意 SPSRELx 中指定的 Execution state 必须与 SCREL3.RW 或 HCR_EL2.RW 中的配置匹配,否则会产生非法异常返回。

执行 ERET 指令时,状态将从 SPSRELx 恢复,PC将更新为 ELRELx 中的值。这两个更新将以原子方式且不可分割地执行,以便 PE 不会处于未定义状态。

5.6. Exception stacks

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

在一般执行期间,预计所有代码都使用 SPEL0。发生异常时,最初选择 SPELx。这允许为初始异常处理维护单独的堆栈。这对于在处理由堆栈溢出引起的异常时维护有效堆栈很有用。

6 The vector tables

在 Armv8-A 中,向量表是包含指令的普通内存区域。处理器元素 (PE) 将表的基地址保存在系统寄存器中,并且每个异常类型都具有一个相对于该基址的定义偏移量。

每个特权异常级别都有自己的向量表,由向量基地址寄存器 VBAR_ELx 定义,其中是 1,2 或 3。

VBAR 寄存器的值在复位后未定义,因此必须在启用中断之前对其进行配置.

向量表的格式如下所示:每种异常类型都可以导致跳转到四个位置之一,具体取决于从中获取异常的taken form的状态。




Arm精选
ARMv8/ARMv9架构、SOC架构、Trustzone/TEE安全、终端安全、SOC安全、ARM安全、ATF、OPTEE等
 最新文章