1 /** @file
2   Driver for Platform Boot Options support.
3 
4 Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
5 SPDX-License-Identifier: BSD-2-Clause-Patent
6 
7 **/
8 
9 #include "BoardBdsHook.h"
10 
11 BOOLEAN    mContinueBoot  = FALSE;
12 BOOLEAN    mBootMenuBoot  = FALSE;
13 BOOLEAN    mPxeBoot       = FALSE;
14 BOOLEAN    mHotKeypressed = FALSE;
15 EFI_EVENT  HotKeyEvent    = NULL;
16 
17 UINTN      mBootMenuOptionNumber;
18 
19 
20 /**
21   This function will create a SHELL BootOption to boot.
22 
23   @return Shell Device path for booting.
24 **/
25 EFI_DEVICE_PATH_PROTOCOL *
BdsCreateShellDevicePath(VOID)26 BdsCreateShellDevicePath (
27   VOID
28   )
29 {
30   UINTN                             FvHandleCount;
31   EFI_HANDLE                        *FvHandleBuffer;
32   UINTN                             Index;
33   EFI_STATUS                        Status;
34   EFI_FIRMWARE_VOLUME2_PROTOCOL     *Fv;
35   UINTN                             Size;
36   UINT32                            AuthenticationStatus;
37   EFI_DEVICE_PATH_PROTOCOL          *DevicePath;
38   VOID                              *Buffer;
39 
40   DevicePath  = NULL;
41   Status      = EFI_SUCCESS;
42 
43   DEBUG ((DEBUG_INFO, "BdsCreateShellDevicePath\n"));
44   gBS->LocateHandleBuffer (
45         ByProtocol,
46         &gEfiFirmwareVolume2ProtocolGuid,
47         NULL,
48         &FvHandleCount,
49         &FvHandleBuffer
50         );
51 
52   for (Index = 0; Index < FvHandleCount; Index++) {
53     gBS->HandleProtocol (
54           FvHandleBuffer[Index],
55           &gEfiFirmwareVolume2ProtocolGuid,
56           (VOID **) &Fv
57           );
58 
59     Buffer  = NULL;
60     Size    = 0;
61     Status  = Fv->ReadSection (
62                     Fv,
63                     &gUefiShellFileGuid,
64                     EFI_SECTION_PE32,
65                     0,
66                     &Buffer,
67                     &Size,
68                     &AuthenticationStatus
69                     );
70     if (EFI_ERROR (Status)) {
71       //
72       // Skip if no shell file in the FV
73       //
74       continue;
75     } else {
76       //
77       // Found the shell
78       //
79       break;
80     }
81   }
82 
83   if (EFI_ERROR (Status)) {
84     //
85     // No shell present
86     //
87     if (FvHandleCount) {
88       FreePool (FvHandleBuffer);
89     }
90     return NULL;
91   }
92   //
93   // Build the shell boot option
94   //
95   DevicePath = DevicePathFromHandle (FvHandleBuffer[Index]);
96 
97   if (FvHandleCount) {
98     FreePool (FvHandleBuffer);
99   }
100 
101   return DevicePath;
102 }
103 
104 
105 EFI_STATUS
CreateFvBootOption(EFI_GUID * FileGuid,CHAR16 * Description,EFI_BOOT_MANAGER_LOAD_OPTION * BootOption,UINT32 Attributes,UINT8 * OptionalData,OPTIONAL UINT32 OptionalDataSize)106 CreateFvBootOption (
107   EFI_GUID                     *FileGuid,
108   CHAR16                       *Description,
109   EFI_BOOT_MANAGER_LOAD_OPTION *BootOption,
110   UINT32                       Attributes,
111   UINT8                        *OptionalData,    OPTIONAL
112   UINT32                       OptionalDataSize
113   )
114 {
115   EFI_STATUS                         Status;
116   EFI_DEVICE_PATH_PROTOCOL           *DevicePath;
117   EFI_LOADED_IMAGE_PROTOCOL          *LoadedImage;
118   MEDIA_FW_VOL_FILEPATH_DEVICE_PATH  FileNode;
119   EFI_FIRMWARE_VOLUME2_PROTOCOL      *Fv;
120   UINT32                             AuthenticationStatus;
121   VOID                               *Buffer;
122   UINTN                              Size;
123 
124   if ((BootOption == NULL) || (FileGuid == NULL) || (Description == NULL)) {
125     return EFI_INVALID_PARAMETER;
126   }
127 
128   EfiInitializeFwVolDevicepathNode (&FileNode, FileGuid);
129 
130   if (!CompareGuid (&gUefiShellFileGuid, FileGuid)) {
131     Status = gBS->HandleProtocol (
132                     gImageHandle,
133                     &gEfiLoadedImageProtocolGuid,
134                     (VOID **) &LoadedImage
135                     );
136     if (!EFI_ERROR (Status)) {
137       Status = gBS->HandleProtocol (
138                       LoadedImage->DeviceHandle,
139                       &gEfiFirmwareVolume2ProtocolGuid,
140                       (VOID **) &Fv
141                       );
142       if (!EFI_ERROR (Status)) {
143         Buffer  = NULL;
144         Size    = 0;
145         Status  = Fv->ReadSection (
146                         Fv,
147                         FileGuid,
148                         EFI_SECTION_PE32,
149                         0,
150                         &Buffer,
151                         &Size,
152                         &AuthenticationStatus
153                         );
154         if (Buffer != NULL) {
155           FreePool (Buffer);
156         }
157       }
158     }
159     if (EFI_ERROR (Status)) {
160       return EFI_NOT_FOUND;
161     }
162 
163     DevicePath = AppendDevicePathNode (
164                    DevicePathFromHandle (LoadedImage->DeviceHandle),
165                    (EFI_DEVICE_PATH_PROTOCOL *) &FileNode
166                    );
167   } else {
168     DevicePath = AppendDevicePathNode (
169                    BdsCreateShellDevicePath (),
170                    (EFI_DEVICE_PATH_PROTOCOL *) &FileNode
171                    );
172   }
173 
174   Status = EfiBootManagerInitializeLoadOption (
175              BootOption,
176              LoadOptionNumberUnassigned,
177              LoadOptionTypeBoot,
178              Attributes,
179              Description,
180              DevicePath,
181              OptionalData,
182              OptionalDataSize
183              );
184   FreePool (DevicePath);
185   return Status;
186 }
187 
188 EFI_GUID mUiFile = {
189   0x462CAA21, 0x7614, 0x4503, { 0x83, 0x6E, 0x8A, 0xB6, 0xF4, 0x66, 0x23, 0x31 }
190 };
191 EFI_GUID mBootMenuFile = {
192   0xEEC25BDC, 0x67F2, 0x4D95, { 0xB1, 0xD5, 0xF8, 0x1B, 0x20, 0x39, 0xD1, 0x1D }
193 };
194 
195 
196 /**
197   Return the index of the load option in the load option array.
198 
199   The function consider two load options are equal when the
200   OptionType, Attributes, Description, FilePath and OptionalData are equal.
201 
202   @param Key    Pointer to the load option to be found.
203   @param Array  Pointer to the array of load options to be found.
204   @param Count  Number of entries in the Array.
205 
206   @retval -1          Key wasn't found in the Array.
207   @retval 0 ~ Count-1 The index of the Key in the Array.
208 **/
209 INTN
PlatformFindLoadOption(IN CONST EFI_BOOT_MANAGER_LOAD_OPTION * Key,IN CONST EFI_BOOT_MANAGER_LOAD_OPTION * Array,IN UINTN Count)210 PlatformFindLoadOption (
211   IN CONST EFI_BOOT_MANAGER_LOAD_OPTION *Key,
212   IN CONST EFI_BOOT_MANAGER_LOAD_OPTION *Array,
213   IN UINTN                              Count
214   )
215 {
216   UINTN                             Index;
217 
218   for (Index = 0; Index < Count; Index++) {
219     if ((Key->OptionType == Array[Index].OptionType) &&
220         (Key->Attributes == Array[Index].Attributes) &&
221         (StrCmp (Key->Description, Array[Index].Description) == 0) &&
222         (CompareMem (Key->FilePath, Array[Index].FilePath, GetDevicePathSize (Key->FilePath)) == 0) &&
223         (Key->OptionalDataSize == Array[Index].OptionalDataSize) &&
224         (CompareMem (Key->OptionalData, Array[Index].OptionalData, Key->OptionalDataSize) == 0)) {
225       return (INTN) Index;
226     }
227   }
228 
229   return -1;
230 }
231 
232 
233 /**
234   Registers a boot option
235 
236   @param FileGuid               Boot file GUID
237   @param Description            Boot option discription
238   @param Position               Position of the new load option to put in the ****Order variable.
239   @param Attributes             Boot option attributes
240   @param OptionalData           Optional data of the boot option.
241   @param OptionalDataSize       Size of the optional data of the boot option
242 
243   @return boot option number
244 **/
245 UINTN
RegisterFvBootOption(EFI_GUID * FileGuid,CHAR16 * Description,UINTN Position,UINT32 Attributes,UINT8 * OptionalData,OPTIONAL UINT32 OptionalDataSize)246 RegisterFvBootOption (
247   EFI_GUID                         *FileGuid,
248   CHAR16                           *Description,
249   UINTN                            Position,
250   UINT32                           Attributes,
251   UINT8                            *OptionalData,    OPTIONAL
252   UINT32                           OptionalDataSize
253   )
254 {
255   EFI_STATUS                       Status;
256   UINTN                            OptionIndex;
257   EFI_BOOT_MANAGER_LOAD_OPTION     NewOption;
258   EFI_BOOT_MANAGER_LOAD_OPTION     *BootOptions;
259   UINTN                            BootOptionCount;
260 
261   NewOption.OptionNumber = LoadOptionNumberUnassigned;
262   Status = CreateFvBootOption (FileGuid, Description, &NewOption, Attributes, OptionalData, OptionalDataSize);
263   if (!EFI_ERROR (Status)) {
264     BootOptions = EfiBootManagerGetLoadOptions (&BootOptionCount, LoadOptionTypeBoot);
265 
266     OptionIndex = PlatformFindLoadOption (&NewOption, BootOptions, BootOptionCount);
267 
268     if (OptionIndex == -1) {
269       Status = EfiBootManagerAddLoadOptionVariable (&NewOption, Position);
270       ASSERT_EFI_ERROR (Status);
271     } else {
272       NewOption.OptionNumber = BootOptions[OptionIndex].OptionNumber;
273     }
274     EfiBootManagerFreeLoadOption (&NewOption);
275     EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);
276   }
277 
278   return NewOption.OptionNumber;
279 }
280 
281 
282 /**
283   Boot manager wait callback
284 
285   @param TimeoutRemain The remaingin timeout period
286 **/
287 VOID
288 EFIAPI
PlatformBootManagerWaitCallback(UINT16 TimeoutRemain)289 PlatformBootManagerWaitCallback (
290   UINT16          TimeoutRemain
291   )
292 {
293   EFI_STATUS                    Status;
294   EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL  *TxtInEx;
295   EFI_KEY_DATA                  KeyData;
296   BOOLEAN                       PausePressed;
297 
298   //
299   // Pause on PAUSE key
300   //
301   Status = gBS->HandleProtocol (gST->ConsoleInHandle, &gEfiSimpleTextInputExProtocolGuid, (VOID **) &TxtInEx);
302   ASSERT_EFI_ERROR (Status);
303 
304   PausePressed = FALSE;
305 
306   while (TRUE) {
307     Status = TxtInEx->ReadKeyStrokeEx (TxtInEx, &KeyData);
308     if (EFI_ERROR (Status)) {
309       break;
310     }
311 
312     if (KeyData.Key.ScanCode == SCAN_PAUSE) {
313       PausePressed = TRUE;
314       break;
315     }
316   }
317 
318   //
319   // Loop until non-PAUSE key pressed
320   //
321   while (PausePressed) {
322     Status = TxtInEx->ReadKeyStrokeEx (TxtInEx, &KeyData);
323     if (!EFI_ERROR (Status)) {
324       DEBUG ((
325         DEBUG_INFO, "[PauseCallback] %x/%x %x/%x\n",
326         KeyData.Key.ScanCode, KeyData.Key.UnicodeChar,
327         KeyData.KeyState.KeyShiftState, KeyData.KeyState.KeyToggleState
328         ));
329       PausePressed = (BOOLEAN) (KeyData.Key.ScanCode == SCAN_PAUSE);
330     }
331   }
332 }
333 
334 
335 EFI_GUID gUefiShellFileGuid = { 0x7C04A583, 0x9E3E, 0x4f1c, { 0xAD, 0x65, 0xE0, 0x52, 0x68, 0xD0, 0xB4, 0xD1 } };
336 
337 #define INTERNAL_UEFI_SHELL_NAME      L"Internal UEFI Shell 2.0"
338 #define UEFI_HARD_DRIVE_NAME          L"UEFI Hard Drive"
339 
340 /**
341    Registers default boot option
342 **/
343 
344 VOID
RegisterDefaultBootOption(VOID)345 RegisterDefaultBootOption (
346   VOID
347   )
348 {
349   UINT16                             *ShellData;
350   UINT32                             ShellDataSize;
351 
352     ShellData = NULL;
353     ShellDataSize = 0;
354     RegisterFvBootOption (&gUefiShellFileGuid,      INTERNAL_UEFI_SHELL_NAME, (UINTN) -1, LOAD_OPTION_ACTIVE, (UINT8 *)ShellData, ShellDataSize);
355 
356   //
357   // Boot Menu
358   //
359   mBootMenuOptionNumber = RegisterFvBootOption (&mBootMenuFile, L"Boot Device List",   (UINTN) -1, LOAD_OPTION_CATEGORY_APP | LOAD_OPTION_ACTIVE | LOAD_OPTION_HIDDEN, NULL, 0);
360 
361   if (mBootMenuOptionNumber == LoadOptionNumberUnassigned) {
362     DEBUG ((DEBUG_INFO, "BootMenuOptionNumber (%d) should not be same to LoadOptionNumberUnassigned(%d).\n", mBootMenuOptionNumber, LoadOptionNumberUnassigned));
363   }
364 #if 0
365   //
366   // Boot Manager Menu
367   //
368   EfiInitializeFwVolDevicepathNode (&FileNode, &mUiFile);
369 
370   gBS->HandleProtocol (
371          gImageHandle,
372          &gEfiLoadedImageProtocolGuid,
373          (VOID **) &LoadedImage
374          );
375   DevicePath = AppendDevicePathNode (DevicePathFromHandle (LoadedImage->DeviceHandle), (EFI_DEVICE_PATH_PROTOCOL *) &FileNode);
376 #endif
377 
378 }
379 
380 /**
381   Registers/Unregisters boot option hotkey
382 
383   @param OptionNumber  The boot option number for the key option.
384   @param Key           The the key input
385   @param Add           Flag to indicate to add or remove a key
386 **/
387 VOID
RegisterBootOptionHotkey(UINT16 OptionNumber,EFI_INPUT_KEY * Key,BOOLEAN Add)388 RegisterBootOptionHotkey (
389   UINT16                       OptionNumber,
390   EFI_INPUT_KEY                *Key,
391   BOOLEAN                      Add
392   )
393 {
394   EFI_STATUS                   Status;
395 
396   if (!Add) {
397     //
398     // No enter hotkey when force to setup or there is no boot option
399     //
400     Status = EfiBootManagerDeleteKeyOptionVariable (NULL, 0, Key, NULL);
401     ASSERT (Status == EFI_SUCCESS || Status == EFI_NOT_FOUND);
402   } else {
403     //
404     // Register enter hotkey for the first boot option
405     //
406     Status = EfiBootManagerAddKeyOptionVariable (NULL, OptionNumber, 0, Key,NULL);
407     ASSERT (Status == EFI_SUCCESS || Status == EFI_ALREADY_STARTED);
408   }
409 }
410 
411 
412 /**
413   Detect key press callback
414 
415   @param    The key data
416 
417   @retval   EFI_SUCCESS
418 **/
419 EFI_STATUS
420 EFIAPI
DetectKeypressCallback(IN EFI_KEY_DATA * KeyData)421 DetectKeypressCallback (
422   IN EFI_KEY_DATA     *KeyData
423 )
424 {
425   mHotKeypressed = TRUE;
426 
427   if (HotKeyEvent != NULL) {
428     gBS->SignalEvent(HotKeyEvent);
429   }
430 
431   return EFI_SUCCESS;
432 }
433 
434 /**
435   This function is called after all the boot options are enumerated and ordered properly.
436 **/
437 VOID
RegisterStaticHotkey(VOID)438 RegisterStaticHotkey (
439   VOID
440   )
441 {
442 
443   EFI_INPUT_KEY                 Enter;
444   EFI_KEY_DATA                  F2;
445   EFI_KEY_DATA                  F7;
446   BOOLEAN                       EnterSetup;
447   EFI_STATUS                    Status;
448   EFI_BOOT_MANAGER_LOAD_OPTION  BootOption;
449 
450   EnterSetup = FALSE;
451 
452   //
453   // [Enter]
454   //
455   mContinueBoot = !EnterSetup;
456   if (mContinueBoot) {
457     Enter.ScanCode    = SCAN_NULL;
458     Enter.UnicodeChar = CHAR_CARRIAGE_RETURN;
459     EfiBootManagerRegisterContinueKeyOption (0, &Enter, NULL);
460   }
461 
462 
463   //
464   // [F2]/[F7]
465   //
466   F2.Key.ScanCode    = SCAN_F2;
467   F2.Key.UnicodeChar = CHAR_NULL;
468   F2.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID;
469   F2.KeyState.KeyToggleState = 0;
470   Status = EfiBootManagerGetBootManagerMenu (&BootOption);
471   ASSERT_EFI_ERROR (Status);
472   RegisterBootOptionHotkey ((UINT16) BootOption.OptionNumber, &F2.Key, TRUE);
473   EfiBootManagerFreeLoadOption (&BootOption);
474 
475   F7.Key.ScanCode    = SCAN_F7;
476   F7.Key.UnicodeChar = CHAR_NULL;
477   F7.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID;
478   F7.KeyState.KeyToggleState = 0;
479   mBootMenuBoot  = !EnterSetup;
480   RegisterBootOptionHotkey ((UINT16) mBootMenuOptionNumber, &F7.Key, mBootMenuBoot);
481 
482 }
483 
484 
485 
486 /**
487   Returns the boot option type of a device
488 
489   @param DevicePath             The path of device whose boot option type
490                                 to be returned
491   @retval -1                    Device type not found
492   @retval > -1                  Device type found
493 **/
494 UINT8
BootOptionType(IN EFI_DEVICE_PATH_PROTOCOL * DevicePath)495 BootOptionType (
496   IN EFI_DEVICE_PATH_PROTOCOL   *DevicePath
497   )
498 {
499   EFI_DEVICE_PATH_PROTOCOL      *Node;
500   EFI_DEVICE_PATH_PROTOCOL      *NextNode;
501 
502   for (Node = DevicePath; !IsDevicePathEndType (Node); Node = NextDevicePathNode (Node)) {
503     if (DevicePathType (Node) == MESSAGING_DEVICE_PATH) {
504       //
505       // Make sure the device path points to the driver device.
506       //
507       NextNode = NextDevicePathNode (Node);
508       if (DevicePathSubType(NextNode) == MSG_DEVICE_LOGICAL_UNIT_DP) {
509         //
510         // if the next node type is Device Logical Unit, which specify the Logical Unit Number (LUN),
511         // skip it
512         //
513         NextNode = NextDevicePathNode (NextNode);
514       }
515       if (IsDevicePathEndType (NextNode)) {
516         if ((DevicePathType (Node) == MESSAGING_DEVICE_PATH)) {
517           return DevicePathSubType (Node);
518         } else {
519           return MSG_SATA_DP;
520         }
521       }
522     }
523   }
524 
525   return (UINT8) -1;
526 }
527 
528 /**
529   Returns the priority number.
530   OptionType                 EFI
531   ------------------------------------
532   PXE                         2
533   DVD                         4
534   USB                         6
535   NVME                        7
536   HDD                         8
537   EFI Shell                   9
538   Others                      100
539 
540   @param BootOption
541 **/
542 UINTN
BootOptionPriority(CONST EFI_BOOT_MANAGER_LOAD_OPTION * BootOption)543 BootOptionPriority (
544   CONST EFI_BOOT_MANAGER_LOAD_OPTION *BootOption
545   )
546 {
547     //
548     // EFI boot options
549     //
550     switch (BootOptionType (BootOption->FilePath)) {
551     case MSG_MAC_ADDR_DP:
552     case MSG_VLAN_DP:
553     case MSG_IPv4_DP:
554     case MSG_IPv6_DP:
555       return 2;
556 
557     case MSG_SATA_DP:
558     case MSG_ATAPI_DP:
559     case MSG_UFS_DP:
560     case MSG_NVME_NAMESPACE_DP:
561       return 4;
562 
563     case MSG_USB_DP:
564       return 6;
565 
566     }
567     if (StrCmp (BootOption->Description, INTERNAL_UEFI_SHELL_NAME) == 0) {
568       if (PcdGetBool (PcdBootToShellOnly)) {
569         return 0;
570       }
571       return 9;
572     }
573     if (StrCmp (BootOption->Description, UEFI_HARD_DRIVE_NAME) == 0) {
574       return 8;
575     }
576     return 100;
577 }
578 
579 /**
580    Compares boot priorities of two boot options
581 
582   @param Left       The left boot option
583   @param Right      The right boot option
584 
585   @return           The difference between the Left and Right
586                     boot options
587  **/
588 INTN
589 EFIAPI
CompareBootOption(CONST VOID * Left,CONST VOID * Right)590 CompareBootOption (
591   CONST VOID  *Left,
592   CONST VOID  *Right
593   )
594 {
595   return BootOptionPriority ((EFI_BOOT_MANAGER_LOAD_OPTION *) Left) -
596          BootOptionPriority ((EFI_BOOT_MANAGER_LOAD_OPTION *) Right);
597 }
598 
599