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