1 /** @file
2 *
3 *  Copyright (c) 2012-2014, ARM Limited. All rights reserved.
4 *
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 <Library/BaseMemoryLib.h>
16 #include <Library/DevicePathLib.h>
17 #include <Library/MemoryAllocationLib.h>
18 #include <Library/PrintLib.h>
19 #include <Library/UefiBootServicesTableLib.h>
20 
21 #include <Protocol/DevicePathFromText.h>
22 #include <Protocol/DriverBinding.h>
23 
24 #include "BootMonFsInternal.h"
25 
26 EFI_DEVICE_PATH* mBootMonFsSupportedDevicePaths;
27 LIST_ENTRY       mInstances;
28 
29 EFI_FILE_PROTOCOL mBootMonFsRootTemplate = {
30   EFI_FILE_PROTOCOL_REVISION,
31   BootMonFsOpenFile,
32   BootMonFsCloseFile,
33   BootMonFsDeleteFail,
34   BootMonFsReadDirectory,
35   BootMonFsWriteFile,
36   BootMonFsGetPositionUnsupported,  // UEFI Spec: GetPosition not valid on dirs
37   BootMonFsSetDirPosition,
38   BootMonFsGetInfo,
39   BootMonFsSetInfo,
40   BootMonFsFlushDirectory
41 };
42 
43 EFI_FILE_PROTOCOL mBootMonFsFileTemplate = {
44   EFI_FILE_PROTOCOL_REVISION,
45   BootMonFsOpenFile,
46   BootMonFsCloseFile,
47   BootMonFsDelete,
48   BootMonFsReadFile,
49   BootMonFsWriteFile,
50   BootMonFsGetPosition,
51   BootMonFsSetPosition,
52   BootMonFsGetInfo,
53   BootMonFsSetInfo,
54   BootMonFsFlushFile
55 };
56 
57 /**
58   Search for a file given its name coded in Ascii.
59 
60   When searching through the files of the volume, if a file is currently not
61   open, its name was written on the media and is kept in RAM in the
62   "HwDescription.Footer.Filename[]" field of the file's description.
63 
64   If a file is currently open, its name might not have been written on the
65   media yet, and as the "HwDescription" is a mirror in RAM of what is on the
66   media the "HwDescription.Footer.Filename[]" might be outdated. In that case,
67   the up to date name of the file is stored in the "Info" field of the file's
68   description.
69 
70   @param[in]   Instance       Pointer to the description of the volume in which
71                               the file has to be search for.
72   @param[in]   AsciiFileName  Name of the file.
73 
74   @param[out]  File           Pointer to the description of the file if the
75                               file was found.
76 
77   @retval  EFI_SUCCESS    The file was found.
78   @retval  EFI_NOT_FOUND  The file was not found.
79 
80 **/
81 EFI_STATUS
BootMonGetFileFromAsciiFileName(IN BOOTMON_FS_INSTANCE * Instance,IN CHAR8 * AsciiFileName,OUT BOOTMON_FS_FILE ** File)82 BootMonGetFileFromAsciiFileName (
83   IN  BOOTMON_FS_INSTANCE   *Instance,
84   IN  CHAR8*                AsciiFileName,
85   OUT BOOTMON_FS_FILE       **File
86   )
87 {
88   LIST_ENTRY       *Entry;
89   BOOTMON_FS_FILE  *FileEntry;
90   CHAR8            OpenFileAsciiFileName[MAX_NAME_LENGTH];
91   CHAR8            *AsciiFileNameToCompare;
92 
93   // Go through all the files in the list and return the file handle
94   for (Entry = GetFirstNode (&Instance->RootFile->Link);
95        !IsNull (&Instance->RootFile->Link, Entry);
96        Entry = GetNextNode (&Instance->RootFile->Link, Entry)
97        )
98   {
99     FileEntry = BOOTMON_FS_FILE_FROM_LINK_THIS (Entry);
100     if (FileEntry->Info != NULL) {
101       UnicodeStrToAsciiStr (FileEntry->Info->FileName, OpenFileAsciiFileName);
102       AsciiFileNameToCompare = OpenFileAsciiFileName;
103     } else {
104       AsciiFileNameToCompare = FileEntry->HwDescription.Footer.Filename;
105     }
106 
107     if (AsciiStrCmp (AsciiFileNameToCompare, AsciiFileName) == 0) {
108       *File = FileEntry;
109       return EFI_SUCCESS;
110     }
111   }
112   return EFI_NOT_FOUND;
113 }
114 
115 EFI_STATUS
BootMonGetFileFromPosition(IN BOOTMON_FS_INSTANCE * Instance,IN UINTN Position,OUT BOOTMON_FS_FILE ** File)116 BootMonGetFileFromPosition (
117   IN  BOOTMON_FS_INSTANCE   *Instance,
118   IN  UINTN                 Position,
119   OUT BOOTMON_FS_FILE       **File
120   )
121 {
122   LIST_ENTRY        *Entry;
123   BOOTMON_FS_FILE   *FileEntry;
124 
125   // Go through all the files in the list and return the file handle
126   for (Entry = GetFirstNode (&Instance->RootFile->Link);
127        !IsNull (&Instance->RootFile->Link, Entry) && (&Instance->RootFile->Link != Entry);
128        Entry = GetNextNode (&Instance->RootFile->Link, Entry)
129        )
130   {
131     if (Position == 0) {
132       FileEntry = BOOTMON_FS_FILE_FROM_LINK_THIS (Entry);
133       *File = FileEntry;
134       return EFI_SUCCESS;
135     }
136     Position--;
137   }
138   return EFI_NOT_FOUND;
139 }
140 
141 EFI_STATUS
BootMonFsCreateFile(IN BOOTMON_FS_INSTANCE * Instance,OUT BOOTMON_FS_FILE ** File)142 BootMonFsCreateFile (
143   IN  BOOTMON_FS_INSTANCE *Instance,
144   OUT BOOTMON_FS_FILE     **File
145   )
146 {
147   BOOTMON_FS_FILE *NewFile;
148 
149   NewFile = (BOOTMON_FS_FILE*)AllocateZeroPool (sizeof (BOOTMON_FS_FILE));
150   if (NewFile == NULL) {
151     return EFI_OUT_OF_RESOURCES;
152   }
153 
154   NewFile->Signature = BOOTMON_FS_FILE_SIGNATURE;
155   InitializeListHead (&NewFile->Link);
156   InitializeListHead (&NewFile->RegionToFlushLink);
157   NewFile->Instance = Instance;
158 
159   // If the created file is the root file then create a directory EFI_FILE_PROTOCOL
160   if (Instance->RootFile == *File) {
161     CopyMem (&NewFile->File, &mBootMonFsRootTemplate, sizeof (mBootMonFsRootTemplate));
162   } else {
163     CopyMem (&NewFile->File, &mBootMonFsFileTemplate, sizeof (mBootMonFsFileTemplate));
164   }
165   *File = NewFile;
166   return EFI_SUCCESS;
167 }
168 
169 STATIC
170 EFI_STATUS
SupportedDevicePathsInit(VOID)171 SupportedDevicePathsInit (
172   VOID
173   )
174 {
175   EFI_STATUS                          Status;
176   CHAR16*                             DevicePathListStr;
177   CHAR16*                             DevicePathStr;
178   CHAR16*                             NextDevicePathStr;
179   EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL *EfiDevicePathFromTextProtocol;
180   EFI_DEVICE_PATH_PROTOCOL           *Instance;
181 
182   Status = gBS->LocateProtocol (&gEfiDevicePathFromTextProtocolGuid, NULL, (VOID **)&EfiDevicePathFromTextProtocol);
183   ASSERT_EFI_ERROR (Status);
184 
185   // Initialize Variable
186   DevicePathListStr = (CHAR16*)PcdGetPtr (PcdBootMonFsSupportedDevicePaths);
187   mBootMonFsSupportedDevicePaths = NULL;
188 
189   // Extract the Device Path instances from the multi-device path string
190   while ((DevicePathListStr != NULL) && (DevicePathListStr[0] != L'\0')) {
191     NextDevicePathStr = StrStr (DevicePathListStr, L";");
192     if (NextDevicePathStr == NULL) {
193       DevicePathStr = DevicePathListStr;
194       DevicePathListStr = NULL;
195     } else {
196       DevicePathStr = (CHAR16*)AllocateCopyPool ((NextDevicePathStr - DevicePathListStr + 1) * sizeof (CHAR16), DevicePathListStr);
197       if (DevicePathStr == NULL) {
198         return EFI_OUT_OF_RESOURCES;
199       }
200       *(DevicePathStr + (NextDevicePathStr - DevicePathListStr)) = L'\0';
201       DevicePathListStr = NextDevicePathStr;
202       if (DevicePathListStr[0] == L';') {
203         DevicePathListStr++;
204       }
205     }
206 
207     Instance = EfiDevicePathFromTextProtocol->ConvertTextToDevicePath (DevicePathStr);
208     ASSERT (Instance != NULL);
209     mBootMonFsSupportedDevicePaths = AppendDevicePathInstance (mBootMonFsSupportedDevicePaths, Instance);
210 
211     if (NextDevicePathStr != NULL) {
212       FreePool (DevicePathStr);
213     }
214     FreePool (Instance);
215   }
216 
217   if (mBootMonFsSupportedDevicePaths == NULL) {
218     return EFI_UNSUPPORTED;
219   } else {
220     return EFI_SUCCESS;
221   }
222 }
223 
224 EFI_STATUS
225 EFIAPI
BootMonFsDriverSupported(IN EFI_DRIVER_BINDING_PROTOCOL * DriverBinding,IN EFI_HANDLE ControllerHandle,IN EFI_DEVICE_PATH_PROTOCOL * DevicePath OPTIONAL)226 BootMonFsDriverSupported (
227   IN        EFI_DRIVER_BINDING_PROTOCOL *DriverBinding,
228   IN        EFI_HANDLE                   ControllerHandle,
229   IN        EFI_DEVICE_PATH_PROTOCOL    *DevicePath OPTIONAL
230   )
231 {
232   EFI_DISK_IO_PROTOCOL  *DiskIo;
233   EFI_DEVICE_PATH_PROTOCOL *DevicePathProtocol;
234   EFI_DEVICE_PATH_PROTOCOL *SupportedDevicePath;
235   EFI_DEVICE_PATH_PROTOCOL *SupportedDevicePaths;
236   EFI_STATUS Status;
237   UINTN Size1;
238   UINTN Size2;
239 
240   //
241   // Open the IO Abstraction(s) needed to perform the supported test
242   //
243   Status = gBS->OpenProtocol (
244                   ControllerHandle,
245                   &gEfiDiskIoProtocolGuid,
246                   (VOID **) &DiskIo,
247                   gImageHandle,
248                   ControllerHandle,
249                   EFI_OPEN_PROTOCOL_BY_DRIVER
250                   );
251 
252   if (EFI_ERROR (Status)) {
253     return Status;
254   }
255   //
256   // Close the I/O Abstraction(s) used to perform the supported test
257   //
258   gBS->CloseProtocol (
259          ControllerHandle,
260          &gEfiDiskIoProtocolGuid,
261          gImageHandle,
262          ControllerHandle
263          );
264 
265   // Check that BlockIo protocol instance exists
266   Status = gBS->OpenProtocol (
267                   ControllerHandle,
268                   &gEfiBlockIoProtocolGuid,
269                   NULL,
270                   gImageHandle,
271                   ControllerHandle,
272                   EFI_OPEN_PROTOCOL_TEST_PROTOCOL
273                   );
274   if (EFI_ERROR (Status)) {
275     return Status;
276   }
277 
278   // Check if a DevicePath is attached to the handle
279   Status = gBS->OpenProtocol (
280                   ControllerHandle,
281                   &gEfiDevicePathProtocolGuid,
282                   (VOID **)&DevicePathProtocol,
283                   gImageHandle,
284                   ControllerHandle,
285                   EFI_OPEN_PROTOCOL_BY_DRIVER
286                   );
287   if (EFI_ERROR (Status)) {
288     return Status;
289   }
290 
291   // Check if the Device Path is the one which contains the Boot Monitor File System
292   Size1 = GetDevicePathSize (DevicePathProtocol);
293 
294   // Go through the list of Device Path Instances
295   Status = EFI_UNSUPPORTED;
296   SupportedDevicePaths = mBootMonFsSupportedDevicePaths;
297   while (SupportedDevicePaths != NULL) {
298     SupportedDevicePath = GetNextDevicePathInstance (&SupportedDevicePaths, &Size2);
299 
300     if ((Size1 == Size2) && (CompareMem (DevicePathProtocol, SupportedDevicePath, Size1) == 0)) {
301       // The Device Path is supported
302       Status = EFI_SUCCESS;
303       break;
304     }
305   }
306 
307   gBS->CloseProtocol (ControllerHandle, &gEfiDevicePathProtocolGuid, gImageHandle, ControllerHandle);
308   return Status;
309 }
310 
311 EFI_STATUS
312 EFIAPI
BootMonFsDriverStart(IN EFI_DRIVER_BINDING_PROTOCOL * DriverBinding,IN EFI_HANDLE ControllerHandle,IN EFI_DEVICE_PATH_PROTOCOL * DevicePath OPTIONAL)313 BootMonFsDriverStart (
314   IN        EFI_DRIVER_BINDING_PROTOCOL *DriverBinding,
315   IN        EFI_HANDLE                   ControllerHandle,
316   IN        EFI_DEVICE_PATH_PROTOCOL    *DevicePath OPTIONAL
317   )
318 {
319   BOOTMON_FS_INSTANCE *Instance;
320   EFI_STATUS           Status;
321   UINTN                VolumeNameSize;
322   EFI_FILE_INFO       *Info;
323 
324   Instance = AllocateZeroPool (sizeof (BOOTMON_FS_INSTANCE));
325   if (Instance == NULL) {
326     return EFI_OUT_OF_RESOURCES;
327   }
328 
329   // Initialize the BlockIo of the Instance
330   Status = gBS->OpenProtocol (
331                   ControllerHandle,
332                   &gEfiBlockIoProtocolGuid,
333                   (VOID **)&(Instance->BlockIo),
334                   gImageHandle,
335                   ControllerHandle,
336                   EFI_OPEN_PROTOCOL_GET_PROTOCOL
337                   );
338   if (EFI_ERROR (Status)) {
339     goto Error;
340   }
341 
342   Status = gBS->OpenProtocol (
343                   ControllerHandle,
344                   &gEfiDiskIoProtocolGuid,
345                   (VOID **)&(Instance->DiskIo),
346                   gImageHandle,
347                   ControllerHandle,
348                   EFI_OPEN_PROTOCOL_BY_DRIVER
349                   );
350   if (EFI_ERROR (Status)) {
351     goto Error;
352   }
353 
354   //
355   // Initialize the attributes of the Instance
356   //
357   Instance->Signature = BOOTMON_FS_SIGNATURE;
358   Instance->ControllerHandle = ControllerHandle;
359   Instance->Media = Instance->BlockIo->Media;
360   Instance->Binding = DriverBinding;
361 
362     // Initialize the Simple File System Protocol
363   Instance->Fs.Revision = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_REVISION;
364   Instance->Fs.OpenVolume = OpenBootMonFsOpenVolume;
365 
366   // Volume name + L' ' + '2' digit number
367   VolumeNameSize = StrSize (BOOTMON_FS_VOLUME_LABEL) + (3 * sizeof (CHAR16));
368 
369   // Initialize FileSystem Information
370   Instance->FsInfo.Size = SIZE_OF_EFI_FILE_SYSTEM_INFO + VolumeNameSize;
371   Instance->FsInfo.BlockSize = Instance->Media->BlockSize;
372   Instance->FsInfo.ReadOnly = FALSE;
373   Instance->FsInfo.VolumeSize =
374     Instance->Media->BlockSize * (Instance->Media->LastBlock - Instance->Media->LowestAlignedLba);
375   CopyMem (Instance->FsInfo.VolumeLabel, BOOTMON_FS_VOLUME_LABEL, StrSize (BOOTMON_FS_VOLUME_LABEL));
376 
377   // Initialize the root file
378   Status = BootMonFsCreateFile (Instance, &Instance->RootFile);
379   if (EFI_ERROR (Status)) {
380     goto Error;
381   }
382 
383   Info = AllocateZeroPool (sizeof (EFI_FILE_INFO));
384   if (Info == NULL) {
385     Status = EFI_OUT_OF_RESOURCES;
386     goto Error;
387   }
388   Instance->RootFile->Info = Info;
389 
390   // Initialize the DevicePath of the Instance
391   Status = gBS->OpenProtocol (
392                   ControllerHandle,
393                   &gEfiDevicePathProtocolGuid,
394                   (VOID **)&(Instance->DevicePath),
395                   gImageHandle,
396                   ControllerHandle,
397                   EFI_OPEN_PROTOCOL_GET_PROTOCOL
398                   );
399   if (EFI_ERROR (Status)) {
400     goto Error;
401   }
402 
403   //
404   // Install the Simple File System Protocol
405   //
406   Status = gBS->InstallMultipleProtocolInterfaces (
407                       &ControllerHandle,
408                       &gEfiSimpleFileSystemProtocolGuid, &Instance->Fs,
409                       NULL
410                       );
411   if (EFI_ERROR (Status)) {
412     goto Error;
413   }
414 
415   InsertTailList (&mInstances, &Instance->Link);
416 
417   return EFI_SUCCESS;
418 
419 Error:
420 
421     if (Instance->RootFile != NULL) {
422       if (Instance->RootFile->Info != NULL) {
423         FreePool (Instance->RootFile->Info);
424       }
425       FreePool (Instance->RootFile);
426     }
427     FreePool (Instance);
428 
429   return Status;
430 }
431 
432 
433 EFI_STATUS
434 EFIAPI
BootMonFsDriverStop(IN EFI_DRIVER_BINDING_PROTOCOL * DriverBinding,IN EFI_HANDLE ControllerHandle,IN UINTN NumberOfChildren,IN EFI_HANDLE * ChildHandleBuffer OPTIONAL)435 BootMonFsDriverStop (
436   IN        EFI_DRIVER_BINDING_PROTOCOL *DriverBinding,
437   IN        EFI_HANDLE                   ControllerHandle,
438   IN        UINTN                        NumberOfChildren,
439   IN        EFI_HANDLE                  *ChildHandleBuffer OPTIONAL
440   )
441 {
442   BOOTMON_FS_INSTANCE *Instance;
443   LIST_ENTRY          *Link;
444   EFI_STATUS           Status;
445   BOOLEAN              InstanceFound;
446 
447   // Find instance from ControllerHandle.
448   Instance = NULL;
449   InstanceFound = FALSE;
450   // For each instance in mInstances:
451   for (Link = GetFirstNode (&mInstances); !IsNull (&mInstances, Link); Link = GetNextNode (&mInstances, Link)) {
452     Instance = BOOTMON_FS_FROM_LINK (Link);
453 
454     if (Instance->ControllerHandle == ControllerHandle) {
455       InstanceFound = TRUE;
456       break;
457     }
458   }
459   ASSERT (InstanceFound == TRUE);
460 
461   gBS->CloseProtocol (
462       ControllerHandle,
463       &gEfiDevicePathProtocolGuid,
464       DriverBinding->ImageHandle,
465       ControllerHandle);
466 
467   gBS->CloseProtocol (
468       ControllerHandle,
469       &gEfiDiskIoProtocolGuid,
470       DriverBinding->ImageHandle,
471       ControllerHandle);
472 
473   gBS->CloseProtocol (
474       ControllerHandle,
475       &gEfiBlockIoProtocolGuid,
476       DriverBinding->ImageHandle,
477       ControllerHandle);
478 
479   Status = gBS->UninstallMultipleProtocolInterfaces (
480       &ControllerHandle,
481       &gEfiSimpleFileSystemProtocolGuid, &Instance->Fs,
482       NULL);
483 
484   FreePool (Instance->RootFile->Info);
485   FreePool (Instance->RootFile);
486   FreePool (Instance);
487 
488   return Status;
489 }
490 
491 //
492 // Simple Network Protocol Driver Global Variables
493 //
494 EFI_DRIVER_BINDING_PROTOCOL mBootMonFsDriverBinding = {
495   BootMonFsDriverSupported,
496   BootMonFsDriverStart,
497   BootMonFsDriverStop,
498   0xa,
499   NULL,
500   NULL
501 };
502 
503 EFI_STATUS
504 EFIAPI
BootMonFsEntryPoint(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)505 BootMonFsEntryPoint (
506   IN EFI_HANDLE                            ImageHandle,
507   IN EFI_SYSTEM_TABLE                      *SystemTable
508   )
509 {
510   EFI_STATUS  Status;
511 
512   InitializeListHead (&mInstances);
513 
514   // Initialize the list of Device Paths that could support BootMonFs
515   Status = SupportedDevicePathsInit ();
516   if (!EFI_ERROR (Status)) {
517     Status = gBS->InstallMultipleProtocolInterfaces (
518                     &ImageHandle,
519                     &gEfiDriverBindingProtocolGuid, &mBootMonFsDriverBinding,
520                     NULL
521                     );
522     ASSERT_EFI_ERROR (Status);
523   } else {
524     DEBUG((EFI_D_ERROR,"Warning: No Device Paths supporting BootMonFs have been defined in the PCD.\n"));
525   }
526 
527   return Status;
528 }
529