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);
 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的規範。
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章