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-3.4.x\arch\arm\kernel\entry-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.x\arch\arm\include\asm\thread_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)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM