/* * IRET * 当使用IRET指令返回到相同保护级别的任务时,也就是当前的CS中的DPL和堆栈中的DPL相同时 * IRET会从堆栈弹出代码段选择子及指令指针分别到CS与IP寄存器, * 并弹出标志寄存器内容到EFLAGS寄存器。 * * 当使用IRET指令返回到一个不同的保护级别时,也就是当前的CS中的DPL和堆栈中的DPL不同时 * IRET不仅会从堆栈弹出以上内容, * 还会弹出堆栈段选择子及堆栈指针分别到SS与SP寄存器。 * * * 我们知道当前系统使用了特权模式0, * movl %%esp,%%eax, 将当前的栈顶指针存放进入eax * pushl $0x17,17的二进制为00010111,表示权限为3,索引为2的段选择子,为数据段描述符,在局部描述符中 * pushl %%eax, 将原有的esp入栈 * pushfl, 将eflags 入栈 * pushl $0x0f, 0xf的二进制为00001111, 表示权限为3,索引为1的段选择子 为代码段选择符,在局部描述符中 * pushl $1f, 将如下标号1处的地址入栈 * iret 由于弹出的CS(0x0f)当前的CS(0x08)因此依次弹出IP,CS, EFLAGS, SS, SP * 根据构造好的栈进行转化到用户空间 * 紧接着执行如下 * movl $0x17,%%eax,设置数据段的描述符,此时系统继续运行下一跳指令 * * 由此我们可以看出来,转到用户空间后使用了本地描述符表的代码段描述符和数据段描述符 * 堆栈仍然使用了内核态的堆栈,但是权限不一样,task0的用户空间栈使用了内核的task_stack * 任务0是一个特殊进程,它的数据段和代码段直接映射到内核代码和数据空间 * 即从物理地址0开始的640KB内存空间,其地址是内核代码使用的堆栈 * 本地描述表在sched_init函数中已经进行了初始化 * */ #define move_to_user_mode() \ __asm__ ("movl %%esp,%%eax\n\t" \ "pushl $0x17\n\t" \ "pushl %%eax\n\t" \ "pushfl\n\t" \ "pushl $0x0f\n\t" \ "pushl $1f\n\t" \ "iret\n" \ "1:\tmovl $0x17,%%eax\n\t" \ "movw %%ax,%%ds\n\t" \ "movw %%ax,%%es\n\t" \ "movw %%ax,%%fs\n\t" \ "movw %%ax,%%gs" \ :::"ax")