xref: /reactos/boot/environ/lib/misc/image.c (revision 8c2e9189)
1 /*
2  * COPYRIGHT:       See COPYING.ARM in the top level directory
3  * PROJECT:         ReactOS UEFI Boot Library
4  * FILE:            boot/environ/lib/misc/image.c
5  * PURPOSE:         Boot Library Image Routines
6  * PROGRAMMER:      Alex Ionescu (alex.ionescu@reactos.org)
7  */
8 
9 /* INCLUDES ******************************************************************/
10 
11 #include "bl.h"
12 #include <bcd.h>
13 
14 /* DATA VARIABLES ************************************************************/
15 
16 ULONG IapAllocatedTableEntries;
17 ULONG IapTableEntries;
18 PVOID* IapImageTable;
19 
20 #ifndef _M_ARM
21 KDESCRIPTOR GdtRegister;
22 KDESCRIPTOR IdtRegister;
23 KDESCRIPTOR BootAppGdtRegister;
24 KDESCRIPTOR BootAppIdtRegister;
25 PVOID BootApp32EntryRoutine;
26 PBOOT_APPLICATION_PARAMETER_BLOCK BootApp32Parameters;
27 PVOID BootApp32Stack;
28 #endif
29 
30 /* FUNCTIONS *****************************************************************/
31 
32 NTSTATUS
33 ImgpGetFileSize (
34     _In_ PBL_IMG_FILE File,
35     _Out_ PULONG FileSize
36     )
37 {
38     NTSTATUS Status;
39     ULONG Size;
40     BL_FILE_INFORMATION FileInformation;
41 
42     /* Check if the file was memory mapped */
43     if (File->Flags & BL_IMG_MEMORY_FILE)
44     {
45         /* Just read the size of the mapping */
46         Size = File->FileSize;
47     }
48     else
49     {
50         /* Do file I/O to get the file size */
51         Status = BlFileGetInformation(File->FileId,
52                                       &FileInformation);
53         if (!NT_SUCCESS(Status))
54         {
55             return Status;
56         }
57 
58         /* We only support files less than 4GB in the Image Mapped */
59         Size = FileInformation.Size;
60         if (FileInformation.Size > ULONG_MAX)
61         {
62             return STATUS_NOT_SUPPORTED;
63         }
64     }
65 
66     /* Return the size and success */
67     *FileSize = Size;
68     return STATUS_SUCCESS;
69 }
70 
71 NTSTATUS
72 ImgpReadAtFileOffset (
73     _In_ PBL_IMG_FILE File,
74     _In_ ULONG Size,
75     _In_ ULONGLONG ByteOffset,
76     _In_ PVOID Buffer,
77     _Out_ PULONG BytesReturned
78     )
79 {
80     NTSTATUS Status;
81 
82     /* Check what if this is a mapped file or not */
83     if (File->Flags & BL_IMG_MEMORY_FILE)
84     {
85         /* Check if the boundaries are within the file size */
86         if ((ByteOffset + Size) <= File->FileSize)
87         {
88             /* Yep, copy into the caller-supplied buffer */
89             RtlCopyMemory(Buffer,
90                           (PVOID)((ULONG_PTR)File->BaseAddress + (ULONG_PTR)ByteOffset),
91                           Size);
92 
93             /* If caller wanted to know, return the size copied */
94             if (BytesReturned)
95             {
96                 *BytesReturned = Size;
97             }
98 
99             /* All good */
100             Status = STATUS_SUCCESS;
101         }
102         else
103         {
104             /* Doesn't fit */
105             Status = STATUS_INVALID_PARAMETER;
106         }
107     }
108     else
109     {
110         /* Issue the file I/O instead */
111         Status = BlFileReadAtOffsetEx(File->FileId,
112                                       Size,
113                                       ByteOffset,
114                                       Buffer,
115                                       BytesReturned,
116                                       0);
117     }
118 
119     /* Return the final status */
120     return Status;
121 }
122 
123 NTSTATUS
124 ImgpOpenFile (
125     _In_ ULONG DeviceId,
126     _In_ PWCHAR FileName,
127     _In_ ULONG Flags,
128     _Out_ PBL_IMG_FILE NewFile
129     )
130 {
131     NTSTATUS Status;
132     ULONG FileSize;
133     ULONGLONG RemoteFileSize;
134     PVOID RemoteFileAddress;
135     ULONG FileId;
136 
137     /* First, try to see if BD has this file remotely */
138     Status = BlBdPullRemoteFile(FileName,
139                                 &RemoteFileAddress,
140                                 &RemoteFileSize);
141     if (NT_SUCCESS(Status))
142     {
143         /* Yep, get the file size and make sure it's < 4GB */
144         FileSize = RemoteFileSize;
145         if (RemoteFileSize <= ULONG_MAX)
146         {
147             /* Remember this is a memory mapped remote file */
148             NewFile->Flags |= (BL_IMG_MEMORY_FILE | BL_IMG_REMOTE_FILE);
149             NewFile->FileSize = FileSize;
150             NewFile->BaseAddress = RemoteFileAddress;
151             goto Quickie;
152         }
153     }
154 
155     /* Use File I/O instead */
156     Status = BlFileOpen(DeviceId,
157                         FileName,
158                         BL_FILE_READ_ACCESS,
159                         &FileId);
160     if (!NT_SUCCESS(Status))
161     {
162         /* Bail out on failure */
163         return Status;
164     }
165 
166     /* Make sure nobody thinks this is a memory file */
167     NewFile->Flags &= ~BL_IMG_MEMORY_FILE;
168     NewFile->FileId = FileId;
169 
170 Quickie:
171     /* Set common data for both memory and I/O based file */
172     NewFile->Flags |= BL_IMG_VALID_FILE;
173     NewFile->FileName = FileName;
174     return Status;
175 }
176 
177 NTSTATUS
178 ImgpCloseFile (
179     _In_ PBL_IMG_FILE File
180     )
181 {
182     NTSTATUS Status;
183 
184     /* Make sure this is a valid file, otherwise no-op */
185     Status = STATUS_SUCCESS;
186     if (File->Flags & BL_IMG_VALID_FILE)
187     {
188         /* Is this a memory mapped file? */
189         if (!(File->Flags & BL_IMG_MEMORY_FILE))
190         {
191             /* Nope, close the file handle */
192             return BlFileClose(File->FileId);
193         }
194 
195         /* Is this a remote file? */
196         if (File->Flags & BL_IMG_REMOTE_FILE)
197         {
198             /* Then only free the memory in that scenario */
199             return MmPapFreePages(File->BaseAddress, BL_MM_INCLUDE_MAPPED_ALLOCATED);
200         }
201     }
202 
203     /* Return the final status */
204     return Status;
205 }
206 
207 NTSTATUS
208 BlImgUnallocateImageBuffer (
209     _In_ PVOID ImageBase,
210     _In_ ULONG ImageSize,
211     _In_ ULONG ImageFlags
212     )
213 {
214     PHYSICAL_ADDRESS PhysicalAddress;
215     NTSTATUS Status;
216 
217     /* Make sure required parameters are present */
218     if (!(ImageBase) || !(ImageSize))
219     {
220         return STATUS_INVALID_PARAMETER;
221     }
222 
223     /* Check if this was a physical allocation */
224     if (!(ImageFlags & BL_LOAD_IMG_VIRTUAL_BUFFER))
225     {
226         return MmPapFreePages(ImageBase, BL_MM_INCLUDE_MAPPED_ALLOCATED);
227     }
228 
229     /* It's virtual, so translate it first */
230     if (!BlMmTranslateVirtualAddress(ImageBase, &PhysicalAddress))
231     {
232         return STATUS_INVALID_PARAMETER;
233     }
234 
235     /* Unmap the virtual mapping */
236     Status = BlMmUnmapVirtualAddressEx(ImageBase, ROUND_TO_PAGES(ImageSize));
237     if (NT_SUCCESS(Status))
238     {
239         /* Now free the physical pages */
240         Status = BlMmFreePhysicalPages(PhysicalAddress);
241     }
242 
243     /* All done */
244     return Status;
245 }
246 
247 NTSTATUS
248 BlImgAllocateImageBuffer (
249     _Inout_ PVOID* ImageBuffer,
250     _In_ ULONG MemoryType,
251     _In_ ULONGLONG ImageSize,
252     _In_ ULONG Flags
253     )
254 {
255     ULONG Attributes;
256     ULONGLONG Pages, Size;
257     PVOID MappedBase, CurrentBuffer;
258     NTSTATUS Status;
259     PHYSICAL_ADDRESS PhysicalAddress;
260 
261     /* Read and reset the current buffer address */
262     CurrentBuffer = *ImageBuffer;
263     *ImageBuffer = NULL;
264 
265     /* Align the image size to page */
266     Size = ROUND_TO_PAGES(ImageSize);
267 
268     /* Not sure what this attribute does yet */
269     Attributes = 0;
270     if (Flags & BL_LOAD_IMG_UNKNOWN_BUFFER_FLAG)
271     {
272         Attributes = 0x10000;
273     }
274 
275     /* Check if the caller wants a virtual buffer */
276     if (Flags & BL_LOAD_IMG_VIRTUAL_BUFFER)
277     {
278         /* Set the physical address to the current buffer */
279         PhysicalAddress.QuadPart = (ULONG_PTR)CurrentBuffer;
280         Pages = Size >> PAGE_SHIFT;
281 
282         /* Allocate the physical pages */
283         Status = BlMmAllocatePhysicalPages(&PhysicalAddress,
284                                            Pages,
285                                            MemoryType,
286                                            Attributes,
287                                            0);
288         if (!NT_SUCCESS(Status))
289         {
290             /* If that failed, remove allocation attributes */
291             PhysicalAddress.QuadPart = 0;
292             Attributes &= ~BlMemoryValidAllocationAttributeMask,
293             Status = BlMmAllocatePhysicalPages(&PhysicalAddress,
294                                                Pages,
295                                                MemoryType,
296                                                Attributes,
297                                                0);
298         }
299 
300         /* Check if either attempts succeeded */
301         if (!NT_SUCCESS(Status))
302         {
303             return Status;
304         }
305 
306         /* Now map the physical buffer at the address requested */
307         MappedBase = PhysicalAddressToPtr(PhysicalAddress);
308         Status = BlMmMapPhysicalAddressEx(&MappedBase,
309                                           BlMemoryFixed,
310                                           Size,
311                                           PhysicalAddress);
312         if (!NT_SUCCESS(Status))
313         {
314             /* Free on failure if needed */
315             BlMmFreePhysicalPages(PhysicalAddress);
316             return Status;
317         }
318     }
319     else
320     {
321         /* Otherwise, allocate raw physical pages */
322         MappedBase = CurrentBuffer;
323         Pages = Size >> PAGE_SHIFT;
324         Status = MmPapAllocatePagesInRange(&MappedBase,
325                                            MemoryType,
326                                            Pages,
327                                            Attributes,
328                                            0,
329                                            NULL,
330                                            0);
331         if (!NT_SUCCESS(Status))
332         {
333             /* If that failed, try without allocation attributes */
334             MappedBase = NULL;
335             Attributes &= ~BlMemoryValidAllocationAttributeMask,
336             Status = MmPapAllocatePagesInRange(&MappedBase,
337                                                MemoryType,
338                                                Pages,
339                                                Attributes,
340                                                0,
341                                                NULL,
342                                                0);
343         }
344 
345         /* Check if either attempts succeeded */
346         if (!NT_SUCCESS(Status))
347         {
348             return Status;
349         }
350     }
351 
352     /* Success path, returned allocated address */
353     *ImageBuffer = MappedBase;
354     return STATUS_SUCCESS;
355 }
356 
357 NTSTATUS
358 BlImgLoadImageWithProgress2 (
359     _In_ ULONG DeviceId,
360     _In_ BL_MEMORY_TYPE MemoryType,
361     _In_ PWCHAR FileName,
362     _Inout_ PVOID* MappedBase,
363     _Inout_ PULONG MappedSize,
364     _In_ ULONG ImageFlags,
365     _In_ BOOLEAN ShowProgress,
366     _Out_opt_ PUCHAR* HashBuffer,
367     _Out_opt_ PULONG HashSize
368     )
369 {
370     NTSTATUS Status;
371     PVOID BaseAddress, Buffer;
372     ULONG RemainingLength, CurrentSize, ImageSize, ReadSize;
373     BOOLEAN ComputeSignature, ComputeHash, Completed;
374     BL_IMG_FILE FileHandle;
375     ULONGLONG ByteOffset;
376     PHYSICAL_ADDRESS PhysicalAddress;
377 
378     /* Initialize variables */
379     BaseAddress = 0;
380     ImageSize = 0;
381     Completed = FALSE;
382     RtlZeroMemory(&FileHandle, sizeof(FileHandle));
383 
384     /* Check for missing parameters */
385     if (!MappedBase)
386     {
387         Status = STATUS_INVALID_PARAMETER;
388         goto Quickie;
389     }
390     if (!FileName)
391     {
392         Status = STATUS_INVALID_PARAMETER;
393         goto Quickie;
394     }
395     if (!MappedSize)
396     {
397         Status = STATUS_INVALID_PARAMETER;
398         goto Quickie;
399     }
400 
401     /* Check if the image buffer is being provided */
402     if (ImageFlags & BL_LOAD_IMG_EXISTING_BUFFER)
403     {
404         /* An existing base must already exist */
405         if (!(*MappedBase))
406         {
407             Status = STATUS_INVALID_PARAMETER;
408             goto Quickie;
409         }
410     }
411 
412     /* Check of a hash is being requested */
413     if (ImageFlags & BL_LOAD_IMG_COMPUTE_HASH)
414     {
415         /* Make sure we can return the hash */
416         if (!HashBuffer)
417         {
418             Status = STATUS_INVALID_PARAMETER;
419             goto Quickie;
420         }
421         if (!HashSize)
422         {
423             Status = STATUS_INVALID_PARAMETER;
424             goto Quickie;
425         }
426     }
427 
428     /* Check for invalid combination of parameters */
429     if ((ImageFlags & BL_LOAD_IMG_COMPUTE_HASH) && (ImageFlags & 0x270))
430     {
431         Status = STATUS_INVALID_PARAMETER;
432         goto Quickie;
433     }
434 
435     /* Initialize hash if requested by caller */
436     if (HashBuffer)
437     {
438         *HashBuffer = 0;
439     }
440 
441     /* Do the same for the hash size */
442     if (HashSize)
443     {
444         *HashSize = 0;
445     }
446 
447     /* Open the image file */
448     Status = ImgpOpenFile(DeviceId, FileName, DeviceId, &FileHandle);
449     if (!NT_SUCCESS(Status))
450     {
451         EfiPrintf(L"Error opening file: %lx\r\n", Status);
452         goto Quickie;
453     }
454 
455     /* Get the size of the image */
456     Status = ImgpGetFileSize(&FileHandle, &ImageSize);
457     if (!NT_SUCCESS(Status))
458     {
459         EfiPrintf(L"Error getting file size: %lx\r\n", Status);
460         goto Quickie;
461     }
462 
463     /* Read the current base address */
464     BaseAddress = *MappedBase;
465     if (ImageFlags & BL_LOAD_IMG_EXISTING_BUFFER)
466     {
467         /* Check if the current buffer is too small */
468         if (*MappedSize < ImageSize)
469         {
470             /* Return the required size of the buffer */
471             *MappedSize = ImageSize;
472             Status = STATUS_BUFFER_TOO_SMALL;
473         }
474     }
475     else
476     {
477         /* A buffer was not provided, allocate one ourselves */
478         Status = BlImgAllocateImageBuffer(&BaseAddress,
479                                           MemoryType,
480                                           ImageSize,
481                                           ImageFlags);
482     }
483 
484     /* Bail out if allocation failed */
485     if (!NT_SUCCESS(Status))
486     {
487         goto Quickie;
488     }
489 
490     /* Set the initial byte offset and length to read */
491     RemainingLength = ImageSize;
492     ByteOffset = 0;
493     Buffer = BaseAddress;
494 
495     /* Update the initial progress */
496     Completed = FALSE;
497     if (ShowProgress)
498     {
499         BlUtlUpdateProgress(0, &Completed);
500         ShowProgress &= (Completed != 0) - 1;
501     }
502 
503     /* Set the chunk size for each read */
504     ReadSize = 0x100000;
505     if (ReadSize > ImageSize)
506     {
507         ReadSize = ImageSize;
508     }
509 
510     /* Check if we should compute hash and/or signatures */
511     ComputeSignature = ImageFlags & BL_LOAD_IMG_COMPUTE_SIGNATURE;
512     ComputeHash = FALSE;
513     if ((ComputeSignature) || (ImageFlags & BL_LOAD_IMG_COMPUTE_HASH))
514     {
515         ComputeHash = TRUE;
516         // todo: crypto is hard
517     }
518 
519     /* Begin the read loop */
520     while (RemainingLength)
521     {
522         /* Check if we've got more than a chunk left to read */
523         if (RemainingLength > ReadSize)
524         {
525             /* Read a chunk*/
526             CurrentSize = ReadSize;
527         }
528         else
529         {
530             /* Read only what's left */
531             CurrentSize = RemainingLength;
532         }
533 
534         /* Read the chunk */
535         Status = ImgpReadAtFileOffset(&FileHandle,
536                                       CurrentSize,
537                                       ByteOffset,
538                                       Buffer,
539                                       0);
540         if (!NT_SUCCESS(Status))
541         {
542             goto Quickie;
543         }
544 
545         /* Check if we need to compute the hash of this chunk */
546         if (ComputeHash)
547         {
548             // todo: crypto is hard
549         }
550 
551         /* Update our position and read information */
552         Buffer = (PVOID)((ULONG_PTR)Buffer + CurrentSize);
553         RemainingLength -= CurrentSize;
554         ByteOffset += CurrentSize;
555 
556         /* Check if we should update the progress bar */
557         if (ShowProgress)
558         {
559             /* Compute new percentage completed, check if we're done */
560             BlUtlUpdateProgress(100 - 100 * RemainingLength / ImageSize,
561                                 &Completed);
562             ShowProgress &= (Completed != 0) - 1;
563         }
564     }
565 
566     /* Is the read fully complete? We need to finalize the hash if requested */
567     if (ComputeHash)
568     {
569         // todo: CRYPTO IS HARD
570     }
571 
572     /* Success path, return back the buffer and the size of the image */
573     *MappedBase = BaseAddress;
574     *MappedSize = ImageSize;
575 
576 Quickie:
577     /* Close the file handle */
578     ImgpCloseFile(&FileHandle);
579 
580     /* Check if we failed and had allocated a buffer */
581     if (!(NT_SUCCESS(Status)) &&
582         (BaseAddress) &&
583         !(ImageFlags & BL_LOAD_IMG_EXISTING_BUFFER))
584     {
585         /* Check what kind of buffer we had allocated */
586         if (ImageFlags & BL_LOAD_IMG_VIRTUAL_BUFFER)
587         {
588             /* Unmap and free the virtual buffer */
589             PhysicalAddress.QuadPart = (ULONG_PTR)BaseAddress;
590             BlMmUnmapVirtualAddressEx(BaseAddress, ImageSize);
591             BlMmFreePhysicalPages(PhysicalAddress);
592         }
593         else
594         {
595             /* Free the physical buffer */
596             MmPapFreePages(BaseAddress, BL_MM_INCLUDE_MAPPED_ALLOCATED);
597         }
598     }
599 
600     /* If we hadn't gotten to 100% yet, do it now */
601     if (ShowProgress)
602     {
603         BlUtlUpdateProgress(100, &Completed);
604     }
605 
606     /* Return the final status */
607     return Status;
608 }
609 
610 PIMAGE_SECTION_HEADER
611 BlImgFindSection (
612     _In_ PVOID ImageBase,
613     _In_ ULONG ImageSize
614     )
615 {
616     PIMAGE_SECTION_HEADER FoundSection;
617     ULONG i;
618     PIMAGE_SECTION_HEADER SectionHeader;
619     PIMAGE_NT_HEADERS NtHeader;
620     NTSTATUS Status;
621 
622     /* Assume failure */
623     FoundSection = NULL;
624 
625     /* Make sure the image is valid */
626     Status = RtlImageNtHeaderEx(0, ImageBase, ImageSize, &NtHeader);
627     if (NT_SUCCESS(Status))
628     {
629         /* Get the first section and loop through them all */
630         SectionHeader = IMAGE_FIRST_SECTION(NtHeader);
631         for (i = 0; i < NtHeader->FileHeader.NumberOfSections; i++)
632         {
633             /* Check if this is the resource section */
634             if (!_stricmp((PCCH)SectionHeader->Name, ".rsrc"))
635             {
636                 /* Yep, we're done */
637                 FoundSection = SectionHeader;
638                 break;
639             }
640 
641             /* Nope, keep going */
642             SectionHeader++;
643         }
644     }
645 
646     /* Return the matching section */
647     return FoundSection;
648 }
649 
650 VOID
651 BlImgQueryCodeIntegrityBootOptions (
652     _In_ PBL_LOADED_APPLICATION_ENTRY ApplicationEntry,
653     _Out_ PBOOLEAN IntegrityChecksDisabled,
654     _Out_ PBOOLEAN TestSigning
655     )
656 {
657 
658     NTSTATUS Status;
659     BOOLEAN Value;
660 
661     /* Check if /DISABLEINTEGRITYCHECKS is on */
662     Status = BlGetBootOptionBoolean(ApplicationEntry->BcdData,
663                                     BcdLibraryBoolean_DisableIntegrityChecks,
664                                     &Value);
665     *IntegrityChecksDisabled = NT_SUCCESS(Status) && (Value);
666 
667     /* Check if /TESTSIGNING is on */
668     Status = BlGetBootOptionBoolean(ApplicationEntry->BcdData,
669                                     BcdLibraryBoolean_AllowPrereleaseSignatures,
670                                     &Value);
671     *TestSigning = NT_SUCCESS(Status) && (Value);
672 }
673 
674 NTSTATUS
675 BlImgUnLoadImage (
676     _In_ PVOID ImageBase,
677     _In_ ULONG ImageSize,
678     _In_ ULONG ImageFlags
679     )
680 {
681     /* Check for missing parameters */
682     if (!(ImageSize) || !(ImageBase))
683     {
684         /* Bail out */
685         return STATUS_INVALID_PARAMETER;
686     }
687 
688     /* Unallocate the image buffer */
689     return BlImgUnallocateImageBuffer(ImageBase, ImageSize, ImageFlags);
690 }
691 
692 NTSTATUS
693 ImgpLoadPEImage (
694     _In_ PBL_IMG_FILE ImageFile,
695     _In_ BL_MEMORY_TYPE MemoryType,
696     _Inout_ PVOID* ImageBase,
697     _Out_opt_ PULONG ImageSize,
698     _Inout_opt_ PVOID Hash,
699     _In_ ULONG Flags
700     )
701 {
702     NTSTATUS Status;
703     ULONG FileSize, HeaderSize;
704     BL_IMG_FILE LocalFileBuffer;
705     PBL_IMG_FILE LocalFile;
706     PVOID VirtualAddress, PreferredBase, ImageBuffer, CertBuffer, HashBuffer;
707     ULONGLONG VirtualSize;
708     PIMAGE_DATA_DIRECTORY CertDirectory;
709     PHYSICAL_ADDRESS PhysicalAddress;
710     PIMAGE_NT_HEADERS NtHeaders;
711     USHORT SectionCount, CheckSum, PartialSum, FinalSum;
712     PIMAGE_SECTION_HEADER Section;
713     ULONG_PTR EndOfHeaders, SectionStart, Slack, SectionEnd;
714     ULONG i, SectionSize, RawSize, BytesRead, RemainingLength, Offset, AlignSize;
715     BOOLEAN First, ImageHashValid;
716     UCHAR LocalBuffer[1024];
717     UCHAR TrustedBootInformation[52];
718     ULONG WorkaroundForBinutils;
719 
720     /* Initialize locals */
721     WorkaroundForBinutils = 0;
722     LocalFile = NULL;
723     ImageBuffer = NULL;
724     FileSize = 0;
725     First = FALSE;
726     VirtualAddress = NULL;
727     CertBuffer = NULL;
728     CertDirectory = NULL;
729     HashBuffer = NULL;
730     Offset = 0;
731     VirtualSize = 0;
732     ImageHashValid = FALSE;
733     RtlZeroMemory(&TrustedBootInformation, sizeof(TrustedBootInformation));
734 
735     /* Get the size of the image */
736     Status = ImgpGetFileSize(ImageFile, &FileSize);
737     if (!NT_SUCCESS(Status))
738     {
739         return STATUS_FILE_INVALID;
740     }
741 
742     /* Allocate a flat buffer for it */
743     Status = BlImgAllocateImageBuffer(&ImageBuffer, BlLoaderData, FileSize, 0);
744     if (!NT_SUCCESS(Status))
745     {
746         goto Quickie;
747     }
748 
749     /* Read the whole file flat for now */
750     Status = ImgpReadAtFileOffset(ImageFile, FileSize, 0, ImageBuffer, NULL);
751     if (!NT_SUCCESS(Status))
752     {
753         goto Quickie;
754     }
755 
756     /* Build a local file handle */
757     LocalFile = &LocalFileBuffer;
758     LocalFileBuffer.FileName = ImageFile->FileName;
759     LocalFileBuffer.Flags = BL_IMG_MEMORY_FILE | BL_IMG_VALID_FILE;
760     LocalFileBuffer.BaseAddress = ImageBuffer;
761     LocalFileBuffer.FileSize = FileSize;
762 
763     /* Get the NT headers of the file */
764     Status = RtlImageNtHeaderEx(0, ImageBuffer, FileSize, &NtHeaders);
765     if (!NT_SUCCESS(Status))
766     {
767         goto Quickie;
768     }
769 
770     /* Check if we should validate the machine type */
771     if (Flags & BL_LOAD_PE_IMG_CHECK_MACHINE)
772     {
773         /* Is it different than our current machine type? */
774 #if _M_AMD64
775         if (NtHeaders->FileHeader.Machine != IMAGE_FILE_MACHINE_AMD64)
776 #else
777         if (NtHeaders->FileHeader.Machine != IMAGE_FILE_MACHINE_I386)
778 #endif
779         {
780             /* Is it x86 (implying we are x64) ? */
781             if (NtHeaders->FileHeader.Machine == IMAGE_FILE_MACHINE_I386)
782             {
783                 /* Return special error code */
784                 Status = STATUS_INVALID_IMAGE_WIN_32;
785             }
786             else if (NtHeaders->FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64)
787             {
788                 /* Otherwise, it's x64 but we are x86 */
789                 Status = STATUS_INVALID_IMAGE_WIN_64;
790             }
791             else
792             {
793                 /* Or it's ARM or something... */
794                 Status = STATUS_INVALID_IMAGE_FORMAT;
795             }
796 
797             /* Return with the distinguished error code */
798             goto Quickie;
799         }
800     }
801 
802     /* Check if we should validate the subsystem */
803     if (Flags & BL_LOAD_PE_IMG_CHECK_SUBSYSTEM)
804     {
805         /* It must be a Windows boot Application */
806         if (NtHeaders->OptionalHeader.Subsystem !=
807             IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION)
808         {
809             Status = STATUS_INVALID_IMAGE_FORMAT;
810             goto Quickie;
811         }
812     }
813 
814     /* Check if we should validate the /INTEGRITYCHECK flag */
815     if (Flags & BL_LOAD_PE_IMG_CHECK_FORCED_INTEGRITY)
816     {
817         /* Check if it's there */
818         if (!(NtHeaders->OptionalHeader.DllCharacteristics &
819               IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY))
820         {
821             /* Nope, fail otherwise */
822             Status = STATUS_INVALID_IMAGE_FORMAT;
823             goto Quickie;
824         }
825     }
826 
827     /* Check if we should compute the image hash */
828     if ((Flags & BL_LOAD_PE_IMG_COMPUTE_HASH) || (Hash))
829     {
830         EfiPrintf(L"No hash support\r\n");
831     }
832 
833     /* Read the current base address, if any */
834     VirtualAddress = *ImageBase;
835 
836     /* Get the virtual size of the image */
837     VirtualSize = NtHeaders->OptionalHeader.SizeOfImage;
838 
839     /* Safely align the virtual size to a page */
840     Status = RtlULongLongAdd(VirtualSize,
841                              PAGE_SIZE - 1,
842                              &VirtualSize);
843     if (!NT_SUCCESS(Status))
844     {
845         goto Quickie;
846     }
847     VirtualSize = ALIGN_DOWN_BY(VirtualSize, PAGE_SIZE);
848 
849     /* Make sure the image isn't larger than 4GB */
850     if (VirtualSize > ULONG_MAX)
851     {
852         Status = STATUS_INVALID_IMAGE_FORMAT;
853         goto Quickie;
854     }
855 
856     /* Check if we have a buffer already */
857     if (Flags & BL_LOAD_IMG_EXISTING_BUFFER)
858     {
859         /* Check if it's too small */
860         if (*ImageSize < VirtualSize)
861         {
862             /* Fail, letting the caller know how big to make it */
863             *ImageSize = VirtualSize;
864             Status = STATUS_BUFFER_TOO_SMALL;
865         }
866     }
867     else
868     {
869         /* Allocate the buffer with the flags and type the caller wants */
870         Status = BlImgAllocateImageBuffer(&VirtualAddress,
871                                           MemoryType,
872                                           VirtualSize,
873                                           Flags);
874     }
875 
876     /* Bail out if allocation failed, or existing buffer is too small */
877     if (!NT_SUCCESS(Status))
878     {
879         goto Quickie;
880     }
881 
882     /* Read the size of the headers */
883     HeaderSize = NtHeaders->OptionalHeader.SizeOfHeaders;
884     if (VirtualSize < HeaderSize)
885     {
886         /* Bail out if they're bigger than the image! */
887         Status = STATUS_INVALID_IMAGE_FORMAT;
888         goto Quickie;
889     }
890 
891     /* Now read the header into the buffer */
892     Status = ImgpReadAtFileOffset(LocalFile, HeaderSize, 0, VirtualAddress, NULL);
893     if (!NT_SUCCESS(Status))
894     {
895         goto Quickie;
896     }
897 
898     /* Get the NT headers of the file */
899     Status = RtlImageNtHeaderEx(0, VirtualAddress, HeaderSize, &NtHeaders);
900     if (!NT_SUCCESS(Status))
901     {
902         goto Quickie;
903     }
904 
905     First = FALSE;
906 
907     /* Record how many sections we have */
908     SectionCount = NtHeaders->FileHeader.NumberOfSections;
909 
910     /* Capture the current checksum and reset it */
911     CheckSum = NtHeaders->OptionalHeader.CheckSum;
912     NtHeaders->OptionalHeader.CheckSum = 0;
913 
914     /* Calculate the checksum of the header, and restore the original one */
915     PartialSum = BlUtlCheckSum(0,
916                                VirtualAddress,
917                                HeaderSize,
918                                BL_UTL_CHECKSUM_COMPLEMENT |
919                                BL_UTL_CHECKSUM_USHORT_BUFFER);
920     NtHeaders->OptionalHeader.CheckSum = CheckSum;
921 
922     /* Record our current position (right after the headers) */
923     EndOfHeaders = (ULONG_PTR)VirtualAddress + HeaderSize;
924 
925     /* Get the first section and iterate through each one */
926     Section = IMAGE_FIRST_SECTION(NtHeaders);
927     for (i = 0; i < SectionCount; i++)
928     {
929         /* Compute where this section starts */
930         SectionStart = (ULONG_PTR)VirtualAddress + Section->VirtualAddress;
931 
932         /* Make sure that the section fits within the image */
933         if ((VirtualSize < Section->VirtualAddress) ||
934             ((PVOID)SectionStart < VirtualAddress))
935         {
936             EfiPrintf(L"fail 1\r\n");
937             Status = STATUS_INVALID_IMAGE_FORMAT;
938             goto Quickie;
939         }
940 
941         /* Check if there's slack space between header end and the section */
942         if (!(First) && (EndOfHeaders < SectionStart))
943         {
944             /* Zero it out */
945             Slack = SectionStart - EndOfHeaders;
946             RtlZeroMemory((PVOID)EndOfHeaders, Slack);
947         }
948 
949         /* Get the section virtual size and the raw size */
950         SectionSize = Section->Misc.VirtualSize;
951         RawSize = Section->SizeOfRawData;
952 
953         /* Safely align the raw size by 2 */
954         Status = RtlULongAdd(RawSize, 1, &AlignSize);
955         if (!NT_SUCCESS(Status))
956         {
957             goto Quickie;
958         }
959         AlignSize = ALIGN_DOWN_BY(AlignSize, 2);
960 
961         /* IF we don't have a virtual size, use the raw size */
962         if (!SectionSize)
963         {
964             SectionSize = RawSize;
965         }
966 
967         /* If we don't have raw data, ignore the raw size */
968         if (!Section->PointerToRawData)
969         {
970             RawSize = 0;
971         }
972         else if (SectionSize < RawSize)
973         {
974             /* And if the virtual size is smaller, use it as the final size */
975             RawSize = SectionSize;
976         }
977 
978         /* Make sure that the section doesn't overflow in memory */
979         Status = RtlULongPtrAdd(Section->VirtualAddress,
980                                 SectionSize,
981                                 &SectionEnd);
982         if (!NT_SUCCESS(Status))
983         {
984             EfiPrintf(L"fail 21\r\n");
985             Status = STATUS_INVALID_IMAGE_FORMAT;
986             goto Quickie;
987         }
988 
989         /* Make sure that it fits within the image */
990         if (VirtualSize < SectionEnd)
991         {
992             Status = STATUS_INVALID_IMAGE_FORMAT;
993             goto Quickie;
994         }
995 
996         /* Make sure it doesn't overflow on disk */
997         Status = RtlULongPtrAdd(Section->VirtualAddress,
998                                 AlignSize,
999                                 &SectionEnd);
1000         if (!NT_SUCCESS(Status))
1001         {
1002             EfiPrintf(L"fail 31\r\n");
1003             Status = STATUS_INVALID_IMAGE_FORMAT;
1004             goto Quickie;
1005         }
1006 
1007         /* Make sure that it fits within the disk image as well */
1008         if (VirtualSize < SectionEnd)
1009         {
1010             Status = STATUS_INVALID_IMAGE_FORMAT;
1011             goto Quickie;
1012         }
1013 
1014         /* So does this section have a valid size after all? */
1015         if (RawSize)
1016         {
1017             /* Are we in the first iteration? */
1018             if (!First)
1019             {
1020                 /* FUCK YOU BINUTILS */
1021                 if (NtHeaders->OptionalHeader.MajorLinkerVersion < 7)
1022                 {
1023                     if ((*(PULONG)&Section->Name == 'ler.') && (RawSize < AlignSize))
1024                     {
1025                         /* Piece of shit won't build relocations when you tell it to,
1026                          * either by using --emit-relocs or --dynamicbase. People online
1027                          * have found out that by using -pie-executable you can get this
1028                          * to happen, but then it turns out that the .reloc section is
1029                          * incorrectly sized, and results in a corrupt PE. However, they
1030                          * still compute the checksum using the correct value. What idiots.
1031                          */
1032                         WorkaroundForBinutils = AlignSize - RawSize;
1033                         AlignSize -= WorkaroundForBinutils;
1034                     }
1035                 }
1036 
1037                 /* Yes, read the section data */
1038                 Status = ImgpReadAtFileOffset(LocalFile,
1039                                               AlignSize,
1040                                               Section->PointerToRawData,
1041                                               (PVOID)SectionStart,
1042                                               NULL);
1043                 if (!NT_SUCCESS(Status))
1044                 {
1045                     goto Quickie;
1046                 }
1047 
1048                 /* Update our current offset */
1049                 Offset = AlignSize + Section->PointerToRawData;
1050 
1051                 /* Update the checksum to include this section */
1052                 PartialSum = BlUtlCheckSum(PartialSum,
1053                                            (PUCHAR)SectionStart,
1054                                            AlignSize,
1055                                            BL_UTL_CHECKSUM_COMPLEMENT |
1056                                            BL_UTL_CHECKSUM_USHORT_BUFFER);
1057                 AlignSize += WorkaroundForBinutils;
1058             }
1059         }
1060 
1061         /* Are we in the first iteration? */
1062         if (!First)
1063         {
1064             /* Is there space at the end of the section? */
1065             if (RawSize < SectionSize)
1066             {
1067                 /* Zero out the slack space that's there */
1068                 Slack = SectionSize - RawSize;
1069                 RtlZeroMemory((PVOID)(SectionStart + RawSize), Slack);
1070             }
1071 
1072             /* Update our tail offset */
1073             EndOfHeaders = SectionStart + SectionSize;
1074         }
1075 
1076         /* Move to the next section */
1077         Section++;
1078     }
1079 
1080     /* Are we in the first iteration? */
1081     if (!First)
1082     {
1083         /* Go to the end of the file */
1084         SectionStart = (ULONG_PTR)VirtualAddress + VirtualSize;
1085 
1086         /* Is there still some slack space left? */
1087         if (EndOfHeaders < SectionStart)
1088         {
1089             /* Zero it out */
1090             Slack = SectionStart - EndOfHeaders;
1091             RtlZeroMemory((PVOID)EndOfHeaders, Slack);
1092         }
1093     }
1094 
1095     /* Did the first iteration complete OK? */
1096     if ((NT_SUCCESS(Status)) && !(First))
1097     {
1098         /* Check how many non-image bytes are left in the file */
1099         RemainingLength = FileSize - Offset;
1100         while (RemainingLength)
1101         {
1102             /* See if the read will fit into our local buffer */
1103             if (RemainingLength >= sizeof(LocalBuffer))
1104             {
1105                 /* Nope, cap it */
1106                 BytesRead = sizeof(LocalBuffer);
1107             }
1108             else
1109             {
1110                 /* Yes, but there's less to read */
1111                 BytesRead = RemainingLength;
1112             }
1113 
1114             /* Read 1024 bytes into the local buffer */
1115             Status = ImgpReadAtFileOffset(LocalFile,
1116                                           BytesRead,
1117                                           Offset,
1118                                           LocalBuffer,
1119                                           &BytesRead);
1120             if (!(NT_SUCCESS(Status)) || !(BytesRead))
1121             {
1122                 Status = STATUS_FILE_INVALID;
1123                 goto Quickie;
1124             }
1125 
1126             /* Advance the offset and reduce the length */
1127             RemainingLength -= BytesRead;
1128             Offset += BytesRead;
1129 
1130             /* Compute the checksum of this leftover space */
1131             PartialSum = BlUtlCheckSum(PartialSum,
1132                                        LocalBuffer,
1133                                        BytesRead,
1134                                        BL_UTL_CHECKSUM_COMPLEMENT |
1135                                        BL_UTL_CHECKSUM_USHORT_BUFFER);
1136         }
1137 
1138         /* Finally, calculate the final checksum and compare it */
1139         FinalSum = FileSize + PartialSum + WorkaroundForBinutils;
1140         if ((FinalSum != CheckSum) && (PartialSum == 0xFFFF))
1141         {
1142             /* It hit overflow, so set it to the file size */
1143             FinalSum = FileSize;
1144         }
1145 
1146         /* If the checksum doesn't match, and caller is enforcing, bail out */
1147         if ((FinalSum != CheckSum) &&
1148             !(Flags & BL_LOAD_PE_IMG_IGNORE_CHECKSUM_MISMATCH))
1149         {
1150             Status = STATUS_IMAGE_CHECKSUM_MISMATCH;
1151             goto Quickie;
1152         }
1153     }
1154 
1155     /* Check if the .rsrc section should be checked with the filename */
1156     if (Flags & BL_LOAD_PE_IMG_VALIDATE_ORIGINAL_FILENAME)
1157     {
1158         EfiPrintf(L"Not yet supported\r\n");
1159         Status = 0xC0430007; // STATUS_SECUREBOOT_FILE_REPLACED
1160         goto Quickie;
1161     }
1162 
1163     /* Check if we should relocate */
1164     if (!(Flags & BL_LOAD_PE_IMG_SKIP_RELOCATIONS))
1165     {
1166         /* Check if we loaded at a different address */
1167         PreferredBase = (PVOID)NtHeaders->OptionalHeader.ImageBase;
1168         if (VirtualAddress != PreferredBase)
1169         {
1170             /* Yep -- do relocations */
1171             Status = LdrRelocateImage(VirtualAddress,
1172                                       "Boot Environment Library",
1173                                       STATUS_SUCCESS,
1174                                       STATUS_UNSUCCESSFUL,
1175                                       STATUS_INVALID_IMAGE_FORMAT);
1176             if (!NT_SUCCESS(Status))
1177             {
1178                 /* That's bad */
1179                 goto Quickie;
1180             }
1181         }
1182     }
1183 
1184 #if BL_TPM_SUPPORT
1185     /* Check if the image hash  was valid */
1186     if (!ImageHashValid)
1187     {
1188         /* Send a TPM/SI notification without a context */
1189         BlEnNotifyEvent(0x10000002, NULL);
1190     }
1191 
1192     /* Now send a TPM/SI notification with the hash of the loaded image */
1193     BlMmTranslateVirtualAddress(VirtualAddress, &Context.ImageBase);
1194     Context.HashAlgorithm = HashAlgorithm;
1195     Context.HashSize = HashSize;
1196     Context.FileName = ImageFile->FileName;
1197     Context.ImageSize = VirtualSize;
1198     Context.HashValid = ImageHashValid;
1199     Context.Hash = Hash;
1200     BlEnNotifyEvent(0x10000002, &Context);
1201 #endif
1202 
1203     /* Return the loaded address to the caller */
1204     *ImageBase = VirtualAddress;
1205 
1206     /* If the caller wanted the image size, return it too */
1207     if (ImageSize)
1208     {
1209         *ImageSize = VirtualSize;
1210     }
1211 
1212 Quickie:
1213     /* Check if we computed the image hash OK */
1214     if (ImageHashValid)
1215     {
1216         /* Then free the information that ImgpValidateImageHash set up */
1217         EfiPrintf(L"leadking trusted boot\r\n");
1218         //ImgpDestroyTrustedBootInformation(&TrustedBootInformation);
1219     }
1220 
1221     /* Check if we had a hash buffer */
1222     if (HashBuffer)
1223     {
1224         /* Free it */
1225         MmPapFreePages(HashBuffer, BL_MM_INCLUDE_MAPPED_ALLOCATED);
1226     }
1227 
1228     /* Check if we have a certificate directory */
1229     if ((CertBuffer) && (CertDirectory))
1230     {
1231         /* Free it */
1232         BlImgUnallocateImageBuffer(CertBuffer, CertDirectory->Size, 0);
1233     }
1234 
1235     /* Check if we had an image buffer allocated */
1236     if ((ImageBuffer) && (FileSize))
1237     {
1238         /* Free it */
1239         BlImgUnallocateImageBuffer(ImageBuffer, FileSize, 0);
1240     }
1241 
1242     /* Check if we had a local file handle */
1243     if (LocalFile)
1244     {
1245         /* Close it */
1246         ImgpCloseFile(LocalFile);
1247     }
1248 
1249     /* Check if this is the failure path */
1250     if (!NT_SUCCESS(Status))
1251     {
1252         /* Check if we had started mapping in the image already */
1253         if ((VirtualAddress) && !(Flags & BL_LOAD_PE_IMG_EXISTING_BUFFER))
1254         {
1255             /* Into a virtual buffer? */
1256             if (Flags & BL_LOAD_PE_IMG_VIRTUAL_BUFFER)
1257             {
1258                 /* Unmap and free it */
1259                 BlMmUnmapVirtualAddressEx(VirtualAddress, VirtualSize);
1260                 PhysicalAddress.QuadPart = (ULONG_PTR)VirtualAddress;
1261                 BlMmFreePhysicalPages(PhysicalAddress);
1262             }
1263             else
1264             {
1265                 /* Into a physical buffer -- free it */
1266                 MmPapFreePages(VirtualAddress, BL_MM_INCLUDE_MAPPED_ALLOCATED);
1267             }
1268         }
1269     }
1270 
1271     /* Return back to caller */
1272     return Status;
1273 }
1274 
1275 NTSTATUS
1276 BlImgLoadPEImageEx (
1277     _In_ ULONG DeviceId,
1278     _In_ BL_MEMORY_TYPE MemoryType,
1279     _In_ PWCHAR Path,
1280     _Out_ PVOID* ImageBase,
1281     _Out_ PULONG ImageSize,
1282     _Out_ PVOID Hash,
1283     _In_ ULONG Flags
1284     )
1285 {
1286     BL_IMG_FILE ImageFile;
1287     NTSTATUS Status;
1288 
1289     /* Initialize the image file structure */
1290     ImageFile.Flags = 0;
1291     ImageFile.FileName = NULL;
1292 
1293     /* Check if the required parameter are missing */
1294     if (!(ImageBase) || !(Path))
1295     {
1296         return STATUS_INVALID_PARAMETER;
1297     }
1298 
1299     /* If we are loading a pre-allocated image, make sure we have it */
1300     if ((Flags & BL_LOAD_IMG_EXISTING_BUFFER) && (!(*ImageBase) || !(ImageSize)))
1301     {
1302         return STATUS_INVALID_PARAMETER;
1303     }
1304 
1305     /* Load the file from disk */
1306     Status = ImgpOpenFile(DeviceId, Path, 0, &ImageFile);
1307     if (NT_SUCCESS(Status))
1308     {
1309         /* If that worked, do the PE parsing */
1310         Status = ImgpLoadPEImage(&ImageFile,
1311                                  MemoryType,
1312                                  ImageBase,
1313                                  ImageSize,
1314                                  Hash,
1315                                  Flags);
1316     }
1317 
1318     /* Close the image file and return back to caller */
1319     ImgpCloseFile(&ImageFile);
1320     return Status;
1321 }
1322 
1323 NTSTATUS
1324 BlImgLoadBootApplication (
1325     _In_ PBL_LOADED_APPLICATION_ENTRY BootEntry,
1326     _Out_ PULONG AppHandle
1327     )
1328 {
1329     NTSTATUS Status;
1330     PULONGLONG AllowedList;
1331     ULONGLONG AllowedCount;
1332     ULONG i, DeviceId, ImageSize, Flags, ListSize;
1333     LARGE_INTEGER Frequency;
1334     PVOID UnlockCode, ImageBase;
1335     PBL_DEVICE_DESCRIPTOR Device, BitLockerDevice;
1336     PWCHAR Path;
1337     PBL_APPLICATION_ENTRY AppEntry;
1338     PBL_IMG_FILE ImageFile;
1339     BOOLEAN DisableIntegrity, TestSigning;
1340     UCHAR Hash[64];
1341     PBL_IMAGE_APPLICATION_ENTRY ImageAppEntry;
1342 
1343     /* Initialize all locals */
1344     BitLockerDevice = NULL;
1345     UnlockCode = NULL;
1346     ImageFile = NULL;
1347     DeviceId = -1;
1348     Device = NULL;
1349     ImageAppEntry = NULL;
1350     AppEntry = NULL;
1351     Path = NULL;
1352     ImageSize = 0;
1353     ImageBase = NULL;
1354 
1355     /* Check for "allowed in-memory settings" */
1356     Status = BlpGetBootOptionIntegerList(BootEntry->BcdData,
1357                                          BcdLibraryIntegerList_AllowedInMemorySettings,
1358                                          &AllowedList,
1359                                          &AllowedCount,
1360                                          TRUE);
1361     if (Status == STATUS_SUCCESS)
1362     {
1363         /* Loop through the list of allowed setting */
1364         for (i = 0; i < AllowedCount; i++)
1365         {
1366             /* Find the super undocumented one */
1367             if (AllowedList[i] == BcdLibraryInteger_UndocumentedMagic)
1368             {
1369                 /* If it's present, append the current perf frequence to it */
1370                 BlTimeQueryPerformanceCounter(&Frequency);
1371                 BlAppendBootOptionInteger(BootEntry,
1372                                           BcdLibraryInteger_UndocumentedMagic,
1373                                           Frequency.QuadPart);
1374             }
1375         }
1376     }
1377 
1378 #if BL_BITLOCKER_SUPPORT
1379     /* Do bitlocker stuff */
1380     Status = BlFveSecureBootUnlockBootDevice(BootEntry, &BitLockerDevice, &UnlockCode);
1381     if (!NT_SUCCESS(Status))
1382     {
1383         goto Quickie;
1384     }
1385 #endif
1386 
1387     /* Get the device on which this application is on*/
1388     Status = BlGetBootOptionDevice(BootEntry->BcdData,
1389                                    BcdLibraryDevice_ApplicationDevice,
1390                                    &Device,
1391                                    NULL);
1392     if (!NT_SUCCESS(Status))
1393     {
1394         goto Quickie;
1395     }
1396 
1397     /* Get the path of the application */
1398     Status = BlGetBootOptionString(BootEntry->BcdData,
1399                                    BcdLibraryString_ApplicationPath,
1400                                    &Path);
1401     if (!NT_SUCCESS(Status))
1402     {
1403         goto Quickie;
1404     }
1405 
1406     /* Open the device */
1407     Status = BlpDeviceOpen(Device,
1408                            BL_DEVICE_READ_ACCESS,
1409                            0,
1410                            &DeviceId);
1411     if (!NT_SUCCESS(Status))
1412     {
1413         goto Quickie;
1414     }
1415 
1416     /* Check for integrity BCD options */
1417     BlImgQueryCodeIntegrityBootOptions(BootEntry,
1418                                        &DisableIntegrity,
1419                                        &TestSigning);
1420 
1421 #if BL_TPM_SUPPORT
1422     RtlZeroMemory(&Context, sizeof(Context);
1423     Context.BootEntry = BootEntry;
1424     BlEnNotifyEvent(0x10000003, &Context);
1425 #endif
1426 
1427     /* Enable signing and hashing checks if integrity is enabled */
1428     Flags = 0;
1429     if (!DisableIntegrity)
1430     {
1431         Flags = 0x8070;
1432     }
1433 
1434     /* Now call the PE loader to load the image */
1435     Status = BlImgLoadPEImageEx(DeviceId,
1436                                 BlLoaderMemory,
1437                                 Path,
1438                                 &ImageBase,
1439                                 &ImageSize,
1440                                 Hash,
1441                                 Flags);
1442     if (!NT_SUCCESS(Status))
1443     {
1444         goto Quickie;
1445     }
1446 
1447 #if BL_KD_SUPPORT
1448     /* Check if we should notify the debugger of load */
1449     if (BdDebugTransitions)
1450     {
1451         /* Initialize it */
1452         BdForceDebug = 1;
1453         Status = BlBdInitialize();
1454         if (NT_SUCCESS(Status))
1455         {
1456             /* Check if it's enabled */
1457             if (BlBdDebuggerEnabled())
1458             {
1459                 /* Send it an image load notification */
1460                 BdDebuggerNotPresent = FALSE;
1461                 RtlInitUnicodeString(&PathString, Path);
1462                 BlBdLoadImageSymbols(&PathString, ImageBase);
1463             }
1464         }
1465     }
1466 #endif
1467 
1468 #if BL_BITLOCKER_SUPPORT
1469     /* Do bitlocker stuff */
1470     Status = BlSecureBootCheckPolicyOnFveDevice(BitLockerDevice);
1471     if (!NT_SUCCESS(Status))
1472     {
1473         goto Quickie;
1474     }
1475 #endif
1476 
1477 #if BL_BITLOCKER_SUPPORT
1478     /* Do bitlocker stuff */
1479     Status = BlFveSecureBootCheckpointBootApp(BootEntry, BitLockerDevice, Hash, UnlockCode);
1480     if (!NT_SUCCESS(Status))
1481     {
1482         goto Quickie;
1483     }
1484 #endif
1485 
1486     /* Get the BCD option size */
1487     ListSize = BlGetBootOptionListSize(BootEntry->BcdData);
1488 
1489     /* Allocate an entry with all the BCD options */
1490     AppEntry = BlMmAllocateHeap(ListSize + sizeof(*AppEntry));
1491     if (!AppEntry)
1492     {
1493         Status = STATUS_NO_MEMORY;
1494         goto Quickie;
1495     }
1496 
1497     /* Zero it out */
1498     RtlZeroMemory(AppEntry, sizeof(*AppEntry));
1499 
1500     /* Initialize it */
1501     strcpy(AppEntry->Signature, "BTAPENT");
1502     AppEntry->Guid = BootEntry->Guid;
1503     AppEntry->Flags = BootEntry->Flags;
1504 
1505     /* Copy the BCD options */
1506     RtlCopyMemory(&AppEntry->BcdData, BootEntry->BcdData, ListSize);
1507 
1508     /* Allocate the image entry */
1509     ImageAppEntry = BlMmAllocateHeap(sizeof(*ImageAppEntry));
1510     if (!ImageAppEntry)
1511     {
1512         Status = STATUS_NO_MEMORY;
1513         goto Quickie;
1514     }
1515 
1516     /* Initialize it */
1517     ImageAppEntry->ImageBase = ImageBase;
1518     ImageAppEntry->ImageSize = ImageSize;
1519     ImageAppEntry->AppEntry = AppEntry;
1520 
1521     /* Check if this is the first entry */
1522     if (!IapTableEntries)
1523     {
1524         /* Allocate two entries */
1525         IapAllocatedTableEntries = 0;
1526         IapTableEntries = 2;
1527         IapImageTable = BlMmAllocateHeap(IapTableEntries * sizeof(PVOID));
1528         if (!IapImageTable)
1529         {
1530             Status = STATUS_NO_MEMORY;
1531             goto Quickie;
1532         }
1533 
1534         /* Zero out the entries for now */
1535         RtlZeroMemory(IapImageTable, IapTableEntries * sizeof(PVOID));
1536     }
1537 
1538     /* Set this entry into the table */
1539     Status = BlTblSetEntry(&IapImageTable,
1540                            &IapTableEntries,
1541                            ImageAppEntry,
1542                            AppHandle,
1543                            TblDoNotPurgeEntry);
1544 
1545 Quickie:
1546     /* Is the device open? Close it if so */
1547     if (DeviceId != 1)
1548     {
1549         BlDeviceClose(DeviceId);
1550     }
1551 
1552     /* Is there an allocated device? Free it */
1553     if (Device)
1554     {
1555         BlMmFreeHeap(Device);
1556     }
1557 
1558     /* Is there an allocated path? Free it */
1559     if (Path)
1560     {
1561         BlMmFreeHeap(Path);
1562     }
1563 
1564     /* Is there a bitlocker device? Free it */
1565     if (BitLockerDevice)
1566     {
1567         BlMmFreeHeap(BitLockerDevice);
1568     }
1569 
1570     /* Is there a bitlocker unlock code? Free it */
1571     if (UnlockCode)
1572     {
1573         BlMmFreeHeap(UnlockCode);
1574     }
1575 
1576     /* Did we succeed in creating an entry? */
1577     if (NT_SUCCESS(Status))
1578     {
1579         /* Remember there's one more in the table */
1580         IapAllocatedTableEntries++;
1581 
1582         /* Return success */
1583         return Status;
1584     }
1585 
1586     /* Did we load an image after all? */
1587     if (ImageBase)
1588     {
1589         /* Unload it */
1590         BlImgUnLoadImage(ImageBase, ImageSize, 0);
1591     }
1592 
1593     /* Did we allocate an app entry? Free it */
1594     if (AppEntry)
1595     {
1596         BlMmFreeHeap(AppEntry);
1597     }
1598 
1599     /* Do we have an image file entry?  Free it */
1600     if (ImageFile)
1601     {
1602         BlMmFreeHeap(ImageFile);
1603     }
1604 
1605     /* Do we no longer have a single entry in the table? */
1606     if (!(IapAllocatedTableEntries) && (IapImageTable))
1607     {
1608         /* Free and destroy the table */
1609         BlMmFreeHeap(IapImageTable);
1610         IapTableEntries = 0;
1611         IapImageTable = NULL;
1612     }
1613 
1614     /* Return the failure code */
1615     return Status;
1616 }
1617 
1618 NTSTATUS
1619 BlpPdParseReturnArguments (
1620     _In_ PBL_RETURN_ARGUMENTS ReturnArguments
1621     )
1622 {
1623     /* Check if any custom data was returned */
1624     if (ReturnArguments->DataPage == 0)
1625     {
1626         /* Nope, nothing to do */
1627         return STATUS_SUCCESS;
1628     }
1629 
1630     /* Yes, we have to parse it */
1631     EfiPrintf(L"Return arguments not supported\r\n");
1632     return STATUS_NOT_IMPLEMENTED;
1633 }
1634 
1635 NTSTATUS
1636 ImgpCopyApplicationBootDevice (
1637     __in PBL_DEVICE_DESCRIPTOR DestinationDevice,
1638     __in PBL_DEVICE_DESCRIPTOR SourceDevice
1639     )
1640 {
1641     /* Is this a partition device? */
1642     if (SourceDevice->DeviceType != PartitionDevice)
1643     {
1644         /* It's not -- a simple copy will do */
1645         RtlCopyMemory(DestinationDevice, SourceDevice, SourceDevice->Size);
1646         return STATUS_SUCCESS;
1647     }
1648 
1649     /* TODO */
1650     EfiPrintf(L"Partition copy not supported\r\n");
1651     return STATUS_NOT_IMPLEMENTED;
1652 
1653 }
1654 
1655 NTSTATUS
1656 ImgpInitializeBootApplicationParameters (
1657     _In_ PBL_BUFFER_DESCRIPTOR ImageParameters,
1658     _In_ PBL_APPLICATION_ENTRY AppEntry,
1659     _In_ PVOID ImageBase,
1660     _In_ ULONG ImageSize
1661     )
1662 {
1663     NTSTATUS Status;
1664     PIMAGE_NT_HEADERS NtHeaders;
1665     BL_BUFFER_DESCRIPTOR MemoryParameters;
1666     LIST_ENTRY MemoryList;
1667     PBL_FIRMWARE_DESCRIPTOR FirmwareParameters;
1668     PBL_DEVICE_DESCRIPTOR BootDevice;
1669     PBL_MEMORY_DATA MemoryData;
1670     PBL_APPLICATION_ENTRY BootAppEntry;
1671     PBL_RETURN_ARGUMENTS ReturnArguments;
1672     PBOOT_APPLICATION_PARAMETER_BLOCK ParameterBlock;
1673     ULONG EntrySize, BufferSize;
1674 
1675     /* Get the image headers and validate it */
1676     Status = RtlImageNtHeaderEx(0, ImageBase, ImageSize, &NtHeaders);
1677     if (!NT_SUCCESS(Status))
1678     {
1679         return Status;
1680     }
1681 
1682     /* Get the size of the entire non-firmware, allocated, memory map */
1683     MemoryParameters.BufferSize = 0;
1684     Status = BlMmGetMemoryMap(&MemoryList,
1685                               &MemoryParameters,
1686                               BL_MM_INCLUDE_PERSISTENT_MEMORY |
1687                               BL_MM_INCLUDE_MAPPED_ALLOCATED |
1688                               BL_MM_INCLUDE_MAPPED_UNALLOCATED |
1689                               BL_MM_INCLUDE_UNMAPPED_ALLOCATED |
1690                               BL_MM_INCLUDE_RESERVED_ALLOCATED,
1691                               0);
1692     if ((Status != STATUS_BUFFER_TOO_SMALL) && (Status != STATUS_SUCCESS))
1693     {
1694         /* We failed due to an unknown reason -- bail out */
1695         return Status;
1696     }
1697 
1698     /* Compute the list of the BCD plus the application entry */
1699     EntrySize = BlGetBootOptionListSize(&AppEntry->BcdData) +
1700                 FIELD_OFFSET(BL_APPLICATION_ENTRY, BcdData);
1701 
1702     /* Compute the total size required for the entire structure */
1703     BufferSize = EntrySize +
1704                  BlpBootDevice->Size +
1705                  MemoryParameters.BufferSize +
1706                  sizeof(*ReturnArguments) +
1707                  sizeof(*MemoryData) +
1708                  sizeof(*FirmwareParameters) +
1709                  sizeof(*ParameterBlock);
1710 
1711     /* Check if this gives us enough space */
1712     if (ImageParameters->BufferSize < BufferSize)
1713     {
1714         /* It does not -- free the existing buffer */
1715         if (ImageParameters->BufferSize)
1716         {
1717             BlMmFreeHeap(ImageParameters->Buffer);
1718         }
1719 
1720         /* Allocate a new buffer of sufficient size */
1721         ImageParameters->BufferSize = BufferSize;
1722         ImageParameters->Buffer = BlMmAllocateHeap(BufferSize);
1723         if (!ImageParameters->Buffer)
1724         {
1725             /* Bail out if we couldn't allocate it */
1726             return STATUS_NO_MEMORY;
1727         }
1728     }
1729 
1730     /* Zero out the parameter block */
1731     ParameterBlock = (PBOOT_APPLICATION_PARAMETER_BLOCK)ImageParameters->Buffer;
1732     RtlZeroMemory(ParameterBlock, BufferSize);
1733 
1734     /* Initialize it */
1735     ParameterBlock->Version = BOOT_APPLICATION_VERSION;
1736     ParameterBlock->Size = BufferSize;
1737     ParameterBlock->Signature[0] = BOOT_APPLICATION_SIGNATURE_1;
1738     ParameterBlock->Signature[1] = BOOT_APPLICATION_SIGNATURE_2;
1739     ParameterBlock->MemoryTranslationType = MmTranslationType;
1740     ParameterBlock->ImageType = IMAGE_FILE_MACHINE_I386;
1741     ParameterBlock->ImageBase = (ULONG_PTR)ImageBase;
1742     ParameterBlock->ImageSize = NtHeaders->OptionalHeader.SizeOfImage;
1743 
1744     /* Get the offset to the memory data */
1745     ParameterBlock->MemoryDataOffset = sizeof(*ParameterBlock);
1746 
1747     /* Fill it out */
1748     MemoryData = (PBL_MEMORY_DATA)((ULONG_PTR)ParameterBlock +
1749                                    ParameterBlock->MemoryDataOffset);
1750     MemoryData->Version = BL_MEMORY_DATA_VERSION;
1751     MemoryData->MdListOffset = sizeof(*MemoryData);
1752     MemoryData->DescriptorSize = sizeof(BL_MEMORY_DESCRIPTOR);
1753     MemoryData->DescriptorOffset = FIELD_OFFSET(BL_MEMORY_DESCRIPTOR, BasePage);
1754 
1755     /* And populate the memory map */
1756     MemoryParameters.Buffer = MemoryData + 1;
1757     Status = BlMmGetMemoryMap(&MemoryList,
1758                               &MemoryParameters,
1759                               BL_MM_INCLUDE_PERSISTENT_MEMORY |
1760                               BL_MM_INCLUDE_MAPPED_ALLOCATED |
1761                               BL_MM_INCLUDE_MAPPED_UNALLOCATED |
1762                               BL_MM_INCLUDE_UNMAPPED_ALLOCATED |
1763                               BL_MM_INCLUDE_RESERVED_ALLOCATED,
1764                               0);
1765     if (!NT_SUCCESS(Status))
1766     {
1767         return Status;
1768     }
1769 
1770     /* Now that we have the map, indicate the number of descriptors */
1771     MemoryData->DescriptorCount = MemoryParameters.ActualSize /
1772                                   MemoryData->DescriptorSize;
1773 
1774     /* Get the offset to the application entry */
1775     ParameterBlock->AppEntryOffset = ParameterBlock->MemoryDataOffset +
1776                                      MemoryData->MdListOffset +
1777                                      MemoryParameters.BufferSize;
1778 
1779     /* Fill it out */
1780     BootAppEntry = (PBL_APPLICATION_ENTRY)((ULONG_PTR)ParameterBlock +
1781                                            ParameterBlock->AppEntryOffset);
1782     RtlCopyMemory(BootAppEntry, AppEntry, EntrySize);
1783 
1784     /* Get the offset to the boot device */
1785     ParameterBlock->BootDeviceOffset = ParameterBlock->AppEntryOffset +
1786                                        EntrySize;
1787 
1788     /* Fill it out */
1789     BootDevice = (PBL_DEVICE_DESCRIPTOR)((ULONG_PTR)ParameterBlock +
1790                                          ParameterBlock->BootDeviceOffset);
1791     Status = ImgpCopyApplicationBootDevice(BootDevice, BlpBootDevice);
1792     if (!NT_SUCCESS(Status))
1793     {
1794         return Status;
1795     }
1796 
1797     /* Get the offset to the firmware data */
1798     ParameterBlock->FirmwareParametersOffset = ParameterBlock->BootDeviceOffset +
1799                                                BootDevice->Size;
1800 
1801     /* Fill it out */
1802     FirmwareParameters = (PBL_FIRMWARE_DESCRIPTOR)((ULONG_PTR)ParameterBlock +
1803                                                    ParameterBlock->
1804                                                    FirmwareParametersOffset);
1805     Status = BlFwGetParameters(FirmwareParameters);
1806     if (!NT_SUCCESS(Status))
1807     {
1808         return Status;
1809     }
1810 
1811     /* Get the offset to the return arguments */
1812     ParameterBlock->ReturnArgumentsOffset = ParameterBlock->FirmwareParametersOffset +
1813                                             sizeof(BL_FIRMWARE_DESCRIPTOR);
1814 
1815     /* Fill them out */
1816     ReturnArguments = (PBL_RETURN_ARGUMENTS)((ULONG_PTR)ParameterBlock +
1817                                              ParameterBlock->
1818                                              ReturnArgumentsOffset);
1819     ReturnArguments->Version = BL_RETURN_ARGUMENTS_VERSION;
1820     ReturnArguments->DataPage = 0;
1821     ReturnArguments->DataSize = 0;
1822 
1823     /* Structure complete */
1824     ImageParameters->ActualSize = ParameterBlock->ReturnArgumentsOffset +
1825                                   sizeof(*ReturnArguments);
1826     return STATUS_SUCCESS;
1827 }
1828 
1829 NTSTATUS
1830 ImgArchEfiStartBootApplication (
1831     _In_ PBL_APPLICATION_ENTRY AppEntry,
1832     _In_ PVOID ImageBase,
1833     _In_ ULONG ImageSize,
1834     _In_ PBL_RETURN_ARGUMENTS ReturnArguments
1835     )
1836 {
1837 #ifndef _M_ARM
1838     KDESCRIPTOR Gdt, Idt;
1839     ULONG BootSizeNeeded;
1840     NTSTATUS Status;
1841     PVOID BootData;
1842     PIMAGE_NT_HEADERS NtHeaders;
1843     PVOID NewStack, NewGdt, NewIdt;
1844     BL_BUFFER_DESCRIPTOR Parameters;
1845 
1846     /* Read the current IDT and GDT */
1847     _sgdt(&Gdt.Limit);
1848     __sidt(&Idt.Limit);
1849 
1850     /* Allocate space for the IDT, GDT, and 24 pages of stack */
1851     BootSizeNeeded = (ULONG_PTR)PAGE_ALIGN(Idt.Limit + Gdt.Limit + 1 + 25 * PAGE_SIZE);
1852     Status = MmPapAllocatePagesInRange(&BootData,
1853                                        BlLoaderArchData,
1854                                        BootSizeNeeded >> PAGE_SHIFT,
1855                                        0,
1856                                        0,
1857                                        NULL,
1858                                        0);
1859     if (!NT_SUCCESS(Status))
1860     {
1861         goto Quickie;
1862     }
1863 
1864     /* Zero the boot data */
1865     RtlZeroMemory(BootData, BootSizeNeeded);
1866 
1867     /* Set the new stack, GDT and IDT */
1868     NewStack = (PVOID)((ULONG_PTR)BootData + (24 * PAGE_SIZE) - 8);
1869     NewGdt = (PVOID)((ULONG_PTR)BootData + (24 * PAGE_SIZE));
1870     NewIdt = (PVOID)((ULONG_PTR)BootData + (24 * PAGE_SIZE) + Gdt.Limit + 1);
1871 
1872     /* Copy the current (firmware) GDT and IDT */
1873     RtlCopyMemory(NewGdt, (PVOID)Gdt.Base, Gdt.Limit + 1);
1874     RtlCopyMemory(NewIdt, (PVOID)Idt.Base, Idt.Limit + 1);
1875 
1876     /* Read the NT headers so that we can get the entrypoint later on */
1877     RtlImageNtHeaderEx(0, ImageBase, ImageSize, &NtHeaders);
1878 
1879     /* Prepare the application parameters */
1880     RtlZeroMemory(&Parameters, sizeof(Parameters));
1881     Status = ImgpInitializeBootApplicationParameters(&Parameters,
1882                                                      AppEntry,
1883                                                      ImageBase,
1884                                                      ImageSize);
1885     if (NT_SUCCESS(Status))
1886     {
1887         /* Set the firmware GDT/IDT as the one the application will use */
1888         BootAppGdtRegister = Gdt;
1889         BootAppIdtRegister = Idt;
1890 
1891         /* Set the entrypoint, parameters, and stack */
1892         BootApp32EntryRoutine = (PVOID)((ULONG_PTR)ImageBase +
1893                                         NtHeaders->OptionalHeader.
1894                                         AddressOfEntryPoint);
1895         BootApp32Parameters = Parameters.Buffer;
1896         BootApp32Stack = NewStack;
1897 
1898 #if BL_KD_SUPPORT
1899         /* Disable the kernel debugger */
1900         BlBdStop();
1901 #endif
1902         /* Make it so */
1903         Archx86TransferTo32BitApplicationAsm();
1904 
1905         /* Not yet implemented. This is the last step! */
1906         EfiPrintf(L"EFI APPLICATION RETURNED!!!\r\n");
1907         EfiStall(100000000);
1908 
1909 #if BL_KD_SUPPORT
1910         /* Re-enable the kernel debugger */
1911         BlBdStart();
1912 #endif
1913     }
1914 
1915 Quickie:
1916     /* Check if we had boot data allocated */
1917     if (BootData)
1918     {
1919         /* Free it */
1920         MmPapFreePages(BootData, BL_MM_INCLUDE_MAPPED_ALLOCATED);
1921     }
1922 #else
1923     EfiPrintf(L"ImgArchEfiStartBootApplication not implemented for this platform.\r\n");
1924 #endif
1925 
1926     /* All done */
1927     return STATUS_NOT_IMPLEMENTED;
1928 }
1929 
1930 NTSTATUS
1931 BlImgStartBootApplication (
1932     _In_ ULONG AppHandle,
1933     _Inout_opt_ PBL_RETURN_ARGUMENTS ReturnArguments
1934     )
1935 {
1936     PBL_IMAGE_APPLICATION_ENTRY ImageAppEntry;
1937     BL_RETURN_ARGUMENTS LocalReturnArgs;
1938     PBL_FILE_SYSTEM_ENTRY FileSystem;
1939     PLIST_ENTRY NextEntry, ListHead;
1940     NTSTATUS Status;
1941 
1942     /* Check if we don't have an argument structure */
1943     if (!ReturnArguments)
1944     {
1945         /* Initialize a local copy and use it instead */
1946         LocalReturnArgs.Version = BL_RETURN_ARGUMENTS_VERSION;
1947         LocalReturnArgs.Status = STATUS_SUCCESS;
1948         LocalReturnArgs.Flags = 0;
1949         LocalReturnArgs.DataPage = 0;
1950         LocalReturnArgs.DataSize = 0;
1951         ReturnArguments = &LocalReturnArgs;
1952     }
1953 
1954     /* Make sure the handle index is valid */
1955     if (IapTableEntries <= AppHandle)
1956     {
1957         return STATUS_INVALID_PARAMETER;
1958     }
1959 
1960     /* Get the entry for this handle, making sure it exists */
1961     ImageAppEntry = IapImageTable[AppHandle];
1962     if (!ImageAppEntry)
1963     {
1964         return STATUS_INVALID_PARAMETER;
1965     }
1966 
1967     /* Loop the registered file systems */
1968     ListHead = &RegisteredFileSystems;
1969     NextEntry = RegisteredFileSystems.Flink;
1970     while (NextEntry != ListHead)
1971     {
1972         /* Get the filesystem entry */
1973         FileSystem = CONTAINING_RECORD(NextEntry,
1974                                        BL_FILE_SYSTEM_ENTRY,
1975                                        ListEntry);
1976 
1977         /* See if it has a purge callback */
1978         if (FileSystem->PurgeCallback)
1979         {
1980             /* Call it */
1981             FileSystem->PurgeCallback();
1982         }
1983 
1984         /* Move to the next entry */
1985         NextEntry = NextEntry->Flink;
1986     }
1987 
1988     /* TODO  -- flush the block I/O cache too */
1989     //BlockIoPurgeCache();
1990 
1991     /* Call into EFI land to start the boot application */
1992     Status = ImgArchEfiStartBootApplication(ImageAppEntry->AppEntry,
1993                                             ImageAppEntry->ImageBase,
1994                                             ImageAppEntry->ImageSize,
1995                                             ReturnArguments);
1996 
1997     /* Parse any arguments we got on the way back */
1998     BlpPdParseReturnArguments(ReturnArguments);
1999 
2000 #if BL_BITLOCKER_SUPPORT
2001     /* Bitlocker stuff */
2002     FvebpCheckAllPartitions(TRUE);
2003 #endif
2004 
2005 #if BL_TPM_SUPPORT
2006     /* Notify a TPM/SI event */
2007     BlEnNotifyEvent(0x10000005, NULL);
2008 #endif
2009 
2010     /* Reset the display */
2011     BlpDisplayReinitialize();
2012 
2013     /* TODO -- reset ETW */
2014     //BlpLogInitialize();
2015 
2016     /* All done */
2017     return Status;
2018 }
2019 
2020 NTSTATUS
2021 BlImgUnloadBootApplication (
2022     _In_ ULONG AppHandle
2023     )
2024 {
2025     PBL_IMAGE_APPLICATION_ENTRY ImageAppEntry;
2026     NTSTATUS Status;
2027 
2028     /* Make sure the handle index is valid */
2029     if (IapTableEntries <= AppHandle)
2030     {
2031         return STATUS_INVALID_PARAMETER;
2032     }
2033 
2034     /* Get the entry for this handle, making sure it exists */
2035     ImageAppEntry = IapImageTable[AppHandle];
2036     if (!ImageAppEntry)
2037     {
2038         return STATUS_INVALID_PARAMETER;
2039     }
2040 
2041     /* Unload the image */
2042     Status = BlImgUnLoadImage(ImageAppEntry->ImageBase,
2043                               ImageAppEntry->ImageSize,
2044                               0);
2045     if (NT_SUCCESS(Status))
2046     {
2047         /* Normalize the success code */
2048         Status = STATUS_SUCCESS;
2049     }
2050     else
2051     {
2052         /* Normalize the failure code */
2053         Status = STATUS_MEMORY_NOT_ALLOCATED;
2054     }
2055 
2056     /* Free the entry and the image entry as well */
2057     BlMmFreeHeap(ImageAppEntry->AppEntry);
2058     BlMmFreeHeap(ImageAppEntry);
2059 
2060     /* Clear the handle */
2061     IapImageTable[AppHandle] = NULL;
2062 
2063     /* Free one entry */
2064     if (!(--IapAllocatedTableEntries))
2065     {
2066         /* There are no more, so get rid of the table itself */
2067         BlMmFreeHeap(IapImageTable);
2068         IapImageTable = NULL;
2069         IapTableEntries = 0;
2070     }
2071 
2072     /* All good */
2073     return Status;
2074 }
2075