1.1 Linux内核异常处理相关文件
Linux内核中,异常处理主要由两个文件完成,entry.S和traps.c,当然还有一些其它异常处理函数分布于fault.c, memory.c等等。entry.S包含异常的入口、进入异常处理C函数前的压栈、退出C函数前的出栈、一些fork函数相关的处理代码(暂不分析)、任务切换汇编处理过程(cpu_switch_to函数,暂不分析)。traps.c主要包含异常处理C函数。
本文主要分析entry.S,对于traps.c作简要介绍。
1.2 执行kernel_entry之前的栈
1.3 执行kernel_entry时的栈
1.4 执行kernel_exit 时的栈
1.5 entry.s代码分析
/*
* Low-level exception handling code
*
* Copyright (C) 2012 ARM Ltd.
* Authors: CatalinMarinas <catalin.marinas@arm.com>
* WillDeacon <will.deacon@arm.com>
*
* This program is free software; you canredistribute it and/or modify
* it under the terms of the GNU General PublicLicense version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope thatit will be useful,
* but WITHOUT ANY WARRANTY; without even theimplied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULARPURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNUGeneral Public License
* along with this program. If not, see<http://www.gnu.org/licenses/>.
*/
#include<linux/init.h>
#include<linux/linkage.h>
#include<asm/assembler.h>
#include<asm/asm-offsets.h>
#include<asm/errno.h>
#include<asm/thread_info.h>
#include<asm/unistd.h>
#include<asm/unistd32.h>
/*
* Bad Abort numbers
*-----------------
*/
#defineBAD_SYNC 0
#defineBAD_IRQ 1
#defineBAD_FIQ 2
#defineBAD_ERROR 3
//根据该结构体内容
/*
structpt_regs {
union {
struct user_pt_regs user_regs;//结构体user_pt_regs和结构体pt_regs内容一样
struct { //共用体存储31个通用寄存器,外加sp,pc,pstate三个特殊寄存器
//该结构体用于异常处理的压栈弹栈操作
u64 regs[31];
u64 sp;
u64 pc;
u64 pstate;
};
};
u64 orig_x0;
u64 syscallno;
};
*/
//S_FRAME_SIZE定义在asm-offsets.c中,DEFINE(S_FRAME_SIZE,sizeof(structpt_regs));
//即结构体pt_regs的大小,结构体pt_regs的定义见上面
//S_LR定义:DEFINE(S_LR,offsetof(structpt_regs, regs[30]));
//即31号寄存器在结构体pt_regs中的偏移量
//阅读以下内容请参考图1 和图2
.macro kernel_entry,el, regsize = 64
sub sp,sp, #S_FRAME_SIZE - S_LR // room for LR,SP, SPSR, ELR,见图2中sp'指向的位置
.if /regsize== 32
mov w0,w0 // zero upper 32bits of x0
.endif
/*
*.macro push,xreg1, xreg2 //压栈两个寄存器
*stp /xreg1,/xreg2, [sp, #-16]! //注意!!!push指令也改变sp的值!!!
*.endm
*/
push x28,x29 //进行压栈操作,push也是一个宏定义,因为ARMv8没有push指令,用stp代替
push x26,x27
push x24,x25
push x22,x23
push x20,x21
push x18,x19
push x16,x17
push x14,x15
push x12,x13
push x10,x11
push x8,x9
push x6,x7
push x4,x5
push x2,x3
push x0,x1 //此时sp指向位置见图2中sp''
.if /el== 0 //如果异常级是el0,把el0的sp栈指针给x21寄存器
mrs x21,sp_el0
.else
add x21,sp, #S_FRAME_SIZE //如果异常级不是el0,把sp指针指向的地方加上pt_regs大小后的地址放入x21,
//即指向没进入kernel_entry函数钱的sp指向的位置,见图2中x21指向的地址
.endif
mrs x22,elr_el1 //把el1的lr寄存器给x22
mrs x23,spsr_el1 //把spsr给x23
stp lr,x21, [sp, #S_LR] //把lr,x21寄存器存入sp+S_LR指向的地方
stp x22,x23, [sp, #S_PC] //把lr,存入sp+s_PC指向的位置,用于异常返回
/*
*Set syscallno to -1 by default (overridden later if real syscall).
*/
.if /el== 0
mvn x21,xzr
str x21,[sp, #S_SYSCALLNO]
.endif
/*
*Registers that may be useful after this macro is invoked:
*
*x21 - aborted SP
*x22 - aborted PC
*x23 - aborted PSTATE
*/
.endm
.macro kernel_exit,el, ret = 0
//把此时sp(即图2中sp'')+S_PC位置处开始的16字节内容分别给x21,x22
//即把栈中存的x21和x22内容取出来
ldp x21,x22, [sp, #S_PC] // load ELR,SPSR
.if /el== 0
ldr x23,[sp, #S_SP] // load return stackpointer,取出
.endif
.if /ret
ldr x1,[sp, #S_X1] // preserve x0(syscall return),如果ret=1,则保存x0,用于系统调用,暂不分析
add sp,sp, S_X2
.else
pop x0,x1 //如果ret=0,弹出x0,x1
.endif
pop x2,x3 // load therest of the registers
pop x4,x5
pop x6,x7
pop x8,x9
msr elr_el1,x21 // set up the returndata,把前面弹出的x21,x22分别赋值给elr_el1,spsr_el1
msr spsr_el1,x22
.if /el== 0
msr sp_el0,x23
.endif
pop x10,x11
pop x12,x13
pop x14,x15
pop x16,x17
pop x18,x19
pop x20,x21
pop x22,x23
pop x24,x25
pop x26,x27
pop x28,x29
ldr lr,[sp], #S_FRAME_SIZE - S_LR // load LR andrestore SP,把lr弹出
eret //return to kernel,异常返回,该指令会把lr给pc,完成跳转
.endm
.macro get_thread_info,rd
mov /rd,sp
and /rd,/rd, #~((1 << 13) - 1) // top of 8Kstack
.endm
/*
* These are the registers used in the syscallhandler, and allow us to
* have in theory up to 7 arguments to afunction - x0 to x6.
*
* x7 is reserved for the system call number in32-bit mode.
*/
sc_nr .req x25 // number of system calls
scno .req x26 // syscall number
stbl .req x27 // syscall table pointer
tsk .req x28 // current thread_info
/*
* Interrupt handling.
*/
.macro irq_handler
ldr x1,handle_arch_irq
mov x0,sp
blr x1
.endm
.text
/*
* Exception vectors.
*/
.macro ventry label //这里是2^7对齐,即对齐到内存地址的0x80
.align 7
b /label
.endm
.align 11
/*ENTRY也是一个宏,定义在include/linkage.h中
*#ifndef ENTRY
*#define ENTRY(name) /
*.globl name; /
*ALIGN; /
*name:
*#endif
*/
ENTRY(vectors)
ventry el1_sync_invalid // Synchronous EL1t,ventry 是一个宏,见上面定义
ventry el1_irq_invalid // IRQ EL1t,这个版本的内核,对于XXX_invalid类异常都是不真正处理的。这里以el1_irq_invalid进行分析
ventry el1_fiq_invalid // FIQ EL1t
ventry el1_error_invalid // Error EL1t
ventry el1_sync // Synchronous EL1h,以el1级发生同步异常为例,详细分析内核异常处理过程
ventry el1_irq // IRQ EL1h
ventry el1_fiq_invalid // FIQ EL1h
ventry el1_error_invalid // Error EL1h
ventry el0_sync // Synchronous 64-bit EL0
ventry el0_irq // IRQ 64-bit EL0
ventry el0_fiq_invalid // FIQ 64-bit EL0
ventry el0_error_invalid // Error 64-bit EL0
#ifdefCONFIG_COMPAT
ventry el0_sync_compat // Synchronous 32-bit EL0
ventry el0_irq_compat // IRQ 32-bit EL0
ventry el0_fiq_invalid_compat // FIQ 32-bit EL0
ventry el0_error_invalid_compat // Error 32-bit EL0
#else
ventry el0_sync_invalid // Synchronous 32-bit EL0
ventry el0_irq_invalid // IRQ 32-bit EL0
ventry el0_fiq_invalid // FIQ 32-bit EL0
ventry el0_error_invalid // Error 32-bit EL0
#endif
END(vectors)
/*
* Invalid mode handlers
*/
.macro inv_entry,el, reason, regsize = 64
kernel_entry el, /regsize //kernel_entry是宏,主要完成寄存器压栈操作。
mov x0,sp //x0,x1,x2是传给函数bad_mode函数的参数。sp是当前栈指针。
mov x1,#/reason //x1是发生异常的原因,用于读取一个结构体,在函数bad_mode中会介绍
mrs x2,esr_el1 //通过分析bad_mode及其他函数,确定esr_el1是el1级异常分类寄存器,
//用于在一个大类异常(例如syc异常)中细分异常类型
b bad_mode
.endm
el0_sync_invalid:
inv_entry 0, BAD_SYNC
ENDPROC(el0_sync_invalid)
el0_irq_invalid:
inv_entry 0, BAD_IRQ
ENDPROC(el0_irq_invalid)
el0_fiq_invalid:
inv_entry 0, BAD_FIQ
ENDPROC(el0_fiq_invalid)
el0_error_invalid:
inv_entry 0, BAD_ERROR
ENDPROC(el0_error_invalid)
#ifdefCONFIG_COMPAT
el0_fiq_invalid_compat:
inv_entry 0, BAD_FIQ, 32
ENDPROC(el0_fiq_invalid_compat)
el0_error_invalid_compat:
inv_entry 0, BAD_ERROR, 32
ENDPROC(el0_error_invalid_compat)
#endif
el1_sync_invalid:
inv_entry 1, BAD_SYNC
ENDPROC(el1_sync_invalid)
el1_irq_invalid:
inv_entry 1, BAD_IRQ //inv_entry是一个宏定义,主要工作就是将寄存器压栈后跳到bad_mode函数运行。
//后面紧跟的1代表异常级是el1,即内核态。
//BAD_IRQ定义在前面,值为1,代表发生异常的原因
ENDPROC(el1_irq_invalid)
el1_fiq_invalid:
inv_entry 1, BAD_FIQ
ENDPROC(el1_fiq_invalid)
el1_error_invalid:
inv_entry 1, BAD_ERROR
ENDPROC(el1_error_invalid)
/*
* EL1 mode handlers.
*/
.align 6
el1_sync:
kernel_entry 1 //把寄存器信息压栈
//读异常类型寄存器
mrs x1,esr_el1 // read thesyndrome register
//逻辑右移26位,取31-27位
lsr x24,x1, #26 // exception class
//判断异常类型
cmp x24,#0x25 // data abort in EL1
//如果是el1的数据中止(data_abort)异常,跳转到el1_da标号处
b.eq el1_da
cmp x24,#0x18 // configurable trap
b.eq el1_undef
cmp x24,#0x26 // stack alignmentexception
b.eq el1_sp_pc
cmp x24,#0x22 // pc alignmentexception
b.eq el1_sp_pc
cmp x24,#0x00 // unknown exceptionin EL1
b.eq el1_undef
cmp x24,#0x30 // debug exceptionin EL1
b.ge el1_dbg
b el1_inv
el1_da:
/*
*Data abort handling,数据中止异常处理函数
*/
mrs x0,far_el1 //看过函数do_mem_abort内容后确定,far_el1寄存器是异常地址寄存器
/* 该宏定义在arm64/include/asm/assembler.h中:
*.macro enable_dbg_if_not_stepping, tmp
*mrs /tmp, mdscr_el1 //通过该宏名称确定,mdscr_el1寄存器是关于硬件debug的,不影响异常处理不分析
*tbnz /tmp, #1, 9990f
*enable_dbg
*9990:
*.endm
*/
//通过以上内容及该宏名称可以猜测,其作用只是根据条件决定是否开启dbg模式,不影响异常执行,不做分析
enable_dbg_if_not_stepping x2
// re-enable interrupts if they wereenabled in the aborted context
//根据x23(在kernel_entry中定义,存储spsr的值)判断是否开启中断
tbnz x23,#7, 1f // PSR_I_BIT
enable_irq
1:
mov x2,sp // structpt_regs,sp中存储的是执行完kernel_entry后的值,其指向压栈后的栈顶,作为参数传给函数do_mem_abort
//do_mem_abort函数在arm64/mm/fault.c中,分析见代码最后面
bl do_mem_abort //传给该函数的x0发生异常的地址信息,x1是异常类型,x2就是压入栈中的寄存器堆首地址。
// disable interrupts before pulling preserveddata off the stack
disable_irq
kernel_exit 1 //异常返回,把所有压入栈中的寄存器弹出。对应于kernel_entry。
el1_sp_pc:
/*
*Stack or PC alignment exception handling
*/
mrs x0,far_el1
mov x1,x25
mov x2,sp
b do_sp_pc_abort
el1_undef:
/*
*Undefined instruction
*/
mov x0,sp
b do_undefinstr
el1_dbg:
/*
*Debug exception handling
*/
tbz x24,#0, el1_inv // EL1 only
mrs x0,far_el1
mov x2,sp // structpt_regs
bl do_debug_exception
kernel_exit 1
el1_inv:
// TODO: add support for undefined instructionsin kernel mode
mov x0,sp
mov x1,#BAD_SYNC
mrs x2,esr_el1
b bad_mode
ENDPROC(el1_sync)
.align 6
el1_irq:
kernel_entry 1
enable_dbg_if_not_stepping x0
#ifdefCONFIG_TRACE_IRQFLAGS
bl trace_hardirqs_off
#endif
#ifdefCONFIG_PREEMPT
get_thread_info tsk
ldr x24,[tsk, #TI_PREEMPT] // getpreempt count
add x0,x24, #1 // increment it
str x0,[tsk, #TI_PREEMPT]
#endif
irq_handler
#ifdefCONFIG_PREEMPT
str x24,[tsk, #TI_PREEMPT] // restorepreempt count
cbnz x24,1f // preempt count!= 0
ldr x0,[tsk, #TI_FLAGS] // get flags
tbz x0,#TIF_NEED_RESCHED, 1f // needsrescheduling?
bl el1_preempt
1:
#endif
#ifdefCONFIG_TRACE_IRQFLAGS
bl trace_hardirqs_on
#endif
kernel_exit 1
ENDPROC(el1_irq)
#ifdefCONFIG_PREEMPT
el1_preempt:
mov x24,lr
1: enable_dbg
bl preempt_schedule_irq // irq en/disable is done inside
ldr x0,[tsk, #TI_FLAGS] // get newtasks TI_FLAGS
tbnz x0,#TIF_NEED_RESCHED, 1b // needsrescheduling?
ret x24
#endif
/*
* EL0 mode handlers.
*/
.align 6
el0_sync:
kernel_entry 0
mrs x25,esr_el1 // read the syndromeregister
lsr x24,x25, #26 // exceptionclass
cmp x24,#0x15 // SVC in 64-bitstate
b.eq el0_svc
adr lr,ret_from_exception
cmp x24,#0x24 // data abort in EL0
b.eq el0_da
cmp x24,#0x20 // instruction abortin EL0
b.eq el0_ia
cmp x24,#0x07 // FP/ASIMD access
b.eq el0_fpsimd_acc
cmp x24,#0x2c // FP/ASIMDexception
b.eq el0_fpsimd_exc
cmp x24,#0x18 // configurable trap
b.eq el0_undef
cmp x24,#0x26 // stack alignmentexception
b.eq el0_sp_pc
cmp x24,#0x22 // pc alignmentexception
b.eq el0_sp_pc
cmp x24,#0x00 // unknown exceptionin EL0
b.eq el0_undef
cmp x24,#0x30 // debug exceptionin EL0
b.ge el0_dbg
b el0_inv
#ifdefCONFIG_COMPAT
.align 6
el0_sync_compat:
kernel_entry 0, 32
mrs x25,esr_el1 // read the syndromeregister
lsr x24,x25, #26 // exceptionclass
cmp x24,#0x11 // SVC in 32-bitstate
b.eq el0_svc_compat
adr lr,ret_from_exception
cmp x24,#0x24 // data abort in EL0
b.eq el0_da
cmp x24,#0x20 // instruction abortin EL0
b.eq el0_ia
cmp x24,#0x07 // FP/ASIMD access
b.eq el0_fpsimd_acc
cmp x24,#0x28 // FP/ASIMDexception
b.eq el0_fpsimd_exc
cmp x24,#0x00 // unknown exceptionin EL0
b.eq el0_undef
cmp x24,#0x30 // debug exceptionin EL0
b.ge el0_dbg
b el0_inv
el0_svc_compat:
/*
*AArch32 syscall handling
*/
adr stbl,compat_sys_call_table // load compatsyscall table pointer
uxtw scno,w7 // syscall numberin w7 (r7)
movsc_nr, #__NR_compat_syscalls
b el0_svc_naked
.align 6
el0_irq_compat:
kernel_entry 0, 32
b el0_irq_naked
#endif
el0_da:
/*
*Data abort handling
*/
mrs x0,far_el1
disable_step x1
isb
enable_dbg
// enable interrupts before calling themain handler
enable_irq
mov x1,x25
mov x2,sp
b do_mem_abort
el0_ia:
/*
*Instruction abort handling
*/
mrs x0,far_el1
disable_step x1
isb
enable_dbg
// enable interrupts before calling themain handler
enable_irq
orr x1,x25, #1 << 24 // usereserved ISS bit for instruction aborts
mov x2,sp
b do_mem_abort
el0_fpsimd_acc:
/*
*Floating Point or Advanced SIMD access
*/
mov x0,x25
mov x1,sp
b do_fpsimd_acc
el0_fpsimd_exc:
/*
*Floating Point or Advanced SIMD exception
*/
mov x0,x25
mov x1,sp
b do_fpsimd_exc
el0_sp_pc:
/*
*Stack or PC alignment exception handling
*/
mrs x0,far_el1
disable_step x1
isb
enable_dbg
// enable interrupts before calling themain handler
enable_irq
mov x1,x25
mov x2,sp
b do_sp_pc_abort
el0_undef:
/*
*Undefined instruction
*/
mov x0,sp
b do_undefinstr
el0_dbg:
/*
*Debug exception handling
*/
tbnz x24,#0, el0_inv // EL0 only
mrs x0,far_el1
disable_step x1
mov x1,x25
mov x2,sp
b do_debug_exception
el0_inv:
mov x0,sp
mov x1,#BAD_SYNC
mrs x2,esr_el1
b bad_mode
ENDPROC(el0_sync)
.align 6
el0_irq:
kernel_entry 0
el0_irq_naked:
disable_step x1
isb
enable_dbg
#ifdefCONFIG_TRACE_IRQFLAGS
bl trace_hardirqs_off
#endif
get_thread_info tsk
#ifdefCONFIG_PREEMPT
ldr x24,[tsk, #TI_PREEMPT] // getpreempt count
add x23,x24, #1 // increment it
str x23,[tsk, #TI_PREEMPT]
#endif
irq_handler
#ifdefCONFIG_PREEMPT
ldr x0,[tsk, #TI_PREEMPT]
str x24,[tsk, #TI_PREEMPT]
cmp x0,x23
b.eq 1f
mov x1,#0
str x1,[x1] // BUG
1:
#endif
#ifdefCONFIG_TRACE_IRQFLAGS
bl trace_hardirqs_on
#endif
b ret_to_user
ENDPROC(el0_irq)
/*
* This is the return code to user mode forabort handlers
*/
ret_from_exception:
get_thread_info tsk
b ret_to_user
ENDPROC(ret_from_exception)
/*
* Register switch for AArch64. Thecallee-saved registers need to be saved
* and restored. On entry:
* x0 =previous task_struct (must be preserved across the switch)
* x1 =next task_struct
* Previous and next are guaranteed not to bethe same.
*
*/
ENTRY(cpu_switch_to)
add x8,x0, #THREAD_CPU_CONTEXT
mov x9,sp
stp x19,x20, [x8], #16 // storecallee-saved registers
stp x21,x22, [x8], #16
stp x23,x24, [x8], #16
stp x25,x26, [x8], #16
stp x27,x28, [x8], #16
stp x29,x9, [x8], #16
str lr,[x8]
add x8,x1, #THREAD_CPU_CONTEXT
ldp x19,x20, [x8], #16 // restorecallee-saved registers
ldp x21,x22, [x8], #16
ldp x23,x24, [x8], #16
ldp x25,x26, [x8], #16
ldp x27,x28, [x8], #16
ldp x29,x9, [x8], #16
ldr lr,[x8]
mov sp,x9
ret
ENDPROC(cpu_switch_to)
/*
* This is the fast syscall return path. We do as little as possible here,
* and this includes saving x0 back into thekernel stack.
*/
ret_fast_syscall:
disable_irq // disable interrupts
ldr x1,[tsk, #TI_FLAGS]
and x2,x1, #_TIF_WORK_MASK
cbnz x2,fast_work_pending
tbz x1,#TIF_SINGLESTEP, fast_exit
disable_dbg
enable_step x2
fast_exit:
kernel_exit 0, ret = 1
/*
* Ok, we need to do extra processing, enterthe slow path.
*/
fast_work_pending:
str x0,[sp, #S_X0] // returned x0
work_pending:
tbnz x1,#TIF_NEED_RESCHED, work_resched
/* TIF_SIGPENDING or TIF_NOTIFY_RESUMEcase */
ldr x2,[sp, #S_PSTATE]
mov x0,sp // 'regs'
tst x2,#PSR_MODE_MASK // user moderegs?
b.ne no_work_pending // returning to kernel
enable_irq //enable interrupts for do_notify_resume()
bl do_notify_resume
b ret_to_user
work_resched:
enable_dbg
bl schedule
/*
* "slow" syscall return path.
*/
ENTRY(ret_to_user)
disable_irq // disable interrupts
ldr x1,[tsk, #TI_FLAGS]
and x2,x1, #_TIF_WORK_MASK
cbnz x2,work_pending
tbz x1,#TIF_SINGLESTEP, no_work_pending
disable_dbg
enable_step x2
no_work_pending:
kernel_exit 0, ret = 0
ENDPROC(ret_to_user)
/*
* This is how we return from a fork.
*/
ENTRY(ret_from_fork)
bl schedule_tail
get_thread_info tsk
b ret_to_user
ENDPROC(ret_from_fork)
/*
* SVC handler.
*/
.align 6
el0_svc:
adrp stbl,sys_call_table // load syscalltable pointer
uxtw scno,w8 //syscall number in w8
mov sc_nr,#__NR_syscalls
el0_svc_naked: // compat entrypoint
stp x0,scno, [sp, #S_ORIG_X0] // save theoriginal x0 and syscall number
disable_step x16
isb
enable_dbg
enable_irq
get_thread_info tsk
ldr x16,[tsk, #TI_FLAGS] // check forsyscall tracing
tbnz x16,#TIF_SYSCALL_TRACE, __sys_trace // are we tracing syscalls?
adr lr,ret_fast_syscall // returnaddress
cmpscno, sc_nr //check upper syscall limit
b.hs ni_sys
ldr x16,[stbl, scno, lsl #3] // address in thesyscall table
br x16 // call sys_* routine
ni_sys:
mov x0,sp
b do_ni_syscall
ENDPROC(el0_svc)
/*
*This is the really slow path. We'regoing to be doing context
*switches, and waiting for our parent to respond.
*/
__sys_trace:
mov x1,sp
mov w0,#0 // trace entry
bl syscall_trace
adr lr,__sys_trace_return // returnaddress
uxtw scno,w0 // syscall number(possibly new)
mov x1,sp // pointer toregs
cmp scno,sc_nr // check uppersyscall limit
b.hs ni_sys
ldp x0,x1, [sp] // restore thesyscall args
ldp x2,x3, [sp, #S_X2]
ldp x4,x5, [sp, #S_X4]
ldp x6,x7, [sp, #S_X6]
ldr x16,[stbl, scno, lsl #3] // address in thesyscall table
br x16 // call sys_* routine
__sys_trace_return:
str x0,[sp] // save returned x0
mov x1,sp
mov w0,#1 // trace exit
bl syscall_trace
b ret_to_user
/*
* Special system call wrappers.
*/
ENTRY(sys_execve_wrapper)
mov x3,sp
b sys_execve
ENDPROC(sys_execve_wrapper)
ENTRY(sys_clone_wrapper)
mov x5,sp
b sys_clone
ENDPROC(sys_clone_wrapper)
ENTRY(sys_rt_sigreturn_wrapper)
mov x0,sp
b sys_rt_sigreturn
ENDPROC(sys_rt_sigreturn_wrapper)
ENTRY(sys_sigaltstack_wrapper)
ldr x2,[sp, #S_SP]
b sys_sigaltstack
ENDPROC(sys_sigaltstack_wrapper)
ENTRY(handle_arch_irq)
.quad 0
/*
* Dispatch a data abort to the relevanthandler.
*/
/*
asmlinkagevoid __exception do_mem_abort(unsigned long addr, unsigned int esr,
struct pt_regs *regs)
{
const struct fault_info *inf = fault_info+ (esr & 63);//取esr所有有效位,用于选择fault_info数组中的相应处理函数,该数组定义在后面
struct siginfo info;
if (!inf->fn(addr, esr, regs)) //如果处理成功(返回0),则直接返回,否则继续执行。
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);
}
*/
/*
staticstruct fault_info {
int (*fn)(unsignedlong addr, unsigned int esr, struct pt_regs *regs);//相应的异常处理函数
int sig;
int code;
const char *name;
}fault_info[] = {
{ do_bad, SIGBUS, 0, "ttbraddress size fault" },
{ do_bad, SIGBUS, 0, "level1 address size fault" },
{ do_bad, SIGBUS, 0, "level2 address size fault" },
{ do_bad, SIGBUS, 0, "level3 address size fault" },
{ do_translation_fault, SIGSEGV, SEGV_MAPERR, "input address range fault" },
{ do_translation_fault, SIGSEGV, SEGV_MAPERR, "level 1 translation fault" },
{ do_translation_fault, SIGSEGV, SEGV_MAPERR, "level 2 translation fault" },
{ do_page_fault, SIGSEGV, SEGV_MAPERR, "level3 translation fault" },
{ do_bad, SIGBUS, 0, "reservedaccess flag fault" },
{ do_bad, SIGSEGV,SEGV_ACCERR, "level 1 access flagfault" },
{ do_bad, SIGSEGV,SEGV_ACCERR, "level 2 access flagfault" },
{ do_page_fault, SIGSEGV, SEGV_ACCERR, "level3 access flag fault" },
{ do_bad, SIGBUS, 0, "reservedpermission fault" },
{ do_bad, SIGSEGV,SEGV_ACCERR, "level 1 permissionfault" },
{ do_sect_fault, SIGSEGV, SEGV_ACCERR, "level2 permission fault" },
{ do_page_fault, SIGSEGV, SEGV_ACCERR, "level3 permission fault" },
{ do_bad, SIGBUS, 0,&nbs