Tuesday, March 10, 2009

Building a Linux Kernel is very easy. Just a three step process. But successfully booting a the kernel with a newly created file system requires a little understanding of the boot process.
At the time of this writing, I have built a linux 2.6 kernel, created a filesystem, and able to boot with initrd mounted as root filesystem.

Getting this far was not easy for me, and I learnt a lot in the process.

This blog might be helpful for people trying to build a minimal linux system.
Also if I am wrong about something or not very clear, I hope to be told :).

I am starting from the point where I have the newly built kernel and my initrd image in the boot directory accessible to GRUB.

My initrd image has following structure
/
/etc
/bin
bash
init (a shell script which just echo's hello world)
/sbin (symbolic link to /bin)
/lib
/dev
console
ram0

Thats it! So I thought after the kernel boots, it starts the init process which then prints "Hello World" and hangs.

But I get the error as:

"Kernel Panic - not syncing: No init found. Try Passing init= option to kernel"

Well I am passing the option!
Apart from init=/sbin/init, I am also passing root=/dev/ram0.
So I am in a fix. Surprisingly Googling did not help.

Solution: The /lib directory
After a while(a day :) ), I suddenly realise that i forgot to populate the /lib directory and linux uses shared libraries. And how did I realise this?? Well some time ago I was playing with my linux system and fo some reason decided to rename the /lib directory, well nothing worked since all the shared libraries were unavailable!!
So now, I did "ldd", and copied the required libraries in /lib and bingo it works.

Tip: Use busybox. Its small and contains many commonly used utilities. Just create symbolic links to each utility.

So the first hurdle out of the way I decided to get serious. I copied the init program into /sbin, create the /etc/inittab as follows:

id:2:initdefault:
si::sysinit: /etc/rc.sysinit

1:2:respawn:/bin/bash -sh

The initialization /etc/rc.sysinit is as follows:
mount -n -t proc /proc /proc
mount -n -t sysfs /sys /sys

In the inittab file I tell init that the default runlevel is 2. The system initialization script is /etc/rc.sysinit. Then when thte system enters runlevel 2, I asked init to spawn the bash program.
Manual pages give good information on these files.

When bash starts I get the error "Job Control Turned Off".
After a lot of searching, I found out that since I did not use getty program to spawn terminals, bash is using /dev/console as the terminal. Seems in this case Job is turned off. So I created a symbolic link /dev/tty to /dev/console and bingo Job Control is enabled.

After building the kernel with appropriate device drivers I could enable networking. So now I wanted to run xinetd.

It would not run because of 2 problems:
1) For some reason my root file system became a read-only filesystem.
2) Even though I had the /etc/services and /etc/protocols files in place, xinetd was unable to read them

Solution to problem 1)
For some reason, the kernel mounts the root file system as readonly, so we need to remount it as readwrite filesystem as a part of system initialization. Adding the following line to /etc/rc.sysinit solved problem 1: mount -n -o remount, rw /dev/ram0 /

Solution to problem 2)
This was tricky. I wrote a small program to test if getprotoent function was working. It was not!! I was stumped. So I decided to have a look at the source code. I downloaded glibc-2.5 and seeing the code I knew I was missing something.
That something was the Name Service Switch. The new glibc treats files like passwd, services, protocols etc. as databases and has a specific procedure to access them which is specified in /etc/nsswitch.conf. There is a lot of information about this on the net.
So creating this file and copying a few libraries solved this problem

Now I am able to run xinetd properly.

Oh yeah I forgot to mention why I am going though all this trouble. I want to create a console server ( or a serial server). Now I have a simple working model.

Next Step: Almost all the utilities I have used were precompiled. I think its important to self compile each and every utility I use which will be very helpful for debugging.