[BOOTMGFW]: Implement BmEnumerateBootEntries.
[reactos.git] / reactos / boot / environ / app / bootmgr / bootmgr.c
1 /*
2 * COPYRIGHT: See COPYING.ARM in the top level directory
3 * PROJECT: ReactOS UEFI Boot Manager
4 * FILE: boot/environ/app/bootmgr/bootmgr.c
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
36 /* FUNCTIONS *****************************************************************/
37
38 NTSTATUS
39 BmGetOptionList (
40 _In_ HANDLE BcdHandle,
41 _In_ PGUID ObjectId,
42 _In_ PBL_BCD_OPTION *OptionList
43 )
44 {
45 NTSTATUS Status;
46 HANDLE ObjectHandle;
47 ULONG ElementSize, ElementCount, i, OptionsSize;
48 BcdElementType Type;
49 PBCD_ELEMENT_HEADER Header;
50 PBCD_ELEMENT BcdElements;
51 PBL_BCD_OPTION Options, Option, PreviousOption, DeviceOptions;
52 PBCD_DEVICE_OPTION DeviceOption;
53 GUID DeviceId;
54 PVOID DeviceData;
55
56 /* Open the BCD object requested */
57 ObjectHandle = NULL;
58 BcdElements = NULL;
59 Status = BcdOpenObject(BcdHandle, ObjectId, &ObjectHandle);
60 if (!NT_SUCCESS(Status))
61 {
62 goto Quickie;
63 }
64
65 /* Do the initial enumeration to get the size needed */
66 ElementSize = 0;
67 Status = BcdEnumerateAndUnpackElements(BcdHandle,
68 ObjectHandle,
69 NULL,
70 &ElementSize,
71 &ElementCount);
72 if (Status != STATUS_BUFFER_TOO_SMALL)
73 {
74 /* If we got success, that doesn't make any sense */
75 if (NT_SUCCESS(Status))
76 {
77 Status = STATUS_INVALID_PARAMETER;
78 }
79
80 /* Bail out */
81 goto Quickie;
82 }
83
84 /* Allocate a large-enough buffer */
85 BcdElements = BlMmAllocateHeap(ElementSize);
86 if (!BcdElements)
87 {
88 Status = STATUS_NO_MEMORY;
89 goto Quickie;
90 }
91
92 /* Now do the real enumeration to fill out the elements buffer */
93 Status = BcdEnumerateAndUnpackElements(BcdHandle,
94 ObjectHandle,
95 BcdElements,
96 &ElementSize,
97 &ElementCount);
98 if (!NT_SUCCESS(Status))
99 {
100 goto Quickie;
101 }
102
103 /* Go through each BCD option to add the sizes up */
104 OptionsSize = 0;
105 for (i = 0; i < ElementCount; i++)
106 {
107 OptionsSize += BcdElements[i].Header->Size + sizeof(BL_BCD_OPTION);
108 }
109
110 /* Allocate the required BCD option list */
111 Options = BlMmAllocateHeap(OptionsSize);
112 if (!Options)
113 {
114 Status = STATUS_NO_MEMORY;
115 goto Quickie;
116 }
117
118 /* Zero it out */
119 RtlZeroMemory(Options, OptionsSize);
120
121 /* Start going through each option */
122 PreviousOption = NULL;
123 Option = Options;
124 EfiPrintf(L"BCD Options found: %d\r\n", ElementCount);
125 for (i = 0; i < ElementCount; i++)
126 {
127 /* Read the header and type */
128 Header = BcdElements[i].Header;
129 Type.PackedValue = Header->Type;
130
131 /* Check if this option isn't already present */
132 if (!MiscGetBootOption(Options, Type.PackedValue))
133 {
134 /* It's a new option. Did we have an existing one? */
135 if (PreviousOption)
136 {
137 /* Link it to this new one */
138 PreviousOption->NextEntryOffset = (ULONG_PTR)Option -
139 (ULONG_PTR)Options;
140 }
141
142 /* Capture the type, size, data, and offset */
143 Option->Type = Type.PackedValue;
144 Option->DataSize = Header->Size;
145 RtlCopyMemory(Option + 1, BcdElements[i].Body, Header->Size);
146 Option->DataOffset = sizeof(BL_BCD_OPTION);
147
148 /* Check if this was a device */
149 if (Type.Format == BCD_TYPE_DEVICE)
150 {
151 /* Grab its GUID */
152 DeviceOption = (PBCD_DEVICE_OPTION)(Option + 1);
153 DeviceId = DeviceOption->AssociatedEntry;
154
155 /* Look up the options for that GUID */
156 Status = BmGetOptionList(BcdHandle, &DeviceId, &DeviceOptions);
157 if (NT_SUCCESS(Status))
158 {
159 /* Device data is after the device option */
160 DeviceData = (PVOID)((ULONG_PTR)DeviceOption + Header->Size);
161
162 /* Copy it */
163 RtlCopyMemory(DeviceData,
164 DeviceOptions,
165 BlGetBootOptionListSize(DeviceOptions));
166
167 /* Don't need this anymore */
168 BlMmFreeHeap(DeviceOptions);
169
170 /* Write the offset of the device options */
171 Option->ListOffset = (ULONG_PTR)DeviceData -
172 (ULONG_PTR)Option;
173 }
174 }
175
176 /* Save the previous option and go to the next one */
177 PreviousOption = Option;
178 Option = (PBL_BCD_OPTION)((ULONG_PTR)Option +
179 BlGetBootOptionSize(Option));
180 }
181 }
182
183 /* Return the pointer back, we've made it! */
184 *OptionList = Options;
185 Status = STATUS_SUCCESS;
186
187 Quickie:
188 /* Did we allocate a local buffer? Free it if so */
189 if (BcdElements)
190 {
191 BlMmFreeHeap(BcdElements);
192 }
193
194 /* Was the key open? Close it if so */
195 if (ObjectHandle)
196 {
197 BiCloseKey(ObjectHandle);
198 }
199
200 /* Return the option list parsing status */
201 return Status;
202 }
203
204 NTSTATUS
205 BmpUpdateApplicationOptions (
206 _In_ HANDLE BcdHandle
207 )
208 {
209 NTSTATUS Status;
210 PBL_BCD_OPTION Options;
211
212 /* Get the boot option list */
213 Status = BmGetOptionList(BcdHandle, &BmApplicationIdentifier, &Options);
214 if (!NT_SUCCESS(Status))
215 {
216 return Status;
217 }
218
219 /* Append the options, free the local buffer, and return success */
220 BlAppendBootOptions(&BlpApplicationEntry, Options);
221 BlMmFreeHeap(Options);
222 return STATUS_SUCCESS;
223 }
224
225 NTSTATUS
226 BmpFwGetApplicationDirectoryPath (
227 _In_ PUNICODE_STRING ApplicationDirectoryPath
228 )
229 {
230 NTSTATUS Status;
231 ULONG i, AppPathLength;
232 PWCHAR ApplicationPath, PathCopy;
233
234 /* Clear the incoming string */
235 ApplicationDirectoryPath->Length = 0;
236 ApplicationDirectoryPath->MaximumLength = 0;
237 ApplicationDirectoryPath->Buffer = 0;
238
239 /* Get the boot application path */
240 ApplicationPath = NULL;
241 Status = BlGetBootOptionString(BlpApplicationEntry.BcdData,
242 BcdLibraryString_ApplicationPath,
243 &ApplicationPath);
244 if (NT_SUCCESS(Status))
245 {
246 /* Calculate the length of the application path */
247 for (i = wcslen(ApplicationPath) - 1; i > 0; i--)
248 {
249 /* Keep going until the path separator */
250 if (ApplicationPath[i] == OBJ_NAME_PATH_SEPARATOR)
251 {
252 break;
253 }
254 }
255
256 /* Check if we have space for one more character */
257 Status = RtlULongAdd(i, 1, &AppPathLength);
258 if (NT_SUCCESS(Status))
259 {
260 /* Check if it's safe to multiply by two */
261 Status = RtlULongMult(AppPathLength, sizeof(WCHAR), &AppPathLength);
262 if (NT_SUCCESS(Status))
263 {
264 /* Allocate a copy for the string */
265 PathCopy = BlMmAllocateHeap(AppPathLength);
266 if (PathCopy)
267 {
268 /* NULL-terminate it */
269 RtlCopyMemory(PathCopy,
270 ApplicationPath,
271 AppPathLength - sizeof(UNICODE_NULL));
272 PathCopy[AppPathLength] = UNICODE_NULL;
273
274 /* Finally, initialize the outoing string */
275 RtlInitUnicodeString(ApplicationDirectoryPath, PathCopy);
276 }
277 else
278 {
279 /* No memory, fail */
280 Status = STATUS_NO_MEMORY;
281 }
282 }
283 }
284 }
285
286 /* Check if we had an application path */
287 if (ApplicationPath)
288 {
289 /* No longer need this, free it */
290 BlMmFreeHeap(ApplicationPath);
291 }
292
293 /* All done! */
294 return Status;
295 }
296
297 NTSTATUS
298 BmFwInitializeBootDirectoryPath (
299 VOID
300 )
301 {
302 PWCHAR FinalPath;
303 NTSTATUS Status;
304 PWCHAR BcdDirectory;
305 UNICODE_STRING BcdPath;
306 ULONG FinalSize;
307 ULONG FileHandle, DeviceHandle;
308
309 /* Initialize everything for failure */
310 BcdPath.MaximumLength = 0;
311 BcdPath.Buffer = NULL;
312 BcdDirectory = NULL;
313 FinalPath = NULL;
314 FileHandle = -1;
315 DeviceHandle = -1;
316
317 /* Try to open the boot device */
318 Status = BlpDeviceOpen(BlpBootDevice,
319 BL_DEVICE_READ_ACCESS,
320 0,
321 &DeviceHandle);
322 if (!NT_SUCCESS(Status))
323 {
324 EfiPrintf(L"Device open failed: %lx\r\n", Status);
325 goto Quickie;
326 }
327
328 /* Get the directory path */
329 Status = BmpFwGetApplicationDirectoryPath(&BcdPath);
330 BcdDirectory = BcdPath.Buffer;
331 if (!NT_SUCCESS(Status))
332 {
333 goto Quickie;
334 }
335
336 /* Add the BCD file name to it */
337 FinalSize = BcdPath.MaximumLength + sizeof(L"\\BCD") - sizeof(UNICODE_NULL);
338 if (FinalSize < BcdPath.MaximumLength)
339 {
340 goto Quickie;
341 }
342
343 /* Allocate space for the final path */
344 FinalPath = BlMmAllocateHeap(FinalSize);
345 if (!FinalPath)
346 {
347 goto Quickie;
348 }
349
350 /* Build it */
351 RtlZeroMemory(FinalPath, FinalSize);
352 RtlCopyMemory(FinalPath, BcdDirectory, BcdPath.MaximumLength);
353 wcsncat(FinalPath, L"\\BCD", FinalSize / sizeof(WCHAR));
354
355 /* Try to open the file */
356 Status = BlFileOpen(DeviceHandle,
357 FinalPath,
358 BL_FILE_READ_ACCESS,
359 &FileHandle);
360 if (!NT_SUCCESS(Status))
361 {
362 BootDirectory = BcdDirectory;
363 goto Quickie;
364 }
365
366 /* Save the boot directory */
367 BootDirectory = L"\\EFI\\Boot"; /* Should be EFI\\ReactOS\\Boot */
368
369 Quickie:
370 /* Free all the allocations we made */
371 if (BcdDirectory)
372 {
373 Status = BlMmFreeHeap(BcdDirectory);
374 }
375 if (FinalPath)
376 {
377 Status = BlMmFreeHeap(FinalPath);
378 }
379
380 /* Close the BCD file */
381 if (FileHandle != -1)
382 {
383 Status = BlFileClose(FileHandle);
384 }
385
386 /* Close the boot device */
387 if (DeviceHandle != -1)
388 {
389 Status = BlDeviceClose(DeviceHandle);
390 }
391
392 /* Return back to the caller */
393 return Status;
394 }
395
396 NTSTATUS
397 BmOpenBootIni (
398 VOID
399 )
400 {
401 /* Don't yet handled boot.ini */
402 return STATUS_NOT_FOUND;
403 }
404
405 ULONG
406 BmpFatalErrorMessageFilter (
407 _In_ NTSTATUS ErrorStatus,
408 _Out_ PULONG ErrorResourceId
409 )
410 {
411 ULONG Result;
412
413 /* Assume no message for now, check for known status message */
414 Result = 0;
415 switch (ErrorStatus)
416 {
417 /* Convert each status to a resource ID */
418 case STATUS_UNEXPECTED_IO_ERROR:
419 *ErrorResourceId = 9017;
420 Result = 1;
421 break;
422 case STATUS_IMAGE_CHECKSUM_MISMATCH:
423 *ErrorResourceId = 9018;
424 break;
425 case STATUS_INVALID_IMAGE_WIN_64:
426 *ErrorResourceId = 9016;
427 break;
428 case 0xC0000428:
429 *ErrorResourceId = 9019;
430 Result = 2;
431 break;
432 case 0xC0210000:
433 *ErrorResourceId = 9013;
434 break;
435 }
436
437 /* Return the type of message */
438 return Result;
439 }
440
441 VOID
442 BmErrorPurge (
443 VOID
444 )
445 {
446 /* Check if a boot error is present */
447 if (BmpPackedBootError.BootError)
448 {
449 /* Purge it */
450 BlMmFreeHeap(BmpPackedBootError.BootError);
451 BmpPackedBootError.BootError = NULL;
452 }
453
454 /* Zero out the packed buffer */
455 BmpPackedBootError.Size = 0;
456 BmpInternalBootError = NULL;
457 RtlZeroMemory(&BmpErrorBuffer, sizeof(BmpErrorBuffer));
458 }
459
460 VOID
461 BmpErrorLog (
462 _In_ ULONG ErrorCode,
463 _In_ NTSTATUS ErrorStatus,
464 _In_ ULONG ErrorMsgId,
465 _In_ PWCHAR FileName,
466 _In_ ULONG HelpMsgId
467 )
468 {
469 PWCHAR ErrorMsgString;
470
471 /* Check if we already had an error */
472 if (BmpInternalBootError)
473 {
474 /* Purge it */
475 BmErrorPurge();
476 }
477
478 /* Find the string for this error ID */
479 ErrorMsgString = BlResourceFindMessage(ErrorMsgId);
480 if (ErrorMsgString)
481 {
482 /* Fill out the error buffer */
483 BmpErrorBuffer.Unknown1 = 0;
484 BmpErrorBuffer.Unknown2 = 0;
485 BmpErrorBuffer.ErrorString = ErrorMsgString;
486 BmpErrorBuffer.FileName = FileName;
487 BmpErrorBuffer.ErrorCode = ErrorCode;
488 BmpErrorBuffer.ErrorStatus = ErrorStatus;
489 BmpErrorBuffer.HelpMsgId = HelpMsgId;
490 BmpInternalBootError = &BmpErrorBuffer;
491 }
492 }
493
494 VOID
495 BmFatalErrorEx (
496 _In_ ULONG ErrorCode,
497 _In_ ULONG_PTR Parameter1,
498 _In_ ULONG_PTR Parameter2,
499 _In_ ULONG_PTR Parameter3,
500 _In_ ULONG_PTR Parameter4
501 )
502 {
503 PWCHAR FileName, Buffer;
504 NTSTATUS ErrorStatus;
505 WCHAR FormatString[256];
506 ULONG ErrorResourceId, ErrorHelpId;
507 BOOLEAN Restart, NoError;
508
509 /* Assume no buffer for now */
510 Buffer = NULL;
511
512 /* Check what error code is being raised */
513 switch (ErrorCode)
514 {
515 /* Error reading the BCD */
516 case BL_FATAL_ERROR_BCD_READ:
517
518 /* Check if we have a name for the BCD file */
519 if (Parameter1)
520 {
521 /* Check if the name fits into our buffer */
522 FileName = (PWCHAR)Parameter1;
523 if (wcslen(FileName) < sizeof(BmpFileNameBuffer))
524 {
525 /* Copy it in there */
526 Buffer = BmpFileNameBuffer;
527 wcsncpy(BmpFileNameBuffer,
528 FileName,
529 RTL_NUMBER_OF(BmpFileNameBuffer));
530 }
531 }
532
533 /* If we don't have a buffer, use an empty one */
534 if (!Buffer)
535 {
536 Buffer = ParentFileName;
537 }
538
539 /* The NTSTATUS code is in parameter 2*/
540 ErrorStatus = (NTSTATUS)Parameter2;
541
542 /* Build the error string */
543 swprintf(FormatString,
544 L"\nAn error occurred (%08x) while attempting "
545 L"to read the boot configuration data file %s\n",
546 ErrorStatus,
547 Buffer);
548
549 /* Select the resource ID message */
550 ErrorResourceId = 9002;
551 break;
552
553 case BL_FATAL_ERROR_BCD_ENTRIES:
554
555 /* File name is in parameter 1 */
556 FileName = (PWCHAR)Parameter1;
557
558 /* The NTSTATUS code is in parameter 2*/
559 ErrorStatus = (NTSTATUS)Parameter2;
560
561 /* Build the error string */
562 swprintf(FormatString,
563 L"\nNo valid entries found in the boot configuration data file %s\n",
564 FileName);
565
566 /* Select the resource ID message */
567 ErrorResourceId = 9007;
568 break;
569
570 case BL_FATAL_ERROR_BCD_PARSE:
571
572 /* File name isin parameter 1 */
573 FileName = (PWCHAR)Parameter1;
574
575 /* The NTSTATUS code is in parameter 2*/
576 ErrorStatus = (NTSTATUS)Parameter2;
577
578 /* Build the error string */
579 swprintf(FormatString,
580 L"\nThe boot configuration file %s is invalid (%08x).\n",
581 FileName,
582 ErrorStatus);
583
584 /* Select the resource ID message */
585 ErrorResourceId = 9015;
586 break;
587
588 case BL_FATAL_ERROR_GENERIC:
589
590 /* The NTSTATUS code is in parameter 1*/
591 ErrorStatus = (NTSTATUS)Parameter1;
592
593 /* Build the error string */
594 swprintf(FormatString,
595 L"\nThe boot manager experienced an error (%08x).\n",
596 ErrorStatus);
597
598 /* Select the resource ID message */
599 ErrorResourceId = 9005;
600 break;
601
602 default:
603
604 /* The rest is not yet handled */
605 EfiPrintf(L"Unexpected fatal error: %lx\r\n", ErrorCode);
606 while (1);
607 break;
608 }
609
610 /* Check if the BCD option for restart is set */
611 BlGetBootOptionBoolean(BlpApplicationEntry.BcdData,
612 BcdLibraryBoolean_RestartOnFailure,
613 &Restart);
614 if (Restart)
615 {
616 /* Yes, so no error should be shown since we'll auto-restart */
617 NoError = TRUE;
618 }
619 else
620 {
621 /* Check if the option for not showing errors is set in the BCD */
622 BlGetBootOptionBoolean(BlpApplicationEntry.BcdData,
623 BcdBootMgrBoolean_NoErrorDisplay,
624 &NoError);
625 }
626
627 /* Do we want an error? */
628 if (!NoError)
629 {
630 /* Yep, print it and then raise an error */
631 BlStatusPrint(FormatString);
632 BlStatusError(1, ErrorCode, Parameter1, Parameter2, Parameter3);
633 }
634
635 /* Get the help message ID */
636 ErrorHelpId = BmpFatalErrorMessageFilter(ErrorStatus, &ErrorResourceId);
637 BmpErrorLog(ErrorCode, ErrorStatus, ErrorResourceId, Buffer, ErrorHelpId);
638 }
639
640 NTSTATUS
641 BmpFwGetFullPath (
642 _In_ PWCHAR FileName,
643 _Out_ PWCHAR* FullPath
644 )
645 {
646 NTSTATUS Status;
647 ULONG BootDirLength, PathLength;
648
649 /* Compute the length of the directory, and add a NUL */
650 BootDirLength = wcslen(BootDirectory);
651 Status = RtlULongAdd(BootDirLength, 1, &BootDirLength);
652 if (!NT_SUCCESS(Status))
653 {
654 goto Quickie;
655 }
656
657 /* Add the length of the file, make sure it fits */
658 PathLength = wcslen(FileName);
659 Status = RtlULongAdd(PathLength, BootDirLength, &PathLength);
660 if (!NT_SUCCESS(Status))
661 {
662 goto Quickie;
663 }
664
665 /* Convert to bytes */
666 Status = RtlULongLongToULong(PathLength * sizeof(WCHAR), &PathLength);
667 if (!NT_SUCCESS(Status))
668 {
669 goto Quickie;
670 }
671
672 /* Allocate the full path */
673 *FullPath = BlMmAllocateHeap(PathLength);
674 if (*FullPath)
675 {
676 /* Copy the directory followed by the file name */
677 wcsncpy(*FullPath, BootDirectory, PathLength / sizeof(WCHAR));
678 wcsncat(*FullPath, FileName, PathLength / sizeof(WCHAR));
679 }
680 else
681 {
682 /* Bail out since we have no memory */
683 Status = STATUS_NO_MEMORY;
684 }
685
686 Quickie:
687 /* Return to caller */
688 return Status;
689 }
690
691 VOID
692 BmCloseDataStore (
693 _In_ HANDLE Handle
694 )
695 {
696 /* Check if boot.ini data needs to be freed */
697 if (BmBootIniUsed)
698 {
699 EfiPrintf(L"Boot.ini not handled\r\n");
700 }
701
702 /* Dereference the hive and close the key */
703 BiDereferenceHive(Handle);
704 BiCloseKey(Handle);
705 }
706
707 NTSTATUS
708 BmOpenDataStore (
709 _Out_ PHANDLE Handle
710 )
711 {
712 NTSTATUS Status;
713 PBL_DEVICE_DESCRIPTOR BcdDevice;
714 PWCHAR BcdPath, FullPath, PathBuffer;
715 BOOLEAN HavePath;
716 ULONG PathLength, FullSize;
717 PVOID FinalBuffer;
718 UNICODE_STRING BcdString;
719
720 /* Initialize variables */
721 PathBuffer = NULL;
722 BcdDevice = NULL;
723 BcdPath = NULL;
724 HavePath = FALSE;
725
726 /* Check if a boot.ini file exists */
727 Status = BmOpenBootIni();
728 if (NT_SUCCESS(Status))
729 {
730 BmBootIniUsed = TRUE;
731 }
732
733 /* Check on which device the BCD is */
734 Status = BlGetBootOptionDevice(BlpApplicationEntry.BcdData,
735 BcdBootMgrDevice_BcdDevice,
736 &BcdDevice,
737 NULL);
738 if (!NT_SUCCESS(Status))
739 {
740 /* It's not on a custom device, so it must be where we are */
741 Status = BlGetBootOptionDevice(BlpApplicationEntry.BcdData,
742 BcdLibraryDevice_ApplicationDevice,
743 &BcdDevice,
744 NULL);
745 if (!NT_SUCCESS(Status))
746 {
747 /* This BCD option is required */
748 goto Quickie;
749 }
750 }
751
752 /* Next, check what file contains the BCD */
753 Status = BlGetBootOptionString(BlpApplicationEntry.BcdData,
754 BcdBootMgrString_BcdFilePath,
755 &BcdPath);
756 if (NT_SUCCESS(Status))
757 {
758 /* We don't handle custom BCDs yet */
759 EfiPrintf(L"Custom BCD Not handled: %s\r\n", BcdPath);
760 Status = STATUS_NOT_IMPLEMENTED;
761 goto Quickie;
762 }
763
764 /* Now check if the BCD is on a remote share */
765 if (BcdDevice->DeviceType == UdpDevice)
766 {
767 /* Nope. Nope. Nope */
768 EfiPrintf(L"UDP device Not handled\r\n");
769 Status = STATUS_NOT_IMPLEMENTED;
770 goto Quickie;
771 }
772
773 /* Otherwise, compute the hardcoded path of the BCD */
774 Status = BmpFwGetFullPath(L"\\BCD", &FullPath);
775 if (!NT_SUCCESS(Status))
776 {
777 /* User the raw path */
778 PathBuffer = BcdPath;
779 }
780 else
781 {
782 /* Use the path we got */
783 PathBuffer = FullPath;
784 HavePath = TRUE;
785 }
786
787 /* Check if we failed to get the BCD path */
788 if (!NT_SUCCESS(Status))
789 {
790 goto Quickie;
791 }
792
793 /* Add a NUL to the path, make sure it'll fit */
794 PathLength = wcslen(PathBuffer);
795 Status = RtlULongAdd(PathLength, 1, &PathLength);
796 if (!NT_SUCCESS(Status))
797 {
798 goto Quickie;
799 }
800
801 /* Convert to bytes */
802 Status = RtlULongLongToULong(PathLength * sizeof(WCHAR), &PathLength);
803 if (!NT_SUCCESS(Status))
804 {
805 goto Quickie;
806 }
807
808 /* Now add the size of the path to the device path, check if it fits */
809 Status = RtlULongAdd(PathLength, BcdDevice->Size, &FullSize);
810 if (!NT_SUCCESS(Status))
811 {
812 goto Quickie;
813 }
814
815 /* Allocate a final structure to hold both entities */
816 FinalBuffer = BlMmAllocateHeap(FullSize);
817 if (!FinalBuffer)
818 {
819 Status = STATUS_NO_MEMORY;
820 goto Quickie;
821 }
822
823 /* Copy the device path and file path into the final buffer */
824 RtlCopyMemory(FinalBuffer, BcdDevice, BcdDevice->Size);
825 RtlCopyMemory((PVOID)((ULONG_PTR)FinalBuffer + BcdDevice->Size),
826 PathBuffer,
827 PathLength);
828
829 /* Now tell the BCD engine to open the store */
830 BcdString.Length = FullSize;
831 BcdString.MaximumLength = FullSize;
832 BcdString.Buffer = FinalBuffer;
833 Status = BcdOpenStoreFromFile(&BcdString, Handle);
834
835 /* Free our final buffer */
836 BlMmFreeHeap(FinalBuffer);
837
838 Quickie:
839 /* Did we allocate a device? */
840 if (BcdDevice)
841 {
842 /* Free it */
843 BlMmFreeHeap(BcdDevice);
844 }
845
846 /* Is this the failure path? */
847 if (!NT_SUCCESS(Status))
848 {
849 /* Raise a fatal error */
850 BmFatalErrorEx(BL_FATAL_ERROR_BCD_READ,
851 (ULONG_PTR)PathBuffer,
852 Status,
853 0,
854 0);
855 }
856
857 /* Did we get an allocated path? */
858 if ((PathBuffer) && (HavePath))
859 {
860 /* Free it */
861 BlMmFreeHeap(PathBuffer);
862 }
863
864 /* Return back to the caller */
865 return Status;
866 }
867
868 typedef struct _BL_BSD_LOG_OBJECT
869 {
870 ULONG DeviceId;
871 ULONG FileId;
872 ULONG Unknown;
873 ULONG Size;
874 ULONG Flags;
875 } BL_BSD_LOG_OBJECT, *PBL_BSD_LOG_OBJECT;
876
877 BL_BSD_LOG_OBJECT BsdpLogObject;
878 BOOLEAN BsdpLogObjectInitialized;
879
880 VOID
881 BlBsdInitializeLog (
882 _In_ PBL_DEVICE_DESCRIPTOR LogDevice,
883 _In_ PWCHAR LogPath,
884 _In_ ULONG Flags
885 )
886 {
887 NTSTATUS Status;
888
889 /* Don't initialize twice */
890 if (BsdpLogObjectInitialized)
891 {
892 return;
893 }
894
895 /* Set invalid IDs for now */
896 BsdpLogObject.DeviceId = -1;
897 BsdpLogObject.FileId = -1;
898
899 /* Open the BSD device */
900 Status = BlpDeviceOpen(LogDevice,
901 BL_DEVICE_READ_ACCESS | BL_DEVICE_WRITE_ACCESS,
902 0,
903 &BsdpLogObject.DeviceId);
904 if (!NT_SUCCESS(Status))
905 {
906 /* Welp that didn't work */
907 goto FailurePath;
908 }
909
910 /* Now open the BSD itself */
911 Status = BlFileOpen(BsdpLogObject.DeviceId,
912 LogPath,
913 BL_FILE_READ_ACCESS | BL_FILE_WRITE_ACCESS,
914 &BsdpLogObject.FileId);
915 if (!NT_SUCCESS(Status))
916 {
917 /* D'oh */
918 goto FailurePath;
919 }
920
921 /* The BSD is open. Start doing stuff to it */
922 EfiPrintf(L"Unimplemented BSD path\r\n");
923 Status = STATUS_NOT_IMPLEMENTED;
924
925 FailurePath:
926 /* Close the BSD if we had it open */
927 if (BsdpLogObject.FileId != -1)
928 {
929 BlFileClose(BsdpLogObject.FileId);
930 }
931
932 /* Close the device if we had it open */
933 if (BsdpLogObject.DeviceId != -1)
934 {
935 BlDeviceClose(BsdpLogObject.DeviceId);
936 }
937
938 /* Set BSD object to its uninitialized state */
939 BsdpLogObjectInitialized = FALSE;
940 BsdpLogObject.FileId = 0;
941 BsdpLogObject.DeviceId = 0;
942 BsdpLogObject.Flags = 0;
943 BsdpLogObject.Unknown = 0;
944 BsdpLogObject.Size = 0;
945 }
946
947 VOID
948 BmpInitializeBootStatusDataLog (
949 VOID
950 )
951 {
952 NTSTATUS Status;
953 PBL_DEVICE_DESCRIPTOR BsdDevice;
954 PWCHAR BsdPath;
955 ULONG Flags;
956 BOOLEAN PreserveBsd;
957
958 /* Initialize locals */
959 BsdPath = NULL;
960 BsdDevice = NULL;
961 Flags = 0;
962
963 /* Check if the BSD is stored in a custom device */
964 Status = BlGetBootOptionDevice(BlpApplicationEntry.BcdData,
965 BcdLibraryDevice_BsdLogDevice,
966 &BsdDevice,
967 NULL);
968 if (!NT_SUCCESS(Status))
969 {
970 /* Nope, use the boot device */
971 BsdDevice = BlpBootDevice;
972 }
973
974 /* Check if the path is custom as well */
975 Status = BlGetBootOptionString(BlpApplicationEntry.BcdData,
976 BcdLibraryString_BsdLogPath,
977 &BsdPath);
978 if (!NT_SUCCESS(Status))
979 {
980 /* Nope, use our default path */
981 Status = BmpFwGetFullPath(L"\\bootstat.dat", &BsdPath);
982 if (!NT_SUCCESS(Status))
983 {
984 BsdPath = NULL;
985 }
986
987 /* Set preserve flag */
988 Flags = 1;
989 }
990 else
991 {
992 /* Set preserve flag */
993 Flags = 1;
994 }
995
996 /* Finally, check if the BSD should be preserved */
997 Status = BlGetBootOptionBoolean(BlpApplicationEntry.BcdData,
998 BcdLibraryBoolean_PreserveBsdLog,
999 &PreserveBsd);
1000 if (!(NT_SUCCESS(Status)) || !(PreserveBsd))
1001 {
1002 /* We failed to read, or we were asked not to preserve it */
1003 Flags = 0;
1004 }
1005
1006 /* Initialize the log */
1007 BlBsdInitializeLog(BsdDevice, BsdPath, Flags);
1008
1009 /* Free the BSD device descriptor if we had one */
1010 if (BsdDevice)
1011 {
1012 BlMmFreeHeap(BsdDevice);
1013 }
1014
1015 /* Free the BSD path if we had one */
1016 if ((Flags) && (BsdPath))
1017 {
1018 BlMmFreeHeap(BsdPath);
1019 }
1020 }
1021
1022 VOID
1023 BmFwMemoryInitialize (
1024 VOID
1025 )
1026 {
1027 NTSTATUS Status;
1028 PHYSICAL_ADDRESS PhysicalAddress;
1029 BL_ADDRESS_RANGE AddressRange;
1030
1031 /* Select the range below 1MB */
1032 AddressRange.Maximum = 0xFFFFF;
1033 AddressRange.Minimum = 0;
1034
1035 /* Allocate one reserved page with the "reserved" attribute */
1036 Status = MmPapAllocatePhysicalPagesInRange(&PhysicalAddress,
1037 BlApplicationReserved,
1038 1,
1039 BlMemoryReserved,
1040 0,
1041 &MmMdlUnmappedAllocated,
1042 &AddressRange,
1043 BL_MM_REQUEST_DEFAULT_TYPE);
1044 if (!NT_SUCCESS(Status))
1045 {
1046 /* Print a message on error, but keep going */
1047 BlStatusPrint(L"BmFwMemoryInitialize: Failed to allocate a page below 1MB. Status: 0x%08x\r\n",
1048 Status);
1049 }
1050 }
1051
1052 NTSTATUS
1053 BmpBgDisplayClearScreen (
1054 _In_ ULONG Color
1055 )
1056 {
1057 /* Not yet supported */
1058 return STATUS_NOT_IMPLEMENTED;
1059 }
1060
1061 NTSTATUS
1062 BlXmiWrite (
1063 _In_ PWCHAR XmlTag
1064 )
1065 {
1066 /* Sigh */
1067 EfiPrintf(L"XML: %s\r\n", XmlTag);
1068 return STATUS_NOT_IMPLEMENTED;
1069 }
1070
1071 NTSTATUS
1072 BlXmiInitialize (
1073 _In_ PWCHAR Stylesheet
1074 )
1075 {
1076 /* Reset the cursor type */
1077 BlDisplaySetCursorType(0);
1078
1079 /* Nope, not doing any XML stuff */
1080 return STATUS_SUCCESS;
1081 }
1082
1083 VOID
1084 BlImgQueryCodeIntegrityBootOptions (
1085 _In_ PBL_LOADED_APPLICATION_ENTRY ApplicationEntry,
1086 _Out_ PBOOLEAN IntegrityChecksDisabled,
1087 _Out_ PBOOLEAN TestSigningEnabled
1088 )
1089 {
1090
1091 NTSTATUS Status;
1092 BOOLEAN Value;
1093
1094 /* Check if /DISABLEINTEGRITYCHECKS is on */
1095 Status = BlGetBootOptionBoolean(ApplicationEntry->BcdData,
1096 BcdLibraryBoolean_DisableIntegrityChecks,
1097 &Value);
1098 *IntegrityChecksDisabled = NT_SUCCESS(Status) && (Value);
1099
1100 /* Check if /TESTSIGNING is on */
1101 Status = BlGetBootOptionBoolean(ApplicationEntry->BcdData,
1102 BcdLibraryBoolean_AllowPrereleaseSignatures,
1103 &Value);
1104 *TestSigningEnabled = NT_SUCCESS(Status) && (Value);
1105 }
1106
1107 NTSTATUS
1108 BmFwVerifySelfIntegrity (
1109 VOID
1110 )
1111 {
1112 /* Check if we're booted by UEFI off the DVD directlry */
1113 if ((BlpBootDevice->DeviceType == LocalDevice) &&
1114 (BlpBootDevice->Local.Type == CdRomDevice) &&
1115 (BlpApplicationFlags & BL_APPLICATION_FLAG_CONVERTED_FROM_EFI))
1116 {
1117 /* Windows actually bypasses integrity checks in this case. Works for us */
1118 return STATUS_SUCCESS;
1119 }
1120
1121 /* Our binaries aren't signed, so always return failure */
1122 return 0xC0000428;
1123 }
1124
1125 NTSTATUS
1126 BmFwRegisterRevocationList (
1127 VOID
1128 )
1129 {
1130 NTSTATUS Status;
1131 BOOLEAN SecureBootEnabled;
1132
1133 /* Is SecureBoot enabled? */
1134 Status = BlSecureBootIsEnabled(&SecureBootEnabled);
1135 if ((NT_SUCCESS(Status)) && (SecureBootEnabled))
1136 {
1137 EfiPrintf(L"SB not implemented revok\r\n");
1138 return STATUS_NOT_IMPLEMENTED;
1139 }
1140 else
1141 {
1142 /* Nothing to do without SecureBoot */
1143 Status = STATUS_SUCCESS;
1144 }
1145
1146 /* Return revocation result back to caller */
1147 return Status;
1148 }
1149
1150 NTSTATUS
1151 BmResumeFromHibernate (
1152 _Out_ PHANDLE BcdResumeHandle
1153 )
1154 {
1155 NTSTATUS Status;
1156 BOOLEAN AttemptResume;
1157
1158 /* Should we attempt to resume from hibernation? */
1159 Status = BlGetBootOptionBoolean(BlpApplicationEntry.BcdData,
1160 BcdBootMgrBoolean_AttemptResume,
1161 &AttemptResume);
1162 if (!NT_SUCCESS(Status))
1163 {
1164 /* Nope. Is automatic restart on crash enabled? */
1165 AttemptResume = FALSE;
1166 Status = BlGetBootOptionBoolean(BlpApplicationEntry.BcdData,
1167 BcdOSLoaderBoolean_DisableCrashAutoReboot,
1168 &AttemptResume);
1169 AttemptResume = (NT_SUCCESS(Status) && (AttemptResume));
1170 }
1171
1172 /* Don't do anything if there's no need to resume anything */
1173 if (!AttemptResume)
1174 {
1175 return STATUS_SUCCESS;
1176 }
1177
1178 /* Not yet implemented */
1179 EfiPrintf(L"Resume not supported\r\n");
1180 return STATUS_NOT_IMPLEMENTED;
1181 }
1182
1183 NTSTATUS
1184 BmpProcessBadMemory (
1185 VOID
1186 )
1187 {
1188 BL_PD_DATA_BLOB BadMemoryData;
1189 NTSTATUS Status;
1190
1191 /* Try to get the memory data from the memtest application */
1192 BadMemoryData.BlobSize = 0;
1193 BadMemoryData.Data = NULL;
1194 BadMemoryData.DataSize = 0;
1195 Status = BlPdQueryData(&BadMemoryGuid, NULL, &BadMemoryData);
1196 if (Status != STATUS_BUFFER_TOO_SMALL)
1197 {
1198 /* No results, or some other error */
1199 return Status;
1200 }
1201
1202 /* Not yet implemented */
1203 EfiPrintf(L"Bad page list persistence not implemented\r\n");
1204 return STATUS_NOT_IMPLEMENTED;
1205 }
1206
1207 NTSTATUS
1208 BmPurgeOption (
1209 _In_ HANDLE BcdHandle,
1210 _In_ PGUID ObjectId,
1211 _In_ ULONG Type
1212 )
1213 {
1214 HANDLE ObjectHandle;
1215 NTSTATUS Status;
1216
1217 /* Open the object */
1218 Status = BcdOpenObject(BcdHandle, ObjectId, &ObjectHandle);
1219 if (NT_SUCCESS(Status))
1220 {
1221 /* Delete the element */
1222 BcdDeleteElement(ObjectHandle, Type);
1223
1224 /* Close the object and set success */
1225 BiCloseKey(ObjectHandle);
1226 Status = STATUS_SUCCESS;
1227 }
1228
1229 /* Return the result */
1230 return Status;
1231 }
1232
1233 NTSTATUS
1234 BmpPopulateBootEntryList (
1235 _In_ HANDLE BcdHandle,
1236 _In_ PGUID SequenceList,
1237 _In_ ULONG Flags,
1238 _Out_ PBL_LOADED_APPLICATION_ENTRY* BootSequence,
1239 _Out_ PULONG SequenceCount
1240 )
1241 {
1242 EfiPrintf(L"Boot population not yet supported\r\n");
1243 *SequenceCount = 0;
1244 *BootSequence = NULL;
1245 return STATUS_NOT_IMPLEMENTED;
1246 }
1247
1248 NTSTATUS
1249 BmGetBootSequence (
1250 _In_ HANDLE BcdHandle,
1251 _In_ PGUID SequenceList,
1252 _In_ ULONG SequenceListCount,
1253 _In_ ULONG Flags,
1254 _Out_ PBL_LOADED_APPLICATION_ENTRY** BootSequence,
1255 _Out_ PULONG SequenceCount
1256 )
1257 {
1258 PBL_LOADED_APPLICATION_ENTRY* Sequence;
1259 ULONG Count;
1260 NTSTATUS Status;
1261
1262 /* Allocate the sequence list */
1263 Sequence = BlMmAllocateHeap(SequenceListCount * sizeof(*Sequence));
1264 if (!Sequence)
1265 {
1266 return STATUS_NO_MEMORY;
1267 }
1268
1269 /* Populate the sequence list */
1270 Status = BmpPopulateBootEntryList(BcdHandle,
1271 SequenceList,
1272 Flags,
1273 Sequence,
1274 &Count);
1275 if (!NT_SUCCESS(Status))
1276 {
1277 /* Free the list on failure */
1278 BlMmFreeHeap(Sequence);
1279 }
1280 else
1281 {
1282 /* Otherwise, set success and return the list and count */
1283 Status = STATUS_SUCCESS;
1284 *BootSequence = Sequence;
1285 *SequenceCount = Count;
1286 }
1287
1288 /* All done */
1289 return Status;
1290 }
1291
1292 NTSTATUS
1293 BmEnumerateBootEntries (
1294 _In_ HANDLE BcdHandle,
1295 _Out_ PBL_LOADED_APPLICATION_ENTRY **BootSequence,
1296 _Out_ PULONG SequenceCount
1297 )
1298 {
1299 NTSTATUS Status;
1300 ULONG BootIndex, BootIniCount, BootEntryCount, BcdCount;
1301 PBL_LOADED_APPLICATION_ENTRY* Sequence;
1302 PGUID DisplayOrder;
1303 GUID DefaultObject;
1304 BOOLEAN UseDisplayList;
1305
1306 /* Initialize locals */
1307 BootIndex = 0;
1308
1309 /* First try to get the display list, if any */
1310 UseDisplayList = TRUE;
1311 Status = BlGetBootOptionGuidList(BlpApplicationEntry.BcdData,
1312 BcdBootMgrObjectList_DisplayOrder,
1313 &DisplayOrder,
1314 &BcdCount);
1315 if (!NT_SUCCESS(Status))
1316 {
1317 /* No list, get the default entry instead */
1318 Status = BlGetBootOptionGuid(BlpApplicationEntry.BcdData,
1319 BcdBootMgrObject_DefaultObject,
1320 &DefaultObject);
1321 if (NT_SUCCESS(Status))
1322 {
1323 /* Set the array to just our entry */
1324 UseDisplayList = FALSE;
1325 BcdCount = 1;
1326 DisplayOrder = &DefaultObject;
1327 }
1328 else
1329 {
1330 /* No default list either, return success but no entries */
1331 *BootSequence = NULL;
1332 *SequenceCount = 0;
1333 Status = STATUS_SUCCESS;
1334 DisplayOrder = NULL;
1335 goto Quickie;
1336 }
1337 }
1338
1339 /* Check if boot.ini was used */
1340 BootIniCount = 0;
1341 if (BmBootIniUsed)
1342 {
1343 /* Get the entries from it */
1344 EfiPrintf(L"Boot.ini not supported\r\n");
1345 BootIniCount = 0;//BmBootIniGetEntryCount();
1346 }
1347
1348 /* Allocate an array large enough for the combined boot entries */
1349 BootEntryCount = BootIniCount + BcdCount;
1350 Sequence = BlMmAllocateHeap(BootEntryCount * sizeof(*Sequence));
1351 if (!Sequence)
1352 {
1353 Status = STATUS_NO_MEMORY;
1354 goto Quickie;
1355 }
1356
1357 /* Zero it out */
1358 RtlZeroMemory(Sequence, BootEntryCount * sizeof(*Sequence));
1359
1360 /* Check if we had BCD entries */
1361 if (BcdCount)
1362 {
1363 /* Populate the list of bootable entries */
1364 Status = BmpPopulateBootEntryList(BcdHandle,
1365 DisplayOrder,
1366 0x800000,
1367 Sequence,
1368 &BcdCount);
1369 if (!NT_SUCCESS(Status))
1370 {
1371 /* Bail out */
1372 goto Quickie;
1373 }
1374 }
1375
1376 /* Check if we had boot.ini entries */
1377 if (BootIniCount)
1378 {
1379 /* TODO */
1380 EfiPrintf(L"Boot.ini not supported\r\n");
1381 }
1382
1383 /* Return success and the sequence + count populated */
1384 Status = STATUS_SUCCESS;
1385 *BootSequence = Sequence;
1386 *SequenceCount = BootIniCount + BcdCount;
1387
1388 Quickie:
1389 /* Check if we had allocated a GUID list */
1390 if ((UseDisplayList) && (DisplayOrder))
1391 {
1392 /* Free it */
1393 BlMmFreeHeap(DisplayOrder);
1394 }
1395
1396 /* Check if this is the failure path */
1397 if (!(NT_SUCCESS(Status)) && (Sequence))
1398 {
1399 /* Loop the remaining boot entries */
1400 while (BootIndex < BootEntryCount)
1401 {
1402 /* Check if it had been allocated */
1403 if (Sequence[BootIndex])
1404 {
1405 /* Free it */
1406 BlMmFreeHeap(Sequence[BootIndex]);
1407 }
1408
1409 /* Next*/
1410 BootIndex++;
1411 }
1412
1413 /* Free the whole sequence now */
1414 BlMmFreeHeap(Sequence);
1415 }
1416
1417 /* All done, return the result */
1418 return Status;
1419 }
1420
1421 NTSTATUS
1422 BmpGetSelectedBootEntry (
1423 _In_ HANDLE BcdHandle,
1424 _Out_ PBL_LOADED_APPLICATION_ENTRY *SelectedBootEntry,
1425 _Out_ PULONG EntryIndex,
1426 _Out_ PBOOLEAN ExitBootManager
1427 )
1428 {
1429 NTSTATUS Status;
1430 PBL_LOADED_APPLICATION_ENTRY* Sequence;
1431 PBL_LOADED_APPLICATION_ENTRY Entry, SelectedEntry;
1432 ULONG Count, BootIndex;
1433
1434 /* Initialize locals */
1435 BootIndex = 0;
1436 Count = 0;
1437 Sequence = NULL;
1438 SelectedEntry = NULL;
1439
1440 /* Enumerate all the boot entries */
1441 Status = BmEnumerateBootEntries(BcdHandle, &Sequence, &Count);
1442 if (!NT_SUCCESS(Status))
1443 {
1444 /* Bail out if we failed */
1445 goto Quickie;
1446 }
1447
1448 /* Check if there are no entries */
1449 if (!Count)
1450 {
1451 /* This is fatal -- kill the system */
1452 Status = STATUS_FILE_INVALID;
1453 BmFatalErrorEx(BL_FATAL_ERROR_BCD_ENTRIES, (ULONG_PTR)L"\\BCD", Status, 0, 0);
1454 goto Quickie;
1455 }
1456
1457 EfiPrintf(L"Boot selection not yet implemented\r\n");
1458 *SelectedBootEntry = NULL;
1459
1460 Quickie:
1461 /* We are done -- did we have a sequence? */
1462 if (Sequence)
1463 {
1464 /* Do we have any boot entries we parsed? */
1465 while (BootIndex < Count)
1466 {
1467 /* Get the current boot entry */
1468 Entry = Sequence[BootIndex];
1469
1470 /* Did we fail, or is is not the selected one? */
1471 if ((Entry) && ((Entry != SelectedEntry) || !(NT_SUCCESS(Status))))
1472 {
1473 /* Destroy it, as it won't be needed */
1474 BlDestroyBootEntry(Entry);
1475 }
1476 else if (Entry == SelectedEntry)
1477 {
1478 /* It's the selected one, return its index */
1479 *EntryIndex = BootIndex;
1480 }
1481
1482 /* Move to the next entry */
1483 BootIndex++;
1484 }
1485
1486 /* Free the sequence of entries */
1487 BlMmFreeHeap(Sequence);
1488 }
1489
1490 /* Return the selection result */
1491 return Status;
1492 }
1493
1494 NTSTATUS
1495 BmpLaunchBootEntry (
1496 _In_ PBL_LOADED_APPLICATION_ENTRY BootEntry,
1497 _Out_ PULONG EntryIndex,
1498 _In_ ULONG Unknown,
1499 _In_ BOOLEAN LaunchWinRe
1500 )
1501 {
1502 EfiPrintf(L"Boot launch not yet implemented\r\n");
1503 return STATUS_NOT_IMPLEMENTED;
1504 }
1505
1506 /*++
1507 * @name BmMain
1508 *
1509 * The BmMain function implements the Windows Boot Application entrypoint for
1510 * the Boot Manager.
1511 *
1512 * @param BootParameters
1513 * Pointer to the Boot Application Parameter Block.
1514 *
1515 * @return NT_SUCCESS if the image was loaded correctly, relevant error code
1516 * otherwise.
1517 *
1518 *--*/
1519 NTSTATUS
1520 BmMain (
1521 _In_ PBOOT_APPLICATION_PARAMETER_BLOCK BootParameters
1522 )
1523 {
1524 NTSTATUS Status, LibraryStatus;
1525 BL_LIBRARY_PARAMETERS LibraryParameters;
1526 PBL_RETURN_ARGUMENTS ReturnArguments;
1527 PGUID AppIdentifier;
1528 HANDLE BcdHandle, ResumeBcdHandle;
1529 PBL_BCD_OPTION EarlyOptions;
1530 PWCHAR Stylesheet;
1531 BOOLEAN XmlLoaded, DisableIntegrity, TestSigning, PersistBootSequence;
1532 BOOLEAN RebootOnError, CustomActions;
1533 ULONG SequenceId;
1534 PBL_LOADED_APPLICATION_ENTRY BootEntry;
1535 PGUID SequenceList;
1536 ULONG SequenceListCount;
1537 PBL_LOADED_APPLICATION_ENTRY* BootSequence;
1538 ULONG BootIndex;
1539 BOOLEAN ExitBootManager;
1540 BOOLEAN BootFailed;
1541 BOOLEAN BootOk;
1542 ULONG SequenceCount;
1543 BOOLEAN GetEntry;
1544 EfiPrintf(L"ReactOS UEFI Boot Manager Initializing...\r\n");
1545
1546 /* Reading the BCD can change this later on */
1547 RebootOnError = FALSE;
1548
1549 /* Save the start/end-of-POST time */
1550 ApplicationStartTime = __rdtsc();
1551 PostTime = ApplicationStartTime;
1552
1553 /* Setup the boot library parameters for this application */
1554 BlSetupDefaultParameters(&LibraryParameters);
1555 LibraryParameters.TranslationType = BlNone;
1556 LibraryParameters.LibraryFlags = 0x400 | 0x8;
1557 LibraryParameters.MinimumAllocationCount = 16;
1558 LibraryParameters.MinimumHeapSize = 512 * 1024;
1559
1560 /* Initialize the boot library */
1561 Status = BlInitializeLibrary(BootParameters, &LibraryParameters);
1562 if (!NT_SUCCESS(Status))
1563 {
1564 /* Check for failure due to invalid application entry */
1565 if (Status != STATUS_INVALID_PARAMETER_9)
1566 {
1567 /* Specifically print out what happened */
1568 EfiPrintf(L"BlInitializeLibrary failed 0x%x\r\n", Status);
1569 }
1570
1571 /* Go to exit path */
1572 goto Quickie;
1573 }
1574
1575 /* Get the application identifier */
1576 AppIdentifier = BlGetApplicationIdentifier();
1577 if (!AppIdentifier)
1578 {
1579 /* None was given, so set our default one */
1580 AppIdentifier = (PGUID)&GUID_WINDOWS_BOOTMGR;
1581 }
1582
1583 /* Save our identifier */
1584 BmApplicationIdentifier = *AppIdentifier;
1585
1586 /* Initialize the file system to open a handle to our root boot directory */
1587 BmFwInitializeBootDirectoryPath();
1588
1589 /* Load and initialize the boot configuration database (BCD) */
1590 Status = BmOpenDataStore(&BcdHandle);
1591 if (NT_SUCCESS(Status))
1592 {
1593 /* Copy the boot options */
1594 Status = BlCopyBootOptions(BlpApplicationEntry.BcdData, &EarlyOptions);
1595 if (NT_SUCCESS(Status))
1596 {
1597 /* Update them */
1598 Status = BmpUpdateApplicationOptions(BcdHandle);
1599 if (!NT_SUCCESS(Status))
1600 {
1601 /* Log a fatal error */
1602 BmFatalErrorEx(BL_FATAL_ERROR_BCD_PARSE,
1603 (ULONG_PTR)L"\\BCD",
1604 Status,
1605 0,
1606 0);
1607 }
1608 }
1609 }
1610
1611 #ifdef _SECURE_BOOT
1612 /* Initialize the secure boot machine policy */
1613 Status = BmSecureBootInitializeMachinePolicy();
1614 if (!NT_SUCCESS(Status))
1615 {
1616 BmFatalErrorEx(BL_FATAL_ERROR_SECURE_BOOT, Status, 0, 0, 0);
1617 }
1618 #endif
1619
1620 /* Copy the library parameters and add the re-initialization flag */
1621 RtlCopyMemory(&LibraryParameters,
1622 &BlpLibraryParameters,
1623 sizeof(LibraryParameters));
1624 LibraryParameters.LibraryFlags |= (BL_LIBRARY_FLAG_REINITIALIZE_ALL |
1625 BL_LIBRARY_FLAG_REINITIALIZE);
1626
1627 /* Now that we've parsed the BCD, re-initialize the library */
1628 LibraryStatus = BlInitializeLibrary(BootParameters, &LibraryParameters);
1629 if (!NT_SUCCESS(LibraryStatus) && (NT_SUCCESS(Status)))
1630 {
1631 Status = LibraryStatus;
1632 }
1633
1634 /* Initialize firmware-specific memory regions */
1635 BmFwMemoryInitialize();
1636
1637 /* Initialize the boot status data log (BSD) */
1638 BmpInitializeBootStatusDataLog();
1639
1640 /* Find our XSL stylesheet */
1641 Stylesheet = BlResourceFindHtml();
1642 if (!Stylesheet)
1643 {
1644 /* Awe, no XML. This is actually fatal lol. Can't boot without XML. */
1645 Status = STATUS_NOT_FOUND;
1646 EfiPrintf(L"BlResourceFindMessage failed 0x%x\r\n", STATUS_NOT_FOUND);
1647 goto Quickie;
1648 }
1649
1650 /* Initialize the XML Engine (as a side-effect, resets cursor) */
1651 Status = BlXmiInitialize(Stylesheet);
1652 if (!NT_SUCCESS(Status))
1653 {
1654 EfiPrintf(L"\r\nBlXmiInitialize failed 0x%x\r\n", Status);
1655 goto Failure;
1656 }
1657 XmlLoaded = TRUE;
1658
1659 /* Check if there's an active bitmap visible */
1660 if (!BlDisplayValidOemBitmap())
1661 {
1662 /* Nope, make the screen black using BGFX */
1663 if (!NT_SUCCESS(BmpBgDisplayClearScreen(0xFF000000)))
1664 {
1665 /* BGFX isn't active, use standard display */
1666 BlDisplayClearScreen();
1667 }
1668 }
1669
1670 #ifdef _BIT_LOCKER_
1671 /* Bitlocker will take over screen UI if enabled */
1672 FveDisplayScreen = BmFveDisplayScreen;
1673 #endif
1674
1675 /* Check if any bypass options are enabled */
1676 BlImgQueryCodeIntegrityBootOptions(&BlpApplicationEntry,
1677 &DisableIntegrity,
1678 &TestSigning);
1679 if (!DisableIntegrity)
1680 {
1681 /* Integrity checks are enabled, so validate our signature */
1682 Status = BmFwVerifySelfIntegrity();
1683 if (!NT_SUCCESS(Status))
1684 {
1685 /* Signature invalid, fail boot */
1686 goto Failure;
1687 }
1688 }
1689
1690 /* Write out the first XML tag */
1691 BlXmiWrite(L"<bootmgr/>");
1692
1693 /* Check for factory resset */
1694 BlSecureBootCheckForFactoryReset();
1695
1696 /* Load the revocation list */
1697 Status = BmFwRegisterRevocationList();
1698 if (!NT_SUCCESS(Status))
1699 {
1700 goto Failure;
1701 }
1702
1703 /* Register our custom progress routine */
1704 BlUtlRegisterProgressRoutine();
1705
1706 /* Display state is not currently cached */
1707 BmDisplayStateCached = FALSE;
1708
1709 /* Check if we need to resume from hibernate */
1710 Status = BmResumeFromHibernate(&ResumeBcdHandle);
1711 if (!NT_SUCCESS(Status))
1712 {
1713 goto Failure;
1714 }
1715
1716 #ifdef BL_NET_SUPPORT
1717 /* Register multicast printing routine */
1718 BlUtlRegisterMulticastRoutine();
1719 #endif
1720
1721 /* Check if restart on failure is enabled */
1722 BlGetBootOptionBoolean(BlpApplicationEntry.BcdData,
1723 BcdLibraryBoolean_RestartOnFailure,
1724 &RebootOnError);
1725
1726 /* Check if the boot sequence is persisted */
1727 Status = BlGetBootOptionBoolean(BlpApplicationEntry.BcdData,
1728 BcdBootMgrBoolean_PersistBootSequence,
1729 &PersistBootSequence);
1730 if (!NT_SUCCESS(Status))
1731 {
1732 /* It usually is */
1733 PersistBootSequence = TRUE;
1734 }
1735
1736 /* Check if there's custom actions to take */
1737 Status = BlGetBootOptionBoolean(BlpApplicationEntry.BcdData,
1738 BcdBootMgrBoolean_ProcessCustomActionsFirst,
1739 &CustomActions);
1740 if ((NT_SUCCESS(Status)) && (CustomActions))
1741 {
1742 /* We don't suppport this yet */
1743 EfiPrintf(L"Not implemented\r\n");
1744 Status = STATUS_NOT_IMPLEMENTED;
1745 goto Failure;
1746 }
1747
1748 /* At last, enter the boot selection stage */
1749 SequenceId = 0;
1750 GetEntry = FALSE;
1751 BootFailed = FALSE;
1752 SequenceList = NULL;
1753 BootSequence = NULL;
1754 SequenceCount = 0;
1755 while (1)
1756 {
1757 /* We don't have a boot entry nor a sequence ID */
1758 BootEntry = NULL;
1759 BootOk = FALSE;
1760
1761 /* Do we have a hardcoded boot sequence set? */
1762 if (!(BootSequence) && !(GetEntry))
1763 {
1764 /* Not yet, read the BCD to see if one is there */
1765 Status = BlGetBootOptionGuidList(BlpApplicationEntry.BcdData,
1766 BcdBootMgrObjectList_BootSequence,
1767 &SequenceList,
1768 &SequenceListCount);
1769 if (NT_SUCCESS(Status))
1770 {
1771 /* A GUID list for the boot sequence is set. Extract it */
1772 Status = BmGetBootSequence(BcdHandle,
1773 SequenceList,
1774 SequenceListCount,
1775 0x20000000,
1776 &BootSequence,
1777 &SequenceCount);
1778 if (NT_SUCCESS(Status))
1779 {
1780 /* Don't get stuck in a loop repeating this sequence */
1781 BlRemoveBootOption(BlpApplicationEntry.BcdData,
1782 BcdBootMgrObjectList_BootSequence);
1783
1784 /* But do check if we should persist it */
1785 if (PersistBootSequence)
1786 {
1787 /* Yes -- so go select an entry now */
1788 GetEntry = TRUE;
1789 }
1790 else
1791 {
1792 /* We shouldn't, so wipe it from the BCD too */
1793 Status = BmPurgeOption(BcdHandle,
1794 &BmApplicationIdentifier,
1795 BcdBootMgrObjectList_BootSequence);
1796 if (!NT_SUCCESS(Status))
1797 {
1798 /* Well that failed */
1799 goto LoopQuickie;
1800 }
1801 }
1802 }
1803 }
1804 else
1805 {
1806 /* No boot entry sequence for us */
1807 BootSequence = NULL;
1808 }
1809 }
1810
1811 /* Do we have a sequence active, and are we still processing it? */
1812 if ((BootSequence) && ((GetEntry) || (SequenceId < SequenceCount)))
1813 {
1814 /* Extract the next entry in the sequence */
1815 BootEntry = BootSequence[SequenceId];
1816 BootSequence[SequenceId] = NULL;
1817
1818 /* Move to the next entry for next time */
1819 SequenceId++;
1820
1821 /* Unless there won't be a a next time? */
1822 if (SequenceId == SequenceCount)
1823 {
1824 /* Clean up, it's the last entry */
1825 BlMmFreeHeap(BootSequence);
1826 BootSequence = NULL;
1827 }
1828 }
1829 else
1830 {
1831 /* Get the selected boot entry from the user */
1832 ExitBootManager = FALSE;
1833 Status = BmpGetSelectedBootEntry(BcdHandle,
1834 &BootEntry,
1835 &BootIndex,
1836 &ExitBootManager);
1837 if (!(NT_SUCCESS(Status)) || (ExitBootManager))
1838 {
1839 /* Selection failed, or user wants to exit */
1840 goto LoopQuickie;
1841 }
1842 }
1843
1844 /* Did we have a BCD open? */
1845 if (BcdHandle)
1846 {
1847 /* Close it, we'll be opening a new one */
1848 BmCloseDataStore(BcdHandle);
1849 BcdHandle = NULL;
1850 }
1851
1852 /* Launch the selected entry */
1853 Status = BmpLaunchBootEntry(BootEntry, &BootIndex, 0, TRUE);
1854 if (NT_SUCCESS(Status))
1855 {
1856 /* Boot worked, uncache display and process the bad memory list */
1857 BmDisplayStateCached = FALSE;
1858 BmpProcessBadMemory();
1859 }
1860 else
1861 {
1862 /* Boot failed -- was it user driven? */
1863 if (Status != STATUS_CANCELLED)
1864 {
1865 /* Nope, remember that booting failed */
1866 BootFailed = TRUE;
1867 goto LoopQuickie;
1868 }
1869
1870 /* Yes -- the display is still valid */
1871 BmDisplayStateCached = TRUE;
1872 }
1873
1874 /* Reopen the BCD */
1875 Status = BmOpenDataStore(&BcdHandle);
1876 if (!NT_SUCCESS(Status))
1877 {
1878 break;
1879 }
1880
1881 /* Put the BCD options back into our entry */
1882 BlReplaceBootOptions(&BlpApplicationEntry, EarlyOptions);
1883
1884 /* Update our options one more time */
1885 Status = BmpUpdateApplicationOptions(BcdHandle);
1886 if (NT_SUCCESS(Status))
1887 {
1888 /* Boot was 100% OK */
1889 BootOk = TRUE;
1890 }
1891
1892 LoopQuickie:
1893 /* Did we have a boot entry? */
1894 if (BootEntry)
1895 {
1896 /* We can destroy it now */
1897 BlDestroyBootEntry(BootEntry);
1898 }
1899
1900 /* Is this the success path? */
1901 if (NT_SUCCESS(Status))
1902 {
1903 /* Did we actually boot something? */
1904 if (!BootOk)
1905 {
1906 /* Bope, fail out */
1907 break;
1908 }
1909 }
1910
1911 /* This is the failure path... should we reboot? */
1912 if (RebootOnError)
1913 {
1914 break;
1915 }
1916 };
1917
1918 Failure:
1919 if (!BootFailed)
1920 {
1921 /* Check if we got here due to an internal error */
1922 if (BmpInternalBootError)
1923 {
1924 /* If XML is available, display the error */
1925 if (XmlLoaded)
1926 {
1927 //BmDisplayDumpError(0, 0);
1928 //BmErrorPurge();
1929 }
1930
1931 /* Don't do a fatal error -- return back to firmware */
1932 goto Quickie;
1933 }
1934 }
1935
1936 /* Log a general fatal error once we're here */
1937 BmFatalErrorEx(BL_FATAL_ERROR_GENERIC, Status, 0, 0, 0);
1938
1939 Quickie:
1940 /* Check if we should reboot */
1941 if ((RebootOnError) ||
1942 (BlpApplicationEntry.Flags & BL_APPLICATION_ENTRY_REBOOT_ON_ERROR))
1943 {
1944 /* Reboot the box */
1945 BlFwReboot();
1946 Status = STATUS_SUCCESS;
1947 }
1948 else
1949 {
1950 /* Return back to the caller with the error argument encoded */
1951 ReturnArguments = (PVOID)((ULONG_PTR)BootParameters + BootParameters->ReturnArgumentsOffset);
1952 ReturnArguments->Version = BL_RETURN_ARGUMENTS_VERSION;
1953 ReturnArguments->Status = Status;
1954
1955 /* Tear down the boot library */
1956 BlDestroyLibrary();
1957 }
1958
1959 /* Return back status */
1960 return Status;
1961 }
1962