进程都是工作的特权级ring3下的,如何跳到ring3呢?我们用iret指令,不过在这之前我们要准备好ring3的堆栈,设置ring3的代码段,为了能在ring3模式下能够打印我们还要修改视频段,还记得前面《ring0到ring3》是如何做的吗?我们先写一个函数_set_gdt_desc来修改gdt,代码非常的简单,无非是设置gdt中的描述符。
typedef unsigned int u32; typedef unsigned short u16; typedef unsigned char u8; typedef struct Descriptor /* ???8 ??a?-?è??*/ { u16 limit_low; /* Limit */ u16 base_low; /* Base */ u8 base_mid; /* Base */ u8 attr1; /* P(1) DPL(2) DT(1) TYPE(4) */ u8 limit_high_attr2; /* G(1) D(1) 0(1) AVL(1) LimitHigh(4) */ u8 base_high; /* Base */ }DESCRIPTOR; void _set_gdt_desc(struct desc_struct *descriptor_addr,u32 base,u32 limit,u16 attr) { DESCRIPTOR *descriptor = (DESCRIPTOR *)descriptor_addr; descriptor->limit_low = limit & 0x0FFFF; descriptor->base_low = base & 0x0FFFF; descriptor->base_mid = (base >> 16) & 0x0FF; descriptor->attr1 = attr & 0xFF; descriptor->limit_high_attr2= ((limit>>16) & 0x0F) | (attr>>8) & 0xF0; descriptor->base_high = (base >> 24) & 0x0FF; }
然后分别设置视频段、堆栈段、代码段
#define DA_C 0x98 //存在的只执行代码段属性值
#define DA_32 0x4000 //32 位段
#define DA_DPL3 0x60 //DPL = 3
#define DA_DPL0 0x00 //DPL = 0
#define DA_DRWA 0x93 //存在的已访问可读写数据段类型
#define DA_DRW 0x92 //存在的可读写数据段属性值
#define SA_RPL3 3 //RPL(Requested Privilege Level)=3: 请求特权级,用于特权检查。
//视频段,选择子为0x10 _set_gdt_desc(&gdt[2],0xb8000,0x0ffff,DA_DRW+ DA_DPL3); //堆栈段,选择子为0x28 _set_gdt_desc(&gdt[5],0,0xffffffff,DA_DRWA + DA_32 + DA_DPL3); //代码段,选择子为0x30 _set_gdt_desc(&gdt[6],0,0xffffffff,DA_C+DA_32+DA_DPL3);
写一个简单的进程,不停的打印A
void delay(int time) { int i, j, k; for (k = 0; k < time; k++) { for (i = 0; i < 10; i++) { for (j = 0; j < 10000; j++) {} } } } void testA(void) { set_registers(); while(1){ disp_str("A"); delay(2); } }
下面我们该实现跳转了
#define move_to_user_mode() \ __asm__ ("movl %%esp,%%eax\n\t" \ "pushl $0x2b\n\t" \ "pushl %%eax\n\t" \ "pushfl\n\t"\ "pushl $0x33\n\t" \ "pushl $testA\n\t" \ "iret\n" \ :::"ax")
第2行将寄存器esp的值放到eax中
第3行设置ss,即堆栈段选择子0x28+3,3是SA_RPL3
第3行设置esp
第4行设置
第5行设置cs,即代码段选择子0x30+3,3是SA_RPL3
第5行设置eip
第6行iret实现跳转
#define move_to_user_mode() \
__asm__ ("movl %%esp,%%eax\n\t" \
"pushl $0x2b\n\t" \
"pushl %%eax\n\t" \
"pushfl\n\t"\
"pushl $0x33\n\t" \
"pushl $testA\n\t" \
"iret\n" \
:::"ax")