(第三章 8 )特权级——CPL、DPL、RPL

      很久以后,等我大量翻阅关于“保护模式的特权级检查(DPL,RPL,CPL, 一致代码段,非一致代码段)”的资料后,我才发现这篇博客理解得太肤浅了,而且有错(但后面的实验步骤和代码还是可以凑合看一下的),因此特意写了另一篇(主要是转载),名为“(第三章 8 )特权级——保护模式的特权级检查(DPL,RPL,CPL, 一致代码段,非一致代码段)”。

      本节见书P48~49。在IA32的分段机制中,特权级共有4个特权级别,从高到低分别是0>1>2>3. 下面通过一个例子来说明CPL、DPL、RPL.

1. 先要明确CPL、DPL、RPL的概念,以及她们在哪里

      DPL ,Descriptor Privilege Level,表示段或门的特权级。它是一个静态的概念,包含于段描述符的属性字段部分(P32),只要代码写好了,每个段的特权级就定了。

      以本章第一个程序chapter3/a/pmtest1.asm(见P25~)为例,

      LABEL_DESC_CODE32:  Descriptor    0,    SegCode32Len-1,    DA_C+DA_32 中最后一个字段指定属性,这里采用默认的DPL特权级0(没有显式指定)

      RPL ,Requested Privilege Level,是“调用过程”的选择子的第0位和第1位(P32下面)。

      以本章第一个程序chapter3/a/pmtest1.asm为例,

      ...

      SelectorCode32    equ    LABEL_DESC_CODE32 - LABEL_GDT

      至此,如果SelectorCode32对应的代码段[SECTION .s32]被调用时(如执行jmp dword SelectorCode32:0),RPL就会被设置成SelectorCode32的第0位和第1位。此时执行jmp dword SelectorCode32:0的过程称“调用过程”,而[SECTION .s32]这个代码段称为“被调用过程”。

      CPL ,Current Privilege Level,是“被调用过程”(当前执行程序或任务)的特权级,它被存储在cs和ss的第0位和第1位上。

      以本章第一个程序chapter3/a/pmtest1.asm为例,在[SECTION .s16]最后,

      jmp dword SelectorCode32:0执行之前,CPL是[SECTION .s16]的DPL,此时为0;

      jmp dword SelectorCode32:0执行时,会将SelectorCode32装入cs(注:选择子和cs都是两个字节的,见P32),因为CPL是cs的第0位和第1位,所以CPL就变成了SelectorCode32对应段的DPL,此时也是0.

      CPL虽然直接由cs中的最低2位决定,但最终是由“jmp到被调用段时指定的选择子”(此处jmp dword SelectorCode32:0,被调用段的选择子SelectorCode32中最低2位为0,则跳转后CPL=0)决定的。

      对于数据的访问,特权级检验还是比较简单的,只要CPL和RPL都小于被访问的数据段的DPL就可以了。

2. 例子

执行方式如下:

1. asm生成com文件

[hadoop@sam1 c]$ pwd

/home/hadoop/Desktop/OSImpl/一个操作系统的实现/chapter3/c

[hadoop@sam1 c]$ nasm pmtest3.asm -o pmtest3.com

 

2. 将编译好的com文件复制到软盘b(镜像为pm.img)

[hadoop@sam1 bochs-2.6]$ pwd

/home/hadoop/Desktop/OSImpl/bochs-2.6

[hadoop@sam1 bochs-2.6]$ sudo mount -o loop pm.img /mnt/floppy/

[hadoop@sam1 bochs-2.6]$ sudo cp /home/hadoop/Desktop/OSImpl/一个操作系统的实现/chapter3/c/pmtest3.com /mnt/floppy/

[hadoop@sam1 bochs-2.6]$ sudo umount /mnt/floppy/

 

3. 启动freedos(软盘a:镜像freedos.img),加载软盘b(镜像pm.img)中的com文件

[hadoop@sam1 bochs-2.6]$ ./bochs

...
Please choose one: [6] 2
...
To cancel, type 'none'. [.bochsrc] 
...
Please choose one: [6]
...
<bochs:1> c
 


 

发现根本不能运行,这就对了

 
注意到这里涉及到特权级别的几个地方:

LABEL_DESC_DATA:   Descriptor       0,       DataLen - 1, DA_DRW+DA_DPL1; Data

...

SelectorDataequLABEL_DESC_DATA- LABEL_GDT + SA_RPL3

...

[SECTION .s32]; 32 位代码段. 由实模式跳入.

LABEL_SEG_CODE32:

movax, SelectorData

movds, ax; 数据段选择子

... 然后尝试访问Data段,就会失败,因为CPL=0 (当前段[SECTION .s32]的特权级),RPL=3(使用的段选择子SelectorData里面末两位),被访问数据段的DPL=1。不满足 CPL<DPL && RPL<DPL

    下面是完整代码:

; ==========================================

; pmtest3.asm

; 编译方法:nasm pmtest3.asm -o pmtest3.com

; ==========================================

%include"pm.inc"; 常量, 宏, 以及一些说明

org0100h

jmpLABEL_BEGIN

[SECTION .gdt]

; GDT

;                                         段基址,       段界限     , 属性

LABEL_GDT:         Descriptor       0,                 0, 0     ; 空描述符

LABEL_DESC_NORMAL: Descriptor       0,            0ffffh, DA_DRW; Normal 描述符

LABEL_DESC_CODE32: Descriptor       0,  SegCode32Len - 1, DA_C + DA_32; 非一致代码段, 32

LABEL_DESC_CODE16: Descriptor       0,            0ffffh, DA_C; 非一致代码段, 16

LABEL_DESC_DATA:   Descriptor       0,       DataLen - 1, DA_DRW+DA_DPL1; Data

LABEL_DESC_STACK:  Descriptor       0,        TopOfStack, DA_DRWA + DA_32; Stack, 32 位

LABEL_DESC_LDT:    Descriptor       0,        LDTLen - 1, DA_LDT; LDT

LABEL_DESC_VIDEO:  Descriptor 0B8000h,            0ffffh, DA_DRW; 显存首地址

; GDT 结束

GdtLenequ$ - LABEL_GDT; GDT长度

GdtPtrdwGdtLen - 1; GDT界限

dd0; GDT基地址

; GDT 选择子

SelectorNormalequLABEL_DESC_NORMAL- LABEL_GDT

SelectorCode32equLABEL_DESC_CODE32- LABEL_GDT

SelectorCode16equLABEL_DESC_CODE16- LABEL_GDT

SelectorDataequLABEL_DESC_DATA- LABEL_GDT + SA_RPL3

SelectorStackequLABEL_DESC_STACK- LABEL_GDT

SelectorLDTequLABEL_DESC_LDT- LABEL_GDT

SelectorVideoequLABEL_DESC_VIDEO- LABEL_GDT

; END of [SECTION .gdt]

[SECTION .data1] ; 数据段

ALIGN32

[BITS32]

LABEL_DATA:

SPValueInRealModedw0

; 字符串

PMMessage:db"In Protect Mode now. ^-^", 0; 进入保护模式后显示此字符串

OffsetPMMessageequPMMessage - $$

StrTest:db"ABCDEFGHIJKLMNOPQRSTUVWXYZ", 0

OffsetStrTestequStrTest - $$

DataLenequ$ - LABEL_DATA

; END of [SECTION .data1]

; 全局堆栈段

[SECTION .gs]

ALIGN32

[BITS32]

LABEL_STACK:

times 512 db 0

TopOfStackequ$ - LABEL_STACK - 1

; END of [SECTION .gs]

[SECTION .s16]

[BITS16]

LABEL_BEGIN:

movax, cs

movds, ax

moves, ax

movss, ax

movsp, 0100h

mov[LABEL_GO_BACK_TO_REAL+3], ax

mov[SPValueInRealMode], sp

; 初始化 16 位代码段描述符 ==> [LABEL_SEG_CODE16]在“GDT本段对应的全局描述符”中已经设置了“段长度”和“段属性”,这里实际上是把“段基址”混入进去,使得“GDT本段对应的全局描述符”完整了!!!

movax, cs

movzxeax, ax

shleax, 4

addeax, LABEL_SEG_CODE16

movword [LABEL_DESC_CODE16 + 2], ax

shreax, 16

movbyte [LABEL_DESC_CODE16 + 4], al

movbyte [LABEL_DESC_CODE16 + 7], ah

; 初始化 32 位代码段描述符

xoreax, eax

movax, cs

shleax, 4

addeax, LABEL_SEG_CODE32

movword [LABEL_DESC_CODE32 + 2], ax

shreax, 16

movbyte [LABEL_DESC_CODE32 + 4], al

movbyte [LABEL_DESC_CODE32 + 7], ah

; 初始化数据段描述符

xoreax, eax

movax, ds

shleax, 4

addeax, LABEL_DATA

movword [LABEL_DESC_DATA + 2], ax

shreax, 16

movbyte [LABEL_DESC_DATA + 4], al

movbyte [LABEL_DESC_DATA + 7], ah

; 初始化堆栈段描述符

xoreax, eax

movax, ds

shleax, 4

addeax, LABEL_STACK

movword [LABEL_DESC_STACK + 2], ax

shreax, 16

movbyte [LABEL_DESC_STACK + 4], al

movbyte [LABEL_DESC_STACK + 7], ah

; 初始化 LDT 在 GDT 中的描述符

xoreax, eax

movax, ds

shleax, 4

addeax, LABEL_LDT

movword [LABEL_DESC_LDT + 2], ax

shreax, 16

movbyte [LABEL_DESC_LDT + 4], al

movbyte [LABEL_DESC_LDT + 7], ah

; 初始化 LDT 中的描述符

xoreax, eax

movax, ds

shleax, 4

addeax, LABEL_CODE_A

movword [LABEL_LDT_DESC_CODEA + 2], ax

shreax, 16

movbyte [LABEL_LDT_DESC_CODEA + 4], al

movbyte [LABEL_LDT_DESC_CODEA + 7], ah

; 为加载 GDTR 作准备

xoreax, eax

movax, ds

shleax, 4

addeax, LABEL_GDT; eax <- gdt 基地址

movdword [GdtPtr + 2], eax; [GdtPtr + 2] <- gdt 基地址

; 加载 GDTR

lgdt[GdtPtr]

; 关中断

cli

; 打开地址线A20

inal, 92h

oral, 00000010b

out92h, al

; 准备切换到保护模式

moveax, cr0

oreax, 1

movcr0, eax

; 真正进入保护模式

jmpdword SelectorCode32:0; 执行这一句会把 SelectorCode32 装入 cs, 并跳转到 Code32Selector:0  处

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

LABEL_REAL_ENTRY:; 从保护模式跳回到实模式就到了这里

movax, cs

movds, ax

moves, ax

movss, ax

movsp, [SPValueInRealMode]

inal, 92h; ┓

andal, 11111101b; ┣ 关闭 A20 地址线

out92h, al; ┛

sti; 开中断

movax, 4c00h; ┓

int21h; ┛回到 DOS

; END of [SECTION .s16]

[SECTION .s32]; 32 位代码段. 由实模式跳入.

[BITS32]

LABEL_SEG_CODE32:

movax, SelectorData

movds, ax; 数据段选择子

movax, SelectorVideo

movgs, ax; 视频段选择子

movax, SelectorStack

movss, ax; 堆栈段选择子

movesp, TopOfStack

; 下面显示一个字符串

movah, 0Ch; 0000: 黑底    1100: 红字

xoresi, esi

xoredi, edi

movesi, OffsetPMMessage; 源数据偏移

movedi, (80 * 10 + 0) * 2; 目的数据偏移。屏幕第 10 行, 第 0 列。

cld

.1:

lodsb                            ;  [esi] --byte--> al

testal, al                     ;  若已经到达最后一个字符0,则结束循环显示每个字符

jz.2

mov[gs:edi], ax           ;  ax(ah: 黑底红字  al: ASCII字符) --2 bytes--> [gs:edi]

addedi, 2                    ;  [gs:edi] 向后跳两个字节  

jmp.1                           ;  显示下一个字符

.2:; 显示完毕

callDispReturn

; Load LDT

movax, SelectorLDT

lldtax

jmpSelectorLDTCodeA:0; 跳入局部任务

; ------------------------------------------------------------------------

DispReturn:

pusheax

pushebx

moveax, edi

movbl, 160

divbl

andeax, 0FFh

inceax

movbl, 160

mulbl

movedi, eax

popebx

popeax

ret

; DispReturn 结束---------------------------------------------------------

SegCode32Lenequ$ - LABEL_SEG_CODE32

; END of [SECTION .s32]

; 16 位代码段. 由 32 位代码段跳入, 跳出后到实模式

[SECTION .s16code]

ALIGN32

[BITS16]

LABEL_SEG_CODE16:

; 跳回实模式:

movax, SelectorNormal

movds, ax

moves, ax

movfs, ax

movgs, ax

movss, ax

moveax, cr0

andal, 11111110b

movcr0, eax

LABEL_GO_BACK_TO_REAL:

jmp0:LABEL_REAL_ENTRY; 段地址会在程序开始处被设置成正确的值

Code16Lenequ$ - LABEL_SEG_CODE16

; END of [SECTION .s16code]

; LDT

[SECTION .ldt]

ALIGN32

LABEL_LDT:

;                            段基址       段界限      属性

LABEL_LDT_DESC_CODEA: Descriptor 0, CodeALen - 1, DA_C + DA_32 ; Code, 32 位

LDTLenequ$ - LABEL_LDT

; LDT 选择子

SelectorLDTCodeAequLABEL_LDT_DESC_CODEA- LABEL_LDT + SA_TIL

; END of [SECTION .ldt]

; CodeA (LDT, 32 位代码段)

[SECTION .la]

ALIGN32

[BITS32]

LABEL_CODE_A:

movax, SelectorVideo

movgs, ax; 视频段选择子(目的)

movedi, (80 * 12 + 0) * 2; 屏幕第 10 行, 第 0 列。

movah, 0Ch; 0000: 黑底    1100: 红字

moval, 'L'

mov[gs:edi], ax

; 准备经由16位代码段跳回实模式

jmpSelectorCode16:0

CodeALenequ$ - LABEL_CODE_A

; END of [SECTION .la]

小结:

      理解“选择子”和DPL, RPL, CPL四者的转化和对应关系。

猜你喜欢

转载自chuanwang66.iteye.com/blog/1584775