其實, CPU 也是一台計算器. 它也會返回不同的異常, 最常見的如: 除零、溢位。

那麼我們怎麼利用到CPU 的這些信息呢? 在這之前, 我們看看我們可能會有甚麼信息。

https://wiki.osdev.org/Exceptions#Faults

其中最重要的:

  1. Page Fault: 寫到未划分的記憶體或者只讀記憶體.
  2. Invalid Opcode: 查無此opcode
  3. General Protection Fault: 包山包海的記體憶錯誤, 最常見是Segmentation fault, 也就是沒有讀取權限。
  4. Double Fault: 常有Exception 時, exception handler 會被叫起, 而如果exception handler 有Exception... 這就是不幸的Double Fault 了
  5. Triple Fault: Double Fault 會直接上交到CPU, CPU 會有Double fault 的處理函數, 而如果這個函數有錯誤, 這就無藥可救, 會導致電腦(OS)重啟。

那麼CPU 是怎麼把這些異常分類的呢?

我們有...

file

file

中斷描述表

中斷描述表 : Interrupt Descriptor Table (IDT)

它是一個16-byte 的描述規格。

Type| 名稱                     | 描述
----|--------------------------|-----------------------------------
u16 | Function Pointer [0:15]  | 函數位置(頭16bit)
u16 | GDT selector             | 指寫何個全局描述符表(GDT) 的區段的(https://en.wikipedia.org/wiki/Global_Descriptor_Table)
u16 | Options                  | (如下)
u16 | Function Pointer [16:31] | 函數位置(中16bit)
u32 | Function Pointer [32:63] | 函數位置(尾16bit)
u32 | Reserved                 |

Option 中有

The options field has the following format:

Bits  | 名稱                              | 描述
------|-----------------------------------|-----------------------------------
0-2   | Interrupt Stack Table Index       | 跳到IST 的第n 層(0 時當前位置)
3-7   | Reserved                          | 保留
8     | 0: Interrupt Gate, 1: Trap Gate   | 當0 時, 中斷將不會發生
9-11  | must be one                       | checksum 用
12    | must be zero                      | checksum 用
13‑14 | Descriptor Privilege Level (DPL)  | 執行這個handler function 的最低權限等級
15    | Present                           | 是否當前

Interrupt Stack Table (IST) , 中文是 中斷棧
就是在除錯時的, 函數調用的最低階狀態, 不同的CPU 架構有不同的棧結構。aCore 現時是在x86 架構上運行的。

當有異常時, CPU 大概會做下面的事:

  1. Register, instruction pointer 和 RFLAGS register, 三者入棧。
  2. 檢查 Interrupt Descriptor Table (IDT). 是哪一個entry. (e.g. 14 就是page fault)
  3. 這個entry 是當前的layer 嗎? 不是的話, 就Raise 一個Double Fault
  4. 如果bit40 是interrupt gate 的話, disable hardware interrupts (變成soft interrupt)
  5. 映射 GDT(https://en.wikipedia.org/wiki/Global_Descriptor_Table) 中的定義, 取後code segment的位置
  6. 跳到handler function

Rust 中有 內置可以讀取的IDT : https://docs.rs/x86_64/0.11.0/x86_64/structures/idt/struct.InterruptDescriptorTable.html

看源碼時 :

#[repr(C)]
pub struct InterruptDescriptorTable {
    pub divide_by_zero: Entry<HandlerFunc>,
    pub debug: Entry<HandlerFunc>,
    pub non_maskable_interrupt: Entry<HandlerFunc>,
    pub breakpoint: Entry<HandlerFunc>,
    pub overflow: Entry<HandlerFunc>,
    pub bound_range_exceeded: Entry<HandlerFunc>,
    pub invalid_opcode: Entry<HandlerFunc>,
    pub device_not_available: Entry<HandlerFunc>,
    pub double_fault: Entry<HandlerFuncWithErrCode>,
    pub invalid_tss: Entry<HandlerFuncWithErrCode>,
    pub segment_not_present: Entry<HandlerFuncWithErrCode>,
    pub stack_segment_fault: Entry<HandlerFuncWithErrCode>,
    pub general_protection_fault: Entry<HandlerFuncWithErrCode>,
    pub page_fault: Entry<PageFaultHandlerFunc>,
    pub x87_floating_point: Entry<HandlerFunc>,
    pub alignment_check: Entry<HandlerFuncWithErrCode>,
    pub machine_check: Entry<HandlerFunc>,
    pub simd_floating_point: Entry<HandlerFunc>,
    pub virtualization: Entry<HandlerFunc>,
    pub security_exception: Entry<HandlerFuncWithErrCode>,
    // some fields omitted
}

會發覺它們都是(idt::Entry<F>)[https://docs.rs/x86_64/0.11.0/x86_64/structures/idt/struct.Entry.html].
F 代表 handler function 的類型。