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
BmGetOptionList(_In_ HANDLE BcdHandle,_In_ PGUID ObjectId,_In_ PBL_BCD_OPTION * OptionList)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
BmpUpdateApplicationOptions(_In_ HANDLE BcdHandle)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
BmpFwGetApplicationDirectoryPath(_In_ PUNICODE_STRING ApplicationDirectoryPath)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
BmFwInitializeBootDirectoryPath(VOID)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
BmOpenBootIni(VOID)400 BmOpenBootIni (
401 VOID
402 )
403 {
404 /* Don't yet handled boot.ini */
405 return STATUS_NOT_FOUND;
406 }
407
408 ULONG
BmpFatalErrorMessageFilter(_In_ NTSTATUS ErrorStatus,_Out_ PULONG ErrorResourceId)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
BmErrorPurge(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
BmpErrorLog(_In_ ULONG ErrorCode,_In_ NTSTATUS ErrorStatus,_In_ ULONG ErrorMsgId,_In_ PWCHAR FileName,_In_ ULONG HelpMsgId)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
BmFatalErrorEx(_In_ ULONG ErrorCode,_In_ ULONG_PTR Parameter1,_In_ ULONG_PTR Parameter2,_In_ ULONG_PTR Parameter3,_In_ ULONG_PTR Parameter4)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
BmpFwGetFullPath(_In_ PWCHAR FileName,_Out_ PWCHAR * FullPath)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
BmCloseDataStore(_In_ HANDLE Handle)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
BmOpenDataStore(_Out_ PHANDLE Handle)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
BlBsdInitializeLog(_In_ PBL_DEVICE_DESCRIPTOR LogDevice,_In_ PWCHAR LogPath,_In_ ULONG Flags)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
BmpInitializeBootStatusDataLog(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
BmFwMemoryInitialize(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
BmpBgDisplayClearScreen(_In_ ULONG Color)1056 BmpBgDisplayClearScreen (
1057 _In_ ULONG Color
1058 )
1059 {
1060 /* Not yet supported */
1061 return STATUS_NOT_IMPLEMENTED;
1062 }
1063
1064 NTSTATUS
BlXmiWrite(_In_ PWCHAR XmlTag)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
BlXmiInitialize(_In_ PWCHAR Stylesheet)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
BmFwVerifySelfIntegrity(VOID)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
BmFwRegisterRevocationList(VOID)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
BmResumeFromHibernate(_Out_ PHANDLE BcdResumeHandle)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
BmpProcessBadMemory(VOID)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
BmPurgeOption(_In_ HANDLE BcdHandle,_In_ PGUID ObjectId,_In_ ULONG Type)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
BmGetEntryDescription(_In_ HANDLE BcdHandle,_In_ PGUID ObjectId,_Out_ PBCD_OBJECT_DESCRIPTION Description)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
BmpPopulateBootEntryList(_In_ HANDLE BcdHandle,_In_ PGUID SequenceList,_In_ ULONG Flags,_Out_ PBL_LOADED_APPLICATION_ENTRY * BootSequence,_Out_ PULONG SequenceCount)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
BmGetBootSequence(_In_ HANDLE BcdHandle,_In_ PGUID SequenceList,_In_ ULONG SequenceListCount,_In_ ULONG Flags,_Out_ PBL_LOADED_APPLICATION_ENTRY ** BootSequence,_Out_ PULONG SequenceCount)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
BmEnumerateBootEntries(_In_ HANDLE BcdHandle,_Out_ PBL_LOADED_APPLICATION_ENTRY ** BootSequence,_Out_ PULONG SequenceCount)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
BmpGetDefaultBootEntry(_In_ PBL_LOADED_APPLICATION_ENTRY * Sequence,_In_ ULONG Count,_Out_ PBL_LOADED_APPLICATION_ENTRY * DefaultEntry,_Out_ PULONG DefaultIndex)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
BmGetBootMenuPolicy(_In_ PBL_LOADED_APPLICATION_ENTRY BootEntry)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
BmDisplayGetBootMenuStatus(_Out_ PL_MENU_STATUS MenuStatus)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
BmProcessCustomAction(_In_ HANDLE BcdHandle,_In_ PWCHAR ActionKey)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
BmpProcessBootEntry(_In_ HANDLE BcdHandle,_In_ PBL_LOADED_APPLICATION_ENTRY BootEntry,_Out_ PBOOLEAN ExitBootManager)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
BmpGetSelectedBootEntry(_In_ HANDLE BcdHandle,_Out_ PBL_LOADED_APPLICATION_ENTRY * SelectedBootEntry,_Out_ PULONG EntryIndex,_Out_ PBOOLEAN ExitBootManager)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
BmLaunchRecoverySequence(_In_ PBL_LOADED_APPLICATION_ENTRY BootEntry,_In_ ULONG LaunchCode)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
BmDisplayDumpError(_In_ PBL_LOADED_APPLICATION_ENTRY BootEntry,_In_ ULONG LaunchCode)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
BmpCreateDevices(_In_ PBL_LOADED_APPLICATION_ENTRY BootEntry)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
BmpTransferExecution(_In_ PBL_LOADED_APPLICATION_ENTRY BootEntry,_Out_ PULONG LaunchCode,_Out_ PBOOLEAN Recover)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
BmpLaunchBootEntry(_In_ PBL_LOADED_APPLICATION_ENTRY BootEntry,_Out_ PULONG EntryIndex,_In_ ULONG LaunchCode,_In_ BOOLEAN LaunchWinRe)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
BmMain(_In_ PBOOT_APPLICATION_PARAMETER_BLOCK BootParameters)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