《Orange's 一個操作系統的實現》讀書手記3(1)--- [ 保護模式(Protect Mode)]

 

 

保護模式(Protect Mode) (1)

 

  到底什麼是保護模式?誰又在保護誰?是通過什麼方式達到保護目的的? 爲什麼要保護呢?帶着這些疑問和幾分好奇,我們開始了第三章的學習,開始隨着作者一步一步地體會“保護”二字的內在含義。那個連胚胎都還不是的“操作系統” 究竟能否正常發育,這一章的“保護”很關鍵!

3.1 認識保護模式

在好奇心的驅使下,人總是很勤勞的,作者如是說。

接着作者馬上就給我們潑來了一頭霧水:

;==========================================

; pmtest1.asm

; 編譯方法:nasm pmtest1.asm -o pmtest1.bin

;==========================================

 

%include  "pm.inc"    ; 常量, , 以及一些說明

 

org   07c00h

jmp LABEL_BEGIN

 

[SECTION .gdt]

; GDT

;                              段基址,       段界限     , 屬性

LABEL_GDT:         Descriptor        0,                0, 0             ; 空描述符

LABEL_DESC_CODE32: Descriptor        0, SegCode32Len - 1, DA_C + DA_32  ; 非一致代碼段

LABEL_DESC_VIDEO:  Descriptor 0B8000h,           0ffffh, DA_DRW        ; 顯存首地址

; GDT 結束

 

GdtLen        equ $ - LABEL_GDT   ; GDT 長度

GdtPtr        dw  GdtLen - 1          ; GDT 界限

dd  0                   ; GDT 基地址

 

; GDT 選擇子

SelectorCode32        equ LABEL_DESC_CODE32   - LABEL_GDT

SelectorVideo         equ LABEL_DESC_VIDEO    - LABEL_GDT

; END of [SECTION .gdt]

 

[SECTION .s16]

[BITS 16]

L ABEL_BEGIN:

mov ax, cs

mov ds, ax

mov es, ax

mov ss, ax

mov sp, 0100h

 

; 初始化 32 位代碼段描述符

xor eax, eax

mov ax, cs

shl eax, 4

add eax, LABEL_SEG_CODE32

mov word [LABEL_DESC_CODE32 + 2], ax

shr eax, 16

mov byte [LABEL_DESC_CODE32 + 4], al

mov byte [LABEL_DESC_CODE32 + 7], ah

 

; 爲加載 GDTR 作準備

xor eax, eax

mov ax, ds

shl eax, 4

add eax, LABEL_GDT      ; eax <- gdt 基地址

mov dword [GdtPtr + 2], eax ; [GdtPtr + 2] <- gdt 基地址

 

; 加載 GDTR

lgdt    [GdtPtr]

 

; 關中斷

cli

 

; 打開地址線A20

in  al, 92h

or  al, 00000010b

out 92h, al

 

; 準備切換到保護模式

mov eax, cr0

or  eax, 1

mov cr0, eax

 

; 真正進入保護模式

jmp dword SelectorCode32:0      ; 執行這一句會把 SelectorCode32 裝入 cs,

; 並跳轉到 Code32Selector:0 

; END of [SECTION .s16]

 

 

[SECTION .s32]; 32 位代碼段. 由實模式跳入.

[BITS 32]

 

LABEL_SEG_CODE32:

mov ax, SelectorVideo

mov gs, ax              ; 視頻段選擇子( 目的)

 

mov edi, (80 * 11 + 79) * 2 ; 屏幕第 11 , 79 列。

mov ah, 0Ch             ; 0000: 黑底    1100: 紅字

mov al, 'P'

mov [gs:edi], ax

 

; 到此停止

jmp $

 

SegCode32Len  equ $ - LABEL_SEG_CODE32

; END of [SECTION .s32]

---------------------------------------%include "pm.inc"-----------------------------------------------

;

; 描述符

; usage: Descriptor Base, Limit, Attr

;        Base:  dd

;        Limit: dd (low 20 bits available)

;        Attr:  dw (lower 4 bits of higher byte are always 0)

%macro Descriptor 3

dw  %2 & 0FFFFh                 ; 段界限1

dw  %1 & 0FFFFh                 ; 段基址1

db  (%1 >> 16) & 0FFh           ; 段基址2

dw  ((%2 >> 8) & 0F00h) | (%3 & 0F0FFh) ; 屬性1 + 段界限2 + 屬性2

db  (%1 >> 24) & 0FFh           ; 段基址3

%endmacro ; 8 字節

 

  如果你是第一次讀到這裏,不知道你對這段代碼有怎麼的感覺,可能對開頭的耐心到第二頁就消失的差不多了,然後很快的掃視,很短時間之後,你已經到達了代碼末尾。

  知我者作者也!我確確實實就是這樣的感受。我只記住了一句話:這段代碼實現了從實模式到保護模式的轉換!

  我們看到,整個程序有很多個類似[SECTION .xxx] 的東西,這個是用來把程序大卸八塊的;我們還看到,[SECTION .gdt] 後面有3 個帶 Descriptor 字樣的行。

  先來看看這個Descriptor 是什麼東東,看字面意思,我們知道它好像描述了一些什麼東西……

  是的,它是一個宏,而且還帶了3 個參數,不過它代表的不是一段可執行的代碼,而是一個數據結構!我們暫且把它看成是結構體好了,彙編器處理的時候會直接把這個結構體替換進來。我們知道,一個dw 兩個字節,一個db 一個字節,總共有3 dw 2 db ,所以這個結構體總共有3*2+2*1 = 8 個字節,跟註釋說的是一致的。

  其中,%1 表示第一個參數,%2 代表第二個參數,%3 代表第三個參數。我們又看到,這些個參數被支離破碎地填充到了Descriptor 8 個字節裏面。

  怎麼搞這麼麻煩啊! ?你問我,呵呵,我也不知道,反正這個叫GDT 的結構體有點變態 是不是,更可惡的是還不止一個。

  相比GDT 這個變態的傢伙,緊挨着它屁股後面的這個數據結構GdtPtr 就要單純多了。

GdtPtr      dw  GdtLen - 1          ; GDT 界限

              dd  0                   ; GDT 基地址

  還真是GDT 的跟屁蟲!你看到了的:裏面的東西全都是跟GDT 有關的。 2 個字節描述了GDT 的身高--- 界限;後4 個字節想要透露出GDT 的藏身地--- 基地址。( 其實剛開始GdtPtr 自己也不知道,所以是0)

  然後是兩行Selector 打頭的常量 (equ 定義出來是常量哦,像上面的dw dd 定義的纔是變量) ,這裏好像是選擇了GDT 中的某個符號到某個符號差值,一下子搞不明白了,我選擇暫時離開。

  再往下走吧,在[SECTION .s16] 後面有個[BITS 16] ,下面應該就是一些16 位的代碼了吧?這些代碼做了什麼事?

  它把ds (數據段)、es( 擴展段) ss( 堆棧段) 全搞成跟cs( 代碼段) 一樣值了( 這個值就由org 指定的0x7c00) ,還把sp( 堆棧指針) 指向0x100h (256) 處。然後eax 自身異或清零,代碼段指針被傳進去,左移4 位後與LABEL_SEG_CODE32 相加。 

  LABEL_SEG_CODE32 在哪裏?

  LABEL_SEG_CODE32 [SECTION .s32] [BITS 32] 後面,是一段32 位代碼段的頭標籤。根據NASM 的規則我們知道,任何不帶[] 標籤或變量是代表一個地址,所以LABEL_SEG_CODE32 就是那段32 位代碼段的偏移地址。Cs 16 位的,左移4 位再加上一個偏移地址不就是一個20 位的物理地址嗎!

  這個20 位的物理地址又被分成了3 部分,被填入變態的GDT LABEL_DESC_CODE32 的第2 4 7 字節處,果真是初始化了一個GDT

  eax 又改過自新了一次,與上面類似地,通過ds 竟然把變態的GDT 老大的地址弄到了,而且還把它存到了跟屁蟲GdtPtr 的第2 字節開始處. 。這下GdtPtr 什麼都沒做就有了那些GDT 的藏身基地,真是踏破鐵鞋無覓處,得來全不費工夫。

  Lgdt 是個探子----- 一旦GdtPrt 有了GDT 們的基地消息,就向GDTR 報告。這樣一來,GDTR 就可以順滕摸瓜把GDT 們一個一個給揪出來了。究竟GDTR 是如何找到GDT 們的,下一節會有分解。

  再看,in/out 做了一些端口操作,打通了地址線A20 ,然後又對控制器寄存器cr0 的第0 位做了些手腳,此時此刻,我們終於走到了保護模式的大門口!

  鯉魚跳龍門,華麗轉身。

  jmp 之後它就開始進入保護模式的天地了,在這片天地裏,它向人們展現了一個紅色的’P ,最終死在了jmp $ 上。

  百說不如一做,讓我們來見證一下這段輝煌。

  第二章已經搭建好的bochs nasm 可以派上用場了:

  nasm pmtest1.asm -o pmtest1.bin

  dd   if=pmtest1.bin of=a.img bs=512 count=1 conv=notrunc

  bochs

 

 

注意:a.img 一定要用上次做好的那個,爲什麼?想一想。



現在,我們已經隱隱約約知道到了:

  •   程序定義了一個叫做GDT 的數據結構。
  • 後面的16 位代碼進行了一些與GDT 有關的操作。
  • 程序最後跳到32 位代碼做了一點操作顯存的工作。

 

  下一節,讓我們跟保護模式走得更近一些。

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章