]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
boot: Try to load UKI from simple filesystem before LoadImage
authorValentin David <me@valentindavid.com>
Sat, 18 Apr 2026 13:09:00 +0000 (15:09 +0200)
committerLennart Poettering <lennart@amutable.com>
Mon, 4 May 2026 08:03:46 +0000 (10:03 +0200)
When the source buffer is NULL, the firmware is supposed to try to load the UKI
with simple filesystem protocol then load file 2 protocol. But it seems
on some versions of AMI, it does not use simple filesystem protocol,
and then fails to load if the ESP was loaded from an El Torito boot
catalog. Trying to load the source buffer from the simple filesystem protocol
protocols seems work around this limitation.

Shim for example, also loads the source buffer before calling LoadImage. So it
seems to be a safe thing to do. We could also maybe in the future use load file
2 protocol if simple filesystem failed in the first place.

src/boot/shim.c
src/boot/util.c
src/boot/util.h

index 97410d666965949117a2f1c7c4521bfb01777a3d..10a0642ac3b9f611a4f2f6028e4d9126cf063f33 100644 (file)
@@ -8,7 +8,6 @@
  * https://github.com/mjg59/efitools
  */
 
-#include "device-path-util.h"
 #include "efi-efivars.h"
 #include "secure-boot.h"
 #include "shim.h"
@@ -56,24 +55,7 @@ static bool shim_validate(
                 if (!device_path)
                         return false;
 
-                EFI_HANDLE device_handle;
-                EFI_DEVICE_PATH *file_dp = (EFI_DEVICE_PATH *) device_path;
-                err = BS->LocateDevicePath(
-                                MAKE_GUID_PTR(EFI_SIMPLE_FILE_SYSTEM_PROTOCOL), &file_dp, &device_handle);
-                if (err != EFI_SUCCESS)
-                        return false;
-
-                _cleanup_file_close_ EFI_FILE *root = NULL;
-                err = open_volume(device_handle, &root);
-                if (err != EFI_SUCCESS)
-                        return false;
-
-                _cleanup_free_ char16_t *dp_str = NULL;
-                err = device_path_to_str(file_dp, &dp_str);
-                if (err != EFI_SUCCESS)
-                        return false;
-
-                err = file_read(root, dp_str, 0, 0, &file_buffer_owned, &file_size);
+                err = load_file_from_simple_filesystem(device_path, &file_buffer_owned, &file_size);
                 if (err != EFI_SUCCESS)
                         return false;
 
@@ -111,12 +93,21 @@ EFI_STATUS shim_load_image(
         if (have_shim)
                 install_security_override(shim_validate, NULL);
 
+        _cleanup_free_ char *source_buffer = NULL;
+        size_t source_size = 0;
+
+        /* For some AMI firmware, BS->LoadImage() does not read correctly when the file comes the ESP on an
+         * optical drive. But the simple filesystem protocol does work. So we try to load it. If that does
+         * not work, we let BS->LoadImage() try instead.
+         */
+        (void) load_file_from_simple_filesystem(device_path, &source_buffer, &source_size);
+
         EFI_STATUS ret = BS->LoadImage(
                         /* BootPolicy= */ boot_policy,
                         parent,
                         (EFI_DEVICE_PATH *) device_path,
-                        /* SourceBuffer= */ NULL,
-                        /* SourceSize= */ 0,
+                        source_buffer,
+                        source_size,
                         ret_image);
         if (have_shim)
                 uninstall_security_override();
index c40a9aad65b0d663414da10cf95e2b41fe1f921a..22981bc00db1fa6426c943314054d450a1122ae7 100644 (file)
@@ -195,6 +195,32 @@ EFI_STATUS file_read(
         return file_handle_read(handle, offset, size, ret, ret_size);
 }
 
+EFI_STATUS load_file_from_simple_filesystem(const EFI_DEVICE_PATH *device_path, char **file_buffer, size_t *file_size) {
+        EFI_STATUS err;
+        EFI_HANDLE device_handle;
+        EFI_DEVICE_PATH *file_dp = (EFI_DEVICE_PATH *) device_path;
+
+        assert(device_path);
+        assert(file_buffer);
+        assert(file_size);
+
+        err = BS->LocateDevicePath(MAKE_GUID_PTR(EFI_SIMPLE_FILE_SYSTEM_PROTOCOL), &file_dp, &device_handle);
+        if (err != EFI_SUCCESS)
+                return err;
+
+        _cleanup_file_close_ EFI_FILE *root = NULL;
+        err = open_volume(device_handle, &root);
+        if (err != EFI_SUCCESS)
+                return err;
+
+        _cleanup_free_ char16_t *dp_str = NULL;
+        err = device_path_to_str(file_dp, &dp_str);
+        if (err != EFI_SUCCESS)
+                return err;
+
+        return file_read(root, dp_str, 0, 0, file_buffer, file_size);
+}
+
 void set_attribute_safe(size_t attr) {
         /* Various UEFI implementations suppress color changes from a color to the same color. Often, we want
          * to force out the color change though, hence change the color here once, and then back. We simply
index 2c8cc36ea580de5a8ec054c1cf8df8ac403b648b..fa552d6f46c08baf224dfa0d054e9cdb2e744b28 100644 (file)
@@ -138,6 +138,7 @@ char16_t *mangle_stub_cmdline(char16_t *cmdline);
 
 EFI_STATUS chunked_read(EFI_FILE *file, size_t *size, void *buf);
 EFI_STATUS file_read(EFI_FILE *dir, const char16_t *name, uint64_t offset, size_t size, char **ret, size_t *ret_size);
+EFI_STATUS load_file_from_simple_filesystem(const EFI_DEVICE_PATH *device_path, char **file_buffer, size_t *file_size);
 EFI_STATUS file_handle_read(EFI_FILE *handle, uint64_t offset, size_t size, char **ret, size_t *ret_size);
 
 static inline void file_closep(EFI_FILE **handle) {