中断分析(五)

中断分析(五)
    ——初始化中断描述符表的具体实现    
中断描述符表的初始化:将中断描述符表的起始地址装入IDTR,并初始化表中的每一项。具体过程如下图所示:
    
    
一、中断描述符表的预初始化:
用256个指向ignore_int中断门的入口地址填充中断描述符表。它不是真正的初始化idt,等到分页和内核跳转到PAGE_OFFSET处时才真正的进行初始化。确定所有相关的准备都已就绪之后,中断可以在任何地方发生。
setup_idt:
    lea ignore_int,%edx  //取ignore_int的有效地址到edx寄存器
    movl $(__KERNEL_CS << 16),%eax    #把内核代码段选择符左移16位,送到eax寄              存器,此时eax的高16位存放选择符。
    movw %dx,%ax        /* selector = 0x0010 = cs */ #ignore_int的有效地址存入eax的底16位。此时,eax中含有门描述符底4字节(32位)的值。
    movw $0x8E00,%dx    /* interrupt gate - dpl=0, present */  #edx中含有门描述符高4字                       节的值。

    lea SYMBOL_NAME(idt_table),%edi //取中断描述符表的地址到edx中
    mov $256,%ecx
rp_sidt:
    movl %eax,(%edi)    #//将通用的中断描述符存入表中(将ignore_int存入edi所指向的地址中)
    movl %edx,4(%edi)    #eax内容放到edi+4所指内存位置处
    addl $8,%edi          #edi指向表中下一项 
    dec %ecx
    jne rp_sidt         #条件跳转,使得idt表有256项
    Ret
Ignore_int()中断处理程序,可以看作是一个空的处理程序,它执行的主要动作有:
1、在栈中保存一些寄存器的内容。
2、调用printk()函数打印“Unknown interrupt”系统消息。
3、恢复栈中寄存器的内容。
4、执行iret指令,恢复被中断的程序。
ignore_int:   
    cld
    pushl %eax
    pushl %ecx
    pushl %edx
    pushl %es
    pushl %ds
    movl $(__KERNEL_DS),%eax
    movl %eax,%ds
    movl %eax,%es
    pushl $int_msg
    call SYMBOL_NAME(printk)
    popl %eax
    popl %ds
    popl %es
    popl %edx
    popl %ecx
    popl %eax
    iret
二、中断描述符表的第二遍初始化
在上述预初始化之后后,内核将在IDT中进行第二遍初始化,用有意义的陷阱和中断处理程序替换空处理程序。第二遍处理过程完成后,针对控制单元产生的每个不同的异常,IDT都有一个专门的陷阱门或系统门;而对于可编程控制器确认的每一个IRQ,IDT都将包含一个专门的中断门。Trap_init()函数的工作就是将一些处理异常的函数插入到IDT的非屏蔽中断及异常表项中。
由于内核中只用到了陷阱门和系统门,因此只考虑对这两个门的初始化。Trap_init()函数用于设置中断描述符表开头的19个陷阱门和Linux系统中唯一用到的一个系统门。这些中断向量都是CPU保留,用于异常处理的。

void __init trap_init(void)
{   
    set_trap_gate(0,&divide_error);
    set_trap_gate(1,&debug);
    set_intr_gate(2,&nmi);
    set_system_gate(3,&int3);    /* int3-5 can be called from all */
    set_system_gate(4,&overflow);
    set_system_gate(5,&bounds);
    set_trap_gate(6,&invalid_op);
    set_trap_gate(7,&device_not_available);
    set_trap_gate(8,&double_fault);
    set_trap_gate(9,&coprocessor_segment_overrun);
    set_trap_gate(10,&invalid_TSS);
    set_trap_gate(11,&segment_not_present);
    set_trap_gate(12,&stack_segment);
    set_trap_gate(13,&general_protection);
    set_intr_gate(14,&page_fault);
    set_trap_gate(15,&spurious_interrupt_bug);
    set_trap_gate(16,&coprocessor_error);
    set_trap_gate(17,&alignment_check);
    set_trap_gate(18,&machine_check);
    set_trap_gate(19,&simd_coprocessor_error);

    set_system_gate(SYSCALL_VECTOR,&system_call);
        }
我们可以看到将异常处理函数插入IDT的表项是由set_trap_gate()和set_system_gate()函数来完成的。具体实现如下:
设置中断门函数。其中,参数n表示中断号;addr是中断程序偏移地址(相对于内核代码或数le据段来说的)。
void set_intr_gate(unsigned int n, void *addr)
{
    _set_gate(idt_table+n,14,0,addr);//idt_table是中断描述符表的起始地址。Idt_table+n是中断描述符表中中断号n对应项的偏移值。中断描述符的类型是14,特权级是0.
}

设置陷阱门函数。
static void __init set_trap_gate(unsigned int n, void *addr)
{
     _set_gate(idt_table+n,15,0,addr);    //陷阱门描述符的类型是15,特权级是0.
}
设置系统陷阱门函数。与陷阱门不同,系统陷阱门的特权级是3,即系统陷阱门设置的中断处理过程能够被所有进程调用(如单步调试、溢出出错和超出边界出错等)
static void __init set_system_gate(unsigned int n, void *addr)
{
    _set_gate(idt_table+n,15,3,addr);//系统陷阱门描述符的类型是15,特权级是0.
}

上述门函数都是调用_set_gate宏来实现的。该宏的具体实现如下:
   其中,参数的含义为:gate_addr—门描述符地址;type—门描述符类型域值;dpl—门的特权级;addr—偏移地址。
#define _set_gate(gate_addr,type,dpl,addr) \
do { \
  int __d0, __d1; \    #定义两个整型变量_d0,_d1;
  __asm__ __volatile__ ("movw %%dx,%%ax\n\t" \ #将偏移地址低字与选择符组合成描述符低4字节(eax);
    "movw %4,%%dx\n\t" \  #将类型标识字与偏移高字组合成描述符高4字节(edx);
    "movl %%eax,%0\n\t" \ #设置门描述符的低4字节
    "movl %%edx,%1" \    #设置门描述符的高4字节
    :"=m" (*((long *) (gate_addr))), \ #输出寄存器:%0(内存地址)指向描述符地址(偏        移值)
     "=m" (*(1+(long *) (gate_addr))), "=&a" (__d0), "=&d" (__d1) \#%1指向描述符地址强制转化为长整型加1字节后的地址处;%2(eax)输出到_d0;%3(edx)输出到_d1;
    :"i" ((short) (0x8000+(dpl<<13)+(type<<8))), \#输入寄存器:%4,dpl和type组合成类型标志字(门描述符中低2字节)
     "3" ((char *) (addr)),"2" (__KERNEL_CS << 16)); \#3表示与上面相同位置上的输出寄存器,(即%3,edx),2同理(eax),在这段代码开始运行时将__KERNEL_CS(内核代码段)左移16位放到eax寄存器中。
} while(0)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值