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