]> git.proxmox.com Git - mirror_edk2.git/blob - ArmPlatformPkg/Bds/BootMenu.c
ArmPlatformPkg/Bds: Correct copy of an unaligned Unicode string
[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), sizeof (CmdLine));
559 CmdLine[sizeof (CmdLine) - 1] = '\0';
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 //
586 // The size in bytes of the string, final zero included, should
587 // be equal to or at least lower than "BootOption->OptionalDataSize"
588 // and the "IsPrintableString()" has already tested that the length
589 // in number of characters is smaller than BOOT_DEVICE_OPTION_MAX,
590 // final '\0' included. We can thus copy the string for editing
591 // using "CopyMem()". Furthermore, note that in the case of an Unicode
592 // string "StrnCpy()" and "StrCpy()" can not be used to copy the
593 // string because the data pointed to by "BootOption->OptionalData"
594 // is not necessarily 2-byte aligned.
595 //
596 if (IsUnicode) {
597 CopyMem (
598 UnicodeCmdLine, BootOption->OptionalData,
599 MIN (sizeof (UnicodeCmdLine),
600 BootOption->OptionalDataSize)
601 );
602 } else {
603 CopyMem (
604 CmdLine, BootOption->OptionalData,
605 MIN (sizeof (CmdLine),
606 BootOption->OptionalDataSize)
607 );
608 }
609 }
610 } else {
611 UnicodeCmdLine[0] = L'\0';
612 IsPrintable = TRUE;
613 IsUnicode = TRUE;
614 }
615
616 // We do not request arguments for OptionalData that cannot be printed
617 if (IsPrintable) {
618 if (IsUnicode) {
619 Status = EditHIInputStr (UnicodeCmdLine, BOOT_DEVICE_OPTION_MAX);
620 if (EFI_ERROR (Status)) {
621 Status = EFI_ABORTED;
622 goto FREE_DEVICE_PATH;
623 }
624
625 OptionalData = (UINT8*)UnicodeCmdLine;
626 OptionalDataSize = StrSize (UnicodeCmdLine);
627 } else {
628 Status = EditHIInputAscii (CmdLine, BOOT_DEVICE_OPTION_MAX);
629 if (EFI_ERROR (Status)) {
630 Status = EFI_ABORTED;
631 goto FREE_DEVICE_PATH;
632 }
633
634 OptionalData = (UINT8*)CmdLine;
635 OptionalDataSize = AsciiStrSize (CmdLine);
636 }
637 } else {
638 // We keep the former OptionalData
639 OptionalData = BootOption->OptionalData;
640 OptionalDataSize = BootOption->OptionalDataSize;
641 }
642 }
643
644 Print(L"Description for this new Entry: ");
645 StrnCpy (BootDescription, BootOption->Description, BOOT_DEVICE_DESCRIPTION_MAX);
646 Status = EditHIInputStr (BootDescription, BOOT_DEVICE_DESCRIPTION_MAX);
647 if (EFI_ERROR(Status)) {
648 Status = EFI_ABORTED;
649 goto FREE_DEVICE_PATH;
650 }
651
652 // Update the entry
653 Status = BootOptionUpdate (BootOption, BootOption->Attributes, BootDescription, DevicePath, BootType, OptionalData, OptionalDataSize);
654
655 FREE_DEVICE_PATH:
656 FreePool (DevicePath);
657
658 EXIT:
659 if (Status == EFI_ABORTED) {
660 Print(L"\n");
661 }
662 return Status;
663 }
664
665 /**
666 Reorder boot options
667
668 Ask for the boot option to move and then move it when up or down arrows
669 are pressed. This function is called when the user selects the "Reorder Boot
670 Device Entries" entry in the boot manager menu.
671 The order of the boot options in BootOptionList and in the UEFI BootOrder
672 global variable are kept coherent until the user confirm his reordering (ie:
673 he does not exit by pressing escape).
674
675 @param[in] BootOptionsList List of the boot devices constructed in
676 BootMenuMain()
677
678 @retval EFI_SUCCESS No error encountered.
679 @retval !EFI_SUCCESS An error has occured either in the selection of the
680 boot option to move or while interacting with the user.
681
682 **/
683 STATIC
684 EFI_STATUS
685 BootMenuReorderBootOptions (
686 IN LIST_ENTRY *BootOptionsList
687 )
688 {
689 EFI_STATUS Status;
690 BDS_LOAD_OPTION_ENTRY *BootOptionEntry;
691 LIST_ENTRY *SelectedEntry;
692 LIST_ENTRY *PrevEntry;
693 BOOLEAN Move;
694 BOOLEAN Save;
695 BOOLEAN Cancel;
696 UINTN WaitIndex;
697 EFI_INPUT_KEY Key;
698 LIST_ENTRY *SecondEntry;
699 UINTN BootOrderSize;
700 UINT16 *BootOrder;
701 LIST_ENTRY *Entry;
702 UINTN Index;
703
704 DisplayBootOptions (BootOptionsList);
705
706 // Ask to select the boot option to move
707 while (TRUE) {
708 Status = SelectBootOption (BootOptionsList, MOVE_BOOT_ENTRY, &BootOptionEntry);
709 if (EFI_ERROR (Status)) {
710 goto ErrorExit;
711 }
712
713 SelectedEntry = &BootOptionEntry->Link;
714 SecondEntry = NULL;
715 // Note down the previous entry in the list to be able to cancel changes
716 PrevEntry = GetPreviousNode (BootOptionsList, SelectedEntry);
717
718 // Start of interaction
719 while (TRUE) {
720 Print (
721 L"* Use up/down arrows to move the entry '%s'",
722 BootOptionEntry->BdsLoadOption->Description
723 );
724
725 // Wait for a move, save or cancel request
726 Move = FALSE;
727 Save = FALSE;
728 Cancel = FALSE;
729 do {
730 Status = gBS->WaitForEvent (1, &gST->ConIn->WaitForKey, &WaitIndex);
731 if (!EFI_ERROR (Status)) {
732 Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
733 }
734 if (EFI_ERROR (Status)) {
735 Print (L"\n");
736 goto ErrorExit;
737 }
738
739 switch (Key.ScanCode) {
740 case SCAN_NULL:
741 Save = (Key.UnicodeChar == CHAR_LINEFEED) ||
742 (Key.UnicodeChar == CHAR_CARRIAGE_RETURN) ||
743 (Key.UnicodeChar == 0x7f);
744 break;
745
746 case SCAN_UP:
747 SecondEntry = GetPreviousNode (BootOptionsList, SelectedEntry);
748 Move = SecondEntry != BootOptionsList;
749 break;
750
751 case SCAN_DOWN:
752 SecondEntry = GetNextNode (BootOptionsList, SelectedEntry);
753 Move = SecondEntry != BootOptionsList;
754 break;
755
756 case SCAN_ESC:
757 Cancel = TRUE;
758 break;
759 }
760 } while ((!Move) && (!Save) && (!Cancel));
761
762 if (Move) {
763 if ((SelectedEntry != NULL) && (SecondEntry != NULL)) {
764 SwapListEntries (SelectedEntry, SecondEntry);
765 }
766 } else {
767 if (Save) {
768 Status = GetGlobalEnvironmentVariable (
769 L"BootOrder", NULL, &BootOrderSize, (VOID**)&BootOrder
770 );
771 BootOrderSize /= sizeof (UINT16);
772
773 if (!EFI_ERROR (Status)) {
774 // The order of the boot options in the 'BootOptionsList' is the
775 // new order that has been just defined by the user. Save this new
776 // order in "BootOrder" UEFI global variable.
777 Entry = GetFirstNode (BootOptionsList);
778 for (Index = 0; Index < BootOrderSize; Index++) {
779 BootOrder[Index] = (LOAD_OPTION_FROM_LINK (Entry))->LoadOptionIndex;
780 Entry = GetNextNode (BootOptionsList, Entry);
781 }
782 Status = gRT->SetVariable (
783 (CHAR16*)L"BootOrder",
784 &gEfiGlobalVariableGuid,
785 EFI_VARIABLE_NON_VOLATILE |
786 EFI_VARIABLE_BOOTSERVICE_ACCESS |
787 EFI_VARIABLE_RUNTIME_ACCESS,
788 BootOrderSize * sizeof (UINT16),
789 BootOrder
790 );
791 FreePool (BootOrder);
792 }
793
794 if (EFI_ERROR (Status)) {
795 Print (L"\nAn error occurred, move not completed!\n");
796 Cancel = TRUE;
797 }
798 }
799
800 if (Cancel) {
801 //
802 // Restore initial position of the selected boot option
803 //
804 RemoveEntryList (SelectedEntry);
805 InsertHeadList (PrevEntry, SelectedEntry);
806 }
807 }
808
809 Print (L"\n");
810 DisplayBootOptions (BootOptionsList);
811 // Saved or cancelled, back to the choice of boot option to move
812 if (!Move) {
813 break;
814 }
815 }
816 }
817
818 ErrorExit:
819 return Status ;
820 }
821
822 EFI_STATUS
823 UpdateFdtPath (
824 IN LIST_ENTRY *BootOptionsList
825 )
826 {
827 EFI_STATUS Status;
828 UINTN FdtDevicePathSize;
829 BDS_SUPPORTED_DEVICE *SupportedBootDevice;
830 EFI_DEVICE_PATH_PROTOCOL *FdtDevicePathNodes;
831 EFI_DEVICE_PATH_PROTOCOL *FdtDevicePath;
832
833 Status = SelectBootDevice (&SupportedBootDevice);
834 if (EFI_ERROR(Status)) {
835 Status = EFI_ABORTED;
836 goto EXIT;
837 }
838
839 // Create the specific device path node
840 Status = SupportedBootDevice->Support->CreateDevicePathNode (L"FDT blob", &FdtDevicePathNodes);
841 if (EFI_ERROR(Status)) {
842 Status = EFI_ABORTED;
843 goto EXIT;
844 }
845
846 if (FdtDevicePathNodes != NULL) {
847 // Append the Device Path node to the select device path
848 FdtDevicePath = AppendDevicePath (SupportedBootDevice->DevicePathProtocol, FdtDevicePathNodes);
849 FdtDevicePathSize = GetDevicePathSize (FdtDevicePath);
850 Status = gRT->SetVariable (
851 (CHAR16*)L"Fdt",
852 &gArmGlobalVariableGuid,
853 EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,
854 FdtDevicePathSize,
855 FdtDevicePath
856 );
857 ASSERT_EFI_ERROR(Status);
858 } else {
859 gRT->SetVariable (
860 (CHAR16*)L"Fdt",
861 &gArmGlobalVariableGuid,
862 EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,
863 0,
864 NULL
865 );
866 ASSERT_EFI_ERROR(Status);
867 }
868
869 EXIT:
870 if (Status == EFI_ABORTED) {
871 Print(L"\n");
872 }
873 FreePool(SupportedBootDevice);
874 return Status;
875 }
876
877 /**
878 Set boot timeout
879
880 Ask for the boot timeout in seconds and if the input succeeds assign the
881 input value to the UEFI global variable "Timeout". This function is called
882 when the user selects the "Set Boot Timeout" of the boot manager menu.
883
884 @param[in] BootOptionsList List of the boot devices, not used here
885
886 @retval EFI_SUCCESS Boot timeout in second retrieved from the standard
887 input and assigned to the UEFI "Timeout" global
888 variable
889 @retval !EFI_SUCCESS Either the input or the setting of the UEFI global
890 variable "Timeout" has failed.
891 **/
892 EFI_STATUS
893 STATIC
894 BootMenuSetBootTimeout (
895 IN LIST_ENTRY *BootOptionsList
896 )
897 {
898 EFI_STATUS Status;
899 UINTN Input;
900 UINT16 Timeout;
901
902 Print (L"Timeout duration (in seconds): ");
903 Status = GetHIInputInteger (&Input);
904 if (EFI_ERROR (Status)) {
905 Print (L"\n");
906 goto ErrorExit;
907 }
908
909 Timeout = Input;
910 Status = gRT->SetVariable (
911 (CHAR16*)L"Timeout",
912 &gEfiGlobalVariableGuid,
913 EFI_VARIABLE_NON_VOLATILE |
914 EFI_VARIABLE_BOOTSERVICE_ACCESS |
915 EFI_VARIABLE_RUNTIME_ACCESS,
916 sizeof (UINT16),
917 &Timeout
918 );
919 ASSERT_EFI_ERROR (Status);
920
921 ErrorExit:
922 return Status;
923 }
924
925 struct BOOT_MANAGER_ENTRY {
926 CONST CHAR16* Description;
927 EFI_STATUS (*Callback) (IN LIST_ENTRY *BootOptionsList);
928 } BootManagerEntries[] = {
929 { L"Add Boot Device Entry", BootMenuAddBootOption },
930 { L"Update Boot Device Entry", BootMenuUpdateBootOption },
931 { L"Remove Boot Device Entry", BootMenuRemoveBootOption },
932 { L"Reorder Boot Device Entries", BootMenuReorderBootOptions },
933 { L"Update FDT path", UpdateFdtPath },
934 { L"Set Boot Timeout", BootMenuSetBootTimeout },
935 };
936
937 EFI_STATUS
938 BootMenuManager (
939 IN LIST_ENTRY *BootOptionsList
940 )
941 {
942 UINTN Index;
943 UINTN OptionSelected;
944 UINTN BootManagerEntryCount;
945 EFI_STATUS Status;
946
947 BootManagerEntryCount = sizeof(BootManagerEntries) / sizeof(struct BOOT_MANAGER_ENTRY);
948
949 while (TRUE) {
950 // Display Boot Manager menu
951 for (Index = 0; Index < BootManagerEntryCount; Index++) {
952 Print(L"[%d] %s\n",Index+1,BootManagerEntries[Index]);
953 }
954 Print(L"[%d] Return to main menu\n",Index+1);
955
956 // Select which entry to call
957 Print(L"Choice: ");
958 Status = GetHIInputInteger (&OptionSelected);
959 if (EFI_ERROR(Status) || (OptionSelected == (BootManagerEntryCount+1))) {
960 if (EFI_ERROR(Status)) {
961 Print(L"\n");
962 }
963 return EFI_SUCCESS;
964 } else if ((OptionSelected > 0) && (OptionSelected <= BootManagerEntryCount)) {
965 BootManagerEntries[OptionSelected-1].Callback (BootOptionsList);
966 }
967 }
968 // Should never go here
969 }
970
971 EFI_STATUS
972 BootShell (
973 IN LIST_ENTRY *BootOptionsList
974 )
975 {
976 EFI_STATUS Status;
977
978 // Start EFI Shell
979 Status = BdsLoadApplication (gImageHandle, L"Shell", 0, NULL);
980 if (Status == EFI_NOT_FOUND) {
981 Print (L"Error: EFI Application not found.\n");
982 } else if (EFI_ERROR(Status)) {
983 Print (L"Error: Status Code: 0x%X\n",(UINT32)Status);
984 }
985
986 return Status;
987 }
988
989 struct BOOT_MAIN_ENTRY {
990 CONST CHAR16* Description;
991 EFI_STATUS (*Callback) (IN LIST_ENTRY *BootOptionsList);
992 } BootMainEntries[] = {
993 { L"Shell", BootShell },
994 { L"Boot Manager", BootMenuManager },
995 };
996
997
998 EFI_STATUS
999 BootMenuMain (
1000 VOID
1001 )
1002 {
1003 LIST_ENTRY BootOptionsList;
1004 UINTN OptionCount;
1005 UINTN BootOptionCount;
1006 EFI_STATUS Status;
1007 LIST_ENTRY* Entry;
1008 BDS_LOAD_OPTION* BootOption;
1009 UINTN BootOptionSelected;
1010 UINTN Index;
1011 UINTN BootMainEntryCount;
1012 BOOLEAN IsUnicode;
1013
1014 BootOption = NULL;
1015 BootMainEntryCount = sizeof(BootMainEntries) / sizeof(struct BOOT_MAIN_ENTRY);
1016
1017 while (TRUE) {
1018 // Get Boot#### list
1019 BootOptionList (&BootOptionsList);
1020
1021 OptionCount = 1;
1022
1023 // Display the Boot options
1024 for (Entry = GetFirstNode (&BootOptionsList);
1025 !IsNull (&BootOptionsList,Entry);
1026 Entry = GetNextNode (&BootOptionsList,Entry)
1027 )
1028 {
1029 BootOption = LOAD_OPTION_FROM_LINK(Entry);
1030
1031 Print(L"[%d] %s\n", OptionCount, BootOption->Description);
1032
1033 DEBUG_CODE_BEGIN();
1034 CHAR16* DevicePathTxt;
1035 EFI_DEVICE_PATH_TO_TEXT_PROTOCOL* DevicePathToTextProtocol;
1036 ARM_BDS_LOADER_OPTIONAL_DATA* OptionalData;
1037 UINTN CmdLineSize;
1038 ARM_BDS_LOADER_TYPE LoaderType;
1039
1040 Status = gBS->LocateProtocol (&gEfiDevicePathToTextProtocolGuid, NULL, (VOID **)&DevicePathToTextProtocol);
1041 if (EFI_ERROR(Status)) {
1042 // You must provide an implementation of DevicePathToTextProtocol in your firmware (eg: DevicePathDxe)
1043 DEBUG((EFI_D_ERROR,"Error: Bds requires DevicePathToTextProtocol\n"));
1044 return Status;
1045 }
1046 DevicePathTxt = DevicePathToTextProtocol->ConvertDevicePathToText (BootOption->FilePathList, TRUE, TRUE);
1047
1048 Print(L"\t- %s\n",DevicePathTxt);
1049
1050 // If it is a supported BootEntry then print its details
1051 if (IS_ARM_BDS_BOOTENTRY (BootOption)) {
1052 OptionalData = BootOption->OptionalData;
1053 LoaderType = (ARM_BDS_LOADER_TYPE)ReadUnaligned32 ((CONST UINT32*)&OptionalData->Header.LoaderType);
1054 if ((LoaderType == BDS_LOADER_KERNEL_LINUX_ATAG) || (LoaderType == BDS_LOADER_KERNEL_LINUX_FDT)) {
1055 if (ReadUnaligned16 (&OptionalData->Arguments.LinuxArguments.InitrdSize) > 0) {
1056 CmdLineSize = ReadUnaligned16 (&OptionalData->Arguments.LinuxArguments.CmdLineSize);
1057 DevicePathTxt = DevicePathToTextProtocol->ConvertDevicePathToText (
1058 GetAlignedDevicePath ((EFI_DEVICE_PATH*)((UINTN)(&OptionalData->Arguments.LinuxArguments + 1) + CmdLineSize)), TRUE, TRUE);
1059 Print(L"\t- Initrd: %s\n", DevicePathTxt);
1060 }
1061 if (ReadUnaligned16 (&OptionalData->Arguments.LinuxArguments.CmdLineSize) > 0) {
1062 Print(L"\t- Arguments: %a\n", (&OptionalData->Arguments.LinuxArguments + 1));
1063 }
1064 }
1065
1066 switch (LoaderType) {
1067 case BDS_LOADER_EFI_APPLICATION:
1068 Print(L"\t- LoaderType: EFI Application\n");
1069 break;
1070
1071 case BDS_LOADER_KERNEL_LINUX_ATAG:
1072 Print(L"\t- LoaderType: Linux kernel with ATAG support\n");
1073 break;
1074
1075 case BDS_LOADER_KERNEL_LINUX_FDT:
1076 Print(L"\t- LoaderType: Linux kernel with FDT support\n");
1077 break;
1078
1079 default:
1080 Print(L"\t- LoaderType: Not recognized (%d)\n", LoaderType);
1081 }
1082 } else if (BootOption->OptionalData != NULL) {
1083 if (IsPrintableString (BootOption->OptionalData, &IsUnicode)) {
1084 if (IsUnicode) {
1085 Print (L"\t- Arguments: %s\n", BootOption->OptionalData);
1086 } else {
1087 AsciiPrint ("\t- Arguments: %a\n", BootOption->OptionalData);
1088 }
1089 }
1090 }
1091 FreePool(DevicePathTxt);
1092 DEBUG_CODE_END();
1093
1094 OptionCount++;
1095 }
1096 BootOptionCount = OptionCount-1;
1097
1098 // Display the hardcoded Boot entries
1099 for (Index = 0; Index < BootMainEntryCount; Index++) {
1100 Print(L"[%d] %s\n",OptionCount,BootMainEntries[Index]);
1101 OptionCount++;
1102 }
1103
1104 // Request the boot entry from the user
1105 BootOptionSelected = 0;
1106 while (BootOptionSelected == 0) {
1107 Print(L"Start: ");
1108 Status = GetHIInputInteger (&BootOptionSelected);
1109 if (EFI_ERROR(Status) || (BootOptionSelected == 0) || (BootOptionSelected > OptionCount)) {
1110 Print(L"Invalid input (max %d)\n",(OptionCount-1));
1111 BootOptionSelected = 0;
1112 }
1113 }
1114
1115 // Start the selected entry
1116 if (BootOptionSelected > BootOptionCount) {
1117 // Start the hardcoded entry
1118 Status = BootMainEntries[BootOptionSelected - BootOptionCount - 1].Callback (&BootOptionsList);
1119 } else {
1120 // Find the selected entry from the Boot#### list
1121 Index = 1;
1122 for (Entry = GetFirstNode (&BootOptionsList);
1123 !IsNull (&BootOptionsList,Entry);
1124 Entry = GetNextNode (&BootOptionsList,Entry)
1125 )
1126 {
1127 if (Index == BootOptionSelected) {
1128 BootOption = LOAD_OPTION_FROM_LINK(Entry);
1129 break;
1130 }
1131 Index++;
1132 }
1133
1134 Status = BootOptionStart (BootOption);
1135 }
1136 }
1137 // Should never go here
1138 }