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