日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Arm64中的异常处理

發布時間:2025/3/15 编程问答 21 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Arm64中的异常处理 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

閑話

最近優化環境中出現了多次不同種類的異常,其他文章中也有提及,為此專門去研究了一下Arm64的異常處理機制和代碼,之前主要的開發和應用環境為X86,ARM接觸很少,也沒有機會去研究和學習,總以為不會有用上的一天,誰知,現在。。。 可能是機會來了,讓自己多長些見識,學習之后發現又有另一番收獲。

Exception in ARM64

## Exception類型

ARM64中包含如下幾種類型的異常:

  • 中斷(Interrupts),就是我們平常理解的中斷,主要由外設觸發,是典型的異步異常。 ARM64中主要包括兩種類型的中斷:IRQ(普通中斷)和FIQ(高優先級中斷,處理更快)。 Linux內核中好像沒有使用FIQ,還沒有仔細看代碼,具體不詳述了。
  • Aborts。 可能是同步或異步異常。包括指令異常(取指令時產生)、數據異常(讀寫內存數據時產生),可以由MMU產生(比如典型的缺頁異常),也可以由外部存儲系統產生(通常是硬件問題)。
  • Reset。復位被視為一種特殊的異常。
  • Exception generating instructions。由異常觸發指令觸發的異常,比如Supervisor Call (SVC)、Hypervisor Call (HVC)、Secure monitor Call (SMC)
  • 異常級別(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種類型的異常:

  • SError
  • FIQ
  • IRQ
  • Synchronous Aborts
  • 4個組的分類根據發生異常時是否發生異常級別切換、和使用的堆棧指針來區別。分別對應于如下4組:

  • 異常發生在當前級別且使用SP_EL0(EL0級別對應的堆棧指針),即發生異常時不發生異常級別切換,可以簡單理解為異常發生在內核態(EL1),且使用EL0級別對應的SP。 這種情況在Linux內核中未進行實質處理,直接進入bad_mode()流程。
  • 異常發生在當前級別且使用SP_ELx(ELx級別對應的堆棧指針,x可能為1、2、3),即發生異常時不發生異常級別切換,可以簡單理解為異常發生在內核態(EL1),且使用EL1級別對應的SP。 這是比較常見的場景。
  • 異常發生在更低級別且在異常處理時使用AArch64模式。 可以簡單理解為異常發生在用戶態,且進入內核處理異常時,使用的是AArch64執行模式(非AArch32模式)。 這也是比較常見的場景。
  • 異常發生在更低級別且在異常處理時使用AArch32模式。 可以簡單理解為異常發生在用戶態,且進入內核處理異常時,使用的是AArch32執行模式(非AArch64模式)。 這中場景基本未做處理。
  • 代碼分析

    ## 中斷向量表

    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

    與50位技術專家面對面20年技術見證,附贈技術全景圖

    總結

    以上是生活随笔為你收集整理的Arm64中的异常处理的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。