XV6 is a UNIX-like operating system that’s used in my OS class (and other OS classes across the world) for pedagogical purposes.
Creating system calls in XV6 is something I do frequently as part of my assignments, and in this article I’m documenting how to do that.
Let’s say I want to implement a system call called square
that returns the square of of its int
argument. (This is just a toy example to show you how the plumbing works; system calls are overkill to find the square of a number).
The basic idea is to look at how an existing system call is implemented, and replicate that. For instance, uptime
is an existing system call in XV6, and I could use grep
utility to figure out how that is implemented. From the root of XV6 system, run:
$ grep "uptime" -n *
syscall.c:105:extern int sys_uptime(void);
syscall.c:125:[SYS_uptime] sys_uptime,
syscall.h:15:#define SYS_uptime 14
sysproc.c:88:sys_uptime(void)
user.h:26:int uptime(void);
usys.S:31:SYSCALL(uptime)
$
So, the word “uptime” exists in the following files:
- syscall.c
- syscall.h
- sysproc.c
- user.h
- usys.S
1. Modifications in syscall.h
Now, let’s start with syscall.h
. You could’ve started with any other file from the above list; the ordering of things you do is arbitrary. In this file there are lines of the form #define SYS_something
and a number. The number increases by 1 as you go downwards. Let’s append #define SYS_square 23
to that file.
// System call numbers
// (The vertical dots `.` indicate that I've skipped over lines).
#define SYS_fork 1
#define SYS_exit 2
.
.
.
#define SYS_close 21
#define SYS_halt 22
#define SYS_square 23
The highlighted line above indicates the relevant change I made. Keep in mind the “SYS_square
” that you chose. We’ll need to refer to it later. “23” is the next number after “22”.
2. Modifications in syscall.c
If you look at the source code for syscall.c
, you’ll see that it contains, among other things, a list of extern int
function declarations, and an array of function pointers. So I’m going declare my square function (which I have arbitrarily chosen to name as sys_square
), and a corresponding entry in the array.
// (The vertical dots `.` indicate that I've skipped over lines).
extern int sys_chdir(void);
extern int sys_close(void);
.
.
.
extern int sys_uptime(void);
extern int sys_halt(void);
extern int sys_square(void);
static int (*syscalls[])(void) = {
[SYS_fork] sys_fork,
[SYS_exit] sys_exit,
.
.
.
[SYS_close] sys_close,
[SYS_halt] sys_halt,
[SYS_square] sys_square,
};
A few things to note here:
- The constant inside
[]
is idential to the one I defined insyscall.h
during the previous step. - The datatype of argument of
sys_square
is written asvoid
, even though we need to pass anint
to the function. This will be taken care of later.
3. Modifications in sysproc.c
Now we go about actually defining sys_square
function. At the end of sysproc.c
, define the function:
int sys_square(void) {
int num;
argptr(0, (void *)&num, sizeof(num)); // extract and store argument into num
return num * num;
}
Observe that we use a separate function argptr
(which is already part of XV6) to extract arguments passed into the function. The parameters of argptr
is as follows:
- The 1st parameter is the nth argument to extract, starting at 0.
- The 2nd parameter is the address of variable to which you want to store the argument, typecast to
(void *)
. - The 3rd parameter is the size of the variable to which you want to store the argument.
This is the usual way to extract arguments passed into system calls.
4. Modifications in user.h
This is where we want to actually declare the function that the user calls. You should already be seeing a bunch of function declarations in there already. Go ahead and declare this function:
int halt(void);
.
.
int priority(int);
int square(int); // this is what is called from the user-program
This is straightforward. The line that’s relevant to us is highlighted above. Some things to note here:
- This function is what would actually be called from a user program.
- We are specifying an
int
parameter.
5. Modifications in usys.S
We’re almost there. This is the final piece of the puzzle. In the file usys.S
, there are lines start with SYSCALL
. Append our function to this file, as highlighted below:
SYSCALL(halt)
.
.
SYSCALL(priority)
SYSCALL(square)
SYSCALL
is a macro that is defined at the top of this file. To be honest with you, I’m not entirely sure what this macro does.
You’re done creating your system call.
To see it in action, create a user program (any .c program), and call the square()
function like you normally would.