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