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