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