VirtualBox

source: vbox/trunk/src/VBox/Devices/EFI/FirmwareNew/MdeModulePkg/Library/UefiBootManagerLib/BmLoadOption.c

Last change on this file was 105670, checked in by vboxsync, 9 months ago

Devices/EFI/FirmwareNew: Merge edk2-stable-202405 and make it build on aarch64, bugref:4643

  • Property svn:eol-style set to native
File size: 45.7 KB
Line 
1/** @file
2 Load option library functions which relate with creating and processing load options.
3
4Copyright (c) 2011 - 2019, Intel Corporation. All rights reserved.<BR>
5(C) Copyright 2015-2018 Hewlett Packard Enterprise Development LP<BR>
6SPDX-License-Identifier: BSD-2-Clause-Patent
7
8**/
9
10#include "InternalBm.h"
11
12#include <Library/VariablePolicyHelperLib.h>
13
14GLOBAL_REMOVE_IF_UNREFERENCED
15CHAR16 *mBmLoadOptionName[] = {
16 L"Driver",
17 L"SysPrep",
18 L"Boot",
19 L"PlatformRecovery"
20};
21
22GLOBAL_REMOVE_IF_UNREFERENCED
23CHAR16 *mBmLoadOptionOrderName[] = {
24 EFI_DRIVER_ORDER_VARIABLE_NAME,
25 EFI_SYS_PREP_ORDER_VARIABLE_NAME,
26 EFI_BOOT_ORDER_VARIABLE_NAME,
27 NULL // PlatformRecovery#### doesn't have associated *Order variable
28};
29
30/**
31 Call Visitor function for each variable in variable storage.
32
33 @param Visitor Visitor function.
34 @param Context The context passed to Visitor function.
35**/
36VOID
37BmForEachVariable (
38 BM_VARIABLE_VISITOR Visitor,
39 VOID *Context
40 )
41{
42 EFI_STATUS Status;
43 CHAR16 *Name;
44 EFI_GUID Guid;
45 UINTN NameSize;
46 UINTN NewNameSize;
47
48 NameSize = sizeof (CHAR16);
49 Name = AllocateZeroPool (NameSize);
50 ASSERT (Name != NULL);
51 while (TRUE) {
52 NewNameSize = NameSize;
53 Status = gRT->GetNextVariableName (&NewNameSize, Name, &Guid);
54 if (Status == EFI_BUFFER_TOO_SMALL) {
55 Name = ReallocatePool (NameSize, NewNameSize, Name);
56 ASSERT (Name != NULL);
57 Status = gRT->GetNextVariableName (&NewNameSize, Name, &Guid);
58 NameSize = NewNameSize;
59 }
60
61 if (Status == EFI_NOT_FOUND) {
62 break;
63 }
64
65 ASSERT_EFI_ERROR (Status);
66
67 Visitor (Name, &Guid, Context);
68 }
69
70 FreePool (Name);
71}
72
73/**
74 Get the Option Number that wasn't used.
75
76 @param LoadOptionType The load option type.
77 @param FreeOptionNumber Return the minimal free option number.
78
79 @retval EFI_SUCCESS The option number is found and will be returned.
80 @retval EFI_OUT_OF_RESOURCES There is no free option number that can be used.
81 @retval EFI_INVALID_PARAMETER FreeOptionNumber is NULL
82
83**/
84EFI_STATUS
85BmGetFreeOptionNumber (
86 IN EFI_BOOT_MANAGER_LOAD_OPTION_TYPE LoadOptionType,
87 OUT UINT16 *FreeOptionNumber
88 )
89{
90 UINTN OptionNumber;
91 UINTN Index;
92 UINT16 *OptionOrder;
93 UINTN OptionOrderSize;
94 UINT16 *BootNext;
95
96 ASSERT (FreeOptionNumber != NULL);
97 ASSERT (
98 LoadOptionType == LoadOptionTypeDriver ||
99 LoadOptionType == LoadOptionTypeBoot ||
100 LoadOptionType == LoadOptionTypeSysPrep
101 );
102
103 GetEfiGlobalVariable2 (mBmLoadOptionOrderName[LoadOptionType], (VOID **)&OptionOrder, &OptionOrderSize);
104 ASSERT ((OptionOrder != NULL && OptionOrderSize != 0) || (OptionOrder == NULL && OptionOrderSize == 0));
105
106 BootNext = NULL;
107 if (LoadOptionType == LoadOptionTypeBoot) {
108 GetEfiGlobalVariable2 (L"BootNext", (VOID **)&BootNext, NULL);
109 }
110
111 for (OptionNumber = 0;
112 OptionNumber < OptionOrderSize / sizeof (UINT16)
113 + ((BootNext != NULL) ? 1 : 0);
114 OptionNumber++
115 )
116 {
117 //
118 // Search in OptionOrder whether the OptionNumber exists
119 //
120 for (Index = 0; Index < OptionOrderSize / sizeof (UINT16); Index++) {
121 if (OptionNumber == OptionOrder[Index]) {
122 break;
123 }
124 }
125
126 //
127 // We didn't find it in the ****Order array and it doesn't equal to BootNext
128 // Otherwise, OptionNumber equals to OptionOrderSize / sizeof (UINT16) + 1
129 //
130 if ((Index == OptionOrderSize / sizeof (UINT16)) &&
131 ((BootNext == NULL) || (OptionNumber != *BootNext))
132 )
133 {
134 break;
135 }
136 }
137
138 if (OptionOrder != NULL) {
139 FreePool (OptionOrder);
140 }
141
142 if (BootNext != NULL) {
143 FreePool (BootNext);
144 }
145
146 //
147 // When BootOrder & BootNext conver all numbers in the range [0 ... 0xffff],
148 // OptionNumber equals to 0x10000 which is not valid.
149 //
150 ASSERT (OptionNumber <= 0x10000);
151 if (OptionNumber == 0x10000) {
152 return EFI_OUT_OF_RESOURCES;
153 } else {
154 *FreeOptionNumber = (UINT16)OptionNumber;
155 return EFI_SUCCESS;
156 }
157}
158
159/**
160 Create the Boot####, Driver####, SysPrep####, PlatformRecovery#### variable
161 from the load option.
162
163 @param LoadOption Pointer to the load option.
164
165 @retval EFI_SUCCESS The variable was created.
166 @retval Others Error status returned by RT->SetVariable.
167**/
168EFI_STATUS
169EFIAPI
170EfiBootManagerLoadOptionToVariable (
171 IN CONST EFI_BOOT_MANAGER_LOAD_OPTION *Option
172 )
173{
174 EFI_STATUS Status;
175 UINTN VariableSize;
176 UINT8 *Variable;
177 UINT8 *Ptr;
178 CHAR16 OptionName[BM_OPTION_NAME_LEN];
179 CHAR16 *Description;
180 CHAR16 NullChar;
181 EDKII_VARIABLE_POLICY_PROTOCOL *VariablePolicy;
182 UINT32 VariableAttributes;
183
184 if ((Option->OptionNumber == LoadOptionNumberUnassigned) ||
185 (Option->FilePath == NULL) ||
186 ((UINT32)Option->OptionType >= LoadOptionTypeMax)
187 )
188 {
189 return EFI_INVALID_PARAMETER;
190 }
191
192 //
193 // Convert NULL description to empty description
194 //
195 NullChar = L'\0';
196 Description = Option->Description;
197 if (Description == NULL) {
198 Description = &NullChar;
199 }
200
201 /*
202 UINT32 Attributes;
203 UINT16 FilePathListLength;
204 CHAR16 Description[];
205 EFI_DEVICE_PATH_PROTOCOL FilePathList[];
206 UINT8 OptionalData[];
207TODO: FilePathList[] IS:
208A packed array of UEFI device paths. The first element of the
209array is a device path that describes the device and location of the
210Image for this load option. The FilePathList[0] is specific
211to the device type. Other device paths may optionally exist in the
212FilePathList, but their usage is OSV specific. Each element
213in the array is variable length, and ends at the device path end
214structure.
215 */
216 VariableSize = sizeof (Option->Attributes)
217 + sizeof (UINT16)
218 + StrSize (Description)
219 + GetDevicePathSize (Option->FilePath)
220 + Option->OptionalDataSize;
221
222 Variable = AllocatePool (VariableSize);
223 ASSERT (Variable != NULL);
224
225 Ptr = Variable;
226 WriteUnaligned32 ((UINT32 *)Ptr, Option->Attributes);
227 Ptr += sizeof (Option->Attributes);
228
229 WriteUnaligned16 ((UINT16 *)Ptr, (UINT16)GetDevicePathSize (Option->FilePath));
230 Ptr += sizeof (UINT16);
231
232 CopyMem (Ptr, Description, StrSize (Description));
233 Ptr += StrSize (Description);
234
235 CopyMem (Ptr, Option->FilePath, GetDevicePathSize (Option->FilePath));
236 Ptr += GetDevicePathSize (Option->FilePath);
237
238 CopyMem (Ptr, Option->OptionalData, Option->OptionalDataSize);
239
240 UnicodeSPrint (OptionName, sizeof (OptionName), L"%s%04x", mBmLoadOptionName[Option->OptionType], Option->OptionNumber);
241
242 VariableAttributes = EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE;
243 if (Option->OptionType == LoadOptionTypePlatformRecovery) {
244 //
245 // Lock the PlatformRecovery####
246 //
247 Status = gBS->LocateProtocol (&gEdkiiVariablePolicyProtocolGuid, NULL, (VOID **)&VariablePolicy);
248 if (!EFI_ERROR (Status)) {
249 Status = RegisterBasicVariablePolicy (
250 VariablePolicy,
251 &gEfiGlobalVariableGuid,
252 OptionName,
253 VARIABLE_POLICY_NO_MIN_SIZE,
254 VARIABLE_POLICY_NO_MAX_SIZE,
255 VARIABLE_POLICY_NO_MUST_ATTR,
256 VARIABLE_POLICY_NO_CANT_ATTR,
257 VARIABLE_POLICY_TYPE_LOCK_NOW
258 );
259 ASSERT_EFI_ERROR (Status);
260 }
261
262 VariableAttributes = EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS;
263 }
264
265 Status = gRT->SetVariable (
266 OptionName,
267 &gEfiGlobalVariableGuid,
268 VariableAttributes,
269 VariableSize,
270 Variable
271 );
272 FreePool (Variable);
273
274 return Status;
275}
276
277/**
278 Update order variable .
279
280 @param OptionOrderName Order variable name which need to be updated.
281 @param OptionNumber Option number for the new option.
282 @param Position Position of the new load option to put in the ****Order variable.
283
284 @retval EFI_SUCCESS The boot#### or driver#### have been successfully registered.
285 @retval EFI_ALREADY_STARTED The option number of Option is being used already.
286 @retval EFI_STATUS Return the status of gRT->SetVariable ().
287
288**/
289EFI_STATUS
290BmAddOptionNumberToOrderVariable (
291 IN CHAR16 *OptionOrderName,
292 IN UINT16 OptionNumber,
293 IN UINTN Position
294 )
295{
296 EFI_STATUS Status;
297 UINTN Index;
298 UINT16 *OptionOrder;
299 UINT16 *NewOptionOrder;
300 UINTN OptionOrderSize;
301
302 //
303 // Update the option order variable
304 //
305 GetEfiGlobalVariable2 (OptionOrderName, (VOID **)&OptionOrder, &OptionOrderSize);
306 ASSERT ((OptionOrder != NULL && OptionOrderSize != 0) || (OptionOrder == NULL && OptionOrderSize == 0));
307
308 Status = EFI_SUCCESS;
309 for (Index = 0; Index < OptionOrderSize / sizeof (UINT16); Index++) {
310 if (OptionOrder[Index] == OptionNumber) {
311 Status = EFI_ALREADY_STARTED;
312 break;
313 }
314 }
315
316 if (!EFI_ERROR (Status)) {
317 Position = MIN (Position, OptionOrderSize / sizeof (UINT16));
318
319 NewOptionOrder = AllocatePool (OptionOrderSize + sizeof (UINT16));
320 ASSERT (NewOptionOrder != NULL);
321 if (OptionOrderSize != 0) {
322 CopyMem (NewOptionOrder, OptionOrder, Position * sizeof (UINT16));
323 CopyMem (&NewOptionOrder[Position + 1], &OptionOrder[Position], OptionOrderSize - Position * sizeof (UINT16));
324 }
325
326 NewOptionOrder[Position] = OptionNumber;
327
328 Status = gRT->SetVariable (
329 OptionOrderName,
330 &gEfiGlobalVariableGuid,
331 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
332 OptionOrderSize + sizeof (UINT16),
333 NewOptionOrder
334 );
335 FreePool (NewOptionOrder);
336 }
337
338 if (OptionOrder != NULL) {
339 FreePool (OptionOrder);
340 }
341
342 return Status;
343}
344
345/**
346 This function will register the new Boot####, Driver#### or SysPrep#### option.
347 After the *#### is updated, the *Order will also be updated.
348
349 @param Option Pointer to load option to add. If on input
350 Option->OptionNumber is LoadOptionNumberUnassigned,
351 then on output Option->OptionNumber is updated to
352 the number of the new Boot####,
353 Driver#### or SysPrep#### option.
354 @param Position Position of the new load option to put in the ****Order variable.
355
356 @retval EFI_SUCCESS The *#### have been successfully registered.
357 @retval EFI_INVALID_PARAMETER The option number exceeds 0xFFFF.
358 @retval EFI_ALREADY_STARTED The option number of Option is being used already.
359 Note: this API only adds new load option, no replacement support.
360 @retval EFI_OUT_OF_RESOURCES There is no free option number that can be used when the
361 option number specified in the Option is LoadOptionNumberUnassigned.
362 @return Status codes of gRT->SetVariable ().
363
364**/
365EFI_STATUS
366EFIAPI
367EfiBootManagerAddLoadOptionVariable (
368 IN OUT EFI_BOOT_MANAGER_LOAD_OPTION *Option,
369 IN UINTN Position
370 )
371{
372 EFI_STATUS Status;
373 UINT16 OptionNumber;
374
375 if (Option == NULL) {
376 return EFI_INVALID_PARAMETER;
377 }
378
379 if ((Option->OptionType != LoadOptionTypeDriver) &&
380 (Option->OptionType != LoadOptionTypeSysPrep) &&
381 (Option->OptionType != LoadOptionTypeBoot)
382 )
383 {
384 return EFI_INVALID_PARAMETER;
385 }
386
387 //
388 // Get the free option number if the option number is unassigned
389 //
390 if (Option->OptionNumber == LoadOptionNumberUnassigned) {
391 Status = BmGetFreeOptionNumber (Option->OptionType, &OptionNumber);
392 if (EFI_ERROR (Status)) {
393 return Status;
394 }
395
396 Option->OptionNumber = OptionNumber;
397 }
398
399 if (Option->OptionNumber >= LoadOptionNumberMax) {
400 return EFI_INVALID_PARAMETER;
401 }
402
403 Status = BmAddOptionNumberToOrderVariable (mBmLoadOptionOrderName[Option->OptionType], (UINT16)Option->OptionNumber, Position);
404 if (!EFI_ERROR (Status)) {
405 //
406 // Save the Boot#### or Driver#### variable
407 //
408 Status = EfiBootManagerLoadOptionToVariable (Option);
409 if (EFI_ERROR (Status)) {
410 //
411 // Remove the #### from *Order variable when the Driver####/SysPrep####/Boot#### cannot be saved.
412 //
413 EfiBootManagerDeleteLoadOptionVariable (Option->OptionNumber, Option->OptionType);
414 }
415 }
416
417 return Status;
418}
419
420/**
421 Sort the load option. The DriverOrder or BootOrder will be re-created to
422 reflect the new order.
423
424 @param OptionType Load option type
425 @param CompareFunction The comparator
426**/
427VOID
428EFIAPI
429EfiBootManagerSortLoadOptionVariable (
430 EFI_BOOT_MANAGER_LOAD_OPTION_TYPE OptionType,
431 SORT_COMPARE CompareFunction
432 )
433{
434 EFI_STATUS Status;
435 EFI_BOOT_MANAGER_LOAD_OPTION *LoadOption;
436 UINTN LoadOptionCount;
437 UINTN Index;
438 UINT16 *OptionOrder;
439
440 LoadOption = EfiBootManagerGetLoadOptions (&LoadOptionCount, OptionType);
441
442 //
443 // Insertion sort algorithm
444 //
445 PerformQuickSort (
446 LoadOption,
447 LoadOptionCount,
448 sizeof (EFI_BOOT_MANAGER_LOAD_OPTION),
449 CompareFunction
450 );
451
452 //
453 // Create new ****Order variable
454 //
455 OptionOrder = AllocatePool (LoadOptionCount * sizeof (UINT16));
456 ASSERT (OptionOrder != NULL);
457 for (Index = 0; Index < LoadOptionCount; Index++) {
458 OptionOrder[Index] = (UINT16)LoadOption[Index].OptionNumber;
459 }
460
461 Status = gRT->SetVariable (
462 mBmLoadOptionOrderName[OptionType],
463 &gEfiGlobalVariableGuid,
464 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
465 LoadOptionCount * sizeof (UINT16),
466 OptionOrder
467 );
468 //
469 // Changing the *Order content without increasing its size with current variable implementation shouldn't fail.
470 //
471 ASSERT_EFI_ERROR (Status);
472
473 FreePool (OptionOrder);
474 EfiBootManagerFreeLoadOptions (LoadOption, LoadOptionCount);
475}
476
477/**
478 Initialize a load option.
479
480 @param Option Pointer to the load option to be initialized.
481 @param OptionNumber Option number of the load option.
482 @param OptionType Type of the load option.
483 @param Attributes Attributes of the load option.
484 @param Description Description of the load option.
485 @param FilePath Device path of the load option.
486 @param OptionalData Optional data of the load option.
487 @param OptionalDataSize Size of the optional data of the load option.
488
489 @retval EFI_SUCCESS The load option was initialized successfully.
490 @retval EFI_INVALID_PARAMETER Option, Description or FilePath is NULL.
491**/
492EFI_STATUS
493EFIAPI
494EfiBootManagerInitializeLoadOption (
495 IN OUT EFI_BOOT_MANAGER_LOAD_OPTION *Option,
496 IN UINTN OptionNumber,
497 IN EFI_BOOT_MANAGER_LOAD_OPTION_TYPE OptionType,
498 IN UINT32 Attributes,
499 IN CHAR16 *Description,
500 IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
501 IN UINT8 *OptionalData OPTIONAL,
502 IN UINT32 OptionalDataSize
503 )
504{
505 if ((Option == NULL) || (Description == NULL) || (FilePath == NULL)) {
506 return EFI_INVALID_PARAMETER;
507 }
508
509 if (((OptionalData != NULL) && (OptionalDataSize == 0)) ||
510 ((OptionalData == NULL) && (OptionalDataSize != 0)))
511 {
512 return EFI_INVALID_PARAMETER;
513 }
514
515 if ((UINT32)OptionType >= LoadOptionTypeMax) {
516 return EFI_INVALID_PARAMETER;
517 }
518
519 ZeroMem (Option, sizeof (EFI_BOOT_MANAGER_LOAD_OPTION));
520 Option->OptionNumber = OptionNumber;
521 Option->OptionType = OptionType;
522 Option->Attributes = Attributes;
523 Option->Description = AllocateCopyPool (StrSize (Description), Description);
524 Option->FilePath = DuplicateDevicePath (FilePath);
525 if (OptionalData != NULL) {
526 Option->OptionalData = AllocateCopyPool (OptionalDataSize, OptionalData);
527 Option->OptionalDataSize = OptionalDataSize;
528 }
529
530 return EFI_SUCCESS;
531}
532
533/**
534 Return the index of the load option in the load option array.
535
536 The function consider two load options are equal when the
537 OptionType, Attributes, Description, FilePath and OptionalData are equal.
538
539 @param Key Pointer to the load option to be found.
540 @param Array Pointer to the array of load options to be found.
541 @param Count Number of entries in the Array.
542
543 @retval -1 Key wasn't found in the Array.
544 @retval 0 ~ Count-1 The index of the Key in the Array.
545**/
546INTN
547EFIAPI
548EfiBootManagerFindLoadOption (
549 IN CONST EFI_BOOT_MANAGER_LOAD_OPTION *Key,
550 IN CONST EFI_BOOT_MANAGER_LOAD_OPTION *Array,
551 IN UINTN Count
552 )
553{
554 UINTN Index;
555
556 for (Index = 0; Index < Count; Index++) {
557 if ((Key->OptionType == Array[Index].OptionType) &&
558 (Key->Attributes == Array[Index].Attributes) &&
559 (StrCmp (Key->Description, Array[Index].Description) == 0) &&
560 (CompareMem (Key->FilePath, Array[Index].FilePath, GetDevicePathSize (Key->FilePath)) == 0) &&
561 (Key->OptionalDataSize == Array[Index].OptionalDataSize) &&
562 (CompareMem (Key->OptionalData, Array[Index].OptionalData, Key->OptionalDataSize) == 0))
563 {
564 return (INTN)Index;
565 }
566 }
567
568 return -1;
569}
570
571/**
572 Delete the load option.
573
574 @param OptionNumber Indicate the option number of load option
575 @param OptionType Indicate the type of load option
576
577 @retval EFI_INVALID_PARAMETER OptionType or OptionNumber is invalid.
578 @retval EFI_NOT_FOUND The load option cannot be found
579 @retval EFI_SUCCESS The load option was deleted
580 @retval others Status of RT->SetVariable()
581**/
582EFI_STATUS
583EFIAPI
584EfiBootManagerDeleteLoadOptionVariable (
585 IN UINTN OptionNumber,
586 IN EFI_BOOT_MANAGER_LOAD_OPTION_TYPE OptionType
587 )
588{
589 UINT16 *OptionOrder;
590 UINTN OptionOrderSize;
591 UINTN Index;
592 CHAR16 OptionName[BM_OPTION_NAME_LEN];
593
594 if (((UINT32)OptionType >= LoadOptionTypeMax) || (OptionNumber >= LoadOptionNumberMax)) {
595 return EFI_INVALID_PARAMETER;
596 }
597
598 if ((OptionType == LoadOptionTypeDriver) || (OptionType == LoadOptionTypeSysPrep) || (OptionType == LoadOptionTypeBoot)) {
599 //
600 // If the associated *Order exists, firstly remove the reference in *Order for
601 // Driver####, SysPrep#### and Boot####.
602 //
603 GetEfiGlobalVariable2 (mBmLoadOptionOrderName[OptionType], (VOID **)&OptionOrder, &OptionOrderSize);
604 ASSERT ((OptionOrder != NULL && OptionOrderSize != 0) || (OptionOrder == NULL && OptionOrderSize == 0));
605
606 for (Index = 0; Index < OptionOrderSize / sizeof (UINT16); Index++) {
607 if (OptionOrder[Index] == OptionNumber) {
608 OptionOrderSize -= sizeof (UINT16);
609 CopyMem (&OptionOrder[Index], &OptionOrder[Index + 1], OptionOrderSize - Index * sizeof (UINT16));
610 gRT->SetVariable (
611 mBmLoadOptionOrderName[OptionType],
612 &gEfiGlobalVariableGuid,
613 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
614 OptionOrderSize,
615 OptionOrder
616 );
617 break;
618 }
619 }
620
621 if (OptionOrder != NULL) {
622 FreePool (OptionOrder);
623 }
624 }
625
626 //
627 // Remove the Driver####, SysPrep####, Boot#### or PlatformRecovery#### itself.
628 //
629 UnicodeSPrint (OptionName, sizeof (OptionName), L"%s%04x", mBmLoadOptionName[OptionType], OptionNumber);
630 return gRT->SetVariable (
631 OptionName,
632 &gEfiGlobalVariableGuid,
633 0,
634 0,
635 NULL
636 );
637}
638
639/**
640 Returns the size of a device path in bytes.
641
642 This function returns the size, in bytes, of the device path data structure
643 specified by DevicePath including the end of device path node. If DevicePath
644 is NULL, then 0 is returned. If the length of the device path is bigger than
645 MaxSize, also return 0 to indicate this is an invalidate device path.
646
647 @param DevicePath A pointer to a device path data structure.
648 @param MaxSize Max valid device path size. If big than this size,
649 return error.
650
651 @retval 0 An invalid device path.
652 @retval Others The size of a device path in bytes.
653
654**/
655UINTN
656BmGetDevicePathSizeEx (
657 IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath,
658 IN UINTN MaxSize
659 )
660{
661 UINTN Size;
662 UINTN NodeSize;
663
664 if (DevicePath == NULL) {
665 return 0;
666 }
667
668 //
669 // Search for the end of the device path structure
670 //
671 Size = 0;
672 while (!IsDevicePathEnd (DevicePath)) {
673 NodeSize = DevicePathNodeLength (DevicePath);
674 if (NodeSize == 0) {
675 return 0;
676 }
677
678 Size += NodeSize;
679 if (Size > MaxSize) {
680 return 0;
681 }
682
683 DevicePath = NextDevicePathNode (DevicePath);
684 }
685
686 Size += DevicePathNodeLength (DevicePath);
687 if (Size > MaxSize) {
688 return 0;
689 }
690
691 return Size;
692}
693
694/**
695 Returns the length of a Null-terminated Unicode string. If the length is
696 bigger than MaxStringLen, return length 0 to indicate that this is an
697 invalidate string.
698
699 This function returns the number of Unicode characters in the Null-terminated
700 Unicode string specified by String.
701
702 If String is NULL, then ASSERT().
703 If String is not aligned on a 16-bit boundary, then ASSERT().
704
705 @param String A pointer to a Null-terminated Unicode string.
706 @param MaxStringLen Max string len in this string.
707
708 @retval 0 An invalid string.
709 @retval Others The length of String.
710
711**/
712UINTN
713BmStrSizeEx (
714 IN CONST CHAR16 *String,
715 IN UINTN MaxStringLen
716 )
717{
718 UINTN Length;
719
720 ASSERT (String != NULL && MaxStringLen != 0);
721 ASSERT (((UINTN)String & BIT0) == 0);
722
723 for (Length = 0; *String != L'\0' && MaxStringLen != Length; String++, Length += 2) {
724 }
725
726 if ((*String != L'\0') && (MaxStringLen == Length)) {
727 return 0;
728 }
729
730 return Length + 2;
731}
732
733/**
734 Validate the Boot####, Driver####, SysPrep#### and PlatformRecovery####
735 variable (VendorGuid/Name)
736
737 @param Variable The variable data.
738 @param VariableSize The variable size.
739
740 @retval TRUE The variable data is correct.
741 @retval FALSE The variable data is corrupted.
742
743**/
744BOOLEAN
745BmValidateOption (
746 UINT8 *Variable,
747 UINTN VariableSize
748 )
749{
750 UINT16 FilePathSize;
751 EFI_DEVICE_PATH_PROTOCOL *DevicePath;
752 UINTN DescriptionSize;
753
754 if (VariableSize <= sizeof (UINT16) + sizeof (UINT32)) {
755 return FALSE;
756 }
757
758 //
759 // Skip the option attribute
760 //
761 Variable += sizeof (UINT32);
762
763 //
764 // Get the option's device path size
765 //
766 FilePathSize = ReadUnaligned16 ((UINT16 *)Variable);
767 Variable += sizeof (UINT16);
768
769 //
770 // Get the option's description string size
771 //
772 DescriptionSize = BmStrSizeEx ((CHAR16 *)Variable, VariableSize - sizeof (UINT16) - sizeof (UINT32));
773 Variable += DescriptionSize;
774
775 //
776 // Get the option's device path
777 //
778 DevicePath = (EFI_DEVICE_PATH_PROTOCOL *)Variable;
779
780 //
781 // Validation boot option variable.
782 //
783 if ((FilePathSize == 0) || (DescriptionSize == 0)) {
784 return FALSE;
785 }
786
787 if (sizeof (UINT32) + sizeof (UINT16) + DescriptionSize + FilePathSize > VariableSize) {
788 return FALSE;
789 }
790
791 return (BOOLEAN)(BmGetDevicePathSizeEx (DevicePath, FilePathSize) != 0);
792}
793
794/**
795 Check whether the VariableName is a valid load option variable name
796 and return the load option type and option number.
797
798 @param VariableName The name of the load option variable.
799 @param OptionType Return the load option type.
800 @param OptionNumber Return the load option number.
801
802 @retval TRUE The variable name is valid; The load option type and
803 load option number is returned.
804 @retval FALSE The variable name is NOT valid.
805**/
806BOOLEAN
807EFIAPI
808EfiBootManagerIsValidLoadOptionVariableName (
809 IN CHAR16 *VariableName,
810 OUT EFI_BOOT_MANAGER_LOAD_OPTION_TYPE *OptionType OPTIONAL,
811 OUT UINT16 *OptionNumber OPTIONAL
812 )
813{
814 UINTN VariableNameLen;
815 UINTN Index;
816 UINTN Uint;
817 EFI_BOOT_MANAGER_LOAD_OPTION_TYPE LocalOptionType;
818 UINT16 LocalOptionNumber;
819
820 if (VariableName == NULL) {
821 return FALSE;
822 }
823
824 VariableNameLen = StrLen (VariableName);
825
826 //
827 // Return FALSE when the variable name length is too small.
828 //
829 if (VariableNameLen <= 4) {
830 return FALSE;
831 }
832
833 //
834 // Return FALSE when the variable name doesn't start with Driver/SysPrep/Boot/PlatformRecovery.
835 //
836 for (LocalOptionType = 0; LocalOptionType < ARRAY_SIZE (mBmLoadOptionName); LocalOptionType++) {
837 if ((VariableNameLen - 4 == StrLen (mBmLoadOptionName[LocalOptionType])) &&
838 (StrnCmp (VariableName, mBmLoadOptionName[LocalOptionType], VariableNameLen - 4) == 0)
839 )
840 {
841 break;
842 }
843 }
844
845 if (LocalOptionType == ARRAY_SIZE (mBmLoadOptionName)) {
846 return FALSE;
847 }
848
849 //
850 // Return FALSE when the last four characters are not hex digits.
851 //
852 LocalOptionNumber = 0;
853 for (Index = VariableNameLen - 4; Index < VariableNameLen; Index++) {
854 Uint = BmCharToUint (VariableName[Index]);
855 if (Uint == -1) {
856 break;
857 } else {
858 LocalOptionNumber = (UINT16)Uint + LocalOptionNumber * 0x10;
859 }
860 }
861
862 if (Index != VariableNameLen) {
863 return FALSE;
864 }
865
866 if (OptionType != NULL) {
867 *OptionType = LocalOptionType;
868 }
869
870 if (OptionNumber != NULL) {
871 *OptionNumber = LocalOptionNumber;
872 }
873
874 return TRUE;
875}
876
877/**
878 Build the Boot#### or Driver#### option from the VariableName.
879
880 @param VariableName Variable name of the load option
881 @param VendorGuid Variable GUID of the load option
882 @param Option Return the load option.
883
884 @retval EFI_SUCCESS Get the option just been created
885 @retval EFI_NOT_FOUND Failed to get the new option
886
887**/
888EFI_STATUS
889EFIAPI
890EfiBootManagerVariableToLoadOptionEx (
891 IN CHAR16 *VariableName,
892 IN EFI_GUID *VendorGuid,
893 IN OUT EFI_BOOT_MANAGER_LOAD_OPTION *Option
894 )
895{
896 EFI_STATUS Status;
897 UINT32 Attribute;
898 UINT16 FilePathSize;
899 UINT8 *Variable;
900 UINT8 *VariablePtr;
901 UINTN VariableSize;
902 EFI_DEVICE_PATH_PROTOCOL *FilePath;
903 UINT8 *OptionalData;
904 UINT32 OptionalDataSize;
905 CHAR16 *Description;
906 EFI_BOOT_MANAGER_LOAD_OPTION_TYPE OptionType;
907 UINT16 OptionNumber;
908
909 if ((VariableName == NULL) || (Option == NULL)) {
910 return EFI_INVALID_PARAMETER;
911 }
912
913 if (!EfiBootManagerIsValidLoadOptionVariableName (VariableName, &OptionType, &OptionNumber)) {
914 return EFI_INVALID_PARAMETER;
915 }
916
917 //
918 // Read the variable
919 //
920 GetVariable2 (VariableName, VendorGuid, (VOID **)&Variable, &VariableSize);
921 if (Variable == NULL) {
922 return EFI_NOT_FOUND;
923 }
924
925 //
926 // Validate *#### variable data.
927 //
928 if (!BmValidateOption (Variable, VariableSize)) {
929 FreePool (Variable);
930 return EFI_INVALID_PARAMETER;
931 }
932
933 //
934 // Get the option attribute
935 //
936 VariablePtr = Variable;
937 Attribute = ReadUnaligned32 ((UINT32 *)VariablePtr);
938 VariablePtr += sizeof (UINT32);
939
940 //
941 // Get the option's device path size
942 //
943 FilePathSize = ReadUnaligned16 ((UINT16 *)VariablePtr);
944 VariablePtr += sizeof (UINT16);
945
946 //
947 // Get the option's description string
948 //
949 Description = (CHAR16 *)VariablePtr;
950
951 //
952 // Get the option's description string size
953 //
954 VariablePtr += StrSize ((CHAR16 *)VariablePtr);
955
956 //
957 // Get the option's device path
958 //
959 FilePath = (EFI_DEVICE_PATH_PROTOCOL *)VariablePtr;
960 VariablePtr += FilePathSize;
961
962 OptionalDataSize = (UINT32)(VariableSize - ((UINTN)VariablePtr - (UINTN)Variable));
963 if (OptionalDataSize == 0) {
964 OptionalData = NULL;
965 } else {
966 OptionalData = VariablePtr;
967 }
968
969 Status = EfiBootManagerInitializeLoadOption (
970 Option,
971 OptionNumber,
972 OptionType,
973 Attribute,
974 Description,
975 FilePath,
976 OptionalData,
977 OptionalDataSize
978 );
979 ASSERT_EFI_ERROR (Status);
980
981 CopyGuid (&Option->VendorGuid, VendorGuid);
982
983 FreePool (Variable);
984 return Status;
985}
986
987/**
988Build the Boot#### or Driver#### option from the VariableName.
989
990@param VariableName EFI Variable name indicate if it is Boot#### or Driver####
991@param Option Return the Boot#### or Driver#### option.
992
993@retval EFI_SUCCESS Get the option just been created
994@retval EFI_NOT_FOUND Failed to get the new option
995**/
996EFI_STATUS
997EFIAPI
998EfiBootManagerVariableToLoadOption (
999 IN CHAR16 *VariableName,
1000 IN OUT EFI_BOOT_MANAGER_LOAD_OPTION *Option
1001 )
1002{
1003 return EfiBootManagerVariableToLoadOptionEx (VariableName, &gEfiGlobalVariableGuid, Option);
1004}
1005
1006typedef struct {
1007 EFI_BOOT_MANAGER_LOAD_OPTION_TYPE OptionType;
1008 EFI_GUID *Guid;
1009 EFI_BOOT_MANAGER_LOAD_OPTION *Options;
1010 UINTN OptionCount;
1011} BM_COLLECT_LOAD_OPTIONS_PARAM;
1012
1013/**
1014 Visitor function to collect the Platform Recovery load options or OS Recovery
1015 load options from NV storage.
1016
1017 @param Name Variable name.
1018 @param Guid Variable GUID.
1019 @param Context The same context passed to BmForEachVariable.
1020**/
1021VOID
1022BmCollectLoadOptions (
1023 IN CHAR16 *Name,
1024 IN EFI_GUID *Guid,
1025 IN VOID *Context
1026 )
1027{
1028 EFI_STATUS Status;
1029 EFI_BOOT_MANAGER_LOAD_OPTION_TYPE OptionType;
1030 UINT16 OptionNumber;
1031 EFI_BOOT_MANAGER_LOAD_OPTION Option;
1032 UINTN Index;
1033 BM_COLLECT_LOAD_OPTIONS_PARAM *Param;
1034
1035 Param = (BM_COLLECT_LOAD_OPTIONS_PARAM *)Context;
1036
1037 if (CompareGuid (Guid, Param->Guid) && (
1038 (Param->OptionType == LoadOptionTypePlatformRecovery) &&
1039 EfiBootManagerIsValidLoadOptionVariableName (Name, &OptionType, &OptionNumber) &&
1040 (OptionType == LoadOptionTypePlatformRecovery)
1041 ))
1042 {
1043 Status = EfiBootManagerVariableToLoadOptionEx (Name, Guid, &Option);
1044 if (!EFI_ERROR (Status)) {
1045 for (Index = 0; Index < Param->OptionCount; Index++) {
1046 if (Param->Options[Index].OptionNumber > Option.OptionNumber) {
1047 break;
1048 }
1049 }
1050
1051 Param->Options = ReallocatePool (
1052 Param->OptionCount * sizeof (EFI_BOOT_MANAGER_LOAD_OPTION),
1053 (Param->OptionCount + 1) * sizeof (EFI_BOOT_MANAGER_LOAD_OPTION),
1054 Param->Options
1055 );
1056 ASSERT (Param->Options != NULL);
1057 CopyMem (&Param->Options[Index + 1], &Param->Options[Index], (Param->OptionCount - Index) * sizeof (EFI_BOOT_MANAGER_LOAD_OPTION));
1058 CopyMem (&Param->Options[Index], &Option, sizeof (EFI_BOOT_MANAGER_LOAD_OPTION));
1059 Param->OptionCount++;
1060 }
1061 }
1062}
1063
1064/**
1065 Returns an array of load options based on the EFI variable
1066 L"BootOrder"/L"DriverOrder" and the L"Boot####"/L"Driver####" variables impled by it.
1067 #### is the hex value of the UINT16 in each BootOrder/DriverOrder entry.
1068
1069 @param LoadOptionCount Returns number of entries in the array.
1070 @param LoadOptionType The type of the load option.
1071
1072 @retval NULL No load options exist.
1073 @retval !NULL Array of load option entries.
1074
1075**/
1076EFI_BOOT_MANAGER_LOAD_OPTION *
1077EFIAPI
1078EfiBootManagerGetLoadOptions (
1079 OUT UINTN *OptionCount,
1080 IN EFI_BOOT_MANAGER_LOAD_OPTION_TYPE LoadOptionType
1081 )
1082{
1083 EFI_STATUS Status;
1084 UINT16 *OptionOrder;
1085 UINTN OptionOrderSize;
1086 UINTN Index;
1087 UINTN OptionIndex;
1088 EFI_BOOT_MANAGER_LOAD_OPTION *Options;
1089 CHAR16 OptionName[BM_OPTION_NAME_LEN];
1090 UINT16 OptionNumber;
1091 BM_COLLECT_LOAD_OPTIONS_PARAM Param;
1092
1093 *OptionCount = 0;
1094 Options = NULL;
1095
1096 if ((LoadOptionType == LoadOptionTypeDriver) || (LoadOptionType == LoadOptionTypeSysPrep) || (LoadOptionType == LoadOptionTypeBoot)) {
1097 //
1098 // Read the BootOrder, or DriverOrder variable.
1099 //
1100 GetEfiGlobalVariable2 (mBmLoadOptionOrderName[LoadOptionType], (VOID **)&OptionOrder, &OptionOrderSize);
1101 if (OptionOrder == NULL) {
1102 return NULL;
1103 }
1104
1105 *OptionCount = OptionOrderSize / sizeof (UINT16);
1106
1107 Options = AllocatePool (*OptionCount * sizeof (EFI_BOOT_MANAGER_LOAD_OPTION));
1108 ASSERT (Options != NULL);
1109
1110 OptionIndex = 0;
1111 for (Index = 0; Index < *OptionCount; Index++) {
1112 OptionNumber = OptionOrder[Index];
1113 UnicodeSPrint (OptionName, sizeof (OptionName), L"%s%04x", mBmLoadOptionName[LoadOptionType], OptionNumber);
1114
1115 Status = EfiBootManagerVariableToLoadOption (OptionName, &Options[OptionIndex]);
1116 if (EFI_ERROR (Status)) {
1117 DEBUG ((DEBUG_INFO, "[Bds] %s doesn't exist - Update ****Order variable to remove the reference!!", OptionName));
1118 EfiBootManagerDeleteLoadOptionVariable (OptionNumber, LoadOptionType);
1119 } else {
1120 ASSERT (Options[OptionIndex].OptionNumber == OptionNumber);
1121 OptionIndex++;
1122 }
1123 }
1124
1125 if (OptionOrder != NULL) {
1126 FreePool (OptionOrder);
1127 }
1128
1129 if (OptionIndex < *OptionCount) {
1130 Options = ReallocatePool (*OptionCount * sizeof (EFI_BOOT_MANAGER_LOAD_OPTION), OptionIndex * sizeof (EFI_BOOT_MANAGER_LOAD_OPTION), Options);
1131 ASSERT (Options != NULL);
1132 *OptionCount = OptionIndex;
1133 }
1134 } else if (LoadOptionType == LoadOptionTypePlatformRecovery) {
1135 Param.OptionType = LoadOptionTypePlatformRecovery;
1136 Param.Options = NULL;
1137 Param.OptionCount = 0;
1138 Param.Guid = &gEfiGlobalVariableGuid;
1139
1140 BmForEachVariable (BmCollectLoadOptions, (VOID *)&Param);
1141
1142 *OptionCount = Param.OptionCount;
1143 Options = Param.Options;
1144 }
1145
1146 return Options;
1147}
1148
1149/**
1150 Free an EFI_BOOT_MANGER_LOAD_OPTION entry that was allocate by the library.
1151
1152 @param LoadOption Pointer to boot option to Free.
1153
1154 @return EFI_SUCCESS BootOption was freed
1155 @return EFI_NOT_FOUND BootOption == NULL
1156
1157**/
1158EFI_STATUS
1159EFIAPI
1160EfiBootManagerFreeLoadOption (
1161 IN EFI_BOOT_MANAGER_LOAD_OPTION *LoadOption
1162 )
1163{
1164 if (LoadOption == NULL) {
1165 return EFI_NOT_FOUND;
1166 }
1167
1168 if (LoadOption->Description != NULL) {
1169 FreePool (LoadOption->Description);
1170 }
1171
1172 if (LoadOption->FilePath != NULL) {
1173 FreePool (LoadOption->FilePath);
1174 }
1175
1176 if (LoadOption->OptionalData != NULL) {
1177 FreePool (LoadOption->OptionalData);
1178 }
1179
1180 return EFI_SUCCESS;
1181}
1182
1183/**
1184 Free an EFI_BOOT_MANGER_LOAD_OPTION array that was allocated by
1185 EfiBootManagerGetLoadOptions().
1186
1187 @param Option Pointer to boot option array to free.
1188 @param OptionCount Number of array entries in BootOption
1189
1190 @return EFI_SUCCESS BootOption was freed
1191 @return EFI_NOT_FOUND BootOption == NULL
1192
1193**/
1194EFI_STATUS
1195EFIAPI
1196EfiBootManagerFreeLoadOptions (
1197 IN EFI_BOOT_MANAGER_LOAD_OPTION *Option,
1198 IN UINTN OptionCount
1199 )
1200{
1201 UINTN Index;
1202
1203 if (Option == NULL) {
1204 return EFI_NOT_FOUND;
1205 }
1206
1207 for (Index = 0; Index < OptionCount; Index++) {
1208 EfiBootManagerFreeLoadOption (&Option[Index]);
1209 }
1210
1211 FreePool (Option);
1212
1213 return EFI_SUCCESS;
1214}
1215
1216/**
1217 Return whether the PE header of the load option is valid or not.
1218
1219 @param[in] Type The load option type.
1220 It's used to check whether the load option is valid.
1221 When it's LoadOptionTypeMax, the routine only guarantees
1222 the load option is a valid PE image but doesn't guarantee
1223 the PE's subsystem type is valid.
1224 @param[in] FileBuffer The PE file buffer of the load option.
1225 @param[in] FileSize The size of the load option file.
1226
1227 @retval TRUE The PE header of the load option is valid.
1228 @retval FALSE The PE header of the load option is not valid.
1229**/
1230BOOLEAN
1231BmIsLoadOptionPeHeaderValid (
1232 IN EFI_BOOT_MANAGER_LOAD_OPTION_TYPE Type,
1233 IN VOID *FileBuffer,
1234 IN UINTN FileSize
1235 )
1236{
1237 EFI_IMAGE_DOS_HEADER *DosHeader;
1238 EFI_IMAGE_OPTIONAL_HEADER_UNION *PeHeader;
1239 EFI_IMAGE_OPTIONAL_HEADER32 *OptionalHeader;
1240 UINT16 Subsystem;
1241
1242 if ((FileBuffer == NULL) || (FileSize == 0)) {
1243 return FALSE;
1244 }
1245
1246#ifdef VBOX
1247 /*
1248 * Check for Fat/Universal EFI binaries provided by older macOS versions
1249 * (Mountain Lion and older).
1250 *
1251 * @todo More checks here? (VBoxPeCoffLib will do more thorough checks
1252 * when the image is actually loaded).
1253 */
1254 if (*(UINT32 *)FileBuffer == 0x0ef1fab9)
1255 return TRUE;
1256#endif
1257
1258 //
1259 // Read dos header
1260 //
1261 DosHeader = (EFI_IMAGE_DOS_HEADER *)FileBuffer;
1262 if ((FileSize >= sizeof (EFI_IMAGE_DOS_HEADER)) &&
1263 (FileSize > DosHeader->e_lfanew) && (DosHeader->e_magic == EFI_IMAGE_DOS_SIGNATURE)
1264 )
1265 {
1266 //
1267 // Read and check PE signature
1268 //
1269 PeHeader = (EFI_IMAGE_OPTIONAL_HEADER_UNION *)((UINT8 *)FileBuffer + DosHeader->e_lfanew);
1270 if ((FileSize >= DosHeader->e_lfanew + sizeof (EFI_IMAGE_OPTIONAL_HEADER_UNION)) &&
1271 (PeHeader->Pe32.Signature == EFI_IMAGE_NT_SIGNATURE)
1272 )
1273 {
1274 //
1275 // Check PE32 or PE32+ magic, and machine type
1276 //
1277 OptionalHeader = (EFI_IMAGE_OPTIONAL_HEADER32 *)&PeHeader->Pe32.OptionalHeader;
1278 if ((OptionalHeader->Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) ||
1279 (OptionalHeader->Magic == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC))
1280 {
1281 //
1282 // Check the Subsystem:
1283 // Driver#### must be of type BootServiceDriver or RuntimeDriver
1284 // SysPrep####, Boot####, OsRecovery####, PlatformRecovery#### must be of type Application
1285 //
1286 Subsystem = OptionalHeader->Subsystem;
1287 if ((Type == LoadOptionTypeMax) ||
1288 ((Type == LoadOptionTypeDriver) && (Subsystem == EFI_IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER)) ||
1289 ((Type == LoadOptionTypeDriver) && (Subsystem == EFI_IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER)) ||
1290 ((Type == LoadOptionTypeSysPrep) && (Subsystem == EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION)) ||
1291 ((Type == LoadOptionTypeBoot) && (Subsystem == EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION)) ||
1292 ((Type == LoadOptionTypePlatformRecovery) && (Subsystem == EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION))
1293 )
1294 {
1295 return TRUE;
1296 }
1297 }
1298 }
1299 }
1300
1301 return FALSE;
1302}
1303
1304/**
1305 Return the next matched load option buffer.
1306 The routine keeps calling BmGetNextLoadOptionDevicePath() until a valid
1307 load option is read.
1308
1309 @param Type The load option type.
1310 It's used to check whether the load option is valid.
1311 When it's LoadOptionTypeMax, the routine only guarantees
1312 the load option is a valid PE image but doesn't guarantee
1313 the PE's subsystem type is valid.
1314 @param FilePath The device path pointing to a load option.
1315 It could be a short-form device path.
1316 @param FullPath Return the next full device path of the load option after
1317 short-form device path expanding.
1318 Caller is responsible to free it.
1319 NULL to return the first matched full device path.
1320 @param FileSize Return the load option size.
1321
1322 @return The load option buffer. Caller is responsible to free the memory.
1323**/
1324VOID *
1325BmGetNextLoadOptionBuffer (
1326 IN EFI_BOOT_MANAGER_LOAD_OPTION_TYPE Type,
1327 IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
1328 OUT EFI_DEVICE_PATH_PROTOCOL **FullPath,
1329 OUT UINTN *FileSize
1330 )
1331{
1332 VOID *FileBuffer;
1333 EFI_DEVICE_PATH_PROTOCOL *PreFullPath;
1334 EFI_DEVICE_PATH_PROTOCOL *CurFullPath;
1335 UINTN LocalFileSize;
1336 UINT32 AuthenticationStatus;
1337 EFI_DEVICE_PATH_PROTOCOL *RamDiskDevicePath;
1338
1339 LocalFileSize = 0;
1340 FileBuffer = NULL;
1341 CurFullPath = *FullPath;
1342 do {
1343 PreFullPath = CurFullPath;
1344 CurFullPath = BmGetNextLoadOptionDevicePath (FilePath, CurFullPath);
1345 //
1346 // Only free the full path created *inside* this routine
1347 //
1348 if ((PreFullPath != NULL) && (PreFullPath != *FullPath)) {
1349 FreePool (PreFullPath);
1350 }
1351
1352 if (CurFullPath == NULL) {
1353 break;
1354 }
1355
1356 FileBuffer = GetFileBufferByFilePath (TRUE, CurFullPath, &LocalFileSize, &AuthenticationStatus);
1357 if ((FileBuffer != NULL) && !BmIsLoadOptionPeHeaderValid (Type, FileBuffer, LocalFileSize)) {
1358 //
1359 // Free the RAM disk file system if the load option is invalid.
1360 //
1361 RamDiskDevicePath = BmGetRamDiskDevicePath (FilePath);
1362 if (RamDiskDevicePath != NULL) {
1363 BmDestroyRamDisk (RamDiskDevicePath);
1364 FreePool (RamDiskDevicePath);
1365 }
1366
1367 //
1368 // Free the invalid load option buffer.
1369 //
1370 FreePool (FileBuffer);
1371 FileBuffer = NULL;
1372 }
1373 } while (FileBuffer == NULL);
1374
1375 if (FileBuffer == NULL) {
1376 CurFullPath = NULL;
1377 LocalFileSize = 0;
1378 }
1379
1380 DEBUG ((DEBUG_INFO, "[Bds] Expand "));
1381 BmPrintDp (FilePath);
1382 DEBUG ((DEBUG_INFO, " -> "));
1383 BmPrintDp (CurFullPath);
1384 DEBUG ((DEBUG_INFO, "\n"));
1385
1386 *FullPath = CurFullPath;
1387 *FileSize = LocalFileSize;
1388 return FileBuffer;
1389}
1390
1391/**
1392 Process (load and execute) the load option.
1393
1394 @param LoadOption Pointer to the load option.
1395
1396 @retval EFI_INVALID_PARAMETER The load option type is invalid,
1397 or the load option file path doesn't point to a valid file.
1398 @retval EFI_UNSUPPORTED The load option type is of LoadOptionTypeBoot.
1399 @retval EFI_SUCCESS The load option is inactive, or successfully loaded and executed.
1400**/
1401EFI_STATUS
1402EFIAPI
1403EfiBootManagerProcessLoadOption (
1404 IN EFI_BOOT_MANAGER_LOAD_OPTION *LoadOption
1405 )
1406{
1407 EFI_STATUS Status;
1408 EFI_DEVICE_PATH_PROTOCOL *PreFullPath;
1409 EFI_DEVICE_PATH_PROTOCOL *CurFullPath;
1410 EFI_HANDLE ImageHandle;
1411 EFI_LOADED_IMAGE_PROTOCOL *ImageInfo;
1412 VOID *FileBuffer;
1413 UINTN FileSize;
1414
1415 if ((UINT32)LoadOption->OptionType >= LoadOptionTypeMax) {
1416 return EFI_INVALID_PARAMETER;
1417 }
1418
1419 if (LoadOption->OptionType == LoadOptionTypeBoot) {
1420 return EFI_UNSUPPORTED;
1421 }
1422
1423 //
1424 // If a load option is not marked as LOAD_OPTION_ACTIVE,
1425 // the boot manager will not automatically load the option.
1426 //
1427 if ((LoadOption->Attributes & LOAD_OPTION_ACTIVE) == 0) {
1428 return EFI_SUCCESS;
1429 }
1430
1431 if (LoadOption->OptionType == LoadOptionTypePlatformRecovery) {
1432 //
1433 // Signal the EVT_SIGNAL_READY_TO_BOOT event when we are about to load and execute the boot option.
1434 //
1435 EfiSignalEventReadyToBoot ();
1436 //
1437 // Report Status Code to indicate ReadyToBoot was signaled
1438 //
1439 REPORT_STATUS_CODE (EFI_PROGRESS_CODE, (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_PC_READY_TO_BOOT_EVENT));
1440 }
1441
1442 //
1443 // Load and start the load option.
1444 //
1445 DEBUG ((
1446 DEBUG_INFO | DEBUG_LOAD,
1447 "Process %s%04x (%s) ...\n",
1448 mBmLoadOptionName[LoadOption->OptionType],
1449 LoadOption->OptionNumber,
1450 LoadOption->Description
1451 ));
1452 ImageHandle = NULL;
1453 CurFullPath = NULL;
1454 EfiBootManagerConnectDevicePath (LoadOption->FilePath, NULL);
1455
1456 //
1457 // while() loop is to keep starting next matched load option if the PlatformRecovery#### returns failure status.
1458 //
1459 while (TRUE) {
1460 Status = EFI_INVALID_PARAMETER;
1461 PreFullPath = CurFullPath;
1462 FileBuffer = BmGetNextLoadOptionBuffer (LoadOption->OptionType, LoadOption->FilePath, &CurFullPath, &FileSize);
1463 if (PreFullPath != NULL) {
1464 FreePool (PreFullPath);
1465 }
1466
1467 if (FileBuffer == NULL) {
1468 break;
1469 }
1470
1471 Status = gBS->LoadImage (
1472 FALSE,
1473 gImageHandle,
1474 CurFullPath,
1475 FileBuffer,
1476 FileSize,
1477 &ImageHandle
1478 );
1479 FreePool (FileBuffer);
1480
1481 if (EFI_ERROR (Status)) {
1482 //
1483 // With EFI_SECURITY_VIOLATION retval, the Image was loaded and an ImageHandle was created
1484 // with a valid EFI_LOADED_IMAGE_PROTOCOL, but the image can not be started right now.
1485 // If the caller doesn't have the option to defer the execution of an image, we should
1486 // unload image for the EFI_SECURITY_VIOLATION to avoid resource leak.
1487 //
1488 if (Status == EFI_SECURITY_VIOLATION) {
1489 gBS->UnloadImage (ImageHandle);
1490 }
1491 } else {
1492 Status = gBS->HandleProtocol (ImageHandle, &gEfiLoadedImageProtocolGuid, (VOID **)&ImageInfo);
1493 ASSERT_EFI_ERROR (Status);
1494
1495 ImageInfo->LoadOptionsSize = LoadOption->OptionalDataSize;
1496 ImageInfo->LoadOptions = LoadOption->OptionalData;
1497 //
1498 // Before calling the image, enable the Watchdog Timer for the 5-minute period
1499 //
1500 gBS->SetWatchdogTimer (5 * 60, 0, 0, NULL);
1501
1502 LoadOption->Status = gBS->StartImage (ImageHandle, &LoadOption->ExitDataSize, &LoadOption->ExitData);
1503 DEBUG ((
1504 DEBUG_INFO | DEBUG_LOAD,
1505 "%s%04x Return Status = %r\n",
1506 mBmLoadOptionName[LoadOption->OptionType],
1507 LoadOption->OptionNumber,
1508 LoadOption->Status
1509 ));
1510
1511 //
1512 // Clear the Watchdog Timer after the image returns
1513 //
1514 gBS->SetWatchdogTimer (0, 0, 0, NULL);
1515
1516 if ((LoadOption->OptionType != LoadOptionTypePlatformRecovery) || (LoadOption->Status == EFI_SUCCESS)) {
1517 break;
1518 }
1519 }
1520 }
1521
1522 if (CurFullPath != NULL) {
1523 FreePool (CurFullPath);
1524 }
1525
1526 return Status;
1527}
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette