1 | /** @file
|
---|
2 | Implement defer image load services for user identification in UEFI2.2.
|
---|
3 |
|
---|
4 | Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>
|
---|
5 | SPDX-License-Identifier: BSD-2-Clause-Patent
|
---|
6 |
|
---|
7 | **/
|
---|
8 | #include "Defer3rdPartyImageLoad.h"
|
---|
9 |
|
---|
10 | //
|
---|
11 | // The structure to save the deferred 3rd party image information.
|
---|
12 | //
|
---|
13 | typedef struct {
|
---|
14 | EFI_DEVICE_PATH_PROTOCOL *ImageDevicePath;
|
---|
15 | BOOLEAN BootOption;
|
---|
16 | BOOLEAN Loaded;
|
---|
17 | } DEFERRED_3RD_PARTY_IMAGE_INFO;
|
---|
18 |
|
---|
19 | //
|
---|
20 | // The table to save the deferred 3rd party image item.
|
---|
21 | //
|
---|
22 | typedef struct {
|
---|
23 | UINTN Count; ///< deferred 3rd party image count
|
---|
24 | DEFERRED_3RD_PARTY_IMAGE_INFO *ImageInfo; ///< deferred 3rd party image item
|
---|
25 | } DEFERRED_3RD_PARTY_IMAGE_TABLE;
|
---|
26 |
|
---|
27 | BOOLEAN mImageLoadedAfterEndOfDxe = FALSE;
|
---|
28 | BOOLEAN mEndOfDxe = FALSE;
|
---|
29 | DEFERRED_3RD_PARTY_IMAGE_TABLE mDeferred3rdPartyImage = {
|
---|
30 | 0, // Deferred image count
|
---|
31 | NULL // The deferred image info
|
---|
32 | };
|
---|
33 |
|
---|
34 | EFI_DEFERRED_IMAGE_LOAD_PROTOCOL mDeferredImageLoad = {
|
---|
35 | GetDefferedImageInfo
|
---|
36 | };
|
---|
37 |
|
---|
38 | /**
|
---|
39 | Return whether the file comes from FV.
|
---|
40 |
|
---|
41 | @param[in] File This is a pointer to the device path of the file
|
---|
42 | that is being dispatched.
|
---|
43 |
|
---|
44 | @retval TRUE File comes from FV.
|
---|
45 | @retval FALSE File doesn't come from FV.
|
---|
46 | **/
|
---|
47 | BOOLEAN
|
---|
48 | FileFromFv (
|
---|
49 | IN CONST EFI_DEVICE_PATH_PROTOCOL *File
|
---|
50 | )
|
---|
51 | {
|
---|
52 | EFI_STATUS Status;
|
---|
53 | EFI_HANDLE DeviceHandle;
|
---|
54 | EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;
|
---|
55 |
|
---|
56 | //
|
---|
57 | // First check to see if File is from a Firmware Volume
|
---|
58 | //
|
---|
59 | DeviceHandle = NULL;
|
---|
60 | TempDevicePath = (EFI_DEVICE_PATH_PROTOCOL *)File;
|
---|
61 | Status = gBS->LocateDevicePath (
|
---|
62 | &gEfiFirmwareVolume2ProtocolGuid,
|
---|
63 | &TempDevicePath,
|
---|
64 | &DeviceHandle
|
---|
65 | );
|
---|
66 | if (!EFI_ERROR (Status)) {
|
---|
67 | Status = gBS->OpenProtocol (
|
---|
68 | DeviceHandle,
|
---|
69 | &gEfiFirmwareVolume2ProtocolGuid,
|
---|
70 | NULL,
|
---|
71 | NULL,
|
---|
72 | NULL,
|
---|
73 | EFI_OPEN_PROTOCOL_TEST_PROTOCOL
|
---|
74 | );
|
---|
75 | if (!EFI_ERROR (Status)) {
|
---|
76 | return TRUE;
|
---|
77 | }
|
---|
78 | }
|
---|
79 |
|
---|
80 | return FALSE;
|
---|
81 | }
|
---|
82 |
|
---|
83 | /**
|
---|
84 | Find the deferred image which matches the device path.
|
---|
85 |
|
---|
86 | @param[in] ImageDevicePath A pointer to the device path of a image.
|
---|
87 | @param[in] BootOption Whether the image is a boot option.
|
---|
88 |
|
---|
89 | @return Pointer to the found deferred image or NULL if not found.
|
---|
90 | **/
|
---|
91 | DEFERRED_3RD_PARTY_IMAGE_INFO *
|
---|
92 | LookupImage (
|
---|
93 | IN CONST EFI_DEVICE_PATH_PROTOCOL *ImageDevicePath,
|
---|
94 | IN BOOLEAN BootOption
|
---|
95 | )
|
---|
96 | {
|
---|
97 | UINTN Index;
|
---|
98 | UINTN DevicePathSize;
|
---|
99 |
|
---|
100 | DevicePathSize = GetDevicePathSize (ImageDevicePath);
|
---|
101 |
|
---|
102 | for (Index = 0; Index < mDeferred3rdPartyImage.Count; Index++) {
|
---|
103 | if (CompareMem (ImageDevicePath, mDeferred3rdPartyImage.ImageInfo[Index].ImageDevicePath, DevicePathSize) == 0) {
|
---|
104 | ASSERT (mDeferred3rdPartyImage.ImageInfo[Index].BootOption == BootOption);
|
---|
105 | return &mDeferred3rdPartyImage.ImageInfo[Index];
|
---|
106 | }
|
---|
107 | }
|
---|
108 |
|
---|
109 | return NULL;
|
---|
110 | }
|
---|
111 |
|
---|
112 | /**
|
---|
113 | Add the image info to a deferred image list.
|
---|
114 |
|
---|
115 | @param[in] ImageDevicePath A pointer to the device path of a image.
|
---|
116 | @param[in] BootOption Whether the image is a boot option.
|
---|
117 |
|
---|
118 | **/
|
---|
119 | VOID
|
---|
120 | QueueImage (
|
---|
121 | IN CONST EFI_DEVICE_PATH_PROTOCOL *ImageDevicePath,
|
---|
122 | IN BOOLEAN BootOption
|
---|
123 | )
|
---|
124 | {
|
---|
125 | DEFERRED_3RD_PARTY_IMAGE_INFO *ImageInfo;
|
---|
126 |
|
---|
127 | //
|
---|
128 | // Expand memory for the new deferred image.
|
---|
129 | //
|
---|
130 | ImageInfo = ReallocatePool (
|
---|
131 | mDeferred3rdPartyImage.Count * sizeof (DEFERRED_3RD_PARTY_IMAGE_INFO),
|
---|
132 | (mDeferred3rdPartyImage.Count + 1) * sizeof (DEFERRED_3RD_PARTY_IMAGE_INFO),
|
---|
133 | mDeferred3rdPartyImage.ImageInfo
|
---|
134 | );
|
---|
135 | if (ImageInfo == NULL) {
|
---|
136 | return;
|
---|
137 | }
|
---|
138 |
|
---|
139 | mDeferred3rdPartyImage.ImageInfo = ImageInfo;
|
---|
140 |
|
---|
141 | //
|
---|
142 | // Save the deferred image information.
|
---|
143 | //
|
---|
144 | ImageInfo = &mDeferred3rdPartyImage.ImageInfo[mDeferred3rdPartyImage.Count];
|
---|
145 | ImageInfo->ImageDevicePath = DuplicateDevicePath (ImageDevicePath);
|
---|
146 | if (ImageInfo->ImageDevicePath == NULL) {
|
---|
147 | return;
|
---|
148 | }
|
---|
149 |
|
---|
150 | ImageInfo->BootOption = BootOption;
|
---|
151 | ImageInfo->Loaded = FALSE;
|
---|
152 | mDeferred3rdPartyImage.Count++;
|
---|
153 | }
|
---|
154 |
|
---|
155 | /**
|
---|
156 | Returns information about a deferred image.
|
---|
157 |
|
---|
158 | This function returns information about a single deferred image. The deferred images are
|
---|
159 | numbered consecutively, starting with 0. If there is no image which corresponds to
|
---|
160 | ImageIndex, then EFI_NOT_FOUND is returned. All deferred images may be returned by
|
---|
161 | iteratively calling this function until EFI_NOT_FOUND is returned.
|
---|
162 | Image may be NULL and ImageSize set to 0 if the decision to defer execution was made
|
---|
163 | because of the location of the executable image, rather than its actual contents.
|
---|
164 |
|
---|
165 | @param[in] This Points to this instance of the EFI_DEFERRED_IMAGE_LOAD_PROTOCOL.
|
---|
166 | @param[in] ImageIndex Zero-based index of the deferred index.
|
---|
167 | @param[out] ImageDevicePath On return, points to a pointer to the device path of the image.
|
---|
168 | The device path should not be freed by the caller.
|
---|
169 | @param[out] Image On return, points to the first byte of the image or NULL if the
|
---|
170 | image is not available. The image should not be freed by the caller
|
---|
171 | unless LoadImage() has been successfully called.
|
---|
172 | @param[out] ImageSize On return, the size of the image, or 0 if the image is not available.
|
---|
173 | @param[out] BootOption On return, points to TRUE if the image was intended as a boot option
|
---|
174 | or FALSE if it was not intended as a boot option.
|
---|
175 |
|
---|
176 | @retval EFI_SUCCESS Image information returned successfully.
|
---|
177 | @retval EFI_NOT_FOUND ImageIndex does not refer to a valid image.
|
---|
178 | @retval EFI_INVALID_PARAMETER ImageDevicePath is NULL or Image is NULL or ImageSize is NULL or
|
---|
179 | BootOption is NULL.
|
---|
180 |
|
---|
181 | **/
|
---|
182 | EFI_STATUS
|
---|
183 | EFIAPI
|
---|
184 | GetDefferedImageInfo (
|
---|
185 | IN EFI_DEFERRED_IMAGE_LOAD_PROTOCOL *This,
|
---|
186 | IN UINTN ImageIndex,
|
---|
187 | OUT EFI_DEVICE_PATH_PROTOCOL **ImageDevicePath,
|
---|
188 | OUT VOID **Image,
|
---|
189 | OUT UINTN *ImageSize,
|
---|
190 | OUT BOOLEAN *BootOption
|
---|
191 | )
|
---|
192 | {
|
---|
193 | UINTN Index;
|
---|
194 | UINTN NewCount;
|
---|
195 |
|
---|
196 | if ((This == NULL) || (ImageSize == NULL) || (Image == NULL)) {
|
---|
197 | return EFI_INVALID_PARAMETER;
|
---|
198 | }
|
---|
199 |
|
---|
200 | if ((ImageDevicePath == NULL) || (BootOption == NULL)) {
|
---|
201 | return EFI_INVALID_PARAMETER;
|
---|
202 | }
|
---|
203 |
|
---|
204 | //
|
---|
205 | // Remove the loaded images from the defer list in the first call.
|
---|
206 | //
|
---|
207 | if (ImageIndex == 0) {
|
---|
208 | NewCount = 0;
|
---|
209 | for (Index = 0; Index < mDeferred3rdPartyImage.Count; Index++) {
|
---|
210 | if (!mDeferred3rdPartyImage.ImageInfo[Index].Loaded) {
|
---|
211 | CopyMem (
|
---|
212 | &mDeferred3rdPartyImage.ImageInfo[NewCount],
|
---|
213 | &mDeferred3rdPartyImage.ImageInfo[Index],
|
---|
214 | sizeof (DEFERRED_3RD_PARTY_IMAGE_INFO)
|
---|
215 | );
|
---|
216 | NewCount++;
|
---|
217 | }
|
---|
218 | }
|
---|
219 |
|
---|
220 | mDeferred3rdPartyImage.Count = NewCount;
|
---|
221 | }
|
---|
222 |
|
---|
223 | if (ImageIndex >= mDeferred3rdPartyImage.Count) {
|
---|
224 | return EFI_NOT_FOUND;
|
---|
225 | }
|
---|
226 |
|
---|
227 | //
|
---|
228 | // Get the request deferred image.
|
---|
229 | //
|
---|
230 | *ImageDevicePath = mDeferred3rdPartyImage.ImageInfo[ImageIndex].ImageDevicePath;
|
---|
231 | *BootOption = mDeferred3rdPartyImage.ImageInfo[ImageIndex].BootOption;
|
---|
232 | *Image = NULL;
|
---|
233 | *ImageSize = 0;
|
---|
234 |
|
---|
235 | return EFI_SUCCESS;
|
---|
236 | }
|
---|
237 |
|
---|
238 | /**
|
---|
239 | Callback function executed when the EndOfDxe event group is signaled.
|
---|
240 |
|
---|
241 | @param[in] Event Event whose notification function is being invoked.
|
---|
242 | @param[in] Context The pointer to the notification function's context, which
|
---|
243 | is implementation-dependent.
|
---|
244 | **/
|
---|
245 | VOID
|
---|
246 | EFIAPI
|
---|
247 | EndOfDxe (
|
---|
248 | IN EFI_EVENT Event,
|
---|
249 | IN VOID *Context
|
---|
250 | )
|
---|
251 | {
|
---|
252 | mEndOfDxe = TRUE;
|
---|
253 | }
|
---|
254 |
|
---|
255 | /**
|
---|
256 | Event notification for gEfiDxeSmmReadyToLockProtocolGuid event.
|
---|
257 |
|
---|
258 | This function reports failure if any deferred image is loaded before
|
---|
259 | this callback.
|
---|
260 | Platform should publish ReadyToLock protocol immediately after signaling
|
---|
261 | of the End of DXE Event.
|
---|
262 |
|
---|
263 | @param Event The Event that is being processed, not used.
|
---|
264 | @param Context Event Context, not used.
|
---|
265 |
|
---|
266 | **/
|
---|
267 | VOID
|
---|
268 | EFIAPI
|
---|
269 | DxeSmmReadyToLock (
|
---|
270 | IN EFI_EVENT Event,
|
---|
271 | IN VOID *Context
|
---|
272 | )
|
---|
273 | {
|
---|
274 | EFI_STATUS Status;
|
---|
275 | VOID *Interface;
|
---|
276 |
|
---|
277 | Status = gBS->LocateProtocol (&gEfiDxeSmmReadyToLockProtocolGuid, NULL, &Interface);
|
---|
278 | if (EFI_ERROR (Status)) {
|
---|
279 | return;
|
---|
280 | }
|
---|
281 |
|
---|
282 | gBS->CloseEvent (Event);
|
---|
283 |
|
---|
284 | if (mImageLoadedAfterEndOfDxe) {
|
---|
285 | //
|
---|
286 | // Platform should not dispatch the 3rd party images after signaling EndOfDxe event
|
---|
287 | // but before publishing DxeSmmReadyToLock protocol.
|
---|
288 | //
|
---|
289 | DEBUG ((
|
---|
290 | DEBUG_ERROR,
|
---|
291 | "[Security] 3rd party images must be dispatched after DxeSmmReadyToLock Protocol installation!\n"
|
---|
292 | ));
|
---|
293 | REPORT_STATUS_CODE (
|
---|
294 | EFI_ERROR_CODE | EFI_ERROR_UNRECOVERED,
|
---|
295 | (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_EC_ILLEGAL_SOFTWARE_STATE)
|
---|
296 | );
|
---|
297 | ASSERT (FALSE);
|
---|
298 | CpuDeadLoop ();
|
---|
299 | }
|
---|
300 | }
|
---|
301 |
|
---|
302 | /**
|
---|
303 | Defer the 3rd party image load and installs Deferred Image Load Protocol.
|
---|
304 |
|
---|
305 | @param[in] File This is a pointer to the device path of the file that
|
---|
306 | is being dispatched. This will optionally be used for
|
---|
307 | logging.
|
---|
308 | @param[in] BootPolicy A boot policy that was used to call LoadImage() UEFI service.
|
---|
309 |
|
---|
310 | @retval EFI_SUCCESS The file is not 3rd party image and can be loaded immediately.
|
---|
311 | @retval EFI_ACCESS_DENIED The file is 3rd party image and needs deferred.
|
---|
312 | **/
|
---|
313 | EFI_STATUS
|
---|
314 | Defer3rdPartyImageLoad (
|
---|
315 | IN CONST EFI_DEVICE_PATH_PROTOCOL *File,
|
---|
316 | IN BOOLEAN BootPolicy
|
---|
317 | )
|
---|
318 | {
|
---|
319 | DEFERRED_3RD_PARTY_IMAGE_INFO *ImageInfo;
|
---|
320 |
|
---|
321 | //
|
---|
322 | // Ignore if File is NULL.
|
---|
323 | //
|
---|
324 | if (File == NULL) {
|
---|
325 | return EFI_SUCCESS;
|
---|
326 | }
|
---|
327 |
|
---|
328 | if (FileFromFv (File)) {
|
---|
329 | return EFI_SUCCESS;
|
---|
330 | }
|
---|
331 |
|
---|
332 | ImageInfo = LookupImage (File, BootPolicy);
|
---|
333 |
|
---|
334 | DEBUG_CODE_BEGIN ();
|
---|
335 | CHAR16 *DevicePathStr;
|
---|
336 |
|
---|
337 | DevicePathStr = ConvertDevicePathToText (File, FALSE, FALSE);
|
---|
338 | DEBUG ((
|
---|
339 | DEBUG_INFO,
|
---|
340 | "[Security] 3rd party image[%p] %s EndOfDxe: %s.\n",
|
---|
341 | ImageInfo,
|
---|
342 | mEndOfDxe ? L"can be loaded after" : L"is deferred to load before",
|
---|
343 | DevicePathStr
|
---|
344 | ));
|
---|
345 | if (DevicePathStr != NULL) {
|
---|
346 | FreePool (DevicePathStr);
|
---|
347 | }
|
---|
348 |
|
---|
349 | DEBUG_CODE_END ();
|
---|
350 |
|
---|
351 | if (mEndOfDxe) {
|
---|
352 | mImageLoadedAfterEndOfDxe = TRUE;
|
---|
353 | //
|
---|
354 | // The image might be first time loaded after EndOfDxe,
|
---|
355 | // So ImageInfo can be NULL.
|
---|
356 | //
|
---|
357 | if (ImageInfo != NULL) {
|
---|
358 | ImageInfo->Loaded = TRUE;
|
---|
359 | }
|
---|
360 |
|
---|
361 | return EFI_SUCCESS;
|
---|
362 | } else {
|
---|
363 | //
|
---|
364 | // The image might be second time loaded before EndOfDxe,
|
---|
365 | // So ImageInfo can be non-NULL.
|
---|
366 | //
|
---|
367 | if (ImageInfo == NULL) {
|
---|
368 | QueueImage (File, BootPolicy);
|
---|
369 | }
|
---|
370 |
|
---|
371 | return EFI_ACCESS_DENIED;
|
---|
372 | }
|
---|
373 | }
|
---|
374 |
|
---|
375 | /**
|
---|
376 | Installs DeferredImageLoad Protocol and listens EndOfDxe event.
|
---|
377 | **/
|
---|
378 | VOID
|
---|
379 | Defer3rdPartyImageLoadInitialize (
|
---|
380 | VOID
|
---|
381 | )
|
---|
382 | {
|
---|
383 | EFI_STATUS Status;
|
---|
384 | EFI_HANDLE Handle;
|
---|
385 | EFI_EVENT Event;
|
---|
386 | VOID *Registration;
|
---|
387 |
|
---|
388 | Handle = NULL;
|
---|
389 | Status = gBS->InstallMultipleProtocolInterfaces (
|
---|
390 | &Handle,
|
---|
391 | &gEfiDeferredImageLoadProtocolGuid,
|
---|
392 | &mDeferredImageLoad,
|
---|
393 | NULL
|
---|
394 | );
|
---|
395 | ASSERT_EFI_ERROR (Status);
|
---|
396 |
|
---|
397 | Status = gBS->CreateEventEx (
|
---|
398 | EVT_NOTIFY_SIGNAL,
|
---|
399 | TPL_CALLBACK,
|
---|
400 | EndOfDxe,
|
---|
401 | NULL,
|
---|
402 | &gEfiEndOfDxeEventGroupGuid,
|
---|
403 | &Event
|
---|
404 | );
|
---|
405 | ASSERT_EFI_ERROR (Status);
|
---|
406 |
|
---|
407 | EfiCreateProtocolNotifyEvent (
|
---|
408 | &gEfiDxeSmmReadyToLockProtocolGuid,
|
---|
409 | TPL_CALLBACK,
|
---|
410 | DxeSmmReadyToLock,
|
---|
411 | NULL,
|
---|
412 | &Registration
|
---|
413 | );
|
---|
414 | }
|
---|