AArch64架构下异常与中断处理及相关疑问解答
运行环境
我通过以下命令在QEMU中运行AArch64裸机应用:
qemu-system-aarch64 -M virt \ -cpu cortex-a72 \ -bios "$(QEMU_PATH)/share/qemu/edk2-aarch64-code.fd" \ -m 128M \ -nographic \ -device loader,file=$(BUILD_DIR)/kernel.elf \ -device loader,addr=0x40100000,cpu-num=0
当前应用运行在EL1特权级,配置异常向量表时参考了如下示例代码:
.text .global vector_table .balign 2048 vector_table: b . .balign 0x80 b . .balign 0x80 b . .balign 0x80 b . ... adr x0, vector_table msr VBAR_EL2, x0
已知需将vector_table地址写入VBAR_EL1寄存器指定异常入口,但仍有三个疑问:
1. 为何向量表要按2048字节对齐?
AArch64的异常向量表总大小固定为2048字节(0x800),包含16个异常入口,每个入口占128字节(0x80)。CPU寻址向量表时,会直接忽略VBAR_ELx寄存器的低11位(因为2^11=2048),只取高地址位作为向量表的起始基地址。如果不按2048字节对齐,低11位不为0,CPU会自动截断这部分,导致向量表实际起始地址和你设置的不一致,直接打乱异常入口的寻址逻辑。这是硬件强制要求,必须按此对齐才能让CPU正确定位每个异常入口。
2. 为何入口点偏移为0x80,而官方文档中的偏移不同?
AArch64的向量表有两种布局模式,针对不同特权级的异常划分了不同的入口位置,但每个异常入口块的固定大小都是128字节(0x80)。你看到的.balign 0x80是在为每个入口块预留固定空间,不管当前入口写了多少代码,都对齐到下一个0x80的位置,保证下一个异常入口的地址准确。
而官方文档里的"不同偏移",指的是不同类型异常在向量表中的相对位置(比如EL1的同步异常在起始地址+0x0,IRQ在+0x80,FIQ在+0x100),但每个入口块的间隔确实是0x80。教程里的写法只是先给每个入口块占位,后续再填充具体的处理代码。
3. EL1级别需处理哪些异常和中断?
EL1是操作系统内核运行的特权级,需要处理的异常和中断主要分为四类:
- 同步异常:用户态(EL0)触发的系统调用(SVC指令)、非法指令、内存访问错误(空指针、权限不足)、未定义指令、除数为零等,这类异常是CPU执行指令时直接触发的,内核需要处理或给用户进程返回错误。
- IRQ中断:外设触发的常规中断,比如定时器中断、UART串口输入、网卡数据包到达、键盘输入等,是外部设备主动向CPU发送的信号,内核需要响应并处理对应设备事件。
- FIQ中断:快速中断,优先级高于IRQ,用于处理响应时间要求极高的场景(比如实时系统的紧急事件),部分硬件会合并处理FIQ和IRQ,但AArch64硬件仍保留该机制。
- SError中断:系统错误异常,比如内存ECC错误、总线错误等硬件层面的严重故障,内核需要处理这类致命或可恢复的硬件问题。
另外,EL0没有权限处理任何异常,所有来自EL0的异常都会自动转到EL1处理。
内容的提问来源于stack exchange,提问作者Denis Steinman

