I like my initrd to be kernel agnostic. That means it only contains userspace tools, so it does not get updated with every kernel update.
Its firmware, make it once for the life of the system.
That's pretty clever!
Never thought of it like this.
The OP (and me too, FWIW) like to boot from EFI directly (EFI stub). This should not prevent me from using this strategy, right? I mean, could the initrd also reside on the EFI partition?
I initially thought I'd have to have everything I need in the kernel directly, for EFI stub booting to work (with LUKS+LVM).
So I wrote a little script back then.
It uses mkinitramfs-ll as it seemed lightweight and good to understand for a bash guy like me
The layout is a little involved, as I compile the kernel, compile the initramfs and then compile the kernel again, putting the initramfs directly into it.
Maybe I overthought things back then, but it works since 5 years now without a hickup.
My production kernel 5.15.x on my thinkpad has 26M right now with this config (with quite a bit of pro-audio stuff in it). Not too bad I think.
One of these days I'll start to host all my Gentoo tools somewhere...
Till then: Here it is. make menuconfig your kernel and call kernel_build.sh afterwards.
Code: Select all
#!/bin/bash
# kernel_build.sh
# Builds an EFI-compatible kernel (no need for a bootloader)
# with integrated initramfs (possibly for LUKS encrypted root)
# Copyright 2018 tbart <gentoo.rockmusic@neverbox.org>
# Licence: Do whatever you see fit with it, but attribute me. Send improvements back to me.
# /etc/fstab should contain something along this line:
#
# /dev/nvme0n1p1 /boot/efi vfat noatime,noauto 0 0
### USER CONFIGURABLE
EFI="/boot/efi"
### END USER CONFIGURABLE
distro="$(sed -n '/NAME/s#.*=\(.*\)#\L\1#p;q' /etc/os-release 2>/dev/null)"
[[ $distro ]] || die "Error getting distro name, /etc/os-release missing (or NAME= variable not contained)"
tools="efibootmgr sed lsblk findmnt mkinitramfs-ll.bash"
for tool in $tools
do
[[ -e $(which $tool 2>/dev/null) ]] || die "Necessary tool $tool not found. Please install."
done
cleanup() { #{{{ #Cleanup in case of interruption
status ok "Cleaning up."
umount $EFI 2>/dev/null
mount -o remount,ro efivarfs 2>/dev/null
exit 0
} #}}}
trap cleanup SIGINT SIGKILL SIGTERM
status() { #{{{ #Output colored status messages
case $1 in
error)
shift
echo -e "\e[01;31m$@\e[0m"
;;
ok)
shift
echo -e "\e[01;32m$@\e[0m"
;;
warn)
shift
echo -e "\e[01;33m$@\e[0m"
;;
*)
die "status function used incorrectly"
;;
esac
echo
} #}}}
die() { #{{{ #Echo message and exit unsuccessfully
echo
status error "$0: $@"
cleanup
echo "Exiting."
echo
exit -1
} #}}}
enabled() { #{{{ #Check whether kernel config option is enabled; accepts y, m, y|m
egrep -iq "$1=($2)" .config
} #}}}
checkkernelconfig() { #{{{ # Check existance of necessary kernel config values
# Options needed for booting have to be built in
for opt in CONFIG_EFI CONFIG_EFI_STUB CONFIG_FW_LOADER
do
enabled "$opt" "y" || die "Kernel config $opt needs to be set to Y (builtin)"
done
enabled CONFIG_EXTRA_FIRMWARE "\".+\"" || die "Kernel config CONFIG_EXTRA_FIRMWARE needs to contain the latest ucode for your CPU (e.g. intel-ucode/06-8e-09; use iucode_tool -S -l /lib/firmware/intel-ucode/* for finding the correct package for your CPU! emerge sys-firmware/intel-microcode sys-apps/iucode_tool or respective packages for your vendor!)"
enabled CONFIG_EXTRA_FIRMWARE_DIR "\".+\"" || die "Kernel config CONFIG_EXTRA_FIRMWARE_DIR needs to be set (to /lib/firmware, most likely)"
# Options for handling installation of a new kernel may be modular
for opt in CONFIG_EFIVAR_FS CONFIG_EFI_PARTITION
do
enabled "$opt" "y|m" || die "Kernel config $opt needs to set to Y (builtin) or M (module)"
done
# Options that improve the user experience are recommended
for opt in CONFIG_FB_EFI CONFIG_EARLY_PRINTK_EFI
do
enabled "$opt" "y|m" || { echo "Kernel config $opt is recommended as Y (builtin) or M (module), but not strictly necessary.";
echo "Ctrl-C to abort. Will continue in 3 seconds.";
sleep 3; }
done
} #}}}
efientry() { #{{{ #Adds/modifies EFI bootmgr entry
[[ -d "$EFI/EFI/$distro" ]] || mkdir "$EFI/EFI/$distro" || die "Error creating directory $EFI/EFI/$distro"
entry="/EFI/$distro/kernel-$KV.efi"
# The efibootmgr executable with all necessary parameters
efiexec="efibootmgr -d $(lsblk -pno pkname $(findmnt -no source $EFI)) -p $(tail -c2 <<< $EFIDEV)"
# Find existing boot number ..
existingbootnum="$($efiexec -v | sed -n "/${entry////\\\\}/{s#^Boot\(....\).*#\1#;p}")" # XXX: probably catch errors of efiexec as well
if [[ $existingbootnum ]] # .. and if it exists
then
[[ ! $setasfirst ]] && return # .. but should not be set as first, don't touch it
# .. otherwise, it is easiest to just delete the existing one and add it anew
$efiexec -b $existingbootnum -B || die "Could not delete existing boot entry."
fi
# .. there's no entry there (anymore), add it
$efiexec -c -l "$entry" -L "$distro kernel $KV" || die "Could not add new EFI boot entry."
} #}}}
[[ $PWD == "/usr/src/linux" ]] || die "PWD is not /usr/src/linux ! Check the symlink is correctly set and cd to /usr/src/linux"
checkkernelconfig
grep -q $EFI /etc/mtab || mount $EFI 2>/dev/null || die "$EFI could not be mounted! It seems the fstab entry is missing."
EFIDEV="$(findmnt -no source $EFI)"
KV=$(readlink -f . | sed 's#.*linux-\(.*\)#\1#') #Kernel version to build
[[ $KV =~ [0-9]-rt[0-9] ]] && KV="${KV/-rt/-rt-rt}" # XXX: This is strange, why do -rt kernels have -rt-rt47 as a version?
JOBS=$(($(grep -c processor /proc/cpuinfo)+1)) #Number of parallel build jobs
# If commandline parameters are needed, they can also be specified in the kernel
# config (look for CMDLINE)
# Kernel build, first round, needed for getting modules for the initramfs
# XXX: Can we just make modules without building the whole kernel in this step?
status ok "Compiling kernel, iteration 1."
make -j$JOBS \
|| die "Error building kernel (iteration 1)."
status ok "Compiling kernel modules."
make -j$JOBS modules \
|| die "Error building modules."
status ok "Installing kernel modules."
make modules_install \
|| die "Error installing modules."
# Build initramfs with the modules just built
status ok "Building initramfs."
mkinitramfs-ll.bash -c none -k $KV \
|| die "Error creating initramfs."
# Include the resulting initramfs in the kernel, so we don't need an initrd parameter
# normally passed by the boot loader
status ok "Modifying kernel config to include built initramfs."
sed -i "/CONFIG_INITRAMFS_SOURCE=/s#\".*\"#\"/boot/initramfs-$KV.cpio\"#" .config \
|| die "Error adapting kernel config for initramfs."
# probably add the missing config for UID and GID, but it gets prompted anyway..
# This just adapts it, does not add it: /CONFIG_INITRAMFS_ROOT_[UG]ID/s#.*\(CONFIG_[^ ]*\).*#\1=\"0\"#
status ok "Compiling kernel, iteration 2 (including initramfs)."
make -j$JOBS \
|| die "Error building kernel (iteration 2) with included initramfs."
# Copy kernel to EFI partition
status ok "Copying kernel to EFI partition."
cp arch/$(uname -m)/boot/bzImage $EFI/EFI/$distro/kernel-$KV.efi \
|| die "Error copying kernel image to EFI partition ($EFI/EFI/$distro/kernel-$KV.efi)."
status ok "Mounting efivarfs read/write."
mount -o remount,rw efivarfs \
|| die "Could not mount efivarsfs rw (missing kernel support in running kernel? Look for EFIVAR_FS)."
status ok "Updating EFI boot order/entries."
efientry \
|| die "Error adding/modifying EFI boot entry."
status ok "Rebuilding kernel modules."
emerge @module-rebuild \
|| die "Error rebuilding kernel modules."
status ok "All necessary operations finished successfully! Some EFI implementations are buggy.
If the boot order has not changed as expected, do so via the EFI firmware during boot.
Make sure you include all necessary firmware and microcode blobs into the kernel or the initramfs
when needed early on."
cleanup
It should be pretty straight forward to change the tool for building the initramfs to something else.
mkinitramfs-ll however is really easy to setup.
This is everything I need in /etc/mkinitramfs-ll.conf (getting discards/cryptsetup_args to work was a little more involved - it's part of the official mkinitramfs-ll distribution now, however; the config below plus "luks,discard" in /etc/crypttab is enough now; you want to be able to fstrim your NVMe)
Code: Select all
opts[-module-boot]+=:kms:
opts[-module-kms]+=:i915:
opts[-firmware]=:iwlwifi-8265:i915/:
opts[-module-group]+=:boot:kms:swsusp
opts[-bin]+=:blkid:
opts[-font]+=:ter-v32n
opts[-keymap]+=:de-latin1-nodeadkeys:
opts[-lvm]=:
opts[-luks]=:
opts[-compressor]=none
opts[-kernel-version]=$(uname -r)
env=(
${MIR_EXTRA_ENV}
# Disable applets/binaries checking
'CHECK_ENV=false'
# cmdline needed for EFI boot
'root=vg00-root'
'lvm=vg00-nvme0n1p5'
'rootflags=user_xattr'
'luks=pwd'
'cryptsetup_args=--allow-discards'
)