summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Brown <mcb30@ipxe.org>2023-02-28 12:04:58 +0000
committerMichael Brown <mcb30@ipxe.org>2023-02-28 12:30:54 +0000
commit04e60a278abcda47301f6be2c23755e5e1004661 (patch)
tree1dbbe4fd7dc600b792c1b9eb011d1c8d6ec86726
parent471599dc7721d454b6658062c901b52038a78be2 (diff)
downloadqemu-ipxe-04e60a278abcda47301f6be2c23755e5e1004661.tar.gz
[efi] Omit EFI_LOAD_FILE2_PROTOCOL for a zero-length initrdnoinitrd
When the Linux kernel is being used with no initrd, iPXE will still provide a zero-length initrd.magic file within the virtual filesystem. As of commit 6a004be ("[efi] Support the initrd autodetection mechanism in newer Linux kernels"), this zero-length file will also be exposed via an EFI_LOAD_FILE2_PROTOCOL instance on a handle with a fixed device path. The correct handling of zero-length files via EFI_LOAD_FILE2_PROTOCOL is unfortunately not well defined. Linux expects the first call to LoadFile() to always fail with EFI_BUFFER_TOO_SMALL. When the initrd is genuinely zero-length, iPXE will return success since the buffer is not too small to hold the (zero-length) file. This causes Linux to immediately report a spurious EFI_LOAD_ERROR boot failure. We could change the logic in iPXE's efi_file_load() to always return EFI_BUFFER_TOO_SMALL if Buffer is NULL on entry. Since the correct behaviour of LoadFile() in the corner case of a zero-length file is left undefined by the UEFI specification, this would be permissible. Unfortunately this approach would not fix the problem. If we return EFI_BUFFER_TOO_SMALL and set the file length to zero, then Linux will call the boot services AllocatePages() method with a zero length. In at least the EDK2 implementation, this combination of parameters will cause AllocatePages() to return EFI_OUT_OF_RESOURCES, and Linux will again report a boot failure. Another approach would be to install the initrd device path handle only if we have a non-empty initrd to offer. Unfortunately this would lead to a failure in yet another corner case: if a previous bootloader has installed an initrd device path handle (e.g. to pass a boot script to iPXE) then we must not leave that initrd in place, since then our loaded kernel would end up seeing the wrong initrd content. The cleanest fix seems to be to ensure that the initrd device path handle is installed with the EFI_DEVICE_PATH_PROTOCOL instance present but with the EFI_LOAD_FILE2_PROTOCOL instance absent (and forcibly uninstalled if necessary), matching the state in which we leave the handle after uninstalling our virtual filesystem. Linux will then not find any handle that supports EFI_LOAD_FILE2_PROTOCOL within the fixed device path, and so will fall through to trying other mechanisms to locate the initrd. Reported-by: Chris Bradshaw <cwbshaw@gmail.com> Signed-off-by: Michael Brown <mcb30@ipxe.org>
-rw-r--r--src/interface/efi/efi_file.c13
1 files changed, 11 insertions, 2 deletions
diff --git a/src/interface/efi/efi_file.c b/src/interface/efi/efi_file.c
index e8debbbe..7bcf8d59 100644
--- a/src/interface/efi/efi_file.c
+++ b/src/interface/efi/efi_file.c
@@ -1020,6 +1020,7 @@ static int efi_file_path_install ( EFI_DEVICE_PATH_PROTOCOL *path,
*/
int efi_file_install ( EFI_HANDLE handle ) {
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+ EFI_LOAD_FILE2_PROTOCOL *load;
union {
EFI_DISK_IO_PROTOCOL *diskio;
void *interface;
@@ -1082,9 +1083,17 @@ int efi_file_install ( EFI_HANDLE handle ) {
}
assert ( diskio.diskio == &efi_disk_io_protocol );
- /* Install Linux initrd fixed device path file */
+ /* Install Linux initrd fixed device path file
+ *
+ * Install the device path handle unconditionally, since we
+ * are definitively the bootloader providing the initrd, if
+ * any, to the booted image. Install the load file protocol
+ * instance only if the initrd is non-empty, since Linux does
+ * not gracefully handle a zero-length initrd.
+ */
+ load = ( list_is_singular ( &images ) ? NULL : &efi_file_initrd.load );
if ( ( rc = efi_file_path_install ( &efi_file_initrd_path.vendor.Header,
- &efi_file_initrd.load ) ) != 0 ) {
+ load ) ) != 0 ) {
goto err_initrd;
}