]> git.proxmox.com Git - mirror_edk2.git/blob - EmulatorPkg/Win/Host/WinHost.c
5b780ca8afef1a98626de9e79b7d7ea350ec89ba
[mirror_edk2.git] / EmulatorPkg / Win / Host / WinHost.c
1 /**@file
2 WinNt emulator of pre-SEC phase. It's really a Win32 application, but this is
3 Ok since all the other modules for NT32 are NOT Win32 applications.
4
5 This program gets NT32 PCD setting and figures out what the memory layout
6 will be, how may FD's will be loaded and also what the boot mode is.
7
8 This code produces 128 K of temporary memory for the SEC stack by directly
9 allocate memory space with ReadWrite and Execute attribute.
10
11 Copyright (c) 2006 - 2022, Intel Corporation. All rights reserved.<BR>
12 (C) Copyright 2016-2020 Hewlett Packard Enterprise Development LP<BR>
13 SPDX-License-Identifier: BSD-2-Clause-Patent
14 **/
15
16 #include "WinHost.h"
17
18 #ifndef SE_TIME_ZONE_NAME
19 #define SE_TIME_ZONE_NAME TEXT("SeTimeZonePrivilege")
20 #endif
21
22 //
23 // The growth size for array of module handle entries
24 //
25 #define MAX_PDB_NAME_TO_MOD_HANDLE_ARRAY_SIZE 0x100
26
27 //
28 // Module handle entry structure
29 //
30 typedef struct {
31 CHAR8 *PdbPointer;
32 VOID *ModHandle;
33 } PDB_NAME_TO_MOD_HANDLE;
34
35 //
36 // An Array to hold the module handles
37 //
38 PDB_NAME_TO_MOD_HANDLE *mPdbNameModHandleArray = NULL;
39 UINTN mPdbNameModHandleArraySize = 0;
40
41 //
42 // Default information about where the FD is located.
43 // This array gets filled in with information from PcdWinNtFirmwareVolume
44 // The number of array elements is allocated base on parsing
45 // PcdWinNtFirmwareVolume and the memory is never freed.
46 //
47 UINTN gFdInfoCount = 0;
48 NT_FD_INFO *gFdInfo;
49
50 //
51 // Array that supports separate memory ranges.
52 // The memory ranges are set by PcdWinNtMemorySizeForSecMain.
53 // The number of array elements is allocated base on parsing
54 // PcdWinNtMemorySizeForSecMain value and the memory is never freed.
55 //
56 UINTN gSystemMemoryCount = 0;
57 NT_SYSTEM_MEMORY *gSystemMemory;
58
59 /*++
60
61 Routine Description:
62 This service is called from Index == 0 until it returns EFI_UNSUPPORTED.
63 It allows discontinuous memory regions to be supported by the emulator.
64 It uses gSystemMemory[] and gSystemMemoryCount that were created by
65 parsing the host environment variable EFI_MEMORY_SIZE.
66 The size comes from the varaible and the address comes from the call to
67 UnixOpenFile.
68
69 Arguments:
70 Index - Which memory region to use
71 MemoryBase - Return Base address of memory region
72 MemorySize - Return size in bytes of the memory region
73
74 Returns:
75 EFI_SUCCESS - If memory region was mapped
76 EFI_UNSUPPORTED - If Index is not supported
77
78 **/
79 EFI_STATUS
80 WinPeiAutoScan (
81 IN UINTN Index,
82 OUT EFI_PHYSICAL_ADDRESS *MemoryBase,
83 OUT UINT64 *MemorySize
84 )
85 {
86 if (Index >= gSystemMemoryCount) {
87 return EFI_UNSUPPORTED;
88 }
89
90 *MemoryBase = gSystemMemory[Index].Memory;
91 *MemorySize = gSystemMemory[Index].Size;
92
93 return EFI_SUCCESS;
94 }
95
96 /*++
97
98 Routine Description:
99 Return the FD Size and base address. Since the FD is loaded from a
100 file into host memory only the SEC will know its address.
101
102 Arguments:
103 Index - Which FD, starts at zero.
104 FdSize - Size of the FD in bytes
105 FdBase - Start address of the FD. Assume it points to an FV Header
106 FixUp - Difference between actual FD address and build address
107
108 Returns:
109 EFI_SUCCESS - Return the Base address and size of the FV
110 EFI_UNSUPPORTED - Index does nto map to an FD in the system
111
112 **/
113 EFI_STATUS
114 WinFdAddress (
115 IN UINTN Index,
116 IN OUT EFI_PHYSICAL_ADDRESS *FdBase,
117 IN OUT UINT64 *FdSize,
118 IN OUT EFI_PHYSICAL_ADDRESS *FixUp
119 )
120 {
121 if (Index >= gFdInfoCount) {
122 return EFI_UNSUPPORTED;
123 }
124
125 *FdBase = (EFI_PHYSICAL_ADDRESS)(UINTN)gFdInfo[Index].Address;
126 *FdSize = (UINT64)gFdInfo[Index].Size;
127 *FixUp = 0;
128
129 if ((*FdBase == 0) && (*FdSize == 0)) {
130 return EFI_UNSUPPORTED;
131 }
132
133 if (Index == 0) {
134 //
135 // FD 0 has XIP code and well known PCD values
136 // If the memory buffer could not be allocated at the FD build address
137 // the Fixup is the difference.
138 //
139 *FixUp = *FdBase - PcdGet64 (PcdEmuFdBaseAddress);
140 }
141
142 return EFI_SUCCESS;
143 }
144
145 /*++
146
147 Routine Description:
148 Since the SEC is the only Unix program in stack it must export
149 an interface to do POSIX calls. gUnix is initialized in UnixThunk.c.
150
151 Arguments:
152 InterfaceSize - sizeof (EFI_WIN_NT_THUNK_PROTOCOL);
153 InterfaceBase - Address of the gUnix global
154
155 Returns:
156 EFI_SUCCESS - Data returned
157
158 **/
159 VOID *
160 WinThunk (
161 VOID
162 )
163 {
164 return &gEmuThunkProtocol;
165 }
166
167 EMU_THUNK_PPI mSecEmuThunkPpi = {
168 WinPeiAutoScan,
169 WinFdAddress,
170 WinThunk
171 };
172
173 VOID
174 SecPrint (
175 CHAR8 *Format,
176 ...
177 )
178 {
179 va_list Marker;
180 UINTN CharCount;
181 CHAR8 Buffer[0x1000];
182
183 va_start (Marker, Format);
184
185 _vsnprintf (Buffer, sizeof (Buffer), Format, Marker);
186
187 va_end (Marker);
188
189 CharCount = strlen (Buffer);
190 WriteFile (
191 GetStdHandle (STD_OUTPUT_HANDLE),
192 Buffer,
193 (DWORD)CharCount,
194 (LPDWORD)&CharCount,
195 NULL
196 );
197 }
198
199 /*++
200
201 Routine Description:
202 Check to see if an address range is in the EFI GCD memory map.
203
204 This is all of GCD for system memory passed to DXE Core. FV
205 mapping and other device mapped into system memory are not
206 inlcuded in the check.
207
208 Arguments:
209 Index - Which memory region to use
210 MemoryBase - Return Base address of memory region
211 MemorySize - Return size in bytes of the memory region
212
213 Returns:
214 TRUE - Address is in the EFI GCD memory map
215 FALSE - Address is NOT in memory map
216
217 **/
218 BOOLEAN
219 EfiSystemMemoryRange (
220 IN VOID *MemoryAddress
221 )
222 {
223 UINTN Index;
224 EFI_PHYSICAL_ADDRESS MemoryBase;
225
226 MemoryBase = (EFI_PHYSICAL_ADDRESS)(UINTN)MemoryAddress;
227 for (Index = 0; Index < gSystemMemoryCount; Index++) {
228 if ((MemoryBase >= gSystemMemory[Index].Memory) &&
229 (MemoryBase < (gSystemMemory[Index].Memory + gSystemMemory[Index].Size)))
230 {
231 return TRUE;
232 }
233 }
234
235 return FALSE;
236 }
237
238 EFI_STATUS
239 WinNtOpenFile (
240 IN CHAR16 *FileName OPTIONAL,
241 IN UINT32 MapSize,
242 IN DWORD CreationDisposition,
243 IN OUT VOID **BaseAddress,
244 OUT UINTN *Length
245 )
246
247 /*++
248
249 Routine Description:
250 Opens and memory maps a file using WinNt services. If *BaseAddress is non zero
251 the process will try and allocate the memory starting at BaseAddress.
252
253 Arguments:
254 FileName - The name of the file to open and map
255 MapSize - The amount of the file to map in bytes
256 CreationDisposition - The flags to pass to CreateFile(). Use to create new files for
257 memory emulation, and exiting files for firmware volume emulation
258 BaseAddress - The base address of the mapped file in the user address space.
259 If *BaseAddress is 0, the new memory region is used.
260 If *BaseAddress is not 0, the request memory region is used for
261 the mapping of the file into the process space.
262 Length - The size of the mapped region in bytes
263
264 Returns:
265 EFI_SUCCESS - The file was opened and mapped.
266 EFI_NOT_FOUND - FileName was not found in the current directory
267 EFI_DEVICE_ERROR - An error occurred attempting to map the opened file
268
269 --*/
270 {
271 HANDLE NtFileHandle;
272 HANDLE NtMapHandle;
273 VOID *VirtualAddress;
274 UINTN FileSize;
275
276 //
277 // Use Win API to open/create a file
278 //
279 NtFileHandle = INVALID_HANDLE_VALUE;
280 if (FileName != NULL) {
281 NtFileHandle = CreateFile (
282 FileName,
283 GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE,
284 FILE_SHARE_READ,
285 NULL,
286 CreationDisposition,
287 FILE_ATTRIBUTE_NORMAL,
288 NULL
289 );
290 if (NtFileHandle == INVALID_HANDLE_VALUE) {
291 return EFI_NOT_FOUND;
292 }
293 }
294
295 //
296 // Map the open file into a memory range
297 //
298 NtMapHandle = CreateFileMapping (
299 NtFileHandle,
300 NULL,
301 PAGE_EXECUTE_READWRITE,
302 0,
303 MapSize,
304 NULL
305 );
306 if (NtMapHandle == NULL) {
307 return EFI_DEVICE_ERROR;
308 }
309
310 //
311 // Get the virtual address (address in the emulator) of the mapped file
312 //
313 VirtualAddress = MapViewOfFileEx (
314 NtMapHandle,
315 FILE_MAP_EXECUTE | FILE_MAP_ALL_ACCESS,
316 0,
317 0,
318 MapSize,
319 *BaseAddress
320 );
321 if (VirtualAddress == NULL) {
322 return EFI_DEVICE_ERROR;
323 }
324
325 if (MapSize == 0) {
326 //
327 // Seek to the end of the file to figure out the true file size.
328 //
329 FileSize = SetFilePointer (
330 NtFileHandle,
331 0,
332 NULL,
333 FILE_END
334 );
335 if (FileSize == -1) {
336 return EFI_DEVICE_ERROR;
337 }
338
339 *Length = FileSize;
340 } else {
341 *Length = MapSize;
342 }
343
344 *BaseAddress = VirtualAddress;
345
346 return EFI_SUCCESS;
347 }
348
349 INTN
350 EFIAPI
351 main (
352 IN INT Argc,
353 IN CHAR8 **Argv,
354 IN CHAR8 **Envp
355 )
356
357 /*++
358
359 Routine Description:
360 Main entry point to SEC for WinNt. This is a Windows program
361
362 Arguments:
363 Argc - Number of command line arguments
364 Argv - Array of command line argument strings
365 Envp - Array of environment variable strings
366
367 Returns:
368 0 - Normal exit
369 1 - Abnormal exit
370
371 --*/
372 {
373 EFI_STATUS Status;
374 HANDLE Token;
375 TOKEN_PRIVILEGES TokenPrivileges;
376 VOID *TemporaryRam;
377 UINT32 TemporaryRamSize;
378 VOID *EmuMagicPage;
379 UINTN Index;
380 UINTN Index1;
381 CHAR16 *FileName;
382 CHAR16 *FileNamePtr;
383 BOOLEAN Done;
384 EFI_PEI_FILE_HANDLE FileHandle;
385 VOID *SecFile;
386 CHAR16 *MemorySizeStr;
387 CHAR16 *FirmwareVolumesStr;
388 UINTN ProcessAffinityMask;
389 UINTN SystemAffinityMask;
390 INT32 LowBit;
391
392 //
393 // Enable the privilege so that RTC driver can successfully run SetTime()
394 //
395 OpenProcessToken (GetCurrentProcess (), TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY, &Token);
396 if (LookupPrivilegeValue (NULL, SE_TIME_ZONE_NAME, &TokenPrivileges.Privileges[0].Luid)) {
397 TokenPrivileges.PrivilegeCount = 1;
398 TokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
399 AdjustTokenPrivileges (Token, FALSE, &TokenPrivileges, 0, (PTOKEN_PRIVILEGES)NULL, 0);
400 }
401
402 MemorySizeStr = (CHAR16 *)PcdGetPtr (PcdEmuMemorySize);
403 FirmwareVolumesStr = (CHAR16 *)PcdGetPtr (PcdEmuFirmwareVolume);
404
405 SecPrint ("\n\rEDK II WIN Host Emulation Environment from http://www.tianocore.org/edk2/\n\r");
406
407 //
408 // Determine the first thread available to this process.
409 //
410 if (GetProcessAffinityMask (GetCurrentProcess (), &ProcessAffinityMask, &SystemAffinityMask)) {
411 LowBit = (INT32)LowBitSet32 ((UINT32)ProcessAffinityMask);
412 if (LowBit != -1) {
413 //
414 // Force the system to bind the process to a single thread to work
415 // around odd semaphore type crashes.
416 //
417 SetProcessAffinityMask (GetCurrentProcess (), (INTN)(BIT0 << LowBit));
418 }
419 }
420
421 //
422 // Make some Windows calls to Set the process to the highest priority in the
423 // idle class. We need this to have good performance.
424 //
425 SetPriorityClass (GetCurrentProcess (), IDLE_PRIORITY_CLASS);
426 SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_HIGHEST);
427
428 SecInitializeThunk ();
429 //
430 // PPIs pased into PEI_CORE
431 //
432 AddThunkPpi (EFI_PEI_PPI_DESCRIPTOR_PPI, &gEmuThunkPpiGuid, &mSecEmuThunkPpi);
433
434 //
435 // Emulator Bus Driver Thunks
436 //
437 AddThunkProtocol (&mWinNtWndThunkIo, (CHAR16 *)PcdGetPtr (PcdEmuGop), TRUE);
438 AddThunkProtocol (&mWinNtFileSystemThunkIo, (CHAR16 *)PcdGetPtr (PcdEmuFileSystem), TRUE);
439 AddThunkProtocol (&mWinNtBlockIoThunkIo, (CHAR16 *)PcdGetPtr (PcdEmuVirtualDisk), TRUE);
440 AddThunkProtocol (&mWinNtSnpThunkIo, (CHAR16 *)PcdGetPtr (PcdEmuNetworkInterface), TRUE);
441
442 //
443 // Allocate space for gSystemMemory Array
444 //
445 gSystemMemoryCount = CountSeparatorsInString (MemorySizeStr, '!') + 1;
446 gSystemMemory = calloc (gSystemMemoryCount, sizeof (NT_SYSTEM_MEMORY));
447 if (gSystemMemory == NULL) {
448 SecPrint ("ERROR : Can not allocate memory for %S. Exiting.\n\r", MemorySizeStr);
449 exit (1);
450 }
451
452 //
453 // Allocate "physical" memory space for emulator. It will be reported out later throuth MemoryAutoScan()
454 //
455 for (Index = 0, Done = FALSE; !Done; Index++) {
456 ASSERT (Index < gSystemMemoryCount);
457 gSystemMemory[Index].Size = ((UINT64)_wtoi (MemorySizeStr)) * ((UINT64)SIZE_1MB);
458 gSystemMemory[Index].Memory = (EFI_PHYSICAL_ADDRESS)(UINTN)VirtualAlloc (NULL, (SIZE_T)(gSystemMemory[Index].Size), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
459 if (gSystemMemory[Index].Memory == 0) {
460 return EFI_OUT_OF_RESOURCES;
461 }
462
463 //
464 // Find the next region
465 //
466 for (Index1 = 0; MemorySizeStr[Index1] != '!' && MemorySizeStr[Index1] != 0; Index1++) {
467 }
468
469 if (MemorySizeStr[Index1] == 0) {
470 Done = TRUE;
471 }
472
473 MemorySizeStr = MemorySizeStr + Index1 + 1;
474 }
475
476 //
477 // Allocate space for gSystemMemory Array
478 //
479 gFdInfoCount = CountSeparatorsInString (FirmwareVolumesStr, '!') + 1;
480 gFdInfo = calloc (gFdInfoCount, sizeof (NT_FD_INFO));
481 if (gFdInfo == NULL) {
482 SecPrint ("ERROR : Can not allocate memory for %S. Exiting.\n\r", FirmwareVolumesStr);
483 exit (1);
484 }
485
486 //
487 // Setup Boot Mode.
488 //
489 SecPrint (" BootMode 0x%02x\n\r", PcdGet32 (PcdEmuBootMode));
490
491 //
492 // Allocate 128K memory to emulate temp memory for PEI.
493 // on a real platform this would be SRAM, or using the cache as RAM.
494 // Set TemporaryRam to zero so WinNtOpenFile will allocate a new mapping
495 //
496 TemporaryRamSize = TEMPORARY_RAM_SIZE;
497 TemporaryRam = VirtualAlloc (NULL, (SIZE_T)(TemporaryRamSize), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
498 if (TemporaryRam == NULL) {
499 SecPrint ("ERROR : Can not allocate enough space for SecStack\n\r");
500 exit (1);
501 }
502
503 SetMem32 (TemporaryRam, TemporaryRamSize, PcdGet32 (PcdInitValueInTempStack));
504
505 SecPrint (
506 " OS Emulator passing in %u KB of temp RAM at 0x%08lx to SEC\n\r",
507 TemporaryRamSize / SIZE_1KB,
508 TemporaryRam
509 );
510
511 //
512 // If enabled use the magic page to communicate between modules
513 // This replaces the PI PeiServicesTable pointer mechanism that
514 // deos not work in the emulator. It also allows the removal of
515 // writable globals from SEC, PEI_CORE (libraries), PEIMs
516 //
517 EmuMagicPage = (VOID *)(UINTN)(FixedPcdGet64 (PcdPeiServicesTablePage) & MAX_UINTN);
518 if (EmuMagicPage != NULL) {
519 UINT64 Size;
520 Status = WinNtOpenFile (
521 NULL,
522 SIZE_4KB,
523 0,
524 &EmuMagicPage,
525 &Size
526 );
527 if (EFI_ERROR (Status)) {
528 SecPrint ("ERROR : Could not allocate PeiServicesTablePage @ %p\n\r", EmuMagicPage);
529 return EFI_DEVICE_ERROR;
530 }
531 }
532
533 //
534 // Open All the firmware volumes and remember the info in the gFdInfo global
535 // Meanwhile, find the SEC Core.
536 //
537 FileNamePtr = AllocateCopyPool (StrSize (FirmwareVolumesStr), FirmwareVolumesStr);
538 if (FileNamePtr == NULL) {
539 SecPrint ("ERROR : Can not allocate memory for firmware volume string\n\r");
540 exit (1);
541 }
542
543 for (Done = FALSE, Index = 0, SecFile = NULL; !Done; Index++) {
544 FileName = FileNamePtr;
545 for (Index1 = 0; (FileNamePtr[Index1] != '!') && (FileNamePtr[Index1] != 0); Index1++) {
546 }
547
548 if (FileNamePtr[Index1] == 0) {
549 Done = TRUE;
550 } else {
551 FileNamePtr[Index1] = '\0';
552 FileNamePtr = &FileNamePtr[Index1 + 1];
553 }
554
555 //
556 // Open the FD and remember where it got mapped into our processes address space
557 //
558 Status = WinNtOpenFile (
559 FileName,
560 0,
561 OPEN_EXISTING,
562 &gFdInfo[Index].Address,
563 &gFdInfo[Index].Size
564 );
565 if (EFI_ERROR (Status)) {
566 SecPrint ("ERROR : Can not open Firmware Device File %S (0x%X). Exiting.\n\r", FileName, Status);
567 exit (1);
568 }
569
570 SecPrint (" FD loaded from %S", FileName);
571
572 if (SecFile == NULL) {
573 //
574 // Assume the beginning of the FD is an FV and look for the SEC Core.
575 // Load the first one we find.
576 //
577 FileHandle = NULL;
578 Status = PeiServicesFfsFindNextFile (
579 EFI_FV_FILETYPE_SECURITY_CORE,
580 (EFI_PEI_FV_HANDLE)gFdInfo[Index].Address,
581 &FileHandle
582 );
583 if (!EFI_ERROR (Status)) {
584 Status = PeiServicesFfsFindSectionData (EFI_SECTION_PE32, FileHandle, &SecFile);
585 if (!EFI_ERROR (Status)) {
586 SecPrint (" contains SEC Core");
587 }
588 }
589 }
590
591 SecPrint ("\n\r");
592 }
593
594 //
595 // Hand off to SEC Core
596 //
597 SecLoadSecCore ((UINTN)TemporaryRam, TemporaryRamSize, gFdInfo[0].Address, gFdInfo[0].Size, SecFile);
598
599 //
600 // If we get here, then the SEC Core returned. This is an error as SEC should
601 // always hand off to PEI Core and then on to DXE Core.
602 //
603 SecPrint ("ERROR : SEC returned\n\r");
604 exit (1);
605 }
606
607 VOID
608 SecLoadSecCore (
609 IN UINTN TemporaryRam,
610 IN UINTN TemporaryRamSize,
611 IN VOID *BootFirmwareVolumeBase,
612 IN UINTN BootFirmwareVolumeSize,
613 IN VOID *SecCorePe32File
614 )
615
616 /*++
617
618 Routine Description:
619 This is the service to load the SEC Core from the Firmware Volume
620
621 Arguments:
622 TemporaryRam - Memory to use for SEC.
623 TemporaryRamSize - Size of Memory to use for SEC
624 BootFirmwareVolumeBase - Start of the Boot FV
625 SecCorePe32File - SEC Core PE32
626
627 Returns:
628 Success means control is transferred and thus we should never return
629
630 --*/
631 {
632 EFI_STATUS Status;
633 VOID *TopOfStack;
634 VOID *SecCoreEntryPoint;
635 EFI_SEC_PEI_HAND_OFF *SecCoreData;
636 UINTN SecStackSize;
637
638 //
639 // Compute Top Of Memory for Stack and PEI Core Allocations
640 //
641 SecStackSize = TemporaryRamSize >> 1;
642
643 //
644 // |-----------| <---- TemporaryRamBase + TemporaryRamSize
645 // | Heap |
646 // | |
647 // |-----------| <---- StackBase / PeiTemporaryMemoryBase
648 // | |
649 // | Stack |
650 // |-----------| <---- TemporaryRamBase
651 //
652 TopOfStack = (VOID *)(TemporaryRam + SecStackSize);
653
654 //
655 // Reservet space for storing PeiCore's parament in stack.
656 //
657 TopOfStack = (VOID *)((UINTN)TopOfStack - sizeof (EFI_SEC_PEI_HAND_OFF) - CPU_STACK_ALIGNMENT);
658 TopOfStack = ALIGN_POINTER (TopOfStack, CPU_STACK_ALIGNMENT);
659
660 //
661 // Bind this information into the SEC hand-off state
662 //
663 SecCoreData = (EFI_SEC_PEI_HAND_OFF *)(UINTN)TopOfStack;
664 SecCoreData->DataSize = sizeof (EFI_SEC_PEI_HAND_OFF);
665 SecCoreData->BootFirmwareVolumeBase = BootFirmwareVolumeBase;
666 SecCoreData->BootFirmwareVolumeSize = BootFirmwareVolumeSize;
667 SecCoreData->TemporaryRamBase = (VOID *)TemporaryRam;
668 SecCoreData->TemporaryRamSize = TemporaryRamSize;
669 SecCoreData->StackBase = SecCoreData->TemporaryRamBase;
670 SecCoreData->StackSize = SecStackSize;
671 SecCoreData->PeiTemporaryRamBase = (VOID *)((UINTN)SecCoreData->TemporaryRamBase + SecStackSize);
672 SecCoreData->PeiTemporaryRamSize = TemporaryRamSize - SecStackSize;
673
674 //
675 // Load the PEI Core from a Firmware Volume
676 //
677 Status = SecPeCoffGetEntryPoint (
678 SecCorePe32File,
679 &SecCoreEntryPoint
680 );
681 if (EFI_ERROR (Status)) {
682 return;
683 }
684
685 //
686 // Transfer control to the SEC Core
687 //
688 SwitchStack (
689 (SWITCH_STACK_ENTRY_POINT)(UINTN)SecCoreEntryPoint,
690 SecCoreData,
691 GetThunkPpiList (),
692 TopOfStack
693 );
694 //
695 // If we get here, then the SEC Core returned. This is an error
696 //
697 return;
698 }
699
700 RETURN_STATUS
701 EFIAPI
702 SecPeCoffGetEntryPoint (
703 IN VOID *Pe32Data,
704 IN OUT VOID **EntryPoint
705 )
706 {
707 EFI_STATUS Status;
708 PE_COFF_LOADER_IMAGE_CONTEXT ImageContext;
709
710 ZeroMem (&ImageContext, sizeof (ImageContext));
711 ImageContext.Handle = Pe32Data;
712
713 ImageContext.ImageRead = (PE_COFF_LOADER_READ_FILE)SecImageRead;
714
715 Status = PeCoffLoaderGetImageInfo (&ImageContext);
716 if (EFI_ERROR (Status)) {
717 return Status;
718 }
719
720 //
721 // Allocate space in NT (not emulator) memory with ReadWrite and Execute attribute.
722 // Extra space is for alignment
723 //
724 ImageContext.ImageAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)VirtualAlloc (NULL, (SIZE_T)(ImageContext.ImageSize + (ImageContext.SectionAlignment * 2)), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
725 if (ImageContext.ImageAddress == 0) {
726 return EFI_OUT_OF_RESOURCES;
727 }
728
729 //
730 // Align buffer on section boundary
731 //
732 ImageContext.ImageAddress += ImageContext.SectionAlignment - 1;
733 ImageContext.ImageAddress &= ~((EFI_PHYSICAL_ADDRESS)ImageContext.SectionAlignment - 1);
734
735 Status = PeCoffLoaderLoadImage (&ImageContext);
736 if (EFI_ERROR (Status)) {
737 return Status;
738 }
739
740 Status = PeCoffLoaderRelocateImage (&ImageContext);
741 if (EFI_ERROR (Status)) {
742 return Status;
743 }
744
745 *EntryPoint = (VOID *)(UINTN)ImageContext.EntryPoint;
746
747 return EFI_SUCCESS;
748 }
749
750 EFI_STATUS
751 EFIAPI
752 SecImageRead (
753 IN VOID *FileHandle,
754 IN UINTN FileOffset,
755 IN OUT UINTN *ReadSize,
756 OUT VOID *Buffer
757 )
758
759 /*++
760
761 Routine Description:
762 Support routine for the PE/COFF Loader that reads a buffer from a PE/COFF file
763
764 Arguments:
765 FileHandle - The handle to the PE/COFF file
766 FileOffset - The offset, in bytes, into the file to read
767 ReadSize - The number of bytes to read from the file starting at FileOffset
768 Buffer - A pointer to the buffer to read the data into.
769
770 Returns:
771 EFI_SUCCESS - ReadSize bytes of data were read into Buffer from the PE/COFF file starting at FileOffset
772
773 --*/
774 {
775 CHAR8 *Destination8;
776 CHAR8 *Source8;
777 UINTN Length;
778
779 Destination8 = Buffer;
780 Source8 = (CHAR8 *)((UINTN)FileHandle + FileOffset);
781 Length = *ReadSize;
782 while (Length--) {
783 *(Destination8++) = *(Source8++);
784 }
785
786 return EFI_SUCCESS;
787 }
788
789 CHAR16 *
790 AsciiToUnicode (
791 IN CHAR8 *Ascii,
792 IN UINTN *StrLen OPTIONAL
793 )
794
795 /*++
796
797 Routine Description:
798 Convert the passed in Ascii string to Unicode.
799 Optionally return the length of the strings.
800
801 Arguments:
802 Ascii - Ascii string to convert
803 StrLen - Length of string
804
805 Returns:
806 Pointer to malloc'ed Unicode version of Ascii
807
808 --*/
809 {
810 UINTN Index;
811 CHAR16 *Unicode;
812
813 //
814 // Allocate a buffer for unicode string
815 //
816 for (Index = 0; Ascii[Index] != '\0'; Index++) {
817 }
818
819 Unicode = malloc ((Index + 1) * sizeof (CHAR16));
820 if (Unicode == NULL) {
821 return NULL;
822 }
823
824 for (Index = 0; Ascii[Index] != '\0'; Index++) {
825 Unicode[Index] = (CHAR16)Ascii[Index];
826 }
827
828 Unicode[Index] = '\0';
829
830 if (StrLen != NULL) {
831 *StrLen = Index;
832 }
833
834 return Unicode;
835 }
836
837 UINTN
838 CountSeparatorsInString (
839 IN CONST CHAR16 *String,
840 IN CHAR16 Separator
841 )
842
843 /*++
844
845 Routine Description:
846 Count the number of separators in String
847
848 Arguments:
849 String - String to process
850 Separator - Item to count
851
852 Returns:
853 Number of Separator in String
854
855 --*/
856 {
857 UINTN Count;
858
859 for (Count = 0; *String != '\0'; String++) {
860 if (*String == Separator) {
861 Count++;
862 }
863 }
864
865 return Count;
866 }
867
868 /**
869 Store the ModHandle in an array indexed by the Pdb File name.
870 The ModHandle is needed to unload the image.
871 @param ImageContext - Input data returned from PE Laoder Library. Used to find the
872 .PDB file name of the PE Image.
873 @param ModHandle - Returned from LoadLibraryEx() and stored for call to
874 FreeLibrary().
875 @return return EFI_SUCCESS when ModHandle was stored.
876 --*/
877 EFI_STATUS
878 AddModHandle (
879 IN PE_COFF_LOADER_IMAGE_CONTEXT *ImageContext,
880 IN VOID *ModHandle
881 )
882
883 {
884 UINTN Index;
885 PDB_NAME_TO_MOD_HANDLE *Array;
886 UINTN PreviousSize;
887 PDB_NAME_TO_MOD_HANDLE *TempArray;
888 HANDLE Handle;
889 UINTN Size;
890
891 //
892 // Return EFI_ALREADY_STARTED if this DLL has already been loaded
893 //
894 Array = mPdbNameModHandleArray;
895 for (Index = 0; Index < mPdbNameModHandleArraySize; Index++, Array++) {
896 if ((Array->PdbPointer != NULL) && (Array->ModHandle == ModHandle)) {
897 return EFI_ALREADY_STARTED;
898 }
899 }
900
901 Array = mPdbNameModHandleArray;
902 for (Index = 0; Index < mPdbNameModHandleArraySize; Index++, Array++) {
903 if (Array->PdbPointer == NULL) {
904 //
905 // Make a copy of the stirng and store the ModHandle
906 //
907 Handle = GetProcessHeap ();
908 Size = AsciiStrLen (ImageContext->PdbPointer) + 1;
909 Array->PdbPointer = HeapAlloc (Handle, HEAP_ZERO_MEMORY, Size);
910 ASSERT (Array->PdbPointer != NULL);
911
912 AsciiStrCpyS (Array->PdbPointer, Size, ImageContext->PdbPointer);
913 Array->ModHandle = ModHandle;
914 return EFI_SUCCESS;
915 }
916 }
917
918 //
919 // No free space in mPdbNameModHandleArray so grow it by
920 // MAX_PDB_NAME_TO_MOD_HANDLE_ARRAY_SIZE entires.
921 //
922 PreviousSize = mPdbNameModHandleArraySize * sizeof (PDB_NAME_TO_MOD_HANDLE);
923 mPdbNameModHandleArraySize += MAX_PDB_NAME_TO_MOD_HANDLE_ARRAY_SIZE;
924 //
925 // re-allocate a new buffer and copy the old values to the new locaiton.
926 //
927 TempArray = HeapAlloc (
928 GetProcessHeap (),
929 HEAP_ZERO_MEMORY,
930 mPdbNameModHandleArraySize * sizeof (PDB_NAME_TO_MOD_HANDLE)
931 );
932
933 CopyMem ((VOID *)(UINTN)TempArray, (VOID *)(UINTN)mPdbNameModHandleArray, PreviousSize);
934
935 HeapFree (GetProcessHeap (), 0, mPdbNameModHandleArray);
936
937 mPdbNameModHandleArray = TempArray;
938 if (mPdbNameModHandleArray == NULL) {
939 ASSERT (FALSE);
940 return EFI_OUT_OF_RESOURCES;
941 }
942
943 return AddModHandle (ImageContext, ModHandle);
944 }
945
946 /**
947 Return the ModHandle and delete the entry in the array.
948 @param ImageContext - Input data returned from PE Laoder Library. Used to find the
949 .PDB file name of the PE Image.
950 @return
951 ModHandle - ModHandle assoicated with ImageContext is returned
952 NULL - No ModHandle associated with ImageContext
953 **/
954 VOID *
955 RemoveModHandle (
956 IN PE_COFF_LOADER_IMAGE_CONTEXT *ImageContext
957 )
958 {
959 UINTN Index;
960 PDB_NAME_TO_MOD_HANDLE *Array;
961
962 if (ImageContext->PdbPointer == NULL) {
963 //
964 // If no PDB pointer there is no ModHandle so return NULL
965 //
966 return NULL;
967 }
968
969 Array = mPdbNameModHandleArray;
970 for (Index = 0; Index < mPdbNameModHandleArraySize; Index++, Array++) {
971 if ((Array->PdbPointer != NULL) && (AsciiStrCmp (Array->PdbPointer, ImageContext->PdbPointer) == 0)) {
972 //
973 // If you find a match return it and delete the entry
974 //
975 HeapFree (GetProcessHeap (), 0, Array->PdbPointer);
976 Array->PdbPointer = NULL;
977 return Array->ModHandle;
978 }
979 }
980
981 return NULL;
982 }
983
984 VOID
985 EFIAPI
986 PeCoffLoaderRelocateImageExtraAction (
987 IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *ImageContext
988 )
989 {
990 EFI_STATUS Status;
991 VOID *DllEntryPoint;
992 CHAR16 *DllFileName;
993 HMODULE Library;
994 UINTN Index;
995
996 ASSERT (ImageContext != NULL);
997 //
998 // If we load our own PE COFF images the Windows debugger can not source
999 // level debug our code. If a valid PDB pointer exists use it to load
1000 // the *.dll file as a library using Windows* APIs. This allows
1001 // source level debug. The image is still loaded and relocated
1002 // in the Framework memory space like on a real system (by the code above),
1003 // but the entry point points into the DLL loaded by the code below.
1004 //
1005
1006 DllEntryPoint = NULL;
1007
1008 //
1009 // Load the DLL if it's not an EBC image.
1010 //
1011 if ((ImageContext->PdbPointer != NULL) &&
1012 (ImageContext->Machine != EFI_IMAGE_MACHINE_EBC))
1013 {
1014 //
1015 // Convert filename from ASCII to Unicode
1016 //
1017 DllFileName = AsciiToUnicode (ImageContext->PdbPointer, &Index);
1018
1019 //
1020 // Check that we have a valid filename
1021 //
1022 if ((Index < 5) || (DllFileName[Index - 4] != '.')) {
1023 free (DllFileName);
1024
1025 //
1026 // Never return an error if PeCoffLoaderRelocateImage() succeeded.
1027 // The image will run, but we just can't source level debug. If we
1028 // return an error the image will not run.
1029 //
1030 return;
1031 }
1032
1033 //
1034 // Replace .PDB with .DLL on the filename
1035 //
1036 DllFileName[Index - 3] = 'D';
1037 DllFileName[Index - 2] = 'L';
1038 DllFileName[Index - 1] = 'L';
1039
1040 //
1041 // Load the .DLL file into the user process's address space for source
1042 // level debug
1043 //
1044 Library = LoadLibraryEx (DllFileName, NULL, DONT_RESOLVE_DLL_REFERENCES);
1045 if (Library != NULL) {
1046 //
1047 // InitializeDriver is the entry point we put in all our EFI DLL's. The
1048 // DONT_RESOLVE_DLL_REFERENCES argument to LoadLIbraryEx() suppresses the
1049 // normal DLL entry point of DllMain, and prevents other modules that are
1050 // referenced in side the DllFileName from being loaded. There is no error
1051 // checking as the we can point to the PE32 image loaded by Tiano. This
1052 // step is only needed for source level debugging
1053 //
1054 DllEntryPoint = (VOID *)(UINTN)GetProcAddress (Library, "InitializeDriver");
1055 }
1056
1057 if ((Library != NULL) && (DllEntryPoint != NULL)) {
1058 Status = AddModHandle (ImageContext, Library);
1059 if (Status == EFI_ALREADY_STARTED) {
1060 //
1061 // If the DLL has already been loaded before, then this instance of the DLL can not be debugged.
1062 //
1063 ImageContext->PdbPointer = NULL;
1064 SecPrint ("WARNING: DLL already loaded. No source level debug %S.\n\r", DllFileName);
1065 } else {
1066 //
1067 // This DLL is not already loaded, so source level debugging is supported.
1068 //
1069 ImageContext->EntryPoint = (EFI_PHYSICAL_ADDRESS)(UINTN)DllEntryPoint;
1070 SecPrint ("LoadLibraryEx (\n\r %S,\n\r NULL, DONT_RESOLVE_DLL_REFERENCES)\n\r", DllFileName);
1071 }
1072 } else {
1073 SecPrint ("WARNING: No source level debug %S. \n\r", DllFileName);
1074 }
1075
1076 free (DllFileName);
1077 }
1078 }
1079
1080 VOID
1081 EFIAPI
1082 PeCoffLoaderUnloadImageExtraAction (
1083 IN PE_COFF_LOADER_IMAGE_CONTEXT *ImageContext
1084 )
1085 {
1086 VOID *ModHandle;
1087
1088 ASSERT (ImageContext != NULL);
1089
1090 ModHandle = RemoveModHandle (ImageContext);
1091 if (ModHandle != NULL) {
1092 FreeLibrary (ModHandle);
1093 SecPrint ("FreeLibrary (\n\r %s)\n\r", ImageContext->PdbPointer);
1094 } else {
1095 SecPrint ("WARNING: Unload image without source level debug\n\r");
1096 }
1097 }
1098
1099 VOID
1100 _ModuleEntryPoint (
1101 VOID
1102 )
1103 {
1104 }