Linux内核do_sea()分析

1、do_sea()函数用途

该函数是硬件同步错误的处理函数,分下面4种情况。
“synchronous external abort”:SEA的内核处理入口。
“level x (translation table walk)”:未知
“synchronous parity or ECC error”:待支持的RAS错误处理
“level x synchronous parity error (translation table walk)”:待支持的RAS错误处理

2、do_sea()流程

函数代码如下:

641 static int do_sea(unsigned long addr, unsigned int esr, struct pt_regs *regs)
642 {
643         const struct fault_info *inf;
644         void __user *siaddr;
645
646         inf = esr_to_fault_info(esr);
647
648         /*
649          * Return value ignored as we rely on signal merging.
650          * Future patches will make this more robust.
651          */
652         apei_claim_sea(regs);
653
654         if (esr & ESR_ELx_FnV)
655                 siaddr = NULL;
656         else
657                 siaddr  = (void __user *)addr;
658         arm64_notify_die(inf->name, regs, inf->sig, inf->code, siaddr, esr);
659
660         return 0;
661 }

其中apei_claim_sea()用来解析APEI的表格,获取固件传递的数据。
arm64_notify_die()是处理的关键函数,处理区分用户态和内核态,用户态直接杀进程,内核态则调用die()。die()其实就是oops的执行程序。oops正常只是打印一些调用栈信息,如果内核还能继续运行,还是可以接着往下跑的,但如果配置了panic_on_oops=1,就会panic。

顺便说下,SEA的设计预期是,如果内核态触发SEA,就要panic,而不是让核接着运行。

3、panic流程

然后系统panic了,CPU的状态是怎样的呢?
从代码来看,该处理函数会先关本地中断,然后执行smp_send_stop()或者crash_smp_send_stop()来停止其它核。如果配置了定时重启,就会执行emergency_restart()做紧急重启,否则,会再打开本地中断,防止丢失一些重要信息的打印。

参考内核代码kernel/panic.c

4、smp_send_stop()

该函数在arch/arm64/kernel/smp.c中实现的, 主要做的事情就是调用smp_cross_call(&mask, IPI_CPU_STOP)让其它核停止。
smp_cross_call的执行涉及了IPI机制,可以如下博文:
一个bug引发的linux smp 血案

其实就是发送一个消息给另外的核,然后让其处理,处理函数是handle_IPI()。
IPI_CPU_STOP事件的处理方式,是把核标记成offline状态,然后MASK住DAIF,让核在那里空跑,如下函数所示:

 833 static void local_cpu_stop(void)834 {835         set_cpu_online(smp_processor_id(), false);836837         local_daif_mask();838         sdei_mask_local_cpu();839         cpu_park_loop();840 }

其中local_daif_mask()实现如下:

 21 /* mask/save/unmask/restore all exceptions, including interrupts. */22 static inline void local_daif_mask(void)23 {24         WARN_ON(system_has_prio_mask_debugging() &&25                 (read_sysreg_s(SYS_ICC_PMR_EL1) == (GIC_PRIO_IRQOFF |26                                                     GIC_PRIO_PSR_I_SET)));2728         asm volatile(29                 "msr    daifset, #0xf           // local_daif_mask\n"30                 :31                 :32                 : "memory");3334         /* Don't really care for a dsb here, we don't intend to enable IRQs */35         if (system_uses_irq_prio_masking())36                 gic_write_pmr(GIC_PRIO_IRQON | GIC_PRIO_PSR_I_SET);3738         trace_hardirqs_off();39 }

备注:mask住daif之后,EL3的中断还是能进的。

5、备注

5.1、内核DAIF能否屏蔽EL3的中断

答案是不能,解释可参考ARM的规范。
在这里插入图片描述


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部