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()函數可獲取到。
