门描述符
环境
CPU : x86 32bit
内核版本:2.6.18
门描述符
在保护模式下,中断向量表中的表项由8个字节组成(两个字节的段描述符,偏移量四字节来表示),其中的每个表项叫做一个门描述符(gate
descriptor)。
“门”的含义是当中断发生时必须先通过这些门,然后才能进入相应的处理程序。
门描述符的一般格式如下:
门描述符类型
1.中断门(Interrupt gate)
其类型码为110,它包含一个中断或异常处理程序所在的段选择符和段内偏移。控制权通过中断门进入中断处理程序时,处理器清IF标志,即关中断,以避免嵌套中断的发生。
中断门中的DPL(Descriptor Privilege Level)为0,因此,用户态的进程不能访问Intel的中断门。所有的中断处理程序都由中断门激活,并全部限制在内核态。
设置中断门的代码如下:
//中断描述符表
struct desc_struct idt_table[256] __attribute__((__section__(".data.idt"))) = { {0, 0}, };
//描述符结构
struct desc_struct {
unsigned long a,b;
};
void set_intr_gate(unsigned int n, void *addr)
{
_set_gate(idt_table+n,14,0,addr,__KERNEL_CS);
}
2.陷阱门(Trap gate)
其类型码为111,与中断门类似,其唯一的区别是,控制权通过陷阱门进入处理程序时维持IF标志位不变,也就是说,不关中断。
其设置代码如下:
static void __init set_trap_gate(unsigned int n, void *addr)
{
_set_gate(idt_table+n,15,0,addr,__KERNEL_CS);
}
3.任务门(Task gate)
IDT中的任务门描述符格式与GDT和LDT中的任务门格式相同,含有一个任务TSS段的选择符,该任务用于处理异常或中断,Linux用于处理Double fault。
其设置代码如下:
static void __init set_task_gate(unsigned int n, unsigned int gdt_entry)
{
_set_gate(idt_table+n,5,0,0,(gdt_entry<<3));
}
4.系统门(System gate)
能够让用户态的进程访问Intel的陷阱门,门描述符的DPL为3。通过系统门来激活3个Linux异常处理程序,它们的向量是4、5及0x80,也就是说,在用户态下,可以使用into、bound
及int0x80三条汇编指令。
static void __init set_system_gate(unsigned int n, void *addr)
{
_set_gate(idt_table+n,15,3,addr,__KERNEL_CS);
}
5.系统中断门 (System intterupt gate)
能够被用户态进程访问的Intel中断门,门的DPL为3.与向量3相关的异常处理程序是由系统中断门激活的,因此,在用户态可以使用汇编语言指令int3.
static inline void set_system_intr_gate(unsigned int n, void *addr)
{
,..._set_gate(idt_table+n, 14, 3, addr, __KERNEL_CS);
}
设置门描述符
1.(seg<<16)段描述符放到eax寄存中.
2.((char *)(addr))段中偏移量(比如_system_call的地址)放到edx寄存中.
3.设置DPL和门类型码:(0x8000+(dpl<<13)+(type<<8))
4.(gate_addr)为中断描述符的低32位
5.(gate_addr+1)为中断描述符的高32位
#define _set_gate(gate_addr,type,dpl,addr,seg) \
do { \
int __d0, __d1; \
//把dx(edx的低16位)放到ax(eax的第16位中)
__asm__ __volatile__ ("movw %%dx,%%ax\n\t" \
//设置DPL和门类型码并放到dx(edx的低16位)
"movw %4,%%dx\n\t" \
//把eax的内容放到门描述符的低32位上
"movl %%eax,%0\n\t" \
//把edx的内容放到门描述符的高32位上
"movl %%edx,%1" \
:"=m" (*((long *) (gate_addr))), \
"=m" (*(1+(long *) (gate_addr))), "=&a" (__d0), "=&d" (__d1) \
:"i" ((short) (0x8000+(dpl<<13)+(type<<8))), \
"3" ((char *) (addr)),"2" ((seg) << 16)); \
} while (0)