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