From e21fd22d09d17982042abbb1d1fa6ca60756c68a Mon Sep 17 00:00:00 2001 From: "chss95cs@gmail.com" Date: Sun, 6 Nov 2022 11:03:10 -0800 Subject: [PATCH 1/2] add x_kthread priority/fpu_exceptions_on fields, set fpu_exceptions_on in KeEnableFpuExceptions, set priority in SetPriority add msr field on context write to msr for mtmsr/mfmsr, do not have correct default value for msr yet, nor has mtmsrd been reimplemented do not evaluate assert expressions in release at all, while still avoiding unused variable warnings --- src/xenia/base/assert.h | 25 ++++++++++++++- src/xenia/cpu/ppc/ppc_context.h | 3 ++ src/xenia/cpu/ppc/ppc_emit_control.cc | 19 ++---------- src/xenia/kernel/kernel_state.cc | 2 +- src/xenia/kernel/util/object_table.cc | 36 ++++++++++++---------- src/xenia/kernel/util/object_table.h | 3 +- src/xenia/kernel/xboxkrnl/xboxkrnl_misc.cc | 7 ++++- src/xenia/kernel/xthread.cc | 3 ++ src/xenia/kernel/xthread.h | 7 ++++- 9 files changed, 68 insertions(+), 37 deletions(-) diff --git a/src/xenia/base/assert.h b/src/xenia/base/assert.h index ab78d353d..710e6bbf5 100644 --- a/src/xenia/base/assert.h +++ b/src/xenia/base/assert.h @@ -20,9 +20,32 @@ namespace xe { static_assert(sizeof(type) == size, \ "bad definition for " #type ": must be " #size " bytes") -// We rely on assert being compiled out in NDEBUG. +/* + * chrispy: we need to ensure our expression is not eliminated by the + * preprocessor before the compiler runs, otherwise clang & gcc will warn about + * unused variables and terminate compilation + * + * Initial approach was to do "#define xenia_assert static_cast" in + * release, however, code is generated in this case for the expression, which + * isnt desirable (we have assert expressions w/ side effects in a few places) + * + * so instead, when compiling for msvc we do __noop (which takes varargs and + * generates no code), and under clang/gcc we do __builtin_constant_p, which + * also does not evaluate the args + * + */ #if defined(NDEBUG) + +#if XE_COMPILER_MSVC == 1 +#define xenia_assert __noop +#elif XE_COMPILER_HAS_GNU_EXTENSIONS == 1 +#define xenia_assert __builtin_constant_p +#else +#warning \ + "Compiler does not have MSVC or GNU extensions, falling back to static_cast for assert expr which may cause expressions with sideeffects to be evaluated" #define xenia_assert static_cast +#endif + #else #define xenia_assert assert #endif diff --git a/src/xenia/cpu/ppc/ppc_context.h b/src/xenia/cpu/ppc/ppc_context.h index 799cef860..629b28392 100644 --- a/src/xenia/cpu/ppc/ppc_context.h +++ b/src/xenia/cpu/ppc/ppc_context.h @@ -377,6 +377,9 @@ typedef struct alignas(64) PPCContext_s { uint64_t r[32]; // 0x20 General purpose registers uint64_t ctr; // 0x18 Count register uint64_t lr; // 0x10 Link register + + uint64_t msr; //machine state register + double f[32]; // 0x120 Floating-point registers vec128_t v[128]; // 0x220 VMX128 vector registers vec128_t vscr_vec; diff --git a/src/xenia/cpu/ppc/ppc_emit_control.cc b/src/xenia/cpu/ppc/ppc_emit_control.cc index 097b91bfa..4348f9a27 100644 --- a/src/xenia/cpu/ppc/ppc_emit_control.cc +++ b/src/xenia/cpu/ppc/ppc_emit_control.cc @@ -793,30 +793,17 @@ int InstrEmit_mfmsr(PPCHIRBuilder& f, const InstrData& i) { // bit 48 = EE; interrupt enabled // bit 62 = RI; recoverable interrupt // return 8000h if unlocked (interrupts enabled), else 0 -#if 0 - f.MemoryBarrier(); - if (cvars::disable_global_lock || true) { - f.StoreGPR(i.X.RT, f.LoadConstantUint64(0)); - - } else { - f.CallExtern(f.builtins()->check_global_lock); - f.StoreGPR(i.X.RT, - f.LoadContext(offsetof(PPCContext, scratch), INT64_TYPE)); - } -#else - f.StoreGPR(i.X.RT, f.LoadConstantUint64(0)); -#endif + f.StoreGPR(i.X.RT, f.LoadContext(offsetof(PPCContext, msr), INT64_TYPE)); return 0; } int InstrEmit_mtmsr(PPCHIRBuilder& f, const InstrData& i) { - f.StoreContext( - offsetof(PPCContext, scratch), - f.ZeroExtend(f.ZeroExtend(f.LoadGPR(i.X.RT), INT64_TYPE), INT64_TYPE)); + f.StoreContext(offsetof(PPCContext, msr), f.LoadGPR(i.X.RT)); return 0; } int InstrEmit_mtmsrd(PPCHIRBuilder& f, const InstrData& i) { + //todo: this is moving msr under a mask, so only writing EE and RI f.StoreContext(offsetof(PPCContext, scratch), f.ZeroExtend(f.LoadGPR(i.X.RT), INT64_TYPE)); return 0; diff --git a/src/xenia/kernel/kernel_state.cc b/src/xenia/kernel/kernel_state.cc index fbd9da668..1f2760763 100644 --- a/src/xenia/kernel/kernel_state.cc +++ b/src/xenia/kernel/kernel_state.cc @@ -518,7 +518,7 @@ void KernelState::UnloadUserModule(const object_ref& module, return e->path() == module->path(); }) == user_modules_.end()); - object_table()->ReleaseHandle(module->handle()); + object_table()->ReleaseHandleInLock(module->handle()); } void KernelState::TerminateTitle() { diff --git a/src/xenia/kernel/util/object_table.cc b/src/xenia/kernel/util/object_table.cc index f560cc837..d6efa32c4 100644 --- a/src/xenia/kernel/util/object_table.cc +++ b/src/xenia/kernel/util/object_table.cc @@ -149,7 +149,7 @@ X_STATUS ObjectTable::DuplicateHandle(X_HANDLE handle, X_HANDLE* out_handle) { X_STATUS ObjectTable::RetainHandle(X_HANDLE handle) { auto global_lock = global_critical_region_.Acquire(); - ObjectTableEntry* entry = LookupTable(handle); + ObjectTableEntry* entry = LookupTableInLock(handle); if (!entry) { return X_STATUS_INVALID_HANDLE; } @@ -161,7 +161,10 @@ X_STATUS ObjectTable::RetainHandle(X_HANDLE handle) { X_STATUS ObjectTable::ReleaseHandle(X_HANDLE handle) { auto global_lock = global_critical_region_.Acquire(); - ObjectTableEntry* entry = LookupTable(handle); + return ReleaseHandleInLock(handle); +} +X_STATUS ObjectTable::ReleaseHandleInLock(X_HANDLE handle) { + ObjectTableEntry* entry = LookupTableInLock(handle); if (!entry) { return X_STATUS_INVALID_HANDLE; } @@ -175,7 +178,6 @@ X_STATUS ObjectTable::ReleaseHandle(X_HANDLE handle) { // (but not a failure code) return X_STATUS_SUCCESS; } - X_STATUS ObjectTable::RemoveHandle(X_HANDLE handle) { X_STATUS result = X_STATUS_SUCCESS; @@ -183,13 +185,13 @@ X_STATUS ObjectTable::RemoveHandle(X_HANDLE handle) { if (!handle) { return X_STATUS_INVALID_HANDLE; } + auto global_lock = global_critical_region_.Acquire(); - ObjectTableEntry* entry = LookupTable(handle); + ObjectTableEntry* entry = LookupTableInLock(handle); if (!entry) { return X_STATUS_INVALID_HANDLE; } - auto global_lock = global_critical_region_.Acquire(); if (entry->object) { auto object = entry->object; entry->object = nullptr; @@ -246,13 +248,16 @@ void ObjectTable::PurgeAllObjects() { } ObjectTable::ObjectTableEntry* ObjectTable::LookupTable(X_HANDLE handle) { + auto global_lock = global_critical_region_.Acquire(); + return LookupTableInLock(handle); +} + +ObjectTable::ObjectTableEntry* ObjectTable::LookupTableInLock(X_HANDLE handle) { handle = TranslateHandle(handle); if (!handle) { return nullptr; } - auto global_lock = global_critical_region_.Acquire(); - // Lower 2 bits are ignored. uint32_t slot = GetHandleSlot(handle); if (slot <= table_capacity_) { @@ -264,8 +269,8 @@ ObjectTable::ObjectTableEntry* ObjectTable::LookupTable(X_HANDLE handle) { // Generic lookup template <> -object_ref ObjectTable::LookupObject( - X_HANDLE handle, bool already_locked) { +object_ref ObjectTable::LookupObject(X_HANDLE handle, + bool already_locked) { auto object = ObjectTable::LookupObject(handle, already_locked); auto result = object_ref(reinterpret_cast(object)); return result; @@ -320,15 +325,14 @@ void ObjectTable::GetObjectsByType(XObject::Type type, } X_HANDLE ObjectTable::TranslateHandle(X_HANDLE handle) { - if (handle == 0xFFFFFFFF) { - // CurrentProcess - // assert_always(); + // chrispy: reordered these by likelihood, most likely case is that handle is + // not a special handle + XE_LIKELY_IF(handle < 0xFFFFFFFE) { return handle; } + else if (handle == 0xFFFFFFFF) { return 0; - } else if (handle == 0xFFFFFFFE) { - // CurrentThread + } + else { return XThread::GetCurrentThreadHandle(); - } else { - return handle; } } diff --git a/src/xenia/kernel/util/object_table.h b/src/xenia/kernel/util/object_table.h index 7722c28d1..aced0ea60 100644 --- a/src/xenia/kernel/util/object_table.h +++ b/src/xenia/kernel/util/object_table.h @@ -38,6 +38,7 @@ class ObjectTable { X_STATUS DuplicateHandle(X_HANDLE orig, X_HANDLE* out_handle); X_STATUS RetainHandle(X_HANDLE handle); X_STATUS ReleaseHandle(X_HANDLE handle); + X_STATUS ReleaseHandleInLock(X_HANDLE handle); X_STATUS RemoveHandle(X_HANDLE handle); bool Save(ByteStream* stream); @@ -87,7 +88,7 @@ class ObjectTable { int handle_ref_count = 0; XObject* object = nullptr; }; - + ObjectTableEntry* LookupTableInLock(X_HANDLE handle); ObjectTableEntry* LookupTable(X_HANDLE handle); XObject* LookupObject(X_HANDLE handle, bool already_locked); void GetObjectsByType(XObject::Type type, diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_misc.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_misc.cc index 1c29c5e57..1ca764d82 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_misc.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_misc.cc @@ -19,8 +19,13 @@ namespace xe { namespace kernel { namespace xboxkrnl { -void KeEnableFpuExceptions_entry(dword_t enabled) { +void KeEnableFpuExceptions_entry( + const ppc_context_t& ctx) { // dword_t enabled) { // TODO(benvanik): can we do anything about exceptions? + // theres a lot more thats supposed to happen here, the floating point state has to be saved to kthread, the irql changes, the machine state register is changed to enable exceptions + + X_KTHREAD* kthread = ctx->TranslateVirtualGPR(ctx->r[13]); + kthread->fpu_exceptions_on = static_cast(ctx->r[3]) != 0; } DECLARE_XBOXKRNL_EXPORT1(KeEnableFpuExceptions, kNone, kStub); #if 0 diff --git a/src/xenia/kernel/xthread.cc b/src/xenia/kernel/xthread.cc index 084485c66..6d548220a 100644 --- a/src/xenia/kernel/xthread.cc +++ b/src/xenia/kernel/xthread.cc @@ -717,6 +717,9 @@ void XThread::RundownAPCs() { int32_t XThread::QueryPriority() { return thread_->priority(); } void XThread::SetPriority(int32_t increment) { + if (is_guest_thread()) { + guest_object()->priority = static_cast(increment); + } priority_ = increment; int32_t target_priority = 0; if (increment > 0x22) { diff --git a/src/xenia/kernel/xthread.h b/src/xenia/kernel/xthread.h index 3e0ff19c4..75e790ebd 100644 --- a/src/xenia/kernel/xthread.h +++ b/src/xenia/kernel/xthread.h @@ -101,7 +101,12 @@ struct X_KTHREAD { xe::be stack_kernel; // 0x64 xe::be tls_address; // 0x68 uint8_t unk_6C; // 0x6C - uint8_t unk_6D[0x7]; // 0x6D + //0x70 = priority? + uint8_t unk_6D[0x3]; // 0x6D + uint8_t priority; // 0x70 + uint8_t fpu_exceptions_on; // 0x71 + uint8_t unk_72; + uint8_t unk_73; xe::be unk_74; // 0x74 xe::be unk_78; // 0x78 xe::be unk_7C; // 0x7C From 7a17fad88a810e13129fa89bc728126fd4b8b3e4 Mon Sep 17 00:00:00 2001 From: "chss95cs@gmail.com" Date: Mon, 7 Nov 2022 05:40:18 -0800 Subject: [PATCH 2/2] fix crash from precompiling out of range funcs, add xexcache version, increment xexcache version (all priors are version 0 thanks to 0 initialization) --- src/xenia/cpu/ppc/ppc_emit_control.cc | 3 ++ src/xenia/cpu/processor.cc | 10 +++-- src/xenia/cpu/xex_module.cc | 58 +++++++++++++++++++++------ src/xenia/cpu/xex_module.h | 20 ++++++++- 4 files changed, 73 insertions(+), 18 deletions(-) diff --git a/src/xenia/cpu/ppc/ppc_emit_control.cc b/src/xenia/cpu/ppc/ppc_emit_control.cc index 4348f9a27..a57e7ca85 100644 --- a/src/xenia/cpu/ppc/ppc_emit_control.cc +++ b/src/xenia/cpu/ppc/ppc_emit_control.cc @@ -789,6 +789,9 @@ int InstrEmit_mtspr(PPCHIRBuilder& f, const InstrData& i) { // code requires it. Sequences of mtmsr/lwar/stcw/mtmsr come up a lot, and // without the lock here threads can livelock. + +//0x400 = debug singlestep i think +//ive seen 0x8000 used in kernel code int InstrEmit_mfmsr(PPCHIRBuilder& f, const InstrData& i) { // bit 48 = EE; interrupt enabled // bit 62 = RI; recoverable interrupt diff --git a/src/xenia/cpu/processor.cc b/src/xenia/cpu/processor.cc index 0fbeb30cd..aa9f83013 100644 --- a/src/xenia/cpu/processor.cc +++ b/src/xenia/cpu/processor.cc @@ -263,6 +263,12 @@ Function* Processor::ResolveFunction(uint32_t address) { return nullptr; } + + if (!DemandFunction(function)) { + entry->status = Entry::STATUS_FAILED; + return nullptr; + } + //only add it to the list of resolved functions if resolving succeeded auto module_for = function->module(); auto xexmod = dynamic_cast(module_for); @@ -273,10 +279,6 @@ Function* Processor::ResolveFunction(uint32_t address) { } } - if (!DemandFunction(function)) { - entry->status = Entry::STATUS_FAILED; - return nullptr; - } entry->function = function; entry->end_address = function->end_address(); status = entry->status = Entry::STATUS_READY; diff --git a/src/xenia/cpu/xex_module.cc b/src/xenia/cpu/xex_module.cc index 2f8afc853..449827cc5 100644 --- a/src/xenia/cpu/xex_module.cc +++ b/src/xenia/cpu/xex_module.cc @@ -1328,6 +1328,7 @@ void XexInfoCache::Init(XexModule* xexmod) { if (cvars::disable_instruction_infocache) { return; } + auto emu = xexmod->kernel_state_->emulator(); std::filesystem::path infocache_path = emu->cache_root(); @@ -1341,13 +1342,16 @@ void XexInfoCache::Init(XexModule* xexmod) { unsigned num_codebytes = xexmod->high_address_ - xexmod->low_address_; num_codebytes += 3; // round up to nearest multiple of 4 num_codebytes &= ~3; + bool did_exist = true; if (!std::filesystem::exists(infocache_path)) { + recreate: xe::filesystem::CreateEmptyFile(infocache_path); did_exist = false; } // todo: prepopulate with stuff from pdata, dll exports + this->executable_addr_flags_ = std::move(xe::MappedMemory::Open( infocache_path, xe::MappedMemory::Mode::kReadWrite, 0, sizeof(InfoCacheFlagsHeader) + @@ -1355,11 +1359,17 @@ void XexInfoCache::Init(XexModule* xexmod) { (num_codebytes / 4)))); // one infocacheflags entry for each PPC instr-sized addr - if (did_exist) { - xexmod->PrecompileKnownFunctions(); + if (!did_exist) { + GetHeader()->version = CURRENT_INFOCACHE_VERSION; + + } else { + if (GetHeader()->version != CURRENT_INFOCACHE_VERSION) { + this->executable_addr_flags_->Close(); + std::filesystem::remove(infocache_path); + goto recreate; + } } } - InfoCacheFlags* XexModule::GetInstructionAddressFlags(uint32_t guest_addr) { if (guest_addr < low_address_ || guest_addr > high_address_) { return nullptr; @@ -1371,7 +1381,7 @@ InfoCacheFlags* XexModule::GetInstructionAddressFlags(uint32_t guest_addr) { } void XexModule::PrecompileDiscoveredFunctions() { if (cvars::disable_early_precompilation) { - return; + return; } auto others = PreanalyzeCode(); @@ -1396,7 +1406,7 @@ void XexModule::PrecompileKnownFunctions() { if (!flags) { return; } - //maybe should pre-acquire global crit? + // maybe should pre-acquire global crit? for (uint32_t i = 0; i < end; i++) { if (flags[i].was_resolved) { uint32_t addr = low_address_ + (i * 4); @@ -1425,8 +1435,20 @@ static bool IsOpcodeBL(unsigned w) { std::vector XexModule::PreanalyzeCode() { uint32_t low_8_aligned = xe::align(low_address_, 8); - uint32_t high_8_aligned = high_address_ & ~(8U - 1); + + + uint32_t highest_exec_addr = 0; + + for (auto&& sec : pe_sections_) { + if ((sec.flags & kXEPESectionContainsCode)) { + + + highest_exec_addr = + std::max(highest_exec_addr, sec.address + sec.size); + } + } + uint32_t high_8_aligned = highest_exec_addr & ~(8U - 1); uint32_t n_possible_8byte_addresses = (high_8_aligned - low_8_aligned) / 8; uint32_t* funcstart_candidate_stack = new uint32_t[n_possible_8byte_addresses]; @@ -1453,6 +1475,10 @@ std::vector XexModule::PreanalyzeCode() { uint32_t mfspr_r12_lr32 = *reinterpret_cast(&mfspr_r12_lr[0]); + + auto add_new_func = [funcstart_candidate_stack, &stack_pos](uint32_t addr) { + funcstart_candidate_stack[stack_pos++] = addr; + }; /* First pass: detect save of the link register at an eight byte aligned address @@ -1462,10 +1488,10 @@ std::vector XexModule::PreanalyzeCode() { if (*first_pass == mfspr_r12_lr32) { // Push our newly discovered function start into our list // All addresses in the list are sorted until the second pass - funcstart_candidate_stack[stack_pos++] = + add_new_func( static_cast(reinterpret_cast(first_pass) - reinterpret_cast(range_start)) + - low_8_aligned; + low_8_aligned); } else if (first_pass[-1] == 0 && *first_pass != 0) { // originally i checked for blr followed by 0, but some functions are // actually aligned to greater boundaries. something that appears to be @@ -1478,10 +1504,10 @@ std::vector XexModule::PreanalyzeCode() { } XE_LIKELY_IF(*check_iter == blr32) { - funcstart_candidate_stack[stack_pos++] = + add_new_func( static_cast(reinterpret_cast(first_pass) - reinterpret_cast(range_start)) + - low_8_aligned; + low_8_aligned); } } } @@ -1494,8 +1520,14 @@ std::vector XexModule::PreanalyzeCode() { uint32_t current_call = xe::byte_swap(*second_pass); if (IsOpcodeBL(current_call)) { - funcstart_candidate_stack[stack_pos++] = GetBLCalledFunction( + uint32_t called_function = GetBLCalledFunction( this, current_guestaddr, ppc::PPCOpcodeBits{current_call}); + // must be 8 byte aligned and in range + if ((called_function & (8 - 1)) == 0 && + called_function >= low_address_ && + called_function < high_address_) { + add_new_func(called_function); + } } } @@ -1509,8 +1541,8 @@ std::vector XexModule::PreanalyzeCode() { for (uint32_t i = 0; i < n_pdata_entries; ++i) { uint32_t funcaddr = xe::load_and_swap(&pdata_base[i * 2]); - if (funcaddr >= low_address_ && funcaddr <= high_address_) { - funcstart_candidate_stack[stack_pos++] = funcaddr; + if (funcaddr >= low_address_ && funcaddr <= highest_exec_addr) { + add_new_func(funcaddr); } else { // we hit 0 for func addr, that means we're done break; diff --git a/src/xenia/cpu/xex_module.h b/src/xenia/cpu/xex_module.h index d5981ad41..bec6b7e0f 100644 --- a/src/xenia/cpu/xex_module.h +++ b/src/xenia/cpu/xex_module.h @@ -29,6 +29,7 @@ constexpr fourcc_t kXEX1Signature = make_fourcc("XEX1"); constexpr fourcc_t kXEX2Signature = make_fourcc("XEX2"); constexpr fourcc_t kElfSignature = make_fourcc(0x7F, 'E', 'L', 'F'); + class Runtime; struct InfoCacheFlags { uint32_t was_resolved : 1; // has this address ever been called/requested @@ -38,8 +39,13 @@ struct InfoCacheFlags { uint32_t reserved : 29; }; struct XexInfoCache { + //increment this to invalidate all user infocaches + static constexpr uint32_t CURRENT_INFOCACHE_VERSION = 1; + struct InfoCacheFlagsHeader { - unsigned char reserved[256]; // put xenia version here + uint32_t version; + + unsigned char reserved[252]; InfoCacheFlags* LookupFlags(unsigned offset) { return &reinterpret_cast(&this[1])[offset]; @@ -51,6 +57,18 @@ struct XexInfoCache { std::unique_ptr executable_addr_flags_; void Init(class XexModule*); + InfoCacheFlagsHeader* GetHeader() { + if (!executable_addr_flags_) { + return nullptr; + } + uint8_t* data = executable_addr_flags_->data(); + + if (!data) { + return nullptr; + } + return reinterpret_cast(data); + } + InfoCacheFlags* LookupFlags(unsigned offset) { offset /= 4; if (!executable_addr_flags_) {