From c00672654aa9a82bc474ce08d27f831bbaea77e7 Mon Sep 17 00:00:00 2001 From: TuxSH <1922548+TuxSH@users.noreply.github.com> Date: Sun, 26 Jan 2020 23:50:30 +0000 Subject: [PATCH] thermosphere: gdb: remove server, rewrite data processing in gdb/context and gdb/net --- thermosphere/src/gdb.c | 74 -------- thermosphere/src/gdb.h | 121 ------------- thermosphere/src/gdb/context.c | 237 +++++++++++++++++++++++++ thermosphere/src/gdb/context.h | 108 +++++++++++ thermosphere/src/gdb/debug.c | 2 +- thermosphere/src/gdb/debug.h | 26 +-- thermosphere/src/gdb/defines.h | 42 +++++ thermosphere/src/gdb/hio.h | 4 +- thermosphere/src/gdb/mem.h | 2 +- thermosphere/src/gdb/net.c | 169 +++++++++--------- thermosphere/src/gdb/net.h | 2 +- thermosphere/src/gdb/query.h | 2 +- thermosphere/src/gdb/regs.h | 2 +- thermosphere/src/gdb/remote_command.h | 2 +- thermosphere/src/gdb/server.c | 203 --------------------- thermosphere/src/gdb/server.h | 34 ---- thermosphere/src/gdb/stop_points.c | 2 +- thermosphere/src/gdb/stop_points.h | 2 +- thermosphere/src/gdb/thread.h | 2 +- thermosphere/src/gdb/verbose.h | 2 +- thermosphere/src/gdb/xfer.h | 2 +- thermosphere/src/transport_interface.c | 8 +- 22 files changed, 497 insertions(+), 551 deletions(-) delete mode 100644 thermosphere/src/gdb.c delete mode 100644 thermosphere/src/gdb.h create mode 100644 thermosphere/src/gdb/context.c create mode 100644 thermosphere/src/gdb/context.h create mode 100644 thermosphere/src/gdb/defines.h delete mode 100644 thermosphere/src/gdb/server.c delete mode 100644 thermosphere/src/gdb/server.h diff --git a/thermosphere/src/gdb.c b/thermosphere/src/gdb.c deleted file mode 100644 index 375a54a62..000000000 --- a/thermosphere/src/gdb.c +++ /dev/null @@ -1,74 +0,0 @@ -/* -* This file is part of Luma3DS. -* Copyright (C) 2016-2019 Aurora Wright, TuxSH -* -* SPDX-License-Identifier: (MIT OR GPL-2.0-or-later) -*/ - -#if 0 -#include "gdb.h" -#include "gdb/net.h" -#include "gdb/server.h" - -#include "gdb/debug.h" - -#include "gdb/stop_point.h" - -#include "breakpoints.h" -#include "software_breakpoints.h" -#include "watchpoints.h" - -void GDB_InitializeContext(GDBContext *ctx) -{ - memset(ctx, 0, sizeof(GDBContext)); -} - -void GDB_FinalizeContext(GDBContext *ctx) -{ - (void)ctx; -} - -void GDB_Attach(GDBContext *ctx) -{ - if (!(ctx->flags & GDB_FLAG_ATTACHED_AT_START)) { - // TODO: debug pause - } - - // TODO: move the debug traps enable here? - // TODO: process the event - - ctx->state = GDB_STATE_ATTACHED; -} - -void GDB_Detach(GDBContext *ctx) -{ - removeAllWatchpoints(); - removeAllBreakpoints(); - removeAllSoftwareBreakpoints(true); - - // Reports to gdb are prevented because of "detaching" state? - - // TODO: disable debug traps - - if(ctx->flags & GDB_FLAG_TERMINATE) { - // TODO: redefine what it means for thermosphère, if anything. - ctx->processEnded = true; - ctx->processExited = false; - } - - ctx->currentHioRequestTargetAddr = 0; - memset(&ctx->currentHioRequest, 0, sizeof(PackedGdbHioRequest)); -} - -GDB_DECLARE_HANDLER(Unsupported) -{ - return GDB_ReplyEmpty(ctx); -} - -GDB_DECLARE_HANDLER(EnableExtendedMode) -{ - // We don't support it for now... - return GDB_HandleUnsupported(ctx); -} - -#endif diff --git a/thermosphere/src/gdb.h b/thermosphere/src/gdb.h deleted file mode 100644 index 6ed7ce0e2..000000000 --- a/thermosphere/src/gdb.h +++ /dev/null @@ -1,121 +0,0 @@ -/* -* This file is part of Luma3DS. -* Copyright (C) 2016-2019 Aurora Wright, TuxSH -* -* SPDX-License-Identifier: (MIT OR GPL-2.0-or-later) -*/ - -#pragma once - -#include "utils.h" -#include "spinlock.h" - -#define MAX_CTX 3 - -// 512+24 is the ideal size as IDA will try to read exactly 0x100 bytes at a time. Add 4 to this, for $#, see below. -// IDA seems to want additional bytes as well. -// 1024 is fine enough to put all regs in the 'T' stop reply packets -#define GDB_BUF_LEN 0x800 -#define GDB_WORK_BUF_LEN 0x2000 - -#define GDB_HANDLER(name) GDB_Handle##name -#define GDB_QUERY_HANDLER(name) GDB_HANDLER(Query##name) -#define GDB_VERBOSE_HANDLER(name) GDB_HANDLER(Verbose##name) - -#define GDB_DECLARE_HANDLER(name) int GDB_HANDLER(name)(GDBContext *ctx) -#define GDB_DECLARE_QUERY_HANDLER(name) GDB_DECLARE_HANDLER(Query##name) -#define GDB_DECLARE_VERBOSE_HANDLER(name) GDB_DECLARE_HANDLER(Verbose##name) - -typedef struct PackedGdbHioRequest -{ - char magic[4]; // "GDB\x00" - u32 version; - - // Request - char functionName[16+1]; - char paramFormat[8+1]; - - u64 parameters[8]; - size_t stringLengths[8]; - - // Return - s64 retval; - int gdbErrno; - bool ctrlC; -} PackedGdbHioRequest; - -enum { - GDB_FLAG_SELECTED = BIT(0), - GDB_FLAG_USED = BIT(1), - GDB_FLAG_ALLOCATED_MASK = GDB_FLAG_SELECTED | GDB_FLAG_USED, - GDB_FLAG_EXTENDED_REMOTE = BIT(2), // unused here - GDB_FLAG_NOACK = BIT(3), - GDB_FLAG_RESTART_MASK = GDB_FLAG_NOACK | GDB_FLAG_EXTENDED_REMOTE | GDB_FLAG_USED, - GDB_FLAG_CONTINUING = BIT(4), - GDB_FLAG_TERMINATE = BIT(5), - GDB_FLAG_ATTACHED_AT_START = BIT(6), - GDB_FLAG_NONSTOP = BIT(7), - //GDB_FLAG_CREATED = BIT(7), -}; - -typedef enum GDBState -{ - GDB_STATE_DISCONNECTED, - GDB_STATE_CONNECTED, - GDB_STATE_ATTACHED, - GDB_STATE_DETACHING, -} GDBState; - -typedef struct ThreadInfo -{ - u32 id; - u32 tls; -} ThreadInfo; - -struct GDBServer; - -typedef struct GDBContext -{ - TransportInterface *transportIface; - struct GDBServer *parent; - - RecursiveSpinlock lock; - - u32 flags; - GDBState state; - bool noAckSent; - - u32 currentThreadId; - u32 selectedThreadId; - u32 selectedThreadIdForContinuing; - bool catchThreadEvents; - bool processEnded, processExited; - - //DebugEventInfo latestDebugEvent; FIXME - - uintptr_t currentHioRequestTargetAddr; - PackedGdbHioRequest currentHioRequest; - - char *commandData, *commandEnd; - int latestSentPacketSize; - char buffer[GDB_BUF_LEN + 4]; - char *workBuffer; - - size_t targetXmlLen; -} GDBContext; - -typedef int (*GDBCommandHandler)(GDBContext *ctx); - -void GDB_InitializeContext(GDBContext *ctx); -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/context.c b/thermosphere/src/gdb/context.c new file mode 100644 index 000000000..6b0a81e8b --- /dev/null +++ b/thermosphere/src/gdb/context.c @@ -0,0 +1,237 @@ +/* + * Copyright (c) 2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +// Lots of code from: +/* +* This file is part of Luma3DS. +* Copyright (C) 2016-2019 Aurora Wright, TuxSH +* +* SPDX-License-Identifier: (MIT OR GPL-2.0-or-later) +*/ + +#include + +#include "context.h" + +#include "net.h" + +#include "debug.h" +#include "query.h" +#include "verbose.h" +#include "thread.h" +#include "debug.h" +#include "regs.h" +#include "mem.h" +#include "hio.h" +#include "stop_points.h" + +#include "../breakpoints.h" +#include "../software_breakpoints.h" +#include "../watchpoints.h" + +static TEMPORARY u8 g_gdbWorkBuffer[GDB_WORK_BUF_LEN]; + +static const struct{ + char command; + GDBCommandHandler handler; +} gdbCommandHandlers[] = { + { '?', GDB_HANDLER(GetStopReason) }, + { '!', GDB_HANDLER(EnableExtendedMode) }, + { 'c', GDB_HANDLER(Continue) }, + { 'C', GDB_HANDLER(Continue) }, + { 'D', GDB_HANDLER(Detach) }, + { 'F', GDB_HANDLER(HioReply) }, + { 'g', GDB_HANDLER(ReadRegisters) }, + { 'G', GDB_HANDLER(WriteRegisters) }, + { 'H', GDB_HANDLER(SetThreadId) }, + { 'k', GDB_HANDLER(Kill) }, + { 'm', GDB_HANDLER(ReadMemory) }, + { 'M', GDB_HANDLER(WriteMemory) }, + { 'p', GDB_HANDLER(ReadRegister) }, + { 'P', GDB_HANDLER(WriteRegister) }, + { 'q', GDB_HANDLER(ReadQuery) }, + { 'Q', GDB_HANDLER(WriteQuery) }, + { 'T', GDB_HANDLER(IsThreadAlive) }, + { 'v', GDB_HANDLER(VerboseCommand) }, + { 'X', GDB_HANDLER(WriteMemoryRaw) }, + { 'z', GDB_HANDLER(ToggleStopPoint) }, + { 'Z', GDB_HANDLER(ToggleStopPoint) }, +}; + +static inline GDBCommandHandler GDB_GetCommandHandler(char command) +{ + static const u32 nbHandlers = sizeof(gdbCommandHandlers) / sizeof(gdbCommandHandlers[0]); + + size_t i; + for (i = 0; i < nbHandlers && gdbCommandHandlers[i].command != command; i++); + + return i < nbHandlers ? gdbCommandHandlers[i].handler : GDB_HANDLER(Unsupported); +} + +static int GDB_ProcessPacket(GDBContext *ctx, size_t len) +{ + int ret; + + u32 oldFlags = ctx->flags; + + ENSURE(ctx->state != GDB_STATE_DISCONNECTED); + + // Handle the packet... + if (ctx->buffer[0] == '\x03') { + GDB_HandleBreak(ctx); + ret = 0; + } else { + GDBCommandHandler handler = GDB_GetCommandHandler(ctx->buffer[1]); + ctx->commandData = ctx->buffer + 2; + ret = handler(ctx); + } + + + // State changes... + if (ctx->state == GDB_STATE_DETACHING) { + return -1; + } + + if ((oldFlags & GDB_FLAG_CONTINUING) && !(ctx->flags & GDB_FLAG_CONTINUING)) { + // TODO + } + else if (!(oldFlags & GDB_FLAG_CONTINUING) && (ctx->flags & GDB_FLAG_CONTINUING)) { + // TODO + } + + return ret; +} + +static size_t GDB_ReceiveDataCallback(TransportInterface *iface, void *ctxVoid) +{ + return (size_t)GDB_ReceivePacket((GDBContext *)ctxVoid); +} + +static void GDB_Disconnect(GDBContext *ctx) +{ + GDB_DetachFromContext(ctx); + + ctx->flags = 0; + ctx->state = GDB_STATE_DISCONNECTED; + + ctx->currentThreadId = 0; + ctx->selectedThreadId = 0; + ctx->selectedThreadIdForContinuing = 0; + + ctx->catchThreadEvents = false; + ctx->lastSentPacketSize = 0; + + ctx->processEnded = false; + ctx->processExited = false; + + ctx->noAckSent = false; + // TODO memset(&ctx->latestDebugEvent, 0, sizeof(DebugEventInfo)); + + ctx->currentHioRequestTargetAddr = 0; + memset(&ctx->currentHioRequest, 0, sizeof(PackedGdbHioRequest)); + + ctx->targetXmlLen = 0; +} + +static void GDB_ProcessDataCallback(TransportInterface *iface, void *ctxVoid, size_t sz) +{ + int r = (int)sz; + GDBContext *ctx = (GDBContext *)ctxVoid; + + if (r == -1) { + // Not sure if GDB has something to forcefully close connections over UART... + char c = '\x04'; // ctrl-D + transportInterfaceWriteData(iface, &c, 1); + GDB_Disconnect(ctx); + } + + r = GDB_ProcessPacket(ctx, sz); + if (r == -1) { + GDB_Disconnect(ctx); + } +} + +void GDB_InitializeContext(GDBContext *ctx, TransportInterfaceType ifaceType, u32 ifaceId, u32 ifaceFlags) +{ + memset(ctx, 0, sizeof(GDBContext)); + ctx->workBuffer = g_gdbWorkBuffer; + ctx->transportInterface = transportInterfaceCreate( + ifaceType, + ifaceId, + ifaceFlags, + GDB_ReceiveDataCallback, + GDB_ProcessDataCallback, + ctx + ); +} + +void GDB_AttachToContext(GDBContext *ctx) +{ + if (!(ctx->flags & GDB_FLAG_ATTACHED_AT_START)) { + // TODO: debug pause + } + + // TODO: move the debug traps enable here? + // TODO: process the event + + ctx->state = GDB_STATE_ATTACHED; +} + +void GDB_DetachFromContext(GDBContext *ctx) +{ + removeAllWatchpoints(); + removeAllBreakpoints(); + removeAllSoftwareBreakpoints(true); + + // Reports to gdb are prevented because of "detaching" state? + + // TODO: disable debug traps + + if(ctx->flags & GDB_FLAG_TERMINATE) { + // TODO: redefine what it means for thermosphère, if anything. + ctx->processEnded = true; + ctx->processExited = false; + } + + ctx->currentHioRequestTargetAddr = 0; + memset(&ctx->currentHioRequest, 0, sizeof(PackedGdbHioRequest)); +} + +void GDB_AcquireContext(GDBContext *ctx) +{ + transportInterfaceAcquire(ctx->transportInterface); +} + +void GDB_ReleaseContext(GDBContext *ctx) +{ + transportInterfaceRelease(ctx->transportInterface); +} + +void GDB_MigrateRxIrq(GDBContext *ctx, u32 newAffinity) +{ + transportInterfaceSetInterruptAffinity(ctx->transportInterface, newAffinity); +} + +GDB_DECLARE_HANDLER(Unsupported) +{ + return GDB_ReplyEmpty(ctx); +} + +GDB_DECLARE_HANDLER(EnableExtendedMode) +{ + // We don't support it for now... + return GDB_HandleUnsupported(ctx); +} diff --git a/thermosphere/src/gdb/context.h b/thermosphere/src/gdb/context.h new file mode 100644 index 000000000..ce64c2139 --- /dev/null +++ b/thermosphere/src/gdb/context.h @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +// Lots of code from: +/* +* This file is part of Luma3DS. +* Copyright (C) 2016-2019 Aurora Wright, TuxSH +* +* SPDX-License-Identifier: (MIT OR GPL-2.0-or-later) +*/ + +#pragma once + +#include "defines.h" +#include "transport_interface.h" + +typedef struct PackedGdbHioRequest +{ + char magic[4]; // "GDB\x00" + u32 version; + + // Request + char functionName[16+1]; + char paramFormat[8+1]; + + u64 parameters[8]; + size_t stringLengths[8]; + + // Return + s64 retval; + int gdbErrno; + bool ctrlC; +} PackedGdbHioRequest; + +enum { + GDB_FLAG_NOACK = BIT(0), + GDB_FLAG_CONTINUING = BIT(1), + GDB_FLAG_TERMINATE = BIT(2), + GDB_FLAG_ATTACHED_AT_START = BIT(3), + GDB_FLAG_NONSTOP = BIT(4), +}; + +typedef enum GDBState +{ + GDB_STATE_DISCONNECTED, + GDB_STATE_CONNECTED, + GDB_STATE_ATTACHED, + GDB_STATE_DETACHING, +} GDBState; + +typedef struct GDBContext { + // No need for a lock, it's in the transport interface layer... + + TransportInterface *transportInterface; + u32 flags; + GDBState state; + bool noAckSent; + + u32 currentThreadId; + u32 selectedThreadId; + u32 selectedThreadIdForContinuing; + bool catchThreadEvents; + bool processEnded, processExited; + + //DebugEventInfo latestDebugEvent; FIXME + + uintptr_t currentHioRequestTargetAddr; + PackedGdbHioRequest currentHioRequest; + + size_t targetXmlLen; + + char *commandData, *commandEnd; + size_t lastSentPacketSize; + char *buffer[GDB_BUF_LEN + 4]; + char *workBuffer; +} GDBContext; + +typedef int (*GDBCommandHandler)(GDBContext *ctx); + +void GDB_InitializeContext(GDBContext *ctx, TransportInterfaceType ifaceType, u32 ifaceId, u32 ifaceFlags); + +void GDB_AttachToContext(GDBContext *ctx); +void GDB_DetachFromContext(GDBContext *ctx); + +void GDB_AcquireContext(GDBContext *ctx); +void GDB_ReleaseContext(GDBContext *ctx); +void GDB_MigrateRxIrq(GDBContext *ctx, u32 newAffinity); + +GDB_DECLARE_HANDLER(Unsupported); +GDB_DECLARE_HANDLER(EnableExtendedMode); + +static inline bool GDB_IsAttached(GDBContext *ctx) +{ + return ctx->state == GDB_STATE_ATTACHED; +} diff --git a/thermosphere/src/gdb/debug.c b/thermosphere/src/gdb/debug.c index d7d23bda7..77d444479 100644 --- a/thermosphere/src/gdb/debug.c +++ b/thermosphere/src/gdb/debug.c @@ -14,7 +14,7 @@ #include "debug.h" #include "net.h" -#include "server.h" +#include "context.h" #include "verbose.h" #include "thread.h" #include "mem.h" diff --git a/thermosphere/src/gdb/debug.h b/thermosphere/src/gdb/debug.h index 564c105ea..3c5389ab4 100644 --- a/thermosphere/src/gdb/debug.h +++ b/thermosphere/src/gdb/debug.h @@ -7,31 +7,9 @@ #pragma once -#include "../gdb.h" +#include "context.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; +#include "../debug_manager.h" GDB_DECLARE_HANDLER(Detach); GDB_DECLARE_HANDLER(Kill); diff --git a/thermosphere/src/gdb/defines.h b/thermosphere/src/gdb/defines.h new file mode 100644 index 000000000..89dbd14ff --- /dev/null +++ b/thermosphere/src/gdb/defines.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +// Some code from: +/* +* This file is part of Luma3DS. +* Copyright (C) 2016-2019 Aurora Wright, TuxSH +* +* SPDX-License-Identifier: (MIT OR GPL-2.0-or-later) +*/ + +#pragma once + +#include "../utils.h" + +// 512+24 is the ideal size as IDA will try to read exactly 0x100 bytes at a time. +// IDA seems to want additional bytes as well. +// 1024 is fine enough to put all regs in the 'T' stop reply packets +// Add 4 to this for the actual allocated size, for $#, see below. +#define GDB_BUF_LEN 0x800 +#define GDB_WORK_BUF_LEN 0x2000 + +#define GDB_HANDLER(name) GDB_Handle##name +#define GDB_QUERY_HANDLER(name) GDB_HANDLER(Query##name) +#define GDB_VERBOSE_HANDLER(name) GDB_HANDLER(Verbose##name) + +#define GDB_DECLARE_HANDLER(name) int GDB_HANDLER(name)(GDBContext *ctx) +#define GDB_DECLARE_QUERY_HANDLER(name) GDB_DECLARE_HANDLER(Query##name) +#define GDB_DECLARE_VERBOSE_HANDLER(name) GDB_DECLARE_HANDLER(Verbose##name) diff --git a/thermosphere/src/gdb/hio.h b/thermosphere/src/gdb/hio.h index 123ba5685..0915dc3a9 100644 --- a/thermosphere/src/gdb/hio.h +++ b/thermosphere/src/gdb/hio.h @@ -7,10 +7,10 @@ #pragma once -#include "gdb.h" +#include "context.h" bool GDB_FetchPackedHioRequest(GDBContext *ctx, u32 addr); bool GDB_IsHioInProgress(GDBContext *ctx); int GDB_SendCurrentHioRequest(GDBContext *ctx); -GDB_DECLARE_HANDLER(HioReply); \ No newline at end of file +GDB_DECLARE_HANDLER(HioReply); diff --git a/thermosphere/src/gdb/mem.h b/thermosphere/src/gdb/mem.h index dadd728ac..b81f8b648 100644 --- a/thermosphere/src/gdb/mem.h +++ b/thermosphere/src/gdb/mem.h @@ -7,7 +7,7 @@ #pragma once -#include "gdb.h" +#include "context.h" int GDB_SendMemory(GDBContext *ctx, const char *prefix, size_t prefixLen, uintptr_t addr, size_t len); int GDB_WriteMemory(GDBContext *ctx, const void *buf, uintptr_t addr, size_t len); diff --git a/thermosphere/src/gdb/net.c b/thermosphere/src/gdb/net.c index 7d32833f8..026d2948f 100644 --- a/thermosphere/src/gdb/net.c +++ b/thermosphere/src/gdb/net.c @@ -5,12 +5,11 @@ * SPDX-License-Identifier: (MIT OR GPL-2.0-or-later) */ -#include "gdb/net.h" +#include "net.h" + #include #include #include -#include "fmt.h" -#include "minisoc.h" #include "../pattern_utils.h" u8 GDB_ComputeChecksum(const char *packetData, size_t len) @@ -136,104 +135,114 @@ const char *GDB_ParseHexIntegerList(unsigned long *dst, const char *src, size_t return GDB_ParseIntegerList(dst, src, nb, ',', lastSep, 16, false); } +static int GDB_SendNackIfPossible(GDBContext *ctx) { + if (ctx->flags & GDB_FLAG_NOACK) { + return -1; + } else { + char hdr = '-'; + transportInterfaceWriteData(ctx->transportInterface, &hdr, 1); + return 1; + } +} + int GDB_ReceivePacket(GDBContext *ctx) { - char backupbuf[GDB_BUF_LEN + 4]; - memcpy(backupbuf, ctx->buffer, ctx->latestSentPacketSize); - memset(ctx->buffer, 0, sizeof(ctx->buffer)); + char hdr; + bool ctrlC = false; + TransportInterface *iface = ctx->transportInterface; - int r = soc_recv(ctx->super.sockfd, ctx->buffer, sizeof(ctx->buffer), MSG_PEEK); - if(r < 1) + // Read the first character... + transportInterfaceReadData(iface, &hdr, 1); + + // Check if the ack/nack packets are not allowed + if ((hdr == '+' || hdr == '-') && (ctx->flags & GDB_FLAG_NOACK) != 0) { + DEBUG("Received a packed with an invalid header from GDB, hdr=%c\n", hdr); return -1; - if(ctx->buffer[0] == '+') // GDB sometimes acknowleges TCP acknowledgment packets (yes...). IDA does it properly - { - if(ctx->flags & GDB_FLAG_NOACK) - return -1; + } - // Consume it - r = soc_recv(ctx->super.sockfd, ctx->buffer, 1, 0); - if(r != 1) - return -1; - - ctx->buffer[0] = 0; - - r = soc_recv(ctx->super.sockfd, ctx->buffer, sizeof(ctx->buffer), MSG_PEEK); - - if(r == -1) - goto packet_error; + switch (hdr) { + case '+': + // Ack, don't do anything else (if allowed) + return 0; + case '-': + // Nack, return the previous packet + transportInterfaceWriteData(iface, ctx->buffer, ctx->lastSentPacketSize); + return ctx->lastSentPacketSize; + case '$': + // Normal packet, handled below + break; + case '\x03': + // Normal packet (Control-C), handled below + ctrlC = true; + break; + default: + // Oops, send a nack + DEBUG("Received a packed with an invalid header from GDB, hdr=%c\n", hdr); + return GDB_SendNackIfPossible(ctx); } - else if(ctx->buffer[0] == '-') - { - soc_send(ctx->super.sockfd, backupbuf, ctx->latestSentPacketSize, 0); - return 0; - } - int maxlen = r > (int)sizeof(ctx->buffer) ? (int)sizeof(ctx->buffer) : r; - if(ctx->buffer[0] == '$') // normal packet - { - char *pos; - for(pos = ctx->buffer; pos < ctx->buffer + maxlen && *pos != '#'; pos++); + // We didn't get a nack past this point, read the remaining data if any - if(pos == ctx->buffer + maxlen) // malformed packet - return -1; - - else - { - u8 checksum; - r = soc_recv(ctx->super.sockfd, ctx->buffer, 3 + pos - ctx->buffer, 0); - if(r != 3 + pos - ctx->buffer || GDB_DecodeHex(&checksum, pos + 1, 1) != 1) - goto packet_error; - else if(GDB_ComputeChecksum(ctx->buffer + 1, pos - ctx->buffer - 1) != checksum) - goto packet_error; - - ctx->commandEnd = pos; - *pos = 0; // replace trailing '#' by a NUL character + ctx->buffer[0] = hdr; + if (ctrlC) { + // Will never normally happen, but ok + if (ctx->state < GDB_STATE_ATTACHED) { + DEBUG("Received connection from GDB, now attaching...\n"); + GDB_AttachToContext(ctx); + ctx->state = GDB_STATE_ATTACHED; } - } - else if(ctx->buffer[0] == '\x03') - { - r = soc_recv(ctx->super.sockfd, ctx->buffer, 1, 0); - if(r != 1) - goto packet_error; - - ctx->commandEnd = ctx->buffer; + return 1; } - if(!(ctx->flags & GDB_FLAG_NOACK)) - { - int r2 = soc_send(ctx->super.sockfd, "+", 1, 0); - if(r2 != 1) - return -1; + size_t delimPos = transportInterfaceReadDataUntil(iface, ctx->buffer + 1, 4 + GDB_BUF_LEN - 1, '#'); + if (ctx->buffer[delimPos] != '#') { + // The packet is malformed, send a nack + return GDB_SendNackIfPossible(ctx); } - if(ctx->noAckSent) - { + // Read the checksum + size_t checksumPos = delimPos + 1; + u8 checksum; + transportInterfaceReadData(iface, ctx->buffer + checksumPos, 2); + + if (GDB_DecodeHex(&checksum, ctx->buffer + checksumPos, 1) != 1) { + // Malformed checksum + return GDB_SendNackIfPossible(ctx); + } else if (GDB_ComputeChecksum(ctx->buffer + 1, delimPos - 1) != checksum) { + // Invalid checksum + return GDB_SendNackIfPossible(ctx); + } + + // Ok, send ack (if possible) + if (!(ctx->flags & GDB_FLAG_NOACK)) { + hdr = '+'; + transportInterfaceWriteData(iface, &hdr, 1); + } + + // State transitions... + if (ctx->state < GDB_STATE_ATTACHED) { + DEBUG("Received connection from GDB, now attaching...\n"); + GDB_AttachToContext(ctx); + ctx->state = GDB_STATE_ATTACHED; + } + + if (ctx->noAckSent) { ctx->flags |= GDB_FLAG_NOACK; ctx->noAckSent = false; } - return r; + // Set helper attributes, change '#' to NUL + ctx->commandEnd = delimPos; + ctx->buffer[delimPos] = '\0'; -packet_error: - if(!(ctx->flags & GDB_FLAG_NOACK)) - { - r = soc_send(ctx->super.sockfd, "-", 1, 0); - if(r != 1) - return -1; - else - return 0; - } - else - return -1; + return (int)(delimPos + 2); } static int GDB_DoSendPacket(GDBContext *ctx, size_t len) { - int r = soc_send(ctx->super.sockfd, ctx->buffer, len, 0); - - if(r > 0) - ctx->latestSentPacketSize = r; - return r; + transportInterfaceWriteData(ctx->transportInterface, ctx->buffer, len); + ctx->lastSentPacketSize = len; + return (int)len; } int GDB_SendPacket(GDBContext *ctx, const char *packetData, size_t len) @@ -323,6 +332,6 @@ int GDB_ReplyOk(GDBContext *ctx) int GDB_ReplyErrno(GDBContext *ctx, int no) { char buf[] = "E01"; - hexItoa((u8)no, buf + 1, 2, false); + hexItoa(no & 0xFF, buf + 1, 2, false); return GDB_SendPacket(ctx, buf, 3); } diff --git a/thermosphere/src/gdb/net.h b/thermosphere/src/gdb/net.h index fa3d063c6..f74e268d7 100644 --- a/thermosphere/src/gdb/net.h +++ b/thermosphere/src/gdb/net.h @@ -7,7 +7,7 @@ #pragma once -#include "../gdb.h" +#include "context.h" #define _REENT_ONLY #include diff --git a/thermosphere/src/gdb/query.h b/thermosphere/src/gdb/query.h index 93ee5e862..a828b023d 100644 --- a/thermosphere/src/gdb/query.h +++ b/thermosphere/src/gdb/query.h @@ -7,7 +7,7 @@ #pragma once -#include "gdb.h" +#include "context.h" int GDB_HandleReadQuery(GDBContext *ctx); int GDB_HandleWriteQuery(GDBContext *ctx); diff --git a/thermosphere/src/gdb/regs.h b/thermosphere/src/gdb/regs.h index 57b965fa6..528c1bc9a 100644 --- a/thermosphere/src/gdb/regs.h +++ b/thermosphere/src/gdb/regs.h @@ -7,7 +7,7 @@ #pragma once -#include "../gdb.h" +#include "context.h" GDB_DECLARE_HANDLER(ReadRegisters); GDB_DECLARE_HANDLER(WriteRegisters); diff --git a/thermosphere/src/gdb/remote_command.h b/thermosphere/src/gdb/remote_command.h index a33327328..be89bea6a 100644 --- a/thermosphere/src/gdb/remote_command.h +++ b/thermosphere/src/gdb/remote_command.h @@ -7,7 +7,7 @@ #pragma once -#include "gdb.h" +#include "context.h" #define GDB_REMOTE_COMMAND_HANDLER(name) GDB_HANDLER(RemoteCommand##name) #define GDB_DECLARE_REMOTE_COMMAND_HANDLER(name) GDB_DECLARE_HANDLER(RemoteCommand##name) diff --git a/thermosphere/src/gdb/server.c b/thermosphere/src/gdb/server.c deleted file mode 100644 index 5f3e33ae6..000000000 --- a/thermosphere/src/gdb/server.c +++ /dev/null @@ -1,203 +0,0 @@ -/* -* This file is part of Luma3DS. -* Copyright (C) 2016-2019 Aurora Wright, TuxSH -* -* SPDX-License-Identifier: (MIT OR GPL-2.0-or-later) -*/ - -#include "gdb/server.h" -#include "gdb/net.h" -#include "gdb/query.h" -#include "gdb/verbose.h" -#include "gdb/thread.h" -#include "gdb/debug.h" -#include "gdb/regs.h" -#include "gdb/mem.h" -#include "gdb/hio.h" -#include "gdb/watchpoints.h" -#include "gdb/breakpoints.h" -#include "gdb/stop_points.h" - -void GDB_InitializeServer(GDBServer *server) -{ - for(u32 i = 0; i < sizeof(server->ctxs) / sizeof(GDBContext); i++) { - GDB_InitializeContext(server->ctxs + i); - } - - return 0; -} - -void GDB_FinalizeServer(GDBServer *server) -{ - // Kill the "next application" context if needed - for (u32 i = 0; i < MAX_CTX; i++) { - if (server->ctxs[i].state != GDB_STATE_DISCONNECTED) { - GDB_CloseClient(&server->ctxs[i]); - } - } -} - -void GDB_RunServer(GDBServer *server) -{ - // TODO transport iface - (void)server; -} - -void GDB_LockAllContexts(GDBServer *server) -{ - for (u32 i = 0; i < MAX_CTX; i++) { - recursiveSpinlockLock(&server->ctxs[i].lock); - } -} - -void GDB_UnlockAllContexts(GDBServer *server) -{ - for (u32 i = MAX_CTX; i > 0; i--) { - recursiveSpinlockUnlock(&server->ctxs[i - 1].lock); - } -} - -GDBContext *GDB_SelectAvailableContext(GDBServer *server) -{ - GDBContext *ctx; - - GDB_LockAllContexts(server); - - // Get a context - size_t id; - for (id = 0; id < MAX_CTX && (server->ctxs[id].flags & GDB_FLAG_ALLOCATED_MASK); id++); - ctx = id < MAX_CTX ? &server->ctxs[id] : NULL; - - GDB_UnlockAllContexts(server); - return ctx; -} - -int GDB_AcceptClient(GDBContext *ctx) -{ - recursiveSpinlockLock(&ctx->lock); - ctx->state = GDB_STATE_CONNECTED; - ctx->latestSentPacketSize = 0; - - /*if (ctx->flags & GDB_FLAG_SELECTED) - r = GDB_AttachToProcess(ctx); - */ - recursiveSpinlockUnlock(&ctx->lock); - - return 0; -} - -int GDB_CloseClient(GDBContext *ctx) -{ - // currently unused - recursiveSpinlockLock(&ctx->lock); - - if (ctx->state >= GDB_STATE_ATTACHED) { - GDB_DetachFromProcess(ctx); - } - - ctx->flags = 0; - ctx->state = GDB_STATE_DISCONNECTED; - - ctx->catchThreadEvents = false; - - // memset(&ctx->latestDebugEvent, 0, sizeof(DebugEventInfo)); TODO - - recursiveSpinlockUnlock(&ctx->lock); - return 0; -} - -void GDB_ReleaseClient(GDBServer *server, GDBContext *ctx) -{ - // same thing - (void)server; - (void)ctx; -} - -static const struct -{ - char command; - GDBCommandHandler handler; -} gdbCommandHandlers[] = -{ - { '?', GDB_HANDLER(GetStopReason) }, - { '!', GDB_HANDLER(EnableExtendedMode) }, - { 'c', GDB_HANDLER(Continue) }, - { 'C', GDB_HANDLER(Continue) }, - { 'D', GDB_HANDLER(Detach) }, - { 'F', GDB_HANDLER(HioReply) }, - { 'g', GDB_HANDLER(ReadRegisters) }, - { 'G', GDB_HANDLER(WriteRegisters) }, - { 'H', GDB_HANDLER(SetThreadId) }, - { 'k', GDB_HANDLER(Kill) }, - { 'm', GDB_HANDLER(ReadMemory) }, - { 'M', GDB_HANDLER(WriteMemory) }, - { 'p', GDB_HANDLER(ReadRegister) }, - { 'P', GDB_HANDLER(WriteRegister) }, - { 'q', GDB_HANDLER(ReadQuery) }, - { 'Q', GDB_HANDLER(WriteQuery) }, - { 'R', GDB_HANDLER(Restart) }, - { 'T', GDB_HANDLER(IsThreadAlive) }, - { 'v', GDB_HANDLER(VerboseCommand) }, - { 'X', GDB_HANDLER(WriteMemoryRaw) }, - { 'z', GDB_HANDLER(ToggleStopPoint) }, - { 'Z', GDB_HANDLER(ToggleStopPoint) }, -}; - -static inline GDBCommandHandler GDB_GetCommandHandler(char command) -{ - static const u32 nbHandlers = sizeof(gdbCommandHandlers) / sizeof(gdbCommandHandlers[0]); - - size_t i; - for (i = 0; i < nbHandlers && gdbCommandHandlers[i].command != command; i++); - - return i < nbHandlers ? gdbCommandHandlers[i].handler : GDB_HANDLER(Unsupported); -} - -int GDB_DoPacket(GDBContext *ctx) -{ - int ret; - - recursiveSpinlockLock(&ctx->lock); - u32 oldFlags = ctx->flags; - - if(ctx->state == GDB_STATE_DISCONNECTED) { - return -1; - } - - int r = GDB_ReceivePacket(ctx); - if (r == 0) { - ret = 0; - } else if (r == -1) { - ret = -1; - } else if (ctx->buffer[0] == '\x03') { - GDB_HandleBreak(ctx); - ret = 0; - } else if (ctx->buffer[0] == '$') { - GDBCommandHandler handler = GDB_GetCommandHandler(ctx->buffer[1]); - ctx->commandData = ctx->buffer + 2; - ret = handler(ctx); - } else { - ret = 0; - } - - if (ctx->state == GDB_STATE_DETACHING) { - if (ctx->flags & GDB_FLAG_EXTENDED_REMOTE) { - ctx->state = GDB_STATE_CONNECTED; - recursiveSpinlockUnlock(&ctx->lock); - return ret; - } else { - recursiveSpinlockUnlock(&ctx->lock); - return -1; - } - } - - if ((oldFlags & GDB_FLAG_CONTINUING) && !(ctx->flags & GDB_FLAG_CONTINUING)) { - // TODO - } - else if (!(oldFlags & GDB_FLAG_CONTINUING) && (ctx->flags & GDB_FLAG_CONTINUING)) { - // TODO - } - - recursiveSpinlockUnlock(&ctx->lock); - return ret; -} diff --git a/thermosphere/src/gdb/server.h b/thermosphere/src/gdb/server.h deleted file mode 100644 index ac28ef2d9..000000000 --- a/thermosphere/src/gdb/server.h +++ /dev/null @@ -1,34 +0,0 @@ -/* -* This file is part of Luma3DS. -* Copyright (C) 2016-2019 Aurora Wright, TuxSH -* -* SPDX-License-Identifier: (MIT OR GPL-2.0-or-later) -*/ - -#pragma once - -#include "../gdb.h" -#include "../transport_interface.h" - -typedef struct GDBServer { - TransportInterface *transportIfaces[MAX_CTX]; - GDBContext ctxs[MAX_CTX]; -} GDBServer; - -void GDB_InitializeServer(GDBServer *server); -void GDB_FinalizeServer(GDBServer *server); - -void GDB_RunServer(GDBServer *server); - -void GDB_LockAllContexts(GDBServer *server); -void GDB_UnlockAllContexts(GDBServer *server); - -// Currently, transport ifaces are tied to client - -GDBContext *GDB_SelectAvailableContext(GDBServer *server); - -int GDB_AcceptClient(GDBContext *ctx); -int GDB_CloseClient(GDBContext *ctx); -GDBContext *GDB_GetClient(GDBServer *server, TransportInterface *iface); -void GDB_ReleaseClient(GDBServer *server, GDBContext *ctx); -int GDB_DoPacket(GDBContext *ctx); diff --git a/thermosphere/src/gdb/stop_points.c b/thermosphere/src/gdb/stop_points.c index a40e79791..9adf42a18 100644 --- a/thermosphere/src/gdb/stop_points.c +++ b/thermosphere/src/gdb/stop_points.c @@ -7,7 +7,7 @@ #include -#include "../gdb.h" +#include "context.h" #include "net.h" #include "../breakpoints.h" diff --git a/thermosphere/src/gdb/stop_points.h b/thermosphere/src/gdb/stop_points.h index ff16d0ed2..37e8f86b5 100644 --- a/thermosphere/src/gdb/stop_points.h +++ b/thermosphere/src/gdb/stop_points.h @@ -7,6 +7,6 @@ #pragma once -#include "../gdb.h" +#include "context.h" GDB_DECLARE_HANDLER(ToggleStopPoint); diff --git a/thermosphere/src/gdb/thread.h b/thermosphere/src/gdb/thread.h index 5e1a06081..4e745a168 100644 --- a/thermosphere/src/gdb/thread.h +++ b/thermosphere/src/gdb/thread.h @@ -7,7 +7,7 @@ #pragma once -#include "gdb.h" +#include "context.h" u32 GDB_GetCurrentThreadFromList(GDBContext *ctx, u32 *threadIds, u32 nbThreads); u32 GDB_GetCurrentThread(GDBContext *ctx); diff --git a/thermosphere/src/gdb/verbose.h b/thermosphere/src/gdb/verbose.h index 2ae67ca79..67b85f914 100644 --- a/thermosphere/src/gdb/verbose.h +++ b/thermosphere/src/gdb/verbose.h @@ -7,7 +7,7 @@ #pragma once -#include "gdb.h" +#include "context.h" GDB_DECLARE_HANDLER(VerboseCommand); GDB_DECLARE_VERBOSE_HANDLER(ContinueSupported); diff --git a/thermosphere/src/gdb/xfer.h b/thermosphere/src/gdb/xfer.h index c4a82ce68..b279721d6 100644 --- a/thermosphere/src/gdb/xfer.h +++ b/thermosphere/src/gdb/xfer.h @@ -7,7 +7,7 @@ #pragma once -#include "gdb.h" +#include "context.h" #define GDB_XFER_HANDLER(name) GDB_HANDLER(Xfer##name) #define GDB_DECLARE_XFER_HANDLER(name) int GDB_XFER_HANDLER(name)(GDBContext *ctx, bool write, const char *annex, size_t offset, size_t length) diff --git a/thermosphere/src/transport_interface.c b/thermosphere/src/transport_interface.c index 1cbc9a69f..7ef17f81b 100644 --- a/thermosphere/src/transport_interface.c +++ b/thermosphere/src/transport_interface.c @@ -160,7 +160,7 @@ TransportInterface *transportInterfaceCreate( void transportInterfaceAcquire(TransportInterface *iface) { // Get the lock, prevent the interrupt from being pending if there's incoming data - recursiveSpinlockLock(&iface->lock); + u64 flags = recursiveSpinlockLockMaskIrq(&iface->lock); switch (iface->type) { case TRANSPORT_INTERFACE_TYPE_UART: { @@ -171,10 +171,14 @@ void transportInterfaceAcquire(TransportInterface *iface) default: break; } + + restoreInterruptFlags(flags); } void transportInterfaceRelease(TransportInterface *iface) { + u64 flags = maskIrq(); + // See transportInterfaceAcquire switch (iface->type) { case TRANSPORT_INTERFACE_TYPE_UART: { @@ -186,7 +190,7 @@ void transportInterfaceRelease(TransportInterface *iface) break; } - recursiveSpinlockUnlock(&iface->lock); + recursiveSpinlockUnlockRestoreIrq(&iface->lock, flags); } TransportInterface *transportInterfaceFindByIrqId(u16 irqId)