LAB3_Part A User Environments and Exception Handling
前言
记录一下自己的学习过程
实验内容翻译:
https://gitee.com/cherrydance/mit6.828
该翻译仅供参考
练习3
练习3:如果你还没有阅读过,请阅读80386程序员手册中的第9章“异常和中断”(或者IA-32开发者手册中的第5章)。这些章节详细介绍了x86体系结构中的异常和中断机制。
自行阅读
练习4
请编辑trapentry.S和trap.c文件,并实现上述描述的功能。trapentry.S中的宏TRAPHANDLER和TRAPHANDLER_NOEC以及inc/trap.h中的T_*定义将对您有所帮助。您需要为inc/trap.h中定义的每个异常在trapentry.S中添加一个入口点(使用这些宏),并提供_alltraps函数,以供TRAPHANDLER宏引用。您还需要修改trap_init()函数,将IDT初始化为指向在trapentry.S中定义的每个入口点;在这里,SETGATE宏将很有帮助。
您的_alltraps函数应该执行以下操作:
推送值使堆栈看起来像一个struct Trapframe结构体
将GD_KD加载到%ds和%es寄存器
使用pushl %esp将struct Trapframe的指针作为参数传递给trap()
调用trap()函数(trap()函数是否会返回?)
考虑使用pushal指令;它与struct Trapframe的布局非常匹配
使用位于用户目录中的一些测试程序对您的异常处理代码进行测试,这些测试程序在进行任何系统调用之前会导致异常,例如user/divzero。此时,您应该能够成功通过make grade命令对divzero、softint和badsegment测试进行评分
/* TRAPHANDLER defines a globally-visible function for handling a trap.
* It pushes a trap number onto the stack, then jumps to _alltraps.
* Use TRAPHANDLER for traps where the CPU automatically pushes an error code.
*
* You shouldn't call a TRAPHANDLER function from C, but you may
* need to _declare_ one in C (for instance, to get a function pointer
* during IDT setup). You can declare the function with
* void NAME();
* where NAME is the argument passed to TRAPHANDLER.
*/
意思是它将陷阱编号推入栈中,然后跳转到_alltraps。结合 IA-32 Developer’s Manual中的表:
得到如下代码:
TRAPHANDLER_NOEC(t_divide, T_DIVIDE)
TRAPHANDLER_NOEC(t_debug, T_DEBUG)
TRAPHANDLER_NOEC(t_nmi, T_NMI)
TRAPHANDLER_NOEC(t_brkpt, T_BRKPT)
TRAPHANDLER_NOEC(t_oflow, T_OFLOW)
TRAPHANDLER_NOEC(t_bound, T_BOUND)
TRAPHANDLER_NOEC(t_illop, T_ILLOP)
TRAPHANDLER_NOEC(t_device, T_DEVICE)
TRAPHANDLER(t_dblflt, T_DBLFLT)
TRAPHANDLER(t_tss, T_TSS)
TRAPHANDLER(t_segnp, T_SEGNP)
TRAPHANDLER(t_stack, T_STACK)
TRAPHANDLER(t_gpflt, T_GPFLT)
TRAPHANDLER(t_pgflt, T_PGFLT)
TRAPHANDLER_NOEC(t_fperr, T_FPERR)
TRAPHANDLER(t_align, T_ALIGN)
接下来是_alltraps函数,这个函数我只能说不会写,虽然练习将要做的步骤都写出来了,但是真不会。。。。下面是抄的代码,尽力解释一下。
_alltraps:
/*首先是顺序,结构体中数据从上到下是低地址到高地址,栈是高地址到低地址的,
所以要反过来进行压栈。又结构体注释说明了最下面的是内核,中间是硬件,最上面的才是
我们需要压入栈中的。在调用函数前已经把错误码压入栈了。所以这里是把ds es寄存器压入栈
而第一个参数是struct PushRegs给出注释是可以使用pusha来入栈。
*/
pushl %ds
pushl %es
pushal
/*将GD_KD加载到%ds和%es寄存器*/
pushl $GD_KD
popl %ds
pushl $GD_KD
popl %es
/*压入trap()的参数tf,%esp指向Trapframe结构的起始地址*/
pushl %esp
call trap
解下来是完成trap_init函数,在练习中提到了宏SETGATE会很有作用,所以我们看一下这个宏:
#define SETGATE(gate, istrap, sel, off, dpl) \
{ \
(gate).gd_off_15_0 = (uint32_t) (off) & 0xffff; \
(gate).gd_sel = (sel); \
(gate).gd_args = 0; \
(gate).gd_rsv1 = 0; \
(gate).gd_type = (istrap) ? STS_TG32 : STS_IG32; \
(gate).gd_s = 0; \
(gate).gd_dpl = (dpl); \
(gate).gd_p = 1; \
(gate).gd_off_31_16 = (uint32_t) (off) >> 16; \
}
他的作用是设置一个普通的中断或者陷阱描述符。trap_init就是使用该宏来初始化。
所以需要先得到几个参数的含义
gate:门描述符
istrap: 一个布尔值,表示门描述符的类型。
如果为1,表示门描述符是一个陷阱门(Trap Gate);
如果为0,表示门描述符是一个中断门(Interrupt Gate)。
sel:该选择子指向中断处理程序所在的代码段。
off: 一个32位的偏移量,表示中断处理程序入口地址相对于代码段起始地址的偏移量。
dpl: 一个表示特权级别(Descriptor Privilege Level)的值。它指定了中断处理程序的特权级别。0表示最高特权级别,3表示最低特权级别。
因为第四个参数是指向中断处理程序,因此我们需要先声明这些函数:
void t_divide();
void t_debug();
void t_nmi();
void t_brkpt();
void t_oflow();
void t_bound();
void t_illop();
void t_device();
void t_dblflt();
void t_tss();
void t_segnp();
void t_stack();
void t_gpflt();
void t_pgflt();
void t_fperr();
void t_align();
在trap_init函数中:
SETGATE(idt[T_DIVIDE], 0, GD_KT, t_divide, 0);
SETGATE(idt[T_DEBUG], 0, GD_KT, t_debug, 0);
SETGATE(idt[T_NMI], 0, GD_KT, t_nmi, 0);
SETGATE(idt[T_BRKPT], 0, GD_KT, t_brkpt, 3);
SETGATE(idt[T_OFLOW], 0, GD_KT, t_oflow, 0);
SETGATE(idt[T_BOUND], 0, GD_KT, t_bound, 0);
SETGATE(idt[T_ILLOP], 0, GD_KT, t_illop, 0);
SETGATE(idt[T_DEVICE], 0, GD_KT, t_device, 0);
SETGATE(idt[T_DBLFLT], 0, GD_KT, t_dblflt, 0);
SETGATE(idt[T_TSS], 0, GD_KT, t_tss, 0);
SETGATE(idt[T_SEGNP], 0, GD_KT, t_segnp, 0);
SETGATE(idt[T_STACK], 0, GD_KT, t_stack, 0);
SETGATE(idt[T_GPFLT], 0, GD_KT, t_gpflt, 0);
SETGATE(idt[T_PGFLT], 0, GD_KT, t_pgflt, 0);
SETGATE(idt[T_FPERR], 0, GD_KT, t_fperr, 0);
SETGATE(idt[T_ALIGN], 0, GD_KT, t_align, 0);
运行结果:
每个异常/中断都有一个单独的处理程序函数的目的是什么?(即,如果所有异常/中断都传递到同一个处理程序,当前实现中存在的哪个功能无法提供?)
因为不同的中断需要使用不同的处理方式。
您是否需要采取任何措施使user/softint程序正确运行?grade脚本希望它产生一个通用保护错误(trap 13),但softint的代码中是int $14。为什么这会产生中断向量13?如果内核实际上允许softint的int $14指令调用内核的页面故障处理程序(中断向量14),会发生什么?
因为程序是运行在用户态的,当前特权级为3,而int指令是系统指令,需要运行在内核态。内核态的特权级为0。因此会引发trap13 的generation protection(通用保护异常)。用户态代码越权。
总结
完成了lab3 的partA。对于中断有一定的了解,但是还很浅薄。
继续加油!!!!!