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