reactos/boot/environ/app/bootmgr/efiemu.c

1035 lines
33 KiB
C

/*
* COPYRIGHT: See COPYING.ARM in the top level directory
* PROJECT: ReactOS UEFI Boot Manager
* FILE: boot/environ/app/bootmgr/efiemu.c
* PURPOSE: UEFI Entrypoint for Boot Manager
* PROGRAMMER: Alex Ionescu (alex.ionescu@reactos.org)
*/
/* INCLUDES ******************************************************************/
#include "bootmgr.h"
/* DATA STRUCTURES ***********************************************************/
typedef struct _BOOT_APPLICATION_PARAMETER_BLOCK_SCRATCH
{
BOOT_APPLICATION_PARAMETER_BLOCK;
BL_MEMORY_DATA BootMemoryData;
BL_MEMORY_DESCRIPTOR MemEntry;
UCHAR AppEntry[788];
} BOOT_APPLICATION_PARAMETER_BLOCK_SCRATCH;
/* DATA VARIABLES ************************************************************/
BOOT_APPLICATION_PARAMETER_BLOCK_SCRATCH EfiInitScratch;
/* FUNCTIONS *****************************************************************/
/*++
* @name AhCreateLoadOptionsList
*
* The AhCreateLoadOptionsList routine
*
* @param CommandLine
* UEFI Image Handle for the current loaded application.
*
* @param BootOptions
* Pointer to the UEFI System Table.
*
* @param MaximumLength
* Pointer to the UEFI System Table.
*
* @param OptionSize
* Pointer to the UEFI System Table.
*
* @param PreviousOption
* Pointer to the UEFI System Table.
*
* @param PreviousOptionSize
* Pointer to the UEFI System Table.
*
* @return None
*
*--*/
NTSTATUS
AhCreateLoadOptionsList (
_In_ PWCHAR CommandLine,
_In_ PBL_BCD_OPTION BootOptions,
_In_ ULONG MaximumLength,
_Out_ PULONG OptionSize,
_In_ PBL_BCD_OPTION* PreviousOption,
_In_ PULONG PreviousOptionSize
)
{
return STATUS_NOT_IMPLEMENTED;
}
/*++
* @name EfiInitpAppendPathString
*
* The EfiInitpAppendPathString routine
*
* @param DestinationPath
* UEFI Image Handle for the current loaded application.
*
* @param RemainingSize
* Pointer to the UEFI System Table.
*
* @param AppendPath
* Pointer to the UEFI System Table.
*
* @param AppendLength
* Pointer to the UEFI System Table.
*
* @param BytesAppended
* Pointer to the UEFI System Table.
*
* @return None
*
*--*/
NTSTATUS
EfiInitpAppendPathString (
_In_ PWCHAR PathString,
_In_ ULONG MaximumLength,
_In_ PWCHAR NewPathString,
_In_ ULONG NewPathLength,
_Out_ PULONG ResultLength
)
{
NTSTATUS Status;
ULONG FinalPathLength;
/* We deal in Unicode, validate the length */
if (NewPathLength & 1)
{
return STATUS_INVALID_PARAMETER;
}
/* Is the new element at least a character? */
Status = STATUS_SUCCESS;
if (NewPathLength >= sizeof(WCHAR))
{
/* Is the last character already a NULL character? */
if (NewPathString[(NewPathLength - sizeof(WCHAR)) / sizeof(WCHAR)] ==
UNICODE_NULL)
{
/* Then we won't need to count it */
NewPathLength -= sizeof(UNICODE_NULL);
}
/* Was it more than just a NULL character? */
if (NewPathLength >= sizeof(WCHAR))
{
/* Yep -- but does it have a separator? */
if (*NewPathString == OBJ_NAME_PATH_SEPARATOR)
{
/* Skip it, we'll add our own later */
NewPathString++;
NewPathLength -= sizeof(OBJ_NAME_PATH_SEPARATOR);
}
/* Was it more than just a separator? */
if (NewPathLength >= sizeof(WCHAR))
{
/* Yep -- but does it end with a separator? */
if (NewPathString[(NewPathLength - sizeof(WCHAR)) / sizeof(WCHAR)] ==
OBJ_NAME_PATH_SEPARATOR)
{
/* That's something else we won't need for now */
NewPathLength -= sizeof(OBJ_NAME_PATH_SEPARATOR);
}
}
}
}
/* Check if anything needs to be appended after all */
if (NewPathLength != 0)
{
/* We will append the length of the new path element, plus a separator */
FinalPathLength = NewPathLength + sizeof(OBJ_NAME_PATH_SEPARATOR);
if (MaximumLength >= FinalPathLength)
{
/* Add a separator to the existing path*/
*PathString = OBJ_NAME_PATH_SEPARATOR;
/* Followed by the new path element */
RtlCopyMemory(PathString + 1, NewPathString, NewPathLength);
/* Return the number of bytes appended */
*ResultLength = FinalPathLength;
}
else
{
/* There's not enough space to do this */
Status = STATUS_BUFFER_TOO_SMALL;
}
}
else
{
/* Nothing to append */
*ResultLength = 0;
}
return Status;
}
/*++
* @name EfiInitpConvertEfiDevicePath
*
* The EfiInitpConvertEfiDevicePath routine
*
* @param DevicePath
* UEFI Image Handle for the current loaded application.
*
* @param DeviceType
* Pointer to the UEFI System Table.
*
* @param Option
* Pointer to the UEFI System Table.
*
* @param MaximumLength
* Pointer to the UEFI System Table.
*
* @return None
*
*--*/
NTSTATUS
EfiInitpConvertEfiFilePath (
_In_ EFI_DEVICE_PATH_PROTOCOL *DevicePath,
_In_ ULONG PathType,
_In_ PBL_BCD_OPTION Option,
_In_ ULONG MaximumLength
)
{
ULONG BytesAppended, DataSize, StringLength;
PWCHAR StringEntry, PathString;
FILEPATH_DEVICE_PATH *FilePath;
NTSTATUS Status;
/* Make sure we have enough space for the option */
if (MaximumLength < sizeof(*Option))
{
return STATUS_INVALID_PARAMETER;
}
/* Set the initial size of the option, and consume from our buffer */
DataSize = sizeof(*Option);
MaximumLength -= sizeof(*Option);
/* Zero out and fill the option header */
RtlZeroMemory(Option, DataSize);
Option->Type = PathType;
Option->DataOffset = sizeof(*Option);
/* Extract the string option */
StringEntry = (PWCHAR)(Option + 1);
PathString = StringEntry;
/* Start parsing the device path */
FilePath = (FILEPATH_DEVICE_PATH*)DevicePath;
while (IsDevicePathEndType(FilePath) == FALSE)
{
/* Is this a file path? */
if ((FilePath->Header.Type == MEDIA_DEVICE_PATH) &&
(FilePath->Header.SubType == MEDIA_FILEPATH_DP))
{
/* Get the length of the file path string, avoiding overflow */
StringLength = DevicePathNodeLength(FilePath) -
FIELD_OFFSET(FILEPATH_DEVICE_PATH, PathName);
if (StringLength < (ULONG)FIELD_OFFSET(FILEPATH_DEVICE_PATH, PathName))
{
Status = STATUS_INTEGER_OVERFLOW;
goto Quickie;
}
/* Append this path string to the current path string */
Status = EfiInitpAppendPathString(PathString,
MaximumLength,
FilePath->PathName,
StringLength,
&BytesAppended);
if (!NT_SUCCESS(Status))
{
return Status;
}
/* Increase the size of the data, consume buffer space */
DataSize += BytesAppended;
MaximumLength -= BytesAppended;
/* Move to the next path string */
PathString = (PWCHAR)((ULONG_PTR)PathString + BytesAppended);
}
/* Move to the next path node */
FilePath = (FILEPATH_DEVICE_PATH*)NextDevicePathNode(FilePath);
}
/* Check if we still have space for a NULL-terminator */
if (MaximumLength < sizeof(UNICODE_NULL))
{
Status = STATUS_INVALID_PARAMETER;
goto Quickie;
}
/* We do -- NULL-terminate the string */
*PathString = UNICODE_NULL;
DataSize += sizeof(UNICODE_NULL);
/* Check if all of this has amounted to a single NULL-char */
if (PathString == StringEntry)
{
/* Then this option is empty */
Option->Empty = TRUE;
}
/* Set the final size of the option */
Option->DataSize = DataSize;
Quickie:
return STATUS_SUCCESS;
}
/*++
* @name EfiInitpGetDeviceNode
*
* The EfiInitpGetDeviceNode routine
*
* @param DevicePath
* UEFI Image Handle for the current loaded application.
*
* @return None
*
*--*/
EFI_DEVICE_PATH_PROTOCOL*
EfiInitpGetDeviceNode (
_In_ EFI_DEVICE_PATH_PROTOCOL *DevicePath
)
{
EFI_DEVICE_PATH_PROTOCOL* NextPath;
/* Check if we hit the end terminator */
if (IsDevicePathEndType(DevicePath))
{
return DevicePath;
}
/* Loop each device path, until we get to the end or to a file path device node */
for ((NextPath = NextDevicePathNode(DevicePath));
!(IsDevicePathEndType(NextPath)) && ((NextPath->Type != MEDIA_DEVICE_PATH) ||
(NextPath->SubType != MEDIA_FILEPATH_DP));
(NextPath = NextDevicePathNode(NextPath)))
{
/* Keep iterating down */
DevicePath = NextPath;
}
/* Return the path found */
return DevicePath;
}
/*++
* @name EfiInitTranslateDevicePath
*
* The EfiInitTranslateDevicePath routine
*
* @param DevicePath
* UEFI Image Handle for the current loaded application.
*
* @param DeviceEntry
* Pointer to the UEFI System Table.
*
* @return None
*
*--*/
NTSTATUS
EfiInitTranslateDevicePath (
_In_ EFI_DEVICE_PATH_PROTOCOL *DevicePath,
_In_ PBL_DEVICE_DESCRIPTOR DeviceEntry
)
{
NTSTATUS Status;
EFI_DEVICE_PATH_PROTOCOL* DeviceNode;
MEMMAP_DEVICE_PATH* MemDevicePath;
ACPI_HID_DEVICE_PATH *AcpiPath;
HARDDRIVE_DEVICE_PATH *DiskPath;
/* Assume failure */
Status = STATUS_UNSUCCESSFUL;
/* Set size first */
DeviceEntry->Size = sizeof(*DeviceEntry);
/* Check if we are booting from a RAM Disk */
if ((DevicePath->Type == HARDWARE_DEVICE_PATH) &&
(DevicePath->SubType == HW_MEMMAP_DP))
{
/* Get the EFI data structure matching this */
MemDevicePath = (MEMMAP_DEVICE_PATH*)DevicePath;
/* Set the boot library specific device types */
DeviceEntry->DeviceType = LocalDevice;
DeviceEntry->Local.Type = RamDiskDevice;
/* Extract the base, size, and offset */
DeviceEntry->Local.RamDisk.ImageBase.QuadPart = MemDevicePath->StartingAddress;
DeviceEntry->Local.RamDisk.ImageSize.QuadPart = MemDevicePath->EndingAddress -
MemDevicePath->StartingAddress;
DeviceEntry->Local.RamDisk.ImageOffset = 0;
return STATUS_SUCCESS;
}
/* Otherwise, check what kind of device node this is */
DeviceNode = EfiInitpGetDeviceNode(DevicePath);
switch (DeviceNode->Type)
{
/* ACPI */
case ACPI_DEVICE_PATH:
/* We only support floppy drives */
AcpiPath = (ACPI_HID_DEVICE_PATH*)DeviceNode;
if ((AcpiPath->HID != EISA_PNP_ID(0x604)) &&
(AcpiPath->HID != EISA_PNP_ID(0x700)))
{
return Status;
}
/* Set the boot library specific device types */
DeviceEntry->DeviceType = LocalDevice;
DeviceEntry->Local.Type = FloppyDevice;
/* The ACPI UID is the drive number */
DeviceEntry->Local.FloppyDisk.DriveNumber = AcpiPath->UID;
return STATUS_SUCCESS;
/* Network, ATAPI, SCSI, USB */
case MESSAGING_DEVICE_PATH:
/* Check if it's network */
if ((DeviceNode->SubType == MSG_MAC_ADDR_DP) ||
(DeviceNode->SubType == MSG_IPv4_DP))
{
/* Set the boot library specific device types */
DeviceEntry->DeviceType = UdpDevice;
DeviceEntry->Remote.Unknown = 256;
return STATUS_SUCCESS;
}
/* Other types should come in as MEDIA_DEVICE_PATH -- Windows assumes this is a floppy */
DeviceEntry->DeviceType = DiskDevice;
DeviceEntry->Local.Type = FloppyDevice;
DeviceEntry->Local.FloppyDisk.DriveNumber = 0;
return STATUS_SUCCESS;
/* Disk or CDROM */
case MEDIA_DEVICE_PATH:
/* Extract the disk path and check if it's a physical disk */
DiskPath = (HARDDRIVE_DEVICE_PATH*)DeviceNode;
if (DeviceNode->SubType == MEDIA_HARDDRIVE_DP)
{
/* Check if this is an MBR partition */
if (DiskPath->SignatureType == SIGNATURE_TYPE_MBR)
{
/* Set that this is a local partition */
DeviceEntry->DeviceType = LegacyPartitionDevice;
DeviceEntry->Partition.Disk.Type = LocalDevice;
DeviceEntry->Partition.Disk.HardDisk.PartitionType = MbrPartition;
DeviceEntry->Partition.Disk.HardDisk.Mbr.PartitionSignature =
*(PULONG)&DiskPath->Signature[0];
DeviceEntry->Partition.Mbr.PartitionNumber = DiskPath->PartitionNumber;
return STATUS_SUCCESS;
}
/* Check if it's a GPT partition */
if (DiskPath->SignatureType == SIGNATURE_TYPE_GUID)
{
/* Set that this is a local disk */
DeviceEntry->DeviceType = PartitionDevice;
DeviceEntry->Partition.Disk.Type = LocalDevice;
/* Set GPT partition ID */
DeviceEntry->Partition.Disk.HardDisk.PartitionType = GptPartition;
/* Copy the signature GUID */
RtlCopyMemory(&DeviceEntry->Partition.Gpt.PartitionGuid,
DiskPath->Signature,
sizeof(GUID));
DeviceEntry->Flags |= 4u;
return STATUS_SUCCESS;
}
/* Otherwise, raw boot is not supported */
DeviceEntry->DeviceType = PartitionDevice;
DeviceEntry->Partition.Disk.Type = LocalDevice;
DeviceEntry->Partition.Disk.HardDisk.PartitionType = RawPartition;
DeviceEntry->Partition.Disk.HardDisk.Raw.DiskNumber = 0;
}
else if (DeviceNode->SubType == MEDIA_CDROM_DP)
{
/* Set the right type for a CDROM */
DeviceEntry->DeviceType = DiskDevice;
DeviceEntry->Local.Type = CdRomDevice;
/* Set the drive number to zero */
DeviceEntry->Local.FloppyDisk.DriveNumber = 0;
return STATUS_SUCCESS;
}
/* Fail anything else */
default:
break;
}
/* Return here only on failure */
return Status;
}
/*++
* @name EfiInitpConvertEfiDevicePath
*
* The EfiInitpConvertEfiDevicePath routine
*
* @param DevicePath
* UEFI Image Handle for the current loaded application.
*
* @param DeviceType
* Pointer to the UEFI System Table.
*
* @param Option
* Pointer to the UEFI System Table.
*
* @param MaximumLength
* Pointer to the UEFI System Table.
*
* @return None
*
*--*/
NTSTATUS
EfiInitpConvertEfiDevicePath (
_In_ EFI_DEVICE_PATH_PROTOCOL *DevicePath,
_In_ ULONG DeviceType,
_In_ PBL_BCD_OPTION Option,
_In_ ULONG MaximumLength
)
{
PBCD_DEVICE_OPTION BcdDevice;
NTSTATUS Status;
/* Make sure we have enough space for the option */
if (MaximumLength < sizeof(*Option))
{
Status = STATUS_INVALID_PARAMETER;
goto Quickie;
}
/* Zero out the option */
RtlZeroMemory(Option, sizeof(*Option));
/* Make sure we have enough space for the device entry */
if ((MaximumLength - sizeof(*Option)) <
(ULONG)FIELD_OFFSET(BCD_DEVICE_OPTION, DeviceDescriptor))
{
Status = STATUS_INVALID_PARAMETER;
goto Quickie;
}
/* Fill it out */
BcdDevice = (PBCD_DEVICE_OPTION)(Option + 1);
Status = EfiInitTranslateDevicePath(DevicePath,
&BcdDevice->DeviceDescriptor);
if (!NT_SUCCESS(Status))
{
goto Quickie;
}
/* Fill out the rest of the option structure */
Option->DataOffset = sizeof(*Option);
Option->Type = DeviceType;
Option->DataSize = FIELD_OFFSET(BCD_DEVICE_OPTION, DeviceDescriptor) +
BcdDevice->DeviceDescriptor.Size;
Status = STATUS_SUCCESS;
Quickie:
return Status;
}
/*++
* @name EfiInitpCreateApplicationEntry
*
* The EfiInitpCreateApplicationEntry routine
*
* @param SystemTable
* UEFI Image Handle for the current loaded application.
*
* @param Entry
* Pointer to the UEFI System Table.
*
* @param MaximumLength
* Pointer to the UEFI System Table.
*
* @param DevicePath
* Pointer to the UEFI System Table.
*
* @param FilePath
* Pointer to the UEFI System Table.
*
* @param LoadOptions
* Pointer to the UEFI System Table.
*
* @param LoadOptionsSize
* Pointer to the UEFI System Table.
*
* @param Flags
* Pointer to the UEFI System Table.
*
* @param ResultLength
* Pointer to the UEFI System Table.
*
* @param AppEntryDevice
* Pointer to the UEFI System Table.
*
* @return None
*
*--*/
VOID
EfiInitpCreateApplicationEntry (
__in EFI_SYSTEM_TABLE *SystemTable,
__in PBL_APPLICATION_ENTRY Entry,
__in ULONG MaximumLength,
__in EFI_DEVICE_PATH *DevicePath,
__in EFI_DEVICE_PATH *FilePath,
__in PWCHAR LoadOptions,
__in ULONG LoadOptionsSize,
__in ULONG Flags,
__out PULONG ResultLength,
__out PBL_DEVICE_DESCRIPTOR *AppEntryDevice
)
{
PBL_WINDOWS_LOAD_OPTIONS WindowsOptions;
PWCHAR ObjectString, CommandLine;
PBL_BCD_OPTION Option, PreviousOption;
ULONG HeaderSize, TotalOptionSize, Size, CommandLineSize, RemainingSize;
NTSTATUS Status;
UNICODE_STRING GuidString;
GUID ObjectGuid;
PBCD_DEVICE_OPTION BcdDevice;
BOOLEAN HaveBinaryOptions, HaveGuid;
PBL_FILE_PATH_DESCRIPTOR OsPath;
EFI_DEVICE_PATH *OsDevicePath;
/* Initialize everything */
TotalOptionSize = 0;
*AppEntryDevice = NULL;
HeaderSize = 0;
/* Check if the load options are in binary Windows format */
WindowsOptions = (PBL_WINDOWS_LOAD_OPTIONS)LoadOptions;
if ((WindowsOptions != NULL) &&
(LoadOptionsSize >= sizeof(BL_WINDOWS_LOAD_OPTIONS)) &&
(WindowsOptions->Length >= sizeof(BL_WINDOWS_LOAD_OPTIONS)) &&
!(strncmp(WindowsOptions->Signature, "WINDOWS", 7)))
{
/* They are, so firmware must have loaded us -- extract arguments */
CommandLine = WindowsOptions->LoadOptions;
CommandLineSize = LoadOptionsSize - FIELD_OFFSET(BL_WINDOWS_LOAD_OPTIONS,
LoadOptions);
/* Remember that we used binary options */
HaveBinaryOptions = TRUE;
}
else
{
/* Nope -- so treat them as raw command-line options */
CommandLine = LoadOptions;
CommandLineSize = LoadOptionsSize;
/* No binary options */
HaveBinaryOptions = FALSE;
}
/* EFI uses UTF-16LE, like NT, so convert to characters */
CommandLineSize /= sizeof(WCHAR);
if (CommandLineSize != 0)
{
/* And check if the options are not NULL-terminated */
if (wcsnlen(CommandLine, CommandLineSize) == CommandLineSize)
{
/* NULL-terminate them */
CommandLine[CommandLineSize - 1] = UNICODE_NULL;
}
}
/* Begin by making sure we at least have space for the app entry header */
RemainingSize = MaximumLength;
if (RemainingSize < sizeof(BL_APPLICATION_ENTRY))
{
Status = STATUS_INVALID_PARAMETER;
goto Quickie;
}
/* On exit, return that we've at least consumed this much */
HeaderSize = FIELD_OFFSET(BL_APPLICATION_ENTRY, BcdData);
/* Zero out the header, and write down the signature */
RtlZeroMemory(Entry, sizeof(BL_APPLICATION_ENTRY));
RtlCopyMemory(Entry->Signature, BL_APP_ENTRY_SIGNATURE, 7);
/* Check if a BCD object was passed on the command-line */
ObjectString = wcsstr(CommandLine, L"BCDOBJECT=");
if (ObjectString != NULL)
{
/* Convert the BCD object to a GUID */
RtlInitUnicodeString(&GuidString, ObjectString + 10);
RtlGUIDFromString(&GuidString, &ObjectGuid);
/* Store it in the application entry */
Entry->Guid = ObjectGuid;
/* Remember one was passed */
HaveGuid = TRUE;
}
else
{
/* Remember that no identifier was passed */
Entry->Flags |= BL_APPLICATION_ENTRY_FLAG_NO_GUID;
HaveGuid = FALSE;
}
/* At this point, the header is consumed, and we must now handle BCD options */
RemainingSize -= FIELD_OFFSET(BL_APPLICATION_ENTRY, BcdData);
/* Convert the device path into a BCD option */
Status = EfiInitpConvertEfiDevicePath(DevicePath,
BcdLibraryDevice_ApplicationDevice,
&Entry->BcdData,
RemainingSize);
if (!NT_SUCCESS(Status))
{
/* We failed, so mark the option as such and return an empty one */
Entry->BcdData.Empty = TRUE;
TotalOptionSize = sizeof(BL_BCD_OPTION);
goto Quickie;
}
/* Extract the device descriptor and return it */
BcdDevice = (PVOID)((ULONG_PTR)&Entry->BcdData + Entry->BcdData.DataOffset);
*AppEntryDevice = &BcdDevice->DeviceDescriptor;
/* Calculate how big this option was and consume that from the buffer */
TotalOptionSize = BlGetBootOptionSize(&Entry->BcdData);
RemainingSize -= TotalOptionSize;
/* Calculate where the next option should go */
Option = (PVOID)((ULONG_PTR)&Entry->BcdData + TotalOptionSize);
/* Check if we're PXE booting or not */
if ((*AppEntryDevice)->DeviceType == UdpDevice)
{
/* lol */
Status = STATUS_NOT_IMPLEMENTED;
}
else
{
/* Convert the local file path into a BCD option */
Status = EfiInitpConvertEfiFilePath(FilePath,
BcdLibraryString_ApplicationPath,
Option,
RemainingSize);
}
/* Bail out on failure */
if (!NT_SUCCESS(Status))
{
goto Quickie;
}
/* The next option is right after this one */
Entry->BcdData.NextEntryOffset = TotalOptionSize;
/* Now compute the size of the next option, and add to the rolling sum */
Size = BlGetBootOptionSize(Option);
TotalOptionSize += Size;
/* Remember the previous option so we can update its next offset */
PreviousOption = Option;
/* Consume the option from the buffer */
RemainingSize -= Size;
/* Calculate where the next option should go */
Option = (PVOID)((ULONG_PTR)Option + Size);
/* Check if we were using binary options without a BCD GUID */
if ((HaveBinaryOptions) && !(HaveGuid))
{
/* Then this means we have to convert the OS paths to BCD too */
WindowsOptions = (PBL_WINDOWS_LOAD_OPTIONS)LoadOptions;
OsPath = (PVOID)((ULONG_PTR)WindowsOptions + WindowsOptions->OsPathOffset);
/* IS the OS path in EFI format? */
if ((OsPath->Length > (ULONG)FIELD_OFFSET(BL_FILE_PATH_DESCRIPTOR, Path)) &&
(OsPath->PathType == EfiPath))
{
/* Convert the device portion */
OsDevicePath = (EFI_DEVICE_PATH*)OsPath->Path;
Status = EfiInitpConvertEfiDevicePath(OsDevicePath,
BcdOSLoaderDevice_OSDevice,
Option,
RemainingSize);
if (!NT_SUCCESS(Status))
{
goto Quickie;
}
/* Update the offset of the previous option */
PreviousOption->NextEntryOffset = (ULONG_PTR)Option - (ULONG_PTR)&Entry->BcdData;
/* Now compute the size of the next option, and add to the rolling sum */
Size = BlGetBootOptionSize(Option);
TotalOptionSize += Size;
/* Remember the previous option so we can update its next offset */
PreviousOption = Option;
/* Consume the option from the buffer */
RemainingSize -= Size;
/* Calculate where the next option should go */
Option = (PVOID)((ULONG_PTR)Option + Size);
/* Convert the path option */
Status = EfiInitpConvertEfiFilePath(OsDevicePath,
BcdOSLoaderString_SystemRoot,
Option,
RemainingSize);
if (!NT_SUCCESS(Status))
{
goto Quickie;
}
/* Update the offset of the previous option */
PreviousOption->NextEntryOffset = (ULONG_PTR)Option - (ULONG_PTR)&Entry->BcdData;
/* Now compute the size of the next option, and add to the rolling sum */
Size = BlGetBootOptionSize(Option);
TotalOptionSize += Size;
/* Remember the previous option so we can update its next offset */
PreviousOption = Option;
/* Consume the option from the buffer */
RemainingSize -= Size;
/* Calculate where the next option should go */
Option = (PVOID)((ULONG_PTR)Option + Size);
}
}
/* Now convert everything else */
AhCreateLoadOptionsList(CommandLine,
&Entry->BcdData,
RemainingSize,
&TotalOptionSize,
&PreviousOption,
&Size);
Quickie:
/* Return the final size */
*ResultLength = HeaderSize + TotalOptionSize;
}
/*++
* @name EfiInitCreateInputParametersEx
*
* The EfiInitCreateInputParametersEx routine converts UEFI entrypoint
* parameters to the ones expected by Windows Boot Applications
*
* @param ImageHandle
* UEFI Image Handle for the current loaded application.
*
* @param SystemTable
* Pointer to the UEFI System Table.
*
* @return A PBOOT_APPLICATION_PARAMETER_BLOCK structure containing the data
* from UEFI, translated to the Boot Library-compatible format.
*
*--*/
PBOOT_APPLICATION_PARAMETER_BLOCK
EfiInitCreateInputParametersEx (
_In_ EFI_HANDLE ImageHandle,
_In_ EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_BOOT_SERVICES* BootServices;
EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
PBL_FIRMWARE_DESCRIPTOR FirmwareData;
PBL_RETURN_ARGUMENTS ReturnArguments;
ULONG FirmwareOffset, ConsumedSize;
PBL_DEVICE_DESCRIPTOR AppDevice;
EFI_STATUS Status;
/* Initialize the header with the signature and version */
EfiInitScratch.Signature[0] = BOOT_APPLICATION_SIGNATURE_1;
EfiInitScratch.Signature[1] = BOOT_APPLICATION_SIGNATURE_2;
EfiInitScratch.Version = BOOT_APPLICATION_VERSION;
/* Set the image type to x86 */
EfiInitScratch.ImageType = EFI_IMAGE_MACHINE_IA32;
/* Set the translation type to physical */
EfiInitScratch.MemoryTranslationType = BOOT_MEMORY_TRANSLATION_TYPE_PHYSICAL;
/* Indicate that the data was converted from EFI */
BlpApplicationFlags |= BL_APPLICATION_FLAG_CONVERTED_FROM_EFI;
/* Grab the loaded image protocol, which has our base and size */
BootServices = SystemTable->BootServices;
Status = BootServices->HandleProtocol(ImageHandle,
&EfiLoadedImageProtocol,
(VOID**)&LoadedImage);
if (Status != EFI_SUCCESS)
{
return NULL;
}
/* Capture it in the boot application parameters */
EfiInitScratch.ImageBase = (ULONG_PTR)LoadedImage->ImageBase;
EfiInitScratch.ImageSize = (ULONG)LoadedImage->ImageSize;
/* Now grab our device path protocol, so we can convert the path later on */
Status = BootServices->HandleProtocol(LoadedImage->DeviceHandle,
&EfiDevicePathProtocol,
(VOID**)&DevicePath);
if (Status != EFI_SUCCESS)
{
return NULL;
}
/* The built-in boot memory data comes right after our block */
EfiInitScratch.MemoryDataOffset =
FIELD_OFFSET(BOOT_APPLICATION_PARAMETER_BLOCK_SCRATCH, BootMemoryData);
/* Build the boot memory data structure, with 1 descriptor */
EfiInitScratch.BootMemoryData.Version = BL_MEMORY_DATA_VERSION;
EfiInitScratch.BootMemoryData.MdListOffset =
FIELD_OFFSET(BOOT_APPLICATION_PARAMETER_BLOCK_SCRATCH, MemEntry) -
EfiInitScratch.MemoryDataOffset;
EfiInitScratch.BootMemoryData.DescriptorSize = sizeof(BL_MEMORY_DESCRIPTOR);
EfiInitScratch.BootMemoryData.DescriptorCount = 1;
EfiInitScratch.BootMemoryData.DescriptorOffset = FIELD_OFFSET(BL_MEMORY_DESCRIPTOR, BasePage);
/* Build the memory entry descriptor for this image itself */
EfiInitScratch.MemEntry.Flags = BlMemoryWriteBack;
EfiInitScratch.MemEntry.Type = BlLoaderMemory;
EfiInitScratch.MemEntry.BasePage = EfiInitScratch.ImageBase >> PAGE_SHIFT;
EfiInitScratch.MemEntry.PageCount = ALIGN_UP_BY(EfiInitScratch.ImageSize, PAGE_SIZE) >> PAGE_SHIFT;
/* The built-in application entry comes right after the memory descriptor*/
EfiInitScratch.AppEntryOffset =
FIELD_OFFSET(BOOT_APPLICATION_PARAMETER_BLOCK_SCRATCH, AppEntry);
/* Go and build it */
EfiInitpCreateApplicationEntry(SystemTable,
(PBL_APPLICATION_ENTRY)&EfiInitScratch.AppEntry,
sizeof(EfiInitScratch.AppEntry),
DevicePath,
LoadedImage->FilePath,
LoadedImage->LoadOptions,
LoadedImage->LoadOptionsSize,
EfiInitScratch.MemEntry.PageCount,
&ConsumedSize,
&AppDevice);
/* Boot device information comes right after the application entry */
EfiInitScratch.BootDeviceOffset = ConsumedSize + EfiInitScratch.AppEntryOffset;
/* Check if we have a boot device */
if (AppDevice != NULL)
{
/* We do -- copy it */
RtlCopyMemory(EfiInitScratch.AppEntry + ConsumedSize,
AppDevice,
AppDevice->Size);
/* Firmware data follows right after the boot device entry */
FirmwareOffset = AppDevice->Size + EfiInitScratch.BootDeviceOffset;
}
else
{
/* We do not, so zero out the space where a full boot device structure would fit */
RtlZeroMemory(EfiInitScratch.AppEntry + ConsumedSize,
sizeof(BL_DEVICE_DESCRIPTOR));
/* And start the firmware data past that */
FirmwareOffset = EfiInitScratch.BootDeviceOffset + sizeof(BL_DEVICE_DESCRIPTOR);
}
/* Set the computed firmware data offset */
EfiInitScratch.FirmwareParametersOffset = FirmwareOffset;
/* Fill out the firmware data that's there */
FirmwareData = (PVOID)((ULONG_PTR)&EfiInitScratch + EfiInitScratch.FirmwareParametersOffset);
FirmwareData->Version = BL_FIRMWARE_DESCRIPTOR_VERSION;
FirmwareData->ImageHandle = ImageHandle;
FirmwareData->SystemTable = SystemTable;
/* Finally, set the return argument offset */
EfiInitScratch.ReturnArgumentsOffset = FirmwareOffset + sizeof(BL_FIRMWARE_DESCRIPTOR);
/* And fill out the return argument data */
ReturnArguments = (PVOID)((ULONG_PTR)&EfiInitScratch + EfiInitScratch.ReturnArgumentsOffset);
ReturnArguments->Version = BL_RETURN_ARGUMENTS_VERSION;
/* We're done, compute the final size and return the block */
EfiInitScratch.Size = EfiInitScratch.ReturnArgumentsOffset + sizeof(BL_RETURN_ARGUMENTS);
return (PBOOT_APPLICATION_PARAMETER_BLOCK)&EfiInitScratch;
}
/*++
* @name EfiEntry
*
* The EfiEntry routine implements the UEFI entrypoint for the application.
*
* @param ImageHandle
* UEFI Image Handle for the current loaded application.
*
* @param SystemTable
* Pointer to the UEFI System Table.
*
* @return EFI_SUCCESS if the image was loaded correctly, relevant error code
* otherwise.
*
*--*/
EFI_STATUS
EFIAPI
EfiEntry (
_In_ EFI_HANDLE ImageHandle,
_In_ EFI_SYSTEM_TABLE *SystemTable
)
{
NTSTATUS Status;
PBOOT_APPLICATION_PARAMETER_BLOCK BootParameters;
/* Convert EFI parameters to Windows Boot Application parameters */
BootParameters = EfiInitCreateInputParametersEx(ImageHandle, SystemTable);
if (BootParameters != NULL)
{
/* Conversion was good -- call the Boot Manager Entrypoint */
Status = BmMain(BootParameters);
}
else
{
/* Conversion failed, bail out */
Status = STATUS_INVALID_PARAMETER;
}
/* Convert the NT status code to an EFI code */
return EfiGetEfiStatusCode(Status);
}