前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >ARMv8 Linux内核异常处理过程分析「建议收藏」

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

作者头像
全栈程序员站长
发布2022-07-07 17:10:24
1.9K0
发布2022-07-07 17:10:24
举报

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

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之前的栈

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

1.3 运行kernel_entry时的栈

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

1.4 运行kernel_exit 时的栈

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

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(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, “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 0x%08x\n”,

handler[reason], esr);

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

local_irq_disable();

panic(“bad mode”);

}

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

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022年1月1,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.1 Linux内核异常处理相关文件
  • 1.2 运行kernel_entry之前的栈
  • 1.3 运行kernel_entry时的栈
  • 1.4 运行kernel_exit 时的栈
  • 1.5 entry.s代码分析
  • 1.6 traps.c代码分析
相关产品与服务
腾讯云代码分析
腾讯云代码分析(内部代号CodeDog)是集众多代码分析工具的云原生、分布式、高性能的代码综合分析跟踪管理平台,其主要功能是持续跟踪分析代码,观测项目代码质量,支撑团队传承代码文化。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档