Linux異常現場--pt_regs淺析


Linux-5.4


結構struct pt_regs用以在堆棧中保存異常發生時的現場寄存器信息,其具體定義與cpu架構相關;內核發生異常時輸出的debug信息就是通過show_regs(regs)來打印的(實際上並步嚴謹,有些上下文中可能無法獲取到pt_regs時使用dump_stack())。
下面以arm64為背景進行介紹。

系統發生異常時會根據異常類型核異常級別進入到對應的異常向量入口,然后開始進行異常現場的保存:

1 kernel_ventry

sub     sp, sp, #S_FRAME_SIZE

在堆棧中預留出S_FRAME_SIZE大小的空間,S_FRAME_SIZE定義在arch/arm64/kernel/asm-offsets.c文件中,大小為sizeof(struct pt_regs);這里就是在堆棧中預留出sizeof(struct pt_regs)大小空間,預留后堆棧指針sp指向這個pt_reg的起始地址。

2 kernel_entry

stp     x0, x1, [sp, #16 * 0]
.......
stp     x28, x29, [sp, #16 * 14]
...
/* 如果異常發生在用戶態(EL0)則pt_regs將保存用戶態堆棧指針sp_el0 */
.if     \el == 0
mrs     x21, sp_el0
.else
/* 否則,異常發生在>EL0的異常級別,則pt_regs將保存上一次堆棧指針的起始位置 */
add     x21, sp, #S_FRAME_SIZE
.endif 

mrs     x22, elr_el1
mrs     x23, spsr_el1
stp     lr, x21, [sp, #S_LR]        //將異常發生前的鏈接指針lr和堆棧指針sp存放到堆棧中

.if \el == 0
stp     xzr, xzr, [sp, #S_STACKFRAME]
.else
stp     x29, x22, [sp, #S_STACKFRAME]        //對於EL != 0時,pt_regs.stackframe[]存放FP和異常LR
.endif
add     x29, sp, #S_STACKFRAME
stp     x22, x23, [sp, #S_PC]    //將異常鏈接地址(即異常處理完畢后的返回地址)elr_el1和異常發生前PE狀態存保存在堆棧中

在kernel_entry這個宏中會將該次異常發生前的x0~x29寄存器、sp、lr、elr_el1、spsr_el1等等寄存器存放到堆棧的struct pt_regs內存中。

3 異常處理具體流程(以中斷處理為例,el0_irq 或者el1_irq)

//irq_handler宏
ldr_l   x1, handle_arch_irq        //irq處理函數鈎子,對於gic-v3而言是gic_handle_irq(struct pt_regs *regs)
mov     x0, sp            //用sp作為irq處理函數handle_arch_irq()的第一個參數
blr     x1

下面是更進一步調用關系:

gic_handle_irq(struct pt_regs *regs)
handle_domain_irq(gic_data.domain, irqnr, regs);
    set_irq_regs(regs)
        __this_cpu_write(__irq_regs, new_regs);

其中__irq_regs是percpu變量,專門用於存放中斷發生時的現場堆棧指針

/*
 * Per-cpu current frame pointer - the location of the last exception frame on
 * the stack
 */
DECLARE_PER_CPU(struct pt_regs *, __irq_regs);

 最終, 各個cpu上的中斷異常發生時的現場就存放在percpu __irq_regs中,通過get_irq_regs()函數可獲取到。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM