Before we can continue with configuring the kernel when installing a new
gentoo system, we need to create an initramfs.

Since the root partition is encrypted, it has to be decrypted during the
boot process, which is not done by the linux kernel, so it has to be
done in userspace - early userspace. Early userspace is a set of
libraries and programs that provide various pieces of functionality that
are important enough to be available while a linux kernel is booting up,
but it doesn't need to be run inside the kernel itself.

An initramfs image is a gzipped cpio format archive, which in our case
is responsible for decrypting the root partition.

Let's describe the ramfs, rootfs, and initramfs a little.
A whole article about that can be read here [1,2].

ramfs: Normally all files are cached in memory. Pages of data read from hard drive are kept around in case they are needed again, but marked as clean in case the Virtual Memory system needs the memory for something else. Similarly, data written to files is marked as clean as soon as it has been written to a hard drive, but kept around for caching purposes, until the VM reallocates the memory. If there is no hard drive, files written into ramfs allocate dentries and page cache as usual, but there is nowhere to write them to. This means the pages are never marked clean, so they can't be freed by the VM when it's looking to recycle memory.

tmpfs: One downside of ramfs is we can keep writing data into it until we fill up all memory, and the VM can't free it because the VM thinks that files should get written to hard drive. However, ramfs don't have a backing store. Because of this, only root users should be allowed to write to a ramfs mount. A tmpfs has the ability to also write data to swap space, so normal users can be allowed to write to tmpfs mount.

rootfs: Rootfs is a special instance of ramfs, which is always present in 2.6 systems.

initramfs: Initramfs is a root filesystem which is embedded into the kernel and loaded at an early stage of the boot process. It is the successor of initrd. It provides early userspace which lets us do things that the kernel can't do by itself during the boot process. Using the initramfs is of course optional. By default, the kernel initializes hardware using built-in drivers, mounts the specified root partition, and loads the init system of the installed linux distribution. The init system then loads additional modules and starts services until finally allows us to log in. With initramfs we can do things even before the root partition is mounted. With initramfs we can do the following things:

We can do anything the kernel can't do as long as we do it in the user space by executing commands.

Creating Initramfs

Initramfs is a root filesystem which is embedded into the kernel and
loaded at an early stage of the boot process. The initramfs must contain
at least one file, the /init, which is executed by the kernel as the
main init process (PID 1) and must do all the work. In addition there
can be any number of additional files and directories required by /init.
They are usually files we will also find on any other root filesystem,
such as /dev for device nodes, /proc for kernel information, /bin for
additional binaries, etc. When the kernel mounts the initramfs, our root
partition is not yet mounted, so we can't access any of our files. This
means that we only have files in initramfs available, which must contain
everything we need. If we want a shell, we must include it in initramfs
[3].

Installing Dependencies

Before doing anything, we must reinstall the following packages to use
the static use flag, so they can be copied to the initramfs system
without also needing to copy their dependencies:

We can also use the mknod command to create device nodes. The mknod
command has the following syntax:

# mknod device-name device-type major-number minor-number

The device-name is the full name of the device, the device-type is a
block or a character device, the major-number is a number referring to
what group the device is in and a minor-number is the number of the
device within a group. The minor and major numbers are chosen by the
writers of the kernel, and are describes in
/usr/src/linux/Documentation/devices.txt. Example from that file is
presented below:

In the output above, the 1 is a major number, whereas the char is
character device and block is a block device. The 1-12 and 0-250 are
minor numbers and /dev/mem to /dev/initrd are names. The following
command creates a character device /dev/null:

# mknod /dev/null c 1 3

Binary Programs

Any binary we want to execute at boot needs to be copied into initramfs;
we also need to copy any libraries that the binary requires. We can use
the ldd command to see what libraries are needed by certain binary.

Instead of creating countless utilities and libraries we can use
busybox, which contains a set of utilities (such as ls, mkdir, mount,
insmod, and more) in a single binary. To include busybox into initramfs,
we need to enable the static USE flag and reemerge it, making it
statically linked, so it doesn't require any additional libraries.

We can check with ldd to make sure that busybox is not dynamic
executable:

The busybox is a nice utility to have, but not as important as
cryptsetup and lvm, which we must have to be able to decrypt the
contents of our encrypted partition. Of course, the file should not be
dynamically linked, which can be verified with the following command:

The /init script gets executed when the initramfs is loaded. Busybox
includes a fully functional shell, which means we can write our /init in
a bash scripting language instead of making a complicated application
written in assembler or C/C++. The following example realizes the /init
executable as a minimalistic shell script based on the busybox shell:

The /init script is properly commented, so no additional comments are
required. During the booting process, we will have to enter the password
whenever the cryptsetup command will be executed. Once we enter the
password, the partition will be available at /dev/mapper/system. In the
example script we can also see that we used mdev, which is busybox
version of udev. When we run mdev, the device nodes are created so that
/dev/sda3 is available when we want to use it with cryptsetup and mount
command. To make mdev available, we must build busybox with the mdev use
flag as follows:

Then we need to call the rescue_shell script whenever the error
occurs and we'll be dropped into the rescue shell where we can execute
the commands and check what the error was.

If we have an internet connection to the network and we don't have a
monitor present (which is quite common on servers), we can access the
rescue shell remotely via the network, using telnetd. For this to work,
we must first populate the /etc/passwd, /etc/shadow and /etc/group files
in our initramfs. Then we can bring up the network and start a telnet
server:

The initramfs can be made available to our kernel at boot time by
packaging it as a compressed cpio archive. This archive is then embedded
directly into our kernel image or stored as a separate file which can be
loaded by grub during the boot process.

With either method, we need to enable support for Initial RAM filesystem
and RAM disk (initramfs/initrd) support in the kernel:

This will install the initramfs.cpio.gz in /boot directory. We need to
instruct grub to load this file at boot time with the initrd line. The
kernel line is the same as above, so the /boot/kernel is the actual
kernel we copied from the arch/x86_64/boot/bzImage.

When compiling a new kernel, just replace the /boot/kernel with the
newly compiled kernel and the initrd line can stay the same.

Apply the new grub:

# grub-install /dev/sda

We can now reboot our machine. On boot, the kernel will extract the
files from our initramfs archive automatically and execute the /init
script, which will take care of mounting the root partition and
executing the init of our installed linux distribution.