mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2024-11-15 03:27:49 +01:00
kern: SvcReturnFromException
This commit is contained in:
parent
8cd81b3092
commit
5d462c626c
@ -47,6 +47,21 @@ namespace ams::kern::arch::arm64 {
|
||||
static Result BreakIfAttached(ams::svc::BreakReason break_reason, uintptr_t address, size_t size);
|
||||
static Result SetHardwareBreakPoint(ams::svc::HardwareBreakPointRegisterName name, u64 flags, u64 value);
|
||||
|
||||
static constexpr bool IsBreakInstruction(u32 insn, u32 psr) {
|
||||
constexpr u32 BreakInstructionAarch64 = 0xE7FFFFFF;
|
||||
constexpr u32 BreakInstructionAarch32 = 0xE7FFDEFE;
|
||||
constexpr u32 BreakInstructionThumb32 = 0xB68E;
|
||||
if ((psr & 0x10) == 0) {
|
||||
return insn == BreakInstructionAarch64;
|
||||
} else {
|
||||
if ((psr & 0x20) == 0) {
|
||||
return insn == BreakInstructionAarch32;
|
||||
} else {
|
||||
return insn == BreakInstructionThumb32;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO: This is a placeholder definition. */
|
||||
};
|
||||
|
||||
|
@ -312,6 +312,32 @@ namespace ams::kern {
|
||||
}
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void CopyEnterExceptionSvcPermissionsTo(KThread::StackParameters &sp) {
|
||||
static_assert(sizeof(svc_access_flags) == sizeof(sp.svc_permission));
|
||||
|
||||
/* Set ReturnFromException if allowed. */
|
||||
if (GetSvcAllowedImpl(this->svc_access_flags, svc::SvcId_ReturnFromException)) {
|
||||
SetSvcAllowedImpl(sp.svc_permission, svc::SvcId_ReturnFromException);
|
||||
}
|
||||
|
||||
/* Set GetInfo if allowed. */
|
||||
if (GetSvcAllowedImpl(this->svc_access_flags, svc::SvcId_GetInfo)) {
|
||||
SetSvcAllowedImpl(sp.svc_permission, svc::SvcId_GetInfo);
|
||||
}
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void CopyLeaveExceptionSvcPermissionsTo(KThread::StackParameters &sp) {
|
||||
static_assert(sizeof(svc_access_flags) == sizeof(sp.svc_permission));
|
||||
|
||||
/* Clear ReturnFromException. */
|
||||
ClearSvcAllowedImpl(sp.svc_permission, svc::SvcId_ReturnFromException);
|
||||
|
||||
/* If pinned, clear GetInfo. */
|
||||
if (sp.is_pinned) {
|
||||
ClearSvcAllowedImpl(sp.svc_permission, svc::SvcId_GetInfo);
|
||||
}
|
||||
}
|
||||
|
||||
constexpr bool IsPermittedInterrupt(u32 id) const {
|
||||
constexpr size_t BitsPerWord = BITSIZEOF(this->irq_access_flags[0]);
|
||||
if (id < BITSIZEOF(this->irq_access_flags)) {
|
||||
|
@ -233,6 +233,14 @@ namespace ams::kern {
|
||||
this->capabilities.CopyUnpinnedSvcPermissionsTo(sp);
|
||||
}
|
||||
|
||||
void CopyEnterExceptionSvcPermissionsTo(KThread::StackParameters &sp) {
|
||||
this->capabilities.CopyEnterExceptionSvcPermissionsTo(sp);
|
||||
}
|
||||
|
||||
void CopyLeaveExceptionSvcPermissionsTo(KThread::StackParameters &sp) {
|
||||
this->capabilities.CopyLeaveExceptionSvcPermissionsTo(sp);
|
||||
}
|
||||
|
||||
constexpr KResourceLimit *GetResourceLimit() const { return this->resource_limit; }
|
||||
|
||||
bool ReserveResource(ams::svc::LimitableResource which, s64 value);
|
||||
|
@ -230,6 +230,10 @@ namespace ams::kern {
|
||||
const StackParameters &GetStackParameters() const {
|
||||
return *(reinterpret_cast<const StackParameters *>(this->kernel_stack_top) - 1);
|
||||
}
|
||||
public:
|
||||
StackParameters &GetStackParametersForExceptionSvcPermission() {
|
||||
return *(reinterpret_cast<StackParameters *>(this->kernel_stack_top) - 1);
|
||||
}
|
||||
public:
|
||||
ALWAYS_INLINE s32 GetDisableDispatchCount() const {
|
||||
MESOSPHERE_ASSERT_THIS();
|
||||
@ -251,6 +255,18 @@ namespace ams::kern {
|
||||
void Pin();
|
||||
void Unpin();
|
||||
|
||||
ALWAYS_INLINE void SaveDebugParams(uintptr_t param1, uintptr_t param2, uintptr_t param3) {
|
||||
this->debug_params[0] = param1;
|
||||
this->debug_params[1] = param2;
|
||||
this->debug_params[2] = param3;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void RestoreDebugParams(uintptr_t *param1, uintptr_t *param2, uintptr_t *param3) {
|
||||
*param1 = this->debug_params[0];
|
||||
*param2 = this->debug_params[1];
|
||||
*param3 = this->debug_params[2];
|
||||
}
|
||||
|
||||
NOINLINE void DisableCoreMigration();
|
||||
NOINLINE void EnableCoreMigration();
|
||||
|
||||
|
@ -15,10 +15,52 @@
|
||||
*/
|
||||
#include <mesosphere.hpp>
|
||||
|
||||
namespace ams::kern::svc {
|
||||
|
||||
void RestoreContext(uintptr_t sp);
|
||||
|
||||
}
|
||||
|
||||
namespace ams::kern::arch::arm64 {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr inline u32 El0PsrMask = 0xFF0FFE20;
|
||||
|
||||
enum EsrEc : u32 {
|
||||
EsrEc_Unknown = 0b000000,
|
||||
EsrEc_WaitForInterruptOrEvent = 0b000001,
|
||||
EsrEc_Cp15McrMrc = 0b000011,
|
||||
EsrEc_Cp15McrrMrrc = 0b000100,
|
||||
EsrEc_Cp14McrMrc = 0b000101,
|
||||
EsrEc_FpAccess = 0b000111,
|
||||
EsrEc_Cp14Mrrc = 0b001100,
|
||||
EsrEc_BranchTarget = 0b001101,
|
||||
EsrEc_IllegalExecution = 0b001110,
|
||||
EsrEc_Svc32 = 0b010001,
|
||||
EsrEc_Svc64 = 0b010101,
|
||||
EsrEc_SystemInstruction64 = 0b011000,
|
||||
EsrEc_SveZen = 0b011001,
|
||||
EsrEc_PointerAuthInstruction = 0b011100,
|
||||
EsrEc_InstructionAbortEl0 = 0b100000,
|
||||
EsrEc_InstructionAbortEl1 = 0b100001,
|
||||
EsrEc_PcAlignmentFault = 0b100010,
|
||||
EsrEc_DataAbortEl0 = 0b100100,
|
||||
EsrEc_DataAbortEl1 = 0b100101,
|
||||
EsrEc_SpAlignmentFault = 0b100110,
|
||||
EsrEc_FpException32 = 0b101000,
|
||||
EsrEc_FpException64 = 0b101100,
|
||||
EsrEc_SErrorInterrupt = 0b101111,
|
||||
EsrEc_BreakPointEl0 = 0b110000,
|
||||
EsrEc_BreakPointEl1 = 0b110001,
|
||||
EsrEc_SoftwareStepEl0 = 0b110010,
|
||||
EsrEc_SoftwareStepEl1 = 0b110011,
|
||||
EsrEc_WatchPointEl0 = 0b110100,
|
||||
EsrEc_WatchPointEl1 = 0b110101,
|
||||
EsrEc_BkptInstruction = 0b111000,
|
||||
EsrEc_BrkInstruction = 0b111100,
|
||||
};
|
||||
|
||||
constexpr u32 GetInstructionData(const KExceptionContext *context, u64 esr) {
|
||||
/* Check for THUMB usermode */
|
||||
if ((context->psr & 0x3F) == 0x30) {
|
||||
@ -35,61 +77,234 @@ namespace ams::kern::arch::arm64 {
|
||||
}
|
||||
|
||||
void HandleUserException(KExceptionContext *context, u64 esr, u64 far, u64 afsr0, u64 afsr1, u32 data) {
|
||||
KProcess *cur_process = GetCurrentProcessPointer();
|
||||
KProcess &cur_process = GetCurrentProcess();
|
||||
bool should_process_user_exception = KTargetSystem::IsUserExceptionHandlersEnabled();
|
||||
|
||||
MESOSPHERE_LOG("User Exception occurred in %s\n", cur_process->GetName());
|
||||
|
||||
for (size_t i = 0; i < 31; i++) {
|
||||
MESOSPHERE_LOG("X[%02zu] = %016lx\n", i, context->x[i]);
|
||||
}
|
||||
MESOSPHERE_LOG("PC = %016lx\n", context->pc);
|
||||
MESOSPHERE_LOG("SP = %016lx\n", context->sp);
|
||||
|
||||
/* Dump the page tables. */
|
||||
/* GetCurrentProcess().GetPageTable().DumpTable(); */
|
||||
GetCurrentProcess().GetPageTable().DumpMemoryBlocks();
|
||||
|
||||
MESOSPHERE_PANIC("Unhandled Exception in User Mode\n");
|
||||
|
||||
const u64 ec = (esr >> 26) & 0x3F;
|
||||
switch (ec) {
|
||||
case 0x0: /* Unknown */
|
||||
case 0xE: /* Illegal Execution State */
|
||||
case 0x11: /* SVC instruction from Aarch32 */
|
||||
case 0x15: /* SVC instruction from Aarch64 */
|
||||
case 0x22: /* PC Misalignment */
|
||||
case 0x26: /* SP Misalignment */
|
||||
case 0x2F: /* SError */
|
||||
case 0x30: /* Breakpoint from lower EL */
|
||||
case 0x32: /* SoftwareStep from lower EL */
|
||||
case 0x34: /* Watchpoint from lower EL */
|
||||
case 0x38: /* BKPT instruction */
|
||||
case 0x3C: /* BRK instruction */
|
||||
case EsrEc_Unknown:
|
||||
case EsrEc_IllegalExecution:
|
||||
case EsrEc_Svc32:
|
||||
case EsrEc_Svc64:
|
||||
case EsrEc_PcAlignmentFault:
|
||||
case EsrEc_SpAlignmentFault:
|
||||
case EsrEc_SErrorInterrupt:
|
||||
case EsrEc_BreakPointEl0:
|
||||
case EsrEc_SoftwareStepEl0:
|
||||
case EsrEc_WatchPointEl0:
|
||||
case EsrEc_BkptInstruction:
|
||||
case EsrEc_BrkInstruction:
|
||||
break;
|
||||
default:
|
||||
{
|
||||
MESOSPHERE_TODO("Get memory state.");
|
||||
/* If state is KMemoryState_Code and the user can't read it, set should_process_user_exception = true; */
|
||||
/* If the fault address's state is KMemoryState_Code and the user can't read the address, force processing exception. */
|
||||
KMemoryInfo info;
|
||||
ams::svc::PageInfo pi;
|
||||
if (R_SUCCEEDED(cur_process.GetPageTable().QueryInfo(std::addressof(info), std::addressof(pi), far))) {
|
||||
if (info.GetState() == KMemoryState_Code && ((info.GetPermission() & KMemoryPermission_UserRead) != KMemoryPermission_UserRead)) {
|
||||
should_process_user_exception = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (should_process_user_exception) {
|
||||
MESOSPHERE_TODO("Process the user exception.");
|
||||
/* If we should process the user exception (and it's not a breakpoint), try to enter. */
|
||||
const bool is_software_break = (ec == EsrEc_Unknown || ec == EsrEc_IllegalExecution || ec == EsrEc_BkptInstruction || ec == EsrEc_BrkInstruction);
|
||||
const bool is_breakpoint = (ec == EsrEc_BreakPointEl0 || ec == EsrEc_SoftwareStepEl0 || ec == EsrEc_WatchPointEl0);
|
||||
if ((should_process_user_exception) &&
|
||||
!(is_software_break && cur_process.IsAttachedToDebugger() && KDebug::IsBreakInstruction(data, context->psr)) &&
|
||||
!(is_breakpoint))
|
||||
{
|
||||
if (cur_process.EnterUserException()) {
|
||||
/* Fill out the exception info. */
|
||||
const bool is_aarch64 = (context->psr & 0x10) == 0;
|
||||
if (is_aarch64) {
|
||||
/* 64-bit. */
|
||||
ams::svc::aarch64::ExceptionInfo *info = std::addressof(GetPointer<ams::svc::aarch64::ProcessLocalRegion>(cur_process.GetProcessLocalRegionAddress())->exception_info);
|
||||
|
||||
for (size_t i = 0; i < util::size(info->r); ++i) {
|
||||
info->r[i] = context->x[i];
|
||||
}
|
||||
info->sp = context->sp;
|
||||
info->lr = context->x[30];
|
||||
info->pc = context->pc;
|
||||
info->pstate = (context->psr & El0PsrMask);
|
||||
info->afsr0 = afsr0;
|
||||
info->afsr1 = afsr1;
|
||||
info->esr = esr;
|
||||
info->far = far;
|
||||
} else {
|
||||
/* 32-bit. */
|
||||
ams::svc::aarch32::ExceptionInfo *info = std::addressof(GetPointer<ams::svc::aarch32::ProcessLocalRegion>(cur_process.GetProcessLocalRegionAddress())->exception_info);
|
||||
|
||||
for (size_t i = 0; i < util::size(info->r); ++i) {
|
||||
info->r[i] = context->x[i];
|
||||
}
|
||||
info->sp = context->x[13];
|
||||
info->lr = context->x[14];
|
||||
info->pc = context->pc;
|
||||
info->flags = 1;
|
||||
|
||||
info->status_64.pstate = (context->psr & El0PsrMask);
|
||||
info->status_64.afsr0 = afsr0;
|
||||
info->status_64.afsr1 = afsr1;
|
||||
info->status_64.esr = esr;
|
||||
info->status_64.far = far;
|
||||
}
|
||||
|
||||
/* Save the debug parameters to the current thread. */
|
||||
GetCurrentThread().SaveDebugParams(far, esr, data);
|
||||
|
||||
/* Get the exception type. */
|
||||
u32 type;
|
||||
switch (ec) {
|
||||
case EsrEc_Unknown:
|
||||
case EsrEc_IllegalExecution:
|
||||
case EsrEc_Cp15McrMrc:
|
||||
case EsrEc_Cp15McrrMrrc:
|
||||
case EsrEc_Cp14McrMrc:
|
||||
case EsrEc_Cp14Mrrc:
|
||||
case EsrEc_SystemInstruction64:
|
||||
case EsrEc_BkptInstruction:
|
||||
case EsrEc_BrkInstruction:
|
||||
type = ams::svc::ExceptionType_InstructionAbort;
|
||||
break;
|
||||
case EsrEc_PcAlignmentFault:
|
||||
type = ams::svc::ExceptionType_UnalignedInstruction;
|
||||
break;
|
||||
case EsrEc_SpAlignmentFault:
|
||||
type = ams::svc::ExceptionType_UnalignedData;
|
||||
break;
|
||||
case EsrEc_Svc32:
|
||||
case EsrEc_Svc64:
|
||||
type = ams::svc::ExceptionType_InvalidSystemCall;
|
||||
break;
|
||||
case EsrEc_SErrorInterrupt:
|
||||
type = ams::svc::ExceptionType_MemorySystemError;
|
||||
break;
|
||||
case EsrEc_InstructionAbortEl0:
|
||||
type = ams::svc::ExceptionType_InstructionAbort;
|
||||
break;
|
||||
case EsrEc_DataAbortEl0:
|
||||
default:
|
||||
type = ams::svc::ExceptionType_DataAbort;
|
||||
break;
|
||||
}
|
||||
|
||||
/* We want to enter at the process entrypoint, with x0 = type. */
|
||||
context->pc = GetInteger(cur_process.GetEntryPoint());
|
||||
context->x[0] = type;
|
||||
if (is_aarch64) {
|
||||
context->x[1] = GetInteger(cur_process.GetProcessLocalRegionAddress() + __builtin_offsetof(ams::svc::aarch64::ProcessLocalRegion, exception_info));
|
||||
|
||||
auto *plr = GetPointer<ams::svc::aarch64::ProcessLocalRegion>(cur_process.GetProcessLocalRegionAddress());
|
||||
context->sp = util::AlignDown(reinterpret_cast<uintptr_t>(plr->data) + sizeof(plr->data), 0x10);
|
||||
context->psr = 0;
|
||||
} else {
|
||||
context->x[1] = GetInteger(cur_process.GetProcessLocalRegionAddress() + __builtin_offsetof(ams::svc::aarch32::ProcessLocalRegion, exception_info));
|
||||
|
||||
auto *plr = GetPointer<ams::svc::aarch32::ProcessLocalRegion>(cur_process.GetProcessLocalRegionAddress());
|
||||
context->x[13] = util::AlignDown(reinterpret_cast<uintptr_t>(plr->data) + sizeof(plr->data), 0x10);
|
||||
context->psr = 0x10;
|
||||
}
|
||||
|
||||
/* Set exception SVC permissions. */
|
||||
cur_process.CopyEnterExceptionSvcPermissionsTo(GetCurrentThread().GetStackParametersForExceptionSvcPermission());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
MESOSPHERE_TODO("Process for KDebug.");
|
||||
/* Collect additional information based on the ec. */
|
||||
ams::svc::DebugException exception;
|
||||
uintptr_t param2 = 0;
|
||||
uintptr_t param3 = 0;
|
||||
switch (ec) {
|
||||
case EsrEc_Unknown:
|
||||
case EsrEc_IllegalExecution:
|
||||
case EsrEc_BkptInstruction:
|
||||
case EsrEc_BrkInstruction:
|
||||
{
|
||||
exception = ams::svc::DebugException_UndefinedInstruction;
|
||||
param2 = far;
|
||||
param3 = data;
|
||||
}
|
||||
break;
|
||||
case EsrEc_PcAlignmentFault:
|
||||
case EsrEc_SpAlignmentFault:
|
||||
{
|
||||
exception = ams::svc::DebugException_AlignmentFault;
|
||||
param2 = far;
|
||||
}
|
||||
break;
|
||||
case EsrEc_Svc32:
|
||||
case EsrEc_Svc64:
|
||||
{
|
||||
exception = ams::svc::DebugException_UndefinedSystemCall;
|
||||
param2 = far;
|
||||
param3 = (esr & 0xFF);
|
||||
}
|
||||
break;
|
||||
case EsrEc_BreakPointEl0:
|
||||
case EsrEc_SoftwareStepEl0:
|
||||
{
|
||||
exception = ams::svc::DebugException_BreakPoint;
|
||||
param2 = far;
|
||||
param3 = ams::svc::BreakPointType_HardwareInstruction;
|
||||
}
|
||||
break;
|
||||
case EsrEc_WatchPointEl0:
|
||||
{
|
||||
exception = ams::svc::DebugException_BreakPoint;
|
||||
param2 = far;
|
||||
param3 = ams::svc::BreakPointType_HardwareInstruction;
|
||||
}
|
||||
break;
|
||||
case EsrEc_SErrorInterrupt:
|
||||
{
|
||||
exception = ams::svc::DebugException_MemorySystemError;
|
||||
param2 = far;
|
||||
}
|
||||
break;
|
||||
case EsrEc_InstructionAbortEl0:
|
||||
{
|
||||
exception = ams::svc::DebugException_InstructionAbort;
|
||||
param2 = far;
|
||||
}
|
||||
break;
|
||||
case EsrEc_DataAbortEl0:
|
||||
default:
|
||||
{
|
||||
exception = ams::svc::DebugException_DataAbort;
|
||||
param2 = far;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
MESOSPHERE_TODO("cur_process->GetProgramId()");
|
||||
MESOSPHERE_RELEASE_LOG("Exception occurred. %016lx\n", 0ul);
|
||||
/* Process the debug event. */
|
||||
Result result = KDebug::OnDebugEvent(ams::svc::DebugEvent_Exception, exception, param2, param3);
|
||||
|
||||
MESOSPHERE_TODO("if (!svc::ResultNotHandled::Includes(res)) { debug process }.");
|
||||
/* If we should stop processing the exception, do so. */
|
||||
if (svc::ResultStopProcessingException::Includes(result)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Print that an exception occurred. */
|
||||
MESOSPHERE_RELEASE_LOG("Exception occurred. %016lx\n", GetCurrentProcess().GetProgramId());
|
||||
|
||||
|
||||
/* If the SVC is handled, handle it. */
|
||||
if (!svc::ResultNotHandled::Includes(result)) {
|
||||
/* If we successfully enter jit debug, stop processing the exception. */
|
||||
if (cur_process.EnterJitDebug(ams::svc::DebugEvent_Exception, exception, param2, param3)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MESOSPHERE_TODO("cur_process->Exit();");
|
||||
(void)cur_process;
|
||||
/* Exit the current process. */
|
||||
cur_process.Exit();
|
||||
}
|
||||
|
||||
}
|
||||
@ -99,6 +314,149 @@ namespace ams::kern::arch::arm64 {
|
||||
KThreadContext::FpuContextSwitchHandler(GetCurrentThreadPointer());
|
||||
}
|
||||
|
||||
/* NOTE: This function is called from ASM. */
|
||||
void ReturnFromException(Result user_result) {
|
||||
/* Get the current thread. */
|
||||
KThread *cur_thread = GetCurrentThreadPointer();
|
||||
|
||||
/* Get the current exception context. */
|
||||
KExceptionContext *e_ctx = GetExceptionContext(cur_thread);
|
||||
|
||||
/* Get the current process. */
|
||||
KProcess &cur_process = GetCurrentProcess();
|
||||
|
||||
/* Read the exception info that userland put in tls. */
|
||||
union {
|
||||
ams::svc::aarch64::ExceptionInfo info64;
|
||||
ams::svc::aarch32::ExceptionInfo info32;
|
||||
} info = {};
|
||||
|
||||
|
||||
const bool is_aarch64 = (e_ctx->psr & 0x10) == 0;
|
||||
if (is_aarch64) {
|
||||
/* We're 64-bit. */
|
||||
info.info64 = GetPointer<ams::svc::aarch64::ProcessLocalRegion>(cur_process.GetProcessLocalRegionAddress())->exception_info;
|
||||
} else {
|
||||
/* We're 32-bit. */
|
||||
info.info32 = GetPointer<ams::svc::aarch32::ProcessLocalRegion>(cur_process.GetProcessLocalRegionAddress())->exception_info;
|
||||
}
|
||||
|
||||
/* Try to leave the user exception. */
|
||||
if (cur_process.LeaveUserException()) {
|
||||
/* We left user exception. Alter our SVC permissions accordingly. */
|
||||
cur_process.CopyLeaveExceptionSvcPermissionsTo(cur_thread->GetStackParametersForExceptionSvcPermission());
|
||||
|
||||
/* Copy the user context to the thread context. */
|
||||
if (is_aarch64) {
|
||||
for (size_t i = 0; i < util::size(info.info64.r); ++i) {
|
||||
e_ctx->x[i] = info.info64.r[i];
|
||||
}
|
||||
e_ctx->x[30] = info.info64.lr;
|
||||
e_ctx->sp = info.info64.sp;
|
||||
e_ctx->pc = info.info64.pc;
|
||||
e_ctx->psr = (info.info64.pstate & El0PsrMask) | (e_ctx->psr & ~El0PsrMask);
|
||||
} else {
|
||||
for (size_t i = 0; i < util::size(info.info32.r); ++i) {
|
||||
e_ctx->x[i] = info.info32.r[i];
|
||||
}
|
||||
e_ctx->x[14] = info.info32.lr;
|
||||
e_ctx->x[13] = info.info32.sp;
|
||||
e_ctx->pc = info.info32.pc;
|
||||
e_ctx->psr = (info.info32.status_64.pstate & El0PsrMask) | (e_ctx->psr & ~El0PsrMask);
|
||||
}
|
||||
|
||||
/* Note that PC was adjusted. */
|
||||
e_ctx->write = 1;
|
||||
|
||||
if (R_SUCCEEDED(user_result)) {
|
||||
/* If result handling succeeded, just restore the context. */
|
||||
svc::RestoreContext(reinterpret_cast<uintptr_t>(e_ctx));
|
||||
} else {
|
||||
/* Restore the debug params for the exception. */
|
||||
uintptr_t far, esr, data;
|
||||
GetCurrentThread().RestoreDebugParams(std::addressof(far), std::addressof(esr), std::addressof(data));
|
||||
|
||||
/* Collect additional information based on the ec. */
|
||||
ams::svc::DebugException exception;
|
||||
uintptr_t param2 = 0;
|
||||
uintptr_t param3 = 0;
|
||||
switch ((esr >> 26) & 0x3F) {
|
||||
case EsrEc_Unknown:
|
||||
case EsrEc_IllegalExecution:
|
||||
case EsrEc_BkptInstruction:
|
||||
case EsrEc_BrkInstruction:
|
||||
{
|
||||
exception = ams::svc::DebugException_UndefinedInstruction;
|
||||
param2 = far;
|
||||
param3 = data;
|
||||
}
|
||||
break;
|
||||
case EsrEc_PcAlignmentFault:
|
||||
case EsrEc_SpAlignmentFault:
|
||||
{
|
||||
exception = ams::svc::DebugException_AlignmentFault;
|
||||
param2 = far;
|
||||
}
|
||||
break;
|
||||
case EsrEc_Svc32:
|
||||
case EsrEc_Svc64:
|
||||
{
|
||||
exception = ams::svc::DebugException_UndefinedSystemCall;
|
||||
param2 = far;
|
||||
param3 = (esr & 0xFF);
|
||||
}
|
||||
break;
|
||||
case EsrEc_SErrorInterrupt:
|
||||
{
|
||||
exception = ams::svc::DebugException_MemorySystemError;
|
||||
param2 = far;
|
||||
}
|
||||
break;
|
||||
case EsrEc_InstructionAbortEl0:
|
||||
{
|
||||
exception = ams::svc::DebugException_InstructionAbort;
|
||||
param2 = far;
|
||||
}
|
||||
break;
|
||||
case EsrEc_DataAbortEl0:
|
||||
default:
|
||||
{
|
||||
exception = ams::svc::DebugException_DataAbort;
|
||||
param2 = far;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* Process the debug event. */
|
||||
Result result = KDebug::OnDebugEvent(ams::svc::DebugEvent_Exception, exception, param2, param3);
|
||||
|
||||
/* If the SVC is handled, handle it. */
|
||||
if (!svc::ResultNotHandled::Includes(result)) {
|
||||
/* If we should stop processing the exception, restore. */
|
||||
if (svc::ResultStopProcessingException::Includes(result)) {
|
||||
svc::RestoreContext(reinterpret_cast<uintptr_t>(e_ctx));
|
||||
}
|
||||
|
||||
/* If we successfully enter jit debug, restore. */
|
||||
if (cur_process.EnterJitDebug(ams::svc::DebugEvent_Exception, exception, param2, param3)) {
|
||||
svc::RestoreContext(reinterpret_cast<uintptr_t>(e_ctx));
|
||||
}
|
||||
}
|
||||
|
||||
/* Otherwise, if result debug was returned, restore. */
|
||||
if (svc::ResultDebug::Includes(result)) {
|
||||
svc::RestoreContext(reinterpret_cast<uintptr_t>(e_ctx));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Print that an exception occurred. */
|
||||
MESOSPHERE_RELEASE_LOG("Exception occurred. %016lx\n", GetCurrentProcess().GetProgramId());
|
||||
|
||||
/* Exit the current process. */
|
||||
GetCurrentProcess().Exit();
|
||||
}
|
||||
|
||||
/* NOTE: This function is called from ASM. */
|
||||
void HandleException(KExceptionContext *context) {
|
||||
MESOSPHERE_ASSERT(!KInterruptManager::AreInterruptsEnabled());
|
||||
@ -112,14 +470,14 @@ namespace ams::kern::arch::arm64 {
|
||||
|
||||
/* Collect far and data based on the ec. */
|
||||
switch ((esr >> 26) & 0x3F) {
|
||||
case 0x0: /* Unknown */
|
||||
case 0xE: /* Illegal Execution State */
|
||||
case 0x38: /* BKPT instruction */
|
||||
case 0x3C: /* BRK instruction */
|
||||
case EsrEc_Unknown:
|
||||
case EsrEc_IllegalExecution:
|
||||
case EsrEc_BkptInstruction:
|
||||
case EsrEc_BrkInstruction:
|
||||
far = context->pc;
|
||||
data = GetInstructionData(context, esr);
|
||||
break;
|
||||
case 0x11: /* SVC instruction from Aarch32 */
|
||||
case EsrEc_Svc32:
|
||||
if (context->psr & 0x20) {
|
||||
/* Thumb mode. */
|
||||
context->pc -= 2;
|
||||
@ -129,11 +487,11 @@ namespace ams::kern::arch::arm64 {
|
||||
}
|
||||
far = context->pc;
|
||||
break;
|
||||
case 0x15: /* SVC instruction from Aarch64 */
|
||||
case EsrEc_Svc64:
|
||||
context->pc -= 4;
|
||||
far = context->pc;
|
||||
break;
|
||||
case 0x30: /* Breakpoint from lower EL */
|
||||
case EsrEc_BreakPointEl0:
|
||||
far = context->pc;
|
||||
break;
|
||||
default:
|
||||
@ -143,6 +501,8 @@ namespace ams::kern::arch::arm64 {
|
||||
|
||||
/* Note that we're in an exception handler. */
|
||||
GetCurrentThread().SetInExceptionHandler();
|
||||
|
||||
/* Verify that spsr's M is allowable (EL0t). */
|
||||
{
|
||||
const bool is_user_mode = (context->psr & 0xF) == 0;
|
||||
if (is_user_mode) {
|
||||
|
@ -40,6 +40,8 @@ namespace ams::kern::arch::arm64 {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr inline u32 El0PsrMask = 0xFF0FFE20;
|
||||
|
||||
ALWAYS_INLINE bool IsFpuEnabled() {
|
||||
return cpu::ArchitecturalFeatureAccessControlRegisterAccessor().IsFpEnabled();
|
||||
}
|
||||
@ -216,7 +218,7 @@ namespace ams::kern::arch::arm64 {
|
||||
out->lr = e_ctx->x[30];
|
||||
out->sp = e_ctx->sp;
|
||||
out->pc = e_ctx->pc;
|
||||
out->pstate = e_ctx->psr & 0xFF0FFE20;
|
||||
out->pstate = e_ctx->psr & El0PsrMask;
|
||||
|
||||
/* Get the thread's general purpose registers. */
|
||||
if (thread->IsCallingSvc()) {
|
||||
|
@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* ams::kern::svc::CallReturnFromException64(Result result) */
|
||||
.section .text._ZN3ams4kern3svc25CallReturnFromException64Ev, "ax", %progbits
|
||||
.global _ZN3ams4kern3svc25CallReturnFromException64Ev
|
||||
.type _ZN3ams4kern3svc25CallReturnFromException64Ev, %function
|
||||
_ZN3ams4kern3svc25CallReturnFromException64Ev:
|
||||
/* Save registers the SVC entry handler didn't. */
|
||||
stp x12, x13, [sp, #(8 * 12)]
|
||||
stp x14, x15, [sp, #(8 * 14)]
|
||||
stp x16, x17, [sp, #(8 * 16)]
|
||||
str x19, [sp, #(8 * 19)]
|
||||
stp x20, x21, [sp, #(8 * 20)]
|
||||
stp x22, x23, [sp, #(8 * 22)]
|
||||
stp x24, x25, [sp, #(8 * 24)]
|
||||
stp x26, x26, [sp, #(8 * 26)]
|
||||
stp x28, x29, [sp, #(8 * 28)]
|
||||
|
||||
/* Call ams::kern::arch::arm64::ReturnFromException(result). */
|
||||
bl _ZN3ams4kern4arch5arm6419ReturnFromExceptionENS_6ResultE
|
||||
|
||||
0: /* We should never reach this point. */
|
||||
b 0b
|
||||
|
||||
/* ams::kern::svc::CallReturnFromException64From32(Result result) */
|
||||
.section .text._ZN3ams4kern3svc31CallReturnFromException64From32Ev, "ax", %progbits
|
||||
.global _ZN3ams4kern3svc31CallReturnFromException64From32Ev
|
||||
.type _ZN3ams4kern3svc31CallReturnFromException64From32Ev, %function
|
||||
_ZN3ams4kern3svc31CallReturnFromException64From32Ev:
|
||||
/* Save registers the SVC entry handler didn't. */
|
||||
/* ... */
|
||||
|
||||
/* Call ams::kern::arch::arm64::ReturnFromException(result). */
|
||||
bl _ZN3ams4kern4arch5arm6419ReturnFromExceptionENS_6ResultE
|
||||
|
||||
0: /* We should never reach this point. */
|
||||
b 0b
|
||||
|
||||
|
||||
/* ams::kern::svc::RestoreContext(uintptr_t sp) */
|
||||
.section .text._ZN3ams4kern3svc14RestoreContextEm, "ax", %progbits
|
||||
.global _ZN3ams4kern3svc14RestoreContextEm
|
||||
.type _ZN3ams4kern3svc14RestoreContextEm, %function
|
||||
_ZN3ams4kern3svc14RestoreContextEm:
|
||||
/* Set the stack pointer, set daif. */
|
||||
mov sp, x0
|
||||
msr daifset, #2
|
||||
|
||||
0: /* We should handle DPC. */
|
||||
/* Check the dpc flags. */
|
||||
ldrb w8, [sp, #(0x120 + 0x10)]
|
||||
cbz w8, 1f
|
||||
|
||||
/* We have DPC to do! */
|
||||
/* Save registers and call ams::kern::KDpcManager::HandleDpc(). */
|
||||
sub sp, sp, #0x40
|
||||
stp x0, x1, [sp, #(8 * 0)]
|
||||
stp x2, x3, [sp, #(8 * 2)]
|
||||
stp x4, x5, [sp, #(8 * 4)]
|
||||
stp x6, x7, [sp, #(8 * 6)]
|
||||
bl _ZN3ams4kern11KDpcManager9HandleDpcEv
|
||||
ldp x0, x1, [sp, #(8 * 0)]
|
||||
ldp x2, x3, [sp, #(8 * 2)]
|
||||
ldp x4, x5, [sp, #(8 * 4)]
|
||||
ldp x6, x7, [sp, #(8 * 6)]
|
||||
add sp, sp, #0x40
|
||||
b 0b
|
||||
|
||||
1: /* We're done with DPC, and should return from the svc. */
|
||||
/* Clear our in-SVC note. */
|
||||
strb wzr, [sp, #(0x120 + 0x12)]
|
||||
|
||||
/* Restore registers. */
|
||||
ldp x30, x8, [sp, #(8 * 30)]
|
||||
ldp x9, x10, [sp, #(8 * 32)]
|
||||
ldr x11, [sp, #(8 * 34)]
|
||||
msr sp_el0, x8
|
||||
msr elr_el1, x9
|
||||
msr spsr_el1, x10
|
||||
msr tpidr_el0, x11
|
||||
ldp x0, x1, [sp, #(8 * 0)]
|
||||
ldp x2, x3, [sp, #(8 * 2)]
|
||||
ldp x4, x5, [sp, #(8 * 4)]
|
||||
ldp x6, x7, [sp, #(8 * 6)]
|
||||
ldp x8, x9, [sp, #(8 * 8)]
|
||||
ldp x10, x11, [sp, #(8 * 10)]
|
||||
ldp x12, x13, [sp, #(8 * 12)]
|
||||
ldp x14, x15, [sp, #(8 * 14)]
|
||||
ldp x16, x17, [sp, #(8 * 16)]
|
||||
ldp x18, x19, [sp, #(8 * 18)]
|
||||
ldp x20, x21, [sp, #(8 * 20)]
|
||||
ldp x22, x23, [sp, #(8 * 22)]
|
||||
ldp x24, x25, [sp, #(8 * 24)]
|
||||
ldp x26, x27, [sp, #(8 * 26)]
|
||||
ldp x28, x29, [sp, #(8 * 28)]
|
||||
|
||||
/* Return. */
|
||||
add sp, sp, #0x120
|
||||
eret
|
@ -29,6 +29,10 @@ namespace ams::kern::svc {
|
||||
void CallReplyAndReceiveLight64();
|
||||
void CallReplyAndReceiveLight64From32();
|
||||
|
||||
/* Declare special prototypes for ReturnFromException. */
|
||||
void CallReturnFromException64();
|
||||
void CallReturnFromException64From32();
|
||||
|
||||
namespace {
|
||||
|
||||
#ifndef MESOSPHERE_USE_STUBBED_SVC_TABLES
|
||||
@ -69,6 +73,8 @@ namespace ams::kern::svc {
|
||||
table[svc::SvcId_SendSyncRequestLight] = CallSendSyncRequestLight64From32;
|
||||
table[svc::SvcId_ReplyAndReceiveLight] = CallReplyAndReceiveLight64From32;
|
||||
|
||||
table[svc::SvcId_ReturnFromException] = CallReturnFromException64From32;
|
||||
|
||||
return table;
|
||||
}();
|
||||
|
||||
@ -83,6 +89,8 @@ namespace ams::kern::svc {
|
||||
table[svc::SvcId_SendSyncRequestLight] = CallSendSyncRequestLight64;
|
||||
table[svc::SvcId_ReplyAndReceiveLight] = CallReplyAndReceiveLight64;
|
||||
|
||||
table[svc::SvcId_ReturnFromException] = CallReturnFromException64;
|
||||
|
||||
return table;
|
||||
}();
|
||||
|
||||
|
@ -704,7 +704,47 @@ namespace ams::kern {
|
||||
}
|
||||
|
||||
bool KProcess::EnterUserException() {
|
||||
MESOSPHERE_UNIMPLEMENTED();
|
||||
/* Get the current thread. */
|
||||
KThread *cur_thread = GetCurrentThreadPointer();
|
||||
MESOSPHERE_ASSERT(this == cur_thread->GetOwnerProcess());
|
||||
|
||||
/* Try to claim the exception thread. */
|
||||
if (this->exception_thread != cur_thread) {
|
||||
const uintptr_t address_key = reinterpret_cast<uintptr_t>(std::addressof(this->exception_thread));
|
||||
while (true) {
|
||||
{
|
||||
KScopedSchedulerLock sl;
|
||||
|
||||
/* If the thread is terminating, it can't enter. */
|
||||
if (cur_thread->IsTerminationRequested()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* If we have no exception thread, we succeeded. */
|
||||
if (this->exception_thread == nullptr) {
|
||||
this->exception_thread = cur_thread;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Otherwise, wait for us to not have an exception thread. */
|
||||
cur_thread->SetAddressKey(address_key);
|
||||
this->exception_thread->AddWaiter(cur_thread);
|
||||
if (cur_thread->GetState() == KThread::ThreadState_Runnable) {
|
||||
cur_thread->SetState(KThread::ThreadState_Waiting);
|
||||
}
|
||||
}
|
||||
/* Remove the thread as a waiter from the lock owner. */
|
||||
{
|
||||
KScopedSchedulerLock sl;
|
||||
KThread *owner_thread = cur_thread->GetLockOwner();
|
||||
if (owner_thread != nullptr) {
|
||||
owner_thread->RemoveWaiter(cur_thread);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool KProcess::LeaveUserException() {
|
||||
@ -715,8 +755,18 @@ namespace ams::kern {
|
||||
KScopedSchedulerLock sl;
|
||||
|
||||
if (this->exception_thread == thread) {
|
||||
/* TODO */
|
||||
MESOSPHERE_UNIMPLEMENTED();
|
||||
this->exception_thread = nullptr;
|
||||
|
||||
/* Remove waiter thread. */
|
||||
s32 num_waiters;
|
||||
KThread *next = thread->RemoveWaiterByKey(std::addressof(num_waiters), reinterpret_cast<uintptr_t>(std::addressof(this->exception_thread)));
|
||||
if (next != nullptr) {
|
||||
if (next->GetState() == KThread::ThreadState_Waiting) {
|
||||
next->SetState(KThread::ThreadState_Runnable);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
@ -79,18 +79,10 @@ namespace ams::kern::svc {
|
||||
return Break(break_reason, arg, size);
|
||||
}
|
||||
|
||||
void ReturnFromException64(ams::Result result) {
|
||||
MESOSPHERE_PANIC("Stubbed SvcReturnFromException64 was called.");
|
||||
}
|
||||
|
||||
/* ============================= 64From32 ABI ============================= */
|
||||
|
||||
void Break64From32(ams::svc::BreakReason break_reason, ams::svc::Address arg, ams::svc::Size size) {
|
||||
return Break(break_reason, arg, size);
|
||||
}
|
||||
|
||||
void ReturnFromException64From32(ams::Result result) {
|
||||
MESOSPHERE_PANIC("Stubbed SvcReturnFromException64From32 was called.");
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -548,6 +548,7 @@ namespace ams::svc {
|
||||
struct ExceptionInfoStatus64 {
|
||||
u32 pstate;
|
||||
u32 afsr0;
|
||||
u32 afsr1;
|
||||
u32 esr;
|
||||
u32 far;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user