Arm linux 系統調用分析
概述
操作系統為在用戶態運行的進程與硬件設備進行交互,提供操作系統的系統服務,提供了一組接口。在應用程序和硬件之間,內核提供的系統服務設置一個額外層具有很多優點。
本文引用地址:http://www.104case.com/article/201611/318014.htm首先,這使得編程更加容易,把用戶從學習硬件設備的低級編程特性中解放出來。
其次,這極大地提高了系統的安全性,因為內核在試圖滿足某個請求之前在接口級就可以檢查這種請求的正確性。
最后,更重要的是這些接口使得程序具有可移植性,因為只要內核所提供的一組接口相同,那么在任一內核之上就可以正確地編譯和執行程序。
ARMLinux系統利用SWI指令來從用戶空間進入內核空間,還是先讓我們了解下這個SWI指令吧。SWI指令用于產生軟件中斷,從而實現從用戶模式到管理模式的變換,CPSR保存到管理模式的SPSR,執行轉移到SWI向量。在其他模式下也可使用SWI指令,處理器同樣地切換到管理模式。指令格式如下:
SWI{cond}immed_24
其中:
immed_2424位立即數,值為從0——16777215之間的整數。
使用SWI指令時,通常使用以下兩種方法進行參數傳遞,SWI異常處理程序可以提供相關的服務,這兩種方法均是用戶軟件協定。
1)指令中24位的立即數指定了用戶請求的服務類型,參數通過通用寄存器傳遞。SWI異常處理程序要通過讀取引起軟件中斷的SWI指令,以取得24為立即數。如:
MOVR0,#34
SWI12
2)指令中的24位立即數被忽略,用戶請求的服務類型由寄存器R0的值決定,參數通過其他的通用寄存器傳遞。如:
MOVR0,#12
MOVR1,#34
SWI0
ArmLinux內核處理系統調用- 在分析arm中斷處理過程中我們提到過arm架構中的異常處理向量表,其中有一個地址就是處理swi軟件中斷的入口。
- .globl __vectors_start
- __vectors_start:
- swi SYS_ERROR0:
- b vector_und + stubs_offset
- ldr pc, .LCvswi + stubs_offset
- 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:
- Cpu執行上面紅色指令之后跳轉到entry-common.s執行vector_swi處的指令,該函數就是內核中處理系統調用的入口。
- 在用戶態調用swi指令,cpu從user狀態切換到svc狀態。首先需要執行的就是保存cpu現場,這樣才能在陷入內核態執行了相應的系統調用處理過程之后,恢復cpu用戶態的狀態,重新執行用戶態進程。
- 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
- BLANK();
- DEFINE(S_R0, offsetof(struct pt_regs, ARM_r0));
- DEFINE(S_R1, offsetof(struct pt_regs, ARM_r1));
- DEFINE(S_R2, offsetof(struct pt_regs, ARM_r2));
- DEFINE(S_R3, offsetof(struct pt_regs, ARM_r3));
- DEFINE(S_R4, offsetof(struct pt_regs, ARM_r4));
- DEFINE(S_R5, offsetof(struct pt_regs, ARM_r5));
- DEFINE(S_R6, offsetof(struct pt_regs, ARM_r6));
- DEFINE(S_R7, offsetof(struct pt_regs, ARM_r7));
- DEFINE(S_R8, offsetof(struct pt_regs, ARM_r8));
- DEFINE(S_R9, offsetof(struct pt_regs, ARM_r9));
- DEFINE(S_R10, offsetof(struct pt_regs, ARM_r10));
- DEFINE(S_FP, offsetof(struct pt_regs, ARM_fp));
- DEFINE(S_IP, offsetof(struct pt_regs, ARM_ip));
- DEFINE(S_SP, offsetof(struct pt_regs, ARM_sp));
- DEFINE(S_LR, offsetof(struct pt_regs, ARM_lr));
- DEFINE(S_PC, offsetof(struct pt_regs, ARM_pc));
- DEFINE(S_PSR, offsetof(struct pt_regs, ARM_cpsr));
- DEFINE(S_OLD_R0, offsetof(struct pt_regs, ARM_ORIG_r0));
- DEFINE(S_FRAME_SIZE, sizeof(struct pt_regs));
- BLANK();
- ldr r10, [lr, #-4] @ get SWI instruction
- 之后,將swi指令保存在r10寄存器中
- enable_irq
- get_thread_info tsk
- adr tbl, sys_call_table @ load syscall table pointer
- ldr ip, [tsk, #TI_FLAGS] @ check for syscall tracing
- 開啟中斷,將thread info結構的指針保存在tsk,將sys_call_table的地址保存在tbl中,將thread info結構中的flag保存ip中。
- scno .req r7 @ syscall number
- tbl .req r8 @ syscall table pointer
- why .req r8 @ Linux syscall (!= 0)
- tsk .req r9 @ current thread_info
- bics r10, r10, #0xff000000
- 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
- eor scno, scno, #__NR_SYSCALL_BASE @ check OS number
- #endif
- 檢查swi命令中帶的中斷號是不是為0,如果不等于零說明是是old ABI方式調用的系統調用,這時將sys_oabi_call_table的地址載入到tbl中,同時得到系統調用的中斷號,保存在scno中。如果swi指令中帶點中斷號為0,說明系統調用是利用scno寄存器傳遞中斷號的。利用異或指令,將__NR_SYSCALL_BASE清除掉,得到實際的系統請求號(系統調用號定義為__NR_SYSCALL_BASE+x)。
- stmdb sp!, {r4, r5} @ push fifth and sixth args
- tst ip, #_TIF_SYSCALL_TRACE @ are we tracing syscalls?
- bne __sys_trace
- 如果進程帶有syscall trace標志就調用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
- 如果系統調用號,不大于NR_syscalls,就跳轉到系統調用函數去執行ldrcc pc, [tbl, scno, lsl #2]
Arm系統調用的簡單概述。首先是用戶態執行swi指令,swi指令使得cpu陷入svc狀態,并跳轉到固定地址去執行系統調用處理過程。用戶態通過兩種方法傳遞給內核執行系統調用的系統調用號。內核執行系統調用處理過程,首先保存cpu現場,之后會獲取到系統調用號,以系統調用地址表的基地址,加系統調用號做偏移,跳轉到相應的系統調用例程做相應的處理。
評論