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 |
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 |
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) |
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 |
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 中断入口: