ARMv8 Linux内核异常处理过程分析「建议收藏」

2022-07-07 17:10:24 浏览数 (1)

大家好,又见面了,我是全栈君。

NOTE:为了方便大家阅读,制作了PDF版文档。下载请猛戳这里

老样子,为了赚点积分下载其它人的文件,下载以上资料须要资源分2分。 假设没有积分请留言全部文档,留下邮箱就可以。

看了Linaro提供的开源ARMv8 Linux内核源代码,发现ARMv8异常处理与ARMv7及之前的架构有所不同,简单分析。

LinaroARMv8工程:http://www.linaro.org/engineering/engineering-projects/armv8

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)

mov sc_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

cmp scno, 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(0xx) at 0x6lxn”,

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, “synchronousexternal abort” },

{ do_bad, SIGBUS, 0, “asynchronousexternal abort” },

{ do_bad, SIGBUS, 0, “unknown18” },

{ do_bad, SIGBUS, 0, “unknown19” },

{ do_bad, SIGBUS, 0, “synchronousabort (translation table walk)” },

{ do_bad, SIGBUS, 0, “synchronousabort (translation table walk)” },

{ do_bad, SIGBUS, 0, “synchronousabort (translation table walk)” },

{ do_bad, SIGBUS, 0, “synchronousabort (translation table walk)” },

{ do_bad, SIGBUS, 0, “synchronousparity error” },

{ do_bad, SIGBUS, 0, “asynchronousparity error” },

{ do_bad, SIGBUS, 0, “unknown26” },

{ do_bad, SIGBUS, 0, “unknown27” },

{ do_bad, SIGBUS, 0, “synchronousparity error (translation table walk” },

{ do_bad, SIGBUS, 0, “synchronousparity error (translation table walk” },

{ do_bad, SIGBUS, 0, “synchronousparity error (translation table walk” },

{ do_bad, SIGBUS, 0, “synchronousparity error (translation table walk” },

{ do_bad, SIGBUS, 0, “unknown32” },

{ do_bad, SIGBUS, BUS_ADRALN, “alignmentfault” },

{ do_bad, SIGBUS, 0, “debugevent” },

{ do_bad, SIGBUS, 0, “unknown35” },

{ do_bad, SIGBUS, 0, “unknown36” },

{ do_bad, SIGBUS, 0, “unknown37” },

{ do_bad, SIGBUS, 0, “unknown38” },

{ do_bad, SIGBUS, 0, “unknown39” },

{ do_bad, SIGBUS, 0, “unknown40” },

{ do_bad, SIGBUS, 0, “unknown41” },

{ do_bad, SIGBUS, 0, “unknown42” },

{ do_bad, SIGBUS, 0, “unknown43” },

{ do_bad, SIGBUS, 0, “unknown44” },

{ do_bad, SIGBUS, 0, “unknown45” },

{ do_bad, SIGBUS, 0, “unknown46” },

{ do_bad, SIGBUS, 0, “unknown47” },

{ do_bad, SIGBUS, 0, “unknown48” },

{ do_bad, SIGBUS, 0, “unknown49” },

{ do_bad, SIGBUS, 0, “unknown50” },

{ do_bad, SIGBUS, 0, “unknown51” },

{ do_bad, SIGBUS, 0, “implementationfault (lockdown abort)” },

{ do_bad, SIGBUS, 0, “unknown53” },

{ do_bad, SIGBUS, 0, “unknown54” },

{ do_bad, SIGBUS, 0, “unknown55” },

{ do_bad, SIGBUS, 0, “unknown56” },

{ do_bad, SIGBUS, 0, “unknown57” },

{ do_bad, SIGBUS, 0, “implementationfault (coprocessor abort)” },

{ do_bad, SIGBUS, 0, “unknown59” },

{ do_bad, SIGBUS, 0, “unknown60” },

{ do_bad, SIGBUS, 0, “unknown61” },

{ do_bad, SIGBUS, 0, “unknown62” },

{ do_bad, SIGBUS, 0, “unknown63” },

};

*/

1.6 traps.c代码分析

//该文件里代码原理非常easy,眼下暂不分析。若须要。兴许再添上。

/*

* bad_mode handles the impossible case in theexception vector.

*/

//三个參数从左到右分别相应x0~x3,该函数的作用就是打印出错原因,跳转到panic()函数

asmlinkagevoid bad_mode(struct pt_regs *regs, int reason, unsigned int esr)

{

console_verbose();

pr_crit(“Bad mode in %s handlerdetected, code 0xxn”,

handler[reason], esr);

die(“Oops – bad mode”, regs, 0);

local_irq_disable();

panic(“bad mode”);

}

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/116547.html原文链接:https://javaforall.cn

0 人点赞