Home System Error(SError) in AArch64
Post
Cancel

System Error(SError) in AArch64

在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

This post is licensed under CC BY 4.0 by the author.