• Linux软中断与系统调用


    1. SWI软中断

    以ARMV7 A/R架构为例, SWI软中断和中断一样,内核空间处理始于异常向量表。Linux向量表默认地址0XFFFF0000,SWI向量偏移8字节为0xFFFF0008:

    具体代码,位于 linux-3.4.xarcharmkernelentry-armv.S:

    __vectors_start:
     ARM(    swi    SYS_ERROR0    )
     THUMB(    svc    #0        )
     THUMB(    nop            )
        W(b)    vector_und + stubs_offset
        W(ldr)    pc, .LCvswi + stubs_offset
        W(b)    vector_pabt + stubs_offset
        W(b)    vector_dabt + stubs_offset
        W(b)    vector_addrexcptn + stubs_offset
        W(b)    vector_irq + stubs_offset
        W(b)    vector_fiq + stubs_offset
    
        .globl    __vectors_end
    __vectors_end:
    
    
    
    .LCvswi:
        .word    vector_swi

    vector_swi的具体实现,位于 linux-3.4.xarcharmkernelentry-common.S,vector_swi函数完成的工作:

    1. 保存异常前的现场 

     1 ENTRY(vector_swi)
     2     sub    sp, sp, #S_FRAME_SIZE
     3     stmia    sp, {r0 - r12}            @ Calling r0 - r12
     4  ARM(    add    r8, sp, #S_PC        )
     5  ARM(    stmdb    r8, {sp, lr}^        )    @ Calling sp, lr
     6  THUMB(    mov    r8, sp            )
     7  THUMB(    store_user_sp_lr r8, r10, S_SP    )    @ calling sp, lr
     8     mrs    r8, spsr            @ called from non-FIQ mode, so ok.
     9     str    lr, [sp, #S_PC]            @ Save calling PC
    10     str    r8, [sp, #S_PSR]        @ Save CPSR
    11     str    r0, [sp, #S_OLD_R0]        @ Save OLD_R0
    12     zero_fp

       保存现场大小为S_FRAME_SIZE,大小是一个完整的寄存器栈帧:

    DEFINE(S_FRAME_SIZE,  sizeof(struct pt_regs));
    
    struct pt_regs {
     long uregs[18];
    };

    #define ARM_cpsr uregs[16]
    #define ARM_pc  uregs[15]
    #define ARM_lr  uregs[14]
    #define ARM_sp  uregs[13]
    #define ARM_ip  uregs[12]
    #define ARM_fp  uregs[11]
    #define ARM_r10  uregs[10]
    #define ARM_r9  uregs[9]
    #define ARM_r8  uregs[8]
    #define ARM_r7  uregs[7]
    #define ARM_r6  uregs[6]
    #define ARM_r5  uregs[5]
    #define ARM_r4  uregs[4]
    #define ARM_r3  uregs[3]
    #define ARM_r2  uregs[2]
    #define ARM_r1  uregs[1]
    #define ARM_r0  uregs[0]
    #define ARM_ORIG_r0 uregs[17]

     从现场可以看出,每次用户空间向内核空间切换时,线程内核栈会保留一份完整的寄存器现场,保存地址在内核栈的(最高地址-8):

    线程内核栈基地址:0xc288a000

    线程内核栈最高地址:0xc288a000+8k = 0xc288c000

    保存形式:

    =============================低地址

    R0        R1           R2          R3

    R4        R5           R6          R7

    R8        R9           R10        R11

    R12       SP           LR         PC

    CPSR   R0_OLD    X          X

    ========================== kernel stack start(最高地址)

    值得注意的是,arm-linux把内核栈的(最高地址-8)作为栈起始地址,详见 linux-3.4.xarcharmincludeasm hread_info.h

    #define THREAD_SIZE        8192
    #define THREAD_START_SP        (THREAD_SIZE - 8)

     原因:

    https://git.kernel.org/pub/scm/linux/kernel/git/history/history.git/commit/?id=415395e19fd197ce4f248902dba54f4065af547c

    Always leave 8 bytes free at the top of the kernel stack.  This prevents the stack becoming completely empty when do_exit() is called from an exiting nfsd() thread, and causing the wrong pointer to be returned from current_thread_info()

    [PATCH] ARM: Fix kernel stack offset calculations Various places in the ARM kernel implicitly assumed that kernel stacks are always 8K due to hard coded constants. Replace these constants with definitions. Correct the allowable range of kernel stack pointer values within the allocation. Arrange for the entire kernel stack to be zeroed, not just the upper 4K if CONFIG_DEBUG_STACK_USAGE is set. Signed-off-by: Russell King

    2. 获取系统调用号

    vector_swi函数中有两个重要的宏,分别为:

    CONFIG_OABI_COMPAT   

    CONFIG_AEABI

    OABI = Old application binary interface

    EABI = Extended application binary interface

    ABI = 应用程序二进制接口, OABI/EABI都是针对ARM处理器的接口,EABI也叫GNU EABI.

    两者的区别:

    1. 调用规则(参数传递,返回值传递)

    2. 系统调用数目以及应用程序怎么做系统调用

    3. 目标文件二进制个数

    4. 结构体中填充和对齐

    EABI的好处

    1. 支持软件浮点/硬件浮点混用

    2. 效率更高

    3. 工具兼容

    这两个宏可以在make menuconfig时进行配置

    kernel features -->

      [*] Use the ARM EABI to compile the kernel

      [*] Allow old ABI binary to run this kernel

    两个可以同时Yes, 也可以只选一个EABI或者都不选

    OABI方式系统调用

     SWI{cond} immed_24

    immed_24: 24位立即数,指定了系统调用号,参数用通用寄存器传递

    MOV R0,#34

    SWI 12

    EABI方式系统调用

    MOV R7,#34

    SWI 0X0

    系统调用号由R7寄存器决定

    在SWI异常处理程序中,得到系统调用号的方法:

    -   如果定义了OABI,说明存在OABI方式的调用,需要从SWI指令中获得调用号

    - 首先确定软中断的SWI指令时ARM指令还是Thumb指令,这可通过对SPSR访问得到(前一步骤R8保存了SPSR);

    -  OABI方式无法使用Thumb指令实现SWI,因此下一步取得ARM指令中SWI操作的地址,这可通过访问(LR-4)得到(最后一条指令即SWI XXX);

    - 接着读出指令,分解出立即数中低24位(这一步先保存整条指令在R10,待下一步骤分解);

    #if defined(CONFIG_OABI_COMPAT)
    
        /*
         * If we have CONFIG_OABI_COMPAT then we need to look at the swi
         * value to determine if it is an EABI or an old ABI call.
         */
    #ifdef CONFIG_ARM_THUMB
        tst    r8, #PSR_T_BIT
        movne    r10, #0                @ no thumb OABI emulation
        ldreq    r10, [lr, #-4]            @ get SWI instruction
    #else
        ldr    r10, [lr, #-4]            @ get SWI instruction
    #endif

    -  如果没有定义OABI,但是定义了EABI,系此时统调用号只会存放在R7寄存器(即scno), 不需要做任何处理,

    #elif defined(CONFIG_AEABI)
    
        /*
         * Pure EABI user space always put syscall number into scno (r7).
         */
    

    -  如果没有定义OABI/EABI,但是定义了CONFIG_ARM_THUMB

    #elif defined(CONFIG_ARM_THUMB)
    
        /* Legacy ABI only, possibly thumb mode. */
        tst    r8, #PSR_T_BIT            @ this is SPSR from save_user_regs
        addne    scno, r7, #__NR_SYSCALL_BASE    @ put OS number in
        ldreq    scno, [lr, #-4]

    -  如果OABI/EABI/CONFIG_ARM_THUMB都没有定义,此时保存整条SWI指令在R7,待下一步骤分解:

    #else
        /* Legacy ABI only. */
        ldr    scno, [lr, #-4]            @ get SWI instruction
    
    #endif


     

    3. 以系统调用号为索引查找系统调用表,然后调用相应的处理例程

    - 首先打开中断,并把tbl变量赋值sys_call_table

     enable_irq
        get_thread_info tsk
        adr    tbl, sys_call_table        @ load syscall table pointer

    -  如果定义了OABI

    #if defined(CONFIG_OABI_COMPAT)
        /*
         * If the swi argument is zero, this is an EABI call and we do nothing.
         *
         * If this is an old ABI call, get the syscall number into scno and
         * get the old ABI syscall table address.
         */
        bics    r10, r10, #0xff000000
        eorne    scno, r10, #__NR_OABI_SYSCALL_BASE
        ldrne    tbl, =sys_oabi_call_table

     - 首先把R10(SWI指令)取低24位,得到系统调用号
     - 然后检查该系统调用号是不是0,如果是0表明这是一个EABI系统调用,不做任何处理;如果不是0则把系统调用号与__NR_OABI_SYSCALL_BASE做异或处理,tbl赋值sys_oabi_call_table

    - 如果OABI/EABI都没有定义,把R7(系统调用号)取低24位,并与__NR_OABI_SYSCALL_BASE做异或处理

    #elif !defined(CONFIG_AEABI)
        bic    scno, scno, #0xff000000        @ mask off SWI op-code
        eor    scno, scno, #__NR_SYSCALL_BASE    @ check OS number
    #endif

    -  其他情况不做处理

    - 然后根据转换后的系统调用号(都存在R7),在对应的调用跳转表(tbl)中进行偏移后,得到相应的调用函数

        cmp    scno, #NR_syscalls        @ check upper syscall limit
        adr    lr, BSYM(ret_fast_syscall)    @ return address
        ldrcc    pc, [tbl, scno, lsl #2]        @ call sys_* routine

    系统调用跳转表在vector_swi中有两份:

    sys_call_table

    sys_oabi_call_table

    - 如果OABI和EABI都选择,应用程序使用OABI方式时调用sys_oabi_call_table,应用程序使用EABI时调用sys_call_table

    - 如果只选择EABI,使用sys_call_table

    - 如果都不选,使用sys_call_table

    - 由于OABI对EABI有依赖关系,所以不能单独选择OABI

     1 ENTRY(sys_call_table)
     2 #include "calls.S"
     3 #undef ABI
     4 #undef OBSOLETE
     5 
     6 /*============================================================================
     7  * Special system call wrappers
     8  */
     9 @ r0 = syscall number
    10 @ r8 = syscall table
    11 sys_syscall:
    12         bic    scno, r0, #__NR_OABI_SYSCALL_BASE
    13         cmp    scno, #__NR_syscall - __NR_SYSCALL_BASE
    14         cmpne    scno, #NR_syscalls    @ check range
    15         stmloia    sp, {r5, r6}        @ shuffle args
    16         movlo    r0, r1
    17         movlo    r1, r2
    18         movlo    r2, r3
    19         movlo    r3, r4
    20         ldrlo    pc, [tbl, scno, lsl #2]
    21         b    sys_ni_syscall
    22 ENDPROC(sys_syscall)
  • 相关阅读:
    Java中的枚举与values()方法
    为什么要使用双亲委派机制?
    java中finalize()方法
    jdk中rt.jar的作用
    IDEA查看maven依赖树,找出冲突jar包,以及 exclusion 冲突的包
    Linux 环境下SQLPLUS 回退键无法使用处理方法
    Hive字段注释会显示成from deserializer
    LinkedList类的poll、pop等方法
    static代码块执行顺序
    机器学习--线性回归
  • 原文地址:https://www.cnblogs.com/DF11G/p/10172520.html
Copyright © 2020-2023  润新知