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);
836
837 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)));
27
28 asm volatile(
29 "msr daifset, #0xf // local_daif_mask\n"
30 :
31 :
32 : "memory");
33
34 /* 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);
37
38 trace_hardirqs_off();
39 }
備註:mask住daif之後,EL3的中斷還是能進的。
5、備註
5.1、內核DAIF能否屏蔽EL3的中斷
答案是不能,解釋可參考ARM的規範。