ARM Linux System Call Process
System Call is a set of interfaces provided by the operating system to users (applications). Each system call has a corresponding system call function to complete the corresponding work. You can use this interface to apply for services from the operating system, such as accessing hardware and managing processes. However, because the user program runs in the user space and the system call runs in the kernel space, the user program cannot directly call the system call function, as we often see, functions such as fork, open, and write are not actually really system-called functions. They are all just c libraries, and these functions will execute a Soft Interrupt swi command, A Soft Interrupt occurs, causing the CPU to fall into the kernel state. Then, a series of judgments are made in the kernel to determine which system is called, and then the system calls the function to complete the corresponding functions. The following is a simple example of the entire execution process from user-mode call to system call to kernel processing.
The user State program is as follows:
Void pk ()
{
_ Asm __(
"Ldr r7 = 365 \ n"
"Swi \ n"
:
:
:
);
}
Int main ()
{
Pk ();
Retrun 0;
}
In the code above, I implemented a new system call. How to do it? I will describe it later. In fact, pk () can be analogous to the open () and other functions we call in user programs. This function only does one simple thing: Pass the system call number to r7 ,, then a soft interrupt occurs. Then the CPU falls into the kernel
Kernel Status:
After the Soft Interrupt of the CPU, the PC pointer is directed to the corresponding interrupt vector table, which is defined in the kernel code: arch/arm/kernel/entry-armv.S.
. LCvswi:
. Word vector_swi
. Globl _ stubs_end
_ Stubs_end:
. Equ stubs_offset, _ vectors_start + 0x200-_ stubs_start
. Globl _ vectors_start
_ Vectors_start:
ARM (swi SYS_ERROR0)
THUMB (svc #0)
THUMB (nop)
W (B) vector_und + stubs_offset
W (ldr) pc,. LCvswi + stubs_offset # After the response is interrupted, the pc points
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:
When the pc receives the preceding command, it jumps to the vector_swi label defined in arch/arm/kernel/entry-commen.S.
. 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 (analytic dB r8, {sp, lr} ^) @ Calling sp, lr
THUMB (mov r8, sp)
THUMB (store_user_sp_lr r8, r10, S_SP) @ 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. # retrieve the system call number
*/
# 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
A710 (and ip, r10, #0x0f000000 @ check for SWI)
A710 (teq ip, #0x0f000000)
A710 (bne. Larm710bug)
# Endif
# Ifdef CONFIG_CPU_ENDIAN_BE8
Rev r10, r10 @ little endian instruction
# Endif
# Elif defined (CONFIG_AEABI)
/*
* Pure EABI user space always put syscall number into scno (r7 ).
*/
A710 (ldr ip, [lr, #-4] @ get SWI instruction)
A710 (and ip, ip, #0x0f000000 @ check for SWI)
A710 (teq ip, #0x0f000000)
A710 (bne. Larm710bug)
# 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]
# Else
/* Legacy ABI only .*/
Ldr scno, [lr, #-4] @ get SWI instruction
A710 (and ip, scno, #0x0f000000 @ check for SWI)
A710 (teq ip, #0x0f000000)
A710 (bne. Larm710bug)
# Endif
# Ifdef CONFIG_ALIGNMENT_TRAP
Ldr ip, _ cr_alignment
Ldr ip, [ip]
Mcr p15, 0, ip, c1, c0 @ update control register
# Endif
Enable_irq
Get_thread_info tsk
Adr tbl, sys_call_table @ load syscall table pointer # obtain the base address of the system call table
Ldr ip, [tsk, # TI_FLAGS] @ check for syscall tracing
# 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
# Elif! Defined (CONFIG_AEABI)
Bic scno, scno, #0xff000000 @ mask off SWI op-code
Eor scno, scno, #__ NR_SYSCALL_BASE @ check OS number
# Endif
Analytic dB sp !, {R4, r5} @ push th and sixth args
Tst ip, # _ TIF_SYSCALL_TRACE @ are we tracing syscils?
Bne _ sys_trace
Cmp scno, # nr_syscils @ check upper syscall limit
Adr lr, BSYM (ret_fast_syscall) @ return address
Ldrcc pc, [tbl, scno, lsl #2] @ call sys _ * routine # Jump to System call Function
Add r1, sp, # S_OFF
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
It can be seen from the above that when the CPU is switched from the interrupted vector table to vector_swi, several things are completed: 1. retrieve System Call number 2. obtain the base address of the system call function in the system call table based on the system call number and obtain a function pointer for the system call function. 3. obtain the items in the system call table based on the base address and system call number of the system call table. Each table item is a function pointer, which is assigned to the PC, the system calls the function.
The system call table is defined in: arch/arm/kernel/CILS. S.
* This program is free software; you can redistribute it and/or modify
* It under the terms of the GNU General Public License version 2
* Published by the Free Software Foundation.
*
* This file is wrongly ded thrice in entry-common.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 (sys_ni_syscall)/* was sys_waitpid */
CALL (sys_creat)
CALL (sys_link)
/* 10 */CALL (sys_unlink)
CALL (sys_execve_wrapper)
CALL (sys_chdir)
CALL (OBSOLETE (sys_time)/* used by libc4 */
CALL (sys_mknod)
/* 15 */CALL (sys_chmod)
CALL (sys_lchown16)
CALL (sys_ni_syscall)/* was sys_break */
CALL (sys_ni_syscall)/* was sys_stat */
CALL (sys_lseek)
/* 20 */CALL (sys_getpid)
CALL (sys_mount)
CALL (OBSOLETE (sys_oldumount)/* used by libc4 */
CALL (sys_setuid16)
CALL (sys_getuid16)
/* 25 */CALL (OBSOLETE (sys_stime ))
CALL (sys_ptrace)
CALL (OBSOLETE (sys_alarm)/* used by libc4 */
CALL (sys_ni_syscall)/* was sys_fstat */
CALL (sys_pause)
/* 30 */CALL (OBSOLETE (sys_utime)/* used by libc4 */
CALL (sys_ni_syscall)/* was sys_stty */
CALL (sys_ni_syscall)/* was sys_getty */
CALL (sys_access)
CALL (sys_nice)
/* 35 */CALL (sys_ni_syscall)/* was sys_ftime */
CALL (sys_sync)
CALL (sys_kill)
CALL (sys_rename)
CALL (sys_mkdir)
/* 40 */CALL (sys_rmdir)
CALL (sys_dup)
CALL (sys_pipe)
CALL (sys_times)
CALL (sys_ni_syscall)/* was sys_prof */
/* 45 */CALL (sys_brk)
CALL (sys_setgid16)
CALL (sys_getgid16)
CALL (sys_ni_syscall)/* was sys_signal */
CALL (sys_geteuid16)
/* 50 */CALL (sys_getegid16)
CALL (sys_acct)
CALL (sys_umount)
CALL (sys_ni_syscall)/* was sys_lock */
CALL (sys_ioctl)
/* 55 */CALL (sys_fcntl)
.......
CALL (sys_eventfd2)
CALL (sys_epoll_create1)
CALL (sys_dup3)
CALL (sys_pipe2)
/* 360 */CALL (sys_inotify_init1)
CALL (sys_preadv)
CALL (sys_pwritev)
CALL (sys_rt_tgsigqueueinfo)
CALL (sys_perf_event_open)
CALL (sys_pk) # The system call I added myself
After learning about the execution process of a system call, you can try to add your own system call:
Kernel:
1. implement a system call function in the kernel code, that is, the sys_xxx () function. For example, I added
Void pk ()
{
Printk (KERN_WARNING "this is my first sys call! \ N ");
}
2. Add the system call number in arch/arm/include/asm/Unistd. h.
Add # define _ NR_pk (_ NR_SYSCALL_BASE + 365)
3. Add the call function pointer list in arch/arm/keenel/CILS. S.
Add CALL (sys_pk)
4. Declare your system call function in include/linux/syscall. h
Add asmlinkage long sys_pk ()
User space:
Void pk ()
{
_ Asm __(
"Ldr r7 = 365 \ n"
"Swi \ n"
:
:
:
);
}
Int main ()
{
Pk ();
Retrun 0;
}
After writing the above code, you can compile the kernel and applications.
Run the generated file on the arm Development Board to print out: This is my first sys call!
This indicates that the added system call can be used.
So far, the implementation mechanism of the system call and the addition of a new system call are complete.
This article permanently updates the link address: