┌┐┌┐∞

      【 80386保護模式簡介 】                    ┘└┘└┘



--------------------------------------------------------------------------

    在保護模式下有很多新的名詞 ,包含 GDT.LDT.IDT 以及 CR0-CR3 ,筆者對保護

模式並不清楚 ,所以底下資料可能有錯誤。這裡使用大量的線性記憶體觀念 ,請您

一定要從頭往後看 ,否則很可能會看不懂 ,且必須懂線性記憶體計算方式。



--------------------------------------------------------------------------

【 GDT 介紹 】

    在真實模式下每個區段都等於64K ,可是保護模式下每個區段的大小卻是可變動

的 ,每個區段有多大呢 ,就是由 GDT 來決定。





    您可以用 SGDT CS:[BX] 的方式將 GDT 的值讀出 ,它的長度為 6 BYTE ,底下

    是筆者寫的小程式讀出。



    XXXX:0000  FF 0F 00 20 C0 00

                     ^^^^^^^^^^^GDT表所在的線性記憶體位址

               ^^^^^GDT表長度+1



將此表資料讀出來.

X:00C02000 00 00 00 00 00 00 00 00-FF FF 00 A0 C2 9B 40 00 ........... B.@.

X:00C02010 FF FF B0 DD 01 93 40 00-FF FF E0 B3 00 9A 00 00 ..0]..@...`3....

X:00C02020 FF FF E0 B3 00 93 00 00-00 00 00 20 C1 82 80 00 ..`3....... A...

X:00C02030 00 00 00 20 C1 93 C0 00-00 00 00 20 C0 93 C0 00 ... A.@.... @.@.

X:00C02040 00 00 00 00 00 92 40 00-FF FF 00 80 0B 92 40 00 ......@.......@.

                                .

                                .

                                .

                                .

它所代表的意思是如下圖所示:(每組 8 byte)



        ┌──────────────────────┐

       1│                Limit bit 0-15              │ 0 byte

        ├──────────────────────┤

       3│                Base bit 0-15               │ 2

        ├──────────┬───────────┤

       5│       存取權       │    Base bit 16-23    │ 4

        ├──────────┼───────────┤

       7│   Base bit 24-31   │G│..│limit bit 16-19│ 6

        └──────────┴───────────┘

            "G"代表 Limit 的單位是 Byte 或 PAGE(4K)



所以....



#0000  Segment not present.

#0008  Base=00C2A000  Limit=0000FFFF  Flags=9B  USE32  Byte granularity

#0010  Base=0001DDB0  Limit=0000FFFF  Flags=93  USE32  Byte granularity

#0018  Base=0000B3E0  Limit=0000FFFF  Flags=9A  USE16  Byte granularity

#0020  Base=0000B3E0  Limit=0000FFFF  Flags=93  USE16  Byte granularity

#0028  Base=00C12000  Limit=00000000  Flags=82         Page granularity

#0030  Base=00C12000  Limit=00000000  Flags=93  USE32  Page granularity

#0038  Base=00C02000  Limit=00000000  Flags=93  USE32  Page granularity

#0040  Base=00000000  Limit=00000000  Flags=92  USE32  Byte granularity

#0048  Base=000B8000  Limit=0000FFFF  Flags=92  USE32  Byte granularity

#0050  Base=0001F56C  Limit=000007FF  Flags=92  USE32  Byte granularity

#0058  Base=00000000  Limit=00000144  Flags=92  USE32  Page granularity

#0060  Base=00000000  Limit=00000144  Flags=93  USE32  Page granularity

#0068  Base=00127F48  Limit=0000C32F  Flags=9B  USE16  Byte granularity

#0070  Base=00134278  Limit=000028F7  Flags=93  USE16  Byte granularity

#0078  Base=00000000  Limit=00000000  Flags=92  USE16  Byte granularity

^^^^^Selector                              ^^存取權



Base 就是指這個Secector:00000000對應到線性記憶體的何處 ,也就是說將線性記

憶體從 Base 所指的地方開始長度為 Limit ,剪下來變成一個獨立的區段 ,如果您

在該區段想看超過 LIMIT 長度的記憶體 ,則會發生保護模式錯誤...應用程式可攔

截所發生的中斷適當的加以處理。

注意 ,Limit的單位可以是 byte ,也可以是page(4k) ,由 "G" 是否為 1 來決定



至於 Selector 的數值我猜想應該是被標上 8 的倍數吧 ,因為很多書都是如此介

紹它。



--------------------------------------------------------------------------

【 LDT 介紹 】

    上面介紹了 GDT 可以設定很多個Secector ,而 LDT 則是在這些被定義出來

的Selector中再切割出更小的單元。也就是說 LDT 的資料長度只有 2 BYTE ,這

個值直接就是指 Selector。



※這個命令必需在最高權力下才能執行 ,所以筆者使用 386DEBUG 來執行 ,在傳

  統 Real Mode/V86 都不能執行。



C:\>386debug 386debug.exp   (改過的.exp檔)

000C:0002743C 660F0007                 SLDT     [EDI]

-T

-D EDI

0014:00000000  28 00                    <-- LDT 所指的Selector為0028

根據 GDT 的資料查表得到下表 ,但是由於 0028 這段落禁止觀看 ,所以我改看0030

的段落 ,因為它的 Base 是一樣的。



#0028  Base=00C12000  Limit=00000000  Flags=82         Page granularity

#0030  Base=00C12000  Limit=00000000  Flags=93  USE32  Page granularity



-D 30:0

0030:00000000  FF 00 F0 CE 09 92 40 00-31 00 00 00 CA 9B C0 00 ..pN..@.1...J.@.

0030:00000010  31 00 00 00 CA 93 C0 00-FF FF 00 80 0B 92 40 00 1...J.@.......@.

0030:00000020  FF 00 F0 CE 09 92 40 00-4D 00 90 CE 09 92 40 00 ..pN..@.M..N..@.

0030:00000030  44 01 00 00 00 93 C0 00-00 00 00 00 00 92 40 00 D.....@.......@.

0030:00000040  FF FF 00 80 0B 92 40 00-00 00 00 00 00 92 40 00 ......@.......@.

0030:00000050  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................

0030:00000060  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................

0030:00000070  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................



-DL 0

#0004  Base=0009CEF0  Limit=000000FF  Flags=92  USE32  Byte granularity

#000C  Base=00CA0000  Limit=00000031  Flags=9B  USE32  Page granularity

#0014  Base=00CA0000  Limit=00000031  Flags=93  USE32  Page granularity

#001C  Base=000B8000  Limit=0000FFFF  Flags=92  USE32  Byte granularity

#0024  Base=0009CEF0  Limit=000000FF  Flags=92  USE32  Byte granularity

#002C  Base=0009CE90  Limit=0000004D  Flags=92  USE32  Byte granularity

#0034  Base=00000000  Limit=00000144  Flags=93  USE32  Page granularity

#003C  Base=00000000  Limit=00000000  Flags=92  USE32  Byte granularity

#0044  Base=000B8000  Limit=0000FFFF  Flags=92  USE32  Byte granularity

#004C  Base=00000000  Limit=00000000  Flags=92  USE32  Byte granularity

#0054  Segment not present.

#005C  Segment not present.

#0064  Segment not present.

#006C  Segment not present.

#0074  Segment not present.

#007C  Segment not present.



--------------------------------------------------------------------------

【 IDT 介紹 】

    在以往中斷向量表都是用 4 byte 來表示 ,但是在保護模式下則由 8 byte 表

示 ,至於那幾個 byte 表示什麼 ,筆者還未搞懂 ,底下只弄懂幾個。





C:\>386debug 386debug.exp   (改過的.exp檔)

000C:00027434 660F010F                 SIDT     [EDI]

-D EDI

0014:00000000  FF 07 6C F5 01 00 .. ..-.. .. .. .. .. .. .. ..

                     ^^^^^^^^^^^線性記憶體位址

               ^^^^^長+1

因為該線性記憶體已對映到 50:0

#0050  Base=0001F56C  Limit=000007FF  Flags=92  USE32  Byte granularity

所以:

0050:00000000  00 34 08 00 00 EE 00 00-0A 34 08 00 00 EE 00 00 .4...n...4...n..

0050:00000010  14 34 08 00 00 EE 00 00-1E 34 08 00 00 EE 00 00 .4...n...4...n..

0050:00000020  28 34 08 00 00 EE 00 00-32 34 08 00 00 EE 00 00 (4...n..24...n..

0050:00000030  3C 34 08 00 00 EE 00 00-6C 16 C8 0F 00 8E 00 00 <4...n..F4...n..

0050:00000040  50 34 08 00 00 EE 00 00-5A 34 08 00 00 EE 00 00 P4...n..Z4...n..

0050:00000050  64 34 08 00 00 EE 00 00-6E 34 08 00 00 EE 00 00 d4...n..n4...n..

0050:00000060  78 34 08 00 00 EE 00 00-82 34 08 00 00 EE 00 00 x4...n...4...n..

0050:00000070  8C 34 08 00 00 EE 00 00-96 34 08 00 00 EE 00 00 .4...n...4...n..



-DI 0

#0000  Selector=0008  Offset=00003400  Flags=EE         ;int_0

#0001  Selector=0008  Offset=0000340A  Flags=EE         ;int_1

#0002  Selector=0008  Offset=00003414  Flags=EE         ;int_2

#0003  Selector=0008  Offset=0000341E  Flags=EE         ;int_3

#0004  Selector=0008  Offset=00003428  Flags=EE

#0005  Selector=0008  Offset=00003432  Flags=EE

#0006  Selector=0008  Offset=0000343C  Flags=EE

#0007  Selector=0FC8  Offset=0000166C  Flags=8E         ;此處為Q387使用

#0008  Selector=0008  Offset=00003450  Flags=EE

#0009  Selector=0008  Offset=0000345A  Flags=EE

#000A  Selector=0008  Offset=00003464  Flags=EE

#000B  Selector=0008  Offset=0000346E  Flags=EE

#000C  Selector=0008  Offset=00003478  Flags=EE

#000D  Selector=0008  Offset=00003482  Flags=EE

#000E  Selector=0008  Offset=0000348C  Flags=EE

#000F  Selector=0008  Offset=00003496  Flags=EE



請仔細看一看這個表的對應情形 ,筆者故意載入Q387 以便讓 INT_7 的 Selector 與

眾不同 ,讓您更易判斷中斷表對應關係。

--------------------------------------------------------------------------

實例解說:

底下是讀取 SoftICE INT_0 的程式碼範例:



Load IDT

LDT = FF 07 12 C0 80 00 所以觀看 0080C012 的記憶體

  0080C012  47 2C 18 00 00 EE 00 00-4C 2C 18 00 00 EE 00 00  G,.....L,.....

  0080C022  51 2C 18 00 00 EE 00 00-56 2C 18 00 00 EE 00 00  Q,.....V,.....

  0080C032  5B 2C 18 00 00 EE 00 00-60 2C 18 00 00 EE 00 00  [,.....`,.....

  0080C042  65 2C 18 00 00 EE 00 00-6A 2C 18 00 00 EE 00 00  e,.....j,.....

  0080C052  6F 2C 18 00 00 EE 00 00-74 2C 18 00 00 EE 00 00  o,.....t,.....

  0080C062  79 2C 18 00 00 EE 00 00-7E 2C 18 00 00 EE 00 00  y,.....~,.....

由此得知 INT_0 是放在 0018:00002C47 的位址 ,於是查GDT表..





Load GDT

GDT = C8 00 18 C8 80 00 所以觀看 0080C818 的記憶體

  0080C818  00 00 00 00 00 00 00 00-FF FF 10 11 83 93 00 00  ..............

  0080C828  FF FF 00 6E 81 93 00 00-FF FF 00 6E 81 9B 00 00  ...n.....n..

  0080C838  FF FF 00 00 00 93 CF 00-FF 7F 00 00 0B 92 00 00  ............

  0080C848  FF 7F 00 80 0B 92 00 00-FF FF 00 00 0C 92 00 00  ............

  0080C858  FF FF F0 32 82 9A 00 00-FF FF 00 C0 80 93 C0 00  ..2......

  0080C868  0F 00 00 C0 7F 92 C0 00-68 20 00 00 81 8B 00 00  ....h ....

得到 Selector=0018=線性記憶體位址 816E00 處



於是我們就可以得知該中斷程式放在 816E00:2C47 了 ,於是筆者把 816E00 的記憶體

搬到 8000:0000 ,然後用 DEBUG 來查看。



-u 8000:2c47

8000:2C47 6A00           PUSH   00

8000:2C49 E9F4D6         JMP    0340

8000:2C4C 6A01           PUSH   01

8000:2C4E E9C7D8         JMP    0518

8000:2C51 6A02           PUSH   02

8000:2C53 E98ADC         JMP    08E0

8000:2C56 6A03           PUSH   03

8000:2C58 E9D6DC         JMP    0931

8000:2C5B 6A04           PUSH   04

8000:2C5D E9E0D6         JMP    0340

8000:2C60 6A05           PUSH   05

8000:2C62 E9DBD6         JMP    0340

8000:2C65 6A06           PUSH   06

8000:2C67 E943DF         JMP    0BAD

8000:2C6A 6A07           PUSH   07

8000:2C6C E975E0         JMP    0CE4

8000:2C6F 6A08           PUSH   08

8000:2C71 E97BE1         JMP    0DEF

8000:2C74 6A09           PUSH   09

8000:2C76 E91605         JMP    318F

8000:2C79 6A0A           PUSH   0A

8000:2C7B E9C4D5         JMP    0242

8000:2C7E 6A0B           PUSH   0B

8000:2C80 E9BFD5         JMP    0242



--------------------------------------------------------------------------

看了上面幾個例子後 ,再來就是練習進入保護模式 ,底下的例子請勿載入 EMM 系

列的保護模式軟體 ,以免等級權限相衝當機。



code    segment

        assume  cs:code,ds:code

start   proc    near

        jmp     next



buffer1 db      18h,00h,00h,00h,00h,00h

;               ---+--- ------+--------

;                  |          |

;                  |          |

;                  |          GDT 表的記憶體位址

;                  |

;                  +----------GDT 表的長度

;

;

buffer2 db      000h,000h,000h,000h,000h,000h,000h,000h ;保留段

        db      0ffh,0ffh,000h,000h,000h,09bh,000h,000h ;程式段code:0

        db      0ffh,0ffh,000h,080h,00bh,093h,000h,000h ;螢幕段B800:0

        db      0100h dup (0)

;                         ------+-------

;                               |

;                               |

;                               線性記憶體位址

;

msg_1   db      'Enter Protect Mode !'

msg_2   db      0dh,0ah,'Return Real Mode !',0dh,0ah,'$'

.386p

next :

        mov     ax,0600h        ;

        mov     bx,0700h        ;

        mov     cx,0000h        ;

        mov     dx,184fh        ;

        int     10h             ; CLS

        mov     ah,02h          ;

        mov     bh,00h          ;

        mov     dx,0100h        ;

        int     10h             ;

        mov     ax,cs

        mov     ds,ax

        mov     es,ax

        xor     eax,eax

        xor     ebx,ebx

        mov     ax,cs

        mov     cl,04h

        shl     eax,cl

        mov     bx,offset buffer2

        add     eax,ebx

        mov     bx,offset buffer1+2

        mov     cs:[bx],eax             ;GDT 位址設定

        NOP

        xor     eax,eax

        xor     ebx,ebx

        mov     ax,cs

        mov     cl,04h

        shl     eax,cl

        add     eax,ebx

        mov     bx,offset buffer2

        mov     cs:[bx+0ah],eax                 ;GDT Table 設定

        mov     byte ptr cs:[bx+0dh],9bh        ;存取權

        mov     ax,cs

        mov     ds,ax

        mov     es,ax

        mov     bx,offset buffer1

        xor     ecx,ecx

        cli

        cli

        lgdt    cs:[bx]                         ;載入GDT

        mov     eax,cr0

        or      eax,01h

        mov     cr0,eax

        jmp     protection                      ;進入保護模式

protection :

        db      66h

        mov     ax,code

        mov     ds,ax

        mov     si,offset msg_1

        mov     bx,0010h

        mov     es,bx

        mov     di,0000h

        mov     cx,0014h

        mov     ah,70h

show :

        cld                                     ;將CS:MSG_1搬到 0010:00000000

        lodsb                                   ;(0010的區段=B8000 請參考GDT

        stosw                                   ; 表)

        loop    show                            ;

        mov     eax,cr0

        and     al,not 1

        mov     cr0,eax

        db      0eah

        dw      real_mode,code                  ;返回真實模式

real_mode :

        sti

        mov     ax,cs

        mov     ds,ax

        mov     ah,09h

        mov     dx,offset msg_2

        int     21h

        mov     ax,4cffh

        int     21h

start   endp

code    ends

        end     start



--------------------------------------------------------------------------

    上面這個例子並沒有設定 IDT (中斷表) ,如果您要設定中斷表的話 ,記得返回

真實模式時要還原 IDT 表.

--------------------------------------------------------------------------

    如果您希望在載入 QEMM386 後還能正常進入保護模式的話 ,則必需透過 VCPI

的命令來切入保護模式 ,詳情可翻閱 VCPI 的書籍。





                                                           -- 軟蛀 --



example.ASM:







code    segment

        assume  cs:code,ds:code

start   proc    near

        jmp     next



buffer1 db      18h,00h,00h,00h,00h,00h

;               ---+--- ------+--------

;                  |          |

;                  |          |

;                  |          GDT 表的記憶體位址

;                  |

;                  +----------GDT 表的長度

;

;

buffer2 db      000h,000h,000h,000h,000h,000h,000h,000h ;保留段

        db      0ffh,0ffh,000h,000h,000h,09bh,000h,000h ;程式段code:0

        db      0ffh,0ffh,000h,080h,00bh,093h,000h,000h ;螢幕段B800:0

        db      0100h dup (0)

;                         ------+-------

;                               |

;                               |

;                               線性記憶體位址

;

msg_1   db      'Enter Protect Mode !'

msg_2   db      0dh,0ah,'Return Real Mode !',0dh,0ah,'$'

.386p

next :

        mov     ax,0600h        ;

        mov     bx,0700h        ;

        mov     cx,0000h        ;

        mov     dx,184fh        ;

        int     10h             ; CLS

        mov     ah,02h          ;

        mov     bh,00h          ;

        mov     dx,0100h        ;

        int     10h             ;

        mov     ax,cs

        mov     ds,ax

        mov     es,ax

        xor     eax,eax

        xor     ebx,ebx

        mov     ax,cs

        mov     cl,04h

        shl     eax,cl

        mov     bx,offset buffer2

        add     eax,ebx

        mov     bx,offset buffer1+2

        mov     cs:[bx],eax             ;GDT 位址設定

        NOP

        xor     eax,eax

        xor     ebx,ebx

        mov     ax,cs

        mov     cl,04h

        shl     eax,cl

        add     eax,ebx

        mov     bx,offset buffer2

	mov     cs:[bx+0ah],eax                 ;GDT Table 設定

        mov     byte ptr cs:[bx+0dh],9bh        ;存取權

        mov     ax,cs

        mov     ds,ax

        mov     es,ax

        mov     bx,offset buffer1

        xor     ecx,ecx

        cli

        cli

        lgdt    cs:[bx]                         ;載入GDT

        mov     eax,cr0

        or      eax,01h

        mov     cr0,eax

        jmp     protection                      ;進入保護模式

protection :

        db      66h

        mov     ax,code

        mov     ds,ax

        mov     si,offset msg_1

        mov     bx,0010h

        mov     es,bx

        mov     di,0000h

        mov     cx,0014h

        mov     ah,70h

show :

        cld                                     ;將CS:MSG_1搬到 0010:00000000

        lodsb                                   ;(0010的區段=B8000 請參考GDT

        stosw                                   ; 表)

        loop    show                            ;

        mov     eax,cr0

        and     al,not 1

        mov     cr0,eax

        db      0eah

        dw      real_mode,code                  ;返回真實模式

real_mode :

        sti

        mov     ax,cs

        mov     ds,ax

        mov     ah,09h

        mov     dx,offset msg_2

        int     21h

        mov     ax,4cffh

        int     21h

start   endp

code    ends

        end     start