reactos/boot/environ/app/bootmgr/bootmgr.c
2018-03-18 16:10:41 +01:00

3245 lines
93 KiB
C

/*
* COPYRIGHT: See COPYING.ARM in the top level directory
* PROJECT: ReactOS UEFI Boot Manager
* FILE: boot/environ/app/bootmgr/bootmgr.cla
* PURPOSE: Boot Manager Entrypoint
* PROGRAMMER: Alex Ionescu (alex.ionescu@reactos.org)
*/
/* INCLUDES ******************************************************************/
#include "bootmgr.h"
/* DATA VARIABLES ************************************************************/
DEFINE_GUID(GUID_WINDOWS_BOOTMGR,
0x9DEA862C,
0x5CDD,
0x4E70,
0xAC, 0xC1, 0xF3, 0x2B, 0x34, 0x4D, 0x47, 0x95);
ULONGLONG ApplicationStartTime;
ULONGLONG PostTime;
GUID BmApplicationIdentifier;
PWCHAR BootDirectory;
BL_BOOT_ERROR BmpErrorBuffer;
PBL_BOOT_ERROR BmpInternalBootError;
BL_PACKED_BOOT_ERROR BmpPackedBootError;
BOOLEAN BmBootIniUsed;
WCHAR BmpFileNameBuffer[128];
PWCHAR ParentFileName = L"";
BOOLEAN BmDisplayStateCached;
PBL_LOADED_APPLICATION_ENTRY* BmpFailedBootEntries;
PBL_LOADED_APPLICATION_ENTRY BmpSelectedBootEntry;
BOOLEAN BmBootEntryOverridePresent;
BOOLEAN BmpDisplayBootMenu;
/* FUNCTIONS *****************************************************************/
NTSTATUS
BmGetOptionList (
_In_ HANDLE BcdHandle,
_In_ PGUID ObjectId,
_In_ PBL_BCD_OPTION *OptionList
)
{
NTSTATUS Status;
HANDLE ObjectHandle;
ULONG ElementSize, ElementCount, i, OptionsSize;
BcdElementType Type;
PBCD_ELEMENT_HEADER Header;
PBCD_ELEMENT BcdElements;
PBL_BCD_OPTION Options, Option, PreviousOption, DeviceOptions;
PBCD_DEVICE_OPTION DeviceOption;
GUID DeviceId;
PVOID DeviceData;
/* Open the BCD object requested */
ObjectHandle = NULL;
BcdElements = NULL;
Status = BcdOpenObject(BcdHandle, ObjectId, &ObjectHandle);
if (!NT_SUCCESS(Status))
{
goto Quickie;
}
/* Do the initial enumeration to get the size needed */
ElementSize = 0;
Status = BcdEnumerateAndUnpackElements(BcdHandle,
ObjectHandle,
NULL,
&ElementSize,
&ElementCount);
if (Status != STATUS_BUFFER_TOO_SMALL)
{
/* If we got success, that doesn't make any sense */
if (NT_SUCCESS(Status))
{
Status = STATUS_INVALID_PARAMETER;
}
/* Bail out */
goto Quickie;
}
/* Allocate a large-enough buffer */
BcdElements = BlMmAllocateHeap(ElementSize);
if (!BcdElements)
{
Status = STATUS_NO_MEMORY;
goto Quickie;
}
/* Now do the real enumeration to fill out the elements buffer */
Status = BcdEnumerateAndUnpackElements(BcdHandle,
ObjectHandle,
BcdElements,
&ElementSize,
&ElementCount);
if (!NT_SUCCESS(Status))
{
goto Quickie;
}
/* Go through each BCD option to add the sizes up */
OptionsSize = 0;
for (i = 0; i < ElementCount; i++)
{
OptionsSize += BcdElements[i].Header->Size + sizeof(BL_BCD_OPTION);
}
/* Allocate the required BCD option list */
Options = BlMmAllocateHeap(OptionsSize);
if (!Options)
{
Status = STATUS_NO_MEMORY;
goto Quickie;
}
/* Zero it out */
RtlZeroMemory(Options, OptionsSize);
/* Start going through each option */
PreviousOption = NULL;
Option = Options;
for (i = 0; i < ElementCount; i++)
{
/* Read the header and type */
Header = BcdElements[i].Header;
Type.PackedValue = Header->Type;
/* Check if this option isn't already present */
if (!MiscGetBootOption(Options, Type.PackedValue))
{
/* It's a new option. Did we have an existing one? */
if (PreviousOption)
{
/* Link it to this new one */
PreviousOption->NextEntryOffset = (ULONG_PTR)Option -
(ULONG_PTR)Options;
}
/* Capture the type, size, data, and offset */
Option->Type = Type.PackedValue;
Option->DataSize = Header->Size;
RtlCopyMemory(Option + 1, BcdElements[i].Body, Header->Size);
Option->DataOffset = sizeof(BL_BCD_OPTION);
/* Check if this was a device */
if (Type.Format == BCD_TYPE_DEVICE)
{
/* Grab its GUID */
DeviceOption = (PBCD_DEVICE_OPTION)(Option + 1);
DeviceId = DeviceOption->AssociatedEntry;
/* Look up the options for that GUID */
Status = BmGetOptionList(BcdHandle, &DeviceId, &DeviceOptions);
if (NT_SUCCESS(Status))
{
/* Device data is after the device option */
DeviceData = (PVOID)((ULONG_PTR)DeviceOption + Header->Size);
/* Copy it */
RtlCopyMemory(DeviceData,
DeviceOptions,
BlGetBootOptionListSize(DeviceOptions));
/* Don't need this anymore */
BlMmFreeHeap(DeviceOptions);
/* Write the offset of the device options */
Option->ListOffset = (ULONG_PTR)DeviceData -
(ULONG_PTR)Option;
}
}
/* Save the previous option and go to the next one */
PreviousOption = Option;
Option = (PBL_BCD_OPTION)((ULONG_PTR)Option +
BlGetBootOptionSize(Option));
}
}
/* Return the pointer back, we've made it! */
*OptionList = Options;
Status = STATUS_SUCCESS;
Quickie:
/* Did we allocate a local buffer? Free it if so */
if (BcdElements)
{
BlMmFreeHeap(BcdElements);
}
/* Was the key open? Close it if so */
if (ObjectHandle)
{
BiCloseKey(ObjectHandle);
}
/* Return the option list parsing status */
return Status;
}
NTSTATUS
BmpUpdateApplicationOptions (
_In_ HANDLE BcdHandle
)
{
NTSTATUS Status;
PBL_BCD_OPTION Options;
/* Get the boot option list */
Status = BmGetOptionList(BcdHandle, &BmApplicationIdentifier, &Options);
if (!NT_SUCCESS(Status))
{
return Status;
}
/* Append the options, free the local buffer, and return success */
BlAppendBootOptions(&BlpApplicationEntry, Options);
BlMmFreeHeap(Options);
return STATUS_SUCCESS;
}
NTSTATUS
BmpFwGetApplicationDirectoryPath (
_In_ PUNICODE_STRING ApplicationDirectoryPath
)
{
NTSTATUS Status;
SIZE_T i, AppPathLength;
PWCHAR ApplicationPath, PathCopy;
/* Clear the incoming string */
ApplicationDirectoryPath->Length = 0;
ApplicationDirectoryPath->MaximumLength = 0;
ApplicationDirectoryPath->Buffer = 0;
/* Get the boot application path */
ApplicationPath = NULL;
Status = BlGetBootOptionString(BlpApplicationEntry.BcdData,
BcdLibraryString_ApplicationPath,
&ApplicationPath);
if (NT_SUCCESS(Status))
{
/* Calculate the length of the application path */
for (i = wcslen(ApplicationPath) - 1; i > 0; i--)
{
/* Keep going until the path separator */
if (ApplicationPath[i] == OBJ_NAME_PATH_SEPARATOR)
{
break;
}
}
/* Check if we have space for one more character */
Status = RtlSIZETAdd(i, 1, &AppPathLength);
if (NT_SUCCESS(Status))
{
/* Check if it's safe to multiply by two */
Status = RtlSIZETMult(AppPathLength, sizeof(WCHAR), &AppPathLength);
if (NT_SUCCESS(Status))
{
/* Allocate a copy for the string */
PathCopy = BlMmAllocateHeap(AppPathLength);
if (PathCopy)
{
/* NULL-terminate it */
RtlCopyMemory(PathCopy,
ApplicationPath,
AppPathLength - sizeof(UNICODE_NULL));
PathCopy[AppPathLength] = UNICODE_NULL;
/* Finally, initialize the outgoing string */
RtlInitUnicodeString(ApplicationDirectoryPath, PathCopy);
}
else
{
/* No memory, fail */
Status = STATUS_NO_MEMORY;
}
}
}
}
/* Check if we had an application path */
if (ApplicationPath)
{
/* No longer need this, free it */
BlMmFreeHeap(ApplicationPath);
}
/* All done! */
return Status;
}
NTSTATUS
BmFwInitializeBootDirectoryPath (
VOID
)
{
PWCHAR FinalPath;
NTSTATUS Status;
PWCHAR BcdDirectory;
UNICODE_STRING BcdPath;
ULONG FinalSize;
ULONG FileHandle, DeviceHandle;
/* Initialize everything for failure */
BcdPath.MaximumLength = 0;
BcdPath.Buffer = NULL;
BcdDirectory = NULL;
FinalPath = NULL;
FileHandle = -1;
DeviceHandle = -1;
/* Try to open the boot device */
Status = BlpDeviceOpen(BlpBootDevice,
BL_DEVICE_READ_ACCESS,
0,
&DeviceHandle);
if (!NT_SUCCESS(Status))
{
EfiPrintf(L"Device open failed: %lx\r\n", Status);
goto Quickie;
}
/* Get the directory path */
Status = BmpFwGetApplicationDirectoryPath(&BcdPath);
BcdDirectory = BcdPath.Buffer;
if (!NT_SUCCESS(Status))
{
goto Quickie;
}
/* Add the BCD file name to it */
FinalSize = BcdPath.MaximumLength + sizeof(L"\\BCD") - sizeof(UNICODE_NULL);
if (FinalSize < BcdPath.MaximumLength)
{
goto Quickie;
}
/* Allocate space for the final path */
FinalPath = BlMmAllocateHeap(FinalSize);
if (!FinalPath)
{
goto Quickie;
}
/* Build it */
RtlZeroMemory(FinalPath, FinalSize);
RtlCopyMemory(FinalPath, BcdDirectory, BcdPath.MaximumLength);
wcsncat(FinalPath, L"\\BCD", FinalSize / sizeof(WCHAR));
/* Try to open the file */
Status = BlFileOpen(DeviceHandle,
FinalPath,
BL_FILE_READ_ACCESS,
&FileHandle);
if (!NT_SUCCESS(Status))
{
BootDirectory = BcdDirectory;
goto Quickie;
}
/* Save the boot directory */
BootDirectory = L"\\EFI\\Boot"; /* Should be EFI\\ReactOS\\Boot */
Quickie:
/* Free all the allocations we made */
if (BcdDirectory)
{
Status = BlMmFreeHeap(BcdDirectory);
}
if (FinalPath)
{
Status = BlMmFreeHeap(FinalPath);
}
/* Close the BCD file */
if (FileHandle != -1)
{
Status = BlFileClose(FileHandle);
}
/* Close the boot device */
if (DeviceHandle != -1)
{
Status = BlDeviceClose(DeviceHandle);
}
/* Return back to the caller */
return Status;
}
NTSTATUS
BmOpenBootIni (
VOID
)
{
/* Don't yet handled boot.ini */
return STATUS_NOT_FOUND;
}
ULONG
BmpFatalErrorMessageFilter (
_In_ NTSTATUS ErrorStatus,
_Out_ PULONG ErrorResourceId
)
{
ULONG Result;
/* Assume no message for now, check for known status message */
Result = 0;
switch (ErrorStatus)
{
/* Convert each status to a resource ID */
case STATUS_UNEXPECTED_IO_ERROR:
*ErrorResourceId = 9017;
Result = 1;
break;
case STATUS_IMAGE_CHECKSUM_MISMATCH:
*ErrorResourceId = 9018;
break;
case STATUS_INVALID_IMAGE_WIN_64:
*ErrorResourceId = 9016;
break;
case 0xC0000428:
*ErrorResourceId = 9019;
Result = 2;
break;
case 0xC0210000:
*ErrorResourceId = 9013;
break;
}
/* Return the type of message */
return Result;
}
VOID
BmErrorPurge (
VOID
)
{
/* Check if a boot error is present */
if (BmpPackedBootError.BootError)
{
/* Purge it */
BlMmFreeHeap(BmpPackedBootError.BootError);
BmpPackedBootError.BootError = NULL;
}
/* Zero out the packed buffer */
BmpPackedBootError.Size = 0;
BmpInternalBootError = NULL;
RtlZeroMemory(&BmpErrorBuffer, sizeof(BmpErrorBuffer));
}
VOID
BmpErrorLog (
_In_ ULONG ErrorCode,
_In_ NTSTATUS ErrorStatus,
_In_ ULONG ErrorMsgId,
_In_ PWCHAR FileName,
_In_ ULONG HelpMsgId
)
{
PWCHAR ErrorMsgString;
/* Check if we already had an error */
if (BmpInternalBootError)
{
/* Purge it */
BmErrorPurge();
}
/* Find the string for this error ID */
ErrorMsgString = BlResourceFindMessage(ErrorMsgId);
if (ErrorMsgString)
{
/* Fill out the error buffer */
BmpErrorBuffer.Unknown1 = 0;
BmpErrorBuffer.Unknown2 = 0;
BmpErrorBuffer.ErrorString = ErrorMsgString;
BmpErrorBuffer.FileName = FileName;
BmpErrorBuffer.ErrorCode = ErrorCode;
BmpErrorBuffer.ErrorStatus = ErrorStatus;
BmpErrorBuffer.HelpMsgId = HelpMsgId;
BmpInternalBootError = &BmpErrorBuffer;
}
}
VOID
BmFatalErrorEx (
_In_ ULONG ErrorCode,
_In_ ULONG_PTR Parameter1,
_In_ ULONG_PTR Parameter2,
_In_ ULONG_PTR Parameter3,
_In_ ULONG_PTR Parameter4
)
{
PWCHAR FileName, Buffer;
NTSTATUS ErrorStatus;
WCHAR FormatString[256];
ULONG ErrorResourceId, ErrorHelpId;
BOOLEAN Restart, NoError;
/* Assume no buffer for now */
Buffer = NULL;
/* Check what error code is being raised */
switch (ErrorCode)
{
/* Error reading the BCD */
case BL_FATAL_ERROR_BCD_READ:
/* Check if we have a name for the BCD file */
if (Parameter1)
{
/* Check if the name fits into our buffer */
FileName = (PWCHAR)Parameter1;
if (wcslen(FileName) < sizeof(BmpFileNameBuffer))
{
/* Copy it in there */
Buffer = BmpFileNameBuffer;
wcsncpy(BmpFileNameBuffer,
FileName,
RTL_NUMBER_OF(BmpFileNameBuffer));
}
}
/* If we don't have a buffer, use an empty one */
if (!Buffer)
{
Buffer = ParentFileName;
}
/* The NTSTATUS code is in parameter 2*/
ErrorStatus = (NTSTATUS)Parameter2;
/* Build the error string */
swprintf(FormatString,
L"\nAn error occurred (%08x) while attempting "
L"to read the boot configuration data file %s\n",
ErrorStatus,
Buffer);
/* Select the resource ID message */
ErrorResourceId = 9002;
break;
case BL_FATAL_ERROR_BCD_ENTRIES:
/* File name is in parameter 1 */
FileName = (PWCHAR)Parameter1;
/* The NTSTATUS code is in parameter 2*/
ErrorStatus = (NTSTATUS)Parameter2;
/* Build the error string */
swprintf(FormatString,
L"\nNo valid entries found in the boot configuration data file %s\n",
FileName);
/* Select the resource ID message */
ErrorResourceId = 9007;
break;
case BL_FATAL_ERROR_BCD_PARSE:
/* File name isin parameter 1 */
FileName = (PWCHAR)Parameter1;
/* The NTSTATUS code is in parameter 2*/
ErrorStatus = (NTSTATUS)Parameter2;
/* Build the error string */
swprintf(FormatString,
L"\nThe boot configuration file %s is invalid (%08x).\n",
FileName,
ErrorStatus);
/* Select the resource ID message */
ErrorResourceId = 9015;
break;
case BL_FATAL_ERROR_GENERIC:
/* The NTSTATUS code is in parameter 1*/
ErrorStatus = (NTSTATUS)Parameter1;
/* Build the error string */
swprintf(FormatString,
L"\nThe boot manager experienced an error (%08x).\n",
ErrorStatus);
/* Select the resource ID message */
ErrorResourceId = 9005;
break;
default:
/* The rest is not yet handled */
EfiPrintf(L"Unexpected fatal error: %lx\r\n", ErrorCode);
while (1);
break;
}
/* Check if the BCD option for restart is set */
BlGetBootOptionBoolean(BlpApplicationEntry.BcdData,
BcdLibraryBoolean_RestartOnFailure,
&Restart);
if (Restart)
{
/* Yes, so no error should be shown since we'll auto-restart */
NoError = TRUE;
}
else
{
/* Check if the option for not showing errors is set in the BCD */
BlGetBootOptionBoolean(BlpApplicationEntry.BcdData,
BcdBootMgrBoolean_NoErrorDisplay,
&NoError);
}
/* Do we want an error? */
if (!NoError)
{
/* Yep, print it and then raise an error */
BlStatusPrint(FormatString);
BlStatusError(1, ErrorCode, Parameter1, Parameter2, Parameter3);
}
/* Get the help message ID */
ErrorHelpId = BmpFatalErrorMessageFilter(ErrorStatus, &ErrorResourceId);
BmpErrorLog(ErrorCode, ErrorStatus, ErrorResourceId, Buffer, ErrorHelpId);
}
NTSTATUS
BmpFwGetFullPath (
_In_ PWCHAR FileName,
_Out_ PWCHAR* FullPath
)
{
NTSTATUS Status;
SIZE_T BootDirLength, PathLength;
/* Compute the length of the directory, and add a NUL */
BootDirLength = wcslen(BootDirectory);
Status = RtlSIZETAdd(BootDirLength, 1, &BootDirLength);
if (!NT_SUCCESS(Status))
{
goto Quickie;
}
/* Add the length of the file, make sure it fits */
PathLength = wcslen(FileName);
Status = RtlSIZETAdd(PathLength, BootDirLength, &PathLength);
if (!NT_SUCCESS(Status))
{
goto Quickie;
}
/* Convert to bytes */
Status = RtlSIZETMult(PathLength, sizeof(WCHAR), &PathLength);
if (!NT_SUCCESS(Status))
{
goto Quickie;
}
/* Allocate the full path */
*FullPath = BlMmAllocateHeap(PathLength);
if (*FullPath)
{
/* Copy the directory followed by the file name */
wcsncpy(*FullPath, BootDirectory, PathLength / sizeof(WCHAR));
wcsncat(*FullPath, FileName, PathLength / sizeof(WCHAR));
}
else
{
/* Bail out since we have no memory */
Status = STATUS_NO_MEMORY;
}
Quickie:
/* Return to caller */
return Status;
}
VOID
BmCloseDataStore (
_In_ HANDLE Handle
)
{
/* Check if boot.ini data needs to be freed */
if (BmBootIniUsed)
{
EfiPrintf(L"Boot.ini not handled\r\n");
}
/* Dereference the hive and close the key */
BiDereferenceHive(Handle);
BiCloseKey(Handle);
}
NTSTATUS
BmOpenDataStore (
_Out_ PHANDLE Handle
)
{
NTSTATUS Status;
PBL_DEVICE_DESCRIPTOR BcdDevice;
PWCHAR BcdPath, FullPath, PathBuffer;
BOOLEAN HavePath;
SIZE_T PathLength, FullSize;
PVOID FinalBuffer;
UNICODE_STRING BcdString;
/* Initialize variables */
PathBuffer = NULL;
BcdDevice = NULL;
BcdPath = NULL;
HavePath = FALSE;
/* Check if a boot.ini file exists */
Status = BmOpenBootIni();
if (NT_SUCCESS(Status))
{
BmBootIniUsed = TRUE;
}
/* Check on which device the BCD is */
Status = BlGetBootOptionDevice(BlpApplicationEntry.BcdData,
BcdBootMgrDevice_BcdDevice,
&BcdDevice,
NULL);
if (!NT_SUCCESS(Status))
{
/* It's not on a custom device, so it must be where we are */
Status = BlGetBootOptionDevice(BlpApplicationEntry.BcdData,
BcdLibraryDevice_ApplicationDevice,
&BcdDevice,
NULL);
if (!NT_SUCCESS(Status))
{
/* This BCD option is required */
goto Quickie;
}
}
/* Next, check what file contains the BCD */
Status = BlGetBootOptionString(BlpApplicationEntry.BcdData,
BcdBootMgrString_BcdFilePath,
&BcdPath);
if (NT_SUCCESS(Status))
{
/* We don't handle custom BCDs yet */
EfiPrintf(L"Custom BCD Not handled: %s\r\n", BcdPath);
Status = STATUS_NOT_IMPLEMENTED;
goto Quickie;
}
/* Now check if the BCD is on a remote share */
if (BcdDevice->DeviceType == UdpDevice)
{
/* Nope. Nope. Nope */
EfiPrintf(L"UDP device Not handled\r\n");
Status = STATUS_NOT_IMPLEMENTED;
goto Quickie;
}
/* Otherwise, compute the hardcoded path of the BCD */
Status = BmpFwGetFullPath(L"\\BCD", &FullPath);
if (!NT_SUCCESS(Status))
{
/* User the raw path */
PathBuffer = BcdPath;
}
else
{
/* Use the path we got */
PathBuffer = FullPath;
HavePath = TRUE;
}
/* Check if we failed to get the BCD path */
if (!NT_SUCCESS(Status))
{
goto Quickie;
}
/* Add a NUL to the path, make sure it'll fit */
PathLength = wcslen(PathBuffer);
Status = RtlSIZETAdd(PathLength, 1, &PathLength);
if (!NT_SUCCESS(Status))
{
goto Quickie;
}
/* Convert to bytes */
Status = RtlSIZETMult(PathLength, sizeof(WCHAR), &PathLength);
if (!NT_SUCCESS(Status))
{
goto Quickie;
}
/* Now add the size of the path to the device path, check if it fits */
Status = RtlSIZETAdd(PathLength, BcdDevice->Size, &FullSize);
if (!NT_SUCCESS(Status))
{
goto Quickie;
}
/* Allocate a final structure to hold both entities */
FinalBuffer = BlMmAllocateHeap(FullSize);
if (!FinalBuffer)
{
Status = STATUS_NO_MEMORY;
goto Quickie;
}
/* Copy the device path and file path into the final buffer */
RtlCopyMemory(FinalBuffer, BcdDevice, BcdDevice->Size);
RtlCopyMemory((PVOID)((ULONG_PTR)FinalBuffer + BcdDevice->Size),
PathBuffer,
PathLength);
/* Now tell the BCD engine to open the store */
BcdString.Length = FullSize;
BcdString.MaximumLength = FullSize;
BcdString.Buffer = FinalBuffer;
Status = BcdOpenStoreFromFile(&BcdString, Handle);
/* Free our final buffer */
BlMmFreeHeap(FinalBuffer);
Quickie:
/* Did we allocate a device? */
if (BcdDevice)
{
/* Free it */
BlMmFreeHeap(BcdDevice);
}
/* Is this the failure path? */
if (!NT_SUCCESS(Status))
{
/* Raise a fatal error */
BmFatalErrorEx(BL_FATAL_ERROR_BCD_READ,
(ULONG_PTR)PathBuffer,
Status,
0,
0);
}
/* Did we get an allocated path? */
if ((PathBuffer) && (HavePath))
{
/* Free it */
BlMmFreeHeap(PathBuffer);
}
/* Return back to the caller */
return Status;
}
typedef struct _BL_BSD_LOG_OBJECT
{
ULONG DeviceId;
ULONG FileId;
ULONG Unknown;
ULONG Size;
ULONG Flags;
} BL_BSD_LOG_OBJECT, *PBL_BSD_LOG_OBJECT;
BL_BSD_LOG_OBJECT BsdpLogObject;
BOOLEAN BsdpLogObjectInitialized;
VOID
BlBsdInitializeLog (
_In_ PBL_DEVICE_DESCRIPTOR LogDevice,
_In_ PWCHAR LogPath,
_In_ ULONG Flags
)
{
NTSTATUS Status;
/* Don't initialize twice */
if (BsdpLogObjectInitialized)
{
return;
}
/* Set invalid IDs for now */
BsdpLogObject.DeviceId = -1;
BsdpLogObject.FileId = -1;
/* Open the BSD device */
Status = BlpDeviceOpen(LogDevice,
BL_DEVICE_READ_ACCESS | BL_DEVICE_WRITE_ACCESS,
0,
&BsdpLogObject.DeviceId);
if (!NT_SUCCESS(Status))
{
/* Welp that didn't work */
goto FailurePath;
}
/* Now open the BSD itself */
Status = BlFileOpen(BsdpLogObject.DeviceId,
LogPath,
BL_FILE_READ_ACCESS | BL_FILE_WRITE_ACCESS,
&BsdpLogObject.FileId);
if (!NT_SUCCESS(Status))
{
/* D'oh */
goto FailurePath;
}
/* The BSD is open. Start doing stuff to it */
EfiPrintf(L"Unimplemented BSD path\r\n");
Status = STATUS_NOT_IMPLEMENTED;
FailurePath:
/* Close the BSD if we had it open */
if (BsdpLogObject.FileId != -1)
{
BlFileClose(BsdpLogObject.FileId);
}
/* Close the device if we had it open */
if (BsdpLogObject.DeviceId != -1)
{
BlDeviceClose(BsdpLogObject.DeviceId);
}
/* Set BSD object to its uninitialized state */
BsdpLogObjectInitialized = FALSE;
BsdpLogObject.FileId = 0;
BsdpLogObject.DeviceId = 0;
BsdpLogObject.Flags = 0;
BsdpLogObject.Unknown = 0;
BsdpLogObject.Size = 0;
}
VOID
BmpInitializeBootStatusDataLog (
VOID
)
{
NTSTATUS Status;
PBL_DEVICE_DESCRIPTOR BsdDevice;
PWCHAR BsdPath;
ULONG Flags;
BOOLEAN PreserveBsd;
/* Initialize locals */
BsdPath = NULL;
BsdDevice = NULL;
Flags = 0;
/* Check if the BSD is stored in a custom device */
Status = BlGetBootOptionDevice(BlpApplicationEntry.BcdData,
BcdLibraryDevice_BsdLogDevice,
&BsdDevice,
NULL);
if (!NT_SUCCESS(Status))
{
/* Nope, use the boot device */
BsdDevice = BlpBootDevice;
}
/* Check if the path is custom as well */
Status = BlGetBootOptionString(BlpApplicationEntry.BcdData,
BcdLibraryString_BsdLogPath,
&BsdPath);
if (!NT_SUCCESS(Status))
{
/* Nope, use our default path */
Status = BmpFwGetFullPath(L"\\bootstat.dat", &BsdPath);
if (!NT_SUCCESS(Status))
{
BsdPath = NULL;
}
/* Set preserve flag */
Flags = 1;
}
else
{
/* Set preserve flag */
Flags = 1;
}
/* Finally, check if the BSD should be preserved */
Status = BlGetBootOptionBoolean(BlpApplicationEntry.BcdData,
BcdLibraryBoolean_PreserveBsdLog,
&PreserveBsd);
if (!(NT_SUCCESS(Status)) || !(PreserveBsd))
{
/* We failed to read, or we were asked not to preserve it */
Flags = 0;
}
/* Initialize the log */
BlBsdInitializeLog(BsdDevice, BsdPath, Flags);
/* Free the BSD device descriptor if we had one */
if (BsdDevice)
{
BlMmFreeHeap(BsdDevice);
}
/* Free the BSD path if we had one */
if ((Flags) && (BsdPath))
{
BlMmFreeHeap(BsdPath);
}
}
VOID
BmFwMemoryInitialize (
VOID
)
{
NTSTATUS Status;
PHYSICAL_ADDRESS PhysicalAddress;
BL_ADDRESS_RANGE AddressRange;
/* Select the range below 1MB */
AddressRange.Maximum = 0xFFFFF;
AddressRange.Minimum = 0;
/* Allocate one reserved page with the "below 1MB" attribute */
Status = MmPapAllocatePhysicalPagesInRange(&PhysicalAddress,
BlApplicationReserved,
1,
BlMemoryBelow1MB,
0,
&MmMdlUnmappedAllocated,
&AddressRange,
BL_MM_REQUEST_DEFAULT_TYPE);
if (!NT_SUCCESS(Status))
{
/* Print a message on error, but keep going */
BlStatusPrint(L"BmFwMemoryInitialize: Failed to allocate a page below 1MB. Status: 0x%08x\r\n",
Status);
}
}
NTSTATUS
BmpBgDisplayClearScreen (
_In_ ULONG Color
)
{
/* Not yet supported */
return STATUS_NOT_IMPLEMENTED;
}
NTSTATUS
BlXmiWrite (
_In_ PWCHAR XmlTag
)
{
/* Sigh */
EfiPrintf(L"XML: %s\r\n", XmlTag);
return STATUS_NOT_IMPLEMENTED;
}
NTSTATUS
BlXmiInitialize (
_In_ PWCHAR Stylesheet
)
{
/* Reset the cursor type */
BlDisplaySetCursorType(0);
/* Nope, not doing any XML stuff */
return STATUS_SUCCESS;
}
NTSTATUS
BmFwVerifySelfIntegrity (
VOID
)
{
/* Check if we're booted by UEFI off the DVD directly */
if ((BlpBootDevice->DeviceType == LocalDevice) &&
(BlpBootDevice->Local.Type == CdRomDevice) &&
(BlpApplicationFlags & BL_APPLICATION_FLAG_CONVERTED_FROM_EFI))
{
/* Windows actually bypasses integrity checks in this case. Works for us */
return STATUS_SUCCESS;
}
/* Our binaries aren't signed, so always return failure */
return 0xC0000428;
}
NTSTATUS
BmFwRegisterRevocationList (
VOID
)
{
NTSTATUS Status;
BOOLEAN SecureBootEnabled;
/* Is SecureBoot enabled? */
Status = BlSecureBootIsEnabled(&SecureBootEnabled);
if ((NT_SUCCESS(Status)) && (SecureBootEnabled))
{
EfiPrintf(L"SB not implemented revok\r\n");
return STATUS_NOT_IMPLEMENTED;
}
else
{
/* Nothing to do without SecureBoot */
Status = STATUS_SUCCESS;
}
/* Return revocation result back to caller */
return Status;
}
NTSTATUS
BmResumeFromHibernate (
_Out_ PHANDLE BcdResumeHandle
)
{
NTSTATUS Status;
BOOLEAN AttemptResume;
/* Should we attempt to resume from hibernation? */
Status = BlGetBootOptionBoolean(BlpApplicationEntry.BcdData,
BcdBootMgrBoolean_AttemptResume,
&AttemptResume);
if (!NT_SUCCESS(Status))
{
/* Nope. Is automatic restart on crash enabled? */
AttemptResume = FALSE;
Status = BlGetBootOptionBoolean(BlpApplicationEntry.BcdData,
BcdOSLoaderBoolean_DisableCrashAutoReboot,
&AttemptResume);
AttemptResume = (NT_SUCCESS(Status) && (AttemptResume));
}
/* Don't do anything if there's no need to resume anything */
if (!AttemptResume)
{
return STATUS_SUCCESS;
}
/* Not yet implemented */
EfiPrintf(L"Resume not supported\r\n");
return STATUS_NOT_IMPLEMENTED;
}
NTSTATUS
BmpProcessBadMemory (
VOID
)
{
BL_PD_DATA_BLOB BadMemoryData;
NTSTATUS Status;
/* Try to get the memory data from the memtest application */
BadMemoryData.BlobSize = 0;
BadMemoryData.Data = NULL;
BadMemoryData.DataSize = 0;
Status = BlPdQueryData(&BadMemoryGuid, NULL, &BadMemoryData);
if (Status != STATUS_BUFFER_TOO_SMALL)
{
/* No results, or some other error */
return Status;
}
/* Not yet implemented */
EfiPrintf(L"Bad page list persistence not implemented\r\n");
return STATUS_NOT_IMPLEMENTED;
}
NTSTATUS
BmPurgeOption (
_In_ HANDLE BcdHandle,
_In_ PGUID ObjectId,
_In_ ULONG Type
)
{
HANDLE ObjectHandle;
NTSTATUS Status;
/* Open the object */
Status = BcdOpenObject(BcdHandle, ObjectId, &ObjectHandle);
if (NT_SUCCESS(Status))
{
/* Delete the element */
BcdDeleteElement(ObjectHandle, Type);
/* Close the object and set success */
BiCloseKey(ObjectHandle);
Status = STATUS_SUCCESS;
}
/* Return the result */
return Status;
}
NTSTATUS
BmGetEntryDescription (
_In_ HANDLE BcdHandle,
_In_ PGUID ObjectId,
_Out_ PBCD_OBJECT_DESCRIPTION Description
)
{
NTSTATUS Status;
HANDLE ObjectHandle;
/* Open the BCD object */
Status = BcdOpenObject(BcdHandle, ObjectId, &ObjectHandle);
if (NT_SUCCESS(Status))
{
/* Make sure the caller passed this argument in */
if (!Description)
{
/* Fail otherwise */
Status = STATUS_INVALID_PARAMETER;
}
else
{
/* Query the description from the BCD interface */
Status = BiGetObjectDescription(ObjectHandle, Description);
}
/* Close the object key */
BiCloseKey(ObjectHandle);
}
/* Return the result back */
return Status;
}
NTSTATUS
BmpPopulateBootEntryList (
_In_ HANDLE BcdHandle,
_In_ PGUID SequenceList,
_In_ ULONG Flags,
_Out_ PBL_LOADED_APPLICATION_ENTRY* BootSequence,
_Out_ PULONG SequenceCount
)
{
NTSTATUS Status;
ULONG BootIndex, i, OptionSize;
PBL_LOADED_APPLICATION_ENTRY BootEntry;
PBL_BCD_OPTION Options;
BCD_OBJECT_DESCRIPTION Description;
BcdObjectType ObjectType;
BOOLEAN HavePath, IsWinPe, SoftReboot;
PWCHAR LoaderPath;
/* Initialize locals */
Options = NULL;
BootIndex = 0;
Status = STATUS_NOT_FOUND;
/* Loop through every element in the sequence */
for (i = 0; i < *SequenceCount; i++)
{
/* Assume failure */
BootEntry = NULL;
/* Get the options for the sequence element */
Status = BmGetOptionList(BcdHandle, SequenceList, &Options);
if (!NT_SUCCESS(Status))
{
EfiPrintf(L"option list failed: %lx\r\n", Status);
goto LoopQuickie;
}
/* Make sure there's at least a path and description */
if (!(MiscGetBootOption(Options, BcdLibraryDevice_ApplicationDevice)) ||
!(MiscGetBootOption(Options, BcdLibraryString_Description)))
{
Status = STATUS_UNSUCCESSFUL;
EfiPrintf(L"missing list failed: %lx\r\n", Status);
goto LoopQuickie;
}
/* Get the size of the BCD options and allocate a large enough entry */
OptionSize = BlGetBootOptionListSize(Options);
BootEntry = BlMmAllocateHeap(sizeof(*BootEntry) + OptionSize);
if (!BootEntry)
{
Status = STATUS_NO_MEMORY;
goto Quickie;
}
/* Save it as part of the sequence */
BootSequence[BootIndex] = BootEntry;
/* Initialize it, and copy the BCD data */
RtlZeroMemory(BootEntry, sizeof(*BootEntry));
BootEntry->Guid = *SequenceList;
BootEntry->BcdData = (PBL_BCD_OPTION)(BootEntry + 1);
BootEntry->Flags = Flags;
RtlCopyMemory(BootEntry->BcdData, Options, OptionSize);
/* Get the object descriptor to find out what kind of entry it is */
Status = BmGetEntryDescription(BcdHandle,
&BootEntry->Guid,
&Description);
if (!NT_SUCCESS(Status))
{
EfiPrintf(L"missing desc failed: %lx\r\n", Status);
goto LoopQuickie;
}
/* Check if a path was given or not */
HavePath = MiscGetBootOption(Options, BcdLibraryString_ApplicationPath) ?
TRUE : FALSE;
/* Now select based on what type of object this is -- must be an app */
ObjectType.PackedValue = Description.Type;
if (ObjectType.Application.ObjectCode == BCD_OBJECT_TYPE_APPLICATION)
{
/* Then select based on what kind of app it is */
switch (ObjectType.Application.ApplicationCode)
{
/* Another boot manager */
case BCD_APPLICATION_TYPE_BOOTMGR:
BootEntry->Flags |= BCD_APPLICATION_TYPE_BOOTMGR;
break;
/* An OS loader */
case BCD_APPLICATION_TYPE_OSLOADER:
BootEntry->Flags |= BL_APPLICATION_ENTRY_WINLOAD;
/* Do we have a path for it? */
if (!HavePath)
{
/* We'll try to make one up. Is this WinPE? */
IsWinPe = FALSE;
Status = BlGetBootOptionBoolean(Options,
BcdOSLoaderBoolean_WinPEMode,
&IsWinPe);
if (!(NT_SUCCESS(Status)) && (Status != STATUS_NOT_FOUND))
{
goto Quickie;
}
/* Use the appropriate path for WinPE or local install */
LoaderPath = IsWinPe ?
L"\\Windows\\System32\\boot\\winload.efi" :
L"\\Windows\\System32\\winload.efi";
/* Add the path to the boot entry */
Status = BlAppendBootOptionString(BootEntry,
BcdLibraryString_ApplicationPath,
LoaderPath);
if (!NT_SUCCESS(Status))
{
goto Quickie;
}
/* We have a path now */
HavePath = TRUE;
}
break;
/* A hibernate-resume application */
case BCD_APPLICATION_TYPE_RESUME:
BootEntry->Flags |= BL_APPLICATION_ENTRY_WINRESUME;
break;
/* An older OS NTLDR */
case BCD_APPLICATION_TYPE_NTLDR:
BootEntry->Flags |= BL_APPLICATION_ENTRY_NTLDR;
break;
/* An older OS SETUPLDR */
case BCD_APPLICATION_TYPE_SETUPLDR:
BootEntry->Flags |= BL_APPLICATION_ENTRY_SETUPLDR;
break;
/* A 3rd party/Win9x boot sector */
case BCD_APPLICATION_TYPE_BOOTSECTOR:
BootEntry->Flags |= BL_APPLICATION_ENTRY_BOOTSECTOR;
break;
/* Something else entirely */
default:
break;
}
}
/* We better have a path by now */
if (!HavePath)
{
Status = STATUS_UNSUCCESSFUL;
goto LoopQuickie;
}
/* Check if this is a real mode startup.com */
if ((ObjectType.Application.ObjectCode == BCD_OBJECT_TYPE_APPLICATION) &&
(ObjectType.Application.ImageCode == BCD_IMAGE_TYPE_REAL_MODE) &&
(ObjectType.Application.ApplicationCode == BCD_APPLICATION_TYPE_STARTUPCOM))
{
/* Check if PXE soft reboot will occur */
Status = BlGetBootOptionBoolean(Options,
BcdStartupBoolean_PxeSoftReboot,
&SoftReboot);
if ((NT_SUCCESS(Status)) && (SoftReboot))
{
/* Then it's a valid startup.com entry */
BootEntry->Flags |= BL_APPLICATION_ENTRY_STARTUP;
}
}
LoopQuickie:
/* All done with this entry -- did we have BCD options? */
if (Options)
{
/* Free them, they're part of the entry now */
BlMmFreeHeap(Options);
Options = NULL;
}
/* Did we fail anywhere? */
if (!NT_SUCCESS(Status))
{
/* Yep -- did we fail with an active boot entry? */
if (BootEntry)
{
/* Destroy it */
BlDestroyBootEntry(BootEntry);
BootSequence[BootIndex] = NULL;
}
}
else
{
/* It worked, so populate the next index now */
BootIndex++;
}
/* And move to the next GUID in the sequence list */
SequenceList++;
}
Quickie:
/* All done now -- did we have any BCD options? */
if (Options)
{
/* Free them */
BlMmFreeHeap(Options);
}
/* Return the status */
return Status;
}
NTSTATUS
BmGetBootSequence (
_In_ HANDLE BcdHandle,
_In_ PGUID SequenceList,
_In_ ULONG SequenceListCount,
_In_ ULONG Flags,
_Out_ PBL_LOADED_APPLICATION_ENTRY** BootSequence,
_Out_ PULONG SequenceCount
)
{
PBL_LOADED_APPLICATION_ENTRY* Sequence;
ULONG Count = SequenceListCount;
NTSTATUS Status;
/* Allocate the sequence list */
Sequence = BlMmAllocateHeap(SequenceListCount * sizeof(*Sequence));
if (!Sequence)
{
return STATUS_NO_MEMORY;
}
/* Populate the sequence list */
Status = BmpPopulateBootEntryList(BcdHandle,
SequenceList,
Flags,
Sequence,
&Count);
if (!NT_SUCCESS(Status))
{
/* Free the list on failure */
BlMmFreeHeap(Sequence);
}
else
{
/* Otherwise, set success and return the list and count */
Status = STATUS_SUCCESS;
*BootSequence = Sequence;
*SequenceCount = Count;
}
/* All done */
return Status;
}
NTSTATUS
BmEnumerateBootEntries (
_In_ HANDLE BcdHandle,
_Out_ PBL_LOADED_APPLICATION_ENTRY **BootSequence,
_Out_ PULONG SequenceCount
)
{
NTSTATUS Status;
ULONG BootIndex, BootIniCount, BootEntryCount, BcdCount;
PBL_LOADED_APPLICATION_ENTRY* Sequence;
PGUID DisplayOrder;
GUID DefaultObject;
BOOLEAN UseDisplayList;
/* Initialize locals */
BootIndex = 0;
/* First try to get the display list, if any */
UseDisplayList = TRUE;
Status = BlGetBootOptionGuidList(BlpApplicationEntry.BcdData,
BcdBootMgrObjectList_DisplayOrder,
&DisplayOrder,
&BcdCount);
if (!NT_SUCCESS(Status))
{
/* No list, get the default entry instead */
Status = BlGetBootOptionGuid(BlpApplicationEntry.BcdData,
BcdBootMgrObject_DefaultObject,
&DefaultObject);
if (NT_SUCCESS(Status))
{
/* Set the array to just our entry */
UseDisplayList = FALSE;
BcdCount = 1;
DisplayOrder = &DefaultObject;
}
else
{
/* No default list either, return success but no entries */
*BootSequence = NULL;
*SequenceCount = 0;
Status = STATUS_SUCCESS;
DisplayOrder = NULL;
goto Quickie;
}
}
/* Check if boot.ini was used */
BootIniCount = 0;
if (BmBootIniUsed)
{
/* Get the entries from it */
EfiPrintf(L"Boot.ini not supported\r\n");
BootIniCount = 0;//BmBootIniGetEntryCount();
}
/* Allocate an array large enough for the combined boot entries */
BootEntryCount = BootIniCount + BcdCount;
Sequence = BlMmAllocateHeap(BootEntryCount * sizeof(*Sequence));
if (!Sequence)
{
Status = STATUS_NO_MEMORY;
goto Quickie;
}
/* Zero it out */
RtlZeroMemory(Sequence, BootEntryCount * sizeof(*Sequence));
/* Check if we had BCD entries */
if (BcdCount)
{
/* Populate the list of bootable entries */
Status = BmpPopulateBootEntryList(BcdHandle,
DisplayOrder,
BL_APPLICATION_ENTRY_DISPLAY_ORDER,
Sequence,
&BcdCount);
if (!NT_SUCCESS(Status))
{
/* Bail out */
goto Quickie;
}
}
/* Check if we had boot.ini entries */
if (BootIniCount)
{
/* TODO */
EfiPrintf(L"Boot.ini not supported\r\n");
}
/* Return success and the sequence + count populated */
Status = STATUS_SUCCESS;
*BootSequence = Sequence;
*SequenceCount = BootIniCount + BcdCount;
Quickie:
/* Check if we had allocated a GUID list */
if ((UseDisplayList) && (DisplayOrder))
{
/* Free it */
BlMmFreeHeap(DisplayOrder);
}
/* Check if this is the failure path */
if (!(NT_SUCCESS(Status)) && (Sequence))
{
/* Loop the remaining boot entries */
while (BootIndex < BootEntryCount)
{
/* Check if it had been allocated */
if (Sequence[BootIndex])
{
/* Free it */
BlMmFreeHeap(Sequence[BootIndex]);
}
/* Next*/
BootIndex++;
}
/* Free the whole sequence now */
BlMmFreeHeap(Sequence);
}
/* All done, return the result */
return Status;
}
VOID
BmpGetDefaultBootEntry (
_In_ PBL_LOADED_APPLICATION_ENTRY* Sequence,
_In_ ULONG Count,
_Out_ PBL_LOADED_APPLICATION_ENTRY* DefaultEntry,
_Out_ PULONG DefaultIndex
)
{
GUID DefaultObject;
NTSTATUS Status;
ULONG BootIndex;
/* Assume no default */
*DefaultEntry = *Sequence;
*DefaultIndex = 0;
/* Nothing to do if there's just one entry */
if (Count == 1)
{
return;
}
/* Get the default object, bail out if there isn't one */
Status = BlGetBootOptionGuid(BlpApplicationEntry.BcdData,
BcdBootMgrObject_DefaultObject,
&DefaultObject);
if (!(NT_SUCCESS(Status)) || !(Count))
{
return;
}
/* Scan the boot sequence */
for (BootIndex = 0; BootIndex < Count; BootIndex++)
{
/* Find one that matches the default */
if (RtlEqualMemory(&Sequence[BootIndex]->Guid,
&DefaultObject,
sizeof(GUID)))
{
/* Return it */
*DefaultEntry = Sequence[BootIndex];
*DefaultIndex = BootIndex;
return;
}
}
}
BL_MENU_POLICY
BmGetBootMenuPolicy (
_In_ PBL_LOADED_APPLICATION_ENTRY BootEntry
)
{
NTSTATUS Status;
BOOLEAN EmsEnabled;
ULONGLONG BootMenuPolicy;
ULONG OptionId;
/* Check if EMS is enabled */
Status = BlGetBootOptionBoolean(BlpApplicationEntry.BcdData,
BcdOSLoaderBoolean_EmsEnabled,
&EmsEnabled);
if ((NT_SUCCESS(Status)) && (EmsEnabled))
{
/* No boot menu */
return MenuPolicyLegacy;
}
/* Check what entry we are looking at */
if (!BootEntry)
{
/* No entry, pick the selected one */
BootEntry = BmpSelectedBootEntry;
}
/* Do we still not have an entry? */
if (!BootEntry)
{
/* Show the menu */
return MenuPolicyStandard;
}
/* Check if this is an OS loader */
BootMenuPolicy = 0;
if (BootEntry->Flags & BL_APPLICATION_ENTRY_WINLOAD)
{
/* Use the correct option ID */
OptionId = BcdOSLoaderInteger_BootMenuPolicy;
}
else
{
/* Check if this is an OS resumer */
if (!(BootEntry->Flags & BL_APPLICATION_ENTRY_WINRESUME))
{
/* Nope, so no reason for a menu */
return MenuPolicyLegacy;
}
/* Use the correct option ID */
OptionId = BcdResumeInteger_BootMenuPolicy;
}
/* Check the option ID for the boot menu policy */
Status = BlGetBootOptionInteger(BootEntry->BcdData,
OptionId,
&BootMenuPolicy);
if (NT_SUCCESS(Status))
{
/* We have one, return it */
return BootMenuPolicy;
}
/* No policy, so assume no menu */
return MenuPolicyLegacy;
}
VOID
BmDisplayGetBootMenuStatus (
_Out_ PL_MENU_STATUS MenuStatus
)
{
/* For now, don't support key input at all */
MenuStatus->AsULong = 0;
MenuStatus->OemKey = UNICODE_NULL;
MenuStatus->BootIndex = -1;
}
NTSTATUS
BmProcessCustomAction (
_In_ HANDLE BcdHandle,
_In_ PWCHAR ActionKey
)
{
EfiPrintf(L"Custom actions not yet handled\r\n");
return STATUS_NOT_IMPLEMENTED;
}
VOID
BmpProcessBootEntry (
_In_ HANDLE BcdHandle,
_In_ PBL_LOADED_APPLICATION_ENTRY BootEntry,
_Out_ PBOOLEAN ExitBootManager
)
{
BL_MENU_STATUS MenuStatus;
/* Don't exit */
*ExitBootManager = FALSE;
/* If the legacy menu must be shown, or if we have a boot entry */
if ((BmGetBootMenuPolicy(BootEntry) != MenuPolicyStandard) || (BootEntry))
{
/* Check if any key has been pressed */
BmDisplayGetBootMenuStatus(&MenuStatus);
if (MenuStatus.AnyKey)
{
/* Was the exit key pressed? */
if (MenuStatus.Exit)
{
/* Don't display a menu, and exit */
*ExitBootManager = TRUE;
BmpDisplayBootMenu = FALSE;
}
else if (MenuStatus.OemKey)
{
/* Process the OEM key action */
BmProcessCustomAction(BcdHandle, &MenuStatus.KeyValue);
}
else
{
/* Process other keys */
EfiPrintf(L"TODO\r\n");
}
}
}
}
NTSTATUS
BmpGetSelectedBootEntry (
_In_ HANDLE BcdHandle,
_Out_ PBL_LOADED_APPLICATION_ENTRY* SelectedBootEntry,
_Out_ PULONG EntryIndex,
_Out_ PBOOLEAN ExitBootManager
)
{
NTSTATUS Status;
PBL_LOADED_APPLICATION_ENTRY* Sequence;
PBL_LOADED_APPLICATION_ENTRY Entry, SelectedEntry;
ULONG Count, BootIndex, SelectedIndex;
// BOOLEAN FoundFailedEntry;
ULONGLONG Timeout;
/* Initialize locals */
BootIndex = 0;
Count = 0;
Sequence = NULL;
SelectedEntry = NULL;
/* Enumerate all the boot entries */
Status = BmEnumerateBootEntries(BcdHandle, &Sequence, &Count);
if (!NT_SUCCESS(Status))
{
/* Bail out if we failed */
goto Quickie;
}
/* Check if there are no entries */
if (!Count)
{
/* This is fatal -- kill the system */
Status = STATUS_FILE_INVALID;
BmFatalErrorEx(BL_FATAL_ERROR_BCD_ENTRIES, (ULONG_PTR)L"\\BCD", Status, 0, 0);
goto Quickie;
}
/* Check if we don't yet have an array of failed boot entries */
if (!BmpFailedBootEntries)
{
/* Allocate it */
BmpFailedBootEntries = BlMmAllocateHeap(Count);
if (BmpFailedBootEntries)
{
/* Zero it out */
RtlZeroMemory(BmpFailedBootEntries, Count);
}
}
/* Check if we have a hardcoded boot override */
if (BmBootEntryOverridePresent)
{
EfiPrintf(L"Hard-coded boot override mode not supported\r\n");
}
/* Log the OS count */
//BlLogEtwWrite(BOOT_BOOTMGR_MULTI_OS_COUNT);
/* Check if the display is already active and cached */
if (!BmDisplayStateCached)
{
/* Check if we should display a boot menu */
Status = BlGetBootOptionBoolean(BlpApplicationEntry.BcdData,
BcdBootMgrBoolean_DisplayBootMenu,
&BmpDisplayBootMenu);
if (!NT_SUCCESS(Status))
{
/* Assume not */
BmpDisplayBootMenu = FALSE;
}
}
/* Check if there's only one entry to boot anyway */
if (Count == 1)
{
/* Read it */
SelectedEntry = *Sequence;
/* Process it */
BmpProcessBootEntry(BcdHandle, SelectedEntry, ExitBootManager);
/* Check if we're not displaying a boot menu */
if (!BmpDisplayBootMenu)
{
/* Now we are */
BmpDisplayBootMenu = TRUE;
/* Return the entry and its index back */
*EntryIndex = 0;
*SelectedBootEntry = SelectedEntry;
Status = STATUS_SUCCESS;
goto Quickie;
}
}
else
{
/* Get the default boot entry */
BmpGetDefaultBootEntry(Sequence, Count, &SelectedEntry, &SelectedIndex);
/* Check if we have a failed boot entry array allocated */
//FoundFailedEntry = FALSE;
if (BmpFailedBootEntries)
{
/* Check if the default entry failed to boot */
if (BmpFailedBootEntries[SelectedIndex])
{
/* Loop through the current boot sequence */
for (SelectedIndex = 0; SelectedIndex < Count; SelectedIndex++)
{
/* Check if there's no sequence for this index, or it failed */
while (!(Sequence[SelectedIndex]) ||
(BmpFailedBootEntries[SelectedIndex]))
{
/* Remember that this is a failed entry */
SelectedEntry = Sequence[SelectedIndex];
//FoundFailedEntry = TRUE;
BmpDisplayBootMenu = FALSE;
}
}
}
}
/* Check if the entry is an OS loader */
if (SelectedEntry->Flags & BL_APPLICATION_ENTRY_WINLOAD)
{
// todo
EfiPrintf(L"todo path\r\n");
}
/* Check if there's no timeout */
Status = BlGetBootOptionInteger(BlpApplicationEntry.BcdData,
BcdBootMgrInteger_Timeout,
&Timeout);
if ((NT_SUCCESS(Status) && !(Timeout)))
{
/* There isn't, so just process the default entry right away */
BmpProcessBootEntry(BcdHandle, SelectedEntry, ExitBootManager);
/* Check if we're not displaying a boot menu */
if (!BmpDisplayBootMenu)
{
/* Now we are */
BmpDisplayBootMenu = TRUE;
/* Return the entry and its index back */
*EntryIndex = 0;
*SelectedBootEntry = SelectedEntry;
Status = STATUS_SUCCESS;
goto Quickie;
}
/* Remove the timeout for this boot instance */
BlRemoveBootOption(BlpApplicationEntry.BcdData,
BcdBootMgrInteger_Timeout);
}
}
/* Here is where we display the menu and list of tools */
EfiPrintf(L"Tool selection not yet implemented\r\n");
EfiStall(10000000);
*SelectedBootEntry = NULL;
Quickie:
/* We are done -- did we have a sequence? */
if (Sequence)
{
/* Do we have any boot entries we parsed? */
while (BootIndex < Count)
{
/* Get the current boot entry */
Entry = Sequence[BootIndex];
/* Did we fail, or is is not the selected one? */
if ((Entry) && ((Entry != SelectedEntry) || !(NT_SUCCESS(Status))))
{
/* Destroy it, as it won't be needed */
BlDestroyBootEntry(Entry);
}
else if (Entry == SelectedEntry)
{
/* It's the selected one, return its index */
*EntryIndex = BootIndex;
}
/* Move to the next entry */
BootIndex++;
}
/* Free the sequence of entries */
BlMmFreeHeap(Sequence);
}
/* Return the selection result */
return Status;
}
NTSTATUS
BmLaunchRecoverySequence (
_In_ PBL_LOADED_APPLICATION_ENTRY BootEntry,
_In_ ULONG LaunchCode
)
{
NTSTATUS Status;
PBL_LOADED_APPLICATION_ENTRY RecoveryEntry;
HANDLE BcdHandle;
PGUID RecoverySequence;
ULONG Count, i, RecoveryIndex, SequenceCount;
PBL_LOADED_APPLICATION_ENTRY* Sequence;
/* Initialize locals */
RecoveryIndex = 0;
Sequence = NULL;
RecoverySequence = NULL;
Count = 0;
BcdHandle = NULL;
/* Open the BCD*/
Status = BmOpenDataStore(&BcdHandle);
if (!NT_SUCCESS(Status))
{
goto Quickie;
}
/* Get the recovery sequence list */
Status = BlGetBootOptionGuidList(BootEntry->BcdData,
BcdLibraryObjectList_RecoverySequence,
&RecoverySequence,
&SequenceCount);
if (!NT_SUCCESS(Status))
{
goto Quickie;
}
/* Get the sequence of boot entries out of it */
Status = BmGetBootSequence(BcdHandle,
RecoverySequence,
SequenceCount,
BL_APPLICATION_ENTRY_RECOVERY,
&Sequence,
&Count);
if (!NT_SUCCESS(Status))
{
goto Quickie;
}
/* Was the BCD open? */
if (BcdHandle)
{
/* Close it */
BmCloseDataStore(BcdHandle);
}
/* Now go over every entry in the sequence */
for (i = 0; i < Count; ++i)
{
/* Check the code for this recovery launch */
if (LaunchCode == 2 || LaunchCode == 5)
{
/* Remove the override if there is one, and set it to 4 */
BlRemoveBootOption(Sequence[i]->BcdData, BcdLibraryInteger_DisplayMessageOverride);
BlAppendBootOptionInteger(Sequence[i],
BcdLibraryInteger_DisplayMessageOverride,
4);
}
else if (LaunchCode == 3)
{
/* Remove the override if there is one, and set it to 10 */
BlRemoveBootOption(Sequence[i]->BcdData, BcdLibraryInteger_DisplayMessageOverride);
BlAppendBootOptionInteger(Sequence[i],
BcdLibraryInteger_DisplayMessageOverride,
10);
}
/* Launch the boot entry for this part of the recovery sequence */
Status = BmpLaunchBootEntry(Sequence[i], NULL, LaunchCode, FALSE);
if (!NT_SUCCESS(Status))
{
break;
}
}
Quickie:
/* Did we have a sequence of entries? */
if (Sequence)
{
/* Loop through each one */
for (RecoveryIndex = 0; RecoveryIndex < Count; RecoveryIndex++)
{
/* Does this index have an allocated boot entry? */
RecoveryEntry = Sequence[RecoveryIndex];
if (RecoveryEntry)
{
/* Destroy it */
BlDestroyBootEntry(RecoveryEntry);
}
}
/* Free the sequence itself */
BlMmFreeHeap(Sequence);
}
/* Was there a sequence list? */
if (RecoverySequence)
{
/* Free it */
BlMmFreeHeap(RecoverySequence);
}
/* Return back to caller */
return Status;
}
ULONG
BmDisplayDumpError (
_In_ PBL_LOADED_APPLICATION_ENTRY BootEntry,
_In_ ULONG LaunchCode
)
{
ULONG BootError;
NTSTATUS Status;
BOOLEAN Restart, NoError;
/* Assume we'll just reboot */
BootError = Reboot;
/* Should we reboot? */
Status = BlGetBootOptionBoolean(BlpApplicationEntry.BcdData,
BcdLibraryBoolean_RestartOnFailure,
&Restart);
if ((NT_SUCCESS(Status)) && (Restart))
{
return BootError;
}
/* Should we not show errors, and thus, reboot? */
Status = BlGetBootOptionBoolean(BlpApplicationEntry.BcdData,
BcdBootMgrBoolean_NoErrorDisplay,
&NoError);
if ((NT_SUCCESS(Status)) && (NoError))
{
return BootError;
}
/* Is there an internal boot error? */
if (BmpInternalBootError)
{
/* Return it -- but it's a pointer? */
return (ULONG_PTR)BmpInternalBootError; // ???
}
/* Otherwise, show the menu to see what to do */
EfiPrintf(L"Error menu not yet implemented\r\n");
return BootError;
}
NTSTATUS
BmpCreateDevices (
_In_ PBL_LOADED_APPLICATION_ENTRY BootEntry
)
{
ULONG NextOffset, DataOffset, ListOffset;
PBL_BCD_OPTION Option, ListOption;
BcdElementType ElementType;
PBCD_DEVICE_OPTION BcdDevice;
/* Starting at offset 0, loop every BCD option */
NextOffset = 0;
do
{
/* Get the current option, and its offset */
Option = (PBL_BCD_OPTION)((ULONG_PTR)BootEntry->BcdData + NextOffset);
NextOffset = Option->NextEntryOffset;
/* If it's empty, ignore it */
if (Option->Empty)
{
continue;
}
/* If it's not a device option, ignore it */
ElementType.PackedValue = Option->Type;
if (ElementType.Format != BCD_TYPE_DEVICE)
{
continue;
}
/* Get the data offset */
DataOffset = Option->DataOffset;
/* Extract the device out of it */
BcdDevice = (PBCD_DEVICE_OPTION)((ULONG_PTR)BootEntry->BcdData + DataOffset);
/* If the device is already fully specified, no need to build it */
if (!(BcdDevice->DeviceDescriptor.Flags & 1))
{
continue;
}
/* Otherwise, check if there's any list options as well */
ListOption = NULL;
ListOffset = Option->ListOffset;
if (Option->ListOffset)
{
ListOption = (PBL_BCD_OPTION)((ULONG_PTR)BootEntry->BcdData + ListOffset);
}
/* And now call BlCreateDevice to build the full device descriptor */
EfiPrintf(L"Unspecified devices not yet supported: %p\r\n", ListOption);
return STATUS_NOT_SUPPORTED;
} while (NextOffset != 0);
/* Devices created successfully */
return STATUS_SUCCESS;
}
NTSTATUS
BmpTransferExecution (
_In_ PBL_LOADED_APPLICATION_ENTRY BootEntry,
_Out_ PULONG LaunchCode,
_Out_ PBOOLEAN Recover
)
{
PWCHAR AppPath;
NTSTATUS Status;
PBL_DEVICE_DESCRIPTOR AppDevice;
BL_RETURN_ARGUMENTS ReturnArgs;
BOOLEAN AdvancedOptions;
ULONG AppHandle;
/* Get the application path */
Status = BlGetBootOptionString(BootEntry->BcdData,
BcdLibraryString_ApplicationPath,
&AppPath);
if (!NT_SUCCESS(Status))
{
/* If we couldn't find one, set this to NULL */
AppPath = NULL;
}
/* Check if this is a PXE startup.com */
if (BootEntry->Flags & BL_APPLICATION_ENTRY_STARTUP)
{
#if BL_NET_SUPPORT
/* Do soft reboot to launch it */
Status = BlNetSoftReboot(BootEntry);
#else
EfiPrintf(L"Net boot not supported\r\n");
Status = STATUS_NOT_SUPPORTED;
#endif
/* Nothing else for us to do */
goto Quickie;
}
/* Loop as long as boot was not cancelled */
do
{
/* Load the boot application */
Status = BlImgLoadBootApplication(BootEntry, &AppHandle);
/* Did we not find it? */
if (Status == STATUS_NOT_FOUND)
{
/* Get the device for the boot application */
Status = BlGetBootOptionDevice(BootEntry->BcdData,
BcdLibraryDevice_ApplicationDevice,
&AppDevice,
NULL);
if (!NT_SUCCESS(Status))
{
/* Force re-enumeration */
Status = BlFwEnumerateDevice(AppDevice);
}
/* Did re-enumeration work? */
if (!NT_SUCCESS(Status))
{
/* Nope, raise a fatal error */
BmFatalErrorEx(BL_FATAL_ERROR_APP_LOAD,
(ULONG_PTR)AppPath,
Status,
0,
0);
goto Quickie;
}
/* Yes, try booting it again */
Status = BlImgLoadBootApplication(BootEntry, &AppHandle);
}
/* Was boot cancelled?*/
if (Status == STATUS_CANCELLED)
{
/* Should we display the menu, or is there no launch sequence? */
if ((BmGetBootMenuPolicy(BootEntry) != MenuPolicyStandard) ||
!(MiscGetBootOption(BootEntry->BcdData,
BcdLibraryObjectList_RecoverySequence)))
{
/* Bail out, the menu will take care of it */
goto Quickie;
}
/* No menu and there's a sequence, launch it */
*LaunchCode = 4;
*Recover = TRUE;
goto Quickie;
}
/* STATUS_FVE_LOCKED_VOLUME -- bitlocker volume is locked */
if (Status == 0xC0210000)
{
/* Launch recovery mode */
*LaunchCode = 4;
*Recover = TRUE;
goto Quickie;
}
/* Was there some other error launching the boot application? */
if (!NT_SUCCESS(Status))
{
/* Raise a fatal error */
BmFatalErrorEx(BL_FATAL_ERROR_APP_LOAD,
(ULONG_PTR)AppPath,
Status,
0,
0);
goto Quickie;
}
/* Zero out the return arguments */
RtlZeroMemory(&ReturnArgs, sizeof(ReturnArgs));
/* Log to ETW this launch */
//BmpLogApplicationLaunchEvent(&BootEntry->Guid, AppPath);
/* Launch the boot application*/
Status = BlImgStartBootApplication(AppHandle, &ReturnArgs);
#if BL_BITLOCKER_SUPPORT
/* Bitlocker stuff */
BlFveSecureBootCheckpointAppReturn(BootEntry, &ReturnArgs);
#endif
/* Log in the boot status log the launch */
//BlBsdLogEntry(1, 0x12, &BootEntry->Guid, 0x14);
/* Unloac the boot application if we've returned */
BlImgUnloadBootApplication(AppHandle);
/* Keep going unless STATUS_RESTART_BOOT_APPLICATION */
} while (Status != 0xC0000453);
/* We've come back. Assume we need to launch the recovery sequence */
*Recover = TRUE;
/* Why did we get back? */
if (ReturnArgs.Flags & 1)
{
/* Flag 1 -- should we display advanced options? */
Status = BlGetBootOptionBoolean(BootEntry->BcdData,
BcdLibraryBoolean_DisplayAdvancedOptions,
&AdvancedOptions);
if ((NT_SUCCESS(Status)) && (AdvancedOptions))
{
/* Yes, so return with code 2 */
*LaunchCode = 2;
}
else
{
/* No, return with code 1 */
*LaunchCode = 1;
}
}
else if (ReturnArgs.Flags & 4)
{
/* Flag 4 -- unknown */
*LaunchCode = 1;
}
else if (ReturnArgs.Flags & 8)
{
/* Flag 5 -- unknown */
*LaunchCode = 5;
}
else if (ReturnArgs.Flags & 0x10)
{
/* Flag 6 -- unknown */
*LaunchCode = 6;
}
else if (ReturnArgs.Flags & 0x20)
{
/* Flag 7 -- unknown */
*LaunchCode = 7;
}
else if (ReturnArgs.Flags & BL_RETURN_ARGUMENTS_NO_PAE_FLAG)
{
/* PAE is not supported -- refuse to boot */
*Recover = FALSE;
BmFatalErrorEx(BL_FATAL_ERROR_NO_PAE, Status, 0, 0, 0);
}
Quickie:
/* All done, did we have an application path? */
if (AppPath)
{
/* Free it */
BlMmFreeHeap(AppPath);
}
/* Back to the caller now */
return Status;
}
NTSTATUS
BmpLaunchBootEntry (
_In_ PBL_LOADED_APPLICATION_ENTRY BootEntry,
_Out_ PULONG EntryIndex,
_In_ ULONG LaunchCode,
_In_ BOOLEAN LaunchWinRe
)
{
HANDLE BcdHandle;
NTSTATUS Status;
GUID ObjectId;
BOOLEAN DoRecovery, AutoRecovery, DoSequence, RestartOnFailure;
ULONG ErrorCode;
BOOLEAN AdvancedOneTime, EditOneTime;
/* Check if this is the OS loader */
if (BootEntry->Flags & BL_APPLICATION_ENTRY_WINLOAD)
{
/* Check if one-time advanced options should be shown */
if (MiscGetBootOption(BootEntry->BcdData,
BcdOSLoaderBoolean_AdvancedOptionsOneTime))
{
/* Open the BCD */
BcdHandle = NULL;
Status = BmOpenDataStore(BcdHandle);
if (NT_SUCCESS(Status))
{
/* Delete the option from the BCD, so it doesn't happen again */
ObjectId = BootEntry->Guid;
BmPurgeOption(BcdHandle,
&ObjectId,
BcdOSLoaderBoolean_AdvancedOptionsOneTime);
BmCloseDataStore(BcdHandle);
}
}
/* Check if one-time options editor should be shown */
if (MiscGetBootOption(BootEntry->BcdData,
BcdOSLoaderBoolean_OptionsEditOneTime))
{
/* Open the BCD */
BcdHandle = NULL;
Status = BmOpenDataStore(BcdHandle);
if (NT_SUCCESS(Status))
{
/* Delete the option from the BCD, so it doesn't happen again */
ObjectId = BootEntry->Guid;
BmPurgeOption(BcdHandle,
&ObjectId,
BcdOSLoaderBoolean_OptionsEditOneTime);
BmCloseDataStore(BcdHandle);
}
}
}
TryAgain:
/* Disable recovery mode */
DoRecovery = FALSE;
/* Store globally which entry we are trying to boot */
BmpSelectedBootEntry = BootEntry;
/* Create any devices that aren't yet fully defined for this boot entry */
Status = BmpCreateDevices(BootEntry);
if (!NT_SUCCESS(Status))
{
/* That failed -- can we launch the recovery environment? */
if (!LaunchWinRe)
{
return Status;
}
/* Yes, so return with the WinRe launch code */
LaunchCode = 2;
goto Quickie;
}
/* Is this an OS loader/ */
if (BootEntry->Flags & BL_APPLICATION_ENTRY_WINLOAD)
{
/* Is the one-time advanced options menu option present? */
Status = BlGetBootOptionBoolean(BootEntry->BcdData,
BcdOSLoaderBoolean_AdvancedOptionsOneTime,
&AdvancedOneTime);
if (NT_SUCCESS(Status))
{
/* Is it turned on? */
if (AdvancedOneTime)
{
/* Set the option this once */
BlAppendBootOptionBoolean(BootEntry,
BcdLibraryBoolean_DisplayAdvancedOptions,
TRUE);
}
else
{
/* It's not, so disable the option if active */
BlRemoveBootOption(BootEntry->BcdData,
BcdLibraryBoolean_DisplayAdvancedOptions);
}
/* Remove the one-time option. We've already purged it earlier */
BlRemoveBootOption(BootEntry->BcdData,
BcdOSLoaderBoolean_AdvancedOptionsOneTime);
}
/* Is the one-time options editor menu option present? */
Status = BlGetBootOptionBoolean(BootEntry->BcdData,
BcdOSLoaderBoolean_OptionsEditOneTime,
&EditOneTime);
if (NT_SUCCESS(Status))
{
/* Is it turned on? */
if (EditOneTime)
{
/* Set the option this once */
BlAppendBootOptionBoolean(BootEntry,
BcdLibraryBoolean_DisplayOptionsEdit,
TRUE);
}
else
{
/* It's not, so disable the option if active */
BlRemoveBootOption(BootEntry->BcdData,
BcdLibraryBoolean_DisplayOptionsEdit);
}
/* Remove the one-time option. We've already purged it earlier */
BlRemoveBootOption(BootEntry->BcdData,
BcdOSLoaderBoolean_OptionsEditOneTime);
}
}
/* BCD handling done, transfer execution to this entry */
Status = BmpTransferExecution(BootEntry, &LaunchCode, &DoRecovery);
if (!LaunchWinRe)
{
return Status;
}
/* Check if boot was successful, or cancelled and we're not doing WinRE */
if (((NT_SUCCESS(Status)) || (Status == STATUS_CANCELLED)) && !(DoRecovery))
{
return Status;
}
/* Boot failed -- are we doing recovery? */
if (!DoRecovery)
{
/* Nope, bail out */
LaunchCode = 2;
goto Quickie;
}
Quickie:
/* Get the recovery sequence */
if (MiscGetBootOption(BootEntry->BcdData, BcdLibraryObjectList_RecoverySequence))
{
/* Check if the launch depends on auto-recovery being enabled or not */
if ((LaunchCode == 3) || (LaunchCode == 5) || (LaunchCode == 6))
{
Status = BlGetBootOptionBoolean(BootEntry->BcdData,
BcdLibraryBoolean_AutoRecoveryEnabled,
&AutoRecovery);
if (NT_SUCCESS(Status))
{
/* Override the setting */
DoRecovery = AutoRecovery;
}
}
}
else
{
/* There's no recovery setting */
DoRecovery = FALSE;
}
/* Check if we should restart on failure */
RestartOnFailure = FALSE;
BlGetBootOptionBoolean(BlpApplicationEntry.BcdData,
BcdLibraryBoolean_RestartOnFailure,
&RestartOnFailure);
/* Do the sequence if recovery is on, unless we should restart instead */
DoSequence = RestartOnFailure ? FALSE : DoRecovery;
while (1)
{
/* Are we doing the recovery sequence? */
if (DoSequence)
{
/* Because of automatic recovery? */
if (AutoRecovery)
{
#if BL_BITLOCKER_SUPPORT
/* Do bitlocker stuff */
BlFveRegisterBootEntryForTrustedWimBoot(BootEntry, TRUE);
#endif
}
/* Launch the recovery sequence*/
Status = BmLaunchRecoverySequence(BootEntry, LaunchCode);
/* Was it launched automatically? */
if (AutoRecovery)
{
#if BL_BITLOCKER_SUPPORT
/* Do bitlocker stuff */
BlFveRegisterBootEntryForTrustedWimBoot(BootEntry, FALSE);
#endif
/* No need to do this again */
AutoRecovery = FALSE;
}
/* Did the recovery sequence work? */
if (NT_SUCCESS(Status))
{
/* All good */
return STATUS_SUCCESS;
}
/* Remove the sequence, don't do it again */
BlRemoveBootOption(BootEntry->BcdData, BcdLibraryObjectList_RecoverySequence);
}
/* Recovery sequence also failed, show fatal error */
if (!BmpInternalBootError)
{
BmFatalErrorEx(BL_FATAL_ERROR_GENERIC, Status, 0, 0, 0);
}
/* Display the error menu */
ErrorCode = BmDisplayDumpError(BootEntry, LaunchCode);
BmErrorPurge();
/* See what the user wants to do */
switch (ErrorCode)
{
case TryAgain:
/* Try again */
goto TryAgain;
case NextOs:
/* Boot the next entry*/
break;
case OsSelection:
/* Cancel the boot*/
return STATUS_CANCELLED;
case RecoverOem:
/* Custom OEM recovery -- open the BCD */
Status = BmOpenDataStore(BcdHandle);
if (NT_SUCCESS(Status))
{
/* See what the custom sequence is */
Status = BmProcessCustomAction(BcdHandle, NULL);
}
/* All done, close the BCD */
if (BcdHandle)
{
BmCloseDataStore(BcdHandle);
}
return Status;
case AdvancedOptions:
/* Show the advanced options next iteration */
BlAppendBootOptionBoolean(BootEntry,
BcdOSLoaderBoolean_AdvancedOptionsOneTime,
TRUE);
goto TryAgain;
case BootOptions:
/* Show the options editor next iteration */
BlAppendBootOptionBoolean(BootEntry,
BcdOSLoaderBoolean_OptionsEditOneTime,
TRUE);
goto TryAgain;
case Recover:
/* Try the recovery sequence next time*/
DoSequence = TRUE;
LaunchCode = 1;
goto TryAgain;
default:
/* Something unknown */
return STATUS_CANCELLED;
}
}
/* We are booting the next OS, so return success as to not kill the boot */
return STATUS_SUCCESS;
}
/*++
* @name BmMain
*
* The BmMain function implements the Windows Boot Application entrypoint for
* the Boot Manager.
*
* @param BootParameters
* Pointer to the Boot Application Parameter Block.
*
* @return NT_SUCCESS if the image was loaded correctly, relevant error code
* otherwise.
*
*--*/
NTSTATUS
NTAPI
BmMain (
_In_ PBOOT_APPLICATION_PARAMETER_BLOCK BootParameters
)
{
NTSTATUS Status, LibraryStatus;
BL_LIBRARY_PARAMETERS LibraryParameters;
PBL_RETURN_ARGUMENTS ReturnArguments;
PGUID AppIdentifier;
HANDLE BcdHandle, ResumeBcdHandle;
PBL_BCD_OPTION EarlyOptions;
PWCHAR Stylesheet;
BOOLEAN XmlLoaded, DisableIntegrity, TestSigning, PersistBootSequence;
BOOLEAN RebootOnError, CustomActions;
ULONG SequenceId;
PBL_LOADED_APPLICATION_ENTRY BootEntry;
PGUID SequenceList;
ULONG SequenceListCount;
PBL_LOADED_APPLICATION_ENTRY* BootSequence;
ULONG BootIndex;
BOOLEAN ExitBootManager;
BOOLEAN BootFailed;
BOOLEAN BootOk;
ULONG SequenceCount;
BOOLEAN GetEntry;
EfiPrintf(L"ReactOS UEFI Boot Manager Initializing...\r\n");
/* Reading the BCD can change this later on */
RebootOnError = FALSE;
/* Save the start/end-of-POST time */
#if defined(_M_IX86) || defined(_M_X64)
ApplicationStartTime = __rdtsc();
#else
EfiPrintf(L"No time source defined for this platform\r\n");
ApplicationStartTime = 0;
#endif
PostTime = ApplicationStartTime;
/* Setup the boot library parameters for this application */
BlSetupDefaultParameters(&LibraryParameters);
LibraryParameters.TranslationType = BlNone;
LibraryParameters.LibraryFlags = 0x400 | 0x8;
LibraryParameters.MinimumAllocationCount = 16;
LibraryParameters.MinimumHeapSize = 512 * 1024;
/* Initialize the boot library */
Status = BlInitializeLibrary(BootParameters, &LibraryParameters);
if (!NT_SUCCESS(Status))
{
/* Check for failure due to invalid application entry */
if (Status != STATUS_INVALID_PARAMETER_9)
{
/* Specifically print out what happened */
EfiPrintf(L"BlInitializeLibrary failed 0x%x\r\n", Status);
}
/* Go to exit path */
goto Quickie;
}
/* Get the application identifier */
AppIdentifier = BlGetApplicationIdentifier();
if (!AppIdentifier)
{
/* None was given, so set our default one */
AppIdentifier = (PGUID)&GUID_WINDOWS_BOOTMGR;
}
/* Save our identifier */
BmApplicationIdentifier = *AppIdentifier;
/* Initialize the file system to open a handle to our root boot directory */
BmFwInitializeBootDirectoryPath();
/* Load and initialize the boot configuration database (BCD) */
Status = BmOpenDataStore(&BcdHandle);
if (NT_SUCCESS(Status))
{
/* Copy the boot options */
Status = BlCopyBootOptions(BlpApplicationEntry.BcdData, &EarlyOptions);
if (NT_SUCCESS(Status))
{
/* Update them */
Status = BmpUpdateApplicationOptions(BcdHandle);
if (!NT_SUCCESS(Status))
{
/* Log a fatal error */
BmFatalErrorEx(BL_FATAL_ERROR_BCD_PARSE,
(ULONG_PTR)L"\\BCD",
Status,
0,
0);
}
}
}
#ifdef _SECURE_BOOT
/* Initialize the secure boot machine policy */
Status = BmSecureBootInitializeMachinePolicy();
if (!NT_SUCCESS(Status))
{
BmFatalErrorEx(BL_FATAL_ERROR_SECURE_BOOT, Status, 0, 0, 0);
}
#endif
/* Copy the library parameters and add the re-initialization flag */
RtlCopyMemory(&LibraryParameters,
&BlpLibraryParameters,
sizeof(LibraryParameters));
LibraryParameters.LibraryFlags |= (BL_LIBRARY_FLAG_REINITIALIZE_ALL |
BL_LIBRARY_FLAG_REINITIALIZE);
/* Now that we've parsed the BCD, re-initialize the library */
LibraryStatus = BlInitializeLibrary(BootParameters, &LibraryParameters);
if (!NT_SUCCESS(LibraryStatus) && (NT_SUCCESS(Status)))
{
Status = LibraryStatus;
}
/* Initialize firmware-specific memory regions */
BmFwMemoryInitialize();
/* Initialize the boot status data log (BSD) */
BmpInitializeBootStatusDataLog();
/* Find our XSL stylesheet */
Stylesheet = BlResourceFindHtml();
if (!Stylesheet)
{
/* Awe, no XML. This is actually fatal lol. Can't boot without XML. */
Status = STATUS_NOT_FOUND;
EfiPrintf(L"BlResourceFindMessage failed 0x%x\r\n", STATUS_NOT_FOUND);
goto Quickie;
}
/* Initialize the XML Engine (as a side-effect, resets cursor) */
Status = BlXmiInitialize(Stylesheet);
if (!NT_SUCCESS(Status))
{
EfiPrintf(L"\r\nBlXmiInitialize failed 0x%x\r\n", Status);
goto Failure;
}
XmlLoaded = TRUE;
/* Check if there's an active bitmap visible */
if (!BlDisplayValidOemBitmap())
{
/* Nope, make the screen black using BGFX */
if (!NT_SUCCESS(BmpBgDisplayClearScreen(0xFF000000)))
{
/* BGFX isn't active, use standard display */
BlDisplayClearScreen();
}
}
#ifdef _BIT_LOCKER_
/* Bitlocker will take over screen UI if enabled */
FveDisplayScreen = BmFveDisplayScreen;
#endif
/* Check if any bypass options are enabled */
BlImgQueryCodeIntegrityBootOptions(&BlpApplicationEntry,
&DisableIntegrity,
&TestSigning);
if (!DisableIntegrity)
{
/* Integrity checks are enabled, so validate our signature */
Status = BmFwVerifySelfIntegrity();
if (!NT_SUCCESS(Status))
{
/* Signature invalid, fail boot */
goto Failure;
}
}
/* TEST MODE */
EfiPrintf(L"Performing memory allocator tests...\r\n");
{
NTSTATUS Status;
PHYSICAL_ADDRESS PhysicalAddress, PhysicalAddress2;
/* Allocate 1 physical page */
PhysicalAddress.QuadPart = 0;
Status = BlMmAllocatePhysicalPages(&PhysicalAddress, BlLoaderData, 1, 0, 1);
if (Status != STATUS_SUCCESS)
{
EfiPrintf(L"FAIL: Allocation status: %lx at address: %llx\r\n", Status, PhysicalAddress.QuadPart);
EfiStall(100000000);
}
/* Write some data */
*(PULONG)((ULONG_PTR)PhysicalAddress.QuadPart) = 0x55555151;
/* Free it */
Status = BlMmFreePhysicalPages(PhysicalAddress);
if (Status != STATUS_SUCCESS)
{
EfiPrintf(L"FAIL: Memory free status: %lx\r\n", Status);
EfiStall(100000000);
}
/* Allocate a page again */
PhysicalAddress2.QuadPart = 0;
Status = BlMmAllocatePhysicalPages(&PhysicalAddress2, BlLoaderData, 1, 0, 1);
if (Status != STATUS_SUCCESS)
{
EfiPrintf(L"FAIL: Allocation status: %lx at address: %llx\r\n", Status, PhysicalAddress2.QuadPart);
EfiStall(100000000);
}
/* It should've given us the same page, since we freed it */
if (PhysicalAddress.QuadPart != PhysicalAddress2.QuadPart)
{
EfiPrintf(L"FAIL: Non-matching addresses: %llx %llx\r\n", PhysicalAddress.QuadPart, PhysicalAddress2.QuadPart);
EfiStall(100000000);
}
/* The data should still be there, since zero-ing is not on for bootmgr */
if (*(PULONG)((ULONG_PTR)PhysicalAddress2.QuadPart) != 0x55555151)
{
EfiPrintf(L"FAIL: Non-matching data: %lx %lx\r\n", 0x55555151, *(PULONG)((ULONG_PTR)PhysicalAddress2.QuadPart));
EfiStall(100000000);
}
/* And free the second page again */
Status = BlMmFreePhysicalPages(PhysicalAddress);
if (Status != STATUS_SUCCESS)
{
EfiPrintf(L"FAIL: Memory free status: %lx\r\n", Status);
EfiStall(100000000);
}
}
/* Write out the first XML tag */
BlXmiWrite(L"<bootmgr/>");
/* Check for factory reset */
BlSecureBootCheckForFactoryReset();
/* Load the revocation list */
Status = BmFwRegisterRevocationList();
if (!NT_SUCCESS(Status))
{
goto Failure;
}
/* Register our custom progress routine */
BlUtlRegisterProgressRoutine();
/* Display state is not currently cached */
BmDisplayStateCached = FALSE;
/* Check if we need to resume from hibernate */
Status = BmResumeFromHibernate(&ResumeBcdHandle);
if (!NT_SUCCESS(Status))
{
goto Failure;
}
#ifdef BL_NET_SUPPORT
/* Register multicast printing routine */
BlUtlRegisterMulticastRoutine();
#endif
/* Check if restart on failure is enabled */
BlGetBootOptionBoolean(BlpApplicationEntry.BcdData,
BcdLibraryBoolean_RestartOnFailure,
&RebootOnError);
/* Check if the boot sequence is persisted */
Status = BlGetBootOptionBoolean(BlpApplicationEntry.BcdData,
BcdBootMgrBoolean_PersistBootSequence,
&PersistBootSequence);
if (!NT_SUCCESS(Status))
{
/* It usually is */
PersistBootSequence = TRUE;
}
/* Check if there's custom actions to take */
Status = BlGetBootOptionBoolean(BlpApplicationEntry.BcdData,
BcdBootMgrBoolean_ProcessCustomActionsFirst,
&CustomActions);
if ((NT_SUCCESS(Status)) && (CustomActions))
{
/* We don't support this yet */
EfiPrintf(L"Not implemented\r\n");
Status = STATUS_NOT_IMPLEMENTED;
goto Failure;
}
//BlResourceFindMessage(BM_MSG_TEST);
/* At last, enter the boot selection stage */
SequenceId = 0;
GetEntry = FALSE;
BootFailed = FALSE;
SequenceList = NULL;
BootSequence = NULL;
SequenceCount = 0;
while (1)
{
/* We don't have a boot entry nor a sequence ID */
BootEntry = NULL;
BootOk = FALSE;
/* Do we have a hardcoded boot sequence set? */
if (!(BootSequence) && !(GetEntry))
{
/* Not yet, read the BCD to see if one is there */
Status = BlGetBootOptionGuidList(BlpApplicationEntry.BcdData,
BcdBootMgrObjectList_BootSequence,
&SequenceList,
&SequenceListCount);
if (NT_SUCCESS(Status))
{
/* A GUID list for the boot sequence is set. Extract it */
Status = BmGetBootSequence(BcdHandle,
SequenceList,
SequenceListCount,
BL_APPLICATION_ENTRY_FIXED_SEQUENCE,
&BootSequence,
&SequenceCount);
if (NT_SUCCESS(Status))
{
/* Don't get stuck in a loop repeating this sequence */
BlRemoveBootOption(BlpApplicationEntry.BcdData,
BcdBootMgrObjectList_BootSequence);
/* But do check if we should persist it */
if (PersistBootSequence)
{
/* Yes -- so go select an entry now */
GetEntry = TRUE;
}
else
{
/* We shouldn't, so wipe it from the BCD too */
Status = BmPurgeOption(BcdHandle,
&BmApplicationIdentifier,
BcdBootMgrObjectList_BootSequence);
if (!NT_SUCCESS(Status))
{
/* Well that failed */
goto LoopQuickie;
}
}
}
}
else
{
/* No boot entry sequence for us */
BootSequence = NULL;
}
}
/* Do we have a sequence active, and are we still processing it? */
if ((BootSequence) && ((GetEntry) || (SequenceId < SequenceCount)))
{
/* Extract the next entry in the sequence */
BootEntry = BootSequence[SequenceId];
BootSequence[SequenceId] = NULL;
/* Move to the next entry for next time */
SequenceId++;
/* Unless there won't be a a next time? */
if (SequenceId == SequenceCount)
{
/* Clean up, it's the last entry */
BlMmFreeHeap(BootSequence);
BootSequence = NULL;
}
}
else
{
/* Get the selected boot entry from the user */
ExitBootManager = FALSE;
Status = BmpGetSelectedBootEntry(BcdHandle,
&BootEntry,
&BootIndex,
&ExitBootManager);
if (!(NT_SUCCESS(Status)) || (ExitBootManager))
{
/* Selection failed, or user wants to exit */
goto LoopQuickie;
}
}
/* Did we have a BCD open? */
if (BcdHandle)
{
/* Close it, we'll be opening a new one */
BmCloseDataStore(BcdHandle);
BcdHandle = NULL;
}
/* Launch the selected entry */
Status = BmpLaunchBootEntry(BootEntry, &BootIndex, 0, TRUE);
if (NT_SUCCESS(Status))
{
/* Boot worked, uncache display and process the bad memory list */
BmDisplayStateCached = FALSE;
BmpProcessBadMemory();
}
else
{
/* Boot failed -- was it user driven? */
if (Status != STATUS_CANCELLED)
{
/* Nope, remember that booting failed */
BootFailed = TRUE;
goto LoopQuickie;
}
/* Yes -- the display is still valid */
BmDisplayStateCached = TRUE;
}
/* Reopen the BCD */
Status = BmOpenDataStore(&BcdHandle);
if (!NT_SUCCESS(Status))
{
break;
}
/* Put the BCD options back into our entry */
BlReplaceBootOptions(&BlpApplicationEntry, EarlyOptions);
/* Update our options one more time */
Status = BmpUpdateApplicationOptions(BcdHandle);
if (NT_SUCCESS(Status))
{
/* Boot was 100% OK */
BootOk = TRUE;
}
LoopQuickie:
/* Did we have a boot entry? */
if (BootEntry)
{
/* We can destroy it now */
BlDestroyBootEntry(BootEntry);
}
/* Is this the success path? */
if (NT_SUCCESS(Status))
{
/* Did we actually boot something? */
if (!BootOk)
{
/* Bope, fail out */
break;
}
}
/* This is the failure path... should we reboot? */
if (RebootOnError)
{
break;
}
};
Failure:
if (!BootFailed)
{
/* Check if we got here due to an internal error */
if (BmpInternalBootError)
{
/* If XML is available, display the error */
if (XmlLoaded)
{
//BmDisplayDumpError(0, 0);
//BmErrorPurge();
}
/* Don't do a fatal error -- return back to firmware */
goto Quickie;
}
}
/* Log a general fatal error once we're here */
BmFatalErrorEx(BL_FATAL_ERROR_GENERIC, Status, 0, 0, 0);
Quickie:
/* Check if we should reboot */
if ((RebootOnError) ||
(BlpApplicationEntry.Flags & BL_APPLICATION_ENTRY_REBOOT_ON_ERROR))
{
/* Reboot the box */
BlFwReboot();
Status = STATUS_SUCCESS;
}
else
{
/* Return back to the caller with the error argument encoded */
ReturnArguments = (PVOID)((ULONG_PTR)BootParameters + BootParameters->ReturnArgumentsOffset);
ReturnArguments->Version = BL_RETURN_ARGUMENTS_VERSION;
ReturnArguments->Status = Status;
/* Tear down the boot library */
BlDestroyLibrary();
}
/* Return back status */
return Status;
}