http://www.haogongju.net/art/578426

《Orange’s 一個操作系統的實現》3.保護模式2----認識保護模式B

作者:Aoysme | 出處:博客園 | 2011/10/25 3:11:30 | 閱讀64

一、詳解GDT(轉載http://blog.csdn.net/zdwzzu2006/archive/2009/03/27/4030882.aspx)

在Protected Mode下,一個重要的必不可少的數據結構就是GDT(Global Descriptor Table)。
爲什麼要有GDT?我們首先考慮一下在Real Mode下的編程模型:
在Real Mode下,我們對一個內存地址的訪問是通過Segment:Offset的方式來進行的,其中Segment是一個段的Base Address,一個Segment的最大長度是64 KB,這是16-bit系統所能表示的最大長度。而Offset則是相對於此Segment Base Address的偏移量。Base Address+Offset就是一個內存絕對地址。由此,我們可以看出,一個段具備兩個因素:Base Address和Limit(段的最大長度),而對一個內存地址的訪問,則是需要指出:使用哪個段?以及相對於這個段Base Address的Offset,這個Offset應該小於此段的Limit。當然對於16-bit系統,Limit不要指定,默認爲最大長度64KB,而 16-bit的Offset也永遠不可能大於此Limit。我們在實際編程的時候,使用16-bit段寄存器CS(Code Segment),DS(Data Segment),SS(Stack Segment)來指定Segment,CPU將段積存器中的數值向左偏移4-bit,放到20-bit的地址線上就成爲20-bit的Base Address。
到了Protected Mode,內存的管理模式分爲兩種,段模式和頁模式,其中頁模式也是基於段模式的。也就是說,Protected Mode的內存管理模式事實上是:純段模式和段頁式。進一步說,段模式是必不可少的,而頁模式則是可選的——如果使用頁模式,則是段頁式;否則這是純段模式。
既然是這樣,我們就先不去考慮頁模式。對於段模式來講,訪問一個內存地址仍然使用Segment:Offset的方式,這是很自然的。由於 Protected Mode運行在32-bit系統上,那麼Segment的兩個因素:Base Address和Limit也都是32位的。IA-32允許將一個段的Base Address設爲32-bit所能表示的任何值(Limit則可以被設爲32-bit所能表示的,以2^12爲倍數的任何指),而不象Real Mode下,一個段的Base Address只能是16的倍數(因爲其低4-bit是通過左移運算得來的,只能爲0,從而達到使用16-bit段寄存器表示20-bit Base Address的目的),而一個段的Limit只能爲固定值64 KB。另外,Protected Mode,顧名思義,又爲段模式提供了保護機制,也就說一個段的描述符需要規定對自身的訪問權限(Access)。所以,在Protected Mode下,對一個段的描述則包括3方面因素:[Base Address, Limit, Access],它們加在一起被放在一個64-bit長的數據結構中,被稱爲段描述符。這種情況下,如果我們直接通過一個64-bit段描述符來引用一個段的時候,就必須使用一個64-bit長的段積存器裝入這個段描述符。但Intel爲了保持向後兼容,將段積存器仍然規定爲16-bit(儘管每個段積存器事實上有一個64-bit長的不可見部分,但對於程序員來說,段積存器就是16-bit的),那麼很明顯,我們無法通過16-bit長度的段積存器來直接引用64-bit的段描述符。
怎麼辦?解決方法就是把這些長度爲64-bit的段描述符放入一個數組中,而將段寄存器中的值作爲下標索引來間接引用(事實上,是將段寄存器中的高13 -bit的內容作爲索引)。這個全局的數組就是GDT。事實上,在GDT中存放的不僅僅是段描述符,還有其它描述符,它們都是64-bit長,我們隨後再討論。
GDT可以被放在內存的任何位置,那麼當程序員通過段寄存器來引用一個段描述符時,CPU必須知道GDT的入口,也就是基地址放在哪裏,所以 Intel的設計者門提供了一個寄存器GDTR用來存放GDT的入口地址,程序員將GDT設定在內存中某個位置之後,可以通過LGDT指令將GDT的入口地址裝入此積存器,從此以後,CPU就根據此積存器中的內容作爲GDT的入口來訪問GDT了。
GDT是Protected Mode所必須的數據結構,也是唯一的——不應該,也不可能有多個。另外,正象它的名字(Global Descriptor Table)所揭示的,它是全局可見的,對任何一個任務而言都是這樣。
除了GDT之外,IA-32還允許程序員構建與GDT類似的數據結構,它們被稱作LDT(Local Descriptor Table),但與GDT不同的是,LDT在系統中可以存在多個,並且從LDT的名字可以得知,LDT不是全局可見的,它們只對引用它們的任務可見,每個任務最多可以擁有一個LDT。另外,每一個LDT自身作爲一個段存在,它們的段描述符被放在GDT中。
IA-32爲LDT的入口地址也提供了一個寄存器LDTR,因爲在任何時刻只能有一個任務在運行,所以LDT寄存器全局也只需要有一個。如果一個任務擁有自身的LDT,那麼當它需要引用自身的 LDT時,它需要通過LLDT將其LDT的段描述符裝入此寄存器。LLDT指令與LGDT指令不同的時,LGDT指令的操作數是一個32-bit的內存地址,這個內存地址處存放的是一個32-bit GDT的入口地址,以及16-bit的GDT Limit。而LLDT指令的操作數是一個16-bit的選擇子,這個選擇子主要內容是:被裝入的LDT的段描述符在GDT中的索引值——這一點和剛纔所討論的通過段積存器引用段的模式是一樣的

二、07c00h(轉載http://blog.chinaunix.net/u/15262/showart_253979.html)

FFFF0h與07C00h,這兩個都是機器啓動後默認訪問的內存地址。曾經讓我一度很暈,搞不清他們之間的關係。現在終於搞明白了,寫到博客司上與大家分享

首先要知道bios是用來初始化硬件的最底層的軟件(然後纔是操作系統),因此計算機啓動後必須最先被執行。另外我們都知道CPU只能執行內存中的內容的,而一般內存中的數據是易失性的,斷電之後內容就會消失。工程師解決方法是:將存放bios的rom芯片與內存芯片統一編址(不明白的話去看看微機原理接口的書就明白了)。這樣就可以把存放bios的ROM芯片看作是數據永遠不會消失不允許被更改的內存了。

開機啓動後默認的CS=FFFFh  IP=0000h。這個地址就是bios的地址。這段內存空間很小,所以不能夠容下操作系統等大型程序。

相對bios而言操作系統的功能更強大,更新也更快但是也需要更多的空間,通常放在硬盤中。但是如果沒有bios的話,那麼將會連硬盤都不能使用,又如何啓動存放在硬盤中的操作系統呢?正是由於此,機器啓動後自動執行bios使其它完成硬件初始化(這樣包括硬盤在內的cpu以外的其他硬件設備就可以工作了)。bios完成硬件初始化的任務後,就要把權力移交給操作系統。

工程師進行了強制性的規定:到內存中的07c00h 處尋找系統的引導程序,即CS=0000h IP=7c00h。也就是說任何系統,他的引導程序都必須安排在07c00h開始的地方,否則就不能被正確的引導。當引導程序完成後我們就進入了 Linux Windows等系統了。

三、A20地址線

地址迴繞

       早期的8086只有20根地址線,只能訪問1M的地址空間。CPU尋址則按段+偏移的方式進行。16位段+16位偏移的可能的範圍是 0~0x10FFEF(即0xFFFF0+0xFFFF),即1M+65520字節的範圍。由於只有20根地址線,所以在對1M~1M+65520範圍進行訪問時,會發生“地址迴繞”的現象,就是說實際會訪問到0~65520的地方。據說某個著名的/臭名昭著的軟件利用了這個特點。在80286,386等 CPU上,它會失敗,因爲這些CPU有多於20根的地址線,並不產生“地址迴繞”現象。爲了保持完全的兼容性,IBM決定在PC AT系統上加個邏輯,來模仿以上的迴繞特徵。他們的方法就是把A20和鍵盤控制器的一個輸出進行AND,這樣來控制A20的打開和關閉。一開始時A20是被屏蔽的(總爲0),直到系統軟件去打開它。

A20地址線       1981年8月,IBM公司最初推出的個人計算機IBM PC使用的CPU是Intel 8088。在該微機中地址線只有20根(A0 – A19)。在當時內存RAM只有幾百KB或不到1MB時,20根地址線已足夠用來尋址這些內存。其所能尋址的最高地址是0xffff:0xffff,也即 0x10ffef。對於超出0x100000(1MB)的尋址地址將默認地環繞到0x0ffef。當IBM公司於1985年引入AT機時,使用的是 Intel 80286 CPU,具有24根地址線,最高可尋址16MB,並且有一個與8088完全兼容的實模式運行方式。然而,在尋址值超過1MB時它卻不能象8088那樣實現地址尋址的環繞。但是當時已經有一些程序是利用這種地址環繞機制進行工作的。爲了實現完全的兼容性,IBM公司發明了使用一個開關來開啓或禁止 0x100000地址比特位。由於在當時的8042鍵盤控制器上恰好有空閒的端口引腳(輸出端口P2,引腳P21),於是便使用了該引腳來作爲與門控制這個地址比特位。該信號即被稱爲A20。如果它爲零,則比特20及以上地址都被清除。從而實現了兼容性。        由於在機器啓動時,默認條件下,A20地址線是禁止的,所以操作系統必須使用適當的方法來開啓它。但是由於各種兼容機所使用的芯片集不同,要做到這一點卻是非常的麻煩。因此通常要在幾種控制方法中選擇。

        對A20信號線進行控制的常用方法是通過設置鍵盤控制器的端口值。 有些操作系統將A20的開啓和禁止作爲實模式與保護運行模式之間進行轉換的標準過程中的一部分。       由於鍵盤的控制器速度很慢,因此就不能使用鍵盤控制器對A20線來進行操作。爲此引進了一個A20快速門選項(Fast Gate A20),它使用I/O端口0x92來處理A20信號線,避免了使用慢速的鍵盤控制器操作方式。對於不含鍵盤控制器的系統就只能使用0x92端口來控制,但是該端口也有可能被其它兼容微機上的設備(如顯示芯片)所使用,從而造成系統錯誤的操作。         還有一種方式是通過讀0xee端口來開啓A20信號線,寫該端口則會禁止A20信號線。

四、GDT、選擇子等圖例

; 高地址………………………………………………………………………低地址

; |   7   |   6   |   5   |   4   |   3   |   2   |   1   |   0    |
; |7654321076543210765432107654321076543210765432107654321076543210|    <- 共 8 字節
; |--------========--------========--------========--------========|
; ┏━━━┳━━━━━━━┳━━━━━━━━━━━┳━━━━━━━┓
; ┃31..24┃   (見下圖)   ┃     段基址(23..0)    ┃ 段界限(15..0)┃
; ┃      ┃              ┃                      ┃              ┃
; ┃ 基址2┃③│②│    ①┃基址1b│   基址1a     ┃    段界限1   ┃
; ┣━━━╋━━━┳━━━╋━━━━━━━━━━━╋━━━━━━━┫
; ┃   %6 ┃  %5  ┃  %4  ┃  %3  ┃     %2       ┃       %1     ┃
; ┗━━━┻━━━┻━━━┻━━━┻━━━━━━━┻━━━━━━━┛
;         │                \_________
;         │                          \__________________
;         │                                             \________________________________________________
;         │                                                                                              \
;         ┏━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┓
;         ┃ 7  ┃ 6  ┃ 5  ┃ 4  ┃ 3  ┃ 2  ┃ 1  ┃ 0  ┃ 7  ┃ 6  ┃ 5  ┃ 4  ┃ 3  ┃ 2  ┃ 1  ┃ 0  ┃
;         ┣━━╋━━╋━━╋━━╋━━┻━━┻━━┻━━╋━━╋━━┻━━╋━━╋━━┻━━┻━━┻━━┫
;         ┃ G  ┃ D  ┃ 0  ┃ AVL┃   段界限 2 (19..16)  ┃  P ┃   DPL    ┃ S  ┃       TYPE           ┃
;         ┣━━┻━━┻━━┻━━╋━━━━━━━━━━━╋━━┻━━━━━┻━━┻━━━━━━━━━━━┫
;         ┃      ③: 屬性 2      ┃    ②: 段界限 2      ┃                   ①: 屬性1                  ┃
;         ┗━━━━━━━━━━━┻━━━━━━━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━┛
;       高地址                                                                                          低地址
;
;

選擇子

image

cr0

image

段式尋址示意圖

image

gdt示意圖

image

五、描述符屬性

;         │                \_________
;         │                          \__________________
;         │                                             \________________________________________________
;         │                                                                                              \
;         ┏━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┓
;         ┃ 7  ┃ 6  ┃ 5  ┃ 4  ┃ 3  ┃ 2  ┃ 1  ┃ 0  ┃ 7  ┃ 6  ┃ 5  ┃ 4  ┃ 3  ┃ 2  ┃ 1  ┃ 0  ┃
;         ┣━━╋━━╋━━╋━━╋━━┻━━┻━━┻━━╋━━╋━━┻━━╋━━╋━━┻━━┻━━┻━━┫
;         ┃ G  ┃ D  ┃ 0  ┃ AVL┃   段界限 2 (19..16)  ┃  P ┃   DPL    ┃ S  ┃       TYPE           ┃
;         ┣━━┻━━┻━━┻━━╋━━━━━━━━━━━╋━━┻━━━━━┻━━┻━━━━━━━━━━━┫
;         ┃      ③: 屬性 2      ┃    ②: 段界限 2      ┃                   ①: 屬性1                  ┃
;         ┗━━━━━━━━━━━┻━━━━━━━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━┛
;       高地址                                                                                          低地址
1.P位:存在位.P=1表示段在內存中存在;P=0表示段在內存中不存在

2.DPL 描述符特權級。可以是0,1,2,3.數字越小特權級越大

3.S位 指明描述符是數據段/代碼段描述符(S=1) 還是系統段/門描述符(S=0)

4.TYPE描述符類型

TYPE值

數據段和代碼段描述符

系統段和門描述符

0 只讀 <未定義>
1 只讀,已訪問 可用286TSS
2 讀/寫 LDT
3 讀/寫,已訪問 忙的286TSS
4 只讀,向下擴展 286調用門
5 只讀,向下擴展,已訪問 任務門
6 讀/寫,向下擴展 286中斷門
7 讀/寫,向下擴展,已訪問 286陷阱
8 只執行 <未定義>
9 只執行,已訪問 可用386TSS
A 執行/讀 <未定義>
B 執行/讀,已訪問 忙的386TSS
C 只執行,一致代碼 386調用門
D 只執行,一致代碼,已訪問 <未定義>
E 執行/讀,一致代碼 386中斷門
F 執行/讀,一致代碼,已訪問 386陷阱門

 

5.G位 段界限粒度位。G=0爲字節 G=1時爲4KB

6.D/B

在描述可執行段的描述符中,D位決定了指令使用的地址及操作數所默認的大小。D=1表示默認情況下指令使用32位地址及32位或8位操作數,這樣的代碼段也稱爲32位代碼段;D=0 表示默認情況下,使用16位地址及16位或8位操作數,這樣的代碼段也稱爲16位代碼段,它與80286兼容。可以使用地址大小前綴和操作數大小前綴分別改變默認的地址或操作數的大小。

在向下擴展數據段的描述符中,D位決定段的上部邊界。D=1表示段的上部界限爲4G;D=0表示段的上部界限爲64K,這是爲了與80286兼容。

在描述由SS寄存器尋址的段描述符中,D位決定隱式的堆棧訪問指令(如PUSH和POP指令)使用何種堆棧指針寄存器。D=1表示使用32位堆棧指針寄存器ESP;D=0表示使用16位堆棧指針寄存器SP,這與80286兼容。

7.AVL位 保留位,可以被系統軟件使用


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