保护模式与实模式的切换


 

关于从保护模式切换到实模式的相关说明

 

参考于渊的《自己动手写操作系统》第三章中从实模式切换到保护模式,最后有重新切回实模式的代码(代码如下),其中有几点不太明白的,参考其他文章之后在此记录一下。


其中还有不太明白的地方,希望大家能在下面 留个言帮我讲明白,谢谢。

下面代码有些宏定义没贴出来,应该能看明白。

  1 ==========================================
  2 ; pmtest2.asm
  3 ; 编译方法:nasm pmtest2.asm  - o pmtest2.com
  4 ==========================================
  5
  6 % include     " pm.inc "     ; 常量, 宏, 以及一些说明
  7
  8 org    0100h
  9     jmp    LABEL_BEGIN
 10
 11 [SECTION .gdt]
 12 ; GDT
 13 ;                                         段基址,       段界限     , 属性
 14 LABEL_GDT:        Descriptor            0 ,                  0 0              ; 空描述符
 15 LABEL_DESC_NORMAL:    Descriptor            0 ,            0ffffh, DA_DRW        ;  *** 注意此处为Normal 描述符 *****
 16 LABEL_DESC_CODE32:    Descriptor            0 ,  SegCode32Len  -   1 , DA_C  +  DA_32    ; 非一致代码段,  32
 17 LABEL_DESC_CODE16:    Descriptor            0 ,            0ffffh, DA_C        ; 非一致代码段,  16
 18 LABEL_DESC_DATA:    Descriptor            0 ,    DataLen  -   1 , DA_DRW        ; Data
 19 LABEL_DESC_STACK:    Descriptor            0 ,        TopOfStack, DA_DRWA  +  DA_32    ; Stack,  32  位
 20 LABEL_DESC_TEST:    Descriptor    0500000h,            0ffffh, DA_DRW
 21 LABEL_DESC_VIDEO:    Descriptor     0B8000h,            0ffffh, DA_DRW        ; 显存首地址
 22 ; GDT 结束
 23
 24 GdtLen        equ    $  -  LABEL_GDT    ; GDT长度
 25 GdtPtr        dw    GdtLen  -   1     ; GDT界限
 26         dd     0         ; GDT基地址
 27
 28 ; GDT 选择子
 29 SelectorNormal        equ    LABEL_DESC_NORMAL     -  LABEL_GDT
 30 SelectorCode32        equ    LABEL_DESC_CODE32     -  LABEL_GDT
 31 SelectorCode16        equ    LABEL_DESC_CODE16     -  LABEL_GDT
 32 SelectorData        equ    LABEL_DESC_DATA         -  LABEL_GDT
 33 SelectorStack        equ    LABEL_DESC_STACK     -  LABEL_GDT
 34 SelectorTest        equ    LABEL_DESC_TEST         -  LABEL_GDT
 35 SelectorVideo        equ    LABEL_DESC_VIDEO     -  LABEL_GDT
 36 ; END of [SECTION .gdt]
 37
 38 [SECTION .data1]     ; 数据段
 39 ALIGN     32
 40 [BITS     32 ]
 41 LABEL_DATA:
 42 SPValueInRealMode    dw     0
 43 ; 字符串
 44 PMMessage:        db     " In Protect Mode now. ^-^ " 0     ; 进入保护模式后显示此字符串
 45 OffsetPMMessage        equ    PMMessage  -  $$
 46 StrTest:        db     " ABCDEFGHIJKLMNOPQRSTUVWXYZ " 0
 47 OffsetStrTest        equ    StrTest  -  $$
 48 DataLen            equ    $  -  LABEL_DATA
 49 ; END of [SECTION .data1]
 50
 51
 52 ; 全局堆栈段
 53 [SECTION .gs]
 54 ALIGN     32
 55 [BITS     32 ]
 56 LABEL_STACK:
 57     times  512  db  0
 58
 59 TopOfStack    equ    $  -  LABEL_STACK  -   1
 60
 61 ; END of [SECTION .gs]
 62
 63
 64 [SECTION .s16]
 65 [BITS     16 ]
 66 LABEL_BEGIN:
 67     mov    ax, cs
 68     mov    ds, ax
 69     mov    es, ax
 70     mov    ss, ax
 71     mov    sp, 0100h
 72
 73     mov    [LABEL_GO_BACK_TO_REAL + 3 ], ax
 74     mov    [SPValueInRealMode], sp
 75
 76     ; 初始化  16  位代码段描述符
 77     mov    ax, cs
 78     movzx    eax, ax
 79     shl    eax,  4
 80     add    eax, LABEL_SEG_CODE16
 81     mov    word [LABEL_DESC_CODE16  +   2 ], ax
 82     shr    eax,  16
 83     mov     byte  [LABEL_DESC_CODE16  +   4 ], al
 84     mov     byte  [LABEL_DESC_CODE16  +   7 ], ah
 85
 86     ; 初始化  32  位代码段描述符
 87     xor    eax, eax
 88     mov    ax, cs
 89     shl    eax,  4
 90     add    eax, LABEL_SEG_CODE32
 91     mov    word [LABEL_DESC_CODE32  +   2 ], ax
 92     shr    eax,  16
 93     mov     byte  [LABEL_DESC_CODE32  +   4 ], al
 94     mov     byte  [LABEL_DESC_CODE32  +   7 ], ah
 95
 96     ; 初始化数据段描述符
 97     xor    eax, eax
 98     mov    ax, ds
 99     shl    eax,  4
100     add    eax, LABEL_DATA
101     mov    word [LABEL_DESC_DATA  +   2 ], ax
102     shr    eax,  16
103     mov     byte  [LABEL_DESC_DATA  +   4 ], al
104     mov     byte  [LABEL_DESC_DATA  +   7 ], ah
105
106     ; 初始化堆栈段描述符
107     xor    eax, eax
108     mov    ax, ds
109     shl    eax,  4
110     add    eax, LABEL_STACK
111     mov    word [LABEL_DESC_STACK  +   2 ], ax
112     shr    eax,  16
113     mov     byte  [LABEL_DESC_STACK  +   4 ], al
114     mov     byte  [LABEL_DESC_STACK  +   7 ], ah
115
116     ; 为加载 GDTR 作准备
117     xor    eax, eax
118     mov    ax, ds
119     shl    eax,  4
120     add    eax, LABEL_GDT        ; eax  <-  gdt 基地址
121     mov    dword [GdtPtr  +   2 ], eax    ; [GdtPtr  +   2 <-  gdt 基地址
122
123     ; 加载 GDTR
124     lgdt    [GdtPtr]
125
126     ; 关中断
127     cli
128
129     ; 打开地址线A20
130      in     al, 92h
131     or    al, 00000010b
132      out     92h, al
133
134     ; 准备切换到保护模式
135     mov    eax, cr0
136     or    eax,  1
137     mov    cr0, eax
138
139     ; 真正进入保护模式
140   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;在此由实模式切进保护模式;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
141     jmp    dword SelectorCode32: 0     ; 执行这一句会把 SelectorCode32 装入 cs, 并跳转到 Code32Selector: 0   处
142
143 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
144
145 LABEL_REAL_ENTRY:        ; 从保护模式跳回到实模式就到了这里
146     mov    ax, cs
147     mov    ds, ax
148     mov    es, ax
149     mov    ss, ax
150
151     mov    sp, [SPValueInRealMode]
152
153      in     al, 92h        ; ┓
154     and    al, 11111101b    ; ┣ 关闭 A20 地址线
155      out     92h, al        ; ┛
156
157     sti            ; 开中断
158
159     mov    ax, 4c00h    ; ┓
160      int     21h        ; ┛回到 DOS
161 ; END of [SECTION .s16]
162
163
164 [SECTION .s32];  32  位代码段. 由实模式跳入.
165 [BITS     32 ]
166
167 LABEL_SEG_CODE32:
168     mov    ax, SelectorData
169     mov    ds, ax            ; 数据段选择子
170     mov    ax, SelectorTest
171     mov    es, ax            ; 测试段选择子
172     mov    ax, SelectorVideo
173     mov    gs, ax            ; 视频段选择子
174
175     mov    ax, SelectorStack
176     mov    ss, ax            ; 堆栈段选择子
177
178     mov    esp, TopOfStack
179
180
181     ; 下面显示一个字符串
182     mov    ah, 0Ch            ;  0000 : 黑底     1100 : 红字
183     xor    esi, esi
184     xor    edi, edi
185     mov    esi, OffsetPMMessage    ; 源数据偏移
186     mov    edi, ( 80   *   10   +   0 *   2     ; 目的数据偏移。屏幕第  10  行, 第  0  列。
187     cld
188 . 1 :
189     lodsb
190     test    al, al
191     jz    . 2
192     mov    [gs:edi], ax
193     add    edi,  2
194     jmp    . 1
195 . 2 :    ; 显示完毕
196
197     call    DispReturn
198
199     call    TestRead
200     call    TestWrite
201     call    TestRead
202
203     ; 到此停止
204   ; ********** 注意在此由32位代码段跳至16位代码段 **********************
205     jmp    SelectorCode16: 0
206
207 ------------------------------------------------------------------------
208 TestRead:
209     xor    esi, esi
210     mov    ecx,  8
211 .loop
212     mov    al, [es:esi]
213     call    DispAL
214     inc    esi
215     loop    .loop
216
217     call    DispReturn
218
219     ret
220 ; TestRead 结束 -----------------------------------------------------------
221
222
223 ------------------------------------------------------------------------
224 TestWrite:
225     push    esi
226     push    edi
227     xor    esi, esi
228     xor    edi, edi
229     mov    esi, OffsetStrTest    ; 源数据偏移
230     cld
231 . 1 :
232     lodsb
233     test    al, al
234     jz    . 2
235     mov    [es:edi], al
236     inc    edi
237     jmp    . 1
238 . 2 :
239
240     pop    edi
241     pop    esi
242
243     ret
244 ; TestWrite 结束 ----------------------------------------------------------
245
246
247 ------------------------------------------------------------------------
248 ; 显示 AL 中的数字
249 ; 默认地:
250 ;    数字已经存在 AL 中
251 ;    edi 始终指向要显示的下一个字符的位置
252 ; 被改变的寄存器:
253 ;    ax, edi
254 ------------------------------------------------------------------------
255 DispAL:
256     push    ecx
257     push    edx
258
259     mov    ah, 0Ch            ;  0000 : 黑底     1100 : 红字
260     mov    dl, al
261     shr    al,  4
262     mov    ecx,  2
263 .begin:
264     and    al, 01111b
265     cmp    al,  9
266     ja    . 1
267     add    al,  ' 0 '
268     jmp    . 2
269 . 1 :
270     sub    al, 0Ah
271     add    al,  ' A '
272 . 2 :
273     mov    [gs:edi], ax
274     add    edi,  2
275
276     mov    al, dl
277     loop    .begin
278     add    edi,  2
279
280     pop    edx
281     pop    ecx
282
283     ret
284 ; DispAL 结束 -------------------------------------------------------------
285
286
287 ------------------------------------------------------------------------
288 DispReturn:
289     push    eax
290     push    ebx
291     mov    eax, edi
292     mov    bl,  160
293     div    bl
294     and    eax, 0FFh
295     inc    eax
296     mov    bl,  160
297     mul    bl
298     mov    edi, eax
299     pop    ebx
300     pop    eax
301
302     ret
303 ; DispReturn 结束 ---------------------------------------------------------
304
305 SegCode32Len    equ    $  -  LABEL_SEG_CODE32
306 ; END of [SECTION .s32]
307
308
309 16  位代码段. 由  32  位代码段跳入, 跳出后到实模式
310 [SECTION .s16code]
311 ALIGN     32
312 [BITS     16 ]
313 LABEL_SEG_CODE16:
314     ; 跳回实模式:
315   ; **************** 注意在此用normal选择子对段寄存器进行填充 ******************************
316     mov    ax, SelectorNormal
317     mov    ds, ax
318     mov    es, ax
319     mov    fs, ax
320     mov    gs, ax
321     mov    ss, ax
322
323     mov    eax, cr0
324     and    al, 11111110b
325     mov    cr0, eax
326
327 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;在此由保护模式切进实模式;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
328 LABEL_GO_BACK_TO_REAL:
329     jmp     0 :LABEL_REAL_ENTRY    ; 段地址会在程序开始处被设置成正确的值
330
331 Code16Len    equ    $  -  LABEL_SEG_CODE16
332
333 ; END of [SECTION .s16code]
334

 

 

 

注意一.在由保护模式切换到实模式之前,用normal选择子对段寄存器进行填充。

原因:

在切换到实模式之前,把一个指向似乎没有用的数据段的描述符Normal的选择子装载到DSES。这是为什么呢?

实模
式下
段描
述符
高速
缓冲
寄存
器的
内容

段寄存器

段基地址

段界限(固定)

段属性(固定)

存在性

特权级

已存取

粒度

扩展方向

可读性

可写性

可执行

堆栈大小

一致特权

CS

当前CS*16

0000FFFFH

Y

0

Y

B

U

Y

Y

Y

-

N

SS

当前SS*16

0000FFFFH

Y

0

Y

B

U

Y

Y

N

W

-

DS

当前DS*16

0000FFFFH

Y

0

Y

B

U

Y

Y

N

-

-

ES

当前ES*16

0000FFFFH

Y

0

Y

B

U

Y

Y

N

-

-

FS

当前FS*16

0000FFFFH

Y

0

Y

B

U

Y

Y

N

-

-

GS

当前GS*16

0000FFFFH

Y

0

Y

B

U

Y

Y

N

-

-

     在分段管理机制中,每个段寄存器都配有段描述符高速缓冲寄存器,这些高速缓冲寄存器在实方式下仍发挥作用,只是内容上与保护模式下有所不同。如上表所示,其中“Y”表示 “N”表示“B”表示字节;“U”表示向上扩展,“W”表示以字方式操作堆栈。段基地址仍是 32位,其值是相应段寄存器值(段值)乘以16,在把段值装载到段寄存器时刷新。由于其值是16位段值乘上16,所以在实模式下基地址实际上有效位只有20位。每个段的32位段界限都固定为0FFFFH,段属性的许多位也是固定的。所谓固定是指在实方式下不可设置这些属性值,只能继续沿用保护方式下所设置的值。因此,在准备结束保护模式回到实模式之前,要通过加载一个合适的描述符选择子(如实例代码中的Normal选择子)到有关段寄存器,以使得对应段描述符高速缓冲寄存器中含有合适的段界限和属性。

 

也就是说,在实模式下装载段寄存器并不会影响段告诉缓冲寄存器的值,比如段界限(其实在实模式也没有必要改变,应为段界限一直都是0ffffh),这也就是为甚麽所有讲保护模式的树在讲到有保护模式切换到实模式时都要加载一个normal选择子的原因了。

应为必须在保护模式下设置好段高速缓冲寄存器的值,因为一旦到了实模式下就不能在改变了。

经我试验,对于normal的描述符,其最重要是段界限一定要设置为0ffffh,如果不是这样,那莫在由保护模式跳转到实模式后会发生错误(对于上述代码如果把normal描述符的段界限改为别的的话,在跳转后会产生死循环的现象,具体是什么原因现在还不明确,哪位高人知道一定要告诉我啊~~)。其次就是属性的设置一定要设置为可读可写的,否则也会发生错误.

 

注意二:不能从32位代码段返回实模式,而只能从16位代码段返回。

原因:(书中说的)因为无法实现从32位代码段返回时CS高速缓冲寄存器中的属性符合实模式的要求(实模式不能改变段属性)

对于这个解释还是不太明确,如果哪位高人明白其中的来龙去脉的话,请一定在下面留个言,给我解释一下,不胜感激.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值