© 2004 - Stéphan Kochen <stephan@kochen.nl>
Feel free to distribute, copy, modify this and the scripts mentioned and linked below as much as you like as long as you keep this copyright notice.
Files that do not contain this copyright notice should be modified to include this entire paragraph before redistribution.
Note: This page is slightly outdated. The structure of uClibc's buildroot has changed. Some things still apply, though I have no real interest in updating this document as I no longer use LVM2 myself.
I've been using an LVM2 contained reiserfs root partition for a while now. LVM2 volumes need to be activated before partitions can be mounted. Therefor, an initrd is required to activate these volumes before the root partition can be mounted.
Up until some time ago, I had been using a slightly customized initrd someone else gave me to activate the volume groups.
I'm not exactly sure anymore at what point or why I switched to my own initrd, but that doesn't matter to me anymore. :)
I'm going to write up here how I put that initrd together.
Besides all that, I bumped into a couple of other things when trying out gensplash. Because Spock nicely asked me to share this information, and I felt like I indeed had to dump it somewhere, I started writing this.
This is a step by step tutorial of how to create an initrd yourself. I will demonstrate tools provided by the uClibc and Busybox teams to build this initrd and get you up and running with an LVM2 root partition.
One of the things that went wrong with gensplash was the device-mapper control device LVM2 requires. Gensplash and the device-mapper both use a misc (major 10) character device with a dynamic minor. I've integrated the fix in this document, you'll notice in the /linuxrc script the /dev/mapper/control device is created while booting.
Gensplash also requires the helper and /dev/fbsplash device to be available. Though usually there's not much you, the user, or gensplash can do during the brief initrd stage. Regardless, if you want these to be available aswell, you can do something similar. Quoth Spock, the man himself:
<spock> yup, it's in /sys/class/misc/fbsplash
<spock> splash_helper uses it to create the splash device when necessary
So you really just need /sys mounted and the helper available.
In short: if you use gensplash and have problems activating the LVM2 volumes, giving you warnings such as:
device-mapper ioctl 0 failed: Invalid argument.
take a look at how the required /dev/mapper/control device is created below in my /linuxrc. It is probably going to fix your problem.
Hopefully, this will be usefull to someone, as Spock claims. But I suppose I should listen to more experienced developers than me. :)
Buildroot is an excellent set of makefiles written by the folks at uClibc to compile an uClibc based toolchain and root filesystem. And that's even easier than it sounds! This FAQ entry on the uClibc site explains briefly how it's done, but I'm going to go through the commands step by step, just for you!
First, we have to check out buildroot from CVS:
# cvs -d:pserver:anonymous@uclibc.org:/var/cvs login # cvs -z3 -d:pserver:anonymous@uclibc.org:/var/cvs co buildroot
Now, by default, buildroot does not support building the tools we need to mount LVM2 contained partitions.
We're going to do something about that ofcourse, more specifically, I've done something about it. ;)
To be able to build these tools very easily, simply download the following three makefiles and put them in the buildroot/make/ directory:
device-mapper.mk lvm2.mk
Optionally, you can also get the reiserfsprogs makefile I created if you could use it in any way:
reiserfsprogs.mk
Now we're going to configure buildroot; tell it what to compile and how. To do this, open buildroot/Makefile in your favorite text editor. The first couple of lines should be pretty straight forward and well commented. If something doesn't make sense to you, there's a fair chance the default will work for you. You should still take a look at it though. What we're really interested, though, is what follows below these lines:
############################################################# # # The list of stuff to build for the target filesystem # #############################################################
Below this, you'll notice the TARGET variable, which contains a list of packages that will be build. You probably want to leave the first two sections for host-sed and the uclibc toolchain alone.
The next section tells buildroot what kernel sources to use. By default, the target 'kernel-headers' uses a set of known to work kernel headers downloaded from an external source. You can use the 'linux' target if you want to build a new kernel within buildroot or the 'system-linux' target if you want to use the headers from an already compiled kernel. In the last case, it'll look in /usr/src/linux. So make sure that either points at your linux sources already or edit the buildroot/make/system-linux.mk file to change the path.
Following that is the busybox target, which you most likely want to leave in.
For those not familiar with busybox, it is a single application containing some minimal functionality of many common commands, such as cd, ls, mkdir, etc.
This application will be installed in the initrd's /bin and alot of symlinks created to it.
You can configure exactly what functionality busybox should be build for at a later point, just leave this in for now.
The tinylogin target should be commented out unless you want to use it.
After that we'll add our LVM2 target we just installed in the buildroot/make directory. Simply add the following line after the busybox line:
TARGETS+=lvm2
If you would also like to use reiserfsprogs, you would ofcourse add this instead:
TARGETS+=lvm2 reiserfsprogs
Don't worry about the device-mapper tools, buildroot is aware of dependencies so it'll be build along.
The build targets following those lines are unnecessary unless you really need the functionality for some reason.
They're mostly development related, and I personally do not see why anyone would want to compile anything from within the initrd. :)
But feel free to comment out anything you need if you want.
Next up is the section starting with the following:
############################################################# # # Pick your root filesystem type. # #############################################################
In this section, you want only one of the TARGET+=... lines uncommented. As it says, you have to choose your filesystem here. I personally had some problems with cramfs not booting before. I've never tried the jffs2 target, ext2 is probably the easiest way to go.
That's it!
Now all you have to do is type 'make' in the buildroot directory and it'll start downloading and compiling the toolchain and initrd packages.
After that, it'll create the filesystem of your choice in a file called buildroot/root_fs_i386 or something along those lines depending on your architecture.
The default busybox configuration most likely builds some functionality you don't need at all.
One way to edit the configuration is by editing the file buildroot/sources/busybox.config.
This is, however, not documented at all. An easier way is to configure busybox after the compile.
After the make step above, cd to buildroot/build_i386/busybox (may differ depending on your platform) and run 'make config' or 'make menuconfig' to edit the configuration.
Make sure you compile in enough options to make the bootscript work!
Take a look at the example bootscript below for this.
Still in the busybox directory, run 'make clean' to cleanup the previously built files and copy the .config file to buildroot/sources/busybox.config.
Then cd back to the main buildroot directory.
Simply run 'make' again to regenerate the initrd, make will skip already built packages, but rebuild busybox and regenerate the initrd as well.
All that's really left now is setting up the scripts to magically make your LVM2 contained root partition mount and Just Work(tm)!
First, we can't just edit the initrd as a file. Instead we mount it on a loopback device, you can do this using the following command:
mount -t ext2 -o loop buildroot/root_fs_i386 /mnt/initrd
Depending on the filesystem choice, you will ofcourse have to change the ext2 part. And again, root_fs_i386 may differ depending on architecture. Finally, /mnt/initrd is an example mountpoint. You can choose another, ofcourse, just make sure the directory exists.
Now we can clean out some directories we don't need. Because if you just want an automated initrd that gets your root partition to work, there's a fair bunch of irrelevant directories in by default. You can delete the following:
By default /dev contains a whole bunch of unnecessary devices. I'm not sure which devices are absolutely necessary, but it's better to stay on the safe side. They don't use up a terrible amount of space anyways. The devices you probably want to keep around are:
The mapper device will be created dynamically later on in the bootscript, because it has a dynamic minor number. Make sure the directory /dev/mapper for it exists, though.
LVM2 needs some directories in /etc to function correctly, simply create the following:
By default, Linux will look for the /linuxrc script on an initrd and execute it. This is where the dirt should happen. As an example, here is my commented /linuxrc script, it's short so I'll paste it inline here:
#!/bin/sh # Mount /proc, necessary for other mounts mount -t proc none /proc # Mount /sys, necessary to create the device mapper control device mount -t sysfs none /sys # Create the mapper device sed -e 's/:/\n/' /sys/class/misc/device-mapper/dev | xargs mknod /dev/mapper/control c # Umount /sys umount /sys # Scan and activate volume groups vgscan vgchange -a y # Mount the root partition mount -t reiserfs -o ro /dev/vg/root /mnt/newroot # cd to it, unmount /proc cd /mnt/newroot umount /proc # Change to the new root partition and execute /sbin/init pivot_root . mnt/oldroot exec chroot . /sbin/init < dev/console > dev/console 2>&1
Note that this is just an example, you probably have to modify the actual logical volume (/dev/vg/root in my case). You can also add other things to script if necessary. Also note that for this script to work, the mountpoints and /sys need to be available. So make sure /mnt/newroot, /sys on the initrd and /mnt/oldroot on the actual root partition exist.
We're done! Now just to install it. First, gzip it up:
# gzip -9 buildroot/root_fs_i386
Again, take note that the filename may differ. Now, move the initrd to your /boot directory:
# mv buildroot/root_fs_i386.gz /boot/initrd.gz
The final step is to edit the grub.conf file or lilo.conf file: Add the initrd line and append the following to the kernel commandline:
root=/dev/ram0 rw init=/linuxrc
Run lilo if necessary and you're all set! Simply reboot, and if something went wrong, you're bound to notice! :p