Getting Started with L4Ka::Pistachio on x86-x32
By Ihor Kuz
Because the L4Ka::Pistachio (which we will simply refer to as L4 or Pistachio) distribution doesn't exactly provide easy to find (much less easy to use) documentation (it unpacks to a top level directory with absolutely no documentation), this document is meant to guide the non-gurus toward a working kernel.
While this document is aimed toward building a kernel for x86-x32, the general steps to build a kernel for non-x86-x32 systems will be largely similar. The main differences would be the use of a cross compiler toolchain, different hardware configuration, and different tools used to create bootable images.
This document assumes that you will be building the development version Pistachio on an x86-x32 Gnu/Linux system that has the gcc toolchain version 3.2 (or greater) already installed (i.e., I'm assuming that it was installed as part of your Linux distribution). Since the source and target platforms are the same, no cross compiler is necessary.
The steps outlines here will probably also work for non-Linux systems (e.g., FreeBSD), however your mileage may vary.
Before starting we need to download the Pistachio distribution.
Unpacking the tarball gives a directory called
> ls l4ka-pistachio
AUTHORS contrib doc kernel tools user
Building L4 proceeds in two stages. The first stage involves configuring and building the kernel, the second involves configuring and building the user-level code. In both stages the actual build occurs in a (build) directory separate from the source directory.
Stage 1: Building the kernel
The first thing to do is to prepare a directory in which to build the kernel. This is done by going into the kernel source directory, calling make and telling it where the build directory should be created. Assuming we want to do the building in
l4ka-pistachio/x86-kernel-build we do:
BUILDDIR requires an absolute path name.
Then we go to the newly created build directory, configure the kernel, and build it.
Configuring the kernel
A word on kernel configuration. The command
make menuconfig (or
make xconfig) presents the user with a GUI that allows various kernel configuration parameters to be set. There are three categories of settings: Hardware, Kernel, and Debugger.
In the first category (Hardware), you must specify what hardware the kernel will be compiled for. Choose the x86 basic architecture and x32 subarchitecture, and appropriate processor type (if you are planning to run the kernel using the Qemu emulator, then choose Pentium 1). In the Kernel category you can turn kernel features on or off (note that if you disable debugging mode then the third main category, Debugging, will not be available). In the third category, Debugging, you can set parameters relating to debugging.
If you enable the kernel debugger you can choose whether to have the debug output sent to the console (keyboard) or to a serial port (in which case you can set the I/O address - 0x3f8 = tty0, 0x2f8 = tty1,0x3e8 = tty2, 0x2e8 =tty3 - and speed appropriately).
make menuconfig type `x' to save the configuration and exit, `q' to exit without saving. In
make xconfig choose File->Save & Exit from the menu.
Stage 2: Building user-level code
The steps taken to build the user-level code are somewhat different from the kernel build. First the build directory has to be created manually, and the user-level code's configure script has to be called from this new directory. Then the generated makefile can be used to build and install the tools, libraries and servers.
Note that make install will install into
/usr/local. In order to change this, pass a
--prefix=<install dir> argument to configure. Likewise you can tell configure where the kernel can be found with a
--with-kerneldir=<kernel build dir> argument. This leads to e.g.:
../user/configure --prefix=../x86-x32-user-install --with-kerneldir=../x86-kernel-build
Furthermore if you want console output to be redirected to a serial port then you should include a
--with-comport=<port> argument (
<port> being 0 for tty0, 1 for tty1, etc.) and possibly a
--with-comspeed=<speed> to set the serial line speed. The configure command then becomes:
../user/configure --with-comport=0 --with-comspeed=115200 --prefix=../x86-x32-user-install --with-kerneldir=../x86-kernel-build
There are a number of other arguments, a description of which can be gotten with
../user/configure --help. Also take a look at the file INSTALL in the user subdirectory.
After following the steps above the user-level code will have been installed in a different directory from the kernel. It may be a good idea to copy the kernel to the same directory as the servers, e.g.,
cp l4ka-pistachio/x86-kernel-build/x86-kernel l4ka-pistachio/x86-x32-user-install/libexec/l4/
Booting and Running
Once the kernel and user-level code are built, it is time to boot L4 and run pingpong, the standard example program. On x86-x32 the standard way to boot L4 is to use the GRUB boot loader. This means that you need a recent version of grub installed (once again it should be installed on most standard Linux distributions).
There are a number of basic ways to use grub to boot L4. These include creating a floppy disk image containing the kernel and all user-level code to run, creating a harddisk image containing the kernel and all the user-level code to run, and creating a floppy (or hard) disk image that loads the kernel and user-level code over the network using TFTP. There are also two different approaches to creating the images, one that requires root access, and one that doesn't.
This document will cover the root and non-root approaches to creating a self-contained floppy disk boot image. Descriptions of other approaches may be provided in a separate document.
The following steps must be performed for both the root and non-root approaches.
First, prepare a directory where you will collect all the files that will go on the boot floppy image:
mkdir -p fdsource/boot/grub
cp /boot/grub/stage1 fdsource/boot/grub
cp /boot/grub/stage2 fdsource/boot/grub
/boot/grub/stage2 should have been created when GRUB was installed. Consult GRUB documentation or the Web if you can't find them.
Next, copy the l4 kernel and user-level servers to the directory:
cp l4ka-pistachio/x86-kernel-build/x86-kernel fdsource/
cp l4ka-pistachio/x86-x32-user-install/libexec/l4/* fdsource/
Then create the file
fdsource/boot/grub/grub.conf with the following contents
# serial --port=0x3f8 --speed=115200
# terminal --timeout=0 serial
Note that if you want all output to go to the serial port then uncomment the "
serial --port=0x3f8 --speed=115200" and "
terminal --timeout=0 serial" lines (and set the serial port and speed to appropriate values)
Create disk image (root privileges required)
This approach to creating a disk image requires root privileges for a number of the commands. First we create a file (we're calling it
fdimage.img) of the right size (1440K), set it up as a loopback device, and create a filesystem on it. Then we mount the loopback device, copy the required files to it and install grub on it. (note that if the mount point /
mnt/fda doesn't exist you can always create a new one with
dd if=/dev/zero of=fdimage.img bs=512 count=2880
/sbin/losetup /dev/loop0 fdimage.img
mount /dev/loop0 -o loop /mnt/fda
chmod 777 /mnt/fda
cp -aR fdsource/* /mnt/fda
cat <<EOF | /sbin/grub --batch --device-map=/dev/null
device (fd0) /dev/loop0
/sbin/losetup -d /dev/loop0
Create disk image (no root privileges required)
This approach does not require root privileges and instead makes use of the mtools toolset (a set of tools used to manipulate DOS disks and filesystems). In this approach we first create a configuration file for mtools specifying which image file to manipulate, then we create a file (which we'll call
fdimage.img) of the right size, create a filesystem on it, copy the required files to it and install grub on it.
dd if=/dev/zero of=fdimage.img bs=512 count=2880
echo 'drive a: file="fdimage.img"' > mtoolsrc
MTOOLSRC=./mtoolsrc mformat -f 1440 a:
MTOOLSRC=./mtoolsrc mmd a:/boot
MTOOLSRC=./mtoolsrc mmd a:/boot/grub
MTOOLSRC=./mtoolsrc mcopy fdsource/boot/grub/stage1 a:/boot/grub
MTOOLSRC=./mtoolsrc mcopy fdsource/boot/grub/stage2 a:/boot/grub
MTOOLSRC=./mtoolsrc mcopy fdsource/boot/grub/grub.conf a:/boot/grub/
MTOOLSRC=./mtoolsrc mcopy fdsource/x86-kernel a:/
MTOOLSRC=./mtoolsrc mcopy fdsource/kickstart a:/
MTOOLSRC=./mtoolsrc mcopy fdsource/sigma0 a:/
MTOOLSRC=./mtoolsrc mcopy fdsource/pingpong a:/
echo "(fd0) fdimage.img" > bmap
cat <<EOF | /sbin/grub --batch --device-map=bmap
Booting - Qemu
This section assumes that we will be booting and running using an emulator, and in particular Qemu. Qemu can be downloaded from here where there are both source and binary distributions.
Once Qemu is installed it is simply a question of running:
qemu -fda fdimage.img
Which will open a separate window representing the emulated PC's console output. A drawback with this window is that it does not have a buffer that allows viewing of text that has scrolled past, nor does it allow cutting and pasting of text from (and to) the console. Luckily it is also possible to run Qemu with no graphical console and with the emulated serial port connected to the terminal from which Qemu was started:
qemu -nographic -fda fdimage.img
The benefit of this approach is that the terminal output can be scrolled. Likewise the terminal allows cutting and pasting of text. Note that for this approach to work, the kernel debugger and the user-level code have to be configured to use the serial port.
Booting - Hardware
Starting the kernel running on actual hardware is pretty straightforward. The only hurdle is that the final floppy disk image has to be put on a real disk. This requires writing the disk image directly to a (floppy) disk, e.g.:
dd if=fdimage.img of=/dev/fd0 bs=512 count=2880
Then it is simply a question of booting from that disk.
Note that if you want input and output to come from/go to the console do not enable the serial options in the kernel or user-level code.