0%

ARM64固件优先SEA/SEI RAS处理基本逻辑

基本逻辑

SEA (Synchronous External Abort)

  • 同步外部中止,由当前指令执行直接触发
  • 异常返回地址指向触发异常的指令本身
  • 常见原因:内存错误、设备访问错误、页表遍历错误

SEI (SError Interrupt)

  • 异步外部中止,与当前指令执行无直接关联
  • 异常返回地址指向下一条指令
  • 也称为 SError

RAS (Reliability, Availability, Serviceability)

  • ARMv8.2-A 引入的 RAS 扩展,提供硬件错误检测和报告机制
  • 支持错误记录、错误注入、错误恢复等功能

当SEI/SEA发生的时候,硬件可以通过配置决定是报到EL2还是EL3(固件),通常系统会配置
成报到固件,因为固件是私有的可以查看私有硬件模块的出错信息。固件处理完成后,固件
把需要报给OS的信息放到APEI表里,然后退回OS处理,这里一般是直接退到OS的异常向量
入口。OS里通过读APEI表的信息,得到RAS的更多信息。

硬件通过SCR_EL3配置SEA/SEI报到EL2还是EL3。

1
2
3
4
5
6
7
8
9
10
11
12
SCR_EL3
+--------+----------------------------------------------------+
| Bit 3 | EA (External Abort) |
| | 1 = External Abort/SError routed to EL3 |
| | 0 = External Abort routed based on exception level |
+--------+----------------------------------------------------+
| Bit 2 | FIQ (Fast Interrupt) |
| | 1 = FIQ routed to EL3 |
+--------+----------------------------------------------------+
| Bit 1 | IRQ (Interrupt Request) |
| | 1 = IRQ routed to EL3 |
+--------+----------------------------------------------------+

注意,整个硬件系统分很多部件,比如很多L3/内存相关的部件OS里是看不见的,而core上
报SEA/SEI是core和这些OS看不见部件综合作用的结果,这些部件可以有私有的配置,这些
私有的配置甚至可以决定给core是否返回可以触发core SEA/SEI的信号,这些私有配置有些
还可以决定是否这些私有模块发现错误的时候直接报RAS相关的中断上来。

Linux内核处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// arch/arm64/mm/fault.c
do_sea(unsigned long addr, unsigned int esr, struct pt_regs *regs)
{
...
arm64_notify_die
if (user_mode(regs)) <--- 用户态触发错误时,会向进程发信号,终止进程
arm64_force_sig_fault
else <--- 如果是内核触发,会做一系列的处理后panic内核
die(str, regs, err)
...
__die()
crash_kexec() <--- 注意crash要显式调用,不然不会有vmcore
panic()
}

注意,有的时候,一个RAS错误源头可能触发一个SEA和一个SEI,如果在SEA处理逻辑还没有
关中断的时候,SEI被taken了,那么SEI看到的信息是SEA的栈信息,无法打印出实际触发这个
RAS错误的栈。这个时候可以hack下这里的do_sea,一进来就panic内核,这样可以看到触发
RAS错误的栈。

RAS处理中上下文保存和恢复

EL级别切换示意:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
+-------------+
| EL0/EL1/EL2 |
+-------------+
| \
| \
| \
| v
| +-------+
SEA | | EL2 |
| +-------+
| ^
| /
| /
v /
+-------------+
| EL3 |
+-------------+

逻辑上看,比EL3低的特权级发生SEA时,CPU直接跳到EL3,EL3这时是知道EL0/EL1/EL2的上
下文的。EL3在做完RAS相关处理后,需要把控制转移到EL2异常入口处理,这个转移过程逻辑
上应该模拟EL0/EL1/EL2直接到EL2异常的过程,这样到EL2后,EL2异常处理先保存EL0/EL1/EL2
的上下文,然后进入EL2处理,各种信息不会丢失。理论上看,EL3应该把之前的上下文放到
GPR和EL2相关的寄存器上,SPSR_EL3配置为一个固定PSTATE值,模拟EL0/EL1/EL2异常进入EL2
时EL2 PSTATE的值(这个值是进入EL2时的新PSTATE值,一般是一个固定的值)。

总结下EL3中的配置:

X0-X31 = EL0/EL1/EL2进入EL3的上下文
SPSR_EL2 = EL0/EL1/EL2进入EL3的SPSR_EL3
SPSR_EL3 = 一个固定的PSTATE

ERET到EL2

实际上,在RAS处理的流程中,之前EL0/EL1/EL2的PSTATE的值,已经不重要了。所以,直接
把SPSR_EL3配置成一个固定的值就好。