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 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
BmIsValidLoadOptionVariableName(IN CHAR16 * VariableName,OUT EFI_BOOT_MANAGER_LOAD_OPTION_TYPE * OptionType,OUT UINT16 * OptionNumber)778 BmIsValidLoadOptionVariableName (
779   IN CHAR16                             *VariableName,
780   OUT EFI_BOOT_MANAGER_LOAD_OPTION_TYPE *OptionType,
781   OUT UINT16                            *OptionNumber
782   )
783 {
784   UINTN                             VariableNameLen;
785   UINTN                             Index;
786   UINTN                             Uint;
787 
788   VariableNameLen = StrLen (VariableName);
789 
790   if (VariableNameLen <= 4) {
791     return FALSE;
792   }
793 
794   for (Index = 0; Index < sizeof (mBmLoadOptionName) / sizeof (mBmLoadOptionName[0]); Index++) {
795     if ((VariableNameLen - 4 == StrLen (mBmLoadOptionName[Index])) &&
796         (StrnCmp (VariableName, mBmLoadOptionName[Index], VariableNameLen - 4) == 0)
797         ) {
798       break;
799     }
800   }
801 
802   if (Index == sizeof (mBmLoadOptionName) / sizeof (mBmLoadOptionName[0])) {
803     return FALSE;
804   }
805 
806   *OptionType = (EFI_BOOT_MANAGER_LOAD_OPTION_TYPE) Index;
807   *OptionNumber = 0;
808   for (Index = VariableNameLen - 4; Index < VariableNameLen; Index++) {
809     Uint = BmCharToUint (VariableName[Index]);
810     if (Uint == -1) {
811       break;
812     } else {
813       *OptionNumber = (UINT16) Uint + *OptionNumber * 0x10;
814     }
815   }
816 
817   return (BOOLEAN) (Index == VariableNameLen);
818 }
819 
820 /**
821   Build the Boot#### or Driver#### option from the VariableName.
822 
823   @param  VariableName          Variable name of the load option
824   @param  VendorGuid            Variable GUID of the load option
825   @param  Option                Return the load option.
826 
827   @retval EFI_SUCCESS     Get the option just been created
828   @retval EFI_NOT_FOUND   Failed to get the new option
829 
830 **/
831 EFI_STATUS
832 EFIAPI
EfiBootManagerVariableToLoadOptionEx(IN CHAR16 * VariableName,IN EFI_GUID * VendorGuid,IN OUT EFI_BOOT_MANAGER_LOAD_OPTION * Option)833 EfiBootManagerVariableToLoadOptionEx (
834   IN CHAR16                           *VariableName,
835   IN EFI_GUID                         *VendorGuid,
836   IN OUT EFI_BOOT_MANAGER_LOAD_OPTION *Option
837   )
838 {
839   EFI_STATUS                         Status;
840   UINT32                             Attribute;
841   UINT16                             FilePathSize;
842   UINT8                              *Variable;
843   UINT8                              *VariablePtr;
844   UINTN                              VariableSize;
845   EFI_DEVICE_PATH_PROTOCOL           *FilePath;
846   UINT8                              *OptionalData;
847   UINT32                             OptionalDataSize;
848   CHAR16                             *Description;
849   EFI_BOOT_MANAGER_LOAD_OPTION_TYPE  OptionType;
850   UINT16                             OptionNumber;
851 
852   if ((VariableName == NULL) || (Option == NULL)) {
853     return EFI_INVALID_PARAMETER;
854   }
855 
856   if (!BmIsValidLoadOptionVariableName (VariableName, &OptionType, &OptionNumber)) {
857     return EFI_INVALID_PARAMETER;
858   }
859 
860   //
861   // Read the variable
862   //
863   GetVariable2 (VariableName, VendorGuid, (VOID **) &Variable, &VariableSize);
864   if (Variable == NULL) {
865     return EFI_NOT_FOUND;
866   }
867 
868   //
869   // Validate *#### variable data.
870   //
871   if (!BmValidateOption(Variable, VariableSize)) {
872     FreePool (Variable);
873     return EFI_INVALID_PARAMETER;
874   }
875 
876   //
877   // Get the option attribute
878   //
879   VariablePtr = Variable;
880   Attribute = ReadUnaligned32 ((UINT32 *) VariablePtr);
881   VariablePtr += sizeof (UINT32);
882 
883   //
884   // Get the option's device path size
885   //
886   FilePathSize = ReadUnaligned16 ((UINT16 *) VariablePtr);
887   VariablePtr += sizeof (UINT16);
888 
889   //
890   // Get the option's description string
891   //
892   Description = (CHAR16 *) VariablePtr;
893 
894   //
895   // Get the option's description string size
896   //
897   VariablePtr += StrSize ((CHAR16 *) VariablePtr);
898 
899   //
900   // Get the option's device path
901   //
902   FilePath = (EFI_DEVICE_PATH_PROTOCOL *) VariablePtr;
903   VariablePtr += FilePathSize;
904 
905   OptionalDataSize = (UINT32) (VariableSize - (UINTN) (VariablePtr - Variable));
906   if (OptionalDataSize == 0) {
907     OptionalData = NULL;
908   } else {
909     OptionalData = VariablePtr;
910   }
911 
912   Status = EfiBootManagerInitializeLoadOption (
913              Option,
914              OptionNumber,
915              OptionType,
916              Attribute,
917              Description,
918              FilePath,
919              OptionalData,
920              OptionalDataSize
921              );
922   ASSERT_EFI_ERROR (Status);
923 
924   CopyGuid (&Option->VendorGuid, VendorGuid);
925 
926   FreePool (Variable);
927   return Status;
928 }
929 
930 /**
931 Build the Boot#### or Driver#### option from the VariableName.
932 
933 @param  VariableName          EFI Variable name indicate if it is Boot#### or Driver####
934 @param  Option                Return the Boot#### or Driver#### option.
935 
936 @retval EFI_SUCCESS     Get the option just been created
937 @retval EFI_NOT_FOUND   Failed to get the new option
938 **/
939 EFI_STATUS
940 EFIAPI
EfiBootManagerVariableToLoadOption(IN CHAR16 * VariableName,IN OUT EFI_BOOT_MANAGER_LOAD_OPTION * Option)941 EfiBootManagerVariableToLoadOption (
942   IN  CHAR16                          *VariableName,
943   IN OUT EFI_BOOT_MANAGER_LOAD_OPTION *Option
944   )
945 {
946   return EfiBootManagerVariableToLoadOptionEx (VariableName, &gEfiGlobalVariableGuid, Option);
947 }
948 
949 typedef struct {
950   EFI_BOOT_MANAGER_LOAD_OPTION_TYPE OptionType;
951   EFI_GUID                          *Guid;
952   EFI_BOOT_MANAGER_LOAD_OPTION      *Options;
953   UINTN                             OptionCount;
954 } BM_COLLECT_LOAD_OPTIONS_PARAM;
955 
956 /**
957   Visitor function to collect the Platform Recovery load options or OS Recovery
958   load options from NV storage.
959 
960   @param Name    Variable name.
961   @param Guid    Variable GUID.
962   @param Context The same context passed to BmForEachVariable.
963 **/
964 VOID
BmCollectLoadOptions(IN CHAR16 * Name,IN EFI_GUID * Guid,IN VOID * Context)965 BmCollectLoadOptions (
966   IN CHAR16               *Name,
967   IN EFI_GUID             *Guid,
968   IN VOID                 *Context
969   )
970 {
971   EFI_STATUS                        Status;
972   EFI_BOOT_MANAGER_LOAD_OPTION_TYPE OptionType;
973   UINT16                            OptionNumber;
974   EFI_BOOT_MANAGER_LOAD_OPTION      Option;
975   UINTN                             Index;
976   BM_COLLECT_LOAD_OPTIONS_PARAM     *Param;
977 
978   Param = (BM_COLLECT_LOAD_OPTIONS_PARAM *) Context;
979 
980   if (CompareGuid (Guid, Param->Guid) && (
981       Param->OptionType == LoadOptionTypePlatformRecovery &&
982       BmIsValidLoadOptionVariableName (Name, &OptionType, &OptionNumber) &&
983       OptionType == LoadOptionTypePlatformRecovery
984      )) {
985     Status = EfiBootManagerVariableToLoadOptionEx (Name, Guid, &Option);
986     if (!EFI_ERROR (Status)) {
987       for (Index = 0; Index < Param->OptionCount; Index++) {
988         if (Param->Options[Index].OptionNumber > Option.OptionNumber) {
989           break;
990         }
991       }
992       Param->Options = ReallocatePool (
993                          Param->OptionCount * sizeof (EFI_BOOT_MANAGER_LOAD_OPTION),
994                          (Param->OptionCount + 1) * sizeof (EFI_BOOT_MANAGER_LOAD_OPTION),
995                          Param->Options
996                          );
997       ASSERT (Param->Options != NULL);
998       CopyMem (&Param->Options[Index + 1], &Param->Options[Index], (Param->OptionCount - Index) * sizeof (EFI_BOOT_MANAGER_LOAD_OPTION));
999       CopyMem (&Param->Options[Index], &Option, sizeof (EFI_BOOT_MANAGER_LOAD_OPTION));
1000       Param->OptionCount++;
1001     }
1002   }
1003 }
1004 
1005 /**
1006   Returns an array of load options based on the EFI variable
1007   L"BootOrder"/L"DriverOrder" and the L"Boot####"/L"Driver####" variables impled by it.
1008   #### is the hex value of the UINT16 in each BootOrder/DriverOrder entry.
1009 
1010   @param  LoadOptionCount   Returns number of entries in the array.
1011   @param  LoadOptionType    The type of the load option.
1012 
1013   @retval NULL  No load options exist.
1014   @retval !NULL Array of load option entries.
1015 
1016 **/
1017 EFI_BOOT_MANAGER_LOAD_OPTION *
1018 EFIAPI
EfiBootManagerGetLoadOptions(OUT UINTN * OptionCount,IN EFI_BOOT_MANAGER_LOAD_OPTION_TYPE LoadOptionType)1019 EfiBootManagerGetLoadOptions (
1020   OUT UINTN                             *OptionCount,
1021   IN EFI_BOOT_MANAGER_LOAD_OPTION_TYPE  LoadOptionType
1022   )
1023 {
1024   EFI_STATUS                    Status;
1025   UINT16                        *OptionOrder;
1026   UINTN                         OptionOrderSize;
1027   UINTN                         Index;
1028   UINTN                         OptionIndex;
1029   EFI_BOOT_MANAGER_LOAD_OPTION  *Options;
1030   CHAR16                        OptionName[BM_OPTION_NAME_LEN];
1031   UINT16                        OptionNumber;
1032   BM_COLLECT_LOAD_OPTIONS_PARAM Param;
1033 
1034   *OptionCount = 0;
1035   Options      = NULL;
1036 
1037   if (LoadOptionType == LoadOptionTypeDriver || LoadOptionType == LoadOptionTypeSysPrep || LoadOptionType == LoadOptionTypeBoot) {
1038     //
1039     // Read the BootOrder, or DriverOrder variable.
1040     //
1041     GetEfiGlobalVariable2 (mBmLoadOptionOrderName[LoadOptionType], (VOID **) &OptionOrder, &OptionOrderSize);
1042     if (OptionOrder == NULL) {
1043       return NULL;
1044     }
1045 
1046     *OptionCount = OptionOrderSize / sizeof (UINT16);
1047 
1048     Options = AllocatePool (*OptionCount * sizeof (EFI_BOOT_MANAGER_LOAD_OPTION));
1049     ASSERT (Options != NULL);
1050 
1051     OptionIndex = 0;
1052     for (Index = 0; Index < *OptionCount; Index++) {
1053       OptionNumber = OptionOrder[Index];
1054       UnicodeSPrint (OptionName, sizeof (OptionName), L"%s%04x", mBmLoadOptionName[LoadOptionType], OptionNumber);
1055 
1056       Status = EfiBootManagerVariableToLoadOption (OptionName, &Options[OptionIndex]);
1057       if (EFI_ERROR (Status)) {
1058         DEBUG ((EFI_D_INFO, "[Bds] %s doesn't exist - Update ****Order variable to remove the reference!!", OptionName));
1059         EfiBootManagerDeleteLoadOptionVariable (OptionNumber, LoadOptionType);
1060       } else {
1061         ASSERT (Options[OptionIndex].OptionNumber == OptionNumber);
1062         OptionIndex++;
1063       }
1064     }
1065 
1066     if (OptionOrder != NULL) {
1067       FreePool (OptionOrder);
1068     }
1069 
1070     if (OptionIndex < *OptionCount) {
1071       Options = ReallocatePool (*OptionCount * sizeof (EFI_BOOT_MANAGER_LOAD_OPTION), OptionIndex * sizeof (EFI_BOOT_MANAGER_LOAD_OPTION), Options);
1072       ASSERT (Options != NULL);
1073       *OptionCount = OptionIndex;
1074     }
1075 
1076   } else if (LoadOptionType == LoadOptionTypePlatformRecovery) {
1077     Param.OptionType = LoadOptionTypePlatformRecovery;
1078     Param.Options = NULL;
1079     Param.OptionCount = 0;
1080     Param.Guid = &gEfiGlobalVariableGuid;
1081 
1082     BmForEachVariable (BmCollectLoadOptions, (VOID *) &Param);
1083 
1084     *OptionCount = Param.OptionCount;
1085     Options = Param.Options;
1086   }
1087 
1088   return Options;
1089 }
1090 
1091 /**
1092   Free an EFI_BOOT_MANGER_LOAD_OPTION entry that was allocate by the library.
1093 
1094   @param  LoadOption   Pointer to boot option to Free.
1095 
1096   @return EFI_SUCCESS   BootOption was freed
1097   @return EFI_NOT_FOUND BootOption == NULL
1098 
1099 **/
1100 EFI_STATUS
1101 EFIAPI
EfiBootManagerFreeLoadOption(IN EFI_BOOT_MANAGER_LOAD_OPTION * LoadOption)1102 EfiBootManagerFreeLoadOption (
1103   IN  EFI_BOOT_MANAGER_LOAD_OPTION  *LoadOption
1104   )
1105 {
1106   if (LoadOption == NULL) {
1107     return EFI_NOT_FOUND;
1108   }
1109 
1110   if (LoadOption->Description != NULL) {
1111     FreePool (LoadOption->Description);
1112   }
1113   if (LoadOption->FilePath != NULL) {
1114     FreePool (LoadOption->FilePath);
1115   }
1116   if (LoadOption->OptionalData != NULL) {
1117     FreePool (LoadOption->OptionalData);
1118   }
1119 
1120   return EFI_SUCCESS;
1121 }
1122 
1123 /**
1124   Free an EFI_BOOT_MANGER_LOAD_OPTION array that was allocated by
1125   EfiBootManagerGetLoadOptions().
1126 
1127   @param  Option       Pointer to boot option array to free.
1128   @param  OptionCount  Number of array entries in BootOption
1129 
1130   @return EFI_SUCCESS   BootOption was freed
1131   @return EFI_NOT_FOUND BootOption == NULL
1132 
1133 **/
1134 EFI_STATUS
1135 EFIAPI
EfiBootManagerFreeLoadOptions(IN EFI_BOOT_MANAGER_LOAD_OPTION * Option,IN UINTN OptionCount)1136 EfiBootManagerFreeLoadOptions (
1137   IN  EFI_BOOT_MANAGER_LOAD_OPTION  *Option,
1138   IN  UINTN                         OptionCount
1139   )
1140 {
1141   UINTN   Index;
1142 
1143   if (Option == NULL) {
1144     return EFI_NOT_FOUND;
1145   }
1146 
1147   for (Index = 0;Index < OptionCount; Index++) {
1148     EfiBootManagerFreeLoadOption (&Option[Index]);
1149   }
1150 
1151   FreePool (Option);
1152 
1153   return EFI_SUCCESS;
1154 }
1155 
1156 /**
1157   Return whether the PE header of the load option is valid or not.
1158 
1159   @param[in] Type       The load option type.
1160   @param[in] FileBuffer The PE file buffer of the load option.
1161   @param[in] FileSize   The size of the load option file.
1162 
1163   @retval TRUE  The PE header of the load option is valid.
1164   @retval FALSE The PE header of the load option is not valid.
1165 **/
1166 BOOLEAN
BmIsLoadOptionPeHeaderValid(IN EFI_BOOT_MANAGER_LOAD_OPTION_TYPE Type,IN VOID * FileBuffer,IN UINTN FileSize)1167 BmIsLoadOptionPeHeaderValid (
1168   IN EFI_BOOT_MANAGER_LOAD_OPTION_TYPE Type,
1169   IN VOID                              *FileBuffer,
1170   IN UINTN                             FileSize
1171   )
1172 {
1173   EFI_IMAGE_DOS_HEADER              *DosHeader;
1174   EFI_IMAGE_OPTIONAL_HEADER_UNION   *PeHeader;
1175   EFI_IMAGE_OPTIONAL_HEADER32       *OptionalHeader;
1176   UINT16                            Subsystem;
1177 
1178   if (FileBuffer == NULL || FileSize == 0) {
1179     return FALSE;
1180   }
1181 
1182   //
1183   // Read dos header
1184   //
1185   DosHeader = (EFI_IMAGE_DOS_HEADER *) FileBuffer;
1186   if (FileSize >= sizeof (EFI_IMAGE_DOS_HEADER) &&
1187       FileSize > DosHeader->e_lfanew && DosHeader->e_magic == EFI_IMAGE_DOS_SIGNATURE
1188       ) {
1189     //
1190     // Read and check PE signature
1191     //
1192     PeHeader = (EFI_IMAGE_OPTIONAL_HEADER_UNION *) ((UINT8 *) FileBuffer + DosHeader->e_lfanew);
1193     if (FileSize >= DosHeader->e_lfanew + sizeof (EFI_IMAGE_OPTIONAL_HEADER_UNION) &&
1194         PeHeader->Pe32.Signature == EFI_IMAGE_NT_SIGNATURE
1195         ) {
1196       //
1197       // Check PE32 or PE32+ magic, and machine type
1198       //
1199       OptionalHeader = (EFI_IMAGE_OPTIONAL_HEADER32 *) &PeHeader->Pe32.OptionalHeader;
1200       if ((OptionalHeader->Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC ||
1201            OptionalHeader->Magic == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC) &&
1202           EFI_IMAGE_MACHINE_TYPE_SUPPORTED (PeHeader->Pe32.FileHeader.Machine)
1203           ) {
1204         //
1205         // Check the Subsystem:
1206         //   Driver#### must be of type BootServiceDriver or RuntimeDriver
1207         //   SysPrep####, Boot####, OsRecovery####, PlatformRecovery#### must be of type Application
1208         //
1209         Subsystem = OptionalHeader->Subsystem;
1210         if ((Type == LoadOptionTypeDriver && Subsystem == EFI_IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER) ||
1211             (Type == LoadOptionTypeDriver && Subsystem == EFI_IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER) ||
1212             (Type == LoadOptionTypeSysPrep && Subsystem == EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION) ||
1213             (Type == LoadOptionTypeBoot && Subsystem == EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION) ||
1214             (Type == LoadOptionTypePlatformRecovery && Subsystem == EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION)
1215             ) {
1216           return TRUE;
1217         }
1218       }
1219     }
1220   }
1221 
1222   return FALSE;
1223 }
1224 
1225 /**
1226   Process (load and execute) the load option.
1227 
1228   @param LoadOption  Pointer to the load option.
1229 
1230   @retval EFI_INVALID_PARAMETER  The load option type is invalid,
1231                                  or the load option file path doesn't point to a valid file.
1232   @retval EFI_UNSUPPORTED        The load option type is of LoadOptionTypeBoot.
1233   @retval EFI_SUCCESS            The load option is inactive, or successfully loaded and executed.
1234 **/
1235 EFI_STATUS
1236 EFIAPI
EfiBootManagerProcessLoadOption(IN EFI_BOOT_MANAGER_LOAD_OPTION * LoadOption)1237 EfiBootManagerProcessLoadOption (
1238   IN EFI_BOOT_MANAGER_LOAD_OPTION       *LoadOption
1239   )
1240 {
1241   EFI_STATUS                        Status;
1242   EFI_DEVICE_PATH_PROTOCOL          *FilePath;
1243   EFI_HANDLE                        ImageHandle;
1244   EFI_LOADED_IMAGE_PROTOCOL         *ImageInfo;
1245   VOID                              *FileBuffer;
1246   UINTN                             FileSize;
1247 
1248   if ((UINT32) LoadOption->OptionType >= LoadOptionTypeMax) {
1249     return EFI_INVALID_PARAMETER;
1250   }
1251 
1252   if (LoadOption->OptionType == LoadOptionTypeBoot) {
1253     return EFI_UNSUPPORTED;
1254   }
1255 
1256   //
1257   // If a load option is not marked as LOAD_OPTION_ACTIVE,
1258   // the boot manager will not automatically load the option.
1259   //
1260   if ((LoadOption->Attributes & LOAD_OPTION_ACTIVE) == 0) {
1261     return EFI_SUCCESS;
1262   }
1263 
1264   Status = EFI_INVALID_PARAMETER;
1265 
1266   //
1267   // Load and start the load option.
1268   //
1269   DEBUG ((
1270     DEBUG_INFO | DEBUG_LOAD, "Process Load Option (%s%04x) ...\n",
1271     mBmLoadOptionName[LoadOption->OptionType], LoadOption->OptionNumber
1272     ));
1273   ImageHandle = NULL;
1274   FileBuffer = BmGetLoadOptionBuffer (LoadOption->FilePath, &FilePath, &FileSize);
1275   DEBUG_CODE (
1276     if (FileBuffer != NULL && CompareMem (LoadOption->FilePath, FilePath, GetDevicePathSize (FilePath)) != 0) {
1277       DEBUG ((EFI_D_INFO, "[Bds] DevicePath expand: "));
1278       BmPrintDp (LoadOption->FilePath);
1279       DEBUG ((EFI_D_INFO, " -> "));
1280       BmPrintDp (FilePath);
1281       DEBUG ((EFI_D_INFO, "\n"));
1282     }
1283   );
1284   if (BmIsLoadOptionPeHeaderValid (LoadOption->OptionType, FileBuffer, FileSize)) {
1285     Status = gBS->LoadImage (
1286                     FALSE,
1287                     gImageHandle,
1288                     FilePath,
1289                     FileBuffer,
1290                     FileSize,
1291                     &ImageHandle
1292                     );
1293   }
1294   if (FilePath != NULL) {
1295     FreePool (FilePath);
1296   }
1297   if (FileBuffer != NULL) {
1298     FreePool (FileBuffer);
1299   }
1300 
1301   if (!EFI_ERROR (Status)) {
1302     Status = gBS->HandleProtocol (ImageHandle, &gEfiLoadedImageProtocolGuid, (VOID **) &ImageInfo);
1303     ASSERT_EFI_ERROR (Status);
1304 
1305     ImageInfo->LoadOptionsSize = LoadOption->OptionalDataSize;
1306     ImageInfo->LoadOptions = LoadOption->OptionalData;
1307     //
1308     // Before calling the image, enable the Watchdog Timer for the 5-minute period
1309     //
1310     gBS->SetWatchdogTimer (5 * 60, 0, 0, NULL);
1311 
1312     LoadOption->Status = gBS->StartImage (ImageHandle, &LoadOption->ExitDataSize, &LoadOption->ExitData);
1313     DEBUG ((
1314       DEBUG_INFO | DEBUG_LOAD, "Load Option (%s%04x) Return Status = %r\n",
1315       mBmLoadOptionName[LoadOption->OptionType], LoadOption->OptionNumber, LoadOption->Status
1316       ));
1317 
1318     //
1319     // Clear the Watchdog Timer after the image returns
1320     //
1321     gBS->SetWatchdogTimer (0, 0, 0, NULL);
1322   }
1323 
1324   return Status;
1325 }
1326