標籤

2008年8月28日 星期四

BIg real mode

http://www.programmer-club.com/pc2020v5/forum/showSameTitleN.asp?board_pc2020=assembly&id=5188&keyword=

我查了討論區有關Big Mode/Flat Mode/Protected Mode/Big Real Mode/Flat Memort Model 以及相關的Intel 說明,但是對於這個Mode 還是不太懂, 我看到的資料都是說 protect mode 跟 Big Mode (我統一這個說法以免混淆)不一樣,但是不一樣的地方在哪裡?先說說我目前了解的地方:

80386 開始的保護模式PM提出了虛擬記憶體VM的概念(80286就有,只是386開始落實),VM最主要的概念是讓OS以為有很多用不完的記憶體,為了達成這個理想提出了利用分段(Segment)及分頁的方式,原本真實模式RM下是利用段暫存器*10h+偏移位址方式來表示出記憶體位址,而這個方式可以看作是 base address + limit 的方式,簡單說就是原本方式 F000:FFFF=F000*10h + FFFF = FFFFF 可以看作是Base address=F0000 他的區段大小的限制在FFFF(64k),所以F0000~FFFFF是他的區段。

除了這個觀念外另一個觀念就是RM的時候因為暫存器16 bit 所以沒辦法定址到全部的位址因此才使用上述的這種方式,但是到了386年代暫存器已經32 bit,所以一般暫存器就可以存取4G的位址而不需要用到"段暫存器"。 因為可以存取到4G而不需要段暫存器,所以在80386的保護模式的虛擬記憶體的分段概念中段暫存器就不是拿來當作存放Base Address用途,而是拿來當段描述的選擇器Segment Selector(或稱之為Index也可以看作是Offset)。

另外就是保護模式要保護什麼?他就是要保護程式彼此之間不會去存取到對方的程式段或是資料段以免發生問題,因此在386 PM模式裡面他去描述了像是一些存取權限/優先權(Ring 0~3)...等的東西,因為要描述這些東西所以他使用了64 bit的資料結構去描述 base address+Limit+優先權+存取權限,簡單說就是每一筆的描述都是64 bit(也有人說8 Bytes)大家稱這個描述叫做段描述符(Segment Descriptor),因為有很多筆所以程式設計師必須在實際的記憶體區塊去分配一個陣列放這些描述的資料,而這個陣列是大家都看得到的(PUBLIC),所以稱之為GDT(Gobal Descriptor Table),而前面說的段暫存器就是用來選這個陣列裡面的那一筆資料,那另外一個問題出現了,程式設計師分配了一會記憶體區塊來描述這些段的資訊那這個Table到底在哪裡? 因此386加入了一個暫存器叫做GDTR指向GDT的入口(GDT的Base Address),你可以利用LGDT指令把GDTR指向GDT... 因為保護所以每個任務都會有屬於自己的記憶體區段,所以每個任務也會有各自的段描述,稱之為LDT(Local Descriptor Table),LDT也是陣列,裡面也有段描述...為了統一處理,所以GDT裡面除了段描述SD0/SD1...SD N 之外還有描述LDT 0/LDT1...LDT N在哪個記憶體區段的資料(這邊要特別強調,GDT分配到一塊記憶體的區段,LDT也是,但是放在GDT裡面的LDT 0只是用來指向LDT是放那一塊記憶體的描述),那他們也設計了一個暫存器叫做LDTR來當作Index(選GDT陣列中的LDT描述),那至於你要選哪一個Table就是看段暫存器的設定,段暫存器 bit 2 =0 就是選GDT,=1就是LDT, bit 1:0=RPL ,其他的bit15:3才是當作Index。

GDT (假設放在00028000)
SD0
SD1
SD2
LDT0 (假設放在0003A000)
LDT0 ------------------------------> SD0
LDT1 SD1
LDT2 SD2

總結 :
1.RM 因為暫存器只有16 bit 所以使用Segment*10h+Offset方式,但是這個方式可看作Base Address+Limit概念

2.PM 模式提出VM概念:利用Segment 跟 Paging,他有3種定址方式: 邏輯位址(Logical Address)、線性位址(Linear Address)、和實體位址(Physical Address)。
a.所謂的「實體位址」,就是指系統的記憶體的真正位址,它的範圍由 00000000H 到 FFFFFFFFH,共有 4GB。
b.在沒有使用分頁(Paging)功能的時候,線性位址是直接對映到實體位址的,也就是說,線性位址就等同於實體位址。不過,在開啟分頁分能之後,一個線性位址可能沒有相對映的實體位址(因為它所對映的記憶體可能被 swap 到硬碟裡了)。
c.邏輯位址則和實際模式類似,仍然是 segment:offset 的形式,只不過現在 offset 的大小改成 32 bit 而已,而 segment 仍然是 16 bit。另外就是段暫存器當作是Segment Selector。

3.段暫存器用來當Index 是bit 15:3 所以是2^13 ,給ㄧ個段描述最大可以4G,所以看作是有2^13 * 4G這樣多的記憶體可以用

4.分段方式可以分成兩種:
a. Flat segmentation model : 所有區段對應整個32 bit實體空間,最少要兩個區段,1個程式段,1個資料段,描述在GDT,當Base=0 Limit=4G時可以存取整個實體位址
b.Multi-Segmentation Model:每個程式有自己的LDT,LDT由Segment Selector組成

5.澄清專有名詞: Big Mode / Flat Mode : BIOS 用來存取4G方式 (旗標BIOS Inside pp.3-17)

Flat Memory Model :應該指的是Flat segmentation model (Paper Author: Steve Gorman Title: Programming with the Intel architecture in the flat memory model)

Virtual Protected Mode/ Protected Mode : 虛擬記憶體概念的保護模式

Flat segmentation model : 分段方式的其中一種 (Assembly Language for Intel-Based Computer 4th edition,KIP R.IRVINE,全華科技)

問題:
1.一般我們看到的 xxxx:zzzzzzzzh 表示方式是指Segment Selecotr: Offset 嗎?

2.DOS底下也可以用平坦區段模式(Flat Segmentation Mode)存取4G,那他跟Big Mode有什麼不同?

3.PM模式中的分段方式裡面的平坦區段模式就可以存取4G實體位址,那麼跟Big Mode有什麼不同

4.進入保護模式PM方式是設定GDT/LDT,設定CR0,而Big Mode也是一模一樣的方式,他們有什麼不一樣?

5.Big Mode為什麼設定好Flat Segmentation Mode後要從PM切換回去RM,並且開啟A20 ?

6.RM底下開啟A20不是只能存取1M + 64k - 16 嗎? 為什麼他切進去PM設定好之後切換回來就可以存取4G ?

7.當Big Mode切換回RM之後定址方式是 Segment:Offset 還是 0: Offset 還是 Segment Selector:Offset(這個應該不可能因為RM底下沒有GDT)???

A:
在 Real Mode 下,改變 Selector 只會更新 Base Address。唯有在 Far Jump 時,CS 的 Limit 會被重載入!至於 A20 ,是由 Chipset 或 KBC 來控制 CPU 的 A20 Mask,所以無關 PM 或 RM。如果 A20 未開,就如同 CPU 的 A20 address line 不會動作一樣,那麼你可以想像會有什麼結果了!不過現在 AMD K8 似乎已沒有 A20M pin,這個古老的東西存在的目的只在相容性而已,說不定未來 Intel 也會拿掉這個東西!(286 我並不了解,也不知是否有特殊限制)
Hidden=Segment Base Address+Segment Limit+ Attri+...
分成固定與非固定兩部份: Segment Base Address是非固定,其他為固定

Real Mode底下
MOV AX,F000
MOV DS,AX ; 此時非固定部分會更新成,目前DS*10

PM Mode底下
MOV BX,Selector
MOV DS,BX ;此時固定與非固定部分都會更新

為什麼要做Short JMP?
因為 CPU 不只一條指令管線(instruction pipeline), 所以會預先提取指令(instruction fetching)並解碼(decoding), 但同樣的指令在 Protected Mode 和 Real Mode 中的解碼結果是不同的, 所以要將指令管線中在 Real Mode 解碼的部分清掉, 怎麼清呢? 執行會產生"程序控制權轉移"的指令即可, 如 jmp,jxx,call,ret,int,iret 等指令.

每個段都有自己的隱含暫存器,CPU定址是利用隱含暫存器的Base Addr + Offset 而不是Seg*10h+Offset (80286以後就不是這樣定址了) 在80386裡面隱含暫存器是64 bit,它分成幾部份:段暫存器Base Addr+ 段暫存器Limit+段暫存器屬性

有人說A20打開就可以存取0~4G了,這是對的嗎? 這個問題要考慮CPU定址方式跟位址線。當A20=0,就算你能定址0~4G,你也只能存取偶數位址。當A20=1 ,而CPU的隱含暫存器如果沒有設定成【Base Addr】=00000000,【Limit】跟【段暫存器屬性】改變成FFFFF * 4K=4GB你也沒辦法定址0~4G,所以答案告訴我們都是CPU在決定一切,A20=1只是必要的一個過程而不是打開就可以存取0~4G。

而這個答案也告訴我們只要你有辦法設定【隱含暫存器】不管那個CPU模式你都能去存取0~4G。真實模式下沒辦法設定【隱含暫存器】,唯有進入保護模式才能設定Big Real Mode 這個模式指的就是改變CPU狀態讓我們可以在真實模式底下存取0~4G的記憶體位址。

這方式步驟就是在真實模式設定好GDT,然後進入保護模式中設定好【隱含暫存器】,接著切換回去真實模式並且開啟A20。

1.找ㄧ塊記憶體區塊放置GDT Table(我們要改變的段描述也在那裡面,設定Limit=4G)
2.載入段描述表(用LGDT指令會將GDT位址放入, GDTR暫存器)
3.設定控制暫存器CR0讓CPU進入保護模式
4.做一個Short JMP,清空CPU Prefetch,因為 CPU 不只一條指令管線(instruction pipeline) , 所以會預先提取指令(instruction fetching)並解碼(decoding) ,但同樣的指令在 Protected Mode 和 Real Mode 中的解碼結果是不同的,所以要將指令管線中在 Real Mode 解碼的部分清掉,怎麼清呢? 執行會產生"程序控制權轉移"的指令即可, 如 jmp,jxx,call,ret,int,iret 等指令。
5.執行CLI指令關閉所有中斷請求以免錯誤(保護模式內段暫存器是Selector,如呼叫中斷會發生錯誤,有解決方式但超過討論範圍)
6.載入索引給你將來會使用到的段暫存器,讓CPU能夠自己去將段描述載入【隱含暫存器】
7.設定CR0,關閉保護模式,回保護模式(因為目的已經達到)
8.做一個FAR JMP ,清空CPU Prefetch ,並且給定正確的CS:IP
9.真實模式下開啟A20 位址線。

;;In Real Mode:;設定一些相關設定
LGDT ;;把GDT 放到LDTR
EnablePMMode ;;改變CR0進入保護模式
JMP Flush1 ;;丟棄已經存於CPU預先存取佇列中的指令
Flush1: ;; 這裡以經是保護模式MOV DS,BX ;;
裝Selector,給你要用的段暫存器DS/ES/FS…等
........
........
DisablePMMode ;; 改變CR0關閉保護模式
DB 0EAH ;;Far JMP回Real Mode,
DW OFFSET Flush2 ;;你程式碼的段內偏移位址
DW F000H ; ;你程式碼的段位址

Flush2: ;; 這裡以經是真實模式

XOR AX,AX
MOV DS,AX ;;DS=0,以後就可以用DS:OFFSET方式存取4G
.......

EnableA20 ;;開啟A20, by Harrison 2006.09.27


====================================================================
我想請問一下你所謂的"RESET CPU"是指HW RESET嗎? 因為我最近在看BOOTBLOCK跟POST 的Code,理論上CPU Reset 一定是去FFFF_FFF0讀第一行指令,然後看讀取的第一條指令要做什麼,但是我最近發現第一條指令是Short JMP 到BootBlock,而不是Far JMP(Far JMP才會跳到1M的地方,因為會改變CS跟IP,造成隱含暫存器的Base Addr=F0000h),所以我覺得很奇怪,感覺BIOS從開機後執行都是在A20 Enable且存取的都是4G那邊的位址,一直到了他去初始化一些HW後才回歸到1M那邊,另外A20 Disable 跟Warm Boot真的有影響嗎?
據我所知,如果你是Phoenix BIOS,你去看第一條指令,他是跳到BoolBlock執行,你可以用反組譯工具或是用Debug工具去FFFF_FFF0看,他是E9 XX XX, 'E9'是Short Jmp

FFFF_FFF0 CPU RESET來這邊讀,如果有裝BootBlock,他會做short jmp 'E9' to bookblock(真正放BootBlock程式碼的地方)

FFFE_XXXX 假設這邊是實際上 BootBlock 進入點的地方,這邊會去判斷一些事情
如果F000:FFF0 是E9,代表沒有Shadow 過
如果F000:FFF0 是EA,代表已經Shadow 過,也就可能是Warm Boot

F000:FFF0 CPU RESET一定不會讀這邊,不管是Warm Boot還是Cold Boot(因為CPU RESET一定是去FFFF_FFF0,去4G而不是1M,雖然都是同一個地方同資料,但是對CPU來說不一樣),如果已經從FFFF_FFF0 JMP到某個地方去開始執行BootBlock後,再來讀取這邊的資料的話,你會讀到跟FFFF_FFF0一樣的資料,也就是E9 xx xx,另外當POST過程中如果Shadow 相容性的資料後,這邊的資料會變成 EA F000:E05B,簡單說就是:
Shadow 前: F000:FFF0讀到的資料跟FFFF_FFF0一樣, E9 XX XX
Shadow 後: F000:FFF0讀到的資料會變成EA F000:E05B

F000:E05B BIOS Entry Point (BIOS POST Test),這邊決定的是Cold Boot或是Warm Boot(註:以BIOS觀點,因為HW的Cold/Warm Boot跟BIOS觀點不同)
P.S CPU RESET後的狀態

CS:F000 <--注意,80286之後不用CS定址
EIP:0000FFF0 <--16 bit只能看到FFF0,因為Segment Limit=64k
隱含暫存器的Base Addr=FFFF0000 <--真正被拿來定址的暫存器
隱含暫存器的Segment Limit=64k

所以第一條指令定址的方式是 Base Addr+IP=FFFF000+FFF0=FFFFFFF0 <--4G的地方

如果第一條指令是FAR JMP,則會改變CS跟IP(也就是隱含暫存器內的Base Addr會重新計算,計算方式是 Base Addr=目前CS*10h=F0000h, IP=跳躍的位址, 所以會定址方式 Base Addr+IP也就是F0000+IP,可以看做F000:IP <--1M的地方)

如果第一條指令是Short JMP,理論上Base Addr不會改變(因為Short JMP只會改變IP不會改變CS,因此Base Addr不會重新計算),所以是FFFF0000+IP <--保持這個狀態直到做了FAR JMP/FAR CALL才會改變,也就是我們說的跳到1M的地方.

因此有人說CPU讀取的第一條指令一定是FAR JMP,做了FAR JMP後就會跳到1M的地方執行,這種說法沒錯,但是這種情況是沒有BootBlock下才這樣。

忘記說明一下最近K完SPEC後發現底下的表示法的不同,這是南北橋SPEC裡面的表示法:FFFF_FFF0 <--代表4G那邊的位址,以前我一直以為FFFF:FFF0 ,我很豬頭吧! 呵呵!

跟FFFFFFF0 表示方式一樣

F000:FFF0 <--代表F000*10h + FFF0

我自己說明一下免得大家搞糊塗我所表示的地方...^^

沒有留言: