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 :
ARMv8 异常处理源码入口_第1张图片

IRQ 中断入口:
ARMv8 异常处理源码入口_第2张图片






 

你可能感兴趣的:(【ARMv8】)