局部描述符表LDT的作用+定义+初始化+跳转相关
【0】寫在前面
0.1)本代碼的作用: 旨在說明局部描述符表的作用,及其相關定義,初始化和跳轉等內容;
0.2)文末的個人總結是干貨,前面代碼僅供參考的,且source code from orange’s implemention of a os.
0.3)由于本文中代碼和 “實模式和保護模式切換的步驟”的源代碼 有90%之多的相似之處,參見http://blog.csdn.net/pacosonswjtu/article/details/48009165,故, 我這里只對涉及局部描述符LDT的代碼進行簡單注釋說明;
0.4)多注意和(實模式+保護模式)模式切換的過程步驟(http://blog.csdn.net/pacosonswjtu/article/details/48009165)做比較;
; ========================================== ; pmtest2.asm ; 編譯方法:nasm pmtest2.asm -o pmtest2.com ; ==========================================%include "pm.inc" ; 常量, 宏, 以及一些說明org 0100hjmp LABEL_BEGIN
;全局描述符表定義
[SECTION .gdt]
; END of [SECTION .gdt]
;數據段定義 + 全局堆棧段定義
……
;16位代碼段, CPU運行在實模式下,為什么只有在16位代碼段下才能修改GDT中的值
[SECTION .s16] ; Mine【為從實模式跳轉到保護模式所做的準備工作】
; 初始化 LDT 在 GDT 中的描述符 ;Mine【 LABEL_DESC_LDT 作為GDT的表項】
xor eax, eaxmov ax, dsshl eax, 4add eax, LABEL_LDTmov word [LABEL_DESC_LDT + 2], axshr eax, 16mov byte [LABEL_DESC_LDT + 4], almov byte [LABEL_DESC_LDT + 7], ah; 初始化 LDT 中的描述符 ; Mine【初始化LDT的基地址所指向的具體的多任務代碼段】
xor eax, eaxmov ax, dsshl eax, 4add eax, LABEL_CODE_Amov word [LABEL_LDT_DESC_CODEA + 2], axshr eax, 16mov byte [LABEL_LDT_DESC_CODEA + 4], almov byte [LABEL_LDT_DESC_CODEA + 7], ah; 為加載 GDTR 作準備,填充GDT基地址的數據結構
xor eax, eaxmov ax, dsshl eax, 4add eax, LABEL_GDT ; eax <- gdt 基地址mov dword [GdtPtr + 2], eax ; [GdtPtr + 2] <- gdt 基地址; 加載 GDTR
lgdt [GdtPtr]; 關中斷
cli; 打開地址線A20
in al, 92hor al, 00000010bout 92h, al; 準備切換到保護模式, PE位置1
mov eax, cr0or eax, 1mov cr0, eax; 真正進入保護模式
jmp dword SelectorCode32:0 ; 執行這一句會把 SelectorCode32 裝入 cs, 并跳轉到 Code32Selector:0 處; 從保護模式跳回到實模式就到了這里(注意:從保護模式跳轉到實模式,即本標識符下,本標識符是存在于 初始化描述符的16位代碼段的末尾的)
LABEL_REAL_ENTRY: mov ax, csmov ds, axmov es, axmov ss, axmov sp, [SPValueInRealMode]; 關閉 A20 地址線
in al, 92h ; and al, 11111101b out 92h, al ; /sti ; 開中斷mov ax, 4c00h ; `.int 21h ; / 回到 DOS; END of [SECTION .s16]
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 32 位代碼段,即保護模式. 由實模式跳入
[SECTION .s32]
; 下面顯示一個字符串(此處代碼有省略)
; Mine【這里的LDT選擇子索引的是LDT中記錄的描述符表項的基地址值,即具體任務的執行代碼】
; 跳入局部任務
jmp SelectorLDTCodeA:0 SegCode32Len equ $ - LABEL_SEG_CODE32; END of [SECTION .s32]
; 16 位代碼段. 由 32 位代碼段跳入, 本段跳出后到實模式
[SECTION .s16code]
; 跳回實模式:
mov ax, SelectorNormal ; Mine【 選擇子 SelectorNormal 是對描述符 LABEL_DESC_NORMAL 的索引 】mov ds, axmov es, axmov fs, axmov gs, axmov ss, axmov eax, cr0and al, 11111110b ; Mine【cr0的最后一位PE位置為0,進入實模式】mov cr0, eaxLABEL_GO_BACK_TO_REAL:jmp 0:LABEL_REAL_ENTRY ; 段地址會在程序開始處被設置成正確的值 Code16Len equ $ - LABEL_SEG_CODE16; END of [SECTION .s16code]
; LDT 的定義
[SECTION .ldt]
; LDT 選擇子的定義
SelectorLDTCodeA equ LABEL_LDT_DESC_CODEA - LABEL_LDT + SA_TIL; END of [SECTION .ldt]
; CodeA (LDT, 32 位代碼段),通過索引選擇子SelectorLDTCodeA跳入
[SECTION .la]
; 準備經由16位代碼段跳回實模式
jmp SelectorCode16:0 CodeALen equ $ - LABEL_CODE_A; END of [SECTION .la]
總結: 局部描述符表的作用+定義+初始化+跳轉相關:(干貨)
(Attention:我們依照程序執行過程的跳轉步驟來對內存內容進行解析)
(1) org 0100, 告訴編譯器程序運行時,要加載到偏移地址0100處;jmp LABEL_BEGIN 跳入到16位代碼段(實模式)進行各個數據段,代碼段,堆棧段的初始化,最后跳入到局部描述符表中某個描述符所存儲的任務代碼的基地址去運行;
(2)GDT初始化 { 定義段描述符(包括LDT描述符LABEL_DESC_LDT) + 定義GDTR的數據結構 + 定義GDT選擇子(包括LDT描述符LABEL_DESC_LDT的選擇子SelectorLDT)}:
- 2.1)定義全局描述符表GDT表項,即定義需要的段描述符,數據段和代碼段描述符數據結構如下:
- 2.2)定義GDTR全局描述符的數據結構并賦值,GDTR的數據結構如下:
- 2.3)定義GDT選擇子:有多少個描述符表項,就有多少個選擇子,選擇子用于記錄描述符相對于GDT基地址的偏移地址,因為GDT的基地址和界限是保存在全局描述符寄存器GDTR中的,我們可以通過GDTR找到GDT基地址,然后通過選擇子找到對應的偏移地址,從而找到對應的段描述符的基地址+界限(偏移量)+屬性,而描述符基地址的值又記錄了各個代碼段或數據段的基地址,這樣我們就可以通過選擇子來索引到具體的代碼段和數據段;
(3)數據段 + 堆棧段的定義
(4)16位代碼段(實模式下)的定義:
- 4.1)設置代碼運行環境,即給相關寄存器賦值;
- 4.2)初始化16位代碼段描述符 + 32位代碼段描述符 + 堆棧段描述符 +數據段描述符,重點在于初始化 LDT 在 GDT 中的描述符 和 初始化 LDT 中的描述符(在代碼末尾處定義);
- 4.3)初始化全局描述符表寄存器GDTR的內容,因為其基地址還沒有初始化, 然后通過lgdt [GdtPtr],將內存中GDTR的內容加載到GDTR中,重點在于保存 GDT的基地址;
- 4.4)關中斷, 即設置CPU不響應任何其他的外部中斷,因為CPU現在的時間片只屬于當前加載的程序;
- 4.5)打開地址線A20; 至于為什么要打開,呵呵,參見:http://blog.csdn.net/pacosonswjtu/article/details/48005813
- 4.6)將CR0的 PE 位置1;PE位==1,表明CPU運行在保護模式下;(這是準備切換到保護模式階段),CR0的數據結構如下:
- 4.7)跳轉到保護模式: jmp dword SelectorCode32:0 ,這里的代碼指提供了選擇子,(2.3)末部分,已經說明了為什么通過選擇子就可以索引到 32位代碼段 LABEL_SEG_CODE32;(這就是從實模式跳入保護模式)
(5)32位代碼段(由實模式跳入,即保護模式)的定義
- 5.1)將對應選擇子賦值到 對應寄存器, 即設置 任務代碼的 運行環境,不得不提的是 本段代碼還改變了ss和esp, 則在32位代碼段中所有的堆棧操作將會在新增的堆棧段中進行;
- 5.2)做任務,我們這里是打印一串字符串;
- 5.3)任務做完后,通過mov ax, SelectorLDT ; lldt ax ;加載LDT的選擇子到 局部描述符表寄存器LDTR; 并跳轉到局部任務去執行;
(6)LDT的定義(LDT描述符定義 + LDT選擇子定義 + 所需完成的任務代碼),其定義內容同GDT差不多;
- 6.1) LDT描述符定義,它這里只定義了一個描述符;
- 6.2) 定義相應的選擇子;
(7) 定義LDT中描述符指向的任務代碼段:該段中,當任務執行完成后,準備經由16位代碼段跳回實模式(先跳轉到16位代碼段,后跳轉到實模式);
- 補充:跳轉到16位代碼段,因為從保護模式跳回實模式,只能從16位代碼段中跳回;(這個,到底是因為什么,我也說不清楚)
(8)調回到實模式的16位代碼段(我們干脆叫它為過渡的16位代碼段, 與模塊(4)做個區別)
- 8.1)加載一個描述符選擇子,說是要實現從32位代碼段返回時cs 高速緩沖寄存器中的屬性符合實模式的要求,(具體的,我這里也沒有懂)
- 8.2)將CR0的 PE 位置0;PE位==0,表明CPU運行在實模式下;(這是準備切換到實模式階段),參見4.6所附圖片;
- 8.3)真正跳轉到實模式:LABEL_GO_BACK_TO_REAL: jmp 0:LABEL_REAL_ENTRY ; 這里的0 純粹是個幌子,我理解的是,它就是個占位符而已,因為在16位代碼段初始化的時候,已經將它的值設定為 跳轉前的 cs(段基址)值了,即 mov [LABEL_GO_BACK_TO_REAL+3], ax;
【9】跳回到16位代碼段(實模式)的定義
- 9.1)該段代碼定義在模塊(4)代碼區域的末尾,因為,它們都是實模式下的運行環境;
- 9.2)初始化寄存器的值,設置運行環境;
- 9.3)重新設置 堆棧指針sp 的值,這個值早在16位代碼段初始化的時候,已經將它的值 暫存在變量里了,這里只是將它恢復到原來的值而已,即 mov [SPValueInRealMode], sp;
- 9.4)關閉A20地址線, 為什么要關閉,參見:http://blog.csdn.net/pacosonswjtu/article/details/48005813
- 9.5)開中斷;
- 9.6)產生中斷信號,回到DOS;
總結
以上是生活随笔為你收集整理的局部描述符表LDT的作用+定义+初始化+跳转相关的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 万网邮箱收件服务器怎么设置(万网邮箱收件
- 下一篇: 调用门的定义+调用