Adding a System Call to a Linux Kernel



In this lab, we will add a system call to the kernel source directory. Today you will create our own KVM image, create a user account on this image. Then, you will be modifying the kernel source directory by adding a new system call to the kernel. Then you will build and install the modified kernel on this image. Finally, we will test the system call.


Table of Contents:

 

Resources

 

Installing QEMU (No need for this step, if you already have QEMU functional from lab 2)

For today's lab, you will be directly working on your machine. We won't be needing VirtualBox or PuTTY for today's lab. You can use the host OS of your machine. It can either be Linux, Windows or MacOS.

  1. Now, go to QEMU's website (qemu.org)
  2. Then, go to the Downloads page and follow the instructions for your specific Operating System. Download QEMU.
  3. Open the downloaded QEMU installer and install QEMU.
  4. Add the installed QEMU directory to your System's PATH environment variable.

We will be using QEMU from the command prompt (Windows) or terminal (Linux, MacOS). The commands are the same for the different platforms.

 

Creating Your Image

Like lab2, we will need a base image first. Then, we will create a differential image off the base image.

  1. For the base image, we have two options:

    • You can either use the base image that you had copied from the cycle servers during lab2.
    •  
    • Or, you can fetch the base image from the cycle servers using the following command:
      scp your_ku_online_id@cycle1.eecs.ku.edu:/srv/EECS_678/eecs678_base.qcow .
  2.  
  3. Open up either the command prompt (on Windows) or terminal (on Linux, MacOS). Navigate to the directory where you have stored the based image.
  4.  
  5. Then, create a differential image off this local copy of the base image. Even though we have already created a differential image for lab2, we will need a new differential image. The command is:

    > qemu-img create -f qcow2 -F qcow2 -b [base_image] [diff_image] 

    This example creates a differential image named 'your_first_name.qcow':
    qemu-img create -f qcow2 -F qcow2 -b eecs678_base.qcow your_first_name.qcow
    (Screenshot)

 

Boot Your Image
You will do this much more often. There are several options for booting into your image.
  1. The basic command for booting into your image is:

    > qemu-system-x86_64 -hda [boot-img] -m [mem] 

    This example boots into the image 'your_first_name.qcow' and simulates it on a machine with 2048M of memory:
    qemu-system-x86_64 -hda your_first_name.qcow -smp 2 -m 2048M
    (Screenshot)

    Here, -smp value denotes the number of cores and -m value denotes the amount of RAM we want to allocate to the QEMU system. This will depend on how many cores and how much memory, you are willing to share from your physical machine with the QEMU system.

 

Setting up Your Account
Boot into your image as the root user. Your image comes with Debian 6.0 pre-installed. Most software you will need is already installed. The last section in this guide describes how to install new software on your KVM. The source code of the kernel we will use is stored on your image in /kernel-src and the config file we will use is in /configs. We need to do a little more setup before we build our kernel.
  1. Create a user account for yourself:

    > adduser [username] 

    This example creates an account for user 'your_first_name'. After you have given the user a password, you can ignore adding the other information:
    root@debian:~# adduser your_first_name
    Adding user `your_first_name' ...
    Adding new group `your_first_name' (1000) ...
    Adding new user `your_first_name' (1000) with group `your_first_name' ...
    Creating home directory `/home/your_first_name' ...
    Copying files from `/etc/skel' ...
    Enter new UNIX password:
    Retype new UNIX password:
    passwd: password updated successfully
    Changing the user information for your_first_name
    Enter the new value, or press ENTER for the default
            Full Name []: 
            Room Number []:
            Work Phone []:
            Home Phone []:
            Other []:
    Is the information correct? [Y/n]
    root@debian:~#
    
    (Screenshot)
     
  2. Add your new user to the sudoers file:
    root@debian:~# vi /etc/sudoers
      
    This will open vi on /etc/sudoers. Add your new user to this file as shown:
    # User privilege specification
    root ALL=(ALL) ALL
    your_first_name ALL=(ALL) ALL
    
  3. (Screenshot)
     
  4. It may also be helpful to add yourself to the sudo group:
    root@debian:~# usermod -a -G sudo your_first_name
  5. (Screenshot)
     
  6. Next we want to move the kernel source (which we have created a folder for you) and the config file (which we have also moved to .config for you) to the user's home directory and change ownership to the user:
    root@debian:~# mkdir /home/your_first_name/kernel
    root@debian:~# mv ~/linux-2.6.32.60 /home/your_first_name/kernel/
    root@debian:~# chown -R your_first_name:your_first_name /home/your_first_name/kernel
            
  7. (Screenshot)
     
  8. We now need to install sudo on the kernel so the user can call commands with the rights sudo grants.
     root@debian:~# apt-get install sudo 
  9. (Screenshot)
     
  10. Optionally, you can copy the .vimrc from /root to your user's home directory (and change ownership to your user):
    root@debian:~# cp /root/.vimrc /home/your_first_name/
    root@debian:~# chown your_first_name:your_first_name /home/your_first_name/.vimrc
      
  11. (No Screenshot needed)
     
  12. Finally, switch user to the new user you have just created:
    root@debian:~# su your_first_name
  13. (Screenshot)
     
  14. At some point, during the installation, you might get the following error:
    error: zlib.h: No such file or directory

    In order to avoid this error, please install the libz now using the following command:
    sudo apt-get install libz-dev
  15. (Screenshot)
     
Adding a System Call to the Linux Kernel
To add a system call to the kernel source directory carry out the following steps:
  1. Go to your home directory using the following command:
    cd

  2. You should have a directory named "linux-2.6.32.60" in your "/home/<your_first_name>/kernel/". Go into the directory "linux-2.6.32.60" using:
    cd /home/<user>/kernel/linux-2.6.32.60
    (Screenshot)

  3. Create a directory named "hello" and create a c program called "hello.c" inside the directory. This "hello.c" will be the definition of the system call. Do the following:
    mkdir hello
    cd hello
    vi hello.c
    (Screenshot)

    The hello.c program should contain the following:


    #include <linux/kernel.h>

    asmlinkage long sys_hello(void)
    {
    printk("Hello world\n");
    return 100;
    }

    (Screenshot)


    asmlinkage -- Tells the gcc compiler that the function should expect all its arguments on the "stack", and not in registers. On 32-bit Linux systems, system calls can have 4 arguments (in addition to the syscall number) that are passed on the stack. Of course, this "asmlinkage" macro is not necessary for our "hello" syscall, since it does not take any argument.
    printk -- function to print messages used exclusively for the Linux Kernel. Mostly used for logging messages from the kernel.


  4. Exit the hello.c source file and create another file called "Makefile" in the "hello" directory, using the following:
    vi Makefile
    (Screenshot)

    The Makefile should contain the following:

    obj-y := hello.o

    (Screenshot)

  5. Go back to the "linux-2.6.32.60" directory using
    cd ..
    Now edit the existing Makefile in the "linux-2.6.32.60" directory, using
    vi Makefile
    Once you are inside the Makefile, search for "core-y". In Vim/Vi, you can search this by going to the command mode by pressing "Esc" and executing the command /core-y. You can press "n" to go to the next instance of core-y. You should be able to find an instance which looks like the following:

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

    Sometimes, after vi has found your desired search term, it might highlight the term (in this case, core-y) in black. As a result, it might apparently seem that the term core-y is invisible whereas, it has only been highlighted with the same black color as that of the background. So, try to manually look at the found line and check whether it contains the text starting from += upto block/.


    Now add "hello/" at the end of this line. You should ensure that there is a space between "block/" and "hello/". The line should look like this:

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

    (Screenshot)

    These are the directories that will be recursively visited during the kernel compilation to look for source files that need to be compiled according to the individual Makefile in those directories.


  6. While you are in the "linux-2.6.32.60" directory, go to the directory "arch/x86/kernel/" using
    cd arch/x86/kernel/
    Open the file named "syscall_table_32.S" using
    vi syscall_table_32.S
    To add our system call's entry into the system call table, edit the file "syscall_table_32.S" by adding the following at the end of this file:

    .long sys_hello

    (Screenshot)

  7. Go back to the "arch/x86" directory by using
    cd ..
    Then, go to the "include/asm" directory by doing
    cd include/asm
    Then, open the file named "unistd_32.h" using
    vi unistd_32.h
    Now, look for the line #define __NR_sched_other_rr_getquantum using the following in vi command mode:
    /#define __NR_sched_other_rr_getquantum
    Once you have found this line, go into the vi insert mode by pressing "i" and after the line
    "#define __NR_sched_other_rr_getquantum", add the following line :

    #define __NR_hello 338

    Before adding __NR_hello, the last system call entry was __NR_sched_other_rr_getquatum with the value 337. We add 1 to it and get the __NR_hello entry with 338.

    Also, increment the existing NR_syscalls value by 1. So, NR_syscalls should be 339

    #define NR_syscalls 339

    (Screenshot)


  8. Go back to the "linux-2.6.32.60" directory using
    cd ../../../..
    Then, go to the "include/linux" directory using
    cd include/linux
    Open the file named "syscalls.h" and add the declaration for our system call, at the end of the file.
    vi syscalls.h

    Then, add the following:

    asmlinkage long sys_hello(void);

    (Screenshot)

Building and Installing Your Kernel
Building and installing the kernel on our Debian system is easy. Follow these simple instructions:
  1. We will use the kvm-kernel-build script to build our kernel. kvm-kernel-build is actually a script. It's contents are shown below:
    #!/bin/sh
    rev=$1
    if [ -z "$rev" ]; then
            echo "Usage: build64 
            "
            exit 1
    fi
    make-kpkg --rootcmd fakeroot --initrd --revision=$rev kernel_image 2>&1 | tee build.log
                    
  2.  
  3. But first, we need to add some level of concurrency to the building process. Open the kvm-kernel-build script.
    sudo vi /usr/bin/kvm-kernel-build
  4. (Screenshot)
     
  5. Then, add the -j flag with value 2 to the make-kpkg command at the end. The updated script should look as follows:
    #!/bin/sh
    rev=$1
    if [ -z "$rev" ]; then
            echo "Usage: build64 
            "
            exit 1
    fi
    make-kpkg -j 2 --rootcmd fakeroot --initrd --revision=$rev kernel_image 2>&1 | tee build.log
                    
  6. (Screenshot)
     
  7. To build the kernel, run the command:

    > sudo kvm-kernel-build [revision] 
    This script takes a single argument, a revision number, which is used to give a unique name to your built kernel package. You always need to run this command from your kernel source directory. We will run it with sudo as the user. For example:
    your_first_name@debian:~$ cd /home/your_first_name/kernel/linux-2.6.32.60/
    your_first_name@debian:~/kernel/linux-2.6.32.60$ sudo kvm-kernel-build 1
      
    (Screenshot)

      This will build the kernel in the KVM. The build process takes a while. Go ahead and relax,
    check your favorite website. If your screen goes black, you can press a key, like the down arrow, to show output again. If for some reason, you need to leave your station for a while, you should ensure that you are not "logging out". Because, in some cases, it might kill the ongoing build process. Instead, you can lock your screen. This should ensure that your build process is continuing.

    When the build process is complete, you should find a .deb file in the directory above your kernel source (so use cd to go back one directory). If you want to scroll the ouput from the build, you can use shift + page up or page down to scroll.

  8. To install your kernel, the command is:

    > dpkg -i [kernel-deb-pkg] 

    You should run this command as root (using su then typing in the root password) as in:
    your_first_name@debian:~/kernel# su
    Password:
    root@debian:home/your_first_name/kernel$ dpkg -i linux-image-2.6.32.60_1_i386.deb
    Selecting previously deselected package linux-image-2.6.32.60.
    (Reading database ... 30041 files and directories currently installed.)
    Unpacking linux-image-2.6.32.60 (from linux-image-2.6.32.60_1_i386.deb) ...
    Done.
    Setting up linux-image-2.6.32.60 (1) ...
    Running depmod.
    Examining /etc/kernel/postinst.d.
    run-parts: executing /etc/kernel/postinst.d/initramfs-tools 2.6.32.60 /boot/vmlinuz-2.6.32.60
    update-initramfs: Generating /boot/initrd.img-2.6.32.60
    run-parts: executing /etc/kernel/postinst.d/zz-update-grub 2.6.32.60 /boot/vmlinuz-2.6.32.60
    Generating grub.cfg ...
    Found linux image: /boot/vmlinuz-2.6.32.60
    Found initrd image: /boot/initrd.img-2.6.32.60
    Found linux image: /boot/vmlinuz-2.6.32-5-686
    Found initrd image: /boot/initrd.img-2.6.32-5-686
    done
    
    (Screenshot)
    When the command completes, the kernel is installed. Go back to being logged into your user using su your_first_name

  9. (Optional) Our installation should have updated the boot loader to select our kernel on the next boot. To check that the bootloader is set up correctly, do:
    your_first_name@eecs678-kvm:~/kernel$ cat /boot/grub/grub.cfg | more
      
    You should see a line that reads:
    set default="0"
      
    The first 'menuentry' in this file should correspond to the kernel you just built (i.e. the version number should match 2.6.32.60). The next time you boot your kvm, it will have both the original kernel and the newly installed kernel selectable from the boot menu. If you use the -nographic option, you will not have the opportunity to select a kernel from the menu and it will automatically boot the default kernel. To change the default boot kernel, you need to change the GRUB_DEFAULT variable in /etc/default/grub to the number of the kernel in the boot menu that you want to boot into. The kernels in the boot menu are listed in /boot/grub/grub.cfg and are indexed at 0. If you change GRUB_DEFAULT, you need to update the grub menu by running (as root):

    > update-grub 

    For now, leave this option as GRUB_DEFAULT=0 and reboot your kvm (as root):
    your_first_name@debian:~/kernel# su
    Password:
    root@debian:/home/your_first_name/kernel# reboot
    
    Broadcast message from root@debian (pts/0) (Fri Mar  1 22:31:19 2013):
    The system is going down for reboot NOW!
    root@debian:/home/your_first_name/kernel# Connection to localhost closed by remote host.
    Connection to localhost closed.
      
    This will kick you out of your kvm session. You need to wait for the KVM to boot before you can log back in.

  10. Next, we need to check that the kvm has booted into the newly built kernel. First, we will reboot the system on QEMU using the following:
    root@debian:/home/your_first_name/kernel# reboot
    
    Broadcast message from root@debian (pts/0) (Fri Mar  1 22:31:19 2013):
    The system is going down for reboot NOW!
    root@debian:/home/your_first_name/kernel# Connection to localhost closed by remote host.
    Connection to localhost closed.
            
    Then, you should select the Linux debian 2.6.32.60 option from the blue boot menu. You can log in directly to your newly created user account:
    Linux debian 2.6.32.60 #1 SMP PREEMPT Fri Mar 1 21:56:30 CST 2013 i686
    
    The programs included with the Debian GNU/Linux system are free software;
    the exact distribution terms for each program are described in the
    individual files in /usr/share/doc/*/copyright.
    
    Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
    permitted by applicable law.
    your_first_name@debian:~$
      
    You should see that you are running kernel version 2.6.32.60. If you want to double-check this from the command line inside KVM, you can do:
    your_first_name@debian:~$ uname -a
    Linux eecs678-kvm 2.6.32.60 #1 SMP PREEMPT Fri Mar 1 21:56:30 CST 2013 i686 GNU/Linux
  11. (Screenshot)

Testing the System Call

Now that we have booted into the system with the modified kernel, we can test our system call. To test the system call, carry out the following steps:
  1. Go to your home directory by doing
    cd

  2. Create a new "test_syscall.c" file by doing
    vi test_syscall.c
    The test_syscall.c file should contain the following:

    #include <stdio.h>
    #include <sys/syscall.h>
    #include <unistd.h>
    #include <linux/kernel.h>
    int main()
    {
    long int syscall_val = syscall(338);
    printf("System call sys_hello returned %ld\n", syscall_val);
    return 0;
    }

    (Screenshot)

  3. Compile the test_syscall.c file using the following command.
    gcc test_syscall.c -o test_syscall
    Now run test_syscall using the following command.
    ./test_syscall

    This should print "System call sys_hello returned 100\n" based on the hello.c program

    (Screenshot - after getting the output from ./test_syscall)

  4. Issue the following command:
    dmesg | tail -5

    This prints the last 5 lines from the kernel logs. Please look for the "Hello World" message printed in the kernel logs from your system call.

    "Hello World" should be printed on the kernel logs based on the printk call in hello.c program

    (Screenshot - after getting the output from dmesg)
Package Issues

If you get the following error during your kernel build process:

root@debian:~# error: zlib.h: No such file or directory
Please install the libz package by executing the following command:
 root@debian:~# apt-get install libz-dev 

Other Important Commands
There are some important commands you should know for using your KVM. This is not a complete list. More commands and information will be added to this section as necessary.
  1. If you would like to install or remove software, use the APT package utility:

    • > apt-get install [package] -- install a package. You must be root to install software.
    • > apt-get remove [package] -- remove a package. You must be root to remove software.
    • > apt-cache search [string] -- search all available package lists for the given string. You can use this utility to find software and package names.


  2. To transfer files to and from your KVM, use scp:

    > scp [user@][src-host]:[src-file] [user@][dst-host]:[dst-file] 

    You can omit the user and host information if it matches the environment you are running the command in. In this example, scp is run from the KVM to copy the file foo.pdf on cycle1.eecs.ku.edu to my kernel directory in the KVM:
    your_first_name@debian:~$ scp cycle1.eecs.ku.edu:~/Desktop/foo.txt /home/your_first_name/kernel/linux-2.6.32.60/
    amodarre@cycle1.eecs.ku.edu's password:
    foo.txt                                                               100%  954KB 954.3KB/s   00:00
    your_first_name@debian:~$
        
    This example, which is also run from within the KVM, copies foo.pdf from the kvm to my Desktop on cycle1.eecs.ku.edu:
    your_first_name@eecs678-kvm:~$ scp /home/your_first_name/kernel/linux-2.6.32.60/foo.txt cycle1.eecs.ku.edu:~/Desktop/
    amodarre@cycle1.eecs.ku.edu's password:
    foo.txt                                                               100%  954KB 954.3KB/s   00:00
    your_first_name@debian:~$
        
  3. If you need to remove a built kernel, use dpkg:

    > dpkg -r [kernel-package] 

    Note that, to remove a built kernel, you cannot be currently running that same kernel. This example removes the kernel we built in this lab:
    root@debian:~# dpkg -r linux-image-2.6.32.60
    (Reading database ... 30089 files and directories currently installed.)
    Removing linux-image-2.6.32.60 ...
    Examining /etc/kernel/prerm.d.
    Examining /etc/kernel/postrm.d .
    run-parts: executing /etc/kernel/postrm.d/initramfs-tools 2.6.32.60 /boot/vmlinuz-2.6.32.60
    update-initramfs: Deleting /boot/initrd.img-2.6.32.60
    run-parts: executing /etc/kernel/postrm.d/zz-update-grub 2.6.32.60 /boot/vmlinuz-2.6.32.60
    Generating grub.cfg ...
    Found linux image: /boot/vmlinuz-2.6.32-5-686
    Found initrd image: /boot/initrd.img-2.6.32-5-686
    done
      
    Note that you might need to reconfigure your boot loader after you uninstall the kernel package.

    In most cases, you will reinstall your built kernel over a kernel you have previously installed.

  4. Finally, to exit the kvm, run halt (as root) from your kvm terminal:

    > halt 

    For example:
    root@debian:~# halt
    
    Broadcast message from root@kvm (pts/0) (Thu Mar 21 19:58:54 2013):
    
    The system is going down for system halt NOW!
      

Debugging the Linux Kernel

Sometimes, it is necessary to add sometime to Linux kernel for experiment or fix a bug in the kernel. Debugging the kernel is a little bit different than debugging an application; because OS doesn't let any access from user to kernel mode directly. The following slides help you to understand how to debug the kernel.

Debugging the Linux Kernel

Submission Requirements

Each student must submit a single report documenting their work for this lab. The report must include a brief description of your process and all required screenshots indicated throughout this document. Your grade for this lab is based entirely on this report and is worth 100 points.

 

< Back to the Lab Home Page