[BOOTLIB]
[reactos.git] / reactos / boot / environ / lib / misc / image.c
index c155930..767fb89 100644 (file)
@@ -17,6 +17,14 @@ ULONG IapAllocatedTableEntries;
 ULONG IapTableEntries;
 PVOID* IapImageTable;
 
+KDESCRIPTOR GdtRegister;
+KDESCRIPTOR IdtRegister;
+KDESCRIPTOR BootAppGdtRegister;
+KDESCRIPTOR BootAppIdtRegister;
+PVOID BootApp32EntryRoutine;
+PBOOT_APPLICATION_PARAMETER_BLOCK BootApp32Parameters;
+PVOID BootApp32Stack;
+
 /* FUNCTIONS *****************************************************************/
 
 NTSTATUS
@@ -186,8 +194,7 @@ ImgpCloseFile (
         if (File->Flags & BL_IMG_REMOTE_FILE)
         {
             /* Then only free the memory in that scenario */
-            EfiPrintf(L"TODO\r\n");
-            //return MmPapFreePages(File->BaseAddress, TRUE);
+            return MmPapFreePages(File->BaseAddress, BL_MM_INCLUDE_MAPPED_ALLOCATED);
         }
     }
 
@@ -202,8 +209,37 @@ BlImgUnallocateImageBuffer (
     _In_ ULONG ImageFlags
     )
 {
-    EfiPrintf(L"leaking the shit out of %p\r\n", ImageBase);
-    return STATUS_NOT_IMPLEMENTED;
+    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
@@ -471,6 +507,7 @@ BlImgLoadImageWithProgress2 (
 
     /* 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;
@@ -525,7 +562,7 @@ BlImgLoadImageWithProgress2 (
     }
 
     /* Is the read fully complete? We need to finalize the hash if requested */
-    if (ComputeHash != RemainingLength)
+    if (ComputeHash)
     {
         // todo: CRYPTO IS HARD
     }
@@ -554,8 +591,7 @@ Quickie:
         else
         {
             /* Free the physical buffer */
-            //MmPapFreePages(VirtualAddress, 1);
-            EfiPrintf(L"Leaking memory\r\n");
+            MmPapFreePages(BaseAddress, BL_MM_INCLUDE_MAPPED_ALLOCATED);
         }
     }
 
@@ -651,95 +687,48 @@ BlImgUnLoadImage (
     return BlImgUnallocateImageBuffer(ImageBase, ImageSize, ImageFlags);
 }
 
-unsigned int  BlUtlCheckSum(unsigned int PartialSum, PUCHAR Source, unsigned int Length, unsigned int Flags)
-{
-    unsigned int Type; // eax@1
-    int Type1; // eax@1
-    unsigned int AlignedLength; // ebx@3
-    unsigned int i; // ebx@21 MAPDST
-
-    Type = Flags & 3;
-    Type1 = Type - 1;
-    if (Type1)
-    {
-        if (Type1 == 1)
-        {
-            PartialSum = (unsigned __int16)PartialSum;
-            AlignedLength = Length & ~1;
-            if (Length & ~1)
-            {
-                i = 0;
-                do
-                {
-                    PartialSum += *(unsigned __int16 *)&Source[i];
-                    if (Flags & 0x10000)
-                        PartialSum = (unsigned __int16)((PartialSum >> 16) + PartialSum);
-                    i += 2;
-                } while (i < AlignedLength);
-            }
-
-            if (Length != AlignedLength)
-            {
-                PartialSum += (unsigned __int8)Source[AlignedLength];
-                if (Flags & 0x10000)
-                    PartialSum = (unsigned __int16)((PartialSum >> 16) + PartialSum);
-            }
-            if (Flags & 0x40000)
-                return ~PartialSum;
-            PartialSum = (unsigned __int16)PartialSum;
-        }
-    }
-    else
-    {
-        EfiPrintf(L"checksum type not supported\r\n");
-    }
-
-    if (Flags & 0x40000)
-        return ~PartialSum;
-    return PartialSum;
-}
-
 NTSTATUS
 ImgpLoadPEImage (
     _In_ PBL_IMG_FILE ImageFile,
     _In_ BL_MEMORY_TYPE MemoryType,
     _Inout_ PVOID* ImageBase,
-    _Out_ PULONG ImageSize,
+    _Out_opt_ PULONG ImageSize,
     _Inout_opt_ PVOID Hash,
     _In_ ULONG Flags
     )
 {
     NTSTATUS Status;
     ULONG FileSize, HeaderSize;
-    PVOID ImageBuffer;
     BL_IMG_FILE LocalFileBuffer;
     PBL_IMG_FILE LocalFile;
-    PVOID VirtualAddress;
+    PVOID VirtualAddress, PreferredBase, ImageBuffer, CertBuffer, HashBuffer;
     ULONGLONG VirtualSize;
+    PIMAGE_DATA_DIRECTORY CertDirectory;
     PHYSICAL_ADDRESS PhysicalAddress;
     PIMAGE_NT_HEADERS NtHeaders;
-    USHORT SectionCount;
-    USHORT CheckSum, PartialSum;
+    USHORT SectionCount, CheckSum, PartialSum, FinalSum;
     PIMAGE_SECTION_HEADER Section;
-    ULONG_PTR EndOfHeaders, SectionStart;
-    ULONG i;
-    BOOLEAN First;
-    ULONG_PTR Slack, SectionEnd;
-    ULONG SectionSize, RawSize;
-    ULONG BytesRead, RemainingLength;
+    ULONG_PTR EndOfHeaders, SectionStart, Slack, SectionEnd;
+    ULONG i, SectionSize, RawSize, BytesRead, RemainingLength, Offset, AlignSize;
+    BOOLEAN First, ImageHashValid;
     UCHAR LocalBuffer[1024];
-    USHORT FinalSum;
-    ULONG Offset;
-    ULONG AlignSize;
+    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);
@@ -921,7 +910,11 @@ ImgpLoadPEImage (
     NtHeaders->OptionalHeader.CheckSum = 0;
 
     /* Calculate the checksum of the header, and restore the original one */
-    PartialSum = BlUtlCheckSum(0, VirtualAddress, HeaderSize, 0x10002);
+    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) */
@@ -938,6 +931,7 @@ ImgpLoadPEImage (
         if ((VirtualSize < Section->VirtualAddress) ||
             ((PVOID)SectionStart < VirtualAddress))
         {
+            EfiPrintf(L"fail 1\r\n");
             Status = STATUS_INVALID_IMAGE_FORMAT;
             goto Quickie;
         }
@@ -985,6 +979,7 @@ ImgpLoadPEImage (
                              &SectionEnd);
         if (!NT_SUCCESS(Status))
         {
+            EfiPrintf(L"fail 21\r\n");
             Status = STATUS_INVALID_IMAGE_FORMAT;
             goto Quickie;
         }
@@ -1002,6 +997,7 @@ ImgpLoadPEImage (
                              &SectionEnd);
         if (!NT_SUCCESS(Status))
         {
+            EfiPrintf(L"fail 31\r\n");
             Status = STATUS_INVALID_IMAGE_FORMAT;
             goto Quickie;
         }
@@ -1019,6 +1015,23 @@ ImgpLoadPEImage (
             /* 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,
@@ -1037,7 +1050,9 @@ ImgpLoadPEImage (
                 PartialSum = BlUtlCheckSum(PartialSum,
                                            (PUCHAR)SectionStart,
                                            AlignSize,
-                                           0x10002);
+                                           BL_UTL_CHECKSUM_COMPLEMENT |
+                                           BL_UTL_CHECKSUM_USHORT_BUFFER);
+                AlignSize += WorkaroundForBinutils;
             }
         }
 
@@ -1114,11 +1129,12 @@ ImgpLoadPEImage (
             PartialSum = BlUtlCheckSum(PartialSum,
                                        LocalBuffer,
                                        BytesRead,
-                                       0x10002);
+                                       BL_UTL_CHECKSUM_COMPLEMENT |
+                                       BL_UTL_CHECKSUM_USHORT_BUFFER);
         }
 
         /* Finally, calculate the final checksum and compare it */
-        FinalSum = FileSize + PartialSum;
+        FinalSum = FileSize + PartialSum + WorkaroundForBinutils;
         if ((FinalSum != CheckSum) && (PartialSum == 0xFFFF))
         {
             /* It hit overflow, so set it to the file size */
@@ -1126,18 +1142,94 @@ ImgpLoadPEImage (
         }
 
         /* If the checksum doesn't match, and caller is enforcing, bail out */
-        EfiPrintf(L"Final checksum: %lx. Original: %lx\r\n", FinalSum, CheckSum);
-        if ((FinalSum != CheckSum) && !(Flags & 0x10000))
+        if ((FinalSum != CheckSum) &&
+            !(Flags & BL_LOAD_PE_IMG_IGNORE_CHECKSUM_MISMATCH))
         {
             Status = STATUS_IMAGE_CHECKSUM_MISMATCH;
             goto Quickie;
         }
     }
 
-    EfiPrintf(L"MORE PE TODO: %lx\r\n", NtHeaders->OptionalHeader.AddressOfEntryPoint);
-    EfiStall(100000000);
+    /* 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))
     {
@@ -1169,8 +1261,7 @@ Quickie:
             else
             {
                 /* Into a physical buffer -- free it */
-                EfiPrintf(L"Leaking physical pages\r\n");
-               // MmPapFreePages(VirtualAddress, TRUE);
+                MmPapFreePages(VirtualAddress, BL_MM_INCLUDE_MAPPED_ALLOCATED);
             }
         }
     }
@@ -1254,6 +1345,7 @@ BlImgLoadBootApplication (
     DeviceId = -1;
     Device = NULL;
     ImageAppEntry = NULL;
+    AppEntry = NULL;
     Path = NULL;
     ImageSize = 0;
     ImageBase = NULL;
@@ -1401,7 +1493,7 @@ BlImgLoadBootApplication (
     }
 
     /* Zero it out */
-    RtlZeroMemory(AppEntry, sizeof(AppEntry));
+    RtlZeroMemory(AppEntry, sizeof(*AppEntry));
 
     /* Initialize it */
     strcpy(AppEntry->Signature, "BTAPENT");
@@ -1438,7 +1530,7 @@ BlImgLoadBootApplication (
         }
 
         /* Zero out the entries for now */
-        RtlZeroMemory(IapImageTable, sizeof(IapTableEntries * sizeof(PVOID)));
+        RtlZeroMemory(IapImageTable, IapTableEntries * sizeof(PVOID));
     }
 
     /* Set this entry into the table */
@@ -1538,6 +1630,200 @@ BlpPdParseReturnArguments (
     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_IMAGE_PARAMETERS ImageParameters,
+    _In_ PBL_APPLICATION_ENTRY AppEntry,
+    _In_ PVOID ImageBase, 
+    _In_ ULONG ImageSize
+    )
+{
+    NTSTATUS Status;
+    PIMAGE_NT_HEADERS NtHeaders;
+    BL_IMAGE_PARAMETERS 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,
@@ -1546,7 +1832,92 @@ ImgArchEfiStartBootApplication (
     _In_ PBL_RETURN_ARGUMENTS ReturnArguments
     )
 {
-    /* Not yet implemented. This is the last step! */
+    KDESCRIPTOR Gdt, Idt;
+    ULONG BootSizeNeeded;
+    NTSTATUS Status;
+    PVOID BootData;
+    PIMAGE_NT_HEADERS NtHeaders;
+    PVOID NewStack, NewGdt, NewIdt;
+    BL_IMAGE_PARAMETERS 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)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);
+    }
+
+    /* All done */
     return STATUS_NOT_IMPLEMENTED;
 }