转自:http://qgjie456.blog.163.com/blog/static/35451367200811241594048/
void __init trap_init(void){ extern char except_vec3_generic, except_vec3_r4000; extern char except_vec4; unsigned long i; 在我们的系统中没有定义这两个变量 == 0。 if (cpu_has_veic || cpu_has_vint) ebase = (unsigned long) alloc_bootmem_low_pages (0x200 + VECTORSPACING*64); else ebase = CAC_BASE; 这个 CAC_BASE 表明支持 CACHE 的 base 地址,由于是 32 的 CPU ,所以等于 0x80000000。 这个 ebase 变量表示异常入口点的基地址。 一般情况下不配置 CONFIG_CPU_MIPSR2_SRSS 这个宏,所以这个函数为空函数。 mips_srs_init(); 初始化 CUP 的 TLB 和 cache ,以及它们的异常处理程序。 per_cpu_trap_init(); 设置通用异常入口(ebase+0x180)的处理程序为 except_vec3_generic 。 set_handler(0x180, &except_vec3_generic, 0x80);--------------------------------- 在通用异常处理程序中,读取 CAUSE 寄存器的 ExcCode 域的值,这个预在发生异常时, 由 CPU 自动设置,使软件也可以知道异常的原因。 通用异常处理程序使用 ExcCode 域的值,来索引异常处理表 exception_handlers[]。 这个通用异常处理表其实是一个 unsigned long exception_handlers[32] 数组,表示 相应异常的处理程序地址,下面的代码进行初始化为保留的异常处理。--------------------------------- for (i = 0; i <= 31; i++) set_except_vector(i, handle_reserved);--------------------------------- 如果 CPU 有 EJTAG,设置 EJTAG 的异常处理程序。 这个可以在 probe_cpu() 函数中自动检测,并设置 cpu_data 的 option 成员。 也可以在 include/asm-mips/mach-XXXX/cpu-feature-overrides.h 头文件中设置 。 移植相关,移植时需要配置。--------------------------------- if (cpu_has_ejtag && board_ejtag_handler_setup) board_ejtag_handler_setup (); 设置 CPU 是否有 watch (内存的访问检测点)异常,如果有进行设置。 移植相关,移植时需要配置。 if (cpu_has_watch) set_except_vector(23, handle_watch);--------------------------------- 初始化中断处理器,如果有外部中断控制器或者支持中断向量模式, 或者除法异常时 (cpu_has_divec) ,需要进行特殊的操作。 下面的我们的 CPU 不执行,不支持。--------------------------------- if (cpu_has_veic || cpu_has_vint) { int nvec = cpu_has_veic ? 64 : 8; for (i = 0; i < nvec; i++) set_vi_handler(i, NULL); }else if (cpu_has_divec) set_handler(0x200, &except_vec4, 0x8); 设置 CPU 的 cache 的奇偶检测位。 parity_protection_init(); 由于 Data Bus Errors / Instruction Bus Errors 异常是由外部硬件通知的, 所以这两种异常需要有板级的特殊处理程序。 if (board_be_init) board_be_init(); 根据 ExcCode 域的值对通用异常处理表进行初始化,注册各个通用异常处理程序。 可以查看 CACUS 寄存器 ExcCode 域的值所对应的异常种类。 set_except_vector(0, handle_int); 中断的处理程序 set_except_vector(1, handle_tlbm); set_except_vector(2, handle_tlbl); set_except_vector(3, handle_tlbs); set_except_vector(4, handle_adel); set_except_vector(5, handle_ades); set_except_vector(6, handle_ibe); set_except_vector(7, handle_dbe); set_except_vector(8, handle_sys); set_except_vector(9, handle_bp); set_except_vector(10, rdhwr_noopt ? handle_ri : (cpu_has_vtag_icache ? handle_ri_rdhwr_vivt : handle_ri_rdhwr)); set_except_vector(11, handle_cpu); set_except_vector(12, handle_ov); set_except_vector(13, handle_tr); if (current_cpu_data.cputype == CPU_R6000 || current_cpu_data.cputype == CPU_R6000A) { 如果 CPU 的类型是 CPU_R6000 或者 CPU_R6000A,就 注册一些专用的通用异常处理程序。 } 如果 CPU 支持不可屏蔽中断,设置不可屏蔽中断的处理函数。 移植相关,移植时需要配置。 if (board_nmi_handler_setup) board_nmi_handler_setup(); if (cpu_has_fpu && !cpu_has_nofpuex) set_except_vector(15, handle_fpe); set_except_vector(22, handle_mdmx); if (cpu_has_mcheck) set_except_vector(24, handle_mcheck); if (cpu_has_mipsmt) set_except_vector(25, handle_mt); set_except_vector(26, handle_dsp);-------------------------------------- 选择特殊的通用异常处理程序,并拷贝到通用异常入口(ebase+0x180)。-------------------------------------- if (cpu_has_vce) memcpy((void *)(CAC_BASE + 0x180), &except_vec3_r4000, 0x100); else if (cpu_has_4kex) memcpy((void *)(CAC_BASE + 0x180), &except_vec3_generic, 0x80); else memcpy((void *)(CAC_BASE + 0x080), &except_vec3_generic, 0x80); 设置浮点处理器的保存和加载程序指针。 signal_init(); 这个 flush_icache_range()是一个函数指针,在 r4k_cache_init() 函数中赋值。 参考《linux-mips启动分析(9-1)》。 把 ebase 地址到 ebase + 0x400 地址的指令装入 指令 cache 中去。 flush_icache_range(ebase, ebase + 0x400); 把 TLB 修改、加载和读取异常的处理代码加载到指令缓存中。 flush_tlb_handlers();}********************************************这个 cpu_has_vint 变量是在 include/asm-mips/cpu-features.h 文件中定义的,这个 CONFIG_CPU_MIPSR2_IRQ_VI 宏定义,表明 CPU 是否支持 Vectored interrupt mode。这种模式能够使中断分发反应更加快速。这种模式的代码兼容非 Vectored interrupt mode。所以如果一个 CPU 不支持这种模式,也可以选择上这个选项。---------------------------------#if defined(CONFIG_CPU_MIPSR2_IRQ_VI) && !defined(cpu_has_vint)# define cpu_has_vint (cpu_data[0].options & MIPS_CPU_VINT)#elif !defined(cpu_has_vint)# define cpu_has_vint 0#endif********************************************这个 CONFIG_CPU_MIPSR2_SRS 宏配置的帮助信息: Allow the kernel to use shadow register sets for fast interrupts. Interrupt handlers must be specially written to use shadow sets. Say N unless you know that shadow register set upport is needed.********************************************把 addr 起始地址的代码(异常处理程序)拷贝到 (ebase + offset) 地址(异常处理入口点)。---------------------------------void __init set_handler (unsigned long offset, void *addr, unsigned long size){ memcpy((void *)(ebase + offset), addr, size); flush_icache_range(ebase + offset, ebase + offset + size);}********************************************这个 signal_init() 函数被 trap_init() 函数所调用。这个函数初始化 save_fp_context 和 restore_fp_context 这两个函数指针。这两个变量 save_fp_context 和 restore_fp_context 是两个函数指针。这个两个函数用来保存和重新加载协处理器浮点处理器的上下文。asmlinkage int (*save_fp_context)(struct sigcontext __user *sc);asmlinkage int (*restore_fp_context)(struct sigcontext __user *sc);-----------------------------------------static inline void signal_init(void){ if (cpu_has_fpu) { save_fp_context = _save_fp_context; restore_fp_context = _restore_fp_context; } else { save_fp_context = fpu_emulator_save_context; restore_fp_context = fpu_emulator_restore_context; }}******************************************** 这个 flush_icache_range()是一个函数指针,在 r4k_cache_init() 函数中赋值。 参考《linux-mips启动分析(9-1)》。 在 r4k_cache_init()赋值 flush_icache_range = r4k_flush_icache_range 。 这个函数的主要作用是刷新 指定指令 cache 的范围。-----------------------------------------static void r4k_flush_icache_range(unsigned long start, unsigned long end){ struct flush_icache_range_args args; args.start = start; args.end = end; r4k_on_each_cpu(local_r4k_flush_icache_range, &args, 1, 1); instruction_hazard();} ********************************************这个 flush_tlb_handlers() 函数被 trap_init() 函数所调用。这个函数把 TLB 修改、加载和读取异常的处理代码加载到指令缓存中。-----------------------------------------void __init flush_tlb_handlers(void){ 把 TLB 读取异常的处理代码加载到指令缓存中。 flush_icache_range((unsigned long)handle_tlbl, (unsigned long)handle_tlbl + sizeof(handle_tlbl)); 把 TLB 加载异常的处理代码加载到指令缓存中。 flush_icache_range((unsigned long)handle_tlbs, (unsigned long)handle_tlbs + sizeof(handle_tlbs)); 把 TLB 修改异常的处理代码加载到指令缓存中。 flush_icache_range((unsigned long)handle_tlbm, (unsigned long)handle_tlbm + sizeof(handle_tlbm));}
********************************************
except_vec3_generic是硬件的异常处 理程序(exception handler),它和其它异常处理程序全部存在于entry-armv.S或genex.S内。except_vec3_generic 内有exception_handlers,它是用来储存每一个硬件中断的lower- ISR,trap_init负责将所有的lower-ISR填入exception_handlers中。所有的lower-ISR都是存在于 entry-armv.S或genex.S内。硬件的lower-ISR称作handle_int,说来奇怪,所有硬件都使用这唯一的lower- ISR,这是因为CPU分配给硬件的中断源只有一个,不管是MIPS或ARM或PPC都是这样的。一有硬件异常或中断发生时,程序计数器(porgam counter;PC)会跳到“异常来源区”,执行except_vec3_generic,再跳到handle_int,最后跳到 plat_irq_dispatch-这就是high-ISR。