保护模式预备知识--书中pm.inc代码详细注释
描述符包括,存储段描述符(代码段,数据段,堆栈段),系统描述符(任务状态段TSS,局部描述符表LDT),门描述符(调用门,任务门,中断门,陷阱门),注意门描述符和系统描述符都是DT=0时候,对应的状态。存储段描述符和系统描述符如图1,门描述符如图2。
图1 存储段描述符和系统描述符
图2 门描述符
(1) P:存在(Present)位。
P=1 表示描述符对地址转换是有效的,或者说该描述符所描述的段存在,即在内存中;
P=0 表示描述符对地址转换无效,即该段不存在。使用该描述符进行内存访问时会引起异常。
(2) DPL: 表示描述符特权级(Descriptor Privilege level),共2位。它规定了所描述段的特权级,用于特权检查,以决定对该段能否访问。
(3) DT:说明描述符的类型。
对于存储段描述符而言,DT=1,以区别与系统段描述符和门描述符(DT=0)。
(4) TYPE: 说明存储段描述符所描述的存储段的具体属性。
数据段类型 类型值说明
----------------------------------
0 只读
1 只读、已访问
2 读/写
3 读/写、已访问
4 只读、向下扩展
5 只读、向下扩展、已访问
6 读/写、向下扩展
7 读/写、向下扩展、已访问
类型值 说明
代码段类型 ----------------------------------
8 只执行
9 只执行、已访问
A 执行/读
B 执行/读、已访问
C 只执行、一致码段
D 只执行、一致码段、已访问
E 执行/读、一致码段
F 执行/读、一致码段、已访问
系统段类型 类型编码说明
----------------------------------
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:段界限粒度(Granularity)位。
G=0 表示界限粒度为字节;
G=1 表示界限粒度为4K 字节。
注意,界限粒度只对段界限有效,对段基地址无效,段基地址总是以字节为单位。
(6) D:D位是一个很特殊的位,在描述可执行段、向下扩展数据段或由SS寄存器寻址的段(通常是堆栈段)的三种描述符中的意义各不相同。
在描述可执行段的描述符中,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:软件可利用位。80386对该位的使用未左规定,Intel公司也保证今后开发生产的处理器只要与80386兼容,就不会对该位的使用做任何定义或规定。
(8) Dword Count:从调用者堆栈中将参数复制到被调用者堆栈(新堆栈)中,复制参数的个数由调用门中Dword Count一项来决定。如果Dword Count为0,那么不会复制参数。
选择子图示:
┏━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┓
┃ 15 ┃ 14 ┃ 13 ┃ 12 ┃ 11 ┃ 10 ┃ 9 ┃ 8 ┃ 7 ┃ 6 ┃ 5 ┃ 4 ┃ 3 ┃ 2 ┃ 1 ┃ 0 ┃
┣━━┻━━┻━━┻━━┻━━┻━━┻━━┻━━┻━━┻━━┻━━┻━━┻━━╋━━╋━━┻━━┫
┃ 描述符索引 ┃ TI ┃ RPL ┃
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┻━━┻━━━━━┛
RPL(Requested Privilege Level): 请求特权级,用于特权检查。
TI(Table Indicator): 引用描述符表指示位
TI=0 指示从全局描述符表GDT中读取描述符;
TI=1 指示从局部描述符表LDT中读取描述符。
LDT中的描述符和GDT中的描述符除了选择子的bit3一个为0一个为1用于区分该描述符是在GDT中还是在LDT中外,描述符本身的结构完全一样。开始我考虑既然是这样,为什么要将LDT放在GDT中而不是像GDT那样找一个GDTR寄存器呢?
后来终于明白了原因--很简单,GDT表只有一个,是固定的;而LDT表每个任务就可以有一个,因此有多个,并且由于任务的个数在不断变化其数量也在不断变化。如果只有一个LDTR寄存器显然不能满足多个LDT的要求。因此INTEL的做法是把它放在放在GDT中。
pm.inc代码如下:
;----------------------------------------; 描述符类型值说明; 其中:; DA_ : Descriptor Attribute; D : 数据段; C : 代码段; S : 系统段; R : 只读; RW : 读写; A : 已访问; 其它 : 可按照字面意思理解G D 0 AVL 0 0 0 0 P DPL(2位) DT TYPE(4位);----------------------------------------DA_32EQU4000h; 32 位段 0100 0000 0000 0000DA_DPL0EQU 00h; DPL = 0 0000 0000DA_DPL1EQU 20h; DPL = 1 0010 0000DA_DPL2EQU 40h; DPL = 2 0100 0000DA_DPL3EQU 60h; DPL = 3 0110 0000;----------------------------------------; 存储段描述符类型值说明;----------------------------------------DA_DREQU90h; 存在的只读数据段类型值 1001 0000DA_DRWEQU92h; 存在的可读写数据段属性值 1001 0010DA_DRWAEQU93h; 存在的已访问可读写数据段类型值 1001 0011DA_CEQU98h; 存在的只执行代码段属性值 1001 1000DA_CREQU9Ah; 存在的可执行可读代码段属性值 1001 1010DA_CCOEQU9Ch; 存在的只执行一致代码段属性值 1001 1100DA_CCOREQU9Eh; 存在的可执行可读一致代码段属性值 1001 1110;----------------------------------------; 系统段描述符类型值说明;----------------------------------------DA_LDTEQU 82h; 局部描述符表段类型值 1000 0010DA_TaskGateEQU 85h; 任务门类型值 1000 0101DA_386TSSEQU 89h; 可用 386 任务状态段类型值 1000 1001DA_386CGateEQU 8Ch; 386 调用门类型值 1000 1100DA_386IGateEQU 8Eh; 386 中断门类型值 1000 1110DA_386TGateEQU 8Fh; 386 陷阱门类型值 1000 1111;----------------------------------------;----------------------------------------; 选择子类型值说明; 其中:; SA_ : Selector AttributeSA_RPL0EQU0; ┓00SA_RPL1EQU1; ┣ RPL01SA_RPL2EQU2; ┃10SA_RPL3EQU3; ┛11SA_TIGEQU0; ┓TI 0000SA_TILEQU4; ┛ 0100;----------------------------------------; 宏 ------------------------------------------------------------------;; 描述符; usage: Descriptor Base, Limit, Attr; Base: dd; Limit: dd (low 20 bits available)低二十位可用; Attr: dw (lower 4 bits of higher byte are always 0)高字节的低四位始终为0%macro Descriptor 3 ;段界限为低地址 1代表Base 2代表Limit 3代表属性 dw%2 & 0FFFFh; 段界限 1(2 字节)dw%1 & 0FFFFh; 段首地址 1(2 字节)db(%1 >> 16) & 0FFh; 段首地址 2(1 字节)dw((%2 >> 8) & 0F00h) | (%3 & 0F0FFh); 属性 1 + 段界限 2 + 属性 2(2 字节)db(%1 >> 24) & 0FFh; 段首地址 3(1 字节)%endmacro ; 共 8 字节;; 门; usage: Gate Selector, Offset, DCount, Attr; Selector: dw; Offset: dd; DCount: db; Attr: db%macro Gate 4 ;1代表Selector 2代表Offset 3代表DCount 4代表Attrdw(%2 & 0FFFFh); 偏移 1(2 字节)dw%1; 选择子(2 字节)dw(%3 & 1Fh) | ((%4 << 8) & 0FF00h); 属性(2 字节)dw((%2 >> 16) & 0FFFFh); 偏移 2(2 字节)%endmacro ; 共 8 字节