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