]> git.proxmox.com Git - mirror_edk2.git/blob - ArmPlatformPkg/Bds/BootMenu.c
3676bf02459471f26a5bc82d4db750f2701dd7c5
[mirror_edk2.git] / ArmPlatformPkg / Bds / BootMenu.c
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 <Guid/ArmGlobalVariableHob.h>
18 #include <Guid/ArmPlatformEvents.h>
19
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 // Free the InitrdPathNodes created by Support->CreateDevicePathNode()
346 FreePool (InitrdPathNodes);
347
348 if (InitrdPath == NULL) {
349 Status = EFI_OUT_OF_RESOURCES;
350 goto EXIT;
351 }
352 } else {
353 InitrdPath = NULL;
354 }
355 } else {
356 InitrdPath = NULL;
357 }
358
359 Print(L"Arguments to pass to the binary: ");
360 Status = GetHIInputAscii (AsciiCmdLine, BOOT_DEVICE_OPTION_MAX);
361 if (EFI_ERROR(Status)) {
362 Status = EFI_ABORTED;
363 goto FREE_DEVICE_PATH;
364 }
365
366 CmdLineSize = AsciiStrSize (AsciiCmdLine);
367 InitrdSize = GetDevicePathSize (InitrdPath);
368
369 OptionalDataSize = sizeof(ARM_BDS_LOADER_ARGUMENTS) + CmdLineSize + InitrdSize;
370 BootArguments = (ARM_BDS_LOADER_ARGUMENTS*)AllocatePool (OptionalDataSize);
371
372 BootArguments->LinuxArguments.CmdLineSize = CmdLineSize;
373 BootArguments->LinuxArguments.InitrdSize = InitrdSize;
374 CopyMem ((VOID*)(&BootArguments->LinuxArguments + 1), AsciiCmdLine, CmdLineSize);
375 CopyMem ((VOID*)((UINTN)(&BootArguments->LinuxArguments + 1) + CmdLineSize), InitrdPath, InitrdSize);
376
377 OptionalData = (UINT8*)BootArguments;
378 } else {
379 Print (L"Arguments to pass to the EFI Application: ");
380 Status = GetHIInputStr (CmdLine, BOOT_DEVICE_OPTION_MAX);
381 if (EFI_ERROR (Status)) {
382 Status = EFI_ABORTED;
383 goto EXIT;
384 }
385
386 OptionalData = (UINT8*)CmdLine;
387 OptionalDataSize = StrSize (CmdLine);
388 }
389
390 Print(L"Description for this new Entry: ");
391 Status = GetHIInputStr (BootDescription, BOOT_DEVICE_DESCRIPTION_MAX);
392 if (EFI_ERROR(Status)) {
393 Status = EFI_ABORTED;
394 goto FREE_DEVICE_PATH;
395 }
396
397 // Create new entry
398 BdsLoadOptionEntry = (BDS_LOAD_OPTION_ENTRY*)AllocatePool (sizeof(BDS_LOAD_OPTION_ENTRY));
399 Status = BootOptionCreate (Attributes, BootDescription, DevicePath, BootType, OptionalData, OptionalDataSize, &BdsLoadOptionEntry->BdsLoadOption);
400 if (!EFI_ERROR(Status)) {
401 InsertTailList (BootOptionsList, &BdsLoadOptionEntry->Link);
402 }
403
404 FREE_DEVICE_PATH:
405 FreePool (DevicePath);
406
407 EXIT:
408 if (Status == EFI_ABORTED) {
409 Print(L"\n");
410 }
411 FreePool(SupportedBootDevice);
412 return Status;
413 }
414
415 EFI_STATUS
416 BootMenuRemoveBootOption (
417 IN LIST_ENTRY *BootOptionsList
418 )
419 {
420 EFI_STATUS Status;
421 BDS_LOAD_OPTION_ENTRY* BootOptionEntry;
422
423 DisplayBootOptions (BootOptionsList);
424 Status = SelectBootOption (BootOptionsList, DELETE_BOOT_ENTRY, &BootOptionEntry);
425 if (EFI_ERROR (Status)) {
426 return Status;
427 }
428
429 // If the Boot Option was attached to a list remove it
430 if (!IsListEmpty (&BootOptionEntry->Link)) {
431 // Remove the entry from the list
432 RemoveEntryList (&BootOptionEntry->Link);
433 }
434
435 // Delete the BDS Load option structures
436 BootOptionDelete (BootOptionEntry->BdsLoadOption);
437
438 return EFI_SUCCESS;
439 }
440
441 EFI_STATUS
442 BootMenuUpdateBootOption (
443 IN LIST_ENTRY *BootOptionsList
444 )
445 {
446 EFI_STATUS Status;
447 BDS_LOAD_OPTION_ENTRY *BootOptionEntry;
448 BDS_LOAD_OPTION *BootOption;
449 BDS_LOAD_OPTION_SUPPORT* DeviceSupport;
450 ARM_BDS_LOADER_ARGUMENTS* BootArguments;
451 CHAR16 BootDescription[BOOT_DEVICE_DESCRIPTION_MAX];
452 CHAR8 CmdLine[BOOT_DEVICE_OPTION_MAX];
453 CHAR16 UnicodeCmdLine[BOOT_DEVICE_OPTION_MAX];
454 EFI_DEVICE_PATH *DevicePath;
455 EFI_DEVICE_PATH *TempInitrdPath;
456 ARM_BDS_LOADER_TYPE BootType;
457 ARM_BDS_LOADER_OPTIONAL_DATA* LoaderOptionalData;
458 ARM_BDS_LINUX_ARGUMENTS* LinuxArguments;
459 EFI_DEVICE_PATH *InitrdPathNodes;
460 EFI_DEVICE_PATH *InitrdPath;
461 UINTN InitrdSize;
462 UINTN CmdLineSize;
463 BOOLEAN InitrdSupport;
464 UINT8* OptionalData;
465 UINTN OptionalDataSize;
466 BOOLEAN IsPrintable;
467 BOOLEAN IsUnicode;
468
469 DisplayBootOptions (BootOptionsList);
470 Status = SelectBootOption (BootOptionsList, UPDATE_BOOT_ENTRY, &BootOptionEntry);
471 if (EFI_ERROR (Status)) {
472 return Status;
473 }
474 BootOption = BootOptionEntry->BdsLoadOption;
475
476 // Get the device support for this Boot Option
477 Status = BootDeviceGetDeviceSupport (BootOption->FilePathList, &DeviceSupport);
478 if (EFI_ERROR(Status)) {
479 Print(L"Not possible to retrieve the supported device for the update\n");
480 return EFI_UNSUPPORTED;
481 }
482
483 Status = DeviceSupport->UpdateDevicePathNode (BootOption->FilePathList, L"EFI Application or the kernel", &DevicePath);
484 if (EFI_ERROR(Status)) {
485 Status = EFI_ABORTED;
486 goto EXIT;
487 }
488
489 if (DeviceSupport->RequestBootType) {
490 Status = BootDeviceGetType (DevicePath, &BootType, &BootOption->Attributes);
491 if (EFI_ERROR(Status)) {
492 Status = EFI_ABORTED;
493 goto EXIT;
494 }
495 }
496
497 LoaderOptionalData = BootOption->OptionalData;
498 if (LoaderOptionalData != NULL) {
499 BootType = (ARM_BDS_LOADER_TYPE)ReadUnaligned32 ((UINT32 *)(&LoaderOptionalData->Header.LoaderType));
500 } else {
501 BootType = BDS_LOADER_EFI_APPLICATION;
502 }
503
504 if ((BootType == BDS_LOADER_KERNEL_LINUX_ATAG) || (BootType == BDS_LOADER_KERNEL_LINUX_FDT)) {
505 LinuxArguments = &LoaderOptionalData->Arguments.LinuxArguments;
506
507 CmdLineSize = ReadUnaligned16 ((CONST UINT16*)&LinuxArguments->CmdLineSize);
508
509 InitrdSize = ReadUnaligned16 ((CONST UINT16*)&LinuxArguments->InitrdSize);
510 if (InitrdSize > 0) {
511 Print(L"Keep the initrd: ");
512 } else {
513 Print(L"Add an initrd: ");
514 }
515 Status = GetHIInputBoolean (&InitrdSupport);
516 if (EFI_ERROR(Status)) {
517 Status = EFI_ABORTED;
518 goto EXIT;
519 }
520
521 if (InitrdSupport) {
522 if (InitrdSize > 0) {
523 // Case we update the initrd device path
524 Status = DeviceSupport->UpdateDevicePathNode ((EFI_DEVICE_PATH*)((UINTN)(LinuxArguments + 1) + CmdLineSize), L"initrd", &InitrdPath);
525 if (EFI_ERROR(Status) && Status != EFI_NOT_FOUND) {// EFI_NOT_FOUND is returned on empty input string, but we can boot without an initrd
526 Status = EFI_ABORTED;
527 goto EXIT;
528 }
529 InitrdSize = GetDevicePathSize (InitrdPath);
530 } else {
531 // Case we create the initrd device path
532
533 Status = DeviceSupport->CreateDevicePathNode (L"initrd", &InitrdPathNodes);
534 if (EFI_ERROR(Status) && Status != EFI_NOT_FOUND) { // EFI_NOT_FOUND is returned on empty input string, but we can boot without an initrd
535 Status = EFI_ABORTED;
536 goto EXIT;
537 }
538
539 if (InitrdPathNodes != NULL) {
540 // Duplicate Linux kernel Device Path
541 TempInitrdPath = DuplicateDevicePath (BootOption->FilePathList);
542 // Replace Linux kernel Node by EndNode
543 SetDevicePathEndNode (GetLastDevicePathNode (TempInitrdPath));
544 // Append the Device Path to the selected device path
545 InitrdPath = AppendDevicePath (TempInitrdPath, (CONST EFI_DEVICE_PATH_PROTOCOL *)InitrdPathNodes);
546 FreePool (TempInitrdPath);
547 // Free the InitrdPathNodes created by Support->CreateDevicePathNode()
548 FreePool (InitrdPathNodes);
549 if (InitrdPath == NULL) {
550 Status = EFI_OUT_OF_RESOURCES;
551 goto EXIT;
552 }
553 InitrdSize = GetDevicePathSize (InitrdPath);
554 } else {
555 InitrdPath = NULL;
556 }
557 }
558 } else {
559 InitrdSize = 0;
560 }
561
562 Print(L"Arguments to pass to the binary: ");
563 if (CmdLineSize > 0) {
564 AsciiStrnCpy (CmdLine, (CONST CHAR8*)(LinuxArguments + 1), sizeof (CmdLine));
565 CmdLine[sizeof (CmdLine) - 1] = '\0';
566 } else {
567 CmdLine[0] = '\0';
568 }
569 Status = EditHIInputAscii (CmdLine, BOOT_DEVICE_OPTION_MAX);
570 if (EFI_ERROR(Status)) {
571 Status = EFI_ABORTED;
572 goto FREE_DEVICE_PATH;
573 }
574
575 CmdLineSize = AsciiStrSize (CmdLine);
576
577 OptionalDataSize = sizeof(ARM_BDS_LOADER_ARGUMENTS) + CmdLineSize + InitrdSize;
578 BootArguments = (ARM_BDS_LOADER_ARGUMENTS*)AllocatePool (OptionalDataSize);
579 BootArguments->LinuxArguments.CmdLineSize = CmdLineSize;
580 BootArguments->LinuxArguments.InitrdSize = InitrdSize;
581 CopyMem (&BootArguments->LinuxArguments + 1, CmdLine, CmdLineSize);
582 CopyMem ((VOID*)((UINTN)(&BootArguments->LinuxArguments + 1) + CmdLineSize), InitrdPath, InitrdSize);
583
584 OptionalData = (UINT8*)BootArguments;
585 } else {
586 Print (L"Arguments to pass to the EFI Application: ");
587
588 if (BootOption->OptionalDataSize > 0) {
589 IsPrintable = IsPrintableString (BootOption->OptionalData, &IsUnicode);
590 if (IsPrintable) {
591 //
592 // The size in bytes of the string, final zero included, should
593 // be equal to or at least lower than "BootOption->OptionalDataSize"
594 // and the "IsPrintableString()" has already tested that the length
595 // in number of characters is smaller than BOOT_DEVICE_OPTION_MAX,
596 // final '\0' included. We can thus copy the string for editing
597 // using "CopyMem()". Furthermore, note that in the case of an Unicode
598 // string "StrnCpy()" and "StrCpy()" can not be used to copy the
599 // string because the data pointed to by "BootOption->OptionalData"
600 // is not necessarily 2-byte aligned.
601 //
602 if (IsUnicode) {
603 CopyMem (
604 UnicodeCmdLine, BootOption->OptionalData,
605 MIN (sizeof (UnicodeCmdLine),
606 BootOption->OptionalDataSize)
607 );
608 } else {
609 CopyMem (
610 CmdLine, BootOption->OptionalData,
611 MIN (sizeof (CmdLine),
612 BootOption->OptionalDataSize)
613 );
614 }
615 }
616 } else {
617 UnicodeCmdLine[0] = L'\0';
618 IsPrintable = TRUE;
619 IsUnicode = TRUE;
620 }
621
622 // We do not request arguments for OptionalData that cannot be printed
623 if (IsPrintable) {
624 if (IsUnicode) {
625 Status = EditHIInputStr (UnicodeCmdLine, BOOT_DEVICE_OPTION_MAX);
626 if (EFI_ERROR (Status)) {
627 Status = EFI_ABORTED;
628 goto FREE_DEVICE_PATH;
629 }
630
631 OptionalData = (UINT8*)UnicodeCmdLine;
632 OptionalDataSize = StrSize (UnicodeCmdLine);
633 } else {
634 Status = EditHIInputAscii (CmdLine, BOOT_DEVICE_OPTION_MAX);
635 if (EFI_ERROR (Status)) {
636 Status = EFI_ABORTED;
637 goto FREE_DEVICE_PATH;
638 }
639
640 OptionalData = (UINT8*)CmdLine;
641 OptionalDataSize = AsciiStrSize (CmdLine);
642 }
643 } else {
644 // We keep the former OptionalData
645 OptionalData = BootOption->OptionalData;
646 OptionalDataSize = BootOption->OptionalDataSize;
647 }
648 }
649
650 Print(L"Description for this new Entry: ");
651 StrnCpy (BootDescription, BootOption->Description, BOOT_DEVICE_DESCRIPTION_MAX);
652 Status = EditHIInputStr (BootDescription, BOOT_DEVICE_DESCRIPTION_MAX);
653 if (EFI_ERROR(Status)) {
654 Status = EFI_ABORTED;
655 goto FREE_DEVICE_PATH;
656 }
657
658 // Update the entry
659 Status = BootOptionUpdate (BootOption, BootOption->Attributes, BootDescription, DevicePath, BootType, OptionalData, OptionalDataSize);
660
661 FREE_DEVICE_PATH:
662 FreePool (DevicePath);
663
664 EXIT:
665 if (Status == EFI_ABORTED) {
666 Print(L"\n");
667 }
668 return Status;
669 }
670
671 /**
672 Reorder boot options
673
674 Ask for the boot option to move and then move it when up or down arrows
675 are pressed. This function is called when the user selects the "Reorder Boot
676 Device Entries" entry in the boot manager menu.
677 The order of the boot options in BootOptionList and in the UEFI BootOrder
678 global variable are kept coherent until the user confirm his reordering (ie:
679 he does not exit by pressing escape).
680
681 @param[in] BootOptionsList List of the boot devices constructed in
682 BootMenuMain()
683
684 @retval EFI_SUCCESS No error encountered.
685 @retval !EFI_SUCCESS An error has occured either in the selection of the
686 boot option to move or while interacting with the user.
687
688 **/
689 STATIC
690 EFI_STATUS
691 BootMenuReorderBootOptions (
692 IN LIST_ENTRY *BootOptionsList
693 )
694 {
695 EFI_STATUS Status;
696 BDS_LOAD_OPTION_ENTRY *BootOptionEntry;
697 LIST_ENTRY *SelectedEntry;
698 LIST_ENTRY *PrevEntry;
699 BOOLEAN Move;
700 BOOLEAN Save;
701 BOOLEAN Cancel;
702 UINTN WaitIndex;
703 EFI_INPUT_KEY Key;
704 LIST_ENTRY *SecondEntry;
705 UINTN BootOrderSize;
706 UINT16 *BootOrder;
707 LIST_ENTRY *Entry;
708 UINTN Index;
709
710 DisplayBootOptions (BootOptionsList);
711
712 // Ask to select the boot option to move
713 while (TRUE) {
714 Status = SelectBootOption (BootOptionsList, MOVE_BOOT_ENTRY, &BootOptionEntry);
715 if (EFI_ERROR (Status)) {
716 goto ErrorExit;
717 }
718
719 SelectedEntry = &BootOptionEntry->Link;
720 SecondEntry = NULL;
721 // Note down the previous entry in the list to be able to cancel changes
722 PrevEntry = GetPreviousNode (BootOptionsList, SelectedEntry);
723
724 // Start of interaction
725 while (TRUE) {
726 Print (
727 L"* Use up/down arrows to move the entry '%s'",
728 BootOptionEntry->BdsLoadOption->Description
729 );
730
731 // Wait for a move, save or cancel request
732 Move = FALSE;
733 Save = FALSE;
734 Cancel = FALSE;
735 do {
736 Status = gBS->WaitForEvent (1, &gST->ConIn->WaitForKey, &WaitIndex);
737 if (!EFI_ERROR (Status)) {
738 Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
739 }
740 if (EFI_ERROR (Status)) {
741 Print (L"\n");
742 goto ErrorExit;
743 }
744
745 switch (Key.ScanCode) {
746 case SCAN_NULL:
747 Save = (Key.UnicodeChar == CHAR_LINEFEED) ||
748 (Key.UnicodeChar == CHAR_CARRIAGE_RETURN) ||
749 (Key.UnicodeChar == 0x7f);
750 break;
751
752 case SCAN_UP:
753 SecondEntry = GetPreviousNode (BootOptionsList, SelectedEntry);
754 Move = SecondEntry != BootOptionsList;
755 break;
756
757 case SCAN_DOWN:
758 SecondEntry = GetNextNode (BootOptionsList, SelectedEntry);
759 Move = SecondEntry != BootOptionsList;
760 break;
761
762 case SCAN_ESC:
763 Cancel = TRUE;
764 break;
765 }
766 } while ((!Move) && (!Save) && (!Cancel));
767
768 if (Move) {
769 if ((SelectedEntry != NULL) && (SecondEntry != NULL)) {
770 SwapListEntries (SelectedEntry, SecondEntry);
771 }
772 } else {
773 if (Save) {
774 Status = GetGlobalEnvironmentVariable (
775 L"BootOrder", NULL, &BootOrderSize, (VOID**)&BootOrder
776 );
777 BootOrderSize /= sizeof (UINT16);
778
779 if (!EFI_ERROR (Status)) {
780 // The order of the boot options in the 'BootOptionsList' is the
781 // new order that has been just defined by the user. Save this new
782 // order in "BootOrder" UEFI global variable.
783 Entry = GetFirstNode (BootOptionsList);
784 for (Index = 0; Index < BootOrderSize; Index++) {
785 BootOrder[Index] = (LOAD_OPTION_FROM_LINK (Entry))->LoadOptionIndex;
786 Entry = GetNextNode (BootOptionsList, Entry);
787 }
788 Status = gRT->SetVariable (
789 (CHAR16*)L"BootOrder",
790 &gEfiGlobalVariableGuid,
791 EFI_VARIABLE_NON_VOLATILE |
792 EFI_VARIABLE_BOOTSERVICE_ACCESS |
793 EFI_VARIABLE_RUNTIME_ACCESS,
794 BootOrderSize * sizeof (UINT16),
795 BootOrder
796 );
797 FreePool (BootOrder);
798 }
799
800 if (EFI_ERROR (Status)) {
801 Print (L"\nAn error occurred, move not completed!\n");
802 Cancel = TRUE;
803 }
804 }
805
806 if (Cancel) {
807 //
808 // Restore initial position of the selected boot option
809 //
810 RemoveEntryList (SelectedEntry);
811 InsertHeadList (PrevEntry, SelectedEntry);
812 }
813 }
814
815 Print (L"\n");
816 DisplayBootOptions (BootOptionsList);
817 // Saved or cancelled, back to the choice of boot option to move
818 if (!Move) {
819 break;
820 }
821 }
822 }
823
824 ErrorExit:
825 return Status ;
826 }
827
828 EFI_STATUS
829 UpdateFdtPath (
830 IN LIST_ENTRY *BootOptionsList
831 )
832 {
833 EFI_STATUS Status;
834 UINTN FdtDevicePathSize;
835 BDS_SUPPORTED_DEVICE *SupportedBootDevice;
836 EFI_DEVICE_PATH_PROTOCOL *FdtDevicePathNodes;
837 EFI_DEVICE_PATH_PROTOCOL *FdtDevicePath;
838 EFI_EVENT UpdateFdtEvent;
839
840 Status = SelectBootDevice (&SupportedBootDevice);
841 if (EFI_ERROR(Status)) {
842 Status = EFI_ABORTED;
843 goto EXIT;
844 }
845
846 // Create the specific device path node
847 Status = SupportedBootDevice->Support->CreateDevicePathNode (L"FDT blob", &FdtDevicePathNodes);
848 if (EFI_ERROR(Status)) {
849 Status = EFI_ABORTED;
850 goto EXIT;
851 }
852
853 if (FdtDevicePathNodes != NULL) {
854 // Append the Device Path node to the select device path
855 FdtDevicePath = AppendDevicePath (SupportedBootDevice->DevicePathProtocol, FdtDevicePathNodes);
856 // Free the FdtDevicePathNodes created by Support->CreateDevicePathNode()
857 FreePool (FdtDevicePathNodes);
858 FdtDevicePathSize = GetDevicePathSize (FdtDevicePath);
859 Status = gRT->SetVariable (
860 (CHAR16*)L"Fdt",
861 &gArmGlobalVariableGuid,
862 EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,
863 FdtDevicePathSize,
864 FdtDevicePath
865 );
866 ASSERT_EFI_ERROR(Status);
867 } else {
868 Status = gRT->SetVariable (
869 (CHAR16*)L"Fdt",
870 &gArmGlobalVariableGuid,
871 EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,
872 0,
873 NULL
874 );
875 ASSERT_EFI_ERROR(Status);
876 }
877
878 if (!EFI_ERROR (Status)) {
879 //
880 // Signal FDT has been updated
881 //
882 Status = gBS->CreateEventEx (
883 EVT_NOTIFY_SIGNAL,
884 TPL_NOTIFY,
885 EmptyCallbackFunction,
886 NULL,
887 &gArmPlatformUpdateFdtEventGuid,
888 &UpdateFdtEvent
889 );
890 if (!EFI_ERROR (Status)) {
891 gBS->SignalEvent (UpdateFdtEvent);
892 }
893 }
894
895 EXIT:
896 if (Status == EFI_ABORTED) {
897 Print(L"\n");
898 }
899 FreePool(SupportedBootDevice);
900 return Status;
901 }
902
903 /**
904 Set boot timeout
905
906 Ask for the boot timeout in seconds and if the input succeeds assign the
907 input value to the UEFI global variable "Timeout". This function is called
908 when the user selects the "Set Boot Timeout" of the boot manager menu.
909
910 @param[in] BootOptionsList List of the boot devices, not used here
911
912 @retval EFI_SUCCESS Boot timeout in second retrieved from the standard
913 input and assigned to the UEFI "Timeout" global
914 variable
915 @retval !EFI_SUCCESS Either the input or the setting of the UEFI global
916 variable "Timeout" has failed.
917 **/
918 EFI_STATUS
919 STATIC
920 BootMenuSetBootTimeout (
921 IN LIST_ENTRY *BootOptionsList
922 )
923 {
924 EFI_STATUS Status;
925 UINTN Input;
926 UINT16 Timeout;
927
928 Print (L"Timeout duration (in seconds): ");
929 Status = GetHIInputInteger (&Input);
930 if (EFI_ERROR (Status)) {
931 Print (L"\n");
932 goto ErrorExit;
933 }
934
935 Timeout = Input;
936 Status = gRT->SetVariable (
937 (CHAR16*)L"Timeout",
938 &gEfiGlobalVariableGuid,
939 EFI_VARIABLE_NON_VOLATILE |
940 EFI_VARIABLE_BOOTSERVICE_ACCESS |
941 EFI_VARIABLE_RUNTIME_ACCESS,
942 sizeof (UINT16),
943 &Timeout
944 );
945 ASSERT_EFI_ERROR (Status);
946
947 ErrorExit:
948 return Status;
949 }
950
951 struct BOOT_MANAGER_ENTRY {
952 CONST CHAR16* Description;
953 EFI_STATUS (*Callback) (IN LIST_ENTRY *BootOptionsList);
954 } BootManagerEntries[] = {
955 { L"Add Boot Device Entry", BootMenuAddBootOption },
956 { L"Update Boot Device Entry", BootMenuUpdateBootOption },
957 { L"Remove Boot Device Entry", BootMenuRemoveBootOption },
958 { L"Reorder Boot Device Entries", BootMenuReorderBootOptions },
959 { L"Update FDT path", UpdateFdtPath },
960 { L"Set Boot Timeout", BootMenuSetBootTimeout },
961 };
962
963 EFI_STATUS
964 BootMenuManager (
965 IN LIST_ENTRY *BootOptionsList
966 )
967 {
968 UINTN Index;
969 UINTN OptionSelected;
970 UINTN BootManagerEntryCount;
971 EFI_STATUS Status;
972
973 BootManagerEntryCount = sizeof(BootManagerEntries) / sizeof(struct BOOT_MANAGER_ENTRY);
974
975 while (TRUE) {
976 // Display Boot Manager menu
977 for (Index = 0; Index < BootManagerEntryCount; Index++) {
978 Print(L"[%d] %s\n",Index+1,BootManagerEntries[Index]);
979 }
980 Print(L"[%d] Return to main menu\n",Index+1);
981
982 // Select which entry to call
983 Print(L"Choice: ");
984 Status = GetHIInputInteger (&OptionSelected);
985 if (EFI_ERROR(Status) || (OptionSelected == (BootManagerEntryCount+1))) {
986 if (EFI_ERROR(Status)) {
987 Print(L"\n");
988 }
989 return EFI_SUCCESS;
990 } else if ((OptionSelected > 0) && (OptionSelected <= BootManagerEntryCount)) {
991 BootManagerEntries[OptionSelected-1].Callback (BootOptionsList);
992 }
993 }
994 // Should never go here
995 }
996
997 EFI_STATUS
998 BootShell (
999 IN LIST_ENTRY *BootOptionsList
1000 )
1001 {
1002 EFI_STATUS Status;
1003
1004 // Start EFI Shell
1005 Status = BdsLoadApplication (gImageHandle, L"Shell", 0, NULL);
1006 if (Status == EFI_NOT_FOUND) {
1007 Print (L"Error: EFI Application not found.\n");
1008 } else if (EFI_ERROR(Status)) {
1009 Print (L"Error: Status Code: 0x%X\n",(UINT32)Status);
1010 }
1011
1012 return Status;
1013 }
1014
1015 struct BOOT_MAIN_ENTRY {
1016 CONST CHAR16* Description;
1017 EFI_STATUS (*Callback) (IN LIST_ENTRY *BootOptionsList);
1018 } BootMainEntries[] = {
1019 { L"Shell", BootShell },
1020 { L"Boot Manager", BootMenuManager },
1021 };
1022
1023
1024 EFI_STATUS
1025 BootMenuMain (
1026 VOID
1027 )
1028 {
1029 LIST_ENTRY BootOptionsList;
1030 UINTN OptionCount;
1031 UINTN BootOptionCount;
1032 EFI_STATUS Status;
1033 LIST_ENTRY* Entry;
1034 BDS_LOAD_OPTION* BootOption;
1035 UINTN BootOptionSelected;
1036 UINTN Index;
1037 UINTN BootMainEntryCount;
1038 BOOLEAN IsUnicode;
1039
1040 BootOption = NULL;
1041 BootMainEntryCount = sizeof(BootMainEntries) / sizeof(struct BOOT_MAIN_ENTRY);
1042
1043 while (TRUE) {
1044 // Get Boot#### list
1045 BootOptionList (&BootOptionsList);
1046
1047 OptionCount = 1;
1048
1049 // Display the Boot options
1050 for (Entry = GetFirstNode (&BootOptionsList);
1051 !IsNull (&BootOptionsList,Entry);
1052 Entry = GetNextNode (&BootOptionsList,Entry)
1053 )
1054 {
1055 BootOption = LOAD_OPTION_FROM_LINK(Entry);
1056
1057 Print(L"[%d] %s\n", OptionCount, BootOption->Description);
1058
1059 DEBUG_CODE_BEGIN();
1060 CHAR16* DevicePathTxt;
1061 EFI_DEVICE_PATH_TO_TEXT_PROTOCOL* DevicePathToTextProtocol;
1062 ARM_BDS_LOADER_OPTIONAL_DATA* OptionalData;
1063 UINTN CmdLineSize;
1064 ARM_BDS_LOADER_TYPE LoaderType;
1065
1066 Status = gBS->LocateProtocol (&gEfiDevicePathToTextProtocolGuid, NULL, (VOID **)&DevicePathToTextProtocol);
1067 if (EFI_ERROR(Status)) {
1068 // You must provide an implementation of DevicePathToTextProtocol in your firmware (eg: DevicePathDxe)
1069 DEBUG((EFI_D_ERROR,"Error: Bds requires DevicePathToTextProtocol\n"));
1070 return Status;
1071 }
1072 DevicePathTxt = DevicePathToTextProtocol->ConvertDevicePathToText (BootOption->FilePathList, TRUE, TRUE);
1073
1074 Print(L"\t- %s\n",DevicePathTxt);
1075
1076 // If it is a supported BootEntry then print its details
1077 if (IS_ARM_BDS_BOOTENTRY (BootOption)) {
1078 OptionalData = BootOption->OptionalData;
1079 LoaderType = (ARM_BDS_LOADER_TYPE)ReadUnaligned32 ((CONST UINT32*)&OptionalData->Header.LoaderType);
1080 if ((LoaderType == BDS_LOADER_KERNEL_LINUX_ATAG) || (LoaderType == BDS_LOADER_KERNEL_LINUX_FDT)) {
1081 if (ReadUnaligned16 (&OptionalData->Arguments.LinuxArguments.InitrdSize) > 0) {
1082 CmdLineSize = ReadUnaligned16 (&OptionalData->Arguments.LinuxArguments.CmdLineSize);
1083 DevicePathTxt = DevicePathToTextProtocol->ConvertDevicePathToText (
1084 GetAlignedDevicePath ((EFI_DEVICE_PATH*)((UINTN)(&OptionalData->Arguments.LinuxArguments + 1) + CmdLineSize)), TRUE, TRUE);
1085 Print(L"\t- Initrd: %s\n", DevicePathTxt);
1086 }
1087 if (ReadUnaligned16 (&OptionalData->Arguments.LinuxArguments.CmdLineSize) > 0) {
1088 Print(L"\t- Arguments: %a\n", (&OptionalData->Arguments.LinuxArguments + 1));
1089 }
1090 }
1091
1092 switch (LoaderType) {
1093 case BDS_LOADER_EFI_APPLICATION:
1094 Print(L"\t- LoaderType: EFI Application\n");
1095 break;
1096
1097 case BDS_LOADER_KERNEL_LINUX_ATAG:
1098 Print(L"\t- LoaderType: Linux kernel with ATAG support\n");
1099 break;
1100
1101 case BDS_LOADER_KERNEL_LINUX_FDT:
1102 Print(L"\t- LoaderType: Linux kernel with FDT support\n");
1103 break;
1104
1105 default:
1106 Print(L"\t- LoaderType: Not recognized (%d)\n", LoaderType);
1107 }
1108 } else if (BootOption->OptionalData != NULL) {
1109 if (IsPrintableString (BootOption->OptionalData, &IsUnicode)) {
1110 if (IsUnicode) {
1111 Print (L"\t- Arguments: %s\n", BootOption->OptionalData);
1112 } else {
1113 AsciiPrint ("\t- Arguments: %a\n", BootOption->OptionalData);
1114 }
1115 }
1116 }
1117 FreePool(DevicePathTxt);
1118 DEBUG_CODE_END();
1119
1120 OptionCount++;
1121 }
1122 BootOptionCount = OptionCount-1;
1123
1124 // Display the hardcoded Boot entries
1125 for (Index = 0; Index < BootMainEntryCount; Index++) {
1126 Print(L"[%d] %s\n",OptionCount,BootMainEntries[Index]);
1127 OptionCount++;
1128 }
1129
1130 // Request the boot entry from the user
1131 BootOptionSelected = 0;
1132 while (BootOptionSelected == 0) {
1133 Print(L"Start: ");
1134 Status = GetHIInputInteger (&BootOptionSelected);
1135 if (EFI_ERROR(Status) || (BootOptionSelected == 0) || (BootOptionSelected > OptionCount)) {
1136 Print(L"Invalid input (max %d)\n",(OptionCount-1));
1137 BootOptionSelected = 0;
1138 }
1139 }
1140
1141 // Start the selected entry
1142 if (BootOptionSelected > BootOptionCount) {
1143 // Start the hardcoded entry
1144 Status = BootMainEntries[BootOptionSelected - BootOptionCount - 1].Callback (&BootOptionsList);
1145 } else {
1146 // Find the selected entry from the Boot#### list
1147 Index = 1;
1148 for (Entry = GetFirstNode (&BootOptionsList);
1149 !IsNull (&BootOptionsList,Entry);
1150 Entry = GetNextNode (&BootOptionsList,Entry)
1151 )
1152 {
1153 if (Index == BootOptionSelected) {
1154 BootOption = LOAD_OPTION_FROM_LINK(Entry);
1155 break;
1156 }
1157 Index++;
1158 }
1159
1160 Status = BootOptionStart (BootOption);
1161 }
1162 }
1163 // Should never go here
1164 }