System calls

This paper presents the implementation of system calls in haiku, and especially on x86 systems. The system call mechanism is what allows user land code to communicate with kernel land code.

The whole paper is focused on the example of one system call: is_computer_on. This api tells if the computer is currently powered on or not. Using this system call as a study is interesting because its implementation is quite simple, and it is a historical one in BeOS system (with its brother is_computer_on_fire, but it is not a system call in haiku :). However all elements presented here remain valid for any other system call.

Execution flow

Let’s start with an otherview of the global process invloved. Here is the whole excecution flow:

[image: syscall backtrace]

When some code calls “is_computer_on”, it will call this function from libroot. For this particular case, a direct call to the internal syscall api is done, that is calling _kern_is_computer_on. So in this case the only aim of “is_computer_on” is to be a public api to the system call. On other cases more things may be done before the “_kern_” call. All syscall functions name in user space begin with the “_kern_” prefix. “_kern_is_computer_on” is just a jump to a fixed location in the process address space called “commpage”.

There can be different code living in the commpage. Depending on the cpu capabilities, either _user_syscall_sysenter or _user_syscall_int code is present at the same address. These two functions contain the few instructions needed to transfer execution context to the kernel.

After that the kernel code is executed. Depending on the cpu capabilities, either “x86_sysenter” or “trap99” will be called. Finally “handle_syscall” calls the handler corresponding to the syscall number via a callback located in the syscall table. In our example it is “_user_is_computer_on” whose huge code is:

No let’s see more in details each of these steps, by following the execution path.

Userland

libroot.so

Starting in userland, libroot is where everything is done: All internal (“_kern_xxx”) APIs are located in it. However there is no source code for these functions. They are generated at compilation time by

gensyscalls

that is built as part of the Haiku compilation process. Gensyscalls uses the

private syscalls.h header file

to generate a source assembly file containing all “_kern_xxx” definitions. The order of the function definitions in “syscalls.h” determine their syscall number. The generated assembly source is located at “generated/objects/haiku/x86/common/system/libroot/os/syscalls.S.inc”. Here is what it looks like:

SYSCALLX are macros. The number after SYSCALL tells how many arguments are used in the system call. The two parameters of the macros are the function name, and the syscall number. The SYSCALLX macros are defined in

syscalls.inc

. They contain code that sets the syscall number in eax, and jump to a fix location in the commpage.

The commpage

The commpage is some pages of code that are mapped by the kernel in each loaded process at a fixed memory location. On the ia32 architecture, this address is 0xffff0000. The first part of this area is a pointer table of each function it contains. The second part of it is the actual code of the functions. This allows the kernel to let a process run optimized/specific code for the cpu in userland.

The syscall entry is the first of the commpage, so its offset is at 0xC from the start of the commpage. The content of the syscall function is filled in

x86_initialize_syscall in syscalls.cpp

. This is where the cpu capabilites are checked (for the syscall feature). Depending on them, either trap or sysenter mechanism is used. So depending on this, the code of “_user_syscall_int” or “_user_syscall_sysenter” is copied in the commpage. The definition of these two functions is in

syscalls_asm.S

. The next figure shows the mapping of the commpage area:

[image: commpage mapping]

In the kernel

system call handler

Just as there can be 2 codes in commpage to use a syscall, 2 handlers exist to trap them. “x86_sysenter” and “trap99” are both defined in

interrupts.S

. However both functions are always present in the kernel. This means that on a cpu that supports sysenter, calling syscalls with int99 is still working. Both functions set the cpu/system in the correct state before calling “handle_syscall”.

handle_syscall

This function is the last one before the specific code of each syscall. It is also defined in “arch_interrupts.S”. What it does is copy the syscall parameters to the correct place, and call the final handler. All these information are retrieved from the “syscall_info” structure defined in

ksyscalls.h

The array object “kSyscallInfos” contains all definitions for all system calls of the system. The index in the array is the number of the system call. This object is also generated by gensyscall in “objects/haiku/x86/common/system/kernel/syscall_table.h”. It typically looks like this:

The first define is used by “handle_syscall” to check that the provided syscall number is correct.

Adding syscalls

So now that all parts are explained, let’s see the process of adding a new syscall. The steps to add a syscall are:

syscalls.h

. Name it _kern_(). Avoid adding unnecessary dependencies to the header. I.e. if your syscall has pointers to structs as arguments, there’s no need to include the headers that define the structs.

syscalls.cpp

.

That’s it. There are some general rules for the implementation of a syscall:

Hello world

Let’s finish this article with something I cannot resist to add here: An assembler hello world example using the write syscall. This is something that was already published a long time ago on <a href=”

http://asm.sourceforge.net/intro/hello.html#AEN159

” rel=”nofollow”>linux assembly</a>. However this was for BeOS, and the syscall interface of haiku is not the same. We will use “int 99” to do the syscalls so that it works on all x86 systems. Looking in the generated “syscalls.S.inc” we can see that “write” is syscall number 131 and “exit” is syscall number 33. So here is the code:

Yasm can be used to compile it, and ld to link it:

Conclusion

This was just a brief description of how system calls are processed. However it should help to see the global picture of this part of the Haiku system. There would be a lot of other things to tell around this subject, especially on the ia32 specific code, but this would go beyond the aim of this introduction paper.