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