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