]>
Commit | Line | Data |
---|---|---|
2553a548 LP |
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 | ||
2553a548 LP |
9 | static EFI_STATUS load_one_driver( |
10 | EFI_HANDLE parent_image, | |
b30a43df | 11 | EFI_LOADED_IMAGE_PROTOCOL *loaded_image, |
3639d1b0 | 12 | const char16_t *fname) { |
2553a548 | 13 | |
f386daa0 | 14 | _cleanup_(unload_imagep) EFI_HANDLE image = NULL; |
93521e55 | 15 | _cleanup_free_ EFI_DEVICE_PATH *path = NULL; |
3639d1b0 | 16 | _cleanup_free_ char16_t *spath = NULL; |
2553a548 LP |
17 | EFI_STATUS err; |
18 | ||
19 | assert(parent_image); | |
20 | assert(loaded_image); | |
21 | assert(fname); | |
22 | ||
0a15a824 | 23 | spath = xpool_print(L"\\EFI\\systemd\\drivers\\%s", fname); |
79a2b916 JJ |
24 | err = make_file_device_path(loaded_image->DeviceHandle, spath, &path); |
25 | if (err != EFI_SUCCESS) | |
26 | return log_error_status_stall(err, L"Error making file device path: %r", err); | |
2553a548 | 27 | |
e5a1b8f9 | 28 | err = BS->LoadImage(false, parent_image, path, NULL, 0, &image); |
2a5e4fe4 | 29 | if (err != EFI_SUCCESS) |
2553a548 LP |
30 | return log_error_status_stall(err, L"Failed to load image %s: %r", fname, err); |
31 | ||
12f32748 | 32 | err = BS->HandleProtocol(image, &LoadedImageProtocol, (void **)&loaded_image); |
2a5e4fe4 | 33 | if (err != EFI_SUCCESS) |
ce5e7872 | 34 | return log_error_status_stall(err, L"Failed to find protocol in driver image %s: %r", fname, err); |
2553a548 LP |
35 | |
36 | if (loaded_image->ImageCodeType != EfiBootServicesCode && | |
37 | loaded_image->ImageCodeType != EfiRuntimeServicesCode) | |
ce5e7872 | 38 | return log_error_status_stall(EFI_INVALID_PARAMETER, L"Image %s is not a driver, refusing.", fname); |
2553a548 | 39 | |
12f32748 | 40 | err = BS->StartImage(image, NULL, NULL); |
2a5e4fe4 | 41 | if (err != EFI_SUCCESS) { |
8fb16fee JJ |
42 | /* EFI_ABORTED signals an initializing driver. It uses this error code on success |
43 | * so that it is unloaded after. */ | |
44 | if (err != EFI_ABORTED) | |
45 | log_error_stall(L"Failed to start image %s: %r", fname, err); | |
46 | return err; | |
47 | } | |
2553a548 LP |
48 | |
49 | TAKE_PTR(image); | |
50 | return EFI_SUCCESS; | |
51 | } | |
52 | ||
5b3e33c2 | 53 | EFI_STATUS reconnect_all_drivers(void) { |
98ac5192 JJ |
54 | _cleanup_free_ EFI_HANDLE *handles = NULL; |
55 | size_t n_handles = 0; | |
56 | EFI_STATUS err; | |
2553a548 | 57 | |
98ac5192 | 58 | /* Reconnects all handles, so that any loaded drivers can take effect. */ |
2553a548 | 59 | |
98ac5192 JJ |
60 | err = BS->LocateHandleBuffer(AllHandles, NULL, NULL, &n_handles, &handles); |
61 | if (err != EFI_SUCCESS) | |
62 | return log_error_status_stall(err, L"Failed to get list of handles: %r", err); | |
2553a548 | 63 | |
98ac5192 JJ |
64 | for (size_t i = 0; i < n_handles; i++) |
65 | /* Some firmware gives us some bogus handles (or they might become bad due to | |
66 | * reconnecting everything). Security policy may also prevent us from doing so too. | |
67 | * There is nothing we can realistically do on errors anyways, so just ignore them. */ | |
68 | (void) BS->ConnectController(handles[i], NULL, NULL, true); | |
2553a548 | 69 | |
98ac5192 | 70 | return EFI_SUCCESS; |
2553a548 LP |
71 | } |
72 | ||
73 | EFI_STATUS load_drivers( | |
74 | EFI_HANDLE parent_image, | |
b30a43df | 75 | EFI_LOADED_IMAGE_PROTOCOL *loaded_image, |
85eb489e | 76 | EFI_FILE *root_dir) { |
2553a548 | 77 | |
85eb489e | 78 | _cleanup_(file_closep) EFI_FILE *drivers_dir = NULL; |
93521e55 | 79 | _cleanup_free_ EFI_FILE_INFO *dirent = NULL; |
2553a548 LP |
80 | UINTN dirent_size = 0, n_succeeded = 0; |
81 | EFI_STATUS err; | |
82 | ||
83 | err = open_directory( | |
84 | root_dir, | |
85 | L"\\EFI\\systemd\\drivers", | |
86 | &drivers_dir); | |
87 | if (err == EFI_NOT_FOUND) | |
88 | return EFI_SUCCESS; | |
2a5e4fe4 | 89 | if (err != EFI_SUCCESS) |
2553a548 LP |
90 | return log_error_status_stall(err, L"Failed to open \\EFI\\systemd\\drivers: %r", err); |
91 | ||
92 | for (;;) { | |
2553a548 | 93 | err = readdir_harder(drivers_dir, &dirent, &dirent_size); |
2a5e4fe4 | 94 | if (err != EFI_SUCCESS) |
2553a548 LP |
95 | return log_error_status_stall(err, L"Failed to read extra directory of loaded image: %r", err); |
96 | if (!dirent) /* End of directory */ | |
97 | break; | |
98 | ||
99 | if (dirent->FileName[0] == '.') | |
100 | continue; | |
785b5fcf | 101 | if (FLAGS_SET(dirent->Attribute, EFI_FILE_DIRECTORY)) |
2553a548 LP |
102 | continue; |
103 | if (!endswith_no_case(dirent->FileName, EFI_MACHINE_TYPE_NAME L".efi")) | |
104 | continue; | |
105 | ||
106 | err = load_one_driver(parent_image, loaded_image, dirent->FileName); | |
2a5e4fe4 | 107 | if (err != EFI_SUCCESS) |
2553a548 LP |
108 | continue; |
109 | ||
110 | n_succeeded++; | |
111 | } | |
112 | ||
113 | if (n_succeeded > 0) | |
5b3e33c2 | 114 | (void) reconnect_all_drivers(); |
2553a548 LP |
115 | |
116 | return EFI_SUCCESS; | |
117 | } |