(转载请注名出处)
/*******************************************************************************/
native linux syscall:
dump 一个 arm eabi toolchain 的libc 找到epoll create syscall 代码
libc.a => ../bin/arm-linux-objdump -D libc.a >> ~/samsung/libc_dump.txt
典型的eabi syscall ,以r7 做scno (syscall number)
epoll_create 的 r7 = 250
使用软中断指令,执行syscall
swi 0 /*__NR_epoll_create */
swi 后,lr(R14) 放的是 swi x 下面那条指令地址 ( 这个lr 可以用来获得ABI syscall 时的number
通过取lr-4 就是swi x (机器码为0xefxxxxxx) 来完成)
----------------------------------------------------------------------------------
因为在trap init 已经把向量进行初始化,如下
trap_init (trap.c)
/*
* Copy the vectors, stubs and kuser helpers (in entry-armv.S)
* into the vector page, mapped at 0xffff0000, and ensure these
* are visible to the instruction stream.
*/
memcpy((void *)vectors, __vectors_start, __vectors_end - __vectors_start);
memcpy((void *)vectors + 0x200, __stubs_start, __stubs_end - __stubs_start);
memcpy((void *)vectors + 0x1000 - kuser_sz, __kuser_helper_start, kuser_sz);
----------------------------------------------------------------------------------
当执行swi 后 ,指令跳到 ldr pc, .LCvswi + stubs_offset
.globl __vectors_start (arch\arm\kernel\entry-armv.S)
__vectors_start:
swi SYS_ERROR0
b vector_und + stubs_offset
ldr pc, .LCvswi + stubs_offset (swi xxx goto this !!!)
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
-----------------------------------------------------------------------------------
执行到 vector_swi ,或因为是 EABI ,所以根据r7(scno) 的syscall number 去调用
.LCvswi:
.word vector_swi
ENTRY(vector_swi) (entry-common.S)
adr tbl, sys_call_table @ load syscall table pointer (calls.S)
......
cmp scno, #NR_syscalls @ check upper syscall limit
adr lr, ret_fast_syscall @ return address
ldrcc pc, [tbl, scno, lsl #2] @ call sys_* routine
......
bcs arm_syscall
----------------------------------------------------------------------------------
asmlinkage int arm_syscall(int no, struct pt_regs *regs) (trap.c)
native linux syscall 基本就是这样一个过程
/******************************************************************************/
那么对于oklinux 来讲:
首先 一个epoll_create EABI syscall ,同样是先设置r7=250 ,然后执行swi 0 ,产生一软中断异常
可以在l4 kernel arch目录下找到traps.spp ,其中
BEGIN_PROC_TRAPS(arm_high_vector)
b arm_reset_exception
...
b arm_swi_syscall
...
END_PROC_TRAPS(arm_high_vector)
这部分代码也会在l4 kernel init 时讲它copy 到地址高端0xFFFF0000
swi 后执行到 b arm_swi_syscall
继续跟arm_swi_syscall:
BEGIN_PROC_TRAPS(arm_swi_syscall)
str lr, [sp, #-8]! /* 将syscall swi 0 这条指令下面那条指令地址 保存下来*/
stmdb sp, {sp, lr}^ /* 保留 user mode 下的lr ,sp */
nop
ldr lr, [sp, #-8]! /* Get user's SP [syscall no] */ /* 其实这句话对 linux syscall 不成立*/
/* 他只是对l4 kernel 的syscall 来讲可以根据sp 可以获得 syscall no,可以看ipc.spp了解 */
str r12, [sp, #SC_SP] /* Save r12 to SP (user SP was in r12) - for sys_ipc */
/* Check whether this is an IPC syscall */
/* l4 kerne ipc syscall */
cmp lr, #(0xffffff00 + SYSCALL_ipc) /* 对这个条件linux syscall 必定不满足,那么就跳到 check_other_syscalls */
/* Not IPC syscall? */
bne check_other_syscalls
check_other_syscalls :
LABEL(check_other_syscalls)
bcc arm_swi_exception
arm_swi_exception:
这段代码比较复杂,但主要是l4 kernel 发 excption ipc
其中 orr tag, tag, #EXCEPT_IPC_SYS_TAG_LO /* SETUP_E1 */ ,将tag 设置13 ,供后面判断用
在syscall_loop.c 中 有个函数syscall_loop:
该函数是个循环,上来设置accptor ,notify bit 后就跳到 return_to_user
因为是第1次,那么reply_user_ipc 不成立,那么就跑到L4_Wait (&from);等着
当发生syscall 时,冲arm_swi_exception 发出的excetion ipc 被 l4_wait 接收到
判断from.raw == curinfo->user_handle.raw 条件成立, 这句没看懂 ,为什么该等式成立就是syscall ???
成立并且 curinfo->tag = tag;
L4_MsgStore (curinfo->tag, &curinfo->regs.msg); 取出ipc 的msg ,store到 curinfo->regs.msg
然后回到循环开头,根据 L4_Label(curinfo->tag)判断 如果tag 表示 L4_ARCH_EXCEPTION:
0xfffb, l4 manul P107 A-13.1 Exception Protocol 中有关于exception 的描述
L4_ARCH_IS_SYSCALL(curinfo->tag) ,就是判断前面arm_swi_exception EXCEPT_IPC_SYS_TAG_LO 是否满足
l4_arch_lookup_syscall(include\asm-l4\arm\syscalls_inline.h) => 查 l4_arm_abi_syscalls 表
返回 return l4_arm_abi_syscalls[call].sys_num;
查表是这样的: 查l4_arm_abi_syscalls 数组 返回 sys_num,
然后 l4_syscall_table 数组 以 sys_num 为下标 找到该函数 调用
比如 epoll_create:
经过两次转换 : __NR_epoll_create => __L4_sys_epoll_create => sys_epoll_create !!!!!!
(l4_arm_abi_syscalls表) (l4_syscall_table表)
最后关于syscall_loop 什么时候运行,
在oklinux kernel 部分主要两部分,
首先init 进程run_init_process(init\main.c)
call 到kernel_execve(arch\l4\kernel\glue.c)=> execve => execve=>
=> __execve => (arch\l4\kernel\process.c)
在_execve 中sys_execve 执行后 会call 到syscall_loop
还有就是kernel\kmod.c (kernel module)
在oklinux user mode 部分
copy thread 时 new_process_handler,也会call 到syscall_loop
这样就是在当前的task 后面跑 syscall_loop, 在syscall_loop ,wait 时
task 继续执行,task 执行过程中 发生syscall_loop 就切到syscall_loop ,
判断后 进行syscall
最后补充一个细节问题:
在 l4_arch_lookup_syscall 里有句
call = ARM_syscall(regs) & 0x00ffffff; 是用来获得syscall num 的
根据eabi 里规定 syscall num 是放在r7 里的
ARM_syscall 在ptrace.h中定义为 #define ARM_syscall(regs) ARM_r7(regs)
然后#define ARM_r7(regs) L4_MsgWord(®s->msg, 3)
开始没明白为什么l4 -x2 ,和实际代码中r7 都是放在mr4的
为什么现在要到msg,3 中去取,(以为是mr3)
而在:
l4 -x2 manual 中关于Virtual Registers [ARM] 里
ipc 时 mr0-mr4 分别对应 r3 -r7 , 其中r7对应mr4
另外traps.spp 中ipc_complete_switch_to_noex 中 可以看到 ,完成ipc 后也是将
mr4(r7) copy 到当前 utcb 的mr4的位置 ,代码如下:
cmp r10, #2
strge mr2, [r12, #8] // MR2
strgt mr3, [r12, #12] // MR3
cmp r10, #4
strge mr4, [r12, #16] // MR4 //其中mr4 在前面有 #define mr4 r7 ,16 offset 对应到mr[4]
strgt mr5, [r12, #20] // MR5
为什么这里 取 r7 要通过L4_MsgWord(®s->msg, 3)
今天突然想起msg 中msg[0] 是放tag的,真正的msg 是从msg[1]开始的
所以L4_MsgWord 去取msg 3 其实对应到 utcb mr4
然后打开L4_MsgWord
L4_INLINE L4_Word_t
L4_MsgWord(L4_Msg_t *msg, L4_Word_t u)
{
return msg->msg[u + 1];
}
u =3 时 其实是取 msg[4] ,这样就可以解释得通了
关于上面syscall_loop 运行时间的修正:
总是在main_thread (kernel thread) 中运行,
1. 所以每次 new_process_handler (stub),kernel thread 切换kernel function 时调用
2. kernel_execve 当在kernel 启动时创建init时
这样oklinux kernel 所有执行路径都有syscall_loop 挂在后面