内核稳定性问题复杂多样,最常见的莫过于“kernel panic”,意为“内核恐慌,不知所措”。这种情况下系统自然无法正常运转,只能自我结束生命,留下死亡信息。诸如:
“Unable to handle kernel XXX at virtual address XXX”
“undefined instruction XXX”
“Bad mode in Error handler detected on CPUX, code 0xbe000011 -- SError”
......
这些死亡信息是系统在什么状态下产生?如何产生?以及如何处理?本文主要从这三个方面介绍ARMv8架构下CPU的异常处理流程。
一、ARMv8异常简介
1.异常级别
不同于Armv7架构采用CPU模式切换的方式进行异常处理,Armv8架构定义了一组全新的异常级别进行异常处理,即EL0至EL3,有如下特性:
- 如果ELn为异常级别,则n的值增加表示软件执行特权增加。
- EL0处的执行称为无特权执行,不能处理异常。
- EL2提供对虚拟化的支持。
- EL3提供了在两个安全状态(安全状态和非安全状态)之间切换的支持。
一个实现可以不包括所有的异常级别,但都必须包括EL0和EL1。EL2和EL3是可选的。
如下是典型的异常级别使用模型:
2. 同步异常和异步异常
如果满足以下所有条件,则将异常描述为同步的:
- 由于直接执行某个指令而产生异常。
- 异常处理程序的返回地址可以表明导致该异常的指令。
- 异常是精确的。
如果满足以下任一条件,则将异常描述为异步的:
- 不是因为直接执行某条指令而产生异常。
- 异常处理程序的返回地址不可以表明导致该异常的指令。
- 异常是不精确的。
3. 主要寄存器
(1)通用寄存器R0-R30
在基本指令集处理指令时,将使用通用寄存器组。它包括31个通用寄存器R0-R30。这些寄存器可以作为31个64位寄存器X0-X30或31个32位寄存器W0-W30进行访问。
(2)堆栈指针寄存器SP
在AArch64状态下,除了通用寄存器外,还为以下每个异常级别实现了专用的堆栈指针寄存器,
堆栈指针寄存器为:
- SP_EL0和SP_EL1。
- 如果实现包括EL2,则为SP_EL2。
- 如果实现包括EL3,则为SP_EL3。
堆栈指针寄存器选择:
在EL0上执行时,处理器使用EL0堆栈指针SP_EL0。在其他任何异常级别执行时,可以将处理器配置为使用SP_EL0或配置为对应该异常级别的堆栈指针SP_ELx。默认情况下,采用目标异常级别的堆栈指针SP_ELx。例如,EL1的异常选择SP_EL1,软件可以在目标异常级别执行的时候通过更新PSTATE.SP来指向SP_EL0的堆栈指针。
可以通过异常级别的堆栈指针后缀表明所选的堆栈指针:
t表明使用SP_EL0堆栈指针。
h表明使用SP_ELx堆栈指针。
t和h后缀基于线程(thread)和处理程序(handler)的首字母。