注:本文转载来源:http://blog.csdn.net/forever_2015/article/details/52653404
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 中断入口: