You are not logged in.
SOLUTION:
For security reasons, the .cmdline section of a unified kernel image will not be used by the systemd-boot efistub unless secure boot is enabled.
If you want to use the .cmdline section of the image for your kernel parameters, you must enable secure boot.
Thankfully this is an easy process, especially if you only need to sign one efi executable as is the case for me.
If you don't want to use secure boot, you can use another loader, such as the systemd-bootx64.efi or something else.
The kernel parameters set in the .cmdline section will be replaced by 'options' set in systemd-boot config files.
In my case, I did not think I was setting any options by way of a config file, but for some reason, systemd-boot had some options set.
If secure boot is enabled, systemd-boot will always use the kernel parameters embedded in the .cmdline section of the unified kernel image.
So I enabled secure boot and signed the unified kernel image, this resolved the problem.
It would be possible to use the kernel parameters in .cmdline using systemd-boot without secure boot if the 'options' were not set,
alternatively you could set command line parameters in your boot entry (if your UEFI supports it).
ORIGINAL POST:
Hello,
I've tried to switch from using grub as a bootloader to a unified kernel image with systemd-boot.
The problem is that no kernel parameters are being used, which means that the filesystem and fsck hooks will not work.
I'm not sure whether they are just not being used by systemd-boot, or if they are for some reason not being included in the unified kernel image.
I am using pacman hooks to create the image by combing my initramfs with the microcode, and then using objcopy to produce the binary using systemd-boot.
I do this in a similar way to in this post
cat /boot/amd-ucode.img /boot/initramfs-linux.img > /boot/initramfs-linux-ucode.img
objcopy --add-section .osrel="/usr/lib/os-release" --change-section-vma .osrel=0x20000 \
--add-section .cmdline="/efi/EFI/Arch/cmdline.txt" --change-section-vma .cmdline=0x30000 \
--add-section .linux="/boot/vmlinuz-linux" --change-section-vma .linux=0x2000000 \
--add-section .initrd="/boot/initramfs-linux-ucode.img" --change-section-vma .initrd=0x3000000 \
"/usr/lib/systemd/boot/efi/linuxx64.efi.stub" "/efi/EFI/Arch/arch.efi"
The cmdline.txt file embeded in the image is as follows:
root=UUID=167e4d24-bb3d-47e5-9f74-2e7debb963c4 rw loglevel=3 quiet quiet psmouse.synaptics_intertouch=0
Where 167e4d24-bb3d-47e5-9f74-2e7debb963c4 is the UUID copied from /etc/fstab mounted at /
However, this is not used by linux at all. The output of cat /proc/cmdline shows that this is the case both in the initrd and in the main system.
[root@jason-arch ~]# cat /proc/cmdline
D
[root@jason-arch ~]#
That's not a typo, its literally the letter 'D' (which I don't think is typical)
Because of this, the system gets as far as unlocking my LUKS encrypted drive, but then refuses to perform the fsck or filesystem hooks and I manually have to mount the root filesystem at /new_root to continue.
I should note that I have modified /etc/mkinitcpio.conf and use my own hook to decrypt the drive.
I don't think that this is the problem though, because everything worked fine using grub before.
I did change the encryption hook though to work with this new setup, it works as intended and performs essentially the same function as the one I used previously.
# /etc/mkinitcpio.conf
MODULES=(loop)
BINARIES=()
FILES=(/root/header.img)
HOOKS=(base udev keyboard autodetect modconf block encryption lvm2 filesystems fsck)
# /etc/initcpio/hooks/encryption
#!/usr/bin/ash
# My hook which will extract a key file from some removable storage and
# use a password given to unlock the root partition
run_hook() {
# Enable the required modules to open and mount encrypted devices
modprobe -a -q dm_crypt >/dev/null 2>&1
modprobe loop
# Output a message
clear
echo
echo "[personal info]"
echo
echo -n " Please insert a valid removable device"
# Wait until a recognised removeable device has been inserted
while [ 1 = 1 ]; do
if [ -L '/dev/disk/by-id/usb-Generic_Flash_Disk_DD03A0AB073A2345-0:0-part2' ]; then
break
fi
# If a device hasn't been inserted then output a message and wait 1 second
echo -n "."
sleep 1
# Set an environment variable to help me make the screen look pretty
AA=1
done
# Insert additional space for pretty purposes
if [ "$AA" = 1 ]; then
echo
fi
# The user can enter password as many times as they'd like, they can enter
# no password in order to break from the loop and use the rootfs shell.
while [ 1 = 1 ]; do
# Prompt the user for a password
echo
read -s -p " Enter password (leave blank to use the rootfs shell): " PASSWORD
echo
# Stop if nothing was entered
if [ -z "$PASSWORD" ]; then
return
fi
# Try to unlock one of the drives with the password given
for DRIVE in "/dev/disk/by-id/usb-Generic_Flash_Disk_DD03A0AB073A2345-0:0-part2"
do
# Only open the drive if it exists and no other drive has already
# been unlocked
if [ ! -z "$UNLOCKED" ]; then
break
fi
if [ -L "$DRIVE" ]; then
# Notify that this drive was detected
echo
echo " Detected drive $DRIVE"
# Try and open drive
ERROR=$(echo -n "$PASSWORD" | cryptsetup open "$DRIVE" cryptUSB -d - 2>&1 >/dev/null)
# Output error message if necessary
if [ ! -z "$ERROR" ]; then
echo " There was an error while attempting to unlock this volume."
echo " Drive: $DRIVE"
echo " Error: $ERROR"
fi
# Check if there is en entry in /dev/mapper
if [ -L "/dev/mapper/cryptUSB" ]; then
UNLOCKED="$DRIVE"
echo " Successfully unlocked drive $DRIVE"
fi
fi
done
# If a drive was unlocked, then break from the password loop
if [ ! -z "$UNLOCKED" ]; then
break
fi
done
# At this point, the user has enterd the correct password, and an encrypted
# volume has been opened at /dev/mapper/cryptUSB
unset PASSWORD
echo
# But if for something went wrong, we let the user finish from the rootfs
# shell
if [ -z "$UNLOCKED" ]; then
echo " Something went wrong, the drive might not have been unlocked"
echo " Mount the root partition to /new_root to continue. Good luck."
return
fi
if [ ! -L /dev/mapper/cryptUSB ]; then
echo " Something went wrong, /dev/mapper/cryptUSB does not exist."
echo " Mount the root partition to /new_root to continue. Good luck."
reutrn
fi
# Mount the volume at /mnt
echo " Mounting volume on drive to /mnt"
echo
mkdir -p /mnt
mount /dev/mapper/cryptUSB /mnt
DISKID="/dev/disk/by-id/nvme-BC511_NVMe_SK_hynix_256GB_ND05N50631010595E-part2"
# Unlock the root partition using the keyfile in /mnt
case $UNLOCKED in
"/dev/disk/by-id/usb-Generic_Flash_Disk_DD03A0AB073A2345-0:0-part2")
echo " Attempting to decrypt root volume with keyfile on drive"
ERROR=$(cryptsetup --header=/root/header.img --key-file=/mnt/keyfile --keyfile-size=8192 open $DISKID cryptroot >/dev/null 2>&1)
;;
*)
echo " I don't know how to use this device."
echo " Mount the root partition to /new_root to continue. Good luck."
return
;;
esac
echo
# Output a message if the drive could not be opened
if [ ! -z "$ERROR" ]; then
echo " There was an error while attempting to unlock the root volume"
echo " Error: $ERROR"
fi
if [ ! -L "/dev/mapper/cryptroot" ]; then
echo " The root drive could not be unlocked."
echo " Mount the root partition to /new_root to continue. Good luck."
return
fi
echo " Drive successfully unlocked."
# Unmount and close the drive with the key
umount /mnt
cryptsetup close cryptUSB
}
The fsck and filesystem hooks are unable to run because no filesystem is specified in the kernel parameters.
This is the output I recieve after the drive is successfully unlocked and ready to be mounted.
...
Attempting to decrypt root volume with keyfile on drive
Drive successfully unlocked.
ERROR: device '' not found. Skipping fsck
:: mounting '' on real root
mount: /new_root: wrong fs type, bad option, bad superblock on , missing codepage or helper program, or other error.
You are now being dropped into an emergency shell.
sh: can't access tty: job control turned off
[rootfs ]#
After this point, I can mount the root filesystem to /new_root and exit to continue the boot as normal (though without my usual kernel parameters), for example:
[rootfs ]# mount /dev/mapper/store-root /new_root
[rootfs ]# exit
I think that I could remove the filesystem and fsck hooks from mkinitcpio.conf and re-create their functionality in my own script,
but that would be less than ideal, and wouldn't solve the problem with the kernel parameters.
Last edited by jasonmxyz (2021-07-20 19:31:35)
Offline
EDIT:
You do not need to use the below solution if you're using secure boot.
See the original post for more details.
ORIGINAL POST:
It appears that the systemd-boot efistub does not load the .cmdline section correctly, but systemd-bootx64.efi does.
So when booting systemd-bootx64.efi and letting that load my unified kernel image, it works fine.
So a fix for my problem was to copy the systemd-boot loader into the efi partition and move my unified kernel image to another directory.
And then modify my pacman hooks to place the unified kernel image in /efi/EFI/Linux
$ mkdir -p /efi/EFI/Boot
$ cp /usr/lib/systemd/boot/efi/systemd-bootx64.efi /efi/EFI/Boot/bootx64.efi
$ mkdir -p /efi/EFI/Linux
$ mv /efi/EFI/Arch/arch.efi /efi/EFI/Linux/arch.efi
$ rm -rf /efi/EFI/Arch
I would not consider this a real solution though, I wanted to be able to boot the unified kernel image directly from my UEFI hardware.
It is strange that the efistub provided by systemd-boot is unable to use the .cmdline seciton.
Is this the intended behaviour of linuxx64.eif.stub?
Last edited by jasonmxyz (2021-07-20 17:33:36)
Offline
Have you tried switching to the systemd based init in mkinitcpio? (In addition I'd suggest first getting it working without your custom encrypt hooks just to establish a working base line).
"the wind-blown way, wanna win? don't play"
Offline
Have you tried switching to the systemd based init in mkinitcpio? (In addition I'd suggest first getting it working without your custom encrypt hooks just to establish a working base line).
Thanks for the suggestion, the issue with the command line parameters would not have been solved by switching to a different init program.
The problem was that the systemd-boot efistub would not use the .cmdline section of the unified image if some "options" were specified elsewhere.
When secure boot is enables, the efistub will always use the embedded parameters.
Offline
Are you sure?
options – space-separated command line options to pass to the EFI program or kernel parameters. Optional, but you will need at least root=dev if booting Linux. This parameter can be omitted if the root partition is assigned the correct Root Partition Type GUID as defined in Discoverable Partitions Specification and if the systemd mkinitcpio hook is present.
...
Note: If options is present in a boot entry and Secure Boot is disabled, the value of options will override any .cmdline string embedded in the EFI image that is specified by efi or linux (see #Preparing a unified kernel image). With Secure Boot, however, options (and any edits made to the kernel command line in the bootloader UI) will be ignored, and only the embedded .cmdline will be used.
I read that to mean that you can indeed still use the embeded cmdline with efi and with secure boot disabled as long as you don't have any options line in your Systemd-boot loader entry AND as long as you are using the systemd mkinitcpio hook (i.e. using the systemd based init not busybox).
"the wind-blown way, wanna win? don't play"
Offline
Are you sure?
Systemd-boot wiki wrote:options – space-separated command line options to pass to the EFI program or kernel parameters. Optional, but you will need at least root=dev if booting Linux. This parameter can be omitted if the root partition is assigned the correct Root Partition Type GUID as defined in Discoverable Partitions Specification and if the systemd mkinitcpio hook is present.
...
Note: If options is present in a boot entry and Secure Boot is disabled, the value of options will override any .cmdline string embedded in the EFI image that is specified by efi or linux (see #Preparing a unified kernel image). With Secure Boot, however, options (and any edits made to the kernel command line in the bootloader UI) will be ignored, and only the embedded .cmdline will be used.I read that to mean that you can indeed still use the embeded cmdline with efi and with secure boot disabled as long as you don't have any options line in your Systemd-boot loader entry AND as long as you are using the systemd mkinitcpio hook (i.e. using the systemd based init not busybox).
I think you're probably right.
I still dont think that using the systemd init would have solved the problem by itself though, because I hadn't set any options outside of the embedded cmdline.
I guess that somehow the systemd-boot efistub thinks that the options variable has been set.
Maybe some config file somewhere has been changed and is setting this option.
I don't remember setting any options, especially not to just "D".
Offline
The "solution" in post #1 is obviously wrong, the unified kernel image works fine, including cmdline, even without secure boot enabled. Just fix your EFI boot entry.
Edit: Spelling
Last edited by Tarqi (2021-07-20 18:48:28)
Knowing others is wisdom, knowing yourself is enlightenment. ~Lao Tse
Offline
The "solution" in post #1 is obviously wrong, the unified kernel image works fine, including cmdline, even without secure boot enabled. Just fix your EFI boot entry.
Edit: Spelling
The unified kernel image works fine, you're right. But some settings somewhere made systemd-boot override my cmdline.
I wanted to boot the kernel image directly from UEFI, but on my system it is not possible to give parameters from the EFI boot entry, so I needed to force it to use the .cmdline section.
I was planning on using secure boot anyway, so the solution I came up with was effective.
Though my explanation of why the cmdline was not being used in my original post was wrong. I've updated it and it makes more sense now (to me at least)
Thanks for the help.
Offline
If you have secureboot enabled and want to still set cmdline externally, leave the .cmdline section of the uefi image blank and firmware will not force it.
Offline