Link Search Menu Expand Document

Adding a System Call to the Linux Kernel (Version 5.5.0)

Due by 11:59 PM on Sunday, Sep 13

Introduction

You will see system calls a lot throughout this course, and you will have to implement quite a few yourselves. In preparation for this task, this guide has been developed to show you how to go about adding a simple system call to the kernel. This guide will focus on the 64-bit x86 architecture, however similar steps could be taken on other CPU architectures that are supported by the Linux kernel.

The system call added to the kernel in this guide is basically pointless, but it demonstrates the process in a relatively simple manner. All the system call developed in this guide does is print a simple “Hello World!” string to the kernel’s log.

Make sure you have accepted the assignment on Piazza before proceeding. You must also ensure that you have completed everything under the VM setup section. Even if you did it before the release of this project.

System call implementation

You should be familiar with header files from your C or C++ courses. The purpose of a header file is to include declarations of functions. So let’s add a new description to our Kernel. All the file paths will be relative to the project’s root directory.

Open up the include/linux/syscalls.h file, and scroll all the way to the bottom. Right before that final #endif add the following line:

asmlinkage long sys_hello(void);

This should be very similar to what you are used to from standard C, with a bit extra because the Kernel is special like that.

Now that we have the declaration, we need to create the implementation of the function. Create the hello/hello.c file (the full path should be /usr/src/project0/hello/hello.c). The directory hello should not exist, so you have to create it. Add the following into the file:

#include <linux/kernel.h>
#include <linux/syscalls.h>

SYSCALL_DEFINE0(hello) {
    printk("Hello World!\n");
    return 0;
}

This is the implemetation of the sys_hello function. SYSCALL_DEFINE0() is a macro and it will take care a lot of function creation that the kernel requires. You might have noticed that the function is called hello instead of sys_hello. That’s because SYSCALL_DEFINE will create sys_hello for you. This is required for many reasons that we will not go into.

The next step is to add this file to the Kernel’s build system and Makefile. Create the hello/Makefile file, and add the following content to it:

obj-y := hello.o

And now we need to link this makefile with the kernel’s makefile. To do that open the Makefile in the root directory of the project, and go to line 1017. You should see the following:

ifeq ($(KBUILD_EXTMOD),)
core-y		+= kernel/ certs/ mm/ fs/ ipc/ security/ crypto/ block/

vmlinux-dirs	:= $(patsubst %/,%,$(filter %/, $(init-y) $(init-m) \
		     $(core-y) $(core-m) $(drivers-y) $(drivers-m) \
		     $(net-y) $(net-m) $(libs-y) $(libs-m) $(virt-y)))

Modify the core-y line to include the hello directory you created:

core-y		+= kernel/ certs/ mm/ fs/ ipc/ security/ crypto/ block/ hello/

Up until now this should seem familiar, with the exception of the Makefile specifics. Since we want to implement a systemcall however, we need a way to tell the kernel to add this function to the systemcall table. You might not have covered systemcalls fully yet in class. If so just keep this in mind and revisit once you do.

To do that open the arch/x86/entry/syscalls/syscall_64.tbl file , and go to the part of the file right before the beginning of the “x32-specific system call numbers” and add the following after the clone3 line (which should be line 359 of the unmodified 5.5.0 kernel):

436	common	hello			__x64_sys_hello

In this file, the first column represents the system call number. The second column indicates which ABI the system call is a part of. For x86-64, your options here are “common”, “64”, and “x32”. Most of the time, you want to use the “common” option to make it so the system call will work regardless of which mode is currently in use. The third column is the descriptive name of the system call. The last column is the name of the stub function that the kernel build will generate to call your system call. This should start with _x64 followed by the name of the actual function you’ve created. Every column except the second one (the ABI column) should have unique values for every system call.

You must now compile > install > reboot as you did during the setup!

Testing your system call

To test out your new system call, you must build a user-space program to call it. Create a new file in your home directory and copy/paste the following content into it:

Your home directory is /home/username or ~/. The two are equivalent.
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <linux/kernel.h>
#include <sys/syscall.h>

#define __NR_hello 436

long hello_syscall(void) {
    return syscall(__NR_hello);
}

int main(int argc, char *argv[]) {
    long rv;

    rv = hello_syscall();

    if(rv < 0) {
        perror("Hello syscall failed");
    }
    else {
        printf("Hello syscall ran successfully, check dmesg output\n");
    }

    return 0;
}

Note that if you had a different number for your system call in the previous step (i.e, you’re not using a clean copy of the 5.5 kernel source), then you will need to adjust the __NR_hello line. The only interesting part of this code is the part where the system call is called. This is done by making use of the syscall macro, provided by the C library. This function-like macro accepts an integer argument to note what system call to perform, along with any number of arguments to pass to the system call. So, if the hello system call accepted an argument (with a name, for instance), you would simply add that argument to the list passed there, after __NR_hello.

Compile the program with your system C compiler (gcc) and run it. The program will print out the success message if the syscall runs correctly, and you should see the “Hello World!” message if you look at the output running the dmesg program as root. If you get an error message from the program (which should usually say “Function not implemented”), make sure that you have made all the changes in the guide properly, built the modified kernel, installed it and rebooted into it before running the test program.

If you’ve gotten this far successfully, congratulations, you’ve written your first Linux kernel system call!

Submission Instructions

You have already initialized and created your git repository early in this project (when you ran the git clone command earlier). Now it is only a matter of updating the copy of the git repository on your local VM and pushing the update up to the GitHub server.

Here is what we expect you to submit:

  • All the files required for the modification of the version string
  • All the files required for the addition of the hello systemcall
  • The form at the very end of this section

Do not submit the test program described in the hello world system call tutorial to your repository. If you followed the directions in that tutorial, the test program should be in your home directory and not in the kernel source tree, thus it should not show up in the git status command below.

First, ensure that your changes are all reflected in the local git repository. Run the following command in a terminal in your /usr/src/project0 directory and ensure that any files you have modified within the kernel sources are included in the section labeled “Changes not staged for commit”: git status.

Next, you should ensure that the changes you made are accurately reflected in the modified data. To do so, run the command git diff and make sure that every change you made to the kernel source code is shown. You should be able to scroll back and forth through the diff output with the arrow keys on your keyboard, if needed.

Now that you have ensured that all the changes you made are reflected in the git repository’s status, you must add the changes to your local repository and commit the changes. Do so with the following commands (replace FilesYouModifiedOrAdded with the filename of any files/directories you modified or created including the hello/ directory and the hello.c and Makefile inside it — the git status command should help you here):

  • git status Will show you of all the changes git is aware of.
  • git add FilesYouModifiedOrAdded This will tell git that you want to track the changes of that file
  • git commit This will create a commit locally with all the new changes you have added so far.
You are not done with git. Keep reading!

Make sure to give a good commit message. Meaning the message should briefly explain what the commit does.

Now that you have committed your changes to your local git repository, you may wish to ensure that the commit has happened properly. To do so, run the git log command and make sure that your commit shows up at the top of the list.

Now that your commit is in your local repository, it is time to push the changes up to your GitHub account. To do so, run the git push origin master command. You should now be able to open your account on the GitHub website and see that your changes are pushed to the repository.

Please fill out this form to inform the TAs of your GitHub username. If you do not, we may not be able to grade your assignments, as we will not know which assignment belongs to whom.

Congratulations! You have completed Project 0!