Merge pull request #2315 from ergo720/thread_ob_handle
Updated Ps kernel functions to use Ob handles + unpatch thread XAPI functions + added APC support to kernel via Ob
This commit is contained in:
commit
f857593f77
|
@ -25,6 +25,8 @@
|
|||
// *
|
||||
// ******************************************************************
|
||||
|
||||
#include <core\kernel\exports\xboxkrnl.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
@ -34,6 +36,7 @@
|
|||
#include "Timer.h"
|
||||
#include "common\util\CxbxUtil.h"
|
||||
#include "core\kernel\init\CxbxKrnl.h"
|
||||
#include "core\kernel\support\EmuFS.h"
|
||||
#ifdef __linux__
|
||||
#include <time.h>
|
||||
#endif
|
||||
|
@ -121,22 +124,51 @@ void Timer_Destroy(TimerObject* Timer)
|
|||
TimerList.erase(TimerList.begin() + index);
|
||||
}
|
||||
|
||||
// Thread that runs the timer
|
||||
void ClockThread(TimerObject* Timer)
|
||||
void Timer_Shutdown()
|
||||
{
|
||||
uint64_t NewExpireTime;
|
||||
unsigned i, iXboxThreads = 0;
|
||||
TimerMtx.lock();
|
||||
|
||||
for (i = 0; i < TimerList.size(); i++) {
|
||||
TimerObject* Timer = TimerList[i];
|
||||
// We only need to terminate host threads.
|
||||
if (!Timer->IsXboxTimer) {
|
||||
Timer_Exit(Timer);
|
||||
}
|
||||
// If the thread is xbox, we need to increment for while statement check
|
||||
else {
|
||||
iXboxThreads++;
|
||||
}
|
||||
}
|
||||
|
||||
// Only perform wait for host threads, otherwise xbox threads are
|
||||
// already handled within xbox kernel for shutdown process. See CxbxrKrnlSuspendThreads function.
|
||||
int counter = 0;
|
||||
while (iXboxThreads != TimerList.size()) {
|
||||
if (counter >= 8) {
|
||||
break;
|
||||
}
|
||||
TimerMtx.unlock();
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
||||
TimerMtx.lock();
|
||||
counter++;
|
||||
}
|
||||
TimerList.clear();
|
||||
TimerMtx.unlock();
|
||||
}
|
||||
|
||||
// Thread that runs the timer
|
||||
void NTAPI ClockThread(void *TimerArg)
|
||||
{
|
||||
TimerObject *Timer = static_cast<TimerObject *>(TimerArg);
|
||||
if (!Timer->Name.empty()) {
|
||||
CxbxSetThreadName(Timer->Name.c_str());
|
||||
}
|
||||
if (Timer->IsXboxTimer) {
|
||||
InitXboxThread();
|
||||
g_AffinityPolicy->SetAffinityXbox();
|
||||
} else {
|
||||
if (!Timer->IsXboxTimer) {
|
||||
g_AffinityPolicy->SetAffinityOther();
|
||||
}
|
||||
|
||||
NewExpireTime = GetNextExpireTime(Timer);
|
||||
uint64_t NewExpireTime = GetNextExpireTime(Timer);
|
||||
|
||||
while (true) {
|
||||
if (GetTime_NS(Timer) > NewExpireTime) {
|
||||
|
@ -185,7 +217,13 @@ TimerObject* Timer_Create(TimerCB Callback, void* Arg, std::string Name, bool Is
|
|||
void Timer_Start(TimerObject* Timer, uint64_t Expire_MS)
|
||||
{
|
||||
Timer->ExpireTime_MS.store(Expire_MS);
|
||||
std::thread(ClockThread, Timer).detach();
|
||||
if (Timer->IsXboxTimer) {
|
||||
xbox::HANDLE hThread;
|
||||
xbox::PsCreateSystemThread(&hThread, xbox::zeroptr, ClockThread, Timer, FALSE);
|
||||
}
|
||||
else {
|
||||
std::thread(ClockThread, Timer).detach();
|
||||
}
|
||||
}
|
||||
|
||||
// Retrives the frequency of the high resolution clock of the host
|
||||
|
|
|
@ -63,6 +63,7 @@ void Timer_Exit(TimerObject* Timer);
|
|||
void Timer_ChangeExpireTime(TimerObject* Timer, uint64_t Expire_ms);
|
||||
uint64_t GetTime_NS(TimerObject* Timer);
|
||||
void Timer_Init();
|
||||
void Timer_Shutdown();
|
||||
|
||||
int64_t Timer_GetScaledPerformanceCounter(int64_t Period);
|
||||
|
||||
|
|
|
@ -36,6 +36,8 @@
|
|||
#include "CxbxVersion.h"
|
||||
#include "core\kernel\init\CxbxKrnl.h"
|
||||
#include "core\kernel\support\Emu.h"
|
||||
#include "core\kernel\support\EmuFS.h"
|
||||
#include "core\kernel\support\NativeHandle.h"
|
||||
#include "EmuShared.h"
|
||||
#include "..\FixedFunctionState.h"
|
||||
#include "core\hle\D3D8\ResourceTracker.h"
|
||||
|
@ -220,7 +222,7 @@ static xbox::dword_xt *g_Xbox_D3DDevice; // TODO: This should b
|
|||
// Static Function(s)
|
||||
static DWORD WINAPI EmuRenderWindow(LPVOID);
|
||||
static LRESULT WINAPI EmuMsgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
||||
static DWORD WINAPI EmuUpdateTickCount(LPVOID);
|
||||
static xbox::void_xt NTAPI EmuUpdateTickCount(xbox::PVOID Arg);
|
||||
static inline void EmuVerifyResourceIsRegistered(xbox::X_D3DResource *pResource, DWORD D3DUsage, int iTextureStage, DWORD dwSize);
|
||||
static void UpdateCurrentMSpFAndFPS(); // Used for benchmarking/fps count
|
||||
static void CxbxImpl_SetRenderTarget(xbox::X_D3DSurface *pRenderTarget, xbox::X_D3DSurface *pNewZStencil);
|
||||
|
@ -626,15 +628,15 @@ void CxbxInitWindow(bool bFullInit)
|
|||
CxbxKrnl_hEmuParent = NULL;
|
||||
|
||||
// create timing thread
|
||||
if (bFullInit)
|
||||
if (bFullInit && !bLLE_GPU)
|
||||
{
|
||||
HANDLE hThread = CreateThread(nullptr, 0, EmuUpdateTickCount, nullptr, 0, nullptr);
|
||||
xbox::HANDLE hThread;
|
||||
xbox::PsCreateSystemThread(&hThread, xbox::zeroptr, EmuUpdateTickCount, xbox::zeroptr, FALSE);
|
||||
// We set the priority of this thread a bit higher, to assure reliable timing :
|
||||
SetThreadPriority(hThread, THREAD_PRIORITY_ABOVE_NORMAL);
|
||||
g_AffinityPolicy->SetAffinityOther(hThread);
|
||||
|
||||
CxbxKrnlRegisterThread(hThread);
|
||||
CloseHandle(hThread); // CxbxKrnlRegisterThread duplicates the handle so we can close this one
|
||||
auto nativeHandle = GetNativeHandle(hThread);
|
||||
assert(nativeHandle);
|
||||
SetThreadPriority(*nativeHandle, THREAD_PRIORITY_ABOVE_NORMAL);
|
||||
g_AffinityPolicy->SetAffinityOther(*nativeHandle);
|
||||
}
|
||||
|
||||
/* TODO : Port this Dxbx code :
|
||||
|
@ -757,7 +759,7 @@ static void CxbxUpdateCursor(bool forceShow = false) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (g_renderbase->IsImGuiFocus() || forceShow) {
|
||||
if (g_renderbase && g_renderbase->IsImGuiFocus() || forceShow) {
|
||||
if (cursorInfo.flags == 0) {
|
||||
ShowCursor(TRUE);
|
||||
}
|
||||
|
@ -1869,8 +1871,6 @@ extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg
|
|||
// rendering window message procedure
|
||||
static LRESULT WINAPI EmuMsgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
static bool bAutoPaused = false;
|
||||
|
||||
const LRESULT imguiResult = ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam);
|
||||
if (imguiResult != 0) return imguiResult;
|
||||
|
||||
|
@ -2037,27 +2037,10 @@ static LRESULT WINAPI EmuMsgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lPar
|
|||
{
|
||||
switch(wParam)
|
||||
{
|
||||
case SIZE_RESTORED:
|
||||
case SIZE_MAXIMIZED:
|
||||
{
|
||||
if(bAutoPaused)
|
||||
{
|
||||
bAutoPaused = false;
|
||||
CxbxKrnlResume();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SIZE_MINIMIZED:
|
||||
{
|
||||
if(g_XBVideo.bFullScreen)
|
||||
CxbxrKrnlAbort(nullptr);
|
||||
|
||||
if(!g_bEmuSuspended)
|
||||
{
|
||||
bAutoPaused = true;
|
||||
CxbxKrnlSuspend();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -2162,20 +2145,12 @@ std::chrono::steady_clock::time_point GetNextVBlankTime()
|
|||
}
|
||||
|
||||
// timing thread procedure
|
||||
static DWORD WINAPI EmuUpdateTickCount(LPVOID)
|
||||
static xbox::void_xt NTAPI EmuUpdateTickCount(xbox::PVOID Arg)
|
||||
{
|
||||
CxbxSetThreadName("Cxbx Timing Thread");
|
||||
|
||||
// since callbacks come from here
|
||||
InitXboxThread();
|
||||
|
||||
EmuLog(LOG_LEVEL::DEBUG, "Timing thread is running.");
|
||||
|
||||
// We check for LLE flag as NV2A handles it's own VBLANK if LLE is enabled!
|
||||
if (bLLE_GPU) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto nextVBlankTime = GetNextVBlankTime();
|
||||
|
||||
while(true)
|
||||
|
|
|
@ -354,19 +354,19 @@ std::map<const std::string, const xbox_patch_t> g_PatchTable = {
|
|||
PATCH_ENTRY("ConvertThreadToFiber", xbox::EMUPATCH(ConvertThreadToFiber), PATCH_IS_FIBER),
|
||||
PATCH_ENTRY("CreateFiber", xbox::EMUPATCH(CreateFiber), PATCH_IS_FIBER),
|
||||
PATCH_ENTRY("DeleteFiber", xbox::EMUPATCH(DeleteFiber), PATCH_IS_FIBER),
|
||||
PATCH_ENTRY("GetExitCodeThread", xbox::EMUPATCH(GetExitCodeThread), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("GetThreadPriority", xbox::EMUPATCH(GetThreadPriority), PATCH_ALWAYS),
|
||||
//PATCH_ENTRY("GetExitCodeThread", xbox::EMUPATCH(GetExitCodeThread), PATCH_ALWAYS),
|
||||
//PATCH_ENTRY("GetThreadPriority", xbox::EMUPATCH(GetThreadPriority), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("OutputDebugStringA", xbox::EMUPATCH(OutputDebugStringA), PATCH_ALWAYS),
|
||||
//PATCH_ENTRY("RaiseException", xbox::EMUPATCH(RaiseException), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("SetThreadPriority", xbox::EMUPATCH(SetThreadPriority), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("SetThreadPriorityBoost", xbox::EMUPATCH(SetThreadPriorityBoost), PATCH_ALWAYS),
|
||||
//PATCH_ENTRY("SetThreadPriority", xbox::EMUPATCH(SetThreadPriority), PATCH_ALWAYS),
|
||||
//PATCH_ENTRY("SetThreadPriorityBoost", xbox::EMUPATCH(SetThreadPriorityBoost), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("SignalObjectAndWait", xbox::EMUPATCH(SignalObjectAndWait), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("SwitchToFiber", xbox::EMUPATCH(SwitchToFiber), PATCH_IS_FIBER),
|
||||
PATCH_ENTRY("XMountMUA", xbox::EMUPATCH(XMountMUA), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("XMountMURootA", xbox::EMUPATCH(XMountMURootA), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("XSetProcessQuantumLength", xbox::EMUPATCH(XSetProcessQuantumLength), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("timeKillEvent", xbox::EMUPATCH(timeKillEvent), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("timeSetEvent", xbox::EMUPATCH(timeSetEvent), PATCH_ALWAYS),
|
||||
//PATCH_ENTRY("XSetProcessQuantumLength", xbox::EMUPATCH(XSetProcessQuantumLength), PATCH_ALWAYS),
|
||||
//PATCH_ENTRY("timeKillEvent", xbox::EMUPATCH(timeKillEvent), PATCH_ALWAYS),
|
||||
//PATCH_ENTRY("timeSetEvent", xbox::EMUPATCH(timeSetEvent), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("XReadMUMetaData", xbox::EMUPATCH(XReadMUMetaData), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("XUnmountMU", xbox::EMUPATCH(XUnmountMU), PATCH_ALWAYS),
|
||||
};
|
||||
|
|
|
@ -38,7 +38,9 @@
|
|||
#include "Logging.h"
|
||||
#include "core\kernel\support\Emu.h"
|
||||
#include "core\kernel\exports\EmuKrnl.h" // For DefaultLaunchDataPage
|
||||
#include "core\kernel\exports\EmuKrnlKi.h"
|
||||
#include "core\kernel\support\EmuFile.h"
|
||||
#include "core\kernel\support\NativeHandle.h"
|
||||
#include "EmuShared.h"
|
||||
#include "core\hle\Intercept.hpp"
|
||||
#include "Windef.h"
|
||||
|
@ -895,177 +897,6 @@ xbox::dword_xt WINAPI xbox::EMUPATCH(XInputSetLightgunCalibration)
|
|||
RETURN(ret);
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
// * patch: SetThreadPriorityBoost
|
||||
// ******************************************************************
|
||||
xbox::bool_xt WINAPI xbox::EMUPATCH(SetThreadPriorityBoost)
|
||||
(
|
||||
HANDLE hThread,
|
||||
bool_xt DisablePriorityBoost
|
||||
)
|
||||
{
|
||||
|
||||
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(hThread)
|
||||
LOG_FUNC_ARG(DisablePriorityBoost)
|
||||
LOG_FUNC_END;
|
||||
|
||||
BOOL bRet = SetThreadPriorityBoost(hThread, DisablePriorityBoost);
|
||||
|
||||
if(bRet == FALSE)
|
||||
EmuLog(LOG_LEVEL::WARNING, "SetThreadPriorityBoost Failed!");
|
||||
|
||||
RETURN(bRet);
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
// * patch: SetThreadPriority
|
||||
// ******************************************************************
|
||||
xbox::bool_xt WINAPI xbox::EMUPATCH(SetThreadPriority)
|
||||
(
|
||||
HANDLE hThread,
|
||||
int nPriority
|
||||
)
|
||||
{
|
||||
|
||||
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(hThread)
|
||||
LOG_FUNC_ARG(nPriority)
|
||||
LOG_FUNC_END;
|
||||
|
||||
BOOL bRet = SetThreadPriority(hThread, nPriority);
|
||||
|
||||
if(bRet == FALSE)
|
||||
EmuLog(LOG_LEVEL::WARNING, "SetThreadPriority Failed!");
|
||||
|
||||
RETURN(bRet);
|
||||
}
|
||||
|
||||
|
||||
// ******************************************************************
|
||||
// * patch: GetThreadPriority
|
||||
// ******************************************************************
|
||||
int WINAPI xbox::EMUPATCH(GetThreadPriority)
|
||||
(
|
||||
HANDLE hThread
|
||||
)
|
||||
{
|
||||
|
||||
|
||||
LOG_FUNC_ONE_ARG(hThread);
|
||||
|
||||
int iRet = GetThreadPriority(hThread);
|
||||
|
||||
if(iRet == THREAD_PRIORITY_ERROR_RETURN)
|
||||
EmuLog(LOG_LEVEL::WARNING, "GetThreadPriority Failed!");
|
||||
|
||||
RETURN(iRet);
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
// * patch: GetExitCodeThread
|
||||
// ******************************************************************
|
||||
xbox::bool_xt WINAPI xbox::EMUPATCH(GetExitCodeThread)
|
||||
(
|
||||
HANDLE hThread,
|
||||
LPDWORD lpExitCode
|
||||
)
|
||||
{
|
||||
|
||||
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(hThread)
|
||||
LOG_FUNC_ARG(lpExitCode)
|
||||
LOG_FUNC_END;
|
||||
|
||||
BOOL bRet = GetExitCodeThread(hThread, (::LPDWORD)lpExitCode);
|
||||
|
||||
RETURN(bRet);
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
// * patch: XapiThreadStartup
|
||||
// ******************************************************************
|
||||
xbox::void_xt WINAPI xbox::EMUPATCH(XapiThreadStartup)
|
||||
(
|
||||
dword_xt dwDummy1,
|
||||
dword_xt dwDummy2
|
||||
)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(dwDummy1)
|
||||
LOG_FUNC_ARG(dwDummy2)
|
||||
LOG_FUNC_END;
|
||||
|
||||
typedef int (__stdcall *pfDummyFunc)(dword_xt dwDummy);
|
||||
|
||||
pfDummyFunc func = (pfDummyFunc)dwDummy1;
|
||||
|
||||
func(dwDummy2);
|
||||
|
||||
// TODO: Call thread notify routines ?
|
||||
|
||||
/*
|
||||
__asm
|
||||
{
|
||||
push dwDummy2
|
||||
call dwDummy1
|
||||
}
|
||||
*/
|
||||
|
||||
//_asm int 3;
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
// * patch: XRegisterThreadNotifyRoutine
|
||||
// ******************************************************************
|
||||
xbox::void_xt WINAPI xbox::EMUPATCH(XRegisterThreadNotifyRoutine)
|
||||
(
|
||||
PXTHREAD_NOTIFICATION pThreadNotification,
|
||||
bool_xt fRegister
|
||||
)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(pThreadNotification)
|
||||
LOG_FUNC_ARG(fRegister)
|
||||
LOG_FUNC_END;
|
||||
|
||||
if(fRegister)
|
||||
{
|
||||
// I honestly don't expect this to happen, but if it does...
|
||||
if(g_iThreadNotificationCount >= 16)
|
||||
CxbxrKrnlAbort("Too many thread notification routines installed\n");
|
||||
|
||||
// Find an empty spot in the thread notification array
|
||||
for(int i = 0; i < 16; i++)
|
||||
{
|
||||
// If we find one, then add it to the array, and break the loop so
|
||||
// that we don't accidently register the same routine twice!
|
||||
if(g_pfnThreadNotification[i] == NULL)
|
||||
{
|
||||
g_pfnThreadNotification[i] = (PVOID)pThreadNotification->pfnNotifyRoutine;
|
||||
g_iThreadNotificationCount++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Go through each routine and nullify the routine passed in.
|
||||
for(int i = 0; i < 16; i++)
|
||||
{
|
||||
if(pThreadNotification->pfnNotifyRoutine == g_pfnThreadNotification[i])
|
||||
{
|
||||
g_pfnThreadNotification[i] = NULL;
|
||||
g_iThreadNotificationCount--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
LPFIBER_START_ROUTINE lpStartRoutine;
|
||||
LPVOID lpParameter;
|
||||
|
@ -1148,214 +979,6 @@ xbox::LPVOID WINAPI xbox::EMUPATCH(ConvertThreadToFiber)
|
|||
|
||||
RETURN(pRet);
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
// * patch: QueueUserAPC
|
||||
// ******************************************************************
|
||||
xbox::dword_xt WINAPI xbox::EMUPATCH(QueueUserAPC)
|
||||
(
|
||||
PAPCFUNC pfnAPC,
|
||||
HANDLE hThread,
|
||||
dword_xt dwData
|
||||
)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG_TYPE(PVOID, pfnAPC)
|
||||
LOG_FUNC_ARG(hThread)
|
||||
LOG_FUNC_ARG(dwData)
|
||||
LOG_FUNC_END;
|
||||
|
||||
dword_xt dwRet = 0;
|
||||
|
||||
// If necessary, we can just continue to emulate NtQueueApcThread (0xCE).
|
||||
// I added this because NtQueueApcThread fails in Metal Slug 3.
|
||||
|
||||
HANDLE hApcThread = NULL;
|
||||
if(!DuplicateHandle(g_CurrentProcessHandle, hThread, g_CurrentProcessHandle, &hApcThread, THREAD_SET_CONTEXT,FALSE,0))
|
||||
EmuLog(LOG_LEVEL::WARNING, "DuplicateHandle failed!");
|
||||
|
||||
dwRet = QueueUserAPC(pfnAPC, hApcThread, dwData);
|
||||
if(!dwRet)
|
||||
EmuLog(LOG_LEVEL::WARNING, "QueueUserAPC failed!");
|
||||
|
||||
RETURN(dwRet);
|
||||
}
|
||||
|
||||
#if 0 // Handled by WaitForSingleObject
|
||||
// ******************************************************************
|
||||
// * patch: GetOverlappedResult
|
||||
// ******************************************************************
|
||||
xbox::bool_xt WINAPI xbox::EMUPATCH(GetOverlappedResult)
|
||||
(
|
||||
HANDLE hFile,
|
||||
LPOVERLAPPED lpOverlapped,
|
||||
LPDWORD lpNumberOfBytesTransferred,
|
||||
bool_xt bWait
|
||||
)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(hFile)
|
||||
LOG_FUNC_ARG(lpOverlapped)
|
||||
LOG_FUNC_ARG(lpNumberOfBytesTransferred)
|
||||
LOG_FUNC_ARG(bWait)
|
||||
LOG_FUNC_END;
|
||||
|
||||
BOOL bRet = GetOverlappedResult( hFile, lpOverlapped, lpNumberOfBytesTransferred, bWait );
|
||||
|
||||
// if(bWait)
|
||||
// bRet = TRUE; // Sucker...
|
||||
|
||||
RETURN(bRet);
|
||||
}
|
||||
#endif
|
||||
|
||||
// ******************************************************************
|
||||
// * patch: XLaunchNewImageA
|
||||
// ******************************************************************
|
||||
xbox::dword_xt WINAPI xbox::EMUPATCH(XLaunchNewImageA)
|
||||
(
|
||||
LPCSTR lpTitlePath,
|
||||
PLAUNCH_DATA pLaunchData
|
||||
)
|
||||
{
|
||||
// Note : This can be tested using "Innocent tears",
|
||||
// which relaunches different xbes between scenes;
|
||||
// One for menus, one for fmvs, etc.
|
||||
//
|
||||
// Other titles do this too (like "DOA2 Ultimate",
|
||||
// and probably "Panzer Dragoon Orta"), but these
|
||||
// titles don't come this far as-of yet.
|
||||
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(lpTitlePath)
|
||||
LOG_FUNC_ARG(pLaunchData)
|
||||
LOG_FUNC_END;
|
||||
|
||||
// TODO : This patch can be removed once NtOpenSymbolicLinkObject()
|
||||
// and NtQuerySymbolicLinkObject() work together correctly.
|
||||
// Also, XLaunchNewImageA() depends on XeImageHeader() and uses
|
||||
// XWriteTitleInfoAndReboot() and indirectly XWriteTitleInfoNoReboot()
|
||||
|
||||
// Update the kernel's LaunchDataPage :
|
||||
{
|
||||
if (xbox::LaunchDataPage == xbox::zeroptr)
|
||||
{
|
||||
PVOID LaunchDataVAddr = xbox::MmAllocateContiguousMemory(sizeof(xbox::LAUNCH_DATA_PAGE));
|
||||
if (!LaunchDataVAddr)
|
||||
{
|
||||
RETURN(X_STATUS_NO_MEMORY);
|
||||
}
|
||||
xbox::LaunchDataPage = (xbox::LAUNCH_DATA_PAGE*)LaunchDataVAddr;
|
||||
}
|
||||
|
||||
xbox::LaunchDataPage->Header.dwTitleId = g_pCertificate->dwTitleId;
|
||||
xbox::LaunchDataPage->Header.dwFlags = 0; // TODO : What to put in here?
|
||||
xbox::LaunchDataPage->Header.dwLaunchDataType = LDT_TITLE;
|
||||
|
||||
xbox::MmPersistContiguousMemory((PVOID)xbox::LaunchDataPage, PAGE_SIZE, TRUE);
|
||||
|
||||
if (pLaunchData != xbox::zeroptr)
|
||||
// Save the launch data
|
||||
memcpy(&(xbox::LaunchDataPage->LaunchData[0]), pLaunchData, sizeof(LAUNCH_DATA));
|
||||
|
||||
if (lpTitlePath == xbox::zeroptr)
|
||||
{
|
||||
// If no path is specified, then the xbe is rebooting to dashboard
|
||||
char szDashboardPath[xbox::max_path] = { 0 };
|
||||
XboxDevice* rootDevice = CxbxDeviceByDevicePath(DeviceHarddisk0Partition2);
|
||||
if (rootDevice != nullptr)
|
||||
sprintf(szDashboardPath, "%s\\xboxdash.xbe", rootDevice->HostDevicePath.c_str());
|
||||
|
||||
if (PathFileExists(szDashboardPath))
|
||||
{
|
||||
PopupInfo(nullptr, "The title is rebooting to dashboard");
|
||||
lpTitlePath = "C:\\xboxdash.xbe";
|
||||
xbox::LaunchDataPage->Header.dwLaunchDataType = LDT_FROM_DASHBOARD;
|
||||
// Other options include LDT_NONE, LDT_FROM_DEBUGGER_CMDLINE and LDT_FROM_UPDATE
|
||||
}
|
||||
else
|
||||
CxbxrKrnlAbort("The xbe rebooted to Dashboard and xboxdash.xbe could not be found");
|
||||
}
|
||||
|
||||
strncpy(&(xbox::LaunchDataPage->Header.szLaunchPath[0]), lpTitlePath, 520);
|
||||
}
|
||||
|
||||
// Note : While this patch exists, HalReturnToFirmware() calls
|
||||
// MmPersistContiguousMemory on LaunchDataPage. When this
|
||||
// patch on XLaunchNewImageA is removed, remove the call to
|
||||
// MmPersistContiguousMemory from HalReturnToFirmware() too!!
|
||||
|
||||
xbox::HalReturnToFirmware(xbox::ReturnFirmwareQuickReboot);
|
||||
|
||||
// If this function succeeds, it doesn't get a chance to return anything.
|
||||
RETURN(ERROR_GEN_FAILURE);
|
||||
}
|
||||
|
||||
#if 0 // patch disabled
|
||||
// ******************************************************************
|
||||
// * patch: XGetLaunchInfo
|
||||
// ******************************************************************
|
||||
xbox::dword_xt WINAPI xbox::EMUPATCH(XGetLaunchInfo)
|
||||
(
|
||||
PDWORD pdwLaunchDataType,
|
||||
PLAUNCH_DATA pLaunchData
|
||||
)
|
||||
{
|
||||
|
||||
|
||||
// TODO : This patch can be removed once we're sure all XAPI library
|
||||
// functions indirectly reference our xbox::LaunchDataPage variable.
|
||||
// For this, we need a test-case that hits this function, and run that
|
||||
// with and without this patch enabled. Behavior should be identical.
|
||||
// When this is verified, this patch can be removed.
|
||||
LOG_TEST_CASE("Unpatching test needed");
|
||||
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(pdwLaunchDataType)
|
||||
LOG_FUNC_ARG(pLaunchData)
|
||||
LOG_FUNC_END;
|
||||
|
||||
dword_xt ret = ERROR_NOT_FOUND;
|
||||
|
||||
if (xbox::LaunchDataPage != NULL)
|
||||
{
|
||||
// Note : Here, CxbxRestoreLaunchDataPage() was already called,
|
||||
// which has loaded LaunchDataPage from a binary file (if present).
|
||||
|
||||
// A title can pass data only to itself, not another title (unless started from the dashboard, of course) :
|
||||
if ( (xbox::LaunchDataPage->Header.dwTitleId == g_pCertificate->dwTitleId)
|
||||
|| (xbox::LaunchDataPage->Header.dwLaunchDataType == LDT_FROM_DASHBOARD)
|
||||
|| (xbox::LaunchDataPage->Header.dwLaunchDataType == LDT_FROM_DEBUGGER_CMDLINE))
|
||||
{
|
||||
*pdwLaunchDataType = xbox::LaunchDataPage->Header.dwLaunchDataType;
|
||||
memcpy(pLaunchData, &(xbox::LaunchDataPage->LaunchData[0]), sizeof(LAUNCH_DATA));
|
||||
|
||||
// Now that LaunchDataPage is retrieved by the emulated software, free it :
|
||||
MmFreeContiguousMemory(xbox::LaunchDataPage);
|
||||
xbox::LaunchDataPage = NULL;
|
||||
|
||||
ret = ERROR_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
RETURN(ret);
|
||||
}
|
||||
#endif
|
||||
|
||||
// ******************************************************************
|
||||
// * patch: XSetProcessQuantumLength
|
||||
// ******************************************************************
|
||||
xbox::void_xt WINAPI xbox::EMUPATCH(XSetProcessQuantumLength)
|
||||
(
|
||||
dword_xt dwMilliseconds
|
||||
)
|
||||
{
|
||||
|
||||
LOG_FUNC_ONE_ARG(dwMilliseconds);
|
||||
|
||||
// TODO: Implement?
|
||||
LOG_IGNORED();
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
// * patch: SignalObjectAndWait
|
||||
|
@ -1368,7 +991,6 @@ xbox::dword_xt WINAPI xbox::EMUPATCH(SignalObjectAndWait)
|
|||
bool_xt bAlertable
|
||||
)
|
||||
{
|
||||
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(hObjectToSignal)
|
||||
LOG_FUNC_ARG(hObjectToWaitOn)
|
||||
|
@ -1376,53 +998,25 @@ xbox::dword_xt WINAPI xbox::EMUPATCH(SignalObjectAndWait)
|
|||
LOG_FUNC_ARG(bAlertable)
|
||||
LOG_FUNC_END;
|
||||
|
||||
dword_xt dwRet = SignalObjectAndWait( hObjectToSignal, hObjectToWaitOn, dwMilliseconds, bAlertable );
|
||||
// Because user APCs from NtQueueApcThread are now handled by the kernel, we need to wait for them ourselves
|
||||
LARGE_INTEGER NewTime;
|
||||
if (dwMilliseconds == INFINITE) {
|
||||
NewTime.QuadPart = ~0ull;
|
||||
}
|
||||
else {
|
||||
NewTime.QuadPart = xbox::KeQueryInterruptTime();
|
||||
NewTime.QuadPart += (static_cast<xbox::ulonglong_xt>(dwMilliseconds) * CLOCK_TIME_INCREMENT);
|
||||
}
|
||||
|
||||
RETURN(dwRet);
|
||||
}
|
||||
xbox::dword_xt ret = WaitApc([hObjectToSignal, hObjectToWaitOn, bAlertable]() -> std::optional<DWORD> {
|
||||
DWORD dwRet = SignalObjectAndWait(hObjectToSignal, hObjectToWaitOn, 0, bAlertable);
|
||||
if (dwRet == WAIT_TIMEOUT) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return std::make_optional<DWORD>(dwRet);
|
||||
}, &NewTime, bAlertable, UserMode);
|
||||
|
||||
// ******************************************************************
|
||||
// * patch: timeSetEvent
|
||||
// ******************************************************************
|
||||
MMRESULT WINAPI xbox::EMUPATCH(timeSetEvent)
|
||||
(
|
||||
uint_xt uDelay,
|
||||
uint_xt uResolution,
|
||||
LPTIMECALLBACK fptc,
|
||||
dword_xt dwUser,
|
||||
uint_xt fuEvent
|
||||
)
|
||||
{
|
||||
|
||||
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(uDelay)
|
||||
LOG_FUNC_ARG(uResolution)
|
||||
LOG_FUNC_ARG_TYPE(PVOID, fptc)
|
||||
LOG_FUNC_ARG(dwUser)
|
||||
LOG_FUNC_ARG(fuEvent)
|
||||
LOG_FUNC_END;
|
||||
|
||||
MMRESULT Ret = timeSetEvent( uDelay, uResolution, fptc, (DWORD_PTR) dwUser, fuEvent );
|
||||
|
||||
RETURN(Ret);
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
// * patch: timeKillEvent
|
||||
// ******************************************************************
|
||||
MMRESULT WINAPI xbox::EMUPATCH(timeKillEvent)
|
||||
(
|
||||
uint_xt uTimerID
|
||||
)
|
||||
{
|
||||
|
||||
|
||||
LOG_FUNC_ONE_ARG(uTimerID);
|
||||
|
||||
MMRESULT Ret = timeKillEvent( uTimerID );
|
||||
|
||||
RETURN(Ret);
|
||||
RETURN((ret == X_STATUS_USER_APC) ? WAIT_IO_COMPLETION : (ret == X_STATUS_TIMEOUT) ? WAIT_TIMEOUT : ret);
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
|
|
|
@ -0,0 +1,393 @@
|
|||
xbox::bool_xt WINAPI xbox::EMUPATCH(SetThreadPriorityBoost)
|
||||
(
|
||||
HANDLE hThread,
|
||||
bool_xt DisablePriorityBoost
|
||||
)
|
||||
{
|
||||
|
||||
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(hThread)
|
||||
LOG_FUNC_ARG(DisablePriorityBoost)
|
||||
LOG_FUNC_END;
|
||||
|
||||
if (const auto &nativeHandle = GetNativeHandle(hThread)) {
|
||||
BOOL bRet = SetThreadPriorityBoost(*nativeHandle, DisablePriorityBoost);
|
||||
if (bRet == FALSE) {
|
||||
EmuLog(LOG_LEVEL::WARNING, "SetThreadPriorityBoost Failed!");
|
||||
}
|
||||
RETURN(bRet);
|
||||
}
|
||||
else {
|
||||
RETURN(0);
|
||||
}
|
||||
}
|
||||
|
||||
xbox::bool_xt WINAPI xbox::EMUPATCH(SetThreadPriority)
|
||||
(
|
||||
HANDLE hThread,
|
||||
int nPriority
|
||||
)
|
||||
{
|
||||
|
||||
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(hThread)
|
||||
LOG_FUNC_ARG(nPriority)
|
||||
LOG_FUNC_END;
|
||||
|
||||
if (const auto &nativeHandle = GetNativeHandle(hThread)) {
|
||||
BOOL bRet = SetThreadPriority(*nativeHandle, nPriority);
|
||||
if (bRet == FALSE) {
|
||||
EmuLog(LOG_LEVEL::WARNING, "SetThreadPriority Failed!");
|
||||
}
|
||||
RETURN(bRet);
|
||||
}
|
||||
else {
|
||||
RETURN(0);
|
||||
}
|
||||
}
|
||||
|
||||
int WINAPI xbox::EMUPATCH(GetThreadPriority)
|
||||
(
|
||||
HANDLE hThread
|
||||
)
|
||||
{
|
||||
|
||||
|
||||
LOG_FUNC_ONE_ARG(hThread);
|
||||
|
||||
if (const auto &nativeHandle = GetNativeHandle(hThread)) {
|
||||
int iRet = GetThreadPriority(*nativeHandle);
|
||||
if (iRet == THREAD_PRIORITY_ERROR_RETURN) {
|
||||
EmuLog(LOG_LEVEL::WARNING, "GetThreadPriority Failed!");
|
||||
}
|
||||
RETURN(iRet);
|
||||
}
|
||||
else {
|
||||
RETURN(THREAD_PRIORITY_ERROR_RETURN);
|
||||
}
|
||||
}
|
||||
|
||||
xbox::bool_xt WINAPI xbox::EMUPATCH(GetExitCodeThread)
|
||||
(
|
||||
HANDLE hThread,
|
||||
LPDWORD lpExitCode
|
||||
)
|
||||
{
|
||||
|
||||
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(hThread)
|
||||
LOG_FUNC_ARG(lpExitCode)
|
||||
LOG_FUNC_END;
|
||||
|
||||
if (const auto &nativeHandle = GetNativeHandle(hThread)) {
|
||||
RETURN(GetExitCodeThread(*nativeHandle, (::LPDWORD)lpExitCode));
|
||||
}
|
||||
else {
|
||||
RETURN(0);
|
||||
}
|
||||
}
|
||||
|
||||
xbox::void_xt WINAPI xbox::EMUPATCH(XapiThreadStartup)
|
||||
(
|
||||
dword_xt dwDummy1,
|
||||
dword_xt dwDummy2
|
||||
)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(dwDummy1)
|
||||
LOG_FUNC_ARG(dwDummy2)
|
||||
LOG_FUNC_END;
|
||||
|
||||
typedef int (__stdcall *pfDummyFunc)(dword_xt dwDummy);
|
||||
|
||||
pfDummyFunc func = (pfDummyFunc)dwDummy1;
|
||||
|
||||
func(dwDummy2);
|
||||
|
||||
// TODO: Call thread notify routines ?
|
||||
|
||||
/*
|
||||
__asm
|
||||
{
|
||||
push dwDummy2
|
||||
call dwDummy1
|
||||
}
|
||||
*/
|
||||
|
||||
//_asm int 3;
|
||||
}
|
||||
|
||||
xbox::void_xt WINAPI xbox::EMUPATCH(XRegisterThreadNotifyRoutine)
|
||||
(
|
||||
PXTHREAD_NOTIFICATION pThreadNotification,
|
||||
bool_xt fRegister
|
||||
)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(pThreadNotification)
|
||||
LOG_FUNC_ARG(fRegister)
|
||||
LOG_FUNC_END;
|
||||
|
||||
if(fRegister)
|
||||
{
|
||||
// I honestly don't expect this to happen, but if it does...
|
||||
if(g_iThreadNotificationCount >= 16)
|
||||
CxbxrKrnlAbort("Too many thread notification routines installed\n");
|
||||
|
||||
// Find an empty spot in the thread notification array
|
||||
for(int i = 0; i < 16; i++)
|
||||
{
|
||||
// If we find one, then add it to the array, and break the loop so
|
||||
// that we don't accidently register the same routine twice!
|
||||
if(g_pfnThreadNotification[i] == NULL)
|
||||
{
|
||||
g_pfnThreadNotification[i] = (PVOID)pThreadNotification->pfnNotifyRoutine;
|
||||
g_iThreadNotificationCount++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Go through each routine and nullify the routine passed in.
|
||||
for(int i = 0; i < 16; i++)
|
||||
{
|
||||
if(pThreadNotification->pfnNotifyRoutine == g_pfnThreadNotification[i])
|
||||
{
|
||||
g_pfnThreadNotification[i] = NULL;
|
||||
g_iThreadNotificationCount--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
xbox::dword_xt WINAPI xbox::EMUPATCH(QueueUserAPC)
|
||||
(
|
||||
PAPCFUNC pfnAPC,
|
||||
HANDLE hThread,
|
||||
dword_xt dwData
|
||||
)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG_TYPE(PVOID, pfnAPC)
|
||||
LOG_FUNC_ARG(hThread)
|
||||
LOG_FUNC_ARG(dwData)
|
||||
LOG_FUNC_END;
|
||||
|
||||
dword_xt dwRet = 0;
|
||||
|
||||
// If necessary, we can just continue to emulate NtQueueApcThread (0xCE).
|
||||
// I added this because NtQueueApcThread fails in Metal Slug 3.
|
||||
|
||||
HANDLE hApcThread = NULL;
|
||||
if(!DuplicateHandle(g_CurrentProcessHandle, hThread, g_CurrentProcessHandle, &hApcThread, THREAD_SET_CONTEXT,FALSE,0))
|
||||
EmuLog(LOG_LEVEL::WARNING, "DuplicateHandle failed!");
|
||||
|
||||
dwRet = QueueUserAPC(pfnAPC, hApcThread, dwData);
|
||||
if(!dwRet)
|
||||
EmuLog(LOG_LEVEL::WARNING, "QueueUserAPC failed!");
|
||||
|
||||
RETURN(dwRet);
|
||||
}
|
||||
|
||||
xbox::bool_xt WINAPI xbox::EMUPATCH(GetOverlappedResult)
|
||||
(
|
||||
HANDLE hFile,
|
||||
LPOVERLAPPED lpOverlapped,
|
||||
LPDWORD lpNumberOfBytesTransferred,
|
||||
bool_xt bWait
|
||||
)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(hFile)
|
||||
LOG_FUNC_ARG(lpOverlapped)
|
||||
LOG_FUNC_ARG(lpNumberOfBytesTransferred)
|
||||
LOG_FUNC_ARG(bWait)
|
||||
LOG_FUNC_END;
|
||||
|
||||
BOOL bRet = GetOverlappedResult( hFile, lpOverlapped, lpNumberOfBytesTransferred, bWait );
|
||||
|
||||
// if(bWait)
|
||||
// bRet = TRUE; // Sucker...
|
||||
|
||||
RETURN(bRet);
|
||||
}
|
||||
|
||||
xbox::dword_xt WINAPI xbox::EMUPATCH(XLaunchNewImageA)
|
||||
(
|
||||
LPCSTR lpTitlePath,
|
||||
PLAUNCH_DATA pLaunchData
|
||||
)
|
||||
{
|
||||
// Note : This can be tested using "Innocent tears",
|
||||
// which relaunches different xbes between scenes;
|
||||
// One for menus, one for fmvs, etc.
|
||||
//
|
||||
// Other titles do this too (like "DOA2 Ultimate",
|
||||
// and probably "Panzer Dragoon Orta"), but these
|
||||
// titles don't come this far as-of yet.
|
||||
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(lpTitlePath)
|
||||
LOG_FUNC_ARG(pLaunchData)
|
||||
LOG_FUNC_END;
|
||||
|
||||
// TODO : This patch can be removed once NtOpenSymbolicLinkObject()
|
||||
// and NtQuerySymbolicLinkObject() work together correctly.
|
||||
// Also, XLaunchNewImageA() depends on XeImageHeader() and uses
|
||||
// XWriteTitleInfoAndReboot() and indirectly XWriteTitleInfoNoReboot()
|
||||
|
||||
// Update the kernel's LaunchDataPage :
|
||||
{
|
||||
if (xbox::LaunchDataPage == xbox::zeroptr)
|
||||
{
|
||||
PVOID LaunchDataVAddr = xbox::MmAllocateContiguousMemory(sizeof(xbox::LAUNCH_DATA_PAGE));
|
||||
if (!LaunchDataVAddr)
|
||||
{
|
||||
RETURN(X_STATUS_NO_MEMORY);
|
||||
}
|
||||
xbox::LaunchDataPage = (xbox::LAUNCH_DATA_PAGE*)LaunchDataVAddr;
|
||||
}
|
||||
|
||||
xbox::LaunchDataPage->Header.dwTitleId = g_pCertificate->dwTitleId;
|
||||
xbox::LaunchDataPage->Header.dwFlags = 0; // TODO : What to put in here?
|
||||
xbox::LaunchDataPage->Header.dwLaunchDataType = LDT_TITLE;
|
||||
|
||||
xbox::MmPersistContiguousMemory((PVOID)xbox::LaunchDataPage, PAGE_SIZE, TRUE);
|
||||
|
||||
if (pLaunchData != xbox::zeroptr)
|
||||
// Save the launch data
|
||||
memcpy(&(xbox::LaunchDataPage->LaunchData[0]), pLaunchData, sizeof(LAUNCH_DATA));
|
||||
|
||||
if (lpTitlePath == xbox::zeroptr)
|
||||
{
|
||||
// If no path is specified, then the xbe is rebooting to dashboard
|
||||
char szDashboardPath[xbox::max_path] = { 0 };
|
||||
XboxDevice* rootDevice = CxbxDeviceByDevicePath(DeviceHarddisk0Partition2);
|
||||
if (rootDevice != nullptr)
|
||||
sprintf(szDashboardPath, "%s\\xboxdash.xbe", rootDevice->HostDevicePath.c_str());
|
||||
|
||||
if (PathFileExists(szDashboardPath))
|
||||
{
|
||||
PopupInfo(nullptr, "The title is rebooting to dashboard");
|
||||
lpTitlePath = "C:\\xboxdash.xbe";
|
||||
xbox::LaunchDataPage->Header.dwLaunchDataType = LDT_FROM_DASHBOARD;
|
||||
// Other options include LDT_NONE, LDT_FROM_DEBUGGER_CMDLINE and LDT_FROM_UPDATE
|
||||
}
|
||||
else
|
||||
CxbxrKrnlAbort("The xbe rebooted to Dashboard and xboxdash.xbe could not be found");
|
||||
}
|
||||
|
||||
strncpy(&(xbox::LaunchDataPage->Header.szLaunchPath[0]), lpTitlePath, 520);
|
||||
}
|
||||
|
||||
// Note : While this patch exists, HalReturnToFirmware() calls
|
||||
// MmPersistContiguousMemory on LaunchDataPage. When this
|
||||
// patch on XLaunchNewImageA is removed, remove the call to
|
||||
// MmPersistContiguousMemory from HalReturnToFirmware() too!!
|
||||
|
||||
xbox::HalReturnToFirmware(xbox::ReturnFirmwareQuickReboot);
|
||||
|
||||
// If this function succeeds, it doesn't get a chance to return anything.
|
||||
RETURN(ERROR_GEN_FAILURE);
|
||||
}
|
||||
|
||||
xbox::dword_xt WINAPI xbox::EMUPATCH(XGetLaunchInfo)
|
||||
(
|
||||
PDWORD pdwLaunchDataType,
|
||||
PLAUNCH_DATA pLaunchData
|
||||
)
|
||||
{
|
||||
|
||||
|
||||
// TODO : This patch can be removed once we're sure all XAPI library
|
||||
// functions indirectly reference our xbox::LaunchDataPage variable.
|
||||
// For this, we need a test-case that hits this function, and run that
|
||||
// with and without this patch enabled. Behavior should be identical.
|
||||
// When this is verified, this patch can be removed.
|
||||
LOG_TEST_CASE("Unpatching test needed");
|
||||
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(pdwLaunchDataType)
|
||||
LOG_FUNC_ARG(pLaunchData)
|
||||
LOG_FUNC_END;
|
||||
|
||||
dword_xt ret = ERROR_NOT_FOUND;
|
||||
|
||||
if (xbox::LaunchDataPage != NULL)
|
||||
{
|
||||
// Note : Here, CxbxRestoreLaunchDataPage() was already called,
|
||||
// which has loaded LaunchDataPage from a binary file (if present).
|
||||
|
||||
// A title can pass data only to itself, not another title (unless started from the dashboard, of course) :
|
||||
if ( (xbox::LaunchDataPage->Header.dwTitleId == g_pCertificate->dwTitleId)
|
||||
|| (xbox::LaunchDataPage->Header.dwLaunchDataType == LDT_FROM_DASHBOARD)
|
||||
|| (xbox::LaunchDataPage->Header.dwLaunchDataType == LDT_FROM_DEBUGGER_CMDLINE))
|
||||
{
|
||||
*pdwLaunchDataType = xbox::LaunchDataPage->Header.dwLaunchDataType;
|
||||
memcpy(pLaunchData, &(xbox::LaunchDataPage->LaunchData[0]), sizeof(LAUNCH_DATA));
|
||||
|
||||
// Now that LaunchDataPage is retrieved by the emulated software, free it :
|
||||
MmFreeContiguousMemory(xbox::LaunchDataPage);
|
||||
xbox::LaunchDataPage = NULL;
|
||||
|
||||
ret = ERROR_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
RETURN(ret);
|
||||
}
|
||||
|
||||
xbox::void_xt WINAPI xbox::EMUPATCH(XSetProcessQuantumLength)
|
||||
(
|
||||
dword_xt dwMilliseconds
|
||||
)
|
||||
{
|
||||
|
||||
LOG_FUNC_ONE_ARG(dwMilliseconds);
|
||||
|
||||
// TODO: Implement?
|
||||
LOG_IGNORED();
|
||||
}
|
||||
|
||||
MMRESULT WINAPI xbox::EMUPATCH(timeSetEvent)
|
||||
(
|
||||
uint_xt uDelay,
|
||||
uint_xt uResolution,
|
||||
LPTIMECALLBACK fptc,
|
||||
dword_xt dwUser,
|
||||
uint_xt fuEvent
|
||||
)
|
||||
{
|
||||
|
||||
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(uDelay)
|
||||
LOG_FUNC_ARG(uResolution)
|
||||
LOG_FUNC_ARG_TYPE(PVOID, fptc)
|
||||
LOG_FUNC_ARG(dwUser)
|
||||
LOG_FUNC_ARG(fuEvent)
|
||||
LOG_FUNC_END;
|
||||
|
||||
MMRESULT Ret = timeSetEvent( uDelay, uResolution, fptc, (DWORD_PTR) dwUser, fuEvent );
|
||||
|
||||
RETURN(Ret);
|
||||
}
|
||||
|
||||
MMRESULT WINAPI xbox::EMUPATCH(timeKillEvent)
|
||||
(
|
||||
uint_xt uTimerID
|
||||
)
|
||||
{
|
||||
|
||||
|
||||
LOG_FUNC_ONE_ARG(uTimerID);
|
||||
|
||||
MMRESULT Ret = timeKillEvent( uTimerID );
|
||||
|
||||
RETURN(Ret);
|
||||
}
|
|
@ -509,6 +509,7 @@ xbox::bool_xt WINAPI EMUPATCH(CloseHandle)
|
|||
HANDLE hObject
|
||||
);
|
||||
|
||||
#if 0 // Handled by KeSetBasePriorityThread
|
||||
// ******************************************************************
|
||||
// * patch: SetThreadPriority
|
||||
// ******************************************************************
|
||||
|
@ -517,7 +518,9 @@ xbox::bool_xt WINAPI EMUPATCH(SetThreadPriority)
|
|||
HANDLE hThread,
|
||||
int nPriority
|
||||
);
|
||||
#endif
|
||||
|
||||
#if 0 // Handled by KeQueryBasePriorityThread
|
||||
// ******************************************************************
|
||||
// * patch: GetThreadPriority
|
||||
// ******************************************************************
|
||||
|
@ -525,7 +528,9 @@ int WINAPI EMUPATCH(GetThreadPriority)
|
|||
(
|
||||
HANDLE hThread
|
||||
);
|
||||
#endif
|
||||
|
||||
#if 0 // Handled by KeSetDisableBoostThread
|
||||
// ******************************************************************
|
||||
// * patch: SetThreadPriorityBoost
|
||||
// ******************************************************************
|
||||
|
@ -534,7 +539,9 @@ xbox::bool_xt WINAPI EMUPATCH(SetThreadPriorityBoost)
|
|||
HANDLE hThread,
|
||||
bool_xt DisablePriorityBoost
|
||||
);
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
// ******************************************************************
|
||||
// * patch: GetExitCodeThread
|
||||
// ******************************************************************
|
||||
|
@ -543,6 +550,7 @@ xbox::bool_xt WINAPI EMUPATCH(GetExitCodeThread)
|
|||
HANDLE hThread,
|
||||
LPDWORD lpExitCode
|
||||
);
|
||||
#endif
|
||||
|
||||
// ******************************************************************
|
||||
// * patch: XapiThreadStartup
|
||||
|
|
|
@ -408,10 +408,10 @@ XBSYSAPI EXPORTNUM(143) long_xt NTAPI KeSetBasePriorityThread
|
|||
IN long_xt Priority
|
||||
);
|
||||
|
||||
XBSYSAPI EXPORTNUM(144) ulong_xt NTAPI KeSetDisableBoostThread
|
||||
XBSYSAPI EXPORTNUM(144) boolean_xt NTAPI KeSetDisableBoostThread
|
||||
(
|
||||
IN PKTHREAD Thread,
|
||||
IN ulong_xt Disable
|
||||
IN boolean_xt Disable
|
||||
);
|
||||
|
||||
// ******************************************************************
|
||||
|
|
|
@ -19,6 +19,8 @@ namespace xbox
|
|||
|
||||
#define NtCurrentThread() ((HANDLE)-2)
|
||||
|
||||
#define X_MAXIMUM_WAIT_OBJECTS 64
|
||||
|
||||
// ******************************************************************
|
||||
// * NtAllocateVirtualMemory
|
||||
// ******************************************************************
|
||||
|
@ -261,7 +263,7 @@ XBSYSAPI EXPORTNUM(206) ntstatus_xt NTAPI NtQueueApcThread
|
|||
IN PIO_APC_ROUTINE ApcRoutine,
|
||||
IN PVOID ApcRoutineContext OPTIONAL,
|
||||
IN PIO_STATUS_BLOCK ApcStatusBlock OPTIONAL,
|
||||
IN ulong_xt ApcReserved OPTIONAL
|
||||
IN PVOID ApcReserved OPTIONAL
|
||||
);
|
||||
|
||||
// ******************************************************************
|
||||
|
|
|
@ -14,18 +14,17 @@
|
|||
|
||||
#include "types.h"
|
||||
|
||||
#define X_THREAD_QUANTUM 60
|
||||
|
||||
namespace xbox
|
||||
{
|
||||
|
||||
#define PsGetCurrentThread() (CONTAINING_RECORD((KeGetCurrentThread()),ETHREAD,Tcb))
|
||||
|
||||
// ******************************************************************
|
||||
// * PsCreateSystemThread
|
||||
// ******************************************************************
|
||||
XBSYSAPI EXPORTNUM(254) ntstatus_xt NTAPI PsCreateSystemThread
|
||||
(
|
||||
OUT PHANDLE ThreadHandle,
|
||||
OUT PDWORD ThreadId OPTIONAL,
|
||||
OUT PHANDLE ThreadId OPTIONAL,
|
||||
IN PKSTART_ROUTINE StartRoutine,
|
||||
IN PVOID StartContext,
|
||||
IN boolean_xt DebuggerThread
|
||||
|
@ -40,7 +39,7 @@ XBSYSAPI EXPORTNUM(255) ntstatus_xt NTAPI PsCreateSystemThreadEx
|
|||
IN ulong_xt ThreadExtensionSize,
|
||||
IN ulong_xt KernelStackSize,
|
||||
IN ulong_xt TlsDataSize,
|
||||
OUT PDWORD ThreadId OPTIONAL,
|
||||
OUT PHANDLE ThreadId OPTIONAL,
|
||||
IN PKSTART_ROUTINE StartRoutine,
|
||||
IN PVOID StartContext,
|
||||
IN boolean_xt CreateSuspended,
|
||||
|
@ -69,7 +68,9 @@ XBSYSAPI EXPORTNUM(257) ntstatus_xt NTAPI PsSetCreateThreadNotifyRoutine
|
|||
// ******************************************************************
|
||||
XBSYSAPI EXPORTNUM(258) void_xt NTAPI PsTerminateSystemThread(IN ntstatus_xt ExitStatus);
|
||||
|
||||
XBSYSAPI EXPORTNUM(259) volatile OBJECT_TYPE PsThreadObjectType;
|
||||
XBSYSAPI EXPORTNUM(259) OBJECT_TYPE PsThreadObjectType;
|
||||
|
||||
PETHREAD PspGetCurrentThread();
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -81,6 +81,7 @@ typedef void* LPSECURITY_ATTRIBUTES;
|
|||
#define X_STATUS_UNRECOGNIZED_MEDIA 0xC0000014L
|
||||
#define X_STATUS_NO_MEMORY 0xC0000017L
|
||||
#define X_STATUS_BUFFER_TOO_SMALL 0xC0000023L
|
||||
#define X_STATUS_INVALID_PARAMETER 0xC000000DL
|
||||
#define X_STATUS_INVALID_PARAMETER_2 0xC00000F0L
|
||||
#define X_STATUS_ALERTED 0x00000101L
|
||||
#define X_STATUS_USER_APC 0x000000C0L
|
||||
|
@ -104,6 +105,7 @@ typedef void* LPSECURITY_ATTRIBUTES;
|
|||
#define X_STATUS_NOT_COMMITTED 0xC000002DL
|
||||
#define X_STATUS_UNRECOGNIZED_VOLUME 0xC000014FL
|
||||
#define X_STATUS_OBJECT_PATH_NOT_FOUND 0xC000003AL
|
||||
#define X_STATUS_TIMEOUT 0x00000102L
|
||||
|
||||
// ******************************************************************
|
||||
// * Registry value types
|
||||
|
@ -1496,8 +1498,10 @@ KFLOATING_SAVE, *PKFLOATING_SAVE;
|
|||
// ******************************************************************
|
||||
typedef enum _KOBJECTS
|
||||
{
|
||||
EventNotificationObject = 0,
|
||||
EventSynchronizationObject = 1,
|
||||
MutantObject = 2,
|
||||
ProcessObject = 3,
|
||||
QueueObject = 4,
|
||||
SemaphoreObject = 5,
|
||||
ThreadObject = 6,
|
||||
|
@ -1506,13 +1510,16 @@ typedef enum _KOBJECTS
|
|||
ApcObject = 0x12,
|
||||
DpcObject = 0x13,
|
||||
DeviceQueueObject = 0x14,
|
||||
EventPairObject = 0x15,
|
||||
InterruptObject = 0x16,
|
||||
ProfileObject = 0x17,
|
||||
}
|
||||
KOBJECTS, *PKOBJECTS;
|
||||
|
||||
// ******************************************************************
|
||||
// * PKNORMAL_ROUTINE
|
||||
// ******************************************************************
|
||||
typedef void_xt (*PKNORMAL_ROUTINE)
|
||||
typedef void_xt (NTAPI *PKNORMAL_ROUTINE)
|
||||
(
|
||||
IN PVOID NormalContext,
|
||||
IN PVOID SystemArgument1,
|
||||
|
@ -1914,11 +1921,15 @@ KTHREAD, *PKTHREAD, *RESTRICTED_POINTER PRKTHREAD;
|
|||
// ******************************************************************
|
||||
typedef struct _ETHREAD
|
||||
{
|
||||
struct _KTHREAD Tcb;
|
||||
uchar_xt UnknownA[0x1C]; // 0x110
|
||||
dword_xt UniqueThread; // 0x12C
|
||||
struct _KTHREAD Tcb;
|
||||
LARGE_INTEGER CreateTime; // 0x110
|
||||
LARGE_INTEGER ExitTime; // 0x118
|
||||
ntstatus_xt ExitStatus; // 0x120
|
||||
uchar_xt Unknown[0x8]; // 0x124
|
||||
HANDLE UniqueThread; // 0x12C
|
||||
}
|
||||
ETHREAD, *PETHREAD;
|
||||
static_assert(sizeof(ETHREAD) == 0x130);
|
||||
|
||||
// ******************************************************************
|
||||
// * PCREATE_THREAD_NOTIFY_ROUTINE
|
||||
|
|
|
@ -29,6 +29,9 @@
|
|||
|
||||
|
||||
#include <core\kernel\exports\xboxkrnl.h>
|
||||
#include <core\kernel\exports\EmuKrnlKi.h>
|
||||
#include "core\kernel\support\EmuFS.h"
|
||||
#include "core\kernel\support\NativeHandle.h"
|
||||
#include <cstdio>
|
||||
#include <cctype>
|
||||
#include <clocale>
|
||||
|
@ -119,12 +122,6 @@ xbox::PLIST_ENTRY RemoveTailList(xbox::PLIST_ENTRY pListHead)
|
|||
return Result;
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
// * Declaring this in a header causes errors with xboxkrnl
|
||||
// * namespace, so we must declare it within any file that uses it
|
||||
// ******************************************************************
|
||||
xbox::KPCR* WINAPI KeGetPcr();
|
||||
|
||||
// Interrupts
|
||||
|
||||
extern volatile DWORD HalInterruptRequestRegister;
|
||||
|
@ -157,8 +154,8 @@ void CallSoftwareInterrupt(const xbox::KIRQL SoftwareIrql)
|
|||
case PASSIVE_LEVEL:
|
||||
KiUnexpectedInterrupt();
|
||||
break;
|
||||
case APC_LEVEL: // = 1 // HalpApcInterrupt
|
||||
EmuLog(LOG_LEVEL::WARNING, "Unimplemented Software Interrupt (APC)"); // TODO : ExecuteApcQueue();
|
||||
case APC_LEVEL: // = 1 HalpApcInterrupt
|
||||
xbox::KiExecuteKernelApc();
|
||||
break;
|
||||
case DISPATCH_LEVEL: // = 2
|
||||
ExecuteDpcQueue();
|
||||
|
@ -213,6 +210,7 @@ const DWORD IrqlMasks[] = {
|
|||
0x00000000, // IRQL 31 (HIGH_LEVEL)
|
||||
};
|
||||
|
||||
|
||||
// ******************************************************************
|
||||
// * 0x0033 - InterlockedCompareExchange()
|
||||
// ******************************************************************
|
||||
|
@ -374,7 +372,7 @@ XBSYSAPI EXPORTNUM(160) xbox::KIRQL FASTCALL xbox::KfRaiseIrql
|
|||
LOG_FUNC_ONE_ARG_TYPE(KIRQL_TYPE, NewIrql);
|
||||
|
||||
// Inlined KeGetCurrentIrql() :
|
||||
PKPCR Pcr = KeGetPcr();
|
||||
PKPCR Pcr = EmuKeGetPcr();
|
||||
KIRQL OldIrql = (KIRQL)Pcr->Irql;
|
||||
|
||||
// Set new before check
|
||||
|
@ -402,7 +400,7 @@ XBSYSAPI EXPORTNUM(161) xbox::void_xt FASTCALL xbox::KfLowerIrql
|
|||
{
|
||||
LOG_FUNC_ONE_ARG_TYPE(KIRQL_TYPE, NewIrql);
|
||||
|
||||
KPCR* Pcr = KeGetPcr();
|
||||
KPCR* Pcr = EmuKeGetPcr();
|
||||
|
||||
if (g_bIsDebugKernel && NewIrql > Pcr->Irql) {
|
||||
KIRQL OldIrql = Pcr->Irql;
|
||||
|
@ -450,12 +448,23 @@ XBSYSAPI EXPORTNUM(163) xbox::void_xt FASTCALL xbox::KiUnlockDispatcherDatabase
|
|||
{
|
||||
LOG_FUNC_ONE_ARG_TYPE(KIRQL_TYPE, OldIrql);
|
||||
|
||||
if (!(KeGetCurrentPrcb()->DpcRoutineActive)) // Avoid KeIsExecutingDpc(), as that logs
|
||||
// Wrong, this should only happen when OldIrql >= DISPATCH_LEVEL
|
||||
if (!(KeGetCurrentPrcb()->DpcRoutineActive)) { // Avoid KeIsExecutingDpc(), as that logs
|
||||
HalRequestSoftwareInterrupt(DISPATCH_LEVEL);
|
||||
}
|
||||
|
||||
LOG_INCOMPLETE(); // TODO : Thread-switch?
|
||||
if (OldIrql < DISPATCH_LEVEL) {
|
||||
// FIXME: this is wrong, it should perform a thread switch and check the kthread of the new selected thread for pending APCs.
|
||||
// We can't perform our own threads switching now, so we will just check the current thread
|
||||
|
||||
if (KeGetCurrentThread()->ApcState.KernelApcPending) {
|
||||
KiExecuteKernelApc();
|
||||
}
|
||||
}
|
||||
|
||||
KfLowerIrql(OldIrql);
|
||||
|
||||
LOG_INCOMPLETE(); // TODO : Thread-switch?
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
|
|
|
@ -27,6 +27,8 @@
|
|||
|
||||
#include "core\kernel\init\CxbxKrnl.h"
|
||||
#include "core\kernel\support\Emu.h"
|
||||
#include "core\kernel\support\EmuFS.h"
|
||||
#include <future>
|
||||
|
||||
// CONTAINING_RECORD macro
|
||||
// Gets the value of structure member (field - num1),given the type(MYSTRUCT, in this code) and the List_Entry head(temp, in this code)
|
||||
|
@ -49,6 +51,8 @@ xbox::PLIST_ENTRY RemoveTailList(xbox::PLIST_ENTRY pListHead);
|
|||
|
||||
extern xbox::LAUNCH_DATA_PAGE DefaultLaunchDataPage;
|
||||
extern xbox::PKINTERRUPT EmuInterruptList[MAX_BUS_INTERRUPT_LEVEL + 1];
|
||||
inline std::condition_variable g_InterruptSignal;
|
||||
inline std::atomic_bool g_AnyInterruptAsserted = false;
|
||||
|
||||
class HalSystemInterrupt {
|
||||
public:
|
||||
|
@ -59,6 +63,8 @@ public:
|
|||
}
|
||||
|
||||
m_Asserted = state;
|
||||
g_AnyInterruptAsserted = true;
|
||||
g_InterruptSignal.notify_one();
|
||||
};
|
||||
|
||||
void Enable() {
|
||||
|
@ -104,4 +110,44 @@ bool DisableInterrupts();
|
|||
void RestoreInterruptMode(bool value);
|
||||
void CallSoftwareInterrupt(const xbox::KIRQL SoftwareIrql);
|
||||
|
||||
template<typename T>
|
||||
xbox::ntstatus_xt WaitApc(T &&Lambda, xbox::PLARGE_INTEGER AbsoluteExpireTime, xbox::boolean_xt Alertable, xbox::char_xt WaitMode)
|
||||
{
|
||||
// NOTE: kThread->Alerted is currently never set. When the alerted mechanism is implemented, the alerts should
|
||||
// also interrupt the wait
|
||||
|
||||
xbox::ulonglong_xt now = xbox::KeQueryInterruptTime();
|
||||
xbox::PETHREAD eThread = reinterpret_cast<xbox::PETHREAD>(EmuKeGetPcr()->Prcb->CurrentThread);
|
||||
|
||||
if (AbsoluteExpireTime->QuadPart == 0) {
|
||||
// This will only happen when the title specifies a zero timeout
|
||||
AbsoluteExpireTime->QuadPart = now;
|
||||
}
|
||||
|
||||
while (now <= static_cast<xbox::ulonglong_xt>(AbsoluteExpireTime->QuadPart)) {
|
||||
if (const auto ret = Lambda()) {
|
||||
return *ret;
|
||||
}
|
||||
|
||||
xbox::KiApcListMtx.lock();
|
||||
bool EmptyKernel = IsListEmpty(&eThread->Tcb.ApcState.ApcListHead[xbox::KernelMode]);
|
||||
bool EmptyUser = IsListEmpty(&eThread->Tcb.ApcState.ApcListHead[xbox::UserMode]);
|
||||
xbox::KiApcListMtx.unlock();
|
||||
if (EmptyKernel == false) {
|
||||
xbox::KiExecuteKernelApc();
|
||||
}
|
||||
if ((EmptyUser == false) &&
|
||||
(Alertable == TRUE) &&
|
||||
(WaitMode == xbox::UserMode)) {
|
||||
xbox::KiExecuteUserApc();
|
||||
return X_STATUS_USER_APC;
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(5));
|
||||
now = xbox::KeQueryInterruptTime();
|
||||
}
|
||||
|
||||
return X_STATUS_TIMEOUT;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include "EmuKrnlLogging.h"
|
||||
#include "core\kernel\init\CxbxKrnl.h" // For CxbxrKrnlAbort, and CxbxExec
|
||||
#include "core\kernel\support\Emu.h" // For EmuLog(LOG_LEVEL::WARNING, )
|
||||
#include "core\kernel\support\EmuFS.h"
|
||||
#include "EmuKrnl.h"
|
||||
#include "devices\x86\EmuX86.h" // HalReadWritePciSpace needs this
|
||||
#include "EmuShared.h"
|
||||
|
@ -59,13 +60,6 @@ uint32_t ResetOrShutdownDataValue = 0;
|
|||
// global list of routines executed during a reboot
|
||||
xbox::LIST_ENTRY ShutdownRoutineList = { &ShutdownRoutineList , &ShutdownRoutineList }; // see InitializeListHead()
|
||||
|
||||
|
||||
// ******************************************************************
|
||||
// * Declaring this in a header causes errors with xboxkrnl
|
||||
// * namespace, so we must declare it within any file that uses it
|
||||
// ******************************************************************
|
||||
xbox::KPCR* WINAPI KeGetPcr();
|
||||
|
||||
#define TRAY_CLOSED_MEDIA_PRESENT 0x60
|
||||
#define TRAY_CLOSED_NO_MEDIA 0x40
|
||||
#define TRAY_OPEN 0x10
|
||||
|
@ -458,10 +452,10 @@ XBSYSAPI EXPORTNUM(48) xbox::void_xt FASTCALL xbox::HalRequestSoftwareInterrupt
|
|||
HalInterruptRequestRegister |= InterruptMask;
|
||||
|
||||
// Get current IRQL
|
||||
PKPCR Pcr = KeGetPcr();
|
||||
PKPCR Pcr = EmuKeGetPcr();
|
||||
KIRQL CurrentIrql = (KIRQL)Pcr->Irql;
|
||||
|
||||
// Get pending Software Interrupts (by masking of the HW interrupt bits)
|
||||
// Get pending Software Interrupts (by masking off the HW interrupt bits)
|
||||
uint8_t SoftwareInterrupt = HalInterruptRequestRegister & 3;
|
||||
|
||||
// Get the highest pending software interrupt level
|
||||
|
@ -605,7 +599,6 @@ XBSYSAPI EXPORTNUM(49) xbox::void_xt DECLSPEC_NORETURN NTAPI xbox::HalReturnToFi
|
|||
}
|
||||
|
||||
CxbxKrnlShutDown(is_reboot);
|
||||
TerminateProcess(GetCurrentProcess(), EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
|
|
|
@ -81,7 +81,9 @@ namespace NtDll
|
|||
#include "EmuKrnlKi.h" // For KiRemoveTreeTimer(), KiInsertTreeTimer()
|
||||
#include "EmuKrnlKe.h"
|
||||
#include "core\kernel\support\EmuFile.h" // For IsEmuHandle(), NtStatusToString()
|
||||
#include "core\kernel\support\NativeHandle.h"
|
||||
#include "Timer.h"
|
||||
#include "Util.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
@ -128,24 +130,18 @@ xbox::ulonglong_xt LARGE_INTEGER2ULONGLONG(xbox::LARGE_INTEGER value)
|
|||
|
||||
|
||||
// ******************************************************************
|
||||
// * KeGetPcr()
|
||||
// * EmuKeGetPcr()
|
||||
// * NOTE: This is a macro on the Xbox, however we implement it
|
||||
// * as a function so it can suit our emulated KPCR structure
|
||||
// ******************************************************************
|
||||
xbox::KPCR* WINAPI KeGetPcr()
|
||||
xbox::KPCR* WINAPI EmuKeGetPcr()
|
||||
{
|
||||
xbox::PKPCR Pcr;
|
||||
|
||||
// See EmuKeSetPcr()
|
||||
Pcr = (xbox::PKPCR)__readfsdword(TIB_ArbitraryDataSlot);
|
||||
|
||||
if (Pcr == nullptr) {
|
||||
EmuLog(LOG_LEVEL::WARNING, "KeGetPCR returned nullptr: Was this called from a non-xbox thread?");
|
||||
// Attempt to salvage the situation by calling InitXboxThread to setup KPCR in place
|
||||
InitXboxThread();
|
||||
g_AffinityPolicy->SetAffinityXbox();
|
||||
Pcr = (xbox::PKPCR)__readfsdword(TIB_ArbitraryDataSlot);
|
||||
}
|
||||
xbox::PKPCR Pcr = (xbox::PKPCR)__readfsdword(TIB_ArbitraryDataSlot);
|
||||
|
||||
// If this fails, it's a bug: it means we are executing xbox code from a host thread, and we have forgotten to initialize
|
||||
// the xbox thread first
|
||||
assert(Pcr);
|
||||
|
||||
return Pcr;
|
||||
}
|
||||
|
@ -155,7 +151,7 @@ xbox::KPCR* WINAPI KeGetPcr()
|
|||
// ******************************************************************
|
||||
xbox::KPRCB *KeGetCurrentPrcb()
|
||||
{
|
||||
return &(KeGetPcr()->PrcbData);
|
||||
return &(EmuKeGetPcr()->PrcbData);
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
|
@ -265,6 +261,26 @@ xbox::void_xt NTAPI xbox::KeInitializeTimer
|
|||
KeInitializeTimerEx(Timer, NotificationTimer);
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
// * KeEmptyQueueApc()
|
||||
// ******************************************************************
|
||||
xbox::void_xt xbox::KeEmptyQueueApc()
|
||||
{
|
||||
PKTHREAD kThread = KeGetCurrentThread();
|
||||
kThread->ApcState.ApcQueueable = FALSE;
|
||||
|
||||
KiApcListMtx.lock();
|
||||
for (int Mode = KernelMode; Mode < MaximumMode; ++Mode) {
|
||||
while (!IsListEmpty(&kThread->ApcState.ApcListHead[Mode])) {
|
||||
PLIST_ENTRY Entry = kThread->ApcState.ApcListHead[Mode].Flink;
|
||||
PKAPC Apc = CONTAINING_RECORD(Entry, KAPC, ApcListEntry);
|
||||
RemoveEntryList(Entry);
|
||||
ExFreePool(Apc);
|
||||
}
|
||||
}
|
||||
KiApcListMtx.unlock();
|
||||
}
|
||||
|
||||
// Forward KeLowerIrql() to KfLowerIrql()
|
||||
#define KeLowerIrql(NewIrql) \
|
||||
KfLowerIrql(NewIrql)
|
||||
|
@ -547,7 +563,30 @@ XBSYSAPI EXPORTNUM(99) xbox::ntstatus_xt NTAPI xbox::KeDelayExecutionThread
|
|||
LOG_FUNC_ARG(Interval)
|
||||
LOG_FUNC_END;
|
||||
|
||||
NTSTATUS ret = NtDll::NtDelayExecution(Alertable, (NtDll::LARGE_INTEGER*)Interval);
|
||||
// Because user APCs from NtQueueApcThread are now handled by the kernel, we need to wait for them ourselves
|
||||
// We can't remove NtDll::NtDelayExecution until all APCs queued by Io are implemented by our kernel as well
|
||||
// Test case: Metal Slug 3
|
||||
LARGE_INTEGER NewTime;
|
||||
PLARGE_INTEGER pNewTime;
|
||||
if (Interval) {
|
||||
LARGE_INTEGER ExpireTime, DueTime;
|
||||
ExpireTime.QuadPart = DueTime.QuadPart = Interval->QuadPart;
|
||||
pNewTime = KiComputeWaitInterval(&ExpireTime, &DueTime, &NewTime);
|
||||
}
|
||||
else {
|
||||
NewTime.QuadPart = ~0ull;
|
||||
pNewTime = &NewTime;
|
||||
}
|
||||
|
||||
xbox::ntstatus_xt ret = WaitApc([Alertable]() -> std::optional<ntstatus_xt> {
|
||||
NtDll::LARGE_INTEGER ExpireTime;
|
||||
ExpireTime.QuadPart = 0;
|
||||
NTSTATUS Status = NtDll::NtDelayExecution(Alertable, &ExpireTime);
|
||||
if (Status == 0) { // STATUS_SUCCESS
|
||||
return std::nullopt;
|
||||
}
|
||||
return std::make_optional<ntstatus_xt>(Status);
|
||||
}, pNewTime, Alertable, WaitMode);
|
||||
|
||||
RETURN(ret);
|
||||
}
|
||||
|
@ -597,7 +636,7 @@ XBSYSAPI EXPORTNUM(103) xbox::KIRQL NTAPI xbox::KeGetCurrentIrql(void)
|
|||
{
|
||||
LOG_FUNC(); // TODO : Remove nested logging on this somehow, so we can call this (instead of inlining)
|
||||
|
||||
KPCR* Pcr = KeGetPcr();
|
||||
KPCR* Pcr = EmuKeGetPcr();
|
||||
KIRQL Irql = (KIRQL)Pcr->Irql;
|
||||
|
||||
RETURN_TYPE(KIRQL_TYPE, Irql);
|
||||
|
@ -995,6 +1034,9 @@ XBSYSAPI EXPORTNUM(117) xbox::long_xt NTAPI xbox::KeInsertQueue
|
|||
RETURN(0);
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
// * 0x0076 - KeInsertQueueApc()
|
||||
// ******************************************************************
|
||||
XBSYSAPI EXPORTNUM(118) xbox::boolean_xt NTAPI xbox::KeInsertQueueApc
|
||||
(
|
||||
IN PRKAPC Apc,
|
||||
|
@ -1010,9 +1052,47 @@ XBSYSAPI EXPORTNUM(118) xbox::boolean_xt NTAPI xbox::KeInsertQueueApc
|
|||
LOG_FUNC_ARG(Increment)
|
||||
LOG_FUNC_END;
|
||||
|
||||
LOG_UNIMPLEMENTED();
|
||||
KIRQL OldIrql = KeRaiseIrqlToDpcLevel();
|
||||
|
||||
RETURN(TRUE);
|
||||
PKTHREAD kThread = Apc->Thread;
|
||||
if (kThread->ApcState.ApcQueueable == FALSE) {
|
||||
KfLowerIrql(OldIrql);
|
||||
RETURN(FALSE);
|
||||
}
|
||||
else {
|
||||
Apc->SystemArgument1 = SystemArgument1;
|
||||
Apc->SystemArgument2 = SystemArgument2;
|
||||
|
||||
if (Apc->Inserted) {
|
||||
KfLowerIrql(OldIrql);
|
||||
RETURN(FALSE);
|
||||
}
|
||||
else {
|
||||
KiApcListMtx.lock();
|
||||
InsertTailList(&kThread->ApcState.ApcListHead[Apc->ApcMode], &Apc->ApcListEntry);
|
||||
Apc->Inserted = TRUE;
|
||||
KiApcListMtx.unlock();
|
||||
|
||||
// We can only attempt to execute the queued apc right away if it is been inserted in the current thread, because otherwise the KTHREAD
|
||||
// in the fs selector will not be correct
|
||||
if (kThread == KeGetCurrentThread()) {
|
||||
if (Apc->ApcMode == KernelMode) { // kernel apc
|
||||
// NOTE: this is wrong, we should check the thread state instead of just signaling the kernel apc, but we currently
|
||||
// don't set the appropriate state in kthread
|
||||
kThread->ApcState.KernelApcPending = TRUE;
|
||||
KiExecuteKernelApc();
|
||||
}
|
||||
else if ((kThread->WaitMode == UserMode) && (kThread->Alertable)) { // user apc
|
||||
// NOTE: this should also check the thread state
|
||||
kThread->ApcState.UserApcPending = TRUE;
|
||||
KiExecuteUserApc();
|
||||
}
|
||||
}
|
||||
|
||||
KfLowerIrql(OldIrql);
|
||||
RETURN(TRUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
|
@ -1089,10 +1169,10 @@ XBSYSAPI EXPORTNUM(122) xbox::void_xt NTAPI xbox::KeLeaveCriticalRegion
|
|||
PKTHREAD thread = KeGetCurrentThread();
|
||||
thread->KernelApcDisable++;
|
||||
if(thread->KernelApcDisable == 0) {
|
||||
LIST_ENTRY *apcListHead = &thread->ApcState.ApcListHead[0/*=KernelMode*/];
|
||||
LIST_ENTRY *apcListHead = &thread->ApcState.ApcListHead[KernelMode];
|
||||
if(apcListHead->Flink != apcListHead) {
|
||||
thread->ApcState.KernelApcPending = 1; // TRUE
|
||||
HalRequestSoftwareInterrupt(1); // APC_LEVEL
|
||||
thread->ApcState.KernelApcPending = TRUE;
|
||||
HalRequestSoftwareInterrupt(APC_LEVEL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1152,9 +1232,16 @@ XBSYSAPI EXPORTNUM(124) xbox::long_xt NTAPI xbox::KeQueryBasePriorityThread
|
|||
{
|
||||
LOG_FUNC_ONE_ARG(Thread);
|
||||
|
||||
LOG_UNIMPLEMENTED();
|
||||
KIRQL OldIrql;
|
||||
KiLockDispatcherDatabase(&OldIrql);
|
||||
|
||||
RETURN(0);
|
||||
// It cannot fail because all thread handles are created by ob
|
||||
const auto& nativeHandle = GetNativeHandle<true>(PspGetCurrentThread()->UniqueThread);
|
||||
long_xt ret = GetThreadPriority(*nativeHandle);
|
||||
|
||||
KiUnlockDispatcherDatabase(OldIrql);
|
||||
|
||||
RETURN(ret);
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
|
@ -1572,21 +1659,31 @@ XBSYSAPI EXPORTNUM(143) xbox::long_xt NTAPI xbox::KeSetBasePriorityThread
|
|||
LOG_FUNC_ARG_OUT(Priority)
|
||||
LOG_FUNC_END;
|
||||
|
||||
LONG ret = GetThreadPriority((HANDLE)Thread);
|
||||
KIRQL oldIRQL;
|
||||
KiLockDispatcherDatabase(&oldIRQL);
|
||||
|
||||
// It cannot fail because all thread handles are created by ob
|
||||
const auto &nativeHandle = GetNativeHandle<true>(PspGetCurrentThread()->UniqueThread);
|
||||
LONG ret = GetThreadPriority(*nativeHandle);
|
||||
|
||||
// This would work normally, but it will slow down the emulation,
|
||||
// don't do that if the priority is higher then normal (so our own)!
|
||||
if((Priority <= THREAD_PRIORITY_NORMAL) && ((HANDLE)Thread != GetCurrentThread())) {
|
||||
SetThreadPriority((HANDLE)Thread, Priority);
|
||||
if (Priority <= THREAD_PRIORITY_NORMAL) {
|
||||
BOOL result = SetThreadPriority(*nativeHandle, Priority);
|
||||
if (!result) {
|
||||
EmuLog(LOG_LEVEL::WARNING, "SetThreadPriority failed: %s", WinError2Str().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
KiUnlockDispatcherDatabase(oldIRQL);
|
||||
|
||||
RETURN(ret);
|
||||
}
|
||||
|
||||
XBSYSAPI EXPORTNUM(144) xbox::ulong_xt NTAPI xbox::KeSetDisableBoostThread
|
||||
XBSYSAPI EXPORTNUM(144) xbox::boolean_xt NTAPI xbox::KeSetDisableBoostThread
|
||||
(
|
||||
IN PKTHREAD Thread,
|
||||
IN ulong_xt Disable
|
||||
IN boolean_xt Disable
|
||||
)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
|
@ -1597,9 +1694,17 @@ XBSYSAPI EXPORTNUM(144) xbox::ulong_xt NTAPI xbox::KeSetDisableBoostThread
|
|||
KIRQL oldIRQL;
|
||||
KiLockDispatcherDatabase(&oldIRQL);
|
||||
|
||||
ULONG prevDisableBoost = Thread->DisableBoost;
|
||||
// It cannot fail because all thread handles are created by ob
|
||||
const auto &nativeHandle = GetNativeHandle<true>(PspGetCurrentThread()->UniqueThread);
|
||||
|
||||
boolean_xt prevDisableBoost = Thread->DisableBoost;
|
||||
Thread->DisableBoost = (CHAR)Disable;
|
||||
|
||||
BOOL bRet = SetThreadPriorityBoost(*nativeHandle, Disable);
|
||||
if (!bRet) {
|
||||
EmuLog(LOG_LEVEL::WARNING, "SetThreadPriorityBoost failed: %s", WinError2Str().c_str());
|
||||
}
|
||||
|
||||
KiUnlockDispatcherDatabase(oldIRQL);
|
||||
|
||||
RETURN(prevDisableBoost);
|
||||
|
@ -1910,24 +2015,6 @@ XBSYSAPI EXPORTNUM(156) xbox::dword_xt VOLATILE xbox::KeTickCount = 0;
|
|||
// ******************************************************************
|
||||
XBSYSAPI EXPORTNUM(157) xbox::ulong_xt xbox::KeTimeIncrement = CLOCK_TIME_INCREMENT;
|
||||
|
||||
|
||||
xbox::PLARGE_INTEGER FASTCALL KiComputeWaitInterval(
|
||||
IN xbox::PLARGE_INTEGER OriginalTime,
|
||||
IN xbox::PLARGE_INTEGER DueTime,
|
||||
IN OUT xbox::PLARGE_INTEGER NewTime
|
||||
)
|
||||
{
|
||||
if (OriginalTime->QuadPart >= 0) {
|
||||
return OriginalTime;
|
||||
|
||||
}
|
||||
else {
|
||||
NewTime->QuadPart = xbox::KeQueryInterruptTime();
|
||||
NewTime->QuadPart -= DueTime->QuadPart;
|
||||
return NewTime;
|
||||
}
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
// * 0x009E - KeWaitForMultipleObjects()
|
||||
// ******************************************************************
|
||||
|
@ -1992,7 +2079,7 @@ XBSYSAPI EXPORTNUM(158) xbox::ntstatus_xt NTAPI xbox::KeWaitForMultipleObjects
|
|||
if ((ObjectMutant->Header.SignalState > 0) || (Thread == ObjectMutant->OwnerThread)) {
|
||||
if (ObjectMutant->Header.SignalState != MINLONG) {
|
||||
KiWaitSatisfyMutant(ObjectMutant, Thread);
|
||||
WaitStatus = (NTSTATUS)(Thread->WaitStatus);
|
||||
WaitStatus = (NTSTATUS)(Index | Thread->WaitStatus);
|
||||
goto NoWait;
|
||||
}
|
||||
else {
|
||||
|
@ -2004,7 +2091,7 @@ XBSYSAPI EXPORTNUM(158) xbox::ntstatus_xt NTAPI xbox::KeWaitForMultipleObjects
|
|||
else if (ObjectMutant->Header.SignalState) {
|
||||
// Otherwise, if the signal state is > 0, we can still just satisfy the wait
|
||||
KiWaitSatisfyOther(ObjectMutant);
|
||||
WaitStatus = X_STATUS_SUCCESS;
|
||||
WaitStatus = Index;
|
||||
goto NoWait;
|
||||
}
|
||||
} else {
|
||||
|
@ -2118,7 +2205,7 @@ XBSYSAPI EXPORTNUM(158) xbox::ntstatus_xt NTAPI xbox::KeWaitForMultipleObjects
|
|||
//WaitStatus = (NTSTATUS)KiSwapThread();
|
||||
|
||||
//if (WaitStatus == X_STATUS_USER_APC) {
|
||||
// TODO: KiDeliverUserApc();
|
||||
// KiExecuteUserApc();
|
||||
//}
|
||||
|
||||
// If the thread was not awakened for an APC, return the Wait Status
|
||||
|
@ -2149,7 +2236,7 @@ XBSYSAPI EXPORTNUM(158) xbox::ntstatus_xt NTAPI xbox::KeWaitForMultipleObjects
|
|||
// So unlock the dispatcher database, lower the IRQ and return the status
|
||||
KiUnlockDispatcherDatabase(Thread->WaitIrql);
|
||||
if (WaitStatus == X_STATUS_USER_APC) {
|
||||
//TODO: KiDeliverUserApc();
|
||||
KiExecuteUserApc();
|
||||
}
|
||||
|
||||
RETURN(WaitStatus);
|
||||
|
@ -2170,7 +2257,7 @@ NoWait:
|
|||
KiUnlockDispatcherDatabase(Thread->WaitIrql);
|
||||
|
||||
if (WaitStatus == X_STATUS_USER_APC) {
|
||||
// TODO: KiDeliverUserApc();
|
||||
KiExecuteUserApc();
|
||||
}
|
||||
|
||||
RETURN(WaitStatus);
|
||||
|
@ -2323,7 +2410,7 @@ XBSYSAPI EXPORTNUM(159) xbox::ntstatus_xt NTAPI xbox::KeWaitForSingleObject
|
|||
WaitStatus = (NTSTATUS)KiSwapThread();
|
||||
|
||||
if (WaitStatus == X_STATUS_USER_APC) {
|
||||
// TODO: KiDeliverUserApc();
|
||||
KiExecuteUserApc();
|
||||
}
|
||||
|
||||
// If the thread was not awakened for an APC, return the Wait Status
|
||||
|
@ -2354,7 +2441,7 @@ XBSYSAPI EXPORTNUM(159) xbox::ntstatus_xt NTAPI xbox::KeWaitForSingleObject
|
|||
// So unlock the dispatcher database, lower the IRQ and return the status
|
||||
KiUnlockDispatcherDatabase(Thread->WaitIrql);
|
||||
if (WaitStatus == X_STATUS_USER_APC) {
|
||||
//TODO: KiDeliverUserApc();
|
||||
KiExecuteUserApc();
|
||||
}
|
||||
|
||||
RETURN(WaitStatus);
|
||||
|
@ -2375,7 +2462,7 @@ NoWait:
|
|||
KiUnlockDispatcherDatabase(Thread->WaitIrql);
|
||||
|
||||
if (WaitStatus == X_STATUS_USER_APC) {
|
||||
// TODO: KiDeliverUserApc();
|
||||
KiExecuteUserApc();
|
||||
}
|
||||
|
||||
RETURN(WaitStatus);
|
||||
|
|
|
@ -27,14 +27,16 @@
|
|||
|
||||
namespace xbox
|
||||
{
|
||||
xbox::void_xt NTAPI KeSetSystemTime
|
||||
void_xt NTAPI KeSetSystemTime
|
||||
(
|
||||
IN PLARGE_INTEGER NewTime,
|
||||
OUT PLARGE_INTEGER OldTime
|
||||
);
|
||||
|
||||
xbox::void_xt NTAPI KeInitializeTimer
|
||||
void_xt NTAPI KeInitializeTimer
|
||||
(
|
||||
IN PKTIMER Timer
|
||||
);
|
||||
|
||||
void_xt KeEmptyQueueApc();
|
||||
}
|
||||
|
|
|
@ -80,6 +80,7 @@ the said software).
|
|||
|
||||
|
||||
#include <core\kernel\exports\xboxkrnl.h> // For KeBugCheck, etc.
|
||||
#include "core\kernel\support\EmuFS.h"
|
||||
#include "Logging.h" // For LOG_FUNC()
|
||||
#include "EmuKrnl.h" // for the list support functions
|
||||
#include "EmuKrnlKi.h"
|
||||
|
@ -88,22 +89,26 @@ the said software).
|
|||
|
||||
#define ASSERT_TIMER_LOCKED assert(KiTimerMtx.Acquired > 0)
|
||||
|
||||
xbox::KPROCESS KiUniqueProcess;
|
||||
const xbox::ulong_xt CLOCK_TIME_INCREMENT = 0x2710;
|
||||
xbox::KDPC KiTimerExpireDpc;
|
||||
xbox::KI_TIMER_LOCK KiTimerMtx;
|
||||
xbox::KTIMER_TABLE_ENTRY KiTimerTableListHead[TIMER_TABLE_SIZE];
|
||||
xbox::LIST_ENTRY KiWaitInListHead;
|
||||
std::mutex xbox::KiApcListMtx;
|
||||
|
||||
|
||||
xbox::void_xt xbox::KiInitSystem()
|
||||
{
|
||||
unsigned int i;
|
||||
KiUniqueProcess.StackCount = 0;
|
||||
KiUniqueProcess.ThreadQuantum = X_THREAD_QUANTUM;
|
||||
InitializeListHead(&KiUniqueProcess.ThreadListHead);
|
||||
|
||||
InitializeListHead(&KiWaitInListHead);
|
||||
|
||||
KiTimerMtx.Acquired = 0;
|
||||
KeInitializeDpc(&KiTimerExpireDpc, KiTimerExpiration, NULL);
|
||||
for (i = 0; i < TIMER_TABLE_SIZE; i++) {
|
||||
for (unsigned i = 0; i < TIMER_TABLE_SIZE; i++) {
|
||||
InitializeListHead(&KiTimerTableListHead[i].Entry);
|
||||
KiTimerTableListHead[i].Time.u.HighPart = 0xFFFFFFFF;
|
||||
KiTimerTableListHead[i].Time.u.LowPart = 0;
|
||||
|
@ -876,3 +881,67 @@ xbox::void_xt FASTCALL xbox::KiWaitSatisfyAll
|
|||
|
||||
return;
|
||||
}
|
||||
|
||||
template<xbox::MODE ApcMode>
|
||||
static xbox::void_xt KiExecuteApc()
|
||||
{
|
||||
xbox::PKTHREAD kThread = xbox::KeGetCurrentThread();
|
||||
|
||||
if constexpr (ApcMode == xbox::KernelMode) {
|
||||
kThread->ApcState.KernelApcPending = FALSE;
|
||||
}
|
||||
else {
|
||||
kThread->ApcState.UserApcPending = FALSE;
|
||||
}
|
||||
|
||||
// Even though the apc list is per-thread, it's still possible that another thread will access it while we are processing it below
|
||||
xbox::KiApcListMtx.lock();
|
||||
while (!IsListEmpty(&kThread->ApcState.ApcListHead[ApcMode])) {
|
||||
if ((ApcMode == xbox::KernelMode) && (kThread->KernelApcDisable != 0)) {
|
||||
xbox::KiApcListMtx.unlock();
|
||||
return;
|
||||
}
|
||||
xbox::PLIST_ENTRY Entry = kThread->ApcState.ApcListHead[ApcMode].Flink;
|
||||
xbox::PKAPC Apc = CONTAINING_RECORD(Entry, xbox::KAPC, ApcListEntry);
|
||||
RemoveEntryList(Entry);
|
||||
Apc->Inserted = FALSE;
|
||||
xbox::KiApcListMtx.unlock();
|
||||
|
||||
// NOTE: we never use KernelRoutine because that is only used for kernel APCs, which we currently don't use
|
||||
if (Apc->NormalRoutine != xbox::zeroptr) {
|
||||
(Apc->NormalRoutine)(Apc->NormalContext, Apc->SystemArgument1, Apc->SystemArgument2);
|
||||
}
|
||||
|
||||
xbox::ExFreePool(Apc);
|
||||
xbox::KiApcListMtx.lock();
|
||||
}
|
||||
|
||||
xbox::KiApcListMtx.unlock();
|
||||
}
|
||||
|
||||
xbox::void_xt xbox::KiExecuteKernelApc()
|
||||
{
|
||||
KiExecuteApc<KernelMode>();
|
||||
}
|
||||
|
||||
xbox::void_xt xbox::KiExecuteUserApc()
|
||||
{
|
||||
KiExecuteApc<UserMode>();
|
||||
}
|
||||
|
||||
xbox::PLARGE_INTEGER FASTCALL xbox::KiComputeWaitInterval
|
||||
(
|
||||
IN xbox::PLARGE_INTEGER OriginalTime,
|
||||
IN xbox::PLARGE_INTEGER DueTime,
|
||||
IN OUT xbox::PLARGE_INTEGER NewTime
|
||||
)
|
||||
{
|
||||
if (OriginalTime->QuadPart >= 0) {
|
||||
return OriginalTime;
|
||||
}
|
||||
else {
|
||||
NewTime->QuadPart = xbox::KeQueryInterruptTime();
|
||||
NewTime->QuadPart -= DueTime->QuadPart;
|
||||
return NewTime;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,14 +47,16 @@ namespace xbox
|
|||
int Acquired;
|
||||
} KI_TIMER_LOCK;
|
||||
|
||||
// NOTE: since the apc list is per-thread, we could also create a different mutex for each kthread
|
||||
extern std::mutex KiApcListMtx;
|
||||
|
||||
xbox::void_xt KiInitSystem();
|
||||
void_xt KiInitSystem();
|
||||
|
||||
xbox::void_xt KiTimerLock();
|
||||
void_xt KiTimerLock();
|
||||
|
||||
xbox::void_xt KiTimerUnlock();
|
||||
void_xt KiTimerUnlock();
|
||||
|
||||
xbox::void_xt KiClockIsr
|
||||
void_xt KiClockIsr
|
||||
(
|
||||
IN unsigned int ScalingFactor
|
||||
);
|
||||
|
@ -64,25 +66,25 @@ namespace xbox
|
|||
IN ULARGE_INTEGER CurrentTime
|
||||
);
|
||||
|
||||
xbox::void_xt KxInsertTimer
|
||||
void_xt KxInsertTimer
|
||||
(
|
||||
IN PKTIMER Timer,
|
||||
IN ulong_xt Hand
|
||||
);
|
||||
|
||||
xbox::void_xt FASTCALL KiCompleteTimer
|
||||
void_xt FASTCALL KiCompleteTimer
|
||||
(
|
||||
IN PKTIMER Timer,
|
||||
IN ulong_xt Hand
|
||||
);
|
||||
|
||||
xbox::void_xt KiRemoveEntryTimer
|
||||
void_xt KiRemoveEntryTimer
|
||||
(
|
||||
IN PKTIMER Timer,
|
||||
IN ulong_xt Hand
|
||||
);
|
||||
|
||||
xbox::void_xt KxRemoveTreeTimer
|
||||
void_xt KxRemoveTreeTimer
|
||||
(
|
||||
IN PKTIMER Timer
|
||||
);
|
||||
|
@ -99,7 +101,7 @@ namespace xbox
|
|||
IN LARGE_INTEGER Interval
|
||||
);
|
||||
|
||||
xbox::ulong_xt KiComputeTimerTableIndex
|
||||
ulong_xt KiComputeTimerTableIndex
|
||||
(
|
||||
IN ulonglong_xt Interval
|
||||
);
|
||||
|
@ -116,7 +118,7 @@ namespace xbox
|
|||
IN PKTIMER Timer
|
||||
);
|
||||
|
||||
xbox::void_xt NTAPI KiTimerExpiration
|
||||
void_xt NTAPI KiTimerExpiration
|
||||
(
|
||||
IN PKDPC Dpc,
|
||||
IN PVOID DeferredContext,
|
||||
|
@ -124,18 +126,29 @@ namespace xbox
|
|||
IN PVOID SystemArgument2
|
||||
);
|
||||
|
||||
xbox::void_xt FASTCALL KiTimerListExpire
|
||||
void_xt FASTCALL KiTimerListExpire
|
||||
(
|
||||
IN PLIST_ENTRY ExpiredListHead,
|
||||
IN KIRQL OldIrql
|
||||
);
|
||||
|
||||
xbox::void_xt FASTCALL KiWaitSatisfyAll
|
||||
void_xt FASTCALL KiWaitSatisfyAll
|
||||
(
|
||||
IN PKWAIT_BLOCK WaitBlock
|
||||
);
|
||||
|
||||
void_xt KiExecuteKernelApc();
|
||||
void_xt KiExecuteUserApc();
|
||||
|
||||
PLARGE_INTEGER FASTCALL KiComputeWaitInterval
|
||||
(
|
||||
IN PLARGE_INTEGER OriginalTime,
|
||||
IN PLARGE_INTEGER DueTime,
|
||||
IN OUT PLARGE_INTEGER NewTime
|
||||
);
|
||||
};
|
||||
|
||||
extern xbox::KPROCESS KiUniqueProcess;
|
||||
extern const xbox::ulong_xt CLOCK_TIME_INCREMENT;
|
||||
extern xbox::LIST_ENTRY KiWaitInListHead;
|
||||
extern xbox::KTIMER_TABLE_ENTRY KiTimerTableListHead[TIMER_TABLE_SIZE];
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
|
||||
|
||||
#include <core\kernel\exports\xboxkrnl.h> // For NtAllocateVirtualMemory, etc.
|
||||
#include "EmuKrnl.h"
|
||||
#include "Logging.h" // For LOG_FUNC()
|
||||
#include "EmuKrnlLogging.h"
|
||||
|
||||
|
@ -41,6 +42,7 @@ namespace NtDll
|
|||
|
||||
#include "core\kernel\init\CxbxKrnl.h" // For CxbxrKrnlAbort
|
||||
#include "core\kernel\exports\EmuKrnlKe.h"
|
||||
#include "EmuKrnlKi.h"
|
||||
#include "core\kernel\support\Emu.h" // For EmuLog(LOG_LEVEL::WARNING, )
|
||||
#include "core\kernel\support\EmuFile.h" // For EmuNtSymbolicLinkObject, NtStatusToString(), etc.
|
||||
#include "core\kernel\memory-manager\VMManager.h" // For g_VMManager
|
||||
|
@ -55,12 +57,9 @@ namespace NtDll
|
|||
#include <unordered_map>
|
||||
#include <mutex>
|
||||
|
||||
// Used to keep track of duplicate handles created by NtQueueApcThread()
|
||||
std::unordered_map<HANDLE, HANDLE> g_DuplicateHandles;
|
||||
// Prevent setting the system time from multiple threads at the same time
|
||||
std::mutex NtSystemTimeMtx;
|
||||
|
||||
|
||||
// ******************************************************************
|
||||
// * 0x00B8 - NtAllocateVirtualMemory()
|
||||
// ******************************************************************
|
||||
|
@ -173,15 +172,6 @@ XBSYSAPI EXPORTNUM(187) xbox::ntstatus_xt NTAPI xbox::NtClose
|
|||
if (GetHandleInformation(Handle, &flags) != 0) {
|
||||
// This was a native handle, call NtDll::NtClose
|
||||
ret = NtDll::NtClose(Handle);
|
||||
|
||||
// Delete duplicate threads created by our implementation of NtQueueApcThread()
|
||||
if (GetHandleInformation(g_DuplicateHandles[Handle], &flags) != 0)
|
||||
{
|
||||
EmuLog(LOG_LEVEL::DEBUG, "Closing duplicate handle...");
|
||||
|
||||
CloseHandle(g_DuplicateHandles[Handle]);
|
||||
g_DuplicateHandles.erase(Handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -719,18 +709,18 @@ XBSYSAPI EXPORTNUM(197) xbox::ntstatus_xt NTAPI xbox::NtDuplicateObject
|
|||
// On the xbox, the duplicated handle always has the same access rigths of the source handle
|
||||
const ACCESS_MASK DesiredAccess = 0;
|
||||
const ULONG Attributes = 0;
|
||||
dword_xt nativeOptions = (Options | DUPLICATE_SAME_ATTRIBUTES | DUPLICATE_SAME_ACCESS);
|
||||
const ULONG nativeOptions = (Options | DUPLICATE_SAME_ATTRIBUTES | DUPLICATE_SAME_ACCESS);
|
||||
|
||||
if (const auto &nativeHandle = GetNativeHandle(SourceHandle)) {
|
||||
// This was a handle created by Ob
|
||||
PVOID Object;
|
||||
status = ObReferenceObjectByHandle(SourceHandle, /*ObjectType=*/nullptr, &Object);
|
||||
status = ObReferenceObjectByHandle(SourceHandle, zeroptr, &Object);
|
||||
if (X_NT_SUCCESS(status)) {
|
||||
if (ObpIsFlagSet(Options, DUPLICATE_CLOSE_SOURCE)) {
|
||||
NtClose(SourceHandle);
|
||||
}
|
||||
|
||||
status = ObOpenObjectByPointer(Object, OBJECT_TO_OBJECT_HEADER(Object)->Type, /*OUT*/TargetHandle);
|
||||
status = ObOpenObjectByPointer(Object, OBJECT_TO_OBJECT_HEADER(Object)->Type, TargetHandle);
|
||||
if (!X_NT_SUCCESS(status)) {
|
||||
*TargetHandle = NULL;
|
||||
RETURN(status);
|
||||
|
@ -1028,7 +1018,7 @@ XBSYSAPI EXPORTNUM(206) xbox::ntstatus_xt NTAPI xbox::NtQueueApcThread
|
|||
IN PIO_APC_ROUTINE ApcRoutine,
|
||||
IN PVOID ApcRoutineContext OPTIONAL,
|
||||
IN PIO_STATUS_BLOCK ApcStatusBlock OPTIONAL,
|
||||
IN ulong_xt ApcReserved OPTIONAL
|
||||
IN PVOID ApcReserved OPTIONAL
|
||||
)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
|
@ -1039,54 +1029,29 @@ XBSYSAPI EXPORTNUM(206) xbox::ntstatus_xt NTAPI xbox::NtQueueApcThread
|
|||
LOG_FUNC_ARG(ApcReserved)
|
||||
LOG_FUNC_END;
|
||||
|
||||
// In order for NtQueueApcThread or QueueUserAPC to work, you must... I repeat...
|
||||
// YOU MUST duplicate the handle with the appropriate permissions first! So far,
|
||||
// the only game that I know of using this is Metal Slug 3, and it won't launch
|
||||
// without it. Other SNK games might use it also, beware.
|
||||
// Test case: Metal Slug 3, possibly other SNK games too
|
||||
|
||||
// TODO: Use our implementation of NtDuplicateObject instead?
|
||||
PETHREAD Thread;
|
||||
ntstatus_xt result = ObReferenceObjectByHandle(ThreadHandle, &PsThreadObjectType, reinterpret_cast<PVOID *>(&Thread));
|
||||
if (!X_NT_SUCCESS(result)) {
|
||||
RETURN(result);
|
||||
}
|
||||
|
||||
HANDLE hApcThread = NULL;
|
||||
|
||||
// Just to be safe, let's see if the appropriate permissions are even set for the
|
||||
// target thread first...
|
||||
|
||||
NTSTATUS ret = NtDll::NtQueueApcThread(
|
||||
(NtDll::HANDLE)ThreadHandle,
|
||||
(NtDll::PIO_APC_ROUTINE)ApcRoutine,
|
||||
ApcRoutineContext,
|
||||
(NtDll::PIO_STATUS_BLOCK)ApcStatusBlock,
|
||||
ApcReserved);
|
||||
|
||||
if( FAILED( ret ) )
|
||||
{
|
||||
EmuLog(LOG_LEVEL::WARNING, "Duplicating handle with THREAD_SET_CONTEXT..." );
|
||||
|
||||
// If we get here, then attempt to duplicate the thread.
|
||||
if(!DuplicateHandle(g_CurrentProcessHandle, ThreadHandle, g_CurrentProcessHandle, &hApcThread, THREAD_SET_CONTEXT,FALSE,0))
|
||||
EmuLog(LOG_LEVEL::WARNING, "DuplicateHandle failed!");
|
||||
else
|
||||
{
|
||||
g_DuplicateHandles[ThreadHandle] = hApcThread; // Save this thread because we'll need to de-reference it later
|
||||
EmuLog(LOG_LEVEL::DEBUG, "DuplicateHandle returned 0x%X (ThreadId 0x%.4X)", hApcThread, GetThreadId( hApcThread ) );
|
||||
PKAPC Apc = static_cast<PKAPC>(ExAllocatePoolWithTag(sizeof(KAPC), 'pasP'));
|
||||
if (Apc != zeroptr) {
|
||||
KeInitializeApc(Apc, &Thread->Tcb, zeroptr, zeroptr, reinterpret_cast<PKNORMAL_ROUTINE>(ApcRoutine), UserMode, ApcRoutineContext);
|
||||
if (!KeInsertQueueApc(Apc, ApcStatusBlock, ApcReserved, 0)) {
|
||||
ExFreePool(Apc);
|
||||
result = X_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
|
||||
ret = NtDll::NtQueueApcThread(
|
||||
(NtDll::HANDLE)hApcThread,
|
||||
(NtDll::PIO_APC_ROUTINE)ApcRoutine,
|
||||
ApcRoutineContext,
|
||||
(NtDll::PIO_STATUS_BLOCK)ApcStatusBlock,
|
||||
ApcReserved);
|
||||
}
|
||||
if (FAILED(ret))
|
||||
{
|
||||
EmuLog(LOG_LEVEL::WARNING, "NtQueueApcThread failed!");
|
||||
CloseHandle( g_DuplicateHandles[ThreadHandle] );
|
||||
g_DuplicateHandles.erase( ThreadHandle );
|
||||
else {
|
||||
result = X_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
RETURN(ret);
|
||||
ObfDereferenceObject(Thread);
|
||||
|
||||
RETURN(result);
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
|
@ -1159,8 +1124,8 @@ XBSYSAPI EXPORTNUM(207) xbox::ntstatus_xt NTAPI xbox::NtQueryDirectoryFile
|
|||
ret = NtDll::NtQueryDirectoryFile(
|
||||
FileHandle,
|
||||
Event,
|
||||
(NtDll::PIO_APC_ROUTINE)ApcRoutine,
|
||||
ApcContext,
|
||||
(NtDll::PIO_APC_ROUTINE)ApcRoutine,
|
||||
ApcContext,
|
||||
(NtDll::IO_STATUS_BLOCK*)IoStatusBlock,
|
||||
/*FileInformation=*/NtFileDirInfo,
|
||||
NtFileDirectoryInformationSize + NtPathBufferSize,
|
||||
|
@ -1891,15 +1856,15 @@ XBSYSAPI EXPORTNUM(224) xbox::ntstatus_xt NTAPI xbox::NtResumeThread
|
|||
LOG_FUNC_ARG_OUT(PreviousSuspendCount)
|
||||
LOG_FUNC_END;
|
||||
|
||||
NTSTATUS ret = NtDll::NtResumeThread(
|
||||
ThreadHandle,
|
||||
(::PULONG)PreviousSuspendCount);
|
||||
if (const auto &nativeHandle = GetNativeHandle(ThreadHandle)) {
|
||||
// Thread handles are created by ob
|
||||
RETURN(NtDll::NtResumeThread(*nativeHandle, (::PULONG)PreviousSuspendCount));
|
||||
}
|
||||
else {
|
||||
RETURN(X_STATUS_INVALID_HANDLE);
|
||||
}
|
||||
|
||||
// TODO : Once we do our own thread-switching, implement NtResumeThread using KetResumeThread
|
||||
|
||||
//Sleep(10);
|
||||
|
||||
RETURN(ret);
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
|
@ -2114,13 +2079,15 @@ XBSYSAPI EXPORTNUM(231) xbox::ntstatus_xt NTAPI xbox::NtSuspendThread
|
|||
LOG_FUNC_ARG_OUT(PreviousSuspendCount)
|
||||
LOG_FUNC_END;
|
||||
|
||||
NTSTATUS ret = NtDll::NtSuspendThread(
|
||||
ThreadHandle,
|
||||
(::PULONG)PreviousSuspendCount);
|
||||
if (const auto &nativeHandle = GetNativeHandle(ThreadHandle)) {
|
||||
// Thread handles are created by ob
|
||||
RETURN(NtDll::NtSuspendThread(*nativeHandle, (::PULONG)PreviousSuspendCount));
|
||||
}
|
||||
else {
|
||||
RETURN(X_STATUS_INVALID_HANDLE);
|
||||
}
|
||||
|
||||
// TODO : Once we do our own thread-switching, implement NtSuspendThread using KeSuspendThread
|
||||
|
||||
RETURN(ret);
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
|
@ -2223,12 +2190,52 @@ XBSYSAPI EXPORTNUM(235) xbox::ntstatus_xt NTAPI xbox::NtWaitForMultipleObjectsEx
|
|||
LOG_FUNC_ARG(Timeout)
|
||||
LOG_FUNC_END;
|
||||
|
||||
return NtDll::NtWaitForMultipleObjects(
|
||||
Count,
|
||||
Handles,
|
||||
(NtDll::OBJECT_WAIT_TYPE)WaitType,
|
||||
Alertable,
|
||||
(NtDll::PLARGE_INTEGER)Timeout);
|
||||
if (!Count || (Count > X_MAXIMUM_WAIT_OBJECTS)) {
|
||||
RETURN(X_STATUS_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
// This function can wait on thread handles, which are currently created by ob,
|
||||
// so we need to check their presence in the handle array
|
||||
::HANDLE nativeHandles[X_MAXIMUM_WAIT_OBJECTS];
|
||||
for (ulong_xt i = 0; i < Count; ++i) {
|
||||
if (const auto &nativeHandle = GetNativeHandle(Handles[i])) {
|
||||
// This is a ob handle, so replace it with its native counterpart
|
||||
nativeHandles[i] = *nativeHandle;
|
||||
}
|
||||
else {
|
||||
nativeHandles[i] = Handles[i];
|
||||
}
|
||||
}
|
||||
|
||||
// Because user APCs from NtQueueApcThread are now handled by the kernel, we need to wait for them ourselves
|
||||
LARGE_INTEGER NewTime;
|
||||
PLARGE_INTEGER pNewTime;
|
||||
if (Timeout) {
|
||||
LARGE_INTEGER ExpireTime, DueTime;
|
||||
ExpireTime.QuadPart = DueTime.QuadPart = Timeout->QuadPart;
|
||||
pNewTime = KiComputeWaitInterval(&ExpireTime, &DueTime, &NewTime);
|
||||
}
|
||||
else {
|
||||
NewTime.QuadPart = ~0ull;
|
||||
pNewTime = &NewTime;
|
||||
}
|
||||
|
||||
xbox::ntstatus_xt ret = WaitApc([Count, &nativeHandles, WaitType, Alertable]() -> std::optional<ntstatus_xt> {
|
||||
NtDll::LARGE_INTEGER ExpireTime;
|
||||
ExpireTime.QuadPart = 0;
|
||||
NTSTATUS Status = NtDll::NtWaitForMultipleObjects(
|
||||
Count,
|
||||
nativeHandles,
|
||||
(NtDll::OBJECT_WAIT_TYPE)WaitType,
|
||||
Alertable,
|
||||
&ExpireTime);
|
||||
if (Status == STATUS_TIMEOUT) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return std::make_optional<ntstatus_xt>(Status);
|
||||
}, pNewTime, Alertable, WaitMode);
|
||||
|
||||
RETURN(ret);
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
|
|
|
@ -1019,7 +1019,7 @@ XBSYSAPI EXPORTNUM(246) xbox::ntstatus_xt NTAPI xbox::ObReferenceObjectByHandle
|
|||
|
||||
if (Handle == NtCurrentThread()) {
|
||||
if ((ObjectType == &PsThreadObjectType) || (ObjectType == NULL)) {
|
||||
Object = PsGetCurrentThread();
|
||||
Object = PspGetCurrentThread();
|
||||
ObjectHeader = OBJECT_TO_OBJECT_HEADER(Object);
|
||||
InterlockedIncrement((::PLONG)(&ObjectHeader->PointerCount));
|
||||
*ReturnedObject = Object;
|
||||
|
|
|
@ -31,14 +31,18 @@
|
|||
|
||||
|
||||
#include <core\kernel\exports\xboxkrnl.h> // For PsCreateSystemThreadEx, etc.
|
||||
#include "core\kernel\exports\EmuKrnlKi.h"
|
||||
#include "core\kernel\exports\EmuKrnlKe.h"
|
||||
#include <process.h> // For __beginthreadex(), etc.
|
||||
#include <float.h> // For _controlfp constants
|
||||
|
||||
#include "Logging.h" // For LOG_FUNC()
|
||||
#include "EmuKrnlLogging.h"
|
||||
#include "core\kernel\init\CxbxKrnl.h" // For CxbxKrnl_TLS
|
||||
#include "EmuKrnl.h"
|
||||
#include "core\kernel\support\Emu.h" // For EmuLog(LOG_LEVEL::WARNING, )
|
||||
#include "core\kernel\support\EmuFS.h" // For EmuGenerateFS
|
||||
#include "core\kernel\support\NativeHandle.h"
|
||||
|
||||
// prevent name collisions
|
||||
namespace NtDll
|
||||
|
@ -46,46 +50,39 @@ namespace NtDll
|
|||
#include "core\kernel\support\EmuNtDll.h"
|
||||
};
|
||||
|
||||
#define PSP_MAX_CREATE_THREAD_NOTIFY 16 /* TODO : Should be 8 */
|
||||
#define PSP_MAX_CREATE_THREAD_NOTIFY 8
|
||||
|
||||
// PsCreateSystemThread proxy parameters
|
||||
typedef struct _PCSTProxyParam
|
||||
{
|
||||
IN PVOID StartRoutine;
|
||||
IN PVOID StartContext;
|
||||
IN PVOID SystemRoutine;
|
||||
IN xbox::PVOID StartRoutine;
|
||||
IN xbox::PVOID StartContext;
|
||||
IN xbox::PVOID SystemRoutine;
|
||||
IN xbox::PVOID Ethread;
|
||||
}
|
||||
PCSTProxyParam;
|
||||
|
||||
// Global Variable(s)
|
||||
extern PVOID g_pfnThreadNotification[PSP_MAX_CREATE_THREAD_NOTIFY] = { NULL };
|
||||
extern int g_iThreadNotificationCount = 0;
|
||||
static xbox::PCREATE_THREAD_NOTIFY_ROUTINE g_pfnThreadNotification[PSP_MAX_CREATE_THREAD_NOTIFY] = { xbox::zeroptr };
|
||||
static std::atomic_int g_iThreadNotificationCount = 0;
|
||||
static std::mutex g_ThreadNotificationMtx;
|
||||
|
||||
// Separate function for logging, otherwise in PCSTProxy __try wont work (Compiler Error C2712)
|
||||
void LOG_PCSTProxy
|
||||
(
|
||||
PVOID StartRoutine,
|
||||
PVOID StartContext,
|
||||
PVOID SystemRoutine
|
||||
xbox::PVOID StartRoutine,
|
||||
xbox::PVOID StartContext,
|
||||
xbox::PVOID SystemRoutine,
|
||||
xbox::PVOID Ethread
|
||||
)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(StartRoutine)
|
||||
LOG_FUNC_ARG(StartContext)
|
||||
LOG_FUNC_ARG(SystemRoutine)
|
||||
LOG_FUNC_ARG(Ethread)
|
||||
LOG_FUNC_END;
|
||||
}
|
||||
|
||||
// Overload which doesn't change affinity
|
||||
void InitXboxThread()
|
||||
{
|
||||
// initialize FS segment selector
|
||||
EmuGenerateFS(CxbxKrnl_TLS, CxbxKrnl_TLSData);
|
||||
|
||||
_controlfp(_PC_53, _MCW_PC); // Set Precision control to 53 bits (verified setting)
|
||||
_controlfp(_RC_NEAR, _MCW_RC); // Set Rounding control to near (unsure about this)
|
||||
}
|
||||
|
||||
// PsCreateSystemThread proxy procedure
|
||||
// Dxbx Note : The signature of PCSTProxy should conform to System.TThreadFunc !
|
||||
static unsigned int WINAPI PCSTProxy
|
||||
|
@ -104,11 +101,11 @@ static unsigned int WINAPI PCSTProxy
|
|||
LOG_PCSTProxy(
|
||||
params.StartRoutine,
|
||||
params.StartContext,
|
||||
params.SystemRoutine);
|
||||
|
||||
params.SystemRoutine,
|
||||
params.Ethread);
|
||||
|
||||
// Do minimal thread initialization
|
||||
InitXboxThread();
|
||||
EmuGenerateFS(CxbxKrnl_TLS, CxbxKrnl_TLSData, static_cast<xbox::PETHREAD>(params.Ethread));
|
||||
|
||||
auto routine = (xbox::PKSYSTEM_ROUTINE)params.SystemRoutine;
|
||||
// Debugging notice : When the below line shows up with an Exception dialog and a
|
||||
|
@ -137,13 +134,30 @@ void PspSystemThreadStartup
|
|||
xbox::PsTerminateSystemThread(X_STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
xbox::PETHREAD xbox::PspGetCurrentThread()
|
||||
{
|
||||
// This works because we assign ethread to Prcb->CurrentThread
|
||||
return reinterpret_cast<PETHREAD>(KeGetCurrentThread());
|
||||
}
|
||||
|
||||
static xbox::void_xt PspCallThreadNotificationRoutines(xbox::PETHREAD eThread, xbox::boolean_xt Create)
|
||||
{
|
||||
std::unique_lock lck(g_ThreadNotificationMtx);
|
||||
for (int i = 0; i < PSP_MAX_CREATE_THREAD_NOTIFY; i++) {
|
||||
if (g_pfnThreadNotification[i]) {
|
||||
EmuLog(LOG_LEVEL::DEBUG, "Calling pfnNotificationRoutine[%d] (0x%.8X)", i, g_pfnThreadNotification[i]);
|
||||
(*g_pfnThreadNotification[i])(eThread, eThread->UniqueThread, Create);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
// * 0x00FE - PsCreateSystemThread()
|
||||
// ******************************************************************
|
||||
XBSYSAPI EXPORTNUM(254) xbox::ntstatus_xt NTAPI xbox::PsCreateSystemThread
|
||||
(
|
||||
OUT PHANDLE ThreadHandle,
|
||||
OUT PDWORD ThreadId OPTIONAL,
|
||||
OUT PHANDLE ThreadId OPTIONAL,
|
||||
IN PKSTART_ROUTINE StartRoutine,
|
||||
IN PVOID StartContext,
|
||||
IN boolean_xt DebuggerThread
|
||||
|
@ -187,7 +201,7 @@ XBSYSAPI EXPORTNUM(255) xbox::ntstatus_xt NTAPI xbox::PsCreateSystemThreadEx
|
|||
IN ulong_xt ThreadExtensionSize,
|
||||
IN ulong_xt KernelStackSize,
|
||||
IN ulong_xt TlsDataSize,
|
||||
OUT PDWORD ThreadId OPTIONAL,
|
||||
OUT PHANDLE ThreadId OPTIONAL,
|
||||
IN PKSTART_ROUTINE StartRoutine,
|
||||
IN PVOID StartContext,
|
||||
IN boolean_xt CreateSuspended,
|
||||
|
@ -222,58 +236,75 @@ XBSYSAPI EXPORTNUM(255) xbox::ntstatus_xt NTAPI xbox::PsCreateSystemThreadEx
|
|||
|
||||
// create thread, using our special proxy technique
|
||||
{
|
||||
DWORD dwThreadId = 0;
|
||||
PETHREAD eThread;
|
||||
ntstatus_xt result = ObCreateObject(&PsThreadObjectType, zeroptr, sizeof(ETHREAD) + ThreadExtensionSize, reinterpret_cast<PVOID *>(&eThread));
|
||||
if (!X_NT_SUCCESS(result)) {
|
||||
RETURN(result);
|
||||
}
|
||||
|
||||
std::memset(eThread, 0, sizeof(ETHREAD) + ThreadExtensionSize);
|
||||
|
||||
// The ob handle of the ethread obj is the thread id we return to the title
|
||||
result = ObInsertObject(eThread, zeroptr, 0, &eThread->UniqueThread);
|
||||
if (!X_NT_SUCCESS(result)) {
|
||||
ObfDereferenceObject(eThread);
|
||||
RETURN(result);
|
||||
}
|
||||
|
||||
if (g_iThreadNotificationCount) {
|
||||
PspCallThreadNotificationRoutines(eThread, TRUE);
|
||||
}
|
||||
|
||||
// Create another handle to pass back to the title in the ThreadHandle argument
|
||||
result = ObOpenObjectByPointer(eThread, &PsThreadObjectType, ThreadHandle);
|
||||
if (!X_NT_SUCCESS(result)) {
|
||||
ObfDereferenceObject(eThread);
|
||||
RETURN(result);
|
||||
}
|
||||
|
||||
if (ThreadId != zeroptr) {
|
||||
*ThreadId = eThread->UniqueThread;
|
||||
}
|
||||
|
||||
// PCSTProxy is responsible for cleaning up this pointer
|
||||
PCSTProxyParam *iPCSTProxyParam = new PCSTProxyParam;
|
||||
|
||||
iPCSTProxyParam->StartRoutine = (PVOID)StartRoutine;
|
||||
iPCSTProxyParam->StartContext = StartContext;
|
||||
iPCSTProxyParam->SystemRoutine = (PVOID)SystemRoutine; // NULL, XapiThreadStartup or unknown?
|
||||
iPCSTProxyParam->Ethread = eThread;
|
||||
|
||||
/*
|
||||
// call thread notification routine(s)
|
||||
if (g_iThreadNotificationCount != 0)
|
||||
{
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
// TODO: This is *very* wrong, ps notification routines are NOT the same as XApi notification routines
|
||||
// TODO: XAPI notification routines are already handeld by XapiThreadStartup and don't need to be called by us
|
||||
// TODO: This type of notification routine is PCREATE_THREAD_NOTIFY_ROUTINE, which takes an ETHREAD pointer as well as Thread ID as input
|
||||
// TODO: This is impossible to support currently, as we do not create or register Xbox ETHREAD objects, so we're better to skip it entirely!
|
||||
xbox::XTHREAD_NOTIFY_PROC pfnNotificationRoutine = (xbox::XTHREAD_NOTIFY_PROC)g_pfnThreadNotification[i];
|
||||
|
||||
// If the routine doesn't exist, don't execute it!
|
||||
if (pfnNotificationRoutine == NULL)
|
||||
continue;
|
||||
|
||||
EmuLog(LOG_LEVEL::DEBUG, "Calling pfnNotificationRoutine[%d] (0x%.8X)", g_iThreadNotificationCount, pfnNotificationRoutine);
|
||||
|
||||
pfnNotificationRoutine(TRUE);
|
||||
}
|
||||
}*/
|
||||
|
||||
HANDLE handle = reinterpret_cast<HANDLE>(_beginthreadex(NULL, KernelStackSize, PCSTProxy, iPCSTProxyParam, CREATE_SUSPENDED, reinterpret_cast<unsigned int*>(&dwThreadId)));
|
||||
unsigned int ThreadId;
|
||||
HANDLE handle = reinterpret_cast<HANDLE>(_beginthreadex(NULL, KernelStackSize, PCSTProxy, iPCSTProxyParam, CREATE_SUSPENDED, &ThreadId));
|
||||
if (handle == NULL) {
|
||||
delete iPCSTProxyParam;
|
||||
ObpClose(eThread->UniqueThread);
|
||||
ObfDereferenceObject(eThread);
|
||||
RETURN(X_STATUS_INSUFFICIENT_RESOURCES);
|
||||
}
|
||||
*ThreadHandle = handle;
|
||||
if (ThreadId != NULL)
|
||||
*ThreadId = dwThreadId;
|
||||
|
||||
// Increment the ref count of the thread once more. This is to guard against the case the title closes the thread handle
|
||||
// before this thread terminates with PsTerminateSystemThread
|
||||
// Test case: Amped
|
||||
ObfReferenceObject(eThread);
|
||||
|
||||
KeQuerySystemTime(&eThread->CreateTime);
|
||||
InsertTailList(&KiUniqueProcess.ThreadListHead, &eThread->Tcb.ThreadListEntry);
|
||||
KiUniqueProcess.StackCount++;
|
||||
RegisterXboxHandle(*ThreadHandle, handle);
|
||||
HANDLE dupHandle = OpenThread(THREAD_ALL_ACCESS, FALSE, ThreadId);
|
||||
assert(dupHandle);
|
||||
RegisterXboxHandle(eThread->UniqueThread, dupHandle);
|
||||
|
||||
g_AffinityPolicy->SetAffinityXbox(handle);
|
||||
CxbxKrnlRegisterThread(handle);
|
||||
|
||||
// Now that ThreadId is populated and affinity is changed, resume the thread (unless the guest passed CREATE_SUSPENDED)
|
||||
if (!CreateSuspended) {
|
||||
ResumeThread(handle);
|
||||
}
|
||||
|
||||
// Note : DO NOT use iPCSTProxyParam anymore, since ownership is transferred to the proxy (which frees it too)
|
||||
|
||||
// Log ThreadID identical to how GetCurrentThreadID() is rendered :
|
||||
EmuLog(LOG_LEVEL::DEBUG, "Created Xbox proxy thread. Handle : 0x%X, ThreadId : [0x%.4X]", handle, dwThreadId);
|
||||
EmuLog(LOG_LEVEL::DEBUG, "Created Xbox proxy thread. Handle : 0x%X, ThreadId : [0x%.4X], Native Handle : 0x%X, Native ThreadId : [0x%.4X]",
|
||||
*ThreadHandle, eThread->UniqueThread, handle, ThreadId);
|
||||
}
|
||||
|
||||
RETURN(X_STATUS_SUCCESS);
|
||||
|
@ -289,14 +320,13 @@ XBSYSAPI EXPORTNUM(256) xbox::ntstatus_xt NTAPI xbox::PsQueryStatistics
|
|||
{
|
||||
LOG_FUNC_ONE_ARG_OUT(ProcessStatistics);
|
||||
|
||||
NTSTATUS ret = X_STATUS_SUCCESS;
|
||||
ntstatus_xt ret = X_STATUS_SUCCESS;
|
||||
|
||||
if (ProcessStatistics->Length == sizeof(PS_STATISTICS)) {
|
||||
LOG_INCOMPLETE(); // TODO : Return number of threads and handles that currently exist
|
||||
ProcessStatistics->ThreadCount = 1;
|
||||
ProcessStatistics->HandleCount = 1;
|
||||
ProcessStatistics->ThreadCount = KiUniqueProcess.StackCount;
|
||||
ProcessStatistics->HandleCount = ObpObjectHandleTable.HandleCount; // This currently doesn't count native handles that we use as xbox handles
|
||||
} else {
|
||||
ret = STATUS_INVALID_PARAMETER;
|
||||
ret = X_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
RETURN(ret);
|
||||
|
@ -312,29 +342,16 @@ XBSYSAPI EXPORTNUM(257) xbox::ntstatus_xt NTAPI xbox::PsSetCreateThreadNotifyRou
|
|||
{
|
||||
LOG_FUNC_ONE_ARG(NotifyRoutine);
|
||||
|
||||
NTSTATUS ret = X_STATUS_INSUFFICIENT_RESOURCES;
|
||||
|
||||
// Taken from xbox::EmuXRegisterThreadNotifyRoutine (perhaps that can be removed now) :
|
||||
|
||||
// I honestly don't expect this to happen, but if it does...
|
||||
if (g_iThreadNotificationCount >= PSP_MAX_CREATE_THREAD_NOTIFY)
|
||||
CxbxrKrnlAbort("Too many thread notification routines installed\n");
|
||||
|
||||
// Find an empty spot in the thread notification array
|
||||
for (int i = 0; i < PSP_MAX_CREATE_THREAD_NOTIFY; i++)
|
||||
{
|
||||
// If we find one, then add it to the array, and break the loop so
|
||||
// that we don't accidently register the same routine twice!
|
||||
if (g_pfnThreadNotification[i] == NULL)
|
||||
{
|
||||
g_pfnThreadNotification[i] = (PVOID)NotifyRoutine;
|
||||
std::unique_lock lck(g_ThreadNotificationMtx);
|
||||
for (int i = 0; i < PSP_MAX_CREATE_THREAD_NOTIFY; i++) {
|
||||
if (g_pfnThreadNotification[i] == zeroptr) {
|
||||
g_pfnThreadNotification[i] = NotifyRoutine;
|
||||
g_iThreadNotificationCount++;
|
||||
ret = X_STATUS_SUCCESS;
|
||||
break;
|
||||
RETURN(X_STATUS_SUCCESS);
|
||||
}
|
||||
}
|
||||
|
||||
RETURN(ret);
|
||||
RETURN(X_STATUS_INSUFFICIENT_RESOURCES);
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
|
@ -350,34 +367,23 @@ XBSYSAPI EXPORTNUM(258) xbox::void_xt NTAPI xbox::PsTerminateSystemThread
|
|||
{
|
||||
LOG_FUNC_ONE_ARG(ExitStatus);
|
||||
|
||||
/*
|
||||
// call thread notification routine(s)
|
||||
if (g_iThreadNotificationCount != 0)
|
||||
{
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
xbox::XTHREAD_NOTIFY_PROC pfnNotificationRoutine = (xbox::XTHREAD_NOTIFY_PROC)g_pfnThreadNotification[i];
|
||||
xbox::PETHREAD eThread = xbox::PspGetCurrentThread();
|
||||
if (eThread->UniqueThread && g_iThreadNotificationCount) {
|
||||
PspCallThreadNotificationRoutines(eThread, FALSE);
|
||||
}
|
||||
|
||||
// If the routine doesn't exist, don't execute it!
|
||||
if (pfnNotificationRoutine == NULL)
|
||||
continue;
|
||||
EmuKeFreeThread(ExitStatus);
|
||||
// Don't do this in EmuKeFreeThread because we only increment the thread ref count in PsCreateSystemThreadEx
|
||||
ObfDereferenceObject(eThread);
|
||||
KiUniqueProcess.StackCount--;
|
||||
|
||||
EmuLog(LOG_LEVEL::DEBUG, "Calling pfnNotificationRoutine[%d] (0x%.8X)", g_iThreadNotificationCount, pfnNotificationRoutine);
|
||||
|
||||
pfnNotificationRoutine(FALSE);
|
||||
}
|
||||
}*/
|
||||
|
||||
EmuKeFreePcr();
|
||||
_endthreadex(ExitStatus);
|
||||
// ExitThread(ExitStatus);
|
||||
// CxbxKrnlTerminateThread();
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
// * 0x0103 - PsThreadObjectType
|
||||
// ******************************************************************
|
||||
XBSYSAPI EXPORTNUM(259) xbox::OBJECT_TYPE VOLATILE xbox::PsThreadObjectType =
|
||||
XBSYSAPI EXPORTNUM(259) xbox::OBJECT_TYPE xbox::PsThreadObjectType =
|
||||
{
|
||||
xbox::ExAllocatePoolWithTag,
|
||||
xbox::ExFreePool,
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
#include "EmuEEPROM.h" // For CxbxRestoreEEPROM, EEPROM, XboxFactoryGameRegion
|
||||
#include "core\kernel\exports\EmuKrnl.h"
|
||||
#include "core\kernel\exports\EmuKrnlKi.h"
|
||||
#include "core\kernel\exports\EmuKrnlKe.h"
|
||||
#include "EmuShared.h"
|
||||
#include "core\hle\D3D8\Direct3D9\Direct3D9.h" // For CxbxInitWindow, EmuD3DInit
|
||||
#include "core\hle\DSOUND\DirectSound\DirectSound.hpp" // For CxbxInitAudio
|
||||
|
@ -71,6 +72,8 @@
|
|||
#include "common\crypto\EmuSha.h" // For the SHA1 functions
|
||||
#include "Timer.h" // For Timer_Init
|
||||
#include "common\input\InputManager.h" // For the InputDeviceManager
|
||||
#include "core/kernel/support/NativeHandle.h"
|
||||
#include "common/win32/Util.h" // for WinError2Str
|
||||
|
||||
#include "common/FilePaths.hpp"
|
||||
|
||||
|
@ -93,9 +96,6 @@ DebugMode CxbxrKrnl_DebugMode = DebugMode::DM_NONE;
|
|||
std::string CxbxrKrnl_DebugFileName = "";
|
||||
Xbe::Certificate *g_pCertificate = NULL;
|
||||
|
||||
/*! thread handles */
|
||||
static std::vector<HANDLE> g_hThreads;
|
||||
|
||||
char szFilePath_CxbxReloaded_Exe[MAX_PATH] = { 0 };
|
||||
std::string g_DataFilePath;
|
||||
char szFilePath_EEPROM_bin[MAX_PATH] = { 0 };
|
||||
|
@ -141,9 +141,11 @@ void SetupPerTitleKeys()
|
|||
|
||||
}
|
||||
|
||||
void CxbxLaunchXbe(void(*Entry)())
|
||||
xbox::void_xt NTAPI CxbxLaunchXbe(xbox::PVOID Entry)
|
||||
{
|
||||
Entry();
|
||||
EmuLogInit(LOG_LEVEL::DEBUG, "Calling XBE entry point...");
|
||||
static_cast<void(*)()>(Entry)();
|
||||
EmuLogInit(LOG_LEVEL::DEBUG, "XBE entry point returned");
|
||||
}
|
||||
|
||||
// Entry point address XOR keys per Xbe type (Retail, Debug or Chihiro) :
|
||||
|
@ -334,37 +336,28 @@ void InitSoftwareInterrupts()
|
|||
}
|
||||
#endif
|
||||
|
||||
void TriggerPendingConnectedInterrupts()
|
||||
{
|
||||
for (int i = 0; i < MAX_BUS_INTERRUPT_LEVEL; i++) {
|
||||
// If the interrupt is pending and connected, process it
|
||||
if (HalSystemInterrupts[i].IsPending() && EmuInterruptList[i] && EmuInterruptList[i]->Connected) {
|
||||
HalSystemInterrupts[i].Trigger(EmuInterruptList[i]);
|
||||
}
|
||||
SwitchToThread();
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned int WINAPI CxbxKrnlInterruptThread(PVOID param)
|
||||
static xbox::void_xt NTAPI CxbxKrnlInterruptThread(xbox::PVOID param)
|
||||
{
|
||||
CxbxSetThreadName("CxbxKrnl Interrupts");
|
||||
|
||||
// Make sure Xbox1 code runs on one core :
|
||||
InitXboxThread();
|
||||
g_AffinityPolicy->SetAffinityXbox();
|
||||
|
||||
#if 0
|
||||
InitSoftwareInterrupts();
|
||||
#endif
|
||||
|
||||
std::mutex m;
|
||||
std::unique_lock<std::mutex> lock(m);
|
||||
while (true) {
|
||||
if (g_bEnableAllInterrupts) {
|
||||
TriggerPendingConnectedInterrupts();
|
||||
for (int i = 0; i < MAX_BUS_INTERRUPT_LEVEL; i++) {
|
||||
// If the interrupt is pending and connected, process it
|
||||
if (HalSystemInterrupts[i].IsPending() && EmuInterruptList[i] && EmuInterruptList[i]->Connected) {
|
||||
HalSystemInterrupts[i].Trigger(EmuInterruptList[i]);
|
||||
}
|
||||
}
|
||||
Sleep(1);
|
||||
g_InterruptSignal.wait(lock, []() { return g_AnyInterruptAsserted.load() && g_bEnableAllInterrupts.load(); });
|
||||
g_AnyInterruptAsserted = false;
|
||||
}
|
||||
|
||||
return 0;
|
||||
assert(0);
|
||||
}
|
||||
|
||||
static void CxbxKrnlClockThread(void* pVoid)
|
||||
|
@ -706,7 +699,6 @@ static bool CxbxrKrnlXbeSystemSelector(int BootFlags, unsigned& reserved_systems
|
|||
// Launch Segaboot
|
||||
CxbxLaunchNewXbe(chihiroSegaBootNew);
|
||||
CxbxKrnlShutDown(true);
|
||||
TerminateProcess(GetCurrentProcess(), EXIT_SUCCESS);
|
||||
|
||||
}
|
||||
#endif // Chihiro wip block
|
||||
|
@ -1405,13 +1397,19 @@ static void CxbxrKrnlInitHacks()
|
|||
|
||||
CxbxrLogDumpXbeInfo(pLibraryVersion);
|
||||
|
||||
CxbxKrnlRegisterThread(GetCurrentThread());
|
||||
|
||||
// Make sure the Xbox1 code runs on one core (as the box itself has only 1 CPU,
|
||||
// this will better aproximate the environment with regard to multi-threading) :
|
||||
EmuLogInit(LOG_LEVEL::DEBUG, "Determining CPU affinity.");
|
||||
g_AffinityPolicy = AffinityPolicy::InitPolicy();
|
||||
|
||||
// Create a kpcr for this thread. This is necessary because ObInitSystem needs to access the irql. This must also be done before
|
||||
// CxbxInitWindow because that function creates the xbox EmuUpdateTickCount thread
|
||||
EmuGenerateFS<true>(nullptr, nullptr, xbox::zeroptr);
|
||||
if (!xbox::ObInitSystem()) {
|
||||
CxbxrKrnlAbortEx(LOG_PREFIX_INIT, "Unable to intialize ObInitSystem.");
|
||||
}
|
||||
xbox::KiInitSystem();
|
||||
|
||||
// initialize graphics
|
||||
EmuLogInit(LOG_LEVEL::DEBUG, "Initializing render window.");
|
||||
CxbxInitWindow(true);
|
||||
|
@ -1492,13 +1490,6 @@ static void CxbxrKrnlInitHacks()
|
|||
|
||||
EmuInitFS();
|
||||
|
||||
InitXboxThread();
|
||||
g_AffinityPolicy->SetAffinityXbox();
|
||||
if (!xbox::ObInitSystem()) {
|
||||
CxbxrKrnlAbortEx(LOG_PREFIX_INIT, "Unable to intialize ObInitSystem.");
|
||||
}
|
||||
xbox::KiInitSystem();
|
||||
|
||||
#ifdef CHIHIRO_WORK
|
||||
// If this title is Chihiro, Setup JVS
|
||||
if (g_bIsChihiro) {
|
||||
|
@ -1508,25 +1499,21 @@ static void CxbxrKrnlInitHacks()
|
|||
|
||||
EmuX86_Init();
|
||||
// Create the interrupt processing thread
|
||||
DWORD dwThreadId;
|
||||
HANDLE hThread = (HANDLE)_beginthreadex(NULL, NULL, CxbxKrnlInterruptThread, NULL, NULL, (unsigned int*)&dwThreadId);
|
||||
xbox::HANDLE hThread;
|
||||
xbox::PsCreateSystemThread(&hThread, xbox::zeroptr, CxbxKrnlInterruptThread, xbox::zeroptr, FALSE);
|
||||
// Start the kernel clock thread
|
||||
TimerObject* KernelClockThr = Timer_Create(CxbxKrnlClockThread, nullptr, "Kernel clock thread", false);
|
||||
TimerObject* KernelClockThr = Timer_Create(CxbxKrnlClockThread, nullptr, "Kernel clock thread", true);
|
||||
Timer_Start(KernelClockThr, SCALE_MS_IN_NS);
|
||||
|
||||
EmuLogInit(LOG_LEVEL::DEBUG, "Calling XBE entry point...");
|
||||
CxbxLaunchXbe(Entry);
|
||||
xbox::PsCreateSystemThread(&hThread, xbox::zeroptr, CxbxLaunchXbe, Entry, FALSE);
|
||||
|
||||
// FIXME: Wait for Cxbx to exit or error fatally
|
||||
Sleep(INFINITE);
|
||||
EmuKeFreePcr<true>();
|
||||
|
||||
EmuLogInit(LOG_LEVEL::DEBUG, "XBE entry point returned");
|
||||
fflush(stdout);
|
||||
|
||||
CxbxUnlockFilePath();
|
||||
|
||||
// EmuShared::Cleanup(); FIXME: commenting this line is a bad workaround for issue #617 (https://github.com/Cxbx-Reloaded/Cxbx-Reloaded/issues/617)
|
||||
CxbxKrnlTerminateThread();
|
||||
// This will wait forever
|
||||
std::condition_variable cv;
|
||||
std::mutex m;
|
||||
std::unique_lock<std::mutex> lock(m);
|
||||
cv.wait(lock, [] { return false; });
|
||||
}
|
||||
|
||||
// REMARK: the following is useless, but PatrickvL has asked to keep it for documentation purposes
|
||||
|
@ -1544,8 +1531,6 @@ static void CxbxrKrnlInitHacks()
|
|||
{
|
||||
g_bEmuException = true;
|
||||
|
||||
CxbxKrnlResume();
|
||||
|
||||
// print out error message (if exists)
|
||||
if(szErrorMessage != NULL)
|
||||
{
|
||||
|
@ -1575,65 +1560,50 @@ static void CxbxrKrnlInitHacks()
|
|||
CxbxKrnlShutDown();
|
||||
}
|
||||
|
||||
void CxbxKrnlRegisterThread(HANDLE hThread)
|
||||
void CxbxrKrnlSuspendThreads()
|
||||
{
|
||||
// we must duplicate this handle in order to retain Suspend/Resume thread rights from a remote thread
|
||||
{
|
||||
HANDLE hDupHandle = NULL;
|
||||
xbox::PLIST_ENTRY ThreadListEntry = KiUniqueProcess.ThreadListHead.Flink;
|
||||
std::vector<HANDLE> threads;
|
||||
threads.reserve(KiUniqueProcess.StackCount);
|
||||
|
||||
if (DuplicateHandle(g_CurrentProcessHandle, hThread, g_CurrentProcessHandle, &hDupHandle, 0, FALSE, DUPLICATE_SAME_ACCESS)) {
|
||||
hThread = hDupHandle; // Thread handle was duplicated, continue registration with the duplicate
|
||||
}
|
||||
else {
|
||||
auto message = CxbxGetLastErrorString("DuplicateHandle");
|
||||
EmuLog(LOG_LEVEL::WARNING, message.c_str());
|
||||
}
|
||||
// Don't use EmuKeGetPcr because that asserts kpcr
|
||||
xbox::KPCR* Pcr = reinterpret_cast<xbox::PKPCR>(__readfsdword(TIB_ArbitraryDataSlot));
|
||||
|
||||
// If there's nothing in list entry, skip this step.
|
||||
if (!ThreadListEntry) {
|
||||
return;
|
||||
}
|
||||
|
||||
g_hThreads.push_back(hThread);
|
||||
}
|
||||
|
||||
void CxbxKrnlSuspend()
|
||||
{
|
||||
if(g_bEmuSuspended || g_bEmuException)
|
||||
return;
|
||||
|
||||
for (auto it = g_hThreads.begin(); it != g_hThreads.end(); ++it)
|
||||
{
|
||||
DWORD dwExitCode;
|
||||
|
||||
if(GetExitCodeThread(*it, &dwExitCode) && dwExitCode == STILL_ACTIVE) {
|
||||
// suspend thread if it is active
|
||||
SuspendThread(*it);
|
||||
} else {
|
||||
// remove thread from thread list if it is dead
|
||||
g_hThreads.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
g_bEmuSuspended = true;
|
||||
}
|
||||
|
||||
void CxbxKrnlResume()
|
||||
{
|
||||
if(!g_bEmuSuspended)
|
||||
return;
|
||||
|
||||
for (auto it = g_hThreads.begin(); it != g_hThreads.end(); ++it)
|
||||
{
|
||||
DWORD dwExitCode;
|
||||
|
||||
if (GetExitCodeThread(*it, &dwExitCode) && dwExitCode == STILL_ACTIVE) {
|
||||
// resume thread if it is active
|
||||
ResumeThread(*it);
|
||||
}
|
||||
else {
|
||||
// remove thread from thread list if it is dead
|
||||
g_hThreads.erase(it);
|
||||
while (ThreadListEntry != &KiUniqueProcess.ThreadListHead) {
|
||||
xbox::HANDLE UniqueThread = CONTAINING_RECORD(ThreadListEntry, xbox::ETHREAD, Tcb.ThreadListEntry)->UniqueThread;
|
||||
if (UniqueThread) {
|
||||
// Current thread is an xbox thread
|
||||
if (Pcr) {
|
||||
const auto& nHandle = GetNativeHandle<true>(UniqueThread);
|
||||
if (nHandle) {
|
||||
// We do not want to suspend current thread, so we let it skip this one.
|
||||
if (*nHandle != NtCurrentThread()) {
|
||||
threads.push_back(*nHandle);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Otherwise, convert all UniqueThread to host thead handles.
|
||||
else {
|
||||
const auto& nHandle = GetNativeHandle<false>(UniqueThread);
|
||||
if (nHandle) {
|
||||
threads.push_back(*nHandle);
|
||||
}
|
||||
}
|
||||
}
|
||||
ThreadListEntry = ThreadListEntry->Flink;
|
||||
}
|
||||
|
||||
g_bEmuSuspended = false;
|
||||
for (const auto& thread : threads) {
|
||||
DWORD PrevCount = SuspendThread(thread);
|
||||
if (PrevCount == -1) {
|
||||
EmuLog(LOG_LEVEL::ERROR2, "Unable to suspend thread 0x%X for: %s", thread, WinError2Str().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CxbxKrnlShutDown(bool is_reboot)
|
||||
|
@ -1656,15 +1626,23 @@ void CxbxKrnlShutDown(bool is_reboot)
|
|||
g_io_mu_metadata = nullptr;
|
||||
}
|
||||
|
||||
// Shutdown the memory manager
|
||||
g_VMManager.Shutdown();
|
||||
|
||||
// Shutdown the render manager
|
||||
if (g_renderbase != nullptr) {
|
||||
g_renderbase->Shutdown();
|
||||
g_renderbase = nullptr;
|
||||
}
|
||||
|
||||
// This is very important process to prevent false positive report and allow IDEs to continue debug multiple reboots.
|
||||
CxbxrKrnlSuspendThreads();
|
||||
|
||||
// NOTE: Require to be after g_renderbase's shutdown process.
|
||||
// Next thing we need to do is shutdown our timer threads.
|
||||
Timer_Shutdown();
|
||||
|
||||
// NOTE: Must be last step of shutdown process and before CxbxUnlockFilePath call!
|
||||
// Shutdown the memory manager
|
||||
g_VMManager.Shutdown();
|
||||
|
||||
CxbxUnlockFilePath();
|
||||
|
||||
if (CxbxKrnl_hEmuParent != NULL && !is_reboot) {
|
||||
|
@ -1766,11 +1744,6 @@ void CxbxPrintUEMInfo(ULONG ErrorCode)
|
|||
}
|
||||
}
|
||||
|
||||
[[noreturn]] void CxbxKrnlTerminateThread()
|
||||
{
|
||||
TerminateThread(GetCurrentThread(), 0);
|
||||
}
|
||||
|
||||
void CxbxKrnlPanic()
|
||||
{
|
||||
CxbxrKrnlAbort("Kernel Panic!");
|
||||
|
|
|
@ -151,17 +151,8 @@ void CxbxKrnlEmulate(unsigned int system, blocks_reserved_t blocks_reserved);
|
|||
|
||||
#define CxbxrKrnlAbort(fmt, ...) CxbxrKrnlAbortEx(LOG_PREFIX, fmt, ##__VA_ARGS__)
|
||||
|
||||
/*! register a thread handle */
|
||||
void CxbxKrnlRegisterThread(HANDLE hThread);
|
||||
|
||||
/*! suspend emulation */
|
||||
void CxbxKrnlSuspend();
|
||||
|
||||
/*! resume emulation */
|
||||
void CxbxKrnlResume();
|
||||
|
||||
/*! terminate gracefully the emulation */
|
||||
void CxbxKrnlShutDown(bool is_reboot = false);
|
||||
[[noreturn]] void CxbxKrnlShutDown(bool is_reboot = false);
|
||||
|
||||
/*! display the fatal error message*/
|
||||
void CxbxKrnlPrintUEM(ULONG ErrorCode);
|
||||
|
@ -169,9 +160,6 @@ void CxbxKrnlPrintUEM(ULONG ErrorCode);
|
|||
/*! display the cause of the fatal error message*/
|
||||
void CxbxPrintUEMInfo(ULONG ErrorCode);
|
||||
|
||||
/*! terminate the calling thread */
|
||||
[[noreturn]] void CxbxKrnlTerminateThread();
|
||||
|
||||
/*! kernel panic (trap for unimplemented kernel functions) */
|
||||
void CxbxKrnlPanic();
|
||||
|
||||
|
@ -193,8 +181,6 @@ extern ULONG g_CxbxFatalErrorCode;
|
|||
|
||||
extern size_t g_SystemMaxMemory;
|
||||
|
||||
void InitXboxThread();
|
||||
|
||||
/*! thread local storage structure */
|
||||
extern Xbe::TLS *CxbxKrnl_TLS;
|
||||
|
||||
|
|
|
@ -45,7 +45,6 @@ CRITICAL_SECTION dbgCritical;
|
|||
// Global Variable(s)
|
||||
volatile thread_local bool g_bEmuException = false;
|
||||
static thread_local bool bOverrideEmuException;
|
||||
volatile bool g_bEmuSuspended = false;
|
||||
volatile bool g_bPrintfOn = true;
|
||||
bool g_DisablePixelShaders = false;
|
||||
bool g_UseAllCores = false;
|
||||
|
|
|
@ -58,7 +58,6 @@ void EmuPrintStackTrace(PCONTEXT ContextRecord);
|
|||
|
||||
// global flags specifying current emulation state
|
||||
extern volatile thread_local bool g_bEmuException;
|
||||
extern volatile bool g_bEmuSuspended;
|
||||
|
||||
// global exception patching address
|
||||
extern void * funcExclude[2048];
|
||||
|
@ -67,10 +66,6 @@ extern HWND g_hEmuWindow;
|
|||
|
||||
#define GET_FRONT_WINDOW_HANDLE ((CxbxKrnl_hEmuParent != nullptr) ? CxbxKrnl_hEmuParent : g_hEmuWindow)
|
||||
|
||||
// thread notification routine
|
||||
extern PVOID g_pfnThreadNotification[16];
|
||||
extern int g_iThreadNotificationCount;
|
||||
|
||||
extern HANDLE g_CurrentProcessHandle; // Set in CxbxKrnlMain
|
||||
|
||||
// Delta added to host SystemTime, used in KiClockIsr and KeSetSystemTime
|
||||
|
|
|
@ -31,7 +31,9 @@
|
|||
#include <core\kernel\exports\xboxkrnl.h>
|
||||
#include "core\kernel\exports\EmuKrnl.h" // For InitializeListHead(), etc.
|
||||
#include "core\kernel\exports\EmuKrnlKe.h"
|
||||
#include "core\kernel\exports\EmuKrnlKi.h"
|
||||
#include "core\kernel\support\EmuFS.h" // For fs_instruction_t
|
||||
#include "core\kernel\support\NativeHandle.h"
|
||||
#include "core\kernel\init\CxbxKrnl.h"
|
||||
#include "Logging.h"
|
||||
|
||||
|
@ -43,6 +45,8 @@
|
|||
#undef RtlZeroMemory
|
||||
#endif
|
||||
|
||||
#define TLS_ALIGNMENT_OFFSET 12
|
||||
|
||||
// NT_TIB (Thread Information Block) offsets - see https://www.microsoft.com/msj/archive/S2CE.aspx
|
||||
#define TIB_ExceptionList offsetof(NT_TIB, ExceptionList) // = 0x00/0
|
||||
#define TIB_StackBase offsetof(NT_TIB, StackBase) // = 0x04/4
|
||||
|
@ -111,14 +115,16 @@
|
|||
// = 0x104/260 */ LIST_ENTRY ThreadListEntry;
|
||||
// = 0x10C/268 */ UCHAR _padding[4];
|
||||
|
||||
template void EmuGenerateFS<true>(Xbe::TLS *pTLS, void *pTLSData, xbox::PETHREAD Ethread);
|
||||
template void EmuGenerateFS<false>(Xbe::TLS *pTLS, void *pTLSData, xbox::PETHREAD Ethread);
|
||||
template void EmuKeFreePcr<true>();
|
||||
template void EmuKeFreePcr<false>();
|
||||
|
||||
NT_TIB *GetNtTib()
|
||||
{
|
||||
return (NT_TIB *)__readfsdword(TIB_LinearSelfAddress);
|
||||
}
|
||||
|
||||
|
||||
xbox::KPCR* WINAPI KeGetPcr();
|
||||
|
||||
uint32_t fs_lock = 0;
|
||||
|
||||
__declspec(naked) void LockFS()
|
||||
|
@ -160,7 +166,7 @@ __declspec(naked) void UnlockFS()
|
|||
|
||||
void EmuKeSetPcr(xbox::KPCR *Pcr)
|
||||
{
|
||||
// Store the Xbox KPCR pointer in FS (See KeGetPcr())
|
||||
// Store the Xbox KPCR pointer in FS (See EmuKeGetPcr())
|
||||
//
|
||||
// Note : Cxbx currently doesn't do preemptive thread switching,
|
||||
// which implies that thread-state management is done by Windows.
|
||||
|
@ -188,43 +194,65 @@ void EmuKeSetPcr(xbox::KPCR *Pcr)
|
|||
__writefsdword(TIB_ArbitraryDataSlot, (DWORD)Pcr);
|
||||
}
|
||||
|
||||
template<bool IsHostThread>
|
||||
void EmuKeFreePcr()
|
||||
{
|
||||
// NOTE: don't call KeGetPcr because that one creates a new pcr for the thread when __readfsdword returns nullptr, which we don't want
|
||||
xbox::PKPCR Pcr = reinterpret_cast<xbox::PKPCR>(__readfsdword(TIB_ArbitraryDataSlot));
|
||||
|
||||
if (Pcr) {
|
||||
// tls can be nullptr
|
||||
xbox::PVOID Dummy;
|
||||
xbox::ulong_xt Size;
|
||||
xbox::ntstatus_xt Status;
|
||||
if (Pcr->NtTib.StackBase) {
|
||||
// NOTE: the tls pointer was increased by 12 bytes to enforce the 16 bytes alignment, so adjust it to reach the correct pointer
|
||||
// that was allocated by xbox::NtAllocateVirtualMemory
|
||||
Dummy = static_cast<xbox::PBYTE>(Pcr->NtTib.StackBase) - 12;
|
||||
Size = xbox::zero;
|
||||
Status = xbox::NtFreeVirtualMemory(&Dummy, &Size, XBOX_MEM_RELEASE); // free tls
|
||||
assert(Status == X_STATUS_SUCCESS);
|
||||
}
|
||||
xbox::PKPCR Pcr = EmuKeGetPcr();
|
||||
xbox::PVOID Dummy;
|
||||
xbox::ulong_xt Size;
|
||||
xbox::ntstatus_xt Status;
|
||||
// tls can be nullptr
|
||||
if (Pcr->NtTib.StackBase) {
|
||||
// NOTE: the tls pointer was increased by 12 bytes to enforce the 16 bytes alignment, so adjust it to reach the correct pointer
|
||||
// that was allocated by xbox::NtAllocateVirtualMemory
|
||||
Dummy = static_cast<xbox::PBYTE>(Pcr->NtTib.StackBase) - TLS_ALIGNMENT_OFFSET;
|
||||
Size = xbox::zero;
|
||||
Status = xbox::NtFreeVirtualMemory(&Dummy, &Size, XBOX_MEM_RELEASE); // free tls
|
||||
assert(Status == X_STATUS_SUCCESS);
|
||||
}
|
||||
if constexpr (IsHostThread) {
|
||||
// This only happens for the kernel initialization thread of cxbxr
|
||||
Dummy = Pcr->Prcb->CurrentThread;
|
||||
Size = xbox::zero;
|
||||
Status = xbox::NtFreeVirtualMemory(&Dummy, &Size, XBOX_MEM_RELEASE); // free ethread
|
||||
assert(Status == X_STATUS_SUCCESS);
|
||||
Dummy = Pcr;
|
||||
Size = xbox::zero;
|
||||
Status = xbox::NtFreeVirtualMemory(&Dummy, &Size, XBOX_MEM_RELEASE); // free pcr
|
||||
assert(Status == X_STATUS_SUCCESS);
|
||||
__writefsdword(TIB_ArbitraryDataSlot, NULL);
|
||||
}
|
||||
else {
|
||||
EmuLog(LOG_LEVEL::WARNING, "__readfsdword in EmuKeFreePcr returned nullptr: was this called from a non-xbox thread?");
|
||||
Dummy = Pcr;
|
||||
Size = xbox::zero;
|
||||
Status = xbox::NtFreeVirtualMemory(&Dummy, &Size, XBOX_MEM_RELEASE); // free pcr
|
||||
assert(Status == X_STATUS_SUCCESS);
|
||||
__writefsdword(TIB_ArbitraryDataSlot, NULL);
|
||||
}
|
||||
|
||||
void EmuKeFreeThread(xbox::ntstatus_xt ExitStatus)
|
||||
{
|
||||
// Free all kernel resources that were allocated fo this thread
|
||||
|
||||
xbox::KeEmptyQueueApc();
|
||||
|
||||
xbox::PETHREAD eThread = xbox::PspGetCurrentThread();
|
||||
|
||||
xbox::KeQuerySystemTime(&eThread->ExitTime);
|
||||
eThread->Tcb.HasTerminated = 1;
|
||||
|
||||
RemoveEntryList(&eThread->Tcb.ThreadListEntry);
|
||||
|
||||
// Emulate our exit strategy for GetExitCodeThread
|
||||
eThread->ExitStatus = ExitStatus;
|
||||
eThread->Tcb.Header.SignalState = 1;
|
||||
|
||||
if (GetNativeHandle(eThread->UniqueThread)) {
|
||||
xbox::NtClose(eThread->UniqueThread);
|
||||
eThread->UniqueThread = xbox::zero;
|
||||
}
|
||||
|
||||
EmuKeFreePcr();
|
||||
}
|
||||
|
||||
__declspec(naked) void EmuFS_RefreshKPCR()
|
||||
{
|
||||
// Backup all registers, call KeGetPcr and then restore all registers
|
||||
// KeGetPcr makes sure a valid KPCR exists for the current thread
|
||||
// Backup all registers, call EmuKeGetPcr and then restore all registers
|
||||
// EmuKeGetPcr makes sure a valid KPCR exists for the current thread
|
||||
// and creates it if missing, we backup and restore all registers
|
||||
// to keep it safe to call in our patches
|
||||
// This function can be later expanded to do nice things
|
||||
|
@ -232,7 +260,7 @@ __declspec(naked) void EmuFS_RefreshKPCR()
|
|||
__asm {
|
||||
pushfd
|
||||
pushad
|
||||
call KeGetPcr
|
||||
call EmuKeGetPcr
|
||||
popad
|
||||
popfd
|
||||
ret
|
||||
|
@ -652,7 +680,8 @@ void EmuInitFS()
|
|||
}
|
||||
|
||||
// generate fs segment selector
|
||||
void EmuGenerateFS(Xbe::TLS *pTLS, void *pTLSData)
|
||||
template<bool IsHostThread>
|
||||
void EmuGenerateFS(Xbe::TLS *pTLS, void *pTLSData, xbox::PETHREAD Ethread)
|
||||
{
|
||||
void *pNewTLS = nullptr;
|
||||
xbox::PVOID base;
|
||||
|
@ -682,7 +711,7 @@ void EmuGenerateFS(Xbe::TLS *pTLS, void *pTLSData)
|
|||
pNewTLS = (void*)base;
|
||||
xbox::RtlZeroMemory(pNewTLS, dwCopySize + dwZeroSize + 0x100 + 0xC);
|
||||
/* Skip the first 12 bytes so that TLSData will be 16 byte aligned (addr returned by NtAllocateVirtualMemory is 4K aligned) */
|
||||
pNewTLS = (uint8_t*)pNewTLS + 12;
|
||||
pNewTLS = (uint8_t*)pNewTLS + TLS_ALIGNMENT_OFFSET;
|
||||
|
||||
if (dwCopySize > 0) {
|
||||
memcpy((uint8_t*)pNewTLS + 4, pTLSData, dwCopySize);
|
||||
|
@ -779,18 +808,29 @@ void EmuGenerateFS(Xbe::TLS *pTLS, void *pTLSData)
|
|||
NewPcr->Irql = PASSIVE_LEVEL; // See KeLowerIrql;
|
||||
}
|
||||
|
||||
// Initialize a fake PrcbData.CurrentThread
|
||||
{
|
||||
if constexpr (IsHostThread) {
|
||||
// This only happens for the kernel initialization thread of cxbxr
|
||||
assert(Ethread == xbox::zeroptr);
|
||||
|
||||
base = xbox::zeroptr;
|
||||
size = sizeof(xbox::ETHREAD);
|
||||
xbox::NtAllocateVirtualMemory(&base, 0, &size, XBOX_MEM_RESERVE | XBOX_MEM_COMMIT, XBOX_PAGE_READWRITE);
|
||||
xbox::ETHREAD *EThread = (xbox::ETHREAD*)base; // Clear, to prevent side-effects on random contents
|
||||
xbox::RtlZeroMemory(EThread, sizeof(xbox::ETHREAD));
|
||||
Ethread = (xbox::PETHREAD)base;
|
||||
xbox::RtlZeroMemory(Ethread, sizeof(xbox::ETHREAD)); // Clear, to prevent side-effects on random contents
|
||||
}
|
||||
|
||||
EThread->Tcb.TlsData = pNewTLS;
|
||||
EThread->UniqueThread = GetCurrentThreadId();
|
||||
// Initialize a fake PrcbData.CurrentThread
|
||||
{
|
||||
// Set PrcbData.CurrentThread
|
||||
Prcb->CurrentThread = (xbox::KTHREAD*)EThread;
|
||||
Prcb->CurrentThread = (xbox::PKTHREAD)Ethread;
|
||||
Prcb->CurrentThread->TlsData = pNewTLS;
|
||||
// Initialize APC stuff
|
||||
InitializeListHead(&Prcb->CurrentThread->ApcState.ApcListHead[xbox::KernelMode]);
|
||||
InitializeListHead(&Prcb->CurrentThread->ApcState.ApcListHead[xbox::UserMode]);
|
||||
Prcb->CurrentThread->KernelApcDisable = 0;
|
||||
Prcb->CurrentThread->ApcState.ApcQueueable = TRUE;
|
||||
Prcb->CurrentThread->ApcState.Process = &KiUniqueProcess;
|
||||
Prcb->CurrentThread->ApcState.Process->ThreadQuantum = KiUniqueProcess.ThreadQuantum;
|
||||
// Initialize the thread header and its wait list
|
||||
Prcb->CurrentThread->Header.Type = xbox::ThreadObject;
|
||||
Prcb->CurrentThread->Header.Size = sizeof(xbox::KTHREAD) / sizeof(xbox::long_xt);
|
||||
|
@ -806,8 +846,11 @@ void EmuGenerateFS(Xbe::TLS *pTLS, void *pTLSData)
|
|||
WaitBlock->WaitListEntry.Blink = &Prcb->CurrentThread->Timer.Header.WaitListHead;
|
||||
}
|
||||
|
||||
// Make the KPCR struct available to KeGetPcr()
|
||||
// Make the KPCR struct available to EmuKeGetPcr()
|
||||
EmuKeSetPcr(NewPcr);
|
||||
|
||||
EmuLog(LOG_LEVEL::DEBUG, "Installed KPCR in TIB_ArbitraryDataSlot (with pTLS = 0x%.8X)", pTLS);
|
||||
|
||||
_controlfp(_PC_53, _MCW_PC); // Set Precision control to 53 bits (verified setting)
|
||||
_controlfp(_RC_NEAR, _MCW_RC); // Set Rounding control to near (unsure about this)
|
||||
}
|
||||
|
|
|
@ -33,14 +33,26 @@
|
|||
extern void EmuInitFS();
|
||||
|
||||
// generate fs segment selector
|
||||
extern void EmuGenerateFS(Xbe::TLS *pTLS, void *pTLSData);
|
||||
template<bool IsHostThread = false>
|
||||
void EmuGenerateFS(Xbe::TLS *pTLS, void *pTLSData, xbox::PETHREAD Ethread);
|
||||
// free resources allocated for the thread
|
||||
void EmuKeFreeThread(xbox::ntstatus_xt ExitStatus = X_STATUS_ABANDONED);
|
||||
// free kpcr allocated for the thread
|
||||
template<bool IsHostThread = false>
|
||||
void EmuKeFreePcr();
|
||||
|
||||
void EmuKeSetPcr(xbox::KPCR *Pcr);
|
||||
xbox::KPCR *_stdcall EmuKeGetPcr();
|
||||
|
||||
typedef struct
|
||||
{
|
||||
std::vector<uint8_t> data;
|
||||
void* functionPtr;
|
||||
}fs_instruction_t;
|
||||
|
||||
extern template void EmuGenerateFS<true>(Xbe::TLS *pTLS, void *pTLSData, xbox::PETHREAD Ethread);
|
||||
extern template void EmuGenerateFS<false>(Xbe::TLS *pTLS, void *pTLSData, xbox::PETHREAD Ethread);
|
||||
extern template void EmuKeFreePcr<true>();
|
||||
extern template void EmuKeFreePcr<false>();
|
||||
|
||||
#endif
|
||||
|
|
|
@ -31,6 +31,8 @@
|
|||
#include "Windows.h"
|
||||
#include "assert.h"
|
||||
#include "NativeHandle.h"
|
||||
#include "core\kernel\init\CxbxKrnl.h"
|
||||
#include "core/kernel/support/EmuFS.h"
|
||||
|
||||
|
||||
std::unordered_map<xbox::HANDLE, HANDLE> g_RegisteredHandles;
|
||||
|
@ -39,9 +41,28 @@ std::shared_mutex g_MapMtx;
|
|||
void RegisterXboxHandle(xbox::HANDLE xhandle, HANDLE nhandle)
|
||||
{
|
||||
std::unique_lock<std::shared_mutex> lck(g_MapMtx);
|
||||
[[maybe_unused]] const auto &ret = g_RegisteredHandles.emplace(xhandle, nhandle);
|
||||
// Even when duplicating xbox handles with NtDuplicateObject, the duplicate will still be different then the source handle
|
||||
assert(ret.second == true);
|
||||
auto ret = g_RegisteredHandles.try_emplace(xhandle, nhandle);
|
||||
if (ret.second == false) {
|
||||
// This can happen when an ob handle has been destroyed, but then a thread switch happens before the first thread
|
||||
// got a chance to remove the old handle from g_RegisteredHandles with RemoveXboxHandle
|
||||
// Test case: dashboard
|
||||
auto now = std::chrono::system_clock::now();
|
||||
auto timeout = now + std::chrono::milliseconds(2000);
|
||||
while (now <= timeout) {
|
||||
lck.unlock();
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(5));
|
||||
lck.lock();
|
||||
ret = g_RegisteredHandles.try_emplace(xhandle, nhandle);
|
||||
if (ret.second) {
|
||||
return;
|
||||
}
|
||||
now += std::chrono::milliseconds(5);
|
||||
}
|
||||
|
||||
// If we reach here, it means that we could not insert the handle after more than two seconds of trying. This probably means
|
||||
// that we have forgotten to call RemoveXboxHandle on the old handle, or the other thread is waiting/deadlocked, so this is a bug
|
||||
CxbxrKrnlAbortEx(CXBXR_MODULE::CXBXR, "Failed to register new xbox handle after more than two seconds!");
|
||||
}
|
||||
}
|
||||
|
||||
void RemoveXboxHandle(xbox::HANDLE xhandle)
|
||||
|
@ -51,8 +72,29 @@ void RemoveXboxHandle(xbox::HANDLE xhandle)
|
|||
assert(ret == 1);
|
||||
}
|
||||
|
||||
template<bool NoConversion>
|
||||
std::optional<HANDLE> GetNativeHandle(xbox::HANDLE xhandle)
|
||||
{
|
||||
// If SourceHandle is -2 = NtCurrentThread, then we are searching the handle of this thread
|
||||
// Test case: Metal Slug 3
|
||||
if (xhandle == NtCurrentThread()) {
|
||||
// Only used for threads as Windows doesn't allow non-special handle for same thread.
|
||||
if (NoConversion) {
|
||||
return xhandle;
|
||||
}
|
||||
else {
|
||||
xhandle = xbox::PspGetCurrentThread()->UniqueThread;
|
||||
}
|
||||
}
|
||||
// If xhandle is not special handle, check if it's the same as current thread.
|
||||
// Only used for threads as Windows doesn't allow non-special handle for same thread.
|
||||
// This will only triggered within kernel functions i.e. KeSetDisableBoostThread and KeSetBasePriorityThread.
|
||||
else if (NoConversion) {
|
||||
if (xhandle == xbox::PspGetCurrentThread()->UniqueThread) {
|
||||
return NtCurrentThread();
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_lock<std::shared_mutex> lck(g_MapMtx);
|
||||
auto &it = g_RegisteredHandles.find(xhandle);
|
||||
if (it == g_RegisteredHandles.end()) {
|
||||
|
@ -62,3 +104,6 @@ std::optional<HANDLE> GetNativeHandle(xbox::HANDLE xhandle)
|
|||
return it->second;
|
||||
}
|
||||
}
|
||||
|
||||
template std::optional<HANDLE> GetNativeHandle<true>(xbox::HANDLE xhandle);
|
||||
template std::optional<HANDLE> GetNativeHandle<false>(xbox::HANDLE xhandle);
|
||||
|
|
|
@ -31,4 +31,4 @@
|
|||
|
||||
void RegisterXboxHandle(xbox::HANDLE xhandle, HANDLE nhandle);
|
||||
void RemoveXboxHandle(xbox::HANDLE xhandle);
|
||||
std::optional<HANDLE> GetNativeHandle(xbox::HANDLE xhandle);
|
||||
template<bool NoConversion = false> std::optional<HANDLE> GetNativeHandle(xbox::HANDLE xhandle);
|
||||
|
|
|
@ -536,21 +536,21 @@ void NVNetDevice::Reset()
|
|||
|
||||
bool NVNetDevice::GetMacAddress(std::string adapterName, void* pMAC)
|
||||
{
|
||||
IP_ADAPTER_INFO AdapterInfo[128];
|
||||
PIP_ADAPTER_INFO pAdapterInfo;
|
||||
ULONG dwBufferLength = sizeof(AdapterInfo);
|
||||
// AdapterInfo is too large to be allocated on the stack, and will cause a crash in debug builds when _chkstk detects it
|
||||
PIP_ADAPTER_INFO pAdapterInfo = new IP_ADAPTER_INFO[128];
|
||||
ULONG dwBufferLength = sizeof(IP_ADAPTER_INFO) * 128;
|
||||
|
||||
DWORD dwStatus = GetAdaptersInfo(AdapterInfo, &dwBufferLength);
|
||||
DWORD dwStatus = GetAdaptersInfo(pAdapterInfo, &dwBufferLength);
|
||||
if (dwStatus != ERROR_SUCCESS) {
|
||||
delete[] pAdapterInfo;
|
||||
return false;
|
||||
}
|
||||
|
||||
pAdapterInfo = AdapterInfo;
|
||||
|
||||
// Find the specified adapter
|
||||
do {
|
||||
if (strcmp(pAdapterInfo->AdapterName, adapterName.c_str()) == 0) {
|
||||
memcpy(pMAC, pAdapterInfo->Address, 6);
|
||||
delete[] pAdapterInfo;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -558,6 +558,7 @@ bool NVNetDevice::GetMacAddress(std::string adapterName, void* pMAC)
|
|||
|
||||
} while (pAdapterInfo);
|
||||
|
||||
delete[] pAdapterInfo;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -150,8 +150,6 @@ static void update_irq(NV2AState *d)
|
|||
else {
|
||||
HalSystemInterrupts[3].Assert(false);
|
||||
}
|
||||
|
||||
SwitchToThread();
|
||||
}
|
||||
|
||||
|
||||
|
@ -1117,7 +1115,7 @@ static void nv2a_vblank_thread(NV2AState *d)
|
|||
//NV2ADevice::UpdateHostDisplay(d);
|
||||
}
|
||||
|
||||
Sleep(1);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue