ARMv8 异常处理源码入口

1、Data abort 
先看64位

分析 kernel/arch/arm64/kernel/entry.S 文件,
查到C函数入口 => do_mem_abort
其中:x0 /x1 /x2 作为传入参数寄存器,
x0 <=far_el1 , 传入出错的的虚拟地址信息(far_el1 在ARMv9 datasheet有详细解释)
x1 <= esr_el1,传入异常类型原因等综合信息
x2 <= sp, 传入当前的堆栈地址 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
el1_sync:
    kernel_entry 1
    mov x0, sp
...
    mrs x1, esr_el1
    b.eq    el1_da
...
el1_da:
    /*
     * Data abort handling
     */
    mrs x0, far_el1
    enable_dbg_if_not_stepping x2
    // re-enable interrupts if they were enabled in the aborted context
    tbnz    x23, #7, 1f           // PSR_I_BIT
    enable_irq
1:
    mov x2, sp             // struct pt_regs
    bl  do_mem_abort
...
    kernel_exit 1
进一步追查 do_mem_abort 函数,C原型在这个文件中:
 kernel/arch/arm64/mm/fault.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/*
 * Dispatch a data abort to the relevant handler.
 */
asmlinkage void __exception do_mem_abort(unsigned long addr, unsigned int esr,
                     struct pt_regs *regs)
{
    const struct fault_info *inf = fault_info + (esr & 63);
    struct siginfo info;
 
    if (!inf->fn(addr, esr, regs))
        return;
 
    pr_alert("Unhandled fault: %s (0x%08x) at 0x%016lx\n",
         inf->name, esr, addr);
 
    info.si_signo = inf->sig;
    info.si_errno = 0;
    info.si_code  = inf->code;
    info.si_addr  = (void __user *)addr;
    arm64_notify_die("", regs, &info, esr);
}

再看32位:
分析入口文件:kernel/arch/arm/kernel/entry-armv.S 
入口程序:__dabt_usr 、__dabt_svc ,表明data abort只能出现在user mode ||svc mode下,其它模式发生直接视为无效.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/*
 * 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_invalid          @  1  (FIQ_26 / FIQ_32)
    .long   __dabt_invalid          @  2  (IRQ_26 / IRQ_32)
    .long   __dabt_svc          @  3  (SVC_26 / SVC_32)
    .long   __dabt_invalid          @  4
    .long   __dabt_invalid          @  5
    .long   __dabt_invalid          @  6
    .long   __dabt_invalid          @  7
    .long   __dabt_invalid          @  8
    .long   __dabt_invalid          @  9
    .long   __dabt_invalid          @  a
    .long   __dabt_invalid          @  b
    .long   __dabt_invalid          @  c
    .long   __dabt_invalid          @  d
    .long   __dabt_invalid          @  e
    .long   __dabt_invalid          @  f
继续追查: __dabt_usr 如下:
1
2
3
4
5
6
7
8
__dabt_usr:
    usr_entry
    kuser_cmpxchg_check
    mov r2, sp
    dabt_helper
    b   ret_from_exception    //此处表明是异常返回了,那么肯定在此之前完成异常处理
 UNWIND(.fnend     )
ENDPROC(__dabt_usr)
继续追查 :dabt_helper 这是个宏定义如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
    .macro  dabt_helper
 
    @
    @ Call the processor-specific abort handler:
    @
    @  r2 - pt_regs
    @  r4 - aborted context pc
    @  r5 - aborted context psr
    @
    @ The abort handler must return the aborted address in r0, and
    @ the fault status register in r1.  r9 must be preserved.
    @
#ifdef MULTI_DABORT     
    ldr ip, .LCprocfns
    mov lr, pc
    ldr pc, [ip, #PROCESSOR_DABT_FUNC]
#else
    bl  CPU_DABORT_HANDLER
#endif
    .endm
这里有个宏开关 MULTI_DABORT,特意查了 kernel/arch/arm/include/asm/glue-df.h 文件发现没有定义这个,结合项目config_def文件,确认是走的  bl  CPU_DABORT_HANDLER ,而 CPU_DABORT_HANDLER 在 kernel/arch/arm/include/asm/glue-df.h 中定义如下:
1
#  define CPU_DABORT_HANDLER   v7_early_abort
所以继续追查:v7_early_abort 
在文件 kernel/arch/arm/mm/abort-ev7.S 中有如下代码:
1
2
3
4
5
6
7
8
9
10
11
12
    .align  5
ENTRY(v7_early_abort)
    mrc p15, 0, r1, c5, c0, 0      @ get FSR
    mrc p15, 0, r0, c6, c0, 0      @ get FAR
 
    /*
     * V6 code adjusts the returned DFSR.
     * New designs should not need to patch up faults.
     */
...
    b   do_DataAbort
ENDPROC(v7_early_abort)
然后一搜索do_DataAbort 终端出现:
./arch/arm/mm/fault.c:546:do_DataAbort(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
终于露出庐山真面目,do_DataAbort 这个就是32位 Data abort 的C代码入口:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
/*
 * Dispatch a data abort to the relevant handler.
 */
asmlinkage void __exception
do_DataAbort(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
{
    struct thread_info *thread = current_thread_info();
    int ret;
    const struct fsr_info *inf = fsr_info + fsr_fs(fsr);
    struct siginfo info;
 
    if (!user_mode(regs)) {
        thread->cpu_excp++;
        if (thread->cpu_excp == 1) {
            thread->regs_on_excp = (void *)regs;
            aee_excp_regs = (void*)regs;
        }
        /*
         * NoteXXX: The data abort exception may happen twice
         *          when calling probe_kernel_address() in which.
         *          __copy_from_user_inatomic() is used and the
         *          fixup table lookup may be performed.
         *          Check if the nested panic happens via 
         *          (cpu_excp >= 3).
         */
        if (thread->cpu_excp >= 3) {
            aee_stop_nested_panic(regs);
        }
    }
 
    ret = inf->fn(addr, fsr & ~FSR_LNX_PF, regs);
    if (!ret) {
        if (!user_mode(regs)) {
            thread->cpu_excp--;
        }
        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;
    arm_notify_die("", regs, &info, fsr, 0);
}

以上就是64/32位架构的Data abort 异常入口,下面介绍IRQ中断入口:
先看64位
 分析 kernel/arch/arm64/kernel/entry.S 文件找到irq入口:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
el1_irq:
    kernel_entry 1
    enable_dbg_if_not_stepping x0
#ifdef CONFIG_TRACE_IRQFLAGS
    bl  trace_hardirqs_off
#endif
    bl  MT_trace_hardirqs_off
#ifdef CONFIG_PREEMPT
    get_thread_info tsk
    ldr w24, [tsk, #TI_PREEMPT]       // get preempt count
    add w0, w24, #1           // increment it
    str w0, [tsk, #TI_PREEMPT]
#endif
    irq_handler        //这是个宏,定义了irq响应处理.
#ifdef CONFIG_PREEMPT
    str w24, [tsk, #TI_PREEMPT]       // restore preempt count
    cbnz    w24, 1f                // preempt count != 0
    ldr x0, [tsk, #TI_FLAGS]      // get flags
    tbz x0, #TIF_NEED_RESCHED, 1f // needs rescheduling?
    bl  el1_preempt
1:
#endif
    bl  MT_trace_hardirqs_on
#ifdef CONFIG_TRACE_IRQFLAGS
    bl  trace_hardirqs_on
#endif
    kernel_exit 1
ENDPROC(el1_irq)
进入 irq_handler 宏定义:
1
2
3
4
5
6
7
8
/*
 * Interrupt handling.
 */
    .macro  irq_handler
    ldr x1, handle_arch_irq
    mov x0, sp
    blr x1      //其实就是跳转到handle_arch_irq 
    .endm
搜索 handle_arch_irq ,找到 kernel/arch/arm64/kernel/irq.c 文件:
1
2
3
4
5
6
7
void __init set_handle_irq(void (*handle_irq)(struct pt_regs *))
{
    if (handle_arch_irq)
        return;
 
    handle_arch_irq = handle_irq;
}
原来handle_arch_irq是一个函数指针,这里给它赋值,然后找了 set_handle_irq 这个函数好久也没有找到合适的赋值的地方,但是看到 kernel/drivers/irqchip/irq-gic.c 中有set_handle_irq传参赋值,而且最终还是会回调 kernel/arch/arm64/kernel/irq.c 中的 handle_IRQ 函数,所以这个应该就是64位的IRQ中断最终入口:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
void handle_IRQ(unsigned int irq, struct pt_regs *regs)
{
    struct pt_regs *old_regs = set_irq_regs(regs);
#ifdef CONFIG_MTK_SCHED_TRACERS
    struct irq_desc *desc;
#endif
 
    irq_enter();
    mt_trace_ISR_start(irq);
#ifdef CONFIG_MTK_SCHED_TRACERS
    desc = irq_to_desc(irq);
    trace_irq_entry(irq,
            (desc && desc->action && desc->action->name) ? desc->action->name : "-");
#endif
 
    /*
     * Some hardware gives randomly wrong interrupts.  Rather
     * than crashing, do something sensible.
     */
    if (unlikely(irq >= nr_irqs)) {
        pr_warn_ratelimited("Bad IRQ%u\n", irq);
        ack_bad_irq(irq);
    else {
        generic_handle_irq(irq);
    }
#ifdef CONFIG_MTK_SCHED_TRACERS
    trace_irq_exit(irq);
#endif
    mt_trace_ISR_end(irq);
    irq_exit();
    set_irq_regs(old_regs);
}

再看32位
kernel/arch/arm/kernel/entry-armv.S 中:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/*
 * Interrupt dispatcher
 */
    vector_stub irq, IRQ_MODE, 4
 
    .long   __irq_usr           @  0  (USR_26 / USR_32)
    .long   __irq_invalid           @  1  (FIQ_26 / FIQ_32)
    .long   __irq_invalid           @  2  (IRQ_26 / IRQ_32)
    .long   __irq_svc           @  3  (SVC_26 / SVC_32)
    .long   __irq_invalid           @  4
    .long   __irq_invalid           @  5
    .long   __irq_invalid           @  6
    .long   __irq_invalid           @  7
    .long   __irq_invalid           @  8
    .long   __irq_invalid           @  9
    .long   __irq_invalid           @  a
    .long   __irq_invalid           @  b
    .long   __irq_invalid           @  c
    .long   __irq_invalid           @  d
    .long   __irq_invalid           @  e
    .long   __irq_invalid           @  f
展开 __irq_usr 
1
2
3
4
5
6
7
8
9
__irq_usr:
    usr_entry
    kuser_cmpxchg_check
    irq_handler
    get_thread_info tsk
    mov why, #0
    b   ret_to_user_from_irq
 UNWIND(.fnend     )
ENDPROC(__irq_usr)
展开 irq_handler 
1
2
3
4
5
6
7
8
9
10
11
12
/*
 * Interrupt handling.
 */
    .macro  irq_handler
#ifdef CONFIG_MULTI_IRQ_HANDLER
    ldr r1, =handle_arch_irq
    mov r0, sp
    adr lr, BSYM(9997f)
    ldr pc, [r1]
#else
    arch_irq_handler_default
#endif
查了 CONFIG_MULTI_IRQ_HANDLER 宏没定义,所以跑的是#else分支.
搜索展开 arch_irq_handler_default 
1
2
3
4
5
6
7
8
9
10
11
12
/*
 * Interrupt handling.  Preserves r7, r8, r9
 */
    .macro  arch_irq_handler_default
    get_irqnr_preamble r6, lr
1:  get_irqnr_and_base r0, r2, r6, lr
    movne   r1, sp
    @
    @ routine called with r0 = irq number, r1 = struct pt_regs *
    @
    adrne   lr, BSYM(1b)
    bne asm_do_IRQ
上面的asm_do_IRQ就是irq中断C程序入口,原型如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
/*
 * asm_do_IRQ is the interface to be used from assembly code.
 */
asmlinkage void __exception_irq_entry
asm_do_IRQ(unsigned int irq, struct pt_regs *regs)
{
    handle_IRQ(irq, regs);
}
 
/*
 * handle_IRQ handles all hardware IRQ's.  Decoded IRQs should
 * not come via this function.  Instead, they should provide their
 * own 'handler'.  Used by platform code implementing C-based 1st
 * level decoding.
 */
void handle_IRQ(unsigned int irq, struct pt_regs *regs)
{
    struct pt_regs *old_regs = set_irq_regs(regs);
#ifdef CONFIG_MTK_SCHED_TRACERS
    struct irq_desc *desc;
#endif
 
    irq_enter();
    mt_trace_ISR_start(irq);
#ifdef CONFIG_MTK_SCHED_TRACERS
    desc = irq_to_desc(irq);
    trace_irq_entry(irq,
            (desc && desc->action && desc->action->name) ? desc->action->name : "-");
#endif
 
    /*
     * Some hardware gives randomly wrong interrupts.  Rather
     * than crashing, do something sensible.
     */
    if (unlikely(irq >= nr_irqs)) {
        if (printk_ratelimit())
            printk(KERN_WARNING "Bad IRQ%u\n", irq);
        ack_bad_irq(irq);
    else {
        generic_handle_irq(irq);
    }
#ifdef CONFIG_MTK_SCHED_TRACERS
    trace_irq_exit(irq);
#endif
    mt_trace_ISR_end(irq);
    irq_exit();
    set_irq_regs(old_regs);
}

 图解64/32位 data abort  &  irq中断入口:
Data abort :


IRQ 中断入口:







 
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值