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