微波EDA网,见证研发工程师的成长!
首页 > 硬件设计 > 嵌入式设计 > ARM Linux异常处理之data abort

ARM Linux异常处理之data abort

时间:11-09 来源:互联网 点击:

3 usr模式进入data abort

usr模式进入data abort,也就是Linux的用户模式进入data bort时,会跳转到__dabt_usr。
__dabt_usr:usr_entry                                    @ 保护寄存器现场kuser_cmpxchg_checkbl    CPU_DABORT_HANDLER  @ 与svc模式时处理过程一样enable_irq                                  @ 开中断mov r2, spadr  lr, ret_from_exception            @ 重设返回地址b     do_DataAbort                      @ 与svc模式时处理过程一样ENDPROC(__dabt_usr)
由代码可知,用户模式和内核模式的data abort处理过程类似,区别在于:
  • 用户模式下data abort处理一定是开中断的;内核模式下则由具体情况决定。
  • 用户模式下异常处理返回地址被设为ret_from_exception (entry-armv.S文件);内核模式下则返回到出现异常的那条语句。
下面看一下ret_from_exception:
ENTRY(ret_from_exception)get_thread_info tskmov why, #0b     ret_to_userENDPROC(__pabt_usr)
ret_to_user会判断是否需要进行进程调度,并最终返回到用户空间。用户空间data abort时可能产生进程调度的原因就在这里。

4 未定义状态的data abort

除了usr和svc模式之外,其它模式下发生data abort时,都会调用__dabt_invalid函数。这里所说的其它模式在linux正常运行过程中是不应该存在的,所以如果进入__dabt_invalid函数,那就代表Linux内核应该崩溃了。
__dabt_invalid:inv_entry BAD_DATAb     common_invalidENDPROC(__dabt_invalid)
inv_entry宏做的主要工作是保存寄存器现场(压栈)。
common_invalid做一些必要的设置,最终调用C函数bad_mode (traps.c)。
asmlinkage void bad_mode(struct pt_regs *regs, int reason){console_verbose();printk(KERN_CRIT "Bad mode in %s handler detected\n", handler[reason]);die("Oops - bad mode", regs, 0);local_irq_disable();panic("bad mode");}
由代码可知,bad_mode主要是输出一些必要的信息,然后调用panic函数,进入死循环。
上文提到data abort的正常处理过程中,最终会调用do_DataAbort函数,下面分析一下该函数的处理过程。

5 do_DataAbort

asmlinkage void __exception do_DataAbort(unsigned long addr,                     // 导致异常的内存地址unsigned int fsr,                          // 异常发生时CP15中的寄存器值,见前文struct pt_regs *regs)                     // 异常发生时的寄存器值列表{     const struct fsr_info *inf = fsr_info + (fsr & 15) + ((fsr & (1 < 10)) >> 6);if (!inf->fn(addr, fsr, regs))return;info.si_signo = inf->sig;info.si_errno = 0;info.si_code  = inf->code;info.si_addr  = (void __user *)addr;arm_notify_die("", regs, &info, fsr, 0);}
处理data abort时,首先根据fsr的值得到产生abort的原因,然后根据此原因从一个全局数组fsr_info中得到处理此种abort的struct fsr_info结构,然后调用结构中的fn函数处理。如果fn函数为空,或者函数返回不为0,则调用arm_notify_die函数。

5.1 arm_notify_die

首先看一下比较简单的情形,即fsr_info中fn未定义,此时调用arm_notify_die处理:
void arm_notify_die(const char *str, struct pt_regs *regs,struct siginfo *info, unsigned long err, unsigned long trap){if (user_mode(regs)) {// 。。。force_sig_info(info->si_signo, info, current);} else {die(str, regs, err);}}
该函数首先使用user_mode判断abort时是属于用户模式还是内核模式,判断方法是看cpsr寄存器中的模式位。按照arm的定义,模式位为0代表用户模式。
  • 如果是用户模式,那么强制发送一个信号给导致abort的任务(注意这里的任务可能是一个线程)。具体哪个信号被发送由struct fsr_info结构体中定义的值决定,一般来说,是一个能使进程停止的信号,比如SIGSEGV等等(SIGSEGV之类的信号即使被发给一个线程,也会停止整个进程,具体可看get_signal_to_deliver函数)。
  • 如果是内核模式,那么调用die函数,这是kernel处理OOPS的标准函数。

5.2 fsr_info

fsr_info数组定义在fault.c中,对于每一种可能导致data abort的原因,都有一个fsr_info结构与之对应。
static struct fsr_info fsr_info[] = {{ do_bad,              SIGSEGV, 0,         "vector exception"            },// 。。。{ do_translation_fault,    SIGSEGV, SEGV_MAPERR, "section translation fault"},{ do_bad,              SIGBUS, 0,          "external abort on linefetch"},{ do_page_fault,     SIGSEGV, SEGV_MAPERR, "page translation fault"      },{ do_bad,              SIGBUS, 0,          "external abort on non-linefetch"  },{ do_bad,              SIGSEGV, SEGV_ACCERR, "section domain fault"              },{ do_bad,              SIGBUS, 0,          "external abort on non-linefetch"  },{ do_bad,              SIGSEGV, SEGV_ACCERR, "page domain fault"           },{ do_bad,              SIGBUS, 0,          "external abort on translation"          },{ do_sect_fault,     SIGSEGV, SEGV_ACCERR, "section perm            

Copyright © 2017-2020 微波EDA网 版权所有

网站地图

Top