]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Library/FileExplorerLib/FileExplorer.c
59c851b7b3b5c381b555b2296c87688148f28c05
[mirror_edk2.git] / MdeModulePkg / Library / FileExplorerLib / FileExplorer.c
1 /** @file
2 File explorer related functions.
3
4 Copyright (c) 2004 - 2016, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials are licensed and made available under
6 the terms and conditions of the BSD License that accompanies this distribution.
7 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
16 #include "FileExplorer.h"
17
18 EFI_GUID FileExplorerGuid = EFI_FILE_EXPLORE_FORMSET_GUID;
19
20 ///
21 /// File system selection menu
22 ///
23 MENU_OPTION mFsOptionMenu = {
24 MENU_OPTION_SIGNATURE,
25 {NULL},
26 0,
27 FALSE
28 };
29
30 FILE_EXPLORER_CALLBACK_DATA gFileExplorerPrivate = {
31 FILE_EXPLORER_CALLBACK_DATA_SIGNATURE,
32 NULL,
33 NULL,
34 {
35 LibExtractConfig,
36 LibRouteConfig,
37 LibCallback
38 },
39 NULL,
40 &mFsOptionMenu,
41 0
42 };
43
44 HII_VENDOR_DEVICE_PATH *gHiiVendorDevicePath;
45
46 HII_VENDOR_DEVICE_PATH FeHiiVendorDevicePath = {
47 {
48 {
49 HARDWARE_DEVICE_PATH,
50 HW_VENDOR_DP,
51 {
52 (UINT8) (sizeof (VENDOR_DEVICE_PATH)),
53 (UINT8) ((sizeof (VENDOR_DEVICE_PATH)) >> 8)
54 }
55 },
56 //
57 // Will be replace with gEfiCallerIdGuid in code.
58 //
59 { 0x0, 0x0, 0x0, { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 } }
60 },
61 {
62 END_DEVICE_PATH_TYPE,
63 END_ENTIRE_DEVICE_PATH_SUBTYPE,
64 {
65 (UINT8) (END_DEVICE_PATH_LENGTH),
66 (UINT8) ((END_DEVICE_PATH_LENGTH) >> 8)
67 }
68 }
69 };
70
71 VOID *mLibStartOpCodeHandle = NULL;
72 VOID *mLibEndOpCodeHandle = NULL;
73 EFI_IFR_GUID_LABEL *mLibStartLabel = NULL;
74 EFI_IFR_GUID_LABEL *mLibEndLabel = NULL;
75
76 /**
77 This function allows a caller to extract the current configuration for one
78 or more named elements from the target driver.
79
80
81 @param This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
82 @param Request A null-terminated Unicode string in <ConfigRequest> format.
83 @param Progress On return, points to a character in the Request string.
84 Points to the string's null terminator if request was successful.
85 Points to the most recent '&' before the first failing name/value
86 pair (or the beginning of the string if the failure is in the
87 first name/value pair) if the request was not successful.
88 @param Results A null-terminated Unicode string in <ConfigAltResp> format which
89 has all values filled in for the names in the Request string.
90 String to be allocated by the called function.
91
92 @retval EFI_INVALID_PARAMETER Request is illegal syntax, or unknown name.
93 @retval EFI_NOT_FOUND Routing data doesn't match any storage in this driver.
94
95 **/
96 EFI_STATUS
97 EFIAPI
98 LibExtractConfig (
99 IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This,
100 IN CONST EFI_STRING Request,
101 OUT EFI_STRING *Progress,
102 OUT EFI_STRING *Results
103 )
104 {
105 if (Progress == NULL || Results == NULL) {
106 return EFI_INVALID_PARAMETER;
107 }
108
109 *Progress = Request;
110 return EFI_NOT_FOUND;
111 }
112
113 /**
114 This function processes the results of changes in configuration.
115
116
117 @param This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
118 @param Configuration A null-terminated Unicode string in <ConfigResp> format.
119 @param Progress A pointer to a string filled in with the offset of the most
120 recent '&' before the first failing name/value pair (or the
121 beginning of the string if the failure is in the first
122 name/value pair) or the terminating NULL if all was successful.
123
124 @retval EFI_INVALID_PARAMETER Configuration is NULL.
125 @retval EFI_NOT_FOUND Routing data doesn't match any storage in this driver.
126
127 **/
128 EFI_STATUS
129 EFIAPI
130 LibRouteConfig (
131 IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This,
132 IN CONST EFI_STRING Configuration,
133 OUT EFI_STRING *Progress
134 )
135 {
136 if (Configuration == NULL || Progress == NULL) {
137 return EFI_INVALID_PARAMETER;
138 }
139
140 *Progress = Configuration;
141 return EFI_NOT_FOUND;
142 }
143
144 /**
145 This function processes the results of changes in configuration.
146 When user select a interactive opcode, this callback will be triggered.
147 Based on the Question(QuestionId) that triggers the callback, the corresponding
148 actions is performed. It handles:
149
150 1) Process the axtra action or exit file explorer when user select one file .
151 2) update of file content if a dir is selected.
152
153 @param This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
154 @param Action Specifies the type of action taken by the browser.
155 @param QuestionId A unique value which is sent to the original exporting driver
156 so that it can identify the type of data to expect.
157 @param Type The type of value for the question.
158 @param Value A pointer to the data being sent to the original exporting driver.
159 @param ActionRequest On return, points to the action requested by the callback function.
160
161 @retval EFI_SUCCESS The callback successfully handled the action.
162 @retval other error Error occur when parse one directory.
163 **/
164 EFI_STATUS
165 EFIAPI
166 LibCallback (
167 IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This,
168 IN EFI_BROWSER_ACTION Action,
169 IN EFI_QUESTION_ID QuestionId,
170 IN UINT8 Type,
171 IN EFI_IFR_TYPE_VALUE *Value,
172 OUT EFI_BROWSER_ACTION_REQUEST *ActionRequest
173 )
174 {
175 EFI_STATUS Status;
176 BOOLEAN NeedExit;
177
178 NeedExit = TRUE;
179
180 if (Action != EFI_BROWSER_ACTION_CHANGING && Action != EFI_BROWSER_ACTION_CHANGED) {
181 //
182 // Do nothing for other UEFI Action. Only do call back when data is changed.
183 //
184 return EFI_UNSUPPORTED;
185 }
186
187 if (Action == EFI_BROWSER_ACTION_CHANGED) {
188 if ((Value == NULL) || (ActionRequest == NULL)) {
189 return EFI_INVALID_PARAMETER;
190 }
191
192 if (QuestionId >= FILE_OPTION_OFFSET) {
193 LibGetDevicePath(QuestionId);
194
195 //
196 // Process the extra action.
197 //
198 if (gFileExplorerPrivate.ChooseHandler != NULL) {
199 NeedExit = gFileExplorerPrivate.ChooseHandler (gFileExplorerPrivate.RetDevicePath);
200 }
201
202 if (NeedExit) {
203 *ActionRequest = EFI_BROWSER_ACTION_REQUEST_EXIT;
204 }
205 }
206 } else if (Action == EFI_BROWSER_ACTION_CHANGING) {
207 if (Value == NULL) {
208 return EFI_INVALID_PARAMETER;
209 }
210
211 if (QuestionId >= FILE_OPTION_OFFSET) {
212 Status = LibUpdateFileExplorer (QuestionId);
213 if (EFI_ERROR (Status)) {
214 return Status;
215 }
216 }
217 }
218
219 return EFI_SUCCESS;
220 }
221
222 /**
223 Create a menu entry by given menu type.
224
225 @retval NULL If failed to create the menu.
226 @return the new menu entry.
227
228 **/
229 MENU_ENTRY *
230 LibCreateMenuEntry (
231 VOID
232 )
233 {
234 MENU_ENTRY *MenuEntry;
235
236 //
237 // Create new menu entry
238 //
239 MenuEntry = AllocateZeroPool (sizeof (MENU_ENTRY));
240 if (MenuEntry == NULL) {
241 return NULL;
242 }
243
244 MenuEntry->VariableContext = AllocateZeroPool (sizeof (FILE_CONTEXT));
245 if (MenuEntry->VariableContext == NULL) {
246 FreePool (MenuEntry);
247 return NULL;
248 }
249
250 MenuEntry->Signature = MENU_ENTRY_SIGNATURE;
251 return MenuEntry;
252 }
253
254
255 /**
256 Get the Menu Entry from the list in Menu Entry List.
257
258 If MenuNumber is great or equal to the number of Menu
259 Entry in the list, then ASSERT.
260
261 @param MenuOption The Menu Entry List to read the menu entry.
262 @param MenuNumber The index of Menu Entry.
263
264 @return The Menu Entry.
265
266 **/
267 MENU_ENTRY *
268 LibGetMenuEntry (
269 MENU_OPTION *MenuOption,
270 UINTN MenuNumber
271 )
272 {
273 MENU_ENTRY *NewMenuEntry;
274 UINTN Index;
275 LIST_ENTRY *List;
276
277 ASSERT (MenuNumber < MenuOption->MenuNumber);
278
279 List = MenuOption->Head.ForwardLink;
280 for (Index = 0; Index < MenuNumber; Index++) {
281 List = List->ForwardLink;
282 }
283
284 NewMenuEntry = CR (List, MENU_ENTRY, Link, MENU_ENTRY_SIGNATURE);
285
286 return NewMenuEntry;
287 }
288
289 /**
290 Free up all resource allocated for a BM_MENU_ENTRY.
291
292 @param MenuEntry A pointer to BM_MENU_ENTRY.
293
294 **/
295 VOID
296 LibDestroyMenuEntry (
297 MENU_ENTRY *MenuEntry
298 )
299 {
300 FILE_CONTEXT *FileContext;
301
302 FileContext = (FILE_CONTEXT *) MenuEntry->VariableContext;
303
304 if (!FileContext->IsRoot) {
305 if (FileContext->DevicePath != NULL) {
306 FreePool (FileContext->DevicePath);
307 }
308 } else {
309 if (FileContext->FileHandle != NULL) {
310 FileContext->FileHandle->Close (FileContext->FileHandle);
311 }
312 }
313
314 if (FileContext->FileName != NULL) {
315 FreePool (FileContext->FileName);
316 }
317
318 FreePool (FileContext);
319
320 if (MenuEntry->DisplayString != NULL) {
321 FreePool (MenuEntry->DisplayString);
322 }
323 if (MenuEntry->HelpString != NULL) {
324 FreePool (MenuEntry->HelpString);
325 }
326
327 FreePool (MenuEntry);
328 }
329
330
331 /**
332 Free resources allocated in Allocate Rountine.
333
334 @param FreeMenu Menu to be freed
335 **/
336 VOID
337 LibFreeMenu (
338 MENU_OPTION *FreeMenu
339 )
340 {
341 MENU_ENTRY *MenuEntry;
342 while (!IsListEmpty (&FreeMenu->Head)) {
343 MenuEntry = CR (
344 FreeMenu->Head.ForwardLink,
345 MENU_ENTRY,
346 Link,
347 MENU_ENTRY_SIGNATURE
348 );
349 RemoveEntryList (&MenuEntry->Link);
350 LibDestroyMenuEntry (MenuEntry);
351 }
352 FreeMenu->MenuNumber = 0;
353 }
354
355 /**
356
357 Function opens and returns a file handle to the root directory of a volume.
358
359 @param DeviceHandle A handle for a device
360
361 @return A valid file handle or NULL is returned
362
363 **/
364 EFI_FILE_HANDLE
365 LibOpenRoot (
366 IN EFI_HANDLE DeviceHandle
367 )
368 {
369 EFI_STATUS Status;
370 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Volume;
371 EFI_FILE_HANDLE File;
372
373 File = NULL;
374
375 //
376 // File the file system interface to the device
377 //
378 Status = gBS->HandleProtocol (
379 DeviceHandle,
380 &gEfiSimpleFileSystemProtocolGuid,
381 (VOID *) &Volume
382 );
383
384 //
385 // Open the root directory of the volume
386 //
387 if (!EFI_ERROR (Status)) {
388 Status = Volume->OpenVolume (
389 Volume,
390 &File
391 );
392 }
393 //
394 // Done
395 //
396 return EFI_ERROR (Status) ? NULL : File;
397 }
398
399 /**
400 This function converts an input device structure to a Unicode string.
401
402 @param DevPath A pointer to the device path structure.
403
404 @return A new allocated Unicode string that represents the device path.
405
406 **/
407 CHAR16 *
408 LibDevicePathToStr (
409 IN EFI_DEVICE_PATH_PROTOCOL *DevPath
410 )
411 {
412 EFI_STATUS Status;
413 CHAR16 *ToText;
414 EFI_DEVICE_PATH_TO_TEXT_PROTOCOL *DevPathToText;
415
416 if (DevPath == NULL) {
417 return NULL;
418 }
419
420 Status = gBS->LocateProtocol (
421 &gEfiDevicePathToTextProtocolGuid,
422 NULL,
423 (VOID **) &DevPathToText
424 );
425 ASSERT_EFI_ERROR (Status);
426 ToText = DevPathToText->ConvertDevicePathToText (
427 DevPath,
428 FALSE,
429 TRUE
430 );
431 ASSERT (ToText != NULL);
432
433 return ToText;
434 }
435
436 /**
437 Duplicate a string.
438
439 @param Src The source.
440
441 @return A new string which is duplicated copy of the source.
442 @retval NULL If there is not enough memory.
443
444 **/
445 CHAR16 *
446 LibStrDuplicate (
447 IN CHAR16 *Src
448 )
449 {
450 CHAR16 *Dest;
451 UINTN Size;
452
453 Size = StrSize (Src);
454 Dest = AllocateZeroPool (Size);
455 ASSERT (Dest != NULL);
456 if (Dest != NULL) {
457 CopyMem (Dest, Src, Size);
458 }
459
460 return Dest;
461 }
462
463 /**
464
465 Function gets the file information from an open file descriptor, and stores it
466 in a buffer allocated from pool.
467
468 @param FHand File Handle.
469 @param InfoType Info type need to get.
470
471 @retval A pointer to a buffer with file information or NULL is returned
472
473 **/
474 VOID *
475 LibFileInfo (
476 IN EFI_FILE_HANDLE FHand,
477 IN EFI_GUID *InfoType
478 )
479 {
480 EFI_STATUS Status;
481 EFI_FILE_INFO *Buffer;
482 UINTN BufferSize;
483
484 Buffer = NULL;
485 BufferSize = 0;
486
487 Status = FHand->GetInfo (
488 FHand,
489 InfoType,
490 &BufferSize,
491 Buffer
492 );
493 if (Status == EFI_BUFFER_TOO_SMALL) {
494 Buffer = AllocatePool (BufferSize);
495 ASSERT (Buffer != NULL);
496 }
497
498 Status = FHand->GetInfo (
499 FHand,
500 InfoType,
501 &BufferSize,
502 Buffer
503 );
504
505 return Buffer;
506 }
507
508 /**
509
510 Get file type base on the file name.
511 Just cut the file name, from the ".". eg ".efi"
512
513 @param FileName File need to be checked.
514
515 @retval the file type string.
516
517 **/
518 CHAR16*
519 LibGetTypeFromName (
520 IN CHAR16 *FileName
521 )
522 {
523 UINTN Index;
524
525 Index = StrLen (FileName) - 1;
526 while ((FileName[Index] != L'.') && (Index != 0)) {
527 Index--;
528 }
529
530 return Index == 0 ? NULL : &FileName[Index];
531 }
532
533 /**
534 Converts the unicode character of the string from uppercase to lowercase.
535 This is a internal function.
536
537 @param ConfigString String to be converted
538
539 **/
540 VOID
541 LibToLowerString (
542 IN CHAR16 *String
543 )
544 {
545 CHAR16 *TmpStr;
546
547 for (TmpStr = String; *TmpStr != L'\0'; TmpStr++) {
548 if (*TmpStr >= L'A' && *TmpStr <= L'Z') {
549 *TmpStr = (CHAR16) (*TmpStr - L'A' + L'a');
550 }
551 }
552 }
553
554 /**
555
556 Check whether current FileName point to a valid
557 Efi Image File.
558
559 @param FileName File need to be checked.
560
561 @retval TRUE Is Efi Image
562 @retval FALSE Not a valid Efi Image
563
564 **/
565 BOOLEAN
566 LibIsSupportedFileType (
567 IN UINT16 *FileName
568 )
569 {
570 CHAR16 *InputFileType;
571 CHAR16 *TmpStr;
572 BOOLEAN IsSupported;
573
574 if (gFileExplorerPrivate.FileType == NULL) {
575 return TRUE;
576 }
577
578 InputFileType = LibGetTypeFromName (FileName);
579 //
580 // If the file not has *.* style, always return TRUE.
581 //
582 if (InputFileType == NULL) {
583 return TRUE;
584 }
585
586 TmpStr = AllocateCopyPool (StrSize (InputFileType), InputFileType);
587 ASSERT(TmpStr != NULL);
588 LibToLowerString(TmpStr);
589
590 IsSupported = (StrStr (gFileExplorerPrivate.FileType, TmpStr) == NULL ? FALSE : TRUE);
591
592 FreePool (TmpStr);
593 return IsSupported;
594 }
595
596 /**
597
598 Append file name to existing file name.
599
600 @param Str1 The existing file name
601 @param Str2 The file name to be appended
602
603 @return Allocate a new string to hold the appended result.
604 Caller is responsible to free the returned string.
605
606 **/
607 CHAR16 *
608 LibAppendFileName (
609 IN CHAR16 *Str1,
610 IN CHAR16 *Str2
611 )
612 {
613 UINTN Size1;
614 UINTN Size2;
615 UINTN MaxLen;
616 CHAR16 *Str;
617 CHAR16 *TmpStr;
618 CHAR16 *Ptr;
619 CHAR16 *LastSlash;
620
621 Size1 = StrSize (Str1);
622 Size2 = StrSize (Str2);
623 MaxLen = (Size1 + Size2 + sizeof (CHAR16))/ sizeof (CHAR16);
624 Str = AllocateZeroPool (Size1 + Size2 + sizeof (CHAR16));
625 ASSERT (Str != NULL);
626
627 TmpStr = AllocateZeroPool (Size1 + Size2 + sizeof (CHAR16));
628 ASSERT (TmpStr != NULL);
629
630 StrCpyS (Str, MaxLen, Str1);
631 if (!((*Str == '\\') && (*(Str + 1) == 0))) {
632 StrCatS (Str, MaxLen, L"\\");
633 }
634
635 StrCatS (Str, MaxLen, Str2);
636
637 Ptr = Str;
638 LastSlash = Str;
639 while (*Ptr != 0) {
640 if (*Ptr == '\\' && *(Ptr + 1) == '.' && *(Ptr + 2) == '.' && *(Ptr + 3) == L'\\') {
641 //
642 // Convert "\Name\..\" to "\"
643 // DO NOT convert the .. if it is at the end of the string. This will
644 // break the .. behavior in changing directories.
645 //
646
647 //
648 // Use TmpStr as a backup, as StrCpyS in BaseLib does not handle copy of two strings
649 // that overlap.
650 //
651 StrCpyS (TmpStr, MaxLen, Ptr + 3);
652 StrCpyS (LastSlash, MaxLen - (UINTN) (LastSlash - Str), TmpStr);
653 Ptr = LastSlash;
654 } else if (*Ptr == '\\' && *(Ptr + 1) == '.' && *(Ptr + 2) == '\\') {
655 //
656 // Convert a "\.\" to a "\"
657 //
658
659 //
660 // Use TmpStr as a backup, as StrCpyS in BaseLib does not handle copy of two strings
661 // that overlap.
662 //
663 StrCpyS (TmpStr, MaxLen, Ptr + 2);
664 StrCpyS (Ptr, MaxLen - (UINTN) (Ptr - Str), TmpStr);
665 Ptr = LastSlash;
666 } else if (*Ptr == '\\') {
667 LastSlash = Ptr;
668 }
669
670 Ptr++;
671 }
672
673 FreePool (TmpStr);
674
675 return Str;
676 }
677
678 /**
679 This function build the FsOptionMenu list which records all
680 available file system in the system. They includes all instances
681 of EFI_SIMPLE_FILE_SYSTEM_PROTOCOL, all instances of EFI_LOAD_FILE_SYSTEM.
682
683
684 @retval EFI_SUCCESS Success find the file system
685 @retval EFI_OUT_OF_RESOURCES Can not create menu entry
686
687 **/
688 EFI_STATUS
689 LibFindFileSystem (
690 VOID
691 )
692 {
693 UINTN NoSimpleFsHandles;
694 UINTN NoLoadFileHandles;
695 EFI_HANDLE *SimpleFsHandle;
696 EFI_HANDLE *LoadFileHandle;
697 UINT16 *VolumeLabel;
698 UINTN Index;
699 EFI_STATUS Status;
700 MENU_ENTRY *MenuEntry;
701 FILE_CONTEXT *FileContext;
702 UINTN OptionNumber;
703 EFI_FILE_SYSTEM_VOLUME_LABEL *Info;
704
705 NoSimpleFsHandles = 0;
706 NoLoadFileHandles = 0;
707 OptionNumber = 0;
708
709 //
710 // Locate Handles that support Simple File System protocol
711 //
712 Status = gBS->LocateHandleBuffer (
713 ByProtocol,
714 &gEfiSimpleFileSystemProtocolGuid,
715 NULL,
716 &NoSimpleFsHandles,
717 &SimpleFsHandle
718 );
719 if (!EFI_ERROR (Status)) {
720 //
721 // Find all the instances of the File System prototocol
722 //
723 for (Index = 0; Index < NoSimpleFsHandles; Index++) {
724 //
725 // Allocate pool for this load option
726 //
727 MenuEntry = LibCreateMenuEntry ();
728 if (NULL == MenuEntry) {
729 FreePool (SimpleFsHandle);
730 return EFI_OUT_OF_RESOURCES;
731 }
732
733 FileContext = (FILE_CONTEXT *) MenuEntry->VariableContext;
734 FileContext->DeviceHandle = SimpleFsHandle[Index];
735 FileContext->FileHandle = LibOpenRoot (FileContext->DeviceHandle);
736 if (FileContext->FileHandle == NULL) {
737 LibDestroyMenuEntry (MenuEntry);
738 continue;
739 }
740
741 MenuEntry->HelpString = LibDevicePathToStr (DevicePathFromHandle (FileContext->DeviceHandle));
742 FileContext->FileName = LibStrDuplicate (L"\\");
743 FileContext->DevicePath = FileDevicePath (FileContext->DeviceHandle, FileContext->FileName);
744 FileContext->IsDir = TRUE;
745 FileContext->IsRoot = TRUE;
746
747 //
748 // Get current file system's Volume Label
749 //
750 Info = (EFI_FILE_SYSTEM_VOLUME_LABEL *) LibFileInfo (FileContext->FileHandle, &gEfiFileSystemVolumeLabelInfoIdGuid);
751 if (Info == NULL) {
752 VolumeLabel = L"NO FILE SYSTEM INFO";
753 } else {
754 if (Info->VolumeLabel == NULL) {
755 VolumeLabel = L"NULL VOLUME LABEL";
756 } else {
757 VolumeLabel = Info->VolumeLabel;
758 if (*VolumeLabel == 0x0000) {
759 VolumeLabel = L"NO VOLUME LABEL";
760 }
761 }
762 }
763 MenuEntry->DisplayString = AllocateZeroPool (MAX_CHAR);
764 ASSERT (MenuEntry->DisplayString != NULL);
765 UnicodeSPrint (
766 MenuEntry->DisplayString,
767 MAX_CHAR,
768 L"%s, [%s]",
769 VolumeLabel,
770 MenuEntry->HelpString
771 );
772 MenuEntry->DisplayStringToken = HiiSetString (
773 gFileExplorerPrivate.FeHiiHandle,
774 0,
775 MenuEntry->DisplayString,
776 NULL
777 );
778
779 if (Info != NULL)
780 FreePool (Info);
781
782 OptionNumber++;
783 InsertTailList (&gFileExplorerPrivate.FsOptionMenu->Head, &MenuEntry->Link);
784 }
785 }
786
787 if (NoSimpleFsHandles != 0) {
788 FreePool (SimpleFsHandle);
789 }
790
791 //
792 // Searching for handles that support Load File protocol
793 //
794 Status = gBS->LocateHandleBuffer (
795 ByProtocol,
796 &gEfiLoadFileProtocolGuid,
797 NULL,
798 &NoLoadFileHandles,
799 &LoadFileHandle
800 );
801
802 if (!EFI_ERROR (Status)) {
803 for (Index = 0; Index < NoLoadFileHandles; Index++) {
804 MenuEntry = LibCreateMenuEntry ();
805 if (NULL == MenuEntry) {
806 FreePool (LoadFileHandle);
807 return EFI_OUT_OF_RESOURCES;
808 }
809
810 FileContext = (FILE_CONTEXT *) MenuEntry->VariableContext;
811 FileContext->DeviceHandle = LoadFileHandle[Index];
812 FileContext->IsRoot = TRUE;
813
814 FileContext->DevicePath = DevicePathFromHandle (FileContext->DeviceHandle);
815 FileContext->FileName = LibDevicePathToStr (FileContext->DevicePath);
816
817 MenuEntry->HelpString = LibDevicePathToStr (FileContext->DevicePath);
818 MenuEntry->DisplayString = AllocateZeroPool (MAX_CHAR);
819 ASSERT (MenuEntry->DisplayString != NULL);
820 UnicodeSPrint (
821 MenuEntry->DisplayString,
822 MAX_CHAR,
823 L"Load File [%s]",
824 MenuEntry->HelpString
825 );
826 MenuEntry->DisplayStringToken = HiiSetString (
827 gFileExplorerPrivate.FeHiiHandle,
828 0,
829 MenuEntry->DisplayString,
830 NULL
831 );
832
833 OptionNumber++;
834 InsertTailList (&gFileExplorerPrivate.FsOptionMenu->Head, &MenuEntry->Link);
835 }
836 }
837
838 if (NoLoadFileHandles != 0) {
839 FreePool (LoadFileHandle);
840 }
841
842 gFileExplorerPrivate.FsOptionMenu->MenuNumber = OptionNumber;
843
844 return EFI_SUCCESS;
845 }
846
847 /**
848 Find the file handle from the input menu info.
849
850 @param MenuEntry Input Menu info.
851 @param RetFileHandle Return the file handle for the input device path.
852
853 @retval EFI_SUCESS Find the file handle success.
854 @retval Other Find the file handle failure.
855 **/
856 EFI_STATUS
857 LibGetFileHandleFromMenu (
858 IN MENU_ENTRY *MenuEntry,
859 OUT EFI_FILE_HANDLE *RetFileHandle
860 )
861 {
862 EFI_FILE_HANDLE Dir;
863 EFI_FILE_HANDLE NewDir;
864 FILE_CONTEXT *FileContext;
865 EFI_STATUS Status;
866
867 FileContext = (FILE_CONTEXT *) MenuEntry->VariableContext;
868 Dir = FileContext->FileHandle;
869
870 //
871 // Open current directory to get files from it
872 //
873 Status = Dir->Open (
874 Dir,
875 &NewDir,
876 FileContext->FileName,
877 EFI_FILE_READ_ONLY,
878 0
879 );
880 if (EFI_ERROR (Status)) {
881 return Status;
882 }
883
884 if (!FileContext->IsRoot) {
885 Dir->Close (Dir);
886 }
887
888 *RetFileHandle = NewDir;
889
890 return EFI_SUCCESS;
891 }
892
893 /**
894 Find the file handle from the input device path info.
895
896 @param RootDirectory Device path info.
897 @param RetFileHandle Return the file handle for the input device path.
898 @param ParentFileName Parent file name.
899 @param DeviceHandle Driver handle for this partition.
900
901 @retval EFI_SUCESS Find the file handle success.
902 @retval Other Find the file handle failure.
903 **/
904 EFI_STATUS
905 LibGetFileHandleFromDevicePath (
906 IN EFI_DEVICE_PATH_PROTOCOL *RootDirectory,
907 OUT EFI_FILE_HANDLE *RetFileHandle,
908 OUT UINT16 **ParentFileName,
909 OUT EFI_HANDLE *DeviceHandle
910 )
911 {
912 EFI_DEVICE_PATH_PROTOCOL *DevicePathNode;
913 EFI_DEVICE_PATH_PROTOCOL *TempDevicePathNode;
914 EFI_STATUS Status;
915 EFI_HANDLE Handle;
916 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Volume;
917 EFI_FILE_HANDLE FileHandle;
918 EFI_FILE_HANDLE LastHandle;
919 CHAR16 *TempPath;
920
921 *ParentFileName = NULL;
922
923 //
924 // Attempt to access the file via a file system interface
925 //
926 DevicePathNode = RootDirectory;
927 Status = gBS->LocateDevicePath (&gEfiSimpleFileSystemProtocolGuid, &DevicePathNode, &Handle);
928 if (EFI_ERROR (Status)) {
929 return Status;
930 }
931
932 Status = gBS->HandleProtocol (Handle, &gEfiSimpleFileSystemProtocolGuid, (VOID**)&Volume);
933 if (EFI_ERROR (Status)) {
934 return Status;
935 }
936
937 //
938 // Open the Volume to get the File System handle
939 //
940 Status = Volume->OpenVolume (Volume, &FileHandle);
941 if (EFI_ERROR (Status)) {
942 return Status;
943 }
944
945 *DeviceHandle = Handle;
946
947 if (IsDevicePathEnd(DevicePathNode)) {
948 *ParentFileName = AllocateCopyPool (StrSize (L"\\"), L"\\");
949 *RetFileHandle = FileHandle;
950 return EFI_SUCCESS;
951 }
952
953 //
954 // Duplicate the device path to avoid the access to unaligned device path node.
955 // Because the device path consists of one or more FILE PATH MEDIA DEVICE PATH
956 // nodes, It assures the fields in device path nodes are 2 byte aligned.
957 //
958 TempDevicePathNode = DuplicateDevicePath (DevicePathNode);
959 if (TempDevicePathNode == NULL) {
960
961 //
962 // Setting Status to an EFI_ERROR value will cause the rest of
963 // the file system support below to be skipped.
964 //
965 Status = EFI_OUT_OF_RESOURCES;
966 }
967
968 //
969 // Parse each MEDIA_FILEPATH_DP node. There may be more than one, since the
970 // directory information and filename can be seperate. The goal is to inch
971 // our way down each device path node and close the previous node
972 //
973 DevicePathNode = TempDevicePathNode;
974 while (!EFI_ERROR (Status) && !IsDevicePathEnd (DevicePathNode)) {
975 if (DevicePathType (DevicePathNode) != MEDIA_DEVICE_PATH ||
976 DevicePathSubType (DevicePathNode) != MEDIA_FILEPATH_DP) {
977 Status = EFI_UNSUPPORTED;
978 goto Done;
979 }
980
981 LastHandle = FileHandle;
982 FileHandle = NULL;
983
984 Status = LastHandle->Open (
985 LastHandle,
986 &FileHandle,
987 ((FILEPATH_DEVICE_PATH *) DevicePathNode)->PathName,
988 EFI_FILE_MODE_READ,
989 0
990 );
991 if (*ParentFileName == NULL) {
992 *ParentFileName = AllocateCopyPool (StrSize (((FILEPATH_DEVICE_PATH *) DevicePathNode)->PathName), ((FILEPATH_DEVICE_PATH *) DevicePathNode)->PathName);
993 } else {
994 TempPath = LibAppendFileName (*ParentFileName, ((FILEPATH_DEVICE_PATH *) DevicePathNode)->PathName);
995 FreePool (*ParentFileName);
996 *ParentFileName = TempPath;
997 }
998
999 //
1000 // Close the previous node
1001 //
1002 LastHandle->Close (LastHandle);
1003
1004 DevicePathNode = NextDevicePathNode (DevicePathNode);
1005 }
1006
1007 if (EFI_ERROR (Status)) {
1008 goto Done;
1009 }
1010
1011 *RetFileHandle = FileHandle;
1012
1013 Status = EFI_SUCCESS;
1014
1015 Done:
1016 if (TempDevicePathNode != NULL) {
1017 FreePool (TempDevicePathNode);
1018 }
1019
1020 if ((FileHandle != NULL) && (EFI_ERROR (Status))) {
1021 FileHandle->Close (FileHandle);
1022 }
1023
1024 return Status;
1025 }
1026
1027 /**
1028 Find files under current directory.
1029
1030 All files and sub-directories in current directory
1031 will be stored in DirectoryMenu for future use.
1032
1033 @param FileHandle Parent file handle.
1034 @param FileName Parent file name.
1035 @param DeviceHandle Driver handle for this partition.
1036
1037 @retval EFI_SUCCESS Get files from current dir successfully.
1038 @return Other value if can't get files from current dir.
1039
1040 **/
1041 EFI_STATUS
1042 LibFindFiles (
1043 IN EFI_FILE_HANDLE FileHandle,
1044 IN UINT16 *FileName,
1045 IN EFI_HANDLE DeviceHandle
1046 )
1047 {
1048 EFI_FILE_INFO *DirInfo;
1049 UINTN BufferSize;
1050 UINTN DirBufferSize;
1051 MENU_ENTRY *NewMenuEntry;
1052 FILE_CONTEXT *NewFileContext;
1053 UINTN Pass;
1054 EFI_STATUS Status;
1055 UINTN OptionNumber;
1056
1057 OptionNumber = 0;
1058
1059 DirBufferSize = sizeof (EFI_FILE_INFO) + 1024;
1060 DirInfo = AllocateZeroPool (DirBufferSize);
1061 if (DirInfo == NULL) {
1062 return EFI_OUT_OF_RESOURCES;
1063 }
1064
1065 //
1066 // Get all files in current directory
1067 // Pass 1 to get Directories
1068 // Pass 2 to get files that are EFI images
1069 //
1070 for (Pass = 1; Pass <= 2; Pass++) {
1071 FileHandle->SetPosition (FileHandle, 0);
1072 for (;;) {
1073 BufferSize = DirBufferSize;
1074 Status = FileHandle->Read (FileHandle, &BufferSize, DirInfo);
1075 if (EFI_ERROR (Status) || BufferSize == 0) {
1076 break;
1077 }
1078
1079 if (((DirInfo->Attribute & EFI_FILE_DIRECTORY) != 0 && Pass == 2) ||
1080 ((DirInfo->Attribute & EFI_FILE_DIRECTORY) == 0 && Pass == 1)
1081 ) {
1082 //
1083 // Pass 1 is for Directories
1084 // Pass 2 is for file names
1085 //
1086 continue;
1087 }
1088
1089 if (!((DirInfo->Attribute & EFI_FILE_DIRECTORY) != 0 || LibIsSupportedFileType (DirInfo->FileName))) {
1090 //
1091 // Slip file unless it is a directory entry or a .EFI file
1092 //
1093 continue;
1094 }
1095
1096 NewMenuEntry = LibCreateMenuEntry ();
1097 if (NULL == NewMenuEntry) {
1098 return EFI_OUT_OF_RESOURCES;
1099 }
1100
1101 NewFileContext = (FILE_CONTEXT *) NewMenuEntry->VariableContext;
1102 NewFileContext->DeviceHandle = DeviceHandle;
1103 NewFileContext->FileName = LibAppendFileName (FileName, DirInfo->FileName);
1104 NewFileContext->FileHandle = FileHandle;
1105 NewFileContext->DevicePath = FileDevicePath (NewFileContext->DeviceHandle, NewFileContext->FileName);
1106 NewMenuEntry->HelpString = NULL;
1107 NewFileContext->IsDir = (BOOLEAN) ((DirInfo->Attribute & EFI_FILE_DIRECTORY) == EFI_FILE_DIRECTORY);
1108
1109 if (NewFileContext->IsDir) {
1110 BufferSize = StrLen (DirInfo->FileName) * 2 + 6;
1111 NewMenuEntry->DisplayString = AllocateZeroPool (BufferSize);
1112 UnicodeSPrint (
1113 NewMenuEntry->DisplayString,
1114 BufferSize,
1115 L"<%s>",
1116 DirInfo->FileName
1117 );
1118 } else {
1119 NewMenuEntry->DisplayString = LibStrDuplicate (DirInfo->FileName);
1120 }
1121
1122 NewMenuEntry->DisplayStringToken = HiiSetString (
1123 gFileExplorerPrivate.FeHiiHandle,
1124 0,
1125 NewMenuEntry->DisplayString,
1126 NULL
1127 );
1128
1129 NewFileContext->IsRoot = FALSE;
1130
1131 OptionNumber++;
1132 InsertTailList (&gFileExplorerPrivate.FsOptionMenu->Head, &NewMenuEntry->Link);
1133 }
1134 }
1135
1136 gFileExplorerPrivate.FsOptionMenu->MenuNumber = OptionNumber;
1137
1138 FreePool (DirInfo);
1139
1140 return EFI_SUCCESS;
1141 }
1142
1143 /**
1144 Refresh the global UpdateData structure.
1145
1146 **/
1147 VOID
1148 LibRefreshUpdateData (
1149 VOID
1150 )
1151 {
1152 //
1153 // Free current updated date
1154 //
1155 if (mLibStartOpCodeHandle != NULL) {
1156 HiiFreeOpCodeHandle (mLibStartOpCodeHandle);
1157 }
1158 if (mLibEndOpCodeHandle != NULL) {
1159 HiiFreeOpCodeHandle (mLibEndOpCodeHandle);
1160 }
1161
1162 //
1163 // Create new OpCode Handle
1164 //
1165 mLibStartOpCodeHandle = HiiAllocateOpCodeHandle ();
1166 mLibEndOpCodeHandle = HiiAllocateOpCodeHandle ();
1167
1168 //
1169 // Create Hii Extend Label OpCode as the start opcode
1170 //
1171 mLibStartLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode (
1172 mLibStartOpCodeHandle,
1173 &gEfiIfrTianoGuid,
1174 NULL,
1175 sizeof (EFI_IFR_GUID_LABEL)
1176 );
1177 mLibStartLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL;
1178
1179 mLibStartLabel->Number = FORM_FILE_EXPLORER_ID;
1180
1181 //
1182 // Create Hii Extend Label OpCode as the start opcode
1183 //
1184 mLibEndLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode (
1185 mLibEndOpCodeHandle,
1186 &gEfiIfrTianoGuid,
1187 NULL,
1188 sizeof (EFI_IFR_GUID_LABEL)
1189 );
1190 mLibEndLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL;
1191
1192 mLibEndLabel->Number = LABEL_END;
1193 }
1194
1195 /**
1196
1197 Update the File Explore page.
1198
1199 **/
1200 VOID
1201 LibUpdateFileExplorePage (
1202 VOID
1203 )
1204 {
1205 UINTN Index;
1206 MENU_ENTRY *NewMenuEntry;
1207 FILE_CONTEXT *NewFileContext;
1208 MENU_OPTION *MenuOption;
1209
1210 NewMenuEntry = NULL;
1211 NewFileContext = NULL;
1212
1213 LibRefreshUpdateData ();
1214 MenuOption = gFileExplorerPrivate.FsOptionMenu;
1215
1216 for (Index = 0; Index < MenuOption->MenuNumber; Index++) {
1217 NewMenuEntry = LibGetMenuEntry (MenuOption, Index);
1218 NewFileContext = (FILE_CONTEXT *) NewMenuEntry->VariableContext;
1219
1220 if (!NewFileContext->IsDir) {
1221 //
1222 // Create Text opcode for directory, also create Text opcode for file in FileExplorerStateBootFromFile.
1223 //
1224 HiiCreateActionOpCode (
1225 mLibStartOpCodeHandle,
1226 (UINT16) (FILE_OPTION_OFFSET + Index),
1227 NewMenuEntry->DisplayStringToken,
1228 STRING_TOKEN (STR_NULL_STRING),
1229 EFI_IFR_FLAG_CALLBACK,
1230 0
1231 );
1232 } else {
1233 //
1234 // Create Goto opcode for file in FileExplorerStateAddBootOption or FileExplorerStateAddDriverOptionState.
1235 //
1236 HiiCreateGotoOpCode (
1237 mLibStartOpCodeHandle,
1238 FORM_FILE_EXPLORER_ID,
1239 NewMenuEntry->DisplayStringToken,
1240 STRING_TOKEN (STR_NULL_STRING),
1241 EFI_IFR_FLAG_CALLBACK,
1242 (UINT16) (FILE_OPTION_OFFSET + Index)
1243 );
1244 }
1245 }
1246
1247 HiiUpdateForm (
1248 gFileExplorerPrivate.FeHiiHandle,
1249 &FileExplorerGuid,
1250 FORM_FILE_EXPLORER_ID,
1251 mLibStartOpCodeHandle, // Label FORM_FILE_EXPLORER_ID
1252 mLibEndOpCodeHandle // LABEL_END
1253 );
1254 }
1255
1256 /**
1257 Update the file explower page with the refershed file system.
1258
1259 @param KeyValue Key value to identify the type of data to expect.
1260
1261 @retval EFI_SUCCESS Update the file explorer form success.
1262 @retval other errors Error occur when parse one directory.
1263
1264 **/
1265 EFI_STATUS
1266 LibUpdateFileExplorer (
1267 IN UINT16 KeyValue
1268 )
1269 {
1270 UINT16 FileOptionMask;
1271 MENU_ENTRY *NewMenuEntry;
1272 FILE_CONTEXT *NewFileContext;
1273 EFI_STATUS Status;
1274 EFI_FILE_HANDLE FileHandle;
1275
1276 Status = EFI_SUCCESS;
1277 FileOptionMask = (UINT16) (FILE_OPTION_MASK & KeyValue);
1278 NewMenuEntry = LibGetMenuEntry (gFileExplorerPrivate.FsOptionMenu, FileOptionMask);
1279 NewFileContext = (FILE_CONTEXT *) NewMenuEntry->VariableContext;
1280
1281 if (NewFileContext->IsDir) {
1282 RemoveEntryList (&NewMenuEntry->Link);
1283 LibFreeMenu (gFileExplorerPrivate.FsOptionMenu);
1284 LibGetFileHandleFromMenu (NewMenuEntry, &FileHandle);
1285 Status = LibFindFiles (FileHandle, NewFileContext->FileName, NewFileContext->DeviceHandle);
1286 if (!EFI_ERROR (Status)) {
1287 LibUpdateFileExplorePage ();
1288 } else {
1289 LibFreeMenu (gFileExplorerPrivate.FsOptionMenu);
1290 }
1291 LibDestroyMenuEntry (NewMenuEntry);
1292 }
1293
1294 return Status;
1295 }
1296
1297 /**
1298 Get the device path info saved in the menu structure.
1299
1300 @param KeyValue Key value to identify the type of data to expect.
1301
1302 **/
1303 VOID
1304 LibGetDevicePath (
1305 IN UINT16 KeyValue
1306 )
1307 {
1308 UINT16 FileOptionMask;
1309 MENU_ENTRY *NewMenuEntry;
1310 FILE_CONTEXT *NewFileContext;
1311
1312 FileOptionMask = (UINT16) (FILE_OPTION_MASK & KeyValue);
1313
1314 NewMenuEntry = LibGetMenuEntry (gFileExplorerPrivate.FsOptionMenu, FileOptionMask);
1315
1316 NewFileContext = (FILE_CONTEXT *) NewMenuEntry->VariableContext;
1317
1318 if (gFileExplorerPrivate.RetDevicePath != NULL) {
1319 FreePool (gFileExplorerPrivate.RetDevicePath);
1320 }
1321 gFileExplorerPrivate.RetDevicePath = DuplicateDevicePath (NewFileContext->DevicePath);
1322 }
1323
1324 /**
1325 Choose a file in the specified directory.
1326
1327 If user input NULL for the RootDirectory, will choose file in the system.
1328
1329 If user input *File != NULL, function will return the allocate device path
1330 info for the choosed file, caller has to free the memory after use it.
1331
1332 @param RootDirectory Pointer to the root directory.
1333 @param FileType The file type need to choose.
1334 @param ChooseHandler Function pointer to the extra task need to do
1335 after choose one file.
1336 @param File Return the device path for the last time chosed file.
1337
1338 @retval EFI_SUCESS Choose file success.
1339 @retval EFI_INVALID_PARAMETER Both ChooseHandler and return device path are NULL
1340 One of them must not NULL.
1341 @retval Other errors Choose file failed.
1342 **/
1343 EFI_STATUS
1344 EFIAPI
1345 ChooseFile (
1346 IN EFI_DEVICE_PATH_PROTOCOL *RootDirectory,
1347 IN CHAR16 *FileType, OPTIONAL
1348 IN CHOOSE_HANDLER ChooseHandler, OPTIONAL
1349 OUT EFI_DEVICE_PATH_PROTOCOL **File OPTIONAL
1350 )
1351 {
1352 EFI_FILE_HANDLE FileHandle;
1353 EFI_STATUS Status;
1354 UINT16 *FileName;
1355 EFI_HANDLE DeviceHandle;
1356
1357 if ((ChooseHandler == NULL) && (File == NULL)) {
1358 return EFI_INVALID_PARAMETER;
1359 }
1360
1361 FileName = NULL;
1362
1363 gFileExplorerPrivate.RetDevicePath = NULL;
1364 gFileExplorerPrivate.ChooseHandler = ChooseHandler;
1365 if (FileType != NULL) {
1366 gFileExplorerPrivate.FileType = AllocateCopyPool (StrSize (FileType), FileType);
1367 ASSERT(gFileExplorerPrivate.FileType != NULL);
1368 LibToLowerString(gFileExplorerPrivate.FileType);
1369 } else {
1370 gFileExplorerPrivate.FileType = NULL;
1371 }
1372
1373 if (RootDirectory == NULL) {
1374 Status = LibFindFileSystem();
1375 } else {
1376 Status = LibGetFileHandleFromDevicePath(RootDirectory, &FileHandle, &FileName, &DeviceHandle);
1377 if (EFI_ERROR (Status)) {
1378 goto Done;
1379 }
1380
1381 Status = LibFindFiles (FileHandle, FileName, DeviceHandle);
1382 }
1383 if (EFI_ERROR (Status)) {
1384 goto Done;
1385 }
1386
1387 LibUpdateFileExplorePage();
1388
1389 gFileExplorerPrivate.FormBrowser2->SendForm (
1390 gFileExplorerPrivate.FormBrowser2,
1391 &gFileExplorerPrivate.FeHiiHandle,
1392 1,
1393 &FileExplorerGuid,
1394 0,
1395 NULL,
1396 NULL
1397 );
1398
1399 Done:
1400 if ((Status == EFI_SUCCESS) && (File != NULL)) {
1401 *File = gFileExplorerPrivate.RetDevicePath;
1402 } else if (gFileExplorerPrivate.RetDevicePath != NULL) {
1403 FreePool (gFileExplorerPrivate.RetDevicePath);
1404 }
1405
1406 if (gFileExplorerPrivate.FileType != NULL) {
1407 FreePool (gFileExplorerPrivate.FileType);
1408 }
1409
1410 LibFreeMenu (gFileExplorerPrivate.FsOptionMenu);
1411
1412 if (FileName != NULL) {
1413 FreePool (FileName);
1414 }
1415
1416 return Status;
1417 }
1418
1419 /**
1420
1421 Install Boot Manager Menu driver.
1422
1423 @param ImageHandle The image handle.
1424 @param SystemTable The system table.
1425
1426 @retval EFI_SUCEESS Install File explorer library success.
1427
1428 **/
1429 EFI_STATUS
1430 EFIAPI
1431 FileExplorerLibConstructor (
1432 IN EFI_HANDLE ImageHandle,
1433 IN EFI_SYSTEM_TABLE *SystemTable
1434 )
1435 {
1436 EFI_STATUS Status;
1437
1438 gHiiVendorDevicePath = (HII_VENDOR_DEVICE_PATH*) DuplicateDevicePath ((EFI_DEVICE_PATH_PROTOCOL*)&FeHiiVendorDevicePath);
1439 ASSERT (gHiiVendorDevicePath != NULL);
1440 CopyGuid (&gHiiVendorDevicePath->VendorDevicePath.Guid, &gEfiCallerIdGuid);
1441
1442 //
1443 // Install Device Path Protocol and Config Access protocol to driver handle
1444 //
1445 Status = gBS->InstallMultipleProtocolInterfaces (
1446 &gFileExplorerPrivate.FeDriverHandle,
1447 &gEfiDevicePathProtocolGuid,
1448 gHiiVendorDevicePath,
1449 &gEfiHiiConfigAccessProtocolGuid,
1450 &gFileExplorerPrivate.FeConfigAccess,
1451 NULL
1452 );
1453 if (Status == EFI_ALREADY_STARTED) {
1454 return EFI_SUCCESS;
1455 }
1456 if (EFI_ERROR (Status)) {
1457 return Status;
1458 }
1459
1460 //
1461 // Post our File Explorer VFR binary to the HII database.
1462 //
1463 gFileExplorerPrivate.FeHiiHandle = HiiAddPackages (
1464 &FileExplorerGuid,
1465 gFileExplorerPrivate.FeDriverHandle,
1466 FileExplorerVfrBin,
1467 FileExplorerLibStrings,
1468 NULL
1469 );
1470 ASSERT (gFileExplorerPrivate.FeHiiHandle != NULL);
1471
1472 //
1473 // Locate Formbrowser2 protocol
1474 //
1475 Status = gBS->LocateProtocol (&gEfiFormBrowser2ProtocolGuid, NULL, (VOID **) &gFileExplorerPrivate.FormBrowser2);
1476 ASSERT_EFI_ERROR (Status);
1477
1478 InitializeListHead (&gFileExplorerPrivate.FsOptionMenu->Head);
1479
1480 return EFI_SUCCESS;
1481 }
1482
1483 /**
1484 Unloads the application and its installed protocol.
1485
1486 @param[in] ImageHandle Handle that identifies the image to be unloaded.
1487 @param[in] SystemTable The system table.
1488
1489 @retval EFI_SUCCESS The image has been unloaded.
1490 **/
1491 EFI_STATUS
1492 EFIAPI
1493 FileExplorerLibDestructor (
1494 IN EFI_HANDLE ImageHandle,
1495 IN EFI_SYSTEM_TABLE *SystemTable
1496 )
1497 {
1498 EFI_STATUS Status;
1499
1500 ASSERT (gHiiVendorDevicePath != NULL);
1501
1502 if (gFileExplorerPrivate.FeDriverHandle != NULL) {
1503 Status = gBS->UninstallMultipleProtocolInterfaces (
1504 gFileExplorerPrivate.FeDriverHandle,
1505 &gEfiDevicePathProtocolGuid,
1506 gHiiVendorDevicePath,
1507 &gEfiHiiConfigAccessProtocolGuid,
1508 &gFileExplorerPrivate.FeConfigAccess,
1509 NULL
1510 );
1511 ASSERT_EFI_ERROR (Status);
1512
1513 HiiRemovePackages (gFileExplorerPrivate.FeHiiHandle);
1514 }
1515
1516 FreePool (gHiiVendorDevicePath);
1517
1518 return EFI_SUCCESS;
1519 }
1520