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