mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-07-15 11:18:36 +12:00
105 lines
3.2 KiB
C++
105 lines
3.2 KiB
C++
#include <stdafx.h>
|
|
#include "AArch64Signal.h"
|
|
|
|
namespace aarch64
|
|
{
|
|
constexpr u32 ESR_CTX_MAGIC = 0x45535201;
|
|
|
|
// Some of the EC codes we care about
|
|
enum class EL1_exception_class
|
|
{
|
|
undefined = 0,
|
|
|
|
instr_abort_0 = 32, // PAGE_FAULT - Execute, change in EL
|
|
instr_abort_1 = 33, // PAGE_FAULT - Execute, same EL
|
|
data_abort_0 = 36, // PAGE_FAULT - Generic, causing change in EL (e.g kernel sig handler back to EL0)
|
|
data_abort_1 = 37, // PAGE_FAULT - Generic, no change in EL, e.g EL1 driver fault
|
|
|
|
illegal_execution = 14, // BUS_ERROR
|
|
unaligned_pc = 34, // BUS_ERROR
|
|
unaligned_sp = 38, // BUS_ERROR
|
|
|
|
breakpoint = 60, // BRK
|
|
};
|
|
|
|
#ifdef __linux__
|
|
const aarch64_esr_ctx* find_EL1_esr_context(const ucontext_t* ctx)
|
|
{
|
|
u32 offset = 0;
|
|
const auto& mctx = ctx->uc_mcontext;
|
|
|
|
while ((offset + 4) < sizeof(mctx.__reserved))
|
|
{
|
|
const auto head = reinterpret_cast<const aarch64_cpu_ctx_block*>(&mctx.__reserved[offset]);
|
|
if (!head->magic)
|
|
{
|
|
// End of linked list
|
|
return nullptr;
|
|
}
|
|
|
|
if (head->magic == ESR_CTX_MAGIC)
|
|
{
|
|
return reinterpret_cast<const aarch64_esr_ctx*>(head);
|
|
}
|
|
|
|
offset += head->size;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
u64 _read_ESR_EL1(const ucontext_t* uctx)
|
|
{
|
|
auto esr_ctx = find_EL1_esr_context(uctx);
|
|
return esr_ctx ? esr_ctx->esr : 0;
|
|
}
|
|
#elif defined(__APPLE__)
|
|
u64 _read_ESR_EL1(const ucontext_t* uctx)
|
|
{
|
|
// Easy to read from mcontext
|
|
const auto darwin_ctx = reinterpret_cast<aarch64_darwin_mcontext64*>(uctx->uc_mcontext);
|
|
return darwin_ctx->es.ESR;
|
|
}
|
|
#else
|
|
u64 _read_ESR_EL1(const ucontext_t*)
|
|
{
|
|
// Unimplemented
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
fault_reason decode_fault_reason(const ucontext_t* uctx)
|
|
{
|
|
auto esr = _read_ESR_EL1(uctx);
|
|
if (!esr)
|
|
{
|
|
return fault_reason::undefined;
|
|
}
|
|
|
|
// We don't really care about most of the register fields, but we can check for a few things.
|
|
const auto exception_class = (esr >> 26) & 0b111111;
|
|
switch (static_cast<EL1_exception_class>(exception_class))
|
|
{
|
|
case EL1_exception_class::breakpoint:
|
|
// Debug break
|
|
return fault_reason::breakpoint;
|
|
case EL1_exception_class::illegal_execution:
|
|
case EL1_exception_class::unaligned_pc:
|
|
case EL1_exception_class::unaligned_sp:
|
|
return fault_reason::illegal_instruction;
|
|
case EL1_exception_class::instr_abort_0:
|
|
case EL1_exception_class::instr_abort_1:
|
|
return fault_reason::instruction_execute;
|
|
case EL1_exception_class::data_abort_0:
|
|
case EL1_exception_class::data_abort_1:
|
|
// Page fault
|
|
break;
|
|
default:
|
|
return fault_reason::undefined;
|
|
}
|
|
|
|
// Check direction bit
|
|
const auto direction = (esr >> 6u) & 1u;
|
|
return direction ? fault_reason::data_write : fault_reason::data_read;
|
|
}
|
|
}
|