Linux中国 Linux中国门户站!
设为主页 设为主页
收藏本站 收藏本站
 
当前位置 :首页 ->Linux技术 ->系统管理 ->正文

基于i386的Linux实现特点剖析——关于中断

来源:Linux-cn.com 作者:Webmaster 时间:2007-05-05 点击: [收藏] [投稿]

三、Linux下中断管理的实现

1.中断向量表IDT的初始化

Linux内核在初始化阶段完成了对页式虚拟管理的初始化以后,便调用trap_init()和init_IRQ()两个函数进行中断机制的初始化。其中trap_init()主要是对一些系统保留的中断向量的初始化,而init_IRQ()则主要是用于外设的中断。


  void _init trap_init(void)
  {
   #ifdef CONFIG_EISA
      if (isa_readl(0x0FFFD9) == 
  'E+('I'<<8)+('S'<<16)+('A'<<24))
         EISA_bus = 1;
   #endif

    set_trap_gate(0,÷_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_trap_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);

    /*
     * default LDT is a single_entry callgate to lcall7 for iBCS
     * and a callgate to lcall27 for Solaris/x86 binaries
     */
    set_call_gate(&default_ldt[0],lcall7);
    set_call_gate(&default_ldt[4],lcall27);

    /*
   _set_gate(a,12,3,addr);
   //12 即1100b,类型码为100,即调用门
  }

这些函数都调用同一个子程序_set_gate(),第一个参数用以设置中断描述符表idt_table中的第n项,第二个参数对应于门格式中的D位加类型位段,第三个参数是对应的DPL位段。


   #define _set_gate(gate_addr,type,dpl,addr) 
   do {
      int _d0,_d1;
      _asm_ _volatile_("movw %%dx,%%ax
	")
      "movw %4,%%dx
	" 
      "mov1 %%eax,%0
	" 
      "mov1 %%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" (_KERNEL_CS << 16)); 
  }while(0)
 

在第一个“:”到第二个“:”之间为输出部,有四个约束输出,将有四个变量会被改变,分别为%0、%1、%2和%3相结合。其中%0和%1都是内存单元,分别和gate_addr、gate_addr+1结合,%2于局部变量_d0结合,存放在寄存器%%eax中;%3于局部变量_d1结合,存放在寄存器%%edx中。

第二个“:”之后的部分是输入部,输出部已经定义了%0-%3,输入部中的第一个变量为%4,而紧接着的第二、第三个变量分别等价于输出部的%3和%2。输入部中说明的个输入变量地值,包括%3和%2,都会在引用这些变量之前设置好。在执行指令部的代码之前,会先将%%eax设成(_KERNEL_CS<<16),把%%edx设为addr,%4变量设置为(0x8000+(dpl<<13)+type<<8),则%4变量对应于门结构中的第32-47位,其P位为1。

有表格

指令部第一条指令“movw %%dx,%%ax”,将%%dx的低16位移入%%ax的低16位,这样,在%%eax中,其高16位为_KERNEL_CS,而低16位为addr的低16位,形成了门结构中的第0-31位。

第二条指令“movx %4 ,%%dx”,将%4放入%%dx中,也就是将门结构中的第32-47位放在%%dx中,而对于%%edx而言,就对应着门结构中的高32位。

第三条指令“mov1 %%eax,%0”,将%%edx写入变量%0中,即*gate_addr。

第四条指令“mov1 %%eax,%1”将%%edx写入变量%1中,即*(gate_addr+1)。

将第三、第四条指令和起来看,就是将整个门结构的内容都写道*gate_addr中去了。


  void _inti init_IRQ(void)
  {
    int i;
  #ifndef CONFIG_X86_VISWS-APIC
     init_ISA_irqs();
  #else
     init_VISWS_APIC_irqs();
  #endif
     /*
      * Cover the whole vector space,no vector can escape
      * us. (some of these will be overridden and become
      * 'special' SMP interrupts)
      */
     for (i=0;i< NR_IRQS;i++) {
         int vector = FIRST_EXTERNAL_VECTOR + i;
         if (vector != SYSCALL_VECTOR)
             set_intr_gate(vector,interrupt[i]);
     }
    
  #ifdef CONFIG_SMP
     /*
      * IRQ0 must ve given a fixed assignment and initialized,
      * because it's used before the I0-APIC is set up.
      */
     set_intr-gate(FIRST_DEVICE_VECTOR,interrupt[0]);
     
     /*
      * The reschedule interrupt is a CPU-to-CPU reschedule-helper
      * IPI,driven by wakeup.
      */
     set_intr_gate(RESCHEDULE_VECTOR,reschedule_interrupt);
     
     /* IPI for generic function call */
     set_intr_gate(CALL_FUCTION_VECTOR,call_funtion_interrupt);
  #endif

  #ifdef CONFIG_X86_LOCAL_APIC
     /* IPI vectors for APIC spurious and error interrupts */
     set_intr_gate(SPURIOUS_APIC_VECTOR,spurious_interrupt);
     set_intr_gate(ERROR_APIC_VECTOR,error_interrupt);
  #endif

     /*
      * Set the clock to HZ Hz, we already have a valid
      * vector now;
      */
    outb_p(0x34,0x43);     /* binary, mode 2, LSB/MSB ,ch 0*/
    outb_p(LATCH & 0xff,0x40);    /* LSB */
    outb(LATCH >> 8, 0x40);   /* MSB */

  #ifndef CONFIG_VISWS
     setup_irq(2, &irq2)
  #endif
    
    /* 
     * External FPU? Set up irq13 if so,for
     * original braindamaged IBM FERR coupling.
     */
    if (boot_cpu_data.hard_math && !cpu_has_fpu)
        setup_irq(13,&irq13);


 如果您对本文有任何疑问或者建议,请到讨论区发表您的意见: >> 论坛入口 <<



上一篇:基于i386的Linux实现特点剖析——基础的基础   下一篇:基于i386体系结构的Linux实现特点剖析——内存与进程

文章评论】 【收藏本文】 【推荐好友】 【打印本文】 【我要投稿】 【论坛讨论
更多相关文章