mirror of https://github.com/acidanthera/audk.git
602 lines
14 KiB
C
602 lines
14 KiB
C
/**@file
|
|
|
|
Copyright (c) 2006 - 2023, Intel Corporation. All rights reserved.<BR>
|
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
|
|
|
Module Name:
|
|
|
|
WinNtThunk.c
|
|
|
|
Abstract:
|
|
|
|
Since the SEC is the only windows program in our emulation we
|
|
must use a Tiano mechanism to export operating system services
|
|
to other modules. This is the role of the EMU_THUNK_PROTOCOL.
|
|
|
|
The gEmuThunkProtocol exists so that a change to EMU_THUNK_PROTOCOL
|
|
will cause an error in initializing the array if all the member functions
|
|
are not added. It looks like adding a element to end and not initializing
|
|
it may cause the table to be initalized with the members at the end being
|
|
set to zero. This is bad as jumping to zero will case EmulatorPkg to crash.
|
|
|
|
**/
|
|
|
|
#include "WinHost.h"
|
|
|
|
STATIC BOOLEAN mEmulatorStdInConfigured = FALSE;
|
|
STATIC DWORD mOldStdInMode;
|
|
#if defined (NTDDI_VERSION) && defined (NTDDI_WIN10_TH2) && (NTDDI_VERSION > NTDDI_WIN10_TH2)
|
|
STATIC DWORD mOldStdOutMode;
|
|
#endif
|
|
|
|
UINTN
|
|
SecWriteStdErr (
|
|
IN UINT8 *Buffer,
|
|
IN UINTN NumberOfBytes
|
|
)
|
|
{
|
|
BOOL Success;
|
|
DWORD CharCount;
|
|
|
|
CharCount = (DWORD)NumberOfBytes;
|
|
Success = WriteFile (
|
|
GetStdHandle (STD_ERROR_HANDLE),
|
|
Buffer,
|
|
CharCount,
|
|
&CharCount,
|
|
NULL
|
|
);
|
|
|
|
return Success ? CharCount : 0;
|
|
}
|
|
|
|
EFI_STATUS
|
|
SecConfigStdIn (
|
|
VOID
|
|
)
|
|
{
|
|
BOOL Success;
|
|
DWORD Mode;
|
|
|
|
Success = GetConsoleMode (GetStdHandle (STD_INPUT_HANDLE), &Mode);
|
|
if (Success) {
|
|
if (!mEmulatorStdInConfigured) {
|
|
//
|
|
// Save the original state of the console so it can be restored on exit
|
|
//
|
|
mOldStdInMode = Mode;
|
|
}
|
|
|
|
//
|
|
// Disable buffer (line input), echo, mouse, window
|
|
//
|
|
Mode &= ~(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_MOUSE_INPUT | ENABLE_WINDOW_INPUT);
|
|
|
|
#if defined (NTDDI_VERSION) && defined (NTDDI_WIN10_TH2) && (NTDDI_VERSION > NTDDI_WIN10_TH2)
|
|
//
|
|
// Enable virtual terminal input for Win10 above TH2
|
|
//
|
|
Mode |= ENABLE_VIRTUAL_TERMINAL_INPUT;
|
|
#endif
|
|
|
|
Success = SetConsoleMode (GetStdHandle (STD_INPUT_HANDLE), Mode);
|
|
}
|
|
|
|
#if defined (NTDDI_VERSION) && defined (NTDDI_WIN10_TH2) && (NTDDI_VERSION > NTDDI_WIN10_TH2)
|
|
//
|
|
// Enable terminal mode for Win10 above TH2
|
|
//
|
|
if (Success) {
|
|
Success = GetConsoleMode (GetStdHandle (STD_OUTPUT_HANDLE), &Mode);
|
|
if (!mEmulatorStdInConfigured) {
|
|
//
|
|
// Save the original state of the console so it can be restored on exit
|
|
//
|
|
mOldStdOutMode = Mode;
|
|
}
|
|
|
|
if (Success) {
|
|
Success = SetConsoleMode (
|
|
GetStdHandle (STD_OUTPUT_HANDLE),
|
|
Mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN
|
|
);
|
|
}
|
|
}
|
|
|
|
#endif
|
|
if (Success) {
|
|
mEmulatorStdInConfigured = TRUE;
|
|
}
|
|
|
|
return Success ? EFI_SUCCESS : EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
UINTN
|
|
SecWriteStdOut (
|
|
IN UINT8 *Buffer,
|
|
IN UINTN NumberOfBytes
|
|
)
|
|
{
|
|
BOOL Success;
|
|
DWORD CharCount;
|
|
|
|
CharCount = (DWORD)NumberOfBytes;
|
|
Success = WriteFile (
|
|
GetStdHandle (STD_OUTPUT_HANDLE),
|
|
Buffer,
|
|
CharCount,
|
|
&CharCount,
|
|
NULL
|
|
);
|
|
|
|
return Success ? CharCount : 0;
|
|
}
|
|
|
|
BOOLEAN
|
|
SecPollStdIn (
|
|
VOID
|
|
)
|
|
{
|
|
BOOL Success;
|
|
INPUT_RECORD Record;
|
|
DWORD RecordNum;
|
|
|
|
do {
|
|
Success = GetNumberOfConsoleInputEvents (GetStdHandle (STD_INPUT_HANDLE), &RecordNum);
|
|
if (!Success || (RecordNum == 0)) {
|
|
break;
|
|
}
|
|
|
|
Success = PeekConsoleInput (
|
|
GetStdHandle (STD_INPUT_HANDLE),
|
|
&Record,
|
|
1,
|
|
&RecordNum
|
|
);
|
|
if (Success && (RecordNum == 1)) {
|
|
if ((Record.EventType == KEY_EVENT) && Record.Event.KeyEvent.bKeyDown) {
|
|
return TRUE;
|
|
} else {
|
|
//
|
|
// Consume the non-key event.
|
|
//
|
|
Success = ReadConsoleInput (
|
|
GetStdHandle (STD_INPUT_HANDLE),
|
|
&Record,
|
|
1,
|
|
&RecordNum
|
|
);
|
|
}
|
|
}
|
|
} while (Success);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
UINTN
|
|
SecReadStdIn (
|
|
IN UINT8 *Buffer,
|
|
IN UINTN NumberOfBytes
|
|
)
|
|
{
|
|
BOOL Success;
|
|
INPUT_RECORD Record;
|
|
DWORD RecordNum;
|
|
UINTN BytesReturn;
|
|
|
|
if (!SecPollStdIn ()) {
|
|
return 0;
|
|
}
|
|
|
|
Success = ReadConsoleInput (
|
|
GetStdHandle (STD_INPUT_HANDLE),
|
|
&Record,
|
|
1,
|
|
&RecordNum
|
|
);
|
|
ASSERT (Success && (RecordNum == 1) && (Record.EventType == KEY_EVENT) && (Record.Event.KeyEvent.bKeyDown));
|
|
NumberOfBytes = MIN (Record.Event.KeyEvent.wRepeatCount, NumberOfBytes);
|
|
BytesReturn = NumberOfBytes;
|
|
while (NumberOfBytes-- != 0) {
|
|
Buffer[NumberOfBytes] = Record.Event.KeyEvent.uChar.AsciiChar;
|
|
}
|
|
|
|
return BytesReturn;
|
|
}
|
|
|
|
VOID *
|
|
SecAlloc (
|
|
IN UINTN Size
|
|
)
|
|
{
|
|
return malloc ((size_t)Size);
|
|
}
|
|
|
|
BOOLEAN
|
|
SecFree (
|
|
IN VOID *Ptr
|
|
)
|
|
{
|
|
if (EfiSystemMemoryRange (Ptr)) {
|
|
// If an address range is in the EFI memory map it was alloced via EFI.
|
|
// So don't free those ranges and let the caller know.
|
|
return FALSE;
|
|
}
|
|
|
|
free (Ptr);
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Define a global that we can use to shut down the NT timer thread when
|
|
// the timer is canceled.
|
|
//
|
|
BOOLEAN mCancelTimerThread = FALSE;
|
|
|
|
//
|
|
// The notification function to call on every timer interrupt
|
|
//
|
|
EMU_SET_TIMER_CALLBACK *mTimerNotifyFunction = NULL;
|
|
|
|
//
|
|
// The thread handle for this driver
|
|
//
|
|
HANDLE mNtMainThreadHandle;
|
|
|
|
//
|
|
// The timer value from the last timer interrupt
|
|
//
|
|
UINT32 mNtLastTick;
|
|
|
|
//
|
|
// Critical section used to update varibles shared between the main thread and
|
|
// the timer interrupt thread.
|
|
//
|
|
CRITICAL_SECTION mNtCriticalSection;
|
|
|
|
//
|
|
// Worker Functions
|
|
//
|
|
UINT mMMTimerThreadID = 0;
|
|
|
|
volatile BOOLEAN mInterruptEnabled = FALSE;
|
|
|
|
VOID
|
|
CALLBACK
|
|
MMTimerThread (
|
|
UINT wTimerID,
|
|
UINT msg,
|
|
DWORD_PTR dwUser,
|
|
DWORD_PTR dw1,
|
|
DWORD_PTR dw2
|
|
)
|
|
{
|
|
UINT32 CurrentTick;
|
|
UINT32 Delta;
|
|
|
|
if (!mCancelTimerThread) {
|
|
//
|
|
// Suspend the main thread until we are done.
|
|
// Enter the critical section before suspending
|
|
// and leave the critical section after resuming
|
|
// to avoid deadlock between main and timer thread.
|
|
//
|
|
EnterCriticalSection (&mNtCriticalSection);
|
|
SuspendThread (mNtMainThreadHandle);
|
|
|
|
//
|
|
// If the timer thread is being canceled, then bail immediately.
|
|
// We check again here because there's a small window of time from when
|
|
// this thread was kicked off and when we suspended the main thread above.
|
|
//
|
|
if (mCancelTimerThread) {
|
|
ResumeThread (mNtMainThreadHandle);
|
|
LeaveCriticalSection (&mNtCriticalSection);
|
|
timeKillEvent (wTimerID);
|
|
mMMTimerThreadID = 0;
|
|
return;
|
|
}
|
|
|
|
while (!mInterruptEnabled) {
|
|
//
|
|
// Resume the main thread
|
|
//
|
|
ResumeThread (mNtMainThreadHandle);
|
|
LeaveCriticalSection (&mNtCriticalSection);
|
|
|
|
//
|
|
// Wait for interrupts to be enabled.
|
|
//
|
|
while (!mInterruptEnabled) {
|
|
Sleep (1);
|
|
}
|
|
|
|
//
|
|
// Suspend the main thread until we are done
|
|
//
|
|
EnterCriticalSection (&mNtCriticalSection);
|
|
SuspendThread (mNtMainThreadHandle);
|
|
}
|
|
|
|
//
|
|
// Get the current system tick
|
|
//
|
|
CurrentTick = GetTickCount ();
|
|
Delta = CurrentTick - mNtLastTick;
|
|
mNtLastTick = CurrentTick;
|
|
|
|
//
|
|
// If delay was more then 1 second, ignore it (probably debugging case)
|
|
//
|
|
if (Delta < 1000) {
|
|
//
|
|
// Only invoke the callback function if a Non-NULL handler has been
|
|
// registered. Assume all other handlers are legal.
|
|
//
|
|
if (mTimerNotifyFunction != NULL) {
|
|
mTimerNotifyFunction (Delta);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Resume the main thread
|
|
//
|
|
ResumeThread (mNtMainThreadHandle);
|
|
LeaveCriticalSection (&mNtCriticalSection);
|
|
} else {
|
|
timeKillEvent (wTimerID);
|
|
mMMTimerThreadID = 0;
|
|
}
|
|
}
|
|
|
|
VOID
|
|
SecSetTimer (
|
|
IN UINT64 TimerPeriod,
|
|
IN EMU_SET_TIMER_CALLBACK Callback
|
|
)
|
|
{
|
|
//
|
|
// If TimerPeriod is 0, then the timer thread should be canceled
|
|
//
|
|
if (TimerPeriod == 0) {
|
|
//
|
|
// Cancel the timer thread
|
|
//
|
|
EnterCriticalSection (&mNtCriticalSection);
|
|
|
|
mCancelTimerThread = TRUE;
|
|
|
|
LeaveCriticalSection (&mNtCriticalSection);
|
|
|
|
//
|
|
// Wait for the timer thread to exit
|
|
//
|
|
|
|
if (mMMTimerThreadID != 0) {
|
|
timeKillEvent (mMMTimerThreadID);
|
|
mMMTimerThreadID = 0;
|
|
}
|
|
} else {
|
|
//
|
|
// If the TimerPeriod is valid, then create and/or adjust the period of the timer thread
|
|
//
|
|
EnterCriticalSection (&mNtCriticalSection);
|
|
|
|
mCancelTimerThread = FALSE;
|
|
|
|
LeaveCriticalSection (&mNtCriticalSection);
|
|
|
|
//
|
|
// Get the starting tick location if we are just starting the timer thread
|
|
//
|
|
mNtLastTick = GetTickCount ();
|
|
|
|
if (mMMTimerThreadID) {
|
|
timeKillEvent (mMMTimerThreadID);
|
|
}
|
|
|
|
SetThreadPriority (
|
|
GetCurrentThread (),
|
|
THREAD_PRIORITY_HIGHEST
|
|
);
|
|
|
|
mMMTimerThreadID = timeSetEvent (
|
|
(UINT)TimerPeriod,
|
|
0,
|
|
MMTimerThread,
|
|
(DWORD_PTR)NULL,
|
|
TIME_PERIODIC | TIME_KILL_SYNCHRONOUS | TIME_CALLBACK_FUNCTION
|
|
);
|
|
}
|
|
|
|
mTimerNotifyFunction = Callback;
|
|
}
|
|
|
|
VOID
|
|
SecInitializeThunk (
|
|
VOID
|
|
)
|
|
{
|
|
InitializeCriticalSection (&mNtCriticalSection);
|
|
|
|
DuplicateHandle (
|
|
GetCurrentProcess (),
|
|
GetCurrentThread (),
|
|
GetCurrentProcess (),
|
|
&mNtMainThreadHandle,
|
|
0,
|
|
FALSE,
|
|
DUPLICATE_SAME_ACCESS
|
|
);
|
|
}
|
|
|
|
VOID
|
|
SecEnableInterrupt (
|
|
VOID
|
|
)
|
|
{
|
|
mInterruptEnabled = TRUE;
|
|
}
|
|
|
|
VOID
|
|
SecDisableInterrupt (
|
|
VOID
|
|
)
|
|
{
|
|
mInterruptEnabled = FALSE;
|
|
}
|
|
|
|
UINT64
|
|
SecQueryPerformanceFrequency (
|
|
VOID
|
|
)
|
|
{
|
|
// Hard code to nanoseconds
|
|
return 1000000000ULL;
|
|
}
|
|
|
|
UINT64
|
|
SecQueryPerformanceCounter (
|
|
VOID
|
|
)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
VOID
|
|
SecSleep (
|
|
IN UINT64 Nanoseconds
|
|
)
|
|
{
|
|
Sleep ((DWORD)DivU64x32 (Nanoseconds, 1000000));
|
|
}
|
|
|
|
VOID
|
|
SecCpuSleep (
|
|
VOID
|
|
)
|
|
{
|
|
Sleep (1);
|
|
}
|
|
|
|
VOID
|
|
SecExit (
|
|
UINTN Status
|
|
)
|
|
{
|
|
if (mEmulatorStdInConfigured) {
|
|
//
|
|
// Reset the console back to its original state
|
|
//
|
|
#if defined (NTDDI_VERSION) && defined (NTDDI_WIN10_TH2) && (NTDDI_VERSION > NTDDI_WIN10_TH2)
|
|
BOOL Success = SetConsoleMode (GetStdHandle (STD_INPUT_HANDLE), mOldStdInMode);
|
|
if (Success) {
|
|
SetConsoleMode (GetStdHandle (STD_OUTPUT_HANDLE), mOldStdOutMode);
|
|
}
|
|
|
|
#else
|
|
SetConsoleMode (GetStdHandle (STD_INPUT_HANDLE), mOldStdInMode);
|
|
#endif
|
|
}
|
|
|
|
exit ((int)Status);
|
|
}
|
|
|
|
VOID
|
|
SecGetTime (
|
|
OUT EFI_TIME *Time,
|
|
OUT EFI_TIME_CAPABILITIES *Capabilities OPTIONAL
|
|
)
|
|
{
|
|
SYSTEMTIME SystemTime;
|
|
TIME_ZONE_INFORMATION TimeZone;
|
|
|
|
GetLocalTime (&SystemTime);
|
|
GetTimeZoneInformation (&TimeZone);
|
|
|
|
Time->Year = (UINT16)SystemTime.wYear;
|
|
Time->Month = (UINT8)SystemTime.wMonth;
|
|
Time->Day = (UINT8)SystemTime.wDay;
|
|
Time->Hour = (UINT8)SystemTime.wHour;
|
|
Time->Minute = (UINT8)SystemTime.wMinute;
|
|
Time->Second = (UINT8)SystemTime.wSecond;
|
|
Time->Nanosecond = (UINT32)(SystemTime.wMilliseconds * 1000000);
|
|
Time->TimeZone = (INT16)TimeZone.Bias;
|
|
|
|
if (Capabilities != NULL) {
|
|
Capabilities->Resolution = 1;
|
|
Capabilities->Accuracy = 50000000;
|
|
Capabilities->SetsToZero = FALSE;
|
|
}
|
|
|
|
Time->Daylight = 0;
|
|
if (TimeZone.StandardDate.wMonth) {
|
|
Time->Daylight = (UINT8)TimeZone.StandardDate.wMonth;
|
|
}
|
|
}
|
|
|
|
EFI_STATUS
|
|
SecSetTime (
|
|
IN EFI_TIME *Time
|
|
)
|
|
{
|
|
TIME_ZONE_INFORMATION TimeZone;
|
|
SYSTEMTIME SystemTime;
|
|
BOOL Flag;
|
|
|
|
//
|
|
// Set Daylight savings time information and Time Zone
|
|
//
|
|
GetTimeZoneInformation (&TimeZone);
|
|
TimeZone.StandardDate.wMonth = Time->Daylight;
|
|
TimeZone.Bias = Time->TimeZone;
|
|
Flag = SetTimeZoneInformation (&TimeZone);
|
|
if (!Flag) {
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
SystemTime.wYear = Time->Year;
|
|
SystemTime.wMonth = Time->Month;
|
|
SystemTime.wDay = Time->Day;
|
|
SystemTime.wHour = Time->Hour;
|
|
SystemTime.wMinute = Time->Minute;
|
|
SystemTime.wSecond = Time->Second;
|
|
SystemTime.wMilliseconds = (INT16)(Time->Nanosecond / 1000000);
|
|
|
|
Flag = SetLocalTime (&SystemTime);
|
|
|
|
if (!Flag) {
|
|
return EFI_DEVICE_ERROR;
|
|
} else {
|
|
return EFI_SUCCESS;
|
|
}
|
|
}
|
|
|
|
EMU_THUNK_PROTOCOL gEmuThunkProtocol = {
|
|
SecWriteStdErr,
|
|
SecConfigStdIn,
|
|
SecWriteStdOut,
|
|
SecReadStdIn,
|
|
SecPollStdIn,
|
|
SecAlloc,
|
|
NULL,
|
|
SecFree,
|
|
SecPeCoffGetEntryPoint,
|
|
PeCoffLoaderRelocateImageExtraAction,
|
|
PeCoffLoaderUnloadImageExtraAction,
|
|
SecEnableInterrupt,
|
|
SecDisableInterrupt,
|
|
SecQueryPerformanceFrequency,
|
|
SecQueryPerformanceCounter,
|
|
SecSleep,
|
|
SecCpuSleep,
|
|
SecExit,
|
|
SecGetTime,
|
|
SecSetTime,
|
|
SecSetTimer,
|
|
GetNextThunkProtocol
|
|
};
|
|
|
|
#pragma warning(default : 4996)
|
|
#pragma warning(default : 4232)
|