BSD DevCenter
oreilly.comSafari Books Online.Conferences.

advertisement


Adding System Calls (an OpenBSD Example)

by Kevin Lo
10/09/2003

This article is a quick example of how to add a system call to OpenBSD. The following description is based on OpenBSD 3.4-beta on i386 machine architecture. Also, it is assumed that readers are familiar with building the OpenBSD kernel.

What is a System Call?

A system call is a request by a running task to the kernel to provide some sort of service on its behalf.

In general, the kernel services invoked by system calls comprise an abstraction layer between hardware and user-space programs, allowing a programmer to implement an operating environment without having to tailor his programs too specifically to one single brand or a precise, specific combination of system hardware components. System calls also serve this generalization function across programming languages. For example, the write system call will write data to the object referenced by a file descriptor. To the programmer, this looks like another C function, but in actuality, the code for write is contained within the kernel.

Should I Add a New System Call?

In most cases, you don't want to create a new system call. Adding a new system call to the kernel and then creating programs that use this call take away the program's portability. Adding a system call can also introduce a serious security problem into your system. The best solution is usually not to write your own system calls, but sometimes they solve problems. For example, you might want to add Access Control Lists support for your system, or make it possible to bind a process to a particular CPU.

Implementation of System Calls

There are four main source and header files associated with system calls. These are:

  • /usr/src/sys/kern/syscalls.c, containing the table of names of system calls.

  • /usr/src/sys/kern/init_sysent.c, containing a table sysent, of which each entry stores two things: the number of parameters to the system call and the function that implements the system call.

  • /usr/src/sys/sys/syscallargs.h, containing system call argument lists.

  • /usr/src/sys/sys/syscall.h, containing system call numbers. For example, the line:

    #define SYS_fork        2

    means fork() is system call number 2.

Now let's see how to implement a new system call, hello, which will print Hello World message to the console. To do this, edit /usr/src/sys/kern/syscalls.master to add an entry for sys_hello.

There are multiple types of system calls:

  • STD: always included in the kernel.
  • OBSOL: obsolete, not included in system.
  • UNIMPL: unimplemented, not included in system.
  • NODEF: included, but don't define the syscall number.
  • NOARGS: included, but don't define the syscall args structure.
  • INDIR: included, but don't define the syscall args structure, and allow it to be a "real" varargs function.

Each entry in the file has the following format:

Systemcall-Number   
Type    { Pseudo-Prototype }   
[Alias]

When adding a new system call, always add it to the end of the syscalls.master file. Bump up the last number of system call and assign it to sys_hello. For example, let's assign number 287 to sys_hello in syscalls.master:

287    
STD { int sys_hello(void); }

Save that file, then run make init_sysent.c in the /usr/src/sys/kern directory. This will regenerate the init_sysent.c, syscalls.c, syscallargs.h, and syscalls.h files, all of which depend on syscalls.master.

The following function implements our hello system call in sys_hello.c:

#include <sys/types.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/proc.h>
#include <sys/mount.h>
#include <sys/syscallargs.h>

/*
 * Print "Hello World" message to the console
 */
int
sys_hello(p, v, retval)
        struct proc *p;
        void        *v;      
        register_t  *retval;
{

        printf("Hello World\n");
        return (0);
}

Modify the file /usr/src/sys/conf/files to include sys_hello.c and recompile the kernel. Reboot into the fresh kernel. Now you can invoke the system call from a user program. The following program uses syscall(2) to test the newly created system call:

#include
<sys/syscall.h>
#include <unistd.h>
#include <stdio.h>

int
main(int argc, char **argv)
{
        syscall(287);
}

To compile, use the command:

$ gcc -I/usr/src/sys/sys

to instruct the compiler to use our include file.

A slightly more instructive example is mysyscall, which prints a string passed in by the user. The entry for sys_mysyscall in syscalls.master should look like this:

288    
STD            
{ int sys_mysyscall(size_t len, char *string); }

The sys_mysyscall function that implements the mysyscall system call in sys_mysyscall.c is:

#include <sys/types.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/proc.h>

#include <sys/mount.h>
#include <sys/syscallargs.h>

/*
 * Print a string passed in by the user, after copying to a kernel buffer.
 * The len argument isn't actually used, it's just to demonstrate
 * bounds checking.
 */
int
sys_mysyscall(struct proc *p, void *v, register_t *retval)
{
        struct sys_mysyscall_args /* {
               syscallarg(size_t) len;
               syscallarg(char *) string;
        } */ *uap = v;
        int error;
        size_t len;
        char buffer[1024];

        /* must bounds check all user values */
        if (SCARG(uap, len) > (size_t)1024)
               return (EINVAL);
        /* must use copy(9) functions to access user pointers */
        if ((error = copyinstr(SCARG(uap, string), buffer, sizeof(buffer),
           &len)))
               return (error);

        printf("The user sent\n%s\n", buffer);

        return (0);
}

If you want to call the system call by name, you must recompile libc after editing /usr/src/libc/sys/Makefile.inc to include hello.o on the ASM line.

An alternative way to implement system calls is using Loadable Kernel Modules (LKM). In this case, you don't have to modify the kernel sources to append your own code. In a future article, I will introduce LKM and tell you how to implement system calls and device drivers via the LKM interface.

Acknowledgments

I would like to thank Philipp Buehler, Tobias Weingartner, and Ted Unangst for reviewing this article. I would also like to thank Ted Unangst for contributing to an example of the system call mysyscall.

Kevin Lo is an OpenBSD developer and a teacher at Kaiping Vocational School.


Return to the BSD DevCenter.




Sponsored by: