System Calls¶
System calls are an interface through which userland programs can call kernel functions, mainly those that are I/O-related, and thus require kernel mode privileges. Userland code cannot of course call kernel functions directly, since this would imply access to kernel memory, which would break the userland sandbox and userland programs could corrupt the kernel at their whim. This means that the system call handlers in the kernel should be written very carefully. A userland program should not be able to affect normal kernel functionality no matter what arguments it passes to the system call (this is called bullet proofing the system calls).
How System Calls Work¶
A system call is made by first placing the arguments for the system call and
the system call function number in predefined registers. In KUDOS, the
standard MIPS32 argument registers a0
, a1
, a3
, and a3
are used
for this purpose. The system call number is placed in a0
, and its three
arguments in a1
, a2
and a3
. If there is a need to pass more
arguments for a system call, this can be easily achieved by making one of the
arguments a memory pointer which points to a structure containing rest of the
arguments. The return value of the system call is placed in a predefined
register by the system call handler. In KUDOS the standard return value
register v0
is used.
After the arguments are in place, the special machine instruction syscall
is executed. The syscall
instruction, in one, atomic instruction, does the
following:
- sets the CPU core into kernel mode,
- disables interrupts, and
- causes a system call exception.
When an instruction causes an exception, while the CPU core is in user mode,
control is transfered to the user exception handler (defined in
kudos/proc/$ARCH/exception.c
).
The system call exception is then handled as follows (note that not all details are mentioned here):
- The context is saved as with any exception or interrupt.
- As we notice that the cause of the exception was a system call, interrupts are enabled and the system call handler is called. Enabling interrupts (and also clearing the EXL bit) results in the thread running as a normal thread rather than an exception handler.
- The system call handler gets a pointer to the user context as its argument.
The system call number and arguments are read from the registers saved in
the user context, and an appropriate handler function is called for each
system call number. The return value is then written to the
v0
register saved in the user context. - The program counter in the saved user context is incremented by one
instruction, since it points to the
syscall
instruction which generated this exception. - Interrupts are disabled (and EXL bit set), and the thread is again running as an exception handler.
- The context is restored, which also restores the thread to user mode.
The last step above uses a “return from exception” instruction, eret
, which
in one, atomic instruction, does the following:
- clears the exception flag,
- enables interrupts,
- sets the CPU core into user mode, and finally,
- jumps to the address in the
EPC
register on MIPS co-processor 0.
Note: You cannot directly change thread/process (i.e. call scheduler) when
in syscall or other exception handlers, since it will mess up the stack. All
thread changes should be done through (software) interrupts (e.g. calling
thread_switch
).
System Calls in KUDOS¶
KUDOS userland has a wrapper function for the syscall
instruction, so there
is no need for the user to write code in assembly. In addition, some syscall
wrappers, with proper handling of syscall argumets are implemented in
userland/lib.c
. These wrappers, or rather, library functions, are
described below.
When implementing the system calls, the interface must remain binary compatible with the unaltered KUDOS. This means that the already existing system call function numbers must not be changed and that return value and argument semantics are exactly as described below. When adding system calls not mentioned below the arguments and return value semantics can of course be defined as desired.
Halting the Operating System¶
void syscall_halt(void)
This is the only system call already implemented in KUDOS. It will unmount all mounted filesystems and then power off the machine (YAMS will terminate). This system call is the only method for userland processes to cause the machine to halt.
Exercises¶
⌨ Implement a new system call
syscall_hello
in KUDOS with the system call number0xAAA
. As a result of issuing the system call, KUDOS should print “Hello, World!” to the terminal and return to the user.You will need to define this system call number in
kudos/proc/syscall.h
, handle it inkudos/proc/syscall.c
, define a wrapper for it inuserland/lib.h
, and write the wrapper itself inuserland/lib.c
. Last, but not least, write a userland programuserland/hello.c
(similar touserland/halt.c
) to test it.You can use either the polling TTY, or the interrupt-driven TTY device driver.