ARM Linux系统调用的原理
ted.*/
591 if ((no & 0xffff) <= 0x7ff)
592 return -ENOSYS;
593 break;
594}
595 #ifdef CONFIG_DEBUG_USER
596/*
597* experience shows that these seem to indicate that
598* something catastrophic has happened
599*/
600if (user_debug & UDBG_SYSCALL) {
601 printk("[%d] %s: arm syscall%d\n",
602task_pid_nr(current),current->comm, no);
603 dump_instr("", regs);
604 if (user_mode(regs)) {
605 __show_regs(regs);
606 c_backtrace(regs->ARM_fp, processor_mode(regs));
607 }
608}
609 #endif
610info.si_signo = SIGILL;
611info.si_errno = 0;
612info.si_code = ILL_ILLTRP;
613info.si_addr = (void __user*)instruction_pointer(regs) -
614 (thumb_mode(regs) ? 2 : 4);
615
616arm_notify_die("Oops - bad syscall(2)", regs, &info, no,0);
617return 0;
618 }
这个函数处理所有的辨别不出来的系统调用。系统调用号正确也好不正确也好,最终都是通过ret_fast_syscall例程来返回,因为我们看到,在进入系统调用处理函数之前,先加载了符号ret_fast_syscall进lr寄存器。ret_fast_syscall定义如下:
arch/arm/kernel/entry-common.S
ret_fast_syscall:
UNWIND(.fnstart )
UNWIND(.cantunwind )
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_userr1, lr
restore_user_regsfast = 1, offset = S_OFF
UNWIND(.fnend )
fast_work_pending:
str r0, [sp, #S_R0+S_OFF]! @ returned r0
work_pending:
tst r1, #_TIF_NEED_RESCHED
bne work_resched
tst r1, #_TIF_SIGPENDING_TIF_NOTIFY_RESUME
beq no_work_pending
mov r0, sp @regs
mov r2, why @syscall
bl do_notify_resume
b ret_slow_syscall @ Check work again
work_resched:
bl schedule
/*
* "slow" syscall return path. "why" tells us if this was a realsyscall.
*/
ENTRY(ret_to_user)
ret_slow_syscall:
disable_irq @ disable interrupts
ldr r1, [tsk, #TI_FLAGS]
tst r1, #_TIF_WORK_MASK
bne work_pending
no_work_pending:
/*perform architecture specific actions before user return */
arch_ret_to_userr1, lr
restore_user_regsfast = 0, offset = 0
ENDPROC(ret_to_user)
对于我们的平台来说,上面的arch_ret_to_user为空。restore_user_regs宏用于恢复现场并返回,restore_user_regs宏定义如下:
arch/arm/kernel/entry-header.S
.macro restore_user_regs, fast = 0, offset = 0
ldr r1, [sp, #\offset + S_PSR] @ get calling cpsr
ldr lr, [sp, #\offset + S_PC]! @ get pc
msr spsr_cxsf, r1 @save in spsr_svc
#if defined(CONFIG_CPU_32v6K)
clrex @ clear the exclusive monitor
#elif defined (CONFIG_CPU_V6)
strex r1, r2, [sp] @clear the exclusive monitor
#endif
.if \fast
ldmdb sp, {r1 - lr}^ @get calling r1 - lr
.else
ldmdb sp, {r0 - lr}^ @get calling r0 - lr
.endif
mov r0, r0 @ARMv5T and earlier require a nop
@after ldm {}^
add sp, sp, #S_FRAME_SIZE - S_PC
movs pc, lr @return & move spsr_svc into cpsr
.endm
添加新的系统调用
第一、打开arch/arm/kernel/calls.S,在最后添加系统调用的函数原型的指针,例如:
CALL(sys_set_senda)
补充说明一点关于NR_syscalls的东西,这个常量表示系统调用的总的个数,在较新版本的内核中,文件arch/arm/kernel/entry-common.S中可以找到:
.equ NR_syscalls,0
#define CALL(x).equ NR_syscalls,NR_syscalls+1
#include"calls.S"
#undef CALL
#define CALL(x).long x
相当的巧妙,不是吗?在系统调用表中每添加一个系统调用,NR_syscalls就自动增加一。在这个地方先求出NR_syscalls,然后重新定义CALL(x)宏,这样也可以不影响文件后面系统调用表的建立。
第二、打开include/asm-arm/unistd.h,添加系统调用号的宏,感觉这步可以省略,因为这个地方定义的系统调用号主要是个C库,比如uClibc、Glibc用的。例如:
#define__NR_plan_set_senda(__NR_SYSCALL_BASE+365)
为了向后兼容,系统调用只能增加而不能减少,这里的编号添加时,也必须按顺序来。否则会导致核心运行错误。
第三,实例化该系统调用,即编写新添加系统调用的实现例如:
SYSCALL_DEFINE1(set_senda, int,iset)
{
if(iset)
UART_PUT_CR(&at91_port[2],AT91C_US_SEN
ARMLinux系统调 相关文章:
- ARM Linux下添加新的系统调用(11-21)
- 《ARM与Linux些许问题》第四章:ARM平台系统调用原理分析(11-09)
- Arm linux 系统调用分析(11-09)
- Android arm linux 系统调用实现(11-09)
- arm linux 系统调用实现(11-09)
- Arm Linux系统调用流程详细解析SWI(11-09)
