dotfiles/guides/05_SYSTEMD_INITRAMFS.md

212 lines
8.3 KiB
Markdown
Raw Normal View History

# Systemd initramfs
The initial ramdisk is in essence a very small environment (early userspace)
whihc loads various kernel modules and sets up necessary things before handing
control over to `init` program (systemd).
By default, Arch Linux uses a BusyBox+udev based initial ramdisk, generated by
`mkinitcpio`. This default initrd is essentially just a small script, that
executes other scripts, called hooks.
As an alternative to this, it's possible to have systemd run from the very
start, during that initial ramdisk phase. With this approach, the tasks ran at
this phase are determined by regular systemd unit files.
## Why?
Obviously, BusyBox initramfs works just fine, so why would you want to switch?
Well, there's a few reasons:
- **Consistency across boot phases:** The same systemd process that handles your
system after boot can also manage the early userspace during boot, providing
consistency in handling services, devices and dependencies throughout the
entire boot process.
- **Simplified troubleshooting:** The tools and logs available during the boot
process will be the same as those used once the system is fully booted,
allowing you to troubleshoot problems with familiar tools (`journalctl`,
`systemctl`, ...)
- **Consistent Unit Files:** Since systemd uses the same unit files in the
initramfs as it does in the fully booted system, the configuration for many
tasks (like mounting filesystems) is unified, reducing duplication of
configuration files.
- **TPM Unlocking Support:** Systemd has built-in support for requesting data
from TPM, allowing for a setup with TPM auto-unlocking an encrypted root
partition, without having to specify the decryption password.
- **Parallel Service Startup:** Systemd is known for the ability to start
services in parallel, which can potentially **speed up the boot process**
compared to sequential script-based approach.
- **Integrated Mount Handling:** With systemd, managing complex mount setup
(e.g. LVM RAID) can be more seamless, since it natively supports these and can
handle them with less custom scripting.
That said, it's important to also mention some downsides and reasons why you
might not want to use systemd-based initramfs:
- **Simplicity:** If you prefer a simple, more minimalistic approach,
BusyBox-based initramfs might be sufficient and easier to manage.
- **Size:** A systemd-based initramfs might be larger than a minimal
BusyBox-based initramfs, which could be a concern on systems with very limited
space.
- **Compatibility:** If you're running some custom scripts or hooks, they might
not work with a systemd-based initramfs.
## Switching to systemd initramfs
Open `/etc/mkinitcpio.conf` and find a line that starts with `HOOKS=`
- Change `udev` to `systemd`
- Change `keymap consolefont` to `sd-vconsole`
- Add `sd-encrypt` before `block`, and remove `encrypt`
- If you were using `mkinitcpio-numlock`, also remove `numlock`, it doesn't work
with systemd (we'll go over how to auto-enable numlock later)
Additionally, with systemd initramfs, you shouldn't be specifying `root` nor
`cryptdevice` kernel arguments, as systemd can actually pick those up
automatically (they'll be discovered by [systemd-cryptsetup-generator] and
auto-mounted from initramfs via [systemd-gpt-auto-generator]). We will however
still need the `rootflags` argument for selecting the btrfs subvolume (unless
your default subvolume is the root partition subvolume).
[systemd-cryptsetup-generator]: https://wiki.archlinux.org/title/Dm-crypt/System_configuration#Using_systemd-cryptsetup-generator
[systemd-gpt-auto-generator]: https://wiki.archlinux.org/title/Systemd#GPT_partition_automounting
So, let's edit our kernel parameters:
```bash
echo "rw loglevel=3" > /etc/kernel/cmdline # overwrite the existing cmdline
echo "rootflags=subvol=/@" >> /etc/kernel/cmdline
```
You'll also need to modify the `/etc/fstab`, as systemd will not use the
`/dev/mapper/cryptfs` name, but rather you'll have a `/dev/gpt-auto-root`
(there'll also be `/dev/gpt-auto-root-luks`, which is the encrypted partition).
If you prefer using a mapper device, you can also use `/dev/mapper/root`.
Alternatively, you can use the label to mount. (if you followed the
installation guide, that would be `/dev/disk/by-label/FS`.)
```bash
vim /etc/fstab
```
Finally, regenerate the initramfs with: `pacman -S linux` (you could also do
`mkinitcpio -P`, however that won't trigger the pacman hook which auto-signs our
UKI images for secure boot, so you'd have to re-sign them with `sbctl` manually,
if you're using secure-boot) and reboot to check if it worked.
## Activating numlock
Since we had to remove `mkinitcpio-numlock`, as that hook isintended for BusyBox
based initrd, we'll want to have an alternative available.
First though, we should also remove the package: `pacman -R mkinitcpio-numlock`.
### The simple, but imperfect option
There is a `systemd-numlockontty` AUR package which creates a systemd service
that enables numlock in TTYs after booting (you'll need to enable it), this
however doesn't happen in initramfs directly, only afterwards.
Depending on what you will need, this may be sufficient. If you are going to be
typing a decryption password at this early stage and you wish to have numlock
support there, you will need to do some more work.
### The proper solution
To enable numlock before you're prompted for the decryption password, we'll need
to create a custom initcpio hook, that will return a systemd service which will
do the enabling. We'll put this hook into `/usr/lib/initcpio/install/numlock`,
with the following content:
```bash
#!/bin/bash
build() {
add_binary /bin/bash
add_binary /usr/bin/setleds
add_binary /usr/local/bin/numlock
cat >"$BUILDROOT/usr/lib/systemd/system/numlock.service" <<EOF
[Unit]
Description=Enable numlock
Before=cryptsetup-pre.target
DefaultDependencies=no
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/local/bin/numlock
EOF
add_systemd_unit cryptsetup-pre.target
cd "$BUILDROOT/usr/lib/systemd/system/sysinit.target.wants" || exit
ln -sf /usr/lib/systemd/system/cryptsetup-pre.target cryptsetup-pre.target
ln -sf /usr/lib/systemd/system/numlock.service numlock.service
}
help() {
cat <<EOF
This hook adds support to enable numlock before sd-encrypt hook is run.
EOF
}
```
This script is also present in my dotfiles, so you can just copy it from there:
```bash
cp ~/dots/root/usr/lib/initcpio/install/numlock /usr/lib/initcpio/install
```
Next we will need to create that `/usr/local/bin/numlock` script. This script
will do the actual enabling of numlock. Note that we can only use the binaries
that we explicitly included in our hook inside our script.
```bash
#!/bin/bash
for tty in /dev/tty[0-9]; do
/usr/bin/setleds -D +num < "$tty"
done
```
If you ran the `install_root.sh` script from my dotfiles during
[INSTALLATION](./01_INSTALLATION.md), this script will already be present in
your `/usr/local/bin`
Now we will need to add our custom new `numlock` hook to
`/etc/mkinitcpio.conf`, before the `sd-encrypt` hook (assuming you're using
encryption), but after the `keyboard` and `sd-vconsole` hooks.
Finally, we'll need to rebuild initramfs, which we should trigger with `sudo
pacman -S linux`, to make sure the secure-boot signing also runs. When
re-building the initramfs, pay attention on the output, you should see it pass
with no errors:
```text
-> Running build hook: [base]
-> Running build hook: [systemd]
-> Running build hook: [autodetect]
-> Running build hook: [microcode]
-> Running build hook: [modconf]
-> Running build hook: [kms]
-> Running build hook: [keyboard]
-> Running build hook: [sd-vconsole]
-> Running build hook: [numlock] # <-- make sure this is present
-> Running build hook: [sd-encrypt]
-> Running build hook: [block]
-> Running build hook: [filesystems]
-> Running build hook: [fsck]
```
> [!NOTE]
> If you see some warnings there, like:
> `==> WARNING: Possibly missing firmware for module: 'xyz'`, you can usually
> safely ignore these. Just make sure there's no `==> ERROR: ...`
If you didn't see any errors, you can now reboot.
> [!IMPORTANT]
> In some cases, the numlock led indicator might not turn on immediately, even
> though numlock was actually turned on. This may mislead you towards thinking
> it is not on, even though it actually is. I'd recommend trying it out by
> actually typing something it at this time.
>
> Note that after this early boot stage, the indicator should light up
> eventually.