在ARMv8环境中遇到了 SError(System Error) ,作参考记录。
什么SError?
SError全称为:System Error,是ARM架构中的一种类型的异常。
AArch64(ARM64)架构中,主要包括如下4中类型的异常:
- Synchronous exception(同步异常),“同步”可以理解为:发生异常的指令为导致异常的指令,即当导致异常发生的指令执行时能立即触发异常。 包括ARM架构中定义的所有Aborts异常,如:指令异常、数据异常、对齐异常等。SError,System Error,是一种异步异常,后面再仔细说明。
- IRQ,普通的中断,是异步异常。
- FIQ,高优先级的中断,是异步异常。
- SError本质上是一种异步外部abort(asynchronous external abort)。所谓异步,就说是发生异常时硬件(相关的寄存器)不能提供有效信息用于分析定位, 异常发生时的指令,并不是导致异常的指令。外部意味着异常来自于外部存储系统(相较于CPU来说,MMU是内部的)。
通常,软件不太可能导致这样的问题,通常是硬件触发,但也不绝对。
SError产生的原因?
到了大家最关心的问题,什么原因可能导致SError。 这个名字实在太宽泛,根据Arm相关手册的说明,其可能原因有很多,比较常见的原因包括:
- asynchronous Data Aborts,异步数据异常,数据异常即CPU读写内存数据时产生的异常。 比如:如果误将ROM对应的区域的页表项设置为RW, 同时该内存熟悉为write-back cacheable,当尝试写这段区域内存时,数据会先写入cache,而在后面的某个时间点,当其他操作触发相应的脏的cacheline回写内存时, 此时内存系统就会返回错误(因为ROM是只读的),从而触发一个异步数据异常。 在Armv8环境中,这就是一个标准的SError。
- 外部引脚触发。 部分处理器,比如Cortex-A5x,拥有独立的引脚来触发SError,也就是说此时的SError是由SoC设计者自己定义的, 比如:ECC和奇偶校验错误就常作为SError来触发。具体应该参考SoC的参考手册来确认SError可能的对应的错误类型。
内核如何处理SError?
Linux内核中,对SError进行了捕获,设置了相应的中断向量,当并未做实际的处理,只是上报异常,并终止进程会内核, 因为对于内核来说,SError是致命的,内核自身无法做相应的修复操作,内核不知道具体原因,也不知道如何修复。
内核中相应的处理具体包括:
设置了相应的中断向量,对应的中断向量设置在entry.S汇编代码中,AArch64对应的四种状态下的SError对应中断处理函数都一样:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/\*el1代表内核态,el0代表用户态*/
ENTRY(vectors)
ventry el1_sync_invalid // Synchronous EL1t
ventry el1_irq_invalid // IRQ EL1t
ventry el1_fiq_invalid // FIQ EL1t
/*内核态System Error ,使用SP_EL0(用户态栈)*/
ventry el1_error_invalid // Error EL1t
ventry el1_sync // Synchronous EL1h
ventry el1_irq // IRQ EL1h
ventry el1_fiq_invalid // FIQ EL1h
/*内核态System Error ,使用SP_EL1(内核态栈)*/
ventry el1_error_invalid // Error EL1h
ventry el0_sync // Synchronous 64-bit EL0
ventry el0_irq // IRQ 64-bit EL0
ventry el0_fiq_invalid // FIQ 64-bit EL0
/*用户态System Error ,使用SP_EL1(内核态栈)*/
ventry el0_error_invalid // Error 64-bit EL0
...
END(vectors)
el0_error_invalid实现:
1
2
3
el0_error_invalid:
inv_entry 0, BAD_ERROR
ENDPROC(el0_error_invalid)
el1_error_invalid实现:
1
2
3
el1_error_invalid:
inv_entry 1, BAD_ERROR
ENDPROC(el1_error_invalid)
可见,最终都调用了inv_entry,inv_entry实现如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/*
* Invalid mode handlers
*/
/*Invalid类异常都在这里处理,统一调用bad_mode函数*/
.macro inv_entry, el, reason, regsize = 64
kernel_entry el, \regsize
/*传入bad_mode的三个参数*/
mov x0, sp
/*reason由上一级传入*/
mov x1, #\reason
/*esr_el1是EL1(内核态)级的ESR(异常状态寄存器),用于记录异常的详细信息,具体内容解析需要参考硬件手册*/
mrs x2, esr_el1
/*调用bad_mode函数*/
b bad_mode
.endm
调用bad_mode,是C函数,通知用户态进程或者panic。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/*bad_mode handles the impossible case in the exception vector. */
asmlinkage void bad_mode(struct pt_regs *regs, int reason, unsigned int esr)
{
siginfo_t info; /*获取异常时的PC指针*/
void __user *pc = (void __user *)instruction_pointer(regs);
console_verbose();
/*打印异常信息,messages中可以看到。*/
pr_crit(“Bad mode in %s handler detected, code 0x%08x – %s\n”, handler[reason], esr, esr_get_class_string(esr));
/*打印寄存器内容*/
__show_regs(regs);
/*如果发生在用户态,需要向其发送信号,这种情况下,发送SIGILL信号,所以就不会有core文件产生了*/
info.si_signo = SIGILL;
info.si_errno = 0;
info.si_code = ILL_ILLOPC;
info.si_addr = pc;
/*给用户态进程发生信号,或者die然后panic*/
arm64_notify_die(“Oops - bad mode”, regs, &info, 0);
}
个人实例
是在ARM初始化 PCIe RC时 发生的SError,RC的初始化后面是异步的,CPU0仍在执行RC的驱动代码,CPU1去扫描PCIe EP设备了(根据log), 其实就是到系统枚举PCIe设备的阶段了,此时CPU1产生了SError,内核信息的Call trace打印,可以发现是读取PCIe EP的配置空间时产生SError。 而硬件通路上确实无法读取到设备的配置空间。CPU发出的地址正确,需要硬件做些调整。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
[ 5.479194] SError Interrupt on CPU1, code 0xbf000000 -- SError
[ 5.479195] CPU: 1 PID: 1 Comm: swapper/0 Not tainted 5.10.0-xilinx-v2021.2 #1
[ 5.479196] Hardware name: Xilinx Versal (DT)
[ 5.479198] pstate: 20000085 (nzCv daIf -PAN -UAO -TCO BTYPE=--)
[ 5.479199] pc : pci_generic_config_read+0xe0/0x130
[ 5.479201] lr : pci_generic_config_read+0x78/0x130
[ 5.479202] sp : ffff80001152b900
[ 5.479203] x29: ffff80001152b900 x28: 0000000000000003
[ 5.479207] x27: ffff800010e29d58 x26: ffff000800126d80
[ 5.479210] x25: ffff80001152bb68 x24: 0000000000000000
[ 5.479212] x23: 0000000000000000 x22: 0000000000000004
[ 5.479215] x21: ffff800010e28d90 x20: ffff80001152b994
[ 5.479217] x19: 0000000000000000 x18: 0000000000000030
[ 5.479220] x17: 0000000000000000 x16: 000000000000000c
[ 5.479223] x15: ffff000800127198 x14: 72656e65675f6963
[ 5.479225] x13: ffff800011383de0 x12: 000000000000029a
[ 5.479228] x11: 00000000000000de x10: ffff8000113afde0
[ 5.479231] x9 : 00000000fffff800 x8 : ffff800011383de0
[ 5.479234] x7 : ffff8000113afde0 x6 : 0000000000000000
[ 5.479236] x5 : 0000000000005ff4 x4 : 0000000000000000
[ 5.479239] x3 : 0000000000000000 x2 : 271fe057132a3400
[ 5.479241] x1 : 0000000000000000 x0 : 0000000000000043
[ 5.479244] Kernel panic - not syncing: Asynchronous SError Interrupt
[ 5.479246] CPU: 1 PID: 1 Comm: swapper/0 Not tainted 5.10.0-xilinx-v2021.2 #1
[ 5.479247] Hardware name: Xilinx Versal (DT)
[ 5.479248] Call trace:
[ 5.479249] dump_backtrace+0x0/0x190
[ 5.479250] show_stack+0x18/0x30
[ 5.479252] dump_stack+0xd4/0x110
[ 5.479253] panic+0x15c/0x324
[ 5.479254] add_taint+0x0/0xb0
[ 5.479255] arm64_serror_panic+0x78/0x84
[ 5.479256] do_serror+0x3c/0x6c
[ 5.479257] el1_error+0x88/0x108
[ 5.479258] pci_generic_config_read+0xe0/0x130
[ 5.479260] pci_bus_read_config_dword+0x94/0x120
[ 5.479261] pci_bus_generic_read_dev_vendor_id+0x34/0x1b0
[ 5.479262] pci_scan_single_device+0xa4/0x144
[ 5.479264] pci_scan_slot+0x40/0x12c
[ 5.479265] pci_scan_child_bus_extend+0x54/0x2bc
[ 5.479266] pci_scan_root_bus_bridge+0x68/0xdc
[ 5.479267] xilinx_pcie_probe+0x3b4/0x7d0
[ 5.479268] platform_drv_probe+0x54/0xb0
[ 5.479270] really_probe+0xe8/0x4b4
[ 5.479271] driver_probe_device+0x58/0xc0
[ 5.479272] device_driver_attach+0xc0/0xd0
[ 5.479273] __driver_attach+0x84/0x124
[ 5.479274] bus_for_each_dev+0x70/0xc0
[ 5.479276] driver_attach+0x24/0x30
[ 5.479277] bus_add_driver+0x104/0x1ec
[ 5.479278] driver_register+0x78/0x130
[ 5.479279] __platform_driver_register+0x4c/0x60
[ 5.479280] xilinx_pcie_driver_init+0x1c/0x28
[ 5.479282] do_one_initcall+0x54/0x1bc
[ 5.479283] kernel_init_freeable+0x1f4/0x264
[ 5.479284] kernel_init+0x14/0x114
[ 5.479285] ret_from_fork+0x10/0x3c
......
[ 5.791124] SMP: stopping secondary CPUs
[ 5.791125] Kernel Offset: disabled
[ 5.791126] CPU features: 0x0040022,21002008
[ 5.791127] Memory Limit: none
以上log,el1_error
开始就是SError 异常部分的调用情况了。
log第一行显示了SError的错误码为 0xbf000000 ,((ESR寄存器内容)),参考ARMv8手册和厂商芯片手册。(optional)
ref: http://happyseeker.github.io/kernel/2016/03/03/about-system-error-in-AArach64.html
http://happyseeker.github.io/kernel/2016/03/03/SError-again.html
https://blog.csdn.net/weixin_45647912/article/details/121340096