From: Lennart Poettering Date: Tue, 21 Sep 2021 12:47:05 +0000 (+0200) Subject: boot: automatically load drop-in EFI drivers off the ESP X-Git-Tag: v250-rc1~629^2~12 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=2553a5482c77980429565d14662f309e385bd472;p=thirdparty%2Fsystemd.git boot: automatically load drop-in EFI drivers off the ESP Fixes: #15617 --- diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c index 65e5262a099..27b0086406c 100644 --- a/src/boot/bootctl.c +++ b/src/boot/bootctl.c @@ -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; diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c index 62fc13e150f..f1777d8de0b 100644 --- a/src/boot/efi/boot.c +++ b/src/boot/efi/boot.c @@ -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 index 00000000000..a876c3df7fb --- /dev/null +++ b/src/boot/efi/drivers.c @@ -0,0 +1,150 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include +#include + +#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 index 00000000000..c192c6d44c9 --- /dev/null +++ b/src/boot/efi/drivers.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include + +EFI_STATUS load_drivers( + EFI_HANDLE parent_image, + EFI_LOADED_IMAGE *loaded_image, + EFI_FILE_HANDLE root_dir); diff --git a/src/boot/efi/meson.build b/src/boot/efi/meson.build index 3fdabb2073e..fef8572c6a0 100644 --- a/src/boot/efi/meson.build +++ b/src/boot/efi/meson.build @@ -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 diff --git a/src/fundamental/efi-loader-features.h b/src/fundamental/efi-loader-features.h index f07dacb8593..287ae3c8927 100644 --- a/src/fundamental/efi-loader-features.h +++ b/src/fundamental/efi-loader-features.h @@ -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)