]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/LoadFileOnFv2/LoadFileOnFv2.c
MdeModulePkg: Replace BSD License with BSD+Patent License
[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 EFI_EVENT 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 return FALSE;
181 }
182
183 /**
184 Create file device path based on FFS file GUID and UI name.
185
186 @param Device Handle to Firmware Volume.
187 @param NameGuid Point to FFS file GUID.
188 @param FileName Point to FFS UI section name.
189
190 @return the combined device path
191 **/
192 EFI_DEVICE_PATH_PROTOCOL *
193 EFIAPI
194 CreateFileDevicePath (
195 IN EFI_HANDLE Device,
196 IN EFI_GUID *NameGuid,
197 IN CONST CHAR16 *FileName
198 )
199 {
200 UINTN Size;
201 FILEPATH_DEVICE_PATH *FilePath;
202 EFI_DEVICE_PATH_PROTOCOL *DevicePath;
203 EFI_DEVICE_PATH_PROTOCOL *FileDevicePath;
204 MEDIA_FW_VOL_FILEPATH_DEVICE_PATH FileNode;
205
206 EfiInitializeFwVolDevicepathNode (&FileNode, NameGuid);
207 DevicePath = AppendDevicePathNode (
208 DevicePathFromHandle (Device),
209 (EFI_DEVICE_PATH_PROTOCOL *) &FileNode
210 );
211
212 Size = StrSize (FileName);
213 FileDevicePath = AllocatePool (Size + SIZE_OF_FILEPATH_DEVICE_PATH + END_DEVICE_PATH_LENGTH);
214 if (FileDevicePath != NULL) {
215 FilePath = (FILEPATH_DEVICE_PATH *) FileDevicePath;
216 FilePath->Header.Type = MEDIA_DEVICE_PATH;
217 FilePath->Header.SubType = MEDIA_FILEPATH_DP;
218 CopyMem (&FilePath->PathName, FileName, Size);
219 SetDevicePathNodeLength (&FilePath->Header, Size + SIZE_OF_FILEPATH_DEVICE_PATH);
220 SetDevicePathEndNode (NextDevicePathNode (&FilePath->Header));
221
222 DevicePath = AppendDevicePath (DevicePath, FileDevicePath);
223 FreePool (FileDevicePath);
224 }
225
226 return DevicePath;
227 }
228
229 /**
230 Install LoadFile Protocol for Application FFS.
231
232 @param Handle FV Handle.
233
234 **/
235 VOID
236 EFIAPI
237 InstallFileLoadProtocol (
238 EFI_HANDLE Handle
239 )
240 {
241 EFI_STATUS Status;
242 LOAD_FILE_ON_FV2_PRIVATE_DATA *Private;
243 EFI_FIRMWARE_VOLUME2_PROTOCOL *Fv;
244 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb;
245 EFI_PHYSICAL_ADDRESS Address;
246 EFI_FV_FILETYPE FileType;
247 UINTN Key;
248 EFI_GUID NameGuid;
249 EFI_FV_FILE_ATTRIBUTES Attributes;
250 UINTN Size;
251 EFI_HANDLE LoadFileHandle;
252 UINT32 AuthenticationStatus;
253 CHAR16 *UiName;
254 UINTN UiNameSize;
255
256 DEBUG ((DEBUG_INFO, "LoadFileOnFv2:Find a FV!\n"));
257 Status = gBS->HandleProtocol (Handle, &gEfiFirmwareVolume2ProtocolGuid, (VOID **)&Fv);
258 ASSERT_EFI_ERROR (Status);
259 Status = gBS->HandleProtocol (Handle, &gEfiFirmwareVolumeBlockProtocolGuid, (VOID **)&Fvb);
260 Fvb->GetPhysicalAddress (Fvb, &Address);
261 DEBUG ((DEBUG_INFO, "LoadFileOnFv2:Fvb->Address=%x \n", Address));
262
263 //
264 // Use Firmware Volume 2 Protocol to search for a FFS files of type
265 // EFI_FV_FILETYPE_APPLICATION and produce a LoadFile protocol for
266 // each one found.
267 //
268 FileType = EFI_FV_FILETYPE_APPLICATION;
269 Key = 0;
270 while (TRUE) {
271 Status = Fv->GetNextFile (Fv, &Key, &FileType, &NameGuid, &Attributes, &Size);
272 if (EFI_ERROR (Status)) {
273 break;
274 }
275
276 UiName = NULL;
277 Status = Fv->ReadSection (
278 Fv,
279 &NameGuid,
280 EFI_SECTION_USER_INTERFACE,
281 0,
282 (VOID **)&UiName,
283 &UiNameSize,
284 &AuthenticationStatus
285 );
286 if (EFI_ERROR (Status)) {
287 continue;
288 }
289 if (!IsInPrivateList (&NameGuid)) {
290 Private = (LOAD_FILE_ON_FV2_PRIVATE_DATA *)AllocateCopyPool (sizeof (mLoadFileOnFv2PrivateDataTemplate), &mLoadFileOnFv2PrivateDataTemplate);
291 ASSERT (Private != NULL);
292 Private->Fv = Fv;
293 Private->DevicePath = CreateFileDevicePath (Handle, &NameGuid, UiName);
294 CopyGuid (&Private->NameGuid, &NameGuid);
295 LoadFileHandle = NULL;
296 DEBUG ((DEBUG_INFO, "Find a APPLICATION in this FV!\n"));
297 Status = gBS->InstallMultipleProtocolInterfaces (
298 &LoadFileHandle,
299 &gEfiDevicePathProtocolGuid, Private->DevicePath,
300 &gEfiLoadFileProtocolGuid, &Private->LoadFile,
301 NULL
302 );
303 if (!EFI_ERROR (Status)) {
304 InsertTailList (&mPrivateDataList, &Private->Link);
305 } else {
306 DEBUG ((DEBUG_ERROR, "Application with the same name %s has been installed.!\n", UiName));
307 FreePool (Private->DevicePath);
308 FreePool (Private);
309 }
310 }
311 }
312 }
313
314 /**
315 This notification function is invoked when an instance of the
316 LzmaCustomDecompressGuid is produced. It installs another instance of the
317 EFI_FIRMWARE_VOLUME_PROTOCOL on the handle of the FFS. This notification function
318 also handles the situation when LZMA decoder driver loaded later than FirmwareVolume driver.
319
320 @param Event The event that occured
321 @param Context Context of event. Not used in this nofication function.
322
323 **/
324 VOID
325 EFIAPI
326 FvNotificationEvent (
327 IN EFI_EVENT Event,
328 IN VOID *Context
329 )
330 {
331 EFI_STATUS Status;
332 UINTN BufferSize;
333 EFI_HANDLE *Handle;
334 UINTN Index;
335 EFI_HANDLE *CurHandle;
336
337
338 Handle = NULL;
339 Index = 0;
340 BufferSize = sizeof (EFI_HANDLE);
341 Handle = AllocateZeroPool (BufferSize);
342 if (Handle == NULL) {
343 return;
344 }
345 Status = gBS->LocateHandle (
346 ByProtocol,
347 &gEfiFirmwareVolume2ProtocolGuid,
348 NULL,
349 &BufferSize,
350 Handle
351 );
352 if (EFI_BUFFER_TOO_SMALL == Status) {
353 FreePool (Handle);
354 Handle = AllocateZeroPool (BufferSize);
355 if (Handle == NULL) {
356 return;
357 }
358 Status = gBS->LocateHandle (
359 ByProtocol,
360 &gEfiFirmwareVolume2ProtocolGuid,
361 NULL,
362 &BufferSize,
363 Handle
364 );
365 if (EFI_ERROR (Status)) {
366 return;
367 }
368 } else if (EFI_ERROR (Status)) {
369 return;
370 }
371
372 CurHandle = Handle;
373 for (Index=0; Index < BufferSize/sizeof (EFI_HANDLE); Index++) {
374 CurHandle = Handle + Index;
375 //
376 // Install LoadFile Protocol
377 //
378 InstallFileLoadProtocol (*CurHandle);
379 }
380 if (Handle != NULL) {
381 FreePool (Handle);
382 }
383 }
384
385 /**
386 Entry point function initializes global variables and installs notifications.
387
388 @param[in] ImageHandle The firmware allocated handle for the EFI image.
389 @param[in] SystemTable A pointer to the EFI System Table.
390
391 @retval EFI_SUCCESS The entry point is executed successfully.
392 @retval other Some error occurs when executing this entry point.
393 **/
394 EFI_STATUS
395 EFIAPI
396 LoadFileOnFv2Intialize (
397 IN EFI_HANDLE ImageHandle,
398 IN EFI_SYSTEM_TABLE *SystemTable
399 )
400 {
401 InitializeListHead (&mPrivateDataList);
402
403 EfiCreateProtocolNotifyEvent (
404 &gEfiFirmwareVolume2ProtocolGuid,
405 TPL_CALLBACK,
406 FvNotificationEvent,
407 NULL,
408 &mFvRegistration
409 );
410
411 EfiCreateProtocolNotifyEvent (
412 &gLzmaCustomDecompressGuid,
413 TPL_CALLBACK,
414 FvNotificationEvent,
415 NULL,
416 &mFvRegistration
417 );
418
419 return EFI_SUCCESS;
420 }
421