mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-12-23 12:51:13 +00:00
thermosphere: gdb: remove server, rewrite data processing in gdb/context and gdb/net
This commit is contained in:
parent
5dc54d8764
commit
f69ef02096
22 changed files with 497 additions and 551 deletions
|
@ -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
|
|
|
@ -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 $#<checksum>, 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);
|
|
237
thermosphere/src/gdb/context.c
Normal file
237
thermosphere/src/gdb/context.c
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// 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 <string.h>
|
||||||
|
|
||||||
|
#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);
|
||||||
|
}
|
108
thermosphere/src/gdb/context.h
Normal file
108
thermosphere/src/gdb/context.h
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
|
@ -14,7 +14,7 @@
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
#include "net.h"
|
#include "net.h"
|
||||||
|
|
||||||
#include "server.h"
|
#include "context.h"
|
||||||
#include "verbose.h"
|
#include "verbose.h"
|
||||||
#include "thread.h"
|
#include "thread.h"
|
||||||
#include "mem.h"
|
#include "mem.h"
|
||||||
|
|
|
@ -7,31 +7,9 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "../gdb.h"
|
#include "context.h"
|
||||||
#include "../core_ctx.h"
|
#include "../core_ctx.h"
|
||||||
|
#include "../debug_manager.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_HANDLER(Detach);
|
GDB_DECLARE_HANDLER(Detach);
|
||||||
GDB_DECLARE_HANDLER(Kill);
|
GDB_DECLARE_HANDLER(Kill);
|
||||||
|
|
42
thermosphere/src/gdb/defines.h
Normal file
42
thermosphere/src/gdb/defines.h
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// 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 $#<checksum>, 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)
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "gdb.h"
|
#include "context.h"
|
||||||
|
|
||||||
bool GDB_FetchPackedHioRequest(GDBContext *ctx, u32 addr);
|
bool GDB_FetchPackedHioRequest(GDBContext *ctx, u32 addr);
|
||||||
bool GDB_IsHioInProgress(GDBContext *ctx);
|
bool GDB_IsHioInProgress(GDBContext *ctx);
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
#pragma once
|
#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_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);
|
int GDB_WriteMemory(GDBContext *ctx, const void *buf, uintptr_t addr, size_t len);
|
||||||
|
|
|
@ -5,12 +5,11 @@
|
||||||
* SPDX-License-Identifier: (MIT OR GPL-2.0-or-later)
|
* SPDX-License-Identifier: (MIT OR GPL-2.0-or-later)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "gdb/net.h"
|
#include "net.h"
|
||||||
|
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include "fmt.h"
|
|
||||||
#include "minisoc.h"
|
|
||||||
#include "../pattern_utils.h"
|
#include "../pattern_utils.h"
|
||||||
|
|
||||||
u8 GDB_ComputeChecksum(const char *packetData, size_t len)
|
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);
|
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)
|
int GDB_ReceivePacket(GDBContext *ctx)
|
||||||
{
|
{
|
||||||
char backupbuf[GDB_BUF_LEN + 4];
|
char hdr;
|
||||||
memcpy(backupbuf, ctx->buffer, ctx->latestSentPacketSize);
|
bool ctrlC = false;
|
||||||
memset(ctx->buffer, 0, sizeof(ctx->buffer));
|
TransportInterface *iface = ctx->transportInterface;
|
||||||
|
|
||||||
int r = soc_recv(ctx->super.sockfd, ctx->buffer, sizeof(ctx->buffer), MSG_PEEK);
|
// Read the first character...
|
||||||
if(r < 1)
|
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;
|
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;
|
|
||||||
}
|
}
|
||||||
else if(ctx->buffer[0] == '-')
|
|
||||||
{
|
switch (hdr) {
|
||||||
soc_send(ctx->super.sockfd, backupbuf, ctx->latestSentPacketSize, 0);
|
case '+':
|
||||||
|
// Ack, don't do anything else (if allowed)
|
||||||
return 0;
|
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);
|
||||||
}
|
}
|
||||||
int maxlen = r > (int)sizeof(ctx->buffer) ? (int)sizeof(ctx->buffer) : r;
|
|
||||||
|
|
||||||
if(ctx->buffer[0] == '$') // normal packet
|
// We didn't get a nack past this point, read the remaining data if any
|
||||||
{
|
|
||||||
char *pos;
|
|
||||||
for(pos = ctx->buffer; pos < ctx->buffer + maxlen && *pos != '#'; pos++);
|
|
||||||
|
|
||||||
if(pos == ctx->buffer + maxlen) // malformed packet
|
ctx->buffer[0] = hdr;
|
||||||
return -1;
|
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;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
else
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the checksum
|
||||||
|
size_t checksumPos = delimPos + 1;
|
||||||
u8 checksum;
|
u8 checksum;
|
||||||
r = soc_recv(ctx->super.sockfd, ctx->buffer, 3 + pos - ctx->buffer, 0);
|
transportInterfaceReadData(iface, ctx->buffer + checksumPos, 2);
|
||||||
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;
|
if (GDB_DecodeHex(&checksum, ctx->buffer + checksumPos, 1) != 1) {
|
||||||
*pos = 0; // replace trailing '#' by a NUL character
|
// Malformed checksum
|
||||||
}
|
return GDB_SendNackIfPossible(ctx);
|
||||||
}
|
} else if (GDB_ComputeChecksum(ctx->buffer + 1, delimPos - 1) != checksum) {
|
||||||
else if(ctx->buffer[0] == '\x03')
|
// Invalid checksum
|
||||||
{
|
return GDB_SendNackIfPossible(ctx);
|
||||||
r = soc_recv(ctx->super.sockfd, ctx->buffer, 1, 0);
|
|
||||||
if(r != 1)
|
|
||||||
goto packet_error;
|
|
||||||
|
|
||||||
ctx->commandEnd = ctx->buffer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!(ctx->flags & GDB_FLAG_NOACK))
|
// Ok, send ack (if possible)
|
||||||
{
|
if (!(ctx->flags & GDB_FLAG_NOACK)) {
|
||||||
int r2 = soc_send(ctx->super.sockfd, "+", 1, 0);
|
hdr = '+';
|
||||||
if(r2 != 1)
|
transportInterfaceWriteData(iface, &hdr, 1);
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ctx->noAckSent)
|
// 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->flags |= GDB_FLAG_NOACK;
|
||||||
ctx->noAckSent = false;
|
ctx->noAckSent = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return r;
|
// Set helper attributes, change '#' to NUL
|
||||||
|
ctx->commandEnd = delimPos;
|
||||||
|
ctx->buffer[delimPos] = '\0';
|
||||||
|
|
||||||
packet_error:
|
return (int)(delimPos + 2);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int GDB_DoSendPacket(GDBContext *ctx, size_t len)
|
static int GDB_DoSendPacket(GDBContext *ctx, size_t len)
|
||||||
{
|
{
|
||||||
int r = soc_send(ctx->super.sockfd, ctx->buffer, len, 0);
|
transportInterfaceWriteData(ctx->transportInterface, ctx->buffer, len);
|
||||||
|
ctx->lastSentPacketSize = len;
|
||||||
if(r > 0)
|
return (int)len;
|
||||||
ctx->latestSentPacketSize = r;
|
|
||||||
return r;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int GDB_SendPacket(GDBContext *ctx, const char *packetData, size_t 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)
|
int GDB_ReplyErrno(GDBContext *ctx, int no)
|
||||||
{
|
{
|
||||||
char buf[] = "E01";
|
char buf[] = "E01";
|
||||||
hexItoa((u8)no, buf + 1, 2, false);
|
hexItoa(no & 0xFF, buf + 1, 2, false);
|
||||||
return GDB_SendPacket(ctx, buf, 3);
|
return GDB_SendPacket(ctx, buf, 3);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "../gdb.h"
|
#include "context.h"
|
||||||
#define _REENT_ONLY
|
#define _REENT_ONLY
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "gdb.h"
|
#include "context.h"
|
||||||
|
|
||||||
int GDB_HandleReadQuery(GDBContext *ctx);
|
int GDB_HandleReadQuery(GDBContext *ctx);
|
||||||
int GDB_HandleWriteQuery(GDBContext *ctx);
|
int GDB_HandleWriteQuery(GDBContext *ctx);
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "../gdb.h"
|
#include "context.h"
|
||||||
|
|
||||||
GDB_DECLARE_HANDLER(ReadRegisters);
|
GDB_DECLARE_HANDLER(ReadRegisters);
|
||||||
GDB_DECLARE_HANDLER(WriteRegisters);
|
GDB_DECLARE_HANDLER(WriteRegisters);
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "gdb.h"
|
#include "context.h"
|
||||||
|
|
||||||
#define GDB_REMOTE_COMMAND_HANDLER(name) GDB_HANDLER(RemoteCommand##name)
|
#define GDB_REMOTE_COMMAND_HANDLER(name) GDB_HANDLER(RemoteCommand##name)
|
||||||
#define GDB_DECLARE_REMOTE_COMMAND_HANDLER(name) GDB_DECLARE_HANDLER(RemoteCommand##name)
|
#define GDB_DECLARE_REMOTE_COMMAND_HANDLER(name) GDB_DECLARE_HANDLER(RemoteCommand##name)
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
|
@ -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);
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "../gdb.h"
|
#include "context.h"
|
||||||
#include "net.h"
|
#include "net.h"
|
||||||
|
|
||||||
#include "../breakpoints.h"
|
#include "../breakpoints.h"
|
||||||
|
|
|
@ -7,6 +7,6 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "../gdb.h"
|
#include "context.h"
|
||||||
|
|
||||||
GDB_DECLARE_HANDLER(ToggleStopPoint);
|
GDB_DECLARE_HANDLER(ToggleStopPoint);
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "gdb.h"
|
#include "context.h"
|
||||||
|
|
||||||
u32 GDB_GetCurrentThreadFromList(GDBContext *ctx, u32 *threadIds, u32 nbThreads);
|
u32 GDB_GetCurrentThreadFromList(GDBContext *ctx, u32 *threadIds, u32 nbThreads);
|
||||||
u32 GDB_GetCurrentThread(GDBContext *ctx);
|
u32 GDB_GetCurrentThread(GDBContext *ctx);
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "gdb.h"
|
#include "context.h"
|
||||||
|
|
||||||
GDB_DECLARE_HANDLER(VerboseCommand);
|
GDB_DECLARE_HANDLER(VerboseCommand);
|
||||||
GDB_DECLARE_VERBOSE_HANDLER(ContinueSupported);
|
GDB_DECLARE_VERBOSE_HANDLER(ContinueSupported);
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "gdb.h"
|
#include "context.h"
|
||||||
|
|
||||||
#define GDB_XFER_HANDLER(name) GDB_HANDLER(Xfer##name)
|
#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)
|
#define GDB_DECLARE_XFER_HANDLER(name) int GDB_XFER_HANDLER(name)(GDBContext *ctx, bool write, const char *annex, size_t offset, size_t length)
|
||||||
|
|
|
@ -160,7 +160,7 @@ TransportInterface *transportInterfaceCreate(
|
||||||
void transportInterfaceAcquire(TransportInterface *iface)
|
void transportInterfaceAcquire(TransportInterface *iface)
|
||||||
{
|
{
|
||||||
// Get the lock, prevent the interrupt from being pending if there's incoming data
|
// 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) {
|
switch (iface->type) {
|
||||||
case TRANSPORT_INTERFACE_TYPE_UART: {
|
case TRANSPORT_INTERFACE_TYPE_UART: {
|
||||||
|
@ -171,10 +171,14 @@ void transportInterfaceAcquire(TransportInterface *iface)
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
restoreInterruptFlags(flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
void transportInterfaceRelease(TransportInterface *iface)
|
void transportInterfaceRelease(TransportInterface *iface)
|
||||||
{
|
{
|
||||||
|
u64 flags = maskIrq();
|
||||||
|
|
||||||
// See transportInterfaceAcquire
|
// See transportInterfaceAcquire
|
||||||
switch (iface->type) {
|
switch (iface->type) {
|
||||||
case TRANSPORT_INTERFACE_TYPE_UART: {
|
case TRANSPORT_INTERFACE_TYPE_UART: {
|
||||||
|
@ -186,7 +190,7 @@ void transportInterfaceRelease(TransportInterface *iface)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
recursiveSpinlockUnlock(&iface->lock);
|
recursiveSpinlockUnlockRestoreIrq(&iface->lock, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
TransportInterface *transportInterfaceFindByIrqId(u16 irqId)
|
TransportInterface *transportInterfaceFindByIrqId(u16 irqId)
|
||||||
|
|
Loading…
Reference in a new issue