内核版本:2.6.23
处理器类型:ARM
1.arch/arm/kernel/traps.c
该文件的trap_init函数,完成中断向量表的拷贝工作,如下:
“CONFIG_VECTORS_BASE”可以在编译内核时配置,一般为0xffff0000,需要把异常向量表拷贝到该处。
void __init trap_init(void)
{
unsigned long vectors = CONFIG_VECTORS_BASE;
extern char __stubs_start[], __stubs_end[];
extern char __vectors_start[], __vectors_end[];
extern char __kuser_helper_start[], __kuser_helper_end[];
int kuser_sz = __kuser_helper_end - __kuser_helper_start;
/*
* Copy the vectors, stubs and kuser helpers (in entry-armv.S)
* into the vector page, mapped at 0xffff0000, and ensure these
* are visible to the instruction stream.
*/
memcpy((void *)vectors, __vectors_start, __vectors_end - __vectors_start);
memcpy((void *)vectors + 0x200, __stubs_start, __stubs_end - __stubs_start);
memcpy((void *)vectors + 0x1000 - kuser_sz, __kuser_helper_start, kuser_sz);
/*
* Copy signal return handlers into the vector page, and
* set sigreturn to be a pointer to these.
*/
memcpy((void *)KERN_SIGRETURN_CODE, sigreturn_codes,
sizeof(sigreturn_codes));
flush_icache_range(vectors, vectors + PAGE_SIZE);
modify_domain(DOMAIN_USER, DOMAIN_CLIENT);
}
|
__vectors_start、__stubs_start和kuser_sz为异常向量表、子向量等所处的位置,分别拷贝到0xffff0000、0xffff0200以及0xffff1000处。
2.arch/arm/kernel/entry-armv.S
__vectors_start、__stubs_start和kuser_sz在该文件定义,如下:
.globl __vectors_start
__vectors_start:
swi SYS_ERROR0
b vector_und + stubs_offset
ldr pc, .LCvswi + stubs_offset
b vector_pabt + stubs_offset
b vector_dabt + stubs_offset
b vector_addrexcptn + stubs_offset
b vector_irq + stubs_offset
b vector_fiq + stubs_offset
.globl __vectors_end
__vectors_end:
|
__stubs_start(对无用代码,有部分的删减):
__stubs_start:
/*
* Interrupt dispatcher
*/
vector_stub irq, IRQ_MODE, 4
.long __irq_usr @ 0 (USR_26 / USR_32)
.long __irq_svc @ 3 (SVC_26 / SVC_32)
/*
* Data abort dispatcher
* Enter in ABT mode, spsr = USR CPSR, lr = USR PC
*/
vector_stub dabt, ABT_MODE, 8
.long __dabt_usr @ 0 (USR_26 / USR_32)
.long __dabt_svc @ 3 (SVC_26 / SVC_32)
/*
* Prefetch abort dispatcher
* Enter in ABT mode, spsr = USR CPSR, lr = USR PC
*/
vector_stub pabt, ABT_MODE, 4
.long __pabt_usr @ 0 (USR_26 / USR_32)
.long __pabt_svc @ 3 (SVC_26 / SVC_32)
/*
* Undef instr entry dispatcher
* Enter in UND mode, spsr = SVC/USR CPSR, lr = SVC/USR PC
*/
vector_stub und, UND_MODE
.long __und_usr @ 0 (USR_26 / USR_32)
.long __und_svc @ 3 (SVC_26 / SVC_32)
.align 5
/*=============================================================================
* Undefined FIQs
*-----------------------------------------------------------------------------
* Enter in FIQ mode, spsr = ANY CPSR, lr = ANY PC
* MUST PRESERVE SVC SPSR, but need to switch to SVC mode to show our msg.
* Basically to switch modes, we *HAVE* to clobber one register... brain
* damage I don't think that we can execute any code in here in any
* other mode than FIQ... Ok you can switch to another mode, but you can't
* get out of that mode without clobbering one register.
*/
vector_fiq:
disable_fiq
subs pc, lr, #4
/*=============================================================================
* Address exception handler
*-----------------------------------------------------------------------------
* These aren't too critical.
* (they're not supposed to happen, and wo n't happen in 32-bit data mode).
*/
vector_addrexcptn: b vector_addrexcptn
/* * We group all the following data together to optimise * for CPUs with separate I & D caches. */ .align 5
.LCvswi: .word vector_swi
.globl __stubs_end __stubs_end: |
3.以svc模式数据异常的处理为例,说明异常处理
/*
* Data abort dispatcher
* Enter in ABT mode, spsr = USR CPSR, lr = USR PC
*/
vector_stub dabt, ABT_MODE, 8
.long __dabt_usr @ 0 (USR_26 / USR_32)
.long __dabt_svc @ 3 (SVC_26 / SVC_32)
|
数据异常发生时,跳转到__dabt_svc接口处理,如下:
__dabt_svc:
svc_entry
@
@ get ready to re-enable interrupts if appropriate
@
mrs r9, cpsr
tst r3, #PSR_I_BIT
biceq r9, r9, #PSR_I_BIT
@
@ Call the processor-specific abort handler:
@
@ r2 - aborted context pc
@ r3 - aborted context cpsr
@
@ The abort handler must return the aborted address in r0, and
@ the fault status register in r1. r9 must be preserved.
@
#ifdef MULTI_ABORT
ldr r4, .LCprocfns
mov lr, pc
ldr pc, [r4]
#else
bl CPU_ABORT_HANDLER
#endif
@
@ set desired IRQ state, then call main handler
@
msr cpsr_c, r9
mov r2, sp
bl do_DataAbort
@
@ IRQs off again before pulling preserved data off the stack
@
disable_irq
@
@ restore SPSR and restart the instruction
@
ldr r0, [sp, #S_PSR]
msr spsr_cxsf, r0
ldmia sp, {r0 - pc}^ @ load r0 - pc, cpsr
.align 5
|
svc_entry:负责寄存器的入栈操作
do_DataAbort:为数据异常处理函数,如下:
arch/arm/mm/fault.c
/*
* Dispatch a data abort to the relevant handler.
*/
asmlinkage void
do_DataAbort(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
{
const struct fsr_info *inf = fsr_info + (fsr & 15) + ((fsr & (1 << 10)) >> 6);
struct siginfo info;
if (!inf->fn(addr, fsr, regs))
return;
printk(KERN_ALERT "Unhandled fault: %s (0x%03x) at 0x%08lx\n",
inf->name, fsr, addr);
info.si_signo = inf->sig;
info.si_errno = 0;
info.si_code = inf->code;
info.si_addr = (void __user *)addr;
notify_die("", regs, &info, fsr, 0);
}
|
该接口打印相关的信息后,调用notify_die接口,根据用户空间还是内核空间的异常,进行不同的处理操作,如下:
arch/arm/kernel/traps.c
void notify_die(const char *str, struct pt_regs *regs, struct siginfo *info,
unsigned long err, unsigned long trap)
{
if (user_mode(regs)) {
current->thread.error_code = err;
current->thread.trap_no = trap;
force_sig_info(info->si_signo, info, current);
} else {
die(str, regs, err);
}
}
|
对用户空间的数据异常,调用force_sig_info发送信号;
对用户空间的数据异常,调用die打印相应的异常信息,然后panic内核,如下:
/*
* This function is protected against re-entrancy.
*/
NORET_TYPE void die(const char *str, struct pt_regs *regs, int err)
{
struct thread_info *thread = current_thread_info();
console_verbose();
spin_lock_irq(&die_lock);
bust_spinlocks(1);
__die(str, err, thread, regs); //打印异常发生时的堆栈等信息
bust_spinlocks(0);
spin_unlock_irq(&die_lock);
if (panic_on_oops)
panic("Fatal exception"); //Panic kernel
do_exit(SIGSEGV);
}
|
阅读(561) | 评论(0) | 转发(0) |