1 /** @file 2 * 3 * Copyright (c) 2011 - 2015, ARM Limited. All rights reserved. 4 * 5 * This program and the accompanying materials 6 * are licensed and made available under the terms and conditions of the BSD License 7 * which accompanies this distribution. The full text of the license may be found at 8 * http://opensource.org/licenses/bsd-license.php 9 * 10 * THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. 12 * 13 **/ 14 15 #include "BdsInternal.h" 16 17 #include <libfdt.h> 18 19 /** 20 Worker function that displays the list of boot options that is passed in. 21 22 The function loops over the entries of the list of boot options that is passed 23 in. For each entry, the boot option description is displayed on a single line 24 along with the position of the option in the list. In debug mode, the UEFI 25 device path and the arguments of the boot option are displayed as well in 26 subsequent lines. 27 28 @param[in] BootOptionsList List of the boot options 29 30 **/ 31 STATIC 32 VOID 33 DisplayBootOptions ( 34 IN LIST_ENTRY* BootOptionsList 35 ) 36 { 37 EFI_STATUS Status; 38 UINTN BootOptionCount; 39 LIST_ENTRY *Entry; 40 BDS_LOAD_OPTION *BdsLoadOption; 41 BOOLEAN IsUnicode; 42 43 BootOptionCount = 0 ; 44 for (Entry = GetFirstNode (BootOptionsList); 45 !IsNull (BootOptionsList, Entry); 46 Entry = GetNextNode (BootOptionsList, Entry) 47 ) { 48 49 BdsLoadOption = LOAD_OPTION_FROM_LINK (Entry); 50 Print (L"[%d] %s\n", ++BootOptionCount, BdsLoadOption->Description); 51 52 DEBUG_CODE_BEGIN (); 53 CHAR16* DevicePathTxt; 54 EFI_DEVICE_PATH_TO_TEXT_PROTOCOL* DevicePathToTextProtocol; 55 56 Status = gBS->LocateProtocol ( 57 &gEfiDevicePathToTextProtocolGuid, 58 NULL, 59 (VOID **)&DevicePathToTextProtocol 60 ); 61 ASSERT_EFI_ERROR (Status); 62 DevicePathTxt = DevicePathToTextProtocol->ConvertDevicePathToText ( 63 BdsLoadOption->FilePathList, 64 TRUE, 65 TRUE 66 ); 67 Print (L"\t- %s\n", DevicePathTxt); 68 69 if (IsPrintableString (BdsLoadOption->OptionalData, &IsUnicode)) { 70 if (IsUnicode) { 71 Print (L"\t- Arguments: %s\n", BdsLoadOption->OptionalData); 72 } else { 73 AsciiPrint ("\t- Arguments: %a\n", BdsLoadOption->OptionalData); 74 } 75 } 76 77 FreePool (DevicePathTxt); 78 DEBUG_CODE_END (); 79 } 80 } 81 82 /** 83 Worker function that asks for a boot option to be selected and returns a 84 pointer to the structure describing the selected boot option. 85 86 @param[in] BootOptionsList List of the boot options 87 88 @retval EFI_SUCCESS Selection succeeded 89 @retval !EFI_SUCCESS Input error or input cancelled 90 91 **/ 92 STATIC 93 EFI_STATUS 94 SelectBootOption ( 95 IN LIST_ENTRY* BootOptionsList, 96 IN CONST CHAR16* InputStatement, 97 OUT BDS_LOAD_OPTION_ENTRY** BdsLoadOptionEntry 98 ) 99 { 100 EFI_STATUS Status; 101 UINTN BootOptionCount; 102 UINT16 *BootOrder; 103 LIST_ENTRY* Entry; 104 UINTN BootOptionSelected; 105 UINTN Index; 106 107 // Get the number of boot options 108 Status = GetGlobalEnvironmentVariable ( 109 L"BootOrder", NULL, &BootOptionCount, (VOID**)&BootOrder 110 ); 111 if (EFI_ERROR (Status)) { 112 goto ErrorExit; 113 } 114 FreePool (BootOrder); 115 BootOptionCount /= sizeof (UINT16); 116 117 // Check if a valid boot option(s) is found 118 if (BootOptionCount == 0) { 119 if (StrCmp (InputStatement, DELETE_BOOT_ENTRY) == 0) { 120 Print (L"Nothing to remove!\n"); 121 } else if (StrCmp (InputStatement, UPDATE_BOOT_ENTRY) == 0) { 122 Print (L"Nothing to update!\n"); 123 } else if (StrCmp (InputStatement, MOVE_BOOT_ENTRY) == 0) { 124 Print (L"Nothing to move!\n"); 125 } else { 126 Print (L"No supported Boot Entry.\n"); 127 } 128 return EFI_NOT_FOUND; 129 } 130 131 // Get the index of the boot device to delete 132 BootOptionSelected = 0; 133 while (BootOptionSelected == 0) { 134 Print (InputStatement); 135 Status = GetHIInputInteger (&BootOptionSelected); 136 if (EFI_ERROR (Status)) { 137 Print (L"\n"); 138 goto ErrorExit; 139 } else if ((BootOptionSelected == 0) || (BootOptionSelected > BootOptionCount)) { 140 Print (L"Invalid input (max %d)\n", BootOptionCount); 141 BootOptionSelected = 0; 142 } 143 } 144 145 // Get the structure of the Boot device to delete 146 Index = 1; 147 for (Entry = GetFirstNode (BootOptionsList); 148 !IsNull (BootOptionsList, Entry); 149 Entry = GetNextNode (BootOptionsList,Entry) 150 ) 151 { 152 if (Index == BootOptionSelected) { 153 *BdsLoadOptionEntry = LOAD_OPTION_ENTRY_FROM_LINK (Entry); 154 break; 155 } 156 Index++; 157 } 158 159 ErrorExit: 160 return Status; 161 } 162 163 STATIC 164 EFI_STATUS 165 SelectBootDevice ( 166 OUT BDS_SUPPORTED_DEVICE** SupportedBootDevice 167 ) 168 { 169 EFI_STATUS Status; 170 LIST_ENTRY SupportedDeviceList; 171 UINTN SupportedDeviceCount; 172 LIST_ENTRY* Entry; 173 UINTN SupportedDeviceSelected; 174 UINTN Index; 175 176 // 177 // List the Boot Devices supported 178 // 179 180 // Start all the drivers first 181 BdsConnectAllDrivers (); 182 183 // List the supported devices 184 Status = BootDeviceListSupportedInit (&SupportedDeviceList); 185 ASSERT_EFI_ERROR(Status); 186 187 SupportedDeviceCount = 0; 188 for (Entry = GetFirstNode (&SupportedDeviceList); 189 !IsNull (&SupportedDeviceList,Entry); 190 Entry = GetNextNode (&SupportedDeviceList,Entry) 191 ) 192 { 193 *SupportedBootDevice = SUPPORTED_BOOT_DEVICE_FROM_LINK(Entry); 194 Print(L"[%d] %s\n",SupportedDeviceCount+1,(*SupportedBootDevice)->Description); 195 196 DEBUG_CODE_BEGIN(); 197 CHAR16* DevicePathTxt; 198 EFI_DEVICE_PATH_TO_TEXT_PROTOCOL* DevicePathToTextProtocol; 199 200 Status = gBS->LocateProtocol (&gEfiDevicePathToTextProtocolGuid, NULL, (VOID **)&DevicePathToTextProtocol); 201 ASSERT_EFI_ERROR(Status); 202 DevicePathTxt = DevicePathToTextProtocol->ConvertDevicePathToText ((*SupportedBootDevice)->DevicePathProtocol,TRUE,TRUE); 203 204 Print(L"\t- %s\n",DevicePathTxt); 205 206 FreePool(DevicePathTxt); 207 DEBUG_CODE_END(); 208 209 SupportedDeviceCount++; 210 } 211 212 if (SupportedDeviceCount == 0) { 213 Print(L"There is no supported device.\n"); 214 Status = EFI_ABORTED; 215 goto EXIT; 216 } 217 218 // 219 // Select the Boot Device 220 // 221 SupportedDeviceSelected = 0; 222 while (SupportedDeviceSelected == 0) { 223 Print(L"Select the Boot Device: "); 224 Status = GetHIInputInteger (&SupportedDeviceSelected); 225 if (EFI_ERROR(Status)) { 226 Status = EFI_ABORTED; 227 goto EXIT; 228 } else if ((SupportedDeviceSelected == 0) || (SupportedDeviceSelected > SupportedDeviceCount)) { 229 Print(L"Invalid input (max %d)\n",SupportedDeviceCount); 230 SupportedDeviceSelected = 0; 231 } 232 } 233 234 // 235 // Get the Device Path for the selected boot device 236 // 237 Index = 1; 238 for (Entry = GetFirstNode (&SupportedDeviceList); 239 !IsNull (&SupportedDeviceList,Entry); 240 Entry = GetNextNode (&SupportedDeviceList,Entry) 241 ) 242 { 243 if (Index == SupportedDeviceSelected) { 244 *SupportedBootDevice = SUPPORTED_BOOT_DEVICE_FROM_LINK(Entry); 245 break; 246 } 247 Index++; 248 } 249 250 EXIT: 251 BootDeviceListSupportedFree (&SupportedDeviceList, *SupportedBootDevice); 252 return Status; 253 } 254 255 EFI_STATUS 256 BootMenuAddBootOption ( 257 IN LIST_ENTRY *BootOptionsList 258 ) 259 { 260 EFI_STATUS Status; 261 BDS_SUPPORTED_DEVICE* SupportedBootDevice; 262 CHAR16 BootDescription[BOOT_DEVICE_DESCRIPTION_MAX]; 263 CHAR16 CmdLine[BOOT_DEVICE_OPTION_MAX]; 264 UINT32 Attributes; 265 BDS_LOAD_OPTION_ENTRY *BdsLoadOptionEntry; 266 EFI_DEVICE_PATH *DevicePath; 267 EFI_DEVICE_PATH_PROTOCOL *DevicePathNodes; 268 UINT8* OptionalData; 269 UINTN OptionalDataSize; 270 BOOLEAN EfiBinary; 271 CHAR16 *LinuxDevicePath; 272 273 Attributes = 0; 274 SupportedBootDevice = NULL; 275 276 // List the Boot Devices supported 277 Status = SelectBootDevice (&SupportedBootDevice); 278 if (EFI_ERROR(Status)) { 279 Status = EFI_ABORTED; 280 goto EXIT; 281 } 282 283 // Create the specific device path node 284 if (FeaturePcdGet (PcdBdsLinuxSupport) && mLinuxLoaderDevicePath) { 285 Status = SupportedBootDevice->Support->CreateDevicePathNode (L"EFI Application or the kernel", &DevicePathNodes); 286 } else { 287 Status = SupportedBootDevice->Support->CreateDevicePathNode (L"EFI Application", &DevicePathNodes); 288 } 289 if (EFI_ERROR (Status)) { 290 Status = EFI_ABORTED; 291 goto EXIT; 292 } 293 // Append the Device Path to the selected device path 294 DevicePath = AppendDevicePath (SupportedBootDevice->DevicePathProtocol, (CONST EFI_DEVICE_PATH_PROTOCOL *)DevicePathNodes); 295 if (DevicePath == NULL) { 296 Status = EFI_OUT_OF_RESOURCES; 297 goto EXIT; 298 } 299 300 // Is it an EFI application? 301 if (FeaturePcdGet (PcdBdsLinuxSupport) && mLinuxLoaderDevicePath) { 302 Status = IsEfiBinary (DevicePath, &EfiBinary); 303 if (EFI_ERROR (Status)) { 304 Status = EFI_ABORTED; 305 goto EXIT; 306 } 307 308 if (EfiBinary == FALSE) { 309 Print (L"It is assumed the binary is a Linux kernel and the embedded Linux Loader is going to be used.\n"); 310 Print (L"Supported command line formats by the embedded Linux Loader:\n"); 311 Print (L"- <EFI device path of the Linux kernel> -c \"<Linux kernel command line>\"\n"); 312 Print (L"- <EFI device path of the Linux kernel> -c \"<Linux kernel command line>\" -f <EFI Device Path of the Linux initrd>\n"); 313 Print (L"- <EFI device path of the Linux kernel> -c \"<Linux kernel command line>\" -a <Machine Type for ATAG Linux kernel>\n"); 314 315 // Copy the Linux path into the command line 316 LinuxDevicePath = ConvertDevicePathToText (DevicePath, FALSE, FALSE); 317 CopyMem (CmdLine, LinuxDevicePath, MAX (sizeof (CmdLine), StrSize (LinuxDevicePath))); 318 FreePool (LinuxDevicePath); 319 320 // Free the generated Device Path 321 FreePool (DevicePath); 322 // and use the embedded Linux Loader as the EFI application 323 DevicePath = mLinuxLoaderDevicePath; 324 } else { 325 CmdLine[0] = L'\0'; 326 } 327 } else { 328 CmdLine[0] = L'\0'; 329 } 330 331 Print (L"Arguments to pass to the EFI Application: "); 332 Status = EditHIInputStr (CmdLine, BOOT_DEVICE_OPTION_MAX); 333 if (EFI_ERROR (Status)) { 334 Status = EFI_ABORTED; 335 goto EXIT; 336 } 337 338 OptionalData = (UINT8*)CmdLine; 339 OptionalDataSize = StrSize (CmdLine); 340 341 Print(L"Description for this new Entry: "); 342 Status = GetHIInputStr (BootDescription, BOOT_DEVICE_DESCRIPTION_MAX); 343 if (EFI_ERROR(Status)) { 344 Status = EFI_ABORTED; 345 goto FREE_DEVICE_PATH; 346 } 347 348 // Create new entry 349 BdsLoadOptionEntry = (BDS_LOAD_OPTION_ENTRY*)AllocatePool (sizeof(BDS_LOAD_OPTION_ENTRY)); 350 Status = BootOptionCreate (Attributes, BootDescription, DevicePath, OptionalData, OptionalDataSize, &BdsLoadOptionEntry->BdsLoadOption); 351 if (!EFI_ERROR(Status)) { 352 InsertTailList (BootOptionsList, &BdsLoadOptionEntry->Link); 353 } 354 355 FREE_DEVICE_PATH: 356 FreePool (DevicePath); 357 358 EXIT: 359 if (Status == EFI_ABORTED) { 360 Print(L"\n"); 361 } 362 FreePool(SupportedBootDevice); 363 return Status; 364 } 365 366 EFI_STATUS 367 BootMenuRemoveBootOption ( 368 IN LIST_ENTRY *BootOptionsList 369 ) 370 { 371 EFI_STATUS Status; 372 BDS_LOAD_OPTION_ENTRY* BootOptionEntry; 373 374 DisplayBootOptions (BootOptionsList); 375 Status = SelectBootOption (BootOptionsList, DELETE_BOOT_ENTRY, &BootOptionEntry); 376 if (EFI_ERROR (Status)) { 377 return Status; 378 } 379 380 // If the Boot Option was attached to a list remove it 381 if (!IsListEmpty (&BootOptionEntry->Link)) { 382 // Remove the entry from the list 383 RemoveEntryList (&BootOptionEntry->Link); 384 } 385 386 // Delete the BDS Load option structures 387 BootOptionDelete (BootOptionEntry->BdsLoadOption); 388 389 return EFI_SUCCESS; 390 } 391 392 EFI_STATUS 393 BootMenuUpdateBootOption ( 394 IN LIST_ENTRY *BootOptionsList 395 ) 396 { 397 EFI_STATUS Status; 398 BDS_LOAD_OPTION_ENTRY *BootOptionEntry; 399 BDS_LOAD_OPTION *BootOption; 400 BDS_LOAD_OPTION_SUPPORT* DeviceSupport; 401 CHAR16 BootDescription[BOOT_DEVICE_DESCRIPTION_MAX]; 402 CHAR8 CmdLine[BOOT_DEVICE_OPTION_MAX]; 403 CHAR16 UnicodeCmdLine[BOOT_DEVICE_OPTION_MAX]; 404 CHAR16 *LinuxDevicePath; 405 EFI_DEVICE_PATH *DevicePath; 406 UINT8* OptionalData; 407 UINTN OptionalDataSize; 408 BOOLEAN IsPrintable; 409 BOOLEAN IsUnicode; 410 BOOLEAN EfiBinary; 411 412 DisplayBootOptions (BootOptionsList); 413 Status = SelectBootOption (BootOptionsList, UPDATE_BOOT_ENTRY, &BootOptionEntry); 414 if (EFI_ERROR (Status)) { 415 return Status; 416 } 417 BootOption = BootOptionEntry->BdsLoadOption; 418 419 // Get the device support for this Boot Option 420 Status = BootDeviceGetDeviceSupport (BootOption->FilePathList, &DeviceSupport); 421 if (EFI_ERROR(Status)) { 422 Print(L"Not possible to retrieve the supported device for the update\n"); 423 return EFI_UNSUPPORTED; 424 } 425 426 EfiBinary = TRUE; 427 if (FeaturePcdGet (PcdBdsLinuxSupport) && mLinuxLoaderDevicePath) { 428 Status = DeviceSupport->UpdateDevicePathNode (BootOption->FilePathList, L"EFI Application or the kernel", &DevicePath); 429 if (EFI_ERROR (Status)) { 430 Status = EFI_ABORTED; 431 goto EXIT; 432 } 433 434 // Is it an EFI application? 435 Status = IsEfiBinary (DevicePath, &EfiBinary); 436 if (EFI_ERROR (Status)) { 437 Status = EFI_ABORTED; 438 goto EXIT; 439 } 440 441 if (EfiBinary == FALSE) { 442 Print (L"It is assumed the binary is a Linux kernel and the embedded Linux Loader is going to be used.\n"); 443 Print (L"Supported command line formats by the embedded Linux Loader:\n"); 444 Print (L"- <EFI device path of the Linux kernel> -c \"<Linux kernel command line>\"\n"); 445 Print (L"- <EFI device path of the Linux kernel> -c \"<Linux kernel command line>\" -f <EFI Device Path of the Linux initrd>\n"); 446 Print (L"- <EFI device path of the Linux kernel> -c \"<Linux kernel command line>\" -a <Machine Type for ATAG Linux kernel>\n"); 447 448 // Copy the Linux path into the command line 449 LinuxDevicePath = ConvertDevicePathToText (DevicePath, FALSE, FALSE); 450 CopyMem (UnicodeCmdLine, LinuxDevicePath, MAX (sizeof (UnicodeCmdLine), StrSize (LinuxDevicePath))); 451 FreePool (LinuxDevicePath); 452 453 // Free the generated Device Path 454 FreePool (DevicePath); 455 // and use the embedded Linux Loader as the EFI application 456 DevicePath = mLinuxLoaderDevicePath; 457 458 // The command line is a unicode printable string 459 IsPrintable = TRUE; 460 IsUnicode = TRUE; 461 } 462 } else { 463 Status = DeviceSupport->UpdateDevicePathNode (BootOption->FilePathList, L"EFI Application", &DevicePath); 464 if (EFI_ERROR (Status)) { 465 Status = EFI_ABORTED; 466 goto EXIT; 467 } 468 } 469 470 Print (L"Arguments to pass to the EFI Application: "); 471 472 // When the command line has not been initialized by the embedded Linux loader earlier 473 if (EfiBinary) { 474 if (BootOption->OptionalDataSize > 0) { 475 IsPrintable = IsPrintableString (BootOption->OptionalData, &IsUnicode); 476 if (IsPrintable) { 477 // 478 // The size in bytes of the string, final zero included, should 479 // be equal to or at least lower than "BootOption->OptionalDataSize" 480 // and the "IsPrintableString()" has already tested that the length 481 // in number of characters is smaller than BOOT_DEVICE_OPTION_MAX, 482 // final '\0' included. We can thus copy the string for editing 483 // using "CopyMem()". Furthermore, note that in the case of an Unicode 484 // string "StrnCpy()" and "StrCpy()" can not be used to copy the 485 // string because the data pointed to by "BootOption->OptionalData" 486 // is not necessarily 2-byte aligned. 487 // 488 if (IsUnicode) { 489 CopyMem ( 490 UnicodeCmdLine, BootOption->OptionalData, 491 MIN (sizeof (UnicodeCmdLine), 492 BootOption->OptionalDataSize) 493 ); 494 } else { 495 CopyMem ( 496 CmdLine, BootOption->OptionalData, 497 MIN (sizeof (CmdLine), 498 BootOption->OptionalDataSize) 499 ); 500 } 501 } 502 } else { 503 UnicodeCmdLine[0] = L'\0'; 504 IsPrintable = TRUE; 505 IsUnicode = TRUE; 506 } 507 } 508 509 // We do not request arguments for OptionalData that cannot be printed 510 if (IsPrintable) { 511 if (IsUnicode) { 512 Status = EditHIInputStr (UnicodeCmdLine, BOOT_DEVICE_OPTION_MAX); 513 if (EFI_ERROR (Status)) { 514 Status = EFI_ABORTED; 515 goto FREE_DEVICE_PATH; 516 } 517 518 OptionalData = (UINT8*)UnicodeCmdLine; 519 OptionalDataSize = StrSize (UnicodeCmdLine); 520 } else { 521 Status = EditHIInputAscii (CmdLine, BOOT_DEVICE_OPTION_MAX); 522 if (EFI_ERROR (Status)) { 523 Status = EFI_ABORTED; 524 goto FREE_DEVICE_PATH; 525 } 526 527 OptionalData = (UINT8*)CmdLine; 528 OptionalDataSize = AsciiStrSize (CmdLine); 529 } 530 } else { 531 // We keep the former OptionalData 532 OptionalData = BootOption->OptionalData; 533 OptionalDataSize = BootOption->OptionalDataSize; 534 } 535 536 Print(L"Description for this new Entry: "); 537 StrnCpy (BootDescription, BootOption->Description, BOOT_DEVICE_DESCRIPTION_MAX); 538 Status = EditHIInputStr (BootDescription, BOOT_DEVICE_DESCRIPTION_MAX); 539 if (EFI_ERROR(Status)) { 540 Status = EFI_ABORTED; 541 goto FREE_DEVICE_PATH; 542 } 543 544 // Update the entry 545 Status = BootOptionUpdate (BootOption, BootOption->Attributes, BootDescription, DevicePath, OptionalData, OptionalDataSize); 546 547 FREE_DEVICE_PATH: 548 FreePool (DevicePath); 549 550 EXIT: 551 if (Status == EFI_ABORTED) { 552 Print(L"\n"); 553 } 554 return Status; 555 } 556 557 /** 558 Reorder boot options 559 560 Ask for the boot option to move and then move it when up or down arrows 561 are pressed. This function is called when the user selects the "Reorder Boot 562 Device Entries" entry in the boot manager menu. 563 The order of the boot options in BootOptionList and in the UEFI BootOrder 564 global variable are kept coherent until the user confirm his reordering (ie: 565 he does not exit by pressing escape). 566 567 @param[in] BootOptionsList List of the boot devices constructed in 568 BootMenuMain() 569 570 @retval EFI_SUCCESS No error encountered. 571 @retval !EFI_SUCCESS An error has occured either in the selection of the 572 boot option to move or while interacting with the user. 573 574 **/ 575 STATIC 576 EFI_STATUS 577 BootMenuReorderBootOptions ( 578 IN LIST_ENTRY *BootOptionsList 579 ) 580 { 581 EFI_STATUS Status; 582 BDS_LOAD_OPTION_ENTRY *BootOptionEntry; 583 LIST_ENTRY *SelectedEntry; 584 LIST_ENTRY *PrevEntry; 585 BOOLEAN Move; 586 BOOLEAN Save; 587 BOOLEAN Cancel; 588 UINTN WaitIndex; 589 EFI_INPUT_KEY Key; 590 LIST_ENTRY *SecondEntry; 591 UINTN BootOrderSize; 592 UINT16 *BootOrder; 593 LIST_ENTRY *Entry; 594 UINTN Index; 595 596 DisplayBootOptions (BootOptionsList); 597 598 // Ask to select the boot option to move 599 while (TRUE) { 600 Status = SelectBootOption (BootOptionsList, MOVE_BOOT_ENTRY, &BootOptionEntry); 601 if (EFI_ERROR (Status)) { 602 goto ErrorExit; 603 } 604 605 SelectedEntry = &BootOptionEntry->Link; 606 SecondEntry = NULL; 607 // Note down the previous entry in the list to be able to cancel changes 608 PrevEntry = GetPreviousNode (BootOptionsList, SelectedEntry); 609 610 // Start of interaction 611 while (TRUE) { 612 Print ( 613 L"* Use up/down arrows to move the entry '%s'", 614 BootOptionEntry->BdsLoadOption->Description 615 ); 616 617 // Wait for a move, save or cancel request 618 Move = FALSE; 619 Save = FALSE; 620 Cancel = FALSE; 621 do { 622 Status = gBS->WaitForEvent (1, &gST->ConIn->WaitForKey, &WaitIndex); 623 if (!EFI_ERROR (Status)) { 624 Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key); 625 } 626 if (EFI_ERROR (Status)) { 627 Print (L"\n"); 628 goto ErrorExit; 629 } 630 631 switch (Key.ScanCode) { 632 case SCAN_NULL: 633 Save = (Key.UnicodeChar == CHAR_LINEFEED) || 634 (Key.UnicodeChar == CHAR_CARRIAGE_RETURN) || 635 (Key.UnicodeChar == 0x7f); 636 break; 637 638 case SCAN_UP: 639 SecondEntry = GetPreviousNode (BootOptionsList, SelectedEntry); 640 Move = SecondEntry != BootOptionsList; 641 break; 642 643 case SCAN_DOWN: 644 SecondEntry = GetNextNode (BootOptionsList, SelectedEntry); 645 Move = SecondEntry != BootOptionsList; 646 break; 647 648 case SCAN_ESC: 649 Cancel = TRUE; 650 break; 651 } 652 } while ((!Move) && (!Save) && (!Cancel)); 653 654 if (Move) { 655 if ((SelectedEntry != NULL) && (SecondEntry != NULL)) { 656 SwapListEntries (SelectedEntry, SecondEntry); 657 } 658 } else { 659 if (Save) { 660 Status = GetGlobalEnvironmentVariable ( 661 L"BootOrder", NULL, &BootOrderSize, (VOID**)&BootOrder 662 ); 663 BootOrderSize /= sizeof (UINT16); 664 665 if (!EFI_ERROR (Status)) { 666 // The order of the boot options in the 'BootOptionsList' is the 667 // new order that has been just defined by the user. Save this new 668 // order in "BootOrder" UEFI global variable. 669 Entry = GetFirstNode (BootOptionsList); 670 for (Index = 0; Index < BootOrderSize; Index++) { 671 BootOrder[Index] = (LOAD_OPTION_FROM_LINK (Entry))->LoadOptionIndex; 672 Entry = GetNextNode (BootOptionsList, Entry); 673 } 674 Status = gRT->SetVariable ( 675 (CHAR16*)L"BootOrder", 676 &gEfiGlobalVariableGuid, 677 EFI_VARIABLE_NON_VOLATILE | 678 EFI_VARIABLE_BOOTSERVICE_ACCESS | 679 EFI_VARIABLE_RUNTIME_ACCESS, 680 BootOrderSize * sizeof (UINT16), 681 BootOrder 682 ); 683 FreePool (BootOrder); 684 } 685 686 if (EFI_ERROR (Status)) { 687 Print (L"\nAn error occurred, move not completed!\n"); 688 Cancel = TRUE; 689 } 690 } 691 692 if (Cancel) { 693 // 694 // Restore initial position of the selected boot option 695 // 696 RemoveEntryList (SelectedEntry); 697 InsertHeadList (PrevEntry, SelectedEntry); 698 } 699 } 700 701 Print (L"\n"); 702 DisplayBootOptions (BootOptionsList); 703 // Saved or cancelled, back to the choice of boot option to move 704 if (!Move) { 705 break; 706 } 707 } 708 } 709 710 ErrorExit: 711 return Status ; 712 } 713 714 EFI_STATUS 715 UpdateFdtPath ( 716 IN LIST_ENTRY *BootOptionsList 717 ) 718 { 719 EFI_STATUS Status; 720 BDS_SUPPORTED_DEVICE *SupportedBootDevice; 721 EFI_DEVICE_PATH_PROTOCOL *FdtDevicePathNodes; 722 EFI_DEVICE_PATH_PROTOCOL *FdtDevicePath; 723 CHAR16 *FdtTextDevicePath; 724 EFI_PHYSICAL_ADDRESS FdtBlobBase; 725 UINTN FdtBlobSize; 726 UINTN NumPages; 727 EFI_PHYSICAL_ADDRESS FdtConfigurationTableBase; 728 729 SupportedBootDevice = NULL; 730 731 Status = SelectBootDevice (&SupportedBootDevice); 732 if (EFI_ERROR (Status)) { 733 Status = EFI_ABORTED; 734 goto EXIT; 735 } 736 737 // Create the specific device path node 738 Status = SupportedBootDevice->Support->CreateDevicePathNode (L"FDT blob", &FdtDevicePathNodes); 739 if (EFI_ERROR (Status)) { 740 Status = EFI_ABORTED; 741 goto EXIT; 742 } 743 744 if (FdtDevicePathNodes != NULL) { 745 Status = EFI_OUT_OF_RESOURCES; 746 747 FdtDevicePath = AppendDevicePath (SupportedBootDevice->DevicePathProtocol, FdtDevicePathNodes); 748 FreePool (FdtDevicePathNodes); 749 if (FdtDevicePath == NULL) { 750 goto EXIT; 751 } 752 753 FdtTextDevicePath = ConvertDevicePathToText (FdtDevicePath, TRUE, TRUE); 754 if (FdtTextDevicePath == NULL) { 755 goto EXIT; 756 } 757 758 Status = gRT->SetVariable ( 759 (CHAR16*)L"Fdt", 760 &gFdtVariableGuid, 761 EFI_VARIABLE_RUNTIME_ACCESS | 762 EFI_VARIABLE_NON_VOLATILE | 763 EFI_VARIABLE_BOOTSERVICE_ACCESS, 764 StrSize (FdtTextDevicePath), 765 FdtTextDevicePath 766 ); 767 ASSERT_EFI_ERROR (Status); 768 FreePool (FdtTextDevicePath); 769 } else { 770 Status = gRT->SetVariable ( 771 (CHAR16*)L"Fdt", 772 &gFdtVariableGuid, 773 EFI_VARIABLE_RUNTIME_ACCESS | 774 EFI_VARIABLE_NON_VOLATILE | 775 EFI_VARIABLE_BOOTSERVICE_ACCESS, 776 0, 777 NULL 778 ); 779 ASSERT_EFI_ERROR (Status); 780 return Status; 781 } 782 783 // 784 // Try to load FDT from the new EFI Device Path 785 // 786 787 // 788 // Load the FDT given its device path. 789 // This operation may fail if the device path is not supported. 790 // 791 FdtBlobBase = 0; 792 NumPages = 0; 793 Status = BdsLoadImage (FdtDevicePath, AllocateAnyPages, &FdtBlobBase, &FdtBlobSize); 794 FreePool (FdtDevicePath); 795 796 if (EFI_ERROR (Status)) { 797 goto EXIT_LOAD_FDT; 798 } 799 800 // Check the FDT header is valid. We only make this check in DEBUG mode in 801 // case the FDT header change on production device and this ASSERT() becomes 802 // not valid. 803 ASSERT (fdt_check_header ((VOID*)(UINTN)FdtBlobBase) == 0); 804 805 // 806 // Ensure the Size of the Device Tree is smaller than the size of the read file 807 // 808 ASSERT ((UINTN)fdt_totalsize ((VOID*)(UINTN)FdtBlobBase) <= FdtBlobSize); 809 810 // 811 // Store the FDT as Runtime Service Data to prevent the Kernel from 812 // overwritting its data. 813 // 814 NumPages = EFI_SIZE_TO_PAGES (FdtBlobSize); 815 Status = gBS->AllocatePages ( 816 AllocateAnyPages, EfiRuntimeServicesData, 817 NumPages, &FdtConfigurationTableBase 818 ); 819 if (EFI_ERROR (Status)) { 820 goto EXIT_LOAD_FDT; 821 } 822 gBS->CopyMem ( 823 (VOID*)(UINTN)FdtConfigurationTableBase, 824 (VOID*)(UINTN)FdtBlobBase, 825 FdtBlobSize 826 ); 827 828 // 829 // Install the FDT into the Configuration Table 830 // 831 Status = gBS->InstallConfigurationTable ( 832 &gFdtTableGuid, 833 (VOID*)(UINTN)FdtConfigurationTableBase 834 ); 835 if (EFI_ERROR (Status)) { 836 gBS->FreePages (FdtConfigurationTableBase, NumPages); 837 } 838 839 EXIT_LOAD_FDT: 840 if (EFI_ERROR (Status)) { 841 Print (L"\nWarning: Did not manage to install the new device tree. Try to restart the platform.\n"); 842 } 843 844 if (FdtBlobBase != 0) { 845 gBS->FreePages (FdtBlobBase, NumPages); 846 } 847 848 EXIT: 849 if (Status == EFI_ABORTED) { 850 Print (L"\n"); 851 } 852 853 if (SupportedBootDevice != NULL) { 854 FreePool (SupportedBootDevice); 855 } 856 857 return Status; 858 } 859 860 /** 861 Set boot timeout 862 863 Ask for the boot timeout in seconds and if the input succeeds assign the 864 input value to the UEFI global variable "Timeout". This function is called 865 when the user selects the "Set Boot Timeout" of the boot manager menu. 866 867 @param[in] BootOptionsList List of the boot devices, not used here 868 869 @retval EFI_SUCCESS Boot timeout in second retrieved from the standard 870 input and assigned to the UEFI "Timeout" global 871 variable 872 @retval !EFI_SUCCESS Either the input or the setting of the UEFI global 873 variable "Timeout" has failed. 874 **/ 875 EFI_STATUS 876 STATIC 877 BootMenuSetBootTimeout ( 878 IN LIST_ENTRY *BootOptionsList 879 ) 880 { 881 EFI_STATUS Status; 882 UINTN Input; 883 UINT16 Timeout; 884 885 Print (L"Timeout duration (in seconds): "); 886 Status = GetHIInputInteger (&Input); 887 if (EFI_ERROR (Status)) { 888 Print (L"\n"); 889 goto ErrorExit; 890 } 891 892 Timeout = Input; 893 Status = gRT->SetVariable ( 894 (CHAR16*)L"Timeout", 895 &gEfiGlobalVariableGuid, 896 EFI_VARIABLE_NON_VOLATILE | 897 EFI_VARIABLE_BOOTSERVICE_ACCESS | 898 EFI_VARIABLE_RUNTIME_ACCESS, 899 sizeof (UINT16), 900 &Timeout 901 ); 902 ASSERT_EFI_ERROR (Status); 903 904 ErrorExit: 905 return Status; 906 } 907 908 struct BOOT_MANAGER_ENTRY { 909 CONST CHAR16* Description; 910 EFI_STATUS (*Callback) (IN LIST_ENTRY *BootOptionsList); 911 } BootManagerEntries[] = { 912 { L"Add Boot Device Entry", BootMenuAddBootOption }, 913 { L"Update Boot Device Entry", BootMenuUpdateBootOption }, 914 { L"Remove Boot Device Entry", BootMenuRemoveBootOption }, 915 { L"Reorder Boot Device Entries", BootMenuReorderBootOptions }, 916 { L"Update FDT path", UpdateFdtPath }, 917 { L"Set Boot Timeout", BootMenuSetBootTimeout }, 918 }; 919 920 EFI_STATUS 921 BootMenuManager ( 922 IN LIST_ENTRY *BootOptionsList 923 ) 924 { 925 UINTN Index; 926 UINTN OptionSelected; 927 UINTN BootManagerEntryCount; 928 EFI_STATUS Status; 929 930 BootManagerEntryCount = sizeof(BootManagerEntries) / sizeof(struct BOOT_MANAGER_ENTRY); 931 932 while (TRUE) { 933 // Display Boot Manager menu 934 for (Index = 0; Index < BootManagerEntryCount; Index++) { 935 Print(L"[%d] %s\n",Index+1,BootManagerEntries[Index]); 936 } 937 Print(L"[%d] Return to main menu\n",Index+1); 938 939 // Select which entry to call 940 Print(L"Choice: "); 941 Status = GetHIInputInteger (&OptionSelected); 942 if (EFI_ERROR(Status) || (OptionSelected == (BootManagerEntryCount+1))) { 943 if (EFI_ERROR(Status)) { 944 Print(L"\n"); 945 } 946 return EFI_SUCCESS; 947 } else if ((OptionSelected > 0) && (OptionSelected <= BootManagerEntryCount)) { 948 BootManagerEntries[OptionSelected-1].Callback (BootOptionsList); 949 } 950 } 951 // Should never go here 952 } 953 954 EFI_STATUS 955 BootShell ( 956 IN LIST_ENTRY *BootOptionsList 957 ) 958 { 959 EFI_STATUS Status; 960 EFI_DEVICE_PATH* EfiShellDevicePath; 961 962 // Find the EFI Shell 963 Status = LocateEfiApplicationInFvByName (L"Shell", &EfiShellDevicePath); 964 if (Status == EFI_NOT_FOUND) { 965 Print (L"Error: EFI Application not found.\n"); 966 return Status; 967 } else if (EFI_ERROR (Status)) { 968 Print (L"Error: Status Code: 0x%X\n", (UINT32)Status); 969 return Status; 970 } else { 971 // Need to connect every drivers to ensure no dependencies are missing for the application 972 Status = BdsConnectAllDrivers (); 973 if (EFI_ERROR (Status)) { 974 DEBUG ((EFI_D_ERROR, "FAIL to connect all drivers\n")); 975 return Status; 976 } 977 978 return BdsStartEfiApplication (gImageHandle, EfiShellDevicePath, 0, NULL); 979 } 980 } 981 982 struct BOOT_MAIN_ENTRY { 983 CONST CHAR16* Description; 984 EFI_STATUS (*Callback) (IN LIST_ENTRY *BootOptionsList); 985 } BootMainEntries[] = { 986 { L"Shell", BootShell }, 987 { L"Boot Manager", BootMenuManager }, 988 }; 989 990 EFI_STATUS 991 BootMenuMain ( 992 VOID 993 ) 994 { 995 LIST_ENTRY BootOptionsList; 996 UINTN OptionCount; 997 UINTN BootOptionCount; 998 EFI_STATUS Status; 999 LIST_ENTRY* Entry; 1000 BDS_LOAD_OPTION* BootOption; 1001 UINTN BootOptionSelected; 1002 UINTN Index; 1003 UINTN BootMainEntryCount; 1004 BOOLEAN IsUnicode; 1005 1006 BootOption = NULL; 1007 BootMainEntryCount = sizeof(BootMainEntries) / sizeof(struct BOOT_MAIN_ENTRY); 1008 1009 if (FeaturePcdGet (PcdBdsLinuxSupport)) { 1010 // Check Linux Loader is present 1011 Status = LocateEfiApplicationInFvByGuid (&mLinuxLoaderAppGuid, &mLinuxLoaderDevicePath); 1012 ASSERT_EFI_ERROR (Status); 1013 } 1014 1015 while (TRUE) { 1016 // Get Boot#### list 1017 BootOptionList (&BootOptionsList); 1018 1019 OptionCount = 1; 1020 1021 // Display the Boot options 1022 for (Entry = GetFirstNode (&BootOptionsList); 1023 !IsNull (&BootOptionsList,Entry); 1024 Entry = GetNextNode (&BootOptionsList,Entry) 1025 ) 1026 { 1027 BootOption = LOAD_OPTION_FROM_LINK(Entry); 1028 1029 Print(L"[%d] %s\n", OptionCount, BootOption->Description); 1030 1031 DEBUG_CODE_BEGIN(); 1032 CHAR16* DevicePathTxt; 1033 EFI_DEVICE_PATH_TO_TEXT_PROTOCOL* DevicePathToTextProtocol; 1034 1035 Status = gBS->LocateProtocol (&gEfiDevicePathToTextProtocolGuid, NULL, (VOID **)&DevicePathToTextProtocol); 1036 if (EFI_ERROR(Status)) { 1037 // You must provide an implementation of DevicePathToTextProtocol in your firmware (eg: DevicePathDxe) 1038 DEBUG((EFI_D_ERROR,"Error: Bds requires DevicePathToTextProtocol\n")); 1039 return Status; 1040 } 1041 DevicePathTxt = DevicePathToTextProtocol->ConvertDevicePathToText (BootOption->FilePathList, TRUE, TRUE); 1042 1043 Print(L"\t- %s\n",DevicePathTxt); 1044 1045 if (BootOption->OptionalData != NULL) { 1046 if (IsPrintableString (BootOption->OptionalData, &IsUnicode)) { 1047 if (IsUnicode) { 1048 Print (L"\t- Arguments: %s\n", BootOption->OptionalData); 1049 } else { 1050 AsciiPrint ("\t- Arguments: %a\n", BootOption->OptionalData); 1051 } 1052 } 1053 } 1054 FreePool(DevicePathTxt); 1055 DEBUG_CODE_END(); 1056 1057 OptionCount++; 1058 } 1059 BootOptionCount = OptionCount-1; 1060 1061 // Display the hardcoded Boot entries 1062 for (Index = 0; Index < BootMainEntryCount; Index++) { 1063 Print(L"[%d] %s\n",OptionCount,BootMainEntries[Index]); 1064 OptionCount++; 1065 } 1066 1067 // Request the boot entry from the user 1068 BootOptionSelected = 0; 1069 while (BootOptionSelected == 0) { 1070 Print(L"Start: "); 1071 Status = GetHIInputInteger (&BootOptionSelected); 1072 if (EFI_ERROR(Status) || (BootOptionSelected == 0) || (BootOptionSelected > OptionCount)) { 1073 Print(L"Invalid input (max %d)\n",(OptionCount-1)); 1074 BootOptionSelected = 0; 1075 } 1076 } 1077 1078 // Start the selected entry 1079 if (BootOptionSelected > BootOptionCount) { 1080 // Start the hardcoded entry 1081 Status = BootMainEntries[BootOptionSelected - BootOptionCount - 1].Callback (&BootOptionsList); 1082 } else { 1083 // Find the selected entry from the Boot#### list 1084 Index = 1; 1085 for (Entry = GetFirstNode (&BootOptionsList); 1086 !IsNull (&BootOptionsList,Entry); 1087 Entry = GetNextNode (&BootOptionsList,Entry) 1088 ) 1089 { 1090 if (Index == BootOptionSelected) { 1091 BootOption = LOAD_OPTION_FROM_LINK(Entry); 1092 break; 1093 } 1094 Index++; 1095 } 1096 1097 Status = BootOptionStart (BootOption); 1098 } 1099 } 1100 // Should never go here 1101 } 1102