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:
ergo720 2022-02-22 23:20:29 +01:00 committed by GitHub
commit f857593f77
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 1257 additions and 950 deletions

View File

@ -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

View File

@ -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);

View File

@ -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)

View File

@ -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),
};

View File

@ -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);
}
// ******************************************************************

View File

@ -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);
}

View File

@ -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

View File

@ -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
);
// ******************************************************************

View File

@ -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
);
// ******************************************************************

View File

@ -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();
}

View File

@ -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

View File

@ -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?
}
// ******************************************************************

View File

@ -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

View File

@ -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);
}
// ******************************************************************

View File

@ -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);

View File

@ -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();
}

View File

@ -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;
}
}

View File

@ -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];

View File

@ -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);
}
// ******************************************************************

View File

@ -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;

View File

@ -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,

View File

@ -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!");

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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)
}

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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;
}

View File

@ -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));
}
}