初始化异常服务

5.7 初始化异常服务

继续走,start_kernel583行,sort_main_extable,把编译期间,kbuild设置的异常表,也就是__start___ex_table__stop___ex_table之中的所有元素进行排序。

 

584行,调用trap_init函数,重要的函数,初始化中断向量表。该函数来自arch/x86/kernel/traps.c

 

882void __init trap_init(void)

 883{

 884        int i;

 885

 886#ifdef CONFIG_EISA

 887        void __iomem *p = early_ioremap(0x0FFFD9, 4);

 888

 889        if (readl(p) == 'E' + ('I'<<8) + ('S'<<16) + ('A'<<24))

 890                EISA_bus = 1;

 891        early_iounmap(p, 4);

 892#endif

 893

 894        set_intr_gate(0, &divide_error);

 895        set_intr_gate_ist(1, &debug, DEBUG_STACK);

 896        set_intr_gate_ist(2, &nmi, NMI_STACK);

 897        /* int3 can be called from all */

 898        set_system_intr_gate_ist(3, &int3, DEBUG_STACK);

 899        /* int4 can be called from all */

 900        set_system_intr_gate(4, &overflow);

 901        set_intr_gate(5, &bounds);

 902        set_intr_gate(6, &invalid_op);

 903        set_intr_gate(7, &device_not_available);

 904#ifdef CONFIG_X86_32

 905        set_task_gate(8, GDT_ENTRY_DOUBLEFAULT_TSS);

 906#else

 907        set_intr_gate_ist(8, &double_fault, DOUBLEFAULT_STACK);

 908#endif

 909        set_intr_gate(9, &coprocessor_segment_overrun);

 910        set_intr_gate(10, &invalid_TSS);

 911        set_intr_gate(11, &segment_not_present);

 912        set_intr_gate_ist(12, &stack_segment, STACKFAULT_STACK);

 913        set_intr_gate(13, &general_protection);

 914        set_intr_gate(14, &page_fault);

 915        set_intr_gate(15, &spurious_interrupt_bug);

 916        set_intr_gate(16, &coprocessor_error);

 917        set_intr_gate(17, &alignment_check);

 918#ifdef CONFIG_X86_MCE

 919        set_intr_gate_ist(18, &machine_check, MCE_STACK);

 920#endif

 921        set_intr_gate(19, &simd_coprocessor_error);

 922

 923        /* Reserve all the builtin and the syscall vector: */

 924        for (i = 0; i < FIRST_EXTERNAL_VECTOR; i++)

 925                set_bit(i, used_vectors);

 926

 927#ifdef CONFIG_IA32_EMULATION

 928        set_system_intr_gate(IA32_SYSCALL_VECTOR, ia32_syscall);

 929        set_bit(IA32_SYSCALL_VECTOR, used_vectors);

 930#endif

 931

 932#ifdef CONFIG_X86_32

 933        if (cpu_has_fxsr) {

 934                printk(KERN_INFO "Enabling fast FPU save and restore... ");

 935                set_in_cr4(X86_CR4_OSFXSR);

 936                printk("done./n");

 937        }

 938        if (cpu_has_xmm) {

 939                printk(KERN_INFO

 940                        "Enabling unmasked SIMD FPU exception support... ");

 941                set_in_cr4(X86_CR4_OSXMMEXCPT);

 942                printk("done./n");

 943        }

 944

 945        set_system_trap_gate(SYSCALL_VECTOR, &system_call);

 946        set_bit(SYSCALL_VECTOR, used_vectors);

 947#endif

 948

 949        /*

 950         * Should be a barrier for any external CPU state:

 951         */

 952        cpu_init();

 953

 954        x86_init.irqs.trap_init();

 955}

 

我们没有配置CONFIG_EISA编译选项,所以886~892行代码忽略。894~921行,对中断向量表0 - 19号异常设置对应的服务程序。我们看到主要有四种类型的设置函数:

set_intr_gate

set_system_intr_gate

set_system_trap_gate

set_task_gate

 

其实这里涉及到一个x86体系的“门”概念,这里简单介绍一下:x86保护模式下物理地址段具有4种特级——04级——,而Linux只使用了表示内核态的0级和用户态的3级。当异常和中断发生时,必然会引起地址的转换,比如从3级段中,转移到0级段中去执行相应的代码。如何转移?Intel提供了一个审查机制,来保护我们的状态切换,这就是“门”的概念。

 

X86体系一个提供了四种门,即任务门(Task Gate)、中断门(Interrupt Gate)、陷阱门(Trap Gate)和调用门(Call Gate)。Linux只使用了前三种门的描述符,我们看到上面四种函数分别就对应这些门的设置。这里插一句,内核2.4.0以后的版本进行进程切换的时候就不使用任务门了,因为描述一个进程所需要的信息远远多于一个任务门所能表达的内容,因为后者仅仅是一个硬件上的概念。所以,我们看到905行,内核只有双重故障异常,也就是8号中断向量使用到了任务门,且仅仅是32x86系统中。

 

有些函数还加入了_ist后缀,不过不管是set_intr_gate还是set_intr_gate_ist函数,最终都调用_set_gate函数,并把中断号、门类型、处理函数地址、DPL的值、ist和目录段寄存器作为参数传给它:

 

static inline void _set_gate(int gate, unsigned type, void *addr,

                          unsigned dpl, unsigned ist, unsigned seg)

{

       gate_desc s;

       pack_gate(&s, type, (unsigned long)addr, dpl, ist, seg);

       /*

        * does not need to be atomic because it is only done once at

        * setup time

        */

       write_idt_entry(idt_table, gate, &s);

}

 

gate_desc结构我们已经见过了,这个结构是一个中断描述符,我们在“初始化中断描述符表”中设置了256个这样的门描述符,idt_table就是门描述符所组成的一个表的首地址,也叫中断描述符表(进入保护模式后,中断向量表IDT的每个表项不再是4个字节,而是8个字节的门描述符gate_desc)。我们看到先是调用pack_gate根据所传进来的参数初始化门描述符gate_desc

static inline void pack_gate(gate_desc *gate, unsigned char type,

                          unsigned long base, unsigned dpl, unsigned flags,

                          unsigned short seg)

{

       gate->a = (seg << 16) | (base & 0xffff);

       gate->b = (base & 0xffff0000) |

                (((0x80 | type | (dpl << 5)) & 0xff) << 8);

}

 

我们看到,32位的x86系统,传递进来的ist参数是没有用的,所以加不加_ist后缀意义不大,随后调用write_idt_entry设置中断描述符表的某个元素:

#define write_idt_entry(dt, entry, g)            /

       native_write_idt_entry(dt, entry, g)

static inline void native_write_idt_entry(gate_desc *idt, int entry,

                                     const gate_desc *gate)

{

       memcpy(&idt[entry], gate, sizeof(*gate));

}

 

好了,trap_init函数设置了中断描述符表的前19个表项后,在945行还做了一个及其重要的工作,就是设置一个系统门:

#define IA32_SYSCALL_VECTOR            0x80

#ifdef CONFIG_X86_32

# define SYSCALL_VECTOR                    0x80

#endif

 

这就是Linux的系统调用对应的中断门我们看到它的中断向量号是0x80号。随后952行调用cpu_init()函数,来自arch/x86/kernel/cpu/common.c

 

1200void __cpuinit cpu_init(void)

1201{

1202        int cpu = smp_processor_id();

1203        struct task_struct *curr = current;

1204        struct tss_struct *t = &per_cpu(init_tss, cpu);

1205        struct thread_struct *thread = &curr->thread;

1206

1207        if (cpumask_test_and_set_cpu(cpu, cpu_initialized_mask)) {

1208                printk(KERN_WARNING "CPU#%d already initialized!/n", cpu);

1209                for (;;)

1210                        local_irq_enable();

1211        }

1212

1213        printk(KERN_INFO "Initializing CPU#%d/n", cpu);

1214

1215        if (cpu_has_vme || cpu_has_tsc || cpu_has_de)

1216                clear_in_cr4(X86_CR4_VME|X86_CR4_PVI|X86_CR4_TSD|X86_CR4_DE);

1217

1218        load_idt(&idt_descr);

1219        switch_to_new_gdt(cpu);

1220

1221        /*

1222         * Set up and load the per-CPU TSS and LDT

1223         */

1224        atomic_inc(&init_mm.mm_count);

1225        curr->active_mm = &init_mm;

1226        BUG_ON(curr->mm);

1227        enter_lazy_tlb(&init_mm, curr);

1228

1229        load_sp0(t, thread);

1230        set_tss_desc(cpu, t);

1231        load_TR_desc();

1232        load_LDT(&init_mm.context);

1233

1234        t->x86_tss.io_bitmap_base = offsetof(struct tss_struct, io_bitmap);

1235

1236#ifdef CONFIG_DOUBLEFAULT

1237        /* Set up doublefault TSS pointer in the GDT */

1238        __set_tss_desc(cpu, GDT_ENTRY_DOUBLEFAULT_TSS, &doublefault_tss);

1239#endif

1240

1241        clear_all_debug_regs();

1242

1243        /*

1244         * Force FPU initialization:

1245         */

1246        if (cpu_has_xsave)

1247                current_thread_info()->status = TS_XSAVE;

1248        else

1249                current_thread_info()->status = 0;

1250        clear_used_math();

1251        mxcsr_feature_mask_init();

1252

1253        /*

1254         * Boot processor to setup the FP and extended state context info.

1255         */

1256        if (smp_processor_id() == boot_cpu_id)

1257                init_thread_xstate();

1258

1259        xsave_init();

1260}

 

首先获得当前cpu的编号然后获得0号进程的task_structthread_struct以及本cpu上的tss结构。随后1218行通过load_idt函数加载刚刚设置好的idt_descr数组首地址到IDTR寄存器;219行加载本CPU的全局描述符表地址:

void switch_to_new_gdt(int cpu)

{

       struct desc_ptr gdt_descr;

 

       gdt_descr.address = (long)get_cpu_gdt_table(cpu);

       gdt_descr.size = GDT_SIZE - 1;

       load_gdt(&gdt_descr);

       /* Reload the per-cpu base */

 

       load_percpu_segment(cpu);

}

随后1225行加载0号进程的active_mm指针指向全局变量init_mm后面还做了一些其他的初始化工作。这个函数的所做的工作很多前面已经完成了,这里只是再次确保这些规定动作没有任何遗漏,充分地显示了Linux内核的完整性和健壮性。

 

trap_init的最后一行调用x86_init.irqs.trap_init函数也就是x86_init_noop是个空函数所以收工了。我们看到trap_init函数主要是对包括大部分异常的服务程序设置。至于中断门、陷阱门及系统门的相关基础知识,请访问我的博客“中断描述符表”

http://blog.csdn.net/yunsongice/archive/2010/02/11/5306387.aspx

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值