"Fix" debug console, we were checking the cvar before any cvars were loaded, and the condition it checks in AttachConsole is somehow always false

Remove dead #if 0'd code in math.h

On amd64, page_size == 4096 constant, on amd64 w/ win32, allocation_granularity == 65536. These values for x86 windows havent changed over the last 20 years so this is probably safe
and gives a modest code size reduction

Enable XE_USE_KUSER_SHARED. This sources host time from KUSER_SHARED instead of from QueryPerformanceCounter, which is far faster, but only has a granularity of 100 nanoseconds.

In some games seemingly random crashes were happening that were hard to trace because
the faulting thread was actually not the one that was misbehaving, another threads stack was underflowing into the faulting thread.

Added a bunch of code to synchronize the guest stack and host stack so that if a guest longjmps the host's stack will be adjusted.
Changes were also made to allow the guest to call into a piece of an existing x64 function.

This synchronization might have a slight performance impact on lower end cpus, to disable it set enable_host_guest_stack_synchronization to false.
It is possible it may have introduced regressions, but i dont know of any yet

So far, i know the synchronization change fixes the "hub crash" in super sonic and allows the game "london 2012" to go ingame.

Removed emit_useless_fpscr_updates, not emitting these updates breaks the raiden game

MapGuestAddressToMachineCode now returns nullptr if no address was found, instead of the start of the function

add Processor::LookupModule

Add Backend::DeinitializeBackendContext
Use WriteRegisterRangeFromRing_WithKnownBound<0, 0xFFFF> in WriteRegisterRangeFromRing for inlining (previously regressed on performance of ExecutePacketType0)

add notes about flags that trap in XamInputGetCapabilities

0 == 3 in XamInputGetCapabilities

Name arg 2 of XamInputSetState

PrefetchW in critical section kernel funcs if available & doing cmpxchg

Add terminated field to X_KTHREAD, set it on termination

Expanded the logic of NtResumeThread/NtSuspendThread to include checking the type of the handle (in release, LookupObject doesnt seem to do anything with the type)
and returning X_STATUS_OBJECT_TYPE_MISMATCH if invalid. Do termination check in NtSuspendThread.

Add basic host exception messagebox, need to flesh it out more (maybe use the new stack tracking stuff if on guest thrd?)

Add rdrand patching hack, mostly affects users with nvidia cards who have many threads on zen

Use page_size_shift in more places

Once again disable precompilation! Raiden is mostly weird ppc asm which probably breaks the precompilation. The code is still useful for running the compiler over the whole of an xex in debug to test for issues
"Fix" debug console, we were checking the cvar before any cvars were loaded, and the condition it checks in AttachConsole is somehow always false

Remove dead #if 0'd code in math.h

On amd64, page_size == 4096 constant, on amd64 w/ win32, allocation_granularity == 65536. These values for x86 windows havent changed over the last 20 years so this is probably safe
and gives a modest code size reduction

Enable XE_USE_KUSER_SHARED. This sources host time from KUSER_SHARED instead of from QueryPerformanceCounter, which is far faster, but only has a granularity of 100 nanoseconds.

In some games seemingly random crashes were happening that were hard to trace because
the faulting thread was actually not the one that was misbehaving, another threads stack was underflowing into the faulting thread.

Added a bunch of code to synchronize the guest stack and host stack so that if a guest longjmps the host's stack will be adjusted.
Changes were also made to allow the guest to call into a piece of an existing x64 function.

This synchronization might have a slight performance impact on lower end cpus, to disable it set enable_host_guest_stack_synchronization to false.
It is possible it may have introduced regressions, but i dont know of any yet

So far, i know the synchronization change fixes the "hub crash" in super sonic and allows the game "london 2012" to go ingame.

Removed emit_useless_fpscr_updates, not emitting these updates breaks the raiden game

MapGuestAddressToMachineCode now returns nullptr if no address was found, instead of the start of the function

add Processor::LookupModule

Add Backend::DeinitializeBackendContext
Use WriteRegisterRangeFromRing_WithKnownBound<0, 0xFFFF> in WriteRegisterRangeFromRing for inlining (previously regressed on performance of ExecutePacketType0)

add notes about flags that trap in XamInputGetCapabilities

0 == 3 in XamInputGetCapabilities

Name arg 2 of XamInputSetState

PrefetchW in critical section kernel funcs if available & doing cmpxchg

Add terminated field to X_KTHREAD, set it on termination

Expanded the logic of NtResumeThread/NtSuspendThread to include checking the type of the handle (in release, LookupObject doesnt seem to do anything with the type)
and returning X_STATUS_OBJECT_TYPE_MISMATCH if invalid. Do termination check in NtSuspendThread.

Add basic host exception messagebox, need to flesh it out more (maybe use the new stack tracking stuff if on guest thrd?)

Add rdrand patching hack, mostly affects users with nvidia cards who have many threads on zen

Use page_size_shift in more places

Once again disable precompilation! Raiden is mostly weird ppc asm which probably breaks the precompilation. The code is still useful for running the compiler over the whole of an xex in debug to test for issues
This commit is contained in:
chss95cs@gmail.com
2022-11-27 09:37:06 -08:00
parent 7a17fad88a
commit 90c771526d
28 changed files with 950 additions and 244 deletions

View File

@@ -41,10 +41,23 @@ DECLARE_XAM_EXPORT1(XamEnableInactivityProcessing, kInput, kStub);
// https://msdn.microsoft.com/en-us/library/windows/desktop/microsoft.directx_sdk.reference.xinputgetcapabilities(v=vs.85).aspx
dword_result_t XamInputGetCapabilities_entry(
dword_t user_index, dword_t flags, pointer_t<X_INPUT_CAPABILITIES> caps) {
dword_t user_index, dword_t _flags, pointer_t<X_INPUT_CAPABILITIES> caps) {
unsigned flags = _flags;
//chrispy: actually, it appears that caps is never checked for null, it is memset at the start regardless
if (!caps) {
return X_ERROR_BAD_ARGUMENTS;
}
if ((flags & 0x40000000) != 0) {
//should trap
}
if ((flags & 4) != 0) {
//should trap
}
if (!flags) {
flags = 3;
}
if ((flags & 0xFF) && (flags & XINPUT_FLAG_GAMEPAD) == 0) {
// Ignore any query for other types of devices.
@@ -118,7 +131,7 @@ dword_result_t XamInputGetState_entry(dword_t user_index, dword_t flags,
DECLARE_XAM_EXPORT2(XamInputGetState, kInput, kImplemented, kHighFrequency);
// https://msdn.microsoft.com/en-us/library/windows/desktop/microsoft.directx_sdk.reference.xinputsetstate(v=vs.85).aspx
dword_result_t XamInputSetState_entry(dword_t user_index, dword_t unk,
dword_result_t XamInputSetState_entry(dword_t user_index, dword_t flags, /* flags, as far as i can see, is not used*/
pointer_t<X_INPUT_VIBRATION> vibration) {
if (user_index >= 4) {
return X_E_DEVICE_NOT_CONNECTED;

View File

@@ -508,7 +508,16 @@ dword_result_t RtlInitializeCriticalSectionAndSpinCount_entry(
DECLARE_XBOXKRNL_EXPORT1(RtlInitializeCriticalSectionAndSpinCount, kNone,
kImplemented);
static void CriticalSectionPrefetchW(const void* vp) {
#if XE_ARCH_AMD64 == 1
if (amd64::GetFeatureFlags() & amd64::kX64EmitPrefetchW) {
swcache::PrefetchW(vp);
}
#endif
}
void RtlEnterCriticalSection_entry(pointer_t<X_RTL_CRITICAL_SECTION> cs) {
CriticalSectionPrefetchW(&cs->lock_count);
uint32_t cur_thread = XThread::GetCurrentThread()->guest_object();
uint32_t spin_count = cs->header.absolute * 256;
@@ -544,6 +553,7 @@ DECLARE_XBOXKRNL_EXPORT2(RtlEnterCriticalSection, kNone, kImplemented,
dword_result_t RtlTryEnterCriticalSection_entry(
pointer_t<X_RTL_CRITICAL_SECTION> cs) {
CriticalSectionPrefetchW(&cs->lock_count);
uint32_t thread = XThread::GetCurrentThread()->guest_object();
if (xe::atomic_cas(-1, 0, &cs->lock_count)) {

View File

@@ -7,6 +7,7 @@
******************************************************************************
*/
#include "xenia/kernel/xboxkrnl/xboxkrnl_threading.h"
#include <algorithm>
#include <vector>
#include "xenia/base/atomic.h"
@@ -18,7 +19,6 @@
#include "xenia/kernel/user_module.h"
#include "xenia/kernel/util/shim_utils.h"
#include "xenia/kernel/xboxkrnl/xboxkrnl_private.h"
#include "xenia/kernel/xboxkrnl/xboxkrnl_threading.h"
#include "xenia/kernel/xevent.h"
#include "xenia/kernel/xmutant.h"
#include "xenia/kernel/xsemaphore.h"
@@ -165,8 +165,16 @@ dword_result_t NtResumeThread_entry(dword_t handle,
uint32_t suspend_count = 0;
auto thread = kernel_state()->object_table()->LookupObject<XThread>(handle);
if (thread) {
result = thread->Resume(&suspend_count);
if (thread->type() == XObject::Type::Thread) {
result = thread->Resume(&suspend_count);
} else {
return X_STATUS_OBJECT_TYPE_MISMATCH;
}
} else {
return X_STATUS_INVALID_HANDLE;
}
if (suspend_count_ptr) {
*suspend_count_ptr = suspend_count;
@@ -190,15 +198,27 @@ dword_result_t KeResumeThread_entry(lpvoid_t thread_ptr) {
DECLARE_XBOXKRNL_EXPORT1(KeResumeThread, kThreading, kImplemented);
dword_result_t NtSuspendThread_entry(dword_t handle,
lpdword_t suspend_count_ptr) {
lpdword_t suspend_count_ptr,
const ppc_context_t& context) {
X_RESULT result = X_STATUS_SUCCESS;
uint32_t suspend_count = 0;
auto thread = kernel_state()->object_table()->LookupObject<XThread>(handle);
if (thread) {
result = thread->Suspend(&suspend_count);
if (thread->type() == XObject::Type::Thread) {
auto current_pcr = context->TranslateVirtualGPR<X_KPCR*>(context->r[13]);
if (current_pcr->current_thread == thread->guest_object() ||
!thread->guest_object<X_KTHREAD>()->terminated) {
result = thread->Suspend(&suspend_count);
} else {
return X_STATUS_THREAD_IS_TERMINATING;
}
} else {
return X_STATUS_OBJECT_TYPE_MISMATCH;
}
} else {
result = X_STATUS_INVALID_HANDLE;
return X_STATUS_INVALID_HANDLE;
}
if (suspend_count_ptr) {
@@ -213,23 +233,23 @@ void KeSetCurrentStackPointers_entry(lpvoid_t stack_ptr,
pointer_t<X_KTHREAD> thread,
lpvoid_t stack_alloc_base,
lpvoid_t stack_base,
lpvoid_t stack_limit) {
lpvoid_t stack_limit, const ppc_context_t& context) {
auto current_thread = XThread::GetCurrentThread();
auto context = current_thread->thread_state()->context();
auto pcr = kernel_memory()->TranslateVirtual<X_KPCR*>(
static_cast<uint32_t>(context->r[13]));
auto pcr = context->TranslateVirtualGPR<X_KPCR*>(context->r[13]);
thread->stack_alloc_base = stack_alloc_base.value();
thread->stack_base = stack_base.value();
thread->stack_limit = stack_limit.value();
pcr->stack_base_ptr = stack_base.guest_address();
pcr->stack_end_ptr = stack_limit.guest_address();
context->r[1] = stack_ptr.guest_address();
// If a fiber is set, and the thread matches, reenter to avoid issues with
// host stack overflowing.
if (thread->fiber_ptr &&
current_thread->guest_object() == thread.guest_address()) {
context->processor->backend()->PrepareForReentry(context.value());
current_thread->Reenter(static_cast<uint32_t>(context->lr));
}
}
@@ -1018,7 +1038,8 @@ void KeAcquireSpinLockAtRaisedIrql_entry(lpdword_t lock_ptr,
assert_true(*lock_ptr != static_cast<uint32_t>(ppc_ctx->r[13]));
PrefetchForCAS(lock);
while (!xe::atomic_cas(0, xe::byte_swap(static_cast<uint32_t>(ppc_ctx->r[13])), lock)) {
while (!xe::atomic_cas(
0, xe::byte_swap(static_cast<uint32_t>(ppc_ctx->r[13])), lock)) {
#if XE_ARCH_AMD64 == 1
// todo: this is just a nop if they don't have SMT, which is not great
// either...
@@ -1038,7 +1059,8 @@ dword_result_t KeTryToAcquireSpinLockAtRaisedIrql_entry(
auto lock = reinterpret_cast<uint32_t*>(lock_ptr.host_address());
assert_true(*lock_ptr != static_cast<uint32_t>(ppc_ctx->r[13]));
PrefetchForCAS(lock);
if (!xe::atomic_cas(0, xe::byte_swap(static_cast<uint32_t>(ppc_ctx->r[13])), lock)) {
if (!xe::atomic_cas(0, xe::byte_swap(static_cast<uint32_t>(ppc_ctx->r[13])),
lock)) {
return 0;
}
return 1;
@@ -1281,7 +1303,8 @@ DECLARE_XBOXKRNL_EXPORT1(ExInitializeReadWriteLock, kThreading, kImplemented);
void ExAcquireReadWriteLockExclusive_entry(pointer_t<X_ERWLOCK> lock_ptr,
const ppc_context_t& ppc_context) {
auto old_irql = xeKeKfAcquireSpinLock(&lock_ptr->spin_lock, ppc_context->r[13]);
auto old_irql =
xeKeKfAcquireSpinLock(&lock_ptr->spin_lock, ppc_context->r[13]);
int32_t lock_count = ++lock_ptr->lock_count;
if (!lock_count) {
@@ -1318,7 +1341,8 @@ DECLARE_XBOXKRNL_EXPORT1(ExTryToAcquireReadWriteLockExclusive, kThreading,
void ExAcquireReadWriteLockShared_entry(pointer_t<X_ERWLOCK> lock_ptr,
const ppc_context_t& ppc_context) {
auto old_irql = xeKeKfAcquireSpinLock(&lock_ptr->spin_lock, ppc_context->r[13]);
auto old_irql =
xeKeKfAcquireSpinLock(&lock_ptr->spin_lock, ppc_context->r[13]);
int32_t lock_count = ++lock_ptr->lock_count;
if (!lock_count ||

View File

@@ -33,8 +33,15 @@ DEFINE_bool(ignore_thread_priorities, true,
DEFINE_bool(ignore_thread_affinities, true,
"Ignores game-specified thread affinities.", "Kernel");
#if 0
DEFINE_int64(stack_size_multiplier_hack, 1,
"A hack for games with setjmp/longjmp issues.", "Kernel");
DEFINE_int64(main_xthread_stack_size_multiplier_hack, 1,
"A hack for games with setjmp/longjmp issues.", "Kernel");
#endif
namespace xe {
namespace kernel {
namespace kernel {
const uint32_t XAPC::kSize;
const uint32_t XAPC::kDummyKernelRoutine;
@@ -373,8 +380,23 @@ X_STATUS XThread::Create() {
RetainHandle();
xe::threading::Thread::CreationParameters params;
params.stack_size = 16_MiB; // Allocate a big host stack.
params.create_suspended = true;
#if 0
uint64_t stack_size_mult = cvars::stack_size_multiplier_hack;
if (main_thread_) {
stack_size_mult =
static_cast<uint64_t>(cvars::main_xthread_stack_size_multiplier_hack);
}
#else
uint64_t stack_size_mult = 1;
#endif
params.stack_size = 16_MiB * stack_size_mult; // Allocate a big host stack.
thread_ = xe::threading::Thread::Create(params, [this]() {
// Set thread ID override. This is used by logging.
xe::threading::set_current_thread_id(handle());
@@ -433,6 +455,9 @@ X_STATUS XThread::Create() {
X_STATUS XThread::Exit(int exit_code) {
// This may only be called on the thread itself.
assert_true(XThread::GetCurrentThread() == this);
//TODO(chrispy): not sure if this order is correct, should it come after apcs?
guest_object<X_KTHREAD>()->terminated = 1;
// TODO(benvanik): dispatch events? waiters? etc?
RundownAPCs();

View File

@@ -121,7 +121,7 @@ struct X_KTHREAD {
uint8_t unk_B4[0x8]; // 0xB4
uint8_t suspend_count; // 0xBC
uint8_t unk_BD; // 0xBD
uint8_t unk_BE; // 0xBE
uint8_t terminated; // 0xBE
uint8_t current_cpu; // 0xBF
uint8_t unk_C0[0x10]; // 0xC0
xe::be<uint32_t> stack_alloc_base; // 0xD0