Adding a System Call to UML
This document describes how to add a system call to User-mode Linux. The
original version was written by Alexandros Karypidis. The version presented
here is slightly modified to handle 2.6 kernels, specifically this is based
on the 2.6.10 kernel. Furthermore, we use output from diff to
show what to change.
Further information can be found here:
- Adding a System Call to UML [pdf, html]. Note: This is the original version of the document you are looking at now.
- Linux System Call Howto [html (mirror), html (original)]. Note: this is for adding a system call to Linux in general, i.e., slightly different than what you have to do in UML.
- How System Calls Are Implemented on i386 Architecture.
Define the entry point
The first thing you need to do is modify the file unistd.h
in include/asm/arch. In this file, you need to add a line
providing an id for your system call. Locate the bunch of lines of the form
#define __NR_somename, and add a new entry for your system
call. Further, you should increment the total number of available system
calls as defined by NR_syscalls.
--- old/include/asm-i386/unistd.h 2005-01-10 09:08:18.000000000 +0100 +++ new/include/asm-i386/unistd.h 2005-03-03 14:33:25.366255000 +0100 @@ -294,8 +294,9 @@ #define __NR_add_key 286 #define __NR_request_key 287 #define __NR_keyctl 288 +#define __NR_somename 289 -#define NR_syscalls 289 +#define NR_syscalls 290 /* * user-visible error numbers are in the range -1 - -128: see
Next, you must add and entry refering to your call in the system calls
table. To do this, modify sys_call_table.c file in
arch/um/kernel and edit the following lines:
--- old/arch/um/kernel/sys_call_table.c 2005-01-10 09:08:18.000000000 +0100
+++ new/arch/um/kernel/sys_call_table.c 2005-03-03 14:21:00.580270000 +0100
@@ -19,7 +19,7 @@
#define NFSSERVCTL sys_ni_syscall
#endif
-#define LAST_GENERIC_SYSCALL __NR_vserver
+#define LAST_GENERIC_SYSCALL __NR_somename
#if LAST_GENERIC_SYSCALL > LAST_ARCH_SYSCALL
#define LAST_SYSCALL LAST_GENERIC_SYSCALL
@@ -51,8 +51,12 @@
extern syscall_handler_t old_select;
extern syscall_handler_t sys_modify_ldt;
extern syscall_handler_t sys_rt_sigsuspend;
+extern syscall_handler_t sys_somename;
syscall_handler_t *sys_call_table[] = {
+ /* The following line ensures that we do not get any problems with */
+ /* undefined system calls. */
+ [ 0 ... LAST_GENERIC_SYSCALL ] = (syscall_handler_t *) sys_ni_syscall,
[ __NR_restart_syscall ] = (syscall_handler_t *) sys_restart_syscall,
[ __NR_exit ] (syscall_handler_t *) sys_exit,
[ __NR_fork ] (syscall_handler_t *) sys_fork,
@@ -306,6 +310,7 @@
[ __NR_utimes ] (syscall_handler_t *) sys_utimes,
[ __NR_fadvise64_64 ] (syscall_handler_t *) sys_fadvise64_64,
[ __NR_vserver ] (syscall_handler_t *) sys_ni_syscall,
+ [ __NR_somename ] (syscall_handler_t *) sys_somename,
ARCH_SYSCALLS
[ LAST_SYSCALL + 1 ... NR_syscalls ] =
LAST_GENERIC_SYSCALL must be changed such that your new
system call's id is considered to be within the allowable range.
Next, we add a declaration for your system call in the area where all the other system calls are defined.
The line with [ 0 ... LAST_GENERIC_SYSCALL ] = (syscall_handler_t
*) sys_ni_syscall, ensures that system calls which are in the normal
kernel, but not in User-mode Linux are defined as not implemented. At least
the 2.6.10 kernel are missing more than 10 system calls, and without this
change, the kernel could segfault if one of the missing system calls were
used.
The last change adds your system call to the system call table, such that it can be called from user space.
Having done all these, an attempt to compile the kernel should fail
during linking, as the sys_somename function must now be
implemented.
Implementation code
First of all, create a header file somename.h for your system call and
place it in arch/um/include as shown here:
/* * somename.h */ #ifndef __UML_SOMENAME_H__ #define __UML_SOMENAME_H__ extern int sys_somename(int flag); #endif
Then, write out the implementation somename.c of your system
call in arch/um/kernel as shown here:
/*
* somename.c
*/
#include "linux/kernel.h"
#include "linux/unistd.h"
asmlinkage
int sys_somename(int flag) {
printk("Hello from somename! flag=%d.\n", flag);
return 0;
}
Finally, modify the respective Makefile in
arch/um/kernel and add somename.o to the list of build
targets.
--- old/arch/um/kernel/Makefile 2005-01-10 09:08:18.000000000 +0100 +++ new/arch/um/kernel/Makefile 2005-02-09 15:59:14.185985000 +0100 @@ -9,13 +9,13 @@ obj-y = checksum.o config.o exec_kern.o exitcode.o frame_kern.o frame.o \ helper.o init_task.o irq.o irq_user.o ksyms.o main.o mem.o mem_user.o \ physmem.o process.o process_kern.o ptrace.o reboot.o resource.o \ sigio_user.o sigio_kern.o signal_kern.o signal_user.o smp.o \ syscall_kern.o syscall_user.o sysrq.o sys_call_table.o tempfile.o \ time.o time_kern.o tlb.o trap_kern.o trap_user.o uaccess_user.o \ - um_arch.o umid.o user_util.o + um_arch.o umid.o user_util.o somename.o obj-$(CONFIG_BLK_DEV_INITRD) += initrd_kern.o initrd_user.o obj-$(CONFIG_GPROF) += gprof_syms.o obj-$(CONFIG_GCOV) += gmon_syms.o obj-$(CONFIG_TTY_LOG) += tty_log.o
Creating a stub for your system call
The listing below shows a program which uses the _syscall
macro to create a stub for the system call. It then proceeds to call the
stub function. When compiling, be sure to specify the -I option so that gcc
will look at the modified version of unistd.h. In the example,
the preprocessor looks for the file in asm/arch/unistd.h, so if
the UML code is in the directory /uml-code you should compile
with -I/uml-code/include.
/*
* test_somename.c
*
* Note: should be compiled with:
* gcc -I path.to.kernel.tree/linux-2.6.10/include -o test_somename test_somename.c
*/
#include <stdio.h>
#include <errno.h>
#include "asm/arch/unistd.h"
_syscall1(int, somename, int, flag);
int main(int argc, char** argv) {
printf("Calling...\n");
somename(1);
}