reactos/boot/environ/lib/misc/image.c
2018-03-18 16:10:41 +01:00

2075 lines
61 KiB
C

/*
* COPYRIGHT: See COPYING.ARM in the top level directory
* PROJECT: ReactOS UEFI Boot Library
* FILE: boot/environ/lib/misc/image.c
* PURPOSE: Boot Library Image Routines
* PROGRAMMER: Alex Ionescu (alex.ionescu@reactos.org)
*/
/* INCLUDES ******************************************************************/
#include "bl.h"
#include <bcd.h>
/* DATA VARIABLES ************************************************************/
ULONG IapAllocatedTableEntries;
ULONG IapTableEntries;
PVOID* IapImageTable;
#ifndef _M_ARM
KDESCRIPTOR GdtRegister;
KDESCRIPTOR IdtRegister;
KDESCRIPTOR BootAppGdtRegister;
KDESCRIPTOR BootAppIdtRegister;
PVOID BootApp32EntryRoutine;
PBOOT_APPLICATION_PARAMETER_BLOCK BootApp32Parameters;
PVOID BootApp32Stack;
#endif
/* FUNCTIONS *****************************************************************/
NTSTATUS
ImgpGetFileSize (
_In_ PBL_IMG_FILE File,
_Out_ PULONG FileSize
)
{
NTSTATUS Status;
ULONG Size;
BL_FILE_INFORMATION FileInformation;
/* Check if the file was memory mapped */
if (File->Flags & BL_IMG_MEMORY_FILE)
{
/* Just read the size of the mapping */
Size = File->FileSize;
}
else
{
/* Do file I/O to get the file size */
Status = BlFileGetInformation(File->FileId,
&FileInformation);
if (!NT_SUCCESS(Status))
{
return Status;
}
/* We only support files less than 4GB in the Image Mapped */
Size = FileInformation.Size;
if (FileInformation.Size > ULONG_MAX)
{
return STATUS_NOT_SUPPORTED;
}
}
/* Return the size and success */
*FileSize = Size;
return STATUS_SUCCESS;
}
NTSTATUS
ImgpReadAtFileOffset (
_In_ PBL_IMG_FILE File,
_In_ ULONG Size,
_In_ ULONGLONG ByteOffset,
_In_ PVOID Buffer,
_Out_ PULONG BytesReturned
)
{
NTSTATUS Status;
/* Check what if this is a mapped file or not */
if (File->Flags & BL_IMG_MEMORY_FILE)
{
/* Check if the boundaries are within the file size */
if ((ByteOffset + Size) <= File->FileSize)
{
/* Yep, copy into the caller-supplied buffer */
RtlCopyMemory(Buffer,
(PVOID)((ULONG_PTR)File->BaseAddress + (ULONG_PTR)ByteOffset),
Size);
/* If caller wanted to know, return the size copied */
if (BytesReturned)
{
*BytesReturned = Size;
}
/* All good */
Status = STATUS_SUCCESS;
}
else
{
/* Doesn't fit */
Status = STATUS_INVALID_PARAMETER;
}
}
else
{
/* Issue the file I/O instead */
Status = BlFileReadAtOffsetEx(File->FileId,
Size,
ByteOffset,
Buffer,
BytesReturned,
0);
}
/* Return the final status */
return Status;
}
NTSTATUS
ImgpOpenFile (
_In_ ULONG DeviceId,
_In_ PWCHAR FileName,
_In_ ULONG Flags,
_Out_ PBL_IMG_FILE NewFile
)
{
NTSTATUS Status;
ULONG FileSize;
ULONGLONG RemoteFileSize;
PVOID RemoteFileAddress;
ULONG FileId;
/* First, try to see if BD has this file remotely */
Status = BlBdPullRemoteFile(FileName,
&RemoteFileAddress,
&RemoteFileSize);
if (NT_SUCCESS(Status))
{
/* Yep, get the file size and make sure it's < 4GB */
FileSize = RemoteFileSize;
if (RemoteFileSize <= ULONG_MAX)
{
/* Remember this is a memory mapped remote file */
NewFile->Flags |= (BL_IMG_MEMORY_FILE | BL_IMG_REMOTE_FILE);
NewFile->FileSize = FileSize;
NewFile->BaseAddress = RemoteFileAddress;
goto Quickie;
}
}
/* Use File I/O instead */
Status = BlFileOpen(DeviceId,
FileName,
BL_FILE_READ_ACCESS,
&FileId);
if (!NT_SUCCESS(Status))
{
/* Bail out on failure */
return Status;
}
/* Make sure nobody thinks this is a memory file */
NewFile->Flags &= ~BL_IMG_MEMORY_FILE;
NewFile->FileId = FileId;
Quickie:
/* Set common data for both memory and I/O based file */
NewFile->Flags |= BL_IMG_VALID_FILE;
NewFile->FileName = FileName;
return Status;
}
NTSTATUS
ImgpCloseFile (
_In_ PBL_IMG_FILE File
)
{
NTSTATUS Status;
/* Make sure this is a valid file, otherwise no-op */
Status = STATUS_SUCCESS;
if (File->Flags & BL_IMG_VALID_FILE)
{
/* Is this a memory mapped file? */
if (!(File->Flags & BL_IMG_MEMORY_FILE))
{
/* Nope, close the file handle */
return BlFileClose(File->FileId);
}
/* Is this a remote file? */
if (File->Flags & BL_IMG_REMOTE_FILE)
{
/* Then only free the memory in that scenario */
return MmPapFreePages(File->BaseAddress, BL_MM_INCLUDE_MAPPED_ALLOCATED);
}
}
/* Return the final status */
return Status;
}
NTSTATUS
BlImgUnallocateImageBuffer (
_In_ PVOID ImageBase,
_In_ ULONG ImageSize,
_In_ ULONG ImageFlags
)
{
PHYSICAL_ADDRESS PhysicalAddress;
NTSTATUS Status;
/* Make sure required parameters are present */
if (!(ImageBase) || !(ImageSize))
{
return STATUS_INVALID_PARAMETER;
}
/* Check if this was a physical allocation */
if (!(ImageFlags & BL_LOAD_IMG_VIRTUAL_BUFFER))
{
return MmPapFreePages(ImageBase, BL_MM_INCLUDE_MAPPED_ALLOCATED);
}
/* It's virtual, so translate it first */
if (!BlMmTranslateVirtualAddress(ImageBase, &PhysicalAddress))
{
return STATUS_INVALID_PARAMETER;
}
/* Unmap the virtual mapping */
Status = BlMmUnmapVirtualAddressEx(ImageBase, ROUND_TO_PAGES(ImageSize));
if (NT_SUCCESS(Status))
{
/* Now free the physical pages */
Status = BlMmFreePhysicalPages(PhysicalAddress);
}
/* All done */
return Status;
}
NTSTATUS
BlImgAllocateImageBuffer (
_Inout_ PVOID* ImageBuffer,
_In_ ULONG MemoryType,
_In_ ULONGLONG ImageSize,
_In_ ULONG Flags
)
{
ULONG Attributes;
ULONGLONG Pages, Size;
PVOID MappedBase, CurrentBuffer;
NTSTATUS Status;
PHYSICAL_ADDRESS PhysicalAddress;
/* Read and reset the current buffer address */
CurrentBuffer = *ImageBuffer;
*ImageBuffer = NULL;
/* Align the image size to page */
Size = ROUND_TO_PAGES(ImageSize);
/* Not sure what this attribute does yet */
Attributes = 0;
if (Flags & BL_LOAD_IMG_UNKNOWN_BUFFER_FLAG)
{
Attributes = 0x10000;
}
/* Check if the caller wants a virtual buffer */
if (Flags & BL_LOAD_IMG_VIRTUAL_BUFFER)
{
/* Set the physical address to the current buffer */
PhysicalAddress.QuadPart = (ULONG_PTR)CurrentBuffer;
Pages = Size >> PAGE_SHIFT;
/* Allocate the physical pages */
Status = BlMmAllocatePhysicalPages(&PhysicalAddress,
Pages,
MemoryType,
Attributes,
0);
if (!NT_SUCCESS(Status))
{
/* If that failed, remove allocation attributes */
PhysicalAddress.QuadPart = 0;
Attributes &= ~BlMemoryValidAllocationAttributeMask,
Status = BlMmAllocatePhysicalPages(&PhysicalAddress,
Pages,
MemoryType,
Attributes,
0);
}
/* Check if either attempts succeeded */
if (!NT_SUCCESS(Status))
{
return Status;
}
/* Now map the physical buffer at the address requested */
MappedBase = PhysicalAddressToPtr(PhysicalAddress);
Status = BlMmMapPhysicalAddressEx(&MappedBase,
BlMemoryFixed,
Size,
PhysicalAddress);
if (!NT_SUCCESS(Status))
{
/* Free on failure if needed */
BlMmFreePhysicalPages(PhysicalAddress);
return Status;
}
}
else
{
/* Otherwise, allocate raw physical pages */
MappedBase = CurrentBuffer;
Pages = Size >> PAGE_SHIFT;
Status = MmPapAllocatePagesInRange(&MappedBase,
MemoryType,
Pages,
Attributes,
0,
NULL,
0);
if (!NT_SUCCESS(Status))
{
/* If that failed, try without allocation attributes */
MappedBase = NULL;
Attributes &= ~BlMemoryValidAllocationAttributeMask,
Status = MmPapAllocatePagesInRange(&MappedBase,
MemoryType,
Pages,
Attributes,
0,
NULL,
0);
}
/* Check if either attempts succeeded */
if (!NT_SUCCESS(Status))
{
return Status;
}
}
/* Success path, returned allocated address */
*ImageBuffer = MappedBase;
return STATUS_SUCCESS;
}
NTSTATUS
BlImgLoadImageWithProgress2 (
_In_ ULONG DeviceId,
_In_ BL_MEMORY_TYPE MemoryType,
_In_ PWCHAR FileName,
_Inout_ PVOID* MappedBase,
_Inout_ PULONG MappedSize,
_In_ ULONG ImageFlags,
_In_ BOOLEAN ShowProgress,
_Out_opt_ PUCHAR* HashBuffer,
_Out_opt_ PULONG HashSize
)
{
NTSTATUS Status;
PVOID BaseAddress, Buffer;
ULONG RemainingLength, CurrentSize, ImageSize, ReadSize;
BOOLEAN ComputeSignature, ComputeHash, Completed;
BL_IMG_FILE FileHandle;
ULONGLONG ByteOffset;
PHYSICAL_ADDRESS PhysicalAddress;
/* Initialize variables */
BaseAddress = 0;
ImageSize = 0;
Completed = FALSE;
RtlZeroMemory(&FileHandle, sizeof(FileHandle));
/* Check for missing parameters */
if (!MappedBase)
{
Status = STATUS_INVALID_PARAMETER;
goto Quickie;
}
if (!FileName)
{
Status = STATUS_INVALID_PARAMETER;
goto Quickie;
}
if (!MappedSize)
{
Status = STATUS_INVALID_PARAMETER;
goto Quickie;
}
/* Check if the image buffer is being provided */
if (ImageFlags & BL_LOAD_IMG_EXISTING_BUFFER)
{
/* An existing base must already exist */
if (!(*MappedBase))
{
Status = STATUS_INVALID_PARAMETER;
goto Quickie;
}
}
/* Check of a hash is being requested */
if (ImageFlags & BL_LOAD_IMG_COMPUTE_HASH)
{
/* Make sure we can return the hash */
if (!HashBuffer)
{
Status = STATUS_INVALID_PARAMETER;
goto Quickie;
}
if (!HashSize)
{
Status = STATUS_INVALID_PARAMETER;
goto Quickie;
}
}
/* Check for invalid combination of parameters */
if ((ImageFlags & BL_LOAD_IMG_COMPUTE_HASH) && (ImageFlags & 0x270))
{
Status = STATUS_INVALID_PARAMETER;
goto Quickie;
}
/* Initialize hash if requested by caller */
if (HashBuffer)
{
*HashBuffer = 0;
}
/* Do the same for the hash size */
if (HashSize)
{
*HashSize = 0;
}
/* Open the image file */
Status = ImgpOpenFile(DeviceId, FileName, DeviceId, &FileHandle);
if (!NT_SUCCESS(Status))
{
EfiPrintf(L"Error opening file: %lx\r\n", Status);
goto Quickie;
}
/* Get the size of the image */
Status = ImgpGetFileSize(&FileHandle, &ImageSize);
if (!NT_SUCCESS(Status))
{
EfiPrintf(L"Error getting file size: %lx\r\n", Status);
goto Quickie;
}
/* Read the current base address */
BaseAddress = *MappedBase;
if (ImageFlags & BL_LOAD_IMG_EXISTING_BUFFER)
{
/* Check if the current buffer is too small */
if (*MappedSize < ImageSize)
{
/* Return the required size of the buffer */
*MappedSize = ImageSize;
Status = STATUS_BUFFER_TOO_SMALL;
}
}
else
{
/* A buffer was not provided, allocate one ourselves */
Status = BlImgAllocateImageBuffer(&BaseAddress,
MemoryType,
ImageSize,
ImageFlags);
}
/* Bail out if allocation failed */
if (!NT_SUCCESS(Status))
{
goto Quickie;
}
/* Set the initial byte offset and length to read */
RemainingLength = ImageSize;
ByteOffset = 0;
Buffer = BaseAddress;
/* Update the initial progress */
Completed = FALSE;
if (ShowProgress)
{
BlUtlUpdateProgress(0, &Completed);
ShowProgress &= (Completed != 0) - 1;
}
/* Set the chunk size for each read */
ReadSize = 0x100000;
if (ReadSize > ImageSize)
{
ReadSize = ImageSize;
}
/* Check if we should compute hash and/or signatures */
ComputeSignature = ImageFlags & BL_LOAD_IMG_COMPUTE_SIGNATURE;
ComputeHash = FALSE;
if ((ComputeSignature) || (ImageFlags & BL_LOAD_IMG_COMPUTE_HASH))
{
ComputeHash = TRUE;
// todo: crypto is hard
}
/* Begin the read loop */
while (RemainingLength)
{
/* Check if we've got more than a chunk left to read */
if (RemainingLength > ReadSize)
{
/* Read a chunk*/
CurrentSize = ReadSize;
}
else
{
/* Read only what's left */
CurrentSize = RemainingLength;
}
/* Read the chunk */
Status = ImgpReadAtFileOffset(&FileHandle,
CurrentSize,
ByteOffset,
Buffer,
0);
if (!NT_SUCCESS(Status))
{
goto Quickie;
}
/* Check if we need to compute the hash of this chunk */
if (ComputeHash)
{
// todo: crypto is hard
}
/* Update our position and read information */
Buffer = (PVOID)((ULONG_PTR)Buffer + CurrentSize);
RemainingLength -= CurrentSize;
ByteOffset += CurrentSize;
/* Check if we should update the progress bar */
if (ShowProgress)
{
/* Compute new percentage completed, check if we're done */
BlUtlUpdateProgress(100 - 100 * RemainingLength / ImageSize,
&Completed);
ShowProgress &= (Completed != 0) - 1;
}
}
/* Is the read fully complete? We need to finalize the hash if requested */
if (ComputeHash)
{
// todo: CRYPTO IS HARD
}
/* Success path, return back the buffer and the size of the image */
*MappedBase = BaseAddress;
*MappedSize = ImageSize;
Quickie:
/* Close the file handle */
ImgpCloseFile(&FileHandle);
/* Check if we failed and had allocated a buffer */
if (!(NT_SUCCESS(Status)) &&
(BaseAddress) &&
!(ImageFlags & BL_LOAD_IMG_EXISTING_BUFFER))
{
/* Check what kind of buffer we had allocated */
if (ImageFlags & BL_LOAD_IMG_VIRTUAL_BUFFER)
{
/* Unmap and free the virtual buffer */
PhysicalAddress.QuadPart = (ULONG_PTR)BaseAddress;
BlMmUnmapVirtualAddressEx(BaseAddress, ImageSize);
BlMmFreePhysicalPages(PhysicalAddress);
}
else
{
/* Free the physical buffer */
MmPapFreePages(BaseAddress, BL_MM_INCLUDE_MAPPED_ALLOCATED);
}
}
/* If we hadn't gotten to 100% yet, do it now */
if (ShowProgress)
{
BlUtlUpdateProgress(100, &Completed);
}
/* Return the final status */
return Status;
}
PIMAGE_SECTION_HEADER
BlImgFindSection (
_In_ PVOID ImageBase,
_In_ ULONG ImageSize
)
{
PIMAGE_SECTION_HEADER FoundSection;
ULONG i;
PIMAGE_SECTION_HEADER SectionHeader;
PIMAGE_NT_HEADERS NtHeader;
NTSTATUS Status;
/* Assume failure */
FoundSection = NULL;
/* Make sure the image is valid */
Status = RtlImageNtHeaderEx(0, ImageBase, ImageSize, &NtHeader);
if (NT_SUCCESS(Status))
{
/* Get the first section and loop through them all */
SectionHeader = IMAGE_FIRST_SECTION(NtHeader);
for (i = 0; i < NtHeader->FileHeader.NumberOfSections; i++)
{
/* Check if this is the resource section */
if (!_stricmp((PCCH)SectionHeader->Name, ".rsrc"))
{
/* Yep, we're done */
FoundSection = SectionHeader;
break;
}
/* Nope, keep going */
SectionHeader++;
}
}
/* Return the matching section */
return FoundSection;
}
VOID
BlImgQueryCodeIntegrityBootOptions (
_In_ PBL_LOADED_APPLICATION_ENTRY ApplicationEntry,
_Out_ PBOOLEAN IntegrityChecksDisabled,
_Out_ PBOOLEAN TestSigning
)
{
NTSTATUS Status;
BOOLEAN Value;
/* Check if /DISABLEINTEGRITYCHECKS is on */
Status = BlGetBootOptionBoolean(ApplicationEntry->BcdData,
BcdLibraryBoolean_DisableIntegrityChecks,
&Value);
*IntegrityChecksDisabled = NT_SUCCESS(Status) && (Value);
/* Check if /TESTSIGNING is on */
Status = BlGetBootOptionBoolean(ApplicationEntry->BcdData,
BcdLibraryBoolean_AllowPrereleaseSignatures,
&Value);
*TestSigning = NT_SUCCESS(Status) && (Value);
}
NTSTATUS
BlImgUnLoadImage (
_In_ PVOID ImageBase,
_In_ ULONG ImageSize,
_In_ ULONG ImageFlags
)
{
/* Check for missing parameters */
if (!(ImageSize) || !(ImageBase))
{
/* Bail out */
return STATUS_INVALID_PARAMETER;
}
/* Unallocate the image buffer */
return BlImgUnallocateImageBuffer(ImageBase, ImageSize, ImageFlags);
}
NTSTATUS
ImgpLoadPEImage (
_In_ PBL_IMG_FILE ImageFile,
_In_ BL_MEMORY_TYPE MemoryType,
_Inout_ PVOID* ImageBase,
_Out_opt_ PULONG ImageSize,
_Inout_opt_ PVOID Hash,
_In_ ULONG Flags
)
{
NTSTATUS Status;
ULONG FileSize, HeaderSize;
BL_IMG_FILE LocalFileBuffer;
PBL_IMG_FILE LocalFile;
PVOID VirtualAddress, PreferredBase, ImageBuffer, CertBuffer, HashBuffer;
ULONGLONG VirtualSize;
PIMAGE_DATA_DIRECTORY CertDirectory;
PHYSICAL_ADDRESS PhysicalAddress;
PIMAGE_NT_HEADERS NtHeaders;
USHORT SectionCount, CheckSum, PartialSum, FinalSum;
PIMAGE_SECTION_HEADER Section;
ULONG_PTR EndOfHeaders, SectionStart, Slack, SectionEnd;
ULONG i, SectionSize, RawSize, BytesRead, RemainingLength, Offset, AlignSize;
BOOLEAN First, ImageHashValid;
UCHAR LocalBuffer[1024];
UCHAR TrustedBootInformation[52];
ULONG WorkaroundForBinutils;
/* Initialize locals */
WorkaroundForBinutils = 0;
LocalFile = NULL;
ImageBuffer = NULL;
FileSize = 0;
First = FALSE;
VirtualAddress = NULL;
CertBuffer = NULL;
CertDirectory = NULL;
HashBuffer = NULL;
Offset = 0;
VirtualSize = 0;
ImageHashValid = FALSE;
RtlZeroMemory(&TrustedBootInformation, sizeof(TrustedBootInformation));
/* Get the size of the image */
Status = ImgpGetFileSize(ImageFile, &FileSize);
if (!NT_SUCCESS(Status))
{
return STATUS_FILE_INVALID;
}
/* Allocate a flat buffer for it */
Status = BlImgAllocateImageBuffer(&ImageBuffer, BlLoaderData, FileSize, 0);
if (!NT_SUCCESS(Status))
{
goto Quickie;
}
/* Read the whole file flat for now */
Status = ImgpReadAtFileOffset(ImageFile, FileSize, 0, ImageBuffer, NULL);
if (!NT_SUCCESS(Status))
{
goto Quickie;
}
/* Build a local file handle */
LocalFile = &LocalFileBuffer;
LocalFileBuffer.FileName = ImageFile->FileName;
LocalFileBuffer.Flags = BL_IMG_MEMORY_FILE | BL_IMG_VALID_FILE;
LocalFileBuffer.BaseAddress = ImageBuffer;
LocalFileBuffer.FileSize = FileSize;
/* Get the NT headers of the file */
Status = RtlImageNtHeaderEx(0, ImageBuffer, FileSize, &NtHeaders);
if (!NT_SUCCESS(Status))
{
goto Quickie;
}
/* Check if we should validate the machine type */
if (Flags & BL_LOAD_PE_IMG_CHECK_MACHINE)
{
/* Is it different than our current machine type? */
#if _M_AMD64
if (NtHeaders->FileHeader.Machine != IMAGE_FILE_MACHINE_AMD64)
#else
if (NtHeaders->FileHeader.Machine != IMAGE_FILE_MACHINE_I386)
#endif
{
/* Is it x86 (implying we are x64) ? */
if (NtHeaders->FileHeader.Machine == IMAGE_FILE_MACHINE_I386)
{
/* Return special error code */
Status = STATUS_INVALID_IMAGE_WIN_32;
}
else if (NtHeaders->FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64)
{
/* Otherwise, it's x64 but we are x86 */
Status = STATUS_INVALID_IMAGE_WIN_64;
}
else
{
/* Or it's ARM or something... */
Status = STATUS_INVALID_IMAGE_FORMAT;
}
/* Return with the distinguished error code */
goto Quickie;
}
}
/* Check if we should validate the subsystem */
if (Flags & BL_LOAD_PE_IMG_CHECK_SUBSYSTEM)
{
/* It must be a Windows boot Application */
if (NtHeaders->OptionalHeader.Subsystem !=
IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION)
{
Status = STATUS_INVALID_IMAGE_FORMAT;
goto Quickie;
}
}
/* Check if we should validate the /INTEGRITYCHECK flag */
if (Flags & BL_LOAD_PE_IMG_CHECK_FORCED_INTEGRITY)
{
/* Check if it's there */
if (!(NtHeaders->OptionalHeader.DllCharacteristics &
IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY))
{
/* Nope, fail otherwise */
Status = STATUS_INVALID_IMAGE_FORMAT;
goto Quickie;
}
}
/* Check if we should compute the image hash */
if ((Flags & BL_LOAD_PE_IMG_COMPUTE_HASH) || (Hash))
{
EfiPrintf(L"No hash support\r\n");
}
/* Read the current base address, if any */
VirtualAddress = *ImageBase;
/* Get the virtual size of the image */
VirtualSize = NtHeaders->OptionalHeader.SizeOfImage;
/* Safely align the virtual size to a page */
Status = RtlULongLongAdd(VirtualSize,
PAGE_SIZE - 1,
&VirtualSize);
if (!NT_SUCCESS(Status))
{
goto Quickie;
}
VirtualSize = ALIGN_DOWN_BY(VirtualSize, PAGE_SIZE);
/* Make sure the image isn't larger than 4GB */
if (VirtualSize > ULONG_MAX)
{
Status = STATUS_INVALID_IMAGE_FORMAT;
goto Quickie;
}
/* Check if we have a buffer already */
if (Flags & BL_LOAD_IMG_EXISTING_BUFFER)
{
/* Check if it's too small */
if (*ImageSize < VirtualSize)
{
/* Fail, letting the caller know how big to make it */
*ImageSize = VirtualSize;
Status = STATUS_BUFFER_TOO_SMALL;
}
}
else
{
/* Allocate the buffer with the flags and type the caller wants */
Status = BlImgAllocateImageBuffer(&VirtualAddress,
MemoryType,
VirtualSize,
Flags);
}
/* Bail out if allocation failed, or existing buffer is too small */
if (!NT_SUCCESS(Status))
{
goto Quickie;
}
/* Read the size of the headers */
HeaderSize = NtHeaders->OptionalHeader.SizeOfHeaders;
if (VirtualSize < HeaderSize)
{
/* Bail out if they're bigger than the image! */
Status = STATUS_INVALID_IMAGE_FORMAT;
goto Quickie;
}
/* Now read the header into the buffer */
Status = ImgpReadAtFileOffset(LocalFile, HeaderSize, 0, VirtualAddress, NULL);
if (!NT_SUCCESS(Status))
{
goto Quickie;
}
/* Get the NT headers of the file */
Status = RtlImageNtHeaderEx(0, VirtualAddress, HeaderSize, &NtHeaders);
if (!NT_SUCCESS(Status))
{
goto Quickie;
}
First = FALSE;
/* Record how many sections we have */
SectionCount = NtHeaders->FileHeader.NumberOfSections;
/* Capture the current checksum and reset it */
CheckSum = NtHeaders->OptionalHeader.CheckSum;
NtHeaders->OptionalHeader.CheckSum = 0;
/* Calculate the checksum of the header, and restore the original one */
PartialSum = BlUtlCheckSum(0,
VirtualAddress,
HeaderSize,
BL_UTL_CHECKSUM_COMPLEMENT |
BL_UTL_CHECKSUM_USHORT_BUFFER);
NtHeaders->OptionalHeader.CheckSum = CheckSum;
/* Record our current position (right after the headers) */
EndOfHeaders = (ULONG_PTR)VirtualAddress + HeaderSize;
/* Get the first section and iterate through each one */
Section = IMAGE_FIRST_SECTION(NtHeaders);
for (i = 0; i < SectionCount; i++)
{
/* Compute where this section starts */
SectionStart = (ULONG_PTR)VirtualAddress + Section->VirtualAddress;
/* Make sure that the section fits within the image */
if ((VirtualSize < Section->VirtualAddress) ||
((PVOID)SectionStart < VirtualAddress))
{
EfiPrintf(L"fail 1\r\n");
Status = STATUS_INVALID_IMAGE_FORMAT;
goto Quickie;
}
/* Check if there's slack space between header end and the section */
if (!(First) && (EndOfHeaders < SectionStart))
{
/* Zero it out */
Slack = SectionStart - EndOfHeaders;
RtlZeroMemory((PVOID)EndOfHeaders, Slack);
}
/* Get the section virtual size and the raw size */
SectionSize = Section->Misc.VirtualSize;
RawSize = Section->SizeOfRawData;
/* Safely align the raw size by 2 */
Status = RtlULongAdd(RawSize, 1, &AlignSize);
if (!NT_SUCCESS(Status))
{
goto Quickie;
}
AlignSize = ALIGN_DOWN_BY(AlignSize, 2);
/* IF we don't have a virtual size, use the raw size */
if (!SectionSize)
{
SectionSize = RawSize;
}
/* If we don't have raw data, ignore the raw size */
if (!Section->PointerToRawData)
{
RawSize = 0;
}
else if (SectionSize < RawSize)
{
/* And if the virtual size is smaller, use it as the final size */
RawSize = SectionSize;
}
/* Make sure that the section doesn't overflow in memory */
Status = RtlULongPtrAdd(Section->VirtualAddress,
SectionSize,
&SectionEnd);
if (!NT_SUCCESS(Status))
{
EfiPrintf(L"fail 21\r\n");
Status = STATUS_INVALID_IMAGE_FORMAT;
goto Quickie;
}
/* Make sure that it fits within the image */
if (VirtualSize < SectionEnd)
{
Status = STATUS_INVALID_IMAGE_FORMAT;
goto Quickie;
}
/* Make sure it doesn't overflow on disk */
Status = RtlULongPtrAdd(Section->VirtualAddress,
AlignSize,
&SectionEnd);
if (!NT_SUCCESS(Status))
{
EfiPrintf(L"fail 31\r\n");
Status = STATUS_INVALID_IMAGE_FORMAT;
goto Quickie;
}
/* Make sure that it fits within the disk image as well */
if (VirtualSize < SectionEnd)
{
Status = STATUS_INVALID_IMAGE_FORMAT;
goto Quickie;
}
/* So does this section have a valid size after all? */
if (RawSize)
{
/* Are we in the first iteration? */
if (!First)
{
/* FUCK YOU BINUTILS */
if (NtHeaders->OptionalHeader.MajorLinkerVersion < 7)
{
if ((*(PULONG)&Section->Name == 'ler.') && (RawSize < AlignSize))
{
/* Piece of shit won't build relocations when you tell it to,
* either by using --emit-relocs or --dynamicbase. People online
* have found out that by using -pie-executable you can get this
* to happen, but then it turns out that the .reloc section is
* incorrectly sized, and results in a corrupt PE. However, they
* still compute the checksum using the correct value. What idiots.
*/
WorkaroundForBinutils = AlignSize - RawSize;
AlignSize -= WorkaroundForBinutils;
}
}
/* Yes, read the section data */
Status = ImgpReadAtFileOffset(LocalFile,
AlignSize,
Section->PointerToRawData,
(PVOID)SectionStart,
NULL);
if (!NT_SUCCESS(Status))
{
goto Quickie;
}
/* Update our current offset */
Offset = AlignSize + Section->PointerToRawData;
/* Update the checksum to include this section */
PartialSum = BlUtlCheckSum(PartialSum,
(PUCHAR)SectionStart,
AlignSize,
BL_UTL_CHECKSUM_COMPLEMENT |
BL_UTL_CHECKSUM_USHORT_BUFFER);
AlignSize += WorkaroundForBinutils;
}
}
/* Are we in the first iteration? */
if (!First)
{
/* Is there space at the end of the section? */
if (RawSize < SectionSize)
{
/* Zero out the slack space that's there */
Slack = SectionSize - RawSize;
RtlZeroMemory((PVOID)(SectionStart + RawSize), Slack);
}
/* Update our tail offset */
EndOfHeaders = SectionStart + SectionSize;
}
/* Move to the next section */
Section++;
}
/* Are we in the first iteration? */
if (!First)
{
/* Go to the end of the file */
SectionStart = (ULONG_PTR)VirtualAddress + VirtualSize;
/* Is there still some slack space left? */
if (EndOfHeaders < SectionStart)
{
/* Zero it out */
Slack = SectionStart - EndOfHeaders;
RtlZeroMemory((PVOID)EndOfHeaders, Slack);
}
}
/* Did the first iteration complete OK? */
if ((NT_SUCCESS(Status)) && !(First))
{
/* Check how many non-image bytes are left in the file */
RemainingLength = FileSize - Offset;
while (RemainingLength)
{
/* See if the read will fit into our local buffer */
if (RemainingLength >= sizeof(LocalBuffer))
{
/* Nope, cap it */
BytesRead = sizeof(LocalBuffer);
}
else
{
/* Yes, but there's less to read */
BytesRead = RemainingLength;
}
/* Read 1024 bytes into the local buffer */
Status = ImgpReadAtFileOffset(LocalFile,
BytesRead,
Offset,
LocalBuffer,
&BytesRead);
if (!(NT_SUCCESS(Status)) || !(BytesRead))
{
Status = STATUS_FILE_INVALID;
goto Quickie;
}
/* Advance the offset and reduce the length */
RemainingLength -= BytesRead;
Offset += BytesRead;
/* Compute the checksum of this leftover space */
PartialSum = BlUtlCheckSum(PartialSum,
LocalBuffer,
BytesRead,
BL_UTL_CHECKSUM_COMPLEMENT |
BL_UTL_CHECKSUM_USHORT_BUFFER);
}
/* Finally, calculate the final checksum and compare it */
FinalSum = FileSize + PartialSum + WorkaroundForBinutils;
if ((FinalSum != CheckSum) && (PartialSum == 0xFFFF))
{
/* It hit overflow, so set it to the file size */
FinalSum = FileSize;
}
/* If the checksum doesn't match, and caller is enforcing, bail out */
if ((FinalSum != CheckSum) &&
!(Flags & BL_LOAD_PE_IMG_IGNORE_CHECKSUM_MISMATCH))
{
Status = STATUS_IMAGE_CHECKSUM_MISMATCH;
goto Quickie;
}
}
/* Check if the .rsrc section should be checked with the filename */
if (Flags & BL_LOAD_PE_IMG_VALIDATE_ORIGINAL_FILENAME)
{
EfiPrintf(L"Not yet supported\r\n");
Status = 0xC0430007; // STATUS_SECUREBOOT_FILE_REPLACED
goto Quickie;
}
/* Check if we should relocate */
if (!(Flags & BL_LOAD_PE_IMG_SKIP_RELOCATIONS))
{
/* Check if we loaded at a different address */
PreferredBase = (PVOID)NtHeaders->OptionalHeader.ImageBase;
if (VirtualAddress != PreferredBase)
{
/* Yep -- do relocations */
Status = LdrRelocateImage(VirtualAddress,
"Boot Environment Library",
STATUS_SUCCESS,
STATUS_UNSUCCESSFUL,
STATUS_INVALID_IMAGE_FORMAT);
if (!NT_SUCCESS(Status))
{
/* That's bad */
goto Quickie;
}
}
}
#if BL_TPM_SUPPORT
/* Check if the image hash was valid */
if (!ImageHashValid)
{
/* Send a TPM/SI notification without a context */
BlEnNotifyEvent(0x10000002, NULL);
}
/* Now send a TPM/SI notification with the hash of the loaded image */
BlMmTranslateVirtualAddress(VirtualAddress, &Context.ImageBase);
Context.HashAlgorithm = HashAlgorithm;
Context.HashSize = HashSize;
Context.FileName = ImageFile->FileName;
Context.ImageSize = VirtualSize;
Context.HashValid = ImageHashValid;
Context.Hash = Hash;
BlEnNotifyEvent(0x10000002, &Context);
#endif
/* Return the loaded address to the caller */
*ImageBase = VirtualAddress;
/* If the caller wanted the image size, return it too */
if (ImageSize)
{
*ImageSize = VirtualSize;
}
Quickie:
/* Check if we computed the image hash OK */
if (ImageHashValid)
{
/* Then free the information that ImgpValidateImageHash set up */
EfiPrintf(L"leadking trusted boot\r\n");
//ImgpDestroyTrustedBootInformation(&TrustedBootInformation);
}
/* Check if we had a hash buffer */
if (HashBuffer)
{
/* Free it */
MmPapFreePages(HashBuffer, BL_MM_INCLUDE_MAPPED_ALLOCATED);
}
/* Check if we have a certificate directory */
if ((CertBuffer) && (CertDirectory))
{
/* Free it */
BlImgUnallocateImageBuffer(CertBuffer, CertDirectory->Size, 0);
}
/* Check if we had an image buffer allocated */
if ((ImageBuffer) && (FileSize))
{
/* Free it */
BlImgUnallocateImageBuffer(ImageBuffer, FileSize, 0);
}
/* Check if we had a local file handle */
if (LocalFile)
{
/* Close it */
ImgpCloseFile(LocalFile);
}
/* Check if this is the failure path */
if (!NT_SUCCESS(Status))
{
/* Check if we had started mapping in the image already */
if ((VirtualAddress) && !(Flags & BL_LOAD_PE_IMG_EXISTING_BUFFER))
{
/* Into a virtual buffer? */
if (Flags & BL_LOAD_PE_IMG_VIRTUAL_BUFFER)
{
/* Unmap and free it */
BlMmUnmapVirtualAddressEx(VirtualAddress, VirtualSize);
PhysicalAddress.QuadPart = (ULONG_PTR)VirtualAddress;
BlMmFreePhysicalPages(PhysicalAddress);
}
else
{
/* Into a physical buffer -- free it */
MmPapFreePages(VirtualAddress, BL_MM_INCLUDE_MAPPED_ALLOCATED);
}
}
}
/* Return back to caller */
return Status;
}
NTSTATUS
BlImgLoadPEImageEx (
_In_ ULONG DeviceId,
_In_ BL_MEMORY_TYPE MemoryType,
_In_ PWCHAR Path,
_Out_ PVOID* ImageBase,
_Out_ PULONG ImageSize,
_Out_ PVOID Hash,
_In_ ULONG Flags
)
{
BL_IMG_FILE ImageFile;
NTSTATUS Status;
/* Initialize the image file structure */
ImageFile.Flags = 0;
ImageFile.FileName = NULL;
/* Check if the required parameter are missing */
if (!(ImageBase) || !(Path))
{
return STATUS_INVALID_PARAMETER;
}
/* If we are loading a pre-allocated image, make sure we have it */
if ((Flags & BL_LOAD_IMG_EXISTING_BUFFER) && (!(*ImageBase) || !(ImageSize)))
{
return STATUS_INVALID_PARAMETER;
}
/* Load the file from disk */
Status = ImgpOpenFile(DeviceId, Path, 0, &ImageFile);
if (NT_SUCCESS(Status))
{
/* If that worked, do the PE parsing */
Status = ImgpLoadPEImage(&ImageFile,
MemoryType,
ImageBase,
ImageSize,
Hash,
Flags);
}
/* Close the image file and return back to caller */
ImgpCloseFile(&ImageFile);
return Status;
}
NTSTATUS
BlImgLoadBootApplication (
_In_ PBL_LOADED_APPLICATION_ENTRY BootEntry,
_Out_ PULONG AppHandle
)
{
NTSTATUS Status;
PULONGLONG AllowedList;
ULONGLONG AllowedCount;
ULONG i, DeviceId, ImageSize, Flags, ListSize;
LARGE_INTEGER Frequency;
PVOID UnlockCode, ImageBase;
PBL_DEVICE_DESCRIPTOR Device, BitLockerDevice;
PWCHAR Path;
PBL_APPLICATION_ENTRY AppEntry;
PBL_IMG_FILE ImageFile;
BOOLEAN DisableIntegrity, TestSigning;
UCHAR Hash[64];
PBL_IMAGE_APPLICATION_ENTRY ImageAppEntry;
/* Initialize all locals */
BitLockerDevice = NULL;
UnlockCode = NULL;
ImageFile = NULL;
DeviceId = -1;
Device = NULL;
ImageAppEntry = NULL;
AppEntry = NULL;
Path = NULL;
ImageSize = 0;
ImageBase = NULL;
/* Check for "allowed in-memory settings" */
Status = BlpGetBootOptionIntegerList(BootEntry->BcdData,
BcdLibraryIntegerList_AllowedInMemorySettings,
&AllowedList,
&AllowedCount,
TRUE);
if (Status == STATUS_SUCCESS)
{
/* Loop through the list of allowed setting */
for (i = 0; i < AllowedCount; i++)
{
/* Find the super undocumented one */
if (AllowedList[i] == BcdLibraryInteger_UndocumentedMagic)
{
/* If it's present, append the current perf frequence to it */
BlTimeQueryPerformanceCounter(&Frequency);
BlAppendBootOptionInteger(BootEntry,
BcdLibraryInteger_UndocumentedMagic,
Frequency.QuadPart);
}
}
}
#if BL_BITLOCKER_SUPPORT
/* Do bitlocker stuff */
Status = BlFveSecureBootUnlockBootDevice(BootEntry, &BitLockerDevice, &UnlockCode);
if (!NT_SUCCESS(Status))
{
goto Quickie;
}
#endif
/* Get the device on which this application is on*/
Status = BlGetBootOptionDevice(BootEntry->BcdData,
BcdLibraryDevice_ApplicationDevice,
&Device,
NULL);
if (!NT_SUCCESS(Status))
{
goto Quickie;
}
/* Get the path of the application */
Status = BlGetBootOptionString(BootEntry->BcdData,
BcdLibraryString_ApplicationPath,
&Path);
if (!NT_SUCCESS(Status))
{
goto Quickie;
}
/* Open the device */
Status = BlpDeviceOpen(Device,
BL_DEVICE_READ_ACCESS,
0,
&DeviceId);
if (!NT_SUCCESS(Status))
{
goto Quickie;
}
/* Check for integrity BCD options */
BlImgQueryCodeIntegrityBootOptions(BootEntry,
&DisableIntegrity,
&TestSigning);
#if BL_TPM_SUPPORT
RtlZeroMemory(&Context, sizeof(Context);
Context.BootEntry = BootEntry;
BlEnNotifyEvent(0x10000003, &Context);
#endif
/* Enable signing and hashing checks if integrity is enabled */
Flags = 0;
if (!DisableIntegrity)
{
Flags = 0x8070;
}
/* Now call the PE loader to load the image */
Status = BlImgLoadPEImageEx(DeviceId,
BlLoaderMemory,
Path,
&ImageBase,
&ImageSize,
Hash,
Flags);
if (!NT_SUCCESS(Status))
{
goto Quickie;
}
#if BL_KD_SUPPORT
/* Check if we should notify the debugger of load */
if (BdDebugTransitions)
{
/* Initialize it */
BdForceDebug = 1;
Status = BlBdInitialize();
if (NT_SUCCESS(Status))
{
/* Check if it's enabled */
if (BlBdDebuggerEnabled())
{
/* Send it an image load notification */
BdDebuggerNotPresent = FALSE;
RtlInitUnicodeString(&PathString, Path);
BlBdLoadImageSymbols(&PathString, ImageBase);
}
}
}
#endif
#if BL_BITLOCKER_SUPPORT
/* Do bitlocker stuff */
Status = BlSecureBootCheckPolicyOnFveDevice(BitLockerDevice);
if (!NT_SUCCESS(Status))
{
goto Quickie;
}
#endif
#if BL_BITLOCKER_SUPPORT
/* Do bitlocker stuff */
Status = BlFveSecureBootCheckpointBootApp(BootEntry, BitLockerDevice, Hash, UnlockCode);
if (!NT_SUCCESS(Status))
{
goto Quickie;
}
#endif
/* Get the BCD option size */
ListSize = BlGetBootOptionListSize(BootEntry->BcdData);
/* Allocate an entry with all the BCD options */
AppEntry = BlMmAllocateHeap(ListSize + sizeof(*AppEntry));
if (!AppEntry)
{
Status = STATUS_NO_MEMORY;
goto Quickie;
}
/* Zero it out */
RtlZeroMemory(AppEntry, sizeof(*AppEntry));
/* Initialize it */
strcpy(AppEntry->Signature, "BTAPENT");
AppEntry->Guid = BootEntry->Guid;
AppEntry->Flags = BootEntry->Flags;
/* Copy the BCD options */
RtlCopyMemory(&AppEntry->BcdData, BootEntry->BcdData, ListSize);
/* Allocate the image entry */
ImageAppEntry = BlMmAllocateHeap(sizeof(*ImageAppEntry));
if (!ImageAppEntry)
{
Status = STATUS_NO_MEMORY;
goto Quickie;
}
/* Initialize it */
ImageAppEntry->ImageBase = ImageBase;
ImageAppEntry->ImageSize = ImageSize;
ImageAppEntry->AppEntry = AppEntry;
/* Check if this is the first entry */
if (!IapTableEntries)
{
/* Allocate two entries */
IapAllocatedTableEntries = 0;
IapTableEntries = 2;
IapImageTable = BlMmAllocateHeap(IapTableEntries * sizeof(PVOID));
if (!IapImageTable)
{
Status = STATUS_NO_MEMORY;
goto Quickie;
}
/* Zero out the entries for now */
RtlZeroMemory(IapImageTable, IapTableEntries * sizeof(PVOID));
}
/* Set this entry into the table */
Status = BlTblSetEntry(&IapImageTable,
&IapTableEntries,
ImageAppEntry,
AppHandle,
TblDoNotPurgeEntry);
Quickie:
/* Is the device open? Close it if so */
if (DeviceId != 1)
{
BlDeviceClose(DeviceId);
}
/* Is there an allocated device? Free it */
if (Device)
{
BlMmFreeHeap(Device);
}
/* Is there an allocated path? Free it */
if (Path)
{
BlMmFreeHeap(Path);
}
/* Is there a bitlocker device? Free it */
if (BitLockerDevice)
{
BlMmFreeHeap(BitLockerDevice);
}
/* Is there a bitlocker unlock code? Free it */
if (UnlockCode)
{
BlMmFreeHeap(UnlockCode);
}
/* Did we succeed in creating an entry? */
if (NT_SUCCESS(Status))
{
/* Remember there's one more in the table */
IapAllocatedTableEntries++;
/* Return success */
return Status;
}
/* Did we load an image after all? */
if (ImageBase)
{
/* Unload it */
BlImgUnLoadImage(ImageBase, ImageSize, 0);
}
/* Did we allocate an app entry? Free it */
if (AppEntry)
{
BlMmFreeHeap(AppEntry);
}
/* Do we have an image file entry? Free it */
if (ImageFile)
{
BlMmFreeHeap(ImageFile);
}
/* Do we no longer have a single entry in the table? */
if (!(IapAllocatedTableEntries) && (IapImageTable))
{
/* Free and destroy the table */
BlMmFreeHeap(IapImageTable);
IapTableEntries = 0;
IapImageTable = NULL;
}
/* Return the failure code */
return Status;
}
NTSTATUS
BlpPdParseReturnArguments (
_In_ PBL_RETURN_ARGUMENTS ReturnArguments
)
{
/* Check if any custom data was returned */
if (ReturnArguments->DataPage == 0)
{
/* Nope, nothing to do */
return STATUS_SUCCESS;
}
/* Yes, we have to parse it */
EfiPrintf(L"Return arguments not supported\r\n");
return STATUS_NOT_IMPLEMENTED;
}
NTSTATUS
ImgpCopyApplicationBootDevice (
__in PBL_DEVICE_DESCRIPTOR DestinationDevice,
__in PBL_DEVICE_DESCRIPTOR SourceDevice
)
{
/* Is this a partition device? */
if (SourceDevice->DeviceType != PartitionDevice)
{
/* It's not -- a simple copy will do */
RtlCopyMemory(DestinationDevice, SourceDevice, SourceDevice->Size);
return STATUS_SUCCESS;
}
/* TODO */
EfiPrintf(L"Partition copy not supported\r\n");
return STATUS_NOT_IMPLEMENTED;
}
NTSTATUS
ImgpInitializeBootApplicationParameters (
_In_ PBL_BUFFER_DESCRIPTOR ImageParameters,
_In_ PBL_APPLICATION_ENTRY AppEntry,
_In_ PVOID ImageBase,
_In_ ULONG ImageSize
)
{
NTSTATUS Status;
PIMAGE_NT_HEADERS NtHeaders;
BL_BUFFER_DESCRIPTOR MemoryParameters;
LIST_ENTRY MemoryList;
PBL_FIRMWARE_DESCRIPTOR FirmwareParameters;
PBL_DEVICE_DESCRIPTOR BootDevice;
PBL_MEMORY_DATA MemoryData;
PBL_APPLICATION_ENTRY BootAppEntry;
PBL_RETURN_ARGUMENTS ReturnArguments;
PBOOT_APPLICATION_PARAMETER_BLOCK ParameterBlock;
ULONG EntrySize, BufferSize;
/* Get the image headers and validate it */
Status = RtlImageNtHeaderEx(0, ImageBase, ImageSize, &NtHeaders);
if (!NT_SUCCESS(Status))
{
return Status;
}
/* Get the size of the entire non-firmware, allocated, memory map */
MemoryParameters.BufferSize = 0;
Status = BlMmGetMemoryMap(&MemoryList,
&MemoryParameters,
BL_MM_INCLUDE_PERSISTENT_MEMORY |
BL_MM_INCLUDE_MAPPED_ALLOCATED |
BL_MM_INCLUDE_MAPPED_UNALLOCATED |
BL_MM_INCLUDE_UNMAPPED_ALLOCATED |
BL_MM_INCLUDE_RESERVED_ALLOCATED,
0);
if ((Status != STATUS_BUFFER_TOO_SMALL) && (Status != STATUS_SUCCESS))
{
/* We failed due to an unknown reason -- bail out */
return Status;
}
/* Compute the list of the BCD plus the application entry */
EntrySize = BlGetBootOptionListSize(&AppEntry->BcdData) +
FIELD_OFFSET(BL_APPLICATION_ENTRY, BcdData);
/* Compute the total size required for the entire structure */
BufferSize = EntrySize +
BlpBootDevice->Size +
MemoryParameters.BufferSize +
sizeof(*ReturnArguments) +
sizeof(*MemoryData) +
sizeof(*FirmwareParameters) +
sizeof(*ParameterBlock);
/* Check if this gives us enough space */
if (ImageParameters->BufferSize < BufferSize)
{
/* It does not -- free the existing buffer */
if (ImageParameters->BufferSize)
{
BlMmFreeHeap(ImageParameters->Buffer);
}
/* Allocate a new buffer of sufficient size */
ImageParameters->BufferSize = BufferSize;
ImageParameters->Buffer = BlMmAllocateHeap(BufferSize);
if (!ImageParameters->Buffer)
{
/* Bail out if we couldn't allocate it */
return STATUS_NO_MEMORY;
}
}
/* Zero out the parameter block */
ParameterBlock = (PBOOT_APPLICATION_PARAMETER_BLOCK)ImageParameters->Buffer;
RtlZeroMemory(ParameterBlock, BufferSize);
/* Initialize it */
ParameterBlock->Version = BOOT_APPLICATION_VERSION;
ParameterBlock->Size = BufferSize;
ParameterBlock->Signature[0] = BOOT_APPLICATION_SIGNATURE_1;
ParameterBlock->Signature[1] = BOOT_APPLICATION_SIGNATURE_2;
ParameterBlock->MemoryTranslationType = MmTranslationType;
ParameterBlock->ImageType = IMAGE_FILE_MACHINE_I386;
ParameterBlock->ImageBase = (ULONG_PTR)ImageBase;
ParameterBlock->ImageSize = NtHeaders->OptionalHeader.SizeOfImage;
/* Get the offset to the memory data */
ParameterBlock->MemoryDataOffset = sizeof(*ParameterBlock);
/* Fill it out */
MemoryData = (PBL_MEMORY_DATA)((ULONG_PTR)ParameterBlock +
ParameterBlock->MemoryDataOffset);
MemoryData->Version = BL_MEMORY_DATA_VERSION;
MemoryData->MdListOffset = sizeof(*MemoryData);
MemoryData->DescriptorSize = sizeof(BL_MEMORY_DESCRIPTOR);
MemoryData->DescriptorOffset = FIELD_OFFSET(BL_MEMORY_DESCRIPTOR, BasePage);
/* And populate the memory map */
MemoryParameters.Buffer = MemoryData + 1;
Status = BlMmGetMemoryMap(&MemoryList,
&MemoryParameters,
BL_MM_INCLUDE_PERSISTENT_MEMORY |
BL_MM_INCLUDE_MAPPED_ALLOCATED |
BL_MM_INCLUDE_MAPPED_UNALLOCATED |
BL_MM_INCLUDE_UNMAPPED_ALLOCATED |
BL_MM_INCLUDE_RESERVED_ALLOCATED,
0);
if (!NT_SUCCESS(Status))
{
return Status;
}
/* Now that we have the map, indicate the number of descriptors */
MemoryData->DescriptorCount = MemoryParameters.ActualSize /
MemoryData->DescriptorSize;
/* Get the offset to the application entry */
ParameterBlock->AppEntryOffset = ParameterBlock->MemoryDataOffset +
MemoryData->MdListOffset +
MemoryParameters.BufferSize;
/* Fill it out */
BootAppEntry = (PBL_APPLICATION_ENTRY)((ULONG_PTR)ParameterBlock +
ParameterBlock->AppEntryOffset);
RtlCopyMemory(BootAppEntry, AppEntry, EntrySize);
/* Get the offset to the boot device */
ParameterBlock->BootDeviceOffset = ParameterBlock->AppEntryOffset +
EntrySize;
/* Fill it out */
BootDevice = (PBL_DEVICE_DESCRIPTOR)((ULONG_PTR)ParameterBlock +
ParameterBlock->BootDeviceOffset);
Status = ImgpCopyApplicationBootDevice(BootDevice, BlpBootDevice);
if (!NT_SUCCESS(Status))
{
return Status;
}
/* Get the offset to the firmware data */
ParameterBlock->FirmwareParametersOffset = ParameterBlock->BootDeviceOffset +
BootDevice->Size;
/* Fill it out */
FirmwareParameters = (PBL_FIRMWARE_DESCRIPTOR)((ULONG_PTR)ParameterBlock +
ParameterBlock->
FirmwareParametersOffset);
Status = BlFwGetParameters(FirmwareParameters);
if (!NT_SUCCESS(Status))
{
return Status;
}
/* Get the offset to the return arguments */
ParameterBlock->ReturnArgumentsOffset = ParameterBlock->FirmwareParametersOffset +
sizeof(BL_FIRMWARE_DESCRIPTOR);
/* Fill them out */
ReturnArguments = (PBL_RETURN_ARGUMENTS)((ULONG_PTR)ParameterBlock +
ParameterBlock->
ReturnArgumentsOffset);
ReturnArguments->Version = BL_RETURN_ARGUMENTS_VERSION;
ReturnArguments->DataPage = 0;
ReturnArguments->DataSize = 0;
/* Structure complete */
ImageParameters->ActualSize = ParameterBlock->ReturnArgumentsOffset +
sizeof(*ReturnArguments);
return STATUS_SUCCESS;
}
NTSTATUS
ImgArchEfiStartBootApplication (
_In_ PBL_APPLICATION_ENTRY AppEntry,
_In_ PVOID ImageBase,
_In_ ULONG ImageSize,
_In_ PBL_RETURN_ARGUMENTS ReturnArguments
)
{
#ifndef _M_ARM
KDESCRIPTOR Gdt, Idt;
ULONG BootSizeNeeded;
NTSTATUS Status;
PVOID BootData;
PIMAGE_NT_HEADERS NtHeaders;
PVOID NewStack, NewGdt, NewIdt;
BL_BUFFER_DESCRIPTOR Parameters;
/* Read the current IDT and GDT */
_sgdt(&Gdt.Limit);
__sidt(&Idt.Limit);
/* Allocate space for the IDT, GDT, and 24 pages of stack */
BootSizeNeeded = (ULONG_PTR)PAGE_ALIGN(Idt.Limit + Gdt.Limit + 1 + 25 * PAGE_SIZE);
Status = MmPapAllocatePagesInRange(&BootData,
BlLoaderArchData,
BootSizeNeeded >> PAGE_SHIFT,
0,
0,
NULL,
0);
if (!NT_SUCCESS(Status))
{
goto Quickie;
}
/* Zero the boot data */
RtlZeroMemory(BootData, BootSizeNeeded);
/* Set the new stack, GDT and IDT */
NewStack = (PVOID)((ULONG_PTR)BootData + (24 * PAGE_SIZE) - 8);
NewGdt = (PVOID)((ULONG_PTR)BootData + (24 * PAGE_SIZE));
NewIdt = (PVOID)((ULONG_PTR)BootData + (24 * PAGE_SIZE) + Gdt.Limit + 1);
/* Copy the current (firmware) GDT and IDT */
RtlCopyMemory(NewGdt, (PVOID)Gdt.Base, Gdt.Limit + 1);
RtlCopyMemory(NewIdt, (PVOID)Idt.Base, Idt.Limit + 1);
/* Read the NT headers so that we can get the entrypoint later on */
RtlImageNtHeaderEx(0, ImageBase, ImageSize, &NtHeaders);
/* Prepare the application parameters */
RtlZeroMemory(&Parameters, sizeof(Parameters));
Status = ImgpInitializeBootApplicationParameters(&Parameters,
AppEntry,
ImageBase,
ImageSize);
if (NT_SUCCESS(Status))
{
/* Set the firmware GDT/IDT as the one the application will use */
BootAppGdtRegister = Gdt;
BootAppIdtRegister = Idt;
/* Set the entrypoint, parameters, and stack */
BootApp32EntryRoutine = (PVOID)((ULONG_PTR)ImageBase +
NtHeaders->OptionalHeader.
AddressOfEntryPoint);
BootApp32Parameters = Parameters.Buffer;
BootApp32Stack = NewStack;
#if BL_KD_SUPPORT
/* Disable the kernel debugger */
BlBdStop();
#endif
/* Make it so */
Archx86TransferTo32BitApplicationAsm();
/* Not yet implemented. This is the last step! */
EfiPrintf(L"EFI APPLICATION RETURNED!!!\r\n");
EfiStall(100000000);
#if BL_KD_SUPPORT
/* Re-enable the kernel debugger */
BlBdStart();
#endif
}
Quickie:
/* Check if we had boot data allocated */
if (BootData)
{
/* Free it */
MmPapFreePages(BootData, BL_MM_INCLUDE_MAPPED_ALLOCATED);
}
#else
EfiPrintf(L"ImgArchEfiStartBootApplication not implemented for this platform.\r\n");
#endif
/* All done */
return STATUS_NOT_IMPLEMENTED;
}
NTSTATUS
BlImgStartBootApplication (
_In_ ULONG AppHandle,
_Inout_opt_ PBL_RETURN_ARGUMENTS ReturnArguments
)
{
PBL_IMAGE_APPLICATION_ENTRY ImageAppEntry;
BL_RETURN_ARGUMENTS LocalReturnArgs;
PBL_FILE_SYSTEM_ENTRY FileSystem;
PLIST_ENTRY NextEntry, ListHead;
NTSTATUS Status;
/* Check if we don't have an argument structure */
if (!ReturnArguments)
{
/* Initialize a local copy and use it instead */
LocalReturnArgs.Version = BL_RETURN_ARGUMENTS_VERSION;
LocalReturnArgs.Status = STATUS_SUCCESS;
LocalReturnArgs.Flags = 0;
LocalReturnArgs.DataPage = 0;
LocalReturnArgs.DataSize = 0;
ReturnArguments = &LocalReturnArgs;
}
/* Make sure the handle index is valid */
if (IapTableEntries <= AppHandle)
{
return STATUS_INVALID_PARAMETER;
}
/* Get the entry for this handle, making sure it exists */
ImageAppEntry = IapImageTable[AppHandle];
if (!ImageAppEntry)
{
return STATUS_INVALID_PARAMETER;
}
/* Loop the registered file systems */
ListHead = &RegisteredFileSystems;
NextEntry = RegisteredFileSystems.Flink;
while (NextEntry != ListHead)
{
/* Get the filesystem entry */
FileSystem = CONTAINING_RECORD(NextEntry,
BL_FILE_SYSTEM_ENTRY,
ListEntry);
/* See if it has a purge callback */
if (FileSystem->PurgeCallback)
{
/* Call it */
FileSystem->PurgeCallback();
}
/* Move to the next entry */
NextEntry = NextEntry->Flink;
}
/* TODO -- flush the block I/O cache too */
//BlockIoPurgeCache();
/* Call into EFI land to start the boot application */
Status = ImgArchEfiStartBootApplication(ImageAppEntry->AppEntry,
ImageAppEntry->ImageBase,
ImageAppEntry->ImageSize,
ReturnArguments);
/* Parse any arguments we got on the way back */
BlpPdParseReturnArguments(ReturnArguments);
#if BL_BITLOCKER_SUPPORT
/* Bitlocker stuff */
FvebpCheckAllPartitions(TRUE);
#endif
#if BL_TPM_SUPPORT
/* Notify a TPM/SI event */
BlEnNotifyEvent(0x10000005, NULL);
#endif
/* Reset the display */
BlpDisplayReinitialize();
/* TODO -- reset ETW */
//BlpLogInitialize();
/* All done */
return Status;
}
NTSTATUS
BlImgUnloadBootApplication (
_In_ ULONG AppHandle
)
{
PBL_IMAGE_APPLICATION_ENTRY ImageAppEntry;
NTSTATUS Status;
/* Make sure the handle index is valid */
if (IapTableEntries <= AppHandle)
{
return STATUS_INVALID_PARAMETER;
}
/* Get the entry for this handle, making sure it exists */
ImageAppEntry = IapImageTable[AppHandle];
if (!ImageAppEntry)
{
return STATUS_INVALID_PARAMETER;
}
/* Unload the image */
Status = BlImgUnLoadImage(ImageAppEntry->ImageBase,
ImageAppEntry->ImageSize,
0);
if (NT_SUCCESS(Status))
{
/* Normalize the success code */
Status = STATUS_SUCCESS;
}
else
{
/* Normalize the failure code */
Status = STATUS_MEMORY_NOT_ALLOCATED;
}
/* Free the entry and the image entry as well */
BlMmFreeHeap(ImageAppEntry->AppEntry);
BlMmFreeHeap(ImageAppEntry);
/* Clear the handle */
IapImageTable[AppHandle] = NULL;
/* Free one entry */
if (!(--IapAllocatedTableEntries))
{
/* There are no more, so get rid of the table itself */
BlMmFreeHeap(IapImageTable);
IapImageTable = NULL;
IapTableEntries = 0;
}
/* All good */
return Status;
}