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