diff --git a/thermosphere/src/gdb.h b/thermosphere/src/gdb.h index 866e973c6..6ed7ce0e2 100644 --- a/thermosphere/src/gdb.h +++ b/thermosphere/src/gdb.h @@ -86,7 +86,8 @@ typedef struct GDBContext bool noAckSent; u32 currentThreadId; - + u32 selectedThreadId; + u32 selectedThreadIdForContinuing; bool catchThreadEvents; bool processEnded, processExited; diff --git a/thermosphere/src/gdb/net.c b/thermosphere/src/gdb/net.c index fa4abc74c..5632c59d0 100644 --- a/thermosphere/src/gdb/net.c +++ b/thermosphere/src/gdb/net.c @@ -236,7 +236,10 @@ static int GDB_DoSendPacket(GDBContext *ctx, size_t len) int GDB_SendPacket(GDBContext *ctx, const char *packetData, size_t len) { - memmove(ctx->buffer + 1, packetData, len); + if (packetData != ctx->buffer + 1) { + memmove(ctx->buffer + 1, packetData, len); + } + ctx->buffer[0] = '$'; char *checksumLoc = ctx->buffer + len + 1; @@ -298,7 +301,9 @@ int GDB_SendStreamData(GDBContext *ctx, const char *streamData, size_t offset, s } // Note: ctx->buffer[0] = '$' - memmove(ctx->buffer + 2, streamData + offset, length); + if (streamData + offset != ctx->buffer + 2) { + memmove(ctx->buffer + 2, streamData + offset, length); + } ctx->buffer[1] = letter; return GDB_SendPacket(ctx, ctx->buffer + 1, 1 + length); } diff --git a/thermosphere/src/gdb/thread.c b/thermosphere/src/gdb/thread.c index 20f655f93..b17533c9d 100644 --- a/thermosphere/src/gdb/thread.c +++ b/thermosphere/src/gdb/thread.c @@ -5,113 +5,32 @@ * SPDX-License-Identifier: (MIT OR GPL-2.0-or-later) */ -#include "gdb/thread.h" -#include "gdb/net.h" -#include "fmt.h" -#include +#include -static s32 GDB_GetDynamicThreadPriority(GDBContext *ctx, u32 threadId) -{ - Handle process, thread; - Result r; - s32 prio = 65; - - r = svcOpenProcess(&process, ctx->pid); - if(R_FAILED(r)) - return 65; - - r = svcOpenThread(&thread, process, threadId); - if(R_FAILED(r)) - goto cleanup; - - r = svcGetThreadPriority(&prio, thread); - -cleanup: - svcCloseHandle(thread); - svcCloseHandle(process); - - return prio; -} - -struct ThreadIdWithCtx -{ - GDBContext *ctx; - u32 id; -}; - -static int thread_compare_func(const void *a_, const void *b_) -{ - const struct ThreadIdWithCtx *a = (const struct ThreadIdWithCtx *)a_; - const struct ThreadIdWithCtx *b = (const struct ThreadIdWithCtx *)b_; - - u32 maskA = 2, maskB = 2; - s32 prioAStatic = 65, prioBStatic = 65; - s32 prioADynamic = GDB_GetDynamicThreadPriority(a->ctx, a->id); - s32 prioBDynamic = GDB_GetDynamicThreadPriority(b->ctx, b->id); - s64 dummy; - - svcGetDebugThreadParam(&dummy, &maskA, a->ctx->debug, a->id, DBGTHREAD_PARAMETER_SCHEDULING_MASK_LOW); - svcGetDebugThreadParam(&dummy, &maskB, b->ctx->debug, b->id, DBGTHREAD_PARAMETER_SCHEDULING_MASK_LOW); - svcGetDebugThreadParam(&dummy, (u32 *)&prioAStatic, a->ctx->debug, a->id, DBGTHREAD_PARAMETER_PRIORITY); - svcGetDebugThreadParam(&dummy, (u32 *)&prioBStatic, b->ctx->debug, b->id, DBGTHREAD_PARAMETER_PRIORITY); - - if(maskA == 1 && maskB != 1) - return -1; - else if(maskA != 1 && maskB == 1) - return 1; - else if(prioADynamic != prioBDynamic) - return prioADynamic - prioBDynamic; - else - return prioAStatic - prioBStatic; -} - -u32 GDB_GetCurrentThreadFromList(GDBContext *ctx, u32 *threadIds, u32 nbThreads) -{ - struct ThreadIdWithCtx lst[MAX_DEBUG_THREAD]; - for(u32 i = 0; i < nbThreads; i++) - { - lst[i].ctx = ctx; - lst[i].id = threadIds[i]; - } - - qsort(lst, nbThreads, sizeof(struct ThreadIdWithCtx), thread_compare_func); - return lst[0].id; -} - -u32 GDB_GetCurrentThread(GDBContext *ctx) -{ - u32 threadIds[MAX_DEBUG_THREAD]; - - if(ctx->nbThreads == 0) - return 0; - - for(u32 i = 0; i < ctx->nbThreads; i++) - threadIds[i] = ctx->threadInfos[i].id; - - return GDB_GetCurrentThreadFromList(ctx, threadIds, ctx->nbThreads); -} +#include "thread.h" +#include "net.h" +#include "../core_ctx.h" GDB_DECLARE_HANDLER(SetThreadId) { - if(ctx->commandData[0] == 'g') - { - if(strncmp(ctx->commandData + 1, "-1", 2) == 0) - return GDB_ReplyErrno(ctx, EILSEQ); // a thread must be specified + if (ctx->commandData[0] == 'g') { + if(strncmp(ctx->commandData + 1, "-1", 2) == 0) { + return GDB_ReplyErrno(ctx, EINVAL); + } - u32 id; - if(GDB_ParseHexIntegerList(&id, ctx->commandData + 1, 1, 0) == NULL) + unsigned long id; + if (GDB_ParseHexIntegerList(&id, ctx->commandData + 1, 1, 0) == NULL) { return GDB_ReplyErrno(ctx, EILSEQ); + } + ctx->selectedThreadId = id; + // TODO: change irq affinity (and remove selectedThreadId?) return GDB_ReplyOk(ctx); - } - else if(ctx->commandData[0] == 'c') - { - // We can't stop/continue particular threads (uncompliant behavior) - if(strncmp(ctx->commandData + 1, "-1", 2) == 0) + } else if (ctx->commandData[0] == 'c') { + if(strncmp(ctx->commandData + 1, "-1", 2) == 0) { ctx->selectedThreadIdForContinuing = 0; - else - { - u32 id; + } else { + unsigned long id; if(GDB_ParseHexIntegerList(&id, ctx->commandData + 1, 1, 0) == NULL) return GDB_ReplyErrno(ctx, EILSEQ); ctx->selectedThreadIdForContinuing = id; @@ -125,97 +44,54 @@ GDB_DECLARE_HANDLER(SetThreadId) GDB_DECLARE_HANDLER(IsThreadAlive) { - u32 threadId; - s64 dummy; - u32 mask; + unsigned long threadId; - if(GDB_ParseHexIntegerList(&threadId, ctx->commandData, 1, 0) == NULL) + if (GDB_ParseHexIntegerList(&threadId, ctx->commandData, 1, 0) == NULL) { return GDB_ReplyErrno(ctx, EILSEQ); + } - Result r = svcGetDebugThreadParam(&dummy, &mask, ctx->debug, threadId, DBGTHREAD_PARAMETER_SCHEDULING_MASK_LOW); - if(R_SUCCEEDED(r) && mask != 2) - return GDB_ReplyOk(ctx); - else - return GDB_ReplyErrno(ctx, EPERM); + u32 coreMask = getActiveCoreMask(); + return (coreMask & BIT(threadId)) != 0 ? GDB_ReplyOk(ctx) : GDB_ReplyErrno(ctx, ESRCH); } GDB_DECLARE_QUERY_HANDLER(CurrentThreadId) { - if(ctx->currentThreadId == 0) - ctx->currentThreadId = GDB_GetCurrentThread(ctx); - - return ctx->currentThreadId != 0 ? GDB_SendFormattedPacket(ctx, "QC%lx", ctx->currentThreadId) : GDB_ReplyErrno(ctx, EPERM); -} - -static void GDB_GenerateThreadListData(GDBContext *ctx) -{ - u32 aliveThreadIds[MAX_DEBUG_THREAD]; - u32 nbAliveThreads = 0; // just in case. This is probably redundant - - for(u32 i = 0; i < ctx->nbThreads; i++) - { - s64 dummy; - u32 mask; - - Result r = svcGetDebugThreadParam(&dummy, &mask, ctx->debug, ctx->threadInfos[i].id, DBGTHREAD_PARAMETER_SCHEDULING_MASK_LOW); - if(R_SUCCEEDED(r) && mask != 2) - aliveThreadIds[nbAliveThreads++] = ctx->threadInfos[i].id; + if (ctx->currentThreadId == 0) { + // FIXME: probably remove this variable + ctx->currentThreadId = 1 + currentCoreCtx->coreId; } - if(nbAliveThreads == 0) - ctx->threadListData[0] = 0; - - char *bufptr = ctx->threadListData; - - for(u32 i = 0; i < nbAliveThreads; i++) - bufptr += sprintf(bufptr, i == (nbAliveThreads - 1) ? "%lx" : "%lx,", aliveThreadIds[i]); -} - -static int GDB_SendThreadData(GDBContext *ctx) -{ - u32 sz = strlen(ctx->threadListData); - u32 len; - if(ctx->threadListDataPos >= sz) - len = 0; - else if(sz - ctx->threadListDataPos <= GDB_BUF_LEN - 1) - len = sz - ctx->threadListDataPos; - else - { - for(len = GDB_BUF_LEN - 1; ctx->threadListData[ctx->threadListDataPos + len] != ',' && len > 0; len--); - if(len > 0) - len--; - } - - int n = GDB_SendStreamData(ctx, ctx->threadListData, ctx->threadListDataPos, len, sz, true); - - if(ctx->threadListDataPos >= sz) - { - ctx->threadListDataPos = 0; - ctx->threadListData[0] = 0; - } - else - ctx->threadListDataPos += len; - - return n; + return GDB_SendFormattedPacket(ctx, "QC%x", ctx->currentThreadId); } GDB_DECLARE_QUERY_HANDLER(fThreadInfo) { - if(ctx->threadListData[0] == 0) - GDB_GenerateThreadListData(ctx); + // We have made our GDB packet big enough to list all the thread ids (coreIds + 1 for each coreId) + char *buf = ctx->buffer + 1; + int n = 1; + buf[0] = 'm'; - return GDB_SendThreadData(ctx); + u32 coreMask = getActiveCoreMask(); + + FOREACH_BIT (tmp, coreId, coreMask) { + n += sprintf(buf + n, "%x,", 1 + coreId); + } + + // Remove trailing comma + buf[--n] = 0; + + return GDB_SendStreamData(ctx, buf, 0, n, n, true); } GDB_DECLARE_QUERY_HANDLER(sThreadInfo) { - return GDB_SendThreadData(ctx); + // We have made our GDB packet big enough to list all the thread ids (coreIds + 1 for each coreId) in fThreadInfo + return GDB_SendPacket(ctx, "m", 1); } GDB_DECLARE_QUERY_HANDLER(ThreadEvents) { - switch(ctx->commandData[0]) - { + switch (ctx->commandData[0]) { case '0': ctx->catchThreadEvents = false; return GDB_ReplyOk(ctx); @@ -229,81 +105,13 @@ GDB_DECLARE_QUERY_HANDLER(ThreadEvents) GDB_DECLARE_QUERY_HANDLER(ThreadExtraInfo) { - u32 id; - s64 dummy; - u32 val; - Result r; + unsigned long id; int n; - const char *sStatus; - char sThreadDynamicPriority[64], sThreadStaticPriority[64]; - char sCoreIdeal[64], sCoreCreator[64]; - char buf[512]; - - u32 tls = 0; - if(GDB_ParseHexIntegerList(&id, ctx->commandData, 1, 0) == NULL) return GDB_ReplyErrno(ctx, EILSEQ); - for(u32 i = 0; i < MAX_DEBUG_THREAD; i++) - { - if(ctx->threadInfos[i].id == id) - tls = ctx->threadInfos[i].tls; - } + n = sprintf(ctx->workBuffer, "TODO"); - r = svcGetDebugThreadParam(&dummy, &val, ctx->debug, id, DBGTHREAD_PARAMETER_SCHEDULING_MASK_LOW); - sStatus = R_SUCCEEDED(r) ? (val == 1 ? ", running, " : ", idle, ") : ""; - - val = (u32)GDB_GetDynamicThreadPriority(ctx, id); - if(val == 65) - sThreadDynamicPriority[0] = 0; - else - sprintf(sThreadDynamicPriority, "dynamic prio.: %ld, ", (s32)val); - - r = svcGetDebugThreadParam(&dummy, &val, ctx->debug, id, DBGTHREAD_PARAMETER_PRIORITY); - if(R_FAILED(r)) - sThreadStaticPriority[0] = 0; - else - sprintf(sThreadStaticPriority, "static prio.: %ld, ", (s32)val); - - r = svcGetDebugThreadParam(&dummy, &val, ctx->debug, id, DBGTHREAD_PARAMETER_CPU_IDEAL); - if(R_FAILED(r)) - sCoreIdeal[0] = 0; - else - sprintf(sCoreIdeal, "ideal core: %lu, ", val); - - r = svcGetDebugThreadParam(&dummy, &val, ctx->debug, id, DBGTHREAD_PARAMETER_CPU_CREATOR); // Creator = "first ran, and running the thread" - if(R_FAILED(r)) - sCoreCreator[0] = 0; - else - sprintf(sCoreCreator, "running on core %lu", val); - - n = sprintf(buf, "TLS: 0x%08lx%s%s%s%s%s", tls, sStatus, sThreadDynamicPriority, sThreadStaticPriority, - sCoreIdeal, sCoreCreator); - - return GDB_SendHexPacket(ctx, buf, (u32)n); -} - -GDB_DECLARE_QUERY_HANDLER(GetTLSAddr) -{ - u32 lst[3]; - if(GDB_ParseHexIntegerList(lst, ctx->commandData, 3, 0) == NULL) - return GDB_ReplyErrno(ctx, EILSEQ); - - // We don't care about the 'lm' parameter... - u32 id = lst[0]; - u32 offset = lst[1]; - - u32 tls = 0; - - for(u32 i = 0; i < MAX_DEBUG_THREAD; i++) - { - if(ctx->threadInfos[i].id == id) - tls = ctx->threadInfos[i].tls; - } - - if(tls == 0) - return GDB_ReplyErrno(ctx, EINVAL); - - return GDB_SendFormattedPacket(ctx, "%08lx", tls + offset); + return GDB_SendHexPacket(ctx, ctx->workBuffer, n); }