Prepared by: Richard A. Sevenich, rsevenic@netscape.net
Chapter 1. An Environment for Linux Device Driver Development 1.0 Introductory Comments The kernel developers use a simple version numerology, a dotted triple of form X.Y.Z e.g. 2.3.4 or 2.4.1. If the second number (Y) is odd then the kernel is a development version; whereas, if the second number is even, then the kernel is a stable, production version. Hence, 2.3.4 is from the development tree and 2.4.1 is from the stable tree. The ordinary user should pick from the stable tree and, when possible, make a choice that seems to be particularly stable. Developers might be running a machine with separate partitions affording various choices. To keep track of what is happening with current kernel development, browse the Kernel Traffic website at http://kt.zork.net/. This also gives useful links. For example, this is a place to start a search for descriptions of changes to the API when the kernel undergoes a major revision (e.g. 2.2.x to 2.4.x). In this course we will work with the current stable kernel version, available from http://www.kernel.org/. 1.1 Kernel Mode Versus User Mode Linux operates in one of two available modes, either user mode or kernel mode. This impacts how one writes code for operating in these two modes, as well. When one programs for user mode, one may use libc and include the related header files. However, when developing for kernel mode, one is constrained to using functions/symbols that it exports. For example, a function as commonplace as printf is not available - there fortunately is a kernel mode replacement, printk. To see the kernel symbols available, look in /proc/ksyms. There, for example, you would find printk listed. If you find something such as c0112d1c printk_Rdd132261 with the suffix attached to printk, then your kernel is configured with the CONFIG_MODVERSIONS setting true. If there is no such suffix, CONFIG_MODVERSIONS is not set. So what is the meaning of this suffix? The answer lies in understanding version dependency. There can be significant changes from one version of the kernel to the next. For example, the prototype for get_user changed from kernel version 2.0 to 2.2. One way to compensate for this possibility is to recompile your module for each different kernel in which it is used. In fact, insmod checks the kernel version, under which the module was compiled, against the currently running kernel. With this convention, recompilation of the module using header files consistent with the currently running kernel is necessary - even if none of the involved function prototypes and underlying data structures have changed. There is a somewhat flexible way to avoid unnecessary recompilation. What is done is to parse the function prototypes and the data structure definitions and create a representative 32-bit CRC, which is appended to the kernel symbol name. For the printk example above the 32-bit CRC would be 0xdd132261. Then insmod checks for consistent CRC values instead of for the kernel version number. To enable this option, the CONFIG_MODVERSIONS setting is active. When you later recompile the kernel, this option will be chosen as true. R.A. Sevenich 2004 Introduction to Linux Device Driver Development 1- 1 1.2 Include Files The header files for use in our drivers are typically drawn from these directories: /usr/src/linux/include/linux/ - chip architecture independent /usr/src/linux/include/asm/ - chip architecture dependent There may be links to these directories from the traditional locations (but sometimes aren't), which are, respectively, /usr/include/linux /usr/include/asm Include statements will then be in this sort of style #include <linux/kernel.h> - chip architecture independent #include <asm/uaccess.h> - chip architecture dependent 1.3 Kernel Data Types The data sizes for standard C types may vary across a set of architectures. For example, for the IA-32 we have (in bytes) sizeof(long) = 4 whereas for the alpha we have sizeof(long) = 8 Clearly, wherever size is a potential issue, the driver writer must take care. For this purpose, linux defines these signed and unsigned size-specific types (in bits - see <asm/types.h>): s8, s16, s32, s64 u8, u16, u32, u64 Further, in <linux/types.h> we find the list of '_t' types found, for example, in prototypes. These are based on typedef statements intended to ensure portability. Here is an example, ssize_t (*read) (struct file *, char *, size_t, loff_t *); which is intended as portable across diverse architectures. 1.4 Getting and Installing the Kernel Sources It is useful to work through a concrete example, making specific choices which will provide later flexibility. Assume that your machine is currently operating with kernel version 2.4.23 and that you wish to install a newer version. 2.4.24. To download kernel sources, you can start by browsing the website http://www.kernel.org/pub/ . This will lead you to a mirror site for whatever country is most convenient for downloading, not necessarily the USA. You can download from two possible formats, either '*.tar.gz' or '*.tar.bz2', and with file sizes for the latter currently on the order of 30 MB. Place the downloaded linux-2.4.24.tar.bz2 (or equivalent) into '/usr/src/', the conventional location. It will be asumed here that you are about to install a kernel version different from that currently on your machine. [Note: On the other hand, if you are recompiling your current version, protecting your original version is somewhat more involved. In that case, get guidance from your instructor.] R.A. Sevenich 2004 Introduction to Linux Device Driver Development 1- 2 Here are the necessary steps (to be taken as root): 1. From within /usr/src, explode the newly downloaded tzarball e.g. tar xvfj linux-2.4.24.tar.bz2 and then change to the just created directory e.g. cd linux-2.4.24 2. Enter the command make mrproper Note: This does a lot of cleanup, which may or may not be necessary. Among other files, it destroys any existing dot files e.g. '.config'. If that file is one you've worked hard to achieve, you'd better back it up (but not to a dot file). 3. Enter the command make xconfig or else make menuconfig Note: In either case, this then requires you to make lots of choices - some obvious and some arcane. An intimate knowledge of your machine is useful. Some distributions now provide some default choices for your machine based on the original Linux install. Ask your instructor about your particular distribution. One of the results of this step is a new '.config' file. Once a '.config' exists it provides default choices when/if this step is repeated. 4. Enter the command make dep 5. Enter the command make bzImage Note: This produces a compressed kernel image based on the choices made in step 3. If the compressed image is not too large, you can also make a boot floppy via 'make bzdisk'. 6. Enter the command make modules Note: This creates any modules you requested in step 3. 7. Enter the command make modules_install Note: This installs the modules created in step 6 to a directory hierarchy in /lib/modules e.g. to /lib/modules/2.4.24. The target directory name, e.g. 2.4.2, can be made unique by placing a unique name in the 'EXTRAVERSION' field found very near the start of the Makefile, i.e /usr/src/linux-2.4.24/Makefile. 8. Copy the new system map to /boot e.g. cp System.map /boot/System.map-2.4.24 9. Copy the new kernel image to /boot e.g. cp arch/i386/boot/bzImage /boot/vmlinuz-2.4.24 1.5 Safely Booting the Freshly Compiled Kernel with lilo We'll assume that your system currently boots by using 'lilo', the Linux loader, which typically writes boot code to the master boot record. We also assume that you have a boot floppy or other rescue disk or cdrom available in case the installation goes badly awry. For in-depth documentation on 'lilo' see the manual which can be found in '/usr/doc/lilo'. The system was already working and booting properly - so we don't want to lose access to that former kernel if things don't work out well with the new kernel. It is assumed that your system boots using the Linux loader, lilo. The configuration file for lilo governs the boot process and is found as the ASCII file '/etc/lilo.conf'. R.A. Sevenich 2004 Introduction to Linux Device Driver Development 1- 3 Here is a typical lilo.conf for a machine with a single working kernel: #/etc/lilo.conf boot=/dev/hda verbose=2 compact install=/boot/boot.b map=/boot/map vga=normal prompt message=/boot/message image=/boot/vmlinuz-2.4.23 root=/dev/hda1 label=linux-2.2.12 initrd=/boot/initrd-2.4.23.img read-only The 'prompt' keyword indicates that a prompt will be issued to the user. The prompt is based on the contents of / etc/lilo.conf as encoded in the binary file /boot/message. This may display a distribution specific graphic as well. The preceding lilo.conf file assumes that the appropriate hard drive is connected as the master (/dev/hda) on the first IDE controller connector. This may be otherwise for your machine and your existing lilo.conf will give the essential guidance in this regard. In particular, it is not necessarily /dev/hda etc. The idea now is to modify the boot process to allow as a second boot possibility, the replacement kernel. One might proceed as follows (the order is important): Step 1 Edit '/etc/lilo.conf', adding a new boot image to obtain this: #/etc/lilo.conf boot=/dev/hda verbose=2 compact install=/boot/boot.b map=/boot/map vga=normal prompt message=/boot/message image=/boot/vmlinuz-2.4.23 root=/dev/hda1 label=linux-2.2.12 initrd=/boot/initrd-2.4.23.img read-only image=/boot/vmlinuz-2.4.24 root=/dev/hda1 label=linux-2.4.24 read-only Step 2 [Don't miss this one!] After saving your new /etc/lilo.conf, run 'lilo' at the command line by typing /sbin/lilo which should give reassuring messages as it writes the new master boot record. R.A. Sevenich 2004 Introduction to Linux Device Driver Development 1- 4 Note: If your system boots using grub, you must add a boot stanza to /boot/grub/menu.lst. This process is similar to that used for /etc/lilo.conf; in particular, menu.lst will have at least one preexisting boot stanza which can be used as template to copy and modify appropriately (be sure to retain the original boot stanza in menu.lst). We won't discuss this in detail. Once menu.lst is modified, there is no further step analogous to running lilo as described in Step 2 of the lilo discussion i.e. the system is ready to reboot once menu.lst is modified appropriately. 1.6 Activities Activity 1 Download a recent kernel version (as specified by your instructor), configure it, and compile it for your hardware. Ensure that module versioning is configured. Also, in preparation for a later case study, ensure that the use of the parallel port is not configured. The instructor may have other configuration suggestions. Activity 2 Minor modifications are added to the kernel by a process called 'patching'. Investigate how to do this by reading the man page for 'patch'. Then download a patch intended for your kernel and update your kernel. We suggest the kdb patch set (two files) as a candidate (see http://oss.sgi.com/projects/kdb/). R.A. Sevenich 2004 Introduction to Linux Device Driver Development 1- 5