kern: Add real SvcBreak implementation

This commit is contained in:
Michael Scire 2020-07-31 00:04:43 -07:00 committed by SciresM
parent 4c3c910774
commit 325802e29d
5 changed files with 160 additions and 11 deletions

View File

@ -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. */

View File

@ -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();

View File

@ -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); \

View File

@ -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());

View File

@ -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();
}
}