edit · history · print

Due: Friday 9/22/06

This assignment has two goals:

  • to familiarize you with the basic ways of modifying the kernel, building kernel modules, and getting information in and out of the kernel.
  • to investigate some of the mechanisms for accessing kernel functionality from user space - system calls, the /proc filesystem, and devices.

At the end of this assignment, here, is a list of resources, one of which is a simple function (sxtime) to return the date and time as a formatted string. We will use this function as a data source in our code below.

Part 1

Here we investigate methods of moving information out of and into the kernel. Printk allows us to output information directly to the user; however, to get information to a program we use a file. The /proc filesystem has a set of kernel hooks to allow us to easily output information, and for more control we can create our own character device and implement its read and write methods.

Assignment: Create a module named sxtime which does the following:

  • outputs the current timestring (via printk) when it is loaded.
  • registers a /proc file (/proc/sxtime) which when read outputs the current timestring as a single line.
  • registers a character device /dev/sxtime, which printks the current timestring when opened.
  • Add a read() method; for any read call the current time string should be returned, truncated to the read length if necessary. If the buffer pointer passed to read() is invalid, then return -EFAULT.
  • (optional) Add an ioctl() method; ioctl 1 will cause the timestring to be printk'ed, and any other ioctl code will result in an error.

Hand in: Makefile and sxtime.c

detailed instructions

Part 2

The basic method for entering the kernel from user space is the kernel call - e.g. in the section above, the open() and read() system calls were used. Here we implement a custom kernel call.

Since the system call list is kept in a fixed table in the kernel, this cannot be done in a module and must instead be done by modifying and recompiling the kernel itself. (see ) To hand in the assignment you will use a common method for packaging and exchanging kernel modifications, the patch.

Assignment: Add a system call, 'sxtime', which takes 3 arguments - flag, buf, and len.

  • If flag is 0, then buf points to a null-terminated string, and lenis ignored. Copy and save that string, and return the number of bytes saved.
  • If flag is 1, then buf,len specifies a user buffer. Copy the saved string followed by the current timestring into this buffer, and return the length written.

If flag has any other value, return an error. (-EINVAL) As for the read() call, generate -EFAULT if necessary.

Hand in: a patch to the 2.6.15 kernel source (as distributed on the course CD) which adds this system call. Note patch instructions below. (optional) - add a configuration option which enables or disables whether the system call is compiled into the kernel.

Part 3 (very optional)

Until Linux 2.6, system calls were implemented on x86 processors with the int 0x80 instruction. (In 2.6, on newer CPUs the sysenter instruction is used - see here for details.)

int 0x80 jumps to a particular exception address - entry 0x80 - of 256, and the other entries available for software interrupts are unused. In the following exercise we implement a new "fast trap" into the kernel using int 0x81.

Start with the following files:

  • ftrap.S - Assembly code for the trap handler itself, _fast_trap, which calls the C function fast_trap.
  • ktrap.c - the actual module which installs the trap.
  • modgate.h - macros and assembly code to install and remove trap handlers.
  • fast_trap.h - user-space macro for invoking the fast trap.

You will need to get ftrap.S to compile, which should be an exercise in adding include files and #defines; arch/i386/kernel/entry.S should provide a good example. Then you need a fast_trap function - if it just does a printk indicating it ran, that should be enough - and a user-space program to invoke the trap.


Details - part 1

  1. start with the basic module skeleton from here.
  2. to create a /proc file you should use the seq_file interface, as described in class. You will need to create a struct seq_operations and initialize it with start, show, next, and stop methods.
    • As mentioned in class, since we are only only outputting a single line, the only function that needs to do anything other than return NULL or non-NULL is show(). The prototypes for these functions are in include/linux/seq_file.h:
        void * (*start) (struct seq_file *m, loff_t *pos);
        void (*stop) (struct seq_file *m, void *v);
        void * (*next) (struct seq_file *m, void *v, loff_t *pos);
        int (*show) (struct seq_file *m, void *v);
  • Use seq_printfto output the information - from seq_file.h:
        int seq_printf(struct seq_file *, const char *, ...)
  1. to register a miscellaneous character device you need to create a struct file_operations and a struct miscdevice
    • the struct file_operations needs open, read, and release methods, and open and read can be no-ops. The prototypes from these functions (from include/linux/fs.h) are:
        int (*open) (struct inode *, struct file *);
        ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
        int (*release) (struct inode *, struct file *);
  • in the struct miscdevice you need to set .minor = MISC_DYNAMIC_MINOR, .fops to the address of your file_operations structure, and .name = "sxtime".

Notes:

Error codes - in (almost?) all cases, system calls return positive or zero values for success, and negative values for errors. For a full list of error codes, look at include/asm/errno.h.

Patches - the simplest way to generate a patch is to keep two copies of the kernel source tree - a pristine one and the one you are working on. To generate the patch we then run a recursive diff:

    # ls
    linux-2.6.15.pristine
    linux-2.6.15
    # diff -Naur linux-2.6.15.pristine linux-2.6.15 > changes.patch
    #

You may want to investigate the use of the following diff option:

       --exclude=pattern
              When comparing  directories,  ignore  files  and  subdirectories
              whose basenames match pattern.

The file changes.patch - the patch file - will now show the changes in any files between the two versions; in addition (the -N flag) it will contain any new files in the modified version. To apply this patch file we use the patch command, along with the '-p' option to tell it to use the path names in the diff file:

     # cp -r linux-2.6.15.pristine linux-2.6.15.new-copy
     # cd linux-2.6.15.new-copy
     # patch -p1 < ../changes.patch

(since the new directory wasn't named 'linux-2.6.15', we couldn't use -p0. Instead we use -p1, which trims off the first directory in every path, and we change directories so the modified paths will work.) Note also that you can reverse a patch by applying the -R flag; thus applying the patch in reverse to your working tree should cause it to match the pristine tree.


Resources

The sxtime() function. This is a hack which will only work for September 2006.

    #include <linux/time.h>
    int sxtime(char *buf, size_t size)
    {
	/* 1/1/70 was a Thursday. */
	static char *days[] = {"Thu", "Fri", "Sat", "Sun", "Mon", "Tue", "Wed"};
	int n = xtime.tv_sec - 4*3600; /* adjust for GMT->EDT */
	int s = n % 60;    n = n / 60;
	int m = n % 60;    n = n / 60;              
	int h = n % 24;    n = n / 24;
	int dow = n % 7;
	int date = n - 13391;	/* crude hack - works for 9/2006 */
	return snprintf(buf, size, "%s Sep %d %02d:%02d:%02d 2006",
                        days[dow], date, h, m, s);
    }

notes - Part 1

There is a very simple module skeleton elsewhere on this site, here.

After looking at that, you may want to refer to the Kernel Module Programming Guide:

In particular:

For the /proc file interface, look at this first (esp. the last para.)

then you can check the LMPG at:

To create and register a character device:

Note in particular any information on copying back and forth between kernel and user space.

note - if you follow the proceeding instructions exactly, you'll have to find out the device major number and create /dev/sxtime manually, or else do a lot of nonsense with class_device_create(). Use misc_register(), instead, as shown in this rather dated article, and everything will be done for you.


notes - Part 2

There is a description of how to modify system calls in the KMPG here:

Note that this describes "hooking" an existing call. We are going to add a syscall - most of the explanation from the KMPG is still applicable, but the steps are:

  1. adding sys_xtime to arch/i386/kernel/syscall_table.S
  2. adding __NR_xtime to include/asm-i386/unistd.h and updating NR_syscalls.
  3. put the system call into a new file, kernel/sxtime.c
  4. adding sxtime.o in kernel/Makefile.

notes - Part 3

Send me a note if you try this section and are having trouble.

edit · history · print
Page last modified on September 15, 2006, at 05:33 PM EST