Created attachment 222729 [details]
Linux Bash script to generate the unified kernel images for testing with FreeBSD loader
A unified Linux kernel image built with systemd's EFI STUB is currently (systemd v247, FreeBSD 13 BETA3) unable to be chain-loaded by FreeBSD's native bootloader "loader".
I have tested this on a VMware virtual machine's UEFI environment.
- STEPS TO REPRODUCE
1. Prepare artifacts by running [reproduce_artifacts.sh] on a recent GNU/Linux machine (depends on bash, zstd, base64, gzip, objcopy, sha1sum)
2. Download latest FreeBSD 13 disc1 iso image (tested with BETA3)
3. Install FreeBSD 13 via UEFI mode (select "Guided ZFS" and "GPT" and enable SSH)
4. Boot into system
5. Copy both [unified-upstream.efi] and [unified-patched.efi] artifacts to the FreeBSD's [/boot/] folder via SSH
6. Reboot the machine
7. At loader prompt, press  to drop into loader shell
8. Execute the previously generated upstream unified kernel image by entering [chain /boot/unified-upstream.efi]
The attached Bash script [reproduce_artifacts.sh] can be used to generate two different unified kernel images consisting of the Debian GNU/Linux installer kernel and ramdisk, one using the upstream stub and one using the patched stub that utilized the workaround described below. ([unified-upstream.efi] and [unified-patched.efi])
(The patched stub variant is stored in the shell file via base64 encoding.)
- EXPECTED BEHAVIOR
Unified linux kernel image with embedded Debian GNU/Linux installer starts properly.
- OBSERVED BEHAVIOR
After the loading process of the EFI binary completes, the system hangs completely.
According to FreeBSD's manpage regarding UEFI, it is noted as caveat that "EFI environment variables are not supported by loader(8) or the kernel."
This lead me to believe that the problem could be circumvented by removing practically all lines from systemd's [stub.c] that had to do with reading or setting EFI environment variables.
The patch I created [systemd-v247-efi-stub-freebsd-loader-workaround.patch] radically removes all the potentially offending lines, adds two convenient debug messages and finally allowed me to successfully chainload unified kernel images via FreeBSD's loader. (I am aware that systemd code is not relevant for FreeBSD, but I think it may serve as an indicator what kind of firmware calls are being executed.)
This is a workaround for this particular situation, not a solution.
This radical approach is obviously not acceptable to be incorporated into the upstream stub, given that there are assumingly significantly more users interested in proper EFI variables than in booting via the FreeBSD loader
- APPLICATION (OFF-TOPIC)
I am developing a GNU/Linux based embedded system that consists of a single, self-contained (e.g. 500+ MB SquashFS ramdisk), highly compressed unified UEFI amd64 kernel image binary.
The embedded system utilizes its own storage / auto-provisioning software which relies heavily on OpenZFS.
Since OpenZFS is a first-class citizen in the FreeBSD world and other third-party bootloaders (GRUB, rEFInd, ...) appear to offer only limited ZFS support (+ problematic GPLv3 licensing), transplanting FreeBSD's bootloader and using it to chainload unified kernel images seems like a reasonable choice.
This approach enables the user to place unified kernel images on a (perhaps RAID-enabled) ZFS pool dataset and have them chain-loaded.
Compared to storing them directly on the FAT32 ESP partition, this ensures the integrity of the images in case of a sudden loss of power (e.g. during a software update), avoids tricky MDRAID/LVM based configurations and also removes the need to manually sync ESP partition content on multiple drives when RAID is not used.
I have not been able to find resources about people attempting to use FreeBSD's loader to conveniently and reliably boot their OpenZFS-based GNU/Linux machines, but I personally think it would be a great alternative compared to what is available right now.
I have previously filled an issue report with the systemd project and got the recommendation to get in touch with people from the FreeBSD project (= "not-our- bug"). Right now, it appears as if certain EFI variables get dropped while chain-loading and thus result in the EFI stub not working correctly. When I comment out all lines that deal with EFI variable reading / setting, the stub works as expected. The unified kernel image made with the upstream stub works flawlessly when being launched via an UEFI shell, iPXE or virtually any other UEFI loader.
You may view this issue here: https://github.com/systemd/systemd/issues/18733
(The patch to systemd may be retrieved on that page as well: https://github.com/systemd/systemd/files/6023017/patch_and_script.tar.gz)
(If any of you do not have a GNU/Linux system handy, I may send / upload the EFI files resulting from that script elsewhere upon your request.)
Is there something you can do about this problem?
Thank you very much!
The statement about EFI variables not being supported is false and should be removed from the manual.
I've read through the referenced discussion and can't find what, exactly, is failing here.
what needs to be done that's not done? What parts of the spec are they relying on that don't seem to being done? The only one I'm aware of is that if they require BootCurrent to point to the BootXXXX variable for their software, then we don't do that: we leave them set to how loader.efi was booted (shell.efi doesn't care).
I've updated the manual to remove the now-obsolete caveat.
Hello Mr. Losh,
thank you very much for your reply!
As reported on the systemd GitHub issue thread, I was able to track down the problem:
The FreeBSD loader does not set the "FilePath" EFI variable, which is hence NULL, but systemd's EFI stub expects it NOT to be NULL.
Apparently, the standard does not dictate that it cannot be NULL, so Mr. Lennart Poettering kindly implemented a NULL check in the stub which fixes this issue that caused me to open this bug report in the first place.
That is, it is not a FreeBSD bug.
I do not know if it would make sense for FreeBSD's loader to set "FilePath" to something else, but I would consider this a feature request which is outside the scope of my bug report here.
Hence, I CLOSE this bug report with "Not A Bug".
Thank you very much!
Alexander Schreiber (schreiberstein)
FilePath isn't listed in the Global Variables as an environment variable. It is the name of a parameter that are passed to LoadFile protocol. The standard describes various scenarios for booting, and what FilePath has to be set to for those scenarios. But in all cases I found in either the 2.6 or the 2.8 standard, they were for this parameter and not a global variable. It's also in the EFI_LOADED_IMAGE_PROTOCOL too, however that's generate by the firmware. There's no indication at all these are environment variables, and none exist on my directly booted from UEFI system.
Was it this FilePath that wasn't set? Or is there some programatic thing that the UEFI interface generates from stuff our chain loader command gives it that are wrong somehow and that's what the complaint was about?
I would love to see this work continue, as eventually it would be possible to boot Linux or FreeBSD from a zfs pool using boot environments, and well, that would just be really really nice for those of us that work in both environments. Right now I do several dual boot systems and being able to toss the complexity that brings and just using the wonderful FreeBSD loader would be *GRAND*.
(In reply to Warner Losh from comment #4)
From my understanding, the value was simply not set. Maybe it could be set on a different UEFI implementation (e.g. I have only confirmed this on VMware)?
However, I can load the unified linux EFI binary via UEFI shell, automatically via renaming it to bootx64.efi, systemd-boot or iPXE. This leads me to conclude that there is something inherently different in FreeBSD loader's behavior.
Take iPXE for instance: I regularly test my system by starting iPXE from an ISO file. It then proceeds to download the unified EFI binary off a HTTP server (to RAM) and chain-loads it.
I have just tested what iPXE sets "FilePath" to (Situation should be similar, given that FilePath cannot possibly be a local EFI accessible filesystem if the file has been fetched via HTTP), and can confirm that it sets "FilePath" to the filename of the EFI binary (e.g. unified-image.efi).
That is, it is not set to NULL.
I have tested such EFI binaries with a variety of bootloaders, and FreeBSD's loader was the first one where I experienced issues.
Since iPXE is a widely used bootloader and simply sets the FilePath to the name of the binary to be loaded, wouldn't it be acceptable for FreeBSD's loader to do the same?
Having said this, the future upstream release versions of the systemd EFI stub will work fine with FreeBSD's loader. If I am not mistaken, it will probably take a longer time for this fix to be incorporated into STABLE GNU/Linux distributions, though I think somebody interested in this novel scenario would be capable of simply extracting the updated EFI stub from something like an ArchLinux package once it arrives in the stable v248 release.
(In reply to schreiberstein from comment #6)
I'll have to take a close look at those other projects. At the time we did boot1.efi (later copied into loader.efi's chain command), it wasn't actually possible for chain booting programs to ensure that this field was set in the downstream booted objects. Whatever it is, it's something subtle and relatively new.