设定IDT表以及中断处理函数编写

设定IDT表以及中断处理函数编写

IDT本质是在kernel中的一个数组,是中断向量id到中断描述符(也即中断服务程序起始地址)的映射。 我画了一个图,图解IDT处理流程。

其中trap_dispatch()分配以后就是中断的下半部。


TRAPHANDLER_NOEC(__idt_divide,T_DIVIDE)
TRAPHANDLER_NOEC(__idt_debug,T_DEBUG)
TRAPHANDLER_NOEC(__idt_nmi,T_NMI)
TRAPHANDLER_NOEC(__idt_breakpoint,T_BRKPT)
TRAPHANDLER_NOEC(__idt_overflow,T_OFLOW)
TRAPHANDLER_NOEC(__idt_bound,T_BOUND)
TRAPHANDLER_NOEC(__idt_illop,T_ILLOP)
TRAPHANDLER_NOEC(__idt_device,T_DEVICE)
TRAPHANDLER(__idt_dblflt,T_DBLFLT)
TRAPHANDLER(__idt_tss,T_TSS)
TRAPHANDLER(__idt_segnp,T_SEGNP)
TRAPHANDLER(__idt_stack,T_STACK)
TRAPHANDLER(__idt_gpflt,T_GPFLT)
TRAPHANDLER(__idt_pgflt,T_PGFLT)
TRAPHANDLER_NOEC(__idt_fperr,T_FPERR)
TRAPHANDLER(__idt_align,T_ALIGN)
TRAPHANDLER_NOEC(__idt_mchk,T_MCHK)//MCE
TRAPHANDLER_NOEC(__idt_simd,T_SIMDERR)
 
TRAPHANDLER_NOEC(__idt_syscall,T_SYSCALL)
TRAPHANDLER(__idt_default,T_DEFAULT)
 
TRAPHANDLER_NOEC(__idt_irq0,IRQ_OFFSET+ IRQ_TIMER)
 
TRAPHANDLER_NOEC(__idt_irq1,IRQ_OFFSET+ IRQ_KBD)
TRAPHANDLER_NOEC(__idt_irq2,IRQ_OFFSET+ 2)
TRAPHANDLER_NOEC(__idt_irq3,IRQ_OFFSET+ 3)
TRAPHANDLER_NOEC(__idt_irq4,IRQ_OFFSET+ IRQ_SERIAL)
TRAPHANDLER_NOEC(__idt_irq5,IRQ_OFFSET+ 5)
TRAPHANDLER_NOEC(__idt_irq6,IRQ_OFFSET+ 6)
TRAPHANDLER_NOEC(__idt_irq7,IRQ_OFFSET+ IRQ_SPURIOUS)
TRAPHANDLER_NOEC(__idt_irq8,IRQ_OFFSET+ 8)
TRAPHANDLER_NOEC(__idt_irq9,IRQ_OFFSET+ 9)
TRAPHANDLER_NOEC(__idt_irq10,IRQ_OFFSET+ 10)
TRAPHANDLER_NOEC(__idt_irq11,IRQ_OFFSET+ 11)
TRAPHANDLER_NOEC(__idt_irq12,IRQ_OFFSET+ 12)
TRAPHANDLER_NOEC(__idt_irq13,IRQ_OFFSET+ 13)
TRAPHANDLER_NOEC(__idt_irq14,IRQ_OFFSET+ IRQ_IDE)
TRAPHANDLER_NOEC(__idt_irq19,IRQ_OFFSET+ IRQ_ERROR)
 
/*
 * Lab 3: Your code here for _alltraps
 */
_alltraps:
    pushl %ds
    pushl %es
    pushal
    movw  $GD_KD, %ax
    movw  %ax,%ds
    movw  %ax,%es
    pushl %esp
    call trap
 
void
trap_init(void)
{
    extern struct Segdesc gdt[];
 
    // LAB 3: Your code here.
 
    extern void __idt_divide();
    extern void __idt_debug();
    extern void __idt_nmi();
    extern void __idt_breakpoint();
    extern void __idt_overflow();
    extern void __idt_bound();
    extern void __idt_illop();
    extern void __idt_device();
    extern void __idt_dblflt();//double fault
    extern void __idt_tss();
    extern void __idt_segnp();
    extern void __idt_stack();
    extern void __idt_gpflt();
    extern void __idt_pgflt();
    extern void __idt_fperr();
    extern void __idt_align();
    extern void __idt_mchk();
    extern void __idt_simd();
    extern void __idt_syscall();
    extern void __idt_default();
 
    extern void __idt_irq0();
    extern void __idt_irq1();
    extern void __idt_irq2();
    extern void __idt_irq3();
    extern void __idt_irq4();
    extern void __idt_irq5();
    extern void __idt_irq6();
    extern void __idt_irq7();
    extern void __idt_irq8();
    extern void __idt_irq9();
    extern void __idt_irq10();
    extern void __idt_irq11();
    extern void __idt_irq12();
    extern void __idt_irq13();
    extern void __idt_irq14();
 
    int i = 0;
    SETGATE(idt[0],0,GD_KT,__idt_default,0);
    for(i=1;i<255;i++)
    {
        idt[i] = idt[0];
    }
 
    SETGATE(idt[T_DIVIDE],1,GD_KT,__idt_divide,0);
    SETGATE(idt[T_DEBUG],0,GD_KT,__idt_debug,0);
    SETGATE(idt[T_NMI],0,GD_KT,__idt_nmi,0);
    SETGATE(idt[T_BRKPT],1,GD_KT,__idt_breakpoint,3);
    SETGATE(idt[T_OFLOW],1,GD_KT,__idt_overflow,0);
    SETGATE(idt[T_BOUND],1,GD_KT,__idt_bound,0);
    SETGATE(idt[T_ILLOP],1,GD_KT,__idt_illop,0);
    SETGATE(idt[T_DEVICE],1,GD_KT,__idt_device,0);
    SETGATE(idt[T_DBLFLT],1,GD_KT,__idt_dblflt,0);
    SETGATE(idt[T_TSS],1,GD_KT,__idt_tss,0);
    SETGATE(idt[T_SEGNP],1,GD_KT,__idt_segnp,0);
    SETGATE(idt[T_STACK],1,GD_KT,__idt_stack,0);
    SETGATE(idt[T_GPFLT],1,GD_KT,__idt_gpflt,0);
    SETGATE(idt[T_PGFLT],1,GD_KT,__idt_pgflt,0);
    SETGATE(idt[T_FPERR],1,GD_KT,__idt_fperr,0);
    SETGATE(idt[T_ALIGN],1,GD_KT,__idt_align,0);
    //SETGATE(idt[T_DIVIDE],1,GD_KT,__idt_divide,0);
 
    SETGATE(idt[T_MCHK],1,GD_KT,__idt_mchk,0);
    SETGATE(idt[T_SIMDERR],1,GD_KT,__idt_simd,0);
    SETGATE(idt[T_SYSCALL],0,GD_KT,__idt_syscall,3);
 
    SETGATE(idt[IRQ_OFFSET],0,GD_KT,__idt_irq0,0);
    SETGATE(idt[IRQ_OFFSET+1],0,GD_KT,__idt_irq1,0);
    SETGATE(idt[IRQ_OFFSET+2],0,GD_KT,__idt_irq2,0);
    SETGATE(idt[IRQ_OFFSET+3],0,GD_KT,__idt_irq3,0);
    SETGATE(idt[IRQ_OFFSET+4],0,GD_KT,__idt_irq4,0);
    SETGATE(idt[IRQ_OFFSET+5],0,GD_KT,__idt_irq5,0);
    SETGATE(idt[IRQ_OFFSET+6],0,GD_KT,__idt_irq6,0);
    SETGATE(idt[IRQ_OFFSET+7],0,GD_KT,__idt_irq7,0);
    SETGATE(idt[IRQ_OFFSET+8],0,GD_KT,__idt_irq8,0);
    SETGATE(idt[IRQ_OFFSET+9],0,GD_KT,__idt_irq9,0);
    SETGATE(idt[IRQ_OFFSET+10],0,GD_KT,__idt_irq10,0);
    SETGATE(idt[IRQ_OFFSET+11],0,GD_KT,__idt_irq11,0);
    SETGATE(idt[IRQ_OFFSET+12],0,GD_KT,__idt_irq12,0);
    SETGATE(idt[IRQ_OFFSET+13],0,GD_KT,__idt_irq13,0);
    SETGATE(idt[IRQ_OFFSET+14],0,GD_KT,__idt_irq14,0);
 
    // Per-CPU setup
    trap_init_percpu();
}

首先,我们利用TRAPHANDLER和TRAPHANDLER_NOEC宏来实现handler。 这两个宏所生成的代码的作用是,根据传入参数定义handler的“标签”,然后进行一些压栈操作。压什么东西?

中断发生后,CPU自动切换到一个新的栈,然后自动地将当前运行程序的EFLAGS寄存器压入栈中,然后压入CS和IP,然后对于一些特殊的异常CPU还会压入error code,然后才会转到中断服务程序的第一条指令——这第一条指令就是在handler中。

然后,handler就开始压栈了。为了保证Trapframe结构的一致性和完整性,对于CPU没有压入error code的情况,TRAPHANDLER_NOEC定义的handler首先压入一个0用于占位。 接着,TRAPHANDLER和TRAPHANDLER_NOEC都压入了trap number。

最后,两者都跳转到_alltraps,这是每个handler都一样的代码,它的作用依然是继续补充Trapframe结构,注意压栈顺序要和Trapframe结构中的顺序一致。我们稍后就会发现,经过上述一系列操作之后,一个完整的Trapframe结构诞生了,也就意味着保存现场的工作完成