thx, 问题找到, 是由于idt描述符里的ss没有设置对, _alltraps 中也要把ds设置成内核态的数据段. 总结如下:
iret指令
* 实模式
从栈中弹出eip, cs和eflags
* 保护模式
NT==0, 从中断返回不会发生任务切换, 返回后的代码权限必须小于或等于当前代码
(CS的最低两位). 如果目标代码的权限更低, 则会从栈上再弹出esp和ss.
NT==1, iret会使得程序从call或int引起的任务切换中返回回来, 执行iret引起的更
新状态保存在任务的tss中.
- kernel -> hello, 启动第一个用户任务
每个进程(env)在自己的Env结构里都有一个Trapframe, 并在env_alloc和load_icode
里初始化这个Env.Trapframe结构, 最终将其传入 env_pop_tf中, 此时Trapframe如下:
{{{
struct Trapframe
+-------------+
|----- ss | void env_pop_tf(struct Trapframe *tf)
+-------------+ {
| esp | __asm __volatile("movl %0,%%esp\n"
+-------------+ "\tpopal\n"
| eflags | "\tpopl %%es\n"
+-------------+ "\tpopl %%ds\n"
|---- cs | "\taddl $0x8,%%esp\n" /* skip tf_trapno and tf_errcode */
+-------------+ "\tiret"
| eip | : : "g" (tf) : "memory");
+-------------+ panic("iret failed"); /* mostly to placate the compiler */
| err | }
+-------------+ movl %0, %esp就把栈指针esp指向了Trapframe的最低地址, 接着弹出
| trapno | 各寄存器. 在iret执行时, esp指向Trapframe.eip; iret命令弹出
+-------------+ eip,cs,eflags,esp,ss. 此时切换到了用户层代码的cs和栈中.
|---- ds |
+-------------+
|---- es |
+-------------+
| ~~~regs~~~ |
+-------------+ <- esp
}}}
- hello -> kernel, 系统调用trap进入内核态
执行int 0x30命令后, cpu从idt的第0x30条目里取得信息. 该条目里内容:
{{{
+-----------------------------------------+
| vector[31...16] |P|DPL| type | |
+-----------------------------------------+
| ss | vector[15...0] |
+-----------------------------------------+
}}}
cpu读取其中的段选择符ss到cs中, 然后再读取vector, 并跳到cs:vector地址开始执行
如此, 便跳到了trapentry.S中的vector48上. 用DPL里的值作为新的特权级, 由于DPL
小于CPL, 特权级发生切换, 引起堆栈切换. 从tr寄存器指定的tss里读取ss0和esp0作
为新的栈, 并将旧的ss,esp,eflags,cs,eip等压入新栈(返回的时候要用到). 这里已经
开在新栈中构建一个新的Trapframe了.
接着跳到trapentry.S中的vector48继续构建Trapframe, 构建完成之后 call trap跳入
处理阶段.
int 0x30执行时, 有了新的cs, 在trapentry.S中还要新的ds, 它们分别指向内核态的
代码段和数据段.