本文声明:内容来源于网络,进行整合/再创作;部分内容由AI辅助生成。
AArch64 异常调用指令
在AArch64中,某些指令的执行会产生异常。通常会执行以下指令,以从运行于更高异常级别的软件中请求服务。
|---------|--------------|-----------|------------------------------------------------------|--------------------------------------------------|
| 指令 类型 | 助记符 | 异常 级别 | 核心用途 | 16 位有效载荷 (#uimm16) |
| 发 起 异 常 | SVC 管理调用 | EL1 (系统) | 最常用。应用程序(用户模式,EL0)请求操作系统(内核态,EL1)服务(如读写文件、退出程序)。 | 告诉内核 "具体要做什么"。例如 #0 代表退出。 |
| 发 起 异 常 | HVC 系统管理程序调用 | EL2 (虚拟化) | 虚拟机(客户操作系统,通常运行在 EL1)请求虚拟机监控器(Hypervisor,EL2)服务 | 虚拟机向宿主机传递的具体功能编号。 |
| 发 起 异 常 | SMC 安全监视调用 | EL3 (安全) | 正常状态(EL1/EL2)请求安全状态(EL3)的安全服务(如指纹识别、硬件加密) | 向安全监控器传递的具体功能编号。 |
| 异常 返回 | ERET | 返回 原级别 | 通用。处理器在异常处理完成后,返回到原地址继续执行程序。 | 无。当前异常级别的SPSR_ELn寄存器重新构造处理器状态,并自动分支到 LR_ELn中的地址。 |
AArch64 异常表
异常向量表
发生异常时,处理器必须执行与异常对应的处理程序。不同异常处理程序在内存中的入口点集合,就构成了异常向量表。
在ARMv8-A架构的AArch64执行状态下,每个异常级别(EL3、EL2、EL1)都拥有自己独立的异常向量表。这一点非常重要,因为不同级别(如安全监控器、虚拟机监视器、操作系统内核)负责处理不同性质的异常。
向量基址寄存器(VBAR)
每个异常级别对应的向量表在内存中的基地址,由该级别下的一个系统寄存器------向量基址寄存器(Vector Based Address Register) 来设定。具体来说:
EL1使用 VBAR_EL1
EL2使用 VBAR_EL2
EL3使用 VBAR_EL3
向量表的内容、结构与布局
AArch64的向量表里存放的是可以直接执行的指令,而不是单纯的内存地址。这意味着,当异常发生时,处理器直接从向量表的相应入口开始取指执行。
- 表的大小与条目:每个异常级别的向量表总共包含16个条目。
- 条目的大小:每个条目占据连续的128字节。由于AArch64指令固定为4字节,因此每个条目可以容纳最多32条指令。这种设计给了开发者足够的空间在入口处直接实现一些简单的处理逻辑,而不必立即进行跳转,例如顶级异常处理程序可以直接写在向量表中。
如何找到正确的异常处理入口
向量表的偏移量和向量表的基地址
上表是其中一个向量表。基地址由VBAR_ELn提供,然后每个条目从这个基地址定义一个偏移量。当异常发生时,处理器会根据以下三个关键因素,动态计算并跳转到正确的向量表条目(计算公式为:VBAR_ELn + 偏移量):
1、异常的类型(Exception Type)
这是触发向量的根本原因,分为四大类:同步异常(Synchronous)、IRQ(普通中断)、FIQ(快速中断)和SError(系统错误)。
2、异常发生时正在使用的堆栈指针(Stack Pointer):
当异常在同一异常级别内部产生时(例如,EL1的代码触发了EL1级别的异常),处理器的响应取决于其当前配置的堆栈指针:
- 如果使用的是 SP_EL0(通常用于用户态应用),则视为一种错误或特殊场景。
- 如果使用的是 SP_EL1/2/3,即当前级别的专用堆栈指针,则为正常的内核态异常。
3、异常来源的异常级别:
当异常来自比当前级别更低的异常级别时(例如,EL0的用户态应用执行了SVC指令,陷入EL1的内核),处理器会考虑目标低级别执行时的指令集状态:
- 较低异常级别运行在AArch64状态。
- 较低异常级别运行在AArch32状态。
实例分析:假设一个最常见的场景:在EL1(如Linux内核)上执行代码时,收到了一个普通的IRQ中断。
异常来源:由于中断发生时处理器正在EL1执行,这属于"在同一异常级别内部产生"的异常。
堆栈指针:内核代码通常会将SPSel位置1,意味着它正在使用专用的SP_EL1作为堆栈指针。
查找向量表:
- 根据表格,我们需要看"同一异常级别,使用SP_ELx"这一分组。
- 异常类型是"IRQ",对应这一分组中的第二个条目(偏移量 0x280)。
计算入口地址:处理器会从 VBAR_EL1 寄存器中读取EL1向量表的基地址,然后加上偏移量 0x280。从这个计算出的地址开始执行的,就是内核为IRQ编写的顶级处理程序。
向量表的偏移量和向量表的基地址