diff --git a/src/xenia/base/exception_handler.cc b/src/xenia/base/exception_handler.cc new file mode 100644 index 000000000..6198797fe --- /dev/null +++ b/src/xenia/base/exception_handler.cc @@ -0,0 +1,85 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2022 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include "xenia/base/exception_handler.h" + +namespace xe { + +// Based on VIXL Instruction::IsLoad and IsStore. +// https://github.com/Linaro/vixl/blob/d48909dd0ac62197edb75d26ed50927e4384a199/src/aarch64/instructions-aarch64.cc#L484 +// +// Copyright 2015, VIXL authors +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of ARM Limited nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +bool IsArm64LoadPrefetchStore(uint32_t instruction, bool& is_store_out) { + if ((instruction & kArm64LoadStoreAnyMask) != kArm64LoadStoreAnyValue) { + return false; + } + if ((instruction & kArm64LoadStorePairAnyMask) == + kArm64LoadStorePairAnyValue) { + is_store_out = !(instruction & kArm64LoadStorePairLoadBit); + return true; + } + switch (Arm64LoadStoreOp(instruction & kArm64LoadStoreMask)) { + case Arm64LoadStoreOp::kLDRB_w: + case Arm64LoadStoreOp::kLDRH_w: + case Arm64LoadStoreOp::kLDR_w: + case Arm64LoadStoreOp::kLDR_x: + case Arm64LoadStoreOp::kLDRSB_x: + case Arm64LoadStoreOp::kLDRSH_x: + case Arm64LoadStoreOp::kLDRSW_x: + case Arm64LoadStoreOp::kLDRSB_w: + case Arm64LoadStoreOp::kLDRSH_w: + case Arm64LoadStoreOp::kLDR_b: + case Arm64LoadStoreOp::kLDR_h: + case Arm64LoadStoreOp::kLDR_s: + case Arm64LoadStoreOp::kLDR_d: + case Arm64LoadStoreOp::kLDR_q: + case Arm64LoadStoreOp::kPRFM: + is_store_out = false; + return true; + case Arm64LoadStoreOp::kSTRB_w: + case Arm64LoadStoreOp::kSTRH_w: + case Arm64LoadStoreOp::kSTR_w: + case Arm64LoadStoreOp::kSTR_x: + case Arm64LoadStoreOp::kSTR_b: + case Arm64LoadStoreOp::kSTR_h: + case Arm64LoadStoreOp::kSTR_s: + case Arm64LoadStoreOp::kSTR_d: + case Arm64LoadStoreOp::kSTR_q: + is_store_out = true; + return true; + default: + return false; + } +} + +} // namespace xe diff --git a/src/xenia/base/exception_handler.h b/src/xenia/base/exception_handler.h index cff15ab1b..27f97f8f1 100644 --- a/src/xenia/base/exception_handler.h +++ b/src/xenia/base/exception_handler.h @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2015 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -10,14 +10,80 @@ #ifndef XENIA_BASE_EXCEPTION_HANDLER_H_ #define XENIA_BASE_EXCEPTION_HANDLER_H_ +#include #include #include #include "xenia/base/assert.h" -#include "xenia/base/x64_context.h" +#include "xenia/base/host_thread_context.h" namespace xe { +// AArch64 load and store decoding based on VIXL. +// https://github.com/Linaro/vixl/blob/ae5957cd66517b3f31dbf37e9bf39db6594abfe3/src/aarch64/constants-aarch64.h +// +// Copyright 2015, VIXL authors +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of ARM Limited nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +constexpr uint32_t kArm64LoadStoreAnyMask = UINT32_C(0x0A000000); +constexpr uint32_t kArm64LoadStoreAnyValue = UINT32_C(0x08000000); +constexpr uint32_t kArm64LoadStorePairAnyMask = UINT32_C(0x3A000000); +constexpr uint32_t kArm64LoadStorePairAnyValue = UINT32_C(0x28000000); +constexpr uint32_t kArm64LoadStorePairLoadBit = UINT32_C(1) << 22; +constexpr uint32_t kArm64LoadStoreMask = UINT32_C(0xC4C00000); + +enum class Arm64LoadStoreOp : uint32_t { + kSTRB_w = UINT32_C(0x00000000), + kSTRH_w = UINT32_C(0x40000000), + kSTR_w = UINT32_C(0x80000000), + kSTR_x = UINT32_C(0xC0000000), + kLDRB_w = UINT32_C(0x00400000), + kLDRH_w = UINT32_C(0x40400000), + kLDR_w = UINT32_C(0x80400000), + kLDR_x = UINT32_C(0xC0400000), + kLDRSB_x = UINT32_C(0x00800000), + kLDRSH_x = UINT32_C(0x40800000), + kLDRSW_x = UINT32_C(0x80800000), + kLDRSB_w = UINT32_C(0x00C00000), + kLDRSH_w = UINT32_C(0x40C00000), + kSTR_b = UINT32_C(0x04000000), + kSTR_h = UINT32_C(0x44000000), + kSTR_s = UINT32_C(0x84000000), + kSTR_d = UINT32_C(0xC4000000), + kSTR_q = UINT32_C(0x04800000), + kLDR_b = UINT32_C(0x04400000), + kLDR_h = UINT32_C(0x44400000), + kLDR_s = UINT32_C(0x84400000), + kLDR_d = UINT32_C(0xC4400000), + kLDR_q = UINT32_C(0x04C00000), + kPRFM = UINT32_C(0xC0800000), +}; + +bool IsArm64LoadPrefetchStore(uint32_t instruction, bool& is_store_out); + class Exception { public: enum class Code { @@ -32,7 +98,7 @@ class Exception { kWrite, }; - void InitializeAccessViolation(X64Context* thread_context, + void InitializeAccessViolation(HostThreadContext* thread_context, uint64_t fault_address, AccessViolationOperation operation) { code_ = Code::kAccessViolation; @@ -40,7 +106,7 @@ class Exception { fault_address_ = fault_address; access_violation_operation_ = operation; } - void InitializeIllegalInstruction(X64Context* thread_context) { + void InitializeIllegalInstruction(HostThreadContext* thread_context) { code_ = Code::kIllegalInstruction; thread_context_ = thread_context; } @@ -48,24 +114,30 @@ class Exception { Code code() const { return code_; } // Returns the platform-specific thread context info. - X64Context* thread_context() const { return thread_context_; } + HostThreadContext* thread_context() const { return thread_context_; } -#if XE_ARCH_AMD64 // Returns the program counter where the exception occurred. - // RIP on x64. - uint64_t pc() const { return thread_context_->rip; } - // Sets the program counter where execution will resume. - void set_resume_pc(uint64_t pc) { thread_context_->rip = pc; } -#else - // Returns the program counter where the exception occurred. - // RIP on x64. uint64_t pc() const { +#if XE_ARCH_AMD64 + return thread_context_->rip; +#elif XE_ARCH_ARM64 + return thread_context_->pc; +#else assert_always(); return 0; +#endif // XE_ARCH } + // Sets the program counter where execution will resume. - void set_resume_pc(uint64_t pc) { assert_always(); } -#endif + void set_resume_pc(uint64_t pc) { +#if XE_ARCH_AMD64 + thread_context_->rip = pc; +#elif XE_ARCH_ARM64 + thread_context_->pc = pc; +#else + assert_always(); +#endif // XE_ARCH + } // In case of AV, address that was read from/written to. uint64_t fault_address() const { return fault_address_; } @@ -77,7 +149,7 @@ class Exception { private: Code code_ = Code::kInvalidException; - X64Context* thread_context_ = nullptr; + HostThreadContext* thread_context_ = nullptr; uint64_t fault_address_ = 0; AccessViolationOperation access_violation_operation_ = AccessViolationOperation::kUnknown; diff --git a/src/xenia/base/exception_handler_posix.cc b/src/xenia/base/exception_handler_posix.cc index c516a213a..41a391e53 100644 --- a/src/xenia/base/exception_handler_posix.cc +++ b/src/xenia/base/exception_handler_posix.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2017 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -13,7 +13,10 @@ #include #include +#include "xenia/base/assert.h" +#include "xenia/base/host_thread_context.h" #include "xenia/base/logging.h" +#include "xenia/base/platform.h" namespace xe { @@ -35,8 +38,9 @@ static void ExceptionHandlerCallback(int signal_number, siginfo_t* signal_info, mcontext_t& mcontext = reinterpret_cast(signal_context)->uc_mcontext; - X64Context thread_context; + HostThreadContext thread_context; +#if XE_ARCH_AMD64 thread_context.rip = uint64_t(mcontext.gregs[REG_RIP]); thread_context.eflags = uint32_t(mcontext.gregs[REG_EFL]); thread_context.rax = uint64_t(mcontext.gregs[REG_RAX]); @@ -57,6 +61,40 @@ static void ExceptionHandlerCallback(int signal_number, siginfo_t* signal_info, thread_context.r15 = uint64_t(mcontext.gregs[REG_R15]); std::memcpy(thread_context.xmm_registers, mcontext.fpregs->_xmm, sizeof(thread_context.xmm_registers)); +#elif XE_ARCH_ARM64 + std::memcpy(thread_context.x, mcontext.regs, sizeof(thread_context.x)); + thread_context.sp = mcontext.sp; + thread_context.pc = mcontext.pc; + thread_context.pstate = mcontext.pstate; + struct fpsimd_context* mcontext_fpsimd = nullptr; + struct esr_context* mcontext_esr = nullptr; + for (struct _aarch64_ctx* mcontext_extension = + reinterpret_cast(mcontext.__reserved); + mcontext_extension->magic; + mcontext_extension = reinterpret_cast( + reinterpret_cast(mcontext_extension) + + mcontext_extension->size)) { + switch (mcontext_extension->magic) { + case FPSIMD_MAGIC: + mcontext_fpsimd = + reinterpret_cast(mcontext_extension); + break; + case ESR_MAGIC: + mcontext_esr = + reinterpret_cast(mcontext_extension); + break; + default: + break; + } + } + assert_not_null(mcontext_fpsimd); + if (mcontext_fpsimd) { + thread_context.fpsr = mcontext_fpsimd->fpsr; + thread_context.fpcr = mcontext_fpsimd->fpcr; + std::memcpy(thread_context.v, mcontext_fpsimd->vregs, + sizeof(thread_context.v)); + } +#endif // XE_ARCH Exception ex; switch (signal_number) { @@ -64,12 +102,53 @@ static void ExceptionHandlerCallback(int signal_number, siginfo_t* signal_info, ex.InitializeIllegalInstruction(&thread_context); break; case SIGSEGV: { + Exception::AccessViolationOperation access_violation_operation; +#if XE_ARCH_AMD64 // x86_pf_error_code::X86_PF_WRITE constexpr uint64_t kX86PageFaultErrorCodeWrite = UINT64_C(1) << 1; - Exception::AccessViolationOperation access_violation_operation = + access_violation_operation = (uint64_t(mcontext.gregs[REG_ERR]) & kX86PageFaultErrorCodeWrite) ? Exception::AccessViolationOperation::kWrite : Exception::AccessViolationOperation::kRead; +#elif XE_ARCH_ARM64 + // For a Data Abort (EC - ESR_EL1 bits 31:26 - 0b100100 from a lower + // Exception Level, 0b100101 without a change in the Exception Level), + // bit 6 is 0 for reading from a memory location, 1 for writing to a + // memory location. + if (mcontext_esr && ((mcontext_esr->esr >> 26) & 0b111110) == 0b100100) { + access_violation_operation = + (mcontext_esr->esr & (UINT64_C(1) << 6)) + ? Exception::AccessViolationOperation::kWrite + : Exception::AccessViolationOperation::kRead; + } else { + // Determine the memory access direction based on which instruction has + // requested it. + // esr_context may be unavailable on certain hosts (for instance, on + // Android, it was added only in NDK r16 - which is the first NDK + // version to support the Android API level 27, while NDK r15 doesn't + // have esr_context in its API 26 sigcontext.h). + // On AArch64 (unlike on AArch32), the program counter is the address of + // the currently executing instruction. + bool instruction_is_store; + if (IsArm64LoadPrefetchStore( + *reinterpret_cast(mcontext.pc), + instruction_is_store)) { + access_violation_operation = + instruction_is_store ? Exception::AccessViolationOperation::kWrite + : Exception::AccessViolationOperation::kRead; + } else { + assert_always( + "No ESR in the exception thread context, or it's not a Data " + "Abort, and the faulting instruction is not a known load, " + "prefetch or store instruction"); + access_violation_operation = + Exception::AccessViolationOperation::kUnknown; + } + } +#else + access_violation_operation = + Exception::AccessViolationOperation::kUnknown; +#endif // XE_ARCH ex.InitializeAccessViolation( &thread_context, reinterpret_cast(signal_info->si_addr), access_violation_operation); @@ -82,7 +161,11 @@ static void ExceptionHandlerCallback(int signal_number, siginfo_t* signal_info, if (handlers_[i].first(&ex, handlers_[i].second)) { // Exception handled. // TODO(benvanik): Update all thread state? Dirty flags? +#if XE_ARCH_AMD64 mcontext.gregs[REG_RIP] = thread_context.rip; +#elif XE_ARCH_ARM64 + mcontext.pc = thread_context.pc; +#endif // XE_ARCH return; } } diff --git a/src/xenia/base/exception_handler_win.cc b/src/xenia/base/exception_handler_win.cc index 6f2ae3216..a2cfa8b44 100644 --- a/src/xenia/base/exception_handler_win.cc +++ b/src/xenia/base/exception_handler_win.cc @@ -35,8 +35,7 @@ LONG CALLBACK ExceptionHandlerCallback(PEXCEPTION_POINTERS ex_info) { return EXCEPTION_CONTINUE_SEARCH; } - // TODO(benvanik): avoid this by mapping X64Context virtual? - X64Context thread_context; + HostThreadContext thread_context; thread_context.rip = ex_info->ContextRecord->Rip; thread_context.eflags = ex_info->ContextRecord->EFlags; std::memcpy(thread_context.int_registers, &ex_info->ContextRecord->Rax, diff --git a/src/xenia/base/host_thread_context.cc b/src/xenia/base/host_thread_context.cc new file mode 100644 index 000000000..435b68a85 --- /dev/null +++ b/src/xenia/base/host_thread_context.cc @@ -0,0 +1,97 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2022 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include "xenia/base/host_thread_context.h" + +#include "xenia/base/assert.h" +#include "xenia/base/platform.h" +#include "xenia/base/string_util.h" + +namespace xe { + +// NOTE: this order matches 1:1 with the HostRegister enums. +static const char* kRegisterNames[] = { +#if XE_ARCH_AMD64 + "rip", "eflags", "rax", "rcx", "rdx", "rbx", "rsp", + "rbp", "rsi", "rdi", "r8", "r9", "r10", "r11", + "r12", "r13", "r14", "r15", "xmm0", "xmm1", "xmm2", + "xmm3", "xmm4", "xmm5", "xmm6", "xmm7", "xmm8", "xmm9", + "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15", +#elif XE_ARCH_ARM64 + "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", + "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "x19", + "x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28", "x29", + "x30", "sp", "pc", "pstate", "fpsr", "fpcr", "q0", "q1", "q2", "q3", + "q4", "q5", "q6", "q7", "q8", "q9", "q10", "q11", "q12", "q13", + "q14", "q15", "q16", "q17", "q18", "q19", "q20", "q21", "q22", "q23", + "q24", "q25", "q26", "q27", "q28", "q29", "q30", "q31", +#endif // XE_ARCH +}; + +const char* HostThreadContext::GetRegisterName(HostRegister reg) { + return kRegisterNames[int(reg)]; +} + +std::string HostThreadContext::GetStringFromValue(HostRegister reg, + bool hex) const { +#if XE_ARCH_AMD64 + switch (reg) { + case X64Register::kRip: + return hex ? string_util::to_hex_string(rip) : std::to_string(rip); + case X64Register::kEflags: + return hex ? string_util::to_hex_string(eflags) : std::to_string(eflags); + default: + if (int(reg) >= int(X64Register::kRax) && + int(reg) <= int(X64Register::kR15)) { + auto value = int_registers[int(reg) - int(X64Register::kRax)]; + return hex ? string_util::to_hex_string(value) : std::to_string(value); + } else if (int(reg) >= int(X64Register::kXmm0) && + int(reg) <= int(X64Register::kXmm15)) { + auto value = xmm_registers[int(reg) - int(X64Register::kXmm0)]; + return hex ? string_util::to_hex_string(value) : xe::to_string(value); + } else { + assert_unhandled_case(reg); + return std::string(); + } + } +#elif XE_ARCH_ARM64 + switch (reg) { + case Arm64Register::kSp: + return hex ? string_util::to_hex_string(sp) : std::to_string(sp); + case Arm64Register::kPc: + return hex ? string_util::to_hex_string(pc) : std::to_string(pc); + case Arm64Register::kPstate: + return hex ? string_util::to_hex_string(pstate) : std::to_string(pstate); + case Arm64Register::kFpsr: + return hex ? string_util::to_hex_string(fpsr) : std::to_string(fpsr); + case Arm64Register::kFpcr: + return hex ? string_util::to_hex_string(fpcr) : std::to_string(fpcr); + default: + if (int(reg) >= int(Arm64Register::kX0) && + int(reg) <= int(Arm64Register::kX30)) { + auto value = x[int(reg) - int(Arm64Register::kX0)]; + return hex ? string_util::to_hex_string(value) : std::to_string(value); + } else if (int(reg) >= int(Arm64Register::kV0) && + int(reg) <= int(Arm64Register::kV31)) { + auto value = v[int(reg) - int(Arm64Register::kV0)]; + return hex ? string_util::to_hex_string(value) : xe::to_string(value); + } else { + assert_unhandled_case(reg); + return std::string(); + } + } +#else + assert_always( + "HostThreadContext::GetStringFromValue not implemented for the target " + "CPU architecture"); + return std::string(); +#endif // XE_ARCH +} + +} // namespace xe diff --git a/src/xenia/base/x64_context.h b/src/xenia/base/host_thread_context.h similarity index 55% rename from src/xenia/base/x64_context.h rename to src/xenia/base/host_thread_context.h index c868e9ed8..8947cc1ec 100644 --- a/src/xenia/base/x64_context.h +++ b/src/xenia/base/host_thread_context.h @@ -2,13 +2,13 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2015 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ -#ifndef XENIA_BASE_X64_CONTEXT_H_ -#define XENIA_BASE_X64_CONTEXT_H_ +#ifndef XENIA_BASE_HOST_THREAD_CONTEXT_H_ +#define XENIA_BASE_HOST_THREAD_CONTEXT_H_ #include #include @@ -22,12 +22,10 @@ namespace xe { -class X64Context; +// NOTE: The order of the registers in the enumerations must match the order in +// the string table in host_thread_context.cc. -#if XE_ARCH_AMD64 enum class X64Register { - // NOTE: this order matches 1:1 with the order in the X64Context. - // NOTE: this order matches 1:1 with a string table in the x64_context.cc. kRip, kEflags, kRax, @@ -64,8 +62,92 @@ enum class X64Register { kXmm15, }; -class X64Context { +enum class Arm64Register { + kX0, + kX1, + kX2, + kX3, + kX4, + kX5, + kX6, + kX7, + kX8, + kX9, + kX10, + kX11, + kX12, + kX13, + kX14, + kX15, + kX16, + kX17, + kX18, + kX19, + kX20, + kX21, + kX22, + kX23, + kX24, + kX25, + kX26, + kX27, + kX28, + // FP (frame pointer). + kX29, + // LR (link register). + kX30, + kSp, + kPc, + kPstate, + kFpsr, + kFpcr, + // In assembly, the whole 128 bits of the Neon vector registers are accessible + // as Q# (quadword registers). VFP also uses these registers. + kV0, + kV1, + kV2, + kV3, + kV4, + kV5, + kV6, + kV7, + kV8, + kV9, + kV10, + kV11, + kV12, + kV13, + kV14, + kV15, + kV16, + kV17, + kV18, + kV19, + kV20, + kV21, + kV22, + kV23, + kV24, + kV25, + kV26, + kV27, + kV28, + kV29, + kV30, + kV31, +}; + +#if XE_ARCH_AMD64 +using HostRegister = X64Register; +#elif XE_ARCH_ARM64 +using HostRegister = Arm64Register; +#else +enum class HostRegister {}; +#endif // XE_ARCH + +class HostThreadContext { public: +#if XE_ARCH_AMD64 uint64_t rip; uint32_t eflags; union { @@ -89,7 +171,6 @@ class X64Context { }; uint64_t int_registers[16]; }; - union { struct { vec128_t xmm0; @@ -111,12 +192,19 @@ class X64Context { }; vec128_t xmm_registers[16]; }; +#elif XE_ARCH_ARM64 + uint64_t x[31]; + uint64_t sp; + uint64_t pc; + uint64_t pstate; + uint32_t fpsr; + uint32_t fpcr; + vec128_t v[32]; +#endif // XE_ARCH - static const char* GetRegisterName(X64Register reg); - std::string GetStringFromValue(X64Register reg, bool hex) const; - void SetValueFromString(X64Register reg, std::string value, bool hex); + static const char* GetRegisterName(HostRegister reg); + std::string GetStringFromValue(HostRegister reg, bool hex) const; }; -#endif // XE_ARCH_AMD64 } // namespace xe diff --git a/src/xenia/base/x64_context.cc b/src/xenia/base/x64_context.cc deleted file mode 100644 index fc6027aeb..000000000 --- a/src/xenia/base/x64_context.cc +++ /dev/null @@ -1,67 +0,0 @@ -/** - ****************************************************************************** - * Xenia : Xbox 360 Emulator Research Project * - ****************************************************************************** - * Copyright 2015 Ben Vanik. All rights reserved. * - * Released under the BSD license - see LICENSE in the root for more details. * - ****************************************************************************** - */ - -#include "xenia/base/x64_context.h" - -#include "xenia/base/assert.h" -#include "xenia/base/platform.h" -#include "xenia/base/string_util.h" - -namespace xe { - -#if XE_ARCH_AMD64 - -// NOTE: this order matches 1:1 with the X64Register enum. -static const char* kRegisterNames[] = { - "rip", "eflags", "rax", "rcx", "rdx", "rbx", "rsp", - "rbp", "rsi", "rdi", "r8", "r9", "r10", "r11", - "r12", "r13", "r14", "r15", "xmm0", "xmm1", "xmm2", - "xmm3", "xmm4", "xmm5", "xmm6", "xmm7", "xmm8", "xmm9", - "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15", -}; - -const char* X64Context::GetRegisterName(X64Register reg) { - return kRegisterNames[static_cast(reg)]; -} - -std::string X64Context::GetStringFromValue(X64Register reg, bool hex) const { - switch (reg) { - case X64Register::kRip: - return hex ? string_util::to_hex_string(rip) : std::to_string(rip); - case X64Register::kEflags: - return hex ? string_util::to_hex_string(eflags) : std::to_string(eflags); - default: - if (static_cast(reg) >= static_cast(X64Register::kRax) && - static_cast(reg) <= static_cast(X64Register::kR15)) { - auto value = int_registers[static_cast(reg) - - static_cast(X64Register::kRax)]; - return hex ? string_util::to_hex_string(value) : std::to_string(value); - } else if (static_cast(reg) >= - static_cast(X64Register::kXmm0) && - static_cast(reg) <= - static_cast(X64Register::kXmm15)) { - auto value = xmm_registers[static_cast(reg) - - static_cast(X64Register::kXmm0)]; - return hex ? string_util::to_hex_string(value) : xe::to_string(value); - } else { - assert_unhandled_case(reg); - return ""; - } - } -} - -void X64Context::SetValueFromString(X64Register reg, std::string value, - bool hex) { - // TODO(benvanik): set value from string. - assert_always(false); -} - -#endif // XE_ARCH_AMD64 - -} // namespace xe diff --git a/src/xenia/cpu/backend/x64/x64_backend.cc b/src/xenia/cpu/backend/x64/x64_backend.cc index 1da4ba9f3..31e1dc9fd 100644 --- a/src/xenia/cpu/backend/x64/x64_backend.cc +++ b/src/xenia/cpu/backend/x64/x64_backend.cc @@ -163,7 +163,7 @@ std::unique_ptr X64Backend::CreateGuestFunction( return std::make_unique(module, address); } -uint64_t ReadCapstoneReg(X64Context* context, x86_reg reg) { +uint64_t ReadCapstoneReg(HostThreadContext* context, x86_reg reg) { switch (reg) { case X86_REG_RAX: return context->rax; diff --git a/src/xenia/cpu/mmio_handler.h b/src/xenia/cpu/mmio_handler.h index fdf202e1c..711427c41 100644 --- a/src/xenia/cpu/mmio_handler.h +++ b/src/xenia/cpu/mmio_handler.h @@ -18,7 +18,7 @@ namespace xe { class Exception; -class X64Context; +class HostThreadContext; } // namespace xe namespace xe { diff --git a/src/xenia/cpu/processor.cc b/src/xenia/cpu/processor.cc index 2fe459dc4..8d1fae81a 100644 --- a/src/xenia/cpu/processor.cc +++ b/src/xenia/cpu/processor.cc @@ -805,8 +805,8 @@ bool Processor::ResumeAllThreads() { return true; } -void Processor::UpdateThreadExecutionStates(uint32_t override_thread_id, - X64Context* override_context) { +void Processor::UpdateThreadExecutionStates( + uint32_t override_thread_id, HostThreadContext* override_context) { auto global_lock = global_critical_region_.Acquire(); uint64_t frame_host_pcs[64]; xe::cpu::StackFrame cpu_frames[64]; @@ -828,7 +828,7 @@ void Processor::UpdateThreadExecutionStates(uint32_t override_thread_id, // Grab stack trace and X64 context then resolve all symbols. uint64_t hash; - X64Context* in_host_context = nullptr; + HostThreadContext* in_host_context = nullptr; if (override_thread_id == thread_info->thread_id) { // If we were passed an override context we use that. Otherwise, ask the // stack walker for a new context. diff --git a/src/xenia/cpu/processor.h b/src/xenia/cpu/processor.h index 2a004548c..0aa06a26d 100644 --- a/src/xenia/cpu/processor.h +++ b/src/xenia/cpu/processor.h @@ -213,8 +213,9 @@ class Processor { // Updates all cached thread execution info (state, call stacks, etc). // The given override thread handle and context will be used in place of // sampled values for that thread. - void UpdateThreadExecutionStates(uint32_t override_handle = 0, - X64Context* override_context = nullptr); + void UpdateThreadExecutionStates( + uint32_t override_handle = 0, + HostThreadContext* override_context = nullptr); // Suspends all breakpoints, uninstalling them as required. // No breakpoints will be triggered until they are resumed. diff --git a/src/xenia/cpu/stack_walker.h b/src/xenia/cpu/stack_walker.h index 4dd4f44e9..3006c2887 100644 --- a/src/xenia/cpu/stack_walker.h +++ b/src/xenia/cpu/stack_walker.h @@ -13,7 +13,7 @@ #include #include -#include "xenia/base/x64_context.h" +#include "xenia/base/host_thread_context.h" #include "xenia/cpu/function.h" namespace xe { @@ -83,8 +83,8 @@ class StackWalker { virtual size_t CaptureStackTrace(void* thread_handle, uint64_t* frame_host_pcs, size_t frame_offset, size_t frame_count, - const X64Context* in_host_context, - X64Context* out_host_context, + const HostThreadContext* in_host_context, + HostThreadContext* out_host_context, uint64_t* out_stack_hash = nullptr) = 0; // Resolves symbol information for the given stack frames. diff --git a/src/xenia/cpu/stack_walker_win.cc b/src/xenia/cpu/stack_walker_win.cc index cbfa96023..aaaab140a 100644 --- a/src/xenia/cpu/stack_walker_win.cc +++ b/src/xenia/cpu/stack_walker_win.cc @@ -153,8 +153,8 @@ class Win32StackWalker : public StackWalker { size_t CaptureStackTrace(void* thread_handle, uint64_t* frame_host_pcs, size_t frame_offset, size_t frame_count, - const X64Context* in_host_context, - X64Context* out_host_context, + const HostThreadContext* in_host_context, + HostThreadContext* out_host_context, uint64_t* out_stack_hash) override { // TODO(benvanik): use xstate? // https://msdn.microsoft.com/en-us/library/windows/desktop/hh134240(v=vs.85).aspx diff --git a/src/xenia/cpu/thread_debug_info.h b/src/xenia/cpu/thread_debug_info.h index ffce6822c..5803880da 100644 --- a/src/xenia/cpu/thread_debug_info.h +++ b/src/xenia/cpu/thread_debug_info.h @@ -12,7 +12,7 @@ #include -#include "xenia/base/x64_context.h" +#include "xenia/base/host_thread_context.h" #include "xenia/cpu/thread.h" #include "xenia/cpu/thread_state.h" @@ -70,10 +70,10 @@ struct ThreadDebugInfo { // Last-sampled PPC context. // This is updated whenever the debugger stops. ppc::PPCContext guest_context; - // Last-sampled host x64 context. + // Last-sampled host context. // This is updated whenever the debugger stops and must be used instead of any // value taken from the StackWalker as it properly respects exception stacks. - X64Context host_context; + HostThreadContext host_context; // A single frame in a call stack. struct Frame { diff --git a/src/xenia/debug/ui/debug_window.cc b/src/xenia/debug/ui/debug_window.cc index bcbf7e042..0c03b7ebb 100644 --- a/src/xenia/debug/ui/debug_window.cc +++ b/src/xenia/debug/ui/debug_window.cc @@ -960,7 +960,7 @@ void DebugWindow::DrawRegistersPane() { auto reg = static_cast(i); ImGui::BeginGroup(); ImGui::AlignTextToFramePadding(); - ImGui::Text("%3s", X64Context::GetRegisterName(reg)); + ImGui::Text("%3s", HostThreadContext::GetRegisterName(reg)); ImGui::SameLine(); ImGui::Dummy(ImVec2(4, 0)); ImGui::SameLine(); @@ -985,7 +985,7 @@ void DebugWindow::DrawRegistersPane() { static_cast(static_cast(X64Register::kXmm0) + i); ImGui::BeginGroup(); ImGui::AlignTextToFramePadding(); - ImGui::Text("%5s", X64Context::GetRegisterName(reg)); + ImGui::Text("%5s", HostThreadContext::GetRegisterName(reg)); ImGui::SameLine(); ImGui::Dummy(ImVec2(4, 0)); ImGui::SameLine(); diff --git a/src/xenia/debug/ui/debug_window.h b/src/xenia/debug/ui/debug_window.h index be7294940..e3c01c54d 100644 --- a/src/xenia/debug/ui/debug_window.h +++ b/src/xenia/debug/ui/debug_window.h @@ -13,7 +13,7 @@ #include #include -#include "xenia/base/x64_context.h" +#include "xenia/base/host_thread_context.h" #include "xenia/cpu/breakpoint.h" #include "xenia/cpu/debug_listener.h" #include "xenia/cpu/processor.h"