现在的位置: 首页 > 综合 > 正文

深入理解linux内核笔记三:异常和中断(2)

2013年08月28日 ⁄ 综合 ⁄ 共 3982字 ⁄ 字号 评论关闭

 

1             中断描述符表IDT的初始化

1.1       预初始化

中断描述表寄存器IDTR的初始化,代码在arch/i386/boot/setup.S

lidt    idt_48                   # load idt with 0,0

    idt_48:

         .word   0                  # idt limit = 0

         .word   0, 0                # idt base = 0L

IDT其实地址装入IDTR,代码在arch/i386/kernel/head.S

#define IDT_ENTRIES     256

.globl SYMBOL_NAME(idt)

 

lidt idt_descr

idt_descr:

        .word IDT_ENTRIES*8-1           # idt contains 256 entries

SYMBOL_NAME(idt):

         .long SYMBOL_NAME(idt_table)

setup_idt()函数填充idt_table表中的256个表项

先看idt_table定义,在arch/i386/kernel/traps.c

  struct desc_struct idt_table[256] __attribute__((__section__(".data.idt"))) = { {0, 0}, };

desc_struct定义为

  struct desc_struct

    unsigned long a,b   }

idt_table表进行填充,使用了一个空的中断处理函数ignore_int()Ignore_int()是一段汇编程序,在head.S

ignore_int:

        cld            #方向标志清0,表示串指令自动增长它们的索引寄存器(esiedi

        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

int_msg:

       .asciz "Unknown interrupt/n"

        ALIGN

该中断程序模拟一般中断处理程序,执行如下操作

 在栈中保存一些寄存器的值

 调用printk打印”Unknown interrupt”信息

 从栈中恢复寄存器的值

 执行iret以恢复被中断的程序

最后,看setup_idt()如何对idt进行填充

/*

  *  setup_idt

  *

  *  sets up a idt with 256 entries pointing to

  *  ignore_int, interrupt gates. It doesn't actually load

  *  idt - that can be done only after paging has been enabled

  *  and the kernel moved to PAGE_OFFSET. Interrupts

  *  are enabled elsewhere, when we can be relatively

  *  sure everything is ok.

  */

setup_idt:

         lea ignore_int,%edx   /*计算ignore_int地址的偏移量,并将其装入%edx*/

         movl $(__KERNEL_CS << 16),%eax  /* selector = 0x0010 = cs */

         movw %dx,%ax           

         movw $0x8E00,%dx        /* interrupt gate - dpl=0, present */

 

         lea SYMBOL_NAME(idt_table),%edi

         mov $256,%ecx

rp_sidt:

         movl %eax,(%edi)

         movl %edx,4(%edi)

         addl $8,%edi

         dec %ecx

         jne rp_sidt

         ret

主要就是循环填充256个表项。

 

1.2       最终初始化

IDT表项的设置是通过_set_gate()函数实现的。

#define _set_gate(gate_addr,type,dpl,addr) /

do { /

   int __d0, __d1; /

   __asm__ __volatile__ ("movw %%dx,%%ax/n/t" /

         "movw %4,%%dx/n/t" /

         "movl %%eax,%0/n/t" /

        "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" (__KERNEL_CS << 16)); /

} while (0)

插入中断门

void set_intr_gate(unsigned int n, void *addr)

{

         _set_gate(idt_table+n,14,0,addr ,__KERNEL_CS);

}

这个门的段选择符设置成代码段的选择符(__KERNEL_CS)DPL域设置成014表示D标志位为1而类型码为110,偏移域设置成中断处理程序的地址addr

 

插入陷阱门

static void __init set_trap_gate(unsigned int n, void *addr)

{

         _set_gate(idt_table+n,15,0,addr ,__KERNEL_CS);

}

这个门的段选择符设置成代码段的选择符,DPL域设置成015表示D标志位为1而类型码为111偏移域设置成异常处理程序的地址addr

插入系统门

static void __init set_system_gate(unsigned int n, void *addr)

{

         _set_gate(idt_table+n,15,3,addr,__KERNEL_CS);

}

这个门的段选择符设置成代码段的选择符,DPL域设置成315表示D标志位为1而类型码为111,所以set_system_gate()设置的也是陷阱门,但因为DPL3,因此,系统调用在用户空间可以通过“INT0X80”顺利穿过系统门,从而进入内核空间

 

对陷阱门和系统门的初始化

trap_init()就是设置中断描述符表开头的19个陷阱门,这些中断向量都是CPU保留用于异常处理的

   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);

 

对中断门的初始化

对中断门的设置由init_IRQ()中一段代码实现,代码在arch/i386/kernel/i8259.

抱歉!评论已关闭.