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