]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/boot/efi/drivers.c
Merge pull request #21653 from yuwata/network-dhcp6pd-unreachable-route-cleanups
[thirdparty/systemd.git] / src / boot / efi / drivers.c
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
9 static void efi_unload_image(EFI_HANDLE *h) {
10 if (*h)
11 (void) BS->UnloadImage(*h);
12 }
13
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: %r", fname);
44
45 err = BS->StartImage(image, NULL, NULL);
46 if (EFI_ERROR(err))
47 return log_error_status_stall(err, L"Failed to start image %s: %r", fname, err);
48
49 TAKE_PTR(image);
50 return EFI_SUCCESS;
51 }
52
53 static EFI_STATUS reconnect(void) {
54 _cleanup_freepool_ EFI_HANDLE *handles = NULL;
55 UINTN n_handles = 0;
56 EFI_STATUS err;
57
58 /* Reconnects all handles, so that any loaded drivers can take effect. */
59
60 err = BS->LocateHandleBuffer(AllHandles, NULL, NULL, &n_handles, &handles);
61 if (EFI_ERROR(err))
62 return log_error_status_stall(err, L"Failed to get list of handles: %r", err);
63
64 for (UINTN i = 0; i < n_handles; i++) {
65 err = BS->ConnectController(handles[i], NULL, NULL, TRUE);
66 if (err == EFI_NOT_FOUND) /* No drivers for this handle */
67 continue;
68 if (EFI_ERROR(err))
69 log_error_status_stall(err, L"Failed to reconnect handle %u, ignoring: %r", i, err);
70 }
71
72 return EFI_SUCCESS;
73 }
74
75 EFI_STATUS load_drivers(
76 EFI_HANDLE parent_image,
77 EFI_LOADED_IMAGE *loaded_image,
78 EFI_FILE_HANDLE root_dir) {
79
80 _cleanup_(FileHandleClosep) EFI_FILE_HANDLE drivers_dir = NULL;
81 _cleanup_freepool_ EFI_FILE_INFO *dirent = NULL;
82 UINTN dirent_size = 0, n_succeeded = 0;
83 EFI_STATUS err;
84
85 err = open_directory(
86 root_dir,
87 L"\\EFI\\systemd\\drivers",
88 &drivers_dir);
89 if (err == EFI_NOT_FOUND)
90 return EFI_SUCCESS;
91 if (EFI_ERROR(err))
92 return log_error_status_stall(err, L"Failed to open \\EFI\\systemd\\drivers: %r", err);
93
94 for (;;) {
95 err = readdir_harder(drivers_dir, &dirent, &dirent_size);
96 if (EFI_ERROR(err))
97 return log_error_status_stall(err, L"Failed to read extra directory of loaded image: %r", err);
98 if (!dirent) /* End of directory */
99 break;
100
101 if (dirent->FileName[0] == '.')
102 continue;
103 if (FLAGS_SET(dirent->Attribute, EFI_FILE_DIRECTORY))
104 continue;
105 if (!endswith_no_case(dirent->FileName, EFI_MACHINE_TYPE_NAME L".efi"))
106 continue;
107
108 err = load_one_driver(parent_image, loaded_image, dirent->FileName);
109 if (EFI_ERROR(err))
110 continue;
111
112 n_succeeded++;
113 }
114
115 if (n_succeeded > 0)
116 (void) reconnect();
117
118 return EFI_SUCCESS;
119 }