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)
9 /* INCLUDES ******************************************************************/
13 /* DATA VARIABLES ************************************************************/
15 DEFINE_GUID(GUID_WINDOWS_BOOTMGR
,
19 0xAC, 0xC1, 0xF3, 0x2B, 0x34, 0x4D, 0x47, 0x95);
21 ULONGLONG ApplicationStartTime
;
23 GUID BmApplicationIdentifier
;
26 BL_BOOT_ERROR BmpErrorBuffer
;
27 PBL_BOOT_ERROR BmpInternalBootError
;
28 BL_PACKED_BOOT_ERROR BmpPackedBootError
;
30 BOOLEAN BmBootIniUsed
;
31 WCHAR BmpFileNameBuffer
[128];
32 PWCHAR ParentFileName
= L
"";
34 /* FUNCTIONS *****************************************************************/
37 BmpFwGetApplicationDirectoryPath (
38 _In_ PUNICODE_STRING ApplicationDirectoryPath
42 ULONG i
, AppPathLength
;
43 PWCHAR ApplicationPath
, PathCopy
;
45 /* Clear the incoming string */
46 ApplicationDirectoryPath
->Length
= 0;
47 ApplicationDirectoryPath
->MaximumLength
= 0;
48 ApplicationDirectoryPath
->Buffer
= 0;
50 /* Get the boot application path */
51 ApplicationPath
= NULL
;
52 Status
= BlGetBootOptionString(BlpApplicationEntry
.BcdData
,
53 BcdLibraryString_ApplicationPath
,
55 if (NT_SUCCESS(Status
))
57 /* Calculate the length of the application path */
58 for (i
= wcslen(ApplicationPath
) - 1; i
> 0; i
--)
60 /* Keep going until the path separator */
61 if (ApplicationPath
[i
] == OBJ_NAME_PATH_SEPARATOR
)
67 /* Check if we have space for one more character */
68 AppPathLength
= i
+ 1;
69 if (AppPathLength
< i
)
71 /* Nope, we'll overflow */
73 Status
= STATUS_INTEGER_OVERFLOW
;
78 Status
= STATUS_SUCCESS
;
82 if (NT_SUCCESS(Status
))
84 /* Check if it's safe to multiply by two */
85 if ((AppPathLength
* sizeof(WCHAR
)) > 0xFFFFFFFF)
89 Status
= STATUS_INTEGER_OVERFLOW
;
93 /* We're good, do the multiplication */
94 Status
= STATUS_SUCCESS
;
95 AppPathLength
*= sizeof(WCHAR
);
98 /* Allocate a copy for the string */
99 if (NT_SUCCESS(Status
))
101 PathCopy
= BlMmAllocateHeap(AppPathLength
);
104 /* NULL-terminate it */
105 RtlCopyMemory(PathCopy
,
107 AppPathLength
- sizeof(UNICODE_NULL
));
108 PathCopy
[AppPathLength
] = UNICODE_NULL
;
110 /* Finally, initialize the outoing string */
111 RtlInitUnicodeString(ApplicationDirectoryPath
, PathCopy
);
115 /* No memory, fail */
116 Status
= STATUS_NO_MEMORY
;
122 /* Check if we had an application path */
125 /* No longer need this, free it */
126 BlMmFreeHeap(ApplicationPath
);
134 BmFwInitializeBootDirectoryPath (
141 UNICODE_STRING BcdPath
;
143 ULONG FileHandle
, DeviceHandle
;
145 /* Initialize everything for failure */
146 BcdPath
.MaximumLength
= 0;
147 BcdPath
.Buffer
= NULL
;
153 /* Try to open the boot device */
154 Status
= BlpDeviceOpen(BlpBootDevice
, 1u, 0, &DeviceHandle
);
155 if (!NT_SUCCESS(Status
))
157 EfiPrintf(L
"Device open failed: %lx\r\n", Status
);
161 /* Get the directory path */
162 Status
= BmpFwGetApplicationDirectoryPath(&BcdPath
);
163 BcdDirectory
= BcdPath
.Buffer
;
164 if (!NT_SUCCESS(Status
))
166 EfiPrintf(L
"path failed: %lx\n", Status
);
170 /* Add the BCD file name to it */
171 FinalSize
= BcdPath
.MaximumLength
+ sizeof(L
"\\BCD") - sizeof(UNICODE_NULL
);
172 if (FinalSize
< BcdPath
.MaximumLength
)
177 /* Allocate space for the final path */
178 FinalPath
= BlMmAllocateHeap(FinalSize
);
185 RtlZeroMemory(FinalPath
, FinalSize
);
186 RtlCopyMemory(FinalPath
, BcdDirectory
, BcdPath
.MaximumLength
);
187 wcsncat(FinalPath
, L
"\\BCD", FinalSize
/ sizeof(WCHAR
));
189 /* Try to open the file */
190 EfiPrintf(L
"Opening: %s\r\n", FinalPath
);
191 Status
= BlFileOpen(DeviceHandle
, FinalPath
, 1, &FileHandle
);
192 if (!NT_SUCCESS(Status
))
194 BootDirectory
= BcdDirectory
;
198 /* Save the boot directory */
199 BootDirectory
= L
"\\EFI\\Microsoft\\Boot";
202 /* Free all the allocations we made */
205 Status
= BlMmFreeHeap(BcdDirectory
);
209 Status
= BlMmFreeHeap(FinalPath
);
212 /* Close the BCD file */
213 if (FileHandle
!= -1)
215 Status
= BlFileClose(FileHandle
);
218 /* Close the boot device */
219 if (DeviceHandle
!= -1)
221 Status
= BlDeviceClose(DeviceHandle
);
224 /* Return back to the caller */
233 /* Don't yet handled boot.ini */
234 return STATUS_NOT_FOUND
;
238 BmpFatalErrorMessageFilter (
239 _In_ NTSTATUS ErrorStatus
,
240 _Out_ PULONG ErrorResourceId
245 /* Assume no message for now, check for known status message */
249 /* Convert each status to a resource ID */
250 case STATUS_UNEXPECTED_IO_ERROR
:
251 *ErrorResourceId
= 9017;
254 case STATUS_IMAGE_CHECKSUM_MISMATCH
:
255 *ErrorResourceId
= 9018;
257 case STATUS_INVALID_IMAGE_WIN_64
:
258 *ErrorResourceId
= 9016;
261 *ErrorResourceId
= 9019;
265 *ErrorResourceId
= 9013;
269 /* Return the type of message */
278 /* Check if a boot error is present */
279 if (BmpPackedBootError
.BootError
)
282 BlMmFreeHeap(BmpPackedBootError
.BootError
);
283 BmpPackedBootError
.BootError
= NULL
;
286 /* Zero out the packed buffer */
287 BmpPackedBootError
.Size
= 0;
288 BmpInternalBootError
= NULL
;
289 RtlZeroMemory(&BmpErrorBuffer
, sizeof(BmpErrorBuffer
));
294 _In_ ULONG ErrorCode
,
295 _In_ NTSTATUS ErrorStatus
,
296 _In_ ULONG ErrorMsgId
,
297 _In_ PWCHAR FileName
,
301 PWCHAR ErrorMsgString
;
303 /* Check if we already had an error */
304 if (BmpInternalBootError
)
310 /* Find the string for this error ID */
311 ErrorMsgString
= BlResourceFindMessage(ErrorMsgId
);
314 /* Fill out the error buffer */
315 BmpErrorBuffer
.Unknown1
= 0;
316 BmpErrorBuffer
.Unknown2
= 0;
317 BmpErrorBuffer
.ErrorString
= ErrorMsgString
;
318 BmpErrorBuffer
.FileName
= FileName
;
319 BmpErrorBuffer
.ErrorCode
= ErrorCode
;
320 BmpErrorBuffer
.ErrorStatus
= ErrorStatus
;
321 BmpErrorBuffer
.HelpMsgId
= HelpMsgId
;
322 BmpInternalBootError
= &BmpErrorBuffer
;
328 _In_ ULONG ErrorCode
,
329 _In_ ULONG_PTR Parameter1
,
330 _In_ ULONG_PTR Parameter2
,
331 _In_ ULONG_PTR Parameter3
,
332 _In_ ULONG_PTR Parameter4
335 PWCHAR FileName
, Buffer
;
336 NTSTATUS ErrorStatus
;
337 WCHAR FormatString
[256];
338 ULONG ErrorResourceId
, ErrorHelpId
;
339 BOOLEAN Restart
, NoError
;
341 /* Assume no buffer for now */
344 /* Check what error code is being raised */
347 /* Error reading the BCD */
348 case BL_FATAL_ERROR_BCD_READ
:
350 /* Check if we have a name for the BCD file */
353 /* Check if the name fits into our buffer */
354 FileName
= (PWCHAR
)Parameter1
;
355 if (wcslen(FileName
) < sizeof(BmpFileNameBuffer
))
357 /* Copy it in there */
358 Buffer
= BmpFileNameBuffer
;
359 wcsncpy(BmpFileNameBuffer
,
361 RTL_NUMBER_OF(BmpFileNameBuffer
));
365 /* If we don't have a buffer, use an empty one */
368 Buffer
= ParentFileName
;
371 /* The NTSTATUS code is in parameter 2*/
372 ErrorStatus
= (NTSTATUS
)Parameter2
;
374 /* Build the error string */
375 swprintf(FormatString
,
376 L
"\nAn error occurred (%08x) while attempting"
377 L
"to read the boot configuration data file %s\n",
381 /* Select the resource ID message */
382 ErrorResourceId
= 9002;
387 /* The rest is not yet handled */
388 EfiPrintf(L
"Unexpected fatal error: %lx\n", ErrorCode
);
393 /* Check if the BCD option for restart is set */
394 BlGetBootOptionBoolean(BlpApplicationEntry
.BcdData
,
395 BcdLibraryBoolean_RestartOnFailure
,
399 /* Yes, so no error should be shown since we'll auto-restart */
404 /* Check if the option for not showing errors is set in the BCD */
405 BlGetBootOptionBoolean(BlpApplicationEntry
.BcdData
,
406 BcdBootMgrBoolean_NoErrorDisplay
,
410 /* Do we want an error? */
413 /* Yep, print it and then raise an error */
414 BlStatusPrint(FormatString
);
415 BlStatusError(1, ErrorCode
, Parameter1
, Parameter2
, Parameter3
);
418 /* Get the help message ID */
419 ErrorHelpId
= BmpFatalErrorMessageFilter(ErrorStatus
, &ErrorResourceId
);
420 BmpErrorLog(ErrorCode
, ErrorStatus
, ErrorResourceId
, Buffer
, ErrorHelpId
);
425 _In_ PWCHAR FileName
,
426 _Out_ PWCHAR
* FullPath
430 ULONG BootDirLength
, BootDirLengthWithNul
;
431 ULONG PathLength
, FullPathLength
;
433 /* Compute the length of the directory, and add a NUL */
434 BootDirLength
= wcslen(BootDirectory
);
435 BootDirLengthWithNul
= BootDirLength
+ 1;
436 if (BootDirLengthWithNul
< BootDirLength
)
438 /* This would overflow */
439 BootDirLengthWithNul
= -1;
440 Status
= STATUS_INTEGER_OVERFLOW
;
445 Status
= STATUS_SUCCESS
;
448 /* Fail on overflow */
449 if (!NT_SUCCESS(Status
))
454 /* Add the length of the file, make sure it fits */
455 PathLength
= wcslen(FileName
);
456 FullPathLength
= PathLength
+ BootDirLength
;
457 if (FullPathLength
< PathLength
)
461 Status
= STATUS_INTEGER_OVERFLOW
;
466 Status
= STATUS_SUCCESS
;
469 /* Fail on overflow */
470 if (!NT_SUCCESS(Status
))
475 /* Allocate the full path */
476 FullPathLength
= FullPathLength
* sizeof(WCHAR
);
477 *FullPath
= BlMmAllocateHeap(FullPathLength
);
480 /* Copy the directory followed by the file name */
481 wcsncpy(*FullPath
, BootDirectory
, FullPathLength
/ sizeof(WCHAR
));
482 wcsncat(*FullPath
, FileName
, FullPathLength
/ sizeof(WCHAR
));
486 /* Bail out since we have no memory */
487 Status
= STATUS_NO_MEMORY
;
491 /* Return to caller */
501 PBL_DEVICE_DESCRIPTOR BcdDevice
;
502 PWCHAR BcdPath
, FullPath
, PathBuffer
;
504 ULONG PathLength
, PathLengthWithNul
, FullSize
;
506 UNICODE_STRING BcdString
;
513 /* Check if a boot.ini file exists */
514 Status
= BmOpenBootIni();
515 if (NT_SUCCESS(Status
))
517 BmBootIniUsed
= TRUE
;
520 /* Check on which device the BCD is */
521 Status
= BlGetBootOptionDevice(BlpApplicationEntry
.BcdData
,
522 BcdBootMgrDevice_BcdDevice
,
525 if (!NT_SUCCESS(Status
))
527 /* It's not on a custom device, so it must be where we are */
528 Status
= BlGetBootOptionDevice(BlpApplicationEntry
.BcdData
,
529 BcdLibraryDevice_ApplicationDevice
,
532 if (!NT_SUCCESS(Status
))
534 /* This BCD option is required */
539 /* Next, check what file contains the BCD */
540 Status
= BlGetBootOptionString(BlpApplicationEntry
.BcdData
,
541 BcdBootMgrString_BcdFilePath
,
543 if (NT_SUCCESS(Status
))
545 /* We don't handle custom BCDs yet */
546 EfiPrintf(L
"Not handled\n");
547 Status
= STATUS_NOT_IMPLEMENTED
;
551 /* Now check if the BCD is on a remote share */
552 if (BcdDevice
->DeviceType
== UdpDevice
)
554 /* Nope. Nope. Nope */
555 EfiPrintf(L
"Not handled\n");
556 Status
= STATUS_NOT_IMPLEMENTED
;
560 /* Otherwise, compute the hardcoded path of the BCD */
561 Status
= BmpFwGetFullPath(L
"\\BCD", &FullPath
);
562 if (!NT_SUCCESS(Status
))
564 /* User the raw path */
565 PathBuffer
= BcdPath
;
569 /* Use the path we got */
570 PathBuffer
= FullPath
;
574 /* Check if we failed to get the BCD path */
575 if (!NT_SUCCESS(Status
))
580 /* Add a NUL to the path, make sure it'll fit */
581 Status
= STATUS_SUCCESS
;
582 PathLength
= wcslen(PathBuffer
);
583 PathLengthWithNul
= PathLength
+ 1;
584 if (PathLengthWithNul
< PathLength
)
586 PathLengthWithNul
= -1;
587 Status
= STATUS_INTEGER_OVERFLOW
;
590 /* Bail out if it doesn't fit */
591 if (!NT_SUCCESS(Status
))
596 /* Now add the size of the path to the device path, check if it fits */
597 PathLengthWithNul
= PathLengthWithNul
* sizeof(WCHAR
);
598 FullSize
= PathLengthWithNul
+ BcdDevice
->Size
;
599 if (FullSize
< BcdDevice
->Size
)
602 Status
= STATUS_INTEGER_OVERFLOW
;
606 Status
= STATUS_SUCCESS
;
609 /* Bail out if it doesn't fit */
610 if (!NT_SUCCESS(Status
))
615 /* Allocate a final structure to hold both entities */
616 FinalBuffer
= BlMmAllocateHeap(FullSize
);
619 Status
= STATUS_NO_MEMORY
;
623 /* Copy the device path and file path into the final buffer */
624 RtlCopyMemory(FinalBuffer
, BcdDevice
, BcdDevice
->Size
);
625 RtlCopyMemory((PVOID
)((ULONG_PTR
)FinalBuffer
+ BcdDevice
->Size
),
629 /* Now tell the BCD engine to open the store */
630 BcdString
.Length
= FullSize
;
631 BcdString
.MaximumLength
= FullSize
;
632 BcdString
.Buffer
= FinalBuffer
;
633 Status
= BcdOpenStoreFromFile(&BcdString
, Handle
);
635 /* Free our final buffer */
636 BlMmFreeHeap(FinalBuffer
);
639 /* Did we allocate a device? */
643 BlMmFreeHeap(BcdDevice
);
646 /* Is this the failure path? */
647 if (!NT_SUCCESS(Status
))
649 /* Raise a fatal error */
650 BmFatalErrorEx(1, (ULONG_PTR
)PathBuffer
, Status
, 0, 0);
653 /* Did we get an allocated path? */
654 if ((PathBuffer
) && (HavePath
))
657 BlMmFreeHeap(PathBuffer
);
660 /* Return back to the caller */
667 * The BmMain function implements the Windows Boot Application entrypoint for
670 * @param BootParameters
671 * Pointer to the Boot Application Parameter Block.
673 * @return NT_SUCCESS if the image was loaded correctly, relevant error code
679 _In_ PBOOT_APPLICATION_PARAMETER_BLOCK BootParameters
683 BL_LIBRARY_PARAMETERS LibraryParameters
;
684 PBL_RETURN_ARGUMENTS ReturnArguments
;
685 BOOLEAN RebootOnError
;
689 EfiPrintf(L
"ReactOS UEFI Boot Manager Initializing...\n");
691 /* Reading the BCD can change this later on */
692 RebootOnError
= FALSE
;
694 /* Save the start/end-of-POST time */
695 ApplicationStartTime
= __rdtsc();
696 PostTime
= ApplicationStartTime
;
698 /* Setup the boot library parameters for this application */
699 BlSetupDefaultParameters(&LibraryParameters
);
700 LibraryParameters
.TranslationType
= BlNone
;
701 LibraryParameters
.LibraryFlags
= 0x400 | 0x8;
702 LibraryParameters
.MinimumAllocationCount
= 16;
703 LibraryParameters
.MinimumHeapSize
= 512 * 1024;
705 /* Initialize the boot library */
706 Status
= BlInitializeLibrary(BootParameters
, &LibraryParameters
);
707 if (!NT_SUCCESS(Status
))
709 /* Check for failure due to invalid application entry */
710 if (Status
!= STATUS_INVALID_PARAMETER_9
)
712 /* Specifically print out what happened */
713 EfiPrintf(L
"BlInitializeLibrary failed 0x%x\r\n", Status
);
716 /* Go to exit path */
720 /* Get the application identifier */
721 AppIdentifier
= BlGetApplicationIdentifier();
724 /* None was given, so set our default one */
725 AppIdentifier
= (PGUID
)&GUID_WINDOWS_BOOTMGR
;
728 /* Save our identifier */
729 BmApplicationIdentifier
= *AppIdentifier
;
731 /* Initialize the file system to open a handle to our root boot directory */
732 BmFwInitializeBootDirectoryPath();
734 /* Load and initialize the boot configuration database (BCD) */
735 Status
= BmOpenDataStore(&BcdHandle
);
736 EfiPrintf(L
"BCD Open: %lx\r\n", Status
);
738 /* do more stuff!! */
739 EfiPrintf(L
"We are A-OK!\r\n");
743 /* Check if we should reboot */
744 if ((RebootOnError
) ||
745 (BlpApplicationEntry
.Flags
& BL_APPLICATION_ENTRY_REBOOT_ON_ERROR
))
749 Status
= STATUS_SUCCESS
;
753 /* Return back to the caller with the error argument encoded */
754 ReturnArguments
= (PVOID
)((ULONG_PTR
)BootParameters
+ BootParameters
->ReturnArgumentsOffset
);
755 ReturnArguments
->Version
= BL_RETURN_ARGUMENTS_VERSION
;
756 ReturnArguments
->Status
= Status
;
758 /* Tear down the boot library*/
762 /* Return back status */