xref: /reactos/boot/environ/app/bootmgr/bootmgr.c (revision f308c6a2)
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     SIZE_T 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 = RtlSIZETAdd(i, 1, &AppPathLength);
261         if (NT_SUCCESS(Status))
262         {
263             /* Check if it's safe to multiply by two */
264             Status = RtlSIZETMult(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 outgoing 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 STATUS_FVE_LOCKED_VOLUME:
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     SIZE_T BootDirLength, PathLength;
651 
652     /* Compute the length of the directory, and add a NUL */
653     BootDirLength = wcslen(BootDirectory);
654     Status = RtlSIZETAdd(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 = RtlSIZETAdd(PathLength, BootDirLength, &PathLength);
663     if (!NT_SUCCESS(Status))
664     {
665         goto Quickie;
666     }
667 
668     /* Convert to bytes */
669     Status = RtlSIZETMult(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     SIZE_T 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 = RtlSIZETAdd(PathLength, 1, &PathLength);
799     if (!NT_SUCCESS(Status))
800     {
801         goto Quickie;
802     }
803 
804     /* Convert to bytes */
805     Status = RtlSIZETMult(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 = RtlSIZETAdd(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 "below 1MB" attribute */
1039     Status = MmPapAllocatePhysicalPagesInRange(&PhysicalAddress,
1040                                                BlApplicationReserved,
1041                                                1,
1042                                                BlMemoryBelow1MB,
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 directly */
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,
1361                                                           BcdLibraryString_ApplicationPath,
1362                                                           LoaderPath);
1363                         if (!NT_SUCCESS(Status))
1364                         {
1365                             goto Quickie;
1366                         }
1367 
1368                         /* We have a path now */
1369                         HavePath = TRUE;
1370                     }
1371                     break;
1372 
1373                 /* A hibernate-resume application */
1374                 case BCD_APPLICATION_TYPE_RESUME:
1375                     BootEntry->Flags |= BL_APPLICATION_ENTRY_WINRESUME;
1376                     break;
1377 
1378                 /* An older OS NTLDR */
1379                 case BCD_APPLICATION_TYPE_NTLDR:
1380                     BootEntry->Flags |= BL_APPLICATION_ENTRY_NTLDR;
1381                     break;
1382 
1383                 /* An older OS SETUPLDR */
1384                 case BCD_APPLICATION_TYPE_SETUPLDR:
1385                     BootEntry->Flags |= BL_APPLICATION_ENTRY_SETUPLDR;
1386                     break;
1387 
1388                 /* A 3rd party/Win9x boot sector */
1389                 case BCD_APPLICATION_TYPE_BOOTSECTOR:
1390                     BootEntry->Flags |= BL_APPLICATION_ENTRY_BOOTSECTOR;
1391                     break;
1392 
1393                 /* Something else entirely */
1394                 default:
1395                     break;
1396             }
1397         }
1398 
1399         /* We better have a path by now */
1400         if (!HavePath)
1401         {
1402             Status = STATUS_UNSUCCESSFUL;
1403             goto LoopQuickie;
1404         }
1405 
1406         /* Check if this is a real mode startup.com */
1407         if ((ObjectType.Application.ObjectCode == BCD_OBJECT_TYPE_APPLICATION) &&
1408             (ObjectType.Application.ImageCode == BCD_IMAGE_TYPE_REAL_MODE) &&
1409             (ObjectType.Application.ApplicationCode == BCD_APPLICATION_TYPE_STARTUPCOM))
1410         {
1411             /* Check if PXE soft reboot will occur */
1412             Status = BlGetBootOptionBoolean(Options,
1413                                             BcdStartupBoolean_PxeSoftReboot,
1414                                             &SoftReboot);
1415             if ((NT_SUCCESS(Status)) && (SoftReboot))
1416             {
1417                 /* Then it's a valid startup.com entry */
1418                 BootEntry->Flags |= BL_APPLICATION_ENTRY_STARTUP;
1419             }
1420         }
1421 
1422 LoopQuickie:
1423         /* All done with this entry -- did we have BCD options? */
1424         if (Options)
1425         {
1426             /* Free them, they're part of the entry now */
1427             BlMmFreeHeap(Options);
1428             Options = NULL;
1429         }
1430 
1431         /* Did we fail anywhere? */
1432         if (!NT_SUCCESS(Status))
1433         {
1434             /* Yep -- did we fail with an active boot entry? */
1435             if (BootEntry)
1436             {
1437                 /* Destroy it */
1438                 BlDestroyBootEntry(BootEntry);
1439                 BootSequence[BootIndex] = NULL;
1440             }
1441         }
1442         else
1443         {
1444             /* It worked, so populate the next index now */
1445             BootIndex++;
1446         }
1447 
1448         /* And move to the next GUID in the sequence list */
1449         SequenceList++;
1450     }
1451 
1452 Quickie:
1453     /* All done now -- did we have any BCD options? */
1454     if (Options)
1455     {
1456         /* Free them */
1457         BlMmFreeHeap(Options);
1458     }
1459 
1460     /* Return the status */
1461     return Status;
1462 }
1463 
1464 NTSTATUS
1465 BmGetBootSequence (
1466     _In_ HANDLE BcdHandle,
1467     _In_ PGUID SequenceList,
1468     _In_ ULONG SequenceListCount,
1469     _In_ ULONG Flags,
1470     _Out_ PBL_LOADED_APPLICATION_ENTRY** BootSequence,
1471     _Out_ PULONG SequenceCount
1472     )
1473 {
1474     PBL_LOADED_APPLICATION_ENTRY* Sequence;
1475     ULONG Count = SequenceListCount;
1476     NTSTATUS Status;
1477 
1478     /* Allocate the sequence list */
1479     Sequence = BlMmAllocateHeap(SequenceListCount * sizeof(*Sequence));
1480     if (!Sequence)
1481     {
1482         return STATUS_NO_MEMORY;
1483     }
1484 
1485     /* Populate the sequence list */
1486     Status = BmpPopulateBootEntryList(BcdHandle,
1487                                       SequenceList,
1488                                       Flags,
1489                                       Sequence,
1490                                       &Count);
1491     if (!NT_SUCCESS(Status))
1492     {
1493         /* Free the list on failure */
1494         BlMmFreeHeap(Sequence);
1495     }
1496     else
1497     {
1498         /* Otherwise, set success and return the list and count */
1499         Status = STATUS_SUCCESS;
1500         *BootSequence = Sequence;
1501         *SequenceCount = Count;
1502     }
1503 
1504     /* All done */
1505     return Status;
1506 }
1507 
1508 NTSTATUS
1509 BmEnumerateBootEntries (
1510     _In_ HANDLE BcdHandle,
1511     _Out_ PBL_LOADED_APPLICATION_ENTRY **BootSequence,
1512     _Out_ PULONG SequenceCount
1513     )
1514 {
1515     NTSTATUS Status;
1516     ULONG BootIndex, BootIniCount, BootEntryCount, BcdCount;
1517     PBL_LOADED_APPLICATION_ENTRY* Sequence;
1518     PGUID DisplayOrder;
1519     GUID DefaultObject;
1520     BOOLEAN UseDisplayList;
1521 
1522     /* Initialize locals */
1523     BootIndex = 0;
1524 
1525     /* First try to get the display list, if any */
1526     UseDisplayList = TRUE;
1527     Status = BlGetBootOptionGuidList(BlpApplicationEntry.BcdData,
1528                                      BcdBootMgrObjectList_DisplayOrder,
1529                                      &DisplayOrder,
1530                                      &BcdCount);
1531     if (!NT_SUCCESS(Status))
1532     {
1533         /* No list, get the default entry instead */
1534         Status = BlGetBootOptionGuid(BlpApplicationEntry.BcdData,
1535                                      BcdBootMgrObject_DefaultObject,
1536                                      &DefaultObject);
1537         if (NT_SUCCESS(Status))
1538         {
1539             /* Set the array to just our entry */
1540             UseDisplayList = FALSE;
1541             BcdCount = 1;
1542             DisplayOrder = &DefaultObject;
1543         }
1544         else
1545         {
1546             /* No default list either, return success but no entries */
1547             *BootSequence = NULL;
1548             *SequenceCount = 0;
1549             Status = STATUS_SUCCESS;
1550             DisplayOrder = NULL;
1551             goto Quickie;
1552         }
1553     }
1554 
1555     /* Check if boot.ini was used */
1556     BootIniCount = 0;
1557     if (BmBootIniUsed)
1558     {
1559         /* Get the entries from it */
1560         EfiPrintf(L"Boot.ini not supported\r\n");
1561         BootIniCount = 0;//BmBootIniGetEntryCount();
1562     }
1563 
1564     /* Allocate an array large enough for the combined boot entries */
1565     BootEntryCount = BootIniCount + BcdCount;
1566     Sequence = BlMmAllocateHeap(BootEntryCount * sizeof(*Sequence));
1567     if (!Sequence)
1568     {
1569         Status = STATUS_NO_MEMORY;
1570         goto Quickie;
1571     }
1572 
1573     /* Zero it out */
1574     RtlZeroMemory(Sequence, BootEntryCount * sizeof(*Sequence));
1575 
1576     /* Check if we had BCD entries */
1577     if (BcdCount)
1578     {
1579         /* Populate the list of bootable entries */
1580         Status = BmpPopulateBootEntryList(BcdHandle,
1581                                           DisplayOrder,
1582                                           BL_APPLICATION_ENTRY_DISPLAY_ORDER,
1583                                           Sequence,
1584                                           &BcdCount);
1585         if (!NT_SUCCESS(Status))
1586         {
1587             /* Bail out */
1588             goto Quickie;
1589         }
1590     }
1591 
1592     /* Check if we had boot.ini entries */
1593     if (BootIniCount)
1594     {
1595         /* TODO */
1596         EfiPrintf(L"Boot.ini not supported\r\n");
1597     }
1598 
1599     /* Return success and the sequence + count populated */
1600     Status = STATUS_SUCCESS;
1601     *BootSequence = Sequence;
1602     *SequenceCount = BootIniCount + BcdCount;
1603 
1604 Quickie:
1605     /* Check if we had allocated a GUID list */
1606     if ((UseDisplayList) && (DisplayOrder))
1607     {
1608         /* Free it */
1609         BlMmFreeHeap(DisplayOrder);
1610     }
1611 
1612     /* Check if this is the failure path */
1613     if (!(NT_SUCCESS(Status)) && (Sequence))
1614     {
1615         /* Loop the remaining boot entries */
1616         while (BootIndex < BootEntryCount)
1617         {
1618             /* Check if it had been allocated */
1619             if (Sequence[BootIndex])
1620             {
1621                 /* Free it */
1622                 BlMmFreeHeap(Sequence[BootIndex]);
1623             }
1624 
1625             /* Next*/
1626             BootIndex++;
1627         }
1628 
1629         /* Free the whole sequence now */
1630         BlMmFreeHeap(Sequence);
1631     }
1632 
1633     /* All done, return the result */
1634     return Status;
1635 }
1636 
1637 VOID
1638 BmpGetDefaultBootEntry (
1639     _In_ PBL_LOADED_APPLICATION_ENTRY* Sequence,
1640     _In_ ULONG Count,
1641     _Out_ PBL_LOADED_APPLICATION_ENTRY* DefaultEntry,
1642     _Out_ PULONG DefaultIndex
1643     )
1644 {
1645     GUID DefaultObject;
1646     NTSTATUS Status;
1647     ULONG BootIndex;
1648 
1649     /* Assume no default */
1650     *DefaultEntry = *Sequence;
1651     *DefaultIndex = 0;
1652 
1653     /* Nothing to do if there's just one entry */
1654     if (Count == 1)
1655     {
1656         return;
1657     }
1658 
1659     /* Get the default object, bail out if there isn't one */
1660     Status = BlGetBootOptionGuid(BlpApplicationEntry.BcdData,
1661                                  BcdBootMgrObject_DefaultObject,
1662                                  &DefaultObject);
1663     if (!(NT_SUCCESS(Status)) || !(Count))
1664     {
1665         return;
1666     }
1667 
1668     /* Scan the boot sequence */
1669     for (BootIndex = 0; BootIndex < Count; BootIndex++)
1670     {
1671         /* Find one that matches the default */
1672         if (RtlEqualMemory(&Sequence[BootIndex]->Guid,
1673                            &DefaultObject,
1674                            sizeof(GUID)))
1675         {
1676             /* Return it */
1677             *DefaultEntry = Sequence[BootIndex];
1678             *DefaultIndex = BootIndex;
1679             return;
1680         }
1681     }
1682 }
1683 
1684 BL_MENU_POLICY
1685 BmGetBootMenuPolicy (
1686     _In_ PBL_LOADED_APPLICATION_ENTRY BootEntry
1687     )
1688 {
1689     NTSTATUS Status;
1690     BOOLEAN EmsEnabled;
1691     ULONGLONG BootMenuPolicy;
1692     ULONG OptionId;
1693 
1694     /* Check if EMS is enabled */
1695     Status = BlGetBootOptionBoolean(BlpApplicationEntry.BcdData,
1696                                     BcdOSLoaderBoolean_EmsEnabled,
1697                                     &EmsEnabled);
1698     if ((NT_SUCCESS(Status)) && (EmsEnabled))
1699     {
1700         /* No boot menu */
1701         return MenuPolicyLegacy;
1702     }
1703 
1704     /* Check what entry we are looking at */
1705     if (!BootEntry)
1706     {
1707         /* No entry, pick the selected one */
1708         BootEntry = BmpSelectedBootEntry;
1709     }
1710 
1711     /* Do we still not have an entry? */
1712     if (!BootEntry)
1713     {
1714         /* Show the menu */
1715         return MenuPolicyStandard;
1716     }
1717 
1718     /* Check if this is an OS loader */
1719     BootMenuPolicy = 0;
1720     if (BootEntry->Flags & BL_APPLICATION_ENTRY_WINLOAD)
1721     {
1722         /* Use the correct option ID */
1723         OptionId = BcdOSLoaderInteger_BootMenuPolicy;
1724     }
1725     else
1726     {
1727         /* Check if this is an OS resumer */
1728         if (!(BootEntry->Flags & BL_APPLICATION_ENTRY_WINRESUME))
1729         {
1730             /* Nope, so no reason for a menu */
1731             return MenuPolicyLegacy;
1732         }
1733 
1734         /* Use the correct option ID */
1735         OptionId = BcdResumeInteger_BootMenuPolicy;
1736     }
1737 
1738     /* Check the option ID for the boot menu policy */
1739     Status = BlGetBootOptionInteger(BootEntry->BcdData,
1740                                     OptionId,
1741                                     &BootMenuPolicy);
1742     if (NT_SUCCESS(Status))
1743     {
1744         /* We have one, return it */
1745         return BootMenuPolicy;
1746     }
1747 
1748     /* No policy, so assume no menu */
1749     return MenuPolicyLegacy;
1750 }
1751 
1752 VOID
1753 BmDisplayGetBootMenuStatus (
1754     _Out_ PL_MENU_STATUS MenuStatus
1755     )
1756 {
1757     /* For now, don't support key input at all */
1758     MenuStatus->AsULong = 0;
1759     MenuStatus->OemKey = UNICODE_NULL;
1760     MenuStatus->BootIndex = -1;
1761 }
1762 
1763 NTSTATUS
1764 BmProcessCustomAction (
1765     _In_ HANDLE BcdHandle,
1766     _In_ PWCHAR ActionKey
1767     )
1768 {
1769     EfiPrintf(L"Custom actions not yet handled\r\n");
1770     return STATUS_NOT_IMPLEMENTED;
1771 }
1772 
1773 VOID
1774 BmpProcessBootEntry (
1775     _In_ HANDLE BcdHandle,
1776     _In_ PBL_LOADED_APPLICATION_ENTRY BootEntry,
1777     _Out_ PBOOLEAN ExitBootManager
1778     )
1779 {
1780     BL_MENU_STATUS MenuStatus;
1781 
1782     /* Don't exit */
1783     *ExitBootManager = FALSE;
1784 
1785     /* If the legacy menu must be shown, or if we have a boot entry */
1786     if ((BmGetBootMenuPolicy(BootEntry) != MenuPolicyStandard) || (BootEntry))
1787     {
1788         /* Check if any key has been pressed */
1789         BmDisplayGetBootMenuStatus(&MenuStatus);
1790         if (MenuStatus.AnyKey)
1791         {
1792             /* Was the exit key pressed? */
1793             if (MenuStatus.Exit)
1794             {
1795                 /* Don't display a menu, and exit */
1796                 *ExitBootManager = TRUE;
1797                 BmpDisplayBootMenu = FALSE;
1798             }
1799             else if (MenuStatus.OemKey)
1800             {
1801                 /* Process the OEM key action */
1802                 BmProcessCustomAction(BcdHandle, &MenuStatus.KeyValue);
1803             }
1804             else
1805             {
1806                 /* Process other keys */
1807                 EfiPrintf(L"TODO\r\n");
1808             }
1809         }
1810     }
1811 }
1812 
1813 NTSTATUS
1814 BmpGetSelectedBootEntry (
1815     _In_ HANDLE BcdHandle,
1816     _Out_ PBL_LOADED_APPLICATION_ENTRY* SelectedBootEntry,
1817     _Out_ PULONG EntryIndex,
1818     _Out_ PBOOLEAN ExitBootManager
1819     )
1820 {
1821     NTSTATUS Status;
1822     PBL_LOADED_APPLICATION_ENTRY* Sequence;
1823     PBL_LOADED_APPLICATION_ENTRY Entry, SelectedEntry;
1824     ULONG Count, BootIndex, SelectedIndex;
1825   //  BOOLEAN FoundFailedEntry;
1826     ULONGLONG Timeout;
1827 
1828     /* Initialize locals */
1829     BootIndex = 0;
1830     Count = 0;
1831     Sequence = NULL;
1832     SelectedEntry = NULL;
1833 
1834     /* Enumerate all the boot entries */
1835     Status = BmEnumerateBootEntries(BcdHandle, &Sequence, &Count);
1836     if (!NT_SUCCESS(Status))
1837     {
1838         /* Bail out if we failed */
1839         goto Quickie;
1840     }
1841 
1842     /* Check if there are no entries */
1843     if (!Count)
1844     {
1845         /* This is fatal -- kill the system */
1846         Status = STATUS_FILE_INVALID;
1847         BmFatalErrorEx(BL_FATAL_ERROR_BCD_ENTRIES, (ULONG_PTR)L"\\BCD", Status, 0, 0);
1848         goto Quickie;
1849     }
1850 
1851     /* Check if we don't yet have an array of failed boot entries */
1852     if (!BmpFailedBootEntries)
1853     {
1854         /* Allocate it */
1855         BmpFailedBootEntries = BlMmAllocateHeap(Count);
1856         if (BmpFailedBootEntries)
1857         {
1858             /* Zero it out */
1859             RtlZeroMemory(BmpFailedBootEntries, Count);
1860         }
1861     }
1862 
1863     /* Check if we have a hardcoded boot override */
1864     if (BmBootEntryOverridePresent)
1865     {
1866         EfiPrintf(L"Hard-coded boot override mode not supported\r\n");
1867     }
1868 
1869     /* Log the OS count */
1870     //BlLogEtwWrite(BOOT_BOOTMGR_MULTI_OS_COUNT);
1871 
1872     /* Check if the display is already active and cached */
1873     if (!BmDisplayStateCached)
1874     {
1875         /* Check if we should display a boot menu */
1876         Status = BlGetBootOptionBoolean(BlpApplicationEntry.BcdData,
1877                                         BcdBootMgrBoolean_DisplayBootMenu,
1878                                         &BmpDisplayBootMenu);
1879         if (!NT_SUCCESS(Status))
1880         {
1881             /* Assume not */
1882             BmpDisplayBootMenu = FALSE;
1883         }
1884     }
1885 
1886     /* Check if there's only one entry to boot anyway */
1887     if (Count == 1)
1888     {
1889         /* Read it */
1890         SelectedEntry = *Sequence;
1891 
1892         /* Process it */
1893         BmpProcessBootEntry(BcdHandle, SelectedEntry, ExitBootManager);
1894 
1895         /* Check if we're not displaying a boot menu */
1896         if (!BmpDisplayBootMenu)
1897         {
1898             /* Now we are */
1899             BmpDisplayBootMenu = TRUE;
1900 
1901             /* Return the entry and its index back */
1902             *EntryIndex = 0;
1903             *SelectedBootEntry = SelectedEntry;
1904             Status = STATUS_SUCCESS;
1905             goto Quickie;
1906         }
1907     }
1908     else
1909     {
1910         /* Get the default boot entry */
1911         BmpGetDefaultBootEntry(Sequence, Count, &SelectedEntry, &SelectedIndex);
1912 
1913         /* Check if we have a failed boot entry array allocated */
1914         //FoundFailedEntry = FALSE;
1915         if (BmpFailedBootEntries)
1916         {
1917             /* Check if the default entry failed to boot */
1918             if (BmpFailedBootEntries[SelectedIndex])
1919             {
1920                 /* Loop through the current boot sequence */
1921                 for (SelectedIndex = 0; SelectedIndex < Count; SelectedIndex++)
1922                 {
1923                     /* Check if there's no sequence for this index, or it failed */
1924                     while (!(Sequence[SelectedIndex]) ||
1925                             (BmpFailedBootEntries[SelectedIndex]))
1926                     {
1927                         /* Remember that this is a failed entry */
1928                         SelectedEntry = Sequence[SelectedIndex];
1929                         //FoundFailedEntry = TRUE;
1930                         BmpDisplayBootMenu = FALSE;
1931                     }
1932                 }
1933             }
1934         }
1935 
1936         /* Check if the entry is an OS loader */
1937         if (SelectedEntry->Flags & BL_APPLICATION_ENTRY_WINLOAD)
1938         {
1939             // todo
1940             EfiPrintf(L"todo path\r\n");
1941         }
1942 
1943         /* Check if there's no timeout */
1944         Status = BlGetBootOptionInteger(BlpApplicationEntry.BcdData,
1945                                         BcdBootMgrInteger_Timeout,
1946                                         &Timeout);
1947         if ((NT_SUCCESS(Status) && !(Timeout)))
1948         {
1949             /* There isn't, so just process the default entry right away */
1950             BmpProcessBootEntry(BcdHandle, SelectedEntry, ExitBootManager);
1951 
1952             /* Check if we're not displaying a boot menu */
1953             if (!BmpDisplayBootMenu)
1954             {
1955                 /* Now we are */
1956                 BmpDisplayBootMenu = TRUE;
1957 
1958                 /* Return the entry and its index back */
1959                 *EntryIndex = 0;
1960                 *SelectedBootEntry = SelectedEntry;
1961                 Status = STATUS_SUCCESS;
1962                 goto Quickie;
1963             }
1964 
1965             /* Remove the timeout for this boot instance */
1966             BlRemoveBootOption(BlpApplicationEntry.BcdData,
1967                                BcdBootMgrInteger_Timeout);
1968         }
1969     }
1970 
1971     /* Here is where we display the menu and list of tools */
1972     EfiPrintf(L"Tool selection not yet implemented\r\n");
1973     EfiStall(10000000);
1974     *SelectedBootEntry = NULL;
1975 
1976 Quickie:
1977     /* We are done -- did we have a sequence? */
1978     if (Sequence)
1979     {
1980         /* Do we have any boot entries we parsed? */
1981         while (BootIndex < Count)
1982         {
1983             /* Get the current boot entry */
1984             Entry = Sequence[BootIndex];
1985 
1986             /* Did we fail, or is is not the selected one? */
1987             if ((Entry) && ((Entry != SelectedEntry) || !(NT_SUCCESS(Status))))
1988             {
1989                 /* Destroy it, as it won't be needed */
1990                 BlDestroyBootEntry(Entry);
1991             }
1992             else if (Entry == SelectedEntry)
1993             {
1994                 /* It's the selected one, return its index */
1995                 *EntryIndex = BootIndex;
1996             }
1997 
1998             /* Move to the next entry */
1999             BootIndex++;
2000         }
2001 
2002         /* Free the sequence of entries */
2003         BlMmFreeHeap(Sequence);
2004     }
2005 
2006     /* Return the selection result */
2007     return Status;
2008 }
2009 
2010 NTSTATUS
2011 BmLaunchRecoverySequence (
2012     _In_ PBL_LOADED_APPLICATION_ENTRY BootEntry,
2013     _In_ ULONG LaunchCode
2014     )
2015 {
2016     NTSTATUS Status;
2017     PBL_LOADED_APPLICATION_ENTRY RecoveryEntry;
2018     HANDLE BcdHandle;
2019     PGUID RecoverySequence;
2020     ULONG Count, i, RecoveryIndex, SequenceCount;
2021     PBL_LOADED_APPLICATION_ENTRY* Sequence;
2022 
2023     /* Initialize locals */
2024     RecoveryIndex = 0;
2025     Sequence = NULL;
2026     RecoverySequence = NULL;
2027     Count = 0;
2028     BcdHandle = NULL;
2029 
2030     /* Open the BCD*/
2031     Status = BmOpenDataStore(&BcdHandle);
2032     if (!NT_SUCCESS(Status))
2033     {
2034         goto Quickie;
2035     }
2036 
2037     /* Get the recovery sequence list */
2038     Status = BlGetBootOptionGuidList(BootEntry->BcdData,
2039                                      BcdLibraryObjectList_RecoverySequence,
2040                                      &RecoverySequence,
2041                                      &SequenceCount);
2042     if (!NT_SUCCESS(Status))
2043     {
2044         goto Quickie;
2045     }
2046 
2047     /* Get the sequence of boot entries out of it */
2048     Status = BmGetBootSequence(BcdHandle,
2049                                RecoverySequence,
2050                                SequenceCount,
2051                                BL_APPLICATION_ENTRY_RECOVERY,
2052                                &Sequence,
2053                                &Count);
2054     if (!NT_SUCCESS(Status))
2055     {
2056         goto Quickie;
2057     }
2058 
2059     /* Was the BCD open? */
2060     if (BcdHandle)
2061     {
2062         /* Close it */
2063         BmCloseDataStore(BcdHandle);
2064     }
2065 
2066     /* Now go over every entry in the sequence */
2067     for (i = 0; i < Count; ++i)
2068     {
2069         /* Check the code for this recovery launch */
2070         if (LaunchCode == 2 || LaunchCode == 5)
2071         {
2072             /* Remove the override if there is one, and set it to 4 */
2073             BlRemoveBootOption(Sequence[i]->BcdData, BcdLibraryInteger_DisplayMessageOverride);
2074             BlAppendBootOptionInteger(Sequence[i],
2075                                       BcdLibraryInteger_DisplayMessageOverride,
2076                                       4);
2077         }
2078         else if (LaunchCode == 3)
2079         {
2080             /* Remove the override if there is one, and set it to 10 */
2081             BlRemoveBootOption(Sequence[i]->BcdData, BcdLibraryInteger_DisplayMessageOverride);
2082             BlAppendBootOptionInteger(Sequence[i],
2083                                         BcdLibraryInteger_DisplayMessageOverride,
2084                                         10);
2085         }
2086 
2087         /* Launch the boot entry for this part of the recovery sequence */
2088         Status = BmpLaunchBootEntry(Sequence[i], NULL, LaunchCode, FALSE);
2089         if (!NT_SUCCESS(Status))
2090         {
2091             break;
2092         }
2093     }
2094 
2095 Quickie:
2096     /* Did we have a sequence of entries? */
2097     if (Sequence)
2098     {
2099         /* Loop through each one */
2100         for (RecoveryIndex = 0; RecoveryIndex < Count; RecoveryIndex++)
2101         {
2102             /* Does this index have an allocated boot entry? */
2103             RecoveryEntry = Sequence[RecoveryIndex];
2104             if (RecoveryEntry)
2105             {
2106                 /* Destroy it */
2107                 BlDestroyBootEntry(RecoveryEntry);
2108             }
2109         }
2110 
2111         /* Free the sequence itself */
2112         BlMmFreeHeap(Sequence);
2113     }
2114 
2115     /* Was there a sequence list? */
2116     if (RecoverySequence)
2117     {
2118         /* Free it */
2119         BlMmFreeHeap(RecoverySequence);
2120     }
2121 
2122     /* Return back to caller */
2123     return Status;
2124 }
2125 
2126 ULONG
2127 BmDisplayDumpError (
2128     _In_ PBL_LOADED_APPLICATION_ENTRY BootEntry,
2129     _In_ ULONG LaunchCode
2130     )
2131 {
2132     ULONG BootError;
2133     NTSTATUS Status;
2134     BOOLEAN Restart, NoError;
2135 
2136     /* Assume we'll just reboot */
2137     BootError = Reboot;
2138 
2139     /* Should we reboot? */
2140     Status = BlGetBootOptionBoolean(BlpApplicationEntry.BcdData,
2141                                     BcdLibraryBoolean_RestartOnFailure,
2142                                     &Restart);
2143     if ((NT_SUCCESS(Status)) && (Restart))
2144     {
2145         return BootError;
2146     }
2147 
2148     /* Should we not show errors, and thus, reboot? */
2149     Status = BlGetBootOptionBoolean(BlpApplicationEntry.BcdData,
2150                                     BcdBootMgrBoolean_NoErrorDisplay,
2151                                     &NoError);
2152     if ((NT_SUCCESS(Status)) && (NoError))
2153     {
2154         return BootError;
2155     }
2156 
2157     /* Is there an internal boot error? */
2158     if (BmpInternalBootError)
2159     {
2160         /* Return it -- but it's a pointer? */
2161         return (ULONG_PTR)BmpInternalBootError; // ???
2162     }
2163 
2164     /* Otherwise, show the menu to see what to do */
2165     EfiPrintf(L"Error menu not yet implemented\r\n");
2166     return BootError;
2167 }
2168 
2169 NTSTATUS
2170 BmpCreateDevices (
2171     _In_ PBL_LOADED_APPLICATION_ENTRY BootEntry
2172     )
2173 {
2174     ULONG NextOffset, DataOffset, ListOffset;
2175     PBL_BCD_OPTION Option, ListOption;
2176     BcdElementType ElementType;
2177     PBCD_DEVICE_OPTION BcdDevice;
2178 
2179     /* Starting at offset 0, loop every BCD option */
2180     NextOffset = 0;
2181     do
2182     {
2183         /* Get the current option, and its offset */
2184         Option = (PBL_BCD_OPTION)((ULONG_PTR)BootEntry->BcdData + NextOffset);
2185         NextOffset = Option->NextEntryOffset;
2186 
2187         /* If it's empty, ignore it */
2188         if (Option->Empty)
2189         {
2190             continue;
2191         }
2192 
2193         /* If it's not a device option, ignore it */
2194         ElementType.PackedValue = Option->Type;
2195         if (ElementType.Format != BCD_TYPE_DEVICE)
2196         {
2197             continue;
2198         }
2199 
2200         /* Get the data offset */
2201         DataOffset = Option->DataOffset;
2202 
2203         /* Extract the device out of it */
2204         BcdDevice = (PBCD_DEVICE_OPTION)((ULONG_PTR)BootEntry->BcdData + DataOffset);
2205 
2206         /* If the device is already fully specified, no need to build it */
2207         if (!(BcdDevice->DeviceDescriptor.Flags & 1))
2208         {
2209             continue;
2210         }
2211 
2212         /* Otherwise, check if there's any list options as well */
2213         ListOption = NULL;
2214         ListOffset = Option->ListOffset;
2215         if (Option->ListOffset)
2216         {
2217             ListOption = (PBL_BCD_OPTION)((ULONG_PTR)BootEntry->BcdData + ListOffset);
2218         }
2219 
2220         /* And now call BlCreateDevice to build the full device descriptor */
2221         EfiPrintf(L"Unspecified devices not yet supported: %p\r\n", ListOption);
2222         return STATUS_NOT_SUPPORTED;
2223     } while (NextOffset != 0);
2224 
2225     /* Devices created successfully */
2226     return STATUS_SUCCESS;
2227 }
2228 
2229 NTSTATUS
2230 BmpTransferExecution (
2231     _In_ PBL_LOADED_APPLICATION_ENTRY BootEntry,
2232     _Out_ PULONG LaunchCode,
2233     _Out_ PBOOLEAN Recover
2234     )
2235 {
2236     PWCHAR AppPath;
2237     NTSTATUS Status;
2238     PBL_DEVICE_DESCRIPTOR AppDevice;
2239     BL_RETURN_ARGUMENTS ReturnArgs;
2240     BOOLEAN AdvancedOptions;
2241     ULONG AppHandle;
2242 
2243     /* Get the application path */
2244     Status = BlGetBootOptionString(BootEntry->BcdData,
2245                                    BcdLibraryString_ApplicationPath,
2246                                    &AppPath);
2247     if (!NT_SUCCESS(Status))
2248     {
2249         /* If we couldn't find one, set this to NULL */
2250         AppPath = NULL;
2251     }
2252 
2253     /* Check if this is a PXE startup.com */
2254     if (BootEntry->Flags & BL_APPLICATION_ENTRY_STARTUP)
2255     {
2256 #if BL_NET_SUPPORT
2257         /* Do soft reboot to launch it */
2258         Status = BlNetSoftReboot(BootEntry);
2259 #else
2260         EfiPrintf(L"Net boot not supported\r\n");
2261         Status = STATUS_NOT_SUPPORTED;
2262 #endif
2263         /* Nothing else for us to do */
2264         goto Quickie;
2265     }
2266 
2267     /* Loop as long as boot was not cancelled */
2268     do
2269     {
2270         /* Load the boot application */
2271         Status = BlImgLoadBootApplication(BootEntry, &AppHandle);
2272 
2273         /* Did we not find it? */
2274         if (Status == STATUS_NOT_FOUND)
2275         {
2276             /* Get the device for the boot application */
2277             Status = BlGetBootOptionDevice(BootEntry->BcdData,
2278                                            BcdLibraryDevice_ApplicationDevice,
2279                                            &AppDevice,
2280                                            NULL);
2281             if (!NT_SUCCESS(Status))
2282             {
2283                 /* Force re-enumeration */
2284                 Status = BlFwEnumerateDevice(AppDevice);
2285             }
2286 
2287             /* Did re-enumeration work? */
2288             if (!NT_SUCCESS(Status))
2289             {
2290                 /* Nope, raise a fatal error */
2291                 BmFatalErrorEx(BL_FATAL_ERROR_APP_LOAD,
2292                                (ULONG_PTR)AppPath,
2293                                Status,
2294                                0,
2295                                0);
2296                 goto Quickie;
2297             }
2298 
2299             /* Yes, try booting it again */
2300             Status = BlImgLoadBootApplication(BootEntry, &AppHandle);
2301         }
2302 
2303         /* Was boot cancelled?*/
2304         if (Status == STATUS_CANCELLED)
2305         {
2306             /* Should we display the menu, or is there no launch sequence? */
2307             if ((BmGetBootMenuPolicy(BootEntry) != MenuPolicyStandard) ||
2308                 !(MiscGetBootOption(BootEntry->BcdData,
2309                                     BcdLibraryObjectList_RecoverySequence)))
2310             {
2311                 /* Bail out, the menu will take care of it */
2312                 goto Quickie;
2313             }
2314 
2315             /* No menu and there's a sequence, launch it */
2316             *LaunchCode = 4;
2317             *Recover = TRUE;
2318             goto Quickie;
2319         }
2320 
2321         /* STATUS_FVE_LOCKED_VOLUME -- bitlocker volume is locked */
2322         if (Status == STATUS_FVE_LOCKED_VOLUME)
2323         {
2324             /* Launch recovery mode */
2325             *LaunchCode = 4;
2326             *Recover = TRUE;
2327             goto Quickie;
2328         }
2329 
2330         /* Was there some other error launching the boot application? */
2331         if (!NT_SUCCESS(Status))
2332         {
2333             /* Raise a fatal error */
2334             BmFatalErrorEx(BL_FATAL_ERROR_APP_LOAD,
2335                            (ULONG_PTR)AppPath,
2336                            Status,
2337                            0,
2338                            0);
2339             goto Quickie;
2340         }
2341 
2342         /* Zero out the return arguments */
2343         RtlZeroMemory(&ReturnArgs, sizeof(ReturnArgs));
2344 
2345         /* Log to ETW this launch */
2346         //BmpLogApplicationLaunchEvent(&BootEntry->Guid, AppPath);
2347 
2348         /* Launch the boot application*/
2349         Status = BlImgStartBootApplication(AppHandle, &ReturnArgs);
2350 
2351 #if BL_BITLOCKER_SUPPORT
2352         /* Bitlocker stuff */
2353         BlFveSecureBootCheckpointAppReturn(BootEntry, &ReturnArgs);
2354 #endif
2355 
2356         /* Log in the boot status log the launch */
2357         //BlBsdLogEntry(1, 0x12, &BootEntry->Guid, 0x14);
2358 
2359         /* Unloac the boot application if we've returned */
2360         BlImgUnloadBootApplication(AppHandle);
2361 
2362         /* Keep going unless STATUS_RESTART_BOOT_APPLICATION */
2363     } while (Status != 0xC0000453);
2364 
2365     /* We've come back. Assume we need to launch the recovery sequence */
2366     *Recover = TRUE;
2367 
2368     /* Why did we get back? */
2369     if (ReturnArgs.Flags & 1)
2370     {
2371         /* Flag 1 -- should we display advanced options? */
2372         Status = BlGetBootOptionBoolean(BootEntry->BcdData,
2373                                         BcdLibraryBoolean_DisplayAdvancedOptions,
2374                                         &AdvancedOptions);
2375         if ((NT_SUCCESS(Status)) && (AdvancedOptions))
2376         {
2377             /* Yes, so return with code 2 */
2378             *LaunchCode = 2;
2379         }
2380         else
2381         {
2382             /* No, return with code 1 */
2383             *LaunchCode = 1;
2384         }
2385     }
2386     else if (ReturnArgs.Flags & 4)
2387     {
2388         /* Flag 4 -- unknown */
2389         *LaunchCode = 1;
2390     }
2391     else if (ReturnArgs.Flags & 8)
2392     {
2393         /* Flag 5 -- unknown */
2394         *LaunchCode = 5;
2395     }
2396     else if (ReturnArgs.Flags & 0x10)
2397     {
2398         /* Flag 6 -- unknown */
2399         *LaunchCode = 6;
2400     }
2401     else if (ReturnArgs.Flags & 0x20)
2402     {
2403         /* Flag 7 -- unknown */
2404         *LaunchCode = 7;
2405     }
2406     else if (ReturnArgs.Flags & BL_RETURN_ARGUMENTS_NO_PAE_FLAG)
2407     {
2408         /* PAE is not supported -- refuse to boot */
2409         *Recover = FALSE;
2410         BmFatalErrorEx(BL_FATAL_ERROR_NO_PAE, Status, 0, 0, 0);
2411     }
2412 
2413 Quickie:
2414     /* All done, did we have an application path? */
2415     if (AppPath)
2416     {
2417         /* Free it */
2418         BlMmFreeHeap(AppPath);
2419     }
2420 
2421     /* Back to the caller now */
2422     return Status;
2423 }
2424 
2425 NTSTATUS
2426 BmpLaunchBootEntry (
2427     _In_ PBL_LOADED_APPLICATION_ENTRY BootEntry,
2428     _Out_ PULONG EntryIndex,
2429     _In_ ULONG LaunchCode,
2430     _In_ BOOLEAN LaunchWinRe
2431     )
2432 {
2433     HANDLE BcdHandle;
2434     NTSTATUS Status;
2435     GUID ObjectId;
2436     BOOLEAN DoRecovery, AutoRecovery, DoSequence, RestartOnFailure;
2437     ULONG ErrorCode;
2438     BOOLEAN AdvancedOneTime, EditOneTime;
2439 
2440     /* Check if this is the OS loader */
2441     if (BootEntry->Flags & BL_APPLICATION_ENTRY_WINLOAD)
2442     {
2443         /* Check if one-time advanced options should be shown */
2444         if (MiscGetBootOption(BootEntry->BcdData,
2445                               BcdOSLoaderBoolean_AdvancedOptionsOneTime))
2446         {
2447             /* Open the BCD */
2448             BcdHandle = NULL;
2449             Status = BmOpenDataStore(BcdHandle);
2450             if (NT_SUCCESS(Status))
2451             {
2452                 /* Delete the option from the BCD, so it doesn't happen again */
2453                 ObjectId = BootEntry->Guid;
2454                 BmPurgeOption(BcdHandle,
2455                               &ObjectId,
2456                               BcdOSLoaderBoolean_AdvancedOptionsOneTime);
2457                 BmCloseDataStore(BcdHandle);
2458             }
2459         }
2460 
2461         /* Check if one-time options editor should be shown */
2462         if (MiscGetBootOption(BootEntry->BcdData,
2463                               BcdOSLoaderBoolean_OptionsEditOneTime))
2464         {
2465             /* Open the BCD */
2466             BcdHandle = NULL;
2467             Status = BmOpenDataStore(BcdHandle);
2468             if (NT_SUCCESS(Status))
2469             {
2470                 /* Delete the option from the BCD, so it doesn't happen again */
2471                 ObjectId = BootEntry->Guid;
2472                 BmPurgeOption(BcdHandle,
2473                               &ObjectId,
2474                               BcdOSLoaderBoolean_OptionsEditOneTime);
2475                 BmCloseDataStore(BcdHandle);
2476             }
2477         }
2478     }
2479 
2480 TryAgain:
2481     /* Disable recovery mode */
2482     DoRecovery = FALSE;
2483 
2484     /* Store globally which entry we are trying to boot */
2485     BmpSelectedBootEntry = BootEntry;
2486 
2487     /* Create any devices that aren't yet fully defined for this boot entry */
2488     Status = BmpCreateDevices(BootEntry);
2489     if (!NT_SUCCESS(Status))
2490     {
2491         /* That failed -- can we launch the recovery environment? */
2492         if (!LaunchWinRe)
2493         {
2494             return Status;
2495         }
2496 
2497         /* Yes, so return with the WinRe launch code */
2498         LaunchCode = 2;
2499         goto Quickie;
2500     }
2501 
2502     /* Is this an OS loader/ */
2503     if (BootEntry->Flags & BL_APPLICATION_ENTRY_WINLOAD)
2504     {
2505         /* Is the one-time advanced options menu option present? */
2506         Status = BlGetBootOptionBoolean(BootEntry->BcdData,
2507                                         BcdOSLoaderBoolean_AdvancedOptionsOneTime,
2508                                         &AdvancedOneTime);
2509         if (NT_SUCCESS(Status))
2510         {
2511             /* Is it turned on? */
2512             if (AdvancedOneTime)
2513             {
2514                 /* Set the option this once */
2515                 BlAppendBootOptionBoolean(BootEntry,
2516                                           BcdLibraryBoolean_DisplayAdvancedOptions,
2517                                           TRUE);
2518             }
2519             else
2520             {
2521                 /* It's not, so disable the option if active */
2522                 BlRemoveBootOption(BootEntry->BcdData,
2523                                    BcdLibraryBoolean_DisplayAdvancedOptions);
2524             }
2525 
2526             /* Remove the one-time option. We've already purged it earlier */
2527             BlRemoveBootOption(BootEntry->BcdData,
2528                                BcdOSLoaderBoolean_AdvancedOptionsOneTime);
2529         }
2530 
2531         /* Is the one-time options editor menu option present? */
2532         Status = BlGetBootOptionBoolean(BootEntry->BcdData,
2533                                         BcdOSLoaderBoolean_OptionsEditOneTime,
2534                                         &EditOneTime);
2535         if (NT_SUCCESS(Status))
2536         {
2537             /* Is it turned on? */
2538             if (EditOneTime)
2539             {
2540                 /* Set the option this once */
2541                 BlAppendBootOptionBoolean(BootEntry,
2542                                           BcdLibraryBoolean_DisplayOptionsEdit,
2543                                           TRUE);
2544             }
2545             else
2546             {
2547                 /* It's not, so disable the option if active */
2548                 BlRemoveBootOption(BootEntry->BcdData,
2549                                    BcdLibraryBoolean_DisplayOptionsEdit);
2550             }
2551 
2552             /* Remove the one-time option. We've already purged it earlier */
2553             BlRemoveBootOption(BootEntry->BcdData,
2554                                BcdOSLoaderBoolean_OptionsEditOneTime);
2555         }
2556     }
2557 
2558     /* BCD handling done, transfer execution to this entry */
2559     Status = BmpTransferExecution(BootEntry, &LaunchCode, &DoRecovery);
2560     if (!LaunchWinRe)
2561     {
2562         return Status;
2563     }
2564 
2565     /* Check if boot was successful, or  cancelled and we're not doing WinRE */
2566     if (((NT_SUCCESS(Status)) || (Status == STATUS_CANCELLED)) && !(DoRecovery))
2567     {
2568         return Status;
2569     }
2570 
2571     /* Boot failed -- are we doing recovery? */
2572     if (!DoRecovery)
2573     {
2574         /* Nope, bail out */
2575         LaunchCode = 2;
2576         goto Quickie;
2577     }
2578 
2579 Quickie:
2580     /* Get the recovery sequence */
2581     if (MiscGetBootOption(BootEntry->BcdData, BcdLibraryObjectList_RecoverySequence))
2582     {
2583         /* Check if the launch depends on auto-recovery being enabled or not */
2584         if ((LaunchCode == 3) || (LaunchCode == 5) || (LaunchCode == 6))
2585         {
2586             Status = BlGetBootOptionBoolean(BootEntry->BcdData,
2587                                             BcdLibraryBoolean_AutoRecoveryEnabled,
2588                                             &AutoRecovery);
2589             if (NT_SUCCESS(Status))
2590             {
2591                 /* Override the setting */
2592                 DoRecovery = AutoRecovery;
2593             }
2594         }
2595     }
2596     else
2597     {
2598         /* There's no recovery setting */
2599         DoRecovery = FALSE;
2600     }
2601 
2602     /* Check if we should restart on failure */
2603     RestartOnFailure = FALSE;
2604     BlGetBootOptionBoolean(BlpApplicationEntry.BcdData,
2605                            BcdLibraryBoolean_RestartOnFailure,
2606                            &RestartOnFailure);
2607 
2608     /* Do the sequence if recovery is on, unless we should restart instead */
2609     DoSequence = RestartOnFailure ? FALSE : DoRecovery;
2610     while (1)
2611     {
2612         /* Are we doing the recovery sequence? */
2613         if (DoSequence)
2614         {
2615             /* Because of automatic recovery? */
2616             if (AutoRecovery)
2617             {
2618 #if BL_BITLOCKER_SUPPORT
2619                 /* Do bitlocker stuff */
2620                 BlFveRegisterBootEntryForTrustedWimBoot(BootEntry, TRUE);
2621 #endif
2622             }
2623 
2624             /* Launch the recovery sequence*/
2625             Status = BmLaunchRecoverySequence(BootEntry, LaunchCode);
2626 
2627             /* Was it launched automatically? */
2628             if (AutoRecovery)
2629             {
2630 #if BL_BITLOCKER_SUPPORT
2631                 /* Do bitlocker stuff */
2632                 BlFveRegisterBootEntryForTrustedWimBoot(BootEntry, FALSE);
2633 #endif
2634 
2635                 /* No need to do this again */
2636                 AutoRecovery = FALSE;
2637             }
2638 
2639             /* Did the recovery sequence work? */
2640             if (NT_SUCCESS(Status))
2641             {
2642                 /* All good */
2643                 return STATUS_SUCCESS;
2644             }
2645 
2646             /* Remove the sequence, don't do it again */
2647             BlRemoveBootOption(BootEntry->BcdData, BcdLibraryObjectList_RecoverySequence);
2648         }
2649 
2650         /* Recovery sequence also failed, show fatal error */
2651         if (!BmpInternalBootError)
2652         {
2653             BmFatalErrorEx(BL_FATAL_ERROR_GENERIC, Status, 0, 0, 0);
2654         }
2655 
2656         /* Display the error menu */
2657         ErrorCode = BmDisplayDumpError(BootEntry, LaunchCode);
2658         BmErrorPurge();
2659 
2660         /* See what the user wants to do */
2661         switch (ErrorCode)
2662         {
2663             case TryAgain:
2664                 /* Try again */
2665                 goto TryAgain;
2666 
2667             case NextOs:
2668                 /* Boot the next entry*/
2669                 break;
2670 
2671             case OsSelection:
2672                 /* Cancel the boot*/
2673                 return STATUS_CANCELLED;
2674 
2675             case RecoverOem:
2676                 /* Custom OEM recovery -- open the BCD */
2677                 Status = BmOpenDataStore(BcdHandle);
2678                 if (NT_SUCCESS(Status))
2679                 {
2680                     /* See what the custom sequence is */
2681                     Status = BmProcessCustomAction(BcdHandle, NULL);
2682                 }
2683 
2684                 /* All done, close the BCD */
2685                 if (BcdHandle)
2686                 {
2687                     BmCloseDataStore(BcdHandle);
2688                 }
2689                 return Status;
2690 
2691             case AdvancedOptions:
2692                 /* Show the advanced options next iteration */
2693                 BlAppendBootOptionBoolean(BootEntry,
2694                                           BcdOSLoaderBoolean_AdvancedOptionsOneTime,
2695                                           TRUE);
2696                 goto TryAgain;
2697 
2698             case BootOptions:
2699                 /* Show the options editor next iteration */
2700                 BlAppendBootOptionBoolean(BootEntry,
2701                                           BcdOSLoaderBoolean_OptionsEditOneTime,
2702                                           TRUE);
2703                 goto TryAgain;
2704 
2705             case Recover:
2706                 /* Try the recovery sequence next time*/
2707                 DoSequence = TRUE;
2708                 LaunchCode = 1;
2709                 goto TryAgain;
2710 
2711             default:
2712                 /* Something unknown */
2713                 return STATUS_CANCELLED;
2714         }
2715     }
2716 
2717     /* We are booting the next OS, so return success as to not kill the boot */
2718     return STATUS_SUCCESS;
2719 }
2720 
2721 /*++
2722  * @name BmMain
2723  *
2724  *     The BmMain function implements the Windows Boot Application entrypoint for
2725  *     the Boot Manager.
2726  *
2727  * @param  BootParameters
2728  *         Pointer to the Boot Application Parameter Block.
2729  *
2730  * @return NT_SUCCESS if the image was loaded correctly, relevant error code
2731  *         otherwise.
2732  *
2733  *--*/
2734 NTSTATUS
2735 NTAPI
2736 BmMain (
2737     _In_ PBOOT_APPLICATION_PARAMETER_BLOCK BootParameters
2738     )
2739 {
2740     NTSTATUS Status, LibraryStatus;
2741     BL_LIBRARY_PARAMETERS LibraryParameters;
2742     PBL_RETURN_ARGUMENTS ReturnArguments;
2743     PGUID AppIdentifier;
2744     HANDLE BcdHandle, ResumeBcdHandle;
2745     PBL_BCD_OPTION EarlyOptions;
2746     PWCHAR Stylesheet;
2747     BOOLEAN XmlLoaded, DisableIntegrity, TestSigning, PersistBootSequence;
2748     BOOLEAN RebootOnError, CustomActions;
2749     ULONG SequenceId;
2750     PBL_LOADED_APPLICATION_ENTRY BootEntry;
2751     PGUID SequenceList;
2752     ULONG SequenceListCount;
2753     PBL_LOADED_APPLICATION_ENTRY* BootSequence;
2754     ULONG BootIndex;
2755     BOOLEAN ExitBootManager;
2756     BOOLEAN BootFailed;
2757     BOOLEAN BootOk;
2758     ULONG SequenceCount;
2759     BOOLEAN GetEntry;
2760     EfiPrintf(L"ReactOS UEFI Boot Manager Initializing...\r\n");
2761 
2762     /* Reading the BCD can change this later on */
2763     RebootOnError = FALSE;
2764 
2765     /* Save the start/end-of-POST time */
2766 #if defined(_M_IX86) || defined(_M_X64)
2767     ApplicationStartTime = __rdtsc();
2768 #else
2769     EfiPrintf(L"No time source defined for this platform\r\n");
2770     ApplicationStartTime = 0;
2771 #endif
2772     PostTime = ApplicationStartTime;
2773 
2774     /* Setup the boot library parameters for this application */
2775     BlSetupDefaultParameters(&LibraryParameters);
2776     LibraryParameters.TranslationType = BlNone;
2777     LibraryParameters.LibraryFlags = 0x400 | 0x8;
2778     LibraryParameters.MinimumAllocationCount = 16;
2779     LibraryParameters.MinimumHeapSize = 512 * 1024;
2780 
2781     /* Initialize the boot library */
2782     Status = BlInitializeLibrary(BootParameters, &LibraryParameters);
2783     if (!NT_SUCCESS(Status))
2784     {
2785         /* Check for failure due to invalid application entry */
2786         if (Status != STATUS_INVALID_PARAMETER_9)
2787         {
2788             /* Specifically print out what happened */
2789             EfiPrintf(L"BlInitializeLibrary failed 0x%x\r\n", Status);
2790         }
2791 
2792         /* Go to exit path */
2793         goto Quickie;
2794     }
2795 
2796     /* Get the application identifier */
2797     AppIdentifier = BlGetApplicationIdentifier();
2798     if (!AppIdentifier)
2799     {
2800         /* None was given, so set our default one */
2801         AppIdentifier = (PGUID)&GUID_WINDOWS_BOOTMGR;
2802     }
2803 
2804     /* Save our identifier */
2805     BmApplicationIdentifier = *AppIdentifier;
2806 
2807     /* Initialize the file system to open a handle to our root boot directory */
2808     BmFwInitializeBootDirectoryPath();
2809 
2810     /* Load and initialize the boot configuration database (BCD) */
2811     Status = BmOpenDataStore(&BcdHandle);
2812     if (NT_SUCCESS(Status))
2813     {
2814         /* Copy the boot options */
2815         Status = BlCopyBootOptions(BlpApplicationEntry.BcdData, &EarlyOptions);
2816         if (NT_SUCCESS(Status))
2817         {
2818             /* Update them */
2819             Status = BmpUpdateApplicationOptions(BcdHandle);
2820             if (!NT_SUCCESS(Status))
2821             {
2822                 /* Log a fatal error */
2823                 BmFatalErrorEx(BL_FATAL_ERROR_BCD_PARSE,
2824                                (ULONG_PTR)L"\\BCD",
2825                                Status,
2826                                0,
2827                                0);
2828             }
2829         }
2830     }
2831 
2832 #ifdef _SECURE_BOOT
2833     /* Initialize the secure boot machine policy */
2834     Status = BmSecureBootInitializeMachinePolicy();
2835     if (!NT_SUCCESS(Status))
2836     {
2837         BmFatalErrorEx(BL_FATAL_ERROR_SECURE_BOOT, Status, 0, 0, 0);
2838     }
2839 #endif
2840 
2841     /* Copy the library parameters and add the re-initialization flag */
2842     RtlCopyMemory(&LibraryParameters,
2843                   &BlpLibraryParameters,
2844                   sizeof(LibraryParameters));
2845     LibraryParameters.LibraryFlags |= (BL_LIBRARY_FLAG_REINITIALIZE_ALL |
2846                                        BL_LIBRARY_FLAG_REINITIALIZE);
2847 
2848     /* Now that we've parsed the BCD, re-initialize the library */
2849     LibraryStatus = BlInitializeLibrary(BootParameters, &LibraryParameters);
2850     if (!NT_SUCCESS(LibraryStatus) && (NT_SUCCESS(Status)))
2851     {
2852         Status = LibraryStatus;
2853     }
2854 
2855     /* Initialize firmware-specific memory regions */
2856     BmFwMemoryInitialize();
2857 
2858     /* Initialize the boot status data log (BSD) */
2859     BmpInitializeBootStatusDataLog();
2860 
2861     /* Find our XSL stylesheet */
2862     Stylesheet = BlResourceFindHtml();
2863     if (!Stylesheet)
2864     {
2865         /* Awe, no XML. This is actually fatal lol. Can't boot without XML. */
2866         Status = STATUS_NOT_FOUND;
2867         EfiPrintf(L"BlResourceFindMessage failed 0x%x\r\n", STATUS_NOT_FOUND);
2868         goto Quickie;
2869     }
2870 
2871     /* Initialize the XML Engine (as a side-effect, resets cursor) */
2872     Status = BlXmiInitialize(Stylesheet);
2873     if (!NT_SUCCESS(Status))
2874     {
2875         EfiPrintf(L"\r\nBlXmiInitialize failed 0x%x\r\n", Status);
2876         goto Failure;
2877     }
2878     XmlLoaded = TRUE;
2879 
2880     /* Check if there's an active bitmap visible */
2881     if (!BlDisplayValidOemBitmap())
2882     {
2883         /* Nope, make the screen black using BGFX */
2884         if (!NT_SUCCESS(BmpBgDisplayClearScreen(0xFF000000)))
2885         {
2886             /* BGFX isn't active, use standard display */
2887             BlDisplayClearScreen();
2888         }
2889     }
2890 
2891 #ifdef _BIT_LOCKER_
2892     /* Bitlocker will take over screen UI if enabled */
2893     FveDisplayScreen = BmFveDisplayScreen;
2894 #endif
2895 
2896     /* Check if any bypass options are enabled */
2897     BlImgQueryCodeIntegrityBootOptions(&BlpApplicationEntry,
2898                                        &DisableIntegrity,
2899                                        &TestSigning);
2900     if (!DisableIntegrity)
2901     {
2902         /* Integrity checks are enabled, so validate our signature */
2903         Status = BmFwVerifySelfIntegrity();
2904         if (!NT_SUCCESS(Status))
2905         {
2906             /* Signature invalid, fail boot */
2907             goto Failure;
2908         }
2909     }
2910 
2911 
2912     /* TEST MODE */
2913     EfiPrintf(L"Performing memory allocator tests...\r\n");
2914     {
2915         NTSTATUS Status;
2916         PHYSICAL_ADDRESS PhysicalAddress, PhysicalAddress2;
2917 
2918         /* Allocate 1 physical page */
2919         PhysicalAddress.QuadPart = 0;
2920         Status = BlMmAllocatePhysicalPages(&PhysicalAddress, BlLoaderData, 1, 0, 1);
2921         if (Status != STATUS_SUCCESS)
2922         {
2923             EfiPrintf(L"FAIL: Allocation status: %lx at address: %llx\r\n", Status, PhysicalAddress.QuadPart);
2924             EfiStall(100000000);
2925         }
2926 
2927         /* Write some data */
2928         *(PULONG)((ULONG_PTR)PhysicalAddress.QuadPart) = 0x55555151;
2929 
2930         /* Free it */
2931         Status = BlMmFreePhysicalPages(PhysicalAddress);
2932         if (Status != STATUS_SUCCESS)
2933         {
2934             EfiPrintf(L"FAIL: Memory free status: %lx\r\n", Status);
2935             EfiStall(100000000);
2936         }
2937 
2938         /* Allocate a page again */
2939         PhysicalAddress2.QuadPart = 0;
2940         Status = BlMmAllocatePhysicalPages(&PhysicalAddress2, BlLoaderData, 1, 0, 1);
2941         if (Status != STATUS_SUCCESS)
2942         {
2943             EfiPrintf(L"FAIL: Allocation status: %lx at address: %llx\r\n", Status, PhysicalAddress2.QuadPart);
2944             EfiStall(100000000);
2945         }
2946 
2947         /* It should've given us the same page, since we freed it */
2948         if (PhysicalAddress.QuadPart != PhysicalAddress2.QuadPart)
2949         {
2950             EfiPrintf(L"FAIL: Non-matching addresses: %llx %llx\r\n", PhysicalAddress.QuadPart, PhysicalAddress2.QuadPart);
2951             EfiStall(100000000);
2952         }
2953 
2954         /* The data should still be there, since zero-ing is not on for bootmgr */
2955         if (*(PULONG)((ULONG_PTR)PhysicalAddress2.QuadPart) != 0x55555151)
2956         {
2957             EfiPrintf(L"FAIL: Non-matching data: %lx %lx\r\n", 0x55555151, *(PULONG)((ULONG_PTR)PhysicalAddress2.QuadPart));
2958             EfiStall(100000000);
2959         }
2960 
2961         /* And free the second page again */
2962         Status = BlMmFreePhysicalPages(PhysicalAddress);
2963         if (Status != STATUS_SUCCESS)
2964         {
2965             EfiPrintf(L"FAIL: Memory free status: %lx\r\n", Status);
2966             EfiStall(100000000);
2967         }
2968     }
2969 
2970     /* Write out the first XML tag */
2971     BlXmiWrite(L"<bootmgr/>");
2972 
2973     /* Check for factory reset */
2974     BlSecureBootCheckForFactoryReset();
2975 
2976     /* Load the revocation list */
2977     Status = BmFwRegisterRevocationList();
2978     if (!NT_SUCCESS(Status))
2979     {
2980         goto Failure;
2981     }
2982 
2983     /* Register our custom progress routine */
2984     BlUtlRegisterProgressRoutine();
2985 
2986     /* Display state is not currently cached */
2987     BmDisplayStateCached = FALSE;
2988 
2989     /* Check if we need to resume from hibernate */
2990     Status = BmResumeFromHibernate(&ResumeBcdHandle);
2991     if (!NT_SUCCESS(Status))
2992     {
2993         goto Failure;
2994     }
2995 
2996 #ifdef BL_NET_SUPPORT
2997     /* Register multicast printing routine */
2998     BlUtlRegisterMulticastRoutine();
2999 #endif
3000 
3001     /* Check if restart on failure is enabled */
3002     BlGetBootOptionBoolean(BlpApplicationEntry.BcdData,
3003                            BcdLibraryBoolean_RestartOnFailure,
3004                            &RebootOnError);
3005 
3006     /* Check if the boot sequence is persisted */
3007     Status = BlGetBootOptionBoolean(BlpApplicationEntry.BcdData,
3008                                     BcdBootMgrBoolean_PersistBootSequence,
3009                                     &PersistBootSequence);
3010     if (!NT_SUCCESS(Status))
3011     {
3012         /* It usually is */
3013         PersistBootSequence = TRUE;
3014     }
3015 
3016     /* Check if there's custom actions to take */
3017     Status = BlGetBootOptionBoolean(BlpApplicationEntry.BcdData,
3018                                     BcdBootMgrBoolean_ProcessCustomActionsFirst,
3019                                     &CustomActions);
3020     if ((NT_SUCCESS(Status)) && (CustomActions))
3021     {
3022         /* We don't support this yet */
3023         EfiPrintf(L"Not implemented\r\n");
3024         Status = STATUS_NOT_IMPLEMENTED;
3025         goto Failure;
3026     }
3027 
3028     //BlResourceFindMessage(BM_MSG_TEST);
3029 
3030     /* At last, enter the boot selection stage */
3031     SequenceId = 0;
3032     GetEntry = FALSE;
3033     BootFailed = FALSE;
3034     SequenceList = NULL;
3035     BootSequence = NULL;
3036     SequenceCount = 0;
3037     while (1)
3038     {
3039         /* We don't have a boot entry nor a sequence ID */
3040         BootEntry = NULL;
3041         BootOk = FALSE;
3042 
3043         /* Do we have a hardcoded boot sequence set? */
3044         if (!(BootSequence) && !(GetEntry))
3045         {
3046             /* Not yet, read the BCD to see if one is there */
3047             Status = BlGetBootOptionGuidList(BlpApplicationEntry.BcdData,
3048                                              BcdBootMgrObjectList_BootSequence,
3049                                              &SequenceList,
3050                                              &SequenceListCount);
3051             if (NT_SUCCESS(Status))
3052             {
3053                 /* A GUID list for the boot sequence is set. Extract it */
3054                 Status = BmGetBootSequence(BcdHandle,
3055                                            SequenceList,
3056                                            SequenceListCount,
3057                                            BL_APPLICATION_ENTRY_FIXED_SEQUENCE,
3058                                            &BootSequence,
3059                                            &SequenceCount);
3060                 if (NT_SUCCESS(Status))
3061                 {
3062                     /* Don't get stuck in a loop repeating this sequence */
3063                     BlRemoveBootOption(BlpApplicationEntry.BcdData,
3064                                        BcdBootMgrObjectList_BootSequence);
3065 
3066                     /* But do check if we should persist it */
3067                     if (PersistBootSequence)
3068                     {
3069                         /* Yes -- so go select an entry now */
3070                         GetEntry = TRUE;
3071                     }
3072                     else
3073                     {
3074                         /* We shouldn't, so wipe it from the BCD too */
3075                         Status = BmPurgeOption(BcdHandle,
3076                                                &BmApplicationIdentifier,
3077                                                BcdBootMgrObjectList_BootSequence);
3078                         if (!NT_SUCCESS(Status))
3079                         {
3080                             /* Well that failed */
3081                             goto LoopQuickie;
3082                         }
3083                     }
3084                 }
3085             }
3086             else
3087             {
3088                 /* No boot entry sequence for us */
3089                 BootSequence = NULL;
3090             }
3091         }
3092 
3093         /* Do we have a sequence active, and are we still processing it? */
3094         if ((BootSequence) && ((GetEntry) || (SequenceId < SequenceCount)))
3095         {
3096             /* Extract the next entry in the sequence */
3097             BootEntry = BootSequence[SequenceId];
3098             BootSequence[SequenceId] = NULL;
3099 
3100             /* Move to the next entry for next time */
3101             SequenceId++;
3102 
3103             /* Unless there won't be a a next time? */
3104             if (SequenceId == SequenceCount)
3105             {
3106                 /* Clean up, it's the last entry */
3107                 BlMmFreeHeap(BootSequence);
3108                 BootSequence = NULL;
3109             }
3110         }
3111         else
3112         {
3113             /* Get the selected boot entry from the user */
3114             ExitBootManager = FALSE;
3115             Status = BmpGetSelectedBootEntry(BcdHandle,
3116                                              &BootEntry,
3117                                              &BootIndex,
3118                                              &ExitBootManager);
3119             if (!(NT_SUCCESS(Status)) || (ExitBootManager))
3120             {
3121                 /* Selection failed, or user wants to exit */
3122                 goto LoopQuickie;
3123             }
3124         }
3125 
3126         /* Did we have a BCD open? */
3127         if (BcdHandle)
3128         {
3129             /* Close it, we'll be opening a new one */
3130             BmCloseDataStore(BcdHandle);
3131             BcdHandle = NULL;
3132         }
3133 
3134         /* Launch the selected entry */
3135         Status = BmpLaunchBootEntry(BootEntry, &BootIndex, 0, TRUE);
3136         if (NT_SUCCESS(Status))
3137         {
3138             /* Boot worked, uncache display and process the bad memory list */
3139             BmDisplayStateCached = FALSE;
3140             BmpProcessBadMemory();
3141         }
3142         else
3143         {
3144             /* Boot failed -- was it user driven? */
3145             if (Status != STATUS_CANCELLED)
3146             {
3147                 /* Nope, remember that booting failed */
3148                 BootFailed = TRUE;
3149                 goto LoopQuickie;
3150             }
3151 
3152             /* Yes -- the display is still valid */
3153             BmDisplayStateCached = TRUE;
3154         }
3155 
3156         /* Reopen the BCD */
3157         Status = BmOpenDataStore(&BcdHandle);
3158         if (!NT_SUCCESS(Status))
3159         {
3160             break;
3161         }
3162 
3163         /* Put the BCD options back into our entry */
3164         BlReplaceBootOptions(&BlpApplicationEntry, EarlyOptions);
3165 
3166         /* Update our options one more time */
3167         Status = BmpUpdateApplicationOptions(BcdHandle);
3168         if (NT_SUCCESS(Status))
3169         {
3170             /* Boot was 100% OK */
3171             BootOk = TRUE;
3172         }
3173 
3174 LoopQuickie:
3175         /* Did we have a boot entry? */
3176         if (BootEntry)
3177         {
3178             /* We can destroy it now */
3179             BlDestroyBootEntry(BootEntry);
3180         }
3181 
3182         /* Is this the success path? */
3183         if (NT_SUCCESS(Status))
3184         {
3185             /* Did we actually boot something? */
3186             if (!BootOk)
3187             {
3188                 /* Bope, fail out */
3189                 break;
3190             }
3191         }
3192 
3193         /* This is the failure path... should we reboot? */
3194         if (RebootOnError)
3195         {
3196             break;
3197         }
3198     };
3199 
3200 Failure:
3201     if (!BootFailed)
3202     {
3203         /* Check if we got here due to an internal error */
3204         if (BmpInternalBootError)
3205         {
3206             /* If XML is available, display the error */
3207             if (XmlLoaded)
3208             {
3209                 //BmDisplayDumpError(0, 0);
3210                 //BmErrorPurge();
3211             }
3212 
3213             /* Don't do a fatal error -- return back to firmware */
3214             goto Quickie;
3215         }
3216     }
3217 
3218     /* Log a general fatal error once we're here */
3219     BmFatalErrorEx(BL_FATAL_ERROR_GENERIC, Status, 0, 0, 0);
3220 
3221 Quickie:
3222     /* Check if we should reboot */
3223     if ((RebootOnError) ||
3224         (BlpApplicationEntry.Flags & BL_APPLICATION_ENTRY_REBOOT_ON_ERROR))
3225     {
3226         /* Reboot the box */
3227         BlFwReboot();
3228         Status = STATUS_SUCCESS;
3229     }
3230     else
3231     {
3232         /* Return back to the caller with the error argument encoded */
3233         ReturnArguments = (PVOID)((ULONG_PTR)BootParameters + BootParameters->ReturnArgumentsOffset);
3234         ReturnArguments->Version = BL_RETURN_ARGUMENTS_VERSION;
3235         ReturnArguments->Status = Status;
3236 
3237         /* Tear down the boot library */
3238         BlDestroyLibrary();
3239     }
3240 
3241     /* Return back status */
3242     return Status;
3243 }
3244 
3245