User:Phoenix591/Handmade initrd
Inspired by Old Fashioned Gentoo (examine the original init and file list in conjunction with this article), I've found manually creating an initrd boots faster and is smaller than a traditional genkernel or dracut generated initrd, my resulting initrd weighs in at only 2.3Mb.
Kernel Configuration
Ensure any kernel modules needed to mount your root file system are built in or included
-> Device Drivers
-> Serial ATA and Parallel ATA drivers (libata) (ATA [=y])
<*> AHCI SATA support
-> Device Drivers
-> NVME Support
<*> NVM Express block device
Device Drivers --->
Multiple devices driver support (RAID and LVM) --->
<*> Device mapper support
<*> Crypt target support
<*> Snapshot target
<*> Mirror target
<*> Multipath target
<*> I/O Path Selector based on the number of in-flight I/Os
<*> I/O Path Selector based on the service time
Not all device mapper options need to be enabled; some of the options are only needed for LVM2 Snapshots and LVM2 Thin Snapshots, LVM2 Mirrors, LVM2 RAID 0/Stripeset and encryption.
-> File systems
<*> The Extended 4 (ext4) filesysteme
Packages
Use Flags
# Enable support for the LVM daemon and related tools if using lvm
#ensure static is enabled for both lvm2 and busybox
# Add thin if you are using thin lvm
sys-fs/lvm2 lvm static
sys-apps/busybox static
Install needed packages
e2fsck-static is from Phoenix591's overlay
root #
emerge eselect-repository
root #
eselect repository enable phoenix591
root #
emerge -av e2fsck-static busybox sys-fs/lvm2
Generating the initrd
Init
#!/bin/busybox sh
rescue_shell() {
echo "$@"
sleep 10
echo "Something went wrong. Dropping you to a shell."
# /bin/busybox --install -s
exec /bin/sh
}
check_filesystem() {
# most of code coming from /etc/init.d/fsck
local fsck_opts= check_extra= RC_UNAME=$(uname -s)
# FIXME : get_bootparam forcefsck
if [ -e /forcefsck ]; then
fsck_opts="$fsck_opts -f"
check_extra="(check forced)"
fi
echo "Checking local filesystem $check_extra : $1"
# if [ "$RC_UNAME" = Linux ]; then
# fsck_opts="$fsck_opts -C0"
# fi
trap : INT QUIT
# using our own fsck, not the builtin one from busybox
/sbin/fsck.ext4 -p $fsck_opts $1
ret_val=$?
case $ret_val in
0) return 0;;
1) echo "Filesystem repaired"; return 0;;
2|3) if [ "$RC_UNAME" = Linux ]; then
echo "Filesystem repaired, but reboot needed"
reboot -f
else
rescue_shell "Filesystem still have errors; manual fsck required"
fi;;
4) if [ "$RC_UNAME" = Linux ]; then
rescue_shell "Fileystem errors left uncorrected, aborting"
else
echo "Filesystem repaired, but reboot needed"
reboot
fi;;
8) echo "Operational error"; return 0;;
16) echo "Use or Syntax Error"; return 16;;
32) echo "fsck interrupted";;
127) echo "Shared Library Error"; sleep 20; return 0;;
*) echo $ret_val; echo "Some random fsck error - continuing anyway"; sleep 20; return 0;;
esac
# rescue_shell can't find tty so its broken
# rescue_shell
}
# start for real here
# temporarily mount proc and sys
mount -t proc none /proc
mount -t sysfs none /sys
/sbin/lvm lvchange -ay ssd/ssd-root2 || rescue_shell " Vgchange error"
dmsetup mknodes
check_filesystem /dev/mapper/ssd-ssd--root2
mount /dev/mapper/ssd-ssd--root2 /mnt/root || rescue_shell
cp -a /dev/mapper /mnt/root/dev/
# clean up. The init process will remount proc sys and dev later
umount /proc
umount /sys
# switch to the real root and execute init
exec switch_root /mnt/root /sbin/init
Modify this script to activate your own root lvm volume and switch out the fsck executable if needed
File List
# directory structure
dir /proc 755 0 0
dir /usr 755 0 0
dir /bin 755 0 0
dir /sys 755 0 0
dir /var 755 0 0
dir /run 755 0 0
#dir /lib 755 0 0
#dir /lib64 755 0 0
dir /sbin 755 0 0
dir /mnt 755 0 0
dir /mnt/root 755 0 0
dir /etc 755 0 0
dir /root 700 0 0
dir /dev 755 0 0
dir /dev/mapper 755 0 0
#dir /usr/lib64 755 0 0
# we have a static /dev so we need all dev entries too
# e.g. /dev/console below
nod /dev/console 0600 0 0 c 5 1
nod /dev/null 0666 0 0 c 1 5
nod /dev/urandom 0666 0 0 c 1 9
# dev/sda and partitions
nod /dev/sda 0660 0 0 b 8 0
nod /dev/sda1 0660 0 0 b 8 1
nod /dev/sda2 0660 0 0 b 8 2
nod /dev/sda4 0660 0 0 b 8 4
# dev/sdb and partitions
nod /dev/sdb 0660 0 0 b 8 16
nod /dev/sdb1 0660 0 0 b 8 17
nod /dev/sdb2 0660 0 0 b 8 18
nod /dev/sdb3 0660 0 0 b 8 19
nod /dev/sdb4 0660 0 0 b 8 20
nod /dev/sdb5 0660 0 0 b 8 21
nod /dev/sdc 0660 0 0 b 8 32
nod /dev/sdc1 0660 0 0 b 8 33
nod /dev/sdc2 0660 0 0 b 8 34
nod /dev/sdc3 0660 0 0 b 8 35
nod /dev/nvme0n1 0660 0 0 b 259 0
nod /dev/nvme0n1p1 0660 0 0 b 259 1
nod /dev/nvme0n1p2 0660 0 0 b 259 2
nod /dev/nvme0n1p3 0660 0 0 b 259 3
# all the lvm nodes I need
nod /dev/dm-0 0660 0 0 b 253 0
nod /dev/dm-1 0660 0 0 b 253 1
nod /dev/dm-2 0660 0 0 b 253 2
nod /dev/dm-3 0660 0 0 b 253 3
slink /dev/stderr /proc/self/fd/2 777 0 0
slink /dev/stdin /proc/self/fd/0 777 0 0
slink /dev/std/out /proc/self/fd/1 777 0 0
# busybox
file bin/busybox /bin/busybox 755 0 0
# our needed binaries
file /sbin/lvm /sbin/lvm.static 755 0 0
slink /sbin/vgchange /sbin/lvm 777 0 0
file /sbin/fsck.ext4 /sbin/e2fsck.static 755 0 0
file /sbin/dmsetup /sbin/dmsetup.static 755 0 0
# our init script
file /init /root/initrd/init 755 0 0
# lddtree -l $(which fsck) $(which fsck.ext4) $(which lvm) $(which dmsetup) | grep .so | sort | uniq | xargs -i echo file {} {} 755 0
The notation here is type initrd-path host-path permissions owner owner (Block/Character major minor) One way to find the values needed for your device nodes is the file command
user $
file /dev/sda
This translates to "nod /dev/sda 0 0 b 8 0" with b 8 0 being the relevant part
usr/gen_init_cpio
Now we use a binary provided in the kernel source (this is NOT currently shipped with distribution kernels!) to assemble the initrd and then compress it
root #
/usr/src/linux/usr/gen_init_cpio /root/initrd/list | gzip -9c >/tmp/real-initrd.gz
Optionally, your processor microcode can be added just as dracut does. Intel processors install sys-firmware/intel-microcode. AMD processors substitute lib/firmware/amd-ucode/microcode_amd*.bin instead of /lib/firmware/intel-ucode/* in the below commands
root #
mkdir -p /tmp/initrd/kernel/x86/microcode
root #
cat /lib/firmware/intel-ucode/* >/tmp/initrd/kernel/x86/microcode/GenuineIntel.bin
root #
cd /tmp/initrd
root #
find . | cpio -o -H newc >/tmp/ucode.cpio
root #
cat /tmp/ucode.cpio /tmp/real-initrd.gz >/boot/initrd
root #
cd -
root #
rm -Rf /tmp/real-initrd.gz /tmp/ucode.cpio /tmp/initrd
The resulting initrd is at /tmp/real-initrd.gz without the microcode step, /boot/initrd after it.