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