Arm64中的异常处理
閑話
最近優化環境中出現了多次不同種類的異常,其他文章中也有提及,為此專門去研究了一下Arm64的異常處理機制和代碼,之前主要的開發和應用環境為X86,ARM接觸很少,也沒有機會去研究和學習,總以為不會有用上的一天,誰知,現在。。。 可能是機會來了,讓自己多長些見識,學習之后發現又有另一番收獲。
Exception in ARM64
## Exception類型
ARM64中包含如下幾種類型的異常:
異常級別(EL)
ARM中,異常由級別之分,具體如下圖所示,只要關注:
普通的用戶程序處于EL0,級別最低
內核處于EL1,HyperV處于EL2,EL1-3屬于特權級別。
異常處理
Arm中的異常處理過程與X86比較相似,同樣包括硬件自動完成部分和軟件部分,同樣需要設置中斷向量,保存上下文,不同的異常類型的處理方式可能有細微差別。 這里不詳述了。
需要關注:用戶態(EL0)不能處理異常,當異常發生在用戶態時,異常級別(EL)會發生切換,默認切換到EL1(內核態)。
中斷向量表
Arm64架構中的中斷向量表有點特別(相對于X86來說~),包含16個entry,這16個entry分為4組,每組包含4個entry,每組中的4個entry分別對應4種類型的異常:
4個組的分類根據發生異常時是否發生異常級別切換、和使用的堆棧指針來區別。分別對應于如下4組:
代碼分析
## 中斷向量表
Linux內核中,中斷向量表實現在entry.S文件中,代碼如下:
/** Exception vectors.*/.align 11 //EL1的異常向量表保存在VBAR_EL1寄存器中(Vector Base Address Register (EL1)),該寄存器的低11bit是reserve的,11~63表示了Vector Base Address,因此這里的異常向量表是2K對齊的。/*el1代表內核態,el0代表用戶態*/ENTRY(vectors)ventry el1_sync_invalid // Synchronous EL1t ventry el1_irq_invalid // IRQ EL1tventry el1_fiq_invalid // FIQ EL1t/*內核態System Error ,使用SP_EL0(用戶態棧)*/ventry el1_error_invalid // Error EL1tventry el1_sync // Synchronous EL1hventry el1_irq // IRQ EL1hventry el1_fiq_invalid // FIQ EL1h/*內核態System Error ,使用SP_EL1(內核態棧)*/ventry el1_error_invalid // Error EL1hventry el0_sync // Synchronous 64-bit EL0ventry el0_irq // IRQ 64-bit EL0ventry el0_fiq_invalid // FIQ 64-bit EL0/*用戶態System Error ,使用SP_EL1(內核態棧)*/ventry el0_error_invalid // Error 64-bit EL0#ifdef CONFIG_COMPATventry el0_sync_compat // Synchronous 32-bit EL0ventry el0_irq_compat // IRQ 32-bit EL0ventry el0_fiq_invalid_compat // FIQ 32-bit EL0ventry el0_error_invalid_compat // Error 32-bit EL0#elseventry el0_sync_invalid // Synchronous 32-bit EL0ventry el0_irq_invalid // IRQ 32-bit EL0ventry el0_fiq_invalid // FIQ 32-bit EL0ventry el0_error_invalid // Error 32-bit EL0#endifEND(vectors)可以明顯看出分組和分類的情況。
invalid類處理
帶invalid后綴的向量都是Linux做未做進一步處理的向量,默認都會進入bad_mode()流程,說明這類異常Linux內核無法處理,只能上報給用戶進程(用戶態,sigkill或sigbus信號)或die(內核態)
帶invalid后綴的向量最終都調用了inv_entry,inv_entry實現如下:
/** 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。
/** 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); }arm64_notify_die:
void arm64_notify_die(const char *str, struct pt_regs *regs,struct siginfo *info, int err) {/*如果發生異常的上下文處于用戶態,則給相應的用戶態進程發送信號*/if (user_mode(regs)) {current->thread.fault_address = 0;current->thread.fault_code = err;force_sig_info(info->si_signo, info, current);} else {/*如果是內核態,則直接die,最終會panic*/die(str, regs, err);} }IRQ中斷處理
場景的場景中(不考慮EL2和EL3),IRQ處理分兩種情況:用戶態發生的中斷和內核態發生的中斷,相應的中斷處理接口分別為:
el0_sync el1_sync相應代碼如下:
el1_sync:
.align 6 el1_irq:/*保存中斷上下文*/kernel_entry 1enable_dbg #ifdef CONFIG_TRACE_IRQFLAGSbl trace_hardirqs_off #endif/*調用中斷處理默認函數*/irq_handler/*如果支持搶占,處理稍復雜*/ #ifdef CONFIG_PREEMPTget_thread_info tskldr w24, [tsk, #TI_PREEMPT] // get preempt countcbnz w24, 1f // preempt count != 0ldr x0, [tsk, #TI_FLAGS] // get flagstbz x0, #TIF_NEED_RESCHED, 1f // needs rescheduling?bl el1_preempt 1: #endif #ifdef CONFIG_TRACE_IRQFLAGSbl trace_hardirqs_on #endif/*恢復上下文*/kernel_exit 1 ENDPROC(el1_irq)代碼非常簡單,主要就是調用了irq_handler()函數,不做深入解析了,有興趣可以自己再看看代碼。
el0_sync處理類似,主要區別在于:其涉及用戶態和內核態的上下文切換和恢復。
.align 6 el0_irq:kernel_entry 0 el0_irq_naked:enable_dbg #ifdef CONFIG_TRACE_IRQFLAGSbl trace_hardirqs_off #endif/*退出用戶上下文*/ct_user_exitirq_handler#ifdef CONFIG_TRACE_IRQFLAGSbl trace_hardirqs_on #endif/*返回用戶態*/b ret_to_user ENDPROC(el0_irq)
原文地址: https://happyseeker.github.io/kernel/2016/03/04/exception-mechanism-in-AArach64.html
總結
以上是生活随笔為你收集整理的Arm64中的异常处理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: kernel 3.10内核源码分析--中
- 下一篇: ARM Cortex-A 编程手册学习笔