初始化中断描述符表

来源:互联网 发布:python ssh执行.sh 编辑:程序博客网 时间:2024/05/01 05:23

4.2.5 初始化中断描述符表

在完成了最重要和最核心的分段、分页环境的初始化后,还有一个就是初始化中断描述符idt的工作。

 

 341/*

 342 * Initialize eflags.  Some BIOS's leave bits like NT set.  This would

 343 * confuse the debugger if this code is traced.

 344 * XXX - best to initialize before switching to protected mode.

 345 */

 346        pushl $0

 347        popfl

 348

 349#ifdef CONFIG_SMP

 350        cmpb $0, ready

 351        jz  1f                          /* Initial CPU cleans BSS */

 352        jmp checkCPUtype

 3531:

 354#endif /* CONFIG_SMP */

 

346347行初始化CPUeflags寄存器,对popf指令不熟的人可以重新学学汇编了。继续走:

 

 355

 356/*

 357 * start system 32-bit setup. We need to re-do some of the things done

 358 * in 16-bit mode for the "real" operations.

 359 */

 360        call setup_idt

 361

 

360行,call setup_idt这里为每一个中断准备最早的处理函数,跳过checkCPUtype的若干行相关代码,我们来到502行:

 

490/*

 491 *  setup_idt

 492 *

 493 *  sets up a idt with 256 entries pointing to

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

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

 496 *  and the kernel moved to PAGE_OFFSET. Interrupts

 497 *  are enabled elsewhere, when we can be relatively

 498 *  sure everything is ok.

 499 *

 500 *  Warning: %esi is live across this function.

 501 */

 502setup_idt:

 503        lea ignore_int,%edx

 504        movl $(__KERNEL_CS << 16),%eax

 505        movw %dx,%ax            /* selector = 0x0010 = cs */

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

 

503行,获得ignore_int的地址,ignore_int575行定义:

 

575ignore_int:

 576        cld

 577#ifdef CONFIG_PRINTK

 578        pushl %eax

 579        pushl %ecx

 580        pushl %edx

 581        pushl %es

 582        pushl %ds

 583        movl $(__KERNEL_DS),%eax

 584        movl %eax,%ds

 585        movl %eax,%es

 586        cmpl $2,early_recursion_flag

 587        je hlt_loop

 588        incl early_recursion_flag

 589        pushl 16(%esp)

 590        pushl 24(%esp)

 591        pushl 32(%esp)

 592        pushl 40(%esp)

 593        pushl $int_msg

 594        call printk

 595

 596        call dump_stack

 597

 598        addl $(5*4),%esp

 599        popl %ds

 600        popl %es

 601        popl %edx

 602        popl %ecx

 603        popl %eax

 604#endif

 605        iret

 

以上575~605这么一堆代码就是ignore_int函数把函数的入口地址赋给edx寄存器。函数的内容我们不详细讲解了,这里只简单说说,就是在early_recursion_flag不为2的情况下,调用printk内核打印函数,打印int_msg信息:

666int_msg:

 667        .asciz "Unknown interrupt or fault at: %p %p %p/n"

 

回到setup_idt中,504行,重要的__KERNEL_CS登场了。这个宏是内核代码段的选择子,其定义在arch/x86/include/asm/segment.h185行:

185#define __KERNEL_CS     (GDT_ENTRY_KERNEL_CS * 8)

 186#define __KERNEL_DS     (GDT_ENTRY_KERNEL_DS * 8)

 187#define __USER_DS     (GDT_ENTRY_DEFAULT_USER_DS* 8 + 3)

 188#define __USER_CS     (GDT_ENTRY_DEFAULT_USER_CS* 8 + 3)

 

GDT_ENTRY_KERNEL_CS定义在同一个文件的149其值为2那么__KERNEL_CS的值就是0x10那么setup_idtmovl $(__KERNEL_CS << 16),%eax之后eax寄存器中的值就是0x100000150行,将ignore_int地址的低16位赋给ax,这样,eax寄存器中的低16就是ignore_int地址的低16位,高16位就是0x0010

 

继续走,%dx的值又被赋成了$0x8E00,暂不管他。

 

508        lea idt_table,%edi

 509        mov $256,%ecx

 

508行,edi寄存器中存放idt_table表的地址。这个表就是中断门描述符表,其在我们的arch/x86/include/asm/desc.h文件中定义:

34 extern gate_desc idt_table[];

 

中断门描述符表是一个数组,每一个元素都是一个中断门描述符gate_desc,其在文件arch/x86/include/asm/desc_defs.h中被定义成:

81 typedef struct gate_struct64 gate_desc;

 

从名字上就能看出,这个门描述符占据8个字节,64位,在同一个文件中定义:

  51struct gate_struct64 {

  52        u16 offset_low;

  53        u16 segment;

  54        unsigned ist : 3, zero0 : 5, type : 5, dpl : 2, p : 1;

  55        u16 offset_middle;

  56        u32 offset_high;

  57        u32 zero1;

  58} __attribute__((packed));

 

这里面segment是段选择子,其他字段如上图所示。idt_table就是包含若干这样的门描述符的数组表,每个表项8个字节,表头被edi寄存器指向。

 

 510rp_sidt:

 511        movl %eax,(%edi)

 512        movl %edx,4(%edi)

 513        addl $8,%edi

 514        dec %ecx

 515        jne rp_sidt

 516

 

随后,511512行,中断门描述符表的第一项前2个字节被设置成ignore_int地址的低16位作为偏移,中间2个字节被设置成段选择子0x0010,后4个字节被设置成$0x8E00。随后513~515行设置256个这样的门描述符,每个门描述符内容一样,都是调用函数ignore_int作为中断服务程序。

 

 517.macro  set_early_handler handler,trapno

 518        lea /handler,%edx

 519        movl $(__KERNEL_CS << 16),%eax

 520        movw %dx,%ax

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

 522        lea idt_table,%edi

 523        movl %eax,8*/trapno(%edi)

 524        movl %edx,8*/trapno+4(%edi)

 525.endm

 526

 527        set_early_handler handler=early_divide_err,trapno=0

 528        set_early_handler handler=early_illegal_opcode,trapno=6

 529        set_early_handler handler=early_protection_fault,trapno=13

 530        set_early_handler handler=early_page_fault,trapno=14

 531

 532        ret

 

上面代码改变几个中断/异常处理函数0/6/13/14号中断描述符表项设置成相应的处理函数527~530就是这些函数early_divide_errearly_illegal_opcodeearly_protection_faultearly_page_fault。这些函数又都是调用的early_fault函数:

 

552early_fault:

 553        cld

 554#ifdef CONFIG_PRINTK

 555        pusha

 556        movl $(__KERNEL_DS),%eax

 557        movl %eax,%ds

 558        movl %eax,%es

 559        cmpl $2,early_recursion_flag

 560        je hlt_loop

 561        incl early_recursion_flag

 562        movl %cr2,%eax

 563        pushl %eax

 564        pushl %edx              /* trapno */

 565        pushl $fault_msg

 566        call printk

 567#endif

 568        call dump_stack

 569hlt_loop:

 570        hlt

 571        jmp hlt_loop

 572

 573/* This is the default interrupt "handler" :-) */

 574        ALIGN

 

主要是打印fault_msg信息:

669fault_msg:

 670/* fault info: */

 671        .ascii "BUG: Int %d: CR2 %p/n"

 672/* pusha regs: */

 673        .ascii "     EDI %p  ESI %p  EBP %p  ESP %p/n"

 674        .ascii "     EBX %p  EDX %p  ECX %p  EAX %p/n"

 675/* fault frame: */

 676        .ascii "     err %p  EIP %p   CS %p  flg %p/n"

 677        .ascii "Stack: %p %p %p %p %p %p %p %p/n"

 678        .ascii "       %p %p %p %p %p %p %p %p/n"

 679        .asciz "       %p %p %p %p %p %p %p %p/n"

 

当然,如果没有配置打印选项CONFIG_PRINTK,就关机(当然不可能没有设置)。