1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <efi.h>
4 #include <efilib.h>
5
6 #include "drivers.h"
7 #include "util.h"
8
efi_unload_image(EFI_HANDLE * h)9 static void efi_unload_image(EFI_HANDLE *h) {
10 if (*h)
11 (void) BS->UnloadImage(*h);
12 }
13
load_one_driver(EFI_HANDLE parent_image,EFI_LOADED_IMAGE * loaded_image,const CHAR16 * fname)14 static EFI_STATUS load_one_driver(
15 EFI_HANDLE parent_image,
16 EFI_LOADED_IMAGE *loaded_image,
17 const CHAR16 *fname) {
18
19 _cleanup_(efi_unload_image) EFI_HANDLE image = NULL;
20 _cleanup_freepool_ EFI_DEVICE_PATH *path = NULL;
21 _cleanup_freepool_ CHAR16 *spath = NULL;
22 EFI_STATUS err;
23
24 assert(parent_image);
25 assert(loaded_image);
26 assert(fname);
27
28 spath = xpool_print(L"\\EFI\\systemd\\drivers\\%s", fname);
29 path = FileDevicePath(loaded_image->DeviceHandle, spath);
30 if (!path)
31 return log_oom();
32
33 err = BS->LoadImage(FALSE, parent_image, path, NULL, 0, &image);
34 if (EFI_ERROR(err))
35 return log_error_status_stall(err, L"Failed to load image %s: %r", fname, err);
36
37 err = BS->HandleProtocol(image, &LoadedImageProtocol, (void **)&loaded_image);
38 if (EFI_ERROR(err))
39 return log_error_status_stall(err, L"Failed to find protocol in driver image %s: %r", fname, err);
40
41 if (loaded_image->ImageCodeType != EfiBootServicesCode &&
42 loaded_image->ImageCodeType != EfiRuntimeServicesCode)
43 return log_error_status_stall(EFI_INVALID_PARAMETER, L"Image %s is not a driver, refusing.", fname);
44
45 err = BS->StartImage(image, NULL, NULL);
46 if (EFI_ERROR(err)) {
47 /* EFI_ABORTED signals an initializing driver. It uses this error code on success
48 * so that it is unloaded after. */
49 if (err != EFI_ABORTED)
50 log_error_stall(L"Failed to start image %s: %r", fname, err);
51 return err;
52 }
53
54 TAKE_PTR(image);
55 return EFI_SUCCESS;
56 }
57
reconnect(void)58 static EFI_STATUS reconnect(void) {
59 _cleanup_freepool_ EFI_HANDLE *handles = NULL;
60 UINTN n_handles = 0;
61 EFI_STATUS err;
62
63 /* Reconnects all handles, so that any loaded drivers can take effect. */
64
65 err = BS->LocateHandleBuffer(AllHandles, NULL, NULL, &n_handles, &handles);
66 if (EFI_ERROR(err))
67 return log_error_status_stall(err, L"Failed to get list of handles: %r", err);
68
69 for (UINTN i = 0; i < n_handles; i++) {
70 err = BS->ConnectController(handles[i], NULL, NULL, TRUE);
71 if (err == EFI_NOT_FOUND) /* No drivers for this handle */
72 continue;
73 if (EFI_ERROR(err))
74 log_error_status_stall(err, L"Failed to reconnect handle %" PRIuN L", ignoring: %r", i, err);
75 }
76
77 return EFI_SUCCESS;
78 }
79
load_drivers(EFI_HANDLE parent_image,EFI_LOADED_IMAGE * loaded_image,EFI_FILE * root_dir)80 EFI_STATUS load_drivers(
81 EFI_HANDLE parent_image,
82 EFI_LOADED_IMAGE *loaded_image,
83 EFI_FILE *root_dir) {
84
85 _cleanup_(file_closep) EFI_FILE *drivers_dir = NULL;
86 _cleanup_freepool_ EFI_FILE_INFO *dirent = NULL;
87 UINTN dirent_size = 0, n_succeeded = 0;
88 EFI_STATUS err;
89
90 err = open_directory(
91 root_dir,
92 L"\\EFI\\systemd\\drivers",
93 &drivers_dir);
94 if (err == EFI_NOT_FOUND)
95 return EFI_SUCCESS;
96 if (EFI_ERROR(err))
97 return log_error_status_stall(err, L"Failed to open \\EFI\\systemd\\drivers: %r", err);
98
99 for (;;) {
100 err = readdir_harder(drivers_dir, &dirent, &dirent_size);
101 if (EFI_ERROR(err))
102 return log_error_status_stall(err, L"Failed to read extra directory of loaded image: %r", err);
103 if (!dirent) /* End of directory */
104 break;
105
106 if (dirent->FileName[0] == '.')
107 continue;
108 if (FLAGS_SET(dirent->Attribute, EFI_FILE_DIRECTORY))
109 continue;
110 if (!endswith_no_case(dirent->FileName, EFI_MACHINE_TYPE_NAME L".efi"))
111 continue;
112
113 err = load_one_driver(parent_image, loaded_image, dirent->FileName);
114 if (EFI_ERROR(err))
115 continue;
116
117 n_succeeded++;
118 }
119
120 if (n_succeeded > 0)
121 (void) reconnect();
122
123 return EFI_SUCCESS;
124 }
125