保护模式
1、为什么GDT(全局描述符表)中第一个描述符为空
答:全局描述符表实际上是为80386以上的32位cpu在保护模式下提供段式寻址机制的一个索引表,这个表的每一个表项(8个字节)描述了一段地址的起始地址、界限以及属性,原来的实模式下的段寄存器存储的值不在是地址的一部分,而其中的高13位表示的是GDT中表项的索引,低3位分别表示TI(1位)和RPL(2位)。GDT的第一个(索引为0)描述符很特殊,8个字节的值全为0,网上查阅资料基本上有三种说法,说法一:GDT的第一个描述符代表NULL的意思,是针对那些没有LDT的任务或用户进程的程序;说法二:代表GDT的起始地址;说法三:CPU规定(ps:处理器规定,GDT中的第一个描述符必须是空描述符。这是什么原因呢?因为很多时候,寄存器和内存单元的初始值都会为0,再加上程序设计有问题,就会在无意中用全0的索引来选择描述符,这当然是不好的。因此,处理器要求将第一个描述符定义成空描述符;而且在进行特权级变换的长返回时,CPU进行的最后一步工作是:检查ds、es、fs、gs的值,如果其中哪一个寄存器指向的段的DPL小于CPL(此规则不适用于一致代码段),那么一个空描述符会被加载到该寄存器)。
我注释掉了GDT中的第一个描述符就不可以运行了,代码如下:
%include "pm.inc"
org 07c00h
jmp LABEL_BEGIN
[SECTION .gdt]
G_DESC_BEGIN:
;G_DESC: Descriptor 0, 0, 0
G_DESC_CODE32: Descriptor 0, SegCode32Len-1, DA_C|DA_32 ;非一致32位代码段
G_DESC_VIDEO: Descriptor 0B8000h, 0ffffh, DA_DRW ;显存段
GdtLen equ $-G_DESC_BEGIN
GdtPtr dw GdtLen-1
dd 0
SelectorCode32 equ G_DESC_CODE32-G_DESC_BEGIN
SelectorVideo equ G_DESC_VIDEO-G_DESC_BEGIN
[SECTION .s16]
[BITS 16]
LABEL_BEGIN:
mov ax,cs
mov ds,ax
;初始化32位代码段描述符
xor eax,eax
mov ax,cs
shl eax,4
add eax,LABEL_SEG_CODE32
mov word [G_DESC_CODE32+2],ax
shr eax,16
mov byte [G_DESC_CODE32+4],al
mov byte [G_DESC_CODE32+7],ah
;为加载GDTR做准备
xor eax,eax
mov ax,ds
shl eax,4
add eax,G_DESC_BEGIN
mov dword [GdtPtr+2],eax
lgdt [GdtPtr]
cli
in al,92h
or al,00000010
out 92,al
mov eax,cr0
or eax,1
mov cr0,eax
jmp dword SelectorCode32:0
[SECTION .s32]
[BITS 32]
LABEL_SEG_CODE32:
mov ax,SelectorVideo
mov gs,ax
mov ah,0Ch
mov al,'G'
mov [gs:((80*18+0)*2)],ax
mov al,'D'
mov [gs:((80*18+1)*2)],ax
mov al,'T'
mov [gs:((80*18+2)*2)],ax
jmp $
SegCode32Len equ $-LABEL_SEG_CODE32
2、G_DESC_NORMAL描述符的作用
从保护模式跳转回实模式,在准备结束保护模式回到实模式之前,需要加载一个合适的描述符选择子到有关的段寄存器,以使对应段描述符高速缓冲寄存器 (见后面的解释)中含有合适的段界限和属性(这里正确的段界限显然是64K,即0ffffh,属性应该是DA_DRW,即90h可读写数据段),而且,不能从32位代码段返回实模式,只能从16位代码段中返回。这是因为无法实现从32位代码段返回时cs高速缓冲寄存器中的属性符合实模式的要求(实模式不能改变段属性)。(高速缓冲寄存器: 在实模式下,段寄存器含有段值,为访问存储器形成物理地址时,处理器引用相应的某个段寄存器并将其值乘以16,形成20位的段基地址。在保护模式下,段寄存器含有段选择子,如上所述,为了访问存储器形成线性地址时,处理器要使用选择子所指定的描述符中的基地址等信息。为了避免在每次存储器访问时,都要访问描述符表而获得对应的段描述符,从80286开始每个段寄存器都配有一个高速缓冲寄存器,称之为段描述符高速缓冲寄存器 或描述符投影寄存器,对程序员而言它是不可见的。每当把一个选择子装入到某个段寄存器时,处理器自动从描述符表中取出相应的描述符,把描述符中的信息保存到对应的高速缓冲寄存器中。此后对该段访问时,处理器都使用对应高速缓冲寄存器中的描述符信息,而不用再从描述符表中取描述符。)
新增的Normal描述符,段界限64K,属性DA_DRW,在返回实模式之前把对应选择子SelectorNormal加载到ds、es和ss正好合适。
3、A20地址线
8086有20位地址总线寻址空间为1M,在理论上段:偏移的寻址模式在实模式下最大地址为FFFF:FFFF=10FFEF,这显然超过了20位地址总线所能表示的最大地址值(FFFFF),而试图访问超过1M以上地址的空间并不会造成错误,而是会回卷,即10FFEF只会表示0FFEF(10FFEF%100000)的物理地址值。但是到了80286(24位地址总线)以上,真的可以访问到1M以上的地址空间了,这样的寻址方式会造成向上不兼容,所以IBM提出了一个方案,通过8042键盘控制器控制索引为20(从0开始)的地址位,在实模式下,关闭A20地址线,这时如果cpu有超过20位的地址线,则A20始终为0,所以FFFF:FFFF还是表示0FFEF的地址值,这样8086、80286、80386等等cpu在实模式下寻址方式就很好的兼容起来,绝大多数IBM PC兼容机默认的A20地址线是被关闭的。由于在当时没有更好的方法来解决这个问题,所以IBM使用了键盘控制器来操作A20地址线