ECS150 TA Website

last updated
Adding System Calls in Minix

Introduction

This document is just a simplification of the one found here. It provides one method of adding a new system call to Minix. At the bottom of this document is a list of modified files, as well as a download link. This is a long process (mainly due to how long it takes to recompile), so make sure allocate plenty of time for the example below.

Summary

There are several files that you must edit to add a new system call to Minix. However, the process can be simplified into three main steps:

  1. Implement system call in kernel
  2. Add user interface to system call (via library)
  3. Recompile kernel and libraries

If you understand how system calls are implemented in Minix, this should make sense. For more information on how system calls are implemented in Minix, see the lecture slides or book.

Also, before making any modifications, you should backup your files.

Example Specifics

We want to add a system call int foo( int num ) to Minix. The specifics for foo are below:

Function: Print message 'syscall foo( <#> ) called!' to screen, where <#> is the value of parameter num.
Parameter: The syscall foo takes one integer parameter, num, which must be greater than 0.
Returns: If num is an integer greater than 0, do_foo returns 0. Otherwise, foo fails and returns -1.

Please note that foo is actually the interface to the system call do_foo that we will implement. Remember, the user calls a library function (in our case foo). The library function then makes the actual system call (do_foo).

Step 1: Kernel Modifications

All system calls in Minix correspond to a specific integer, as defined in /usr/include/minix/callnr.h. So, the first thing we should do is associate our new system call to an integer value.

  1. Open /usr/include/minix/callnr.h
  2. Increase the value of NCALLS by one
  3. At the bottom of the document, add the line '#define FOO 78'. This assigns the value of 78 to our system call foo
  4. Save changes and exit

Now we must modify the system call tables for FS and MM (file system, memory manager).

Lets take care of the system call table in FS first. Since we want to implement this code in MM, we do not want FS to do anything for our system call. However, we will need to add an entry in the FS call table. This is done as follows:

  1. Open /usr/src/fs/table.c
  2. Go to the end of the call_vec array
  3. Add the line no_sys, /* 78 = FOO */ at the end of call_vec
  4. Save and exit

Our new call_vec should look like this:

PUBLIC _PROTOTYPE( int (*call_vec[]), (void) ) = {
	no_sys,	/* 0 = unused   	*/
	do_exit,	/* 1 = exit		*/

	/* ... omitted code ... */

	do_svrctl,	/* 77 = SVRCTL	*/
	no_sys,	/* 78 = FOO		*/
};

Next we must modify the system call table in MM. Since this is where we want our system call to actually be implemented, we must do the following:

  1. Open /usr/src/mm/table.c
  2. Go to the end of the call_vec array
  3. Add the line do_foo, /* 78 = FOO */ at the end of call_vec
  4. Save and exit

Our new call_vec should look like this:

PUBLIC _PROTOTYPE( int (*call_vec[]), (void) ) = {
	no_sys,	/* 0 = unused   	*/
	do_mm_exit,	/* 1 = exit		*/

	/* ... omitted code ... */

	do_svrctl,	/* 77 = SVRCTL	*/
	do_foo,	/* 78 = FOO		*/
};

Now that FS and MM know about our new system call, we should implement it. This can be done in a couple of ways, but only one is presented here:

  1. Open /usr/src/mm/misc.c
  2. Add the line #include <stdio.h> with the other #include lines.
  3. Go to the bottom of the file, and add the following code:
PUBLIC int do_foo()
{
	int num;

	num = mm_in.m1_i1;

	if( num > 0 )
	{
		printf( "syscall foo( %d ) called!\n", num );
		return 0;
	}
	else
	{
		return -1;
	}
}

Now, we should give MM the prototype for our new system call. This is done as follows:

  1. Open /usr/src/mm/proto.h
  2. Go to the /* misc.c */ section (on line 41)
  3. Add the line _PROTOTYPE( int do_foo, (void) );
  4. Save changes and exit

The misc.c section should look like this:

/* misc.c */
_PROTOTYPE( int do_reboot, (void)	);
_PROTOTYPE( int do_svrctl, (void)	);
_PROTOTYPE( int do_foo, (void)		);

At this point, we have made all the necessary kernel modifications. More information on the passing of parameter num from user space to kernel space can be found towards the bottom of this document.

Step 2: Library Modifications

Now we must make a library function call foo(), so that users can call our new do_foo system call. (This is just like how users call the fork() library function call to indirectly call the do_fork system call.) Again, there are multiple ways to do this, but only one is given here.

  1. Open /usr/include/unistd.h
  2. Add _PROTOTYPE( int foo, (int _num) ); at the end of the /* Function Prototypes*/ section (around line 126)
  3. Save and exit

The end of the /* Function Prototypes */ section should look like:

/* Function Prototypes. */
_PROTOTYPE( void _exit, (int _status)		);

/* ... omitted code ... */

_PROTOTYPE( int foo, (int _num)			);

Now we must create a small user library for our foo function call. This is done as follows:

  1. Create file /usr/include/foolib.h
  2. Add the following code:
#include <lib.h>
#define foo _foo
#include <unistd.h>

PUBLIC int foo( int _num )
{
	message m;

	m.m1_i1 = _num;

	return( _syscall( MM, FOO, &m ) );
}

This completes all modifications we must make to add the do_foo system call. Please notice the underscores in the variable name!

Recompile Kernel

To compile the kernel, do the following commands in Minix (will take awhile):

  • cd /usr/src/tools/
  • make hdboot
  • sync

Finally, type shutdown and restart Minix. (Note: You might be able to find a faster way to compile the kernel.)

Testing New System Call

Here is some sample test code (foo.c) that will test to make sure our new system call works properly.

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include "foolib.h"

void main( int argc, char* argv )
{
	int rval1;
	int rval2;

	rval1 = foo( -5 );
	printf( "called foo( -5 )... rval was %i\n", rval1 );

	rval2 = foo( 2 );
	printf( "called foo( 2 )... rval was %i\n", rval2 );

	return;
}

This program should output the following:

called foo( -5 )... rval was -1
syscall foo( 2 ) called!
called foo( 2 )... rval was 0

Now you have successfully added a system call to Minix! That wasnt too bad...

Parameter Passing

Passing parameters to system calls can be done in many ways. To understand how this example is passing the num parameter, see the following files:

  • /usr/src/mm/glo.h
  • /usr/src/mm/param.h

You can also look at how other system calls pass parameters.

List of Modified Files

  • /usr/include/minix/callnr.h
  • /usr/src/fs/table.c
  • /usr/src/mm/table.c
  • /usr/src/mm/misc.c
  • /usr/src/mm/proto.h
  • /usr/include/unistd.h
  • /usr/include/foolib.h
  • foo.c

( download all files )