内核要和用户程序分开,内核一定要安全,不能被用户程序干涉,但是有时候用户程序也需要读取内核的某些数据,怎么办呢?x86就引入了访问特权等级(0-3)的机制,x86 cpu共有4个特权级 level0 到 level3 其中level0特权级最高,level3特权级最高。处理器通过识别CPL、DPL、RPL这3中种特权级进行特权级检验。
CPL(Current Privilege Level)是当前执行的程序或任务的特权级。它被存储在CS和SS的第0位和第1位上。
DPL(Descriptor Privilege Level)表示段或者门的特权级。它被存储在段描述符或者门描述符的DPL字段中。
RPL(Requessted Privilege Level)是通过段选择子的第0位和第1位表现出来的。
jmp和call所能进行的代码间转移是非常有限的,对于非一致代码段,只能在相同特权级代码间转移;对于一致代码段,可以从低到高,且CPL不会改变。
我们一般用调用门和lcall指令实现特权级由低到高的转移,通过lret指令实现特权级由高到低的转移。
其实我们一直在特权级0下工作,下面我们就用lret实现特权级由高到低的转移,ring0--->ring3
- #define Descriptor(base,lim,attr)\
- .word lim&0xffff;\
- .word base&0xffff;\
- .byte (base>>16)&0xff;\
- .word ((lim>>8)&0xf00)|(attr&0x0f0ff);\
- .byte ((base>>24)&0xff)
- /*
- *InitDescrptor(Descriptor,SegBase)初始化描述符函数
- *Descriptor:要初始化的描述符
- *SegBase:段基址
- */
- #define InitDescrptor(Descriptor,SegBase)\
- xor %eax,%eax; \
- mov %cs,%ax ; \
- shl $4,%eax ; \
- addl $(SegBase), %eax ;\
- movw %ax, (Descriptor + 2);\
- shr $16, %eax;\
- movb %al, (Descriptor + 4);\
- movb %ah, (Descriptor + 7)
- #define Gate(Selector,Offset,PCount,Attr)\
- .2byte (Offset&0xffff);\
- .2byte (Selector);\
- .2byte (PCount&0x1f)|((Attr<<8)&0xff00);\
- .2byte ((Offset>>16)&0xffff)
- DA_386CGate = 0x8c
- DA_C = 0x98
- DA_32 = 0x4000
- DA_DRW = 0x92
- DA_DRWA=0x93
- SA_TIL = 0x4
- DA_LDT = 0x82
- SA_RPL3 = 3
- DA_DPL0=0x00
- DA_DPL1=0x20
- DA_DPL2=0x40
- DA_DPL3=0x60
- .text
- .globl start
- .code16
- start:
- jmpl $0x0, $code
- /**-----------------------------------------------------------------
- * 全局描述符表: GDT
- *-------------------------------*/
- GDT_START:
- Descriptor_DUMMY: Descriptor(0x0,0x0,0x0)
- Descriptor_CODE32:Descriptor(0x0,0xffffffff,DA_C+DA_32)
- Descriptor_VIDEO: Descriptor(0xb8000,0x0ffff,DA_DRW + DA_DPL3)
- Descriptor_CODE_GATE:Descriptor(0x0,CodeGLen,DA_C+DA_32)
- Descriptor_LDT: Descriptor(0x0,LDTLen - 1, DA_LDT)
- Descriptor_CODE3:Descriptor(0x0,0xffffffff,DA_C+DA_32+DA_DPL3)
- Descriptor_STACKR3:Descriptor(0,TopOfStackR3, (DA_DRWA + DA_32 + DA_DPL3))
- /*门描述符*/
- Descriptor_Gate_Call:Gate(Selector_Code_Gate,0,0,(DA_386CGate+DA_DPL0))
- GDT_END:
- GdtPtr:
- .word (GDT_END-GDT_START)-1 # so does gdt
- .long GDT_START # This will be rewrite by code.
- /**-----------------------------------------------------------------
- * GDT中的选择子
- *-------------------------------*/
- .set selector_Code32,(Descriptor_CODE32-GDT_START)
- .set Selector_Video,(Descriptor_VIDEO-GDT_START)
- .set Selector_Code_Gate,(Descriptor_CODE_GATE-GDT_START)
- .set SelectorLDT,(Descriptor_LDT-GDT_START)
- .set selector_CodeR3,(Descriptor_CODE3-GDT_START+SA_RPL3)
- .set SelectorStackR3,(Descriptor_STACKR3- GDT_START + SA_RPL3)
- /*调用门选择子*/
- .set Selector_Gate_Call,(Descriptor_Gate_Call-GDT_START)
- /**-----------------------------------------------------------------
- * 局部描述符表: LDT
- *-------------------------------*/
- LDT_START:
- /* 段基址 段界限 属性*/
- Descriptor_LDT_CODEA: Descriptor(0, CodeALen - 1, DA_C + DA_32)# Code, 32 位
- .set LDTLen,(. - LDT_START)
- #LDT 选择子
- .set SelectorLDTCodeA,(0x0 + SA_TIL)
- /* 32-bit ring 3 stack segment. */
- .align 4
- LABEL_STACKR3:
- .space 10, 0
- .set TopOfStackR3, (. - LABEL_STACKR3)
- msg:
- .string "Hello world!"
- code:
- mov %cs,%ax
- mov %ax,%ds
- mov %ax,%es
- mov %ax,%ss
- mov $0x8000,%sp
- /*显示HelloWorld字符串*/
- mov $msg ,%ax
- mov %ax ,%bp
- mov $12 ,%cx
- mov $0x1301,%ax
- mov $0x000c,%bx
- mov $0 ,%dl
- int $0x10
- /*初始全局描述符Descriptor_CODE32*/
- InitDescrptor(Descriptor_CODE32,LABEL_SEG_CODE32);
- /*初始全局描述符Descriptor_LDT*/
- InitDescrptor(Descriptor_LDT,LDT_START);
- /*初始全局描述符Descriptor_CODE_GATE*/
- InitDescrptor(Descriptor_CODE_GATE,LABEL_CODE_G);
- /*初始全局描述符LABEL_STACKR3 */
- InitDescrptor(LABEL_STACKR3,Descriptor_STACKR3);
- /*初始LDT 中的描述符Descriptor_LDT_CODEA*/
- InitDescrptor(Descriptor_LDT_CODEA,LABEL_CODE_A);
- /*初始全局描述符Descriptor_CODE3*/
- InitDescrptor(Descriptor_CODE3,LABEL_CODE_3);
- /*加载gdtr即将全局描述符表gdt的首地址和gdt的界限赋给gdtr寄存器*/
- lgdt GdtPtr
- /*关中断*/
- cli
- /*打开地址线A20*/
- inb $0x92,%al
- or $0x02,%al
- outb %al,$0x92
- /*设置cr0寄存器,切换到保护模式*/
- movl %cr0,%eax
- or $1,%eax
- movl %eax,%cr0
- /*真正进入保护模式,执行此命令后CS=0x8,IP=0*/
- ljmp $selector_Code32,$0
- LABEL_SEG_CODE32:
- .align 32
- .code32
- movw $Selector_Video,%ax
- movw %ax,%gs/* 视频段选择子(目的)*/
- movl $((80*11+79)*2),%edi/*第11行,79列*/
- movb $0x0c,%ah/*高四位表示黑底,低四位表示红字*/
- movb $'P',%al/*显示的字符*/
- movw %ax,%gs:(%edi)
- #ljmp $selector_CodeR3,$0
- pushl $SelectorStackR3
- pushl $TopOfStackR3
- pushl $selector_CodeR3
- pushl $0
- lret
- lcall $Selector_Gate_Call,$0
- #ljmp $Selector_Code_Gate,$0
- movw $SelectorLDT,%ax
- lldt %ax/*加载lgtr*/
- ljmp $SelectorLDTCodeA,$0 /* 跳入局部任务 LABEL_CODE_A*/
- jmp .
- LABEL_CODE_G:
- movl $((80*12+0)*2),%edi/*第10行,0列*/
- movb $0x0c,%ah/*高四位0000表示黑底,低四位1100表示红字*/
- movb $'G',%al/*要显示的字符*/
- movw %ax,%gs:(%edi)
- lret
- .set CodeGLen,(. - LABEL_CODE_G)
- LABEL_CODE_A:
- movw $0x10,%ax
- movw %ax,%gs/* 视频段选择子(目的)*/
- movl $((80*12+10)*2),%edi/*第10行,0列*/
- movb $0x0c,%ah/*高四位0000表示黑底,低四位1100表示红字*/
- movb $'A',%al/*要显示的字符*/
- movw %ax,%gs:(%edi)
- jmp .
- .set CodeALen,(. - LABEL_CODE_A)
- LABEL_CODE_3:
- movw $0x10,%ax
- movw %ax,%gs/* 视频段选择子(目的)*/
- movl $((80*12+10)*2),%edi/*第10行,0列*/
- movb $0x0c,%ah/*高四位0000表示黑底,低四位1100表示红字*/
- movb $'3',%al/*要显示的字符*/
- movw %ax,%gs:(%edi)
- jmp .
- .set Code3Len,(. - LABEL_CODE_3)
- .org 0x1fe, 0x90
- .word 0xaa55
具体改动点如下:
DA_DRWA=0x93
SA_RPL3 = 3
DA_DPL3=0x60
我们将的描述符Descriptor_VIDEO设为DA_DPL3,添加描述符Descriptor_CODE3和Descriptor_STACKR3
Descriptor_VIDEO: Descriptor(0xb8000,0x0ffff,DA_DRW + DA_DPL3)
Descriptor_CODE3:Descriptor(0x0,0xffffffff,DA_C+DA_32+DA_DPL3)
Descriptor_STACKR3:Descriptor(0,TopOfStackR3, (DA_DRWA + DA_32 + DA_DPL3))
添加选择子selector_CodeR3和SelectorStackR3
.set selector_CodeR3,(Descriptor_CODE3-GDT_START+SA_RPL3)
.set SelectorStackR3,(Descriptor_STACKR3- GDT_START + SA_RPL3)
ring3下的堆栈
.align 4
LABEL_STACKR3:
.space 10, 0
.set TopOfStackR3, (. - LABEL_STACKR3)
/*初始全局描述符LABEL_STACKR3 */
InitDescrptor(LABEL_STACKR3,Descriptor_STACKR3);
/*初始全局描述符Descriptor_CODE3*/
InitDescrptor(Descriptor_CODE3,LABEL_CODE_3);
在ring3下打印'3'
LABEL_CODE_3:
movw $0x10,%ax
movw %ax,%gs/* 视频段选择子(目的)*/
movl $((80*12+10)*2),%edi/*第10行,0列*/
movb $0x0c,%ah/*高四位0000表示黑底,低四位1100表示红字*/
movb $'3',%al/*要显示的字符*/
movw %ax,%gs:(%edi)
jmp .
.set Code3Len,(. - LABEL_CODE_3)
实现从ring0到ring3的跳转
pushl $SelectorStackR3 //ss入栈
pushl $TopOfStackR3 //esp入栈
pushl $selector_CodeR3//cs入栈
pushl $0 //ip入栈
lret //将栈中的内容弹出