From abeaa72f94e98bf3c4d13cfe7dc150c2bdb0af73 Mon Sep 17 00:00:00 2001 From: TuxSH <1922548+TuxSH@users.noreply.github.com> Date: Thu, 23 Jan 2020 02:15:03 +0000 Subject: [PATCH] thermosphere: some gdb/debug refactor --- thermosphere/src/exceptions.h | 17 + thermosphere/src/gdb.c | 2 +- thermosphere/src/gdb.h | 8 +- thermosphere/src/gdb/debug.c | 602 +++++++--------------------------- thermosphere/src/gdb/debug.h | 30 +- thermosphere/src/irq.c | 1 - 6 files changed, 164 insertions(+), 496 deletions(-) diff --git a/thermosphere/src/exceptions.h b/thermosphere/src/exceptions.h index 9cd398845..6d8ecec01 100644 --- a/thermosphere/src/exceptions.h +++ b/thermosphere/src/exceptions.h @@ -135,6 +135,23 @@ static inline void writeFrameRegisterZ(ExceptionStackFrame *frame, u32 id, u64 v } } +static inline uintptr_t exceptionGetSp(const ExceptionStackFrame *frame) +{ + // Note: the return value is more or less meaningless if we took an exception from A32... + // We try our best to reflect which privilege level the exception was took from, nonetheless + + bool spEl0; + u64 m = frame->spsr_el2 & 0xF; + if (spsrIsA32(frame->spsr_el2)) { + spEl0 = m == 0; + } else { + u64 el = m >> 2; + spEl0 = el == 2 || el == 0 || (m & 1) == 0; // note: frame->sp_el2 is aliased to frame->sp_el0 + } + + return spEl0 ? frame->sp_el0 : frame->sp_el1; +} + bool spsrEvaluateConditionCode(u64 spsr, u32 conditionCode); void skipFaultingInstruction(ExceptionStackFrame *frame, u32 size); void dumpStackFrame(const ExceptionStackFrame *frame, bool sameEl); diff --git a/thermosphere/src/gdb.c b/thermosphere/src/gdb.c index 2154f354b..375a54a62 100644 --- a/thermosphere/src/gdb.c +++ b/thermosphere/src/gdb.c @@ -5,7 +5,7 @@ * SPDX-License-Identifier: (MIT OR GPL-2.0-or-later) */ -#if 1 +#if 0 #include "gdb.h" #include "gdb/net.h" #include "gdb/server.h" diff --git a/thermosphere/src/gdb.h b/thermosphere/src/gdb.h index 2bad5eb46..db75856b8 100644 --- a/thermosphere/src/gdb.h +++ b/thermosphere/src/gdb.h @@ -53,7 +53,8 @@ enum { GDB_FLAG_CONTINUING = BIT(4), GDB_FLAG_TERMINATE = BIT(5), GDB_FLAG_ATTACHED_AT_START = BIT(6), - GDB_FLAG_CREATED = BIT(7), + GDB_FLAG_NONSTOP = BIT(7), + //GDB_FLAG_CREATED = BIT(7), }; typedef enum GDBState @@ -106,5 +107,10 @@ void GDB_FinalizeContext(GDBContext *ctx); void GDB_Attach(GDBContext *ctx); void GDB_Detach(GDBContext *ctx); +static inline bool GDB_IsAttached(GDBContext *ctx) +{ + return ctx->state == GDB_STATE_ATTACHED; +} + GDB_DECLARE_HANDLER(Unsupported); GDB_DECLARE_HANDLER(EnableExtendedMode); diff --git a/thermosphere/src/gdb/debug.c b/thermosphere/src/gdb/debug.c index 2f2baed3d..60b18dc91 100644 --- a/thermosphere/src/gdb/debug.c +++ b/thermosphere/src/gdb/debug.c @@ -6,145 +6,23 @@ */ #define _GNU_SOURCE // for strchrnul -#include "gdb/debug.h" -#include "gdb/server.h" -#include "gdb/verbose.h" -#include "gdb/net.h" -#include "gdb/thread.h" -#include "gdb/mem.h" -#include "gdb/hio.h" -#include "gdb/watchpoints.h" -#include "fmt.h" +#include +#include +#include "../exceptions.h" +#include "../watchpoints.h" + +#include "debug.h" +#include "net.h" + +#include "server.h" +#include "verbose.h" +#include "thread.h" +#include "mem.h" +#include "hio.h" +#include "watchpoints.h" #include #include -#include "pmdbgext.h" - -static void GDB_DetachImmediatelyExtended(GDBContext *ctx) -{ - // detach immediately - recursiveSpinlockLock(&ctx->lock); - ctx->state = GDB_STATE_DETACHING; - - svcClearEvent(ctx->processAttachedEvent); - ctx->eventToWaitFor = ctx->processAttachedEvent; - - svcClearEvent(ctx->parent->statusUpdateReceived); - svcSignalEvent(ctx->parent->statusUpdated); - recursiveSpinlockUnlock(&ctx->lock); - - svcWaitSynchronization(ctx->parent->statusUpdateReceived, -1LL); - - recursiveSpinlockLock(&ctx->lock); - GDB_DetachFromProcess(ctx); - ctx->flags &= GDB_FLAG_RESTART_MASK; - recursiveSpinlockUnlock(&ctx->lock); -} - -GDB_DECLARE_VERBOSE_HANDLER(Run) -{ - // Note: only titleId [mediaType [launchFlags]] is supported, and the launched title shouldn't rely on APT - // all 3 parameters should be hex-encoded. - - // Extended remote only - if (!(ctx->flags & GDB_FLAG_EXTENDED_REMOTE)) - return GDB_ReplyErrno(ctx, EPERM); - - u64 titleId; - u32 mediaType = MEDIATYPE_NAND; - u32 launchFlags = PMLAUNCHFLAG_LOAD_DEPENDENCIES; - - char args[3][32] = {{0}}; - char *pos = ctx->commandData; - for (u32 i = 0; i < 3 && *pos != 0; i++) - { - char *pos2 = strchrnul(pos, ';'); - u32 dist = pos2 - pos; - if (dist < 2) - return GDB_ReplyErrno(ctx, EILSEQ); - if (dist % 2 == 1) - return GDB_ReplyErrno(ctx, EILSEQ); - - if (dist / 2 > 16) // buffer overflow check - return GDB_ReplyErrno(ctx, EINVAL); - - u32 n = GDB_DecodeHex(args[i], pos, dist / 2); - if (n == 0) - return GDB_ReplyErrno(ctx, EILSEQ); - pos = *pos2 == 0 ? pos2 : pos2 + 1; - } - - if (args[0][0] == 0) - return GDB_ReplyErrno(ctx, EINVAL); // first arg mandatory - - if (GDB_ParseIntegerList64(&titleId, args[0], 1, 0, 0, 16, false) == NULL) - return GDB_ReplyErrno(ctx, EINVAL); - - if (args[1][0] != 0 && (GDB_ParseIntegerList(&mediaType, args[1], 1, 0, 0, 16, true) == NULL || mediaType >= 0x100)) - return GDB_ReplyErrno(ctx, EINVAL); - - if (args[2][0] != 0 && GDB_ParseIntegerList(&launchFlags, args[2], 1, 0, 0, 16, true) == NULL) - return GDB_ReplyErrno(ctx, EINVAL); - - FS_ProgramInfo progInfo; - progInfo.mediaType = (FS_MediaType)mediaType; - progInfo.programId = titleId; - - recursiveSpinlockLock(&ctx->lock); - Result r = GDB_CreateProcess(ctx, &progInfo, launchFlags); - - if (R_FAILED(r)) - { - if(ctx->debug != 0) - GDB_DetachImmediatelyExtended(ctx); - recursiveSpinlockUnlock(&ctx->lock); - return GDB_ReplyErrno(ctx, EPERM); - } - - recursiveSpinlockUnlock(&ctx->lock); - return R_SUCCEEDED(r) ? GDB_SendStopReply(ctx, &ctx->latestDebugEvent) : GDB_ReplyErrno(ctx, EPERM); -} - -GDB_DECLARE_HANDLER(Restart) -{ - // Note: removed from gdb - // Extended remote only & process must have been created - if (!(ctx->flags & GDB_FLAG_EXTENDED_REMOTE) || !(ctx->flags & GDB_FLAG_CREATED)) - return GDB_ReplyErrno(ctx, EPERM); - - FS_ProgramInfo progInfo = ctx->launchedProgramInfo; - u32 launchFlags = ctx->launchedProgramLaunchFlags; - - ctx->flags |= GDB_FLAG_TERMINATE; - if (ctx->flags & GDB_FLAG_EXTENDED_REMOTE) - GDB_DetachImmediatelyExtended(ctx); - - recursiveSpinlockLock(&ctx->lock); - Result r = GDB_CreateProcess(ctx, &progInfo, launchFlags); - if (R_FAILED(r) && ctx->debug != 0) - GDB_DetachImmediatelyExtended(ctx); - recursiveSpinlockUnlock(&ctx->lock); - return 0; -} - -GDB_DECLARE_VERBOSE_HANDLER(Attach) -{ - // Extended remote only - if (!(ctx->flags & GDB_FLAG_EXTENDED_REMOTE)) - return GDB_ReplyErrno(ctx, EPERM); - - u32 pid; - if(GDB_ParseHexIntegerList(&pid, ctx->commandData, 1, 0) == NULL) - return GDB_ReplyErrno(ctx, EILSEQ); - - recursiveSpinlockLock(&ctx->lock); - ctx->pid = pid; - Result r = GDB_AttachToProcess(ctx); - if(R_FAILED(r)) - GDB_DetachImmediatelyExtended(ctx); - recursiveSpinlockUnlock(&ctx->lock); - return R_SUCCEEDED(r) ? GDB_SendStopReply(ctx, &ctx->latestDebugEvent) : GDB_ReplyErrno(ctx, EPERM); -} /* Since we can't select particular threads to continue (and that's uncompliant behavior): @@ -155,8 +33,6 @@ GDB_DECLARE_VERBOSE_HANDLER(Attach) GDB_DECLARE_HANDLER(Detach) { ctx->state = GDB_STATE_DETACHING; - if (ctx->flags & GDB_FLAG_EXTENDED_REMOTE) - GDB_DetachImmediatelyExtended(ctx); return GDB_ReplyOk(ctx); } @@ -164,14 +40,13 @@ GDB_DECLARE_HANDLER(Kill) { ctx->state = GDB_STATE_DETACHING; ctx->flags |= GDB_FLAG_TERMINATE; - if (ctx->flags & GDB_FLAG_EXTENDED_REMOTE) - GDB_DetachImmediatelyExtended(ctx); return 0; } GDB_DECLARE_HANDLER(Break) { + // TODO if(!(ctx->flags & GDB_FLAG_CONTINUING)) // Is this ever reached? return GDB_SendPacket(ctx, "S02", 3); else @@ -190,6 +65,7 @@ void GDB_ContinueExecution(GDBContext *ctx) GDB_DECLARE_HANDLER(Continue) { + // TODO char *addrStart = NULL; u32 addr = 0; @@ -232,6 +108,7 @@ GDB_DECLARE_HANDLER(Continue) GDB_DECLARE_VERBOSE_HANDLER(Continue) { + // TODO char *pos = ctx->commandData; bool currentThreadFound = false; while(pos != NULL && *pos != 0 && !currentThreadFound) @@ -273,388 +150,137 @@ GDB_DECLARE_HANDLER(GetStopReason) return GDB_SendPacket(ctx, "W00", 3); } else if (ctx->processEnded && !ctx->processExited) { return GDB_SendPacket(ctx, "X0f", 3); - } else if (ctx->debug == 0) { + } else if (!GDB_IsAttached(ctx)) { return GDB_SendPacket(ctx, "W00", 3); } else { - return GDB_SendStopReply(ctx, &ctx->latestDebugEvent); + // TODO //return GDB_SendStopReply(ctx, &ctx->latestDebugEvent); } } -static int GDB_ParseCommonThreadInfo(char *out, GDBContext *ctx, int sig) +static int GDB_ParseExceptionFrame(char *out, const DebugEventInfo *info, int sig) { - u32 threadId = ctx->currentThreadId; - ThreadContext regs; - s64 dummy; - u32 core; - Result r = svcGetDebugThreadContext(®s, ctx->debug, threadId, THREADCONTEXT_CONTROL_ALL); - int n = sprintf(out, "T%02xthread:%lx;", sig, threadId); + u32 coreId = info->coreId; + ExceptionStackFrame *frame = info->frame; - if(R_FAILED(r)) - return n; + int n = sprintf(out, "T%02xthread:%lx;core:%lx;", sig, 1 + coreId, coreId); - r = svcGetDebugThreadParam(&dummy, &core, ctx->debug, ctx->currentThreadId, DBGTHREAD_PARAMETER_CPU_CREATOR); // Creator = "first ran, and running the thread" - - if(R_SUCCEEDED(r)) - n += sprintf(out + n, "core:%lx;", core); - - for(u32 i = 0; i <= 12; i++) - n += sprintf(out + n, "%lx:%08lx;", i, __builtin_bswap32(regs.cpu_registers.r[i])); - - n += sprintf(out + n, "d:%08lx;e:%08lx;f:%08lx;19:%08lx;", - __builtin_bswap32(regs.cpu_registers.sp), __builtin_bswap32(regs.cpu_registers.lr), __builtin_bswap32(regs.cpu_registers.pc), - __builtin_bswap32(regs.cpu_registers.cpsr)); - - for(u32 i = 0; i < 16; i++) - { - u64 val; - memcpy(&val, ®s.fpu_registers.d[i], 8); - n += sprintf(out + n, "%lx:%016llx;", 26 + i, __builtin_bswap64(val)); + // Dump the GPRs & sp & pc & cpsr (cpsr is 32-bit in the xml desc) + // For performance reasons, we don't include the FPU registers here + for (u32 i = 0; i < 31; i++) { + n += sprintf(out + n, "%x:%016lx;", i, __builtin_bswap64(readFrameRegister(frame, i))); } - n += sprintf(out + n, "2a:%08lx;2b:%08lx;", __builtin_bswap32(regs.fpu_registers.fpscr), __builtin_bswap32(regs.fpu_registers.fpexc)); + n += sprintf( + out + n, + "1f:%016lx;20:%016lx;21:%08x", + __builtin_bswap64(exceptionGetSp(frame)), + __builitin_bswap32((u32)frame->spsr_el2) + ); return n; } -void GDB_PreprocessDebugEvent(GDBContext *ctx, DebugEventInfo *info) -{ - switch(info->type) - { - case DBGEVENT_ATTACH_PROCESS: - { - ctx->pid = info->attach_process.process_id; - break; - } - - case DBGEVENT_ATTACH_THREAD: - { - if(ctx->nbThreads == MAX_DEBUG_THREAD) - svcBreak(USERBREAK_ASSERT); - else - { - ++ctx->totalNbCreatedThreads; - ctx->threadInfos[ctx->nbThreads].id = info->thread_id; - ctx->threadInfos[ctx->nbThreads++].tls = info->attach_thread.thread_local_storage; - } - - break; - } - - case DBGEVENT_EXIT_THREAD: - { - u32 i; - for(i = 0; i < ctx->nbThreads && ctx->threadInfos[i].id != info->thread_id; i++); - if(i == ctx->nbThreads || ctx->threadInfos[i].id != info->thread_id) - svcBreak(USERBREAK_ASSERT); - else - { - for(u32 j = i; j < ctx->nbThreads - 1; j++) - memcpy(ctx->threadInfos + j, ctx->threadInfos + j + 1, sizeof(ThreadInfo)); - memset(ctx->threadInfos + --ctx->nbThreads, 0, sizeof(ThreadInfo)); - } - - break; - } - - case DBGEVENT_EXIT_PROCESS: - { - ctx->processEnded = true; - ctx->processExited = info->exit_process.reason == EXITPROCESS_EVENT_EXIT; - - break; - } - - case DBGEVENT_OUTPUT_STRING: - { - if(info->output_string.string_addr >= 0xFFFFFFFE) - { - u32 sz = info->output_string.string_size, addr = info->output_string.string_addr, threadId = info->thread_id; - memset(info, 0, sizeof(DebugEventInfo)); - info->type = (addr == 0xFFFFFFFF) ? DBGEVENT_SYSCALL_OUT : DBGEVENT_SYSCALL_IN; - info->thread_id = threadId; - info->flags = 1; - info->syscall.syscall = sz; - } - else if (info->output_string.string_size == 0) - GDB_FetchPackedHioRequest(ctx, info->output_string.string_addr); - - break; - } - - case DBGEVENT_EXCEPTION: - { - switch(info->exception.type) - { - case EXCEVENT_UNDEFINED_INSTRUCTION: - { - // kernel bugfix for thumb mode - ThreadContext regs; - Result r = svcGetDebugThreadContext(®s, ctx->debug, info->thread_id, THREADCONTEXT_CONTROL_CPU_SPRS); - if(R_SUCCEEDED(r) && (regs.cpu_registers.cpsr & 0x20) != 0) - { - regs.cpu_registers.pc += 2; - r = svcSetDebugThreadContext(ctx->debug, info->thread_id, ®s, THREADCONTEXT_CONTROL_CPU_SPRS); - } - - break; - } - - default: - break; - } - } - default: - break; - } -} - int GDB_SendStopReply(GDBContext *ctx, const DebugEventInfo *info) { char buffer[GDB_BUF_LEN + 1]; + int n; - switch(info->type) - { - case DBGEVENT_ATTACH_PROCESS: - break; // Dismissed + buffer[0] = 0; - case DBGEVENT_ATTACH_THREAD: - { - if((ctx->flags & GDB_FLAG_ATTACHED_AT_START) && ctx->totalNbCreatedThreads == 1) - { - // Main thread created - ctx->currentThreadId = info->thread_id; - GDB_ParseCommonThreadInfo(buffer, ctx, SIGINT); - return GDB_SendFormattedPacket(ctx, "%s", buffer); - } - else if(info->attach_thread.creator_thread_id == 0 || !ctx->catchThreadEvents) - break; // Dismissed - else - { - ctx->currentThreadId = info->thread_id; - return GDB_SendPacket(ctx, "T05create:;", 10); - } + switch(info->type) { + case DBGEVENT_DEBUGGER_BREAK: { + strcpy(buffer, "S02"); } - - case DBGEVENT_EXIT_THREAD: - { - if(ctx->catchThreadEvents && info->exit_thread.reason < EXITTHREAD_EVENT_EXIT_PROCESS) - { - // no signal, SIGTERM, SIGQUIT (process exited), SIGTERM (process terminated) - static int threadExitRepliesSigs[] = { 0, SIGTERM, SIGQUIT, SIGTERM }; - return GDB_SendFormattedPacket(ctx, "w%02x;%lx", threadExitRepliesSigs[(u32)info->exit_thread.reason], info->thread_id); + + case DBGEVENT_CORE_ON: { + if (ctx->catchThreadEvents) { + ctx->currentThreadId = info->coreId + 1; // FIXME? + strcpy(buffer, "T05create:;"); } break; } - case DBGEVENT_EXIT_PROCESS: - { + case DBGEVENT_CORE_OFF: { + if(ctx->catchThreadEvents) { + sprintf(buffer, "w0;%x", info->coreId + 1); + } + break; + } + + case DBGEVENT_EXIT: { // exited (no error / unhandled exception), SIGTERM (process terminated) * 2 - static const char *processExitReplies[] = { "W00", "X0f", "X0f" }; - return GDB_SendPacket(ctx, processExitReplies[(u32)info->exit_process.reason], 3); - } - - case DBGEVENT_EXCEPTION: - { - ExceptionEvent exc = info->exception; - - switch(exc.type) - { - case EXCEVENT_UNDEFINED_INSTRUCTION: - case EXCEVENT_PREFETCH_ABORT: // doesn't include hardware breakpoints - case EXCEVENT_DATA_ABORT: // doesn't include hardware watchpoints - case EXCEVENT_UNALIGNED_DATA_ACCESS: - case EXCEVENT_UNDEFINED_SYSCALL: - { - u32 signum = exc.type == EXCEVENT_UNDEFINED_INSTRUCTION ? SIGILL : - (exc.type == EXCEVENT_UNDEFINED_SYSCALL ? SIGSYS : SIGSEGV); - - ctx->currentThreadId = info->thread_id; - GDB_ParseCommonThreadInfo(buffer, ctx, signum); - return GDB_SendFormattedPacket(ctx, "%s", buffer); - } - - case EXCEVENT_ATTACH_BREAK: - return GDB_SendPacket(ctx, "S00", 3); - - case EXCEVENT_STOP_POINT: - { - ctx->currentThreadId = info->thread_id; - - switch(exc.stop_point.type) - { - case STOPPOINT_SVC_FF: - { - GDB_ParseCommonThreadInfo(buffer, ctx, SIGTRAP); - return GDB_SendFormattedPacket(ctx, "%sswbreak:;", buffer); - break; - } - - case STOPPOINT_BREAKPOINT: - { - // /!\ Not actually implemented (and will never be) - GDB_ParseCommonThreadInfo(buffer, ctx, SIGTRAP); - return GDB_SendFormattedPacket(ctx, "%shwbreak:;", buffer); - break; - } - - case STOPPOINT_WATCHPOINT: - { - const char *kinds[] = { "", "r", "", "a" }; - WatchpointKind kind = GDB_GetWatchpointKind(ctx, exc.stop_point.fault_information); - if(kind == WATCHPOINT_DISABLED) - GDB_SendDebugString(ctx, "Warning: unknown watchpoint encountered!\n"); - - GDB_ParseCommonThreadInfo(buffer, ctx, SIGTRAP); - return GDB_SendFormattedPacket(ctx, "%s%swatch:%08lx;", buffer, kinds[(u32)kind], exc.stop_point.fault_information); - break; - } - - default: - break; - } - break; - } - - case EXCEVENT_USER_BREAK: - { - ctx->currentThreadId = info->thread_id; - GDB_ParseCommonThreadInfo(buffer, ctx, SIGINT); - return GDB_SendFormattedPacket(ctx, "%s", buffer); - //TODO - } - - case EXCEVENT_DEBUGGER_BREAK: - { - u32 threadIds[4]; - u32 nbThreads = 0; - - for(u32 i = 0; i < 4; i++) - { - if(exc.debugger_break.thread_ids[i] > 0) - threadIds[nbThreads++] = (u32)exc.debugger_break.thread_ids[i]; - } - - u32 currentThreadId = nbThreads > 0 ? GDB_GetCurrentThreadFromList(ctx, threadIds, nbThreads) : GDB_GetCurrentThread(ctx); - s64 dummy; - u32 mask = 0; - - svcGetDebugThreadParam(&dummy, &mask, ctx->debug, currentThreadId, DBGTHREAD_PARAMETER_SCHEDULING_MASK_LOW); - - if(mask == 1) - { - ctx->currentThreadId = currentThreadId; - GDB_ParseCommonThreadInfo(buffer, ctx, SIGINT); - return GDB_SendFormattedPacket(ctx, "%s", buffer); - } - else - return GDB_SendPacket(ctx, "S02", 3); - } - - default: - break; - } - + static const char *processExitReplies[] = { "W00", "X0f" }; + strcpy(buffer, processExitReplies[ctx->processExited ? 0 : 1]); break; } - case DBGEVENT_SYSCALL_IN: - { - ctx->currentThreadId = info->thread_id; - GDB_ParseCommonThreadInfo(buffer, ctx, SIGTRAP); - return GDB_SendFormattedPacket(ctx, "%ssyscall_entry:%02x;", buffer, info->syscall.syscall); - } + case DBGEVENT_EXCEPTION: { + ExceptionClass ec = info->frame->esr_el2.ec; + ctx->currentThreadId = info->coreId + 1; // FIXME? - case DBGEVENT_SYSCALL_OUT: - { - ctx->currentThreadId = info->thread_id; - GDB_ParseCommonThreadInfo(buffer, ctx, SIGTRAP); - return GDB_SendFormattedPacket(ctx, "%ssyscall_return:%02x;", buffer, info->syscall.syscall); - } - - case DBGEVENT_OUTPUT_STRING: - { - // Regular "output string" - if (!GDB_IsHioInProgress(ctx)) - { - u32 addr = info->output_string.string_addr; - u32 remaining = info->output_string.string_size; - u32 sent = 0; - int total = 0; - while(remaining > 0) - { - u32 pending = (GDB_BUF_LEN - 1) / 2; - pending = pending < remaining ? pending : remaining; - - int res = GDB_SendMemory(ctx, "O", 1, addr + sent, pending); - if(res < 0 || (u32) res != 5 + 2 * pending) - break; - - sent += pending; - remaining -= pending; - total += res; + // Aside from stage 2 translation faults and other pre-handled exceptions, + // the only notable exceptions we get are stop point/single step events from the debugee (basically classes 0x3x) + switch(ec) { + case Exception_BreakpointLowerEl: { + n = GDB_ParseExceptionFrame(buffer, ctx, SIGTRAP); + strcat(buffer + n, "hwbreak:;"); } - return total; - } - else // HIO - { - return GDB_SendCurrentHioRequest(ctx); - } + case Exception_WatchpointLowerEl: { + static const char *kinds[] = { "", "r", "", "a" }; + n = GDB_ParseExceptionFrame(buffer, ctx, SIGTRAP); + u32 kind = 3; // TODO: retrieve the actual wp direction (if it's a bidirectional wp or not) + sprintf(buffer + n, "%swatch:%016lx;", kinds[kind], info->frame->far_el2); + } - } - default: + // Note: we don't really support 32-bit sw breakpoints, we'll still report them + // if the guest has inserted some of them manually... + case Exception_SoftwareBreakpointA64: + case Exception_SoftwareBreakpointA32: { + n = GDB_ParseExceptionFrame(buffer, ctx, SIGTRAP); + strcat(buffer + n, "swbreak:;"); + } + + default: { + DEBUG("GDB: oops, unhandled exception for core id %u\n", info->coreId); + break; + } + } break; + } + + case DBGEVENT_OUTPUT_STRING: { + uintptr_t addr = info->outputString.address; + size_t remaining = info->outputString.size; + size_t sent = 0; + size_t total = 0; + while (remaining > 0) { + u32 pending = (GDB_BUF_LEN - 1) / 2; + pending = pending < remaining ? pending : remaining; + + int res = GDB_SendMemory(ctx, "O", 1, addr + sent, pending); + if(res < 0 || res != 5 + 2 * pending) + break; + + sent += pending; + remaining -= pending; + total += res; + } + + return (int)total; + } + + // TODO: HIO + + default: { + DEBUG("GDB: unknown exception type %u, core id %u\n", (u32)info->type, info->coreId); + break; + } } - return 0; -} - -/* - Only 1 blocking event can be enqueued at a time: they preempt all the other threads. - The only "non-blocking" event that is implemented is EXIT PROCESS (but it's a very special case) -*/ -int GDB_HandleDebugEvents(GDBContext *ctx) -{ - if(ctx->state == GDB_STATE_DETACHING) - return -1; - - DebugEventInfo info; - Result rdbg = svcGetProcessDebugEvent(&info, ctx->debug); - - if(R_FAILED(rdbg)) - return -1; - - GDB_PreprocessDebugEvent(ctx, &info); - - int ret = 0; - bool continueAutomatically = (info.type == DBGEVENT_OUTPUT_STRING && !GDB_IsHioInProgress(ctx)) || - info.type == DBGEVENT_ATTACH_PROCESS || - (info.type == DBGEVENT_ATTACH_THREAD && (info.attach_thread.creator_thread_id == 0 || !ctx->catchThreadEvents)) || - (info.type == DBGEVENT_EXIT_THREAD && (info.exit_thread.reason >= EXITTHREAD_EVENT_EXIT_PROCESS || !ctx->catchThreadEvents)) || - info.type == DBGEVENT_EXIT_PROCESS || !(info.flags & 1); - - if(continueAutomatically) - { - Result r = 0; - ret = GDB_SendStopReply(ctx, &info); - if(info.flags & 1) - r = svcContinueDebugEvent(ctx->debug, ctx->continueFlags); - - if(r == (Result)0xD8A02008) // process ended - return -2; - - return -ret - 3; - } - else - { - int ret; - - if(ctx->processEnded) - return -2; - - ctx->latestDebugEvent = info; - ret = GDB_SendStopReply(ctx, &info); - ctx->flags &= ~GDB_FLAG_CONTINUING; - return ret; + if (buffer[0] == 0) { + return 0; + } else { + return GDB_SendPacket(ctx, buffer, strlen(buffer)); } } diff --git a/thermosphere/src/gdb/debug.h b/thermosphere/src/gdb/debug.h index 95f65c4ae..564c105ea 100644 --- a/thermosphere/src/gdb/debug.h +++ b/thermosphere/src/gdb/debug.h @@ -7,11 +7,32 @@ #pragma once -#include "gdb.h" +#include "../gdb.h" +#include "../core_ctx.h" + +typedef enum DebugEventType { + DBGEVENT_DEBUGGER_BREAK = 0, + DBGEVENT_EXCEPTION, + DBGEVENT_CORE_ON, + DBGEVENT_CORE_OFF, + DBGEVENT_EXIT, + DBGEVENT_OUTPUT_STRING, +} DebugEventType; + +typedef struct OutputStringDebugEventInfo { + uintptr_t address; + size_t size; +} OutputStringDebugEventInfo; + +typedef struct DebugEventInfo { + DebugEventType type; + u32 coreId; + ExceptionStackFrame *frame; + union { + OutputStringDebugEventInfo outputString; + }; +} DebugEventInfo; -GDB_DECLARE_VERBOSE_HANDLER(Run); -GDB_DECLARE_HANDLER(Restart); -GDB_DECLARE_VERBOSE_HANDLER(Attach); GDB_DECLARE_HANDLER(Detach); GDB_DECLARE_HANDLER(Kill); GDB_DECLARE_HANDLER(Break); @@ -20,7 +41,6 @@ GDB_DECLARE_VERBOSE_HANDLER(Continue); GDB_DECLARE_HANDLER(GetStopReason); void GDB_ContinueExecution(GDBContext *ctx); -void GDB_PreprocessDebugEvent(GDBContext *ctx, DebugEventInfo *info); int GDB_SendStopReply(GDBContext *ctx, const DebugEventInfo *info); int GDB_HandleDebugEvents(GDBContext *ctx); void GDB_BreakProcessAndSinkDebugEvents(GDBContext *ctx, DebugFlags flags); diff --git a/thermosphere/src/irq.c b/thermosphere/src/irq.c index 521e225c0..62787c205 100644 --- a/thermosphere/src/irq.c +++ b/thermosphere/src/irq.c @@ -206,7 +206,6 @@ void handleIrqException(ExceptionStackFrame *frame, bool isLowerEl, bool isA32) u32 irqId = iar & 0x3FF; u32 srcCore = (iar >> 10) & 7; - frame->esr_el2.ec = Exception_Uncategorized; DEBUG("EL2 [core %d]: Received irq %x\n", (int)currentCoreCtx->coreId, irqId); if (irqId == GIC_IRQID_SPURIOUS) {