mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-12-22 20:31:14 +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) { /* ... */ }
|
||||
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);
|
||||
|
||||
/* TODO: This is a placeholder definition. */
|
||||
|
|
|
@ -193,6 +193,8 @@ namespace ams::kern {
|
|||
KProcess::State SetDebugObject(void *debug_object);
|
||||
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();
|
||||
void ClearJitDebugInfo();
|
||||
|
||||
|
|
|
@ -38,6 +38,36 @@ namespace ams::kern::arch::arm64 {
|
|||
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) \
|
||||
({ \
|
||||
cpu::SetDbgBcr##ID##El1(0); \
|
||||
|
|
|
@ -465,7 +465,7 @@ namespace ams::kern {
|
|||
|
||||
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);
|
||||
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() {
|
||||
MESOSPHERE_ASSERT_THIS();
|
||||
MESOSPHERE_ASSERT(KScheduler::IsSchedulerLockedByCurrentThread());
|
||||
|
|
|
@ -21,25 +21,54 @@ namespace ams::kern::svc {
|
|||
|
||||
namespace {
|
||||
|
||||
void Break(ams::svc::BreakReason break_reason, uintptr_t address, size_t size) {
|
||||
/* Log for debug that Break was called. */
|
||||
MESOSPHERE_LOG("%s: Break(%08x, %016lx, %zu)\n", GetCurrentProcess().GetName(), static_cast<u32>(break_reason), address, size);
|
||||
[[maybe_unused]] void PrintBreak(ams::svc::BreakReason break_reason) {
|
||||
/* Print that break was called. */
|
||||
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()) {
|
||||
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 ((break_reason & ams::svc::BreakReason_NotificationOnlyFlag) != 0) {
|
||||
if (is_notification) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* TODO */
|
||||
if (size == sizeof(u32)) {
|
||||
MESOSPHERE_LOG("DEBUG: %08x\n", *reinterpret_cast<u32 *>(address));
|
||||
/* Print that break was called. */
|
||||
MESOSPHERE_RELEASE_LOG("Break() called. %016lx\n", GetCurrentProcess().GetProgramId());
|
||||
|
||||
/* 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