mirror of
https://github.com/reactos/reactos.git
synced 2024-11-20 06:15:26 +00:00
1034 lines
33 KiB
C
1034 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);
|
|
}
|
|
|