Chinaunix首页 | 论坛 | 博客
  • 博客访问: 31255
  • 博文数量: 21
  • 博客积分: 360
  • 博客等级: 一等列兵
  • 技术积分: 165
  • 用 户 组: 普通用户
  • 注册时间: 2010-11-14 12:02
个人简介

慢下来,享受技术

文章分类

全部博文(21)

文章存档

2014年(13)

2010年(8)

我的朋友

分类: LINUX

2010-11-14 17:20:10

内核版本: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在该文件定义,如下:
__vectors_start:

.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);
}


阅读(555) | 评论(0) | 转发(0) |
0

上一篇:没有了

下一篇:佛家四大经典爱情故事---很有哲理性

给主人留下些什么吧!~~