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