Sunday 14 June 2009

initramfs and how not to claw your eyes out whilst living with it.

I've been trying to persuade muddle to get Linux booting - and it now can, at least up to saying 'hello world' from a C program that masquerades as init.
This turns out to be a little bit tricky. The key bit of documentation is in the kernel source - Documentation/filesystems/ramfs-rootfs-initramfs.txt. Important things to know include:

  • 2.6 kernels understand compressed (gzipped) cpio archives as a ramdisk format. This means you can build them as an ordinary user, using cpio -Hnewc, without needing to be root.
    This being UNIX there are, of course, many different kinds of cpio archive. The -H newc above forces hex SVR4 interoperable cpio format, which turns out to be vaguely documented by the LSB.
    This means that you can build ramdiscs without needing to be root anymore - the muddle cpio deployment will do this for you in the right format (and incidentally there is some stealable python in the cpiofile.py file that will build you SVR4 cpio archives - there doesn't seem to be a standard python module to do this).

  • On boot, the kernel looks for a suitable initrd in three places:

    • In the ramdisk built with the kernel.
    • In any initrd it finds in memory, provided by the bootloader.
    • In the root fs.

    Since a ramdisk is always built into the kernel (to avoid testing for the case where there isn't one), it can't do this by mounting the first disk it comes across and assuming it's the one to use. So it mounts each in turn and tries to find /init to execute. If there isn't one, it tries the next method.
    We actually want to use a separate ramdisk in this case to avoid having to faff with asking the kernel to incorporate our ramdisk in its image, which makes the build system marginally nastier than it needs to be.

  • (aside) you will note that the bootloader is obliged to parse the kernel command line for an initrd parameter so that it can make the ramdisk available - I'm currently using syslinux which does this for you; uboot and other, more primitive, bootloaders will probably require you to package the initrd with the kernel - I'll cover this in a later article when I actually have to do it :-). This is how the initrd data gets made available despite the kernel not having yet loaded the module which gives it access to the filesystem on which a separate initrd might reside.

  • Hence, if your cpio archive doesn't contain a /init the kernel can execute, the kernel will simply skip on and issue a 'can't mount root' error message which implies that it thought there was something bad about your cpio archive. This may be a lie - perhaps your cpio archive was fine and just didn't contain the right file. Attempting to set root=/dev/ram0 won't work either - cramfs can't understand compressed cpio archives.

  • To build a working initrd, all you need is the libgcc_1 and libc6 packages from ubuntu (muddle now has a deb package builder which will rip the useful data out of these for you, but dpkg-deb -X will do just as well), and a /init . cpio -Hnewc -o these together into an initrd file.

  • You can test this fairly easily with qemu -kernel my_kernel -initrd my_initrd -append noapic (qemu doesn't love 2.6.30's APIC support and I expect this is true of other kernel versions too).


And that's all, folks. I'll package this up into a muddle example at some point - leave a comment or poke me by email if you're interested and it's not on trunk.

No comments:

Post a Comment