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