HOWTO Add a system call to the 2.6 Linux Kernel

Introduction

This is a simple HOWTO that shows how to add a "Hello World" system call to the 2.6 revisions of the linux kernel. The reader is expected to know how to compile and configure their kernel and know a little bit about programming.

This HOWTO was last updated for version 2.6.28.2. If you are using a later or earlier version of the kernel, things will be slightly different. Also, if you are working with an architecture other than intel x86, you will have to do some extra work in your specific arch folder.

Getting Started

Before you begin, make sure that you have a fresh copy of the kernel sources. Hereafter, let $linux stand for the directory in which you have stored the kernel sources.

We will create a new system call, called hello_world, and we will assign it a new, unused system call number. The steps in doing so are outlined below:

  1. Modify the architecture specific sources to point to your syscall code.
  2. Modify the generic source files to point to your syscall code.
  3. Create the code for our system call.
  4. Make sure the kernel will compile the code for our syscall.
  5. Creating a driver.
  6. About the Author.

1. Modify Architecture Specific Sources

As mentioned in the introduction, these are specific to intel 32bit x86. It should be relatively straightforward to make the right changes for your architecture. We simply need to add the name to the syscall table and a reference to the index needs to be placed in unistd:

  1. Register the system call symbolic name with the kernel by appending the line: .long sys_hello_world at the end of $linux/arch/x86/kernel/syscall_table_32.S
  2. Register the system call number with the kernel by appending the line #define __NR_hello_world 333 after the line #define __NR_inotify_init1 332 in the file $linux/arch/x86/include/asm/unistd_32.h. This file is included by userspace programs to reference the system call.

2. Modify Generic Sources

Add a function prototype to our system call in syscalls.h:
  1. Add the system call to the $linux/include/linux/syscalls.h file by adding the line asmlinkage long sys_hello_world(void); to the system call list just before the int kernel_execve... line.

3. Create the Code

At this point you can put your syscall inside of some other source file or put it in a file just for itself. Assuming you do the latter, your hello_world.c file should look like this:

/**
 * hello_world.c - The hello world system call, the best system call ever.
 **/
#include <linux/syscalls.h>

asmlinkage long sys_hello_world()
{
  printk("Hello World!\n");
  return 0;
}

If you choose to group your system call with sys_read or another system call, you can just append the above function definition to the appropriate file.

4. Make it Compile

This is optional, and you only need to do it if you want to make a clean, organized, and separate modification to the kernel. If you chose to group your new syscall as previously described, your code should compile and you should be fine.

Again, we have some options here. One is to place hello_world.c in a folder we know will be compiled and to add hello_world.o to the makefile:

obj-y := hello_world.o

You should just be able to find the obj-y line and tack it on to the end. If you are confused about the syntax, you can look at other makefiles in the other directories.

If you want to place hello_world.c in a seperate folder (e.g. hello_world), you simply need to add that folder to the Kernel Makefile, and create a blank makefile in the new folder with the line as shown above.

To edit the kernel makefile, you would find:

core-y                += kernel/ mm/ fs/ ipc/ security/ crypto/ block/

And change it to:

core-y                += kernel/ mm/ fs/ ipc/ security/ crypto/ block/ hello_world/

5. Create a Driver

Compile, install, and boot to the modified kernel. You can test your new system call by using a simple driver program:

/**
 * hello_world_driver.c - The driver to the most awesome system call ever.
 **/
#include <unistd.h>
#include <syscall.h>

long hello_world();

int main()
{
  hello_world();
  return 0;
}

long hello_world()
{
  return syscall(__NR_hello_world);
}
In order to get this to compile, two things must be done, first we have to make the kernel headers and install them to a location we choose and then we have to compile with a special gcc option. To make and install the headers, run make headers_check and then make INSTALL_HDR_PATH=../linux-headers headers_install in $linux. This will place the headers in $linux/../linux-headers/include.

All you have to do now is compile your driver program using the -I gcc option like so:

-I/usr/src/linux-headers/include/

Assuming your $linux is /usr/src/linux. This is necessary to make gcc include the new unistd header, but you could potentially install those headers into the default include spot (not recommended).

You may find older documentation discussing the use of the _syscallN(..) macros (where N is 0-6). Older versions of the kernel used the _syscallN(..) macros for user applications to interface with system calls. In the most recent versions of the kernel this macro based interface has been deprecated and replaced with the syscall(..) interface. The above driver program illustrates the use of this new interface.

You can check that the system call is working by examining the output of printk by using either dmesg or tail -f /var/log/messages

6. About the Author

See Richard Carback.