User:Phoenix591/Handmade initrd

From Gentoo Wiki
Jump to:navigation Jump to:search

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

KERNEL Most modern hard drives use AHCI(CONFIG_SATA_AHCI)
-> Device Drivers
  -> Serial ATA and Parallel ATA drivers (libata) (ATA [=y])
     <*>   AHCI SATA support
KERNEL NVME drives require NVME block device support(CONFIG_BLK_DEV_NVME)
-> Device Drivers
  -> NVME Support
      <*> NVM Express block device
KERNEL linux-4.9 Enabling LVM
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
Note
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.
KERNEL The actual filesystem driver you used for root (ext4 in this example)(CONFIG_EXT4_FS)
-> File systems
   <*> The Extended 4 (ext4) filesysteme

Packages

Use Flags

FILE /etc/portage/package.use
# 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

FILE /root/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

FILE /root/initrd/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
/dev/sda: block special (8/0)

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
root # 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 .
root # 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.

See Also

Custom_Initramfs