x86权限简介
x86芯片支持四种特权级:第0级权限最高,用于运行操作系统:第3级权限最低,用于运行应用程序;其它两级用于运行操作系统服务(可选)。
2,3等级用的少
权限检查时,会综合比较CPL、RPL、DPL的值。当不符合检查规则时,存储访问将出现异常。
CPL字段指定是按照什么特权级执行的,RPL是放在DS,SS这些其他的寄存器中,因为x86这个权限划分实在是太复杂了,所以我们将操作系统代直接设定在Level 0这个特权级,然后建两个进程,这两个进程运行在特权级3这个模式下
系统上电,到进入到保护模式,到初始化之后,整个系统默认是运行在Level 0这个地方,怎么实现0特权级切换到进程的特权级3
因为内核代码段和内核数据段都是运行在特权级0,所以我们需要重新建立一个特权级为3的数据段和代码段
//os.h
#define APP_CODE_SEG ((3 * 8) | 3) // 特权级3
#define APP_DATA_SEG ((4 * 8) | 3) // 特权级3
//os.c
struct {uint16_t limit_l, base_l, basehl_attr, base_limit;}gdt_table[256] __attribute__((aligned(8))) = {// 0x00cffa000000ffff - 从0地址开始(平坦模型),P存在,DPL=3,Type=非系统段,32位代码段,界限4G[APP_CODE_SEG/ 8] = {0xffff, 0x0000, 0xfa00, 0x00cf},// 0x00cff3000000ffff - 从0地址开始,P存在,DPL=3,Type=非系统段,数据段,界限4G,可读写[APP_DATA_SEG/ 8] = {0xffff, 0x0000, 0xf300, 0x00cf},
};
//Start.S
_start_32:
、、、、、、
// 下面模拟中断返回,从而实现从特权级0到特权级3的变化
// 中断发生时,会自动压入原SS, ESP, EFLAGS, CS, EIP到栈中
push $APP_DATA_SEG //SS
push $task0_dpl3_stack + 1024 // 特权级3时的栈,ESP
push $0 // 中断暂时关掉 0x202 // EFLAGS格式
push $APP_CODE_SEG // CPL=3时的选择子,CS
push $task_0_entry // eip
iret // 从中断返回,将切换至任务0,到了特权级3
、、、、、、
task_0_entry: //进程,需要运行在特权级3的位置// 进入任务0时,需要重设其数据段寄存器为特权级3的mov %ss, %axmov %ax, %dsmov %ax, %esjmp task_0 // 跳转到任务0运行
切换到进程0, 我们不能采用jmp直接跳过去, 这样改变不了特权级
假设我们是在特权级3下面运行这个task_0_entry,结果发生了中断,然后就会从特权级3的栈切换到特权级0的栈,并且特权级0栈里面保存特权级3发生中断时候的各种状态像SS,ESP等,然后进特权级0状态时候运行的中断处理程序
当执行iret的时候,CPU会执行一些出栈的操作,将SS,ESP,EFLAGS等弹回到对应的寄存器中去(回到特权级3模式),那么我们关注一下这个EIP寄存器,我们可以将EIP寄存器的值设置为task0这个函数的地址,CS设置为task0代码段对应的寄存器,EFLAGS也可以对应的设置,ESP设置为task0当前的栈指针