第三十期 Linux核心的異常(2)

2021-10-05 20:34:20 字數 4804 閱讀 3766

上一期中我們介紹了 linux 4.19 核心的異常向量表,這一期我們將介紹 linux 4.19 核心在 arm64 處理器上的異常處理。

當異常發生時,處理器需要呼叫異常處理程式來處理異常,該呼叫過程可以粗略地分為儲存處理器當前狀態、呼叫異常處理程式和恢復異常發生前的處理器狀態三步,具體說來核心中異常處理的流程是[1]:

儲存處理器當前狀態。將當預處理器狀態 pstate 儲存在 spsr_el1 暫存器中,將返回位址儲存在 elr_el1 暫存器中,這兩個暫存器中的變數會在返回時被 eret 指令用於恢復處理器狀態。通過設定處理器狀態中的除錯掩碼位d、系統錯誤掩碼位a、中斷掩碼位i和快速中斷掩碼位f禁止除錯異常、系統錯誤異常、外部中斷和快速中斷。將發生錯誤的原因儲存在 esr_el1 暫存器中,將同步異常的錯誤位址儲存在 far_el1 暫存器中。如果處理器處於異常級別 el0 則將異常級別提公升到 el1。根據異常向量表基位址、生成異常的異常級別和異常型別計算出異常向量的位置,通過異常向量跳轉到異常處理程式的入口。異常向量表的基位址是儲存在 vbar_el1 暫存器中的。

呼叫異常處理程式。以異常級別 el0 下64位應用程式發生的同步異常為例,其異常處理程式入口為 sync,異常向量表通過 kernel_ventry 巨集跳轉到了該入口(同步異常和非同步異常的概念見第27期)。kernel_ventry 在跳轉的時候會為將異常級別加到跳轉入口之前,所以這時實際跳轉到的入口是 el0_sync,其彙編**在 openeuler/kernel/blob/kernel-4.19/arch/arm64/kernel/entry.s 檔案中可以找到:

/*

* el0 mode handlers.

*/.align 6

el0_sync:

kernel_entry 0

mrs x25, esr_el1 // read the syndrome register

lsr x24, x25, #esr_elx_ec_shift // exception class

cmp x24, #esr_elx_ec_svc64 // svc in 64-bit state

b.eq el0_svc

cmp x24, #esr_elx_ec_dabt_low // data abort in el0

b.eq el0_da

cmp x24, #esr_elx_ec_iabt_low // instruction abort in el0

b.eq el0_ia

cmp x24, #esr_elx_ec_fp_asimd // fp/asimd access

b.eq el0_fpsimd_acc

cmp x24, #esr_elx_ec_sve // sve access

b.eq el0_sve_acc

cmp x24, #esr_elx_ec_fp_exc64 // fp/asimd exception

b.eq el0_fpsimd_exc

cmp x24, #esr_elx_ec_sys64 // configurable trap

b.eq el0_sys

cmp x24, #esr_elx_ec_sp_align // stack alignment exception

b.eq el0_sp

cmp x24, #esr_elx_ec_pc_align // pc alignment exception

b.eq el0_pc

cmp x24, #esr_elx_ec_unknown // unknown exception in el0

b.eq el0_undef

cmp x24, #esr_elx_ec_breakpt_low // debug exception in el0

b.ge el0_dbg

b el0_inv

這段**首先通過 kernel_entry 巨集儲存了異常處理前的通用暫存器狀態,然後從 esr_el1 暫存器中讀取了錯誤的原因,移位之後和各種異常的標誌值相比較,若與某一異常的標誌值相同則跳轉到該異常的處理程式。kernel_entry 巨集會將通用暫存器的值儲存在當前程序的核心棧中。其彙編**比較複雜,可以在同乙個檔案中找到:

.macro kernel_entry, el, regsize =64.

if \regsize ==

32mov w0, w0 // zero upper 32 bits of x0

.endif

stp x0, x1,

[sp, #16*0

]stp x2, x3,

[sp, #16*1

]stp x4, x5,

[sp, #16*2

]stp x6, x7,

[sp, #16*3

]stp x8, x9,

[sp, #16*4

]stp x10, x11,

[sp, #16*5

]stp x12, x13,

[sp, #16*6

]stp x14, x15,

[sp, #16*7

]stp x16, x17,

[sp, #16*8

]stp x18, x19,

[sp, #16*9

]stp x20, x21,

[sp, #16*10

]stp x22, x23,

[sp, #16*11

]stp x24, x25,

[sp, #16*12

]stp x26, x27,

[sp, #16*13

]stp x28, x29,

[sp, #16*14

]……

跳轉到處理程式後將會進行異常處理,以異常級別el0下的資料中止異常為例,其處理程式的彙編**在同乙個檔案中可以找到:

el0_da:

/** data abort handling

*/mrs x26, far_el1

enable_daif

ct_user_exit

clear_address_tag x0, x26

mov x1, x25

mov x2, sp

bl do_mem_abort

b ret_to_user

在這段**中,處理器從 far_el1 暫存器中讀取了資料中止發生的虛擬位址,然後呼叫了c程式處理函式 do_mem_abort 並通過暫存器 x0 和 x1 傳遞了兩個引數,其中 x0 中儲存了錯誤發生的虛擬位址,x1 中儲存了錯誤發生的原因(el0_sync 巨集中從 elr_el1 暫存器讀到了 x25 中)。

恢復處理器狀態,繼續執行程式。異常處理程式執行完之後跳轉到了ret_to_user,其彙編**在同乙個檔案中可以找到:

ret_to_user:

disable_daif

gic_prio_kentry_setup tmp=x3

ldr x1,

[tsk, #tsk_ti_flags]

and x2, x1, #_tif_work_mask

cbnz x2, work_pending

finish_ret_to_user:

enable_step_tsk x1, x2

#ifdef config_gcc_plugin_stackleak

bl stackleak_erase

#endif

kernel_exit 0

這段**在最後呼叫了kernel_exit巨集,其作用是將之前kernel_entry中儲存的通用暫存器恢復,並使用eret指令返回異常發生前的程式執行位置,該位置的位址是被事先儲存在elr_el1暫存器中的,其取值情況為[1]:

kernel_exit 巨集的彙編**可以在同乙個檔案中找到:

……

msr elr_el1, x21 // set up the return data

msr spsr_el1, x22

ldp x0, x1,

[sp, #16*0

]ldp x2, x3,

[sp, #16*1

]ldp x4, x5,

[sp, #16*2

]ldp x6, x7,

[sp, #16*3

]ldp x8, x9,

[sp, #16*4

]ldp x10, x11,

[sp, #16*5

]ldp x12, x13,

[sp, #16*6

]ldp x14, x15,

[sp, #16*7

]ldp x16, x17,

[sp, #16*8

]ldp x18, x19,

[sp, #16*9

]ldp x20, x21,

[sp, #16*10

]ldp x22, x23,

[sp, #16*11

]ldp x24, x25,

[sp, #16*12

]ldp x26, x27,

[sp, #16*13

]ldp x28, x29,

[sp, #16*14

]ldr lr,

[sp, #s_lr]

add sp, sp, #s_frame_size // restore sp

……

本期我們考察了 linux 4.19 核心的異常處理,下一期我們將介紹 linux 核心中的中斷處理流程。

參考文獻

[1] 《linux核心深度解析》,余華兵著,2019

python第三十課 異常 異常物件傳遞過程

演示異常物件傳遞的過程 往上 拋 並將其解決def func1 print func1.print 10 0 deffunc2 print func2.try func1 except exception as e print e def func3 print func3.func2 try fu...

Leetcode第三十題 串聯所有單詞的子串

題目 給定乙個字串 s 和一些長度相同的單詞 words。找出 s 中恰好可以由 words 中所有單詞串聯形成的子串的起始位置。注意子串要與 words 中的單詞完全匹配,中間不能有其他字元,但不需要考慮 words 中單詞串聯的順序。示例 1 輸入 s barfoothefoobarman wo...

第三十次總結 程序和執行緒的關係

粘包的解決 1,延遲,阻塞 發訊息1 time.sleep 0.1 input,recv recvfrom 發訊息2 2,改變我們的傳送流程 客戶端 服務端 先傳送資料的長度 接收資料長度,存為乙個變數,cl 傳送資料的內容 連線套接字.recv cl struct模組 ret struct.pac...