- 1 建立向量表
- 2 建立向量表的示例
- 3 使能中断
异常初始化的过程:
- 建立异常向量表
- 异常路由和屏蔽配置
1 建立向量表
AArch64中,reset不再是异常向量表的一部分,它有专用的配置输入管脚和寄存器。其余的异常存储在异常向量表中。
1.1 Reset向量
AArch64中,处理器的开始执行位置是跟处理器的实现有关的,由硬件输入管脚RVBARADDR控制,可以在RVBAR_EL3
中读取该地址。启动(boot)代码应该放在该地址处。
1.2 向量表
每个异常级别都有专门的异常向量表,分别存储在下面的寄存器中:
- VBAR_EL3
- VBAR_EL2
- VBAR_EL1
AArch64的向量表与AArch32的不同。每个向量占用128字节,最多包含32个指令。向量表按照2K大小对齐。初始化的时候,将向量表的基地址写入到VBAR_ELx
寄存器中即可。
关于向量表更多的细节,可以参考ARM官方文档ARM® Architecture Reference Manual ARMv8, for ARMv8-A architecture
规范中异常向量表部分。
下表展示了向量表的组成:
地址 | 异常类型 | 描述 |
---|---|---|
VBAR_Eln 0x000 | Synchronous | 异常EL与异常前的EL相同,且使用SP_EL0 |
0x080 | IRQ/vIRQ | |
0x100 | FIQ/vFIQ | |
0x180 | Serror/vSError | |
0x200 | Synchronous | 异常EL与异常前的EL相同, 且使用SP_ELx |
0x280 | IRQ/vIRQ | |
0x300 | FIQ/vFIQ | |
0x380 | Serror/vSError | |
0x400 | Synchronous | 异常前的EL比异常EL低,异常前系统模式为aarch64 |
0x480 | IRQ/vIRQ | |
0x500 | FIQ/vFIQ | |
0x580 | Serror/vSError | |
0x600 | Synchronous | 异常前的EL比异常EL低,异常前系统模式为aarch32 |
0x680 | IRQ/vIRQ | |
0x700 | FIQ/vFIQ | |
0x780 | Serror/vSError |
2 建立向量表的示例
示例1
:展示了reset之后,如何初始化向量表基地址寄存器
LDR X1, = vector_table_el3
MSR VBAR_EL3, X1
LDR X1, = vector_table_el2
MSR VBAR_EL2, X1
LDR X1, = vector_table_el1
MSR VBAR_EL1, X1
示例2
:展示了AArch64状态下一个典型的异常向量表
.balign 0x800 // 向量表2k(2048字节)大小对齐
Vector_table_el3:
curr_el_sp0_sync: // synchronous处理程序
// 来自当前EL的异常,使用SP0
.balign 0x80
curr_el_sp0_irq: // IRQ中断处理程序
// 来自当前EL的异常,使用SP0
.balign 0x80
curr_el_sp0_fiq: // FIQ快速中断处理程序
// 来自当前EL的异常,使用SP0
.balign 0x80
curr_el_sp0_serror: // Serror系统错误的处理程序
// 来自当前EL的异常,使用SP0
.balign 0x80
curr_el_spx_sync: // synchronous处理程序
// 来自当前EL的异常,使用SPx
.balign 0x80
curr_el_spx_irq: // IRQ中断处理程序
// 来自当前EL的异常,使用SPx
.balign 0x80
curr_el_spx_fiq: // FIQ快速中断处理程序
// 来自当前EL的异常,使用SPx
.balign 0x80
curr_el_spx_serror: // Serror系统错误的处理程序
// 来自当前EL的异常,使用SPx
.balign 0x80
lower_el_aarch64_sync: // synchronous处理程序
// 来自低EL且处于AArch64的异常
.balign 0x80
lower_el_aarch64_irq: // IRQ中断处理程序
// 来自低EL且处于AArch64的异常
.balign 0x80
lower_el_aarch64_fiq: // FIQ快速中断处理程序
// 来自低EL且处于AArch64的异常
.balign 0x80
lower_el_aarch64_serror:// Serror系统错误的处理程序
// 来自低EL且处于AArch64的异常
.balign 0x80
lower_el_aarch32_sync: // synchronous处理程序
// 来自低EL且处于AArch32的异常
.balign 0x80
lower_el_aarch32_irq: // IRQ中断处理程序
// 来自低EL且处于AArch32的异常
.balign 0x80
lower_el_aarch32_fiq: // FIQ快速中断处理程序
// 来自低EL且处于AArch32的异常
.balign 0x80
lower_el_aarch32_serror:// Serror系统错误的处理程序
// 来自低EL且处于AArch32的异常
3 使能中断
异常分为异步和同步异常,异步异常通俗的讲就是我们常规意义上的
中断
,同步异常就是我们常规意义上的异常
中断包括SError
、IRQ
和FIQ
。这些中断在reset之后,默认是屏蔽掉的。因此,如果想要获取SError
、IRQ
和FIQ
,必须设置路由规则,并清除掉屏蔽。
另外,想要使能中断,还应该初始化中断控制器,使其发送中断请求给处理器,但这不是本文的范围。
3.1 中断路由规则
中断的路由规则,决定了中断发生时,哪个异常级别处理该中断。如果要路由到EL3,需要设置SCR_EL3.{EA,IRQ,FIQ}
。
示例3
:展示了如何将SError
、IRQ
和FIQ
路由到EL3异常级别的设置
MRS X0, SCR_EL3
ORR X0, X0, #(1<<3) // 设置EA位
ORR X0, X0, #(1<<1) // 设置IRQ位
ORR X0, X0, #(1<<2) // 设置FIQ位
MSR SCR_EL3, X0
想把中断路由到EL2而不是EL3,必须设置HCR_EL2.{AMO,FMO,IMO}
并清除SCR_EL3.{EA,IRQ,FIQ}
。
示例4
:展示如何将SError
、IRQ
和FIQ
路由到EL2异常级别的设置
MRS X0, HCR_EL2
ORR X0, X0, #(1<<5) // 设置AMO位
ORR X0, X0, #(1<<4) // 设置IMO位
ORR X0, X0, #(1<<3) // 设置FMO位
MSR HCR_EL2, X0
如果中断没有设置路由到EL3或EL2,默认路由到EL1。
3.2 中断的掩码
中断是否被屏蔽,取决于下面的因素:
- 中断被路由到的目标异常级别
PSTATE.{A,I,F}
的值
目标异常级别低于当前异常级别,不管PSTATE.{A,I,F}
的值是多少,该中断都被屏蔽(隐含规则);
目标异常级别等于当前异常级别,如果PSTATE.{A,I,F}
设置为1,则异常被屏蔽;
目标异常级别高于当前异常级别,且目标异常级别是EL2或EL3,不管PSTATE.{A,I,F}
的值是多少,异常都会被接收;
目标异常级别高于当前异常级别,且目标异常级别是EL1, 如果PSTATE.{A,I,F}
设置为1,则异常被屏蔽;
示例5
:展示如何在PSTATE
中清除SError
、IRQ
和FIQ
的掩码
// 使能SError, IRQ和FIQ
MSR DAIFClr, #0x7
更多关于使能中断的细节,查看ARM® Architecture Reference Manual ARMv8, for ARMv8-A architecture
规范中的异步异常的类型、路由、屏蔽和优先级
部分。