/* * 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""); /* 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; }