[PRINTING]
[reactos.git] / reactos / boot / environ / app / bootmgr / bootmgr.c
1 /*
2 * COPYRIGHT: See COPYING.ARM in the top level directory
3 * PROJECT: ReactOS UEFI Boot Manager
4 * FILE: boot/environ/app/bootmgr/bootmgr.cla
5 * PURPOSE: Boot Manager Entrypoint
6 * PROGRAMMER: Alex Ionescu (alex.ionescu@reactos.org)
7 */
8
9 /* INCLUDES ******************************************************************/
10
11 #include "bootmgr.h"
12
13 /* DATA VARIABLES ************************************************************/
14
15 DEFINE_GUID(GUID_WINDOWS_BOOTMGR,
16 0x9DEA862C,
17 0x5CDD,
18 0x4E70,
19 0xAC, 0xC1, 0xF3, 0x2B, 0x34, 0x4D, 0x47, 0x95);
20
21 ULONGLONG ApplicationStartTime;
22 ULONGLONG PostTime;
23 GUID BmApplicationIdentifier;
24 PWCHAR BootDirectory;
25
26 BL_BOOT_ERROR BmpErrorBuffer;
27 PBL_BOOT_ERROR BmpInternalBootError;
28 BL_PACKED_BOOT_ERROR BmpPackedBootError;
29
30 BOOLEAN BmBootIniUsed;
31 WCHAR BmpFileNameBuffer[128];
32 PWCHAR ParentFileName = L"";
33
34 BOOLEAN BmDisplayStateCached;
35 PBL_LOADED_APPLICATION_ENTRY* BmpFailedBootEntries;
36 PBL_LOADED_APPLICATION_ENTRY BmpSelectedBootEntry;
37 BOOLEAN BmBootEntryOverridePresent;
38 BOOLEAN BmpDisplayBootMenu;
39
40 /* FUNCTIONS *****************************************************************/
41
42 NTSTATUS
43 BmGetOptionList (
44 _In_ HANDLE BcdHandle,
45 _In_ PGUID ObjectId,
46 _In_ PBL_BCD_OPTION *OptionList
47 )
48 {
49 NTSTATUS Status;
50 HANDLE ObjectHandle;
51 ULONG ElementSize, ElementCount, i, OptionsSize;
52 BcdElementType Type;
53 PBCD_ELEMENT_HEADER Header;
54 PBCD_ELEMENT BcdElements;
55 PBL_BCD_OPTION Options, Option, PreviousOption, DeviceOptions;
56 PBCD_DEVICE_OPTION DeviceOption;
57 GUID DeviceId;
58 PVOID DeviceData;
59
60 /* Open the BCD object requested */
61 ObjectHandle = NULL;
62 BcdElements = NULL;
63 Status = BcdOpenObject(BcdHandle, ObjectId, &ObjectHandle);
64 if (!NT_SUCCESS(Status))
65 {
66 goto Quickie;
67 }
68
69 /* Do the initial enumeration to get the size needed */
70 ElementSize = 0;
71 Status = BcdEnumerateAndUnpackElements(BcdHandle,
72 ObjectHandle,
73 NULL,
74 &ElementSize,
75 &ElementCount);
76 if (Status != STATUS_BUFFER_TOO_SMALL)
77 {
78 /* If we got success, that doesn't make any sense */
79 if (NT_SUCCESS(Status))
80 {
81 Status = STATUS_INVALID_PARAMETER;
82 }
83
84 /* Bail out */
85 goto Quickie;
86 }
87
88 /* Allocate a large-enough buffer */
89 BcdElements = BlMmAllocateHeap(ElementSize);
90 if (!BcdElements)
91 {
92 Status = STATUS_NO_MEMORY;
93 goto Quickie;
94 }
95
96 /* Now do the real enumeration to fill out the elements buffer */
97 Status = BcdEnumerateAndUnpackElements(BcdHandle,
98 ObjectHandle,
99 BcdElements,
100 &ElementSize,
101 &ElementCount);
102 if (!NT_SUCCESS(Status))
103 {
104 goto Quickie;
105 }
106
107 /* Go through each BCD option to add the sizes up */
108 OptionsSize = 0;
109 for (i = 0; i < ElementCount; i++)
110 {
111 OptionsSize += BcdElements[i].Header->Size + sizeof(BL_BCD_OPTION);
112 }
113
114 /* Allocate the required BCD option list */
115 Options = BlMmAllocateHeap(OptionsSize);
116 if (!Options)
117 {
118 Status = STATUS_NO_MEMORY;
119 goto Quickie;
120 }
121
122 /* Zero it out */
123 RtlZeroMemory(Options, OptionsSize);
124
125 /* Start going through each option */
126 PreviousOption = NULL;
127 Option = Options;
128 for (i = 0; i < ElementCount; i++)
129 {
130 /* Read the header and type */
131 Header = BcdElements[i].Header;
132 Type.PackedValue = Header->Type;
133
134 /* Check if this option isn't already present */
135 if (!MiscGetBootOption(Options, Type.PackedValue))
136 {
137 /* It's a new option. Did we have an existing one? */
138 if (PreviousOption)
139 {
140 /* Link it to this new one */
141 PreviousOption->NextEntryOffset = (ULONG_PTR)Option -
142 (ULONG_PTR)Options;
143 }
144
145 /* Capture the type, size, data, and offset */
146 Option->Type = Type.PackedValue;
147 Option->DataSize = Header->Size;
148 RtlCopyMemory(Option + 1, BcdElements[i].Body, Header->Size);
149 Option->DataOffset = sizeof(BL_BCD_OPTION);
150
151 /* Check if this was a device */
152 if (Type.Format == BCD_TYPE_DEVICE)
153 {
154 /* Grab its GUID */
155 DeviceOption = (PBCD_DEVICE_OPTION)(Option + 1);
156 DeviceId = DeviceOption->AssociatedEntry;
157
158 /* Look up the options for that GUID */
159 Status = BmGetOptionList(BcdHandle, &DeviceId, &DeviceOptions);
160 if (NT_SUCCESS(Status))
161 {
162 /* Device data is after the device option */
163 DeviceData = (PVOID)((ULONG_PTR)DeviceOption + Header->Size);
164
165 /* Copy it */
166 RtlCopyMemory(DeviceData,
167 DeviceOptions,
168 BlGetBootOptionListSize(DeviceOptions));
169
170 /* Don't need this anymore */
171 BlMmFreeHeap(DeviceOptions);
172
173 /* Write the offset of the device options */
174 Option->ListOffset = (ULONG_PTR)DeviceData -
175 (ULONG_PTR)Option;
176 }
177 }
178
179 /* Save the previous option and go to the next one */
180 PreviousOption = Option;
181 Option = (PBL_BCD_OPTION)((ULONG_PTR)Option +
182 BlGetBootOptionSize(Option));
183 }
184 }
185
186 /* Return the pointer back, we've made it! */
187 *OptionList = Options;
188 Status = STATUS_SUCCESS;
189
190 Quickie:
191 /* Did we allocate a local buffer? Free it if so */
192 if (BcdElements)
193 {
194 BlMmFreeHeap(BcdElements);
195 }
196
197 /* Was the key open? Close it if so */
198 if (ObjectHandle)
199 {
200 BiCloseKey(ObjectHandle);
201 }
202
203 /* Return the option list parsing status */
204 return Status;
205 }
206
207 NTSTATUS
208 BmpUpdateApplicationOptions (
209 _In_ HANDLE BcdHandle
210 )
211 {
212 NTSTATUS Status;
213 PBL_BCD_OPTION Options;
214
215 /* Get the boot option list */
216 Status = BmGetOptionList(BcdHandle, &BmApplicationIdentifier, &Options);
217 if (!NT_SUCCESS(Status))
218 {
219 return Status;
220 }
221
222 /* Append the options, free the local buffer, and return success */
223 BlAppendBootOptions(&BlpApplicationEntry, Options);
224 BlMmFreeHeap(Options);
225 return STATUS_SUCCESS;
226 }
227
228 NTSTATUS
229 BmpFwGetApplicationDirectoryPath (
230 _In_ PUNICODE_STRING ApplicationDirectoryPath
231 )
232 {
233 NTSTATUS Status;
234 ULONG i, AppPathLength;
235 PWCHAR ApplicationPath, PathCopy;
236
237 /* Clear the incoming string */
238 ApplicationDirectoryPath->Length = 0;
239 ApplicationDirectoryPath->MaximumLength = 0;
240 ApplicationDirectoryPath->Buffer = 0;
241
242 /* Get the boot application path */
243 ApplicationPath = NULL;
244 Status = BlGetBootOptionString(BlpApplicationEntry.BcdData,
245 BcdLibraryString_ApplicationPath,
246 &ApplicationPath);
247 if (NT_SUCCESS(Status))
248 {
249 /* Calculate the length of the application path */
250 for (i = wcslen(ApplicationPath) - 1; i > 0; i--)
251 {
252 /* Keep going until the path separator */
253 if (ApplicationPath[i] == OBJ_NAME_PATH_SEPARATOR)
254 {
255 break;
256 }
257 }
258
259 /* Check if we have space for one more character */
260 Status = RtlULongAdd(i, 1, &AppPathLength);
261 if (NT_SUCCESS(Status))
262 {
263 /* Check if it's safe to multiply by two */
264 Status = RtlULongMult(AppPathLength, sizeof(WCHAR), &AppPathLength);
265 if (NT_SUCCESS(Status))
266 {
267 /* Allocate a copy for the string */
268 PathCopy = BlMmAllocateHeap(AppPathLength);
269 if (PathCopy)
270 {
271 /* NULL-terminate it */
272 RtlCopyMemory(PathCopy,
273 ApplicationPath,
274 AppPathLength - sizeof(UNICODE_NULL));
275 PathCopy[AppPathLength] = UNICODE_NULL;
276
277 /* Finally, initialize the outoing string */
278 RtlInitUnicodeString(ApplicationDirectoryPath, PathCopy);
279 }
280 else
281 {
282 /* No memory, fail */
283 Status = STATUS_NO_MEMORY;
284 }
285 }
286 }
287 }
288
289 /* Check if we had an application path */
290 if (ApplicationPath)
291 {
292 /* No longer need this, free it */
293 BlMmFreeHeap(ApplicationPath);
294 }
295
296 /* All done! */
297 return Status;
298 }
299
300 NTSTATUS
301 BmFwInitializeBootDirectoryPath (
302 VOID
303 )
304 {
305 PWCHAR FinalPath;
306 NTSTATUS Status;
307 PWCHAR BcdDirectory;
308 UNICODE_STRING BcdPath;
309 ULONG FinalSize;
310 ULONG FileHandle, DeviceHandle;
311
312 /* Initialize everything for failure */
313 BcdPath.MaximumLength = 0;
314 BcdPath.Buffer = NULL;
315 BcdDirectory = NULL;
316 FinalPath = NULL;
317 FileHandle = -1;
318 DeviceHandle = -1;
319
320 /* Try to open the boot device */
321 Status = BlpDeviceOpen(BlpBootDevice,
322 BL_DEVICE_READ_ACCESS,
323 0,
324 &DeviceHandle);
325 if (!NT_SUCCESS(Status))
326 {
327 EfiPrintf(L"Device open failed: %lx\r\n", Status);
328 goto Quickie;
329 }
330
331 /* Get the directory path */
332 Status = BmpFwGetApplicationDirectoryPath(&BcdPath);
333 BcdDirectory = BcdPath.Buffer;
334 if (!NT_SUCCESS(Status))
335 {
336 goto Quickie;
337 }
338
339 /* Add the BCD file name to it */
340 FinalSize = BcdPath.MaximumLength + sizeof(L"\\BCD") - sizeof(UNICODE_NULL);
341 if (FinalSize < BcdPath.MaximumLength)
342 {
343 goto Quickie;
344 }
345
346 /* Allocate space for the final path */
347 FinalPath = BlMmAllocateHeap(FinalSize);
348 if (!FinalPath)
349 {
350 goto Quickie;
351 }
352
353 /* Build it */
354 RtlZeroMemory(FinalPath, FinalSize);
355 RtlCopyMemory(FinalPath, BcdDirectory, BcdPath.MaximumLength);
356 wcsncat(FinalPath, L"\\BCD", FinalSize / sizeof(WCHAR));
357
358 /* Try to open the file */
359 Status = BlFileOpen(DeviceHandle,
360 FinalPath,
361 BL_FILE_READ_ACCESS,
362 &FileHandle);
363 if (!NT_SUCCESS(Status))
364 {
365 BootDirectory = BcdDirectory;
366 goto Quickie;
367 }
368
369 /* Save the boot directory */
370 BootDirectory = L"\\EFI\\Boot"; /* Should be EFI\\ReactOS\\Boot */
371
372 Quickie:
373 /* Free all the allocations we made */
374 if (BcdDirectory)
375 {
376 Status = BlMmFreeHeap(BcdDirectory);
377 }
378 if (FinalPath)
379 {
380 Status = BlMmFreeHeap(FinalPath);
381 }
382
383 /* Close the BCD file */
384 if (FileHandle != -1)
385 {
386 Status = BlFileClose(FileHandle);
387 }
388
389 /* Close the boot device */
390 if (DeviceHandle != -1)
391 {
392 Status = BlDeviceClose(DeviceHandle);
393 }
394
395 /* Return back to the caller */
396 return Status;
397 }
398
399 NTSTATUS
400 BmOpenBootIni (
401 VOID
402 )
403 {
404 /* Don't yet handled boot.ini */
405 return STATUS_NOT_FOUND;
406 }
407
408 ULONG
409 BmpFatalErrorMessageFilter (
410 _In_ NTSTATUS ErrorStatus,
411 _Out_ PULONG ErrorResourceId
412 )
413 {
414 ULONG Result;
415
416 /* Assume no message for now, check for known status message */
417 Result = 0;
418 switch (ErrorStatus)
419 {
420 /* Convert each status to a resource ID */
421 case STATUS_UNEXPECTED_IO_ERROR:
422 *ErrorResourceId = 9017;
423 Result = 1;
424 break;
425 case STATUS_IMAGE_CHECKSUM_MISMATCH:
426 *ErrorResourceId = 9018;
427 break;
428 case STATUS_INVALID_IMAGE_WIN_64:
429 *ErrorResourceId = 9016;
430 break;
431 case 0xC0000428:
432 *ErrorResourceId = 9019;
433 Result = 2;
434 break;
435 case 0xC0210000:
436 *ErrorResourceId = 9013;
437 break;
438 }
439
440 /* Return the type of message */
441 return Result;
442 }
443
444 VOID
445 BmErrorPurge (
446 VOID
447 )
448 {
449 /* Check if a boot error is present */
450 if (BmpPackedBootError.BootError)
451 {
452 /* Purge it */
453 BlMmFreeHeap(BmpPackedBootError.BootError);
454 BmpPackedBootError.BootError = NULL;
455 }
456
457 /* Zero out the packed buffer */
458 BmpPackedBootError.Size = 0;
459 BmpInternalBootError = NULL;
460 RtlZeroMemory(&BmpErrorBuffer, sizeof(BmpErrorBuffer));
461 }
462
463 VOID
464 BmpErrorLog (
465 _In_ ULONG ErrorCode,
466 _In_ NTSTATUS ErrorStatus,
467 _In_ ULONG ErrorMsgId,
468 _In_ PWCHAR FileName,
469 _In_ ULONG HelpMsgId
470 )
471 {
472 PWCHAR ErrorMsgString;
473
474 /* Check if we already had an error */
475 if (BmpInternalBootError)
476 {
477 /* Purge it */
478 BmErrorPurge();
479 }
480
481 /* Find the string for this error ID */
482 ErrorMsgString = BlResourceFindMessage(ErrorMsgId);
483 if (ErrorMsgString)
484 {
485 /* Fill out the error buffer */
486 BmpErrorBuffer.Unknown1 = 0;
487 BmpErrorBuffer.Unknown2 = 0;
488 BmpErrorBuffer.ErrorString = ErrorMsgString;
489 BmpErrorBuffer.FileName = FileName;
490 BmpErrorBuffer.ErrorCode = ErrorCode;
491 BmpErrorBuffer.ErrorStatus = ErrorStatus;
492 BmpErrorBuffer.HelpMsgId = HelpMsgId;
493 BmpInternalBootError = &BmpErrorBuffer;
494 }
495 }
496
497 VOID
498 BmFatalErrorEx (
499 _In_ ULONG ErrorCode,
500 _In_ ULONG_PTR Parameter1,
501 _In_ ULONG_PTR Parameter2,
502 _In_ ULONG_PTR Parameter3,
503 _In_ ULONG_PTR Parameter4
504 )
505 {
506 PWCHAR FileName, Buffer;
507 NTSTATUS ErrorStatus;
508 WCHAR FormatString[256];
509 ULONG ErrorResourceId, ErrorHelpId;
510 BOOLEAN Restart, NoError;
511
512 /* Assume no buffer for now */
513 Buffer = NULL;
514
515 /* Check what error code is being raised */
516 switch (ErrorCode)
517 {
518 /* Error reading the BCD */
519 case BL_FATAL_ERROR_BCD_READ:
520
521 /* Check if we have a name for the BCD file */
522 if (Parameter1)
523 {
524 /* Check if the name fits into our buffer */
525 FileName = (PWCHAR)Parameter1;
526 if (wcslen(FileName) < sizeof(BmpFileNameBuffer))
527 {
528 /* Copy it in there */
529 Buffer = BmpFileNameBuffer;
530 wcsncpy(BmpFileNameBuffer,
531 FileName,
532 RTL_NUMBER_OF(BmpFileNameBuffer));
533 }
534 }
535
536 /* If we don't have a buffer, use an empty one */
537 if (!Buffer)
538 {
539 Buffer = ParentFileName;
540 }
541
542 /* The NTSTATUS code is in parameter 2*/
543 ErrorStatus = (NTSTATUS)Parameter2;
544
545 /* Build the error string */
546 swprintf(FormatString,
547 L"\nAn error occurred (%08x) while attempting "
548 L"to read the boot configuration data file %s\n",
549 ErrorStatus,
550 Buffer);
551
552 /* Select the resource ID message */
553 ErrorResourceId = 9002;
554 break;
555
556 case BL_FATAL_ERROR_BCD_ENTRIES:
557
558 /* File name is in parameter 1 */
559 FileName = (PWCHAR)Parameter1;
560
561 /* The NTSTATUS code is in parameter 2*/
562 ErrorStatus = (NTSTATUS)Parameter2;
563
564 /* Build the error string */
565 swprintf(FormatString,
566 L"\nNo valid entries found in the boot configuration data file %s\n",
567 FileName);
568
569 /* Select the resource ID message */
570 ErrorResourceId = 9007;
571 break;
572
573 case BL_FATAL_ERROR_BCD_PARSE:
574
575 /* File name isin parameter 1 */
576 FileName = (PWCHAR)Parameter1;
577
578 /* The NTSTATUS code is in parameter 2*/
579 ErrorStatus = (NTSTATUS)Parameter2;
580
581 /* Build the error string */
582 swprintf(FormatString,
583 L"\nThe boot configuration file %s is invalid (%08x).\n",
584 FileName,
585 ErrorStatus);
586
587 /* Select the resource ID message */
588 ErrorResourceId = 9015;
589 break;
590
591 case BL_FATAL_ERROR_GENERIC:
592
593 /* The NTSTATUS code is in parameter 1*/
594 ErrorStatus = (NTSTATUS)Parameter1;
595
596 /* Build the error string */
597 swprintf(FormatString,
598 L"\nThe boot manager experienced an error (%08x).\n",
599 ErrorStatus);
600
601 /* Select the resource ID message */
602 ErrorResourceId = 9005;
603 break;
604
605 default:
606
607 /* The rest is not yet handled */
608 EfiPrintf(L"Unexpected fatal error: %lx\r\n", ErrorCode);
609 while (1);
610 break;
611 }
612
613 /* Check if the BCD option for restart is set */
614 BlGetBootOptionBoolean(BlpApplicationEntry.BcdData,
615 BcdLibraryBoolean_RestartOnFailure,
616 &Restart);
617 if (Restart)
618 {
619 /* Yes, so no error should be shown since we'll auto-restart */
620 NoError = TRUE;
621 }
622 else
623 {
624 /* Check if the option for not showing errors is set in the BCD */
625 BlGetBootOptionBoolean(BlpApplicationEntry.BcdData,
626 BcdBootMgrBoolean_NoErrorDisplay,
627 &NoError);
628 }
629
630 /* Do we want an error? */
631 if (!NoError)
632 {
633 /* Yep, print it and then raise an error */
634 BlStatusPrint(FormatString);
635 BlStatusError(1, ErrorCode, Parameter1, Parameter2, Parameter3);
636 }
637
638 /* Get the help message ID */
639 ErrorHelpId = BmpFatalErrorMessageFilter(ErrorStatus, &ErrorResourceId);
640 BmpErrorLog(ErrorCode, ErrorStatus, ErrorResourceId, Buffer, ErrorHelpId);
641 }
642
643 NTSTATUS
644 BmpFwGetFullPath (
645 _In_ PWCHAR FileName,
646 _Out_ PWCHAR* FullPath
647 )
648 {
649 NTSTATUS Status;
650 ULONG BootDirLength, PathLength;
651
652 /* Compute the length of the directory, and add a NUL */
653 BootDirLength = wcslen(BootDirectory);
654 Status = RtlULongAdd(BootDirLength, 1, &BootDirLength);
655 if (!NT_SUCCESS(Status))
656 {
657 goto Quickie;
658 }
659
660 /* Add the length of the file, make sure it fits */
661 PathLength = wcslen(FileName);
662 Status = RtlULongAdd(PathLength, BootDirLength, &PathLength);
663 if (!NT_SUCCESS(Status))
664 {
665 goto Quickie;
666 }
667
668 /* Convert to bytes */
669 Status = RtlULongLongToULong(PathLength * sizeof(WCHAR), &PathLength);
670 if (!NT_SUCCESS(Status))
671 {
672 goto Quickie;
673 }
674
675 /* Allocate the full path */
676 *FullPath = BlMmAllocateHeap(PathLength);
677 if (*FullPath)
678 {
679 /* Copy the directory followed by the file name */
680 wcsncpy(*FullPath, BootDirectory, PathLength / sizeof(WCHAR));
681 wcsncat(*FullPath, FileName, PathLength / sizeof(WCHAR));
682 }
683 else
684 {
685 /* Bail out since we have no memory */
686 Status = STATUS_NO_MEMORY;
687 }
688
689 Quickie:
690 /* Return to caller */
691 return Status;
692 }
693
694 VOID
695 BmCloseDataStore (
696 _In_ HANDLE Handle
697 )
698 {
699 /* Check if boot.ini data needs to be freed */
700 if (BmBootIniUsed)
701 {
702 EfiPrintf(L"Boot.ini not handled\r\n");
703 }
704
705 /* Dereference the hive and close the key */
706 BiDereferenceHive(Handle);
707 BiCloseKey(Handle);
708 }
709
710 NTSTATUS
711 BmOpenDataStore (
712 _Out_ PHANDLE Handle
713 )
714 {
715 NTSTATUS Status;
716 PBL_DEVICE_DESCRIPTOR BcdDevice;
717 PWCHAR BcdPath, FullPath, PathBuffer;
718 BOOLEAN HavePath;
719 ULONG PathLength, FullSize;
720 PVOID FinalBuffer;
721 UNICODE_STRING BcdString;
722
723 /* Initialize variables */
724 PathBuffer = NULL;
725 BcdDevice = NULL;
726 BcdPath = NULL;
727 HavePath = FALSE;
728
729 /* Check if a boot.ini file exists */
730 Status = BmOpenBootIni();
731 if (NT_SUCCESS(Status))
732 {
733 BmBootIniUsed = TRUE;
734 }
735
736 /* Check on which device the BCD is */
737 Status = BlGetBootOptionDevice(BlpApplicationEntry.BcdData,
738 BcdBootMgrDevice_BcdDevice,
739 &BcdDevice,
740 NULL);
741 if (!NT_SUCCESS(Status))
742 {
743 /* It's not on a custom device, so it must be where we are */
744 Status = BlGetBootOptionDevice(BlpApplicationEntry.BcdData,
745 BcdLibraryDevice_ApplicationDevice,
746 &BcdDevice,
747 NULL);
748 if (!NT_SUCCESS(Status))
749 {
750 /* This BCD option is required */
751 goto Quickie;
752 }
753 }
754
755 /* Next, check what file contains the BCD */
756 Status = BlGetBootOptionString(BlpApplicationEntry.BcdData,
757 BcdBootMgrString_BcdFilePath,
758 &BcdPath);
759 if (NT_SUCCESS(Status))
760 {
761 /* We don't handle custom BCDs yet */
762 EfiPrintf(L"Custom BCD Not handled: %s\r\n", BcdPath);
763 Status = STATUS_NOT_IMPLEMENTED;
764 goto Quickie;
765 }
766
767 /* Now check if the BCD is on a remote share */
768 if (BcdDevice->DeviceType == UdpDevice)
769 {
770 /* Nope. Nope. Nope */
771 EfiPrintf(L"UDP device Not handled\r\n");
772 Status = STATUS_NOT_IMPLEMENTED;
773 goto Quickie;
774 }
775
776 /* Otherwise, compute the hardcoded path of the BCD */
777 Status = BmpFwGetFullPath(L"\\BCD", &FullPath);
778 if (!NT_SUCCESS(Status))
779 {
780 /* User the raw path */
781 PathBuffer = BcdPath;
782 }
783 else
784 {
785 /* Use the path we got */
786 PathBuffer = FullPath;
787 HavePath = TRUE;
788 }
789
790 /* Check if we failed to get the BCD path */
791 if (!NT_SUCCESS(Status))
792 {
793 goto Quickie;
794 }
795
796 /* Add a NUL to the path, make sure it'll fit */
797 PathLength = wcslen(PathBuffer);
798 Status = RtlULongAdd(PathLength, 1, &PathLength);
799 if (!NT_SUCCESS(Status))
800 {
801 goto Quickie;
802 }
803
804 /* Convert to bytes */
805 Status = RtlULongLongToULong(PathLength * sizeof(WCHAR), &PathLength);
806 if (!NT_SUCCESS(Status))
807 {
808 goto Quickie;
809 }
810
811 /* Now add the size of the path to the device path, check if it fits */
812 Status = RtlULongAdd(PathLength, BcdDevice->Size, &FullSize);
813 if (!NT_SUCCESS(Status))
814 {
815 goto Quickie;
816 }
817
818 /* Allocate a final structure to hold both entities */
819 FinalBuffer = BlMmAllocateHeap(FullSize);
820 if (!FinalBuffer)
821 {
822 Status = STATUS_NO_MEMORY;
823 goto Quickie;
824 }
825
826 /* Copy the device path and file path into the final buffer */
827 RtlCopyMemory(FinalBuffer, BcdDevice, BcdDevice->Size);
828 RtlCopyMemory((PVOID)((ULONG_PTR)FinalBuffer + BcdDevice->Size),
829 PathBuffer,
830 PathLength);
831
832 /* Now tell the BCD engine to open the store */
833 BcdString.Length = FullSize;
834 BcdString.MaximumLength = FullSize;
835 BcdString.Buffer = FinalBuffer;
836 Status = BcdOpenStoreFromFile(&BcdString, Handle);
837
838 /* Free our final buffer */
839 BlMmFreeHeap(FinalBuffer);
840
841 Quickie:
842 /* Did we allocate a device? */
843 if (BcdDevice)
844 {
845 /* Free it */
846 BlMmFreeHeap(BcdDevice);
847 }
848
849 /* Is this the failure path? */
850 if (!NT_SUCCESS(Status))
851 {
852 /* Raise a fatal error */
853 BmFatalErrorEx(BL_FATAL_ERROR_BCD_READ,
854 (ULONG_PTR)PathBuffer,
855 Status,
856 0,
857 0);
858 }
859
860 /* Did we get an allocated path? */
861 if ((PathBuffer) && (HavePath))
862 {
863 /* Free it */
864 BlMmFreeHeap(PathBuffer);
865 }
866
867 /* Return back to the caller */
868 return Status;
869 }
870
871 typedef struct _BL_BSD_LOG_OBJECT
872 {
873 ULONG DeviceId;
874 ULONG FileId;
875 ULONG Unknown;
876 ULONG Size;
877 ULONG Flags;
878 } BL_BSD_LOG_OBJECT, *PBL_BSD_LOG_OBJECT;
879
880 BL_BSD_LOG_OBJECT BsdpLogObject;
881 BOOLEAN BsdpLogObjectInitialized;
882
883 VOID
884 BlBsdInitializeLog (
885 _In_ PBL_DEVICE_DESCRIPTOR LogDevice,
886 _In_ PWCHAR LogPath,
887 _In_ ULONG Flags
888 )
889 {
890 NTSTATUS Status;
891
892 /* Don't initialize twice */
893 if (BsdpLogObjectInitialized)
894 {
895 return;
896 }
897
898 /* Set invalid IDs for now */
899 BsdpLogObject.DeviceId = -1;
900 BsdpLogObject.FileId = -1;
901
902 /* Open the BSD device */
903 Status = BlpDeviceOpen(LogDevice,
904 BL_DEVICE_READ_ACCESS | BL_DEVICE_WRITE_ACCESS,
905 0,
906 &BsdpLogObject.DeviceId);
907 if (!NT_SUCCESS(Status))
908 {
909 /* Welp that didn't work */
910 goto FailurePath;
911 }
912
913 /* Now open the BSD itself */
914 Status = BlFileOpen(BsdpLogObject.DeviceId,
915 LogPath,
916 BL_FILE_READ_ACCESS | BL_FILE_WRITE_ACCESS,
917 &BsdpLogObject.FileId);
918 if (!NT_SUCCESS(Status))
919 {
920 /* D'oh */
921 goto FailurePath;
922 }
923
924 /* The BSD is open. Start doing stuff to it */
925 EfiPrintf(L"Unimplemented BSD path\r\n");
926 Status = STATUS_NOT_IMPLEMENTED;
927
928 FailurePath:
929 /* Close the BSD if we had it open */
930 if (BsdpLogObject.FileId != -1)
931 {
932 BlFileClose(BsdpLogObject.FileId);
933 }
934
935 /* Close the device if we had it open */
936 if (BsdpLogObject.DeviceId != -1)
937 {
938 BlDeviceClose(BsdpLogObject.DeviceId);
939 }
940
941 /* Set BSD object to its uninitialized state */
942 BsdpLogObjectInitialized = FALSE;
943 BsdpLogObject.FileId = 0;
944 BsdpLogObject.DeviceId = 0;
945 BsdpLogObject.Flags = 0;
946 BsdpLogObject.Unknown = 0;
947 BsdpLogObject.Size = 0;
948 }
949
950 VOID
951 BmpInitializeBootStatusDataLog (
952 VOID
953 )
954 {
955 NTSTATUS Status;
956 PBL_DEVICE_DESCRIPTOR BsdDevice;
957 PWCHAR BsdPath;
958 ULONG Flags;
959 BOOLEAN PreserveBsd;
960
961 /* Initialize locals */
962 BsdPath = NULL;
963 BsdDevice = NULL;
964 Flags = 0;
965
966 /* Check if the BSD is stored in a custom device */
967 Status = BlGetBootOptionDevice(BlpApplicationEntry.BcdData,
968 BcdLibraryDevice_BsdLogDevice,
969 &BsdDevice,
970 NULL);
971 if (!NT_SUCCESS(Status))
972 {
973 /* Nope, use the boot device */
974 BsdDevice = BlpBootDevice;
975 }
976
977 /* Check if the path is custom as well */
978 Status = BlGetBootOptionString(BlpApplicationEntry.BcdData,
979 BcdLibraryString_BsdLogPath,
980 &BsdPath);
981 if (!NT_SUCCESS(Status))
982 {
983 /* Nope, use our default path */
984 Status = BmpFwGetFullPath(L"\\bootstat.dat", &BsdPath);
985 if (!NT_SUCCESS(Status))
986 {
987 BsdPath = NULL;
988 }
989
990 /* Set preserve flag */
991 Flags = 1;
992 }
993 else
994 {
995 /* Set preserve flag */
996 Flags = 1;
997 }
998
999 /* Finally, check if the BSD should be preserved */
1000 Status = BlGetBootOptionBoolean(BlpApplicationEntry.BcdData,
1001 BcdLibraryBoolean_PreserveBsdLog,
1002 &PreserveBsd);
1003 if (!(NT_SUCCESS(Status)) || !(PreserveBsd))
1004 {
1005 /* We failed to read, or we were asked not to preserve it */
1006 Flags = 0;
1007 }
1008
1009 /* Initialize the log */
1010 BlBsdInitializeLog(BsdDevice, BsdPath, Flags);
1011
1012 /* Free the BSD device descriptor if we had one */
1013 if (BsdDevice)
1014 {
1015 BlMmFreeHeap(BsdDevice);
1016 }
1017
1018 /* Free the BSD path if we had one */
1019 if ((Flags) && (BsdPath))
1020 {
1021 BlMmFreeHeap(BsdPath);
1022 }
1023 }
1024
1025 VOID
1026 BmFwMemoryInitialize (
1027 VOID
1028 )
1029 {
1030 NTSTATUS Status;
1031 PHYSICAL_ADDRESS PhysicalAddress;
1032 BL_ADDRESS_RANGE AddressRange;
1033
1034 /* Select the range below 1MB */
1035 AddressRange.Maximum = 0xFFFFF;
1036 AddressRange.Minimum = 0;
1037
1038 /* Allocate one reserved page with the "reserved" attribute */
1039 Status = MmPapAllocatePhysicalPagesInRange(&PhysicalAddress,
1040 BlApplicationReserved,
1041 1,
1042 BlMemoryReserved,
1043 0,
1044 &MmMdlUnmappedAllocated,
1045 &AddressRange,
1046 BL_MM_REQUEST_DEFAULT_TYPE);
1047 if (!NT_SUCCESS(Status))
1048 {
1049 /* Print a message on error, but keep going */
1050 BlStatusPrint(L"BmFwMemoryInitialize: Failed to allocate a page below 1MB. Status: 0x%08x\r\n",
1051 Status);
1052 }
1053 }
1054
1055 NTSTATUS
1056 BmpBgDisplayClearScreen (
1057 _In_ ULONG Color
1058 )
1059 {
1060 /* Not yet supported */
1061 return STATUS_NOT_IMPLEMENTED;
1062 }
1063
1064 NTSTATUS
1065 BlXmiWrite (
1066 _In_ PWCHAR XmlTag
1067 )
1068 {
1069 /* Sigh */
1070 EfiPrintf(L"XML: %s\r\n", XmlTag);
1071 return STATUS_NOT_IMPLEMENTED;
1072 }
1073
1074 NTSTATUS
1075 BlXmiInitialize (
1076 _In_ PWCHAR Stylesheet
1077 )
1078 {
1079 /* Reset the cursor type */
1080 BlDisplaySetCursorType(0);
1081
1082 /* Nope, not doing any XML stuff */
1083 return STATUS_SUCCESS;
1084 }
1085
1086 NTSTATUS
1087 BmFwVerifySelfIntegrity (
1088 VOID
1089 )
1090 {
1091 /* Check if we're booted by UEFI off the DVD directlry */
1092 if ((BlpBootDevice->DeviceType == LocalDevice) &&
1093 (BlpBootDevice->Local.Type == CdRomDevice) &&
1094 (BlpApplicationFlags & BL_APPLICATION_FLAG_CONVERTED_FROM_EFI))
1095 {
1096 /* Windows actually bypasses integrity checks in this case. Works for us */
1097 return STATUS_SUCCESS;
1098 }
1099
1100 /* Our binaries aren't signed, so always return failure */
1101 return 0xC0000428;
1102 }
1103
1104 NTSTATUS
1105 BmFwRegisterRevocationList (
1106 VOID
1107 )
1108 {
1109 NTSTATUS Status;
1110 BOOLEAN SecureBootEnabled;
1111
1112 /* Is SecureBoot enabled? */
1113 Status = BlSecureBootIsEnabled(&SecureBootEnabled);
1114 if ((NT_SUCCESS(Status)) && (SecureBootEnabled))
1115 {
1116 EfiPrintf(L"SB not implemented revok\r\n");
1117 return STATUS_NOT_IMPLEMENTED;
1118 }
1119 else
1120 {
1121 /* Nothing to do without SecureBoot */
1122 Status = STATUS_SUCCESS;
1123 }
1124
1125 /* Return revocation result back to caller */
1126 return Status;
1127 }
1128
1129 NTSTATUS
1130 BmResumeFromHibernate (
1131 _Out_ PHANDLE BcdResumeHandle
1132 )
1133 {
1134 NTSTATUS Status;
1135 BOOLEAN AttemptResume;
1136
1137 /* Should we attempt to resume from hibernation? */
1138 Status = BlGetBootOptionBoolean(BlpApplicationEntry.BcdData,
1139 BcdBootMgrBoolean_AttemptResume,
1140 &AttemptResume);
1141 if (!NT_SUCCESS(Status))
1142 {
1143 /* Nope. Is automatic restart on crash enabled? */
1144 AttemptResume = FALSE;
1145 Status = BlGetBootOptionBoolean(BlpApplicationEntry.BcdData,
1146 BcdOSLoaderBoolean_DisableCrashAutoReboot,
1147 &AttemptResume);
1148 AttemptResume = (NT_SUCCESS(Status) && (AttemptResume));
1149 }
1150
1151 /* Don't do anything if there's no need to resume anything */
1152 if (!AttemptResume)
1153 {
1154 return STATUS_SUCCESS;
1155 }
1156
1157 /* Not yet implemented */
1158 EfiPrintf(L"Resume not supported\r\n");
1159 return STATUS_NOT_IMPLEMENTED;
1160 }
1161
1162 NTSTATUS
1163 BmpProcessBadMemory (
1164 VOID
1165 )
1166 {
1167 BL_PD_DATA_BLOB BadMemoryData;
1168 NTSTATUS Status;
1169
1170 /* Try to get the memory data from the memtest application */
1171 BadMemoryData.BlobSize = 0;
1172 BadMemoryData.Data = NULL;
1173 BadMemoryData.DataSize = 0;
1174 Status = BlPdQueryData(&BadMemoryGuid, NULL, &BadMemoryData);
1175 if (Status != STATUS_BUFFER_TOO_SMALL)
1176 {
1177 /* No results, or some other error */
1178 return Status;
1179 }
1180
1181 /* Not yet implemented */
1182 EfiPrintf(L"Bad page list persistence not implemented\r\n");
1183 return STATUS_NOT_IMPLEMENTED;
1184 }
1185
1186 NTSTATUS
1187 BmPurgeOption (
1188 _In_ HANDLE BcdHandle,
1189 _In_ PGUID ObjectId,
1190 _In_ ULONG Type
1191 )
1192 {
1193 HANDLE ObjectHandle;
1194 NTSTATUS Status;
1195
1196 /* Open the object */
1197 Status = BcdOpenObject(BcdHandle, ObjectId, &ObjectHandle);
1198 if (NT_SUCCESS(Status))
1199 {
1200 /* Delete the element */
1201 BcdDeleteElement(ObjectHandle, Type);
1202
1203 /* Close the object and set success */
1204 BiCloseKey(ObjectHandle);
1205 Status = STATUS_SUCCESS;
1206 }
1207
1208 /* Return the result */
1209 return Status;
1210 }
1211
1212 NTSTATUS
1213 BmGetEntryDescription (
1214 _In_ HANDLE BcdHandle,
1215 _In_ PGUID ObjectId,
1216 _Out_ PBCD_OBJECT_DESCRIPTION Description
1217 )
1218 {
1219 NTSTATUS Status;
1220 HANDLE ObjectHandle;
1221
1222 /* Open the BCD object */
1223 Status = BcdOpenObject(BcdHandle, ObjectId, &ObjectHandle);
1224 if (NT_SUCCESS(Status))
1225 {
1226 /* Make sure the caller passed this argument in */
1227 if (!Description)
1228 {
1229 /* Fail otherwise */
1230 Status = STATUS_INVALID_PARAMETER;
1231 }
1232 else
1233 {
1234 /* Query the description from the BCD interface */
1235 Status = BiGetObjectDescription(ObjectHandle, Description);
1236 }
1237
1238 /* Close the object key */
1239 BiCloseKey(ObjectHandle);
1240 }
1241
1242 /* Return the result back */
1243 return Status;
1244 }
1245
1246 NTSTATUS
1247 BmpPopulateBootEntryList (
1248 _In_ HANDLE BcdHandle,
1249 _In_ PGUID SequenceList,
1250 _In_ ULONG Flags,
1251 _Out_ PBL_LOADED_APPLICATION_ENTRY* BootSequence,
1252 _Out_ PULONG SequenceCount
1253 )
1254 {
1255 NTSTATUS Status;
1256 ULONG BootIndex, i, OptionSize;
1257 PBL_LOADED_APPLICATION_ENTRY BootEntry;
1258 PBL_BCD_OPTION Options;
1259 BCD_OBJECT_DESCRIPTION Description;
1260 BcdObjectType ObjectType;
1261 BOOLEAN HavePath, IsWinPe, SoftReboot;
1262 PWCHAR LoaderPath;
1263
1264 /* Initialize locals */
1265 Options = NULL;
1266 BootIndex = 0;
1267 Status = STATUS_NOT_FOUND;
1268
1269 /* Loop through every element in the sequence */
1270 for (i = 0; i < *SequenceCount; i++)
1271 {
1272 /* Assume failure */
1273 BootEntry = NULL;
1274
1275 /* Get the options for the sequence element */
1276 Status = BmGetOptionList(BcdHandle, SequenceList, &Options);
1277 if (!NT_SUCCESS(Status))
1278 {
1279 EfiPrintf(L"option list failed: %lx\r\n", Status);
1280 goto LoopQuickie;
1281 }
1282
1283 /* Make sure there's at least a path and description */
1284 if (!(MiscGetBootOption(Options, BcdLibraryDevice_ApplicationDevice)) ||
1285 !(MiscGetBootOption(Options, BcdLibraryString_Description)))
1286 {
1287 Status = STATUS_UNSUCCESSFUL;
1288 EfiPrintf(L"missing list failed: %lx\r\n", Status);
1289 goto LoopQuickie;
1290 }
1291
1292 /* Get the size of the BCD options and allocate a large enough entry */
1293 OptionSize = BlGetBootOptionListSize(Options);
1294 BootEntry = BlMmAllocateHeap(sizeof(*BootEntry) + OptionSize);
1295 if (!BootEntry)
1296 {
1297 Status = STATUS_NO_MEMORY;
1298 goto Quickie;
1299 }
1300
1301 /* Save it as part of the sequence */
1302 BootSequence[BootIndex] = BootEntry;
1303
1304 /* Initialize it, and copy the BCD data */
1305 RtlZeroMemory(BootEntry, sizeof(*BootEntry));
1306 BootEntry->Guid = *SequenceList;
1307 BootEntry->BcdData = (PBL_BCD_OPTION)(BootEntry + 1);
1308 BootEntry->Flags = Flags;
1309 RtlCopyMemory(BootEntry->BcdData, Options, OptionSize);
1310
1311 /* Get the object descriptor to find out what kind of entry it is */
1312 Status = BmGetEntryDescription(BcdHandle,
1313 &BootEntry->Guid,
1314 &Description);
1315 if (!NT_SUCCESS(Status))
1316 {
1317 EfiPrintf(L"missing desc failed: %lx\r\n", Status);
1318 goto LoopQuickie;
1319 }
1320
1321 /* Check if a path was given or not */
1322 HavePath = MiscGetBootOption(Options, BcdLibraryString_ApplicationPath) ?
1323 TRUE : FALSE;
1324
1325 /* Now select based on what type of object this is -- must be an app */
1326 ObjectType.PackedValue = Description.Type;
1327 if (ObjectType.Application.ObjectCode == BCD_OBJECT_TYPE_APPLICATION)
1328 {
1329 /* Then select based on what kind of app it is */
1330 switch (ObjectType.Application.ApplicationCode)
1331 {
1332 /* Another boot manager */
1333 case BCD_APPLICATION_TYPE_BOOTMGR:
1334 BootEntry->Flags |= BCD_APPLICATION_TYPE_BOOTMGR;
1335 break;
1336
1337 /* An OS loader */
1338 case BCD_APPLICATION_TYPE_OSLOADER:
1339 BootEntry->Flags |= BL_APPLICATION_ENTRY_WINLOAD;
1340
1341 /* Do we have a path for it? */
1342 if (!HavePath)
1343 {
1344 /* We'll try to make one up. Is this WinPE? */
1345 IsWinPe = FALSE;
1346 Status = BlGetBootOptionBoolean(Options,
1347 BcdOSLoaderBoolean_WinPEMode,
1348 &IsWinPe);
1349 if (!(NT_SUCCESS(Status)) && (Status != STATUS_NOT_FOUND))
1350 {
1351 goto Quickie;
1352 }
1353
1354 /* Use the appropriate path for WinPE or local install */
1355 LoaderPath = IsWinPe ?
1356 L"\\Windows\\System32\\boot\\winload.efi" :
1357 L"\\Windows\\System32\\winload.efi";
1358
1359 /* Add the path to the boot entry */
1360 Status = BlAppendBootOptionString(BootEntry, LoaderPath);
1361 if (!NT_SUCCESS(Status))
1362 {
1363 goto Quickie;
1364 }
1365
1366 /* We have a path now */
1367 HavePath = TRUE;
1368 }
1369 break;
1370
1371 /* A hibernate-resume application */
1372 case BCD_APPLICATION_TYPE_RESUME:
1373 BootEntry->Flags |= BL_APPLICATION_ENTRY_WINRESUME;
1374 break;
1375
1376 /* An older OS NTLDR */
1377 case BCD_APPLICATION_TYPE_NTLDR:
1378 BootEntry->Flags |= BL_APPLICATION_ENTRY_NTLDR;
1379 break;
1380
1381 /* An older OS SETUPLDR */
1382 case BCD_APPLICATION_TYPE_SETUPLDR:
1383 BootEntry->Flags |= BL_APPLICATION_ENTRY_SETUPLDR;
1384 break;
1385
1386 /* A 3rd party/Win9x boot sector */
1387 case BCD_APPLICATION_TYPE_BOOTSECTOR:
1388 BootEntry->Flags |= BL_APPLICATION_ENTRY_BOOTSECTOR;
1389 break;
1390
1391 /* Something else entirely */
1392 default:
1393 break;
1394 }
1395 }
1396
1397 /* We better have a path by now */
1398 if (!HavePath)
1399 {
1400 Status = STATUS_UNSUCCESSFUL;
1401 goto LoopQuickie;
1402 }
1403
1404 /* Check if this is a real mode startup.com */
1405 if ((ObjectType.Application.ObjectCode == BCD_OBJECT_TYPE_APPLICATION) &&
1406 (ObjectType.Application.ImageCode == BCD_IMAGE_TYPE_REAL_MODE) &&
1407 (ObjectType.Application.ApplicationCode == BCD_APPLICATION_TYPE_STARTUPCOM))
1408 {
1409 /* Check if PXE soft reboot will occur */
1410 Status = BlGetBootOptionBoolean(Options,
1411 BcdStartupBoolean_PxeSoftReboot,
1412 &SoftReboot);
1413 if ((NT_SUCCESS(Status)) && (SoftReboot))
1414 {
1415 /* Then it's a valid startup.com entry */
1416 BootEntry->Flags |= BL_APPLICATION_ENTRY_STARTUP;
1417 }
1418 }
1419
1420 LoopQuickie:
1421 /* All done with this entry -- did we have BCD options? */
1422 if (Options)
1423 {
1424 /* Free them, they're part of the entry now */
1425 BlMmFreeHeap(Options);
1426 Options = NULL;
1427 }
1428
1429 /* Did we fail anywhere? */
1430 if (!NT_SUCCESS(Status))
1431 {
1432 /* Yep -- did we fail with an active boot entry? */
1433 if (BootEntry)
1434 {
1435 /* Destroy it */
1436 BlDestroyBootEntry(BootEntry);
1437 BootSequence[BootIndex] = NULL;
1438 }
1439 }
1440 else
1441 {
1442 /* It worked, so populate the next index now */
1443 BootIndex++;
1444 }
1445
1446 /* And move to the next GUID in the sequence list */
1447 SequenceList++;
1448 }
1449
1450 Quickie:
1451 /* All done now -- did we have any BCD options? */
1452 if (Options)
1453 {
1454 /* Free them */
1455 BlMmFreeHeap(Options);
1456 }
1457
1458 /* Return the status */
1459 return Status;
1460 }
1461
1462 NTSTATUS
1463 BmGetBootSequence (
1464 _In_ HANDLE BcdHandle,
1465 _In_ PGUID SequenceList,
1466 _In_ ULONG SequenceListCount,
1467 _In_ ULONG Flags,
1468 _Out_ PBL_LOADED_APPLICATION_ENTRY** BootSequence,
1469 _Out_ PULONG SequenceCount
1470 )
1471 {
1472 PBL_LOADED_APPLICATION_ENTRY* Sequence;
1473 ULONG Count = SequenceListCount;
1474 NTSTATUS Status;
1475
1476 /* Allocate the sequence list */
1477 Sequence = BlMmAllocateHeap(SequenceListCount * sizeof(*Sequence));
1478 if (!Sequence)
1479 {
1480 return STATUS_NO_MEMORY;
1481 }
1482
1483 /* Populate the sequence list */
1484 Status = BmpPopulateBootEntryList(BcdHandle,
1485 SequenceList,
1486 Flags,
1487 Sequence,
1488 &Count);
1489 if (!NT_SUCCESS(Status))
1490 {
1491 /* Free the list on failure */
1492 BlMmFreeHeap(Sequence);
1493 }
1494 else
1495 {
1496 /* Otherwise, set success and return the list and count */
1497 Status = STATUS_SUCCESS;
1498 *BootSequence = Sequence;
1499 *SequenceCount = Count;
1500 }
1501
1502 /* All done */
1503 return Status;
1504 }
1505
1506 NTSTATUS
1507 BmEnumerateBootEntries (
1508 _In_ HANDLE BcdHandle,
1509 _Out_ PBL_LOADED_APPLICATION_ENTRY **BootSequence,
1510 _Out_ PULONG SequenceCount
1511 )
1512 {
1513 NTSTATUS Status;
1514 ULONG BootIndex, BootIniCount, BootEntryCount, BcdCount;
1515 PBL_LOADED_APPLICATION_ENTRY* Sequence;
1516 PGUID DisplayOrder;
1517 GUID DefaultObject;
1518 BOOLEAN UseDisplayList;
1519
1520 /* Initialize locals */
1521 BootIndex = 0;
1522
1523 /* First try to get the display list, if any */
1524 UseDisplayList = TRUE;
1525 Status = BlGetBootOptionGuidList(BlpApplicationEntry.BcdData,
1526 BcdBootMgrObjectList_DisplayOrder,
1527 &DisplayOrder,
1528 &BcdCount);
1529 if (!NT_SUCCESS(Status))
1530 {
1531 /* No list, get the default entry instead */
1532 Status = BlGetBootOptionGuid(BlpApplicationEntry.BcdData,
1533 BcdBootMgrObject_DefaultObject,
1534 &DefaultObject);
1535 if (NT_SUCCESS(Status))
1536 {
1537 /* Set the array to just our entry */
1538 UseDisplayList = FALSE;
1539 BcdCount = 1;
1540 DisplayOrder = &DefaultObject;
1541 }
1542 else
1543 {
1544 /* No default list either, return success but no entries */
1545 *BootSequence = NULL;
1546 *SequenceCount = 0;
1547 Status = STATUS_SUCCESS;
1548 DisplayOrder = NULL;
1549 goto Quickie;
1550 }
1551 }
1552
1553 /* Check if boot.ini was used */
1554 BootIniCount = 0;
1555 if (BmBootIniUsed)
1556 {
1557 /* Get the entries from it */
1558 EfiPrintf(L"Boot.ini not supported\r\n");
1559 BootIniCount = 0;//BmBootIniGetEntryCount();
1560 }
1561
1562 /* Allocate an array large enough for the combined boot entries */
1563 BootEntryCount = BootIniCount + BcdCount;
1564 Sequence = BlMmAllocateHeap(BootEntryCount * sizeof(*Sequence));
1565 if (!Sequence)
1566 {
1567 Status = STATUS_NO_MEMORY;
1568 goto Quickie;
1569 }
1570
1571 /* Zero it out */
1572 RtlZeroMemory(Sequence, BootEntryCount * sizeof(*Sequence));
1573
1574 /* Check if we had BCD entries */
1575 if (BcdCount)
1576 {
1577 /* Populate the list of bootable entries */
1578 Status = BmpPopulateBootEntryList(BcdHandle,
1579 DisplayOrder,
1580 BL_APPLICATION_ENTRY_DISPLAY_ORDER,
1581 Sequence,
1582 &BcdCount);
1583 if (!NT_SUCCESS(Status))
1584 {
1585 /* Bail out */
1586 goto Quickie;
1587 }
1588 }
1589
1590 /* Check if we had boot.ini entries */
1591 if (BootIniCount)
1592 {
1593 /* TODO */
1594 EfiPrintf(L"Boot.ini not supported\r\n");
1595 }
1596
1597 /* Return success and the sequence + count populated */
1598 Status = STATUS_SUCCESS;
1599 *BootSequence = Sequence;
1600 *SequenceCount = BootIniCount + BcdCount;
1601
1602 Quickie:
1603 /* Check if we had allocated a GUID list */
1604 if ((UseDisplayList) && (DisplayOrder))
1605 {
1606 /* Free it */
1607 BlMmFreeHeap(DisplayOrder);
1608 }
1609
1610 /* Check if this is the failure path */
1611 if (!(NT_SUCCESS(Status)) && (Sequence))
1612 {
1613 /* Loop the remaining boot entries */
1614 while (BootIndex < BootEntryCount)
1615 {
1616 /* Check if it had been allocated */
1617 if (Sequence[BootIndex])
1618 {
1619 /* Free it */
1620 BlMmFreeHeap(Sequence[BootIndex]);
1621 }
1622
1623 /* Next*/
1624 BootIndex++;
1625 }
1626
1627 /* Free the whole sequence now */
1628 BlMmFreeHeap(Sequence);
1629 }
1630
1631 /* All done, return the result */
1632 return Status;
1633 }
1634
1635 VOID
1636 BmpGetDefaultBootEntry (
1637 _In_ PBL_LOADED_APPLICATION_ENTRY* Sequence,
1638 _In_ ULONG Count,
1639 _Out_ PBL_LOADED_APPLICATION_ENTRY* DefaultEntry,
1640 _Out_ PULONG DefaultIndex
1641 )
1642 {
1643 GUID DefaultObject;
1644 NTSTATUS Status;
1645 ULONG BootIndex;
1646
1647 /* Assume no default */
1648 *DefaultEntry = *Sequence;
1649 *DefaultIndex = 0;
1650
1651 /* Nothing to do if there's just one entry */
1652 if (Count == 1)
1653 {
1654 return;
1655 }
1656
1657 /* Get the default object, bail out if there isn't one */
1658 Status = BlGetBootOptionGuid(BlpApplicationEntry.BcdData,
1659 BcdBootMgrObject_DefaultObject,
1660 &DefaultObject);
1661 if (!(NT_SUCCESS(Status)) || !(Count))
1662 {
1663 return;
1664 }
1665
1666 /* Scan the boot sequence */
1667 for (BootIndex = 0; BootIndex < Count; BootIndex++)
1668 {
1669 /* Find one that matches the default */
1670 if (RtlEqualMemory(&Sequence[BootIndex]->Guid,
1671 &DefaultObject,
1672 sizeof(GUID)))
1673 {
1674 /* Return it */
1675 *DefaultEntry = Sequence[BootIndex];
1676 *DefaultIndex = BootIndex;
1677 return;
1678 }
1679 }
1680 }
1681
1682 BL_MENU_POLICY
1683 BmGetBootMenuPolicy (
1684 _In_ PBL_LOADED_APPLICATION_ENTRY BootEntry
1685 )
1686 {
1687 NTSTATUS Status;
1688 BOOLEAN EmsEnabled;
1689 ULONGLONG BootMenuPolicy;
1690 ULONG OptionId;
1691
1692 /* Check if EMS is enabled */
1693 Status = BlGetBootOptionBoolean(BlpApplicationEntry.BcdData,
1694 BcdOSLoaderBoolean_EmsEnabled,
1695 &EmsEnabled);
1696 if ((NT_SUCCESS(Status)) && (EmsEnabled))
1697 {
1698 /* No boot menu */
1699 return MenuPolicyLegacy;
1700 }
1701
1702 /* Check what entry we are looking at */
1703 if (!BootEntry)
1704 {
1705 /* No entry, pick the selected one */
1706 BootEntry = BmpSelectedBootEntry;
1707 }
1708
1709 /* Do we still not have an entry? */
1710 if (!BootEntry)
1711 {
1712 /* Show the menu */
1713 return MenuPolicyStandard;
1714 }
1715
1716 /* Check if this is an OS loader */
1717 BootMenuPolicy = 0;
1718 if (BootEntry->Flags & BL_APPLICATION_ENTRY_WINLOAD)
1719 {
1720 /* Use the correct option ID */
1721 OptionId = BcdOSLoaderInteger_BootMenuPolicy;
1722 }
1723 else
1724 {
1725 /* Check if this is an OS resumer */
1726 if (!(BootEntry->Flags & BL_APPLICATION_ENTRY_WINRESUME))
1727 {
1728 /* Nope, so no reason for a menu */
1729 return MenuPolicyLegacy;
1730 }
1731
1732 /* Use the correct opetion ID */
1733 OptionId = BcdResumeInteger_BootMenuPolicy;
1734 }
1735
1736 /* Check the option ID for the boot menu policy */
1737 Status = BlGetBootOptionInteger(BootEntry->BcdData,
1738 OptionId,
1739 &BootMenuPolicy);
1740 if (NT_SUCCESS(Status))
1741 {
1742 /* We have one, return it */
1743 return BootMenuPolicy;
1744 }
1745
1746 /* No policy, so assume no menu */
1747 return MenuPolicyLegacy;
1748 }
1749
1750 VOID
1751 BmDisplayGetBootMenuStatus (
1752 _Out_ PL_MENU_STATUS MenuStatus
1753 )
1754 {
1755 /* For now, don't support key input at all */
1756 MenuStatus->AsULong = 0;
1757 MenuStatus->OemKey = UNICODE_NULL;
1758 MenuStatus->BootIndex = -1;
1759 }
1760
1761 NTSTATUS
1762 BmProcessCustomAction (
1763 _In_ HANDLE BcdHandle,
1764 _In_ PWCHAR ActionKey
1765 )
1766 {
1767 EfiPrintf(L"Custom actions not yet handled\r\n");
1768 return STATUS_NOT_IMPLEMENTED;
1769 }
1770
1771 VOID
1772 BmpProcessBootEntry (
1773 _In_ HANDLE BcdHandle,
1774 _In_ PBL_LOADED_APPLICATION_ENTRY BootEntry,
1775 _Out_ PBOOLEAN ExitBootManager
1776 )
1777 {
1778 BL_MENU_STATUS MenuStatus;
1779
1780 /* Don't exit */
1781 *ExitBootManager = FALSE;
1782
1783 /* If the legacy menu must be shown, or if we have a boot entry */
1784 if ((BmGetBootMenuPolicy(BootEntry) != MenuPolicyStandard) || (BootEntry))
1785 {
1786 /* Check if any key has been presseed */
1787 BmDisplayGetBootMenuStatus(&MenuStatus);
1788 if (MenuStatus.AnyKey)
1789 {
1790 /* Was the exit key pressed? */
1791 if (MenuStatus.Exit)
1792 {
1793 /* Don't display a menu, and exit */
1794 *ExitBootManager = TRUE;
1795 BmpDisplayBootMenu = FALSE;
1796 }
1797 else if (MenuStatus.OemKey)
1798 {
1799 /* Process the OEM key action */
1800 BmProcessCustomAction(BcdHandle, &MenuStatus.KeyValue);
1801 }
1802 else
1803 {
1804 /* Process other keys */
1805 EfiPrintf(L"TODO\r\n");
1806 }
1807 }
1808 }
1809 }
1810
1811 NTSTATUS
1812 BmpGetSelectedBootEntry (
1813 _In_ HANDLE BcdHandle,
1814 _Out_ PBL_LOADED_APPLICATION_ENTRY* SelectedBootEntry,
1815 _Out_ PULONG EntryIndex,
1816 _Out_ PBOOLEAN ExitBootManager
1817 )
1818 {
1819 NTSTATUS Status;
1820 PBL_LOADED_APPLICATION_ENTRY* Sequence;
1821 PBL_LOADED_APPLICATION_ENTRY Entry, SelectedEntry;
1822 ULONG Count, BootIndex, SelectedIndex;
1823 // BOOLEAN FoundFailedEntry;
1824 ULONGLONG Timeout;
1825
1826 /* Initialize locals */
1827 BootIndex = 0;
1828 Count = 0;
1829 Sequence = NULL;
1830 SelectedEntry = NULL;
1831
1832 /* Enumerate all the boot entries */
1833 Status = BmEnumerateBootEntries(BcdHandle, &Sequence, &Count);
1834 if (!NT_SUCCESS(Status))
1835 {
1836 /* Bail out if we failed */
1837 goto Quickie;
1838 }
1839
1840 /* Check if there are no entries */
1841 if (!Count)
1842 {
1843 /* This is fatal -- kill the system */
1844 Status = STATUS_FILE_INVALID;
1845 BmFatalErrorEx(BL_FATAL_ERROR_BCD_ENTRIES, (ULONG_PTR)L"\\BCD", Status, 0, 0);
1846 goto Quickie;
1847 }
1848
1849 /* Check if we don't yet have an array of failed boot entries */
1850 if (!BmpFailedBootEntries)
1851 {
1852 /* Allocate it */
1853 BmpFailedBootEntries = BlMmAllocateHeap(Count);
1854 if (BmpFailedBootEntries)
1855 {
1856 /* Zero it out */
1857 RtlZeroMemory(BmpFailedBootEntries, Count);
1858 }
1859 }
1860
1861 /* Check if we have a hardcoded boot override */
1862 if (BmBootEntryOverridePresent)
1863 {
1864 EfiPrintf(L"Hard-coded boot override mode not supported\r\n");
1865 }
1866
1867 /* Log the OS count */
1868 //BlLogEtwWrite(BOOT_BOOTMGR_MULTI_OS_COUNT);
1869
1870 /* Check if the display is already active and cached */
1871 if (!BmDisplayStateCached)
1872 {
1873 /* Check if we should display a boot menu */
1874 Status = BlGetBootOptionBoolean(BlpApplicationEntry.BcdData,
1875 BcdBootMgrBoolean_DisplayBootMenu,
1876 &BmpDisplayBootMenu);
1877 if (!NT_SUCCESS(Status))
1878 {
1879 /* Assume not */
1880 BmpDisplayBootMenu = FALSE;
1881 }
1882 }
1883
1884 /* Check if there's only one entry to boot anyway */
1885 if (Count == 1)
1886 {
1887 /* Read it */
1888 SelectedEntry = *Sequence;
1889
1890 /* Process it */
1891 BmpProcessBootEntry(BcdHandle, SelectedEntry, ExitBootManager);
1892
1893 /* Check if we're not displaying a boot menu */
1894 if (!BmpDisplayBootMenu)
1895 {
1896 /* Now we are */
1897 BmpDisplayBootMenu = TRUE;
1898
1899 /* Return the entry and its index back */
1900 *EntryIndex = 0;
1901 *SelectedBootEntry = SelectedEntry;
1902 Status = STATUS_SUCCESS;
1903 goto Quickie;
1904 }
1905 }
1906 else
1907 {
1908 /* Get the default boot entry */
1909 BmpGetDefaultBootEntry(Sequence, Count, &SelectedEntry, &SelectedIndex);
1910
1911 /* Check if we have a failed boot entry array allocated */
1912 //FoundFailedEntry = FALSE;
1913 if (BmpFailedBootEntries)
1914 {
1915 /* Check if the default entry failed to boot */
1916 if (BmpFailedBootEntries[SelectedIndex])
1917 {
1918 /* Loop through the current boot sequence */
1919 for (SelectedIndex = 0; SelectedIndex < Count; SelectedIndex++)
1920 {
1921 /* Check if there's no sequence for this index, or it failed */
1922 while (!(Sequence[SelectedIndex]) ||
1923 (BmpFailedBootEntries[SelectedIndex]))
1924 {
1925 /* Remember that this is a failed entry */
1926 SelectedEntry = Sequence[SelectedIndex];
1927 //FoundFailedEntry = TRUE;
1928 BmpDisplayBootMenu = FALSE;
1929 }
1930 }
1931 }
1932 }
1933
1934 /* Check if the entry is an OS loader */
1935 if (SelectedEntry->Flags & BL_APPLICATION_ENTRY_WINLOAD)
1936 {
1937 // todo
1938 EfiPrintf(L"todo path\r\n");
1939 }
1940
1941 /* Check if there's no timeout */
1942 Status = BlGetBootOptionInteger(BlpApplicationEntry.BcdData,
1943 BcdBootMgrInteger_Timeout,
1944 &Timeout);
1945 if ((NT_SUCCESS(Status) && !(Timeout)))
1946 {
1947 /* There isn't, so just process the default entry right away */
1948 BmpProcessBootEntry(BcdHandle, SelectedEntry, ExitBootManager);
1949
1950 /* Check if we're not displaying a boot menu */
1951 if (!BmpDisplayBootMenu)
1952 {
1953 /* Now we are */
1954 BmpDisplayBootMenu = TRUE;
1955
1956 /* Return the entry and its index back */
1957 *EntryIndex = 0;
1958 *SelectedBootEntry = SelectedEntry;
1959 Status = STATUS_SUCCESS;
1960 goto Quickie;
1961 }
1962
1963 /* Remove the timeout for this boot instance */
1964 BlRemoveBootOption(BlpApplicationEntry.BcdData,
1965 BcdBootMgrInteger_Timeout);
1966 }
1967 }
1968
1969 /* Here is where we display the menu and list of tools */
1970 EfiPrintf(L"Tool selection not yet implemented\r\n");
1971 EfiStall(10000000);
1972 *SelectedBootEntry = NULL;
1973
1974 Quickie:
1975 /* We are done -- did we have a sequence? */
1976 if (Sequence)
1977 {
1978 /* Do we have any boot entries we parsed? */
1979 while (BootIndex < Count)
1980 {
1981 /* Get the current boot entry */
1982 Entry = Sequence[BootIndex];
1983
1984 /* Did we fail, or is is not the selected one? */
1985 if ((Entry) && ((Entry != SelectedEntry) || !(NT_SUCCESS(Status))))
1986 {
1987 /* Destroy it, as it won't be needed */
1988 BlDestroyBootEntry(Entry);
1989 }
1990 else if (Entry == SelectedEntry)
1991 {
1992 /* It's the selected one, return its index */
1993 *EntryIndex = BootIndex;
1994 }
1995
1996 /* Move to the next entry */
1997 BootIndex++;
1998 }
1999
2000 /* Free the sequence of entries */
2001 BlMmFreeHeap(Sequence);
2002 }
2003
2004 /* Return the selection result */
2005 return Status;
2006 }
2007
2008 NTSTATUS
2009 BmLaunchRecoverySequence (
2010 _In_ PBL_LOADED_APPLICATION_ENTRY BootEntry,
2011 _In_ ULONG LaunchCode
2012 )
2013 {
2014 NTSTATUS Status;
2015 PBL_LOADED_APPLICATION_ENTRY RecoveryEntry;
2016 HANDLE BcdHandle;
2017 PGUID RecoverySequence;
2018 ULONG Count, i, RecoveryIndex, SequenceCount;
2019 PBL_LOADED_APPLICATION_ENTRY* Sequence;
2020
2021 /* Initialize locals */
2022 RecoveryIndex = 0;
2023 Sequence = NULL;
2024 RecoverySequence = NULL;
2025 Count = 0;
2026 BcdHandle = NULL;
2027
2028 /* Open the BCD*/
2029 Status = BmOpenDataStore(&BcdHandle);
2030 if (!NT_SUCCESS(Status))
2031 {
2032 goto Quickie;
2033 }
2034
2035 /* Get the recovery sequence list */
2036 Status = BlGetBootOptionGuidList(BootEntry->BcdData,
2037 BcdLibraryObjectList_RecoverySequence,
2038 &RecoverySequence,
2039 &SequenceCount);
2040 if (!NT_SUCCESS(Status))
2041 {
2042 goto Quickie;
2043 }
2044
2045 /* Get the sequence of boot entries out of it */
2046 Status = BmGetBootSequence(BcdHandle,
2047 RecoverySequence,
2048 SequenceCount,
2049 BL_APPLICATION_ENTRY_RECOVERY,
2050 &Sequence,
2051 &Count);
2052 if (!NT_SUCCESS(Status))
2053 {
2054 goto Quickie;
2055 }
2056
2057 /* Was the BCD open? */
2058 if (BcdHandle)
2059 {
2060 /* Close it */
2061 BmCloseDataStore(BcdHandle);
2062 }
2063
2064 /* Now go over every entry in the sequence */
2065 for (i = 0; i < Count; ++i)
2066 {
2067 /* Check the code for this recovery launch */
2068 if (LaunchCode == 2 || LaunchCode == 5)
2069 {
2070 /* Remove the override if there is one, and set it to 4 */
2071 BlRemoveBootOption(Sequence[i]->BcdData, BcdLibraryInteger_DisplayMessageOverride);
2072 BlAppendBootOptionInteger(Sequence[i],
2073 BcdLibraryInteger_DisplayMessageOverride,
2074 4);
2075 }
2076 else if (LaunchCode == 3)
2077 {
2078 /* Remove the override if there is one, and set it to 10 */
2079 BlRemoveBootOption(Sequence[i]->BcdData, BcdLibraryInteger_DisplayMessageOverride);
2080 BlAppendBootOptionInteger(Sequence[i],
2081 BcdLibraryInteger_DisplayMessageOverride,
2082 10);
2083 }
2084
2085 /* Launch the boot entry for this part of the recovery sequence */
2086 Status = BmpLaunchBootEntry(Sequence[i], NULL, LaunchCode, FALSE);
2087 if (!NT_SUCCESS(Status))
2088 {
2089 break;
2090 }
2091 }
2092
2093 Quickie:
2094 /* Did we have a sequence of entries? */
2095 if (Sequence)
2096 {
2097 /* Loop through each one */
2098 for (RecoveryIndex = 0; RecoveryIndex < Count; RecoveryIndex++)
2099 {
2100 /* Does this index have an allocated boot entry? */
2101 RecoveryEntry = Sequence[RecoveryIndex];
2102 if (RecoveryEntry)
2103 {
2104 /* Destroy it */
2105 BlDestroyBootEntry(RecoveryEntry);
2106 }
2107 }
2108
2109 /* Free the sequence itself */
2110 BlMmFreeHeap(Sequence);
2111 }
2112
2113 /* Was there a sequence list? */
2114 if (RecoverySequence)
2115 {
2116 /* Free it */
2117 BlMmFreeHeap(RecoverySequence);
2118 }
2119
2120 /* Return back to caller */
2121 return Status;
2122 }
2123
2124 ULONG
2125 BmDisplayDumpError (
2126 _In_ PBL_LOADED_APPLICATION_ENTRY BootEntry,
2127 _In_ ULONG LaunchCode
2128 )
2129 {
2130 ULONG BootError;
2131 NTSTATUS Status;
2132 BOOLEAN Restart, NoError;
2133
2134 /* Assume we'll just reboot */
2135 BootError = Reboot;
2136
2137 /* Should we reboot? */
2138 Status = BlGetBootOptionBoolean(BlpApplicationEntry.BcdData,
2139 BcdLibraryBoolean_RestartOnFailure,
2140 &Restart);
2141 if ((NT_SUCCESS(Status)) && (Restart))
2142 {
2143 return BootError;
2144 }
2145
2146 /* Should we not show errors, and thus, reboot? */
2147 Status = BlGetBootOptionBoolean(BlpApplicationEntry.BcdData,
2148 BcdBootMgrBoolean_NoErrorDisplay,
2149 &NoError);
2150 if ((NT_SUCCESS(Status)) && (NoError))
2151 {
2152 return BootError;
2153 }
2154
2155 /* Is there an internal boot error? */
2156 if (BmpInternalBootError)
2157 {
2158 /* Return it -- but it's a pointer? */
2159 return (ULONG)BmpInternalBootError; // ???
2160 }
2161
2162 /* Otherwise, show the menu to see what to do */
2163 EfiPrintf(L"Error menu not yet implemented\r\n");
2164 return BootError;
2165 }
2166
2167 NTSTATUS
2168 BmpCreateDevices (
2169 _In_ PBL_LOADED_APPLICATION_ENTRY BootEntry
2170 )
2171 {
2172 ULONG NextOffset, DataOffset, ListOffset;
2173 PBL_BCD_OPTION Option, ListOption;
2174 BcdElementType ElementType;
2175 PBCD_DEVICE_OPTION BcdDevice;
2176
2177 /* Starting at offset 0, loop every BCD option */
2178 NextOffset = 0;
2179 do
2180 {
2181 /* Get the current option, and its offset */
2182 Option = (PBL_BCD_OPTION)((ULONG_PTR)BootEntry->BcdData + NextOffset);
2183 NextOffset = Option->NextEntryOffset;
2184
2185 /* If it's empty, ignore it */
2186 if (Option->Empty)
2187 {
2188 continue;
2189 }
2190
2191 /* If it's not a device option, ignore it */
2192 ElementType.PackedValue = Option->Type;
2193 if (ElementType.Format != BCD_TYPE_DEVICE)
2194 {
2195 continue;
2196 }
2197
2198 /* Get the data offset */
2199 DataOffset = Option->DataOffset;
2200
2201 /* Extract the device out of it */
2202 BcdDevice = (PBCD_DEVICE_OPTION)((ULONG_PTR)BootEntry->BcdData + DataOffset);
2203
2204 /* If the device is already fully specified, no need to build it */
2205 if (!(BcdDevice->DeviceDescriptor.Flags & 1))
2206 {
2207 continue;
2208 }
2209
2210 /* Otherwise, check if there's any list options as well */
2211 ListOption = NULL;
2212 ListOffset = Option->ListOffset;
2213 if (Option->ListOffset)
2214 {
2215 ListOption = (PBL_BCD_OPTION)((ULONG_PTR)BootEntry->BcdData + ListOffset);
2216 }
2217
2218 /* And now call BlCreateDevice to build the full device descriptor */
2219 EfiPrintf(L"Unspecified devices not yet supported: %p\r\n", ListOption);
2220 return STATUS_NOT_SUPPORTED;
2221 } while (NextOffset != 0);
2222
2223 /* Devices created successfully */
2224 return STATUS_SUCCESS;
2225 }
2226
2227 NTSTATUS
2228 BmpTransferExecution (
2229 _In_ PBL_LOADED_APPLICATION_ENTRY BootEntry,
2230 _Out_ PULONG LaunchCode,
2231 _Out_ PBOOLEAN Recover
2232 )
2233 {
2234 PWCHAR AppPath;
2235 NTSTATUS Status;
2236 PBL_DEVICE_DESCRIPTOR AppDevice;
2237 BL_RETURN_ARGUMENTS ReturnArgs;
2238 BOOLEAN AdvancedOptions;
2239 ULONG AppHandle;
2240
2241 /* Get the application path */
2242 Status = BlGetBootOptionString(BootEntry->BcdData,
2243 BcdLibraryString_ApplicationPath,
2244 &AppPath);
2245 if (!NT_SUCCESS(Status))
2246 {
2247 /* If we couldn't find one, set this to NULL */
2248 AppPath = NULL;
2249 }
2250
2251 /* Check if this is a PXE startup.com */
2252 if (BootEntry->Flags & BL_APPLICATION_ENTRY_STARTUP)
2253 {
2254 #if BL_NET_SUPPORT
2255 /* Do soft reboot to launch it */
2256 Status = BlNetSoftReboot(BootEntry);
2257 #else
2258 EfiPrintf(L"Net boot not supported\r\n");
2259 Status = STATUS_NOT_SUPPORTED;
2260 #endif
2261 /* Nothing else for us to do */
2262 goto Quickie;
2263 }
2264
2265 /* Loop as long as boot was not cancelled */
2266 do
2267 {
2268 /* Load the boot application */
2269 Status = BlImgLoadBootApplication(BootEntry, &AppHandle);
2270
2271 /* Did we not find it? */
2272 if (Status == STATUS_NOT_FOUND)
2273 {
2274 /* Get the device for the boot application */
2275 Status = BlGetBootOptionDevice(BootEntry->BcdData,
2276 BcdLibraryDevice_ApplicationDevice,
2277 &AppDevice,
2278 NULL);
2279 if (NT_SUCCESS(Status))
2280 {
2281 /* Force re-enumeration */
2282 Status = BlFwEnumerateDevice(AppDevice);
2283 }
2284
2285 /* Did re-enumeration work? */
2286 if (!NT_SUCCESS(Status))
2287 {
2288 /* Nope, raise a fatal error */
2289 BmFatalErrorEx(BL_FATAL_ERROR_APP_LOAD,
2290 (ULONG_PTR)AppPath,
2291 Status,
2292 0,
2293 0);
2294 goto Quickie;
2295 }
2296
2297 /* Yes, try booting it again */
2298 Status = BlImgLoadBootApplication(BootEntry, &AppHandle);
2299 }
2300
2301 /* Was boot cancelled?*/
2302 if (Status == STATUS_CANCELLED)
2303 {
2304 /* Should we display the menu, or is there no launch sequence? */
2305 if ((BmGetBootMenuPolicy(BootEntry) != MenuPolicyStandard) ||
2306 !(MiscGetBootOption(BootEntry->BcdData,
2307 BcdLibraryObjectList_RecoverySequence)))
2308 {
2309 /* Bail out, the menu will take care of it */
2310 goto Quickie;
2311 }
2312
2313 /* No menu and there's a sequence, launch it */
2314 *LaunchCode = 4;
2315 *Recover = TRUE;
2316 goto Quickie;
2317 }
2318
2319 /* STATUS_FVE_LOCKED_VOLUME -- bitlocker volume is locked */
2320 if (Status == 0xC0210000)
2321 {
2322 /* Launch recovery mode */
2323 *LaunchCode = 4;
2324 *Recover = TRUE;
2325 goto Quickie;
2326 }
2327
2328 /* Was there some other error launching the boot application? */
2329 if (!NT_SUCCESS(Status))
2330 {
2331 /* Raise a fatal error */
2332 BmFatalErrorEx(BL_FATAL_ERROR_APP_LOAD,
2333 (ULONG_PTR)AppPath,
2334 Status,
2335 0,
2336 0);
2337 goto Quickie;
2338 }
2339
2340 /* Zero out the return arguments */
2341 RtlZeroMemory(&ReturnArgs, sizeof(ReturnArgs));
2342
2343 /* Log to ETW this launch */
2344 //BmpLogApplicationLaunchEvent(&BootEntry->Guid, AppPath);
2345
2346 /* Launch the boot application*/
2347 Status = BlImgStartBootApplication(AppHandle, &ReturnArgs);
2348
2349 #if BL_BITLOCKER_SUPPORT
2350 /* Bitlocker stuff */
2351 BlFveSecureBootCheckpointAppReturn(BootEntry, &ReturnArgs);
2352 #endif
2353
2354 /* Log in the boot status log the launch */
2355 //BlBsdLogEntry(1, 0x12, &BootEntry->Guid, 0x14);
2356
2357 /* Unloac the boot application if we've returned */
2358 BlImgUnloadBootApplication(AppHandle);
2359
2360 /* Keep going unless STATUS_RESTART_BOOT_APPLICATION */
2361 } while (Status != 0xC0000453);
2362
2363 /* We've come back. Assume we need to launch the recovery sequence */
2364 *Recover = TRUE;
2365
2366 /* Why did we get back? */
2367 if (ReturnArgs.Flags & 1)
2368 {
2369 /* Flag 1 -- should we display advanced options? */
2370 Status = BlGetBootOptionBoolean(BootEntry->BcdData,
2371 BcdLibraryBoolean_DisplayAdvancedOptions,
2372 &AdvancedOptions);
2373 if ((NT_SUCCESS(Status)) && (AdvancedOptions))
2374 {
2375 /* Yes, so return with code 2 */
2376 *LaunchCode = 2;
2377 }
2378 else
2379 {
2380 /* No, return with code 1 */
2381 *LaunchCode = 1;
2382 }
2383 }
2384 else if (ReturnArgs.Flags & 4)
2385 {
2386 /* Flag 4 -- unkown */
2387 *LaunchCode = 1;
2388 }
2389 else if (ReturnArgs.Flags & 8)
2390 {
2391 /* Flag 5 -- unkown */
2392 *LaunchCode = 5;
2393 }
2394 else if (ReturnArgs.Flags & 0x10)
2395 {
2396 /* Flag 6 -- unkown */
2397 *LaunchCode = 6;
2398 }
2399 else if (ReturnArgs.Flags & 0x20)
2400 {
2401 /* Flag 7 -- unkown */
2402 *LaunchCode = 7;
2403 }
2404 else if (ReturnArgs.Flags & BL_RETURN_ARGUMENTS_NO_PAE_FLAG)
2405 {
2406 /* PAE is not supported -- refuse to boot */
2407 *Recover = FALSE;
2408 BmFatalErrorEx(BL_FATAL_ERROR_NO_PAE, Status, 0, 0, 0);
2409 }
2410
2411 Quickie:
2412 /* All done, did we have an application path? */
2413 if (AppPath)
2414 {
2415 /* Free it */
2416 BlMmFreeHeap(AppPath);
2417 }
2418
2419 /* Back to the caller now */
2420 return Status;
2421 }
2422
2423 NTSTATUS
2424 BmpLaunchBootEntry (
2425 _In_ PBL_LOADED_APPLICATION_ENTRY BootEntry,
2426 _Out_ PULONG EntryIndex,
2427 _In_ ULONG LaunchCode,
2428 _In_ BOOLEAN LaunchWinRe
2429 )
2430 {
2431 HANDLE BcdHandle;
2432 NTSTATUS Status;
2433 GUID ObjectId;
2434 BOOLEAN DoRecovery, AutoRecovery, DoSequence, RestartOnFailure;
2435 ULONG ErrorCode;
2436 BOOLEAN AdvancedOneTime, EditOneTime;
2437
2438 /* Check if this is the OS loader */
2439 if (BootEntry->Flags & BL_APPLICATION_ENTRY_WINLOAD)
2440 {
2441 /* Check if one-time advanced options should be shown */
2442 if (MiscGetBootOption(BootEntry->BcdData,
2443 BcdOSLoaderBoolean_AdvancedOptionsOneTime))
2444 {
2445 /* Open the BCD */
2446 BcdHandle = NULL;
2447 Status = BmOpenDataStore(BcdHandle);
2448 if (NT_SUCCESS(Status))
2449 {
2450 /* Delete the option from the BCD, so it doesn't happen again */
2451 ObjectId = BootEntry->Guid;
2452 BmPurgeOption(BcdHandle,
2453 &ObjectId,
2454 BcdOSLoaderBoolean_AdvancedOptionsOneTime);
2455 BmCloseDataStore(BcdHandle);
2456 }
2457 }
2458
2459 /* Check if one-time options editor should be shown */
2460 if (MiscGetBootOption(BootEntry->BcdData,
2461 BcdOSLoaderBoolean_OptionsEditOneTime))
2462 {
2463 /* Open the BCD */
2464 BcdHandle = NULL;
2465 Status = BmOpenDataStore(BcdHandle);
2466 if (NT_SUCCESS(Status))
2467 {
2468 /* Delete the option from the BCD, so it doesn't happen again */
2469 ObjectId = BootEntry->Guid;
2470 BmPurgeOption(BcdHandle,
2471 &ObjectId,
2472 BcdOSLoaderBoolean_OptionsEditOneTime);
2473 BmCloseDataStore(BcdHandle);
2474 }
2475 }
2476 }
2477
2478 TryAgain:
2479 /* Disable recovery mode */
2480 DoRecovery = FALSE;
2481
2482 /* Store globally which entry we are trying to boot */
2483 BmpSelectedBootEntry = BootEntry;
2484
2485 /* Create any devices that aren't yet fully defined for this boot entry */
2486 Status = BmpCreateDevices(BootEntry);
2487 if (!NT_SUCCESS(Status))
2488 {
2489 /* That failed -- can we launch the recovery environment? */
2490 if (!LaunchWinRe)
2491 {
2492 return Status;
2493 }
2494
2495 /* Yes, so return with the WinRe launch code */
2496 LaunchCode = 2;
2497 goto Quickie;
2498 }
2499
2500 /* Is this an OS loader/ */
2501 if (BootEntry->Flags & BL_APPLICATION_ENTRY_WINLOAD)
2502 {
2503 /* Is the one-time advanced options menu option present? */
2504 Status = BlGetBootOptionBoolean(BootEntry->BcdData,
2505 BcdOSLoaderBoolean_AdvancedOptionsOneTime,
2506 &AdvancedOneTime);
2507 if (NT_SUCCESS(Status))
2508 {
2509 /* Is it turned on? */
2510 if (AdvancedOneTime)
2511 {
2512 /* Set the option this once */
2513 BlAppendBootOptionBoolean(BootEntry,
2514 BcdLibraryBoolean_DisplayAdvancedOptions);
2515 }
2516 else
2517 {
2518 /* It's not, so disable the option if active */
2519 BlRemoveBootOption(BootEntry->BcdData,
2520 BcdLibraryBoolean_DisplayAdvancedOptions);
2521 }
2522
2523 /* Remove the one-time option. We've already purged it earlier */
2524 BlRemoveBootOption(BootEntry->BcdData,
2525 BcdOSLoaderBoolean_AdvancedOptionsOneTime);
2526 }
2527
2528 /* Is the one-time options editor menu option present? */
2529 Status = BlGetBootOptionBoolean(BootEntry->BcdData,
2530 BcdOSLoaderBoolean_OptionsEditOneTime,
2531 &EditOneTime);
2532 if (NT_SUCCESS(Status))
2533 {
2534 /* Is it turned on? */
2535 if (EditOneTime)
2536 {
2537 /* Set the option this once */
2538 BlAppendBootOptionBoolean(BootEntry,
2539 BcdLibraryBoolean_DisplayOptionsEdit);
2540 }
2541 else
2542 {
2543 /* It's not, so disable the option if active */
2544 BlRemoveBootOption(BootEntry->BcdData,
2545 BcdLibraryBoolean_DisplayOptionsEdit);
2546 }
2547
2548 /* Remove the one-time option. We've already purged it earlier */
2549 BlRemoveBootOption(BootEntry->BcdData,
2550 BcdOSLoaderBoolean_OptionsEditOneTime);
2551 }
2552 }
2553
2554 /* BCD handling done, transfer execution to this entry */
2555 Status = BmpTransferExecution(BootEntry, &LaunchCode, &DoRecovery);
2556 if (!LaunchWinRe)
2557 {
2558 return Status;
2559 }
2560
2561 /* Check if boot was successfull, or cancelled and we're not doing WinRE */
2562 if (((NT_SUCCESS(Status)) || (Status == STATUS_CANCELLED)) && !(DoRecovery))
2563 {
2564 return Status;
2565 }
2566
2567 /* Boot failed -- are we doing recovery? */
2568 if (!DoRecovery)
2569 {
2570 /* Nope, bail out */
2571 LaunchCode = 2;
2572 goto Quickie;
2573 }
2574
2575 Quickie:
2576 /* Get the recovery sequence */
2577 if (MiscGetBootOption(BootEntry->BcdData, BcdLibraryObjectList_RecoverySequence))
2578 {
2579 /* Check if the launch depends on auto-recovery being enabled or not */
2580 if ((LaunchCode == 3) || (LaunchCode == 5) || (LaunchCode == 6))
2581 {
2582 Status = BlGetBootOptionBoolean(BootEntry->BcdData,
2583 BcdLibraryBoolean_AutoRecoveryEnabled,
2584 &AutoRecovery);
2585 if (NT_SUCCESS(Status))
2586 {
2587 /* Override the setting */
2588 DoRecovery = AutoRecovery;
2589 }
2590 }
2591 }
2592 else
2593 {
2594 /* There's no recovery setting */
2595 DoRecovery = FALSE;
2596 }
2597
2598 /* Check if we should restart on failure */
2599 RestartOnFailure = FALSE;
2600 BlGetBootOptionBoolean(BlpApplicationEntry.BcdData,
2601 BcdLibraryBoolean_RestartOnFailure,
2602 &RestartOnFailure);
2603
2604 /* Do the sequence if recovery is on, unless we should restart instead */
2605 DoSequence = RestartOnFailure ? FALSE : DoRecovery;
2606 while (1)
2607 {
2608 /* Are we doing the recovery sequence? */
2609 if (DoSequence)
2610 {
2611 /* Because of automatic recovery? */
2612 if (AutoRecovery)
2613 {
2614 #if BL_BITLOCKER_SUPPORT
2615 /* Do bitlocker stuff */
2616 BlFveRegisterBootEntryForTrustedWimBoot(BootEntry, TRUE);
2617 #endif
2618 }
2619
2620 /* Launch the recovery sequence*/
2621 Status = BmLaunchRecoverySequence(BootEntry, LaunchCode);
2622
2623 /* Was it launched automatically? */
2624 if (AutoRecovery)
2625 {
2626 #if BL_BITLOCKER_SUPPORT
2627 /* Do bitlocker stuff */
2628 BlFveRegisterBootEntryForTrustedWimBoot(BootEntry, FALSE);
2629 #endif
2630
2631 /* No need to do this again */
2632 AutoRecovery = FALSE;
2633 }
2634
2635 /* Did the recovery sequence work? */
2636 if (NT_SUCCESS(Status))
2637 {
2638 /* All good */
2639 return STATUS_SUCCESS;
2640 }
2641
2642 /* Remove the sequence, don't do it again */
2643 BlRemoveBootOption(BootEntry->BcdData, BcdLibraryObjectList_RecoverySequence);
2644 }
2645
2646 /* Recovery sequence also failed, show fatal error */
2647 if (!BmpInternalBootError)
2648 {
2649 BmFatalErrorEx(BL_FATAL_ERROR_GENERIC, Status, 0, 0, 0);
2650 }
2651
2652 /* Display the error menu */
2653 ErrorCode = BmDisplayDumpError(BootEntry, LaunchCode);
2654 BmErrorPurge();
2655
2656 /* See what the user wants to do */
2657 switch (ErrorCode)
2658 {
2659 case TryAgain:
2660 /* Try again */
2661 goto TryAgain;
2662
2663 case NextOs:
2664 /* Boot the next entry*/
2665 break;
2666
2667 case OsSelection:
2668 /* Cancel the boot*/
2669 return STATUS_CANCELLED;
2670
2671 case RecoverOem:
2672 /* Custom OEM recovery -- open the BCD */
2673 Status = BmOpenDataStore(BcdHandle);
2674 if (NT_SUCCESS(Status))
2675 {
2676 /* See what the custom sequence is */
2677 Status = BmProcessCustomAction(BcdHandle, NULL);
2678 }
2679
2680 /* All done, close the BCD */
2681 if (BcdHandle)
2682 {
2683 BmCloseDataStore(BcdHandle);
2684 }
2685 return Status;
2686
2687 case AdvancedOptions:
2688 /* Show the advanced options next iteration */
2689 BlAppendBootOptionBoolean(BootEntry, BcdOSLoaderBoolean_AdvancedOptionsOneTime);
2690 goto TryAgain;
2691
2692 case BootOptions:
2693 /* Show the options editor next iteration */
2694 BlAppendBootOptionBoolean(BootEntry, BcdOSLoaderBoolean_OptionsEditOneTime);
2695 goto TryAgain;
2696
2697 case Recover:
2698 /* Try the recovery sequence next time*/
2699 DoSequence = TRUE;
2700 LaunchCode = 1;
2701 goto TryAgain;
2702
2703 default:
2704 /* Something unknown */
2705 return STATUS_CANCELLED;
2706 }
2707 }
2708
2709 /* We are booting the next OS, so return success as to not kill the boot */
2710 return STATUS_SUCCESS;
2711 }
2712
2713 /*++
2714 * @name BmMain
2715 *
2716 * The BmMain function implements the Windows Boot Application entrypoint for
2717 * the Boot Manager.
2718 *
2719 * @param BootParameters
2720 * Pointer to the Boot Application Parameter Block.
2721 *
2722 * @return NT_SUCCESS if the image was loaded correctly, relevant error code
2723 * otherwise.
2724 *
2725 *--*/
2726 NTSTATUS
2727 NTAPI
2728 BmMain (
2729 _In_ PBOOT_APPLICATION_PARAMETER_BLOCK BootParameters
2730 )
2731 {
2732 NTSTATUS Status, LibraryStatus;
2733 BL_LIBRARY_PARAMETERS LibraryParameters;
2734 PBL_RETURN_ARGUMENTS ReturnArguments;
2735 PGUID AppIdentifier;
2736 HANDLE BcdHandle, ResumeBcdHandle;
2737 PBL_BCD_OPTION EarlyOptions;
2738 PWCHAR Stylesheet;
2739 BOOLEAN XmlLoaded, DisableIntegrity, TestSigning, PersistBootSequence;
2740 BOOLEAN RebootOnError, CustomActions;
2741 ULONG SequenceId;
2742 PBL_LOADED_APPLICATION_ENTRY BootEntry;
2743 PGUID SequenceList;
2744 ULONG SequenceListCount;
2745 PBL_LOADED_APPLICATION_ENTRY* BootSequence;
2746 ULONG BootIndex;
2747 BOOLEAN ExitBootManager;
2748 BOOLEAN BootFailed;
2749 BOOLEAN BootOk;
2750 ULONG SequenceCount;
2751 BOOLEAN GetEntry;
2752 EfiPrintf(L"ReactOS UEFI Boot Manager Initializing...\r\n");
2753
2754 /* Reading the BCD can change this later on */
2755 RebootOnError = FALSE;
2756
2757 /* Save the start/end-of-POST time */
2758 ApplicationStartTime = __rdtsc();
2759 PostTime = ApplicationStartTime;
2760
2761 /* Setup the boot library parameters for this application */
2762 BlSetupDefaultParameters(&LibraryParameters);
2763 LibraryParameters.TranslationType = BlNone;
2764 LibraryParameters.LibraryFlags = 0x400 | 0x8;
2765 LibraryParameters.MinimumAllocationCount = 16;
2766 LibraryParameters.MinimumHeapSize = 512 * 1024;
2767
2768 /* Initialize the boot library */
2769 Status = BlInitializeLibrary(BootParameters, &LibraryParameters);
2770 if (!NT_SUCCESS(Status))
2771 {
2772 /* Check for failure due to invalid application entry */
2773 if (Status != STATUS_INVALID_PARAMETER_9)
2774 {
2775 /* Specifically print out what happened */
2776 EfiPrintf(L"BlInitializeLibrary failed 0x%x\r\n", Status);
2777 }
2778
2779 /* Go to exit path */
2780 goto Quickie;
2781 }
2782
2783 /* Get the application identifier */
2784 AppIdentifier = BlGetApplicationIdentifier();
2785 if (!AppIdentifier)
2786 {
2787 /* None was given, so set our default one */
2788 AppIdentifier = (PGUID)&GUID_WINDOWS_BOOTMGR;
2789 }
2790
2791 /* Save our identifier */
2792 BmApplicationIdentifier = *AppIdentifier;
2793
2794 /* Initialize the file system to open a handle to our root boot directory */
2795 BmFwInitializeBootDirectoryPath();
2796
2797 /* Load and initialize the boot configuration database (BCD) */
2798 Status = BmOpenDataStore(&BcdHandle);
2799 if (NT_SUCCESS(Status))
2800 {
2801 /* Copy the boot options */
2802 Status = BlCopyBootOptions(BlpApplicationEntry.BcdData, &EarlyOptions);
2803 if (NT_SUCCESS(Status))
2804 {
2805 /* Update them */
2806 Status = BmpUpdateApplicationOptions(BcdHandle);
2807 if (!NT_SUCCESS(Status))
2808 {
2809 /* Log a fatal error */
2810 BmFatalErrorEx(BL_FATAL_ERROR_BCD_PARSE,
2811 (ULONG_PTR)L"\\BCD",
2812 Status,
2813 0,
2814 0);
2815 }
2816 }
2817 }
2818
2819 #ifdef _SECURE_BOOT
2820 /* Initialize the secure boot machine policy */
2821 Status = BmSecureBootInitializeMachinePolicy();
2822 if (!NT_SUCCESS(Status))
2823 {
2824 BmFatalErrorEx(BL_FATAL_ERROR_SECURE_BOOT, Status, 0, 0, 0);
2825 }
2826 #endif
2827
2828 /* Copy the library parameters and add the re-initialization flag */
2829 RtlCopyMemory(&LibraryParameters,
2830 &BlpLibraryParameters,
2831 sizeof(LibraryParameters));
2832 LibraryParameters.LibraryFlags |= (BL_LIBRARY_FLAG_REINITIALIZE_ALL |
2833 BL_LIBRARY_FLAG_REINITIALIZE);
2834
2835 /* Now that we've parsed the BCD, re-initialize the library */
2836 LibraryStatus = BlInitializeLibrary(BootParameters, &LibraryParameters);
2837 if (!NT_SUCCESS(LibraryStatus) && (NT_SUCCESS(Status)))
2838 {
2839 Status = LibraryStatus;
2840 }
2841
2842 /* Initialize firmware-specific memory regions */
2843 BmFwMemoryInitialize();
2844
2845 /* Initialize the boot status data log (BSD) */
2846 BmpInitializeBootStatusDataLog();
2847
2848 /* Find our XSL stylesheet */
2849 Stylesheet = BlResourceFindHtml();
2850 if (!Stylesheet)
2851 {
2852 /* Awe, no XML. This is actually fatal lol. Can't boot without XML. */
2853 Status = STATUS_NOT_FOUND;
2854 EfiPrintf(L"BlResourceFindMessage failed 0x%x\r\n", STATUS_NOT_FOUND);
2855 goto Quickie;
2856 }
2857
2858 /* Initialize the XML Engine (as a side-effect, resets cursor) */
2859 Status = BlXmiInitialize(Stylesheet);
2860 if (!NT_SUCCESS(Status))
2861 {
2862 EfiPrintf(L"\r\nBlXmiInitialize failed 0x%x\r\n", Status);
2863 goto Failure;
2864 }
2865 XmlLoaded = TRUE;
2866
2867 /* Check if there's an active bitmap visible */
2868 if (!BlDisplayValidOemBitmap())
2869 {
2870 /* Nope, make the screen black using BGFX */
2871 if (!NT_SUCCESS(BmpBgDisplayClearScreen(0xFF000000)))
2872 {
2873 /* BGFX isn't active, use standard display */
2874 BlDisplayClearScreen();
2875 }
2876 }
2877
2878 #ifdef _BIT_LOCKER_
2879 /* Bitlocker will take over screen UI if enabled */
2880 FveDisplayScreen = BmFveDisplayScreen;
2881 #endif
2882
2883 /* Check if any bypass options are enabled */
2884 BlImgQueryCodeIntegrityBootOptions(&BlpApplicationEntry,
2885 &DisableIntegrity,
2886 &TestSigning);
2887 if (!DisableIntegrity)
2888 {
2889 /* Integrity checks are enabled, so validate our signature */
2890 Status = BmFwVerifySelfIntegrity();
2891 if (!NT_SUCCESS(Status))
2892 {
2893 /* Signature invalid, fail boot */
2894 goto Failure;
2895 }
2896 }
2897
2898 /* Write out the first XML tag */
2899 BlXmiWrite(L"<bootmgr/>");
2900
2901 /* Check for factory resset */
2902 BlSecureBootCheckForFactoryReset();
2903
2904 /* Load the revocation list */
2905 Status = BmFwRegisterRevocationList();
2906 if (!NT_SUCCESS(Status))
2907 {
2908 goto Failure;
2909 }
2910
2911 /* Register our custom progress routine */
2912 BlUtlRegisterProgressRoutine();
2913
2914 /* Display state is not currently cached */
2915 BmDisplayStateCached = FALSE;
2916
2917 /* Check if we need to resume from hibernate */
2918 Status = BmResumeFromHibernate(&ResumeBcdHandle);
2919 if (!NT_SUCCESS(Status))
2920 {
2921 goto Failure;
2922 }
2923
2924 #ifdef BL_NET_SUPPORT
2925 /* Register multicast printing routine */
2926 BlUtlRegisterMulticastRoutine();
2927 #endif
2928
2929 /* Check if restart on failure is enabled */
2930 BlGetBootOptionBoolean(BlpApplicationEntry.BcdData,
2931 BcdLibraryBoolean_RestartOnFailure,
2932 &RebootOnError);
2933
2934 /* Check if the boot sequence is persisted */
2935 Status = BlGetBootOptionBoolean(BlpApplicationEntry.BcdData,
2936 BcdBootMgrBoolean_PersistBootSequence,
2937 &PersistBootSequence);
2938 if (!NT_SUCCESS(Status))
2939 {
2940 /* It usually is */
2941 PersistBootSequence = TRUE;
2942 }
2943
2944 /* Check if there's custom actions to take */
2945 Status = BlGetBootOptionBoolean(BlpApplicationEntry.BcdData,
2946 BcdBootMgrBoolean_ProcessCustomActionsFirst,
2947 &CustomActions);
2948 if ((NT_SUCCESS(Status)) && (CustomActions))
2949 {
2950 /* We don't suppport this yet */
2951 EfiPrintf(L"Not implemented\r\n");
2952 Status = STATUS_NOT_IMPLEMENTED;
2953 goto Failure;
2954 }
2955
2956 //BlResourceFindMessage(BM_MSG_TEST);
2957
2958 /* At last, enter the boot selection stage */
2959 SequenceId = 0;
2960 GetEntry = FALSE;
2961 BootFailed = FALSE;
2962 SequenceList = NULL;
2963 BootSequence = NULL;
2964 SequenceCount = 0;
2965 while (1)
2966 {
2967 /* We don't have a boot entry nor a sequence ID */
2968 BootEntry = NULL;
2969 BootOk = FALSE;
2970
2971 /* Do we have a hardcoded boot sequence set? */
2972 if (!(BootSequence) && !(GetEntry))
2973 {
2974 /* Not yet, read the BCD to see if one is there */
2975 Status = BlGetBootOptionGuidList(BlpApplicationEntry.BcdData,
2976 BcdBootMgrObjectList_BootSequence,
2977 &SequenceList,
2978 &SequenceListCount);
2979 if (NT_SUCCESS(Status))
2980 {
2981 /* A GUID list for the boot sequence is set. Extract it */
2982 Status = BmGetBootSequence(BcdHandle,
2983 SequenceList,
2984 SequenceListCount,
2985 BL_APPLICATION_ENTRY_FIXED_SEQUENCE,
2986 &BootSequence,
2987 &SequenceCount);
2988 if (NT_SUCCESS(Status))
2989 {
2990 /* Don't get stuck in a loop repeating this sequence */
2991 BlRemoveBootOption(BlpApplicationEntry.BcdData,
2992 BcdBootMgrObjectList_BootSequence);
2993
2994 /* But do check if we should persist it */
2995 if (PersistBootSequence)
2996 {
2997 /* Yes -- so go select an entry now */
2998 GetEntry = TRUE;
2999 }
3000 else
3001 {
3002 /* We shouldn't, so wipe it from the BCD too */
3003 Status = BmPurgeOption(BcdHandle,
3004 &BmApplicationIdentifier,
3005 BcdBootMgrObjectList_BootSequence);
3006 if (!NT_SUCCESS(Status))
3007 {
3008 /* Well that failed */
3009 goto LoopQuickie;
3010 }
3011 }
3012 }
3013 }
3014 else
3015 {
3016 /* No boot entry sequence for us */
3017 BootSequence = NULL;
3018 }
3019 }
3020
3021 /* Do we have a sequence active, and are we still processing it? */
3022 if ((BootSequence) && ((GetEntry) || (SequenceId < SequenceCount)))
3023 {
3024 /* Extract the next entry in the sequence */
3025 BootEntry = BootSequence[SequenceId];
3026 BootSequence[SequenceId] = NULL;
3027
3028 /* Move to the next entry for next time */
3029 SequenceId++;
3030
3031 /* Unless there won't be a a next time? */
3032 if (SequenceId == SequenceCount)
3033 {
3034 /* Clean up, it's the last entry */
3035 BlMmFreeHeap(BootSequence);
3036 BootSequence = NULL;
3037 }
3038 }
3039 else
3040 {
3041 /* Get the selected boot entry from the user */
3042 ExitBootManager = FALSE;
3043 Status = BmpGetSelectedBootEntry(BcdHandle,
3044 &BootEntry,
3045 &BootIndex,
3046 &ExitBootManager);
3047 if (!(NT_SUCCESS(Status)) || (ExitBootManager))
3048 {
3049 /* Selection failed, or user wants to exit */
3050 goto LoopQuickie;
3051 }
3052 }
3053
3054 /* Did we have a BCD open? */
3055 if (BcdHandle)
3056 {
3057 /* Close it, we'll be opening a new one */
3058 BmCloseDataStore(BcdHandle);
3059 BcdHandle = NULL;
3060 }
3061
3062 /* Launch the selected entry */
3063 Status = BmpLaunchBootEntry(BootEntry, &BootIndex, 0, TRUE);
3064 if (NT_SUCCESS(Status))
3065 {
3066 /* Boot worked, uncache display and process the bad memory list */
3067 BmDisplayStateCached = FALSE;
3068 BmpProcessBadMemory();
3069 }
3070 else
3071 {
3072 /* Boot failed -- was it user driven? */
3073 if (Status != STATUS_CANCELLED)
3074 {
3075 /* Nope, remember that booting failed */
3076 BootFailed = TRUE;
3077 goto LoopQuickie;
3078 }
3079
3080 /* Yes -- the display is still valid */
3081 BmDisplayStateCached = TRUE;
3082 }
3083
3084 /* Reopen the BCD */
3085 Status = BmOpenDataStore(&BcdHandle);
3086 if (!NT_SUCCESS(Status))
3087 {
3088 break;
3089 }
3090
3091 /* Put the BCD options back into our entry */
3092 BlReplaceBootOptions(&BlpApplicationEntry, EarlyOptions);
3093
3094 /* Update our options one more time */
3095 Status = BmpUpdateApplicationOptions(BcdHandle);
3096 if (NT_SUCCESS(Status))
3097 {
3098 /* Boot was 100% OK */
3099 BootOk = TRUE;
3100 }
3101
3102 LoopQuickie:
3103 /* Did we have a boot entry? */
3104 if (BootEntry)
3105 {
3106 /* We can destroy it now */
3107 BlDestroyBootEntry(BootEntry);
3108 }
3109
3110 /* Is this the success path? */
3111 if (NT_SUCCESS(Status))
3112 {
3113 /* Did we actually boot something? */
3114 if (!BootOk)
3115 {
3116 /* Bope, fail out */
3117 break;
3118 }
3119 }
3120
3121 /* This is the failure path... should we reboot? */
3122 if (RebootOnError)
3123 {
3124 break;
3125 }
3126 };
3127
3128 Failure:
3129 if (!BootFailed)
3130 {
3131 /* Check if we got here due to an internal error */
3132 if (BmpInternalBootError)
3133 {
3134 /* If XML is available, display the error */
3135 if (XmlLoaded)
3136 {
3137 //BmDisplayDumpError(0, 0);
3138 //BmErrorPurge();
3139 }
3140
3141 /* Don't do a fatal error -- return back to firmware */
3142 goto Quickie;
3143 }
3144 }
3145
3146 /* Log a general fatal error once we're here */
3147 BmFatalErrorEx(BL_FATAL_ERROR_GENERIC, Status, 0, 0, 0);
3148
3149 Quickie:
3150 /* Check if we should reboot */
3151 if ((RebootOnError) ||
3152 (BlpApplicationEntry.Flags & BL_APPLICATION_ENTRY_REBOOT_ON_ERROR))
3153 {
3154 /* Reboot the box */
3155 BlFwReboot();
3156 Status = STATUS_SUCCESS;
3157 }
3158 else
3159 {
3160 /* Return back to the caller with the error argument encoded */
3161 ReturnArguments = (PVOID)((ULONG_PTR)BootParameters + BootParameters->ReturnArgumentsOffset);
3162 ReturnArguments->Version = BL_RETURN_ARGUMENTS_VERSION;
3163 ReturnArguments->Status = Status;
3164
3165 /* Tear down the boot library */
3166 BlDestroyLibrary();
3167 }
3168
3169 /* Return back status */
3170 return Status;
3171 }
3172