]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
boot: automatically load drop-in EFI drivers off the ESP
authorLennart Poettering <lennart@poettering.net>
Tue, 21 Sep 2021 12:47:05 +0000 (14:47 +0200)
committerLennart Poettering <lennart@poettering.net>
Thu, 23 Sep 2021 15:24:10 +0000 (17:24 +0200)
Fixes: #15617
src/boot/bootctl.c
src/boot/efi/boot.c
src/boot/efi/drivers.c [new file with mode: 0644]
src/boot/efi/drivers.h [new file with mode: 0644]
src/boot/efi/meson.build
src/fundamental/efi-loader-features.h

index 65e5262a099b3f32df64c0a720d1bfebde052128..27b0086406c1a4bd1654367accbcc007760d746e 100644 (file)
@@ -1298,6 +1298,7 @@ static int verb_status(int argc, char *argv[], void *userdata) {
                         { EFI_LOADER_FEATURE_ENTRY_ONESHOT,           "One-shot entry control"                },
                         { EFI_LOADER_FEATURE_XBOOTLDR,                "Support for XBOOTLDR partition"        },
                         { EFI_LOADER_FEATURE_RANDOM_SEED,             "Support for passing random seed to OS" },
+                        { EFI_LOADER_FEATURE_LOAD_DRIVER,             "Load drop-in drivers"                  },
                 };
 
                 _cleanup_free_ char *fw_type = NULL, *fw_info = NULL, *loader = NULL, *loader_path = NULL, *stub = NULL;
index 62fc13e150fca44ebe5c5fd1150ce6d9a1e344ba..f1777d8de0b9d3fb4aa32d95ac34af7dedb023f5 100644 (file)
@@ -7,6 +7,7 @@
 #include "console.h"
 #include "devicetree.h"
 #include "disk.h"
+#include "drivers.h"
 #include "efi-loader-features.h"
 #include "graphics.h"
 #include "linux.h"
@@ -2428,6 +2429,7 @@ static VOID export_variables(
                 EFI_LOADER_FEATURE_BOOT_COUNTING |
                 EFI_LOADER_FEATURE_XBOOTLDR |
                 EFI_LOADER_FEATURE_RANDOM_SEED |
+                EFI_LOADER_FEATURE_LOAD_DRIVER |
                 0;
 
         _cleanup_freepool_ CHAR16 *infostr = NULL, *typestr = NULL;
@@ -2538,6 +2540,8 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
                         return log_error_status_stall(err, L"Error installing security policy: %r", err);
         }
 
+        (VOID) load_drivers(image, loaded_image, root_dir);
+
         config_load_all_entries(&config, loaded_image, loaded_image_path, root_dir);
 
         if (config.entry_count == 0) {
diff --git a/src/boot/efi/drivers.c b/src/boot/efi/drivers.c
new file mode 100644 (file)
index 0000000..a876c3d
--- /dev/null
@@ -0,0 +1,150 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <efi.h>
+#include <efilib.h>
+
+#include "drivers.h"
+#include "util.h"
+
+static VOID efi_unload_image(EFI_HANDLE *h) {
+        if (*h)
+                (VOID) uefi_call_wrapper(BS->UnloadImage, 1, *h);
+}
+
+static EFI_STATUS load_one_driver(
+                EFI_HANDLE parent_image,
+                EFI_LOADED_IMAGE *loaded_image,
+                const CHAR16 *fname) {
+
+        _cleanup_(efi_unload_image) EFI_HANDLE image = NULL;
+        _cleanup_freepool_ EFI_DEVICE_PATH *path = NULL;
+        _cleanup_freepool_ CHAR16 *spath = NULL;
+        EFI_STATUS err;
+
+        assert(parent_image);
+        assert(loaded_image);
+        assert(fname);
+
+        spath = PoolPrint(L"\\EFI\\systemd\\drivers\\%s", fname);
+        if (!spath)
+                return log_oom();
+
+        path = FileDevicePath(loaded_image->DeviceHandle, spath);
+        if (!path)
+                return log_oom();
+
+        err = uefi_call_wrapper(
+                        BS->LoadImage, 6,
+                        FALSE,
+                        parent_image,
+                        path,
+                        NULL, 0,
+                        &image);
+        if (EFI_ERROR(err))
+                return log_error_status_stall(err, L"Failed to load image %s: %r", fname, err);
+
+        err = uefi_call_wrapper(
+                        BS->HandleProtocol, 3,
+                        image,
+                        &LoadedImageProtocol,
+                        (VOID **)&loaded_image);
+        if (EFI_ERROR(err))
+                return log_error_status_stall(err, L"Failed to find protocol in driver image s: %r", fname, err);
+
+        if (loaded_image->ImageCodeType != EfiBootServicesCode &&
+            loaded_image->ImageCodeType != EfiRuntimeServicesCode)
+                return log_error_status_stall(EFI_INVALID_PARAMETER, L"Image %s is not a driver, refusing: %r", fname);
+
+        err = uefi_call_wrapper(
+                        BS->StartImage, 3,
+                        image,
+                        NULL,
+                        NULL);
+        if (EFI_ERROR(err))
+                return log_error_status_stall(err, L"Failed to start image %s: %r", fname, err);
+
+        TAKE_PTR(image);
+        return EFI_SUCCESS;
+}
+
+static EFI_STATUS reconnect(VOID) {
+          _cleanup_freepool_ EFI_HANDLE *handles = NULL;
+          UINTN n_handles = 0;
+          EFI_STATUS err;
+
+          /* Reconnects all handles, so that any loaded drivers can take effect. */
+
+          err = uefi_call_wrapper(
+                          BS->LocateHandleBuffer, 5,
+                          AllHandles,
+                          NULL,
+                          NULL,
+                          &n_handles,
+                          &handles);
+          if (EFI_ERROR(err))
+                  return log_error_status_stall(err, L"Failed to get list of handles: %r", err);
+
+          for (UINTN i = 0; i < n_handles; i++) {
+                  err = uefi_call_wrapper(
+                                  BS->ConnectController, 4,
+                                  handles[i],
+                                  NULL,
+                                  NULL,
+                                  TRUE);
+                  if (err == EFI_NOT_FOUND) /* No drivers for this handle */
+                          continue;
+                  if (EFI_ERROR(err))
+                          log_error_status_stall(err, L"Failed to reconnect handle %u, ignoring: %r", i, err);
+          }
+
+          return EFI_SUCCESS;
+}
+
+EFI_STATUS load_drivers(
+                EFI_HANDLE parent_image,
+                EFI_LOADED_IMAGE *loaded_image,
+                EFI_FILE_HANDLE root_dir) {
+
+        _cleanup_(FileHandleClosep) EFI_FILE_HANDLE drivers_dir = NULL;
+        _cleanup_freepool_ EFI_FILE_INFO *dirent = NULL;
+        _cleanup_freepool_ EFI_DEVICE_PATH *path = NULL;
+        UINTN dirent_size = 0, n_succeeded = 0;
+        EFI_STATUS err;
+
+        err = open_directory(
+                        root_dir,
+                        L"\\EFI\\systemd\\drivers",
+                        &drivers_dir);
+        if (err == EFI_NOT_FOUND)
+                return EFI_SUCCESS;
+        if (EFI_ERROR(err))
+                return log_error_status_stall(err, L"Failed to open \\EFI\\systemd\\drivers: %r", err);
+
+        for (;;) {
+                _cleanup_freepool_ CHAR16 *d = NULL;
+
+                err = readdir_harder(drivers_dir, &dirent, &dirent_size);
+                if (EFI_ERROR(err))
+                        return log_error_status_stall(err, L"Failed to read extra directory of loaded image: %r", err);
+                if (!dirent) /* End of directory */
+                        break;
+
+                if (dirent->FileName[0] == '.')
+                        continue;
+                if (dirent->Attribute & EFI_FILE_DIRECTORY)
+                        continue;
+                if (!endswith_no_case(dirent->FileName, EFI_MACHINE_TYPE_NAME L".efi"))
+                        continue;
+
+                err = load_one_driver(parent_image, loaded_image, dirent->FileName);
+                if (EFI_ERROR(err))
+                        continue;
+
+                n_succeeded++;
+        }
+
+        if (n_succeeded > 0)
+                (VOID) reconnect();
+
+        return EFI_SUCCESS;
+}
diff --git a/src/boot/efi/drivers.h b/src/boot/efi/drivers.h
new file mode 100644 (file)
index 0000000..c192c6d
--- /dev/null
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <efi.h>
+
+EFI_STATUS load_drivers(
+                EFI_HANDLE parent_image,
+                EFI_LOADED_IMAGE *loaded_image,
+                EFI_FILE_HANDLE root_dir);
index 3fdabb2073eeecb6e426bc5d971acb7f69083d74..fef8572c6a030620b8145102980ad1b3e2266c0d 100644 (file)
@@ -5,6 +5,7 @@ efi_headers = files('''
         cpio.h
         devicetree.h
         disk.h
+        drivers.h
         graphics.h
         linux.h
         measure.h
@@ -31,6 +32,7 @@ systemd_boot_sources = '''
         boot.c
         console.c
         devicetree.c
+        drivers.c
         random-seed.c
         sha256.c
         shim.c
index f07dacb8593fb2e01842cac82bca2879adaf3772..287ae3c89270958781f9225c0510dbf9f4314110 100644 (file)
@@ -12,3 +12,4 @@
 #define EFI_LOADER_FEATURE_BOOT_COUNTING           (UINT64_C(1) << 4)
 #define EFI_LOADER_FEATURE_XBOOTLDR                (UINT64_C(1) << 5)
 #define EFI_LOADER_FEATURE_RANDOM_SEED             (UINT64_C(1) << 6)
+#define EFI_LOADER_FEATURE_LOAD_DRIVER             (UINT64_C(1) << 7)