01d8e62462e8013a5178804dc4b727e5849256d2
[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_PARSE:
554
555 /* File name isin 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"\nThe boot configuration file %s is invalid (%08x).\n",
564 FileName,
565 ErrorStatus);
566
567 /* Select the resource ID message */
568 ErrorResourceId = 9015;
569 break;
570
571 case BL_FATAL_ERROR_GENERIC:
572
573 /* The NTSTATUS code is in parameter 1*/
574 ErrorStatus = (NTSTATUS)Parameter1;
575
576 /* Build the error string */
577 swprintf(FormatString,
578 L"\nThe boot manager experienced an error (%08x).\n",
579 ErrorStatus);
580
581 /* Select the resource ID message */
582 ErrorResourceId = 9005;
583 break;
584
585 default:
586
587 /* The rest is not yet handled */
588 EfiPrintf(L"Unexpected fatal error: %lx\r\n", ErrorCode);
589 while (1);
590 break;
591 }
592
593 /* Check if the BCD option for restart is set */
594 BlGetBootOptionBoolean(BlpApplicationEntry.BcdData,
595 BcdLibraryBoolean_RestartOnFailure,
596 &Restart);
597 if (Restart)
598 {
599 /* Yes, so no error should be shown since we'll auto-restart */
600 NoError = TRUE;
601 }
602 else
603 {
604 /* Check if the option for not showing errors is set in the BCD */
605 BlGetBootOptionBoolean(BlpApplicationEntry.BcdData,
606 BcdBootMgrBoolean_NoErrorDisplay,
607 &NoError);
608 }
609
610 /* Do we want an error? */
611 if (!NoError)
612 {
613 /* Yep, print it and then raise an error */
614 BlStatusPrint(FormatString);
615 BlStatusError(1, ErrorCode, Parameter1, Parameter2, Parameter3);
616 }
617
618 /* Get the help message ID */
619 ErrorHelpId = BmpFatalErrorMessageFilter(ErrorStatus, &ErrorResourceId);
620 BmpErrorLog(ErrorCode, ErrorStatus, ErrorResourceId, Buffer, ErrorHelpId);
621 }
622
623 NTSTATUS
624 BmpFwGetFullPath (
625 _In_ PWCHAR FileName,
626 _Out_ PWCHAR* FullPath
627 )
628 {
629 NTSTATUS Status;
630 ULONG BootDirLength, PathLength;
631
632 /* Compute the length of the directory, and add a NUL */
633 BootDirLength = wcslen(BootDirectory);
634 Status = RtlULongAdd(BootDirLength, 1, &BootDirLength);
635 if (!NT_SUCCESS(Status))
636 {
637 goto Quickie;
638 }
639
640 /* Add the length of the file, make sure it fits */
641 PathLength = wcslen(FileName);
642 Status = RtlULongAdd(PathLength, BootDirLength, &PathLength);
643 if (!NT_SUCCESS(Status))
644 {
645 goto Quickie;
646 }
647
648 /* Convert to bytes */
649 Status = RtlULongLongToULong(PathLength * sizeof(WCHAR), &PathLength);
650 if (!NT_SUCCESS(Status))
651 {
652 goto Quickie;
653 }
654
655 /* Allocate the full path */
656 *FullPath = BlMmAllocateHeap(PathLength);
657 if (*FullPath)
658 {
659 /* Copy the directory followed by the file name */
660 wcsncpy(*FullPath, BootDirectory, PathLength / sizeof(WCHAR));
661 wcsncat(*FullPath, FileName, PathLength / sizeof(WCHAR));
662 }
663 else
664 {
665 /* Bail out since we have no memory */
666 Status = STATUS_NO_MEMORY;
667 }
668
669 Quickie:
670 /* Return to caller */
671 return Status;
672 }
673
674 VOID
675 BmCloseDataStore (
676 _In_ HANDLE Handle
677 )
678 {
679 /* Check if boot.ini data needs to be freed */
680 if (BmBootIniUsed)
681 {
682 EfiPrintf(L"Not handled\r\n");
683 }
684
685 /* Dereference the hive and close the key */
686 BiDereferenceHive(Handle);
687 BiCloseKey(Handle);
688 }
689
690 NTSTATUS
691 BmOpenDataStore (
692 _Out_ PHANDLE Handle
693 )
694 {
695 NTSTATUS Status;
696 PBL_DEVICE_DESCRIPTOR BcdDevice;
697 PWCHAR BcdPath, FullPath, PathBuffer;
698 BOOLEAN HavePath;
699 ULONG PathLength, FullSize;
700 PVOID FinalBuffer;
701 UNICODE_STRING BcdString;
702
703 /* Initialize variables */
704 PathBuffer = NULL;
705 BcdDevice = NULL;
706 BcdPath = NULL;
707 HavePath = FALSE;
708
709 /* Check if a boot.ini file exists */
710 Status = BmOpenBootIni();
711 if (NT_SUCCESS(Status))
712 {
713 BmBootIniUsed = TRUE;
714 }
715
716 /* Check on which device the BCD is */
717 Status = BlGetBootOptionDevice(BlpApplicationEntry.BcdData,
718 BcdBootMgrDevice_BcdDevice,
719 &BcdDevice,
720 NULL);
721 if (!NT_SUCCESS(Status))
722 {
723 /* It's not on a custom device, so it must be where we are */
724 Status = BlGetBootOptionDevice(BlpApplicationEntry.BcdData,
725 BcdLibraryDevice_ApplicationDevice,
726 &BcdDevice,
727 NULL);
728 if (!NT_SUCCESS(Status))
729 {
730 /* This BCD option is required */
731 goto Quickie;
732 }
733 }
734
735 /* Next, check what file contains the BCD */
736 Status = BlGetBootOptionString(BlpApplicationEntry.BcdData,
737 BcdBootMgrString_BcdFilePath,
738 &BcdPath);
739 if (NT_SUCCESS(Status))
740 {
741 /* We don't handle custom BCDs yet */
742 EfiPrintf(L"Not handled: %s\r\n", BcdPath);
743 Status = STATUS_NOT_IMPLEMENTED;
744 goto Quickie;
745 }
746
747 /* Now check if the BCD is on a remote share */
748 if (BcdDevice->DeviceType == UdpDevice)
749 {
750 /* Nope. Nope. Nope */
751 EfiPrintf(L"Not handled\r\n");
752 Status = STATUS_NOT_IMPLEMENTED;
753 goto Quickie;
754 }
755
756 /* Otherwise, compute the hardcoded path of the BCD */
757 Status = BmpFwGetFullPath(L"\\BCD", &FullPath);
758 if (!NT_SUCCESS(Status))
759 {
760 /* User the raw path */
761 PathBuffer = BcdPath;
762 }
763 else
764 {
765 /* Use the path we got */
766 PathBuffer = FullPath;
767 HavePath = TRUE;
768 }
769
770 /* Check if we failed to get the BCD path */
771 if (!NT_SUCCESS(Status))
772 {
773 goto Quickie;
774 }
775
776 /* Add a NUL to the path, make sure it'll fit */
777 PathLength = wcslen(PathBuffer);
778 Status = RtlULongAdd(PathLength, 1, &PathLength);
779 if (!NT_SUCCESS(Status))
780 {
781 goto Quickie;
782 }
783
784 /* Convert to bytes */
785 Status = RtlULongLongToULong(PathLength * sizeof(WCHAR), &PathLength);
786 if (!NT_SUCCESS(Status))
787 {
788 goto Quickie;
789 }
790
791 /* Now add the size of the path to the device path, check if it fits */
792 Status = RtlULongAdd(PathLength, BcdDevice->Size, &FullSize);
793 if (!NT_SUCCESS(Status))
794 {
795 goto Quickie;
796 }
797
798 /* Allocate a final structure to hold both entities */
799 FinalBuffer = BlMmAllocateHeap(FullSize);
800 if (!FinalBuffer)
801 {
802 Status = STATUS_NO_MEMORY;
803 goto Quickie;
804 }
805
806 /* Copy the device path and file path into the final buffer */
807 RtlCopyMemory(FinalBuffer, BcdDevice, BcdDevice->Size);
808 RtlCopyMemory((PVOID)((ULONG_PTR)FinalBuffer + BcdDevice->Size),
809 PathBuffer,
810 PathLength);
811
812 /* Now tell the BCD engine to open the store */
813 BcdString.Length = FullSize;
814 BcdString.MaximumLength = FullSize;
815 BcdString.Buffer = FinalBuffer;
816 Status = BcdOpenStoreFromFile(&BcdString, Handle);
817
818 /* Free our final buffer */
819 BlMmFreeHeap(FinalBuffer);
820
821 Quickie:
822 /* Did we allocate a device? */
823 if (BcdDevice)
824 {
825 /* Free it */
826 BlMmFreeHeap(BcdDevice);
827 }
828
829 /* Is this the failure path? */
830 if (!NT_SUCCESS(Status))
831 {
832 /* Raise a fatal error */
833 BmFatalErrorEx(BL_FATAL_ERROR_BCD_READ,
834 (ULONG_PTR)PathBuffer,
835 Status,
836 0,
837 0);
838 }
839
840 /* Did we get an allocated path? */
841 if ((PathBuffer) && (HavePath))
842 {
843 /* Free it */
844 BlMmFreeHeap(PathBuffer);
845 }
846
847 /* Return back to the caller */
848 return Status;
849 }
850
851 typedef struct _BL_BSD_LOG_OBJECT
852 {
853 ULONG DeviceId;
854 ULONG FileId;
855 ULONG Unknown;
856 ULONG Size;
857 ULONG Flags;
858 } BL_BSD_LOG_OBJECT, *PBL_BSD_LOG_OBJECT;
859
860 BL_BSD_LOG_OBJECT BsdpLogObject;
861 BOOLEAN BsdpLogObjectInitialized;
862
863 VOID
864 BlBsdInitializeLog (
865 _In_ PBL_DEVICE_DESCRIPTOR LogDevice,
866 _In_ PWCHAR LogPath,
867 _In_ ULONG Flags
868 )
869 {
870 NTSTATUS Status;
871
872 /* Don't initialize twice */
873 if (BsdpLogObjectInitialized)
874 {
875 return;
876 }
877
878 /* Set invalid IDs for now */
879 BsdpLogObject.DeviceId = -1;
880 BsdpLogObject.FileId = -1;
881
882 /* Open the BSD device */
883 Status = BlpDeviceOpen(LogDevice,
884 BL_DEVICE_READ_ACCESS | BL_DEVICE_WRITE_ACCESS,
885 0,
886 &BsdpLogObject.DeviceId);
887 if (!NT_SUCCESS(Status))
888 {
889 /* Welp that didn't work */
890 goto FailurePath;
891 }
892
893 /* Now open the BSD itself */
894 Status = BlFileOpen(BsdpLogObject.DeviceId,
895 LogPath,
896 BL_FILE_READ_ACCESS | BL_FILE_WRITE_ACCESS,
897 &BsdpLogObject.FileId);
898 if (!NT_SUCCESS(Status))
899 {
900 /* D'oh */
901 goto FailurePath;
902 }
903
904 /* The BSD is open. Start doing stuff to it */
905 EfiPrintf(L"Unimplemented BSD path\r\n");
906 Status = STATUS_NOT_IMPLEMENTED;
907
908 FailurePath:
909 /* Close the BSD if we had it open */
910 if (BsdpLogObject.FileId != -1)
911 {
912 BlFileClose(BsdpLogObject.FileId);
913 }
914
915 /* Close the device if we had it open */
916 if (BsdpLogObject.DeviceId != -1)
917 {
918 BlDeviceClose(BsdpLogObject.DeviceId);
919 }
920
921 /* Set BSD object to its uninitialized state */
922 BsdpLogObjectInitialized = FALSE;
923 BsdpLogObject.FileId = 0;
924 BsdpLogObject.DeviceId = 0;
925 BsdpLogObject.Flags = 0;
926 BsdpLogObject.Unknown = 0;
927 BsdpLogObject.Size = 0;
928 }
929
930 VOID
931 BmpInitializeBootStatusDataLog (
932 VOID
933 )
934 {
935 NTSTATUS Status;
936 PBL_DEVICE_DESCRIPTOR BsdDevice;
937 PWCHAR BsdPath;
938 ULONG Flags;
939 BOOLEAN PreserveBsd;
940
941 /* Initialize locals */
942 BsdPath = NULL;
943 BsdDevice = NULL;
944 Flags = 0;
945
946 /* Check if the BSD is stored in a custom device */
947 Status = BlGetBootOptionDevice(BlpApplicationEntry.BcdData,
948 BcdLibraryDevice_BsdLogDevice,
949 &BsdDevice,
950 NULL);
951 if (!NT_SUCCESS(Status))
952 {
953 /* Nope, use the boot device */
954 BsdDevice = BlpBootDevice;
955 }
956
957 /* Check if the path is custom as well */
958 Status = BlGetBootOptionString(BlpApplicationEntry.BcdData,
959 BcdLibraryString_BsdLogPath,
960 &BsdPath);
961 if (!NT_SUCCESS(Status))
962 {
963 /* Nope, use our default path */
964 Status = BmpFwGetFullPath(L"\\bootstat.dat", &BsdPath);
965 if (!NT_SUCCESS(Status))
966 {
967 BsdPath = NULL;
968 }
969
970 /* Set preserve flag */
971 Flags = 1;
972 }
973 else
974 {
975 /* Set preserve flag */
976 Flags = 1;
977 }
978
979 /* Finally, check if the BSD should be preserved */
980 Status = BlGetBootOptionBoolean(BlpApplicationEntry.BcdData,
981 BcdLibraryBoolean_PreserveBsdLog,
982 &PreserveBsd);
983 if (!(NT_SUCCESS(Status)) || !(PreserveBsd))
984 {
985 /* We failed to read, or we were asked not to preserve it */
986 Flags = 0;
987 }
988
989 /* Initialize the log */
990 BlBsdInitializeLog(BsdDevice, BsdPath, Flags);
991
992 /* Free the BSD device descriptor if we had one */
993 if (BsdDevice)
994 {
995 BlMmFreeHeap(BsdDevice);
996 }
997
998 /* Free the BSD path if we had one */
999 if ((Flags) && (BsdPath))
1000 {
1001 BlMmFreeHeap(BsdPath);
1002 }
1003 }
1004
1005 VOID
1006 BmFwMemoryInitialize (
1007 VOID
1008 )
1009 {
1010 NTSTATUS Status;
1011 PHYSICAL_ADDRESS PhysicalAddress;
1012 BL_ADDRESS_RANGE AddressRange;
1013
1014 /* Select the range below 1MB */
1015 AddressRange.Maximum = 0xFFFFF;
1016 AddressRange.Minimum = 0;
1017
1018 /* Allocate one reserved page with the "reserved" attribute */
1019 Status = MmPapAllocatePhysicalPagesInRange(&PhysicalAddress,
1020 BlApplicationReserved,
1021 1,
1022 BlMemoryReserved,
1023 0,
1024 &MmMdlUnmappedAllocated,
1025 &AddressRange,
1026 BL_MM_REQUEST_DEFAULT_TYPE);
1027 if (!NT_SUCCESS(Status))
1028 {
1029 /* Print a message on error, but keep going */
1030 BlStatusPrint(L"BmFwMemoryInitialize: Failed to allocate a page below 1MB. Status: 0x%08x\r\n",
1031 Status);
1032 }
1033 }
1034
1035 NTSTATUS
1036 BmpBgDisplayClearScreen (
1037 _In_ ULONG Color
1038 )
1039 {
1040 /* Not yet supported */
1041 return STATUS_NOT_IMPLEMENTED;
1042 }
1043
1044 NTSTATUS
1045 BlXmiWrite (
1046 _In_ PWCHAR XmlTag
1047 )
1048 {
1049 /* Sigh */
1050 EfiPrintf(L"XML: %s\r\n", XmlTag);
1051 return STATUS_NOT_IMPLEMENTED;
1052 }
1053
1054 NTSTATUS
1055 BlXmiInitialize (
1056 _In_ PWCHAR Stylesheet
1057 )
1058 {
1059 /* Reset the cursor type */
1060 BlDisplaySetCursorType(0);
1061
1062 /* Nope, not doing any XML stuff */
1063 return STATUS_SUCCESS;
1064 }
1065
1066 VOID
1067 BlImgQueryCodeIntegrityBootOptions (
1068 _In_ PBL_LOADED_APPLICATION_ENTRY ApplicationEntry,
1069 _Out_ PBOOLEAN IntegrityChecksDisabled,
1070 _Out_ PBOOLEAN TestSigningEnabled
1071 )
1072 {
1073
1074 NTSTATUS Status;
1075 BOOLEAN Value;
1076
1077 /* Check if /DISABLEINTEGRITYCHECKS is on */
1078 Status = BlGetBootOptionBoolean(ApplicationEntry->BcdData,
1079 BcdLibraryBoolean_DisableIntegrityChecks,
1080 &Value);
1081 *IntegrityChecksDisabled = NT_SUCCESS(Status) && (Value);
1082
1083 /* Check if /TESTSIGNING is on */
1084 Status = BlGetBootOptionBoolean(ApplicationEntry->BcdData,
1085 BcdLibraryBoolean_AllowPrereleaseSignatures,
1086 &Value);
1087 *TestSigningEnabled = NT_SUCCESS(Status) && (Value);
1088 }
1089
1090 NTSTATUS
1091 BmFwVerifySelfIntegrity (
1092 VOID
1093 )
1094 {
1095 /* Check if we're booted by UEFI off the DVD directlry */
1096 if ((BlpBootDevice->DeviceType == LocalDevice) &&
1097 (BlpBootDevice->Local.Type == CdRomDevice) &&
1098 (BlpApplicationFlags & BL_APPLICATION_FLAG_CONVERTED_FROM_EFI))
1099 {
1100 /* Windows actually bypasses integrity checks in this case. Works for us */
1101 return STATUS_SUCCESS;
1102 }
1103
1104 /* Our binaries aren't signed, so always return failure */
1105 return 0xC0000428;
1106 }
1107
1108 NTSTATUS
1109 BmFwRegisterRevocationList (
1110 VOID
1111 )
1112 {
1113 NTSTATUS Status;
1114 BOOLEAN SecureBootEnabled;
1115
1116 /* Is SecureBoot enabled? */
1117 Status = BlSecureBootIsEnabled(&SecureBootEnabled);
1118 if ((NT_SUCCESS(Status)) && (SecureBootEnabled))
1119 {
1120 EfiPrintf(L"SB not implemented revok\r\n");
1121 return STATUS_NOT_IMPLEMENTED;
1122 }
1123 else
1124 {
1125 /* Nothing to do without SecureBoot */
1126 Status = STATUS_SUCCESS;
1127 }
1128
1129 /* Return revocation result back to caller */
1130 return Status;
1131 }
1132
1133 NTSTATUS
1134 BmResumeFromHibernate (
1135 _Out_ PHANDLE BcdResumeHandle
1136 )
1137 {
1138 NTSTATUS Status;
1139 BOOLEAN AttemptResume;
1140
1141 /* Should we attempt to resume from hibernation? */
1142 Status = BlGetBootOptionBoolean(BlpApplicationEntry.BcdData,
1143 BcdBootMgrBoolean_AttemptResume,
1144 &AttemptResume);
1145 if (!NT_SUCCESS(Status))
1146 {
1147 /* Nope. Is automatic restart on crash enabled? */
1148 AttemptResume = FALSE;
1149 Status = BlGetBootOptionBoolean(BlpApplicationEntry.BcdData,
1150 BcdOSLoaderBoolean_DisableCrashAutoReboot,
1151 &AttemptResume);
1152 AttemptResume = (NT_SUCCESS(Status) && (AttemptResume));
1153 }
1154
1155 /* Don't do anything if there's no need to resume anything */
1156 if (!AttemptResume)
1157 {
1158 return STATUS_SUCCESS;
1159 }
1160
1161 /* Not yet implemented */
1162 EfiPrintf(L"Resume not supported\r\n");
1163 return STATUS_NOT_IMPLEMENTED;
1164 }
1165
1166 /*++
1167 * @name BmMain
1168 *
1169 * The BmMain function implements the Windows Boot Application entrypoint for
1170 * the Boot Manager.
1171 *
1172 * @param BootParameters
1173 * Pointer to the Boot Application Parameter Block.
1174 *
1175 * @return NT_SUCCESS if the image was loaded correctly, relevant error code
1176 * otherwise.
1177 *
1178 *--*/
1179 NTSTATUS
1180 BmMain (
1181 _In_ PBOOT_APPLICATION_PARAMETER_BLOCK BootParameters
1182 )
1183 {
1184 NTSTATUS Status, LibraryStatus;
1185 BL_LIBRARY_PARAMETERS LibraryParameters;
1186 PBL_RETURN_ARGUMENTS ReturnArguments;
1187 PGUID AppIdentifier;
1188 HANDLE BcdHandle, ResumeBcdHandle;
1189 PBL_BCD_OPTION EarlyOptions;
1190 PWCHAR Stylesheet;
1191 BOOLEAN XmlLoaded, DisableIntegrity, TestSigning, PersistBootSequence;
1192 BOOLEAN RebootOnError, CustomActions, ContinueLoop;
1193 ULONG SequenceId, SequenceCount;
1194 PGUID BootSequences;
1195 //PBL_LOADED_APPLICATION_ENTRY* BootEntries;
1196 //PBL_LOADED_APPLICATION_ENTRY BootEntry;
1197
1198 EfiPrintf(L"ReactOS UEFI Boot Manager Initializing...\r\n");
1199
1200 /* Reading the BCD can change this later on */
1201 RebootOnError = FALSE;
1202
1203 /* Save the start/end-of-POST time */
1204 ApplicationStartTime = __rdtsc();
1205 PostTime = ApplicationStartTime;
1206
1207 /* Setup the boot library parameters for this application */
1208 BlSetupDefaultParameters(&LibraryParameters);
1209 LibraryParameters.TranslationType = BlNone;
1210 LibraryParameters.LibraryFlags = 0x400 | 0x8;
1211 LibraryParameters.MinimumAllocationCount = 16;
1212 LibraryParameters.MinimumHeapSize = 512 * 1024;
1213
1214 /* Initialize the boot library */
1215 Status = BlInitializeLibrary(BootParameters, &LibraryParameters);
1216 if (!NT_SUCCESS(Status))
1217 {
1218 /* Check for failure due to invalid application entry */
1219 if (Status != STATUS_INVALID_PARAMETER_9)
1220 {
1221 /* Specifically print out what happened */
1222 EfiPrintf(L"BlInitializeLibrary failed 0x%x\r\n", Status);
1223 }
1224
1225 /* Go to exit path */
1226 goto Quickie;
1227 }
1228
1229 /* Get the application identifier */
1230 AppIdentifier = BlGetApplicationIdentifier();
1231 if (!AppIdentifier)
1232 {
1233 /* None was given, so set our default one */
1234 AppIdentifier = (PGUID)&GUID_WINDOWS_BOOTMGR;
1235 }
1236
1237 /* Save our identifier */
1238 BmApplicationIdentifier = *AppIdentifier;
1239
1240 /* Initialize the file system to open a handle to our root boot directory */
1241 BmFwInitializeBootDirectoryPath();
1242
1243 /* Load and initialize the boot configuration database (BCD) */
1244 Status = BmOpenDataStore(&BcdHandle);
1245 if (NT_SUCCESS(Status))
1246 {
1247 /* Copy the boot options */
1248 Status = BlCopyBootOptions(BlpApplicationEntry.BcdData, &EarlyOptions);
1249 if (NT_SUCCESS(Status))
1250 {
1251 /* Update them */
1252 Status = BmpUpdateApplicationOptions(BcdHandle);
1253 if (!NT_SUCCESS(Status))
1254 {
1255 /* Log a fatal error */
1256 BmFatalErrorEx(BL_FATAL_ERROR_BCD_PARSE,
1257 (ULONG_PTR)L"\\BCD",
1258 Status,
1259 0,
1260 0);
1261 }
1262 }
1263 }
1264
1265 #ifdef _SECURE_BOOT
1266 /* Initialize the secure boot machine policy */
1267 Status = BmSecureBootInitializeMachinePolicy();
1268 if (!NT_SUCCESS(Status))
1269 {
1270 BmFatalErrorEx(BL_FATAL_ERROR_SECURE_BOOT, Status, 0, 0, 0);
1271 }
1272 #endif
1273
1274 /* Copy the library parameters and add the re-initialization flag */
1275 RtlCopyMemory(&LibraryParameters,
1276 &BlpLibraryParameters,
1277 sizeof(LibraryParameters));
1278 LibraryParameters.LibraryFlags |= (BL_LIBRARY_FLAG_REINITIALIZE_ALL |
1279 BL_LIBRARY_FLAG_REINITIALIZE);
1280
1281 /* Now that we've parsed the BCD, re-initialize the library */
1282 LibraryStatus = BlInitializeLibrary(BootParameters, &LibraryParameters);
1283 if (!NT_SUCCESS(LibraryStatus) && (NT_SUCCESS(Status)))
1284 {
1285 Status = LibraryStatus;
1286 }
1287
1288 /* Initialize firmware-specific memory regions */
1289 BmFwMemoryInitialize();
1290
1291 /* Initialize the boot status data log (BSD) */
1292 BmpInitializeBootStatusDataLog();
1293
1294 /* Find our XSL stylesheet */
1295 Stylesheet = BlResourceFindHtml();
1296 if (!Stylesheet)
1297 {
1298 /* Awe, no XML. This is actually fatal lol. Can't boot without XML. */
1299 Status = STATUS_NOT_FOUND;
1300 EfiPrintf(L"BlResourceFindMessage failed 0x%x\r\n", STATUS_NOT_FOUND);
1301 goto Quickie;
1302 }
1303
1304 /* Initialize the XML Engine (as a side-effect, resets cursor) */
1305 Status = BlXmiInitialize(Stylesheet);
1306 if (!NT_SUCCESS(Status))
1307 {
1308 EfiPrintf(L"\r\nBlXmiInitialize failed 0x%x\r\n", Status);
1309 goto Failure;
1310 }
1311 XmlLoaded = TRUE;
1312
1313 /* Check if there's an active bitmap visible */
1314 if (!BlDisplayValidOemBitmap())
1315 {
1316 /* Nope, make the screen black using BGFX */
1317 if (!NT_SUCCESS(BmpBgDisplayClearScreen(0xFF000000)))
1318 {
1319 /* BGFX isn't active, use standard display */
1320 BlDisplayClearScreen();
1321 }
1322 }
1323
1324 #ifdef _BIT_LOCKER_
1325 /* Bitlocker will take over screen UI if enabled */
1326 FveDisplayScreen = BmFveDisplayScreen;
1327 #endif
1328
1329 /* Check if any bypass options are enabled */
1330 BlImgQueryCodeIntegrityBootOptions(&BlpApplicationEntry,
1331 &DisableIntegrity,
1332 &TestSigning);
1333 if (!DisableIntegrity)
1334 {
1335 /* Integrity checks are enabled, so validate our signature */
1336 Status = BmFwVerifySelfIntegrity();
1337 if (!NT_SUCCESS(Status))
1338 {
1339 /* Signature invalid, fail boot */
1340 goto Failure;
1341 }
1342 }
1343
1344 /* Write out the first XML tag */
1345 BlXmiWrite(L"<bootmgr/>");
1346
1347 /* Check for factory resset */
1348 BlSecureBootCheckForFactoryReset();
1349
1350 /* Load the revocation list */
1351 Status = BmFwRegisterRevocationList();
1352 if (!NT_SUCCESS(Status))
1353 {
1354 goto Failure;
1355 }
1356
1357 /* Register our custom progress routine */
1358 BlUtlRegisterProgressRoutine();
1359
1360 /* Display state is not currently cached */
1361 BmDisplayStateCached = FALSE;
1362
1363 /* Check if we need to resume from hibernate */
1364 Status = BmResumeFromHibernate(&ResumeBcdHandle);
1365 if (!NT_SUCCESS(Status))
1366 {
1367 goto Failure;
1368 }
1369
1370 #ifdef BL_NET_SUPPORT
1371 /* Register multicast printing routine */
1372 BlUtlRegisterMulticastRoutine();
1373 #endif
1374
1375 /* Check if restart on failure is enabled */
1376 BlGetBootOptionBoolean(BlpApplicationEntry.BcdData,
1377 BcdLibraryBoolean_RestartOnFailure,
1378 &RebootOnError);
1379
1380 /* Check if the boot sequence is persisted */
1381 Status = BlGetBootOptionBoolean(BlpApplicationEntry.BcdData,
1382 BcdBootMgrBoolean_PersistBootSequence,
1383 &PersistBootSequence);
1384 if (!NT_SUCCESS(Status))
1385 {
1386 /* It usually is */
1387 PersistBootSequence = TRUE;
1388 }
1389
1390 /* Check if there's custom actions to take */
1391 Status = BlGetBootOptionBoolean(BlpApplicationEntry.BcdData,
1392 BcdBootMgrBoolean_ProcessCustomActionsFirst,
1393 &CustomActions);
1394 if ((NT_SUCCESS(Status)) && (CustomActions))
1395 {
1396 /* We don't suppport this yet */
1397 EfiPrintf(L"Not implemented\r\n");
1398 Status = STATUS_NOT_IMPLEMENTED;
1399 goto Failure;
1400 }
1401
1402 /* At last, enter the boot selection stage */
1403 SequenceId = 0;
1404 do
1405 {
1406 ContinueLoop = FALSE;
1407
1408 /* Get the list of boot sequences */
1409 SequenceCount = 0;
1410 Status = BlGetBootOptionGuidList(BlpApplicationEntry.BcdData,
1411 BcdBootMgrObjectList_BootSequence,
1412 &BootSequences,
1413 &SequenceCount);
1414 EfiPrintf(L"Count: %d\r\n", SequenceCount);
1415 EfiStall(1000000);
1416 if (!NT_SUCCESS(Status))
1417 {
1418 //goto GetEntry;
1419 }
1420
1421 if (NT_SUCCESS(Status))
1422 {
1423 continue;
1424 }
1425
1426 if (RebootOnError)
1427 {
1428 break;
1429 }
1430
1431 SequenceId++;
1432 } while (ContinueLoop);
1433
1434 Failure:
1435 /* Check if we got here due to an internal error */
1436 if (BmpInternalBootError)
1437 {
1438 /* If XML is available, display the error */
1439 if (XmlLoaded)
1440 {
1441 //BmDisplayDumpError(0, 0);
1442 //BmErrorPurge();
1443 }
1444
1445 /* Don't do a fatal error -- return back to firmware */
1446 goto Quickie;
1447 }
1448
1449 /* Log a general fatal error once we're here */
1450 BmFatalErrorEx(BL_FATAL_ERROR_GENERIC, Status, 0, 0, 0);
1451
1452 Quickie:
1453 /* Check if we should reboot */
1454 if ((RebootOnError) ||
1455 (BlpApplicationEntry.Flags & BL_APPLICATION_ENTRY_REBOOT_ON_ERROR))
1456 {
1457 /* Reboot the box */
1458 BlFwReboot();
1459 Status = STATUS_SUCCESS;
1460 }
1461 else
1462 {
1463 /* Return back to the caller with the error argument encoded */
1464 ReturnArguments = (PVOID)((ULONG_PTR)BootParameters + BootParameters->ReturnArgumentsOffset);
1465 ReturnArguments->Version = BL_RETURN_ARGUMENTS_VERSION;
1466 ReturnArguments->Status = Status;
1467
1468 /* Tear down the boot library */
1469 BlDestroyLibrary();
1470 }
1471
1472 /* Return back status */
1473 return Status;
1474 }
1475