1/** @file
2  Provide boot option support for Application "BootMaint"
3
4  Include file system navigation, system handle selection
5
6  Boot option manipulation
7
8Copyright (c) 2004 - 2015, Intel Corporation. All rights reserved.<BR>
9This program and the accompanying materials
10are licensed and made available under the terms and conditions of the BSD License
11which accompanies this distribution.  The full text of the license may be found at
12http://opensource.org/licenses/bsd-license.php
13
14THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
15WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
16
17**/
18
19#include "BootMaint.h"
20#include "BBSsupport.h"
21
22/**
23  Create a menu entry by given menu type.
24
25  @param MenuType        The Menu type to be created.
26
27  @retval NULL           If failed to create the menu.
28  @return the new menu entry.
29
30**/
31BM_MENU_ENTRY *
32BOpt_CreateMenuEntry (
33  UINTN           MenuType
34  )
35{
36  BM_MENU_ENTRY *MenuEntry;
37  UINTN         ContextSize;
38
39  //
40  // Get context size according to menu type
41  //
42  switch (MenuType) {
43  case BM_LOAD_CONTEXT_SELECT:
44    ContextSize = sizeof (BM_LOAD_CONTEXT);
45    break;
46
47  case BM_FILE_CONTEXT_SELECT:
48    ContextSize = sizeof (BM_FILE_CONTEXT);
49    break;
50
51  case BM_CONSOLE_CONTEXT_SELECT:
52    ContextSize = sizeof (BM_CONSOLE_CONTEXT);
53    break;
54
55  case BM_TERMINAL_CONTEXT_SELECT:
56    ContextSize = sizeof (BM_TERMINAL_CONTEXT);
57    break;
58
59  case BM_HANDLE_CONTEXT_SELECT:
60    ContextSize = sizeof (BM_HANDLE_CONTEXT);
61    break;
62
63  case BM_LEGACY_DEV_CONTEXT_SELECT:
64    ContextSize = sizeof (BM_LEGACY_DEVICE_CONTEXT);
65    break;
66
67  default:
68    ContextSize = 0;
69    break;
70  }
71
72  if (ContextSize == 0) {
73    return NULL;
74  }
75
76  //
77  // Create new menu entry
78  //
79  MenuEntry = AllocateZeroPool (sizeof (BM_MENU_ENTRY));
80  if (MenuEntry == NULL) {
81    return NULL;
82  }
83
84  MenuEntry->VariableContext = AllocateZeroPool (ContextSize);
85  if (MenuEntry->VariableContext == NULL) {
86    FreePool (MenuEntry);
87    return NULL;
88  }
89
90  MenuEntry->Signature        = BM_MENU_ENTRY_SIGNATURE;
91  MenuEntry->ContextSelection = MenuType;
92  return MenuEntry;
93}
94
95/**
96  Free up all resource allocated for a BM_MENU_ENTRY.
97
98  @param MenuEntry   A pointer to BM_MENU_ENTRY.
99
100**/
101VOID
102BOpt_DestroyMenuEntry (
103  BM_MENU_ENTRY         *MenuEntry
104  )
105{
106  BM_LOAD_CONTEXT           *LoadContext;
107  BM_FILE_CONTEXT           *FileContext;
108  BM_CONSOLE_CONTEXT        *ConsoleContext;
109  BM_TERMINAL_CONTEXT       *TerminalContext;
110  BM_HANDLE_CONTEXT         *HandleContext;
111  BM_LEGACY_DEVICE_CONTEXT  *LegacyDevContext;
112
113  //
114  //  Select by the type in Menu entry for current context type
115  //
116  switch (MenuEntry->ContextSelection) {
117  case BM_LOAD_CONTEXT_SELECT:
118    LoadContext = (BM_LOAD_CONTEXT *) MenuEntry->VariableContext;
119    FreePool (LoadContext->FilePathList);
120    FreePool (LoadContext->LoadOption);
121    if (LoadContext->OptionalData != NULL) {
122      FreePool (LoadContext->OptionalData);
123    }
124    FreePool (LoadContext);
125    break;
126
127  case BM_FILE_CONTEXT_SELECT:
128    FileContext = (BM_FILE_CONTEXT *) MenuEntry->VariableContext;
129
130    if (!FileContext->IsRoot) {
131      FreePool (FileContext->DevicePath);
132    } else {
133      if (FileContext->FHandle != NULL) {
134        FileContext->FHandle->Close (FileContext->FHandle);
135      }
136    }
137
138    if (FileContext->FileName != NULL) {
139      FreePool (FileContext->FileName);
140    }
141    if (FileContext->Info != NULL) {
142      FreePool (FileContext->Info);
143    }
144    FreePool (FileContext);
145    break;
146
147  case BM_CONSOLE_CONTEXT_SELECT:
148    ConsoleContext = (BM_CONSOLE_CONTEXT *) MenuEntry->VariableContext;
149    FreePool (ConsoleContext->DevicePath);
150    FreePool (ConsoleContext);
151    break;
152
153  case BM_TERMINAL_CONTEXT_SELECT:
154    TerminalContext = (BM_TERMINAL_CONTEXT *) MenuEntry->VariableContext;
155    FreePool (TerminalContext->DevicePath);
156    FreePool (TerminalContext);
157    break;
158
159  case BM_HANDLE_CONTEXT_SELECT:
160    HandleContext = (BM_HANDLE_CONTEXT *) MenuEntry->VariableContext;
161    FreePool (HandleContext);
162    break;
163
164  case BM_LEGACY_DEV_CONTEXT_SELECT:
165    LegacyDevContext = (BM_LEGACY_DEVICE_CONTEXT *) MenuEntry->VariableContext;
166    FreePool (LegacyDevContext);
167
168  default:
169    break;
170  }
171
172  FreePool (MenuEntry->DisplayString);
173  if (MenuEntry->HelpString != NULL) {
174    FreePool (MenuEntry->HelpString);
175  }
176
177  FreePool (MenuEntry);
178}
179
180/**
181  Get the Menu Entry from the list in Menu Entry List.
182
183  If MenuNumber is great or equal to the number of Menu
184  Entry in the list, then ASSERT.
185
186  @param MenuOption      The Menu Entry List to read the menu entry.
187  @param MenuNumber      The index of Menu Entry.
188
189  @return The Menu Entry.
190
191**/
192BM_MENU_ENTRY *
193BOpt_GetMenuEntry (
194  BM_MENU_OPTION      *MenuOption,
195  UINTN               MenuNumber
196  )
197{
198  BM_MENU_ENTRY   *NewMenuEntry;
199  UINTN           Index;
200  LIST_ENTRY      *List;
201
202  ASSERT (MenuNumber < MenuOption->MenuNumber);
203
204  List = MenuOption->Head.ForwardLink;
205  for (Index = 0; Index < MenuNumber; Index++) {
206    List = List->ForwardLink;
207  }
208
209  NewMenuEntry = CR (List, BM_MENU_ENTRY, Link, BM_MENU_ENTRY_SIGNATURE);
210
211  return NewMenuEntry;
212}
213
214/**
215  This function build the FsOptionMenu list which records all
216  available file system in the system. They includes all instances
217  of EFI_SIMPLE_FILE_SYSTEM_PROTOCOL, all instances of EFI_LOAD_FILE_SYSTEM
218  and all type of legacy boot device.
219
220  @param CallbackData    BMM context data
221
222  @retval  EFI_SUCCESS             Success find the file system
223  @retval  EFI_OUT_OF_RESOURCES    Can not create menu entry
224
225**/
226EFI_STATUS
227BOpt_FindFileSystem (
228  IN BMM_CALLBACK_DATA          *CallbackData
229  )
230{
231  UINTN                     NoBlkIoHandles;
232  UINTN                     NoSimpleFsHandles;
233  UINTN                     NoLoadFileHandles;
234  EFI_HANDLE                *BlkIoHandle;
235  EFI_HANDLE                *SimpleFsHandle;
236  EFI_HANDLE                *LoadFileHandle;
237  UINT16                    *VolumeLabel;
238  EFI_BLOCK_IO_PROTOCOL     *BlkIo;
239  UINTN                     Index;
240  EFI_STATUS                Status;
241  BM_MENU_ENTRY             *MenuEntry;
242  BM_FILE_CONTEXT           *FileContext;
243  UINT16                    *TempStr;
244  UINTN                     OptionNumber;
245  VOID                      *Buffer;
246  EFI_LEGACY_BIOS_PROTOCOL  *LegacyBios;
247  UINT16                    DeviceType;
248  BBS_BBS_DEVICE_PATH       BbsDevicePathNode;
249  EFI_DEVICE_PATH_PROTOCOL  *DevicePath;
250  BOOLEAN                   RemovableMedia;
251
252
253  NoSimpleFsHandles = 0;
254  NoLoadFileHandles = 0;
255  OptionNumber      = 0;
256  InitializeListHead (&FsOptionMenu.Head);
257
258  //
259  // Locate Handles that support BlockIo protocol
260  //
261  Status = gBS->LocateHandleBuffer (
262                  ByProtocol,
263                  &gEfiBlockIoProtocolGuid,
264                  NULL,
265                  &NoBlkIoHandles,
266                  &BlkIoHandle
267                  );
268  if (!EFI_ERROR (Status)) {
269
270    for (Index = 0; Index < NoBlkIoHandles; Index++) {
271      Status = gBS->HandleProtocol (
272                      BlkIoHandle[Index],
273                      &gEfiBlockIoProtocolGuid,
274                      (VOID **) &BlkIo
275                      );
276
277      if (EFI_ERROR (Status)) {
278        continue;
279      }
280
281      //
282      // Issue a dummy read to trigger reinstall of BlockIo protocol for removable media
283      //
284      if (BlkIo->Media->RemovableMedia) {
285        Buffer = AllocateZeroPool (BlkIo->Media->BlockSize);
286        if (NULL == Buffer) {
287          FreePool (BlkIoHandle);
288          return EFI_OUT_OF_RESOURCES;
289        }
290
291        BlkIo->ReadBlocks (
292                BlkIo,
293                BlkIo->Media->MediaId,
294                0,
295                BlkIo->Media->BlockSize,
296                Buffer
297                );
298        FreePool (Buffer);
299      }
300    }
301    FreePool (BlkIoHandle);
302  }
303
304  //
305  // Locate Handles that support Simple File System protocol
306  //
307  Status = gBS->LocateHandleBuffer (
308                  ByProtocol,
309                  &gEfiSimpleFileSystemProtocolGuid,
310                  NULL,
311                  &NoSimpleFsHandles,
312                  &SimpleFsHandle
313                  );
314  if (!EFI_ERROR (Status)) {
315    //
316    // Find all the instances of the File System prototocol
317    //
318    for (Index = 0; Index < NoSimpleFsHandles; Index++) {
319      Status = gBS->HandleProtocol (
320                      SimpleFsHandle[Index],
321                      &gEfiBlockIoProtocolGuid,
322                      (VOID **) &BlkIo
323                      );
324      if (EFI_ERROR (Status)) {
325        //
326        // If no block IO exists assume it's NOT a removable media
327        //
328        RemovableMedia = FALSE;
329      } else {
330        //
331        // If block IO exists check to see if it's remobable media
332        //
333        RemovableMedia = BlkIo->Media->RemovableMedia;
334      }
335
336      //
337      // Allocate pool for this load option
338      //
339      MenuEntry = BOpt_CreateMenuEntry (BM_FILE_CONTEXT_SELECT);
340      if (NULL == MenuEntry) {
341        FreePool (SimpleFsHandle);
342        return EFI_OUT_OF_RESOURCES;
343      }
344
345      FileContext = (BM_FILE_CONTEXT *) MenuEntry->VariableContext;
346
347      FileContext->Handle     = SimpleFsHandle[Index];
348      MenuEntry->OptionNumber = Index;
349      FileContext->FHandle    = EfiLibOpenRoot (FileContext->Handle);
350      if (FileContext->FHandle == NULL) {
351        BOpt_DestroyMenuEntry (MenuEntry);
352        continue;
353      }
354
355      MenuEntry->HelpString = DevicePathToStr (DevicePathFromHandle (FileContext->Handle));
356      FileContext->Info = EfiLibFileSystemVolumeLabelInfo (FileContext->FHandle);
357      FileContext->FileName = EfiStrDuplicate (L"\\");
358      FileContext->DevicePath = FileDevicePath (
359                                  FileContext->Handle,
360                                  FileContext->FileName
361                                  );
362      FileContext->IsDir            = TRUE;
363      FileContext->IsRoot           = TRUE;
364      FileContext->IsRemovableMedia = RemovableMedia;
365      FileContext->IsLoadFile       = FALSE;
366
367      //
368      // Get current file system's Volume Label
369      //
370      if (FileContext->Info == NULL) {
371        VolumeLabel = L"NO FILE SYSTEM INFO";
372      } else {
373        VolumeLabel = FileContext->Info->VolumeLabel;
374        if (*VolumeLabel == 0x0000) {
375          VolumeLabel = L"NO VOLUME LABEL";
376        }
377      }
378
379      TempStr                   = MenuEntry->HelpString;
380      MenuEntry->DisplayString  = AllocateZeroPool (MAX_CHAR);
381      ASSERT (MenuEntry->DisplayString != NULL);
382      UnicodeSPrint (
383        MenuEntry->DisplayString,
384        MAX_CHAR,
385        L"%s, [%s]",
386        VolumeLabel,
387        TempStr
388        );
389      OptionNumber++;
390      InsertTailList (&FsOptionMenu.Head, &MenuEntry->Link);
391    }
392  }
393
394  if (NoSimpleFsHandles != 0) {
395    FreePool (SimpleFsHandle);
396  }
397  //
398  // Searching for handles that support Load File protocol
399  //
400  Status = gBS->LocateHandleBuffer (
401                  ByProtocol,
402                  &gEfiLoadFileProtocolGuid,
403                  NULL,
404                  &NoLoadFileHandles,
405                  &LoadFileHandle
406                  );
407
408  if (!EFI_ERROR (Status)) {
409    for (Index = 0; Index < NoLoadFileHandles; Index++) {
410      MenuEntry = BOpt_CreateMenuEntry (BM_FILE_CONTEXT_SELECT);
411      if (NULL == MenuEntry) {
412        FreePool (LoadFileHandle);
413        return EFI_OUT_OF_RESOURCES;
414      }
415
416      FileContext                   = (BM_FILE_CONTEXT *) MenuEntry->VariableContext;
417      FileContext->IsRemovableMedia = FALSE;
418      FileContext->IsLoadFile       = TRUE;
419      FileContext->Handle           = LoadFileHandle[Index];
420      FileContext->IsRoot           = TRUE;
421
422      FileContext->DevicePath       = DevicePathFromHandle (FileContext->Handle);
423      FileContext->FileName         = DevicePathToStr (FileContext->DevicePath);
424
425      MenuEntry->HelpString     = DevicePathToStr (FileContext->DevicePath);
426
427      TempStr                   = MenuEntry->HelpString;
428      MenuEntry->DisplayString  = AllocateZeroPool (MAX_CHAR);
429      ASSERT (MenuEntry->DisplayString != NULL);
430      UnicodeSPrint (
431        MenuEntry->DisplayString,
432        MAX_CHAR,
433        L"Load File [%s]",
434        TempStr
435        );
436
437      MenuEntry->OptionNumber = OptionNumber;
438      OptionNumber++;
439      InsertTailList (&FsOptionMenu.Head, &MenuEntry->Link);
440    }
441  }
442
443  if (NoLoadFileHandles != 0) {
444    FreePool (LoadFileHandle);
445  }
446
447  //
448  // Add Legacy Boot Option Support Here
449  //
450  Status = gBS->LocateProtocol (
451                  &gEfiLegacyBiosProtocolGuid,
452                  NULL,
453                  (VOID **) &LegacyBios
454                  );
455  if (!EFI_ERROR (Status)) {
456
457    for (Index = BBS_TYPE_FLOPPY; Index <= BBS_TYPE_EMBEDDED_NETWORK; Index++) {
458      MenuEntry = BOpt_CreateMenuEntry (BM_FILE_CONTEXT_SELECT);
459      if (NULL == MenuEntry) {
460        return EFI_OUT_OF_RESOURCES;
461      }
462
463      FileContext                       = (BM_FILE_CONTEXT *) MenuEntry->VariableContext;
464
465      FileContext->IsRemovableMedia     = FALSE;
466      FileContext->IsLoadFile           = TRUE;
467      FileContext->IsBootLegacy         = TRUE;
468      DeviceType                        = (UINT16) Index;
469      BbsDevicePathNode.Header.Type     = BBS_DEVICE_PATH;
470      BbsDevicePathNode.Header.SubType  = BBS_BBS_DP;
471      SetDevicePathNodeLength (
472        &BbsDevicePathNode.Header,
473        sizeof (BBS_BBS_DEVICE_PATH)
474        );
475      BbsDevicePathNode.DeviceType  = DeviceType;
476      BbsDevicePathNode.StatusFlag  = 0;
477      BbsDevicePathNode.String[0]   = 0;
478      DevicePath = AppendDevicePathNode (
479                    EndDevicePath,
480                    (EFI_DEVICE_PATH_PROTOCOL *) &BbsDevicePathNode
481                    );
482
483      FileContext->DevicePath   = DevicePath;
484      MenuEntry->HelpString     = DevicePathToStr (FileContext->DevicePath);
485
486      TempStr                   = MenuEntry->HelpString;
487      MenuEntry->DisplayString  = AllocateZeroPool (MAX_CHAR);
488      ASSERT (MenuEntry->DisplayString != NULL);
489      UnicodeSPrint (
490        MenuEntry->DisplayString,
491        MAX_CHAR,
492        L"Boot Legacy [%s]",
493        TempStr
494        );
495      MenuEntry->OptionNumber = OptionNumber;
496      OptionNumber++;
497      InsertTailList (&FsOptionMenu.Head, &MenuEntry->Link);
498    }
499  }
500  //
501  // Remember how many file system options are here
502  //
503  FsOptionMenu.MenuNumber = OptionNumber;
504  return EFI_SUCCESS;
505}
506
507/**
508  Free resources allocated in Allocate Rountine.
509
510  @param FreeMenu        Menu to be freed
511**/
512VOID
513BOpt_FreeMenu (
514  BM_MENU_OPTION        *FreeMenu
515  )
516{
517  BM_MENU_ENTRY *MenuEntry;
518  while (!IsListEmpty (&FreeMenu->Head)) {
519    MenuEntry = CR (
520                  FreeMenu->Head.ForwardLink,
521                  BM_MENU_ENTRY,
522                  Link,
523                  BM_MENU_ENTRY_SIGNATURE
524                  );
525    RemoveEntryList (&MenuEntry->Link);
526    BOpt_DestroyMenuEntry (MenuEntry);
527  }
528  FreeMenu->MenuNumber = 0;
529}
530
531/**
532  Find files under current directory
533  All files and sub-directories in current directory
534  will be stored in DirectoryMenu for future use.
535
536  @param CallbackData  The BMM context data.
537  @param MenuEntry     The Menu Entry.
538
539  @retval EFI_SUCCESS         Get files from current dir successfully.
540  @return Other value if can't get files from current dir.
541
542**/
543EFI_STATUS
544BOpt_FindFiles (
545  IN BMM_CALLBACK_DATA          *CallbackData,
546  IN BM_MENU_ENTRY              *MenuEntry
547  )
548{
549  EFI_FILE_HANDLE NewDir;
550  EFI_FILE_HANDLE Dir;
551  EFI_FILE_INFO   *DirInfo;
552  UINTN           BufferSize;
553  UINTN           DirBufferSize;
554  BM_MENU_ENTRY   *NewMenuEntry;
555  BM_FILE_CONTEXT *FileContext;
556  BM_FILE_CONTEXT *NewFileContext;
557  UINTN           Pass;
558  EFI_STATUS      Status;
559  UINTN           OptionNumber;
560
561  FileContext   = (BM_FILE_CONTEXT *) MenuEntry->VariableContext;
562  Dir           = FileContext->FHandle;
563  OptionNumber  = 0;
564  //
565  // Open current directory to get files from it
566  //
567  Status = Dir->Open (
568                  Dir,
569                  &NewDir,
570                  FileContext->FileName,
571                  EFI_FILE_READ_ONLY,
572                  0
573                  );
574  if (!FileContext->IsRoot) {
575    Dir->Close (Dir);
576  }
577
578  if (EFI_ERROR (Status)) {
579    return Status;
580  }
581
582  DirInfo = EfiLibFileInfo (NewDir);
583  if (DirInfo == NULL) {
584    return EFI_NOT_FOUND;
585  }
586
587  if ((DirInfo->Attribute & EFI_FILE_DIRECTORY) == 0) {
588    return EFI_INVALID_PARAMETER;
589  }
590
591  FileContext->DevicePath = FileDevicePath (
592                              FileContext->Handle,
593                              FileContext->FileName
594                              );
595
596  DirBufferSize = sizeof (EFI_FILE_INFO) + 1024;
597  DirInfo       = AllocateZeroPool (DirBufferSize);
598  if (DirInfo == NULL) {
599    return EFI_OUT_OF_RESOURCES;
600  }
601  //
602  // Get all files in current directory
603  // Pass 1 to get Directories
604  // Pass 2 to get files that are EFI images
605  //
606  for (Pass = 1; Pass <= 2; Pass++) {
607    NewDir->SetPosition (NewDir, 0);
608    for (;;) {
609      BufferSize  = DirBufferSize;
610      Status      = NewDir->Read (NewDir, &BufferSize, DirInfo);
611      if (EFI_ERROR (Status) || BufferSize == 0) {
612        break;
613      }
614
615      if (((DirInfo->Attribute & EFI_FILE_DIRECTORY) != 0 && Pass == 2) ||
616          ((DirInfo->Attribute & EFI_FILE_DIRECTORY) == 0 && Pass == 1)
617          ) {
618        //
619        // Pass 1 is for Directories
620        // Pass 2 is for file names
621        //
622        continue;
623      }
624
625      if (!(BOpt_IsEfiImageName (DirInfo->FileName) || (DirInfo->Attribute & EFI_FILE_DIRECTORY) != 0)) {
626        //
627        // Slip file unless it is a directory entry or a .EFI file
628        //
629        continue;
630      }
631
632      NewMenuEntry = BOpt_CreateMenuEntry (BM_FILE_CONTEXT_SELECT);
633      if (NULL == NewMenuEntry) {
634        return EFI_OUT_OF_RESOURCES;
635      }
636
637      NewFileContext          = (BM_FILE_CONTEXT *) NewMenuEntry->VariableContext;
638      NewFileContext->Handle  = FileContext->Handle;
639      NewFileContext->FileName = BOpt_AppendFileName (
640                                  FileContext->FileName,
641                                  DirInfo->FileName
642                                  );
643      NewFileContext->FHandle = NewDir;
644      NewFileContext->DevicePath = FileDevicePath (
645                                    NewFileContext->Handle,
646                                    NewFileContext->FileName
647                                    );
648      NewMenuEntry->HelpString = NULL;
649
650      MenuEntry->DisplayStringToken = GetStringTokenFromDepository (
651                                        CallbackData,
652                                        FileOptionStrDepository
653                                        );
654
655      NewFileContext->IsDir = (BOOLEAN) ((DirInfo->Attribute & EFI_FILE_DIRECTORY) == EFI_FILE_DIRECTORY);
656
657      if (NewFileContext->IsDir) {
658        BufferSize                  = StrLen (DirInfo->FileName) * 2 + 6;
659        NewMenuEntry->DisplayString = AllocateZeroPool (BufferSize);
660
661        UnicodeSPrint (
662          NewMenuEntry->DisplayString,
663          BufferSize,
664          L"<%s>",
665          DirInfo->FileName
666          );
667
668      } else {
669        NewMenuEntry->DisplayString = EfiStrDuplicate (DirInfo->FileName);
670      }
671
672      NewFileContext->IsRoot            = FALSE;
673      NewFileContext->IsLoadFile        = FALSE;
674      NewFileContext->IsRemovableMedia  = FALSE;
675
676      NewMenuEntry->OptionNumber        = OptionNumber;
677      OptionNumber++;
678      InsertTailList (&DirectoryMenu.Head, &NewMenuEntry->Link);
679    }
680  }
681
682  DirectoryMenu.MenuNumber = OptionNumber;
683  FreePool (DirInfo);
684  return EFI_SUCCESS;
685}
686
687/**
688  Build the LegacyFDMenu LegacyHDMenu LegacyCDMenu according to LegacyBios.GetBbsInfo().
689
690  @retval EFI_SUCCESS The function complete successfully.
691  @retval EFI_OUT_OF_RESOURCES No enough memory to complete this function.
692
693**/
694EFI_STATUS
695BOpt_GetLegacyOptions (
696  VOID
697  )
698{
699  BM_MENU_ENTRY             *NewMenuEntry;
700  BM_LEGACY_DEVICE_CONTEXT  *NewLegacyDevContext;
701  EFI_STATUS                Status;
702  EFI_LEGACY_BIOS_PROTOCOL  *LegacyBios;
703  UINT16                    HddCount;
704  HDD_INFO                  *HddInfo;
705  UINT16                    BbsCount;
706  BBS_TABLE                 *BbsTable;
707  UINT16                    Index;
708  CHAR16                    DescString[100];
709  UINTN                     FDNum;
710  UINTN                     HDNum;
711  UINTN                     CDNum;
712  UINTN                     NETNum;
713  UINTN                     BEVNum;
714
715  NewMenuEntry  = NULL;
716  HddInfo       = NULL;
717  BbsTable      = NULL;
718  BbsCount      = 0;
719
720  //
721  // Initialize Bbs Table Context from BBS info data
722  //
723  InitializeListHead (&LegacyFDMenu.Head);
724  InitializeListHead (&LegacyHDMenu.Head);
725  InitializeListHead (&LegacyCDMenu.Head);
726  InitializeListHead (&LegacyNETMenu.Head);
727  InitializeListHead (&LegacyBEVMenu.Head);
728
729  Status = gBS->LocateProtocol (
730                  &gEfiLegacyBiosProtocolGuid,
731                  NULL,
732                  (VOID **) &LegacyBios
733                  );
734  if (!EFI_ERROR (Status)) {
735    Status = LegacyBios->GetBbsInfo (
736                          LegacyBios,
737                          &HddCount,
738                          &HddInfo,
739                          &BbsCount,
740                          &BbsTable
741                          );
742    if (EFI_ERROR (Status)) {
743      return Status;
744    }
745  }
746
747  FDNum   = 0;
748  HDNum   = 0;
749  CDNum   = 0;
750  NETNum  = 0;
751  BEVNum  = 0;
752
753  for (Index = 0; Index < BbsCount; Index++) {
754    if ((BBS_IGNORE_ENTRY == BbsTable[Index].BootPriority) ||
755        (BBS_DO_NOT_BOOT_FROM == BbsTable[Index].BootPriority)
756        ) {
757      continue;
758    }
759
760    NewMenuEntry = BOpt_CreateMenuEntry (BM_LEGACY_DEV_CONTEXT_SELECT);
761    if (NULL == NewMenuEntry) {
762      break;
763    }
764
765    NewLegacyDevContext           = (BM_LEGACY_DEVICE_CONTEXT *) NewMenuEntry->VariableContext;
766    NewLegacyDevContext->BbsEntry = &BbsTable[Index];
767    NewLegacyDevContext->BbsIndex = Index;
768    NewLegacyDevContext->BbsCount = BbsCount;
769    BdsBuildLegacyDevNameString (
770      &BbsTable[Index],
771      Index,
772      sizeof (DescString),
773      DescString
774      );
775    NewLegacyDevContext->Description = AllocateCopyPool (StrSize (DescString), DescString);
776    if (NULL == NewLegacyDevContext->Description) {
777      break;
778    }
779
780    NewMenuEntry->DisplayString = NewLegacyDevContext->Description;
781    NewMenuEntry->HelpString    = NULL;
782
783    switch (BbsTable[Index].DeviceType) {
784    case BBS_FLOPPY:
785      InsertTailList (&LegacyFDMenu.Head, &NewMenuEntry->Link);
786      FDNum++;
787      break;
788
789    case BBS_HARDDISK:
790      InsertTailList (&LegacyHDMenu.Head, &NewMenuEntry->Link);
791      HDNum++;
792      break;
793
794    case BBS_CDROM:
795      InsertTailList (&LegacyCDMenu.Head, &NewMenuEntry->Link);
796      CDNum++;
797      break;
798
799    case BBS_EMBED_NETWORK:
800      InsertTailList (&LegacyNETMenu.Head, &NewMenuEntry->Link);
801      NETNum++;
802      break;
803
804    case BBS_BEV_DEVICE:
805      InsertTailList (&LegacyBEVMenu.Head, &NewMenuEntry->Link);
806      BEVNum++;
807      break;
808    }
809  }
810
811  if (Index != BbsCount) {
812    BOpt_FreeLegacyOptions ();
813    return EFI_OUT_OF_RESOURCES;
814  }
815
816  LegacyFDMenu.MenuNumber   = FDNum;
817  LegacyHDMenu.MenuNumber   = HDNum;
818  LegacyCDMenu.MenuNumber   = CDNum;
819  LegacyNETMenu.MenuNumber  = NETNum;
820  LegacyBEVMenu.MenuNumber  = BEVNum;
821  return EFI_SUCCESS;
822}
823
824/**
825  Free out resouce allocated from Legacy Boot Options.
826
827**/
828VOID
829BOpt_FreeLegacyOptions (
830  VOID
831  )
832{
833  BOpt_FreeMenu (&LegacyFDMenu);
834  BOpt_FreeMenu (&LegacyHDMenu);
835  BOpt_FreeMenu (&LegacyCDMenu);
836  BOpt_FreeMenu (&LegacyNETMenu);
837  BOpt_FreeMenu (&LegacyBEVMenu);
838}
839
840/**
841
842  Build the BootOptionMenu according to BootOrder Variable.
843  This Routine will access the Boot#### to get EFI_LOAD_OPTION.
844
845  @param CallbackData The BMM context data.
846
847  @return EFI_NOT_FOUND Fail to find "BootOrder" variable.
848  @return EFI_SUCESS    Success build boot option menu.
849
850**/
851EFI_STATUS
852BOpt_GetBootOptions (
853  IN  BMM_CALLBACK_DATA         *CallbackData
854  )
855{
856  UINTN                     Index;
857  UINT16                    BootString[10];
858  UINT8                     *LoadOptionFromVar;
859  UINT8                     *LoadOption;
860  UINTN                     BootOptionSize;
861  BOOLEAN                   BootNextFlag;
862  UINT16                    *BootOrderList;
863  UINTN                     BootOrderListSize;
864  UINT16                    *BootNext;
865  UINTN                     BootNextSize;
866  BM_MENU_ENTRY             *NewMenuEntry;
867  BM_LOAD_CONTEXT           *NewLoadContext;
868  UINT8                     *LoadOptionPtr;
869  UINTN                     StringSize;
870  UINTN                     OptionalDataSize;
871  UINT8                     *LoadOptionEnd;
872  EFI_DEVICE_PATH_PROTOCOL  *DevicePath;
873  UINTN                     MenuCount;
874  UINT8                     *Ptr;
875
876  MenuCount         = 0;
877  BootOrderListSize = 0;
878  BootNextSize      = 0;
879  BootOrderList     = NULL;
880  BootNext          = NULL;
881  LoadOptionFromVar = NULL;
882  BOpt_FreeMenu (&BootOptionMenu);
883  InitializeListHead (&BootOptionMenu.Head);
884
885  //
886  // Get the BootOrder from the Var
887  //
888  BootOrderList = BdsLibGetVariableAndSize (
889                    L"BootOrder",
890                    &gEfiGlobalVariableGuid,
891                    &BootOrderListSize
892                    );
893  if (BootOrderList == NULL) {
894    return EFI_NOT_FOUND;
895  }
896
897  //
898  // Get the BootNext from the Var
899  //
900  BootNext = BdsLibGetVariableAndSize (
901              L"BootNext",
902              &gEfiGlobalVariableGuid,
903              &BootNextSize
904              );
905
906  if (BootNext != NULL) {
907    if (BootNextSize != sizeof (UINT16)) {
908      FreePool (BootNext);
909      BootNext = NULL;
910    }
911  }
912
913  for (Index = 0; Index < BootOrderListSize / sizeof (UINT16); Index++) {
914    UnicodeSPrint (BootString, sizeof (BootString), L"Boot%04x", BootOrderList[Index]);
915    //
916    //  Get all loadoptions from the VAR
917    //
918    LoadOptionFromVar = BdsLibGetVariableAndSize (
919                          BootString,
920                          &gEfiGlobalVariableGuid,
921                          &BootOptionSize
922                          );
923    if (LoadOptionFromVar == NULL) {
924      continue;
925    }
926
927    LoadOption = AllocateZeroPool (BootOptionSize);
928    if (LoadOption == NULL) {
929      continue;
930    }
931
932    CopyMem (LoadOption, LoadOptionFromVar, BootOptionSize);
933    FreePool (LoadOptionFromVar);
934
935    if (BootNext != NULL) {
936      BootNextFlag = (BOOLEAN) (*BootNext == BootOrderList[Index]);
937    } else {
938      BootNextFlag = FALSE;
939    }
940
941    if (0 == (*((UINT32 *) LoadOption) & LOAD_OPTION_ACTIVE)) {
942      FreePool (LoadOption);
943      continue;
944    }
945    //
946    // BUGBUG: could not return EFI_OUT_OF_RESOURCES here directly.
947    // the buffer allocated already should be freed before returning.
948    //
949    NewMenuEntry = BOpt_CreateMenuEntry (BM_LOAD_CONTEXT_SELECT);
950    if (NULL == NewMenuEntry) {
951      return EFI_OUT_OF_RESOURCES;
952    }
953
954    NewLoadContext                      = (BM_LOAD_CONTEXT *) NewMenuEntry->VariableContext;
955
956    LoadOptionPtr                       = LoadOption;
957    LoadOptionEnd                       = LoadOption + BootOptionSize;
958
959    NewMenuEntry->OptionNumber          = BootOrderList[Index];
960    NewLoadContext->LoadOptionModified  = FALSE;
961    NewLoadContext->Deleted             = FALSE;
962    NewLoadContext->IsBootNext          = BootNextFlag;
963
964    //
965    // Is a Legacy Device?
966    //
967    Ptr = (UINT8 *) LoadOption;
968
969    //
970    // Attribute = *(UINT32 *)Ptr;
971    //
972    Ptr += sizeof (UINT32);
973
974    //
975    // FilePathSize = *(UINT16 *)Ptr;
976    //
977    Ptr += sizeof (UINT16);
978
979    //
980    // Description = (CHAR16 *)Ptr;
981    //
982    Ptr += StrSize ((CHAR16 *) Ptr);
983
984    //
985    // Now Ptr point to Device Path
986    //
987    DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) Ptr;
988    if ((BBS_DEVICE_PATH == DevicePath->Type) && (BBS_BBS_DP == DevicePath->SubType)) {
989      NewLoadContext->IsLegacy = TRUE;
990    } else {
991      NewLoadContext->IsLegacy = FALSE;
992    }
993    //
994    // LoadOption is a pointer type of UINT8
995    // for easy use with following LOAD_OPTION
996    // embedded in this struct
997    //
998    NewLoadContext->LoadOption      = LoadOption;
999    NewLoadContext->LoadOptionSize  = BootOptionSize;
1000
1001    NewLoadContext->Attributes      = *(UINT32 *) LoadOptionPtr;
1002    NewLoadContext->IsActive        = (BOOLEAN) (NewLoadContext->Attributes & LOAD_OPTION_ACTIVE);
1003
1004    NewLoadContext->ForceReconnect  = (BOOLEAN) (NewLoadContext->Attributes & LOAD_OPTION_FORCE_RECONNECT);
1005
1006    LoadOptionPtr += sizeof (UINT32);
1007
1008    NewLoadContext->FilePathListLength = *(UINT16 *) LoadOptionPtr;
1009    LoadOptionPtr += sizeof (UINT16);
1010
1011    StringSize = StrSize((UINT16*)LoadOptionPtr);
1012
1013    NewLoadContext->Description = AllocateCopyPool (StrSize((UINT16*)LoadOptionPtr), LoadOptionPtr);
1014    ASSERT (NewLoadContext->Description != NULL);
1015
1016    NewMenuEntry->DisplayString = NewLoadContext->Description;
1017
1018    LoadOptionPtr += StringSize;
1019
1020    NewLoadContext->FilePathList = AllocateZeroPool (NewLoadContext->FilePathListLength);
1021    ASSERT (NewLoadContext->FilePathList != NULL);
1022    CopyMem (
1023      NewLoadContext->FilePathList,
1024      (EFI_DEVICE_PATH_PROTOCOL *) LoadOptionPtr,
1025      NewLoadContext->FilePathListLength
1026      );
1027
1028    NewMenuEntry->HelpString = DevicePathToStr (NewLoadContext->FilePathList);
1029    NewMenuEntry->DisplayStringToken = GetStringTokenFromDepository (
1030                                        CallbackData,
1031                                        BootOptionStrDepository
1032                                        );
1033    NewMenuEntry->HelpStringToken = GetStringTokenFromDepository (
1034                                      CallbackData,
1035                                      BootOptionHelpStrDepository
1036                                      );
1037    LoadOptionPtr += NewLoadContext->FilePathListLength;
1038
1039    if (LoadOptionPtr < LoadOptionEnd) {
1040      OptionalDataSize = BootOptionSize -
1041        sizeof (UINT32) -
1042        sizeof (UINT16) -
1043        StringSize -
1044        NewLoadContext->FilePathListLength;
1045
1046      NewLoadContext->OptionalData = AllocateZeroPool (OptionalDataSize);
1047      ASSERT (NewLoadContext->OptionalData != NULL);
1048      CopyMem (
1049        NewLoadContext->OptionalData,
1050        LoadOptionPtr,
1051        OptionalDataSize
1052        );
1053
1054      NewLoadContext->OptionalDataSize = OptionalDataSize;
1055    }
1056
1057    InsertTailList (&BootOptionMenu.Head, &NewMenuEntry->Link);
1058    MenuCount++;
1059  }
1060
1061  if (BootNext != NULL) {
1062    FreePool (BootNext);
1063  }
1064  if (BootOrderList != NULL) {
1065    FreePool (BootOrderList);
1066  }
1067  BootOptionMenu.MenuNumber = MenuCount;
1068  return EFI_SUCCESS;
1069}
1070
1071/**
1072
1073  Append file name to existing file name.
1074
1075  @param Str1  The existing file name
1076  @param Str2  The file name to be appended
1077
1078  @return Allocate a new string to hold the appended result.
1079          Caller is responsible to free the returned string.
1080
1081**/
1082CHAR16 *
1083BOpt_AppendFileName (
1084  IN  CHAR16  *Str1,
1085  IN  CHAR16  *Str2
1086  )
1087{
1088  UINTN   Size1;
1089  UINTN   Size2;
1090  UINTN   MaxLen;
1091  CHAR16  *Str;
1092  CHAR16  *TmpStr;
1093  CHAR16  *Ptr;
1094  CHAR16  *LastSlash;
1095
1096  Size1 = StrSize (Str1);
1097  Size2 = StrSize (Str2);
1098  MaxLen = (Size1 + Size2 + sizeof (CHAR16)) / sizeof (CHAR16);
1099  Str   = AllocateZeroPool (MaxLen * sizeof (CHAR16));
1100  ASSERT (Str != NULL);
1101
1102  TmpStr = AllocateZeroPool (MaxLen * sizeof (CHAR16));
1103  ASSERT (TmpStr != NULL);
1104
1105  StrCatS (Str, MaxLen, Str1);
1106  if (!((*Str == '\\') && (*(Str + 1) == 0))) {
1107    StrCatS (Str, MaxLen, L"\\");
1108  }
1109
1110  StrCatS (Str, MaxLen, Str2);
1111
1112  Ptr       = Str;
1113  LastSlash = Str;
1114  while (*Ptr != 0) {
1115    if (*Ptr == '\\' && *(Ptr + 1) == '.' && *(Ptr + 2) == '.' && *(Ptr + 3) == L'\\') {
1116      //
1117      // Convert "\Name\..\" to "\"
1118      // DO NOT convert the .. if it is at the end of the string. This will
1119      // break the .. behavior in changing directories.
1120      //
1121
1122      //
1123      // Use TmpStr as a backup, as StrCpyS in BaseLib does not handle copy of two strings
1124      // that overlap.
1125      //
1126      StrCpyS (TmpStr, MaxLen, Ptr + 3);
1127      StrCpyS (LastSlash, MaxLen - (UINTN) (LastSlash - Str), TmpStr);
1128      Ptr = LastSlash;
1129    } else if (*Ptr == '\\' && *(Ptr + 1) == '.' && *(Ptr + 2) == '\\') {
1130      //
1131      // Convert a "\.\" to a "\"
1132      //
1133
1134      //
1135      // Use TmpStr as a backup, as StrCpyS in BaseLib does not handle copy of two strings
1136      // that overlap.
1137      //
1138      StrCpyS (TmpStr, MaxLen, Ptr + 2);
1139      StrCpyS (Ptr, MaxLen - (UINTN) (Ptr - Str), TmpStr);
1140      Ptr = LastSlash;
1141    } else if (*Ptr == '\\') {
1142      LastSlash = Ptr;
1143    }
1144
1145    Ptr++;
1146  }
1147
1148  FreePool (TmpStr);
1149
1150  return Str;
1151}
1152
1153/**
1154
1155  Check whether current FileName point to a valid
1156  Efi Image File.
1157
1158  @param FileName  File need to be checked.
1159
1160  @retval TRUE  Is Efi Image
1161  @retval FALSE Not a valid Efi Image
1162
1163**/
1164BOOLEAN
1165BOpt_IsEfiImageName (
1166  IN UINT16  *FileName
1167  )
1168{
1169  //
1170  // Search for ".efi" extension
1171  //
1172  while (*FileName != L'\0') {
1173    if (FileName[0] == '.') {
1174      if (FileName[1] == 'e' || FileName[1] == 'E') {
1175        if (FileName[2] == 'f' || FileName[2] == 'F') {
1176          if (FileName[3] == 'i' || FileName[3] == 'I') {
1177            return TRUE;
1178          } else if (FileName[3] == 0x0000) {
1179            return FALSE;
1180          }
1181        } else if (FileName[2] == 0x0000) {
1182          return FALSE;
1183        }
1184      } else if (FileName[1] == 0x0000) {
1185        return FALSE;
1186      }
1187    }
1188
1189    FileName += 1;
1190  }
1191
1192  return FALSE;
1193}
1194
1195/**
1196
1197  Check whether current FileName point to a valid Efi Application
1198
1199  @param Dir       Pointer to current Directory
1200  @param FileName  Pointer to current File name.
1201
1202  @retval TRUE      Is a valid Efi Application
1203  @retval FALSE     not a valid Efi Application
1204
1205**/
1206BOOLEAN
1207BOpt_IsEfiApp (
1208  IN EFI_FILE_HANDLE Dir,
1209  IN UINT16          *FileName
1210  )
1211{
1212  UINTN                       BufferSize;
1213  EFI_IMAGE_DOS_HEADER        DosHdr;
1214  UINT16                      Subsystem;
1215  EFI_FILE_HANDLE             File;
1216  EFI_STATUS                  Status;
1217  EFI_IMAGE_OPTIONAL_HEADER_UNION PeHdr;
1218
1219  Status = Dir->Open (Dir, &File, FileName, EFI_FILE_MODE_READ, 0);
1220
1221  if (EFI_ERROR (Status)) {
1222    return FALSE;
1223  }
1224
1225  BufferSize = sizeof (EFI_IMAGE_DOS_HEADER);
1226  File->Read (File, &BufferSize, &DosHdr);
1227  if (DosHdr.e_magic != EFI_IMAGE_DOS_SIGNATURE) {
1228    File->Close (File);
1229    return FALSE;
1230  }
1231
1232  File->SetPosition (File, DosHdr.e_lfanew);
1233  BufferSize = sizeof (EFI_IMAGE_OPTIONAL_HEADER_UNION);
1234  File->Read (File, &BufferSize, &PeHdr);
1235  if (PeHdr.Pe32.Signature != EFI_IMAGE_NT_SIGNATURE) {
1236    File->Close (File);
1237    return FALSE;
1238  }
1239  //
1240  // Determine PE type and read subsytem
1241  //
1242  if (PeHdr.Pe32.OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
1243    Subsystem = PeHdr.Pe32.OptionalHeader.Subsystem;
1244  } else if (PeHdr.Pe32.OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
1245    Subsystem = PeHdr.Pe32Plus.OptionalHeader.Subsystem;
1246  } else {
1247    return FALSE;
1248  }
1249
1250  if (Subsystem == EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION) {
1251    File->Close (File);
1252    return TRUE;
1253  } else {
1254    File->Close (File);
1255    return FALSE;
1256  }
1257}
1258
1259/**
1260
1261  Find drivers that will be added as Driver#### variables from handles
1262  in current system environment
1263  All valid handles in the system except those consume SimpleFs, LoadFile
1264  are stored in DriverMenu for future use.
1265
1266  @retval EFI_SUCCESS The function complets successfully.
1267  @return Other value if failed to build the DriverMenu.
1268
1269**/
1270EFI_STATUS
1271BOpt_FindDrivers (
1272  VOID
1273  )
1274{
1275  UINTN                           NoDevicePathHandles;
1276  EFI_HANDLE                      *DevicePathHandle;
1277  UINTN                           Index;
1278  EFI_STATUS                      Status;
1279  BM_MENU_ENTRY                   *NewMenuEntry;
1280  BM_HANDLE_CONTEXT               *NewHandleContext;
1281  EFI_HANDLE                      CurHandle;
1282  UINTN                           OptionNumber;
1283  EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *SimpleFs;
1284  EFI_LOAD_FILE_PROTOCOL          *LoadFile;
1285
1286  SimpleFs  = NULL;
1287  LoadFile  = NULL;
1288
1289  InitializeListHead (&DriverMenu.Head);
1290
1291  //
1292  // At first, get all handles that support Device Path
1293  // protocol which is the basic requirement for
1294  // Driver####
1295  //
1296  Status = gBS->LocateHandleBuffer (
1297                  ByProtocol,
1298                  &gEfiDevicePathProtocolGuid,
1299                  NULL,
1300                  &NoDevicePathHandles,
1301                  &DevicePathHandle
1302                  );
1303  if (EFI_ERROR (Status)) {
1304    return Status;
1305  }
1306
1307  OptionNumber = 0;
1308  for (Index = 0; Index < NoDevicePathHandles; Index++) {
1309    CurHandle = DevicePathHandle[Index];
1310
1311    Status = gBS->HandleProtocol (
1312                    CurHandle,
1313                    &gEfiSimpleFileSystemProtocolGuid,
1314                    (VOID **) &SimpleFs
1315                    );
1316    if (Status == EFI_SUCCESS) {
1317      continue;
1318    }
1319
1320    Status = gBS->HandleProtocol (
1321                    CurHandle,
1322                    &gEfiLoadFileProtocolGuid,
1323                    (VOID **) &LoadFile
1324                    );
1325    if (Status == EFI_SUCCESS) {
1326      continue;
1327    }
1328
1329    NewMenuEntry = BOpt_CreateMenuEntry (BM_HANDLE_CONTEXT_SELECT);
1330    if (NULL == NewMenuEntry) {
1331      FreePool (DevicePathHandle);
1332      return EFI_OUT_OF_RESOURCES;
1333    }
1334
1335    NewHandleContext              = (BM_HANDLE_CONTEXT *) NewMenuEntry->VariableContext;
1336    NewHandleContext->Handle      = CurHandle;
1337    NewHandleContext->DevicePath  = DevicePathFromHandle (CurHandle);
1338    NewMenuEntry->DisplayString = DevicePathToStr (NewHandleContext->DevicePath);
1339    NewMenuEntry->HelpString    = NULL;
1340    NewMenuEntry->OptionNumber  = OptionNumber;
1341    OptionNumber++;
1342    InsertTailList (&DriverMenu.Head, &NewMenuEntry->Link);
1343
1344  }
1345
1346  if (DevicePathHandle != NULL) {
1347    FreePool (DevicePathHandle);
1348  }
1349
1350  DriverMenu.MenuNumber = OptionNumber;
1351  return EFI_SUCCESS;
1352}
1353
1354/**
1355
1356  Get the Option Number that has not been allocated for use.
1357
1358  @param Type  The type of Option.
1359
1360  @return The available Option Number.
1361
1362**/
1363UINT16
1364BOpt_GetOptionNumber (
1365  CHAR16        *Type
1366  )
1367{
1368  UINT16        *OrderList;
1369  UINTN         OrderListSize;
1370  UINTN         Index;
1371  CHAR16        StrTemp[20];
1372  UINT16        *OptionBuffer;
1373  UINT16        OptionNumber;
1374  UINTN         OptionSize;
1375
1376  OrderListSize = 0;
1377  OrderList     = NULL;
1378  OptionNumber  = 0;
1379  Index         = 0;
1380
1381  UnicodeSPrint (StrTemp, sizeof (StrTemp), L"%sOrder", Type);
1382
1383  OrderList = BdsLibGetVariableAndSize (
1384                          StrTemp,
1385                          &gEfiGlobalVariableGuid,
1386                          &OrderListSize
1387                          );
1388
1389  for (OptionNumber = 0; ; OptionNumber++) {
1390    if (OrderList != NULL) {
1391      for (Index = 0; Index < OrderListSize / sizeof (UINT16); Index++) {
1392        if (OptionNumber == OrderList[Index]) {
1393          break;
1394        }
1395      }
1396    }
1397
1398    if (Index < OrderListSize / sizeof (UINT16)) {
1399      //
1400      // The OptionNumber occurs in the OrderList, continue to use next one
1401      //
1402      continue;
1403    }
1404    UnicodeSPrint (StrTemp, sizeof (StrTemp), L"%s%04x", Type, (UINTN) OptionNumber);
1405    DEBUG((EFI_D_ERROR,"Option = %s\n", StrTemp));
1406    OptionBuffer = BdsLibGetVariableAndSize (
1407                       StrTemp,
1408                       &gEfiGlobalVariableGuid,
1409                       &OptionSize
1410                       );
1411    if (NULL == OptionBuffer) {
1412      //
1413      // The Boot[OptionNumber] / Driver[OptionNumber] NOT occurs, we found it
1414      //
1415      break;
1416    }
1417  }
1418
1419  return OptionNumber;
1420}
1421
1422/**
1423
1424  Get the Option Number for Boot#### that does not used.
1425
1426  @return The available Option Number.
1427
1428**/
1429UINT16
1430BOpt_GetBootOptionNumber (
1431  VOID
1432  )
1433{
1434  return BOpt_GetOptionNumber (L"Boot");
1435}
1436
1437/**
1438
1439  Get the Option Number for Driver#### that does not used.
1440
1441  @return The unused Option Number.
1442
1443**/
1444UINT16
1445BOpt_GetDriverOptionNumber (
1446  VOID
1447  )
1448{
1449  return BOpt_GetOptionNumber (L"Driver");
1450}
1451
1452/**
1453
1454  Build up all DriverOptionMenu
1455
1456  @param CallbackData The BMM context data.
1457
1458  @retval EFI_SUCESS           The functin completes successfully.
1459  @retval EFI_OUT_OF_RESOURCES Not enough memory to compete the operation.
1460  @retval EFI_NOT_FOUND        Fail to get "DriverOrder" variable.
1461
1462**/
1463EFI_STATUS
1464BOpt_GetDriverOptions (
1465  IN  BMM_CALLBACK_DATA         *CallbackData
1466  )
1467{
1468  UINTN           Index;
1469  UINT16          DriverString[12];
1470  UINT8           *LoadOptionFromVar;
1471  UINT8           *LoadOption;
1472  UINTN           DriverOptionSize;
1473
1474  UINT16          *DriverOrderList;
1475  UINTN           DriverOrderListSize;
1476  BM_MENU_ENTRY   *NewMenuEntry;
1477  BM_LOAD_CONTEXT *NewLoadContext;
1478  UINT8           *LoadOptionPtr;
1479  UINTN           StringSize;
1480  UINTN           OptionalDataSize;
1481  UINT8           *LoadOptionEnd;
1482
1483  DriverOrderListSize = 0;
1484  DriverOrderList     = NULL;
1485  DriverOptionSize    = 0;
1486  LoadOptionFromVar   = NULL;
1487  BOpt_FreeMenu (&DriverOptionMenu);
1488  InitializeListHead (&DriverOptionMenu.Head);
1489  //
1490  // Get the DriverOrder from the Var
1491  //
1492  DriverOrderList = BdsLibGetVariableAndSize (
1493                      L"DriverOrder",
1494                      &gEfiGlobalVariableGuid,
1495                      &DriverOrderListSize
1496                      );
1497  if (DriverOrderList == NULL) {
1498    return EFI_NOT_FOUND;
1499  }
1500
1501  for (Index = 0; Index < DriverOrderListSize / sizeof (UINT16); Index++) {
1502    UnicodeSPrint (
1503      DriverString,
1504      sizeof (DriverString),
1505      L"Driver%04x",
1506      DriverOrderList[Index]
1507      );
1508    //
1509    //  Get all loadoptions from the VAR
1510    //
1511    LoadOptionFromVar = BdsLibGetVariableAndSize (
1512                          DriverString,
1513                          &gEfiGlobalVariableGuid,
1514                          &DriverOptionSize
1515                          );
1516    if (LoadOptionFromVar == NULL) {
1517      continue;
1518    }
1519
1520    LoadOption = AllocateZeroPool (DriverOptionSize);
1521    if (LoadOption == NULL) {
1522      continue;
1523    }
1524
1525    CopyMem (LoadOption, LoadOptionFromVar, DriverOptionSize);
1526    FreePool (LoadOptionFromVar);
1527
1528    NewMenuEntry = BOpt_CreateMenuEntry (BM_LOAD_CONTEXT_SELECT);
1529    if (NULL == NewMenuEntry) {
1530      return EFI_OUT_OF_RESOURCES;
1531    }
1532
1533    NewLoadContext                      = (BM_LOAD_CONTEXT *) NewMenuEntry->VariableContext;
1534    LoadOptionPtr                       = LoadOption;
1535    LoadOptionEnd                       = LoadOption + DriverOptionSize;
1536    NewMenuEntry->OptionNumber          = DriverOrderList[Index];
1537    NewLoadContext->LoadOptionModified  = FALSE;
1538    NewLoadContext->Deleted             = FALSE;
1539    NewLoadContext->IsLegacy            = FALSE;
1540
1541    //
1542    // LoadOption is a pointer type of UINT8
1543    // for easy use with following LOAD_OPTION
1544    // embedded in this struct
1545    //
1546    NewLoadContext->LoadOption      = LoadOption;
1547    NewLoadContext->LoadOptionSize  = DriverOptionSize;
1548
1549    NewLoadContext->Attributes      = *(UINT32 *) LoadOptionPtr;
1550    NewLoadContext->IsActive        = (BOOLEAN) (NewLoadContext->Attributes & LOAD_OPTION_ACTIVE);
1551
1552    NewLoadContext->ForceReconnect  = (BOOLEAN) (NewLoadContext->Attributes & LOAD_OPTION_FORCE_RECONNECT);
1553
1554    LoadOptionPtr += sizeof (UINT32);
1555
1556    NewLoadContext->FilePathListLength = *(UINT16 *) LoadOptionPtr;
1557    LoadOptionPtr += sizeof (UINT16);
1558
1559    StringSize                  = StrSize ((UINT16 *) LoadOptionPtr);
1560    NewLoadContext->Description = AllocateZeroPool (StringSize);
1561    ASSERT (NewLoadContext->Description != NULL);
1562    CopyMem (
1563      NewLoadContext->Description,
1564      (UINT16 *) LoadOptionPtr,
1565      StringSize
1566      );
1567    NewMenuEntry->DisplayString = NewLoadContext->Description;
1568
1569    LoadOptionPtr += StringSize;
1570
1571    NewLoadContext->FilePathList = AllocateZeroPool (NewLoadContext->FilePathListLength);
1572    ASSERT (NewLoadContext->FilePathList != NULL);
1573    CopyMem (
1574      NewLoadContext->FilePathList,
1575      (EFI_DEVICE_PATH_PROTOCOL *) LoadOptionPtr,
1576      NewLoadContext->FilePathListLength
1577      );
1578
1579    NewMenuEntry->HelpString = DevicePathToStr (NewLoadContext->FilePathList);
1580    NewMenuEntry->DisplayStringToken = GetStringTokenFromDepository (
1581                                        CallbackData,
1582                                        DriverOptionStrDepository
1583                                        );
1584    NewMenuEntry->HelpStringToken = GetStringTokenFromDepository (
1585                                      CallbackData,
1586                                      DriverOptionHelpStrDepository
1587                                      );
1588    LoadOptionPtr += NewLoadContext->FilePathListLength;
1589
1590    if (LoadOptionPtr < LoadOptionEnd) {
1591      OptionalDataSize = DriverOptionSize -
1592        sizeof (UINT32) -
1593        sizeof (UINT16) -
1594        StringSize -
1595        NewLoadContext->FilePathListLength;
1596
1597      NewLoadContext->OptionalData = AllocateZeroPool (OptionalDataSize);
1598      ASSERT (NewLoadContext->OptionalData != NULL);
1599      CopyMem (
1600        NewLoadContext->OptionalData,
1601        LoadOptionPtr,
1602        OptionalDataSize
1603        );
1604
1605      NewLoadContext->OptionalDataSize = OptionalDataSize;
1606    }
1607
1608    InsertTailList (&DriverOptionMenu.Head, &NewMenuEntry->Link);
1609
1610  }
1611
1612  if (DriverOrderList != NULL) {
1613    FreePool (DriverOrderList);
1614  }
1615  DriverOptionMenu.MenuNumber = Index;
1616  return EFI_SUCCESS;
1617
1618}
1619
1620/**
1621  Get option number according to Boot#### and BootOrder variable.
1622  The value is saved as #### + 1.
1623
1624  @param CallbackData    The BMM context data.
1625**/
1626VOID
1627GetBootOrder (
1628  IN  BMM_CALLBACK_DATA    *CallbackData
1629  )
1630{
1631  BMM_FAKE_NV_DATA          *BmmConfig;
1632  UINT16                    Index;
1633  UINT16                    OptionOrderIndex;
1634  UINTN                     DeviceType;
1635  BM_MENU_ENTRY             *NewMenuEntry;
1636  BM_LOAD_CONTEXT           *NewLoadContext;
1637
1638  ASSERT (CallbackData != NULL);
1639
1640  DeviceType = (UINTN) -1;
1641  BmmConfig  = &CallbackData->BmmFakeNvData;
1642  ZeroMem (BmmConfig->BootOptionOrder, sizeof (BmmConfig->BootOptionOrder));
1643
1644  for (Index = 0, OptionOrderIndex = 0; ((Index < BootOptionMenu.MenuNumber) &&
1645       (OptionOrderIndex < (sizeof (BmmConfig->BootOptionOrder) / sizeof (BmmConfig->BootOptionOrder[0]))));
1646       Index++) {
1647    NewMenuEntry   = BOpt_GetMenuEntry (&BootOptionMenu, Index);
1648    NewLoadContext = (BM_LOAD_CONTEXT *) NewMenuEntry->VariableContext;
1649
1650    if (NewLoadContext->IsLegacy) {
1651      if (((BBS_BBS_DEVICE_PATH *) NewLoadContext->FilePathList)->DeviceType != DeviceType) {
1652        DeviceType = ((BBS_BBS_DEVICE_PATH *) NewLoadContext->FilePathList)->DeviceType;
1653      } else {
1654        //
1655        // Only show one legacy boot option for the same device type
1656        // assuming the boot options are grouped by the device type
1657        //
1658        continue;
1659      }
1660    }
1661    BmmConfig->BootOptionOrder[OptionOrderIndex++] = (UINT32) (NewMenuEntry->OptionNumber + 1);
1662  }
1663}
1664
1665/**
1666  According to LegacyDevOrder variable to get legacy FD\HD\CD\NET\BEV
1667  devices list .
1668
1669  @param CallbackData    The BMM context data.
1670**/
1671VOID
1672GetLegacyDeviceOrder (
1673  IN  BMM_CALLBACK_DATA    *CallbackData
1674  )
1675{
1676  UINTN                     Index;
1677  UINTN                     OptionIndex;
1678  UINT16                    PageIdList[5];
1679  UINTN                     PageNum;
1680  UINTN                     VarSize;
1681  UINT8                     *VarData;
1682  UINT8                     *WorkingVarData;
1683  LEGACY_DEV_ORDER_ENTRY    *DevOrder;
1684  UINT16                    VarDevOrder;
1685  UINT8                     *DisMap;
1686  BM_MENU_OPTION            *OptionMenu;
1687  BBS_TYPE                  BbsType;
1688  UINT8                     *LegacyOrder;
1689  UINT8                     *OldData;
1690  UINTN                     Pos;
1691  UINTN                     Bit;
1692
1693  ASSERT (CallbackData != NULL);
1694
1695  PageIdList[0] = FORM_SET_FD_ORDER_ID;
1696  PageIdList[1] = FORM_SET_HD_ORDER_ID;
1697  PageIdList[2] = FORM_SET_CD_ORDER_ID;
1698  PageIdList[3] = FORM_SET_NET_ORDER_ID;
1699  PageIdList[4] = FORM_SET_BEV_ORDER_ID;
1700  OptionMenu  = NULL;
1701  BbsType     = 0;
1702  LegacyOrder = NULL;
1703  OldData     = NULL;
1704  DisMap      = ZeroMem (CallbackData->BmmFakeNvData.DisableMap, sizeof (CallbackData->BmmFakeNvData.DisableMap));
1705  PageNum     = ARRAY_SIZE (PageIdList);
1706  VarData     = BdsLibGetVariableAndSize (
1707                  VAR_LEGACY_DEV_ORDER,
1708                  &gEfiLegacyDevOrderVariableGuid,
1709                  &VarSize
1710                  );
1711
1712  for (Index = 0; Index < PageNum; Index++) {
1713    switch (PageIdList[Index]) {
1714
1715    case FORM_SET_FD_ORDER_ID:
1716      OptionMenu  = (BM_MENU_OPTION *) &LegacyFDMenu;
1717      BbsType     = BBS_FLOPPY;
1718      LegacyOrder = CallbackData->BmmFakeNvData.LegacyFD;
1719      OldData     = CallbackData->BmmOldFakeNVData.LegacyFD;
1720      break;
1721
1722    case FORM_SET_HD_ORDER_ID:
1723      OptionMenu  = (BM_MENU_OPTION *) &LegacyHDMenu;
1724      BbsType     = BBS_HARDDISK;
1725      LegacyOrder = CallbackData->BmmFakeNvData.LegacyHD;
1726      OldData     = CallbackData->BmmOldFakeNVData.LegacyHD;
1727      break;
1728
1729    case FORM_SET_CD_ORDER_ID:
1730      OptionMenu  = (BM_MENU_OPTION *) &LegacyCDMenu;
1731      BbsType     = BBS_CDROM;
1732      LegacyOrder = CallbackData->BmmFakeNvData.LegacyCD;
1733      OldData     = CallbackData->BmmOldFakeNVData.LegacyCD;
1734      break;
1735
1736    case FORM_SET_NET_ORDER_ID:
1737      OptionMenu  = (BM_MENU_OPTION *) &LegacyNETMenu;
1738      BbsType     = BBS_EMBED_NETWORK;
1739      LegacyOrder = CallbackData->BmmFakeNvData.LegacyNET;
1740      OldData     = CallbackData->BmmOldFakeNVData.LegacyNET;
1741      break;
1742
1743    default:
1744      ASSERT (PageIdList[Index] == FORM_SET_BEV_ORDER_ID);
1745      OptionMenu  = (BM_MENU_OPTION *) &LegacyBEVMenu;
1746      BbsType     = BBS_BEV_DEVICE;
1747      LegacyOrder = CallbackData->BmmFakeNvData.LegacyBEV;
1748      OldData     = CallbackData->BmmOldFakeNVData.LegacyBEV;
1749      break;
1750    }
1751
1752    if (NULL != VarData) {
1753      WorkingVarData = VarData;
1754      DevOrder    = (LEGACY_DEV_ORDER_ENTRY *) WorkingVarData;
1755      while (WorkingVarData < VarData + VarSize) {
1756        if (DevOrder->BbsType == BbsType) {
1757          break;
1758        }
1759
1760        WorkingVarData  = (UINT8 *)((UINTN)WorkingVarData + sizeof (BBS_TYPE));
1761        WorkingVarData += *(UINT16 *) WorkingVarData;
1762        DevOrder = (LEGACY_DEV_ORDER_ENTRY *) WorkingVarData;
1763      }
1764      for (OptionIndex = 0; OptionIndex < OptionMenu->MenuNumber; OptionIndex++) {
1765        VarDevOrder = *(UINT16 *) ((UINTN) DevOrder + sizeof (BBS_TYPE) + sizeof (UINT16) + OptionIndex * sizeof (UINT16));
1766         if (0xFF00 == (VarDevOrder & 0xFF00)) {
1767          LegacyOrder[OptionIndex]  = 0xFF;
1768          Pos                       = (VarDevOrder & 0xFF) / 8;
1769          Bit                       = 7 - ((VarDevOrder & 0xFF) % 8);
1770          DisMap[Pos] = (UINT8) (DisMap[Pos] | (UINT8) (1 << Bit));
1771        } else {
1772          LegacyOrder[OptionIndex] = (UINT8) (VarDevOrder & 0xFF);
1773        }
1774      }
1775      CopyMem (OldData, LegacyOrder, 100);
1776    }
1777  }
1778}
1779
1780/**
1781  Get driver option order from globalc DriverOptionMenu.
1782
1783  @param CallbackData    The BMM context data.
1784
1785**/
1786VOID
1787GetDriverOrder (
1788  IN  BMM_CALLBACK_DATA    *CallbackData
1789  )
1790{
1791  BMM_FAKE_NV_DATA          *BmmConfig;
1792  UINT16                    Index;
1793  UINT16                    OptionOrderIndex;
1794  UINTN                     DeviceType;
1795  BM_MENU_ENTRY             *NewMenuEntry;
1796  BM_LOAD_CONTEXT           *NewLoadContext;
1797
1798  ASSERT (CallbackData != NULL);
1799
1800  DeviceType = (UINTN) -1;
1801  BmmConfig  = &CallbackData->BmmFakeNvData;
1802  ZeroMem (BmmConfig->DriverOptionOrder, sizeof (BmmConfig->DriverOptionOrder));
1803
1804  for (Index = 0, OptionOrderIndex = 0; ((Index < DriverOptionMenu.MenuNumber) &&
1805       (OptionOrderIndex < (sizeof (BmmConfig->DriverOptionOrder) / sizeof (BmmConfig->DriverOptionOrder[0]))));
1806       Index++) {
1807    NewMenuEntry   = BOpt_GetMenuEntry (&DriverOptionMenu, Index);
1808    NewLoadContext = (BM_LOAD_CONTEXT *) NewMenuEntry->VariableContext;
1809
1810    if (NewLoadContext->IsLegacy) {
1811      if (((BBS_BBS_DEVICE_PATH *) NewLoadContext->FilePathList)->DeviceType != DeviceType) {
1812        DeviceType = ((BBS_BBS_DEVICE_PATH *) NewLoadContext->FilePathList)->DeviceType;
1813      } else {
1814        //
1815        // Only show one legacy boot option for the same device type
1816        // assuming the boot options are grouped by the device type
1817        //
1818        continue;
1819      }
1820    }
1821    BmmConfig->DriverOptionOrder[OptionOrderIndex++] = (UINT32) (NewMenuEntry->OptionNumber + 1);
1822  }
1823}
1824