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

ARM Linux异常处理之data abort二

时间:11-09 来源:互联网 点击:
上文提到data abort的正常处理过程中,最终会调用do_DataAbort函数,下面分析一下该函数的处理过程。

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函数。

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代表用户模式。

l如果是用户模式,那么强制发送一个信号给导致abort的任务(注意这里的任务可能是一个线程)。具体哪个信号被发送由struct fsr_info结构体中定义的值决定,一般来说,是一个能使进程停止的信号,比如SIGSEGV等等(SIGSEGV之类的信号即使被发给一个线程,也会停止整个进程,具体可看get_signal_to_deliver函数)。

l如果是内核模式,那么调用die函数,这是kernel处理OOPS的标准函数。

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 permission fault"},

{ do_bad,SIGBUS,0,"external abort on translation"},

{do_page_fault,SIGSEGV, SEGV_ACCERR,"page permission fault"},

{ do_bad,SIGBUS,0,"unknown 16"},

//。。。

{ do_bad,SIGBUS,0,"unknown 30"},

{ do_bad,SIGBUS,0,"unknown 31"}

};

fsr_info对大多数abort都调用do_bad函数处理,do_bad函数简单返回1,这样就可以继续执行上面提到的arm_notify_die。

fsr_info对以下四种特殊abort将作单独处理:

l"section translation fault"do_translation_fault
段转换错误,即找不到二级页表

l"page translation fault"do_page_fault
页表错误,即线性地址无效,没有对应的物理地址

l"section permission fault"do_sect_fault
段权限错误,即二级页表权限错误

l"page permission fault"do_page_fault
页权限错误

段权限错误do_sect_fault

do_sect_fault函数直接调用do_bad_area作处理,并返回0,所以不会再经过arm_notify_die。do_bad_area中,判断是否属于用户模式。如果是用户模式,调用__do_user_fault函数;否则调用__do_kernel_fault函数。

void do_bad_area(unsigned long addr, unsigned int fsr, struct pt_regs *regs)

if (user_mode(regs))

__do_user_fault(tsk, addr, fsr, SIGSEGV, SEGV_MAPERR, regs);

else

__do_kernel_fault(mm, addr, fsr, regs);

__do_user_fault中,会发送信号给当前线程。

__do_kernel_fault则比较复杂:

l调用fixup_exception进行修复操作,fixup的具体细节可在内核文档exception.txt中找到,它可用于处理get_user之类函数传入的地址参数无效的情况。

l如果不能修复,调用die函数处理oops。

l如果没有进程上下文,内核会在

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

网站地图

Top