mirror of
https://github.com/reactos/reactos.git
synced 2024-10-31 20:02:55 +00:00
3245 lines
93 KiB
C
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 STATUS_FVE_LOCKED_VOLUME:
|
|
*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 == STATUS_FVE_LOCKED_VOLUME)
|
|
{
|
|
/* 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;
|
|
}
|
|
|