You are not logged in.

#1 2021-07-19 22:41:32

jasonmxyz
Member
From: United Kingdom
Registered: 2021-07-19
Posts: 5

[SOLVED] Unified kernel image (efistub) not using cmdline

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

#2 2021-07-20 15:59:20

jasonmxyz
Member
From: United Kingdom
Registered: 2021-07-19
Posts: 5

Re: [SOLVED] Unified kernel image (efistub) not using cmdline

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

#3 2021-07-20 17:18:01

CarbonChauvinist
Member
Registered: 2012-06-16
Posts: 366
Website

Re: [SOLVED] Unified kernel image (efistub) not using cmdline

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

#4 2021-07-20 17:32:03

jasonmxyz
Member
From: United Kingdom
Registered: 2021-07-19
Posts: 5

Re: [SOLVED] Unified kernel image (efistub) not using cmdline

CarbonChauvinist wrote:

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

#5 2021-07-20 17:49:09

CarbonChauvinist
Member
Registered: 2012-06-16
Posts: 366
Website

Re: [SOLVED] Unified kernel image (efistub) not using cmdline

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).


"the wind-blown way, wanna win? don't play"

Offline

#6 2021-07-20 18:26:40

jasonmxyz
Member
From: United Kingdom
Registered: 2021-07-19
Posts: 5

Re: [SOLVED] Unified kernel image (efistub) not using cmdline

CarbonChauvinist wrote:

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

#7 2021-07-20 18:45:29

Tarqi
Member
From: Ixtlan
Registered: 2012-11-27
Posts: 141
Website

Re: [SOLVED] Unified kernel image (efistub) not using cmdline

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

#8 2021-07-20 19:37:36

jasonmxyz
Member
From: United Kingdom
Registered: 2021-07-19
Posts: 5

Re: [SOLVED] Unified kernel image (efistub) not using cmdline

Tarqi wrote:

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

Board footer

Powered by FluxBB