Three tiny squirmy subtle bugs combined themselves with the bug that was just fixed...
[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\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\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 BlXmiInitialize (
1046 _In_ PWCHAR Stylesheet
1047 )
1048 {
1049 /* Reset the cursor type */
1050 BlDisplaySetCursorType(0);
1051
1052 /* Nope, not doing any XML stuff */
1053 return STATUS_SUCCESS;
1054 }
1055
1056 VOID
1057 BlImgQueryCodeIntegrityBootOptions (
1058 _In_ PBL_LOADED_APPLICATION_ENTRY ApplicationEntry,
1059 _Out_ PBOOLEAN IntegrityChecksDisabled,
1060 _Out_ PBOOLEAN TestSigningEnabled
1061 )
1062 {
1063
1064 NTSTATUS Status;
1065 BOOLEAN Value;
1066
1067 /* Check if /DISABLEINTEGRITYCHECKS is on */
1068 Status = BlGetBootOptionBoolean(ApplicationEntry->BcdData,
1069 BcdLibraryBoolean_DisableIntegrityChecks,
1070 &Value);
1071 *IntegrityChecksDisabled = NT_SUCCESS(Status) && (Value);
1072
1073 /* Check if /TESTSIGNING is on */
1074 Status = BlGetBootOptionBoolean(ApplicationEntry->BcdData,
1075 BcdLibraryBoolean_AllowPrereleaseSignatures,
1076 &Value);
1077 *TestSigningEnabled = NT_SUCCESS(Status) && (Value);
1078 }
1079
1080 NTSTATUS
1081 BmFwVerifySelfIntegrity (
1082 VOID
1083 )
1084 {
1085 EfiPrintf(L"Device Type %d Local Type %d\r\n", BlpBootDevice->DeviceType, BlpBootDevice->Local.Type);
1086 if ((BlpBootDevice->DeviceType == LocalDevice) &&
1087 (BlpBootDevice->Local.Type == CdRomDevice) &&
1088 (BlpApplicationFlags & BL_APPLICATION_FLAG_CONVERTED_FROM_EFI))
1089 {
1090 return STATUS_SUCCESS;
1091 }
1092
1093 return 0xC0000428;
1094 }
1095
1096 /*++
1097 * @name BmMain
1098 *
1099 * The BmMain function implements the Windows Boot Application entrypoint for
1100 * the Boot Manager.
1101 *
1102 * @param BootParameters
1103 * Pointer to the Boot Application Parameter Block.
1104 *
1105 * @return NT_SUCCESS if the image was loaded correctly, relevant error code
1106 * otherwise.
1107 *
1108 *--*/
1109 NTSTATUS
1110 BmMain (
1111 _In_ PBOOT_APPLICATION_PARAMETER_BLOCK BootParameters
1112 )
1113 {
1114 NTSTATUS Status, LibraryStatus;
1115 BL_LIBRARY_PARAMETERS LibraryParameters;
1116 PBL_RETURN_ARGUMENTS ReturnArguments;
1117 BOOLEAN RebootOnError;
1118 PGUID AppIdentifier;
1119 HANDLE BcdHandle;
1120 PBL_BCD_OPTION EarlyOptions;
1121 PWCHAR Stylesheet;
1122 BOOLEAN XmlLoaded, DisableIntegrity, TestSigning;
1123
1124 EfiPrintf(L"ReactOS UEFI Boot Manager Initializing...\n");
1125
1126 /* Reading the BCD can change this later on */
1127 RebootOnError = FALSE;
1128
1129 /* Save the start/end-of-POST time */
1130 ApplicationStartTime = __rdtsc();
1131 PostTime = ApplicationStartTime;
1132
1133 /* Setup the boot library parameters for this application */
1134 BlSetupDefaultParameters(&LibraryParameters);
1135 LibraryParameters.TranslationType = BlNone;
1136 LibraryParameters.LibraryFlags = 0x400 | 0x8;
1137 LibraryParameters.MinimumAllocationCount = 16;
1138 LibraryParameters.MinimumHeapSize = 512 * 1024;
1139
1140 /* Initialize the boot library */
1141 Status = BlInitializeLibrary(BootParameters, &LibraryParameters);
1142 if (!NT_SUCCESS(Status))
1143 {
1144 /* Check for failure due to invalid application entry */
1145 if (Status != STATUS_INVALID_PARAMETER_9)
1146 {
1147 /* Specifically print out what happened */
1148 EfiPrintf(L"BlInitializeLibrary failed 0x%x\r\n", Status);
1149 }
1150
1151 /* Go to exit path */
1152 goto Quickie;
1153 }
1154
1155 /* Get the application identifier */
1156 AppIdentifier = BlGetApplicationIdentifier();
1157 if (!AppIdentifier)
1158 {
1159 /* None was given, so set our default one */
1160 AppIdentifier = (PGUID)&GUID_WINDOWS_BOOTMGR;
1161 }
1162
1163 /* Save our identifier */
1164 BmApplicationIdentifier = *AppIdentifier;
1165
1166 /* Initialize the file system to open a handle to our root boot directory */
1167 BmFwInitializeBootDirectoryPath();
1168
1169 /* Load and initialize the boot configuration database (BCD) */
1170 Status = BmOpenDataStore(&BcdHandle);
1171 if (NT_SUCCESS(Status))
1172 {
1173 /* Copy the boot options */
1174 Status = BlCopyBootOptions(BlpApplicationEntry.BcdData, &EarlyOptions);
1175 if (NT_SUCCESS(Status))
1176 {
1177 /* Update them */
1178 Status = BmpUpdateApplicationOptions(BcdHandle);
1179 if (!NT_SUCCESS(Status))
1180 {
1181 /* Log a fatal error */
1182 BmFatalErrorEx(BL_FATAL_ERROR_BCD_PARSE,
1183 (ULONG_PTR)L"\\BCD",
1184 Status,
1185 0,
1186 0);
1187 }
1188 }
1189 }
1190
1191 #ifdef _SECURE_BOOT
1192 /* Initialize the secure boot machine policy */
1193 Status = BmSecureBootInitializeMachinePolicy();
1194 if (!NT_SUCCESS(Status))
1195 {
1196 BmFatalErrorEx(BL_FATAL_ERROR_SECURE_BOOT, Status, 0, 0, 0);
1197 }
1198 #endif
1199
1200 /* Copy the library parameters and add the re-initialization flag */
1201 RtlCopyMemory(&LibraryParameters,
1202 &BlpLibraryParameters,
1203 sizeof(LibraryParameters));
1204 LibraryParameters.LibraryFlags |= (BL_LIBRARY_FLAG_REINITIALIZE_ALL |
1205 BL_LIBRARY_FLAG_REINITIALIZE);
1206
1207 /* Now that we've parsed the BCD, re-initialize the library */
1208 LibraryStatus = BlInitializeLibrary(BootParameters, &LibraryParameters);
1209 if (!NT_SUCCESS(LibraryStatus) && (NT_SUCCESS(Status)))
1210 {
1211 Status = LibraryStatus;
1212 }
1213
1214 /* Initialize firmware-specific memory regions */
1215 BmFwMemoryInitialize();
1216
1217 /* Initialize the boot status data log (BSD) */
1218 BmpInitializeBootStatusDataLog();
1219
1220 /* Find our XSL stylesheet */
1221 Stylesheet = BlResourceFindHtml();
1222 if (!Stylesheet)
1223 {
1224 /* Awe, no XML. This is actually fatal lol. Can't boot without XML. */
1225 Status = STATUS_NOT_FOUND;
1226 EfiPrintf(L"BlResourceFindMessage failed 0x%x\r\n", STATUS_NOT_FOUND);
1227 goto Quickie;
1228 }
1229
1230 /* Initialize the XML Engine (as a side-effect, resets cursor) */
1231 Status = BlXmiInitialize(Stylesheet);
1232 if (!NT_SUCCESS(Status))
1233 {
1234 EfiPrintf(L"\r\nBlXmiInitialize failed 0x%x\r\n", Status);
1235 goto Failure;
1236 }
1237 XmlLoaded = TRUE;
1238
1239 /* Check if there's an active bitmap visible */
1240 if (!BlDisplayValidOemBitmap())
1241 {
1242 /* Nope, make the screen black using BGFX */
1243 if (!NT_SUCCESS(BmpBgDisplayClearScreen(0xFF000000)))
1244 {
1245 /* BGFX isn't active, use standard display */
1246 BlDisplayClearScreen();
1247 }
1248 }
1249
1250 #ifdef _BIT_LOCKER_
1251 /* Bitlocker will take over screen UI if enabled */
1252 FveDisplayScreen = BmFveDisplayScreen;
1253 #endif
1254
1255 /* Check if any bypass options are enabled */
1256 BlImgQueryCodeIntegrityBootOptions(&BlpApplicationEntry,
1257 &DisableIntegrity,
1258 &TestSigning);
1259 if (!DisableIntegrity)
1260 {
1261 /* Integrity checks are enabled, so validate our signature */
1262 Status = BmFwVerifySelfIntegrity();
1263 if (!NT_SUCCESS(Status))
1264 {
1265 /* Signature invalid, fail boot */
1266 goto Failure;
1267 }
1268 }
1269
1270 // BlXmiWrite(L"<bootmgr/>");
1271
1272 //BlSecureBootCheckForFactoryReset();
1273
1274
1275 /* do more stuff!! */
1276 EfiPrintf(BlResourceFindMessage(BM_MSG_TEST));
1277 EfiPrintf(Stylesheet);
1278 EfiStall(10000000);
1279
1280 Failure:
1281 /* Check if we got here due to an internal error */
1282 if (BmpInternalBootError)
1283 {
1284 /* If XML is available, display the error */
1285 if (XmlLoaded)
1286 {
1287 //BmDisplayDumpError(0, 0);
1288 //BmErrorPurge();
1289 }
1290
1291 /* Don't do a fatal error -- return back to firmware */
1292 goto Quickie;
1293 }
1294
1295 /* Log a general fatal error once we're here */
1296 BmFatalErrorEx(BL_FATAL_ERROR_GENERIC, Status, 0, 0, 0);
1297
1298 Quickie:
1299 /* Check if we should reboot */
1300 if ((RebootOnError) ||
1301 (BlpApplicationEntry.Flags & BL_APPLICATION_ENTRY_REBOOT_ON_ERROR))
1302 {
1303 /* Reboot the box */
1304 BlFwReboot();
1305 Status = STATUS_SUCCESS;
1306 }
1307 else
1308 {
1309 /* Return back to the caller with the error argument encoded */
1310 ReturnArguments = (PVOID)((ULONG_PTR)BootParameters + BootParameters->ReturnArgumentsOffset);
1311 ReturnArguments->Version = BL_RETURN_ARGUMENTS_VERSION;
1312 ReturnArguments->Status = Status;
1313
1314 /* Tear down the boot library*/
1315 BlDestroyLibrary();
1316 }
1317
1318 /* Return back status */
1319 return Status;
1320 }
1321