Classic Logic

XV6 System Call

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:

  1. syscall.c
  2. syscall.h
  3. sysproc.c
  4. user.h
  5. 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 in syscall.h during the previous step.
  • The datatype of argument of sys_square is written as void, even though we need to pass an int 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.