The initramfs concept is a bug. It is the kludge that allows the kernel to punt to userspace on all kinds of problems. Basically it is a small filesystem that the kernel loads before it loads the real file system. Something sounds fishy there doesn’t it? This is helpful for things like encrypted file systems, drives with weird (proprietary driver?) hardware or NFS mounts, and, my problem, RAID boot drives. Read more about what it is at the pretty good Wikipedia page.
Note
|
I also just found out that LVM partitions which contain a Linux root file system need an initramfs too. This is not just to boot and find the kernel since GRUB2 can penetrate LVM. But the kernel can not mount a file system on LVM without userspace tools. |
I like to have my kernel do what is needed because it is set up exactly the way it should be. And in 2005, this worked for something like mounting a simple Linux system that just happened to be protected by a kernel managed software RAID1 double drive setup. But as time wore on, the metadata for the RAID volumes changed and although the new bootloader understood it, the kernel, weirdly, did not. No one seemed to care that these formerly robust systems were now broken. This was because *every*body used an initramfs so that the kernel wouldn’t have to do its job. I fought this and tried to do it the Right Way, but eventually I just ran into too many brick walls. So a stupid initramfs it is.
Start with this guy’s great notes.
Homemade initramfs
Because I like to understand what’s going on, and minimize cruft (maintenance and security headaches), I wanted to create my own initramfs and not rely on any automagical tools that did who-knows-what. Here is the system that worked for me.
Build Automation
Start in a directory which contains a subdirectory called
initramfs-src
which contains the file system you want to be in the
initramfs. Then run this and you will get
-
initramfs.cpio
- suitable for including in the kernel itself -
initramfs.igz
- the gzipped version suitable for loading by the bootloader.
#/bin/sh
cd /boot/initramfs-src/
find . | cpio -H newc -o > ../initramfs.cpio
cd ..
cat initramfs.cpio | gzip > initramfs.igz
Minimal Directory Structure
The initram filesystem must contain the following directories:
-
bin
-
busybox
(static) -
sh
i.e.ln busybox sh
-
-
etc
-
mdev.conf
(can be empty)
-
-
newroot
-
proc
-
sbin
-
mdadm
(static) The whole point of my initramfs - see below
-
-
sys
/init
Also in the top level there needs to be a file called init
. This is
what control is handed over to by the kernel right after the file
system is loaded into ram. Here is a pretty competent version of that
that I got from
jootamam’s notes.
#!/bin/sh
#Mount things needed by this script
mount -t proc proc /proc
mount -t sysfs sysfs /sys
#Disable kernel messages from popping onto the screen
# echo 0 > /proc/sys/kernel/printk
#Clear the screen
# clear
#Create all the symlinks to /bin/busybox
busybox --install -s
#Create device nodes
mknod /dev/null c 1 3
mknod /dev/tty c 5 0
mdev -s
#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
}
# XED-
/sbin/mdadm --verbose --assemble /dev/md0 /dev/sda /dev/sdb
#Defaults
init="/sbin/init"
#root="/dev/hda1"
root="/dev/md0"
#Process command line options
for i in $(cat /proc/cmdline); do
case $i in
root\=*)
root=$(get_opt $i)
;;
init\=*)
init=$(get_opt $i)
;;
esac
done
#Mount the root device
mount "${root}" /newroot
#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}"
fi
#This will only be run if the exec above failed
echo "Failed to switch_root, dropping to a shell"
exec sh
Statically Compiled Tools
Note the /sbin/mdadm
line that assembles that RAID1 array with
modern >v1.0 volume metadata, the whole point of this annoying
exercise.
Of course you’ll need a /sbin/mdadm
and either 1. all of its library
dependencies checkable (recursively) with ldd /sbin/mdadm
or 2. a
statically compiled version of that executable (i.e. no dependencies).
I went with the latter in Gentoo which was quite easy with the
following command.
USE="static" ebuild /usr/portage/sys-fs/mdadm/mdadm-3.2.6.r1.ebuild compile
GRUB2
When booting this with a linux
kernel use initrd
but when booting
it as a paravirtualized Xen kernel, use module
for both the kernel
file and the initramfs
.
Editing Existing initrd
Got an initrd with something stupid going on? For example, maybe the
wrong names are used in the init
file. Here’s how to get into the
thing and make changes.
mkdir /tmp/initrd
cd /tmp/initrd
gzip -cd /boot/initrd-xxxxxxxxx.img | cpio -imd --quiet
Edit anything you need to edit and then put it back like this.
cd /tmp/initrd
find . | cpio -co | gzip -9 > /boot/initrd-xxxxxxxxx.img