You are on page 1of 13

Implementing System Calls

CS552
Kartik Gopalan

Steps in writing a system call


1.

Create an entry for the system call in the kernels


syscall_table

User processes trapping to the kernel (through SYS_ENTER or


int 0x80) find the syscall function through this table.

2. Write the system call code as a kernel function


Be careful when reading/writing to user-space
Use copy_to_user or copy_from_user routines

3. Generate/Use a user-level system call stub

Hides the complexity of making a system call from user


applications.

CS552/BU/Spring2008

Step 1: Create a sys_call_table entry


/usr/src/redhat/BUILD/kernel2.6.18/linux2.6.18.s390x/arch/s390/kernel/syscalls.S
// for a z/390 kernel)
#define NI_SYSCALL SYSCALL()
syscall (sys_fork, sys_fork, sys_fork)

syscall(sys_mysvc, sys_mysvc, sys_mysvc)


arch/x86/kernel/syscall_table_32.S
//for a PC kernel
ENTRY(sys_call_table)
.long sys_restart_syscall
/* 0 */
.long sys_exit
.long sys_fork
.long sys_read

.long sys_unshare
/* 310 */
.long sys_foo
/* 311 */
CS552/BU/Spring2008

include/asm/unistd_32.h

/*
* This file contains the system call
numbers.
*/
#define
#define
#define
#define
#define

__NR_restart_syscall
__NR_exit
1
__NR_fork
2
__NR_read
3
__NR_write
4

#define __NR_foo

325

#define NR_syscalls 326


/* increment by one
*/

Step 2: Write the system call (1)


No arguments, Integer return value
asmlinkage int sys_foo(void) {
printk (KERN ALERT I am foo. UID is %d\n, current->uid);
return current->uid;
}

Note: no comma after KERN ALERT


One primitive argument

asmlinkage int sys_foo(int arg) {


printk (KERN ALERT This is foo. Argument is %d\n, arg);
return arg;
}
CS552/BU/Spring2008

Step 2: Write the system call (2)

Verifying argument passed by user space


asmlinkage long sys_close(unsigned int fd)
{
struct file * filp;
struct files_struct *files = current>files;
struct fdtable *fdt;
spin_lock(&files->file_lock);
fdt = files_fdtable(files);
if (fd >= fdt->max_fds)
goto out_unlock;

filp = fdt->fd[fd];
if (!filp)
goto out_unlock;

out_unlock:
spin_unlock(&files->file_lock);
return -EBADF;
}
CS552/BU/Spring2008

Call-by-reference argument
o
o

User-space pointer sent as argument.


Data to be copied back using the pointer.

asmlinkage ssize_t sys_read ( unsigned int fd,


char __user * buf, size_t count)
{

if( !access_ok( VERIFY_WRITE, buf, count))


return EFAULT;

Example syscall implementation


asmlinkage int sys_foo(void) {
static int count = 0;
printk(KERN_ALERT "Hello World! %d\n", count++);
return -EFAULT; // what happens to this return value?
}
EXPORT_SYMBOL(sys_foo);

CS552/BU/Spring2008

Step 3: Generate user-level stub

Using your new system call - the new way


Old macros _syscall0, _syscall1, etc are now obsolete in the new
kernels.
The new way to invoke a system call is using the the syscall(...) library
function.
Do a "man syscall" for details.

For instance, for a no-argument system call named foo(), you'll call

ret = syscall(__NR_sys_foo); // Assuming you've defined __NR_sys_foo earlier

For a 1 argument system call named foo(arg), you call


ret = syscall(__NR_sys_foo, arg);

and so on for 2, 3, 4 arguments etc.

For this method, check

http://www.ibm.com/developerworks/linux/library/l-system-calls/

CS552/BU/Spring2008

Using your new system call - the new way


#include
#include
#include
#include

<stdio.h>
<errno.h>
<unistd.h>
<linux/unistd.h>

// define the new syscall number. Standard syscalls are defined in


linux/unistd.h
#define
__NR_sys_foo
311 // or add this to unistd.h
int main(void)
{
int ret;
while(1) {
// making the system call
ret = syscall(__NR_sys_foo);
printf("ret = %d errno = %d\n", ret, errno);

sleep(1);
}
return 0;

CS552/BU/Spring2008

(contd.)

Using your new system call - the old way


You can still replicate the old _syscall0, _syscall1 etc
assembly code stubs in your user program, but this is really
not necessary anymore.
These stubs use the old method of raising "int 0x80"
software interrupts

which are found to be quite slow on newer Pentium machines.


But this technique still works for backward compatibility.

For this method, check


http://www.linuxjournal.com/article/1145

CS552/BU/Spring2008

Using your new system call - the old way


(contd.)
_syscall0(type,name)

type : type of return value (e.g. void or int)


name : name of the system call (e.g. foo)
_syscall0(int,foo)

Defines syscall entry point for asmlinkage int sys_foo(void)

_syscall1(type,name,type1,arg1)

type and name same as before


type1 : type of first argument
name1 : name of first argument
_syscall1(void,foo,int,arg)

Defines syscall entry point for asmlinkage void sys_foo(int arg)

and similarly for two arguments, three arguments and so on.


For definitions of _syscallN macros, check

include/asm/unistd.h
Also, pay attention to the usage and implementation of
__syscall_return macro. What does it do?

CS552/BU/Spring2008

Using your new system call - the old way


#include
<stdio.h>
(contd.)
#include <errno.h>
#include <unistd.h>
#include <linux/unistd.h>

// define the new syscall number. Standard syscalls are defined in linux/unistd.h
#define __NR_foo 311
// generate a user-level stub
_syscall0(int,foo)
int main(void)
{
int ret;
while(1) {
// making the system call
ret = foo();
printf("ret = %d errno = %d\n", ret, errno);
sleep(1);

}
return 0;

CS552/BU/Spring2008

SYSENTER/SYSEXIT Method
This is the newest and fastest of all methods to make system calls in
Pentium class machines.
Pentium machines have long supported these new instructions as a
faster technique to enter and exit the kernel mode than the old
technique based on raising the "int 0x80" software interrupt. Newer
linux kernels have apparently adopted this technique.
The details of how this works is quite interesting and I may try to
cover this briefly in the class.
Meanwhile you can read about the details in the following links and
maybe even try it out using the example code.
http://manugarg.googlepages.com/systemcallinlinux2_6.html
http://www.win.tue.nl/~aeb/linux/lk/lk-4.html
http://manugarg.googlepages.com/aboutelfauxiliaryvectors
CS552/BU/Spring2008

Files to change in Redhat Linux


/usr/src/redhat/BUILD/kernel-2.6.18/linux2.6.18.s390x/arch/s390/mm/fault.c

o Your kernel function implementation goes here (actual function code)

/usr/src/redhat/BUILD/kernel-2.6.18/linux2.6.18.s390x/arch/s390/kernel/syscalls.S
o SYSCALL(sys_mysvc, sys_mysvc, sys_mysvc); // typical entry

/usr/src/redhat/BUILD/kernel-2.6.18/linux-2.6.18.s390x/include/asms390/unistd.h
o #define __NR_mysvc n and #define __NR_syscalls

n+1

/usr/src/kernels/2.6.18-92.el5-s390x/include/asm-s390/unistd.h
o Same as the BUILD UNISTD.H

-------- following are for a PC build, not S390 --------- /usr/include/asm/unistd.h


/usr/src/redhat/BUILD/kernel-2.6.18/linux2.6.18.s390x/arch/i386/kernel/syscall_table.S
o .long sys_mysvc

CS552/BU/Spring2008

You might also like