mirror of
https://github.com/ItsDrike/dotfiles.git
synced 2024-12-26 13:14:35 +00:00
212 lines
8.3 KiB
Markdown
212 lines
8.3 KiB
Markdown
|
# 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.
|