Monday, 15 March 2010

Building Initramfs in the kernel

If you read my last post regarding Booting with /etc in a separate partition, you may have noticed that the initrd is specified as a separate file. Although this is good, there is a way to have it built into the kernel.

Follow my previous post for the method to setup your initramfs build directory. For me it will be /media/work/initramfs. Make a note of this path(or the one that you have setup). We will need this later while configuring the kernel.

Make sure you have the kernel Source installed. I won't be guiding you about the distribution specifics on how to install a kernel source. You can download from or apt-get it or installpkg. Its your choice. usually you will find the source in /usr/src/linux

We need to configure the kernel. I prefer to use the configuration for the Slackware huge-smp kernel, only because it has most of the needed modules built-in, you can opt to configure everything manually if you want.

If you are not running Slackware you can obtain the config from

$ wget -O /usr/src/linux/.config
Now lets start configuring the kernel source so that we can let it know where we have our initramfs source files

$ make menuconfig
This will load the ncurses based kernel configurator. This is how it will look like.

Now select the "General setup --->" option from this page. This opens a new page.

Scroll down to the section "[*] Initial RAM filesystem and RAM disk (initramfs/initrd) support"

Make sure that the brackets before this has [*]. You will have it by default if you downloaded the config file from the link above.

The line exactly below this is the one we need to worry about.

() Initramfs source file(s)

Move your highlight bar on this and press Enter. This brings up an input prompt where you type the path of the initramfs source files. In my case it will be "/media/work/initramfs". Make sure you are typing it without the quotes. Now press Enter.

This should now change to

(/media/work/initramfs) Initramfs source file(s)

Optionally you can enable initramfs compression mode. To do this scroll down two lines and select

Built-in initramfs compression mode (None) --->
In the new page that comes up, select the compression mode that you like.

Now Press ESC key twice so that you are taken to the main page. Press them twice Again. You will prompted to save the changed configuration. Select Yes.

You will be taken back to the shell.

Now is the time to build your kernel.

at the command prompt type
$ make

The kernel takes a long time to build, so it would be good if you go have your meal or watch a movie. ;)

After this has finished type the following command to install all the modules.

$ make modules_install

The compiled kernel will be in /usr/src/linux/arch/c86/boot/bzImage. Let us copy it to /boot

$ cp /usr/src/linux/arch/c86/boot/bzImage /boot/vmlinuz-with-initramfs

We now need to create a bootloader entry.

As in the earlier post, I'll use Lilo. You can use your boot loader of choice.

My new /etc/lilo.conf entries for this new kernel will be

Image = /boot/vmlinuz-with-initramfs
root = /dev/sda1
label = TestInit
append = "home=/dev/sda6 etc=/dev/sda6 root=/dev/sda1"
You will note that I am not using an "initrd=" line over here. That is because I do not need to. The initrd is now built into the kernel. so we only need one file, the kernel to do our task.

Now simply run lilo to install the changes and reboot the machine.

During the boot prompt select TestInit and you will boot just like you did earlier with the separate /etc

Thursday, 11 March 2010

Booting with /etc in a separate Partition

A friend of mine(Tejas Surve) had this question. "Is it possible to boot a linux system with having /etc in a separate partition?" He buzzed about it. A friend of his replied that it can't be done and he even blogged the reasons for it. I chose to disagree and found a solution, after all the beauty of opensource is that it can be bent and moulded to do what you want, the way you want.

So how is it done? This requires the use of initramfs or initrd. Most of the Ubuntu users may be using initrd by default. You can verify this by checking your menu.lst and looking for the line "initrd="

Initramfs or initrd are very commonly used with small kernels which do not have al the modules preloaded so that they can be loaded before additional tasks can be performed. This sometimes also includes loading the filesystems modules for mounting partitions. At this point the root (/) of the system is residing in memory and hence they do not need the hard drive's / or /etc for anything. The kernel has booted and hence it passes control to the init script on the initramfs which it loaded during booting and this script does the additional admin work that we need to do before passing control over to the real init which lies in /sbin/init on our Hard Drive, which in turn would require the /etc/inittab to function. This is how many systems without HardDrive boot off a network drive.

With the theory behind this done, let me explain how I did it. As most of you know, I use Slackware-current and my choice of kernel has always been the huge-smp flavour. For those who do not know, the huge-smp kernel is a kernel compiled with all or most of the modules as built-in and not as a module. This causes the size of the kernel to be huge(hence the name). Hence I usually do not need an initrd/initramfs. Just in case you do not have a huge-smp kernel, you can get it from This is a slackware archive but you can extract with tar xvf filename command. Copy the vmlinuz.* file to your boot and use it accordingly.

Although I could use the mkinitrd to create my intramfs image, I chose not to and used a guide from with some modifications.

Creating the folder structure

As discussed in the blog link above I created the folder structure, but I chose to create a /dev folder as well.

$ mkdir -p work/initramfs/{bin,sbin,dev,etc,proc,sys,newroot}
$ cd work
$ touch initramfs/etc/mdev.conf

Installing Busybox

We need a shell to do anything, hence we can install busybox for this.

$ wget
$ bunzip2 busybox-1.10.1-static.bz2
$ mv busybox-1.10.1-static initramfs/bin/busybox
$ chmod +x initramfs/bin/busybox
$ ln -s busybox initramfs/bin/sh

The /init script

The real magic is played by the init script. It will first mount the root and etc partition and then switch to the real root and pass on control to the real init script located at /sbin/init on our Hard Drive

Let us setup the script by running the following commands

$ touch initramfs/init
$ chmod +x initramfs/init

The init script which I created is different from the init script given in the blog above, So I will paste my copy here with some explanations:

busybox --install -s
#Mount things needed by this script
mount -t proc none /proc
mount -n devtmpfs -t devtmpfs /dev && mkdir /dev/pts
mount -t devpts devpts /dev/pts
mount -t sysfs sysfs /sys

echo /sbin/mdev > /proc/sys/kernel/hotplug

#Function for parsing command line options with "=" in them
# get_opt("init=/sbin/init") will return "/sbin/init"
get_opt() {
echo "$@" | cut -d "=" -f 2


#Process command line options
for i in $(cat /proc/cmdline); do
case $i in
root=$(get_opt $i)
init=$(get_opt $i)
etc=$(get_opt $i)
home=$(get_opt $i)

#Mount the root device
if [ -n "${root}" ]; then
mount "${root}" /newroot -o ro
echo mounted real_root
if [ -n "${etc}" ]; then
mount "${etc}" /newroot/etc
echo mounted etc
if [ -n "${home}" ]; then
mount "${home}" /newroot/home
echo mounted home

#Check if $init exists and is executable
if [[ -x "/newroot/${init}" ]] ; then
#Unmount all other mounts so that the ram used by
#the initramfs can be cleared after switch_root
umount /sys /proc

#Switch to the new root and execute init
exec switch_root /newroot "${init}"

#This will only be run if the exec above failed
echo "Failed to switch_root, dropping to a shell"
exec sh

If you compare this script with the original script from the blog you will notice many differences.
I haven't used mdev at all. Instead I mount /dev as devtmpfs.
You will notice some additional cases in the for loop for parsing command lines
You will notice the section where I'm mounting the additional partitions, if they have been defined.

Also note that I have set the default root as /dev/hda1 although my root is on /dev/sda1. This is intended to demonstrate the variable being replaced when it is passed at the boot command.

Creating the initramfs.igz file
Since I was editting the init file a lot and rebuilding the initramfs frequently, I decided to make a script instead. I called it

The script is as follows:

cd initramfs
find . | cpio -H newc -o > ../initramfs.cpio
cd ..
cat initramfs.cpio | gzip > initramfs.igz

Once you run this script from the work folder it will create a file initramfs.igz. Move/copy it to the /boot folder

$ mv initramfs.igz /boot/

You could have added this line to the script, but I chose not to.

Now add a new bootloader entry for this. I use LILO, so my entry in lilo.conf was as follows:

image = /boot/vmlinuz
initrd = /boot/initramfs.igz
root = /dev/sda1
label = Testopia
append = "home=/dev/sda6 etc=/dev/sda6 root=/dev/sda1"

If anyone uses grub and is following my post paste the grub entry and I'll add it here.

With this done I simple rerun the lilo command to install the changes.

Preparing the new /etc

To demo this, I need /etc on a different partition. So I moved my /etc contents to /home as it is a separate partition on my system. I used the following command to do this in order to preserve the permissions

$ cd /etc && tar -cf – . | (cd /home && tar -xpvf -)

After this I moved my /etc to /etc.old to remove the real /etc completely and made a blank directory called /etc

$ mv /etc /etc.old
$ mkdir /etc

With this done I rebooted the PC and selected Testopia as my boot choice from the Lilo prompt. The system booted completely with the /etc in a different partition.

Note: I did not have to touch my fstab for this and the newly mounted /etc wont be seen by the mount output.