x86 保護模式結合了以描述元為基礎的分段、受控控制轉移、分頁與特權指令。長模式降低了一般分段的作用,但仍保留描述元表、特權層級、中斷閘門與工作狀態區段(TSS)。作業系統使用這些機制進入核心並隔離使用者執行,而在目前的 x86-64 系統中,頁表提供主要的記憶體保護邊界。
表格暫存器 #
全域描述元表(GDT)包含由邏輯處理器共用的程式碼、資料與系統描述元。GDTR 儲存 GDT 的線性基底與位元組限制。依照慣例,GDT 項目 0 是空描述元,因此索引為 0 的選擇子不能用於一般程式碼或資料存取。
區域描述元表(LDT)是透過 LDTR 選取的選用表格。LDTR 包含 GDT 中 LDT 系統描述元的選擇子,並快取解析後的基底、限制與屬性。在傳統架構中,工作可以擁有自己的 LDT,但目前的 64 位元作業系統很少使用 LDT。
IDTR 儲存中斷描述元表(IDT)的線性基底與位元組限制。IDT 以例外或中斷向量為索引,而不是以一般區段選擇子為索引。其項目是閘門,用於識別處理常式程式碼並控制進入語意。
工作暫存器(TR)包含 GDT 中 TSS 描述元的選擇子,並快取該描述元。LGDT、LIDT、LLDT 與 LTR 會在架構定義的特權與型別檢查下載入表格狀態。
選擇子與隱藏快取 #
16 位元區段選擇子包含索引、表格指示器(TI)以及要求的特權層級(RPL):
15 3 2 1 0
+--------------------------------+---+---+
| index | TI|RPL|
+--------------------------------+---+---+TI=0 選取 GDT,而 TI=1 選取目前的 LDT。處理器將索引乘以描述元大小,檢查表格限制,擷取描述元,並驗證其型別、存在狀態以及該操作所需的特權。
CS、SS、DS、ES、FS 與 GS 會公開選擇子,但也包含基底、限制、型別與存取權限的隱藏快取。記憶體參照會使用快取,而不會再次讀取描述元表。因此,重寫 GDT 或 LDT 項目不會變更已載入的區段;軟體必須重新載入選擇子,或執行架構要求的轉移。
LDTR 與 TR 也會快取解析後的描述元。變更 GDT、LDT 或 TSS 的內容切換必須考慮這些快取,而不能將可見的選擇子視為完整狀態。
區段描述元 #
傳統保護模式的程式碼與資料描述元長度為 8 位元組。它們編碼 32 位元基底、可選擇 4 KiB 粒度的 20 位元限制、描述元特權層級(DPL)、存在位元、描述元類別以及型別專用旗標。
S=1表示程式碼或資料描述元;S=0表示系統描述元或閘門。- 程式碼描述元選擇可執行、可讀取與一致性行為。
- 資料描述元選擇可寫入與向下擴展行為。
G會縮放限制,而D/B則依描述元型別控制預設運算元大小或堆疊大小。- 在長模式中,
L表示 64 位元程式碼區段;與D的無效組合會被拒絕。
分段會將描述元基底加到指令的有效偏移,並在分頁前檢查限制與存取權限。平坦的 32 位元模型使用基底為零且限制較大的區段,同時保留這些檢查。
在 64 位元模式中,一般位址產生會將 CS、SS、DS 與 ES 的基底視為零,且不套用其傳統限制。FS 與 GS 保留分別控制的 64 位元基底。程式碼區段型別、CS 特權以及選定的 SS 規則仍具有架構上的重要性。
系統描述元與閘門 #
系統描述元用於識別 LDT、可用或忙碌的 TSS,以及傳統閘門。IA-32 使用 8 位元組的 TSS 與 LDT 描述元。在 64 位元模式中,TSS 與 LDT 描述元佔用 16 位元組,以便容納 64 位元基底;高 8 位元組會使用下一個 GDT 槽位。
呼叫閘門指定目的地程式碼選擇子與偏移,為對相同或更高特權層級執行遠端呼叫提供經檢查的路徑。傳統呼叫閘門可以將設定數量的參數複製到新堆疊。遠端返回會在另一組檢查下反轉特權轉移。
IDT 項目可以是中斷閘門、陷阱閘門,或在傳統模式中是工作閘門。中斷閘門與陷阱閘門包含處理常式選擇子與偏移。中斷閘門在進入時清除 IF;陷阱閘門則保留它。64 位元中斷閘門或陷阱閘門還包含一個 IST 選擇器,用於緊急堆疊或專用堆疊。
特權值與有效檢查 #
x86 定義了編號 0 至 3 的四個特權層級。數字越小,特權越高。目前特權層級(CPL)由 CS 的低兩位元表示;核心通常在 CPL 0 執行,而應用程式在 CPL 3 執行。環 1 與環 2 存在,但作業系統設計不要求使用它們。
每個描述元都有 DPL,每個選擇子都攜帶 RPL。對許多資料存取而言,處理器會形成有效特權層級:
EPL = max(CPL, RPL)存取一般資料區段需要 EPL <= DPL,同時還必須通過型別與存在狀態檢查。由於數字越大表示特權越低,RPL 可以刻意降低選擇子的權限。SS 載入使用更嚴格的規則:選擇子 RPL 與可寫入資料區段 DPL 必須符合 CPL。
程式碼轉移會依其機制使用特定規則:
- 直接遠端轉移至非一致程式碼區段不會任意降低 CPL;其 DPL 必須符合要求的目前特權關係。
- 一致程式碼區段允許數值相同或特權較低的 CPL 執行,只要其 DPL 允許如此,而 CPL 保持不變。這種轉移不會授予對目標 DPL 資料的存取權。
- 呼叫閘門選擇子會使用 CPL 與 RPL,根據閘門 DPL 進行檢查。閘門接著控制進入其目的地程式碼區段。
- 軟體
INT n會根據 CPL 檢查 IDT 閘門 DPL。處理器例外與外部中斷不會將閘門 DPL 用作呼叫者授權檢查,因為軟體並未透過INT n呼叫它們。
沒有單一的 CPL/DPL 比較可以描述每一種操作。區段載入、一致程式碼、閘門、工作切換、IRET、連接埠 I/O 與特權指令各自定義其檢查規則。
受控轉移 #
使用者程式碼不能載入環 0 程式碼選擇子,然後跳至任意核心位址。閘門、例外、中斷、工作機制或設定好的 syscall 指令會建立受控進入點與已儲存的返回狀態。
對於進入數值較低 CPL 的中斷或例外閘門,處理器會從目前 TSS 取得目的地特權層級的堆疊指標。它會切換堆疊、儲存舊堆疊內容與返回狀態,並從閘門的處理常式開始執行。確切的框架、選用的錯誤碼與儲存值取決於執行模式與向量。
相同特權層級的閘門通常保留目前堆疊。在 64 位元模式中,即使 CPL 沒有變更,非零的 IDT IST 欄位也會選取其設定的 IST 堆疊。接著,核心進入程式碼必須儲存其餘暫存器、建立 ABI 不變量、驗證由使用者控制的狀態,並安全處理巢狀進入。
使用 IRET 返回時,會驗證並還原已儲存的目的地。返回至特權較低的 CPL 時,也會還原外層堆疊。軟體不得將未經檢查的選擇子、旗標或指令指標放入中斷返回框架,因為僅依賴返回時驗證無法表達核心原則。
中斷與系統呼叫 #
例外是指令執行的同步結果。外部中斷透過中斷控制器到達,並在優先權、遮罩與處理器狀態允許時傳遞。兩者最終都會選取 IDT 向量,但只有部分例外會推入架構定義的錯誤碼。
SYSCALL/SYSRET 與 SYSENTER/SYSEXIT 避免一般的 IDT 閘門查找,並使用模型專用暫存器建立進入與返回狀態。64 位元模式中的 SYSCALL 會將下一個 RIP 儲存至 RCX,將 RFLAGS 儲存至 R11,載入設定好的程式碼選擇子與進入 RIP,並遮罩選定的旗標。它不會切換 RSP;核心進入虛設程式必須在不暴露不安全中間狀態的情況下切換到受信任的堆疊。SYSENTER 具有不同的設定堆疊語意。暫存器引數與錯誤慣例屬於作業系統的 syscall ABI,而不是 x86 本身。
呼叫閘門仍然是架構的一部分,但目前的核心通常使用 syscall 指令與中斷閘門,因為這些路徑適合平坦位址空間與軟體定義的進入框架。分頁、SMEP、SMAP、保護金鑰及其他控制機制會補充描述元檢查。
IA-32 硬體工作 #
80286 與 IA-32 保護模式架構將硬體工作定義為由 TSS 與 TSS 描述元表示的狀態。IA-32 TSS 儲存 EIP、EFLAGS、通用暫存器、區段選擇子、CR3、LDT 選擇子、特權層級 0 至 2 的堆疊指標,以及 I/O 權限點陣圖偏移。
硬體工作切換可以透過以下方式啟動:
- 遠端
CALL或JMP至 TSS 描述元。 - 透過工作閘門進行遠端轉移。
- IDT 項目為工作閘門的中斷或例外。
- 當
EFLAGS.NT表示巢狀工作時執行IRET。
處理器會驗證目的地,將選定的離開狀態儲存至目前 TSS,載入目的地 TSS,依要求標記描述元的忙碌狀態,載入 TR 與其他工作狀態,並設定 CR0 中的工作切換旗標。確切的忙碌位元、連結與例外行為取決於啟動機制。
由 CALL、工作閘門或中斷工作閘門啟動的巢狀工作切換,可以將舊 TSS 選擇子寫入目的地 TSS 的回鏈,並設定巢狀工作(NT)旗標。NT=1 的 IRET 會遵循回鏈以恢復先前的工作。回鏈是架構控制狀態,而不是由排程器維護的程序清單;格式錯誤或不一致的工作狀態可能產生工作區段例外。
目前的作業系統會以軟體切換排程中的執行緒,因為硬體工作切換具有固定的狀態配置與副作用,而核心還必須管理排程器中繼資料、位址空間原則、每個 CPU 的狀態、浮點與向量狀態、除錯狀態以及實作特定的緩解措施。軟體切換可以精確儲存核心內容與 ABI 所需的狀態。IA-32 核心仍使用 TSS 實作特權堆疊與 I/O 點陣圖功能,但不使用硬體工作切換進行排程。
長模式中的 TSS 使用方式 #
64 位元模式不支援硬體工作切換,但每個邏輯處理器仍需要有效的 64 位元 TSS 才能正常執行核心。TSS 包含:
RSP0、RSP1與RSP2:當中斷或例外在未選取 IST 的情況下進入相應的較高特權 CPL 時使用。- 七個中斷堆疊表指標,即
IST1至IST7。 - I/O 權限點陣圖偏移。
IDT 閘門的三位元 IST 欄位會選取七個 IST 指標中的一個。當目前堆疊可能無法使用,或處理常式需要獨立堆疊時,IST 很有用,例如雙重錯誤處理路徑。IST 硬體會載入新的堆疊指標,但不會提供完整的軟體巢狀策略。進入程式碼仍必須防止有限的緊急堆疊遭到無控制地重複使用或溢位。
I/O 權限點陣圖可以依據 IOPL 與模式規則,允許或拒絕特權較低的程式碼依連接埠執行 IN 與 OUT。其位置與界限由 TSS 描述元限制推導,因此描述元、偏移、點陣圖與終止位元組必須一致建構。
TSS 描述元仍是 GDT 中的 16 位元組系統描述元。LTR 會將它載入 TR,設定或觀察定義的忙碌型別,並快取其基底與限制。每個邏輯處理器通常都有自己的 TSS,因為特權進入需要處理器本機的受信任堆疊。載入 TR 是進入處理的初始化,而不是排程器的內容切換。
長模式考量 #
長模式保留標準的 CPL 0/CPL 3 核心與使用者區分、程式碼描述元、IDT 閘門、GDT、TR 與 TSS。它移除一般 64 位元位址處理中大部分區段基底與限制的作用,並省略硬體工作切換。因此,頁表的使用者/超級使用者、可寫入與禁止執行權限形成主要的位址空間邊界。
描述元狀態仍不可省略。64 位元程式碼區段必須具有有效描述元,中斷閘門包含程式碼選擇子,特權進入使用 TSS 堆疊狀態,而相容模式會套用額外的傳統描述元語意。啟動與核心程式碼應遵循處理器供應商目前的系統程式設計手冊,因為保留欄位與模式轉移順序是架構要求。
主要參考資料 #
- Intel 64 and IA-32 Architectures Software Developer Manuals,尤其是 Volume 3A
- AMD64 Architecture Programmer’s Manual,尤其是 Volume 2
評論
在 GitHub 上閱讀或評論(發表評論需要登入)