Linux软中断与系统调用

1. SWI软中断

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

具体代码,位于 \linux-3.4.x\arch\arm\kernel\entry-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_plat\base\linux\linux-3.4.x\arch\arm\kernel\entry-common.S:

  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
 13 
 14     /*
 15      * Get the system call number.
 16      */
 17 
 18 #if defined(CONFIG_OABI_COMPAT)
 19 
 20     /*
 21      * If we have CONFIG_OABI_COMPAT then we need to look at the swi
 22      * value to determine if it is an EABI or an old ABI call.
 23      */
 24 #ifdef CONFIG_ARM_THUMB
 25     tst    r8, #PSR_T_BIT
 26     movne    r10, #0                @ no thumb OABI emulation
 27     ldreq    r10, [lr, #-4]            @ get SWI instruction
 28 #else
 29     ldr    r10, [lr, #-4]            @ get SWI instruction
 30   A710(    and    ip, r10, #0x0f000000        @ check for SWI        )
 31   A710(    teq    ip, #0x0f000000                        )
 32   A710(    bne    .Larm710bug                        )
 33 #endif
 34 #ifdef CONFIG_CPU_ENDIAN_BE8
 35     rev    r10, r10            @ little endian instruction
 36 #endif
 37 
 38 #elif defined(CONFIG_AEABI)
 39 
 40     /*
 41      * Pure EABI user space always put syscall number into scno (r7).
 42      */
 43   A710(    ldr    ip, [lr, #-4]            @ get SWI instruction    )
 44   A710(    and    ip, ip, #0x0f000000        @ check for SWI        )
 45   A710(    teq    ip, #0x0f000000                        )
 46   A710(    bne    .Larm710bug                        )
 47 
 48 #elif defined(CONFIG_ARM_THUMB)
 49 
 50     /* Legacy ABI only, possibly thumb mode. */
 51     tst    r8, #PSR_T_BIT            @ this is SPSR from save_user_regs
 52     addne    scno, r7, #__NR_SYSCALL_BASE    @ put OS number in
 53     ldreq    scno, [lr, #-4]
 54 
 55 #else
 56 
 57     /* Legacy ABI only. */
 58     ldr    scno, [lr, #-4]            @ get SWI instruction
 59   A710(    and    ip, scno, #0x0f000000        @ check for SWI        )
 60   A710(    teq    ip, #0x0f000000                        )
 61   A710(    bne    .Larm710bug                        )
 62 
 63 #endif
 64 
 65 #ifdef CONFIG_ALIGNMENT_TRAP
 66     ldr    ip, __cr_alignment
 67     ldr    ip, [ip]
 68     mcr    p15, 0, ip, c1, c0        @ update control register
 69 #endif
 70     enable_irq
 71 
 72     get_thread_info tsk
 73     adr    tbl, sys_call_table        @ load syscall table pointer
 74 
 75 #if defined(CONFIG_OABI_COMPAT)
 76     /*
 77      * If the swi argument is zero, this is an EABI call and we do nothing.
 78      *
 79      * If this is an old ABI call, get the syscall number into scno and
 80      * get the old ABI syscall table address.
 81      */
 82     bics    r10, r10, #0xff000000
 83     eorne    scno, r10, #__NR_OABI_SYSCALL_BASE
 84     ldrne    tbl, =sys_oabi_call_table
 85 #elif !defined(CONFIG_AEABI)
 86     bic    scno, scno, #0xff000000        @ mask off SWI op-code
 87     eor    scno, scno, #__NR_SYSCALL_BASE    @ check OS number
 88 #endif
 89 
 90     ldr    r10, [tsk, #TI_FLAGS]        @ check for syscall tracing
 91     stmdb    sp!, {r4, r5}            @ push fifth and sixth args
 92 
 93 #ifdef CONFIG_SECCOMP
 94     tst    r10, #_TIF_SECCOMP
 95     beq    1f
 96     mov    r0, scno
 97     bl    __secure_computing    
 98     add    r0, sp, #S_R0 + S_OFF        @ pointer to regs
 99     ldmia    r0, {r0 - r3}            @ have to reload r0 - r3
100 1:
101 #endif
102 
103     tst    r10, #_TIF_SYSCALL_WORK        @ are we tracing syscalls?
104     bne    __sys_trace
105 
106     cmp    scno, #NR_syscalls        @ check upper syscall limit
107     adr    lr, BSYM(ret_fast_syscall)    @ return address
108     ldrcc    pc, [tbl, scno, lsl #2]        @ call sys_* routine
109 
110     add    r1, sp, #S_OFF
111 2:    mov    why, #0                @ no longer a real syscall
112     cmp    scno, #(__ARM_NR_BASE - __NR_SYSCALL_BASE)
113     eor    r0, scno, #__NR_SYSCALL_BASE    @ put OS number back
114     bcs    arm_syscall    
115     b    sys_ni_syscall            @ not private func
116 ENDPROC(vector_swi)

 这个vector_swi函数完成的工作:

1. 保护异常前的现场 

扫描二维码关注公众号,回复: 4644827 查看本文章

   保存现场大小为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(最高地址)

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

猜你喜欢

转载自www.cnblogs.com/DF11G/p/10172520.html