mirror of https://github.com/acidanthera/audk.git
1180 lines
34 KiB
C
1180 lines
34 KiB
C
/**@file
|
|
WinNt emulator of pre-SEC phase. It's really a Win32 application, but this is
|
|
Ok since all the other modules for NT32 are NOT Win32 applications.
|
|
|
|
This program gets NT32 PCD setting and figures out what the memory layout
|
|
will be, how may FD's will be loaded and also what the boot mode is.
|
|
|
|
This code produces 128 K of temporary memory for the SEC stack by directly
|
|
allocate memory space with ReadWrite and Execute attribute.
|
|
|
|
Copyright (c) 2006 - 2022, Intel Corporation. All rights reserved.<BR>
|
|
(C) Copyright 2016-2020 Hewlett Packard Enterprise Development LP<BR>
|
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
|
**/
|
|
|
|
#include "WinHost.h"
|
|
|
|
#ifndef SE_TIME_ZONE_NAME
|
|
#define SE_TIME_ZONE_NAME TEXT("SeTimeZonePrivilege")
|
|
#endif
|
|
|
|
//
|
|
// The growth size for array of module handle entries
|
|
//
|
|
#define MAX_PDB_NAME_TO_MOD_HANDLE_ARRAY_SIZE 0x100
|
|
|
|
//
|
|
// Module handle entry structure
|
|
//
|
|
typedef struct {
|
|
CHAR8 *PdbPointer;
|
|
VOID *ModHandle;
|
|
} PDB_NAME_TO_MOD_HANDLE;
|
|
|
|
//
|
|
// An Array to hold the module handles
|
|
//
|
|
PDB_NAME_TO_MOD_HANDLE *mPdbNameModHandleArray = NULL;
|
|
UINTN mPdbNameModHandleArraySize = 0;
|
|
|
|
//
|
|
// Default information about where the FD is located.
|
|
// This array gets filled in with information from PcdWinNtFirmwareVolume
|
|
// The number of array elements is allocated base on parsing
|
|
// PcdWinNtFirmwareVolume and the memory is never freed.
|
|
//
|
|
UINTN gFdInfoCount = 0;
|
|
NT_FD_INFO *gFdInfo;
|
|
|
|
//
|
|
// Array that supports separate memory ranges.
|
|
// The memory ranges are set by PcdWinNtMemorySizeForSecMain.
|
|
// The number of array elements is allocated base on parsing
|
|
// PcdWinNtMemorySizeForSecMain value and the memory is never freed.
|
|
//
|
|
UINTN gSystemMemoryCount = 0;
|
|
NT_SYSTEM_MEMORY *gSystemMemory;
|
|
|
|
BASE_LIBRARY_JUMP_BUFFER mResetJumpBuffer;
|
|
CHAR8 *mResetTypeStr[] = {
|
|
"EfiResetCold",
|
|
"EfiResetWarm",
|
|
"EfiResetShutdown",
|
|
"EfiResetPlatformSpecific"
|
|
};
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
This service is called from Index == 0 until it returns EFI_UNSUPPORTED.
|
|
It allows discontinuous memory regions to be supported by the emulator.
|
|
It uses gSystemMemory[] and gSystemMemoryCount that were created by
|
|
parsing the host environment variable EFI_MEMORY_SIZE.
|
|
The size comes from the varaible and the address comes from the call to
|
|
UnixOpenFile.
|
|
|
|
Arguments:
|
|
Index - Which memory region to use
|
|
MemoryBase - Return Base address of memory region
|
|
MemorySize - Return size in bytes of the memory region
|
|
|
|
Returns:
|
|
EFI_SUCCESS - If memory region was mapped
|
|
EFI_UNSUPPORTED - If Index is not supported
|
|
|
|
**/
|
|
EFI_STATUS
|
|
WinPeiAutoScan (
|
|
IN UINTN Index,
|
|
OUT EFI_PHYSICAL_ADDRESS *MemoryBase,
|
|
OUT UINT64 *MemorySize
|
|
)
|
|
{
|
|
if (Index >= gSystemMemoryCount) {
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
*MemoryBase = gSystemMemory[Index].Memory;
|
|
*MemorySize = gSystemMemory[Index].Size;
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
Return the FD Size and base address. Since the FD is loaded from a
|
|
file into host memory only the SEC will know its address.
|
|
|
|
Arguments:
|
|
Index - Which FD, starts at zero.
|
|
FdSize - Size of the FD in bytes
|
|
FdBase - Start address of the FD. Assume it points to an FV Header
|
|
FixUp - Difference between actual FD address and build address
|
|
|
|
Returns:
|
|
EFI_SUCCESS - Return the Base address and size of the FV
|
|
EFI_UNSUPPORTED - Index does nto map to an FD in the system
|
|
|
|
**/
|
|
EFI_STATUS
|
|
WinFdAddress (
|
|
IN UINTN Index,
|
|
IN OUT EFI_PHYSICAL_ADDRESS *FdBase,
|
|
IN OUT UINT64 *FdSize,
|
|
IN OUT EFI_PHYSICAL_ADDRESS *FixUp
|
|
)
|
|
{
|
|
if (Index >= gFdInfoCount) {
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
*FdBase = (EFI_PHYSICAL_ADDRESS)(UINTN)gFdInfo[Index].Address;
|
|
*FdSize = (UINT64)gFdInfo[Index].Size;
|
|
*FixUp = 0;
|
|
|
|
if ((*FdBase == 0) && (*FdSize == 0)) {
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
if (Index == 0) {
|
|
//
|
|
// FD 0 has XIP code and well known PCD values
|
|
// If the memory buffer could not be allocated at the FD build address
|
|
// the Fixup is the difference.
|
|
//
|
|
*FixUp = *FdBase - PcdGet64 (PcdEmuFdBaseAddress);
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
Since the SEC is the only Unix program in stack it must export
|
|
an interface to do POSIX calls. gUnix is initialized in UnixThunk.c.
|
|
|
|
Arguments:
|
|
InterfaceSize - sizeof (EFI_WIN_NT_THUNK_PROTOCOL);
|
|
InterfaceBase - Address of the gUnix global
|
|
|
|
Returns:
|
|
EFI_SUCCESS - Data returned
|
|
|
|
**/
|
|
VOID *
|
|
WinThunk (
|
|
VOID
|
|
)
|
|
{
|
|
return &gEmuThunkProtocol;
|
|
}
|
|
|
|
EMU_THUNK_PPI mSecEmuThunkPpi = {
|
|
WinPeiAutoScan,
|
|
WinFdAddress,
|
|
WinThunk
|
|
};
|
|
|
|
VOID
|
|
SecPrint (
|
|
CHAR8 *Format,
|
|
...
|
|
)
|
|
{
|
|
va_list Marker;
|
|
UINTN CharCount;
|
|
CHAR8 Buffer[0x1000];
|
|
|
|
va_start (Marker, Format);
|
|
|
|
_vsnprintf (Buffer, sizeof (Buffer), Format, Marker);
|
|
|
|
va_end (Marker);
|
|
|
|
CharCount = strlen (Buffer);
|
|
WriteFile (
|
|
GetStdHandle (STD_OUTPUT_HANDLE),
|
|
Buffer,
|
|
(DWORD)CharCount,
|
|
(LPDWORD)&CharCount,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
/**
|
|
Resets the entire platform.
|
|
|
|
@param[in] ResetType The type of reset to perform.
|
|
@param[in] ResetStatus The status code for the reset.
|
|
@param[in] DataSize The size, in bytes, of ResetData.
|
|
@param[in] ResetData For a ResetType of EfiResetCold, EfiResetWarm, or EfiResetShutdown
|
|
the data buffer starts with a Null-terminated string, optionally
|
|
followed by additional binary data. The string is a description
|
|
that the caller may use to further indicate the reason for the
|
|
system reset.
|
|
|
|
**/
|
|
VOID
|
|
EFIAPI
|
|
WinReset (
|
|
IN EFI_RESET_TYPE ResetType,
|
|
IN EFI_STATUS ResetStatus,
|
|
IN UINTN DataSize,
|
|
IN VOID *ResetData OPTIONAL
|
|
)
|
|
{
|
|
UINTN Index;
|
|
|
|
ASSERT (ResetType <= EfiResetPlatformSpecific);
|
|
SecPrint (" Emu ResetSystem is called: ResetType = %s\n", mResetTypeStr[ResetType]);
|
|
|
|
if (ResetType == EfiResetShutdown) {
|
|
exit (0);
|
|
} else {
|
|
//
|
|
// Unload all DLLs
|
|
//
|
|
for (Index = 0; Index < mPdbNameModHandleArraySize; Index++) {
|
|
if (mPdbNameModHandleArray[Index].PdbPointer != NULL) {
|
|
SecPrint (" Emu Unload DLL: %s\n", mPdbNameModHandleArray[Index].PdbPointer);
|
|
FreeLibrary (mPdbNameModHandleArray[Index].ModHandle);
|
|
HeapFree (GetProcessHeap (), 0, mPdbNameModHandleArray[Index].PdbPointer);
|
|
mPdbNameModHandleArray[Index].PdbPointer = NULL;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Jump back to SetJump with jump code = ResetType + 1
|
|
//
|
|
LongJump (&mResetJumpBuffer, ResetType + 1);
|
|
}
|
|
}
|
|
|
|
EFI_PEI_RESET2_PPI mEmuReset2Ppi = {
|
|
WinReset
|
|
};
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
Check to see if an address range is in the EFI GCD memory map.
|
|
|
|
This is all of GCD for system memory passed to DXE Core. FV
|
|
mapping and other device mapped into system memory are not
|
|
inlcuded in the check.
|
|
|
|
Arguments:
|
|
Index - Which memory region to use
|
|
MemoryBase - Return Base address of memory region
|
|
MemorySize - Return size in bytes of the memory region
|
|
|
|
Returns:
|
|
TRUE - Address is in the EFI GCD memory map
|
|
FALSE - Address is NOT in memory map
|
|
|
|
**/
|
|
BOOLEAN
|
|
EfiSystemMemoryRange (
|
|
IN VOID *MemoryAddress
|
|
)
|
|
{
|
|
UINTN Index;
|
|
EFI_PHYSICAL_ADDRESS MemoryBase;
|
|
|
|
MemoryBase = (EFI_PHYSICAL_ADDRESS)(UINTN)MemoryAddress;
|
|
for (Index = 0; Index < gSystemMemoryCount; Index++) {
|
|
if ((MemoryBase >= gSystemMemory[Index].Memory) &&
|
|
(MemoryBase < (gSystemMemory[Index].Memory + gSystemMemory[Index].Size)))
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
EFI_STATUS
|
|
WinNtOpenFile (
|
|
IN CHAR16 *FileName OPTIONAL,
|
|
IN UINT32 MapSize,
|
|
IN DWORD CreationDisposition,
|
|
IN OUT VOID **BaseAddress,
|
|
OUT UINTN *Length
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
Opens and memory maps a file using WinNt services. If *BaseAddress is non zero
|
|
the process will try and allocate the memory starting at BaseAddress.
|
|
|
|
Arguments:
|
|
FileName - The name of the file to open and map
|
|
MapSize - The amount of the file to map in bytes
|
|
CreationDisposition - The flags to pass to CreateFile(). Use to create new files for
|
|
memory emulation, and exiting files for firmware volume emulation
|
|
BaseAddress - The base address of the mapped file in the user address space.
|
|
If *BaseAddress is 0, the new memory region is used.
|
|
If *BaseAddress is not 0, the request memory region is used for
|
|
the mapping of the file into the process space.
|
|
Length - The size of the mapped region in bytes
|
|
|
|
Returns:
|
|
EFI_SUCCESS - The file was opened and mapped.
|
|
EFI_NOT_FOUND - FileName was not found in the current directory
|
|
EFI_DEVICE_ERROR - An error occurred attempting to map the opened file
|
|
|
|
--*/
|
|
{
|
|
HANDLE NtFileHandle;
|
|
HANDLE NtMapHandle;
|
|
VOID *VirtualAddress;
|
|
UINTN FileSize;
|
|
|
|
//
|
|
// Use Win API to open/create a file
|
|
//
|
|
NtFileHandle = INVALID_HANDLE_VALUE;
|
|
if (FileName != NULL) {
|
|
NtFileHandle = CreateFile (
|
|
FileName,
|
|
GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE,
|
|
FILE_SHARE_READ,
|
|
NULL,
|
|
CreationDisposition,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL
|
|
);
|
|
if (NtFileHandle == INVALID_HANDLE_VALUE) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Map the open file into a memory range
|
|
//
|
|
NtMapHandle = CreateFileMapping (
|
|
NtFileHandle,
|
|
NULL,
|
|
PAGE_EXECUTE_READWRITE,
|
|
0,
|
|
MapSize,
|
|
NULL
|
|
);
|
|
if (NtMapHandle == NULL) {
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
//
|
|
// Get the virtual address (address in the emulator) of the mapped file
|
|
//
|
|
VirtualAddress = MapViewOfFileEx (
|
|
NtMapHandle,
|
|
FILE_MAP_EXECUTE | FILE_MAP_ALL_ACCESS,
|
|
0,
|
|
0,
|
|
MapSize,
|
|
*BaseAddress
|
|
);
|
|
if (VirtualAddress == NULL) {
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
if (MapSize == 0) {
|
|
//
|
|
// Seek to the end of the file to figure out the true file size.
|
|
//
|
|
FileSize = SetFilePointer (
|
|
NtFileHandle,
|
|
0,
|
|
NULL,
|
|
FILE_END
|
|
);
|
|
if (FileSize == -1) {
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
*Length = FileSize;
|
|
} else {
|
|
*Length = MapSize;
|
|
}
|
|
|
|
*BaseAddress = VirtualAddress;
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
INTN
|
|
EFIAPI
|
|
main (
|
|
IN INT Argc,
|
|
IN CHAR8 **Argv,
|
|
IN CHAR8 **Envp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
Main entry point to SEC for WinNt. This is a Windows program
|
|
|
|
Arguments:
|
|
Argc - Number of command line arguments
|
|
Argv - Array of command line argument strings
|
|
Envp - Array of environment variable strings
|
|
|
|
Returns:
|
|
0 - Normal exit
|
|
1 - Abnormal exit
|
|
|
|
--*/
|
|
{
|
|
EFI_STATUS Status;
|
|
HANDLE Token;
|
|
TOKEN_PRIVILEGES TokenPrivileges;
|
|
VOID *TemporaryRam;
|
|
UINT32 TemporaryRamSize;
|
|
VOID *EmuMagicPage;
|
|
UINTN Index;
|
|
UINTN Index1;
|
|
CHAR16 *FileName;
|
|
CHAR16 *FileNamePtr;
|
|
BOOLEAN Done;
|
|
EFI_PEI_FILE_HANDLE FileHandle;
|
|
VOID *SecFile;
|
|
CHAR16 *MemorySizeStr;
|
|
CHAR16 *FirmwareVolumesStr;
|
|
UINTN ProcessAffinityMask;
|
|
UINTN SystemAffinityMask;
|
|
INT32 LowBit;
|
|
UINTN ResetJumpCode;
|
|
EMU_THUNK_PPI *SecEmuThunkPpi;
|
|
|
|
//
|
|
// Enable the privilege so that RTC driver can successfully run SetTime()
|
|
//
|
|
OpenProcessToken (GetCurrentProcess (), TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY, &Token);
|
|
if (LookupPrivilegeValue (NULL, SE_TIME_ZONE_NAME, &TokenPrivileges.Privileges[0].Luid)) {
|
|
TokenPrivileges.PrivilegeCount = 1;
|
|
TokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
|
|
AdjustTokenPrivileges (Token, FALSE, &TokenPrivileges, 0, (PTOKEN_PRIVILEGES)NULL, 0);
|
|
}
|
|
|
|
MemorySizeStr = (CHAR16 *)PcdGetPtr (PcdEmuMemorySize);
|
|
FirmwareVolumesStr = (CHAR16 *)PcdGetPtr (PcdEmuFirmwareVolume);
|
|
|
|
SecPrint ("\n\rEDK II WIN Host Emulation Environment from http://www.tianocore.org/edk2/\n\r");
|
|
|
|
//
|
|
// Determine the first thread available to this process.
|
|
//
|
|
if (GetProcessAffinityMask (GetCurrentProcess (), &ProcessAffinityMask, &SystemAffinityMask)) {
|
|
LowBit = (INT32)LowBitSet32 ((UINT32)ProcessAffinityMask);
|
|
if (LowBit != -1) {
|
|
//
|
|
// Force the system to bind the process to a single thread to work
|
|
// around odd semaphore type crashes.
|
|
//
|
|
SetProcessAffinityMask (GetCurrentProcess (), (INTN)(BIT0 << LowBit));
|
|
}
|
|
}
|
|
|
|
//
|
|
// Make some Windows calls to Set the process to the highest priority in the
|
|
// idle class. We need this to have good performance.
|
|
//
|
|
SetPriorityClass (GetCurrentProcess (), IDLE_PRIORITY_CLASS);
|
|
SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_HIGHEST);
|
|
|
|
SecInitializeThunk ();
|
|
//
|
|
// PPIs pased into PEI_CORE
|
|
//
|
|
SecEmuThunkPpi = AllocateZeroPool (sizeof (EMU_THUNK_PPI) + FixedPcdGet32 (PcdPersistentMemorySize));
|
|
if (SecEmuThunkPpi == NULL) {
|
|
SecPrint ("ERROR : Can not allocate memory for SecEmuThunkPpi. Exiting.\n");
|
|
exit (1);
|
|
}
|
|
|
|
CopyMem (SecEmuThunkPpi, &mSecEmuThunkPpi, sizeof (EMU_THUNK_PPI));
|
|
SecEmuThunkPpi->Argc = Argc;
|
|
SecEmuThunkPpi->Argv = Argv;
|
|
SecEmuThunkPpi->Envp = Envp;
|
|
SecEmuThunkPpi->PersistentMemorySize = FixedPcdGet32 (PcdPersistentMemorySize);
|
|
AddThunkPpi (EFI_PEI_PPI_DESCRIPTOR_PPI, &gEmuThunkPpiGuid, SecEmuThunkPpi);
|
|
AddThunkPpi (EFI_PEI_PPI_DESCRIPTOR_PPI, &gEfiPeiReset2PpiGuid, &mEmuReset2Ppi);
|
|
|
|
//
|
|
// Emulator Bus Driver Thunks
|
|
//
|
|
AddThunkProtocol (&mWinNtWndThunkIo, (CHAR16 *)PcdGetPtr (PcdEmuGop), TRUE);
|
|
AddThunkProtocol (&mWinNtFileSystemThunkIo, (CHAR16 *)PcdGetPtr (PcdEmuFileSystem), TRUE);
|
|
AddThunkProtocol (&mWinNtBlockIoThunkIo, (CHAR16 *)PcdGetPtr (PcdEmuVirtualDisk), TRUE);
|
|
AddThunkProtocol (&mWinNtSnpThunkIo, (CHAR16 *)PcdGetPtr (PcdEmuNetworkInterface), TRUE);
|
|
|
|
//
|
|
// Allocate space for gSystemMemory Array
|
|
//
|
|
gSystemMemoryCount = CountSeparatorsInString (MemorySizeStr, '!') + 1;
|
|
gSystemMemory = calloc (gSystemMemoryCount, sizeof (NT_SYSTEM_MEMORY));
|
|
if (gSystemMemory == NULL) {
|
|
SecPrint ("ERROR : Can not allocate memory for %S. Exiting.\n\r", MemorySizeStr);
|
|
exit (1);
|
|
}
|
|
|
|
//
|
|
// Allocate "physical" memory space for emulator. It will be reported out later throuth MemoryAutoScan()
|
|
//
|
|
for (Index = 0, Done = FALSE; !Done; Index++) {
|
|
ASSERT (Index < gSystemMemoryCount);
|
|
gSystemMemory[Index].Size = ((UINT64)_wtoi (MemorySizeStr)) * ((UINT64)SIZE_1MB);
|
|
gSystemMemory[Index].Memory = (EFI_PHYSICAL_ADDRESS)(UINTN)VirtualAlloc (NULL, (SIZE_T)(gSystemMemory[Index].Size), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
|
|
if (gSystemMemory[Index].Memory == 0) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Find the next region
|
|
//
|
|
for (Index1 = 0; MemorySizeStr[Index1] != '!' && MemorySizeStr[Index1] != 0; Index1++) {
|
|
}
|
|
|
|
if (MemorySizeStr[Index1] == 0) {
|
|
Done = TRUE;
|
|
}
|
|
|
|
MemorySizeStr = MemorySizeStr + Index1 + 1;
|
|
}
|
|
|
|
//
|
|
// Allocate space for gSystemMemory Array
|
|
//
|
|
gFdInfoCount = CountSeparatorsInString (FirmwareVolumesStr, '!') + 1;
|
|
gFdInfo = calloc (gFdInfoCount, sizeof (NT_FD_INFO));
|
|
if (gFdInfo == NULL) {
|
|
SecPrint ("ERROR : Can not allocate memory for %S. Exiting.\n\r", FirmwareVolumesStr);
|
|
exit (1);
|
|
}
|
|
|
|
//
|
|
// Setup Boot Mode.
|
|
//
|
|
SecPrint (" BootMode 0x%02x\n\r", PcdGet32 (PcdEmuBootMode));
|
|
|
|
//
|
|
// Allocate 128K memory to emulate temp memory for PEI.
|
|
// on a real platform this would be SRAM, or using the cache as RAM.
|
|
// Set TemporaryRam to zero so WinNtOpenFile will allocate a new mapping
|
|
//
|
|
TemporaryRamSize = TEMPORARY_RAM_SIZE;
|
|
TemporaryRam = VirtualAlloc (NULL, (SIZE_T)(TemporaryRamSize), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
|
|
if (TemporaryRam == NULL) {
|
|
SecPrint ("ERROR : Can not allocate enough space for SecStack\n\r");
|
|
exit (1);
|
|
}
|
|
|
|
//
|
|
// If enabled use the magic page to communicate between modules
|
|
// This replaces the PI PeiServicesTable pointer mechanism that
|
|
// deos not work in the emulator. It also allows the removal of
|
|
// writable globals from SEC, PEI_CORE (libraries), PEIMs
|
|
//
|
|
EmuMagicPage = (VOID *)(UINTN)(FixedPcdGet64 (PcdPeiServicesTablePage) & MAX_UINTN);
|
|
if (EmuMagicPage != NULL) {
|
|
UINT64 Size;
|
|
Status = WinNtOpenFile (
|
|
NULL,
|
|
SIZE_4KB,
|
|
0,
|
|
&EmuMagicPage,
|
|
&Size
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
SecPrint ("ERROR : Could not allocate PeiServicesTablePage @ %p\n\r", EmuMagicPage);
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Open All the firmware volumes and remember the info in the gFdInfo global
|
|
// Meanwhile, find the SEC Core.
|
|
//
|
|
FileNamePtr = AllocateCopyPool (StrSize (FirmwareVolumesStr), FirmwareVolumesStr);
|
|
if (FileNamePtr == NULL) {
|
|
SecPrint ("ERROR : Can not allocate memory for firmware volume string\n\r");
|
|
exit (1);
|
|
}
|
|
|
|
for (Done = FALSE, Index = 0, SecFile = NULL; !Done; Index++) {
|
|
FileName = FileNamePtr;
|
|
for (Index1 = 0; (FileNamePtr[Index1] != '!') && (FileNamePtr[Index1] != 0); Index1++) {
|
|
}
|
|
|
|
if (FileNamePtr[Index1] == 0) {
|
|
Done = TRUE;
|
|
} else {
|
|
FileNamePtr[Index1] = '\0';
|
|
FileNamePtr = &FileNamePtr[Index1 + 1];
|
|
}
|
|
|
|
//
|
|
// Open the FD and remember where it got mapped into our processes address space
|
|
//
|
|
Status = WinNtOpenFile (
|
|
FileName,
|
|
0,
|
|
OPEN_EXISTING,
|
|
&gFdInfo[Index].Address,
|
|
&gFdInfo[Index].Size
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
SecPrint ("ERROR : Can not open Firmware Device File %S (0x%X). Exiting.\n\r", FileName, Status);
|
|
exit (1);
|
|
}
|
|
|
|
SecPrint (" FD loaded from %S", FileName);
|
|
|
|
if (SecFile == NULL) {
|
|
//
|
|
// Assume the beginning of the FD is an FV and look for the SEC Core.
|
|
// Load the first one we find.
|
|
//
|
|
FileHandle = NULL;
|
|
Status = PeiServicesFfsFindNextFile (
|
|
EFI_FV_FILETYPE_SECURITY_CORE,
|
|
(EFI_PEI_FV_HANDLE)gFdInfo[Index].Address,
|
|
&FileHandle
|
|
);
|
|
if (!EFI_ERROR (Status)) {
|
|
Status = PeiServicesFfsFindSectionData (EFI_SECTION_PE32, FileHandle, &SecFile);
|
|
if (!EFI_ERROR (Status)) {
|
|
SecPrint (" contains SEC Core");
|
|
}
|
|
}
|
|
}
|
|
|
|
SecPrint ("\n\r");
|
|
}
|
|
|
|
ResetJumpCode = SetJump (&mResetJumpBuffer);
|
|
|
|
//
|
|
// Do not clear memory content for warm reset.
|
|
//
|
|
if (ResetJumpCode != EfiResetWarm + 1) {
|
|
SecPrint (" OS Emulator clearing temp RAM and physical RAM (to be discovered later)......\n\r");
|
|
SetMem32 (TemporaryRam, TemporaryRamSize, PcdGet32 (PcdInitValueInTempStack));
|
|
for (Index = 0; Index < gSystemMemoryCount; Index++) {
|
|
SetMem32 ((VOID *)(UINTN)gSystemMemory[Index].Memory, (UINTN)gSystemMemory[Index].Size, PcdGet32 (PcdInitValueInTempStack));
|
|
}
|
|
}
|
|
|
|
SecPrint (
|
|
" OS Emulator passing in %u KB of temp RAM at 0x%08lx to SEC\n\r",
|
|
TemporaryRamSize / SIZE_1KB,
|
|
TemporaryRam
|
|
);
|
|
//
|
|
// Hand off to SEC Core
|
|
//
|
|
SecLoadSecCore ((UINTN)TemporaryRam, TemporaryRamSize, gFdInfo[0].Address, gFdInfo[0].Size, SecFile);
|
|
|
|
//
|
|
// If we get here, then the SEC Core returned. This is an error as SEC should
|
|
// always hand off to PEI Core and then on to DXE Core.
|
|
//
|
|
SecPrint ("ERROR : SEC returned\n\r");
|
|
exit (1);
|
|
}
|
|
|
|
VOID
|
|
SecLoadSecCore (
|
|
IN UINTN TemporaryRam,
|
|
IN UINTN TemporaryRamSize,
|
|
IN VOID *BootFirmwareVolumeBase,
|
|
IN UINTN BootFirmwareVolumeSize,
|
|
IN VOID *SecCorePe32File
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
This is the service to load the SEC Core from the Firmware Volume
|
|
|
|
Arguments:
|
|
TemporaryRam - Memory to use for SEC.
|
|
TemporaryRamSize - Size of Memory to use for SEC
|
|
BootFirmwareVolumeBase - Start of the Boot FV
|
|
SecCorePe32File - SEC Core PE32
|
|
|
|
Returns:
|
|
Success means control is transferred and thus we should never return
|
|
|
|
--*/
|
|
{
|
|
EFI_STATUS Status;
|
|
VOID *TopOfStack;
|
|
VOID *SecCoreEntryPoint;
|
|
EFI_SEC_PEI_HAND_OFF *SecCoreData;
|
|
UINTN SecStackSize;
|
|
|
|
//
|
|
// Compute Top Of Memory for Stack and PEI Core Allocations
|
|
//
|
|
SecStackSize = TemporaryRamSize >> 1;
|
|
|
|
//
|
|
// |-----------| <---- TemporaryRamBase + TemporaryRamSize
|
|
// | Heap |
|
|
// | |
|
|
// |-----------| <---- StackBase / PeiTemporaryMemoryBase
|
|
// | |
|
|
// | Stack |
|
|
// |-----------| <---- TemporaryRamBase
|
|
//
|
|
TopOfStack = (VOID *)(TemporaryRam + SecStackSize);
|
|
|
|
//
|
|
// Reservet space for storing PeiCore's parament in stack.
|
|
//
|
|
TopOfStack = (VOID *)((UINTN)TopOfStack - sizeof (EFI_SEC_PEI_HAND_OFF) - CPU_STACK_ALIGNMENT);
|
|
TopOfStack = ALIGN_POINTER (TopOfStack, CPU_STACK_ALIGNMENT);
|
|
|
|
//
|
|
// Bind this information into the SEC hand-off state
|
|
//
|
|
SecCoreData = (EFI_SEC_PEI_HAND_OFF *)(UINTN)TopOfStack;
|
|
SecCoreData->DataSize = sizeof (EFI_SEC_PEI_HAND_OFF);
|
|
SecCoreData->BootFirmwareVolumeBase = BootFirmwareVolumeBase;
|
|
SecCoreData->BootFirmwareVolumeSize = BootFirmwareVolumeSize;
|
|
SecCoreData->TemporaryRamBase = (VOID *)TemporaryRam;
|
|
SecCoreData->TemporaryRamSize = TemporaryRamSize;
|
|
SecCoreData->StackBase = SecCoreData->TemporaryRamBase;
|
|
SecCoreData->StackSize = SecStackSize;
|
|
SecCoreData->PeiTemporaryRamBase = (VOID *)((UINTN)SecCoreData->TemporaryRamBase + SecStackSize);
|
|
SecCoreData->PeiTemporaryRamSize = TemporaryRamSize - SecStackSize;
|
|
|
|
//
|
|
// Load the PEI Core from a Firmware Volume
|
|
//
|
|
Status = SecPeCoffGetEntryPoint (
|
|
SecCorePe32File,
|
|
&SecCoreEntryPoint
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Transfer control to the SEC Core
|
|
//
|
|
SwitchStack (
|
|
(SWITCH_STACK_ENTRY_POINT)(UINTN)SecCoreEntryPoint,
|
|
SecCoreData,
|
|
GetThunkPpiList (),
|
|
TopOfStack
|
|
);
|
|
//
|
|
// If we get here, then the SEC Core returned. This is an error
|
|
//
|
|
return;
|
|
}
|
|
|
|
RETURN_STATUS
|
|
EFIAPI
|
|
SecPeCoffGetEntryPoint (
|
|
IN VOID *Pe32Data,
|
|
IN OUT VOID **EntryPoint
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
PE_COFF_LOADER_IMAGE_CONTEXT ImageContext;
|
|
|
|
ZeroMem (&ImageContext, sizeof (ImageContext));
|
|
ImageContext.Handle = Pe32Data;
|
|
|
|
ImageContext.ImageRead = (PE_COFF_LOADER_READ_FILE)SecImageRead;
|
|
|
|
Status = PeCoffLoaderGetImageInfo (&ImageContext);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// XIP for SEC and PEI_CORE
|
|
//
|
|
ImageContext.ImageAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)Pe32Data;
|
|
|
|
Status = PeCoffLoaderLoadImage (&ImageContext);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
Status = PeCoffLoaderRelocateImage (&ImageContext);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
*EntryPoint = (VOID *)(UINTN)ImageContext.EntryPoint;
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
SecImageRead (
|
|
IN VOID *FileHandle,
|
|
IN UINTN FileOffset,
|
|
IN OUT UINTN *ReadSize,
|
|
OUT VOID *Buffer
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
Support routine for the PE/COFF Loader that reads a buffer from a PE/COFF file
|
|
|
|
Arguments:
|
|
FileHandle - The handle to the PE/COFF file
|
|
FileOffset - The offset, in bytes, into the file to read
|
|
ReadSize - The number of bytes to read from the file starting at FileOffset
|
|
Buffer - A pointer to the buffer to read the data into.
|
|
|
|
Returns:
|
|
EFI_SUCCESS - ReadSize bytes of data were read into Buffer from the PE/COFF file starting at FileOffset
|
|
|
|
--*/
|
|
{
|
|
CHAR8 *Destination8;
|
|
CHAR8 *Source8;
|
|
UINTN Length;
|
|
|
|
Destination8 = Buffer;
|
|
Source8 = (CHAR8 *)((UINTN)FileHandle + FileOffset);
|
|
Length = *ReadSize;
|
|
while (Length--) {
|
|
*(Destination8++) = *(Source8++);
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
CHAR16 *
|
|
AsciiToUnicode (
|
|
IN CHAR8 *Ascii,
|
|
IN UINTN *StrLen OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
Convert the passed in Ascii string to Unicode.
|
|
Optionally return the length of the strings.
|
|
|
|
Arguments:
|
|
Ascii - Ascii string to convert
|
|
StrLen - Length of string
|
|
|
|
Returns:
|
|
Pointer to malloc'ed Unicode version of Ascii
|
|
|
|
--*/
|
|
{
|
|
UINTN Index;
|
|
CHAR16 *Unicode;
|
|
|
|
//
|
|
// Allocate a buffer for unicode string
|
|
//
|
|
for (Index = 0; Ascii[Index] != '\0'; Index++) {
|
|
}
|
|
|
|
Unicode = malloc ((Index + 1) * sizeof (CHAR16));
|
|
if (Unicode == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
for (Index = 0; Ascii[Index] != '\0'; Index++) {
|
|
Unicode[Index] = (CHAR16)Ascii[Index];
|
|
}
|
|
|
|
Unicode[Index] = '\0';
|
|
|
|
if (StrLen != NULL) {
|
|
*StrLen = Index;
|
|
}
|
|
|
|
return Unicode;
|
|
}
|
|
|
|
UINTN
|
|
CountSeparatorsInString (
|
|
IN CONST CHAR16 *String,
|
|
IN CHAR16 Separator
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
Count the number of separators in String
|
|
|
|
Arguments:
|
|
String - String to process
|
|
Separator - Item to count
|
|
|
|
Returns:
|
|
Number of Separator in String
|
|
|
|
--*/
|
|
{
|
|
UINTN Count;
|
|
|
|
for (Count = 0; *String != '\0'; String++) {
|
|
if (*String == Separator) {
|
|
Count++;
|
|
}
|
|
}
|
|
|
|
return Count;
|
|
}
|
|
|
|
/**
|
|
Store the ModHandle in an array indexed by the Pdb File name.
|
|
The ModHandle is needed to unload the image.
|
|
@param ImageContext - Input data returned from PE Laoder Library. Used to find the
|
|
.PDB file name of the PE Image.
|
|
@param ModHandle - Returned from LoadLibraryEx() and stored for call to
|
|
FreeLibrary().
|
|
@return return EFI_SUCCESS when ModHandle was stored.
|
|
--*/
|
|
EFI_STATUS
|
|
AddModHandle (
|
|
IN PE_COFF_LOADER_IMAGE_CONTEXT *ImageContext,
|
|
IN VOID *ModHandle
|
|
)
|
|
|
|
{
|
|
UINTN Index;
|
|
PDB_NAME_TO_MOD_HANDLE *Array;
|
|
UINTN PreviousSize;
|
|
PDB_NAME_TO_MOD_HANDLE *TempArray;
|
|
HANDLE Handle;
|
|
UINTN Size;
|
|
|
|
//
|
|
// Return EFI_ALREADY_STARTED if this DLL has already been loaded
|
|
//
|
|
Array = mPdbNameModHandleArray;
|
|
for (Index = 0; Index < mPdbNameModHandleArraySize; Index++, Array++) {
|
|
if ((Array->PdbPointer != NULL) && (Array->ModHandle == ModHandle)) {
|
|
return EFI_ALREADY_STARTED;
|
|
}
|
|
}
|
|
|
|
Array = mPdbNameModHandleArray;
|
|
for (Index = 0; Index < mPdbNameModHandleArraySize; Index++, Array++) {
|
|
if (Array->PdbPointer == NULL) {
|
|
//
|
|
// Make a copy of the stirng and store the ModHandle
|
|
//
|
|
Handle = GetProcessHeap ();
|
|
Size = AsciiStrLen (ImageContext->PdbPointer) + 1;
|
|
Array->PdbPointer = HeapAlloc (Handle, HEAP_ZERO_MEMORY, Size);
|
|
ASSERT (Array->PdbPointer != NULL);
|
|
|
|
AsciiStrCpyS (Array->PdbPointer, Size, ImageContext->PdbPointer);
|
|
Array->ModHandle = ModHandle;
|
|
return EFI_SUCCESS;
|
|
}
|
|
}
|
|
|
|
//
|
|
// No free space in mPdbNameModHandleArray so grow it by
|
|
// MAX_PDB_NAME_TO_MOD_HANDLE_ARRAY_SIZE entires.
|
|
//
|
|
PreviousSize = mPdbNameModHandleArraySize * sizeof (PDB_NAME_TO_MOD_HANDLE);
|
|
mPdbNameModHandleArraySize += MAX_PDB_NAME_TO_MOD_HANDLE_ARRAY_SIZE;
|
|
//
|
|
// re-allocate a new buffer and copy the old values to the new locaiton.
|
|
//
|
|
TempArray = HeapAlloc (
|
|
GetProcessHeap (),
|
|
HEAP_ZERO_MEMORY,
|
|
mPdbNameModHandleArraySize * sizeof (PDB_NAME_TO_MOD_HANDLE)
|
|
);
|
|
|
|
CopyMem ((VOID *)(UINTN)TempArray, (VOID *)(UINTN)mPdbNameModHandleArray, PreviousSize);
|
|
|
|
HeapFree (GetProcessHeap (), 0, mPdbNameModHandleArray);
|
|
|
|
mPdbNameModHandleArray = TempArray;
|
|
if (mPdbNameModHandleArray == NULL) {
|
|
ASSERT (FALSE);
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
return AddModHandle (ImageContext, ModHandle);
|
|
}
|
|
|
|
/**
|
|
Return the ModHandle and delete the entry in the array.
|
|
@param ImageContext - Input data returned from PE Laoder Library. Used to find the
|
|
.PDB file name of the PE Image.
|
|
@return
|
|
ModHandle - ModHandle assoicated with ImageContext is returned
|
|
NULL - No ModHandle associated with ImageContext
|
|
**/
|
|
VOID *
|
|
RemoveModHandle (
|
|
IN PE_COFF_LOADER_IMAGE_CONTEXT *ImageContext
|
|
)
|
|
{
|
|
UINTN Index;
|
|
PDB_NAME_TO_MOD_HANDLE *Array;
|
|
|
|
if (ImageContext->PdbPointer == NULL) {
|
|
//
|
|
// If no PDB pointer there is no ModHandle so return NULL
|
|
//
|
|
return NULL;
|
|
}
|
|
|
|
Array = mPdbNameModHandleArray;
|
|
for (Index = 0; Index < mPdbNameModHandleArraySize; Index++, Array++) {
|
|
if ((Array->PdbPointer != NULL) && (AsciiStrCmp (Array->PdbPointer, ImageContext->PdbPointer) == 0)) {
|
|
//
|
|
// If you find a match return it and delete the entry
|
|
//
|
|
HeapFree (GetProcessHeap (), 0, Array->PdbPointer);
|
|
Array->PdbPointer = NULL;
|
|
return Array->ModHandle;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
VOID
|
|
EFIAPI
|
|
PeCoffLoaderRelocateImageExtraAction (
|
|
IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *ImageContext
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
VOID *DllEntryPoint;
|
|
CHAR16 *DllFileName;
|
|
HMODULE Library;
|
|
UINTN Index;
|
|
|
|
ASSERT (ImageContext != NULL);
|
|
//
|
|
// If we load our own PE COFF images the Windows debugger can not source
|
|
// level debug our code. If a valid PDB pointer exists use it to load
|
|
// the *.dll file as a library using Windows* APIs. This allows
|
|
// source level debug. The image is still loaded and relocated
|
|
// in the Framework memory space like on a real system (by the code above),
|
|
// but the entry point points into the DLL loaded by the code below.
|
|
//
|
|
|
|
DllEntryPoint = NULL;
|
|
|
|
//
|
|
// Load the DLL if it's not an EBC image.
|
|
//
|
|
if ((ImageContext->PdbPointer != NULL) &&
|
|
(ImageContext->Machine != EFI_IMAGE_MACHINE_EBC))
|
|
{
|
|
//
|
|
// Convert filename from ASCII to Unicode
|
|
//
|
|
DllFileName = AsciiToUnicode (ImageContext->PdbPointer, &Index);
|
|
|
|
//
|
|
// Check that we have a valid filename
|
|
//
|
|
if ((Index < 5) || (DllFileName[Index - 4] != '.')) {
|
|
free (DllFileName);
|
|
|
|
//
|
|
// Never return an error if PeCoffLoaderRelocateImage() succeeded.
|
|
// The image will run, but we just can't source level debug. If we
|
|
// return an error the image will not run.
|
|
//
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Replace .PDB with .DLL on the filename
|
|
//
|
|
DllFileName[Index - 3] = 'D';
|
|
DllFileName[Index - 2] = 'L';
|
|
DllFileName[Index - 1] = 'L';
|
|
|
|
//
|
|
// Load the .DLL file into the user process's address space for source
|
|
// level debug
|
|
//
|
|
Library = LoadLibraryEx (DllFileName, NULL, DONT_RESOLVE_DLL_REFERENCES);
|
|
if (Library != NULL) {
|
|
//
|
|
// InitializeDriver is the entry point we put in all our EFI DLL's. The
|
|
// DONT_RESOLVE_DLL_REFERENCES argument to LoadLIbraryEx() suppresses the
|
|
// normal DLL entry point of DllMain, and prevents other modules that are
|
|
// referenced in side the DllFileName from being loaded. There is no error
|
|
// checking as the we can point to the PE32 image loaded by Tiano. This
|
|
// step is only needed for source level debugging
|
|
//
|
|
DllEntryPoint = (VOID *)(UINTN)GetProcAddress (Library, "InitializeDriver");
|
|
}
|
|
|
|
if ((Library != NULL) && (DllEntryPoint != NULL)) {
|
|
Status = AddModHandle (ImageContext, Library);
|
|
if (Status == EFI_ALREADY_STARTED) {
|
|
//
|
|
// If the DLL has already been loaded before, then this instance of the DLL can not be debugged.
|
|
//
|
|
ImageContext->PdbPointer = NULL;
|
|
SecPrint ("WARNING: DLL already loaded. No source level debug %S.\n\r", DllFileName);
|
|
} else {
|
|
//
|
|
// This DLL is not already loaded, so source level debugging is supported.
|
|
//
|
|
ImageContext->EntryPoint = (EFI_PHYSICAL_ADDRESS)(UINTN)DllEntryPoint;
|
|
SecPrint ("LoadLibraryEx (\n\r %S,\n\r NULL, DONT_RESOLVE_DLL_REFERENCES)\n\r", DllFileName);
|
|
}
|
|
} else {
|
|
SecPrint ("WARNING: No source level debug %S. \n\r", DllFileName);
|
|
}
|
|
|
|
free (DllFileName);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
EFIAPI
|
|
PeCoffLoaderUnloadImageExtraAction (
|
|
IN PE_COFF_LOADER_IMAGE_CONTEXT *ImageContext
|
|
)
|
|
{
|
|
VOID *ModHandle;
|
|
|
|
ASSERT (ImageContext != NULL);
|
|
|
|
ModHandle = RemoveModHandle (ImageContext);
|
|
if (ModHandle != NULL) {
|
|
FreeLibrary (ModHandle);
|
|
SecPrint ("FreeLibrary (\n\r %s)\n\r", ImageContext->PdbPointer);
|
|
} else {
|
|
SecPrint ("WARNING: Unload image without source level debug\n\r");
|
|
}
|
|
}
|
|
|
|
VOID
|
|
_ModuleEntryPoint (
|
|
VOID
|
|
)
|
|
{
|
|
}
|