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

  ARMv8 Linux内核异常处理过程分析_第1张图片


1.3 执行kernel_entry时的栈

ARMv8 Linux内核异常处理过程分析_第2张图片

1.4 执行kernel_exit 时的栈

ARMv8 Linux内核异常处理过程分析_第3张图片

 

 

 

  


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代码分析

//该文件中代码原理很简单,目前暂不分析,若需要,后续再添上。

 

/*

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

}

你可能感兴趣的:(linux,armv8)