1 /** @file
2 Produce Load File Protocol for UEFI Applications in Firmware Volumes
3
4 Copyright (c) 2011 - 2016, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution. The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
9
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12
13 **/
14
15 #include <PiDxe.h>
16
17 #include <Guid/LzmaDecompress.h>
18 #include <Protocol/LoadFile.h>
19 #include <Protocol/DevicePath.h>
20 #include <Protocol/FirmwareVolume2.h>
21 #include <Protocol/FirmwareVolumeBlock.h>
22
23 #include <Library/DebugLib.h>
24 #include <Library/UefiLib.h>
25 #include <Library/BaseMemoryLib.h>
26 #include <Library/UefiDriverEntryPoint.h>
27 #include <Library/UefiBootServicesTableLib.h>
28 #include <Library/MemoryAllocationLib.h>
29 #include <Library/DevicePathLib.h>
30
31 #define LOAD_FILE_ON_FV2_PRIVATE_DATA_SIGNATURE SIGNATURE_32 ('l', 'f', 'f', 'v')
32
33 typedef struct {
34 UINTN Signature;
35 EFI_LOAD_FILE_PROTOCOL LoadFile;
36 EFI_DEVICE_PATH_PROTOCOL *DevicePath;
37 EFI_FIRMWARE_VOLUME2_PROTOCOL *Fv;
38 EFI_GUID NameGuid;
39 LIST_ENTRY Link;
40 } LOAD_FILE_ON_FV2_PRIVATE_DATA;
41
42 #define LOAD_FILE_ON_FV2_PRIVATE_DATA_FROM_THIS(a) CR (a, LOAD_FILE_ON_FV2_PRIVATE_DATA, LoadFile, LOAD_FILE_ON_FV2_PRIVATE_DATA_SIGNATURE)
43 #define LOAD_FILE_ON_FV2_PRIVATE_DATA_FROM_LINK(a) CR (a, LOAD_FILE_ON_FV2_PRIVATE_DATA, Link, LOAD_FILE_ON_FV2_PRIVATE_DATA_SIGNATURE)
44
45 EFI_EVENT mFvRegistration;
46 LIST_ENTRY mPrivateDataList;
47
48 /**
49 Causes the driver to load a specified file from firmware volume.
50
51 @param[in] This Protocol instance pointer.
52 @param[in] FilePath The device specific path of the file to load.
53 @param[in] BootPolicy If TRUE, indicates that the request originates from the
54 boot manager is attempting to load FilePath as a boot
55 selection. If FALSE, then FilePath must match an exact file
56 to be loaded.
57 @param[in, out] BufferSize On input the size of Buffer in bytes. On output with a return
58 code of EFI_SUCCESS, the amount of data transferred to
59 Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL,
60 the size of Buffer required to retrieve the requested file.
61 @param[in] Buffer The memory buffer to transfer the file to. IF Buffer is NULL,
62 then no the size of the requested file is returned in
63 BufferSize.
64
65 @retval EFI_SUCCESS The file was loaded.
66 @retval EFI_UNSUPPORTED The device does not support the provided BootPolicy.
67 @retval EFI_INVALID_PARAMETER FilePath is not a valid device path, or
68 BufferSize is NULL.
69 @retval EFI_DEVICE_ERROR The file was not loaded due to a device error.
70 @retval EFI_NOT_FOUND The file was not found.
71 @retval EFI_OUT_OF_RESOURCES An allocation failure occurred.
72 @retval EFI_ACCESS_DENIED The firmware volume is configured to
73 disallow reads.
74 **/
75 EFI_STATUS
76 EFIAPI
LoadFileOnFv2LoadFile(IN EFI_LOAD_FILE_PROTOCOL * This,IN EFI_DEVICE_PATH_PROTOCOL * FilePath,IN BOOLEAN BootPolicy,IN OUT UINTN * BufferSize,IN VOID * Buffer OPTIONAL)77 LoadFileOnFv2LoadFile (
78 IN EFI_LOAD_FILE_PROTOCOL *This,
79 IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
80 IN BOOLEAN BootPolicy,
81 IN OUT UINTN *BufferSize,
82 IN VOID *Buffer OPTIONAL
83 )
84 {
85 EFI_STATUS Status;
86 LOAD_FILE_ON_FV2_PRIVATE_DATA *Private;
87 VOID *Pe32Buffer;
88 UINTN Pe32BufferSize;
89 UINT32 AuthenticationStatus;
90
91 if (This == NULL || BufferSize == NULL) {
92 return EFI_INVALID_PARAMETER;
93 }
94
95 //
96 // Only support BootPolicy
97 //
98 if (!BootPolicy) {
99 return EFI_UNSUPPORTED;
100 }
101
102 //
103 // Get private context data
104 //
105 Private = LOAD_FILE_ON_FV2_PRIVATE_DATA_FROM_THIS (This);
106
107 //
108 // Determine the size of the PE32 section
109 //
110 Pe32Buffer = NULL;
111 Pe32BufferSize = 0;
112 Status = Private->Fv->ReadSection (
113 Private->Fv,
114 &Private->NameGuid,
115 EFI_SECTION_PE32,
116 0,
117 &Pe32Buffer,
118 &Pe32BufferSize,
119 &AuthenticationStatus
120 );
121 if (EFI_ERROR (Status)) {
122 return Status;
123 }
124
125 //
126 // If the buffer passed in is not large enough, return the size of the required
127 // buffer in BufferSize and return EFI_BUFFER_TOO_SMALL
128 //
129 if (*BufferSize < Pe32BufferSize || Buffer == NULL) {
130 *BufferSize = Pe32BufferSize;
131 return EFI_BUFFER_TOO_SMALL;
132 }
133
134 //
135 // The buffer passed in is large enough, so read the PE32 section directly into
136 // the buffer, update BufferSize with the actual size read, and return the status
137 // from ReadSection()
138 //
139 return Private->Fv->ReadSection (
140 Private->Fv,
141 &Private->NameGuid,
142 EFI_SECTION_PE32,
143 0,
144 &Buffer,
145 BufferSize,
146 &AuthenticationStatus
147 );
148 }
149
150 LOAD_FILE_ON_FV2_PRIVATE_DATA mLoadFileOnFv2PrivateDataTemplate = {
151 LOAD_FILE_ON_FV2_PRIVATE_DATA_SIGNATURE,
152 {
153 LoadFileOnFv2LoadFile
154 }
155 };
156
157 /**
158 Check if the FFS has been installed LoadFileProtocol for it.
159
160 @param[in] NameGuid Point to FFS File GUID to be checked.
161
162 @retval TRUE The FFS's FileLoadProtocol is in list.
163 @retval FALSE The FFS's FileLoadProtocol is not in list.
164
165 **/
166 BOOLEAN
167 EFIAPI
IsInPrivateList(IN EFI_GUID * NameGuid)168 IsInPrivateList (
169 IN EFI_GUID *NameGuid
170 )
171 {
172 LIST_ENTRY *Entry;
173 LOAD_FILE_ON_FV2_PRIVATE_DATA *PrivateData;
174
175 if (IsListEmpty (&mPrivateDataList)) {
176 return FALSE;
177 }
178
179 for(Entry = (&mPrivateDataList)->ForwardLink; Entry != (&mPrivateDataList); Entry = Entry->ForwardLink) {
180 PrivateData = LOAD_FILE_ON_FV2_PRIVATE_DATA_FROM_LINK (Entry);
181 if (CompareGuid (NameGuid, &PrivateData->NameGuid)) {
182 DEBUG ((DEBUG_INFO, "LoadFileOnFv2:FileLoadProtocol has been installed in:%g\n", NameGuid));
183 return TRUE;
184 }
185 }
186 return FALSE;
187 }
188
189 /**
190 Create file device path based on FFS file GUID and UI name.
191
192 @param Device Handle to Firmware Volume.
193 @param NameGuid Point to FFS file GUID.
194 @param FileName Point to FFS UI section name.
195
196 @return the combined device path
197 **/
198 EFI_DEVICE_PATH_PROTOCOL *
199 EFIAPI
CreateFileDevicePath(IN EFI_HANDLE Device,IN EFI_GUID * NameGuid,IN CONST CHAR16 * FileName)200 CreateFileDevicePath (
201 IN EFI_HANDLE Device,
202 IN EFI_GUID *NameGuid,
203 IN CONST CHAR16 *FileName
204 )
205 {
206 UINTN Size;
207 FILEPATH_DEVICE_PATH *FilePath;
208 EFI_DEVICE_PATH_PROTOCOL *DevicePath;
209 EFI_DEVICE_PATH_PROTOCOL *FileDevicePath;
210 MEDIA_FW_VOL_FILEPATH_DEVICE_PATH FileNode;
211
212 EfiInitializeFwVolDevicepathNode (&FileNode, NameGuid);
213 DevicePath = AppendDevicePathNode (
214 DevicePathFromHandle (Device),
215 (EFI_DEVICE_PATH_PROTOCOL *) &FileNode
216 );
217
218 Size = StrSize (FileName);
219 FileDevicePath = AllocatePool (Size + SIZE_OF_FILEPATH_DEVICE_PATH + END_DEVICE_PATH_LENGTH);
220 if (FileDevicePath != NULL) {
221 FilePath = (FILEPATH_DEVICE_PATH *) FileDevicePath;
222 FilePath->Header.Type = MEDIA_DEVICE_PATH;
223 FilePath->Header.SubType = MEDIA_FILEPATH_DP;
224 CopyMem (&FilePath->PathName, FileName, Size);
225 SetDevicePathNodeLength (&FilePath->Header, Size + SIZE_OF_FILEPATH_DEVICE_PATH);
226 SetDevicePathEndNode (NextDevicePathNode (&FilePath->Header));
227
228 DevicePath = AppendDevicePath (DevicePath, FileDevicePath);
229 FreePool (FileDevicePath);
230 }
231
232 return DevicePath;
233 }
234
235 /**
236 Install LoadFile Protocol for Application FFS.
237
238 @param Handle FV Handle.
239
240 **/
241 VOID
242 EFIAPI
InstallFileLoadProtocol(EFI_HANDLE Handle)243 InstallFileLoadProtocol (
244 EFI_HANDLE Handle
245 )
246 {
247 EFI_STATUS Status;
248 LOAD_FILE_ON_FV2_PRIVATE_DATA *Private;
249 EFI_FIRMWARE_VOLUME2_PROTOCOL *Fv;
250 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb;
251 EFI_PHYSICAL_ADDRESS Address;
252 EFI_FV_FILETYPE FileType;
253 UINTN Key;
254 EFI_GUID NameGuid;
255 EFI_FV_FILE_ATTRIBUTES Attributes;
256 UINTN Size;
257 EFI_HANDLE LoadFileHandle;
258 UINT32 AuthenticationStatus;
259 CHAR16 *UiName;
260 UINTN UiNameSize;
261
262 DEBUG ((DEBUG_INFO, "LoadFileOnFv2:Find a FV!\n"));
263 Status = gBS->HandleProtocol (Handle, &gEfiFirmwareVolume2ProtocolGuid, (VOID **)&Fv);
264 ASSERT_EFI_ERROR (Status);
265 Status = gBS->HandleProtocol (Handle, &gEfiFirmwareVolumeBlockProtocolGuid, (VOID **)&Fvb);
266 Fvb->GetPhysicalAddress (Fvb, &Address);
267 DEBUG ((DEBUG_INFO, "LoadFileOnFv2:Fvb->Address=%x \n", Address));
268
269 //
270 // Use Firmware Volume 2 Protocol to search for a FFS files of type
271 // EFI_FV_FILETYPE_APPLICATION and produce a LoadFile protocol for
272 // each one found.
273 //
274 FileType = EFI_FV_FILETYPE_APPLICATION;
275 Key = 0;
276 while (TRUE) {
277 Status = Fv->GetNextFile (Fv, &Key, &FileType, &NameGuid, &Attributes, &Size);
278 if (EFI_ERROR (Status)) {
279 break;
280 }
281
282 UiName = NULL;
283 Status = Fv->ReadSection (
284 Fv,
285 &NameGuid,
286 EFI_SECTION_USER_INTERFACE,
287 0,
288 (VOID **)&UiName,
289 &UiNameSize,
290 &AuthenticationStatus
291 );
292 if (EFI_ERROR (Status)) {
293 continue;
294 }
295 if (!IsInPrivateList (&NameGuid)) {
296 Private = (LOAD_FILE_ON_FV2_PRIVATE_DATA *)AllocateCopyPool (sizeof (mLoadFileOnFv2PrivateDataTemplate), &mLoadFileOnFv2PrivateDataTemplate);
297 ASSERT (Private != NULL);
298 Private->Fv = Fv;
299 Private->DevicePath = CreateFileDevicePath (Handle, &NameGuid, UiName);
300 CopyGuid (&Private->NameGuid, &NameGuid);
301 LoadFileHandle = NULL;
302 DEBUG ((DEBUG_INFO, "Find a APPLICATION in this FV!\n"));
303 Status = gBS->InstallMultipleProtocolInterfaces (
304 &LoadFileHandle,
305 &gEfiDevicePathProtocolGuid, Private->DevicePath,
306 &gEfiLoadFileProtocolGuid, &Private->LoadFile,
307 NULL
308 );
309 if (!EFI_ERROR (Status)) {
310 InsertTailList (&mPrivateDataList, &Private->Link);
311 } else {
312 DEBUG ((DEBUG_ERROR, "Application with the same name %s has been installed.!\n", UiName));
313 FreePool (Private->DevicePath);
314 FreePool (Private);
315 }
316 }
317 }
318 }
319
320 /**
321 This notification function is invoked when an instance of the
322 LzmaCustomDecompressGuid is produced. It installs another instance of the
323 EFI_FIRMWARE_VOLUME_PROTOCOL on the handle of the FFS. This notification function
324 also handles the situation when LZMA decoder driver loaded later than FirmwareVolume driver.
325
326 @param Event The event that occured
327 @param Context Context of event. Not used in this nofication function.
328
329 **/
330 VOID
331 EFIAPI
FvNotificationEvent(IN EFI_EVENT Event,IN VOID * Context)332 FvNotificationEvent (
333 IN EFI_EVENT Event,
334 IN VOID *Context
335 )
336 {
337 EFI_STATUS Status;
338 UINTN BufferSize;
339 EFI_HANDLE *Handle;
340 UINTN Index;
341 EFI_HANDLE *CurHandle;
342
343
344 Handle = NULL;
345 Index = 0;
346 BufferSize = sizeof (EFI_HANDLE);
347 Handle = AllocateZeroPool (BufferSize);
348 if (Handle == NULL) {
349 return;
350 }
351 Status = gBS->LocateHandle (
352 ByProtocol,
353 &gEfiFirmwareVolume2ProtocolGuid,
354 NULL,
355 &BufferSize,
356 Handle
357 );
358 if (EFI_BUFFER_TOO_SMALL == Status) {
359 FreePool (Handle);
360 Handle = AllocateZeroPool (BufferSize);
361 if (Handle == NULL) {
362 return;
363 }
364 Status = gBS->LocateHandle (
365 ByProtocol,
366 &gEfiFirmwareVolume2ProtocolGuid,
367 NULL,
368 &BufferSize,
369 Handle
370 );
371 if (EFI_ERROR (Status)) {
372 return;
373 }
374 } else if (EFI_ERROR (Status)) {
375 return;
376 }
377
378 CurHandle = Handle;
379 for (Index=0; Index < BufferSize/sizeof (EFI_HANDLE); Index++) {
380 CurHandle = Handle + Index;
381 //
382 // Install LoadFile Protocol
383 //
384 InstallFileLoadProtocol (*CurHandle);
385 }
386 if (Handle != NULL) {
387 FreePool (Handle);
388 }
389 }
390
391 /**
392 Entry point function initializes global variables and installs notifications.
393
394 @param[in] ImageHandle The firmware allocated handle for the EFI image.
395 @param[in] SystemTable A pointer to the EFI System Table.
396
397 @retval EFI_SUCCESS The entry point is executed successfully.
398 @retval other Some error occurs when executing this entry point.
399 **/
400 EFI_STATUS
401 EFIAPI
LoadFileOnFv2Intialize(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)402 LoadFileOnFv2Intialize (
403 IN EFI_HANDLE ImageHandle,
404 IN EFI_SYSTEM_TABLE *SystemTable
405 )
406 {
407 InitializeListHead (&mPrivateDataList);
408
409 EfiCreateProtocolNotifyEvent (
410 &gEfiFirmwareVolume2ProtocolGuid,
411 TPL_CALLBACK,
412 FvNotificationEvent,
413 NULL,
414 &mFvRegistration
415 );
416
417 EfiCreateProtocolNotifyEvent (
418 &gLzmaCustomDecompressGuid,
419 TPL_CALLBACK,
420 FvNotificationEvent,
421 NULL,
422 &mFvRegistration
423 );
424
425 return EFI_SUCCESS;
426 }
427
428