zoukankan      html  css  js  c++  java
  • 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 <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   egsize== 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   et

          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       d,sp

          and d, d, #~((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, egsize        //kernel_entry是宏,主要完毕寄存器压栈操作。

          mov       x0,sp                                       //x0,x1,x2是传给函数bad_mode函数的參数。

    sp是当前栈指针。

          mov       x1,# eason                      //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   mp, mdscr_el1  //通过该宏名称确定,mdscr_el1寄存器是关于硬件debug的。不影响异常处理不分析

           *tbnz  mp, #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 ",

                  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 ",

                 handler[reason], esr);

          die("Oops - bad mode", regs, 0);

          local_irq_disable();

          panic("bad mode");

    }

  • 相关阅读:
    小程序(1)
    手机端放在线条中间的标题
    不定长度导航的两端对齐
    扇形导航菜单
    个性搜索框
    javascript数组原型方法
    jquery插件开发的demo
    监听表单中的内容变化
    mui中的关闭页面的几种方法
    css之伪类选择器:before :after(::before ::after)
  • 原文地址:https://www.cnblogs.com/yxwkf/p/5077582.html
Copyright © 2011-2022 走看看