天天看點

IA-32e模式下的異常處理

系統異常處理

CPU如果調用系統異常處理程式

需要的資料結構
  • IDT_Table: 中斷向量表, 在中斷向量表中的每一項都是一個中斷描述符(中斷門或者陷阱門), 一個中斷描述符中的幾位是段選擇符
  • GDT或者LDT
處理過程(沒有特權變化的情況),在進行中斷和異常的時候, 需要處理程式, 注意: 是處理程式的代碼, 每一個處理程式都要有的執行現場的儲存工作
  • 中斷向量号 -> IDT -> 中斷描述符 -> 段選擇子 -> GDT/LDT -> 段描述符 -> 基位址, 同時得知中斷描述符儲存的段内偏移量 -> 中斷處理程式(在開頭最先執行的就是程式現場保留的工作, 在entry.S中完成, 在entry.S檔案中定義了每一個寄存器在目前棧頂的偏移量, 用來在保留現場的時候将對應的寄存器的值放到對應的偏移量中, 這樣友善恢複現場(恢複現場的程式也在entry.S中, 該檔案中有一個子產品(函數), 是将所有可用寄存器中的資料都壓入到棧中))
處理過程(有特權變化的情況)
  • CPU将EFLAGS, CS, EIP, Error Code都壓入棧

注意

  • 中斷和異常向量都在一張IDT表中, IDT表總共256項
  • IDT表的前32項都是異常向量, 但是21-31是Intel保留的不能使用, 是以我們實際上使用的是20個異常
  • 我們已經在head.S程式的前半部分手動初始化了一下IDT(是不完整的初始化), 在head.S程式的後半階段我們setup IDT表示, 給這256個異常和中斷向量的異常處理程式都設定為同一個目标代碼位址, 都是顯示一串字元串"Error! Unknown interrupt or fault at RIP"
  • 初始化完畢IDT之後, 緊接着我們要初始化TSS
真正進入到核心程度之後的系統異常處理
  • 在trap.c檔案中對每一個異常都提供一個異常處理函數(trap, interrupt, system)
  • 在trap.c檔案中, 使用set_trap_gate, set_intr_gate, set_system_gate三個函數為20個異常項提供處理函數, 處理函數名為nmi, divide_error, page_fault等, 他們都是在彙編層面上編寫的, 因為C語言不能對寄存器進行棧操作, 在這些彙編函數中, 會調用各自的do_nmi, do_divide_error等子函數, 這些才是真正處理異常的邏輯封裝, 是在C語言層面編寫的, 主要就是編寫do程式, 而nmi, divide_error的存在就是為了程式的現場恢複與保留等額外的處理
  • 在do函數中, 我們要顯示的是異常資訊, 如果發生異常時的error_code(隻有内部中斷才會有錯誤碼, 是以int指令和外部中斷是沒有的), rip, rsp, 以及通過分析error_code的32位得出的詳細錯誤資訊

繼續閱讀