ARMv8 Linux内核异常处理过程分析

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 <[email protected]>

*         WillDeacon <[email protected]>

*

* 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");

}

时间: 2024-10-13 20:28:46

ARMv8 Linux内核异常处理过程分析的相关文章

详解Linux内核异常处理体系结构

本节内容:Linux内核异常处理的的初始化过程和异常发生时的处理流程. [首先来区分一下两个概念:中断(Interrupt)和异常(Exception).中断属于异常的一种,就拿2440开发板来说,他有60多种中断源,例如来自DMA控制器.UART.IIC和外部中断等.2440有一个专门的中断控制器来处理这些中断,中断控制器在接收到这些中断信号之后就需要ARM920T进入IRQ或FIQ模式进行处理,这两种模式也是中断异常的仅有模式.而异常的概念要广的多,它包括复位.未定义指令.软中断.IRQ等等

ARMv8 Linux内核源码分析:__flush_dcache_all()

1.1 /* *  __flush_dcache_all() *  Flush the wholeD-cache. * Corrupted registers: x0-x7, x9-x11 */ ENTRY(__flush_dcache_all) //保证之前的访存指令的顺序 dsb sy //读cache level id register mrs x0, clidr_el1           // read clidr //取bits[26:24](Level of Coherency f

Linux内核编译过程分析

http://pan.baidu.com/s/1ntqLA7v 其中是我总结生成的一些文档,以便于理解当我们输入make uImage后,系统是怎么一步一步生成uImage的,我采用的是逆向分析的方法. Linux内核的编译结构还是挺复杂的,我分析的还不到位,文档还有欠缺. 下面是缩略图,在共享的目录中有一个swf的文件,可以用浏览器打开,然后就可以动态的阅读了,同时还提供了通过网页.PDF以及word阅读的文件. Linux内核编译过程分析,布布扣,bubuko.com

linux 内核学习之五 system_call过程分析

一   使用gdb工具跟踪分析一个自添加的系统调用 应用程序的进程通常在用户空间下运行,当它调用一个系统调用时,进程进入内核空间,执行的是kernel内部的代码,从而具有执行特权指令的权限,完成特定的功能. 在上次实验的基础上修改test.c,添加自己实现的setuid系统调用,部分代码修改如下: int uid_c() { int i=65535,k=0; i=getuid(); printf("current user id is:%d\n",i); setuid(200); k=

Linux内核的启动过程分析

秦鼎涛 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 一.实验目的及要求: 使用gdb跟踪调试内核从start_kernel到init进程启动 详细分析从start_kernel到init进程启动的过程并结合实验截图撰写一篇署名博客,并在博客文章中注明“真实姓名(与最后申请证书的姓名务必一致) + 原创作品转载请注明出处 + <Linux内核分析>MOOC课程http://mooc.study.1

linux 内核学习之八 进程调度过程分析

一  关于进程的补充 进程调度的时机 中断处理过程(包括时钟中断.I/O中断.系统调用和异常)中,直接调用schedule(),或者返回用户态时根据need_resched标记调用schedule(): 内核线程可以直接调用schedule()进行进程切换,也可以在中断处理过程中进行调度,也就是说内核线程作为一类的特殊的进程可以主动调度,也可以被动调度: 用户态进程无法实现主动调度,仅能通过陷入内核态后的某个时机点进行调度,即在中断处理过程中进行调度. 进程的切换 为了控制进程的执行,内核必须有

Linux内核分析:实验六--Linux进程的创建过程分析

刘畅 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 概述 本次实验在MenuOS中加入fork系统调用,并通过GDB的调试跟踪,近距离的观察Linux中进程创建的过程.阅读Linux进程部分的源码,结合起来理解Linux内核创建新进程的过程. Linux中对进程的描述 Linux中task_struct结构体用于描述系统中的进程,对应x86机器的此结构体定义放在了/include/li

Linux内核分析(六)----字符设备控制方法实现|揭秘系统调用本质

原文:Linux内核分析(六)----字符设备控制方法实现|揭秘系统调用本质 Linux内核分析(六) 昨天我们对字符设备进行了初步的了解,并且实现了简单的字符设备驱动,今天我们继续对字符设备的某些方法进行完善. 今天我们会分析到以下内容: 1.      字符设备控制方法实现 2.      揭秘系统调用本质 在昨天我们实现的字符设备中有open.read.write等方法,由于这些方法我们在以前编写应用程序的时候,相信大家已经有所涉及所以就没单独列出来分析,今天我们主要来分析一下我们以前接触

Linux开机启动过程分析

Linux开机启动过程分析 开机过程指的是从打开计算机电源直到LINUX显示用户登录画面的全过程.分析LINUX开机过程也是深入了解LINUX核心工作原理的一个很好的途径. 启动第一步--加载BIOS 当 你打开计算机电源,计算机会首先加载BIOS信息,BIOS信息是如此的重要,以至于计算机必须在最开始就找到它.这是因为BIOS中包含了CPU的相关 信息.设备启动顺序信息.硬盘信息.内存信息.时钟信息.PnP特性等等.在此之后,计算机心里就有谱了,知道应该去读取哪个硬件设备了.在BIOS将系 统