Linux ARM系统调用实现

Linux ARM系统调用实现

一.Arm SWI 异常中断

本部分参考《ARM体系结构与编程》杜春雷

ARM版本的Linux的系统调用的实现是通过发一个SWI异常中断的方式实现的,通过SWI异常可以实现用户空间陷入到内核的操作。通常SWI异常中断处理程序可以分为两级:第一级SWI异常中断处理程序为汇编语言,用于确定SWI中的24位立即数;第二级SWI异常中断处理程序实现SWI的各个功能。

1.1 SWI指令

SWI指令中包含有24位的立即数,通过该立即数可以使用户跳转到不同的执行分支上。在进入SWI异常时,LR寄存器保存的是SWI指令的下一条,所以,SWI指令应该是:

LDR  R0[LR, # -4]

SWI指令的24位立即数

   BIC R0R0#0XFF00 0000

linux系统调用时,采用该两行代码取得系统调用号。

1.2实现代码

AREA TopLevelSWI, CODE, READONLY

EXPORT SWI_Handler

SWI_Handler

STMFD sp!, [r0-r12, lr]

LDR r0, [lr, # -4]

BIC r0, r0, #0xff00 0000

BLC C_SWI_Handler  //使用R0 寄存器中的值,调用相应的SWI异常中断的第二级处理程序。

LDMFD sp!, [r0-r12, pc]^

END

/////////////////////////////////

Void C_SWI_Handler(unsigned number)

{

Switch(number)

Case 0://执行分支0

Break;

Case 1://执行分支1

Break;

Default:

break

}

 

二.Linux系统调用的实现

Linux系统调用是应用程序的API函数调用内核提供的系统调用实现对linux内核或者硬件的访问,在ARM平台上是通过产生SWI异常来实现的。

2.1SWI异常执行函数的注册

void __init trap_init(void)

{

    unsigned long vectors = CONFIG_VECTORS_BASE;

       memcpy((void *)vectors, __vectors_start, __vectors_end - __vectors_start);

}

该函数在start_kernel()中执行,即初始化的时候执行。

 

 

.LCvswi:

       .word      vector_swi/* SYS_CALL*/

       .globl      __stubs_end

__stubs_end:

       .equ stubs_offset, __vectors_start + 0x200 - __stubs_start

       .globl      __vectors_start

__vectors_start:

       swi  SYS_ERROR0

       b     vector_und + stubs_offset

       ldr   pc, .LCvswi + stubs_offset //此处即为定义的SWI函数,指向vector_swi

       b     vector_pabt + stubs_offset

       b     vector_dabt + stubs_offset

       b     vector_addrexcptn + stubs_offset

       b     vector_irq + stubs_offset

       b     vector_fiq + stubs_offset

       .globl      __vectors_end

__vectors_end:

       .data

       .globl      cr_alignment

       .globl      cr_no_alignment

cr_alignment:

       .space     4

cr_no_alignment:

       .space     4

2.2 linux SWI函数vector_swi

       .align       5

ENTRY(vector_swi)/**/

       sub  sp, sp, #S_FRAME_SIZE

       stmia       sp, {r0 - r12}               @ Calling r0 - r12

       add  r8, sp, #S_PC

       stmdb     r8, {sp, lr}^                  @ Calling sp, lr

       mrs  r8, spsr                 @ called from non-FIQ mode, so ok.

       str   lr, [sp, #S_PC]                     @ Save calling PC

       str   r8, [sp, #S_PSR]           @ Save CPSR

       str   r0, [sp, #S_OLD_R0]           @ Save OLD_R0

       zero_fp

// 保存寄存器值到栈中

       /*

        * Get the system call number.

        */

 

#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,取swi指令中的立即数,即系统调用号

#else

       ldr   r10, [lr, #-4]                 @ get SWI instruction // here get syscall num swi

  A710(  and  ip, r10, #0x0f000000            @ check for SWI          )

  A710(  teq   ip, #0x0f000000                                        )

  A710(  bne  .Larm710bug                                     )

#endif

 

#elif defined(CONFIG_AEABI)

 

       /*

        * Pure EABI user space always put syscall number into scno (r7).

        */

  A710(  ldr   ip, [lr, #-4]                    @ get SWI instruction   )

  A710(  and  ip, ip, #0x0f000000              @ check for SWI          )

  A710(  teq   ip, #0x0f000000                                        )

  A710(  bne  .Larm710bug                                     )

 

#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  /*__NR_SYSCALL_BASE*/

    ldreq  scno, [lr, #-4] // scno是寄存器r7的别名

 

 

#else

 

       /* Legacy ABI only. */

       ldr   scno, [lr, #-4]               @ get SWI instruction

  A710(  and  ip, scno, #0x0f000000          @ check for SWI          )

  A710(  teq   ip, #0x0f000000                                        )

  A710(  bne  .Larm710bug                                     )

 

#endif

 

#ifdef CONFIG_ALIGNMENT_TRAP

       ldr   ip, __cr_alignment

       ldr   ip, [ip]

       mcr p15, 0, ip, c1, c0           @ update control register

#endif

       enable_irq

 

       get_thread_info tsk

       adr   tbl, sys_call_table          @ load syscall table pointer/* important here load sys_call_table, details see line268,line269,and see call.s*///  系统调用函数的基指针

       ldr   ip, [tsk, #TI_FLAGS]           @ check for syscall tracing

 

#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                        // here get syscall num swi

       eorne      scno, r10, #__NR_OABI_SYSCALL_BASE

       ldrne       tbl, =sys_oabi_call_table

#elif !defined(CONFIG_AEABI)

       bic   scno, scno, #0xff000000             @ mask off SWI op-code   //here get syscall num swi

       eor   scno, scno, #__NR_SYSCALL_BASE  @ check OS number

#endif

 

       stmdb     sp!, {r4, r5}                 @ push fifth and sixth args

       tst    ip, #_TIF_SYSCALL_TRACE             @ are we tracing syscalls?

       bne  __sys_trace

 

       cmp scno, #NR_syscalls              @ check upper syscall limit

       adr   lr, ret_fast_syscall         @ return address系统调用结束后的返回函数,恢复寄存器

       ldrcc       pc, [tbl, scno, lsl #2]            @ call sys_* routine  /* execut the call function, 将调用号逻辑左移2*/系统调用的执行函数

 

       add  r1, sp, #S_OFF

2:     mov why, #0                        @ no longer a real syscall

       cmp scno, #(__ARM_NR_BASE - __NR_SYSCALL_BASE)

       eor   r0, scno, #__NR_SYSCALL_BASE      @ put OS number back

       bcs  arm_syscall    //系统调用号不正确

       b     sys_ni_syscall               @ not private func

 

2.3 linux系统调用的返回函数

ret_fast_syscall:

       disable_irq                           @ disable interrupts

       ldr   r1, [tsk, #TI_FLAGS]

       tst    r1, #_TIF_WORK_MASK

       bne  fast_work_pending

 

       /* perform architecture specific actions before user return */

       arch_ret_to_user r1, lr

 

       @ fast_restore_user_regs

       ldr   r1, [sp, #S_OFF + S_PSR]   @ get calling cpsr

       ldr   lr, [sp, #S_OFF + S_PC]!     @ get pc

       msr  spsr_cxsf, r1                @ save in spsr_svc

       ldmdb     sp, {r1 - lr}^                 @ get calling r1 - lr

       mov r0, r0

       add  sp, sp, #S_FRAME_SIZE - S_PC

       movs      pc, lr                            @ return & move spsr_svc into cpsr

你可能感兴趣的:(Linux ARM系统调用实现)