操作体系为在用户态运转的进程与硬件设备进行交互供给了一组接口。在运用程序和硬件之间设置一个额外层具有许多长处。首要,这使得编程愈加简单,把 用户从学习硬件设备的初级编程特性中解放出来。其次,这极大地提高了体系的安全性,由于内核在企图满意某个恳求之前在接口级就能够查看这种恳求的正确性。 终究,更重要的是这些接口使得程序具有可移植性,由于只需内核所供给的一组接口相同,那么在任一内核之上就能够正确地编译和履行程序。
Unix体系经过向内核宣布体系调用(systemcall)完结了用户态进程和硬件设备之间的大部分接口。体系调用是操作体系供给的服务,用户程序经过各种体系调用,来引证内核供给的各种服务,体系调用的履行让用户程序堕入内核,该堕入动作由swi软中止完结。
运用编程接口(API)与体系调用的不同在于,前者只是一个函数界说,阐明了怎样取得一个给定的服务,而后者是经过软件中止向内核宣布的一个清晰的 恳求。POSIX规范针对API,而不针对体系调用。Unix体系给程序员供给了许多API库函数。libc的规范c库所界说的一些API引证了封装例程 (wrapper routine)(其仅有意图便是发布体系调用)。一般状况下,每个体系调用对应一个封装例程,而封装例程界说了运用程序运用的API。反之则不然,一个 API没必要对应一个特定的体系调用。从编程者的观念看,API和体系调用之间的不同是没有联系的:仅有相关的作业便是函数名、参数类型及回来代码的含 义。可是,从内核设计者的观念看,这种不同的确有联系,由于体系调用归于内核,而用户态的库函数不归于内核。
大部分封装例程回来一个整数,其值的意义依赖于相应的体系调用。回来-1一般标明内核不能满意进程的恳求。体系调用处理程序的失利或许是由无效参数 引起的,也或许是由于缺少可用资源,或硬件出了问题等等。在libc库中界说的errno变量包括特定的犯错码,每个犯错码界说为一个常量宏。
当用户态的进程调用一个体系调用时,CPU切换到内核态并开端履行一个内核函数。由于内核完结了许多不同的体系调用,因而进程有必要传递一个名为体系 调用号(system call number)的参数来辨认所需的体系调用。一切的体系调用核都回来一个整数值。这些回来值与封装例程回来值的约定是不同的。在内里,整数或0标明体系调 用成功完毕,而负数标明一个犯错条件。在后一种状况下,这个值便是寄存在errno变量中有必要回来给运用程序的负犯错码。
ARM Linux体系运用SWI指令来从用户空间进入内核空间,仍是先让咱们了解下这个SWI指令吧。SWI指令用于发生软件中止,然后完结从用户形式到办理模 式的改换,CPSR保存到办理形式的SPSR,履行转移到SWI向量。在其他形式下也可运用SWI指令,处理器相同地切换到办理形式。指令格局如下:
SWI{cond} immed_24
其间:
immed_24 24位当即数,值为从0——16215之间的整数。
运用SWI指令时,一般运用以下两种办法进行参数传递,SWI反常处理程序能够供给相关的服务,这两种办法均是用户软件协议。
1)、指令中24位的当即数指定了用户恳求的服务类型,参数经过通用寄存器传递。SWI反常处理程序要经过读取引起软件中止的SWI指令,以取得24为当即数。如:
MOV R0,#34
SWI 12
2)、指令中的24位当即数被疏忽,用户恳求的服务类型由寄存器R0的值决议,参数经过其他的通用寄存器传递。如:
MOV R0, #12
MOV R1, #34
SWI 0
在SWI反常处理程序中,取出SWI当即数的过程为:首要确认引起软件中止的SWI指令是ARM指令仍是Thumb指令,这可经过对SPSR拜访得到;然后取得该SWI指令的地址,这可经过拜访LR寄存器得到;接着读出指令,分解出当即数(低24位)。
由用户空间进入体系调用
一般状况下,咱们写的用户空间运用程序都是经过封装的C lib来调用体系调用的。以0.9.30版uClibc中的open为例,来追寻一下这个封装的函数是怎样一步一步的调用体系调用的。在include/fcntl.h中有界说:
#define open open64
open实践上只是open64的一个别号罢了。
在libc/sysdeps/linux/common/open64.c中能够看到:
extern __typeof(open64) __libc_open64;
extern __typeof(open) __libc_open;
可见open64也只不过是__libc_open64的别号,而__libc_open64函数在同一个文件中界说:
libc_hidden_proto(__libc_open64)
int __libc_open64 (const char *file,int oflag, …)
{
mode_t mode = 0;
if (oflag & O_CREAT)
{
va_listarg;
va_start(arg, oflag);
mode= va_arg (arg, mode_t);
va_end(arg);
}
return __libc_open(file, oflag O_LARGEFILE, mode);
}
libc_hidden_def(__libc_open64)
终究__libc_open64又调用了__libc_open函数,这个函数在文件libc/sysdeps/linux/common/open.c中界说:
libc_hidden_proto(__libc_open)
int __libc_open(const char *file, intoflag, …)
{
mode_tmode = 0;
if(oflag & O_CREAT) {
va_listarg;
va_start(arg, oflag);
mode= va_arg (arg, mode_t);
va_end (arg);
}
return__syscall_open(file, oflag, mode);
}
libc_hidden_def(__libc_open)
这个函数,也是只是依据翻开标志oflag的值,来判别是否有第三个参数,若由,则取得其值。之后,便用取得的参数来调用__syscall_open(file,oflag, mode)。
__syscall_open在同一个文件中界说:
static __inline__ _syscall3(int,__syscall_open, const char *, file,
int,flags, __kernel_mode_t, mode)
在文件libc/sysdeps/linux/arm/bits/syscalls.h文件中能够看到:
#undef _syscall3
#define_syscall3(type,name,type1,arg1,type2,arg2,type3,arg3) \
type name(type1 arg1,type2 arg2,type3arg3) \
{ \
return (type) (INLINE_SYSCALL(name, 3,arg1, arg2, arg3)); \
}
这个宏实践上完结界说一个函数的作业,宏的榜首个参数是函数的回来值类型,第二个参数是函数名,之后的参数就好像它们的参数名所标明的那样,分别是函数的参数类型及参数名。__syscall_open实践上为:
int __syscall_open (const char * file,intflags, __kernel_mode_t mode)
{
return (int) (INLINE_SYSCALL(__syscall_open,3, file, flags, mode));
}
INLINE_SYSCALL为同一个文件中界说的宏:
#undef INLINE_SYSCALL
#define INLINE_SYSCALL(name, nr,args…) \
({ unsigned int _inline_sys_result = INTERNAL_SYSCALL (name, , nr,args); \
if (__builtin_expect (INTERNAL_SYSCALL_ERROR_P (_inline_sys_result, ),0)) \
{ \
__set_errno (INTERNAL_SYSCALL_ERRNO(_inline_sys_result, )); \
_inline_sys_result = (unsigned int) -1; \
} \
(int) _inline_sys_result; })
INLINE_SYSCALL宏中最值得注意的是INTERNAL_SYSCALL,其界说如下:
#undef INTERNAL_SYSCALL
#if !defined(__thumb__)
#if defined(__ARM_EABI__)
#define INTERNAL_SYSCALL(name, err, nr,args…) \
({unsigned int __sys_result; \
{ \
register int _a1 __asm__ (“r0”), _nr __asm__ (“r7”); \
LOAD_ARGS_##nr (args) \
_nr = SYS_ify(name); \
__asm__ __volatile__ (“swi 0x0 @ syscall ” #name \
: “=r” (_a1) \
: “r” (_nr) ASM_ARGS_##nr \
: “memory”); \
__sys_result = _a1; \
} \
(int) __sys_result; })
#else /* defined(__ARM_EABI__) */
#define INTERNAL_SYSCALL(name, err, nr,args…) \
({ unsigned int __sys_result; \
{ \
register int _a1 __asm__ (“a1”); \
LOAD_ARGS_##nr (args) \
__asm__ __volatile__ (“swi %1 @ syscall ” #name \
: “=r” (_a1) \
: “i” (SYS_ify(name))ASM_ARGS_##nr \
: “memory”); \
__sys_result = _a1; \
} \
(int) __sys_result; })
#endif
这儿也将同文件中的LOAD_ARGS宏的界说贴出来:
#define LOAD_ARGS_0()
#define ASM_ARGS_0
#define LOAD_ARGS_1(a1) \
_a1 = (int) (a1); \
LOAD_ARGS_0 ()
#define ASM_ARGS_1 ASM_ARGS_0, “r” (_a1)
#define LOAD_ARGS_2(a1, a2) \
register int _a2 __asm__ (“a2”) = (int) (a2); \
LOAD_ARGS_1 (a1)
#define ASM_ARGS_2 ASM_ARGS_1, “r” (_a2)
#define LOAD_ARGS_3(a1, a2, a3) \
register int _a3 __asm__ (“a3”) = (int) (a3); \
LOAD_ARGS_2 (a1, a2)
这几个宏用来在寄存器中加载相应的参数,参数传递的办法和一般的C函数也没有什么太大的差异,相同都是将参数列表中的参数顺次放入寄存器r0、r1、r2、r3…中。
上面的SYS_ify(name)宏,是用来取得体系调用号的。
#define SYS_ify(syscall_name) (__NR_##syscall_name)
也便是__NR___syscall_open,在libc/sysdeps/linux/common/open.c中能够看到这个宏的界说:
#define __NR___syscall_open __NR_open
__NR_open在内核代码的头文件中有界说。
在这儿咱们疏忽界说__thumb__的状况,而假定咱们编译出来的库函数运用的都是ARM指令集。在上面的代码中,咱们看到,依据是否界说宏__ARM_EABI__,INTERNAL_SYSCALL会被翻开为两种不同的版别。关于这一点,与运用二进制接口ABI有关,不同的ABI,则会有不同的传递体系调用号的办法。关于比较新的EABI,则在r7寄存器保存体系调用号,经过swi 0x0来堕入内核。不然,经过swi指令的24位当即数参数来传递体系调用号。后边还会有内核中关于这个问题的更具体的阐明。
一起这两种调用办法的体系调用号也是存在这差异的,在内核的文件arch/arm/inclue/asm/unistd.h中能够看到:
#define __NR_OABI_SYSCALL_BASE 0x900
#if defined(__thumb__) defined(__ARM_EABI__)
#define __NR_SYSCALL_BASE 0
#else
#define __NR_SYSCALL_BASE __NR_OABI_SYSCALL_BASE
#endif
/*
* This file contains the system call numbers.
*/
#define __NR_restart_syscall (__NR_SYSCALL_BASE+ 0)
#define __NR_exit (__NR_SYSCALL_BASE+ 1)
#define __NR_fork (__NR_SYSCALL_BASE+ 2)
#define __NR_read (__NR_SYSCALL_BASE+ 3)
#define __NR_write (__NR_SYSCALL_BASE+ 4)
#define __NR_open (__NR_SYSCALL_BASE+ 5)
……
接下来来看操作体系对体系调用的处理。咱们回到ARMLinux的反常向量表,由于当履行swi时,会从反常向量表中取例程的地址然后跳转到相应的处理程序中。在文件arch/arm/kernel/entry-armv.S中咱们看到SWI反常向量:
W(ldr) pc,.LCvswi + stubs_offset
而.LCvswi在同一个文件中界说为:
.LCvswi:
.word vector_swi
也便是终究会履行例程vector_swi来完结对体系调用的处理,接下来咱们来看下在arch/arm/kernel/entry-common.S中界说的vector_swi例程(删去一些和咱们的示例渠道无关的代码):
.align 5
ENTRY(vector_swi)
sub sp, sp, #S_FRAME_SIZE
stmia sp, {r0 – r12} @Calling r0 – r12
ARM( add r8, sp, #S_PC )
ARM( 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 needto look at the swi
* value to determine if it is an EABI or anold ABI call.
*/
ldr r10, [lr, #-4] @ get SWI instruction
#ifdef CONFIG_CPU_ENDIAN_BE8
//rev指令的功用是回转字中的字节序
rev r10, r10 @little endian instruction
#endif
#elif defined(CONFIG_AEABI)
…
#else
/*Legacy ABI only. */
ldr scno, [lr, #-4] @ get SWI instruction
#endif
#ifdef CONFIG_ALIGNMENT_TRAP
ldr ip, __cr_alignment
ldr ip, [ip]
mcr p15, 0, ip, c1, c0 @ update control register
#endif
enable_irq
// tsk 是寄存器r9的别号,在arch/arm/kernel/entry-header.S中界说:// tsk .req r9 @current thread_info
// 取得线程目标的基地址。
get_thread_infotsk
// tbl是r8寄存器的别号,在arch/arm/kernel/entry-header.S中界说:
// tbl .req r8 @syscall table pointer,
// 用来寄存体系调用表的指针,体系调用表在后边调用
adr tbl, sys_call_table @ load syscall table pointer
ldr ip, [tsk, #TI_FLAGS] @ check for syscall tracing
#if defined(CONFIG_OABI_COMPAT)
/*
* If the swi argument is zero, this is an EABIcall and we do nothing.
*
* If this is an old ABI call, get the syscallnumber into scno and
* get the old ABI syscall table address.
*/
bics r10, r10, #0xff
eorne scno, r10, #__NR_OABI_SYSCALL_BASE
ldrne tbl, =sys_oabi_call_table
#elif !defined(CONFIG_AEABI)
// scno是寄存器r7的别号
bic scno, scno, #0xff @ mask off SWI op-code
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, BSYM(ret_fast_syscall) @ return address
ldrcc pc, [tbl, scno, lsl #2] @ call sys_* routine
add r1, sp, #S_OFF
// why也是r8寄存器的别号
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
ENDPROC(vector_swi)
上面的zero_fp是一个宏,在arch/arm/kernel/entry-header.S中界说:
.macro zero_fp
#ifdef CONFIG_FRAME_POINTER
mov fp, #0
#endif
.endm
而fp位寄存器r11。
像每一个反常处理程序相同,要做的榜首件事当然便是维护现场了。紧接着是取得体系调用的体系调用号。然后以体系调用号作为索引来查找体系调用表,假如体系调用号正常的话,就会调用相应的处理例程来处理,便是上面的那个ldrcc pc, [tbl, scno, lsl #2]句子,然后经过例程ret_fast_syscall来回来。
在这个当地咱们接着来评论ABI的问题。现在,咱们首要来看两个宏,一个是CONFIG_OABI_COMPAT 意思是说与old ABI兼容,另一个是CONFIG_AEABI 意思是说指定现在的办法为EABI。这两个宏能够一起装备,也能够都不配,也能够装备任何一种。咱们来看一下内核是怎样处理这一问题的。咱们知 道,sys_call_table 在内核中是个跳转表,这个表中存储的是一系列的函数指针,这些指针便是体系调用函数的指针,如(sys_open)。内核是依据一个体系调用号(关于 EABI来说为体系调用表的索引)找到实践该调用内核哪个函数,然后经过运转该函数完结体系调用的。
首要,关于old ABI,内核给出的处理是为它树立一个独自的system call table,叫sys_oabi_call_table。这样,兼容办法下就会有两个system call table, 以old ABI办法的体系调用会履行old_syscall_table表中的体系调用函数,EABI办法的体系调用会用sys_call_table中的函数指 针。
装备无外乎以下4中:
榜首、两个宏都装备行为便是上面说的那样。
第二、只装备CONFIG_OABI_COMPAT,那么以oldABI办法调用的会用sys_oabi_call_table,以EABI办法调用的用sys_call_table,和1实质上是相同的。只是状况1愈加清晰。
第三、只装备CONFIG_AEABI体系中不存在sys_oabi_call_table,对old ABI办法调用不兼容。只能 以EABI办法调用,用sys_call_table。
第四、两个都没有装备,体系默许会只允许old ABI办法,可是不存在old_syscall_table,终究会经过sys_call_table 完结函数调用
体系会依据ABI的不同而将相应的体系调用表的基地址加载进tbl寄存器,也便是r8寄存器。接下来来看体系调用表,如前面所说的那样,有两个,相同都在文件arch/arm/kernel/entry-armv.S中:
#define ABI(native, compat) native
#ifdef CONFIG_AEABI
#define OBSOLETE(syscall)sys_ni_syscall
#else
#define OBSOLETE(syscall) syscall
#endif
.type sys_call_table, #object
ENTRY(sys_call_table)
#include “calls.S”
#undef ABI
#undef OBSOLETE
别的一个为:
#define ABI(native, compat) compat
#define OBSOLETE(syscall) syscall
.type sys_oabi_call_table, #object
ENTRY(sys_oabi_call_table)
#include “calls.S”
#undef ABI
#undef OBSOLETE
这样看来形似两个体系调用表是彻底相同的。这儿预处理指令include的共同用法也挺有意思,体系调用表的内容便是整个arch/arm/kernel/calls.S文件的内容(由于太长,这儿就不悉数列出了):
/* 0 */ CALL(sys_restart_syscall)
CALL(sys_exit)
CALL(sys_fork_wrapper)
CALL(sys_read)
CALL(sys_write)
/* 5 */ CALL(sys_open)
CALL(sys_close)
……
上面的CALL()是个宏,它相同在文件arch/arm/kernel/entry-armv.S中界说:
#define CALL(x) .equNR_syscalls,NR_syscalls+1
#include “calls.S”
#undef CALL
#define CALL(x) .long x
在界说宏CALL()的当地,咱们看到calls.S现已被包括了一次,只不过在这儿,不是为了树立体系调用表,而只是是为了取得体系的体系调用的数量,并保存在宏NR_syscalls中。在SWI向量中,咱们也看到,是运用了这个宏的。
终究再罗嗦一点,假如用sys_open来搜的话,是搜不到体系调用open的界说的,体系调用函数都是用宏来界说的,比方关于open,有这样的界说:
fs/open.c
1066 SYSCALL_DEFINE3(open, const char__user *, filename, int, flags, int, mode)
1067 {
1068 long ret;
1069
1070 if (force_o_largefile())
1071 flags = O_LARGEFILE;
1072
1073 ret = do_sys_open(AT_FDCWD, filename,flags, mode);
1074 /* avoid REGPARM breakage on x86: */
1075 asmlinkage_protect(3, ret, filename,flags, mode);
1076 return ret;
1077 }
持续回到vector_swi,假如体系调用号不正确,则会调用arm_syscall函数来进行处理,这个函数界说如下:
arch/arm/kernel/traps.c
465 #define NR(x) ((__ARM_NR_##x) -__ARM_NR_BASE)
466 asmlinkage int arm_syscall(int no,struct pt_regs *regs)
467 {
468struct thread_info *thread = current_thread_info();
469siginfo_t info;
470
471if ((no >> 16) != (__ARM_NR_BASE>> 16))
472 return bad_syscall(no, regs);
473
474switch (no & 0xffff) {
475case 0: /* branch through 0 */
476 info.si_signo = SIGSEGV;
477 info.si_errno = 0;
478 info.si_code = SEGV_MAPERR;
479 info.si_addr = NULL;
480
481 arm_notify_die(“branch throughzero”, regs, &info, 0, 0);
482 return 0;
483
484case NR(breakpoint): /* SWI BREAK_POINT */
485 regs->ARM_pc -= thumb_mode(regs) ? 2: 4;
486 ptrace_break(current, regs);
487 return regs->ARM_r0;
488
489/*
490* Flush a region from virtual address r0 to virtual address r1
491 * _exclusive_. There is no alignment requirement on eitheraddress;
492* user space does not need to know the hardware cache layout.
493*
494* r2 contains flags. It shouldALWAYS be passed as ZERO until it
495* is defined to be something else.For now we ignore it, but may
496* the fires of hell burn in your belly if you break this rule. 😉
497*
498* (at a later date, we may want to allow this call to not flush
499* various aspects of the cache.Passing will guarantee that
500* everything necessary gets flushed to maintain consistency in
501* the specified region).
502*/
503case NR(cacheflush):
504 do_cache_op(regs->ARM_r0,regs->ARM_r1, regs->ARM_r2);
505 return 0;
506
507case NR(usr26):
508 if (!(elf_hwcap & HWCAP_26BIT))
509 break;
510 regs->ARM_cpsr &= ~MODE32_BIT;
511 return regs->ARM_r0;
512
513case NR(usr32):
514 if (!(elf_hwcap & HWCAP_26BIT))
515 break;
516 regs->ARM_cpsr = MODE32_BIT;
517 return regs->ARM_r0;
518
519case NR(set_tls):
520 thread->tp_value = regs->ARM_r0;
521 #if defined(CONFIG_HAS_TLS_REG)
522 asm (“mcr p15, 0, %0, c13, c0,3” : : “r” (regs->ARM_r0) );
523 #elif !defined(CONFIG_TLS_REG_EMUL)
524 /*
525 * User space must never try to accessthis directly.
526 * Expect your app to break eventuallyif you do so.
527 * The user helper at 0xffff0fe0 mustbe used instead.
528 * (see entry-armv.S for details)
529 */
530 *((unsigned int *)0xffff0ff0) =regs->ARM_r0;
531 #endif
532 return 0;
533
534 #ifdef CONFIG_NEEDS_SYSCALL_FOR_CMPXCHG
535/*
536* Atomically store r1 in*r2 if *r2 is equal to r0 for user space.
537* Return zero in r0 if *MEM was changed or non-zero if no exchange
538* happened. Also set the user Cflag accordingly.
539* If access permissions have to be fixed up then non-zero is
540* returned and the operation has to be re-attempted.
541*
542* *NOTE*: This is a ghost syscall private to the kernel. Only the
543* __kuser_cmpxchg code in entry-armv.S should be aware of its
544* existence. Dont ever use thisfrom user code.
545*/
546case NR(cmpxchg):
547for (;;) {
548extern void do_DataAbort(unsigned long addr, unsigned int fsr,
549 struct pt_regs*regs);
550unsigned long val;
551unsigned long addr = regs->ARM_r2;
552struct mm_struct *mm = current->mm;
553pgd_t *pgd; pmd_t *pmd; pte_t *pte;
554spinlock_t *ptl;
556regs->ARM_cpsr &= ~PSR_C_BIT;
557down_read(&mm->mmap_sem);
558pgd = pgd_offset(mm, addr);
559if (!pgd_present(*pgd))
560goto bad_access;
561pmd = pmd_offset(pgd, addr);
562if (!pmd_present(*pmd))
563 goto bad_access;
564pte = pte_offset_map_lock(mm, pmd, addr, &ptl);
565if (!pte_present(*pte) !pte_dirty(*pte)) {
566 pte_unmap_unlock(pte, ptl);
567 goto bad_access;
568}
569val = *(unsigned long *)addr;
570val -= regs->ARM_r0;
571if (val == 0) {
572 *(unsigned long *)addr =regs->ARM_r1;
573 regs->ARM_cpsr = PSR_C_BIT;
574}
575pte_unmap_unlock(pte, ptl);
576up_read(&mm->mmap_sem);
577return val;
578
579bad_access:
580up_read(&mm->mmap_sem);
581/* simulate a write access fault */
582do_DataAbort(addr, 15 + (1 << 11), regs);
583}
584 #endif
585
586default:
587 /* Calls 9f00xx..9f07ff are defined to return -ENOSYS
588 if not implemented, rather thanraising SIGILL. This
589 way the calling program can gracefullydetermine whether
590 a feature is supported.*/
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_SENDA);
else
UART_PUT_CR(&at91_port[2],AT91C_US_RSTSTA);
return 0;
}
第四、翻开include/linux/syscalls.h增加函数声明
asmlinkagelong sys_set_senda(int iset);
第五、在运用程序中调用该体系调用,能够参阅uClibc的完结。
第六、完毕。
参阅文档:
[精华] arm Linux 2.6高版别中的体系调用办法
http://www.unixresources.net/linux/clf/linuxK/archive/00/00/67/92/679297.html
ARMLinux下增加新的体系调用