Thursday, April 12, 2007

Unleash the Ramdisk Within [Ubuntu/Debian]

If you check out your grub configuration file (menu.1st) you'll see something similar to this:

title Ubuntu, kernel 2.6.15-28-686
root (hd0,1)
kernel /boot/vmlinuz-2.6.15-28-686 root=/dev/sda2 ro quiet splash
initrd /boot/initrd.img-2.6.15-28-686
Now initrd's (initial ramdisks) were the bread and butter technology I used to build Trinux. Although it says initrd it is actually an initrams, a kernel feature introduced in late 2.5 . meaning it is available in the 2.6 kernel you are probably running.

What is this file and how is it created?

root@gx620:/boot# file initrd.img-2.6.15-28-686
initrd.img-2.6.15-28-686: gzip compressed data, from Unix, max compression

So it can be gunzipped and un-cpio'd (-vid are the options, do an "info cpio") and you can get whats inside. This file is created by /usr/sbin/mkinitramfs and you can create you own. Let's say you wanted to create a new one with the latest kernel (, right now) to check out some new features you would do "mkinitramfs -o my-initramfs" based on:

Usage: /usr/sbin/mkinitramfs [OPTION]... <-o outfile> [version]

-d confdir Specify an alternative configuration directory.
-k Keep temporary directory used to make the image.
-o outfile Write to outfile.
-r root Override ROOT setting in mkinitrd.conf.

One of the really cool things about this script is the copy_exec function which adds files to the new ramdisk image, automatically populating /lib and /usr/lib (or whatever) which the required dynamic libraries.

copy_exec /sbin/modprobe /sbin
copy_exec /sbin/depmod /sbin
copy_exec /sbin/rmmod /sbin
mkdir -p ${DESTDIR}/etc/modprobe.d
cp -a /etc/modprobe.d/* ${DESTDIR}/etc/modprobe.d

/sbin/rmmod /sbinmkdir -p ${DESTDIR}/etc/modprobe.dcp -a /etc/modprobe.d/* ${DESTDIR}/etc/modprobe.d

As you can see, the module utilities aren't terribly interesting, but even a simple tool like wget depends on a quite a few libraries:

root@gx620:/usr/share/initramfs-tools# ldd /sbin/insmod => (0xffffe000) => /lib/tls/i686/cmov/ (0xb7dac000)
/lib/ (0xb7eed000)
root@gx620:/usr/share/initramfs-tools# ldd /usr/bin/wget => (0xffffe000) => /lib/tls/i686/cmov/ (0xb7fce000) => /lib/tls/i686/cmov/ (0xb7fc6000) => /usr/lib/i686/cmov/ (0xb7f88000) => /usr/lib/i686/cmov/ (0xb7e59000) => /lib/tls/i686/cmov/ (0xb7d2a000)
/lib/ (0xb7fe3000) => /lib/tls/i686/cmov/ (0xb7d18000) => /usr/lib/ (0xb7d04000)

This took me ages to do with Trinux.

But how does the new system boot? Instead of a good old linuxrc (used by initrd) initramfs uses an init shell script which you can see in /usr/share/initramfs-tools/init. If you look near the end, you can see where the "real" init is called

root@gx620:/usr/share/initramfs-tools# tail -f init
while [ ! -x ${rootmnt}${init} ]; do
panic "Target filesystem doesn't have ${init}"
maybe_break init

# Unset the problematic debug variable and chain to real filesystem
unset debug
exec run-init ${rootmnt} ${init} "$@" <${rootmnt}/dev/console >${rootmnt}/dev/console

If you comment out these last few lines and add a /bin/sh you could (and then rebuild with mkinitramfs.

I'll be documenting more of this on my wiki and as part of ubuntutrinux, so stay tuned!

No comments: