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