mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2024-12-19 00:42:06 +00:00
kern: Add real SvcBreak implementation
This commit is contained in:
parent
4c3c910774
commit
325802e29d
5 changed files with 160 additions and 11 deletions
|
@ -35,6 +35,10 @@ namespace ams::kern::arch::arm64 {
|
||||||
|
|
||||||
static void PostDestroy(uintptr_t arg) { /* ... */ }
|
static void PostDestroy(uintptr_t arg) { /* ... */ }
|
||||||
public:
|
public:
|
||||||
|
static uintptr_t GetProgramCounter(const KThread &thread);
|
||||||
|
static void SetPreviousProgramCounter();
|
||||||
|
|
||||||
|
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 Result SetHardwareBreakPoint(ams::svc::HardwareBreakPointRegisterName name, u64 flags, u64 value);
|
||||||
|
|
||||||
/* TODO: This is a placeholder definition. */
|
/* TODO: This is a placeholder definition. */
|
||||||
|
|
|
@ -193,6 +193,8 @@ namespace ams::kern {
|
||||||
KProcess::State SetDebugObject(void *debug_object);
|
KProcess::State SetDebugObject(void *debug_object);
|
||||||
void ClearDebugObject(KProcess::State state);
|
void ClearDebugObject(KProcess::State state);
|
||||||
|
|
||||||
|
bool EnterJitDebug(ams::svc::DebugEvent event, ams::svc::DebugException exception, uintptr_t param1 = 0, uintptr_t param2 = 0, uintptr_t param3 = 0, uintptr_t param4 = 0);
|
||||||
|
|
||||||
KEventInfo *GetJitDebugInfo();
|
KEventInfo *GetJitDebugInfo();
|
||||||
void ClearJitDebugInfo();
|
void ClearJitDebugInfo();
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,36 @@ namespace ams::kern::arch::arm64 {
|
||||||
static_assert(ForbiddenWatchPointFlagsMask == 0xFFFFFFFF00F0E006ul);
|
static_assert(ForbiddenWatchPointFlagsMask == 0xFFFFFFFF00F0E006ul);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uintptr_t KDebug::GetProgramCounter(const KThread &thread) {
|
||||||
|
return GetExceptionContext(std::addressof(thread))->pc;
|
||||||
|
}
|
||||||
|
|
||||||
|
void KDebug::SetPreviousProgramCounter() {
|
||||||
|
/* Get the current thread. */
|
||||||
|
KThread *thread = GetCurrentThreadPointer();
|
||||||
|
MESOSPHERE_ASSERT(thread->IsCallingSvc());
|
||||||
|
|
||||||
|
/* Get the exception context. */
|
||||||
|
KExceptionContext *e_ctx = GetExceptionContext(thread);
|
||||||
|
|
||||||
|
/* Set the previous pc. */
|
||||||
|
if (e_ctx->write == 0) {
|
||||||
|
/* Subtract from the program counter. */
|
||||||
|
if (thread->GetOwnerProcess()->Is64Bit()) {
|
||||||
|
e_ctx->pc -= sizeof(u32);
|
||||||
|
} else {
|
||||||
|
e_ctx->pc -= (e_ctx->psr & 0x20) ? sizeof(u16) : sizeof(u32);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mark that we've set. */
|
||||||
|
e_ctx->write = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Result KDebug::BreakIfAttached(ams::svc::BreakReason break_reason, uintptr_t address, size_t size) {
|
||||||
|
return KDebugBase::OnDebugEvent(ams::svc::DebugEvent_Exception, ams::svc::DebugException_UserBreak, GetProgramCounter(GetCurrentThread()), break_reason, address, size);
|
||||||
|
}
|
||||||
|
|
||||||
#define MESOSPHERE_SET_HW_BREAK_POINT(ID, FLAGS, VALUE) \
|
#define MESOSPHERE_SET_HW_BREAK_POINT(ID, FLAGS, VALUE) \
|
||||||
({ \
|
({ \
|
||||||
cpu::SetDbgBcr##ID##El1(0); \
|
cpu::SetDbgBcr##ID##El1(0); \
|
||||||
|
|
|
@ -465,7 +465,7 @@ namespace ams::kern {
|
||||||
|
|
||||||
KScopedSchedulerLock sl;
|
KScopedSchedulerLock sl;
|
||||||
|
|
||||||
if (this->state == State_Running || this->state == State_RunningAttached|| this->state == State_Crashed || this->state == State_DebugBreak) {
|
if (this->state == State_Running || this->state == State_RunningAttached || this->state == State_Crashed || this->state == State_DebugBreak) {
|
||||||
this->ChangeState(State_Terminating);
|
this->ChangeState(State_Terminating);
|
||||||
needs_terminate = true;
|
needs_terminate = true;
|
||||||
}
|
}
|
||||||
|
@ -999,6 +999,90 @@ namespace ams::kern {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool KProcess::EnterJitDebug(ams::svc::DebugEvent event, ams::svc::DebugException exception, uintptr_t param1, uintptr_t param2, uintptr_t param3, uintptr_t param4) {
|
||||||
|
/* Check that we're the current process. */
|
||||||
|
MESOSPHERE_ASSERT_THIS();
|
||||||
|
MESOSPHERE_ASSERT(this == GetCurrentProcessPointer());
|
||||||
|
|
||||||
|
/* If we aren't allowed to enter jit debug, don't. */
|
||||||
|
if ((this->flags & ams::svc::CreateProcessFlag_EnableDebug) == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We're the current process, so we should be some kind of running. */
|
||||||
|
MESOSPHERE_ASSERT(this->state != State_Created);
|
||||||
|
MESOSPHERE_ASSERT(this->state != State_CreatedAttached);
|
||||||
|
MESOSPHERE_ASSERT(this->state != State_Terminated);
|
||||||
|
|
||||||
|
/* Try to enter JIT debug. */
|
||||||
|
while (true) {
|
||||||
|
/* Lock ourselves and the scheduler. */
|
||||||
|
KScopedLightLock lk(this->state_lock);
|
||||||
|
KScopedLightLock list_lk(this->list_lock);
|
||||||
|
KScopedSchedulerLock sl;
|
||||||
|
|
||||||
|
/* If we're attached to a debugger, we're necessarily in debug. */
|
||||||
|
if (this->IsAttachedToDebugger()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If the current thread is terminating, we can't enter debug. */
|
||||||
|
if (GetCurrentThread().IsTerminationRequested()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We're not attached to debugger, so check that. */
|
||||||
|
MESOSPHERE_ASSERT(this->state != State_RunningAttached);
|
||||||
|
MESOSPHERE_ASSERT(this->state != State_DebugBreak);
|
||||||
|
|
||||||
|
/* If we're terminating, we can't enter debug. */
|
||||||
|
if (this->state != State_Running && this->state != State_Crashed) {
|
||||||
|
MESOSPHERE_ASSERT(this->state == State_Terminating);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If the current thread is suspended, retry. */
|
||||||
|
if (GetCurrentThread().IsSuspended()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Suspend all our threads. */
|
||||||
|
{
|
||||||
|
auto end = this->GetThreadList().end();
|
||||||
|
for (auto it = this->GetThreadList().begin(); it != end; ++it) {
|
||||||
|
it->RequestSuspend(KThread::SuspendType_Debug);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Change our state to crashed. */
|
||||||
|
this->ChangeState(State_Crashed);
|
||||||
|
|
||||||
|
/* Enter jit debug. */
|
||||||
|
this->is_jit_debug = true;
|
||||||
|
this->jit_debug_event_type = event;
|
||||||
|
this->jit_debug_exception_type = exception;
|
||||||
|
this->jit_debug_params[0] = param1;
|
||||||
|
this->jit_debug_params[1] = param2;
|
||||||
|
this->jit_debug_params[2] = param3;
|
||||||
|
this->jit_debug_params[3] = param4;
|
||||||
|
this->jit_debug_thread_id = GetCurrentThread().GetId();
|
||||||
|
|
||||||
|
/* Exit our retry loop. */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if our state indicates we're in jit debug. */
|
||||||
|
{
|
||||||
|
KScopedSchedulerLock sl;
|
||||||
|
|
||||||
|
if (this->state == State_Running || this->state == State_RunningAttached || this->state == State_Crashed || this->state == State_DebugBreak) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
KEventInfo *KProcess::GetJitDebugInfo() {
|
KEventInfo *KProcess::GetJitDebugInfo() {
|
||||||
MESOSPHERE_ASSERT_THIS();
|
MESOSPHERE_ASSERT_THIS();
|
||||||
MESOSPHERE_ASSERT(KScheduler::IsSchedulerLockedByCurrentThread());
|
MESOSPHERE_ASSERT(KScheduler::IsSchedulerLockedByCurrentThread());
|
||||||
|
|
|
@ -21,25 +21,54 @@ namespace ams::kern::svc {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
void Break(ams::svc::BreakReason break_reason, uintptr_t address, size_t size) {
|
[[maybe_unused]] void PrintBreak(ams::svc::BreakReason break_reason) {
|
||||||
/* Log for debug that Break was called. */
|
/* Print that break was called. */
|
||||||
MESOSPHERE_LOG("%s: Break(%08x, %016lx, %zu)\n", GetCurrentProcess().GetName(), static_cast<u32>(break_reason), address, size);
|
MESOSPHERE_RELEASE_LOG("%s: svc::Break(%d) was called, pid=%ld, tid=%ld\n", GetCurrentProcess().GetName(), static_cast<s32>(break_reason), GetCurrentProcess().GetId(), GetCurrentThread().GetId());
|
||||||
|
|
||||||
/* If the current process is attached to debugger, notify it. */
|
/* Print the current thread's registers. */
|
||||||
|
/* TODO: KDebug::PrintRegisters(); */
|
||||||
|
|
||||||
|
/* Print a backtrace. */
|
||||||
|
/* TODO: KDebug::PrintBacktrace(); */
|
||||||
|
}
|
||||||
|
|
||||||
|
void Break(ams::svc::BreakReason break_reason, uintptr_t address, size_t size) {
|
||||||
|
/* Determine whether the break is only a notification. */
|
||||||
|
const bool is_notification = (break_reason & ams::svc::BreakReason_NotificationOnlyFlag) != 0;
|
||||||
|
|
||||||
|
/* If the break isn't a notification, print it. */
|
||||||
|
if (!is_notification) {
|
||||||
|
#ifdef MESOSPHERE_BUILD_FOR_DEBUGGING
|
||||||
|
PrintBreak(break_reason);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If the current process is attached to debugger, try to notify it. */
|
||||||
if (GetCurrentProcess().IsAttachedToDebugger()) {
|
if (GetCurrentProcess().IsAttachedToDebugger()) {
|
||||||
MESOSPHERE_UNIMPLEMENTED();
|
if (R_SUCCEEDED(KDebug::BreakIfAttached(break_reason, address, size))) {
|
||||||
|
/* If we attached, set the pc to the instruction before the current one and return. */
|
||||||
|
KDebug::SetPreviousProgramCounter();
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If the break is only a notification, we're done. */
|
/* If the break is only a notification, we're done. */
|
||||||
if ((break_reason & ams::svc::BreakReason_NotificationOnlyFlag) != 0) {
|
if (is_notification) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO */
|
/* Print that break was called. */
|
||||||
if (size == sizeof(u32)) {
|
MESOSPHERE_RELEASE_LOG("Break() called. %016lx\n", GetCurrentProcess().GetProgramId());
|
||||||
MESOSPHERE_LOG("DEBUG: %08x\n", *reinterpret_cast<u32 *>(address));
|
|
||||||
|
/* Try to enter JIT debug state. */
|
||||||
|
if (GetCurrentProcess().EnterJitDebug(ams::svc::DebugEvent_Exception, ams::svc::DebugException_UserBreak, KDebug::GetProgramCounter(GetCurrentThread()), break_reason, address, size)) {
|
||||||
|
/* We entered JIT debug, so set the pc to the instruction before the current one and return. */
|
||||||
|
KDebug::SetPreviousProgramCounter();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
MESOSPHERE_PANIC("Break was called\n");
|
|
||||||
|
/* Exit the current process. */
|
||||||
|
GetCurrentProcess().Exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue