[Base] Linux Arm64 exception handler
This commit is contained in:
@@ -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 <ucontext.h>
|
||||
#include <cstdint>
|
||||
|
||||
#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<ucontext_t*>(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<struct _aarch64_ctx*>(mcontext.__reserved);
|
||||
mcontext_extension->magic;
|
||||
mcontext_extension = reinterpret_cast<struct _aarch64_ctx*>(
|
||||
reinterpret_cast<uint8_t*>(mcontext_extension) +
|
||||
mcontext_extension->size)) {
|
||||
switch (mcontext_extension->magic) {
|
||||
case FPSIMD_MAGIC:
|
||||
mcontext_fpsimd =
|
||||
reinterpret_cast<struct fpsimd_context*>(mcontext_extension);
|
||||
break;
|
||||
case ESR_MAGIC:
|
||||
mcontext_esr =
|
||||
reinterpret_cast<struct esr_context*>(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<const uint32_t*>(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<uint64_t>(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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user