系列上篇:ARMv8指令集架构
Overview
在详细解释 AArch64 Exception 模型之前,我们需要引入权限的概念。
现代软件被开发成分成不同的模块,每个模块对系统和处理器资源的访问级别不同。这方面的一个例子是操作系统内核和用户应用程序之间的分离。操作系统需要执行我们不希望用户应用程序能够执行的操作。内核需要对系统资源进行高级访问,而用户应用程序需要有限的系统配置能力。特权决定软件实体可以查看和控制哪些处理器资源。
AArch64 架构通过实现不同级别的特权来实现这种划分。当前特权级别只能在处理器接受异常或从异常中返回时更改。因此,这些特权级别在 Arm 架构中被称为异常级别。
Exception levels
需要注意的一点在于:该体系结构没有指定什么软件使用哪个异常级别,只是常见或标准软件采用此模型。
:::warning
异常转移:
当出现异常时,异常级别可以增加或保持不变。您永远不能通过异常转移到较低的特权级别。从异常返回时,异常级别可以降低或保持不变。您永远不能通过从异常返回来移动到更高的特权级别。
:::
异常类型
同步异常
同步异常与当前正在执行的指令直接相关。例如,同步异常将由试图写入 MMU 定义的只读位置的指令触发。
有四种产生同步异常的原因
Invalid instructions and trap exceptions
尝试执行无效指令可能会导致同步异常。导致无效指令的原因有很多,包括未定义的指令、当前异常级别不允许的指令或已被禁用的指令。任何执行内核无法识别的指令的尝试都会生成UNDEFINED异常。
当执行特定操作(例如读取特定寄存器)时,陷阱会触发异常。
例如,EL1 的操作系统内核可能会禁用 EL0 的浮点指令,以节省应用程序之间上下文切换的时间。这被称为惰性上下文切换;例如,如果在上下文切换之前未使用 SIMD 或浮点 (FP) 单元,则可以减少压入堆栈的寄存器数量。然后可以使用陷阱异常来处理边缘情况。
在这种情况下,操作系统内核可以通过禁用 SIMD/FP 单元来监视 SIMD/FP 操作的状态。当执行 FP 或 SIMD 指令时,将在 EL1 处将陷阱异常带到操作系统内核。然后内核可以启用 SIMD/FP 单元,执行失败的指令并设置一个标志来声明 SIMD/FP 单元已被使用。这可确保在下一次上下文切换时将大型 SIMD/FP 寄存器文件包含在寄存器上下文中。如果在下一次上下文切换时没有断言标志,则不需要包括 SIMD/FP 寄存器。
内存访问
内存访问也可能导致同步异常。这可能是 MMU 执行检查的结果,也可能是由于内存系统返回的错误。
例如,当启用 MMU 时,将检查所有由加载和存储指令引起的内存访问。如果您尝试从非特权代码访问特权地址,或尝试写入只读地址,则触发异常。当然,内存访问也可以生成异步异常
异常生成指令/系统调用
Arm 架构包括异常生成指令SVC、HVC和SMC。这些指令的目的仅仅是为了生成异常并使 PE 能够在异常级别之间移动:
- Supervisor Call ( SVC) 指令使 EL0 的用户程序能够请求 EL1 的 OS 服务
- 如果实施了虚拟化扩展,则Hypervisor Call ( HVC) 指令可用,使操作系统能够在 EL2 上请求管理程序服务
- Secure Monitor Call ( SMC) 指令,如果实现了安全扩展,则可用,使普通世界能够从 EL3 的固件请求安全世界服务
当 PE 在 EL0 执行时,它不能直接调用 EL2 的管理程序或 EL3 的安全监视器,因为这只能从 EL1 和更高版本调用。EL0 的应用程序必须使用 SVC 调用内核,并让内核执行调用更高异常级别的操作。
调试异常
调试异常是路由到托管调试器的异常级别的同步异常。然后调试器代码的执行很像异常处理程序代码。
有许多特定的同步调试异常,包括:
- 断点指令异常
- 断点异常
- 观察点异常
- 向量捕获异常
- 软件步骤异常
异步异常
异步异常与当前正在执行的指令没有直接关联,通常是来自处理器外部的系统事件。这可能是软件需要响应的系统事件,例如计时器的活动或屏幕的触摸。我们不知道它们何时会发生。异步异常也称为中断。
物理中断
物理中断通常由外围设备产生。系统不是内核不断轮询外部信号,而是通过生成中断来通知内核必须发生某些事情。
例如,系统可能使用通用异步接收器/发送器 (UART) 接口与外界通信。当 UART 接收数据时,它需要一种机制能够告诉处理器新数据已经到达并准备好进行处理。UART 可以使用的一种机制是生成中断以向处理器发出信号。
复杂的系统可能有许多具有不同优先级的中断源,包括嵌套中断处理的能力,其中较高优先级的中断可以中断较低优先级的中断。内核响应此类事件的速度可能是系统设计中的一个关键问题,称为中断延迟。
错误SError
系统错误 (SError) 是一种异常类型,旨在由内存系统生成以响应意外事件。我们不希望发生这些事件,但需要知道它们是否已经发生。这些是异步报告的,因为触发事件的指令可能已经停用。
SError 的一个典型示例是以前称为外部异步中止的情况。SError 中断的示例包括:
已通过所有 MMU 检查但随后在内存总线上遇到错误的内存访问
在某些 RAM 上进行奇偶校验或纠错码 (ECC) 检查,例如内置高速缓存中的那些
将脏数据从高速缓存行回写到外部存储器触发的中断
SErrors 被视为一个单独的异步异常类,因为您通常会对这些情况有单独的处理程序。SError 生成是实现定义的。
IRQ 和 FIQ
Arm 架构有两种异步异常类型,IRQ 和 FIQ,旨在用于支持外设中断的处理。这些用于发出外部事件信号,例如定时器关闭,并不表示系统错误。它们是与处理器指令流异步的预期事件。
在旧版本的 Arm 架构中,FIQ 被用作更高优先级的快速中断。这与 AArch64 不同,在AArch64 中,FIQ 与 IRQ 具有相同的优先级。
掩蔽
物理和虚拟异步异常都可以被临时屏蔽。这意味着异步异常可以保持在挂起状态,直到它们被取消屏蔽并采取异常。这对于处理嵌套异常特别有用。
不能屏蔽同步异常。这是因为同步异常是由指令的执行直接引起的,因此如果它们被挂起或忽略,将会阻止执行。
异常向量表
ARM v8有4个异常级别,每一个异常级别对应一个 VBAR(Vector Base Address Register) 寄存器,用来指向异常向量表的基地址,同时每一个异常向量表会分为4组,每一组包含4 种异常,
- 如果发生异常并不会导致exception level切换,并且使用的栈指针是SP_EL0,那么使用第一组异常向量表。
- 如果发生异常并不会导致exception level切换,并且使用的栈指针是SP_EL1/2/3,那么使用第二组异常向量表。
- 如果发生异常会导致exception level切换,并且比目的exception level低一级的exception level运行在AARCH64模式,那么使用第三组异常向量表。
- 如果发生异常会导致exception level切换,并且比目的exception level低一级的exception level运行在AARCH32模式,那么使用第四组异常向量表。
发生异常时,先根据所处异常级别,根据VBAR_ELx找到异常向量表,再根据发生的异常类型分组,有不同的偏移量。