How System Calls are Executed On ARM:
The ARM
architecture supports seven types of exceptions. When an exception
occurs, execution is forced from a fixed memory address corresponding to
the type of exception. These fixed addresses are called the exception
vectors. These vectors are same as the vectors of x86 interrupt
descriptor table.
One of the seven exceptions is the software interrupt exception. Address of the function to be executed when this exception is raised is stored at the physical address 0x00000008. The Software Interrupt instruction (SWI) is used to generate the software interrupt exception. Linux uses this vector to invoke the system calls. When this exception is generated a function, vector_swi(), is called. vector_swi() is defined in <arch/arm/kernel/entry-common.S>. vector_swi() gets the system call number in the R7 general-purpose register and finds the system call address in the sys_call_table and invokes it. Registers R0-R6 are used to send arguments to the system calls.
One of the seven exceptions is the software interrupt exception. Address of the function to be executed when this exception is raised is stored at the physical address 0x00000008. The Software Interrupt instruction (SWI) is used to generate the software interrupt exception. Linux uses this vector to invoke the system calls. When this exception is generated a function, vector_swi(), is called. vector_swi() is defined in <arch/arm/kernel/entry-common.S>. vector_swi() gets the system call number in the R7 general-purpose register and finds the system call address in the sys_call_table and invokes it. Registers R0-R6 are used to send arguments to the system calls.
System Call Implementation in ARM:
Download linux kernel from www.kernel.org and extract it.
Files that are to be modified to add system call on ARM are:
1. include/linux/syscalls.h
2. arch/arm/include/asm/unistd.h
3. arch/arm/kernel/calls.S
4. arch/arm/kernel/entry_common.S
5. arch/arm/kernel/sys_arm.c
Procedure for adding a system call in ARM:
Step 1:
Define system call number for your system call in <arch/arm/include/asm/unistd.h>.
This file looks like this.
path : /arch/arm/include/asm/unistd.h
Add our define at the end.
#define __NR_sendmmsg (__NR_SYSCALL_BASE+376)
#define __NR_setns (__NR_SYSCALL_BASE+377)
#define __NR_mynew_add (__NR_SYSCALL_BASE +378 )
Step 2:
Add prototype of the system call in the <include/linux/syscalls.h>
Register the function with the prototype.
path: /include/linux/syscall.h
Add the following line:
asmlinkage long sys_mynew_add(int,int);
Step 3:
Add
our system call entry in the sys_call_table. sys_call_table is defined
as follows in <arch/arm/kernel/entry-common.S> file.
Make an entry to calls.S with path given below
path: /arch/arm/kernel/calls.S
CALL(sys_mynew_add); (the position has to be same as the system call number ie 378)
This file will be automatically added to entry-common.S as the file entry-common.S include it as follows.
#include "calls.S"
(Alternatively)
We
can define a wrapper function in entry-common.S and then register this
wrapper function in calls.S. In the entry-common.S then finally we can
jump to our system call routine.
path: /arch/arm/kernel/entry-common.S
PATH: vim arch/arm/kernel/calls.S
In calls.S at the end
CALL(sys_mynew_add_wrapper); (the position has to be same as the system call number ie 378)
In entry-common.S,
Add the following code
Path: arch/arm/kernel/entry_common.S
sys_mynew_add_wrapper:
b sys_mynew_add
ENDPROC(sys_mynew_add_wrapper)
That's it, our system call has been added successfully.
Step 4:
Writing the routine/definition for system call. Again it can be done in two ways:
i. Directly it can be included in the existing files( so that it doesn't require us to modify the Makefile)
ii. Make a separate file and change the Make file to make sure it compiles.
Example:
Method 1:
arch/arm/kernel/sys_arm.c
asmlinkage long sys_mynew_add(int a,int b)
{
printk(KERN_ALERT "The addition is %d ",a+b);
return a+b;
}
Method 2:
If separate file (say mynew_add.c) is maintained we have to add the following line in the Makefile.
e.g.:
obj-y :=mynew_add.o
Note: Now compile the kernel port that kernel image on to target device and boot it;
System call API:
Invoking a system call requires to raise an interrupt using the INT instruction, and requires to write
assembly code to send arguments to the system call. So when you write a system call you need to
provide a function which invokes the system call. For example, if you take open system call, there
is a open() function provided by the glibc and for read system call there is a read() function
provided by the glibc.
syscall() function For all standard system calls glibc provides API to invoke them. But, what if there is no API
available
for a system call?. You need to write the API for that. It will be good
if there is a generic API which can be used to invoke any system call,
right?. That is what syscall() API. This function takes a system call
number and arguments to that system call and invokes the system call.
The prototype of this function is:
int syscall(int number, ...);
First argument is the system call number and other are variable number of arguments and depends
on the system call that you are invoking.
The return value of the syscall() is defined by the system call being invoked.
In general, a 0 return value indicates success. -1 return value indicates an error, and an error code is stored in errno.
User Application:
Now that kernel is updated with a
few new system calls, let's look at what's necessary to use them from a
user-space application. There are two ways that you can use new kernel
system calls. With the first method, you call your new functions as
identified by their index through the syscall function. With the syscall
function, you can call a system call by specifying its call index and a
set of arguments. For example, the short application shown bellow calls
your sys_my_add using its index. The
syscall function is architecture specific but uses a mechanism to
transfer control to the kernel. The argument is based on a mapping of
__NR indexes to SYS_ symbols provided by /usr/include/bits/syscall.h
(defined when the libc is built). Never reference this file directly;
instead use /usr/include/sys/syscall.h.The kernel header file
<linux/unistd.h> provides a set of _syscall[0-6] macros that
automatically generate functions that do what is necessary to properly
trigger the software interrupt. The digit at the end refers to the
number of arguments taken by that particular syscall. For example, this
program calls the kernel syscall mynew_add()systemcall directly:
Assembly code to invoke system call sys_mynew_add will be as follows
Movl r7,#378
mov r0,#25
mov r1,#35
SWI 0
mov r7,#1
SWI 0
Assembling:
arm-linux-as -o sysnewadd.o sysnewadd.S
arm-linux-ld -s -o syscall sysnewadd.o
In C Application code is
vim sysnewadd.c
#include <stdio.h>
#include <linux/kernel.h>
#include <sys/syscall.h>
#include <unistd.h>
#define __NR_sys_add 378
int main(int argc, char *argv[])
{
long int a = syscall(__NR_sys_add, 25, 35); //Invoking our own system call
printf(“System call invoked\n”);
printf("system call returned %ld\n",a);
return 0;
}
Compile and Execute:
*compile that code in host side
$ arm-linux-gcc -static -o syscall sysnewadd.c
Copy that syscall executable file to target device and execute it as follows: