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